@marktiderman/genesis-ui 2.7.0 → 2.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/EXTENDING.md +40 -1
  2. package/SKILL.md +198 -9
  3. package/dist/{AppShell-CX0M-dfc.d.ts → AppShell-LXQykEqe.d.ts} +47 -5
  4. package/dist/{chunk-M62CHO3N.js → chunk-4C4ESW6O.js} +9 -4
  5. package/dist/chunk-4C4ESW6O.js.map +1 -0
  6. package/dist/chunk-DIV4BYXX.js +58 -0
  7. package/dist/chunk-DIV4BYXX.js.map +1 -0
  8. package/dist/chunk-FUVWKTPL.js +19 -0
  9. package/dist/chunk-FUVWKTPL.js.map +1 -0
  10. package/dist/{chunk-PLIYF6KI.js → chunk-MPI33I4K.js} +2 -2
  11. package/dist/{chunk-PLIYF6KI.js.map → chunk-MPI33I4K.js.map} +1 -1
  12. package/dist/{chunk-JA3ZKULS.js → chunk-OITWCIG2.js} +3 -3
  13. package/dist/chunk-OITWCIG2.js.map +1 -0
  14. package/dist/chunk-PKFDGGZV.js +65 -0
  15. package/dist/chunk-PKFDGGZV.js.map +1 -0
  16. package/dist/{chunk-IXGAEECT.js → chunk-RKPS7ERS.js} +9 -5
  17. package/dist/chunk-RKPS7ERS.js.map +1 -0
  18. package/dist/{chunk-TXOZMKNW.js → chunk-S7NXD37O.js} +27 -26
  19. package/dist/chunk-S7NXD37O.js.map +1 -0
  20. package/dist/{chunk-ZPPLUGLA.js → chunk-UYI3GLFJ.js} +162 -19
  21. package/dist/chunk-UYI3GLFJ.js.map +1 -0
  22. package/dist/{chunk-CDYZRZ6U.js → chunk-WGMEVX3Y.js} +2 -2
  23. package/dist/chunk-WGMEVX3Y.js.map +1 -0
  24. package/dist/chunk-ZDHWNQTR.js +552 -0
  25. package/dist/chunk-ZDHWNQTR.js.map +1 -0
  26. package/dist/components/data/index.d.ts +306 -19
  27. package/dist/components/data/index.js +1177 -284
  28. package/dist/components/data/index.js.map +1 -1
  29. package/dist/components/layout/form-page.d.ts +1 -1
  30. package/dist/components/layout/form-page.js +1 -1
  31. package/dist/components/layout/index.d.ts +2 -2
  32. package/dist/components/layout/index.js +8 -8
  33. package/dist/components/patterns/index.d.ts +1 -1
  34. package/dist/components/patterns/index.js +5 -5
  35. package/dist/components/patterns/sortable-list.js +1 -1
  36. package/dist/components/rich-text/rich-text-editor.d.ts +194 -0
  37. package/dist/components/rich-text/rich-text-editor.js +833 -0
  38. package/dist/components/rich-text/rich-text-editor.js.map +1 -0
  39. package/dist/components/rich-text/rich-text.d.ts +51 -0
  40. package/dist/components/rich-text/rich-text.js +4 -0
  41. package/dist/components/rich-text/rich-text.js.map +1 -0
  42. package/dist/components/ui/alert-dialog.d.ts +12 -1
  43. package/dist/components/ui/alert-dialog.js +1 -1
  44. package/dist/hooks/index.d.ts +4 -3
  45. package/dist/hooks/index.js +5 -4
  46. package/dist/hooks/use-data-filters.d.ts +5 -3
  47. package/dist/hooks/use-data-filters.js +2 -2
  48. package/dist/hooks/use-resource-page.d.ts +2 -1
  49. package/dist/hooks/use-resource-page.js +3 -2
  50. package/dist/hooks/use-resource-url-state.d.ts +68 -0
  51. package/dist/hooks/use-resource-url-state.js +3 -0
  52. package/dist/hooks/use-resource-url-state.js.map +1 -0
  53. package/dist/hooks/use-view-preference.d.ts +17 -2
  54. package/dist/hooks/use-view-preference.js +2 -1
  55. package/dist/index.d.ts +2 -2
  56. package/dist/index.js +32 -32
  57. package/dist/{navigation-DBwcZ6vb.d.ts → navigation-Bha0R3Fu.d.ts} +6 -3
  58. package/dist/rich-text-doc-C54fME2C.d.ts +180 -0
  59. package/dist/router/react-router.d.ts +47 -0
  60. package/dist/router/react-router.js +53 -0
  61. package/dist/router/react-router.js.map +1 -0
  62. package/dist/router/tanstack-router.d.ts +47 -0
  63. package/dist/router/tanstack-router.js +105 -0
  64. package/dist/router/tanstack-router.js.map +1 -0
  65. package/dist/{use-resource-page-Dd2VUv30.d.ts → use-resource-page-BDnnCssE.d.ts} +83 -5
  66. package/package.json +61 -14
  67. package/dist/chunk-CDYZRZ6U.js.map +0 -1
  68. package/dist/chunk-FTTNGGLA.js +0 -20
  69. package/dist/chunk-FTTNGGLA.js.map +0 -1
  70. package/dist/chunk-IXGAEECT.js.map +0 -1
  71. package/dist/chunk-JA3ZKULS.js.map +0 -1
  72. package/dist/chunk-M62CHO3N.js.map +0 -1
  73. package/dist/chunk-TXOZMKNW.js.map +0 -1
  74. package/dist/chunk-ZPPLUGLA.js.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marktiderman/genesis-ui",
3
- "version": "2.7.0",
3
+ "version": "2.9.0",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -62,6 +62,26 @@
62
62
  "types": "./dist/provider/index.d.ts",
63
63
  "import": "./dist/provider/index.js"
64
64
  },
65
+ "./sheet-grid": {
66
+ "types": "./dist/components/sheet-grid/sheet-grid.d.ts",
67
+ "import": "./dist/components/sheet-grid/sheet-grid.js"
68
+ },
69
+ "./rich-text": {
70
+ "types": "./dist/components/rich-text/rich-text.d.ts",
71
+ "import": "./dist/components/rich-text/rich-text.js"
72
+ },
73
+ "./rich-text-editor": {
74
+ "types": "./dist/components/rich-text/rich-text-editor.d.ts",
75
+ "import": "./dist/components/rich-text/rich-text-editor.js"
76
+ },
77
+ "./router/react-router": {
78
+ "types": "./dist/router/react-router.d.ts",
79
+ "import": "./dist/router/react-router.js"
80
+ },
81
+ "./router/tanstack-router": {
82
+ "types": "./dist/router/tanstack-router.d.ts",
83
+ "import": "./dist/router/tanstack-router.js"
84
+ },
65
85
  "./styles.css": "./dist/styles.css",
66
86
  "./accessible-icon": {
67
87
  "types": "./dist/components/ui/accessible-icon.d.ts",
@@ -255,18 +275,10 @@
255
275
  "types": "./dist/components/patterns/settings-row.d.ts",
256
276
  "import": "./dist/components/patterns/settings-row.js"
257
277
  },
258
- "./sortable-list": {
259
- "types": "./dist/components/patterns/sortable-list.d.ts",
260
- "import": "./dist/components/patterns/sortable-list.js"
261
- },
262
278
  "./sheet": {
263
279
  "types": "./dist/components/ui/sheet.d.ts",
264
280
  "import": "./dist/components/ui/sheet.js"
265
281
  },
266
- "./sheet-grid": {
267
- "types": "./dist/components/sheet-grid/sheet-grid.d.ts",
268
- "import": "./dist/components/sheet-grid/sheet-grid.js"
269
- },
270
282
  "./skeleton": {
271
283
  "types": "./dist/components/ui/skeleton.d.ts",
272
284
  "import": "./dist/components/ui/skeleton.js"
@@ -279,6 +291,10 @@
279
291
  "types": "./dist/components/ui/sonner.d.ts",
280
292
  "import": "./dist/components/ui/sonner.js"
281
293
  },
294
+ "./sortable-list": {
295
+ "types": "./dist/components/patterns/sortable-list.d.ts",
296
+ "import": "./dist/components/patterns/sortable-list.js"
297
+ },
282
298
  "./spinner": {
283
299
  "types": "./dist/components/ui/spinner.d.ts",
284
300
  "import": "./dist/components/ui/spinner.js"
@@ -338,7 +354,12 @@
338
354
  },
