@evinvest/uikit 0.24.0 → 0.26.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.
- package/README.md +147 -4
- package/dist/components/alert-dialog.js +1 -1
- package/dist/components/alert-dialog.js.map +1 -1
- package/dist/components/app-shell.js +85 -0
- package/dist/components/app-shell.js.map +1 -0
- package/dist/components/bottom-tab-bar.js +91 -0
- package/dist/components/bottom-tab-bar.js.map +1 -0
- package/dist/components/dialog.js +2 -1
- package/dist/components/dialog.js.map +1 -1
- package/dist/components/drawer.js +6 -4
- package/dist/components/drawer.js.map +1 -1
- package/dist/components/list-rows.js +73 -0
- package/dist/components/list-rows.js.map +1 -0
- package/dist/components/mobile-app-bar.js +51 -0
- package/dist/components/mobile-app-bar.js.map +1 -0
- package/dist/components/nav-badge.js +61 -0
- package/dist/components/nav-badge.js.map +1 -0
- package/dist/components/nav-link.js +50 -0
- package/dist/components/nav-link.js.map +1 -0
- package/dist/components/nav-model.js +55 -0
- package/dist/components/nav-model.js.map +1 -0
- package/dist/components/page-frame.js +92 -0
- package/dist/components/page-frame.js.map +1 -0
- package/dist/components/resource-error.js +47 -0
- package/dist/components/resource-error.js.map +1 -0
- package/dist/components/section-nav.js +86 -0
- package/dist/components/section-nav.js.map +1 -0
- package/dist/components/settled.js +19 -0
- package/dist/components/settled.js.map +1 -0
- package/dist/components/sheet.js +8 -2
- package/dist/components/sheet.js.map +1 -1
- package/dist/components/shell-icons.js +57 -0
- package/dist/components/shell-icons.js.map +1 -0
- package/dist/components/shell-nav.js +141 -0
- package/dist/components/shell-nav.js.map +1 -0
- package/dist/components/system-banner.js +53 -0
- package/dist/components/system-banner.js.map +1 -0
- package/dist/components/use-shell-nav.js +46 -0
- package/dist/components/use-shell-nav.js.map +1 -0
- package/dist/generated/alert-dialog.js +2 -2
- package/dist/generated/alert-dialog.js.map +1 -1
- package/dist/generated/dialog.js +3 -3
- package/dist/generated/dialog.js.map +1 -1
- package/dist/generated/drawer.js +1 -1
- package/dist/generated/drawer.js.map +1 -1
- package/dist/generated/sheet.js +3 -3
- package/dist/generated/sheet.js.map +1 -1
- package/dist/index.d.ts +451 -6
- package/dist/index.js +12 -0
- package/dist/primitives/focus-scope.js +22 -6
- package/dist/primitives/focus-scope.js.map +1 -1
- package/package.json +1 -1
- package/styles/theme.css +180 -1
- package/styles/tokens.css +182 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/resource-error.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { Alert, AlertDescription, AlertTitle } from \"./alert\";\nimport { Button } from \"./button\";\nimport { RefreshIcon, TriangleAlertIcon } from \"./shell-icons\";\n\nexport interface ResourceErrorLabels {\n /** The retry button. Default `\"Try again\"`. */\n retry?: string;\n}\n\ninterface ResourceErrorBase extends Omit<React.ComponentProps<\"div\">, \"title\" | \"children\"> {\n /** What failed, already in the reader's words — the kit formats no errors. */\n message: React.ReactNode;\n labels?: ResourceErrorLabels;\n}\n\n// The two forms take different props and the union says so: an `alert` has no\n// retry and an `inline` line has no heading, so asking for either is a type\n// error rather than a prop that silently does nothing.\nexport type ResourceErrorProps = ResourceErrorBase &\n (\n | {\n /** One line under the page header — for dense screens where a block would push everything down. */\n variant?: \"inline\";\n title?: never;\n /** Re-run the read; boxes the line so the button and the message read as one thing. */\n onRetry?: () => void;\n /** The retry is in flight: the button is disabled and its icon spins. */\n retrying?: boolean;\n }\n | {\n /** The kit's `Alert` with a heading — for screens that address the reader. */\n variant: \"alert\";\n title?: React.ReactNode;\n onRetry?: never;\n retrying?: never;\n }\n );\n\n/**\n * What a screen shows when a read it needed never arrived. Whether there is\n * anything to report stays with the caller — only it knows whether stale data is\n * still on screen, and a failed refresh over figures the reader can see belongs\n * beside them, not in place of them.\n */\nexport function ResourceError(props: ResourceErrorProps) {\n if (props.variant === \"alert\") {\n const { message, title, labels: _labels, variant: _variant, className, ...rest } = props;\n return (\n <div data-slot=\"resource-error\" data-variant=\"alert\" className={className} {...rest}>\n <Alert variant=\"destructive\">\n <TriangleAlertIcon className=\"size-4\" />\n {title !== undefined && <AlertTitle>{title}</AlertTitle>}\n <AlertDescription>{message}</AlertDescription>\n </Alert>\n </div>\n );\n }\n const { message, onRetry, retrying = false, labels = {}, variant: _variant, title: _title, className, ...rest } = props;\n const line = (\n <p className=\"flex min-w-0 items-center gap-2 text-sm text-accent-error\">\n {/* Holds its size: beside the button it is the first thing squeezed, and a\n half-width triangle reads as a rendering fault. */}\n <TriangleAlertIcon className=\"size-4 shrink-0\" /> {message}\n </p>\n );\n return (\n <div\n data-slot=\"resource-error\"\n data-variant=\"inline\"\n role=\"alert\"\n className={cn(\n onRetry &&\n \"flex flex-wrap items-center gap-3 rounded-lg border border-accent-error/40 bg-accent-error/10 px-3 py-2.5\",\n className,\n )}\n {...rest}\n >\n {line}\n {onRetry && (\n <Button type=\"button\" variant=\"outline\" size=\"sm\" disabled={retrying} onClick={onRetry}>\n <RefreshIcon className={cn(\"size-4\", retrying && \"motion-safe:animate-spin\")} />\n {labels.retry ?? \"Try again\"}\n </Button>\n )}\n </div>\n );\n}\n"],"mappings":";AAqDQ,SACE,KADF;AAnDR,YAAY,WAAW;AACvB,SAAS,UAAU;AACnB,SAAS,OAAO,kBAAkB,kBAAkB;AACpD,SAAS,cAAc;AACvB,SAAS,aAAa,yBAAyB;AA0CxC,SAAS,cAAc,OAA2B;AACvD,MAAI,MAAM,YAAY,SAAS;AAC7B,UAAM,EAAE,SAAAA,UAAS,OAAO,QAAQ,SAAS,SAASC,WAAU,WAAAC,YAAW,GAAGC,MAAK,IAAI;AACnF,WACE,oBAAC,SAAI,aAAU,kBAAiB,gBAAa,SAAQ,WAAWD,YAAY,GAAGC,OAC7E,+BAAC,SAAM,SAAQ,eACb;AAAA,0BAAC,qBAAkB,WAAU,UAAS;AAAA,MACrC,UAAU,UAAa,oBAAC,cAAY,iBAAM;AAAA,MAC3C,oBAAC,oBAAkB,UAAAH,UAAQ;AAAA,OAC7B,GACF;AAAA,EAEJ;AACA,QAAM,EAAE,SAAS,SAAS,WAAW,OAAO,SAAS,CAAC,GAAG,SAAS,UAAU,OAAO,QAAQ,WAAW,GAAG,KAAK,IAAI;AAClH,QAAM,OACJ,qBAAC,OAAE,WAAU,6DAGX;AAAA,wBAAC,qBAAkB,WAAU,mBAAkB;AAAA,IAAE;AAAA,IAAE;AAAA,KACrD;AAEF,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,gBAAa;AAAA,MACb,MAAK;AAAA,MACL,WAAW;AAAA,QACT,WACE;AAAA,QACF;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEH;AAAA;AAAA,QACA,WACC,qBAAC,UAAO,MAAK,UAAS,SAAQ,WAAU,MAAK,MAAK,UAAU,UAAU,SAAS,SAC7E;AAAA,8BAAC,eAAY,WAAW,GAAG,UAAU,YAAY,0BAA0B,GAAG;AAAA,UAC7E,OAAO,SAAS;AAAA,WACnB;AAAA;AAAA;AAAA,EAEJ;AAEJ;","names":["message","_variant","className","rest"]}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { cn } from "../lib/cn.js";
|
|
5
|
+
import { SectionLabel } from "./page-frame.js";
|
|
6
|
+
const ITEM = "flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-left text-sm outline-none transition-colors focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50";
|
|
7
|
+
function SectionNav({
|
|
8
|
+
groups,
|
|
9
|
+
value,
|
|
10
|
+
onValueChange,
|
|
11
|
+
linkComponent,
|
|
12
|
+
labels = {},
|
|
13
|
+
className,
|
|
14
|
+
...props
|
|
15
|
+
}) {
|
|
16
|
+
return /* @__PURE__ */ jsx(
|
|
17
|
+
"nav",
|
|
18
|
+
{
|
|
19
|
+
"data-slot": "section-nav",
|
|
20
|
+
"aria-label": labels.nav ?? "Sections",
|
|
21
|
+
className: cn("flex w-60 shrink-0 flex-col gap-5", className),
|
|
22
|
+
...props,
|
|
23
|
+
children: groups.map((group) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
24
|
+
(group.label !== void 0 || group.description !== void 0) && /* @__PURE__ */ jsxs("div", { className: "mb-1 flex flex-col gap-0.5 px-3", children: [
|
|
25
|
+
group.label !== void 0 && /* @__PURE__ */ jsx(SectionLabel, { children: group.label }),
|
|
26
|
+
group.description !== void 0 && /* @__PURE__ */ jsx("p", { className: "text-xs leading-snug text-ink-soft", children: group.description })
|
|
27
|
+
] }),
|
|
28
|
+
group.items.map((item) => /* @__PURE__ */ jsx(
|
|
29
|
+
SectionNavRow,
|
|
30
|
+
{
|
|
31
|
+
item,
|
|
32
|
+
active: item.id === value,
|
|
33
|
+
onSelect: onValueChange,
|
|
34
|
+
linkComponent
|
|
35
|
+
},
|
|
36
|
+
item.id
|
|
37
|
+
))
|
|
38
|
+
] }, group.id))
|
|
39
|
+
}
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
function SectionNavRow({
|
|
43
|
+
item,
|
|
44
|
+
active,
|
|
45
|
+
onSelect,
|
|
46
|
+
linkComponent
|
|
47
|
+
}) {
|
|
48
|
+
const Icon = item.icon;
|
|
49
|
+
const className = cn(
|
|
50
|
+
ITEM,
|
|
51
|
+
active ? "bg-primary-ink/15 font-semibold text-primary-ink" : "text-ink hover:bg-hover"
|
|
52
|
+
);
|
|
53
|
+
const content = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
54
|
+
Icon && /* @__PURE__ */ jsx(Icon, { className: "size-4.5 shrink-0" }),
|
|
55
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate", children: item.label }),
|
|
56
|
+
item.trailing
|
|
57
|
+
] });
|
|
58
|
+
if (item.href !== void 0 && !item.disabled) {
|
|
59
|
+
const Link = linkComponent ?? "a";
|
|
60
|
+
return /* @__PURE__ */ jsx(
|
|
61
|
+
Link,
|
|
62
|
+
{
|
|
63
|
+
href: item.href,
|
|
64
|
+
"aria-current": active ? "page" : void 0,
|
|
65
|
+
className,
|
|
66
|
+
onClick: () => onSelect?.(item.id),
|
|
67
|
+
children: content
|
|
68
|
+
}
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
return /* @__PURE__ */ jsx(
|
|
72
|
+
"button",
|
|
73
|
+
{
|
|
74
|
+
type: "button",
|
|
75
|
+
disabled: item.disabled,
|
|
76
|
+
"aria-current": active ? "true" : void 0,
|
|
77
|
+
className,
|
|
78
|
+
onClick: () => onSelect?.(item.id),
|
|
79
|
+
children: content
|
|
80
|
+
}
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
export {
|
|
84
|
+
SectionNav
|
|
85
|
+
};
|
|
86
|
+
//# sourceMappingURL=section-nav.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/section-nav.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { SectionLabel } from \"./page-frame\";\n\nexport interface SectionNavItem {\n id: string;\n label: React.ReactNode;\n icon?: React.ComponentType<{ className?: string }>;\n /** A link (`?section=…`, a sub-route) instead of a button calling `onValueChange`. */\n href?: string;\n /** After the label — a count, a status chip. */\n trailing?: React.ReactNode;\n disabled?: boolean;\n}\n\nexport interface SectionNavGroup {\n id: string;\n label?: React.ReactNode;\n /** One line under the label saying what kind of thing lives here. */\n description?: React.ReactNode;\n items: readonly SectionNavItem[];\n}\n\nexport interface SectionNavLabels {\n /** Accessible name of the nav. Default `\"Sections\"`. */\n nav?: string;\n}\n\nexport interface SectionNavProps extends Omit<React.ComponentProps<\"nav\">, \"children\"> {\n groups: readonly SectionNavGroup[];\n /** The current section's id. */\n value: string;\n /** Called by button items (those without `href`), and by link items on click. */\n onValueChange?: (id: string) => void;\n linkComponent?: React.ElementType;\n labels?: SectionNavLabels;\n}\n\nconst ITEM =\n \"flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-left text-sm outline-none transition-colors focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50\";\n\n/**\n * A screen's own section rail — settings panes, a document's chapters: grouped\n * rows, one current (`aria-current`), each a button or a link. The shell's rail\n * is `ShellNav`; this one lives inside a page, so it carries a tint rather than\n * the rail's solid marker and does not slide.\n */\nexport function SectionNav({\n groups,\n value,\n onValueChange,\n linkComponent,\n labels = {},\n className,\n ...props\n}: SectionNavProps) {\n return (\n <nav\n data-slot=\"section-nav\"\n aria-label={labels.nav ?? \"Sections\"}\n className={cn(\"flex w-60 shrink-0 flex-col gap-5\", className)}\n {...props}\n >\n {groups.map((group) => (\n <div key={group.id} className=\"flex flex-col gap-1\">\n {(group.label !== undefined || group.description !== undefined) && (\n <div className=\"mb-1 flex flex-col gap-0.5 px-3\">\n {group.label !== undefined && <SectionLabel>{group.label}</SectionLabel>}\n {group.description !== undefined && (\n <p className=\"text-xs leading-snug text-ink-soft\">{group.description}</p>\n )}\n </div>\n )}\n {group.items.map((item) => (\n <SectionNavRow\n key={item.id}\n item={item}\n active={item.id === value}\n onSelect={onValueChange}\n linkComponent={linkComponent}\n />\n ))}\n </div>\n ))}\n </nav>\n );\n}\n\nfunction SectionNavRow({\n item,\n active,\n onSelect,\n linkComponent,\n}: {\n item: SectionNavItem;\n active: boolean;\n onSelect: ((id: string) => void) | undefined;\n linkComponent: React.ElementType | undefined;\n}) {\n const Icon = item.icon;\n const className = cn(\n ITEM,\n active ? \"bg-primary-ink/15 font-semibold text-primary-ink\" : \"text-ink hover:bg-hover\",\n );\n const content = (\n <>\n {Icon && <Icon className=\"size-4.5 shrink-0\" />}\n <span className=\"min-w-0 flex-1 truncate\">{item.label}</span>\n {item.trailing}\n </>\n );\n if (item.href !== undefined && !item.disabled) {\n const Link = linkComponent ?? \"a\";\n return (\n <Link\n href={item.href}\n aria-current={active ? \"page\" : undefined}\n className={className}\n onClick={() => onSelect?.(item.id)}\n >\n {content}\n </Link>\n );\n }\n return (\n <button\n type=\"button\"\n disabled={item.disabled}\n aria-current={active ? \"true\" : undefined}\n className={className}\n onClick={() => onSelect?.(item.id)}\n >\n {content}\n </button>\n );\n}\n"],"mappings":";AAoEY,SAuCR,UAtCwC,KADhC;AAlEZ,YAAY,WAAW;AACvB,SAAS,UAAU;AACnB,SAAS,oBAAoB;AAoC7B,MAAM,OACJ;AAQK,SAAS,WAAW;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,SAAS,CAAC;AAAA,EACV;AAAA,EACA,GAAG;AACL,GAAoB;AAClB,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,cAAY,OAAO,OAAO;AAAA,MAC1B,WAAW,GAAG,qCAAqC,SAAS;AAAA,MAC3D,GAAG;AAAA,MAEH,iBAAO,IAAI,CAAC,UACX,qBAAC,SAAmB,WAAU,uBAC1B;AAAA,eAAM,UAAU,UAAa,MAAM,gBAAgB,WACnD,qBAAC,SAAI,WAAU,mCACZ;AAAA,gBAAM,UAAU,UAAa,oBAAC,gBAAc,gBAAM,OAAM;AAAA,UACxD,MAAM,gBAAgB,UACrB,oBAAC,OAAE,WAAU,sCAAsC,gBAAM,aAAY;AAAA,WAEzE;AAAA,QAED,MAAM,MAAM,IAAI,CAAC,SAChB;AAAA,UAAC;AAAA;AAAA,YAEC;AAAA,YACA,QAAQ,KAAK,OAAO;AAAA,YACpB,UAAU;AAAA,YACV;AAAA;AAAA,UAJK,KAAK;AAAA,QAKZ,CACD;AAAA,WAjBO,MAAM,EAkBhB,CACD;AAAA;AAAA,EACH;AAEJ;AAEA,SAAS,cAAc;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKG;AACD,QAAM,OAAO,KAAK;AAClB,QAAM,YAAY;AAAA,IAChB;AAAA,IACA,SAAS,qDAAqD;AAAA,EAChE;AACA,QAAM,UACJ,iCACG;AAAA,YAAQ,oBAAC,QAAK,WAAU,qBAAoB;AAAA,IAC7C,oBAAC,UAAK,WAAU,2BAA2B,eAAK,OAAM;AAAA,IACrD,KAAK;AAAA,KACR;AAEF,MAAI,KAAK,SAAS,UAAa,CAAC,KAAK,UAAU;AAC7C,UAAM,OAAO,iBAAiB;AAC9B,WACE;AAAA,MAAC;AAAA;AAAA,QACC,MAAM,KAAK;AAAA,QACX,gBAAc,SAAS,SAAS;AAAA,QAChC;AAAA,QACA,SAAS,MAAM,WAAW,KAAK,EAAE;AAAA,QAEhC;AAAA;AAAA,IACH;AAAA,EAEJ;AACA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,UAAU,KAAK;AAAA,MACf,gBAAc,SAAS,SAAS;AAAA,MAChC;AAAA,MACA,SAAS,MAAM,WAAW,KAAK,EAAE;AAAA,MAEhC;AAAA;AAAA,EACH;AAEJ;","names":[]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
function Settled({ loading, skeleton, children, ...props }) {
|
|
5
|
+
const [wasLoading, setWasLoading] = React.useState(loading);
|
|
6
|
+
const [reveals, setReveals] = React.useState(0);
|
|
7
|
+
if (wasLoading !== loading) {
|
|
8
|
+
setWasLoading(loading);
|
|
9
|
+
if (wasLoading) setReveals((n) => n + 1);
|
|
10
|
+
}
|
|
11
|
+
if (loading) {
|
|
12
|
+
return /* @__PURE__ */ jsx("div", { "data-slot": "settled", "data-state": "loading", "aria-busy": "true", ...props, children: skeleton });
|
|
13
|
+
}
|
|
14
|
+
return /* @__PURE__ */ jsx("div", { "data-slot": "settled", "data-state": reveals > 0 ? "revealed" : "ready", ...props, children }, reveals);
|
|
15
|
+
}
|
|
16
|
+
export {
|
|
17
|
+
Settled
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=settled.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/settled.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\n\nexport interface SettledProps extends Omit<React.ComponentProps<\"div\">, \"children\"> {\n /** True while the data this surface needs is in flight. */\n loading: boolean;\n /** What stands in meanwhile — a `Skeleton` or a group of them. */\n skeleton: React.ReactNode;\n /** The content; also the empty and error states, guarded on the same condition. */\n children: React.ReactNode;\n}\n\n/**\n * The skeleton → content handover. Swapped in a single frame, a skeleton reads\n * as a flicker — the eye sees that something changed, not what. Here the\n * content fades and rises the last `--ev-rise` into place\n * (`data-state=\"revealed\"`, motion.css).\n *\n * Deliberately not a cross-fade: the two would have to overlap, which means\n * taking one out of flow and animating a height, and the surface would jump\n * when the content is a different size. And no entrance when no skeleton was\n * shown — data already present on the first render cuts straight in; fading it\n * would invent a delay the data never had.\n *\n * `data-state`: `loading` → `revealed` after a skeleton, `ready` without one.\n */\nexport function Settled({ loading, skeleton, children, ...props }: SettledProps) {\n // Adjusted during render, not in an effect, so the entrance plays on the very\n // first render after `loading` drops: an effect would paint the content opaque\n // once and only then start the fade. `reveals` keys the wrapper, so a later\n // refetch that shows the skeleton again gets a handover of its own.\n const [wasLoading, setWasLoading] = React.useState(loading);\n const [reveals, setReveals] = React.useState(0);\n if (wasLoading !== loading) {\n setWasLoading(loading);\n if (wasLoading) setReveals((n) => n + 1);\n }\n if (loading) {\n return (\n <div data-slot=\"settled\" data-state=\"loading\" aria-busy=\"true\" {...props}>\n {skeleton}\n </div>\n );\n }\n return (\n <div key={reveals} data-slot=\"settled\" data-state={reveals > 0 ? \"revealed\" : \"ready\"} {...props}>\n {children}\n </div>\n );\n}\n"],"mappings":";AAwCM;AAtCN,YAAY,WAAW;AAyBhB,SAAS,QAAQ,EAAE,SAAS,UAAU,UAAU,GAAG,MAAM,GAAiB;AAK/E,QAAM,CAAC,YAAY,aAAa,IAAI,MAAM,SAAS,OAAO;AAC1D,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAS,CAAC;AAC9C,MAAI,eAAe,SAAS;AAC1B,kBAAc,OAAO;AACrB,QAAI,WAAY,YAAW,CAAC,MAAM,IAAI,CAAC;AAAA,EACzC;AACA,MAAI,SAAS;AACX,WACE,oBAAC,SAAI,aAAU,WAAU,cAAW,WAAU,aAAU,QAAQ,GAAG,OAChE,oBACH;AAAA,EAEJ;AACA,SACE,oBAAC,SAAkB,aAAU,WAAU,cAAY,UAAU,IAAI,aAAa,SAAU,GAAG,OACxF,YADO,OAEV;AAEJ;","names":[]}
|
package/dist/components/sheet.js
CHANGED
|
@@ -89,10 +89,16 @@ const XIcon = (props) => /* @__PURE__ */ jsx(
|
|
|
89
89
|
children: /* @__PURE__ */ jsx("path", { d: "M18 6 6 18M6 6l12 12" })
|
|
90
90
|
}
|
|
91
91
|
);
|
|
92
|
-
function SheetContent({
|
|
92
|
+
function SheetContent({
|
|
93
|
+
className,
|
|
94
|
+
children,
|
|
95
|
+
side = "right",
|
|
96
|
+
initialFocus = "container",
|
|
97
|
+
...props
|
|
98
|
+
}) {
|
|
93
99
|
const { open, setOpen, triggerRef } = useSheet();
|
|
94
100
|
const { isPresent, ref: presRef } = usePresence(open);
|
|
95
|
-
const focusRef = useFocusScope(open);
|
|
101
|
+
const focusRef = useFocusScope(open, { initialFocus, returnFocusTo: triggerRef });
|
|
96
102
|
const dismissRef = useDismissableLayer({
|
|
97
103
|
enabled: open,
|
|
98
104
|
onDismiss: () => setOpen(false),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/sheet.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { useControllableState } from \"../primitives/use-controllable-state\";\nimport { useDismissableLayer } from \"../primitives/dismissable-layer\";\nimport { useFocusScope } from \"../primitives/focus-scope\";\nimport { usePresence } from \"../primitives/presence\";\nimport { mergeRefs } from \"../primitives/merge-refs\";\nimport { Portal } from \"../primitives/portal\";\nimport { Slot } from \"../primitives/slot\";\nimport {\n SHEET_CLOSE,\n SHEET_CONTENT,\n SHEET_DESCRIPTION,\n SHEET_FOOTER,\n SHEET_HEADER,\n SHEET_OVERLAY,\n SHEET_SIDE_BOTTOM,\n SHEET_SIDE_LEFT,\n SHEET_SIDE_RIGHT,\n SHEET_SIDE_TOP,\n SHEET_TITLE,\n} from \"../generated/sheet\";\n\ninterface SheetContextValue {\n open: boolean;\n setOpen: (next: boolean) => void;\n triggerRef: React.RefObject<HTMLElement | null>;\n}\n\nconst SheetContext = React.createContext<SheetContextValue | null>(null);\n\nfunction useSheet(): SheetContextValue {\n const ctx = React.useContext(SheetContext);\n if (!ctx) throw new Error(\"Sheet parts must be used within <Sheet>\");\n return ctx;\n}\n\nexport interface SheetProps {\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n children?: React.ReactNode;\n}\n\nexport function Sheet({ open, defaultOpen = false, onOpenChange, children }: SheetProps) {\n const [isOpen, setOpen] = useControllableState<boolean>({\n ...(open !== undefined ? { value: open } : {}),\n defaultValue: defaultOpen,\n ...(onOpenChange ? { onChange: onOpenChange } : {}),\n });\n const triggerRef = React.useRef<HTMLElement | null>(null);\n return (\n <SheetContext.Provider value={{ open: isOpen, setOpen, triggerRef }}>\n {children}\n </SheetContext.Provider>\n );\n}\n\nexport interface SheetTriggerProps extends React.ComponentProps<\"button\"> {\n asChild?: boolean;\n}\n\nexport function SheetTrigger({ asChild = false, onClick, ...props }: SheetTriggerProps) {\n const { open, setOpen, triggerRef } = useSheet();\n const Comp = asChild ? Slot : \"button\";\n return (\n <Comp\n data-slot=\"sheet-trigger\"\n aria-expanded={open}\n ref={triggerRef as React.Ref<HTMLButtonElement>}\n onClick={(e: React.MouseEvent<HTMLButtonElement>) => {\n onClick?.(e);\n setOpen(true);\n }}\n {...(props as Record<string, unknown>)}\n />\n );\n}\n\nexport interface SheetOverlayProps extends React.ComponentProps<\"div\"> {}\n\nexport function SheetOverlay({ className, ...props }: SheetOverlayProps) {\n const { open, setOpen } = useSheet();\n return (\n <div\n data-slot=\"sheet-overlay\"\n data-state={open ? \"open\" : \"closed\"}\n onClick={() => setOpen(false)}\n className={cn(SHEET_OVERLAY, className)}\n {...(props as Record<string, unknown>)}\n />\n );\n}\n\nconst sheetSideClasses = {\n right: SHEET_SIDE_RIGHT,\n left: SHEET_SIDE_LEFT,\n top: SHEET_SIDE_TOP,\n bottom: SHEET_SIDE_BOTTOM,\n} as const;\n\nconst XIcon = (props: React.SVGProps<SVGSVGElement>) => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n {...props}\n >\n <path d=\"M18 6 6 18M6 6l12 12\" />\n </svg>\n);\n\nexport interface SheetContentProps extends React.ComponentProps<\"div\"> {\n side?: \"top\" | \"right\" | \"bottom\" | \"left\";\n}\n\nexport function SheetContent({
|
|
1
|
+
{"version":3,"sources":["../../src/components/sheet.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { useControllableState } from \"../primitives/use-controllable-state\";\nimport { useDismissableLayer } from \"../primitives/dismissable-layer\";\nimport { type InitialFocus, useFocusScope } from \"../primitives/focus-scope\";\nimport { usePresence } from \"../primitives/presence\";\nimport { mergeRefs } from \"../primitives/merge-refs\";\nimport { Portal } from \"../primitives/portal\";\nimport { Slot } from \"../primitives/slot\";\nimport {\n SHEET_CLOSE,\n SHEET_CONTENT,\n SHEET_DESCRIPTION,\n SHEET_FOOTER,\n SHEET_HEADER,\n SHEET_OVERLAY,\n SHEET_SIDE_BOTTOM,\n SHEET_SIDE_LEFT,\n SHEET_SIDE_RIGHT,\n SHEET_SIDE_TOP,\n SHEET_TITLE,\n} from \"../generated/sheet\";\n\ninterface SheetContextValue {\n open: boolean;\n setOpen: (next: boolean) => void;\n triggerRef: React.RefObject<HTMLElement | null>;\n}\n\nconst SheetContext = React.createContext<SheetContextValue | null>(null);\n\nfunction useSheet(): SheetContextValue {\n const ctx = React.useContext(SheetContext);\n if (!ctx) throw new Error(\"Sheet parts must be used within <Sheet>\");\n return ctx;\n}\n\nexport interface SheetProps {\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n children?: React.ReactNode;\n}\n\nexport function Sheet({ open, defaultOpen = false, onOpenChange, children }: SheetProps) {\n const [isOpen, setOpen] = useControllableState<boolean>({\n ...(open !== undefined ? { value: open } : {}),\n defaultValue: defaultOpen,\n ...(onOpenChange ? { onChange: onOpenChange } : {}),\n });\n const triggerRef = React.useRef<HTMLElement | null>(null);\n return (\n <SheetContext.Provider value={{ open: isOpen, setOpen, triggerRef }}>\n {children}\n </SheetContext.Provider>\n );\n}\n\nexport interface SheetTriggerProps extends React.ComponentProps<\"button\"> {\n asChild?: boolean;\n}\n\nexport function SheetTrigger({ asChild = false, onClick, ...props }: SheetTriggerProps) {\n const { open, setOpen, triggerRef } = useSheet();\n const Comp = asChild ? Slot : \"button\";\n return (\n <Comp\n data-slot=\"sheet-trigger\"\n aria-expanded={open}\n ref={triggerRef as React.Ref<HTMLButtonElement>}\n onClick={(e: React.MouseEvent<HTMLButtonElement>) => {\n onClick?.(e);\n setOpen(true);\n }}\n {...(props as Record<string, unknown>)}\n />\n );\n}\n\nexport interface SheetOverlayProps extends React.ComponentProps<\"div\"> {}\n\nexport function SheetOverlay({ className, ...props }: SheetOverlayProps) {\n const { open, setOpen } = useSheet();\n return (\n <div\n data-slot=\"sheet-overlay\"\n data-state={open ? \"open\" : \"closed\"}\n onClick={() => setOpen(false)}\n className={cn(SHEET_OVERLAY, className)}\n {...(props as Record<string, unknown>)}\n />\n );\n}\n\nconst sheetSideClasses = {\n right: SHEET_SIDE_RIGHT,\n left: SHEET_SIDE_LEFT,\n top: SHEET_SIDE_TOP,\n bottom: SHEET_SIDE_BOTTOM,\n} as const;\n\nconst XIcon = (props: React.SVGProps<SVGSVGElement>) => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n {...props}\n >\n <path d=\"M18 6 6 18M6 6l12 12\" />\n </svg>\n);\n\nexport interface SheetContentProps extends React.ComponentProps<\"div\"> {\n side?: \"top\" | \"right\" | \"bottom\" | \"left\";\n /**\n * Where focus lands on open. Default `container`: a sheet is a panel of\n * content, and its first Tab stop is as likely an action as a field — the\n * panel itself takes focus and the first Tab reaches the first control.\n */\n initialFocus?: InitialFocus;\n}\n\nexport function SheetContent({\n className,\n children,\n side = \"right\",\n initialFocus = \"container\",\n ...props\n}: SheetContentProps) {\n const { open, setOpen, triggerRef } = useSheet();\n const { isPresent, ref: presRef } = usePresence(open);\n const focusRef = useFocusScope(open, { initialFocus, returnFocusTo: triggerRef });\n const dismissRef = useDismissableLayer({\n enabled: open,\n onDismiss: () => setOpen(false),\n exclude: [triggerRef],\n });\n if (!isPresent) return null;\n return (\n <Portal>\n <SheetOverlay />\n <div\n data-slot=\"sheet-content\"\n role=\"dialog\"\n aria-modal=\"true\"\n data-state={open ? \"open\" : \"closed\"}\n ref={mergeRefs(focusRef, dismissRef, presRef as React.Ref<HTMLDivElement>)}\n className={cn(SHEET_CONTENT, sheetSideClasses[side], className)}\n {...(props as Record<string, unknown>)}\n >\n {children}\n <button\n type=\"button\"\n data-slot=\"sheet-close\"\n onClick={() => setOpen(false)}\n className={SHEET_CLOSE}\n >\n <XIcon className=\"size-4\" />\n <span className=\"sr-only\">Close</span>\n </button>\n </div>\n </Portal>\n );\n}\n\nexport interface SheetCloseProps extends React.ComponentProps<\"button\"> {\n asChild?: boolean;\n}\n\nexport function SheetClose({ asChild = false, onClick, ...props }: SheetCloseProps) {\n const { setOpen } = useSheet();\n const Comp = asChild ? Slot : \"button\";\n return (\n <Comp\n data-slot=\"sheet-close\"\n onClick={(e: React.MouseEvent<HTMLButtonElement>) => {\n onClick?.(e);\n setOpen(false);\n }}\n {...(props as Record<string, unknown>)}\n />\n );\n}\n\nexport function SheetHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"sheet-header\"\n className={cn(SHEET_HEADER, className)}\n {...props}\n />\n );\n}\n\nexport function SheetFooter({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"sheet-footer\"\n className={cn(SHEET_FOOTER, className)}\n {...props}\n />\n );\n}\n\nexport function SheetTitle({ className, ...props }: React.ComponentProps<\"h2\">) {\n return (\n <h2\n data-slot=\"sheet-title\"\n className={cn(SHEET_TITLE, className)}\n {...props}\n />\n );\n}\n\nexport function SheetDescription({ className, ...props }: React.ComponentProps<\"p\">) {\n return (\n <p\n data-slot=\"sheet-description\"\n className={cn(SHEET_DESCRIPTION, className)}\n {...props}\n />\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
function Glyph({ className, children }) {
|
|
4
|
+
return /* @__PURE__ */ jsx(
|
|
5
|
+
"svg",
|
|
6
|
+
{
|
|
7
|
+
className,
|
|
8
|
+
viewBox: "0 0 24 24",
|
|
9
|
+
fill: "none",
|
|
10
|
+
stroke: "currentColor",
|
|
11
|
+
strokeWidth: 2,
|
|
12
|
+
strokeLinecap: "round",
|
|
13
|
+
strokeLinejoin: "round",
|
|
14
|
+
"aria-hidden": true,
|
|
15
|
+
children
|
|
16
|
+
}
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
function ChevronLeftIcon({ className }) {
|
|
20
|
+
return /* @__PURE__ */ jsx(Glyph, { className, children: /* @__PURE__ */ jsx("path", { d: "m15 18-6-6 6-6" }) });
|
|
21
|
+
}
|
|
22
|
+
function TriangleAlertIcon({ className }) {
|
|
23
|
+
return /* @__PURE__ */ jsxs(Glyph, { className, children: [
|
|
24
|
+
/* @__PURE__ */ jsx("path", { d: "m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3" }),
|
|
25
|
+
/* @__PURE__ */ jsx("path", { d: "M12 9v4" }),
|
|
26
|
+
/* @__PURE__ */ jsx("path", { d: "M12 17h.01" })
|
|
27
|
+
] });
|
|
28
|
+
}
|
|
29
|
+
function InfoIcon({ className }) {
|
|
30
|
+
return /* @__PURE__ */ jsxs(Glyph, { className, children: [
|
|
31
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }),
|
|
32
|
+
/* @__PURE__ */ jsx("path", { d: "M12 16v-4" }),
|
|
33
|
+
/* @__PURE__ */ jsx("path", { d: "M12 8h.01" })
|
|
34
|
+
] });
|
|
35
|
+
}
|
|
36
|
+
function RefreshIcon({ className }) {
|
|
37
|
+
return /* @__PURE__ */ jsxs(Glyph, { className, children: [
|
|
38
|
+
/* @__PURE__ */ jsx("path", { d: "M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8" }),
|
|
39
|
+
/* @__PURE__ */ jsx("path", { d: "M21 3v5h-5" }),
|
|
40
|
+
/* @__PURE__ */ jsx("path", { d: "M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16" }),
|
|
41
|
+
/* @__PURE__ */ jsx("path", { d: "M8 16H3v5" })
|
|
42
|
+
] });
|
|
43
|
+
}
|
|
44
|
+
function CloseIcon({ className }) {
|
|
45
|
+
return /* @__PURE__ */ jsxs(Glyph, { className, children: [
|
|
46
|
+
/* @__PURE__ */ jsx("path", { d: "M18 6 6 18" }),
|
|
47
|
+
/* @__PURE__ */ jsx("path", { d: "m6 6 12 12" })
|
|
48
|
+
] });
|
|
49
|
+
}
|
|
50
|
+
export {
|
|
51
|
+
ChevronLeftIcon,
|
|
52
|
+
CloseIcon,
|
|
53
|
+
InfoIcon,
|
|
54
|
+
RefreshIcon,
|
|
55
|
+
TriangleAlertIcon
|
|
56
|
+
};
|
|
57
|
+
//# sourceMappingURL=shell-icons.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/shell-icons.tsx"],"sourcesContent":["import * as React from \"react\";\n\n// lucide glyphs the shell needs, inlined so the kit keeps its zero-icon-dep footprint.\nfunction Glyph({ className, children }: { className?: string | undefined; children: React.ReactNode }) {\n return (\n <svg\n className={className}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden\n >\n {children}\n </svg>\n );\n}\n\nexport function ChevronLeftIcon({ className }: { className?: string | undefined }) {\n return (\n <Glyph className={className}>\n <path d=\"m15 18-6-6 6-6\" />\n </Glyph>\n );\n}\n\nexport function TriangleAlertIcon({ className }: { className?: string | undefined }) {\n return (\n <Glyph className={className}>\n <path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\" />\n <path d=\"M12 9v4\" />\n <path d=\"M12 17h.01\" />\n </Glyph>\n );\n}\n\nexport function InfoIcon({ className }: { className?: string | undefined }) {\n return (\n <Glyph className={className}>\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"M12 16v-4\" />\n <path d=\"M12 8h.01\" />\n </Glyph>\n );\n}\n\nexport function RefreshIcon({ className }: { className?: string | undefined }) {\n return (\n <Glyph className={className}>\n <path d=\"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8\" />\n <path d=\"M21 3v5h-5\" />\n <path d=\"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16\" />\n <path d=\"M8 16H3v5\" />\n </Glyph>\n );\n}\n\nexport function CloseIcon({ className }: { className?: string | undefined }) {\n return (\n <Glyph className={className}>\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </Glyph>\n );\n}\n"],"mappings":"AAKI,cAyBA,YAzBA;AALJ,YAAY,WAAW;AAGvB,SAAS,MAAM,EAAE,WAAW,SAAS,GAAkE;AACrG,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,SAAQ;AAAA,MACR,MAAK;AAAA,MACL,QAAO;AAAA,MACP,aAAa;AAAA,MACb,eAAc;AAAA,MACd,gBAAe;AAAA,MACf,eAAW;AAAA,MAEV;AAAA;AAAA,EACH;AAEJ;AAEO,SAAS,gBAAgB,EAAE,UAAU,GAAuC;AACjF,SACE,oBAAC,SAAM,WACL,8BAAC,UAAK,GAAE,kBAAiB,GAC3B;AAEJ;AAEO,SAAS,kBAAkB,EAAE,UAAU,GAAuC;AACnF,SACE,qBAAC,SAAM,WACL;AAAA,wBAAC,UAAK,GAAE,4EAA2E;AAAA,IACnF,oBAAC,UAAK,GAAE,WAAU;AAAA,IAClB,oBAAC,UAAK,GAAE,cAAa;AAAA,KACvB;AAEJ;AAEO,SAAS,SAAS,EAAE,UAAU,GAAuC;AAC1E,SACE,qBAAC,SAAM,WACL;AAAA,wBAAC,YAAO,IAAG,MAAK,IAAG,MAAK,GAAE,MAAK;AAAA,IAC/B,oBAAC,UAAK,GAAE,aAAY;AAAA,IACpB,oBAAC,UAAK,GAAE,aAAY;AAAA,KACtB;AAEJ;AAEO,SAAS,YAAY,EAAE,UAAU,GAAuC;AAC7E,SACE,qBAAC,SAAM,WACL;AAAA,wBAAC,UAAK,GAAE,sDAAqD;AAAA,IAC7D,oBAAC,UAAK,GAAE,cAAa;AAAA,IACrB,oBAAC,UAAK,GAAE,uDAAsD;AAAA,IAC9D,oBAAC,UAAK,GAAE,aAAY;AAAA,KACtB;AAEJ;AAEO,SAAS,UAAU,EAAE,UAAU,GAAuC;AAC3E,SACE,qBAAC,SAAM,WACL;AAAA,wBAAC,UAAK,GAAE,cAAa;AAAA,IACrB,oBAAC,UAAK,GAAE,cAAa;AAAA,KACvB;AAEJ;","names":[]}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { cn } from "../lib/cn.js";
|
|
5
|
+
import { NavBadge } from "./nav-badge.js";
|
|
6
|
+
import { labelTitle, NavLink } from "./nav-link.js";
|
|
7
|
+
import { resolveActiveNavItem } from "./nav-model.js";
|
|
8
|
+
import { usePendingNavigation, useSlideMarker } from "./use-shell-nav.js";
|
|
9
|
+
const NAV_FOCUS = "outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-secondary";
|
|
10
|
+
const ROW = "flex items-center gap-3 rounded-lg px-3 py-2 text-sm transition-colors";
|
|
11
|
+
function ShellNav({
|
|
12
|
+
groups,
|
|
13
|
+
footerGroups = [],
|
|
14
|
+
pathname,
|
|
15
|
+
linkComponent,
|
|
16
|
+
header,
|
|
17
|
+
footer,
|
|
18
|
+
renderItem,
|
|
19
|
+
onItemIntent,
|
|
20
|
+
onNavigate,
|
|
21
|
+
labels = {},
|
|
22
|
+
className,
|
|
23
|
+
...props
|
|
24
|
+
}) {
|
|
25
|
+
const [pendingId, markPending] = usePendingNavigation(pathname);
|
|
26
|
+
const all = [...groups, ...footerGroups].flatMap((g) => g.items);
|
|
27
|
+
const currentId = (pendingId !== null && all.some((i) => i.id === pendingId) ? pendingId : void 0) ?? resolveActiveNavItem(all, pathname)?.id;
|
|
28
|
+
const section = (group) => /* @__PURE__ */ jsx(
|
|
29
|
+
ShellNavSection,
|
|
30
|
+
{
|
|
31
|
+
group,
|
|
32
|
+
currentId,
|
|
33
|
+
pendingId,
|
|
34
|
+
row: { linkComponent, markPending, onItemIntent, onNavigate, renderItem, badgeLabel: labels.badge }
|
|
35
|
+
},
|
|
36
|
+
group.id
|
|
37
|
+
);
|
|
38
|
+
return /* @__PURE__ */ jsxs(
|
|
39
|
+
"aside",
|
|
40
|
+
{
|
|
41
|
+
"data-slot": "shell-nav",
|
|
42
|
+
className: cn(
|
|
43
|
+
"flex h-full w-(--shell-rail-w) flex-col gap-7 overflow-y-auto border-r border-border bg-secondary px-4.5 pb-5 pt-6",
|
|
44
|
+
className
|
|
45
|
+
),
|
|
46
|
+
...props,
|
|
47
|
+
children: [
|
|
48
|
+
header,
|
|
49
|
+
/* @__PURE__ */ jsx("nav", { "aria-label": labels.primary ?? "Main", className: "flex flex-col gap-4.5", children: groups.map(section) }),
|
|
50
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1" }),
|
|
51
|
+
footerGroups.length > 0 && /* @__PURE__ */ jsx("nav", { "aria-label": labels.footer ?? "Account", className: "flex flex-col gap-4.5", children: footerGroups.map(section) }),
|
|
52
|
+
footer
|
|
53
|
+
]
|
|
54
|
+
}
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
function ShellNavSection({
|
|
58
|
+
group,
|
|
59
|
+
currentId,
|
|
60
|
+
pendingId,
|
|
61
|
+
row
|
|
62
|
+
}) {
|
|
63
|
+
const root = React.useRef(null);
|
|
64
|
+
const labelId = React.useId();
|
|
65
|
+
const at = group.items.some((i) => i.id === currentId) ? currentId ?? null : null;
|
|
66
|
+
useSlideMarker(root, at);
|
|
67
|
+
return /* @__PURE__ */ jsxs(
|
|
68
|
+
"div",
|
|
69
|
+
{
|
|
70
|
+
ref: root,
|
|
71
|
+
"data-slot": "shell-nav-section",
|
|
72
|
+
className: cn("relative isolate flex flex-col gap-1", group.label !== void 0 && "pl-1"),
|
|
73
|
+
children: [
|
|
74
|
+
at !== null && /* @__PURE__ */ jsx(
|
|
75
|
+
"span",
|
|
76
|
+
{
|
|
77
|
+
"data-slot": "shell-nav-marker",
|
|
78
|
+
"aria-hidden": true,
|
|
79
|
+
className: "pointer-events-none absolute left-0 top-0 -z-10 rounded-lg bg-primary"
|
|
80
|
+
}
|
|
81
|
+
),
|
|
82
|
+
group.label !== void 0 && /* @__PURE__ */ jsx("p", { id: labelId, className: "mb-0.5 text-xs font-semibold uppercase tracking-widest text-ink-soft", children: group.label }),
|
|
83
|
+
/* @__PURE__ */ jsx(
|
|
84
|
+
"ul",
|
|
85
|
+
{
|
|
86
|
+
"aria-labelledby": group.label !== void 0 ? labelId : void 0,
|
|
87
|
+
className: "flex flex-col gap-1",
|
|
88
|
+
children: group.items.map((item) => /* @__PURE__ */ jsx("li", { className: "flex flex-col", children: /* @__PURE__ */ jsx(
|
|
89
|
+
ShellNavRow,
|
|
90
|
+
{
|
|
91
|
+
item,
|
|
92
|
+
state: { active: item.id === currentId, pending: item.id === pendingId },
|
|
93
|
+
row
|
|
94
|
+
}
|
|
95
|
+
) }, item.id))
|
|
96
|
+
}
|
|
97
|
+
)
|
|
98
|
+
]
|
|
99
|
+
}
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
function ShellNavRow({ item, state, row }) {
|
|
103
|
+
const Icon = item.icon;
|
|
104
|
+
const trailing = typeof item.trailing === "function" ? item.trailing(state) : item.trailing;
|
|
105
|
+
return /* @__PURE__ */ jsx(
|
|
106
|
+
NavLink,
|
|
107
|
+
{
|
|
108
|
+
item,
|
|
109
|
+
current: state.active,
|
|
110
|
+
pending: state.pending,
|
|
111
|
+
linkComponent: row.linkComponent,
|
|
112
|
+
markPending: row.markPending,
|
|
113
|
+
onItemIntent: row.onItemIntent,
|
|
114
|
+
onNavigate: row.onNavigate,
|
|
115
|
+
title: labelTitle(item.label),
|
|
116
|
+
className: cn(
|
|
117
|
+
ROW,
|
|
118
|
+
NAV_FOCUS,
|
|
119
|
+
state.active ? "font-semibold text-on-primary" : "font-medium text-ink hover:bg-hover",
|
|
120
|
+
item.disabled && "cursor-not-allowed opacity-50 hover:bg-transparent"
|
|
121
|
+
),
|
|
122
|
+
children: row.renderItem ? row.renderItem(item, state) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
123
|
+
Icon && /* @__PURE__ */ jsx(Icon, { className: "size-4.5 shrink-0" }),
|
|
124
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate", children: item.label }),
|
|
125
|
+
typeof item.badge === "number" ? /* @__PURE__ */ jsx(
|
|
126
|
+
NavBadge,
|
|
127
|
+
{
|
|
128
|
+
count: item.badge,
|
|
129
|
+
active: state.active,
|
|
130
|
+
...row.badgeLabel ? { label: row.badgeLabel } : {}
|
|
131
|
+
}
|
|
132
|
+
) : item.badge,
|
|
133
|
+
trailing
|
|
134
|
+
] })
|
|
135
|
+
}
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
export {
|
|
139
|
+
ShellNav
|
|
140
|
+
};
|
|
141
|
+
//# sourceMappingURL=shell-nav.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/shell-nav.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { NavBadge } from \"./nav-badge\";\nimport { labelTitle, NavLink, type NavLinkCallbacks } from \"./nav-link\";\nimport { resolveActiveNavItem, type NavGroup, type NavItem, type NavItemState } from \"./nav-model\";\nimport { usePendingNavigation, useSlideMarker } from \"./use-shell-nav\";\n\nexport interface ShellNavLabels {\n /** Accessible name of the nav holding `groups`. Default `\"Main\"`. */\n primary?: string;\n /** Accessible name of the nav holding `footerGroups`. Default `\"Account\"`. */\n footer?: string;\n /** Spoken text of a numeric badge, given the real count. Default `\"{n} new\"`. */\n badge?: (count: number) => string;\n}\n\nexport interface ShellNavProps\n extends Omit<React.ComponentProps<\"aside\">, \"children\">,\n NavLinkCallbacks {\n groups: readonly NavGroup[];\n /** Pinned to the bottom of the rail (profile, notifications, settings). */\n footerGroups?: readonly NavGroup[];\n /** The router's current path — `usePathname()` in Next. */\n pathname: string;\n /** Your router's link (`next/link`); defaults to a plain `<a>`. */\n linkComponent?: React.ElementType;\n /** Above the groups — the brand lock-up. */\n header?: React.ReactNode;\n /** Below the footer groups — the signed-in account, a sign-out. */\n footer?: React.ReactNode;\n /** Replaces a row's content (icon, label, badge); the link, `aria-current` and the marker stay the kit's. */\n renderItem?: (item: NavItem, state: NavItemState) => React.ReactNode;\n labels?: ShellNavLabels;\n}\n\n// The ring is the solid token, never a tint — at 50% it composites under the 3:1\n// non-text floor. The offset keeps it legible around the marked row, whose fill\n// is that same hue.\nconst NAV_FOCUS =\n \"outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-secondary\";\nconst ROW = \"flex items-center gap-3 rounded-lg px-3 py-2 text-sm transition-colors\";\n\n/**\n * The app's left rail: data-driven groups, a footer block pinned to the bottom,\n * one sliding marker per group, and an optimistic mark on click so the row\n * answers the click rather than the router's round-trip.\n *\n * Exactly one row across the rail is current — the strongest claim on the path\n * (see {@link resolveActiveNavItem}) — so overlapping `prefix` items are safe.\n * Visibility (roles, flags) is the caller's: pass only the items to show.\n */\nexport function ShellNav({\n groups,\n footerGroups = [],\n pathname,\n linkComponent,\n header,\n footer,\n renderItem,\n onItemIntent,\n onNavigate,\n labels = {},\n className,\n ...props\n}: ShellNavProps) {\n const [pendingId, markPending] = usePendingNavigation(pathname);\n const all = [...groups, ...footerGroups].flatMap((g) => g.items);\n const currentId =\n (pendingId !== null && all.some((i) => i.id === pendingId) ? pendingId : undefined) ??\n resolveActiveNavItem(all, pathname)?.id;\n const section = (group: NavGroup) => (\n <ShellNavSection\n key={group.id}\n group={group}\n currentId={currentId}\n pendingId={pendingId}\n row={{ linkComponent, markPending, onItemIntent, onNavigate, renderItem, badgeLabel: labels.badge }}\n />\n );\n return (\n <aside\n data-slot=\"shell-nav\"\n className={cn(\n \"flex h-full w-(--shell-rail-w) flex-col gap-7 overflow-y-auto border-r border-border bg-secondary px-4.5 pb-5 pt-6\",\n className,\n )}\n {...props}\n >\n {header}\n <nav aria-label={labels.primary ?? \"Main\"} className=\"flex flex-col gap-4.5\">\n {groups.map(section)}\n </nav>\n <div className=\"flex-1\" />\n {footerGroups.length > 0 && (\n <nav aria-label={labels.footer ?? \"Account\"} className=\"flex flex-col gap-4.5\">\n {footerGroups.map(section)}\n </nav>\n )}\n {footer}\n </aside>\n );\n}\n\ninterface RowContext extends NavLinkCallbacks {\n linkComponent: React.ElementType | undefined;\n markPending: (id: string) => void;\n renderItem: ShellNavProps[\"renderItem\"];\n badgeLabel: ((count: number) => string) | undefined;\n}\n\n// One marker node per SECTION, mounted once and moved — not one per rail: a\n// marker travelling between groups would cross headings that have nothing to do\n// with either row. Crossing groups unmounts one marker and mounts another (a\n// fade). `isolate` is load-bearing: the marker sits on a negative z-index and\n// without its own stacking context would land behind the rail's background.\nfunction ShellNavSection({\n group,\n currentId,\n pendingId,\n row,\n}: {\n group: NavGroup;\n currentId: string | undefined;\n pendingId: string | null;\n row: RowContext;\n}) {\n const root = React.useRef<HTMLDivElement>(null);\n const labelId = React.useId();\n const at = group.items.some((i) => i.id === currentId) ? (currentId ?? null) : null;\n useSlideMarker(root, at);\n return (\n <div\n ref={root}\n data-slot=\"shell-nav-section\"\n className={cn(\"relative isolate flex flex-col gap-1\", group.label !== undefined && \"pl-1\")}\n >\n {/* First in DOM order: the pre-hydration fill in motion.css reaches the marked\n row through a sibling combinator, which only looks forward. */}\n {at !== null && (\n <span\n data-slot=\"shell-nav-marker\"\n aria-hidden\n className=\"pointer-events-none absolute left-0 top-0 -z-10 rounded-lg bg-primary\"\n />\n )}\n {group.label !== undefined && (\n <p id={labelId} className=\"mb-0.5 text-xs font-semibold uppercase tracking-widest text-ink-soft\">\n {group.label}\n </p>\n )}\n <ul\n aria-labelledby={group.label !== undefined ? labelId : undefined}\n className=\"flex flex-col gap-1\"\n >\n {group.items.map((item) => (\n <li key={item.id} className=\"flex flex-col\">\n <ShellNavRow\n item={item}\n state={{ active: item.id === currentId, pending: item.id === pendingId }}\n row={row}\n />\n </li>\n ))}\n </ul>\n </div>\n );\n}\n\nfunction ShellNavRow({ item, state, row }: { item: NavItem; state: NavItemState; row: RowContext }) {\n const Icon = item.icon;\n const trailing = typeof item.trailing === \"function\" ? item.trailing(state) : item.trailing;\n return (\n <NavLink\n item={item}\n current={state.active}\n pending={state.pending}\n linkComponent={row.linkComponent}\n markPending={row.markPending}\n onItemIntent={row.onItemIntent}\n onNavigate={row.onNavigate}\n title={labelTitle(item.label)}\n className={cn(\n ROW,\n NAV_FOCUS,\n state.active ? \"font-semibold text-on-primary\" : \"font-medium text-ink hover:bg-hover\",\n item.disabled && \"cursor-not-allowed opacity-50 hover:bg-transparent\",\n )}\n >\n {row.renderItem ? (\n row.renderItem(item, state)\n ) : (\n <>\n {Icon && <Icon className=\"size-4.5 shrink-0\" />}\n {/* `min-w-0`: without it the label refuses to shrink below its own text and\n the rail widens to the longest translation instead of truncating. */}\n <span className=\"min-w-0 flex-1 truncate\">{item.label}</span>\n {typeof item.badge === \"number\" ? (\n <NavBadge\n count={item.badge}\n active={state.active}\n {...(row.badgeLabel ? { label: row.badgeLabel } : {})}\n />\n ) : (\n item.badge\n )}\n {trailing}\n </>\n )}\n </NavLink>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { cn } from "../lib/cn.js";
|
|
5
|
+
import { CloseIcon, InfoIcon, TriangleAlertIcon } from "./shell-icons.js";
|
|
6
|
+
const TONE = {
|
|
7
|
+
warn: "border-accent-warn/40 bg-accent-warn/10 text-accent-warn",
|
|
8
|
+
info: "border-border bg-card text-ink"
|
|
9
|
+
};
|
|
10
|
+
function SystemBanner({
|
|
11
|
+
tone = "info",
|
|
12
|
+
title,
|
|
13
|
+
icon,
|
|
14
|
+
onDismiss,
|
|
15
|
+
labels = {},
|
|
16
|
+
className,
|
|
17
|
+
children,
|
|
18
|
+
...props
|
|
19
|
+
}) {
|
|
20
|
+
const glyph = icon !== void 0 ? icon : tone === "warn" ? /* @__PURE__ */ jsx(TriangleAlertIcon, { className: "size-4" }) : /* @__PURE__ */ jsx(InfoIcon, { className: "size-4" });
|
|
21
|
+
return /* @__PURE__ */ jsxs(
|
|
22
|
+
"div",
|
|
23
|
+
{
|
|
24
|
+
"data-slot": "system-banner",
|
|
25
|
+
"data-tone": tone,
|
|
26
|
+
role: "status",
|
|
27
|
+
className: cn("flex items-start gap-2.5 rounded-lg border px-4 py-2.5 text-sm", TONE[tone], className),
|
|
28
|
+
...props,
|
|
29
|
+
children: [
|
|
30
|
+
glyph !== null && /* @__PURE__ */ jsx("span", { className: "mt-0.5 flex shrink-0", children: glyph }),
|
|
31
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
32
|
+
title !== void 0 && /* @__PURE__ */ jsx("span", { className: "font-semibold", children: title }),
|
|
33
|
+
title !== void 0 && children !== void 0 && " \u2014 ",
|
|
34
|
+
children !== void 0 && /* @__PURE__ */ jsx("span", { className: cn(title !== void 0 && tone === "info" && "text-ink-soft"), children })
|
|
35
|
+
] }),
|
|
36
|
+
onDismiss && /* @__PURE__ */ jsx(
|
|
37
|
+
"button",
|
|
38
|
+
{
|
|
39
|
+
type: "button",
|
|
40
|
+
"aria-label": labels.dismiss ?? "Dismiss",
|
|
41
|
+
onClick: onDismiss,
|
|
42
|
+
className: "-mr-1 shrink-0 rounded-md p-0.5 opacity-70 outline-none transition-opacity hover:opacity-100 focus-visible:ring-2 focus-visible:ring-ring",
|
|
43
|
+
children: /* @__PURE__ */ jsx(CloseIcon, { className: "size-4" })
|
|
44
|
+
}
|
|
45
|
+
)
|
|
46
|
+
]
|
|
47
|
+
}
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
export {
|
|
51
|
+
SystemBanner
|
|
52
|
+
};
|
|
53
|
+
//# sourceMappingURL=system-banner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/system-banner.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { CloseIcon, InfoIcon, TriangleAlertIcon } from \"./shell-icons\";\n\nexport type SystemBannerTone = \"warn\" | \"info\";\n\nexport interface SystemBannerLabels {\n /** The close button. Default `\"Dismiss\"`. */\n dismiss?: string;\n}\n\nexport interface SystemBannerProps extends Omit<React.ComponentProps<\"div\">, \"title\"> {\n /** `warn` — a platform state (maintenance, read-only); `info` — an announcement. */\n tone?: SystemBannerTone;\n /** Bold lead before the message. */\n title?: React.ReactNode;\n /** Replaces the tone's glyph; `null` drops it. */\n icon?: React.ReactNode;\n /** Shows a close button; remembering the dismissal is the caller's. */\n onDismiss?: () => void;\n labels?: SystemBannerLabels;\n}\n\nconst TONE: Record<SystemBannerTone, string> = {\n warn: \"border-accent-warn/40 bg-accent-warn/10 text-accent-warn\",\n info: \"border-border bg-card text-ink\",\n};\n\n/**\n * A slim full-width strip above the page — presentation only: where its state\n * comes from, and whether a dismissal sticks, are the caller's. Put it in\n * `AppShell`'s `banner` slot; several stack with a gap.\n */\nexport function SystemBanner({\n tone = \"info\",\n title,\n icon,\n onDismiss,\n labels = {},\n className,\n children,\n ...props\n}: SystemBannerProps) {\n const glyph =\n icon !== undefined ? icon : tone === \"warn\" ? <TriangleAlertIcon className=\"size-4\" /> : <InfoIcon className=\"size-4\" />;\n return (\n <div\n data-slot=\"system-banner\"\n data-tone={tone}\n role=\"status\"\n className={cn(\"flex items-start gap-2.5 rounded-lg border px-4 py-2.5 text-sm\", TONE[tone], className)}\n {...props}\n >\n {glyph !== null && <span className=\"mt-0.5 flex shrink-0\">{glyph}</span>}\n <div className=\"min-w-0 flex-1\">\n {title !== undefined && <span className=\"font-semibold\">{title}</span>}\n {title !== undefined && children !== undefined && \" — \"}\n {children !== undefined && (\n <span className={cn(title !== undefined && tone === \"info\" && \"text-ink-soft\")}>{children}</span>\n )}\n </div>\n {onDismiss && (\n <button\n type=\"button\"\n aria-label={labels.dismiss ?? \"Dismiss\"}\n onClick={onDismiss}\n className=\"-mr-1 shrink-0 rounded-md p-0.5 opacity-70 outline-none transition-opacity hover:opacity-100 focus-visible:ring-2 focus-visible:ring-ring\"\n >\n <CloseIcon className=\"size-4\" />\n </button>\n )}\n </div>\n );\n}\n"],"mappings":";AA8CkD,cAU5C,YAV4C;AA5ClD,YAAY,WAAW;AACvB,SAAS,UAAU;AACnB,SAAS,WAAW,UAAU,yBAAyB;AAqBvD,MAAM,OAAyC;AAAA,EAC7C,MAAM;AAAA,EACN,MAAM;AACR;AAOO,SAAS,aAAa;AAAA,EAC3B,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EACA;AAAA,EACA,SAAS,CAAC;AAAA,EACV;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAsB;AACpB,QAAM,QACJ,SAAS,SAAY,OAAO,SAAS,SAAS,oBAAC,qBAAkB,WAAU,UAAS,IAAK,oBAAC,YAAS,WAAU,UAAS;AACxH,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,aAAW;AAAA,MACX,MAAK;AAAA,MACL,WAAW,GAAG,kEAAkE,KAAK,IAAI,GAAG,SAAS;AAAA,MACpG,GAAG;AAAA,MAEH;AAAA,kBAAU,QAAQ,oBAAC,UAAK,WAAU,wBAAwB,iBAAM;AAAA,QACjE,qBAAC,SAAI,WAAU,kBACZ;AAAA,oBAAU,UAAa,oBAAC,UAAK,WAAU,iBAAiB,iBAAM;AAAA,UAC9D,UAAU,UAAa,aAAa,UAAa;AAAA,UACjD,aAAa,UACZ,oBAAC,UAAK,WAAW,GAAG,UAAU,UAAa,SAAS,UAAU,eAAe,GAAI,UAAS;AAAA,WAE9F;AAAA,QACC,aACC;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,cAAY,OAAO,WAAW;AAAA,YAC9B,SAAS;AAAA,YACT,WAAU;AAAA,YAEV,8BAAC,aAAU,WAAU,UAAS;AAAA;AAAA,QAChC;AAAA;AAAA;AAAA,EAEJ;AAEJ;","names":[]}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
function usePendingNavigation(pathname) {
|
|
4
|
+
const [pending, setPending] = React.useState(null);
|
|
5
|
+
if (pending && pending.from !== pathname) setPending(null);
|
|
6
|
+
const pendingId = pending && pending.from === pathname ? pending.id : null;
|
|
7
|
+
const markPending = React.useCallback(
|
|
8
|
+
(id) => setPending({ id, from: pathname }),
|
|
9
|
+
[pathname]
|
|
10
|
+
);
|
|
11
|
+
return [pendingId, markPending];
|
|
12
|
+
}
|
|
13
|
+
function useSlideMarker(ref, at) {
|
|
14
|
+
const placed = React.useRef(void 0);
|
|
15
|
+
React.useLayoutEffect(() => {
|
|
16
|
+
const section = ref.current;
|
|
17
|
+
if (!section) return;
|
|
18
|
+
const place = () => {
|
|
19
|
+
const marker = section.querySelector('[data-slot="shell-nav-marker"]');
|
|
20
|
+
const row = section.querySelector('[aria-current="page"]');
|
|
21
|
+
const fresh = marker !== placed.current;
|
|
22
|
+
const entering = fresh && placed.current !== void 0;
|
|
23
|
+
placed.current = marker;
|
|
24
|
+
if (!marker || !row) return;
|
|
25
|
+
if (fresh) marker.style.transition = "none";
|
|
26
|
+
marker.style.transform = `translate(${row.offsetLeft}px, ${row.offsetTop}px)`;
|
|
27
|
+
marker.style.width = `${row.offsetWidth}px`;
|
|
28
|
+
marker.style.height = `${row.offsetHeight}px`;
|
|
29
|
+
if (fresh) {
|
|
30
|
+
void marker.offsetWidth;
|
|
31
|
+
marker.style.transition = "";
|
|
32
|
+
marker.dataset["placed"] = entering ? "entered" : "first";
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
place();
|
|
36
|
+
if (typeof ResizeObserver === "undefined") return;
|
|
37
|
+
const observer = new ResizeObserver(place);
|
|
38
|
+
observer.observe(section);
|
|
39
|
+
return () => observer.disconnect();
|
|
40
|
+
}, [ref, at]);
|
|
41
|
+
}
|
|
42
|
+
export {
|
|
43
|
+
usePendingNavigation,
|
|
44
|
+
useSlideMarker
|
|
45
|
+
};
|
|
46
|
+
//# sourceMappingURL=use-shell-nav.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/use-shell-nav.ts"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\n\n/**\n * The optimistic half of a nav: the id of the item just clicked, until the\n * pathname catches up.\n *\n * A router that waits for the next page's payload changes the pathname 150–300ms\n * after the click — exactly the window in which a highlight that has not moved\n * reads as a click that did not land. So the clicked item is marked at once.\n *\n * The entry remembers the pathname it was made on, so the moment the pathname\n * changes it is stale by construction and the URL is the truth again. It is\n * cleared during render, not in an effect (an effect would paint one frame with\n * the URL's row marked and the stale one not), and cleared rather than ignored:\n * left behind, it would come back to life on a return to the page it was made\n * on. A click that never navigates is forgotten by the next one.\n */\nexport function usePendingNavigation(\n pathname: string,\n): readonly [pendingId: string | null, markPending: (id: string) => void] {\n const [pending, setPending] = React.useState<{ id: string; from: string } | null>(null);\n if (pending && pending.from !== pathname) setPending(null);\n const pendingId = pending && pending.from === pathname ? pending.id : null;\n const markPending = React.useCallback(\n (id: string) => setPending({ id, from: pathname }),\n [pathname],\n );\n return [pendingId, markPending] as const;\n}\n\n/**\n * Places the section's `[data-slot=\"shell-nav-marker\"]` over its\n * `[aria-current=\"page\"]` row, by measurement: rows differ in count and every\n * label is somebody's translation, and SSR cannot know those offsets (until the\n * first placement the row carries the fill itself — see motion.css). Re-runs\n * when `at` changes and whenever the section resizes — rows come and go, labels\n * change width with the locale or a font swap.\n */\nexport function useSlideMarker(\n ref: React.RefObject<HTMLElement | null>,\n at: string | null,\n): void {\n // Which marker node was last placed, so a re-run over the same node — a move,\n // a resize — is told apart from a marker that has just mounted. `undefined`\n // until the first run, so only a marker mounting AFTER the section settled\n // counts as entering (and fades); one hydration finds on its row cuts in.\n const placed = React.useRef<HTMLElement | null | undefined>(undefined);\n React.useLayoutEffect(() => {\n const section = ref.current;\n if (!section) return;\n const place = () => {\n const marker = section.querySelector<HTMLElement>('[data-slot=\"shell-nav-marker\"]');\n const row = section.querySelector<HTMLElement>('[aria-current=\"page\"]');\n const fresh = marker !== placed.current;\n const entering = fresh && placed.current !== undefined;\n placed.current = marker;\n if (!marker || !row) return;\n if (fresh) marker.style.transition = \"none\";\n marker.style.transform = `translate(${row.offsetLeft}px, ${row.offsetTop}px)`;\n marker.style.width = `${row.offsetWidth}px`;\n marker.style.height = `${row.offsetHeight}px`;\n if (fresh) {\n // A transition is decided by the style AFTER the change: flushing the\n // placement first makes it the starting point, so a new marker appears on\n // its row instead of sliding in from the section's origin.\n void marker.offsetWidth;\n marker.style.transition = \"\";\n marker.dataset[\"placed\"] = entering ? \"entered\" : \"first\";\n }\n };\n place();\n if (typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver(place);\n observer.observe(section);\n return () => observer.disconnect();\n }, [ref, at]);\n}\n"],"mappings":";AAEA,YAAY,WAAW;AAiBhB,SAAS,qBACd,UACwE;AACxE,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAA8C,IAAI;AACtF,MAAI,WAAW,QAAQ,SAAS,SAAU,YAAW,IAAI;AACzD,QAAM,YAAY,WAAW,QAAQ,SAAS,WAAW,QAAQ,KAAK;AACtE,QAAM,cAAc,MAAM;AAAA,IACxB,CAAC,OAAe,WAAW,EAAE,IAAI,MAAM,SAAS,CAAC;AAAA,IACjD,CAAC,QAAQ;AAAA,EACX;AACA,SAAO,CAAC,WAAW,WAAW;AAChC;AAUO,SAAS,eACd,KACA,IACM;AAKN,QAAM,SAAS,MAAM,OAAuC,MAAS;AACrE,QAAM,gBAAgB,MAAM;AAC1B,UAAM,UAAU,IAAI;AACpB,QAAI,CAAC,QAAS;AACd,UAAM,QAAQ,MAAM;AAClB,YAAM,SAAS,QAAQ,cAA2B,gCAAgC;AAClF,YAAM,MAAM,QAAQ,cAA2B,uBAAuB;AACtE,YAAM,QAAQ,WAAW,OAAO;AAChC,YAAM,WAAW,SAAS,OAAO,YAAY;AAC7C,aAAO,UAAU;AACjB,UAAI,CAAC,UAAU,CAAC,IAAK;AACrB,UAAI,MAAO,QAAO,MAAM,aAAa;AACrC,aAAO,MAAM,YAAY,aAAa,IAAI,UAAU,OAAO,IAAI,SAAS;AACxE,aAAO,MAAM,QAAQ,GAAG,IAAI,WAAW;AACvC,aAAO,MAAM,SAAS,GAAG,IAAI,YAAY;AACzC,UAAI,OAAO;AAIT,aAAK,OAAO;AACZ,eAAO,MAAM,aAAa;AAC1B,eAAO,QAAQ,QAAQ,IAAI,WAAW,YAAY;AAAA,MACpD;AAAA,IACF;AACA,UAAM;AACN,QAAI,OAAO,mBAAmB,YAAa;AAC3C,UAAM,WAAW,IAAI,eAAe,KAAK;AACzC,aAAS,QAAQ,OAAO;AACxB,WAAO,MAAM,SAAS,WAAW;AAAA,EACnC,GAAG,CAAC,KAAK,EAAE,CAAC;AACd;","names":[]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
const ALERT_DIALOG_OVERLAY = "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50";
|
|
2
|
-
const ALERT_DIALOG_CONTENT = "bg-background 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 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border border-border p-6 shadow-lg duration-200 sm:max-w-lg";
|
|
1
|
+
const ALERT_DIALOG_OVERLAY = "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 duration-200 data-[state=closed]:fill-mode-forwards data-[state=closed]:pointer-events-none fixed inset-0 z-50 bg-black/50";
|
|
2
|
+
const ALERT_DIALOG_CONTENT = "bg-background 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 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border border-border p-6 shadow-lg duration-200 data-[state=closed]:fill-mode-forwards outline-hidden sm:max-w-lg";
|
|
3
3
|
const ALERT_DIALOG_HEADER = "flex flex-col gap-2 text-center sm:text-left";
|
|
4
4
|
const ALERT_DIALOG_FOOTER = "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end";
|
|
5
5
|
const ALERT_DIALOG_TITLE = "text-lg font-semibold";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/generated/alert-dialog.ts"],"sourcesContent":["// AUTO-GENERATED by `nix run .#gen` — do not edit.\n\nexport const ALERT_DIALOG_OVERLAY = \"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50\";\n\nexport const ALERT_DIALOG_CONTENT = \"bg-background 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 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border border-border p-6 shadow-lg duration-200 sm:max-w-lg\";\n\nexport const ALERT_DIALOG_HEADER = \"flex flex-col gap-2 text-center sm:text-left\";\n\nexport const ALERT_DIALOG_FOOTER = \"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end\";\n\nexport const ALERT_DIALOG_TITLE = \"text-lg font-semibold\";\n\nexport const ALERT_DIALOG_DESCRIPTION = \"text-ink-soft text-sm\";\n"],"mappings":"AAEO,MAAM,uBAAuB;AAE7B,MAAM,uBAAuB;AAE7B,MAAM,sBAAsB;AAE5B,MAAM,sBAAsB;AAE5B,MAAM,qBAAqB;AAE3B,MAAM,2BAA2B;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/generated/alert-dialog.ts"],"sourcesContent":["// AUTO-GENERATED by `nix run .#gen` — do not edit.\n\nexport const ALERT_DIALOG_OVERLAY = \"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 duration-200 data-[state=closed]:fill-mode-forwards data-[state=closed]:pointer-events-none fixed inset-0 z-50 bg-black/50\";\n\nexport const ALERT_DIALOG_CONTENT = \"bg-background 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 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border border-border p-6 shadow-lg duration-200 data-[state=closed]:fill-mode-forwards outline-hidden sm:max-w-lg\";\n\nexport const ALERT_DIALOG_HEADER = \"flex flex-col gap-2 text-center sm:text-left\";\n\nexport const ALERT_DIALOG_FOOTER = \"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end\";\n\nexport const ALERT_DIALOG_TITLE = \"text-lg font-semibold\";\n\nexport const ALERT_DIALOG_DESCRIPTION = \"text-ink-soft text-sm\";\n"],"mappings":"AAEO,MAAM,uBAAuB;AAE7B,MAAM,uBAAuB;AAE7B,MAAM,sBAAsB;AAE5B,MAAM,sBAAsB;AAE5B,MAAM,qBAAqB;AAE3B,MAAM,2BAA2B;","names":[]}
|
package/dist/generated/dialog.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
const DIALOG_OVERLAY = "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50";
|
|
2
|
-
const DIALOG_CONTENT = "bg-background 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 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border border-border p-6 shadow-lg duration-200 sm:max-w-lg";
|
|
3
|
-
const DIALOG_CLOSE = "ring-offset-background focus:ring-ring absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4";
|
|
1
|
+
const DIALOG_OVERLAY = "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 duration-200 data-[state=closed]:fill-mode-forwards data-[state=closed]:pointer-events-none fixed inset-0 z-50 bg-black/50";
|
|
2
|
+
const DIALOG_CONTENT = "bg-background 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 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border border-border p-6 shadow-lg duration-200 data-[state=closed]:fill-mode-forwards outline-hidden sm:max-w-lg";
|
|
3
|
+
const DIALOG_CLOSE = "ring-offset-background focus-visible:ring-ring absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4";
|
|
4
4
|
const DIALOG_HEADER = "flex flex-col gap-2 text-center sm:text-left";
|
|
5
5
|
const DIALOG_FOOTER = "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end";
|
|
6
6
|
const DIALOG_TITLE = "text-lg leading-none font-semibold";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/generated/dialog.ts"],"sourcesContent":["// AUTO-GENERATED by `nix run .#gen` — do not edit.\n\nexport const DIALOG_OVERLAY = \"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50\";\n\nexport const DIALOG_CONTENT = \"bg-background 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 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border border-border p-6 shadow-lg duration-200 sm:max-w-lg\";\n\nexport const DIALOG_CLOSE = \"ring-offset-background focus:ring-ring absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\";\n\nexport const DIALOG_HEADER = \"flex flex-col gap-2 text-center sm:text-left\";\n\nexport const DIALOG_FOOTER = \"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end\";\n\nexport const DIALOG_TITLE = \"text-lg leading-none font-semibold\";\n\nexport const DIALOG_DESCRIPTION = \"text-ink-soft text-sm\";\n"],"mappings":"AAEO,MAAM,iBAAiB;AAEvB,MAAM,iBAAiB;AAEvB,MAAM,eAAe;AAErB,MAAM,gBAAgB;AAEtB,MAAM,gBAAgB;AAEtB,MAAM,eAAe;AAErB,MAAM,qBAAqB;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/generated/dialog.ts"],"sourcesContent":["// AUTO-GENERATED by `nix run .#gen` — do not edit.\n\nexport const DIALOG_OVERLAY = \"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 duration-200 data-[state=closed]:fill-mode-forwards data-[state=closed]:pointer-events-none fixed inset-0 z-50 bg-black/50\";\n\nexport const DIALOG_CONTENT = \"bg-background 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 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border border-border p-6 shadow-lg duration-200 data-[state=closed]:fill-mode-forwards outline-hidden sm:max-w-lg\";\n\nexport const DIALOG_CLOSE = \"ring-offset-background focus-visible:ring-ring absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\";\n\nexport const DIALOG_HEADER = \"flex flex-col gap-2 text-center sm:text-left\";\n\nexport const DIALOG_FOOTER = \"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end\";\n\nexport const DIALOG_TITLE = \"text-lg leading-none font-semibold\";\n\nexport const DIALOG_DESCRIPTION = \"text-ink-soft text-sm\";\n"],"mappings":"AAEO,MAAM,iBAAiB;AAEvB,MAAM,iBAAiB;AAEvB,MAAM,eAAe;AAErB,MAAM,gBAAgB;AAEtB,MAAM,gBAAgB;AAEtB,MAAM,eAAe;AAErB,MAAM,qBAAqB;","names":[]}
|