diff --git a/apps/admin/package.json b/apps/admin/package.json index d39dca3..39cc77e 100644 --- a/apps/admin/package.json +++ b/apps/admin/package.json @@ -23,6 +23,7 @@ "lodash": "^4.17.21", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-use": "^17.5.0", "zustand": "^4.5.2" }, "devDependencies": { diff --git a/apps/admin/src/Wrapper.tsx b/apps/admin/src/Wrapper.tsx new file mode 100644 index 0000000..c61c2f1 --- /dev/null +++ b/apps/admin/src/Wrapper.tsx @@ -0,0 +1,51 @@ +import { StyleProvider } from '@ant-design/cssinjs'; +import { ConfigProvider, App as AntdApp } from 'antd'; +import { FC, useMemo } from 'react'; + +import $styles from './app.module.css'; +import { localeData } from './components/demo/constants'; +import ContextDemo from './components/demo/context'; +import CustomDemo from './components/demo/custom'; +import { useLocale } from './components/demo/hooks'; +import { useAntdAlgorithm } from './components/theme/hooks'; + +export const Wrapper: FC = () => { + const locale = useLocale(); + const antdLocaleData = useMemo(() => { + if (!Object.keys(localeData).find((v) => v === locale.name)) { + return localeData[0]; + } + return localeData[locale.name]; + }, [locale.name]); + // const themeState = useTheme(); + // const algorithm = useMemo(() => { + // const result = [themeState.compact ? theme.compactAlgorithm : theme.defaultAlgorithm]; + // if (themeState.mode === 'dark') result.push(theme.darkAlgorithm); + // return result; + // }, [themeState]); + const algorithm = useAntdAlgorithm(); + return ( + + + +
+ {/* + + + + */} + + {/* */} + +
+
+
+
+ ); +}; diff --git a/apps/admin/src/app.tsx b/apps/admin/src/app.tsx index 976a65d..e34fd2c 100644 --- a/apps/admin/src/app.tsx +++ b/apps/admin/src/app.tsx @@ -1,15 +1,16 @@ import { StyleProvider } from '@ant-design/cssinjs'; -import { ConfigProvider, theme, App as AntdApp } from 'antd'; +import { ConfigProvider, App as AntdApp } from 'antd'; // import 'dayjs/locale/zh-cn'; import { FC, useMemo } from 'react'; import $styles from './app.module.css'; import { localeData } from './components/demo/constants'; -import ContextDemo, { Locale } from './components/demo/context'; -import CustomDemo from './components/demo/custom'; -import { useLocale, useTheme } from './components/demo/hooks'; -import ReducerDemo, { Theme } from './components/demo/reducer'; +import { Locale } from './components/demo/context'; +import { useLocale } from './components/demo/hooks'; +import Theme from './components/theme'; +import ThemeDemo from './components/theme/demo'; +import { useAntdAlgorithm } from './components/theme/hooks'; const Wrapper: FC = () => { const locale = useLocale(); @@ -19,12 +20,13 @@ const Wrapper: FC = () => { } return localeData[locale.name]; }, [locale.name]); - const themeState = useTheme(); - const algorithm = useMemo(() => { - const result = [themeState.compact ? theme.compactAlgorithm : theme.defaultAlgorithm]; - if (themeState.mode === 'dark') result.push(theme.darkAlgorithm); - return result; - }, [themeState]); + // const themeState = useTheme(); + // const algorithm = useMemo(() => { + // const result = [themeState.compact ? theme.compactAlgorithm : theme.defaultAlgorithm]; + // if (themeState.mode === 'dark') result.push(theme.darkAlgorithm); + // return result; + // }, [themeState]); + const algorithm = useAntdAlgorithm(); return ( { - */} + - + */} + diff --git a/apps/admin/src/components/demo/types.ts b/apps/admin/src/components/demo/types.ts index 2d0105a..3456b02 100644 --- a/apps/admin/src/components/demo/types.ts +++ b/apps/admin/src/components/demo/types.ts @@ -21,12 +21,16 @@ export type ThemeState = { export enum ThemeActionType { CHANGE_MODE = 'change_mode', + TOOGLE_MODE = 'toggle_mode', CHANGE_COMPACT = 'change_compact', + TOOGLE_COMPACT = 'toggle_compact', } export type ThemeAction = | { type: `${ThemeActionType.CHANGE_MODE}`; value: `${ThemeMode}` } - | { type: `${ThemeActionType.CHANGE_COMPACT}`; value: boolean }; + | { type: `${ThemeActionType.TOOGLE_MODE}` } + | { type: `${ThemeActionType.CHANGE_COMPACT}`; value: boolean } + | { type: `${ThemeActionType.TOOGLE_COMPACT}` }; export type ThemeContextType = { state: ThemeState; diff --git a/apps/admin/src/components/store/index.ts b/apps/admin/src/components/store/index.ts index f0c4eaa..ccd1687 100644 --- a/apps/admin/src/components/store/index.ts +++ b/apps/admin/src/components/store/index.ts @@ -1,2 +1,167 @@ -export * from './utils'; -export * from './types'; +import { capitalize } from 'lodash'; +import { create, StateCreator, Mutate, StoreApi, UseBoundStore } from 'zustand'; +import { + subscribeWithSelector, + devtools, + persist, + PersistOptions, + DevtoolsOptions, + redux, +} from 'zustand/middleware'; +import { immer } from 'zustand/middleware/immer'; + +export type ZustandHookSelectors = { + [Key in keyof StateType as `use${Capitalize}`]: () => StateType[Key]; +}; +export interface ZustandGetterSelectors { + getters: { + [key in keyof StateType]: () => StateType[key]; + }; +} + +/** + * 创建包含订阅,immer以及devtoools功能的普通状态商店 + * @param creator + * @param devtoolsOptions + */ +export const createStore = ( + creator: StateCreator< + T, + [ + ['zustand/subscribeWithSelector', never], + ['zustand/immer', never], + ['zustand/devtools', never], + ] + >, + devtoolsOptions?: DevtoolsOptions, +) => { + return create()(subscribeWithSelector(immer(devtools(creator, devtoolsOptions)))); +}; + +/** + * 创建包含订阅,immer以及devtoools功能的普通状态商店 + * 同时支持自动存储到客户端,默认存储到localstorage + * @param creator + * @param persistOptions + * @param devtoolsOptions + */ +export const createPersistStore = ( + creator: StateCreator< + T, + [ + ['zustand/subscribeWithSelector', never], + ['zustand/immer', never], + ['zustand/devtools', never], + ['zustand/persist', P], + ] + >, + persistOptions: PersistOptions, + devtoolsOptions?: DevtoolsOptions, +) => { + return create()( + subscribeWithSelector( + immer(devtools(persist(creator as unknown as any, persistOptions), devtoolsOptions)), + ), + ); +}; + +/** + * 创建包含订阅,immer以及devtoools功能的reducer状态商店 + * 同时支持自动存储到客户端,默认存储到localstorage + * @param reducer + * @param initialState + * @param devtoolsOptions + */ +export const createReduxStore = < + T extends object, + A extends { + type: string; + }, +>( + reducer: (state: T, action: A) => T, + initialState: T, + devtoolsOptions?: DevtoolsOptions, +) => create(subscribeWithSelector(immer(devtools(redux(reducer, initialState), devtoolsOptions)))); + +/** + * 创建包含订阅,immer以及devtoools功能的reducer状态商店 + * @param reducer + * @param initialState + * @param persistOptions + * @param devtoolsOptions + */ +export const createPersistReduxStore = < + T extends object, + A extends { + type: string; + }, + P = T, +>( + reducer: (state: T, action: A) => T, + initialState: T, + persistOptions: PersistOptions, + devtoolsOptions?: DevtoolsOptions, +) => + create( + subscribeWithSelector( + immer( + devtools( + persist(redux(reducer, initialState), persistOptions as any), + devtoolsOptions, + ), + ), + ), + ); + +/** + * 直接通过getters获取状态值,比如store.getters.xxx() + * @param store + */ +export function createStoreGetters( + store: UseBoundStore< + Mutate< + StoreApi, + [ + ['zustand/subscribeWithSelector', never], + ['zustand/immer', never], + ['zustand/devtools', never], + ] + > + >, +) { + const storeIn = store as any; + + storeIn.getters = {}; + Object.keys(storeIn.getState()).forEach((key) => { + const selector = (state: T) => state[key as keyof T]; + storeIn.getters[key] = () => storeIn(selector); + }); + + return storeIn as typeof store & ZustandGetterSelectors; +} + +/** + * 直接通过类似hooks的方法获取状态值,比如store.useXxx() + * @param store + */ +export function createStoreHooks>( + store: UseBoundStore< + Mutate< + StoreApi, + [ + ['zustand/subscribeWithSelector', never], + ['zustand/immer', never], + ['zustand/devtools', never], + ] + > + >, +) { + const storeIn = store as any; + + Object.keys(storeIn.getState()).forEach((key) => { + const selector = (state: T) => state[key as keyof T]; + storeIn[`use${capitalize(key)}`] = () => storeIn(selector); + }); + + return storeIn as typeof store & ZustandHookSelectors; +} diff --git a/apps/admin/src/components/store/types.ts b/apps/admin/src/components/store/types.ts deleted file mode 100644 index 8d1d002..0000000 --- a/apps/admin/src/components/store/types.ts +++ /dev/null @@ -1,8 +0,0 @@ -export type ZustandHookSelectors = { - [Key in keyof StateType as `use${Capitalize}`]: () => StateType[Key]; -}; -export interface ZustandGetterSelectors { - getters: { - [key in keyof StateType]: () => StateType[key]; - }; -} diff --git a/apps/admin/src/components/store/utils.ts b/apps/admin/src/components/store/utils.ts deleted file mode 100644 index 36a4bcb..0000000 --- a/apps/admin/src/components/store/utils.ts +++ /dev/null @@ -1,160 +0,0 @@ -import { capitalize } from 'lodash'; -import { create, Mutate, StateCreator, StoreApi, UseBoundStore } from 'zustand'; -import { - subscribeWithSelector, - devtools, - persist, - PersistOptions, - DevtoolsOptions, - redux, -} from 'zustand/middleware'; -import { immer } from 'zustand/middleware/immer'; - -import { ZustandGetterSelectors, ZustandHookSelectors } from './types'; - -/** - * 创建包含订阅,immer以及devtoools功能的普通状态商店 - * @param creator - * @param devtoolsOptions - */ -export const createStore = ( - creator: StateCreator< - T, - [ - ['zustand/subscribeWithSelector', never], - ['zustand/immer', never], - ['zustand/devtools', never], - ] - >, - devtoolsOptions?: DevtoolsOptions, -) => { - return create()(subscribeWithSelector(immer(devtools(creator, devtoolsOptions)))); -}; - -/** - * 创建包含订阅,immer以及devtoools功能的普通状态商店 - * 同时支持自动存储到客户端,默认存储到localstorage - * @param creator - * @param persistOptions - * @param devtoolsOptions - */ -export const createPersistStore = ( - creator: StateCreator< - T, - [ - ['zustand/subscribeWithSelector', never], - ['zustand/immer', never], - ['zustand/devtools', never], - ['zustand/persist', P], - ] - >, - persistOptions: PersistOptions, - devtoolsOptions?: DevtoolsOptions, -) => { - return create()( - subscribeWithSelector( - immer(devtools(persist(creator as unknown as any, persistOptions), devtoolsOptions)), - ), - ); -}; - -/** - * 创建包含订阅,immer以及devtoools功能的reducer状态商店 - * 同时支持自动存储到客户端,默认存储到localstorage - * @param reducer - * @param initialState - * @param devtoolsOptions - */ -export const createReduxStore = < - T extends object, - A extends { - type: string; - }, ->( - reducer: (state: T, action: A) => T, - initialState: T, - devtoolsOptions?: DevtoolsOptions, -) => create(subscribeWithSelector(immer(devtools(redux(reducer, initialState), devtoolsOptions)))); - -/** - * 创建包含订阅,immer以及devtoools功能的reducer状态商店 - * @param reducer - * @param initialState - * @param persistOptions - * @param devtoolsOptions - */ -export const createPersistReduxStore = < - T extends object, - A extends { - type: string; - }, - P = T, ->( - reducer: (state: T, action: A) => T, - initialState: T, - persistOptions: PersistOptions, - devtoolsOptions?: DevtoolsOptions, -) => - create( - subscribeWithSelector( - immer( - devtools( - persist(redux(reducer, initialState), persistOptions as any), - devtoolsOptions, - ), - ), - ), - ); - -/** - * 直接通过getters获取状态值,比如store.getters.xxx() - * @param store - */ -export function createStoreGetters( - store: UseBoundStore< - Mutate< - StoreApi, - [ - ['zustand/subscribeWithSelector', never], - ['zustand/immer', never], - ['zustand/devtools', never], - ] - > - >, -) { - const storeIn = store as any; - - storeIn.getters = {}; - Object.keys(storeIn.getState()).forEach((key) => { - const selector = (state: T) => state[key as keyof T]; - storeIn.getters[key] = () => storeIn(selector); - }); - - return storeIn as typeof store & ZustandGetterSelectors; -} - -/** - * 直接通过类似hooks的方法获取状态值,比如store.useXxx() - * @param store - */ -export function createStoreHooks>( - store: UseBoundStore< - Mutate< - StoreApi, - [ - ['zustand/subscribeWithSelector', never], - ['zustand/immer', never], - ['zustand/devtools', never], - ] - > - >, -) { - const storeIn = store as any; - - Object.keys(storeIn.getState()).forEach((key) => { - const selector = (state: T) => state[key as keyof T]; - storeIn[`use${capitalize(key)}`] = () => storeIn(selector); - }); - - return storeIn as typeof store & ZustandHookSelectors; -} diff --git a/apps/admin/src/components/theme/constants.ts b/apps/admin/src/components/theme/constants.ts new file mode 100644 index 0000000..3e06945 --- /dev/null +++ b/apps/admin/src/components/theme/constants.ts @@ -0,0 +1,6 @@ +import { ThemeState } from './types'; + +export const defaultThemeConfig: ThemeState = { + mode: 'light', + compact: false, +}; diff --git a/apps/admin/src/components/theme/demo.tsx b/apps/admin/src/components/theme/demo.tsx new file mode 100644 index 0000000..e767452 --- /dev/null +++ b/apps/admin/src/components/theme/demo.tsx @@ -0,0 +1,48 @@ +import { Calendar, Switch } from 'antd'; +import { FC } from 'react'; + +import { useTheme, useThemeActions } from './hooks'; +import $styles from './style.module.css'; + +const Setting: FC = () => { + const { mode, compact } = useTheme(); + const { toggleMode, toggleCompact } = useThemeActions(); + return ( + <> + + + + ); +}; + +const ThemeDemo: FC = () => { + const { mode, compact } = useTheme(); + return ( +
+

