@jirawatpyk/aura-react 5.15.0 → 5.16.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -47,6 +47,15 @@ export interface ButtonLinkProps extends Omit<React$1.AnchorHTMLAttributes<HTMLA
47
47
  /** A router's link to render instead of `<a>`, e.g. `Link` from `next/link` (client-side navigation). It gets `href`, `className`, the children and the ref. */
48
48
  linkComponent?: React$1.ElementType | undefined;
49
49
  }
50
+ /** How a DataTable selection changed: the second argument of `onSelectionChange` (5.16). */
51
+ export interface DataTableSelectionChange {
52
+ /** The row whose box or Space made the change; null for select-all and `sync`. */
53
+ key: string | number | null;
54
+ shiftKey: boolean;
55
+ source: "click" | "keyboard" | "all" | "sync";
56
+ /** With `rangeSelect`, the keys a Shift range set (the row's own key included). */
57
+ range?: Array<string | number> | undefined;
58
+ }
50
59
  /** What DataTable's onStateChange reports: the sort and the 1-based page, together. */
51
60
  export interface DataTableState {
52
61
  sort: DataTableSort | null;
@@ -149,7 +158,13 @@ export interface DataTableProps<Row extends Record<string, any> = Record<string,
149
158
  selected?: Array<string | number> | undefined;
150
159
  /** Initial selection when uncontrolled. */
151
160
  defaultSelected?: Array<string | number> | undefined;
152
- onSelectionChange?: ((keys: Array<string | number>) => void) | undefined;
161
+ /** The new keys, and how they changed (5.16, Chamber-OS 98): the row's `key` (null for select-all), whether Shift
162
+ * was held, and the `source` — `click` (a checkbox), `keyboard` (Space on a row), `all` (the select-all box) or
163
+ * `sync` (keys of rows `isRowSelectable` rejects, dropped). `range` lists the keys a Shift range changed. */
164
+ onSelectionChange?: ((keys: Array<string | number>, change: DataTableSelectionChange) => void) | undefined;
165
+ /** Shift-click a row's checkbox (or Shift+Space on a row) to set every selectable row from the last one changed to
166
+ * this one, on this page and in this order, to this row's new state (5.16, Chamber-OS 98). Default false. */
167
+ rangeSelect?: boolean | undefined;
153
168
  /** Shown when rows is empty. */
154
169
  empty?: DataTableEmpty | undefined;
155
170
  /** A totals row under the body (4.19): content per column key, e.g. `{ id: 'Total', vat: '7,000.00', total: '107,000.00' }`.
@@ -606,9 +621,22 @@ export interface FieldPropsPublic {
606
621
  required?: boolean | undefined;
607
622
  optional?: boolean | undefined;
608
623
  }
609
- export interface DialogProps {
610
- open: boolean;
611
- onClose: () => void;
624
+ /** `id`, `data-*`, `aria-*`, `style`, `lang` and `dir` go on the `.aura-dialog` panel (5.16, Chamber-OS 101). */
625
+ export interface DialogProps extends Pick<React$1.HTMLAttributes<HTMLDivElement>, "id" | "style" | "lang" | "dir">, React$1.AriaAttributes, DataAttributes {
626
+ /** Controlled open state. Leave it out with a `trigger` and the Dialog opens and closes itself (5.16). */
627
+ open?: boolean | undefined;
628
+ onClose?: (() => void) | undefined;
629
+ /** The button that opens it (5.16, Chamber-OS 101): rendered in place, with `aria-haspopup="dialog"`,
630
+ * `aria-expanded` and a click that opens the Dialog (its own onClick runs first; `preventDefault()` keeps it shut).
631
+ * Focus returns to it on close. */
632
+ trigger?: React$1.ReactElement | undefined;
633
+ /** Called when the trigger opens it (5.16). */
634
+ onOpen?: (() => void) | undefined;
635
+ /** Where focus goes when it closes — by a button, Escape or the scrim — instead of back to what opened it: the
636
+ * row a save just created (5.16). A ref, or a function read at close; null falls back to the opener. */
637
+ finalFocus?: React$1.RefObject<HTMLElement | null> | (() => HTMLElement | null) | undefined;
638
+ /** Runs once after it has closed and its panel has left the page (5.16) — reset a selection there. */
639
+ onCloseComplete?: (() => void) | undefined;
612
640
  title: React$1.ReactNode;
613
641
  description?: React$1.ReactNode | undefined;
614
642
  children?: React$1.ReactNode | undefined;
@@ -706,6 +734,11 @@ export interface NavItem {
706
734
  children?: NavItem[] | undefined;
707
735
  /** Start open. A group also opens by itself when one of its items is the current one. */
708
736
  defaultOpen?: boolean | undefined;
737
+ /** Runs when the item is chosen (5.16, Chamber-OS 95). */
738
+ onSelect?: (() => void) | undefined;
739
+ /** `false` makes an action row — Sign out, Collapse sidebar: it looks like the other rows and runs `onSelect`,
740
+ * but never becomes the current item (no `aria-current`, no `onChange`). Default true. (5.16) */
741
+ selectable?: boolean | undefined;
709
742
  }
710
743
  export interface SideNavProps {
711
744
  sections?: Array<{
@@ -731,6 +764,15 @@ export interface SideNavProps {
731
764
  onCollapsedChange?: ((collapsed: boolean) => void) | undefined;
732
765
  /** Adds a collapse / expand button at the bottom. AppShell hides it in its phone drawer. */
733
766
  collapsible?: boolean | undefined;
767
+ /** Called with the item's id when an action row (`selectable: false`) is chosen, after its `onSelect` (5.16).
768
+ * AppShell uses it to close its phone drawer. */
769
+ onAction?: ((id: string) => void) | undefined;
770
+ /** How `collapsible` shows its toggle: `icon` (default, an IconButton) or `row` — a labelled nav row
771
+ * ("Collapse sidebar") with the rows' look, its icon alone in the rail (5.16, Chamber-OS 95). */
772
+ collapseToggle?: "icon" | "row" | undefined;
773
+ /** Where a closed group's chevron points: `down` (default; up when open) or `right` (down when open; left in
774
+ * right-to-left pages) (5.16, Chamber-OS 96). */
775
+ chevron?: "down" | "right" | undefined;
734
776
  /** The 1px right edge that divides the nav from the page. Default true; `false` when your layout draws its own
735
777
  * divider. (5.1) */
736
778
  bordered?: boolean | undefined;
@@ -790,7 +832,7 @@ export interface ComboboxOption {
790
832
  icon?: IconInput | undefined;
791
833
  }
792
834
  /** Combobox with `multiple`: pick any number; the picks show as removable chips in the field. */
793
- export interface ComboboxMultipleProps extends Omit<ComboboxProps, "value" | "defaultValue" | "onChange" | "clearable"> {
835
+ export interface ComboboxMultipleProps extends Omit<ComboboxProps, "value" | "defaultValue" | "onChange" | "clearable" | "allowCustomValue"> {
794
836
  multiple: true;
795
837
  /** Selected option values, in the order they were picked (controlled). */
796
838
  value?: string[] | undefined;
@@ -802,7 +844,8 @@ export interface ComboboxMultipleProps extends Omit<ComboboxProps, "value" | "de
802
844
  max?: number | undefined;
803
845
  }
804
846
  export interface ComboboxProps extends FieldProps {
805
- options: Array<ComboboxOption | string>;
847
+ /** The options. Optional with `groups` (5.16). */
848
+ options?: Array<ComboboxOption | string> | undefined;
806
849
  /** Selected option value (controlled). */
807
850
  value?: string | null | undefined;
808
851
  defaultValue?: string | null | undefined;
@@ -825,6 +868,17 @@ export interface ComboboxProps extends FieldProps {
825
868
  limit?: number | undefined;
826
869
  /** Replace the Thai-aware default filter (label, description, keywords). */
827
870
  filter?: ((option: ComboboxOption, query: string) => boolean) | undefined;
871
+ /** Options under headings, after any in `options` (5.16, Chamber-OS 105): `[{ label: 'Nordic and Thailand',
872
+ * options: [...] }]`. Each heading names its group for screen readers; filtering keeps a heading while any of its
873
+ * options match. */
874
+ groups?: Array<{
875
+ label: string;
876
+ options: Array<ComboboxOption | string>;
877
+ }> | undefined;
878
+ /** A typed value that isn't in the list is kept (5.16, Chamber-OS 105): Enter with no match highlighted, or leaving
879
+ * the field, makes the text the value (text matching an option's label picks that option). Clearing the text
880
+ * clears the value. Single value only. */
881
+ allowCustomValue?: boolean | undefined;
828
882
  className?: string | undefined;
829
883
  }
830
884
  /** Number input: thousands separators, +/− buttons, arrow keys, min/max/step, prefix/suffix (฿, %). */
@@ -973,6 +1027,10 @@ export type ButtonAttributes = React$1.ButtonHTMLAttributes<HTMLButtonElement> &
973
1027
  export interface DrawerProps extends Pick<React$1.HTMLAttributes<HTMLDivElement>, "id" | "style" | "lang" | "dir">, React$1.AriaAttributes, DataAttributes {
974
1028
  open: boolean;
975
1029
  onClose: () => void;
1030
+ /** As Dialog's (5.16): where focus goes when it closes, instead of back to what opened it. */
1031
+ finalFocus?: React$1.RefObject<HTMLElement | null> | (() => HTMLElement | null) | undefined;
1032
+ /** As Dialog's (5.16): runs once after it has closed and left the page. */
1033
+ onCloseComplete?: (() => void) | undefined;
976
1034
  /** Default `right`. */
977
1035
  side?: "right" | "left" | undefined;
978
1036
  /** sm 360 · md 480 (default) · lg 640 · nav = SideNav width. Full width below 640px. */
@@ -1509,6 +1567,10 @@ export declare const FormErrorSummary: React$1.ForwardRefExoticComponent<FormErr
1509
1567
  export declare const FilterBar: React$1.ForwardRefExoticComponent<FilterBarProps & React$1.RefAttributes<HTMLDivElement>>;
1510
1568
  export declare function Command(props: CommandProps): React$1.ReactElement | null;
1511
1569
  export declare const Tooltip: React$1.ForwardRefExoticComponent<TooltipProps & React$1.RefAttributes<HTMLSpanElement>>;
1570
+ /** Inside a Dialog's body or footer: a function that closes it (5.16) — also when `dismissible={false}`, which only
1571
+ * turns off Escape, the scrim and ×. Outside a Dialog it does nothing. Use it for Cancel and Save in a Dialog that
1572
+ * has a `trigger` and no `open`. */
1573
+ export declare function useDialogClose(): () => void;
1512
1574
  export declare const Dialog: React$1.ForwardRefExoticComponent<DialogProps & React$1.RefAttributes<HTMLDivElement>>;
1513
1575
  /** Side panel over the page: record detail, filters, mobile navigation. Modal (focus trap, scroll lock, focus restore). */
1514
1576
  export declare const Drawer: React$1.ForwardRefExoticComponent<DrawerProps & React$1.RefAttributes<HTMLDivElement>>;
package/dist/index.d.ts CHANGED
@@ -47,6 +47,15 @@ export interface ButtonLinkProps extends Omit<React$1.AnchorHTMLAttributes<HTMLA
47
47
  /** A router's link to render instead of `<a>`, e.g. `Link` from `next/link` (client-side navigation). It gets `href`, `className`, the children and the ref. */
48
48
  linkComponent?: React$1.ElementType | undefined;
49
49
  }
50
+ /** How a DataTable selection changed: the second argument of `onSelectionChange` (5.16). */
51
+ export interface DataTableSelectionChange {
52
+ /** The row whose box or Space made the change; null for select-all and `sync`. */
53
+ key: string | number | null;
54
+ shiftKey: boolean;
55
+ source: "click" | "keyboard" | "all" | "sync";
56
+ /** With `rangeSelect`, the keys a Shift range set (the row's own key included). */
57
+ range?: Array<string | number> | undefined;
58
+ }
50
59
  /** What DataTable's onStateChange reports: the sort and the 1-based page, together. */
51
60
  export interface DataTableState {
52
61
  sort: DataTableSort | null;
@@ -149,7 +158,13 @@ export interface DataTableProps<Row extends Record<string, any> = Record<string,
149
158
  selected?: Array<string | number> | undefined;
150
159
  /** Initial selection when uncontrolled. */
151
160
  defaultSelected?: Array<string | number> | undefined;
152
- onSelectionChange?: ((keys: Array<string | number>) => void) | undefined;
161
+ /** The new keys, and how they changed (5.16, Chamber-OS 98): the row's `key` (null for select-all), whether Shift
162
+ * was held, and the `source` — `click` (a checkbox), `keyboard` (Space on a row), `all` (the select-all box) or
163
+ * `sync` (keys of rows `isRowSelectable` rejects, dropped). `range` lists the keys a Shift range changed. */
164
+ onSelectionChange?: ((keys: Array<string | number>, change: DataTableSelectionChange) => void) | undefined;
165
+ /** Shift-click a row's checkbox (or Shift+Space on a row) to set every selectable row from the last one changed to
166
+ * this one, on this page and in this order, to this row's new state (5.16, Chamber-OS 98). Default false. */
167
+ rangeSelect?: boolean | undefined;
153
168
  /** Shown when rows is empty. */
154
169
  empty?: DataTableEmpty | undefined;
155
170
  /** A totals row under the body (4.19): content per column key, e.g. `{ id: 'Total', vat: '7,000.00', total: '107,000.00' }`.
@@ -606,9 +621,22 @@ export interface FieldPropsPublic {
606
621
  required?: boolean | undefined;
607
622
  optional?: boolean | undefined;
608
623
  }
609
- export interface DialogProps {
610
- open: boolean;
611
- onClose: () => void;
624
+ /** `id`, `data-*`, `aria-*`, `style`, `lang` and `dir` go on the `.aura-dialog` panel (5.16, Chamber-OS 101). */
625
+ export interface DialogProps extends Pick<React$1.HTMLAttributes<HTMLDivElement>, "id" | "style" | "lang" | "dir">, React$1.AriaAttributes, DataAttributes {
626
+ /** Controlled open state. Leave it out with a `trigger` and the Dialog opens and closes itself (5.16). */
627
+ open?: boolean | undefined;
628
+ onClose?: (() => void) | undefined;
629
+ /** The button that opens it (5.16, Chamber-OS 101): rendered in place, with `aria-haspopup="dialog"`,
630
+ * `aria-expanded` and a click that opens the Dialog (its own onClick runs first; `preventDefault()` keeps it shut).
631
+ * Focus returns to it on close. */
632
+ trigger?: React$1.ReactElement | undefined;
633
+ /** Called when the trigger opens it (5.16). */
634
+ onOpen?: (() => void) | undefined;
635
+ /** Where focus goes when it closes — by a button, Escape or the scrim — instead of back to what opened it: the
636
+ * row a save just created (5.16). A ref, or a function read at close; null falls back to the opener. */
637
+ finalFocus?: React$1.RefObject<HTMLElement | null> | (() => HTMLElement | null) | undefined;
638
+ /** Runs once after it has closed and its panel has left the page (5.16) — reset a selection there. */
639
+ onCloseComplete?: (() => void) | undefined;
612
640
  title: React$1.ReactNode;
613
641
  description?: React$1.ReactNode | undefined;
614
642
  children?: React$1.ReactNode | undefined;
@@ -706,6 +734,11 @@ export interface NavItem {
706
734
  children?: NavItem[] | undefined;
707
735
  /** Start open. A group also opens by itself when one of its items is the current one. */
708
736
  defaultOpen?: boolean | undefined;
737
+ /** Runs when the item is chosen (5.16, Chamber-OS 95). */
738
+ onSelect?: (() => void) | undefined;
739
+ /** `false` makes an action row — Sign out, Collapse sidebar: it looks like the other rows and runs `onSelect`,
740
+ * but never becomes the current item (no `aria-current`, no `onChange`). Default true. (5.16) */
741
+ selectable?: boolean | undefined;
709
742
  }
710
743
  export interface SideNavProps {
711
744
  sections?: Array<{
@@ -731,6 +764,15 @@ export interface SideNavProps {
731
764
  onCollapsedChange?: ((collapsed: boolean) => void) | undefined;
732
765
  /** Adds a collapse / expand button at the bottom. AppShell hides it in its phone drawer. */
733
766
  collapsible?: boolean | undefined;
767
+ /** Called with the item's id when an action row (`selectable: false`) is chosen, after its `onSelect` (5.16).
768
+ * AppShell uses it to close its phone drawer. */
769
+ onAction?: ((id: string) => void) | undefined;
770
+ /** How `collapsible` shows its toggle: `icon` (default, an IconButton) or `row` — a labelled nav row
771
+ * ("Collapse sidebar") with the rows' look, its icon alone in the rail (5.16, Chamber-OS 95). */
772
+ collapseToggle?: "icon" | "row" | undefined;
773
+ /** Where a closed group's chevron points: `down` (default; up when open) or `right` (down when open; left in
774
+ * right-to-left pages) (5.16, Chamber-OS 96). */
775
+ chevron?: "down" | "right" | undefined;
734
776
  /** The 1px right edge that divides the nav from the page. Default true; `false` when your layout draws its own
735
777
  * divider. (5.1) */
736
778
  bordered?: boolean | undefined;
@@ -790,7 +832,7 @@ export interface ComboboxOption {
790
832
  icon?: IconInput | undefined;
791
833
  }
792
834
  /** Combobox with `multiple`: pick any number; the picks show as removable chips in the field. */
793
- export interface ComboboxMultipleProps extends Omit<ComboboxProps, "value" | "defaultValue" | "onChange" | "clearable"> {
835
+ export interface ComboboxMultipleProps extends Omit<ComboboxProps, "value" | "defaultValue" | "onChange" | "clearable" | "allowCustomValue"> {
794
836
  multiple: true;
795
837
  /** Selected option values, in the order they were picked (controlled). */
796
838
  value?: string[] | undefined;
@@ -802,7 +844,8 @@ export interface ComboboxMultipleProps extends Omit<ComboboxProps, "value" | "de
802
844
  max?: number | undefined;
803
845
  }
804
846
  export interface ComboboxProps extends FieldProps {
805
- options: Array<ComboboxOption | string>;
847
+ /** The options. Optional with `groups` (5.16). */
848
+ options?: Array<ComboboxOption | string> | undefined;
806
849
  /** Selected option value (controlled). */
807
850
  value?: string | null | undefined;
808
851
  defaultValue?: string | null | undefined;
@@ -825,6 +868,17 @@ export interface ComboboxProps extends FieldProps {
825
868
  limit?: number | undefined;
826
869
  /** Replace the Thai-aware default filter (label, description, keywords). */
827
870
  filter?: ((option: ComboboxOption, query: string) => boolean) | undefined;
871
+ /** Options under headings, after any in `options` (5.16, Chamber-OS 105): `[{ label: 'Nordic and Thailand',
872
+ * options: [...] }]`. Each heading names its group for screen readers; filtering keeps a heading while any of its
873
+ * options match. */
874
+ groups?: Array<{
875
+ label: string;
876
+ options: Array<ComboboxOption | string>;
877
+ }> | undefined;
878
+ /** A typed value that isn't in the list is kept (5.16, Chamber-OS 105): Enter with no match highlighted, or leaving
879
+ * the field, makes the text the value (text matching an option's label picks that option). Clearing the text
880
+ * clears the value. Single value only. */
881
+ allowCustomValue?: boolean | undefined;
828
882
  className?: string | undefined;
829
883
  }
830
884
  /** Number input: thousands separators, +/− buttons, arrow keys, min/max/step, prefix/suffix (฿, %). */
@@ -973,6 +1027,10 @@ export type ButtonAttributes = React$1.ButtonHTMLAttributes<HTMLButtonElement> &
973
1027
  export interface DrawerProps extends Pick<React$1.HTMLAttributes<HTMLDivElement>, "id" | "style" | "lang" | "dir">, React$1.AriaAttributes, DataAttributes {
974
1028
  open: boolean;
975
1029
  onClose: () => void;
1030
+ /** As Dialog's (5.16): where focus goes when it closes, instead of back to what opened it. */
1031
+ finalFocus?: React$1.RefObject<HTMLElement | null> | (() => HTMLElement | null) | undefined;
1032
+ /** As Dialog's (5.16): runs once after it has closed and left the page. */
1033
+ onCloseComplete?: (() => void) | undefined;
976
1034
  /** Default `right`. */
977
1035
  side?: "right" | "left" | undefined;
978
1036
  /** sm 360 · md 480 (default) · lg 640 · nav = SideNav width. Full width below 640px. */
@@ -1509,6 +1567,10 @@ export declare const FormErrorSummary: React$1.ForwardRefExoticComponent<FormErr
1509
1567
  export declare const FilterBar: React$1.ForwardRefExoticComponent<FilterBarProps & React$1.RefAttributes<HTMLDivElement>>;
1510
1568
  export declare function Command(props: CommandProps): React$1.ReactElement | null;
1511
1569
  export declare const Tooltip: React$1.ForwardRefExoticComponent<TooltipProps & React$1.RefAttributes<HTMLSpanElement>>;
1570
+ /** Inside a Dialog's body or footer: a function that closes it (5.16) — also when `dismissible={false}`, which only
1571
+ * turns off Escape, the scrim and ×. Outside a Dialog it does nothing. Use it for Cancel and Save in a Dialog that
1572
+ * has a `trigger` and no `open`. */
1573
+ export declare function useDialogClose(): () => void;
1512
1574
  export declare const Dialog: React$1.ForwardRefExoticComponent<DialogProps & React$1.RefAttributes<HTMLDivElement>>;
1513
1575
  /** Side panel over the page: record detail, filters, mobile navigation. Modal (focus trap, scroll lock, focus restore). */
1514
1576
  export declare const Drawer: React$1.ForwardRefExoticComponent<DrawerProps & React$1.RefAttributes<HTMLDivElement>>;
package/dist/styles.css CHANGED
@@ -464,7 +464,11 @@ a.aura-icon-btn { text-decoration: none; }
464
464
  .aura-field.is-invalid .aura-input, .aura-field.is-invalid .aura-textarea { border-color: var(--aura-border-danger); }
465
465
  .aura-field.is-disabled .aura-input { background: var(--aura-bg-input-disabled); color: var(--aura-fg-secondary); border-color: var(--aura-border-default); }
466
466
  .aura-field.is-disabled .aura-input__control { cursor: not-allowed; }
467
- .aura-input:has(.aura-input__control:read-only:not(select)) { background: var(--aura-bg-input-disabled); }
467
+ /* A read-only text field. Only an input: `:read-only` also matches any element that isn't editable — a
468
+ * Select's trigger <button> — which painted every custom Select with the disabled ground (5.16.1, Chamber-OS 109). */
469
+ .aura-input:has(input.aura-input__control:read-only) { background: var(--aura-bg-input-disabled); }
470
+ /* A read-only Textarea looks like a read-only TextField (it is its own .aura-input, so the rule above can't reach it). */
471
+ .aura-textarea:read-only:not(:disabled) { background: var(--aura-bg-input-disabled); }
468
472
 
469
473
  .aura-textarea { display: block; padding: 10px var(--aura-space-3); min-height: 96px; line-height: 1.6; resize: vertical; outline: none; }
470
474
  .aura-textarea:focus { outline: var(--aura-focus-ring-width) solid var(--aura-focus-ring); outline-offset: 1px; border-color: var(--aura-border-strong); }
@@ -685,7 +689,7 @@ a.aura-tab { text-decoration: none; }
685
689
  background: var(--aura-bg-canvas); border-right: 1px solid var(--aura-border-default);
686
690
  font-family: var(--font-sans); color: var(--aura-fg-primary);
687
691
  }
688
- .aura-nav__header { padding: var(--aura-space-5) var(--aura-space-4) var(--aura-space-4); }
692
+ .aura-nav__header { padding: var(--aura-space-5) var(--aura-space-4) var(--aura-space-4); padding-inline-end: var(--aura-space-2); } /* 5.16 (Chamber-OS 96): 8px at the end, so a brand, its dot and a badge share one row at 240px */
689
693
  .aura-nav__scroll { flex: 1; overflow-y: auto; padding: 0 var(--aura-space-3); }
690
694
  /* No header: keep the first item off the top edge (5.0.2). */
691
695
  .aura-nav > .aura-nav__scroll:first-child { padding-top: var(--aura-space-3); }
@@ -722,6 +726,10 @@ a.aura-tab { text-decoration: none; }
722
726
  .aura-nav__item--group.has-active { color: var(--aura-fg-primary); }
723
727
  .aura-nav__chevron { color: var(--aura-fg-secondary); transition: transform var(--aura-duration-fast) var(--aura-ease); }
724
728
  .aura-nav__chevron.is-open { transform: rotate(180deg); }
729
+ /* 5.16 (Chamber-OS 96): chevron="right" — right when closed, down when open (left when closed in right-to-left). */
730
+ .aura-nav--chevron-right .aura-nav__chevron { transform: rotate(-90deg); }
731
+ .aura-nav--chevron-right .aura-nav__chevron.is-open { transform: none; }
732
+ [dir='rtl'] .aura-nav--chevron-right .aura-nav__chevron:not(.is-open), .aura-nav--chevron-right[dir='rtl'] .aura-nav__chevron:not(.is-open) { transform: rotate(90deg); }
725
733
  .aura-nav__sub { margin-top: 2px; }
726
734
  .aura-nav__sub[hidden] { display: none; }
727
735
  .aura-nav__footer { padding: var(--aura-space-3); border-top: 1px solid var(--aura-border-default); }
@@ -832,6 +840,11 @@ a.aura-tab { text-decoration: none; }
832
840
  .aura-combo__desc { font-size: 12px; line-height: 1.5; color: var(--aura-fg-secondary); }
833
841
  /* Same specificity as `.aura-combo__option > .aura-icon` above and later in the file, so it wins by order (4.18). */
834
842
  .aura-combo__option > .aura-combo__check { color: var(--aura-fg-accent); }
843
+ /* 5.16 (Chamber-OS 105): option groups under a heading. */
844
+ .aura-combo__group { list-style: none; }
845
+ .aura-combo__group + .aura-combo__group, .aura-combo__option + .aura-combo__group { margin-top: var(--aura-space-1); padding-top: var(--aura-space-1); border-top: 1px solid var(--aura-border-subtle); }
846
+ .aura-combo__group-label { display: block; padding: var(--aura-space-2) var(--aura-space-2) var(--aura-space-1); font-family: var(--font-mono); font-size: 11px; line-height: 16px; color: var(--aura-fg-secondary); text-transform: uppercase; letter-spacing: 0.04em; }
847
+ .aura-combo__grouplist { list-style: none; margin: 0; padding: 0; }
835
848
  .aura-combo__note { display: flex; align-items: center; gap: var(--aura-space-2); padding: var(--aura-space-3) var(--aura-space-2); font-size: 13px; color: var(--aura-fg-secondary); }
836
849
 
837
850
  /* ---------- DatePicker / Calendar ---------- */
@@ -466,7 +466,11 @@ a.aura-icon-btn { text-decoration: none; }
466
466
  .aura-field.is-invalid .aura-input, .aura-field.is-invalid .aura-textarea { border-color: var(--aura-border-danger); }
467
467
  .aura-field.is-disabled .aura-input { background: var(--aura-bg-input-disabled); color: var(--aura-fg-secondary); border-color: var(--aura-border-default); }
468
468
  .aura-field.is-disabled .aura-input__control { cursor: not-allowed; }
469
- .aura-input:has(.aura-input__control:read-only:not(select)) { background: var(--aura-bg-input-disabled); }
469
+ /* A read-only text field. Only an input: `:read-only` also matches any element that isn't editable — a
470
+ * Select's trigger <button> — which painted every custom Select with the disabled ground (5.16.1, Chamber-OS 109). */
471
+ .aura-input:has(input.aura-input__control:read-only) { background: var(--aura-bg-input-disabled); }
472
+ /* A read-only Textarea looks like a read-only TextField (it is its own .aura-input, so the rule above can't reach it). */
473
+ .aura-textarea:read-only:not(:disabled) { background: var(--aura-bg-input-disabled); }
470
474
 
471
475
  .aura-textarea { display: block; padding: 10px var(--aura-space-3); min-height: 96px; line-height: 1.6; resize: vertical; outline: none; }
472
476
  .aura-textarea:focus { outline: var(--aura-focus-ring-width) solid var(--aura-focus-ring); outline-offset: 1px; border-color: var(--aura-border-strong); }
@@ -687,7 +691,7 @@ a.aura-tab { text-decoration: none; }
687
691
  background: var(--aura-bg-canvas); border-right: 1px solid var(--aura-border-default);
688
692
  font-family: var(--font-sans); color: var(--aura-fg-primary);
689
693
  }
690
- .aura-nav__header { padding: var(--aura-space-5) var(--aura-space-4) var(--aura-space-4); }
694
+ .aura-nav__header { padding: var(--aura-space-5) var(--aura-space-4) var(--aura-space-4); padding-inline-end: var(--aura-space-2); } /* 5.16 (Chamber-OS 96): 8px at the end, so a brand, its dot and a badge share one row at 240px */
691
695
  .aura-nav__scroll { flex: 1; overflow-y: auto; padding: 0 var(--aura-space-3); }
692
696
  /* No header: keep the first item off the top edge (5.0.2). */
693
697
  .aura-nav > .aura-nav__scroll:first-child { padding-top: var(--aura-space-3); }
@@ -724,6 +728,10 @@ a.aura-tab { text-decoration: none; }
724
728
  .aura-nav__item--group.has-active { color: var(--aura-fg-primary); }
725
729
  .aura-nav__chevron { color: var(--aura-fg-secondary); transition: transform var(--aura-duration-fast) var(--aura-ease); }
726
730
  .aura-nav__chevron.is-open { transform: rotate(180deg); }
731
+ /* 5.16 (Chamber-OS 96): chevron="right" — right when closed, down when open (left when closed in right-to-left). */
732
+ .aura-nav--chevron-right .aura-nav__chevron { transform: rotate(-90deg); }
733
+ .aura-nav--chevron-right .aura-nav__chevron.is-open { transform: none; }
734
+ [dir='rtl'] .aura-nav--chevron-right .aura-nav__chevron:not(.is-open), .aura-nav--chevron-right[dir='rtl'] .aura-nav__chevron:not(.is-open) { transform: rotate(90deg); }
727
735
  .aura-nav__sub { margin-top: 2px; }
728
736
  .aura-nav__sub[hidden] { display: none; }
729
737
  .aura-nav__footer { padding: var(--aura-space-3); border-top: 1px solid var(--aura-border-default); }
@@ -834,6 +842,11 @@ a.aura-tab { text-decoration: none; }
834
842
  .aura-combo__desc { font-size: 12px; line-height: 1.5; color: var(--aura-fg-secondary); }
835
843
  /* Same specificity as `.aura-combo__option > .aura-icon` above and later in the file, so it wins by order (4.18). */
836
844
  .aura-combo__option > .aura-combo__check { color: var(--aura-fg-accent); }
845
+ /* 5.16 (Chamber-OS 105): option groups under a heading. */
846
+ .aura-combo__group { list-style: none; }
847
+ .aura-combo__group + .aura-combo__group, .aura-combo__option + .aura-combo__group { margin-top: var(--aura-space-1); padding-top: var(--aura-space-1); border-top: 1px solid var(--aura-border-subtle); }
848
+ .aura-combo__group-label { display: block; padding: var(--aura-space-2) var(--aura-space-2) var(--aura-space-1); font-family: var(--font-mono); font-size: 11px; line-height: 16px; color: var(--aura-fg-secondary); text-transform: uppercase; letter-spacing: 0.04em; }
849
+ .aura-combo__grouplist { list-style: none; margin: 0; padding: 0; }
837
850
  .aura-combo__note { display: flex; align-items: center; gap: var(--aura-space-2); padding: var(--aura-space-3) var(--aura-space-2); font-size: 13px; color: var(--aura-fg-secondary); }
838
851
 
839
852
  /* ---------- DatePicker / Calendar ---------- */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jirawatpyk/aura-react",
3
- "version": "5.15.0",
3
+ "version": "5.16.1",
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",