From 6f3e0516547904f989f2e105ef3f69f88f76c699 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=90=E6=B6=9B?= Date: Wed, 25 Dec 2024 15:35:35 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=96=B9=E6=A1=88=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E7=9A=84=E5=B8=83=E5=B1=80=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 10 ++- .../schemes/SchemeList.module.css | 33 +++++++ src/page-components/schemes/SchemeList.tsx | 59 ++++++++++++ src/pages/Schemes.module.css | 13 +++ src/pages/Schemes.tsx | 14 +++ src/stores/schemes.ts | 90 +++++++++++++++++++ 6 files changed, 218 insertions(+), 1 deletion(-) create mode 100644 src/page-components/schemes/SchemeList.module.css create mode 100644 src/page-components/schemes/SchemeList.tsx create mode 100644 src/pages/Schemes.module.css create mode 100644 src/pages/Schemes.tsx create mode 100644 src/stores/schemes.ts diff --git a/src/App.tsx b/src/App.tsx index a466f14..ae998d6 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2,9 +2,17 @@ import { createBrowserRouter, RouterProvider } from 'react-router-dom'; import { Notifications } from './components/Notifications'; import { Home } from './pages/Home'; import { MainLayout } from './pages/MainLayout'; +import { Schemes } from './pages/Schemes'; const routes = createBrowserRouter([ - { path: '/', element: , children: [{ index: true, element: }] }, + { + path: '/', + element: , + children: [ + { index: true, element: }, + { path: 'schemes', element: }, + ], + }, ]); export function App() { diff --git a/src/page-components/schemes/SchemeList.module.css b/src/page-components/schemes/SchemeList.module.css new file mode 100644 index 0000000..a0589d1 --- /dev/null +++ b/src/page-components/schemes/SchemeList.module.css @@ -0,0 +1,33 @@ +@layer pages { + .scheme_list { + max-width: calc(var(--spacing) * 125); + flex: 1 1 calc(var(--spacing) * 125); + padding: calc(var(--spacing) * 4) 0; + box-shadow: 2px 0 8px oklch(from var(--color-black) l c h / 65%); + z-index: 40; + .operates_buttons { + width: 100%; + display: flex; + flex-direction: row; + justify-content: flex-end; + align-items: center; + gap: calc(var(--spacing) * 2); + padding: calc(var(--spacing) * 4) calc(var(--spacing) * 6); + } + .scheme_items { + flex-grow: 1; + overflow-y: auto; + display: flex; + flex-direction: column; + align-items: stretch; + } + .empty_prompt { + padding-inline: calc(var(--spacing) * 6); + padding-block: calc(var(--spacing) * 4); + font-size: var(--font-size-s); + font-style: italic; + text-align: center; + color: var(--color-neutral-focus); + } + } +} diff --git a/src/page-components/schemes/SchemeList.tsx b/src/page-components/schemes/SchemeList.tsx new file mode 100644 index 0000000..ab60db0 --- /dev/null +++ b/src/page-components/schemes/SchemeList.tsx @@ -0,0 +1,59 @@ +import { Icon } from '@iconify/react/dist/iconify.js'; +import dayjs from 'dayjs'; +import { useAtomValue } from 'jotai'; +import { isEmpty, isEqual } from 'lodash-es'; +import { useMemo } from 'react'; +import { Link } from 'react-router-dom'; +import { activeSchemeAtom, useSchemeList } from '../../stores/schemes'; +import styles from './SchemeList.module.css'; + +function OperateButtons() { + return ( +
+ + New Scheme + +
+ ); +} + +type SchemeItemProps = { + item: ReturnType[number]; +}; + +function SchemeItem({ item }: SchemeItemProps) { + const activedScheme = useAtomValue(activeSchemeAtom); + const isActived = useMemo(() => isEqual(activedScheme, item.id), [activedScheme, item.id]); + + return ( +
+
{item.name}
+
+
created at {dayjs(item.createdAt).format('YYYY-MM-DD')}
+
{isActived && }
+
+
+ ); +} + +function SchemesItems() { + const schemes = useSchemeList(); + + return ( +
+ {isEmpty(schemes) &&
Create a scheme first.
} + {schemes.map((item) => ( + + ))} +
+ ); +} + +export function SchemeList() { + return ( +
+ + +
+ ); +} diff --git a/src/pages/Schemes.module.css b/src/pages/Schemes.module.css new file mode 100644 index 0000000..5ef54b2 --- /dev/null +++ b/src/pages/Schemes.module.css @@ -0,0 +1,13 @@ +@layer pages { + .schemes_workspace { + height: 100%; + width: 100%; + overflow: hidden; + display: flex; + flex-direction: row; + align-items: stretch; + } + .scheme_operates { + flex: 1 0; + } +} diff --git a/src/pages/Schemes.tsx b/src/pages/Schemes.tsx new file mode 100644 index 0000000..1161be0 --- /dev/null +++ b/src/pages/Schemes.tsx @@ -0,0 +1,14 @@ +import { Outlet } from 'react-router-dom'; +import { SchemeList } from '../page-components/schemes/SchemeList'; +import styles from './Schemes.module.css'; + +export function Schemes() { + return ( +
+ +
+ +
+
+ ); +} diff --git a/src/stores/schemes.ts b/src/stores/schemes.ts new file mode 100644 index 0000000..ff81ff1 --- /dev/null +++ b/src/stores/schemes.ts @@ -0,0 +1,90 @@ +import dayjs from 'dayjs'; +import { useAtomValue, useSetAtom } from 'jotai'; +import { atomWithStorage } from 'jotai/utils'; +import { useCallback, useMemo } from 'react'; +import { v4 } from 'uuid'; + +type ColorSet = { + normal?: string | null; + hover?: string | null; + active?: string | null; + focus?: string | null; + disabled?: string | null; + lighten?: string | null; + darken?: string | null; +}; + +type Scheme = { + primary?: ColorSet | null; + secondary?: ColorSet | ColorSet[] | null; + accent?: ColorSet | null; + neutral?: ColorSet | null; + foreground?: ColorSet | null; + background?: ColorSet | null; + danger?: ColorSet | null; + warning?: ColorSet | null; + success?: ColorSet | null; + info?: ColorSet | null; + border?: ColorSet | null; +}; + +export type SchemeSet = { + id: string; + name: string; + createdAt: string; + description: string | null; + lightScheme: Scheme; + darkScheme: Scheme; +}; + +const schemesAtom = atomWithStorage('schemes', []); +export const activeSchemeAtom = atomWithStorage('activeScheme', null); + +export function useSchemeList(): Pick[] { + const schemes = useAtomValue(schemesAtom); + const sortedSchemes = useMemo( + () => + schemes + .sort((a, b) => dayjs(b.createdAt).diff(dayjs(a.createdAt))) + .map(({ id, name, createdAt }) => ({ id, name, createdAt })), + [schemes], + ); + + return sortedSchemes; +} + +export function useScheme(id: string): SchemeSet | null { + const schemes = useAtomValue(schemesAtom); + const scheme = useMemo(() => schemes.find((s) => s.id === id) ?? null, [schemes, id]); + return scheme; +} + +export function useActiveScheme(): SchemeSet | null { + const activeSchemeId = useAtomValue(activeSchemeAtom); + const activeScheme = useScheme(activeSchemeId ?? 'UNEXISTS'); + return activeScheme; +} + +export function useCreateScheme(): (name: string, description?: string) => string { + const updateSchemes = useSetAtom(schemesAtom); + const createSchemeAction = useCallback( + (name: string, description?: string) => { + const newId = v4(); + updateSchemes((prev) => [ + ...prev, + { + id: newId, + name, + createdAt: dayjs().toISOString(), + description: description ?? null, + lightScheme: {}, + darkScheme: {}, + }, + ]); + return newId; + }, + [updateSchemes], + ); + + return createSchemeAction; +}