@waveinno/ui 0.0.0-stage → 0.1.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 +35 -0
- package/LICENSE +5 -0
- package/README.md +64 -2
- package/dist/brand/waveinno-logo.svg +1 -0
- package/dist/components/brand/index.d.ts +3 -0
- package/dist/components/brand/index.js +1 -0
- package/dist/components/brand/logo.d.ts +89 -0
- package/dist/components/brand/logo.js +1 -0
- package/dist/components/brand/waveinno-logo.d.ts +52 -0
- package/dist/components/brand/waveinno-logo.js +1 -0
- package/dist/components/charts/bar-chart.d.ts +20 -0
- package/dist/components/charts/bar-chart.js +1 -0
- package/dist/components/charts/bar-list.d.ts +27 -0
- package/dist/components/charts/bar-list.js +1 -0
- package/dist/components/charts/chart-card.d.ts +32 -0
- package/dist/components/charts/chart-card.js +1 -0
- package/dist/components/charts/donut-chart.d.ts +27 -0
- package/dist/components/charts/donut-chart.js +1 -0
- package/dist/components/charts/gauge-chart.d.ts +21 -0
- package/dist/components/charts/gauge-chart.js +1 -0
- package/dist/components/charts/heatmap.d.ts +23 -0
- package/dist/components/charts/heatmap.js +1 -0
- package/dist/components/charts/index.d.ts +10 -0
- package/dist/components/charts/index.js +1 -0
- package/dist/components/charts/line-chart.d.ts +24 -0
- package/dist/components/charts/line-chart.js +1 -0
- package/dist/components/charts/palette.d.ts +39 -0
- package/dist/components/charts/palette.js +1 -0
- package/dist/components/charts/trend-chart.d.ts +22 -0
- package/dist/components/charts/trend-chart.js +1 -0
- package/dist/components/patterns/activity-feed.d.ts +28 -0
- package/dist/components/patterns/activity-feed.js +1 -0
- package/dist/components/patterns/data-table-cells.d.ts +39 -0
- package/dist/components/patterns/data-table-cells.js +1 -0
- package/dist/components/patterns/data-table-edit.d.ts +56 -0
- package/dist/components/patterns/data-table-edit.js +1 -0
- package/dist/components/patterns/data-table-filters.d.ts +84 -0
- package/dist/components/patterns/data-table-filters.js +1 -0
- package/dist/components/patterns/data-table.d.ts +76 -0
- package/dist/components/patterns/data-table.js +2 -0
- package/dist/components/patterns/index.d.ts +16 -0
- package/dist/components/patterns/index.js +1 -0
- package/dist/components/patterns/kanban.d.ts +28 -0
- package/dist/components/patterns/kanban.js +1 -0
- package/dist/components/patterns/kpi-card.d.ts +22 -0
- package/dist/components/patterns/kpi-card.js +1 -0
- package/dist/components/patterns/money.d.ts +28 -0
- package/dist/components/patterns/money.js +1 -0
- package/dist/components/patterns/page-header.d.ts +38 -0
- package/dist/components/patterns/page-header.js +1 -0
- package/dist/components/patterns/page-hero.d.ts +50 -0
- package/dist/components/patterns/page-hero.js +1 -0
- package/dist/components/patterns/page-shell.d.ts +27 -0
- package/dist/components/patterns/page-shell.js +1 -0
- package/dist/components/patterns/stat-card.d.ts +56 -0
- package/dist/components/patterns/stat-card.js +1 -0
- package/dist/components/patterns/states.d.ts +27 -0
- package/dist/components/patterns/states.js +1 -0
- package/dist/components/patterns/timeline.d.ts +100 -0
- package/dist/components/patterns/timeline.js +1 -0
- package/dist/components/ui/avatar.d.ts +37 -0
- package/dist/components/ui/avatar.js +1 -0
- package/dist/components/ui/badge.d.ts +41 -0
- package/dist/components/ui/badge.js +1 -0
- package/dist/components/ui/button.d.ts +25 -0
- package/dist/components/ui/button.js +1 -0
- package/dist/components/ui/calendar.d.ts +42 -0
- package/dist/components/ui/calendar.js +1 -0
- package/dist/components/ui/card.d.ts +11 -0
- package/dist/components/ui/card.js +1 -0
- package/dist/components/ui/checkbox.d.ts +3 -0
- package/dist/components/ui/checkbox.js +1 -0
- package/dist/components/ui/combobox.d.ts +47 -0
- package/dist/components/ui/combobox.js +1 -0
- package/dist/components/ui/confirm-dialog.d.ts +34 -0
- package/dist/components/ui/confirm-dialog.js +1 -0
- package/dist/components/ui/date-picker.d.ts +80 -0
- package/dist/components/ui/date-picker.js +1 -0
- package/dist/components/ui/dialog.d.ts +54 -0
- package/dist/components/ui/dialog.js +1 -0
- package/dist/components/ui/dropdown-menu.d.ts +44 -0
- package/dist/components/ui/dropdown-menu.js +1 -0
- package/dist/components/ui/form-field.d.ts +27 -0
- package/dist/components/ui/form-field.js +1 -0
- package/dist/components/ui/index.d.ts +26 -0
- package/dist/components/ui/index.js +1 -0
- package/dist/components/ui/input.d.ts +21 -0
- package/dist/components/ui/input.js +1 -0
- package/dist/components/ui/label.d.ts +5 -0
- package/dist/components/ui/label.js +1 -0
- package/dist/components/ui/popover.d.ts +22 -0
- package/dist/components/ui/popover.js +1 -0
- package/dist/components/ui/progress.d.ts +43 -0
- package/dist/components/ui/progress.js +1 -0
- package/dist/components/ui/segmented.d.ts +42 -0
- package/dist/components/ui/segmented.js +1 -0
- package/dist/components/ui/select.d.ts +10 -0
- package/dist/components/ui/select.js +1 -0
- package/dist/components/ui/separator.d.ts +3 -0
- package/dist/components/ui/separator.js +1 -0
- package/dist/components/ui/sheet.d.ts +19 -0
- package/dist/components/ui/sheet.js +1 -0
- package/dist/components/ui/skeleton.d.ts +10 -0
- package/dist/components/ui/skeleton.js +1 -0
- package/dist/components/ui/switch.d.ts +3 -0
- package/dist/components/ui/switch.js +1 -0
- package/dist/components/ui/tabs.d.ts +19 -0
- package/dist/components/ui/tabs.js +1 -0
- package/dist/components/ui/time-picker.d.ts +52 -0
- package/dist/components/ui/time-picker.js +1 -0
- package/dist/components/ui/tooltip.d.ts +13 -0
- package/dist/components/ui/tooltip.js +1 -0
- package/dist/hooks/index.d.ts +4 -0
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/use-hotkey.d.ts +16 -0
- package/dist/hooks/use-hotkey.js +1 -0
- package/dist/hooks/use-media-query.d.ts +4 -0
- package/dist/hooks/use-media-query.js +1 -0
- package/dist/hooks/use-mounted.d.ts +2 -0
- package/dist/hooks/use-mounted.js +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +1 -0
- package/dist/lib/boot-script.d.ts +15 -0
- package/dist/lib/boot-script.js +12 -0
- package/dist/lib/currency.d.ts +55 -0
- package/dist/lib/currency.js +1 -0
- package/dist/lib/format.d.ts +28 -0
- package/dist/lib/format.js +1 -0
- package/dist/lib/index.d.ts +5 -0
- package/dist/lib/index.js +1 -0
- package/dist/lib/utils.d.ts +7 -0
- package/dist/lib/utils.js +1 -0
- package/dist/shell/app-shell.d.ts +18 -0
- package/dist/shell/app-shell.js +1 -0
- package/dist/shell/app-switcher.d.ts +9 -0
- package/dist/shell/app-switcher.js +1 -0
- package/dist/shell/appearance-controls.d.ts +13 -0
- package/dist/shell/appearance-controls.js +1 -0
- package/dist/shell/command-palette.d.ts +9 -0
- package/dist/shell/command-palette.js +1 -0
- package/dist/shell/config.d.ts +66 -0
- package/dist/shell/config.js +1 -0
- package/dist/shell/icon-rail.d.ts +2 -0
- package/dist/shell/icon-rail.js +1 -0
- package/dist/shell/index.d.ts +7 -0
- package/dist/shell/index.js +1 -0
- package/dist/shell/mobile-nav.d.ts +7 -0
- package/dist/shell/mobile-nav.js +1 -0
- package/dist/shell/module-sidebar.d.ts +9 -0
- package/dist/shell/module-sidebar.js +1 -0
- package/dist/shell/notifications.d.ts +5 -0
- package/dist/shell/notifications.js +1 -0
- package/dist/shell/placeholder-screen.d.ts +11 -0
- package/dist/shell/placeholder-screen.js +1 -0
- package/dist/shell/record-drawer.d.ts +32 -0
- package/dist/shell/record-drawer.js +1 -0
- package/dist/shell/registry.d.ts +24 -0
- package/dist/shell/registry.js +1 -0
- package/dist/shell/topbar.d.ts +9 -0
- package/dist/shell/topbar.js +1 -0
- package/dist/shell/use-active.d.ts +21 -0
- package/dist/shell/use-active.js +1 -0
- package/dist/shell/user-menu.d.ts +5 -0
- package/dist/shell/user-menu.js +1 -0
- package/dist/store/app-store.d.ts +10 -0
- package/dist/store/app-store.js +1 -0
- package/dist/store/drawer-store.d.ts +22 -0
- package/dist/store/drawer-store.js +1 -0
- package/dist/store/index.d.ts +5 -0
- package/dist/store/index.js +1 -0
- package/dist/store/settings-store.d.ts +22 -0
- package/dist/store/settings-store.js +1 -0
- package/dist/store/ui-store.d.ts +28 -0
- package/dist/store/ui-store.js +1 -0
- package/dist/styles.css +658 -0
- package/dist/types.d.ts +140 -0
- package/dist/types.js +0 -0
- package/package.json +98 -4
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { Crumb, Tone } from "../../types";
|
|
3
|
+
/**
|
|
4
|
+
* Page hero — the premium page header, for a screen's front door: a facility,
|
|
5
|
+
* a customer, a report, a module home.
|
|
6
|
+
*
|
|
7
|
+
* Everything a person needs before scrolling sits in one framed block: where
|
|
8
|
+
* they are (crumbs), what this is (icon, eyebrow, title, status), the facts
|
|
9
|
+
* that identify it (meta), the actions, a strip of headline numbers, and the
|
|
10
|
+
* tabs that split the page. Each part is optional, so the same component
|
|
11
|
+
* serves a bare title and a full record header. PageHeader stays the quiet
|
|
12
|
+
* default for list and settings screens; use this where the page *is* the
|
|
13
|
+
* object.
|
|
14
|
+
*/
|
|
15
|
+
export interface HeroMeta {
|
|
16
|
+
icon?: React.ElementType;
|
|
17
|
+
label: React.ReactNode;
|
|
18
|
+
}
|
|
19
|
+
export interface HeroStat {
|
|
20
|
+
label: string;
|
|
21
|
+
value: React.ReactNode;
|
|
22
|
+
/** Small line under the value: a delta, a period, a unit. */
|
|
23
|
+
hint?: React.ReactNode;
|
|
24
|
+
tone?: Tone;
|
|
25
|
+
}
|
|
26
|
+
export interface HeroTab {
|
|
27
|
+
id: string;
|
|
28
|
+
label: string;
|
|
29
|
+
count?: number;
|
|
30
|
+
}
|
|
31
|
+
export declare function PageHero({ crumbs, icon, eyebrow, badge, title, description, meta, actions, stats, tabs, activeTab, onTabChange, className, }: {
|
|
32
|
+
crumbs?: Crumb[];
|
|
33
|
+
/** A lucide icon element, an avatar or an image, shown in a 56px tile. */
|
|
34
|
+
icon?: React.ReactNode;
|
|
35
|
+
eyebrow?: React.ReactNode;
|
|
36
|
+
badge?: {
|
|
37
|
+
label: string;
|
|
38
|
+
tone?: Tone;
|
|
39
|
+
};
|
|
40
|
+
title: React.ReactNode;
|
|
41
|
+
description?: React.ReactNode;
|
|
42
|
+
meta?: HeroMeta[];
|
|
43
|
+
/** Buttons, right-aligned on desktop and below the title on phones. */
|
|
44
|
+
actions?: React.ReactNode;
|
|
45
|
+
stats?: HeroStat[];
|
|
46
|
+
tabs?: HeroTab[];
|
|
47
|
+
activeTab?: string;
|
|
48
|
+
onTabChange?: (id: string) => void;
|
|
49
|
+
className?: string;
|
|
50
|
+
}): React.JSX.Element;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as t,jsxs as r}from"react/jsx-runtime";import{ChevronRight as v}from"lucide-react";import*as h from"react";import{cn as l}from"../../lib/utils.js";import{StatusBadge as N}from"../ui/badge.js";const y={neutral:"text-muted",info:"text-primary",success:"text-success",warning:"text-warning",danger:"text-danger",accent:"text-accent"};function T({crumbs:n,icon:s,eyebrow:i,badge:o,title:b,description:d,meta:p,actions:m,stats:c,tabs:x,activeTab:u,onTabChange:g,className:f}){return r("header",{className:l("relative isolate overflow-hidden rounded-[16px] border border-border bg-surface shadow-[var(--lit)]","before:absolute before:inset-0 before:-z-10 before:bg-[radial-gradient(80%_120%_at_100%_0%,var(--primary-subtle),transparent_55%)]","after:absolute after:inset-0 after:-z-10 after:bg-[radial-gradient(var(--border)_1px,transparent_1px)] after:[background-size:16px_16px] after:[mask-image:linear-gradient(135deg,transparent_35%,black_100%)] after:opacity-60",f),children:[r("div",{className:"px-5 pb-5 pt-4 sm:px-6",children:[n?.length?t("nav",{"aria-label":"Breadcrumb",className:"mb-3 flex flex-wrap items-center gap-1 text-[12px] text-muted",children:n.map((e,a)=>r(h.Fragment,{children:[a>0?t(v,{className:"size-3 opacity-60"}):null,e.href&&a<n.length-1?t("a",{href:e.href,className:"rounded px-0.5 transition-colors hover:text-text",children:e.label}):t("span",{className:l(a===n.length-1&&"text-body"),children:e.label})]},`${e.label}-${a}`))}):null,r("div",{className:"flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between",children:[r("div",{className:"flex min-w-0 items-start gap-4",children:[s?t("span",{className:"grid size-14 shrink-0 place-items-center overflow-hidden rounded-[14px] border border-border bg-raised text-primary shadow-elev2 [&>svg]:size-6",children:s}):null,r("div",{className:"min-w-0",children:[i?t("p",{className:"mb-1 text-[11px] font-semibold uppercase tracking-[0.1em] text-primary",children:i}):null,r("div",{className:"flex flex-wrap items-center gap-x-2.5 gap-y-1",children:[t("h1",{className:"disp text-[22px] font-semibold leading-tight tracking-[-0.01em] text-text sm:text-[26px]",children:b}),o?t(N,{label:o.label,tone:o.tone??"neutral"}):null]}),d?t("p",{className:"mt-1.5 max-w-[68ch] text-[13.5px] leading-relaxed text-muted",children:d}):null,p?.length?t("ul",{className:"mt-3 flex flex-wrap items-center gap-x-4 gap-y-1.5 text-[12.5px] text-body",children:p.map((e,a)=>r("li",{className:"flex items-center gap-1.5",children:[e.icon?t(e.icon,{className:"size-3.5 text-muted"}):null,e.label]},a))}):null]})]}),m?t("div",{className:"flex shrink-0 flex-wrap items-center gap-2",children:m}):null]}),c?.length?t("dl",{className:"mt-5 grid grid-cols-2 gap-px overflow-hidden rounded-[12px] border border-border bg-border sm:grid-cols-4",children:c.map(e=>r("div",{className:"bg-raised/90 px-4 py-3 backdrop-blur",children:[t("dt",{className:"text-[11px] font-medium uppercase tracking-[0.06em] text-muted",children:e.label}),t("dd",{className:"num mt-1 text-[19px] font-semibold leading-none text-text",children:e.value}),e.hint?t("dd",{className:l("num mt-1 text-[11.5px]",y[e.tone??"neutral"]),children:e.hint}):null]},e.label))}):null]}),x?.length?t("nav",{role:"tablist",className:"flex gap-1 overflow-x-auto border-t border-border px-3 [scrollbar-width:none] sm:px-4",children:x.map(e=>{const a=e.id===u;return r("button",{type:"button",role:"tab","aria-selected":a,onClick:()=>g?.(e.id),className:l("relative flex h-11 shrink-0 cursor-pointer items-center gap-1.5 px-3 text-[13px] font-medium transition-colors",a?"text-text":"text-muted hover:text-text","after:absolute after:inset-x-2 after:bottom-0 after:h-[2px] after:rounded-full after:transition-colors",a?"after:bg-primary":"after:bg-transparent"),children:[e.label,e.count!==void 0?t("span",{className:l("num rounded-full px-1.5 py-px text-[10.5px]",a?"bg-primary-subtle text-primary":"bg-hover text-muted"),children:e.count}):null]},e.id)})}):null]})}export{T as PageHero};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Page measure.
|
|
4
|
+
*
|
|
5
|
+
* A settings page rendered edge to edge on a 1920px monitor puts its one
|
|
6
|
+
* secondary button 1,300px away from its own title, and runs the description
|
|
7
|
+
* past 140 characters on a line. Neither is a styling problem — both are the
|
|
8
|
+
* page being told it is as wide as the window.
|
|
9
|
+
*
|
|
10
|
+
* settings ~1080px forms, preferences, billing — reading and choosing
|
|
11
|
+
* default ~1440px dashboards and detail pages
|
|
12
|
+
* full tables, boards and maps, where width is the feature
|
|
13
|
+
*
|
|
14
|
+
* Tables genuinely want the room, which is why `full` exists and why this is
|
|
15
|
+
* opt-in rather than a global clamp.
|
|
16
|
+
*/
|
|
17
|
+
declare const widths: {
|
|
18
|
+
readonly settings: "max-w-[1080px]";
|
|
19
|
+
readonly default: "max-w-[1440px]";
|
|
20
|
+
readonly full: "max-w-none";
|
|
21
|
+
};
|
|
22
|
+
export declare function PageShell({ width, className, children, }: {
|
|
23
|
+
width?: keyof typeof widths;
|
|
24
|
+
className?: string;
|
|
25
|
+
children: React.ReactNode;
|
|
26
|
+
}): React.JSX.Element;
|
|
27
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as l}from"react/jsx-runtime";import{cn as o}from"../../lib/utils.js";const s={settings:"max-w-[1080px]",default:"max-w-[1440px]",full:"max-w-none"};function c({width:t="default",className:a,children:e}){return l("div",{className:o("mx-auto w-full",s[t],a),children:e})}export{c as PageShell};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { Tone } from "../../types";
|
|
3
|
+
/**
|
|
4
|
+
* Stat cards — the premium siblings of KpiCard, for dashboards that open a
|
|
5
|
+
* product, not for dense report rows.
|
|
6
|
+
*
|
|
7
|
+
* icon — a tinted icon tile, the value, a delta pill: the everyday card.
|
|
8
|
+
* spark — the value over a soft area chart that bleeds to the card edge:
|
|
9
|
+
* when the trend matters as much as the number.
|
|
10
|
+
* target — progress towards a goal as a ring, with "of target" spelled out.
|
|
11
|
+
* gradient — the brand gradient with a faint symbol: the one number a page
|
|
12
|
+
* leads with (one per page).
|
|
13
|
+
* comparison — two figures side by side (cars vs bikes, cash vs digital) with
|
|
14
|
+
* a split bar, so the share is seen, not computed.
|
|
15
|
+
*
|
|
16
|
+
* Delta: the arrow is direction, the colour is good-or-bad (`invertDelta` for
|
|
17
|
+
* costs and failures), the text says what it is compared with.
|
|
18
|
+
*/
|
|
19
|
+
export interface StatCardProps {
|
|
20
|
+
label: string;
|
|
21
|
+
value: React.ReactNode;
|
|
22
|
+
/** Put the active currency symbol before the value. */
|
|
23
|
+
currency?: boolean;
|
|
24
|
+
delta?: number;
|
|
25
|
+
invertDelta?: boolean;
|
|
26
|
+
/** "vs last week" — what the delta compares with. */
|
|
27
|
+
deltaLabel?: string;
|
|
28
|
+
hint?: React.ReactNode;
|
|
29
|
+
icon?: React.ReactNode;
|
|
30
|
+
tone?: Tone;
|
|
31
|
+
variant?: "icon" | "spark" | "target" | "gradient" | "comparison";
|
|
32
|
+
/** spark: the series, oldest first. */
|
|
33
|
+
spark?: number[];
|
|
34
|
+
/** target: progress 0–100 and the goal in words. */
|
|
35
|
+
target?: {
|
|
36
|
+
percent: number;
|
|
37
|
+
label: string;
|
|
38
|
+
};
|
|
39
|
+
/** comparison: two parts of the whole. */
|
|
40
|
+
parts?: [{
|
|
41
|
+
label: string;
|
|
42
|
+
value: number;
|
|
43
|
+
display?: string;
|
|
44
|
+
}, {
|
|
45
|
+
label: string;
|
|
46
|
+
value: number;
|
|
47
|
+
display?: string;
|
|
48
|
+
}];
|
|
49
|
+
className?: string;
|
|
50
|
+
}
|
|
51
|
+
export declare function StatCard({ label, value, currency, delta, invertDelta, deltaLabel, hint, icon, tone, variant, spark, target, parts, className, }: StatCardProps): React.JSX.Element;
|
|
52
|
+
/** A row of StatCards — 1 up on phones, 2 on tablets, up to 4 on laptops. */
|
|
53
|
+
export declare function StatGrid({ children, className }: {
|
|
54
|
+
children: React.ReactNode;
|
|
55
|
+
className?: string;
|
|
56
|
+
}): React.JSX.Element;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{ArrowDownRight as $,ArrowUpRight as k}from"lucide-react";import*as z from"react";import{cn as d}from"../../lib/utils.js";import{useCurrency as C}from"../../store/settings-store.js";import{WaveinnoSymbol as S}from"../brand/waveinno-logo.js";import{ProgressRing as L}from"../ui/progress.js";const M={neutral:"bg-hover text-muted",info:"bg-primary-subtle text-primary",success:"bg-success-subtle text-success",warning:"bg-warning-subtle text-warning",danger:"bg-danger-subtle text-danger",accent:"bg-primary-subtle text-accent"},P={neutral:"var(--muted)",info:"var(--primary)",success:"var(--success)",warning:"var(--warning)",danger:"var(--danger)",accent:"var(--accent)"};function T({delta:a,invert:m,label:i,onBrand:n}){const s=m?a<0:a>0;return t("span",{className:"flex flex-wrap items-center gap-1.5 text-[12px]",children:[t("span",{className:d("num inline-flex items-center gap-0.5 rounded-full px-1.5 py-0.5 font-semibold",n?"bg-white/18 text-white":s?"bg-success-subtle text-success":"bg-danger-subtle text-danger"),children:[a>=0?e(k,{className:"size-3"}):e($,{className:"size-3"}),Math.abs(a).toFixed(1),"%"]}),i?e("span",{className:n?"text-white/70":"text-muted",children:i}):null]})}function A(a){const m=Math.max(...a),i=Math.min(...a),n=m-i||1,s=a.map((r,p)=>[p/(a.length-1)*100,36-(r-i)/n*30]);let f=`M${s[0][0]},${s[0][1]}`;for(let r=1;r<s.length;r++){const[p,v]=s[r-1],[l,g]=s[r],u=(p+l)/2;f+=` C${u},${v} ${u},${g} ${l},${g}`}return{line:f,area:`${f} L100,40 L0,40 Z`,last:s[s.length-1]}}function E({label:a,value:m,currency:i,delta:n,invertDelta:s,deltaLabel:f,hint:r,icon:p,tone:v="info",variant:l="icon",spark:g,target:u,parts:b,className:_}){const h=C(),w=z.useId().replace(/:/g,""),x=l==="gradient",y=t("span",{className:"num disp text-[28px] font-semibold leading-none tracking-[-0.01em]",children:[i&&h.position==="before"?e("span",{className:"mr-0.5 text-[0.62em] font-medium opacity-70",children:h.symbol}):null,m,i&&h.position==="after"?e("span",{className:"ml-1 text-[0.62em] font-medium opacity-70",children:h.symbol}):null]});return t("div",{className:d("relative isolate flex flex-col overflow-hidden rounded-[16px] border p-[18px] transition-[transform,box-shadow] duration-200 hover:-translate-y-0.5",x?"border-transparent bg-[linear-gradient(140deg,#1f45ea,#1437d3_45%,#7560fd)] text-white shadow-[inset_0_1px_0_rgba(255,255,255,.25),0_14px_30px_-14px_rgba(20,55,211,.7)]":"border-border bg-surface text-text shadow-[var(--lit)] hover:shadow-[var(--lit),0_10px_24px_-14px_rgba(5,8,22,.25)]",_),children:[x?e(S,{mono:!0,title:"",className:"pointer-events-none absolute -bottom-4 -right-6 -z-10 h-auto w-40 rotate-[-12deg] text-white opacity-[.12]"}):null,t("div",{className:"flex items-start justify-between gap-3",children:[e("p",{className:d("text-[12px] font-medium",x?"text-white/80":"text-muted"),children:a}),p&&l!=="target"?e("span",{className:d("grid size-9 shrink-0 place-items-center rounded-[10px] [&_svg]:size-[18px]",x?"bg-white/15 text-white ring-1 ring-inset ring-white/25":M[v]),children:p}):null]}),l==="target"&&u?t("div",{className:"mt-2 flex items-center justify-between gap-3",children:[t("div",{className:"min-w-0",children:[y,e("p",{className:"mt-2 text-[12px] text-muted",children:u.label})]}),e(L,{value:u.percent,size:68,thickness:7,gradient:!0,label:a})]}):l==="comparison"&&b?t("div",{className:"mt-2",children:[e("div",{className:"grid grid-cols-2 gap-3",children:b.map((o,c)=>t("div",{className:d(c===1&&"border-l border-border pl-3"),children:[t("p",{className:"flex items-center gap-1.5 text-[11.5px] text-muted",children:[e("span",{className:d("size-2 rounded-full",c===0?"bg-primary":"bg-accent")}),o.label]}),e("p",{className:"num disp mt-1 text-[22px] font-semibold leading-none text-text",children:o.display??o.value.toLocaleString()})]},o.label))}),(()=>{const o=b[0].value+b[1].value||1,c=b[0].value/o*100;return t("div",{className:"mt-3",children:[t("div",{className:"flex h-2 gap-0.5 overflow-hidden rounded-full",children:[e("div",{className:"h-full rounded-l-full bg-[linear-gradient(90deg,#1437d3,#4f46e5)]",style:{width:`${c}%`}}),e("div",{className:"h-full flex-1 rounded-r-full bg-[linear-gradient(90deg,#7c3aed,#a78bfa)]"})]}),t("div",{className:"num mt-1.5 flex justify-between text-[11px] text-muted",children:[t("span",{children:[c.toFixed(0),"%"]}),t("span",{children:[(100-c).toFixed(0),"%"]})]})]})})()]}):e("div",{className:"mt-2",children:y}),n!==void 0||r?t("div",{className:"mt-2.5 flex flex-wrap items-center gap-2",children:[n!==void 0?e(T,{delta:n,invert:s,label:f,onBrand:x}):null,r?e("span",{className:d("text-[12px]",x?"text-white/70":"text-muted"),children:r}):null]}):null,l==="spark"&&g&&g.length>1?(()=>{const{line:o,area:c,last:R}=A(g),N=P[n===void 0?v:(s?n<0:n>0)?"success":"danger"];return t("svg",{viewBox:"0 0 100 40",preserveAspectRatio:"none",className:"-mx-[18px] -mb-[18px] mt-3 h-16 w-[calc(100%+36px)]","aria-hidden":!0,children:[e("defs",{children:t("linearGradient",{id:`${w}-a`,x1:"0",y1:"0",x2:"0",y2:"1",children:[e("stop",{offset:"0",stopColor:N,stopOpacity:".28"}),e("stop",{offset:"1",stopColor:N,stopOpacity:"0"})]})}),e("path",{d:c,fill:`url(#${w}-a)`}),e("path",{d:o,fill:"none",stroke:N,strokeWidth:"1.6",vectorEffect:"non-scaling-stroke"}),e("circle",{cx:R[0],cy:R[1],r:"1.6",fill:N,vectorEffect:"non-scaling-stroke"})]})})():null]})}function O({children:a,className:m}){return e("div",{className:d("stagger grid gap-3 sm:grid-cols-2 xl:grid-cols-4",m),children:a})}export{E as StatCard,O as StatGrid};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* The required trio — §6, acceptance criterion 6.
|
|
4
|
+
*
|
|
5
|
+
* Every list in every module renders one of these three. An empty state that
|
|
6
|
+
* only says "No data" wastes the one moment the user is most willing to be
|
|
7
|
+
* taught what the screen is for, so each takes an action.
|
|
8
|
+
*/
|
|
9
|
+
export declare function EmptyState({ icon: Icon, title, description, action, className, }: {
|
|
10
|
+
icon?: React.ElementType;
|
|
11
|
+
title: string;
|
|
12
|
+
description?: string;
|
|
13
|
+
action?: React.ReactNode;
|
|
14
|
+
className?: string;
|
|
15
|
+
}): React.JSX.Element;
|
|
16
|
+
export declare function ErrorState({ title, description, onRetry, detail, }: {
|
|
17
|
+
title?: string;
|
|
18
|
+
description?: string;
|
|
19
|
+
onRetry?: () => void;
|
|
20
|
+
detail?: string;
|
|
21
|
+
}): React.JSX.Element;
|
|
22
|
+
/** Loading keeps the real geometry so nothing jumps when data arrives. */
|
|
23
|
+
export declare function LoadingState({ kpis, rows, cols, }: {
|
|
24
|
+
kpis?: number;
|
|
25
|
+
rows?: number;
|
|
26
|
+
cols?: number;
|
|
27
|
+
}): React.JSX.Element;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as e,jsxs as s}from"react/jsx-runtime";import{CircleAlert as m,Inbox as o,RotateCw as l}from"lucide-react";import{Button as d}from"../ui/button.js";import{SkeletonKpis as c,SkeletonTable as u}from"../ui/skeleton.js";import{cn as x}from"../../lib/utils.js";function b({icon:a=o,title:n,description:t,action:r,className:i}){return s("div",{className:x("px-6 py-16 text-center",i),children:[e("div",{className:"mx-auto grid size-11 place-items-center rounded-full bg-hover",children:e(a,{className:"size-5 text-muted"})}),e("p",{className:"mt-3.5 text-[15px] font-medium text-text",children:n}),t?e("p",{className:"mx-auto mt-1.5 max-w-sm text-[13px] leading-5 text-muted",children:t}):null,r?e("div",{className:"mt-4 flex justify-center",children:r}):null]})}function y({title:a="This view could not load",description:n="The request failed before any rows came back. Nothing has been changed.",onRetry:t,detail:r}){return s("div",{className:"rounded-card border border-danger/30 bg-danger-subtle px-6 py-12 text-center",children:[e("div",{className:"mx-auto grid size-11 place-items-center rounded-full bg-danger/10",children:e(m,{className:"size-5 text-danger"})}),e("p",{className:"mt-3.5 text-[15px] font-medium text-text",children:a}),e("p",{className:"mx-auto mt-1.5 max-w-md text-[13px] leading-5 text-body",children:n}),r?e("p",{className:"mono mx-auto mt-3 max-w-md truncate text-[11px] text-muted",children:r}):null,t?e("div",{className:"mt-4 flex justify-center",children:s(d,{variant:"secondary",size:"sm",onClick:t,children:[e(l,{}),"Try again"]})}):null]})}function w({kpis:a=5,rows:n=8,cols:t=6}){return s("div",{className:"space-y-4",children:[a>0?e(c,{count:a}):null,e(u,{rows:n,cols:t})]})}export{b as EmptyState,y as ErrorState,w as LoadingState};
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { Tone } from "../../types";
|
|
3
|
+
export interface ActivityTimelineItem {
|
|
4
|
+
id: string;
|
|
5
|
+
title: React.ReactNode;
|
|
6
|
+
description?: React.ReactNode;
|
|
7
|
+
/** "14:32" or "2 min ago". */
|
|
8
|
+
time: string;
|
|
9
|
+
/** Day heading the item sits under: "Today", "Yesterday", "Mon 29 Sep". */
|
|
10
|
+
group?: string;
|
|
11
|
+
tone?: Tone;
|
|
12
|
+
icon?: React.ReactNode;
|
|
13
|
+
actor?: {
|
|
14
|
+
name: string;
|
|
15
|
+
src?: string;
|
|
16
|
+
};
|
|
17
|
+
/** Small extra, right of the title: an amount, a plate, a badge. */
|
|
18
|
+
meta?: React.ReactNode;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Activity log — events down a single line, grouped under sticky day headings.
|
|
22
|
+
* Each event: a toned icon on the line, who did it, what happened, when. The
|
|
23
|
+
* line ends at the last item, so the log never looks unfinished.
|
|
24
|
+
*/
|
|
25
|
+
export declare function ActivityTimeline({ items, className }: {
|
|
26
|
+
items: ActivityTimelineItem[];
|
|
27
|
+
className?: string;
|
|
28
|
+
}): React.JSX.Element;
|
|
29
|
+
export interface FieldChange {
|
|
30
|
+
field: string;
|
|
31
|
+
from?: React.ReactNode;
|
|
32
|
+
to?: React.ReactNode;
|
|
33
|
+
}
|
|
34
|
+
export interface ChangeEntry {
|
|
35
|
+
id: string;
|
|
36
|
+
actor: {
|
|
37
|
+
name: string;
|
|
38
|
+
role?: string;
|
|
39
|
+
src?: string;
|
|
40
|
+
};
|
|
41
|
+
/** "Created", "Updated", "Published v7", "Deleted". */
|
|
42
|
+
action: string;
|
|
43
|
+
at: string;
|
|
44
|
+
changes?: FieldChange[];
|
|
45
|
+
/** The reason the person gave — audit trails need the why, not just the what. */
|
|
46
|
+
note?: React.ReactNode;
|
|
47
|
+
version?: string;
|
|
48
|
+
tone?: Tone;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Change history — an audit trail people can actually read. Every entry says
|
|
52
|
+
* who, what kind of change, when, and the reason they gave; expand it to see
|
|
53
|
+
* each field as old → new, the old struck through on red, the new on green.
|
|
54
|
+
* The newest entry starts open; the rest stay folded so a long history scans.
|
|
55
|
+
*/
|
|
56
|
+
export declare function ChangeHistory({ entries, defaultOpen, labels, className, }: {
|
|
57
|
+
entries: ChangeEntry[];
|
|
58
|
+
/** How many of the newest entries start expanded. */
|
|
59
|
+
defaultOpen?: number;
|
|
60
|
+
labels?: {
|
|
61
|
+
changes?: (n: number) => string;
|
|
62
|
+
empty?: string;
|
|
63
|
+
};
|
|
64
|
+
className?: string;
|
|
65
|
+
}): React.JSX.Element;
|
|
66
|
+
export interface NoteThread {
|
|
67
|
+
id: string;
|
|
68
|
+
author: {
|
|
69
|
+
name: string;
|
|
70
|
+
role?: string;
|
|
71
|
+
src?: string;
|
|
72
|
+
};
|
|
73
|
+
at: string;
|
|
74
|
+
text: React.ReactNode;
|
|
75
|
+
/** A label for the note: "Decision", "Question", "Change request". */
|
|
76
|
+
tag?: {
|
|
77
|
+
label: string;
|
|
78
|
+
tone?: Tone;
|
|
79
|
+
};
|
|
80
|
+
pinned?: boolean;
|
|
81
|
+
replies?: NoteThread[];
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Change notes — the discussion around a change (a rate, a policy, a refund):
|
|
85
|
+
* notes with tags (Decision, Question…), pinned decisions on top, replies
|
|
86
|
+
* nested under the note they answer, folded after the first level so a long
|
|
87
|
+
* thread stays readable. Pass `onReply` to let people answer in place.
|
|
88
|
+
*/
|
|
89
|
+
export declare function ChangeNotes({ notes, onReply, labels, className, }: {
|
|
90
|
+
notes: NoteThread[];
|
|
91
|
+
onReply?: (parentId: string, text: string) => void;
|
|
92
|
+
labels?: Partial<{
|
|
93
|
+
reply: string;
|
|
94
|
+
replies: (n: number) => string;
|
|
95
|
+
hide: string;
|
|
96
|
+
placeholder: string;
|
|
97
|
+
send: string;
|
|
98
|
+
}>;
|
|
99
|
+
className?: string;
|
|
100
|
+
}): React.JSX.Element;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{jsx as e,jsxs as a}from"react/jsx-runtime";import{ChevronDown as y,CornerDownRight as v,MessageSquare as w,Pin as R,Send as k}from"lucide-react";import*as b from"react";import{cn as c}from"../../lib/utils.js";import{Avatar as f}from"../ui/avatar.js";import{Badge as N}from"../ui/badge.js";import{Button as z}from"../ui/button.js";const T={neutral:"bg-raised text-muted ring-border",info:"bg-primary-subtle text-primary ring-primary/25",success:"bg-success-subtle text-success ring-success/25",warning:"bg-warning-subtle text-warning ring-warning/25",danger:"bg-danger-subtle text-danger ring-danger/25",accent:"bg-primary-subtle text-accent ring-accent/25"};function D({items:r,className:p}){const o=[];for(const n of r){const l=n.group??"",s=o[o.length-1];s&&s[0]===l?s[1].push(n):o.push([l,[n]])}return e("div",{className:c("space-y-5",p),children:o.map(([n,l])=>a("section",{children:[n?a("h4",{className:"mb-3 flex items-center gap-3 text-[11px] font-semibold uppercase tracking-[0.08em] text-muted",children:[n,e("span",{className:"h-px flex-1 bg-border"})]}):null,e("ol",{className:"relative",children:l.map((s,d)=>a("li",{className:"relative flex gap-3.5 pb-5 last:pb-0",children:[d<l.length-1?e("span",{"aria-hidden":!0,className:"absolute left-[15px] top-8 bottom-0 w-px bg-border"}):null,e("span",{className:c("relative z-[1] grid size-8 shrink-0 place-items-center rounded-full ring-1 [&_svg]:size-4",T[s.tone??"neutral"]),children:s.icon??e("i",{className:"size-2 rounded-full bg-current"})}),a("div",{className:"min-w-0 flex-1 pt-1",children:[a("div",{className:"flex flex-wrap items-baseline gap-x-2 gap-y-0.5",children:[a("p",{className:"text-[13px] text-body",children:[s.actor?a("span",{className:"font-semibold text-text",children:[s.actor.name," "]}):null,s.title]}),s.meta,e("time",{className:"ml-auto shrink-0 text-[11.5px] text-muted",children:s.time})]}),s.description?e("div",{className:"mt-1 text-[12.5px] leading-[18px] text-muted",children:s.description}):null]})]},s.id))})]},n||"_"))})}function O({entries:r,defaultOpen:p=1,labels:o,className:n}){const l={changes:t=>`${t} ${t===1?"change":"changes"}`,empty:"(empty)",...o},[s,d]=b.useState(()=>new Set(r.slice(0,p).map(t=>t.id))),g=t=>d(u=>{const m=new Set(u);return m.has(t)?m.delete(t):m.add(t),m});return e("ol",{className:c("relative space-y-3",n),children:r.map((t,u)=>{const m=s.has(t.id),i=t.changes?.length??0;return a("li",{className:"relative flex gap-3.5",children:[u<r.length-1?e("span",{"aria-hidden":!0,className:"absolute left-[17px] top-10 -bottom-3 w-px bg-border"}):null,e(f,{name:t.actor.name,src:t.actor.src,size:"md",className:"relative z-[1] mt-1 ring-4 ring-canvas"}),a("div",{className:"min-w-0 flex-1 overflow-hidden rounded-[12px] border border-border bg-raised shadow-[var(--lit)]",children:[a("button",{type:"button",onClick:()=>i&&g(t.id),className:c("flex w-full items-start gap-2 px-3.5 py-3 text-left",i?"cursor-pointer hover:bg-hover/50":"cursor-default"),"aria-expanded":i?m:void 0,children:[a("div",{className:"min-w-0 flex-1",children:[a("p",{className:"text-[13px] text-body",children:[e("span",{className:"font-semibold text-text",children:t.actor.name}),t.actor.role?a("span",{className:"text-muted",children:[" \xB7 ",t.actor.role]}):null]}),a("div",{className:"mt-1 flex flex-wrap items-center gap-1.5",children:[e(N,{tone:t.tone??"info",variant:"soft",children:t.action}),t.version?e("span",{className:"num rounded-[5px] border border-border bg-surface px-1.5 text-[11px] font-medium leading-[18px] text-muted",children:t.version}):null,i?e("span",{className:"text-[11.5px] text-muted",children:l.changes(i)}):null]})]}),e("time",{className:"shrink-0 pt-0.5 text-[11.5px] text-muted",children:t.at}),i?e(y,{className:c("mt-0.5 size-4 shrink-0 text-muted transition-transform",m&&"rotate-180")}):null]}),t.note?a("p",{className:"mx-3.5 mb-3 rounded-[8px] border-l-2 border-primary/40 bg-surface px-2.5 py-1.5 text-[12.5px] italic leading-[18px] text-body",children:["\u201C",t.note,"\u201D"]}):null,i&&m?e("dl",{className:"divide-y divide-border border-t border-border bg-surface/60",children:t.changes.map(x=>a("div",{className:"grid grid-cols-[110px_1fr] items-center gap-3 px-3.5 py-2 text-[12.5px] sm:grid-cols-[140px_1fr]",children:[e("dt",{className:"truncate font-medium text-muted",children:x.field}),a("dd",{className:"flex min-w-0 flex-wrap items-center gap-1.5",children:[e("span",{className:"num rounded-[5px] bg-danger-subtle px-1.5 py-0.5 text-danger line-through decoration-danger/50",children:x.from??l.empty}),e("span",{className:"text-muted",children:"\u2192"}),e("span",{className:"num rounded-[5px] bg-success-subtle px-1.5 py-0.5 font-medium text-success",children:x.to??l.empty})]})]},x.field))}):null]})]},t.id)})})}function h({note:r,depth:p,onReply:o,labels:n}){const[l,s]=b.useState(p<1),[d,g]=b.useState(!1),[t,u]=b.useState(""),m=r.replies??[];return e("li",{className:"relative",children:a("div",{className:"flex gap-3",children:[e(f,{name:r.author.name,src:r.author.src,size:p?"sm":"md",className:"relative z-[1] mt-0.5 ring-4 ring-canvas"}),a("div",{className:"min-w-0 flex-1",children:[a("div",{className:c("rounded-[12px] border px-3.5 py-2.5",r.pinned?"border-primary/30 bg-primary-subtle/40":"border-border bg-raised"),children:[a("div",{className:"flex flex-wrap items-center gap-x-2 gap-y-1",children:[e("span",{className:"text-[13px] font-semibold text-text",children:r.author.name}),r.author.role?e("span",{className:"text-[11.5px] text-muted",children:r.author.role}):null,r.tag?e(N,{tone:r.tag.tone??"neutral",variant:"outline",children:r.tag.label}):null,r.pinned?e(R,{className:"size-3 text-primary","aria-label":"pinned"}):null,e("time",{className:"ml-auto text-[11.5px] text-muted",children:r.at})]}),e("div",{className:"mt-1 text-[13px] leading-5 text-body",children:r.text})]}),a("div",{className:"mt-1 flex items-center gap-3 pl-1 text-[12px]",children:[o?a("button",{type:"button",onClick:()=>g(i=>!i),className:"flex cursor-pointer items-center gap-1 font-medium text-muted hover:text-primary",children:[e(v,{className:"size-3.5"})," ",n.reply]}):null,m.length?a("button",{type:"button",onClick:()=>s(i=>!i),className:"flex cursor-pointer items-center gap-1 font-medium text-primary",children:[e(w,{className:"size-3.5"})," ",l?n.hide:n.replies(m.length)]}):null]}),d&&o?a("form",{className:"mt-2 flex items-start gap-2",onSubmit:i=>{i.preventDefault(),t.trim()&&(o(r.id,t.trim()),u(""),g(!1),s(!0))},children:[e("textarea",{autoFocus:!0,value:t,onChange:i=>u(i.target.value),rows:2,placeholder:n.placeholder,className:"min-h-16 flex-1 resize-y rounded-[10px] border border-border2 bg-raised px-3 py-2 text-[13px] text-text outline-none placeholder:text-placeholder focus:border-primary focus:ring-2 focus:ring-primary/20"}),e(z,{type:"submit",variant:"primary",size:"icon","aria-label":n.send,disabled:!t.trim(),children:e(k,{})})]}):null,m.length&&l?e("ul",{className:"relative mt-3 space-y-3 before:absolute before:-left-[22px] before:top-0 before:bottom-3 before:w-px before:bg-border",children:m.map(i=>e(h,{note:i,depth:p+1,onReply:o,labels:n},i.id))}):null]})]})})}function _({notes:r,onReply:p,labels:o,className:n}){const l={reply:"Reply",replies:d=>`${d} ${d===1?"reply":"replies"}`,hide:"Hide replies",placeholder:"Write a reply\u2026",send:"Send",...o},s=[...r].sort((d,g)=>+!!g.pinned-+!!d.pinned);return e("ul",{className:c("space-y-4",n),children:s.map(d=>e(h,{note:d,depth:0,onReply:p,labels:l},d.id))})}export{D as ActivityTimeline,O as ChangeHistory,_ as ChangeNotes};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
declare const sizes: {
|
|
3
|
+
readonly sm: "size-6 text-[10px]";
|
|
4
|
+
readonly md: "size-8 text-xs";
|
|
5
|
+
readonly lg: "size-10 text-[13px]";
|
|
6
|
+
readonly xl: "size-14 text-[17px]";
|
|
7
|
+
readonly "2xl": "size-20 text-[24px]";
|
|
8
|
+
};
|
|
9
|
+
declare const STATUS: {
|
|
10
|
+
readonly online: "bg-success";
|
|
11
|
+
readonly away: "bg-warning";
|
|
12
|
+
readonly busy: "bg-danger";
|
|
13
|
+
readonly offline: "bg-border2";
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Person or organisation. Options: `status` dot (online / away / busy /
|
|
17
|
+
* offline), `verified` tick, `ring="gradient"` (the brand ring — the owner, a
|
|
18
|
+
* featured partner, someone live), `shape="square"` for organisations and car
|
|
19
|
+
* parks rather than people.
|
|
20
|
+
*/
|
|
21
|
+
export declare function Avatar({ name, src, size, status, verified, ring, shape, className, }: {
|
|
22
|
+
name: string;
|
|
23
|
+
src?: string | null;
|
|
24
|
+
size?: keyof typeof sizes;
|
|
25
|
+
status?: keyof typeof STATUS;
|
|
26
|
+
verified?: boolean;
|
|
27
|
+
ring?: "gradient";
|
|
28
|
+
shape?: "circle" | "square";
|
|
29
|
+
className?: string;
|
|
30
|
+
}): React.JSX.Element;
|
|
31
|
+
/** Overlapping stack with a +n remainder — caps at 4 so the row never reflows. */
|
|
32
|
+
export declare function AvatarStack({ names, max, size, }: {
|
|
33
|
+
names: string[];
|
|
34
|
+
max?: number;
|
|
35
|
+
size?: keyof typeof sizes;
|
|
36
|
+
}): React.JSX.Element;
|
|
37
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{jsx as r,jsxs as o}from"react/jsx-runtime";import*as m from"@radix-ui/react-avatar";import{BadgeCheck as x}from"lucide-react";import{cn as n,initials as v}from"../../lib/utils.js";const p={sm:"size-6 text-[10px]",md:"size-8 text-xs",lg:"size-10 text-[13px]",xl:"size-14 text-[17px]","2xl":"size-20 text-[24px]"},b={sm:"size-2",md:"size-2.5",lg:"size-3",xl:"size-3.5","2xl":"size-4"},u=["linear-gradient(135deg,#1f45ea,#5b4af5)","linear-gradient(135deg,#7560fd,#a78bfa)","linear-gradient(135deg,#0891b2,#22d3ee)","linear-gradient(135deg,#047857,#10b981)","linear-gradient(135deg,#d97706,#f59e0b)","linear-gradient(135deg,#db2777,#f472b6)","linear-gradient(135deg,#1a113e,#4338ca)"];function z(e){let a=0;for(const s of e)a=a*31+s.charCodeAt(0)>>>0;return u[a%u.length]}const h={online:"bg-success",away:"bg-warning",busy:"bg-danger",offline:"bg-border2"};function k({name:e,src:a,size:s="md",status:i,verified:l,ring:t,shape:c="circle",className:f}){const d=c==="circle"?"rounded-full":"rounded-[28%]",g=o(m.Root,{className:n("relative grid shrink-0 place-items-center overflow-hidden font-semibold text-white select-none shadow-[inset_0_1px_0_rgba(255,255,255,.25)]",d,p[s],!t&&f),style:{background:z(e)},children:[a?r(m.Image,{src:a,alt:e,className:"size-full object-cover"}):null,r(m.Fallback,{className:"grid size-full place-items-center",children:v(e)})]});return!t&&!i&&!l?g:o("span",{className:n("relative inline-grid shrink-0",t&&f),children:[t?r("span",{className:n("bg-[conic-gradient(from_200deg,#1437d3,#7560fd,#22d3ee,#1437d3)] p-[2px]",d),children:r("span",{className:n("block bg-canvas p-[2px]",d),children:g})}):g,i?r("span",{className:n("absolute bottom-0 right-0 rounded-full ring-2 ring-canvas",h[i],b[s]),"aria-label":i}):null,l?r(x,{className:n("absolute -right-1 -top-1 fill-primary text-canvas",b[s]==="size-2"?"size-3":"size-4"),"aria-label":"verified"}):null]})}function S({names:e,max:a=4,size:s="sm"}){const i=e.slice(0,a),l=e.length-i.length;return o("div",{className:"flex items-center",children:[i.map((t,c)=>r(k,{name:t,size:s,className:"-ml-1.5 ring-2 ring-surface first:ml-0"},t+c)),l>0?o("span",{className:n("num -ml-1.5 grid place-items-center rounded-full bg-hover font-semibold text-muted ring-2 ring-surface",p[s]),children:["+",l]}):null]})}export{k as Avatar,S as AvatarStack};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { type VariantProps } from "class-variance-authority";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import type { Tone } from "../../types";
|
|
4
|
+
/**
|
|
5
|
+
* Status badge — §2.6.
|
|
6
|
+
*
|
|
7
|
+
* The dot is not decoration. Colour alone never carries status in this system,
|
|
8
|
+
* so every badge renders shape (dot) + text + hue together. Removing the dot
|
|
9
|
+
* breaks WCAG 1.4.1 for the 8% of users who cannot separate the hues.
|
|
10
|
+
*/
|
|
11
|
+
declare const badgeVariants: (props?: ({
|
|
12
|
+
tone?: "accent" | "danger" | "info" | "neutral" | "success" | "warning" | null | undefined;
|
|
13
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
14
|
+
/**
|
|
15
|
+
* Styles beyond the default soft fill:
|
|
16
|
+
* solid — filled with the tone, white text: counts that must be seen.
|
|
17
|
+
* outline — hairline in the tone, no fill: dense tables where fills get loud.
|
|
18
|
+
* dot — neutral pill, only the dot carries the tone: calm status lists.
|
|
19
|
+
* gradient — the brand gradient: New, Pro, Beta. Rare by design.
|
|
20
|
+
* live — soft, with a pulsing dot: live, recording, gate online.
|
|
21
|
+
* Every style keeps dot + text, so status is never colour alone.
|
|
22
|
+
*/
|
|
23
|
+
export type BadgeVariant = "soft" | "solid" | "outline" | "dot" | "gradient" | "live";
|
|
24
|
+
export interface BadgeProps extends React.HTMLAttributes<HTMLSpanElement>, VariantProps<typeof badgeVariants> {
|
|
25
|
+
dot?: boolean;
|
|
26
|
+
variant?: BadgeVariant;
|
|
27
|
+
}
|
|
28
|
+
export declare function Badge({ className, tone, dot, variant, style, children, ...props }: BadgeProps): React.JSX.Element;
|
|
29
|
+
export declare function StatusBadge({ label, tone, variant, className, }: {
|
|
30
|
+
label: string;
|
|
31
|
+
tone?: Tone;
|
|
32
|
+
variant?: BadgeVariant;
|
|
33
|
+
className?: string;
|
|
34
|
+
}): React.JSX.Element;
|
|
35
|
+
/** Rail and sidebar counters. `danger` promotes the count to an alarm. */
|
|
36
|
+
export declare function CountBadge({ value, tone, className, }: {
|
|
37
|
+
value: string | number;
|
|
38
|
+
tone?: "neutral" | "danger";
|
|
39
|
+
className?: string;
|
|
40
|
+
}): React.JSX.Element;
|
|
41
|
+
export { badgeVariants };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as r,jsxs as o}from"react/jsx-runtime";import{cva as u}from"class-variance-authority";import{cn as s}from"../../lib/utils.js";const i=u("inline-flex items-center gap-[5px] h-[22px] px-[9px] rounded-full text-xs font-medium leading-none whitespace-nowrap",{variants:{tone:{neutral:"bg-hover text-muted",info:"bg-primary-subtle text-primary",success:"bg-success-subtle text-success",warning:"bg-warning-subtle text-warning",danger:"bg-danger-subtle text-danger",accent:"bg-primary-subtle text-accent"}},defaultVariants:{tone:"neutral"}}),c={neutral:"var(--muted)",info:"var(--primary)",success:"var(--success)",warning:"var(--warning)",danger:"var(--danger)",accent:"var(--accent)"},x={solid:"text-white bg-[var(--tone)] shadow-[inset_0_1px_0_rgba(255,255,255,.2)]",outline:"bg-transparent text-[var(--tone)] ring-1 ring-inset ring-[color-mix(in_srgb,var(--tone)_40%,transparent)]",dot:"bg-raised text-body ring-1 ring-inset ring-border",gradient:"text-white bg-[linear-gradient(135deg,#1437d3,#7560fd)] shadow-[inset_0_1px_0_rgba(255,255,255,.25),0_2px_8px_-2px_rgba(20,55,211,.5)]",live:""};function b({className:n,tone:t,dot:a=!0,variant:e="soft",style:g,children:l,...d}){const p=t??"neutral";return o("span",{className:s(i({tone:t}),e!=="soft"&&e!=="live"&&x[e],n),style:{"--tone":c[p],...g},...d,children:[a?o("span",{className:"relative grid size-1.5 shrink-0 place-items-center","aria-hidden":!0,children:[e==="live"?r("span",{className:"absolute inline-flex size-full animate-ping rounded-full bg-current opacity-60"}):null,r("i",{className:s("relative size-1.5 rounded-full",e==="dot"?"bg-[var(--tone)]":"bg-current")})]}):null,l]})}function _({label:n,tone:t="neutral",variant:a,className:e}){return r(b,{tone:t,variant:a,className:e,children:n})}function w({value:n,tone:t="neutral",className:a}){return r("span",{className:s("num inline-grid min-w-4 place-items-center rounded-full px-1 text-[10px] font-semibold leading-4 text-white",t==="danger"?"bg-danger":"bg-muted",a),children:n})}export{b as Badge,w as CountBadge,_ as StatusBadge,i as badgeVariants};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type VariantProps } from "class-variance-authority";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
/**
|
|
4
|
+
* Working variants — primary, secondary, ghost, subtle, danger, link — are for
|
|
5
|
+
* product screens, and §7 still holds: one primary action per screen. If a page
|
|
6
|
+
* needs two, the page is wrong, not the button.
|
|
7
|
+
*
|
|
8
|
+
* Showcase variants — gradient, glow, dark, outline, elevated, glass — are for
|
|
9
|
+
* moments, not toolbars: a sign-in, an onboarding step, an upgrade prompt, a
|
|
10
|
+
* hero on the website, a button on a photo. One per view.
|
|
11
|
+
*
|
|
12
|
+
* Every button presses in (scale .98) so a tap is felt; `shape="pill"` rounds
|
|
13
|
+
* it fully for marketing and mobile.
|
|
14
|
+
*/
|
|
15
|
+
declare const buttonVariants: (props?: ({
|
|
16
|
+
variant?: "danger" | "dark" | "elevated" | "ghost" | "glass" | "glow" | "gradient" | "link" | "outline" | "primary" | "secondary" | "subtle" | "white" | null | undefined;
|
|
17
|
+
shape?: "default" | "pill" | null | undefined;
|
|
18
|
+
size?: "default" | "icon" | "iconSm" | "lg" | "sm" | "xl" | null | undefined;
|
|
19
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
20
|
+
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
|
|
21
|
+
asChild?: boolean;
|
|
22
|
+
loading?: boolean;
|
|
23
|
+
}
|
|
24
|
+
export declare const Button: React.ForwardRefExoticComponent<ButtonProps & React.RefAttributes<HTMLButtonElement>>;
|
|
25
|
+
export { buttonVariants };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{jsx as s,jsxs as v}from"react/jsx-runtime";import{Slot as d}from"@radix-ui/react-slot";import{cva as l}from"class-variance-authority";import{Loader2 as h}from"lucide-react";import*as f from"react";import{cn as _}from"../../lib/utils.js";const e=l("relative isolate inline-flex items-center justify-center gap-[7px] whitespace-nowrap rounded-ctl text-[13px] font-medium leading-none transition-[background-color,border-color,color,box-shadow,transform,filter] duration-150 ease-wave active:scale-[.98] disabled:pointer-events-none disabled:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0 cursor-pointer",{variants:{variant:{primary:"bg-primary text-white hover:bg-primary-hover",secondary:"bg-raised text-text border border-border2 hover:bg-hover",ghost:"text-muted hover:bg-hover hover:text-text",subtle:"bg-primary-subtle text-primary hover:brightness-[.97]",danger:"bg-danger text-white hover:brightness-110",link:"text-primary underline-offset-4 hover:underline px-0",gradient:"overflow-hidden text-white bg-[linear-gradient(135deg,#1f45ea,#1437d3_40%,#7560fd)] shadow-[inset_0_1px_0_rgba(255,255,255,.3),0_1px_2px_rgba(5,8,22,.2),0_8px_20px_-8px_rgba(20,55,211,.6)] hover:brightness-110 hover:shadow-[inset_0_1px_0_rgba(255,255,255,.3),0_1px_2px_rgba(5,8,22,.2),0_12px_26px_-8px_rgba(20,55,211,.7)] after:absolute after:inset-y-0 after:-left-full after:w-1/2 after:-skew-x-12 after:bg-[linear-gradient(90deg,transparent,rgba(255,255,255,.35),transparent)] after:transition-[left] after:duration-700 hover:after:left-[150%]",glow:"bg-primary text-white shadow-[0_0_0_1px_rgba(99,102,241,.4),0_6px_22px_-4px_rgba(79,70,229,.65)] hover:shadow-[0_0_0_1px_rgba(99,102,241,.55),0_8px_30px_-4px_rgba(79,70,229,.8)] hover:bg-primary-hover",dark:"text-white bg-[linear-gradient(180deg,#2a1f66,#1a113e)] shadow-[inset_0_1px_0_rgba(255,255,255,.14),0_1px_2px_rgba(5,8,22,.3),0_6px_16px_-8px_rgba(26,17,62,.7)] hover:brightness-125 dark:ring-1 dark:ring-white/15",outline:"border border-primary/40 text-primary hover:bg-primary-subtle hover:border-primary",elevated:"bg-raised text-text shadow-[var(--lit),0_1px_2px_rgba(5,8,22,.08),0_6px_16px_-6px_rgba(5,8,22,.18)] ring-1 ring-border hover:-translate-y-px hover:shadow-[var(--lit),0_2px_4px_rgba(5,8,22,.08),0_10px_22px_-8px_rgba(5,8,22,.25)]",glass:"text-white backdrop-blur-xl backdrop-saturate-150 [text-shadow:0_1px_1px_rgba(5,8,22,.3)] bg-[linear-gradient(180deg,rgba(255,255,255,.28),rgba(255,255,255,.1))] ring-1 ring-inset ring-white/40 shadow-[inset_0_1px_0_rgba(255,255,255,.5),inset_0_-1px_0_rgba(255,255,255,.08),0_8px_24px_-10px_rgba(5,8,22,.55)] hover:bg-[linear-gradient(180deg,rgba(255,255,255,.36),rgba(255,255,255,.16))] hover:ring-white/60",white:"bg-white text-[#1a113e] shadow-[inset_0_-1px_0_rgba(26,17,62,.08),0_1px_2px_rgba(5,8,22,.2),0_10px_24px_-10px_rgba(5,8,22,.6)] hover:bg-[#f4f5fb]"},shape:{default:"",pill:"rounded-full"},size:{default:"h-ctl px-[14px]",sm:"h-8 px-3 text-xs",lg:"h-11 px-5 text-sm",xl:"h-[52px] px-7 text-[15px] font-semibold [&_svg]:size-5",icon:"h-ctl w-ctl px-0",iconSm:"h-8 w-8 px-0"}},defaultVariants:{variant:"secondary",size:"default",shape:"default"}}),u=f.forwardRef(({className:r,variant:t,size:a,shape:o,asChild:g=!1,loading:i,children:n,disabled:b,...p},x)=>g?s(d,{ref:x,className:_(e({variant:t,size:a,shape:o}),r),...p,children:n}):v("button",{ref:x,className:_(e({variant:t,size:a,shape:o}),r),disabled:b||i,...p,children:[i?s(h,{className:"animate-spin"}):null,n]}));u.displayName="Button";export{u as Button,e as buttonVariants};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Calendar — the month grid every date control is built on (DatePicker,
|
|
4
|
+
* DateTimePicker, DateRangePicker), also usable inline.
|
|
5
|
+
*
|
|
6
|
+
* Click the month title to jump by month, click it again to jump by year: a
|
|
7
|
+
* date of birth or a contract start three years back should be three clicks,
|
|
8
|
+
* not thirty-six. Names and digits come from `locale` through Intl ("bn-BD"
|
|
9
|
+
* gives Bangla months and numerals), and the week can start on any day.
|
|
10
|
+
* Keyboard: arrows move a day / a week, PageUp/PageDown a month (with Shift a
|
|
11
|
+
* year), Home/End the week, Enter picks.
|
|
12
|
+
*/
|
|
13
|
+
export type Weekday = 0 | 1 | 2 | 3 | 4 | 5 | 6;
|
|
14
|
+
export interface DayRange {
|
|
15
|
+
from: Date | null;
|
|
16
|
+
to: Date | null;
|
|
17
|
+
}
|
|
18
|
+
/** "2026-10-10" → local Date at midnight; anything else → null. */
|
|
19
|
+
export declare function parseIsoDay(iso: string | null | undefined): Date | null;
|
|
20
|
+
/** Local Date → "2026-10-10" (no time zone shift). */
|
|
21
|
+
export declare function toIsoDay(d: Date): string;
|
|
22
|
+
export declare const addDays: (d: Date, n: number) => Date;
|
|
23
|
+
export declare function addMonths(d: Date, n: number): Date;
|
|
24
|
+
export interface CalendarProps {
|
|
25
|
+
/** Single mode: the picked day. */
|
|
26
|
+
selected?: Date | null;
|
|
27
|
+
/** Range mode: the picked span. */
|
|
28
|
+
range?: DayRange;
|
|
29
|
+
onDayClick: (day: Date) => void;
|
|
30
|
+
min?: Date | null;
|
|
31
|
+
max?: Date | null;
|
|
32
|
+
locale?: string;
|
|
33
|
+
/** 0 = Sunday … 6 = Saturday. Bangladesh weeks usually start on Saturday (6). */
|
|
34
|
+
weekStartsOn?: Weekday;
|
|
35
|
+
numberOfMonths?: 1 | 2;
|
|
36
|
+
/** First month shown; defaults to the selection, else today. */
|
|
37
|
+
defaultMonth?: Date;
|
|
38
|
+
/** Put keyboard focus on the selected (or today's) day when mounted. */
|
|
39
|
+
autoFocus?: boolean;
|
|
40
|
+
className?: string;
|
|
41
|
+
}
|
|
42
|
+
export declare function Calendar({ selected, range, onDayClick, min, max, locale, weekStartsOn, numberOfMonths, defaultMonth, autoFocus, className, }: CalendarProps): React.JSX.Element;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{jsx as r,jsxs as k}from"react/jsx-runtime";import{ChevronLeft as W,ChevronRight as G}from"lucide-react";import*as y from"react";import{cn as D}from"../../lib/utils.js";function et(t){const e=/^(\d{4})-(\d{2})-(\d{2})/.exec(t??"");return e?new Date(Number(e[1]),Number(e[2])-1,Number(e[3])):null}function c(t){return`${t.getFullYear()}-${String(t.getMonth()+1).padStart(2,"0")}-${String(t.getDate()).padStart(2,"0")}`}const v=(t,e)=>new Date(t.getFullYear(),t.getMonth(),t.getDate()+e),F=t=>new Date(t.getFullYear(),t.getMonth(),1),f=(t,e)=>!!t&&!!e&&c(t)===c(e);function p(t,e){const m=new Date(t.getFullYear(),t.getMonth()+e,1),x=new Date(m.getFullYear(),m.getMonth()+1,0).getDate();return new Date(m.getFullYear(),m.getMonth(),Math.min(t.getDate(),x))}function U(t,e){const m=F(t),x=(m.getDay()-e+7)%7;return Array.from({length:42},(S,I)=>v(m,I-x))}function Z(t){return y.useMemo(()=>({title:new Intl.DateTimeFormat(t,{month:"long",year:"numeric"}),month:new Intl.DateTimeFormat(t,{month:"short"}),year:new Intl.DateTimeFormat(t,{year:"numeric"}),weekday:new Intl.DateTimeFormat(t,{weekday:"short"}),day:new Intl.DateTimeFormat(t,{day:"numeric"}),full:new Intl.DateTimeFormat(t,{weekday:"long",day:"numeric",month:"long",year:"numeric"})}),[t])}const C="grid size-8 cursor-pointer place-items-center rounded-ctl text-muted transition-colors hover:bg-hover hover:text-text disabled:pointer-events-none disabled:opacity-30";function ot({selected:t=null,range:e,onDayClick:m,min:x=null,max:S=null,locale:I="en-GB",weekStartsOn:R=1,numberOfMonths:M=1,defaultMonth:j,autoFocus:q,className:E}){const d=Z(I),z=y.useMemo(()=>new Date,[]),P=t??e?.from??z,[i,w]=y.useState(()=>F(j??P)),[a,$]=y.useState(P),[u,_]=y.useState("days"),[T,K]=y.useState(null),Y=y.useRef(null),A=y.useRef(!!q),L=o=>(!x||c(o)>=c(x))&&(!S||c(o)<=c(S));y.useEffect(()=>{!A.current||u!=="days"||(A.current=!1,Y.current?.querySelector(`[data-day="${c(a)}"]`)?.focus({preventScroll:!0}))},[a,i,u]);const V=o=>{A.current=!0,$(o);const s=p(i,M-1);(o<i||o>=p(F(s),1))&&w(F(o))},J=o=>{const s=(a.getDay()-R+7)%7,b={ArrowLeft:()=>v(a,-1),ArrowRight:()=>v(a,1),ArrowUp:()=>v(a,-7),ArrowDown:()=>v(a,7),PageUp:()=>p(a,o.shiftKey?-12:-1),PageDown:()=>p(a,o.shiftKey?12:1),Home:()=>v(a,-s),End:()=>v(a,6-s)};b[o.key]?(o.preventDefault(),V(b[o.key]())):(o.key==="Enter"||o.key===" ")&&(o.preventDefault(),L(a)&&m(a))},Q=U(new Date(2026,0,1),R).slice(0,7),h=e?e.to??(e.from&&T&&T>=e.from?T:null):null,X=o=>!!e?.from&&!!h&&o>e.from&&o<h;if(u!=="days"){const o=i.getFullYear(),s=o-o%12,b=u==="months"?Array.from({length:12},(l,g)=>new Date(o,g,1)):Array.from({length:12},(l,g)=>new Date(s+g,i.getMonth(),1)),n=l=>u==="months"?l.getMonth()===i.getMonth():l.getFullYear()===o;return k("div",{ref:Y,className:D("w-[268px]",E),children:[k("div",{className:"mb-2 flex items-center justify-between",children:[r("button",{type:"button","aria-label":"Previous",className:C,onClick:()=>w(p(i,u==="months"?-12:-144)),children:r(W,{className:"size-4"})}),r("button",{type:"button",onClick:()=>_(u==="months"?"years":"days"),className:"disp cursor-pointer rounded-ctl px-2 py-1 text-[13.5px] font-semibold text-text transition-colors hover:bg-hover",children:u==="months"?d.year.format(i):`${d.year.format(new Date(s,0))} \u2013 ${d.year.format(new Date(s+11,0))}`}),r("button",{type:"button","aria-label":"Next",className:C,onClick:()=>w(p(i,u==="months"?12:144)),children:r(G,{className:"size-4"})})]}),r("div",{className:"grid grid-cols-3 gap-1.5",children:b.map(l=>r("button",{type:"button",onClick:()=>{w(l),_(u==="years"?"months":"days")},className:D("num h-11 cursor-pointer rounded-ctl text-[13px] transition-colors",n(l)?"bg-primary font-semibold text-white shadow-[0_1px_2px_rgba(5,8,22,.14)]":"text-text hover:bg-hover"),children:u==="months"?d.month.format(l):d.year.format(l)},l.toISOString()))})]})}return r("div",{ref:Y,className:D("flex gap-5",E),onMouseLeave:()=>K(null),children:Array.from({length:M},(o,s)=>{const b=p(i,s);return k("div",{className:"w-[268px]",children:[k("div",{className:"mb-2 flex items-center justify-between",children:[r("button",{type:"button","aria-label":"Previous month",className:D(C,s>0&&"invisible"),onClick:()=>w(p(i,-1)),children:r(W,{className:"size-4"})}),r("button",{type:"button","aria-live":"polite",onClick:()=>_("months"),className:"disp cursor-pointer rounded-ctl px-2 py-1 text-[13.5px] font-semibold text-text transition-colors hover:bg-hover",children:d.title.format(b)}),r("button",{type:"button","aria-label":"Next month",className:D(C,s<M-1&&"invisible"),onClick:()=>w(p(i,1)),children:r(G,{className:"size-4"})})]}),r("div",{className:"grid grid-cols-7 pb-1",children:Q.map(n=>r("div",{className:"py-1 text-center text-[11px] font-medium text-muted",children:d.weekday.format(n)},n.getDay()))}),r("div",{role:"grid",onKeyDown:J,className:"grid grid-cols-7 gap-y-0.5",children:U(b,R).map(n=>{const l=n.getMonth()===b.getMonth();if(!l&&M>1)return r("div",{"aria-hidden":!0},c(n));const g=L(n),N=e?f(n,e.from)||f(n,h):f(n,t),H=X(n),B=f(n,z);return r("div",{className:D("relative",H&&"bg-primary-subtle",e&&f(n,e.from)&&h&&!f(e.from,h)&&"rounded-l-ctl bg-primary-subtle",e&&f(n,h)&&e.from&&!f(e.from,h)&&"rounded-r-ctl bg-primary-subtle"),children:k("button",{type:"button",role:"gridcell","data-day":c(n),tabIndex:f(n,a)?0:-1,"aria-selected":N,"aria-label":d.full.format(n),disabled:!g,onClick:()=>m(n),onFocus:()=>$(n),onMouseEnter:()=>e&&K(n),className:D("num relative grid h-9 w-full cursor-pointer place-items-center rounded-ctl text-[13px] outline-none transition-[background-color,color,box-shadow] duration-150",l?"text-text":"text-muted/55",g&&!N&&"hover:bg-hover",H&&"rounded-none text-primary hover:bg-primary/10","focus-visible:ring-2 focus-visible:ring-primary/40",N&&"bg-primary font-semibold text-white shadow-[0_1px_2px_rgba(5,8,22,.14)]",B&&!N&&"font-semibold text-primary",!g&&"cursor-not-allowed opacity-30"),children:[d.day.format(n),B&&!N?r("span",{className:"absolute bottom-1 left-1/2 size-1 -translate-x-1/2 rounded-full bg-primary"}):null]})},c(n))})})]},s)})})}export{ot as Calendar,v as addDays,p as addMonths,et as parseIsoDay,c as toIsoDay};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
/** The one surface. Padding 17px, radius 10px, hairline border — never a shadow
|
|
3
|
+
* at rest, because a page of 12 floating cards has no hierarchy left to spend. */
|
|
4
|
+
export declare const Card: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & {
|
|
5
|
+
interactive?: boolean;
|
|
6
|
+
} & React.RefAttributes<HTMLDivElement>>;
|
|
7
|
+
export declare const CardHeader: ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => React.JSX.Element;
|
|
8
|
+
export declare const CardTitle: ({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) => React.JSX.Element;
|
|
9
|
+
export declare const CardDescription: ({ className, ...props }: React.HTMLAttributes<HTMLParagraphElement>) => React.JSX.Element;
|
|
10
|
+
export declare const CardContent: ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => React.JSX.Element;
|
|
11
|
+
export declare const CardFooter: ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => React.JSX.Element;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as a}from"react/jsx-runtime";import*as d from"react";import{cn as r}from"../../lib/utils.js";const i=d.forwardRef(({className:e,interactive:t,...o},s)=>a("div",{ref:s,className:r("rounded-card border border-border bg-surface p-[17px] shadow-[var(--lit)]",t&&"cursor-pointer transition-[transform,box-shadow,border-color] duration-150 ease-wave hover:-translate-y-0.5 hover:border-border2 hover:shadow-elev2",e),...o}));i.displayName="Card";const c=({className:e,...t})=>a("div",{className:r("mb-3 flex items-start justify-between gap-3",e),...t}),m=({className:e,...t})=>a("h3",{className:r("text-[13px] font-semibold text-text",e),...t}),l=({className:e,...t})=>a("p",{className:r("mt-0.5 text-xs text-muted",e),...t}),b=({className:e,...t})=>a("div",{className:r(e),...t}),p=({className:e,...t})=>a("div",{className:r("mt-3 flex items-center gap-2 border-t border-border pt-3",e),...t});export{i as Card,b as CardContent,l as CardDescription,p as CardFooter,c as CardHeader,m as CardTitle};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
export declare const Checkbox: React.ForwardRefExoticComponent<Omit<CheckboxPrimitive.CheckboxProps & React.RefAttributes<HTMLButtonElement>, "ref"> & React.RefAttributes<HTMLButtonElement>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{jsx as e}from"react/jsx-runtime";import*as r from"@radix-ui/react-checkbox";import{Check as a,Minus as c}from"lucide-react";import*as d from"react";import{cn as s}from"../../lib/utils.js";const m=d.forwardRef(({className:o,...t},i)=>e(r.Root,{ref:i,className:s("peer size-4 shrink-0 cursor-pointer rounded-[4px] border border-border2 bg-raised transition-colors duration-100 data-[state=checked]:border-primary data-[state=checked]:bg-primary data-[state=indeterminate]:border-primary data-[state=indeterminate]:bg-primary disabled:cursor-not-allowed disabled:opacity-50",o),...t,children:e(r.Indicator,{className:"grid place-items-center text-white",children:t.checked==="indeterminate"?e(c,{className:"size-3",strokeWidth:3}):e(a,{className:"size-3",strokeWidth:3})})}));m.displayName="Checkbox";export{m as Checkbox};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Searchable select. Use instead of Select once a list passes ~8 items. Search matches the label, the hint and any keywords, in
|
|
4
|
+
* either script: "dha", "ঢাকা" and "dhk" can all find Dhaka, and Bangla digits
|
|
5
|
+
* match Latin ones. Keyboard: type on the closed box to open and search;
|
|
6
|
+
* ↑ ↓ Home End move, Enter picks, Esc closes.
|
|
7
|
+
*
|
|
8
|
+
* Words on screen come from `labels`, so each product passes its own language.
|
|
9
|
+
*/
|
|
10
|
+
export interface ComboOption {
|
|
11
|
+
value: string;
|
|
12
|
+
label: string;
|
|
13
|
+
/** Second line, muted: area, type, count. Also searched. */
|
|
14
|
+
hint?: string;
|
|
15
|
+
/** Extra words to match, e.g. the other-language name, a code. */
|
|
16
|
+
keywords?: string[];
|
|
17
|
+
icon?: React.ReactNode;
|
|
18
|
+
/** Options with the same group are listed under one heading, in order of first appearance. */
|
|
19
|
+
group?: string;
|
|
20
|
+
disabled?: boolean;
|
|
21
|
+
}
|
|
22
|
+
/** Everything the combobox says. Pass the ones your product translates. */
|
|
23
|
+
export interface ComboboxLabels {
|
|
24
|
+
placeholder: string;
|
|
25
|
+
search: string;
|
|
26
|
+
clear: string;
|
|
27
|
+
empty: (query: string) => string;
|
|
28
|
+
count: (shown: number, total: number) => string;
|
|
29
|
+
total: (total: number) => string;
|
|
30
|
+
}
|
|
31
|
+
export declare const COMBOBOX_LABELS: ComboboxLabels;
|
|
32
|
+
export declare function Combobox({ id, value, onChange, options, placeholder, searchPlaceholder, clearable, disabled, className, contentClassName, hintInValue, labels: labelOverrides, "aria-invalid": invalid, }: {
|
|
33
|
+
id?: string;
|
|
34
|
+
value: string | null;
|
|
35
|
+
onChange: (value: string | null) => void;
|
|
36
|
+
options: ComboOption[];
|
|
37
|
+
placeholder?: string;
|
|
38
|
+
searchPlaceholder?: string;
|
|
39
|
+
clearable?: boolean;
|
|
40
|
+
disabled?: boolean;
|
|
41
|
+
className?: string;
|
|
42
|
+
contentClassName?: string;
|
|
43
|
+
/** Show the selected option's hint in the closed box too (e.g. a zone's offset). */
|
|
44
|
+
hintInValue?: boolean | ((o: ComboOption) => string);
|
|
45
|
+
labels?: Partial<ComboboxLabels>;
|
|
46
|
+
"aria-invalid"?: boolean;
|
|
47
|
+
}): React.JSX.Element;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{Fragment as $,jsx as t,jsxs as o}from"react/jsx-runtime";import{Check as q,ChevronsUpDown as F,Search as W,SearchX as G,X as R}from"lucide-react";import*as d from"react";import{cn as x}from"../../lib/utils.js";import{Popover as Q,PopoverContent as j,PopoverTrigger as J}from"./popover.js";const V={placeholder:"Choose\u2026",search:"Search\u2026",clear:"Clear",empty:r=>`Nothing matches \u201C${r}\u201D`,count:(r,a)=>`${r} of ${a}`,total:r=>`${r} options`},Y="\u09E6\u09E7\u09E8\u09E9\u09EA\u09EB\u09EC\u09ED\u09EE\u09EF";function h(r){return r.normalize("NFKD").replace(/[̀-ͯ]/g,"").replace(/[০-৯]/g,a=>String(Y.indexOf(a))).toLowerCase().trim()}function Z(r,a){const i=h(r.label);return i.startsWith(a)?0:i.split(/[\s\-–—(),./]+/).some(v=>v.startsWith(a))?1:[i,h(r.hint??""),...(r.keywords??[]).map(h)].some(v=>v.includes(a))?2:-1}function ee({text:r,q:a}){if(!a)return t($,{children:r});const i=h(r).indexOf(a);return i<0||h(r).length!==r.length?t($,{children:r}):o($,{children:[r.slice(0,i),t("mark",{className:"rounded-[3px] bg-primary-subtle px-px text-primary",children:r.slice(i,i+a.length)}),r.slice(i+a.length)]})}function ae({id:r,value:a,onChange:i,options:f,placeholder:v,searchPlaceholder:z,clearable:L=!1,disabled:E,className:H,contentClassName:I,hintInValue:O=!1,labels:B,"aria-invalid":U}){const u={...V,...B},[m,k]=d.useState(!1),[N,w]=d.useState(""),[p,D]=d.useState(0),K=d.useRef(null),y=d.useId(),c=f.find(e=>e.value===a)??null,g=h(N),A=d.useMemo(()=>g?f.map((e,s)=>({o:e,i:s,r:Z(e,g)})).filter(e=>e.r>=0).sort((e,s)=>e.r-s.r||e.i-s.i).map(e=>e.o):f,[f,g]),M=d.useMemo(()=>{const e=new Map;for(const s of A){const n=s.group??"";e.has(n)||e.set(n,[]),e.get(n).push(s)}return[...e.entries()]},[A]),l=d.useMemo(()=>M.flatMap(([,e])=>e),[M]);d.useEffect(()=>{if(!m)return;const e=g?l.findIndex(s=>!s.disabled):Math.max(0,l.findIndex(s=>s.value===a));D(e<0?0:e)},[m,g]),d.useEffect(()=>{if(!m)return;K.current?.querySelector(`[data-index="${p}"]`)?.scrollIntoView({block:"nearest"})},[p,m]);const P=e=>{e.disabled||(i(e.value),k(!1))},C=(e,s=p)=>{if(!l.length)return;let n=s;for(let b=0;b<l.length;b++)if(n=(n+e+l.length)%l.length,!l[n].disabled)return D(n)},X=e=>{if(e.key==="ArrowDown")C(1);else if(e.key==="ArrowUp")C(-1);else if(e.key==="Home")C(1,-1);else if(e.key==="End")C(-1,l.length);else if(e.key==="Enter")l[p]&&P(l[p]);else return;e.preventDefault()},_=e=>{k(e),e||w("")};let T=-1;return o(Q,{open:m,onOpenChange:_,children:[o("div",{className:x("relative",H),children:[t(J,{asChild:!0,disabled:E,children:o("button",{id:r,type:"button",role:"combobox","aria-expanded":m,"aria-controls":`${y}-list`,"aria-haspopup":"listbox","aria-invalid":U,onKeyDown:e=>{!m&&e.key.length===1&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&e.key!==" "?(w(e.key),k(!0),e.preventDefault()):!m&&(e.key==="ArrowDown"||e.key==="ArrowUp")&&(k(!0),e.preventDefault())},className:x("group flex h-ctl w-full cursor-pointer items-center gap-2 rounded-ctl border border-border2 bg-raised pl-3 text-left text-[13px] text-text outline-none transition-[border-color,box-shadow]","focus-visible:border-primary focus-visible:ring-2 focus-visible:ring-primary/20 data-[state=open]:border-primary data-[state=open]:ring-2 data-[state=open]:ring-primary/20","disabled:cursor-not-allowed disabled:bg-hover disabled:text-muted aria-[invalid=true]:border-danger",L&&c?"pr-14":"pr-8"),children:[c?.icon?t("span",{className:"grid shrink-0 place-items-center text-muted",children:c.icon}):null,o("span",{className:x("min-w-0 flex-1 truncate",!c&&"text-placeholder"),children:[c?c.label:v??u.placeholder,c&&O?o("span",{className:"ml-1.5 text-muted",children:["\xB7 ",typeof O=="function"?O(c):c.hint]}):null]})]})}),L&&c&&!E?t("button",{type:"button","aria-label":u.clear,onClick:()=>i(null),className:"absolute right-7 top-1/2 grid size-6 -translate-y-1/2 cursor-pointer place-items-center rounded-md text-muted hover:bg-hover hover:text-text",children:t(R,{className:"size-3.5"})}):null,t(F,{className:"pointer-events-none absolute right-2.5 top-1/2 size-4 -translate-y-1/2 text-muted"})]}),o(j,{align:"start",className:x("w-[var(--radix-popover-trigger-width)] min-w-[260px] overflow-hidden p-0",I),onOpenAutoFocus:e=>{e.preventDefault(),e.currentTarget.querySelector("input")?.focus()},children:[o("div",{className:"flex items-center gap-2 border-b border-border px-3",children:[t(W,{className:"size-4 shrink-0 text-muted"}),t("input",{value:N,onChange:e=>w(e.target.value),onKeyDown:X,placeholder:z??u.search,"aria-label":z??u.search,"aria-controls":`${y}-list`,"aria-activedescendant":l[p]?`${y}-${p}`:void 0,autoComplete:"off",spellCheck:!1,className:"h-11 min-w-0 flex-1 bg-transparent text-[13.5px] text-text outline-none placeholder:text-placeholder"}),N?t("button",{type:"button","aria-label":u.clear,onClick:()=>w(""),className:"grid size-6 cursor-pointer place-items-center rounded-md text-muted hover:bg-hover hover:text-text",children:t(R,{className:"size-3.5"})}):null]}),t("div",{ref:K,id:`${y}-list`,role:"listbox",className:"max-h-[296px] overflow-y-auto overscroll-contain p-1.5",children:l.length===0?o("div",{className:"flex flex-col items-center gap-2 px-4 py-8 text-center",children:[t(G,{className:"size-6 text-muted"}),t("p",{className:"text-[13px] text-muted",children:u.empty(N)})]}):M.map(([e,s])=>o("div",{role:"group","aria-label":e||void 0,children:[e?t("p",{className:"sticky top-0 z-10 bg-raised px-2 pb-1 pt-2 text-[10.5px] font-semibold uppercase tracking-[0.08em] text-muted",children:e}):null,s.map(n=>{T++;const b=T,S=n.value===a;return o("div",{id:`${y}-${b}`,"data-index":b,role:"option","aria-selected":S,"aria-disabled":n.disabled,onMouseMove:()=>!n.disabled&&p!==b&&D(b),onClick:()=>P(n),className:x("flex cursor-pointer items-center gap-2.5 rounded-[8px] px-2 py-1.5 text-[13px]",b===p&&"bg-hover",n.disabled&&"cursor-not-allowed opacity-45"),children:[n.icon?t("span",{className:"grid size-7 shrink-0 place-items-center rounded-[7px] bg-surface text-muted",children:n.icon}):null,o("span",{className:"min-w-0 flex-1",children:[t("span",{className:x("block truncate text-text",S&&"font-medium"),children:t(ee,{text:n.label,q:g})}),n.hint?t("span",{className:"block truncate text-[11.5px] text-muted",children:n.hint}):null]}),t(q,{className:x("size-4 shrink-0 text-primary",!S&&"invisible")})]},n.value)})]},e||"_"))}),o("div",{className:"flex items-center justify-between border-t border-border bg-surface px-3 py-1.5 text-[11px] text-muted",children:[t("span",{className:"num",children:g?u.count(l.length,f.length):u.total(f.length)}),o("span",{className:"hidden items-center gap-1 sm:flex",children:[t("kbd",{className:"rounded border border-border bg-raised px-1 font-sans",children:"\u2191\u2193"}),t("kbd",{className:"rounded border border-border bg-raised px-1 font-sans",children:"\u21B5"}),t("kbd",{className:"rounded border border-border bg-raised px-1 font-sans",children:"esc"})]})]})]})]})}export{V as COMBOBOX_LABELS,ae as Combobox};
|