Theme Setting Demo

+
+

主题模式: 「{mode === 'dark' ? '暗黑' : '明亮'}」

+

是否紧凑: 「{compact ? '是' : '否'}」

+
+ +
+
+ +
+
+
+ ); +}; +export default ThemeDemo; diff --git a/apps/admin/src/components/theme/hooks.ts b/apps/admin/src/components/theme/hooks.ts new file mode 100644 index 0000000..dcd1ab7 --- /dev/null +++ b/apps/admin/src/components/theme/hooks.ts @@ -0,0 +1,46 @@ +import { theme } from 'antd'; +import { omit } from 'lodash'; +import { useCallback, useMemo } from 'react'; +import { useShallow } from 'zustand/react/shallow'; + +import { useThemeStore } from './store'; +import { ThemeMode } from './types'; + +/** + * 获取主题状态 + */ +export const useTheme = () => useThemeStore(useShallow((state) => omit(state, ['dispatch']))); + +/** + * 获取Antd主题算法 + */ +export const useAntdAlgorithm = () => { + const { mode, compact } = useTheme(); + return useMemo(() => { + const result = [compact ? theme.compactAlgorithm : theme.defaultAlgorithm]; + if (mode === 'dark') result.push(theme.darkAlgorithm); + return result; + }, [mode, compact]); +}; + +/** + * 主题操作函数 + */ +export const useThemeActions = () => { + const dispatch = useThemeStore((state) => state.dispatch); + return { + changeMode: useCallback( + (v: `${ThemeMode}`) => dispatch({ type: 'change_mode', value: v }), + [], + ), + toggleMode: useCallback(() => dispatch({ type: 'toggle_mode' }), []), + changeCompact: useCallback( + (v: boolean) => dispatch({ type: 'change_compact', value: v }), + [], + ), + toggleCompact: useCallback( + useCallback(() => dispatch({ type: 'toggle_compact' }), []), + [], + ), + }; +}; diff --git a/apps/admin/src/components/theme/index.tsx b/apps/admin/src/components/theme/index.tsx new file mode 100644 index 0000000..774b3e3 --- /dev/null +++ b/apps/admin/src/components/theme/index.tsx @@ -0,0 +1,50 @@ +import { debounce, isNil } from 'lodash'; +import { FC, ReactNode } from 'react'; + +import { useLifecycles } from 'react-use'; + +import { useThemeStore } from './store'; + +const Theme: FC<{ children?: ReactNode }> = ({ children }) => { + let unSub: () => void; + + useLifecycles( + () => { + useThemeStore.subscribe( + (state) => state.mode, + (mode) => { + debounce(() => { + console.log(mode); + }); + const body = document.getElementsByTagName('body'); + if (body.length) { + body[0].classList.remove('light'); + body[0].classList.remove('dark'); + body[0].classList.add(mode === 'dark' ? 'dark' : 'light'); + } + }, + // debounce( + // () => { + // const body = document.getElementsByTagName('body'); + // if (body.length) { + // body[0].classList.remove('light'); + // body[0].classList.remove('dark'); + // body[0].classList.add(mode === 'dark' ? 'dark' : 'light'); + // } + // }, + // 10, + // {}, + // ), + { + fireImmediately: true, + }, + ); + }, + () => { + if (!isNil(unSub)) unSub(); + }, + ); + + return <>{children}; +}; +export default Theme; diff --git a/apps/admin/src/components/theme/store.ts b/apps/admin/src/components/theme/store.ts new file mode 100644 index 0000000..1882012 --- /dev/null +++ b/apps/admin/src/components/theme/store.ts @@ -0,0 +1,32 @@ +import { produce } from 'immer'; +import { Reducer } from 'react'; + +import { ThemeAction } from '../demo/types'; +import { createPersistReduxStore } from '../store'; + +import { defaultThemeConfig } from './constants'; +import { ThemeState } from './types'; + +const ThemeReducer: Reducer = produce((draft, action) => { + switch (action.type) { + case 'change_mode': + draft.mode = action.value; + break; + case 'toggle_mode': + draft.mode = draft.mode === 'dark' ? 'light' : 'dark'; + break; + case 'change_compact': + draft.compact = action.value; + break; + case 'toggle_compact': + draft.compact = !draft.compact; + break; + default: + break; + } +}); + +export const useThemeStore = createPersistReduxStore(ThemeReducer, defaultThemeConfig, { + name: 'theme', + partialize: (state) => ({ mode: state.mode, compact: state.compact }), +}); diff --git a/apps/admin/src/components/theme/style.module.css b/apps/admin/src/components/theme/style.module.css new file mode 100644 index 0000000..6780c1e --- /dev/null +++ b/apps/admin/src/components/theme/style.module.css @@ -0,0 +1,9 @@ +.container { + @apply tw-bg-neutral-100/40 tw-shadow-black/20 tw-backdrop-blur-sm tw-shadow-md tw-rounded-md tw-p-5 tw-m-5 tw-min-w-[20rem]; +} + +body(:global(.dark)) { + .container { + @apply tw-bg-neutral-800/40 tw-shadow-slate-100/30; + } +} diff --git a/apps/admin/src/components/theme/types.ts b/apps/admin/src/components/theme/types.ts new file mode 100644 index 0000000..322f9d4 --- /dev/null +++ b/apps/admin/src/components/theme/types.ts @@ -0,0 +1,9 @@ +export enum ThemeMode { + LIGHT = 'light', + DARK = 'dark', +} + +export type ThemeState = { + mode: `${ThemeMode}`; + compact: boolean; +}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b52fa42..270e76c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -56,6 +56,9 @@ importers: react-dom: specifier: ^18.2.0 version: 18.3.1(react@18.3.1) + react-use: + specifier: ^17.5.0 + version: 17.5.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) zustand: specifier: ^4.5.2 version: 4.5.2(@types/react@18.3.1)(immer@10.1.1)(react@18.3.1) @@ -1632,6 +1635,9 @@ packages: '@types/jest@29.5.12': resolution: {integrity: sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==} + '@types/js-cookie@2.2.7': + resolution: {integrity: sha512-aLkWa0C0vO5b4Sr798E26QgOkss68Un0bLjs7u9qxzPT5CG+8DuNTffWES58YzJs3hrVAOs1wonycqEBqNJubA==} + '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} @@ -1798,6 +1804,9 @@ packages: '@webassemblyjs/wast-printer@1.12.1': resolution: {integrity: sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==} + '@xobotyi/scrollbar-width@1.9.5': + resolution: {integrity: sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ==} + '@xtuc/ieee754@1.2.0': resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} @@ -2361,6 +2370,13 @@ packages: resolution: {integrity: sha512-c+N0v6wbKVxTu5gOBBFkr9BEdBWaqqjQeiJ8QvSRIJOf+UxlJh930m8e6/WNeODIK0mYLFkoONrnj16i2EcvfQ==} engines: {node: '>=12 || >=16'} + css-in-js-utils@3.1.0: + resolution: {integrity: sha512-fJAcud6B3rRu+KHYk+Bwf+WFL2MDCJJ1XG9x137tJQ0xYxor7XziQtuGFbWNdqrvF4Tk26O3H73nfVqXt/fW1A==} + + css-tree@1.1.3: + resolution: {integrity: sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==} + engines: {node: '>=8.0.0'} + css-tree@2.3.1: resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} @@ -2524,6 +2540,9 @@ packages: error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + error-stack-parser@2.1.4: + resolution: {integrity: sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==} + es-abstract@1.23.3: resolution: {integrity: sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==} engines: {node: '>= 0.4'} @@ -2839,6 +2858,9 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fast-loops@1.1.3: + resolution: {integrity: sha512-8EZzEP0eKkEEVX+drtd9mtuQ+/QrlfW/5MlwcwK5Nds6EkZ/tRzEexkzUY2mIssnAyVLT+TKHuRXmFNNXYUd6g==} + fast-querystring@1.1.2: resolution: {integrity: sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==} @@ -2849,6 +2871,9 @@ packages: fast-safe-stringify@2.1.1: resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + fast-shallow-equal@1.0.0: + resolution: {integrity: sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw==} + fast-uri@2.3.0: resolution: {integrity: sha512-eel5UKGn369gGEWOqBShmFJWfq/xSJvsgDzgLYC845GneayWvXBf0lJCBn5qTABfewy1ZDPoaR5OZCP+kssfuw==} @@ -2856,6 +2881,9 @@ packages: resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==} engines: {node: '>= 4.9.1'} + fastest-stable-stringify@2.0.2: + resolution: {integrity: sha512-bijHueCGd0LqqNK9b5oCMHc0MluJAx0cwqASgbWMvkO01lCYgIhacVRLcaDz3QnyYIRNJRDwMb41VuT6pHJ91Q==} + fastify-plugin@4.5.1: resolution: {integrity: sha512-stRHYGeuqpEZTL1Ef0Ovr2ltazUT9g844X5z/zEBFLG8RYlpDiOCIG+ATvYEp+/zmc7sN29mcIMp8gvYplYPIQ==} @@ -3171,6 +3199,9 @@ packages: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} + hyphenate-style-name@1.0.4: + resolution: {integrity: sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==} + iconv-lite@0.4.24: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} @@ -3211,6 +3242,9 @@ packages: ini@1.3.8: resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + inline-style-prefixer@7.0.0: + resolution: {integrity: sha512-I7GEdScunP1dQ6IM2mQWh6v0mOYdYmH3Bp31UecKdrcUgcURTcctSe1IECdUznSHKSmsHtjrT3CwCPI1pyxfUQ==} + inquirer@8.2.6: resolution: {integrity: sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==} engines: {node: '>=12.0.0'} @@ -3569,6 +3603,9 @@ packages: jju@1.4.0: resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} + js-cookie@2.2.1: + resolution: {integrity: sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -3778,6 +3815,9 @@ packages: mathml-tag-names@2.1.3: resolution: {integrity: sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==} + mdn-data@2.0.14: + resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==} + mdn-data@2.0.30: resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} @@ -3883,6 +3923,12 @@ packages: mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + nano-css@5.6.1: + resolution: {integrity: sha512-T2Mhc//CepkTa3X4pUhKgbEheJHYAxD0VptuqFhDbGMUWVV2m+lkNiW/Ieuj35wrfC8Zm0l7HvssQh7zcEttSw==} + peerDependencies: + react: '*' + react-dom: '*' + nanoid@3.3.7: resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -4636,6 +4682,18 @@ packages: resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} engines: {node: '>=0.10.0'} + react-universal-interface@0.6.2: + resolution: {integrity: sha512-dg8yXdcQmvgR13RIlZbTRQOoUrDciFVoSBZILwjE2LFISxZZ8loVJKAkuzswl5js8BHda79bIb2b84ehU8IjXw==} + peerDependencies: + react: '*' + tslib: '*' + + react-use@17.5.0: + resolution: {integrity: sha512-PbfwSPMwp/hoL847rLnm/qkjg3sTRCvn6YhUZiHaUa3FA6/aNoFX79ul5Xt70O1rK+9GxSVqkY0eTwMdsR/bWg==} + peerDependencies: + react: '*' + react-dom: '*' + react@18.3.1: resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} engines: {node: '>=0.10.0'} @@ -4786,6 +4844,9 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + rtl-css-js@1.16.1: + resolution: {integrity: sha512-lRQgou1mu19e+Ya0LsTvKrVJ5TYUbqCVPAiImX3UfLTenarvPUl1QFdvu5Z3PYmHT9RCcwIfbjRQBntExyj3Zg==} + run-async@2.4.1: resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} engines: {node: '>=0.12.0'} @@ -4828,6 +4889,10 @@ packages: resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==} engines: {node: '>= 10.13.0'} + screenfull@5.2.0: + resolution: {integrity: sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==} + engines: {node: '>=0.10.0'} + scroll-into-view-if-needed@3.1.0: resolution: {integrity: sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==} @@ -4870,6 +4935,10 @@ packages: resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} engines: {node: '>= 0.4'} + set-harmonic-interval@1.0.1: + resolution: {integrity: sha512-AhICkFV84tBP1aWqPwLZqFvAwqEoVA9kxNMniGEUvzOlm4vLmOFLiTT3UZ6bziJTy4bOVpzWGTfSCbmaayGx8g==} + engines: {node: '>=6.9'} + shebang-command@1.2.0: resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} engines: {node: '>=0.10.0'} @@ -4934,6 +5003,10 @@ packages: source-map-support@0.5.21: resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + source-map@0.5.6: + resolution: {integrity: sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==} + engines: {node: '>=0.10.0'} + source-map@0.6.1: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} @@ -4953,10 +5026,22 @@ packages: resolution: {integrity: sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==} engines: {node: '>= 0.6'} + stack-generator@2.0.10: + resolution: {integrity: sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ==} + stack-utils@2.0.6: resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} engines: {node: '>=10'} + stackframe@1.3.4: + resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==} + + stacktrace-gps@3.1.2: + resolution: {integrity: sha512-GcUgbO4Jsqqg6RxfyTHFiPxdPqF+3LFmQhm7MgCuYQOYuWyqxo5pwRPz5d/u6/WYJdEnWfK4r+jGbyD8TSggXQ==} + + stacktrace-js@2.0.2: + resolution: {integrity: sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg==} + streamsearch@1.1.0: resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} engines: {node: '>=10.0.0'} @@ -5208,6 +5293,10 @@ packages: thread-stream@2.7.0: resolution: {integrity: sha512-qQiRWsU/wvNolI6tbbCKd9iKaTnCXsTwVxhhKM6nctPdujTyztjlbUkUTUymidWcMnZ5pWR0ej4a0tjsW021vw==} + throttle-debounce@3.0.1: + resolution: {integrity: sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg==} + engines: {node: '>=10'} + throttle-debounce@5.0.0: resolution: {integrity: sha512-2iQTSgkkc1Zyk0MeVrt/3BvuOXYPl/R8Z0U2xxo9rjwNciaHDG3R+Lm6dh4EeUci49DanvBnuqI6jshoQQRGEg==} engines: {node: '>=12.22'} @@ -5262,6 +5351,9 @@ packages: peerDependencies: typescript: '>=4.2.0' + ts-easing@0.2.0: + resolution: {integrity: sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ==} + ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} @@ -6960,6 +7052,8 @@ snapshots: expect: 29.7.0 pretty-format: 29.7.0 + '@types/js-cookie@2.2.7': {} + '@types/json-schema@7.0.15': {} '@types/json5@0.0.29': {} @@ -7191,6 +7285,8 @@ snapshots: '@webassemblyjs/ast': 1.12.1 '@xtuc/long': 4.2.2 + '@xobotyi/scrollbar-width@1.9.5': {} + '@xtuc/ieee754@1.2.0': {} '@xtuc/long@4.2.2': {} @@ -7860,6 +7956,15 @@ snapshots: css-functions-list@3.2.2: {} + css-in-js-utils@3.1.0: + dependencies: + hyphenate-style-name: 1.0.4 + + css-tree@1.1.3: + dependencies: + mdn-data: 2.0.14 + source-map: 0.6.1 + css-tree@2.3.1: dependencies: mdn-data: 2.0.30 @@ -7989,6 +8094,10 @@ snapshots: dependencies: is-arrayish: 0.2.1 + error-stack-parser@2.1.4: + dependencies: + stackframe: 1.3.4 + es-abstract@1.23.3: dependencies: array-buffer-byte-length: 1.0.1 @@ -8453,6 +8562,8 @@ snapshots: fast-levenshtein@2.0.6: {} + fast-loops@1.1.3: {} + fast-querystring@1.1.2: dependencies: fast-decode-uri-component: 1.0.1 @@ -8461,10 +8572,14 @@ snapshots: fast-safe-stringify@2.1.1: {} + fast-shallow-equal@1.0.0: {} + fast-uri@2.3.0: {} fastest-levenshtein@1.0.16: {} + fastest-stable-stringify@2.0.2: {} + fastify-plugin@4.5.1: {} fastify@4.26.2: @@ -8844,6 +8959,8 @@ snapshots: human-signals@2.1.0: {} + hyphenate-style-name@1.0.4: {} + iconv-lite@0.4.24: dependencies: safer-buffer: 2.1.2 @@ -8877,6 +8994,11 @@ snapshots: ini@1.3.8: {} + inline-style-prefixer@7.0.0: + dependencies: + css-in-js-utils: 3.1.0 + fast-loops: 1.1.3 + inquirer@8.2.6: dependencies: ansi-escapes: 4.3.2 @@ -9431,6 +9553,8 @@ snapshots: jju@1.4.0: {} + js-cookie@2.2.1: {} + js-tokens@4.0.0: {} js-yaml@3.14.1: @@ -9613,6 +9737,8 @@ snapshots: mathml-tag-names@2.1.3: {} + mdn-data@2.0.14: {} + mdn-data@2.0.30: {} memfs@3.5.3: @@ -9690,6 +9816,19 @@ snapshots: object-assign: 4.1.1 thenify-all: 1.6.0 + nano-css@5.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + css-tree: 1.1.3 + csstype: 3.1.3 + fastest-stable-stringify: 2.0.2 + inline-style-prefixer: 7.0.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + rtl-css-js: 1.16.1 + stacktrace-js: 2.0.2 + stylis: 4.3.2 + nanoid@3.3.7: {} natural-compare@1.4.0: {} @@ -10499,6 +10638,30 @@ snapshots: react-refresh@0.14.2: {} + react-universal-interface@0.6.2(react@18.3.1)(tslib@2.6.2): + dependencies: + react: 18.3.1 + tslib: 2.6.2 + + react-use@17.5.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@types/js-cookie': 2.2.7 + '@xobotyi/scrollbar-width': 1.9.5 + copy-to-clipboard: 3.3.3 + fast-deep-equal: 3.1.3 + fast-shallow-equal: 1.0.0 + js-cookie: 2.2.1 + nano-css: 5.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-universal-interface: 0.6.2(react@18.3.1)(tslib@2.6.2) + resize-observer-polyfill: 1.5.1 + screenfull: 5.2.0 + set-harmonic-interval: 1.0.1 + throttle-debounce: 3.0.1 + ts-easing: 0.2.0 + tslib: 2.6.2 + react@18.3.1: dependencies: loose-envify: 1.4.0 @@ -10665,6 +10828,10 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.17.2 fsevents: 2.3.3 + rtl-css-js@1.16.1: + dependencies: + '@babel/runtime': 7.24.5 + run-async@2.4.1: {} run-async@3.0.0: {} @@ -10710,6 +10877,8 @@ snapshots: ajv: 6.12.6 ajv-keywords: 3.5.2(ajv@6.12.6) + screenfull@5.2.0: {} + scroll-into-view-if-needed@3.1.0: dependencies: compute-scroll-into-view: 3.1.0 @@ -10752,6 +10921,8 @@ snapshots: functions-have-names: 1.2.3 has-property-descriptors: 1.0.2 + set-harmonic-interval@1.0.1: {} + shebang-command@1.2.0: dependencies: shebang-regex: 1.0.0 @@ -10815,6 +10986,8 @@ snapshots: buffer-from: 1.1.2 source-map: 0.6.1 + source-map@0.5.6: {} + source-map@0.6.1: {} source-map@0.7.4: {} @@ -10825,10 +10998,27 @@ snapshots: sqlstring@2.3.3: {} + stack-generator@2.0.10: + dependencies: + stackframe: 1.3.4 + stack-utils@2.0.6: dependencies: escape-string-regexp: 2.0.0 + stackframe@1.3.4: {} + + stacktrace-gps@3.1.2: + dependencies: + source-map: 0.5.6 + stackframe: 1.3.4 + + stacktrace-js@2.0.2: + dependencies: + error-stack-parser: 2.1.4 + stack-generator: 2.0.10 + stacktrace-gps: 3.1.2 + streamsearch@1.1.0: {} string-argv@0.3.2: {} @@ -11157,6 +11347,8 @@ snapshots: dependencies: real-require: 0.2.0 + throttle-debounce@3.0.1: {} + throttle-debounce@5.0.0: {} through@2.3.8: {} @@ -11196,6 +11388,8 @@ snapshots: dependencies: typescript: 5.4.5 + ts-easing@0.2.0: {} + ts-interface-checker@0.1.13: {} ts-jest@29.1.2(@babel/core@7.24.5)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.5))(jest@29.7.0(@types/node@20.12.11)(ts-node@10.9.2(@swc/core@1.5.5(@swc/helpers@0.5.11))(@types/node@20.12.11)(typescript@5.4.5)))(typescript@5.4.5):