@marktiderman/genesis-ui 2.11.0 → 2.12.1
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.
- package/SKILL.md +57 -0
- package/dist/AppShell-D5nD0mcL.d.ts +348 -0
- package/dist/chunk-424PT5DM.js +21 -0
- package/dist/chunk-424PT5DM.js.map +1 -0
- package/dist/{chunk-UYI3GLFJ.js → chunk-6N6MUN3T.js} +722 -159
- package/dist/chunk-6N6MUN3T.js.map +1 -0
- package/dist/chunk-J45FHL7D.js +46 -0
- package/dist/chunk-J45FHL7D.js.map +1 -0
- package/dist/{chunk-OITWCIG2.js → chunk-PMCUHSZS.js} +3 -3
- package/dist/{chunk-OITWCIG2.js.map → chunk-PMCUHSZS.js.map} +1 -1
- package/dist/{chunk-5GOQ72WB.js → chunk-STZVZUYO.js} +3 -3
- package/dist/{chunk-5GOQ72WB.js.map → chunk-STZVZUYO.js.map} +1 -1
- package/dist/chunk-WALJMVEF.js +10 -0
- package/dist/chunk-WALJMVEF.js.map +1 -0
- package/dist/components/data/index.d.ts +7 -6
- package/dist/components/data/index.js +15 -13
- package/dist/components/data/index.js.map +1 -1
- package/dist/components/layout/dashboard-page.js +1 -0
- package/dist/components/layout/detail-page.js +1 -0
- package/dist/components/layout/form-page.js +1 -0
- package/dist/components/layout/index.d.ts +3 -2
- package/dist/components/layout/index.js +4 -2
- package/dist/components/layout/settings-page.js +1 -0
- package/dist/components/patterns/empty-state.js +1 -0
- package/dist/components/patterns/form-field.js +1 -0
- package/dist/components/patterns/index.js +1 -0
- package/dist/components/patterns/page-loading.js +1 -0
- package/dist/components/patterns/settings-row.js +1 -0
- package/dist/components/patterns/sortable-list.js +1 -0
- package/dist/components/patterns/status-badge.js +1 -0
- package/dist/components/patterns/toggle-row.js +1 -0
- package/dist/components/patterns/user-avatar.js +1 -0
- package/dist/components/rich-text/rich-text-editor.js +2 -1
- package/dist/components/rich-text/rich-text-editor.js.map +1 -1
- package/dist/components/rich-text/rich-text.js +1 -0
- package/dist/components/sheet-grid/sheet-grid.js +1 -0
- package/dist/components/sheet-grid/sheet-grid.js.map +1 -1
- package/dist/components/ui/accessible-icon.js +1 -0
- package/dist/components/ui/accordion.js +1 -0
- package/dist/components/ui/alert-dialog.js +1 -0
- package/dist/components/ui/alert.js +1 -0
- package/dist/components/ui/aspect-ratio.js +1 -0
- package/dist/components/ui/avatar.js +1 -0
- package/dist/components/ui/badge.js +1 -0
- package/dist/components/ui/breadcrumb.js +1 -0
- package/dist/components/ui/button-group.js +1 -0
- package/dist/components/ui/button.js +1 -0
- package/dist/components/ui/calendar.js +1 -0
- package/dist/components/ui/card.js +1 -0
- package/dist/components/ui/carousel.js +1 -0
- package/dist/components/ui/chart.js +1 -0
- package/dist/components/ui/checkbox.js +1 -0
- package/dist/components/ui/collapsible.js +1 -0
- package/dist/components/ui/command-palette.js +1 -0
- package/dist/components/ui/command.js +1 -0
- package/dist/components/ui/context-menu.js +1 -0
- package/dist/components/ui/date-picker.js +1 -0
- package/dist/components/ui/dialog.js +1 -0
- package/dist/components/ui/drawer.js +1 -0
- package/dist/components/ui/dropdown-menu.js +1 -0
- package/dist/components/ui/hover-card.js +1 -0
- package/dist/components/ui/input-group.js +1 -0
- package/dist/components/ui/input-otp.js +1 -0
- package/dist/components/ui/input.js +1 -0
- package/dist/components/ui/kbd.js +1 -0
- package/dist/components/ui/label.js +1 -0
- package/dist/components/ui/menubar.js +1 -0
- package/dist/components/ui/navigation-menu.js +1 -0
- package/dist/components/ui/pagination.js +1 -0
- package/dist/components/ui/password-toggle-field.js +1 -0
- package/dist/components/ui/popover.js +1 -0
- package/dist/components/ui/progress.js +1 -0
- package/dist/components/ui/radio-group.js +1 -0
- package/dist/components/ui/resizable.js +1 -0
- package/dist/components/ui/scroll-area.js +1 -0
- package/dist/components/ui/select.js +1 -0
- package/dist/components/ui/separator.js +1 -0
- package/dist/components/ui/sheet.js +1 -0
- package/dist/components/ui/skeleton.js +1 -0
- package/dist/components/ui/slider.js +1 -0
- package/dist/components/ui/sonner.js +1 -0
- package/dist/components/ui/spinner.js +1 -0
- package/dist/components/ui/switch.js +1 -0
- package/dist/components/ui/table.js +1 -0
- package/dist/components/ui/tabs.js +1 -0
- package/dist/components/ui/textarea.js +1 -0
- package/dist/components/ui/toggle-group.js +1 -0
- package/dist/components/ui/toggle.js +1 -0
- package/dist/components/ui/toolbar.js +1 -0
- package/dist/components/ui/tooltip.js +1 -0
- package/dist/components/ui/typography.js +1 -0
- package/dist/components/ui/visually-hidden.js +1 -0
- package/dist/hooks/index.d.ts +3 -3
- package/dist/hooks/index.js +4 -3
- package/dist/hooks/use-data-filters.d.ts +1 -1
- package/dist/hooks/use-data-filters.js +3 -2
- package/dist/hooks/use-keyboard-navigation.js +1 -0
- package/dist/hooks/use-mobile.js +1 -0
- package/dist/hooks/use-prefetch.js +1 -0
- package/dist/hooks/use-resource-page.js +1 -0
- package/dist/hooks/use-resource-url-state.d.ts +1 -1
- package/dist/hooks/use-resource-url-state.js +1 -0
- package/dist/hooks/use-saved-views.js +1 -0
- package/dist/hooks/use-view-preference.js +1 -0
- package/dist/hooks/use-view-settings.js +1 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +361 -3
- package/dist/index.js.map +1 -1
- package/dist/navigation-vmFJ6r1m.d.ts +90 -0
- package/dist/router/react-router.d.ts +1 -1
- package/dist/router/react-router.js +1 -0
- package/dist/router/react-router.js.map +1 -1
- package/dist/router/tanstack-router.d.ts +1 -1
- package/dist/router/tanstack-router.js +2 -1
- package/dist/router/tanstack-router.js.map +1 -1
- package/dist/status-colors.js +2 -0
- package/dist/status-colors.js.map +1 -1
- package/dist/{use-resource-table-Dx1OZRne.d.ts → use-resource-table-easmRDQI.d.ts} +2 -2
- package/dist/utils.js +1 -0
- package/package.json +6 -5
- package/dist/AppShell-BVr4V0UK.d.ts +0 -174
- package/dist/chunk-7S7LKQBD.js +0 -12
- package/dist/chunk-7S7LKQBD.js.map +0 -1
- package/dist/chunk-UYI3GLFJ.js.map +0 -1
- package/dist/chunk-WGMEVX3Y.js +0 -141
- package/dist/chunk-WGMEVX3Y.js.map +0 -1
- package/dist/navigation-Bha0R3Fu.d.ts +0 -166
- package/dist/slots-tR0wf89f.d.ts +0 -60
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { ComponentType, ReactNode, AnchorHTMLAttributes } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Router-agnostic navigation primitives.
|
|
5
|
+
*
|
|
6
|
+
* genesis-ui does NOT depend on any router. Components that need to render a
|
|
7
|
+
* link or navigate imperatively accept an injectable abstraction defined here,
|
|
8
|
+
* and default to plain browser behavior (`<a>` / `window.location`). Consumers
|
|
9
|
+
* on a router inject an adapter that maps these props to their own `<Link>` /
|
|
10
|
+
* router. Ready-made ones ship on their own subpaths, each with its router as
|
|
11
|
+
* an optional peer: `@marktiderman/genesis-ui/router/react-router` and
|
|
12
|
+
* `@marktiderman/genesis-ui/router/tanstack-router`. Next.js passes
|
|
13
|
+
* `next/link` directly.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Props passed to an injectable link component. The contract is a plain anchor
|
|
18
|
+
* (`href`), so the default works with NO router. A React Router consumer maps
|
|
19
|
+
* `href` → `to`; a Next.js consumer passes `next/link` directly (it already
|
|
20
|
+
* accepts `href`, `className`, `children`).
|
|
21
|
+
*
|
|
22
|
+
* @stability Beta
|
|
23
|
+
*/
|
|
24
|
+
interface GenesisLinkProps {
|
|
25
|
+
/** Destination path or URL. */
|
|
26
|
+
href: string;
|
|
27
|
+
className?: string;
|
|
28
|
+
onClick?: () => void;
|
|
29
|
+
children?: ReactNode;
|
|
30
|
+
/** Set to "page" when the link points at the active route. */
|
|
31
|
+
"aria-current"?: AnchorHTMLAttributes<HTMLAnchorElement>["aria-current"];
|
|
32
|
+
/** Accessible name — useful when the visible label is hidden (icon rail). */
|
|
33
|
+
"aria-label"?: string;
|
|
34
|
+
/** Native tooltip / title attribute. */
|
|
35
|
+
title?: string;
|
|
36
|
+
/**
|
|
37
|
+
* Stable test/automation selector. {@link DefaultLink} renders it as
|
|
38
|
+
* `data-testid`; injected router adapters (`next/link`, RR `Link`) forward it
|
|
39
|
+
* to the underlying `<a>` too.
|
|
40
|
+
*/
|
|
41
|
+
testID?: string;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* A component that renders a navigable link from {@link GenesisLinkProps}.
|
|
45
|
+
*
|
|
46
|
+
* @stability Beta
|
|
47
|
+
*/
|
|
48
|
+
type LinkComponent = ComponentType<GenesisLinkProps>;
|
|
49
|
+
/**
|
|
50
|
+
* Default link — a plain `<a>`. No router required.
|
|
51
|
+
*
|
|
52
|
+
* @stability Beta
|
|
53
|
+
*/
|
|
54
|
+
declare const DefaultLink: LinkComponent;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Injectable imperative navigation callback.
|
|
58
|
+
*
|
|
59
|
+
* @stability Beta
|
|
60
|
+
*/
|
|
61
|
+
type NavigateFn = (path: string) => void;
|
|
62
|
+
/**
|
|
63
|
+
* Default navigate — a full-page navigation. No router required.
|
|
64
|
+
*
|
|
65
|
+
* @stability Beta
|
|
66
|
+
*/
|
|
67
|
+
declare const defaultNavigate: NavigateFn;
|
|
68
|
+
/**
|
|
69
|
+
* Minimal, router-agnostic replacement for React Router's `useSearchParams`
|
|
70
|
+
* return shape. Consumers may inject their router's implementation for full
|
|
71
|
+
* SPA integration; otherwise the browser History API is used.
|
|
72
|
+
*
|
|
73
|
+
* @stability Beta
|
|
74
|
+
*/
|
|
75
|
+
interface SearchParamsAdapter {
|
|
76
|
+
searchParams: URLSearchParams;
|
|
77
|
+
setSearchParams: (next: URLSearchParams, options?: {
|
|
78
|
+
replace?: boolean;
|
|
79
|
+
}) => void;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Browser-native {@link SearchParamsAdapter} built on the History API. Reads
|
|
83
|
+
* the current query string and writes updates via `pushState`/`replaceState`,
|
|
84
|
+
* staying reactive to `popstate` events. No router required.
|
|
85
|
+
*
|
|
86
|
+
* @stability Beta
|
|
87
|
+
*/
|
|
88
|
+
declare function useBrowserSearchParams(): SearchParamsAdapter;
|
|
89
|
+
|
|
90
|
+
export { DefaultLink as D, type GenesisLinkProps as G, type LinkComponent as L, type NavigateFn as N, type SearchParamsAdapter as S, defaultNavigate as d, useBrowserSearchParams as u };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { isExternalHref } from '../chunk-FUVWKTPL.js';
|
|
3
|
+
import '../chunk-424PT5DM.js';
|
|
3
4
|
import { useCallback, useMemo } from 'react';
|
|
4
5
|
import { Link, useNavigate, useSearchParams, useLocation } from 'react-router';
|
|
5
6
|
import { jsx } from 'react/jsx-runtime';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/router/react-router.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../src/router/react-router.tsx"],"names":[],"mappings":";;;;;;AA2CO,IAAM,kBAAiC,CAAC,EAAE,IAAA,EAAM,MAAA,EAAQ,GAAG,KAAA,EAAM;AAAA;AAAA;AAAA;AAAA;AAAA,kBAKtE,GAAA,CAAC,IAAA,EAAA,EAAK,EAAA,EAAI,IAAA,EAAO,GAAG,KAAA,EAAQ,GAAI,MAAA,KAAW,MAAA,GAAY,EAAE,aAAA,EAAe,MAAA,KAAW,IAAA,EAAO;AAAA;AAYrF,SAAS,sBAAA,GAAqC;AACnD,EAAA,MAAM,WAAW,WAAA,EAAY;AAC7B,EAAA,OAAO,WAAA;AAAA,IACL,CAAC,IAAA,KAAS;AACR,MAAA,IAAI,cAAA,CAAe,IAAI,CAAA,EAAG;AACxB,QAAA,IAAI,OAAO,MAAA,KAAW,WAAA,EAAa,MAAA,CAAO,QAAA,CAAS,OAAO,IAAI,CAAA;AAC9D,QAAA;AAAA,MACF;AACA,MAAA,KAAK,SAAS,IAAI,CAAA;AAAA,IACpB,CAAA;AAAA,IACA,CAAC,QAAQ;AAAA,GACX;AACF;AAWO,SAAS,0BAAA,GAAkD;AAChE,EAAA,MAAM,CAAC,YAAY,CAAA,GAAI,eAAA,EAAgB;AACvC,EAAA,MAAM,EAAE,IAAA,EAAK,GAAI,WAAA,EAAY;AAC7B,EAAA,MAAM,WAAW,WAAA,EAAY;AAE7B,EAAA,MAAM,eAAA,GAAkB,WAAA;AAAA,IACtB,CAAC,MAAM,OAAA,KAAY;AACjB,MAAA,MAAM,EAAA,GAAK,KAAK,QAAA,EAAS;AACzB,MAAA,KAAK,QAAA;AAAA,QACH,EAAE,MAAA,EAAQ,EAAA,GAAK,IAAI,EAAE,CAAA,CAAA,GAAK,IAAI,IAAA,EAAK;AAAA,QACnC,EAAE,OAAA,EAAS,OAAA,EAAS,OAAA;AAAQ,OAC9B;AAAA,IACF,CAAA;AAAA,IACA,CAAC,UAAU,IAAI;AAAA,GACjB;AAEA,EAAA,OAAO,OAAA;AAAA,IACL,OAAO,EAAE,YAAA,EAAc,eAAA,EAAgB,CAAA;AAAA,IACvC,CAAC,cAAc,eAAe;AAAA,GAChC;AACF;AASO,SAAS,wBAAA,GAAmC;AACjD,EAAA,MAAM,EAAE,QAAA,EAAU,MAAA,EAAQ,IAAA,KAAS,WAAA,EAAY;AAC/C,EAAA,OAAO,CAAA,EAAG,QAAQ,CAAA,EAAG,MAAM,GAAG,IAAI,CAAA,CAAA;AACpC","file":"react-router.js","sourcesContent":["\"use client\";\n\n/**\n * React Router (v7, library or framework mode) adapters for genesis-ui's\n * router-agnostic navigation contract.\n *\n * Import from `@marktiderman/genesis-ui/router/react-router`. This is the only\n * genesis-ui entry that imports `react-router`, and `react-router` is an\n * OPTIONAL peer: apps on another router never install it, and apps that do\n * not import this subpath never load it.\n *\n * ```tsx\n * import { AppShell } from \"@marktiderman/genesis-ui/layout\";\n * import {\n * ReactRouterLink,\n * useReactRouterActivePath,\n * } from \"@marktiderman/genesis-ui/router/react-router\";\n *\n * <AppShell linkComponent={ReactRouterLink} activePath={useReactRouterActivePath()} … />\n * ```\n */\nimport { useCallback, useMemo } from \"react\";\nimport { Link, useLocation, useNavigate, useSearchParams } from \"react-router\";\nimport type {\n LinkComponent,\n NavigateFn,\n SearchParamsAdapter,\n} from \"../navigation\";\nimport { isExternalHref } from \"./utils\";\n\n/**\n * {@link LinkComponent} for React Router. Pass it as `linkComponent` to\n * `AppShell`, `StatCard` and anything else that takes one. `href` maps to\n * React Router's `to` (query and hash included), and `testID` is forwarded as\n * `data-testid`, as `DefaultLink` does.\n *\n * Genesis decides the active state and passes `aria-current` itself. This is\n * a plain `Link`, not a `NavLink`, so the router never adds a second opinion.\n * It forwards `ref` and unknown props, so it works as the child of a Radix\n * `asChild` (`<Button asChild>`).\n *\n * @stability Beta\n */\nexport const ReactRouterLink: LinkComponent = ({ href, testID, ...props }) => (\n // `props` carries `ref` and anything a Radix `asChild` parent merges in;\n // React Router's `Link` forwards both to the anchor. `testID` wins over a\n // `data-testid` in `props` (a parent's may be `undefined`), but only when\n // given, so a parent's real one survives.\n <Link to={href} {...props} {...(testID !== undefined ? { \"data-testid\": testID } : null)} />\n);\n\n/**\n * {@link NavigateFn} backed by React Router's `useNavigate()`. Pass it as\n * `ResourcePage`'s `onNavigate` (or anywhere a `NavigateFn` is accepted) for\n * client-side navigation instead of a full page load. Absolute URLs\n * (`https://…`, `mailto:…`, `//host/…`) leave the app with a document\n * navigation, as `defaultNavigate` does.\n *\n * @stability Beta\n */\nexport function useReactRouterNavigate(): NavigateFn {\n const navigate = useNavigate();\n return useCallback<NavigateFn>(\n (path) => {\n if (isExternalHref(path)) {\n if (typeof window !== \"undefined\") window.location.assign(path);\n return;\n }\n void navigate(path);\n },\n [navigate],\n );\n}\n\n/**\n * {@link SearchParamsAdapter} backed by React Router's location. Pass it as\n * `useDataFilters(key, defs, { searchParams })` so URL-synced filters go\n * through the router (loaders re-run, back/forward stays in sync) instead of\n * the raw History API. Like the default browser adapter, an update keeps the\n * current `#hash`.\n *\n * @stability Beta\n */\nexport function useReactRouterSearchParams(): SearchParamsAdapter {\n const [searchParams] = useSearchParams();\n const { hash } = useLocation();\n const navigate = useNavigate();\n\n const setSearchParams = useCallback<SearchParamsAdapter[\"setSearchParams\"]>(\n (next, options) => {\n const qs = next.toString();\n void navigate(\n { search: qs ? `?${qs}` : \"\", hash },\n { replace: options?.replace },\n );\n },\n [navigate, hash],\n );\n\n return useMemo(\n () => ({ searchParams, setSearchParams }),\n [searchParams, setSearchParams],\n );\n}\n\n/**\n * The current location as `pathname + search + hash`, the shape `AppShell`'s\n * `activePath` expects, so nav items whose `to` names a query or hash\n * (`/admin?tab=users`, `/settings#profile`) highlight correctly.\n *\n * @stability Beta\n */\nexport function useReactRouterActivePath(): string {\n const { pathname, search, hash } = useLocation();\n return `${pathname}${search}${hash}`;\n}\n"]}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { isExternalHref, splitNavigationHref } from '../chunk-FUVWKTPL.js';
|
|
3
|
-
import { DefaultLink } from '../chunk-
|
|
3
|
+
import { DefaultLink } from '../chunk-J45FHL7D.js';
|
|
4
|
+
import '../chunk-424PT5DM.js';
|
|
4
5
|
import { useCallback, useMemo } from 'react';
|
|
5
6
|
import { useRouter, useRouterState, useLinkProps } from '@tanstack/react-router';
|
|
6
7
|
import { jsx } from 'react/jsx-runtime';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/router/tanstack-router.tsx"],"names":[],"mappings":"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","file":"tanstack-router.js","sourcesContent":["\"use client\";\n\n/**\n * TanStack Router adapters for genesis-ui's router-agnostic navigation\n * contract.\n *\n * Import from `@marktiderman/genesis-ui/router/tanstack-router`. This is the\n * only genesis-ui entry that imports `@tanstack/react-router`, and that\n * package is an OPTIONAL peer: apps on another router never install it, and\n * apps that do not import this subpath never load it.\n *\n * Genesis hands links a plain string `href` (`/admin?tab=users#top`), while\n * TanStack Router wants `to`, `search` and `hash` separately. These adapters\n * do that split, and they parse the query with the router's OWN\n * `parseSearch`, so a custom search serializer and `validateSearch` see the\n * same values a typed `<Link search={…}>` would produce.\n *\n * ```tsx\n * import { AppShell } from \"@marktiderman/genesis-ui/layout\";\n * import {\n * TanStackRouterLink,\n * useTanStackRouterActivePath,\n * } from \"@marktiderman/genesis-ui/router/tanstack-router\";\n *\n * <AppShell linkComponent={TanStackRouterLink} activePath={useTanStackRouterActivePath()} … />\n * ```\n */\nimport { useCallback, useMemo, type AnchorHTMLAttributes, type Ref } from \"react\";\nimport {\n useLinkProps,\n useRouter,\n useRouterState,\n type AnyRouter,\n} from \"@tanstack/react-router\";\nimport {\n DefaultLink,\n type GenesisLinkProps,\n type LinkComponent,\n type NavigateFn,\n type SearchParamsAdapter,\n} from \"../navigation\";\nimport { isExternalHref, splitNavigationHref } from \"./utils\";\n\n// Genesis owns the active state (it passes `aria-current` itself, computed by\n// `isPathActive` or the app's `isActive`). TanStack's Link adds its own —\n// `aria-current=\"page\"`, `data-status=\"active\"` and an `active` class —\n// whenever ITS prefix match passes, which can disagree with Genesis (an\n// exact-match `/app` item, or a `?tab=` item) and would leave two items\n// announced as the current page. An empty `activeProps` stops the class;\n// the attributes are overridden below.\nconst NO_ACTIVE_PROPS = {};\n\n/**\n * The navigation target for a Genesis href, with the query parsed by the\n * router's own `parseSearch` (what `<Link search>` expects).\n */\nfunction toLocation(router: AnyRouter, href: string) {\n const { pathname, search, hash } = splitNavigationHref(href);\n return {\n // `\"\"` (a bare `?q=1` or `#x`) means \"this page\" — TanStack's `.`.\n to: pathname || \".\",\n search: search ? router.options.parseSearch(`?${search}`) : undefined,\n hash: hash || undefined,\n };\n}\n\nfunction InAppLink({\n href,\n testID,\n children,\n \"aria-current\": ariaCurrent,\n ref,\n ...rest\n}: GenesisLinkProps & { ref?: Ref<HTMLAnchorElement> }) {\n const router = useRouter();\n const target = useMemo(() => toLocation(router, href), [router, href]);\n // `useLinkProps` is the hook behind TanStack's `<Link>`: client-side\n // navigation on click, `href` built through the router (basepath, rewrites,\n // masks), preloading per the router's `defaultPreload`. Props it does not\n // know (anything a Radix `asChild` parent merges in) pass through to the\n // anchor, and `ref` is merged with the one it needs for preloading.\n const linkProps = useLinkProps(\n {\n ...target,\n ...rest,\n activeProps: NO_ACTIVE_PROPS,\n // The adapter is route-tree agnostic, so it cannot satisfy the generated\n // route types; the target is checked at runtime by the router instead.\n } as Parameters<typeof useLinkProps>[0],\n ref as Parameters<typeof useLinkProps>[1],\n ) as AnchorHTMLAttributes<HTMLAnchorElement> & {\n \"data-status\"?: string;\n ref?: Ref<HTMLAnchorElement>;\n };\n const { \"data-status\": _status, ...anchorProps } = linkProps;\n return (\n <a\n {...anchorProps}\n aria-current={ariaCurrent}\n // Only when given, so a `data-testid` merged in by an `asChild` parent survives.\n {...(testID !== undefined ? { \"data-testid\": testID } : null)}\n >\n {children}\n </a>\n );\n}\n\n/**\n * {@link LinkComponent} for TanStack Router. Pass it as `linkComponent` to\n * `AppShell`, `StatCard` and anything else that takes one. In-app hrefs\n * navigate client-side (query and hash included, parsed by the router's\n * `parseSearch`); absolute URLs render a plain anchor. `testID` is forwarded\n * as `data-testid`, as `DefaultLink` does, and the active state stays the one\n * Genesis passes: TanStack's own `aria-current` / `active` class are not\n * applied. It forwards `ref` and unknown props, so it works as the child of a\n * Radix `asChild` (`<Button asChild>`).\n *\n * @stability Beta\n */\nexport const TanStackRouterLink: LinkComponent = (props) =>\n isExternalHref(props.href) ? <DefaultLink {...props} /> : <InAppLink {...props} />;\n\n/**\n * {@link NavigateFn} backed by the TanStack router. Pass it as\n * `ResourcePage`'s `onNavigate` (or anywhere a `NavigateFn` is accepted) for\n * client-side navigation instead of a full page load. Absolute URLs\n * (`https://…`, `mailto:…`, `//host/…`) leave the app with a document\n * navigation, as `defaultNavigate` does.\n *\n * @stability Beta\n */\nexport function useTanStackRouterNavigate(): NavigateFn {\n const router = useRouter();\n return useCallback<NavigateFn>(\n (path) => {\n if (isExternalHref(path)) {\n if (typeof window !== \"undefined\") window.location.assign(path);\n return;\n }\n void router.navigate(\n toLocation(router, path) as Parameters<AnyRouter[\"navigate\"]>[0],\n );\n },\n [router],\n );\n}\n\n/**\n * {@link SearchParamsAdapter} backed by the TanStack router's location. Pass\n * it as `useDataFilters(key, defs, { searchParams })` so URL-synced filters go\n * through the router (loaders and `validateSearch` run, back/forward stays in\n * sync) instead of the raw History API. Like the default browser adapter, an\n * update keeps the current path and `#hash`.\n *\n * @stability Beta\n */\nexport function useTanStackRouterSearchParams(): SearchParamsAdapter {\n const router = useRouter();\n const searchStr = useRouterState({\n select: (state) => state.location.searchStr,\n });\n const searchParams = useMemo(\n () => new URLSearchParams(searchStr),\n [searchStr],\n );\n\n const setSearchParams = useCallback<SearchParamsAdapter[\"setSearchParams\"]>(\n (next, options) => {\n const qs = next.toString();\n void router.navigate({\n // The current URL path, not `.`: `.` resolves against the matched\n // route's template, which is not this URL under a splat or\n // not-found route.\n to: router.state.location.pathname,\n search: router.options.parseSearch(qs ? `?${qs}` : \"\"),\n hash: true,\n replace: options?.replace,\n } as Parameters<AnyRouter[\"navigate\"]>[0]);\n },\n [router],\n );\n\n return useMemo(\n () => ({ searchParams, setSearchParams }),\n [searchParams, setSearchParams],\n );\n}\n\n/**\n * The current location as `pathname + search + hash`, the shape `AppShell`'s\n * `activePath` expects, so nav items whose `to` names a query or hash\n * (`/admin?tab=users`, `/settings#profile`) highlight correctly. Paths are the\n * router's (basepath removed), matching the `to` your nav items use.\n *\n * @stability Beta\n */\nexport function useTanStackRouterActivePath(): string {\n return useRouterState({\n select: ({ location }) => {\n const search =\n location.searchStr && !location.searchStr.startsWith(\"?\")\n ? `?${location.searchStr}`\n : location.searchStr;\n return `${location.pathname}${search}${location.hash ? `#${location.hash}` : \"\"}`;\n },\n });\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/router/tanstack-router.tsx"],"names":[],"mappings":"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","file":"tanstack-router.js","sourcesContent":["\"use client\";\n\n/**\n * TanStack Router adapters for genesis-ui's router-agnostic navigation\n * contract.\n *\n * Import from `@marktiderman/genesis-ui/router/tanstack-router`. This is the\n * only genesis-ui entry that imports `@tanstack/react-router`, and that\n * package is an OPTIONAL peer: apps on another router never install it, and\n * apps that do not import this subpath never load it.\n *\n * Genesis hands links a plain string `href` (`/admin?tab=users#top`), while\n * TanStack Router wants `to`, `search` and `hash` separately. These adapters\n * do that split, and they parse the query with the router's OWN\n * `parseSearch`, so a custom search serializer and `validateSearch` see the\n * same values a typed `<Link search={…}>` would produce.\n *\n * ```tsx\n * import { AppShell } from \"@marktiderman/genesis-ui/layout\";\n * import {\n * TanStackRouterLink,\n * useTanStackRouterActivePath,\n * } from \"@marktiderman/genesis-ui/router/tanstack-router\";\n *\n * <AppShell linkComponent={TanStackRouterLink} activePath={useTanStackRouterActivePath()} … />\n * ```\n */\nimport { useCallback, useMemo, type AnchorHTMLAttributes, type Ref } from \"react\";\nimport {\n useLinkProps,\n useRouter,\n useRouterState,\n type AnyRouter,\n} from \"@tanstack/react-router\";\nimport {\n DefaultLink,\n type GenesisLinkProps,\n type LinkComponent,\n type NavigateFn,\n type SearchParamsAdapter,\n} from \"../navigation\";\nimport { isExternalHref, splitNavigationHref } from \"./utils\";\n\n// Genesis owns the active state (it passes `aria-current` itself, computed by\n// `isPathActive` or the app's `isActive`). TanStack's Link adds its own —\n// `aria-current=\"page\"`, `data-status=\"active\"` and an `active` class —\n// whenever ITS prefix match passes, which can disagree with Genesis (an\n// exact-match `/app` item, or a `?tab=` item) and would leave two items\n// announced as the current page. An empty `activeProps` stops the class;\n// the attributes are overridden below.\nconst NO_ACTIVE_PROPS = {};\n\n/**\n * The navigation target for a Genesis href, with the query parsed by the\n * router's own `parseSearch` (what `<Link search>` expects).\n */\nfunction toLocation(router: AnyRouter, href: string) {\n const { pathname, search, hash } = splitNavigationHref(href);\n return {\n // `\"\"` (a bare `?q=1` or `#x`) means \"this page\" — TanStack's `.`.\n to: pathname || \".\",\n search: search ? router.options.parseSearch(`?${search}`) : undefined,\n hash: hash || undefined,\n };\n}\n\nfunction InAppLink({\n href,\n testID,\n children,\n \"aria-current\": ariaCurrent,\n ref,\n ...rest\n}: GenesisLinkProps & { ref?: Ref<HTMLAnchorElement> }) {\n const router = useRouter();\n const target = useMemo(() => toLocation(router, href), [router, href]);\n // `useLinkProps` is the hook behind TanStack's `<Link>`: client-side\n // navigation on click, `href` built through the router (basepath, rewrites,\n // masks), preloading per the router's `defaultPreload`. Props it does not\n // know (anything a Radix `asChild` parent merges in) pass through to the\n // anchor, and `ref` is merged with the one it needs for preloading.\n const linkProps = useLinkProps(\n {\n ...target,\n ...rest,\n activeProps: NO_ACTIVE_PROPS,\n // The adapter is route-tree agnostic, so it cannot satisfy the generated\n // route types; the target is checked at runtime by the router instead.\n } as Parameters<typeof useLinkProps>[0],\n ref as Parameters<typeof useLinkProps>[1],\n ) as AnchorHTMLAttributes<HTMLAnchorElement> & {\n \"data-status\"?: string;\n ref?: Ref<HTMLAnchorElement>;\n };\n const { \"data-status\": _status, ...anchorProps } = linkProps;\n return (\n <a\n {...anchorProps}\n aria-current={ariaCurrent}\n // Only when given, so a `data-testid` merged in by an `asChild` parent survives.\n {...(testID !== undefined ? { \"data-testid\": testID } : null)}\n >\n {children}\n </a>\n );\n}\n\n/**\n * {@link LinkComponent} for TanStack Router. Pass it as `linkComponent` to\n * `AppShell`, `StatCard` and anything else that takes one. In-app hrefs\n * navigate client-side (query and hash included, parsed by the router's\n * `parseSearch`); absolute URLs render a plain anchor. `testID` is forwarded\n * as `data-testid`, as `DefaultLink` does, and the active state stays the one\n * Genesis passes: TanStack's own `aria-current` / `active` class are not\n * applied. It forwards `ref` and unknown props, so it works as the child of a\n * Radix `asChild` (`<Button asChild>`).\n *\n * @stability Beta\n */\nexport const TanStackRouterLink: LinkComponent = (props) =>\n isExternalHref(props.href) ? <DefaultLink {...props} /> : <InAppLink {...props} />;\n\n/**\n * {@link NavigateFn} backed by the TanStack router. Pass it as\n * `ResourcePage`'s `onNavigate` (or anywhere a `NavigateFn` is accepted) for\n * client-side navigation instead of a full page load. Absolute URLs\n * (`https://…`, `mailto:…`, `//host/…`) leave the app with a document\n * navigation, as `defaultNavigate` does.\n *\n * @stability Beta\n */\nexport function useTanStackRouterNavigate(): NavigateFn {\n const router = useRouter();\n return useCallback<NavigateFn>(\n (path) => {\n if (isExternalHref(path)) {\n if (typeof window !== \"undefined\") window.location.assign(path);\n return;\n }\n void router.navigate(\n toLocation(router, path) as Parameters<AnyRouter[\"navigate\"]>[0],\n );\n },\n [router],\n );\n}\n\n/**\n * {@link SearchParamsAdapter} backed by the TanStack router's location. Pass\n * it as `useDataFilters(key, defs, { searchParams })` so URL-synced filters go\n * through the router (loaders and `validateSearch` run, back/forward stays in\n * sync) instead of the raw History API. Like the default browser adapter, an\n * update keeps the current path and `#hash`.\n *\n * @stability Beta\n */\nexport function useTanStackRouterSearchParams(): SearchParamsAdapter {\n const router = useRouter();\n const searchStr = useRouterState({\n select: (state) => state.location.searchStr,\n });\n const searchParams = useMemo(\n () => new URLSearchParams(searchStr),\n [searchStr],\n );\n\n const setSearchParams = useCallback<SearchParamsAdapter[\"setSearchParams\"]>(\n (next, options) => {\n const qs = next.toString();\n void router.navigate({\n // The current URL path, not `.`: `.` resolves against the matched\n // route's template, which is not this URL under a splat or\n // not-found route.\n to: router.state.location.pathname,\n search: router.options.parseSearch(qs ? `?${qs}` : \"\"),\n hash: true,\n replace: options?.replace,\n } as Parameters<AnyRouter[\"navigate\"]>[0]);\n },\n [router],\n );\n\n return useMemo(\n () => ({ searchParams, setSearchParams }),\n [searchParams, setSearchParams],\n );\n}\n\n/**\n * The current location as `pathname + search + hash`, the shape `AppShell`'s\n * `activePath` expects, so nav items whose `to` names a query or hash\n * (`/admin?tab=users`, `/settings#profile`) highlight correctly. Paths are the\n * router's (basepath removed), matching the `to` your nav items use.\n *\n * @stability Beta\n */\nexport function useTanStackRouterActivePath(): string {\n return useRouterState({\n select: ({ location }) => {\n const search =\n location.searchStr && !location.searchStr.startsWith(\"?\")\n ? `?${location.searchStr}`\n : location.searchStr;\n return `${location.pathname}${search}${location.hash ? `#${location.hash}` : \"\"}`;\n },\n });\n}\n"]}
|
package/dist/status-colors.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/status-colors.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/status-colors.ts"],"names":[],"mappings":";;;AAQO,IAAM,eAAA,GAA0C;AAAA,EACrD,IAAA,EAAM,+DAAA;AAAA,EACN,QAAA,EAAU,mEAAA;AAAA,EACV,MAAA,EAAQ,kEAAA;AAAA,EACR,MAAA,EAAQ,0EAAA;AAAA,EACR,QAAA,EAAU,sEAAA;AAAA,EACV,OAAA,EAAS;AACX;AAGO,IAAM,gBAAA,GAA2C;AAAA,EACtD,IAAA,EAAM,+DAAA;AAAA,EACN,WAAA,EAAa,sEAAA;AAAA,EACb,SAAA,EAAW,kEAAA;AAAA,EACX,IAAA,EAAM,8EAAA;AAAA,EACN,OAAA,EAAS;AACX;AAGO,IAAM,mBAAA,GAA8C;AAAA,EACzD,UAAA,EAAY,8EAAA;AAAA,EACZ,MAAA,EAAQ,kEAAA;AAAA,EACR,MAAA,EAAQ;AACV;AAGO,IAAM,gBAAA,GAA2C;AAAA,EACtD,GAAA,EAAK,+DAAA;AAAA,EACL,QAAA,EAAU,kEAAA;AAAA,EACV,QAAA,EAAU,8EAAA;AAAA,EACV,SAAA,EAAW;AACb;AAGO,IAAM,oBAAA,GAA+C;AAAA,EAC1D,GAAA,EAAK,+DAAA;AAAA,EACL,QAAA,EAAU,kEAAA;AAAA,EACV,QAAA,EAAU,8EAAA;AAAA,EACV,QAAA,EAAU;AACZ;AAGO,IAAM,mBAAA,GAA8C;AAAA,EACzD,OAAA,EAAS,8EAAA;AAAA,EACT,GAAA,EAAK,kEAAA;AAAA,EACL,WAAA,EAAa,sEAAA;AAAA,EACb,QAAA,EAAU;AACZ;AAGO,IAAM,oBAAA,GAA+C;AAAA,EAC1D,SAAA,EAAW,8EAAA;AAAA,EACX,WAAA,EAAa,kEAAA;AAAA,EACb,OAAA,EAAS,sEAAA;AAAA,EACT,OAAA,EAAS;AACX;AAGO,IAAM,gBAAA,GAA2C;AAAA,EACtD,MAAA,EAAQ,8EAAA;AAAA,EACR,UAAA,EAAY,sEAAA;AAAA,EACZ,MAAA,EAAQ,sEAAA;AAAA,EACR,QAAA,EAAU;AACZ;AAGO,SAAS,iBAAiB,GAAA,EAAqB;AACpD,EAAA,IAAI,GAAA,IAAO,IAAI,OAAO,gBAAA;AACtB,EAAA,IAAI,GAAA,IAAO,IAAI,OAAO,cAAA;AACtB,EAAA,OAAO,YAAA;AACT;AAEO,SAAS,qBAAqB,GAAA,EAAqB;AACxD,EAAA,IAAI,GAAA,IAAO,IAAI,OAAO,wCAAA;AACtB,EAAA,IAAI,GAAA,IAAO,IAAI,OAAO,oCAAA;AACtB,EAAA,OAAO,gCAAA;AACT;AAGO,IAAM,eAAA,GAA0C;AAAA,EACrD,GAAG,eAAA;AAAA,EACH,GAAG,gBAAA;AAAA,EACH,GAAG,mBAAA;AAAA,EACH,GAAG,gBAAA;AAAA,EACH,GAAG;AACL","file":"status-colors.js","sourcesContent":["/**\n * Shared status color mappings\n *\n * Single source of truth for all status → color badge mappings.\n * Used across Command Center, Product Detail, Task Board, Roadmap, etc.\n */\n\n/** PRD statuses */\nexport const prdStatusColors: Record<string, string> = {\n idea: \"bg-zinc-100 text-zinc-600 dark:bg-zinc-800 dark:text-zinc-400\",\n drafting: \"bg-slate-100 text-slate-700 dark:bg-slate-800 dark:text-slate-400\",\n active: \"bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400\",\n locked: \"bg-purple-100 text-purple-700 dark:bg-purple-900/30 dark:text-purple-400\",\n building: \"bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400\",\n shipped: \"bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-400\",\n};\n\n/** Task statuses */\nexport const taskStatusColors: Record<string, string> = {\n todo: \"bg-zinc-100 text-zinc-600 dark:bg-zinc-800 dark:text-zinc-400\",\n in_progress: \"bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400\",\n in_review: \"bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400\",\n done: \"bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-400\",\n blocked: \"bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400\",\n};\n\n/** Product statuses */\nexport const productStatusColors: Record<string, string> = {\n production: \"bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-400\",\n active: \"bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400\",\n design: \"bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400\",\n};\n\n/** Idea statuses */\nexport const ideaStatusColors: Record<string, string> = {\n new: \"bg-zinc-100 text-zinc-600 dark:bg-zinc-800 dark:text-zinc-400\",\n reviewed: \"bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400\",\n promoted: \"bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-400\",\n dismissed: \"bg-slate-100 text-slate-600 dark:bg-slate-800 dark:text-slate-400\",\n};\n\n/** Feedback statuses */\nexport const feedbackStatusColors: Record<string, string> = {\n new: \"bg-zinc-100 text-zinc-600 dark:bg-zinc-800 dark:text-zinc-400\",\n reviewed: \"bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400\",\n promoted: \"bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-400\",\n archived: \"bg-slate-100 text-slate-600 dark:bg-slate-800 dark:text-slate-400\",\n};\n\n/** Changelog entry types */\nexport const changelogTypeColors: Record<string, string> = {\n feature: \"bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-400\",\n fix: \"bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400\",\n improvement: \"bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400\",\n breaking: \"bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400\",\n};\n\n/** Research recommended actions */\nexport const researchActionColors: Record<string, string> = {\n implement: \"bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-400\",\n investigate: \"bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400\",\n monitor: \"bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400\",\n dismiss: \"bg-zinc-100 text-zinc-600 dark:bg-zinc-800 dark:text-zinc-400\",\n};\n\n/** Sync statuses */\nexport const syncStatusColors: Record<string, string> = {\n synced: \"bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-400\",\n pending_pr: \"bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400\",\n behind: \"bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400\",\n diverged: \"bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400\",\n};\n\n/** Coverage color helpers */\nexport function getCoverageColor(pct: number): string {\n if (pct >= 70) return \"bg-emerald-500\";\n if (pct >= 30) return \"bg-amber-500\";\n return \"bg-red-500\";\n}\n\nexport function getCoverageTextColor(pct: number): string {\n if (pct >= 70) return \"text-emerald-600 dark:text-emerald-400\";\n if (pct >= 30) return \"text-amber-600 dark:text-amber-400\";\n return \"text-red-600 dark:text-red-400\";\n}\n\n/** Combined lookup — useful when you don't know the entity type */\nexport const allStatusColors: Record<string, string> = {\n ...prdStatusColors,\n ...taskStatusColors,\n ...productStatusColors,\n ...ideaStatusColors,\n ...feedbackStatusColors,\n};\n"]}
|
|
@@ -8,11 +8,11 @@ import { V as ViewMode } from './view-toggle-CahQ87PW.js';
|
|
|
8
8
|
import { Density } from './hooks/use-view-settings.js';
|
|
9
9
|
import { WizardStep, MutationMode, UndoableWrite, UseResourceOptions } from '@marktiderman/genesis-core/hooks';
|
|
10
10
|
import { ResourceFormFieldDef } from '@marktiderman/genesis-core';
|
|
11
|
-
import {
|
|
11
|
+
import { Slots, SlotPropsFor } from '@marktiderman/genesis-core/slots';
|
|
12
12
|
import { ViewModeDefault } from './hooks/use-view-preference.js';
|
|
13
13
|
import { ResourceUrlStateOptions } from './hooks/use-resource-url-state.js';
|
|
14
14
|
import { FilterParam, SortParam } from '@marktiderman/genesis-core/provider';
|
|
15
|
-
import {
|
|
15
|
+
import { N as NavigateFn, S as SearchParamsAdapter } from './navigation-vmFJ6r1m.js';
|
|
16
16
|
|
|
17
17
|
interface DataPageShellProps {
|
|
18
18
|
/** Display title for the page */
|
package/dist/utils.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@marktiderman/genesis-ui",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.12.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -353,7 +353,7 @@
|
|
|
353
353
|
}
|
|
354
354
|
},
|
|
355
355
|
"peerDependencies": {
|
|
356
|
-
"@marktiderman/genesis-design-system": "^1.
|
|
356
|
+
"@marktiderman/genesis-design-system": "^1.4.0",
|
|
357
357
|
"@tanstack/react-router": "^1.0.0",
|
|
358
358
|
"@tiptap/core": "^3.31.0",
|
|
359
359
|
"@tiptap/extensions": "^3.31.0",
|
|
@@ -378,7 +378,7 @@
|
|
|
378
378
|
"vaul": "^1.0.0"
|
|
379
379
|
},
|
|
380
380
|
"dependencies": {
|
|
381
|
-
"@marktiderman/genesis-core": "^1.
|
|
381
|
+
"@marktiderman/genesis-core": "^1.7.0",
|
|
382
382
|
"@tanstack/react-table": "^8.21.3",
|
|
383
383
|
"@tanstack/react-virtual": "^3.14.0",
|
|
384
384
|
"class-variance-authority": "^0.7.1",
|
|
@@ -387,7 +387,7 @@
|
|
|
387
387
|
},
|
|
388
388
|
"devDependencies": {
|
|
389
389
|
"@hookform/resolvers": "^5.2.2",
|
|
390
|
-
"@marktiderman/genesis-design-system": "^1.
|
|
390
|
+
"@marktiderman/genesis-design-system": "^1.4.0",
|
|
391
391
|
"@radix-ui/react-accordion": "^1.2.0",
|
|
392
392
|
"@radix-ui/react-aspect-ratio": "^1.1.0",
|
|
393
393
|
"@radix-ui/react-avatar": "^1.1.0",
|
|
@@ -459,6 +459,7 @@
|
|
|
459
459
|
"build": "tsup && node scripts/stamp-use-client.mjs && cp src/styles.css dist/styles.css && node ../../scripts/check-exports-resolve.mjs",
|
|
460
460
|
"clean": "rm -rf dist",
|
|
461
461
|
"check:exports": "node ../../scripts/check-exports-resolve.mjs",
|
|
462
|
-
"test": "vitest run"
|
|
462
|
+
"test": "vitest run",
|
|
463
|
+
"typecheck": "tsc -p tsconfig.type-tests.json"
|
|
463
464
|
}
|
|
464
465
|
}
|
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
|
-
import { ReactNode } from 'react';
|
|
3
|
-
import { LucideIcon } from 'lucide-react';
|
|
4
|
-
import { N as NavigationNode, L as LinkComponent } from './navigation-Bha0R3Fu.js';
|
|
5
|
-
|
|
6
|
-
interface NavItem {
|
|
7
|
-
to: string;
|
|
8
|
-
icon: LucideIcon;
|
|
9
|
-
label: string;
|
|
10
|
-
/** Nested items — rendered as a collapsible group when the rail is expanded */
|
|
11
|
-
children?: NavItem[];
|
|
12
|
-
badge?: string | number;
|
|
13
|
-
disabled?: boolean;
|
|
14
|
-
/**
|
|
15
|
-
* Exact-match active state (e.g. overview `/`). Exact on the path only, so
|
|
16
|
-
* an `activePath` carrying `?search#hash` still matches.
|
|
17
|
-
*/
|
|
18
|
-
end?: boolean;
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* The "up" link AppShell renders above page content: a real link to the
|
|
22
|
-
* logical parent, never `history.back()`.
|
|
23
|
-
*
|
|
24
|
-
* @stability Beta
|
|
25
|
-
*/
|
|
26
|
-
interface ParentNavigation {
|
|
27
|
-
href: string;
|
|
28
|
-
label: string;
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Declarative chrome for AppShell — developer defaults + optional
|
|
32
|
-
* in-sidebar user toggles (Notion/Linear “customize chrome” pattern).
|
|
33
|
-
*
|
|
34
|
-
* @stability Beta
|
|
35
|
-
*/
|
|
36
|
-
interface AppShellOptions {
|
|
37
|
-
showLogo?: boolean;
|
|
38
|
-
showAppName?: boolean;
|
|
39
|
-
showThemeToggle?: boolean;
|
|
40
|
-
/** Desktop rail collapse control */
|
|
41
|
-
showCollapse?: boolean;
|
|
42
|
-
showUserSlot?: boolean;
|
|
43
|
-
showFooter?: boolean;
|
|
44
|
-
showSecondaryNav?: boolean;
|
|
45
|
-
/** Mobile fixed bottom tab bar */
|
|
46
|
-
showMobileBottomNav?: boolean;
|
|
47
|
-
defaultCollapsed?: boolean;
|
|
48
|
-
/**
|
|
49
|
-
* Show the in-sidebar Sidebar options popover so users can customize chrome.
|
|
50
|
-
* Default true when collapse / secondary / theme / slots are available.
|
|
51
|
-
*/
|
|
52
|
-
configurable?: boolean;
|
|
53
|
-
/**
|
|
54
|
-
* Toggle keys the developer has decided on and users may not override —
|
|
55
|
-
* e.g. hiding `showThemeToggle` for a brand that manages theme elsewhere.
|
|
56
|
-
* Locked keys are dropped from the options popover and any stored user
|
|
57
|
-
* override for them is ignored on resolve.
|
|
58
|
-
*/
|
|
59
|
-
lockedOptions?: (keyof AppShellOptions)[];
|
|
60
|
-
/** Comfortable (default) vs compact row padding */
|
|
61
|
-
density?: "comfortable" | "compact";
|
|
62
|
-
/** Expanded rail width in rem-ish tokens: narrow ≈ 13rem, wide ≈ 16rem */
|
|
63
|
-
railWidth?: "narrow" | "wide";
|
|
64
|
-
/**
|
|
65
|
-
* Where page content scrolls.
|
|
66
|
-
* - `container` (default): main panel scrolls — classic app-shell chrome.
|
|
67
|
-
* - `window`: document/window scrolls with a sticky rail so React Router
|
|
68
|
-
* `ScrollRestoration` keeps working (dashboard dogfood).
|
|
69
|
-
*/
|
|
70
|
-
scrollRoot?: "container" | "window";
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* @stability Beta
|
|
74
|
-
*/
|
|
75
|
-
interface AppShellProps {
|
|
76
|
-
/** App name shown in sidebar header */
|
|
77
|
-
appName: string;
|
|
78
|
-
/** App logo letter or short text (ignored when `logo` is set) */
|
|
79
|
-
appLogo?: string;
|
|
80
|
-
/** Custom logo node — preferred over `appLogo` letter tile */
|
|
81
|
-
logo?: ReactNode;
|
|
82
|
-
/**
|
|
83
|
-
* Primary navigation items. Optional when `navigation` is set: the tree
|
|
84
|
-
* root's `items` are the global nav then.
|
|
85
|
-
*/
|
|
86
|
-
navItems?: NavItem[];
|
|
87
|
-
/**
|
|
88
|
-
* The app's logical navigation hierarchy. AppShell resolves it against
|
|
89
|
-
* `activePath` (see `resolveNavigation`) and derives:
|
|
90
|
-
* - the sidebar's global items from the root's `items` (unless `navItems`),
|
|
91
|
-
* - a contextual section from the current node's `items`, labelled with
|
|
92
|
-
* the node's `label` (`$param` segments in `to` filled from the URL),
|
|
93
|
-
* - the mobile tab bar: the contextual items when there are any, else the
|
|
94
|
-
* global top-level items (unless `mobileNavItems`),
|
|
95
|
-
* - a back link to the current node's logical parent (unless
|
|
96
|
-
* `parentNavigation`).
|
|
97
|
-
*
|
|
98
|
-
* @stability Beta
|
|
99
|
-
*/
|
|
100
|
-
navigation?: NavigationNode<NavItem>;
|
|
101
|
-
/**
|
|
102
|
-
* Back-to-parent link rendered in the content header, above `children`, on
|
|
103
|
-
* mobile and desktop. Derived from `navigation` when omitted; `null` hides
|
|
104
|
-
* it. Use this instead of placing a back row inside page content.
|
|
105
|
-
*
|
|
106
|
-
* @stability Beta
|
|
107
|
-
*/
|
|
108
|
-
parentNavigation?: ParentNavigation | null;
|
|
109
|
-
/** Secondary nav (e.g. “More”) — rendered below a divider when shown */
|
|
110
|
-
secondaryNavItems?: NavItem[];
|
|
111
|
-
/** Label above secondary nav (default “More”) */
|
|
112
|
-
secondaryNavLabel?: string;
|
|
113
|
-
/**
|
|
114
|
-
* Items for the mobile bottom tab bar. Defaults to top-level `navItems`
|
|
115
|
-
* (no children). Pass a short list for apps with large sidebars; pass `[]`
|
|
116
|
-
* and set `options.showMobileBottomNav: false` to hide.
|
|
117
|
-
*/
|
|
118
|
-
mobileNavItems?: NavItem[];
|
|
119
|
-
/** Slot below the logo row (product switcher, workspace picker, …) */
|
|
120
|
-
headerSlot?: ReactNode;
|
|
121
|
-
/**
|
|
122
|
-
* Compact slot shown in the **collapsed** desktop rail, where `headerSlot`
|
|
123
|
-
* is hidden for space. Use it to keep a core affordance reachable without
|
|
124
|
-
* expanding — e.g. an icon-only product switcher. Omit to hide the header
|
|
125
|
-
* region entirely while collapsed.
|
|
126
|
-
*/
|
|
127
|
-
collapsedHeaderSlot?: ReactNode;
|
|
128
|
-
/** Footer region above the user slot (version, links) */
|
|
129
|
-
footer?: ReactNode;
|
|
130
|
-
/**
|
|
131
|
-
* User / account block at the bottom of the sidebar. When the rail is
|
|
132
|
-
* collapsed, AppShell hides any descendant carrying the
|
|
133
|
-
* `data-shell-user-detail` attribute (e.g. name/email text) so only a
|
|
134
|
-
* compact element (avatar, initial) remains — tag the detail wrapper you
|
|
135
|
-
* want hidden in the collapsed rail with that attribute.
|
|
136
|
-
*/
|
|
137
|
-
userSlot?: ReactNode;
|
|
138
|
-
/** Page content */
|
|
139
|
-
children: ReactNode;
|
|
140
|
-
/**
|
|
141
|
-
* @deprecated Prefer `options.showThemeToggle`. Kept for sample-app
|
|
142
|
-
* back-compat; merges into resolved options when `options` omits it.
|
|
143
|
-
*/
|
|
144
|
-
showThemeToggle?: boolean;
|
|
145
|
-
/** Chrome config (developer defaults + optional in-sidebar menu) */
|
|
146
|
-
options?: AppShellOptions;
|
|
147
|
-
/** Persist user sidebar-option overrides + collapse state (localStorage) */
|
|
148
|
-
optionsStorageKey?: string;
|
|
149
|
-
/** Controlled collapse (desktop rail) */
|
|
150
|
-
collapsed?: boolean;
|
|
151
|
-
onCollapsedChange?: (collapsed: boolean) => void;
|
|
152
|
-
/**
|
|
153
|
-
* Injectable link component. Defaults to a plain `<a>` (no router). Pass a
|
|
154
|
-
* router adapter — e.g. React Router `({ href, ...p }) => <Link to={href} {...p} />`
|
|
155
|
-
* or Next.js `next/link` — to get client-side navigation.
|
|
156
|
-
*/
|
|
157
|
-
linkComponent?: LinkComponent;
|
|
158
|
-
/**
|
|
159
|
-
* Current location used to highlight the active nav item. Pass the full
|
|
160
|
-
* location, `pathname + search + hash` (React Router: `useLocation()`), so
|
|
161
|
-
* items whose `to` names a query or hash (`/admin?tab=users`) can become
|
|
162
|
-
* active. A pathname alone still works for path-only items, `end` included.
|
|
163
|
-
* Omit to disable active highlighting.
|
|
164
|
-
*/
|
|
165
|
-
activePath?: string;
|
|
166
|
-
/**
|
|
167
|
-
* Optional custom active-state matcher. Defaults to a prefix match (with `/`
|
|
168
|
-
* treated as exact), mirroring React Router `NavLink`.
|
|
169
|
-
*/
|
|
170
|
-
isActive?: (to: string, activePath?: string) => boolean;
|
|
171
|
-
}
|
|
172
|
-
declare function AppShell({ appName, appLogo, logo, navItems: navItemsProp, navigation, parentNavigation: parentNavigationProp, secondaryNavItems, secondaryNavLabel, mobileNavItems, headerSlot, collapsedHeaderSlot, footer, userSlot, children, showThemeToggle: showThemeToggleProp, options: optionsProp, optionsStorageKey, collapsed: collapsedProp, onCollapsedChange, linkComponent, activePath, isActive, }: AppShellProps): React.JSX.Element;
|
|
173
|
-
|
|
174
|
-
export { AppShell as A, type NavItem as N, type ParentNavigation as P, type AppShellOptions as a, type AppShellProps as b };
|
package/dist/chunk-7S7LKQBD.js
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { jsx } from 'react/jsx-runtime';
|
|
2
|
-
|
|
3
|
-
// src/slots.tsx
|
|
4
|
-
function renderSlot(Default, override, props, extra) {
|
|
5
|
-
if (override === null) return null;
|
|
6
|
-
const Component = override ?? Default;
|
|
7
|
-
return /* @__PURE__ */ jsx(Component, { ...props, ...extra });
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export { renderSlot };
|
|
11
|
-
//# sourceMappingURL=chunk-7S7LKQBD.js.map
|
|
12
|
-
//# sourceMappingURL=chunk-7S7LKQBD.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/slots.tsx"],"names":[],"mappings":";;;AA6DO,SAAS,UAAA,CACd,OAAA,EACA,QAAA,EACA,KAAA,EACA,KAAA,EACW;AACX,EAAA,IAAI,QAAA,KAAa,MAAM,OAAO,IAAA;AAC9B,EAAA,MAAM,YAAY,QAAA,IAAY,OAAA;AAC9B,EAAA,uBAAO,GAAA,CAAC,SAAA,EAAA,EAAW,GAAG,KAAA,EAAQ,GAAI,KAAA,EAAa,CAAA;AACjD","file":"chunk-7S7LKQBD.js","sourcesContent":["import type { ComponentType, ReactNode } from \"react\";\n\n/**\n * The slot contract — genesis's escape hatch from an all-or-nothing pattern.\n *\n * A pattern component (`ResourcePage`, `DataPageShell`, …) is a composition of\n * interior parts. Without a way to reach those parts, a consumer who needs ONE\n * of them to differ has exactly two options: accept the whole thing as shipped,\n * or fork it. Both are bad, and the second is what actually happens — which is\n * how a downstream app ends up maintaining its own copy of a header it only\n * wanted to change one button in.\n *\n * Genesis offers a customisation ladder, and this is its missing rung:\n *\n * 1. the pattern `<ResourcePage title=\"Questions\" />`\n * 2. the pattern + slots ← here: keep the wiring, swap or drop one part\n * 3. the primitives `<DataPageShell><DataTable/></DataPageShell>`\n * 4. the hook `useResourcePage()`, render it all yourself\n *\n * Rungs 1, 3 and 4 already existed (75 subpath exports, 9 hooks). Rung 2 is\n * the one that keeps a consumer ON the ladder: dropping to rung 3 because a\n * single part was wrong means re-deriving all the wiring that was already\n * right, and that re-derivation is where the forks come from.\n *\n * Two knobs, following the `slots`/`slotProps` convention Material UI settled\n * on (mui/material-ui#33416) so the shape is one a React developer already\n * knows rather than one genesis invented:\n *\n * `slots` — REPLACE a part. Your component receives exactly the props\n * genesis would have passed the default, so it can wrap the\n * default and change one thing rather than reimplement it.\n * `slotProps` — KEEP the part, pass it more props. The common case: a\n * `className`, a `data-testid`, an `aria-label`.\n *\n * And the eject button: `slots={{ bulkBar: null }}` drops that part entirely.\n * A pattern is a default, not a mandate — a consumer who wants the shell and\n * the grid but none of the bulk bar should not have to leave to get that.\n */\n\n/** Replace a part (`ComponentType`), or eject it (`null`). */\nexport type SlotOverride<P> = ComponentType<P> | null;\n\n/** `{ [part]: Component | null }` — see {@link renderSlot}. */\nexport type Slots<M extends Record<string, object>> = {\n [K in keyof M]?: SlotOverride<M[K]>;\n};\n\n/** `{ [part]: extraProps }` — merged over what genesis passes. */\nexport type SlotPropsFor<M extends Record<string, object>> = {\n [K in keyof M]?: Partial<M[K]>;\n};\n\n/**\n * Renders one slot: the override if given, the default otherwise, nothing if\n * ejected with `null`.\n *\n * `extra` merges LAST, so `slotProps` wins over genesis's own value. That is\n * deliberate: a consumer reaching for `slotProps` is overriding on purpose,\n * and a hatch that silently loses to the default is not a hatch. `className`\n * is the one prop a caller should merge with `cn()` instead of replacing.\n */\nexport function renderSlot<P extends object>(\n Default: ComponentType<P>,\n override: SlotOverride<P> | undefined,\n props: P,\n extra?: Partial<P>,\n): ReactNode {\n if (override === null) return null;\n const Component = override ?? Default;\n return <Component {...props} {...(extra as P)} />;\n}\n"]}
|