ionbase-ui 0.48.0 → 0.59.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/LICENSE +21 -0
  2. package/dist/components/Accordion.d.ts +54 -0
  3. package/dist/components/Accordion.d.ts.map +1 -0
  4. package/dist/components/Accordion.js +68 -0
  5. package/dist/components/Accordion.js.map +1 -0
  6. package/dist/components/Breadcrumb.d.ts +45 -0
  7. package/dist/components/Breadcrumb.d.ts.map +1 -0
  8. package/dist/components/Breadcrumb.js +31 -0
  9. package/dist/components/Breadcrumb.js.map +1 -0
  10. package/dist/components/Checkbox.d.ts +2 -1
  11. package/dist/components/Checkbox.d.ts.map +1 -1
  12. package/dist/components/Checkbox.js +4 -2
  13. package/dist/components/Checkbox.js.map +1 -1
  14. package/dist/components/Combobox.d.ts +86 -0
  15. package/dist/components/Combobox.d.ts.map +1 -0
  16. package/dist/components/Combobox.js +261 -0
  17. package/dist/components/Combobox.js.map +1 -0
  18. package/dist/components/Drawer.d.ts +52 -0
  19. package/dist/components/Drawer.d.ts.map +1 -0
  20. package/dist/components/Drawer.js +66 -0
  21. package/dist/components/Drawer.js.map +1 -0
  22. package/dist/components/FileUpload.d.ts +79 -0
  23. package/dist/components/FileUpload.d.ts.map +1 -0
  24. package/dist/components/FileUpload.js +218 -0
  25. package/dist/components/FileUpload.js.map +1 -0
  26. package/dist/components/ProgressBar.d.ts +46 -0
  27. package/dist/components/ProgressBar.d.ts.map +1 -0
  28. package/dist/components/ProgressBar.js +52 -0
  29. package/dist/components/ProgressBar.js.map +1 -0
  30. package/dist/components/Select.d.ts +9 -2
  31. package/dist/components/Select.d.ts.map +1 -1
  32. package/dist/components/Select.js.map +1 -1
  33. package/dist/components/Skeleton.d.ts +39 -0
  34. package/dist/components/Skeleton.d.ts.map +1 -0
  35. package/dist/components/Skeleton.js +50 -0
  36. package/dist/components/Skeleton.js.map +1 -0
  37. package/dist/components/Spinner.d.ts +45 -0
  38. package/dist/components/Spinner.d.ts.map +1 -0
  39. package/dist/components/Spinner.js +44 -0
  40. package/dist/components/Spinner.js.map +1 -0
  41. package/dist/components/Textarea.d.ts +50 -0
  42. package/dist/components/Textarea.d.ts.map +1 -0
  43. package/dist/components/Textarea.js +48 -0
  44. package/dist/components/Textarea.js.map +1 -0
  45. package/dist/components/Toggle.d.ts +2 -1
  46. package/dist/components/Toggle.d.ts.map +1 -1
  47. package/dist/components/Toggle.js +4 -2
  48. package/dist/components/Toggle.js.map +1 -1
  49. package/dist/components/index.d.ts +18 -0
  50. package/dist/components/index.d.ts.map +1 -1
  51. package/dist/components/index.js +9 -0
  52. package/dist/components/index.js.map +1 -1
  53. package/dist/components/resolve-selection.d.ts +36 -0
  54. package/dist/components/resolve-selection.d.ts.map +1 -0
  55. package/dist/components/resolve-selection.js +14 -0
  56. package/dist/components/resolve-selection.js.map +1 -0
  57. package/dist/figma-descriptions.json +204 -144
  58. package/dist/figma-map.json +2311 -1643
  59. package/dist/meta/Accordion.json +123 -0
  60. package/dist/meta/AccordionItem.json +90 -0
  61. package/dist/meta/Breadcrumb.json +87 -0
  62. package/dist/meta/BreadcrumbItem.json +78 -0
  63. package/dist/meta/Checkbox.json +18 -1
  64. package/dist/meta/Combobox.json +265 -0
  65. package/dist/meta/Drawer.json +234 -0
  66. package/dist/meta/EmptyState.json +1 -1
  67. package/dist/meta/FileUpload.json +246 -0
  68. package/dist/meta/Input.json +0 -1
  69. package/dist/meta/PhoneInput.json +39 -39
  70. package/dist/meta/Popover.json +3 -3
  71. package/dist/meta/ProgressBar.json +166 -0
  72. package/dist/meta/Select.json +4 -3
  73. package/dist/meta/Skeleton.json +120 -0
  74. package/dist/meta/Spinner.json +123 -0
  75. package/dist/meta/Textarea.json +520 -0
  76. package/dist/meta/Toggle.json +18 -1
  77. package/dist/meta/Tooltip.json +3 -3
  78. package/dist/meta/components.json +2447 -361
  79. package/dist/meta/contrast.json +3130 -227
  80. package/dist/meta/index.json +149 -7
  81. package/dist/meta/patterns/index.json +1 -1
  82. package/dist/styles/accordion.css +102 -0
  83. package/dist/styles/breadcrumb.css +72 -0
  84. package/dist/styles/combobox.css +154 -0
  85. package/dist/styles/drawer.css +181 -0
  86. package/dist/styles/empty-state.css +16 -4
  87. package/dist/styles/file-upload.css +236 -0
  88. package/dist/styles/index.css +9 -0
  89. package/dist/styles/input.css +34 -15
  90. package/dist/styles/progress-bar.css +107 -0
  91. package/dist/styles/skeleton.css +76 -0
  92. package/dist/styles/spinner.css +70 -0
  93. package/dist/styles/textarea.css +178 -0
  94. package/dist/styles/tokens/base.css +0 -1
  95. package/dist/styles/tokens/theme-dark.css +0 -1
  96. package/dist/tokens/index.d.ts +0 -6
  97. package/dist/tokens/index.d.ts.map +1 -1
  98. package/dist/tokens/index.js +0 -5
  99. package/dist/tokens/index.js.map +1 -1
  100. package/llms.txt +2 -2
  101. package/package.json +19 -19
