@godxjp/ui 18.4.0 → 18.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. package/README.md +16 -1
  2. package/dist/app/theme-axes.d.ts +11 -3
  3. package/dist/app/theme-axes.js +2 -1
  4. package/dist/components/charts/chart-frame.d.ts +17 -3
  5. package/dist/components/charts/chart-frame.js +7 -1
  6. package/dist/components/charts/chart-summary.d.ts +21 -0
  7. package/dist/components/charts/chart-summary.js +32 -5
  8. package/dist/components/charts/compact-bar-trend.d.ts +30 -0
  9. package/dist/components/charts/compact-bar-trend.js +74 -0
  10. package/dist/components/charts/index.d.ts +6 -0
  11. package/dist/components/charts/index.js +2 -0
  12. package/dist/components/data-display/badge.d.ts +8 -0
  13. package/dist/components/data-display/badge.js +7 -1
  14. package/dist/components/data-display/data-table.d.ts +40 -4
  15. package/dist/components/data-display/data-table.js +69 -2
  16. package/dist/components/data-display/empty-state.js +1 -1
  17. package/dist/components/data-display/index.d.ts +4 -2
  18. package/dist/components/data-display/index.js +10 -1
  19. package/dist/components/data-display/list-row.d.ts +41 -4
  20. package/dist/components/data-display/list-row.js +23 -3
  21. package/dist/components/data-display/qr-code.d.ts +1 -1
  22. package/dist/components/data-display/service-launcher-card.d.ts +40 -0
  23. package/dist/components/data-display/service-launcher-card.js +97 -0
  24. package/dist/components/data-display/table.d.ts +47 -2
  25. package/dist/components/data-display/table.js +29 -4
  26. package/dist/components/data-entry/command-palette.d.ts +37 -0
  27. package/dist/components/data-entry/command-palette.js +125 -0
  28. package/dist/components/data-entry/command.d.ts +10 -10
  29. package/dist/components/data-entry/index.d.ts +3 -0
  30. package/dist/components/data-entry/index.js +2 -0
  31. package/dist/components/data-entry/input-otp.d.ts +1 -0
  32. package/dist/components/feedback/dialog.d.ts +16 -1
  33. package/dist/components/feedback/dialog.js +46 -33
  34. package/dist/components/feedback/index.d.ts +5 -2
  35. package/dist/components/feedback/index.js +8 -2
  36. package/dist/components/feedback/sheet.d.ts +26 -4
  37. package/dist/components/feedback/sheet.js +54 -7
  38. package/dist/components/feedback/two-factor-setup.d.ts +31 -0
  39. package/dist/components/feedback/two-factor-setup.js +97 -0
  40. package/dist/components/general/index.d.ts +1 -1
  41. package/dist/components/general/logo.d.ts +49 -9
  42. package/dist/components/general/logo.js +67 -14
  43. package/dist/components/layout/app-shell.d.ts +1 -1
  44. package/dist/components/layout/app-shell.js +55 -30
  45. package/dist/components/layout/auth-account-summary.d.ts +5 -0
  46. package/dist/components/layout/auth-account-summary.js +31 -0
  47. package/dist/components/layout/auth-divider.d.ts +7 -0
  48. package/dist/components/layout/auth-divider.js +21 -0
  49. package/dist/components/layout/auth-footer.d.ts +5 -0
  50. package/dist/components/layout/auth-footer.js +24 -0
  51. package/dist/components/layout/auth-identity.d.ts +5 -0
  52. package/dist/components/layout/auth-identity.js +17 -0
  53. package/dist/components/layout/auth-shell.d.ts +9 -1
  54. package/dist/components/layout/auth-shell.js +25 -6
  55. package/dist/components/layout/auth-stack.d.ts +4 -0
  56. package/dist/components/layout/auth-stack.js +8 -0
  57. package/dist/components/layout/centered-shell.d.ts +5 -2
  58. package/dist/components/layout/centered-shell.js +29 -19
  59. package/dist/components/layout/error-surface.d.ts +35 -0
  60. package/dist/components/layout/error-surface.js +155 -0
  61. package/dist/components/layout/flex.d.ts +1 -1
  62. package/dist/components/layout/flex.js +4 -0
  63. package/dist/components/layout/index.d.ts +23 -3
  64. package/dist/components/layout/index.js +21 -2
  65. package/dist/components/layout/legal-document-shell.d.ts +34 -0
  66. package/dist/components/layout/legal-document-shell.js +186 -0
  67. package/dist/components/layout/master-detail.d.ts +24 -0
  68. package/dist/components/layout/master-detail.js +48 -0
  69. package/dist/components/layout/org-switcher.d.ts +9 -0
  70. package/dist/components/layout/org-switcher.js +218 -0
  71. package/dist/components/layout/page-container.d.ts +1 -1
  72. package/dist/components/layout/page-container.js +7 -2
  73. package/dist/components/layout/sidebar-link.d.ts +43 -0
  74. package/dist/components/layout/sidebar-link.js +13 -0
  75. package/dist/components/layout/sidebar.d.ts +42 -5
  76. package/dist/components/layout/sidebar.js +170 -85
  77. package/dist/components/navigation/app-setting-picker.js +17 -6
  78. package/dist/components/navigation/filter-bar.d.ts +12 -3
  79. package/dist/components/navigation/filter-bar.js +8 -2
  80. package/dist/components/navigation/index.d.ts +2 -2
  81. package/dist/components/navigation/index.js +3 -1
  82. package/dist/components/navigation/steps.js +38 -19
  83. package/dist/components/navigation/tabs-scroll.d.ts +70 -0
  84. package/dist/components/navigation/tabs-scroll.js +76 -0
  85. package/dist/components/navigation/tabs.js +47 -23
  86. package/dist/components/ui/avatar.d.ts +2 -1
  87. package/dist/components/ui/avatar.js +2 -1
  88. package/dist/components/ui/input-otp.d.ts +4 -1
  89. package/dist/components/ui/input-otp.js +2 -1
  90. package/dist/components/ui/toggle-group.d.ts +7 -4
  91. package/dist/components/ui/toggle-group.js +32 -21
  92. package/dist/email/brand-mark.d.ts +108 -0
  93. package/dist/email/brand-mark.js +88 -0
  94. package/dist/email/color.d.ts +53 -0
  95. package/dist/email/color.js +37 -0
  96. package/dist/email/geometry.d.ts +120 -0
  97. package/dist/email/geometry.js +90 -0
  98. package/dist/email/index.d.ts +83 -0
  99. package/dist/email/index.js +82 -0
  100. package/dist/email/inline-style.d.ts +27 -0
  101. package/dist/email/inline-style.js +26 -0
  102. package/dist/email/tokens.generated.d.ts +167 -0
  103. package/dist/email/tokens.generated.js +162 -0
  104. package/dist/email/urgency.d.ts +22 -0
  105. package/dist/email/urgency.js +14 -0
  106. package/dist/i18n/messages/en.json +36 -3
  107. package/dist/i18n/messages/ja.json +36 -3
  108. package/dist/i18n/messages/vi.json +36 -3
  109. package/dist/inertia/index.d.ts +50 -0
  110. package/dist/inertia/index.js +5 -0
  111. package/dist/props/components/app.prop.d.ts +11 -1
  112. package/dist/props/components/charts.prop.d.ts +40 -1
  113. package/dist/props/components/data-display.prop.d.ts +75 -1
  114. package/dist/props/components/data-entry.prop.d.ts +6 -0
  115. package/dist/props/components/feedback.prop.d.ts +10 -0
  116. package/dist/props/components/index.d.ts +3 -3
  117. package/dist/props/components/layout.prop.d.ts +560 -10
  118. package/dist/props/components/layout.prop.js +1 -0
  119. package/dist/props/components/navigation.prop.d.ts +24 -2
  120. package/dist/props/registry.d.ts +433 -8
  121. package/dist/props/registry.js +527 -9
  122. package/dist/props/vocabulary/data.prop.d.ts +27 -0
  123. package/dist/props/vocabulary/index.d.ts +3 -3
  124. package/dist/props/vocabulary/interaction.prop.d.ts +10 -1
  125. package/dist/props/vocabulary/layout.prop.d.ts +49 -0
  126. package/dist/styles/base.css +8 -2
  127. package/dist/styles/card-layout.css +161 -7
  128. package/dist/styles/chart-layout.css +97 -0
  129. package/dist/styles/control.css +76 -2
  130. package/dist/styles/data-display-layout.css +78 -6
  131. package/dist/styles/dialog-layout.css +105 -4
  132. package/dist/styles/fonts.css +15 -4
  133. package/dist/styles/index.css +11 -3
  134. package/dist/styles/layout.css +466 -1
  135. package/dist/styles/logo-layout.css +95 -0
  136. package/dist/styles/navigation-layout.css +152 -0
  137. package/dist/styles/shell-layout.css +790 -40
  138. package/dist/styles/table-layout.css +164 -1
  139. package/dist/theme/dxs.canonical.css +77 -0
  140. package/dist/tokens/axes.css +40 -0
  141. package/dist/tokens/base.css +6 -0
  142. package/dist/tokens/components/card.css +48 -0
  143. package/dist/tokens/components/chart.css +47 -0
  144. package/dist/tokens/components/control.css +16 -0
  145. package/dist/tokens/components/data-display.css +13 -0
  146. package/dist/tokens/components/email.css +93 -0
  147. package/dist/tokens/components/error-surface.css +36 -0
  148. package/dist/tokens/components/feedback.css +17 -0
  149. package/dist/tokens/components/legal-document.css +67 -0
  150. package/dist/tokens/components/list-row.css +43 -2
  151. package/dist/tokens/components/logo.css +74 -0
  152. package/dist/tokens/components/navigation.css +28 -0
  153. package/dist/tokens/components/sheet.css +19 -0
  154. package/dist/tokens/components/shell.css +210 -0
  155. package/dist/tokens/components/sidebar.css +33 -0
  156. package/dist/tokens/components/table.css +45 -0
  157. package/dist/tokens/foundation.css +31 -2
  158. package/dist/tokens/semantic/layout.css +69 -2
  159. package/package.json +22 -5
