diff --git a/dist/App.js b/dist/App.js new file mode 100644 index 0000000000..d8bc624244 --- /dev/null +++ b/dist/App.js @@ -0,0 +1,5 @@ +import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime"; +const App = () => { + return _jsx(_Fragment, {}); +}; +export default App; diff --git a/dist/Main.js b/dist/Main.js new file mode 100644 index 0000000000..40062bf33b --- /dev/null +++ b/dist/Main.js @@ -0,0 +1,9 @@ +import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; +import { BrowserRouter, Routes, Route } from "react-router-dom"; +import Shared from "./pages/Shared"; +import Folder from "./pages/Folder"; +import { Home } from "./pages/Home"; +function Main() { + return (_jsx(BrowserRouter, { children: _jsxs(Routes, { children: [_jsx(Route, { path: "/", element: _jsx(Home, {}) }), _jsx(Route, { path: "/shared", element: _jsx(Shared, {}) }), _jsx(Route, { path: "/folder", element: _jsx(Folder, {}) })] }) })); +} +export default Main; diff --git a/dist/api/api.js b/dist/api/api.js new file mode 100644 index 0000000000..70c1f8cc4e --- /dev/null +++ b/dist/api/api.js @@ -0,0 +1,45 @@ +const BASE_URL = "https://bootcamp-api.codeit.kr/api/"; +export const getFolderInfo = async () => { + try { + const response = await fetch(`${BASE_URL}sample/folder`); + const result = await response.json(); + return result; + } + catch (error) { + console.log(error); + } +}; +export const getUserInfo = async () => { + try { + const response = await fetch(`${BASE_URL}sample/user`); + const result = await response.json(); + return result; + } + catch (error) { + console.log(error); + } +}; +export const getFolderList = async () => { + try { + const response = await fetch(`${BASE_URL}users/1/folders`); + const result = await response.json(); + // console.log(result); + return result; + } + catch (error) { + console.log(error); + } +}; +export const getAllLinkData = async (id) => { + const url = id + ? `${BASE_URL}users/1/folders/${id}` + : `${BASE_URL}users/1/links`; + try { + const response = await fetch(url); + const result = await response.json(); + return result; + } + catch (error) { + console.log(error); + } +}; diff --git a/dist/components/Folder/FolderInput.js b/dist/components/Folder/FolderInput.js new file mode 100644 index 0000000000..43d64f1394 --- /dev/null +++ b/dist/components/Folder/FolderInput.js @@ -0,0 +1,76 @@ +import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; +import { styled } from "styled-components"; +import Link from "../../assets/icons/link.svg"; +import { BlueButton } from "../common/BlueButton"; +import { forwardRef } from "react"; +const FolderInput = forwardRef(({ setIsVisible, $isAddLinkVisible }, ref) => { + const onAddLinkButtonClick = () => { + setIsVisible("폴더 추가"); + }; + return (_jsx(BackGround, { ref: ref, "$isAddLinkVisible": $isAddLinkVisible, children: !$isAddLinkVisible ? (_jsx(BackGroundFixed, { children: _jsxs(InputBoxFixed, { children: [_jsx("img", { src: Link, alt: "LinkIcon" }), _jsx(Input, { placeholder: "\uB9C1\uD06C\uB97C \uCD94\uAC00\uD574 \uBCF4\uC138\uC694." }), _jsx(BlueButton, { width: "80px", height: "auto", padding: "10px 16px", margin: "0px", text: "\uCD94\uAC00\uD558\uAE30", fontSize: "", radius: "8px", onBtnHandle: () => onAddLinkButtonClick() })] }) })) : (_jsxs(InputBox, { "$isAddLinkVisible": $isAddLinkVisible, children: [_jsx("img", { src: Link, alt: "LinkIcon" }), _jsx(Input, { placeholder: "\uB9C1\uD06C\uB97C \uCD94\uAC00\uD574 \uBCF4\uC138\uC694." }), _jsx(BlueButton, { width: "80px", height: "auto", padding: "10px 16px", margin: "0px", text: "\uCD94\uAC00\uD558\uAE30", fontSize: "", radius: "8px", onBtnHandle: () => onAddLinkButtonClick() })] })) })); +}); +export default FolderInput; +const BackGround = styled.div ` + background-color: var(--Grey_100); + display: flex; + justify-content: center; + position: relative; +`; +const BackGroundFixed = styled.div ` + background-color: var(--Grey_100); + display: flex; + justify-content: center; + position: fixed; + left: 0; + right: 0; + bottom: 0; + z-index: 9999; +`; +const InputBox = styled.div ` + width: 800px; + padding: 16px 20px; + border-radius: 15px; + border: 1px solid var(--Linkbrary-primary-color, #6d6afe); + background: var(--Linkbrary-white, #fff); + margin: 60px auto 90px; + display: flex; + flex-direction: row; + + @media (max-width: 1124px) { + width: 704px; + } + @media (max-width: 774px) { + width: 325px; + } +`; +const InputBoxFixed = styled(InputBox) ` + margin: 24px auto; + + @media (max-width: 360px) { + margin: 16px auto; + } +`; +const Input = styled.input ` + width: 100%; + border: none; + margin-left: 12px; + + &:focus { + outline: none; + } + + &::placeholder { + color: #9fa6b2; + font-family: Pretendard; + font-size: 16px; + font-style: normal; + font-weight: 400; + line-height: 24px; + } + + @media (max-width: 774px) { + &::placeholder { + font-size: 14px; + } + } +`; diff --git a/dist/components/Folder/FolderTitle.js b/dist/components/Folder/FolderTitle.js new file mode 100644 index 0000000000..5d79fd9736 --- /dev/null +++ b/dist/components/Folder/FolderTitle.js @@ -0,0 +1,71 @@ +import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; +import styled from "styled-components"; +import share from "../../assets/icons/share.svg"; +import pen from "../../assets/icons/pen.svg"; +import trash from "../../assets/icons/trash.svg"; +const FolderTitle = ({ titleName, setIsModal }) => { + return (_jsxs(Container, { children: [_jsx(Title, { children: titleName }), titleName !== "전체" && (_jsxs(OptionBox, { children: [_jsxs(Option, { onClick: () => { + setIsModal("공유"); + }, children: [_jsx(OptionIcon, { src: share }), _jsx(OptionText, { children: "\uACF5\uC720" })] }), _jsxs(Option, { onClick: () => { + setIsModal("이름 변경"); + }, children: [_jsx(OptionIcon, { src: pen }), _jsx(OptionText, { children: "\uC774\uB984 \uBCC0\uACBD" })] }), _jsxs(Option, { onClick: () => { + setIsModal("삭제"); + }, children: [_jsx(OptionIcon, { src: trash }), _jsx(OptionText, { children: "\uC0AD\uC81C" })] })] }))] })); +}; +const Container = styled.div ` + width: 1060px; + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + margin: 24px auto; + + @media (max-width: 1124px) { + width: 704px; + } + @media (max-width: 774px) { + width: 325px; + flex-direction: column; + align-items: flex-start; + gap: 12px; + margin: 28px auto 20px; + } +`; +const Title = styled.span ` + color: #000; + font-family: Pretendard; + font-size: 24px; + font-style: normal; + font-weight: 600; + line-height: normal; + letter-spacing: -0.2px; +`; +const OptionBox = styled.div ` + display: flex; + flex-direction: row; + align-items: center; + gap: 12px; +`; +const Option = styled.div ` + display: flex; + flex-direction: row; + align-items: center; + gap: 4px; + + &:hover { + cursor: pointer; + } +`; +const OptionIcon = styled.img ` + width: 18px; + height: 18px; +`; +const OptionText = styled.span ` + color: #9fa6b2; + font-family: Pretendard; + font-size: 14px; + font-style: normal; + font-weight: 600; + line-height: normal; +`; +export default FolderTitle; diff --git a/dist/components/Folder/Menus.js b/dist/components/Folder/Menus.js new file mode 100644 index 0000000000..0343ce3082 --- /dev/null +++ b/dist/components/Folder/Menus.js @@ -0,0 +1,94 @@ +import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; +import { useState } from "react"; +import { getFolderList } from "../../api/api"; +import GlobalStyle from "../common/GlobalStyle"; +import styled from "styled-components"; +import union from "../../assets/icons/Union.svg"; +import { COLORS } from "../../constants/colors"; +import { useGetPromise } from "../../hooks/uesGetPromise"; +const Menus = ({ changeTitle, changeID, setIsVisible }) => { + const listsData = useGetPromise(getFolderList); + const lists = listsData?.data ?? []; + if (lists[0]) { + lists[0].name === "전체" || lists.unshift({ id: 0, name: "전체" }); + } + const initialButtonColors = lists.reduce((colors, list) => { + colors[list.name] = COLORS.White; + return colors; + }, {}); + const [buttonColors, setButtonColors] = useState(initialButtonColors); + const handleClick = async (name, id) => { + changeTitle(name); + changeID(id); + setButtonColors((prevColors) => { + return { + ...initialButtonColors, + [name]: prevColors[name] === COLORS.White ? COLORS.Primary : COLORS.White, + }; + }); + }; + return (_jsxs(Container, { children: [_jsx(GlobalStyle, {}), _jsx(ButtonDiv, { children: lists.map((val) => (_jsx(Button, { onClick: () => handleClick(val.name, val.id), color: buttonColors[val.name], id: val.name, children: val.name }, val.id))) }), _jsxs(AddFolderDiv, { onClick: () => setIsVisible("폴더 추가"), children: [_jsx(AddFolder, { children: "\uD3F4\uB354 \uCD94\uAC00" }), _jsx("img", { src: union, alt: "unionIcon" })] })] })); +}; +const Container = styled.div ` + width: 1080px; + margin: 0px auto; + display: flex; + padding: 8px 12px; + flex-direction: row; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + + @media (max-width: 1124px) { + width: 704px; + } + @media (max-width: 774px) { + width: 325px; + padding: 0px; + } +`; +const ButtonDiv = styled.div ` + display: flex; + flex-direction: row; + gap: 8px; + flex-wrap: wrap; + gap: 12px 8px; +`; +const Button = styled.button ` + min-width: max-content; + padding: 8px 12px; + border-radius: 5px; + border: 1px solid ${COLORS.Primary}; + background-color: ${({ color }) => color || COLORS.White}; + color: ${({ color = COLORS.White }) => color === COLORS.White ? "#000000" : "#FFFFFF"}; + transition: all 0.3s ease-in-out; + + &:hover { + cursor: pointer; + } +`; +const AddFolderDiv = styled.div ` + margin: 8px; + display: flex; + flex-direction: row; + + &:hover { + cursor: pointer; + } + + @media (max-width: 774px) { + display: none; + } +`; +const AddFolder = styled.span ` + color: #6d6afe; + text-align: center; + font-family: Pretendard; + font-size: 16px; + font-style: normal; + font-weight: 500; + line-height: normal; + letter-spacing: -0.3px; + margin-right: 4px; +`; +export default Menus; diff --git a/dist/components/common/BlueButton.js b/dist/components/common/BlueButton.js new file mode 100644 index 0000000000..16eab82c9d --- /dev/null +++ b/dist/components/common/BlueButton.js @@ -0,0 +1,23 @@ +import { jsx as _jsx } from "react/jsx-runtime"; +import { styled } from "styled-components"; +import { COLORS } from "../../constants/colors"; +export const BlueButton = ({ text, width, height, margin, padding, fontSize, radius, onBtnHandle, }) => { + return (_jsx(Button, { width: width, height: height, margin: margin, padding: padding, color: COLORS.White, fontSize: fontSize, radius: radius, onClick: () => (onBtnHandle ? onBtnHandle() : null), children: text })); +}; +const Button = styled.button ` + display: block; + width: ${({ width }) => width || "auto"}; + min-width: max-content; + height: ${({ height }) => height || "auto"}; + border: 0px; + border-radius: ${({ radius }) => radius || "0px"}; + margin: ${({ margin }) => margin || "auto"}; + padding: ${({ padding }) => padding || "auto"}; + background: linear-gradient(91deg, #6d6afe 0.12%, #6ae3fe 101.84%); + cursor: pointer; + + color: ${({ color }) => color}; + font-size: ${({ fontSize }) => fontSize || "14px"}; + font-weight: 600; + line-height: 21.6px; +`; diff --git a/dist/components/common/FolderItem.js b/dist/components/common/FolderItem.js new file mode 100644 index 0000000000..83859adebf --- /dev/null +++ b/dist/components/common/FolderItem.js @@ -0,0 +1,111 @@ +import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; +import styled from "styled-components"; +import { useState } from "react"; +import { CalcTime } from "../../utils/calculator"; +import { ReactComponent as Star } from "../../assets/icons/card_star.svg"; +import { ReactComponent as Kebab } from "../../assets/icons/kebab.svg"; +import logo from "../../assets/icons/logo.png"; +import { PopOver } from "./modals/PopOver"; +import "../../styles/shared.css"; +function FolderItem({ item, $isModalVisible, setIsModalVisible }) { + const [isHovering, setIsHovering] = useState(false); + const { imageSource, createdAt, description, url, id } = item; + const { created_at, favorite, image_source } = item; + const [isPopOverVisible, setIsPopOverVisible] = useState(false); + let time = ""; + let img_src = ""; + if (created_at) { + time = CalcTime(created_at); + img_src = image_source; + } + else { + time = CalcTime(createdAt); + img_src = imageSource; + } + const handleMouseOver = () => { + setIsHovering(true); + }; + const handleMouseOut = () => { + setIsHovering(false); + }; + return (_jsx("a", { href: url, target: "_blank", rel: "noreferrer", children: _jsxs(Folder, { onMouseOver: handleMouseOver, onMouseOut: handleMouseOut, children: [_jsxs(ImageContainer, { children: [img_src ? (_jsx("img", { src: img_src, alt: id, className: `folderImage ${isHovering ? "grow" : "folder-img"}` })) : (_jsx(DefaultImage, { children: _jsx("img", { src: logo, alt: "logo" }) })), _jsx(Star, { className: "star", fill: favorite ? "purple" : "black" })] }), _jsxs(TextBox, { children: [_jsxs(TimeContainer, { children: [_jsx(TimeText, { children: time }), _jsx(Kebab, { onClick: (e) => { + e.preventDefault(); + setIsPopOverVisible(!isPopOverVisible); + } }), _jsx(PopOver, { "$isPopOverVisible": isPopOverVisible, setIsPopOverVisible: setIsPopOverVisible, "$options": ["삭제하기", "폴더에 추가"], "$modalType": ["삭제", "폴더에 추가"], "$top": "20px", "$right": "-70px", "$isModalVisible": $isModalVisible, setIsModalVisible: setIsModalVisible })] }), _jsx(Description, { children: description }), _jsx(DateText, { children: "2023. 3. 26" })] })] }) })); +} +const Folder = styled.div ` + display: flex; + flex-direction: column; + justify-content: space-between; + border-radius: 15px; + box-shadow: 0px 5px 25px 0px rgba(0, 0, 0, 0.08); + position: relative; +`; +const ImageContainer = styled.div ` + width: 100%; + height: 230px; + overflow: hidden; + margin: 0px; + border-radius: 15px 15px 0px 0px; + display: flex; + justify-content: center; + align-items: center; +`; +const DefaultImage = styled.div ` + width: 100%; + height: 100%; + background-color: #dddfff; + display: flex; + justify-content: center; + align-items: center; +`; +const TextBox = styled.div ` + width: 100%; + height: 135px; + display: flex; + flex-direction: column; + gap: 10px; + padding: 15px 20px; + border-radius: 0px 0px 15px 15px; +`; +const Description = styled.div ` + width: 100%; + height: 49px; + font-family: Pretendard; + font-size: 16px; + font-style: normal; + font-weight: 400; + line-height: 24px; + margin: 0px; + color: #000; + + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + overflow: hidden; +`; +const TimeContainer = styled.div ` + display: flex; + flex-direction: row; + justify-content: space-between; + position: relative; +`; +const TimeText = styled.span ` + font-size: 13px; + font-weight: 400; + color: #666; + margin: 0px; +`; +const DateText = styled.span ` + overflow: hidden; + color: #333; + text-overflow: ellipsis; + white-space: nowrap; + font-family: Pretendard; + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: normal; + margin: 0px; +`; +export default FolderItem; diff --git a/dist/components/common/FolderList.js b/dist/components/common/FolderList.js new file mode 100644 index 0000000000..6e06ce1fab --- /dev/null +++ b/dist/components/common/FolderList.js @@ -0,0 +1,8 @@ +import { jsx as _jsx } from "react/jsx-runtime"; +import FolderItem from "./FolderItem"; +function FolderList({ items, $isModalVisible, setIsModalVisible }) { + return (_jsx("article", { children: _jsx("div", { className: "folders-gridBox", children: items.map((item) => { + return (_jsx(FolderItem, { item: item, "$isModalVisible": $isModalVisible, setIsModalVisible: setIsModalVisible }, item.id)); + }) }) })); +} +export default FolderList; diff --git a/dist/components/common/FooterElement.js b/dist/components/common/FooterElement.js new file mode 100644 index 0000000000..c17b975e5c --- /dev/null +++ b/dist/components/common/FooterElement.js @@ -0,0 +1,10 @@ +import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; +import facebookIcon from "../../assets/icons/icon_facebook.png"; +import twitterIcon from "../../assets/icons/icon_twitter.png"; +import youtubeIcon from "../../assets/icons/icon_youtube.png"; +import instagramIcon from "../../assets/icons/icon_instagram.png"; +import "../../styles/common.css"; +function FooterElement() { + return (_jsx("footer", { children: _jsxs("div", { className: "footer-frame", children: [_jsx("div", { id: "footer-codeit", children: _jsx("p", { children: "@codeit - 2023" }) }), _jsxs("div", { id: "footer-notice", children: [_jsx("a", { href: "privacy.html", children: _jsx("p", { children: "Privacy Policy" }) }), _jsx("a", { href: "faq.html", children: _jsx("p", { children: "FAQ" }) })] }), _jsxs("div", { id: "footer-icons", children: [_jsx("a", { href: "https://www.facebook.com/", target: "_blank", rel: "noopener noreferrer", children: _jsx("img", { src: facebookIcon, alt: "facebook_icon" }) }), _jsx("a", { href: "https://twitter.com/", target: "_blank", rel: "noopener noreferrer", children: _jsx("img", { src: twitterIcon, alt: "twitter`_icon" }) }), _jsx("a", { href: "https://www.youtube.com/", target: "_blank", rel: "noopener noreferrer", children: _jsx("img", { src: youtubeIcon, alt: "youtube_icon" }) }), _jsx("a", { href: "https://www.instagram.com/", target: "_blank", rel: "noopener noreferrer", children: _jsx("img", { src: instagramIcon, alt: "instagram_icon" }) })] })] }) })); +} +export default FooterElement; diff --git a/dist/components/common/GlobalStyle.js b/dist/components/common/GlobalStyle.js new file mode 100644 index 0000000000..9184bb91ab --- /dev/null +++ b/dist/components/common/GlobalStyle.js @@ -0,0 +1,9 @@ +import { createGlobalStyle } from "styled-components"; +const GlobalStyle = createGlobalStyle ` + * { + margin: 0; + padding: 0; + box-sizing: border-box; + } +`; +export default GlobalStyle; diff --git a/dist/components/common/HeaderElement.js b/dist/components/common/HeaderElement.js new file mode 100644 index 0000000000..3ddc26a9aa --- /dev/null +++ b/dist/components/common/HeaderElement.js @@ -0,0 +1,31 @@ +import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; +import styled from "styled-components"; +import logo from "../../assets/Linkbrary.png"; +import profile from "../../assets/icons/icon_myprofile.png"; +import { getUserInfo } from "../../api/api"; +import { useGetPromise } from "../../hooks/uesGetPromise"; +import "../../styles/common.css"; +function HeaderElement({ $positionval }) { + const user = useGetPromise(getUserInfo); + const email = user?.email; + const profileImageSource = user?.profileImageSource; + return (_jsxs(Header, { "$positionval": $positionval, children: [_jsx("img", { src: logo, alt: "logo" }), _jsx("div", { className: "myProfile", children: user ? (_jsxs("div", { id: "myProfileName", children: [_jsx("div", { id: "myProfile-back_img", children: _jsx("img", { src: profileImageSource ? profileImageSource : profile, id: "myProfile-img", alt: "myProfile-img" }) }), _jsx("span", { id: "myEmail", children: email })] })) : (_jsx("a", { href: "/signup.html", children: _jsx("button", { id: "LoginBtn", type: "button", children: "\uB85C\uADF8\uC778" }) })) })] })); +} +const Header = styled.div ` + background-color: var(--Grey_100); + padding: 20px 200px; + position: ${({ $positionval }) => ($positionval ? $positionval : "sticky")}; + top: 0; + z-index: 2; + display: flex; + justify-content: space-between; + align-items: center; + + @media (max-width: 1124px) { + padding: 32px; + } + @media (max-width: 774px) { + padding: 18px 32px; + } +`; +export default HeaderElement; diff --git a/dist/components/common/Input.js b/dist/components/common/Input.js new file mode 100644 index 0000000000..5899f6a0c4 --- /dev/null +++ b/dist/components/common/Input.js @@ -0,0 +1,32 @@ +import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; +import styled from "styled-components"; +import searchIcon from "../../assets/icons/icon_search.png"; +import { ReactComponent as Delete } from "../../assets/icons/delete.svg"; +const Input = ({ setInputValue, inputValue, onEnterButtonHandle }) => { + const onClickDeleteButtonHandle = () => { + setInputValue(""); + }; + const onKeyPressHandle = (e) => { + if (e.key === "Enter") { + onEnterButtonHandle(); + } + }; + return (_jsxs("div", { id: "search-bar", children: [_jsx("img", { src: searchIcon, alt: "searchIcon" }), _jsx("input", { type: "text", placeholder: "\uB9C1\uD06C\uB97C \uAC80\uC0C9\uD574 \uBCF4\uC138\uC694.", value: inputValue, onChange: (e) => setInputValue(e.target.value), onKeyDown: onKeyPressHandle }), _jsx(DeleteAllButton, { onClick: () => onClickDeleteButtonHandle(), children: _jsx(Delete, {}) })] })); +}; +const DeleteAllButton = styled.button ` + width: 25px; + height: 25px; + border-radius: 999px; + border: none; + background-color: #ccd5e3; + display: flex; + justify-content: center; + align-items: center; + color: white; + + @media (max-width: 767px) { + width: 29px; + height: 25px; + } +`; +export default Input; diff --git a/dist/components/common/RedButton.js b/dist/components/common/RedButton.js new file mode 100644 index 0000000000..5b99f31364 --- /dev/null +++ b/dist/components/common/RedButton.js @@ -0,0 +1,22 @@ +import { jsx as _jsx } from "react/jsx-runtime"; +import { styled } from "styled-components"; +import { COLORS } from "../../constants/colors"; +export const RedButton = ({ text, width, height, margin, padding, fontSize, radius, }) => { + return (_jsx(Button, { width: width, height: height, margin: margin, padding: padding, color: COLORS.White, fontSize: fontSize, radius: radius, children: text })); +}; +const Button = styled.button ` + display: block; + width: ${({ width }) => width || "auto"}; + height: ${({ height }) => height || "auto"}; + border: 0px; + border-radius: ${({ radius }) => radius || "0px"}; + margin: ${({ margin }) => margin || "auto"}; + padding: ${({ padding }) => padding || "auto"}; + background: ${COLORS.Red}; + cursor: pointer; + + color: ${({ color }) => color}; + font-size: ${({ fontSize }) => fontSize || "14px"}; + font-weight: 600; + line-height: 21.6px; +`; diff --git a/dist/components/common/modals/AddFolderModal.js b/dist/components/common/modals/AddFolderModal.js new file mode 100644 index 0000000000..74173de2f1 --- /dev/null +++ b/dist/components/common/modals/AddFolderModal.js @@ -0,0 +1,73 @@ +import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; +import styled from "styled-components"; +import { COLORS } from "../../../constants/colors"; +import { BlueButton } from "../BlueButton"; +import closeIcon from "../../../assets/icons/closeModal.png"; +export const AddFolderModal = ({ $isModalVisible, setIsModalVisible }) => { + const handleCloseBtn = () => { + setIsModalVisible(null); + }; + return (_jsx(Background, { "$isVisible": $isModalVisible, children: _jsxs(Modal, { children: [_jsx(Close, { onClick: () => handleCloseBtn(), children: _jsx("img", { src: closeIcon, alt: closeIcon }) }), _jsx(Title, { children: "\uD3F4\uB354 \uCD94\uAC00" }), _jsx(Input, { placeholder: "\uB0B4\uC6A9 \uC785\uB825" }), _jsx(BlueButton, { text: "\uCD94\uAC00\uD558\uAE30", width: "280px", height: "auto", margin: "0px", padding: "16px 20px", fontSize: "16px", radius: "8px", onBtnHandle: () => { } })] }) })); +}; +const Background = styled.div ` + display: ${({ $isVisible }) => $isVisible === "폴더 추가" ? "block" : "none"}; + z-index: 2; + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background-color: rgba(0, 0, 0, 0.8); + transition: visibility 0.3s ease; +`; +const Modal = styled.div ` + position: absolute; + top: 20%; + left: 50%; + transform: translate(-50%, 50%); + padding: 32px 40px; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 24px; + border-radius: 15px; + background-color: ${COLORS.White}; + transition: visibility 0.3s ease; +`; +const Close = styled.button ` + border: none; + position: absolute; + top: 16px; + right: 16px; + + &:hover { + cursor: pointer; + } +`; +const Title = styled.div ` + color: #373740; + font-family: Pretendard; + font-size: 20px; + font-style: normal; + font-weight: 700; + line-height: normal; +`; +const Input = styled.input ` + width: 280px; + padding: 18px 15px; + border-radius: 8px; + border: 1px solid ${COLORS.Grey_300}; + background: ${COLORS.White}; + color: var(--Linkbrary-gray100, #373740); + font-family: Pretendard; + font-size: 16px; + font-style: normal; + font-weight: 400; + line-height: 24px; + margin-bottom: 10px; + + &:focus { + border: 1px solid ${COLORS.Primary}; + } +`; diff --git a/dist/components/common/modals/AddToFolder.js b/dist/components/common/modals/AddToFolder.js new file mode 100644 index 0000000000..14f8dc1067 --- /dev/null +++ b/dist/components/common/modals/AddToFolder.js @@ -0,0 +1,110 @@ +import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; +import { styled } from "styled-components"; +import { COLORS } from "../../../constants/colors"; +import closeIcon from "../../../assets/icons/closeModal.png"; +import { BlueButton } from "../BlueButton"; +export const AddToFolder = ({ $isModalVisible, setIsModalVisible }) => { + const handleCloseBtn = () => { + setIsModalVisible(null); + }; + return (_jsx(Background, { "$isVisible": $isModalVisible, onClick: (e) => { + e.preventDefault(); + }, children: _jsxs(Modal, { children: [_jsx(Close, { onClick: (e) => { + handleCloseBtn(); + }, children: _jsx("img", { src: closeIcon, alt: closeIcon }) }), _jsxs(Title, { children: [_jsx("h3", { children: "\uD3F4\uB354\uC5D0 \uCD94\uAC00" }), _jsx("p", { children: "\uB9C1\uD06C \uC8FC\uC18C" })] }), _jsxs(Folders, { children: [_jsxs(Folder, { children: ["\uCF54\uB529\uD301 ", _jsx("p", { children: "7\uAC1C \uB9C1\uD06C" })] }), _jsxs(Folder, { children: ["\uCC44\uC6A9 \uC0AC\uC774\uD2B8 ", _jsx("p", { children: "12\uAC1C \uB9C1\uD06C" })] }), _jsxs(Folder, { children: ["\uC720\uC6A9\uD55C \uAE00 ", _jsx("p", { children: "30\uAC1C \uB9C1\uD06C" })] }), _jsxs(Folder, { children: ["\uB098\uB9CC\uC758 \uC7A5\uC18C ", _jsx("p", { children: "3\uAC1C \uB9C1\uD06C" })] })] }), _jsx(BlueButton, { text: "\uCD94\uAC00\uD558\uAE30", width: "280px", height: "auto", margin: "0px", onBtnHandle: () => { }, padding: "16px 20px", fontSize: "16px", radius: "8px" })] }) })); +}; +const Background = styled.div ` + display: ${({ $isVisible }) => $isVisible === "폴더에 추가" ? "block" : "none"}; + z-index: 2; + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background-color: rgba(0, 0, 0, 0.8); + transition: visibility 0.3s ease; + + &:hover { + cursor: default; + } +`; +const Modal = styled.div ` + position: absolute; + top: 10%; + left: 50%; + transform: translate(-50%, 50%); + padding: 32px 40px; + background-color: #fff; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 24px; + border-radius: 15px; + background: ${COLORS.White}; + transition: visibility 0.3s ease; +`; +const Close = styled.button ` + border: none; + position: absolute; + top: 16px; + right: 16px; + + &:hover { + cursor: pointer; + } +`; +const Title = styled.div ` + text-align: center; + font-family: Pretendard; + font-style: normal; + + & > h3 { + color: #373740; + font-size: 20px; + font-weight: 700; + line-height: normal; + margin-bottom: 8px; + } + + & > p { + color: ${COLORS.Grey_400}; + font-size: 14px; + font-weight: 400; + line-height: 22px; /* 157.143% */ + } +`; +const Folders = styled.div ` + width: 264px; +`; +const Folder = styled.div ` + width: 100%; + padding: 8px; + display: flex; + flex-direction: row; + align-items: center; + gap: 8px; + + color: #373740; + font-family: Pretendard; + font-size: 16px; + font-style: normal; + font-weight: 400; + line-height: 24px; + margin-right: 8px; + + &:hover { + cursor: pointer; + background: ${COLORS.Grey_100}; + color: ${COLORS.Primary}; + } + + & > p { + color: ${COLORS.Grey_400}; + font-family: Pretendard; + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: normal; + } +`; diff --git a/dist/components/common/modals/DeleteModal.js b/dist/components/common/modals/DeleteModal.js new file mode 100644 index 0000000000..55542f0f50 --- /dev/null +++ b/dist/components/common/modals/DeleteModal.js @@ -0,0 +1,72 @@ +import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; +import styled from "styled-components"; +import { COLORS } from "../../../constants/colors"; +import closeIcon from "../../../assets/icons/closeModal.png"; +import { RedButton } from "../../../components/common/RedButton"; +export const DeleteModal = ({ $isModalVisible, setIsModalVisible }) => { + const handleCloseBtn = () => { + setIsModalVisible(null); + }; + return (_jsx(Background, { "$isVisible": $isModalVisible, children: _jsxs(Modal, { children: [_jsx(Close, { onClick: (e) => { + e.preventDefault(); + handleCloseBtn(); + }, children: _jsx("img", { src: closeIcon, alt: closeIcon }) }), _jsxs(Title, { children: [_jsx("h3", { children: "\uD3F4\uB354 \uC0AD\uC81C" }), _jsx("p", { children: "\uD3F4\uB354\uBA85" })] }), _jsx(RedButton, { text: "\uC0AD\uC81C\uD558\uAE30", width: "280px", height: "auto", margin: "0px", padding: "16px 20px", fontSize: "16px", radius: "8px" })] }) })); +}; +const Background = styled.div ` + display: ${({ $isVisible }) => ($isVisible === "삭제" ? "block" : "none")}; + z-index: 2; + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background-color: rgba(0, 0, 0, 0.8); + transition: visibility 0.3s ease; +`; +const Modal = styled.div ` + position: absolute; + top: 30%; + left: 50%; + transform: translate(-50%, 50%); + padding: 32px 40px; + background-color: #fff; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 24px; + border-radius: 15px; + background: ${COLORS.White}; + transition: visibility 0.3s ease; +`; +const Close = styled.button ` + border: none; + position: absolute; + top: 16px; + right: 16px; + + &:hover { + cursor: pointer; + } +`; +const Title = styled.div ` + & > h3 { + color: #373740; + font-family: Pretendard; + font-size: 20px; + font-style: normal; + font-weight: 700; + line-height: normal; + margin-bottom: 8px; + } + + & > p { + color: ${COLORS.Grey_400}; + text-align: center; + font-family: Pretendard; + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 22px; /* 157.143% */ + } +`; diff --git a/dist/components/common/modals/EditNameModal.js b/dist/components/common/modals/EditNameModal.js new file mode 100644 index 0000000000..834ee1b6a3 --- /dev/null +++ b/dist/components/common/modals/EditNameModal.js @@ -0,0 +1,74 @@ +import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; +import { styled } from "styled-components"; +import { COLORS } from "../../../constants/colors"; +import closeIcon from "../../../assets/icons/closeModal.png"; +import { BlueButton } from "../BlueButton"; +export const EditNameModal = ({ $isModalVisible, setIsModalVisible }) => { + const handleCloseBtn = () => { + setIsModalVisible(null); + }; + return (_jsx(Background, { "$isVisible": $isModalVisible, children: _jsxs(Modal, { children: [_jsx(Close, { onClick: () => handleCloseBtn(), children: _jsx("img", { src: closeIcon, alt: closeIcon }) }), _jsx(Title, { children: "\uD3F4\uB354 \uC774\uB984 \uBCC0\uACBD" }), _jsx(Input, {}), _jsx(BlueButton, { text: "\uBCC0\uACBD\uD558\uAE30", width: "280px", height: "auto", margin: "0px", padding: "16px 20px", fontSize: "16px", radius: "8px", onBtnHandle: () => { } })] }) })); +}; +const Background = styled.div ` + display: ${({ $isVisible }) => $isVisible === "이름 변경" ? "block" : "none"}; + z-index: 2; + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background-color: rgba(0, 0, 0, 0.8); + transition: visibility 0.3s ease; +`; +const Modal = styled.div ` + position: absolute; + top: 30%; + left: 50%; + transform: translate(-50%, 50%); + padding: 32px 40px; + background-color: #fff; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 24px; + border-radius: 15px; + background: ${COLORS.White}; + transition: visibility 0.3s ease; +`; +const Close = styled.button ` + border: none; + position: absolute; + top: 16px; + right: 16px; + + &:hover { + cursor: pointer; + } +`; +const Title = styled.div ` + color: #373740; + font-family: Pretendard; + font-size: 20px; + font-style: normal; + font-weight: 700; + line-height: normal; +`; +const Input = styled.input ` + width: 280px; + padding: 18px 15px; + border-radius: 8px; + border: 1px solid ${COLORS.Primary}; + background: ${COLORS.White}; + color: var(--Linkbrary-gray100, #373740); + font-family: Pretendard; + font-size: 16px; + font-style: normal; + font-weight: 400; + line-height: 24px; + margin-bottom: 10px; + + &:focus { + border: 1px solid ${COLORS.Primary}; + } +`; diff --git a/dist/components/common/modals/PopOver.js b/dist/components/common/modals/PopOver.js new file mode 100644 index 0000000000..7adc664ef8 --- /dev/null +++ b/dist/components/common/modals/PopOver.js @@ -0,0 +1,38 @@ +import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime"; +import { styled } from "styled-components"; +import { COLORS } from "../../../constants/colors"; +import { DeleteModal } from "../../../components/common/modals/DeleteModal"; +import { AddToFolder } from "../../../components/common/modals/AddToFolder"; +export const PopOver = ({ $isPopOverVisible, setIsPopOverVisible, $options, $modalType, $top, $right, $isModalVisible, setIsModalVisible, }) => { + return (_jsxs(_Fragment, { children: [_jsx(DeleteModal, { "$isModalVisible": $isModalVisible, setIsModalVisible: setIsModalVisible }), _jsx(AddToFolder, { "$isModalVisible": $isModalVisible, setIsModalVisible: setIsModalVisible }), _jsx(MenuOptions, { "$isVisible": $isPopOverVisible, "$top": $top, "$right": $right, children: $options.map((option, index) => (_jsx(Option, { onClick: (e) => { + e.preventDefault(); + setIsModalVisible($modalType[index]); + }, children: option }, option))) })] })); +}; +const MenuOptions = styled.div ` + width: 100px; + position: absolute; + right: ${({ $right }) => $right ?? 0}; + top: ${({ $top }) => $top ?? 0}; + border: 1px; + display: ${({ $isVisible }) => ($isVisible ? "block" : "none")}; + background-color: #fff; + z-index: 1; +`; +const Option = styled.p ` + padding: 7px 12px; + background-color: #fff; + color: #333236; + font-family: Pretendard; + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: normal; + text-align: center; + + &:hover { + cursor: pointer; + color: ${COLORS.Primary}; + background-color: ${COLORS.Grey_100}; + } +`; diff --git a/dist/components/common/modals/SharedModal.js b/dist/components/common/modals/SharedModal.js new file mode 100644 index 0000000000..98c78e0f88 --- /dev/null +++ b/dist/components/common/modals/SharedModal.js @@ -0,0 +1,124 @@ +import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; +import styled from "styled-components"; +import { COLORS } from "../../../constants/colors"; +import closeIcon from "../../../assets/icons/closeModal.png"; +import kakao from "../../../assets/icons/icon_kakao.png"; +import facebook from "../../../assets/icons/icon_facebook.png"; +import link from "../../../assets/icons/link.png"; +export const SharedModal = ({ $isModalVisible, setIsModalVisible }) => { + const ICONS = [ + { + name: "카카오톡", + backgroundColor: "#F5E14B", + imgUrl: kakao, + }, + { + name: "페이스북", + backgroundColor: "#1877F2", + imgUrl: facebook, + }, + { + name: "링크 복사", + imgUrl: link, + }, + ]; + const handleCloseBtn = () => { + setIsModalVisible(null); + }; + return (_jsx(Background, { "$isVisible": $isModalVisible, children: _jsxs(Modal, { children: [_jsx(Close, { onClick: () => handleCloseBtn(), children: _jsx("img", { src: closeIcon, alt: closeIcon }) }), _jsxs(Title, { children: [_jsx("h3", { children: "\uD3F4\uB354 \uACF5\uC720" }), _jsx("p", { children: "\uD3F4\uB354\uBA85" })] }), _jsx(Icons, { children: ICONS.map((icon) => (_jsxs(Icon, { children: [_jsx(IconImg, { "$backgroundColor": icon.backgroundColor, children: _jsx("img", { src: icon.imgUrl, alt: icon.name }) }), _jsx("span", { children: icon.name })] }, icon.name))) })] }) })); +}; +const Background = styled.div ` + display: ${({ $isVisible }) => ($isVisible === "공유" ? "block" : "none")}; + z-index: 2; + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background-color: rgba(0, 0, 0, 0.8); + transition: visibility 0.3s ease; +`; +const Modal = styled.div ` + position: absolute; + top: 30%; + left: 50%; + transform: translate(-50%, 50%); + padding: 32px 40px; + background-color: #fff; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 24px; + border-radius: 15px; + background: ${COLORS.White}; + transition: visibility 0.3s ease; +`; +const Close = styled.button ` + border: none; + position: absolute; + top: 16px; + right: 16px; + + &:hover { + cursor: pointer; + } +`; +const Title = styled.div ` + & > h3 { + color: #373740; + font-family: Pretendard; + font-size: 20px; + font-style: normal; + font-weight: 700; + line-height: normal; + margin-bottom: 8px; + } + + & > p { + color: ${COLORS.Grey_400}; + text-align: center; + font-family: Pretendard; + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 22px; /* 157.143% */ + } +`; +const Icons = styled.div ` + display: flex; + flex-direction: row; + gap: 32px; + padding: 0px 32px; +`; +const Icon = styled.div ` + width: max-content; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 10px; + + & > span { + color: #373740; + text-align: center; + font-size: 13px; + font-style: normal; + font-weight: 400; + line-height: 15px; + } +`; +const IconImg = styled.div ` + width: 42px; + height: 42px; + background-color: ${({ $backgroundColor }) => $backgroundColor ?? null}; + border-radius: 40px; + display: flex; + justify-content: center; + align-items: center; + + & > img { + width: 18px; + height: 18px; + } +`; diff --git a/dist/components/home/CardFrame.js b/dist/components/home/CardFrame.js new file mode 100644 index 0000000000..14a5b9a276 --- /dev/null +++ b/dist/components/home/CardFrame.js @@ -0,0 +1,124 @@ +import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime"; +import { styled } from "styled-components"; +import card1 from "../../assets/cards/card1.png"; +import card2 from "../../assets/cards/card2.png"; +import card3 from "../../assets/cards/card3.png"; +import card4 from "../../assets/cards/card4.png"; +import { COLORS } from "../../constants/colors"; +export const CardFrame = ({ num, reversed, height }) => { + const card = CARDS[`card${num}`]; + return (_jsx(Section, { children: _jsx(SectionFrame, { reversed: reversed, children: reversed ? (_jsxs(_Fragment, { children: [_jsxs(Description, { children: [card.headline, _jsx(SectionCardsMobile, { children: _jsx("img", { src: card.imgUrl, alt: `card${num}Img` }) }), card.description] }), _jsx(SectionCards, { children: _jsx("img", { src: card.imgUrl, alt: `card${num}Img` }) })] })) : (_jsxs(_Fragment, { children: [_jsx(SectionCards, { children: _jsx("img", { src: card.imgUrl, alt: `card${num}Img` }) }), _jsxs(Description, { children: [card.headline, _jsx(SectionCardsMobile, { children: _jsx("img", { src: card.imgUrl, alt: `card${num}Img` }) }), card.description] })] })) }) })); +}; +const Section = styled.section ` + width: 100%; + height: ${({ height }) => height ?? 550}px; + display: flex; + background-color: ${COLORS.White}; + + @media (max-width: 1124px) { + height: 445px; + } + @media (max-width: 774px) { + height: auto; + } +`; +const SectionFrame = styled.div ` + display: flex; + align-items: center; + justify-content: center; + gap: 157px; + + @media (max-width: 1124px) { + height: 315px; + gap: 50px; + } + @media (max-width: 774px) { + flex-direction: column; + height: auto; + } +`; +const SectionCards = styled.div ` + width: 550px; + height: 450px; + display: flex; + justify-content: center; + align-items: center; + + @media (max-width: 1124px) { + width: 384px; + height: 315px; + } + @media (max-width: 774px) { + width: 384px; + height: 315px; + display: none; + } + + & > img { + width: 100%; + } +`; +const SectionCardsMobile = styled(SectionCards) ` + display: none; + + @media (max-width: 774px) { + display: block; + } +`; +const Description = styled.div ` + height: 100%; + display: flex; + flex-direction: column; + justify-content: center; + + & > h3 { + font-family: Pretendard; + font-size: 48px; + font-style: normal; + font-weight: 700; + line-height: normal; + letter-spacing: -0.3px; + margin-bottom: 10px; + } + + @media (max-width: 774px) { + width: 100%; + padding: 40px 32px; + gap: 20px; + + & > h3 { + font-size: 24px; + margin-bottom: 0px; + } + & > h3 > br { + display: none; + } + } +`; +const StrongText = styled.span ` + background: linear-gradient(${({ color }) => color}); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; +`; +const CARDS = { + card1: { + headline: (_jsxs("h3", { children: [_jsx(StrongText, { color: "96deg, #fe8a8a 3%, #a4ceff 74.97%", children: "\uC6D0\uD558\uB294 \uB9C1\uD06C" }), "\uB97C ", _jsx("br", {}), "\uC800\uC7A5\uD558\uC138\uC694"] })), + imgUrl: card1, + description: (_jsxs("p", { children: ["\uB098\uC911\uC5D0 \uC77D\uACE0 \uC2F6\uC740 \uAE00, \uB2E4\uC2DC \uBCF4\uACE0 \uC2F6\uC740 \uC601\uC0C1,", _jsx("br", {}), "\uC0AC\uACE0 \uC2F6\uC740 \uC637, \uAE30\uC5B5\uD558\uACE0 \uC2F6\uC740 \uBAA8\uB4E0 \uAC83\uC744", _jsx("br", {}), "\uD55C \uACF5\uAC04\uC5D0 \uC800\uC7A5\uD558\uC138\uC694."] })), + }, + card2: { + headline: (_jsxs("h3", { children: ["\uB9C1\uD06C\uB97C \uD3F4\uB354\uB85C ", _jsx("br", {}), _jsx(StrongText, { color: "277deg, #6fbaff, #ffd88b", children: "\uAD00\uB9AC" }), "\uD558\uC138\uC694"] })), + imgUrl: card2, + description: (_jsxs("p", { children: ["\uB098\uB9CC\uC758 \uD3F4\uB354\uB97C \uBB34\uC81C\uD55C\uC73C\uB85C \uB9CC\uB4E4\uACE0", _jsx("br", {}), "\uB2E4\uC591\uD558\uAC8C \uD65C\uC6A9\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4."] })), + }, + card3: { + headline: (_jsxs("h3", { children: ["\uC800\uC7A5\uD55C \uB9C1\uD06C\uB97C ", _jsx("br", {}), _jsx(StrongText, { color: "99deg, #6d7ccd 27%, rgba(82, 136, 133, 0.22) 52%", children: "\uACF5\uC720" }), "\uD574 \uBCF4\uC138\uC694."] })), + imgUrl: card3, + description: (_jsxs("p", { children: ["\uC5EC\uB7EC \uB9C1\uD06C\uB97C \uD3F4\uB354\uC5D0 \uB2F4\uACE0 \uACF5\uC720\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.", _jsx("br", {}), "\uAC00\uC871,\uCE5C\uAD6C,\uB3D9\uB8CC\uB4E4\uC5D0\uAC8C \uC27D\uACE0 \uBE60\uB974\uAC8C \uB9C1\uD06C\uB97C", _jsx("br", {}), "\uACF5\uC720\uD574 \uBCF4\uC138\uC694."] })), + }, + card4: { + headline: (_jsxs("h3", { children: ["\uC800\uC7A5\uD55C \uB9C1\uD06C\uB97C ", _jsx("br", {}), _jsx(StrongText, { color: "271deg, #fe578f -79.84%, #68e8f9 107.18%", children: "\uAC80\uC0C9" }), "\uD574 \uBCF4\uC138\uC694."] })), + imgUrl: card4, + description: _jsx("p", { children: "\uC911\uC694\uD55C \uC815\uBCF4\uB4E4\uC744 \uAC80\uC0C9\uC73C\uB85C \uC27D\uAC8C \uCC3E\uC544\uBCF4\uC138\uC694." }), + }, +}; diff --git a/dist/components/home/Headline.js b/dist/components/home/Headline.js new file mode 100644 index 0000000000..e1356c77bd --- /dev/null +++ b/dist/components/home/Headline.js @@ -0,0 +1,37 @@ +import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; +import styled from "styled-components"; +import { BlueButton } from "../common/BlueButton"; +export const Headline = () => { + return (_jsxs(Container, { children: [_jsx(TextBox, { children: _jsxs(HeadlineText, { children: [_jsx(Strong, { children: "\uC138\uC0C1\uC758 \uBAA8\uB4E0 \uC815\uBCF4" }), "\uB97C", _jsx("br", {}), "\uC27D\uAC8C \uC800\uC7A5\uD558\uACE0 ", _jsx(LineBreak, {}), "\uAD00\uB9AC\uD574 \uBCF4\uC138\uC694."] }) }), _jsx(BlueButton, { text: "\uB9C1\uD06C \uCD94\uAC00\uD558\uAE30", width: "350px", height: "auto", margin: "40px auto", padding: "10px 20px", fontSize: "18px", radius: "8px", onBtnHandle: () => { } })] })); +}; +const Container = styled.div ` + height: 100%; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +`; +const TextBox = styled.p ` + width: 100%; + text-align: center; +`; +const HeadlineText = styled.span ` + font-size: 64px; + font-weight: 700; + + @media (max-width: 774px) { + font-size: 32px; + } +`; +const Strong = styled(HeadlineText) ` + background: linear-gradient(90deg, #6d6afe, #ff9f9f); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; +`; +const LineBreak = styled.br ` + display: none; + + @media (max-width: 1124px) { + display: block; + } +`; diff --git a/dist/components/shared/SharedSection.js b/dist/components/shared/SharedSection.js new file mode 100644 index 0000000000..13a27109e6 --- /dev/null +++ b/dist/components/shared/SharedSection.js @@ -0,0 +1,30 @@ +import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; +import { useState, useEffect } from "react"; +import { getFolderInfo } from "../../api/api"; +import smileIcon from "../../assets/icons/icon_smile.png"; +import styled from "styled-components"; +const SharedSection = () => { + const [folderName, setFolderName] = useState([]); + const [owner, setOwner] = useState([]); + const { profileImageSource, name } = owner; + async function handleLoad() { + const folderInfo = await getFolderInfo(); + const { name, owner } = folderInfo.folder; + setFolderName(name); + setOwner(owner); + } + useEffect(() => { + handleLoad(); + }, []); + return (_jsxs("section", { className: "codeit-mark-section", children: [_jsx(OwnerProfile, { src: profileImageSource || smileIcon, alt: "smile icon" }), _jsx("span", { children: name }), _jsx("div", { id: "favorites", children: _jsx("h1", { children: folderName }) })] })); +}; +const OwnerProfile = styled.img ` + display: flex; + justify-content: center; + width: 60px; + height: 60px; + align-items: center; + border-radius: 47px; + margin-bottom: 12px; +`; +export default SharedSection; diff --git a/dist/constants/colors.js b/dist/constants/colors.js new file mode 100644 index 0000000000..05b1b5f2e1 --- /dev/null +++ b/dist/constants/colors.js @@ -0,0 +1,11 @@ +export const COLORS = { + Primary: "#6d6afe", + Red: "#ff5b56", + Black: "#111322", + White: "#fff", + Grey_100: "#f0f6ff", + Grey_200: "#e7effb", + Grey_300: "#ccd5e3", + Grey_400: "#9fa6b2", + Grey_500: "#3e3e43", +}; diff --git a/dist/constants/errorMsg.js b/dist/constants/errorMsg.js new file mode 100644 index 0000000000..83f2017581 --- /dev/null +++ b/dist/constants/errorMsg.js @@ -0,0 +1,15 @@ +const ERROR_MESSAGE = { + email: { + empty: "이메일을 입력해주세요.", + invalid: "올바른 이메일이 아닙니다.", + check: "이메일을 확인해 주세요.", + inUse: "이미 존재하는 이메일입니다.", + }, + password: { + empty: "비밀번호을 입력해 주세요", + invalid: "비밀번호는 영문,숫자 조합 8자 이상 입력해주세요.", + check: "비밀번호을 확인해 주세요", + recheck: "비밀번호가 일치하지 않아요.", + }, +}; +export { ERROR_MESSAGE }; diff --git a/dist/hooks/uesGetPromise.js b/dist/hooks/uesGetPromise.js new file mode 100644 index 0000000000..18b34b8e8e --- /dev/null +++ b/dist/hooks/uesGetPromise.js @@ -0,0 +1,21 @@ +import { useState, useEffect, useCallback } from "react"; +export const useGetPromise = (func) => { + const [values, setValues] = useState([]); + const HandleLoad = useCallback(async () => { + let results; + try { + results = await func(); + // await console.log(results); + } + catch (error) { + console.error(error); + } + if (!results) + return; + setValues(results); + }, []); + useEffect(() => { + HandleLoad(); + }, [HandleLoad]); + return values; +}; diff --git a/dist/index.js b/dist/index.js new file mode 100644 index 0000000000..18bff61ad2 --- /dev/null +++ b/dist/index.js @@ -0,0 +1,5 @@ +import { jsx as _jsx } from "react/jsx-runtime"; +import ReactDOM from "react-dom/client"; +import Main from "./Main"; +const root = ReactDOM.createRoot(document.getElementById("root")); +root.render(_jsx(Main, {})); diff --git a/dist/pages/Folder.js b/dist/pages/Folder.js new file mode 100644 index 0000000000..c9737026b4 --- /dev/null +++ b/dist/pages/Folder.js @@ -0,0 +1,128 @@ +import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; +import { useState, useEffect, useRef } from "react"; +import styled from "styled-components"; +import { getAllLinkData } from "../api/api"; +import HeaderElement from "../components/common/HeaderElement"; +import FooterElement from "../components/common/FooterElement"; +import GlobalStyle from "../components/common/GlobalStyle"; +import FolderInput from "../components/Folder/FolderInput"; +import FolderList from "../components/common/FolderList"; +import Input from "../components/common/Input"; +import Menus from "../components/Folder/Menus"; +import FolderTitle from "../components/Folder/FolderTitle"; +import { SharedModal } from "../components/common/modals/SharedModal"; +import { EditNameModal } from "../components/common/modals/EditNameModal"; +import { DeleteModal } from "../components/common/modals/DeleteModal"; +import { AddFolderModal } from "../components/common/modals/AddFolderModal"; +import { COLORS } from "../constants/colors"; +const Folder = () => { + const [titleName, setTitleName] = useState("전체"); + const [listId, setListId] = useState(""); + const [data, setData] = useState([]); + const [isModalVisible, setIsModalVisible] = useState(null); + const [searchInputValue, setSearchInputValue] = useState(""); + useEffect(() => { + const fetchData = async () => { + try { + const response = await getAllLinkData(listId); + const result = await response.data; + setData(result); + } + catch (error) { + console.error(error); + } + }; + fetchData(); + }, [listId]); + const onSearchEnterClickHandle = () => { + findCardsByKeyword(searchInputValue); + }; + const addLinkDivRef = useRef(null); + const footerDivRef = useRef(null); + const [isAddLinkVisible, setIsAddLinkVisible] = useState(false); + const onIntersectionHandle = async (entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting) { + setIsAddLinkVisible(true); + } + else { + setIsAddLinkVisible(false); + } + }); + }; + useEffect(() => { + if (addLinkDivRef.current) { + const observer1 = new IntersectionObserver(onIntersectionHandle, { + threshold: 0.1, + }); + if (addLinkDivRef.current) { + observer1.observe(addLinkDivRef.current); + } + const observer2 = new IntersectionObserver(onIntersectionHandle, { + threshold: 0.1, + }); + if (footerDivRef.current) { + observer2.observe(footerDivRef.current); + } + return () => { + observer1.disconnect(); + observer2.disconnect(); + }; + } + }, []); + const findCardsByKeyword = (keyword) => { + const results = []; + if (data) { + data.forEach((card) => { + const cardInfo = card.title + card.description + card.url; + if (cardInfo && cardInfo.includes(keyword)) { + results.push(card); + } + else { + console.log("없음"); + } + }); + } + setData(results); + }; + return (_jsxs(Container, { children: [_jsx(SharedModal, { "$isModalVisible": isModalVisible, setIsModalVisible: setIsModalVisible }), _jsx(EditNameModal, { "$isModalVisible": isModalVisible, setIsModalVisible: setIsModalVisible }), _jsx(DeleteModal, { "$isModalVisible": isModalVisible, setIsModalVisible: setIsModalVisible }), _jsx(AddFolderModal, { "$isModalVisible": isModalVisible, setIsModalVisible: setIsModalVisible }), _jsx(GlobalStyle, {}), _jsx(HeaderElement, { "$positionval": "static" }), _jsx(FolderInput, { setIsVisible: setIsModalVisible, "$isAddLinkVisible": isAddLinkVisible, ref: addLinkDivRef }), _jsx(Input, { inputValue: searchInputValue, setInputValue: setSearchInputValue, onEnterButtonHandle: onSearchEnterClickHandle }), _jsx(Menus, { changeTitle: setTitleName, changeID: setListId, setIsVisible: setIsModalVisible }), _jsx(FolderTitle, { titleName: titleName, setIsModal: setIsModalVisible }), data[0] ? (_jsx(FolderList, { items: data, "$isModalVisible": isModalVisible, setIsModalVisible: setIsModalVisible })) : (_jsx(NoLinkMsg, { children: "\uC800\uC7A5\uB41C \uB9C1\uD06C\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4." })), _jsx(AddFolderBtn, { "$isAddLinkVisible": isAddLinkVisible, children: "\uD3F4\uB354 \uCD94\uAC00 +" }), _jsx("div", { ref: footerDivRef, children: _jsx(FooterElement, {}) })] })); +}; +const Container = styled.div ` + margin: 0px; +`; +const NoLinkMsg = styled.p ` + color: #000; + text-align: center; + font-family: Pretendard; + font-size: 16px; + font-style: normal; + font-weight: 400; + line-height: 24px; /* 150% */ + margin-top: 40px; +`; +const AddFolderBtn = styled.button ` + border: none; + border-radius: 20px; + border: 1px solid ${COLORS.White}; + background: ${COLORS.Primary}; + position: sticky; + left: 50%; + transform: translateX(-50%); + bottom: ${({ $isAddLinkVisible }) => ($isAddLinkVisible ? "50px" : "150px")}; + padding: 8px 24px; + display: none; + + color: ${COLORS.Grey_200}; + text-align: center; + font-family: Pretendard; + font-size: 16px; + font-style: normal; + font-weight: 500; + line-height: normal; + letter-spacing: -0.3px; + + @media (max-width: 375px) { + display: block; + } +`; +export default Folder; diff --git a/dist/pages/Home.js b/dist/pages/Home.js new file mode 100644 index 0000000000..b89548b649 --- /dev/null +++ b/dist/pages/Home.js @@ -0,0 +1,38 @@ +import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime"; +import styled from "styled-components"; +import HeaderElement from "../components/common/HeaderElement"; +import FooterElement from "../components/common/FooterElement"; +import { Headline } from "../components/home/Headline"; +import { CardFrame } from "../components/home/CardFrame"; +import cards_img from "../assets/cards/cards_img.png"; +import { COLORS } from "../constants/colors"; +export const Home = () => { + return (_jsxs(_Fragment, { children: [_jsx(HeaderElement, { "$positionval": "" }), _jsxs(HeadlineContainer, { children: [_jsx(Headline, {}), _jsx(CardsContainer, { children: _jsx("img", { src: cards_img, alt: cards_img }) })] }), _jsx(CardFrame, { num: 1, height: 620, reversed: true }), _jsx(CardFrame, { num: 2, height: null, reversed: false }), _jsx(CardFrame, { num: 3, height: null, reversed: true }), _jsx(CardFrame, { num: 4, height: null, reversed: false }), _jsx(FooterElement, {})] })); +}; +const HeadlineContainer = styled.div ` + padding-top: 70px; + display: flex; + flex-direction: column; + align-items: center; + background-color: ${COLORS.Grey_100}; +`; +const CardsContainer = styled.div ` + width: 1118px; + height: 590px; + overflow-y: hidden; + + & > img { + width: 100%; + margin-top: 50px; + border-radius: 25px; + } + + @media (max-width: 1124px) { + width: 698px; + height: 343px; + } + @media (max-width: 774px) { + width: 325px; + height: 160px; + } +`; diff --git a/dist/pages/Shared.js b/dist/pages/Shared.js new file mode 100644 index 0000000000..7a72b9f89c --- /dev/null +++ b/dist/pages/Shared.js @@ -0,0 +1,20 @@ +import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime"; +import { useState } from "react"; +import { getFolderInfo } from "../api/api"; +import HeaderElement from "../components/common/HeaderElement"; +import FooterElement from "../components/common/FooterElement"; +import SharedSection from "../components/shared/SharedSection"; +import Input from "../components/common/Input"; +import FolderList from "../components/common/FolderList"; +import { DeleteModal } from "../components/common/modals/DeleteModal"; +import { AddFolderModal } from "../components/common/modals/AddFolderModal"; +import { useGetPromise } from "../hooks/uesGetPromise"; +import "../styles/shared.css"; +function Shared() { + const foldersData = useGetPromise(getFolderInfo); + const folders = foldersData?.folder?.links || []; + const [searchInputValue, setSearchInputValue] = useState(""); + const [isModalVisible, setIsModalVisible] = useState(null); + return (_jsxs(_Fragment, { children: [_jsx(DeleteModal, { "$isModalVisible": isModalVisible, setIsModalVisible: setIsModalVisible }), _jsx(AddFolderModal, { "$isModalVisible": isModalVisible, setIsModalVisible: setIsModalVisible }), _jsx(HeaderElement, { "$positionval": "" }), _jsx(SharedSection, {}), _jsx(Input, { inputValue: searchInputValue, setInputValue: setSearchInputValue, onEnterButtonHandle: () => { } }), _jsx(FolderList, { items: folders, "$isModalVisible": isModalVisible, setIsModalVisible: setIsModalVisible }), _jsx(FooterElement, {})] })); +} +export default Shared; diff --git a/dist/reportWebVitals.js b/dist/reportWebVitals.js new file mode 100644 index 0000000000..d62e395879 --- /dev/null +++ b/dist/reportWebVitals.js @@ -0,0 +1,12 @@ +const reportWebVitals = (onPerfEntry) => { + if (onPerfEntry && onPerfEntry instanceof Function) { + import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { + getCLS(onPerfEntry); + getFID(onPerfEntry); + getFCP(onPerfEntry); + getLCP(onPerfEntry); + getTTFB(onPerfEntry); + }); + } +}; +export default reportWebVitals; diff --git a/src/setupTests.js b/dist/setupTests.js similarity index 100% rename from src/setupTests.js rename to dist/setupTests.js diff --git a/dist/utils/calculator.js b/dist/utils/calculator.js new file mode 100644 index 0000000000..61bafa3934 --- /dev/null +++ b/dist/utils/calculator.js @@ -0,0 +1,31 @@ +function timeToString(time) { + let diffSec = parseInt(time / 1000); + if (diffSec <= 120) { + return "1 minute ago"; + } + let diffMinute = parseInt(diffSec / 60); + if (diffMinute <= 59) { + return `${diffMinute} minutes ago`; + } + let diffHour = parseInt(diffMinute / 60); + if (diffHour <= 23) { + return `${diffHour} hours ago`; + } + let diffDay = parseInt(diffHour / 24); + if (diffDay <= 30) { + return `${diffDay} days ago`; + } + let diffMonth = parseInt(diffDay / 30); + if (diffMonth <= 11) { + return `${diffMonth} months ago`; + } + let diffYear = parseInt(diffMonth / 12); + return `${diffYear} years ago`; +} +export function CalcTime(time) { + let nowTime = Date.now(); + let writingTime = Date.parse(time); + let diffTime = nowTime - writingTime; + let sentence = timeToString(diffTime); + return sentence; +} diff --git a/package-lock.json b/package-lock.json index a1e590ee62..5f84a8f5b0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,8 +13,16 @@ "@testing-library/user-event": "^13.5.0", "react": "^18.2.0", "react-dom": "^18.2.0", - "react-scripts": "5.0.1", + "react-router-dom": "^6.22.3", + "react-scripts": "^5.0.1", + "styled-components": "^6.1.8", "web-vitals": "^2.1.4" + }, + "devDependencies": { + "@types/node": "^20.11.30", + "@types/react": "^18.2.71", + "@types/react-dom": "^18.2.22", + "typescript": "^5.4.3" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -2270,6 +2278,24 @@ "postcss-selector-parser": "^6.0.10" } }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz", + "integrity": "sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw==", + "dependencies": { + "@emotion/memoize": "^0.8.1" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" + }, + "node_modules/@emotion/unitless": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.0.tgz", + "integrity": "sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw==" + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -3241,6 +3267,14 @@ } } }, + "node_modules/@remix-run/router": { + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.3.tgz", + "integrity": "sha512-Oy8rmScVrVxWZVOpEF57ovlnhpZ8CCPlnIIumVcV9nFdiSIrus99+Lw78ekXyGvVDlIsFJbSfmSovJUhCWYV3w==", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@rollup/plugin-babel": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", @@ -4297,9 +4331,12 @@ "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" }, "node_modules/@types/node": { - "version": "20.5.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.9.tgz", - "integrity": "sha512-PcGNd//40kHAS3sTlzKB9C9XL4K0sTup8nbG5lC14kzEteTNuAFh9u5nA0o5TWnSG2r/JNPRXFVcHJIIeRlmqQ==" + "version": "20.11.30", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.30.tgz", + "integrity": "sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw==", + "dependencies": { + "undici-types": "~5.26.4" + } }, "node_modules/@types/parse-json": { "version": "4.0.0", @@ -4332,9 +4369,9 @@ "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" }, "node_modules/@types/react": { - "version": "18.2.21", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.21.tgz", - "integrity": "sha512-neFKG/sBAwGxHgXiIxnbm3/AAVQ/cMRS93hvBpg8xYRbeQSPVABp9U2bRnPf0iI4+Ucdv3plSxKK+3CW2ENJxA==", + "version": "18.2.71", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.71.tgz", + "integrity": "sha512-PxEsB9OjmQeYGffoWnYAd/r5FiJuUw2niFQHPc2v2idwh8wGPkkYzOHuinNJJY6NZqfoTCiOIizDOz38gYNsyw==", "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -4342,9 +4379,9 @@ } }, "node_modules/@types/react-dom": { - "version": "18.2.7", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.7.tgz", - "integrity": "sha512-GRaAEriuT4zp9N4p1i8BDBYmEyfo+xQ3yHjJU4eiK5NDa1RmUZG+unZABUTK4/Ox/M+GaHwb6Ow8rUITrtjszA==", + "version": "18.2.22", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.22.tgz", + "integrity": "sha512-fHkBXPeNtfvri6gdsMYyW+dW7RXFo6Ad09nLFK0VQWR7yGLai/Cyvyj696gbwYvBnhGtevUG9cET0pmUbMtoPQ==", "dependencies": { "@types/react": "*" } @@ -4412,6 +4449,11 @@ "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==" }, + "node_modules/@types/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-n4sx2bqL0mW1tvDf/loQ+aMX7GQD3lc3fkCMC55VFNDu/vBOabO+LTIeXKM14xK0ppk5TUGcWRjiSpIlUpghKw==" + }, "node_modules/@types/testing-library__jest-dom": { "version": "5.14.9", "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.9.tgz", @@ -5826,6 +5868,14 @@ "node": ">= 6" } }, + "node_modules/camelize": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", + "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/caniuse-api": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", @@ -6261,6 +6311,14 @@ "postcss": "^8.4" } }, + "node_modules/css-color-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", + "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==", + "engines": { + "node": ">=4" + } + }, "node_modules/css-declaration-sorter": { "version": "6.4.1", "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.1.tgz", @@ -6442,6 +6500,16 @@ "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==" }, + "node_modules/css-to-react-native": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", + "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", + "dependencies": { + "camelize": "^1.0.0", + "css-color-keywords": "^1.0.0", + "postcss-value-parser": "^4.0.2" + } + }, "node_modules/css-tree": { "version": "1.0.0-alpha.37", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", @@ -13085,9 +13153,9 @@ } }, "node_modules/postcss": { - "version": "8.4.29", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.29.tgz", - "integrity": "sha512-cbI+jaqIeu/VGqXEarWkRCCffhjgXc0qjBtXpqJhTBohMUjUQnbBr0xqX3vEKudc4iviTewcJo5ajcec5+wdJw==", + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", "funding": [ { "type": "opencollective", @@ -14671,6 +14739,36 @@ "node": ">=0.10.0" } }, + "node_modules/react-router": { + "version": "6.22.3", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.3.tgz", + "integrity": "sha512-dr2eb3Mj5zK2YISHK++foM9w4eBnO23eKnZEDs7c880P6oKbrjz/Svg9+nxqtHQK+oMW4OtjZca0RqPglXxguQ==", + "dependencies": { + "@remix-run/router": "1.15.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.22.3", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.3.tgz", + "integrity": "sha512-7ZILI7HjcE+p31oQvwbokjk6OA/bnFxrhJ19n82Ex9Ph8fNAq+Hm/7KchpMGlTgWhUxRHMMCut+vEtNpWpowKw==", + "dependencies": { + "@remix-run/router": "1.15.3", + "react-router": "6.22.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, "node_modules/react-scripts": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz", @@ -15485,6 +15583,11 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "node_modules/shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -15980,6 +16083,38 @@ "webpack": "^5.0.0" } }, + "node_modules/styled-components": { + "version": "6.1.8", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.8.tgz", + "integrity": "sha512-PQ6Dn+QxlWyEGCKDS71NGsXoVLKfE1c3vApkvDYS5KAK+V8fNWGhbSUEo9Gg2iaID2tjLXegEW3bZDUGpofRWw==", + "dependencies": { + "@emotion/is-prop-valid": "1.2.1", + "@emotion/unitless": "0.8.0", + "@types/stylis": "4.2.0", + "css-to-react-native": "3.2.0", + "csstype": "3.1.2", + "postcss": "8.4.31", + "shallowequal": "1.1.0", + "stylis": "4.3.1", + "tslib": "2.5.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/styled-components" + }, + "peerDependencies": { + "react": ">= 16.8.0", + "react-dom": ">= 16.8.0" + } + }, + "node_modules/styled-components/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + }, "node_modules/stylehacks": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", @@ -15995,6 +16130,11 @@ "postcss": "^8.2.15" } }, + "node_modules/stylis": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.1.tgz", + "integrity": "sha512-EQepAV+wMsIaGVGX1RECzgrcqRRU/0sYOHkeLsZ3fzHaHXZy4DaOOX0vOlGQdlsjkh3mFHAIlVimpwAs4dslyQ==" + }, "node_modules/sucrase": { "version": "3.34.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.34.0.tgz", @@ -16624,16 +16764,15 @@ } }, "node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", - "peer": true, + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz", + "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, "node_modules/unbox-primitive": { @@ -16655,6 +16794,11 @@ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz", "integrity": "sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw==" }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", diff --git a/package.json b/package.json index 7ff0d6b58c..9edb0ee0a6 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,9 @@ "@testing-library/user-event": "^13.5.0", "react": "^18.2.0", "react-dom": "^18.2.0", - "react-scripts": "5.0.1", + "react-router-dom": "^6.22.3", + "react-scripts": "^5.0.1", + "styled-components": "^6.1.8", "web-vitals": "^2.1.4" }, "scripts": { @@ -34,5 +36,11 @@ "last 1 firefox version", "last 1 safari version" ] + }, + "devDependencies": { + "@types/node": "^20.11.30", + "@types/react": "^18.2.71", + "@types/react-dom": "^18.2.22", + "typescript": "^5.4.3" } } diff --git a/public/index.html b/public/index.html index aa069f27cb..97ffe674e6 100644 --- a/public/index.html +++ b/public/index.html @@ -1,43 +1,26 @@ - +
- - +
- Edit src/App.js
and save to reload.
-
링크 주소
+7개 링크
+12개 링크
+30개 링크
+3개 링크
+폴더명
+폴더명
+
+ 나중에 읽고 싶은 글, 다시 보고 싶은 영상,
+
+ 사고 싶은 옷, 기억하고 싶은 모든 것을
+
한 공간에 저장하세요.
+
+ 나만의 폴더를 무제한으로 만들고
+
+ 다양하게 활용할 수 있습니다.
+
+ 여러 링크를 폴더에 담고 공유할 수 있습니다.
+
+ 가족,친구,동료들에게 쉽고 빠르게 링크를
+
+ 공유해 보세요.
+
중요한 정보들을 검색으로 쉽게 찾아보세요.
, + }, +}; \ No newline at end of file diff --git a/src/components/home/Headline.tsx b/src/components/home/Headline.tsx new file mode 100644 index 0000000000..7affe2e48d --- /dev/null +++ b/src/components/home/Headline.tsx @@ -0,0 +1,65 @@ +import styled from "styled-components"; +import { BlueButton } from "../common/BlueButton"; +import { Link } from 'react-router-dom'; + +export const Headline = () => { + return ( +