@dreamtree-org/twreact-ui 1.3.2 → 1.3.4

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.
Files changed (52) hide show
  1. package/DESIGN.md +2 -2
  2. package/README.md +14 -6
  3. package/ai-skills/dreamtree-ui.md +3 -1
  4. package/bin/cli.mjs +72 -4
  5. package/dist/ai-schema.json +3 -2
  6. package/dist/charts/index.js +75 -47
  7. package/dist/components/charts/CartesianChart.d.ts.map +1 -1
  8. package/dist/components/charts/DonutChart.d.ts.map +1 -1
  9. package/dist/components/charts/Sparkline.d.ts.map +1 -1
  10. package/dist/components/charts/chartTheme.d.ts +13 -3
  11. package/dist/components/charts/chartTheme.d.ts.map +1 -1
  12. package/dist/components/core/Table/MobileCard.d.ts.map +1 -1
  13. package/dist/components/core/Table/MobileFilters.d.ts.map +1 -1
  14. package/dist/components/core/Table/Table.d.ts.map +1 -1
  15. package/dist/components/core/Table/TableRow.d.ts.map +1 -1
  16. package/dist/components/core/Table/helpers.d.ts +12 -0
  17. package/dist/components/core/Table/helpers.d.ts.map +1 -1
  18. package/dist/components/utility/Stepper.d.ts.map +1 -1
  19. package/dist/esm/components/blocks/StatCard.js +1 -1
  20. package/dist/esm/components/charts/CartesianChart.js +7 -2
  21. package/dist/esm/components/charts/DonutChart.js +4 -1
  22. package/dist/esm/components/charts/Sparkline.js +4 -1
  23. package/dist/esm/components/charts/chartTheme.js +65 -48
  24. package/dist/esm/components/core/Select.js +2 -2
  25. package/dist/esm/components/core/Table/ActionMenu.js +5 -5
  26. package/dist/esm/components/core/Table/ColumnMenu.js +1 -1
  27. package/dist/esm/components/core/Table/FieldInput.js +5 -5
  28. package/dist/esm/components/core/Table/MobileCard.js +14 -12
  29. package/dist/esm/components/core/Table/MobileFilters.js +11 -4
  30. package/dist/esm/components/core/Table/Pagination.js +1 -1
  31. package/dist/esm/components/core/Table/Table.js +22 -19
  32. package/dist/esm/components/core/Table/TableRow.js +10 -11
  33. package/dist/esm/components/core/Table/helpers.js +16 -1
  34. package/dist/esm/components/navigation/Dropdown.js +1 -1
  35. package/dist/esm/components/navigation/Navbar.js +21 -21
  36. package/dist/esm/components/navigation/Sidebar.js +12 -12
  37. package/dist/esm/components/utility/Card.js +5 -5
  38. package/dist/esm/components/utility/Stepper.js +6 -4
  39. package/dist/index.css +1 -1
  40. package/dist/index.esm.css +1 -1
  41. package/dist/index.esm.js +129 -101
  42. package/dist/index.js +129 -101
  43. package/dist/tokens/brands/practicepro.json +1 -0
  44. package/dist/tokens/brands/studio.json +1 -0
  45. package/dist/types/index.d.ts +1 -0
  46. package/dist/types/index.d.ts.map +1 -1
  47. package/mcp/catalog.snapshot.json +21 -21
  48. package/package.json +2 -1
  49. package/tokens/brand.schema.json +612 -0
  50. package/tokens/brands/practicepro.json +1 -0
  51. package/tokens/brands/studio.json +1 -0
  52. package/tokens/lib.cjs +131 -7
package/DESIGN.md CHANGED
@@ -41,7 +41,7 @@ rounded:
41
41
  spacing:
42
42
  grid: 4px
43
43
  steps: [4, 8, 12, 16, 20, 24, 32]
44
- brands: [default, practicepro, studio]
44
+ brand: dreamtree.brand.json in each product repo (build its CSS with `npx @dreamtree-org/twreact-ui brand build`)
45
45
  ---
46
46
 
47
47
  <!-- Generated by twreact-ui scripts/build-tokens.mjs from tokens/tokens.json. Edit the tokens or this generator, not this file. -->
@@ -76,7 +76,7 @@ Every Dreamtree product screen, and every prototype generated by Dreamtree Studi
76
76
 
77
77
  - **One accent per screen.** `primary` marks the primary action, the active nav item, the current step and progress. Red (`error`) is only for errors, overdue items and destructive actions.
78
78
  - Badge and status fills use the 100 step with 800 text, which keeps contrast at or above 4.5:1.
79
- - Products change their look with a **brand file** (`tokens/brands/<product>.json`), which overrides `--dt-*` variables. Brands available: `default`, `practicepro`, `studio`.
79
+ - Each product keeps its look in its own repo, in **`dreamtree.brand.json`** (its `--dt-*` overrides: colour scales, roles, font). `npx @dreamtree-org/twreact-ui brand build` turns it into the CSS the product loads after twreact-ui's CSS. Never restyle a product with raw colours; change its brand file.
80
80
 
81
81
  ## 3. Type, space and shape
82
82
 
package/README.md CHANGED
@@ -278,7 +278,7 @@ Advanced select component with search, multi-select, and grouping support.
278
278
  - `selectAllOption`: boolean
279
279
  - `closeOnSelect`: boolean
280
280
  - `maxTagCount`: number (default: 3)
