feat(directory): add layout scaffold
This commit is contained in:
@@ -1,17 +1,24 @@
|
|||||||
@import url("https://fonts.googleapis.com/css2?family=Josefin+Sans&family=Noto+Sans+SC&display=swap");
|
@import url('https://fonts.googleapis.com/css2?family=Josefin+Sans:wght@400;500&family=Noto+Sans+SC&display=swap');
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
|
--text-color: rgba(255, 255, 255, 0.87);
|
||||||
|
--border-color: #707070;
|
||||||
|
--link-highlight-color: #33b5e5;
|
||||||
|
--drawer-background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
--root-background-color: #131313;
|
||||||
|
|
||||||
font-family: "Josefin Sans", "Noto Sans SC", sans-serif;
|
font-family: "Josefin Sans", "Noto Sans SC", sans-serif;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
|
|
||||||
color: rgba(255, 255, 255, 0.87);
|
color: var(--text-color);
|
||||||
background-color: #131313;
|
background-color: var(--root-background-color);
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
|
|
||||||
font-synthesis: none;
|
font-synthesis: none;
|
||||||
text-rendering: optimizeLegibility;
|
text-rendering: optimizeLegibility;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#root {
|
#root {
|
||||||
@@ -20,6 +27,11 @@
|
|||||||
flex-wrap: nowrap;
|
flex-wrap: nowrap;
|
||||||
align-content: flex-start;
|
align-content: flex-start;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
align-items: flex-start;
|
align-items: stretch;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--text-color);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
@@ -11,8 +11,8 @@ import ErrorPage from "@/routes/error-page";
|
|||||||
import routes from "@/routes";
|
import routes from "@/routes";
|
||||||
import '@/App.css';
|
import '@/App.css';
|
||||||
import 'reset-css';
|
import 'reset-css';
|
||||||
|
import { TitleProvider } from '@/context/useTitleContext';
|
||||||
document.title = import.meta.env.VITE_APP_TITLE;
|
import { LanguageProvider } from '@/context/useLanguageContext';
|
||||||
|
|
||||||
const router = createBrowserRouter(
|
const router = createBrowserRouter(
|
||||||
createRoutesFromElements(
|
createRoutesFromElements(
|
||||||
@@ -40,6 +40,10 @@ const router = createBrowserRouter(
|
|||||||
|
|
||||||
ReactDOM.createRoot(document.getElementById('root')).render(
|
ReactDOM.createRoot(document.getElementById('root')).render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<RouterProvider router={router} />
|
<LanguageProvider>
|
||||||
|
<TitleProvider>
|
||||||
|
<RouterProvider router={router} />
|
||||||
|
</TitleProvider>
|
||||||
|
</LanguageProvider>
|
||||||
</React.StrictMode>
|
</React.StrictMode>
|
||||||
)
|
)
|
||||||
98
directory/src/component/dropdown.css
Normal file
98
directory/src/component/dropdown.css
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
.dropdown {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
user-select: none;
|
||||||
|
z-index: 2;
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown .text {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown .content {
|
||||||
|
padding-right: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown .icon {
|
||||||
|
width: 0.5em;
|
||||||
|
height: 0.5em;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
border-left: 0.15em solid var(--text-color);
|
||||||
|
border-bottom: 0.15em solid var(--text-color);
|
||||||
|
border-right: 0.15em solid var(--text-color);
|
||||||
|
border-top: 0.15em solid var(--text-color);
|
||||||
|
transform: translate(0, -0.15em) rotate(-45deg);
|
||||||
|
transition: all cubic-bezier(0.65, 0.05, 0.36, 1) 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown .icon.active {
|
||||||
|
animation: icon-flash 2s cubic-bezier(0.65, 0.05, 0.36, 1) infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown .menu {
|
||||||
|
opacity: 0;
|
||||||
|
position: absolute;
|
||||||
|
background-color: var(--background-color);
|
||||||
|
color: var(--text-color);
|
||||||
|
width: max-content;
|
||||||
|
z-index: -1;
|
||||||
|
top: 2rem;
|
||||||
|
right: 0;
|
||||||
|
gap: 0.5rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
transition: all cubic-bezier(0.65, 0.05, 0.36, 1) 0.3s;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 0.5rem;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown .menu.active {
|
||||||
|
visibility: visible;
|
||||||
|
opacity: 1;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown .menu .item {
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0.5rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
width: max-content;
|
||||||
|
height: max-content;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
transition: color cubic-bezier(0.65, 0.05, 0.36, 1) 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown .menu .item:hover, .dropdown .menu .item:focus, .dropdown .menu .item.active {
|
||||||
|
color: var(--link-highlight-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown .overlay {
|
||||||
|
z-index: 1;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes icon-flash {
|
||||||
|
50% {
|
||||||
|
opacity: 0.2;
|
||||||
|
}
|
||||||
|
}
|
||||||
47
directory/src/component/dropdown.jsx
Normal file
47
directory/src/component/dropdown.jsx
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import {
|
||||||
|
useState,
|
||||||
|
useEffect
|
||||||
|
} from 'react'
|
||||||
|
import './dropdown.css'
|
||||||
|
|
||||||
|
export default function Dropdown(props) {
|
||||||
|
const [hidden, setHidden] = useState(true)
|
||||||
|
|
||||||
|
const toggleDropdown = () => {
|
||||||
|
setHidden(!hidden)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<section className='dropdown'>
|
||||||
|
<section
|
||||||
|
className='text'
|
||||||
|
onClick={() => toggleDropdown()}
|
||||||
|
>
|
||||||
|
<span className='content'>{props.text}</span>
|
||||||
|
<span className={`icon ${hidden ? '' : 'active'}`}></span>
|
||||||
|
</section>
|
||||||
|
<ul className={`menu ${hidden ? '' : 'active'}`}>
|
||||||
|
{
|
||||||
|
props.menu.map((item) => {
|
||||||
|
return (
|
||||||
|
<li
|
||||||
|
key={item.name}
|
||||||
|
className={`item${item.name === props.text ? ' active' : ''}`}
|
||||||
|
onClick={() => props.onClick(item)}
|
||||||
|
>
|
||||||
|
{item.name}
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
<section
|
||||||
|
className='overlay'
|
||||||
|
hidden={hidden}
|
||||||
|
onClick={() => toggleDropdown()}
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
68
directory/src/component/popup.css
Normal file
68
directory/src/component/popup.css
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
.popup-text {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup {
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
opacity: 0;
|
||||||
|
z-index: -1;
|
||||||
|
border: unset;
|
||||||
|
transition: all cubic-bezier(0.65, 0.05, 0.36, 1) 0.3s;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup .wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
width: 480px;
|
||||||
|
height: fit-content;
|
||||||
|
margin: 0 auto;
|
||||||
|
background-color: var(--root-background-color);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup.active {
|
||||||
|
opacity: 1;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup .overlay {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: -1;
|
||||||
|
opacity: 0;
|
||||||
|
background-color: var(--root-background-color);
|
||||||
|
transition: all cubic-bezier(0.65, 0.05, 0.36, 1) 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup .overlay.active {
|
||||||
|
opacity: 0.5;
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
38
directory/src/component/popup.jsx
Normal file
38
directory/src/component/popup.jsx
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import {
|
||||||
|
useState,
|
||||||
|
useEffect
|
||||||
|
} from 'react'
|
||||||
|
import './popup.css'
|
||||||
|
import ReturnButton from '@/component/return_button';
|
||||||
|
|
||||||
|
export default function Popup(props) {
|
||||||
|
const [hidden, setHidden] = useState(true)
|
||||||
|
|
||||||
|
const toggle = () => {
|
||||||
|
setHidden(!hidden)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<section className={`popup ${hidden ? '' : 'active'}`}>
|
||||||
|
<section className='wrapper'>
|
||||||
|
<section className='title'>
|
||||||
|
<span>{props.title}</span>
|
||||||
|
<ReturnButton onClick={toggle} />
|
||||||
|
</section>
|
||||||
|
<section className='content'>
|
||||||
|
{props.children}
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
|
<section className={`overlay ${hidden ? '' : 'active'}`}
|
||||||
|
onClick={() => toggle()} />
|
||||||
|
</section>
|
||||||
|
<span
|
||||||
|
className="popup-text"
|
||||||
|
onClick={toggle}
|
||||||
|
>
|
||||||
|
{props.title}
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
50
directory/src/component/return_button.css
Normal file
50
directory/src/component/return_button.css
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
.return-button {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
padding: 0.6rem 0;
|
||||||
|
width: 3rem;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.return-button .bar-wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.return-button .bar {
|
||||||
|
width: 1rem;
|
||||||
|
height: 0.4rem;
|
||||||
|
background-color: var(--text-color);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
.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);
|
||||||
|
}
|
||||||
|
|
||||||
|
.return-button .bar-wrapper:nth-child(1) {
|
||||||
|
transform: translateY(-0.1rem) rotate(45deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.return-button .bar-wrapper:nth-child(2) {
|
||||||
|
transform: translateY(-0.1rem) translate(90%, -100%) rotate(-45deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.return-button .bar-wrapper:nth-child(3) {
|
||||||
|
transform: translateY(-0.1rem) translateY(150%) rotate(315deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.return-button .bar-wrapper:nth-child(4) {
|
||||||
|
transform: translateY(-0.1rem) translate(90%, 50%) rotate(225deg);
|
||||||
|
}
|
||||||
33
directory/src/component/return_button.jsx
Normal file
33
directory/src/component/return_button.jsx
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import './return_button.css'
|
||||||
|
|
||||||
|
export default function ReturnButton(props) {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<section className='return-button'
|
||||||
|
onClick={() => props.onClick()}
|
||||||
|
>
|
||||||
|
<section className='bar-wrapper'>
|
||||||
|
<section className='bar-arrow-left'></section>
|
||||||
|
<section className='bar'></section>
|
||||||
|
<section className='bar-arrow-right'></section>
|
||||||
|
</section>
|
||||||
|
<section className='bar-wrapper'>
|
||||||
|
<section className='bar-arrow-left'></section>
|
||||||
|
<section className='bar'></section>
|
||||||
|
<section className='bar-arrow-right'></section>
|
||||||
|
</section>
|
||||||
|
<section className='bar-wrapper'>
|
||||||
|
<section className='bar-arrow-left'></section>
|
||||||
|
<section className='bar'></section>
|
||||||
|
<section className='bar-arrow-right'></section>
|
||||||
|
</section>
|
||||||
|
<section className='bar-wrapper'>
|
||||||
|
<section className='bar-arrow-left'></section>
|
||||||
|
<section className='bar'></section>
|
||||||
|
<section className='bar-arrow-right'></section>
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
27
directory/src/context/useLanguageContext.jsx
Normal file
27
directory/src/context/useLanguageContext.jsx
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import { createContext, useState, useEffect } from "react"
|
||||||
|
import i18n from '@/i18n'
|
||||||
|
|
||||||
|
export const LanguageContext = createContext()
|
||||||
|
|
||||||
|
export function LanguageProvider(props) {
|
||||||
|
const drawerTextDefaultLang = "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-*
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setDrawerAlternateLang(language.startsWith("en") ? "zh-CN" : language)
|
||||||
|
}, [language])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<LanguageContext.Provider
|
||||||
|
value={{
|
||||||
|
language, setLanguage,
|
||||||
|
drawerTextDefaultLang,
|
||||||
|
drawerAlternateLang,
|
||||||
|
i18n
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{props.children}
|
||||||
|
</LanguageContext.Provider>
|
||||||
|
)
|
||||||
|
}
|
||||||
25
directory/src/context/useTitleContext.jsx
Normal file
25
directory/src/context/useTitleContext.jsx
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
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 (
|
||||||
|
<TitleContext.Provider value={{ title, setTitle }}>
|
||||||
|
{props.children}
|
||||||
|
</TitleContext.Provider>
|
||||||
|
)
|
||||||
|
}
|
||||||
39
directory/src/i18n.json
Normal file
39
directory/src/i18n.json
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"available": [
|
||||||
|
"zh-CN", "en-US"
|
||||||
|
],
|
||||||
|
"key": {
|
||||||
|
"dynamic_compile": {
|
||||||
|
"zh-CN": "干员动态集录",
|
||||||
|
"en-US": "Dynamic Compile"
|
||||||
|
},
|
||||||
|
"home": {
|
||||||
|
"zh-CN": "首页",
|
||||||
|
"en-US": "Home"
|
||||||
|
},
|
||||||
|
"news": {
|
||||||
|
"zh-CN": "新闻",
|
||||||
|
"en-US": "News"
|
||||||
|
},
|
||||||
|
"offical_page": {
|
||||||
|
"zh-CN": "官方页面",
|
||||||
|
"en-US": "Offical Page"
|
||||||
|
},
|
||||||
|
"privacy_policy": {
|
||||||
|
"zh-CN": "隐私政策",
|
||||||
|
"en-US": "Privacy Policy"
|
||||||
|
},
|
||||||
|
"contact_us": {
|
||||||
|
"zh-CN": "联系我们",
|
||||||
|
"en-US": "Contact Us"
|
||||||
|
},
|
||||||
|
"zh-CN": {
|
||||||
|
"zh-CN": "简体中文",
|
||||||
|
"en-US": "Chinese (Simplified)"
|
||||||
|
},
|
||||||
|
"en-US": {
|
||||||
|
"zh-CN": "英语",
|
||||||
|
"en-US": "English"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,16 +1,31 @@
|
|||||||
import Home from "@/routes/path/home";
|
import Home from "@/routes/path/home";
|
||||||
import Operator from "@/routes/path/operator";
|
import Operator from "@/routes/path/operator";
|
||||||
|
import News from "@/routes/path/news";
|
||||||
|
|
||||||
export default [
|
export default [
|
||||||
{
|
{
|
||||||
path: "",
|
path: "/",
|
||||||
index: true,
|
index: true,
|
||||||
name: "Home",
|
name: "home",
|
||||||
element: <Home />
|
element: <Home />,
|
||||||
|
inDrawer: true
|
||||||
}, {
|
}, {
|
||||||
path: "operator/:key",
|
path: "operator/:key",
|
||||||
index: false,
|
index: false,
|
||||||
name: "Operator",
|
name: "operator",
|
||||||
element: <Operator />
|
element: <Operator />,
|
||||||
|
inDrawer: false
|
||||||
|
}, {
|
||||||
|
path: "news",
|
||||||
|
index: false,
|
||||||
|
name: "news",
|
||||||
|
element: <News />,
|
||||||
|
inDrawer: true
|
||||||
|
}, {
|
||||||
|
path: "https://ak.hypergryph.com/archive/dynamicCompile/",
|
||||||
|
index: false,
|
||||||
|
name: "offical_page",
|
||||||
|
element: <a/>,
|
||||||
|
inDrawer: true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -1,20 +1,28 @@
|
|||||||
import {
|
import {
|
||||||
useState,
|
useState,
|
||||||
useEffect
|
useEffect,
|
||||||
|
useContext
|
||||||
} from 'react'
|
} from 'react'
|
||||||
import './home.css'
|
import './home.css'
|
||||||
|
import { TitleContext } from '@/context/useTitleContext';
|
||||||
|
|
||||||
export default function Home(props) {
|
export default function Home(props) {
|
||||||
|
const { title, setTitle } = useContext(TitleContext)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetch("/_assets/directory.json").then(res => res.json()).then(data => {
|
fetch("/_assets/directory.json").then(res => res.json()).then(data => {
|
||||||
console.log(data)
|
console.log(data)
|
||||||
})
|
})
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setTitle('dynamic_compile')
|
||||||
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section>
|
<section>
|
||||||
<section>
|
<section>
|
||||||
1233
|
{title}
|
||||||
|
<button onClick={() => setTitle('123')}>123</button>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
22s
|
22s
|
||||||
|
|||||||
0
directory/src/routes/path/news.css
Normal file
0
directory/src/routes/path/news.css
Normal file
27
directory/src/routes/path/news.jsx
Normal file
27
directory/src/routes/path/news.jsx
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
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 (
|
||||||
|
<section>
|
||||||
|
<section>
|
||||||
|
1
|
||||||
|
<button onClick={() => setTitle('123')}>123</button>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
2
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,15 +1,23 @@
|
|||||||
import {
|
import {
|
||||||
useState,
|
useState,
|
||||||
useEffect
|
useEffect,
|
||||||
|
useContext
|
||||||
} from 'react'
|
} from 'react'
|
||||||
import './operator.css'
|
import './operator.css'
|
||||||
|
import { TitleContext } from '@/context/useTitleContext';
|
||||||
|
|
||||||
export default function Operator(props) {
|
export default function Operator(props) {
|
||||||
|
const { title, setTitle } = useContext(TitleContext)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setTitle('Chen')
|
||||||
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section>
|
<section>
|
||||||
<section>
|
<section>
|
||||||
1
|
1
|
||||||
|
<button onClick={() => setTitle('123')}>123</button>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
2
|
2
|
||||||
|
|||||||
@@ -1,3 +1,149 @@
|
|||||||
.main {
|
.main {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header .dropdown {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navButton .bar:nth-child(1) {
|
||||||
|
transform: translate(0, -200%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.navButton .bar:nth-child(3) {
|
||||||
|
transform: translate(0, 200%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.navButton.active .bar:nth-child(1) {
|
||||||
|
transform: translate(0, 100%) rotateZ(45deg) scaleX(0.5) translate(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.navButton.active .bar:nth-child(2) {
|
||||||
|
transform: rotateZ(-45deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.navButton.active .bar:nth-child(3) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
.drawer .overlay {
|
||||||
|
height: 100%;
|
||||||
|
flex-grow: 1;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.drawer.active {
|
||||||
|
pointer-events: all;
|
||||||
|
left: 0;
|
||||||
|
width: 100vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.drawer a {
|
||||||
|
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 a:hover {
|
||||||
|
color: var(--link-highlight-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.main {
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 90%;
|
||||||
|
max-width: 100rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer .links {
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer .links .separator {
|
||||||
|
height: 1rem;
|
||||||
|
border-left: 2px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer .copyright {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
useState,
|
useState,
|
||||||
useEffect
|
useEffect,
|
||||||
|
useContext
|
||||||
} from 'react'
|
} from 'react'
|
||||||
import {
|
import {
|
||||||
Outlet,
|
Outlet,
|
||||||
@@ -11,21 +12,100 @@ import {
|
|||||||
useNavigate,
|
useNavigate,
|
||||||
} from "react-router-dom";
|
} from "react-router-dom";
|
||||||
import './root.css'
|
import './root.css'
|
||||||
|
import routes from '@/routes'
|
||||||
|
import { TitleContext } from '@/context/useTitleContext';
|
||||||
|
import { LanguageContext } from '@/context/useLanguageContext';
|
||||||
|
import Dropdown from '@/component/dropdown';
|
||||||
|
import Popup from '@/component/popup';
|
||||||
|
import ReturnButton from '@/component/return_button';
|
||||||
|
|
||||||
export default function Root(props) {
|
export default function Root(props) {
|
||||||
|
const { title, setTitle } = useContext(TitleContext)
|
||||||
|
const [drawerHidden, setDrawerHidden] = useState(true)
|
||||||
|
const {
|
||||||
|
language, setLanguage,
|
||||||
|
drawerTextDefaultLang,
|
||||||
|
drawerAlternateLang,
|
||||||
|
i18n
|
||||||
|
} = useContext(LanguageContext)
|
||||||
|
const [drawerDestinations, setDrawerDestinations] = useState(null)
|
||||||
|
|
||||||
|
const toggleDrawer = () => {
|
||||||
|
setDrawerHidden(!drawerHidden)
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderDrawerDestinations = () => {
|
||||||
|
return routes.filter((item) => item.inDrawer).map((item) => {
|
||||||
|
if (typeof item.element.type === 'string') {
|
||||||
|
return (
|
||||||
|
<a key={item.name}
|
||||||
|
href={item.path}
|
||||||
|
target="_blank">
|
||||||
|
<section>
|
||||||
|
{i18n.key[item.name][drawerTextDefaultLang]}
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
{i18n.key[item.name][drawerAlternateLang]}
|
||||||
|
</section>
|
||||||
|
</a>
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<NavLink to={item.path} key={item.name}>
|
||||||
|
<section>
|
||||||
|
{i18n.key[item.name][drawerTextDefaultLang]}
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
{i18n.key[item.name][drawerAlternateLang]}
|
||||||
|
</section>
|
||||||
|
</NavLink>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setDrawerDestinations(renderDrawerDestinations())
|
||||||
|
}, [drawerAlternateLang])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<header>
|
<header className='header'>
|
||||||
<section>Nav button</section>
|
<section
|
||||||
<nav>
|
className={`navButton ${drawerHidden ? '' : 'active'}`}
|
||||||
<NavLink to="/">Home</NavLink>
|
onClick={() => toggleDrawer()}
|
||||||
<NavLink to="/operator/chen">About</NavLink>
|
>
|
||||||
<NavLink to="/contact">Contact</NavLink>
|
<section className='bar'></section>
|
||||||
</nav>
|
<section className='bar'></section>
|
||||||
|
<section className='bar'></section>
|
||||||
|
</section>
|
||||||
|
<Dropdown
|
||||||
|
text={i18n.key[language][language]}
|
||||||
|
menu={i18n.available.map((item) => {
|
||||||
|
return {
|
||||||
|
name: i18n.key[item][language],
|
||||||
|
value: item
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
onClick={(item) => {
|
||||||
|
setLanguage(item.value)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</header>
|
</header>
|
||||||
|
<nav className={`drawer ${drawerHidden ? '' : 'active'}`}>
|
||||||
|
<section
|
||||||
|
className='links'
|
||||||
|
>
|
||||||
|
{drawerDestinations}
|
||||||
|
</section>
|
||||||
|
<section
|
||||||
|
className={`overlay ${drawerHidden ? '' : 'active'}`}
|
||||||
|
onClick={() => toggleDrawer()}
|
||||||
|
/>
|
||||||
|
</nav>
|
||||||
<main className='main'>
|
<main className='main'>
|
||||||
<section>
|
<section>
|
||||||
<section>Dynamic Compile</section>
|
<section>{title}</section>
|
||||||
<section>
|
<section>
|
||||||
{/* <NavLink to="/home">All</NavLink>
|
{/* <NavLink to="/home">All</NavLink>
|
||||||
<NavLink to="/about">Operator</NavLink>
|
<NavLink to="/about">Operator</NavLink>
|
||||||
@@ -34,16 +114,23 @@ export default function Root(props) {
|
|||||||
</section>
|
</section>
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</main>
|
</main>
|
||||||
<footer>
|
<footer className='footer'>
|
||||||
<section>
|
<section className='links section'>
|
||||||
<p>Privacy Policy</p>
|
<a href="https://privacy.halyul.dev" target="_blank" className='item'>{i18n.key.privacy_policy[language]}</a>
|
||||||
<p>Github</p>
|
<span className='separator' />
|
||||||
<p>Contact Us</p>
|
<a href="https://github.com/Halyul/aklive2d" target="_blank" className='item'>Github</a>
|
||||||
|
<span className='separator'/>
|
||||||
|
<Popup
|
||||||
|
className='item'
|
||||||
|
title={i18n.key.contact_us[language]}
|
||||||
|
>
|
||||||
|
ak#halyul.dev
|
||||||
|
</Popup>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section className='copyright section'>
|
||||||
<p>Spine Runtimes © 2013 - 2019, Esoteric Software LLC</p>
|
<span>Spine Runtimes © 2013 - 2019, Esoteric Software LLC</span>
|
||||||
<p>Assets © 2017 - 2023 上海鹰角网络科技有限公司</p>
|
<span>Assets © 2017 - 2023 上海鹰角网络科技有限公司</span>
|
||||||
<p>Source Code © 2021 - 2023 Halyul</p>
|
<span>Source Code © 2021 - 2023 Halyul</span>
|
||||||
</section>
|
</section>
|
||||||
</footer>
|
</footer>
|
||||||
</>
|
</>
|
||||||
|
|||||||
Reference in New Issue
Block a user