@jirawatpyk/aura-react 5.0.0 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/esm/dates.js CHANGED
@@ -144,12 +144,16 @@ function parseDate(text) {
144
144
  function todayIn(timeZone) {
145
145
  const now = /* @__PURE__ */ new Date();
146
146
  if (!timeZone) return toISO(now);
147
- return new Intl.DateTimeFormat("en-CA", {
148
- timeZone,
149
- year: "numeric",
150
- month: "2-digit",
151
- day: "2-digit"
152
- }).format(now);
147
+ try {
148
+ return new Intl.DateTimeFormat("en-CA", {
149
+ timeZone,
150
+ year: "numeric",
151
+ month: "2-digit",
152
+ day: "2-digit"
153
+ }).format(now);
154
+ } catch (e) {
155
+ return toISO(now);
156
+ }
153
157
  }
154
158
  export {
155
159
  DATE_TEXT,
@@ -83,11 +83,25 @@ const TONES = ["neutral", "accent", "success", "warning", "danger"];
83
83
  function tone(t) {
84
84
  return TONES.indexOf(t) >= 0 ? t : "neutral";
85
85
  }
86
+ const warned = {};
87
+ function devWarnOnce(key, message) {
88
+ if (warned[key]) return;
89
+ let dev = false;
90
+ try {
91
+ dev = true;
92
+ } catch (e) {
93
+ dev = false;
94
+ }
95
+ if (!dev) return;
96
+ warned[key] = true;
97
+ console.warn("[AURA] " + message);
98
+ }
86
99
  export {
87
100
  FOCUSABLE,
88
101
  collator,
89
102
  compare,
90
103
  cx,
104
+ devWarnOnce,
91
105
  omit,
92
106
  tone,
93
107
  trapTab,
@@ -2,6 +2,7 @@
2
2
  const TONE_WORDS = {
3
3
  ready: ["ready", "done", "complete", "completed", "approved", "live", "passed"],
4
4
  progress: ["in progress", "in review", "review", "syncing", "running", "pending"],
5
+ warning: ["warning", "problem", "degraded", "at risk"],
5
6
  blocked: ["blocked", "failed", "error", "rejected", "on hold", "cancelled"]
6
7
  };
7
8
  function toneFor(status) {
@@ -9,7 +10,7 @@ function toneFor(status) {
9
10
  for (const t in TONE_WORDS) if (TONE_WORDS[t].indexOf(s) >= 0) return t;
10
11
  return "neutral";
11
12
  }
12
- const TONE_ORDER = { neutral: 0, progress: 1, ready: 2, blocked: 3 };
13
+ const TONE_ORDER = { neutral: 0, progress: 1, ready: 2, warning: 3, blocked: 4 };
13
14
  export {
14
15
  TONE_ORDER,
15
16
  toneFor as statusTone,
@@ -12,7 +12,17 @@ function useModal(open, ref, opts) {
12
12
  const body = document.body, overflow = body.style.overflow;
13
13
  body.style.overflow = "hidden";
14
14
  const el = ref.current;
15
- const target = el && (el.querySelector("[data-autofocus]") || el.querySelector(o.bodySelector + " " + FOCUSABLE) || el.querySelector(o.footSelector + " " + FOCUSABLE) || el);
15
+ const firstIn = function(scope) {
16
+ if (!scope || !el) return null;
17
+ const sel = FOCUSABLE.split(",").map(function(s) {
18
+ return scope + " " + s;
19
+ }).join(",");
20
+ const list = el.querySelectorAll(sel);
21
+ for (let i = 0; i < list.length; i++)
22
+ if (list[i].tabIndex >= 0 && list[i].getClientRects().length > 0) return list[i];
23
+ return null;
24
+ };
25
+ const target = el && (el.querySelector("[data-autofocus]") || firstIn(o.bodySelector) || firstIn(o.footSelector) || el);
16
26
  if (target && o.autoFocus !== false) target.focus();
17
27
  return function() {
18
28
  body.style.overflow = overflow;
package/dist/index.d.ts CHANGED
@@ -171,13 +171,13 @@ export interface DataTableProps {
171
171
  onPinnedColumnsChange?: ((keys: string[]) => void) | undefined;
172
172
  /** `compact` = 40px rows (48px on touch screens). Default: the surrounding density. */
173
173
  density?: "comfortable" | "compact" | undefined;
174
- /** Table width in px below which rows render as stacked cards (phones). Try 640. It follows the table's own width, not the window. Since 4.20 the cards are the same markup as the grid, laid out by a container query, so any width is right before hydration and each row is in the HTML once. */
174
+ /** Table width in px below which rows render as stacked cards (phones). Try 640. It follows the table's own width, not the window. Since 4.20 the cards are the same markup as the grid, laid out by a container query, so any width is right before hydration and each row is in the HTML once. With `height` (virtual rows) the server can only send the first screenful; the rest of the cards arrive on hydration. */
175
175
  stackBelow?: number | undefined;
176
176
  className?: string | undefined;
177
177
  }
178
178
  /** An AURA icon name, or any icon element (e.g. `<Building />` from lucide-react). AURA sizes it and hides it from screen readers. */
179
179
  export type IconInput = IconName | React$1.ReactElement;
180
- 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" | "panel-left-close" | "panel-left-open";
180
+ 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" | "panel-left-close" | "panel-left-open" | "server" | "globe" | "activity" | "shield-alert" | "phone" | "wrench";
181
181
  /** Lucide stroke icon drawn inline in currentColor. */
182
182
  export interface IconProps {
183
183
  /** A name from the set, or an icon element of your own (sized and styled the same way). */
@@ -214,12 +214,13 @@ export interface AuraProviderProps {
214
214
  linkComponent?: React$1.ElementType | undefined;
215
215
  children?: React$1.ReactNode | undefined;
216
216
  }
217
- export type StatusTone = "neutral" | "progress" | "ready" | "blocked";
217
+ /** `warning` (5.1): needs attention but still works — Warning, Problem, Degraded, At risk. */
218
+ export type StatusTone = "neutral" | "progress" | "ready" | "warning" | "blocked";
218
219
  /** Status pill: tone fill + icon + the status word. Tone comes from the word unless given. */
219
220
  export interface StatusPillProps {
220
221
  /** The status word shown, e.g. "In Progress". */
221
222
  children: React$1.ReactNode;
222
- /** Force a tone. Default: matched from the word (Ready/Done → ready, In Progress/In Review → progress, Blocked/Failed → blocked, anything else → neutral). */
223
+ /** Force a tone. Default: matched from the word (Ready/Done → ready, In Progress/In Review → progress, Warning/Problem/Degraded/At risk → warning (5.1), Blocked/Failed → blocked, anything else → neutral). */
223
224
  tone?: StatusTone | undefined;
224
225
  className?: string | undefined;
225
226
  }
@@ -229,9 +230,12 @@ export interface CheckboxProps extends Omit<React$1.InputHTMLAttributes<HTMLInpu
229
230
  checked?: boolean | undefined;
230
231
  defaultChecked?: boolean | undefined;
231
232
  indeterminate?: boolean | undefined;
232
- /** Accessible name for a bare box (tables). Not needed when children give a visible label. */
233
+ /** Accessible name. In 5.x it is shown only when given as children; **6.0 shows `label` beside the box** like
234
+ * Switch and TextField. Without children and without `hideLabel` it warns once in development. */
233
235
  label?: string | undefined;
234
- /** Visible label beside the box (form use). */
236
+ /** A bare box named by `label`, e.g. in a table row: keeps it hidden in 6.0 and silences the 5.1 notice. */
237
+ hideLabel?: boolean | undefined;
238
+ /** The visible label beside the box. */
235
239
  children?: React$1.ReactNode | undefined;
236
240
  /** Second line under the visible label (needs an id to be linked). */
237
241
  description?: React$1.ReactNode | undefined;
@@ -590,6 +594,9 @@ export interface SideNavProps {
590
594
  onCollapsedChange?: ((collapsed: boolean) => void) | undefined;
591
595
  /** Adds a collapse / expand button at the bottom. AppShell hides it in its phone drawer. */
592
596
  collapsible?: boolean | undefined;
597
+ /** The 1px right edge that divides the nav from the page. Default true; `false` when your layout draws its own
598
+ * divider. (5.1) */
599
+ bordered?: boolean | undefined;
593
600
  className?: string | undefined;
594
601
  }
595
602
  export interface BreadcrumbProps {
@@ -116,12 +116,16 @@ function parseDate(text) {
116
116
  function todayIn(timeZone) {
117
117
  const now = /* @__PURE__ */ new Date();
118
118
  if (!timeZone) return toISO(now);
119
- return new Intl.DateTimeFormat("en-CA", {
120
- timeZone,
121
- year: "numeric",
122
- month: "2-digit",
123
- day: "2-digit"
124
- }).format(now);
119
+ try {
120
+ return new Intl.DateTimeFormat("en-CA", {
121
+ timeZone,
122
+ year: "numeric",
123
+ month: "2-digit",
124
+ day: "2-digit"
125
+ }).format(now);
126
+ } catch (e) {
127
+ return toISO(now);
128
+ }
125
129
  }
126
130
 
127
131
  // src/text.ts
@@ -155,6 +159,7 @@ function parseTime(text) {
155
159
  var TONE_WORDS = {
156
160
  ready: ["ready", "done", "complete", "completed", "approved", "live", "passed"],
157
161
  progress: ["in progress", "in review", "review", "syncing", "running", "pending"],
162
+ warning: ["warning", "problem", "degraded", "at risk"],
158
163
  blocked: ["blocked", "failed", "error", "rejected", "on hold", "cancelled"]
159
164
  };
160
165
  function toneFor(status) {
@@ -1,6 +1,7 @@
1
1
  import * as React$1 from 'react';
2
2
 
3
- export type StatusTone = "neutral" | "progress" | "ready" | "blocked";
3
+ /** `warning` (5.1): needs attention but still works — Warning, Problem, Degraded, At risk. */
4
+ export type StatusTone = "neutral" | "progress" | "ready" | "warning" | "blocked";
4
5
  /** ISO date string, `YYYY-MM-DD` (Gregorian — the era is display only). */
5
6
  export type ISODate = string;
6
7
  export interface DateDisplayOptions {
@@ -77,12 +77,16 @@ function parseDate(text) {
77
77
  function todayIn(timeZone) {
78
78
  const now = /* @__PURE__ */ new Date();
79
79
  if (!timeZone) return toISO(now);
80
- return new Intl.DateTimeFormat("en-CA", {
81
- timeZone,
82
- year: "numeric",
83
- month: "2-digit",
84
- day: "2-digit"
85
- }).format(now);
80
+ try {
81
+ return new Intl.DateTimeFormat("en-CA", {
82
+ timeZone,
83
+ year: "numeric",
84
+ month: "2-digit",
85
+ day: "2-digit"
86
+ }).format(now);
87
+ } catch (e) {
88
+ return toISO(now);
89
+ }
86
90
  }
87
91
 
88
92
  // src/text.ts
@@ -116,6 +120,7 @@ function parseTime(text) {
116
120
  var TONE_WORDS = {
117
121
  ready: ["ready", "done", "complete", "completed", "approved", "live", "passed"],
118
122
  progress: ["in progress", "in review", "review", "syncing", "running", "pending"],
123
+ warning: ["warning", "problem", "degraded", "at risk"],
119
124
  blocked: ["blocked", "failed", "error", "rejected", "on hold", "cancelled"]
120
125
  };
121
126
  function toneFor(status) {
package/dist/styles.css CHANGED
@@ -334,6 +334,7 @@ a.aura-icon-btn { text-decoration: none; }
334
334
  }
335
335
  .aura-pill--progress { background: var(--aura-status-progress-bg); color: var(--aura-status-progress-fg); }
336
336
  .aura-pill--ready { background: var(--aura-status-ready-bg); color: var(--aura-status-ready-fg); }
337
+ .aura-pill--warning { background: var(--aura-status-warning-bg); color: var(--aura-status-warning-fg); }
337
338
  .aura-pill--blocked { background: var(--aura-status-blocked-bg); color: var(--aura-status-blocked-fg); }
338
339
 
339
340
  /* Icon — Lucide, 24-grid, stroke in currentColor */
@@ -596,6 +597,9 @@ a.aura-tab { text-decoration: none; }
596
597
  }
597
598
  .aura-nav__header { padding: var(--aura-space-5) var(--aura-space-4) var(--aura-space-4); }
598
599
  .aura-nav__scroll { flex: 1; overflow-y: auto; padding: 0 var(--aura-space-3); }
600
+ /* No header: keep the first item off the top edge (5.0.2). */
601
+ .aura-nav > .aura-nav__scroll:first-child { padding-top: var(--aura-space-3); }
602
+ .aura-nav--borderless { border-right: 0; }
599
603
  .aura-nav__section + .aura-nav__section { margin-top: var(--aura-space-5); }
600
604
  .aura-nav__title { margin: 0 0 var(--aura-space-1); padding: 0 var(--aura-space-3); font-family: var(--font-mono); font-size: 11px; line-height: 20px; color: var(--aura-fg-tertiary); text-transform: uppercase; letter-spacing: 0.04em; }
601
605
  .aura-nav__list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
@@ -1365,6 +1369,8 @@ button.aura-stepper__step:hover .aura-stepper__label { text-decoration: underlin
1365
1369
  @media (max-width: 767.98px) { :root:has(.aura-bottomnav--below-md) { --aura-bottomnav-offset: calc(var(--aura-bottomnav-height) + env(safe-area-inset-bottom, 0px)); } }
1366
1370
  @media (max-width: 1023.98px) { :root:has(.aura-bottomnav--below-lg) { --aura-bottomnav-offset: calc(var(--aura-bottomnav-height) + env(safe-area-inset-bottom, 0px)); } }
1367
1371
  @media (max-width: 1279.98px) { :root:has(.aura-bottomnav--below-xl) { --aura-bottomnav-offset: calc(var(--aura-bottomnav-height) + env(safe-area-inset-bottom, 0px)); } }
1372
+ /* Toasts clear a BottomNav wherever it shows (phones and tablets). */
1373
+ .aura-toaster:not(.is-top) { bottom: calc(var(--aura-space-6) + var(--aura-bottomnav-offset, 0px)); }
1368
1374
  @media (max-width: 639.98px) { .aura-toaster:not(.is-top) { bottom: calc(var(--aura-space-4) + var(--aura-bottomnav-offset, 0px)); } }
1369
1375
 
1370
1376
  /* ---------- 4.19: ActionBar ---------- */
@@ -1459,7 +1465,8 @@ button.aura-stepper__step:hover .aura-stepper__label { text-decoration: underlin
1459
1465
  margin-bottom: var(--aura-space-1);
1460
1466
  padding: 0; line-height: normal;
1461
1467
  }
1462
- .aura-table__td[data-hide] { display: block; }
1468
+ .aura-table__td[data-hide], .aura-table__head > .aura-table__th[data-hide] { display: block; }
1469
+ .aura-table__spacer { display: none; }
1463
1470
  .aura-table__td[data-label]::before {
1464
1471
  content: attr(data-label); content: attr(data-label) / '';
1465
1472
  display: block; margin-bottom: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-align: start;
@@ -336,6 +336,7 @@ a.aura-icon-btn { text-decoration: none; }
336
336
  }
337
337
  .aura-pill--progress { background: var(--aura-status-progress-bg); color: var(--aura-status-progress-fg); }
338
338
  .aura-pill--ready { background: var(--aura-status-ready-bg); color: var(--aura-status-ready-fg); }
339
+ .aura-pill--warning { background: var(--aura-status-warning-bg); color: var(--aura-status-warning-fg); }
339
340
  .aura-pill--blocked { background: var(--aura-status-blocked-bg); color: var(--aura-status-blocked-fg); }
340
341
 
341
342
  /* Icon — Lucide, 24-grid, stroke in currentColor */
@@ -598,6 +599,9 @@ a.aura-tab { text-decoration: none; }
598
599
  }
599
600
  .aura-nav__header { padding: var(--aura-space-5) var(--aura-space-4) var(--aura-space-4); }
600
601
  .aura-nav__scroll { flex: 1; overflow-y: auto; padding: 0 var(--aura-space-3); }
602
+ /* No header: keep the first item off the top edge (5.0.2). */
603
+ .aura-nav > .aura-nav__scroll:first-child { padding-top: var(--aura-space-3); }
604
+ .aura-nav--borderless { border-right: 0; }
601
605
  .aura-nav__section + .aura-nav__section { margin-top: var(--aura-space-5); }
602
606
  .aura-nav__title { margin: 0 0 var(--aura-space-1); padding: 0 var(--aura-space-3); font-family: var(--font-mono); font-size: 11px; line-height: 20px; color: var(--aura-fg-tertiary); text-transform: uppercase; letter-spacing: 0.04em; }
603
607
  .aura-nav__list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
@@ -1367,6 +1371,8 @@ button.aura-stepper__step:hover .aura-stepper__label { text-decoration: underlin
1367
1371
  @media (max-width: 767.98px) { :root:has(.aura-bottomnav--below-md) { --aura-bottomnav-offset: calc(var(--aura-bottomnav-height) + env(safe-area-inset-bottom, 0px)); } }
1368
1372
  @media (max-width: 1023.98px) { :root:has(.aura-bottomnav--below-lg) { --aura-bottomnav-offset: calc(var(--aura-bottomnav-height) + env(safe-area-inset-bottom, 0px)); } }
1369
1373
  @media (max-width: 1279.98px) { :root:has(.aura-bottomnav--below-xl) { --aura-bottomnav-offset: calc(var(--aura-bottomnav-height) + env(safe-area-inset-bottom, 0px)); } }
1374
+ /* Toasts clear a BottomNav wherever it shows (phones and tablets). */
1375
+ .aura-toaster:not(.is-top) { bottom: calc(var(--aura-space-6) + var(--aura-bottomnav-offset, 0px)); }
1370
1376
  @media (max-width: 639.98px) { .aura-toaster:not(.is-top) { bottom: calc(var(--aura-space-4) + var(--aura-bottomnav-offset, 0px)); } }
1371
1377
 
1372
1378
  /* ---------- 4.19: ActionBar ---------- */
@@ -1461,7 +1467,8 @@ button.aura-stepper__step:hover .aura-stepper__label { text-decoration: underlin
1461
1467
  margin-bottom: var(--aura-space-1);
1462
1468
  padding: 0; line-height: normal;
1463
1469
  }
1464
- .aura-table__td[data-hide] { display: block; }
1470
+ .aura-table__td[data-hide], .aura-table__head > .aura-table__th[data-hide] { display: block; }
1471
+ .aura-table__spacer { display: none; }
1465
1472
  .aura-table__td[data-label]::before {
1466
1473
  content: attr(data-label); content: attr(data-label) / '';
1467
1474
  display: block; margin-bottom: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-align: start;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jirawatpyk/aura-react",
3
- "version": "5.0.0",
3
+ "version": "5.1.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",