281
- - `allowClear`: boolean
281
+ - `allowClear`: boolean (default: false; set it when the field may be emptied)
282
282
 
283
283
  ```jsx
284
284
  <Select
@@ -836,14 +836,20 @@ See [doc/useApi.md](doc/useApi.md).
836
836
 
837
837
  Every colour in the compiled CSS is a CSS variable (`--dt-primary-600`, `--dt-surface`, …) generated from `tokens/tokens.json` (W3C Design Tokens format). The defaults equal the classic palette, so nothing changes until you override a variable.
838
838
 
839
- **Re-theme with a brand file** (no Tailwind config needed, works with the precompiled CSS):
839
+ **Your brand lives in your repo.** Keep it in `dreamtree.brand.json` at the repo root (colour scales, roles, font, logo, and optional landing-page colours) and build the CSS from it. No Tailwind config is needed, and it works with the precompiled CSS:
840
+
841
+ ```bash
842
+ npx @dreamtree-org/twreact-ui brand init --id acme --name "Acme" # starter file
843
+ npx @dreamtree-org/twreact-ui brand build --out app/brand.css # only the --dt-* variables you override
844
+ npx @dreamtree-org/twreact-ui brand build --out app/brand.css --check # in CI: fails if the CSS is stale
845
+ ```
840
846
 
841
847
  ```css
842
848
  @import "@dreamtree-org/twreact-ui/dist/index.css";
843
- @import "@dreamtree-org/twreact-ui/dist/tokens/brands/practicepro.css"; /* or your own --dt-* overrides */
849
+ @import "./brand.css";
844
850
  ```
845
851
 
846
- Shipped brands: `default`, `practicepro`, `studio` (Dreamtree Studio's own pages).
852
+ The file is checked against `tokens/brand.schema.json` (add `"$schema": "https://unpkg.com/@dreamtree-org/twreact-ui/tokens/brand.schema.json"` for editor help). `brand validate` lists every problem with its fix, and `--scope ".my-app"` limits the brand to part of a page. twreact-ui ships only `default`; the `practicepro` and `studio` brands are deprecated copies until 1.5.0.
847
853
 
848
854
  **Use the same tokens in your own classes:**
849
855
 
@@ -872,7 +878,7 @@ This adds semantic utilities next to the palettes: `bg-surface`, `bg-background`
872
878
 
873
879
  **Colour props are overrides, not defaults.** Without colour props, `ProgressBar`, `Checkbox`, `Rate` and `Skeleton` use token classes, so a brand file recolours them. Their HEX props (`color`, `bgColor`, `borderColor`, `activeColor`, `inactiveColor`) still work but are deprecated.
874
880
 
875
- **SSR:** `Tabs`, `Tooltip` and `Switch` generate ids with `React.useId()`, so server-rendered pages (Next.js) hydrate without mismatches.
881
+ **SSR:** `Tabs`, `Tooltip` and `Switch` generate ids with `React.useId()`, so server-rendered pages (Next.js) hydrate without mismatches. The `/charts` components do the same for their SVG ids, and they render token colours and reduced-motion state the way the server did until hydration finishes.
876
882
 
877
883
  ## Dark Mode
878
884
 
@@ -890,6 +896,8 @@ function App() {
890
896
  }
891
897
  ```
892
898
 
899
+ Surfaces such as `Card`, `Sidebar`, `Navbar`, `Table`, `FootNav` and `Dropdown` use the semantic `bg-surface` / `bg-background` / `border-line` / `text-foreground` / `text-muted` tokens, which switch under `.dark` (or `[data-theme="dark"]`). Re-theme them by overriding the `--dt-surface`, `--dt-background`, `--dt-line`, … variables.
900
+
893
901
  ## Examples
894
902
 
895
903
  ### Complete Form Example
@@ -1019,7 +1027,7 @@ MIT License - see [LICENSE](LICENSE) for details.
1019
1027
 
1020
1028
  ## Support
1021
1029
 
