diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 00000000..d80acbcf --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,18 @@ +{ + "permissions": { + "allow": [ + "Bash(nvm ls:*)", + "Bash(fnm list:*)", + "Bash(volta list:*)", + "Bash(pnpm tsc:*)", + "Bash(npx tsc:*)", + "Bash(npx eslint:*)", + "Bash(grep -r title: /Users/eunji/Documents/Projects/frontend/stories/*.tsx /Users/eunji/Documents/Projects/frontend/stories/*.ts)", + "Bash(node -e \"const sb = require\\(''''./node_modules/.pnpm/storybook@10.3.3_@babel+core@7.27.1_jiti@2.4.2/node_modules/storybook/package.json''''\\); console.log\\(sb.version\\)\")", + "Bash(pnpm list:*)", + "Bash(grep -r autodocs node_modules/.pnpm/storybook@10.3.3_*/node_modules/storybook/dist/*.js)", + "WebSearch", + "WebFetch(domain:storybook.js.org)" + ] + } +} diff --git a/.gitignore b/.gitignore index a547bf36..64c2d9c0 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,5 @@ dist-ssr *.njsproj *.sln *.sw? + +.pnpm-store/* \ No newline at end of file diff --git a/.storybook/main.ts b/.storybook/main.ts new file mode 100644 index 00000000..537886fc --- /dev/null +++ b/.storybook/main.ts @@ -0,0 +1,40 @@ +import { dirname, resolve } from "path"; +import { fileURLToPath } from "url"; + +import type { StorybookConfig } from "@storybook/react-vite"; + +const storybookDir = dirname(fileURLToPath(import.meta.url)); +const repoRoot = resolve(storybookDir, ".."); + +function getAbsolutePath(value: string) { + return dirname( + fileURLToPath(import.meta.resolve(`${value}/package.json`)) + ); +} + +const config: StorybookConfig = { + stories: [ + "../stories/**/*.mdx", + "../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)", + ], + addons: [ + getAbsolutePath("@chromatic-com/storybook"), + getAbsolutePath("@storybook/addon-vitest"), + getAbsolutePath("@storybook/addon-a11y"), + getAbsolutePath("@storybook/addon-docs"), + getAbsolutePath("@storybook/addon-onboarding"), + ], + framework: getAbsolutePath("@storybook/react-vite"), + viteFinal: async (config) => { + config.resolve ??= {}; + config.resolve.alias = { + ...config.resolve.alias, + "@frontend/common": resolve(repoRoot, "packages/common/src/index.ts"), + "@frontend/shop": resolve(repoRoot, "packages/shop/src/index.ts"), + "@frontend/theme": resolve(repoRoot, "packages/theme/src/index.ts"), + }; + return config; + }, +}; + +export default config; diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx new file mode 100644 index 00000000..39786a42 --- /dev/null +++ b/.storybook/preview.tsx @@ -0,0 +1,59 @@ +import { Global } from "@emotion/react"; +import { CssBaseline, ThemeProvider } from "@mui/material"; +import { createTheme } from "@mui/material/styles"; +import type { Preview } from "@storybook/react-vite"; +import * as React from "react"; + +import { theme2025, theme2026 } from "@frontend/theme"; +import type { PyConKRThemeConfig } from "@frontend/theme"; + +const themes: Record = { + "PyCon KR 2025": theme2025, + "PyCon KR 2026": theme2026, +}; + +const preview: Preview = { + tags: ["autodocs"], + globalTypes: { + theme: { + description: "PyConKR Theme", + toolbar: { + title: "Theme", + icon: "paintbrush", + items: Object.keys(themes), + dynamicTitle: true, + }, + }, + }, + initialGlobals: { + theme: "PyCon KR 2025", + }, + parameters: { + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/i, + }, + }, + }, + decorators: [ + (Story, context) => { + const themeName = context.globals.theme || "PyCon KR 2025"; + const themeConfig = themes[themeName] ?? theme2025; + const muiTheme = React.useMemo( + () => createTheme(themeConfig.muiTheme), + [themeConfig] + ); + + return ( + + + + + + ); + }, + ], +}; + +export default preview; diff --git a/apps/pyconkr-2026/index.html b/apps/pyconkr-2026/index.html new file mode 100644 index 00000000..5eb42276 --- /dev/null +++ b/apps/pyconkr-2026/index.html @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + PyCon Korea 2026 + + +
+ + + diff --git a/apps/pyconkr-2026/package.json b/apps/pyconkr-2026/package.json new file mode 100644 index 00000000..67eaad31 --- /dev/null +++ b/apps/pyconkr-2026/package.json @@ -0,0 +1,14 @@ +{ + "name": "@apps/pyconkr-2026", + "dependencies": { + "@frontend/common": "workspace:*", + "@frontend/shop": "workspace:*", + "@frontend/theme": "workspace:*" + }, + "devDependencies": { + "vite": "^6.3.5", + "vite-plugin-mdx": "^3.6.1", + "vite-plugin-mkcert": "^1.17.8", + "vite-plugin-svgr": "^4.3.0" + } +} diff --git a/apps/pyconkr-2026/src/App.tsx b/apps/pyconkr-2026/src/App.tsx new file mode 100644 index 00000000..2db88403 --- /dev/null +++ b/apps/pyconkr-2026/src/App.tsx @@ -0,0 +1,50 @@ +import * as Common from "@frontend/common"; +import * as React from "react"; +import { Route, Routes, useLocation } from "react-router-dom"; +import * as R from "remeda"; + +import BackendAPISchemas from "../../../packages/common/src/schemas/backendAPI"; +import { PageIdParamRenderer, RouteRenderer } from "./components/pages/dynamic_route.tsx"; +import { IS_DEBUG_ENV } from "./consts"; +import { useAppContext } from "./contexts/app_context"; + +export const App: React.FC = () => { + const backendAPIClient = Common.Hooks.BackendAPI.useBackendClient(); + const { data: sponsorTiers } = Common.Hooks.BackendAPI.useSponsorQuery(backendAPIClient); + const { data: flatSiteMap } = Common.Hooks.BackendAPI.useFlattenSiteMapQuery(backendAPIClient); + const siteMapNode = Common.Utils.buildNestedSiteMap(flatSiteMap)?.[""]; + + const location = useLocation(); + const { setAppContext, language } = useAppContext(); + + React.useEffect(() => { + (async () => { + const currentRouteCodes = ["", ...location.pathname.split("/").filter((code) => !R.isEmpty(code))]; + const currentSiteMapDepth: (BackendAPISchemas.NestedSiteMapSchema | undefined)[] = [siteMapNode]; + + for (const routeCode of currentRouteCodes.splice(1)) { + const childrenMap = currentSiteMapDepth + .at(-1) + ?.children?.reduce((acc, child) => ({ ...acc, [child.route_code]: child }), {} as Record); + currentSiteMapDepth.push(childrenMap?.[routeCode]); + if (R.isNullish(currentSiteMapDepth.at(-1))) { + console.warn(`Route not found in site map: ${routeCode}`); + break; + } + } + + setAppContext((ps) => ({ ...ps, siteMapNode, sponsorTiers, currentSiteMapDepth })); + })(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [location, language, flatSiteMap, sponsorTiers]); + + return ( + + + {IS_DEBUG_ENV && Debug Page} />} + } /> + } /> + + + ); +}; diff --git a/apps/pyconkr-2026/src/components/pages/dynamic_route.tsx b/apps/pyconkr-2026/src/components/pages/dynamic_route.tsx new file mode 100644 index 00000000..bc39578d --- /dev/null +++ b/apps/pyconkr-2026/src/components/pages/dynamic_route.tsx @@ -0,0 +1,150 @@ +import * as Common from "@frontend/common"; +import { CircularProgress, Stack, Theme } from "@mui/material"; +import { ErrorBoundary, Suspense } from "@suspensive/react"; +import { useQueryClient } from "@tanstack/react-query"; +import { AxiosError, AxiosResponse } from "axios"; +import * as React from "react"; +import { useParams } from "react-router-dom"; +import * as R from "remeda"; + +import { useAppContext } from "../../contexts/app_context"; + +const initialPageStyle: (additionalStyle: React.CSSProperties) => (theme: Theme) => React.CSSProperties = (additionalStyle) => (theme) => ({ + width: "100%", + display: "flex", + justifyContent: "flex-start", + alignItems: "center", + flexDirection: "column", + + marginTop: theme.spacing(4), + + ...(!R.isEmpty(additionalStyle) + ? additionalStyle + : { + [theme.breakpoints.down("lg")]: { + marginTop: theme.spacing(2), + }, + [theme.breakpoints.down("sm")]: { + marginTop: theme.spacing(1), + }, + }), +}); + +const initialSectionStyle: (additionalStyle: React.CSSProperties) => (theme: Theme) => React.CSSProperties = (additionalStyle) => (theme) => ({ + width: "100%", + maxWidth: "1200px", + display: "flex", + justifyContent: "flex-start", + alignItems: "center", + paddingRight: theme.spacing(16), + paddingLeft: theme.spacing(16), + + "& .markdown-body": { width: "100%" }, + ...(!R.isEmpty(additionalStyle) + ? additionalStyle + : { + [theme.breakpoints.down("lg")]: { + paddingRight: theme.spacing(4), + paddingLeft: theme.spacing(4), + }, + [theme.breakpoints.down("sm")]: { + paddingRight: theme.spacing(2), + paddingLeft: theme.spacing(2), + }, + }), +}); + +const LoginRequired: React.FC = () => <>401 Login Required; +const PermissionDenied: React.FC = () => <>403 Permission Denied; +const PageNotFound: React.FC = () => <>404 Not Found; +const CenteredLoadingPage: React.FC = () => ( + + + +); + +const throwPageNotFound: (message: string) => never = (message) => { + const errorStr = `RouteRenderer: ${message}`; + const axiosError = new AxiosError(errorStr, errorStr, undefined, undefined, { + status: 404, + } as AxiosResponse); + throw new Common.BackendAPIs.BackendAPIClientError(axiosError); +}; + +const RouteErrorFallback: React.FC<{ error: Error; reset: () => void }> = ({ error, reset }) => { + if (error instanceof Common.BackendAPIs.BackendAPIClientError) { + switch (error.status) { + case 401: + return ; + case 403: + return ; + case 404: + return ; + default: + return ; + } + } + return ; +}; + +const WaitedCenteredLoadingPage: React.FC = Suspense.with({ fallback: }, () => { + const [isFetching, setIsFetching] = React.useState(true); + const qClient = useQueryClient(); + + React.useEffect(() => { + const unsubscribe = qClient.getQueryCache().subscribe(() => setIsFetching(qClient.isFetching() > 0)); + return () => unsubscribe(); + }, [qClient]); + + return isFetching ? : ; +}); + +const InnerPageRenderer: React.FC<{ id: string }> = Suspense.with({ fallback: }, ({ id }) => { + const { setAppContext } = useAppContext(); + const backendClient = Common.Hooks.BackendAPI.useBackendClient(); + const { data } = Common.Hooks.BackendAPI.usePageQuery(backendClient, id); + + React.useEffect(() => { + setAppContext((prev) => ({ + ...prev, + title: data.title, + shouldShowTitleBanner: data.show_top_title_banner, + shouldShowSponsorBanner: data.show_bottom_sponsor_banner, + })); + }, [data, setAppContext]); + + return ( + + {data.sections.map((s) => ( + + + + ))} + + ); +}); + +export const PageRenderer: React.FC<{ id: string }> = ({ id }) => ( + + + +); + +export const RouteRenderer: React.FC = ErrorBoundary.with( + { fallback: RouteErrorFallback }, + Suspense.with({ fallback: }, () => { + const { siteMapNode, currentSiteMapDepth } = useAppContext(); + const routeInfo = !R.isEmpty(currentSiteMapDepth) && currentSiteMapDepth[currentSiteMapDepth.length - 1]; + + if (!(siteMapNode && routeInfo)) return ; + if (R.isString(routeInfo.page)) return ; + if (R.isString(routeInfo.external_link)) window.location.replace(routeInfo.external_link); + return ; + }) +); + +export const PageIdParamRenderer: React.FC = Suspense.with({ fallback: }, () => { + const { id } = useParams(); + if (!id) throwPageNotFound("Page ID is required"); + return ; +}); diff --git a/apps/pyconkr-2026/src/consts/index.ts b/apps/pyconkr-2026/src/consts/index.ts new file mode 100644 index 00000000..3b678c6c --- /dev/null +++ b/apps/pyconkr-2026/src/consts/index.ts @@ -0,0 +1 @@ +export const IS_DEBUG_ENV = import.meta.env.MODE === "development"; diff --git a/apps/pyconkr-2026/src/consts/local_storage.ts b/apps/pyconkr-2026/src/consts/local_storage.ts new file mode 100644 index 00000000..057a4b66 --- /dev/null +++ b/apps/pyconkr-2026/src/consts/local_storage.ts @@ -0,0 +1 @@ +export const LOCAL_STORAGE_LANGUAGE_KEY = "pyconkr-2026-language"; diff --git a/apps/pyconkr-2026/src/consts/mdx_components.ts b/apps/pyconkr-2026/src/consts/mdx_components.ts new file mode 100644 index 00000000..49fabccd --- /dev/null +++ b/apps/pyconkr-2026/src/consts/mdx_components.ts @@ -0,0 +1,22 @@ +import * as Common from "@frontend/common"; +import * as Shop from "@frontend/shop"; +import type { MDXComponents } from "mdx/types.js"; + +const ShopMDXComponents: MDXComponents = { + Shop__Common__PriceDisplay: Shop.Components.Common.PriceDisplay, + Shop__Common__SignInGuard: Shop.Components.Common.SignInGuard, + Shop__Common__ContextProvider: Shop.Components.Common.ShopContextProvider, + Shop__Common__UserSignInMethod: Shop.Components.Common.UserSignInMethod, + Shop__Common__UserSignInAccount: Shop.Components.Common.UserSignInAccount, + Shop__Feature__CartStatus: Shop.Components.Features.CartStatus, + Shop__Feature__ProductList: Shop.Components.Features.ProductList, + Shop__Feature__ProductImageCardList: Shop.Components.Features.ProductImageCardList, + Shop__Feature__OrderList: Shop.Components.Features.OrderList, + Shop__Feature__UserInfo: Shop.Components.Features.UserInfo, + Shop__Feature__PatronList: Shop.Components.Features.PatronList, +}; + +export const PyConKRMDXComponents: MDXComponents = { + ...Common.BaseMDXComponents, + ...ShopMDXComponents, +}; diff --git a/apps/pyconkr-2026/src/contexts/app_context.tsx b/apps/pyconkr-2026/src/contexts/app_context.tsx new file mode 100644 index 00000000..875a772e --- /dev/null +++ b/apps/pyconkr-2026/src/contexts/app_context.tsx @@ -0,0 +1,28 @@ +import * as React from "react"; + +import BackendAPISchemas from "../../../../packages/common/src/schemas/backendAPI"; + +type LanguageType = "ko" | "en"; + +export type AppContextType = { + language: LanguageType; + shouldShowTitleBanner: boolean; + shouldShowSponsorBanner: boolean; + + siteMapNode?: BackendAPISchemas.NestedSiteMapSchema; + sponsorTiers?: BackendAPISchemas.SponsorTierSchema[]; + title: string; + currentSiteMapDepth: (BackendAPISchemas.NestedSiteMapSchema | undefined)[]; + + setAppContext: React.Dispatch>>; +}; + +export const AppContext = React.createContext(undefined); + +export const useAppContext = (): AppContextType => { + const context = React.useContext(AppContext); + if (!context) { + throw new Error("useAppContext must be used within an AppContextProvider"); + } + return context; +}; diff --git a/apps/pyconkr-2026/src/main.tsx b/apps/pyconkr-2026/src/main.tsx new file mode 100644 index 00000000..fd0018d3 --- /dev/null +++ b/apps/pyconkr-2026/src/main.tsx @@ -0,0 +1,116 @@ +import { Global } from "@emotion/react"; +import * as Common from "@frontend/common"; +import * as Shop from "@frontend/shop"; +import { theme2026 } from "@frontend/theme"; +import { CircularProgress, CssBaseline, ThemeProvider } from "@mui/material"; +import { createTheme } from "@mui/material/styles"; +import { ErrorBoundary, Suspense } from "@suspensive/react"; +import { matchQuery, MutationCache, QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; +import { SnackbarProvider } from "notistack"; +import * as React from "react"; +import * as ReactDom from "react-dom/client"; +import { BrowserRouter } from "react-router-dom"; + +import { App } from "./App.tsx"; +import { IS_DEBUG_ENV } from "./consts"; +import { LOCAL_STORAGE_LANGUAGE_KEY } from "./consts/local_storage.ts"; +import { PyConKRMDXComponents } from "./consts/mdx_components.ts"; +import { AppContext, AppContextType } from "./contexts/app_context.tsx"; + +const muiTheme = createTheme(theme2026.muiTheme); + +const eventConfig: Common.Contexts.EventConfig = { + ...theme2026.event, + assets: { + ...theme2026.event.assets, + }, +}; + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + staleTime: 60 * 1000, + gcTime: 5 * 60 * 1000, + retry: 1, + }, + }, + mutationCache: new MutationCache({ + onMutate: (_variables, mutation) => { + queryClient.resetQueries({ + predicate: (query) => mutation.meta?.invalidates?.some((queryKey) => matchQuery({ queryKey }, query)) ?? true, + }); + queryClient.cancelQueries({ + predicate: (query) => mutation.meta?.invalidates?.some((queryKey) => matchQuery({ queryKey }, query)) ?? true, + }); + }, + onSuccess: (_data, _variables, _context, mutation) => { + queryClient.resetQueries({ + predicate: (query) => mutation.meta?.invalidates?.some((queryKey) => matchQuery({ queryKey }, query)) ?? true, + }); + }, + }), +}); + +const CommonOptions: Common.Contexts.ContextOptions = { + language: "ko", + debug: IS_DEBUG_ENV, + baseUrl: ".", + backendApiDomain: import.meta.env.VITE_PYCONKR_BACKEND_API_DOMAIN, + backendApiTimeout: 10000, + mdxComponents: PyConKRMDXComponents, + eventConfig, +}; + +const ShopOptions: Shop.Contexts.ContextOptions = { + language: "ko", + shopApiDomain: import.meta.env.VITE_PYCONKR_SHOP_API_DOMAIN, + shopApiCSRFCookieName: import.meta.env.VITE_PYCONKR_SHOP_CSRF_COOKIE_NAME, + shopApiTimeout: 10000, + shopImpAccountId: import.meta.env.VITE_PYCONKR_SHOP_IMP_ACCOUNT_ID, +}; + +const SuspenseFallback = ( + + + +); + +const MainApp: React.FC = () => { + const [appState, setAppContext] = React.useState>({ + language: (localStorage.getItem(LOCAL_STORAGE_LANGUAGE_KEY) as "ko" | "en" | null) ?? "ko", + shouldShowTitleBanner: true, + shouldShowSponsorBanner: false, + currentSiteMapDepth: [], + title: eventConfig.eventName, + }); + + return ( + + + + + + + + + + + + + + + + + + + + + + + + + ); +}; + +ReactDom.createRoot(document.getElementById("root")!).render(); diff --git a/apps/pyconkr-2026/tsconfig.json b/apps/pyconkr-2026/tsconfig.json new file mode 100644 index 00000000..929b2c4a --- /dev/null +++ b/apps/pyconkr-2026/tsconfig.json @@ -0,0 +1,29 @@ +{ + "compilerOptions": { + "target": "ES2022", + "useDefineForClassFields": true, + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true, + "forceConsistentCasingInFileNames": false, + + "baseUrl": ".", + "paths": { + "@apps/pyconkr-2026/*": ["apps/pyconkr-2026/src/*"] + } + }, + "include": ["src", "vite.config.mts", "vite-env.d.ts", "../../types"] +} diff --git a/apps/pyconkr-2026/vite-env.d.ts b/apps/pyconkr-2026/vite-env.d.ts new file mode 100644 index 00000000..11f02fe2 --- /dev/null +++ b/apps/pyconkr-2026/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/apps/pyconkr-2026/vite.config.mts b/apps/pyconkr-2026/vite.config.mts new file mode 100644 index 00000000..132a3db3 --- /dev/null +++ b/apps/pyconkr-2026/vite.config.mts @@ -0,0 +1,21 @@ +import path from "path"; + +import mdx from "@mdx-js/rollup"; +import react from "@vitejs/plugin-react"; +import { defineConfig } from "vite"; +import mkcert from "vite-plugin-mkcert"; +import svgr from "vite-plugin-svgr"; + +export default defineConfig({ + base: "/", + envDir: "../../dotenv", + plugins: [react(), mdx(), mkcert({ hosts: ["local.dev.pycon.kr"] }), svgr()], + resolve: { + alias: { + "@frontend/common": path.resolve(__dirname, "../../packages/common/src/index.ts"), + "@frontend/shop": path.resolve(__dirname, "../../packages/shop/src/index.ts"), + "@frontend/theme": path.resolve(__dirname, "../../packages/theme/src/index.ts"), + "@apps/pyconkr-2026": path.resolve(__dirname, "./src"), + }, + }, +}); diff --git a/apps/pyconkr-admin/package.json b/apps/pyconkr-admin/package.json index e4628e11..1a51f938 100644 --- a/apps/pyconkr-admin/package.json +++ b/apps/pyconkr-admin/package.json @@ -2,7 +2,8 @@ "name": "@apps/pyconkr-admin", "dependencies": { "@frontend/common": "workspace:*", - "@frontend/shop": "workspace:*" + "@frontend/shop": "workspace:*", + "@frontend/theme": "workspace:*" }, "devDependencies": { "vite": "^6.3.5", diff --git a/apps/pyconkr-admin/src/components/pages/page/editor.tsx b/apps/pyconkr-admin/src/components/pages/page/editor.tsx index 82586518..d8e39d27 100644 --- a/apps/pyconkr-admin/src/components/pages/page/editor.tsx +++ b/apps/pyconkr-admin/src/components/pages/page/editor.tsx @@ -9,8 +9,12 @@ import { useParams } from "react-router-dom"; // I don't know why, I don't want to know why, I shouldn't have to wonder why, // but for whatever reason this stupid namespace won't import on Common.Schemas.BackendAdminAPI.PageSectionSchema. // TODO: FIXME: Remove this hack when the issue is resolved. This is dumb. +import { theme2025 } from "@frontend/theme"; +import { createTheme } from "@mui/material/styles"; + import BackendAdminAPISchemas from "../../../../../../packages/common/src/schemas/backendAdminAPI"; -import { muiTheme } from "../../../styles/globalStyles"; + +const muiTheme = createTheme(theme2025.muiTheme); import { addErrorSnackbar } from "../../../utils/snackbar"; import { AdminEditor } from "../../layouts/admin_editor"; diff --git a/apps/pyconkr-admin/src/consts/mdx_components.ts b/apps/pyconkr-admin/src/consts/mdx_components.ts index ef656268..49fabccd 100644 --- a/apps/pyconkr-admin/src/consts/mdx_components.ts +++ b/apps/pyconkr-admin/src/consts/mdx_components.ts @@ -1,149 +1,8 @@ -// 후대의 개발자님께 : 컴포넌트 맨 첫글자가 대문자로 시작하지 않으면 JSX 컴포넌트가 아니라 일반 HTML 태그로 인식합니다. 제발 대문자로 시작해주세요. import * as Common from "@frontend/common"; import * as Shop from "@frontend/shop"; -import * as mui from "@mui/material"; import type { MDXComponents } from "mdx/types.js"; -const MUIMDXComponents: MDXComponents = { - Mui__material__Accordion: mui.Accordion, - Mui__material__AccordionActions: mui.AccordionActions, - Mui__material__AccordionDetails: mui.AccordionDetails, - Mui__material__AccordionSummary: mui.AccordionSummary, - Mui__material__Alert: mui.Alert, - Mui__material__AlertTitle: mui.AlertTitle, - Mui__material__AppBar: mui.AppBar, - Mui__material__Autocomplete: mui.Autocomplete, - Mui__material__Avatar: mui.Avatar, - Mui__material__AvatarGroup: mui.AvatarGroup, - Mui__material__Backdrop: mui.Backdrop, - Mui__material__Badge: mui.Badge, - Mui__material__BottomNavigation: mui.BottomNavigation, - Mui__material__BottomNavigationAction: mui.BottomNavigationAction, - Mui__material__Box: mui.Box, - Mui__material__Breadcrumbs: mui.Breadcrumbs, - Mui__material__Button: mui.Button, - Mui__material__ButtonBase: mui.ButtonBase, - Mui__material__ButtonGroup: mui.ButtonGroup, - Mui__material__Card: mui.Card, - Mui__material__CardActionArea: mui.CardActionArea, - Mui__material__CardActions: mui.CardActions, - Mui__material__CardContent: mui.CardContent, - Mui__material__CardHeader: mui.CardHeader, - Mui__material__CardMedia: mui.CardMedia, - Mui__material__Checkbox: mui.Checkbox, - Mui__material__Chip: mui.Chip, - Mui__material__CircularProgress: mui.CircularProgress, - Mui__material__Collapse: mui.Collapse, - Mui__material__Container: mui.Container, - Mui__material__Dialog: mui.Dialog, - Mui__material__DialogActions: mui.DialogActions, - Mui__material__DialogContent: mui.DialogContent, - Mui__material__DialogContentText: mui.DialogContentText, - Mui__material__DialogTitle: mui.DialogTitle, - Mui__material__Divider: mui.Divider, - Mui__material__Drawer: mui.Drawer, - Mui__material__Fab: mui.Fab, - Mui__material__Fade: mui.Fade, - Mui__material__FilledInput: mui.FilledInput, - Mui__material__FormControl: mui.FormControl, - Mui__material__FormControlLabel: mui.FormControlLabel, - Mui__material__FormGroup: mui.FormGroup, - Mui__material__FormHelperText: mui.FormHelperText, - Mui__material__FormLabel: mui.FormLabel, - Mui__material__Grid: mui.Grid, - Mui__material__Grow: mui.Grow, - Mui__material__Icon: mui.Icon, - Mui__material__IconButton: mui.IconButton, - Mui__material__ImageList: mui.ImageList, - Mui__material__ImageListItem: mui.ImageListItem, - Mui__material__ImageListItemBar: mui.ImageListItemBar, - Mui__material__Input: mui.Input, - Mui__material__InputAdornment: mui.InputAdornment, - Mui__material__InputBase: mui.InputBase, - Mui__material__InputLabel: mui.InputLabel, - Mui__material__LinearProgress: mui.LinearProgress, - Mui__material__Link: mui.Link, - Mui__material__List: mui.List, - Mui__material__ListItem: mui.ListItem, - Mui__material__ListItemAvatar: mui.ListItemAvatar, - Mui__material__ListItemButton: mui.ListItemButton, - Mui__material__ListItemIcon: mui.ListItemIcon, - Mui__material__ListItemSecondaryAction: mui.ListItemSecondaryAction, - Mui__material__ListItemText: mui.ListItemText, - Mui__material__ListSubheader: mui.ListSubheader, - Mui__material__Menu: mui.Menu, - Mui__material__MenuItem: mui.MenuItem, - Mui__material__MenuList: mui.MenuList, - Mui__material__MobileStepper: mui.MobileStepper, - Mui__material__Modal: mui.Modal, - Mui__material__NativeSelect: mui.NativeSelect, - Mui__material__NoSsr: mui.NoSsr, - Mui__material__OutlinedInput: mui.OutlinedInput, - Mui__material__Pagination: mui.Pagination, - Mui__material__PaginationItem: mui.PaginationItem, - Mui__material__Paper: mui.Paper, - Mui__material__Popover: mui.Popover, - Mui__material__Popper: mui.Popper, - Mui__material__Portal: mui.Portal, - Mui__material__Radio: mui.Radio, - Mui__material__RadioGroup: mui.RadioGroup, - Mui__material__Rating: mui.Rating, - Mui__material__Select: mui.Select, - Mui__material__Skeleton: mui.Skeleton, - Mui__material__Slide: mui.Slide, - Mui__material__Slider: mui.Slider, - Mui__material__Snackbar: mui.Snackbar, - Mui__material__SnackbarContent: mui.SnackbarContent, - Mui__material__SpeedDial: mui.SpeedDial, - Mui__material__SpeedDialAction: mui.SpeedDialAction, - Mui__material__SpeedDialIcon: mui.SpeedDialIcon, - Mui__material__Stack: mui.Stack, - Mui__material__Step: mui.Step, - Mui__material__StepButton: mui.StepButton, - Mui__material__StepConnector: mui.StepConnector, - Mui__material__StepContent: mui.StepContent, - Mui__material__StepIcon: mui.StepIcon, - Mui__material__StepLabel: mui.StepLabel, - Mui__material__Stepper: mui.Stepper, - Mui__material__SvgIcon: mui.SvgIcon, - Mui__material__SwipeableDrawer: mui.SwipeableDrawer, - Mui__material__Switch: mui.Switch, - Mui__material__Tab: mui.Tab, - Mui__material__Table: mui.Table, - Mui__material__TableBody: mui.TableBody, - Mui__material__TableCell: mui.TableCell, - Mui__material__TableContainer: mui.TableContainer, - Mui__material__TableFooter: mui.TableFooter, - Mui__material__TableHead: mui.TableHead, - Mui__material__TablePagination: mui.TablePagination, - Mui__material__TableRow: mui.TableRow, - Mui__material__TableSortLabel: mui.TableSortLabel, - Mui__material__Tabs: mui.Tabs, - Mui__material__TabScrollButton: mui.TabScrollButton, - Mui__material__TextField: mui.TextField, - Mui__material__TextareaAutosize: mui.TextareaAutosize, - Mui__material__ToggleButton: mui.ToggleButton, - Mui__material__ToggleButtonGroup: mui.ToggleButtonGroup, - Mui__material__Toolbar: mui.Toolbar, - Mui__material__Tooltip: mui.Tooltip, - Mui__material__Typography: mui.Typography, - Mui__material__Zoom: mui.Zoom, -}; - -const PyConKRCommonMDXComponents: MDXComponents = { - Common__Components__Lottie: Common.Components.LottiePlayer, - Common__Components__NetworkLottie: Common.Components.NetworkLottiePlayer, - Common__Components__MDX__Confetti: Common.Components.MDX.Confetti, - Common__Components__MDX__PrimaryStyledDetails: Common.Components.MDX.PrimaryStyledDetails, - Common__Components__MDX__SecondaryStyledDetails: Common.Components.MDX.SecondaryStyledDetails, - Common__Components__MDX__Map: Common.Components.MDX.Map, - Common__Components__MDX__FAQAccordion: Common.Components.MDX.FAQAccordion, - Common__Components__MDX__FullWidthStyledButton: Common.Components.MDX.StyledFullWidthButton, - Common__Components__Session__List: Common.Components.MDX.SessionList, - Common__Components__Session__TimeTable: Common.Components.MDX.SessionTimeTable, -}; - -const PythonKRShopMDXComponents: MDXComponents = { +const ShopMDXComponents: MDXComponents = { Shop__Common__PriceDisplay: Shop.Components.Common.PriceDisplay, Shop__Common__SignInGuard: Shop.Components.Common.SignInGuard, Shop__Common__ContextProvider: Shop.Components.Common.ShopContextProvider, @@ -157,8 +16,7 @@ const PythonKRShopMDXComponents: MDXComponents = { Shop__Feature__PatronList: Shop.Components.Features.PatronList, }; -export const PyConKRMDXComponents = { - ...MUIMDXComponents, - ...PyConKRCommonMDXComponents, - ...PythonKRShopMDXComponents, +export const PyConKRMDXComponents: MDXComponents = { + ...Common.BaseMDXComponents, + ...ShopMDXComponents, }; diff --git a/apps/pyconkr-admin/vite.config.ts b/apps/pyconkr-admin/vite.config.ts index 1226307e..5bf33074 100644 --- a/apps/pyconkr-admin/vite.config.ts +++ b/apps/pyconkr-admin/vite.config.ts @@ -15,6 +15,7 @@ export default defineConfig({ alias: { "@frontend/common": path.resolve(__dirname, "../../packages/common/src/index.ts"), "@frontend/shop": path.resolve(__dirname, "../../packages/shop/src/index.ts"), + "@frontend/theme": path.resolve(__dirname, "../../packages/theme/src/index.ts"), "@apps/pyconkr-admin": path.resolve(__dirname, "./src"), }, }, diff --git a/apps/pyconkr-participant-portal/vite.config.mts b/apps/pyconkr-participant-portal/vite.config.mts index d8314af2..fac50e8d 100644 --- a/apps/pyconkr-participant-portal/vite.config.mts +++ b/apps/pyconkr-participant-portal/vite.config.mts @@ -15,6 +15,7 @@ export default defineConfig({ alias: { "@frontend/common": path.resolve(__dirname, "../../packages/common/src/index.ts"), "@frontend/shop": path.resolve(__dirname, "../../packages/shop/src/index.ts"), + "@frontend/theme": path.resolve(__dirname, "../../packages/theme/src/index.ts"), "@apps/pyconkr-participant-portal": path.resolve(__dirname, "./src"), }, }, diff --git a/apps/pyconkr/package.json b/apps/pyconkr/package.json index 88bd428c..d8891e92 100644 --- a/apps/pyconkr/package.json +++ b/apps/pyconkr/package.json @@ -2,7 +2,8 @@ "name": "@apps/pyconkr", "dependencies": { "@frontend/common": "workspace:*", - "@frontend/shop": "workspace:*" + "@frontend/shop": "workspace:*", + "@frontend/theme": "workspace:*" }, "devDependencies": { "vite": "^6.3.5", diff --git a/packages/common/src/assets/pyconkr2025_hostlogo_big.png b/apps/pyconkr/src/assets/pyconkr2025_hostlogo_big.png similarity index 100% rename from packages/common/src/assets/pyconkr2025_hostlogo_big.png rename to apps/pyconkr/src/assets/pyconkr2025_hostlogo_big.png diff --git a/packages/common/src/assets/pyconkr2025_hostlogo_small.png b/apps/pyconkr/src/assets/pyconkr2025_hostlogo_small.png similarity index 100% rename from packages/common/src/assets/pyconkr2025_hostlogo_small.png rename to apps/pyconkr/src/assets/pyconkr2025_hostlogo_small.png diff --git a/packages/common/src/assets/pyconkr2025_main_cover_image.png b/apps/pyconkr/src/assets/pyconkr2025_main_cover_image.png similarity index 100% rename from packages/common/src/assets/pyconkr2025_main_cover_image.png rename to apps/pyconkr/src/assets/pyconkr2025_main_cover_image.png diff --git a/packages/common/src/assets/pyconkr2025_main_cover_title.png b/apps/pyconkr/src/assets/pyconkr2025_main_cover_title.png similarity index 100% rename from packages/common/src/assets/pyconkr2025_main_cover_title.png rename to apps/pyconkr/src/assets/pyconkr2025_main_cover_title.png diff --git a/apps/pyconkr/src/components/pages/presentation_detail.tsx b/apps/pyconkr/src/components/pages/presentation_detail.tsx index 8183a16f..61c0afa0 100644 --- a/apps/pyconkr/src/components/pages/presentation_detail.tsx +++ b/apps/pyconkr/src/components/pages/presentation_detail.tsx @@ -6,7 +6,6 @@ import * as React from "react"; import { Navigate, useParams } from "react-router-dom"; import * as R from "remeda"; -import PyCon2025Logo from "../../assets/pyconkr2025_logo.png"; import { useAppContext } from "../../contexts/app_context"; import { PageLayout } from "../layout/PageLayout"; @@ -92,11 +91,16 @@ const ProfileImageStyle: React.CSSProperties = { const ProfileImage = styled(Common.Components.FallbackImage)(ProfileImageStyle); -const ProfileImageErrorFallback: React.FC = () => ( - - PyCon 2025 Logo - -); +const ProfileImageErrorFallback: React.FC = () => { + const { eventConfig } = Common.Hooks.Common.useCommonContext(); + const logo = eventConfig?.assets.logo; + + return ( + + {logo && Event Logo} + + ); +}; const PresentationSpeakerItem: React.FC<{ speaker: SimplifiedSpeakerSchema }> = ({ speaker }) => { return ( diff --git a/apps/pyconkr/src/consts/mdx_components.ts b/apps/pyconkr/src/consts/mdx_components.ts index ef656268..49fabccd 100644 --- a/apps/pyconkr/src/consts/mdx_components.ts +++ b/apps/pyconkr/src/consts/mdx_components.ts @@ -1,149 +1,8 @@ -// 후대의 개발자님께 : 컴포넌트 맨 첫글자가 대문자로 시작하지 않으면 JSX 컴포넌트가 아니라 일반 HTML 태그로 인식합니다. 제발 대문자로 시작해주세요. import * as Common from "@frontend/common"; import * as Shop from "@frontend/shop"; -import * as mui from "@mui/material"; import type { MDXComponents } from "mdx/types.js"; -const MUIMDXComponents: MDXComponents = { - Mui__material__Accordion: mui.Accordion, - Mui__material__AccordionActions: mui.AccordionActions, - Mui__material__AccordionDetails: mui.AccordionDetails, - Mui__material__AccordionSummary: mui.AccordionSummary, - Mui__material__Alert: mui.Alert, - Mui__material__AlertTitle: mui.AlertTitle, - Mui__material__AppBar: mui.AppBar, - Mui__material__Autocomplete: mui.Autocomplete, - Mui__material__Avatar: mui.Avatar, - Mui__material__AvatarGroup: mui.AvatarGroup, - Mui__material__Backdrop: mui.Backdrop, - Mui__material__Badge: mui.Badge, - Mui__material__BottomNavigation: mui.BottomNavigation, - Mui__material__BottomNavigationAction: mui.BottomNavigationAction, - Mui__material__Box: mui.Box, - Mui__material__Breadcrumbs: mui.Breadcrumbs, - Mui__material__Button: mui.Button, - Mui__material__ButtonBase: mui.ButtonBase, - Mui__material__ButtonGroup: mui.ButtonGroup, - Mui__material__Card: mui.Card, - Mui__material__CardActionArea: mui.CardActionArea, - Mui__material__CardActions: mui.CardActions, - Mui__material__CardContent: mui.CardContent, - Mui__material__CardHeader: mui.CardHeader, - Mui__material__CardMedia: mui.CardMedia, - Mui__material__Checkbox: mui.Checkbox, - Mui__material__Chip: mui.Chip, - Mui__material__CircularProgress: mui.CircularProgress, - Mui__material__Collapse: mui.Collapse, - Mui__material__Container: mui.Container, - Mui__material__Dialog: mui.Dialog, - Mui__material__DialogActions: mui.DialogActions, - Mui__material__DialogContent: mui.DialogContent, - Mui__material__DialogContentText: mui.DialogContentText, - Mui__material__DialogTitle: mui.DialogTitle, - Mui__material__Divider: mui.Divider, - Mui__material__Drawer: mui.Drawer, - Mui__material__Fab: mui.Fab, - Mui__material__Fade: mui.Fade, - Mui__material__FilledInput: mui.FilledInput, - Mui__material__FormControl: mui.FormControl, - Mui__material__FormControlLabel: mui.FormControlLabel, - Mui__material__FormGroup: mui.FormGroup, - Mui__material__FormHelperText: mui.FormHelperText, - Mui__material__FormLabel: mui.FormLabel, - Mui__material__Grid: mui.Grid, - Mui__material__Grow: mui.Grow, - Mui__material__Icon: mui.Icon, - Mui__material__IconButton: mui.IconButton, - Mui__material__ImageList: mui.ImageList, - Mui__material__ImageListItem: mui.ImageListItem, - Mui__material__ImageListItemBar: mui.ImageListItemBar, - Mui__material__Input: mui.Input, - Mui__material__InputAdornment: mui.InputAdornment, - Mui__material__InputBase: mui.InputBase, - Mui__material__InputLabel: mui.InputLabel, - Mui__material__LinearProgress: mui.LinearProgress, - Mui__material__Link: mui.Link, - Mui__material__List: mui.List, - Mui__material__ListItem: mui.ListItem, - Mui__material__ListItemAvatar: mui.ListItemAvatar, - Mui__material__ListItemButton: mui.ListItemButton, - Mui__material__ListItemIcon: mui.ListItemIcon, - Mui__material__ListItemSecondaryAction: mui.ListItemSecondaryAction, - Mui__material__ListItemText: mui.ListItemText, - Mui__material__ListSubheader: mui.ListSubheader, - Mui__material__Menu: mui.Menu, - Mui__material__MenuItem: mui.MenuItem, - Mui__material__MenuList: mui.MenuList, - Mui__material__MobileStepper: mui.MobileStepper, - Mui__material__Modal: mui.Modal, - Mui__material__NativeSelect: mui.NativeSelect, - Mui__material__NoSsr: mui.NoSsr, - Mui__material__OutlinedInput: mui.OutlinedInput, - Mui__material__Pagination: mui.Pagination, - Mui__material__PaginationItem: mui.PaginationItem, - Mui__material__Paper: mui.Paper, - Mui__material__Popover: mui.Popover, - Mui__material__Popper: mui.Popper, - Mui__material__Portal: mui.Portal, - Mui__material__Radio: mui.Radio, - Mui__material__RadioGroup: mui.RadioGroup, - Mui__material__Rating: mui.Rating, - Mui__material__Select: mui.Select, - Mui__material__Skeleton: mui.Skeleton, - Mui__material__Slide: mui.Slide, - Mui__material__Slider: mui.Slider, - Mui__material__Snackbar: mui.Snackbar, - Mui__material__SnackbarContent: mui.SnackbarContent, - Mui__material__SpeedDial: mui.SpeedDial, - Mui__material__SpeedDialAction: mui.SpeedDialAction, - Mui__material__SpeedDialIcon: mui.SpeedDialIcon, - Mui__material__Stack: mui.Stack, - Mui__material__Step: mui.Step, - Mui__material__StepButton: mui.StepButton, - Mui__material__StepConnector: mui.StepConnector, - Mui__material__StepContent: mui.StepContent, - Mui__material__StepIcon: mui.StepIcon, - Mui__material__StepLabel: mui.StepLabel, - Mui__material__Stepper: mui.Stepper, - Mui__material__SvgIcon: mui.SvgIcon, - Mui__material__SwipeableDrawer: mui.SwipeableDrawer, - Mui__material__Switch: mui.Switch, - Mui__material__Tab: mui.Tab, - Mui__material__Table: mui.Table, - Mui__material__TableBody: mui.TableBody, - Mui__material__TableCell: mui.TableCell, - Mui__material__TableContainer: mui.TableContainer, - Mui__material__TableFooter: mui.TableFooter, - Mui__material__TableHead: mui.TableHead, - Mui__material__TablePagination: mui.TablePagination, - Mui__material__TableRow: mui.TableRow, - Mui__material__TableSortLabel: mui.TableSortLabel, - Mui__material__Tabs: mui.Tabs, - Mui__material__TabScrollButton: mui.TabScrollButton, - Mui__material__TextField: mui.TextField, - Mui__material__TextareaAutosize: mui.TextareaAutosize, - Mui__material__ToggleButton: mui.ToggleButton, - Mui__material__ToggleButtonGroup: mui.ToggleButtonGroup, - Mui__material__Toolbar: mui.Toolbar, - Mui__material__Tooltip: mui.Tooltip, - Mui__material__Typography: mui.Typography, - Mui__material__Zoom: mui.Zoom, -}; - -const PyConKRCommonMDXComponents: MDXComponents = { - Common__Components__Lottie: Common.Components.LottiePlayer, - Common__Components__NetworkLottie: Common.Components.NetworkLottiePlayer, - Common__Components__MDX__Confetti: Common.Components.MDX.Confetti, - Common__Components__MDX__PrimaryStyledDetails: Common.Components.MDX.PrimaryStyledDetails, - Common__Components__MDX__SecondaryStyledDetails: Common.Components.MDX.SecondaryStyledDetails, - Common__Components__MDX__Map: Common.Components.MDX.Map, - Common__Components__MDX__FAQAccordion: Common.Components.MDX.FAQAccordion, - Common__Components__MDX__FullWidthStyledButton: Common.Components.MDX.StyledFullWidthButton, - Common__Components__Session__List: Common.Components.MDX.SessionList, - Common__Components__Session__TimeTable: Common.Components.MDX.SessionTimeTable, -}; - -const PythonKRShopMDXComponents: MDXComponents = { +const ShopMDXComponents: MDXComponents = { Shop__Common__PriceDisplay: Shop.Components.Common.PriceDisplay, Shop__Common__SignInGuard: Shop.Components.Common.SignInGuard, Shop__Common__ContextProvider: Shop.Components.Common.ShopContextProvider, @@ -157,8 +16,7 @@ const PythonKRShopMDXComponents: MDXComponents = { Shop__Feature__PatronList: Shop.Components.Features.PatronList, }; -export const PyConKRMDXComponents = { - ...MUIMDXComponents, - ...PyConKRCommonMDXComponents, - ...PythonKRShopMDXComponents, +export const PyConKRMDXComponents: MDXComponents = { + ...Common.BaseMDXComponents, + ...ShopMDXComponents, }; diff --git a/apps/pyconkr/src/main.tsx b/apps/pyconkr/src/main.tsx index 705368a4..d9a64c06 100644 --- a/apps/pyconkr/src/main.tsx +++ b/apps/pyconkr/src/main.tsx @@ -1,7 +1,9 @@ import { Global } from "@emotion/react"; import * as Common from "@frontend/common"; import * as Shop from "@frontend/shop"; +import { theme2025 } from "@frontend/theme"; import { CircularProgress, CssBaseline, ThemeProvider } from "@mui/material"; +import { createTheme } from "@mui/material/styles"; import { ErrorBoundary, Suspense } from "@suspensive/react"; import { matchQuery, MutationCache, QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; @@ -11,11 +13,29 @@ import * as ReactDom from "react-dom/client"; import { BrowserRouter } from "react-router-dom"; import { App } from "./App.tsx"; +import PyCon2025HostLogoBig from "./assets/pyconkr2025_hostlogo_big.png"; +import PyCon2025HostLogoSmall from "./assets/pyconkr2025_hostlogo_small.png"; +import PyCon2025Logo from "./assets/pyconkr2025_logo.png"; +import PyCon2025MobileCoverImage from "./assets/pyconkr2025_main_cover_image.png"; +import PyCon2025MobileCoverTitle from "./assets/pyconkr2025_main_cover_title.png"; import { IS_DEBUG_ENV } from "./consts"; import { LOCAL_STORAGE_LANGUAGE_KEY } from "./consts/local_stroage.ts"; import { PyConKRMDXComponents } from "./consts/mdx_components.ts"; import { AppContext, AppContextType } from "./contexts/app_context.tsx"; -import { globalStyles, muiTheme } from "./styles/globalStyles.ts"; + +const muiTheme = createTheme(theme2025.muiTheme); + +const eventConfig: Common.Contexts.EventConfig = { + ...theme2025.event, + assets: { + ...theme2025.event.assets, + logo: PyCon2025Logo, + mobileCoverImage: PyCon2025MobileCoverImage, + mobileCoverTitle: PyCon2025MobileCoverTitle, + hostLogoBig: PyCon2025HostLogoBig, + hostLogoSmall: PyCon2025HostLogoSmall, + }, +}; const queryClient = new QueryClient({ defaultOptions: { @@ -49,6 +69,7 @@ const CommonOptions: Common.Contexts.ContextOptions = { backendApiDomain: import.meta.env.VITE_PYCONKR_BACKEND_API_DOMAIN, backendApiTimeout: 10000, mdxComponents: PyConKRMDXComponents, + eventConfig, }; const ShopOptions: Shop.Contexts.ContextOptions = { @@ -73,7 +94,7 @@ const MainApp: React.FC = () => { currentSiteMapDepth: [], - title: "PyCon Korea 2025", + title: eventConfig.eventName, }); return ( @@ -89,7 +110,7 @@ const MainApp: React.FC = () => { - + diff --git a/apps/pyconkr/vite.config.mts b/apps/pyconkr/vite.config.mts index 9765567e..d9dc6356 100644 --- a/apps/pyconkr/vite.config.mts +++ b/apps/pyconkr/vite.config.mts @@ -15,6 +15,7 @@ export default defineConfig({ alias: { "@frontend/common": path.resolve(__dirname, "../../packages/common/src/index.ts"), "@frontend/shop": path.resolve(__dirname, "../../packages/shop/src/index.ts"), + "@frontend/theme": path.resolve(__dirname, "../../packages/theme/src/index.ts"), "@apps/pyconkr": path.resolve(__dirname, "./src"), }, }, diff --git a/eslint.config.js b/eslint.config.js index 76c5720b..e6976f63 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -21,6 +21,15 @@ export default tseslint.config(js.configs.recommended, tseslint.configs.recommen project: "./tsconfig.json", }, }, +}, { + files: ["stories/**/*.{ts,tsx}", ".storybook/**/*.{ts,tsx}"], + languageOptions: { + parserOptions: { + project: "./stories/tsconfig.json", + }, + }, +}, +{ plugins: { "react-hooks": reactHooks, "react-refresh": reactRefresh, diff --git a/package.json b/package.json index 854ab543..c8c48a3b 100644 --- a/package.json +++ b/package.json @@ -15,11 +15,16 @@ "dev:@apps/pyconkr-participant-portal": "pnpm vite ./apps/pyconkr-participant-portal", "build:@apps/pyconkr-participant-portal": "tsc -b && pnpm vite build ./apps/pyconkr-participant-portal", "preview:@apps/pyconkr-participant-portal": "pnpm vite preview ./apps/pyconkr-admin", + "dev:@apps/pyconkr-2026": "pnpm vite ./apps/pyconkr-2026", + "build:@apps/pyconkr-2026": "tsc -b && pnpm vite build ./apps/pyconkr-2026", + "preview:@apps/pyconkr-2026": "pnpm vite preview ./apps/pyconkr-2026", "lint": "eslint .", "format": "prettier --write \"{apps,packages}/**/*.{js,jsx,ts,tsx}\"", "format:check": "prettier --check \"{apps,packages}/**/*.{js,jsx,ts,tsx}\"", "predeploy": "pnpm build:@apps/pyconkr", - "deploy": "gh-pages -d dist" + "deploy": "gh-pages -d dist", + "storybook": "storybook dev -p 6006", + "build-storybook": "storybook build" }, "workspaces": [ "packages/*", @@ -103,7 +108,18 @@ "vite": "^6.3.5", "vite-plugin-mdx": "^3.6.1", "vite-plugin-mkcert": "^1.17.8", - "vite-plugin-svgr": "^4.3.0" + "vite-plugin-svgr": "^4.3.0", + "storybook": "^10.3.3", + "@storybook/react-vite": "^10.3.3", + "@chromatic-com/storybook": "^5.1.1", + "@storybook/addon-vitest": "^10.3.3", + "@storybook/addon-a11y": "^10.3.3", + "@storybook/addon-docs": "^10.3.3", + "@storybook/addon-onboarding": "^10.3.3", + "vitest": "^4.1.2", + "playwright": "^1.58.2", + "@vitest/browser-playwright": "^4.1.2", + "@vitest/coverage-v8": "^4.1.2" }, "packageManager": "pnpm@10.7.1+sha512.2d92c86b7928dc8284f53494fb4201f983da65f0fb4f0d40baafa5cf628fa31dae3e5968f12466f17df7e97310e30f343a648baea1b9b350685dafafffdf5808" } diff --git a/packages/common/package.json b/packages/common/package.json index ad74d613..05f52d76 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -5,7 +5,5 @@ "scripts": { "build": "rollup -c" }, - "dependencies": { - "@apps/pyconkr": "link:." - } + "dependencies": {} } diff --git a/packages/common/src/assets/pyconkr2025_logo.png b/packages/common/src/assets/pyconkr2025_logo.png deleted file mode 100755 index a4b1f567..00000000 Binary files a/packages/common/src/assets/pyconkr2025_logo.png and /dev/null differ diff --git a/packages/common/src/components/index.ts b/packages/common/src/components/index.ts index 8b9914d8..bd61f9cf 100644 --- a/packages/common/src/components/index.ts +++ b/packages/common/src/components/index.ts @@ -21,6 +21,8 @@ import { } from "./mdx_components/faq_accordion"; import type { MapPropType as MapComponentPropType } from "./mdx_components/map"; import { Map as MapComponent } from "./mdx_components/map"; +import { MobileAccordion as MobileAccordionComponent } from "./mdx_components/mobile_accordion"; +import { MobileCover as MobileCoverComponent } from "./mdx_components/mobile_cover"; import { OneDetailsOpener as OneDetailsOpenerComponent } from "./mdx_components/one_details_opener"; import { SessionList as SessionListComponent } from "./mdx_components/session_list"; import { SessionTimeTable as SessionTimeTableComponent } from "./mdx_components/session_timetable"; @@ -28,7 +30,9 @@ import { PrimaryStyledDetails as PrimaryStyledDetailsComponent, HighlightedStyledDetails as SecondaryStyledDetailsComponent, } from "./mdx_components/styled_details"; +import { StyledDivider as StyledDividerComponent } from "./mdx_components/styled_divider"; import { StyledFullWidthButton as StyledFullWidthButtonComponent } from "./mdx_components/styled_full_width_button"; +import { SubContentContainer as SubContentContainerComponent } from "./mdx_components/sub_content_container"; import { MDXEditor as MDXEditorComponent } from "./mdx_editor"; import { PythonKorea as PythonKoreaComponent } from "./pythonkorea"; @@ -57,6 +61,10 @@ namespace Components { export const Map = MapComponent; export const FAQAccordion = FAQAccordionComponent; export const OneDetailsOpener = OneDetailsOpenerComponent; + export const StyledDivider = StyledDividerComponent; + export const SubContentContainer = SubContentContainerComponent; + export const MobileAccordion = MobileAccordionComponent; + export const MobileCover = MobileCoverComponent; export const SessionList = SessionListComponent; export const SessionTimeTable = SessionTimeTableComponent; export type MapPropType = MapComponentPropType; diff --git a/packages/common/src/components/mdx_components/mobile_accordion.tsx b/packages/common/src/components/mdx_components/mobile_accordion.tsx index 151023b2..8d626f2f 100644 --- a/packages/common/src/components/mdx_components/mobile_accordion.tsx +++ b/packages/common/src/components/mdx_components/mobile_accordion.tsx @@ -4,29 +4,40 @@ import { AccordionDetails, AccordionSummary, Accordion as MuiAccordion, Stack, T import * as React from "react"; import Marquee from "react-fast-marquee"; -import { useAppContext } from "../../../../../apps/pyconkr/src/contexts/app_context"; -import PyCon2025HostLogoBig from "../../assets/pyconkr2025_hostlogo_big.png"; -import PyCon2025HostLogoSmall from "../../assets/pyconkr2025_hostlogo_small.png"; +import Hooks from "../../hooks"; -const MarqueeAccordion: React.FC = () => { +type MobileAccordionProps = { + eventDates?: string; + venue?: { ko: string; en: string }; + hostLogoBig?: string; + hostLogoSmall?: string; +}; + +const MarqueeAccordion: React.FC<{ eventDates: string; hostLogoSmall?: string }> = ({ eventDates, hostLogoSmall }) => { const marqueeWidth = window.innerWidth * 0.9; const marqueeGradientWidth = window.innerWidth * 0.1; const items = React.useMemo(() => { return Array.from({ length: 100 }, () => ( - AUG 15 - 17 - logo + {eventDates} + {hostLogoSmall && logo} )); - }, []); + }, [eventDates, hostLogoSmall]); return ; }; -export const MobileAccordion: React.FC = () => { - const { language } = useAppContext(); +export const MobileAccordion: React.FC = (props) => { + const { language, eventConfig } = Hooks.Common.useCommonContext(); const [expanded, setExpanded] = React.useState(false); + const eventDates = props.eventDates ?? eventConfig?.eventDates ?? ""; + const venue = props.venue ?? eventConfig?.venue; + const hostLogoBig = props.hostLogoBig ?? eventConfig?.assets.hostLogoBig; + const hostLogoSmall = props.hostLogoSmall ?? eventConfig?.assets.hostLogoSmall; + const venueLines = venue ? (language === "ko" ? [venue.ko] : venue.en.split("\n")) : []; + return ( setExpanded((prev) => !prev)}> @@ -44,27 +55,20 @@ export const MobileAccordion: React.FC = () => { } sx={{ margin: 0, padding: 0 }} > - {expanded ? null : } + {expanded ? null : } - PyCon 2025 Host Logo + {hostLogoBig && Host Logo} - {language === "ko" ? ( - - - {"서울특별시 중구 필동로 1길 30 동국대학교 신공학관"} - - - ) : ( - - - {"New Engineering Building, Dongguk University"} - - - {"Pildong-ro 1-gil, Jung-gu, Seoul, Republic of Korea"} - + {venueLines.length > 0 && ( + + {venueLines.map((line) => ( + + {line} + + ))} )} diff --git a/packages/common/src/components/mdx_components/mobile_cover.tsx b/packages/common/src/components/mdx_components/mobile_cover.tsx index b2878c8d..fac9a774 100644 --- a/packages/common/src/components/mdx_components/mobile_cover.tsx +++ b/packages/common/src/components/mdx_components/mobile_cover.tsx @@ -1,22 +1,33 @@ import ArrowForwardIcon from "@mui/icons-material/ArrowForward"; import { ButtonBase, Stack, Typography } from "@mui/material"; import * as React from "react"; -import { useAppContext } from "../../../../../apps/pyconkr/src/contexts/app_context"; -import PyCon2025MobileLogoImage from "../../assets/pyconkr2025_main_cover_image.png"; -import PyCon2025MobileLogoTitle from "../../assets/pyconkr2025_main_cover_title.png"; -export const MobileCover: React.FC = () => { - const { language } = useAppContext(); +import Hooks from "../../hooks"; + +type MobileCoverProps = { + mobileCoverImage?: string; + mobileCoverTitle?: string; +}; + +export const MobileCover: React.FC = (props) => { + const { language, eventConfig } = Hooks.Common.useCommonContext(); const buttonTitle = language === "ko" ? "티켓 구매하기" : "Buy Ticket"; + const coverImage = props.mobileCoverImage ?? eventConfig?.assets.mobileCoverImage; + const coverTitle = props.mobileCoverTitle ?? eventConfig?.assets.mobileCoverTitle; + return ( - - Pycon 2025 Mobile Image - - - Pycon 2025 Mobile Title - + {coverImage && ( + + Mobile Cover Image + + )} + {coverTitle && ( + + Mobile Cover Title + + )} ({ justifyContent: "center", })); -const SessionImageErrorFallback: React.FC = () => ( - - PyCon 2025 Logo - -); +const SessionImageErrorFallback: React.FC = () => { + const { eventConfig } = Hooks.Common.useCommonContext(); + const logo = eventConfig?.assets.logo; + + return ( + + {logo && Event Logo} + + ); +}; const SessionTitle = styled(Typography)({ fontSize: "1.5em", diff --git a/packages/common/src/consts/mdx_components.ts b/packages/common/src/consts/mdx_components.ts new file mode 100644 index 00000000..0056cf2f --- /dev/null +++ b/packages/common/src/consts/mdx_components.ts @@ -0,0 +1,149 @@ +// 후대의 개발자님께 : 컴포넌트 맨 첫글자가 대문자로 시작하지 않으면 JSX 컴포넌트가 아니라 일반 HTML 태그로 인식합니다. 제발 대문자로 시작해주세요. +import * as mui from "@mui/material"; +import type { MDXComponents } from "mdx/types.js"; + +import Components from "../components"; + +const MUIMDXComponents: MDXComponents = { + Mui__material__Accordion: mui.Accordion, + Mui__material__AccordionActions: mui.AccordionActions, + Mui__material__AccordionDetails: mui.AccordionDetails, + Mui__material__AccordionSummary: mui.AccordionSummary, + Mui__material__Alert: mui.Alert, + Mui__material__AlertTitle: mui.AlertTitle, + Mui__material__AppBar: mui.AppBar, + Mui__material__Autocomplete: mui.Autocomplete, + Mui__material__Avatar: mui.Avatar, + Mui__material__AvatarGroup: mui.AvatarGroup, + Mui__material__Backdrop: mui.Backdrop, + Mui__material__Badge: mui.Badge, + Mui__material__BottomNavigation: mui.BottomNavigation, + Mui__material__BottomNavigationAction: mui.BottomNavigationAction, + Mui__material__Box: mui.Box, + Mui__material__Breadcrumbs: mui.Breadcrumbs, + Mui__material__Button: mui.Button, + Mui__material__ButtonBase: mui.ButtonBase, + Mui__material__ButtonGroup: mui.ButtonGroup, + Mui__material__Card: mui.Card, + Mui__material__CardActionArea: mui.CardActionArea, + Mui__material__CardActions: mui.CardActions, + Mui__material__CardContent: mui.CardContent, + Mui__material__CardHeader: mui.CardHeader, + Mui__material__CardMedia: mui.CardMedia, + Mui__material__Checkbox: mui.Checkbox, + Mui__material__Chip: mui.Chip, + Mui__material__CircularProgress: mui.CircularProgress, + Mui__material__Collapse: mui.Collapse, + Mui__material__Container: mui.Container, + Mui__material__Dialog: mui.Dialog, + Mui__material__DialogActions: mui.DialogActions, + Mui__material__DialogContent: mui.DialogContent, + Mui__material__DialogContentText: mui.DialogContentText, + Mui__material__DialogTitle: mui.DialogTitle, + Mui__material__Divider: mui.Divider, + Mui__material__Drawer: mui.Drawer, + Mui__material__Fab: mui.Fab, + Mui__material__Fade: mui.Fade, + Mui__material__FilledInput: mui.FilledInput, + Mui__material__FormControl: mui.FormControl, + Mui__material__FormControlLabel: mui.FormControlLabel, + Mui__material__FormGroup: mui.FormGroup, + Mui__material__FormHelperText: mui.FormHelperText, + Mui__material__FormLabel: mui.FormLabel, + Mui__material__Grid: mui.Grid, + Mui__material__Grow: mui.Grow, + Mui__material__Icon: mui.Icon, + Mui__material__IconButton: mui.IconButton, + Mui__material__ImageList: mui.ImageList, + Mui__material__ImageListItem: mui.ImageListItem, + Mui__material__ImageListItemBar: mui.ImageListItemBar, + Mui__material__Input: mui.Input, + Mui__material__InputAdornment: mui.InputAdornment, + Mui__material__InputBase: mui.InputBase, + Mui__material__InputLabel: mui.InputLabel, + Mui__material__LinearProgress: mui.LinearProgress, + Mui__material__Link: mui.Link, + Mui__material__List: mui.List, + Mui__material__ListItem: mui.ListItem, + Mui__material__ListItemAvatar: mui.ListItemAvatar, + Mui__material__ListItemButton: mui.ListItemButton, + Mui__material__ListItemIcon: mui.ListItemIcon, + Mui__material__ListItemSecondaryAction: mui.ListItemSecondaryAction, + Mui__material__ListItemText: mui.ListItemText, + Mui__material__ListSubheader: mui.ListSubheader, + Mui__material__Menu: mui.Menu, + Mui__material__MenuItem: mui.MenuItem, + Mui__material__MenuList: mui.MenuList, + Mui__material__MobileStepper: mui.MobileStepper, + Mui__material__Modal: mui.Modal, + Mui__material__NativeSelect: mui.NativeSelect, + Mui__material__NoSsr: mui.NoSsr, + Mui__material__OutlinedInput: mui.OutlinedInput, + Mui__material__Pagination: mui.Pagination, + Mui__material__PaginationItem: mui.PaginationItem, + Mui__material__Paper: mui.Paper, + Mui__material__Popover: mui.Popover, + Mui__material__Popper: mui.Popper, + Mui__material__Portal: mui.Portal, + Mui__material__Radio: mui.Radio, + Mui__material__RadioGroup: mui.RadioGroup, + Mui__material__Rating: mui.Rating, + Mui__material__Select: mui.Select, + Mui__material__Skeleton: mui.Skeleton, + Mui__material__Slide: mui.Slide, + Mui__material__Slider: mui.Slider, + Mui__material__Snackbar: mui.Snackbar, + Mui__material__SnackbarContent: mui.SnackbarContent, + Mui__material__SpeedDial: mui.SpeedDial, + Mui__material__SpeedDialAction: mui.SpeedDialAction, + Mui__material__SpeedDialIcon: mui.SpeedDialIcon, + Mui__material__Stack: mui.Stack, + Mui__material__Step: mui.Step, + Mui__material__StepButton: mui.StepButton, + Mui__material__StepConnector: mui.StepConnector, + Mui__material__StepContent: mui.StepContent, + Mui__material__StepIcon: mui.StepIcon, + Mui__material__StepLabel: mui.StepLabel, + Mui__material__Stepper: mui.Stepper, + Mui__material__SvgIcon: mui.SvgIcon, + Mui__material__SwipeableDrawer: mui.SwipeableDrawer, + Mui__material__Switch: mui.Switch, + Mui__material__Tab: mui.Tab, + Mui__material__Table: mui.Table, + Mui__material__TableBody: mui.TableBody, + Mui__material__TableCell: mui.TableCell, + Mui__material__TableContainer: mui.TableContainer, + Mui__material__TableFooter: mui.TableFooter, + Mui__material__TableHead: mui.TableHead, + Mui__material__TablePagination: mui.TablePagination, + Mui__material__TableRow: mui.TableRow, + Mui__material__TableSortLabel: mui.TableSortLabel, + Mui__material__Tabs: mui.Tabs, + Mui__material__TabScrollButton: mui.TabScrollButton, + Mui__material__TextField: mui.TextField, + Mui__material__TextareaAutosize: mui.TextareaAutosize, + Mui__material__ToggleButton: mui.ToggleButton, + Mui__material__ToggleButtonGroup: mui.ToggleButtonGroup, + Mui__material__Toolbar: mui.Toolbar, + Mui__material__Tooltip: mui.Tooltip, + Mui__material__Typography: mui.Typography, + Mui__material__Zoom: mui.Zoom, +}; + +const CommonMDXComponents: MDXComponents = { + Common__Components__Lottie: Components.LottiePlayer, + Common__Components__NetworkLottie: Components.NetworkLottiePlayer, + Common__Components__MDX__Confetti: Components.MDX.Confetti, + Common__Components__MDX__PrimaryStyledDetails: Components.MDX.PrimaryStyledDetails, + Common__Components__MDX__SecondaryStyledDetails: Components.MDX.SecondaryStyledDetails, + Common__Components__MDX__Map: Components.MDX.Map, + Common__Components__MDX__FAQAccordion: Components.MDX.FAQAccordion, + Common__Components__MDX__FullWidthStyledButton: Components.MDX.StyledFullWidthButton, + Common__Components__Session__List: Components.MDX.SessionList, + Common__Components__Session__TimeTable: Components.MDX.SessionTimeTable, +}; + +export const BaseMDXComponents: MDXComponents = { + ...MUIMDXComponents, + ...CommonMDXComponents, +}; diff --git a/packages/common/src/contexts/index.ts b/packages/common/src/contexts/index.ts index 69ffc1d2..8fcb05b9 100644 --- a/packages/common/src/contexts/index.ts +++ b/packages/common/src/contexts/index.ts @@ -2,6 +2,23 @@ import { MDXComponents } from "mdx/types"; import * as React from "react"; namespace GlobalContext { + export type EventConfig = { + year: number; + eventName: string; + eventDates: string; + venue: { + ko: string; + en: string; + }; + assets: { + logo: string; + mobileCoverImage?: string; + mobileCoverTitle?: string; + hostLogoBig?: string; + hostLogoSmall?: string; + }; + }; + export type ContextOptions = { language: "ko" | "en"; frontendDomain?: string; @@ -11,6 +28,7 @@ namespace GlobalContext { backendApiTimeout: number; backendApiCSRFCookieName?: string; mdxComponents?: MDXComponents; + eventConfig?: EventConfig; }; export const context = React.createContext({ diff --git a/packages/common/src/index.ts b/packages/common/src/index.ts index 3517381a..14cb945f 100644 --- a/packages/common/src/index.ts +++ b/packages/common/src/index.ts @@ -2,6 +2,7 @@ export { default as BackendAdminAPIs } from "./apis/admin_api"; export { default as BackendAPIs } from "./apis/index"; export { default as BackendParticipantPortalAPIs } from "./apis/participant_portal_api"; export { default as Components } from "./components/index"; +export { BaseMDXComponents } from "./consts/mdx_components"; export { default as Contexts } from "./contexts/index"; export { default as Hooks } from "./hooks/index"; export { default as Schemas } from "./schemas/index"; diff --git a/packages/theme/package.json b/packages/theme/package.json new file mode 100644 index 00000000..ad75e395 --- /dev/null +++ b/packages/theme/package.json @@ -0,0 +1,5 @@ +{ + "name": "@frontend/theme", + "type": "module", + "main": "./src/index.ts" +} diff --git a/packages/theme/src/global-styles.ts b/packages/theme/src/global-styles.ts new file mode 100644 index 00000000..21f019ff --- /dev/null +++ b/packages/theme/src/global-styles.ts @@ -0,0 +1,47 @@ +import { css } from "@emotion/react"; + +export const baseGlobalStyles = css` + @import url("https://cdn.jsdelivr.net/gh/orioncactus/pretendard/dist/web/static/pretendard.css"); + + html, + body { + font-family: + "Pretendard", + -apple-system, + BlinkMacSystemFont, + system-ui, + Roboto, + "Helvetica Neue", + "Segoe UI", + "Apple SD Gothic Neo", + "Noto Sans KR", + "Malgun Gothic", + "Apple Color Emoji", + "Segoe UI Emoji", + "Segoe UI Symbol", + sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-touch-callout: none; + + overscroll-behavior: none; + word-break: keep-all; + overflow-wrap: break-all; + + -webkit-user-drag: none; + -khtml-user-drag: none; + -moz-user-drag: none; + -o-user-drag: none; + user-drag: none; + + font-variant-numeric: tabular-nums; + } + + a { + text-decoration: none; + color: inherit; + } +`; + +export const PRETENDARD_FONT_FAMILY = + 'Pretendard, -apple-system, BlinkMacSystemFont, system-ui, Roboto, "Helvetica Neue", "Segoe UI", "Apple SD Gothic Neo", "Noto Sans KR", "Malgun Gothic", "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", sans-serif'; diff --git a/packages/theme/src/index.ts b/packages/theme/src/index.ts new file mode 100644 index 00000000..e5cfb706 --- /dev/null +++ b/packages/theme/src/index.ts @@ -0,0 +1,4 @@ +export type { PyConKREventConfig, PyConKRThemeConfig } from "./types"; +export { baseGlobalStyles, PRETENDARD_FONT_FAMILY } from "./global-styles"; +export { theme2025 } from "./themes/2025"; +export { theme2026 } from "./themes/2026"; diff --git a/packages/theme/src/themes/2025.ts b/packages/theme/src/themes/2025.ts new file mode 100644 index 00000000..8d2f17c3 --- /dev/null +++ b/packages/theme/src/themes/2025.ts @@ -0,0 +1,132 @@ +import { baseGlobalStyles, PRETENDARD_FONT_FAMILY } from "../global-styles"; +import type { PyConKRThemeConfig } from "../types"; + +export const theme2025: PyConKRThemeConfig = { + event: { + year: 2025, + eventName: "PyCon Korea 2025", + eventDates: "AUG 15 - 17", + venue: { + ko: "서울특별시 중구 필동로 1길 30 동국대학교 신공학관", + en: "New Engineering Building, Dongguk University\nPildong-ro 1-gil, Jung-gu, Seoul, Republic of Korea", + }, + assets: { + logo: "", + mobileCoverImage: "", + mobileCoverTitle: "", + hostLogoBig: "", + hostLogoSmall: "", + }, + }, + muiTheme: { + typography: { + fontFamily: PRETENDARD_FONT_FAMILY, + }, + palette: { + primary: { + main: "#259299", + light: "#B6D8D7", + dark: "#126D7F", + nonFocus: "#7AB2B3", + }, + secondary: { + main: "#259299", + light: "#B6D8D7", + dark: "#126D7F", + }, + highlight: { + main: "#E17101", + light: "#EE8D74", + dark: "#C66900", + contrastText: "#FFFFFF", + }, + mobileHeader: { + main: { + background: "rgba(182, 216, 215, 0.1)", + text: "#FFFFFF", + activeLanguage: "#888888", + }, + sub: { + background: "#B6D8D7", + text: "rgba(18, 109, 127, 0.6)", + activeLanguage: "#126D7F", + }, + }, + mobileNavigation: { + main: { + background: + "linear-gradient(0deg, rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.5)), linear-gradient(0deg, rgba(0, 0, 0, 0.15), rgba(0, 0, 0, 0.15))", + text: "#FFFFFF", + chip: { + background: "rgba(212, 212, 212, 0.5)", + hover: "rgba(212, 212, 212, 0.7)", + }, + divider: "rgba(255, 255, 255, 0.3)", + languageToggle: { + background: "transparent", + active: { + background: "rgba(255, 255, 255, 0.7)", + hover: "rgba(255, 255, 255, 0.8)", + }, + inactive: { + hover: "rgba(255, 255, 255, 0.1)", + }, + }, + }, + sub: { + background: "#B6D8D7", + text: "rgba(18, 109, 127, 0.9)", + chip: { + background: "rgba(18, 109, 127, 0.2)", + hover: "rgba(18, 109, 127, 0.3)", + }, + divider: "rgba(18, 109, 127, 0.3)", + languageToggle: { + background: "rgba(255, 255, 255, 0.1)", + active: { + background: "rgba(255, 255, 255, 0.9)", + hover: "rgba(255, 255, 255, 1)", + }, + inactive: { + hover: "rgba(255, 255, 255, 0.3)", + }, + }, + }, + }, + text: { + primary: "#000000", + secondary: "#666666", + disabled: "#999999", + }, + background: { + default: "#FFFFFF", + paper: "#FFFFFF", + }, + common: { + black: "#000000", + white: "#FFFFFF", + }, + error: { + main: "#d32f2f", + light: "#ef5350", + dark: "#c62828", + }, + warning: { + main: "#ed6c02", + light: "#ff9800", + dark: "#e65100", + }, + info: { + main: "#0288d1", + light: "#03a9f4", + dark: "#01579b", + }, + success: { + main: "#2e7d32", + light: "#4caf50", + dark: "#1b5e20", + }, + }, + }, + globalStyles: baseGlobalStyles, +}; diff --git a/packages/theme/src/themes/2026.ts b/packages/theme/src/themes/2026.ts new file mode 100644 index 00000000..d7ccf51f --- /dev/null +++ b/packages/theme/src/themes/2026.ts @@ -0,0 +1,127 @@ +import { baseGlobalStyles, PRETENDARD_FONT_FAMILY } from "../global-styles"; +import type { PyConKRThemeConfig } from "../types"; + +export const theme2026: PyConKRThemeConfig = { + event: { + year: 2026, + eventName: "PyCon Korea 2026", + eventDates: "", + venue: { + ko: "", + en: "", + }, + assets: { + logo: "", + }, + }, + muiTheme: { + typography: { + fontFamily: PRETENDARD_FONT_FAMILY, + }, + palette: { + primary: { + main: "#7C3AED", + light: "#C4B5FD", + dark: "#4C1D95", + }, + secondary: { + main: "#7C3AED", + light: "#C4B5FD", + dark: "#4C1D95", + }, + highlight: { + main: "#EC4899", + light: "#F9A8D4", + dark: "#BE185D", + contrastText: "#FFFFFF", + }, + mobileHeader: { + main: { + background: "rgba(124, 58, 237, 0.1)", + text: "#FFFFFF", + activeLanguage: "#888888", + }, + sub: { + background: "#C4B5FD", + text: "rgba(76, 29, 149, 0.6)", + activeLanguage: "#4C1D95", + }, + }, + mobileNavigation: { + main: { + background: + "linear-gradient(0deg, rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.5)), linear-gradient(0deg, rgba(0, 0, 0, 0.15), rgba(0, 0, 0, 0.15))", + text: "#FFFFFF", + chip: { + background: "rgba(196, 181, 253, 0.5)", + hover: "rgba(196, 181, 253, 0.7)", + }, + divider: "rgba(255, 255, 255, 0.3)", + languageToggle: { + background: "transparent", + active: { + background: "rgba(255, 255, 255, 0.7)", + hover: "rgba(255, 255, 255, 0.8)", + }, + inactive: { + hover: "rgba(255, 255, 255, 0.1)", + }, + }, + }, + sub: { + background: "#C4B5FD", + text: "rgba(76, 29, 149, 0.9)", + chip: { + background: "rgba(76, 29, 149, 0.2)", + hover: "rgba(76, 29, 149, 0.3)", + }, + divider: "rgba(76, 29, 149, 0.3)", + languageToggle: { + background: "rgba(255, 255, 255, 0.1)", + active: { + background: "rgba(255, 255, 255, 0.9)", + hover: "rgba(255, 255, 255, 1)", + }, + inactive: { + hover: "rgba(255, 255, 255, 0.3)", + }, + }, + }, + }, + text: { + primary: "#000000", + secondary: "#666666", + disabled: "#999999", + }, + background: { + default: "#FFFFFF", + paper: "#FFFFFF", + }, + common: { + black: "#000000", + white: "#FFFFFF", + }, + error: { + main: "#d32f2f", + light: "#ef5350", + dark: "#c62828", + }, + warning: { + main: "#ed6c02", + light: "#ff9800", + dark: "#e65100", + }, + info: { + main: "#0288d1", + light: "#03a9f4", + dark: "#01579b", + }, + success: { + main: "#2e7d32", + light: "#4caf50", + dark: "#1b5e20", + }, + }, + }, + globalStyles: baseGlobalStyles, +}; diff --git a/packages/theme/src/types.ts b/packages/theme/src/types.ts new file mode 100644 index 00000000..5f7596aa --- /dev/null +++ b/packages/theme/src/types.ts @@ -0,0 +1,25 @@ +import type { SerializedStyles } from "@emotion/react"; +import type { ThemeOptions } from "@mui/material/styles"; + +export interface PyConKREventConfig { + year: number; + eventName: string; + eventDates: string; + venue: { + ko: string; + en: string; + }; + assets: { + logo: string; + mobileCoverImage?: string; + mobileCoverTitle?: string; + hostLogoBig?: string; + hostLogoSmall?: string; + }; +} + +export interface PyConKRThemeConfig { + event: PyConKREventConfig; + muiTheme: ThemeOptions; + globalStyles: SerializedStyles; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 81b455ed..85d8aad2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -120,6 +120,9 @@ importers: specifier: ^1.15.6 version: 1.15.6 devDependencies: + '@chromatic-com/storybook': + specifier: ^5.1.1 + version: 5.1.1(storybook@10.3.3(@testing-library/dom@10.4.1)(prettier@3.5.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)) '@eslint/js': specifier: ^9.26.0 version: 9.26.0 @@ -135,6 +138,21 @@ importers: '@rollup/plugin-replace': specifier: ^6.0.2 version: 6.0.2(rollup@4.40.2) + '@storybook/addon-a11y': + specifier: ^10.3.3 + version: 10.3.3(storybook@10.3.3(@testing-library/dom@10.4.1)(prettier@3.5.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)) + '@storybook/addon-docs': + specifier: ^10.3.3 + version: 10.3.3(@types/react@19.1.4)(esbuild@0.25.4)(rollup@4.40.2)(storybook@10.3.3(@testing-library/dom@10.4.1)(prettier@3.5.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(vite@6.3.5(@types/node@22.15.18)) + '@storybook/addon-onboarding': + specifier: ^10.3.3 + version: 10.3.3(storybook@10.3.3(@testing-library/dom@10.4.1)(prettier@3.5.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)) + '@storybook/addon-vitest': + specifier: ^10.3.3 + version: 10.3.3(@vitest/browser-playwright@4.1.2)(@vitest/browser@4.1.2(vite@6.3.5(@types/node@22.15.18))(vitest@4.1.2))(@vitest/runner@4.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@10.3.3(@testing-library/dom@10.4.1)(prettier@3.5.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(vitest@4.1.2) + '@storybook/react-vite': + specifier: ^10.3.3 + version: 10.3.3(esbuild@0.25.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rollup@4.40.2)(storybook@10.3.3(@testing-library/dom@10.4.1)(prettier@3.5.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(typescript@5.7.3)(vite@6.3.5(@types/node@22.15.18)) '@tanstack/react-query-devtools': specifier: ^5.76.1 version: 5.76.1(@tanstack/react-query@5.76.1(react@19.1.0))(react@19.1.0) @@ -174,6 +192,12 @@ importers: '@vitejs/plugin-react': specifier: ^4.4.1 version: 4.4.1(vite@6.3.5(@types/node@22.15.18)) + '@vitest/browser-playwright': + specifier: ^4.1.2 + version: 4.1.2(playwright@1.58.2)(vite@6.3.5(@types/node@22.15.18))(vitest@4.1.2) + '@vitest/coverage-v8': + specifier: ^4.1.2 + version: 4.1.2(@vitest/browser@4.1.2(vite@6.3.5(@types/node@22.15.18))(vitest@4.1.2))(vitest@4.1.2) csstype: specifier: ^3.1.3 version: 3.1.3 @@ -207,6 +231,9 @@ importers: iamport-typings: specifier: ^1.4.0 version: 1.4.0 + playwright: + specifier: ^1.58.2 + version: 1.58.2 prettier: specifier: ^3.5.3 version: 3.5.3 @@ -219,6 +246,9 @@ importers: rollup-plugin-typescript2: specifier: ^0.36.0 version: 0.36.0(rollup@4.40.2)(typescript@5.7.3) + storybook: + specifier: ^10.3.3 + version: 10.3.3(@testing-library/dom@10.4.1)(prettier@3.5.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) typescript: specifier: ~5.7.3 version: 5.7.3 @@ -237,6 +267,9 @@ importers: vite-plugin-svgr: specifier: ^4.3.0 version: 4.3.0(rollup@4.40.2)(typescript@5.7.3)(vite@6.3.5(@types/node@22.15.18)) + vitest: + specifier: ^4.1.2 + version: 4.1.2(@types/node@22.15.18)(@vitest/browser-playwright@4.1.2)(vite@6.3.5(@types/node@22.15.18)) apps/pyconkr: dependencies: @@ -246,13 +279,41 @@ importers: '@frontend/shop': specifier: workspace:* version: link:../../packages/shop + '@frontend/theme': + specifier: workspace:* + version: link:../../packages/theme devDependencies: vite: specifier: ^6.3.5 version: 6.3.5(@types/node@22.15.18) vite-plugin-mdx: specifier: ^3.6.1 - version: 3.6.1(@mdx-js/mdx@3.1.0(acorn@8.14.1))(vite@6.3.5(@types/node@22.15.18)) + version: 3.6.1(@mdx-js/mdx@3.1.0(acorn@8.16.0))(vite@6.3.5(@types/node@22.15.18)) + vite-plugin-mkcert: + specifier: ^1.17.8 + version: 1.17.8(vite@6.3.5(@types/node@22.15.18)) + vite-plugin-svgr: + specifier: ^4.3.0 + version: 4.3.0(rollup@4.40.2)(typescript@5.7.3)(vite@6.3.5(@types/node@22.15.18)) + + apps/pyconkr-2026: + dependencies: + '@frontend/common': + specifier: workspace:* + version: link:../../packages/common + '@frontend/shop': + specifier: workspace:* + version: link:../../packages/shop + '@frontend/theme': + specifier: workspace:* + version: link:../../packages/theme + devDependencies: + vite: + specifier: ^6.3.5 + version: 6.3.5(@types/node@22.15.18) + vite-plugin-mdx: + specifier: ^3.6.1 + version: 3.6.1(@mdx-js/mdx@3.1.0(acorn@8.16.0))(vite@6.3.5(@types/node@22.15.18)) vite-plugin-mkcert: specifier: ^1.17.8 version: 1.17.8(vite@6.3.5(@types/node@22.15.18)) @@ -268,13 +329,16 @@ importers: '@frontend/shop': specifier: workspace:* version: link:../../packages/shop + '@frontend/theme': + specifier: workspace:* + version: link:../../packages/theme devDependencies: vite: specifier: ^6.3.5 version: 6.3.5(@types/node@22.15.18) vite-plugin-mdx: specifier: ^3.6.1 - version: 3.6.1(@mdx-js/mdx@3.1.0(acorn@8.14.1))(vite@6.3.5(@types/node@22.15.18)) + version: 3.6.1(@mdx-js/mdx@3.1.0(acorn@8.16.0))(vite@6.3.5(@types/node@22.15.18)) vite-plugin-mkcert: specifier: ^1.17.8 version: 1.17.8(vite@6.3.5(@types/node@22.15.18)) @@ -296,7 +360,7 @@ importers: version: 6.3.5(@types/node@22.15.18) vite-plugin-mdx: specifier: ^3.6.1 - version: 3.6.1(@mdx-js/mdx@3.1.0(acorn@8.14.1))(vite@6.3.5(@types/node@22.15.18)) + version: 3.6.1(@mdx-js/mdx@3.1.0(acorn@8.16.0))(vite@6.3.5(@types/node@22.15.18)) vite-plugin-mkcert: specifier: ^1.17.8 version: 1.17.8(vite@6.3.5(@types/node@22.15.18)) @@ -304,11 +368,7 @@ importers: specifier: ^4.3.0 version: 4.3.0(rollup@4.40.2)(typescript@5.7.3)(vite@6.3.5(@types/node@22.15.18)) - packages/common: - dependencies: - '@apps/pyconkr': - specifier: link:. - version: 'link:' + packages/common: {} packages/shop: dependencies: @@ -316,8 +376,13 @@ importers: specifier: workspace:^ version: link:../common + packages/theme: {} + packages: + '@adobe/css-tools@4.4.4': + resolution: {integrity: sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==} + '@alloc/quick-lru@5.2.0': resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} @@ -330,32 +395,66 @@ packages: resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} + '@babel/code-frame@7.29.0': + resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} + engines: {node: '>=6.9.0'} + '@babel/compat-data@7.27.2': resolution: {integrity: sha512-TUtMJYRPyUb/9aU8f3K0mjmjf6M9N5Woshn2CS6nqJSeJtTtQcpLUXjGt9vbF8ZGff0El99sWkLgzwW3VXnxZQ==} engines: {node: '>=6.9.0'} + '@babel/compat-data@7.29.0': + resolution: {integrity: sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==} + engines: {node: '>=6.9.0'} + '@babel/core@7.27.1': resolution: {integrity: sha512-IaaGWsQqfsQWVLqMn9OB92MNN7zukfVA4s7KKAI0KfrrDsZ0yhi5uV4baBuLuN7n3vsZpwP8asPPcVwApxvjBQ==} engines: {node: '>=6.9.0'} + '@babel/core@7.29.0': + resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==} + engines: {node: '>=6.9.0'} + '@babel/generator@7.27.1': resolution: {integrity: sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w==} engines: {node: '>=6.9.0'} + '@babel/generator@7.29.1': + resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} + engines: {node: '>=6.9.0'} + '@babel/helper-compilation-targets@7.27.2': resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} engines: {node: '>=6.9.0'} + '@babel/helper-compilation-targets@7.28.6': + resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + '@babel/helper-module-imports@7.27.1': resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} engines: {node: '>=6.9.0'} + '@babel/helper-module-imports@7.28.6': + resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} + engines: {node: '>=6.9.0'} + '@babel/helper-module-transforms@7.27.1': resolution: {integrity: sha512-9yHn519/8KvTU5BjTVEEeIM3w9/2yXNKoD82JifINImhpKkARMJKPP59kLo+BafpdN5zgNeIcS4jsGDmd3l58g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 + '@babel/helper-module-transforms@7.28.6': + resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + '@babel/helper-plugin-utils@7.27.1': resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} engines: {node: '>=6.9.0'} @@ -368,6 +467,10 @@ packages: resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + '@babel/helper-validator-option@7.27.1': resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} engines: {node: '>=6.9.0'} @@ -376,11 +479,20 @@ packages: resolution: {integrity: sha512-FCvFTm0sWV8Fxhpp2McP5/W53GPllQ9QeQ7SiqGWjMf/LVG07lFa5+pgK05IRhVwtvafT22KF+ZSnM9I545CvQ==} engines: {node: '>=6.9.0'} + '@babel/helpers@7.29.2': + resolution: {integrity: sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==} + engines: {node: '>=6.9.0'} + '@babel/parser@7.27.2': resolution: {integrity: sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw==} engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@7.29.2': + resolution: {integrity: sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==} + engines: {node: '>=6.0.0'} + hasBin: true + '@babel/plugin-transform-react-jsx-self@7.27.1': resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==} engines: {node: '>=6.9.0'} @@ -405,14 +517,39 @@ packages: resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} engines: {node: '>=6.9.0'} + '@babel/template@7.28.6': + resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} + engines: {node: '>=6.9.0'} + '@babel/traverse@7.27.1': resolution: {integrity: sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg==} engines: {node: '>=6.9.0'} + '@babel/traverse@7.29.0': + resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} + engines: {node: '>=6.9.0'} + '@babel/types@7.27.1': resolution: {integrity: sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==} engines: {node: '>=6.9.0'} + '@babel/types@7.29.0': + resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} + engines: {node: '>=6.9.0'} + + '@bcoe/v8-coverage@1.0.2': + resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} + engines: {node: '>=18'} + + '@blazediff/core@1.9.1': + resolution: {integrity: sha512-ehg3jIkYKulZh+8om/O25vkvSsXXwC+skXmyA87FFx6A/45eqOkZsBltMw/TVteb0mloiGT8oGRTcjRAz66zaA==} + + '@chromatic-com/storybook@5.1.1': + resolution: {integrity: sha512-BPoAXHM71XgeCK2u0jKr9i8apeQMm/Z9IWGyndA2FMijfQG9m8ox45DdWh/pxFkK5ClhGgirv5QwMhFIeHmThg==} + engines: {node: '>=20.0.0', yarn: '>=1.22.18'} + peerDependencies: + storybook: ^0.0.0-0 || ^10.1.0 || ^10.1.0-0 || ^10.2.0-0 || ^10.3.0-0 || ^10.4.0-0 + '@emnapi/core@1.4.3': resolution: {integrity: sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g==} @@ -684,10 +821,25 @@ packages: resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} + '@joshwooding/vite-plugin-react-docgen-typescript@0.6.4': + resolution: {integrity: sha512-6PyZBYKnnVNqOSB0YFly+62R7dmov8segT27A+RVTBVd4iAE6kbW9QBJGlyR2yG4D4ohzhZSTIu7BK1UTtmFFA==} + peerDependencies: + typescript: '>= 4.3.x' + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + '@jridgewell/gen-mapping@0.3.8': resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} engines: {node: '>=6.0.0'} + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} @@ -699,9 +851,15 @@ packages: '@jridgewell/sourcemap-codec@1.5.0': resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@mdx-js/mdx@3.1.0': resolution: {integrity: sha512-/QxEhPAvGwbQmy1Px8F899L5Uc2KZ6JtXwlCgJmjSTBedwOZkByYcBG4GceIGPXRDsmfxhHazuS+hlOShRLeDw==} @@ -894,6 +1052,9 @@ packages: '@napi-rs/wasm-runtime@0.2.10': resolution: {integrity: sha512-bCsCyeZEwVErsGmyPNSzwfwFn4OdxBj0mmv6hOFucB/k81Ojdu68RbZdxYsRQUPc9l6SU5F/cG+bXgWs3oUgsQ==} + '@neoconfetti/react@1.0.0': + resolution: {integrity: sha512-klcSooChXXOzIm+SE5IISIAn3bYzYfPjbX7D7HoqZL84oAfgREeSg5vSIaSFH+DaGzzvImTyWe1OyrJ67vik4A==} + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -910,6 +1071,9 @@ packages: resolution: {integrity: sha512-ROFF39F6ZrnzSUEmQQZUar0Jt4xVoP9WnDRdWwF4NNcXs3xBTLgBUDoOwW141y1jP+S8nahIbdxbFC7IShw9Iw==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + '@polka/url@1.0.0-next.29': + resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} + '@popperjs/core@2.11.8': resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} @@ -1096,6 +1260,101 @@ packages: '@rtsao/scc@1.1.0': resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + + '@storybook/addon-a11y@10.3.3': + resolution: {integrity: sha512-1yELCE8NXUJKcfS2k97pujtVw4z95PCwyoy2I6VAPiG/nRnJI8M6ned08YmCMEJhLBgGA1+GBh9HO4uk+xPcYA==} + peerDependencies: + storybook: ^10.3.3 + + '@storybook/addon-docs@10.3.3': + resolution: {integrity: sha512-trJQTpOtuOEuNv1Rn8X2Sopp5hSPpb0u0soEJ71BZAbxe4d2Y1d/1MYcxBdRKwncum6sCTsnxTpqQ/qvSJKlTQ==} + peerDependencies: + storybook: ^10.3.3 + + '@storybook/addon-onboarding@10.3.3': + resolution: {integrity: sha512-HZiHfXdcLc29WkYFW+1VAMtJCeAZOOLRYPvs97woJUcZqW8yfWEJ9MWH+j++736SFAv2aqZWNmP47OdBJ/kMkw==} + peerDependencies: + storybook: ^10.3.3 + + '@storybook/addon-vitest@10.3.3': + resolution: {integrity: sha512-9bbUAgraZhHh35WuWJn/83B0KvkcsP8dNpzbhssMeWQTfu92TR3DqRNeGTNSlyZvhbGfwiwT3TfBzzM4dX1feg==} + peerDependencies: + '@vitest/browser': ^3.0.0 || ^4.0.0 + '@vitest/browser-playwright': ^4.0.0 + '@vitest/runner': ^3.0.0 || ^4.0.0 + storybook: ^10.3.3 + vitest: ^3.0.0 || ^4.0.0 + peerDependenciesMeta: + '@vitest/browser': + optional: true + '@vitest/browser-playwright': + optional: true + '@vitest/runner': + optional: true + vitest: + optional: true + + '@storybook/builder-vite@10.3.3': + resolution: {integrity: sha512-awspKCTZvXyeV3KabL0id62mFbxR5u/5yyGQultwCiSb2/yVgBfip2MAqLyS850pvTiB6QFVM9deOyd2/G/bEA==} + peerDependencies: + storybook: ^10.3.3 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + + '@storybook/csf-plugin@10.3.3': + resolution: {integrity: sha512-Utlh7zubm+4iOzBBfzLW4F4vD99UBtl2Do4edlzK2F7krQIcFvR2ontjAE8S1FQVLZAC3WHalCOS+Ch8zf3knA==} + peerDependencies: + esbuild: '*' + rollup: '*' + storybook: ^10.3.3 + vite: '*' + webpack: '*' + peerDependenciesMeta: + esbuild: + optional: true + rollup: + optional: true + vite: + optional: true + webpack: + optional: true + + '@storybook/global@5.0.0': + resolution: {integrity: sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ==} + + '@storybook/icons@2.0.1': + resolution: {integrity: sha512-/smVjw88yK3CKsiuR71vNgWQ9+NuY2L+e8X7IMrFjexjm6ZR8ULrV2DRkTA61aV6ryefslzHEGDInGpnNeIocg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + '@storybook/react-dom-shim@10.3.3': + resolution: {integrity: sha512-lkhuh4G3UTreU9M3Iz5Dt32c6U+l/4XuvqLtbe1sDHENZH6aPj7y0b5FwnfHyvuTvYRhtbo29xZrF5Bp9kCC0w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + storybook: ^10.3.3 + + '@storybook/react-vite@10.3.3': + resolution: {integrity: sha512-qHdlBe1hjqFAGXa8JL7bWTLbP/gDqXbWDm+SYCB646NHh5yvVDkZLwigP5Y+UL7M2ASfqFtosnroUK9tcCM2dw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + storybook: ^10.3.3 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + + '@storybook/react@10.3.3': + resolution: {integrity: sha512-cGG5TbR8Tdx9zwlpsWyBEfWrejm5iWdYF26EwIhwuKq9GFUTAVrQzo0Rs7Tqc3ZyVhRS/YfsRiWSEH+zmq2JiQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + storybook: ^10.3.3 + typescript: '>= 4.9.x' + peerDependenciesMeta: + typescript: + optional: true + '@suspensive/react@3.2.0': resolution: {integrity: sha512-yDJyhhhT26iag2NwEJE9GVpK8OGC15YOAcLi98cZaQeLyQ9YlmSY5bDiomSt0sqdv6ydz3QZAKoQKp4hKLuhlg==} peerDependencies: @@ -1186,9 +1445,26 @@ packages: peerDependencies: react: ^18 || ^19 + '@testing-library/dom@10.4.1': + resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} + engines: {node: '>=18'} + + '@testing-library/jest-dom@6.9.1': + resolution: {integrity: sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==} + engines: {node: '>=14', npm: '>=6', yarn: '>=1'} + + '@testing-library/user-event@14.6.1': + resolution: {integrity: sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==} + engines: {node: '>=12', npm: '>=6'} + peerDependencies: + '@testing-library/dom': '>=7.21.4' + '@tybys/wasm-util@0.9.0': resolution: {integrity: sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==} + '@types/aria-query@5.0.4': + resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} + '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} @@ -1201,12 +1477,21 @@ packages: '@types/babel__traverse@7.20.7': resolution: {integrity: sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==} + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + '@types/crypto-js@4.2.2': resolution: {integrity: sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ==} '@types/debug@4.1.12': resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + + '@types/doctrine@0.0.9': + resolution: {integrity: sha512-eOIHzCUSH7SMfonMG1LsC2f8vxBFtho6NGBznK41R84YzPuvSBzrhEps33IsQiOW9+VL6NQ9DbjQJznk/S4uRA==} + '@types/estree-jsx@1.0.5': resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} @@ -1442,6 +1727,67 @@ packages: peerDependencies: vite: ^4.2.0 || ^5.0.0 || ^6.0.0 + '@vitest/browser-playwright@4.1.2': + resolution: {integrity: sha512-N0Z2HzMLvMR6k/tWPTS6Q/DaRscrkax/f2f9DIbNQr+Cd1l4W4wTf/I6S983PAMr0tNqqoTL+xNkLh9M5vbkLg==} + peerDependencies: + playwright: '*' + vitest: 4.1.2 + + '@vitest/browser@4.1.2': + resolution: {integrity: sha512-CwdIf90LNf1Zitgqy63ciMAzmyb4oIGs8WZ40VGYrWkssQKeEKr32EzO8MKUrDPPcPVHFI9oQ5ni2Hp24NaNRQ==} + peerDependencies: + vitest: 4.1.2 + + '@vitest/coverage-v8@4.1.2': + resolution: {integrity: sha512-sPK//PHO+kAkScb8XITeB1bf7fsk85Km7+rt4eeuRR3VS1/crD47cmV5wicisJmjNdfeokTZwjMk4Mj2d58Mgg==} + peerDependencies: + '@vitest/browser': 4.1.2 + vitest: 4.1.2 + peerDependenciesMeta: + '@vitest/browser': + optional: true + + '@vitest/expect@3.2.4': + resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} + + '@vitest/expect@4.1.2': + resolution: {integrity: sha512-gbu+7B0YgUJ2nkdsRJrFFW6X7NTP44WlhiclHniUhxADQJH5Szt9mZ9hWnJPJ8YwOK5zUOSSlSvyzRf0u1DSBQ==} + + '@vitest/mocker@4.1.2': + resolution: {integrity: sha512-Ize4iQtEALHDttPRCmN+FKqOl2vxTiNUhzobQFFt/BM1lRUTG7zRCLOykG/6Vo4E4hnUdfVLo5/eqKPukcWW7Q==} + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@3.2.4': + resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} + + '@vitest/pretty-format@4.1.2': + resolution: {integrity: sha512-dwQga8aejqeuB+TvXCMzSQemvV9hNEtDDpgUKDzOmNQayl2OG241PSWeJwKRH3CiC+sESrmoFd49rfnq7T4RnA==} + + '@vitest/runner@4.1.2': + resolution: {integrity: sha512-Gr+FQan34CdiYAwpGJmQG8PgkyFVmARK8/xSijia3eTFgVfpcpztWLuP6FttGNfPLJhaZVP/euvujeNYar36OQ==} + + '@vitest/snapshot@4.1.2': + resolution: {integrity: sha512-g7yfUmxYS4mNxk31qbOYsSt2F4m1E02LFqO53Xpzg3zKMhLAPZAjjfyl9e6z7HrW6LvUdTwAQR3HHfLjpko16A==} + + '@vitest/spy@3.2.4': + resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} + + '@vitest/spy@4.1.2': + resolution: {integrity: sha512-DU4fBnbVCJGNBwVA6xSToNXrkZNSiw59H8tcuUspVMsBDBST4nfvsPsEHDHGtWRRnqBERBQu7TrTKskmjqTXKA==} + + '@vitest/utils@3.2.4': + resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} + + '@vitest/utils@4.1.2': + resolution: {integrity: sha512-xw2/TiX82lQHA06cgbqRKFb5lCAy3axQ4H4SoUFhUsg+wztiet+co86IAMDtF6Vm1hc7J6j09oh/rgDn+JdKIQ==} + '@xobotyi/scrollbar-width@1.9.5': resolution: {integrity: sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ==} @@ -1459,6 +1805,11 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + acorn@8.16.0: + resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} + engines: {node: '>=0.4.0'} + hasBin: true + ajv-draft-04@1.0.0: resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} peerDependencies: @@ -1481,13 +1832,28 @@ packages: ajv@8.17.1: resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} + engines: {node: '>=12'} + ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + aria-query@5.3.0: + resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + aria-query@5.3.2: resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} engines: {node: '>= 0.4'} @@ -1520,9 +1886,20 @@ packages: resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} engines: {node: '>= 0.4'} + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + ast-types-flow@0.0.8: resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} + ast-types@0.16.1: + resolution: {integrity: sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==} + engines: {node: '>=4'} + + ast-v8-to-istanbul@1.0.0: + resolution: {integrity: sha512-1fSfIwuDICFA4LKkCzRPO7F0hzFf0B7+Xqrl27ynQaa+Rh0e1Es0v6kWHPott3lU10AyAr7oKHa65OppjLn3Rg==} + astring@1.9.0: resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==} hasBin: true @@ -1568,6 +1945,10 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + bcp-47-match@2.0.3: resolution: {integrity: sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ==} @@ -1584,6 +1965,10 @@ packages: brace-expansion@2.0.1: resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + brace-expansion@5.0.5: + resolution: {integrity: sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==} + engines: {node: 18 || 20 || >=22} + braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} @@ -1593,6 +1978,10 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true + bundle-name@4.1.0: + resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} + engines: {node: '>=18'} + bytes@3.1.2: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} @@ -1623,6 +2012,14 @@ packages: ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + chai@5.3.3: + resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} + engines: {node: '>=18'} + + chai@6.2.2: + resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} + engines: {node: '>=18'} + chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} @@ -1639,6 +2036,22 @@ packages: character-reference-invalid@2.0.1: resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} + check-error@2.1.3: + resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==} + engines: {node: '>= 16'} + + chromatic@13.3.5: + resolution: {integrity: sha512-MzPhxpl838qJUo0A55osCF2ifwPbjcIPeElr1d4SHcjnHoIcg7l1syJDrAYK/a+PcCBrOGi06jPNpQAln5hWgw==} + hasBin: true + peerDependencies: + '@chromatic-com/cypress': ^0.*.* || ^1.0.0 + '@chromatic-com/playwright': ^0.*.* || ^1.0.0 + peerDependenciesMeta: + '@chromatic-com/cypress': + optional: true + '@chromatic-com/playwright': + optional: true + classnames@2.3.1: resolution: {integrity: sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==} @@ -1750,6 +2163,9 @@ packages: resolution: {integrity: sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==} engines: {node: '>=8.0.0'} + css.escape@1.5.1: + resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} + csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} @@ -1788,6 +2204,10 @@ packages: decode-named-character-reference@1.1.0: resolution: {integrity: sha512-Wy+JTSbFThEOXQIR2L6mxJvEs+veIzpmqD7ynWxMXGpnk3smkHQOp6forLdHsKpAMW9iJpaBBIxz285t1n1C3w==} + deep-eql@5.0.2: + resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} + engines: {node: '>=6'} + deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} @@ -1795,10 +2215,22 @@ packages: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} + default-browser-id@5.0.1: + resolution: {integrity: sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==} + engines: {node: '>=18'} + + default-browser@5.5.0: + resolution: {integrity: sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==} + engines: {node: '>=18'} + define-data-property@1.1.4: resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} engines: {node: '>= 0.4'} + define-lazy-prop@3.0.0: + resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} + engines: {node: '>=12'} + define-properties@1.2.1: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} @@ -1830,6 +2262,16 @@ packages: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} engines: {node: '>=0.10.0'} + doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + + dom-accessibility-api@0.5.16: + resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} + + dom-accessibility-api@0.6.3: + resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} + dom-helpers@5.2.1: resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} @@ -1852,6 +2294,10 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + empathic@2.0.0: + resolution: {integrity: sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==} + engines: {node: '>=14'} + encodeurl@2.0.0: resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} engines: {node: '>= 0.8'} @@ -1882,6 +2328,9 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} + es-module-lexer@2.0.0: + resolution: {integrity: sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==} + es-object-atoms@1.1.1: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} @@ -2136,6 +2585,11 @@ packages: resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + esquery@1.6.0: resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} engines: {node: '>=0.10'} @@ -2188,6 +2642,10 @@ packages: resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==} engines: {node: '>=18.0.0'} + expect-type@1.3.0: + resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} + engines: {node: '>=12.0.0'} + express-rate-limit@7.5.0: resolution: {integrity: sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==} engines: {node: '>= 16'} @@ -2237,6 +2695,15 @@ packages: picomatch: optional: true + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + file-entry-cache@8.0.0: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} @@ -2249,6 +2716,10 @@ packages: resolution: {integrity: sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==} engines: {node: '>=8'} + filesize@10.1.6: + resolution: {integrity: sha512-sJslQKU2uM33qH5nqewAwVB2QgR6w1aMNsYUp3aN5rMRyXEwJGmZvaWzeJFNTOXWlHQyBFCWrdj3fV/fsTOX8w==} + engines: {node: '>= 10.4.0'} + fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} @@ -2312,6 +2783,11 @@ packages: resolution: {integrity: sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==} engines: {node: '>=14.14'} + fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -2362,6 +2838,10 @@ packages: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} + glob@13.0.6: + resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==} + engines: {node: 18 || 20 || >=22} + globals@11.12.0: resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} engines: {node: '>=4'} @@ -2478,6 +2958,9 @@ packages: hoist-non-react-statics@3.3.2: resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + html-url-attributes@3.0.1: resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} @@ -2514,6 +2997,10 @@ packages: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} + indent-string@4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} @@ -2582,6 +3069,11 @@ packages: is-decimal@2.0.1: resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} + is-docker@3.0.0: + resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + hasBin: true + is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -2601,6 +3093,11 @@ packages: is-hexadecimal@2.0.1: resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} + is-inside-container@1.0.0: + resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} + engines: {node: '>=14.16'} + hasBin: true + is-map@2.0.3: resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} engines: {node: '>= 0.4'} @@ -2666,15 +3163,34 @@ packages: resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} engines: {node: '>= 0.4'} + is-wsl@3.1.1: + resolution: {integrity: sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==} + engines: {node: '>=16'} + isarray@2.0.5: resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + istanbul-lib-coverage@3.2.2: + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + engines: {node: '>=8'} + + istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + + istanbul-reports@3.2.0: + resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} + engines: {node: '>=8'} + js-cookie@2.2.1: resolution: {integrity: sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==} + js-tokens@10.0.0: + resolution: {integrity: sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -2776,9 +3292,16 @@ packages: lottie-web@5.13.0: resolution: {integrity: sha512-+gfBXl6sxXMPe8tKQm7qzLnUy5DUPJPKIyRHwtpCpyUEYjHYRJC/5gjUvdkuO2c3JllrPtHXH5UJJK8LRYl5yQ==} + loupe@3.2.1: + resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} + lower-case@2.0.2: resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} + lru-cache@11.2.7: + resolution: {integrity: sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==} + engines: {node: 20 || >=22} + lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} @@ -2786,13 +3309,27 @@ packages: resolution: {integrity: sha512-RkRWjA926cTvz5rAb1BqyWkKbbjzCGchDUIKMCUvNi17j6f6j8uHGDV82Aqcqtzd+icoYpELmG3ksgGiFNNcNg==} engines: {node: '>=12'} + lz-string@1.5.0: + resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} + hasBin: true + magic-string@0.30.17: resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + magicast@0.5.2: + resolution: {integrity: sha512-E3ZJh4J3S9KfwdjZhe2afj6R9lGIN5Pher1pF39UGrXRqq/VDaGVIGN13BjHd2u8B61hArAGOnso7nBOouW3TQ==} + make-dir@3.1.0: resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} engines: {node: '>=8'} + make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + markdown-extensions@2.0.0: resolution: {integrity: sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==} engines: {node: '>=16'} @@ -2998,6 +3535,14 @@ packages: resolution: {integrity: sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==} engines: {node: '>= 0.6'} + min-indent@1.0.1: + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} + + minimatch@10.2.5: + resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} + engines: {node: 18 || 20 || >=22} + minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -3008,6 +3553,14 @@ packages: minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + minipass@7.1.3: + resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} + engines: {node: '>=16 || 14 >=14.17'} + + mrmime@2.0.1: + resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} + engines: {node: '>=10'} + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -3095,6 +3648,9 @@ packages: resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} engines: {node: '>= 0.4'} + obug@2.1.1: + resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} + on-finished@2.4.1: resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} engines: {node: '>= 0.8'} @@ -3102,6 +3658,10 @@ packages: once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + open@10.2.0: + resolution: {integrity: sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==} + engines: {node: '>=18'} + optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} @@ -3162,6 +3722,10 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + path-scurry@2.0.2: + resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} + engines: {node: 18 || 20 || >=22} + path-to-regexp@8.2.0: resolution: {integrity: sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==} engines: {node: '>=16'} @@ -3170,6 +3734,13 @@ packages: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + pathval@2.0.1: + resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} + engines: {node: '>= 14.16'} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -3181,6 +3752,10 @@ packages: resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} engines: {node: '>=12'} + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} + pkce-challenge@5.0.0: resolution: {integrity: sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==} engines: {node: '>=16.20.0'} @@ -3189,6 +3764,20 @@ packages: resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} engines: {node: '>=8'} + playwright-core@1.58.2: + resolution: {integrity: sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==} + engines: {node: '>=18'} + hasBin: true + + playwright@1.58.2: + resolution: {integrity: sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==} + engines: {node: '>=18'} + hasBin: true + + pngjs@7.0.0: + resolution: {integrity: sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==} + engines: {node: '>=14.19.0'} + possible-typed-array-names@1.1.0: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} @@ -3210,6 +3799,10 @@ packages: engines: {node: '>=14'} hasBin: true + pretty-format@27.5.1: + resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} @@ -3251,6 +3844,15 @@ packages: peerDependencies: react: ^16.3.0 || ^17.0.1 || ^18.0.0 || ^19.0.0 + react-docgen-typescript@2.4.0: + resolution: {integrity: sha512-ZtAp5XTO5HRzQctjPU0ybY0RRCQO19X/8fxn3w7y2VVTUbGHDKULPTL4ky3vB05euSgG5NpALhEhDPvQ56wvXg==} + peerDependencies: + typescript: '>= 4.3.x' + + react-docgen@8.0.3: + resolution: {integrity: sha512-aEZ9qP+/M+58x2qgfSFEWH1BxLyHe5+qkLNJOZQb5iGS017jpbRnoKhNRrXPeA6RfBrZO5wZrT9DMC1UqE1f1w==} + engines: {node: ^20.9.0 || >=22} + react-dom@19.1.0: resolution: {integrity: sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==} peerDependencies: @@ -3271,6 +3873,9 @@ packages: react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + react-is@17.0.2: + resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + react-is@18.3.1: resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} @@ -3339,6 +3944,10 @@ packages: resolution: {integrity: sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==} engines: {node: '>=0.10.0'} + recast@0.23.11: + resolution: {integrity: sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA==} + engines: {node: '>= 4'} + recma-build-jsx@1.0.0: resolution: {integrity: sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==} @@ -3351,6 +3960,10 @@ packages: recma-stringify@1.0.0: resolution: {integrity: sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==} + redent@3.0.0: + resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} + engines: {node: '>=8'} + reflect.getprototypeof@1.0.10: resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} engines: {node: '>= 0.4'} @@ -3476,6 +4089,10 @@ packages: rtl-css-js@1.16.1: resolution: {integrity: sha512-lRQgou1mu19e+Ya0LsTvKrVJ5TYUbqCVPAiImX3UfLTenarvPUl1QFdvu5Z3PYmHT9RCcwIfbjRQBntExyj3Zg==} + run-applescript@7.1.0: + resolution: {integrity: sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==} + engines: {node: '>=18'} + run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} @@ -3513,6 +4130,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.7.4: + resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} + engines: {node: '>=10'} + hasBin: true + send@1.2.0: resolution: {integrity: sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==} engines: {node: '>= 18'} @@ -3567,6 +4189,13 @@ packages: resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} engines: {node: '>= 0.4'} + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + sirv@3.0.2: + resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==} + engines: {node: '>=18'} + slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} @@ -3606,6 +4235,9 @@ packages: stack-generator@2.0.10: resolution: {integrity: sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ==} + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + stackframe@1.3.4: resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==} @@ -3619,6 +4251,18 @@ packages: resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} engines: {node: '>= 0.8'} + std-env@4.0.0: + resolution: {integrity: sha512-zUMPtQ/HBY3/50VbpkupYHbRroTRZJPRLvreamgErJVys0ceuzMkD44J/QjqhHjOzK42GQ3QZIeFG1OYfOtKqQ==} + + storybook@10.3.3: + resolution: {integrity: sha512-tMoRAts9EVqf+mEMPLC6z1DPyHbcPe+CV1MhLN55IKsl0HxNjvVGK44rVPSePbltPE6vIsn4bdRj6CCUt8SJwQ==} + hasBin: true + peerDependencies: + prettier: ^2 || ^3 + peerDependenciesMeta: + prettier: + optional: true + string.prototype.includes@2.0.1: resolution: {integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==} engines: {node: '>= 0.4'} @@ -3638,10 +4282,22 @@ packages: stringify-entities@4.0.4: resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + strip-ansi@7.2.0: + resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} + engines: {node: '>=12'} + strip-bom@3.0.0: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} + strip-indent@3.0.0: + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + engines: {node: '>=8'} + + strip-indent@4.1.1: + resolution: {integrity: sha512-SlyRoSkdh1dYP0PzclLE7r0M9sgbFKKMFXpFRUMNuKhQSbC6VQIGzq3E0qsfvGJaUFJPGv6Ws1NZ/haTAjfbMA==} + engines: {node: '>=12'} + strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} @@ -3684,14 +4340,36 @@ packages: tiny-invariant@1.2.0: resolution: {integrity: sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg==} - tinyglobby@0.2.13: - resolution: {integrity: sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==} - engines: {node: '>=12.0.0'} + tiny-invariant@1.3.3: + resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@1.0.4: + resolution: {integrity: sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==} + engines: {node: '>=18'} tinyglobby@0.2.14: resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} engines: {node: '>=12.0.0'} + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + + tinyrainbow@2.0.0: + resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} + engines: {node: '>=14.0.0'} + + tinyrainbow@3.1.0: + resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} + engines: {node: '>=14.0.0'} + + tinyspy@4.0.4: + resolution: {integrity: sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==} + engines: {node: '>=14.0.0'} + to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -3703,6 +4381,10 @@ packages: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} + totalist@3.0.1: + resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} + engines: {node: '>=6'} + trim-lines@3.0.1: resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} @@ -3722,12 +4404,20 @@ packages: peerDependencies: typescript: '>=4.8.4' + ts-dedent@2.2.0: + resolution: {integrity: sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==} + engines: {node: '>=6.10'} + ts-easing@0.2.0: resolution: {integrity: sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ==} tsconfig-paths@3.15.0: resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + tsconfig-paths@4.2.0: + resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} + engines: {node: '>=6'} + tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} @@ -3819,6 +4509,10 @@ packages: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} + unplugin@2.3.11: + resolution: {integrity: sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==} + engines: {node: '>=18.12.0'} + unrs-resolver@1.7.8: resolution: {integrity: sha512-2zsXwyOXmCX9nGz4vhtZRYhe30V78heAv+KDc21A/KMdovGHbZcixeD5JHEF0DrFXzdytwuzYclcPbvp8A3Jlw==} @@ -3831,6 +4525,11 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + use-sync-external-store@1.6.0: + resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + validate.io-array@1.0.6: resolution: {integrity: sha512-DeOy7CnPEziggrOO5CZhVKJw6S3Yi7e9e65R1Nl/RTN1vTQKnzjfvks0/8kQ40FP/dsjRAOd4hxmJ7uLa6vxkg==} @@ -3922,9 +4621,47 @@ packages: yaml: optional: true + vitest@4.1.2: + resolution: {integrity: sha512-xjR1dMTVHlFLh98JE3i/f/WePqJsah4A0FK9cc8Ehp9Udk0AZk6ccpIZhh1qJ/yxVWRZ+Q54ocnD8TXmkhspGg==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@opentelemetry/api': ^1.9.0 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.1.2 + '@vitest/browser-preview': 4.1.2 + '@vitest/browser-webdriverio': 4.1.2 + '@vitest/ui': 4.1.2 + happy-dom: '*' + jsdom: '*' + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@opentelemetry/api': + optional: true + '@types/node': + optional: true + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + web-namespaces@2.0.1: resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} + webpack-virtual-modules@0.6.2: + resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} + which-boxed-primitive@1.1.1: resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} engines: {node: '>= 0.4'} @@ -3946,6 +4683,11 @@ packages: engines: {node: '>= 8'} hasBin: true + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + word-wrap@1.2.5: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} @@ -3953,6 +4695,22 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + ws@8.20.0: + resolution: {integrity: sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + wsl-utils@0.1.0: + resolution: {integrity: sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==} + engines: {node: '>=18'} + yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} @@ -3977,6 +4735,8 @@ packages: snapshots: + '@adobe/css-tools@4.4.4': {} + '@alloc/quick-lru@5.2.0': {} '@ampproject/remapping@2.3.0': @@ -3990,8 +4750,16 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.1 + '@babel/code-frame@7.29.0': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + '@babel/compat-data@7.27.2': {} + '@babel/compat-data@7.29.0': {} + '@babel/core@7.27.1': dependencies: '@ampproject/remapping': 2.3.0 @@ -4012,6 +4780,26 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/core@7.29.0': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helpers': 7.29.2 + '@babel/parser': 7.29.2 + '@babel/template': 7.28.6 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.1 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + '@babel/generator@7.27.1': dependencies: '@babel/parser': 7.27.2 @@ -4020,6 +4808,14 @@ snapshots: '@jridgewell/trace-mapping': 0.3.25 jsesc: 3.1.0 + '@babel/generator@7.29.1': + dependencies: + '@babel/parser': 7.29.2 + '@babel/types': 7.29.0 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + '@babel/helper-compilation-targets@7.27.2': dependencies: '@babel/compat-data': 7.27.2 @@ -4028,6 +4824,16 @@ snapshots: lru-cache: 5.1.1 semver: 6.3.1 + '@babel/helper-compilation-targets@7.28.6': + dependencies: + '@babel/compat-data': 7.29.0 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.24.5 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-globals@7.28.0': {} + '@babel/helper-module-imports@7.27.1': dependencies: '@babel/traverse': 7.27.1 @@ -4035,6 +4841,13 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-module-imports@7.28.6': + dependencies: + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + '@babel/helper-module-transforms@7.27.1(@babel/core@7.27.1)': dependencies: '@babel/core': 7.27.1 @@ -4044,12 +4857,23 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-imports': 7.28.6 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + '@babel/helper-plugin-utils@7.27.1': {} '@babel/helper-string-parser@7.27.1': {} '@babel/helper-validator-identifier@7.27.1': {} + '@babel/helper-validator-identifier@7.28.5': {} + '@babel/helper-validator-option@7.27.1': {} '@babel/helpers@7.27.1': @@ -4057,10 +4881,19 @@ snapshots: '@babel/template': 7.27.2 '@babel/types': 7.27.1 + '@babel/helpers@7.29.2': + dependencies: + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + '@babel/parser@7.27.2': dependencies: '@babel/types': 7.27.1 + '@babel/parser@7.29.2': + dependencies: + '@babel/types': 7.29.0 + '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.27.1)': dependencies: '@babel/core': 7.27.1 @@ -4081,6 +4914,12 @@ snapshots: '@babel/parser': 7.27.2 '@babel/types': 7.27.1 + '@babel/template@7.28.6': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/parser': 7.29.2 + '@babel/types': 7.29.0 + '@babel/traverse@7.27.1': dependencies: '@babel/code-frame': 7.27.1 @@ -4093,11 +4932,44 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/traverse@7.29.0': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.29.2 + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + debug: 4.4.1 + transitivePeerDependencies: + - supports-color + '@babel/types@7.27.1': dependencies: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 + '@babel/types@7.29.0': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + + '@bcoe/v8-coverage@1.0.2': {} + + '@blazediff/core@1.9.1': {} + + '@chromatic-com/storybook@5.1.1(storybook@10.3.3(@testing-library/dom@10.4.1)(prettier@3.5.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))': + dependencies: + '@neoconfetti/react': 1.0.0 + chromatic: 13.3.5 + filesize: 10.1.6 + jsonfile: 6.1.0 + storybook: 10.3.3(@testing-library/dom@10.4.1)(prettier@3.5.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + strip-ansi: 7.2.0 + transitivePeerDependencies: + - '@chromatic-com/cypress' + - '@chromatic-com/playwright' + '@emnapi/core@1.4.3': dependencies: '@emnapi/wasi-threads': 1.0.2 @@ -4329,23 +5201,48 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} + '@joshwooding/vite-plugin-react-docgen-typescript@0.6.4(typescript@5.7.3)(vite@6.3.5(@types/node@22.15.18))': + dependencies: + glob: 13.0.6 + react-docgen-typescript: 2.4.0(typescript@5.7.3) + vite: 6.3.5(@types/node@22.15.18) + optionalDependencies: + typescript: 5.7.3 + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping': 0.3.31 + '@jridgewell/gen-mapping@0.3.8': dependencies: '@jridgewell/set-array': 1.2.1 '@jridgewell/sourcemap-codec': 1.5.0 '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.8 + '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/resolve-uri@3.1.2': {} '@jridgewell/set-array@1.2.1': {} '@jridgewell/sourcemap-codec@1.5.0': {} + '@jridgewell/sourcemap-codec@1.5.5': {} + '@jridgewell/trace-mapping@0.3.25': dependencies: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + '@mdx-js/mdx@3.1.0(acorn@8.14.1)': dependencies: '@types/estree': 1.0.7 @@ -4376,6 +5273,36 @@ snapshots: - acorn - supports-color + '@mdx-js/mdx@3.1.0(acorn@8.16.0)': + dependencies: + '@types/estree': 1.0.7 + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdx': 2.0.13 + collapse-white-space: 2.1.0 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + estree-util-scope: 1.0.0 + estree-walker: 3.0.3 + hast-util-to-jsx-runtime: 2.3.6 + markdown-extensions: 2.0.0 + recma-build-jsx: 1.0.0 + recma-jsx: 1.0.0(acorn@8.16.0) + recma-stringify: 1.0.0 + rehype-recma: 1.0.0 + remark-mdx: 3.1.0 + remark-parse: 11.0.0 + remark-rehype: 11.1.2 + source-map: 0.7.4 + unified: 11.0.5 + unist-util-position-from-estree: 2.0.0 + unist-util-stringify-position: 4.0.0 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + transitivePeerDependencies: + - acorn + - supports-color + '@mdx-js/react@3.1.0(@types/react@19.1.4)(react@19.1.0)': dependencies: '@types/mdx': 2.0.13 @@ -4568,6 +5495,8 @@ snapshots: '@tybys/wasm-util': 0.9.0 optional: true + '@neoconfetti/react@1.0.0': {} + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -4582,6 +5511,8 @@ snapshots: '@pkgr/core@0.2.4': {} + '@polka/url@1.0.0-next.29': {} + '@popperjs/core@2.11.8': {} '@rjsf/core@6.0.0-beta.10(@rjsf/utils@6.0.0-beta.10(react@19.1.0))(react@19.1.0)': @@ -4722,16 +5653,128 @@ snapshots: '@rollup/rollup-linux-x64-musl@4.40.2': optional: true - '@rollup/rollup-win32-arm64-msvc@4.40.2': - optional: true + '@rollup/rollup-win32-arm64-msvc@4.40.2': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.40.2': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.40.2': + optional: true + + '@rtsao/scc@1.1.0': {} + + '@standard-schema/spec@1.1.0': {} + + '@storybook/addon-a11y@10.3.3(storybook@10.3.3(@testing-library/dom@10.4.1)(prettier@3.5.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))': + dependencies: + '@storybook/global': 5.0.0 + axe-core: 4.10.3 + storybook: 10.3.3(@testing-library/dom@10.4.1)(prettier@3.5.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + + '@storybook/addon-docs@10.3.3(@types/react@19.1.4)(esbuild@0.25.4)(rollup@4.40.2)(storybook@10.3.3(@testing-library/dom@10.4.1)(prettier@3.5.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(vite@6.3.5(@types/node@22.15.18))': + dependencies: + '@mdx-js/react': 3.1.0(@types/react@19.1.4)(react@19.1.0) + '@storybook/csf-plugin': 10.3.3(esbuild@0.25.4)(rollup@4.40.2)(storybook@10.3.3(@testing-library/dom@10.4.1)(prettier@3.5.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(vite@6.3.5(@types/node@22.15.18)) + '@storybook/icons': 2.0.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@storybook/react-dom-shim': 10.3.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@10.3.3(@testing-library/dom@10.4.1)(prettier@3.5.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + storybook: 10.3.3(@testing-library/dom@10.4.1)(prettier@3.5.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + ts-dedent: 2.2.0 + transitivePeerDependencies: + - '@types/react' + - esbuild + - rollup + - vite + - webpack + + '@storybook/addon-onboarding@10.3.3(storybook@10.3.3(@testing-library/dom@10.4.1)(prettier@3.5.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))': + dependencies: + storybook: 10.3.3(@testing-library/dom@10.4.1)(prettier@3.5.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + + '@storybook/addon-vitest@10.3.3(@vitest/browser-playwright@4.1.2)(@vitest/browser@4.1.2(vite@6.3.5(@types/node@22.15.18))(vitest@4.1.2))(@vitest/runner@4.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@10.3.3(@testing-library/dom@10.4.1)(prettier@3.5.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(vitest@4.1.2)': + dependencies: + '@storybook/global': 5.0.0 + '@storybook/icons': 2.0.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + storybook: 10.3.3(@testing-library/dom@10.4.1)(prettier@3.5.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + optionalDependencies: + '@vitest/browser': 4.1.2(vite@6.3.5(@types/node@22.15.18))(vitest@4.1.2) + '@vitest/browser-playwright': 4.1.2(playwright@1.58.2)(vite@6.3.5(@types/node@22.15.18))(vitest@4.1.2) + '@vitest/runner': 4.1.2 + vitest: 4.1.2(@types/node@22.15.18)(@vitest/browser-playwright@4.1.2)(vite@6.3.5(@types/node@22.15.18)) + transitivePeerDependencies: + - react + - react-dom + + '@storybook/builder-vite@10.3.3(esbuild@0.25.4)(rollup@4.40.2)(storybook@10.3.3(@testing-library/dom@10.4.1)(prettier@3.5.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(vite@6.3.5(@types/node@22.15.18))': + dependencies: + '@storybook/csf-plugin': 10.3.3(esbuild@0.25.4)(rollup@4.40.2)(storybook@10.3.3(@testing-library/dom@10.4.1)(prettier@3.5.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(vite@6.3.5(@types/node@22.15.18)) + storybook: 10.3.3(@testing-library/dom@10.4.1)(prettier@3.5.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + ts-dedent: 2.2.0 + vite: 6.3.5(@types/node@22.15.18) + transitivePeerDependencies: + - esbuild + - rollup + - webpack + + '@storybook/csf-plugin@10.3.3(esbuild@0.25.4)(rollup@4.40.2)(storybook@10.3.3(@testing-library/dom@10.4.1)(prettier@3.5.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(vite@6.3.5(@types/node@22.15.18))': + dependencies: + storybook: 10.3.3(@testing-library/dom@10.4.1)(prettier@3.5.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + unplugin: 2.3.11 + optionalDependencies: + esbuild: 0.25.4 + rollup: 4.40.2 + vite: 6.3.5(@types/node@22.15.18) + + '@storybook/global@5.0.0': {} - '@rollup/rollup-win32-ia32-msvc@4.40.2': - optional: true + '@storybook/icons@2.0.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) - '@rollup/rollup-win32-x64-msvc@4.40.2': - optional: true + '@storybook/react-dom-shim@10.3.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@10.3.3(@testing-library/dom@10.4.1)(prettier@3.5.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))': + dependencies: + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + storybook: 10.3.3(@testing-library/dom@10.4.1)(prettier@3.5.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@rtsao/scc@1.1.0': {} + '@storybook/react-vite@10.3.3(esbuild@0.25.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rollup@4.40.2)(storybook@10.3.3(@testing-library/dom@10.4.1)(prettier@3.5.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(typescript@5.7.3)(vite@6.3.5(@types/node@22.15.18))': + dependencies: + '@joshwooding/vite-plugin-react-docgen-typescript': 0.6.4(typescript@5.7.3)(vite@6.3.5(@types/node@22.15.18)) + '@rollup/pluginutils': 5.1.4(rollup@4.40.2) + '@storybook/builder-vite': 10.3.3(esbuild@0.25.4)(rollup@4.40.2)(storybook@10.3.3(@testing-library/dom@10.4.1)(prettier@3.5.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(vite@6.3.5(@types/node@22.15.18)) + '@storybook/react': 10.3.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@10.3.3(@testing-library/dom@10.4.1)(prettier@3.5.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(typescript@5.7.3) + empathic: 2.0.0 + magic-string: 0.30.17 + react: 19.1.0 + react-docgen: 8.0.3 + react-dom: 19.1.0(react@19.1.0) + resolve: 1.22.10 + storybook: 10.3.3(@testing-library/dom@10.4.1)(prettier@3.5.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + tsconfig-paths: 4.2.0 + vite: 6.3.5(@types/node@22.15.18) + transitivePeerDependencies: + - esbuild + - rollup + - supports-color + - typescript + - webpack + + '@storybook/react@10.3.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@10.3.3(@testing-library/dom@10.4.1)(prettier@3.5.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(typescript@5.7.3)': + dependencies: + '@storybook/global': 5.0.0 + '@storybook/react-dom-shim': 10.3.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@10.3.3(@testing-library/dom@10.4.1)(prettier@3.5.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)) + react: 19.1.0 + react-docgen: 8.0.3 + react-docgen-typescript: 2.4.0(typescript@5.7.3) + react-dom: 19.1.0(react@19.1.0) + storybook: 10.3.3(@testing-library/dom@10.4.1)(prettier@3.5.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + optionalDependencies: + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color '@suspensive/react@3.2.0(react@19.1.0)': dependencies: @@ -4822,11 +5865,37 @@ snapshots: '@tanstack/query-core': 5.76.0 react: 19.1.0 + '@testing-library/dom@10.4.1': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/runtime': 7.28.2 + '@types/aria-query': 5.0.4 + aria-query: 5.3.0 + dom-accessibility-api: 0.5.16 + lz-string: 1.5.0 + picocolors: 1.1.1 + pretty-format: 27.5.1 + + '@testing-library/jest-dom@6.9.1': + dependencies: + '@adobe/css-tools': 4.4.4 + aria-query: 5.3.2 + css.escape: 1.5.1 + dom-accessibility-api: 0.6.3 + picocolors: 1.1.1 + redent: 3.0.0 + + '@testing-library/user-event@14.6.1(@testing-library/dom@10.4.1)': + dependencies: + '@testing-library/dom': 10.4.1 + '@tybys/wasm-util@0.9.0': dependencies: tslib: 2.8.1 optional: true + '@types/aria-query@5.0.4': {} + '@types/babel__core@7.20.5': dependencies: '@babel/parser': 7.27.2 @@ -4848,12 +5917,21 @@ snapshots: dependencies: '@babel/types': 7.27.1 + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + '@types/crypto-js@4.2.2': {} '@types/debug@4.1.12': dependencies: '@types/ms': 2.1.0 + '@types/deep-eql@4.0.2': {} + + '@types/doctrine@0.0.9': {} + '@types/estree-jsx@1.0.5': dependencies: '@types/estree': 1.0.7 @@ -5100,6 +6178,115 @@ snapshots: transitivePeerDependencies: - supports-color + '@vitest/browser-playwright@4.1.2(playwright@1.58.2)(vite@6.3.5(@types/node@22.15.18))(vitest@4.1.2)': + dependencies: + '@vitest/browser': 4.1.2(vite@6.3.5(@types/node@22.15.18))(vitest@4.1.2) + '@vitest/mocker': 4.1.2(vite@6.3.5(@types/node@22.15.18)) + playwright: 1.58.2 + tinyrainbow: 3.1.0 + vitest: 4.1.2(@types/node@22.15.18)(@vitest/browser-playwright@4.1.2)(vite@6.3.5(@types/node@22.15.18)) + transitivePeerDependencies: + - bufferutil + - msw + - utf-8-validate + - vite + + '@vitest/browser@4.1.2(vite@6.3.5(@types/node@22.15.18))(vitest@4.1.2)': + dependencies: + '@blazediff/core': 1.9.1 + '@vitest/mocker': 4.1.2(vite@6.3.5(@types/node@22.15.18)) + '@vitest/utils': 4.1.2 + magic-string: 0.30.21 + pngjs: 7.0.0 + sirv: 3.0.2 + tinyrainbow: 3.1.0 + vitest: 4.1.2(@types/node@22.15.18)(@vitest/browser-playwright@4.1.2)(vite@6.3.5(@types/node@22.15.18)) + ws: 8.20.0 + transitivePeerDependencies: + - bufferutil + - msw + - utf-8-validate + - vite + + '@vitest/coverage-v8@4.1.2(@vitest/browser@4.1.2(vite@6.3.5(@types/node@22.15.18))(vitest@4.1.2))(vitest@4.1.2)': + dependencies: + '@bcoe/v8-coverage': 1.0.2 + '@vitest/utils': 4.1.2 + ast-v8-to-istanbul: 1.0.0 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-report: 3.0.1 + istanbul-reports: 3.2.0 + magicast: 0.5.2 + obug: 2.1.1 + std-env: 4.0.0 + tinyrainbow: 3.1.0 + vitest: 4.1.2(@types/node@22.15.18)(@vitest/browser-playwright@4.1.2)(vite@6.3.5(@types/node@22.15.18)) + optionalDependencies: + '@vitest/browser': 4.1.2(vite@6.3.5(@types/node@22.15.18))(vitest@4.1.2) + + '@vitest/expect@3.2.4': + dependencies: + '@types/chai': 5.2.3 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.3.3 + tinyrainbow: 2.0.0 + + '@vitest/expect@4.1.2': + dependencies: + '@standard-schema/spec': 1.1.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.1.2 + '@vitest/utils': 4.1.2 + chai: 6.2.2 + tinyrainbow: 3.1.0 + + '@vitest/mocker@4.1.2(vite@6.3.5(@types/node@22.15.18))': + dependencies: + '@vitest/spy': 4.1.2 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 6.3.5(@types/node@22.15.18) + + '@vitest/pretty-format@3.2.4': + dependencies: + tinyrainbow: 2.0.0 + + '@vitest/pretty-format@4.1.2': + dependencies: + tinyrainbow: 3.1.0 + + '@vitest/runner@4.1.2': + dependencies: + '@vitest/utils': 4.1.2 + pathe: 2.0.3 + + '@vitest/snapshot@4.1.2': + dependencies: + '@vitest/pretty-format': 4.1.2 + '@vitest/utils': 4.1.2 + magic-string: 0.30.21 + pathe: 2.0.3 + + '@vitest/spy@3.2.4': + dependencies: + tinyspy: 4.0.4 + + '@vitest/spy@4.1.2': {} + + '@vitest/utils@3.2.4': + dependencies: + '@vitest/pretty-format': 3.2.4 + loupe: 3.2.1 + tinyrainbow: 2.0.0 + + '@vitest/utils@4.1.2': + dependencies: + '@vitest/pretty-format': 4.1.2 + convert-source-map: 2.0.0 + tinyrainbow: 3.1.0 + '@xobotyi/scrollbar-width@1.9.5': {} accepts@2.0.0: @@ -5111,8 +6298,14 @@ snapshots: dependencies: acorn: 8.14.1 + acorn-jsx@5.3.2(acorn@8.16.0): + dependencies: + acorn: 8.16.0 + acorn@8.14.1: {} + acorn@8.16.0: {} + ajv-draft-04@1.0.0(ajv@8.17.1): optionalDependencies: ajv: 8.17.1 @@ -5135,12 +6328,22 @@ snapshots: json-schema-traverse: 1.0.0 require-from-string: 2.0.2 + ansi-regex@5.0.1: {} + + ansi-regex@6.2.2: {} + ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 + ansi-styles@5.2.0: {} + argparse@2.0.1: {} + aria-query@5.3.0: + dependencies: + dequal: 2.0.3 + aria-query@5.3.2: {} array-buffer-byte-length@1.0.2: @@ -5193,8 +6396,20 @@ snapshots: get-intrinsic: 1.3.0 is-array-buffer: 3.0.5 + assertion-error@2.0.1: {} + ast-types-flow@0.0.8: {} + ast-types@0.16.1: + dependencies: + tslib: 2.8.1 + + ast-v8-to-istanbul@1.0.0: + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + estree-walker: 3.0.3 + js-tokens: 10.0.0 + astring@1.9.0: {} async-function@1.0.0: {} @@ -5236,6 +6451,8 @@ snapshots: balanced-match@1.0.2: {} + balanced-match@4.0.4: {} + bcp-47-match@2.0.3: {} body-parser@2.2.0: @@ -5263,6 +6480,10 @@ snapshots: dependencies: balanced-match: 1.0.2 + brace-expansion@5.0.5: + dependencies: + balanced-match: 4.0.4 + braces@3.0.3: dependencies: fill-range: 7.1.1 @@ -5274,6 +6495,10 @@ snapshots: node-releases: 2.0.19 update-browserslist-db: 1.1.3(browserslist@4.24.5) + bundle-name@4.1.0: + dependencies: + run-applescript: 7.1.0 + bytes@3.1.2: {} call-bind-apply-helpers@1.0.2: @@ -5301,6 +6526,16 @@ snapshots: ccount@2.0.1: {} + chai@5.3.3: + dependencies: + assertion-error: 2.0.1 + check-error: 2.1.3 + deep-eql: 5.0.2 + loupe: 3.2.1 + pathval: 2.0.1 + + chai@6.2.2: {} + chalk@4.1.2: dependencies: ansi-styles: 4.3.0 @@ -5314,6 +6549,10 @@ snapshots: character-reference-invalid@2.0.1: {} + check-error@2.1.3: {} + + chromatic@13.3.5: {} + classnames@2.3.1: {} clsx@1.2.1: {} @@ -5416,6 +6655,8 @@ snapshots: mdn-data: 2.0.14 source-map: 0.6.1 + css.escape@1.5.1: {} + csstype@3.1.3: {} damerau-levenshtein@1.0.8: {} @@ -5450,16 +6691,27 @@ snapshots: dependencies: character-entities: 2.0.2 + deep-eql@5.0.2: {} + deep-is@0.1.4: {} deepmerge@4.3.1: {} + default-browser-id@5.0.1: {} + + default-browser@5.5.0: + dependencies: + bundle-name: 4.1.0 + default-browser-id: 5.0.1 + define-data-property@1.1.4: dependencies: es-define-property: 1.0.1 es-errors: 1.3.0 gopd: 1.2.0 + define-lazy-prop@3.0.0: {} + define-properties@1.2.1: dependencies: define-data-property: 1.1.4 @@ -5486,6 +6738,14 @@ snapshots: dependencies: esutils: 2.0.3 + doctrine@3.0.0: + dependencies: + esutils: 2.0.3 + + dom-accessibility-api@0.5.16: {} + + dom-accessibility-api@0.6.3: {} + dom-helpers@5.2.1: dependencies: '@babel/runtime': 7.28.2 @@ -5510,6 +6770,8 @@ snapshots: emoji-regex@9.2.2: {} + empathic@2.0.0: {} + encodeurl@2.0.0: {} entities@4.5.0: {} @@ -5582,6 +6844,8 @@ snapshots: es-errors@1.3.0: {} + es-module-lexer@2.0.0: {} + es-object-atoms@1.1.1: dependencies: es-errors: 1.3.0 @@ -5893,6 +7157,8 @@ snapshots: acorn-jsx: 5.3.2(acorn@8.14.1) eslint-visitor-keys: 4.2.0 + esprima@4.0.1: {} + esquery@1.6.0: dependencies: estraverse: 5.3.0 @@ -5948,6 +7214,8 @@ snapshots: dependencies: eventsource-parser: 3.0.2 + expect-type@1.3.0: {} + express-rate-limit@7.5.0(express@5.1.0): dependencies: express: 5.1.0 @@ -6016,6 +7284,10 @@ snapshots: optionalDependencies: picomatch: 4.0.2 + fdir@6.5.0(picomatch@4.0.4): + optionalDependencies: + picomatch: 4.0.4 + file-entry-cache@8.0.0: dependencies: flat-cache: 4.0.1 @@ -6028,6 +7300,8 @@ snapshots: strip-outer: 1.0.1 trim-repeated: 1.0.0 + filesize@10.1.6: {} + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 @@ -6099,6 +7373,9 @@ snapshots: jsonfile: 6.1.0 universalify: 2.0.1 + fsevents@2.3.2: + optional: true + fsevents@2.3.3: optional: true @@ -6165,6 +7442,12 @@ snapshots: dependencies: is-glob: 4.0.3 + glob@13.0.6: + dependencies: + minimatch: 10.2.5 + minipass: 7.1.3 + path-scurry: 2.0.2 + globals@11.12.0: {} globals@14.0.0: {} @@ -6384,6 +7667,8 @@ snapshots: dependencies: react-is: 16.13.1 + html-escaper@2.0.2: {} + html-url-attributes@3.0.1: {} html-void-elements@3.0.0: {} @@ -6415,6 +7700,8 @@ snapshots: imurmurhash@0.1.4: {} + indent-string@4.0.0: {} + inherits@2.0.4: {} inline-style-parser@0.2.4: {} @@ -6488,6 +7775,8 @@ snapshots: is-decimal@2.0.1: {} + is-docker@3.0.0: {} + is-extglob@2.1.1: {} is-finalizationregistry@1.1.1: @@ -6507,6 +7796,10 @@ snapshots: is-hexadecimal@2.0.1: {} + is-inside-container@1.0.0: + dependencies: + is-docker: 3.0.0 + is-map@2.0.3: {} is-module@1.0.0: {} @@ -6567,12 +7860,31 @@ snapshots: call-bound: 1.0.4 get-intrinsic: 1.3.0 + is-wsl@3.1.1: + dependencies: + is-inside-container: 1.0.0 + isarray@2.0.5: {} isexe@2.0.0: {} + istanbul-lib-coverage@3.2.2: {} + + istanbul-lib-report@3.0.1: + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + + istanbul-reports@3.2.0: + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + js-cookie@2.2.1: {} + js-tokens@10.0.0: {} + js-tokens@4.0.0: {} js-yaml@4.1.0: @@ -6663,24 +7975,44 @@ snapshots: lottie-web@5.13.0: {} + loupe@3.2.1: {} + lower-case@2.0.2: dependencies: tslib: 2.8.1 + lru-cache@11.2.7: {} + lru-cache@5.1.1: dependencies: yallist: 3.1.1 luxon@3.7.1: {} + lz-string@1.5.0: {} + magic-string@0.30.17: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + magicast@0.5.2: + dependencies: + '@babel/parser': 7.29.2 + '@babel/types': 7.29.0 + source-map-js: 1.2.1 + make-dir@3.1.0: dependencies: semver: 6.3.1 + make-dir@4.0.0: + dependencies: + semver: 7.7.2 + markdown-extensions@2.0.0: {} markdown-table@3.0.4: {} @@ -7143,6 +8475,12 @@ snapshots: dependencies: mime-db: 1.54.0 + min-indent@1.0.1: {} + + minimatch@10.2.5: + dependencies: + brace-expansion: 5.0.5 + minimatch@3.1.2: dependencies: brace-expansion: 1.1.11 @@ -7153,6 +8491,10 @@ snapshots: minimist@1.2.8: {} + minipass@7.1.3: {} + + mrmime@2.0.1: {} + ms@2.1.3: {} mui-mdx-components@0.5.0(@mdx-js/mdx@3.1.0(acorn@8.14.1))(@mui/material@7.1.0(@emotion/react@11.14.0(@types/react@19.1.4)(react@19.1.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.4)(react@19.1.0))(@types/react@19.1.4)(react@19.1.0))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0): @@ -7240,6 +8582,8 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.1.1 + obug@2.1.1: {} + on-finished@2.4.1: dependencies: ee-first: 1.1.1 @@ -7248,6 +8592,13 @@ snapshots: dependencies: wrappy: 1.0.2 + open@10.2.0: + dependencies: + default-browser: 5.5.0 + define-lazy-prop: 3.0.0 + is-inside-container: 1.0.0 + wsl-utils: 0.1.0 + optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -7316,22 +8667,43 @@ snapshots: path-parse@1.0.7: {} + path-scurry@2.0.2: + dependencies: + lru-cache: 11.2.7 + minipass: 7.1.3 + path-to-regexp@8.2.0: {} path-type@4.0.0: {} + pathe@2.0.3: {} + + pathval@2.0.1: {} + picocolors@1.1.1: {} picomatch@2.3.1: {} picomatch@4.0.2: {} + picomatch@4.0.4: {} + pkce-challenge@5.0.0: {} pkg-dir@4.2.0: dependencies: find-up: 4.1.0 + playwright-core@1.58.2: {} + + playwright@1.58.2: + dependencies: + playwright-core: 1.58.2 + optionalDependencies: + fsevents: 2.3.2 + + pngjs@7.0.0: {} + possible-typed-array-names@1.1.0: {} postcss@8.5.3: @@ -7348,6 +8720,12 @@ snapshots: prettier@3.5.3: {} + pretty-format@27.5.1: + dependencies: + ansi-regex: 5.0.1 + ansi-styles: 5.2.0 + react-is: 17.0.2 + prop-types@15.8.1: dependencies: loose-envify: 1.4.0 @@ -7387,6 +8765,25 @@ snapshots: react: 19.1.0 tween-functions: 1.2.0 + react-docgen-typescript@2.4.0(typescript@5.7.3): + dependencies: + typescript: 5.7.3 + + react-docgen@8.0.3: + dependencies: + '@babel/core': 7.29.0 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + '@types/babel__core': 7.20.5 + '@types/babel__traverse': 7.20.7 + '@types/doctrine': 0.0.9 + '@types/resolve': 1.20.2 + doctrine: 3.0.0 + resolve: 1.22.10 + strip-indent: 4.1.1 + transitivePeerDependencies: + - supports-color + react-dom@19.1.0(react@19.1.0): dependencies: react: 19.1.0 @@ -7403,6 +8800,8 @@ snapshots: react-is@16.13.1: {} + react-is@17.0.2: {} + react-is@18.3.1: {} react-is@19.1.0: {} @@ -7491,6 +8890,14 @@ snapshots: react@19.1.0: {} + recast@0.23.11: + dependencies: + ast-types: 0.16.1 + esprima: 4.0.1 + source-map: 0.6.1 + tiny-invariant: 1.3.3 + tslib: 2.8.1 + recma-build-jsx@1.0.0: dependencies: '@types/estree': 1.0.7 @@ -7507,6 +8914,16 @@ snapshots: transitivePeerDependencies: - acorn + recma-jsx@1.0.0(acorn@8.16.0): + dependencies: + acorn-jsx: 5.3.2(acorn@8.16.0) + estree-util-to-js: 2.0.0 + recma-parse: 1.0.0 + recma-stringify: 1.0.0 + unified: 11.0.5 + transitivePeerDependencies: + - acorn + recma-parse@1.0.0: dependencies: '@types/estree': 1.0.7 @@ -7521,6 +8938,11 @@ snapshots: unified: 11.0.5 vfile: 6.0.3 + redent@3.0.0: + dependencies: + indent-string: 4.0.0 + strip-indent: 3.0.0 + reflect.getprototypeof@1.0.10: dependencies: call-bind: 1.0.8 @@ -7757,6 +9179,8 @@ snapshots: dependencies: '@babel/runtime': 7.27.1 + run-applescript@7.1.0: {} + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 @@ -7792,6 +9216,8 @@ snapshots: semver@7.7.2: {} + semver@7.7.4: {} + send@1.2.0: dependencies: debug: 4.4.1 @@ -7879,6 +9305,14 @@ snapshots: side-channel-map: 1.0.1 side-channel-weakmap: 1.0.2 + siginfo@2.0.0: {} + + sirv@3.0.2: + dependencies: + '@polka/url': 1.0.0-next.29 + mrmime: 2.0.1 + totalist: 3.0.1 + slash@3.0.0: {} snake-case@3.0.4: @@ -7906,6 +9340,8 @@ snapshots: dependencies: stackframe: 1.3.4 + stackback@0.0.2: {} + stackframe@1.3.4: {} stacktrace-gps@3.1.2: @@ -7921,6 +9357,31 @@ snapshots: statuses@2.0.1: {} + std-env@4.0.0: {} + + storybook@10.3.3(@testing-library/dom@10.4.1)(prettier@3.5.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + dependencies: + '@storybook/global': 5.0.0 + '@storybook/icons': 2.0.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@testing-library/jest-dom': 6.9.1 + '@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.1) + '@vitest/expect': 3.2.4 + '@vitest/spy': 3.2.4 + esbuild: 0.25.4 + open: 10.2.0 + recast: 0.23.11 + semver: 7.7.4 + use-sync-external-store: 1.6.0(react@19.1.0) + ws: 8.20.0 + optionalDependencies: + prettier: 3.5.3 + transitivePeerDependencies: + - '@testing-library/dom' + - bufferutil + - react + - react-dom + - utf-8-validate + string.prototype.includes@2.0.1: dependencies: call-bind: 1.0.8 @@ -7955,8 +9416,18 @@ snapshots: character-entities-html4: 2.1.0 character-entities-legacy: 3.0.0 + strip-ansi@7.2.0: + dependencies: + ansi-regex: 6.2.2 + strip-bom@3.0.0: {} + strip-indent@3.0.0: + dependencies: + min-indent: 1.0.1 + + strip-indent@4.1.1: {} + strip-json-comments@3.1.1: {} strip-outer@1.0.1: @@ -7992,16 +9463,28 @@ snapshots: tiny-invariant@1.2.0: {} - tinyglobby@0.2.13: - dependencies: - fdir: 6.4.4(picomatch@4.0.2) - picomatch: 4.0.2 + tiny-invariant@1.3.3: {} + + tinybench@2.9.0: {} + + tinyexec@1.0.4: {} tinyglobby@0.2.14: dependencies: fdir: 6.4.4(picomatch@4.0.2) picomatch: 4.0.2 + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + + tinyrainbow@2.0.0: {} + + tinyrainbow@3.1.0: {} + + tinyspy@4.0.4: {} + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 @@ -8010,6 +9493,8 @@ snapshots: toidentifier@1.0.1: {} + totalist@3.0.1: {} + trim-lines@3.0.1: {} trim-repeated@1.0.0: @@ -8024,6 +9509,8 @@ snapshots: dependencies: typescript: 5.7.3 + ts-dedent@2.2.0: {} + ts-easing@0.2.0: {} tsconfig-paths@3.15.0: @@ -8033,6 +9520,12 @@ snapshots: minimist: 1.2.8 strip-bom: 3.0.0 + tsconfig-paths@4.2.0: + dependencies: + json5: 2.2.3 + minimist: 1.2.8 + strip-bom: 3.0.0 + tslib@2.8.1: {} tween-functions@1.2.0: {} @@ -8164,6 +9657,13 @@ snapshots: unpipe@1.0.0: {} + unplugin@2.3.11: + dependencies: + '@jridgewell/remapping': 2.3.5 + acorn: 8.16.0 + picomatch: 4.0.4 + webpack-virtual-modules: 0.6.2 + unrs-resolver@1.7.8: dependencies: napi-postinstall: 0.2.4 @@ -8196,6 +9696,10 @@ snapshots: dependencies: punycode: 2.3.1 + use-sync-external-store@1.6.0(react@19.1.0): + dependencies: + react: 19.1.0 + validate.io-array@1.0.6: {} validate.io-function@1.0.2: {} @@ -8249,6 +9753,15 @@ snapshots: unified: 9.2.2 vite: 6.3.5(@types/node@22.15.18) + vite-plugin-mdx@3.6.1(@mdx-js/mdx@3.1.0(acorn@8.16.0))(vite@6.3.5(@types/node@22.15.18)): + dependencies: + '@alloc/quick-lru': 5.2.0 + '@mdx-js/mdx': 3.1.0(acorn@8.16.0) + esbuild: 0.13.8 + resolve: 1.22.10 + unified: 9.2.2 + vite: 6.3.5(@types/node@22.15.18) + vite-plugin-mkcert@1.17.8(vite@6.3.5(@types/node@22.15.18)): dependencies: axios: 1.9.0(debug@4.4.1) @@ -8276,13 +9789,43 @@ snapshots: picomatch: 4.0.2 postcss: 8.5.3 rollup: 4.40.2 - tinyglobby: 0.2.13 + tinyglobby: 0.2.14 optionalDependencies: '@types/node': 22.15.18 fsevents: 2.3.3 + vitest@4.1.2(@types/node@22.15.18)(@vitest/browser-playwright@4.1.2)(vite@6.3.5(@types/node@22.15.18)): + dependencies: + '@vitest/expect': 4.1.2 + '@vitest/mocker': 4.1.2(vite@6.3.5(@types/node@22.15.18)) + '@vitest/pretty-format': 4.1.2 + '@vitest/runner': 4.1.2 + '@vitest/snapshot': 4.1.2 + '@vitest/spy': 4.1.2 + '@vitest/utils': 4.1.2 + es-module-lexer: 2.0.0 + expect-type: 1.3.0 + magic-string: 0.30.21 + obug: 2.1.1 + pathe: 2.0.3 + picomatch: 4.0.4 + std-env: 4.0.0 + tinybench: 2.9.0 + tinyexec: 1.0.4 + tinyglobby: 0.2.15 + tinyrainbow: 3.1.0 + vite: 6.3.5(@types/node@22.15.18) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 22.15.18 + '@vitest/browser-playwright': 4.1.2(playwright@1.58.2)(vite@6.3.5(@types/node@22.15.18))(vitest@4.1.2) + transitivePeerDependencies: + - msw + web-namespaces@2.0.1: {} + webpack-virtual-modules@0.6.2: {} + which-boxed-primitive@1.1.1: dependencies: is-bigint: 1.1.0 @@ -8328,10 +9871,21 @@ snapshots: dependencies: isexe: 2.0.0 + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + word-wrap@1.2.5: {} wrappy@1.0.2: {} + ws@8.20.0: {} + + wsl-utils@0.1.0: + dependencies: + is-wsl: 3.1.1 + yallist@3.1.1: {} yaml@1.10.2: {} diff --git a/stories/BaseMDXComponents.stories.tsx b/stories/BaseMDXComponents.stories.tsx new file mode 100644 index 00000000..ff74ff9d --- /dev/null +++ b/stories/BaseMDXComponents.stories.tsx @@ -0,0 +1,232 @@ +import * as Common from "@frontend/common"; +import { Alert, Box, Chip, FormControlLabel, Stack, Switch, TextField, Typography } from "@mui/material"; +import type { Meta, StoryObj } from "@storybook/react-vite"; +import * as React from "react"; + +import { withCommonContext } from "./decorators/withCommonContext"; + +type ComponentEntry = { + key: string; + Component: unknown; +}; + +const entries: ComponentEntry[] = Object.entries(Common.BaseMDXComponents).map(([key, Component]) => ({ key, Component })); + +function isReactComponent(x: unknown): x is React.ElementType { + return typeof x === "function" || typeof x === "object"; +} + +function makePreview(key: string): React.ReactNode { + // 공통 컴포넌트는 실제 동작/필수 props가 각기 달라서, “안전하게 렌더 가능한 샘플”만 제공합니다. + // (backend/kakao SDK/유효한 lottie json 등이 필요한 컴포넌트는 안내만 표시) + switch (key) { + case "Common__Components__MDX__Confetti": + return ; + case "Common__Components__MDX__PrimaryStyledDetails": + return ( + + Details body + + ); + case "Common__Components__MDX__SecondaryStyledDetails": + return ( + + Details body + + ); + case "Common__Components__MDX__FAQAccordion": + return ( + + ); + case "Common__Components__MDX__FullWidthStyledButton": + return ( + + 예시 버튼 + + ); + case "Common__Components__MDX__OneDetailsOpener": + return ( + + 첫 번째 내용 + , + + 두 번째 내용 + , + ]} + /> + ); + case "Common__Components__MDX__Map": + return ( + + ); + case "Common__Components__Session__List": + return `SessionList`는 백엔드 API 호출이 필요해서 Storybook 기본 환경에서는 바로 미리보기를 제공하지 않습니다.; + case "Common__Components__Session__TimeTable": + return ( + `SessionTimeTable`는 백엔드 API 호출이 필요해서 Storybook 기본 환경에서는 바로 미리보기를 제공하지 않습니다. + ); + case "Common__Components__Lottie": + case "Common__Components__NetworkLottie": + return ( + Lottie 컴포넌트는 유효한 lottie JSON 데이터(또는 URL)가 필요합니다. 스토리에서 args로 주입해 테스트해주세요. + ); + default: + // MUI 컴포넌트들은 “필수 props/컨텍스트”가 다양한 관계로, 개별 샘플 렌더는 안전한 것만 제공합니다. + if (key.startsWith("Mui__material__")) { + if (key === "Mui__material__Chip") return ; + if (key === "Mui__material__Alert") return Alert; + if (key === "Mui__material__TextField") return ; + return ( + + 이 컴포넌트는 필수 props가 있을 수 있어 자동 프리뷰를 생략했습니다. (key: {key}) + + ); + } + return ( + + 프리뷰가 준비되지 않았습니다. (key: {key}) + + ); + } +} + +const meta = { + title: "MDX/BaseMDXComponents", + decorators: [withCommonContext], + parameters: { + layout: "padded", + }, + argTypes: { + search: { control: "text" }, + selectedKey: { control: "select", options: entries.map((e) => e.key) }, + showOnlyCustom: { control: "boolean" }, + }, + args: { + search: "", + selectedKey: "Common__Components__MDX__Confetti", + showOnlyCustom: true, + }, +} satisfies Meta<{ + search: string; + selectedKey: string; + showOnlyCustom: boolean; +}>; + +export default meta; +type Story = StoryObj; + +const CatalogView: React.FC<{ search: string; selectedKey: string; showOnlyCustom: boolean }> = (args) => { + const [search, setSearch] = React.useState(args.search); + const [selectedKey, setSelectedKey] = React.useState(args.selectedKey); + const [showOnlyCustom, setShowOnlyCustom] = React.useState(args.showOnlyCustom); + + const filtered = React.useMemo(() => { + const s = search.trim().toLowerCase(); + return entries + .filter((e) => (showOnlyCustom ? !e.key.startsWith("Mui__material__") : true)) + .filter((e) => (s ? e.key.toLowerCase().includes(s) : true)); + }, [search, showOnlyCustom]); + + React.useEffect(() => { + // controls 패널에서 args가 바뀐 경우 동기화 + setSearch(args.search); + setSelectedKey(args.selectedKey); + setShowOnlyCustom(args.showOnlyCustom); + }, [args.search, args.selectedKey, args.showOnlyCustom]); + + const selected = entries.find((e) => e.key === selectedKey); + const selectedComponent = selected?.Component; + + return ( + + BaseMDXComponents 카탈로그 + + `packages/common/src/consts/mdx_components.ts`의 `BaseMDXComponents`를 기준으로 목록을 만들었습니다. + + + + setSearch(e.target.value)} fullWidth /> + setShowOnlyCustom(e.target.checked)} />} + label="커스텀만 보기(Common__/...)" + /> + + + + + + {filtered.map((e) => ( + setSelectedKey(e.key)} + color={e.key === selectedKey ? "primary" : "default"} + variant={e.key === selectedKey ? "filled" : "outlined"} + sx={{ justifyContent: "flex-start" }} + /> + ))} + {filtered.length === 0 && ( + + 검색 결과가 없습니다. + + )} + + + + + + Selected key + + + {selectedKey} + + + {!selectedComponent ? ( + 선택된 key에 해당하는 컴포넌트를 찾지 못했습니다. + ) : !isReactComponent(selectedComponent) ? ( + React 컴포넌트 타입으로 판단되지 않아 렌더를 생략했습니다. + ) : ( + {makePreview(selectedKey)} + )} + + + + ); +}; + +export const Catalog: Story = { + render: (rawArgs) => { + const args = rawArgs as { search: string; selectedKey: string; showOnlyCustom: boolean }; + return ; + }, +}; diff --git a/stories/Confetti.stories.tsx b/stories/Confetti.stories.tsx new file mode 100644 index 00000000..1eae2a36 --- /dev/null +++ b/stories/Confetti.stories.tsx @@ -0,0 +1,18 @@ +import type { Meta, StoryObj } from "@storybook/react-vite"; + +import * as Common from "@frontend/common"; + +const Confetti = Common.Components.MDX.Confetti; + +const meta = { + title: "MDX Components/Common/Confetti", + component: Confetti, + parameters: { + layout: "fullscreen", + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = {}; diff --git a/stories/FAQAccordion.stories.tsx b/stories/FAQAccordion.stories.tsx new file mode 100644 index 00000000..bdd5adbd --- /dev/null +++ b/stories/FAQAccordion.stories.tsx @@ -0,0 +1,52 @@ +import type { Meta, StoryObj } from "@storybook/react-vite"; + +import * as Common from "@frontend/common"; + +const FAQAccordion = Common.Components.MDX.FAQAccordion; + +const faqItems = [ + { + id: "1", + question: "파이콘 한국이 무엇인가요?", + answer: + "파이콘 한국은 파이썬 사용자들이 모여 지식과 경험을 공유하는 비영리 개발자 컨퍼런스입니다. 매년 다양한 발표와 워크숍이 진행됩니다.", + }, + { + id: "2", + question: "티켓은 어떻게 구매할 수 있나요?", + answer: + "티켓은 파이콘 한국 공식 웹사이트에서 구매하실 수 있습니다. 얼리버드, 일반, 후원 티켓 등 다양한 종류가 있습니다.", + }, + { + id: "3", + question: "발표 신청은 어떻게 하나요?", + answer: + "CFP(Call For Proposals) 기간에 공식 웹사이트를 통해 신청하실 수 있습니다. 발표 주제, 내용, 발표자 정보를 제출하시면 됩니다.", + }, +]; + +const meta = { + title: "MDX Components/Common/FAQAccordion", + component: FAQAccordion, + parameters: { + layout: "padded", + }, + argTypes: { + items: { control: "object" }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + items: faqItems, + }, +}; + +export const SingleItem: Story = { + args: { + items: [faqItems[0]], + }, +}; diff --git a/stories/Map.stories.tsx b/stories/Map.stories.tsx new file mode 100644 index 00000000..f9d766a4 --- /dev/null +++ b/stories/Map.stories.tsx @@ -0,0 +1,37 @@ +import type { Meta, StoryObj } from "@storybook/react-vite"; + +import * as Common from "@frontend/common"; + +import { withCommonContext } from "./decorators/withCommonContext"; + +const Map = Common.Components.MDX.Map; + +const meta = { + title: "MDX Components/Common/Map", + component: Map, + decorators: [withCommonContext], + parameters: { + layout: "padded", + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +// 동국대학교 신공학관 (PyCon KR 2025 행사장) +export const Default: Story = { + args: { + geo: { lat: 37.5587, lng: 127.0004 }, + placeName: { + ko: "동국대학교 신공학관", + en: "New Engineering Building, Dongguk University", + }, + placeCode: { + kakao: "27296493", + naver: "FvuDaEWgtV", + google: "https://maps.app.goo.gl/example", + }, + googleMapIframeSrc: + "https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d3162.5!2d127.0004!3d37.5587!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x0%3A0x0!2zMzfCsDMzJzMxLjMiTiAxMjfCsDAwJzAxLjQiRQ!5e0!3m2!1sko!2skr!4v1234567890", + }, +}; diff --git a/stories/MobileAccordion.stories.tsx b/stories/MobileAccordion.stories.tsx new file mode 100644 index 00000000..6f257648 --- /dev/null +++ b/stories/MobileAccordion.stories.tsx @@ -0,0 +1,39 @@ +import type { Meta, StoryObj } from "@storybook/react-vite"; + +import * as Common from "@frontend/common"; + +import { withCommonContext } from "./decorators/withCommonContext"; + +const MobileAccordion = Common.Components.MDX.MobileAccordion; + +const meta = { + title: "MDX Components/Common/MobileAccordion", + component: MobileAccordion, + decorators: [withCommonContext], + parameters: { + layout: "centered", + viewport: { defaultViewport: "mobile1" }, + }, + argTypes: { + eventDates: { control: "text" }, + hostLogoBig: { control: "text" }, + hostLogoSmall: { control: "text" }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = {}; + +export const WithCustomProps: Story = { + args: { + eventDates: "AUG 15 - 17", + venue: { + ko: "서울특별시 중구 필동로 1길 30 동국대학교 신공학관", + en: "New Engineering Building, Dongguk University", + }, + hostLogoBig: "https://placehold.co/300x100/259299/white?text=PyCon+Korea", + hostLogoSmall: "https://placehold.co/30x30/259299/white?text=PK", + }, +}; diff --git a/stories/MobileCover.stories.tsx b/stories/MobileCover.stories.tsx new file mode 100644 index 00000000..e80fadeb --- /dev/null +++ b/stories/MobileCover.stories.tsx @@ -0,0 +1,33 @@ +import type { Meta, StoryObj } from "@storybook/react-vite"; + +import * as Common from "@frontend/common"; + +import { withCommonContext } from "./decorators/withCommonContext"; + +const MobileCover = Common.Components.MDX.MobileCover; + +const meta = { + title: "MDX Components/Common/MobileCover", + component: MobileCover, + decorators: [withCommonContext], + parameters: { + layout: "fullscreen", + viewport: { defaultViewport: "mobile1" }, + }, + argTypes: { + mobileCoverImage: { control: "text" }, + mobileCoverTitle: { control: "text" }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = {}; + +export const WithCustomAssets: Story = { + args: { + mobileCoverImage: "https://placehold.co/400x800/259299/white?text=PyCon+Cover", + mobileCoverTitle: "https://placehold.co/200x60/E17101/white?text=PyCon+KR", + }, +}; diff --git a/stories/OneDetailsOpener.stories.tsx b/stories/OneDetailsOpener.stories.tsx new file mode 100644 index 00000000..2ae52304 --- /dev/null +++ b/stories/OneDetailsOpener.stories.tsx @@ -0,0 +1,42 @@ +import { Accordion, AccordionDetails, AccordionSummary, Typography } from "@mui/material"; +import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; +import type { Meta, StoryObj } from "@storybook/react-vite"; + +import * as Common from "@frontend/common"; + +const OneDetailsOpener = Common.Components.MDX.OneDetailsOpener; + +const meta = { + title: "MDX Components/Common/OneDetailsOpener", + component: OneDetailsOpener, + parameters: { + layout: "padded", + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +const accordionItems = [ + { id: "1", title: "파이썬이란?", content: "파이썬은 간결하고 읽기 쉬운 문법을 가진 프로그래밍 언어입니다." }, + { id: "2", title: "파이콘이란?", content: "파이콘은 파이썬 사용자들이 모이는 커뮤니티 컨퍼런스입니다." }, + { id: "3", title: "발표 신청 방법", content: "공식 웹사이트에서 CFP 기간에 신청하실 수 있습니다." }, +]; + +export const Default: Story = { + args: { children: [] as never }, + render: () => ( + + {accordionItems.map((item) => ( + + }> + {item.title} + + + {item.content} + + + ))} + + ), +}; diff --git a/stories/SessionList.stories.tsx b/stories/SessionList.stories.tsx new file mode 100644 index 00000000..924abc28 --- /dev/null +++ b/stories/SessionList.stories.tsx @@ -0,0 +1,37 @@ +import type { Meta, StoryObj } from "@storybook/react-vite"; + +import * as Common from "@frontend/common"; + +import { withCommonContext } from "./decorators/withCommonContext"; +import { withQueryClient } from "./decorators/withQueryClient"; +import { mockSessions } from "./fixtures/sessions"; + +const SessionList = Common.Components.MDX.SessionList; + +const meta = { + title: "MDX Components/Common/SessionList", + component: SessionList, + decorators: [withQueryClient, withCommonContext], + parameters: { + layout: "padded", + mockSessions, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = {}; + +export const WithLink: Story = { + args: { enableLink: true }, +}; + +export const FilteredByType: Story = { + args: { types: "keynote" }, + parameters: { + mockSessions: mockSessions.filter((s) => + s.categories.some((c: { id: string; name: string }) => c.name === "Keynote") + ), + }, +}; diff --git a/stories/SessionTimetable.stories.tsx b/stories/SessionTimetable.stories.tsx new file mode 100644 index 00000000..674594cf --- /dev/null +++ b/stories/SessionTimetable.stories.tsx @@ -0,0 +1,24 @@ +import type { Meta, StoryObj } from "@storybook/react-vite"; + +import * as Common from "@frontend/common"; + +import { withCommonContext } from "./decorators/withCommonContext"; +import { withQueryClient } from "./decorators/withQueryClient"; +import { mockSessions } from "./fixtures/sessions"; + +const SessionTimeTable = Common.Components.MDX.SessionTimeTable; + +const meta = { + title: "MDX Components/Common/SessionTimetable", + component: SessionTimeTable, + decorators: [withQueryClient, withCommonContext], + parameters: { + layout: "fullscreen", + mockSessions, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = {}; diff --git a/stories/ShopCartStatus.stories.tsx b/stories/ShopCartStatus.stories.tsx new file mode 100644 index 00000000..20d34d42 --- /dev/null +++ b/stories/ShopCartStatus.stories.tsx @@ -0,0 +1,48 @@ +import * as Shop from "@frontend/shop"; +import type { Meta, StoryObj } from "@storybook/react-vite"; +import { MemoryRouter } from "react-router-dom"; + +import { withShopContext } from "./decorators/withShopContext"; +import { mockUserSignedIn, mockUserNotSignedIn, mockCart, mockEmptyCart } from "./fixtures/shop"; + +const CartStatus = Shop.Components.Features.CartStatus; + +const meta = { + title: "MDX Components/Shop/Feature/CartStatus", + component: CartStatus, + decorators: [ + (Story) => ( + + + + ), + withShopContext, + ], + parameters: { + layout: "padded", + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const WithItems: Story = { + parameters: { + mockUserStatus: mockUserSignedIn, + mockCart, + }, +}; + +export const EmptyCart: Story = { + parameters: { + mockUserStatus: mockUserSignedIn, + mockCart: mockEmptyCart, + }, +}; + +export const NotSignedIn: Story = { + parameters: { + mockUserStatus: mockUserNotSignedIn, + mockCart, + }, +}; diff --git a/stories/ShopCommon.stories.tsx b/stories/ShopCommon.stories.tsx new file mode 100644 index 00000000..7f511ac9 --- /dev/null +++ b/stories/ShopCommon.stories.tsx @@ -0,0 +1,32 @@ +import * as Shop from "@frontend/shop"; +import type { Meta, StoryObj } from "@storybook/react-vite"; + +import { withShopContext } from "./decorators/withShopContext"; +import { mockUserSignedIn } from "./fixtures/shop"; + +const PriceDisplay = Shop.Components.Common.PriceDisplay; + +const meta = { + title: "MDX Components/Shop/Common/PriceDisplay", + component: PriceDisplay, + decorators: [withShopContext], + parameters: { + layout: "padded", + mockUserStatus: mockUserSignedIn, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { price: 50000 }, +}; + +export const WithLabel: Story = { + args: { price: 100000, label: "결제 금액" }, +}; + +export const ZeroPrice: Story = { + args: { price: 0, label: "무료" }, +}; diff --git a/stories/ShopOrderList.stories.tsx b/stories/ShopOrderList.stories.tsx new file mode 100644 index 00000000..5498887c --- /dev/null +++ b/stories/ShopOrderList.stories.tsx @@ -0,0 +1,40 @@ +import * as Shop from "@frontend/shop"; +import type { Meta, StoryObj } from "@storybook/react-vite"; + +import { withShopContext } from "./decorators/withShopContext"; +import { mockUserSignedIn, mockUserNotSignedIn, mockOrders } from "./fixtures/shop"; + +const OrderList = Shop.Components.Features.OrderList; + +const meta = { + title: "MDX Components/Shop/Feature/OrderList", + component: OrderList, + decorators: [withShopContext], + parameters: { + layout: "padded", + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const WithOrders: Story = { + parameters: { + mockUserStatus: mockUserSignedIn, + mockOrders, + }, +}; + +export const EmptyOrders: Story = { + parameters: { + mockUserStatus: mockUserSignedIn, + mockOrders: [], + }, +}; + +export const NotSignedIn: Story = { + parameters: { + mockUserStatus: mockUserNotSignedIn, + mockOrders: [], + }, +}; diff --git a/stories/ShopPatronList.stories.tsx b/stories/ShopPatronList.stories.tsx new file mode 100644 index 00000000..db877034 --- /dev/null +++ b/stories/ShopPatronList.stories.tsx @@ -0,0 +1,30 @@ +import * as Shop from "@frontend/shop"; +import type { Meta, StoryObj } from "@storybook/react-vite"; + +import { withShopContext } from "./decorators/withShopContext"; +import { mockPatrons } from "./fixtures/shop"; + +const PatronList = Shop.Components.Features.PatronList; + +const meta = { + title: "MDX Components/Shop/Feature/PatronList", + component: PatronList, + decorators: [withShopContext], + parameters: { + layout: "padded", + mockPatrons, + mockPatronsYear: 2025, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { year: 2025 }, +}; + +export const Empty: Story = { + parameters: { mockPatrons: [] }, + args: { year: 2025 }, +}; diff --git a/stories/ShopProductImageCardList.stories.tsx b/stories/ShopProductImageCardList.stories.tsx new file mode 100644 index 00000000..28afc445 --- /dev/null +++ b/stories/ShopProductImageCardList.stories.tsx @@ -0,0 +1,35 @@ +import * as Shop from "@frontend/shop"; +import type { Meta, StoryObj } from "@storybook/react-vite"; +import { MemoryRouter } from "react-router-dom"; + +import { withShopContext } from "./decorators/withShopContext"; +import { mockUserSignedIn, mockProducts } from "./fixtures/shop"; + +const ProductImageCardList = Shop.Components.Features.ProductImageCardList; + +const meta = { + title: "MDX Components/Shop/Feature/ProductImageCardList", + component: ProductImageCardList, + decorators: [ + (Story) => ( + + + + ), + withShopContext, + ], + parameters: { + layout: "padded", + mockUserStatus: mockUserSignedIn, + mockProducts, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = {}; + +export const Empty: Story = { + parameters: { mockProducts: [] }, +}; diff --git a/stories/ShopProductList.stories.tsx b/stories/ShopProductList.stories.tsx new file mode 100644 index 00000000..d6365a31 --- /dev/null +++ b/stories/ShopProductList.stories.tsx @@ -0,0 +1,43 @@ +import * as Shop from "@frontend/shop"; +import type { Meta, StoryObj } from "@storybook/react-vite"; +import { MemoryRouter } from "react-router-dom"; + +import { withShopContext } from "./decorators/withShopContext"; +import { mockUserSignedIn, mockUserNotSignedIn, mockProducts } from "./fixtures/shop"; + +const ProductList = Shop.Components.Features.ProductList; + +const meta = { + title: "MDX Components/Shop/Feature/ProductList", + component: ProductList, + decorators: [ + (Story) => ( + + + + ), + withShopContext, + ], + parameters: { + layout: "padded", + mockProducts, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const SignedIn: Story = { + parameters: { mockUserStatus: mockUserSignedIn }, +}; + +export const NotSignedIn: Story = { + parameters: { mockUserStatus: mockUserNotSignedIn }, +}; + +export const Empty: Story = { + parameters: { + mockUserStatus: mockUserSignedIn, + mockProducts: [], + }, +}; diff --git a/stories/ShopSignInGuard.stories.tsx b/stories/ShopSignInGuard.stories.tsx new file mode 100644 index 00000000..1345685d --- /dev/null +++ b/stories/ShopSignInGuard.stories.tsx @@ -0,0 +1,46 @@ +import * as Shop from "@frontend/shop"; +import { Typography } from "@mui/material"; +import type { Meta, StoryObj } from "@storybook/react-vite"; + +import { withShopContext } from "./decorators/withShopContext"; +import { mockUserSignedIn, mockUserNotSignedIn } from "./fixtures/shop"; + +const SignInGuard = Shop.Components.Common.SignInGuard; + +const meta = { + title: "MDX Components/Shop/Common/SignInGuard", + component: SignInGuard, + decorators: [withShopContext], + parameters: { + layout: "padded", + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const SignedIn: Story = { + parameters: { mockUserStatus: mockUserSignedIn }, + args: { + children: 로그인된 사용자에게만 보이는 콘텐츠입니다., + }, +}; + +export const NotSignedIn: Story = { + parameters: { mockUserStatus: mockUserNotSignedIn }, + args: { + children: 이 콘텐츠는 로그인 후 확인할 수 있습니다., + }, +}; + +export const CustomFallback: Story = { + parameters: { mockUserStatus: mockUserNotSignedIn }, + args: { + children: 비밀 콘텐츠, + fallback: ( + + 회원 전용 콘텐츠입니다. 로그인해주세요. + + ), + }, +}; diff --git a/stories/ShopUserIdent.stories.tsx b/stories/ShopUserIdent.stories.tsx new file mode 100644 index 00000000..9f5b767b --- /dev/null +++ b/stories/ShopUserIdent.stories.tsx @@ -0,0 +1,45 @@ +import * as Shop from "@frontend/shop"; +import { Stack, Typography } from "@mui/material"; +import type { Meta, StoryObj } from "@storybook/react-vite"; + +import { withShopContext } from "./decorators/withShopContext"; +import { mockUserSignedIn, mockUserNotSignedIn } from "./fixtures/shop"; + +const UserSignInMethod = Shop.Components.Common.UserSignInMethod; + +const meta = { + title: "MDX Components/Shop/Common/UserSignInMethod", + component: UserSignInMethod, + decorators: [withShopContext], + parameters: { + layout: "padded", + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const SignedInWithGoogle: Story = { + parameters: { mockUserStatus: mockUserSignedIn }, +}; + +export const NotSignedIn: Story = { + parameters: { mockUserStatus: mockUserNotSignedIn }, +}; + +export const WithAccount: Story = { + parameters: { mockUserStatus: mockUserSignedIn }, + render: () => { + const UserSignInAccount = Shop.Components.Common.UserSignInAccount; + return ( + + + 로그인 방법: + + + 계정: + + + ); + }, +}; diff --git a/stories/ShopUserInfo.stories.tsx b/stories/ShopUserInfo.stories.tsx new file mode 100644 index 00000000..e73ca797 --- /dev/null +++ b/stories/ShopUserInfo.stories.tsx @@ -0,0 +1,27 @@ +import * as Shop from "@frontend/shop"; +import type { Meta, StoryObj } from "@storybook/react-vite"; + +import { withShopContext } from "./decorators/withShopContext"; +import { mockUserSignedIn, mockUserNotSignedIn } from "./fixtures/shop"; + +const UserInfo = Shop.Components.Features.UserInfo; + +const meta = { + title: "MDX Components/Shop/Feature/UserInfo", + component: UserInfo, + decorators: [withShopContext], + parameters: { + layout: "padded", + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const SignedIn: Story = { + parameters: { mockUserStatus: mockUserSignedIn }, +}; + +export const NotSignedIn: Story = { + parameters: { mockUserStatus: mockUserNotSignedIn }, +}; diff --git a/stories/StyledDetails.stories.tsx b/stories/StyledDetails.stories.tsx new file mode 100644 index 00000000..e0bd6faa --- /dev/null +++ b/stories/StyledDetails.stories.tsx @@ -0,0 +1,59 @@ +import { Stack, Typography } from "@mui/material"; +import type { Meta, StoryObj } from "@storybook/react-vite"; + +import * as Common from "@frontend/common"; + +const PrimaryStyledDetails = Common.Components.MDX.PrimaryStyledDetails; +const HighlightedStyledDetails = Common.Components.MDX.SecondaryStyledDetails; + +const meta = { + title: "MDX Components/Common/StyledDetails", + component: PrimaryStyledDetails, + parameters: { + layout: "padded", + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Primary: Story = { + args: { + summary: "발표 일정 안내", + children: ( + + 발표는 8월 15일부터 17일까지 3일간 진행됩니다. 각 트랙별 세부 일정은 공식 + 홈페이지에서 확인하실 수 있습니다. + + ), + }, +}; + +export const Highlighted: Story = { + render: (args: Story["args"]) => , + args: { + summary: "중요 공지사항", + children: ( + + 발표 신청 마감일은 6월 30일입니다. 기한 내에 신청해 주시기 바랍니다. + + ), + }, +}; + +export const Multiple: Story = { + args: { children: null as never }, + render: () => ( + + + 파이썬의 기본 문법과 자료구조를 학습합니다. + + + Django와 FastAPI를 사용한 웹 개발 방법을 소개합니다. + + + 파이콘 한국 2025 특별 기조 세션입니다. + + + ), +}; diff --git a/stories/StyledDivider.stories.tsx b/stories/StyledDivider.stories.tsx new file mode 100644 index 00000000..f2188c4b --- /dev/null +++ b/stories/StyledDivider.stories.tsx @@ -0,0 +1,31 @@ +import { Stack, Typography } from "@mui/material"; +import type { Meta, StoryObj } from "@storybook/react-vite"; + +import * as Common from "@frontend/common"; + +const StyledDivider = Common.Components.MDX.StyledDivider; + +const meta = { + title: "MDX Components/Common/StyledDivider", + component: StyledDivider, + parameters: { + layout: "padded", + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = {}; + +export const InContext: Story = { + render: () => ( + + 섹션 1 내용입니다. + + 섹션 2 내용입니다. + + 섹션 3 내용입니다. + + ), +}; diff --git a/stories/StyledFullWidthButton.stories.tsx b/stories/StyledFullWidthButton.stories.tsx new file mode 100644 index 00000000..8fb4b7df --- /dev/null +++ b/stories/StyledFullWidthButton.stories.tsx @@ -0,0 +1,70 @@ +import { Stack } from "@mui/material"; +import type { Meta, StoryObj } from "@storybook/react-vite"; +import { MemoryRouter } from "react-router-dom"; + +import * as Common from "@frontend/common"; + +const StyledFullWidthButton = Common.Components.MDX.StyledFullWidthButton; + +const meta = { + title: "MDX Components/Common/StyledFullWidthButton", + component: StyledFullWidthButton, + decorators: [ + (Story) => ( + + + + ), + ], + parameters: { + layout: "padded", + }, + argTypes: { + transparency: { control: { type: "range", min: 0, max: 100, step: 5 } }, + setBackgroundColor: { control: "boolean" }, + link: { control: "text" }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + children: "발표 신청하기", + transparency: 10, + }, +}; + +export const WithBackground: Story = { + args: { + children: "티켓 구매하기", + transparency: 20, + setBackgroundColor: true, + }, +}; + +export const WithExternalLink: Story = { + args: { + children: "공식 홈페이지 방문", + transparency: 10, + link: "https://pycon.kr", + }, +}; + +export const Sizes: Story = { + args: { transparency: 10 }, + render: () => ( + + + Small + + + Medium (기본값) + + + Large + + + ), +}; diff --git a/stories/SubContentContainer.stories.tsx b/stories/SubContentContainer.stories.tsx new file mode 100644 index 00000000..8e0bb27a --- /dev/null +++ b/stories/SubContentContainer.stories.tsx @@ -0,0 +1,28 @@ +import { Typography } from "@mui/material"; +import type { Meta, StoryObj } from "@storybook/react-vite"; + +import * as Common from "@frontend/common"; + +const SubContentContainer = Common.Components.MDX.SubContentContainer; + +const meta = { + title: "MDX Components/Common/SubContentContainer", + component: SubContentContainer, + parameters: { + layout: "fullscreen", + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + children: ( + + SubContentContainer는 콘텐츠에 일정한 패딩을 적용하는 레이아웃 컴포넌트입니다. + MDX 문서 내에서 서브 섹션을 감싸는 용도로 사용됩니다. + + ), + }, +}; diff --git a/stories/ThemeComponents.stories.ts b/stories/ThemeComponents.stories.ts new file mode 100644 index 00000000..ce42b517 --- /dev/null +++ b/stories/ThemeComponents.stories.ts @@ -0,0 +1,16 @@ +import type { Meta, StoryObj } from "@storybook/react-vite"; + +import { ThemeComponents } from "./ThemeComponents"; + +const meta = { + title: "Theme/Components", + component: ThemeComponents, + parameters: { + layout: "fullscreen", + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = {}; diff --git a/stories/ThemeComponents.tsx b/stories/ThemeComponents.tsx new file mode 100644 index 00000000..d7f849df --- /dev/null +++ b/stories/ThemeComponents.tsx @@ -0,0 +1,136 @@ +import { + Alert, + Box, + Button, + Card, + CardContent, + Divider, + IconButton, + Stack, + Switch, + TextField, + Typography, +} from "@mui/material"; +import * as React from "react"; + +export const ThemeComponents: React.FC = () => ( + +
+ + Typography + + + H4 — 파이콘 한국 2025 + H5 — PyCon Korea 2025 + H6 — 발표 세션 목록 + Subtitle1 — 서브타이틀 + + Body1 — 파이콘 한국은 한국의 파이썬 개발자가 만드는 비영리 개발자 + 대상 컨퍼런스입니다. + + + Body2 — Secondary color text + + + Caption — Disabled color text + + +
+ + + +
+ + Buttons + + + + + + + + + +
+ + + +
+ + Text Fields + + + + + + + +
+ + + +
+ + Cards + + + + + + 세션 카드 + + + 파이썬 프로젝트 구조화 모범 사례와 실전 팁을 공유합니다. + + + + + + + 후원사 + + + 파이콘 한국을 후원해 주셔서 감사합니다. + + + + +
+ + + +
+ + Alerts + + + 정보: 발표 신청이 마감되었습니다. + 성공: 참가 신청이 완료되었습니다. + 경고: 좌석이 얼마 남지 않았습니다. + 오류: 결제에 실패했습니다. + +
+ + + +
+ + Controls + + + + + + + ✦ + + +
+
+); diff --git a/stories/ThemePalette.stories.ts b/stories/ThemePalette.stories.ts new file mode 100644 index 00000000..19f13b56 --- /dev/null +++ b/stories/ThemePalette.stories.ts @@ -0,0 +1,16 @@ +import type { Meta, StoryObj } from "@storybook/react-vite"; + +import { ThemePalette } from "./ThemePalette"; + +const meta = { + title: "Theme/Palette", + component: ThemePalette, + parameters: { + layout: "fullscreen", + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = {}; diff --git a/stories/ThemePalette.tsx b/stories/ThemePalette.tsx new file mode 100644 index 00000000..8ad85a59 --- /dev/null +++ b/stories/ThemePalette.tsx @@ -0,0 +1,164 @@ +import { Box, Chip, Stack, Typography } from "@mui/material"; +import { useTheme } from "@mui/material/styles"; +import * as React from "react"; + +type ColorSwatchProps = { + label: string; + color: string; + textColor?: string; +}; + +const ColorSwatch: React.FC = ({ + label, + color, + textColor = "#fff", +}) => ( + + + {label} + + + {color} + + +); + +type PaletteGroupProps = { + title: string; + colors: { label: string; color: string; textColor?: string }[]; +}; + +const PaletteGroup: React.FC = ({ title, colors }) => ( + + + {title} + + + {colors.map(({ label, color, textColor }) => ( + + ))} + + +); + +export const ThemePalette: React.FC = () => { + const theme = useTheme(); + const p = theme.palette; + + const groups: PaletteGroupProps[] = [ + { + title: "Primary", + colors: [ + { label: "main", color: p.primary.main }, + { label: "light", color: p.primary.light, textColor: "#333" }, + { label: "dark", color: p.primary.dark }, + ], + }, + { + title: "Secondary", + colors: [ + { label: "main", color: p.secondary.main }, + { label: "light", color: p.secondary.light, textColor: "#333" }, + { label: "dark", color: p.secondary.dark }, + ], + }, + { + title: "Highlight", + colors: p.highlight + ? [ + { label: "main", color: p.highlight.main }, + { label: "light", color: p.highlight.light, textColor: "#333" }, + { label: "dark", color: p.highlight.dark }, + ] + : [], + }, + { + title: "Text", + colors: [ + { + label: "primary", + color: p.text.primary, + textColor: p.background.default, + }, + { + label: "secondary", + color: p.text.secondary, + textColor: p.background.default, + }, + { + label: "disabled", + color: p.text.disabled, + textColor: p.background.default, + }, + ], + }, + { + title: "Status", + colors: [ + { label: "error", color: p.error.main }, + { label: "warning", color: p.warning.main }, + { label: "info", color: p.info.main }, + { label: "success", color: p.success.main }, + ], + }, + { + title: "Background", + colors: [ + { + label: "default", + color: p.background.default, + textColor: p.text.primary, + }, + { + label: "paper", + color: p.background.paper, + textColor: p.text.primary, + }, + ], + }, + ]; + + return ( + + + Theme Palette + + {groups.map((group) => ( + + ))} + + + + Chip 컴포넌트 (MUI 테마 적용 확인) + + + + + + + + + + + + + + ); +}; diff --git a/stories/assets/accessibility.png b/stories/assets/accessibility.png new file mode 100644 index 00000000..6ffe6fea Binary files /dev/null and b/stories/assets/accessibility.png differ diff --git a/stories/assets/addon-library.png b/stories/assets/addon-library.png new file mode 100644 index 00000000..95deb38a Binary files /dev/null and b/stories/assets/addon-library.png differ diff --git a/stories/assets/assets.png b/stories/assets/assets.png new file mode 100644 index 00000000..cfba6817 Binary files /dev/null and b/stories/assets/assets.png differ diff --git a/stories/assets/avif-test-image.avif b/stories/assets/avif-test-image.avif new file mode 100644 index 00000000..530709bc Binary files /dev/null and b/stories/assets/avif-test-image.avif differ diff --git a/stories/assets/context.png b/stories/assets/context.png new file mode 100644 index 00000000..e5cd249a Binary files /dev/null and b/stories/assets/context.png differ diff --git a/stories/assets/docs.png b/stories/assets/docs.png new file mode 100644 index 00000000..a749629d Binary files /dev/null and b/stories/assets/docs.png differ diff --git a/stories/assets/figma-plugin.png b/stories/assets/figma-plugin.png new file mode 100644 index 00000000..8f79b08c Binary files /dev/null and b/stories/assets/figma-plugin.png differ diff --git a/stories/assets/share.png b/stories/assets/share.png new file mode 100644 index 00000000..8097a370 Binary files /dev/null and b/stories/assets/share.png differ diff --git a/stories/assets/styling.png b/stories/assets/styling.png new file mode 100644 index 00000000..d341e826 Binary files /dev/null and b/stories/assets/styling.png differ diff --git a/stories/assets/testing.png b/stories/assets/testing.png new file mode 100644 index 00000000..d4ac39a0 Binary files /dev/null and b/stories/assets/testing.png differ diff --git a/stories/assets/theming.png b/stories/assets/theming.png new file mode 100644 index 00000000..1535eb9b Binary files /dev/null and b/stories/assets/theming.png differ diff --git a/stories/decorators/withCommonContext.tsx b/stories/decorators/withCommonContext.tsx new file mode 100644 index 00000000..2e8f1e62 --- /dev/null +++ b/stories/decorators/withCommonContext.tsx @@ -0,0 +1,31 @@ +import * as Common from "@frontend/common"; +import { theme2025, theme2026 } from "@frontend/theme"; +import type { PyConKRThemeConfig } from "@frontend/theme"; +import type { Decorator } from "@storybook/react-vite"; +import { MemoryRouter } from "react-router-dom"; + +const themes: Record = { + "PyCon KR 2025": theme2025, + "PyCon KR 2026": theme2026, +}; + +export const withCommonContext: Decorator = (Story, context) => { + const themeName = context.globals.theme || "PyCon KR 2025"; + const themeConfig = themes[themeName] ?? theme2025; + + const commonOptions: Common.Contexts.ContextOptions = { + language: "ko", + baseUrl: ".", + backendApiDomain: "", + backendApiTimeout: 10000, + eventConfig: themeConfig.event, + }; + + return ( + + + + + + ); +}; diff --git a/stories/decorators/withQueryClient.tsx b/stories/decorators/withQueryClient.tsx new file mode 100644 index 00000000..6f93630f --- /dev/null +++ b/stories/decorators/withQueryClient.tsx @@ -0,0 +1,35 @@ +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import type { Decorator } from "@storybook/react-vite"; +import * as React from "react"; + +/** + * story parameters에 `mockSessions`를 넣으면 React Query 캐시에 주입합니다. + * + * 예시: + * export const Default: Story = { + * parameters: { mockSessions: [...] }, + * }; + */ +export const withQueryClient: Decorator = (Story, context) => { + const [queryClient] = React.useState(() => { + const client = new QueryClient({ + defaultOptions: { + queries: { retry: false, staleTime: Infinity }, + }, + }); + + const mockSessions: unknown[] | undefined = context.parameters.mockSessions; + if (mockSessions) { + // language는 withCommonContext에서 "ko"로 고정되어 있으므로 "ko" 키만 주입합니다. + client.setQueryData(["query", "session", "list", "ko"], mockSessions); + } + + return client; + }); + + return ( + + + + ); +}; diff --git a/stories/decorators/withShopContext.tsx b/stories/decorators/withShopContext.tsx new file mode 100644 index 00000000..b1c19885 --- /dev/null +++ b/stories/decorators/withShopContext.tsx @@ -0,0 +1,52 @@ +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import * as React from "react"; +import type { Decorator } from "@storybook/react-vite"; + +import * as Shop from "@frontend/shop"; + +const LANGUAGE = "ko"; + +export const withShopContext: Decorator = (Story, context) => { + const [queryClient] = React.useState(() => { + const client = new QueryClient({ + defaultOptions: { queries: { retry: false, staleTime: Infinity } }, + }); + + const { mockUserStatus, mockProducts, mockCart, mockOrders, mockPatrons, mockPatronsYear } = context.parameters; + + if (mockUserStatus !== undefined) { + client.setQueryData(["query", "shop", "user", LANGUAGE], mockUserStatus); + } + if (mockProducts !== undefined) { + client.setQueryData(["query", "shop", "products", "", LANGUAGE], mockProducts); + } + if (mockCart !== undefined) { + client.setQueryData(["query", "shop", "cart", LANGUAGE], mockCart); + } + if (mockOrders !== undefined) { + client.setQueryData(["query", "shop", "orders", LANGUAGE], mockOrders); + } + if (mockPatrons !== undefined) { + const year: number = mockPatronsYear ?? 2025; + client.setQueryData(["query", "shop", "patrons", year], mockPatrons); + } + + return client; + }); + + const shopOptions: Shop.Contexts.ContextOptions = { + language: LANGUAGE, + shopApiDomain: "http://localhost:8000", + shopApiCSRFCookieName: "csrftoken", + shopApiTimeout: 10000, + shopImpAccountId: "imp_test", + }; + + return ( + + + + + + ); +}; diff --git a/stories/fixtures/sessions.ts b/stories/fixtures/sessions.ts new file mode 100644 index 00000000..a66936e4 --- /dev/null +++ b/stories/fixtures/sessions.ts @@ -0,0 +1,111 @@ +export const mockSessions = [ + { + id: "1", + title: "파이썬으로 배우는 클린 아키텍처", + summary: "파이썬 프로젝트에 클린 아키텍처를 적용하는 실전 사례를 공유합니다.", + description: "", + slideshow_url: null, + public_slideshow_file: null, + image: null, + categories: [ + { id: "cat-web", name: "Web" }, + { id: "cat-arch", name: "Architecture" }, + ], + speakers: [ + { id: "sp1", nickname: "김파이썬", biography: "", image: null }, + ], + room_schedules: [ + { + id: "rs1", + room_name: "Track A", + start_at: "2025-08-15T10:00:00+09:00", + end_at: "2025-08-15T10:50:00+09:00", + }, + ], + }, + { + id: "2", + title: "FastAPI와 Django의 차이점\n실무 관점에서 비교하기", + summary: null, + description: "", + slideshow_url: null, + public_slideshow_file: null, + image: null, + categories: [{ id: "cat-web", name: "Web" }], + speakers: [ + { id: "sp2", nickname: "이장고", biography: "", image: null }, + { id: "sp3", nickname: "박패스트", biography: "", image: null }, + ], + room_schedules: [ + { + id: "rs2", + room_name: "Track B", + start_at: "2025-08-15T10:00:00+09:00", + end_at: "2025-08-15T10:30:00+09:00", + }, + ], + }, + { + id: "3", + title: "타입 힌트로 더 안전한 파이썬 코드 작성하기", + summary: "mypy와 pyright를 활용해 정적 타입 검사를 도입하는 방법을 알아봅니다.", + description: "", + slideshow_url: "https://slides.example.com/3", + public_slideshow_file: null, + image: null, + categories: [{ id: "cat-quality", name: "Code Quality" }], + speakers: [{ id: "sp4", nickname: "최타입", biography: "", image: null }], + room_schedules: [ + { + id: "rs3", + room_name: "Track B", + start_at: "2025-08-15T10:30:00+09:00", + end_at: "2025-08-15T11:10:00+09:00", + }, + ], + }, + { + id: "4", + title: "기조 연설: 파이썬 커뮤니티의 과거, 현재, 미래", + summary: "파이콘 한국 2025 기조 연설입니다.", + description: "", + slideshow_url: null, + public_slideshow_file: null, + image: null, + categories: [{ id: "cat-keynote", name: "Keynote" }], + speakers: [{ id: "sp5", nickname: "파이콘 오거나이저", biography: "", image: null }], + room_schedules: [ + { + id: "rs4", + room_name: "Track A", + start_at: "2025-08-15T09:00:00+09:00", + end_at: "2025-08-15T09:50:00+09:00", + }, + { + id: "rs5", + room_name: "Track B", + start_at: "2025-08-15T09:00:00+09:00", + end_at: "2025-08-15T09:50:00+09:00", + }, + ], + }, + { + id: "5", + title: "데이터 파이프라인 구축 with Airflow", + summary: "Apache Airflow를 활용한 데이터 파이프라인 구축 경험을 공유합니다.", + description: "", + slideshow_url: null, + public_slideshow_file: null, + image: null, + categories: [{ id: "cat-data", name: "Data" }], + speakers: [{ id: "sp6", nickname: "정에어플로우", biography: "", image: null }], + room_schedules: [ + { + id: "rs6", + room_name: "Track A", + start_at: "2025-08-15T11:00:00+09:00", + end_at: "2025-08-15T11:50:00+09:00", + }, + ], + }, +]; diff --git a/stories/fixtures/shop.ts b/stories/fixtures/shop.ts new file mode 100644 index 00000000..c34dd169 --- /dev/null +++ b/stories/fixtures/shop.ts @@ -0,0 +1,172 @@ +import type ShopSchemas from "@frontend/shop/src/schemas"; + +export const mockUserSignedIn: ShopSchemas.UserSignedInStatus = { + status: 200, + meta: { is_authenticated: true }, + data: { + user: { + id: 1, + display: "홍길동", + has_usable_password: false, + email: "hong@example.com", + username: "hong", + }, + methods: [ + { + method: "socialaccount", + at: 1700000000, + provider: "google", + uid: "123456789", + }, + ], + }, +}; + +export const mockUserNotSignedIn: ShopSchemas.UserNotSignedInStatus = { + status: 401, + meta: { is_authenticated: false }, + data: { + flows: { + id: "provider_redirect", + providers: ["google", "naver", "kakao"], + }, + }, +}; + +export const mockProducts: ShopSchemas.Product[] = [ + { + id: "prod-001", + name: "파이콘 한국 2025 개인 티켓", + description: "파이콘 한국 2025 행사에 참가할 수 있는 개인 티켓입니다.\n\n- 3일 모든 행사 참여 가능\n- 점심 식사 포함", + image: null, + price: 50000, + orderable_starts_at: "2025-01-01T00:00:00Z", + orderable_ends_at: "2099-12-31T23:59:59Z", + refundable_ends_at: "2099-08-01T23:59:59Z", + category_group: "ticket", + category: "individual", + donation_allowed: false, + donation_min_price: null, + donation_max_price: null, + option_groups: [ + { + id: "og-001", + name: "식사 옵션", + min_quantity_per_product: 1, + max_quantity_per_product: 1, + is_custom_response: false, + custom_response_pattern: null, + options: [ + { id: "o-001", name: "일반식", additional_price: 0, max_quantity_per_user: 1, leftover_stock: 100 }, + { id: "o-002", name: "채식", additional_price: 0, max_quantity_per_user: 1, leftover_stock: 20 }, + ], + }, + ], + leftover_stock: 200, + tag_names: ["early-bird"], + }, + { + id: "prod-002", + name: "파이콘 한국 2025 후원 티켓", + description: "행사를 후원하는 분들을 위한 티켓입니다. 추가 기부도 가능합니다.", + image: null, + price: 100000, + orderable_starts_at: "2025-01-01T00:00:00Z", + orderable_ends_at: "2099-12-31T23:59:59Z", + refundable_ends_at: "2099-08-01T23:59:59Z", + category_group: "ticket", + category: "supporter", + donation_allowed: true, + donation_min_price: 0, + donation_max_price: 500000, + option_groups: [], + leftover_stock: 50, + tag_names: ["supporter"], + }, +]; + +const mockOrderProductItem: ShopSchemas.OrderProductItem = { + id: "opi-001", + status: "paid", + price: 50000, + donation_price: 0, + not_refundable_reason: null, + scancode_url: null, + product: { + id: "prod-001", + name: "파이콘 한국 2025 개인 티켓", + price: 50000, + image: null, + }, + options: [ + { + id: "opor-001", + product_option_group: { + id: "og-001", + name: "식사 옵션", + is_custom_response: false, + custom_response_pattern: null, + response_modifiable_ends_at: null, + }, + product_option: { + id: "o-001", + name: "일반식", + additional_price: 0, + }, + custom_response: null, + }, + ], +}; + +export const mockCart: ShopSchemas.Cart = { + id: "cart-001", + name: "장바구니", + first_paid_price: 50000, + current_paid_price: 50000, + current_status: "pending", + not_fully_refundable_reason: null, + created_at: "2025-03-01T10:00:00Z", + payment_histories: [], + products: [mockOrderProductItem], + customer_info: null, +}; + +export const mockEmptyCart: ShopSchemas.Cart = { + id: "cart-empty", + name: "장바구니", + first_paid_price: 0, + current_paid_price: 0, + current_status: "pending", + not_fully_refundable_reason: null, + created_at: "2025-03-01T10:00:00Z", + payment_histories: [], + products: [], + customer_info: null, +}; + +export const mockOrders: ShopSchemas.Order[] = [ + { + id: "order-001", + name: "파이콘 한국 2025 개인 티켓", + first_paid_price: 50000, + current_paid_price: 50000, + current_status: "completed", + not_fully_refundable_reason: null, + created_at: "2025-03-01T10:00:00Z", + payment_histories: [{ price: 50000, status: "completed" }], + products: [mockOrderProductItem], + customer_info: { + name: "홍길동", + phone: "010-1234-5678", + email: "hong@example.com", + organization: null, + }, + }, +]; + +export const mockPatrons: ShopSchemas.Patron[] = [ + { name: "홍길동", contribution_message: "파이콘을 응원합니다!" }, + { name: "김파이", contribution_message: "Python과 함께라면 무엇이든 가능해요." }, + { name: "이코드", contribution_message: "Weave with Python!" }, + { name: "박오픈", contribution_message: "오픈소스 커뮤니티 화이팅!" }, +]; diff --git a/stories/tsconfig.json b/stories/tsconfig.json new file mode 100644 index 00000000..76e27139 --- /dev/null +++ b/stories/tsconfig.json @@ -0,0 +1,23 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "baseUrl": "..", + "paths": { + "@apps/pyconkr/*": ["apps/pyconkr/src/*"], + "@apps/pyconkr-2026/*": ["apps/pyconkr-2026/src/*"], + "@frontend/common": ["packages/common/src/index.ts"], + "@frontend/common/*": ["packages/common/*"], + "@frontend/shop": ["packages/shop/src/index.ts"], + "@frontend/shop/*": ["packages/shop/*"], + "@frontend/theme": ["packages/theme/src/index.ts"], + "@frontend/theme/*": ["packages/theme/*"] + } + }, + "include": [ + "./**/*.ts", + "./**/*.tsx", + "../.storybook/**/*.ts", + "../.storybook/**/*.tsx", + "../types/**/*.d.ts" + ] +} diff --git a/tsconfig.json b/tsconfig.json index 669a8405..48481143 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -27,8 +27,10 @@ "baseUrl": ".", "paths": { "@apps/pyconkr/*": ["apps/pyconkr/src/*"], + "@apps/pyconkr-2026/*": ["apps/pyconkr-2026/src/*"], "@frontend/common/*": ["packages/common/*"], "@frontend/shop/*": ["packages/shop/*"], + "@frontend/theme/*": ["packages/theme/*"], } }, "include": ["apps", "packages", "types", "eslint.config.js"] diff --git a/types/kakao.d.ts b/types/kakao.d.ts new file mode 100644 index 00000000..8ef12af6 --- /dev/null +++ b/types/kakao.d.ts @@ -0,0 +1,22 @@ +// Kakao Maps SDK 최소 타입 선언 +interface KakaoMaps { + LatLng: new (lat: number, lng: number) => unknown; + Map: new (container: HTMLElement, options: { center: unknown; level: number }) => { + setCenter: (latlng: unknown) => void; + }; + Marker: new (options: { map: unknown; position: unknown }) => unknown; + InfoWindow: new (options: { content: string }) => { + open: (map: unknown, marker: unknown) => void; + close: () => void; + }; +} + +interface Kakao { + maps: KakaoMaps; +} + +declare const kakao: Kakao; + +interface Window { + kakao?: Kakao; +}