@jirawatpyk/aura-react 4.9.0 → 4.10.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.
@@ -3,6 +3,9 @@ import * as React from "react";
3
3
  const n = function(x) {
4
4
  return Number(x).toLocaleString("en");
5
5
  };
6
+ const nsv = function(x) {
7
+ return Number(x).toLocaleString("sv-SE");
8
+ };
6
9
  const STRINGS = {
7
10
  en: {
8
11
  close: "Close",
@@ -199,6 +202,104 @@ const STRINGS = {
199
202
  accepts: function(list, max) {
200
203
  return [list, max && "\u0E44\u0E21\u0E48\u0E40\u0E01\u0E34\u0E19 " + max + " \u0E15\u0E48\u0E2D\u0E44\u0E1F\u0E25\u0E4C"].filter(Boolean).join(" \xB7 ");
201
204
  }
205
+ },
206
+ sv: {
207
+ close: "St\xE4ng",
208
+ dismiss: "St\xE4ng",
209
+ dismissToast: "St\xE4ng aviseringen",
210
+ notifications: "Aviseringar",
211
+ mainNav: "Huvudmeny",
212
+ breadcrumb: "Br\xF6dsmulor",
213
+ navigation: "Navigering",
214
+ openNav: "\xD6ppna menyn",
215
+ searching: "S\xF6ker\u2026",
216
+ noMatches: "Inga tr\xE4ffar",
217
+ clear: function(what) {
218
+ return "Rensa " + (what || "urvalet");
219
+ },
220
+ keepTyping: function(total) {
221
+ return "Skriv mer f\xF6r att begr\xE4nsa " + nsv(total) + " alternativ";
222
+ },
223
+ sortAsc: "Sortera stigande",
224
+ sortDesc: "Sortera fallande",
225
+ pin: "F\xE4st till v\xE4nster",
226
+ unpin: "Lossa kolumnen",
227
+ moveLeft: "Flytta v\xE4nster",
228
+ moveRight: "Flytta h\xF6ger",
229
+ hideColumn: "D\xF6lj kolumnen",
230
+ resetColumns: "\xC5terst\xE4ll kolumner",
231
+ selectRows: "V\xE4lj rader",
232
+ selectAllRows: "V\xE4lj alla rader",
233
+ deselectAllRows: "Avmarkera alla rader",
234
+ selectAll: "V\xE4lj alla",
235
+ selectRow: function(k) {
236
+ return "V\xE4lj " + k;
237
+ },
238
+ selectedCount: function(c) {
239
+ return nsv(c) + " valda";
240
+ },
241
+ pinned: "F\xE4st",
242
+ columnOptions: function(label) {
243
+ return "Alternativ f\xF6r kolumnen " + label;
244
+ },
245
+ column: function(label) {
246
+ return label ? "Kolumnen " + label : "Kolumn";
247
+ },
248
+ columns: "Kolumner",
249
+ showHideColumns: "Visa eller d\xF6lj kolumner",
250
+ empty: "Inget h\xE4r \xE4nnu",
251
+ loading: "Laddar\u2026",
252
+ loadingRows: "Laddar rader",
253
+ range: function(a, b, total) {
254
+ return a + "\u2013" + b + " av " + nsv(total);
255
+ },
256
+ page: function(p, total) {
257
+ return "Sida " + p + " av " + total;
258
+ },
259
+ prevPage: "F\xF6reg\xE5ende sida",
260
+ nextPage: "N\xE4sta sida",
261
+ rowCount: function(c) {
262
+ return nsv(c) + " rader";
263
+ },
264
+ actions: "\xC5tg\xE4rder",
265
+ optional: "valfritt",
266
+ timePlaceholder: "tt:mm",
267
+ timeInvalid: "Skriv en tid som 09:30",
268
+ timeOutOfRange: function(a, b) {
269
+ return "V\xE4lj en tid mellan " + a + " och " + b;
270
+ },
271
+ dropFiles: "Dra filer hit eller",
272
+ browse: "V\xE4lj filer",
273
+ browseOne: "V\xE4lj en fil",
274
+ remove: function(n2) {
275
+ return "Ta bort " + n2;
276
+ },
277
+ fileTooBig: function(max) {
278
+ return "St\xF6rre \xE4n " + max;
279
+ },
280
+ fileWrongType: "Den h\xE4r filtypen godtas inte",
281
+ tooManyFiles: function(n2) {
282
+ return "H\xF6gst " + n2 + " filer";
283
+ },
284
+ colorScheme: "F\xE4rgl\xE4ge",
285
+ increase: "\xD6ka",
286
+ decrease: "Minska",
287
+ stepDone: "klart",
288
+ stepOf: function(i, total) {
289
+ return "Steg " + i + " av " + total;
290
+ },
291
+ schemeLight: "Ljust",
292
+ schemeDark: "M\xF6rkt",
293
+ schemeSystem: "System",
294
+ uploading: "Laddar upp\u2026",
295
+ images: "Bilder",
296
+ pagination: "Sidnumrering",
297
+ pageN: function(p) {
298
+ return "Sida " + p;
299
+ },
300
+ accepts: function(list, max) {
301
+ return [list, max && "h\xF6gst " + max + " per fil"].filter(Boolean).join(", ");
302
+ }
202
303
  }
203
304
  };
204
305
  const LocaleContext = React.createContext(null);
@@ -209,16 +310,21 @@ function AuraProvider(props) {
209
310
  return {
210
311
  locale: props.locale || "en",
211
312
  calendar: props.calendar || null,
212
- strings: props.strings ? Object.assign({}, base, props.strings) : base
313
+ strings: props.strings ? Object.assign({}, base, props.strings) : base,
314
+ linkComponent: props.linkComponent || null
213
315
  };
214
316
  },
215
- [props.locale, props.calendar, props.strings]
317
+ [props.locale, props.calendar, props.strings, props.linkComponent]
216
318
  );