1022
- - 📖 [Documentation](https://dreamtree-ui.dev)
1030
+ - 📖 [Storybook](https://storybook.app.drm3.org)
1023
1031
  - 🐛 [Issue Tracker](https://github.com/dreamtree-ui/dreamtree-ui/issues)
1024
1032
  - 💬 [Discussions](https://github.com/dreamtree-ui/dreamtree-ui/discussions)
1025
1033
  - 🌐 [Dreamtree Global](http://dreamtreeglobal.com/)
@@ -205,7 +205,9 @@ When suggesting a component or new variant value, use this exact vocabulary.
205
205
  **Tokens:** colours are CSS variables (`--dt-primary-600`, `--dt-surface`, …) generated from
206
206
  `tokens/tokens.json`. Use semantic classes (`bg-primary-600`, `bg-surface`, `text-foreground`,
207
207
  `text-muted`, `border-line`, `text-micro`, `rounded-card`) — never hex values or Tailwind's raw
208
- palette (`bg-blue-500`). A product re-themes by loading `dist/tokens/brands/<brand>.css`.
208
+ palette (`bg-blue-500`). A product's brand lives in its own repo as `dreamtree.brand.json`;
209
+ `npx @dreamtree-org/twreact-ui brand build --out app/brand.css` turns it into the CSS to load after
210
+ twreact-ui's CSS. To change a product's colours, edit that file, never the components.
209
211
  The full rule set for screens is `DESIGN.md` in the package root.
210
212
 
211
213
  ```jsx
package/bin/cli.mjs CHANGED
@@ -17,7 +17,8 @@
17
17
 
18
18
  import { readFileSync, writeFileSync, mkdirSync, existsSync } from "node:fs";
19
19
  import { fileURLToPath } from "node:url";
20
- import { dirname, join, resolve } from "node:path";
20
+ import { dirname, join, resolve, relative } from "node:path";
21
+ import { createRequire } from "node:module";
21
22
 
22
23
  const __dirname = dirname(fileURLToPath(import.meta.url));
23
24
  const PKG_ROOT = resolve(__dirname, "..");
@@ -56,14 +57,20 @@ function printHelp() {
56
57
  `${PKG} CLI (v${VERSION})\n\n` +
57
58
  `Usage:\n` +
58
59
  ` npx ${PKG} init --ai <provider> [--out <path>] [--force]\n` +
59
- ` npx ${PKG} mcp\n\n` +
60
+ ` npx ${PKG} mcp\n` +
61
+ ` npx ${PKG} brand init|validate|build [dreamtree.brand.json] [options]\n\n` +
60
62
  `Commands:\n` +
61
63
  ` init Install the AI-assistant skill (ai-skills/dreamtree-ui.md) so your\n` +
62
64
  ` coding agent knows this library's components, conventions, wiring.\n` +
63
65
  ` mcp Run the Model Context Protocol server over stdio — gives an MCP\n` +
64
66
  ` client live, queryable access to the library (list_components,\n` +
65
67
  ` get_component, search_components + docs + a compose_ui prompt).\n` +
66
- ` Wire it as: { "command": "npx", "args": ["-y", "${PKG}", "mcp"] }\n\n` +
68
+ ` Wire it as: { "command": "npx", "args": ["-y", "${PKG}", "mcp"] }\n` +
69
+ ` brand Your product's brand lives in your repo as dreamtree.brand.json.\n` +
70
+ ` init write a starter file (--id <slug> --name <Name>)\n` +
71
+ ` validate check it, with a fix for every problem\n` +
72
+ ` build write the CSS to load after twreact-ui's CSS\n` +
73
+ ` (--out app/brand.css, --scope <selector>, --check for CI)\n\n` +
67
74
  `init options:\n` +
68
75
  ` --ai <provider> Target assistant: ${providers} (default: claude)\n` +
69
76
  ` --out <path> Write to this path instead of the provider default\n` +
@@ -74,12 +81,16 @@ function printHelp() {
74
81
  }
75
82
 
76
83
  function parseArgs(argv) {
77
- const out = { _: [], ai: undefined, out: undefined, force: false, help: false, version: false };
84
+ const out = { _: [], ai: undefined, out: undefined, force: false, help: false, version: false, scope: undefined, check: false, id: undefined, name: undefined };
78
85
  for (let i = 0; i < argv.length; i++) {
79
86
  const a = argv[i];
80
87
  if (a === "--ai") out.ai = argv[++i];
81
88
  else if (a === "--out") out.out = argv[++i];
82
89
  else if (a === "--force") out.force = true;
90
+ else if (a === "--scope") out.scope = argv[++i];
91
+ else if (a === "--check") out.check = true;
92
+ else if (a === "--id") out.id = argv[++i];
93
+ else if (a === "--name") out.name = argv[++i];
83
94
  else if (a === "-h" || a === "--help") out.help = true;
84
95
  else if (a === "-v" || a === "--version") out.version = true;
85
96
  else out._.push(a);
@@ -117,6 +128,62 @@ function runInit(args) {
117
128
  );
118
129
  }
119
130
 
131
+ // `brand` — a product keeps its brand in its own repo (dreamtree.brand.json); these commands turn it into CSS.
132
+ // tokens/lib.cjs is CommonJS and dependency-free, so this stays a zero-dependency path.
133
+ const BRAND_FILE = "dreamtree.brand.json";
134
+
135
+ function runBrand(args) {
136
+ const lib = createRequire(import.meta.url)("../tokens/lib.cjs");
137
+ const sub = args._[1];
138
+ const file = resolve(process.cwd(), args._[2] || BRAND_FILE);
139
+ const shown = relative(process.cwd(), file) || BRAND_FILE;
140
+
141
+ if (sub === "init") {
142
+ if (existsSync(file) && !args.force) fail(`${shown} already exists. Re-run with --force to overwrite.`);
143
+ const id = (args.id || "my-product").toLowerCase();
144
+ const base = lib.scales(lib.loadTokens()).primary;
145
+ const starter = {
146
+ $schema: "https://unpkg.com/@dreamtree-org/twreact-ui/tokens/brand.schema.json",
147
+ id,
148
+ name: args.name || id,
149
+ color: { primary: base },
150
+ role: {},
151
+ marketing: {},
152
+ };
153
+ writeFileSync(file, JSON.stringify(starter, null, 2) + "\n");
154
+ process.stdout.write(`✓ wrote ${shown}. Change color.primary to your product's scale, then run: npx ${PKG} brand build\n`);
155
+ return;
156
+ }
157
+
158
+ if (sub === "validate" || sub === "build") {
159
+ let brand;
160
+ try {
161
+ brand = lib.loadBrand(file);
162
+ } catch (err) {
163
+ fail(err.message);
164
+ }
165
+ if (sub === "validate") {
166
+ process.stdout.write(`✓ ${shown} is a valid brand ("${lib.brandId(brand)}").\n`);
167
+ return;
168
+ }
169
+ const css = lib.brandCss(lib.loadTokens(), brand, { scope: args.scope });
170
+ const outFile = resolve(process.cwd(), args.out || "brand.css");
171
+ const outShown = relative(process.cwd(), outFile);
172
+ if (args.check) {
173
+ const current = existsSync(outFile) ? readFileSync(outFile, "utf8") : "";
174
+ if (current !== css) fail(`${outShown} is out of date with ${shown}. Run: npx ${PKG} brand build ${shown} --out ${outShown}${args.scope ? ` --scope "${args.scope}"` : ""}`);
175
+ process.stdout.write(`✓ ${outShown} matches ${shown}.\n`);
176
+ return;
177
+ }
178
+ mkdirSync(dirname(outFile), { recursive: true });
179
+ writeFileSync(outFile, css);
180
+ process.stdout.write(`✓ wrote ${outShown} from ${shown}. Import it after "${PKG}/dist/index.css".\n`);
181
+ return;
182
+ }
183
+
184
+ fail(`unknown brand command "${sub || ""}". Use: brand init | brand validate | brand build.`);
185
+ }
186
+
120
187
  // Boot the bundled MCP server over stdio. The SDK + server are lazy-imported
121
188
  // here so plain `init` carries no dependency cost. stdout is the MCP protocol
122
189
  // channel, so this path must only ever log to stderr. The shipped server reads
@@ -140,6 +207,7 @@ async function main() {
140
207
  const cmd = args._[0];
141
208
  if (cmd === "init") return runInit(args);
142
209
  if (cmd === "mcp") return runMcp();
210
+ if (cmd === "brand") return runBrand(args);
143
211
 
144
212
  fail(`unknown command "${cmd}". Run \`npx ${PKG} --help\`.`);
145
213
  }
@@ -2251,7 +2251,7 @@
2251
2251
  "allowClear": {
2252
2252
  "type": "boolean",
2253
2253
  "required": false,
2254
- "default": "true"
2254
+ "default": "false"
2255
2255
  },
2256
2256
  "name": {
2257
2257
  "type": "string",
@@ -2936,7 +2936,8 @@
2936
2936
  },
2937
2937
  "theme": {
2938
2938
  "type": "TableTheme",
2939
- "required": false
2939
+ "required": false,
2940
+ "default": "see below"
2940
2941
  },
2941
2942
  "responsiveBreakpoint": {
2942
2943
  "type": "number",
@@ -200,9 +200,11 @@ function readVar(name) {
200
200
  }
201
201
  // SVG presentation attributes do not resolve var() in every engine, so a token is
202
202
  // turned into a concrete rgb() string when the browser can tell us its value. The
203
- // var() form is the SSR / jsdom fallback.
203
+ // var() form is the SSR / jsdom fallback, and what the first (hydrating) client
204
+ // render uses so it matches the server's HTML (`live` = false).
204
205
  function resolveVar(name) {
205
- var value = readVar(name);
206
+ var live = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
207
+ var value = live ? readVar(name) : "";
206
208
  return value ? "rgb(".concat(value.split(/[\s,]+/).join(" "), ")") : "rgb(var(".concat(name, "))");
207
209
  }
208
210
  /**
@@ -211,77 +213,92 @@ function resolveVar(name) {
211
213
  * by the consumer (e.g. a colour stored with the data).
212
214
  */
213
215
  function resolveColor(color) {
216
+ var live = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
214
217
  var name = tokenVar(color);
215
- return name ? resolveVar(name) : color;
218
+ return name ? resolveVar(name, live) : color;
216
219
  }
217
220
  function seriesColors(categories, colors) {
221
+ var live = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
218
222
  var palette = colors && colors.length ? colors : DEFAULT_CHART_COLORS;
219
223
  return categories.map(function (_, i) {
220
- return resolveColor(palette[i % palette.length]);
224
+ return resolveColor(palette[i % palette.length], live);
221
225
  });
222
226
  }
223
- function readChrome() {
227
+ function readChrome(live) {
224
228
  return Object.fromEntries(Object.entries(CHROME_VARS).map(function (_ref) {
225
229
  var _ref2 = cn._slicedToArray(_ref, 2),
226
230
  k = _ref2[0],
227
231
  v = _ref2[1];
228
- return [k, resolveVar(v)];
232
+ return [k, resolveVar(v, live)];
229
233
  }));
230
234
  }
235
+ // The theme is an external store: the attributes on <html> that flip it. The
236
+ // server snapshot is null, so a hydrating render keeps the var() fallback the
237
+ // server rendered and React re-renders with live colours right after (#130).
238
+ function subscribeTheme(onChange) {
239
+ if (typeof document === "undefined" || typeof MutationObserver !== "function") return function () {};
240
+ var observer = new MutationObserver(onChange);
241
+ observer.observe(document.documentElement, {
242
+ attributes: true,
243
+ attributeFilter: ["class", "data-theme", "style"]
244
+ });
245
+ return function () {
246
+ return observer.disconnect();
247
+ };
248
+ }
249
+ function themeSnapshot() {
250
+ if (typeof document === "undefined") return null;
251
+ var el = document.documentElement;
252
+ return "".concat(el.className, "|").concat(el.getAttribute("data-theme") || "", "|").concat(el.getAttribute("style") || "");
253
+ }
254
+ var serverThemeSnapshot = function serverThemeSnapshot() {
255
+ return null;
256
+ };
231
257
  /**
232
258
  * Chart chrome + series colours, re-read whenever the theme flips (the `dark`
233
259
  * class or `data-theme` on <html>) so axes and series follow dark mode without
234
260
  * a remount.
235
261
  */
236
262
  function useChartTheme(categories, colors) {
237
- var _useState = React.useState(0),
238
- _useState2 = cn._slicedToArray(_useState, 2);
239
- _useState2[0];
240
- var setVersion = _useState2[1];
241
- React.useEffect(function () {
242
- if (typeof MutationObserver !== "function") return undefined;
243
- var observer = new MutationObserver(function () {
244
- return setVersion(function (v) {
245
- return v + 1;
246
- });
247
- });
248
- observer.observe(document.documentElement, {
249
- attributes: true,
250
- attributeFilter: ["class", "data-theme", "style"]
251
- });
252
- return function () {
253
- return observer.disconnect();
254
- };
255
- }, []);
263
+ // The snapshot only signals a change; the values come from the live document.
264
+ var live = React.useSyncExternalStore(subscribeTheme, themeSnapshot, serverThemeSnapshot) !== null;
256
265
  return {
257
- chrome: readChrome(),
258
- series: seriesColors(categories, colors)
266
+ chrome: readChrome(live),
267
+ series: seriesColors(categories, colors, live)
259
268
  };
260
269
  }
261
- /** `true` while the user asks the OS for reduced motion; follows changes live. */
270
+ function subscribeReducedMotion(onChange) {
271
+ var _query$addEventListen;
272
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") return function () {};
273
+ var query = window.matchMedia("(prefers-reduced-motion: reduce)");
274
+ (_query$addEventListen = query.addEventListener) === null || _query$addEventListen === void 0 || _query$addEventListen.call(query, "change", onChange);
275
+ return function () {
276
+ var _query$removeEventLis;
277
+ return (_query$removeEventLis = query.removeEventListener) === null || _query$removeEventLis === void 0 ? void 0 : _query$removeEventLis.call(query, "change", onChange);
278
+ };
279
+ }
280
+ var serverReducedMotion = function serverReducedMotion() {
281
+ return false;
282
+ };
283
+ /**
284
+ * `true` while the user asks the OS for reduced motion; follows changes live.
285
+ * `false` on the server and while hydrating, so the first render matches.
286
+ */
262
287
  function useReducedMotion() {
263
- var _useState3 = React.useState(prefersReducedMotion),
264
- _useState4 = cn._slicedToArray(_useState3, 2),
265
- reduced = _useState4[0],
266
- setReduced = _useState4[1];
267
- React.useEffect(function () {
268
- var _query$addEventListen;
269
- if (typeof window === "undefined" || typeof window.matchMedia !== "function") return undefined;
270
- var query = window.matchMedia("(prefers-reduced-motion: reduce)");
271
- var update = function update() {
272
- return setReduced(query.matches);
273
- };
274
- (_query$addEventListen = query.addEventListener) === null || _query$addEventListen === void 0 || _query$addEventListen.call(query, "change", update);
275
- return function () {
276
- var _query$removeEventLis;
277
- return (_query$removeEventLis = query.removeEventListener) === null || _query$removeEventLis === void 0 ? void 0 : _query$removeEventLis.call(query, "change", update);
278
- };
279
- }, []);
280
- return reduced;
288
+ return React.useSyncExternalStore(subscribeReducedMotion, prefersReducedMotion, serverReducedMotion);
281
289
  }
282
290
  var defaultValueFormatter = function defaultValueFormatter(value) {
283
291
  return typeof value === "number" ? value.toLocaleString() : value == null ? "" : String(value);
284
292
  };
293
+ /**
294
+ * A per-instance id for the SVG defs recharts draws (clip paths). recharts 2
295
+ * falls back to a module-level counter, which differs between the server and the
296
+ * client; an id from React.useId() is the same on both. Only [A-Za-z0-9_-] so it
297
+ * is safe inside `url(#…)`.
298
+ */
299
+ function useChartId() {
300
+ return "dt-chart-".concat(React.useId().replace(/[^A-Za-z0-9_-]/g, ""));
301
+ }
285
302
 
286
303
  var _excluded$2 = ["kind", "data", "index", "categories", "colors", "valueFormatter", "xAxisFormatter", "height", "showLegend", "showTooltip", "showXAxis", "showYAxis", "showGrid", "yAxisWidth", "curveType", "stacked", "horizontal", "showDots", "connectNulls", "animate", "title", "description", "emptyState", "accessibleTable", "className"];
287
304
  var ROOTS = {
@@ -301,7 +318,8 @@ var CURVES = {
301
318
  natural: "natural"
302
319
  };
303
320
  function renderSeries(kind, _ref) {
304
- var category = _ref.category,
321
+ var id = _ref.id,
322
+ category = _ref.category,
305
323
  color = _ref.color,
306
324
  curve = _ref.curve,
307
325
  stacked = _ref.stacked,
@@ -311,6 +329,7 @@ function renderSeries(kind, _ref) {
311
329
  horizontal = _ref.horizontal,
312
330
  barRadius = _ref.barRadius;
313
331
  var common = {
332
+ id: id,
314
333
  dataKey: category,
315
334
  name: category,
316
335
  isAnimationActive: animate
@@ -410,6 +429,7 @@ var CartesianChart = /*#__PURE__*/React__default.default.forwardRef(function Car
410
429
  chrome = _useChartTheme.chrome,
411
430
  series = _useChartTheme.series;
412
431
  var reducedMotion = useReducedMotion();
432
+ var chartId = useChartId();
413
433
  var Root = ROOTS[kind];
414
434
  var isHorizontalBars = kind === "bar" && horizontal;
415
435
  var curve = CURVES[curveType] || "monotone";
@@ -470,6 +490,7 @@ var CartesianChart = /*#__PURE__*/React__default.default.forwardRef(function Car
470
490
  height: "100%",
471
491
  minWidth: 0,
472
492
  children: jsxRuntime.jsxs(Root, {
493
+ id: chartId,
473
494
  data: data,
474
495
  layout: isHorizontalBars ? "vertical" : "horizontal",
475
496
  margin: {
@@ -508,6 +529,7 @@ var CartesianChart = /*#__PURE__*/React__default.default.forwardRef(function Car
508
529
  })
509
530
  }), categories.map(function (category, i) {
510
531
  return renderSeries(kind, {
532
+ id: "".concat(chartId, "-").concat(i),
511
533
  category: category,
512
534
  color: series[i],
513
535
  curve: curve,
@@ -591,6 +613,7 @@ var DonutChart = /*#__PURE__*/React__default.default.forwardRef(function (_ref,
591
613
  chrome = _useChartTheme.chrome,
592
614
  series = _useChartTheme.series;
593
615
  var reducedMotion = useReducedMotion();
616
+ var chartId = useChartId();
594
617
  var slices = rows.map(function (row, i) {
595
618
  return cn._objectSpread2(cn._objectSpread2({}, row), {}, {
596
619
  fill: series[i]
@@ -632,6 +655,7 @@ var DonutChart = /*#__PURE__*/React__default.default.forwardRef(function (_ref,
632
655
  height: "100%",
633
656
  minWidth: 0,
634
657
  children: jsxRuntime.jsxs(recharts.PieChart, {
658
+ id: chartId,
635
659
  accessibilityLayer: false,
636
660
  children: [showTooltip && jsxRuntime.jsx(recharts.Tooltip, {
637
661
  isAnimationActive: false,
@@ -640,6 +664,7 @@ var DonutChart = /*#__PURE__*/React__default.default.forwardRef(function (_ref,
640
664
  hideLabel: true
641
665
  })
642
666
  }), jsxRuntime.jsx(recharts.Pie, {
667
+ id: "".concat(chartId, "-0"),
643
668
  data: slices,
644
669
  dataKey: category,
645
670
  nameKey: index,
@@ -691,9 +716,11 @@ var Sparkline = /*#__PURE__*/React__default.default.forwardRef(function (_ref, r
691
716
  var _useChartTheme = useChartTheme([category], [color]),
692
717
  series = _useChartTheme.series;
693
718
  var reducedMotion = useReducedMotion();
719
+ var chartId = useChartId();
694
720
  var stroke = series[0];
695
721
  var Root = type === "area" ? recharts.AreaChart : recharts.LineChart;
696
722
  var common = {
723
+ id: "".concat(chartId, "-0"),
697
724
  dataKey: category,
698
725
  name: category,
699
726
  type: "monotone",
@@ -734,6 +761,7 @@ var Sparkline = /*#__PURE__*/React__default.default.forwardRef(function (_ref, r
734
761
  height: "100%",
735
762
  minWidth: 0,
736
763
  children: jsxRuntime.jsxs(Root, {
764
+ id: chartId,
737
765
  data: data,
738
766
  margin: {
739
767
  top: 2,
@@ -1 +1 @@
1
- {"version":3,"file":"CartesianChart.d.ts","sourceRoot":"","sources":["../../../src/components/charts/CartesianChart.jsx"],"names":[],"mappings":";AA0DA,iEAAiE;AACjE,wFAwHG;kBAnLe,OAAO"}
1
+ {"version":3,"file":"CartesianChart.d.ts","sourceRoot":"","sources":["../../../src/components/charts/CartesianChart.jsx"],"names":[],"mappings":";AA0DA,iEAAiE;AACjE,wFA2HG;kBAtLe,OAAO"}
@@ -1 +1 @@
1
- {"version":3,"file":"DonutChart.d.ts","sourceRoot":"","sources":["../../../src/components/charts/DonutChart.jsx"],"names":[],"mappings":";AAKA,oFA8EE;kBAnFgB,OAAO"}
1
+ {"version":3,"file":"DonutChart.d.ts","sourceRoot":"","sources":["../../../src/components/charts/DonutChart.jsx"],"names":[],"mappings":";AAKA,oFAgFE;kBArFgB,OAAO"}
@@ -1 +1 @@
1
- {"version":3,"file":"Sparkline.d.ts","sourceRoot":"","sources":["../../../src/components/charts/Sparkline.jsx"],"names":[],"mappings":";AAKA,qFAAqF;AACrF,mFAqEE;kBA3EgB,OAAO"}
1
+ {"version":3,"file":"Sparkline.d.ts","sourceRoot":"","sources":["../../../src/components/charts/Sparkline.jsx"],"names":[],"mappings":";AAKA,qFAAqF;AACrF,mFAuEE;kBA7EgB,OAAO"}
@@ -5,8 +5,8 @@ export function tokenVar(color: any): string | null;
5
5
  * matching `--dt-*` variable; any other string is treated as a CSS colour supplied
6
6
  * by the consumer (e.g. a colour stored with the data).
7
7
  */
8
- export function resolveColor(color: any): any;
9
- export function seriesColors(categories: any, colors: any): any;
8
+ export function resolveColor(color: any, live?: boolean): any;
9
+ export function seriesColors(categories: any, colors: any, live?: boolean): any;
10
10
  /**
11
11
  * Chart chrome + series colours, re-read whenever the theme flips (the `dark`
12
12
  * class or `data-theme` on <html>) so axes and series follow dark mode without
@@ -18,8 +18,18 @@ export function useChartTheme(categories: any, colors: any): {
18
18
  };
19
19
  series: any;
20
20
  };
21
- /** `true` while the user asks the OS for reduced motion; follows changes live. */
21
+ /**
22
+ * `true` while the user asks the OS for reduced motion; follows changes live.
23
+ * `false` on the server and while hydrating, so the first render matches.
24
+ */
22
25
  export function useReducedMotion(): boolean;
26
+ /**
27
+ * A per-instance id for the SVG defs recharts draws (clip paths). recharts 2
28
+ * falls back to a module-level counter, which differs between the server and the
29
+ * client; an id from React.useId() is the same on both. Only [A-Za-z0-9_-] so it
30
+ * is safe inside `url(#…)`.
31
+ */
32
+ export function useChartId(): string;
23
33
  export const CHART_PALETTES: string[];
24
34
  export const DEFAULT_CHART_COLORS: string[];
25
35
  export function defaultValueFormatter(value: any): string;
@@ -1 +1 @@
1
- {"version":3,"file":"chartTheme.d.ts","sourceRoot":"","sources":["../../../src/components/charts/chartTheme.js"],"names":[],"mappings":"AAgBA,sGAAsG;AACtG,oDAGC;AAeD;;;;GAIG;AACH,8CAGC;AAED,gEAGC;AAMD;;;;GAIG;AACH;;;;;EAaC;AAED,kFAAkF;AAClF,4CAYC;AApFD,sCAAsG;AAEtG,4CAA4G;AAoFrG,0DACkF"}
1
+ {"version":3,"file":"chartTheme.d.ts","sourceRoot":"","sources":["../../../src/components/charts/chartTheme.js"],"names":[],"mappings":"AAgBA,sGAAsG;AACtG,oDAGC;AAgBD;;;;GAIG;AACH,8DAGC;AAED,gFAGC;AAwBD;;;;GAIG;AACH;;;;;EAIC;AAWD;;;GAGG;AACH,4CAEC;AAKD;;;;;GAKG;AACH,qCAEC;AA7GD,sCAAsG;AAEtG,4CAA4G;AAgGrG,0DACkF"}
@@ -1 +1 @@
1
- {"version":3,"file":"MobileCard.d.ts","sourceRoot":"","sources":["../../../../src/components/core/Table/MobileCard.jsx"],"names":[],"mappings":";AAWA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4CAkOC"}
1
+ {"version":3,"file":"MobileCard.d.ts","sourceRoot":"","sources":["../../../../src/components/core/Table/MobileCard.jsx"],"names":[],"mappings":";AAYA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4CAsOC"}
@@ -1 +1 @@
1
- {"version":3,"file":"MobileFilters.d.ts","sourceRoot":"","sources":["../../../../src/components/core/Table/MobileFilters.jsx"],"names":[],"mappings":";AAGA;;GAEG;AACH;;;;;;;;;;;4CA2DC"}
1
+ {"version":3,"file":"MobileFilters.d.ts","sourceRoot":"","sources":["../../../../src/components/core/Table/MobileFilters.jsx"],"names":[],"mappings":";AAGA;;GAEG;AACH;;;;;;;;;;;4CAmEC"}
@@ -1 +1 @@
1
- {"version":3,"file":"Table.d.ts","sourceRoot":"","sources":["../../../../src/components/core/Table/Table.jsx"],"names":[],"mappings":";AA8CA,+EA+gDG;kBApjDI,OAAO"}
1
+ {"version":3,"file":"Table.d.ts","sourceRoot":"","sources":["../../../../src/components/core/Table/Table.jsx"],"names":[],"mappings":";AA8CA,+EAkhDG;kBAvjDI,OAAO"}
@@ -1 +1 @@
1
- {"version":3,"file":"TableRow.d.ts","sourceRoot":"","sources":["../../../../src/components/core/Table/TableRow.jsx"],"names":[],"mappings":";AAWA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4CA6RC"}
1
+ {"version":3,"file":"TableRow.d.ts","sourceRoot":"","sources":["../../../../src/components/core/Table/TableRow.jsx"],"names":[],"mappings":";AAYA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4CA+RC"}
@@ -1,4 +1,16 @@
1
1
  export function getRowKey(row: any, globalIndex: any): string;
2
+ export function getStripeProps(stripe: any): {
3
+ className?: undefined;
4
+ style?: undefined;
5
+ } | {
6
+ className: any;
7
+ style?: undefined;
8
+ } | {
9
+ style: {
10
+ backgroundColor: any;
11
+ };
12
+ className?: undefined;
13
+ };
2
14
  export function rowHasDetails(hasDetails: any, row: any, index: any): boolean;
3
15
  export function getActionLabelText(action: any): any;
4
16
  export function mergeDefaultActions(overrides: any[], defaults: any[]): any[];
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../../src/components/core/Table/helpers.js"],"names":[],"mappings":"AAGO,8DAMN;AAaM,8EACqE;AAMrE,qDAGG;AAcH,8EAmBN;AAuBM,wCALI,gBAAc,OACd,MAAM,SACN,MAAM,GACJ;IAAE,aAAa,QAAQ;IAAC,WAAW,QAAO;CAAE,CAyBxD"}
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../../src/components/core/Table/helpers.js"],"names":[],"mappings":"AAGO,8DAMN;AAOM;;;;;;;;;;;EAKN;AAaM,8EACqE;AAMrE,qDAGG;AAcH,8EAmBN;AAuBM,wCALI,gBAAc,OACd,MAAM,SACN,MAAM,GACJ;IAAE,aAAa,QAAQ;IAAC,WAAW,QAAO;CAAE,CAyBxD"}
@@ -1 +1 @@
1
- {"version":3,"file":"Stepper.d.ts","sourceRoot":"","sources":["../../../src/components/utility/Stepper.jsx"],"names":[],"mappings":";AAIA,iFAkIG;kBAtIe,OAAO"}
1
+ {"version":3,"file":"Stepper.d.ts","sourceRoot":"","sources":["../../../src/components/utility/Stepper.jsx"],"names":[],"mappings":";AAIA,iFAoIG;kBAxIe,OAAO"}
@@ -57,7 +57,7 @@ var StatCard = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
57
57
  }), change && !loading && jsxs("p", {
58
58
  className: "mt-1 flex items-center gap-1 text-xs text-muted",
59
59
  children: [jsxs("span", {
60
- className: cn('inline-flex items-center gap-0.5 font-semibold', good ? 'text-success-600' : 'text-error-600'),
60
+ className: cn('inline-flex items-center gap-0.5 font-semibold', good ? 'text-success-700 dark:text-success-400' : 'text-error-700 dark:text-error-400'),
61
61
  children: [up ? jsx(ArrowUp, {
62
62
  className: "h-3.5 w-3.5",
63
63
  "aria-hidden": "true"
@@ -4,7 +4,7 @@ import { jsx, jsxs } from 'react/jsx-runtime';
4
4
  import React from 'react';
5
5
  import { ResponsiveContainer, AreaChart, BarChart, LineChart, CartesianGrid, XAxis, YAxis, Tooltip, Bar, Area, Line } from 'recharts';
6
6
  import { ChartFrame, ChartTooltipContent } from './ChartFrame.js';
7
- import { useChartTheme, useReducedMotion, defaultValueFormatter } from './chartTheme.js';
7
+ import { useChartTheme, useReducedMotion, useChartId, defaultValueFormatter } from './chartTheme.js';
8
8
 
9
9
  var _excluded = ["kind", "data", "index", "categories", "colors", "valueFormatter", "xAxisFormatter", "height", "showLegend", "showTooltip", "showXAxis", "showYAxis", "showGrid", "yAxisWidth", "curveType", "stacked", "horizontal", "showDots", "connectNulls", "animate", "title", "description", "emptyState", "accessibleTable", "className"];
10
10
  var ROOTS = {
@@ -24,7 +24,8 @@ var CURVES = {
24
24
  natural: "natural"
25
25
  };
26
26
  function renderSeries(kind, _ref) {
27
- var category = _ref.category,
27
+ var id = _ref.id,
28
+ category = _ref.category,
28
29
  color = _ref.color,
29
30
  curve = _ref.curve,
30
31
  stacked = _ref.stacked,
@@ -34,6 +35,7 @@ function renderSeries(kind, _ref) {
34
35
  horizontal = _ref.horizontal,
35
36
  barRadius = _ref.barRadius;
36
37
  var common = {
38
+ id: id,
37
39
  dataKey: category,
38
40
  name: category,
39
41
  isAnimationActive: animate
@@ -133,6 +135,7 @@ var CartesianChart = /*#__PURE__*/React.forwardRef(function CartesianChart(_ref2
133
135
  chrome = _useChartTheme.chrome,
134
136
  series = _useChartTheme.series;
135
137
  var reducedMotion = useReducedMotion();
138
+ var chartId = useChartId();
136
139
  var Root = ROOTS[kind];
137
140
  var isHorizontalBars = kind === "bar" && horizontal;
138
141
  var curve = CURVES[curveType] || "monotone";
@@ -193,6 +196,7 @@ var CartesianChart = /*#__PURE__*/React.forwardRef(function CartesianChart(_ref2
193
196
  height: "100%",
194
197
  minWidth: 0,
195
198
  children: jsxs(Root, {
199
+ id: chartId,
196
200
  data: data,
197
201
  layout: isHorizontalBars ? "vertical" : "horizontal",
198
202
  margin: {
@@ -231,6 +235,7 @@ var CartesianChart = /*#__PURE__*/React.forwardRef(function CartesianChart(_ref2
231
235
  })
232
236
  }), categories.map(function (category, i) {
233
237
  return renderSeries(kind, {
238
+ id: "".concat(chartId, "-").concat(i),
234
239
  category: category,
235
240
  color: series[i],
236
241
  curve: curve,