339
355
  "peerDependencies": {
340
356
  "@marktiderman/genesis-design-system": "^1.3.1",
341
- "@tanstack/react-virtual": "^3.14.0",
357
+ "@tanstack/react-router": "^1.0.0",
358
+ "@tiptap/core": "^3.31.0",
359
+ "@tiptap/extensions": "^3.31.0",
360
+ "@tiptap/pm": "^3.31.0",
361
+ "@tiptap/react": "^3.31.0",
362
+ "@tiptap/starter-kit": "^3.31.0",
342
363
  "cmdk": "^1.0.0",
343
364
  "date-fns": "^4.0.0",
344
365
  "embla-carousel-react": "^8.0.0",
@@ -351,12 +372,15 @@
351
372
  "react-dropzone": "^14.0.0 || ^15.0.0",
352
373
  "react-hook-form": "^7.0.0",
353
374
  "react-resizable-panels": "^2.0.0",
375
+ "react-router": "^7.0.0",
354
376
  "recharts": "^2.12.0 || ^3.0.0",
355
377
  "sonner": "^1.0.0 || ^2.0.0",
356
378
  "vaul": "^1.0.0"
357
379
  },
358
380
  "dependencies": {
359
- "@marktiderman/genesis-core": "^1.3.0",
381
+ "@marktiderman/genesis-core": "^1.5.0",
382
+ "@tanstack/react-table": "^8.21.3",
383
+ "@tanstack/react-virtual": "^3.14.0",
360
384
  "class-variance-authority": "^0.7.1",
361
385
  "clsx": "^2.1.1",
362
386
  "tailwind-merge": "^3.5.0"
@@ -379,9 +403,13 @@
379
403
  "@radix-ui/react-toggle": "^1.1.0",
380
404
  "@radix-ui/react-toggle-group": "^1.1.0",
381
405
  "@tanstack/react-query": "^5.75.0",
382
- "@tanstack/react-table": "^8.21.3",
383
- "@tanstack/react-virtual": "3.14.13",
406
+ "@tanstack/react-router": "1.170.41",
384
407
  "@testing-library/react": "^16.3.2",
408
+ "@tiptap/core": "3.31.4",
409
+ "@tiptap/extensions": "3.31.4",
410
+ "@tiptap/pm": "3.31.4",
411
+ "@tiptap/react": "3.31.4",
412
+ "@tiptap/starter-kit": "3.31.4",
385
413
  "@types/react": "^19.2.14",
386
414
  "@types/react-dom": "^19.2.3",
387
415
  "date-fns": "^4.1.0",
@@ -395,6 +423,7 @@
395
423
  "react-dropzone": "^15.0.0",
396
424
  "react-hook-form": "^7.72.1",
397
425
  "react-resizable-panels": "^2.0.0",
426
+ "react-router": "7.14.0",
398
427
  "recharts": "^2.15.0",
399
428
  "sonner": "^1.0.0",
400
429
  "tsup": "^8.3.0",
@@ -404,7 +433,25 @@
404
433
  "zod": "^4.3.6"
405
434
  },