package/README.md CHANGED
@@ -144,12 +144,20 @@ per-layer files you need (mirrors the JS subpaths — the CSS tree-shakes too):
144
144
  @import "@godxjp/ui/styles/form-layout"; /* FormField */
145
145
  @import "@godxjp/ui/styles/dialog-layout"; /* Dialog */
146
146
  /* …only the layers you use. Layer files need `base` first (they use @layer/@apply). */
147
+ @import "@godxjp/ui/styles/fonts"; /* optional bundled faces — MUST come after `base` */
147
148
  ```
148
149
 
149
150
  Skip `@godxjp/ui/styles/fonts` when you manage fonts yourself (next/font, etc.)
150
151
  and set the font tokens instead (see below). A marketing site using ~10
151
152
  components typically drops component CSS from ~142K → ~26K gzip.
152
153
 
154
+ > **Import order rule — `fonts` after `base`.** `styles/base` pulls in the token
155
+ > layers, whose font-agnostic `:root { --font-sans-base: <system stack> }` sits at
156
+ > the same specificity (0,1,0) as the bundle's fill, so the later import wins.
157
+ > Import `styles/fonts` first and the bundled faces silently never apply while the
158
+ > ~800 KB of `@font-face` still ships. The all-in-one `@godxjp/ui/styles` entry
159
+ > already orders this correctly.
160
+
153
161
  ### Fonts — token-driven, per-language, no library hardcoding
154
162
 
155
163
  The base ships NO hardcoded brand face. Supply your own faces and set tokens —
@@ -165,7 +173,14 @@ one face everywhere, or per-language (no `[lang]` selectors to write):
165
173
  ```
166
174
 
