@antadesign/anta 0.3.11 → 0.3.13

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 (73) hide show
  1. package/README.md +31 -36
  2. package/dist/anta_helpers.js +5 -0
  3. package/dist/components/Button.d.ts +20 -64
  4. package/dist/components/Button.js +15 -27
  5. package/dist/components/ButtonCopy.d.ts +43 -25
  6. package/dist/components/ButtonCopy.js +46 -4
  7. package/dist/components/Expander.js +1 -1
  8. package/dist/components/InputDate.d.ts +9 -1
  9. package/dist/components/InputDate.js +4 -1
  10. package/dist/components/Menu.d.ts +6 -5
  11. package/dist/components/MenuItem.d.ts +2 -9
  12. package/dist/components/MenuItem.js +2 -16
  13. package/dist/components/MenuItemCopy.d.ts +22 -19
  14. package/dist/components/MenuItemCopy.js +38 -3
  15. package/dist/components/Select.d.ts +63 -51
  16. package/dist/components/Select.js +9 -8
  17. package/dist/components/SelectFaceted.d.ts +21 -16
  18. package/dist/components/SelectFaceted.js +2 -2
  19. package/dist/components/Text.d.ts +2 -2
  20. package/dist/components/Title.d.ts +2 -2
  21. package/dist/components/Tooltip.d.ts +1 -1
  22. package/dist/components/copy-props.d.ts +72 -0
  23. package/dist/components/copy-props.js +20 -0
  24. package/dist/components/select-options.d.ts +10 -5
  25. package/dist/components/select-options.js +1 -1
  26. package/dist/elements/a-button.css +1 -1
  27. package/dist/elements/a-button.d.ts +0 -7
  28. package/dist/elements/a-button.js +0 -23
  29. package/dist/elements/a-checkbox.css +1 -1
  30. package/dist/elements/a-copy.css +1 -0
  31. package/dist/elements/a-copy.d.ts +16 -0
  32. package/dist/elements/a-copy.js +148 -0
  33. package/dist/elements/a-expander.css +1 -1
  34. package/dist/elements/a-expander.js +1 -18
  35. package/dist/elements/a-input-time.css +1 -1
  36. package/dist/elements/a-input.css +1 -1
  37. package/dist/elements/a-input.js +2 -2
  38. package/dist/elements/a-menu-item.css +1 -1
  39. package/dist/elements/a-menu-item.d.ts +6 -6
  40. package/dist/elements/a-menu-item.js +0 -4
  41. package/dist/elements/a-menu.d.ts +6 -2
  42. package/dist/elements/a-menu.js +67 -31
  43. package/dist/elements/a-radio-group.css +1 -1
  44. package/dist/elements/a-radio-group.d.ts +1 -0
  45. package/dist/elements/a-radio-group.js +11 -2
  46. package/dist/elements/a-radio.css +1 -1
  47. package/dist/elements/a-tabs.css +1 -1
  48. package/dist/elements/a-tabs.d.ts +1 -0
  49. package/dist/elements/a-tabs.js +35 -6
  50. package/dist/elements/a-tag.css +1 -1
  51. package/dist/elements/a-text.css +1 -1
  52. package/dist/elements/a-title.css +1 -1
  53. package/dist/elements/a-tooltip.js +1 -1
  54. package/dist/elements/copy-behavior.d.ts +6 -5
  55. package/dist/elements/index.d.ts +1 -0
  56. package/dist/elements/index.js +3 -0
  57. package/dist/elements/key-activation.d.ts +5 -9
  58. package/dist/elements/key-activation.js +0 -1
  59. package/dist/general_types.d.ts +32 -41
  60. package/dist/index.d.ts +3 -2
  61. package/dist/jsx-runtime.d.ts +2 -1
  62. package/dist/reset.css +1 -1
  63. package/dist/theme-anta.css +1 -0
  64. package/dist/tokens.css +1 -1
  65. package/package.json +2 -1
  66. package/dist/components/Card.module.css +0 -35
  67. package/dist/components/Progress.module.css +0 -0
  68. package/dist/components/Radio.d.ts +0 -37
  69. package/dist/components/Radio.js +0 -33
  70. package/dist/components/Tab.d.ts +0 -37
  71. package/dist/components/Tab.js +0 -5
  72. package/dist/components/Tabs.module.css +0 -1
  73. package/dist/components/Text.module.css +0 -0
@@ -1,6 +1,5 @@
1
1
  import type { BaseProps } from '../general_types';
2
2
  import type { IconShape } from '../elements/a-icon.shapes';
3
- import type { CopyMode } from './Button';
4
3
  /** Props shared by every menu item, link or not. */