217
319
  return /* @__PURE__ */ React.createElement(LocaleContext.Provider, { value }, props.children);
218
320
  }
219
321
  function useAuraLocale() {
220
322
  return React.useContext(LocaleContext) || { locale: null, calendar: null, strings: STRINGS.en };
221
323
  }
324
+ function useLinkComponent(own) {
325
+ const ctx = React.useContext(LocaleContext);
326
+ return own || ctx && ctx.linkComponent || "a";
327
+ }
222
328
  function useStrings() {
223
329
  return useAuraLocale().strings;
224
330
  }
@@ -226,5 +332,6 @@ export {
226
332
  AuraProvider,
227
333
  STRINGS,
228
334
  useAuraLocale,
335
+ useLinkComponent,
229
336
  useStrings
230
337
  };
package/dist/esm/theme.js CHANGED
@@ -105,6 +105,20 @@ function fit(hex, grounds, target, dir) {
105
105
  }
106
106
  return c;
107
107
  }
108
+ function atLuminance(hex, Y) {
109
+ const o = rgbToOklch(hexToRgb(hex)), C = Math.max(o[1], 0.02);
110
+ let lo = 0, hi = 1, c = hex;
111
+ for (let i = 0; i < 30; i++) {
112
+ const mid = (lo + hi) / 2;
113
+ c = oklchToHex(mid, C, o[2]);
114
+ if (luminance(c) < Y) lo = mid;
115
+ else hi = mid;
116
+ }
117
+ return c;
118
+ }
119
+ const CHART_Y = { light: 0.075, dark: 0.46 };
120
+ const SEQ_Y = { light: [0.26, 0.165, 0.1, 0.055, 0.028], dark: [0.14, 0.22, 0.34, 0.5, 0.7] };
121
+ const CHART_2 = "#0a8b7a";
108
122
  const ZINC = { 0: "#ffffff", 50: "#fafafa", 100: "#f4f4f5", 800: "#27272a", 900: "#18181b", 950: "#09090b" };
109
123
  const INK = "#18181b";
