@jirawatpyk/aura-react 5.49.0 → 5.50.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,15 @@ 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.50 — Chamber-OS addendum 57 (#156)
190
+
191
+ - **`invalid` on TextField and Textarea** (and PasswordField, which is a TextField; `Field` for a custom control): marks the value invalid without AURA's message — the danger edge (`--aura-border-danger`, light and dark, also on hover and focus) and `aria-invalid="true"` on the control, no error line, and `aria-describedby` as it would be without it (the addon, the hint, then your own ids). For a page that explains the problem itself — a live contrast readout, a reason line a disabled Save points at. Point `aria-describedby` at that explanation, or a screen reader hears "invalid" with no reason. `error` still wins: its message replaces the hint and its id the hint's. A disabled field keeps its disabled edge.
192
+ - **Your own `aria-invalid="true"` is enough too**: a field whose input or textarea carries it gets the same edge, so a custom control inside `Field` (with `invalid` on the Field for the class) or a form library that sets it needs nothing else.
193
+
194
+ ```jsx
195
+ <TextField label="Primary colour" hint="A hex value." value={colour} onChange={…} invalid={!meetsAa} aria-describedby="contrast-readout" />
196
+ ```
197
+
189
198
  ## 5.49 — Chamber-OS addendum 56 (#155)
190
199
 
191
200
  - **The short-screen threshold follows the browser's text size**: the height at which bottom bars stop pinning (5.48, #154) is `30rem`, not `480px`. In a media query `rem` is the browser's default font size (Chrome, Edge, Firefox; not iOS Dynamic Type or Safari's text size), not the page's `html` font size: 480px at the default 16px (nothing changes), 720px at 24px (an iPhone SE counts as short, an 844px phone keeps the bar), 960px at 32px (an 844px phone counts as short — it holds what a 422px screen holds at 16px). There the `actionsBarBelow` actions stay in the header, an ActionBar sits at the end of its container and AppShell's top bar scrolls away, as at 400% zoom. Changing the text size on an open page re-checks it (the bar-height tokens follow). The width breakpoints stay in px.
@@ -1992,7 +1992,12 @@ window.Aura = (() => {
1992
1992
  "div",
1993
1993
  {
1994
1994
  ref,
1995
- className: cx("aura-field", props.error && "is-invalid", props.disabled && "is-disabled", props.className)
1995
+ className: cx(
1996
+ "aura-field",
1997
+ (props.error || props.invalid) && "is-invalid",
1998
+ props.disabled && "is-disabled",
1999
+ props.className
2000
+ )
1996
2001
  },
1997
2002
  props.label ? h3(
1998
2003
  props.labelAs || "label",
@@ -2037,7 +2042,8 @@ window.Aura = (() => {
2037
2042
  "placeholder",
2038
2043
  "touchHeight",
2039
2044
  "labelAddon",
2040
- "labelAddonDescribes"
2045
+ "labelAddonDescribes",
2046
+ "invalid"
2041
2047
  ];
2042
2048
 
2043
2049
  // src/TextField.tsx
@@ -2052,6 +2058,7 @@ window.Aura = (() => {
2052
2058
  label: props.label,
2053
2059
  hint: props.hint,
2054
2060
  error: props.error,
2061
+ invalid: props.invalid,
2055
2062
  required: props.required,
2056
2063
  optional: props.optional,
2057
2064
  labelAddon: props.labelAddon,
@@ -2068,7 +2075,7 @@ window.Aura = (() => {
2068
2075
  className: "aura-input__control",
2069
2076
  placeholder: props.placeholder,
2070
2077
  required: props.required,
2071
- "aria-invalid": props.error ? true : rest["aria-invalid"],
2078
+ "aria-invalid": props.error || props.invalid ? true : rest["aria-invalid"],
2072
2079
  "aria-describedby": joinDescribedBy(id, props, rest["aria-describedby"])
2073
2080
  }
2074
2081
  ), props.suffix ? /* @__PURE__ */ React14.createElement("span", { className: "aura-input__suffix" }, props.suffix) : null)
@@ -2087,6 +2094,7 @@ window.Aura = (() => {
2087
2094
  label: props.label,
2088
2095
  hint: props.hint,
2089
2096
  error: props.error,
2097
+ invalid: props.invalid,
2090
2098
  required: props.required,
2091
2099
  optional: props.optional,
2092
2100
  labelAddon: props.labelAddon,
@@ -2103,7 +2111,7 @@ window.Aura = (() => {
2103
2111
  className: "aura-input aura-textarea",
2104
2112
  placeholder: props.placeholder,
2105
2113
  required: props.required,
2106
- "aria-invalid": props.error ? true : rest["aria-invalid"],
2114
+ "aria-invalid": props.error || props.invalid ? true : rest["aria-invalid"],
2107
2115
  "aria-describedby": joinDescribedBy(id, props, rest["aria-describedby"])
2108
2116
  }
2109
2117
  )
@@ -1974,7 +1974,12 @@ var Field = React13.forwardRef(function Field2(props, ref) {
1974
1974
  "div",
1975
1975
  {
1976
1976
  ref,
1977
- className: cx("aura-field", props.error && "is-invalid", props.disabled && "is-disabled", props.className)
1977
+ className: cx(
1978
+ "aura-field",
1979
+ (props.error || props.invalid) && "is-invalid",
1980
+ props.disabled && "is-disabled",
1981
+ props.className
1982
+ )
1978
1983
  },
1979
1984
  props.label ? h3(
1980
1985
  props.labelAs || "label",
@@ -2019,7 +2024,8 @@ var FIELD_KEYS = [
2019
2024
  "placeholder",
2020
2025
  "touchHeight",
2021
2026
  "labelAddon",
2022
- "labelAddonDescribes"
2027
+ "labelAddonDescribes",
2028
+ "invalid"
2023
2029
  ];
2024
2030
 
2025
2031
  // src/TextField.tsx
@@ -2034,6 +2040,7 @@ var TextField = React14.forwardRef(function TextField2(props, ref) {
2034
2040
  label: props.label,
2035
2041
  hint: props.hint,
2036
2042
  error: props.error,
2043
+ invalid: props.invalid,
2037
2044
  required: props.required,
2038
2045
  optional: props.optional,
2039
2046
  labelAddon: props.labelAddon,
@@ -2050,7 +2057,7 @@ var TextField = React14.forwardRef(function TextField2(props, ref) {
2050
2057
  className: "aura-input__control",
2051
2058
  placeholder: props.placeholder,
2052
2059
  required: props.required,
2053
- "aria-invalid": props.error ? true : rest["aria-invalid"],
2060
+ "aria-invalid": props.error || props.invalid ? true : rest["aria-invalid"],
2054
2061
  "aria-describedby": joinDescribedBy(id, props, rest["aria-describedby"])
2055
2062
  }
2056
2063
  ), props.suffix ? /* @__PURE__ */ React14.createElement("span", { className: "aura-input__suffix" }, props.suffix) : null)
@@ -2069,6 +2076,7 @@ var Textarea = React15.forwardRef(function Textarea2(props, ref) {
2069
2076
  label: props.label,
2070
2077
  hint: props.hint,
2071
2078
  error: props.error,
2079
+ invalid: props.invalid,
2072
2080
  required: props.required,
2073
2081
  optional: props.optional,
2074
2082
  labelAddon: props.labelAddon,
@@ -2085,7 +2093,7 @@ var Textarea = React15.forwardRef(function Textarea2(props, ref) {
2085
2093
  className: "aura-input aura-textarea",
2086
2094
  placeholder: props.placeholder,
2087
2095
  required: props.required,
2088
- "aria-invalid": props.error ? true : rest["aria-invalid"],
2096
+ "aria-invalid": props.error || props.invalid ? true : rest["aria-invalid"],
2089
2097
  "aria-describedby": joinDescribedBy(id, props, rest["aria-describedby"])
2090
2098
  }
2091
2099
  )
package/dist/esm/Field.js CHANGED
@@ -11,7 +11,12 @@ const Field = React.forwardRef(function Field2(props, ref) {
11
11
  "div",
12
12
  {
13
13
  ref,
14
- className: cx("aura-field", props.error && "is-invalid", props.disabled && "is-disabled", props.className)
14
+ className: cx(
15
+ "aura-field",
16
+ (props.error || props.invalid) && "is-invalid",
17
+ props.disabled && "is-disabled",
18
+ props.className
19
+ )
15
20
  },
16
21
  props.label ? h(
17
22
  props.labelAs || "label",
@@ -56,7 +61,8 @@ const FIELD_KEYS = [
56
61
  "placeholder",
57
62
  "touchHeight",
58
63
  "labelAddon",
59
- "labelAddonDescribes"
64
+ "labelAddonDescribes",
65
+ "invalid"
60
66
  ];
61
67
  export {
62
68
  FIELD_KEYS,
@@ -13,6 +13,7 @@ const TextField = React.forwardRef(function TextField2(props, ref) {
13
13
  label: props.label,
14
14
  hint: props.hint,
15
15
  error: props.error,
16
+ invalid: props.invalid,
16
17
  required: props.required,
17
18
  optional: props.optional,
18
19
  labelAddon: props.labelAddon,
@@ -29,7 +30,7 @@ const TextField = React.forwardRef(function TextField2(props, ref) {
29
30
  className: "aura-input__control",
30
31
  placeholder: props.placeholder,
31
32
  required: props.required,
32
- "aria-invalid": props.error ? true : rest["aria-invalid"],
33
+ "aria-invalid": props.error || props.invalid ? true : rest["aria-invalid"],
33
34
  "aria-describedby": joinDescribedBy(id, props, rest["aria-describedby"])
34
35
  }
35
36
  ), props.suffix ? /* @__PURE__ */ React.createElement("span", { className: "aura-input__suffix" }, props.suffix) : null)
@@ -12,6 +12,7 @@ const Textarea = React.forwardRef(function Textarea2(props, ref) {
12
12
  label: props.label,
13
13
  hint: props.hint,
14
14
  error: props.error,
15
+ invalid: props.invalid,
15
16
  required: props.required,
16
17
  optional: props.optional,
17
18
  labelAddon: props.labelAddon,
@@ -28,7 +29,7 @@ const Textarea = React.forwardRef(function Textarea2(props, ref) {
28
29
  className: "aura-input aura-textarea",
29
30
  placeholder: props.placeholder,
30
31
  required: props.required,
31
- "aria-invalid": props.error ? true : rest["aria-invalid"],
32
+ "aria-invalid": props.error || props.invalid ? true : rest["aria-invalid"],
32
33
  "aria-describedby": joinDescribedBy(id, props, rest["aria-describedby"])
33
34
  }
34
35
  )
package/dist/index.d.cts CHANGED
@@ -419,6 +419,10 @@ export interface FieldProps {
419
419
  optional?: boolean | undefined;
420
420
  }
421
421
  export interface TextFieldProps extends FieldProps, Omit<React$1.InputHTMLAttributes<HTMLInputElement>, "required"> {
422
+ /** 5.50 (Chamber-OS 156): marks the value invalid — the danger border and `aria-invalid="true"` — without AURA's
423
+ * message: no error line, and `aria-describedby` stays as it is (the hint, the addon, your own ids). For a page that
424
+ * explains the problem itself (a live readout, a reason a button points at). `error` still wins when set. */
425
+ invalid?: boolean | undefined;
422
426
  /** Leading icon inside the field. */
423
427
  icon?: IconInput | undefined;
424
428
  /** Trailing unit, e.g. "THB". */
@@ -570,6 +574,10 @@ export interface CommandProps {
570
574
  className?: string | undefined;
571
575
  }
572
576
  export interface TextareaProps extends FieldProps, Omit<React$1.TextareaHTMLAttributes<HTMLTextAreaElement>, "required"> {
577
+ /** 5.50 (Chamber-OS 156): marks the value invalid — the danger border and `aria-invalid="true"` — without AURA's
578
+ * message: no error line, and `aria-describedby` stays as it is (the hint, the addon, your own ids). For a page that
579
+ * explains the problem itself (a live readout, a reason a button points at). `error` still wins when set. */
580
+ invalid?: boolean | undefined;
573
581
  /** 5.31 (Chamber-OS addendum 41): a node between the label and the box — e.g. the phrase to type, with a copy
574
582
  * button, on a typed confirmation. The hint and error stay below. Its box's id (`{id}-addon`) comes first in the
575
583
  * input's `aria-describedby`, so its text is read after the label; keep any button names in it short. */
@@ -1798,6 +1806,9 @@ export interface FieldComponentProps extends Omit<FieldPropsPublic, "label"> {
1798
1806
  labelAs?: string | undefined;
1799
1807
  labelId?: string | undefined;
1800
1808
  disabled?: boolean | undefined;
1809
+ /** 5.50 (Chamber-OS 156): the danger look without a message (`is-invalid`, no error line). Set `aria-invalid` on
1810
+ * your control yourself; `error` still renders its message. */
1811
+ invalid?: boolean | undefined;
1801
1812
  className?: string | undefined;
1802
1813
  }
1803
1814
  export declare const Field: React$1.ForwardRefExoticComponent<FieldComponentProps & React$1.RefAttributes<HTMLDivElement>>;
package/dist/index.d.ts CHANGED
@@ -419,6 +419,10 @@ export interface FieldProps {
419
419
  optional?: boolean | undefined;
420
420
  }
421
421
  export interface TextFieldProps extends FieldProps, Omit<React$1.InputHTMLAttributes<HTMLInputElement>, "required"> {
422
+ /** 5.50 (Chamber-OS 156): marks the value invalid — the danger border and `aria-invalid="true"` — without AURA's
423
+ * message: no error line, and `aria-describedby` stays as it is (the hint, the addon, your own ids). For a page that
424
+ * explains the problem itself (a live readout, a reason a button points at). `error` still wins when set. */
425
+ invalid?: boolean | undefined;
422
426
  /** Leading icon inside the field. */
423
427
  icon?: IconInput | undefined;
424
428
  /** Trailing unit, e.g. "THB". */
@@ -570,6 +574,10 @@ export interface CommandProps {
570
574
  className?: string | undefined;
571
575
  }
572
576
  export interface TextareaProps extends FieldProps, Omit<React$1.TextareaHTMLAttributes<HTMLTextAreaElement>, "required"> {
577
+ /** 5.50 (Chamber-OS 156): marks the value invalid — the danger border and `aria-invalid="true"` — without AURA's
578
+ * message: no error line, and `aria-describedby` stays as it is (the hint, the addon, your own ids). For a page that
579
+ * explains the problem itself (a live readout, a reason a button points at). `error` still wins when set. */
580
+ invalid?: boolean | undefined;
573
581
  /** 5.31 (Chamber-OS addendum 41): a node between the label and the box — e.g. the phrase to type, with a copy
574
582
  * button, on a typed confirmation. The hint and error stay below. Its box's id (`{id}-addon`) comes first in the
575
583
  * input's `aria-describedby`, so its text is read after the label; keep any button names in it short. */
@@ -1798,6 +1806,9 @@ export interface FieldComponentProps extends Omit<FieldPropsPublic, "label"> {
1798
1806
  labelAs?: string | undefined;
1799
1807
  labelId?: string | undefined;
1800
1808
  disabled?: boolean | undefined;
1809
+ /** 5.50 (Chamber-OS 156): the danger look without a message (`is-invalid`, no error line). Set `aria-invalid` on
1810
+ * your control yourself; `error` still renders its message. */
1811
+ invalid?: boolean | undefined;
1801
1812
  className?: string | undefined;
1802
1813
  }
1803
1814
  export declare const Field: React$1.ForwardRefExoticComponent<FieldComponentProps & React$1.RefAttributes<HTMLDivElement>>;
package/dist/styles.css CHANGED
@@ -528,6 +528,10 @@ a.aura-icon-btn { text-decoration: none; }
528
528
  .aura-input__icon { position: absolute; left: var(--aura-space-3); color: var(--aura-fg-secondary); pointer-events: none; }
529
529
  .aura-input__suffix { padding: 0 var(--aura-space-3) 0 0; color: var(--aura-fg-secondary); font-size: max(13px, .8125rem); white-space: nowrap; }
530
530
  .aura-field.is-invalid .aura-input, .aura-field.is-invalid .aura-textarea { border-color: var(--aura-border-danger); }
531
+ /* 5.50 (Chamber-OS 156): a control the page marks aria-invalid="true" itself gets the same edge, message or not (a
532
+ * disabled field keeps its disabled edge, as with is-invalid). */
533
+ .aura-field:not(.is-disabled) .aura-input:has(> .aura-input__control[aria-invalid="true"]),
534
+ .aura-field:not(.is-disabled) .aura-textarea[aria-invalid="true"] { border-color: var(--aura-border-danger); }
531
535
  .aura-field.is-disabled .aura-input { background: var(--aura-bg-input-disabled); color: var(--aura-fg-secondary); border-color: var(--aura-border-default); }
532
536
  .aura-field.is-disabled .aura-input__control { cursor: not-allowed; }
533
537
  /* A read-only text field. Only an input: `:read-only` also matches any element that isn't editable — a
@@ -530,6 +530,10 @@ a.aura-icon-btn { text-decoration: none; }
530
530
  .aura-input__icon { position: absolute; left: var(--aura-space-3); color: var(--aura-fg-secondary); pointer-events: none; }
531
531
  .aura-input__suffix { padding: 0 var(--aura-space-3) 0 0; color: var(--aura-fg-secondary); font-size: max(13px, .8125rem); white-space: nowrap; }
532
532
  .aura-field.is-invalid .aura-input, .aura-field.is-invalid .aura-textarea { border-color: var(--aura-border-danger); }
533
+ /* 5.50 (Chamber-OS 156): a control the page marks aria-invalid="true" itself gets the same edge, message or not (a
534
+ * disabled field keeps its disabled edge, as with is-invalid). */
535
+ .aura-field:not(.is-disabled) .aura-input:has(> .aura-input__control[aria-invalid="true"]),
536
+ .aura-field:not(.is-disabled) .aura-textarea[aria-invalid="true"] { border-color: var(--aura-border-danger); }
533
537
  .aura-field.is-disabled .aura-input { background: var(--aura-bg-input-disabled); color: var(--aura-fg-secondary); border-color: var(--aura-border-default); }
534
538
  .aura-field.is-disabled .aura-input__control { cursor: not-allowed; }
535
539
  /* A read-only text field. Only an input: `:read-only` also matches any element that isn't editable — a
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jirawatpyk/aura-react",
3
- "version": "5.49.0",
3
+ "version": "5.50.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",