5
4
  export interface MenuItemCommonProps extends BaseProps {
6
5
  /** Leading icon shape. */
@@ -84,12 +83,6 @@ export type MenuItemLinkMode = {
84
83
  selectionIndicator?: never;
85
84
  indeterminate?: never;
86
85
  indicator?: never;
87
- copy?: never;
88
- copyNode?: never;
89
- copyUrl?: never;
90
- copyWithUrl?: never;
91
- onCopied?: never;
92
- onCopyRequest?: never;
93
86
  };
94
87
  export type MenuItemActionMode = {
95
88
  href?: never;
@@ -121,7 +114,7 @@ export type MenuItemActionMode = {
121
114
  * passive (aria-hidden, no pointer events) so the row owns the click. */
122
115
  indicator?: React.ReactNode;
123
116
  };
124
- export type MenuItemProps = MenuItemCommonProps & (MenuItemLinkMode | (MenuItemActionMode & CopyMode));
117
+ export type MenuItemProps = MenuItemCommonProps & (MenuItemLinkMode | MenuItemActionMode);
125
118
  /**
126
119
  * MenuItem — a single selectable row inside a `Menu`. Composes a leading
127
120
  * `icon`, a `label` (or `children`), an optional trailing `kbd` hint, and an
@@ -142,4 +135,4 @@ export type MenuItemProps = MenuItemCommonProps & (MenuItemLinkMode | (MenuItemA
142
135
  * </MenuItem>
143
136
  * ```
144
137
  */
145
- export declare const MenuItem: ({ icon, label, hint, kbd, iconTrailing, disabled, selected, selectionIndicator, indeterminate, indicator, tone, toneSelected, submenu, value, onSelect, href, target, rel, download, ping, copy, copyNode, copyUrl, copyWithUrl, onCopied, onCopyRequest, role: roleOverride, className, style, children, ...rest }: MenuItemProps) => any;
138
+ export declare const MenuItem: ({ icon, label, hint, kbd, iconTrailing, disabled, selected, selectionIndicator, indeterminate, indicator, tone, toneSelected, submenu, value, onSelect, href, target, rel, download, ping, role: roleOverride, className, style, children, ...rest }: MenuItemProps) => any;
@@ -1,5 +1,5 @@
1
1
  import { jsx, jsxs } from "@antadesign/anta/jsx-runtime";
2
- import { toneStyle, nativeStateChange } from "../anta_helpers";
2
+ import { toneStyle } from "../anta_helpers";
3
3
  const MenuItem = ({
4
4
  icon,
5
5
  label,
@@ -21,20 +21,13 @@ const MenuItem = ({
21
21
  rel,
22
22
  download,
23
23
  ping,
24
- copy,
25
- copyNode,
26
- copyUrl,
27
- copyWithUrl,
28
- onCopied,
29
- onCopyRequest,
30
24
  role: roleOverride,
31
25
  className,
32
26
  style,
33
27
  children,
34
28
  ...rest
35
29
  }) => {
36
- const isCopy = copy != null || copyNode != null || copyUrl === true;
37
- const leadingIcon = icon ?? (isCopy ? "copy" : void 0);
30
+ const leadingIcon = icon;
38
31
  const checkable = selectionIndicator === "checkbox" || selectionIndicator === "radio" || selectionIndicator === "check";
39
32
  const role = selectionIndicator === "checkbox" ? "menuitemcheckbox" : selectionIndicator === "radio" || selectionIndicator === "check" ? "menuitemradio" : "menuitem";
40
33
  const ariaChecked = !checkable ? void 0 : selectionIndicator === "checkbox" && indeterminate ? "mixed" : selected ? "true" : "false";
@@ -84,13 +77,6 @@ const MenuItem = ({
84
77
  "aria-haspopup": submenu ? "menu" : void 0,
85
78
  "aria-disabled": disabled ? "true" : void 0,
86
79
  onmenuselect: onSelect ? (e) => onSelect(e, { value, label }) : void 0,
87
- copy: copy != null ? copy : void 0,
88
- "copy-node": copyNode === true ? "" : typeof copyNode === "string" ? copyNode : void 0,
89
- "copy-url": copyUrl ? "" : void 0,
90
- "copy-with-url": copyWithUrl ? "" : void 0,
91
- "data-copy-node-button": copyNode != null && copyNode !== false ? "" : void 0,
92
- oncopydone: onCopied ? (e) => onCopied(nativeStateChange(e).detail?.ok ?? false) : void 0,
93
- oncopyrequest: onCopyRequest ? () => onCopyRequest() : void 0,
94
80
  class: className,
95
81
  ...rest,
96
82
  children: [
@@ -1,34 +1,37 @@
1
1
  import { type MenuItemCommonProps, type MenuItemActionMode } from './MenuItem';
2
- import type { CopyMode } from './Button';
3
- /** The action-mode menu-item props plus a required copy target (a copying row
4
- * must copy something). The target intersection drops `CopyMode`'s "no copy
5
- * prop" member. */
6
- export type MenuItemCopyProps = MenuItemCommonProps & MenuItemActionMode & CopyMode & ({
7
- copy: string;
8
- } | {
9
- copyNode: boolean | string;
10
- } | {
11
- copyUrl: true;
12
- });
2
+ import { type CopyTarget } from './copy-props';
3
+ /** Action-mode menu-item props plus a required copy target (a copying row must
4
+ * copy something). The action-mode props that don't fit a copy row are pinned to
5
+ * `never`: `submenu` (a copy row has no flyout) and the selection-indicator axis
6
+ * (`selectionIndicator` / `indeterminate` / `indicator` — a copy row isn't a
7
+ * checkable option). Pinning (not `Omit`) keeps the intersection expanding fully
8
+ * in the docs props table. */
9
+ export type MenuItemCopyProps = MenuItemCommonProps & MenuItemActionMode & {
10
+ submenu?: never;
11
+ selectionIndicator?: never;
12
+ indeterminate?: never;
13
+ indicator?: never;
14
+ } & CopyTarget;
13
15
  /**
14
16
  * Copying menu item — a `MenuItem` preset for copy-to-clipboard rows inside a
15
17
  * `Menu`. Set `copy` for a literal string, `copyNode` to copy a DOM region, or
16
18
  * `copyUrl` to copy the current page URL. For content computed on demand, keep
17
- * `copy` reactive and refresh it in `onCopyRequest` (fired on pointerdown). The
18
- * `<a-menu-item>` element performs the write when the row is chosen; this wrapper
19
+ * `copy` reactive and refresh it in `onCopyRequest` (fired on pointerdown / keydown).
20
+ *
21
+ * It composes a plain `<MenuItem>` with a slotted `<a-copy>` child that performs
22
+ * the write when the row is chosen — the item itself carries no copy behavior.
23
+ * The row is kept open (`data-menu-open`) so the feedback shows: this wrapper
19
24
  * flips the leading icon to a check / ✕ and retones for ~2s (see `useCopyFeedback`).
20
25
  *
21
- * The menu closes on select by default, so the feedback shows only when the row
22
- * is kept open (`data-menu-open`). Defaults `label` to "Copy" and the leading
23
- * `icon` to `copy`; override either. Other props pass through — `tone`, `kbd`,
24
- * `onCopied`.
26
+ * Defaults `label` to "Copy" and the leading `icon` to `copy`; override either.
27
+ * Other props pass through — `tone`, `kbd`, `onCopied`.
25
28
  *
26
29
  * @example
27
30
  * ```tsx
28
31
  * <Menu>
29
32
  * <MenuItemCopy copy={shareUrl} label="Copy link" kbd="⌘C" />
30
- * <MenuItemCopy copyNode=".invoice" label="Copy invoice" data-menu-open />
33
+ * <MenuItemCopy copyNode=".invoice" label="Copy invoice" />
31
34
  * </Menu>
32
35
  * ```
33
36
  */
34
- export declare const MenuItemCopy: ({ label, icon, tone, onCopied, ...rest }: MenuItemCopyProps) => any;
37
+ export declare const MenuItemCopy: ({ label, icon, tone, onCopied, onCopyRequest, copy, copyNode, copyUrl, copyWithUrl, children, ...rest }: MenuItemCopyProps) => any;
@@ -1,9 +1,44 @@
1
- import { jsx } from "@antadesign/anta/jsx-runtime";
1
+ import { jsx, jsxs } from "@antadesign/anta/jsx-runtime";
2
2
  import { MenuItem } from "./MenuItem";
3
3
  import { useCopyFeedback } from "../anta_helpers";
4
- const MenuItemCopy = ({ label = "Copy", icon = "copy", tone, onCopied, ...rest }) => {
4
+ import { copyElementProps, isNodeCopy } from "./copy-props";
5
+ const MenuItemCopy = ({
6
+ label = "Copy",
7
+ icon = "copy",
8
+ tone,
9
+ onCopied,
10
+ onCopyRequest,
11
+ copy,
12
+ copyNode,
13
+ copyUrl,
14
+ copyWithUrl,
15
+ children,
16
+ ...rest
17
+ }) => {
5
18
  const { shownIcon, shownTone, handleCopied } = useCopyFeedback(icon, tone, onCopied);
6
- return /* @__PURE__ */ jsx(MenuItem, { label, icon: shownIcon, tone: shownTone, onCopied: handleCopied, ...rest });
19
+ const copyAttrs = copyElementProps({
20
+ copy,
21
+ copyNode,
22
+ copyUrl,
23
+ copyWithUrl,
24
+ onCopyRequest,
25
+ onCopied: handleCopied
26
+ });
27
+ return /* @__PURE__ */ jsxs(
28
+ MenuItem,
29
+ {
30
+ label,
31
+ icon: shownIcon,
32
+ tone: shownTone,
33
+ "data-menu-open": "",
34
+ "data-copy-node-button": isNodeCopy({ copyNode }) ? "" : void 0,
35
+ ...rest,
36
+ children: [
37
+ children,
38
+ /* @__PURE__ */ jsx("a-copy", { ...copyAttrs })
39
+ ]
40
+ }
41
+ );
7
42
  };
8
43
  export {
9
44
  MenuItemCopy
@@ -1,15 +1,23 @@
1
1
  import type { BaseProps } from '../general_types';
2
2
  import type { IconShape } from '../elements/a-icon.shapes';
3
3
  import { type MenuProps } from './Menu';
4
+ /** The type an option `value` may take: `string`, `number`, or `boolean`. Selection
5
+ * compares values with `===`. Object-shaped values are out of scope; give the option a
6
+ * stable primitive `value` and read the object back off `attrs.option` (or, in
7
+ * `SelectFaceted`, use a `custom` facet). */
8
+ export type OptionValue = string | number | boolean;
4
9
  /** One option in a `<Select>`. Pass a bare string as shorthand for
5
10
  * `{ value: s, label: s }`. Carries an index signature so you can attach
6
11
  * arbitrary fields (a `ranAt` date, a `status`, …) and read them back in
7
- * `renderOption`; the built-in filter still matches on `value`/`label`/`hint`. */
8
- export interface SelectOption {
9
- /** The option's value — its identity, what `value` / `defaultValue` name, and what
10
- * `onValueChange` reports. Unique across the whole `options` tree (selection is
12
+ * `renderOption`; the built-in filter still matches on `value`/`label`/`hint`.
13
+ *
14
+ * Generic in the value type `V` (defaults to `string`): `Select` infers `V` from your
15
+ * `options`, so `{ value: 365 }` round-trips as `number` through `onValueChange`. */
16
+ export interface SelectOption<V extends OptionValue = string> {
17
+ /** The option's value (`V`) — its identity, what `value` / `defaultValue` name, and
18
+ * what `onValueChange` reports. Unique across the whole `options` tree (selection is
11
19
  * value-keyed and global across groups / submenus). */
12
- value: string;
20
+ value: V;
13
21
  /** Visible label. Defaults to `value`. */
14
22
  label?: string;
15
23
  /** Secondary text under the label (the option row's `hint`). */
@@ -32,11 +40,11 @@ export interface SelectOption {
32
40
  /** A titled group of options rendered **inline** under a heading (like `MenuGroup`).
33
41
  * Selection stays global — a group only organizes; its heading isn't selectable.
34
42
  * `disabled` cascades to every descendant. Nest groups / submenus freely. */
35
- export interface SelectGroup {
43
+ export interface SelectGroup<V extends OptionValue = string> {
36
44
  /** The section heading (non-interactive). */
37
45
  label: string;
38
46
  /** The grouped items — options, or further groups / submenus. */
39
- options: SelectItem[];
47
+ options: SelectItem<V>[];
40
48
  /** Disable the whole group (cascades to all descendants). */
41
49
  disabled?: boolean;
42
50
  }
@@ -44,13 +52,13 @@ export interface SelectGroup {
44
52
  * Navigation only — the parent row opens the submenu and is never itself selectable.
45
53
  * While a filter query is active the tree flattens and a submenu collapses into a
46
54
  * group (its `label` becomes the heading). `disabled` cascades to descendants. */
47
- export interface SelectSubmenu {
55
+ export interface SelectSubmenu<V extends OptionValue = string> {
48
56
  /** The parent row's label — and the group heading when filtering flattens it. */
49
57
  label: string;
50
58
  /** Leading icon on the parent row. */
51
59
  icon?: IconShape;
52
60
  /** The submenu's items — options, or further groups / submenus. */
53
- submenu: SelectItem[];
61
+ submenu: SelectItem<V>[];
54
62
  /** Disable the whole branch (cascades to all descendants). */
55
63
  disabled?: boolean;
56
64
  }
@@ -58,13 +66,13 @@ export interface SelectSubmenu {
58
66
  * inline `SelectGroup`, or a flyout `SelectSubmenu`. Discriminated by shape — an
59
67
  * `options` array is a group, a `submenu` array is a submenu, else it's an option.
60
68
  * (`options` / `submenu` are therefore reserved keys on an option object.) */
61
- export type SelectItem = string | SelectOption | SelectGroup | SelectSubmenu;
69
+ export type SelectItem<V extends OptionValue = string> = string | SelectOption<V> | SelectGroup<V> | SelectSubmenu<V>;
62
70
  /** Per-row snapshot passed to `renderOption` / `renderIndicator`. Everything here
63
71
  * is known at render time; the combobox "active" cursor is deliberately absent
64
72
  * (it's a live element state the DOM owns, not a render-time value). */
65
- export interface OptionState {
73
+ export interface OptionState<V extends OptionValue = string> {
66
74
  /** The option's value. */
67
- value: string;
75
+ value: V;
68
76
  /** Whether this row is currently selected. */
69
77
  selected: boolean;
70
78
  /** Whether this row is disabled. */
@@ -81,15 +89,15 @@ export interface EmptyState {
81
89
  }
82
90
  /** Snapshot passed to `renderTrigger` so a custom trigger can reflect the current
83
91
  * selection and open state. */
84
- export interface TriggerState {
92
+ export interface TriggerState<V extends OptionValue = string> {
85
93
  /** Whether the menu is open — use it for `aria-expanded` and a chevron. */
86
94
  open: boolean;
87
- /** The current selection: a string (single), a string array (`multiple`), or
95
+ /** The current selection: a single value (single), an array (`multiple`), or
88
96
  * `undefined` when nothing is chosen. */
89
- value: string | string[] | undefined;
97
+ value: V | V[] | undefined;
90
98
  /** The resolved option objects for the current selection (empty when none).
91
99
  * `selected.length` is the multi-select count. */
92
- selected: SelectOption[];
100
+ selected: SelectOption<V>[];
93
101
  /** Whether the whole select is disabled. */
94
102
  disabled: boolean;
95
103
  /** The `icon` shape passed to `Select`, if any — hand it to your trigger
@@ -102,12 +110,12 @@ export interface TriggerState {
102
110
  * row, or the `clearable` "Clear" footer) carries `all: true` instead. **Always
103
111
  * narrow on `'all' in attrs` before reading `option`** — the `all` variant fires
104
112
  * for single-select too (via `clearable`), not just multiple. */
105
- export type SelectChangeAttrs = {
113
+ export type SelectChangeAttrs<V extends OptionValue = string> = {
106
114
  /** The option value that changed — the chosen value (single) or the toggled
107
115
  * row (multiple). */
108
- value: string;
116
+ value: V;
109
117
  /** The resolved option object for `value`. */
110
- option: SelectOption;
118
+ option: SelectOption<V>;
111
119
  /** Multiple only: whether the change turned selection **on** (true) or off. */
112
120
  selected?: boolean;
113
121
  } | {
@@ -121,17 +129,21 @@ export type SelectChangeAttrs = {
121
129
  /** Props shared by both selection modes, intersected into `SelectProps`. Exported
122
130
  * (and kept as an interface intersected — not a union base via `extends`) so its
123
131
  * members read as `Select`'s *own* props in the generated docs, not inherited. */
124
- export interface SelectCommonProps extends Omit<BaseProps, 'children'> {
132
+ export interface SelectCommonProps<V extends OptionValue = string> extends Omit<BaseProps, 'children'> {
125
133
  /** The options to choose from — bare strings, `SelectOption` objects, `SelectGroup`s
126
134
  * (inline titled sections), or `SelectSubmenu`s (flyout branches). Groups and
127
135
  * submenus nest and mix with plain options. Selection stays global (one `value`,
128
136
  * leaf options only); a filter query flattens the tree into grouped results.
129
137
  *
138
+ * `Select` infers its value type `V` from these options: `{ value: 365 }` makes
139
+ * `onValueChange` report `number`. A mix of value types widens `V` to the union.
140
+ *
130
141
  * Each leaf `value` is the option's identity and must be **unique across the whole
131
- * tree** — selection is value-keyed, so a value repeated in two sections is one
132
- * logical pick (both rows toggle together; the trigger resolves to the last). Dev
133
- * builds `console.warn` on a duplicate. */
134
- options: SelectItem[];
142
+ * tree** (selection is value-keyed, so a value repeated in two sections is one logical
143
+ * pick: both rows toggle together, the trigger resolves to the last). Values that
144
+ * stringify alike (`365` and `"365"`) also collide as row keys; dev builds
145
+ * `console.warn` on either. */
146
+ options: SelectItem<V>[];
135
147
  /** Preferred placement of the options menu relative to the trigger. The menu
136
148
  * auto-flips vertically and clamps horizontally when needed.
137
149
  * @defaultValue bottom-start */
@@ -183,7 +195,7 @@ export interface SelectCommonProps extends Omit<BaseProps, 'children'> {
183
195
  * type. `true` uses the built-in matcher — a case-insensitive substring of the
184
196
  * option's **value / label / hint**. Pass a **function** `(option, query) =>
185
197
  * boolean` for custom matching (called per option; return `true` to keep it). */
186
- filter?: boolean | ((option: SelectOption, query: string) => boolean);
198
+ filter?: boolean | ((option: SelectOption<V>, query: string) => boolean);
187
199
  /** `multiple` only: a "Select all" row at the top that toggles every enabled
188
200
  * option (the currently-visible ones when a `filter` query is active); its box
189
201
  * shows the mixed state when only some are selected. On by default in
@@ -208,13 +220,13 @@ export interface SelectCommonProps extends Omit<BaseProps, 'children'> {
208
220
  * `OptionState` (`value`/`selected`/`disabled`). Filtering still works (it
209
221
  * matches the option's `value`/`label`/`hint`), but match-highlighting is
210
222
  * skipped — your content owns its own display. */
211
- renderOption?: (option: SelectOption, state: OptionState) => React.ReactNode;
223
+ renderOption?: (option: SelectOption<V>, state: OptionState<V>) => React.ReactNode;
212
224
  /** Replace each row's selection **mark** with your own node, drawn at the
213
225
  * leading edge. The row stays the control (`role` + `aria-checked` from
214
226
  * `indicator` / `selection`); only the drawn mark changes, so pair it with an
215
227
  * `indicator` (`'check'` / `'radio'`) or `selection="multiple"` for the
216
228
  * semantics. Composes with `renderOption`. */
217
- renderIndicator?: (state: OptionState) => React.ReactNode;
229
+ renderIndicator?: (state: OptionState<V>) => React.ReactNode;
218
230
  /** Render your own trigger in place of the default field. Receives a
219
231
  * `TriggerState` (`open` / `value` / `selected` / `disabled` / `icon`) to drive
220
232
  * its look. **Return exactly one focusable element** (an Anta `Button`, say) —
@@ -225,7 +237,7 @@ export interface SelectCommonProps extends Omit<BaseProps, 'children'> {
225
237
  * trigger owns its own styling: the field props (`label`, `hint`, `size`, `status`,
226
238
  * `placeholder`, `round`) and `className` / `style` apply to the *default* trigger
227
239
  * only — put your own on the element you return. */
228
- renderTrigger?: (state: TriggerState) => React.ReactNode;
240
+ renderTrigger?: (state: TriggerState<V>) => React.ReactNode;
229
241
  /** Render content in the menu body when the (filtered) option list is empty —
230
242
  * a "no results" message, a loading indicator (gated on your own external
231
243
  * loading state), or a "create from the query" row. Receives an `EmptyState`
@@ -239,37 +251,37 @@ export interface SelectCommonProps extends Omit<BaseProps, 'children'> {
239
251
  * `<Select>` props. `selection` discriminates the value shape: single mode
240
252
  * (`value: string`) or `multiple` (`value: string[]`).
241
253
  */
242
- export type SelectProps = SelectCommonProps & ({
243
- /** Selection mode. `'single'` (the default) keeps `value` a string and
254
+ export type SelectProps<V extends OptionValue = string> = SelectCommonProps<V> & ({
255
+ /** Selection mode. `'single'` (the default) keeps `value` a single value and
244
256
  * closes the menu on pick. Switch to `'multiple'` for checkboxes + an
245
257
  * array value.
246
258
  * @defaultValue single */
247
259
  selection?: 'single';
248
- /** Controlled value — the `value` string of the selected option. When
249
- * provided, the consumer owns selection: the field follows this prop and a
250
- * pick only *requests* a change via `onValueChange` (reject by not updating).
251
- * Leave undefined for uncontrolled. */
252
- value?: string;
253
- /** Initial value (an option's `value` string) for the uncontrolled case — the
260
+ /** Controlled value — the `value` of the selected option. When provided, the
261
+ * consumer owns selection: the field follows this prop and a pick only
262
+ * *requests* a change via `onValueChange` (reject by not updating). Leave
263
+ * undefined for uncontrolled. */
264
+ value?: V;
265
+ /** Initial value (an option's `value`) for the uncontrolled case — the
254
266
  * wrapper then owns it. */
255
- defaultValue?: string;
267
+ defaultValue?: V;
256
268
  /** Fires after the selection changes, with the new value and a
257
269
  * `{ value, option }` snapshot. Select has no discrete element state, so
258
270
  * there is no cancelable `onStateChange` (see the Input event-model note). */
259
- onValueChange?: (value: string, attrs: SelectChangeAttrs) => void;
271
+ onValueChange?: (value: V, attrs: SelectChangeAttrs<V>) => void;
260
272
  } | {
261
273
  /** Multi-select: checkboxes on every row, the menu stays open while
262
274
  * toggling, the field shows an "N selected" count, and `value` is an array. */
263
275
  selection: 'multiple';
264
- /** Controlled values — the `value` strings of the selected options (see the
276
+ /** Controlled values — the `value`s of the selected options (see the
265
277
  * single-select `value` note). */
266
- value?: string[];
267
- /** Initial values (option `value` strings) for the uncontrolled case. */
268
- defaultValue?: string[];
278
+ value?: V[];
279
+ /** Initial values (option `value`s) for the uncontrolled case. */
280
+ defaultValue?: V[];
269
281
  /** Fires after any toggle, with the new value array and a `{ value, option,
270
282
  * selected }` snapshot of the row that changed (or `{ all: true }` for the
271
283
  * Select-all row). */
272
- onValueChange?: (value: string[], attrs: SelectChangeAttrs) => void;
284
+ onValueChange?: (value: V[], attrs: SelectChangeAttrs<V>) => void;
273
285
  });
274
286
  /** Rolled-up selection of a group / submenu subtree: `'none'` / `'all'` of the
275
287
  * descendant leaves selected, or `'some'` in between. On `SelectedGroup` /
@@ -277,26 +289,26 @@ export type SelectProps = SelectCommonProps & ({
277
289
  export type SelectionState = 'none' | 'some' | 'all';
278
290
  /** A leaf option annotated with its current selection — a `SelectOption` plus
279
291
  * `selected`. Returned by {@link optionsWithSelection}. */
280
- export type SelectedOption = SelectOption & {
292
+ export type SelectedOption<V extends OptionValue = string> = SelectOption<V> & {
281
293
  selected: boolean;
282
294
  };
283
295
  /** A group with its descendants annotated and a rolled-up `selectionState`.
284
296
  * Returned by {@link optionsWithSelection}. */
285
- export interface SelectedGroup extends Omit<SelectGroup, 'options'> {
286
- options: SelectedItem[];
297
+ export interface SelectedGroup<V extends OptionValue = string> extends Omit<SelectGroup<V>, 'options'> {
298
+ options: SelectedItem<V>[];
287
299
  selectionState: SelectionState;
288
300
  }
289
301
  /** A submenu with its descendants annotated and a rolled-up `selectionState`.
290
302
  * Returned by {@link optionsWithSelection}. */
291
- export interface SelectedSubmenu extends Omit<SelectSubmenu, 'submenu'> {
292
- submenu: SelectedItem[];
303
+ export interface SelectedSubmenu<V extends OptionValue = string> extends Omit<SelectSubmenu<V>, 'submenu'> {
304
+ submenu: SelectedItem<V>[];
293
305
  selectionState: SelectionState;
294
306
  }
295
307
  /** One node of the tree from {@link optionsWithSelection}: a leaf `SelectedOption`
296
308
  * (with `selected`), or a `SelectedGroup` / `SelectedSubmenu` (annotated children
297
309
  * + rolled-up `selectionState`). Mirrors `SelectItem` minus the bare-string
298
310
  * shorthand — strings are normalized to `SelectedOption`. */
299
- export type SelectedItem = SelectedOption | SelectedGroup | SelectedSubmenu;
311
+ export type SelectedItem<V extends OptionValue = string> = SelectedOption<V> | SelectedGroup<V> | SelectedSubmenu<V>;
300
312
  /**
301
313
  * Project a `Select` `options` tree onto a set of selected values: returns a mirror
302
314
  * of the tree with every leaf marked `selected` and every group / submenu carrying a
@@ -319,7 +331,7 @@ export type SelectedItem = SelectedOption | SelectedGroup | SelectedSubmenu;
319
331
  * // { value: 'eng-fe', label: 'Frontend', selected: false }, … ] }
320
332
  * ```
321
333
  */
322
- export declare function optionsWithSelection(options: SelectItem[], values: string | string[] | undefined): SelectedItem[];
334
+ export declare function optionsWithSelection<V extends OptionValue = string>(options: SelectItem<V>[], values: V | V[] | undefined): SelectedItem<V>[];
323
335
  /**
324
336
  * `<Select>` — a single- or multi-select dropdown, composed from `<Input>` (a
325
337
  * read-only trigger) and `<Menu>` (the options). `selection` sets behaviour:
@@ -345,4 +357,4 @@ export declare function optionsWithSelection(options: SelectItem[], values: stri
345
357
  * />
346
358
  * ```
347
359
  */
348
- export declare const Select: (props: SelectProps) => any;
360
+ export declare const Select: <V extends OptionValue = string>(props: SelectProps<V>) => any;
@@ -96,9 +96,10 @@ const Select = (props) => {
96
96
  collectLeaves(options, void 0, false, allLeaves);
97
97
  const seenValues = /* @__PURE__ */ new Set();
98
98
  for (const l of allLeaves) {
99
- if (seenValues.has(l.opt.value))
100
- console.warn(`[anta] <Select> duplicate option value=${JSON.stringify(l.opt.value)} \u2014 values must be unique.`);
101
- seenValues.add(l.opt.value);
99
+ const key = String(l.opt.value);
100
+ if (seenValues.has(key))
101
+ console.warn(`[anta] <Select> duplicate option value=${JSON.stringify(l.opt.value)} \u2014 values must be unique (compared as strings for row keys).`);
102
+ seenValues.add(key);
102
103
  }
103
104
  const byValue = new Map(allLeaves.map((l) => [l.opt.value, l.opt]));
104
105
  const filtering = filter !== void 0 && filter !== false;
@@ -113,11 +114,11 @@ const Select = (props) => {
113
114
  const selectedOptions = selectedValues.map((v) => byValue.get(v)).filter(Boolean);
114
115
  let display = "";
115
116
  if (multiple) {
116
- if (selectedOptions.length === 1) display = selectedOptions[0].label ?? selectedOptions[0].value;
117
+ if (selectedOptions.length === 1) display = selectedOptions[0].label ?? String(selectedOptions[0].value);
117
118
  else if (selectedOptions.length > 1) display = `${selectedOptions.length} selected`;
118
119
  } else if (currentRaw != null) {
119
120
  const o = byValue.get(currentRaw);
120
- display = o ? o.label ?? o.value : "";
121
+ display = o ? o.label ?? String(o.value) : "";
121
122
  }
122
123
  const choose = (o, e) => {
123
124
  if (multiple) {
@@ -166,7 +167,7 @@ const Select = (props) => {
166
167
  selectionIndicator: menuItemIndicator,
167
168
  ...ariaSelectable,
168
169
  indicator: customMark ?? void 0,
169
- label: custom ? void 0 : highlight(o.label ?? o.value, queryRe),
170
+ label: custom ? void 0 : highlight(o.label ?? String(o.value), queryRe),
170
171
  hint: custom ? void 0 : o.hint ? highlight(o.hint, queryRe) : o.hint,
171
172
  icon: custom ? void 0 : o.icon,
172
173
  tone: o.tone,
@@ -177,10 +178,10 @@ const Select = (props) => {
177
178
  onSelect: (e) => choose(o, e),
178
179
  children: [
179
180
  custom,
180
- tip && /* @__PURE__ */ jsx(Tooltip, { ...hintOnly ? { follow: true, delay: 700 } : {}, children: tip })
181
+ tip && /* @__PURE__ */ jsx(Tooltip, { follow: true, ...hintOnly ? { delay: 700 } : {}, children: tip })
181
182
  ]
182
183
  },
183
- o.value
184
+ String(o.value)
184
185
  );
185
186
  };
186
187
  const renderTree = (items, disabled2) => {
@@ -1,6 +1,6 @@
1
1
  import type { BaseProps } from '../general_types';
2
2
  import type { IconShape } from '../elements/a-icon.shapes';
3
- import type { SelectItem, SelectOption } from './Select';
3
+ import type { OptionValue, SelectItem, SelectOption } from './Select';
4
4
  import { type MenuProps } from './Menu';
5
5
  interface FacetBase {
6
6
  /** The facet's namespace — the key its value is stored under in the value
@@ -15,26 +15,27 @@ interface FacetBase {
15
15
  /** A per-facet option-list filter (like `Select`'s `filter`): `true` uses the
16
16
  * built-in case-insensitive substring match on an option's value / label /
17
17
  * hint; a function `(option, query) => boolean` does custom matching. */
18
- export type FacetFilter = boolean | ((option: SelectOption, query: string) => boolean);
19
- /** Pick **one** option. Value: the chosen option's `value` string (or
20
- * `undefined` when cleared). Re-picking the selected option clears it. Options
21
- * are `Select`'s `SelectItem`s — bare strings or `SelectOption`s carrying the
22
- * same fields (`value`, `label`, `hint`, `icon`, `tone`, `disabled`). */
18
+ export type FacetFilter = boolean | ((option: SelectOption<OptionValue>, query: string) => boolean);
19
+ /** Pick **one** option. Value: the chosen option's `value` (or `undefined` when
20
+ * cleared). Re-picking the selected option clears it. Options are `Select`'s
21
+ * `SelectItem`s — bare strings or `SelectOption`s carrying the same fields
22
+ * (`value`, `label`, `hint`, `icon`, `tone`, `disabled`). Option `value`s may be
23
+ * `string` / `number` / `boolean` and round-trip unchanged. */
23
24
  export interface SelectFacetSingle extends FacetBase {
24
25
  kind: 'single';
25
26
  /** The options — bare strings or `SelectOption`s (groups / submenus are
26
27
  * flattened to their leaves). */
27
- options: SelectItem[];
28
+ options: SelectItem<OptionValue>[];
28
29
  /** Add a search field atop this facet's flyout that filters its options. */
29
30
  filter?: FacetFilter;
30
31
  }
31
- /** Pick **any number** of options. Value: an array of the chosen `value`
32
- * strings (empty array clears the facet). Options are the same `SelectItem`s
33
- * as `single`. */
32
+ /** Pick **any number** of options. Value: an array of the chosen `value`s
33
+ * (empty array clears the facet). Options are the same `SelectItem`s as `single`,
34
+ * with the same `string` / `number` / `boolean` value support. */
34
35
  export interface SelectFacetMultiple extends FacetBase {
35
36
  kind: 'multiple';
36
37
  /** The options — bare strings or `SelectOption`s (groups / submenus flattened). */
37
- options: SelectItem[];
38
+ options: SelectItem<OptionValue>[];
38
39
  /** Add a search field atop this facet's flyout that filters its options. */
39
40
  filter?: FacetFilter;
40
41
  /** A "Select all" row that toggles every option. On by default — set `false`
@@ -75,9 +76,11 @@ export interface SelectFacetCustom<V = unknown> extends FacetBase {
75
76
  }
76
77
  /** One facet (dimension) of the filter, discriminated by `kind`. */
77
78
  export type SelectFacet = SelectFacetSingle | SelectFacetMultiple | SelectFacetText | SelectFacetCustom;
78
- /** The filter value: a facet key → that facet's value. Per kind the value is a
79
- * `string` (single / text), a `string[]` (multiple), or your own `V` (custom).
80
- * A cleared facet is absent from the record (not a falsy entry). */
79
+ /** The filter value: a facet key → that facet's value. Per kind: an option value
80
+ * (`string` / `number` / `boolean`) for `single`, a `string` for `text`, an array for
81
+ * `multiple`, or your own `V` for `custom`. Values are stored and reported unchanged.
82
+ * Typed loosely as `unknown` since facets in one control can hold different value
83
+ * types; narrow per facet when you read it. A cleared facet is absent from the record. */
81
84
  export type SelectFacetedValue = Record<string, unknown>;
82
85
  /** Second argument to `onValueChange` — what changed. A single-facet edit
83
86
  * carries `facet` / `kind` / the facet's new `value`; the "Clear all" row
@@ -122,8 +125,10 @@ export interface SelectFacetedProps extends Omit<BaseProps, 'children'> {
122
125
  value?: SelectFacetedValue;
123
126
  /** Initial value for the uncontrolled case (the wrapper then owns it). */
124
127
  defaultValue?: SelectFacetedValue;
125
- /** Fires after any facet changes, with the new full value record and an
126
- * attrs snapshot of what changed. */
128
+ /** Fires after any facet changes. `value` is the whole new record (a facet key →
129
+ * that facet's value; a cleared facet is absent). `attrs` says what changed:
130
+ * `{ facet, kind, value }` for a single facet edit, or `{ all: true }` for the
131
+ * "Clear all" row — narrow on `'all' in attrs` before reading `facet`. */
127
132
  onValueChange?: (value: SelectFacetedValue, attrs: SelectFacetedChangeAttrs) => void;
128
133
  /** Default trigger's button label.
129
134
  * @defaultValue Filter */
@@ -120,7 +120,7 @@ const SelectFaceted = (props) => {
120
120
  const optionRow = (facet, opt, keyPrefix = "") => {
121
121
  const shared = {
122
122
  icon: opt.icon,
123
- label: opt.label ?? opt.value,
123
+ label: opt.label ?? String(opt.value),
124
124
  hint: opt.hint,
125
125
  tone: opt.tone,
126
126
  toneSelected,
@@ -151,7 +151,7 @@ const SelectFaceted = (props) => {
151
151
  selectionIndicator: "checkbox",
152
152
  selected: arr.includes(opt.value),
153
153
  onSelect: (e) => isolable && e?.altKey ? setFacet(facet, [opt.value]) : setFacet(facet, arr.includes(opt.value) ? arr.filter((v) => v !== opt.value) : [...arr, opt.value]),
154
- children: tip && /* @__PURE__ */ jsx(Tooltip, { ...hintOnly ? { follow: true, delay: 700 } : {}, children: tip })
154
+ children: tip && /* @__PURE__ */ jsx(Tooltip, { follow: true, ...hintOnly ? { delay: 700 } : {}, children: tip })
155
155
  },
156
156
  `${keyPrefix}${opt.value}`
157
157
  );
@@ -18,11 +18,11 @@ export type ExpandMode = {
18
18
  collapsible?: boolean;
19
19
  };
20
20
  export type TextProps = BaseProps & {
21
- /** Visual priority. Maps to text-1..text-5 (`primary` = text-1, the
21
+ /** Visual priority. Maps to text-1..text-4 (`primary` = text-1, the
22
22
  * strongest). The default is `secondary` (text-2) — body text reads a
23
23
  * step softer than the strongest foreground; pass `primary` for emphasis.
24
24
  * @defaultValue secondary */
25
- priority?: 'primary' | 'secondary' | 'tertiary' | 'quaternary' | 'quinary';
25
+ priority?: 'primary' | 'secondary' | 'tertiary' | 'quaternary';
26
26
  /** Color tint. `neutral` (the default) is the untinted `--text-{N}` scale; a
27
27
  * named tone applies the matching `--text-{N}-{tone}` palette. Any literal CSS
28
28
  * color (`'#ff1493'`, `'rebeccapurple'`) is a one-off custom tone — its hue is
@@ -5,9 +5,9 @@ export interface TitleProps extends BaseProps {
5
5
  * (h1 is typically reserved for the page title).
6
6
  * @defaultValue 2 */
7
7
  level?: 1 | 2 | 3 | 4 | 5 | 6;
8
- /** Visual priority. Maps to text-1..text-5 (`primary` = text-1).
8
+ /** Visual priority. Maps to text-1..text-4 (`primary` = text-1).
9
9
  * @defaultValue primary */
10
- priority?: 'primary' | 'secondary' | 'tertiary' | 'quaternary' | 'quinary';
10
+ priority?: 'primary' | 'secondary' | 'tertiary' | 'quaternary';
11
11
  /** Color tint. `neutral` (the default) is the untinted `--text-{N}` scale; a
12
12
  * named tone applies the matching `--text-{N}-{tone}` palette. Any literal CSS
13
13
  * color (`'#ff1493'`, `'rebeccapurple'`) is a one-off custom tone — its hue is
@@ -5,7 +5,7 @@ export interface TooltipProps extends BaseProps {
5
5
  children?: React.ReactNode;
6
6
  /** Show delay in milliseconds after hover / focus. Never use `0` — use
7
7
  * ~`50` for a near-instant tooltip (0 has caused issues in practice).
8
- * @defaultValue 250 */
8
+ * @defaultValue 300 */
9
9
  delay?: number;
10
10
  /** Which side of the anchor the bubble prefers. Auto-flips to the other
11
11
  * side when there isn't room.