@waveinno/ui 0.9.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/README.md +6 -2
- package/bin/waveinno-ui.mjs +103 -8
- package/dist/components/patterns/index.d.ts +0 -3
- package/dist/components/patterns/index.js +1 -1
- package/dist/components/ui/index.d.ts +0 -1
- package/dist/components/ui/index.js +1 -1
- package/dist/lib/currency.js +1 -1
- package/package.json +13 -8
- package/dist/components/patterns/app-launcher.d.ts +0 -53
- package/dist/components/patterns/app-launcher.js +0 -1
- package/dist/components/patterns/kanban.d.ts +0 -28
- package/dist/components/patterns/kanban.js +0 -1
- package/dist/components/patterns/org-chart.d.ts +0 -51
- package/dist/components/patterns/org-chart.js +0 -1
- package/dist/components/ui/color-picker.d.ts +0 -14
- package/dist/components/ui/color-picker.js +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,27 @@ prop, major = a renamed or removed export or prop.
|
|
|
6
6
|
|
|
7
7
|
## Unreleased
|
|
8
8
|
|
|
9
|
+
## 1.0.1 — 2026-10-03
|
|
10
|
+
|
|
11
|
+
- `compact()` and `formatMoney(…, { compact: true })` drop trailing zeros: 2.5 crore shows as `2.5Cr`, not `2.50Cr`
|
|
12
|
+
(same for M and B).
|
|
13
|
+
|
|
14
|
+
## 1.0.0 — 2026-10-03
|
|
15
|
+
|
|
16
|
+
- **Breaking: four components move to Wavi Pro.** `OrgChart` (with `orgIds`, `OrgNode`), `KanbanBoard`, `AppLauncher`
|
|
17
|
+
(with `LauncherApp`) and `ColorPicker` / `ColorPickerPopover` are no longer exported by `@waveinno/ui`.
|
|
18
|
+
- **To keep using them:** run `npx @waveinno/ui add org-chart kanban app-launcher color-picker --key <licence>`.
|
|
19
|
+
It writes the source to `components/wavi-pro/`, installs `@radix-ui/react-popover` where needed, and prints the
|
|
20
|
+
new import lines. Then change `from "@waveinno/ui"` to those lines.
|
|
21
|
+
- **Unchanged:** the APIs, and the styles (they stay in `styles.css`).
|
|
22
|
+
- **Already-published versions:** up to 0.9.x stay MIT.
|
|
23
|
+
- **`npx @waveinno/ui add <id>`**: adds a Wavi Pro component to your project with your licence key (remembered after
|
|
24
|
+
the first success). `--list`, `--overwrite`, `--dir`, `--url`.
|
|
25
|
+
- Docs: the components menu is grouped by job (Actions, Forms, Date and time, Bangladesh, Overlays, Navigation…), with a
|
|
26
|
+
filter, sections that fold, and Pro and New badges; the components index uses the same groups. Pro component pages
|
|
27
|
+
show the live example to everyone and the source with a licence. The Kanban board has its own page.
|
|
28
|
+
- The CLI calls itself Wavi.
|
|
29
|
+
|
|
9
30
|
## 0.9.0 — 2026-10-03
|
|
10
31
|
|
|
11
32
|
- **`npx @waveinno/ui init`**: one-command setup.
|
package/README.md
CHANGED
|
@@ -75,10 +75,11 @@ through `labels` props, so each product passes its own language.
|
|
|
75
75
|
|
|
76
76
|
| Group | Components |
|
|
77
77
|
| --- | --- |
|
|
78
|
-
| 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), MultiSelect, Checkbox and CheckboxCard, 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 (right, left, bottom with drag to close), ConfirmDialog, DropdownMenu (rich items, header, dark), ContextMenu, Menu (soft, line, raised, gradient, dark, plus the shell's panel and rail; branches, sliding highlight), Popover (arrow, dark, brand), HoverCard, Tooltip, Alert (five tones, four looks), Toaster and toast, Spinner (ring, dots, wave), Kbd, Accordion, Pagination (seven looks), Steps (six looks), TreeView, ScrollArea, TagInput, Rating, Carousel,
|
|
78
|
+
| 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), MultiSelect, Checkbox and CheckboxCard, 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 (right, left, bottom with drag to close), ConfirmDialog, DropdownMenu (rich items, header, dark), ContextMenu, Menu (soft, line, raised, gradient, dark, plus the shell's panel and rail; branches, sliding highlight), Popover (arrow, dark, brand), HoverCard, Tooltip, Alert (five tones, four looks), Toaster and toast, Spinner (ring, dots, wave), Kbd, Accordion, Pagination (seven looks), Steps (six looks), TreeView, ScrollArea, TagInput, Rating, Carousel, Progress / ProgressRing, Avatar, Skeleton |
|
|
79
79
|
| Bangladesh | PhoneInput (+880, operator), TakaInput (lakh grouping, words), BdAddressPicker (divisions and districts, English or Bangla), PaymentMethodPicker (bKash, Nagad, Rocket, Upay, card, cash, bank, COD), PlateInput, NidInput; helpers for Bangla digits, the Bangla calendar and lakh/crore |
|
|
80
|
-
| Patterns |
|
|
80
|
+
| 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, empty / error / loading states |
|
|
81
81
|
| Charts | TrendChart, BarChart, LineChart, DonutChart, GaugeChart, Heatmap, BarList, ChartCard, validated palette |
|
|
82
|
+
| **Wavi Pro** | OrgChart (card, gradient, avatar, compact, list), KanbanBoard, AppLauncher (grid, list, spotlight, glass, dark), ColorPicker. Not in this package: added to your project with `npx @waveinno/ui add <id> --key <licence>` |
|
|
82
83
|
| Brand | WaveinnoLogo, WaveinnoSymbol, WaveMark (app icon: seven tones, round, 3D, animated), PoweredBy, Watermark; `@waveinno/ui/brand/waveinno-logo.svg` |
|
|
83
84
|
| Shell | AppShell (rail → panel → content → drawer, ⌘K), theme and density controls, record drawer |
|
|
84
85
|
|
|
@@ -87,8 +88,11 @@ through `labels` props, so each product passes its own language.
|
|
|
87
88
|
```bash
|
|
88
89
|
npm install
|
|
89
90
|
npm run dev # http://localhost:4500 — docs (/docs), blocks (/blocks), templates (/templates), theme builder (/themes), every component on one page (/playground), live from src/
|
|
91
|
+
npm run build && npm run start:production # production build, served on port 4500
|
|
90
92
|
```
|
|
91
93
|
|
|
94
|
+
Deploy the site to a VPS with Docker and nginx: [deploy/README.md](deploy/README.md).
|
|
95
|
+
|
|
92
96
|
Adding or changing a component: see [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
93
97
|
|
|
94
98
|
## Licence
|
package/bin/waveinno-ui.mjs
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* npx @waveinno/ui init set up
|
|
3
|
+
* npx @waveinno/ui init set up Wavi in a Next.js or React (Vite) app
|
|
4
4
|
* npx @waveinno/ui doctor check a setup and say what is missing
|
|
5
|
+
* npx @waveinno/ui add <id> add a Wavi Pro component (org-chart, app-launcher, kanban, color-picker)
|
|
5
6
|
*
|
|
6
7
|
* init asks what you are building (Next.js or React + Vite), creates the app
|
|
7
8
|
* if there is none, asks the currency and the package manager, shows every
|
|
@@ -19,6 +20,7 @@
|
|
|
19
20
|
*/
|
|
20
21
|
import { spawnSync } from "node:child_process";
|
|
21
22
|
import fs from "node:fs";
|
|
23
|
+
import os from "node:os";
|
|
22
24
|
import path from "node:path";
|
|
23
25
|
import readline from "node:readline";
|
|
24
26
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
@@ -60,7 +62,7 @@ const opt = (name) => {
|
|
|
60
62
|
return i >= 0 ? argv[i + 1] : undefined;
|
|
61
63
|
};
|
|
62
64
|
// The first word that is neither a flag nor the value of a flag that takes one.
|
|
63
|
-
const VALUED = new Set(["--cwd", "--pm", "--currency", "--framework", "--name"]);
|
|
65
|
+
const VALUED = new Set(["--cwd", "--pm", "--currency", "--framework", "--name", "--key", "--dir", "--url"]);
|
|
64
66
|
const command = argv.find((a, i) => !a.startsWith("-") && !VALUED.has(argv[i - 1] ?? "")) ?? "help";
|
|
65
67
|
const interactive = process.stdin.isTTY && process.stdout.isTTY && !flag("yes");
|
|
66
68
|
let cwd = path.resolve(opt("cwd") ?? process.cwd());
|
|
@@ -200,15 +202,15 @@ function setupCss(file, starter, create) {
|
|
|
200
202
|
const isStarter = STARTER.test(css) && css.length < 8000;
|
|
201
203
|
const next = withSource(starter, file);
|
|
202
204
|
if (isStarter || !css.trim()) return replace(file, `${next}\n`, "set up");
|
|
203
|
-
write(file, `${next}\n\n/* ── Your styles ── */\n${css.replace(/^@import ["']tailwindcss["'];\s*/m, "")}`, "add
|
|
205
|
+
write(file, `${next}\n\n/* ── Your styles ── */\n${css.replace(/^@import ["']tailwindcss["'];\s*/m, "")}`, "add Wavi to the top of");
|
|
204
206
|
// Your own variables with a token's name would recolour every component.
|
|
205
207
|
const clash = [...new Set([...css.matchAll(/(--(?:text|body|muted|border2?|primary|surface|canvas|raised|hover|accent|ink|brand-[12]|success|warning|danger|info)):/g)].map((m) => m[1]))];
|
|
206
|
-
if (clash.length) warn(`${file} sets ${clash.join(", ")}: the same names as
|
|
208
|
+
if (clash.length) warn(`${file} sets ${clash.join(", ")}: the same names as Wavi's tokens, so components change colour. Rename yours.`);
|
|
207
209
|
}
|
|
208
210
|
|
|
209
211
|
function stepsNext(pm, currency) {
|
|
210
212
|
const appDir = exists("src/app") ? "src/app" : exists("app") ? "app" : null;
|
|
211
|
-
if (!appDir) fail("This Next.js app has no app/ directory.
|
|
213
|
+
if (!appDir) fail("This Next.js app has no app/ directory. Wavi's setup is for the App Router.");
|
|
212
214
|
const d = deps(readPackage());
|
|
213
215
|
|
|
214
216
|
heading("Packages");
|
|
@@ -315,7 +317,7 @@ const CURRENCIES = [
|
|
|
315
317
|
const PMS = ["npm", "pnpm", "yarn", "bun"];
|
|
316
318
|
|
|
317
319
|
async function init() {
|
|
318
|
-
say(`\n ${magenta("◆")} ${bold("
|
|
320
|
+
say(`\n ${magenta("◆")} ${bold("Wavi")} ${dim(`v${PKG.version}`)} ${dim("— the setup")}`);
|
|
319
321
|
say(dim(` ${cwd}`));
|
|
320
322
|
|
|
321
323
|
// 1. What it is, or what to make.
|
|
@@ -391,10 +393,101 @@ async function init() {
|
|
|
391
393
|
say(dim(` Check the setup any time: npx @waveinno/ui doctor · ${DOCS}\n`));
|
|
392
394
|
}
|
|
393
395
|
|
|
396
|
+
/* ── add (Wavi Pro components) ─────────────────────────────────────────── */
|
|
397
|
+
|
|
398
|
+
/** Where the Pro source is served. Set WAVI_URL (or --url) to your own copy of the docs site. */
|
|
399
|
+
const PRO_URL = (opt("url") ?? process.env.WAVI_URL ?? "https://wavi.waveinno.com").replace(/\/$/, "");
|
|
400
|
+
const KEY_FILE = path.join(os.homedir(), ".wavi", "licence");
|
|
401
|
+
|
|
402
|
+
/** Keep a key the server accepted, for next time, readable only by you. */
|
|
403
|
+
function rememberKey(key) {
|
|
404
|
+
try {
|
|
405
|
+
fs.mkdirSync(path.dirname(KEY_FILE), { recursive: true });
|
|
406
|
+
fs.writeFileSync(KEY_FILE, key, { mode: 0o600 });
|
|
407
|
+
} catch {
|
|
408
|
+
// Not saved; it still worked this once.
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
function licenceKey() {
|
|
413
|
+
const given = opt("key") ?? process.env.WAVI_PRO_KEY;
|
|
414
|
+
if (given) return given.trim();
|
|
415
|
+
try {
|
|
416
|
+
return fs.readFileSync(KEY_FILE, "utf8").trim();
|
|
417
|
+
} catch {
|
|
418
|
+
return null;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
async function add() {
|
|
423
|
+
const ids = argv.filter((a, i) => !a.startsWith("-") && !VALUED.has(argv[i - 1] ?? "") && a !== "add");
|
|
424
|
+
say(`\n ${magenta("◆")} ${bold("Wavi Pro")} ${dim(`— adding ${ids.join(", ") || "components"}`)}`);
|
|
425
|
+
|
|
426
|
+
if (flag("list") || !ids.length) {
|
|
427
|
+
const r = await fetch(`${PRO_URL}/api/pro/component`).catch(() => null);
|
|
428
|
+
if (!r?.ok) fail(`Could not reach ${PRO_URL}. Check your connection, or pass --url.`);
|
|
429
|
+
const list = await r.json();
|
|
430
|
+
heading("Pro components");
|
|
431
|
+
for (const c of list) say(` ${cyan(c.id.padEnd(16))} ${c.title}`);
|
|
432
|
+
say(dim(`\n npx @waveinno/ui add <id> --key <your licence key>\n`));
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
const key = licenceKey();
|
|
437
|
+
if (!key) fail(`A Wavi Pro licence key is needed: npx @waveinno/ui add ${ids[0]} --key <key>\n Get one at ${PRO_URL}/pricing. The key is then remembered in ${KEY_FILE}.`);
|
|
438
|
+
|
|
439
|
+
const pkg = readPackage();
|
|
440
|
+
if (!pkg) fail("Run add inside your app (no package.json here).");
|
|
441
|
+
const d = deps(pkg);
|
|
442
|
+
if (!d["@waveinno/ui"]) warn("@waveinno/ui is not installed here yet: run npx @waveinno/ui init first.");
|
|
443
|
+
const fw = detect(pkg);
|
|
444
|
+
const dir = opt("dir") ?? (exists("src") && (fw === "vite" || exists("src/app")) ? "src/components/wavi-pro" : "components/wavi-pro");
|
|
445
|
+
const pm = opt("pm") ?? lockfilePm() ?? "npm";
|
|
446
|
+
dry = flag("dry-run");
|
|
447
|
+
|
|
448
|
+
const needed = new Set();
|
|
449
|
+
const imports = [];
|
|
450
|
+
for (const id of ids) {
|
|
451
|
+
const r = await fetch(`${PRO_URL}/api/pro/component`, {
|
|
452
|
+
method: "POST",
|
|
453
|
+
headers: { "content-type": "application/json", authorization: `Bearer ${key}` },
|
|
454
|
+
body: JSON.stringify({ id }),
|
|
455
|
+
}).catch(() => null);
|
|
456
|
+
if (!r) fail(`Could not reach ${PRO_URL}. Check your connection, or pass --url.`);
|
|
457
|
+
const data = await r.json().catch(() => ({}));
|
|
458
|
+
if (!r.ok) fail(data.error ?? `${id}: the server said ${r.status}.`);
|
|
459
|
+
rememberKey(key);
|
|
460
|
+
heading(data.title);
|
|
461
|
+
for (const f of data.files) {
|
|
462
|
+
const target = `${dir}/${f.path}`;
|
|
463
|
+
if (exists(target) && !flag("overwrite")) {
|
|
464
|
+
if (read(target) === f.content) skip(`${target} is up to date`);
|
|
465
|
+
else warn(`${target} exists and differs. Run with --overwrite to replace it (yours is kept as .bak).`);
|
|
466
|
+
continue;
|
|
467
|
+
}
|
|
468
|
+
if (exists(target)) replace(target, f.content, "update");
|
|
469
|
+
else write(target, f.content, "add");
|
|
470
|
+
}
|
|
471
|
+
for (const dep of data.dependencies ?? []) if (!d[dep]) needed.add(dep);
|
|
472
|
+
const names = (data.exports ?? []).filter((e) => !e.startsWith("type "));
|
|
473
|
+
const from = fw === "vite" ? `./components/wavi-pro/${data.files[0].path.replace(/\.tsx$/, "")}` : `@/${dir.replace(/^src\//, "")}/${data.files[0].path.replace(/\.tsx$/, "")}`;
|
|
474
|
+
imports.push(`import { ${names.join(", ")} } from "${from}";`);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
if (needed.size) {
|
|
478
|
+
heading("Packages");
|
|
479
|
+
install(pm, [...needed], false);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
say(`\n ${green("◆")} ${bold("Added.")} Import it:`);
|
|
483
|
+
for (const line of imports) say(` ${dim(line)}`);
|
|
484
|
+
say(dim(` The files are yours: covered by the Wavi Pro Licence, kept up to date with npx @waveinno/ui add <id> --overwrite.\n`));
|
|
485
|
+
}
|
|
486
|
+
|
|
394
487
|
/* ── doctor ─────────────────────────────────────────────────────────────── */
|
|
395
488
|
|
|
396
489
|
function doctor() {
|
|
397
|
-
say(`\n ${magenta("◆")} ${bold("
|
|
490
|
+
say(`\n ${magenta("◆")} ${bold("Wavi doctor")} ${dim(cwd)}\n`);
|
|
398
491
|
const pkg = readPackage();
|
|
399
492
|
if (!pkg) fail(`No package.json in ${cwd}.`);
|
|
400
493
|
const d = deps(pkg);
|
|
@@ -447,12 +540,14 @@ function doctor() {
|
|
|
447
540
|
if (flag("version") || command === "version") say(PKG.version);
|
|
448
541
|
else if (command === "init") await init();
|
|
449
542
|
else if (command === "doctor") doctor();
|
|
543
|
+
else if (command === "add") await add();
|
|
450
544
|
else {
|
|
451
545
|
say(`
|
|
452
|
-
${magenta("◆")} ${bold("
|
|
546
|
+
${magenta("◆")} ${bold("Wavi")} ${dim(`v${PKG.version}`)}
|
|
453
547
|
|
|
454
548
|
${bold("npx @waveinno/ui init")} set up a Next.js or React (Vite) app, or create one
|
|
455
549
|
${bold("npx @waveinno/ui doctor")} check the setup
|
|
550
|
+
${bold("npx @waveinno/ui add <id>")} add a Wavi Pro component (--list to see them; --key once)
|
|
456
551
|
|
|
457
552
|
${dim("--yes take the defaults, ask nothing")}
|
|
458
553
|
${dim("--framework next next or vite")}
|
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
/** Patterns: compositions of primitives that whole screens are built from. */
|
|
2
2
|
export * from "./activity-feed";
|
|
3
|
-
export * from "./app-launcher";
|
|
4
3
|
export * from "./data-table";
|
|
5
4
|
export * from "./data-table-cells";
|
|
6
5
|
export * from "./data-table-edit";
|
|
7
6
|
export * from "./data-table-filters";
|
|
8
7
|
export type { ColumnDef } from "@tanstack/react-table";
|
|
9
|
-
export * from "./kanban";
|
|
10
8
|
export * from "./kpi-card";
|
|
11
9
|
export * from "./money";
|
|
12
|
-
export * from "./org-chart";
|
|
13
10
|
export * from "./page-header";
|
|
14
11
|
export * from "./page-hero";
|
|
15
12
|
export * from "./page-shell";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export*from"./activity-feed.js";export*from"./
|
|
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"./kpi-card.js";export*from"./money.js";export*from"./page-header.js";export*from"./page-hero.js";export*from"./page-shell.js";export*from"./stat-card.js";export*from"./states.js";export*from"./timeline.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export*from"./accordion.js";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"./carousel.js";export*from"./checkbox.js";export*from"./
|
|
1
|
+
export*from"./accordion.js";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"./carousel.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"./hover-card.js";export*from"./input.js";export*from"./kbd.js";export*from"./label.js";export*from"./menu.js";export*from"./number-input.js";export*from"./pagination.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"./rating.js";export*from"./scroll-area.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"./steps.js";export*from"./switch.js";export*from"./tabs.js";export*from"./tag-input.js";export*from"./time-picker.js";export*from"./toast.js";export*from"./tooltip.js";export*from"./tree-view.js";export*from"./bd-address.js";export*from"./checkbox-card.js";export*from"./context-menu.js";export*from"./multi-select.js";export*from"./nid-input.js";export*from"./payment-method-picker.js";export*from"./phone-input.js";export*from"./plate-input.js";export*from"./taka-input.js";
|
package/dist/lib/currency.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const
|
|
1
|
+
const m=[{code:"BDT",symbol:"\u09F3",name:"Bangladeshi taka",decimals:2,grouping:"south-asian"},{code:"INR",symbol:"\u20B9",name:"Indian rupee",decimals:2,grouping:"south-asian"},{code:"PKR",symbol:"\u20A8",name:"Pakistani rupee",decimals:2,grouping:"south-asian"},{code:"LKR",symbol:"Rs",name:"Sri Lankan rupee",decimals:2,grouping:"south-asian"},{code:"NPR",symbol:"\u0930\u0942",name:"Nepalese rupee",decimals:2,grouping:"south-asian"},{code:"USD",symbol:"$",name:"US dollar",decimals:2,grouping:"western"},{code:"EUR",symbol:"\u20AC",name:"Euro",decimals:2,grouping:"western"},{code:"GBP",symbol:"\xA3",name:"Pound sterling",decimals:2,grouping:"western"},{code:"AED",symbol:"\u062F.\u0625",name:"UAE dirham",decimals:2,grouping:"western"},{code:"SAR",symbol:"\uFDFC",name:"Saudi riyal",decimals:2,grouping:"western"},{code:"MYR",symbol:"RM",name:"Malaysian ringgit",decimals:2,grouping:"western"},{code:"SGD",symbol:"S$",name:"Singapore dollar",decimals:2,grouping:"western"},{code:"AUD",symbol:"A$",name:"Australian dollar",decimals:2,grouping:"western"},{code:"CAD",symbol:"C$",name:"Canadian dollar",decimals:2,grouping:"western"},{code:"CNY",symbol:"\xA5",name:"Chinese yuan",decimals:2,grouping:"western"},{code:"JPY",symbol:"\xA5",name:"Japanese yen",decimals:0,grouping:"western"}],i=Object.fromEntries(m.map(e=>[e.code,e])),u=i.BDT,s={"south-asian":"en-IN",western:"en-US"};function g(e,n){return new Intl.NumberFormat(s[n.grouping],{minimumFractionDigits:n.decimals,maximumFractionDigits:n.decimals}).format(e)}function c(e,n){return new Intl.NumberFormat(s[n]).format(e)}function l(e,n){const o=Math.abs(e),r=(t,a)=>String(Number(t.toFixed(a)));if(n.grouping==="south-asian"){if(o>=1e7)return`${r(e/1e7,2)}Cr`;if(o>=1e5)return`${r(e/1e5,1)}L`;if(o>=1e3)return`${r(e/1e3,1)}K`}else{if(o>=1e9)return`${r(e/1e9,2)}B`;if(o>=1e6)return`${r(e/1e6,2)}M`;if(o>=1e3)return`${r(e/1e3,1)}K`}return c(e,n.grouping)}function p(e,n,o={}){const r=o.compact?l(e,n):g(e,n);return n.position==="before"?`${n.symbol}${r}`:`${r} ${n.symbol}`}function d(e,n={}){const o=i[e]??u;return{code:o.code,symbol:n.symbol??o.symbol,decimals:n.decimals??o.decimals,grouping:n.grouping??o.grouping,position:n.position??"before"}}export{m as CURRENCIES,i as CURRENCY_MAP,u as DEFAULT_CURRENCY,l as compact,d as configFor,p as formatMoney,g as groupDigits,c as groupPlain};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@waveinno/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Wavi: the React design system from Bangladesh. 100+ components, app shell, charts, and bKash/taka/Bangla-ready inputs for Next.js and Vite, with Tailwind CSS 4. Set up with npx @waveinno/ui init.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"waveinno",
|
|
@@ -62,14 +62,18 @@
|
|
|
62
62
|
],
|
|
63
63
|
"scripts": {
|
|
64
64
|
"dev": "npm run dev -w playground",
|
|
65
|
-
"build": "
|
|
66
|
-
"
|
|
67
|
-
"build:
|
|
65
|
+
"build": "npm run build:lib && npm run build:site",
|
|
66
|
+
"build:lib": "node scripts/build.mjs",
|
|
67
|
+
"build:site": "npm run build -w playground",
|
|
68
|
+
"start": "npm run start -w playground",
|
|
69
|
+
"start:production": "npm run start:production -w playground",
|
|
70
|
+
"test:dist": "npm run build:lib && cross-env UI_FROM=dist npm run build -w playground",
|
|
68
71
|
"new": "node scripts/new-component.mjs",
|
|
69
72
|
"typecheck": "tsc --noEmit",
|
|
70
|
-
"
|
|
71
|
-
"
|
|
72
|
-
"
|
|
73
|
+
"test": "vitest run",
|
|
74
|
+
"check": "npm run typecheck && node scripts/check-exports.mjs && npm run typecheck -w playground && npm test",
|
|
75
|
+
"prepublishOnly": "npm run check && npm run build:lib",
|
|
76
|
+
"pack:local": "npm run build:lib && node -e \"require('fs').mkdirSync('packs',{recursive:true})\" && npm pack --pack-destination ./packs"
|
|
73
77
|
},
|
|
74
78
|
"publishConfig": {
|
|
75
79
|
"access": "public",
|
|
@@ -125,7 +129,8 @@
|
|
|
125
129
|
"react": "^19.3.0",
|
|
126
130
|
"react-dom": "^19.3.0",
|
|
127
131
|
"tailwindcss": "^4.3.3",
|
|
128
|
-
"typescript": "^7.0.2"
|
|
132
|
+
"typescript": "^7.0.2",
|
|
133
|
+
"vitest": "^3.2.7"
|
|
129
134
|
},
|
|
130
135
|
"bin": {
|
|
131
136
|
"waveinno-ui": "bin/waveinno-ui.mjs"
|
|
@@ -1,53 +0,0 @@
|
|
|
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;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use client";import{Fragment as _,jsx as e,jsxs as i}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-[var(--ink)] bg-[image:radial-gradient(120%_60%_at_0%_0%,color-mix(in_srgb,var(--brand-2)_32%,transparent),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:a}){return e("svg",{viewBox:"0 0 18 18",className:"size-[18px] shrink-0","aria-hidden":!0,children:Array.from({length:9},(n,s)=>{const r=Math.floor(s/3),o=s%3,p=r===o||r+o===2,g=a&&p?(1-o)*1.6:0,u=a&&p?(1-r)*1.6:0;return e("circle",{cx:3+o*6,cy:3+r*6,r:1.7,fill:"currentColor",style:{transform:`translate(${g}px, ${u}px) scale(${a&&!p?0:1})`,transformOrigin:`${3+o*6}px ${3+r*6}px`,transition:`transform 260ms cubic-bezier(.3,.7,.2,1) ${a?s*12:(8-s)*12}ms`}},s)})})}function x({app:a,size:n,glow:s}){const r={sm:"size-8 rounded-[calc(9px*var(--radius-scale))] [&_svg]:size-4",md:"size-12 rounded-[calc(14px*var(--radius-scale))] [&_svg]:size-[22px]",lg:"size-14 rounded-[calc(16px*var(--radius-scale))] [&_svg]:size-6"}[n];return a.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:a.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)${s?", 0 10px 24px -10px color-mix(in srgb, var(--brand-2) 70%, transparent)":""}`},children:a.icon}):e("span",{className:c("grid shrink-0 place-items-center [&>img]:size-full [&>*]:max-h-full [&>*]:max-w-full",r),children:a.icon})}function j(a){return n=>{const s=Array.from(n.currentTarget.querySelectorAll("[data-launcher-app]:not([aria-disabled='true'])")),r=s.indexOf(document.activeElement);if(r<0)return;const o={ArrowRight:1,ArrowLeft:-1,ArrowDown:a,ArrowUp:-a}[n.key];let p;o!==void 0?p=s[Math.min(Math.max(r+o,0),s.length-1)]:n.key==="Home"?p=s[0]:n.key==="End"&&(p=s[s.length-1]),p&&(n.preventDefault(),p.focus())}}function y({app:a,current:n,onChoose:s,className:r,children:o,onFocus:p,onMouseEnter:g}){const u={"data-launcher-app":"","aria-current":n?"page":void 0,"aria-disabled":a.disabled||void 0,className:c("outline-none focus-visible:ring-2 focus-visible:ring-primary/45",a.disabled&&"pointer-events-none opacity-45",r),onFocus:p,onMouseEnter:g};return a.href&&!a.disabled?e("a",{href:a.href,...u,onClick:()=>s(a),children:o}):e("button",{type:"button",disabled:a.disabled,...u,onClick:()=>s(a),children:o})}function W({apps:a,current:n,onSelect:s,variant:r="grid",trigger:o="icon",title:p="Apps",action:g,footer:u,align:C="end",side:O="bottom",open:k,onOpenChange:S,className:D}){const[H,$]=N.useState(!1),h=k??H,A=t=>{k===void 0&&$(t),S?.(t)},m=a.find(t=>t.id===n),[K,L]=N.useState(n),l=a.find(t=>t.id===K)??m??a[0],z=r==="grid"||r==="glass"||r==="dark",R=j(z?3:1),f=N.useRef(null),v=t=>{s?.(t),A(!1)},P=i("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}),g]}),q=e("div",{ref:f,role:"list",onKeyDown:R,className:"grid grid-cols-3 gap-1",children:a.map(t=>{const d=t.id===n;return e("div",{role:"listitem",children:i(y,{app:t,current:d,onChoose:v,className:c("group/app relative flex w-full cursor-pointer flex-col items-center gap-2 rounded-[calc(12px*var(--radius-scale))] 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:[i("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:f,role:"list",onKeyDown:R,className:c("flex flex-col gap-0.5",r==="spotlight"&&"p-1.5"),children:a.map(t=>{const d=t.id===n,V=r==="spotlight"&&t.id===l?.id;return e("div",{role:"listitem",children:i(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-[calc(10px*var(--radius-scale))] 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"}),i("span",{className:"min-w-0 flex-1",children:[i("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=i(_,{children:[P,i("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?i("div",{className:"hidden flex-col p-5 animate-[wave-in_220ms_var(--ease)] sm:flex",children:[e(x,{app:l,size:"lg",glow:!0}),i("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===n?i("p",{className:"flex items-center gap-1.5 text-[12px] font-medium text-primary",children:[e(w,{className:"size-3.5"})," You are here"]}):i(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,var(--brand-1),var(--brand-2))] px-3.5 text-[12.5px] font-medium text-white shadow-[inset_0_1px_0_rgba(255,255,255,.25),0_6px_14px_-6px_color-mix(in_srgb,var(--brand-1)_70%,transparent)] transition-[filter] hover:brightness-110",children:["Open ",l.name," ",e(M,{className:"size-3.5"})]})})]},l.id):null]}),u?e("div",{className:"border-t border-border bg-surface px-4 py-2.5 text-[12px] text-muted",children:u}):null]});return i(b.Root,{open:h,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[o],D),children:o==="chip"&&m?i(_,{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:h})})]}):e(T,{open:h})}),e(b.Portal,{children:e(b.Content,{"data-pop":"",align:C,side:O,sideOffset:8,collisionPadding:12,onOpenAutoFocus:t=>{t.preventDefault();const d=f.current;(d?.querySelector("[data-launcher-app][aria-current]")??d?.querySelector("[data-launcher-app]"))?.focus()},className:c("z-50 overflow-hidden rounded-[calc(18px*var(--radius-scale))] border p-2.5 shadow-elev3 outline-none",G[r]),children:r==="spotlight"?F:i(_,{children:[e("div",{className:"px-1 pt-1",children:P}),z?q:E,u?e("div",{className:"mt-2 border-t border-border px-1 pt-2.5 text-[12px] text-muted",children:u}):null]})})})]})}export{W as AppLauncher};
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
export interface KanbanColumn {
|
|
3
|
-
id: string;
|
|
4
|
-
label: string;
|
|
5
|
-
/** Accent for the column rule and the count chip. */
|
|
6
|
-
color: string;
|
|
7
|
-
}
|
|
8
|
-
/**
|
|
9
|
-
* Kanban board — the pipeline surface from docs/crm-module.md §3.
|
|
10
|
-
*
|
|
11
|
-
* Native HTML5 drag and drop, with a keyboard path that does the same job:
|
|
12
|
-
* focus a card and press ← / → to move it a stage. A board that can only be
|
|
13
|
-
* operated with a mouse is a board half the organisation cannot use, and in a
|
|
14
|
-
* sales tool the stage change *is* the record update.
|
|
15
|
-
*
|
|
16
|
-
* Columns keep a fixed width and scroll horizontally. Making them flex would
|
|
17
|
-
* squeeze seven stages into unreadable slivers on a laptop.
|
|
18
|
-
*/
|
|
19
|
-
export declare function KanbanBoard<T>({ columns, items, getId, getColumn, onMove, renderCard, renderColumnSummary, className, }: {
|
|
20
|
-
columns: KanbanColumn[];
|
|
21
|
-
items: T[];
|
|
22
|
-
getId: (item: T) => string;
|
|
23
|
-
getColumn: (item: T) => string;
|
|
24
|
-
onMove: (id: string, toColumn: string) => void;
|
|
25
|
-
renderCard: (item: T) => React.ReactNode;
|
|
26
|
-
renderColumnSummary?: (column: KanbanColumn, items: T[]) => React.ReactNode;
|
|
27
|
-
className?: string;
|
|
28
|
-
}): React.JSX.Element;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use client";import{jsx as a,jsxs as g}from"react/jsx-runtime";import*as h from"react";import{cn as c}from"../../lib/utils.js";function k({columns:l,items:m,getId:b,getColumn:p,onMove:f,renderCard:N,renderColumnSummary:x,className:y}){const[d,u]=h.useState(null),[D,i]=h.useState(null),v=(e,o)=>{const s=m.find(n=>b(n)===e);if(!s)return;const r=l.findIndex(n=>n.id===p(s)),t=l[r+o];t&&f(e,t.id)};return a("div",{className:c("flex gap-3 overflow-x-auto pb-3",y),children:l.map(e=>{const o=m.filter(r=>p(r)===e.id),s=D===e.id;return g("section",{"aria-label":e.label,onDragOver:r=>{r.preventDefault(),i(e.id)},onDragLeave:()=>i(r=>r===e.id?null:r),onDrop:r=>{r.preventDefault(),d&&f(d,e.id),u(null),i(null)},className:c("flex w-[268px] shrink-0 flex-col rounded-card border bg-surface transition-colors",s?"border-primary bg-selected":"border-border"),children:[g("header",{className:"border-b border-border px-3 py-2.5",children:[g("div",{className:"flex items-center gap-2",children:[a("span",{className:"h-3.5 w-1 shrink-0 rounded-full",style:{background:e.color},"aria-hidden":!0}),a("h3",{className:"flex-1 truncate text-[13px] font-semibold text-text",children:e.label}),a("span",{className:"num rounded-full bg-hover px-1.5 text-[11px] font-medium leading-[18px] text-muted",children:o.length})]}),x?a("div",{className:"mt-1.5",children:x(e,o)}):null]}),a("div",{className:"flex min-h-24 flex-1 flex-col gap-2 p-2",children:o.length===0?a("p",{className:"px-2 py-6 text-center text-xs text-muted",children:"Nothing at this stage"}):o.map(r=>{const t=b(r);return a("div",{tabIndex:0,role:"button",draggable:!0,onDragStart:()=>u(t),onDragEnd:()=>{u(null),i(null)},onKeyDown:n=>{n.key==="ArrowRight"?(n.preventDefault(),v(t,1)):n.key==="ArrowLeft"&&(n.preventDefault(),v(t,-1))},className:c("cursor-grab rounded-ctl border border-border bg-raised p-2.5 text-left","transition-[box-shadow,border-color,opacity] duration-150","hover:border-border2 hover:shadow-elev2 active:cursor-grabbing",d===t&&"opacity-40"),children:N(r)},t)})})]},e.id)})})}export{k as KanbanBoard};
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
/**
|
|
3
|
-
* OrgChart — who reports to whom: a company, a site's staff, a franchise.
|
|
4
|
-
*
|
|
5
|
-
* A top-down tree with rounded connectors. Each person with reports carries a
|
|
6
|
-
* count that folds and unfolds their team. Opening a team never moves the
|
|
7
|
-
* person you clicked: the chart is re-anchored under them before the frame is
|
|
8
|
-
* painted, the connector draws down, the team springs in one by one, and the
|
|
9
|
-
* view glides to show all of it. Closing plays the same in reverse. Drag the
|
|
10
|
-
* background to pan (it glides on when let go), Ctrl/⌘ + wheel or the toolbar
|
|
11
|
-
* to zoom; Fit, Expand all and Collapse all are one click. Departments get a
|
|
12
|
-
* colour (pass `departments`, or one is chosen from the name).
|
|
13
|
-
*
|
|
14
|
-
* Looks:
|
|
15
|
-
* card — the default: a card per person with photo, role and department.
|
|
16
|
-
* gradient — the top of the tree in the brand gradient, department-coloured cards below.
|
|
17
|
-
* avatar — photo-first: large round portraits with names under them.
|
|
18
|
-
* compact — small pills, for large organisations.
|
|
19
|
-
* list — an indented outline with guide lines, for phones and very wide teams.
|
|
20
|
-
*/
|
|
21
|
-
export type OrgChartVariant = "card" | "gradient" | "avatar" | "compact" | "list";
|
|
22
|
-
export interface OrgNode {
|
|
23
|
-
id: string;
|
|
24
|
-
name: string;
|
|
25
|
-
title?: string;
|
|
26
|
-
department?: string;
|
|
27
|
-
/** Photo URL; without one, initials. */
|
|
28
|
-
avatar?: string;
|
|
29
|
-
/** Beside the name: "New", "On leave". */
|
|
30
|
-
badge?: React.ReactNode;
|
|
31
|
-
children?: OrgNode[];
|
|
32
|
-
}
|
|
33
|
-
export interface OrgChartProps {
|
|
34
|
-
data: OrgNode;
|
|
35
|
-
variant?: OrgChartVariant;
|
|
36
|
-
/** Department → colour. Departments not listed get one from a palette. */
|
|
37
|
-
departments?: Record<string, string>;
|
|
38
|
-
/** Ids folded at first. Or `collapseDepth` folds everything below a depth. */
|
|
39
|
-
defaultCollapsed?: string[];
|
|
40
|
-
collapseDepth?: number;
|
|
41
|
-
selected?: string;
|
|
42
|
-
onSelect?: (node: OrgNode) => void;
|
|
43
|
-
/** Height of the chart box (not used by `list`). */
|
|
44
|
-
height?: number;
|
|
45
|
-
className?: string;
|
|
46
|
-
}
|
|
47
|
-
declare const ids: (n: OrgNode) => string[];
|
|
48
|
-
export declare function OrgChart({ data, variant, departments, defaultCollapsed, collapseDepth, selected, onSelect, height, className, }: OrgChartProps): React.JSX.Element;
|
|
49
|
-
/** Every id in a tree: handy for "expand all" outside the chart. */
|
|
50
|
-
export declare const orgIds: typeof ids;
|
|
51
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use client";import{jsx as r,jsxs as d}from"react/jsx-runtime";import{ChevronDown as ie,ChevronRight as oe,ChevronsDownUp as le,ChevronsUpDown as ce,Maximize2 as de,Minus as ue,Plus as pe}from"lucide-react";import*as h from"react";import{cn as w}from"../../lib/utils.js";import{Avatar as I}from"../ui/avatar.js";const re=["#4f46e5","#0ea5e9","#10b981","#f59e0b","#e11d48","#7c3aed","#0d9488","#ea580c"],ge=t=>[...t].reduce((_,y)=>_*31+y.charCodeAt(0)>>>0,7),j=t=>(t.children??[]).reduce((_,y)=>_+1+j(y),0),ne=t=>[t.id,...(t.children??[]).flatMap(ne)],me=220,U=()=>typeof window<"u"&&window.matchMedia("(prefers-reduced-motion: reduce)").matches;function we({data:t,variant:_="card",departments:y,defaultCollapsed:v,collapseDepth:C,selected:k,onSelect:$,height:R=520,className:o}){const c=h.useMemo(()=>{if(v)return new Set(v);if(C===void 0)return new Set;const s=new Set,x=(O,N)=>{N>=C&&O.children?.length&&s.add(O.id),O.children?.forEach(P=>x(P,N+1))};return x(t,0),s},[t,v,C]),[p,a]=h.useState(c),g=s=>s.department?y?.[s.department]??re[ge(s.department)%re.length]:"var(--primary)";return _==="list"?r(xe,{data:t,folded:p,toggle:x=>a(O=>{const N=new Set(O);return N.has(x)?N.delete(x):N.add(x),N}),tone:g,selected:k,onSelect:$,className:o}):r(be,{data:t,variant:_,folded:p,setFolded:a,tone:g,selected:k,onSelect:$,height:R,className:o})}function be({data:t,variant:_,folded:y,setFolded:v,tone:C,selected:k,onSelect:$,height:R,className:o}){const c=h.useRef(null),p=h.useRef(null),[a,g]=h.useState({x:0,y:28,z:1}),[s,x]=h.useState(!1),[O,N]=h.useState(new Set),[P,H]=h.useState(!1),S=h.useRef(a);S.current=a;const E=h.useRef(null),T=h.useRef(null),m=h.useRef(void 0),A=e=>p.current?.querySelector(`[data-org-card="${CSS.escape(e)}"]`)?.getBoundingClientRect(),J=(e,n)=>{x(n&&!U()),g(e)},D=h.useCallback(e=>{const n=c.current,u=p.current;if(!n||!u)return;const l=u.offsetWidth,i=u.offsetHeight,b=Math.max(.3,Math.min(1,(n.clientWidth-64)/l,(n.clientHeight-64)/i));x(e&&!U()),g({x:(n.clientWidth-l*b)/2,y:Math.max(28,(n.clientHeight-i*b)/2),z:b})},[]);h.useLayoutEffect(()=>D(!1),[D]);const se=e=>{const n=A(e);if(y.has(e)){n&&(E.current={id:e,x:n.left,y:n.top,reveal:!0}),v(l=>{const i=new Set(l);return i.delete(e),i});return}N(l=>new Set(l).add(e)),window.setTimeout(()=>{const l=A(e);l&&(E.current={id:e,x:l.left,y:l.top,reveal:!1}),v(i=>new Set(i).add(e)),N(i=>{const b=new Set(i);return b.delete(e),b})},U()?0:me)},Q=e=>{const n=A(t.id);n&&(E.current={id:t.id,x:n.left,y:n.top,reveal:!1}),v(e),requestAnimationFrame(()=>requestAnimationFrame(()=>D(!0)))};h.useLayoutEffect(()=>{const e=E.current;if(!e)return;E.current=null;const n=A(e.id);if(!n)return;const u=S.current,l={...u,x:u.x+(e.x-n.left),y:u.y+(e.y-n.top)};x(!1),g(l),e.reveal&&window.setTimeout(()=>{const i=c.current,b=p.current?.querySelector(`[data-org-li="${CSS.escape(e.id)}"]`);if(!i||!b)return;const f=i.getBoundingClientRect(),L=b.getBoundingClientRect(),M=40,z=S.current;let F=z.z;L.width>f.width-M*2&&(F=Math.max(.35,z.z*((f.width-M*2)/L.width)));const q=F/z.z,V=(L.left-f.left-z.x)*q+z.x,K=(L.top-f.top-z.y)*q+z.y,W=L.width*q,te=L.height*q;let X=z.x,Z=z.y;W>f.width-M*2?X+=f.width/2-(V+W/2):V<M?X+=M-V:V+W>f.width-M&&(X+=f.width-M-(V+W)),K+te>f.height-M&&(Z+=Math.max(M-K,f.height-M-(K+te))),(X!==z.x||Z!==z.y||F!==z.z)&&J({x:X,y:Z,z:F},!0)},60)},[y]);const B=(e,n,u)=>{const l=c.current;if(!l)return;const i=S.current,b=Math.min(1.6,Math.max(.3,e)),f=n??l.clientWidth/2,L=u??l.clientHeight/2;J({z:b,x:f-(f-i.x)/i.z*b,y:L-(L-i.y)/i.z*b},!0)},ee=()=>{m.current&&cancelAnimationFrame(m.current),m.current=void 0};h.useEffect(()=>ee,[]);const Y="grid size-8 cursor-pointer place-items-center rounded-[calc(8px*var(--radius-scale))] text-muted outline-none transition-[background-color,color,transform] hover:bg-hover hover:text-text active:scale-90 focus-visible:ring-2 focus-visible:ring-primary/40 [&_svg]:size-4",G="520ms cubic-bezier(.22,1,.36,1)";return d("div",{ref:c,className:w("relative touch-none select-none overflow-hidden rounded-[calc(16px*var(--radius-scale))] border border-border bg-surface",P?"cursor-grabbing":"cursor-grab",o),style:{height:R,backgroundImage:"radial-gradient(color-mix(in srgb, var(--border2) 60%, transparent) 1px, transparent 1px)",backgroundSize:`${20*a.z}px ${20*a.z}px`,backgroundPosition:`${a.x}px ${a.y}px`,transition:s?`background-position ${G}, background-size ${G}`:void 0},onPointerDown:e=>{e.target.closest("button, a, input")||(ee(),x(!1),H(!0),T.current={x:e.clientX,y:e.clientY,vx:a.x,vy:a.y,t:performance.now(),speedX:0,speedY:0},e.currentTarget.setPointerCapture(e.pointerId))},onPointerMove:e=>{const n=T.current;if(!n)return;const u=performance.now(),l=n.vx+e.clientX-n.x,i=n.vy+e.clientY-n.y,b=S.current,f=Math.max(1,u-n.t);n.speedX=n.speedX*.6+(l-b.x)/f*.4,n.speedY=n.speedY*.6+(i-b.y)/f*.4,n.t=u,g({...b,x:l,y:i})},onPointerUp:()=>{const e=T.current;if(T.current=null,H(!1),!e||U())return;let n=e.speedX*16,u=e.speedY*16;if(Math.hypot(n,u)<1.5||performance.now()-e.t>80)return;const l=()=>{n*=.92,u*=.92,g(i=>({...i,x:i.x+n,y:i.y+u})),m.current=Math.hypot(n,u)>.4?requestAnimationFrame(l):void 0};m.current=requestAnimationFrame(l)},onWheel:e=>{if(!(e.ctrlKey||e.metaKey))return;e.preventDefault();const n=c.current.getBoundingClientRect();B(S.current.z*(e.deltaY<0?1.12:1/1.12),e.clientX-n.left,e.clientY-n.top)},onDoubleClick:e=>{if(e.target.closest("button, a, input"))return;const n=c.current.getBoundingClientRect();B(S.current.z*1.4,e.clientX-n.left,e.clientY-n.top)},children:[r("div",{ref:p,className:"wm-org absolute left-0 top-0 w-max origin-top-left will-change-transform",style:{transform:`translate3d(${a.x}px, ${a.y}px, 0) scale(${a.z})`,transition:s?`transform ${G}`:void 0,"--org-line":_==="gradient"?"color-mix(in srgb, var(--primary) 55%, var(--border2))":void 0},onTransitionEnd:e=>{e.target===e.currentTarget&&x(!1)},children:r("ul",{children:r(ae,{node:t,index:0,depth:0,variant:_,folded:y,leaving:O,toggle:se,tone:C,selected:k,onSelect:$})})}),d("div",{className:"absolute right-3 top-3 flex items-center gap-0.5 rounded-[calc(11px*var(--radius-scale))] border border-border bg-raised/90 p-1 shadow-[var(--lit),0_4px_14px_-6px_rgba(5,8,22,.25)] backdrop-blur",children:[r("button",{type:"button","aria-label":"Zoom out",className:Y,onClick:()=>B(a.z/1.25),children:r(ue,{})}),d("span",{className:"num w-11 text-center text-[11.5px] font-medium tabular-nums text-muted",children:[Math.round(a.z*100),"%"]}),r("button",{type:"button","aria-label":"Zoom in",className:Y,onClick:()=>B(a.z*1.25),children:r(pe,{})}),r("span",{className:"mx-0.5 h-5 w-px bg-border"}),r("button",{type:"button","aria-label":"Fit to the box",className:Y,onClick:()=>D(!0),children:r(de,{})}),r("button",{type:"button","aria-label":"Expand everyone",className:Y,onClick:()=>Q(new Set),children:r(ce,{})}),r("button",{type:"button","aria-label":"Collapse to the top",className:Y,onClick:()=>Q(new Set((t.children??[]).filter(e=>e.children?.length).map(e=>e.id))),children:r(le,{})})]})]})}function ae({node:t,index:_,depth:y,variant:v,folded:C,leaving:k,toggle:$,tone:R,selected:o,onSelect:c}){const p=t.children??[],a=p.length>0&&!C.has(t.id),g=k.has(t.id),s=R(t),x=o===t.id,O=j(t),N=p.length?d("button",{type:"button",onClick:()=>$(t.id),"aria-expanded":a&&!g,"aria-label":`${a&&!g?"Hide":"Show"} ${t.name}'s team (${O})`,className:w("absolute -bottom-3 left-1/2 z-10 flex h-6 -translate-x-1/2 cursor-pointer items-center gap-0.5 rounded-full border px-2 text-[11px] font-semibold outline-none","shadow-[0_2px_6px_-2px_rgba(5,8,22,.25)] transition-[background-color,color,border-color,transform,box-shadow] duration-300 ease-[cubic-bezier(.3,1.4,.5,1)] hover:scale-110 active:scale-95 focus-visible:ring-2 focus-visible:ring-primary/40",a&&!g?"border-border bg-raised text-muted hover:text-text":"border-transparent bg-[linear-gradient(135deg,var(--brand-1),var(--brand-2))] text-white hover:shadow-[0_6px_16px_-6px_var(--brand-1)]"),children:[r("span",{className:"num",children:O}),r(ie,{className:w("size-3 transition-transform duration-300 ease-[cubic-bezier(.3,1.4,.5,1)]",a&&!g&&"rotate-180")})]}):null,P=c?()=>c(t):void 0,H=c?"button":"div",S=c?{type:"button",onClick:P}:{},E="transition-[border-color,box-shadow,transform] duration-300 ease-[cubic-bezier(.22,1,.36,1)]";let T;if(v==="avatar")T=d("div",{"data-org-card":t.id,className:"relative flex w-36 flex-col items-center pb-4 text-center",children:[r(H,{...S,className:w("relative rounded-full outline-none focus-visible:ring-2 focus-visible:ring-primary/50",E,c&&"cursor-pointer hover:-translate-y-1"),children:r("span",{className:w("block rounded-full p-[3px] transition-shadow duration-300",x&&"shadow-[0_0_0_5px_var(--primary-subtle),0_12px_28px_-12px_var(--primary)]"),style:{background:`conic-gradient(from 210deg, ${s}, color-mix(in srgb, ${s} 30%, transparent), ${s})`},children:r("span",{className:"block rounded-full bg-raised p-[3px]",children:r(I,{name:t.name,src:t.avatar,size:"xl"})})})}),r("p",{className:"mt-2 text-[13.5px] font-semibold leading-5 text-text",children:t.name}),t.title?r("p",{className:"text-[11.5px] leading-4 text-muted",children:t.title}):null,t.badge?r("div",{className:"mt-1.5",children:t.badge}):null,N]});else if(v==="compact")T=d("div",{"data-org-card":t.id,className:"relative pb-3",children:[d(H,{...S,className:w("flex h-10 max-w-[200px] items-center gap-2 rounded-full border bg-raised pl-1.5 pr-3.5 text-left shadow-[var(--lit),0_1px_2px_rgba(5,8,22,.06)] outline-none focus-visible:ring-2 focus-visible:ring-primary/40",E,x?"border-primary shadow-[0_0_0_3px_var(--primary-subtle)]":"border-border hover:border-border2",c&&"cursor-pointer hover:-translate-y-0.5 hover:shadow-[var(--lit),0_8px_18px_-10px_rgba(5,8,22,.3)]"),children:[r(I,{name:t.name,src:t.avatar,size:"md"}),d("span",{className:"min-w-0",children:[r("span",{className:"block truncate text-[12.5px] font-semibold leading-4 text-text",children:t.name}),t.title?r("span",{className:"block truncate text-[10.5px] leading-[14px] text-muted",children:t.title}):null]}),r("span",{className:"ml-1 size-2 shrink-0 rounded-full",style:{background:s}})]}),N]});else{const m=v==="gradient"&&y===0;T=d("div",{"data-org-card":t.id,className:"relative pb-3",children:[d(H,{...S,className:w("relative flex w-[212px] flex-col overflow-hidden rounded-[calc(14px*var(--radius-scale))] border text-left outline-none focus-visible:ring-2 focus-visible:ring-primary/40",E,m?"border-transparent bg-[linear-gradient(140deg,var(--brand-1),color-mix(in_srgb,var(--brand-1)_78%,#000)_45%,var(--brand-2))] text-white shadow-[inset_0_1px_0_rgba(255,255,255,.25),0_16px_34px_-16px_var(--brand-1)] [--muted:rgba(255,255,255,.75)] [--text:#fff]":"border-border bg-raised shadow-[var(--lit),0_1px_2px_rgba(5,8,22,.05),0_8px_20px_-14px_rgba(5,8,22,.25)]",x&&!m&&"border-primary shadow-[0_0_0_3px_var(--primary-subtle),0_14px_30px_-16px_color-mix(in_srgb,var(--primary)_65%,transparent)]",c&&"cursor-pointer hover:-translate-y-1 hover:shadow-[var(--lit),0_18px_36px_-18px_rgba(5,8,22,.35)]"),children:[m?null:r("span",{"aria-hidden":!0,className:"h-[3px] w-full",style:{background:v==="gradient"?`linear-gradient(90deg, ${s}, color-mix(in srgb, ${s} 40%, transparent))`:s}}),d("span",{className:"flex items-center gap-3 p-3",children:[r("span",{className:w("rounded-full",m&&"ring-2 ring-white/40"),style:m?void 0:{boxShadow:`0 0 0 2px var(--raised), 0 0 0 3.5px color-mix(in srgb, ${s} 55%, transparent)`},children:r(I,{name:t.name,src:t.avatar,size:"lg"})}),d("span",{className:"min-w-0 flex-1",children:[d("span",{className:"flex items-center gap-1.5",children:[r("span",{className:"truncate text-[13.5px] font-semibold text-text",children:t.name}),t.badge]}),t.title?r("span",{className:"block truncate text-[12px] text-muted",children:t.title}):null]})]}),t.department?d("span",{className:w("mx-3 mb-3 inline-flex w-fit items-center gap-1.5 rounded-full px-2 py-0.5 text-[10.5px] font-semibold",m&&"bg-white/15 text-white"),style:m?void 0:{background:`color-mix(in srgb, ${s} 12%, transparent)`,color:s},children:[r("span",{className:"size-1.5 rounded-full",style:{background:m?"#fff":s}}),t.department]}):null]}),N]})}return d("li",{"data-org-li":t.id,style:{"--i":_},children:[T,a?r("ul",{className:g?"wm-org-leave":"wm-org-enter",children:p.map((m,A)=>r(ae,{node:m,index:A,depth:y+1,variant:v,folded:C,leaving:k,toggle:$,tone:R,selected:o,onSelect:c},m.id))}):null]})}function xe({data:t,folded:_,toggle:y,tone:v,selected:C,onSelect:k,className:$}){const R=(o,c)=>{const p=o.children??[],a=p.length>0&&!_.has(o.id),g=C===o.id;return d("li",{children:[d("div",{className:w("group/row flex items-center gap-2.5 rounded-[calc(10px*var(--radius-scale))] py-1.5 pr-2 transition-colors duration-200",g?"bg-primary-subtle":"hover:bg-hover"),style:{paddingLeft:6+c*22},children:[p.length?r("button",{type:"button","aria-expanded":a,"aria-label":`${a?"Hide":"Show"} ${o.name}'s team`,onClick:()=>y(o.id),className:"grid size-6 shrink-0 cursor-pointer place-items-center rounded-[6px] text-muted transition-colors hover:bg-border/60 hover:text-text",children:r(oe,{className:w("size-3.5 transition-transform duration-300 ease-[cubic-bezier(.3,1.4,.5,1)]",a&&"rotate-90")})}):r("span",{className:"size-6 shrink-0"}),d("button",{type:"button",onClick:k?()=>k(o):void 0,className:w("flex min-w-0 flex-1 items-center gap-2.5 text-left outline-none",k&&"cursor-pointer"),children:[r("span",{className:"rounded-full",style:{boxShadow:`0 0 0 2px var(--canvas), 0 0 0 3.5px color-mix(in srgb, ${v(o)} 60%, transparent)`},children:r(I,{name:o.name,src:o.avatar,size:"md"})}),d("span",{className:"min-w-0 flex-1",children:[d("span",{className:"flex items-center gap-1.5",children:[r("span",{className:w("truncate text-[13px] font-semibold",g?"text-primary":"text-text"),children:o.name}),o.badge]}),r("span",{className:"block truncate text-[11.5px] text-muted",children:[o.title,o.department].filter(Boolean).join(" \xB7 ")})]})]}),p.length?r("span",{className:"num shrink-0 rounded-full bg-hover px-2 py-px text-[11px] font-medium text-muted group-hover/row:bg-raised",children:j(o)}):null]}),p.length?r("div",{className:w("grid transition-[grid-template-rows,opacity] duration-[380ms] ease-[cubic-bezier(.22,1,.36,1)]",a?"grid-rows-[1fr] opacity-100":"grid-rows-[0fr] opacity-0"),children:d("ul",{className:"relative min-h-0 overflow-hidden",inert:!a,children:[r("span",{"aria-hidden":!0,className:"absolute bottom-3 top-0 w-px bg-border",style:{left:18+c*22}}),p.map(s=>R(s,c+1))]})}):null]},o.id)};return r("ul",{className:w("space-y-px",$),children:R(t,0)})}const ye=ne;export{we as OrgChart,ye as orgIds};
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
export interface ColorPickerProps {
|
|
3
|
-
value?: string;
|
|
4
|
-
defaultValue?: string;
|
|
5
|
-
onValueChange?: (hex: string) => void;
|
|
6
|
-
/** Quick choices under the strip; `false` for none. */
|
|
7
|
-
swatches?: string[] | false;
|
|
8
|
-
className?: string;
|
|
9
|
-
}
|
|
10
|
-
export declare function ColorPicker({ value, defaultValue, onValueChange, swatches, className }: ColorPickerProps): React.JSX.Element;
|
|
11
|
-
/** The picker behind a field-like button showing the colour and its hex. */
|
|
12
|
-
export declare function ColorPickerPopover({ value, defaultValue, onValueChange, swatches, "aria-label": ariaLabel, className, }: ColorPickerProps & {
|
|
13
|
-
"aria-label"?: string;
|
|
14
|
-
}): React.JSX.Element;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use client";import{jsx as l,jsxs as m}from"react/jsx-runtime";import*as x from"@radix-ui/react-popover";import{Pipette as $}from"lucide-react";import*as p from"react";import{cn as P}from"../../lib/utils.js";function _(o){let t=o.trim().replace(/^#/,"");if(t.length===3&&(t=[...t].map(e=>e+e).join("")),!/^[0-9a-f]{6}$/i.test(t))return null;const a=parseInt(t,16);return[a>>16&255,a>>8&255,a&255]}const H=(o,t,a)=>`#${[o,t,a].map(e=>Math.round(e).toString(16).padStart(2,"0")).join("")}`;function N(o,t,a){o/=255,t/=255,a/=255;const e=Math.max(o,t,a),i=e-Math.min(o,t,a);let d=0;return i&&(d=e===o?(t-a)/i%6:e===t?(a-o)/i+2:(o-t)/i+4),{h:(d*60+360)%360,s:e?i/e:0,v:e}}function k({h:o,s:t,v:a}){const e=i=>{const d=(i+o/60)%6;return(a-a*t*Math.max(0,Math.min(d,4-d,1)))*255};return H(e(5),e(3),e(1))}const S=["#4f46e5","#1f45ea","#7c3aed","#db2777","#e11d48","#ea580c","#d97706","#16a34a","#059669","#0d9488","#0284c7","#334155"];function T(o){const t=p.useRef(null),a=e=>{const i=t.current.getBoundingClientRect();o(Math.min(1,Math.max(0,(e.clientX-i.left)/i.width)),Math.min(1,Math.max(0,(e.clientY-i.top)/i.height)))};return{ref:t,onPointerDown:e=>{e.preventDefault(),e.currentTarget.setPointerCapture(e.pointerId),e.currentTarget.focus(),a(e)},onPointerMove:e=>{e.currentTarget.hasPointerCapture(e.pointerId)&&a(e)}}}function L({value:o,defaultValue:t="#4f46e5",onValueChange:a,swatches:e=S,className:i}){const[d,b]=p.useState(t),c=(o??d).toLowerCase(),[s,g]=p.useState(()=>N(..._(c)??[79,70,229])),[h,w]=p.useState(c);p.useEffect(()=>{if(k(s)!==c){const r=_(c);if(r){const n=N(...r);g(u=>({h:n.s===0||n.v===0?u.h:n.h,s:n.v===0?u.s:n.s,v:n.v}))}}w(c)},[c]);const v=r=>{g(r);const n=k(r);o===void 0&&b(n),a?.(n)},y=r=>{const n=_(r);if(!n)return;const u=H(...n);o===void 0&&b(u),a?.(u)},E=T((r,n)=>v({...s,s:r,v:1-n})),R=T(r=>v({...s,h:r*360})),M=(r,n)=>{const u=r.shiftKey?.1:.02,f={ArrowLeft:[-u,0],ArrowRight:[u,0],ArrowUp:[0,u],ArrowDown:[0,-u]}[r.key];f&&(r.preventDefault(),v(n==="area"?{...s,s:Math.min(1,Math.max(0,s.s+f[0])),v:Math.min(1,Math.max(0,s.v+f[1]))}:{...s,h:(s.h+(f[0]+f[1])*360+360)%360}))},D=typeof window<"u"&&"EyeDropper"in window,C=k({h:s.h,s:1,v:1});return m("div",{className:P("w-64 space-y-3",i),children:[l("div",{...E,role:"slider",tabIndex:0,"aria-label":"Saturation and brightness","aria-valuetext":`Saturation ${Math.round(s.s*100)}%, brightness ${Math.round(s.v*100)}%`,onKeyDown:r=>M(r,"area"),className:"relative h-40 cursor-crosshair touch-none rounded-[calc(10px*var(--radius-scale))] outline-none ring-offset-2 ring-offset-[var(--raised)] focus-visible:ring-2 focus-visible:ring-primary/50",style:{background:`linear-gradient(to top, #000, transparent), linear-gradient(to right, #fff, ${C})`},children:l("span",{"aria-hidden":!0,className:"pointer-events-none absolute size-4 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-white shadow-[0_0_0_1px_rgba(5,8,22,.25),0_2px_6px_rgba(5,8,22,.35)]",style:{left:`${s.s*100}%`,top:`${(1-s.v)*100}%`,background:c}})}),l("div",{...R,role:"slider",tabIndex:0,"aria-label":"Hue","aria-valuemin":0,"aria-valuemax":360,"aria-valuenow":Math.round(s.h),onKeyDown:r=>M(r,"hue"),className:"relative h-3 cursor-pointer touch-none rounded-full outline-none ring-offset-2 ring-offset-[var(--raised)] focus-visible:ring-2 focus-visible:ring-primary/50",style:{background:"linear-gradient(to right, #f00, #ff0, #0f0, #0ff, #00f, #f0f, #f00)"},children:l("span",{"aria-hidden":!0,className:"pointer-events-none absolute top-1/2 size-4 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-white shadow-[0_0_0_1px_rgba(5,8,22,.25),0_2px_6px_rgba(5,8,22,.35)]",style:{left:`${s.h/360*100}%`,background:C}})}),m("div",{className:"flex items-center gap-2",children:[l("span",{className:"size-8 shrink-0 rounded-[calc(8px*var(--radius-scale))] shadow-[inset_0_0_0_1px_rgba(5,8,22,.12)]",style:{background:c}}),m("label",{className:"flex h-8 min-w-0 flex-1 items-center gap-1 rounded-ctl border border-border2 bg-raised px-2 focus-within:border-primary focus-within:ring-2 focus-within:ring-primary/20",children:[l("span",{className:"text-[12px] text-muted",children:"#"}),l("input",{value:h.replace(/^#/,""),"aria-label":"Hex colour",spellCheck:!1,maxLength:7,onChange:r=>{const n=`#${r.target.value.replace(/[^0-9a-f]/gi,"").slice(0,6)}`;w(n),n.length===7&&y(n)},onBlur:()=>w(c),className:"num min-w-0 flex-1 bg-transparent font-mono text-[12.5px] uppercase text-text outline-none"})]}),D?l("button",{type:"button","aria-label":"Pick a colour from the screen",onClick:async()=>{try{const r=await new window.EyeDropper().open();y(r.sRGBHex)}catch{}},className:"grid size-8 shrink-0 cursor-pointer place-items-center rounded-ctl border border-border2 bg-raised text-muted transition-colors hover:bg-hover hover:text-text",children:l($,{className:"size-4"})}):null]}),e&&e.length?l("div",{className:"grid grid-cols-6 gap-1.5",children:e.map(r=>{const n=r.toLowerCase()===c;return l("button",{type:"button","aria-label":r,"aria-pressed":n,onClick:()=>y(r),className:P("h-7 cursor-pointer rounded-[calc(7px*var(--radius-scale))] shadow-[inset_0_0_0_1px_rgba(5,8,22,.12)] transition-transform hover:scale-110",n&&"ring-2 ring-[var(--text)] ring-offset-2 ring-offset-[var(--raised)]"),style:{background:r}},r)})}):null]})}function B({value:o,defaultValue:t="#4f46e5",onValueChange:a,swatches:e,"aria-label":i="Colour",className:d}){const[b,c]=p.useState(t),s=o??b,g=h=>{o===void 0&&c(h),a?.(h)};return m(x.Root,{children:[m(x.Trigger,{"aria-label":`${i}: ${s}`,className:P("inline-flex h-ctl cursor-pointer items-center gap-2 rounded-ctl border border-border2 bg-raised pl-1.5 pr-3 text-[13px] text-text outline-none transition-[border-color,box-shadow]","hover:border-muted/50 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",d),children:[l("span",{className:"size-6 rounded-[calc(6px*var(--radius-scale))] shadow-[inset_0_0_0_1px_rgba(5,8,22,.12)]",style:{background:s}}),l("span",{className:"num font-mono text-[12.5px] uppercase",children:s})]}),l(x.Portal,{children:l(x.Content,{"data-pop":"",align:"start",sideOffset:6,collisionPadding:12,className:"z-50 rounded-dlg border border-border bg-raised p-3 shadow-elev3 outline-none",children:l(L,{value:s,onValueChange:g,swatches:e})})})]})}export{L as ColorPicker,B as ColorPickerPopover};
|