@jirawatpyk/aura-react 4.8.0 → 4.9.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.
@@ -0,0 +1,175 @@
1
+ 'use client';
2
+ import * as React from "react";
3
+ import { Field, describedBy } from "./Field.js";
4
+ import { Icon } from "./Icon.js";
5
+ import { cx, uid, useMaybeControlled } from "./internal.js";
6
+ import { useStrings } from "./locale.js";
7
+ function decimalsOf(n) {
8
+ const s = String(n);
9
+ const i = s.indexOf(".");
10
+ return i < 0 ? 0 : s.length - i - 1;
11
+ }
12
+ function parse(text) {
13
+ const s = text.replace(/[,\s ]/g, "");
14
+ if (!/^-?(\d+\.?\d*|\.\d+)$/.test(s)) return NaN;
15
+ return Number(s);
16
+ }
17
+ const NumberField = React.forwardRef(function NumberField2(props, ref) {
18
+ const t = useStrings();
19
+ const auto = uid(), id = props.id || auto;
20
+ const step = props.step || 1;
21
+ const decimals = props.decimals != null ? props.decimals : decimalsOf(step);
22
+ const st = useMaybeControlled(
23
+ props.value,
24
+ props.defaultValue == null ? null : props.defaultValue,
25
+ props.onChange
26
+ );
27
+ const value = st[0], setValue = st[1];
28
+ const draftState = React.useState(null), draft = draftState[0], setDraft = draftState[1];
29
+ function fmt(n) {
30
+ if (n == null || isNaN(n)) return "";
31
+ return n.toLocaleString("en", { minimumFractionDigits: decimals, maximumFractionDigits: decimals });
32
+ }
33
+ function fit(n) {
34
+ let v = n;
35
+ if (props.min != null && v < props.min) v = props.min;
36
+ if (props.max != null && v > props.max) v = props.max;
37
+ const f = Math.pow(10, decimals);
38
+ return Math.round(v * f) / f;
39
+ }
40
+ function nudge(by) {
41
+ if (props.disabled || props.readOnly) return;
42
+ const base = value == null ? props.min != null && props.min > 0 ? props.min - by : 0 : value;
43
+ const next = fit(base + by);
44
+ setValue(next);
45
+ setDraft(null);
46
+ }
47
+ function onKeyDown(e) {
48
+ const k = e.key;
49
+ let by = 0;
50
+ if (k === "ArrowUp") by = step;
51
+ else if (k === "ArrowDown") by = -step;
52
+ else if (k === "PageUp") by = step * 10;
53
+ else if (k === "PageDown") by = -step * 10;
54
+ else if (k === "Home" && props.min != null) {
55
+ e.preventDefault();
56
+ setValue(props.min);
57
+ setDraft(null);
58
+ return;
59
+ } else if (k === "End" && props.max != null) {
60
+ e.preventDefault();
61
+ setValue(props.max);
62
+ setDraft(null);
63
+ return;
64
+ }
65
+ if (by) {
66
+ e.preventDefault();
67
+ nudge(by);
68
+ }
69
+ }
70
+ const atMin = value != null && props.min != null && value <= props.min;
71
+ const atMax = value != null && props.max != null && value >= props.max;
72
+ const affixText = function(x) {
73
+ return typeof x === "string" || typeof x === "number" ? String(x) : "";
74
+ };
75
+ const valueText = value == null ? void 0 : [affixText(props.prefix), fmt(value), affixText(props.suffix)].join(" ").trim();
76
+ const stepper = props.stepper !== false && !props.readOnly;
77
+ return /* @__PURE__ */ React.createElement(
78
+ Field,
79
+ {
80
+ id,
81
+ label: props.label,
82
+ hint: props.hint,
83
+ error: props.error,
84
+ required: props.required,
85
+ optional: props.optional,
86
+ disabled: props.disabled,
87
+ className: props.className
88
+ },
89
+ /* @__PURE__ */ React.createElement("div", { className: cx("aura-input aura-number", stepper && "has-stepper") }, props.prefix != null ? /* @__PURE__ */ React.createElement("span", { className: "aura-number__affix", "aria-hidden": true }, props.prefix) : null, /* @__PURE__ */ React.createElement(
90
+ "input",
91
+ {
92
+ ref,
93
+ id,
94
+ type: "text",
95
+ role: "spinbutton",
96
+ inputMode: decimals > 0 || props.min != null && props.min < 0 ? "decimal" : "numeric",
97
+ autoComplete: "off",
98
+ className: "aura-input__control",
99
+ name: props.name,
100
+ placeholder: props.placeholder,
101
+ disabled: props.disabled,
102
+ readOnly: props.readOnly,
103
+ required: props.required,
104
+ "aria-valuenow": value == null ? void 0 : value,
105
+ "aria-valuemin": props.min,
106
+ "aria-valuemax": props.max,
107
+ "aria-valuetext": valueText,
108
+ "aria-invalid": props.error ? true : void 0,
109
+ "aria-describedby": describedBy(id, props),
110
+ value: draft != null ? draft : fmt(value),
111
+ onFocus: function() {
112
+ setDraft(fmt(value));
113
+ },
114
+ onChange: function(e) {
115
+ const text = e.target.value;
116
+ setDraft(text);
117
+ if (!text.trim()) setValue(null);
118
+ else {
119
+ const n = parse(text);
120
+ if (!isNaN(n)) setValue(n);
121
+ }
122
+ },
123
+ onBlur: function(e) {
124
+ const n = draft == null ? value : !draft.trim() ? null : parse(draft);
125
+ if (n == null) {
126
+ if (value != null) setValue(null);
127
+ } else if (!isNaN(n)) {
128
+ const f = fit(n);
129
+ if (f !== value) setValue(f);
130
+ }
131
+ setDraft(null);
132
+ if (props.onBlur) props.onBlur(e);
133
+ },
134
+ onKeyDown
135
+ }
136
+ ), props.suffix != null ? /* @__PURE__ */ React.createElement("span", { className: "aura-number__affix", "aria-hidden": true }, props.suffix) : null, stepper ? /* @__PURE__ */ React.createElement("span", { className: "aura-number__steps" }, /* @__PURE__ */ React.createElement(
137
+ "button",
138
+ {
139
+ type: "button",
140
+ tabIndex: -1,
141
+ className: "aura-number__step",
142
+ "aria-label": t.decrease,
143
+ "aria-controls": id,
144
+ disabled: props.disabled || atMin,
145
+ onPointerDown: function(e) {
146
+ e.preventDefault();
147
+ },
148
+ onClick: function() {
149
+ nudge(-step);
150
+ }
151
+ },
152
+ /* @__PURE__ */ React.createElement(Icon, { name: "minus" })
153
+ ), /* @__PURE__ */ React.createElement(
154
+ "button",
155
+ {
156
+ type: "button",
157
+ tabIndex: -1,
158
+ className: "aura-number__step",
159
+ "aria-label": t.increase,
160
+ "aria-controls": id,
161
+ disabled: props.disabled || atMax,
162
+ onPointerDown: function(e) {
163
+ e.preventDefault();
164
+ },
165
+ onClick: function() {
166
+ nudge(step);
167
+ }
168
+ },
169
+ /* @__PURE__ */ React.createElement(Icon, { name: "plus" })
170
+ )) : null)
171
+ );
172
+ });
173
+ export {
174
+ NumberField
175
+ };
@@ -0,0 +1,104 @@
1
+ 'use client';
2
+ import * as React from "react";
3
+ import { Icon } from "./Icon.js";
4
+ import { cx, uid, useMaybeControlled } from "./internal.js";
5
+ function toOpt(o) {
6
+ return typeof o === "object" ? o : { value: o, label: o };
7
+ }
8
+ const SegmentedControl = React.forwardRef(
9
+ function SegmentedControl2(props, ref) {
10
+ const options = (props.options || []).map(toOpt);
11
+ const firstEnabled = options.filter(function(o) {
12
+ return !o.disabled;
13
+ })[0];
14
+ const st = useMaybeControlled(
15
+ props.value,
16
+ props.defaultValue != null ? props.defaultValue : firstEnabled ? firstEnabled.value : void 0,
17
+ props.onChange
18
+ );
19
+ const value = st[0], setValue = st[1];
20
+ const auto = uid(), id = props.id || auto;
21
+ const refs = React.useRef([]);
22
+ const selIdx = options.findIndex(function(o) {
23
+ return o.value === value;
24
+ });
25
+ const tabIdx = selIdx >= 0 && !options[selIdx].disabled ? selIdx : options.indexOf(firstEnabled);
26
+ function move(from, dir) {
27
+ const n = options.length;
28
+ for (let k = 1; k <= n; k++) {
29
+ const i = ((from + dir * k) % n + n) % n;
30
+ if (!options[i].disabled) {
31
+ setValue(options[i].value);
32
+ const el = refs.current[i];
33
+ if (el) el.focus();
34
+ return;
35
+ }
36
+ }
37
+ }
38
+ function onKeyDown(e, i) {
39
+ const k = e.key;
40
+ if (k === "ArrowRight" || k === "ArrowDown") {
41
+ e.preventDefault();
42
+ move(i, 1);
43
+ } else if (k === "ArrowLeft" || k === "ArrowUp") {
44
+ e.preventDefault();
45
+ move(i, -1);
46
+ } else if (k === "Home") {
47
+ e.preventDefault();
48
+ move(-1, 1);
49
+ } else if (k === "End") {
50
+ e.preventDefault();
51
+ move(options.length, -1);
52
+ }
53
+ }
54
+ return /* @__PURE__ */ React.createElement(
55
+ "div",
56
+ {
57
+ ref,
58
+ id,
59
+ role: "radiogroup",
60
+ "aria-label": props.label,
61
+ "aria-disabled": props.disabled || void 0,
62
+ className: cx(
63
+ "aura-segmented",
64
+ props.size === "sm" && "aura-segmented--sm",
65
+ props.fullWidth && "is-full",
66
+ props.disabled && "is-disabled",
67
+ props.className
68
+ )
69
+ },
70
+ options.map(function(o, i) {
71
+ const on = o.value === value;
72
+ const off = props.disabled || o.disabled;
73
+ return /* @__PURE__ */ React.createElement(
74
+ "button",
75
+ {
76
+ key: o.value,
77
+ ref: function(el) {
78
+ refs.current[i] = el;
79
+ },
80
+ type: "button",
81
+ role: "radio",
82
+ "aria-checked": on,
83
+ "aria-label": o.iconOnly ? o.label : void 0,
84
+ title: o.iconOnly ? o.label : void 0,
85
+ tabIndex: i === tabIdx && !props.disabled ? 0 : -1,
86
+ disabled: off,
87
+ className: cx("aura-segmented__option", on && "is-selected", o.iconOnly && "is-icon"),
88
+ onClick: function() {
89
+ setValue(o.value);
90
+ },
91
+ onKeyDown: function(e) {
92
+ onKeyDown(e, i);
93
+ }
94
+ },
95
+ o.icon ? /* @__PURE__ */ React.createElement(Icon, { name: o.icon }) : null,
96
+ o.iconOnly ? null : /* @__PURE__ */ React.createElement("span", null, o.label)
97
+ );
98
+ })
99
+ );
100
+ }
101
+ );
102
+ export {
103
+ SegmentedControl
104
+ };
@@ -0,0 +1,54 @@
1
+ 'use client';
2
+ import * as React from "react";
3
+ import { Icon } from "./Icon.js";
4
+ import { cx } from "./internal.js";
5
+ import { useStrings } from "./locale.js";
6
+ const Stepper = React.forwardRef(function Stepper2(props, ref) {
7
+ const t = useStrings();
8
+ const steps = props.steps || [];
9
+ let at = -1;
10
+ steps.forEach(function(s, i) {
11
+ if (s.id === props.current) at = i;
12
+ });
13
+ if (at < 0) at = 0;
14
+ const vertical = props.orientation === "vertical";
15
+ const cur = steps[at];
16
+ return /* @__PURE__ */ React.createElement(
17
+ "nav",
18
+ {
19
+ ref,
20
+ "aria-label": props.label,
21
+ className: cx("aura-stepper", vertical ? "aura-stepper--vertical" : "aura-stepper--horizontal", props.className)
22
+ },
23
+ /* @__PURE__ */ React.createElement("ol", { className: "aura-stepper__list" }, steps.map(function(s, i) {
24
+ const state = i < at ? "done" : i === at ? "current" : "upcoming";
25
+ const marker = /* @__PURE__ */ React.createElement("span", { className: "aura-stepper__marker", "aria-hidden": true }, state === "done" ? /* @__PURE__ */ React.createElement(Icon, { name: "check" }) : i + 1);
26
+ 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 clickable = state === "done" && !!props.onStepClick;
28
+ return /* @__PURE__ */ React.createElement(
29
+ "li",
30
+ {
31
+ key: s.id,
32
+ className: cx("aura-stepper__item", "is-" + state),
33
+ "aria-current": state === "current" ? "step" : void 0
34
+ },
35
+ clickable ? /* @__PURE__ */ React.createElement(
36
+ "button",
37
+ {
38
+ type: "button",
39
+ className: "aura-stepper__step aura-focusable",
40
+ onClick: function() {
41
+ props.onStepClick(s.id);
42
+ }
43
+ },
44
+ marker,
45
+ text
46
+ ) : /* @__PURE__ */ React.createElement("span", { className: "aura-stepper__step" }, marker, text)
47
+ );
48
+ })),
49
+ !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
50
+ );
51
+ });
52
+ export {
53
+ Stepper
54
+ };
package/dist/esm/index.js CHANGED
@@ -45,6 +45,9 @@ import { EmptyState } from "./EmptyState.js";
45
45
  import { Pagination } from "./Pagination.js";