110
124
  function createTheme(opts) {
@@ -123,6 +137,14 @@ function createTheme(opts) {
123
137
  L["alert-info-fg"] = fit(b[800], [b[50]], 4.5, -1);
124
138
  L["alert-info-border"] = b[200];
125
139
  L["mesh-from"] = b[400];
140
+ L["chart-1"] = atLuminance(o.brand, CHART_Y.light);
141
+ D["chart-1"] = atLuminance(o.brand, CHART_Y.dark);
142
+ SEQ_Y.light.forEach(function(y, i) {
143
+ L["chart-seq-" + (i + 1)] = atLuminance(o.brand, y);
144
+ });
145
+ SEQ_Y.dark.forEach(function(y, i) {
146
+ D["chart-seq-" + (i + 1)] = atLuminance(o.brand, y);
147
+ });
126
148
  D["fg-accent"] = fit(b[300], darkGrounds, 4.5, 1);
127
149
  D["accent-violet"] = fit(b[400], darkGrounds, 3, 1);
128
150
  D["focus-ring"] = fit(b[400], darkGrounds, 3, 1);
@@ -182,6 +204,19 @@ function createTheme(opts) {
182
204
  check("dark", "fg-accent", g[0], 4.5, D["fg-accent"], g[1]);
183
205
  check("dark", "focus-ring", g[0], 3, D["focus-ring"], g[1]);
184
206
  });
207
+ [
208
+ ["light", ZINC[0], L],
209
+ ["light", ZINC[50], L],
210
+ ["dark", ZINC[900], D],
211
+ ["dark", ZINC[950], D]
212
+ ].forEach(function(g) {
213
+ const t = g[0], ground = g[1], m = g[2], name = ground === ZINC[0] || ground === ZINC[900] ? "bg-surface" : "bg-canvas";
214
+ ["chart-1", "chart-seq-1", "chart-seq-5"].forEach(function(k) {
215
+ check(t, k, name, 3, m[k], ground);
216
+ });
217
+ });
218
+ check("light", "chart-1", "chart-2 (lightness step)", 1.8, L["chart-1"], CHART_2);
219
+ check("dark", "chart-1", "chart-2 (lightness step)", 1.8, D["chart-1"], CHART_2);
185
220
  check("light", "fg-primary", "bg-selected", 4.5, INK, L["bg-selected"]);
186
221
  check("dark", "fg-primary", "bg-selected", 4.5, "#ffffff", D["bg-selected"]);
187
222
  check("light", "status-progress-fg", "status-progress-bg", 4.5, L["status-progress-fg"], L["status-progress-bg"]);
package/dist/index.d.ts CHANGED
@@ -1,15 +1,17 @@
1
1
  import * as React$1 from 'react';
2
2
 
3
+ /** Button look. */
4
+ export type ButtonVariant = "primary" | "secondary" | "creative" | "danger" | "danger-secondary";
3
5
  /** AURA pill button. Enterprise (`primary`, `secondary`) for product UI; `creative` for marketing moments only. */
4
6
  export interface ButtonProps extends React$1.ButtonHTMLAttributes<HTMLButtonElement> {
5
- /** Visual variant. Default `primary`. */
6
- variant?: "primary" | "secondary" | "creative";
7
+ /** Visual variant. Default `primary`. `danger` (filled) and `danger-secondary` (outline) are for irreversible or destructive actions only — typically the confirm button of a `Dialog role="alertdialog"`. */
8
+ variant?: ButtonVariant;
7
9
  /** Label text — short, Title Case English or Thai. */
8
10
  children: React$1.ReactNode;
9
11
  /** Leading icon name (see `IconName`). Hidden while loading. */
10
- icon?: IconName;
12
+ icon?: IconInput;
11
13
  /** Trailing icon name, e.g. `arrow-right` for forward actions. */
12
- iconRight?: IconName;
14
+ iconRight?: IconInput;
13
15
  /** Shows a spinner in place of the leading icon, sets aria-busy and swallows clicks. Keeps the label and width. */
14
16
  loading?: boolean;
15
17
  }
@@ -17,13 +19,13 @@ export interface ButtonProps extends React$1.ButtonHTMLAttributes<HTMLButtonElem
17
19
  export interface ButtonLinkProps extends Omit<React$1.AnchorHTMLAttributes<HTMLAnchorElement>, "children"> {
18
20
  /** Where the link goes. With `href`, Button renders an `<a>` and its ref is the `<a>`. */
19
21
  href: string;
20
- /** Visual variant. Default `primary`. */
21
- variant?: "primary" | "secondary" | "creative";
22
+ /** Visual variant. Default `primary`. `danger` (filled) and `danger-secondary` (outline) are for irreversible or destructive actions only — typically the confirm button of a `Dialog role="alertdialog"`. */
23
+ variant?: ButtonVariant;
22
24
  /** Label text — say where it goes ("View Orders"), not "Click here". */
23
25
  children: React$1.ReactNode;
24
- icon?: IconName;
26
+ icon?: IconInput;
25
27
  /** Trailing icon, e.g. `arrow-right`, or `external-link` with `target="_blank"`. */
26
- iconRight?: IconName;
28
+ iconRight?: IconInput;
27
29
  /** Looks unavailable and can't be followed: no href, `aria-disabled`, out of the Tab order. */
28
30
  disabled?: boolean;
29
31
  /** A router's link to render instead of `<a>`, e.g. `Link` from `next/link` (client-side navigation). It gets `href`, `className`, the children and the ref. */
@@ -61,6 +63,8 @@ export interface DataTableColumn {
61
63
  hideBelow?: number | "sm" | "md" | "lg" | "xl";
62
64
  /** Row actions (a DropdownMenu or IconButton). In stacked cards it sits top-right instead of in the field list. Give it an empty label. */
63
65
  actions?: boolean;
66
+ /** `end` right-aligns header and cells (amounts, counts) and uses tabular figures. Default `start`. */
67
+ align?: "start" | "end";
64
68
  }
65
69
  export interface DataTableSort {
66
70
  key: string;
@@ -68,7 +72,7 @@ export interface DataTableSort {
68
72
  }
69
73
  export interface DataTableEmpty {
70
74
  /** Default `inbox`; use `search` for "no matches". */
71
- icon?: IconName;
75
+ icon?: IconInput;
72
76
  /** Default "Nothing here yet". */
73
77
  title?: string;
74
78
  description?: string;
@@ -108,8 +112,20 @@ export interface DataTableProps {
108
112
  /** Adds drag/keyboard resize handles to every column that has a width. */
109
113
  resizable?: boolean;
110
114
  onColumnResize?: (key: string, width: number) => void;
111
- /** Shows skeleton rows and disables sorting, select-all and paging. */
115
+ /** Shows skeleton rows and disables sorting, select-all and paging. With `manual` and rows already shown, the rows stay (dimmed, with a progress bar) while the next page loads. */
112
116
  loading?: boolean;
117
+ /** Server mode: `rows` is already the current page, sorted by the server. The table doesn't sort or slice; it reports
118
+ * sort and page through onSortChange / onPageChange (or getPageHref links). Pair with `totalRows` and `pageSize`. */
119
+ manual?: boolean;
120
+ /** manual: rows across all pages (drives the page count, "1–25 of 312" and aria-rowcount). */
121
+ totalRows?: number;
122
+ /** Makes each row a link: the first column's content renders as the provider's linkComponent (or `<a>`), and a click
123
+ * or Enter anywhere on the row follows it. Ctrl/⌘-click opens a new tab as usual. */
124
+ getRowHref?: (row: Record<string, any>) => string;
125
+ /** Pager arrows become links to these URLs (search-param paging). Without onPageChange the link navigates. */
126
+ getPageHref?: (page: number) => string;
127
+ /** Router link for getRowHref / getPageHref. Default: AuraProvider's linkComponent, else `<a>`. */
128
+ linkComponent?: React$1.ElementType;
113
129
  /** Skeleton row count when there is no pageSize. Default 5. */
114
130
  skeletonRows?: number;
115
131
  /** Called on row click or Enter. */
@@ -133,10 +149,13 @@ export interface DataTableProps {
133
149
  stackBelow?: number;
134
150
  className?: string;
135
151
  }
152
+ /** An AURA icon name, or any icon element (e.g. `<Building />` from lucide-react). AURA sizes it and hides it from screen readers. */
153
+ export type IconInput = IconName | React$1.ReactElement;
136
154
  export type IconName = "check" | "x" | "plus" | "minus" | "search" | "chevron-down" | "chevron-up" | "chevron-left" | "chevron-right" | "arrow-right" | "arrow-up-right" | "arrow-up-down" | "loader-circle" | "circle-alert" | "circle-check" | "info" | "triangle-alert" | "settings" | "user" | "users" | "filter" | "ellipsis" | "external-link" | "copy" | "trash-2" | "pencil" | "download" | "upload" | "calendar" | "bell" | "menu" | "eye" | "log-out" | "circle" | "circle-dot-dashed" | "ban" | "arrow-up" | "arrow-down" | "inbox" | "pin" | "pin-off" | "eye-off" | "columns-3" | "arrow-left" | "rotate-ccw" | "house" | "layout-dashboard" | "folder" | "chart-column" | "file-text" | "mail" | "lock" | "clock" | "trending-up" | "trending-down" | "image" | "paperclip" | "cloud-upload" | "file" | "sun" | "moon" | "monitor";
137
155
  /** Lucide stroke icon drawn inline in currentColor. */
138
156
  export interface IconProps {
139
- name: IconName;
157
+ /** A name from the set, or an icon element of your own (sized and styled the same way). */
158
+ name: IconInput;
140
159
  /** `sm` 16 (default) · `md` 20 · `lg` 24, or a px number. */
141
160
  size?: "sm" | "md" | "lg" | number;
142
161
  /** Accessible name. Omit for decorative icons next to a text label (then aria-hidden). */
@@ -155,12 +174,14 @@ export interface SurfaceProps extends React$1.HTMLAttributes<HTMLElement> {
155
174
  }
156
175
  /** Sets the language of built-in labels (pagination, close buttons, empty states…) and the default date display for everything inside. */
157
176
  export interface AuraProviderProps {
158
- /** Default `en` strings when there is no provider (dates still default to Thai / พ.ศ.). */
159
- locale?: "th" | "en";
160
- /** Default calendar for DatePicker / DateRangePicker / Calendar / formatDate callers that read it. */
177
+ /** Built-in labels and date display. `sv` = Swedish labels, `sv-SE` dates, Monday weeks and the Gregorian calendar. Default `en` strings when there is no provider (dates still default to Thai / พ.ศ.). */
178
+ locale?: "th" | "en" | "sv";
179
+ /** Default calendar for DatePicker / DateRangePicker / Calendar / formatDate callers that read it. Unset: `buddhist` for `th` and `en` (unchanged), `gregory` for `sv`. */
161
180
  calendar?: "buddhist" | "gregory";
162
181
  /** Override individual strings. */
163
182
  strings?: Partial<Record<string, string | ((...args: any[]) => string)>>;
183
+ /** Your router's link (e.g. `Link` from `next/link`), used by every AURA component that renders a link: Button with `href`, SideNav, Breadcrumb, Stat, Pagination, DataTable row links. It gets `href`, `className`, `aria-current`, the children and the ref. A component's own `linkComponent` wins. */
184
+ linkComponent?: React$1.ElementType;
164
185
  children?: React$1.ReactNode;
165
186
  }
166
187
  export type StatusTone = "neutral" | "progress" | "ready" | "blocked";
@@ -191,7 +212,9 @@ export interface CheckboxProps extends Omit<React$1.InputHTMLAttributes<HTMLInpu
191
212
  }
192
213
  /** 32px round button holding one icon. */
193
214
  export interface IconButtonProps extends React$1.ButtonHTMLAttributes<HTMLButtonElement> {
194
- icon: IconName;
215
+ icon: IconInput;
216
+ /** `danger` colours the icon and hover for a destructive action (Delete, Revoke). */
217
+ tone?: "neutral" | "danger";
195
218
  /** Accessible name and tooltip (required). */
196
219
  label: string;
197
220
  /** Icon size. Default `sm` (16). */
@@ -199,7 +222,7 @@ export interface IconButtonProps extends React$1.ButtonHTMLAttributes<HTMLButton
199
222
  }
200
223
  export interface MenuItem {
201
224
  label?: string;
202
- icon?: IconName;
225
+ icon?: IconInput;
203
226
  /** Present = a checkable item (menuitemcheckbox). */
204
227
  checked?: boolean;
205
228
  disabled?: boolean;
@@ -237,7 +260,7 @@ export interface FieldProps {
237
260
  }
238
261
  export interface TextFieldProps extends FieldProps, Omit<React$1.InputHTMLAttributes<HTMLInputElement>, "required"> {
239
262
  /** Leading icon inside the field. */
240
- icon?: IconName;
263
+ icon?: IconInput;
241
264
  /** Trailing unit, e.g. "THB". */
242
265
  suffix?: React$1.ReactNode;
243
266
  }
@@ -252,7 +275,7 @@ export interface SelectProps extends FieldProps, Omit<React$1.SelectHTMLAttribut
252
275
  options: SelectOption[];
253
276
  /** A disabled first option shown in fg-tertiary until something is chosen. */
254
277
  placeholder?: string;
255
- icon?: IconName;
278
+ icon?: IconInput;
256
279
  }
257
280
  export type ChoiceOption = string | {
258
281
  value: string;
@@ -364,7 +387,7 @@ export interface CardProps {
364
387
  export interface TabItem {
365
388
  id: string;
366
389
  label: string;
367
- icon?: IconName;
390
+ icon?: IconInput;
368
391
  count?: number;
369
392
  disabled?: boolean;
370
393
  content?: React$1.ReactNode;
@@ -381,9 +404,15 @@ export interface TabsProps {
381
404
  export interface NavItem {
382
405
  id: string;
383
406
  label: string;
384
- icon?: IconName;
407
+ icon?: IconInput;
385
408
  count?: number;
409
+ /** Anything beside the label, e.g. a status dot or a `Badge`. */
410
+ badge?: React$1.ReactNode;
386
411
  href?: string;
412
+ /** Makes this item a collapsible group of links. The group header is a button, never a link. */
413
+ children?: NavItem[];
414
+ /** Start open. A group also opens by itself when one of its items is the current one. */
415
+ defaultOpen?: boolean;
387
416
  }
388
417
  export interface SideNavProps {
389
418
  sections?: Array<{
@@ -398,6 +427,8 @@ export interface SideNavProps {
398
427
  footer?: React$1.ReactNode;
399
428
  /** Default "Main". */
400
429
  label?: string;
430
+ /** Router link for items with `href`; defaults to the AuraProvider's `linkComponent`, then `<a>`. */
431
+ linkComponent?: React$1.ElementType;
401
432
  className?: string;
402
433
  }
403
434
  export interface BreadcrumbProps {
@@ -435,7 +466,7 @@ export interface ComboboxOption {
435
466
  keywords?: string[];
436
467
  disabled?: boolean;
437
468
  /** Leading icon in the list. */
438
- icon?: IconName;
469
+ icon?: IconInput;
439
470
  }
440
471
  /** Combobox with `multiple`: pick any number; the picks show as removable chips in the field. */
441
472
  export interface ComboboxMultipleProps extends Omit<ComboboxProps, "value" | "defaultValue" | "onChange" | "clearable"> {
@@ -461,7 +492,7 @@ export interface ComboboxProps extends FieldProps {
461
492
  disabled?: boolean;
462
493
  readOnly?: boolean;
463
494
  /** Leading icon. Default `search`. */
464
- icon?: IconName;
495
+ icon?: IconInput;
465
496
  /** Clear button while a value is selected (Escape clears too). Default true. */
466
497
  clearable?: boolean;
467
498
  /** Server-side search: called with the typed text; the component stops filtering and shows `options` as given. */
@@ -525,7 +556,7 @@ export interface StepperProps {
525
556
  export interface SegmentedOption {
526
557
  value: string;
527
558
  label: string;
528
- icon?: IconName;
559
+ icon?: IconInput;
529
560
  /** Show only the icon; `label` becomes its accessible name and tooltip. */
530
561
  iconOnly?: boolean;
531
562
  disabled?: boolean;
@@ -549,9 +580,9 @@ export interface SegmentedControlProps {
549
580
  /** ISO date string, `YYYY-MM-DD` (Gregorian — the era is display only). */
550
581
  export type ISODate = string;
551
582
  export interface DateDisplayOptions {
552
- /** `th` (default) or `en`. */
553
- locale?: "th" | "en";
554
- /** `buddhist` (default, พ.ศ.) or `gregory` (ค.ศ.). */
583
+ /** `th` (default), `en` or `sv`. */
584
+ locale?: "th" | "en" | "sv";
585
+ /** `buddhist` (พ.ศ.; default for th and en) or `gregory` (ค.ศ.; default for sv). */
555
586
  calendar?: "buddhist" | "gregory";
556
587
  }
557
588
  export interface CalendarProps extends DateDisplayOptions {
@@ -673,7 +704,7 @@ export interface StatProps {
673
704
  unit?: React$1.ReactNode;
674
705
  change?: StatChange;
675
706
  caption?: React$1.ReactNode;
676
- icon?: IconName;
707
+ icon?: IconInput;
677
708
  /** Skeleton in place of the value. */
678
709
  loading?: boolean;
679
710
  /** Makes the whole card a link or a button (drill-down). */
@@ -740,11 +771,11 @@ export interface BadgeProps extends React$1.HTMLAttributes<HTMLSpanElement> {
740
771
  tone?: Tone;
741
772
  /** soft (default) · solid · outline */
742
773
  variant?: "soft" | "solid" | "outline";
743
- icon?: IconName;
774
+ icon?: IconInput;
744
775
  }
745
776
  export interface TagProps extends Omit<React$1.HTMLAttributes<HTMLElement>, "onClick"> {
746
777
  children: React$1.ReactNode;
747
- icon?: IconName;
778
+ icon?: IconInput;
748
779
  /** Adds a remove button (applied filters, chosen people). */
749
780
  onRemove?: () => void;
750
781
  removeLabel?: string;
@@ -781,7 +812,7 @@ export interface SkeletonProps {
781
812
  export interface EmptyStateProps {
782
813
  title: React$1.ReactNode;
783
814
  description?: React$1.ReactNode;
784
- icon?: IconName;
815
+ icon?: IconInput;
785
816
  /** One or two buttons. */
786
817
  action?: React$1.ReactNode;
787
818
  size?: "sm" | "md";
@@ -807,7 +838,7 @@ export interface AccordionItem {
807
838
  title: React$1.ReactNode;
808
839
  description?: React$1.ReactNode;
809
840
  content: React$1.ReactNode;
810
- icon?: IconName;
841
+ icon?: IconInput;
811
842
  disabled?: boolean;
812
843
  }
813
844
  export interface AccordionProps {
@@ -956,6 +987,7 @@ export declare const Drawer: React$1.ForwardRefExoticComponent<DrawerProps & Rea
956
987
  export declare const DataTable: React$1.ForwardRefExoticComponent<DataTableProps & React$1.RefAttributes<HTMLDivElement>>;
957
988
  export declare const Card: React$1.ForwardRefExoticComponent<CardProps & React$1.RefAttributes<HTMLElement>>;
958
989
  export declare const Tabs: React$1.ForwardRefExoticComponent<TabsProps & React$1.RefAttributes<HTMLDivElement>>;
990
+ /** Side navigation: sections of links or buttons, collapsible groups, counts and badges. Arrow keys move between items. */
959
991
  export declare const SideNav: React$1.ForwardRefExoticComponent<SideNavProps & React$1.RefAttributes<HTMLElement>>;
960
992
  export declare const Breadcrumb: React$1.ForwardRefExoticComponent<BreadcrumbProps & React$1.RefAttributes<HTMLElement>>;
961
993
  export declare const Avatar: React$1.ForwardRefExoticComponent<AvatarProps & React$1.RefAttributes<HTMLSpanElement>>;
@@ -1058,12 +1090,15 @@ export interface AuraStrings {
1058
1090
  export declare const STRINGS: {
1059
1091
  en: AuraStrings;
1060
1092
  th: AuraStrings;
1093
+ sv: AuraStrings;
1061
1094
  };
1062
1095
  /** What useAuraLocale returns. `locale` and `calendar` are null outside an AuraProvider. */
1063
1096
  export interface AuraLocaleValue {
1064
- locale: "th" | "en" | null;
1097
+ locale: "th" | "en" | "sv" | null;
1065
1098
  calendar: "buddhist" | "gregory" | null;
1066
1099
  strings: AuraStrings;
1100
+ /** The router link set on AuraProvider, if any. */
1101
+ linkComponent?: React$1.ElementType | null;
1067
1102
  }
1068
1103
  /** Sets the language of built-in labels (and the default date locale) for everything inside. */
1069
1104
  export declare function AuraProvider(props: AuraProviderProps): React$1.ReactElement;
@@ -1101,7 +1136,10 @@ export interface ColorSchemeOptions {
1101
1136
  /** The script ColorSchemeScript renders, as a string — for frameworks that want it in a raw <head> template. */
1102
1137
  export declare function colorSchemeScript(options?: ColorSchemeOptions): string;
1103
1138
  /** Put in <head>: applies the saved colour scheme before the page paints. Server-rendering safe. */
1104
- export declare function ColorSchemeScript(props: ColorSchemeOptions): React$1.ReactElement;
1139
+ export declare function ColorSchemeScript(props: ColorSchemeOptions & {
1140
+ /** Nonce for a nonce-based Content-Security-Policy (script-src 'nonce-…'). */
1141
+ nonce?: string;
1142
+ }): React$1.ReactElement;
1105
1143
  /** Current colour scheme and a setter. `resolved` is what is on screen (system resolved to light or dark). */
1106
1144
  export interface ColorSchemeState {
1107
1145
  scheme: ColorScheme;
package/dist/styles.css CHANGED
@@ -43,6 +43,11 @@ a.aura-btn { text-decoration: none; }
43
43
 
44
44
  .aura-btn--secondary { border: 1px solid var(--aura-button-secondary-border); color: var(--aura-fg-primary); }
45
45
  .aura-btn--secondary:hover { background: var(--aura-bg-surface-hover); }
46
+ /* Destructive: filled for the confirm step, outline beside other actions. Loading keeps the colour (busy, not unavailable). */
47
+ .aura-btn--danger { background: var(--aura-button-danger-bg); color: var(--aura-button-danger-fg); }
48
+ .aura-btn--danger:hover { background: var(--aura-button-danger-bg-hover); }
49
+ .aura-btn--danger-secondary { border: 1px solid var(--aura-border-danger); color: var(--aura-fg-danger); background: transparent; }
50
+ .aura-btn--danger-secondary:hover { background: var(--aura-alert-danger-bg); }
46
51
 
47
52
  .aura-btn--creative {
48
53
  border: 2px solid var(--aura-button-creative-border);
@@ -82,6 +87,28 @@ a.aura-btn { text-decoration: none; }
82
87
  .aura-table__row--skeleton:hover { background: var(--aura-bg-surface); }
83
88
  .aura-table__row--skeleton .aura-table__td { display: flex; align-items: center; }
84
89
  .aura-table__row.is-selected { background: var(--aura-bg-selected); }
90
+ /* Right-aligned (numeric) columns */
91
+ .aura-table__th.is-end { padding-right: var(--aura-space-4); }
92
+ .aura-table__th.is-end .aura-table__th-inner { justify-content: flex-end; }
93
+ .aura-table__th.is-end .aura-table__sort { flex-direction: row-reverse; }
94
+ .aura-table__td.is-end { text-align: end; font-variant-numeric: tabular-nums; }
95
+ .aura-table__row--skeleton .aura-table__td.is-end { justify-content: flex-end; }
96
+ /* Row links (getRowHref): the first cell's text is the link; the whole row is the click target */
97
+ .aura-table__row-link { color: inherit; text-decoration: none; }
98
+ .aura-table__row-link:hover, .aura-table__row:hover .aura-table__row-link, .aura-table__card:hover .aura-table__row-link { text-decoration: underline; text-underline-offset: 2px; }
99
+ .aura-table__row-link:focus-visible { outline: var(--aura-focus-ring-width) solid var(--aura-focus-ring); outline-offset: 2px; border-radius: var(--aura-radius-xs); }
100
+ /* Server mode refresh: the previous page stays, inactive, under a progress bar until the next one arrives */
101
+ .aura-table.is-refreshing .aura-table__row, .aura-table.is-refreshing .aura-table__card { opacity: 0.6; pointer-events: none; }
102
+ .aura-table__busy-bar {
103
+ position: absolute; z-index: 6; top: calc(var(--aura-space-12) - 2px); left: 0; right: 0; height: 2px; overflow: hidden;
104
+ border-radius: var(--aura-radius-full); pointer-events: none;
105
+ }
106
+ .aura-table--stacked .aura-table__busy-bar { top: 0; }
107
+ .aura-table__busy-bar::after {
108
+ content: ''; position: absolute; top: 0; bottom: 0; width: 35%; background: var(--aura-fg-accent);
109
+ animation: aura-progress-slide var(--aura-duration-pulse) var(--aura-ease) infinite;
110
+ }
111
+ @media (prefers-reduced-motion: reduce) { .aura-table__busy-bar::after { animation-duration: calc(var(--aura-duration-pulse) * 3); } }
85
112
 
86
113
  /* Cells: text cells stay block so long values end in an ellipsis */
87
114
  .aura-table__td {
@@ -212,11 +239,13 @@ a.aura-btn { text-decoration: none; }
212
239
  cursor: pointer; transition: background var(--aura-duration-fast) var(--aura-ease);
213
240
  }
214
241
  .aura-icon-btn:hover { background: var(--aura-bg-surface-hover); }
242
+ .aura-icon-btn--danger { color: var(--aura-fg-danger); }
243
+ .aura-icon-btn--danger:hover { background: var(--aura-alert-danger-bg); }
215
244
  .aura-icon-btn:disabled { opacity: var(--aura-disabled-opacity); pointer-events: none; }
216
245
  .aura-icon-btn:focus-visible { outline: var(--aura-focus-ring-width) solid var(--aura-focus-ring); outline-offset: var(--aura-focus-ring-offset); }
217
246
 
218
247
  /* Skeleton */
219
- @keyframes aura-pulse { 50% { opacity: 0.45; } }
248
+ @keyframes aura-pulse { 50% { opacity: 0.6; } }
220
249
  .aura-skel {
221
250
  display: block; height: 10px; width: 60%; border-radius: var(--aura-radius-full);
222
251
  background: var(--aura-bg-skeleton); animation: aura-pulse var(--aura-duration-pulse) var(--aura-ease) infinite;
@@ -275,6 +304,9 @@ a.aura-btn { text-decoration: none; }
275
304
 
276
305
  /* Icon — Lucide, 24-grid, stroke in currentColor */
277
306
  .aura-icon { flex: none; display: inline-block; vertical-align: middle; }
307
+ /* A consumer's own icon element (e.g. lucide-react) fills the same box, strokes in currentColor at width 2. */
308
+ .aura-icon--custom { display: inline-flex; align-items: center; justify-content: center; color: inherit; line-height: 0; }
309
+ .aura-icon--custom > svg { width: 100%; height: 100%; stroke-width: var(--aura-icon-stroke, 2); }
278
310
 
279
311
  /* Loading spinner */
280
312
  @keyframes aura-spin { to { transform: rotate(360deg); } }
@@ -543,6 +575,14 @@ a.aura-btn { text-decoration: none; }
543
575
  .aura-nav__item:focus-visible { outline: var(--aura-focus-ring-width) solid var(--aura-focus-ring); outline-offset: -2px; }
544
576
  .aura-nav__label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
545
577
  .aura-nav__count { font-size: 11px; font-variant-numeric: tabular-nums; color: var(--aura-fg-secondary); }
578
+ .aura-nav__badge { display: inline-flex; align-items: center; flex: none; }
579
+ /* Groups: a disclosure button, its items indented one step per level. */
580
+ .aura-nav__item { padding-left: calc(var(--aura-space-3) + var(--aura-nav-depth, 0) * (var(--aura-icon-sm) + var(--aura-space-3))); }
581
+ .aura-nav__item--group.has-active { color: var(--aura-fg-primary); }
582
+ .aura-nav__chevron { color: var(--aura-fg-secondary); transition: transform var(--aura-duration-fast) var(--aura-ease); }
583
+ .aura-nav__chevron.is-open { transform: rotate(180deg); }
584
+ .aura-nav__sub { margin-top: 2px; }
585
+ .aura-nav__sub[hidden] { display: none; }
546
586
  .aura-nav__footer { padding: var(--aura-space-3); border-top: 1px solid var(--aura-border-default); }
547
587
 
548
588
  /* ---------- Breadcrumb ---------- */
@@ -775,7 +815,8 @@ button.aura-stat { font: inherit; width: 100%; cursor: pointer; }
775
815
  .aura-stat__head { display: flex; align-items: center; justify-content: space-between; gap: var(--aura-space-2); }
776
816
  .aura-stat__label { font-size: 13px; font-weight: 500; color: var(--aura-fg-secondary); }
777
817
  .aura-stat__icon { display: inline-flex; color: var(--aura-fg-secondary); }
778
- .aura-stat__value { display: flex; align-items: baseline; gap: var(--aura-space-1); font-size: 28px; line-height: 1.2; font-weight: 600; letter-spacing: -0.01em; font-variant-numeric: tabular-nums; min-height: 34px; }
818
+ .aura-stat__value { display: flex; align-items: baseline; gap: var(--aura-space-1); font-size: 28px; line-height: 1.2; font-weight: 600; letter-spacing: -0.01em; min-height: 34px; }
819
+ .aura-stat__value.is-numeric { font-variant-numeric: tabular-nums; }
779
820
  .aura-stat__unit { font-size: 14px; font-weight: 500; color: var(--aura-fg-secondary); letter-spacing: 0; }
780
821
  .aura-stat__skel { width: 55%; height: 24px; align-self: center; }
781
822
  .aura-stat__foot { display: flex; flex-wrap: wrap; align-items: center; gap: var(--aura-space-1) var(--aura-space-2); font-size: 12px; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jirawatpyk/aura-react",
3
- "version": "4.9.0",
3
+ "version": "4.10.0",
4
4
  "description": "AURA Design System — React components (Enterprise Standard, Human Creative). Thai-first: Buddhist-era dates, Thai collation.",
5
5
  "license": "MIT",
6
6
  "type": "module",