@antadesign/anta 0.3.2 → 0.3.4

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 (96) hide show
  1. package/dist/anta_helpers.d.ts +97 -0
  2. package/dist/anta_helpers.js +70 -0
  3. package/dist/calendar-core.d.ts +126 -0
  4. package/dist/calendar-core.js +289 -0
  5. package/dist/components/Button.d.ts +9 -1
  6. package/dist/components/Button.js +5 -2
  7. package/dist/components/Calendar.d.ts +85 -0
  8. package/dist/components/Calendar.js +226 -0
  9. package/dist/components/Checkbox.d.ts +16 -11
  10. package/dist/components/Checkbox.js +13 -7
  11. package/dist/components/Expander.d.ts +7 -1
  12. package/dist/components/Expander.js +4 -2
  13. package/dist/components/Input.d.ts +13 -5
  14. package/dist/components/Input.js +14 -6
  15. package/dist/components/InputDate.d.ts +89 -0
  16. package/dist/components/InputDate.js +273 -0
  17. package/dist/components/InputDate.module.css +1 -0
  18. package/dist/components/Menu.d.ts +14 -1
  19. package/dist/components/Menu.js +6 -2
  20. package/dist/components/MenuItem.d.ts +48 -7
  21. package/dist/components/MenuItem.js +72 -14
  22. package/dist/components/MenuSeparator.d.ts +13 -2
  23. package/dist/components/MenuSeparator.js +12 -2
  24. package/dist/components/Progress.d.ts +4 -1
  25. package/dist/components/Progress.js +4 -3
  26. package/dist/components/RadioGroup.d.ts +12 -10
  27. package/dist/components/RadioGroup.js +5 -5
  28. package/dist/components/Select.d.ts +319 -0
  29. package/dist/components/Select.js +341 -0
  30. package/dist/components/Select.module.css +1 -0
  31. package/dist/components/Tab.d.ts +3 -0
  32. package/dist/components/Tab.js +2 -1
  33. package/dist/components/TabPanel.d.ts +21 -13
  34. package/dist/components/TabPanel.js +13 -1
  35. package/dist/components/Tabs.d.ts +75 -40
  36. package/dist/components/Tabs.js +20 -88
  37. package/dist/components/Tag.d.ts +10 -9
  38. package/dist/components/Tag.js +2 -2
  39. package/dist/components/Text.d.ts +6 -5
  40. package/dist/components/Title.d.ts +4 -2
  41. package/dist/components/Tooltip.d.ts +4 -1
  42. package/dist/components/Tooltip.js +5 -0
  43. package/dist/elements/a-button.css +1 -1
  44. package/dist/elements/a-calendar.css +1 -0
  45. package/dist/elements/a-calendar.d.ts +76 -0
  46. package/dist/elements/a-calendar.js +190 -0
  47. package/dist/elements/a-checkbox.css +1 -1
  48. package/dist/elements/a-checkbox.d.ts +1 -2
  49. package/dist/elements/a-checkbox.js +5 -5
  50. package/dist/elements/a-expander.css +1 -1
  51. package/dist/elements/a-expander.d.ts +16 -5
  52. package/dist/elements/a-expander.js +59 -8
  53. package/dist/elements/a-icon.shapes.css +1 -1
  54. package/dist/elements/a-icon.shapes.d.ts +6 -1
  55. package/dist/elements/a-icon.shapes.js +9 -0
  56. package/dist/elements/a-input.css +1 -1
  57. package/dist/elements/a-input.d.ts +6 -0
  58. package/dist/elements/a-input.js +28 -8
  59. package/dist/elements/a-menu-group.css +1 -1
  60. package/dist/elements/a-menu-item.css +1 -1
  61. package/dist/elements/a-menu-item.d.ts +22 -10
  62. package/dist/elements/a-menu-item.js +17 -0
  63. package/dist/elements/a-menu-separator.css +1 -1
  64. package/dist/elements/a-menu.css +1 -1
  65. package/dist/elements/a-menu.d.ts +61 -20
  66. package/dist/elements/a-menu.js +337 -58
  67. package/dist/elements/a-progress.css +1 -1
  68. package/dist/elements/a-radio-group.d.ts +1 -3
  69. package/dist/elements/a-radio-group.js +13 -13
  70. package/dist/elements/a-radio.css +1 -1
  71. package/dist/elements/a-radio.d.ts +3 -11
  72. package/dist/elements/a-radio.js +3 -34
  73. package/dist/elements/a-tab.css +1 -1
  74. package/dist/elements/a-tab.d.ts +3 -11
  75. package/dist/elements/a-tab.js +3 -34
  76. package/dist/elements/a-tabpanel.css +1 -1
  77. package/dist/elements/a-tabpanel.d.ts +17 -0
  78. package/dist/elements/a-tabpanel.js +66 -0
  79. package/dist/elements/a-tabs.css +1 -1
  80. package/dist/elements/a-tabs.d.ts +1 -4
  81. package/dist/elements/a-tabs.js +14 -14
  82. package/dist/elements/a-tag.css +1 -1
  83. package/dist/elements/a-text.d.ts +14 -13
  84. package/dist/elements/a-text.js +53 -23
  85. package/dist/elements/a-tooltip.css +1 -1
  86. package/dist/elements/a-tooltip.d.ts +1 -11
  87. package/dist/elements/a-tooltip.js +20 -19
  88. package/dist/elements/index.d.ts +2 -1
  89. package/dist/elements/index.js +6 -1
  90. package/dist/general_types.d.ts +158 -42
  91. package/dist/index.d.ts +10 -4
  92. package/dist/index.js +38 -3
  93. package/dist/jsx-runtime.d.ts +16 -7
  94. package/dist/jsx-runtime.js +6 -0
  95. package/dist/reset.css +1 -1
  96. package/package.json +4 -2