46
46
  import { Accordion } from "./Accordion.js";
47
47
  import { Popover } from "./Popover.js";
48
+ import { NumberField } from "./NumberField.js";
49
+ import { Stepper } from "./Stepper.js";
50
+ import { SegmentedControl } from "./SegmentedControl.js";
48
51
  export {
49
52
  Accordion,
50
53
  Alert,
@@ -75,17 +78,20 @@ export {
75
78
  Icon,
76
79
  IconButton,
77
80
  Menu,
81
+ NumberField,
78
82
  Pagination,
79
83
  Popover,
80
84
  Progress,
81
85
  RadioGroup,
82
86
  STRINGS,
87
+ SegmentedControl,
83
88
  Select,
84
89
  SideNav,
85
90
  Skeleton,
86
91
  Stack,
87
92
  Stat,
88
93
  StatusPill,
94
+ Stepper,
89
95
  Surface,
90
96
  Switch,
91
97
  Tabs,
@@ -83,6 +83,12 @@ const STRINGS = {
83
83
  return "Up to " + n2 + " files";
84
84
  },
85
85
  colorScheme: "Colour scheme",
86
+ increase: "Increase",
87
+ decrease: "Decrease",
88
+ stepDone: "completed",
89
+ stepOf: function(i, total) {
90
+ return "Step " + i + " of " + total;
91
+ },
86
92
  schemeLight: "Light",
87
93
  schemeDark: "Dark",
88
94
  schemeSystem: "System",
@@ -175,6 +181,12 @@ const STRINGS = {
175
181
  return "\u0E41\u0E19\u0E1A\u0E44\u0E14\u0E49\u0E44\u0E21\u0E48\u0E40\u0E01\u0E34\u0E19 " + n2 + " \u0E44\u0E1F\u0E25\u0E4C";
176
182
  },
177
183
  colorScheme: "\u0E42\u0E2B\u0E21\u0E14\u0E2A\u0E35",
184
+ increase: "\u0E40\u0E1E\u0E34\u0E48\u0E21",
185
+ decrease: "\u0E25\u0E14",
186
+ stepDone: "\u0E40\u0E2A\u0E23\u0E47\u0E08\u0E41\u0E25\u0E49\u0E27",
187
+ stepOf: function(i, total) {
188
+ return "\u0E02\u0E31\u0E49\u0E19\u0E17\u0E35\u0E48 " + i + " \u0E08\u0E32\u0E01 " + total;
189
+ },
178
190
  schemeLight: "\u0E2A\u0E27\u0E48\u0E32\u0E07",
179
191
  schemeDark: "\u0E21\u0E37\u0E14",
180
192
  schemeSystem: "\u0E15\u0E32\u0E21\u0E23\u0E30\u0E1A\u0E1A",
package/dist/index.d.ts CHANGED
@@ -437,6 +437,18 @@ export interface ComboboxOption {
437
437
  /** Leading icon in the list. */
438
438
  icon?: IconName;
439
439
  }
440
+ /** Combobox with `multiple`: pick any number; the picks show as removable chips in the field. */
441
+ export interface ComboboxMultipleProps extends Omit<ComboboxProps, "value" | "defaultValue" | "onChange" | "clearable"> {
442
+ multiple: true;
443
+ /** Selected option values, in the order they were picked (controlled). */
444
+ value?: string[];
445
+ defaultValue?: string[];
446
+ onChange?: (value: string[]) => void;
447
+ /** Clear-all button while anything is selected. Default true. */
448
+ clearable?: boolean;
449
+ /** Most picks allowed; further options are disabled. */
450
+ max?: number;
451
+ }
440
452
  export interface ComboboxProps extends FieldProps {
441
453
  options: Array<ComboboxOption | string>;
442
454
  /** Selected option value (controlled). */
@@ -463,6 +475,77 @@ export interface ComboboxProps extends FieldProps {
463
475
  filter?: (option: ComboboxOption, query: string) => boolean;
464
476
  className?: string;
465
477
  }
478
+ /** Number input: thousands separators, +/− buttons, arrow keys, min/max/step, prefix/suffix (฿, %). */
479
+ export interface NumberFieldProps extends FieldProps {
480
+ /** The number (controlled). `null` = empty. */
481
+ value?: number | null;
482
+ defaultValue?: number | null;
483
+ /** Called with the parsed number (or null when emptied) as the person types, and with the clamped value on blur. */
484
+ onChange?: (value: number | null) => void;
485
+ min?: number;
486
+ max?: number;
487
+ /** Arrow keys and buttons move by this. Default 1. PageUp/PageDown move 10×. */
488
+ step?: number;
489
+ /** Decimal places shown and kept. Default: 0 when step is whole, else step's decimals. */
490
+ decimals?: number;
491
+ /** Text before the number, e.g. `฿`. */
492
+ prefix?: React$1.ReactNode;
493
+ /** Text after the number, e.g. `%` or `ชิ้น`. */
494
+ suffix?: React$1.ReactNode;
495
+ /** +/− buttons beside the number. Default true. */
496
+ stepper?: boolean;
497
+ id?: string;
498
+ name?: string;
499
+ placeholder?: string;
500
+ disabled?: boolean;
501
+ readOnly?: boolean;
502
+ className?: string;
503
+ onBlur?: React$1.FocusEventHandler<HTMLInputElement>;
504
+ }
505
+ /** One step of a Stepper. */
506
+ export interface StepItem {
507
+ id: string;
508
+ label: React$1.ReactNode;
509
+ /** Short line under the label, e.g. what the step asks for. */
510
+ description?: React$1.ReactNode;
511
+ }
512
+ /** Progress through a multi-step flow (wizard, checkout, booking). */
513
+ export interface StepperProps {
514
+ steps: StepItem[];
515
+ /** id of the step the person is on. Steps before it are completed. */
516
+ current: string;
517
+ /** Accessible name of the list. Default: none (give one when the page has several). */
518
+ label?: string;
519
+ /** Makes completed steps buttons that go back to them. Upcoming steps never are. */
520
+ onStepClick?: (id: string) => void;
521
+ /** Default `horizontal`. Below `sm` a horizontal stepper shows only the current step ("Step 2 of 4"). */
522
+ orientation?: "horizontal" | "vertical";
523
+ className?: string;
524
+ }
525
+ export interface SegmentedOption {
526
+ value: string;
527
+ label: string;
528
+ icon?: IconName;
529
+ /** Show only the icon; `label` becomes its accessible name and tooltip. */
530
+ iconOnly?: boolean;
531
+ disabled?: boolean;
532
+ }
533
+ /** A row of 2–5 mutually exclusive choices that apply at once (view, period, unit). A radio group underneath. */
534
+ export interface SegmentedControlProps {
535
+ /** Accessible name of the group, e.g. "View". Required. */
536
+ label: string;
537
+ options: Array<SegmentedOption | string>;
538
+ value?: string;
539
+ defaultValue?: string;
540
+ onChange?: (value: string) => void;
541
+ /** `md` (default, 36px) or `sm` (28px) for toolbars. */
542
+ size?: "sm" | "md";
543
+ /** Stretch across the container, segments equal width. */
544
+ fullWidth?: boolean;
545
+ disabled?: boolean;
546
+ id?: string;
547
+ className?: string;
548
+ }
466
549
  /** ISO date string, `YYYY-MM-DD` (Gregorian — the era is display only). */
467
550
  export type ISODate = string;
468
551
  export interface DateDisplayOptions {
@@ -832,10 +915,16 @@ export declare const Textarea: React$1.ForwardRefExoticComponent<TextareaProps &
832
915
  export declare const Select: React$1.ForwardRefExoticComponent<SelectProps & React$1.RefAttributes<HTMLSelectElement>>;
833
916
  export declare const RadioGroup: React$1.ForwardRefExoticComponent<RadioGroupProps & React$1.RefAttributes<HTMLFieldSetElement>>;
834
917
  export declare const Switch: React$1.ForwardRefExoticComponent<SwitchProps & React$1.RefAttributes<HTMLButtonElement>>;
918
+ /** One value, or with `multiple` any number (chips). Two call signatures so value/onChange are typed for each. */
919
+ export interface ComboboxComponent {
920
+ (props: ComboboxMultipleProps & React$1.RefAttributes<HTMLInputElement>): React$1.ReactElement | null;
921
+ (props: ComboboxProps & React$1.RefAttributes<HTMLInputElement>): React$1.ReactElement | null;
922
+ displayName?: string;
923
+ }
835
924
  /** The Thai-aware default filter: label, description and keywords contain the query. */
836
925
  declare function defaultFilter(option: ComboboxOption, query: string): boolean;
837
- /** Text field that filters a list as you type (ARIA 1.2 combobox). One value. */
838
- export declare const Combobox: React$1.ForwardRefExoticComponent<ComboboxProps & React$1.RefAttributes<HTMLInputElement>>;
926
+ /** Text field that filters a list as you type (ARIA 1.2 combobox). One value, or any number with `multiple`. */
927
+ export declare const Combobox: ComboboxComponent;
839
928
  /** Options for formatDate: locale/calendar plus a preset or any Intl.DateTimeFormat options. */
840
929
  export type FormatDateOptions = DateDisplayOptions & {
841
930
  format?: "short" | "long" | "numeric" | Intl.DateTimeFormatOptions;
@@ -953,6 +1042,10 @@ export interface AuraStrings {
953
1042
  fileWrongType: string;
954
1043
  tooManyFiles: (n: number | string) => string;
955
1044
  colorScheme: string;
1045
+ increase: string;
1046
+ decrease: string;
1047
+ stepDone: string;
1048
+ stepOf: (i: number, total: number) => string;
956
1049
  schemeLight: string;
957
1050
  schemeDark: string;
958
1051
  schemeSystem: string;
@@ -1031,6 +1124,12 @@ export declare const EmptyState: React$1.ForwardRefExoticComponent<EmptyStatePro
1031
1124
  export declare const Pagination: React$1.ForwardRefExoticComponent<PaginationProps & React$1.RefAttributes<HTMLElement>>;
1032
1125
  export declare const Accordion: React$1.ForwardRefExoticComponent<AccordionProps & React$1.RefAttributes<HTMLDivElement>>;
1033
1126
  export declare const Popover: React$1.ForwardRefExoticComponent<PopoverProps & React$1.RefAttributes<HTMLDivElement>>;
1127
+ /** Number input with thousands separators, +/− buttons and arrow keys (ARIA spinbutton). */
1128
+ export declare const NumberField: React$1.ForwardRefExoticComponent<NumberFieldProps & React$1.RefAttributes<HTMLInputElement>>;
1129
+ /** Progress through a multi-step flow. Completed steps can link back; the current one has aria-current="step". */
1130
+ export declare const Stepper: React$1.ForwardRefExoticComponent<StepperProps & React$1.RefAttributes<HTMLElement>>;
1131
+ /** A row of mutually exclusive choices that apply at once. A radio group: one Tab stop, arrow keys move and select. */
1132
+ export declare const SegmentedControl: React$1.ForwardRefExoticComponent<SegmentedControlProps & React$1.RefAttributes<HTMLDivElement>>;
1034
1133
 
1035
1134
  export {
1036
1135
  defaultFilter as comboboxFilter,
package/dist/styles.css CHANGED
@@ -944,6 +944,107 @@ button.aura-stat { font: inherit; width: 100%; cursor: pointer; }
944
944
  .aura-popover__head + .aura-popover__body { padding-top: var(--aura-space-2); }
945
945
  @media (prefers-reduced-motion: reduce) { .aura-popover { animation: none; } }
946
946
 
947
+ /* ---------- Combobox, multiple: picks as chips in the field ---------- */
948
+ .aura-combo.is-multi { flex-wrap: wrap; align-items: center; gap: var(--aura-space-1); padding: 5px calc(var(--aura-space-2) + 2 * var(--aura-control-sm)) 5px calc(var(--aura-space-3) + var(--aura-icon-sm) + var(--aura-space-2)); }
949
+ .aura-combo.is-multi .aura-input__icon { top: calc(var(--aura-input-height) / 2 - var(--aura-icon-sm) / 2 - 1px); }
950
+ .aura-combo.is-multi .aura-input__control { flex: 1 0 4ch; height: 32px; padding: 0 var(--aura-space-1); }
951
+ /* Wrapped chips grow the field: keep the icon and buttons on the first line. */
952
+ .aura-combo.is-multi .aura-combo__clear, .aura-combo.is-multi .aura-combo__toggle { top: calc((var(--aura-input-height) - 2px - var(--aura-control-sm)) / 2); }
953
+ .aura-combo__chips { display: contents; }
954
+ .aura-combo__chip {
955
+ display: inline-flex; align-items: center; gap: 2px; max-width: 100%; height: 28px; box-sizing: border-box;
956
+ padding: 0 2px 0 var(--aura-space-2); border-radius: var(--aura-radius-full);
957
+ background: var(--aura-bg-selected); color: var(--aura-fg-primary); font-size: 13px; font-weight: 500;
958
+ }
959
+ .aura-combo__chip-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 24ch; }
960
+ .aura-combo__chip-remove {
961
+ display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; padding: 0;
962
+ border: 0; border-radius: var(--aura-radius-full); background: transparent; color: var(--aura-fg-secondary); cursor: pointer;
963
+ }
964
+ .aura-combo__chip-remove:hover { background: var(--aura-bg-surface-hover); color: var(--aura-fg-primary); }
965
+ .aura-combo__chip-remove .aura-icon { width: 14px; height: 14px; }
966
+
967
+ /* ---------- NumberField ---------- */
968
+ .aura-number .aura-input__control { font-variant-numeric: tabular-nums; }
969
+ .aura-number__affix { padding-left: var(--aura-space-3); color: var(--aura-fg-secondary); font-size: 14px; white-space: nowrap; }
970
+ .aura-number__affix + .aura-input__control { padding-left: var(--aura-space-2); }
971
+ .aura-input__control + .aura-number__affix { padding: 0 var(--aura-space-2) 0 0; }
972
+ .aura-number__steps { display: flex; align-self: stretch; border-left: 1px solid var(--aura-border-default); }
973
+ .aura-number__step {
974
+ display: inline-flex; align-items: center; justify-content: center; width: 36px; padding: 0;
975
+ border: 0; background: transparent; color: var(--aura-fg-secondary); cursor: pointer;
976
+ transition: background-color var(--aura-duration-fast) var(--aura-ease), color var(--aura-duration-fast) var(--aura-ease);
977
+ }
978
+ .aura-number__step + .aura-number__step { border-left: 1px solid var(--aura-border-default); }
979
+ .aura-number__step:last-child { border-radius: 0 calc(var(--aura-radius-md) - 1px) calc(var(--aura-radius-md) - 1px) 0; }
980
+ .aura-number__step:hover:not(:disabled) { background: var(--aura-bg-surface-hover); color: var(--aura-fg-primary); }
981
+ .aura-number__step:disabled { opacity: var(--aura-disabled-opacity); cursor: default; }
982
+
983
+ /* ---------- Stepper ---------- */
984
+ .aura-stepper { font-family: var(--font-sans); }
985
+ .aura-stepper__list { display: flex; list-style: none; margin: 0; padding: 0; }
986
+ .aura-stepper--vertical .aura-stepper__list { flex-direction: column; }
987
+ .aura-stepper__item { position: relative; flex: 1 1 0; min-width: 0; }
988
+ .aura-stepper__step {
989
+ display: flex; align-items: flex-start; gap: var(--aura-space-3); width: 100%; box-sizing: border-box; padding: 0 var(--aura-space-3) 0 0;
990
+ border: 0; background: none; color: inherit; font: inherit; text-align: left; border-radius: var(--aura-radius-md);
991
+ }
992
+ button.aura-stepper__step { cursor: pointer; }
993
+ button.aura-stepper__step:hover .aura-stepper__label { text-decoration: underline; }
994
+ .aura-stepper__marker {
995
+ position: relative; z-index: 1; flex: none; display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; box-sizing: border-box;
996
+ border-radius: var(--aura-radius-full); border: 1px solid var(--aura-border-control); background: var(--aura-bg-surface);
997
+ color: var(--aura-fg-secondary); font-size: 13px; font-weight: 600; font-variant-numeric: tabular-nums;
998
+ }
999
+ .aura-stepper__marker .aura-icon { width: 16px; height: 16px; }
1000
+ .aura-stepper__item.is-done .aura-stepper__marker { background: var(--aura-button-primary-bg); border-color: var(--aura-button-primary-bg); color: var(--aura-button-primary-fg); }
1001
+ .aura-stepper__item.is-current .aura-stepper__marker { border: 2px solid var(--aura-fg-accent); color: var(--aura-fg-accent); }
1002
+ .aura-stepper__text { display: flex; flex-direction: column; min-width: 0; padding-top: 4px; }
1003
+ .aura-stepper__label { font-size: 13px; font-weight: 500; line-height: 1.4; color: var(--aura-fg-secondary); }
1004
+ .aura-stepper__item.is-current .aura-stepper__label, .aura-stepper__item.is-done .aura-stepper__label { color: var(--aura-fg-primary); }
1005
+ .aura-stepper__item.is-current .aura-stepper__label { font-weight: 600; }
1006
+ .aura-stepper__desc { font-size: 12px; line-height: 1.5; color: var(--aura-fg-secondary); }
1007
+ /* Connector: a line from each marker to the next (labels sit under the markers), or down the left when vertical. */
1008
+ .aura-stepper--horizontal .aura-stepper__item:not(:last-child)::after {
1009
+ content: ""; position: absolute; top: 14px; left: 36px; right: var(--aura-space-2); height: 1px; background: var(--aura-border-control); pointer-events: none;
1010
+ }
1011
+ .aura-stepper--horizontal .aura-stepper__step { flex-direction: column; gap: var(--aura-space-2); }
1012
+ .aura-stepper--horizontal .aura-stepper__text { padding-top: 0; }
1013
+ .aura-stepper--horizontal .aura-stepper__item.is-done::after { background: var(--aura-button-primary-bg); }
1014
+ .aura-stepper--vertical .aura-stepper__item { flex: none; padding-bottom: var(--aura-space-5); }
1015
+ .aura-stepper--vertical .aura-stepper__item:not(:last-child)::after {
1016
+ content: ""; position: absolute; left: 14px; top: 32px; bottom: 4px; width: 1px; background: var(--aura-border-control);
1017
+ }
1018
+ .aura-stepper--vertical .aura-stepper__item.is-done::after { background: var(--aura-button-primary-bg); }
1019
+ .aura-stepper__compact { display: none; margin: var(--aura-space-2) 0 0; font-size: 13px; }
1020
+ .aura-stepper__count { color: var(--aura-fg-secondary); margin-right: var(--aura-space-2); }
1021
+ .aura-stepper__compact-label { font-weight: 600; }
1022
+
1023
+ /* ---------- SegmentedControl ---------- */
1024
+ .aura-segmented {
1025
+ display: inline-flex; gap: 2px; padding: 2px; box-sizing: border-box; max-width: 100%;
1026
+ border: 1px solid var(--aura-border-default); border-radius: var(--aura-radius-full); background: var(--aura-bg-surface-hover);
1027
+ font-family: var(--font-sans);
1028
+ }
1029
+ .aura-segmented:not(.is-full) { width: fit-content; justify-self: start; align-self: flex-start; }
1030
+ .aura-segmented.is-full { display: flex; width: 100%; }
1031
+ .aura-segmented__option {
1032
+ flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; gap: 6px;
1033
+ height: 32px; padding: 0 var(--aura-space-4); box-sizing: border-box;
1034
+ border: 0; border-radius: var(--aura-radius-full); background: transparent; color: var(--aura-fg-secondary);
1035
+ font: inherit; font-size: 13px; font-weight: 500; white-space: nowrap; cursor: pointer;
1036
+ transition: background-color var(--aura-duration-fast) var(--aura-ease), color var(--aura-duration-fast) var(--aura-ease), box-shadow var(--aura-duration-fast) var(--aura-ease);
1037
+ }
1038
+ .aura-segmented.is-full .aura-segmented__option { flex: 1 1 0; min-width: 0; }
1039
+ .aura-segmented__option.is-icon { padding: 0; width: 36px; }
1040
+ .aura-segmented__option:hover:not(:disabled):not(.is-selected) { color: var(--aura-fg-primary); }
1041
+ .aura-segmented__option.is-selected { background: var(--aura-bg-surface); color: var(--aura-fg-primary); box-shadow: var(--aura-shadow-enterprise); }
1042
+ .aura-segmented__option:focus-visible { outline: var(--aura-focus-ring-width) solid var(--aura-focus-ring); outline-offset: 1px; }
1043
+ .aura-segmented__option:disabled { opacity: var(--aura-disabled-opacity); cursor: default; }
1044
+ .aura-segmented--sm .aura-segmented__option { height: 24px; padding: 0 var(--aura-space-3); font-size: 12px; }
1045
+ .aura-segmented--sm .aura-segmented__option.is-icon { width: 28px; padding: 0; }
1046
+ .aura-segmented.is-disabled { opacity: var(--aura-disabled-opacity); }
1047
+
947
1048
  /* A field inside a row Stack takes the remaining width (a search box beside its button). */
948
1049
  .aura-stack > .aura-field { flex: 1 1 auto; min-width: 0; }
949
1050
 
@@ -951,10 +1052,16 @@ button.aura-stat { font: inherit; width: 100%; cursor: pointer; }
951
1052
  @media (pointer: coarse) {
952
1053
  .aura-cal { --aura-cal-cell: 40px; }
953
1054
  .aura-combo__option { min-height: 44px; }
1055
+ .aura-segmented__option, .aura-segmented--sm .aura-segmented__option { height: 40px; }
1056
+ .aura-number__step { width: 44px; }
954
1057
  }
955
1058
 
956
1059
  /* ---------- Small screens (< 640px) ---------- */
957
1060
  @media (max-width: 639.98px) {
1061
+ /* Horizontal stepper on phones: markers only (names kept for screen readers), the current step named underneath. */
1062
+ .aura-stepper--horizontal .aura-stepper__text { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; padding: 0; }
1063
+ .aura-stepper--horizontal .aura-stepper__item:not(:last-child)::after { right: 8px; }
1064
+ .aura-stepper--horizontal .aura-stepper__compact { display: block; }
958
1065
  .aura-dialog-layer:not(.aura-drawer-layer) { align-items: flex-end; padding: 0; }
959
1066
  .aura-dialog { max-width: none !important; max-height: 92vh; border-radius: var(--aura-radius-2xl) var(--aura-radius-2xl) 0 0; border-bottom: 0;
960
1067
  padding-bottom: calc(var(--aura-space-6) + env(safe-area-inset-bottom)); animation-name: aura-sheet-in; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jirawatpyk/aura-react",
3
- "version": "4.8.0",
3
+ "version": "4.9.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",