@adia-ai/web-components 0.6.33 → 0.6.34
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/CHANGELOG.md +22 -0
- package/components/accordion/accordion.css +2 -2
- package/components/action-list/action-list.css +2 -2
- package/components/agent-artifact/agent-artifact.css +31 -31
- package/components/agent-feedback-bar/agent-feedback-bar.css +10 -10
- package/components/agent-questions/agent-questions.css +57 -57
- package/components/agent-reasoning/agent-reasoning.css +62 -62
- package/components/agent-suggestions/agent-suggestions.css +4 -4
- package/components/agent-trace/agent-trace.css +53 -53
- package/components/alert/alert.css +41 -41
- package/components/avatar/avatar.css +27 -27
- package/components/badge/badge.css +27 -27
- package/components/block/block.css +16 -16
- package/components/breadcrumb/breadcrumb.css +23 -23
- package/components/button/button.css +101 -91
- package/components/calendar-grid/calendar-grid.a2ui.json +136 -0
- package/components/calendar-grid/calendar-grid.css +226 -0
- package/components/calendar-grid/calendar-grid.d.ts +37 -0
- package/components/calendar-grid/calendar-grid.js +17 -0
- package/components/calendar-grid/calendar-grid.yaml +116 -0
- package/components/calendar-grid/class.js +300 -0
- package/components/calendar-picker/calendar-picker.css +139 -139
- package/components/canvas/canvas.css +12 -12
- package/components/card/card.css +83 -83
- package/components/chart/chart.css +224 -224
- package/components/chart-legend/chart-legend.css +26 -26
- package/components/check/check.css +40 -40
- package/components/code/code.css +125 -125
- package/components/col/col.css +15 -15
- package/components/color-picker/color-picker.css +55 -55
- package/components/combobox/class.js +861 -0
- package/components/combobox/combobox.a2ui.json +363 -0
- package/components/combobox/combobox.css +244 -0
- package/components/combobox/combobox.d.ts +113 -0
- package/components/combobox/combobox.examples.md +59 -0
- package/components/combobox/combobox.js +17 -0
- package/components/combobox/combobox.test.js +181 -0
- package/components/combobox/combobox.yaml +369 -0
- package/components/command/command.css +90 -90
- package/components/date-range-picker/class.js +775 -0
- package/components/date-range-picker/date-range-picker.a2ui.json +300 -0
- package/components/date-range-picker/date-range-picker.css +178 -0
- package/components/date-range-picker/date-range-picker.d.ts +82 -0
- package/components/date-range-picker/date-range-picker.examples.md +37 -0
- package/components/date-range-picker/date-range-picker.js +17 -0
- package/components/date-range-picker/date-range-picker.test.js +387 -0
- package/components/date-range-picker/date-range-picker.yaml +285 -0
- package/components/datetime-picker/class.js +706 -0
- package/components/datetime-picker/datetime-picker.a2ui.json +334 -0
- package/components/datetime-picker/datetime-picker.css +150 -0
- package/components/datetime-picker/datetime-picker.d.ts +86 -0
- package/components/datetime-picker/datetime-picker.examples.md +46 -0
- package/components/datetime-picker/datetime-picker.js +17 -0
- package/components/datetime-picker/datetime-picker.test.js +454 -0
- package/components/datetime-picker/datetime-picker.yaml +332 -0
- package/components/demo-toggle/demo-toggle.css +27 -27
- package/components/description-list/description-list.css +18 -18
- package/components/divider/divider.css +24 -24
- package/components/embed/embed.css +6 -6
- package/components/empty-state/empty-state.css +27 -27
- package/components/feed/feed.css +12 -12
- package/components/field/field.css +28 -28
- package/components/fields/fields.css +5 -5
- package/components/grid/grid.css +5 -5
- package/components/heatmap/heatmap.css +63 -63
- package/components/icon/icon.css +12 -12
- package/components/image/image.css +14 -14
- package/components/index.js +8 -0
- package/components/input/input.css +66 -66
- package/components/inspector/inspector.css +6 -6
- package/components/integration-card/class.js +410 -0
- package/components/integration-card/integration-card.a2ui.json +268 -0
- package/components/integration-card/integration-card.css +169 -0
- package/components/integration-card/integration-card.d.ts +63 -0
- package/components/integration-card/integration-card.examples.md +41 -0
- package/components/integration-card/integration-card.js +17 -0
- package/components/integration-card/integration-card.test.js +306 -0
- package/components/integration-card/integration-card.yaml +280 -0
- package/components/kbd/kbd.css +32 -32
- package/components/link/link.css +12 -12
- package/components/list/list.css +8 -8
- package/components/list-window/class.js +688 -0
- package/components/list-window/list-window.a2ui.json +277 -0
- package/components/list-window/list-window.css +124 -0
- package/components/list-window/list-window.d.ts +84 -0
- package/components/list-window/list-window.examples.md +73 -0
- package/components/list-window/list-window.js +17 -0
- package/components/list-window/list-window.test.js +303 -0
- package/components/list-window/list-window.yaml +270 -0
- package/components/menu/menu.css +8 -8
- package/components/modal/modal.css +43 -43
- package/components/nav/nav.css +40 -40
- package/components/nav-group/nav-group.css +52 -52
- package/components/nav-item/nav-item.css +44 -44
- package/components/noodles/noodles.css +31 -31
- package/components/option-card/option-card.css +69 -69
- package/components/otp-input/otp-input.css +30 -30
- package/components/page/page.css +18 -18
- package/components/pagination/pagination.css +61 -61
- package/components/pane/pane.css +57 -57
- package/components/pipeline-status/pipeline-status.css +65 -65
- package/components/popover/popover.css +17 -17
- package/components/progress/progress.css +23 -23
- package/components/progress-row/progress-row.css +17 -17
- package/components/radio/radio.css +39 -39
- package/components/range/range.css +55 -55
- package/components/rating/rating.css +28 -28
- package/components/richtext/richtext.css +133 -133
- package/components/row/row.css +19 -19
- package/components/search/search.css +5 -5
- package/components/segment/segment.css +24 -24
- package/components/segmented/segmented.css +25 -25
- package/components/select/select.css +84 -84
- package/components/skeleton/skeleton.css +14 -14
- package/components/slider/slider.css +46 -46
- package/components/spinner/class.js +69 -0
- package/components/spinner/spinner.a2ui.json +197 -0
- package/components/spinner/spinner.css +165 -0
- package/components/spinner/spinner.d.ts +26 -0
- package/components/spinner/spinner.examples.md +26 -0
- package/components/spinner/spinner.js +17 -0
- package/components/spinner/spinner.test.js +234 -0
- package/components/spinner/spinner.yaml +230 -0
- package/components/stack/stack.css +11 -11
- package/components/stat/stat.css +25 -25
- package/components/step-progress/step-progress.css +20 -20
- package/components/stepper/stepper.css +29 -29
- package/components/stream/stream.css +12 -12
- package/components/swatch/swatch.css +68 -68
- package/components/swiper/swiper.css +57 -57
- package/components/switch/switch.css +52 -52
- package/components/table/table.css +162 -162
- package/components/table-toolbar/table-toolbar.css +32 -32
- package/components/tabs/tabs.css +51 -51
- package/components/tag/tag.css +48 -48
- package/components/text/text.css +44 -44
- package/components/textarea/textarea.css +46 -46
- package/components/time-picker/class.js +693 -0
- package/components/time-picker/time-picker.a2ui.json +267 -0
- package/components/time-picker/time-picker.css +122 -0
- package/components/time-picker/time-picker.d.ts +75 -0
- package/components/time-picker/time-picker.examples.md +35 -0
- package/components/time-picker/time-picker.js +17 -0
- package/components/time-picker/time-picker.test.js +287 -0
- package/components/time-picker/time-picker.yaml +256 -0
- package/components/timeline/timeline.css +50 -50
- package/components/toast/toast.css +58 -58
- package/components/toggle-group/toggle-group.css +6 -6
- package/components/toggle-scheme/toggle-scheme.css +2 -2
- package/components/toolbar/toolbar.css +17 -17
- package/components/tooltip/tooltip.css +2 -2
- package/components/tree/tree.css +37 -37
- package/components/upload/upload.css +49 -49
- package/dist/web-components.min.css +1 -1
- package/dist/web-components.min.js +121 -83
- package/package.json +1 -1
- package/styles/components.css +8 -0
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
Variables
|
|
8
8
|
W = track width (100% of the track element)
|
|
9
9
|
t = thumb width (full outer width including internal padding)
|
|
10
|
-
h = track height (var(--slider-track-height))
|
|
11
|
-
pad = internal padding on the thumb (var(--slider-thumb-padding))
|
|
10
|
+
h = track height (var(--slider-track-height, var(--slider-track-height-default)))
|
|
11
|
+
pad = internal padding on the thumb (var(--slider-thumb-padding, var(--slider-thumb-padding-default)))
|
|
12
12
|
p = progress (0.0 → 1.0, written by JS as --slider-pct)
|
|
13
13
|
|
|
14
14
|
The thumb consists of two layers:
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
In CSS (percentages relative to W):
|
|
45
45
|
|
|
46
46
|
left = calc(t/2 + p · (W − t))
|
|
47
|
-
= calc(var(--slider-thumb-width) / 2
|
|
48
|
-
+ var(--slider-pct) * (100% - var(--slider-thumb-width)))
|
|
47
|
+
= calc(var(--slider-thumb-width, var(--slider-thumb-width-default)) / 2
|
|
48
|
+
+ var(--slider-pct, var(--slider-pct-default)) * (100% - var(--slider-thumb-width, var(--slider-thumb-width-default))))
|
|
49
49
|
|
|
50
50
|
The element is shifted back by 50% via transform so its
|
|
51
51
|
geometric center lands on the computed point.
|
|
@@ -60,50 +60,50 @@
|
|
|
60
60
|
═══════════════════════════════════════════════════════════════ */
|
|
61
61
|
|
|
62
62
|
/* ── Layout ── */
|
|
63
|
-
--slider-gap: var(--a-space-1);
|
|
64
|
-
--slider-readout-gap: var(--a-space-0-5);
|
|
65
|
-
--slider-radius: var(--a-radius-full);
|
|
63
|
+
--slider-gap-default: var(--a-space-1);
|
|
64
|
+
--slider-readout-gap-default: var(--a-space-0-5);
|
|
65
|
+
--slider-radius-default: var(--a-radius-full);
|
|
66
66
|
|
|
67
67
|
/* Track height scales with the universal [size] attribute
|
|
68
68
|
via --a-toggle-size: 16 sm / 20 md / 24 lg at density=1.
|
|
69
69
|
Override --slider-track-height directly for custom sizes. */
|
|
70
|
-
--slider-track-height: var(--a-toggle-size);
|
|
70
|
+
--slider-track-height-default: var(--a-toggle-size);
|
|
71
71
|
|
|
72
72
|
/* Thumb padding: internal breathing room on all sides.
|
|
73
73
|
The visual pill sits pad px away from the container edge. */
|
|
74
|
-
--slider-thumb-padding: 2px;
|
|
74
|
+
--slider-thumb-padding-default: 2px;
|
|
75
75
|
|
|
76
76
|
/* Visual thumb height: track height minus top and bottom padding. */
|
|
77
|
-
--slider-thumb-visual-h: calc(var(--slider-track-height) - 2 * var(--slider-thumb-padding));
|
|
77
|
+
--slider-thumb-visual-h-default: calc(var(--slider-track-height, var(--slider-track-height-default)) - 2 * var(--slider-thumb-padding, var(--slider-thumb-padding-default)));
|
|
78
78
|
|
|
79
79
|
/* Visual thumb width: 2×1 pill ratio. */
|
|
80
|
-
--slider-thumb-visual-w: calc(var(--slider-thumb-visual-h) * 2);
|
|
80
|
+
--slider-thumb-visual-w-default: calc(var(--slider-thumb-visual-h, var(--slider-thumb-visual-h-default)) * 2);
|
|
81
81
|
|
|
82
82
|
/* Container / geometry thumb width: visual width plus left and
|
|
83
83
|
right padding. This is the width used in the travel equation. */
|
|
84
|
-
--slider-thumb-width: calc(var(--slider-thumb-visual-w) + 2 * var(--slider-thumb-padding));
|
|
84
|
+
--slider-thumb-width-default: calc(var(--slider-thumb-visual-w, var(--slider-thumb-visual-w-default)) + 2 * var(--slider-thumb-padding, var(--slider-thumb-padding-default)));
|
|
85
85
|
|
|
86
86
|
/* Progress fraction (0.0 → 1.0) written by JS. */
|
|
87
|
-
--slider-pct: 0;
|
|
87
|
+
--slider-pct-default: 0;
|
|
88
88
|
|
|
89
89
|
/* ── Colors ──
|
|
90
90
|
Track: dim recessed surface | Fill: primary | Thumb: white chrome */
|
|
91
|
-
--slider-track-bg: var(--a-bg-muted);
|
|
92
|
-
--slider-fill-bg: var(--a-primary-bg);
|
|
93
|
-
--slider-thumb-bg: var(--a-chrome-light);
|
|
94
|
-
--slider-fill-bg-disabled: var(--a-border-subtle);
|
|
95
|
-
--slider-thumb-bg-disabled: var(--a-fg-muted);
|
|
91
|
+
--slider-track-bg-default: var(--a-bg-muted);
|
|
92
|
+
--slider-fill-bg-default: var(--a-primary-bg);
|
|
93
|
+
--slider-thumb-bg-default: var(--a-chrome-light);
|
|
94
|
+
--slider-fill-bg-disabled-default: var(--a-border-subtle);
|
|
95
|
+
--slider-thumb-bg-disabled-default: var(--a-fg-muted);
|
|
96
96
|
|
|
97
97
|
/* ── Typography ── */
|
|
98
|
-
--slider-font-size: var(--a-ui-size);
|
|
99
|
-
--slider-value-weight: var(--a-weight-bold);
|
|
98
|
+
--slider-font-size-default: var(--a-ui-size);
|
|
99
|
+
--slider-value-weight-default: var(--a-weight-bold);
|
|
100
100
|
|
|
101
101
|
/* ── Transition ── */
|
|
102
|
-
--slider-duration: var(--a-duration-fast);
|
|
103
|
-
--slider-easing: var(--a-easing);
|
|
102
|
+
--slider-duration-default: var(--a-duration-fast);
|
|
103
|
+
--slider-easing-default: var(--a-easing);
|
|
104
104
|
|
|
105
105
|
/* ── Focus ── */
|
|
106
|
-
--slider-focus-ring: var(--a-focus-ring);
|
|
106
|
+
--slider-focus-ring-default: var(--a-focus-ring);
|
|
107
107
|
text-align: start; /* §text-align-reset — blocks inheritance from centered ancestors */
|
|
108
108
|
}
|
|
109
109
|
|
|
@@ -112,7 +112,7 @@
|
|
|
112
112
|
box-sizing: border-box;
|
|
113
113
|
display: flex;
|
|
114
114
|
flex-direction: column;
|
|
115
|
-
gap: var(--slider-gap);
|
|
115
|
+
gap: var(--slider-gap, var(--slider-gap-default));
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
:scope[data-direction="row"] {
|
|
@@ -126,7 +126,7 @@
|
|
|
126
126
|
display: flex;
|
|
127
127
|
align-items: baseline;
|
|
128
128
|
justify-content: space-between;
|
|
129
|
-
font-size: var(--slider-font-size);
|
|
129
|
+
font-size: var(--slider-font-size, var(--slider-font-size-default));
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
[slot="label"] {
|
|
@@ -136,12 +136,12 @@
|
|
|
136
136
|
[slot="readout"] {
|
|
137
137
|
display: flex;
|
|
138
138
|
align-items: baseline;
|
|
139
|
-
gap: var(--slider-readout-gap);
|
|
139
|
+
gap: var(--slider-readout-gap, var(--slider-readout-gap-default));
|
|
140
140
|
}
|
|
141
141
|
|
|
142
142
|
[slot="value"] {
|
|
143
143
|
color: var(--a-fg);
|
|
144
|
-
font-weight: var(--slider-value-weight);
|
|
144
|
+
font-weight: var(--slider-value-weight, var(--slider-value-weight-default));
|
|
145
145
|
font-variant-numeric: tabular-nums;
|
|
146
146
|
}
|
|
147
147
|
|
|
@@ -152,9 +152,9 @@
|
|
|
152
152
|
/* Track: the reference frame W for all geometry calculations. */
|
|
153
153
|
[slot="track"] {
|
|
154
154
|
position: relative;
|
|
155
|
-
height: var(--slider-track-height);
|
|
156
|
-
border-radius: var(--slider-radius);
|
|
157
|
-
background: var(--slider-track-bg);
|
|
155
|
+
height: var(--slider-track-height, var(--slider-track-height-default));
|
|
156
|
+
border-radius: var(--slider-radius, var(--slider-radius-default));
|
|
157
|
+
background: var(--slider-track-bg, var(--slider-track-bg-default));
|
|
158
158
|
cursor: pointer;
|
|
159
159
|
touch-action: none;
|
|
160
160
|
}
|
|
@@ -166,10 +166,10 @@
|
|
|
166
166
|
left: 0;
|
|
167
167
|
height: 100%;
|
|
168
168
|
border-radius: inherit;
|
|
169
|
-
background: var(--slider-fill-bg);
|
|
169
|
+
background: var(--slider-fill-bg, var(--slider-fill-bg-default));
|
|
170
170
|
pointer-events: none;
|
|
171
|
-
width: calc(var(--slider-thumb-width)
|
|
172
|
-
+ var(--slider-pct) * (100% - var(--slider-thumb-width)));
|
|
171
|
+
width: calc(var(--slider-thumb-width, var(--slider-thumb-width-default))
|
|
172
|
+
+ var(--slider-pct, var(--slider-pct-default)) * (100% - var(--slider-thumb-width, var(--slider-thumb-width-default))));
|
|
173
173
|
}
|
|
174
174
|
|
|
175
175
|
/* Thumb CONTAINER: full track height, geometry width.
|
|
@@ -178,18 +178,18 @@
|
|
|
178
178
|
[slot="thumb"] {
|
|
179
179
|
position: absolute;
|
|
180
180
|
top: 50%;
|
|
181
|
-
left: calc(var(--slider-thumb-width) / 2
|
|
182
|
-
+ var(--slider-pct) * (100% - var(--slider-thumb-width)));
|
|
183
|
-
width: var(--slider-thumb-width);
|
|
184
|
-
height: var(--slider-track-height);
|
|
185
|
-
border-radius: var(--slider-radius);
|
|
181
|
+
left: calc(var(--slider-thumb-width, var(--slider-thumb-width-default)) / 2
|
|
182
|
+
+ var(--slider-pct, var(--slider-pct-default)) * (100% - var(--slider-thumb-width, var(--slider-thumb-width-default))));
|
|
183
|
+
width: var(--slider-thumb-width, var(--slider-thumb-width-default));
|
|
184
|
+
height: var(--slider-track-height, var(--slider-track-height-default));
|
|
185
|
+
border-radius: var(--slider-radius, var(--slider-radius-default));
|
|
186
186
|
background: transparent;
|
|
187
187
|
transform: translate(-50%, -50%);
|
|
188
188
|
cursor: grab;
|
|
189
189
|
touch-action: none;
|
|
190
190
|
transition:
|
|
191
|
-
left var(--slider-duration) var(--slider-easing),
|
|
192
|
-
transform var(--slider-duration) var(--slider-easing);
|
|
191
|
+
left var(--slider-duration, var(--slider-duration-default)) var(--slider-easing, var(--slider-easing-default)),
|
|
192
|
+
transform var(--slider-duration, var(--slider-duration-default)) var(--slider-easing, var(--slider-easing-default));
|
|
193
193
|
z-index: 1;
|
|
194
194
|
}
|
|
195
195
|
|
|
@@ -200,15 +200,15 @@
|
|
|
200
200
|
[slot="thumb"]::before {
|
|
201
201
|
content: '';
|
|
202
202
|
position: absolute;
|
|
203
|
-
inset: var(--slider-thumb-padding);
|
|
203
|
+
inset: var(--slider-thumb-padding, var(--slider-thumb-padding-default));
|
|
204
204
|
border-radius: inherit;
|
|
205
|
-
background: var(--slider-thumb-bg);
|
|
205
|
+
background: var(--slider-thumb-bg, var(--slider-thumb-bg-default));
|
|
206
206
|
}
|
|
207
207
|
|
|
208
208
|
/* During drag: kill the left transition so the thumb follows
|
|
209
209
|
the pointer frame-by-frame without CSS interpolation lag. */
|
|
210
210
|
:scope[data-dragging] [slot="thumb"] {
|
|
211
|
-
transition: transform var(--slider-duration) var(--slider-easing);
|
|
211
|
+
transition: transform var(--slider-duration, var(--slider-duration-default)) var(--slider-easing, var(--slider-easing-default));
|
|
212
212
|
}
|
|
213
213
|
|
|
214
214
|
[slot="thumb"]:hover {
|
|
@@ -221,13 +221,13 @@
|
|
|
221
221
|
}
|
|
222
222
|
|
|
223
223
|
:scope:focus-visible { outline: none; }
|
|
224
|
-
:scope:focus-visible [slot="thumb"] { box-shadow: var(--slider-focus-ring); }
|
|
224
|
+
:scope:focus-visible [slot="thumb"] { box-shadow: var(--slider-focus-ring, var(--slider-focus-ring-default)); }
|
|
225
225
|
|
|
226
226
|
/* Disabled */
|
|
227
227
|
:scope[disabled] [slot="track"] { cursor: not-allowed; }
|
|
228
|
-
:scope[disabled] [slot="fill"] { background: var(--slider-fill-bg-disabled); }
|
|
228
|
+
:scope[disabled] [slot="fill"] { background: var(--slider-fill-bg-disabled, var(--slider-fill-bg-disabled-default)); }
|
|
229
229
|
:scope[disabled] [slot="thumb"]::before {
|
|
230
|
-
background: var(--slider-thumb-bg-disabled);
|
|
230
|
+
background: var(--slider-thumb-bg-disabled, var(--slider-thumb-bg-disabled-default));
|
|
231
231
|
}
|
|
232
232
|
|
|
233
233
|
/* ── Hint (§184, v0.5.5, FEEDBACK-08 §7) ── */
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Non-side-effect class export for `<spinner-ui>`.
|
|
3
|
+
*
|
|
4
|
+
* Importing this file gives you the class without auto-registering the
|
|
5
|
+
* tag. Useful for test isolation, subclassing with tag-name override, or
|
|
6
|
+
* selective composition.
|
|
7
|
+
*
|
|
8
|
+
* The auto-register path stays at `@adia-ai/web-components/components/spinner`
|
|
9
|
+
* (which imports this file + calls `defineIfFree()`).
|
|
10
|
+
*
|
|
11
|
+
* @see ../../USAGE.md#registration--auto-vs-explicit
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* <spinner-ui size="md" variant="arc" tone="current" label="Loading"></spinner-ui>
|
|
16
|
+
* <spinner-ui variant="dots" tone="subtle" label="Assistant is typing"></spinner-ui>
|
|
17
|
+
* <spinner-ui paused size="lg"></spinner-ui>
|
|
18
|
+
*
|
|
19
|
+
* Circular animated indicator for INDETERMINATE loading. The visual is
|
|
20
|
+
* rendered entirely via `::before` (and per-variant peers) on the host;
|
|
21
|
+
* the component stamps no internal DOM (`static template = () => null`).
|
|
22
|
+
*
|
|
23
|
+
* Props (attributes):
|
|
24
|
+
* size — sm | md (default) | lg
|
|
25
|
+
* variant — arc (default) | ring | dots
|
|
26
|
+
* tone — current (default) | subtle | accent | inverse
|
|
27
|
+
* paused — freeze the animation in place (default: false)
|
|
28
|
+
* label — accessible operation name (default: "Loading")
|
|
29
|
+
*
|
|
30
|
+
* ARIA: role="progressbar" + aria-busy="true" + aria-valuetext=<label>.
|
|
31
|
+
* Reduced motion: when prefers-reduced-motion: reduce is active, the
|
|
32
|
+
* spinning animation is replaced with a static "…" ellipsis via CSS
|
|
33
|
+
* only — no JS observer required. WCAG 2.3.3.
|
|
34
|
+
*
|
|
35
|
+
* Lifecycle: pure-CSS animation. No timers, no observers. connected()
|
|
36
|
+
* sets the three ARIA attributes once; render() keeps aria-valuetext in
|
|
37
|
+
* sync with the [label] prop.
|
|
38
|
+
*
|
|
39
|
+
* @see SPEC-001 (docs/specs/implementation-ready/SPEC-001-spinner-loader.md)
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
import { UIElement } from '../../core/element.js';
|
|
43
|
+
|
|
44
|
+
export class UISpinner extends UIElement {
|
|
45
|
+
static properties = {
|
|
46
|
+
size: { type: String, default: 'md', reflect: true },
|
|
47
|
+
variant: { type: String, default: 'arc', reflect: true },
|
|
48
|
+
tone: { type: String, default: 'current', reflect: true },
|
|
49
|
+
paused: { type: Boolean, default: false, reflect: true },
|
|
50
|
+
label: { type: String, default: 'Loading', reflect: false },
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// No stamped children. The visual is pure CSS (::before / per-variant
|
|
54
|
+
// peers) on the host. Same shape as the spec calls out for SPEC-003
|
|
55
|
+
// <visually-hidden-ui> — a CSS-styled wrapper with no internal stamp.
|
|
56
|
+
static template = () => null;
|
|
57
|
+
|
|
58
|
+
connected() {
|
|
59
|
+
// ARIA wiring — fixed for the lifetime of the element.
|
|
60
|
+
if (!this.hasAttribute('role')) this.setAttribute('role', 'progressbar');
|
|
61
|
+
if (!this.hasAttribute('aria-busy')) this.setAttribute('aria-busy', 'true');
|
|
62
|
+
this.setAttribute('aria-valuetext', this.label || 'Loading');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
render() {
|
|
66
|
+
// Keep aria-valuetext in sync if `label` changes after connect.
|
|
67
|
+
this.setAttribute('aria-valuetext', this.label || 'Loading');
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://adiaui.dev/a2ui/v0_9/components/Spinner.json",
|
|
4
|
+
"title": "Spinner",
|
|
5
|
+
"description": "Circular animated indicator for indeterminate loading. Renders a rotating arc, full ring, or three bouncing dots inside a sized box; the animation runs while the element is in the DOM and `[paused]` is unset. Fills the circular-spinner gap left by <skeleton-ui> (rectangular placeholder) and <progress-ui> (linear determinate bar) — use <spinner-ui> when the wait duration is unknown and the shape of the eventual content is irregular or the region is too small for a placeholder block.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"allOf": [
|
|
8
|
+
{
|
|
9
|
+
"$ref": "common_types.json#/$defs/ComponentCommon"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"$ref": "common_types.json#/$defs/CatalogComponentCommon"
|
|
13
|
+
}
|
|
14
|
+
],
|
|
15
|
+
"properties": {
|
|
16
|
+
"component": {
|
|
17
|
+
"const": "Spinner"
|
|
18
|
+
},
|
|
19
|
+
"label": {
|
|
20
|
+
"description": "Accessible operation name surfaced via `aria-valuetext`. Override for context-specific labels (\"Saving\", \"Uploading\").",
|
|
21
|
+
"type": "string",
|
|
22
|
+
"default": "Loading"
|
|
23
|
+
},
|
|
24
|
+
"paused": {
|
|
25
|
+
"description": "Pause the animation in-place. Useful for screenshot tests and explicit-control flows.",
|
|
26
|
+
"type": "boolean",
|
|
27
|
+
"default": false
|
|
28
|
+
},
|
|
29
|
+
"size": {
|
|
30
|
+
"description": "Diameter — matches icon-ui's ladder (sm 14px, md 16px, lg 20px).",
|
|
31
|
+
"type": "string",
|
|
32
|
+
"enum": [
|
|
33
|
+
"sm",
|
|
34
|
+
"md",
|
|
35
|
+
"lg"
|
|
36
|
+
],
|
|
37
|
+
"default": "md"
|
|
38
|
+
},
|
|
39
|
+
"tone": {
|
|
40
|
+
"description": "Color tone — `current` inherits parent text color (matches button label), `accent` uses brand accent, `subtle` is muted, `inverse` flips for on-accent surfaces.",
|
|
41
|
+
"type": "string",
|
|
42
|
+
"enum": [
|
|
43
|
+
"current",
|
|
44
|
+
"accent",
|
|
45
|
+
"subtle",
|
|
46
|
+
"inverse"
|
|
47
|
+
],
|
|
48
|
+
"default": "current"
|
|
49
|
+
},
|
|
50
|
+
"variant": {
|
|
51
|
+
"description": "Visual flavor — arc (rotating quarter-circle), ring (full ring with one colored segment), dots (three bouncing dots).",
|
|
52
|
+
"type": "string",
|
|
53
|
+
"enum": [
|
|
54
|
+
"arc",
|
|
55
|
+
"ring",
|
|
56
|
+
"dots"
|
|
57
|
+
],
|
|
58
|
+
"default": "arc"
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
"required": [
|
|
62
|
+
"component"
|
|
63
|
+
],
|
|
64
|
+
"unevaluatedProperties": false,
|
|
65
|
+
"x-adiaui": {
|
|
66
|
+
"anti_patterns": [
|
|
67
|
+
{
|
|
68
|
+
"fix": "{\"component\": \"Progress\", \"value\": 42, \"max\": 100}\n",
|
|
69
|
+
"why": "Spinner is INDETERMINATE only. `value` and any quantitative\nprogress field belongs on Progress, not Spinner. Also \"Spin\" is\nnot a valid operation label.\n",
|
|
70
|
+
"wrong": "{\"component\": \"Spinner\", \"label\": \"Spin\", \"value\": 0.42}\n"
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"fix": "{\"component\": \"Spinner\", \"size\": \"md\"}\n",
|
|
74
|
+
"why": "Skeleton is a placeholder; rotation is not part of its contract.\nA rotating circle is a Spinner.\n",
|
|
75
|
+
"wrong": "{\"component\": \"Skeleton\", \"variant\": \"circle\", \"animation\": \"rotate\"}\n"
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"fix": "{\"component\": \"Card\", \"children\": [\n {\"component\": \"Spinner\", \"size\": \"lg\"}\n]}\n",
|
|
79
|
+
"why": "Multiple sibling spinners in one region produce visual noise\nwithout extra information. Use one parent-level Spinner.\n",
|
|
80
|
+
"wrong": "{\"component\": \"Card\", \"children\": [\n {\"component\": \"Spinner\"},\n {\"component\": \"Spinner\"},\n {\"component\": \"Spinner\"}\n]}\n"
|
|
81
|
+
}
|
|
82
|
+
],
|
|
83
|
+
"category": "feedback",
|
|
84
|
+
"composes": [],
|
|
85
|
+
"events": {},
|
|
86
|
+
"examples": [
|
|
87
|
+
{
|
|
88
|
+
"description": "Loading button — primary action with a saving spinner. The button is disabled while the operation is in progress; the spinner matches the button label color via tone=\"current\".",
|
|
89
|
+
"a2ui": "[\n {\n \"id\": \"btn-save\",\n \"component\": \"Button\",\n \"text\": \"Saving\",\n \"variant\": \"primary\",\n \"disabled\": true,\n \"children\": [\"sp-1\"]\n },\n {\n \"id\": \"sp-1\",\n \"component\": \"Spinner\",\n \"size\": \"sm\",\n \"tone\": \"current\",\n \"label\": \"Saving\"\n }\n]",
|
|
90
|
+
"name": "button-saving"
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"description": "Standalone centered spinner inside a card while body content fetches.",
|
|
94
|
+
"a2ui": "[\n {\n \"id\": \"card\",\n \"component\": \"Card\",\n \"children\": [\"row\"]\n },\n {\n \"id\": \"row\",\n \"component\": \"Row\",\n \"justify\": \"center\",\n \"align\": \"center\",\n \"children\": [\"sp\"]\n },\n {\n \"id\": \"sp\",\n \"component\": \"Spinner\",\n \"size\": \"lg\",\n \"tone\": \"subtle\",\n \"label\": \"Loading dashboard\"\n }\n]",
|
|
95
|
+
"name": "centered-card-loading"
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"description": "Three bouncing dots — good for chat / typing indicators.",
|
|
99
|
+
"a2ui": "[\n {\n \"id\": \"sp\",\n \"component\": \"Spinner\",\n \"variant\": \"dots\",\n \"tone\": \"subtle\",\n \"label\": \"Assistant is typing\"\n }\n]",
|
|
100
|
+
"name": "typing-indicator"
|
|
101
|
+
}
|
|
102
|
+
],
|
|
103
|
+
"keywords": [
|
|
104
|
+
"spinner",
|
|
105
|
+
"loader",
|
|
106
|
+
"loading",
|
|
107
|
+
"indeterminate",
|
|
108
|
+
"progress",
|
|
109
|
+
"busy",
|
|
110
|
+
"feedback",
|
|
111
|
+
"circular"
|
|
112
|
+
],
|
|
113
|
+
"name": "UISpinner",
|
|
114
|
+
"related": [
|
|
115
|
+
"Progress",
|
|
116
|
+
"Skeleton",
|
|
117
|
+
"Button",
|
|
118
|
+
"EmptyState"
|
|
119
|
+
],
|
|
120
|
+
"slots": {},
|
|
121
|
+
"states": [
|
|
122
|
+
{
|
|
123
|
+
"description": "Default; animation active.",
|
|
124
|
+
"name": "running"
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
"description": "Animation frozen at the current frame.",
|
|
128
|
+
"attribute": "paused",
|
|
129
|
+
"name": "paused"
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
"description": "Triggered by prefers-reduced-motion. Animation replaced with a static ellipsis. Detected via CSS, not JS.",
|
|
133
|
+
"name": "reduced"
|
|
134
|
+
}
|
|
135
|
+
],
|
|
136
|
+
"status": "stable",
|
|
137
|
+
"synonyms": {
|
|
138
|
+
"busy": [
|
|
139
|
+
"spinner",
|
|
140
|
+
"loading"
|
|
141
|
+
],
|
|
142
|
+
"indeterminate": [
|
|
143
|
+
"spinner",
|
|
144
|
+
"progress"
|
|
145
|
+
],
|
|
146
|
+
"loader": [
|
|
147
|
+
"spinner",
|
|
148
|
+
"loading",
|
|
149
|
+
"progress"
|
|
150
|
+
],
|
|
151
|
+
"loading": [
|
|
152
|
+
"spinner",
|
|
153
|
+
"loading",
|
|
154
|
+
"progress",
|
|
155
|
+
"skeleton"
|
|
156
|
+
],
|
|
157
|
+
"saving": [
|
|
158
|
+
"spinner",
|
|
159
|
+
"loading"
|
|
160
|
+
],
|
|
161
|
+
"spinner": [
|
|
162
|
+
"spinner",
|
|
163
|
+
"loader",
|
|
164
|
+
"progress"
|
|
165
|
+
],
|
|
166
|
+
"uploading": [
|
|
167
|
+
"spinner",
|
|
168
|
+
"loading"
|
|
169
|
+
]
|
|
170
|
+
},
|
|
171
|
+
"tag": "spinner-ui",
|
|
172
|
+
"tokens": {
|
|
173
|
+
"--spinner-color": {
|
|
174
|
+
"description": "Color of the active arc / ring / dots. Defaults to currentColor so a tone-driven cascade resolves naturally.",
|
|
175
|
+
"default": "currentColor"
|
|
176
|
+
},
|
|
177
|
+
"--spinner-duration": {
|
|
178
|
+
"description": "One full rotation duration.",
|
|
179
|
+
"default": "var(--a-duration-slow)"
|
|
180
|
+
},
|
|
181
|
+
"--spinner-size": {
|
|
182
|
+
"description": "Diameter of the spinner box.",
|
|
183
|
+
"default": "1rem"
|
|
184
|
+
},
|
|
185
|
+
"--spinner-stroke": {
|
|
186
|
+
"description": "Border thickness for the arc / ring variants.",
|
|
187
|
+
"default": "2px"
|
|
188
|
+
},
|
|
189
|
+
"--spinner-track-opacity": {
|
|
190
|
+
"description": "Opacity for the non-rotating ring track (variant=ring only).",
|
|
191
|
+
"default": "0.25"
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
"traits": [],
|
|
195
|
+
"version": 1
|
|
196
|
+
}
|
|
197
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
@scope (spinner-ui) {
|
|
2
|
+
/* ── Block 1 — TOKENS ─────────────────────────────────────────────
|
|
3
|
+
Spinner inherits the icon ladder for sizing (matches icon-ui's
|
|
4
|
+
`:scope[size="..."]` rems). The semantic colors come via `tone=…`
|
|
5
|
+
re-assigning --spinner-color. Stroke is 2px (raw px allowed for
|
|
6
|
+
borders 1-2px per component-token-contract §"Forbidden patterns"). */
|
|
7
|
+
:where(:scope) {
|
|
8
|
+
--spinner-size-default: 1rem; /* icon-ui md */
|
|
9
|
+
--spinner-color-default: currentColor;
|
|
10
|
+
--spinner-stroke-default: 2px;
|
|
11
|
+
--spinner-duration-default: var(--a-duration-slow);
|
|
12
|
+
--spinner-track-opacity-default: 0.25;
|
|
13
|
+
--spinner-dot-size-default: calc(var(--spinner-size, var(--spinner-size-default)) / 4);
|
|
14
|
+
--spinner-dot-gap-default: calc(var(--spinner-size, var(--spinner-size-default)) / 8);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/* ── Block 2 — BASE ────────────────────────────────────────────── */
|
|
18
|
+
:scope {
|
|
19
|
+
box-sizing: border-box;
|
|
20
|
+
display: inline-block;
|
|
21
|
+
width: var(--spinner-size, var(--spinner-size-default));
|
|
22
|
+
height: var(--spinner-size, var(--spinner-size-default));
|
|
23
|
+
color: var(--spinner-color, var(--spinner-color-default));
|
|
24
|
+
vertical-align: middle;
|
|
25
|
+
flex-shrink: 0;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/* ── Size ladder (mirrors icon-ui's local rem declarations) ─────── */
|
|
29
|
+
:scope[size="sm"] { --spinner-size-default: 0.875rem; } /* 14px */
|
|
30
|
+
:scope[size="md"] { --spinner-size-default: 1rem; } /* 16px */
|
|
31
|
+
:scope[size="lg"] { --spinner-size-default: 1.25rem; } /* 20px */
|
|
32
|
+
|
|
33
|
+
/* ── Tones (override --spinner-color token) ────────────────────── */
|
|
34
|
+
:scope[tone="current"] { --spinner-color-default: currentColor; }
|
|
35
|
+
:scope[tone="subtle"] { --spinner-color-default: var(--a-fg-subtle); }
|
|
36
|
+
:scope[tone="accent"] { --spinner-color-default: var(--a-accent-strong); }
|
|
37
|
+
:scope[tone="inverse"] { --spinner-color-default: var(--a-chrome-light); }
|
|
38
|
+
|
|
39
|
+
/* ── Variant: ARC (default) — rotating quarter-circle border ───── */
|
|
40
|
+
:scope[variant="arc"]::before,
|
|
41
|
+
:scope:not([variant])::before {
|
|
42
|
+
content: "";
|
|
43
|
+
box-sizing: border-box;
|
|
44
|
+
display: block;
|
|
45
|
+
width: 100%;
|
|
46
|
+
height: 100%;
|
|
47
|
+
border-radius: 50%;
|
|
48
|
+
border: var(--spinner-stroke, var(--spinner-stroke-default)) solid;
|
|
49
|
+
border-color: currentColor transparent transparent transparent;
|
|
50
|
+
animation: spinner-ui-rotate var(--spinner-duration, var(--spinner-duration-default)) linear infinite;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/* ── Variant: RING — full ring with one rotating colored segment ── */
|
|
54
|
+
:scope[variant="ring"]::before {
|
|
55
|
+
content: "";
|
|
56
|
+
box-sizing: border-box;
|
|
57
|
+
display: block;
|
|
58
|
+
width: 100%;
|
|
59
|
+
height: 100%;
|
|
60
|
+
border-radius: 50%;
|
|
61
|
+
border: var(--spinner-stroke, var(--spinner-stroke-default)) solid;
|
|
62
|
+
border-color: color-mix(in oklch, currentColor calc(var(--spinner-track-opacity, var(--spinner-track-opacity-default)) * 100%), transparent);
|
|
63
|
+
border-top-color: currentColor;
|
|
64
|
+
animation: spinner-ui-rotate var(--spinner-duration, var(--spinner-duration-default)) linear infinite;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/* ── Variant: DOTS — three bouncing dots ─────────────────────────
|
|
68
|
+
The dots layout uses inline-flex + three ::before/::after-style
|
|
69
|
+
children. Since pseudo-elements only give us two, we render dots
|
|
70
|
+
via three radial-gradient stops on a background animation that
|
|
71
|
+
translates each stop independently — but a simpler, more robust
|
|
72
|
+
shape is to overlay three identical dot pseudos via a flexbox
|
|
73
|
+
container of pseudo + two child pseudos isn't possible without
|
|
74
|
+
a stamped child. So we use a single ::before that paints all
|
|
75
|
+
three dots via background gradients + an animation on the
|
|
76
|
+
`--dot-bounce` custom property (animated via @property).
|
|
77
|
+
|
|
78
|
+
Pragmatic alternative: render the three dots using ::before +
|
|
79
|
+
::after pseudos as the outer two dots, and a tightly-controlled
|
|
80
|
+
box-shadow on ::before to paint the middle dot. Animate each via
|
|
81
|
+
keyframes that stagger the bounce timing. */
|
|
82
|
+
:scope[variant="dots"] {
|
|
83
|
+
display: inline-flex;
|
|
84
|
+
align-items: center;
|
|
85
|
+
justify-content: center;
|
|
86
|
+
width: auto; /* dots row is wider than tall */
|
|
87
|
+
min-width: var(--spinner-size, var(--spinner-size-default));
|
|
88
|
+
gap: var(--spinner-dot-gap, var(--spinner-dot-gap-default));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
:scope[variant="dots"]::before,
|
|
92
|
+
:scope[variant="dots"]::after {
|
|
93
|
+
content: "";
|
|
94
|
+
display: block;
|
|
95
|
+
width: var(--spinner-dot-size, var(--spinner-dot-size-default));
|
|
96
|
+
height: var(--spinner-dot-size, var(--spinner-dot-size-default));
|
|
97
|
+
border-radius: 50%;
|
|
98
|
+
background: currentColor;
|
|
99
|
+
animation: spinner-ui-bounce var(--spinner-duration, var(--spinner-duration-default)) ease-in-out infinite;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/* Stagger the trailing dot so the row reads as a wave. The middle
|
|
103
|
+
dot is painted via box-shadow on ::before — same dot diameter, a
|
|
104
|
+
dot-gap to the right, painted with the same currentColor. The
|
|
105
|
+
middle dot's animation phase is the average of the two pseudos. */
|
|
106
|
+
:scope[variant="dots"]::before {
|
|
107
|
+
box-shadow:
|
|
108
|
+
calc(var(--spinner-dot-size, var(--spinner-dot-size-default)) + var(--spinner-dot-gap, var(--spinner-dot-gap-default))) 0 0 currentColor;
|
|
109
|
+
margin-right: calc(var(--spinner-dot-size, var(--spinner-dot-size-default)) + var(--spinner-dot-gap, var(--spinner-dot-gap-default)));
|
|
110
|
+
animation-delay: 0s;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
:scope[variant="dots"]::after {
|
|
114
|
+
animation-delay: calc(var(--spinner-duration, var(--spinner-duration-default)) / 3);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/* ── Paused state — freeze in place ────────────────────────────── */
|
|
118
|
+
:scope[paused]::before,
|
|
119
|
+
:scope[paused]::after {
|
|
120
|
+
animation-play-state: paused;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/* ── Reduced-motion replacement: static "…" ellipsis ────────────
|
|
124
|
+
WCAG 2.3.3 — animations of any duration are a vestibular risk.
|
|
125
|
+
The static replacement still announces aria-busy="true" via the
|
|
126
|
+
JS lifecycle. */
|
|
127
|
+
@media (prefers-reduced-motion: reduce) {
|
|
128
|
+
:scope::before,
|
|
129
|
+
:scope::after {
|
|
130
|
+
animation: none !important;
|
|
131
|
+
}
|
|
132
|
+
:scope::before {
|
|
133
|
+
content: "…";
|
|
134
|
+
border: none;
|
|
135
|
+
background: transparent;
|
|
136
|
+
box-shadow: none;
|
|
137
|
+
margin: 0;
|
|
138
|
+
width: 100%;
|
|
139
|
+
height: 100%;
|
|
140
|
+
display: flex;
|
|
141
|
+
align-items: center;
|
|
142
|
+
justify-content: center;
|
|
143
|
+
font: inherit;
|
|
144
|
+
line-height: 1;
|
|
145
|
+
text-align: center;
|
|
146
|
+
color: currentColor;
|
|
147
|
+
}
|
|
148
|
+
:scope[variant="dots"]::after {
|
|
149
|
+
display: none;
|
|
150
|
+
}
|
|
151
|
+
:scope[variant="dots"] {
|
|
152
|
+
width: var(--spinner-size, var(--spinner-size-default)); /* shrink back to a square in reduced mode */
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/* ── Keyframes ─────────────────────────────────────────────────── */
|
|
157
|
+
@keyframes spinner-ui-rotate {
|
|
158
|
+
to { transform: rotate(360deg); }
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
@keyframes spinner-ui-bounce {
|
|
162
|
+
0%, 80%, 100% { transform: scale(0.6); opacity: 0.6; }
|
|
163
|
+
40% { transform: scale(1); opacity: 1; }
|
|
164
|
+
}
|
|
165
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<spinner-ui>` — Circular animated indicator for indeterminate loading. Renders a rotating arc, full ring, or three bouncing dots inside a sized box; the animation runs while the element is in the DOM and `[paused]` is unset. Fills the circular-spinner gap left by <skeleton-ui> (rectangular placeholder) and <progress-ui> (linear determinate bar) — use <spinner-ui> when the wait duration is unknown and the shape of the eventual content is irregular or the region is too small for a placeholder block.
|
|
3
|
+
*
|
|
4
|
+
* @see https://ui-kit.exe.xyz/site/components/spinner
|
|
5
|
+
*
|
|
6
|
+
* Type declarations generated by scripts/build/dts-codegen.mjs from
|
|
7
|
+
* the component's `.a2ui.json` sidecar(s). Edit the source `.yaml`,
|
|
8
|
+
* run `npm run build:components`, then `npm run codegen:dts` to
|
|
9
|
+
* regenerate; or hand-author this file fully if rich event types are
|
|
10
|
+
* needed beyond what the yaml `events:` block can express.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { UIElement } from '../../core/element.js';
|
|
14
|
+
|
|
15
|
+
export class UISpinner extends UIElement {
|
|
16
|
+
/** Accessible operation name surfaced via `aria-valuetext`. Override for context-specific labels ("Saving", "Uploading"). */
|
|
17
|
+
label: string;
|
|
18
|
+
/** Pause the animation in-place. Useful for screenshot tests and explicit-control flows. */
|
|
19
|
+
paused: boolean;
|
|
20
|
+
/** Diameter — matches icon-ui's ladder (sm 14px, md 16px, lg 20px). */
|
|
21
|
+
size: 'sm' | 'md' | 'lg';
|
|
22
|
+
/** Color tone — `current` inherits parent text color (matches button label), `accent` uses brand accent, `subtle` is muted, `inverse` flips for on-accent surfaces. */
|
|
23
|
+
tone: 'current' | 'accent' | 'subtle' | 'inverse';
|
|
24
|
+
/** Visual flavor — arc (rotating quarter-circle), ring (full ring with one colored segment), dots (three bouncing dots). */
|
|
25
|
+
variant: 'arc' | 'ring' | 'dots';
|
|
26
|
+
}
|