diff --git a/package.json b/package.json index 221842f8..784257be 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "react": "^17.0.1", "react-dom": "^17.0.1", "react-icons": "^4.2.0", + "react-responsive": "^9.0.0-beta.4", "url-loader": "^4.1.1" }, "browserslist": { diff --git a/src/components/WikiDetail/DetailFullSize.tsx b/src/components/WikiDetail/DetailFullSize.tsx new file mode 100644 index 00000000..6e6d01cc --- /dev/null +++ b/src/components/WikiDetail/DetailFullSize.tsx @@ -0,0 +1,11 @@ +import React from 'react'; +import { WikiWord } from '../../types'; + +type DetailFullSizeProps = { + selectedWord: WikiWord | null; + onClose: () => void; +}; + +export default function DetailFullSize({ onClose }: DetailFullSizeProps) { + return <>DetailFullSize; +} diff --git a/src/components/WikiDetail/DetailModal.tsx b/src/components/WikiDetail/DetailModal.tsx new file mode 100644 index 00000000..c010e5f8 --- /dev/null +++ b/src/components/WikiDetail/DetailModal.tsx @@ -0,0 +1,86 @@ +import React from 'react'; +import styled from '@emotion/styled'; +import { WikiWord } from '../../types'; + +type DetailModalProps = { + selectedWord: WikiWord | null; + onClose: () => void; +}; + +export default function DetailModal({ + selectedWord, + onClose, +}: DetailModalProps) { + const { name, description, content } = selectedWord; + + return ( + onClose()}> + { + e.stopPropagation(); + }} + > + + {name} + + {description} + {content && ( + <> + + {content} + + )} + + + ); +} + +const Dimmed = styled.div` + position: fixed; + z-index: 100; + left: 0; + top: 0; + width: 100%; + height: 100vh; + background-color: rgba(0, 0, 0, 0.6); +`; + +const Modal = styled.div` + background-color: #ffffff; + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + max-width: 800px; + min-width: 500px; + padding: 40px; + border-radius: 12px; + box-shadow: rgba(0, 0, 0, 0.28) 0 8px 28px; + overflow: hidden; +`; + +const DetailHeader = styled.header` + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 1rem; + + svg { + font-size: 1.5rem; + } +`; + +const DetailTitle = styled.h2` + margin-bottom: 0; +`; + +const Description = styled.div` + font-weight: 500; +`; + +const Line = styled.div` + border-top: 1px solid var(--ifm-color-gray-300); + margin: 20px 0 30px; +`; + +const Content = styled.div``; diff --git a/src/components/WikiDetail/index.tsx b/src/components/WikiDetail/index.tsx new file mode 100644 index 00000000..27698f92 --- /dev/null +++ b/src/components/WikiDetail/index.tsx @@ -0,0 +1,38 @@ +import React, { useEffect } from 'react'; +import { useMediaQuery } from 'react-responsive'; +import DetailModal from './DetailModal'; +import DetailFullSize from './DetailFullSize'; +import { WikiWord } from '../../types'; + +type WikiDetailInnerProps = WikiDetailProps; + +function WikiDetailInner({ selectedWord, onClose }: WikiDetailInnerProps) { + const isMobile = useMediaQuery({ + query: '(max-width: 500px)', + }); + + useEffect(() => { + return () => { + // Todo: Clean State + }; + }, []); + + return isMobile ? ( + + ) : ( + + ); +} + +type WikiDetailProps = { + selectedWord: WikiWord | null; + onClose: () => void; +}; + +export default function WikiDetail({ selectedWord, onClose }: WikiDetailProps) { + return ( + selectedWord && ( + + ) + ); +} diff --git a/src/components/WikiTable/WikiTable.tsx b/src/components/WikiTable/WikiTable.tsx index 4e38d02c..de2da93c 100644 --- a/src/components/WikiTable/WikiTable.tsx +++ b/src/components/WikiTable/WikiTable.tsx @@ -1,16 +1,26 @@ -import React, { useEffect } from 'react'; +import React, { Dispatch, SetStateAction } from 'react'; import WikiTableRow from './WikiTableRow'; import usePagination from '../../hooks/usePagination'; -export default function WikiTable({ words = [] }: { words: string[] }) { +export default function WikiTable({ + words = [], + setSelectedWord, +}: { + words: string[]; + setSelectedWord: Dispatch>; +}) { const { onPrevious, onNext, currentPage, result, isLastPage, isFirstPage } = usePagination({ source: words, offset: 2, }); + const handleRowClick = word => { + setSelectedWord(word); + }; + return ( <> {currentPage} @@ -29,7 +39,11 @@ export default function WikiTable({ words = [] }: { words: string[] }) { {result.map(word => ( - + handleRowClick(word)} + key={word.name} + {...word} + /> ))} diff --git a/src/components/WikiTable/WikiTableRow.tsx b/src/components/WikiTable/WikiTableRow.tsx index 13e8be63..3081ff05 100644 --- a/src/components/WikiTable/WikiTableRow.tsx +++ b/src/components/WikiTable/WikiTableRow.tsx @@ -2,9 +2,17 @@ import React from 'react'; import { WikiWord } from '../../types'; -export default function WikiTableRow({ name, description }: WikiWord) { +type WikiWordType = WikiWord & { + handleRowClick: () => void; +}; + +export default function WikiTableRow({ + name, + description, + handleRowClick, +}: WikiWordType) { return ( - + {name} {description} diff --git a/src/pages/wiki/index.js b/src/pages/wiki/index.js index 5689cfae..4b05b6c9 100644 --- a/src/pages/wiki/index.js +++ b/src/pages/wiki/index.js @@ -8,12 +8,15 @@ import WikiSearch from '../../components/WikiSearch'; import useSearch from '../../hooks/useSearch'; import words from '../../../wiki.json'; +import WikiDetail from '../../components/WikiDetail'; export default function Wiki() { const { siteConfig } = useDocusaurusContext(); const [keyword, setKeyword] = useState(''); + const [selectedWord, setSelectedWord] = useState(null); const { result: searchResult, onSearch } = useSearch(words); + useEffect(() => { onSearch(keyword); }, [keyword]); @@ -26,7 +29,11 @@ export default function Wiki() {
용어사전 - + + setSelectedWord(null)} + />
); diff --git a/src/types/WikiWord.ts b/src/types/WikiWord.ts index 660ed1c8..03f54d7a 100644 --- a/src/types/WikiWord.ts +++ b/src/types/WikiWord.ts @@ -1,4 +1,5 @@ export type WikiWord = { name: string; description: string; + content?: string; }; diff --git a/yarn.lock b/yarn.lock index 7feb4b4b..791d74f2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3434,6 +3434,11 @@ css-loader@^5.1.1: schema-utils "^3.0.0" semver "^7.3.5" +css-mediaquery@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/css-mediaquery/-/css-mediaquery-0.1.2.tgz#6a2c37344928618631c54bd33cedd301da18bea0" + integrity sha1-aiw3NEkoYYYxxUvTPO3TAdoYvqA= + css-minimizer-webpack-plugin@^3.0.1: version "3.0.2" resolved "https://registry.yarnpkg.com/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.0.2.tgz#8fadbdf10128cb40227bff275a4bb47412534245" @@ -5328,6 +5333,11 @@ human-signals@^2.1.0: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== +hyphenate-style-name@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz#691879af8e220aea5750e8827db4ef62a54e361d" + integrity sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ== + iconv-lite@0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -6299,6 +6309,13 @@ markdown-escapes@^1.0.0: resolved "https://registry.yarnpkg.com/markdown-escapes/-/markdown-escapes-1.0.4.tgz#c95415ef451499d7602b91095f3c8e8975f78535" integrity sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg== +matchmediaquery@^0.3.0: + version "0.3.1" + resolved "https://registry.yarnpkg.com/matchmediaquery/-/matchmediaquery-0.3.1.tgz#8247edc47e499ebb7c58f62a9ff9ccf5b815c6d7" + integrity sha512-Hlk20WQHRIm9EE9luN1kjRjYXAQToHOIAHPJn9buxBwuhfTHoKUcX+lXBbxc85DVQfXYbEQ4HcwQdd128E3qHQ== + dependencies: + css-mediaquery "^0.1.2" + mdast-squeeze-paragraphs@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/mdast-squeeze-paragraphs/-/mdast-squeeze-paragraphs-4.0.0.tgz#7c4c114679c3bee27ef10b58e2e015be79f1ef97" @@ -7563,7 +7580,7 @@ prompts@^2.4.1: kleur "^3.0.3" sisteransi "^1.0.5" -prop-types@^15.5.0, prop-types@^15.6.2, prop-types@^15.7.2: +prop-types@^15.5.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2: version "15.7.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== @@ -7797,6 +7814,16 @@ react-loadable@^5.5.0: dependencies: prop-types "^15.5.0" +react-responsive@^9.0.0-beta.4: + version "9.0.0-beta.4" + resolved "https://registry.yarnpkg.com/react-responsive/-/react-responsive-9.0.0-beta.4.tgz#90c1f1a4048971497ca9795068af6803eabc0ddb" + integrity sha512-jQSs5kIi38T39Ku98r8s75jM6JAaNEeVrHPHTrL2EYWuv8nusV3KRQcZZ4VlfwndIRedxmpb4T8FXA9ltlCFiw== + dependencies: + hyphenate-style-name "^1.0.0" + matchmediaquery "^0.3.0" + prop-types "^15.6.1" + shallow-equal "^1.2.1" + react-router-config@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/react-router-config/-/react-router-config-5.1.1.tgz#0f4263d1a80c6b2dc7b9c1902c9526478194a988" @@ -8478,6 +8505,11 @@ shallow-clone@^3.0.0: dependencies: kind-of "^6.0.2" +shallow-equal@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/shallow-equal/-/shallow-equal-1.2.1.tgz#4c16abfa56043aa20d050324efa68940b0da79da" + integrity sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA== + shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"