@waveinno/ui 0.1.0 → 0.2.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/CHANGELOG.md +49 -35
- package/README.md +2 -2
- package/dist/components/charts/donut-chart.d.ts +2 -0
- package/dist/components/charts/donut-chart.js +1 -1
- package/dist/components/patterns/app-launcher.d.ts +53 -0
- package/dist/components/patterns/app-launcher.js +1 -0
- package/dist/components/patterns/index.d.ts +1 -0
- package/dist/components/patterns/index.js +1 -1
- package/dist/components/ui/index.d.ts +1 -0
- package/dist/components/ui/index.js +1 -1
- package/dist/components/ui/menu.d.ts +74 -0
- package/dist/components/ui/menu.js +1 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,35 +1,49 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
Every release of `@waveinno/ui`. Add a line under **Unreleased** with each change; `./npmup.sh` moves it under the
|
|
4
|
-
new version when you release. Versions follow [semver](https://semver.org): patch = fix, minor = new component or
|
|
5
|
-
prop, major = a renamed or removed export or prop.
|
|
6
|
-
|
|
7
|
-
## Unreleased
|
|
8
|
-
|
|
9
|
-
## 0.
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
`
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
Every release of `@waveinno/ui`. Add a line under **Unreleased** with each change; `./npmup.sh` moves it under the
|
|
4
|
+
new version when you release. Versions follow [semver](https://semver.org): patch = fix, minor = new component or
|
|
5
|
+
prop, major = a renamed or removed export or prop.
|
|
6
|
+
|
|
7
|
+
## Unreleased
|
|
8
|
+
|
|
9
|
+
## 0.2.0 — 2026-10-02
|
|
10
|
+
|
|
11
|
+
- **Menu**: an always-open menu for settings sidebars and side panels. Looks: soft, line, raised, gradient, dark, plus
|
|
12
|
+
`panel` and `rail`, the AppShell's module panel and icon rail as standalone menus (as in ParkingOS). The highlight
|
|
13
|
+
slides to the chosen item; MenuSub folds a branch away, opens itself around the chosen item and lights its trigger
|
|
14
|
+
when folded; MenuGroup, MenuSeparator; icon, description, count, badge, shortcut, danger, `asChild` for links;
|
|
15
|
+
arrow keys, Home / End, → / ← on branches.
|
|
16
|
+
- **AppLauncher**: the switch-app button, 3×3 dots that fold into a × while open. Looks: grid, list, spotlight (list
|
|
17
|
+
beside a preview), glass, dark; `trigger="chip"` shows the current app by name; arrow keys move by tile or row.
|
|
18
|
+
|
|
19
|
+
## 0.1.1 — 2026-10-02
|
|
20
|
+
|
|
21
|
+
- DonutChart: no floating tooltip on hover; it covered the centre readout, which already shows the hovered slice.
|
|
22
|
+
|
|
23
|
+
## 0.1.0 — 2026-10-02
|
|
24
|
+
|
|
25
|
+
First release on npm, as compiled JavaScript (one module per file, `"use client"` kept per module) with type
|
|
26
|
+
definitions; no source or source maps are published.
|
|
27
|
+
|
|
28
|
+
- **Tokens** in `@waveinno/ui/styles.css`: light and dark, three densities, motion that stops for reduced motion.
|
|
29
|
+
- **Brand**: official `WaveinnoLogo` and `WaveinnoSymbol` (from brand/waveinno-logo.svg), `WaveMark` app icon in seven
|
|
30
|
+
tones drawn from the logo colours, squircle or round, flat or 3D, with shine / float / pulse / flip motion;
|
|
31
|
+
`PoweredBy` signature (inline or floating, like WAVEMAP's), `Watermark`; logo file at `@waveinno/ui/brand/`.
|
|
32
|
+
- **Primitives**: Button (primary, secondary, ghost, subtle, danger, link; gradient, glow, dark, outline, elevated,
|
|
33
|
+
glass, white; pill shape, xl size), Badge (soft, solid, outline, dot, gradient, live), Input and Textarea with
|
|
34
|
+
error / warning / success states and leading / trailing slots, FormField, Select, searchable Combobox, Checkbox,
|
|
35
|
+
Switch, Segmented (pill, solid, outline, glass, sliding thumb, three sizes), Tabs (underline, pill, solid, enclosed,
|
|
36
|
+
sliding indicator), Calendar, DatePicker, DateTimePicker, DateRangePicker with presets, TimePicker (12/24 h),
|
|
37
|
+
Dialog (floating drawer that widens or goes full width; icon and eyebrow header; frosted footer), Sheet,
|
|
38
|
+
ConfirmDialog (item preview, type-to-confirm), DropdownMenu (rich items, account header, glass and dark), Popover
|
|
39
|
+
(arrow, glass, dark, brand), Tooltip, Progress (bar, gradient, striped, segmented, indeterminate) and ProgressRing,
|
|
40
|
+
Avatar (name colours, status, verified, gradient ring, square), Skeleton.
|
|
41
|
+
- **Patterns**: DataTable with advanced filters (text, select, number, date; all / any; chips; saved in views),
|
|
42
|
+
search, sort, columns, pinning, CSV export, five looks (default, striped, bordered, cards, minimal), inline cell
|
|
43
|
+
editing with validation, bulk edit and bulk delete, sticky action column; UserCell and RowActions; PageHero;
|
|
44
|
+
PageHeader; StatCard (icon, spark, target, gradient, comparison) and KpiCard; Money; ActivityFeed (list, cards,
|
|
45
|
+
compact); ActivityTimeline; ChangeHistory (field diffs); ChangeNotes (threaded, replies); Note (tones, outline,
|
|
46
|
+
gradient); Kanban; empty, error and loading states.
|
|
47
|
+
- **Charts**: TrendChart, BarChart, LineChart (comparison and target), DonutChart, GaugeChart, Heatmap, BarList,
|
|
48
|
+
ChartCard and a validated palette.
|
|
49
|
+
- **Shell**: AppShell (rail → panel → content → drawer, ⌘K), theme and density controls, record drawer.
|
package/README.md
CHANGED
|
@@ -45,8 +45,8 @@ through `labels` props, so each product passes its own language.
|
|
|
45
45
|
|
|
46
46
|
| Group | Components |
|
|
47
47
|
| --- | --- |
|
|
48
|
-
| Primitives | Button (working + showcase variants), Badge (soft, solid, outline, dot, gradient, live), Input / Textarea (states, adornments), FormField, Select, Combobox (searchable), Checkbox, Switch, Segmented, Tabs (underline, pill, solid, enclosed), Calendar, DatePicker, DateTimePicker, DateRangePicker, TimePicker, Dialog (drawer that widens or goes full width), Sheet, ConfirmDialog, DropdownMenu (rich items, header, dark), Popover (arrow, dark, brand), Tooltip, Progress / ProgressRing, Avatar, Skeleton |
|
|
49
|
-
| Patterns | DataTable (advanced filters, saved views, five looks, inline and bulk edit, bulk delete, sticky actions), UserCell, RowActions, PageHero, PageHeader, StatCard, KpiCard, Money, ActivityFeed, ActivityTimeline, ChangeHistory, ChangeNotes, Note, Kanban, empty / error / loading states |
|
|
48
|
+
| Primitives | Button (working + showcase variants), Badge (soft, solid, outline, dot, gradient, live), Input / Textarea (states, adornments), FormField, Select, Combobox (searchable), Checkbox, Switch, Segmented, Tabs (underline, pill, solid, enclosed), Calendar, DatePicker, DateTimePicker, DateRangePicker, TimePicker, Dialog (drawer that widens or goes full width), Sheet, ConfirmDialog, DropdownMenu (rich items, header, dark), Menu (soft, line, raised, gradient, dark, plus the shell's panel and rail; branches, sliding highlight), Popover (arrow, dark, brand), Tooltip, Progress / ProgressRing, Avatar, Skeleton |
|
|
49
|
+
| Patterns | AppLauncher (switch-app button: grid, list, spotlight, glass, dark), DataTable (advanced filters, saved views, five looks, inline and bulk edit, bulk delete, sticky actions), UserCell, RowActions, PageHero, PageHeader, StatCard, KpiCard, Money, ActivityFeed, ActivityTimeline, ChangeHistory, ChangeNotes, Note, Kanban, empty / error / loading states |
|
|
50
50
|
| Charts | TrendChart, BarChart, LineChart, DonutChart, GaugeChart, Heatmap, BarList, ChartCard, validated palette |
|
|
51
51
|
| Brand | WaveinnoLogo, WaveinnoSymbol, WaveMark (app icon: seven tones, round, 3D, animated), PoweredBy, Watermark; `@waveinno/ui/brand/waveinno-logo.svg` |
|
|
52
52
|
| Shell | AppShell (rail → panel → content → drawer, ⌘K), theme and density controls, record drawer |
|
|
@@ -6,6 +6,8 @@ import * as React from "react";
|
|
|
6
6
|
* Six slices at most: past that, a donut is a guessing game, so the smallest
|
|
7
7
|
* fold into "Other" (pass `maxSlices`). The legend always shows value and
|
|
8
8
|
* percentage, because angles are the hardest thing for eyes to compare.
|
|
9
|
+
* Hovering a slice (or its legend row) reads it out in the hole — no floating
|
|
10
|
+
* tooltip, which would sit on top of that same readout.
|
|
9
11
|
*/
|
|
10
12
|
export interface DonutDatum {
|
|
11
13
|
id: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use client";import{jsx as t,jsxs as
|
|
1
|
+
"use client";import{jsx as t,jsxs as c}from"react/jsx-runtime";import*as f from"react";import{Cell as D,Pie as M,PieChart as A,ResponsiveContainer as E}from"recharts";import{cn as N}from"../../lib/utils.js";import{SERIES as y}from"./palette.js";function _({data:d,format:m=a=>a.toLocaleString(),centerLabel:R="Total",centerValue:k,maxSlices:p=6,otherLabel:x="Other",height:w=220,legend:v="right",className:C}){const[a,r]=f.useState(null),l=f.useMemo(()=>{const e=[...d].sort((o,i)=>i.value-o.value),n=e.slice(0,p-(e.length>p?1:0)),h=e.slice(n.length),b=n.map((o,i)=>({...o,color:o.color??y[i%y.length]}));return h.length&&b.push({id:"__other",label:x,value:h.reduce((o,i)=>o+i.value,0),color:"var(--border2)"}),b},[d,p,x]),u=l.reduce((e,n)=>e+n.value,0),g=e=>u?`${(e/u*100).toFixed(e/u<.1?1:0)}%`:"0%",s=a!==null?l[a]:null;return c("div",{className:N("flex gap-5",v==="bottom"?"flex-col":"flex-col sm:flex-row sm:items-center",C),children:[c("div",{className:"relative mx-auto aspect-square w-full max-w-[220px] shrink-0",style:{height:w},children:[t(E,{width:"100%",height:"100%",children:t(A,{children:t(M,{data:l,dataKey:"value",nameKey:"label",innerRadius:"68%",outerRadius:"92%",paddingAngle:l.length>1?2:0,cornerRadius:4,stroke:"var(--raised)",strokeWidth:2,onMouseEnter:(e,n)=>r(n),onMouseLeave:()=>r(null),isAnimationActive:!0,animationDuration:700,children:l.map((e,n)=>t(D,{fill:e.color,fillOpacity:a===null||a===n?1:.35,style:{transition:"fill-opacity 150ms"}},e.id))})})}),t("div",{className:"pointer-events-none absolute inset-0 grid place-items-center text-center",children:c("div",{children:[t("p",{className:"text-[11px] font-medium uppercase tracking-[0.06em] text-muted",children:s?s.label:R}),t("p",{className:"num disp mt-0.5 text-[20px] font-semibold leading-none text-text",children:s?m(s.value):k??m(u)}),s?t("p",{className:"num mt-1 text-[11.5px] text-muted",children:g(s.value)}):null]})})]}),v!=="none"?t("ul",{className:"min-w-0 flex-1 space-y-1",children:l.map((e,n)=>c("li",{onMouseEnter:()=>r(n),onMouseLeave:()=>r(null),className:N("flex items-center gap-2.5 rounded-ctl px-2 py-1.5 text-[13px] transition-colors",a===n&&"bg-hover"),children:[t("span",{className:"size-2.5 shrink-0 rounded-[3px]",style:{background:e.color},"aria-hidden":!0}),t("span",{className:"min-w-0 flex-1 truncate text-body",children:e.label}),t("span",{className:"num font-medium text-text",children:m(e.value)}),t("span",{className:"num w-12 text-right text-[12px] text-muted",children:g(e.value)})]},e.id))}):null]})}export{_ as DonutChart};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* AppLauncher — the "switch app" button: a 3×3 grid of dots that opens the
|
|
4
|
+
* apps a person can move to. The dots fold into a × while it is open, so the
|
|
5
|
+
* same spot closes it.
|
|
6
|
+
*
|
|
7
|
+
* Five looks — set `variant`:
|
|
8
|
+
* grid — the default: tiles in threes. Up to nine apps.
|
|
9
|
+
* list — rows with a line on each app. When the names alone do not say enough.
|
|
10
|
+
* spotlight — a list beside a preview of the app under the pointer. A suite's front door.
|
|
11
|
+
* glass — frosted tiles, for a launcher over a map, a photo, a hero.
|
|
12
|
+
* dark — ink tiles that glow in each app's colour.
|
|
13
|
+
*
|
|
14
|
+
* For a product inside AppShell, the shell's own switcher (⌘⇧K) already does
|
|
15
|
+
* this; AppLauncher is for everything outside it: marketing sites, consoles,
|
|
16
|
+
* a header that is not the shell's.
|
|
17
|
+
*/
|
|
18
|
+
export interface LauncherApp {
|
|
19
|
+
id: string;
|
|
20
|
+
name: string;
|
|
21
|
+
/** One line on what it is for. Shown by `list` and `spotlight`. */
|
|
22
|
+
description?: string;
|
|
23
|
+
/** A finished mark (WaveMark, an <img>), or a plain icon drawn on `tint`. */
|
|
24
|
+
icon: React.ReactNode;
|
|
25
|
+
/** Tile colour behind a plain icon: any CSS colour or gradient. Leave out for a finished mark. */
|
|
26
|
+
tint?: string;
|
|
27
|
+
/** Where it opens. Without one, the app is a button and `onSelect` decides. */
|
|
28
|
+
href?: string;
|
|
29
|
+
badge?: React.ReactNode;
|
|
30
|
+
/** Shown, not openable: coming soon, not on the plan. */
|
|
31
|
+
disabled?: boolean;
|
|
32
|
+
}
|
|
33
|
+
export type AppLauncherVariant = "grid" | "list" | "spotlight" | "glass" | "dark";
|
|
34
|
+
export interface AppLauncherProps {
|
|
35
|
+
apps: LauncherApp[];
|
|
36
|
+
/** The `id` of the app the person is in. */
|
|
37
|
+
current?: string;
|
|
38
|
+
onSelect?: (app: LauncherApp) => void;
|
|
39
|
+
variant?: AppLauncherVariant;
|
|
40
|
+
/** `icon`: the dots alone (headers). `chip`: the current app's tile and name beside them. */
|
|
41
|
+
trigger?: "icon" | "chip";
|
|
42
|
+
title?: React.ReactNode;
|
|
43
|
+
/** Top right of the panel: "Manage", a settings link. */
|
|
44
|
+
action?: React.ReactNode;
|
|
45
|
+
/** Under the apps: "All products", a link to the marketplace. */
|
|
46
|
+
footer?: React.ReactNode;
|
|
47
|
+
align?: "start" | "center" | "end";
|
|
48
|
+
side?: "top" | "right" | "bottom" | "left";
|
|
49
|
+
open?: boolean;
|
|
50
|
+
onOpenChange?: (open: boolean) => void;
|
|
51
|
+
className?: string;
|
|
52
|
+
}
|
|
53
|
+
export declare function AppLauncher({ apps, current, onSelect, variant, trigger, title, action, footer, align, side, open: openProp, onOpenChange, className, }: AppLauncherProps): React.JSX.Element;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{Fragment as _,jsx as e,jsxs as o}from"react/jsx-runtime";import*as b from"@radix-ui/react-popover";import{ArrowUpRight as M,Check as w}from"lucide-react";import*as N from"react";import{cn as c}from"../../lib/utils.js";const G={grid:"w-[328px] border-border bg-raised",list:"w-[340px] border-border bg-raised",spotlight:"w-[560px] max-w-[calc(100vw-24px)] border-border bg-raised p-0",glass:"w-[328px] border-white/50 bg-raised/65 backdrop-blur-2xl backdrop-saturate-150 shadow-[inset_0_1px_0_rgba(255,255,255,.6),var(--elev3)] dark:border-white/10 dark:shadow-[inset_0_1px_0_rgba(255,255,255,.08),var(--elev3)]",dark:"w-[328px] border-white/10 text-white bg-[#120b2e] bg-[image:radial-gradient(120%_60%_at_0%_0%,rgba(91,74,245,.32),transparent_60%)] [--text:#f5f6fa] [--body:#c9cce0] [--muted:#8d93b3] [--border:rgba(255,255,255,.1)] [--hover:rgba(255,255,255,.07)] [--primary:#a5a8ff] [--primary-subtle:rgba(139,143,248,.16)] [--raised:rgba(255,255,255,.06)] [--surface:rgba(255,255,255,.04)]"},U={icon:"size-9 rounded-full text-muted hover:bg-hover hover:text-text data-[state=open]:bg-hover data-[state=open]:text-text",chip:"h-9 gap-2 rounded-full border border-border bg-raised pl-1.5 pr-3 text-[13px] font-medium text-text shadow-[var(--lit),0_1px_2px_rgba(5,8,22,.06)] hover:bg-hover data-[state=open]:bg-hover"};function T({open:s}){return e("svg",{viewBox:"0 0 18 18",className:"size-[18px] shrink-0","aria-hidden":!0,children:Array.from({length:9},(i,a)=>{const r=Math.floor(a/3),n=a%3,p=r===n||r+n===2,u=s&&p?(1-n)*1.6:0,g=s&&p?(1-r)*1.6:0;return e("circle",{cx:3+n*6,cy:3+r*6,r:1.7,fill:"currentColor",style:{transform:`translate(${u}px, ${g}px) scale(${s&&!p?0:1})`,transformOrigin:`${3+n*6}px ${3+r*6}px`,transition:`transform 260ms cubic-bezier(.3,.7,.2,1) ${s?a*12:(8-a)*12}ms`}},a)})})}function x({app:s,size:i,glow:a}){const r={sm:"size-8 rounded-[9px] [&_svg]:size-4",md:"size-12 rounded-[14px] [&_svg]:size-[22px]",lg:"size-14 rounded-[16px] [&_svg]:size-6"}[i];return s.tint?e("span",{className:c("relative isolate grid shrink-0 place-items-center overflow-hidden text-white","before:absolute before:inset-x-0 before:top-0 before:-z-10 before:h-1/2 before:bg-[linear-gradient(180deg,rgba(255,255,255,.28),transparent)]",r),style:{background:s.tint,boxShadow:`inset 0 1px 0 rgba(255,255,255,.35), inset 0 -8px 16px -8px rgba(5,8,22,.3), 0 1px 2px rgba(5,8,22,.18)${a?", 0 10px 24px -10px rgba(91,74,245,.7)":""}`},children:s.icon}):e("span",{className:c("grid shrink-0 place-items-center [&>img]:size-full [&>*]:max-h-full [&>*]:max-w-full",r),children:s.icon})}function j(s){return i=>{const a=Array.from(i.currentTarget.querySelectorAll("[data-launcher-app]:not([aria-disabled='true'])")),r=a.indexOf(document.activeElement);if(r<0)return;const n={ArrowRight:1,ArrowLeft:-1,ArrowDown:s,ArrowUp:-s}[i.key];let p;n!==void 0?p=a[Math.min(Math.max(r+n,0),a.length-1)]:i.key==="Home"?p=a[0]:i.key==="End"&&(p=a[a.length-1]),p&&(i.preventDefault(),p.focus())}}function y({app:s,current:i,onChoose:a,className:r,children:n,onFocus:p,onMouseEnter:u}){const g={"data-launcher-app":"","aria-current":i?"page":void 0,"aria-disabled":s.disabled||void 0,className:c("outline-none focus-visible:ring-2 focus-visible:ring-primary/45",s.disabled&&"pointer-events-none opacity-45",r),onFocus:p,onMouseEnter:u};return s.href&&!s.disabled?e("a",{href:s.href,...g,onClick:()=>a(s),children:n}):e("button",{type:"button",disabled:s.disabled,...g,onClick:()=>a(s),children:n})}function W({apps:s,current:i,onSelect:a,variant:r="grid",trigger:n="icon",title:p="Apps",action:u,footer:g,align:C="end",side:O="bottom",open:k,onOpenChange:S,className:D}){const[H,$]=N.useState(!1),f=k??H,A=t=>{k===void 0&&$(t),S?.(t)},m=s.find(t=>t.id===i),[K,L]=N.useState(i),l=s.find(t=>t.id===K)??m??s[0],z=r==="grid"||r==="glass"||r==="dark",R=j(z?3:1),h=N.useRef(null),v=t=>{a?.(t),A(!1)},P=o("div",{className:c("flex items-center justify-between gap-2",r==="spotlight"?"border-b border-border px-4 py-3":"px-1 pb-2.5"),children:[e("p",{className:"text-[13px] font-semibold text-text",children:p}),u]}),q=e("div",{ref:h,role:"list",onKeyDown:R,className:"grid grid-cols-3 gap-1",children:s.map(t=>{const d=t.id===i;return e("div",{role:"listitem",children:o(y,{app:t,current:d,onChoose:v,className:c("group/app relative flex w-full cursor-pointer flex-col items-center gap-2 rounded-[12px] px-1 pb-2.5 pt-3 text-center transition-colors duration-150",r==="glass"?"hover:bg-white/40 focus-visible:bg-white/40 dark:hover:bg-white/10 dark:focus-visible:bg-white/10":"hover:bg-hover focus-visible:bg-hover",d&&(r==="dark"?"bg-white/[.06]":r==="glass"?"bg-white/30 dark:bg-white/[.06]":"bg-primary-subtle/60")),children:[o("span",{className:"relative transition-transform duration-200 ease-[cubic-bezier(.3,.7,.2,1)] group-hover/app:-translate-y-0.5 group-hover/app:scale-[1.04]",children:[e(x,{app:t,size:"md",glow:r==="dark"}),d?e("span",{className:"absolute -bottom-1 -right-1 grid size-4 place-items-center rounded-full bg-primary text-white ring-2 ring-[var(--raised)] [&_svg]:size-2.5",children:e(w,{strokeWidth:3.5})}):null]}),e("span",{className:"w-full truncate text-[12px] font-medium leading-4 text-text",children:t.name}),t.badge?e("span",{className:"-mt-1 scale-90",children:t.badge}):null]})},t.id)})}),E=e("div",{ref:h,role:"list",onKeyDown:R,className:c("flex flex-col gap-0.5",r==="spotlight"&&"p-1.5"),children:s.map(t=>{const d=t.id===i,V=r==="spotlight"&&t.id===l?.id;return e("div",{role:"listitem",children:o(y,{app:t,current:d,onChoose:v,onFocus:()=>L(t.id),onMouseEnter:()=>L(t.id),className:c("group/app flex w-full cursor-pointer items-center gap-3 rounded-[10px] px-2 py-2 text-left transition-colors duration-150",V?"bg-raised shadow-[var(--lit),0_1px_2px_rgba(5,8,22,.06),0_4px_12px_-4px_rgba(5,8,22,.14)] ring-1 ring-border":"hover:bg-hover focus-visible:bg-hover"),children:[e(x,{app:t,size:r==="spotlight"?"sm":"md"}),o("span",{className:"min-w-0 flex-1",children:[o("span",{className:"flex items-center gap-2",children:[e("span",{className:c("truncate text-[13px] font-medium",d?"text-primary":"text-text"),children:t.name}),t.badge]}),t.description&&r==="list"?e("span",{className:"mt-0.5 block truncate text-[11.5px] text-muted",children:t.description}):null]}),d?e(w,{className:"size-4 shrink-0 text-primary"}):e(M,{className:"size-4 shrink-0 text-muted opacity-0 transition-[opacity,transform] duration-150 group-hover/app:translate-x-0.5 group-hover/app:-translate-y-0.5 group-hover/app:opacity-100"})]})},t.id)})}),F=o(_,{children:[P,o("div",{className:"grid min-h-[260px] grid-cols-1 sm:grid-cols-[220px_1fr]",children:[e("div",{className:"border-border bg-surface sm:border-r",children:E}),l?o("div",{className:"hidden flex-col p-5 animate-[wave-in_220ms_var(--ease)] sm:flex",children:[e(x,{app:l,size:"lg",glow:!0}),o("div",{className:"mt-4 flex items-center gap-2",children:[e("p",{className:"disp text-[16px] font-semibold text-text",children:l.name}),l.badge]}),l.description?e("p",{className:"mt-1 text-[12.5px] leading-[18px] text-body",children:l.description}):null,e("div",{className:"mt-auto pt-5",children:l.id===i?o("p",{className:"flex items-center gap-1.5 text-[12px] font-medium text-primary",children:[e(w,{className:"size-3.5"})," You are here"]}):o(y,{app:l,current:!1,onChoose:v,className:"inline-flex h-8 cursor-pointer items-center gap-1.5 rounded-full bg-[linear-gradient(135deg,#1f45ea,#5b4af5)] px-3.5 text-[12.5px] font-medium text-white shadow-[inset_0_1px_0_rgba(255,255,255,.25),0_6px_14px_-6px_rgba(20,55,211,.7)] transition-[filter] hover:brightness-110",children:["Open ",l.name," ",e(M,{className:"size-3.5"})]})})]},l.id):null]}),g?e("div",{className:"border-t border-border bg-surface px-4 py-2.5 text-[12px] text-muted",children:g}):null]});return o(b.Root,{open:f,onOpenChange:A,children:[e(b.Trigger,{"aria-label":m?`${m.name}. Switch app`:"Switch app",className:c("inline-flex shrink-0 cursor-pointer items-center justify-center outline-none transition-colors duration-150 focus-visible:ring-2 focus-visible:ring-primary/45",U[n],D),children:n==="chip"&&m?o(_,{children:[e("span",{className:"[&_svg]:!size-3.5",children:e(x,{app:m,size:"sm"})}),e("span",{className:"max-w-[120px] truncate",children:m.name}),e("span",{className:"text-muted",children:e(T,{open:f})})]}):e(T,{open:f})}),e(b.Portal,{children:e(b.Content,{"data-pop":"",align:C,side:O,sideOffset:8,collisionPadding:12,onOpenAutoFocus:t=>{t.preventDefault();const d=h.current;(d?.querySelector("[data-launcher-app][aria-current]")??d?.querySelector("[data-launcher-app]"))?.focus()},className:c("z-50 overflow-hidden rounded-[18px] border p-2.5 shadow-elev3 outline-none",G[r]),children:r==="spotlight"?F:o(_,{children:[e("div",{className:"px-1 pt-1",children:P}),z?q:E,g?e("div",{className:"mt-2 border-t border-border px-1 pt-2.5 text-[12px] text-muted",children:g}):null]})})})]})}export{W as AppLauncher};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export*from"./activity-feed.js";export*from"./data-table.js";export*from"./data-table-cells.js";export*from"./data-table-edit.js";export*from"./data-table-filters.js";export*from"./kanban.js";export*from"./kpi-card.js";export*from"./money.js";export*from"./page-header.js";export*from"./page-hero.js";export*from"./page-shell.js";export*from"./stat-card.js";export*from"./states.js";export*from"./timeline.js";
|
|
1
|
+
export*from"./activity-feed.js";export*from"./app-launcher.js";export*from"./data-table.js";export*from"./data-table-cells.js";export*from"./data-table-edit.js";export*from"./data-table-filters.js";export*from"./kanban.js";export*from"./kpi-card.js";export*from"./money.js";export*from"./page-header.js";export*from"./page-hero.js";export*from"./page-shell.js";export*from"./stat-card.js";export*from"./states.js";export*from"./timeline.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export*from"./avatar.js";export*from"./badge.js";export*from"./button.js";export*from"./calendar.js";export*from"./card.js";export*from"./checkbox.js";export*from"./combobox.js";export*from"./confirm-dialog.js";export*from"./dialog.js";export*from"./dropdown-menu.js";export*from"./form-field.js";export*from"./input.js";export*from"./label.js";export*from"./popover.js";export*from"./date-picker.js";export*from"./progress.js";export*from"./segmented.js";export*from"./select.js";export*from"./separator.js";export*from"./sheet.js";export*from"./skeleton.js";export*from"./switch.js";export*from"./tabs.js";export*from"./time-picker.js";export*from"./tooltip.js";
|
|
1
|
+
export*from"./avatar.js";export*from"./badge.js";export*from"./button.js";export*from"./calendar.js";export*from"./card.js";export*from"./checkbox.js";export*from"./combobox.js";export*from"./confirm-dialog.js";export*from"./dialog.js";export*from"./dropdown-menu.js";export*from"./form-field.js";export*from"./input.js";export*from"./label.js";export*from"./menu.js";export*from"./popover.js";export*from"./date-picker.js";export*from"./progress.js";export*from"./segmented.js";export*from"./select.js";export*from"./separator.js";export*from"./sheet.js";export*from"./skeleton.js";export*from"./switch.js";export*from"./tabs.js";export*from"./time-picker.js";export*from"./tooltip.js";
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Menu — an always-open list of places: a settings sidebar, an account page, a
|
|
4
|
+
* side panel. (A list that opens from a button is DropdownMenu.)
|
|
5
|
+
*
|
|
6
|
+
* Seven looks, one API — set `variant` on Menu:
|
|
7
|
+
* soft — the default: the chosen item in a tinted fill. Settings, panels.
|
|
8
|
+
* line — a rail down the left with a lit segment. Long lists, docs, drawers.
|
|
9
|
+
* raised — a recessed tray with the chosen item lifted out of it. Side panels.
|
|
10
|
+
* gradient — the chosen item in the brand gradient. The main menu of a page.
|
|
11
|
+
* dark — an ink panel with a glowing edge. Command areas, media, maps.
|
|
12
|
+
* panel — the AppShell's module panel (ParkingOS): a titled surface, plain counts.
|
|
13
|
+
* rail — the AppShell's icon rail (ParkingOS): icon over a short label, a
|
|
14
|
+
* bar on the edge. Labels stay short; no descriptions or branches.
|
|
15
|
+
*
|
|
16
|
+
* The highlight is one element that slides to the chosen item rather than a
|
|
17
|
+
* fill that jumps, so a change of place is seen. Items take an `icon`, a
|
|
18
|
+
* second-line `description`, a `count`, a `badge` and a `shortcut`; MenuSub
|
|
19
|
+
* folds a branch away and opens itself when it holds the chosen item. Arrow
|
|
20
|
+
* keys, Home and End move between items; → and ← open and close a branch.
|
|
21
|
+
*/
|
|
22
|
+
export type MenuLook = "soft" | "line" | "raised" | "gradient" | "dark" | "panel" | "rail";
|
|
23
|
+
export interface MenuProps extends Omit<React.HTMLAttributes<HTMLElement>, "defaultValue" | "onChange"> {
|
|
24
|
+
variant?: MenuLook;
|
|
25
|
+
/** The chosen item's `value` (controlled). */
|
|
26
|
+
value?: string;
|
|
27
|
+
defaultValue?: string;
|
|
28
|
+
onValueChange?: (value: string) => void;
|
|
29
|
+
/** On top of the list: a title, or in the rail, the product mark. */
|
|
30
|
+
heading?: React.ReactNode;
|
|
31
|
+
}
|
|
32
|
+
export declare const Menu: React.ForwardRefExoticComponent<MenuProps & React.RefAttributes<HTMLElement>>;
|
|
33
|
+
interface ItemContent {
|
|
34
|
+
icon?: React.ReactNode;
|
|
35
|
+
/** Second line, muted: what is behind the item. Puts the icon in a tile. */
|
|
36
|
+
description?: React.ReactNode;
|
|
37
|
+
badge?: React.ReactNode;
|
|
38
|
+
/** A number on the right: unread, pending, items inside. */
|
|
39
|
+
count?: number | string;
|
|
40
|
+
shortcut?: string;
|
|
41
|
+
}
|
|
42
|
+
export interface MenuItemProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "value">, ItemContent {
|
|
43
|
+
/** Chosen when it matches Menu's `value`; clicking makes it the value. */
|
|
44
|
+
value?: string;
|
|
45
|
+
/** Mark it chosen yourself, e.g. from the router's pathname. Overrides `value`. */
|
|
46
|
+
active?: boolean;
|
|
47
|
+
danger?: boolean;
|
|
48
|
+
/** Render your own element (a Next.js Link): `<MenuItem asChild><Link href="/x">Label</Link></MenuItem>`. */
|
|
49
|
+
asChild?: boolean;
|
|
50
|
+
}
|
|
51
|
+
export declare const MenuItem: React.ForwardRefExoticComponent<MenuItemProps & React.RefAttributes<HTMLButtonElement>>;
|
|
52
|
+
export interface MenuSubProps extends ItemContent {
|
|
53
|
+
label: React.ReactNode;
|
|
54
|
+
defaultOpen?: boolean;
|
|
55
|
+
open?: boolean;
|
|
56
|
+
onOpenChange?: (open: boolean) => void;
|
|
57
|
+
disabled?: boolean;
|
|
58
|
+
className?: string;
|
|
59
|
+
children: React.ReactNode;
|
|
60
|
+
}
|
|
61
|
+
/** A branch: a trigger that folds its items away. Opens by itself when it holds the chosen item. */
|
|
62
|
+
export declare function MenuSub({ label, defaultOpen, open, onOpenChange, disabled, className, children, ...content }: MenuSubProps): React.JSX.Element;
|
|
63
|
+
/** A labelled section. The label is an overline, not an item: it does nothing. */
|
|
64
|
+
export declare function MenuGroup({ label, action, className, children, }: {
|
|
65
|
+
label?: React.ReactNode;
|
|
66
|
+
/** Something small on the label's right: a "+" button, a count. */
|
|
67
|
+
action?: React.ReactNode;
|
|
68
|
+
className?: string;
|
|
69
|
+
children: React.ReactNode;
|
|
70
|
+
}): React.JSX.Element;
|
|
71
|
+
export declare function MenuSeparator({ className }: {
|
|
72
|
+
className?: string;
|
|
73
|
+
}): React.JSX.Element;
|
|
74
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{Fragment as D,jsx as o,jsxs as M}from"react/jsx-runtime";import{Slot as V}from"@radix-ui/react-slot";import{ChevronRight as q}from"lucide-react";import*as r from"react";import{cn as u}from"../../lib/utils.js";const S={soft:{root:"",indicator:"-z-10 rounded-[9px] bg-primary-subtle ring-1 ring-inset ring-primary/10",item:["text-body hover:bg-hover hover:text-text","text-primary"],icon:["text-muted group-hover/item:text-text","text-primary"],tile:["border-border bg-surface","border-primary/20 bg-raised shadow-[0_2px_6px_-2px_rgba(79,70,229,.35)]"],description:["text-muted","text-primary/70"],count:["bg-hover text-muted","bg-primary text-white"],kbd:["border-border bg-surface text-muted","border-primary/20 bg-raised text-primary"]},line:{root:"pl-3 before:absolute before:inset-y-1 before:left-[5px] before:w-px before:bg-border",indicator:"z-[1] rounded-full bg-[linear-gradient(180deg,#5b4af5,#1f45ea)] shadow-[0_0_0_3px_var(--canvas),0_0_12px_rgba(91,74,245,.55)]",item:["text-muted hover:bg-hover/70 hover:text-text","bg-[linear-gradient(90deg,var(--primary-subtle),transparent_85%)] text-text"],icon:["text-muted group-hover/item:text-text","text-primary"],tile:["border-border bg-surface","border-primary/20 bg-primary-subtle"],description:["text-muted","text-muted"],count:["bg-hover text-muted","bg-primary-subtle text-primary"],kbd:["border-border bg-surface text-muted","border-border bg-raised text-text"]},raised:{root:"rounded-[14px] border border-border bg-surface p-1.5 shadow-[inset_0_1px_2px_rgba(5,8,22,.04)]",indicator:"-z-10 rounded-[10px] bg-raised ring-1 ring-border/70 shadow-[var(--lit),0_1px_2px_rgba(5,8,22,.06),0_6px_16px_-6px_rgba(5,8,22,.16)]",item:["text-muted hover:bg-raised/60 hover:text-text","text-text"],icon:["text-muted group-hover/item:text-text","text-primary"],tile:["border-border bg-raised","border-primary/15 bg-primary-subtle"],description:["text-muted","text-muted"],count:["bg-hover text-muted","bg-primary-subtle text-primary"],kbd:["border-border bg-raised text-muted","border-border bg-surface text-text"]},gradient:{root:"",indicator:"-z-10 rounded-[10px] bg-[linear-gradient(135deg,#1f45ea,#5b4af5)] shadow-[inset_0_1px_0_rgba(255,255,255,.25),0_8px_20px_-8px_rgba(20,55,211,.7)]",item:["text-body hover:bg-hover hover:text-text","text-white"],icon:["text-muted group-hover/item:text-text","text-white"],tile:["border-border bg-surface","border-white/20 bg-white/15 shadow-[inset_0_1px_0_rgba(255,255,255,.2)]"],description:["text-muted","text-white/75"],count:["bg-hover text-muted","bg-white/20 text-white"],kbd:["border-border bg-surface text-muted","border-white/25 bg-white/10 text-white/85"]},dark:{root:"rounded-[16px] border border-white/10 p-1.5 text-white bg-[#120b2e] bg-[image:radial-gradient(120%_70%_at_0%_0%,rgba(91,74,245,.30),transparent_60%),radial-gradient(90%_60%_at_100%_100%,rgba(31,69,234,.18),transparent_60%)] shadow-[inset_0_1px_0_rgba(255,255,255,.06),0_18px_40px_-16px_rgba(5,8,22,.6)] [--hover:rgba(255,255,255,.06)] [--surface:rgba(255,255,255,.05)] [--raised:rgba(255,255,255,.08)] [--text:#f5f6fa] [--body:#c9cce0] [--muted:#8d93b3] [--border:rgba(255,255,255,.1)] [--primary:#a5a8ff] [--primary-subtle:rgba(139,143,248,.16)] [--danger:#f87171] [--danger-subtle:rgba(239,68,68,.16)] [--canvas:#120b2e]",indicator:"-z-10 rounded-[10px] bg-white/[.07] ring-1 ring-inset ring-white/10 shadow-[inset_0_1px_0_rgba(255,255,255,.08)] before:absolute before:inset-y-2 before:left-0 before:w-[3px] before:rounded-r-full before:bg-[#a5a8ff] before:shadow-[0_0_12px_2px_rgba(139,143,248,.65)]",item:["text-body hover:bg-hover hover:text-text","text-white"],icon:["text-muted group-hover/item:text-text","text-[#a5a8ff]"],tile:["border-border bg-surface","border-[#a5a8ff]/30 bg-[#a5a8ff]/15"],description:["text-muted","text-white/55"],count:["bg-white/[.06] text-muted","bg-[#a5a8ff]/20 text-white"],kbd:["border-border bg-surface text-muted","border-white/15 bg-white/10 text-white/70"]},panel:{root:"rounded-[14px] border border-border bg-surface px-2.5 py-3",indicator:"-z-10 rounded-ctl bg-primary-subtle",item:["font-normal text-body hover:bg-hover hover:text-text","text-primary"],icon:["text-muted group-hover/item:text-text","text-primary"],tile:["border-border bg-raised","border-primary/20 bg-raised"],description:["text-muted","text-primary/70"],count:["bg-transparent px-0 text-[12px] font-normal text-muted","bg-transparent px-0 text-[12px] font-normal text-primary"],kbd:["border-border bg-raised text-muted","border-primary/20 bg-raised text-primary"]},rail:{root:"w-[72px] items-center rounded-[14px] border border-border bg-surface py-2",indicator:"-z-10 rounded-ctl bg-primary-subtle before:absolute before:inset-y-2.5 before:left-[var(--bar-x)] before:w-[3px] before:rounded-r-[3px] before:bg-primary",item:["text-muted hover:bg-hover hover:text-text","text-primary"],icon:["",""],tile:["",""],description:["",""],count:["bg-raised text-body ring-2 ring-surface","bg-primary text-white ring-2 ring-surface"],kbd:["",""],rail:!0}},P=r.createContext({look:"soft",value:void 0,select:()=>{}}),O=r.createContext(null),I="[data-menu-item]",z='[data-menu-sub-body][data-state="closed"]';function K(t){let a=t.querySelector(`${I}[data-active]`),n=a?.closest(z)??null;for(;a&&n&&t.contains(n);)a=n.parentElement?.querySelector(":scope > [data-menu-trigger]")??null,n=n.parentElement?.closest(z)??null;return a}function $(t,a){let n=0,b=0,i=t;for(;i&&i!==a;)n+=i.offsetLeft,b+=i.offsetTop,i=i.offsetParent;return i===a?{x:n,y:b,w:t.offsetWidth,h:t.offsetHeight}:null}const j=r.forwardRef(({variant:t="soft",value:a,defaultValue:n,onValueChange:b,heading:i,className:_,children:N,onKeyDown:y,...E},s)=>{const d=r.useRef(null),[H,l]=r.useState(n),L=a!==void 0?a:H,[x,f]=r.useState(null),c=r.useCallback(e=>{a===void 0&&l(e),b?.(e)},[a,b]),R=r.useMemo(()=>({look:t,value:L,select:c}),[t,L,c]);r.useLayoutEffect(()=>{const e=d.current;if(!e)return;const h=()=>{const T=K(e),p=T?$(T,e):null;f(g=>g&&p&&g.x===p.x&&g.y===p.y&&g.w===p.w&&g.h===p.h?g:p)};h();const C=new MutationObserver(h);C.observe(e,{subtree:!0,childList:!0,attributes:!0,attributeFilter:["data-active","data-state"]});const m=new ResizeObserver(h);return m.observe(e),e.addEventListener("transitionend",h),()=>{C.disconnect(),m.disconnect(),e.removeEventListener("transitionend",h)}},[t]);const v=e=>{y?.(e);const h=d.current;if(e.defaultPrevented||!h)return;const C=document.activeElement,m=Array.from(h.querySelectorAll(I)).filter(g=>!g.closest("[inert]")&&!g.hasAttribute("disabled")&&g.getAttribute("aria-disabled")!=="true"),T=C?m.indexOf(C):-1;if(T<0)return;let p;e.key==="ArrowDown"?p=m[(T+1)%m.length]:e.key==="ArrowUp"?p=m[(T-1+m.length)%m.length]:e.key==="Home"?p=m[0]:e.key==="End"?p=m[m.length-1]:e.key==="ArrowLeft"&&(p=C.closest("[data-menu-sub-body]")?.parentElement?.querySelector(":scope > [data-menu-trigger]")),p&&(e.preventDefault(),p.focus())},k=S[t],w=t==="line";return o(P.Provider,{value:R,children:M("nav",{ref:e=>{d.current=e,typeof s=="function"?s(e):s&&(s.current=e)},"data-variant":t,className:u("relative isolate flex flex-col gap-0.5 text-[13px]",k.root,_),onKeyDown:v,...E,children:[i?k.rail?o("div",{className:"mb-3 grid shrink-0 place-items-center",children:i}):o("h2",{className:"px-2.5 pb-2.5 pt-1.5 text-[13px] font-semibold leading-[18px] text-text",children:i}):null,N,x?o("span",{"aria-hidden":!0,className:u("pointer-events-none absolute left-0 top-0 transition-[transform,width,height] duration-300 ease-[cubic-bezier(.3,.7,.2,1)]",k.indicator),style:w?{transform:`translate3d(4px, ${x.y+7}px, 0)`,width:3,height:Math.max(x.h-14,8)}:{transform:`translate3d(${x.x}px, ${x.y}px, 0)`,width:x.w,height:x.h,"--bar-x":`${-x.x}px`}}):null]})})});j.displayName="Menu";function A({label:t,icon:a,description:n,badge:b,count:i,shortcut:_,on:N,danger:y,trailing:E}){const s=S[r.useContext(P).look],d=N?1:0;return s.rail?M(D,{children:[a,o("span",{className:"w-full truncate px-0.5 text-center text-[10px] font-medium leading-3",children:t}),i!==void 0?o("span",{className:u("num absolute right-1 top-1 min-w-4 rounded-full px-1 text-center text-[9.5px] font-semibold leading-4 transition-colors",s.count[d]),children:i}):null]}):M(D,{children:[a?n?o("span",{className:u("grid size-8 shrink-0 place-items-center rounded-[9px] border transition-colors",y?"border-danger/20 bg-danger-subtle text-danger":u(s.tile[d],s.icon[d])),children:a}):o("span",{className:u("grid shrink-0 place-items-center transition-colors",y?"text-danger":s.icon[d]),children:a}):null,M("span",{className:"min-w-0 flex-1",children:[M("span",{className:"flex items-center gap-2",children:[o("span",{className:"truncate",children:t}),b]}),n?o("span",{className:u("mt-0.5 block truncate text-[11.5px] font-normal leading-4",y?"text-danger/70":s.description[d]),children:n}):null]}),i!==void 0?o("span",{className:u("num min-w-5 shrink-0 rounded-full px-1.5 text-center text-[10.5px] font-semibold leading-[18px] transition-colors",s.count[d]),children:i}):null,_?o("kbd",{className:u("shrink-0 rounded-[5px] border px-1.5 py-px font-sans text-[10.5px] font-medium transition-colors",s.kbd[d]),children:_}):null,E]})}const F="mx-auto min-h-14 w-[60px] flex-col justify-center gap-[3px] px-0.5 py-1.5 [&_svg]:size-[19px]",B="group/item relative flex min-h-item w-full cursor-pointer select-none items-center gap-2.5 rounded-[9px] px-2.5 py-1.5 text-left font-medium outline-none transition-[color,background-color] duration-150 focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary/45 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0",G=r.forwardRef(({value:t,active:a,danger:n,asChild:b,icon:i,description:_,badge:N,count:y,shortcut:E,className:s,children:d,onClick:H,...l},L)=>{const x=r.useContext(P),f=r.useContext(O),c=!n&&(a??(t!==void 0&&x.value===t)),R=S[x.look];r.useEffect(()=>{if(!(!c||!f))return f.reveal(),f.mark(1),()=>f.mark(-1)},[c,f]);const v={"data-menu-item":"","data-active":c?"":void 0,"aria-current":c?"page":void 0,className:u(B,R.rail&&F,n?"text-danger hover:bg-danger-subtle":R.item[c?1:0],s),onClick:w=>{H?.(w),!w.defaultPrevented&&t!==void 0&&x.select(t)}},k=w=>o(A,{label:w,icon:i,description:_,badge:N,count:y,shortcut:E,on:c,danger:n});return b&&r.isValidElement(d)?o(V,{ref:L,...v,...l,children:r.cloneElement(d,void 0,k(d.props.children))}):o("button",{ref:L,type:"button",...v,...l,children:k(d)})});G.displayName="MenuItem";function J({label:t,defaultOpen:a=!1,open:n,onOpenChange:b,disabled:i,className:_,children:N,...y}){const E=r.useContext(P),s=r.useContext(O),[d,H]=r.useState(a),l=n??d,[L,x]=r.useState(0),f=r.useId(),c=r.useCallback(e=>{n===void 0&&H(e),b?.(e)},[n,b]),R=r.useMemo(()=>({reveal:()=>{c(!0),s?.reveal()},mark:e=>x(h=>h+e)}),[c,s]),v=L>0;r.useEffect(()=>{if(!(!v||!s))return s.mark(1),()=>s.mark(-1)},[v,s]);const k=S[E.look],w=v&&!l;return M("div",{"data-menu-sub":"",className:_,children:[o("button",{type:"button","data-menu-item":"","data-menu-trigger":"","data-state":l?"open":"closed","aria-expanded":l,"aria-controls":f,disabled:i,className:u(B,k.item[w?1:0],v&&l&&"text-text"),onClick:()=>c(!l),onKeyDown:e=>{e.key==="ArrowRight"?(e.preventDefault(),l?document.getElementById(f)?.querySelector(I)?.focus():c(!0)):e.key==="ArrowLeft"&&l&&(e.preventDefault(),c(!1))},children:o(A,{label:t,...y,on:w,trailing:o(q,{className:u("!size-3.5 shrink-0 opacity-60 transition-transform duration-200",l&&"rotate-90"),"aria-hidden":!0})})}),o("div",{id:f,"data-menu-sub-body":"","data-state":l?"open":"closed",inert:!l,className:u("grid transition-[grid-template-rows,opacity] duration-300 ease-[var(--ease)]",l?"grid-rows-[1fr] opacity-100":"grid-rows-[0fr] opacity-0"),children:o("div",{className:"min-h-0 overflow-hidden",children:o("div",{className:"relative ml-[18px] flex flex-col gap-0.5 py-0.5 pl-3 before:absolute before:inset-y-1 before:left-0 before:w-px before:bg-border",children:o(O.Provider,{value:R,children:N})})})})]})}function Q({label:t,action:a,className:n,children:b}){const i=r.useId();return S[r.useContext(P).look].rail?o("div",{role:"group","aria-label":typeof t=="string"?t:void 0,className:u("flex w-full flex-col items-center gap-0.5 [&:not(:first-child)]:before:my-1.5 [&:not(:first-child)]:before:h-px [&:not(:first-child)]:before:w-8 [&:not(:first-child)]:before:bg-border2",n),children:b}):M("div",{role:"group","aria-labelledby":t?i:void 0,className:u("flex flex-col gap-0.5 [&:not(:first-child)]:mt-2",n),children:[t?M("div",{className:"flex items-center justify-between gap-2 px-2.5 pb-1 pt-1.5",children:[o("p",{id:i,className:"ov",children:t}),a]}):null,b]})}function X({className:t}){return o("div",{role:"separator",className:u("mx-1 my-1.5 h-px shrink-0 bg-border",t)})}export{j as Menu,Q as MenuGroup,G as MenuItem,X as MenuSeparator,J as MenuSub};
|