diff --git a/apps/playground-typescript/index.html b/apps/playground-typescript/index.html new file mode 100644 index 0000000..fa41216 --- /dev/null +++ b/apps/playground-typescript/index.html @@ -0,0 +1,14 @@ + + + + + + + Vite + React (TypeScript) + + +
+ + + + diff --git a/apps/playground-typescript/package.json b/apps/playground-typescript/package.json new file mode 100644 index 0000000..3416511 --- /dev/null +++ b/apps/playground-typescript/package.json @@ -0,0 +1,29 @@ +{ + "name": "playground-typescript", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "typecheck": "tsc -p tsconfig.json --noEmit", + "preview": "vite preview" + }, + "dependencies": { + "@7span/react-list": "workspace:*", + "@iconify/react": "^5.2.1", + "react": "^19.0.0", + "react-dom": "^19.0.0" + }, + "devDependencies": { + "@tailwindcss/postcss": "^4.2.2", + "@types/react": "^19.0.10", + "@types/react-dom": "^19.0.4", + "@vitejs/plugin-react": "^4.3.4", + "autoprefixer": "^10.4.27", + "postcss": "^8.5.8", + "tailwindcss": "^4.2.2", + "typescript": "^5.9.3", + "vite": "^6.3.1" + } +} diff --git a/apps/playground-typescript/postcss.config.mjs b/apps/playground-typescript/postcss.config.mjs new file mode 100644 index 0000000..51a6e4e --- /dev/null +++ b/apps/playground-typescript/postcss.config.mjs @@ -0,0 +1,6 @@ +export default { + plugins: { + '@tailwindcss/postcss': {}, + autoprefixer: {}, + }, +}; diff --git a/apps/playground-typescript/src/App.tsx b/apps/playground-typescript/src/App.tsx new file mode 100644 index 0000000..92febc7 --- /dev/null +++ b/apps/playground-typescript/src/App.tsx @@ -0,0 +1,6 @@ +import ListWrapper from './components/list-wrapper'; + +export default function App() { + return ; +} + diff --git a/apps/playground-typescript/src/api/request-handler.ts b/apps/playground-typescript/src/api/request-handler.ts new file mode 100644 index 0000000..e6b611a --- /dev/null +++ b/apps/playground-typescript/src/api/request-handler.ts @@ -0,0 +1,87 @@ +import type { ReactListRequestArgs, ReactListResponse } from '@7span/react-list'; + +export type PlaygroundItem = { + id: number; + name: string; + status?: string; + date_updated?: string; + [key: string]: unknown; +}; + +type Filters = Record; + +const requestHandler = async ( + args: ReactListRequestArgs, +): Promise> => { + const { + endpoint, + page, + perPage, + search, + sortBy, + sortOrder, + filters, + meta, + } = args; + + const params = new URLSearchParams(); + + if (page && perPage) { + params.append('page', String(page)); + params.append('limit', String(perPage)); + } + + if (search) params.append('search', search); + + if (sortBy) { + params.append( + 'sort', + sortOrder === 'desc' ? `-${sortBy}` : sortBy, + ); + } + + if (filters && Object.keys(filters).length > 0) { + // Keep it simple for the playground. + for (const [key, value] of Object.entries(filters)) { + if (value === undefined || value === '') continue; + params.append(`filter[${key}][_eq]`, String(value)); + } + } + + // Example: meta support (not required by ReactList, just forwarded in args) + if (meta && typeof meta === 'object') { + // noop; you can add meta params here. + } + + const queryString = params.toString(); + const url = `https://everest.7span.in/items/${endpoint}${ + queryString ? `?${queryString}` : '' + }`; + + try { + const response = await fetch(url); + if (!response.ok) { + throw new Error(`API request failed: ${response.status}`); + } + const data: any = await response.json(); + + return { + items: (data.data ?? []) as PlaygroundItem[], + count: + data.meta?.total_count ?? + data.meta?.filter_count ?? + 0, + meta: data.meta ?? {}, + }; + } catch (error) { + return { + items: [], + count: 0, + meta: {}, + error: error instanceof Error ? error : undefined, + } as unknown as ReactListResponse; + } +}; + +export default requestHandler; + diff --git a/apps/playground-typescript/src/components/list-wrapper.tsx b/apps/playground-typescript/src/components/list-wrapper.tsx new file mode 100644 index 0000000..02e2641 --- /dev/null +++ b/apps/playground-typescript/src/components/list-wrapper.tsx @@ -0,0 +1,298 @@ +import { useMemo, useState } from "react"; + +import ReactList, { + ReactListEmpty, + ReactListError, + ReactListGoTo, + ReactListInitialLoader, + ReactListItems, + ReactListLoader, + ReactListPagination, + ReactListPerPage, + ReactListProvider, + ReactListSearch, + ReactListSummary, +} from "@7span/react-list"; + +import { Icon } from "@iconify/react"; + +import reactListOptions from "./react-list"; +import type { PlaygroundItem } from "../api/request-handler"; + +type Filters = Record; + +export default function ListWrapper() { + const [filters, setFilters] = useState({}); + const paginationMode = "pagination" as const; + + // Stable render-prop children for the search component. + const searchChildren = useMemo( + () => + ({ + search, + setSearch, + }: { + search: string; + setSearch: (value: string) => void; + }) => ( +
+ setSearch(e.target.value)} + placeholder="Search skills..." + className="w-full rounded-md border border-gray-200 bg-white px-3 py-2 text-sm text-gray-900 shadow-sm outline-none focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500" + /> +
+ ), + [], + ); + + return ( +
+
+ + +
+

+ React List TypeScript Playground +

+ +
+ {searchChildren} + + +
+
+ +
+ +
+
+
+ {[0, 1, 2, 3].map((i) => ( +
+ ))} +
+
+ + +
+
+ +
+
+ No data found +
+
+
+ + + {({ error }: { error: Error }) => ( +
+
+ +
+
{error.name}
+
{error.message}
+
+
+
+ )} +
+ + + {({ items }) => ( +
+ {items.map((item, idx) => { + const row = item as PlaygroundItem; + + return ( +
+
+
+ ID {row?.id} +
+ +
+ {row?.name} +
+ +
+ Updated:{" "} + {row?.date_updated + ? new Date( + row.date_updated, + ).toLocaleString() + : "-"} +
+
+ +
+ {row?.status ?? "-"} +
+
+ ); + })} +
+ )} +
+
+ +
+ + {({ isLoading }: { isLoading: boolean }) => + isLoading ? ( +
+ Loading... +
+ ) : null + } +
+
+
+ +
+
+ + {({ visibleCount, count }) => ( +
+ {visibleCount} results + {typeof count === "number" ? ` (total ${count})` : ""} +
+ )} +
+ +
+ + {({ perPage, setPerPage, options }) => ( + + )} + + + + {({ setPage, page, pagesCount }) => ( +
+ setPage(Number(e.target.value))} + className="w-20 rounded-md border border-gray-200 bg-white px-3 py-2 text-sm shadow-sm outline-none focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500" + /> + + of {pagesCount} + +
+ )} +
+ + + {({ + page, + pagesToDisplay, + hasPrev, + hasNext, + prev, + next, + first, + last, + setPage, + }) => ( +
+ + + + +
+ {pagesToDisplay.map((p) => ( + + ))} +
+ + + + +
+ )} +
+
+
+
+
+ + +
+
+ ); +} diff --git a/apps/playground-typescript/src/components/react-list.ts b/apps/playground-typescript/src/components/react-list.ts new file mode 100644 index 0000000..ed965c9 --- /dev/null +++ b/apps/playground-typescript/src/components/react-list.ts @@ -0,0 +1,75 @@ +import type { + ReactListProviderConfig, + ReactListRequestArgs, + ReactListResponse, + ReactListStateManager, +} from "@7span/react-list"; + +import requestHandler from "../api/request-handler"; +import type { PlaygroundItem } from "../api/request-handler"; + +type Filters = Record; + +const stateManagerKey = (endpoint: string, version: number) => + `react-list--${endpoint}--${version}`; + +const stateManager: ReactListStateManager = { + init(context: any) { + const { endpoint, version } = context; + const allKeys = `react-list--${endpoint}--`; + const latestKey = stateManagerKey(endpoint, version); + + const staleKeys = Object.keys(localStorage).filter( + (key) => key.startsWith(allKeys) && key !== latestKey, + ); + staleKeys.forEach((key) => localStorage.removeItem(key)); + }, + set(context: any) { + const { + endpoint, + version, + search, + page, + perPage, + sortBy, + sortOrder, + filters, + attrSettings, + } = context; + + const key = stateManagerKey(endpoint, version); + localStorage.setItem( + key, + JSON.stringify({ + search, + page, + perPage, + sortBy, + sortOrder, + filters, + attrSettings, + }), + ); + }, + get(context: any) { + const { endpoint, version } = context; + const key = stateManagerKey(endpoint, version); + + try { + const raw = localStorage.getItem(key); + if (!raw) return null; + return JSON.parse(raw) as any; + } catch { + return null; + } + }, +}; + +const config: ReactListProviderConfig = { + requestHandler: requestHandler as unknown as ( + args: ReactListRequestArgs, + ) => Promise>, + stateManager, +}; + +export default config; diff --git a/apps/playground-typescript/src/index.css b/apps/playground-typescript/src/index.css new file mode 100644 index 0000000..d1d9133 --- /dev/null +++ b/apps/playground-typescript/src/index.css @@ -0,0 +1,2 @@ +@import "tailwindcss"; +@source "../**/*.{js,jsx,ts,tsx}"; \ No newline at end of file diff --git a/apps/playground-typescript/src/main.tsx b/apps/playground-typescript/src/main.tsx new file mode 100644 index 0000000..0c5505f --- /dev/null +++ b/apps/playground-typescript/src/main.tsx @@ -0,0 +1,11 @@ +import { StrictMode } from "react"; +import { createRoot } from "react-dom/client"; + +import App from "./App"; +import "./index.css"; + +createRoot(document.getElementById("root")!).render( + + + , +); diff --git a/apps/playground-typescript/tailwind.config.js b/apps/playground-typescript/tailwind.config.js new file mode 100644 index 0000000..a117e69 --- /dev/null +++ b/apps/playground-typescript/tailwind.config.js @@ -0,0 +1,8 @@ +/** @type {import('tailwindcss').Config} */ +export default { + content: ["./index.html", "./src/**/*.{ts,tsx,jsx,js}"], + theme: { + extend: {}, + }, + plugins: [], +}; diff --git a/apps/playground-typescript/tsconfig.json b/apps/playground-typescript/tsconfig.json new file mode 100644 index 0000000..e034b35 --- /dev/null +++ b/apps/playground-typescript/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "target": "ES2022", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "module": "ESNext", + "moduleResolution": "Bundler", + "jsx": "react-jsx", + "strict": true, + "skipLibCheck": true, + "noEmit": true, + "types": ["vite/client"], + "resolveJsonModule": true + }, + "include": ["src"] +} + diff --git a/apps/playground-typescript/vite.config.ts b/apps/playground-typescript/vite.config.ts new file mode 100644 index 0000000..f0c8be1 --- /dev/null +++ b/apps/playground-typescript/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; + +export default defineConfig({ + plugins: [react()], +}); + diff --git a/apps/playground/package.json b/apps/playground/package.json index c903db3..90148b5 100644 --- a/apps/playground/package.json +++ b/apps/playground/package.json @@ -10,21 +10,25 @@ "preview": "vite preview" }, "dependencies": { + "@7span/react-list": "workspace:*", + "@iconify/react": "^5.2.1", "axios": "1.11.0", "react": "^19.0.0", - "react-dom": "^19.0.0", - "@iconify/react": "^5.2.1", - "@7span/react-list": "workspace:*" + "react-dom": "^19.0.0" }, "devDependencies": { "@eslint/js": "^9.22.0", + "@tailwindcss/postcss": "^4.2.2", "@types/react": "^19.0.10", "@types/react-dom": "^19.0.4", "@vitejs/plugin-react": "^4.3.4", + "autoprefixer": "^10.4.27", "eslint": "^9.22.0", "eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-refresh": "^0.4.19", "globals": "^16.0.0", + "postcss": "^8.5.8", + "tailwindcss": "^4.2.2", "vite": "^6.3.1" } } diff --git a/apps/playground/postcss.config.js b/apps/playground/postcss.config.js new file mode 100644 index 0000000..51a6e4e --- /dev/null +++ b/apps/playground/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + '@tailwindcss/postcss': {}, + autoprefixer: {}, + }, +}; diff --git a/apps/playground/src/components/list-wrapper.jsx b/apps/playground/src/components/list-wrapper.jsx index a74d603..c8600b0 100644 --- a/apps/playground/src/components/list-wrapper.jsx +++ b/apps/playground/src/components/list-wrapper.jsx @@ -1,3 +1,5 @@ +import { useMemo, useState } from 'react'; + import ReactList, { ReactListEmpty, ReactListError, @@ -10,416 +12,334 @@ import ReactList, { ReactListProvider, ReactListSearch, ReactListSummary, -} from "@7span/react-list"; +} from '@7span/react-list'; + +import { Icon } from '@iconify/react'; -import { Icon } from "@iconify/react"; -import { useState } from "react"; +import reactListOptions from './react-list'; -import reactListOptions from "./react-list"; +const STATUS_OPTIONS = [ + { label: 'All Status', value: '' }, + { label: 'Draft', value: 'draft' }, + { label: 'Published', value: 'published' }, + { label: 'Archived', value: 'archived' }, +]; -import "../app.css"; +const COLOR_OPTIONS = [ + { label: 'All Colors', value: '' }, + { label: 'Red', value: '#FF9900' }, + { label: 'Blue', value: '#FFEB0F' }, + { label: 'Green', value: '#F7DF1E' }, + { label: 'Yellow', value: '#5E24FF' }, +]; -const ListWrapper = () => { +export default function ListWrapper() { const [filters, setFilters] = useState({}); + const paginationMode = 'pagination'; + + const searchChildren = useMemo( + () => + ({ search, setSearch }) => ( +
+ setSearch(e.target.value)} + placeholder="Search skills..." + className="w-full rounded-md border border-gray-700 bg-gray-900 px-3 py-2 text-sm text-gray-100 shadow-sm outline-none focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500/20" + /> +
+ ), + [], + ); + return ( -
-

React List Playground

- - -
-
- {/* */} - - {({ search, setSearch }) => ( -
- { - setSearch(e.target.value); - }} - placeholder="Search skills..." - className="search-input" - /> -
- )} -
-
-
-
-
- +
+
+
+

+ React List Playground (Tailwind) +

+ +
+ + +
+ {searchChildren} + -
- -
-
-
- -
- -
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
- {[...Array(5)].map((_, index) => ( -
-
-
-
-
-
+ {COLOR_OPTIONS.map((opt) => ( + ))} -
-
-
-
- -
-
-
-
- -
-
- -

No Data Found

- -

- We couldn't find any matching records. Try adjusting your - search or filters. -

+ - -
-
- - {({ error }) => ( -
-
- -
-

Something went wrong

-

- {error.message || - "An unexpected error occurred while fetching data."} -

-
-

- {error.name}: {error.message} -

-
-
- )} -
- - {({ items, sort, setSort }) => { - return ( -
- -
-
- + +
+
+
+ {[0, 1, 2, 3].map((i) => ( +
+ ))} +
+
+ + +
+ +
+
+ No data found
- - - - - - - - - - - - {items.map((item) => ( - - - - - - - ))} - -
ID - Name{" "} - - Status - Update At{" "} - -
{item.id}{item.name}{item.status} - {new Date(item.date_updated).toLocaleString()} -
-
- ); - }} - - {/* Footer */} -
-
-
- - {({ visibleCount }) => ( - - {visibleCount} Results - - )} - -
+ + {({ items }) => ( +
+ {items.map((item, idx) => { + const row = item ?? {}; + return ( +
+
+
+ ID {row.id} +
-
- Page Size: - - {({ perPage, setPerPage, options }) => ( -
- -
- +
+ {row.name} +
+ +
+ Updated:{' '} + {row.date_updated + ? new Date( + row.date_updated, + ).toLocaleString() + : '-'} +
+
+ +
+ {row.status ?? '-'} +
+
+ ); + })}
-
- )} - -
+ )} + -
- - {({ setPage, page, pagesCount }) => ( -
- Go to: - { - const page = Number(e.target.value); - setPage(page); - }} - className="page-input" - /> - of {pagesCount} -
- )} -
-
-
+ + {({ isLoading }) => + isLoading ? ( +
+ Loading... +
+ ) : null + } +
+
- - {({ - page, - pagesToDisplay, - hasNext, - hasPrev, - prev, - next, - first, - last, - setPage, - }) => ( -
- +
+
+ + {({ visibleCount, count }) => ( +
+ {visibleCount} results + {typeof count === 'number' + ? ` (total ${count})` + : ''} +
+ )} +
- +
+ + {({ perPage, setPerPage, options }) => ( + + )} + -
- {pagesToDisplay.map((item, index) => { - const isActive = item === page; + + {({ setPage, page, pagesCount }) => ( +
+ + setPage(Number(e.target.value)) + } + className="w-20 rounded-md border border-gray-700 bg-gray-900 px-3 py-2 text-sm text-gray-100 shadow-sm outline-none focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500/20" + /> + + of {pagesCount} + +
+ )} +
- return ( - - ); - })} -
+ + {({ + page, + pagesToDisplay, + hasPrev, + hasNext, + first, + prev, + next, + last, + setPage, + }) => ( +
+ + - +
+ {pagesToDisplay.map((p) => ( + + ))} +
- + + +
+ )} +
+
+
- )} - -
+
+ +
- - +
+
); -}; +} -export default ListWrapper; diff --git a/apps/playground/src/index.css b/apps/playground/src/index.css index 08a3ac9..a50c033 100644 --- a/apps/playground/src/index.css +++ b/apps/playground/src/index.css @@ -1,3 +1,6 @@ +@import "tailwindcss"; +@source "../**/*.{js,jsx,ts,tsx}"; + :root { font-family: system-ui, Avenir, Helvetica, Arial, sans-serif; line-height: 1.5; @@ -18,6 +21,7 @@ a { color: #646cff; text-decoration: inherit; } + a:hover { color: #535bf2; } @@ -46,9 +50,11 @@ button { cursor: pointer; transition: border-color 0.25s; } + button:hover { border-color: #646cff; } + button:focus, button:focus-visible { outline: 4px auto -webkit-focus-ring-color; @@ -59,10 +65,12 @@ button:focus-visible { color: #213547; background-color: #ffffff; } + a:hover { color: #747bff; } + button { background-color: #f9f9f9; } -} +} \ No newline at end of file diff --git a/apps/playground/tailwind.config.js b/apps/playground/tailwind.config.js new file mode 100644 index 0000000..524f96e --- /dev/null +++ b/apps/playground/tailwind.config.js @@ -0,0 +1,9 @@ +/** @type {import('tailwindcss').Config} */ +export default { + content: ['./index.html', './src/**/*.{js,jsx,ts,tsx}'], + theme: { + extend: {}, + }, + plugins: [], +}; + diff --git a/packages/react-list/package.json b/packages/react-list/package.json index ebe29c5..9ed6aa8 100644 --- a/packages/react-list/package.json +++ b/packages/react-list/package.json @@ -1,32 +1,41 @@ { "name": "@7span/react-list", - "version": "1.0.1", + "version": "1.1.0", "description": "A simple and reusable list component for React", "type": "module", "scripts": { "dev": "vite", - "build": "vite build", + "build": "vite build && pnpm run build:types", + "build:types": "tsc -p tsconfig.build.json", "lint": "eslint .", "preview": "vite preview", - "prepublishOnly": "npm run build" + "prepublishOnly": "pnpm run build" }, - "main": "src/index.js", + "main": "dist/react-list.cjs", + "module": "dist/react-list.js", + "types": "dist/types/index.d.ts", "exports": { - ".": "./src/index.js" + ".": { + "types": "./dist/types/index.d.ts", + "import": "./dist/react-list.js", + "require": "./dist/react-list.cjs" + } }, "files": [ - "dist", - "src" + "dist" ], + "publishConfig": { + "access": "public" + }, "peerDependencies": { "react": "^18.2.0 || ^19.0.0", "react-dom": "^18.2.0 || ^19.0.0" }, - "publishConfig": { - "access": "public" - }, "devDependencies": { + "@types/react": "^19.1.13", + "@types/react-dom": "^19.1.9", "@vitejs/plugin-react": "^4.3.4", + "typescript": "^5.9.3", "vite": "^6.3.1" } -} +} \ No newline at end of file diff --git a/packages/react-list/src/components/attributes.jsx b/packages/react-list/src/components/attributes.jsx deleted file mode 100644 index b3fc189..0000000 --- a/packages/react-list/src/components/attributes.jsx +++ /dev/null @@ -1,53 +0,0 @@ -import { memo, useCallback, useMemo } from "react"; -import { useListContext } from "../context/list-provider"; - -export const ReactListAttributes = memo(({ children, renderAttribute }) => { - const { listState } = useListContext(); - const { attrs, attrSettings, updateAttr } = listState; - - const handleAttrChange = useCallback( - (attrName) => (e) => { - updateAttr(attrName, "visible", e.target.checked); - }, - [updateAttr] - ); - - const scope = useMemo( - () => ({ - attrs, - attrSettings, - updateAttr, - }), - [attrs, attrSettings, updateAttr] - ); - - if (children) { - return children(scope); - } - - return ( -
- {attrs.map((attr, index) => { - if (renderAttribute) { - return renderAttribute({ - key: `attr-${index}`, - attr, - updateAttr, - attrSettings, - }); - } - - return ( - - ); - })} -
- ); -}); diff --git a/packages/react-list/src/components/attributes.tsx b/packages/react-list/src/components/attributes.tsx new file mode 100644 index 0000000..fa4e2d7 --- /dev/null +++ b/packages/react-list/src/components/attributes.tsx @@ -0,0 +1,87 @@ +import { memo, useCallback, useMemo } from "react"; +import { useListContext } from "../context/list-provider"; +import type { ReactListAttrSettings, ReactListAttribute } from "../types"; +import type { ChangeEvent, ReactNode } from "react"; + +type ReactListAttributesUpdateAttr = ( + attrName: string, + settingKey: string, + value: unknown, +) => void; + +type ReactListAttributesScope = { + attrs: ReactListAttribute[]; + attrSettings: ReactListAttrSettings; + updateAttr: ReactListAttributesUpdateAttr; +}; + +type ReactListAttributesRenderAttributeArgs = { + key: string; + attr: ReactListAttribute; + updateAttr: ReactListAttributesUpdateAttr; + attrSettings: ReactListAttrSettings; +}; + +type ReactListAttributesProps = { + children?: ReactNode | ((scope: ReactListAttributesScope) => ReactNode); + renderAttribute?: (args: ReactListAttributesRenderAttributeArgs) => ReactNode; +}; + +export const ReactListAttributes = memo( + ({ children, renderAttribute }: ReactListAttributesProps) => { + const { listState } = useListContext(); + const { attrs, attrSettings, updateAttr } = listState; + + const handleAttrChange = useCallback( + (attrName: string) => (e: ChangeEvent) => { + updateAttr(attrName, "visible", e.target.checked); + }, + [updateAttr], + ); + + const scope = useMemo( + () => ({ + attrs, + attrSettings, + updateAttr, + }), + [attrs, attrSettings, updateAttr], + ); + + if (typeof children === "function") { + return children(scope); + } + + if (children) return children; + + return ( +
+ {attrs.map((attr, index) => { + if (renderAttribute) { + return ( + + {renderAttribute({ + key: `attr-${index}`, + attr, + updateAttr, + attrSettings, + })} + + ); + } + + return ( + + ); + })} +
+ ); + }, +); diff --git a/packages/react-list/src/components/empty.jsx b/packages/react-list/src/components/empty.tsx similarity index 77% rename from packages/react-list/src/components/empty.jsx rename to packages/react-list/src/components/empty.tsx index 40dbeed..c97be07 100644 --- a/packages/react-list/src/components/empty.jsx +++ b/packages/react-list/src/components/empty.tsx @@ -1,7 +1,11 @@ import { memo } from "react"; import { useListContext } from "../context/list-provider"; -export const ReactListEmpty = memo(({ children }) => { +type ReactListEmptyProps = { + children?: React.ReactNode; +}; + +export const ReactListEmpty = memo(({ children }: ReactListEmptyProps) => { const { listState } = useListContext(); const { data: items, loader, error } = listState; const { isLoading, initialLoading } = loader; diff --git a/packages/react-list/src/components/error.jsx b/packages/react-list/src/components/error.tsx similarity index 72% rename from packages/react-list/src/components/error.jsx rename to packages/react-list/src/components/error.tsx index 957ad19..476b5d0 100644 --- a/packages/react-list/src/components/error.jsx +++ b/packages/react-list/src/components/error.tsx @@ -1,7 +1,14 @@ import { memo } from "react"; +import type { ReactNode } from "react"; import { useListContext } from "../context/list-provider"; -export const ReactListError = memo(({ children }) => { +type ReactListErrorProps = { + children?: + | ReactNode + | ((scope: { error: Error }) => ReactNode); +}; + +export const ReactListError = memo(({ children }: ReactListErrorProps) => { const { listState } = useListContext(); const { error, loader } = listState; const { isLoading } = loader; diff --git a/packages/react-list/src/components/go-to.jsx b/packages/react-list/src/components/go-to.tsx similarity index 73% rename from packages/react-list/src/components/go-to.jsx rename to packages/react-list/src/components/go-to.tsx index 361162b..a076d86 100644 --- a/packages/react-list/src/components/go-to.jsx +++ b/packages/react-list/src/components/go-to.tsx @@ -1,7 +1,21 @@ import { memo, useCallback, useMemo } from "react"; +import type { ReactNode } from "react"; import { useListContext } from "../context/list-provider"; -export const ReactListGoTo = memo(({ children }) => { +type ReactListGoToScope = { + setPage: (page: number, addContext?: Record) => void; + page: number; + pages: number[]; + pagesCount: number; +}; + +type ReactListGoToProps = { + children?: + | ReactNode + | ((scope: ReactListGoToScope) => ReactNode); +}; + +export const ReactListGoTo = memo(({ children }: ReactListGoToProps) => { const { listState } = useListContext(); const { data, count, pagination, setPage, loader, error } = listState; const { page, perPage } = pagination; @@ -42,8 +56,10 @@ export const ReactListGoTo = memo(({ children }) => { return (
- {children ? ( + {typeof children === "function" ? ( children(scope) + ) : children ? ( + children ) : ( handleChange(e.target.value)} - placeholder="Search..." - /> - )} -
- ); -}); diff --git a/packages/react-list/src/components/search.tsx b/packages/react-list/src/components/search.tsx new file mode 100644 index 0000000..0eb7d1c --- /dev/null +++ b/packages/react-list/src/components/search.tsx @@ -0,0 +1,75 @@ +import { memo, useEffect, useRef, useState } from "react"; +import type { ReactNode } from "react"; + +import { useListContext } from "../context/list-provider"; + +type ReactListSearchScope = { + search: string; + setSearch: (value: string) => void; +}; + +type ReactListSearchProps = { + debounceTime?: number; + children?: ReactNode | ((scope: ReactListSearchScope) => ReactNode); +}; + +export const ReactListSearch = memo( + ({ children, debounceTime = 500 }: ReactListSearchProps) => { + const { listState } = useListContext(); + const { search, setSearch } = listState; + + const [localSearch, setLocalSearch] = useState(search ?? ""); + const debounceTimerRef = useRef | null>(null); + + // Sync local state with context when the list search value changes. + useEffect(() => { + if (search !== localSearch) { + setLocalSearch(search ?? ""); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [search]); + + const handleChange = (value: string) => { + setLocalSearch(value); + + if (debounceTimerRef.current) { + clearTimeout(debounceTimerRef.current); + } + + debounceTimerRef.current = setTimeout(() => { + setSearch(value); + }, debounceTime); + }; + + useEffect(() => { + return () => { + if (debounceTimerRef.current) { + clearTimeout(debounceTimerRef.current); + } + }; + }, []); + + const scope: ReactListSearchScope = { + search: localSearch, + setSearch: handleChange, + }; + + return ( +
+ {typeof children === "function" ? ( + children(scope) + ) : children ? ( + children + ) : ( + handleChange(e.target.value)} + placeholder="Search..." + /> + )} +
+ ); + } +); + diff --git a/packages/react-list/src/components/summary.jsx b/packages/react-list/src/components/summary.tsx similarity index 72% rename from packages/react-list/src/components/summary.jsx rename to packages/react-list/src/components/summary.tsx index 284da5c..a360b0a 100644 --- a/packages/react-list/src/components/summary.jsx +++ b/packages/react-list/src/components/summary.tsx @@ -1,7 +1,21 @@ import { memo, useMemo } from "react"; import { useListContext } from "../context/list-provider"; -export const ReactListSummary = memo(({ children }) => { +import type { ReactNode } from "react"; + +type ReactListSummaryScope = { + from: number; + to: number; + visibleCount: number; + count: number; +}; + +type ReactListSummaryProps = { + children?: ReactNode | ((scope: ReactListSummaryScope) => ReactNode); +}; + +export const ReactListSummary = memo( + ({ children }: ReactListSummaryProps) => { const { listState } = useListContext(); const { data, count, pagination, loader, error } = listState; const { page, perPage } = pagination; @@ -35,8 +49,10 @@ export const ReactListSummary = memo(({ children }) => { return (
- {children ? ( + {typeof children === "function" ? ( children(scope) + ) : children ? ( + children ) : ( Showing {summaryData.visibleCount} items ( @@ -48,4 +64,5 @@ export const ReactListSummary = memo(({ children }) => { )}
); -}); + } +); diff --git a/packages/react-list/src/components/utils.js b/packages/react-list/src/components/utils.ts similarity index 51% rename from packages/react-list/src/components/utils.js rename to packages/react-list/src/components/utils.ts index f4a7279..e9f4e33 100644 --- a/packages/react-list/src/components/utils.js +++ b/packages/react-list/src/components/utils.ts @@ -1,10 +1,11 @@ -export const deepEqual = (obj1, obj2) => { +export const deepEqual = (obj1: unknown, obj2: unknown): boolean => { if (obj1 === obj2) return true; if (obj1 == null || obj2 == null) return obj1 === obj2; - if (typeof obj1 !== "object" || typeof obj2 !== "object") + if (typeof obj1 !== "object" || typeof obj2 !== "object") { return obj1 === obj2; + } if (Array.isArray(obj1) && Array.isArray(obj2)) { if (obj1.length !== obj2.length) return false; @@ -16,22 +17,28 @@ export const deepEqual = (obj1, obj2) => { if (Array.isArray(obj1) || Array.isArray(obj2)) return false; - const keys1 = Object.keys(obj1).filter((key) => obj1[key] !== undefined); - const keys2 = Object.keys(obj2).filter((key) => obj2[key] !== undefined); + const record1 = obj1 as Record; + const record2 = obj2 as Record; + + const keys1 = Object.keys(record1).filter((key) => record1[key] !== undefined); + const keys2 = Object.keys(record2).filter((key) => record2[key] !== undefined); if (keys1.length !== keys2.length) return false; - for (let key of keys1) { + for (const key of keys1) { if (!keys2.includes(key)) return false; - if (!deepEqual(obj1[key], obj2[key])) return false; + if (!deepEqual(record1[key], record2[key])) return false; } return true; }; -export const hasActiveFilters = (currentFilters, initialFilters) => { +export const hasActiveFilters = ( + currentFilters: Record | null | undefined, + initialFilters: Record | null | undefined +): boolean => { if (!initialFilters || Object.keys(initialFilters).length === 0) { - return currentFilters && Object.keys(currentFilters).length > 0; + return !!currentFilters && Object.keys(currentFilters).length > 0; } if (!currentFilters || Object.keys(currentFilters).length === 0) { diff --git a/packages/react-list/src/context/list-provider.jsx b/packages/react-list/src/context/list-provider.jsx deleted file mode 100644 index bd4e21a..0000000 --- a/packages/react-list/src/context/list-provider.jsx +++ /dev/null @@ -1,55 +0,0 @@ -import { createContext, useContext, useMemo, useState } from "react"; - -const ListContext = createContext(null); - -export const ReactListProvider = ({ children, config }) => { - const { requestHandler, stateManager = {} } = config; - const [listState, setListState] = useState({ - data: [], - response: null, - error: null, - count: 0, - selection: [], - pagination: { - page: 1, - perPage: 25, - }, - loader: { - isLoading: false, - initialLoading: true, - }, - sort: { - sortBy: null, - sortOrder: "desc", - }, - search: "", - filters: {}, - attrs: [], - isEmpty: true, - isInitializing: true, - }); - - if (!requestHandler) { - throw new Error("ListProvider: requestHandler is required."); - } - - const value = useMemo( - () => ({ - requestHandler, - stateManager, - listState, - setListState, - }), - [requestHandler, stateManager, listState] - ); - - return {children}; -}; - -export const useListContext = () => { - const context = useContext(ListContext); - if (!context) { - throw new Error("useListContext must be used within a ListProvider"); - } - return context; -}; diff --git a/packages/react-list/src/context/list-provider.tsx b/packages/react-list/src/context/list-provider.tsx new file mode 100644 index 0000000..0182397 --- /dev/null +++ b/packages/react-list/src/context/list-provider.tsx @@ -0,0 +1,91 @@ +import { createContext, useContext, useMemo, useState } from "react"; +import type { ReactNode } from "react"; + +import type { + ReactListContextValue, + ReactListFilters, + ReactListItem, + ReactListItemId, + ReactListListState, + ReactListProviderConfig, + ReactListResponse, + ReactListRequestHandler, + ReactListStateManager, +} from "../types"; + +const ListContext = createContext(null); + +type ReactListProviderProps = { + children: ReactNode; + config: ReactListProviderConfig; +}; + +const noop = () => {}; + +export const ReactListProvider = ({ children, config }: ReactListProviderProps) => { + const { requestHandler, stateManager = {} } = config; + + if (!requestHandler) { + throw new Error("ListProvider: requestHandler is required."); + } + + const [listState, setListState] = useState>({ + data: [], + response: null as ReactListResponse | null, + error: null, + count: 0, + selection: [] as ReactListItemId[], + pagination: { + page: 1, + perPage: 25, + hasMore: false, + }, + loader: { + isLoading: false, + initialLoading: true, + }, + sort: { + sortBy: "", + sortOrder: "desc", + }, + hasActiveFilters: false, + search: "", + filters: {}, + attrs: [], + attrSettings: {}, + isEmpty: true, + + // These actions are replaced by `ReactList` once it mounts. + setPage: noop, + setPerPage: noop, + setSearch: noop, + setSort: noop, + loadMore: noop, + clearFilters: noop, + refresh: noop, + setFilters: noop, + updateAttr: noop, + updateItemById: noop, + setSelection: noop, + }); + + const value: ReactListContextValue = useMemo( + () => ({ + requestHandler: requestHandler as ReactListRequestHandler, + stateManager: stateManager as ReactListStateManager, + listState, + setListState, + }), + [requestHandler, stateManager, listState] + ); + + return {children}; +}; + +export const useListContext = (): ReactListContextValue => { + const context = useContext(ListContext); + if (!context) { + throw new Error("useListContext must be used within a ListProvider"); + } + return context; +}; diff --git a/packages/react-list/src/index.js b/packages/react-list/src/index.ts similarity index 64% rename from packages/react-list/src/index.js rename to packages/react-list/src/index.ts index d132e3f..65eca60 100644 --- a/packages/react-list/src/index.js +++ b/packages/react-list/src/index.ts @@ -13,3 +13,25 @@ export { ReactListRefresh } from "./components/refresh"; export { ReactListSearch } from "./components/search"; export { ReactListSummary } from "./components/summary"; export { ReactListProvider } from "./context/list-provider"; + +export type { + ReactListAttrSettings, + ReactListContext, + ReactListContextValue, + ReactListFilters, + ReactListAttribute, + ReactListProps, + ReactListPaginationMode, + ReactListItem, + ReactListItemId, + ReactListListState, + ReactListLoaderState, + ReactListPaginationState, + ReactListProviderConfig, + ReactListRequestArgs, + ReactListRequestHandler, + ReactListResponse, + ReactListSort, + ReactListSortOrder, + ReactListStateManager, +} from "./types"; diff --git a/packages/react-list/src/types/index.ts b/packages/react-list/src/types/index.ts new file mode 100644 index 0000000..d27aaf5 --- /dev/null +++ b/packages/react-list/src/types/index.ts @@ -0,0 +1,22 @@ +export type { + ReactListAttrSettings, + ReactListContext, + ReactListContextValue, + ReactListItem, + ReactListItemId, + ReactListListState, + ReactListLoaderState, + ReactListPaginationState, + ReactListProviderConfig, + ReactListRequestArgs, + ReactListRequestHandler, + ReactListResponse, + ReactListSort, + ReactListSortOrder, + ReactListStateManager, + ReactListFilters, + ReactListAttribute, +} from './list-provider.ts'; + +export type { ReactListProps, ReactListPaginationMode } from './react-list'; + diff --git a/packages/react-list/src/types/list-provider.ts b/packages/react-list/src/types/list-provider.ts new file mode 100644 index 0000000..795ddbd --- /dev/null +++ b/packages/react-list/src/types/list-provider.ts @@ -0,0 +1,156 @@ +import type * as React from 'react'; + +export type ReactListItemId = string | number; + +export type ReactListItem = Record & { + id?: ReactListItemId; +}; + +export type ReactListFilters = Record; + +export type ReactListSortOrder = '' | 'asc' | 'desc' | (string & {}); + +export type ReactListAttrSettings = Record< + string, + { + visible: boolean; + } +>; + +export type ReactListRequestArgs< + TFilters extends ReactListFilters = ReactListFilters, +> = { + endpoint: string; + version: number; + meta: Record; + page: number; + perPage: number; + search: string; + sortBy: string; + sortOrder: ReactListSortOrder; + filters: TFilters; +} & Record; + +export type ReactListResponse = { + items: TItem[]; + count: number; + meta?: unknown; + [key: string]: unknown; +}; + +export type ReactListRequestHandler< + TItem = ReactListItem, + TFilters extends ReactListFilters = ReactListFilters, +> = (args: ReactListRequestArgs) => Promise>; + +export type ReactListPersistedState< + TFilters extends ReactListFilters = ReactListFilters, +> = { + page?: number; + perPage?: number; + sortBy?: string; + sortOrder?: ReactListSortOrder; + search?: string; + attrSettings?: ReactListAttrSettings; + filters?: TFilters; +}; + +export type ReactListContext = { + endpoint: string; + version: number; + meta: Record; + search: string; + page: number; + perPage: number; + sortBy: string; + sortOrder: ReactListSortOrder; + filters: TFilters; + attrSettings: ReactListAttrSettings; + isRefresh: boolean; +}; + +export type ReactListStateManager< + TFilters extends ReactListFilters = ReactListFilters, +> = { + init?: (context: ReactListContext) => void; + get?: (context: ReactListContext) => ReactListPersistedState | null; + set?: (context: ReactListContext) => void; +}; + +export type ReactListProviderConfig< + TItem = ReactListItem, + TFilters extends ReactListFilters = ReactListFilters, +> = { + requestHandler: ReactListRequestHandler; + stateManager?: ReactListStateManager; +}; + +export type ReactListPaginationState = { + page: number; + perPage: number; + hasMore: boolean; +}; + +export type ReactListLoaderState = { + isLoading: boolean; + initialLoading: boolean; +}; + +export type ReactListSort = { + sortBy: string; + sortOrder: ReactListSortOrder; +}; + +export type ReactListAttribute = { + name: string; + label?: string; +}; + +export type ReactListActions< + TItem = ReactListItem, + TFilters extends ReactListFilters = ReactListFilters, +> = { + setPage: (value: number, addContext?: Record) => void; + setPerPage: (value: number) => void; + setSearch: (value: string) => void; + setSort: (args: { by: string; order: ReactListSortOrder }) => void; + loadMore: () => void; + clearFilters: () => void; + refresh: (addContext?: Record) => void; + setFilters: (filters: TFilters) => void; + updateAttr: (attrName: string, settingKey: string, value: unknown) => void; + updateItemById: (item: Partial, id: ReactListItemId) => void; + setSelection: (selection: ReactListItemId[]) => void; +}; + +export type ReactListListState< + TItem = ReactListItem, + TFilters extends ReactListFilters = ReactListFilters, +> = { + data: TItem[]; + response: ReactListResponse | null; + error: Error | null; + count: number; + selection: ReactListItemId[]; + pagination: ReactListPaginationState; + loader: ReactListLoaderState; + sort: ReactListSort; + hasActiveFilters: boolean; + search: string; + filters: TFilters; + attrs: ReactListAttribute[]; + attrSettings: ReactListAttrSettings; + isEmpty: boolean; + // Actions are appended by the main `ReactList` component. +} & ReactListActions; + +export type ReactListContextValue< + TItem = ReactListItem, + TFilters extends ReactListFilters = ReactListFilters, +> = { + requestHandler: ReactListRequestHandler; + stateManager: ReactListStateManager; + listState: ReactListListState; + setListState: React.Dispatch>>; +}; + diff --git a/packages/react-list/src/types/react-list.ts b/packages/react-list/src/types/react-list.ts new file mode 100644 index 0000000..4ec6d6c --- /dev/null +++ b/packages/react-list/src/types/react-list.ts @@ -0,0 +1,39 @@ +import type { ReactNode } from 'react'; + +import type { + ReactListAttribute, + ReactListFilters, + ReactListItem, + ReactListListState, + ReactListResponse, + ReactListSortOrder, +} from './list-provider'; + +export type ReactListPaginationMode = 'pagination' | 'loadMore'; + +export type ReactListProps< + TItem = ReactListItem, + TFilters extends ReactListFilters = ReactListFilters, +> = { + initialItems?: TItem[]; + children?: + | ReactNode + | ((scope: ReactListListState) => ReactNode); + + endpoint: string; + page?: number; + perPage?: number; + sortBy?: string; + sortOrder?: ReactListSortOrder; + count?: number; + search?: string; + filters?: TFilters; + attrs?: Array; + version?: number; + paginationMode?: ReactListPaginationMode; + meta?: Record; + onResponse?: (res: ReactListResponse) => void; + afterPageChange?: (res: ReactListResponse) => void; + afterLoadMore?: (res: ReactListResponse) => void; +}; + diff --git a/packages/react-list/src/utils.js b/packages/react-list/src/utils.ts similarity index 100% rename from packages/react-list/src/utils.js rename to packages/react-list/src/utils.ts diff --git a/packages/react-list/tsconfig.build.json b/packages/react-list/tsconfig.build.json new file mode 100644 index 0000000..b96c8ea --- /dev/null +++ b/packages/react-list/tsconfig.build.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "jsx": "react-jsx", + "allowJs": true, + "checkJs": false, + "declaration": true, + "emitDeclarationOnly": true, + "declarationMap": true, + "outDir": "dist/types", + "strict": false, + "skipLibCheck": true + }, + "include": ["src/**/*"], + "exclude": ["dist", "node_modules"] +} diff --git a/packages/react-list/tsconfig.json b/packages/react-list/tsconfig.json new file mode 100644 index 0000000..5c29504 --- /dev/null +++ b/packages/react-list/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.build.json", + "compilerOptions": { + "noEmit": true + } +} diff --git a/packages/react-list/vite.config.js b/packages/react-list/vite.config.js index 0d1dd24..a1037f0 100644 --- a/packages/react-list/vite.config.js +++ b/packages/react-list/vite.config.js @@ -1,25 +1,25 @@ import { defineConfig } from "vite"; import react from "@vitejs/plugin-react"; -import { createRequire } from "module"; -const require = createRequire(import.meta.url); + // https://vite.dev/config/ export default defineConfig({ plugins: [react()], build: { rollupOptions: { - external: ["react", "react/jsx-runtime"], + external: ["react", "react-dom", "react/jsx-runtime"], output: { globals: { - react: "react", + react: "React", + "react-dom": "ReactDOM", "react/jsx-runtime": "jsxRuntime", }, - exports: "named", }, }, lib: { - entry: [`src/index.js`], + entry: "src/index.js", name: "ReactList", fileName: "react-list", + formats: ["es", "cjs"], }, }, }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0ee8495..a620d7b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,7 +12,7 @@ importers: devDependencies: vitepress: specifier: ^2.0.0-alpha.15 - version: 2.0.0-alpha.15(axios@1.11.0)(postcss@8.5.6) + version: 2.0.0-alpha.15(axios@1.11.0)(jiti@2.6.1)(lightningcss@1.32.0)(postcss@8.5.8)(typescript@5.9.3) apps/playground: dependencies: @@ -35,6 +35,9 @@ importers: '@eslint/js': specifier: ^9.22.0 version: 9.25.1 + '@tailwindcss/postcss': + specifier: ^4.2.2 + version: 4.2.2 '@types/react': specifier: ^19.0.10 version: 19.1.2 @@ -43,22 +46,74 @@ importers: version: 19.1.2(@types/react@19.1.2) '@vitejs/plugin-react': specifier: ^4.3.4 - version: 4.4.1(vite@6.3.3) + version: 4.4.1(vite@6.3.3(jiti@2.6.1)(lightningcss@1.32.0)) + autoprefixer: + specifier: ^10.4.27 + version: 10.4.27(postcss@8.5.8) eslint: specifier: ^9.22.0 - version: 9.25.1 + version: 9.25.1(jiti@2.6.1) eslint-plugin-react-hooks: specifier: ^5.2.0 - version: 5.2.0(eslint@9.25.1) + version: 5.2.0(eslint@9.25.1(jiti@2.6.1)) eslint-plugin-react-refresh: specifier: ^0.4.19 - version: 0.4.20(eslint@9.25.1) + version: 0.4.20(eslint@9.25.1(jiti@2.6.1)) globals: specifier: ^16.0.0 version: 16.0.0 + postcss: + specifier: ^8.5.8 + version: 8.5.8 + tailwindcss: + specifier: ^4.2.2 + version: 4.2.2 vite: specifier: ^6.3.1 - version: 6.3.3 + version: 6.3.3(jiti@2.6.1)(lightningcss@1.32.0) + + apps/playground-typescript: + dependencies: + '@7span/react-list': + specifier: workspace:* + version: link:../../packages/react-list + '@iconify/react': + specifier: ^5.2.1 + version: 5.2.1(react@19.1.0) + react: + specifier: ^19.0.0 + version: 19.1.0 + react-dom: + specifier: ^19.0.0 + version: 19.1.0(react@19.1.0) + devDependencies: + '@tailwindcss/postcss': + specifier: ^4.2.2 + version: 4.2.2 + '@types/react': + specifier: ^19.0.10 + version: 19.2.14 + '@types/react-dom': + specifier: ^19.0.4 + version: 19.2.3(@types/react@19.2.14) + '@vitejs/plugin-react': + specifier: ^4.3.4 + version: 4.4.1(vite@6.3.3(jiti@2.6.1)(lightningcss@1.32.0)) + autoprefixer: + specifier: ^10.4.27 + version: 10.4.27(postcss@8.5.8) + postcss: + specifier: ^8.5.8 + version: 8.5.8 + tailwindcss: + specifier: ^4.2.2 + version: 4.2.2 + typescript: + specifier: ^5.9.3 + version: 5.9.3 + vite: + specifier: ^6.3.1 + version: 6.3.3(jiti@2.6.1)(lightningcss@1.32.0) packages/react-list: dependencies: @@ -69,15 +124,28 @@ importers: specifier: ^18.2.0 || ^19.0.0 version: 19.1.0(react@19.1.0) devDependencies: + '@types/react': + specifier: ^19.1.13 + version: 19.2.14 + '@types/react-dom': + specifier: ^19.1.9 + version: 19.2.3(@types/react@19.2.14) '@vitejs/plugin-react': specifier: ^4.3.4 - version: 4.4.1(vite@6.3.3) + version: 4.4.1(vite@6.3.3(jiti@2.6.1)(lightningcss@1.32.0)) + typescript: + specifier: ^5.9.3 + version: 5.9.3 vite: specifier: ^6.3.1 - version: 6.3.3 + version: 6.3.3(jiti@2.6.1)(lightningcss@1.32.0) packages: + '@alloc/quick-lru@5.2.0': + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + '@ampproject/remapping@2.3.0': resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} @@ -407,6 +475,9 @@ packages: 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'} @@ -415,9 +486,6 @@ packages: resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} engines: {node: '>=6.0.0'} - '@jridgewell/sourcemap-codec@1.5.0': - resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} - '@jridgewell/sourcemap-codec@1.5.5': resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} @@ -661,6 +729,94 @@ packages: '@shikijs/vscode-textmate@10.0.2': resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + '@tailwindcss/node@4.2.2': + resolution: {integrity: sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA==} + + '@tailwindcss/oxide-android-arm64@4.2.2': + resolution: {integrity: sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.2.2': + resolution: {integrity: sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.2.2': + resolution: {integrity: sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw==} + engines: {node: '>= 20'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.2.2': + resolution: {integrity: sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ==} + engines: {node: '>= 20'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.2': + resolution: {integrity: sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ==} + engines: {node: '>= 20'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.2.2': + resolution: {integrity: sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-musl@4.2.2': + resolution: {integrity: sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-gnu@4.2.2': + resolution: {integrity: sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-musl@4.2.2': + resolution: {integrity: sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-wasm32-wasi@4.2.2': + resolution: {integrity: sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.2.2': + resolution: {integrity: sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.2.2': + resolution: {integrity: sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA==} + engines: {node: '>= 20'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.2.2': + resolution: {integrity: sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg==} + engines: {node: '>= 20'} + + '@tailwindcss/postcss@4.2.2': + resolution: {integrity: sha512-n4goKQbW8RVXIbNKRB/45LzyUqN451deQK0nzIeauVEqjlI49slUlgKYJM2QyUzap/PcpnS7kzSUmPb1sCRvYQ==} + '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} @@ -702,9 +858,17 @@ packages: peerDependencies: '@types/react': ^19.0.0 + '@types/react-dom@19.2.3': + resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} + peerDependencies: + '@types/react': ^19.2.0 + '@types/react@19.1.2': resolution: {integrity: sha512-oxLPMytKchWGbnQM9O7D67uPa9paTNxO7jVoNMXgkkErULBPhPARCfkKL9ytcIJJRGjbsVwW4ugJzyFFvm/Tiw==} + '@types/react@19.2.14': + resolution: {integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==} + '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} @@ -843,12 +1007,24 @@ packages: asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + autoprefixer@10.4.27: + resolution: {integrity: sha512-NP9APE+tO+LuJGn7/9+cohklunJsXWiaWEfV3si4Gi/XHDwVNgkwr1J3RQYFIvPy76GmJ9/bW8vyoU1LcxwKHA==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + axios@1.11.0: resolution: {integrity: sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==} balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + baseline-browser-mapping@2.10.9: + resolution: {integrity: sha512-OZd0e2mU11ClX8+IdXe3r0dbqMEznRiT4TfbhYIbcRPZkqJ7Qwer8ij3GZAmLsRKa+II9V1v5czCkvmHH3XZBg==} + engines: {node: '>=6.0.0'} + hasBin: true + birpc@2.9.0: resolution: {integrity: sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==} @@ -860,6 +1036,11 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true + browserslist@4.28.1: + resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} @@ -868,8 +1049,8 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - caniuse-lite@1.0.30001715: - resolution: {integrity: sha512-7ptkFGMm2OAOgvZpwgA4yjQ5SQbrNVGdRjzH0pBdy1Fasvcr+KAeECmbCAECzTuDuoX0FCY8KzUxjf9+9kfZEw==} + caniuse-lite@1.0.30001780: + resolution: {integrity: sha512-llngX0E7nQci5BPJDqoZSbuZ5Bcs9F5db7EtgfwBerX9XGtkkiO4NwfDDIRzHTTwcYC8vC7bmeUEPGrKlR/TkQ==} ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} @@ -915,6 +1096,9 @@ packages: csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + debug@4.4.0: resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} engines: {node: '>=6.0'} @@ -935,6 +1119,10 @@ packages: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} @@ -945,6 +1133,13 @@ packages: electron-to-chromium@1.5.141: resolution: {integrity: sha512-qS+qH9oqVYc1ooubTiB9l904WVyM6qNYxtOEEGReoZXw3xlqeYdFr5GclNzbkAufWgwWLEPoDi3d9MoRwwIjGw==} + electron-to-chromium@1.5.321: + resolution: {integrity: sha512-L2C7Q279W2D/J4PLZLk7sebOILDSWos7bMsMNN06rK482umHUrh/3lM8G7IlHFOYip2oAg5nha1rCMxr/rs6ZQ==} + + enhanced-resolve@5.20.1: + resolution: {integrity: sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==} + engines: {node: '>=10.13.0'} + entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} @@ -1091,6 +1286,9 @@ packages: resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} engines: {node: '>= 6'} + fraction.js@5.3.4: + resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==} + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -1131,6 +1329,9 @@ packages: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} @@ -1189,6 +1390,10 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + jiti@2.6.1: + resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} + hasBin: true + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -1222,6 +1427,76 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} + lightningcss-android-arm64@1.32.0: + resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.32.0: + resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.32.0: + resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.32.0: + resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.32.0: + resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.32.0: + resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.32.0: + resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.32.0: + resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.32.0: + resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-win32-arm64-msvc@1.32.0: + resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.32.0: + resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.32.0: + resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} + engines: {node: '>= 12.0.0'} + locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} @@ -1291,6 +1566,9 @@ packages: node-releases@2.0.19: resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} + node-releases@2.0.36: + resolution: {integrity: sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==} + oniguruma-parser@0.12.1: resolution: {integrity: sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==} @@ -1335,12 +1613,11 @@ packages: resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} - postcss@8.5.3: - resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==} - engines: {node: ^10 || ^12 || >=14} + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} - postcss@8.5.6: - resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + postcss@8.5.8: + resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==} engines: {node: ^10 || ^12 || >=14} prelude-ls@1.2.1: @@ -1443,6 +1720,13 @@ packages: tabbable@6.3.0: resolution: {integrity: sha512-EIHvdY5bPLuWForiR/AN2Bxngzpuwn1is4asboytXtpTgsArc+WmSJKVLlhdh71u7jFcryDqB2A8lQvj78MkyQ==} + tailwindcss@4.2.2: + resolution: {integrity: sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==} + + tapable@2.3.0: + resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} + engines: {node: '>=6'} + tinyglobby@0.2.13: resolution: {integrity: sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==} engines: {node: '>=12.0.0'} @@ -1458,6 +1742,11 @@ packages: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + unist-util-is@6.0.1: resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} @@ -1479,6 +1768,12 @@ packages: peerDependencies: browserslist: '>= 4.21.0' + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} @@ -1612,6 +1907,8 @@ packages: snapshots: + '@alloc/quick-lru@5.2.0': {} + '@ampproject/remapping@2.3.0': dependencies: '@jridgewell/gen-mapping': 0.3.8 @@ -1821,9 +2118,9 @@ snapshots: '@esbuild/win32-x64@0.25.3': optional: true - '@eslint-community/eslint-utils@4.6.1(eslint@9.25.1)': + '@eslint-community/eslint-utils@4.6.1(eslint@9.25.1(jiti@2.6.1))': dependencies: - eslint: 9.25.1 + eslint: 9.25.1(jiti@2.6.1) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.1': {} @@ -1892,21 +2189,24 @@ snapshots: '@jridgewell/gen-mapping@0.3.8': dependencies: '@jridgewell/set-array': 1.2.1 - '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/sourcemap-codec': 1.5.5 + '@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/sourcemap-codec': 1.5.5 '@rolldown/pluginutils@1.0.0-beta.53': {} @@ -2074,6 +2374,75 @@ snapshots: '@shikijs/vscode-textmate@10.0.2': {} + '@tailwindcss/node@4.2.2': + dependencies: + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.20.1 + jiti: 2.6.1 + lightningcss: 1.32.0 + magic-string: 0.30.21 + source-map-js: 1.2.1 + tailwindcss: 4.2.2 + + '@tailwindcss/oxide-android-arm64@4.2.2': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.2.2': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.2.2': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.2.2': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.2': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.2.2': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.2.2': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.2.2': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.2.2': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.2.2': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.2.2': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.2.2': + optional: true + + '@tailwindcss/oxide@4.2.2': + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.2.2 + '@tailwindcss/oxide-darwin-arm64': 4.2.2 + '@tailwindcss/oxide-darwin-x64': 4.2.2 + '@tailwindcss/oxide-freebsd-x64': 4.2.2 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.2.2 + '@tailwindcss/oxide-linux-arm64-gnu': 4.2.2 + '@tailwindcss/oxide-linux-arm64-musl': 4.2.2 + '@tailwindcss/oxide-linux-x64-gnu': 4.2.2 + '@tailwindcss/oxide-linux-x64-musl': 4.2.2 + '@tailwindcss/oxide-wasm32-wasi': 4.2.2 + '@tailwindcss/oxide-win32-arm64-msvc': 4.2.2 + '@tailwindcss/oxide-win32-x64-msvc': 4.2.2 + + '@tailwindcss/postcss@4.2.2': + dependencies: + '@alloc/quick-lru': 5.2.0 + '@tailwindcss/node': 4.2.2 + '@tailwindcss/oxide': 4.2.2 + postcss: 8.5.8 + tailwindcss: 4.2.2 + '@types/babel__core@7.20.5': dependencies: '@babel/parser': 7.27.0 @@ -2122,32 +2491,40 @@ snapshots: dependencies: '@types/react': 19.1.2 + '@types/react-dom@19.2.3(@types/react@19.2.14)': + dependencies: + '@types/react': 19.2.14 + '@types/react@19.1.2': dependencies: csstype: 3.1.3 + '@types/react@19.2.14': + dependencies: + csstype: 3.2.3 + '@types/unist@3.0.3': {} '@types/web-bluetooth@0.0.21': {} '@ungap/structured-clone@1.3.0': {} - '@vitejs/plugin-react@4.4.1(vite@6.3.3)': + '@vitejs/plugin-react@4.4.1(vite@6.3.3(jiti@2.6.1)(lightningcss@1.32.0))': dependencies: '@babel/core': 7.26.10 '@babel/plugin-transform-react-jsx-self': 7.25.9(@babel/core@7.26.10) '@babel/plugin-transform-react-jsx-source': 7.25.9(@babel/core@7.26.10) '@types/babel__core': 7.20.5 react-refresh: 0.17.0 - vite: 6.3.3 + vite: 6.3.3(jiti@2.6.1)(lightningcss@1.32.0) transitivePeerDependencies: - supports-color - '@vitejs/plugin-vue@6.0.3(vite@7.2.7)(vue@3.5.25)': + '@vitejs/plugin-vue@6.0.3(vite@7.2.7(jiti@2.6.1)(lightningcss@1.32.0))(vue@3.5.25(typescript@5.9.3))': dependencies: '@rolldown/pluginutils': 1.0.0-beta.53 - vite: 7.2.7 - vue: 3.5.25 + vite: 7.2.7(jiti@2.6.1)(lightningcss@1.32.0) + vue: 3.5.25(typescript@5.9.3) '@vue/compiler-core@3.5.25': dependencies: @@ -2171,7 +2548,7 @@ snapshots: '@vue/shared': 3.5.25 estree-walker: 2.0.2 magic-string: 0.30.21 - postcss: 8.5.6 + postcss: 8.5.8 source-map-js: 1.2.1 '@vue/compiler-ssr@3.5.25': @@ -2213,35 +2590,35 @@ snapshots: '@vue/shared': 3.5.25 csstype: 3.1.3 - '@vue/server-renderer@3.5.25(vue@3.5.25)': + '@vue/server-renderer@3.5.25(vue@3.5.25(typescript@5.9.3))': dependencies: '@vue/compiler-ssr': 3.5.25 '@vue/shared': 3.5.25 - vue: 3.5.25 + vue: 3.5.25(typescript@5.9.3) '@vue/shared@3.5.25': {} - '@vueuse/core@14.1.0(vue@3.5.25)': + '@vueuse/core@14.1.0(vue@3.5.25(typescript@5.9.3))': dependencies: '@types/web-bluetooth': 0.0.21 '@vueuse/metadata': 14.1.0 - '@vueuse/shared': 14.1.0(vue@3.5.25) - vue: 3.5.25 + '@vueuse/shared': 14.1.0(vue@3.5.25(typescript@5.9.3)) + vue: 3.5.25(typescript@5.9.3) - '@vueuse/integrations@14.1.0(axios@1.11.0)(focus-trap@7.6.6)(vue@3.5.25)': + '@vueuse/integrations@14.1.0(axios@1.11.0)(focus-trap@7.6.6)(vue@3.5.25(typescript@5.9.3))': dependencies: - '@vueuse/core': 14.1.0(vue@3.5.25) - '@vueuse/shared': 14.1.0(vue@3.5.25) - vue: 3.5.25 + '@vueuse/core': 14.1.0(vue@3.5.25(typescript@5.9.3)) + '@vueuse/shared': 14.1.0(vue@3.5.25(typescript@5.9.3)) + vue: 3.5.25(typescript@5.9.3) optionalDependencies: axios: 1.11.0 focus-trap: 7.6.6 '@vueuse/metadata@14.1.0': {} - '@vueuse/shared@14.1.0(vue@3.5.25)': + '@vueuse/shared@14.1.0(vue@3.5.25(typescript@5.9.3))': dependencies: - vue: 3.5.25 + vue: 3.5.25(typescript@5.9.3) acorn-jsx@5.3.2(acorn@8.14.1): dependencies: @@ -2264,6 +2641,15 @@ snapshots: asynckit@0.4.0: {} + autoprefixer@10.4.27(postcss@8.5.8): + dependencies: + browserslist: 4.28.1 + caniuse-lite: 1.0.30001780 + fraction.js: 5.3.4 + picocolors: 1.1.1 + postcss: 8.5.8 + postcss-value-parser: 4.2.0 + axios@1.11.0: dependencies: follow-redirects: 1.15.11 @@ -2274,6 +2660,8 @@ snapshots: balanced-match@1.0.2: {} + baseline-browser-mapping@2.10.9: {} + birpc@2.9.0: {} brace-expansion@1.1.11: @@ -2283,11 +2671,19 @@ snapshots: browserslist@4.24.4: dependencies: - caniuse-lite: 1.0.30001715 + caniuse-lite: 1.0.30001780 electron-to-chromium: 1.5.141 node-releases: 2.0.19 update-browserslist-db: 1.1.3(browserslist@4.24.4) + browserslist@4.28.1: + dependencies: + baseline-browser-mapping: 2.10.9 + caniuse-lite: 1.0.30001780 + electron-to-chromium: 1.5.321 + node-releases: 2.0.36 + update-browserslist-db: 1.2.3(browserslist@4.28.1) + call-bind-apply-helpers@1.0.2: dependencies: es-errors: 1.3.0 @@ -2295,7 +2691,7 @@ snapshots: callsites@3.1.0: {} - caniuse-lite@1.0.30001715: {} + caniuse-lite@1.0.30001780: {} ccount@2.0.1: {} @@ -2336,6 +2732,8 @@ snapshots: csstype@3.1.3: {} + csstype@3.2.3: {} + debug@4.4.0: dependencies: ms: 2.1.3 @@ -2346,6 +2744,8 @@ snapshots: dequal@2.0.3: {} + detect-libc@2.1.2: {} + devlop@1.1.0: dependencies: dequal: 2.0.3 @@ -2358,6 +2758,13 @@ snapshots: electron-to-chromium@1.5.141: {} + electron-to-chromium@1.5.321: {} + + enhanced-resolve@5.20.1: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.0 + entities@4.5.0: {} es-define-property@1.0.1: {} @@ -2407,13 +2814,13 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-plugin-react-hooks@5.2.0(eslint@9.25.1): + eslint-plugin-react-hooks@5.2.0(eslint@9.25.1(jiti@2.6.1)): dependencies: - eslint: 9.25.1 + eslint: 9.25.1(jiti@2.6.1) - eslint-plugin-react-refresh@0.4.20(eslint@9.25.1): + eslint-plugin-react-refresh@0.4.20(eslint@9.25.1(jiti@2.6.1)): dependencies: - eslint: 9.25.1 + eslint: 9.25.1(jiti@2.6.1) eslint-scope@8.3.0: dependencies: @@ -2424,9 +2831,9 @@ snapshots: eslint-visitor-keys@4.2.0: {} - eslint@9.25.1: + eslint@9.25.1(jiti@2.6.1): dependencies: - '@eslint-community/eslint-utils': 4.6.1(eslint@9.25.1) + '@eslint-community/eslint-utils': 4.6.1(eslint@9.25.1(jiti@2.6.1)) '@eslint-community/regexpp': 4.12.1 '@eslint/config-array': 0.20.0 '@eslint/config-helpers': 0.2.1 @@ -2461,6 +2868,8 @@ snapshots: minimatch: 3.1.2 natural-compare: 1.4.0 optionator: 0.9.4 + optionalDependencies: + jiti: 2.6.1 transitivePeerDependencies: - supports-color @@ -2528,6 +2937,8 @@ snapshots: hasown: 2.0.2 mime-types: 2.1.35 + fraction.js@5.3.4: {} + fsevents@2.3.3: optional: true @@ -2565,6 +2976,8 @@ snapshots: gopd@1.2.0: {} + graceful-fs@4.2.11: {} + has-flag@4.0.0: {} has-symbols@1.1.0: {} @@ -2620,6 +3033,8 @@ snapshots: isexe@2.0.0: {} + jiti@2.6.1: {} + js-tokens@4.0.0: {} js-yaml@4.1.0: @@ -2645,6 +3060,55 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 + lightningcss-android-arm64@1.32.0: + optional: true + + lightningcss-darwin-arm64@1.32.0: + optional: true + + lightningcss-darwin-x64@1.32.0: + optional: true + + lightningcss-freebsd-x64@1.32.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.32.0: + optional: true + + lightningcss-linux-arm64-gnu@1.32.0: + optional: true + + lightningcss-linux-arm64-musl@1.32.0: + optional: true + + lightningcss-linux-x64-gnu@1.32.0: + optional: true + + lightningcss-linux-x64-musl@1.32.0: + optional: true + + lightningcss-win32-arm64-msvc@1.32.0: + optional: true + + lightningcss-win32-x64-msvc@1.32.0: + optional: true + + lightningcss@1.32.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.32.0 + lightningcss-darwin-arm64: 1.32.0 + lightningcss-darwin-x64: 1.32.0 + lightningcss-freebsd-x64: 1.32.0 + lightningcss-linux-arm-gnueabihf: 1.32.0 + lightningcss-linux-arm64-gnu: 1.32.0 + lightningcss-linux-arm64-musl: 1.32.0 + lightningcss-linux-x64-gnu: 1.32.0 + lightningcss-linux-x64-musl: 1.32.0 + lightningcss-win32-arm64-msvc: 1.32.0 + lightningcss-win32-x64-msvc: 1.32.0 + locate-path@6.0.0: dependencies: p-locate: 5.0.0 @@ -2714,6 +3178,8 @@ snapshots: node-releases@2.0.19: {} + node-releases@2.0.36: {} + oniguruma-parser@0.12.1: {} oniguruma-to-es@4.3.4: @@ -2755,13 +3221,9 @@ snapshots: picomatch@4.0.3: {} - postcss@8.5.3: - dependencies: - nanoid: 3.3.11 - picocolors: 1.1.1 - source-map-js: 1.2.1 + postcss-value-parser@4.2.0: {} - postcss@8.5.6: + postcss@8.5.8: dependencies: nanoid: 3.3.11 picocolors: 1.1.1 @@ -2896,6 +3358,10 @@ snapshots: tabbable@6.3.0: {} + tailwindcss@4.2.2: {} + + tapable@2.3.0: {} + tinyglobby@0.2.13: dependencies: fdir: 6.4.4(picomatch@4.0.2) @@ -2912,6 +3378,8 @@ snapshots: dependencies: prelude-ls: 1.2.1 + typescript@5.9.3: {} + unist-util-is@6.0.1: dependencies: '@types/unist': 3.0.3 @@ -2941,6 +3409,12 @@ snapshots: escalade: 3.2.0 picocolors: 1.1.1 + update-browserslist-db@1.2.3(browserslist@4.28.1): + dependencies: + browserslist: 4.28.1 + escalade: 3.2.0 + picocolors: 1.1.1 + uri-js@4.4.1: dependencies: punycode: 2.3.1 @@ -2955,29 +3429,33 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite@6.3.3: + vite@6.3.3(jiti@2.6.1)(lightningcss@1.32.0): dependencies: esbuild: 0.25.3 fdir: 6.4.4(picomatch@4.0.2) picomatch: 4.0.2 - postcss: 8.5.3 + postcss: 8.5.8 rollup: 4.40.0 tinyglobby: 0.2.13 optionalDependencies: fsevents: 2.3.3 + jiti: 2.6.1 + lightningcss: 1.32.0 - vite@7.2.7: + vite@7.2.7(jiti@2.6.1)(lightningcss@1.32.0): dependencies: esbuild: 0.25.3 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 - postcss: 8.5.6 + postcss: 8.5.8 rollup: 4.53.3 tinyglobby: 0.2.15 optionalDependencies: fsevents: 2.3.3 + jiti: 2.6.1 + lightningcss: 1.32.0 - vitepress@2.0.0-alpha.15(axios@1.11.0)(postcss@8.5.6): + vitepress@2.0.0-alpha.15(axios@1.11.0)(jiti@2.6.1)(lightningcss@1.32.0)(postcss@8.5.8)(typescript@5.9.3): dependencies: '@docsearch/css': 4.3.2 '@docsearch/js': 4.3.2 @@ -2986,19 +3464,19 @@ snapshots: '@shikijs/transformers': 3.20.0 '@shikijs/types': 3.20.0 '@types/markdown-it': 14.1.2 - '@vitejs/plugin-vue': 6.0.3(vite@7.2.7)(vue@3.5.25) + '@vitejs/plugin-vue': 6.0.3(vite@7.2.7(jiti@2.6.1)(lightningcss@1.32.0))(vue@3.5.25(typescript@5.9.3)) '@vue/devtools-api': 8.0.5 '@vue/shared': 3.5.25 - '@vueuse/core': 14.1.0(vue@3.5.25) - '@vueuse/integrations': 14.1.0(axios@1.11.0)(focus-trap@7.6.6)(vue@3.5.25) + '@vueuse/core': 14.1.0(vue@3.5.25(typescript@5.9.3)) + '@vueuse/integrations': 14.1.0(axios@1.11.0)(focus-trap@7.6.6)(vue@3.5.25(typescript@5.9.3)) focus-trap: 7.6.6 mark.js: 8.11.1 minisearch: 7.2.0 shiki: 3.20.0 - vite: 7.2.7 - vue: 3.5.25 + vite: 7.2.7(jiti@2.6.1)(lightningcss@1.32.0) + vue: 3.5.25(typescript@5.9.3) optionalDependencies: - postcss: 8.5.6 + postcss: 8.5.8 transitivePeerDependencies: - '@types/node' - async-validator @@ -3024,13 +3502,15 @@ snapshots: - universal-cookie - yaml - vue@3.5.25: + vue@3.5.25(typescript@5.9.3): dependencies: '@vue/compiler-dom': 3.5.25 '@vue/compiler-sfc': 3.5.25 '@vue/runtime-dom': 3.5.25 - '@vue/server-renderer': 3.5.25(vue@3.5.25) + '@vue/server-renderer': 3.5.25(vue@3.5.25(typescript@5.9.3)) '@vue/shared': 3.5.25 + optionalDependencies: + typescript: 5.9.3 which@2.0.2: dependencies: