From 81ee2d2170632eb85cda596d9cb07db276d9a0c1 Mon Sep 17 00:00:00 2001 From: Haoyu Xu Date: Sun, 26 Feb 2023 01:06:10 -0500 Subject: [PATCH] feat(directory): finish home page --- directory/src/App.css | 13 +- directory/src/App.jsx | 15 +- directory/src/component/char_icon.jsx | 13 + directory/src/component/dropdown.css | 2 +- directory/src/component/dropdown.jsx | 5 +- directory/src/component/main_border.css | 23 ++ directory/src/component/main_border.jsx | 7 + directory/src/component/popup.css | 16 +- directory/src/component/popup.jsx | 2 +- directory/src/component/return_button.css | 6 +- directory/src/context/useConfigContext.jsx | 19 ++ directory/src/context/useHeaderContext.jsx | 39 +++ directory/src/context/useLanguageContext.jsx | 10 +- directory/src/context/useTitleContext.jsx | 25 -- directory/src/i18n.json | 26 +- directory/src/routes/index.jsx | 8 +- .../routes/path/{news.css => changelogs.css} | 0 directory/src/routes/path/changelogs.jsx | 32 +++ directory/src/routes/path/home.css | 126 ++++++++++ directory/src/routes/path/home.jsx | 117 +++++++-- directory/src/routes/path/news.jsx | 27 --- directory/src/routes/path/operator.jsx | 8 +- directory/src/routes/root.css | 229 +++++++++++------- directory/src/routes/root.jsx | 98 +++++--- 24 files changed, 652 insertions(+), 214 deletions(-) create mode 100644 directory/src/component/char_icon.jsx create mode 100644 directory/src/component/main_border.css create mode 100644 directory/src/component/main_border.jsx create mode 100644 directory/src/context/useConfigContext.jsx create mode 100644 directory/src/context/useHeaderContext.jsx delete mode 100644 directory/src/context/useTitleContext.jsx rename directory/src/routes/path/{news.css => changelogs.css} (100%) create mode 100644 directory/src/routes/path/changelogs.jsx delete mode 100644 directory/src/routes/path/news.jsx diff --git a/directory/src/App.css b/directory/src/App.css index c8237e0..ffaebd1 100644 --- a/directory/src/App.css +++ b/directory/src/App.css @@ -1,13 +1,20 @@ -@import url('https://fonts.googleapis.com/css2?family=Josefin+Sans:wght@400;500&family=Noto+Sans+SC&display=swap'); +@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;700&display=swap'); +@import url('https://fonts.cdnfonts.com/css/bender'); +@import url('https://fonts.cdnfonts.com/css/geometos'); :root { --text-color: rgba(255, 255, 255, 0.87); + --date-color: rgba(255, 255, 255, 0.2); --border-color: #707070; --link-highlight-color: #33b5e5; - --drawer-background-color: rgba(0, 0, 0, 0.5); + --drawer-background-color: rgba(0, 0, 0, 0.88); --root-background-color: #131313; + --home-item-hover-background-color: rgba(67, 67, 67, 0.3); + --home-item-background-linear-gradient-color: rgba(255, 255, 255, 0.1); + --home-item-outline-color: rgba(214, 214, 214, 0.3); + --button-color: #666; - font-family: "Josefin Sans", "Noto Sans SC", sans-serif; + font-family: "Geometos", "Noto Sans SC", sans-serif; font-size: 16px; line-height: 24px; font-weight: 400; diff --git a/directory/src/App.jsx b/directory/src/App.jsx index 60c986a..66523b0 100644 --- a/directory/src/App.jsx +++ b/directory/src/App.jsx @@ -11,8 +11,9 @@ import ErrorPage from "@/routes/error-page"; import routes from "@/routes"; import '@/App.css'; import 'reset-css'; -import { TitleProvider } from '@/context/useTitleContext'; import { LanguageProvider } from '@/context/useLanguageContext'; +import { ConfigProvider } from '@/context/useConfigContext'; +import { HeaderProvider } from '@/context/useHeaderContext'; const router = createBrowserRouter( createRoutesFromElements( @@ -40,10 +41,12 @@ const router = createBrowserRouter( ReactDOM.createRoot(document.getElementById('root')).render( - - - - - + + + + + + + ) \ No newline at end of file diff --git a/directory/src/component/char_icon.jsx b/directory/src/component/char_icon.jsx new file mode 100644 index 0000000..6ba5529 --- /dev/null +++ b/directory/src/component/char_icon.jsx @@ -0,0 +1,13 @@ +export default function CharIcon(props) { + return ( + + { + props.type === 'operator' ? + + : + + } + + + ) +} \ No newline at end of file diff --git a/directory/src/component/dropdown.css b/directory/src/component/dropdown.css index 17274db..226da17 100644 --- a/directory/src/component/dropdown.css +++ b/directory/src/component/dropdown.css @@ -37,7 +37,7 @@ .dropdown .menu { opacity: 0; position: absolute; - background-color: var(--background-color); + background-color: var(--root-background-color); color: var(--text-color); width: max-content; z-index: -1; diff --git a/directory/src/component/dropdown.jsx b/directory/src/component/dropdown.jsx index 87a907e..5a0600b 100644 --- a/directory/src/component/dropdown.jsx +++ b/directory/src/component/dropdown.jsx @@ -28,7 +28,10 @@ export default function Dropdown(props) {
  • props.onClick(item)} + onClick={() => { + props.onClick(item) + toggleDropdown() + }} > {item.name}
  • diff --git a/directory/src/component/main_border.css b/directory/src/component/main_border.css new file mode 100644 index 0000000..9de11bb --- /dev/null +++ b/directory/src/component/main_border.css @@ -0,0 +1,23 @@ +.main-border { + position: relative; + bottom: 1px; + border-bottom: 1px solid var(--text-color); +} + +.main-border::before, .main-border::after { + content: ""; + display: block; + position: absolute; + width: 5px; + height: 5px; + top: -2px; + background-color: var(--text-color); +} + +.main-border::before { + right: 100%; +} + +.main-border::after { + left: 100%; +} \ No newline at end of file diff --git a/directory/src/component/main_border.jsx b/directory/src/component/main_border.jsx new file mode 100644 index 0000000..3707de2 --- /dev/null +++ b/directory/src/component/main_border.jsx @@ -0,0 +1,7 @@ +import './main_border.css'; + +export default function MainBorder(props) { + return ( +
    + ) +} \ No newline at end of file diff --git a/directory/src/component/popup.css b/directory/src/component/popup.css index 32fa6c6..d905e53 100644 --- a/directory/src/component/popup.css +++ b/directory/src/component/popup.css @@ -41,13 +41,18 @@ .popup .title { font-size: 3rem; font-weight: 700; - margin-bottom: 1rem; border-bottom: 1px solid var(--border-color); display: flex; flex-direction: row; justify-content: space-between; align-content: center; align-items: center; + padding-bottom: 1rem; +} + +.popup .content { + line-height: 1.5rem; + padding: 1rem; } .popup .overlay { @@ -66,3 +71,12 @@ opacity: 0.5; visibility: visible; } + +.popup .return-button { + color: #666; + transition: color cubic-bezier(0.65, 0.05, 0.36, 1) 0.3s; +} + +.popup .return-button:hover { + color: var(--text-color); +} \ No newline at end of file diff --git a/directory/src/component/popup.jsx b/directory/src/component/popup.jsx index 6de2148..b98b56e 100644 --- a/directory/src/component/popup.jsx +++ b/directory/src/component/popup.jsx @@ -18,7 +18,7 @@ export default function Popup(props) {
    {props.title} - +
    {props.children} diff --git a/directory/src/component/return_button.css b/directory/src/component/return_button.css index daf20e6..9c30f80 100644 --- a/directory/src/component/return_button.css +++ b/directory/src/component/return_button.css @@ -17,20 +17,20 @@ .return-button .bar { width: 1rem; height: 0.4rem; - background-color: var(--text-color); + background-color: currentColor; transition: transform cubic-bezier(0.65, 0.05, 0.36, 1) 0.3s; } .return-button .bar-arrow-left { border-top: 0.24rem solid transparent; border-bottom: 0.24rem solid transparent; - border-right: 0.3rem solid var(--text-color); + border-right: 0.3rem solid currentColor; } .return-button .bar-arrow-right { border-top: 0.24rem solid transparent; border-bottom: 0.24rem solid transparent; - border-left: 0.3rem solid var(--text-color); + border-left: 0.3rem solid currentColor; } .return-button .bar-wrapper:nth-child(1) { diff --git a/directory/src/context/useConfigContext.jsx b/directory/src/context/useConfigContext.jsx new file mode 100644 index 0000000..bc048c5 --- /dev/null +++ b/directory/src/context/useConfigContext.jsx @@ -0,0 +1,19 @@ +import { createContext, useState, useEffect } from "react" + +export const ConfigContext = createContext() + +export function ConfigProvider(props) { + const [config, setConfig] = useState(null) + + useEffect(() => { + fetch("/_assets/directory.json").then(res => res.json()).then(data => { + setConfig(data) + }) + }, []) + + return ( + + {props.children} + + ) +} \ No newline at end of file diff --git a/directory/src/context/useHeaderContext.jsx b/directory/src/context/useHeaderContext.jsx new file mode 100644 index 0000000..3d21587 --- /dev/null +++ b/directory/src/context/useHeaderContext.jsx @@ -0,0 +1,39 @@ +import { + createContext, + useState, + useEffect, + useContext +} from "react" +import { LanguageContext } from "@/context/useLanguageContext" + +export const HeaderContext = createContext() + +export function HeaderProvider(props) { + const [key, setTitle] = useState('') + const [title, setRealTitle] = useState('') + const { + language, + i18n + } = useContext(LanguageContext) + const [tabs, setTabs] = useState(null) + const [currentTab, setCurrentTab] = useState(null) + + useEffect(() => { + let newTitle = key + if (i18n.key[newTitle]) { + newTitle = i18n.key[newTitle][language] + } + document.title = `${newTitle} - ${import.meta.env.VITE_APP_TITLE}`; + setRealTitle(newTitle) + }, [key, language]) + + return ( + + {props.children} + + ) +} \ No newline at end of file diff --git a/directory/src/context/useLanguageContext.jsx b/directory/src/context/useLanguageContext.jsx index 758f503..f75c04e 100644 --- a/directory/src/context/useLanguageContext.jsx +++ b/directory/src/context/useLanguageContext.jsx @@ -4,20 +4,20 @@ import i18n from '@/i18n' export const LanguageContext = createContext() export function LanguageProvider(props) { - const drawerTextDefaultLang = "en-US" + const textDefaultLang = "en-US" const [language, setLanguage] = useState(i18n.available.includes(navigator.language) ? navigator.language : "en-US") // language will be default to en-US if not available - const [drawerAlternateLang, setDrawerAlternateLang] = useState(null) // drawerAlternateLang will be default to zh-CN if language is en-* + const [alternateLang, setAlternateLang] = useState(null) // drawerAlternateLang will be default to zh-CN if language is en-* useEffect(() => { - setDrawerAlternateLang(language.startsWith("en") ? "zh-CN" : language) + setAlternateLang(language.startsWith("en") ? "zh-CN" : language) }, [language]) return ( diff --git a/directory/src/context/useTitleContext.jsx b/directory/src/context/useTitleContext.jsx deleted file mode 100644 index c464d7d..0000000 --- a/directory/src/context/useTitleContext.jsx +++ /dev/null @@ -1,25 +0,0 @@ -import { createContext, useState, useEffect, useContext } from "react" -import { LanguageContext } from '@/context/useLanguageContext'; - -export const TitleContext = createContext() - -export function TitleProvider(props) { - const { i18n, language } = useContext(LanguageContext) - const [fakeTitle, setTitle] = useState('dynamic_compile') - const [title, setActualTitle] = useState(null) - - useEffect(() => { - let newTitle = fakeTitle - if (i18n.key[fakeTitle]) { - newTitle = i18n.key[fakeTitle][language] - } - document.title = `${newTitle} - ${import.meta.env.VITE_APP_TITLE}`; - setActualTitle(newTitle) - }, [fakeTitle, language]) - - return ( - - {props.children} - - ) -} \ No newline at end of file diff --git a/directory/src/i18n.json b/directory/src/i18n.json index ca88210..a6ed7ce 100644 --- a/directory/src/i18n.json +++ b/directory/src/i18n.json @@ -11,14 +11,22 @@ "zh-CN": "首页", "en-US": "Home" }, - "news": { - "zh-CN": "新闻", - "en-US": "News" + "changelogs": { + "zh-CN": "更新日志", + "en-US": "Changelogs" }, "offical_page": { "zh-CN": "官方页面", "en-US": "Offical Page" }, + "disclaimer": { + "zh-CN": "免责声明", + "en-US": "Disclaimer" + }, + "disclaimer_content": { + "zh-CN": "本网站由 Halyul 设立并为明日方舟社区服务,Halyul 声明本网站完全独立运营,与 上海鹰角网络科技有限公司, Esoteric Software LLC 或其任何关联实体并无任何联系。", + "en-US": "This website is set up and operated by Halyul for the benefit of the Arknights Community. Halyul hereby states that this website is dedicated, but not related to Hypergryph Co., Ltd, Esoteric Software LLC or any of its affiliated entity." + }, "privacy_policy": { "zh-CN": "隐私政策", "en-US": "Privacy Policy" @@ -27,6 +35,18 @@ "zh-CN": "联系我们", "en-US": "Contact Us" }, + "all": { + "zh-CN": "综合", + "en-US": "All" + }, + "elite2": { + "zh-CN": "干员晋升", + "en-US": "Elite 2" + }, + "skin": { + "zh-CN": "干员时装", + "en-US": "Skin" + }, "zh-CN": { "zh-CN": "简体中文", "en-US": "Chinese (Simplified)" diff --git a/directory/src/routes/index.jsx b/directory/src/routes/index.jsx index 9b5bb9a..8e2db77 100644 --- a/directory/src/routes/index.jsx +++ b/directory/src/routes/index.jsx @@ -1,6 +1,6 @@ import Home from "@/routes/path/home"; import Operator from "@/routes/path/operator"; -import News from "@/routes/path/news"; +import Changelogs from "@/routes/path/changelogs"; export default [ { @@ -16,10 +16,10 @@ export default [ element: , inDrawer: false }, { - path: "news", + path: "changelogs", index: false, - name: "news", - element: , + name: "changelogs", + element: , inDrawer: true }, { path: "https://ak.hypergryph.com/archive/dynamicCompile/", diff --git a/directory/src/routes/path/news.css b/directory/src/routes/path/changelogs.css similarity index 100% rename from directory/src/routes/path/news.css rename to directory/src/routes/path/changelogs.css diff --git a/directory/src/routes/path/changelogs.jsx b/directory/src/routes/path/changelogs.jsx new file mode 100644 index 0000000..7c0d62f --- /dev/null +++ b/directory/src/routes/path/changelogs.jsx @@ -0,0 +1,32 @@ +import { + useState, + useEffect, + useContext +} from 'react' +import './changelogs.css' +import { HeaderContext } from '@/context/useHeaderContext'; +import { LanguageContext } from '@/context/useLanguageContext'; +import useUmami from '@parcellab/react-use-umami' + +export default function Changelogs(props) { + const _trackEvt = useUmami('/changelogs') + const { + setTitle, + setTabs, + currentTab, setCurrentTab + } = useContext(HeaderContext) + const { language, i18n } = useContext(LanguageContext) + + useEffect(() => { + setTitle('changelogs') + setTabs([]) + }, []) + + return ( +
    +
    + Under Construction :( +
    +
    + ) +} \ No newline at end of file diff --git a/directory/src/routes/path/home.css b/directory/src/routes/path/home.css index e69de29..f8b3e7a 100644 --- a/directory/src/routes/path/home.css +++ b/directory/src/routes/path/home.css @@ -0,0 +1,126 @@ +.home .item-group { + padding: 1rem; + display: flex; + align-items: flex-end; + flex-wrap: wrap; +} + +.home .item-group-date { + margin: 1.5rem; + font-family: "Bender"; + font-weight: bold; + text-align: right; + color: var(--date-color); + font-size: 1.5rem; + letter-spacing: 0.1rem; + flex: auto; +} + +.home .item-group .item { + position: relative; + cursor: pointer; + width: 180px; + margin: 1.25rem; + background-image: repeating-linear-gradient(90deg, var(--home-item-background-linear-gradient-color) 0, var(--home-item-background-linear-gradient-color) 1px, transparent 1px, transparent 5px); +} + +.home .item-group .item .item-outline { + content: ""; + display: block; + position: absolute; + opacity: 0; + visibility: hidden; + transition: all cubic-bezier(0.65, 0.05, 0.36, 1) 0.3s; +} + +.home .item-group .item .item-outline{ + width: 100%; + height: 100%; + left: -6px; + top: -6px; + border: var(--home-item-outline-color) 1px dashed; + padding: 6px; +} + +.home .item-group .item .item-outline::before, +.home .item-group .item .item-outline::after { + content: ""; + display: block; + position: absolute; + left: -3px; + height: 3px; + width: 100%; + border-left: var(--text-color) solid 3px; + border-right: var(--text-color) solid 3px; +} + +.home .item-group .item .item-outline::before { + top: -3px; +} + +.home .item-group .item .item-outline::after { + bottom: -3px; +} + +.home .item-group .item:hover .item-outline, +.home .item-group .item:hover .item-info .item-info-background { + opacity: 1; + visibility: visible; +} + +.home .item-group .item .item-img { + height: 360px; + width: 100%; + transition: background-color cubic-bezier(0.65, 0.05, 0.36, 1) 0.3s; +} + +.home .item-group .item:hover .item-img { + background-color: var(--home-item-hover-background-color); +} + +.home .item-group .item .item-info { + white-space: nowrap; + position: relative; + padding: 0.8rem 0.4rem; + line-height: 1; +} + +.home .item-group .item .item-info .item-title-container { + color: white; + display: flex; + justify-content: space-between; + align-items: center; + font-size: 1.25rem; + font-weight: bold; +} + +.home .item-group .item .item-info .item-title-container .item-title, +.home .item-group .item .item-info .item-text-wrapper { + flex: 1; + overflow: hidden; + text-overflow: ellipsis; +} + +.home .item-group .item .item-info .item-title-container .item-type { + width: 1.5rem; + fill: var(--text-color) +} + +.home .item-group .item .item-info .item-text { + color: var(--text-color); + font-size: 0.75rem; + font-family: "Geometos"; + margin-top: 1rem; +} + +.home .item-group .item .item-info .item-info-background { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + opacity: 0; + visibility: hidden; + transition: all cubic-bezier(0.65, 0.05, 0.36, 1) 0.3s; + background-image: linear-gradient(70deg, transparent 40%, currentColor 150%); +} \ No newline at end of file diff --git a/directory/src/routes/path/home.jsx b/directory/src/routes/path/home.jsx index e1bf073..053065b 100644 --- a/directory/src/routes/path/home.jsx +++ b/directory/src/routes/path/home.jsx @@ -1,32 +1,115 @@ import { useState, useEffect, - useContext + useContext, + useMemo } from 'react' +import { + Link, +} from "react-router-dom"; import './home.css' -import { TitleContext } from '@/context/useTitleContext'; +import { ConfigContext } from '@/context/useConfigContext'; +import { LanguageContext } from '@/context/useLanguageContext'; +import { HeaderContext } from '@/context/useHeaderContext'; +import CharIcon from '@/component/char_icon'; +import MainBorder from '@/component/main_border'; +import useUmami from '@parcellab/react-use-umami' -export default function Home(props) { - const { title, setTitle } = useContext(TitleContext) - useEffect(() => { - fetch("/_assets/directory.json").then(res => res.json()).then(data => { - console.log(data) - }) - }, []) +export default function Home() { + const _trackEvt = useUmami('/') + const { + setTitle, + setTabs, + currentTab, setCurrentTab } = useContext(HeaderContext) + const { config } = useContext(ConfigContext) + const { + language, + textDefaultLang, + alternateLang, + i18n + } = useContext(LanguageContext) useEffect(() => { setTitle('dynamic_compile') + setTabs([ + { + key: 'all', + text: i18n.key.all + }, { + key: 'operator', + text: i18n.key.elite2 + }, { + key: 'skin', + text: i18n.key.skin + } + ]) + setCurrentTab('all') }, []) + const renderContent = () => { + const list = config?.filter((v) => currentTab === 'all' || currentTab === v.type) + const content = [] + if (list) { + console.log(list) + let items = {} + list.forEach(element => { + if (items[element.date]) { + items[element.date].push(toItemEl(element)) + } else { + items[element.date] = [toItemEl(element)] + } + }); + items = Object.keys(items).sort().reverse().reduce((r, k) => { + r[k] = items[k] + return r + }, {}) + for (const [date, group] of Object.entries(items)) { + content.push( +
    +
    + {group.map((v) => v)} +
    {date}
    +
    + +
    + ) + } + } + return content + } + const toItemEl = (item) => { + return ( + +
    +
    + {item.codename[language]} +
    +
    +
    +
    {item.codename[language]}
    +
    + +
    +
    +
    + {item.codename[language.startsWith("en") ? alternateLang : textDefaultLang]} +
    +
    +
    + + ) + } + const content = useMemo(() => renderContent(), [currentTab, config, language]) + return ( -
    -
    - {title} - -
    -
    - 22s -
    +
    + {content}
    ) } \ No newline at end of file diff --git a/directory/src/routes/path/news.jsx b/directory/src/routes/path/news.jsx deleted file mode 100644 index e5f138a..0000000 --- a/directory/src/routes/path/news.jsx +++ /dev/null @@ -1,27 +0,0 @@ -import { - useState, - useEffect, - useContext -} from 'react' -import './news.css' -import { TitleContext } from '@/context/useTitleContext'; - -export default function News(props) { - const { title, setTitle } = useContext(TitleContext) - - useEffect(() => { - setTitle('news') - }, []) - - return ( -
    -
    - 1 - -
    -
    - 2 -
    -
    - ) -} \ No newline at end of file diff --git a/directory/src/routes/path/operator.jsx b/directory/src/routes/path/operator.jsx index 21c3091..94c6eb4 100644 --- a/directory/src/routes/path/operator.jsx +++ b/directory/src/routes/path/operator.jsx @@ -4,13 +4,15 @@ import { useContext } from 'react' import './operator.css' -import { TitleContext } from '@/context/useTitleContext'; +import { HeaderContext } from '@/context/useHeaderContext'; +import useUmami from '@parcellab/react-use-umami' export default function Operator(props) { - const { title, setTitle } = useContext(TitleContext) + const _trackEvt = useUmami('/operator/:key') + const { setTitle } = useContext(HeaderContext) useEffect(() => { - setTitle('Chen') + setTitle('chen') }, []) return ( diff --git a/directory/src/routes/root.css b/directory/src/routes/root.css index a9d4550..d35235c 100644 --- a/directory/src/routes/root.css +++ b/directory/src/routes/root.css @@ -1,149 +1,204 @@ .main { - flex-grow: 1; + flex-grow: 1; + display: flex; + flex-direction: column; + align-items: stretch; + justify-content: flex-start; + padding-bottom: 3rem; + margin: 0 auto; + width: 70%; + max-width: 100rem; } .header { - width: auto; - position: sticky; - top: 0; - right: 0; - padding: 1rem; - z-index: 1; - height: 3rem; - display: flex; - flex-direction: row; - align-items: center; - flex-wrap: nowrap; - justify-content: flex-start; + width: auto; + position: sticky; + top: 0; + right: 0; + padding: 1rem; + z-index: 2; + height: 3rem; + display: flex; + flex-direction: row; + align-items: center; + flex-wrap: nowrap; + justify-content: flex-start; } .header .dropdown { - margin-left: auto; + margin-left: auto; } .navButton { - padding: 0.5rem; - font-size: 2rem; - width: 2rem; - height: 2rem; - cursor: pointer; - display: flex; - justify-content: center; - align-items: flex-start; - flex-direction: column; - z-index: 2; + padding: 0.5rem; + font-size: 2rem; + width: 2rem; + height: 2rem; + cursor: pointer; + display: flex; + justify-content: center; + align-items: flex-start; + flex-direction: column; + z-index: 2; } .navButton .bar { - width: 2rem; - height: 0.2rem; - background-color: var(--text-color); - transition: transform cubic-bezier(0.65, 0.05, 0.36, 1) 0.3s; + width: 2rem; + height: 0.2rem; + background-color: var(--text-color); + transition: transform cubic-bezier(0.65, 0.05, 0.36, 1) 0.3s; } .navButton .bar:nth-child(1) { - transform: translate(0, -200%); + transform: translate(0, -200%); } .navButton .bar:nth-child(3) { - transform: translate(0, 200%); + transform: translate(0, 200%); } .navButton.active .bar:nth-child(1) { - transform: translate(0, 100%) rotateZ(45deg) scaleX(0.5) translate(-50%); + transform: translate(0, 100%) rotateZ(45deg) scaleX(0.5) translate(-50%); } .navButton.active .bar:nth-child(2) { - transform: rotateZ(-45deg); + transform: rotateZ(-45deg); } .navButton.active .bar:nth-child(3) { - transform: translate(0, -100%) rotateZ(45deg) scaleX(0.5) translate(50%); + transform: translate(0, -100%) rotateZ(45deg) scaleX(0.5) translate(50%); } .drawer { - position: fixed; - top: 0; - left: -15rem; - width: 15rem; - height: 100%; - z-index: 0; - pointer-events: none; - transition: left cubic-bezier(0.65, 0.05, 0.36, 1) 0.3s; - text-align: center; - display: flex; - flex-direction: row; - align-items: flex-start; - gap: 1rem; + position: fixed; + top: 0; + left: -15rem; + width: 15rem; + height: 100%; + z-index: 1; + pointer-events: none; + transition: left cubic-bezier(0.65, 0.05, 0.36, 1) 0.3s; + text-align: center; + display: flex; + flex-direction: row; + align-items: flex-start; } .drawer .links { - padding: 8rem 0; - display: flex; - flex-direction: column; - align-items: center; - gap: 1rem; - background-color: var(--drawer-background-color); - height: 100%; - width: 15rem; + padding: 8rem 0; + display: flex; + flex-direction: column; + align-items: center; + gap: 1rem; + background-color: var(--drawer-background-color); + height: 100%; + width: 15rem; } .drawer .overlay { - height: 100%; - flex-grow: 1; - z-index: 2; + height: 100%; + flex-grow: 1; + z-index: 2; } .drawer.active { - pointer-events: all; - left: 0; - width: 100vw; + pointer-events: all; + left: 0; + width: 100vw; } -.drawer a { +.drawer .link { + display: flex; + flex-direction: column; + align-items: center; + gap: 0.2rem; + color: var(--text-color); + font-size: 0.8rem; + font-weight: 500; + transition: color cubic-bezier(0.65, 0.05, 0.36, 1) 0.3s; + text-transform: uppercase; +} + +.drawer .link:hover { + color: var(--link-highlight-color); +} + +.drawer .link.active { + color: var(--link-highlight-color); +} + +.main .main-header { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: flex-end; + flex-wrap: wrap; + position: relative; +} + +.main .main-header .main-title { + font-size: 3rem; + font-weight: 700; + text-transform: uppercase; +} + +.main .main-header .main-tab { display: flex; - flex-direction: column; + flex-direction: row; align-items: center; - gap: 0.2rem; - color: var(--text-color); - font-size: 0.8rem; - font-weight: 500; - transition: color cubic-bezier(0.65, 0.05, 0.36, 1) 0.3s; +} + +.main .main-header .main-tab .main-tab-item { + font-size: 1em; + line-height: 3rem; + font-weight: 700; + padding: 0 1rem; text-transform: uppercase; + transition: all cubic-bezier(0.65, 0.05, 0.36, 1) 0.3s; + cursor: pointer; + border-bottom: 0.3rem solid transparent; } -.drawer a:hover { +.main .main-header .main-tab .main-tab-item.active { color: var(--link-highlight-color); + border-bottom-color: var(--link-highlight-color); } -.main { - margin: 0 auto; - width: 90%; - max-width: 100rem; +.main .main-header .return-button { + position: absolute; + right: -4rem; + bottom: -1.5rem; + color: #666; + transition: color cubic-bezier(0.65, 0.05, 0.36, 1) 0.3s; +} + +.main .main-header .return-button:hover { + color: var(--text-color); } .footer .section { - border-top: 1px solid var(--border-color); - padding: 1rem 0; - display: flex; - align-content: center; - justify-content: center; - align-items: center; - flex-wrap: nowrap; + border-top: 1px solid var(--border-color); + padding: 1rem 0; + display: flex; + align-content: center; + justify-content: center; + align-items: center; + flex-wrap: nowrap; + font-family: "Noto Sans SC", sans-serif; } .footer .links { - flex-direction: row; - gap: 1rem; + flex-direction: row; + gap: 1rem; } .footer .links .separator { - height: 1rem; - border-left: 2px solid var(--border-color); + height: 1rem; + border-left: 2px solid var(--border-color); } .footer .copyright { - flex-direction: column; - gap: 0.5rem; - font-size: 12px; -} \ No newline at end of file + flex-direction: column; + gap: 0.5rem; + font-size: 12px; +} diff --git a/directory/src/routes/root.jsx b/directory/src/routes/root.jsx index 42265fe..1a4b89f 100644 --- a/directory/src/routes/root.jsx +++ b/directory/src/routes/root.jsx @@ -7,28 +7,47 @@ import { Outlet, Link, NavLink, - useNavigation, - useLocation, useNavigate, } from "react-router-dom"; import './root.css' import routes from '@/routes' -import { TitleContext } from '@/context/useTitleContext'; +import { HeaderContext } from '@/context/useHeaderContext'; import { LanguageContext } from '@/context/useLanguageContext'; import Dropdown from '@/component/dropdown'; import Popup from '@/component/popup'; import ReturnButton from '@/component/return_button'; +import MainBorder from '@/component/main_border'; export default function Root(props) { - const { title, setTitle } = useContext(TitleContext) + const navigate = useNavigate() const [drawerHidden, setDrawerHidden] = useState(true) const { language, setLanguage, - drawerTextDefaultLang, - drawerAlternateLang, + textDefaultLang, + alternateLang, i18n } = useContext(LanguageContext) + const { title, tabs, currentTab, setCurrentTab } = useContext(HeaderContext) const [drawerDestinations, setDrawerDestinations] = useState(null) + const currentYear = new Date().getFullYear() + const [headerTabs, setHeaderTabs] = useState(null) + + const renderHeaderTabs = (tabs) => { + setHeaderTabs(tabs?.map((item) => { + return ( +
    { + setCurrentTab(item.key) + item.onClick && item.onClick(e, item.key) + }} + > + {item.text[language]} +
    + ) + })) + } const toggleDrawer = () => { setDrawerHidden(!drawerHidden) @@ -38,25 +57,34 @@ export default function Root(props) { return routes.filter((item) => item.inDrawer).map((item) => { if (typeof item.element.type === 'string') { return ( - + toggleDrawer()} + >
    - {i18n.key[item.name][drawerTextDefaultLang]} + {i18n.key[item.name][textDefaultLang]}
    - {i18n.key[item.name][drawerAlternateLang]} + {i18n.key[item.name][alternateLang]}
    -
    + ) } else { return ( - + toggleDrawer()} + >
    - {i18n.key[item.name][drawerTextDefaultLang]} + {i18n.key[item.name][textDefaultLang]}
    - {i18n.key[item.name][drawerAlternateLang]} + {i18n.key[item.name][alternateLang]}
    ) @@ -66,7 +94,11 @@ export default function Root(props) { useEffect(() => { setDrawerDestinations(renderDrawerDestinations()) - }, [drawerAlternateLang]) + }, [alternateLang]) + + useEffect(() => { + renderHeaderTabs(tabs) + }, [tabs, currentTab, language]) return ( <> @@ -104,21 +136,33 @@ export default function Root(props) { />
    -
    -
    {title}
    -
    - {/* All - Operator - Skill */} +
    +
    {title}
    +
    + {headerTabs}
    + { + navigate("/") + }} + />
    +