@antadesign/anta 0.3.4 → 0.3.6

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.
Files changed (55) hide show
  1. package/dist/anta_helpers.d.ts +10 -0
  2. package/dist/anta_helpers.js +8 -0
  3. package/dist/components/Calendar.d.ts +3 -2
  4. package/dist/components/Calendar.js +18 -7
  5. package/dist/components/Dialog.d.ts +92 -0
  6. package/dist/components/Dialog.js +71 -0
  7. package/dist/components/Input.d.ts +3 -0
  8. package/dist/components/InputAutocomplete.d.ts +69 -0
  9. package/dist/components/InputAutocomplete.js +95 -0
  10. package/dist/components/InputDate.d.ts +3 -2
  11. package/dist/components/InputDate.js +36 -91
  12. package/dist/components/InputDate.module.css +1 -1
  13. package/dist/components/InputTime.d.ts +105 -0
  14. package/dist/components/InputTime.js +110 -0
  15. package/dist/components/Menu.d.ts +9 -1
  16. package/dist/components/Menu.js +4 -1
  17. package/dist/components/MenuItem.d.ts +9 -1
  18. package/dist/components/MenuItem.js +2 -1
  19. package/dist/components/Select.d.ts +29 -8
  20. package/dist/components/Select.js +23 -30
  21. package/dist/components/Select.module.css +1 -1
  22. package/dist/components/SelectFaceted.d.ts +190 -0
  23. package/dist/components/SelectFaceted.js +332 -0
  24. package/dist/components/SelectFaceted.module.css +1 -0
  25. package/dist/components/Tabs.d.ts +6 -0
  26. package/dist/components/Tabs.js +3 -1
  27. package/dist/components/select-options.d.ts +14 -0
  28. package/dist/components/select-options.js +31 -0
  29. package/dist/elements/a-button.css +1 -1
  30. package/dist/elements/a-calendar.css +1 -1
  31. package/dist/elements/a-dialog.css +1 -0
  32. package/dist/elements/a-dialog.d.ts +26 -0
  33. package/dist/elements/a-dialog.js +386 -0
  34. package/dist/elements/a-expander.js +2 -2
  35. package/dist/elements/a-input-time.css +1 -0
  36. package/dist/elements/a-input-time.d.ts +27 -0
  37. package/dist/elements/a-input-time.js +857 -0
  38. package/dist/elements/a-input.css +1 -1
  39. package/dist/elements/a-input.js +7 -1
  40. package/dist/elements/a-menu-item.css +1 -1
  41. package/dist/elements/a-menu.d.ts +5 -2
  42. package/dist/elements/a-menu.js +42 -7
  43. package/dist/elements/a-tab.css +1 -1
  44. package/dist/elements/a-tabs.css +1 -1
  45. package/dist/elements/a-tooltip.d.ts +17 -0
  46. package/dist/elements/a-tooltip.js +29 -1
  47. package/dist/elements/index.d.ts +2 -0
  48. package/dist/elements/index.js +6 -0
  49. package/dist/general_types.d.ts +97 -0
  50. package/dist/index.d.ts +8 -0
  51. package/dist/index.js +8 -0
  52. package/dist/jsx-runtime.d.ts +3 -1
  53. package/dist/reset.css +1 -1
  54. package/dist/tokens.css +1 -1
  55. package/package.json +1 -1
@@ -17,7 +17,7 @@ import { Menu } from "./Menu";
17
17
  import { Calendar } from "./Calendar";
18
18
  import { Button } from "./Button";
19
19
  import { Icon } from "./Icon";
20
- import { Tabs } from "./Tabs";
20
+ import { InputTime } from "./InputTime";
21
21
  import styles from "./InputDate.module.css";