406
435
  "peerDependenciesMeta": {
407
- "@tanstack/react-virtual": {
436
+ "@tanstack/react-router": {
437
+ "optional": true
438
+ },
439
+ "@tiptap/core": {
440
+ "optional": true
441
+ },
442
+ "@tiptap/extensions": {
443
+ "optional": true
444
+ },
445
+ "@tiptap/pm": {
446
+ "optional": true
447
+ },
448
+ "@tiptap/react": {
449
+ "optional": true
450
+ },
451
+ "@tiptap/starter-kit": {
452
+ "optional": true
453
+ },
454
+ "react-router": {
408
455
  "optional": true
409
456
  }
410
457
  },
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/navigation.tsx"],"names":["trail"],"mappings":"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","file":"chunk-CDYZRZ6U.js","sourcesContent":["\"use client\";\n\n/**\n * Router-agnostic navigation primitives.\n *\n * genesis-ui does NOT depend on any router. Components that need to render a\n * link or navigate imperatively accept an injectable abstraction defined here,\n * and default to plain browser behavior (`<a>` / `window.location`). Consumers\n * on React Router or Next.js inject a thin adapter that maps these props to\n * their own `<Link>` / router.\n */\nimport {\n useCallback,\n useEffect,\n useState,\n type AnchorHTMLAttributes,\n type ComponentType,\n type ReactNode,\n} from \"react\";\n\n// ---------------------------------------------------------------------------\n// Link abstraction\n// ---------------------------------------------------------------------------\n\n/**\n * Props passed to an injectable link component. The contract is a plain anchor\n * (`href`), so the default works with NO router. A React Router consumer maps\n * `href` → `to`; a Next.js consumer passes `next/link` directly (it already\n * accepts `href`, `className`, `children`).\n *\n * @stability Beta\n */\nexport interface GenesisLinkProps {\n /** Destination path or URL. */\n href: string;\n className?: string;\n onClick?: () => void;\n children?: ReactNode;\n /** Set to \"page\" when the link points at the active route. */\n \"aria-current\"?: AnchorHTMLAttributes<HTMLAnchorElement>[\"aria-current\"];\n /** Accessible name — useful when the visible label is hidden (icon rail). */\n \"aria-label\"?: string;\n /** Native tooltip / title attribute. */\n title?: string;\n /**\n * Stable test/automation selector. {@link DefaultLink} renders it as\n * `data-testid`; injected router adapters (`next/link`, RR `Link`) forward it\n * to the underlying `<a>` too.\n */\n testID?: string;\n}\n\n/**\n * A component that renders a navigable link from {@link GenesisLinkProps}.\n *\n * @stability Beta\n */\nexport type LinkComponent = ComponentType<GenesisLinkProps>;\n\n/**\n * Default link — a plain `<a>`. No router required.\n *\n * @stability Beta\n */\nexport const DefaultLink: LinkComponent = ({\n href,\n children,\n testID,\n ...rest\n}) => (\n <a href={href} data-testid={testID} {...rest}>\n {children}\n </a>\n);\n\n/**\n * Split a href into its path (trailing slash dropped, except for `/`), query\n * (no `?`) and hash (no `#`).\n */\nfunction splitHref(href: string): { path: string; search: string; hash: string } {\n const hashAt = href.indexOf(\"#\");\n const hash = hashAt === -1 ? \"\" : href.slice(hashAt + 1);\n const beforeHash = hashAt === -1 ? href : href.slice(0, hashAt);\n const searchAt = beforeHash.indexOf(\"?\");\n const rawPath = searchAt === -1 ? beforeHash : beforeHash.slice(0, searchAt);\n return {\n path: rawPath.length > 1 ? rawPath.replace(/\\/+$/, \"\") || \"/\" : rawPath,\n search: searchAt === -1 ? \"\" : beforeHash.slice(searchAt + 1),\n hash,\n };\n}\n\n/**\n * Options for {@link isPathActive}.\n *\n * @stability Beta\n */\nexport interface IsPathActiveOptions {\n /** Require an exact path match instead of a prefix match (like `NavLink end`). */\n end?: boolean;\n}\n\n/**\n * Default active-path matcher, mirroring React Router's `NavLink` (prefix\n * match, with `/` treated as exact). Consumers can override via an `isActive`\n * prop where components accept one.\n *\n * `activePath` may carry a query and hash (`pathname + search + hash`). An\n * item whose `to` has neither compares against the path part only, exactly as\n * before. An item that opts in by putting a query or hash in its `to`\n * (`/admin?tab=users`, `/settings#profile`) is active only when the path\n * matches exactly AND every query param it names has the same value in\n * `activePath` (extra params such as `&page=2` are ignored) AND, if it names a\n * hash, the hash is equal.\n *\n * @stability Beta\n */\nexport function isPathActive(\n to: string,\n activePath?: string,\n options?: IsPathActiveOptions,\n): boolean {\n if (activePath == null) return false;\n if (to === activePath) return true;\n const target = splitHref(to);\n const current = splitHref(activePath);\n\n if (!target.search && !target.hash) {\n if (target.path === current.path) return true;\n if (options?.end) return false;\n return target.path !== \"/\" && current.path.startsWith(target.path + \"/\");\n }\n\n if (target.path !== current.path) return false;\n if (target.hash && target.hash !== current.hash) return false;\n if (target.search) {\n const want = new URLSearchParams(target.search);\n const have = new URLSearchParams(current.search);\n for (const key of new Set(want.keys())) {\n const wanted = want.getAll(key);\n const had = have.getAll(key);\n if (!wanted.every((value) => had.includes(value))) return false;\n }\n }\n return true;\n}\n\n// ---------------------------------------------------------------------------\n// Hierarchical navigation resolver\n// ---------------------------------------------------------------------------\n\n/**\n * One node of a logical navigation hierarchy. `path` is a route pattern whose\n * segments are either static (`clients`) or named params (`$clientId`). A\n * node's place in the tree is its LOGICAL parent, which need not share its URL\n * prefix — `/clients/$clientId` can sit under `/dashboard`.\n *\n * @stability Beta\n */\nexport interface NavigationNode<T> {\n id: string;\n path: string;\n label: string;\n /** Contextual nav items to show while this node is current. */\n items: T[];\n children?: NavigationNode<T>[];\n}\n\n/**\n * Result of {@link resolveNavigation}. `current`, `parent` and every `trail`\n * entry are shallow copies of their tree nodes with `path` interpolated from\n * `params` (a param the URL did not supply stays as `$name`).\n *\n * @stability Beta\n */\nexport interface ResolvedNavigation<T> {\n /** The best-matching node — the root when nothing else matches. */\n current: NavigationNode<T>;\n /** `current`'s logical parent, for an \"up\" / back-to-parent link. */\n parent?: NavigationNode<T>;\n /** `current.items`. */\n items: T[];\n /** Root → `current`, inclusive. */\n trail: NavigationNode<T>[];\n /** Params captured by `current.path`, URL-decoded. */\n params: Record<string, string>;\n}\n\nfunction pathSegments(path: string): string[] {\n return splitHref(path).path.split(\"/\").filter(Boolean);\n}\n\nfunction decodeSegment(segment: string): string {\n try {\n return decodeURIComponent(segment);\n } catch {\n return segment;\n }\n}\n\n/**\n * Resolve where `pathname` sits in a logical navigation tree. Pure: it never\n * reads browser history or `window`.\n *\n * A node matches when its pattern matches a leading run of WHOLE path\n * segments (`/my` matches `/my/tasks`, never `/my-other`). Among matches the\n * winner is decided by positional specificity: at the first position where\n * one pattern is static and the other a `$param`, the static one wins (so\n * `/clients/new` beats `/clients/$clientId` whatever the sibling order, and\n * `/my` beats `/$space/tasks` for `/my/tasks`). Otherwise the pattern matching\n * more segments wins, then the deeper tree node.\n * Non-matching nodes are still searched, so a match keeps its logical\n * ancestors even when their URLs differ.\n *\n * @stability Beta\n */\nexport function resolveNavigation<T>(\n tree: NavigationNode<T>,\n pathname: string,\n): ResolvedNavigation<T> {\n // Compare decoded forms on both sides, so `/caf%C3%A9` matches a `/café`\n // node (and an encoded pattern matches a decoded pathname).\n const segments = pathSegments(pathname).map(decodeSegment);\n\n type Match = { trail: NavigationNode<T>[]; pattern: string[] };\n const matches = (pattern: string[]) =>\n pattern.length <= segments.length &&\n pattern.every(\n (part, i) => part.startsWith(\"$\") || decodeSegment(part) === segments[i],\n );\n\n // > 0 when `a` should win over `b`.\n // Positional specificity, as TanStack Router and React Router rank routes:\n // walking left to right, the first position where one pattern is static and\n // the other a `$param` decides. Only then does the longer match win.\n const compare = (a: Match, b: Match): number => {\n const shared = Math.min(a.pattern.length, b.pattern.length);\n for (let i = 0; i < shared; i++) {\n const aParam = a.pattern[i]!.startsWith(\"$\");\n const bParam = b.pattern[i]!.startsWith(\"$\");\n if (aParam !== bParam) return aParam ? -1 : 1;\n }\n if (a.pattern.length !== b.pattern.length) {\n return a.pattern.length - b.pattern.length;\n }\n return a.trail.length - b.trail.length;\n };\n\n let best: Match | undefined;\n const visit = (node: NavigationNode<T>, ancestors: NavigationNode<T>[]) => {\n const trail = [...ancestors, node];\n const pattern = pathSegments(node.path);\n if (matches(pattern)) {\n const candidate = { trail, pattern };\n if (!best || compare(candidate, best) > 0) best = candidate;\n }\n for (const child of node.children ?? []) visit(child, trail);\n };\n visit(tree, []);\n\n const winner: Match = best ?? { trail: [tree], pattern: [] };\n const params: Record<string, string> = {};\n winner.pattern.forEach((part, i) => {\n if (part.startsWith(\"$\")) params[part.slice(1)] = segments[i]!;\n });\n\n const interpolate = (node: NavigationNode<T>): NavigationNode<T> => ({\n ...node,\n path: node.path\n .split(\"/\")\n .map((part) => {\n if (!part.startsWith(\"$\")) return part;\n const value = params[part.slice(1)];\n return value == null ? part : encodeURIComponent(value);\n })\n .join(\"/\"),\n });\n\n const trail = winner.trail.map(interpolate);\n const current = trail[trail.length - 1]!;\n return {\n current,\n parent: trail.length > 1 ? trail[trail.length - 2] : undefined,\n items: current.items,\n trail,\n params,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Imperative navigation abstraction\n// ---------------------------------------------------------------------------\n\n/**\n * Injectable imperative navigation callback.\n *\n * @stability Beta\n */\nexport type NavigateFn = (path: string) => void;\n\n/**\n * Default navigate — a full-page navigation. No router required.\n *\n * @stability Beta\n */\nexport const defaultNavigate: NavigateFn = (path) => {\n if (typeof window !== \"undefined\") window.location.assign(path);\n};\n\n// ---------------------------------------------------------------------------\n// Search-params abstraction (for URL-synced hooks)\n// ---------------------------------------------------------------------------\n\n/**\n * Minimal, router-agnostic replacement for React Router's `useSearchParams`\n * return shape. Consumers may inject their router's implementation for full\n * SPA integration; otherwise the browser History API is used.\n *\n * @stability Beta\n */\nexport interface SearchParamsAdapter {\n searchParams: URLSearchParams;\n setSearchParams: (\n next: URLSearchParams,\n options?: { replace?: boolean },\n ) => void;\n}\n\n/**\n * Browser-native {@link SearchParamsAdapter} built on the History API. Reads\n * the current query string and writes updates via `pushState`/`replaceState`,\n * staying reactive to `popstate` events. No router required.\n *\n * @stability Beta\n */\nexport function useBrowserSearchParams(): SearchParamsAdapter {\n const read = () =>\n new URLSearchParams(\n typeof window !== \"undefined\" ? window.location.search : \"\",\n );\n\n const [searchParams, setLocal] = useState<URLSearchParams>(read);\n\n useEffect(() => {\n if (typeof window === \"undefined\") return;\n const onPop = () => setLocal(read());\n window.addEventListener(\"popstate\", onPop);\n return () => window.removeEventListener(\"popstate\", onPop);\n }, []);\n\n const setSearchParams = useCallback<SearchParamsAdapter[\"setSearchParams\"]>(\n (next, options) => {\n if (typeof window === \"undefined\") return;\n const qs = next.toString();\n const url =\n window.location.pathname + (qs ? `?${qs}` : \"\") + window.location.hash;\n if (options?.replace) {\n window.history.replaceState(window.history.state, \"\", url);\n } else {\n window.history.pushState(window.history.state, \"\", url);\n }\n setLocal(new URLSearchParams(next));\n },\n [],\n );\n\n return { searchParams, setSearchParams };\n}\n"]}
@@ -1,20 +0,0 @@
1
- "use client";
2
- import { useState, useEffect } from 'react';
3
- import { useStorage } from '@marktiderman/genesis-core';
4
-
5
- // src/hooks/use-view-preference.ts
6
- function useViewPreference(key, defaultMode = "grid") {
7
- const storage = useStorage();
8
- const storageKey = `genesis-view-${key}`;
9
- const [mode, setMode] = useState(() => {
10
- return storage.getItem(storageKey) || defaultMode;
11
- });
12
- useEffect(() => {
13
- storage.setItem(storageKey, mode);
14
- }, [mode, storageKey, storage]);
15
- return [mode, setMode];
16
- }
17
-
18
- export { useViewPreference };
19
- //# sourceMappingURL=chunk-FTTNGGLA.js.map
20
- //# sourceMappingURL=chunk-FTTNGGLA.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/hooks/use-view-preference.ts"],"names":[],"mappings":";;;;AAMO,SAAS,iBAAA,CACd,GAAA,EACA,WAAA,GAAwB,MAAA,EACc;AACtC,EAAA,MAAM,UAAU,UAAA,EAAW;AAC3B,EAAA,MAAM,UAAA,GAAa,gBAAgB,GAAG,CAAA,CAAA;AACtC,EAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAI,SAAmB,MAAM;AAC/C,IAAA,OAAQ,OAAA,CAAQ,OAAA,CAAQ,UAAU,CAAA,IAAkB,WAAA;AAAA,EACtD,CAAC,CAAA;AAED,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,OAAA,CAAQ,OAAA,CAAQ,YAAY,IAAI,CAAA;AAAA,EAClC,CAAA,EAAG,CAAC,IAAA,EAAM,UAAA,EAAY,OAAO,CAAC,CAAA;AAE9B,EAAA,OAAO,CAAC,MAAM,OAAO,CAAA;AACvB","file":"chunk-FTTNGGLA.js","sourcesContent":["\"use client\";\n\nimport { useState, useEffect } from \"react\";\nimport { useStorage } from \"@marktiderman/genesis-core\";\nimport type { ViewMode } from \"../components/patterns/view-toggle\";\n\nexport function useViewPreference(\n key: string,\n defaultMode: ViewMode = \"grid\",\n): [ViewMode, (mode: ViewMode) => void] {\n const storage = useStorage();\n const storageKey = `genesis-view-${key}`;\n const [mode, setMode] = useState<ViewMode>(() => {\n return (storage.getItem(storageKey) as ViewMode) || defaultMode;\n });\n\n useEffect(() => {\n storage.setItem(storageKey, mode);\n }, [mode, storageKey, storage]);\n\n return [mode, setMode];\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/hooks/use-resource-page.ts"],"names":[],"mappings":"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","file":"chunk-IXGAEECT.js","sourcesContent":["\"use client\";\n\nimport { useState, useMemo, useCallback, useEffect, useRef } from \"react\";\nimport type { ReactNode } from \"react\";\nimport type { DataTableColumn } from \"../components/data/DataTable\";\nimport type { DataFilterConfig } from \"../components/data/DataFilters\";\nimport type { ViewMode } from \"../components/patterns/view-toggle\";\nimport type { Identifier, FilterParam, SortParam } from \"@marktiderman/genesis-core/provider\";\nimport { useViewPreference } from \"./use-view-preference\";\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/** Actions available inside column render functions. */\nexport interface ResourceActions<T> {\n update: (id: Identifier, data: Partial<T>) => Promise<T>;\n remove: (id: Identifier) => Promise<void>;\n refetch: () => void;\n}\n\n/** Column definition — full config form. */\nexport interface ResourceColumnDef<T> {\n key: string;\n header?: string;\n sortable?: boolean;\n hideBelow?: \"sm\" | \"md\" | \"lg\" | \"xl\";\n render?: (\n item: T,\n value: unknown,\n actions: ResourceActions<T>,\n ) => ReactNode;\n /** In-place cell editing, passed straight through to `DataTable`. */\n editable?: DataTableColumn<T>[\"editable\"];\n /**\n * The cell's value, when it isn't `item[key]`. Passed straight through to\n * `DataTable`, which also starts an edit from `String(accessor(item))`.\n */\n accessor?: (item: T) => unknown;\n}\n\nexport interface UseResourcePageProps<T> {\n /** Resource name — used for view-preference storage key. */\n resource?: string;\n /** The data to display (pre-fetched or from useResource). */\n data: T[];\n /** Total count of records (before client-side filtering). */\n total: number;\n /** Column definitions. Strings are expanded to `{ key, header, sortable }`. Omit to auto-generate from data keys. */\n columns?: Array<string | ResourceColumnDef<T>>;\n /** Status filter config. String = field name (options auto-derived). Object = explicit field + options. */\n statusFilter?: string | { field: string; options?: string[] };\n /** Fields to search across. Defaults to all string-valued fields in data. */\n searchFields?: string[];\n /** Initial filter params applied on mount. */\n initialFilters?: FilterParam[];\n /** Callback when filters change. */\n onFiltersChange?: (filters: FilterParam[]) => void;\n /** Default view mode. */\n defaultView?: ViewMode;\n /** Detail interaction mode. */\n detail?: \"panel\" | \"modal\" | \"route\" | \"none\";\n /** Field used for the detail title. Defaults to first column key. */\n titleField?: string;\n /** Field used for the detail subtitle. Defaults to second column key. */\n subtitleField?: string;\n /**\n * Server-controlled mode. The `data` handed in has ALREADY been searched,\n * sorted and paginated by the server, so this hook must not do any of it a\n * second time — `data` is returned untouched and `total` is the server's\n * `total` rather than the length of a client-filtered slice.\n *\n * Search / sort / status state is still tracked (the filter UI stays\n * controlled and `onFiltersChange` still fires) — only the client-side\n * transform of `data` is skipped. Defaults to `false`, so every existing\n * caller keeps its current client-side behavior.\n */\n controlled?: boolean;\n}\n\nexport interface UseResourcePageReturn<T> {\n /** Filtered and sorted data for display. */\n data: T[];\n /** Total count (of the filtered set; the server's `total` when `controlled`). */\n total: number;\n /** Resolved column definitions. */\n columns: ResourceColumnDef<T>[];\n /** Columns mapped to DataTable format. */\n tableColumns: DataTableColumn<T>[];\n /** Ready-to-spread filter config for DataFilters. */\n filterConfig: {\n search: string;\n onSearchChange: (value: string) => void;\n sort: string;\n onSortChange: (sort: string) => void;\n sortOptions: Array<{ value: string; label: string }>;\n statusChips?: {\n options: string[];\n selected: string[];\n onChange: (values: string[]) => void;\n };\n };\n viewMode: ViewMode;\n setViewMode: (mode: ViewMode) => void;\n search: string;\n setSearch: (value: string) => void;\n sort: SortParam | null;\n setSort: (sort: SortParam | null) => void;\n statusValue: string[];\n setStatusValue: (values: string[]) => void;\n selectedItem: T | null;\n setSelectedItem: (item: T | null) => void;\n detailOpen: boolean;\n setDetailOpen: (open: boolean) => void;\n handleRowClick: (item: T) => void;\n titleField: string;\n subtitleField: string;\n statusOptions: string[];\n hasActiveFilters: boolean;\n clearFilters: () => void;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Convert a key string to title case.\n * \"created_at\" → \"Created At\"\n * \"firstName\" → \"First Name\"\n */\nexport function titleCase(str: string): string {\n // Split on underscores, hyphens, and camelCase boundaries\n const words = str\n .replace(/([a-z])([A-Z])/g, \"$1 $2\") // camelCase → camel Case\n .replace(/[_-]/g, \" \") // snake_case / kebab-case → spaces\n .split(/\\s+/)\n .filter(Boolean);\n\n return words\n .map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase())\n .join(\" \");\n}\n\n/**\n * Resolve column shorthand into full ResourceColumnDef objects.\n * - undefined + data present → auto-generate from Object.keys(data[0]), skip \"id\"\n * - undefined + empty data → empty array\n * - string items → { key, header: titleCase(key), sortable: true }\n * - object items → defaults applied\n */\nfunction resolveColumns<T>(\n columns: Array<string | ResourceColumnDef<T>> | undefined,\n data: T[],\n): ResourceColumnDef<T>[] {\n if (columns === undefined) {\n if (data.length === 0) return [];\n const keys = Object.keys(data[0] as Record<string, unknown>).filter(\n (k) => k !== \"id\",\n );\n return keys.map((key) => ({\n key,\n header: titleCase(key),\n sortable: true,\n }));\n }\n\n return columns.map((col) => {\n if (typeof col === \"string\") {\n return { key: col, header: titleCase(col), sortable: true };\n }\n return {\n ...col,\n header: col.header ?? titleCase(col.key),\n sortable: col.sortable ?? true,\n };\n });\n}\n\n/**\n * Derive the list of string fields in a data set (for default search).\n */\nfunction deriveStringFields<T>(data: T[]): string[] {\n if (data.length === 0) return [];\n const first = data[0] as Record<string, unknown>;\n return Object.keys(first).filter((k) => typeof first[k] === \"string\");\n}\n\n/**\n * Derive unique status options from a data array.\n */\nfunction deriveStatusOptions<T>(data: T[], field: string): string[] {\n const values = new Set<string>();\n for (const item of data) {\n const val = (item as Record<string, unknown>)[field];\n if (val != null) values.add(String(val));\n }\n return Array.from(values).sort();\n}\n\n/**\n * Client-side text search across multiple fields.\n */\nfunction matchesSearch<T>(\n item: T,\n query: string,\n fields: string[],\n): boolean {\n if (!query) return true;\n const lower = query.toLowerCase();\n const record = item as Record<string, unknown>;\n return fields.some((f) => {\n const val = record[f];\n return val != null && String(val).toLowerCase().includes(lower);\n });\n}\n\n/**\n * Client-side sort comparator.\n */\nfunction compareItems<T>(a: T, b: T, sort: SortParam): number {\n const ra = a as Record<string, unknown>;\n const rb = b as Record<string, unknown>;\n const va = ra[sort.field];\n const vb = rb[sort.field];\n const sa = va == null ? \"\" : String(va);\n const sb = vb == null ? \"\" : String(vb);\n const cmp = sa.localeCompare(sb, undefined, {\n numeric: true,\n sensitivity: \"base\",\n });\n return sort.order === \"asc\" ? cmp : -cmp;\n}\n\n// ---------------------------------------------------------------------------\n// Hook\n// ---------------------------------------------------------------------------\n\nexport function useResourcePage<T = Record<string, unknown>>(\n props: UseResourcePageProps<T>,\n): UseResourcePageReturn<T> {\n const {\n resource,\n data: rawData,\n total: rawTotal,\n columns: columnsProp,\n statusFilter: statusFilterProp,\n searchFields: searchFieldsProp,\n defaultView = \"table\",\n detail = \"panel\",\n titleField: titleFieldProp,\n subtitleField: subtitleFieldProp,\n initialFilters,\n onFiltersChange,\n controlled = false,\n } = props;\n\n // -- View mode --\n const [viewMode, setViewMode] = useViewPreference(\n resource ?? \"resource-page\",\n defaultView,\n );\n\n // -- Derive initial values from initialFilters --\n const initialSearch = useMemo(() => {\n if (!initialFilters) return \"\";\n const searchFilter = initialFilters.find(\n (f) => f.operator === \"contains\" && f.field === \"__search__\",\n );\n return searchFilter && \"value\" in searchFilter ? String(searchFilter.value ?? \"\") : \"\";\n }, []); // eslint-disable-line react-hooks/exhaustive-deps -- only on mount\n\n const initialSort = useMemo(() => {\n if (!initialFilters) return null;\n const sortFilter = initialFilters.find(\n (f) => f.field === \"__sort__\",\n );\n if (!sortFilter || !(\"value\" in sortFilter) || typeof sortFilter.value !== \"string\") return null;\n const [field, order] = sortFilter.value.split(\":\");\n if (!field || (order !== \"asc\" && order !== \"desc\")) return null;\n return { field, order } as SortParam;\n }, []); // eslint-disable-line react-hooks/exhaustive-deps -- only on mount\n\n const initialStatus = useMemo(() => {\n if (!initialFilters) return [];\n const statusFilter = initialFilters.find(\n (f) => f.operator === \"in\" && f.field === \"__status__\",\n );\n if (!statusFilter || statusFilter.operator !== \"in\") return [];\n return statusFilter.value.map((v) => String(v));\n }, []); // eslint-disable-line react-hooks/exhaustive-deps -- only on mount\n\n // -- Search --\n const [search, setSearch] = useState(initialSearch);\n\n // -- Sort --\n const [sort, setSort] = useState<SortParam | null>(initialSort);\n\n // -- Status filter --\n const [statusValue, setStatusValue] = useState<string[]>(initialStatus);\n\n // -- Detail panel --\n const [selectedItem, setSelectedItem] = useState<T | null>(null);\n const [detailOpen, setDetailOpen] = useState(false);\n\n // -- Resolve columns --\n const resolvedColumns = useMemo(\n () => resolveColumns(columnsProp, rawData),\n [columnsProp, rawData],\n );\n\n // -- Title / subtitle fields --\n const titleField =\n titleFieldProp ?? resolvedColumns[0]?.key ?? \"name\";\n const subtitleField =\n subtitleFieldProp ?? resolvedColumns[1]?.key ?? \"\";\n\n // -- Status filter config --\n const statusFieldName = useMemo(() => {\n if (!statusFilterProp) return null;\n return typeof statusFilterProp === \"string\"\n ? statusFilterProp\n : statusFilterProp.field;\n }, [statusFilterProp]);\n\n const statusOptions = useMemo(() => {\n if (!statusFilterProp) return [];\n if (typeof statusFilterProp === \"object\" && statusFilterProp.options) {\n return statusFilterProp.options;\n }\n if (!statusFieldName) return [];\n return deriveStatusOptions(rawData, statusFieldName);\n }, [statusFilterProp, statusFieldName, rawData]);\n\n // -- Search fields --\n const searchFields = useMemo(\n () => searchFieldsProp ?? deriveStringFields(rawData),\n [searchFieldsProp, rawData],\n );\n\n // -- Client-side filtering + sorting --\n // Skipped entirely in controlled mode: the server already applied the\n // search, the sort and the page window, so re-running them here would\n // filter the current page against a query the server has already honoured\n // (and re-sort a slice of rows that is not the full result set).\n const filteredData = useMemo(() => {\n if (controlled) return rawData;\n\n let result = rawData;\n\n // Text search\n if (search) {\n result = result.filter((item) =>\n matchesSearch(item, search, searchFields),\n );\n }\n\n // Status filter\n if (statusFieldName && statusValue.length > 0) {\n result = result.filter((item) => {\n const val = (item as Record<string, unknown>)[statusFieldName];\n return val != null && statusValue.includes(String(val));\n });\n }\n\n // Sort\n if (sort) {\n result = [...result].sort((a, b) => compareItems(a, b, sort));\n }\n\n return result;\n }, [controlled, rawData, search, searchFields, statusFieldName, statusValue, sort]);\n\n // -- Sort options (built from resolved columns) --\n const sortOptions = useMemo(() => {\n const opts: Array<{ value: string; label: string }> = [];\n for (const col of resolvedColumns) {\n if (col.sortable !== false) {\n opts.push({\n value: `${col.key}:asc`,\n label: `${col.header ?? titleCase(col.key)} (A-Z)`,\n });\n opts.push({\n value: `${col.key}:desc`,\n label: `${col.header ?? titleCase(col.key)} (Z-A)`,\n });\n }\n }\n return opts;\n }, [resolvedColumns]);\n\n // -- Sort string for DataFilters select --\n const sortString = sort ? `${sort.field}:${sort.order}` : \"\";\n\n const handleSortChange = useCallback((value: string) => {\n if (!value) {\n setSort(null);\n return;\n }\n const [field, order] = value.split(\":\");\n setSort({ field, order: order as \"asc\" | \"desc\" });\n }, []);\n\n // -- Map to DataTableColumn --\n const tableColumns: DataTableColumn<T>[] = useMemo(\n () =>\n resolvedColumns.map((col) => ({\n key: col.key,\n header: col.header ?? titleCase(col.key),\n sortable: col.sortable ?? true,\n hideBelow: col.hideBelow,\n editable: col.editable,\n accessor: col.accessor,\n render: col.render\n ? (item: T, value: unknown) =>\n col.render!(item, value, {\n update: async () => {\n throw new Error(\n \"ResourceActions.update not available in server-data mode\",\n );\n },\n remove: async () => {\n throw new Error(\n \"ResourceActions.remove not available in server-data mode\",\n );\n },\n refetch: () => {\n /* no-op in server-data mode */\n },\n })\n : undefined,\n })),\n [resolvedColumns],\n );\n\n // -- Active filters check --\n const hasActiveFilters =\n search !== \"\" || statusValue.length > 0 || sort !== null;\n\n const clearFilters = useCallback(() => {\n setSearch(\"\");\n setStatusValue([]);\n setSort(null);\n }, []);\n\n // -- Notify parent of filter changes --\n const onFiltersChangeRef = useRef(onFiltersChange);\n onFiltersChangeRef.current = onFiltersChange;\n\n useEffect(() => {\n if (!onFiltersChangeRef.current) return;\n const filters: FilterParam[] = [];\n if (search) {\n filters.push({ field: \"__search__\", operator: \"contains\", value: search });\n }\n if (sort) {\n filters.push({ field: \"__sort__\", operator: \"eq\", value: `${sort.field}:${sort.order}` });\n }\n if (statusValue.length > 0) {\n filters.push({ field: \"__status__\", operator: \"in\", value: statusValue });\n }\n onFiltersChangeRef.current(filters);\n }, [search, sort, statusValue]);\n\n // -- Row click --\n const handleRowClick = useCallback(\n (item: T) => {\n if (detail === \"none\") return;\n setSelectedItem(item);\n setDetailOpen(true);\n },\n [detail],\n );\n\n // -- Filter config (ready to spread into DataFilters) --\n const filterConfig = useMemo(\n () => ({\n search,\n onSearchChange: setSearch,\n sort: sortString,\n onSortChange: handleSortChange,\n sortOptions,\n ...(statusFieldName && statusOptions.length > 0\n ? {\n statusChips: {\n options: statusOptions,\n selected: statusValue,\n onChange: setStatusValue,\n },\n }\n : {}),\n }),\n [\n search,\n sortString,\n handleSortChange,\n sortOptions,\n statusFieldName,\n statusOptions,\n statusValue,\n ],\n );\n\n return {\n data: filteredData,\n // Controlled mode: `filteredData` is one server page, so its length is\n // not the record count — the server's total is.\n total: controlled ? rawTotal : filteredData.length,\n columns: resolvedColumns,\n tableColumns,\n filterConfig,\n viewMode,\n setViewMode,\n search,\n setSearch,\n sort,\n setSort,\n statusValue,\n setStatusValue,\n selectedItem,\n setSelectedItem,\n detailOpen,\n setDetailOpen,\n handleRowClick,\n titleField,\n subtitleField,\n statusOptions,\n hasActiveFilters,\n clearFilters,\n };\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/hooks/use-data-filters.ts"],"names":[],"mappings":"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","file":"chunk-JA3ZKULS.js","sourcesContent":["\"use client\";\n\nimport { useState, useCallback, useEffect, useRef } from \"react\";\nimport { useStorage } from \"@marktiderman/genesis-core\";\nimport {\n useBrowserSearchParams,\n type SearchParamsAdapter,\n} from \"../navigation\";\n\ntype FilterType = \"string\" | \"multi\";\n\nexport interface FilterDef {\n type: FilterType;\n default: string | string[];\n}\n\nexport type FilterDefs = Record<string, FilterDef>;\n\nexport type FilterValues<T extends FilterDefs> = {\n [K in keyof T]: T[K][\"type\"] extends \"multi\" ? string[] : string;\n};\n\nfunction parseArray(val: string | null): string[] {\n if (!val) return [];\n return val.split(\",\").filter(Boolean);\n}\n\n/**\n * @stability Beta\n */\nexport interface UseDataFiltersOptions {\n /**\n * Injectable URL search-params adapter. Defaults to a browser History-API\n * implementation (no router). Router consumers may pass their own — e.g.\n * React Router's `useSearchParams()` result — for full SPA integration.\n */\n searchParams?: SearchParamsAdapter;\n}\n\nexport function useDataFilters<T extends FilterDefs>(\n storageKey: string,\n defs: T,\n options?: UseDataFiltersOptions\n) {\n // Always call the default hook (rules-of-hooks); use the override if provided.\n const browserAdapter = useBrowserSearchParams();\n const { searchParams, setSearchParams } =\n options?.searchParams ?? browserAdapter;\n const storage = useStorage();\n const initialized = useRef(false);\n\n // Build initial state from URL -> storage -> defaults\n const getInitial = useCallback((): FilterValues<T> => {\n const result = {} as Record<string, unknown>;\n const localKey = `${storageKey}-filters`;\n let stored: Record<string, unknown> = {};\n\n try {\n const raw = storage.getItem(localKey);\n if (raw) stored = JSON.parse(raw);\n } catch {\n /* ignore */\n }\n\n // Check if URL has any of our params\n const hasUrlParams = Object.keys(defs).some((k) => searchParams.has(k));\n\n for (const [key, def] of Object.entries(defs)) {\n if (def.type === \"multi\") {\n const urlVal = hasUrlParams ? searchParams.get(key) : null;\n if (urlVal !== null) {\n result[key] = parseArray(urlVal);\n } else if (Array.isArray(stored[key])) {\n result[key] = stored[key];\n } else {\n result[key] = def.default;\n }\n } else {\n const urlVal = hasUrlParams ? searchParams.get(key) : null;\n if (urlVal !== null) {\n result[key] = urlVal;\n } else if (typeof stored[key] === \"string\") {\n result[key] = stored[key];\n } else {\n result[key] = def.default;\n }\n }\n }\n\n return result as FilterValues<T>;\n }, [defs, searchParams, storageKey, storage]);\n\n const [filters, setFilters] = useState<FilterValues<T>>(() => getInitial());\n\n // Mark as initialized after first render\n useEffect(() => {\n if (!initialized.current) {\n initialized.current = true;\n }\n }, []);\n\n // Persist to storage + URL whenever filters change\n useEffect(() => {\n if (!initialized.current) return;\n\n const localKey = `${storageKey}-filters`;\n storage.setItem(localKey, JSON.stringify(filters));\n\n // Update URL params. When using the DEFAULT browser adapter, seed from the\n // LIVE URL (not the adapter's possibly stale snapshot) so a second\n // useDataFilters instance on the same page can't merge against an old query\n // string and drop params another instance just wrote. When a caller\n // explicitly injects a SearchParamsAdapter (custom router / test double\n // whose URL state may not be synced to the DOM), trust ITS snapshot instead\n // of reading the real DOM location. Also falls back to the adapter snapshot\n // in non-browser envs.\n const newParams = new URLSearchParams(\n !options?.searchParams && typeof window !== \"undefined\"\n ? window.location.search\n : searchParams.toString(),\n );\n for (const [key, def] of Object.entries(defs)) {\n const val = (filters as Record<string, unknown>)[key];\n const isDefault =\n def.type === \"multi\"\n ? (val as string[]).length === 0\n : val === def.default;\n\n if (isDefault) {\n newParams.delete(key);\n } else if (def.type === \"multi\") {\n newParams.set(key, (val as string[]).join(\",\"));\n } else {\n newParams.set(key, val as string);\n }\n }\n setSearchParams(newParams, { replace: true });\n }, [filters, defs, storageKey, storage]); // eslint-disable-line react-hooks/exhaustive-deps\n\n const setFilter = useCallback(\n <K extends keyof T>(\n key: K,\n value: T[K][\"type\"] extends \"multi\" ? string[] : string\n ) => {\n setFilters((prev) => ({ ...prev, [key]: value }));\n },\n []\n );\n\n const resetFilters = useCallback(() => {\n const result = {} as Record<string, unknown>;\n for (const [key, def] of Object.entries(defs)) {\n result[key] = def.default;\n }\n setFilters(result as FilterValues<T>);\n }, [defs]);\n\n const loadView = useCallback(\n (viewFilters: Record<string, unknown>) => {\n const result = {} as Record<string, unknown>;\n for (const [key, def] of Object.entries(defs)) {\n const v = viewFilters[key];\n if (def.type === \"multi\" && Array.isArray(v)) {\n result[key] = v;\n } else if (def.type === \"string\" && typeof v === \"string\") {\n result[key] = v;\n } else {\n result[key] = def.default;\n }\n }\n setFilters(result as FilterValues<T>);\n },\n [defs]\n );\n\n const hasActiveFilters = Object.entries(defs).some(([key, def]) => {\n const val = (filters as Record<string, unknown>)[key];\n if (def.type === \"multi\") return (val as string[]).length > 0;\n return val !== def.default;\n });\n\n return {\n filters,\n setFilter,\n resetFilters,\n loadView,\n hasActiveFilters,\n };\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/ui/alert-dialog.tsx"],"names":["AlertDialogPrimitive"],"mappings":"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","file":"chunk-M62CHO3N.js","sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { AlertDialog as AlertDialogPrimitive } from \"radix-ui\";\nimport { cn } from \"../../utils\";\n\nconst AlertDialog = AlertDialogPrimitive.Root;\nconst AlertDialogTrigger = AlertDialogPrimitive.Trigger;\nconst AlertDialogPortal = AlertDialogPrimitive.Portal;\n\nconst AlertDialogOverlay = React.forwardRef<\n React.ComponentRef<typeof AlertDialogPrimitive.Overlay>,\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>\n>(({ className, ...props }, ref) => (\n <AlertDialogPrimitive.Overlay\n className={cn(\n \"fixed inset-0 z-50 bg-black/50 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n className\n )}\n {...props}\n ref={ref}\n />\n));\nAlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName;\n\nconst AlertDialogContent = React.forwardRef<\n React.ComponentRef<typeof AlertDialogPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content>\n>(({ className, ...props }, ref) => (\n <AlertDialogPortal>\n <AlertDialogOverlay />\n <AlertDialogPrimitive.Content\n ref={ref}\n className={cn(\n \"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-border bg-card p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg\",\n className\n )}\n {...props}\n />\n </AlertDialogPortal>\n));\nAlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName;\n\nconst AlertDialogHeader = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n <div className={cn(\"flex flex-col space-y-2 text-center sm:text-left\", className)} {...props} />\n);\n\nconst AlertDialogFooter = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n <div className={cn(\"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2\", className)} {...props} />\n);\n\nconst AlertDialogTitle = React.forwardRef<\n React.ComponentRef<typeof AlertDialogPrimitive.Title>,\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title>\n>(({ className, ...props }, ref) => (\n <AlertDialogPrimitive.Title\n ref={ref}\n className={cn(\"text-lg font-semibold\", className)}\n {...props}\n />\n));\nAlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName;\n\nconst AlertDialogDescription = React.forwardRef<\n React.ComponentRef<typeof AlertDialogPrimitive.Description>,\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description>\n>(({ className, ...props }, ref) => (\n <AlertDialogPrimitive.Description\n ref={ref}\n className={cn(\"text-sm text-muted-foreground\", className)}\n {...props}\n />\n));\nAlertDialogDescription.displayName = AlertDialogPrimitive.Description.displayName;\n\nconst AlertDialogAction = React.forwardRef<\n React.ComponentRef<typeof AlertDialogPrimitive.Action>,\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action>\n>(({ className, ...props }, ref) => (\n <AlertDialogPrimitive.Action\n ref={ref}\n className={cn(\n \"inline-flex h-10 items-center justify-center rounded-lg bg-destructive px-4 py-2 text-sm font-semibold text-destructive-foreground transition-colors hover:bg-destructive/90 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50\",\n className\n )}\n {...props}\n />\n));\nAlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName;\n\nconst AlertDialogCancel = React.forwardRef<\n React.ComponentRef<typeof AlertDialogPrimitive.Cancel>,\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Cancel>\n>(({ className, ...props }, ref) => (\n <AlertDialogPrimitive.Cancel\n ref={ref}\n className={cn(\n \"mt-2 inline-flex h-10 items-center justify-center rounded-lg border border-border bg-card px-4 py-2 text-sm font-medium transition-colors hover:bg-accent focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 sm:mt-0\",\n className\n )}\n {...props}\n />\n));\nAlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName;\n\nexport {\n AlertDialog,\n AlertDialogPortal,\n AlertDialogOverlay,\n AlertDialogTrigger,\n AlertDialogContent,\n AlertDialogHeader,\n AlertDialogFooter,\n AlertDialogTitle,\n AlertDialogDescription,\n AlertDialogAction,\n AlertDialogCancel,\n};\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/patterns/sortable-list.tsx"],"names":[],"mappings":"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","file":"chunk-TXOZMKNW.js","sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { GripVertical, MoreVertical } from \"lucide-react\";\nimport { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from \"../ui/dropdown-menu\";\nimport { cn } from \"../../utils\";\n\nexport interface SortableListItemContext {\n /** The item's position as shown, from 0. */\n index: number;\n /**\n * The drag handle: drag it with a pointer, or focus it and press Space or\n * Enter to pick the item up, the arrow keys to move it and Space or Enter\n * again to drop it. Render it somewhere in the row.\n */\n handle: React.ReactNode;\n /** True while this item is being dragged with a pointer. */\n isDragging: boolean;\n /** True while this item is picked up from the keyboard. */\n isGrabbed: boolean;\n /** Move this item without dragging, for a row menu of your own. */\n moveUp: () => void;\n moveDown: () => void;\n moveTo: (index: number) => void;\n}\n\nexport interface SortableListReorder {\n /** Key of the item that moved. */\n key: string;\n}\n\nexport interface SortableListProps<T> {\n items: readonly T[];\n /** A stable key per item. Required: dragging finds the item again by key. */\n getKey: (item: T) => string;\n /** A name for an item, for the handle's label and announcements (\"Week 1\"). */\n getItemLabel: (item: T) => string;\n renderItem: (item: T, context: SortableListItemContext) => React.ReactNode;\n /**\n * Move the item at `from` to `to` (both from 0, `to` counted after the item\n * is taken out). Each finished move calls this once: a keyboard pick-up\n * that wanders and comes back calls nothing.\n *\n * The list shows the new order straight away and keeps showing it until the\n * returned promise settles; by then `items` must be in the new order. A\n * synchronous handler must update `items` in the same tick. A rejected\n * promise puts the old order back and announces that the move failed. Other\n * moves wait until this one settles.\n */\n onReorder: (from: number, to: number, move: SortableListReorder) => void | Promise<void>;\n /** Accessible name of the list (\"Course steps\"). */\n label: string;\n /** Turns off dragging and every kind of move. */\n disabled?: boolean | undefined;\n /**\n * A \"Move\" menu on each row (up, down, to top, to bottom), on by default so\n * touch and screen-reader users always have a way to move an item. Turn it\n * off only when the row offers `moveUp` / `moveDown` / `moveTo` itself.\n */\n moveMenu?: boolean | undefined;\n className?: string | undefined;\n itemClassName?: string | undefined;\n}\n\ntype DropAt = { key: string; edge: \"before\" | \"after\" };\n\nconst DRAG_TYPE = \"application/x-genesis-sortable-item\";\n\n// useLayoutEffect warns during server rendering; nothing needs refocusing there.\nconst useIsomorphicLayoutEffect = typeof window === \"undefined\" ? React.useEffect : React.useLayoutEffect;\n\nfunction moved(keys: readonly string[], from: number, to: number) {\n const next = keys.slice();\n const [key] = next.splice(from, 1);\n next.splice(to, 0, key!);\n return next;\n}\n\n/**\n * A vertical list the user can rearrange: course steps, lesson blocks, form\n * fields.\n *\n * Every way to move an item is equivalent, so dragging is never the only one\n * (the same stance as KanbanBoard's \"Move to…\" menu):\n * - drag the handle with a pointer;\n * - focus the handle, Space or Enter to pick up, ↑/↓ and Home/End to move,\n * Space or Enter to drop, Escape to put it back (tabbing away also puts it\n * back); Alt+↑/↓ moves one step without picking up;\n * - the row's \"Move\" menu, or `moveUp` / `moveDown` / `moveTo` from the render\n * context.\n *\n * Moves are announced in a polite live region once they succeed. Native drag\n * and drop needs no dependency and Radix has none to offer (docs/FRAMEWORK.md\n * rule 3).\n *\n * @stability Beta\n */\nexport function SortableList<T>({\n items,\n getKey,\n getItemLabel,\n renderItem,\n onReorder,\n label,\n disabled,\n moveMenu = true,\n className,\n itemClassName,\n}: SortableListProps<T>) {\n const [armed, setArmed] = React.useState<string | null>(null);\n const [dragging, setDragging] = React.useState<string | null>(null);\n const [dropAt, setDropAt] = React.useState<DropAt | null>(null);\n // While a key is grabbed, `preview` is the order on screen; nothing is\n // written until the drop.\n const [grabbed, setGrabbed] = React.useState<{ key: string; from: number } | null>(null);\n // The order to show instead of `items`: during a keyboard pick-up, and from\n // a committed move until its promise settles.\n const [preview, setPreview] = React.useState<string[] | null>(null);\n const [pending, setPending] = React.useState(false);\n const [announcement, setAnnouncement] = React.useState(\"\");\n const handles = React.useRef(new Map<string, HTMLButtonElement>());\n const refocus = React.useRef<string | null>(null);\n const instructionsId = React.useId();\n\n const byKey = new Map(items.map((item) => [getKey(item), item] as const));\n const itemKeys = items.map(getKey);\n const keys = preview && preview.length === itemKeys.length && preview.every((k) => byKey.has(k)) ? preview : itemKeys;\n const total = keys.length;\n const nameOf = (key: string) => {\n const item = byKey.get(key);\n return item === undefined ? \"Item\" : getItemLabel(item);\n };\n\n // A live region only speaks when its text changes, so the same message\n // twice in a row is told apart by a trailing no-break space.\n const announce = (message: string) =>\n setAnnouncement((previous) => (previous === message ? `${message} ` : message));\n\n // Only follow the moved item with focus when its handle had focus (or the\n // DOM move dropped focus to the body); a row menu keeps its own focus.\n const keepFocus = (key: string) => {\n const active = typeof document === \"undefined\" ? null : document.activeElement;\n refocus.current = active === handles.current.get(key) || active === document.body || active === null ? key : null;\n };\n const keysSignature = keys.join(\"\\u0000\");\n useIsomorphicLayoutEffect(() => {\n const key = refocus.current;\n refocus.current = null;\n if (!key) return;\n const handle = handles.current.get(key);\n const active = document.activeElement;\n if (handle && (active === document.body || active === null || active === handle)) handle.focus();\n }, [keysSignature]);\n\n // An item that disappears mid-gesture, or the list turning disabled, ends\n // the gesture.\n React.useEffect(() => {\n if (disabled && (grabbed || armed || dragging)) {\n setGrabbed(null);\n setArmed(null);\n setDragging(null);\n setDropAt(null);\n if (!pending) setPreview(null);\n return;\n }\n if (dragging && !byKey.has(dragging)) {\n setDragging(null);\n setDropAt(null);\n setArmed(null);\n }\n if (grabbed && !byKey.has(grabbed.key)) {\n setGrabbed(null);\n setPreview(null);\n }\n });\n\n const commit = (key: string, from: number, to: number, message: string) => {\n if (from === to) return;\n setPending(true);\n setPreview(moved(itemKeys, from, to));\n // Called synchronously, so a synchronous handler updates `items` in the\n // same tick as the preview.\n let result: void | Promise<void>;\n try {\n result = onReorder(from, to, { key });\n } catch (error) {\n result = Promise.reject(error);\n }\n Promise.resolve(result)\n .then(\n () => announce(message),\n () => announce(`Couldn’t move ${nameOf(key)}.`),\n )\n .finally(() => {\n setPending(false);\n setPreview(null);\n });\n };\n\n const move = (key: string, to: number) => {\n if (disabled || pending || grabbed) return;\n if (!Number.isFinite(to)) return;\n const from = itemKeys.indexOf(key);\n const target = Math.max(0, Math.min(total - 1, Math.round(to)));\n if (from < 0 || target === from) return;\n keepFocus(key);\n commit(key, from, target, `${nameOf(key)} moved to position ${target + 1} of ${total}.`);\n };\n\n const pickUp = (key: string) => {\n if (disabled || pending) return;\n const from = itemKeys.indexOf(key);\n if (from < 0) return;\n setGrabbed({ key, from });\n setPreview(itemKeys);\n announce(\n `${nameOf(key)} picked up, position ${from + 1} of ${total}. Use the up and down arrow keys to move, Space to drop, Escape to cancel.`,\n );\n };\n\n const drop = (key: string) => {\n if (!grabbed) return;\n const to = keys.indexOf(key);\n const from = grabbed.from;\n setGrabbed(null);\n if (to === from) {\n setPreview(null);\n announce(`${nameOf(key)} dropped at position ${to + 1} of ${total}.`);\n return;\n }\n keepFocus(key);\n commit(key, from, to, `${nameOf(key)} dropped at position ${to + 1} of ${total}.`);\n };\n\n const cancel = (key: string) => {\n if (!grabbed) return;\n const from = grabbed.from;\n keepFocus(key);\n setGrabbed(null);\n setPreview(null);\n announce(`Move cancelled. ${nameOf(key)} is back at position ${from + 1} of ${total}.`);\n };\n\n const step = (key: string, to: number) => {\n const index = keys.indexOf(key);\n const target = Math.max(0, Math.min(total - 1, to));\n if (index < 0 || target === index) return;\n keepFocus(key);\n setPreview(moved(keys, index, target));\n announce(`${nameOf(key)} moved to position ${target + 1} of ${total}.`);\n };\n\n // Escape while an item is picked up must not also close a surrounding\n // Dialog or Sheet, which listen before the handle does.\n React.useEffect(() => {\n if (!grabbed) return;\n const onKey = (event: KeyboardEvent) => {\n if (event.key !== \"Escape\") return;\n event.preventDefault();\n event.stopPropagation();\n cancel(grabbed.key);\n };\n window.addEventListener(\"keydown\", onKey, true);\n return () => window.removeEventListener(\"keydown\", onKey, true);\n });\n\n // A press on the handle arms its row for native dragging; any release or\n // cancel, wherever it lands, disarms it.\n React.useEffect(() => {\n if (!armed || dragging) return;\n const disarm = () => setArmed(null);\n window.addEventListener(\"pointerup\", disarm, { once: true });\n window.addEventListener(\"pointercancel\", disarm, { once: true });\n return () => {\n window.removeEventListener(\"pointerup\", disarm);\n window.removeEventListener(\"pointercancel\", disarm);\n };\n }, [armed, dragging]);\n\n const onHandleKeyDown = (event: React.KeyboardEvent, key: string) => {\n if (disabled) return;\n const isGrabbed = grabbed?.key === key;\n const index = keys.indexOf(key);\n const delta = event.key === \"ArrowUp\" ? -1 : event.key === \"ArrowDown\" ? 1 : 0;\n if (isGrabbed) {\n if (delta) {\n event.preventDefault();\n step(key, index + delta);\n } else if (event.key === \"Home\" || event.key === \"End\") {\n event.preventDefault();\n step(key, event.key === \"Home\" ? 0 : total - 1);\n }\n return;\n }\n if (delta && event.altKey) {\n event.preventDefault();\n move(key, index + delta);\n }\n };\n\n // Space and Enter reach the handle as a click with `detail === 0`, as do\n // screen readers' activations; a pointer click is a press for dragging.\n const onHandleClick = (event: React.MouseEvent, key: string) => {\n if (disabled || event.detail !== 0) return;\n if (grabbed?.key === key) drop(key);\n else if (!grabbed) pickUp(key);\n };\n\n const onDragOver = (event: React.DragEvent<HTMLLIElement>, key: string) => {\n if (!dragging) return;\n event.preventDefault();\n event.dataTransfer.dropEffect = \"move\";\n const rect = event.currentTarget.getBoundingClientRect();\n const edge = event.clientY < rect.top + rect.height / 2 ? \"before\" : \"after\";\n if (dropAt?.key !== key || dropAt.edge !== edge) setDropAt({ key, edge });\n };\n\n const endDrag = () => {\n setDragging(null);\n setDropAt(null);\n setArmed(null);\n };\n\n const onDrop = (event: React.DragEvent) => {\n event.preventDefault();\n const key = event.dataTransfer.getData(DRAG_TYPE) || dragging;\n const target = dropAt;\n endDrag();\n if (!key || !target || pending || grabbed) return;\n const from = itemKeys.indexOf(key);\n const over = itemKeys.indexOf(target.key);\n if (from < 0 || over < 0) return;\n // `to` counts positions after the dragged item is taken out.\n let to = target.edge === \"before\" ? over : over + 1;\n if (from < to) to -= 1;\n commit(key, from, to, `${nameOf(key)} dropped at position ${to + 1} of ${total}.`);\n };\n\n return (\n <>\n <ul aria-label={label} aria-busy={pending || undefined} className={cn(\"flex flex-col gap-1\", className)}>\n {keys.map((key, index) => {\n const item = byKey.get(key)!;\n const itemLabel = getItemLabel(item);\n const isGrabbed = grabbed?.key === key;\n const isDragging = dragging === key;\n const handle = disabled ? null : (\n <button\n type=\"button\"\n ref={(node) => {\n if (node) handles.current.set(key, node);\n else handles.current.delete(key);\n }}\n aria-label={`Reorder ${itemLabel}`}\n aria-describedby={instructionsId}\n data-grabbed={isGrabbed || undefined}\n className={cn(\n \"inline-flex h-7 w-5 shrink-0 cursor-grab items-center justify-center rounded text-muted-foreground\",\n \"hover:bg-accent hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\",\n isGrabbed && \"bg-accent text-foreground\",\n )}\n onPointerDown={() => {\n if (!pending && !grabbed) setArmed(key);\n }}\n onPointerCancel={() => setArmed(null)}\n onClick={(event) => onHandleClick(event, key)}\n onKeyDown={(event) => onHandleKeyDown(event, key)}\n onBlur={() => {\n // A DOM move during the pick-up blurs the handle for a moment;\n // that is not the user leaving.\n if (isGrabbed && refocus.current !== key) cancel(key);\n }}\n >\n <GripVertical className=\"h-4 w-4\" aria-hidden />\n </button>\n );\n return (\n <li\n key={key}\n data-sortable-key={key}\n draggable={(!disabled && armed === key) || undefined}\n className={cn(\n \"relative rounded-md\",\n moveMenu && !disabled && \"pr-8\",\n isDragging && \"opacity-50\",\n isGrabbed && \"ring-2 ring-primary/50\",\n itemClassName,\n )}\n onDragStart={(event) => {\n if (disabled || armed !== key) return;\n event.dataTransfer.setData(DRAG_TYPE, key);\n event.dataTransfer.effectAllowed = \"move\";\n setDragging(key);\n }}\n onDragEnd={endDrag}\n onDragOver={(event) => onDragOver(event, key)}\n onDrop={onDrop}\n >\n {dropAt?.key === key && dragging !== key && (\n <span\n aria-hidden\n className={cn(\n \"pointer-events-none absolute inset-x-0 h-0.5 rounded bg-primary\",\n dropAt.edge === \"before\" ? \"-top-0.5\" : \"-bottom-0.5\",\n )}\n />\n )}\n {renderItem(item, {\n index,\n handle,\n isDragging,\n isGrabbed,\n moveUp: () => move(key, index - 1),\n moveDown: () => move(key, index + 1),\n moveTo: (to) => move(key, to),\n })}\n {moveMenu && !disabled && total > 1 && (\n <DropdownMenu>\n <DropdownMenuTrigger\n aria-label={`Move ${itemLabel}`}\n className=\"absolute right-1 top-1 rounded p-1 text-muted-foreground hover:bg-accent hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n <MoreVertical className=\"h-4 w-4\" aria-hidden />\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuItem disabled={index === 0} onSelect={() => move(key, index - 1)}>\n Move up\n </DropdownMenuItem>\n <DropdownMenuItem disabled={index === total - 1} onSelect={() => move(key, index + 1)}>\n Move down\n </DropdownMenuItem>\n <DropdownMenuItem disabled={index === 0} onSelect={() => move(key, 0)}>\n Move to top\n </DropdownMenuItem>\n <DropdownMenuItem disabled={index === total - 1} onSelect={() => move(key, total - 1)}>\n Move to bottom\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n )}\n </li>\n );\n })}\n </ul>\n <p id={instructionsId} hidden>\n Space or Enter picks the item up. Up and down arrows, Home and End move it. Space or Enter drops it, Escape puts\n it back. Alt with an arrow moves it one step.\n </p>\n <div aria-live=\"polite\" aria-atomic=\"true\" className=\"sr-only\">\n {announcement}\n </div>\n </>\n );\n}\n"]}