@waveinno/ui 0.1.1 → 0.3.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 +26 -0
- package/README.md +2 -2
- 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/alert.d.ts +28 -0
- package/dist/components/ui/alert.js +1 -0
- package/dist/components/ui/file-upload.d.ts +36 -0
- package/dist/components/ui/file-upload.js +1 -0
- package/dist/components/ui/index.d.ts +12 -1
- package/dist/components/ui/index.js +1 -1
- package/dist/components/ui/kbd.d.ts +13 -0
- package/dist/components/ui/kbd.js +1 -0
- package/dist/components/ui/menu.d.ts +74 -0
- package/dist/components/ui/menu.js +1 -0
- package/dist/components/ui/number-input.d.ts +30 -0
- package/dist/components/ui/number-input.js +1 -0
- package/dist/components/ui/password-input.d.ts +18 -0
- package/dist/components/ui/password-input.js +1 -0
- package/dist/components/ui/pin-input.d.ts +32 -0
- package/dist/components/ui/pin-input.js +1 -0
- package/dist/components/ui/radio-group.d.ts +27 -0
- package/dist/components/ui/radio-group.js +1 -0
- package/dist/components/ui/slider.d.ts +24 -0
- package/dist/components/ui/slider.js +1 -0
- package/dist/components/ui/spinner.d.ts +27 -0
- package/dist/components/ui/spinner.js +1 -0
- package/dist/components/ui/toast.d.ts +13 -0
- package/dist/components/ui/toast.js +1 -0
- package/dist/shell/app-shell.js +1 -1
- package/dist/styles.css +29 -0
- package/package.json +3 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,32 @@ prop, major = a renamed or removed export or prop.
|
|
|
6
6
|
|
|
7
7
|
## Unreleased
|
|
8
8
|
|
|
9
|
+
## 0.3.0 — 2026-10-02
|
|
10
|
+
|
|
11
|
+
- **RadioGroup**, **RadioGroupItem** and **RadioCard** (whole-card choices with icon, description and a price or badge).
|
|
12
|
+
- **Slider**: single or range, value over the thumb while used (or always), labelled marks, gradient look.
|
|
13
|
+
- **NumberInput**: split (− value +) or stacked steppers, hold to repeat, ↑ ↓ with Shift for ten, clamps on blur,
|
|
14
|
+
prefix and suffix, grouped display out of focus.
|
|
15
|
+
- **PasswordInput**: show / hide, Caps Lock warning, optional four-step strength meter; `passwordScore()`.
|
|
16
|
+
- **PinInput**: one-time codes and PINs; paste and SMS autofill fill every box; boxed, underline and split looks;
|
|
17
|
+
`onComplete`, mask, error shake.
|
|
18
|
+
- **FileUpload**: dropzone or compact; rejected files listed with the reason; image thumbnails; upload progress and
|
|
19
|
+
retry.
|
|
20
|
+
- **Alert**: info, success, warning, danger, neutral in soft, outline, accent and solid looks; actions, dismiss.
|
|
21
|
+
- **Toaster** and **toast**: sonner with the WAVEINNO look, tone icons in tiles and a tone wash; AppShell uses it.
|
|
22
|
+
- **Spinner**: ring, dots and wave in five sizes; keeps turning slowly under reduced motion.
|
|
23
|
+
- **Kbd**: keys and key combinations.
|
|
24
|
+
|
|
25
|
+
## 0.2.0 — 2026-10-02
|
|
26
|
+
|
|
27
|
+
- **Menu**: an always-open menu for settings sidebars and side panels. Looks: soft, line, raised, gradient, dark, plus
|
|
28
|
+
`panel` and `rail`, the AppShell's module panel and icon rail as standalone menus (as in ParkingOS). The highlight
|
|
29
|
+
slides to the chosen item; MenuSub folds a branch away, opens itself around the chosen item and lights its trigger
|
|
30
|
+
when folded; MenuGroup, MenuSeparator; icon, description, count, badge, shortcut, danger, `asChild` for links;
|
|
31
|
+
arrow keys, Home / End, → / ← on branches.
|
|
32
|
+
- **AppLauncher**: the switch-app button, 3×3 dots that fold into a × while open. Looks: grid, list, spotlight (list
|
|
33
|
+
beside a preview), glass, dark; `trigger="chip"` shows the current app by name; arrow keys move by tile or row.
|
|
34
|
+
|
|
9
35
|
## 0.1.1 — 2026-10-02
|
|
10
36
|
|
|
11
37
|
- DonutChart: no floating tooltip on hover; it covered the centre readout, which already shows the hovered slice.
|
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), NumberInput, PasswordInput (strength, Caps Lock), PinInput (OTP), FileUpload, FormField, Select, Combobox (searchable), Checkbox, RadioGroup and RadioCard, Switch, Slider (range, marks), 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, Alert (five tones, four looks), Toaster and toast, Spinner (ring, dots, wave), Kbd, 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 |
|
|
@@ -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";
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Alert — a message that stays on the page until it is dealt with: a payout
|
|
4
|
+
* on hold, a gate offline, a plan about to lapse. (One that comes and goes is
|
|
5
|
+
* a toast.)
|
|
6
|
+
*
|
|
7
|
+
* Tones: info, success, warning, danger, neutral — each with its own icon, so
|
|
8
|
+
* the tone is never colour alone. Looks:
|
|
9
|
+
* soft — the default: a tinted box. In a page or a form.
|
|
10
|
+
* outline — a hairline in the tone on the surface: calmer, for lists of notices.
|
|
11
|
+
* solid — filled with the tone: the one message nobody may miss.
|
|
12
|
+
* accent — a bar on the left on a raised card: inside cards and drawers.
|
|
13
|
+
* Danger and warning are announced as they appear (role="alert"); the rest are polite.
|
|
14
|
+
*/
|
|
15
|
+
export type AlertTone = "info" | "success" | "warning" | "danger" | "neutral";
|
|
16
|
+
export type AlertVariant = "soft" | "outline" | "solid" | "accent";
|
|
17
|
+
export interface AlertProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "title"> {
|
|
18
|
+
tone?: AlertTone;
|
|
19
|
+
variant?: AlertVariant;
|
|
20
|
+
title?: React.ReactNode;
|
|
21
|
+
/** Replace the tone's icon, or `false` for none. */
|
|
22
|
+
icon?: React.ReactNode | false;
|
|
23
|
+
/** Buttons under the text: "Retry", "Review". */
|
|
24
|
+
action?: React.ReactNode;
|
|
25
|
+
/** Shows a close button; called when it is pressed. */
|
|
26
|
+
onDismiss?: () => void;
|
|
27
|
+
}
|
|
28
|
+
export declare const Alert: React.ForwardRefExoticComponent<AlertProps & React.RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{jsx as e,jsxs as x}from"react/jsx-runtime";import{CircleAlert as m,CircleCheck as f,Info as b,TriangleAlert as _,X as w}from"lucide-react";import*as h from"react";import{cn as t}from"../../lib/utils.js";const y={info:{c:"var(--info)",s:"var(--info-subtle)",icon:e(b,{})},success:{c:"var(--success)",s:"var(--success-subtle)",icon:e(f,{})},warning:{c:"var(--warning)",s:"var(--warning-subtle)",icon:e(_,{})},danger:{c:"var(--danger)",s:"var(--danger-subtle)",icon:e(m,{})},neutral:{c:"var(--muted)",s:"var(--hover)",icon:e(b,{})}},R={soft:{box:"border border-[color-mix(in_srgb,var(--tone)_22%,transparent)] bg-[var(--tone-subtle)]",icon:"text-[var(--tone)]",title:"text-text",body:"text-body"},outline:{box:"border border-[color-mix(in_srgb,var(--tone)_45%,transparent)] bg-raised",icon:"text-[var(--tone)]",title:"text-text",body:"text-body"},solid:{box:"border border-transparent bg-[var(--tone)] text-white shadow-[inset_0_1px_0_rgba(255,255,255,.18),0_8px_20px_-10px_var(--tone)]",icon:"text-white",title:"text-white",body:"text-white/85"},accent:{box:"border border-border bg-raised pl-5 shadow-[var(--lit),0_1px_2px_rgba(5,8,22,.04)] before:absolute before:inset-y-2.5 before:left-2 before:w-[3px] before:rounded-full before:bg-[var(--tone)]",icon:"text-[var(--tone)]",title:"text-text",body:"text-body"}},A=h.forwardRef(({tone:o="info",variant:i="soft",title:n,icon:s,action:l,onDismiss:c,className:v,style:p,children:d,...g},u)=>{const a=y[o],r=R[i];return x("div",{ref:u,role:o==="danger"||o==="warning"?"alert":"status",style:{"--tone":a.c,"--tone-subtle":a.s,...p},className:t("relative flex gap-3 rounded-[12px] p-3.5 text-[13px] animate-[wave-in_220ms_var(--ease)]",r.box,v),...g,children:[s!==!1?e("span",{className:t("mt-px shrink-0 [&_svg]:size-[18px]",r.icon),children:s??a.icon}):null,x("div",{className:"min-w-0 flex-1",children:[n?e("p",{className:t("font-semibold leading-5",r.title),children:n}):null,d?e("div",{className:t("leading-5",n&&"mt-0.5",r.body),children:d}):null,l?e("div",{className:"mt-2.5 flex flex-wrap items-center gap-2",children:l}):null]}),c?e("button",{type:"button",onClick:c,"aria-label":"Dismiss",className:t("-m-1 grid size-7 shrink-0 cursor-pointer place-items-center rounded-[7px] transition-colors [&_svg]:size-3.5",i==="solid"?"text-white/80 hover:bg-white/15 hover:text-white":"text-muted hover:bg-[color-mix(in_srgb,var(--tone)_12%,transparent)] hover:text-text"),children:e(w,{})}):null]})});A.displayName="Alert";export{A as Alert};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* FileUpload — drop files or pick them, and see what was taken and what was not.
|
|
4
|
+
*
|
|
5
|
+
* Files that break `accept` or `maxSize` are not silently dropped: they are
|
|
6
|
+
* listed with the reason, so the person knows to shrink the photo rather than
|
|
7
|
+
* wonder why nothing happened. Images get a thumbnail; everything else a type
|
|
8
|
+
* icon. Pass `progress` (by file name, 0–100) to show uploads moving, and
|
|
9
|
+
* `failed` for the ones to retry.
|
|
10
|
+
*
|
|
11
|
+
* Looks: `dropzone` (default, a dashed area) and `compact` (one row with a
|
|
12
|
+
* button, for a form with other fields).
|
|
13
|
+
*/
|
|
14
|
+
export type FileUploadVariant = "dropzone" | "compact";
|
|
15
|
+
export interface FileUploadProps {
|
|
16
|
+
value?: File[];
|
|
17
|
+
defaultValue?: File[];
|
|
18
|
+
onValueChange?: (files: File[]) => void;
|
|
19
|
+
/** As on <input type="file">: "image/*", ".csv,.xlsx". */
|
|
20
|
+
accept?: string;
|
|
21
|
+
multiple?: boolean;
|
|
22
|
+
/** Bytes. */
|
|
23
|
+
maxSize?: number;
|
|
24
|
+
maxFiles?: number;
|
|
25
|
+
variant?: FileUploadVariant;
|
|
26
|
+
title?: React.ReactNode;
|
|
27
|
+
hint?: React.ReactNode;
|
|
28
|
+
/** Upload progress by file name, 0–100. */
|
|
29
|
+
progress?: Record<string, number>;
|
|
30
|
+
/** Names of files whose upload failed; they get a retry button. */
|
|
31
|
+
failed?: string[];
|
|
32
|
+
onRetry?: (file: File) => void;
|
|
33
|
+
disabled?: boolean;
|
|
34
|
+
className?: string;
|
|
35
|
+
}
|
|
36
|
+
export declare function FileUpload({ value, defaultValue, onValueChange, accept, multiple, maxSize, maxFiles, variant, title, hint, progress, failed, onRetry, disabled, className, }: FileUploadProps): React.JSX.Element;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{Fragment as Q,jsx as t,jsxs as n}from"react/jsx-runtime";import{CloudUpload as j,File as A,FileImage as H,FileSpreadsheet as K,FileText as X,RotateCcw as q,X as E}from"lucide-react";import*as c from"react";import{cn as x}from"../../lib/utils.js";const k=r=>r<1024*1024?`${Math.max(1,Math.round(r/1024))} KB`:`${(r/1024/1024).toFixed(1)} MB`;function G(r,i){return i?i.split(",").some(u=>{const a=u.trim().toLowerCase();return a.startsWith(".")?r.name.toLowerCase().endsWith(a):a.endsWith("/*")?r.type.startsWith(a.slice(0,-1)):r.type===a}):!0}function O({file:r}){const i=r.name.toLowerCase();return r.type.startsWith("image/")?t(H,{}):/\.(csv|xlsx?|ods)$/.test(i)?t(K,{}):/\.(pdf|docx?|txt|md)$/.test(i)?t(X,{}):t(A,{})}function J({file:r}){const[i,u]=c.useState(null);return c.useEffect(()=>{if(!r.type.startsWith("image/"))return;const a=URL.createObjectURL(r);return u(a),()=>URL.revokeObjectURL(a)},[r]),t("span",{className:"grid size-10 shrink-0 place-items-center overflow-hidden rounded-[9px] border border-border bg-surface text-muted [&_svg]:size-[18px]",children:i?t("img",{src:i,alt:"",className:"size-full object-cover"}):t(O,{file:r})})}function ee({value:r,defaultValue:i=[],onValueChange:u,accept:a,multiple:h=!0,maxSize:m,maxFiles:p,variant:M="dropzone",title:z,hint:W,progress:I,failed:T,onRetry:F,disabled:b,className:B}){const[V,P]=c.useState(i),g=r??V,[R,_]=c.useState([]),[f,y]=c.useState(!1),N=c.useRef(null),v=c.useRef(0),$=e=>{r===void 0&&P(e),u?.(e)},C=e=>{if(!e)return;const s=[],o=[];for(const d of Array.from(e))G(d,a)?m&&d.size>m?o.push({file:d,reason:`Over ${k(m)}`}):s.push(d):o.push({file:d,reason:"Not an accepted type"});let l=h?[...g,...s.filter(d=>!g.some(L=>L.name===d.name&&L.size===d.size))]:s.slice(0,1);p&&l.length>p&&(l.slice(p).forEach(d=>o.push({file:d,reason:`Only ${p} file${p>1?"s":""}`})),l=l.slice(0,p)),_(o),$(l)},S=e=>$(g.filter(s=>s!==e)),w=W??[a?.split(",").map(e=>e.trim().replace(/^\./,"").toUpperCase()).join(", "),m?`up to ${k(m)}`:null].filter(Boolean).join(" \xB7 "),D=t("input",{ref:N,type:"file",className:"sr-only",tabIndex:-1,accept:a,multiple:h,disabled:b,onChange:e=>{C(e.target.files),e.target.value=""}}),U={onDragEnter:e=>{e.preventDefault(),v.current+=1,y(!0)},onDragOver:e=>e.preventDefault(),onDragLeave:()=>{v.current-=1,v.current<=0&&y(!1)},onDrop:e=>{e.preventDefault(),v.current=0,y(!1),b||C(e.dataTransfer.files)}};return n("div",{className:x("w-full space-y-2.5",B),children:[M==="dropzone"?n("button",{type:"button",disabled:b,onClick:()=>N.current?.click(),...U,className:x("group relative flex w-full cursor-pointer flex-col items-center justify-center gap-2 overflow-hidden rounded-[14px] border-[1.5px] border-dashed border-border2 bg-surface px-6 py-8 text-center outline-none","transition-[border-color,background-color,box-shadow] duration-200 hover:border-primary/50 hover:bg-primary-subtle/30 focus-visible:ring-2 focus-visible:ring-primary/35",f&&"border-primary bg-primary-subtle/50 shadow-[0_0_0_4px_var(--primary-subtle)]",b&&"cursor-not-allowed opacity-50"),children:[t("span",{className:x("grid size-12 place-items-center rounded-[14px] border border-border bg-raised text-primary shadow-[var(--lit),0_4px_12px_-6px_rgba(79,70,229,.45)] transition-transform duration-300 ease-[cubic-bezier(.3,1.5,.5,1)] [&_svg]:size-[22px]",f?"-translate-y-1 scale-110":"group-hover:-translate-y-0.5"),children:t(j,{})}),t("span",{className:"text-[13.5px] font-semibold text-text",children:f?"Drop to add":z??n(Q,{children:[n("span",{className:"text-primary",children:["Choose ",h?"files":"a file"]})," or drag ",h?"them":"it"," here"]})}),w?t("span",{className:"text-[12px] text-muted",children:w}):null,D]}):n("div",{...U,className:x("flex items-center gap-3 rounded-ctl border border-dashed border-border2 bg-surface p-2 pl-3 transition-colors",f&&"border-primary bg-primary-subtle/50"),children:[t(j,{className:"size-4 shrink-0 text-muted"}),t("span",{className:"min-w-0 flex-1 truncate text-[12.5px] text-muted",children:f?"Drop to add":z??w??"Drag files here"}),t("button",{type:"button",disabled:b,onClick:()=>N.current?.click(),className:"h-7 shrink-0 cursor-pointer rounded-[7px] border border-border2 bg-raised px-2.5 text-[12px] font-medium text-text shadow-[0_1px_1px_rgba(5,8,22,.05)] transition-colors hover:bg-hover disabled:cursor-not-allowed disabled:opacity-50",children:"Browse"}),D]}),g.length||R.length?n("ul",{className:"space-y-1.5",children:[g.map(e=>{const s=I?.[e.name],o=T?.includes(e.name),l=s!==void 0&&s<100&&!o;return n("li",{className:"flex items-center gap-3 rounded-[11px] border border-border bg-raised p-2 pr-2.5 animate-[wave-in_220ms_var(--ease)]",children:[t(J,{file:e}),n("span",{className:"min-w-0 flex-1",children:[t("span",{className:"block truncate text-[13px] font-medium text-text",children:e.name}),n("span",{className:x("num flex items-center gap-1.5 text-[11.5px]",o?"text-danger":"text-muted"),children:[k(e.size),o?" \xB7 Upload failed":s===100?" \xB7 Uploaded":l?` \xB7 ${Math.round(s)}%`:null]}),l?t("span",{className:"mt-1.5 block h-1 overflow-hidden rounded-full bg-hover",children:t("span",{className:"block h-full rounded-full bg-[linear-gradient(90deg,#1f45ea,#7560fd)] transition-[width] duration-300",style:{width:`${s}%`}})}):null]}),o&&F?t("button",{type:"button",onClick:()=>F(e),"aria-label":`Retry ${e.name}`,className:"grid size-7 cursor-pointer place-items-center rounded-[7px] text-danger transition-colors hover:bg-danger-subtle [&_svg]:size-3.5",children:t(q,{})}):null,t("button",{type:"button",onClick:()=>S(e),"aria-label":`Remove ${e.name}`,className:"grid size-7 cursor-pointer place-items-center rounded-[7px] text-muted transition-colors hover:bg-hover hover:text-text [&_svg]:size-3.5",children:t(E,{})})]},`${e.name}-${e.size}-${e.lastModified}`)}),R.map(({file:e,reason:s})=>n("li",{className:"flex items-center gap-3 rounded-[11px] border border-danger/25 bg-danger-subtle/50 p-2 pr-2.5",role:"alert",children:[t("span",{className:"grid size-10 shrink-0 place-items-center rounded-[9px] border border-danger/20 bg-raised text-danger [&_svg]:size-[18px]",children:t(O,{file:e})}),n("span",{className:"min-w-0 flex-1",children:[t("span",{className:"block truncate text-[13px] font-medium text-text",children:e.name}),n("span",{className:"text-[11.5px] text-danger",children:[s," \xB7 not added"]})]}),t("button",{type:"button",onClick:()=>_(o=>o.filter(l=>l.file!==e)),"aria-label":`Dismiss ${e.name}`,className:"grid size-7 cursor-pointer place-items-center rounded-[7px] text-muted transition-colors hover:bg-raised hover:text-text [&_svg]:size-3.5",children:t(E,{})})]},`x-${e.name}-${e.size}`))]}):null]})}export{ee as FileUpload};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/** Primitives: small, unopinionated building blocks (Radix underneath where it helps). */
|
|
2
|
+
export * from "./alert";
|
|
2
3
|
export * from "./avatar";
|
|
3
4
|
export * from "./badge";
|
|
4
5
|
export * from "./button";
|
|
@@ -7,20 +8,30 @@ export * from "./card";
|
|
|
7
8
|
export * from "./checkbox";
|
|
8
9
|
export * from "./combobox";
|
|
9
10
|
export * from "./confirm-dialog";
|
|
11
|
+
export * from "./date-picker";
|
|
10
12
|
export * from "./dialog";
|
|
11
13
|
export * from "./dropdown-menu";
|
|
14
|
+
export * from "./file-upload";
|
|
12
15
|
export * from "./form-field";
|
|
13
16
|
export * from "./input";
|
|
17
|
+
export * from "./kbd";
|
|
14
18
|
export * from "./label";
|
|
19
|
+
export * from "./menu";
|
|
20
|
+
export * from "./number-input";
|
|
21
|
+
export * from "./password-input";
|
|
22
|
+
export * from "./pin-input";
|
|
15
23
|
export * from "./popover";
|
|
16
|
-
export * from "./date-picker";
|
|
17
24
|
export * from "./progress";
|
|
25
|
+
export * from "./radio-group";
|
|
18
26
|
export * from "./segmented";
|
|
19
27
|
export * from "./select";
|
|
20
28
|
export * from "./separator";
|
|
21
29
|
export * from "./sheet";
|
|
22
30
|
export * from "./skeleton";
|
|
31
|
+
export * from "./slider";
|
|
32
|
+
export * from "./spinner";
|
|
23
33
|
export * from "./switch";
|
|
24
34
|
export * from "./tabs";
|
|
25
35
|
export * from "./time-picker";
|
|
36
|
+
export * from "./toast";
|
|
26
37
|
export * from "./tooltip";
|
|
@@ -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"./
|
|
1
|
+
export*from"./alert.js";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"./date-picker.js";export*from"./dialog.js";export*from"./dropdown-menu.js";export*from"./file-upload.js";export*from"./form-field.js";export*from"./input.js";export*from"./kbd.js";export*from"./label.js";export*from"./menu.js";export*from"./number-input.js";export*from"./password-input.js";export*from"./pin-input.js";export*from"./popover.js";export*from"./progress.js";export*from"./radio-group.js";export*from"./segmented.js";export*from"./select.js";export*from"./separator.js";export*from"./sheet.js";export*from"./skeleton.js";export*from"./slider.js";export*from"./spinner.js";export*from"./switch.js";export*from"./tabs.js";export*from"./time-picker.js";export*from"./toast.js";export*from"./tooltip.js";
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Kbd — a key, drawn as a key: a lip along the bottom, so "⌘K" reads as
|
|
4
|
+
* something to press and not as text. Pass `keys` for a combination; they
|
|
5
|
+
* are set apart with a thin joiner, and read out with "+".
|
|
6
|
+
*/
|
|
7
|
+
export declare function Kbd({ keys, size, className, children, }: {
|
|
8
|
+
/** A combination: ["⌘", "K"]. */
|
|
9
|
+
keys?: string[];
|
|
10
|
+
size?: "sm" | "md";
|
|
11
|
+
className?: string;
|
|
12
|
+
children?: React.ReactNode;
|
|
13
|
+
}): React.JSX.Element;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as e,jsxs as p}from"react/jsx-runtime";import*as o from"react";import{cn as r}from"../../lib/utils.js";function c({keys:a,size:i="md",className:t,children:s}){const n=r("inline-grid place-items-center rounded-[6px] border border-border2 border-b-[2.5px] bg-raised font-sans font-medium text-body","shadow-[inset_0_-1px_0_var(--border),0_1px_1px_rgba(5,8,22,.06)]",i==="sm"?"h-5 min-w-5 px-1 text-[10.5px]":"h-6 min-w-6 px-1.5 text-[11.5px]");return a?e("kbd",{className:r("inline-flex items-center gap-1",t),"aria-label":a.join(" + "),children:a.map((m,d)=>p(o.Fragment,{children:[d>0?e("span",{"aria-hidden":!0,className:"text-[10px] text-placeholder",children:"+"}):null,e("kbd",{className:n,"aria-hidden":!0,children:m})]},d))}):e("kbd",{className:r(n,t),children:s})}export{c as Kbd};
|
|
@@ -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};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { type InputState } from "./input";
|
|
3
|
+
/**
|
|
4
|
+
* NumberInput — a number you type or step: a rate, a capacity, a quantity.
|
|
5
|
+
*
|
|
6
|
+
* ↑ / ↓ step it (Shift for ten steps), the buttons repeat while held, and the
|
|
7
|
+
* value is clamped to min / max when the field is left, not while typing, so
|
|
8
|
+
* "1" on the way to "150" is never snapped to the minimum. Out of focus it
|
|
9
|
+
* shows grouped ("12,500"); in focus, the raw number, ready to edit.
|
|
10
|
+
*
|
|
11
|
+
* `stepper`: "split" puts − and + either side (counts, quantities), "stack"
|
|
12
|
+
* puts small chevrons on the right (forms full of numbers), "none" hides them.
|
|
13
|
+
*/
|
|
14
|
+
export interface NumberInputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "value" | "defaultValue" | "onChange" | "type" | "min" | "max" | "step" | "prefix"> {
|
|
15
|
+
value?: number | null;
|
|
16
|
+
defaultValue?: number | null;
|
|
17
|
+
onValueChange?: (value: number | null) => void;
|
|
18
|
+
min?: number;
|
|
19
|
+
max?: number;
|
|
20
|
+
step?: number;
|
|
21
|
+
/** Decimal places kept. Default: as many as `step` has. */
|
|
22
|
+
precision?: number;
|
|
23
|
+
/** Inside the box, before the number: "৳", "+880". */
|
|
24
|
+
prefix?: React.ReactNode;
|
|
25
|
+
/** After it: "min", "cars", "%". */
|
|
26
|
+
suffix?: React.ReactNode;
|
|
27
|
+
stepper?: "split" | "stack" | "none";
|
|
28
|
+
state?: InputState;
|
|
29
|
+
}
|
|
30
|
+
export declare const NumberInput: React.ForwardRefExoticComponent<NumberInputProps & React.RefAttributes<HTMLInputElement>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{jsx as r,jsxs as k}from"react/jsx-runtime";import{ChevronDown as j,ChevronUp as q,Minus as G,Plus as J}from"lucide-react";import*as s from"react";import{cn as u}from"../../lib/utils.js";import{inputClass as Q}from"./input.js";const X=l=>(String(l).split(".")[1]??"").length,Y=s.forwardRef(({value:l,defaultValue:T=null,onValueChange:P,min:i=-1/0,max:c=1/0,step:v=1,precision:z,prefix:D,suffix:M,stepper:d="split",state:h="default",className:A,disabled:b,onBlur:H,onFocus:W,onKeyDown:_,...N},K)=>{const m=z??X(v),[O,V]=s.useState(T),t=l!==void 0?l:O,[B,S]=s.useState(!1),[$,y]=s.useState(""),R=e=>Number(e.toFixed(m)),F=e=>Math.min(c,Math.max(i,e)),w=e=>{l===void 0&&V(e),P?.(e)},L=B?$:t===null?"":t.toLocaleString(void 0,{minimumFractionDigits:m,maximumFractionDigits:m}),I=s.useRef(t);I.current=t;const p=s.useCallback((e,n=1)=>{const a=I.current??(i>-1/0?i:0),f=F(R(a+e*v*n));I.current=f,w(f),y(String(f))},[i,c,v,m,l,P]),U=s.useRef(void 0),o=()=>window.clearTimeout(U.current),x=e=>n=>{if(n.button!==0)return;n.preventDefault(),p(e);const a=f=>{U.current=window.setTimeout(()=>{p(e),a(Math.max(40,f*.85))},f)};a(400)};s.useEffect(()=>o,[]);const C=t!==null&&t<=i,E=t!==null&&t>=c,g="grid shrink-0 cursor-pointer place-items-center text-muted transition-colors hover:bg-hover hover:text-text active:bg-[var(--selected)] disabled:pointer-events-none disabled:opacity-35";return k("div",{className:u(Q,"flex items-stretch overflow-hidden p-0 focus-within:border-primary focus-within:ring-2 focus-within:ring-primary/20",h==="error"&&"border-danger bg-danger-subtle/30 focus-within:border-danger focus-within:ring-danger/20 wm-shake",h==="warning"&&"border-warning focus-within:border-warning focus-within:ring-warning/20",h==="success"&&"border-success focus-within:border-success focus-within:ring-success/20",b&&"cursor-not-allowed bg-hover",A),children:[d==="split"?r("button",{type:"button",tabIndex:-1,"aria-label":"Decrease",disabled:b||C,onPointerDown:x(-1),onPointerUp:o,onPointerLeave:o,className:u(g,"w-9 border-r border-border [&_svg]:size-3.5"),children:r(G,{strokeWidth:2.4})}):null,k("div",{className:u("flex min-w-0 flex-1 items-center gap-1.5 px-3",d==="split"&&"justify-center"),children:[D?r("span",{className:"shrink-0 text-[13px] text-muted",children:D}):null,r("input",{ref:K,type:"text",inputMode:m>0||i<0?"decimal":"numeric",role:"spinbutton","aria-valuenow":t??void 0,"aria-valuemin":Number.isFinite(i)?i:void 0,"aria-valuemax":Number.isFinite(c)?c:void 0,"aria-invalid":h==="error"?!0:N["aria-invalid"],disabled:b,value:L,style:d==="split"?{width:`${Math.max(2,(L||String(N.placeholder??"")).length+1)}ch`}:void 0,className:u("num h-full min-w-0 bg-transparent text-[13px] text-text outline-none placeholder:text-placeholder disabled:cursor-not-allowed",d==="split"?"max-w-full flex-none text-center":"w-full"),onFocus:e=>{y(t===null?"":String(t)),S(!0),W?.(e)},onChange:e=>{const n=e.target.value.replace(/[^\d.,-]/g,"");y(n);const a=Number(n.replace(/,/g,""));n===""||n==="-"?w(null):Number.isNaN(a)||w(a)},onBlur:e=>{if(S(!1),t!==null){const n=F(R(t));n!==t&&w(n)}H?.(e)},onKeyDown:e=>{_?.(e),!e.defaultPrevented&&(e.key==="ArrowUp"||e.key==="ArrowDown"?(e.preventDefault(),p(e.key==="ArrowUp"?1:-1,e.shiftKey?10:1)):e.key==="Home"&&Number.isFinite(i)?(e.preventDefault(),p(-1,1/0)):e.key==="End"&&Number.isFinite(c)&&(e.preventDefault(),p(1,1/0)))},...N}),M?r("span",{className:"shrink-0 text-[12.5px] text-muted",children:M}):null]}),d==="split"?r("button",{type:"button",tabIndex:-1,"aria-label":"Increase",disabled:b||E,onPointerDown:x(1),onPointerUp:o,onPointerLeave:o,className:u(g,"w-9 border-l border-border [&_svg]:size-3.5"),children:r(J,{strokeWidth:2.4})}):d==="stack"?k("div",{className:"flex w-7 shrink-0 flex-col border-l border-border [&_svg]:size-3",children:[r("button",{type:"button",tabIndex:-1,"aria-label":"Increase",disabled:b||E,onPointerDown:x(1),onPointerUp:o,onPointerLeave:o,className:u(g,"flex-1 border-b border-border"),children:r(q,{strokeWidth:2.4})}),r("button",{type:"button",tabIndex:-1,"aria-label":"Decrease",disabled:b||C,onPointerDown:x(-1),onPointerUp:o,onPointerLeave:o,className:u(g,"flex-1"),children:r(j,{strokeWidth:2.4})})]}):null]})});Y.displayName="NumberInput";export{Y as NumberInput};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { type InputState } from "./input";
|
|
3
|
+
/**
|
|
4
|
+
* PasswordInput — a password field that can be read back before it is sent.
|
|
5
|
+
*
|
|
6
|
+
* The eye shows and hides it. Caps Lock is called out while it is on, because
|
|
7
|
+
* a password that "will not work" is most often typed in capitals. `strength`
|
|
8
|
+
* adds a four-step meter under the field for new passwords (not for sign-in,
|
|
9
|
+
* where it only gets in the way).
|
|
10
|
+
*/
|
|
11
|
+
export interface PasswordInputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "type"> {
|
|
12
|
+
state?: InputState;
|
|
13
|
+
/** A meter under the field: for choosing a new password. */
|
|
14
|
+
strength?: boolean;
|
|
15
|
+
}
|
|
16
|
+
/** 0–4. Length counts most; each kind of character adds one. */
|
|
17
|
+
export declare function passwordScore(pw: string): number;
|
|
18
|
+
export declare const PasswordInput: React.ForwardRefExoticComponent<PasswordInputProps & React.RefAttributes<HTMLInputElement>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{jsx as t,jsxs as p}from"react/jsx-runtime";import{ArrowBigUpDash as k,Eye as C,EyeOff as L}from"lucide-react";import*as i from"react";import{cn as u}from"../../lib/utils.js";import{Input as E}from"./input.js";const M=[{label:"Too short",tone:"bg-danger",text:"text-danger"},{label:"Weak",tone:"bg-danger",text:"text-danger"},{label:"Fair",tone:"bg-warning",text:"text-warning"},{label:"Good",tone:"bg-[#4f9d2a]",text:"text-[#4f9d2a]"},{label:"Strong",tone:"bg-success",text:"text-success"}];function P(s){if(s.length<6)return 0;const a=[/[a-z]/,/[A-Z]/,/\d/,/[^A-Za-z0-9]/].filter(d=>d.test(s)).length;let n=s.length>=12?2:s.length>=8?1:0;return n+=Math.max(0,a-1),Math.min(4,Math.max(1,n))}const z=i.forwardRef(({className:s,strength:a,onKeyUp:n,onKeyDown:d,onBlur:w,onChange:h,value:o,defaultValue:m,...v},y)=>{const[r,S]=i.useState(!1),[g,f]=i.useState(!1),[N,I]=i.useState(String(m??"")),l=o!==void 0?String(o):N,x=P(l),c=M[x],b=e=>f(e.getModifierState?.("CapsLock")??!1);return p("div",{className:u("w-full",s),children:[t(E,{ref:y,type:r?"text":"password",autoComplete:a?"new-password":"current-password",spellCheck:!1,value:o,defaultValue:m,onChange:e=>{o===void 0&&I(e.target.value),h?.(e)},onKeyUp:e=>{b(e),n?.(e)},onKeyDown:e=>{b(e),d?.(e)},onBlur:e=>{f(!1),w?.(e)},trailing:p("span",{className:"flex items-center gap-1",children:[g?p("span",{title:"Caps Lock is on",className:"grid size-6 place-items-center rounded-[6px] bg-warning-subtle text-warning animate-[wm-pop_180ms_var(--ease)] [&_svg]:!size-3.5",children:[t(k,{}),t("span",{className:"sr-only",children:"Caps Lock is on"})]}):null,t("button",{type:"button",onClick:()=>S(e=>!e),"aria-label":r?"Hide password":"Show password","aria-pressed":r,className:"grid size-7 cursor-pointer place-items-center rounded-[6px] text-muted transition-colors hover:bg-hover hover:text-text",children:r?t(L,{}):t(C,{})})]}),className:g?"pr-[72px]":"pr-11",...v}),a?p("div",{className:"mt-2 flex items-center gap-3","aria-live":"polite",children:[t("div",{className:"flex flex-1 gap-1",children:[1,2,3,4].map(e=>t("span",{className:"h-1 flex-1 overflow-hidden rounded-full bg-hover",children:t("span",{className:u("block h-full rounded-full transition-[width,background-color] duration-300",c.tone),style:{width:l&&x>=e?"100%":"0%"}})},e))}),t("span",{className:u("w-16 text-right text-[11.5px] font-medium",l?c.text:"text-muted"),children:l?c.label:"Strength"})]}):null]})});z.displayName="PasswordInput";export{z as PasswordInput,P as passwordScore};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* PinInput — a one-time code or a PIN, one box per character.
|
|
4
|
+
*
|
|
5
|
+
* Typing moves to the next box, Backspace to the previous, and a pasted code
|
|
6
|
+
* (or the SMS code the phone offers) fills every box at once. `onComplete`
|
|
7
|
+
* fires when the last box is filled, so a verify screen needs no button.
|
|
8
|
+
*
|
|
9
|
+
* Looks: `boxed` (default), `underline` (quiet, on a card), `split` (two
|
|
10
|
+
* halves with a dash between: 123–456, the way people read codes aloud).
|
|
11
|
+
*/
|
|
12
|
+
export type PinInputVariant = "boxed" | "underline" | "split";
|
|
13
|
+
export interface PinInputProps {
|
|
14
|
+
length?: number;
|
|
15
|
+
value?: string;
|
|
16
|
+
defaultValue?: string;
|
|
17
|
+
onValueChange?: (value: string) => void;
|
|
18
|
+
onComplete?: (value: string) => void;
|
|
19
|
+
/** `numeric` (default) brings up the number pad; `alphanumeric` takes letters too. */
|
|
20
|
+
type?: "numeric" | "alphanumeric";
|
|
21
|
+
/** Dots instead of characters: PINs, not one-time codes. */
|
|
22
|
+
mask?: boolean;
|
|
23
|
+
variant?: PinInputVariant;
|
|
24
|
+
size?: "md" | "lg";
|
|
25
|
+
error?: boolean;
|
|
26
|
+
disabled?: boolean;
|
|
27
|
+
autoFocus?: boolean;
|
|
28
|
+
/** Read aloud as the group's name. */
|
|
29
|
+
"aria-label"?: string;
|
|
30
|
+
className?: string;
|
|
31
|
+
}
|
|
32
|
+
export declare function PinInput({ length, value, defaultValue, onValueChange, onComplete, type, mask, variant, size, error, disabled, autoFocus, "aria-label": ariaLabel, className, }: PinInputProps): React.JSX.Element;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{jsx as u,jsxs as z}from"react/jsx-runtime";import*as c from"react";import{cn as y}from"../../lib/utils.js";const N={boxed:"rounded-[10px] border border-border2 bg-raised shadow-[inset_0_1px_1px_rgba(5,8,22,.05)] focus:border-primary focus:ring-[3px] focus:ring-primary/20 data-[filled]:border-border2",split:"rounded-[10px] border border-border2 bg-raised shadow-[inset_0_1px_1px_rgba(5,8,22,.05)] focus:border-primary focus:ring-[3px] focus:ring-primary/20",underline:"rounded-none border-0 border-b-2 border-border2 bg-transparent focus:border-primary"};function O({length:n=6,value:p,defaultValue:w="",onValueChange:h,onComplete:v,type:f="numeric",mask:_,variant:b="boxed",size:P="md",error:l,disabled:k,autoFocus:D,"aria-label":I="Verification code",className:C}){const[V,A]=c.useState(w.slice(0,n)),i=(p??V).slice(0,n),m=c.useRef([]),F=f==="numeric"?/\d/:/[a-z0-9]/i,g=t=>{p===void 0&&A(t),h?.(t),t.length===n&&!t.includes(" ")&&v?.(t)},d=t=>{const e=m.current[Math.max(0,Math.min(n-1,t))];e?.focus(),e?.select()},x=(t,e)=>{const o=[...e].filter(a=>F.test(a)).map(a=>a.toUpperCase());if(!o.length)return;const r=Array.from({length:n},(a,s)=>i[s]??" ");o.slice(0,n-t).forEach((a,s)=>r[t+s]=a),g(r.join("").trimEnd()),d(t+o.length)},M=Math.ceil(n/2),R=P==="lg";return u("div",{role:"group","aria-label":I,className:y("flex items-center gap-2",l&&"wm-shake",C),children:Array.from({length:n},(t,e)=>{const o=i[e]&&i[e]!==" "?i[e]:"";return z(c.Fragment,{children:[b==="split"&&e===M?u("span",{"aria-hidden":!0,className:"h-0.5 w-3 rounded-full bg-border2"}):null,u("input",{ref:r=>{m.current[e]=r},"aria-label":`Character ${e+1} of ${n}`,inputMode:f==="numeric"?"numeric":"text",autoComplete:e===0?"one-time-code":"off",autoFocus:D&&e===0,disabled:k,type:_?"password":"text",value:o,"data-filled":o?"":void 0,"aria-invalid":l||void 0,onFocus:r=>r.target.select(),onChange:r=>{const a=r.target.value.replace(o,"")||r.target.value;x(e,a.length>1?a:a.slice(-1))},onPaste:r=>{r.preventDefault(),x(e,r.clipboardData.getData("text"))},onKeyDown:r=>{if(r.key==="Backspace"){r.preventDefault();const a=Array.from({length:n},(B,E)=>i[E]??" "),s=o?e:e-1;if(s<0)return;a[s]=" ",g(a.join("").trimEnd()),d(s)}else r.key==="ArrowLeft"?(r.preventDefault(),d(e-1)):r.key==="ArrowRight"&&(r.preventDefault(),d(e+1))},className:y("num text-center font-semibold text-text caret-primary outline-none transition-[border-color,box-shadow,transform] duration-150","disabled:cursor-not-allowed disabled:opacity-50 data-[filled]:animate-[wm-pop_160ms_var(--ease)]",R?"h-14 w-12 text-[22px]":"h-11 w-10 text-[18px]",N[b],l&&"border-danger focus:border-danger focus:ring-danger/20 data-[filled]:border-danger")})]},e)})})}export{O as PinInput};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
/**
|
|
4
|
+
* RadioGroup — one choice out of a few, all of them in view. Past five or six
|
|
5
|
+
* options it becomes a Select: a wall of radios is a list nobody reads.
|
|
6
|
+
*
|
|
7
|
+
* Two kinds of item, one group:
|
|
8
|
+
* RadioGroupItem — a dot and a label, with a line under it if it needs one. Forms.
|
|
9
|
+
* RadioCard — the whole card is the target: icon, title, description, and
|
|
10
|
+
* a price or badge on the right. Plans, payment methods, vehicle types.
|
|
11
|
+
* Arrow keys move the choice, as with native radios.
|
|
12
|
+
*/
|
|
13
|
+
export declare const RadioGroup: React.ForwardRefExoticComponent<Omit<RadioGroupPrimitive.RadioGroupProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
14
|
+
export interface RadioGroupItemProps extends React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item> {
|
|
15
|
+
label?: React.ReactNode;
|
|
16
|
+
/** A muted line under the label. */
|
|
17
|
+
description?: React.ReactNode;
|
|
18
|
+
}
|
|
19
|
+
export declare const RadioGroupItem: React.ForwardRefExoticComponent<RadioGroupItemProps & React.RefAttributes<HTMLButtonElement>>;
|
|
20
|
+
export interface RadioCardProps extends Omit<React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item>, "title"> {
|
|
21
|
+
title: React.ReactNode;
|
|
22
|
+
description?: React.ReactNode;
|
|
23
|
+
icon?: React.ReactNode;
|
|
24
|
+
/** On the right: a price, a time, a badge. */
|
|
25
|
+
aside?: React.ReactNode;
|
|
26
|
+
}
|
|
27
|
+
export declare const RadioCard: React.ForwardRefExoticComponent<RadioCardProps & React.RefAttributes<HTMLButtonElement>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{jsx as e,jsxs as s}from"react/jsx-runtime";import*as n from"@radix-ui/react-radio-group";import{Check as m}from"lucide-react";import*as l from"react";import{cn as i}from"../../lib/utils.js";const u=l.forwardRef(({className:o,orientation:t,...a},r)=>e(n.Root,{ref:r,orientation:t,className:i(t==="horizontal"?"flex flex-wrap gap-x-5 gap-y-2":"grid gap-2.5",o),...a}));u.displayName="RadioGroup";const b="peer grid size-4 shrink-0 cursor-pointer place-items-center rounded-full border border-border2 bg-raised outline-none shadow-[inset_0_1px_1px_rgba(5,8,22,.06)] transition-[background-color,border-color,box-shadow] duration-150 hover:border-primary/60 focus-visible:ring-2 focus-visible:ring-primary/30 data-[state=checked]:border-primary data-[state=checked]:bg-primary data-[state=checked]:shadow-[0_1px_3px_rgba(79,70,229,.4)] disabled:cursor-not-allowed disabled:opacity-50",x=l.forwardRef(({className:o,label:t,description:a,disabled:r,...d},c)=>{const p=e(n.Item,{ref:c,disabled:r,className:i(b,t?"mt-0.5":null,!t&&o),...d,children:e(n.Indicator,{className:"size-1.5 rounded-full bg-white animate-[wm-pop_180ms_var(--ease)]"})});return t?s("label",{className:i("group flex cursor-pointer items-start gap-2.5",r&&"cursor-not-allowed opacity-60",o),children:[p,s("span",{className:"min-w-0",children:[e("span",{className:"block text-[13px] font-medium leading-5 text-text",children:t}),a?e("span",{className:"block text-[12px] leading-[17px] text-muted",children:a}):null]})]}):p});x.displayName="RadioGroupItem";const R=l.forwardRef(({className:o,title:t,description:a,icon:r,aside:d,...c},p)=>s(n.Item,{ref:p,className:i("group relative flex w-full cursor-pointer items-start gap-3 rounded-[12px] border border-border bg-raised p-3.5 pr-10 text-left outline-none","transition-[border-color,background-color,box-shadow] duration-200 hover:border-border2","focus-visible:ring-2 focus-visible:ring-primary/35","data-[state=checked]:border-primary data-[state=checked]:bg-[linear-gradient(180deg,var(--primary-subtle),transparent_120%)] data-[state=checked]:shadow-[0_0_0_3px_var(--primary-subtle),0_6px_16px_-8px_rgba(79,70,229,.35)]","disabled:cursor-not-allowed disabled:opacity-50",o),...c,children:[r?e("span",{className:i("grid size-9 shrink-0 place-items-center rounded-[10px] border border-border bg-surface text-muted transition-colors duration-200 [&_svg]:size-[18px]","group-data-[state=checked]:border-primary/25 group-data-[state=checked]:bg-raised group-data-[state=checked]:text-primary"),children:r}):null,s("span",{className:"min-w-0 flex-1",children:[s("span",{className:"flex items-center justify-between gap-3",children:[e("span",{className:"truncate text-[13.5px] font-semibold text-text",children:t}),d?e("span",{className:"num shrink-0 text-[13px] font-semibold text-text",children:d}):null]}),a?e("span",{className:"mt-0.5 block text-[12px] leading-[17px] text-muted",children:a}):null]}),e("span",{"aria-hidden":!0,className:i("absolute right-3.5 top-3.5 grid size-[18px] place-items-center rounded-full border border-border2 bg-raised text-white transition-[background-color,border-color] duration-200","group-data-[state=checked]:border-primary group-data-[state=checked]:bg-primary"),children:e(m,{className:"size-3 scale-0 transition-transform duration-200 ease-[cubic-bezier(.3,1.6,.5,1)] group-data-[state=checked]:scale-100",strokeWidth:3.2})})]}));R.displayName="RadioCard";export{R as RadioCard,u as RadioGroup,x as RadioGroupItem};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as SliderPrimitive from "@radix-ui/react-slider";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
/**
|
|
4
|
+
* Slider — a value along a range, or a range itself (pass two values).
|
|
5
|
+
*
|
|
6
|
+
* The value rides above the thumb while it is held, hovered or focused, so the
|
|
7
|
+
* number is read where the eye already is; `showValue="always"` keeps it up.
|
|
8
|
+
* `marks` put ticks under the track (with labels when given) for the stops
|
|
9
|
+
* that mean something: 0, the free hour, the cap. `variant="gradient"` fills
|
|
10
|
+
* the range in the brand gradient, for the one slider a screen is about.
|
|
11
|
+
*/
|
|
12
|
+
export type SliderVariant = "default" | "gradient";
|
|
13
|
+
export interface SliderProps extends Omit<React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root>, "children"> {
|
|
14
|
+
variant?: SliderVariant;
|
|
15
|
+
/** Ticks under the track. */
|
|
16
|
+
marks?: {
|
|
17
|
+
value: number;
|
|
18
|
+
label?: React.ReactNode;
|
|
19
|
+
}[];
|
|
20
|
+
/** The value over the thumb: while used (default), always, or never. */
|
|
21
|
+
showValue?: "active" | "always" | "never";
|
|
22
|
+
format?: (value: number) => string;
|
|
23
|
+
}
|
|
24
|
+
export declare const Slider: React.ForwardRefExoticComponent<SliderProps & React.RefAttributes<HTMLSpanElement>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{jsx as t,jsxs as v}from"react/jsx-runtime";import*as n from"@radix-ui/react-slider";import*as f from"react";import{cn as r}from"../../lib/utils.js";const N=18,P=f.forwardRef(({className:g,variant:h="default",marks:i,showValue:s="active",format:x=p=>p.toLocaleString(),value:u,defaultValue:d,onValueChange:y,min:o=0,max:b=100,...c},_)=>{const[p,w]=f.useState(d??[o]),a=u??p,m=e=>(e-o)/(b-o||1)*100,S=e=>`calc(${m(e)}% + ${N/2*(1-m(e)/50)}px)`,R=i?.some(e=>e.label!==void 0);return v(n.Root,{ref:_,value:u,defaultValue:d,onValueChange:e=>{u===void 0&&w(e),y?.(e)},min:o,max:b,className:r("relative flex w-full touch-none select-none items-center py-2 data-[disabled]:opacity-50",s==="always"&&"mt-7",i&&(R?"mb-6":"mb-2"),g),...c,children:[t(n.Track,{className:"relative h-1.5 w-full grow overflow-hidden rounded-full bg-hover shadow-[inset_0_1px_2px_rgba(5,8,22,.08)]",children:t(n.Range,{className:r("absolute h-full rounded-full",h==="gradient"?"bg-[linear-gradient(90deg,#1f45ea,#7560fd)] shadow-[0_0_10px_rgba(91,74,245,.45)]":"bg-primary")})}),i?.map(e=>{const l=a.length>1?e.value>=a[0]&&e.value<=a[a.length-1]:e.value<=a[0];return v("span",{"aria-hidden":!0,className:"pointer-events-none absolute top-full -translate-x-1/2",style:{left:S(e.value)},children:[t("span",{className:r("mx-auto -mt-1.5 block h-1.5 w-px",l?"bg-primary/50":"bg-border2")}),e.label!==void 0?t("span",{className:"num mt-1 block whitespace-nowrap text-[11px] text-muted",children:e.label}):null]},e.value)}),a.map((e,l)=>t(n.Thumb,{"aria-label":a.length>1?l===0?"Minimum":"Maximum":c["aria-label"],className:r("group/thumb relative block size-[18px] cursor-grab rounded-full border-2 border-primary bg-white outline-none active:cursor-grabbing","shadow-[0_1px_3px_rgba(5,8,22,.25),0_0_0_0_var(--primary-subtle)] transition-[box-shadow,transform] duration-150","hover:shadow-[0_1px_3px_rgba(5,8,22,.25),0_0_0_6px_var(--primary-subtle)] focus-visible:shadow-[0_1px_3px_rgba(5,8,22,.25),0_0_0_6px_var(--primary-subtle)] active:scale-110"),children:s!=="never"?t("span",{className:r("num pointer-events-none absolute bottom-full left-1/2 mb-2.5 -translate-x-1/2 whitespace-nowrap rounded-[7px] bg-[#140d33] px-2 py-1 text-[11.5px] font-semibold leading-none text-white shadow-elev2","after:absolute after:left-1/2 after:top-full after:-translate-x-1/2 after:border-4 after:border-transparent after:border-t-[#140d33]","transition-[opacity,transform] duration-150",s==="always"?"opacity-100":"translate-y-1 opacity-0 group-hover/thumb:translate-y-0 group-hover/thumb:opacity-100 group-focus-visible/thumb:translate-y-0 group-focus-visible/thumb:opacity-100 group-active/thumb:translate-y-0 group-active/thumb:opacity-100"),children:x(e)}):null},l))]})});P.displayName="Slider";export{P as Slider};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Spinner — work under way with no known end. (With a known end, Progress.)
|
|
4
|
+
*
|
|
5
|
+
* ring — the default: an arc that fades into its tail. Buttons, inline.
|
|
6
|
+
* dots — three dots in turn: "typing", "connecting", a chat waiting.
|
|
7
|
+
* wave — bars rising like the WAVEINNO wave: a whole panel or page loading.
|
|
8
|
+
* It colours with `currentColor` unless `tone="brand"`, so it takes the text
|
|
9
|
+
* colour of a button it sits in. The label is read out, not shown.
|
|
10
|
+
*/
|
|
11
|
+
export type SpinnerVariant = "ring" | "dots" | "wave";
|
|
12
|
+
declare const SIZE: {
|
|
13
|
+
readonly xs: 12;
|
|
14
|
+
readonly sm: 16;
|
|
15
|
+
readonly md: 20;
|
|
16
|
+
readonly lg: 28;
|
|
17
|
+
readonly xl: 40;
|
|
18
|
+
};
|
|
19
|
+
export declare function Spinner({ variant, size, tone, label, className, }: {
|
|
20
|
+
variant?: SpinnerVariant;
|
|
21
|
+
size?: keyof typeof SIZE;
|
|
22
|
+
/** `current` follows the text colour; `brand` uses the primary (the gradient, for ring). */
|
|
23
|
+
tone?: "current" | "brand";
|
|
24
|
+
label?: string;
|
|
25
|
+
className?: string;
|
|
26
|
+
}): React.JSX.Element;
|
|
27
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as n,jsxs as g}from"react/jsx-runtime";import{cn as l}from"../../lib/utils.js";const m={xs:12,sm:16,md:20,lg:28,xl:40};function u({variant:i="ring",size:o="md",tone:s="current",label:c="Loading",className:d}){const e=m[o],p=s==="brand"?"text-primary":void 0;let r;if(i==="ring"){const a=Math.max(2,Math.round(e/9));r=n("span",{className:"block animate-spin rounded-full [animation-duration:.75s]",style:{width:e,height:e,background:s==="brand"?"conic-gradient(from 90deg, transparent 10%, #1f45ea 60%, #7560fd)":"conic-gradient(from 90deg, transparent 10%, currentColor)",mask:`radial-gradient(farthest-side, transparent calc(100% - ${a+.5}px), #000 calc(100% - ${a}px))`,WebkitMask:`radial-gradient(farthest-side, transparent calc(100% - ${a+.5}px), #000 calc(100% - ${a}px))`}})}else if(i==="dots"){const a=Math.max(3,Math.round(e/4.5));r=n("span",{className:"flex items-center",style:{gap:a*.7,height:e},children:[0,1,2].map(t=>n("span",{className:"wm-dot block rounded-full bg-current",style:{width:a,height:a,animationDelay:`${t*140}ms`}},t))})}else{const a=Math.max(2,Math.round(e/8));r=n("span",{className:"flex items-center",style:{gap:a*.8,height:e},children:[0,1,2,3,4].map(t=>n("span",{className:l("wm-bar block h-full rounded-full",s==="brand"?"bg-[linear-gradient(180deg,#7560fd,#1f45ea)]":"bg-current"),style:{width:a,animationDelay:`${t*90}ms`}},t))})}return g("span",{role:"status",className:l("inline-grid shrink-0 place-items-center",p,d),children:[r,n("span",{className:"sr-only",children:c})]})}export{u as Spinner};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Toaster as Sonner, toast } from "sonner";
|
|
3
|
+
/**
|
|
4
|
+
* Toaster and toast — a message that comes and goes: saved, sent, exported.
|
|
5
|
+
* (One that must stay until it is dealt with is an Alert.)
|
|
6
|
+
*
|
|
7
|
+
* Put <Toaster /> once near the root (AppShell already has one), then call
|
|
8
|
+
* `toast("Saved")`, `toast.success(…)`, `toast.error(…)`, `toast.promise(…)`
|
|
9
|
+
* from anywhere. The toast is sonner's; the look is WAVEINNO's: a raised card,
|
|
10
|
+
* the tone's icon in a soft tile, and the theme's colours in light and dark.
|
|
11
|
+
*/
|
|
12
|
+
export { toast };
|
|
13
|
+
export declare function Toaster({ position, ...props }: React.ComponentProps<typeof Sonner>): React.JSX.Element;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{jsx as r}from"react/jsx-runtime";import{CircleAlert as a,CircleCheck as n,Info as i,LoaderCircle as s,TriangleAlert as d}from"lucide-react";import{Toaster as p,toast as c}from"sonner";const e=(t,o)=>r("span",{className:"grid size-7 place-items-center rounded-[8px] [&_svg]:size-4",style:{color:`var(--${t})`,background:`var(--${t}-subtle)`},children:o});function b({position:t="bottom-right",...o}){return r(p,{position:t,gap:10,icons:{success:e("success",r(n,{})),error:e("danger",r(a,{})),warning:e("warning",r(d,{})),info:e("info",r(i,{})),loading:e("primary",r(s,{className:"animate-spin"}))},toastOptions:{unstyled:!0,classNames:{toast:"group flex w-[356px] max-w-[calc(100vw-32px)] items-start gap-3 rounded-[14px] border border-border bg-raised p-3 pr-4 text-[13px] text-text shadow-[var(--lit),0_1px_2px_rgba(5,8,22,.06),0_16px_40px_-12px_rgba(5,8,22,.28)]",icon:"mt-px shrink-0 [&>svg]:hidden [&>span]:flex",content:"min-w-0 flex-1 pt-[5px]",title:"font-semibold leading-[18px] text-text",description:"mt-0.5 text-[12.5px] leading-[17px] text-muted",actionButton:"ml-auto shrink-0 cursor-pointer self-center rounded-[8px] bg-primary px-2.5 py-1.5 text-[12px] font-medium text-white transition-colors hover:bg-primary-hover",cancelButton:"shrink-0 cursor-pointer self-center rounded-[8px] border border-border px-2.5 py-1.5 text-[12px] font-medium text-body transition-colors hover:bg-hover",closeButton:"!border-border !bg-raised !text-muted hover:!text-text",success:"bg-[image:radial-gradient(130%_140%_at_0%_0%,var(--success-subtle),transparent_55%)] border-[color-mix(in_srgb,var(--success)_22%,var(--border))]!",error:"bg-[image:radial-gradient(130%_140%_at_0%_0%,var(--danger-subtle),transparent_55%)] border-[color-mix(in_srgb,var(--danger)_22%,var(--border))]!",warning:"bg-[image:radial-gradient(130%_140%_at_0%_0%,var(--warning-subtle),transparent_55%)] border-[color-mix(in_srgb,var(--warning)_22%,var(--border))]!",info:"bg-[image:radial-gradient(130%_140%_at_0%_0%,var(--info-subtle),transparent_55%)] border-[color-mix(in_srgb,var(--info)_22%,var(--border))]!"}},...o})}export{b as Toaster,c as toast};
|
package/dist/shell/app-shell.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use client";import{jsx as e,jsxs as u}from"react/jsx-runtime";import{useRouter as g}from"next/navigation";import*as i from"react";import{Toaster as
|
|
1
|
+
"use client";import{jsx as e,jsxs as u}from"react/jsx-runtime";import{useRouter as g}from"next/navigation";import*as i from"react";import{Toaster as R}from"../components/ui/toast.js";import{TooltipProvider as b}from"../components/ui/tooltip.js";import{useHotkey as S,useNumberHotkeys as N}from"../hooks/use-hotkey.js";import{useApp as d}from"../store/app-store.js";import{useUi as f}from"../store/ui-store.js";import{CommandPalette as P}from"./command-palette.js";import{ShellProvider as k}from"./config.js";import{IconRail as w}from"./icon-rail.js";import{MobileNav as x}from"./mobile-nav.js";import{ModuleSidebar as A}from"./module-sidebar.js";import{RecordDrawer as C}from"./record-drawer.js";import{Topbar as D}from"./topbar.js";import{useActive as F}from"./use-active.js";function V({config:t,children:r}){return e(k,{config:t,children:e(T,{children:r})})}function T({children:t}){const r=f(o=>o.hydrate),a=d(o=>o.hydrate),m=d(o=>o.syncFromRoute),h=f(o=>o.togglePanel),l=g(),{cfg:p,pathname:y,mod:n,rail:v}=F();i.useEffect(()=>{r(),a(p.apps.map(o=>o.id))},[r,a,p.apps]),i.useEffect(()=>{m(n.app)},[n.app,m]),S({key:"b",mod:!0},h);const s=v.order;return N(i.useCallback(o=>{const c=s[o];c&&l.push(c.href)},[s,l])),u(b,{delayDuration:400,skipDelayDuration:200,children:[u("div",{className:"shell-grid overflow-hidden",children:[e(w,{}),e(A,{}),e(D,{}),e("main",{id:"main",className:"col-start-3 col-end-4 row-start-2 overflow-y-auto px-4 pb-16 pt-5 sm:px-6",children:e("div",{className:"page-in mx-auto w-full max-w-[1600px]",children:t},y)})]}),e(C,{}),e(P,{}),e(x,{}),e(R,{})]})}export{V as AppShell};
|
package/dist/styles.css
CHANGED
|
@@ -656,3 +656,32 @@ select {
|
|
|
656
656
|
100% { background-color: transparent }
|
|
657
657
|
}
|
|
658
658
|
.wm-saved { animation: wm-saved 900ms var(--ease) 1 }
|
|
659
|
+
|
|
660
|
+
/* ── input and loading motion ── */
|
|
661
|
+
/* A mark arriving: a radio's dot, a filled code box, the Caps Lock hint. */
|
|
662
|
+
@keyframes wm-pop {
|
|
663
|
+
0% { transform: scale(.4); opacity: 0 }
|
|
664
|
+
70% { transform: scale(1.12); opacity: 1 }
|
|
665
|
+
100% { transform: scale(1) }
|
|
666
|
+
}
|
|
667
|
+
@keyframes wm-dot {
|
|
668
|
+
0%, 80%, 100% { transform: translateY(0); opacity: .35 }
|
|
669
|
+
40% { transform: translateY(-40%); opacity: 1 }
|
|
670
|
+
}
|
|
671
|
+
@keyframes wm-bar {
|
|
672
|
+
0%, 100% { transform: scaleY(.35) }
|
|
673
|
+
50% { transform: scaleY(1) }
|
|
674
|
+
}
|
|
675
|
+
.wm-dot { animation: wm-dot 1.1s var(--ease) infinite }
|
|
676
|
+
.wm-bar { animation: wm-bar 1s var(--ease) infinite; transform-origin: center }
|
|
677
|
+
|
|
678
|
+
/* A spinner that stops looks like a hang. Under reduced motion it keeps
|
|
679
|
+
turning, slowly, instead of being cut to one frame by the rule above. */
|
|
680
|
+
@media (prefers-reduced-motion: reduce) {
|
|
681
|
+
[role="status"] .animate-spin,
|
|
682
|
+
.wm-dot,
|
|
683
|
+
.wm-bar {
|
|
684
|
+
animation-duration: 1.8s !important;
|
|
685
|
+
animation-iteration-count: infinite !important;
|
|
686
|
+
}
|
|
687
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@waveinno/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "WAVEINNO design system — tokens, shell, primitives and patterns shared by every WAVEINNO product",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"waveinno",
|
|
@@ -64,8 +64,10 @@
|
|
|
64
64
|
"@radix-ui/react-label": "^2.1.15",
|
|
65
65
|
"@radix-ui/react-popover": "^1.1.23",
|
|
66
66
|
"@radix-ui/react-progress": "^1.1.16",
|
|
67
|
+
"@radix-ui/react-radio-group": "^1.4.7",
|
|
67
68
|
"@radix-ui/react-select": "^2.3.7",
|
|
68
69
|
"@radix-ui/react-separator": "^1.1.15",
|
|
70
|
+
"@radix-ui/react-slider": "^1.4.7",
|
|
69
71
|
"@radix-ui/react-slot": "^1.3.3",
|
|
70
72
|
"@radix-ui/react-switch": "^1.3.7",
|
|
71
73
|
"@radix-ui/react-tabs": "^1.1.21",
|