22
22
  const InputDate = ({
23
23
  value,
@@ -67,47 +67,21 @@ const InputDate = ({
67
67
  const hasTime = !!current && current.length >= 16 && current[10] === "T";
68
68
  const h24 = hasTime ? parseInt(current.slice(11, 13), 10) || 0 : 0;
69
69
  const curM = hasTime ? current.slice(14, 16) : "00";
70
- const curMer = h24 < 12 ? "AM" : "PM";
71
- const curHourShown = twelveHour ? String((h24 + 11) % 12 + 1).padStart(2, "0") : String(h24).padStart(2, "0");
72
70
  const [text, setText] = useState(() => fmt(current));
73
71
  const [open, setOpen] = useState(false);
74
72
  const [invalid, setInvalid] = useState(false);
75
73
  const [focusNonce, setFocusNonce] = useState(0);
76
- const [hourText, setHourText] = useState(curHourShown);
77
- const [minuteText, setMinuteText] = useState(curM);
78
- const meridiem = curMer;
79
74
  const [lastValue, setLastValue] = useState(current);
80
75
  if (current !== lastValue) {
81
76
  setLastValue(current);
82
77
  setText(fmt(current));
83
78
  setInvalid(false);
84
79
  }
85
- const [lastTime, setLastTime] = useState(`${h24}:${curM}`);
86
- if (time && `${h24}:${curM}` !== lastTime) {
87
- setLastTime(`${h24}:${curM}`);
88
- setHourText(curHourShown);
89
- setMinuteText(curM);
90
- }
91
80
  const commit = (iso) => {
92
81
  if (!controlled) setInternal(iso || void 0);
93
82
  onValueChange?.(iso, { value: iso, name });
94
83
  };
95
- const clampPad = (v, hi) => String(Math.min(Math.max(parseInt(String(v || "0"), 10) || 0, 0), hi)).padStart(2, "0");
96
- const resolveHour = (raw, mer) => {
97
- let n = parseInt(String(raw || "0"), 10) || 0;
98
- if (!twelveHour) {
99
- const h = Math.min(Math.max(n, 0), 23);
100
- return { h24: h, shown: String(h).padStart(2, "0"), mer };
101
- }
102
- if (n === 0) return { h24: 0, shown: "12", mer: "AM" };
103
- if (n > 23) n = 23;
104
- if (n > 12) return { h24: n, shown: String(n - 12).padStart(2, "0"), mer: "PM" };
105
- return { h24: mer === "AM" ? n % 12 : n % 12 + 12, shown: String(n).padStart(2, "0"), mer };
106
- };
107
- const withTime = (d) => {
108
- const { h24: h } = resolveHour(hourText, meridiem);
109
- return `${d}T${String(h).padStart(2, "0")}:${clampPad(minuteText, 59)}`;
110
- };
84
+ const withTime = (d) => `${d}T${String(h24).padStart(2, "0")}:${curM}`;
111
85
  const apply = (iso) => {
112
86
  setInvalid(false);
113
87
  if (iso === (current ?? "")) setText(fmt(iso));
@@ -119,24 +93,25 @@ const InputDate = ({
119
93
  setInvalid(false);
120
94
  if (current) commit("");
121
95
  else setText("");
122
- return;
96
+ return true;
123
97
  }
124
98
  const parsed = time ? parseDateTimeInput(t, resolvedLocale, { min: minD, max: maxD }) : parseDateInput(t, resolvedLocale, { min: minD, max: maxD });
125
99
  if (!parsed) {
126
100
  setInvalid(true);
127
- return;
101
+ return false;
128
102
  }
129
103
  const iso = time ? parsed.toString({ smallestUnit: "minute" }) : parsed.toString();
130
104
  apply(iso);
105
+ return true;
131
106
  };
132
- const commitTime = (hRaw, mRaw, mer) => {
133
- const { h24: h, shown } = resolveHour(hRaw, mer);
134
- const mm = clampPad(mRaw, 59);
135
- setHourText(shown);
136
- setMinuteText(mm);
137
- const base = dateISO || Temporal.Now.plainDateISO().toString();
138
- commit(`${base}T${String(h).padStart(2, "0")}:${mm}`);
139
- };
107
+ const parsedPreview = useMemo(() => {
108
+ const t = text.trim();
109
+ if (!t) return dateISO;
110
+ const parsed = time ? parseDateTimeInput(t, resolvedLocale, { min: minD, max: maxD }) : parseDateInput(t, resolvedLocale, { min: minD, max: maxD });
111
+ return parsed ? parsed.toString().slice(0, 10) : null;
112
+ }, [text, time, resolvedLocale, dateISO, min, max]);
113
+ const [previewISO, setPreviewISO] = useState(dateISO);
114
+ if (parsedPreview !== null && parsedPreview !== previewISO) setPreviewISO(parsedPreview);
140
115
  return /* @__PURE__ */ jsxs(Fragment, { children: [
141
116
  /* @__PURE__ */ jsx(
142
117
  Input,
@@ -148,7 +123,7 @@ const InputDate = ({
148
123
  round,
149
124
  disabled,
150
125
  clearable,
151
- leading: /* @__PURE__ */ jsx(Icon, { shape: icon ?? "calendar-days" }),
126
+ leading: icon === false ? void 0 : /* @__PURE__ */ jsx(Icon, { shape: icon ?? "calendar-days" }),
152
127
  value: text,
153
128
  placeholder: pattern,
154
129
  inputMode: time ? "text" : "numeric",
@@ -166,6 +141,10 @@ const InputDate = ({
166
141
  if (!open) e.currentTarget.click();
167
142
  setFocusNonce((n) => n + 1);
168
143
  }
144
+ if (e.key === "Enter" && !disabled) {
145
+ e.preventDefault();
146
+ if (resolve(e.currentTarget.value)) setOpen(false);
147
+ }
169
148
  },
170
149
  className: cn(styles.dateField, className),
171
150
  style,
@@ -177,13 +156,14 @@ const InputDate = ({
177
156
  {
178
157
  open,
179
158
  placement: "bottom-start",
159
+ autoWidth: true,
180
160
  className: styles.calendarMenu,
181
161
  onStateChange: (_e, { next }) => setOpen(next),
182
162
  children: /* @__PURE__ */ jsxs("div", { "data-menu-open": "", children: [
183
163
  /* @__PURE__ */ jsx(
184
164
  Calendar,
185
165
  {
186
- value: dateISO,
166
+ value: previewISO,
187
167
  min,
188
168
  max,
189
169
  locale,
@@ -199,58 +179,23 @@ const InputDate = ({
199
179
  }
200
180
  ),
201
181
  time && /* @__PURE__ */ jsxs("div", { className: styles.timeRow, children: [
202
- /* @__PURE__ */ jsxs("div", { className: styles.time, role: "group", "aria-label": "Time", children: [
203
- /* @__PURE__ */ jsx(Icon, { shape: "clock", "aria-hidden": "true", className: styles.clock }),
204
- /* @__PURE__ */ jsx(
205
- Input,
206
- {
207
- size,
208
- className: styles.timeField,
209
- value: hourText,
210
- placeholder: "HH",
211
- type: "number",
212
- inputMode: "numeric",
213
- "aria-label": "Hours",
214
- disabled,
215
- onInput: (e) => setHourText(e.currentTarget.value.replace(/\D/g, "").slice(0, 2)),
216
- onChange: (e) => commitTime(e.currentTarget.value, minuteText, meridiem)
217
- }
218
- ),
219
- /* @__PURE__ */ jsx("span", { className: styles.sep, "aria-hidden": "true", children: ":" }),
220
- /* @__PURE__ */ jsx(
221
- Input,
222
- {
223
- size,
224
- className: styles.timeField,
225
- value: minuteText,
226
- placeholder: "MM",
227
- type: "number",
228
- inputMode: "numeric",
229
- "aria-label": "Minutes",
230
- disabled,
231
- onInput: (e) => setMinuteText(e.currentTarget.value.replace(/\D/g, "").slice(0, 2)),
232
- onChange: (e) => commitTime(hourText, e.currentTarget.value, meridiem)
233
- }
234
- ),
235
- twelveHour && /* @__PURE__ */ jsx(
236
- Tabs,
237
- {
238
- className: styles.meridiem,
239
- size,
240
- priority: "primary",
241
- "aria-label": "AM or PM",
242
- options: [
243
- { value: "AM", label: "AM" },
244
- { value: "PM", label: "PM" }
245
- ],
246
- value: meridiem,
247
- onStateChange: (_e, { next }) => {
248
- if (next === "AM" || next === "PM") commitTime(hourText, minuteText, next);
249
- },
250
- disabled
182
+ /* @__PURE__ */ jsx(
183
+ InputTime,
184
+ {
185
+ size,
186
+ locale,
187
+ hour12,
188
+ disabled,
189
+ "aria-label": "Time",
190
+ value: hasTime ? `${String(h24).padStart(2, "0")}:${curM}` : "",
191
+ onValueChange: (e, { value: t }) => {
192
+ const ev = "nativeEvent" in e ? e.nativeEvent : e;
193
+ if (ev?.type !== "change") return;
194
+ const base = dateISO || Temporal.Now.plainDateISO().toString();
195
+ commit(t ? `${base}T${t}` : base);
251
196
  }
252
- )
253
- ] }),
197
+ }
198
+ ),
254
199
  /* @__PURE__ */ jsx(
255
200
  Button,
256
201
  {
@@ -1 +1 @@
1
- .calendarMenu{--menu-padding: 8px}.timeRow{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-top:6px;padding-top:8px;border-top:1px solid var(--border-3)}.time{display:flex;align-items:center;gap:4px}.clock{flex:none;color:var(--text-5);margin-inline-end:2px}.sep{color:var(--text-3);user-select:none}.timeField{width:2.25rem}.timeField[size=small]{width:2rem}.timeField[size=large]{width:2.5rem}.timeField::part(input){text-align:center;font-variant-numeric:tabular-nums;font-weight:500}.meridiem{margin-inline-start:4px;--tab-padding-x: 6px;--tabs-gap: 0px;--tab-padding-y: max(0px, calc((10px - var(--_pb)) / 2 - 2px))}.meridiem a-tab{font-weight:400}.dateField::part(input){text-overflow:ellipsis}
1
+ .calendarMenu{--menu-padding: 8px}.timeRow{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-top:6px;padding-top:8px;border-top:1px solid var(--border-3)}.dateField::part(input){text-overflow:ellipsis}
@@ -0,0 +1,105 @@
1
+ import type { BaseProps, DOMEventHandlers } from '../general_types';
2
+ import type { IconShape } from '../elements/a-icon.shapes';
3
+ /** Convenience snapshot passed as the 2nd argument to `onValueChange`. */
4
+ export interface InputTimeChangeAttrs {
5
+ /** Current value — 24-hour `"HH:mm"`, `''` when incomplete. */
6
+ value: string;
7
+ /** The field's `name`, for keyed updates: `s => ({ ...s, [name]: value })`. */
8
+ name?: string;
9
+ /** `true` when the value is empty / incomplete. */
10
+ empty: boolean;
11
+ /** Whether the field currently passes validation. `undefined` where
12
+ * `ElementInternals` is unsupported. */
13
+ valid?: boolean;
14
+ /** Current validation message (`''` when valid). */
15
+ validationMessage: string;
16
+ }
17
+ export interface InputTimeProps extends BaseProps, DOMEventHandlers {
18
+ /** Extra content rendered under the field, above the hint (a no-box child like
19
+ * a `<Tooltip>` takes no space and just anchors to the field). */
20
+ children?: React.ReactNode;
21
+ /** Field label, shown above the control and used as the segment group's
22
+ * accessible name. */
23
+ label?: React.ReactNode;
24
+ /** Message below the field. `status` recolors it and prefixes a glyph. */
25
+ hint?: React.ReactNode;
26
+ /** Validation / feedback tone. Only `critical` marks the field invalid; the
27
+ * others are advisory. Omit (or `neutral`) for a plain field. */
28
+ status?: 'neutral' | 'brand' | 'info' | 'success' | 'warning' | 'critical';
29
+ /** Glyph before the `hint` when `status` is set (per-status default; pass a
30
+ * shape to override, or `false` to drop it). */
31
+ statusIcon?: IconShape | (string & {}) | false;
32
+ /** Custom accent colour — any literal CSS colour tints the resting + hover
33
+ * border (focus ring stays `--focus-ring`); `status` overrides for validation. */
34
+ tone?: string;
35
+ /** Size variant. small=24px, medium=28px, large=32px tall.
36
+ * @defaultValue medium */
37
+ size?: 'small' | 'medium' | 'large';
38
+ /** Controlled value — 24-hour `"HH:mm"`. Pair with `onValueChange`. */
39
+ value?: string;
40
+ /** Initial value for the uncontrolled case (24-hour `"HH:mm"`). */
41
+ defaultValue?: string;
42
+ /** BCP-47 locale driving the clock (12h vs 24h), segment order, separator, and
43
+ * the AM/PM text.
44
+ * @defaultValue navigator.language */
45
+ locale?: string;
46
+ /** Force the clock: `true` = 12-hour (AM/PM), `false` = 24-hour. Omit to follow
47
+ * the locale. */
48
+ hour12?: boolean;
49
+ /** Earliest allowed time, 24-hour `"HH:mm"`. A complete value below it is
50
+ * clamped up (on step / blur) and flagged `rangeUnderflow` for form validity. */
51
+ min?: string;
52
+ /** Latest allowed time, 24-hour `"HH:mm"`. A complete value above it is clamped
53
+ * down (on step / blur) and flagged `rangeOverflow`. */
54
+ max?: string;
55
+ /** Form field name — the 24-hour value submits under this key. */
56
+ name?: string;
57
+ /** Disable the field. */
58
+ disabled?: boolean;
59
+ /** Mark the field required (drives native validity). */
60
+ required?: boolean;
61
+ /** Leading icon at the start of the field — the clock affordance. Pass another
62
+ * shape to change it, or `false` to drop it.
63
+ * @defaultValue clock */
64
+ icon?: IconShape | false;
65
+ /** Show a clear button once the field has a value. */
66
+ clearable?: boolean;
67
+ /** Dim the trailing adornments at rest; they brighten on hover / focus. */
68
+ dimActions?: boolean;
69
+ /** Content pinned to the end of the field (after the clear button). */
70
+ trailing?: React.ReactNode;
71
+ /** Fully-round the field, or a custom radius (`number` px / CSS length). */
72
+ round?: boolean | number | string;
73
+ /** Fires on every edit (`input`), with the native event + an `attrs` snapshot
74
+ * (`value`, `name`, `empty`, `valid`, `validationMessage`). Also fires on
75
+ * `change` (blur) and on clear. */
76
+ onValueChange?: (event: any, attrs: InputTimeChangeAttrs) => void;
77
+ /** Fires after the built-in clear button has cleared the field. */
78
+ onClearInput?: (e: CustomEvent) => void;
79
+ /** Fires when the field gains focus. */
80
+ onFocus?: (e: any) => void;
81
+ /** Fires when the field loses focus. */
82
+ onBlur?: (e: any) => void;
83
+ }
84
+ /**
85
+ * `<InputTime>` — a segmented wall-clock time field. One boxed input (matching
86
+ * the other inputs) with separate hour / minute / (12-hour) AM-PM sections that
87
+ * behave as one control: each is a `role="spinbutton"` — ↑/↓ steps with wrap,
88
+ * ←/→ moves between them, typing digits auto-advances, and AM/PM toggles with
89
+ * ↑/↓ or `a`/`p`. Renders an `<a-input-time>` web component that owns the
90
+ * segments, keyboard, and form value (via `ElementInternals`).
91
+ *
92
+ * The clock and layout follow the locale (`en-US` → 12-hour, most others 24),
93
+ * overridable with `hour12`. The value is a 24-hour `"HH:mm"` string; controlled
94
+ * (`value` + `onValueChange`) or uncontrolled (`defaultValue`), submitting under
95
+ * `name`.
96
+ *
97
+ * Requires `@antadesign/anta/elements` (client-side only). Reads
98
+ * `navigator.language` at render, so render client-side (not SSR-safe).
99
+ *
100
+ * @example
101
+ * ```tsx
102
+ * <InputTime label="Start" defaultValue="09:30" onValueChange={(_, { value }) => save(value)} />
103
+ * ```
104
+ */
105
+ export declare const InputTime: ({ label, hint, status, statusIcon, tone, size, round, value, defaultValue, locale, hour12, min, max, name, disabled, required, icon, clearable, dimActions, trailing, onValueChange, onClearInput, children, className, style, ...rest }: InputTimeProps) => any;
@@ -0,0 +1,110 @@
1
+ import { jsx, jsxs } from "@antadesign/anta/jsx-runtime";
2
+ import { nativeStateChange, toneStyle, roundStyle } from "../anta_helpers";
3
+ import { Button } from "./Button";
4
+ import { Icon } from "./Icon";
5
+ const presence = (on) => on ? "" : void 0;
6
+ const isStringish = (n) => typeof n === "string" || typeof n === "number";
7
+ const attrsOf = (e) => {
8
+ const el = e?.target ?? {};
9
+ const value = el.value ?? "";
10
+ return {
11
+ value,
12
+ name: el.getAttribute?.("name") ?? void 0,
13
+ empty: !value,
14
+ valid: el.validity ? el.validity.valid : void 0,
15
+ validationMessage: el.validationMessage ?? ""
16
+ };
17
+ };
18
+ const STATUS_ICON = {
19
+ critical: "warning-diamond",
20
+ warning: "warning-triangle",
21
+ success: "circle-check",
22
+ info: "info",
23
+ brand: "circle-small-solid"
24
+ };
25
+ const InputTime = ({
26
+ label,
27
+ hint,
28
+ status,
29
+ statusIcon,
30
+ tone,
31
+ size,
32
+ round,
33
+ value,
34
+ defaultValue,
35
+ locale,
36
+ hour12,
37
+ min,
38
+ max,
39
+ name,
40
+ disabled,
41
+ required,
42
+ icon,
43
+ clearable,
44
+ dimActions,
45
+ trailing,
46
+ onValueChange,
47
+ onClearInput,
48
+ children,
49
+ className,
50
+ style,
51
+ ...rest
52
+ }) => {
53
+ const statusTone = status && status !== "neutral" ? status : void 0;
54
+ const glyph = statusIcon === void 0 ? statusTone ? STATUS_ICON[statusTone] : void 0 : statusIcon;
55
+ return /* @__PURE__ */ jsxs(
56
+ "a-input-time",
57
+ {
58
+ size: size && size !== "medium" ? size : void 0,
59
+ round: round ? "" : void 0,
60
+ value,
61
+ defaultvalue: defaultValue,
62
+ locale,
63
+ hour12: hour12 === void 0 ? void 0 : hour12 ? "true" : "false",
64
+ min,
65
+ max,
66
+ status: statusTone,
67
+ tone: tone || void 0,
68
+ name,
69
+ disabled: presence(disabled),
70
+ required: presence(required),
71
+ "dim-actions": presence(dimActions),
72
+ "aria-invalid": status === "critical" ? "true" : void 0,
73
+ oninput: onValueChange ? (e) => onValueChange(e, attrsOf(e)) : void 0,
74
+ onchange: onValueChange ? (e) => onValueChange(e, attrsOf(e)) : void 0,
75
+ onclearinput: onClearInput ? (e) => onClearInput(nativeStateChange(e).event) : void 0,
76
+ class: className,
77
+ style: roundStyle(round, "--input-time-round", toneStyle(tone, "--input-time-tone-source", style)),
78
+ ...rest,
79
+ children: [
80
+ label != null && (isStringish(label) ? /* @__PURE__ */ jsx("span", { slot: "label", children: label }) : /* @__PURE__ */ jsx("span", { slot: "label", style: { display: "contents" }, children: label })),
81
+ icon !== false && /* @__PURE__ */ jsx("span", { slot: "leading", style: { display: "contents" }, children: /* @__PURE__ */ jsx(Icon, { shape: icon ?? "clock" }) }),
82
+ clearable && // Real <a-button> in the element's `clear` slot; the element owns its
83
+ // visibility (shown only when filled). It fires `clearrequest`, which the
84
+ // element turns into clear(). CONTRACT: `data-custom-event` MUST match
85
+ // CLEAR_TRIGGER in src/elements/a-input-time.ts (string duplicated, not
86
+ // shared, to keep the wrapper/element decoupled).
87
+ /* @__PURE__ */ jsx("span", { slot: "clear", style: { display: "contents" }, children: /* @__PURE__ */ jsx(
88
+ Button,
89
+ {
90
+ priority: "tertiary",
91
+ size,
92
+ round: !!round,
93
+ icon: "x",
94
+ "aria-label": "Clear",
95
+ "data-custom-event": "clearrequest"
96
+ }
97
+ ) }),
98
+ trailing != null && /* @__PURE__ */ jsx("span", { slot: "trailing", style: { display: "contents" }, children: trailing }),
99
+ hint != null && /* @__PURE__ */ jsxs("span", { slot: "hint", style: { display: "contents" }, children: [
100
+ glyph && /* @__PURE__ */ jsx(Icon, { shape: glyph, "aria-hidden": "true" }),
101
+ /* @__PURE__ */ jsx("span", { children: hint })
102
+ ] }),
103
+ children
104
+ ]
105
+ }
106
+ );
107
+ };
108
+ export {
109
+ InputTime
110
+ };
@@ -22,6 +22,11 @@ export interface MenuProps extends BaseProps {
22
22
  /** Gap in pixels between the trigger and the menu.
23
23
  * @defaultValue 4 */
24
24
  offset?: number;
25
+ /** Size the menu to its content instead of flooring its width to the trigger.
26
+ * A root menu is never narrower than its trigger by default; set this for a
27
+ * content menu under a wide trigger (e.g. a calendar below a full-width field)
28
+ * so it wraps its content and left-aligns under the trigger. */
29
+ autoWidth?: boolean;
25
30
  /** Controlled open state. Omit for the default **uncontrolled** menu (it
26
31
  * opens/closes itself via its triggers). Pass a boolean to **control** it:
27
32
  * the menu's visibility follows `open`, and user dismiss (Esc, outside-click,
@@ -53,6 +58,9 @@ export interface MenuProps extends BaseProps {
53
58
  onactivedescendant?: (e: CustomEvent<{
54
59
  id: string | null;
55
60
  }>) => void;
61
+ /** ARIA role of the menu surface.
62
+ * @defaultValue menu */
63
+ role?: string;
56
64
  /** The menu's contents: `MenuItem`, `MenuSeparator`, `MenuGroup`, or any
57
65
  * custom element. */
58
66
  children?: React.ReactNode;
@@ -83,4 +91,4 @@ export interface MenuProps extends BaseProps {
83
91
  * </Menu>
84
92
  * ```
85
93
  */
86
- export declare const Menu: ({ placement, context, coord, nohover, offset, open, onStateChange, round, className, style, children, ...rest }: MenuProps) => any;
94
+ export declare const Menu: ({ placement, context, coord, nohover, offset, autoWidth, open, onStateChange, round, role, className, style, children, ...rest }: MenuProps) => any;
@@ -6,9 +6,11 @@ const Menu = ({
6
6
  coord,
7
7
  nohover,
8
8
  offset,
9
+ autoWidth,
9
10
  open,
10
11
  onStateChange,
11
12
  round,
13
+ role = "menu",
12
14
  className,
13
15
  style,
14
16
  children,
@@ -22,6 +24,7 @@ const Menu = ({
22
24
  coord: coord ? "" : void 0,
23
25
  nohover: nohover ? "" : void 0,
24
26
  offset: offset != null ? String(offset) : void 0,
27
+ autowidth: autoWidth ? "" : void 0,
25
28
  state: open === void 0 ? void 0 : open ? "open" : "closed",
26
29
  onstatechange: onStateChange ? (e) => {
27
30
  const { event, detail } = nativeStateChange(e);
@@ -32,7 +35,7 @@ const Menu = ({
32
35
  });
33
36
  } : void 0,
34
37
  round: round ? "" : void 0,
35
- role: "menu",
38
+ role,
36
39
  "aria-orientation": "vertical",
37
40
  class: className,
38
41
  style: roundStyle(round, "--menu-round", style),
@@ -75,6 +75,14 @@ export interface MenuItemProps extends BaseProps {
75
75
  value?: string | number;
76
76
  label?: React.ReactNode;
77
77
  }) => void;
78
+ /** Raw `mousedown` on the row. Mainly to `preventDefault()` so the row doesn't
79
+ * take focus on a mouse press — e.g. a combobox option keeping focus in its
80
+ * input field while the click still selects. */
81
+ onMouseDown?: (event: any) => void;
82
+ /** ARIA role override. Defaults to the role implied by `selectionIndicator`
83
+ * (`menuitem` / `menuitemcheckbox` / `menuitemradio`); set it to reparent the
84
+ * row under a different container role — e.g. `option` inside a `listbox`. */
85
+ role?: string;
78
86
  /** Item content. With `label` set, children are extra content — most
79
87
  * notably the nested `<Menu>` for a submenu parent. */
80
88
  children?: React.ReactNode;
@@ -99,4 +107,4 @@ export interface MenuItemProps extends BaseProps {
99
107
  * </MenuItem>
100
108
  * ```
101
109
  */
102
- export declare const MenuItem: ({ icon, label, hint, kbd, iconTrailing, disabled, selected, selectionIndicator, indeterminate, indicator, tone, toneSelected, submenu, value, onSelect, className, style, children, ...rest }: MenuItemProps) => any;
110
+ export declare const MenuItem: ({ icon, label, hint, kbd, iconTrailing, disabled, selected, selectionIndicator, indeterminate, indicator, tone, toneSelected, submenu, value, onSelect, role: roleOverride, className, style, children, ...rest }: MenuItemProps) => any;
@@ -16,6 +16,7 @@ const MenuItem = ({
16
16
  submenu,
17
17
  value,
18
18
  onSelect,
19
+ role: roleOverride,
19
20
  className,
20
21
  style,
21
22
  children,
@@ -30,7 +31,7 @@ const MenuItem = ({
30
31
  return /* @__PURE__ */ jsxs(
31
32
  "a-menu-item",
32
33
  {
33
- role,
34
+ role: roleOverride ?? role,
34
35
  tabIndex: 0,
35
36
  disabled: disabled ? "" : void 0,
36
37
  selected: keepTint ? "" : void 0,
@@ -97,8 +97,10 @@ export interface TriggerState {
97
97
  }
98
98
  /** Snapshot passed as the 2nd argument to `onValueChange` — describes *what*
99
99
  * changed, alongside the new full value in the 1st argument. A discriminated
100
- * union: a row toggle carries `value` + `option`; the "Select all" row carries
101
- * `all: true` instead. Narrow on `'all' in attrs` before reading `option`. */
100
+ * union: a row toggle carries `value` + `option`; a bulk change (the "Select all"
101
+ * row, or the `clearable` "Clear" footer) carries `all: true` instead. **Always
102
+ * narrow on `'all' in attrs` before reading `option`** — the `all` variant fires
103
+ * for single-select too (via `clearable`), not just multiple. */
102
104
  export type SelectChangeAttrs = {
103
105
  /** The option value that changed — the chosen value (single) or the toggled
104
106
  * row (multiple). */
@@ -108,9 +110,11 @@ export type SelectChangeAttrs = {
108
110
  /** Multiple only: whether the change turned selection **on** (true) or off. */
109
111
  selected?: boolean;
110
112
  } | {
111
- /** Marks the change as coming from the "Select all" row (multiple only). */
113
+ /** Marks the change as a bulk one with no single `option`: the "Select all"
114
+ * row (multiple), or the `clearable` "Clear" footer (single or multiple). */
112
115
  all: true;
113
- /** Whether Select-all turned everything **on** (true) or cleared it. */
116
+ /** Whether the bulk change turned everything **on** (true) or cleared it
117
+ * (false — always false for a "Clear"). */
114
118
  selected: boolean;
115
119
  };
116
120
  /** Props shared by both selection modes, intersected into `SelectProps`. Exported
@@ -139,6 +143,11 @@ export interface SelectCommonProps extends Omit<BaseProps, 'children'> {
139
143
  * `leading` slot). With a custom `renderTrigger`, it's passed through as
140
144
  * `state.icon` instead — the consumer places it. */
141
145
  icon?: IconShape;
146
+ /** Arbitrary content for the default trigger's leading slot (piped straight to
147
+ * `Input`'s `leading`) — e.g. a key prefix before the value. Overrides the
148
+ * `icon`-derived glyph when both are set; include your own `<Icon>` if you want
149
+ * one alongside. Ignored with a custom `renderTrigger`. */
150
+ leading?: React.ReactNode;
142
151
  /** Field label, above the trigger (Input's `label`). */
143
152
  label?: string;
144
153
  /** Helper text under the field (Input's `hint`). */
@@ -167,13 +176,23 @@ export interface SelectCommonProps extends Omit<BaseProps, 'children'> {
167
176
  * option's **value / label / hint**. Pass a **function** `(option, query) =>
168
177
  * boolean` for custom matching (called per option; return `true` to keep it). */
169
178
  filter?: boolean | ((option: SelectOption, query: string) => boolean);
170
- /** `multiple` only: add a "Select all" row at the top that toggles every enabled
179
+ /** `multiple` only: a "Select all" row at the top that toggles every enabled
171
180
  * option (the currently-visible ones when a `filter` query is active); its box
172
- * shows the mixed state when only some are selected. */
181
+ * shows the mixed state when only some are selected. On by default in
182
+ * `multiple` mode — set `false` to drop the row (and with it the Alt/⌥-click
183
+ * isolate accelerator it gates).
184
+ * @defaultValue true */
173
185
  selectAll?: boolean;
174
186
  /** Label for the `selectAll` row.
175
187
  * @defaultValue Select all */
176
188
  selectAllLabel?: string;
189
+ /** Add a "Clear" row pinned in the menu **footer** that empties the selection
190
+ * (single → none, multiple → `[]`). Shown only while something is selected, so
191
+ * it never scrolls away in a long or filtered list. */
192
+ clearable?: boolean;
193
+ /** Label for the `clearable` footer row.
194
+ * @defaultValue Clear */
195
+ clearLabel?: string;
177
196
  /** Render the **content** of each option row yourself, replacing the built-in
178
197
  * `label`/`hint`/`icon` layout. Select still supplies the row box, click, ARIA,
179
198
  * and the selection indicator — you return only what goes *inside*. Read extra
@@ -194,8 +213,10 @@ export interface SelectCommonProps extends Omit<BaseProps, 'children'> {
194
213
  * the menu anchors to it (its own previous DOM sibling) and opens it on click,
195
214
  * so a fragment, multiple siblings, or a non-focusable wrapper will misanchor
196
215
  * (the menu warns in the console if the trigger has no focusable element). Give
197
- * the element `aria-haspopup="menu"` plus `aria-expanded={state.open}`. The field
198
- * props (`label`, `hint`, `size`, `status`, `placeholder`, `round`) don't apply. */
216
+ * the element `aria-haspopup="menu"` plus `aria-expanded={state.open}`. The custom
217
+ * trigger owns its own styling: the field props (`label`, `hint`, `size`, `status`,
218
+ * `placeholder`, `round`) and `className` / `style` apply to the *default* trigger
219
+ * only — put your own on the element you return. */
199
220
  renderTrigger?: (state: TriggerState) => React.ReactNode;
200
221
  /** Render content in the menu body when the (filtered) option list is empty —
201
222
  * a "no results" message, a loading indicator (gated on your own external