Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: 삭제하기 기능 추가, editor 및 preview 구현, 복사하기 기능 추가 #71

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6,847 changes: 4,361 additions & 2,486 deletions .pnp.cjs

Large diffs are not rendered by default.

Binary file modified .yarn/install-state.gz
Binary file not shown.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
]
},
"scripts": {
"start": "react-scripts start",
"start": " GENERATE_SOURCEMAP=false react-scripts start",
"build": "react-scripts build",
"test": "jest",
"eject": "react-scripts eject",
Expand All @@ -62,6 +62,7 @@
"@dnd-kit/sortable": "^8.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@testing-library/user-event": "^14.5.2",
"@uiw/react-md-editor": "^4.0.4",
"bootstrap": "^5.3.3",
"clsx": "^2.1.1",
"qs": "^6.12.1",
Expand Down
2 changes: 1 addition & 1 deletion src/components/Editor/Components/Auto.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const Auto = () => {
{create ? (
<>
<div className="w-full h-auto">
<EditSections />
<EditSections keyName="auto-sections-list" />
</div>
<div className="w-full h-auto">
<SelectSections />
Expand Down
2 changes: 1 addition & 1 deletion src/components/Editor/Components/Builder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const Builder = () => {
return (
<div className="w-full h-full flex flex-col gap-[20px]">
<div className="w-full h-auto">
<EditSections />
<EditSections keyName="builder-sections-list" />
</div>
<div className="w-full h-auto">
<SelectSections />
Expand Down
17 changes: 13 additions & 4 deletions src/components/Editor/Components/EditSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ import clsx from "clsx";
interface Props {
id: number;
title: string | undefined;
markdown: string | undefined;
onDeleteSection: (e: React.MouseEvent<HTMLElement, MouseEvent>, targetId: number) => void;
}

const EditSection = ({ id, title }: Props) => {
const EditSection = ({ id, title, markdown, onDeleteSection }: Props) => {
const [hover, setHover] = useState<boolean>(false);

const onMouseEnter = () => setHover(true);
const onMouseLeave = () => setHover(false);

Expand Down Expand Up @@ -43,8 +44,16 @@ const EditSection = ({ id, title }: Props) => {
<p className="text-textPrimary mb-0 truncate">{title}</p>
{hover && (
<div className="flex flex-row gap-[10px] ml-auto">
<Reset size={20} className="fill-[#ADB5BD]" onClick={() => alert("rest")} />
<TrashCan size={20} className="fill-textPrimary" onClick={() => alert("delete")} />
<button>
<Reset size={20} className="fill-[#ADB5BD]" onClick={() => alert("rest")} />
</button>
<button
onClick={e => {
onDeleteSection(e, id);
}}
>
<TrashCan size={20} className="fill-textPrimary" />
</button>
</div>
)}
</div>
Expand Down
49 changes: 33 additions & 16 deletions src/components/Editor/Components/EditSections.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
KeyboardSensor,
MouseSensor,
TouchSensor,
DragEndEvent,
} from "@dnd-kit/core";
import {
SortableContext,
Expand All @@ -16,23 +17,21 @@ import {
sortableKeyboardCoordinates,
} from "@dnd-kit/sortable";
import { restrictToVerticalAxis } from "@dnd-kit/modifiers";
import { useSection } from "context/SectionContext";
import { KeyNameType, SectionsType } from "../types";

interface Props {
id: number;
title: string | undefined;
}

const EditSections = () => {
const [sections, setSections] = useState<Props[]>([]);
const EditSections = ({ keyName }: KeyNameType) => {
const { value, setValue } = useSection();
const [sections, setSections] = useState<SectionsType[]>([]);

const getIndex = (id: number) => sections.findIndex(el => el.id === id);

const handleDragEnd = (event: any) => {
const handleDragEnd = (event: DragEndEvent) => {
const { active, over } = event;
if (active.id === over.id) return;
if (active.id === over?.id) return;
setSections(sections => {
const oldIndex = getIndex(active.id);
const newIndex = getIndex(over.id);
const oldIndex = getIndex(active.id as number);
const newIndex = getIndex(over?.id as number);

return arrayMove(sections, oldIndex, newIndex);
});
Expand All @@ -46,15 +45,27 @@ const EditSections = () => {
}),
);

const onDeleteSection = (e: React.MouseEvent<HTMLElement, MouseEvent>, targetId: number) => {
e.stopPropagation();
setSections(prev => prev.filter(el => el.id !== targetId));
};

const onResetSection = (e: React.MouseEvent<HTMLElement, MouseEvent>, targetId: number) => {
e.stopPropagation();
};

useEffect(() => {
const prevSectionsList = JSON.parse(localStorage.getItem("builder-sections-list") || "[]");
if (prevSectionsList.length > 0) {
setSections(prevSectionsList);
const sectionsList = JSON.parse(localStorage.getItem(`${keyName}`) || "[]");
if (sectionsList.length > 0) {
setSections(sectionsList);
}
}, []);

useEffect(() => {
localStorage.setItem("builder-sections-list", JSON.stringify(sections));
localStorage.setItem(`${keyName}`, JSON.stringify(sections));
const sectionsList = JSON.parse(localStorage.getItem(`${keyName}`) || "[]");
const markdownList = sectionsList.map((el: SectionsType) => el.markdown).join("");
setValue(markdownList);
}, [sections]);

return (
Expand All @@ -71,7 +82,13 @@ const EditSections = () => {
>
<SortableContext items={sections} strategy={verticalListSortingStrategy}>
{sections.map(section => (
<EditSection key={section.id} title={section.title} id={section.id} />
<EditSection
key={section.id}
title={section.title}
id={section.id}
markdown={section.markdown}
onDeleteSection={onDeleteSection}
/>
))}
</SortableContext>
</DndContext>
Expand Down
28 changes: 26 additions & 2 deletions src/components/Editor/Components/Editor.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,33 @@
import React from "react";
import { useSection } from "context/SectionContext";
import MDEditor, { commands } from "@uiw/react-md-editor";

const Editor = () => {
const sectionsList = JSON.parse(localStorage.getItem("builder-sections-list") || "[]");
const { value: markdown, setValue } = useSection();
const commandsList = [...commands.getCommands()].slice(0, 17);

return sectionsList.length > 0 ? <div className="w-full h-full rounded-[8px] bg-[#333333]"></div> : <EmptySections />;
return (
<div
className="w-full h-full rounded-[8px] border-solid border border-textTertiary overflow-y-auto"
data-color-mode="dark"
>
<MDEditor
className="editor"
value={markdown}
onChange={value => setValue(value!)}
preview="edit"
height="100%"
commands={commandsList}
extraCommands={[]}
visibleDragbar={false}
style={{
height: "100%",
overflow: "scroll",
border: "none",
}}
/>
</div>
);
};

export default Editor;
Expand Down
15 changes: 12 additions & 3 deletions src/components/Editor/Components/Preview.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
import MDEditor from "@uiw/react-md-editor";
import React from "react";
import { ValueType } from "../types";

const Preview = () => {
const Preview = ({ value }: ValueType) => {
return (
<div className="w-full h-full rounded-[8px] border-solid border border-textTertiary"></div>
<div className="w-full h-full p-[20px] rounded-[8px] border-solid border border-textTertiary overflow-y-scroll">
<MDEditor.Markdown
source={value}
style={{
overflow: "auto",
}}
/>
</div>
);
};

export default Preview;
export default Preview;
32 changes: 25 additions & 7 deletions src/components/Editor/Components/Raw.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,32 @@
import React from "react";
import React, { useRef } from "react";
import { Copy } from "@carbon/icons-react";
import { ValueType } from "../types";

const Raw = ({ value }: ValueType) => {
const textAreaRef = useRef<HTMLTextAreaElement | null>(null);

const handleCopyClick = async (textToCopy: string) => {
textAreaRef.current?.select();
try {
await navigator.clipboard.writeText(textToCopy);
alert("Copying is complete.");
} catch (e) {
alert("Copying failed.");
}
};

const Raw = () => {
return (
<div className= "w-full h-full p-[20px] rounded-[8px] border-solid border border-textTertiary">
<div className="w-full flex justify-end">
<Copy size={20} className="cursor-pointer" onClick={() => alert("복사하기가 완료되었습니다.")}/>
</div>
<div className="w-full h-full p-[20px] rounded-[8px] border-solid border border-textTertiary relative">
{value!.length > 0 && (
<div className="h-auto absolute right-[20px]" onClick={() => handleCopyClick(value!)}>
<Copy size={18} className="cursor-pointer" />
</div>
)}
<textarea ref={textAreaRef} key={value} readOnly className="h-full w-full resize-none focus:outline-none p-0">
{value}
</textarea>
</div>
);
};

export default Raw;
export default Raw;
10 changes: 6 additions & 4 deletions src/components/Editor/EditorPreviewContainer.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import React, { useState } from "react";
import Editor from "./Components/Editor";
import Preview from "./Components/Preview";
import Raw from "./Components/Raw";
import { Tab, Tabs } from "../Common/Tabs";
import { useSection } from "../../context/SectionContext";
import Editor from "./Components/Editor";

const EditorPreviewContainer = () => {
const { value } = useSection();
const [selectedTab, setSelectedTab] = useState<string | undefined>("Preview");

const handleTabClick = (value?: string | undefined) => {
Expand All @@ -16,7 +18,7 @@ const EditorPreviewContainer = () => {
<div className="w-1/2">
<div className="w-full h-full flex flex-col gap-[10px]">
<div className="min-h-[30px] mx-[5px] flex items-center">
<p className="text-textBlue font-semibold mb-0">Edior</p>
<p className="text-textBlue font-semibold mb-0">Editor</p>
</div>
<Editor />
</div>
Expand All @@ -27,8 +29,8 @@ const EditorPreviewContainer = () => {
<Tab value="Preview">Preview</Tab>
<Tab value="Raw">Raw</Tab>
</Tabs>
{selectedTab === "Preview" && <Preview />}
{selectedTab === "Raw" && <Raw />}
{selectedTab === "Preview" && <Preview value={value} />}
{selectedTab === "Raw" && <Raw value={value} />}
</div>
</div>
</>
Expand Down
13 changes: 13 additions & 0 deletions src/components/Editor/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export interface ValueType {
value: string | undefined;
}

export interface SectionsType {
id: number;
title: string | undefined;
markdown: string | undefined;
}

export interface KeyNameType {
keyName: string;
}
35 changes: 35 additions & 0 deletions src/context/SectionContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React, { createContext, useContext, useState } from "react";

interface SectionContextType {
value: string;
setValue: (value: string) => void;
}

interface SectionContextProviderProps {
children: React.ReactNode;
}

const SectionContext = createContext<SectionContextType | null>(null);

export const useSection = () => {
const context = useContext(SectionContext);
if (!context) {
throw new Error("useSection must be used within a SectionProvider");
}
return context;
};

export const SectionProvider = ({ children }: SectionContextProviderProps) => {
const [value, setValue] = useState<string>("# Welcome To README-MONSTER");

return (
<SectionContext.Provider
value={{
value,
setValue,
}}
>
{children}
</SectionContext.Provider>
);
};
11 changes: 7 additions & 4 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,20 @@ import { BrowserRouter as Router } from "react-router-dom";
import App from "./pages/App";
import "../src/styles/global.css";
import "bootstrap/dist/css/bootstrap.min.css";
import { SectionProvider } from "context/SectionContext";

const rootElement = document.getElementById("root");

if (rootElement) {
const root = ReactDOM.createRoot(rootElement);
root.render(
<React.StrictMode>
<Router>
<App />
</Router>
</React.StrictMode>
<SectionProvider>
<Router>
<App />
</Router>
</SectionProvider>
</React.StrictMode>,
);
} else {
console.error("Failed to find the root element");
Expand Down
9 changes: 9 additions & 0 deletions src/styles/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,13 @@
@media (max-width: 768px) { /* Tailwind의 md 브레이크포인트는 기본적으로 768px입니다. */
.display-contents {display: contents;}
}

.editor .w-md-editor-toolbar {
border-radius: 8px 8px 0px 0px !important;
padding: 10px;
}

.editor .w-md-editor-content {
padding: 5px
}
}
Loading
Loading