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.
- package/LICENSE +21 -0
- package/dist/components/Accordion.d.ts +54 -0
- package/dist/components/Accordion.d.ts.map +1 -0
- package/dist/components/Accordion.js +68 -0
- package/dist/components/Accordion.js.map +1 -0
- package/dist/components/Breadcrumb.d.ts +45 -0
- package/dist/components/Breadcrumb.d.ts.map +1 -0
- package/dist/components/Breadcrumb.js +31 -0
- package/dist/components/Breadcrumb.js.map +1 -0
- package/dist/components/Checkbox.d.ts +2 -1
- package/dist/components/Checkbox.d.ts.map +1 -1
- package/dist/components/Checkbox.js +4 -2
- package/dist/components/Checkbox.js.map +1 -1
- package/dist/components/Combobox.d.ts +86 -0
- package/dist/components/Combobox.d.ts.map +1 -0
- package/dist/components/Combobox.js +261 -0
- package/dist/components/Combobox.js.map +1 -0
- package/dist/components/Drawer.d.ts +52 -0
- package/dist/components/Drawer.d.ts.map +1 -0
- package/dist/components/Drawer.js +66 -0
- package/dist/components/Drawer.js.map +1 -0
- package/dist/components/FileUpload.d.ts +79 -0
- package/dist/components/FileUpload.d.ts.map +1 -0
- package/dist/components/FileUpload.js +218 -0
- package/dist/components/FileUpload.js.map +1 -0
- package/dist/components/ProgressBar.d.ts +46 -0
- package/dist/components/ProgressBar.d.ts.map +1 -0
- package/dist/components/ProgressBar.js +52 -0
- package/dist/components/ProgressBar.js.map +1 -0
- package/dist/components/Select.d.ts +9 -2
- package/dist/components/Select.d.ts.map +1 -1
- package/dist/components/Select.js.map +1 -1
- package/dist/components/Skeleton.d.ts +39 -0
- package/dist/components/Skeleton.d.ts.map +1 -0
- package/dist/components/Skeleton.js +50 -0
- package/dist/components/Skeleton.js.map +1 -0
- package/dist/components/Spinner.d.ts +45 -0
- package/dist/components/Spinner.d.ts.map +1 -0
- package/dist/components/Spinner.js +44 -0
- package/dist/components/Spinner.js.map +1 -0
- package/dist/components/Textarea.d.ts +50 -0
- package/dist/components/Textarea.d.ts.map +1 -0
- package/dist/components/Textarea.js +48 -0
- package/dist/components/Textarea.js.map +1 -0
- package/dist/components/Toggle.d.ts +2 -1
- package/dist/components/Toggle.d.ts.map +1 -1
- package/dist/components/Toggle.js +4 -2
- package/dist/components/Toggle.js.map +1 -1
- package/dist/components/index.d.ts +18 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +9 -0
- package/dist/components/index.js.map +1 -1
- package/dist/components/resolve-selection.d.ts +36 -0
- package/dist/components/resolve-selection.d.ts.map +1 -0
- package/dist/components/resolve-selection.js +14 -0
- package/dist/components/resolve-selection.js.map +1 -0
- package/dist/figma-descriptions.json +204 -144
- package/dist/figma-map.json +2311 -1643
- package/dist/meta/Accordion.json +123 -0
- package/dist/meta/AccordionItem.json +90 -0
- package/dist/meta/Breadcrumb.json +87 -0
- package/dist/meta/BreadcrumbItem.json +78 -0
- package/dist/meta/Checkbox.json +18 -1
- package/dist/meta/Combobox.json +265 -0
- package/dist/meta/Drawer.json +234 -0
- package/dist/meta/EmptyState.json +1 -1
- package/dist/meta/FileUpload.json +246 -0
- package/dist/meta/Input.json +0 -1
- package/dist/meta/PhoneInput.json +39 -39
- package/dist/meta/Popover.json +3 -3
- package/dist/meta/ProgressBar.json +166 -0
- package/dist/meta/Select.json +4 -3
- package/dist/meta/Skeleton.json +120 -0
- package/dist/meta/Spinner.json +123 -0
- package/dist/meta/Textarea.json +520 -0
- package/dist/meta/Toggle.json +18 -1
- package/dist/meta/Tooltip.json +3 -3
- package/dist/meta/components.json +2447 -361
- package/dist/meta/contrast.json +3130 -227
- package/dist/meta/index.json +149 -7
- package/dist/meta/patterns/index.json +1 -1
- package/dist/styles/accordion.css +102 -0
- package/dist/styles/breadcrumb.css +72 -0
- package/dist/styles/combobox.css +154 -0
- package/dist/styles/drawer.css +181 -0
- package/dist/styles/empty-state.css +16 -4
- package/dist/styles/file-upload.css +236 -0
- package/dist/styles/index.css +9 -0
- package/dist/styles/input.css +34 -15
- package/dist/styles/progress-bar.css +107 -0
- package/dist/styles/skeleton.css +76 -0
- package/dist/styles/spinner.css +70 -0
- package/dist/styles/textarea.css +178 -0
- package/dist/styles/tokens/base.css +0 -1
- package/dist/styles/tokens/theme-dark.css +0 -1
- package/dist/tokens/index.d.ts +0 -6
- package/dist/tokens/index.d.ts.map +1 -1
- package/dist/tokens/index.js +0 -5
- package/dist/tokens/index.js.map +1 -1
- package/llms.txt +2 -2
- 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
|
-
/**
|
|
12
|
-
|
|
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
|
|
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":"AAAA,YAAY,CAAC;;AAEb,OAAc,EAAE,UAAU,EAAE,MAAM,EAAE,mBAAmB,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAC9E,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;
|
|
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"}
|