Skip to content

Commit

Permalink
Fixed modal and added copy functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
dostonnabotov committed Dec 8, 2024
1 parent 2ac0718 commit ecbd3ee
Show file tree
Hide file tree
Showing 8 changed files with 101 additions and 52 deletions.
4 changes: 2 additions & 2 deletions src/components/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ const Button = ({
}: ButtonProps) => {
return as === "button" ? (
<button
className={`button ${isIcon ? "button--icon" : ""} ${className}`}
className={`button ${isIcon ? "button--icon" : ""} ${className || ""}`}
onClick={onClick}
{...props}
>
{children}
</button>
) : (
<a className={`button ${className}`} href={href} {...props}>
<a className={`button ${className || ""}`} href={href} {...props}>
{children}
</a>
);
Expand Down
4 changes: 2 additions & 2 deletions src/components/CategoryList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ const CategoryList = () => {

return (
<ul role="list" className="categories">
{fetchedCategories.map((name) => (
<li className="category">
{fetchedCategories.map((name, idx) => (
<li key={idx} className="category">
<button
className={`category__btn ${
name === category ? "category__btn--active" : ""
Expand Down
29 changes: 29 additions & 0 deletions src/components/CopyToClipboard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { useState } from "react";
import { useAppContext } from "../contexts/AppContext";
import Button from "./Button";
import { CopyIcon } from "./Icons";

const CopyToClipboard = ({ ...props }) => {
const { snippet } = useAppContext();
const [isCopied, setIsCopied] = useState(false);

const snippetCode = snippet ? snippet.code : "";

const copySnippetCode = () => {
navigator.clipboard
.writeText(snippetCode)
.then(() => {
setIsCopied(true);
setTimeout(() => setIsCopied(false), 2000);
})
.catch((err) => alert("Error occured: " + err));
};

return (
<Button isIcon={true} onClick={copySnippetCode} {...props}>
{isCopied ? "Copied!" : <CopyIcon />}
</Button>
);
};

export default CopyToClipboard;
1 change: 0 additions & 1 deletion src/components/Logo.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { Link } from "react-router-dom";
import { LogoIcon } from "./Icons";

const Logo = () => {
Expand Down
64 changes: 57 additions & 7 deletions src/components/SnippetList.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,81 @@
import { useState } from "react";
import { SnippetType } from "../types";
import { useAppContext } from "../contexts/AppContext";
import { useSnippets } from "../hooks/useSnippets";
import slugify from "../utils/slugify";

import Button from "./Button";
import { CopyIcon, ExpandIcon } from "./Icons";
import CodePreview from "./CodePreview";
import SnippetModal from "./SnippetModal";
import CopyToClipboard from "./CopyToClipboard";
import { CloseIcon, ExpandIcon } from "./Icons";

const SnippetList = () => {
const { language } = useAppContext();
const { language, setSnippet } = useAppContext();
const { fetchedSnippets } = useSnippets();
const [isModalOpen, setIsModalOpen] = useState(false);

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

const handleOpenModal = (activeSnippet: SnippetType) => {
setIsModalOpen(true);
setSnippet(activeSnippet);
};

const handleCloseModal = () => {
setIsModalOpen(false);
setSnippet(null);
};

return (
<ul role="list" className="snippets">
{fetchedSnippets.map((snippet) => (
<li className="snippet">
{fetchedSnippets.map((snippet, idx) => (
<li key={idx} className="snippet">
<div className="snippet__preview">
<img src={language.icon} alt={language.lang} />
<Button isIcon={true} className="snippet__copy">
{/* <Button isIcon={true} className="snippet__copy">
<CopyIcon />
</Button>
</Button> */}
</div>

<div className="snippet__content">
<h3 className="snippet__title">{snippet.title}</h3>
<Button isIcon={true}>
<Button isIcon={true} onClick={() => handleOpenModal(snippet)}>
<ExpandIcon />
</Button>
</div>
{isModalOpen && (
<SnippetModal>
<div className="modal | flow" data-flow-space="lg">
<div className="modal__header">
<h2 className="section-title">{snippet.title}</h2>
<Button isIcon={true} onClick={handleCloseModal}>
<CloseIcon />
</Button>
</div>
<div className="code-preview">
<CopyToClipboard className="modal__copy" />
<CodePreview language={slugify(language.lang)}>
{snippet.code}
</CodePreview>
</div>
<p>
<b>Description: </b>
{snippet.description}
</p>
<p>
Contributed by <b>{snippet.author}</b>
</p>
<ul role="list" className="modal__tags">
{snippet.tags.map((tag) => (
<li key={tag} className="modal__tag">
{tag}
</li>
))}
</ul>
</div>
</SnippetModal>
)}
</li>
))}
</ul>
Expand Down
44 changes: 7 additions & 37 deletions src/components/SnippetModal.tsx
Original file line number Diff line number Diff line change
@@ -1,46 +1,16 @@
import { useEffect, useState } from "react";
import React from "react";
import ReactDOM from "react-dom";
import { LanguageData, SnippetType } from "../types";
import { getSnippetByTitleAndCategory } from "../utils/filters";
import { CloseIcon, CopyIcon } from "./Icons";
import Button from "./Button";
import CodePreview from "./CodePreview";

const SnippetModal = () => {
type Props = {
children: React.ReactNode;
};

const SnippetModal: React.FC<Props> = ({ children }) => {
const modalRoot = document.getElementById("modal-root");
if (!modalRoot) return null;

return ReactDOM.createPortal(
<div className="modal-overlay">
<div className="modal | flow" data-flow-space="lg">
<div className="modal__header">
<h2 className="section-title">{snippet.title}</h2>
<Button isIcon={true} onClick={() => navigate(-1)}>
<CloseIcon />
</Button>
</div>
<div className="code-preview">
<Button isIcon={true} className="modal__copy">
<CopyIcon />
</Button>
<CodePreview language={language}>{snippet.code}</CodePreview>
</div>
<p>
<b>Description: </b>
{snippet.description}
</p>
<p>
Contributed by <b>{snippet.author}</b>
</p>
<ul role="list" className="modal__tags">
{snippet.tags.map((tag) => (
<li key={tag} className="modal__tag">
{tag}
</li>
))}
</ul>
</div>
</div>,
<div className="modal-overlay">{children}</div>,
modalRoot
);
};
Expand Down
3 changes: 2 additions & 1 deletion src/contexts/AppContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const defaultState: AppState = {
setLanguage: () => {},
category: defaultCategory,
setCategory: () => {},
snippet: null,
setSnippet: () => {},
};

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

return (
<AppContext.Provider
Expand Down
4 changes: 2 additions & 2 deletions src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@ export type AppState = {
setLanguage: React.Dispatch<React.SetStateAction<LanguageType>>;
category: string;
setCategory: React.Dispatch<React.SetStateAction<string>>;
snippet?: SnippetType;
setSnippet: React.Dispatch<React.SetStateAction<SnippetType | undefined>>;
snippet: SnippetType | null;
setSnippet: React.Dispatch<React.SetStateAction<SnippetType | null>>;
};

0 comments on commit ecbd3ee

Please sign in to comment.