@@ -0,0 +1,218 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useCallback, useId, useRef, useState } from 'react';
4
+ const UploadIcon = () => (_jsx("svg", { viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", focusable: "false", children: _jsx("path", { d: "M12 16V4m0 0L8 8m4-4 4 4M4 16v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }));
5
+ const RemoveIcon = () => (_jsx("svg", { viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", focusable: "false", children: _jsx("path", { d: "M18 6 6 18M6 6l12 12", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }));
6
+ /**
7
+ * Bytes as a person would say them.
8
+ *
9
+ * Decimal units, not binary: a disk sold as 500 GB and a browser reporting a
10
+ * file size both mean powers of 1000, and showing "476 MiB" for a 500 MB file
11
+ * is correct arithmetic and a confusing answer.
12
+ */
13
+ const formatBytes = (bytes) => {
14
+ if (bytes < 1000)
15
+ return `${bytes} B`;
16
+ const units = ['kB', 'MB', 'GB', 'TB'];
17
+ let value = bytes / 1000;
18
+ let unit = 0;
19
+ while (value >= 1000 && unit < units.length - 1) {
20
+ value /= 1000;
21
+ unit += 1;
22
+ }
23
+ return `${value < 10 ? value.toFixed(1) : Math.round(value)} ${units[unit]}`;
24
+ };
25
+ /**
26
+ * Does a file match an `accept` list?
27
+ *
28
+ * The browser enforces `accept` in its own picker but NOT on drop, and not at
29
+ * all if the user switches the picker's filter to "All files" — which every
30
+ * platform picker offers. So the check is repeated here. Treating the native
31
+ * attribute as a guarantee is the standard way a drop zone ends up holding a
32
+ * .exe someone dragged into it.
33
+ */
34
+ const matchesAccept = (file, accept) => {
35
+ if (!accept)
36
+ return true;
37
+ const name = file.name.toLowerCase();
38
+ const type = file.type.toLowerCase();
39
+ return accept.split(',').some((raw) => {
40
+ const token = raw.trim().toLowerCase();
41
+ if (!token)
42
+ return false;
43
+ if (token.startsWith('.'))
44
+ return name.endsWith(token);
45
+ if (token.endsWith('/*'))
46
+ return type.startsWith(token.slice(0, -1));
47
+ return type === token;
48
+ });
49
+ };
50
+ /**
51
+ * FileUpload — a drop target wrapped around a real `<input type="file">`.
52
+ *
53
+ * Drawn in Figma as `File Upload` (1367:2333) — two sizes by five states. The
54
+ * measurements came from `Input` and `EmptyState` rather than being invented:
55
+ * the border, radius and disabled treatment are Input's; the centred
56
+ * icon-over-text stack is EmptyState's. Figma draws the drop zone alone, the
57
+ * same split `Input` makes against `Form Field`.
58
+ *
59
+ * THE INPUT IS THE CONTROL. THE DROP ZONE IS DECORATION.
60
+ *
61
+ * The usual build of this component is a `<div>` with drag handlers and a
62
+ * click that calls `input.click()`. That version cannot be reached by keyboard,
63
+ * has no accessible name, does not participate in a form, and does not work in
64
+ * any environment without a pointer — which includes switch access, voice
65
+ * control and most screen-reader browse modes.
66
+ *
67
+ * Here the file input is a real, focusable, labelled control that is visually
68
+ * hidden but NOT `display: none` — it keeps its place in the tab order and its
69
+ * label. Drag-and-drop is layered on top as an enhancement, and every path it
70
+ * offers is also reachable without it. That ordering is the whole component.
71
+ *
72
+ * VALIDATION IS ADVISORY, NOT SECURITY. `accept` and `maxSize` are checked here
73
+ * so the user finds out immediately instead of after an upload. A server that
74
+ * trusts either one is trusting a value the client chose.
75
+ */
76
+ export function FileUpload({ label, description, errorMessage, isInvalid, isDisabled: isDisabledProp, size = 'md', accept, multiple, maxSize, maxFiles, files: controlledFiles, onChange, onReject, prompt, hint, removeLabel = (name) => `Remove ${name}`, className, wrapperClassName, id: providedId, name: inputName, }) {
77
+ /* No `disabled` alias to resolve: FileUpload is new, so it never had one. */
78
+ const isDisabled = isDisabledProp ?? false;
79
+ const generatedId = useId();
80
+ const id = providedId ?? generatedId;
81
+ const helperId = `${id}-helper`;
82
+ const hintId = `${id}-hint`;
83
+ const inputRef = useRef(null);
84
+ const [uncontrolled, setUncontrolled] = useState([]);
85
+ const [isDragging, setIsDragging] = useState(false);
86
+ /*
87
+ * A depth counter, not a boolean. `dragleave` fires every time the pointer
88
+ * crosses into a CHILD element, so a boolean flickers off whenever the
89
+ * cursor passes over the icon or the prompt text. Counting enter/leave pairs
90
+ * is the only version that survives having children.
91
+ */
92
+ const dragDepth = useRef(0);
93
+ const files = controlledFiles ? [...controlledFiles] : uncontrolled;
94
+ const commit = useCallback((next) => {
95
+ if (!controlledFiles)
96
+ setUncontrolled(next);
97
+ onChange?.(next);
98
+ }, [controlledFiles, onChange]);
99
+ const add = useCallback((incoming) => {
100
+ if (isDisabled || incoming.length === 0)
101
+ return;
102
+ const accepted = [];
103
+ const rejected = [];
104
+ /*
105
+ * Room is counted against what is already held, so dropping four files
106
+ * onto a control holding two with maxFiles=3 accepts one and reports
107
+ * three — rather than accepting all four or refusing the lot.
108
+ */
109
+ let room = multiple ? (maxFiles ?? Infinity) - files.length : 1;
110
+ for (const file of incoming) {
111
+ if (!matchesAccept(file, accept)) {
112
+ rejected.push({
113
+ file,
114
+ reason: 'type',
115
+ message: `${file.name} is not an accepted file type.`,
116
+ });
117
+ }
118
+ else if (maxSize !== undefined && file.size > maxSize) {
119
+ rejected.push({
120
+ file,
121
+ reason: 'size',
122
+ message: `${file.name} is ${formatBytes(file.size)}, over the ${formatBytes(maxSize)} limit.`,
123
+ });
124
+ }
125
+ else if (room <= 0) {
126
+ rejected.push({
127
+ file,
128
+ reason: 'count',
129
+ message: multiple
130
+ ? `${file.name} was not added — at most ${maxFiles} files.`
131
+ : `${file.name} was not added — only one file is allowed.`,
132
+ });
133
+ }
134
+ else {
135
+ accepted.push(file);
136
+ room -= 1;
137
+ }
138
+ }
139
+ if (accepted.length)
140
+ commit(multiple ? [...files, ...accepted] : accepted);
141
+ if (rejected.length)
142
+ onReject?.(rejected);
143
+ }, [accept, commit, files, isDisabled, maxFiles, maxSize, multiple, onReject]);
144
+ const remove = (index) => {
145
+ commit(files.filter((_, i) => i !== index));
146
+ /*
147
+ * Clearing the input's own value matters: a browser fires no `change` when
148
+ * the same file is picked twice in a row, so removing a file and picking it
149
+ * again would silently do nothing.
150
+ */
151
+ if (inputRef.current)
152
+ inputRef.current.value = '';
153
+ };
154
+ const helper = isInvalid && errorMessage ? errorMessage : description;
155
+ const zoneClassNames = [
156
+ 'ion-file-upload',
157
+ size !== 'md' ? `ion-file-upload--${size}` : '',
158
+ isDragging ? 'ion-file-upload--dragging' : '',
159
+ isInvalid ? 'ion-file-upload--invalid' : '',
160
+ isDisabled ? 'ion-file-upload--disabled' : '',
161
+ className || '',
162
+ ]
163
+ .filter(Boolean)
164
+ .join(' ');
165
+ const zone = (_jsxs("div", { className: zoneClassNames, "data-dragging": isDragging || undefined, "data-invalid": isInvalid || undefined, "data-disabled": isDisabled || undefined, onDragEnter: (e) => {
166
+ if (isDisabled)
167
+ return;
168
+ e.preventDefault();
169
+ dragDepth.current += 1;
170
+ setIsDragging(true);
171
+ }, onDragOver: (e) => {
172
+ if (isDisabled)
173
+ return;
174
+ // Without this the browser navigates to the dropped file. There is no
175
+ // other reason for a dragover handler that only calls preventDefault.
176
+ e.preventDefault();
177
+ }, onDragLeave: () => {
178
+ dragDepth.current = Math.max(0, dragDepth.current - 1);
179
+ if (dragDepth.current === 0)
180
+ setIsDragging(false);
181
+ }, onDrop: (e) => {
182
+ if (isDisabled)
183
+ return;
184
+ e.preventDefault();
185
+ dragDepth.current = 0;
186
+ setIsDragging(false);
187
+ add(Array.from(e.dataTransfer.files));
188
+ }, children: [_jsx("input", { ref: inputRef, type: "file", id: id, name: inputName, className: "ion-file-upload__input", accept: accept, multiple: multiple, disabled: isDisabled, "aria-describedby": [hint ? hintId : null, helper ? helperId : null]
189
+ .filter(Boolean)
190
+ .join(' ') || undefined, "aria-invalid": isInvalid || undefined, onChange: (e) => {
191
+ add(Array.from(e.target.files ?? []));
192
+ // Same reason as remove(): re-picking an identical file must fire.
193
+ e.target.value = '';
194
+ } }), _jsxs("label", { htmlFor: id, className: "ion-file-upload__target", children: [_jsx("span", { className: "ion-file-upload__icon", "aria-hidden": "true", children: _jsx(UploadIcon, {}) }), _jsx("span", { className: "ion-file-upload__prompt", children: prompt ??
195
+ (multiple
196
+ ? 'Choose files or drag them here'
197
+ : 'Choose a file or drag it here') }), hint && (_jsx("span", { id: hintId, className: "ion-file-upload__hint", children: hint }))] })] }));
198
+ const list = files.length > 0 && (_jsx("ul", { className: "ion-file-upload__list", children: files.map((file, index) => (_jsxs("li", { className: "ion-file-upload__file", children: [_jsx("span", { className: "ion-file-upload__name", children: file.name }), _jsx("span", { className: "ion-file-upload__size", children: formatBytes(file.size) }), _jsx("button", { type: "button", className: "ion-file-upload__remove", "aria-label": removeLabel(file.name), disabled: isDisabled, onClick: () => remove(index), children: _jsx(RemoveIcon, {}) })] }, `${file.name}-${file.size}-${index}`))) }));
199
+ /*
200
+ * The count, announced. A file added by drop produces no focus change and no
201
+ * visible change anywhere near the user's attention, so without this a screen
202
+ * reader user gets no confirmation that the drop did anything at all. Polite,
203
+ * and the count rather than the names: reading six filenames over an
204
+ * assertive interruption is worse than saying how many there now are.
205
+ */
206
+ const status = (_jsx("span", { className: "ion-visually-hidden", role: "status", "aria-live": "polite", children: files.length === 0
207
+ ? ''
208
+ : `${files.length} file${files.length === 1 ? '' : 's'} selected` }));
209
+ return (_jsxs("div", { className: [
210
+ 'ion-field',
211
+ isInvalid ? 'ion-field--error' : '',
212
+ wrapperClassName || '',
213
+ ]
214
+ .filter(Boolean)
215
+ .join(' '), children: [label && (_jsx("label", { htmlFor: id, className: "ion-field__label", children: label })), zone, list, status, helper && (_jsx("span", { id: helperId, className: "ion-field__helper", children: helper }))] }));
216
+ }
217
+ FileUpload.displayName = 'FileUpload';
218
+ //# sourceMappingURL=FileUpload.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FileUpload.js","sourceRoot":"","sources":["../../src/components/FileUpload.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ import React from 'react';
2
+ export type ProgressBarIntent = 'primary' | 'success' | 'warning' | 'error';
3
+ export type ProgressBarSize = 'sm' | 'md';
4
+ export interface ProgressBarProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> {
5
+ /** What is progressing. Required — a bare bar announces a number and no noun. */
6
+ label: string;
7
+ /** 0 to `max`. Omit for an indeterminate bar. */
8
+ value?: number;
9
+ /** Defaults to 100. */
10
+ max?: number;
11
+ intent?: ProgressBarIntent;
12
+ size?: ProgressBarSize;
13
+ /** Show the label as text above the track. */
14
+ isLabelVisible?: boolean;
15
+ /** Show the percentage beside the label. Determinate bars only. */
16
+ isValueVisible?: boolean;
17
+ /**
18
+ * Spoken instead of the percentage — "3 of 12 files". A percentage is rarely
19
+ * the thing a person wants read aloud.
20
+ */
21
+ valueText?: string;
22
+ }
23
+ /**
24
+ * ProgressBar — a determinate or indeterminate measure of work.
25
+ *
26
+ * DETERMINATE AND INDETERMINATE ARE THE SAME COMPONENT ON PURPOSE
27
+ *
28
+ * Work that starts unmeasurable and becomes measurable is the common case — a
29
+ * upload that does not know its size until the first chunk lands. Splitting
30
+ * them into two components would make that transition a swap, which remounts
31
+ * the node and loses the live region with it.
32
+ *
33
+ * Omitting `value` is what makes it indeterminate. `aria-valuenow` is then
34
+ * omitted too, which is exactly what the ARIA spec asks for and is the detail
35
+ * hand-rolled progress bars get wrong most often — a bar reporting
36
+ * `aria-valuenow="0"` forever tells a screen reader the work is stuck at zero,
37
+ * not that it is unmeasured.
38
+ *
39
+ * WHY `label` IS REQUIRED
40
+ *
41
+ * `role="progressbar"` announces a number. Without a name the user hears "42
42
+ * percent" with no indication of what is at 42 percent, which is worse than
43
+ * silence because it sounds like information.
44
+ */
45
+ export declare const ProgressBar: React.ForwardRefExoticComponent<ProgressBarProps & React.RefAttributes<HTMLDivElement>>;
46
+ //# sourceMappingURL=ProgressBar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProgressBar.d.ts","sourceRoot":"","sources":["../../src/components/ProgressBar.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4B,MAAM,OAAO,CAAC;AAEjD,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;AAC5E,MAAM,MAAM,eAAe,GAAG,IAAI,GAAG,IAAI,CAAC;AAE1C,MAAM,WAAW,gBAAiB,SAAQ,IAAI,CAC5C,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,EACpC,UAAU,CACX;IACC,iFAAiF;IACjF,KAAK,EAAE,MAAM,CAAC;IACd,iDAAiD;IACjD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uBAAuB;IACvB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB,8CAA8C;IAC9C,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,mEAAmE;IACnE,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAID;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,WAAW,yFA6EvB,CAAC"}
@@ -0,0 +1,52 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { forwardRef, useId } from 'react';
3
+ const clamp = (n, max) => Math.min(Math.max(n, 0), max);
4
+ /**
5
+ * ProgressBar — a determinate or indeterminate measure of work.
6
+ *
7
+ * DETERMINATE AND INDETERMINATE ARE THE SAME COMPONENT ON PURPOSE
8
+ *
9
+ * Work that starts unmeasurable and becomes measurable is the common case — a
10
+ * upload that does not know its size until the first chunk lands. Splitting
11
+ * them into two components would make that transition a swap, which remounts
12
+ * the node and loses the live region with it.
13
+ *
14
+ * Omitting `value` is what makes it indeterminate. `aria-valuenow` is then
15
+ * omitted too, which is exactly what the ARIA spec asks for and is the detail
16
+ * hand-rolled progress bars get wrong most often — a bar reporting
17
+ * `aria-valuenow="0"` forever tells a screen reader the work is stuck at zero,
18
+ * not that it is unmeasured.
19
+ *
20
+ * WHY `label` IS REQUIRED
21
+ *
22
+ * `role="progressbar"` announces a number. Without a name the user hears "42
23
+ * percent" with no indication of what is at 42 percent, which is worse than
24
+ * silence because it sounds like information.
25
+ */
26
+ export const ProgressBar = forwardRef(({ label, value, max = 100, intent = 'primary', size = 'md', isLabelVisible = false, isValueVisible = false, valueText, className, ...rest }, ref) => {
27
+ const labelId = useId();
28
+ const isIndeterminate = value === undefined;
29
+ const resolved = isIndeterminate ? 0 : clamp(value, max);
30
+ const pct = max > 0 ? (resolved / max) * 100 : 0;
31
+ const classNames = [
32
+ 'ion-progress-bar',
33
+ intent !== 'primary' ? `ion-progress-bar--${intent}` : '',
34
+ size !== 'md' ? `ion-progress-bar--${size}` : '',
35
+ isIndeterminate ? 'ion-progress-bar--indeterminate' : '',
36
+ className || '',
37
+ ]
38
+ .filter(Boolean)
39
+ .join(' ');
40
+ return (_jsxs("div", { ...rest, ref: ref, className: classNames, children: [_jsxs("div", { className: isLabelVisible ? 'ion-progress-bar__header' : 'ion-visually-hidden', children: [_jsx("span", { id: labelId, className: "ion-progress-bar__label", children: label }), isLabelVisible && isValueVisible && !isIndeterminate && (_jsx("span", { className: "ion-progress-bar__value", children: valueText ?? `${Math.round(pct)}%` }))] }), _jsx("div", { className: "ion-progress-bar__track", role: "progressbar", "aria-labelledby": labelId, "aria-valuemin": 0, "aria-valuemax": max, ...(isIndeterminate
41
+ ? {}
42
+ : {
43
+ 'aria-valuenow': resolved,
44
+ ...(valueText ? { 'aria-valuetext': valueText } : {}),
45
+ }), children: _jsx("div", { className: "ion-progress-bar__fill", style: isIndeterminate
46
+ ? undefined
47
+ : {
48
+ '--ion-progress-bar-pct': `${pct}%`,
49
+ } }) })] }));
50
+ });
51
+ ProgressBar.displayName = 'ProgressBar';
52
+ //# sourceMappingURL=ProgressBar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProgressBar.js","sourceRoot":"","sources":["../../src/components/ProgressBar.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AA4BjD,MAAM,KAAK,GAAG,CAAC,CAAS,EAAE,GAAW,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AAExE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,UAAU,CACnC,CACE,EACE,KAAK,EACL,KAAK,EACL,GAAG,GAAG,GAAG,EACT,MAAM,GAAG,SAAS,EAClB,IAAI,GAAG,IAAI,EACX,cAAc,GAAG,KAAK,EACtB,cAAc,GAAG,KAAK,EACtB,SAAS,EACT,SAAS,EACT,GAAG,IAAI,EACR,EACD,GAAG,EACH,EAAE;IACF,MAAM,OAAO,GAAG,KAAK,EAAE,CAAC;IACxB,MAAM,eAAe,GAAG,KAAK,KAAK,SAAS,CAAC;IAC5C,MAAM,QAAQ,GAAG,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IACzD,MAAM,GAAG,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAEjD,MAAM,UAAU,GAAG;QACjB,kBAAkB;QAClB,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,qBAAqB,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE;QACzD,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,qBAAqB,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE;QAChD,eAAe,CAAC,CAAC,CAAC,iCAAiC,CAAC,CAAC,CAAC,EAAE;QACxD,SAAS,IAAI,EAAE;KAChB;SACE,MAAM,CAAC,OAAO,CAAC;SACf,IAAI,CAAC,GAAG,CAAC,CAAC;IAEb,OAAO,CACL,kBAAS,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,SAAS,EAAE,UAAU,aAC5C,eACE,SAAS,EACP,cAAc,CAAC,CAAC,CAAC,0BAA0B,CAAC,CAAC,CAAC,qBAAqB,aAGrE,eAAM,EAAE,EAAE,OAAO,EAAE,SAAS,EAAC,yBAAyB,YACnD,KAAK,GACD,EACN,cAAc,IAAI,cAAc,IAAI,CAAC,eAAe,IAAI,CACvD,eAAM,SAAS,EAAC,yBAAyB,YACtC,SAAS,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,GAC9B,CACR,IACG,EAEN,cACE,SAAS,EAAC,yBAAyB,EACnC,IAAI,EAAC,aAAa,qBACD,OAAO,mBACT,CAAC,mBACD,GAAG,KAGd,CAAC,eAAe;oBAClB,CAAC,CAAC,EAAE;oBACJ,CAAC,CAAC;wBACE,eAAe,EAAE,QAAQ;wBACzB,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,gBAAgB,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;qBACtD,CAAC,YAEN,cACE,SAAS,EAAC,wBAAwB,EAClC,KAAK,EACH,eAAe;wBACb,CAAC,CAAC,SAAS;wBACX,CAAC,CAAE;4BACC,wBAAwB,EAAE,GAAG,GAAG,GAAG;yBACZ,GAE/B,GACE,IACF,CACP,CAAC;AACJ,CAAC,CACF,CAAC;AAEF,WAAW,CAAC,WAAW,GAAG,aAAa,CAAC"}
@@ -8,8 +8,15 @@ export interface SelectOption {
8
8
  export interface SelectProps extends Omit<React.SelectHTMLAttributes<HTMLSelectElement>, 'size' | 'disabled'> {
9
9
  /** Matches the Figma `Size` variant: Small, Medium, Large. */
10
10
  size?: SelectSize;
11
- /** The options to render. Omit and pass `children` for grouped options. */
12
- options?: SelectOption[];
11
+ /**
12
+ * The options to render. Omit and pass `children` for grouped options.
13
+ *
14
+ * `readonly` because an options list is almost always a module-level constant,
15
+ * and `as const` on one produces a readonly array that a mutable parameter
16
+ * rejects. The component only ever maps over it, so demanding a mutable array
17
+ * bought nothing and cost callers a spread or a cast.
18
+ */
19
+ options?: readonly SelectOption[];
13
20
  /**
14
21
  * Text shown when nothing is selected. Renders as a disabled option so the
15
22
  * field can start empty without that being a valid choice.
@@ -1 +1 @@
1
- {"version":3,"file":"Select.d.ts","sourceRoot":"","sources":["../../src/components/Select.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAyD,MAAM,OAAO,CAAC;AAI9E,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAE5C,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,WAAY,SAAQ,IAAI,CACvC,KAAK,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAC7C,MAAM,GAAG,UAAU,CACpB;IACC,8DAA8D;IAC9D,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,2EAA2E;IAC3E,OAAO,CAAC,EAAE,YAAY,EAAE,CAAC;IACzB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8EAA8E;IAC9E,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,mCAAmC;IACnC,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC9B,wDAAwD;IACxD,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC/B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gFAAgF;IAChF,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAuBD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,MAAM,uFAiIlB,CAAC"}
1
+ {"version":3,"file":"Select.d.ts","sourceRoot":"","sources":["../../src/components/Select.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAyD,MAAM,OAAO,CAAC;AAI9E,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAE5C,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,WAAY,SAAQ,IAAI,CACvC,KAAK,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAC7C,MAAM,GAAG,UAAU,CACpB;IACC,8DAA8D;IAC9D,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,SAAS,YAAY,EAAE,CAAC;IAClC;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8EAA8E;IAC9E,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,mCAAmC;IACnC,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC9B,wDAAwD;IACxD,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC/B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gFAAgF;IAChF,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAuBD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,MAAM,uFAiIlB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"Select.js","sourceRoot":"","sources":["../../src/components/Select.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Select.js","sourceRoot":"","sources":["../../src/components/Select.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ import React from 'react';
2
+ export type SkeletonVariant = 'text' | 'circle' | 'rect';
3
+ export interface SkeletonProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> {
4
+ variant?: SkeletonVariant;
5
+ /** Any CSS length. Defaults to filling the width available. */
6
+ width?: string;
7
+ /** Any CSS length. `text` derives its height from the line box instead. */
8
+ height?: string;
9
+ /** `text` only: how many lines to draw. The last is drawn short. */
10
+ lines?: number;
11
+ }
12
+ /**
13
+ * Skeleton — the shape of content that has not arrived.
14
+ *
15
+ * IT IS HIDDEN FROM ASSISTIVE TECHNOLOGY, ALWAYS, AND THAT IS THE WHOLE POINT
16
+ *
17
+ * A skeleton is a picture of content, and a screen reader cannot use a picture
18
+ * of content. Announcing it produces a stream of empty boxes between the user
19
+ * and the thing they asked for. So every skeleton is `aria-hidden`, with no way
20
+ * to opt out — a prop for that would only ever be used by mistake.
21
+ *
22
+ * WHICH MEANS THE CALLER OWES THE ANNOUNCEMENT, AND THIS COMPONENT CANNOT DO IT
23
+ *
24
+ * Hiding the placeholder is only half an answer: something still has to tell a
25
+ * screen-reader user that the region is loading. That belongs on the region
26
+ * being replaced — `aria-busy="true"` while it loads — because only the caller
27
+ * knows where that region starts and ends. `Spinner` is the other half when the
28
+ * wait deserves an announcement of its own.
29
+ *
30
+ * <section aria-busy={isLoading}>
31
+ * {isLoading ? <Skeleton lines={3} /> : <Rows data={data} />}
32
+ * </section>
33
+ *
34
+ * This is the one component in the system whose correct use REQUIRES something
35
+ * of the caller that the type system cannot check, which is why it is stated
36
+ * here, in the contract, and in the story.
37
+ */
38
+ export declare const Skeleton: React.ForwardRefExoticComponent<SkeletonProps & React.RefAttributes<HTMLDivElement>>;
39
+ //# sourceMappingURL=Skeleton.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Skeleton.d.ts","sourceRoot":"","sources":["../../src/components/Skeleton.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAqB,MAAM,OAAO,CAAC;AAE1C,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEzD,MAAM,WAAW,aAAc,SAAQ,IAAI,CACzC,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,EACpC,UAAU,CACX;IACC,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,+DAA+D;IAC/D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2EAA2E;IAC3E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oEAAoE;IACpE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,QAAQ,sFAiDpB,CAAC"}
@@ -0,0 +1,50 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { forwardRef } from 'react';
3
+ /**
4
+ * Skeleton — the shape of content that has not arrived.
5
+ *
6
+ * IT IS HIDDEN FROM ASSISTIVE TECHNOLOGY, ALWAYS, AND THAT IS THE WHOLE POINT
7
+ *
8
+ * A skeleton is a picture of content, and a screen reader cannot use a picture
9
+ * of content. Announcing it produces a stream of empty boxes between the user
10
+ * and the thing they asked for. So every skeleton is `aria-hidden`, with no way
11
+ * to opt out — a prop for that would only ever be used by mistake.
12
+ *
13
+ * WHICH MEANS THE CALLER OWES THE ANNOUNCEMENT, AND THIS COMPONENT CANNOT DO IT
14
+ *
15
+ * Hiding the placeholder is only half an answer: something still has to tell a
16
+ * screen-reader user that the region is loading. That belongs on the region
17
+ * being replaced — `aria-busy="true"` while it loads — because only the caller
18
+ * knows where that region starts and ends. `Spinner` is the other half when the
19
+ * wait deserves an announcement of its own.
20
+ *
21
+ * <section aria-busy={isLoading}>
22
+ * {isLoading ? <Skeleton lines={3} /> : <Rows data={data} />}
23
+ * </section>
24
+ *
25
+ * This is the one component in the system whose correct use REQUIRES something
26
+ * of the caller that the type system cannot check, which is why it is stated
27
+ * here, in the contract, and in the story.
28
+ */
29
+ export const Skeleton = forwardRef(({ variant = 'text', width, height, lines = 1, className, ...rest }, ref) => {
30
+ const classNames = [
31
+ 'ion-skeleton',
32
+ `ion-skeleton--${variant}`,
33
+ className || '',
34
+ ]
35
+ .filter(Boolean)
36
+ .join(' ');
37
+ // Lengths travel as custom properties rather than inline width/height, so a
38
+ // caller never writes a raw value into a style attribute the lint rules
39
+ // read. See ProgressBar for the same trade.
40
+ const style = {
41
+ ...(width ? { '--ion-skeleton-width': width } : {}),
42
+ ...(height ? { '--ion-skeleton-height': height } : {}),
43
+ };
44
+ if (variant === 'text' && lines > 1) {
45
+ return (_jsx("div", { ...rest, ref: ref, "aria-hidden": "true", className: "ion-skeleton-group", style: style, children: Array.from({ length: lines }, (_, i) => (_jsx("div", { className: `${classNames}${i === lines - 1 ? ' ion-skeleton--last' : ''}` }, i))) }));
46
+ }
47
+ return (_jsx("div", { ...rest, ref: ref, "aria-hidden": "true", className: classNames, style: style }));
48
+ });
49
+ Skeleton.displayName = 'Skeleton';
50
+ //# sourceMappingURL=Skeleton.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Skeleton.js","sourceRoot":"","sources":["../../src/components/Skeleton.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAiB1C;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,UAAU,CAChC,CAAC,EAAE,OAAO,GAAG,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,GAAG,CAAC,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,GAAG,EAAE,EAAE;IAC1E,MAAM,UAAU,GAAG;QACjB,cAAc;QACd,iBAAiB,OAAO,EAAE;QAC1B,SAAS,IAAI,EAAE;KAChB;SACE,MAAM,CAAC,OAAO,CAAC;SACf,IAAI,CAAC,GAAG,CAAC,CAAC;IAEb,4EAA4E;IAC5E,wEAAwE;IACxE,4CAA4C;IAC5C,MAAM,KAAK,GAAG;QACZ,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,sBAAsB,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACnD,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,uBAAuB,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAChC,CAAC;IAEzB,IAAI,OAAO,KAAK,MAAM,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QACpC,OAAO,CACL,iBACM,IAAI,EACR,GAAG,EAAE,GAAG,iBACI,MAAM,EAClB,SAAS,EAAC,oBAAoB,EAC9B,KAAK,EAAE,KAAK,YAEX,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CACvC,cAEE,SAAS,EAAE,GAAG,UAAU,GACtB,CAAC,KAAK,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,EAC5C,EAAE,IAHG,CAAC,CAIN,CACH,CAAC,GACE,CACP,CAAC;IACJ,CAAC;IAED,OAAO,CACL,iBACM,IAAI,EACR,GAAG,EAAE,GAAG,iBACI,MAAM,EAClB,SAAS,EAAE,UAAU,EACrB,KAAK,EAAE,KAAK,GACZ,CACH,CAAC;AACJ,CAAC,CACF,CAAC;AAEF,QAAQ,CAAC,WAAW,GAAG,UAAU,CAAC"}
@@ -0,0 +1,45 @@
1
+ import React from 'react';
2
+ export type SpinnerSize = 'sm' | 'md' | 'lg';
3
+ export interface SpinnerProps extends Omit<React.HTMLAttributes<HTMLSpanElement>, 'children'> {
4
+ /** Matches the icon size ramp: sm, md, lg. */
5
+ size?: SpinnerSize;
6
+ /** What is loading. Announced politely; visible only with `isLabelVisible`. */
7
+ label?: string;
8
+ /** Render the label as text beside the ring as well as announcing it. */
9
+ isLabelVisible?: boolean;
10
+ /**
11
+ * The spinner conveys nothing on its own — something else already says the
12
+ * region is busy. Renders inert: no role, no live region, hidden from AT.
13
+ */
14
+ isDecorative?: boolean;
15
+ }
16
+ /**
17
+ * Spinner — an indeterminate wait, announced once.
18
+ *
19
+ * WHY THIS IS NOT A DIV WITH A CSS ANIMATION
20
+ *
21
+ * The obvious implementation is inaccessible in a specific and common way: a
22
+ * spinning graphic with no text is silence to a screen reader, so the user is
23
+ * told nothing happened. Generated code in this repo's own eval corpus reached
24
+ * for a bare `role="status"` in 48 files, which is the right instinct and only
25
+ * half the job — the role creates a live region, but an empty one announces
26
+ * nothing at all.
27
+ *
28
+ * So the default carries a label. It is visually hidden, it lives inside the
29
+ * live region, and it is what makes "Loading" reach the user who cannot see the
30
+ * ring.
31
+ *
32
+ * `isDecorative` exists because the opposite mistake is just as easy. A spinner
33
+ * inside a button whose label already changed to "Saving…" would announce
34
+ * twice, and two live regions racing is worse than one. Decorative renders no
35
+ * role at all rather than `aria-hidden` on a live region, which browsers treat
36
+ * inconsistently.
37
+ *
38
+ * MOTION
39
+ *
40
+ * `prefers-reduced-motion` slows the rotation rather than stopping it. A
41
+ * stationary spinner is not a calmer spinner — it is a broken one, and it
42
+ * removes the only signal a sighted user has that the wait is still live.
43
+ */
44
+ export declare const Spinner: React.ForwardRefExoticComponent<SpinnerProps & React.RefAttributes<HTMLSpanElement>>;
45
+ //# sourceMappingURL=Spinner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Spinner.d.ts","sourceRoot":"","sources":["../../src/components/Spinner.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAqB,MAAM,OAAO,CAAC;AAE1C,MAAM,MAAM,WAAW,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAE7C,MAAM,WAAW,YAAa,SAAQ,IAAI,CACxC,KAAK,CAAC,cAAc,CAAC,eAAe,CAAC,EACrC,UAAU,CACX;IACC,8CAA8C;IAC9C,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,+EAA+E;IAC/E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,eAAO,MAAM,OAAO,sFA0CnB,CAAC"}
@@ -0,0 +1,44 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { forwardRef } from 'react';
3
+ /**
4
+ * Spinner — an indeterminate wait, announced once.
5
+ *
6
+ * WHY THIS IS NOT A DIV WITH A CSS ANIMATION
7
+ *
8
+ * The obvious implementation is inaccessible in a specific and common way: a
9
+ * spinning graphic with no text is silence to a screen reader, so the user is
10
+ * told nothing happened. Generated code in this repo's own eval corpus reached
11
+ * for a bare `role="status"` in 48 files, which is the right instinct and only
12
+ * half the job — the role creates a live region, but an empty one announces
13
+ * nothing at all.
14
+ *
15
+ * So the default carries a label. It is visually hidden, it lives inside the
16
+ * live region, and it is what makes "Loading" reach the user who cannot see the
17
+ * ring.
18
+ *
19
+ * `isDecorative` exists because the opposite mistake is just as easy. A spinner
20
+ * inside a button whose label already changed to "Saving…" would announce
21
+ * twice, and two live regions racing is worse than one. Decorative renders no
22
+ * role at all rather than `aria-hidden` on a live region, which browsers treat
23
+ * inconsistently.
24
+ *
25
+ * MOTION
26
+ *
27
+ * `prefers-reduced-motion` slows the rotation rather than stopping it. A
28
+ * stationary spinner is not a calmer spinner — it is a broken one, and it
29
+ * removes the only signal a sighted user has that the wait is still live.
30
+ */
31
+ export const Spinner = forwardRef(({ size = 'md', label = 'Loading', isLabelVisible = false, isDecorative = false, className, ...rest }, ref) => {
32
+ const classNames = [
33
+ 'ion-spinner',
34
+ size !== 'md' ? `ion-spinner--${size}` : '',
35
+ className || '',
36
+ ]
37
+ .filter(Boolean)
38
+ .join(' ');
39
+ return (_jsxs("span", { ...rest, ref: ref, className: classNames, ...(isDecorative
40
+ ? { 'aria-hidden': true }
41
+ : { role: 'status', 'aria-live': 'polite' }), children: [_jsx("span", { className: "ion-spinner__ring", "aria-hidden": "true" }), !isDecorative && (_jsx("span", { className: isLabelVisible ? 'ion-spinner__label' : 'ion-visually-hidden', children: label }))] }));
42
+ });
43
+ Spinner.displayName = 'Spinner';
44
+ //# sourceMappingURL=Spinner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Spinner.js","sourceRoot":"","sources":["../../src/components/Spinner.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAqB1C;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,UAAU,CAC/B,CACE,EACE,IAAI,GAAG,IAAI,EACX,KAAK,GAAG,SAAS,EACjB,cAAc,GAAG,KAAK,EACtB,YAAY,GAAG,KAAK,EACpB,SAAS,EACT,GAAG,IAAI,EACR,EACD,GAAG,EACH,EAAE;IACF,MAAM,UAAU,GAAG;QACjB,aAAa;QACb,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,gBAAgB,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE;QAC3C,SAAS,IAAI,EAAE;KAChB;SACE,MAAM,CAAC,OAAO,CAAC;SACf,IAAI,CAAC,GAAG,CAAC,CAAC;IAEb,OAAO,CACL,mBACM,IAAI,EACR,GAAG,EAAE,GAAG,EACR,SAAS,EAAE,UAAU,KACjB,CAAC,YAAY;YACf,CAAC,CAAC,EAAE,aAAa,EAAE,IAAI,EAAE;YACzB,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC,aAE9C,eAAM,SAAS,EAAC,mBAAmB,iBAAa,MAAM,GAAG,EACxD,CAAC,YAAY,IAAI,CAChB,eACE,SAAS,EACP,cAAc,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,qBAAqB,YAG9D,KAAK,GACD,CACR,IACI,CACR,CAAC;AACJ,CAAC,CACF,CAAC;AAEF,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC"}