@@ -0,0 +1,341 @@
1
+ import { Fragment, jsx, jsxs } from "@antadesign/anta/jsx-runtime";
2
+ import { useState, useId } from "../jsx-runtime";
3
+ import { Input } from "./Input";
4
+ import { Icon } from "./Icon";
5
+ import { Menu } from "./Menu";
6
+ import { MenuItem } from "./MenuItem";
7
+ import { MenuGroup } from "./MenuGroup";
8
+ import { MenuSeparator } from "./MenuSeparator";
9
+ import { Tooltip } from "./Tooltip";
10
+ import styles from "./Select.module.css";
11
+ const IS_MAC = typeof navigator !== "undefined" && /Mac|iPhone|iPad/i.test(navigator.userAgent || "");
12
+ const normalize = (o) => typeof o === "string" ? { value: o, label: o } : o;
13
+ function optionsWithSelection(options, values) {
14
+ const set = new Set(values == null ? [] : Array.isArray(values) ? values : [values]);
15
+ const state = (on, total) => on === 0 ? "none" : on === total ? "all" : "some";
16
+ const walk = (items) => items.map((raw) => {
17
+ if (typeof raw !== "string" && Array.isArray(raw.submenu)) {
18
+ const sm = raw;
19
+ const children = walk(sm.submenu);
20
+ const on = children.reduce((n, c) => n + c.on, 0);
21
+ const total = children.reduce((n, c) => n + c.total, 0);
22
+ return { node: { ...sm, submenu: children.map((c) => c.node), selectionState: state(on, total) }, on, total };
23
+ }
24
+ if (typeof raw !== "string" && Array.isArray(raw.options)) {
25
+ const g = raw;
26
+ const children = walk(g.options);
27
+ const on = children.reduce((n, c) => n + c.on, 0);
28
+ const total = children.reduce((n, c) => n + c.total, 0);
29
+ return { node: { ...g, options: children.map((c) => c.node), selectionState: state(on, total) }, on, total };
30
+ }
31
+ const o = normalize(raw);
32
+ const selected = set.has(o.value);
33
+ return { node: { ...o, selected }, on: selected ? 1 : 0, total: 1 };
34
+ });
35
+ return walk(options).map((c) => c.node);
36
+ }
37
+ const Select = (props) => {
38
+ const {
39
+ options,
40
+ selection,
41
+ indicator,
42
+ value,
43
+ defaultValue,
44
+ onValueChange,
45
+ placeholder,
46
+ icon,
47
+ label,
48
+ hint,
49
+ size,
50
+ status,
51
+ statusIcon,
52
+ round,
53
+ disabled,
54
+ toneSelected,
55
+ filter,
56
+ selectAll,
57
+ selectAllLabel = "Select all",
58
+ renderOption,
59
+ renderIndicator,
60
+ renderTrigger,
61
+ renderEmpty,
62
+ className,
63
+ style,
64
+ ...rest
65
+ } = props;
66
+ const multiple = selection === "multiple";
67
+ const statusColor = status && status !== "neutral" ? `var(--text-2-${status})` : void 0;
68
+ const mark = multiple ? "checkbox" : indicator ?? "none";
69
+ const menuItemIndicator = mark === "none" ? void 0 : mark;
70
+ const emit = onValueChange;
71
+ const controlled = value !== void 0;
72
+ const [internal, setInternal] = useState(defaultValue);
73
+ const currentRaw = controlled ? value : internal;
74
+ const [open, setOpen] = useState(false);
75
+ const [query, setQuery] = useState("");
76
+ const [activeId, setActiveId] = useState(null);
77
+ const uid = useId();
78
+ const isSubmenu = (it) => typeof it === "object" && Array.isArray(it.submenu);
79
+ const isGroup = (it) => typeof it === "object" && Array.isArray(it.options);
80
+ const collectLeaves = (items, group, disabled2, out) => {
81
+ for (const raw of items) {
82
+ if (typeof raw !== "string" && isSubmenu(raw)) collectLeaves(raw.submenu, raw.label, disabled2 || !!raw.disabled, out);
83
+ else if (typeof raw !== "string" && isGroup(raw)) collectLeaves(raw.options, raw.label, disabled2 || !!raw.disabled, out);
84
+ else {
85
+ const o = normalize(raw);
86
+ out.push({ opt: o, disabled: disabled2 || !!o.disabled, group });
87
+ }
88
+ }
89
+ };
90
+ const allLeaves = [];
91
+ collectLeaves(options, void 0, false, allLeaves);
92
+ const seenValues = /* @__PURE__ */ new Set();
93
+ for (const l of allLeaves) {
94
+ if (seenValues.has(l.opt.value))
95
+ console.warn(`[anta] <Select> duplicate option value=${JSON.stringify(l.opt.value)} \u2014 values must be unique.`);
96
+ seenValues.add(l.opt.value);
97
+ }
98
+ const byValue = new Map(allLeaves.map((l) => [l.opt.value, l.opt]));
99
+ const filtering = filter !== void 0 && filter !== false;
100
+ const q = query.trim();
101
+ const escapeRe = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
102
+ const queryRe = filtering && typeof filter !== "function" && q ? new RegExp(q.split(/\s+/).map(escapeRe).join("\\s+"), "i") : null;
103
+ const matches = (o) => typeof filter === "function" ? filter(o, q) : !queryRe || [o.value, o.label ?? "", o.hint ?? ""].some((s) => queryRe.test(s));
104
+ const prune = typeof filter === "function" || filtering && !!q;
105
+ const visibleLeaves = prune ? allLeaves.filter((l) => matches(l.opt)) : allLeaves;
106
+ const flattening = filtering && !!q;
107
+ const highlight = (text) => {
108
+ if (!queryRe) return text;
109
+ const re = new RegExp(queryRe.source, "gi");
110
+ const out = [];
111
+ let last = 0;
112
+ let m;
113
+ while ((m = re.exec(text)) !== null) {
114
+ if (m.index > last) out.push(text.slice(last, m.index));
115
+ out.push(/* @__PURE__ */ jsx("b", { children: m[0] }, out.length));
116
+ last = m.index + m[0].length;
117
+ if (m[0].length === 0) re.lastIndex++;
118
+ }
119
+ if (out.length === 0) return text;
120
+ if (last < text.length) out.push(text.slice(last));
121
+ return out;
122
+ };
123
+ const selectedValues = Array.isArray(currentRaw) ? currentRaw : currentRaw != null ? [currentRaw] : [];
124
+ const isSelected = (v) => selectedValues.includes(v);
125
+ const selectedOptions = selectedValues.map((v) => byValue.get(v)).filter(Boolean);
126
+ let display = "";
127
+ if (multiple) {
128
+ if (selectedOptions.length === 1) display = selectedOptions[0].label ?? selectedOptions[0].value;
129
+ else if (selectedOptions.length > 1) display = `${selectedOptions.length} selected`;
130
+ } else if (currentRaw != null) {
131
+ const o = byValue.get(currentRaw);
132
+ display = o ? o.label ?? o.value : "";
133
+ }
134
+ const choose = (o, e) => {
135
+ if (multiple) {
136
+ if (selectAll && e?.altKey) {
137
+ const next2 = [o.value];
138
+ if (!controlled) setInternal(next2);
139
+ emit?.(next2, { value: o.value, option: o, selected: true });
140
+ return;
141
+ }
142
+ const has = selectedValues.includes(o.value);
143
+ const next = has ? selectedValues.filter((v) => v !== o.value) : [...selectedValues, o.value];
144
+ if (!controlled) setInternal(next);
145
+ emit?.(next, { value: o.value, option: o, selected: !has });
146
+ } else {
147
+ if (!controlled) setInternal(o.value);
148
+ emit?.(o.value, { value: o.value, option: o });
149
+ }
150
+ };
151
+ const enabledValues = multiple ? visibleLeaves.filter((l) => !l.disabled).map((l) => l.opt.value) : [];
152
+ const enabledSelected = enabledValues.filter((v) => selectedValues.includes(v));
153
+ const allSelected = enabledValues.length > 0 && enabledSelected.length === enabledValues.length;
154
+ const someSelected = enabledSelected.length > 0 && !allSelected;
155
+ const toggleAll = () => {
156
+ const keep = selectedValues.filter((v) => !enabledValues.includes(v));
157
+ const next = allSelected ? keep : [...keep, ...enabledValues];
158
+ if (!controlled) setInternal(next);
159
+ emit?.(next, { all: true, selected: !allSelected });
160
+ };
161
+ const renderOptionRow = (o, disabled2) => {
162
+ const optState = { value: o.value, selected: isSelected(o.value), disabled: disabled2 };
163
+ const custom = renderOption?.(o, optState);
164
+ const customMark = renderIndicator?.(optState);
165
+ const ariaSelectable = !multiple && mark === "none" ? { role: "menuitemradio", "aria-checked": isSelected(o.value) ? "true" : "false" } : void 0;
166
+ const isolateHint = multiple && selectAll && !disabled2 ? IS_MAC ? "\u2325+Click to select only this" : "Alt+Click to select only this" : void 0;
167
+ const tip = o.tooltip ?? isolateHint;
168
+ const hintOnly = o.tooltip == null && isolateHint != null;
169
+ return /* @__PURE__ */ jsxs(
170
+ MenuItem,
171
+ {
172
+ id: `${uid}-opt-${o.value}`,
173
+ selectionIndicator: menuItemIndicator,
174
+ ...ariaSelectable,
175
+ indicator: customMark ?? void 0,
176
+ label: custom ? void 0 : queryRe ? highlight(o.label ?? o.value) : o.label ?? o.value,
177
+ hint: custom ? void 0 : queryRe && o.hint ? highlight(o.hint) : o.hint,
178
+ icon: custom ? void 0 : o.icon,
179
+ tone: o.tone,
180
+ toneSelected,
181
+ selected: isSelected(o.value),
182
+ disabled: disabled2 || void 0,
183
+ "data-menu-open": multiple ? "" : void 0,
184
+ onSelect: (e) => choose(o, e),
185
+ children: [
186
+ custom,
187
+ tip && /* @__PURE__ */ jsx(Tooltip, { ...hintOnly ? { follow: true, delay: 700 } : {}, children: tip })
188
+ ]
189
+ },
190
+ o.value
191
+ );
192
+ };
193
+ const renderTree = (items, disabled2) => {
194
+ const out = [];
195
+ items.forEach((raw, i) => {
196
+ if (typeof raw !== "string" && isSubmenu(raw)) {
197
+ const dis = disabled2 || !!raw.disabled;
198
+ const inner = renderTree(raw.submenu, dis);
199
+ if (inner.length)
200
+ out.push(
201
+ /* @__PURE__ */ jsx(
202
+ MenuItem,
203
+ {
204
+ submenu: true,
205
+ label: raw.label,
206
+ icon: raw.icon,
207
+ disabled: dis || void 0,
208
+ "data-menu-open": multiple ? "" : void 0,
209
+ children: /* @__PURE__ */ jsx(Menu, { children: inner })
210
+ },
211
+ `sub-${i}-${raw.label}`
212
+ )
213
+ );
214
+ return;
215
+ }
216
+ if (typeof raw !== "string" && isGroup(raw)) {
217
+ const dis = disabled2 || !!raw.disabled;
218
+ const inner = renderTree(raw.options, dis);
219
+ if (inner.length) out.push(/* @__PURE__ */ jsx(MenuGroup, { label: raw.label, children: inner }, `grp-${i}-${raw.label}`));
220
+ return;
221
+ }
222
+ const o = normalize(raw);
223
+ if (typeof filter === "function" && !matches(o)) return;
224
+ out.push(renderOptionRow(o, disabled2 || !!o.disabled));
225
+ });
226
+ return out;
227
+ };
228
+ const renderFlat = () => {
229
+ const shown = allLeaves.filter((l) => matches(l.opt));
230
+ const out = [];
231
+ for (let i = 0; i < shown.length; ) {
232
+ const label2 = shown[i].group;
233
+ if (label2 === void 0) {
234
+ out.push(renderOptionRow(shown[i].opt, shown[i].disabled));
235
+ i++;
236
+ continue;
237
+ }
238
+ const rows = [];
239
+ const start = i;
240
+ while (i < shown.length && shown[i].group === label2) {
241
+ rows.push(renderOptionRow(shown[i].opt, shown[i].disabled));
242
+ i++;
243
+ }
244
+ out.push(/* @__PURE__ */ jsx(MenuGroup, { label: label2, children: rows }, `flat-${start}-${label2}`));
245
+ }
246
+ return out;
247
+ };
248
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
249
+ renderTrigger ? renderTrigger({ open, value: currentRaw, selected: selectedOptions, disabled: !!disabled, icon }) : /* @__PURE__ */ jsx(
250
+ Input,
251
+ {
252
+ label,
253
+ hint,
254
+ placeholder,
255
+ value: display,
256
+ readOnly: true,
257
+ dimActions: true,
258
+ disabled,
259
+ leading: icon ? /* @__PURE__ */ jsx(Icon, { shape: icon }) : void 0,
260
+ size,
261
+ status,
262
+ statusIcon,
263
+ round,
264
+ "aria-haspopup": "menu",
265
+ "aria-expanded": open ? "true" : "false",
266
+ onKeyDown: (e) => {
267
+ if (!open && (e.key === "Enter" || e.key === " " || e.key === "ArrowDown")) {
268
+ e.preventDefault();
269
+ e.currentTarget.click();
270
+ }
271
+ },
272
+ trailing: (
273
+ // Chevron rotates down → up when open (spacing + rotation in the CSS
274
+ // module, matching the Input "Select dropdown" example).
275
+ /* @__PURE__ */ jsx(
276
+ Icon,
277
+ {
278
+ shape: "chevron-down",
279
+ className: open ? `${styles.chevron} ${styles.open}` : styles.chevron,
280
+ style: statusColor ? { color: statusColor } : void 0
281
+ }
282
+ )
283
+ ),
284
+ className,
285
+ style,
286
+ ...rest
287
+ }
288
+ ),
289
+ /* @__PURE__ */ jsxs(
290
+ Menu,
291
+ {
292
+ onStateChange: (_e, { next }) => {
293
+ setOpen(next);
294
+ if (!next) {
295
+ setQuery("");
296
+ setActiveId(null);
297
+ }
298
+ },
299
+ onactivedescendant: (e) => setActiveId((e.nativeEvent ?? e).detail?.id ?? null),
300
+ children: [
301
+ filtering && // `slot="header"` pins the field in the Menu's fixed header region (above
302
+ // the scrolling options); `data-menu-search` puts the menu in combobox
303
+ // mode — it focuses this field on open and drives an active-option cursor.
304
+ /* @__PURE__ */ jsx("div", { className: styles.filter, slot: "header", "data-menu-open": "", children: /* @__PURE__ */ jsx(
305
+ Input,
306
+ {
307
+ "data-menu-search": "",
308
+ size: "small",
309
+ value: query,
310
+ placeholder: "Filter\u2026",
311
+ "aria-label": "Filter options",
312
+ "aria-autocomplete": "list",
313
+ "aria-activedescendant": open && activeId ? activeId : void 0,
314
+ onInput: (e) => setQuery(e.currentTarget.value)
315
+ }
316
+ ) }),
317
+ multiple && selectAll && visibleLeaves.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
318
+ /* @__PURE__ */ jsx(
319
+ MenuItem,
320
+ {
321
+ selectionIndicator: "checkbox",
322
+ selected: allSelected,
323
+ indeterminate: someSelected,
324
+ label: selectAllLabel,
325
+ "data-menu-open": "",
326
+ "data-menu-skip-active": "",
327
+ onSelect: toggleAll
328
+ }
329
+ ),
330
+ /* @__PURE__ */ jsx(MenuSeparator, {})
331
+ ] }),
332
+ visibleLeaves.length === 0 ? renderEmpty?.({ query: q }) : flattening ? renderFlat() : renderTree(options, false)
333
+ ]
334
+ }
335
+ )
336
+ ] });
337
+ };
338
+ export {
339
+ Select,
340
+ optionsWithSelection
341
+ };
@@ -0,0 +1 @@
1
+ .chevron{margin-inline-end:7px;transition:transform .15s ease}.chevron.open{transform:rotate(180deg)}.filter{padding:4px 4px 0}
@@ -14,6 +14,9 @@ export interface TabProps {
14
14
  icon?: IconShape;
15
15
  /** Trailing icon shape, rendered after the label. */
16
16
  iconTrailing?: IconShape;
17
+ /** Fully-round just this tab's box. `<Tabs round>` rounds the whole strip
18
+ * (tabs + sliding indicator) instead. */
19
+ round?: boolean;
17
20
  /** Per-tab tone override, same vocabulary as `<Tabs tone>` — colours this one tab's
18
21
  * label + icons (all priorities/modes, named or custom colour) and, when it's the
19
22
  * active tab, its indicator. For a **custom literal colour** the sliding indicator can't
@@ -1,4 +1,5 @@
1
- const Tab = (_props) => null;
1
+ import { markTabsKind } from "../anta_helpers";
2
+ const Tab = markTabsKind((_props) => null, "tab");
2
3
  export {
3
4
  Tab
4
5
  };
@@ -1,22 +1,30 @@
1
- import type { TabsMounting } from "./Tabs";
2
- /** A panel paired to the `<Tab>` of the same `value`. A **config component**: `Tabs`
3
- * reads these props and renders the underlying `<a-tabpanel>` (id wiring, show/hide,
4
- * `inert`, and the mounting strategy are all `Tabs`' job). */
1
+ /**
2
+ * A tab panel inside `<Tabs>`. Renders a self-managing `<a-tabpanel>` the element
3
+ * finds its `<a-tabs>` (its flat sibling under the same parent — `Tabs` renders the
4
+ * strip and panels with no wrapper), matches by `value`, and shows/hides itself
5
+ * off-DOM. This wrapper is a pure projection: it sets the static ARIA (`role`,
6
+ * `tabindex`, `value`) and passes `children` straight through; `Tabs` never reads
7
+ * or toggles it.
8
+ *
9
+ * Use it as a direct child of `<Tabs>`, paired to an `options` entry by `value`.
10
+ * For a strip and panels in different layout regions (no shared parent), or to
11
+ * unmount an inactive panel (the old `mounting="active" | "lazy"`), drive selection
12
+ * with a controlled `value` and render the content yourself — see the Tabs docs.
13
+ */
5
14
  export interface TabPanelProps {
6
- /** Pairs this panel with the `<Tab value="…">` of the same value. */
15
+ /** Pairs this panel with the tab (`options` entry) of the same `value`. */
7
16
  value: string;
8
17
  /** Panel content — arbitrary React/Preact. */
9
18
  children?: React.ReactNode;
10
- /** Override the `<Tabs mounting>` strategy for just this panel. See `TabsMounting`. */
11
- mounting?: TabsMounting;
19
+ /** How this panel hides while inactive: `display` (default removed from layout
20
+ * and the a11y tree) or `visibility` (keeps its layout box, to measure it or
21
+ * avoid reflow). Both stay mounted; to *not render* an inactive panel, render it
22
+ * conditionally off a controlled `value` (see the Tabs docs).
23
+ * @defaultValue display */
24
+ hideMode?: "display" | "visibility";
12
25
  /** CSS class on the rendered `<a-tabpanel>`. */
13
26
  className?: string;
14
27
  /** Inline style on the rendered `<a-tabpanel>`. */
15
28
  style?: React.CSSProperties;
16
29
  }
17
- /**
18
- * A tab panel inside `<Tabs>`. Configuration only — `Tabs` reads its props and renders
19
- * the real `<a-tabpanel>`, so this component itself produces no DOM. Use it as a direct
20
- * child of `<Tabs>`; rendering it elsewhere does nothing.
21
- */
22
- export declare const TabPanel: (_props: TabPanelProps) => null;
30
+ export declare const TabPanel: ({ value, children, hideMode, className, style }: TabPanelProps) => any;
@@ -1,4 +1,16 @@
1
- const TabPanel = (_props) => null;
1
+ import { jsx } from "@antadesign/anta/jsx-runtime";
2
+ const TabPanel = ({ value, children, hideMode, className, style }) => /* @__PURE__ */ jsx(
3
+ "a-tabpanel",
4
+ {
5
+ value,
6
+ role: "tabpanel",
7
+ tabIndex: 0,
8
+ "hide-mode": hideMode === "visibility" ? "visibility" : void 0,
9
+ class: className,
10
+ style,
11
+ children
12
+ }
13
+ );
2
14
  export {
3
15
  TabPanel
4
16
  };
@@ -1,4 +1,5 @@
1
1
  import type { BaseProps } from "../general_types";
2
+ import type { IconShape } from "../elements/a-icon.shapes";
2
3
  /** The element's `statechange` payload — `next`/`prev` are tab values (`null` = none). */
3
4
  type StateDetail = {
4
5
  next: string | null;
@@ -9,33 +10,58 @@ type StateChangeEvent = CustomEvent<StateDetail>;
9
10
  export interface TabsChangeAttrs {
10
11
  value: string | null;
11
12
  }
12
- /**
13
- * How `<TabPanel>`s that aren't the active one are handled.
14
- * - `'display'` *(default)* all panels mounted; inactive ones hidden via
15
- * `display:none`. Preserves panel DOM/form state; correct a11y for free.
16
- * - `'visibility'` — all mounted; inactive hidden via `visibility:hidden`, so each
17
- * panel keeps its layout box (useful to avoid reflow / for measuring).
18
- * - `'active'` only the active panel is rendered; React unmounts the rest (state
19
- * resets on switch; defers expensive subtrees).
20
- * - `'lazy'` a panel mounts on first activation, then stays mounted+hidden.
21
- */
22
- export type TabsMounting = "display" | "visibility" | "active" | "lazy";
13
+ /** One tab as a plain data object for the `options` array — the single source `Tabs`
14
+ * renders its strip from (like `RadioGroup`'s `options`). `Tabs` reads these fields to
15
+ * render the underlying `<a-tab>` (`tabindex`, `role`, and selection are all `Tabs`'
16
+ * job, published off-DOM by the element). */
17
+ export interface TabOption {
18
+ /** This tab's identity pairs it with the `<TabPanel value="…">` of the same
19
+ * value, and the value reported by `onStateChange` / `onChange`. Unique per strip. */
20
+ value: string;
21
+ /** Visible label. The string shorthand for the tab's content; for richer content
22
+ * pass `children` instead (`label` wins when both are set). */
23
+ label?: React.ReactNode;
24
+ /** Tab content when you need more than a string — used if `label` is omitted. */
25
+ children?: React.ReactNode;
26
+ /** Leading icon shape, rendered before the label. */
27
+ icon?: IconShape;
28
+ /** Trailing icon shape, rendered after the label. */
29
+ iconTrailing?: IconShape;
30
+ /** Fully-round just this tab's box. `<Tabs round>` rounds the whole strip
31
+ * (tabs + sliding indicator) instead. */
32
+ round?: boolean;
33
+ /** Per-tab tone override, same vocabulary as `<Tabs tone>` — colours this one tab's
34
+ * label + icons (all priorities/modes, named or custom colour) and, when it's the
35
+ * active tab, its indicator. For a **custom literal colour** the sliding indicator can't
36
+ * adopt it (the shared moving element can't read a descendant's colour), so a custom tone
37
+ * colours the label everywhere and the indicator only in `noslide`; the six **named**
38
+ * tones colour both in every mode. Overrides the strip's `tone` for this tab.
39
+ * @defaultValue inherits the strip's `tone` */
40
+ tone?: "neutral" | "brand" | "info" | "success" | "warning" | "critical" | (string & {});
41
+ /** Disable just this tab — skipped by keyboard nav and dropped from the tab order
42
+ * (a disabled-but-selected tab stays reachable, per the ARIA pattern). */
43
+ disabled?: boolean;
44
+ }
23
45
  /** Public props for `<Tabs>`. */
24
46
  export interface TabsProps extends Omit<BaseProps, "onChange"> {
25
- /** The strip's contents, as config-only elements `Tabs` reads (they render
26
- * nothing themselves): one `<Tab value="…" label="…">` per tab, and optionally a
27
- * `<TabPanel value="…">` whose `value` matches a tab `Tabs` then shows that
28
- * panel's body when its tab is active. Omit the panels to use `Tabs` as a bare
29
- * selectable strip. Order is free; tabs and panels can interleave. */
47
+ /** Optional `<TabPanel value="…">` panels, one per tab value. Each is a
48
+ * self-managing `<a-tabpanel>` that shows itself when its `value` is the active
49
+ * tab. Omit them to use `Tabs` as a bare selectable strip. To place panels in a
50
+ * different layout region, or to unmount an inactive panel, drive selection with
51
+ * a controlled `value` and render the content yourself (see the docs). */
30
52
  children?: React.ReactNode;
31
- /** Controlled active value the `value` of the `<Tab>` to mark selected (and, when a
32
- * `<TabPanel value="…">` shares it, the panel to reveal). When set, you own selection:
33
- * the strip renders exactly what this says, and a user pick only *requests* a change
34
- * via `onStateChange` — apply it by updating this prop, reject it by leaving it
35
- * unchanged. Leave undefined (and use `defaultValue`) for uncontrolled. */
53
+ /** The tabs, as a data array (the strip's single source). Each entry is a
54
+ * `TabOption` (`value`, `label` or `children`, `icon`, `iconTrailing`, `tone`,
55
+ * `disabled`, `round`). */
56
+ options?: TabOption[];
57
+ /** Controlled active value the tab `value` to mark selected (and, when a
58
+ * `<TabPanel value="…">` shares it, the panel to reveal). When set, you own
59
+ * selection: the strip renders exactly what this says, and a user pick only
60
+ * *requests* a change via `onStateChange` — apply it by updating this prop.
61
+ * Leave undefined (and use `defaultValue`) for uncontrolled. */
36
62
  value?: string;
37
- /** Initial active value for the uncontrolled case the `value` of the `<Tab>` selected
38
- * on first render. After that `Tabs` owns selection itself. */
63
+ /** Initial active value for the uncontrolled case. After first render `Tabs`
64
+ * owns selection itself. */
39
65
  defaultValue?: string;
40
66
  /** Fired whenever the active tab changes — event-first. `detail` is
41
67
  * `{ next, prev }` (values; `null` = none). Cancelable: `event.preventDefault()`
@@ -71,42 +97,51 @@ export interface TabsProps extends Omit<BaseProps, "onChange"> {
71
97
  * is opt-in via CSS); vertical stacks them.
72
98
  * @defaultValue 'horizontal' */
73
99
  orientation?: "horizontal" | "vertical";
74
- /** How inactive panels are mounted/hidden. Per-panel `<TabPanel mounting>` overrides.
75
- * @defaultValue 'display' */
76
- mounting?: TabsMounting;
77
100
  /** Disable the sliding indicator. By default the selected-tab indicator animates
78
101
  * between tabs (a single rectangle, via CSS anchor positioning); `noslide` paints it
79
102
  * per tab so it snaps with no movement. (Browsers without anchor positioning get that
80
103
  * per-tab paint automatically — `noslide` is the explicit opt-out.) */
81
104
  noslide?: boolean;
105
+ /** Fully-round the tabs and the sliding indicator (and the primary track
106
+ * well). Applies strip-wide; a single tab's `round` rounds just that tab. A
107
+ * `number` (px) or CSS length string sets a custom radius on the top-level
108
+ * track well only — the tab pills + indicator stay fully round. */
109
+ round?: boolean | number | string;
82
110
  /** Disable the whole strip. */
83
111
  disabled?: boolean;
84
112
  }
85
113
  /**
86
- * `<Tabs>` — a tablist with optional panels, rendered from `<Tab>` / `<TabPanel>`
87
- * children.
114
+ * `<Tabs>` — a tablist with optional panels. The strip renders from the `options`
115
+ * array; panels are `<TabPanel>` children that manage their own visibility.
88
116
  *
89
- * Like `RadioGroup`, this wrapper owns the **declarative** DOM concerns the elements
90
- * deliberately don't touch each tab's **`tabindex`** (every enabled tab is its own
91
- * tab stop), `role`, the `aria-controls`/`aria-labelledby` wiring, and which panel
92
- * shows (per `mounting`). Selection itself lives in `<a-tabs>` off-DOM (it sets each
93
- * tab's `selected` property), so the elements never mutate the DOM; this wrapper
94
- * reflects the current value into panel visibility on re-render.
117
+ * The strip (`<a-tabs>`) and the panels render as flat siblings — there is no wrapper
118
+ * element, so `className` / `id` / `style` / `...rest` land on the strip. Laying the
119
+ * strip out relative to its panels is the consumer's job: a horizontal strip stacks
120
+ * above the panels in normal flow; for a vertical strip beside them, wrap `<Tabs>` in
121
+ * your own flex container. Any non-`TabPanel` children render verbatim as siblings too.
95
122
  *
96
- * Controlled (`value` + `onStateChange`) or uncontrolled (`defaultValue`). Drop the
97
- * `<TabPanel>`s and it's just a selectable strip emitting `statechange` / `change`.
123
+ * `<a-tabs>` owns selection off-DOM (it sets each tab's `selected` property and the
124
+ * roving `aria-activedescendant` via `ElementInternals`, writing no attribute to any
125
+ * tab); this wrapper mirrors the value only to render the strip's roving `tabindex`,
126
+ * and the panels read it straight from `<a-tabs>`. Controlled (`value` + `onStateChange`)
127
+ * or uncontrolled (`defaultValue`).
98
128
  *
99
129
  * Requires `@antadesign/anta/elements` (client-side only).
100
130
  *
101
131
  * @example
102
132
  * ```tsx
103
- * <Tabs defaultValue="account" label="Settings">
104
- * <Tab value="account" label="Account" icon="user" />
105
- * <Tab value="security" label="Security" />
133
+ * <Tabs
134
+ * defaultValue="account"
135
+ * label="Settings"
136
+ * options={[
137
+ * { value: "account", label: "Account", icon: "user" },
138
+ * { value: "security", label: "Security" },
139
+ * ]}
140
+ * >
106
141
  * <TabPanel value="account"><AccountForm /></TabPanel>
107
142
  * <TabPanel value="security"><SecurityForm /></TabPanel>
108
143
  * </Tabs>
109
144
  * ```
110
145
  */
111
- export declare const Tabs: ({ children, value, defaultValue, onStateChange, onChange, onValueChange, onFocus, onBlur, label, priority, tone, size, orientation, mounting, noslide, disabled, className, style, id, ...rest }: TabsProps) => any;
146
+ export declare const Tabs: ({ children, options, value, defaultValue, onStateChange, onChange, onValueChange, onFocus, onBlur, label, priority, tone, size, orientation, noslide, round, disabled, className, style, id, ...rest }: TabsProps) => any;
112
147
  export {};