mirror of
https://github.com/VickScarlet/lifeRestart.git
synced 2026-08-28 09:16:47 +08:00
update: hooks, web
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
ul.replaced {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
li {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 1rem;
|
||||
padding: 0;
|
||||
.talent {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
li:not(:first-child) {
|
||||
position: relative;
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
border: 0.0625rem solid var(--base-text-color);
|
||||
border-left: none;
|
||||
right: 0;
|
||||
top: -1rem;
|
||||
margin-right: -1rem;
|
||||
width: 0.625rem;
|
||||
height: 1.5rem;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
border: 0.0625rem solid var(--base-text-color);
|
||||
border-right: none;
|
||||
border-bottom: none;
|
||||
right: 0;
|
||||
top: 0.5rem;
|
||||
margin-right: -0.6875rem;
|
||||
margin-top: -0.03125rem;
|
||||
width: 0.375rem;
|
||||
height: 0.375rem;
|
||||
transform: rotate(-45deg);
|
||||
transform-origin: 0 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import type { Talent } from '@remake/data/talent'
|
||||
import TalentComponent from './Talent'
|
||||
import talents from '@remake/data/talent'
|
||||
import './Replaced.css'
|
||||
|
||||
export interface TalentProps {
|
||||
id: Talent['id']
|
||||
chains?: Talent['id'][]
|
||||
}
|
||||
export function Replaced({ id, chains }: TalentProps) {
|
||||
return (
|
||||
<ul className="replaced">
|
||||
<li>
|
||||
<TalentComponent id={id} selected={true} />
|
||||
</li>
|
||||
{chains?.map(id => {
|
||||
const talent = talents.get(id)
|
||||
if (!talent) return null
|
||||
return (
|
||||
<li key={id}>
|
||||
<TalentComponent id={id} selected={false} />
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
|
||||
export default Replaced
|
||||
@@ -0,0 +1,41 @@
|
||||
.talent {
|
||||
--color: var(--grade-color);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 1rem;
|
||||
line-height: 2rem;
|
||||
gap: 1rem;
|
||||
color: var(--color);
|
||||
background: var(--background, var(--base-background-color));
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
border: 0.0625rem solid var(--grade-color);
|
||||
transition: all 0.2s ease-in-out;
|
||||
&.talent-grade-0 { --grade-color: var(--color-grade-0); }
|
||||
&.talent-grade-1 { --grade-color: var(--color-grade-1); }
|
||||
&.talent-grade-2 { --grade-color: var(--color-grade-2); }
|
||||
&.talent-grade-3 { --grade-color: var(--color-grade-3); }
|
||||
&.selected {
|
||||
--color: var(--base-background-color);
|
||||
--background: var(--grade-color);
|
||||
&:hover { --background: oklch(from var(--grade-color) calc(l + 0.1) c h); }
|
||||
}
|
||||
&:hover { --background: oklch(from var(--grade-color) calc(l + 0.5) c h); }
|
||||
.talent-description {
|
||||
color: oklch(from var(--color) calc(l + 0.1) c h);
|
||||
font-size: 0.6rem;
|
||||
align-self: flex-end;
|
||||
user-select: none;
|
||||
&::before { content: '['; margin-right: 0.125rem; }
|
||||
&::after { content: ']'; margin-left: 0.125rem; }
|
||||
&::before, &::after { color: var(--color); }
|
||||
}
|
||||
}
|
||||
html[data-theme='dark'] {
|
||||
.talent{
|
||||
&:hover {--background: oklch(from var(--grade-color) calc(l - 0.5) c h);}
|
||||
&.selected:hover { --background: oklch(from var(--grade-color) calc(l - 0.1) c h);}
|
||||
.talent-description { color: oklch(from var(--color) calc(l - 0.2) c h); }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import type { Talent } from '@remake/data/talent'
|
||||
import talents from '@remake/data/talent'
|
||||
import cx from 'classnames'
|
||||
import './Talent.css'
|
||||
|
||||
export interface TalentProps {
|
||||
id: Talent['id']
|
||||
selected: boolean
|
||||
}
|
||||
export function TalentComponent({ id, selected }: TalentProps) {
|
||||
const talent = talents.get(id)
|
||||
if (!talent) return null
|
||||
const grade = talent?.grade ?? 0
|
||||
return (
|
||||
<div className={cx('talent', `talent-grade-${grade}`, { selected })}>
|
||||
<span className="talent-name">{talent.name}</span>
|
||||
<span className="talent-description">{talent.description}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default TalentComponent
|
||||
@@ -0,0 +1,46 @@
|
||||
.theme-toggle-btn {
|
||||
background: transparent;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
padding: 0.25rem;
|
||||
border-radius: 50%;
|
||||
position: fixed;
|
||||
top: 0.5rem;
|
||||
right: 0.5rem;
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
transition: background-color 0.3s;
|
||||
--hover-color: rgba(0, 0, 0, 0.05);
|
||||
&:hover { background-color: var(--hover-color);}
|
||||
}
|
||||
|
||||
.theme-svg {
|
||||
transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transform-origin: center;
|
||||
.sun-center, .sun-beams, .mask-cutter-group {
|
||||
transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.5s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transform-origin: center;
|
||||
}
|
||||
.mask-cutter-group { transform: translate(0.75rem, -0.75rem); }
|
||||
.sun-center { fill: oklch(from var(--base-text-color) calc(l + 0.2) c h); }
|
||||
.sun-beams { stroke: oklch(from var(--base-text-color) calc(l + 0.2) c h); }
|
||||
}
|
||||
|
||||
html[data-theme='dark'] {
|
||||
.theme-toggle-btn { --hover-color: rgba(255, 255, 255, 0.1); }
|
||||
.mask-cutter-group { transform: translate(0, 0); }
|
||||
.theme-svg { transform: rotate(375deg); }
|
||||
.sun-center {
|
||||
transform: scale(1.5);
|
||||
fill: #fbbf24;
|
||||
}
|
||||
.sun-beams {
|
||||
opacity: 0;
|
||||
transform: scale(0.6);
|
||||
stroke: #fbbf24;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import './ThemeToggle.css'
|
||||
|
||||
export function ThemeToggle() {
|
||||
const [isDark, setIsDark] = useState(
|
||||
() => matchMedia('(prefers-color-scheme: dark)').matches,
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
document.documentElement.dataset.theme = isDark ? 'dark' : 'light'
|
||||
}, [isDark])
|
||||
|
||||
return (
|
||||
<button
|
||||
className="theme-toggle-btn"
|
||||
onClick={() => setIsDark(!isDark)}
|
||||
aria-label="Toggle theme"
|
||||
>
|
||||
<svg
|
||||
className="theme-svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<defs>
|
||||
<mask id="ios-fail-proof-mask">
|
||||
<rect
|
||||
x="0"
|
||||
y="0"
|
||||
width="24"
|
||||
height="24"
|
||||
fill="#ffffff"
|
||||
/>
|
||||
<g className="mask-cutter-group">
|
||||
<circle cx="18" cy="5" r="7" fill="#000000" />
|
||||
</g>
|
||||
</mask>
|
||||
</defs>
|
||||
|
||||
<circle
|
||||
className="sun-center"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="5"
|
||||
mask="url(#ios-fail-proof-mask)"
|
||||
/>
|
||||
<g className="sun-beams">
|
||||
<line x1="12" y1="1" x2="12" y2="3" />
|
||||
<line x1="12" y1="21" x2="12" y2="23" />
|
||||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64" />
|
||||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78" />
|
||||
<line x1="1" y1="12" x2="3" y2="12" />
|
||||
<line x1="21" y1="12" x2="23" y2="12" />
|
||||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36" />
|
||||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22" />
|
||||
</g>
|
||||
</svg>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
export default ThemeToggle
|
||||
Reference in New Issue
Block a user