@jirawatpyk/aura-react 5.17.0 → 5.19.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/README.md CHANGED
@@ -186,6 +186,18 @@ DataTable with sort and page in the URL (4.17): `onStateChange={({ sort, page })
186
186
  - **Time zone**: `timeZone` on AuraProvider, DatePicker, DateRangePicker and Calendar decides "today" (the marker, `min`/`max="today"`, the first month shown); or pass `today` as an ISO date. `todayIn('Asia/Bangkok')` is exported from the root and `/server`.
187
187
  - **Toasts** queue past three instead of dropping: six in a row all show, in order, three at a time.
188
188
 
189
+ ## 5.19 — Chamber-OS addendum 20
190
+
191
+ - **Switch `readOnly`, `icon` and `aria-describedby`** (item 113): `readOnly` keeps a locked switch in the Tab order with `aria-readonly="true"`, so its name, state and notes are heard; click, Space, its label and its row change nothing. It keeps its colours (a locked "on" reads as on) and loses the pointer. `icon` puts an icon at the end of the row, the field icon's size, in fg-secondary (`icon={<IconLock />}`), hidden from screen readers. `aria-describedby` is merged with the description's id, as on TextField and Select. `disabled` wins over `readOnly`.
192
+ - **Select `readOnly`** (item 114): the field stays in the Tab order with `aria-readonly="true"` and shows the chosen option on the read-only ground of a read-only TextField, with no chevron; no click or key opens the list. Unlike `disabled`, the value is still posted with the form, and your code can still set it (`reset()`, `setValue()`). Before JavaScript runs, the options from `options` other than the value are disabled so the native control keeps it (`<option>` children aren't; with no `value` or `defaultValue` nothing is); once hydrated they are enabled again, since a disabled selected option is left out of the form's data. A `multiple` / `size` list box ignores the mouse and the keys that pick (arrows, Home/End, Page keys, letters, Space, Ctrl+A); a change that gets through anyway (a touch tap, a phone's picker, `selectOption` in a test) is put back, and your `onChange` isn't called — though a native `change` or `input` listener on the form still sees the event, with the value already restored.
193
+ - Read-only is `aria-readonly` alone; pair it with a note that says why (`aria-describedby`), which screen readers read after the state.
194
+
195
+ ## 5.18 — Chamber-OS addendum 19
196
+
197
+ - **Stepper step errors** (item 112): a step can take `status: 'error'` — say, Save found errors on it. It keeps its place in the order and its state (a completed step is still a button with `onStepClick`, the current one keeps `aria-current="step"`), shows the danger tone with an alert icon instead of the check or number (outlined when it is the current step), and its accessible name ends ", has errors" instead of ", completed". On a phone the line reads "Step 2 of 4 — has errors" when the current step has errors; the markers, error ones included, stay visible. Steps without `status` look the same as before.
198
+ - **Stepper step names without the stray space**: a clickable step with a text label now has `aria-label` "Basics, completed" / "Fees, has errors" — from its content Chrome read "Basics , completed" (it puts a space before an absolutely positioned visually hidden span). Its `description` moves from the name to `aria-describedby`. The markup inside is unchanged; a label that is an element keeps the name from its content.
199
+ - New string `stepError` in every locale ("has errors", "มีข้อผิดพลาด", "har fel"); a pack you wrote yourself should add it (the built-in one for the locale is used otherwise, and the 6.0 notice for Thai and Swedish shows until it does).
200
+
189
201
  ## 5.17 — Chamber-OS addendum 18
190
202
 
191
203
  - **Stat attributes, status line and a link on the label** (item 110): `id`, `data-*`, `aria-*`, `style`, `lang` and `dir` go on the `.aura-stat` root, from the root package and `/server` — a `data-testid`, or `aria-hidden` on a loading placeholder. `status` is a line under the value ("Active · renews 1 Jan" with its tone icon), hidden while loading. With `href`, `linkArea="label"` puts the link on the label (inside the heading when there is one) and stretches its hit area over the tile: a click anywhere follows it, the tile shows the focus ring and hover edge, and other controls in the tile (links, buttons, fields, anything focusable) stay clickable above it. `aria-label`, `aria-labelledby`, `aria-describedby` and `aria-current` then go on the link, and an `onClick` too. Text in such a tile can't be selected by dragging (the click belongs to the link). The default (`tile`) is unchanged. Only the documented attributes reach the root; anything else passed is dropped, as before.
@@ -928,6 +928,7 @@ window.Aura = (() => {
928
928
  return "\u0E43\u0E0A\u0E49\u0E41\u0E25\u0E49\u0E27 " + n(v) + " \u0E08\u0E32\u0E01 " + n(max) + " \u0E08\u0E2D\u0E07\u0E44\u0E27\u0E49 " + n(r);
929
929
  },
930
930
  breadcrumbMore: "\u0E41\u0E2A\u0E14\u0E07\u0E40\u0E2A\u0E49\u0E19\u0E17\u0E32\u0E07\u0E17\u0E31\u0E49\u0E07\u0E2B\u0E21\u0E14",
931
+ stepError: "\u0E21\u0E35\u0E02\u0E49\u0E2D\u0E1C\u0E34\u0E14\u0E1E\u0E25\u0E32\u0E14",
931
932
  accepts: function(list, max) {
932
933
  return [list, max && "\u0E44\u0E21\u0E48\u0E40\u0E01\u0E34\u0E19 " + max + " \u0E15\u0E48\u0E2D\u0E44\u0E1F\u0E25\u0E4C"].filter(Boolean).join(" \xB7 ");
933
934
  }
@@ -1058,6 +1059,7 @@ window.Aura = (() => {
1058
1059
  return nsv(v) + " av " + nsv(max) + " anv\xE4nda, " + nsv(r) + " reserverade";
1059
1060
  },
1060
1061
  breadcrumbMore: "Visa hela s\xF6kv\xE4gen",
1062
+ stepError: "har fel",
1061
1063
  accepts: function(list, max) {
1062
1064
  return [list, max && "h\xF6gst " + max + " per fil"].filter(Boolean).join(", ");
1063
1065
  }
@@ -1189,6 +1191,7 @@ window.Aura = (() => {
1189
1191
  return n2(v) + " of " + n2(max) + " used, " + n2(r) + " reserved";
1190
1192
  },
1191
1193
  breadcrumbMore: "Show the full path",
1194
+ stepError: "has errors",
1192
1195
  accepts: function(list, max) {
1193
1196
  return [list, max && "up to " + max + " each"].filter(Boolean).join(", ");
1194
1197
  }
@@ -1866,10 +1869,22 @@ window.Aura = (() => {
1866
1869
  var Select = React15.forwardRef(function Select2(props, ref) {
1867
1870
  const auto = uid(), id = props.id || auto;
1868
1871
  const filter = props[FILTER_KEY];
1869
- const rest = omit(props, FIELD_KEYS.concat(["children", FILTER_KEY]));
1872
+ const rest = omit(props, FIELD_KEYS.concat(["children", FILTER_KEY, "readOnly"]));
1873
+ const ro = !!props.readOnly && !props.disabled;
1874
+ const mounted = useMounted();
1875
+ const kept = React15.useRef(null);
1876
+ const current2 = [].concat(props.value != null ? props.value : props.defaultValue != null ? props.defaultValue : []).map(String);
1870
1877
  const opts = (props.options || []).map(function(o) {
1871
1878
  const v = typeof o === "object" ? o : { value: o, label: o };
1872
- return /* @__PURE__ */ React15.createElement("option", { key: v.value, value: v.value, disabled: v.disabled }, v.label);
1879
+ return /* @__PURE__ */ React15.createElement(
1880
+ "option",
1881
+ {
1882
+ key: v.value,
1883
+ value: v.value,
1884
+ disabled: v.disabled || ro && !mounted && current2.length > 0 && current2.indexOf(v.value) < 0 || void 0
1885
+ },
1886
+ v.label
1887
+ );
1873
1888
  });
1874
1889
  if (props.placeholder)
1875
1890
  opts.unshift(
@@ -1877,7 +1892,6 @@ window.Aura = (() => {
1877
1892
  );
1878
1893
  const extra = props.value === void 0 && props.defaultValue === void 0 && props.placeholder ? { defaultValue: "" } : {};
1879
1894
  const native = !!props.multiple || props.size != null && props.size > 1;
1880
- const mounted = useMounted();
1881
1895
  const live = !native && mounted;
1882
1896
  const density = useDensity();
1883
1897
  React15.useEffect(function() {
@@ -1999,7 +2013,7 @@ window.Aura = (() => {
1999
2013
  }
2000
2014
  function choose(it) {
2001
2015
  const el = selRef.current;
2002
- if (!el || it.disabled) return;
2016
+ if (!el || it.disabled || ro) return;
2003
2017
  if (el.value !== it.value) {
2004
2018
  el.value = it.value;
2005
2019
  el.dispatchEvent(new Event("input", { bubbles: true }));
@@ -2009,7 +2023,7 @@ window.Aura = (() => {
2009
2023
  close(true);
2010
2024
  }
2011
2025
  function openList(at) {
2012
- if (props.disabled || trigRef.current && trigRef.current.disabled) return;
2026
+ if (props.disabled || ro || trigRef.current && trigRef.current.disabled) return;
2013
2027
  const list = readItems(selRef.current);
2014
2028
  const ok = choices(list);
2015
2029
  if (!ok.length) return;
@@ -2140,6 +2154,45 @@ window.Aura = (() => {
2140
2154
  [open, active, pos[0] != null]
2141
2155
  );
2142
2156
  const described = [describedBy(id, props), rest["aria-describedby"]].filter(Boolean).join(" ") || void 0;
2157
+ function keep(e) {
2158
+ kept.current = Array.prototype.filter.call(e.currentTarget.options, function(o) {
2159
+ return o.selected;
2160
+ }).map(function(o) {
2161
+ return o.value;
2162
+ });
2163
+ }
2164
+ const listBoxLock = ro && native ? {
2165
+ onFocus: function(e) {
2166
+ const user = rest.onFocus;
2167
+ if (user) user(e);
2168
+ keep(e);
2169
+ },
2170
+ onPointerDown: function(e) {
2171
+ const user = rest.onPointerDown;
2172
+ if (user) user(e);
2173
+ keep(e);
2174
+ },
2175
+ onMouseDown: function(e) {
2176
+ const user = rest.onMouseDown;
2177
+ if (user) user(e);
2178
+ e.preventDefault();
2179
+ e.currentTarget.focus();
2180
+ },
2181
+ onKeyDown: function(e) {
2182
+ const user = rest.onKeyDown;
2183
+ if (user) user(e);
2184
+ const k = e.key;
2185
+ const picks = /^(Arrow(Up|Down|Left|Right)|Home|End|Page(Up|Down))$/.test(k) || k.length === 1 && !e.altKey && (!(e.ctrlKey || e.metaKey) || k === " " || k.toLowerCase() === "a");
2186
+ if (picks) e.preventDefault();
2187
+ },
2188
+ onChange: function(e) {
2189
+ const was = kept.current;
2190
+ if (!was) return;
2191
+ Array.prototype.forEach.call(e.currentTarget.options, function(o) {
2192
+ o.selected = was.indexOf(o.value) >= 0;
2193
+ });
2194
+ }
2195
+ } : {};
2143
2196
  const field = (child) => filter ? child : /* @__PURE__ */ React15.createElement(
2144
2197
  Field,
2145
2198
  {
@@ -2172,19 +2225,25 @@ window.Aura = (() => {
2172
2225
  ));
2173
2226
  if (!live)
2174
2227
  return field(
2175
- /* @__PURE__ */ React15.createElement("div", { className: cx("aura-input aura-select", props.icon && "has-icon") }, props.icon ? /* @__PURE__ */ React15.createElement(Icon, { name: props.icon, className: "aura-input__icon" }) : null, h4(
2228
+ /* @__PURE__ */ React15.createElement("div", { className: cx("aura-input aura-select", props.icon && "has-icon", ro && "is-readonly") }, props.icon ? /* @__PURE__ */ React15.createElement(Icon, { name: props.icon, className: "aura-input__icon" }) : null, h4(
2176
2229
  "select",
2177
- Object.assign(extra, rest, {
2178
- ref: native ? ref : selMerged,
2179
- id,
2180
- className: "aura-input__control",
2181
- required: props.required,
2182
- "aria-invalid": props.error ? true : void 0,
2183
- "aria-describedby": described
2184
- }),
2230
+ Object.assign(
2231
+ extra,
2232
+ rest,
2233
+ {
2234
+ ref: native ? ref : selMerged,
2235
+ id,
2236
+ className: "aura-input__control",
2237
+ required: props.required,
2238
+ "aria-invalid": props.error ? true : void 0,
2239
+ "aria-readonly": ro || void 0,
2240
+ "aria-describedby": described
2241
+ },
2242
+ listBoxLock
2243
+ ),
2185
2244
  opts,
2186
2245
  props.children
2187
- ), /* @__PURE__ */ React15.createElement(Icon, { name: /* @__PURE__ */ React15.createElement(IconChevronDown, null), className: "aura-select__chevron" }))
2246
+ ), ro ? null : /* @__PURE__ */ React15.createElement(Icon, { name: /* @__PURE__ */ React15.createElement(IconChevronDown, null), className: "aura-select__chevron" }))
2188
2247
  );
2189
2248
  const listId = id + "-list", labelId = id + "-label";
2190
2249
  const optId = function(i) {
@@ -2273,6 +2332,7 @@ window.Aura = (() => {
2273
2332
  filter ? "aura-filterselect" : "aura-input",
2274
2333
  "aura-select aura-select--custom",
2275
2334
  props.icon && "has-icon",
2335
+ ro && "is-readonly",
2276
2336
  open && "is-open",
2277
2337
  filter && props.disabled && "is-disabled",
2278
2338
  filter && props.className
@@ -2325,6 +2385,7 @@ window.Aura = (() => {
2325
2385
  "aria-labelledby": rest["aria-labelledby"] || (filter && !rest["aria-label"] ? nameId : void 0),
2326
2386
  "aria-required": props.required || void 0,
2327
2387
  "aria-invalid": props.error ? true : void 0,
2388
+ "aria-readonly": ro || void 0,
2328
2389
  "aria-describedby": described,
2329
2390
  onFocus: function(e) {
2330
2391
  if (userFocus) userFocus(e);
@@ -2347,7 +2408,7 @@ window.Aura = (() => {
2347
2408
  },
2348
2409
  face || /* @__PURE__ */ React15.createElement("span", { id: id + "-value", className: cx("aura-select__value", shown[0].empty && "is-placeholder") }, shown[0].label || " ")
2349
2410
  ),
2350
- filter ? null : /* @__PURE__ */ React15.createElement(Icon, { name: /* @__PURE__ */ React15.createElement(IconChevronDown, null), className: "aura-select__chevron" }),
2411
+ filter || ro ? null : /* @__PURE__ */ React15.createElement(Icon, { name: /* @__PURE__ */ React15.createElement(IconChevronDown, null), className: "aura-select__chevron" }),
2351
2412
  popup
2352
2413
  )
2353
2414
  );
@@ -2426,13 +2487,15 @@ window.Aura = (() => {
2426
2487
  const auto = uid(), id = props.id || auto;
2427
2488
  const st = useMaybeControlled(props.checked, !!props.defaultChecked, props.onChange);
2428
2489
  const on = !!st[0];
2490
+ const ro = !!props.readOnly && !props.disabled;
2491
+ const described = [props.description ? id + "-desc" : "", props["aria-describedby"] || ""].filter(Boolean).join(" ");
2429
2492
  return /* @__PURE__ */ React18.createElement(
2430
2493
  "div",
2431
2494
  {
2432
- className: cx("aura-switch-row", props.disabled && "is-disabled", props.className),
2495
+ className: cx("aura-switch-row", props.disabled && "is-disabled", ro && "is-readonly", props.className),
2433
2496
  onClick: function(e) {
2434
2497
  const t = e.target;
2435
- if (props.disabled || t.closest("button, label, a, input")) return;
2498
+ if (props.disabled || ro || t.closest("button, label, a, input")) return;
2436
2499
  st[1](!on);
2437
2500
  }
2438
2501
  },
@@ -2447,15 +2510,17 @@ window.Aura = (() => {
2447
2510
  disabled: props.disabled,
2448
2511
  "aria-labelledby": props.label ? id + "-label" : void 0,
2449
2512
  "aria-label": props.label ? void 0 : props["aria-label"],
2450
- "aria-describedby": props.description ? id + "-desc" : void 0,
2513
+ "aria-readonly": ro || void 0,
2514
+ "aria-describedby": described || void 0,
2451
2515
  className: cx("aura-switch", on && "is-on"),
2452
2516
  onClick: function() {
2453
- st[1](!on);
2517
+ if (!ro) st[1](!on);
2454
2518
  }
2455
2519
  },
2456
2520
  /* @__PURE__ */ React18.createElement("span", { className: "aura-switch__thumb" })
2457
2521
  ),
2458
- props.label ? /* @__PURE__ */ React18.createElement("span", { className: "aura-choice__text" }, /* @__PURE__ */ React18.createElement("label", { className: "aura-choice__label", id: id + "-label", htmlFor: id }, props.label), props.description ? /* @__PURE__ */ React18.createElement("span", { className: "aura-choice__desc", id: id + "-desc" }, props.description) : null) : props.description ? /* @__PURE__ */ React18.createElement("span", { className: "aura-sr-only", id: id + "-desc" }, props.description) : null
2522
+ props.label ? /* @__PURE__ */ React18.createElement("span", { className: "aura-choice__text" }, /* @__PURE__ */ React18.createElement("label", { className: "aura-choice__label", id: id + "-label", htmlFor: id }, props.label), props.description ? /* @__PURE__ */ React18.createElement("span", { className: "aura-choice__desc", id: id + "-desc" }, props.description) : null) : props.description ? /* @__PURE__ */ React18.createElement("span", { className: "aura-sr-only", id: id + "-desc" }, props.description) : null,
2523
+ props.icon ? /* @__PURE__ */ React18.createElement(Icon, { name: props.icon, className: "aura-switch-row__icon" }) : null
2459
2524
  );
2460
2525
  });
2461
2526
 
@@ -8912,6 +8977,7 @@ window.Aura = (() => {
8912
8977
  var React60 = __toESM(require_react(), 1);
8913
8978
  var Stepper = React60.forwardRef(function Stepper2(props, ref) {
8914
8979
  const t = useStrings();
8980
+ const idBase = uid();
8915
8981
  const steps = props.steps || [];
8916
8982
  let at = -1;
8917
8983
  steps.forEach(function(s, i) {
@@ -8929,14 +8995,18 @@ window.Aura = (() => {
8929
8995
  },
8930
8996
  /* @__PURE__ */ React60.createElement("ol", { className: "aura-stepper__list" }, steps.map(function(s, i) {
8931
8997
  const state = i < at ? "done" : i === at ? "current" : "upcoming";
8932
- const marker = /* @__PURE__ */ React60.createElement("span", { className: "aura-stepper__marker", "aria-hidden": true }, state === "done" ? /* @__PURE__ */ React60.createElement(Icon, { name: /* @__PURE__ */ React60.createElement(IconCheck, null) }) : i + 1);
8933
- const text2 = /* @__PURE__ */ React60.createElement("span", { className: "aura-stepper__text" }, /* @__PURE__ */ React60.createElement("span", { className: "aura-stepper__label" }, s.label, state === "done" ? /* @__PURE__ */ React60.createElement("span", { className: "aura-sr-only" }, ", " + t.stepDone) : null), s.description ? /* @__PURE__ */ React60.createElement("span", { className: "aura-stepper__desc" }, s.description) : null);
8998
+ const error = s.status === "error";
8999
+ const marker = /* @__PURE__ */ React60.createElement("span", { className: "aura-stepper__marker", "aria-hidden": true }, error ? /* @__PURE__ */ React60.createElement(Icon, { name: /* @__PURE__ */ React60.createElement(IconCircleAlert, null) }) : state === "done" ? /* @__PURE__ */ React60.createElement(Icon, { name: /* @__PURE__ */ React60.createElement(IconCheck, null) }) : i + 1);
9000
+ const note = error ? t.stepError : state === "done" ? t.stepDone : "";
8934
9001
  const clickable = state === "done" && !!props.onStepClick;
9002
+ const named = clickable && (typeof s.label === "string" || typeof s.label === "number") && String(s.label).trim() !== "";
9003
+ const descId = idBase + "-step-" + i;
9004
+ const text2 = /* @__PURE__ */ React60.createElement("span", { className: "aura-stepper__text" }, /* @__PURE__ */ React60.createElement("span", { className: "aura-stepper__label" }, s.label, note ? /* @__PURE__ */ React60.createElement("span", { className: "aura-sr-only" }, ", " + note) : null), s.description ? /* @__PURE__ */ React60.createElement("span", { className: "aura-stepper__desc", id: named ? descId : void 0 }, s.description) : null);
8935
9005
  return /* @__PURE__ */ React60.createElement(
8936
9006
  "li",
8937
9007
  {
8938
9008
  key: s.id,
8939
- className: cx("aura-stepper__item", "is-" + state),
9009
+ className: cx("aura-stepper__item", "is-" + state, error && "is-error"),
8940
9010
  "aria-current": state === "current" ? "step" : void 0
8941
9011
  },
8942
9012
  clickable ? /* @__PURE__ */ React60.createElement(
@@ -8944,6 +9014,8 @@ window.Aura = (() => {
8944
9014
  {
8945
9015
  type: "button",
8946
9016
  className: "aura-stepper__step aura-focusable",
9017
+ "aria-label": named ? String(s.label) + (note ? ", " + note : "") : void 0,
9018
+ "aria-describedby": named && s.description ? descId : void 0,
8947
9019
  onClick: function() {
8948
9020
  props.onStepClick(s.id);
8949
9021
  }
@@ -8953,7 +9025,7 @@ window.Aura = (() => {
8953
9025
  ) : /* @__PURE__ */ React60.createElement("span", { className: "aura-stepper__step" }, marker, text2)
8954
9026
  );
8955
9027
  })),
8956
- !vertical && cur ? /* @__PURE__ */ React60.createElement("p", { className: "aura-stepper__compact", "aria-hidden": true }, /* @__PURE__ */ React60.createElement("span", { className: "aura-stepper__count" }, t.stepOf(at + 1, steps.length)), /* @__PURE__ */ React60.createElement("span", { className: "aura-stepper__compact-label" }, cur.label)) : null
9028
+ !vertical && cur ? /* @__PURE__ */ React60.createElement("p", { className: "aura-stepper__compact", "aria-hidden": true }, /* @__PURE__ */ React60.createElement("span", { className: "aura-stepper__count" }, t.stepOf(at + 1, steps.length), cur.status === "error" ? /* @__PURE__ */ React60.createElement("span", { className: "aura-stepper__count-error" }, " \u2014 " + t.stepError) : null), /* @__PURE__ */ React60.createElement("span", { className: "aura-stepper__compact-label" }, cur.label)) : null
8957
9029
  );
8958
9030
  });
8959
9031
 
@@ -910,6 +910,7 @@ var th = {
910
910
  return "\u0E43\u0E0A\u0E49\u0E41\u0E25\u0E49\u0E27 " + n(v) + " \u0E08\u0E32\u0E01 " + n(max) + " \u0E08\u0E2D\u0E07\u0E44\u0E27\u0E49 " + n(r);
911
911
  },
912
912
  breadcrumbMore: "\u0E41\u0E2A\u0E14\u0E07\u0E40\u0E2A\u0E49\u0E19\u0E17\u0E32\u0E07\u0E17\u0E31\u0E49\u0E07\u0E2B\u0E21\u0E14",
913
+ stepError: "\u0E21\u0E35\u0E02\u0E49\u0E2D\u0E1C\u0E34\u0E14\u0E1E\u0E25\u0E32\u0E14",
913
914
  accepts: function(list, max) {
914
915
  return [list, max && "\u0E44\u0E21\u0E48\u0E40\u0E01\u0E34\u0E19 " + max + " \u0E15\u0E48\u0E2D\u0E44\u0E1F\u0E25\u0E4C"].filter(Boolean).join(" \xB7 ");
915
916
  }
@@ -1040,6 +1041,7 @@ var sv = {
1040
1041
  return nsv(v) + " av " + nsv(max) + " anv\xE4nda, " + nsv(r) + " reserverade";
1041
1042
  },
1042
1043
  breadcrumbMore: "Visa hela s\xF6kv\xE4gen",
1044
+ stepError: "har fel",
1043
1045
  accepts: function(list, max) {
1044
1046
  return [list, max && "h\xF6gst " + max + " per fil"].filter(Boolean).join(", ");
1045
1047
  }
@@ -1171,6 +1173,7 @@ var STRINGS = {
1171
1173
  return n2(v) + " of " + n2(max) + " used, " + n2(r) + " reserved";
1172
1174
  },
1173
1175
  breadcrumbMore: "Show the full path",
1176
+ stepError: "has errors",
1174
1177
  accepts: function(list, max) {
1175
1178
  return [list, max && "up to " + max + " each"].filter(Boolean).join(", ");
1176
1179
  }
@@ -1848,10 +1851,22 @@ function initialLabel(props, filter) {
1848
1851
  var Select = React15.forwardRef(function Select2(props, ref) {
1849
1852
  const auto = uid(), id = props.id || auto;
1850
1853
  const filter = props[FILTER_KEY];
1851
- const rest = omit(props, FIELD_KEYS.concat(["children", FILTER_KEY]));
1854
+ const rest = omit(props, FIELD_KEYS.concat(["children", FILTER_KEY, "readOnly"]));
1855
+ const ro = !!props.readOnly && !props.disabled;
1856
+ const mounted = useMounted();
1857
+ const kept = React15.useRef(null);
1858
+ const current2 = [].concat(props.value != null ? props.value : props.defaultValue != null ? props.defaultValue : []).map(String);
1852
1859
  const opts = (props.options || []).map(function(o) {
1853
1860
  const v = typeof o === "object" ? o : { value: o, label: o };
1854
- return /* @__PURE__ */ React15.createElement("option", { key: v.value, value: v.value, disabled: v.disabled }, v.label);
1861
+ return /* @__PURE__ */ React15.createElement(
1862
+ "option",
1863
+ {
1864
+ key: v.value,
1865
+ value: v.value,
1866
+ disabled: v.disabled || ro && !mounted && current2.length > 0 && current2.indexOf(v.value) < 0 || void 0
1867
+ },
1868
+ v.label
1869
+ );
1855
1870
  });
1856
1871
  if (props.placeholder)
1857
1872
  opts.unshift(
@@ -1859,7 +1874,6 @@ var Select = React15.forwardRef(function Select2(props, ref) {
1859
1874
  );
1860
1875
  const extra = props.value === void 0 && props.defaultValue === void 0 && props.placeholder ? { defaultValue: "" } : {};
1861
1876
  const native = !!props.multiple || props.size != null && props.size > 1;
1862
- const mounted = useMounted();
1863
1877
  const live = !native && mounted;
1864
1878
  const density = useDensity();
1865
1879
  React15.useEffect(function() {
@@ -1981,7 +1995,7 @@ var Select = React15.forwardRef(function Select2(props, ref) {
1981
1995
  }
1982
1996
  function choose(it) {
1983
1997
  const el = selRef.current;
1984
- if (!el || it.disabled) return;
1998
+ if (!el || it.disabled || ro) return;
1985
1999
  if (el.value !== it.value) {
1986
2000
  el.value = it.value;
1987
2001
  el.dispatchEvent(new Event("input", { bubbles: true }));
@@ -1991,7 +2005,7 @@ var Select = React15.forwardRef(function Select2(props, ref) {
1991
2005
  close(true);
1992
2006
  }
1993
2007
  function openList(at) {
1994
- if (props.disabled || trigRef.current && trigRef.current.disabled) return;
2008
+ if (props.disabled || ro || trigRef.current && trigRef.current.disabled) return;
1995
2009
  const list = readItems(selRef.current);
1996
2010
  const ok = choices(list);
1997
2011
  if (!ok.length) return;
@@ -2122,6 +2136,45 @@ var Select = React15.forwardRef(function Select2(props, ref) {
2122
2136
  [open, active, pos[0] != null]
2123
2137
  );
2124
2138
  const described = [describedBy(id, props), rest["aria-describedby"]].filter(Boolean).join(" ") || void 0;
2139
+ function keep(e) {
2140
+ kept.current = Array.prototype.filter.call(e.currentTarget.options, function(o) {
2141
+ return o.selected;
2142
+ }).map(function(o) {
2143
+ return o.value;
2144
+ });
2145
+ }
2146
+ const listBoxLock = ro && native ? {
2147
+ onFocus: function(e) {
2148
+ const user = rest.onFocus;
2149
+ if (user) user(e);
2150
+ keep(e);
2151
+ },
2152
+ onPointerDown: function(e) {
2153
+ const user = rest.onPointerDown;
2154
+ if (user) user(e);
2155
+ keep(e);
2156
+ },
2157
+ onMouseDown: function(e) {
2158
+ const user = rest.onMouseDown;
2159
+ if (user) user(e);
2160
+ e.preventDefault();
2161
+ e.currentTarget.focus();
2162
+ },
2163
+ onKeyDown: function(e) {
2164
+ const user = rest.onKeyDown;
2165
+ if (user) user(e);
2166
+ const k = e.key;
2167
+ const picks = /^(Arrow(Up|Down|Left|Right)|Home|End|Page(Up|Down))$/.test(k) || k.length === 1 && !e.altKey && (!(e.ctrlKey || e.metaKey) || k === " " || k.toLowerCase() === "a");
2168
+ if (picks) e.preventDefault();
2169
+ },
2170
+ onChange: function(e) {
2171
+ const was = kept.current;
2172
+ if (!was) return;
2173
+ Array.prototype.forEach.call(e.currentTarget.options, function(o) {
2174
+ o.selected = was.indexOf(o.value) >= 0;
2175
+ });
2176
+ }
2177
+ } : {};
2125
2178
  const field = (child) => filter ? child : /* @__PURE__ */ React15.createElement(
2126
2179
  Field,
2127
2180
  {
@@ -2154,19 +2207,25 @@ var Select = React15.forwardRef(function Select2(props, ref) {
2154
2207
  ));
2155
2208
  if (!live)
2156
2209
  return field(
2157
- /* @__PURE__ */ React15.createElement("div", { className: cx("aura-input aura-select", props.icon && "has-icon") }, props.icon ? /* @__PURE__ */ React15.createElement(Icon, { name: props.icon, className: "aura-input__icon" }) : null, h4(
2210
+ /* @__PURE__ */ React15.createElement("div", { className: cx("aura-input aura-select", props.icon && "has-icon", ro && "is-readonly") }, props.icon ? /* @__PURE__ */ React15.createElement(Icon, { name: props.icon, className: "aura-input__icon" }) : null, h4(
2158
2211
  "select",
2159
- Object.assign(extra, rest, {
2160
- ref: native ? ref : selMerged,
2161
- id,
2162
- className: "aura-input__control",
2163
- required: props.required,
2164
- "aria-invalid": props.error ? true : void 0,
2165
- "aria-describedby": described
2166
- }),
2212
+ Object.assign(
2213
+ extra,
2214
+ rest,
2215
+ {
2216
+ ref: native ? ref : selMerged,
2217
+ id,
2218
+ className: "aura-input__control",
2219
+ required: props.required,
2220
+ "aria-invalid": props.error ? true : void 0,
2221
+ "aria-readonly": ro || void 0,
2222
+ "aria-describedby": described
2223
+ },
2224
+ listBoxLock
2225
+ ),
2167
2226
  opts,
2168
2227
  props.children
2169
- ), /* @__PURE__ */ React15.createElement(Icon, { name: /* @__PURE__ */ React15.createElement(IconChevronDown, null), className: "aura-select__chevron" }))
2228
+ ), ro ? null : /* @__PURE__ */ React15.createElement(Icon, { name: /* @__PURE__ */ React15.createElement(IconChevronDown, null), className: "aura-select__chevron" }))
2170
2229
  );
2171
2230
  const listId = id + "-list", labelId = id + "-label";
2172
2231
  const optId = function(i) {
@@ -2255,6 +2314,7 @@ var Select = React15.forwardRef(function Select2(props, ref) {
2255
2314
  filter ? "aura-filterselect" : "aura-input",
2256
2315
  "aura-select aura-select--custom",
2257
2316
  props.icon && "has-icon",
2317
+ ro && "is-readonly",
2258
2318
  open && "is-open",
2259
2319
  filter && props.disabled && "is-disabled",
2260
2320
  filter && props.className
@@ -2307,6 +2367,7 @@ var Select = React15.forwardRef(function Select2(props, ref) {
2307
2367
  "aria-labelledby": rest["aria-labelledby"] || (filter && !rest["aria-label"] ? nameId : void 0),
2308
2368
  "aria-required": props.required || void 0,
2309
2369
  "aria-invalid": props.error ? true : void 0,
2370
+ "aria-readonly": ro || void 0,
2310
2371
  "aria-describedby": described,
2311
2372
  onFocus: function(e) {
2312
2373
  if (userFocus) userFocus(e);
@@ -2329,7 +2390,7 @@ var Select = React15.forwardRef(function Select2(props, ref) {
2329
2390
  },
2330
2391
  face || /* @__PURE__ */ React15.createElement("span", { id: id + "-value", className: cx("aura-select__value", shown[0].empty && "is-placeholder") }, shown[0].label || " ")
2331
2392
  ),
2332
- filter ? null : /* @__PURE__ */ React15.createElement(Icon, { name: /* @__PURE__ */ React15.createElement(IconChevronDown, null), className: "aura-select__chevron" }),
2393
+ filter || ro ? null : /* @__PURE__ */ React15.createElement(Icon, { name: /* @__PURE__ */ React15.createElement(IconChevronDown, null), className: "aura-select__chevron" }),
2333
2394
  popup
2334
2395
  )
2335
2396
  );
@@ -2408,13 +2469,15 @@ var Switch = React18.forwardRef(function Switch2(props, ref) {
2408
2469
  const auto = uid(), id = props.id || auto;
2409
2470
  const st = useMaybeControlled(props.checked, !!props.defaultChecked, props.onChange);
2410
2471
  const on = !!st[0];
2472
+ const ro = !!props.readOnly && !props.disabled;
2473
+ const described = [props.description ? id + "-desc" : "", props["aria-describedby"] || ""].filter(Boolean).join(" ");
2411
2474
  return /* @__PURE__ */ React18.createElement(
2412
2475
  "div",
2413
2476
  {
2414
- className: cx("aura-switch-row", props.disabled && "is-disabled", props.className),
2477
+ className: cx("aura-switch-row", props.disabled && "is-disabled", ro && "is-readonly", props.className),
2415
2478
  onClick: function(e) {
2416
2479
  const t = e.target;
2417
- if (props.disabled || t.closest("button, label, a, input")) return;
2480
+ if (props.disabled || ro || t.closest("button, label, a, input")) return;
2418
2481
  st[1](!on);
2419
2482
  }
2420
2483
  },
@@ -2429,15 +2492,17 @@ var Switch = React18.forwardRef(function Switch2(props, ref) {
2429
2492
  disabled: props.disabled,
2430
2493
  "aria-labelledby": props.label ? id + "-label" : void 0,
2431
2494
  "aria-label": props.label ? void 0 : props["aria-label"],
2432
- "aria-describedby": props.description ? id + "-desc" : void 0,
2495
+ "aria-readonly": ro || void 0,
2496
+ "aria-describedby": described || void 0,
2433
2497
  className: cx("aura-switch", on && "is-on"),
2434
2498
  onClick: function() {
2435
- st[1](!on);
2499
+ if (!ro) st[1](!on);
2436
2500
  }
2437
2501
  },
2438
2502
  /* @__PURE__ */ React18.createElement("span", { className: "aura-switch__thumb" })
2439
2503
  ),
2440
- props.label ? /* @__PURE__ */ React18.createElement("span", { className: "aura-choice__text" }, /* @__PURE__ */ React18.createElement("label", { className: "aura-choice__label", id: id + "-label", htmlFor: id }, props.label), props.description ? /* @__PURE__ */ React18.createElement("span", { className: "aura-choice__desc", id: id + "-desc" }, props.description) : null) : props.description ? /* @__PURE__ */ React18.createElement("span", { className: "aura-sr-only", id: id + "-desc" }, props.description) : null
2504
+ props.label ? /* @__PURE__ */ React18.createElement("span", { className: "aura-choice__text" }, /* @__PURE__ */ React18.createElement("label", { className: "aura-choice__label", id: id + "-label", htmlFor: id }, props.label), props.description ? /* @__PURE__ */ React18.createElement("span", { className: "aura-choice__desc", id: id + "-desc" }, props.description) : null) : props.description ? /* @__PURE__ */ React18.createElement("span", { className: "aura-sr-only", id: id + "-desc" }, props.description) : null,
2505
+ props.icon ? /* @__PURE__ */ React18.createElement(Icon, { name: props.icon, className: "aura-switch-row__icon" }) : null
2441
2506
  );
2442
2507
  });
2443
2508
 
@@ -8894,6 +8959,7 @@ var NumberField = React59.forwardRef(function NumberField2(props, ref) {
8894
8959
  var React60 = __toESM(require("react"), 1);
8895
8960
  var Stepper = React60.forwardRef(function Stepper2(props, ref) {
8896
8961
  const t = useStrings();
8962
+ const idBase = uid();
8897
8963
  const steps = props.steps || [];
8898
8964
  let at = -1;
8899
8965
  steps.forEach(function(s, i) {
@@ -8911,14 +8977,18 @@ var Stepper = React60.forwardRef(function Stepper2(props, ref) {
8911
8977
  },
8912
8978
  /* @__PURE__ */ React60.createElement("ol", { className: "aura-stepper__list" }, steps.map(function(s, i) {
8913
8979
  const state = i < at ? "done" : i === at ? "current" : "upcoming";
8914
- const marker = /* @__PURE__ */ React60.createElement("span", { className: "aura-stepper__marker", "aria-hidden": true }, state === "done" ? /* @__PURE__ */ React60.createElement(Icon, { name: /* @__PURE__ */ React60.createElement(IconCheck, null) }) : i + 1);
8915
- const text2 = /* @__PURE__ */ React60.createElement("span", { className: "aura-stepper__text" }, /* @__PURE__ */ React60.createElement("span", { className: "aura-stepper__label" }, s.label, state === "done" ? /* @__PURE__ */ React60.createElement("span", { className: "aura-sr-only" }, ", " + t.stepDone) : null), s.description ? /* @__PURE__ */ React60.createElement("span", { className: "aura-stepper__desc" }, s.description) : null);
8980
+ const error = s.status === "error";
8981
+ const marker = /* @__PURE__ */ React60.createElement("span", { className: "aura-stepper__marker", "aria-hidden": true }, error ? /* @__PURE__ */ React60.createElement(Icon, { name: /* @__PURE__ */ React60.createElement(IconCircleAlert, null) }) : state === "done" ? /* @__PURE__ */ React60.createElement(Icon, { name: /* @__PURE__ */ React60.createElement(IconCheck, null) }) : i + 1);
8982
+ const note = error ? t.stepError : state === "done" ? t.stepDone : "";
8916
8983
  const clickable = state === "done" && !!props.onStepClick;
8984
+ const named = clickable && (typeof s.label === "string" || typeof s.label === "number") && String(s.label).trim() !== "";
8985
+ const descId = idBase + "-step-" + i;
8986
+ const text2 = /* @__PURE__ */ React60.createElement("span", { className: "aura-stepper__text" }, /* @__PURE__ */ React60.createElement("span", { className: "aura-stepper__label" }, s.label, note ? /* @__PURE__ */ React60.createElement("span", { className: "aura-sr-only" }, ", " + note) : null), s.description ? /* @__PURE__ */ React60.createElement("span", { className: "aura-stepper__desc", id: named ? descId : void 0 }, s.description) : null);
8917
8987
  return /* @__PURE__ */ React60.createElement(
8918
8988
  "li",
8919
8989
  {
8920
8990
  key: s.id,
8921
- className: cx("aura-stepper__item", "is-" + state),
8991
+ className: cx("aura-stepper__item", "is-" + state, error && "is-error"),
8922
8992
  "aria-current": state === "current" ? "step" : void 0
8923
8993
  },
8924
8994
  clickable ? /* @__PURE__ */ React60.createElement(
@@ -8926,6 +8996,8 @@ var Stepper = React60.forwardRef(function Stepper2(props, ref) {
8926
8996
  {
8927
8997
  type: "button",
8928
8998
  className: "aura-stepper__step aura-focusable",
8999
+ "aria-label": named ? String(s.label) + (note ? ", " + note : "") : void 0,
9000
+ "aria-describedby": named && s.description ? descId : void 0,
8929
9001
  onClick: function() {
8930
9002
  props.onStepClick(s.id);
8931
9003
  }
@@ -8935,7 +9007,7 @@ var Stepper = React60.forwardRef(function Stepper2(props, ref) {
8935
9007
  ) : /* @__PURE__ */ React60.createElement("span", { className: "aura-stepper__step" }, marker, text2)
8936
9008
  );
8937
9009
  })),
8938
- !vertical && cur ? /* @__PURE__ */ React60.createElement("p", { className: "aura-stepper__compact", "aria-hidden": true }, /* @__PURE__ */ React60.createElement("span", { className: "aura-stepper__count" }, t.stepOf(at + 1, steps.length)), /* @__PURE__ */ React60.createElement("span", { className: "aura-stepper__compact-label" }, cur.label)) : null
9010
+ !vertical && cur ? /* @__PURE__ */ React60.createElement("p", { className: "aura-stepper__compact", "aria-hidden": true }, /* @__PURE__ */ React60.createElement("span", { className: "aura-stepper__count" }, t.stepOf(at + 1, steps.length), cur.status === "error" ? /* @__PURE__ */ React60.createElement("span", { className: "aura-stepper__count-error" }, " \u2014 " + t.stepError) : null), /* @__PURE__ */ React60.createElement("span", { className: "aura-stepper__compact-label" }, cur.label)) : null
8939
9011
  );
8940
9012
  });
8941
9013
 
@@ -83,10 +83,22 @@ function initialLabel(props, filter) {
83
83
  const Select = React.forwardRef(function Select2(props, ref) {
84
84
  const auto = uid(), id = props.id || auto;
85
85
  const filter = props[FILTER_KEY];
86
- const rest = omit(props, FIELD_KEYS.concat(["children", FILTER_KEY]));
86
+ const rest = omit(props, FIELD_KEYS.concat(["children", FILTER_KEY, "readOnly"]));
87
+ const ro = !!props.readOnly && !props.disabled;
88
+ const mounted = useMounted();
89
+ const kept = React.useRef(null);
90
+ const current = [].concat(props.value != null ? props.value : props.defaultValue != null ? props.defaultValue : []).map(String);
87
91
  const opts = (props.options || []).map(function(o) {
88
92
  const v = typeof o === "object" ? o : { value: o, label: o };
89
- return /* @__PURE__ */ React.createElement("option", { key: v.value, value: v.value, disabled: v.disabled }, v.label);
93
+ return /* @__PURE__ */ React.createElement(
94
+ "option",
95
+ {
96
+ key: v.value,
97
+ value: v.value,
98
+ disabled: v.disabled || ro && !mounted && current.length > 0 && current.indexOf(v.value) < 0 || void 0
99
+ },
100
+ v.label
101
+ );
90
102
  });
91
103
  if (props.placeholder)
92
104
  opts.unshift(
@@ -94,7 +106,6 @@ const Select = React.forwardRef(function Select2(props, ref) {
94
106
  );
95
107
  const extra = props.value === void 0 && props.defaultValue === void 0 && props.placeholder ? { defaultValue: "" } : {};
96
108
  const native = !!props.multiple || props.size != null && props.size > 1;
97
- const mounted = useMounted();
98
109
  const live = !native && mounted;
99
110
  const density = useDensity();
100
111
  React.useEffect(function() {
@@ -216,7 +227,7 @@ const Select = React.forwardRef(function Select2(props, ref) {
216
227
  }
217
228
  function choose(it) {
218
229
  const el = selRef.current;
219
- if (!el || it.disabled) return;
230
+ if (!el || it.disabled || ro) return;
220
231
  if (el.value !== it.value) {
221
232
  el.value = it.value;
222
233
  el.dispatchEvent(new Event("input", { bubbles: true }));
@@ -226,7 +237,7 @@ const Select = React.forwardRef(function Select2(props, ref) {
226
237
  close(true);
227
238
  }
228
239
  function openList(at) {
229
- if (props.disabled || trigRef.current && trigRef.current.disabled) return;
240
+ if (props.disabled || ro || trigRef.current && trigRef.current.disabled) return;
230
241
  const list = readItems(selRef.current);
231
242
  const ok = choices(list);
232
243
  if (!ok.length) return;
@@ -357,6 +368,45 @@ const Select = React.forwardRef(function Select2(props, ref) {
357
368
  [open, active, pos[0] != null]
358
369
  );
359
370
  const described = [describedBy(id, props), rest["aria-describedby"]].filter(Boolean).join(" ") || void 0;
371
+ function keep(e) {
372
+ kept.current = Array.prototype.filter.call(e.currentTarget.options, function(o) {
373
+ return o.selected;
374
+ }).map(function(o) {
375
+ return o.value;
376
+ });
377
+ }
378
+ const listBoxLock = ro && native ? {
379
+ onFocus: function(e) {
380
+ const user = rest.onFocus;
381
+ if (user) user(e);
382
+ keep(e);
383
+ },
384
+ onPointerDown: function(e) {
385
+ const user = rest.onPointerDown;
386
+ if (user) user(e);
387
+ keep(e);
388
+ },
389
+ onMouseDown: function(e) {
390
+ const user = rest.onMouseDown;
391
+ if (user) user(e);
392
+ e.preventDefault();
393
+ e.currentTarget.focus();
394
+ },
395
+ onKeyDown: function(e) {
396
+ const user = rest.onKeyDown;
397
+ if (user) user(e);
398
+ const k = e.key;
399
+ const picks = /^(Arrow(Up|Down|Left|Right)|Home|End|Page(Up|Down))$/.test(k) || k.length === 1 && !e.altKey && (!(e.ctrlKey || e.metaKey) || k === " " || k.toLowerCase() === "a");
400
+ if (picks) e.preventDefault();
401
+ },
402
+ onChange: function(e) {
403
+ const was = kept.current;
404
+ if (!was) return;
405
+ Array.prototype.forEach.call(e.currentTarget.options, function(o) {
406
+ o.selected = was.indexOf(o.value) >= 0;
407
+ });
408
+ }
409
+ } : {};
360
410
  const field = (child) => filter ? child : /* @__PURE__ */ React.createElement(
361
411
  Field,
362
412
  {
@@ -389,19 +439,25 @@ const Select = React.forwardRef(function Select2(props, ref) {
389
439
  ));
390
440
  if (!live)
391
441
  return field(
392
- /* @__PURE__ */ React.createElement("div", { className: cx("aura-input aura-select", props.icon && "has-icon") }, props.icon ? /* @__PURE__ */ React.createElement(Icon, { name: props.icon, className: "aura-input__icon" }) : null, h(
442
+ /* @__PURE__ */ React.createElement("div", { className: cx("aura-input aura-select", props.icon && "has-icon", ro && "is-readonly") }, props.icon ? /* @__PURE__ */ React.createElement(Icon, { name: props.icon, className: "aura-input__icon" }) : null, h(
393
443
  "select",
394
- Object.assign(extra, rest, {
395
- ref: native ? ref : selMerged,
396
- id,
397
- className: "aura-input__control",
398
- required: props.required,
399
- "aria-invalid": props.error ? true : void 0,
400
- "aria-describedby": described
401
- }),
444
+ Object.assign(
445
+ extra,
446
+ rest,
447
+ {
448
+ ref: native ? ref : selMerged,
449
+ id,
450
+ className: "aura-input__control",
451
+ required: props.required,
452
+ "aria-invalid": props.error ? true : void 0,
453
+ "aria-readonly": ro || void 0,
454
+ "aria-describedby": described
455
+ },
456
+ listBoxLock
457
+ ),
402
458
  opts,
403
459
  props.children
404
- ), /* @__PURE__ */ React.createElement(Icon, { name: /* @__PURE__ */ React.createElement(IconChevronDown, null), className: "aura-select__chevron" }))
460
+ ), ro ? null : /* @__PURE__ */ React.createElement(Icon, { name: /* @__PURE__ */ React.createElement(IconChevronDown, null), className: "aura-select__chevron" }))
405
461
  );
406
462
  const listId = id + "-list", labelId = id + "-label";
407
463
  const optId = function(i) {
@@ -490,6 +546,7 @@ const Select = React.forwardRef(function Select2(props, ref) {
490
546
  filter ? "aura-filterselect" : "aura-input",
491
547
  "aura-select aura-select--custom",
492
548
  props.icon && "has-icon",
549
+ ro && "is-readonly",
493
550
  open && "is-open",
494
551
  filter && props.disabled && "is-disabled",
495
552
  filter && props.className
@@ -542,6 +599,7 @@ const Select = React.forwardRef(function Select2(props, ref) {
542
599
  "aria-labelledby": rest["aria-labelledby"] || (filter && !rest["aria-label"] ? nameId : void 0),
543
600
  "aria-required": props.required || void 0,
544
601
  "aria-invalid": props.error ? true : void 0,
602
+ "aria-readonly": ro || void 0,
545
603
  "aria-describedby": described,
546
604
  onFocus: function(e) {
547
605
  if (userFocus) userFocus(e);
@@ -564,7 +622,7 @@ const Select = React.forwardRef(function Select2(props, ref) {
564
622
  },
565
623
  face || /* @__PURE__ */ React.createElement("span", { id: id + "-value", className: cx("aura-select__value", shown[0].empty && "is-placeholder") }, shown[0].label || " ")
566
624
  ),
567
- filter ? null : /* @__PURE__ */ React.createElement(Icon, { name: /* @__PURE__ */ React.createElement(IconChevronDown, null), className: "aura-select__chevron" }),
625
+ filter || ro ? null : /* @__PURE__ */ React.createElement(Icon, { name: /* @__PURE__ */ React.createElement(IconChevronDown, null), className: "aura-select__chevron" }),
568
626
  popup
569
627
  )
570
628
  );
@@ -1,11 +1,12 @@
1
1
  'use client';
2
2
  import * as React from "react";
3
3
  import { Icon } from "./Icon.js";
4
- import { cx } from "./internal.js";
4
+ import { cx, uid } from "./internal.js";
5
5
  import { useStrings } from "./locale.js";
6
- import { IconCheck } from "./icons.js";
6
+ import { IconCheck, IconCircleAlert } from "./icons.js";
7
7
  const Stepper = React.forwardRef(function Stepper2(props, ref) {
8
8
  const t = useStrings();
9
+ const idBase = uid();
9
10
  const steps = props.steps || [];
10
11
  let at = -1;
11
12
  steps.forEach(function(s, i) {
@@ -23,14 +24,18 @@ const Stepper = React.forwardRef(function Stepper2(props, ref) {
23
24
  },
24
25
  /* @__PURE__ */ React.createElement("ol", { className: "aura-stepper__list" }, steps.map(function(s, i) {
25
26
  const state = i < at ? "done" : i === at ? "current" : "upcoming";
26
- const marker = /* @__PURE__ */ React.createElement("span", { className: "aura-stepper__marker", "aria-hidden": true }, state === "done" ? /* @__PURE__ */ React.createElement(Icon, { name: /* @__PURE__ */ React.createElement(IconCheck, null) }) : i + 1);
27
- const text = /* @__PURE__ */ React.createElement("span", { className: "aura-stepper__text" }, /* @__PURE__ */ React.createElement("span", { className: "aura-stepper__label" }, s.label, state === "done" ? /* @__PURE__ */ React.createElement("span", { className: "aura-sr-only" }, ", " + t.stepDone) : null), s.description ? /* @__PURE__ */ React.createElement("span", { className: "aura-stepper__desc" }, s.description) : null);
27
+ const error = s.status === "error";
28
+ const marker = /* @__PURE__ */ React.createElement("span", { className: "aura-stepper__marker", "aria-hidden": true }, error ? /* @__PURE__ */ React.createElement(Icon, { name: /* @__PURE__ */ React.createElement(IconCircleAlert, null) }) : state === "done" ? /* @__PURE__ */ React.createElement(Icon, { name: /* @__PURE__ */ React.createElement(IconCheck, null) }) : i + 1);
29
+ const note = error ? t.stepError : state === "done" ? t.stepDone : "";
28
30
  const clickable = state === "done" && !!props.onStepClick;
31
+ const named = clickable && (typeof s.label === "string" || typeof s.label === "number") && String(s.label).trim() !== "";
32
+ const descId = idBase + "-step-" + i;
33
+ const text = /* @__PURE__ */ React.createElement("span", { className: "aura-stepper__text" }, /* @__PURE__ */ React.createElement("span", { className: "aura-stepper__label" }, s.label, note ? /* @__PURE__ */ React.createElement("span", { className: "aura-sr-only" }, ", " + note) : null), s.description ? /* @__PURE__ */ React.createElement("span", { className: "aura-stepper__desc", id: named ? descId : void 0 }, s.description) : null);
29
34
  return /* @__PURE__ */ React.createElement(
30
35
  "li",
31
36
  {
32
37
  key: s.id,
33
- className: cx("aura-stepper__item", "is-" + state),
38
+ className: cx("aura-stepper__item", "is-" + state, error && "is-error"),
34
39
  "aria-current": state === "current" ? "step" : void 0
35
40
  },
36
41
  clickable ? /* @__PURE__ */ React.createElement(
@@ -38,6 +43,8 @@ const Stepper = React.forwardRef(function Stepper2(props, ref) {
38
43
  {
39
44
  type: "button",
40
45
  className: "aura-stepper__step aura-focusable",
46
+ "aria-label": named ? String(s.label) + (note ? ", " + note : "") : void 0,
47
+ "aria-describedby": named && s.description ? descId : void 0,
41
48
  onClick: function() {
42
49
  props.onStepClick(s.id);
43
50
  }
@@ -47,7 +54,7 @@ const Stepper = React.forwardRef(function Stepper2(props, ref) {
47
54
  ) : /* @__PURE__ */ React.createElement("span", { className: "aura-stepper__step" }, marker, text)
48
55
  );
49
56
  })),
50
- !vertical && cur ? /* @__PURE__ */ React.createElement("p", { className: "aura-stepper__compact", "aria-hidden": true }, /* @__PURE__ */ React.createElement("span", { className: "aura-stepper__count" }, t.stepOf(at + 1, steps.length)), /* @__PURE__ */ React.createElement("span", { className: "aura-stepper__compact-label" }, cur.label)) : null
57
+ !vertical && cur ? /* @__PURE__ */ React.createElement("p", { className: "aura-stepper__compact", "aria-hidden": true }, /* @__PURE__ */ React.createElement("span", { className: "aura-stepper__count" }, t.stepOf(at + 1, steps.length), cur.status === "error" ? /* @__PURE__ */ React.createElement("span", { className: "aura-stepper__count-error" }, " \u2014 " + t.stepError) : null), /* @__PURE__ */ React.createElement("span", { className: "aura-stepper__compact-label" }, cur.label)) : null
51
58
  );
52
59
  });
53
60
  export {
@@ -1,17 +1,20 @@
1
1
  'use client';
2
2
  import * as React from "react";
3
+ import { Icon } from "./Icon.js";
3
4
  import { cx, uid, useMaybeControlled } from "./internal.js";
4
5
  const Switch = React.forwardRef(function Switch2(props, ref) {
5
6
  const auto = uid(), id = props.id || auto;
6
7
  const st = useMaybeControlled(props.checked, !!props.defaultChecked, props.onChange);
7
8
  const on = !!st[0];
9
+ const ro = !!props.readOnly && !props.disabled;
10
+ const described = [props.description ? id + "-desc" : "", props["aria-describedby"] || ""].filter(Boolean).join(" ");
8
11
  return /* @__PURE__ */ React.createElement(
9
12
  "div",
10
13
  {
11
- className: cx("aura-switch-row", props.disabled && "is-disabled", props.className),
14
+ className: cx("aura-switch-row", props.disabled && "is-disabled", ro && "is-readonly", props.className),
12
15
  onClick: function(e) {
13
16
  const t = e.target;
14
- if (props.disabled || t.closest("button, label, a, input")) return;
17
+ if (props.disabled || ro || t.closest("button, label, a, input")) return;
15
18
  st[1](!on);
16
19
  }
17
20
  },
@@ -26,15 +29,17 @@ const Switch = React.forwardRef(function Switch2(props, ref) {
26
29
  disabled: props.disabled,
27
30
  "aria-labelledby": props.label ? id + "-label" : void 0,
28
31
  "aria-label": props.label ? void 0 : props["aria-label"],
29
- "aria-describedby": props.description ? id + "-desc" : void 0,
32
+ "aria-readonly": ro || void 0,
33
+ "aria-describedby": described || void 0,
30
34
  className: cx("aura-switch", on && "is-on"),
31
35
  onClick: function() {
32
- st[1](!on);
36
+ if (!ro) st[1](!on);
33
37
  }
34
38
  },
35
39
  /* @__PURE__ */ React.createElement("span", { className: "aura-switch__thumb" })
36
40
  ),
37
- props.label ? /* @__PURE__ */ React.createElement("span", { className: "aura-choice__text" }, /* @__PURE__ */ React.createElement("label", { className: "aura-choice__label", id: id + "-label", htmlFor: id }, props.label), props.description ? /* @__PURE__ */ React.createElement("span", { className: "aura-choice__desc", id: id + "-desc" }, props.description) : null) : props.description ? /* @__PURE__ */ React.createElement("span", { className: "aura-sr-only", id: id + "-desc" }, props.description) : null
41
+ props.label ? /* @__PURE__ */ React.createElement("span", { className: "aura-choice__text" }, /* @__PURE__ */ React.createElement("label", { className: "aura-choice__label", id: id + "-label", htmlFor: id }, props.label), props.description ? /* @__PURE__ */ React.createElement("span", { className: "aura-choice__desc", id: id + "-desc" }, props.description) : null) : props.description ? /* @__PURE__ */ React.createElement("span", { className: "aura-sr-only", id: id + "-desc" }, props.description) : null,
42
+ props.icon ? /* @__PURE__ */ React.createElement(Icon, { name: props.icon, className: "aura-switch-row__icon" }) : null
38
43
  );
39
44
  });
40
45
  export {
@@ -126,6 +126,7 @@ const STRINGS = {
126
126
  return n(v) + " of " + n(max) + " used, " + n(r) + " reserved";
127
127
  },
128
128
  breadcrumbMore: "Show the full path",
129
+ stepError: "has errors",
129
130
  accepts: function(list, max) {
130
131
  return [list, max && "up to " + max + " each"].filter(Boolean).join(", ");
131
132
  }
@@ -122,6 +122,7 @@ const sv = {
122
122
  return nsv(v) + " av " + nsv(max) + " anv\xE4nda, " + nsv(r) + " reserverade";
123
123
  },
124
124
  breadcrumbMore: "Visa hela s\xF6kv\xE4gen",
125
+ stepError: "har fel",
125
126
  accepts: function(list, max) {
126
127
  return [list, max && "h\xF6gst " + max + " per fil"].filter(Boolean).join(", ");
127
128
  }
@@ -122,6 +122,7 @@ const th = {
122
122
  return "\u0E43\u0E0A\u0E49\u0E41\u0E25\u0E49\u0E27 " + n(v) + " \u0E08\u0E32\u0E01 " + n(max) + " \u0E08\u0E2D\u0E07\u0E44\u0E27\u0E49 " + n(r);
123
123
  },
124
124
  breadcrumbMore: "\u0E41\u0E2A\u0E14\u0E07\u0E40\u0E2A\u0E49\u0E19\u0E17\u0E32\u0E07\u0E17\u0E31\u0E49\u0E07\u0E2B\u0E21\u0E14",
125
+ stepError: "\u0E21\u0E35\u0E02\u0E49\u0E2D\u0E1C\u0E34\u0E14\u0E1E\u0E25\u0E32\u0E14",
125
126
  accepts: function(list, max) {
126
127
  return [list, max && "\u0E44\u0E21\u0E48\u0E40\u0E01\u0E34\u0E19 " + max + " \u0E15\u0E48\u0E2D\u0E44\u0E1F\u0E25\u0E4C"].filter(Boolean).join(" \xB7 ");
127
128
  }
package/dist/index.d.cts CHANGED
@@ -517,6 +517,10 @@ export interface SelectProps extends Omit<FieldProps, "label">, Omit<React$1.Sel
517
517
  /** Shown in fg-tertiary on the closed field until something is chosen; not listed as a choice. */
518
518
  placeholder?: string | undefined;
519
519
  icon?: IconInput | undefined;
520
+ /** 5.19 (Chamber-OS 114): a locked value. The field stays in the Tab order with `aria-readonly` and shows the chosen
521
+ * option on the read-only ground, with no chevron; no click or key opens the list. The value is still submitted
522
+ * with the form (unlike `disabled`). Before JavaScript runs, the other options from `options` are disabled. */
523
+ readOnly?: boolean | undefined;
520
524
  }
521
525
  /** A compact filter for FilterBar (5.12): "Status All ▾", as wide as its words. */
522
526
  export interface FilterSelectProps extends Omit<React$1.SelectHTMLAttributes<HTMLSelectElement>, "onChange" | "value" | "defaultValue" | "multiple" | "size" | "placeholder" | "required" | "children"> {
@@ -557,8 +561,16 @@ export interface SwitchProps {
557
561
  defaultChecked?: boolean | undefined;
558
562
  onChange?: ((checked: boolean) => void) | undefined;
559
563
  disabled?: boolean | undefined;
564
+ /** 5.19 (Chamber-OS 113): a locked value. The switch stays in the Tab order with `aria-readonly`, so its name,
565
+ * state and description are heard; click, Space and the row ignore it. Its colours stay (on reads as on). */
566
+ readOnly?: boolean | undefined;
567
+ /** 5.19: an icon at the end of the row, in fg-secondary and the field icon's size — `icon={<IconLock />}` or
568
+ * `icon="lock"` on a locked row. Hidden from screen readers; say what it means in the description. */
569
+ icon?: IconInput | undefined;
560
570
  /** Required when there is no visible label. */
561
571
  "aria-label"?: string | undefined;
572
+ /** 5.19: ids of more text that describes the switch (a lock note); merged with `description`'s. */
573
+ "aria-describedby"?: string | undefined;
562
574
  id?: string | undefined;
563
575
  className?: string | undefined;
564
576
  }
@@ -914,6 +926,10 @@ export interface StepItem {
914
926
  label: React$1.ReactNode;
915
927
  /** Short line under the label, e.g. what the step asks for. */
916
928
  description?: React$1.ReactNode | undefined;
929
+ /** `'error'` (5.18, Chamber-OS 112): the step has a problem — say, Save found errors on it. It keeps its place in the
930
+ * order and whether it can be clicked, shows the danger tone with an alert icon instead of the check or number, and
931
+ * adds "has errors" to its accessible name (in place of "completed"). */
932
+ status?: "error" | undefined;
917
933
  }
918
934
  /** Progress through a multi-step flow (wizard, checkout, booking). */
919
935
  export interface StepperProps {
@@ -924,7 +940,8 @@ export interface StepperProps {
924
940
  label?: string | undefined;
925
941
  /** Makes completed steps buttons that go back to them. Upcoming steps never are. */
926
942
  onStepClick?: ((id: string) => void) | undefined;
927
- /** Default `horizontal`. Below `sm` a horizontal stepper shows only the current step ("Step 2 of 4"). */
943
+ /** Default `horizontal`. Below `sm` a horizontal stepper shows only the current step ("Step 2 of 4"; "Step 2 of 4 —
944
+ * has errors" when that step has `status: 'error'`). */
928
945
  orientation?: "horizontal" | "vertical" | undefined;
929
946
  className?: string | undefined;
930
947
  }
@@ -1738,6 +1755,8 @@ export interface AuraStrings {
1738
1755
  progressReserved: (value: number, reserved: number, max: number) => string;
1739
1756
  /** The collapsed middle of a Breadcrumb (5.15). */
1740
1757
  breadcrumbMore: string;
1758
+ /** A Stepper step with `status: 'error'` (5.18). */
1759
+ stepError: string;
1741
1760
  }
1742
1761
  /** Built-in strings by locale. th and sv also ship as packs (`@jirawatpyk/aura-react/locales/th`, `/sv`, 5.9); in 6.0
1743
1762
  * only en stays built in and a Thai or Swedish app passes its pack to AuraProvider `strings`. */
package/dist/index.d.ts CHANGED
@@ -517,6 +517,10 @@ export interface SelectProps extends Omit<FieldProps, "label">, Omit<React$1.Sel
517
517
  /** Shown in fg-tertiary on the closed field until something is chosen; not listed as a choice. */
518
518
  placeholder?: string | undefined;
519
519
  icon?: IconInput | undefined;
520
+ /** 5.19 (Chamber-OS 114): a locked value. The field stays in the Tab order with `aria-readonly` and shows the chosen
521
+ * option on the read-only ground, with no chevron; no click or key opens the list. The value is still submitted
522
+ * with the form (unlike `disabled`). Before JavaScript runs, the other options from `options` are disabled. */
523
+ readOnly?: boolean | undefined;
520
524
  }
521
525
  /** A compact filter for FilterBar (5.12): "Status All ▾", as wide as its words. */
522
526
  export interface FilterSelectProps extends Omit<React$1.SelectHTMLAttributes<HTMLSelectElement>, "onChange" | "value" | "defaultValue" | "multiple" | "size" | "placeholder" | "required" | "children"> {
@@ -557,8 +561,16 @@ export interface SwitchProps {
557
561
  defaultChecked?: boolean | undefined;
558
562
  onChange?: ((checked: boolean) => void) | undefined;
559
563
  disabled?: boolean | undefined;
564
+ /** 5.19 (Chamber-OS 113): a locked value. The switch stays in the Tab order with `aria-readonly`, so its name,
565
+ * state and description are heard; click, Space and the row ignore it. Its colours stay (on reads as on). */
566
+ readOnly?: boolean | undefined;
567
+ /** 5.19: an icon at the end of the row, in fg-secondary and the field icon's size — `icon={<IconLock />}` or
568
+ * `icon="lock"` on a locked row. Hidden from screen readers; say what it means in the description. */
569
+ icon?: IconInput | undefined;
560
570
  /** Required when there is no visible label. */
561
571
  "aria-label"?: string | undefined;
572
+ /** 5.19: ids of more text that describes the switch (a lock note); merged with `description`'s. */
573
+ "aria-describedby"?: string | undefined;
562
574
  id?: string | undefined;
563
575
  className?: string | undefined;
564
576
  }
@@ -914,6 +926,10 @@ export interface StepItem {
914
926
  label: React$1.ReactNode;
915
927
  /** Short line under the label, e.g. what the step asks for. */
916
928
  description?: React$1.ReactNode | undefined;
929
+ /** `'error'` (5.18, Chamber-OS 112): the step has a problem — say, Save found errors on it. It keeps its place in the
930
+ * order and whether it can be clicked, shows the danger tone with an alert icon instead of the check or number, and
931
+ * adds "has errors" to its accessible name (in place of "completed"). */
932
+ status?: "error" | undefined;
917
933
  }
918
934
  /** Progress through a multi-step flow (wizard, checkout, booking). */
919
935
  export interface StepperProps {
@@ -924,7 +940,8 @@ export interface StepperProps {
924
940
  label?: string | undefined;
925
941
  /** Makes completed steps buttons that go back to them. Upcoming steps never are. */
926
942
  onStepClick?: ((id: string) => void) | undefined;
927
- /** Default `horizontal`. Below `sm` a horizontal stepper shows only the current step ("Step 2 of 4"). */
943
+ /** Default `horizontal`. Below `sm` a horizontal stepper shows only the current step ("Step 2 of 4"; "Step 2 of 4 —
944
+ * has errors" when that step has `status: 'error'`). */
928
945
  orientation?: "horizontal" | "vertical" | undefined;
929
946
  className?: string | undefined;
930
947
  }
@@ -1738,6 +1755,8 @@ export interface AuraStrings {
1738
1755
  progressReserved: (value: number, reserved: number, max: number) => string;
1739
1756
  /** The collapsed middle of a Breadcrumb (5.15). */
1740
1757
  breadcrumbMore: string;
1758
+ /** A Stepper step with `status: 'error'` (5.18). */
1759
+ stepError: string;
1741
1760
  }
1742
1761
  /** Built-in strings by locale. th and sv also ship as packs (`@jirawatpyk/aura-react/locales/th`, `/sv`, 5.9); in 6.0
1743
1762
  * only en stays built in and a Thai or Swedish app passes its pack to AuraProvider `strings`. */
@@ -147,6 +147,7 @@ var sv = {
147
147
  return nsv(v) + " av " + nsv(max) + " anv\xE4nda, " + nsv(r) + " reserverade";
148
148
  },
149
149
  breadcrumbMore: "Visa hela s\xF6kv\xE4gen",
150
+ stepError: "har fel",
150
151
  accepts: function(list, max) {
151
152
  return [list, max && "h\xF6gst " + max + " per fil"].filter(Boolean).join(", ");
152
153
  }
@@ -87,6 +87,8 @@ export interface AuraStrings {
87
87
  progressReserved: (value: number, reserved: number, max: number) => string;
88
88
  /** The collapsed middle of a Breadcrumb (5.15). */
89
89
  breadcrumbMore: string;
90
+ /** A Stepper step with `status: 'error'` (5.18). */
91
+ stepError: string;
90
92
  }
91
93
  /** Built-in strings by locale. th and sv also ship as packs (`@jirawatpyk/aura-react/locales/th`, `/sv`, 5.9); in 6.0
92
94
  * only en stays built in and a Thai or Swedish app passes its pack to AuraProvider `strings`. */
@@ -87,6 +87,8 @@ export interface AuraStrings {
87
87
  progressReserved: (value: number, reserved: number, max: number) => string;
88
88
  /** The collapsed middle of a Breadcrumb (5.15). */
89
89
  breadcrumbMore: string;
90
+ /** A Stepper step with `status: 'error'` (5.18). */
91
+ stepError: string;
90
92
  }
91
93
  /** Built-in strings by locale. th and sv also ship as packs (`@jirawatpyk/aura-react/locales/th`, `/sv`, 5.9); in 6.0
92
94
  * only en stays built in and a Thai or Swedish app passes its pack to AuraProvider `strings`. */
@@ -147,6 +147,7 @@ var th = {
147
147
  return "\u0E43\u0E0A\u0E49\u0E41\u0E25\u0E49\u0E27 " + n(v) + " \u0E08\u0E32\u0E01 " + n(max) + " \u0E08\u0E2D\u0E07\u0E44\u0E27\u0E49 " + n(r);
148
148
  },
149
149
  breadcrumbMore: "\u0E41\u0E2A\u0E14\u0E07\u0E40\u0E2A\u0E49\u0E19\u0E17\u0E32\u0E07\u0E17\u0E31\u0E49\u0E07\u0E2B\u0E21\u0E14",
150
+ stepError: "\u0E21\u0E35\u0E02\u0E49\u0E2D\u0E1C\u0E34\u0E14\u0E1E\u0E25\u0E32\u0E14",
150
151
  accepts: function(list, max) {
151
152
  return [list, max && "\u0E44\u0E21\u0E48\u0E40\u0E01\u0E34\u0E19 " + max + " \u0E15\u0E48\u0E2D\u0E44\u0E1F\u0E25\u0E4C"].filter(Boolean).join(" \xB7 ");
152
153
  }
@@ -87,6 +87,8 @@ export interface AuraStrings {
87
87
  progressReserved: (value: number, reserved: number, max: number) => string;
88
88
  /** The collapsed middle of a Breadcrumb (5.15). */
89
89
  breadcrumbMore: string;
90
+ /** A Stepper step with `status: 'error'` (5.18). */
91
+ stepError: string;
90
92
  }
91
93
  /** Built-in strings by locale. th and sv also ship as packs (`@jirawatpyk/aura-react/locales/th`, `/sv`, 5.9); in 6.0
92
94
  * only en stays built in and a Thai or Swedish app passes its pack to AuraProvider `strings`. */
@@ -87,6 +87,8 @@ export interface AuraStrings {
87
87
  progressReserved: (value: number, reserved: number, max: number) => string;
88
88
  /** The collapsed middle of a Breadcrumb (5.15). */
89
89
  breadcrumbMore: string;
90
+ /** A Stepper step with `status: 'error'` (5.18). */
91
+ stepError: string;
90
92
  }
91
93
  /** Built-in strings by locale. th and sv also ship as packs (`@jirawatpyk/aura-react/locales/th`, `/sv`, 5.9); in 6.0
92
94
  * only en stays built in and a Thai or Swedish app passes its pack to AuraProvider `strings`. */
@@ -331,6 +331,7 @@ var th = {
331
331
  return "\u0E43\u0E0A\u0E49\u0E41\u0E25\u0E49\u0E27 " + n(v) + " \u0E08\u0E32\u0E01 " + n(max) + " \u0E08\u0E2D\u0E07\u0E44\u0E27\u0E49 " + n(r);
332
332
  },
333
333
  breadcrumbMore: "\u0E41\u0E2A\u0E14\u0E07\u0E40\u0E2A\u0E49\u0E19\u0E17\u0E32\u0E07\u0E17\u0E31\u0E49\u0E07\u0E2B\u0E21\u0E14",
334
+ stepError: "\u0E21\u0E35\u0E02\u0E49\u0E2D\u0E1C\u0E34\u0E14\u0E1E\u0E25\u0E32\u0E14",
334
335
  accepts: function(list, max) {
335
336
  return [list, max && "\u0E44\u0E21\u0E48\u0E40\u0E01\u0E34\u0E19 " + max + " \u0E15\u0E48\u0E2D\u0E44\u0E1F\u0E25\u0E4C"].filter(Boolean).join(" \xB7 ");
336
337
  }
@@ -461,6 +462,7 @@ var sv = {
461
462
  return nsv(v) + " av " + nsv(max) + " anv\xE4nda, " + nsv(r) + " reserverade";
462
463
  },
463
464
  breadcrumbMore: "Visa hela s\xF6kv\xE4gen",
465
+ stepError: "har fel",
464
466
  accepts: function(list, max) {
465
467
  return [list, max && "h\xF6gst " + max + " per fil"].filter(Boolean).join(", ");
466
468
  }
@@ -592,6 +594,7 @@ var STRINGS = {
592
594
  return n2(v) + " of " + n2(max) + " used, " + n2(r) + " reserved";
593
595
  },
594
596
  breadcrumbMore: "Show the full path",
597
+ stepError: "has errors",
595
598
  accepts: function(list, max) {
596
599
  return [list, max && "up to " + max + " each"].filter(Boolean).join(", ");
597
600
  }
@@ -306,6 +306,8 @@ export interface AuraStrings {
306
306
  progressReserved: (value: number, reserved: number, max: number) => string;
307
307
  /** The collapsed middle of a Breadcrumb (5.15). */
308
308
  breadcrumbMore: string;
309
+ /** A Stepper step with `status: 'error'` (5.18). */
310
+ stepError: string;
309
311
  }
310
312
  /** Built-in strings by locale. th and sv also ship as packs (`@jirawatpyk/aura-react/locales/th`, `/sv`, 5.9); in 6.0
311
313
  * only en stays built in and a Thai or Swedish app passes its pack to AuraProvider `strings`. */
@@ -306,6 +306,8 @@ export interface AuraStrings {
306
306
  progressReserved: (value: number, reserved: number, max: number) => string;
307
307
  /** The collapsed middle of a Breadcrumb (5.15). */
308
308
  breadcrumbMore: string;
309
+ /** A Stepper step with `status: 'error'` (5.18). */
310
+ stepError: string;
309
311
  }
310
312
  /** Built-in strings by locale. th and sv also ship as packs (`@jirawatpyk/aura-react/locales/th`, `/sv`, 5.9); in 6.0
311
313
  * only en stays built in and a Thai or Swedish app passes its pack to AuraProvider `strings`. */
@@ -272,6 +272,7 @@ var th = {
272
272
  return "\u0E43\u0E0A\u0E49\u0E41\u0E25\u0E49\u0E27 " + n(v) + " \u0E08\u0E32\u0E01 " + n(max) + " \u0E08\u0E2D\u0E07\u0E44\u0E27\u0E49 " + n(r);
273
273
  },
274
274
  breadcrumbMore: "\u0E41\u0E2A\u0E14\u0E07\u0E40\u0E2A\u0E49\u0E19\u0E17\u0E32\u0E07\u0E17\u0E31\u0E49\u0E07\u0E2B\u0E21\u0E14",
275
+ stepError: "\u0E21\u0E35\u0E02\u0E49\u0E2D\u0E1C\u0E34\u0E14\u0E1E\u0E25\u0E32\u0E14",
275
276
  accepts: function(list, max) {
276
277
  return [list, max && "\u0E44\u0E21\u0E48\u0E40\u0E01\u0E34\u0E19 " + max + " \u0E15\u0E48\u0E2D\u0E44\u0E1F\u0E25\u0E4C"].filter(Boolean).join(" \xB7 ");
277
278
  }
@@ -402,6 +403,7 @@ var sv = {
402
403
  return nsv(v) + " av " + nsv(max) + " anv\xE4nda, " + nsv(r) + " reserverade";
403
404
  },
404
405
  breadcrumbMore: "Visa hela s\xF6kv\xE4gen",
406
+ stepError: "har fel",
405
407
  accepts: function(list, max) {
406
408
  return [list, max && "h\xF6gst " + max + " per fil"].filter(Boolean).join(", ");
407
409
  }
@@ -533,6 +535,7 @@ var STRINGS = {
533
535
  return n2(v) + " of " + n2(max) + " used, " + n2(r) + " reserved";
534
536
  },
535
537
  breadcrumbMore: "Show the full path",
538
+ stepError: "has errors",
536
539
  accepts: function(list, max) {
537
540
  return [list, max && "up to " + max + " each"].filter(Boolean).join(", ");
538
541
  }
package/dist/styles.css CHANGED
@@ -487,6 +487,10 @@ a.aura-icon-btn { text-decoration: none; }
487
487
  .aura-select__value.is-placeholder { color: var(--aura-fg-tertiary); }
488
488
  .aura-select__group { padding: var(--aura-space-2) var(--aura-space-2) var(--aura-space-1); font-family: var(--font-mono); font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em; color: var(--aura-fg-tertiary); }
489
489
  .aura-select.is-open .aura-select__chevron { transform: rotate(180deg); }
490
+ /* 5.19 (Chamber-OS 114): a read-only Select looks like a read-only TextField — the same ground, no chevron (it doesn't
491
+ * open), no pointer; the value keeps full contrast. */
492
+ .aura-select.is-readonly { background: var(--aura-bg-input-disabled); }
493
+ .aura-select.is-readonly .aura-input__control { cursor: default; padding-right: var(--aura-space-3); }
490
494
 
491
495
  .aura-radio-group > legend { margin-bottom: 6px; }
492
496
  .aura-radio-group__list { display: flex; flex-direction: column; gap: var(--aura-space-3); }
@@ -528,6 +532,10 @@ a.aura-icon-btn { text-decoration: none; }
528
532
  .aura-switch.is-on .aura-switch__thumb { transform: translateX(16px); background: var(--aura-button-primary-fg); }
529
533
  .aura-switch:focus-visible { outline: var(--aura-focus-ring-width) solid var(--aura-focus-ring); outline-offset: var(--aura-focus-ring-offset); }
530
534
  .aura-switch:disabled { cursor: not-allowed; }
535
+ /* 5.19 (Chamber-OS 113): a read-only switch keeps its colours (a locked "on" reads as on at full contrast) and stays
536
+ * focusable; only the pointer says it won't change. The row's icon (a lock) sits at its end, like a field's icon. */
537
+ .aura-switch-row.is-readonly, .aura-switch-row.is-readonly .aura-switch, .aura-switch-row.is-readonly .aura-choice__label { cursor: default; }
538
+ .aura-switch-row__icon { flex: none; margin-inline-start: auto; margin-top: 2px; width: var(--aura-icon-sm); height: var(--aura-icon-sm); color: var(--aura-fg-secondary); }
531
539
 
532
540
  /* ---------- Alerts & toasts ---------- */
533
541
  .aura-alert {
@@ -1294,6 +1302,10 @@ button.aura-stepper__step:hover .aura-stepper__label { text-decoration: underlin
1294
1302
  .aura-stepper__compact { display: none; margin: var(--aura-space-2) 0 0; font-size: 13px; }
1295
1303
  .aura-stepper__count { color: var(--aura-fg-secondary); margin-right: var(--aura-space-2); }
1296
1304
  .aura-stepper__compact-label { font-weight: 600; }
1305
+ /* 5.18 (Chamber-OS 112): a step with errors — the danger fill and an alert icon; outlined when it is the current step. */
1306
+ .aura-stepper__item.is-error .aura-stepper__marker { background: var(--aura-button-danger-bg); border-color: var(--aura-button-danger-bg); color: var(--aura-button-danger-fg); }
1307
+ .aura-stepper__item.is-current.is-error .aura-stepper__marker { background: var(--aura-bg-surface); border: 2px solid var(--aura-fg-danger); color: var(--aura-fg-danger); }
1308
+ .aura-stepper__item.is-error .aura-stepper__label, .aura-stepper__count-error { color: var(--aura-fg-danger); }
1297
1309
 
1298
1310
  /* ---------- SegmentedControl ---------- */
1299
1311
  .aura-segmented {
@@ -1410,7 +1422,7 @@ button.aura-stepper__step:hover .aura-stepper__label { text-decoration: underlin
1410
1422
  padding-block: calc((var(--aura-touch-target) - 20px) / 2);
1411
1423
  }
1412
1424
  .aura-check--labelled .aura-check__input { top: calc((var(--aura-touch-target) - 20px) / 2 + 3px); }
1413
- .aura-switch-row:not(.is-disabled) { cursor: pointer; }
1425
+ .aura-switch-row:not(.is-disabled):not(.is-readonly) { cursor: pointer; }
1414
1426
  .aura-radio-group__list { gap: 0; }
1415
1427
  .aura-radio-group__list.is-horizontal { gap: 0 var(--aura-space-6); }
1416
1428
  }
@@ -489,6 +489,10 @@ a.aura-icon-btn { text-decoration: none; }
489
489
  .aura-select__value.is-placeholder { color: var(--aura-fg-tertiary); }
490
490
  .aura-select__group { padding: var(--aura-space-2) var(--aura-space-2) var(--aura-space-1); font-family: var(--font-mono); font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em; color: var(--aura-fg-tertiary); }
491
491
  .aura-select.is-open .aura-select__chevron { transform: rotate(180deg); }
492
+ /* 5.19 (Chamber-OS 114): a read-only Select looks like a read-only TextField — the same ground, no chevron (it doesn't
493
+ * open), no pointer; the value keeps full contrast. */
494
+ .aura-select.is-readonly { background: var(--aura-bg-input-disabled); }
495
+ .aura-select.is-readonly .aura-input__control { cursor: default; padding-right: var(--aura-space-3); }
492
496
 
493
497
  .aura-radio-group > legend { margin-bottom: 6px; }
494
498
  .aura-radio-group__list { display: flex; flex-direction: column; gap: var(--aura-space-3); }
@@ -530,6 +534,10 @@ a.aura-icon-btn { text-decoration: none; }
530
534
  .aura-switch.is-on .aura-switch__thumb { transform: translateX(16px); background: var(--aura-button-primary-fg); }
531
535
  .aura-switch:focus-visible { outline: var(--aura-focus-ring-width) solid var(--aura-focus-ring); outline-offset: var(--aura-focus-ring-offset); }
532
536
  .aura-switch:disabled { cursor: not-allowed; }
537
+ /* 5.19 (Chamber-OS 113): a read-only switch keeps its colours (a locked "on" reads as on at full contrast) and stays
538
+ * focusable; only the pointer says it won't change. The row's icon (a lock) sits at its end, like a field's icon. */
539
+ .aura-switch-row.is-readonly, .aura-switch-row.is-readonly .aura-switch, .aura-switch-row.is-readonly .aura-choice__label { cursor: default; }
540
+ .aura-switch-row__icon { flex: none; margin-inline-start: auto; margin-top: 2px; width: var(--aura-icon-sm); height: var(--aura-icon-sm); color: var(--aura-fg-secondary); }
533
541
 
534
542
  /* ---------- Alerts & toasts ---------- */
535
543
  .aura-alert {
@@ -1296,6 +1304,10 @@ button.aura-stepper__step:hover .aura-stepper__label { text-decoration: underlin
1296
1304
  .aura-stepper__compact { display: none; margin: var(--aura-space-2) 0 0; font-size: 13px; }
1297
1305
  .aura-stepper__count { color: var(--aura-fg-secondary); margin-right: var(--aura-space-2); }
1298
1306
  .aura-stepper__compact-label { font-weight: 600; }
1307
+ /* 5.18 (Chamber-OS 112): a step with errors — the danger fill and an alert icon; outlined when it is the current step. */
1308
+ .aura-stepper__item.is-error .aura-stepper__marker { background: var(--aura-button-danger-bg); border-color: var(--aura-button-danger-bg); color: var(--aura-button-danger-fg); }
1309
+ .aura-stepper__item.is-current.is-error .aura-stepper__marker { background: var(--aura-bg-surface); border: 2px solid var(--aura-fg-danger); color: var(--aura-fg-danger); }
1310
+ .aura-stepper__item.is-error .aura-stepper__label, .aura-stepper__count-error { color: var(--aura-fg-danger); }
1299
1311
 
1300
1312
  /* ---------- SegmentedControl ---------- */
1301
1313
  .aura-segmented {
@@ -1412,7 +1424,7 @@ button.aura-stepper__step:hover .aura-stepper__label { text-decoration: underlin
1412
1424
  padding-block: calc((var(--aura-touch-target) - 20px) / 2);
1413
1425
  }
1414
1426
  .aura-check--labelled .aura-check__input { top: calc((var(--aura-touch-target) - 20px) / 2 + 3px); }
1415
- .aura-switch-row:not(.is-disabled) { cursor: pointer; }
1427
+ .aura-switch-row:not(.is-disabled):not(.is-readonly) { cursor: pointer; }
1416
1428
  .aura-radio-group__list { gap: 0; }
1417
1429
  .aura-radio-group__list.is-horizontal { gap: 0 var(--aura-space-6); }
1418
1430
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jirawatpyk/aura-react",
3
- "version": "5.17.0",
3
+ "version": "5.19.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",