Skip to content

Commit ecbd3ee

Browse files
committed
Fixed modal and added copy functionality
1 parent 2ac0718 commit ecbd3ee

File tree

8 files changed

+101
-52
lines changed

8 files changed

+101
-52
lines changed

src/components/Button.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,14 @@ const Button = ({
2020
}: ButtonProps) => {
2121
return as === "button" ? (
2222
<button
23-
className={`button ${isIcon ? "button--icon" : ""} ${className}`}
23+
className={`button ${isIcon ? "button--icon" : ""} ${className || ""}`}
2424
onClick={onClick}
2525
{...props}
2626
>
2727
{children}
2828
</button>
2929
) : (
30-
<a className={`button ${className}`} href={href} {...props}>
30+
<a className={`button ${className || ""}`} href={href} {...props}>
3131
{children}
3232
</a>
3333
);

src/components/CategoryList.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ const CategoryList = () => {
77

88
return (
99
<ul role="list" className="categories">
10-
{fetchedCategories.map((name) => (
11-
<li className="category">
10+
{fetchedCategories.map((name, idx) => (
11+
<li key={idx} className="category">
1212
<button
1313
className={`category__btn ${
1414
name === category ? "category__btn--active" : ""

src/components/CopyToClipboard.tsx

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { useState } from "react";
2+
import { useAppContext } from "../contexts/AppContext";
3+
import Button from "./Button";
4+
import { CopyIcon } from "./Icons";
5+
6+
const CopyToClipboard = ({ ...props }) => {
7+
const { snippet } = useAppContext();
8+
const [isCopied, setIsCopied] = useState(false);
9+
10+
const snippetCode = snippet ? snippet.code : "";
11+
12+
const copySnippetCode = () => {
13+
navigator.clipboard
14+
.writeText(snippetCode)
15+
.then(() => {
16+
setIsCopied(true);
17+
setTimeout(() => setIsCopied(false), 2000);
18+
})
19+
.catch((err) => alert("Error occured: " + err));
20+
};
21+
22+
return (
23+
<Button isIcon={true} onClick={copySnippetCode} {...props}>
24+
{isCopied ? "Copied!" : <CopyIcon />}
25+
</Button>
26+
);
27+
};
28+
29+
export default CopyToClipboard;

src/components/Logo.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { Link } from "react-router-dom";
21
import { LogoIcon } from "./Icons";
32

43
const Logo = () => {

src/components/SnippetList.tsx

Lines changed: 57 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,81 @@
1+
import { useState } from "react";
2+
import { SnippetType } from "../types";
13
import { useAppContext } from "../contexts/AppContext";
24
import { useSnippets } from "../hooks/useSnippets";
5+
import slugify from "../utils/slugify";
6+
37
import Button from "./Button";
4-
import { CopyIcon, ExpandIcon } from "./Icons";
8+
import CodePreview from "./CodePreview";
9+
import SnippetModal from "./SnippetModal";
10+
import CopyToClipboard from "./CopyToClipboard";
11+
import { CloseIcon, ExpandIcon } from "./Icons";
512

613
const SnippetList = () => {
7-
const { language } = useAppContext();
14+
const { language, setSnippet } = useAppContext();
815
const { fetchedSnippets } = useSnippets();
16+
const [isModalOpen, setIsModalOpen] = useState(false);
917

1018
if (!fetchedSnippets) return <p>Empty List</p>;
1119

20+
const handleOpenModal = (activeSnippet: SnippetType) => {
21+
setIsModalOpen(true);
22+
setSnippet(activeSnippet);
23+
};
24+
25+
const handleCloseModal = () => {
26+
setIsModalOpen(false);
27+
setSnippet(null);
28+
};
29+
1230
return (
1331
<ul role="list" className="snippets">
14-
{fetchedSnippets.map((snippet) => (
15-
<li className="snippet">
32+
{fetchedSnippets.map((snippet, idx) => (
33+
<li key={idx} className="snippet">
1634
<div className="snippet__preview">
1735
<img src={language.icon} alt={language.lang} />
18-
<Button isIcon={true} className="snippet__copy">
36+
{/* <Button isIcon={true} className="snippet__copy">
1937
<CopyIcon />
20-
</Button>
38+
</Button> */}
2139
</div>
2240

2341
<div className="snippet__content">
2442
<h3 className="snippet__title">{snippet.title}</h3>
25-
<Button isIcon={true}>
43+
<Button isIcon={true} onClick={() => handleOpenModal(snippet)}>
2644
<ExpandIcon />
2745
</Button>
2846
</div>
47+
{isModalOpen && (
48+
<SnippetModal>
49+
<div className="modal | flow" data-flow-space="lg">
50+
<div className="modal__header">
51+
<h2 className="section-title">{snippet.title}</h2>
52+
<Button isIcon={true} onClick={handleCloseModal}>
53+
<CloseIcon />
54+
</Button>
55+
</div>
56+
<div className="code-preview">
57+
<CopyToClipboard className="modal__copy" />
58+
<CodePreview language={slugify(language.lang)}>
59+
{snippet.code}
60+
</CodePreview>
61+
</div>
62+
<p>
63+
<b>Description: </b>
64+
{snippet.description}
65+
</p>
66+
<p>
67+
Contributed by <b>{snippet.author}</b>
68+
</p>
69+
<ul role="list" className="modal__tags">
70+
{snippet.tags.map((tag) => (
71+
<li key={tag} className="modal__tag">
72+
{tag}
73+
</li>
74+
))}
75+
</ul>
76+
</div>
77+
</SnippetModal>
78+
)}
2979
</li>
3080
))}
3181
</ul>

src/components/SnippetModal.tsx

Lines changed: 7 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,16 @@
1-
import { useEffect, useState } from "react";
1+
import React from "react";
22
import ReactDOM from "react-dom";
3-
import { LanguageData, SnippetType } from "../types";
4-
import { getSnippetByTitleAndCategory } from "../utils/filters";
5-
import { CloseIcon, CopyIcon } from "./Icons";
6-
import Button from "./Button";
7-
import CodePreview from "./CodePreview";
83

9-
const SnippetModal = () => {
4+
type Props = {
5+
children: React.ReactNode;
6+
};
7+
8+
const SnippetModal: React.FC<Props> = ({ children }) => {
109
const modalRoot = document.getElementById("modal-root");
1110
if (!modalRoot) return null;
1211

1312
return ReactDOM.createPortal(
14-
<div className="modal-overlay">
15-
<div className="modal | flow" data-flow-space="lg">
16-
<div className="modal__header">
17-
<h2 className="section-title">{snippet.title}</h2>
18-
<Button isIcon={true} onClick={() => navigate(-1)}>
19-
<CloseIcon />
20-
</Button>
21-
</div>
22-
<div className="code-preview">
23-
<Button isIcon={true} className="modal__copy">
24-
<CopyIcon />
25-
</Button>
26-
<CodePreview language={language}>{snippet.code}</CodePreview>
27-
</div>
28-
<p>
29-
<b>Description: </b>
30-
{snippet.description}
31-
</p>
32-
<p>
33-
Contributed by <b>{snippet.author}</b>
34-
</p>
35-
<ul role="list" className="modal__tags">
36-
{snippet.tags.map((tag) => (
37-
<li key={tag} className="modal__tag">
38-
{tag}
39-
</li>
40-
))}
41-
</ul>
42-
</div>
43-
</div>,
13+
<div className="modal-overlay">{children}</div>,
4414
modalRoot
4515
);
4616
};

src/contexts/AppContext.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const defaultState: AppState = {
1414
setLanguage: () => {},
1515
category: defaultCategory,
1616
setCategory: () => {},
17+
snippet: null,
1718
setSnippet: () => {},
1819
};
1920

@@ -26,7 +27,7 @@ export const AppProvider: React.FC<{ children: React.ReactNode }> = ({
2627
}) => {
2728
const [language, setLanguage] = useState<LanguageType>(defaultLanguage);
2829
const [category, setCategory] = useState<string>(defaultCategory);
29-
const [snippet, setSnippet] = useState<SnippetType>();
30+
const [snippet, setSnippet] = useState<SnippetType | null>(null);
3031

3132
return (
3233
<AppContext.Provider

src/types/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,6 @@ export type AppState = {
2121
setLanguage: React.Dispatch<React.SetStateAction<LanguageType>>;
2222
category: string;
2323
setCategory: React.Dispatch<React.SetStateAction<string>>;
24-
snippet?: SnippetType;
25-
setSnippet: React.Dispatch<React.SetStateAction<SnippetType | undefined>>;
24+
snippet: SnippetType | null;
25+
setSnippet: React.Dispatch<React.SetStateAction<SnippetType | null>>;
2626
};

0 commit comments

Comments
 (0)