167
175
  `styles/base.css` wires each `[lang]` to its slot (falling back to
168
- `--font-sans-base`); `styles/fonts` fills these slots for the bundled faces.
176
+ `--font-sans-base`); the opt-in `styles/fonts` overwrites `--font-sans-base` (and
177
+ `--font-sans-vi`) with the bundled DXS stack — **M PLUS 2** primary with **Noto
178
+ Sans JP** as the CJK fallback.
179
+
180
+ > **The bundled faces changed in v18.** v16 bundled Noto Sans JP + Montserrat;
181
+ > v18 bundles M PLUS 2 (primary, incl. Vietnamese coverage) + Noto Sans JP (CJK
182
+ > fallback). If your design spec named the v16 faces, set the tokens yourself
183
+ > instead of relying on the bundle.
169
184
 
170
185
  ```tsx
171
186
  import { AppProvider } from "@godxjp/ui/app"; // locale, tz, date/time format
@@ -1,14 +1,22 @@
1
1
  import type { PageDensityProp } from "../props/vocabulary/layout.prop.js";
2
2
  /** Light / dark spine — `data-theme` (alias of the legacy `.dark` class). */
3
3
  export type AppTheme = "light" | "dark";
4
- /** Primary-palette preset — `data-brand`. `null` = keep the app's own `--primary`. */
5
- export type AppBrand = "brand" | "crm" | "logistics" | "partner" | "slate";
4
+ /**
5
+ * Primary-palette preset — `data-brand`. `null` = keep the app's own `--primary`.
6
+ *
7
+ * `"dxs"` is the CANONICAL DXS preset: unlike the per-vertical tints it also binds the canonical
8
+ * hosted-identity surface contract (auth measure/density/insets + the brand-green identity mark),
9
+ * so every surface matches the canonical artboards with no consumer page CSS (gh#214). See
10
+ * `src/tokens/axes.css` for the bindings and `@godxjp/ui/theme/dxs.canonical.css` for the
11
+ * copy-free stylesheet entry point.
12
+ */
13
+ export type AppBrand = "brand" | "crm" | "logistics" | "partner" | "slate" | "dxs";
6
14
  /** Control / table / spacing density — `data-density` (same vocab as PageContainer). */
7
15
  export type AppDensity = PageDensityProp;
8
16
  /** Base type size — `data-font-size`; a preset rescales the whole golden scale. */
9
17
  export type AppFontSize = "sm" | "default" | "lg";
10
18
  export declare const APP_THEMES: readonly ["light", "dark"];
11
- export declare const APP_BRANDS: readonly ["brand", "crm", "logistics", "partner", "slate"];
19
+ export declare const APP_BRANDS: readonly ["brand", "crm", "logistics", "partner", "slate", "dxs"];
12
20
  export declare const APP_DENSITIES: readonly ["compact", "default", "comfortable"];
13
21
  export declare const APP_FONT_SIZES: readonly ["sm", "default", "lg"];
14
22
  /** The axes as held by AppProvider. `brand: null` opts out (app token wins).
@@ -4,7 +4,8 @@ const APP_BRANDS = [
4
4
  "crm",
5
5
  "logistics",
6
6
  "partner",
7
- "slate"
7
+ "slate",
8
+ "dxs"
8
9
  ];
9
10
  const APP_DENSITIES = [
10
11
  "compact",
@@ -30,15 +30,29 @@ type ChartFrameProps = {
30
30
  imgSummary: string;
31
31
  /** True when there is data to plot (otherwise children render an empty state). */
32
32
  hasData: boolean;
33
- height: number;
33
+ /**
34
+ * Canvas height in px. OPTIONAL: recharts needs a measured pixel box, but a
35
+ * CSS-drawn chart leaves it undefined and sizes its plot from a token tier
36
+ * instead (`size` → `data-size` → `--*-plot-height`), so no geometry is baked
37
+ * into an inline style.
38
+ */
39
+ height?: number;
40
+ /** Mirrored onto the figure as `data-size` so CSS can pick a token height tier. */
41
+ size?: "xs" | "sm" | "md" | "lg";
42
+ /**
43
+ * Content rendered BELOW the plot, outside the `role="img"` canvas — an activity
44
+ * footer, a delta, a link. Kept outside so interactive children stay reachable.
45
+ */
46
+ footer?: React.ReactNode;
34
47
  className?: string;
35
48
  id?: string;
36
49
  children: React.ReactNode;
50
+ ref?: React.Ref<HTMLElement>;
37
51
  };
38
52
  /**
39
- * The accessible figure wrapper. The recharts SVG passed as `children` is wrapped
53
+ * The accessible figure wrapper. The chart body passed as `children` is wrapped
40
54
  * in a `role="img"` element with a short `aria-label`; the full data is exposed as
41
55
  * a sibling visually-hidden list referenced by the figure's `aria-describedby`.
42
56
  */
43
- export declare function ChartFrame({ label, description, summaryRows, imgSummary, hasData, height, className, id, children, }: ChartFrameProps): React.JSX.Element;
57
+ export declare function ChartFrame({ label, description, summaryRows, imgSummary, hasData, height, size, footer, className, id, children, ref, }: ChartFrameProps): React.JSX.Element;
44
58
  export {};
@@ -34,9 +34,12 @@ function ChartFrame({
34
34
  imgSummary,
35
35
  hasData,
36
36
  height,
37
+ size,
38
+ footer,
37
39
  className,
38
40
  id,
39
- children
41
+ children,
42
+ ref
40
43
  }) {
41
44
  const reactId = React.useId();
42
45
  const titleId = `${id ?? reactId}-title`;
@@ -44,14 +47,17 @@ function ChartFrame({
44
47
  return /* @__PURE__ */ jsxs(
45
48
  "figure",
46
49
  {
50
+ ref,
47
51
  id,
48
52
  "data-slot": "chart",
53
+ "data-size": size,
49
54
  className: cn("ui-chart", className),
50
55
  "aria-labelledby": titleId,
51
56
  "aria-describedby": descId,
52
57
  children: [
53
58
  /* @__PURE__ */ jsx("figcaption", { id: titleId, className: "ui-chart-title", children: label }),
54
59
  /* @__PURE__ */ jsx("div", { className: "ui-chart-canvas", style: { height }, role: "img", "aria-label": imgSummary, children }),
60
+ footer ? /* @__PURE__ */ jsx("div", { className: "ui-chart-footer", children: footer }) : null,
55
61
  /* @__PURE__ */ jsxs("p", { id: descId, className: "sr-only", children: [
56
62
  description ? /* @__PURE__ */ jsxs(Fragment, { children: [
57
63
  description,
@@ -14,8 +14,29 @@ export declare function buildCartesianSummary(data: ChartDatum[], series: ChartS
14
14
  series: number;
15
15
  points: number;
16
16
  }) => string): ChartSummary;
17
+ /**
18
+ * Build the "category: value" rows for a SINGLE-series chart (pie slices, a
19
+ * compact bar trend…). `annotate` decorates the row of a highlighted datum —
20
+ * a text alternative for an emphasis that is otherwise colour-only (WCAG 1.4.1).
21
+ */
22
+ export declare function buildSeriesRows(data: ChartDatum[], categoryKey: string, valueKey: string, fmt: Formatter, annotate?: (index: number) => string | undefined): string[];
17
23
  /** Build the per-slice rows + a one-line summary for a pie chart. */
18
24
  export declare function buildPieSummary(data: ChartDatum[], dataKey: string, nameKey: string, fmt: Formatter, summaryLine: (counts: {
19
25
  slices: number;
20
26
  }) => string): ChartSummary;
27
+ /** Build the per-category rows + a one-line summary for a single-series trend. */
28
+ export declare function buildTrendSummary(data: ChartDatum[], categoryKey: string, valueKey: string, fmt: Formatter, summaryLine: (counts: {
29
+ points: number;
30
+ }) => string, annotate?: (index: number) => string | undefined): ChartSummary;
31
+ /**
32
+ * Normalize raw values to 0..1 bar ratios against the largest magnitude.
33
+ * Negative values plot at the baseline (a compact trend has no negative axis) but
34
+ * still read their true figure in the text alternative.
35
+ */
36
+ export declare function trendRatios(data: ChartDatum[], valueKey: string): number[];
37
+ /**
38
+ * Resolve `emphasizedIndex` against a row count. Negative counts from the end
39
+ * (`-1` = the latest datum). Out-of-range → no emphasis.
40
+ */
41
+ export declare function resolveEmphasizedIndex(index: number | undefined, length: number): number;
21
42
  export {};
@@ -11,13 +11,40 @@ function buildCartesianSummary(data, series, categoryKey, fmt, list, summaryLine
11
11
  });
12
12
  return { rows, img: summaryLine({ series: series.length, points: data.length }) };
13
13
  }
14
+ function buildSeriesRows(data, categoryKey, valueKey, fmt, annotate) {
15
+ return data.map((datum, index) => {
16
+ const row = `${String(datum[categoryKey] ?? "")}: ${fmt.format(numeric(datum[valueKey]))}`;
17
+ const note = annotate?.(index);
18
+ return note ? `${row} (${note})` : row;
19
+ });
20
+ }
14
21
  function buildPieSummary(data, dataKey, nameKey, fmt, summaryLine) {
15
- const rows = data.map(
16
- (datum) => `${String(datum[nameKey] ?? "")}: ${fmt.format(numeric(datum[dataKey]))}`
17
- );
18
- return { rows, img: summaryLine({ slices: data.length }) };
22
+ return {
23
+ rows: buildSeriesRows(data, nameKey, dataKey, fmt),
24
+ img: summaryLine({ slices: data.length })
25
+ };
26
+ }
27
+ function buildTrendSummary(data, categoryKey, valueKey, fmt, summaryLine, annotate) {
28
+ return {
29
+ rows: buildSeriesRows(data, categoryKey, valueKey, fmt, annotate),
30
+ img: summaryLine({ points: data.length })
31
+ };
32
+ }
33
+ function trendRatios(data, valueKey) {
34
+ const values = data.map((datum) => Math.max(0, numeric(datum[valueKey])));
35
+ const max = values.reduce((acc, value) => value > acc ? value : acc, 0);
36
+ return values.map((value) => max > 0 ? value / max : 0);
37
+ }
38
+ function resolveEmphasizedIndex(index, length) {
39
+ if (index === void 0 || !Number.isInteger(index) || length === 0) return -1;
40
+ const resolved = index < 0 ? length + index : index;
41
+ return resolved >= 0 && resolved < length ? resolved : -1;
19
42
  }
20
43
  export {
21
44
  buildCartesianSummary,
22
- buildPieSummary
45
+ buildPieSummary,
46
+ buildSeriesRows,
47
+ buildTrendSummary,
48
+ resolveEmphasizedIndex,
49
+ trendRatios
23
50
  };
@@ -0,0 +1,30 @@
1
+ import * as React from "react";
2
+ import type { CompactBarTrendProp } from "../../props/components/charts.prop.js";
3
+ export type { CompactBarTrendProp, CompactBarTrendProp as CompactBarTrendProps, } from "../../props/components/charts.prop.js";
4
+ /**
5
+ * CompactBarTrend — a **dependency-free** compact vertical bar trend for dashboard
6
+ * summary cards (a seven-day "new organizations" strip, a weekly activity pulse…).
7
+ *
8
+ * Unlike `BarChart` it does NOT need the `recharts` optional peer: the marks are
9
+ * token-sized CSS blocks, so a consumer that may not add dependencies still gets a
10
+ * real framework chart instead of a page-local grid with inline heights.
11
+ *
12
+ * - **Geometry is entirely token-owned** — `--chart-trend-plot-height-{xs,sm,md,lg}`,
13
+ * `--chart-trend-bar-{gap,radius,max-width,min-height}`. The only inline value is
14
+ * the datum itself (`--chart-trend-bar-value`, a 0..1 ratio of the largest bar).
15
+ * - **Colour is token-owned** — muted marks read `--chart-trend-bar-background`
16
+ * (default `hsl(var(--muted-foreground) / .75)`), the emphasized mark reads
17
+ * `--chart-trend-bar-emphasis-background` (default `hsl(var(--primary))`).
18
+ * - **a11y** — reuses `ChartFrame`: a `<figure>` with a visible `<figcaption>`, a
19
+ * `role="img"` plot named by a localized one-line summary, and a visually-hidden
20
+ * per-category value list referenced through `aria-describedby` (WCAG 1.1.1). The
21
+ * emphasized bar is ALSO named in that list, so the highlight is never
22
+ * colour-only (WCAG 1.4.1). The graphic holds no focusable elements — anything
23
+ * interactive belongs in `footer`, which renders outside the graphic.
24
+ * - **i18n** — values are formatted with the active locale's `Intl.NumberFormat`;
25
+ * every generated string comes from `t()`.
26
+ *
27
+ * Import from `@godxjp/ui/charts/compact-bar-trend`. The isolated public entry keeps
28
+ * consumers without the optional `recharts` peer away from the peer-backed charts barrel.
29
+ */
30
+ export declare function CompactBarTrend({ data, categoryKey, valueKey, label, description, size, emphasizedIndex, showCategoryLabels, numberFormat, footer, emptyMessage, className, id, ref, }: CompactBarTrendProp): React.JSX.Element;
@@ -0,0 +1,74 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { cn } from "../../lib/utils.js";
5
+ import { EmptyState } from "../data-display/empty-state.js";
6
+ import { useTranslation } from "../../i18n/use-translation.js";
7
+ import { ChartFrame, useChartNumberFormat } from "./chart-frame.js";
8
+ import { buildTrendSummary, resolveEmphasizedIndex, trendRatios } from "./chart-summary.js";
9
+ function CompactBarTrend({
10
+ data,
11
+ categoryKey,
12
+ valueKey,
13
+ label,
14
+ description,
15
+ size = "xs",
16
+ emphasizedIndex,
17
+ showCategoryLabels = true,
18
+ numberFormat,
19
+ footer,
20
+ emptyMessage,
21
+ className,
22
+ id,
23
+ ref
24
+ }) {
25
+ const { t } = useTranslation();
26
+ const fmt = useChartNumberFormat(numberFormat);
27
+ const hasData = data.length > 0;
28
+ const emphasized = resolveEmphasizedIndex(emphasizedIndex, data.length);
29
+ const ratios = React.useMemo(() => trendRatios(data, valueKey), [data, valueKey]);
30
+ const summary = buildTrendSummary(
31
+ data,
32
+ categoryKey,
33
+ valueKey,
34
+ fmt,
35
+ (counts) => t("chart.summaryTrend", { points: fmt.format(counts.points) }),
36
+ (index) => index === emphasized ? t("chart.trendCurrent") : void 0
37
+ );
38
+ return /* @__PURE__ */ jsx(
39
+ ChartFrame,
40
+ {
41
+ ref,
42
+ label,
43
+ description,
44
+ summaryRows: summary.rows,
45
+ imgSummary: summary.img,
46
+ hasData,
47
+ size,
48
+ footer,
49
+ className: cn("ui-chart-trend", className),
50
+ id,
51
+ children: hasData ? /* @__PURE__ */ jsxs("div", { className: "ui-chart-trend-body", children: [
52
+ /* @__PURE__ */ jsx("div", { className: "ui-chart-trend-plot", "aria-hidden": "true", children: data.map((datum, index) => /* @__PURE__ */ jsx("div", { className: "ui-chart-trend-column", children: /* @__PURE__ */ jsx(
53
+ "div",
54
+ {
55
+ className: "ui-chart-trend-bar",
56
+ "data-emphasized": index === emphasized ? "true" : void 0,
57
+ style: { "--chart-trend-bar-value": ratios[index] }
58
+ }
59
+ ) }, `${String(datum[categoryKey])}-${index}`)) }),
60
+ showCategoryLabels ? /* @__PURE__ */ jsx("div", { className: "ui-chart-trend-ticks", "aria-hidden": "true", children: data.map((datum, index) => /* @__PURE__ */ jsx(
61
+ "span",
62
+ {
63
+ className: "ui-chart-trend-tick",
64
+ children: String(datum[categoryKey] ?? "")
65
+ },
66
+ `${String(datum[categoryKey])}-${index}`
67
+ )) }) : null
68
+ ] }) : /* @__PURE__ */ jsx(EmptyState, { title: emptyMessage ?? t("chart.empty") })
69
+ }
70
+ );
71
+ }
72
+ export {
73
+ CompactBarTrend
74
+ };
@@ -8,6 +8,12 @@ export { LineChart } from "./line-chart.js";
8
8
  export type { LineChartProp, LineChartProps } from "./line-chart.js";
9
9
  export { BarChart } from "./bar-chart.js";
10
10
  export type { BarChartProp, BarChartProps } from "./bar-chart.js";
11
+ /**
12
+ * Dependency-free compact trend. Consumers without recharts should use the isolated
13
+ * `@godxjp/ui/charts/compact-bar-trend` entry so this barrel does not link peer-backed charts.
14
+ */
15
+ export { CompactBarTrend } from "./compact-bar-trend.js";
16
+ export type { CompactBarTrendProp, CompactBarTrendProps } from "./compact-bar-trend.js";
11
17
  export { AreaChart } from "./area-chart.js";
12
18
  export type { AreaChartProp, AreaChartProps } from "./area-chart.js";
13
19
  export { PieChart } from "./pie-chart.js";
@@ -1,5 +1,6 @@
1
1
  import { LineChart } from "./line-chart.js";
2
2
  import { BarChart } from "./bar-chart.js";
3
+ import { CompactBarTrend } from "./compact-bar-trend.js";
3
4
  import { AreaChart } from "./area-chart.js";
4
5
  import { PieChart } from "./pie-chart.js";
5
6
  import { CHART_COLORS } from "./chart-frame.js";
@@ -7,6 +8,7 @@ export {
7
8
  AreaChart,
8
9
  BarChart,
9
10
  CHART_COLORS,
11
+ CompactBarTrend,
10
12
  LineChart,
11
13
  PieChart
12
14
  };
@@ -24,4 +24,12 @@ export interface BadgeProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "
24
24
  children?: React.ReactNode;
25
25
  }
26
26
  export declare function Badge({ className, variant, shape, tone, icon, status, children, ...props }: BadgeProps): React.JSX.Element;
27
+ /**
28
+ * Status-aware badge with the shared domain-status-to-tone mapping.
29
+ *
30
+ * `Badge` remains the general-purpose primitive. Prefer `StatusBadge` when the
31
+ * `status` prop drives presentation so status colours remain consistent across
32
+ * products.
33
+ */
34
+ export declare const StatusBadge: typeof Badge;
27
35
  export {};
@@ -29,6 +29,10 @@ const STATUS_MAP = {
29
29
  done: { tone: "success", icon: CheckCircle2 },
30
30
  permanent: { tone: "success", icon: CheckCircle2 },
31
31
  succeeded: { tone: "success", icon: CheckCircle2 },
32
+ trialing: { tone: "info", icon: Clock },
33
+ past_due: { tone: "warning", icon: AlertCircle },
34
+ incomplete: { tone: "default", icon: Circle },
35
+ canceled: { tone: "destructive", icon: XCircle },
32
36
  draft: { tone: "neutral", icon: Circle },
33
37
  pending: { tone: "warning", icon: Clock },
34
38
  scheduled: { tone: "info", icon: Clock },
@@ -109,6 +113,8 @@ function Badge({
109
113
  }
110
114
  );
111
115
  }
116
+ const StatusBadge = Badge;
112
117
  export {
113
- Badge
118
+ Badge,
119
+ StatusBadge
114
120
  };
@@ -1,12 +1,12 @@
1
1
  import * as React from "react";
2
2
  import { flexRender, type OnChangeFn, type PaginationState, type RowData, type VisibilityState } from "@tanstack/react-table";
3
- import type { ColumnDefProp, DensityProp, SortStateProp } from "../../props/vocabulary/index.js";
3
+ import type { BreakpointProp, ColumnDefProp, DensityProp, SortStateProp, TablePresetProp } from "../../props/vocabulary/index.js";
4
4
  export type Density = DensityProp;
5
5
  /**
6
6
  * Lean column definition — the simple, common-case column API. `render` shapes
7
7
  * a cell; `sortable` opts the column into the sort cycle; `align` / `width` /
8
- * `pin` / `hiddenOnMobile` tune layout; `enableHiding` (default true) lists the
9
- * column in DataTable.ViewOptions. Adapted to a TanStack column internally.
8
+ * `pin` / `hiddenOnMobile` / `priority` tune layout; `enableHiding` (default true)
9
+ * lists the column in DataTable.ViewOptions. Adapted to a TanStack column internally.
10
10
  */
11
11
  export type ColumnDef<T> = ColumnDefProp<T>;
12
12
  declare module "@tanstack/react-table" {
@@ -52,12 +52,48 @@ interface DataTableProps<T> {
52
52
  loading?: boolean;
53
53
  /** Custom empty content when `data` is empty; defaults to a built-in EmptyState. */
54
54
  empty?: React.ReactNode;
55
+ /**
56
+ * Failure state (#216). `true` renders the built-in localized error message; any other node
57
+ * REPLACES it (e.g. an `Alert` carrying an error code + request id). Pass `undefined`/`false`
58
+ * when the read succeeded. Never pass a raw `Error` object — it is not renderable.
59
+ * Announced via `role="alert"`; precedence is loading → denied → error → empty → rows.
60
+ */
61
+ error?: React.ReactNode;
62
+ /**
63
+ * Permission-denied state (#216) — the read was refused, not failed. `true` renders the
64
+ * built-in localized "no access" message; any other node replaces it. Distinct from `error`
65
+ * so a 403 never offers a pointless retry.
66
+ */
67
+ denied?: React.ReactNode;
68
+ /** Retry handler for the built-in `error` state; omit to hide the retry action. */
69
+ onRetry?: () => void;
55
70
  /** Zebra-stripe the body rows (even rows get a subtle fill). */
56
71
  striped?: boolean;
57
72
  /** Highlight a row on hover even when it is not clickable (no `onRowClick`). */
58
73
  hoverable?: boolean;
59
74
  /** Pin the header to the top while the body scrolls. Default true. */
60
75
  stickyHeader?: boolean;
76
+ /**
77
+ * Named collection contract (gh#253) — the SAME preset the `Table` primitive owns, forwarded to
78
+ * the table DataTable renders, so a TanStack-driven queue gets the identical responsive contract.
79
+ * `"default"` (the default) emits no attribute and matches no selector: an existing DataTable is
80
+ * byte-identical. `"action-collection"` is the canonical dense approval / action queue: below
81
+ * `collapseBelow` the desktop intrinsic column widths give way to the token-owned column-PRIORITY
82
+ * measures (`--table-action-collection-*`) under `table-layout: fixed`, cells wrap, and the
83
+ * bordered surface drops its `--table-surface-min-inline-size` floor — so requester · target ·
84
+ * reason · requested date · row actions all stay inside a 390px frame with no horizontal scroll.
85
+ * Mark each column with `priority` on its `ColumnDef`; DataTable stamps it on the `<th>` AND the
86
+ * `<td>` for you. The table stays a REAL table: no `display` change, no role rewriting, no card
87
+ * swap, so header association, `aria-sort` and screen-reader navigation are identical at 1440.
88
+ */
89
+ preset?: TablePresetProp;
90
+ /**
91
+ * Step at which `preset="action-collection"` switches to the compact priority measures, measured
92
+ * against the TABLE's own container (a container query), not the viewport — a table inside a
93
+ * master rail collapses before the page does. Default `"sm"`. Ignored while `preset` is
94
+ * `"default"`.
95
+ */
96
+ collapseBelow?: BreakpointProp;
61
97
  /**
62
98
  * Per-row className for state-based row tinting (e.g. flag an invalid or
63
99
  * empty record). Returned classes are appended last, so they win over the
@@ -67,7 +103,7 @@ interface DataTableProps<T> {
67
103
  className?: string;
68
104
  children?: React.ReactNode;
69
105
  }
70
- export declare function DataTable<T>({ data, columns, getRowId, selectable, selected: controlledSelected, onSelectChange, onRowClick, density: controlledDensity, onDensityChange, sort, onSortChange, globalFilter: controlledGlobalFilter, onGlobalFilterChange, pagination: controlledPagination, onPaginationChange, rowCount, columnVisibility: controlledVisibility, onColumnVisibilityChange, manualSorting, manualFiltering, manualPagination, loading, empty, striped, hoverable, stickyHeader, rowClassName, className, children, }: DataTableProps<T>): React.JSX.Element;
106
+ export declare function DataTable<T>({ data, columns, getRowId, selectable, selected: controlledSelected, onSelectChange, onRowClick, density: controlledDensity, onDensityChange, sort, onSortChange, globalFilter: controlledGlobalFilter, onGlobalFilterChange, pagination: controlledPagination, onPaginationChange, rowCount, columnVisibility: controlledVisibility, onColumnVisibilityChange, manualSorting, manualFiltering, manualPagination, loading, empty, error, denied, onRetry, striped, hoverable, stickyHeader, preset, collapseBelow, rowClassName, className, children, }: DataTableProps<T>): React.JSX.Element;
71
107
  export declare namespace DataTable {
72
108
  var Toolbar: ({ children, className, }: {
73
109
  children?: React.ReactNode;
@@ -10,6 +10,7 @@ import {
10
10
  useReactTable
11
11
  } from "@tanstack/react-table";
12
12
  import {
13
+ AlertCircle,
13
14
  ArrowDown,
14
15
  ArrowUp,
15
16
  ChevronLeft,
@@ -18,6 +19,8 @@ import {
18
19
  Layers,
19
20
  Layers2,
20
21
  MoreHorizontal,
22
+ RefreshCw,
23
+ ShieldAlert,
21
24
  SlidersHorizontal
22
25
  } from "lucide-react";
23
26
  import { useTranslation } from "../../i18n/use-translation.js";
@@ -113,9 +116,14 @@ function DataTable({
113
116
  manualPagination = false,
114
117
  loading = false,
115
118
  empty,
119
+ error,
120
+ denied,
121
+ onRetry,
116
122
  striped = false,
117
123
  hoverable = false,
118
124
  stickyHeader = true,
125
+ preset = "default",
126
+ collapseBelow = "sm",
119
127
  rowClassName,
120
128
  className,
121
129
  children
@@ -207,9 +215,14 @@ function DataTable({
207
215
  onRowClick,
208
216
  loading,
209
217
  empty,
218
+ error,
219
+ denied,
220
+ onRetry,
210
221
  striped,
211
222
  hoverable,
212
223
  stickyHeader,
224
+ preset,
225
+ collapseBelow,
213
226
  rowClassName
214
227
  };
215
228
  const hasContent = React.Children.toArray(children).some(
@@ -378,12 +391,20 @@ DataTable.Content = function DataTableContent() {
378
391
  onRowClick,
379
392
  loading,
380
393
  empty,
394
+ error,
395
+ denied,
396
+ onRetry,
381
397
  striped,
382
398
  hoverable,
383
399
  stickyHeader,
400
+ preset,
401
+ collapseBelow,
384
402
  rowClassName
385
403
  } = useDataTableContext();
386
404
  const { t } = useTranslation();
405
+ const presetAttr = preset === "default" ? void 0 : preset;
406
+ const isDenied = denied != null && denied !== false;
407
+ const isError = !isDenied && error != null && error !== false;
387
408
  const rowPadding = tableRowHeightClass;
388
409
  const cellPadding = tableCellPaddingClass;
389
410
  const visibleColumns = table.getVisibleLeafColumns().map((c) => c.columnDef.meta?.lean).filter((c) => !!c);
@@ -425,10 +446,11 @@ DataTable.Content = function DataTableContent() {
425
446
  children: /* @__PURE__ */ jsx(
426
447
  "div",
427
448
  {
428
- className: "ui-data-table-surface min-w-[640px] sm:min-w-0",
449
+ className: "ui-data-table-surface",
450
+ "data-preset": presetAttr,
429
451
  "data-striped": striped ? "" : void 0,
430
452
  "data-hoverable": hoverable ? "" : void 0,
431
- children: /* @__PURE__ */ jsxs(Table, { scrollable: false, children: [
453
+ children: /* @__PURE__ */ jsxs(Table, { scrollable: false, preset, collapseBelow, children: [
432
454
  /* @__PURE__ */ jsx(TableHeader, { className: cn("bg-secondary", stickyHeader && "sticky top-0 z-10"), children: /* @__PURE__ */ jsxs(TableRow, { children: [
433
455
  selectable && /* @__PURE__ */ jsx(TableHead, { className: "w-10", children: /* @__PURE__ */ jsx(DataTable.SelectAll, {}) }),
434
456
  visibleColumns.map((col) => {
@@ -444,6 +466,7 @@ DataTable.Content = function DataTableContent() {
444
466
  return /* @__PURE__ */ jsx(
445
467
  TableHead,
446
468
  {
469
+ priority: col.priority,
447
470
  "data-empty": headerEmpty || void 0,
448
471
  "aria-sort": isSortable ? isActiveSort ? activeSort?.direction === "asc" ? "ascending" : "descending" : "none" : void 0,
449
472
  className: cn(
@@ -480,6 +503,7 @@ DataTable.Content = function DataTableContent() {
480
503
  visibleColumns.map((col, j) => /* @__PURE__ */ jsx(
481
504
  TableCell,
482
505
  {
506
+ priority: col.priority,
483
507
  className: cn(
484
508
  cellPadding,
485
509
  col.width,
@@ -503,6 +527,48 @@ DataTable.Content = function DataTableContent() {
503
527
  col.key
504
528
  ))
505
529
  ] }, `skeleton-${i}`))
530
+ ) : isDenied ? (
531
+ // DENIED (#216): the read was REFUSED, not failed — no retry is offered, because
532
+ // repeating a 403 cannot succeed. `warning` (not destructive) keeps it a policy
533
+ // statement rather than a system fault. Announced politely: a permission boundary
534
+ // is expected information, not an interruption.
535
+ /* @__PURE__ */ jsx(TableRow, { className: "hover:bg-transparent", children: /* @__PURE__ */ jsx(
536
+ TableCell,
537
+ {
538
+ colSpan: emptyColSpan,
539
+ className: "ui-data-table-empty",
540
+ "aria-live": "polite",
541
+ children: denied === true ? /* @__PURE__ */ jsx(
542
+ EmptyState,
543
+ {
544
+ icon: ShieldAlert,
545
+ tone: "warning",
546
+ title: t("dataTable.denied"),
547
+ description: t("dataTable.deniedDescription"),
548
+ titleAs: "p"
549
+ }
550
+ ) : denied
551
+ }
552
+ ) })
553
+ ) : isError ? (
554
+ // ERROR (#216): a failed read. Announced assertively (`role="alert"`) because the
555
+ // user's request did not complete — unlike empty/denied, which are states of the data
556
+ // itself. The role sits on an inner wrapper, never on the <td>: overriding a cell's
557
+ // role would strip it out of the table's grid semantics for AT.
558
+ /* @__PURE__ */ jsx(TableRow, { className: "hover:bg-transparent", children: /* @__PURE__ */ jsx(TableCell, { colSpan: emptyColSpan, className: "ui-data-table-empty", children: /* @__PURE__ */ jsx("div", { role: "alert", children: error === true ? /* @__PURE__ */ jsx(
559
+ EmptyState,
560
+ {
561
+ icon: AlertCircle,
562
+ tone: "destructive",
563
+ title: t("dataTable.error"),
564
+ description: t("dataTable.errorDescription"),
565
+ titleAs: "p",
566
+ action: onRetry ? /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: onRetry, children: [
567
+ /* @__PURE__ */ jsx(RefreshCw, { "aria-hidden": "true" }),
568
+ t("common.retry")
569
+ ] }) : void 0
570
+ }
571
+ ) : error }) }) })
506
572
  ) : rowCount === 0 ? /* @__PURE__ */ jsx(TableRow, { className: "hover:bg-transparent", children: /* @__PURE__ */ jsx(
507
573
  TableCell,
508
574
  {
@@ -559,6 +625,7 @@ DataTable.Content = function DataTableContent() {
559
625
  visibleColumns.map((col) => /* @__PURE__ */ jsx(
560
626
  TableCell,
561
627
  {
628
+ priority: col.priority,
562
629
  className: cn(
563
630
  cellPadding,
564
631
  col.width,
@@ -22,7 +22,7 @@ function EmptyState({
22
22
  role: "status",
23
23
  className: cn("ui-empty-state", className),
24
24
  children: [
25
- Icon && variant !== "compact" && /* @__PURE__ */ jsx("div", { className: "ui-empty-state-icon", children: /* @__PURE__ */ jsx(Icon, { className: "text-muted-foreground size-6", "aria-hidden": "true" }) }),
25
+ Icon && variant !== "compact" && /* @__PURE__ */ jsx("div", { className: "ui-empty-state-icon", children: /* @__PURE__ */ jsx(Icon, { className: "size-6", "aria-hidden": "true" }) }),
26
26
  createElement(TitleTag, { className: "ui-empty-state-title" }, title),
27
27
  description && /* @__PURE__ */ jsx("p", { className: "ui-empty-state-description", children: description }),
28
28
  action && /* @__PURE__ */ jsx("div", { children: action })
@@ -1,7 +1,7 @@
1
- export { Badge } from "./badge.js";
1
+ export { Badge, StatusBadge } from "./badge.js";
2
2
  export type { BadgeProps } from "./badge.js";
3
3
  export { ListRow } from "./list-row.js";
4
- export type { ListRowProps } from "./list-row.js";
4
+ export type { ListRowProps, ListRowDensity } from "./list-row.js";
5
5
  export { CredentialReveal } from "./credential-reveal.js";
6
6
  export type { CredentialRevealProp, CredentialRevealProps, CredentialRevealTone, } from "./credential-reveal.js";
7
7
  export { QrCode } from "./qr-code.js";
@@ -10,6 +10,8 @@ export type { QrCodeProp } from "../../props/components/data-display.prop.js";
10
10
  export { Avatar, AvatarImage, AvatarFallback } from "./avatar.js";
11
11
  export { Card, CardBar, CardContent, CardCover, CardDescription, CardFooter, CardHeader, CardTitle, CardAction, StatCard, } from "./card.js";
12
12
  export type { StatCardProps, CardBarProps } from "./card.js";
13
+ export { ServiceCatalogCta, ServiceLauncherCard, ServiceLauncherCardSkeleton, } from "./service-launcher-card.js";
14
+ export type { ServiceCatalogCtaProps, ServiceLauncherCardProps, ServiceLauncherCardSkeletonProps, ServiceLauncherStatusTone, } from "./service-launcher-card.js";
13
15
  export { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "./table.js";
14
16
  export { Descriptions } from "./descriptions.js";
15
17
  export { DataTable, flexRender } from "./data-table.js";