stimeo-ui 0.15.0 → 0.17.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +209 -0
- data/README.md +128 -1
- data/dist/cable/index.js +392 -417
- data/dist/controllers/accordion_controller.js +228 -28
- data/dist/controllers/alert_dialog_controller.js +862 -221
- data/dist/controllers/announcer_controller.js +135 -191
- data/dist/controllers/aspect_ratio_controller.js +0 -10
- data/dist/controllers/auto_submit_controller.js +295 -110
- data/dist/controllers/avatar_controller.js +172 -106
- data/dist/controllers/breadcrumb_controller.js +0 -129
- data/dist/controllers/bulk_select_controller.js +264 -53
- data/dist/controllers/calendar_controller.js +346 -180
- data/dist/controllers/carousel_controller.js +587 -300
- data/dist/controllers/character_counter_controller.js +271 -143
- data/dist/controllers/checkbox_controller.js +77 -75
- data/dist/controllers/clipboard_controller.js +187 -108
- data/dist/controllers/collapsible_controller.js +374 -108
- data/dist/controllers/color_picker_controller.js +183 -127
- data/dist/controllers/combobox_controller.js +426 -109
- data/dist/controllers/command_palette_controller.js +994 -350
- data/dist/controllers/conditional_fields_controller.js +212 -125
- data/dist/controllers/confirm_controller.js +939 -245
- data/dist/controllers/context_menu_controller.js +275 -127
- data/dist/controllers/count_up_controller.js +73 -21
- data/dist/controllers/countdown_controller.js +235 -97
- data/dist/controllers/currency_input_controller.js +241 -118
- data/dist/controllers/data_grid_controller.js +229 -135
- data/dist/controllers/date_range_picker_controller.js +359 -165
- data/dist/controllers/dialog_controller.js +887 -225
- data/dist/controllers/direct_upload_controller.js +80 -142
- data/dist/controllers/dirty_form_controller.js +26 -47
- data/dist/controllers/dismissible_controller.js +175 -20
- data/dist/controllers/drawer_controller.js +963 -336
- data/dist/controllers/dropdown_controller.js +342 -85
- data/dist/controllers/editable_controller.js +182 -58
- data/dist/controllers/empty_state_controller.js +67 -53
- data/dist/controllers/file_dropzone_controller.js +426 -229
- data/dist/controllers/filter_controller.js +78 -36
- data/dist/controllers/flash_controller.js +328 -211
- data/dist/controllers/focus_controller.js +654 -239
- data/dist/controllers/form_field_controller.js +153 -136
- data/dist/controllers/form_validation_controller.js +29 -96
- data/dist/controllers/frame_loading_controller.js +263 -198
- data/dist/controllers/highlight_controller.js +119 -70
- data/dist/controllers/hover_card_controller.js +335 -118
- data/dist/controllers/idle_controller.js +316 -65
- data/dist/controllers/input_mask_controller.js +116 -61
- data/dist/controllers/intersection_controller.js +147 -103
- data/dist/controllers/lazy_frame_controller.js +9 -56
- data/dist/controllers/listbox_controller.js +413 -153
- data/dist/controllers/local_time_controller.js +54 -64
- data/dist/controllers/masonry_controller.js +70 -95
- data/dist/controllers/menu_controller.js +254 -167
- data/dist/controllers/menubar_controller.js +206 -331
- data/dist/controllers/meter_controller.js +159 -71
- data/dist/controllers/multi_select_controller.js +797 -388
- data/dist/controllers/navigation_menu_controller.js +156 -231
- data/dist/controllers/nested_form_controller.js +373 -174
- data/dist/controllers/network_status_controller.js +241 -45
- data/dist/controllers/number_input_controller.js +387 -227
- data/dist/controllers/optimistic_controller.js +28 -75
- data/dist/controllers/otp_controller.js +344 -196
- data/dist/controllers/overflow_indicator_controller.js +167 -93
- data/dist/controllers/overflow_menu_controller.js +408 -277
- data/dist/controllers/pagination_controller.js +342 -120
- data/dist/controllers/password_reveal_controller.js +424 -99
- data/dist/controllers/password_strength_controller.js +99 -153
- data/dist/controllers/persist_controller.js +252 -96
- data/dist/controllers/pointer_drag_controller.js +263 -164
- data/dist/controllers/popover_controller.js +267 -94
- data/dist/controllers/portal_controller.js +209 -72
- data/dist/controllers/preview_guard_controller.js +243 -112
- data/dist/controllers/progress_controller.js +132 -59
- data/dist/controllers/radio_group_controller.js +138 -133
- data/dist/controllers/range_slider_controller.js +245 -112
- data/dist/controllers/rating_controller.js +429 -194
- data/dist/controllers/read_more_controller.js +185 -47
- data/dist/controllers/reading_progress_controller.js +147 -135
- data/dist/controllers/relative_time_controller.js +145 -110
- data/dist/controllers/{reset_before_cache_controller.js → reset_on_restore_controller.js} +97 -38
- data/dist/controllers/resizable_controller.js +294 -118
- data/dist/controllers/roving_controller.js +39 -84
- data/dist/controllers/scroll_area_controller.js +428 -221
- data/dist/controllers/scroll_restore_controller.js +0 -53
- data/dist/controllers/scroll_visibility_controller.js +395 -146
- data/dist/controllers/scrollspy_controller.js +181 -236
- data/dist/controllers/separator_controller.js +288 -132
- data/dist/controllers/sidebar_controller.js +1071 -375
- data/dist/controllers/skeleton_controller.js +248 -92
- data/dist/controllers/slider_controller.js +211 -96
- data/dist/controllers/smart_sticky_header_controller.js +163 -54
- data/dist/controllers/sortable_controller.js +13 -110
- data/dist/controllers/spinner_controller.js +277 -161
- data/dist/controllers/step_indicator_controller.js +107 -53
- data/dist/controllers/stepper_controller.js +171 -48
- data/dist/controllers/stick_to_bottom_controller.js +310 -116
- data/dist/controllers/sticky_observer_controller.js +214 -49
- data/dist/controllers/submit_once_controller.js +294 -192
- data/dist/controllers/switch_controller.js +107 -43
- data/dist/controllers/tabs_controller.js +50 -25
- data/dist/controllers/tags_input_controller.js +290 -149
- data/dist/controllers/textarea_autosize_controller.js +71 -58
- data/dist/controllers/theme_controller.js +148 -80
- data/dist/controllers/time_picker_controller.js +162 -81
- data/dist/controllers/toast_controller.js +365 -193
- data/dist/controllers/toggle_group_controller.js +194 -99
- data/dist/controllers/toolbar_controller.js +92 -90
- data/dist/controllers/tooltip_controller.js +185 -125
- data/dist/controllers/transition_controller.js +123 -115
- data/dist/controllers/tree_view_controller.js +248 -290
- data/dist/index.js +8649 -8572
- data/dist/positioning/index.js +148 -45
- data/lib/stimeo/ui/version.rb +1 -1
- metadata +3 -3
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import { Controller } from '@hotwired/stimulus';
|
|
2
2
|
|
|
3
|
-
// src/controllers/progress_controller.ts
|
|
4
|
-
|
|
5
|
-
// src/utils/announce.ts
|
|
6
3
|
function announce(message, options = {}) {
|
|
7
4
|
const text = message.trim();
|
|
8
5
|
if (text.length === 0) return;
|
|
@@ -19,34 +16,59 @@ function fillTemplate(template, values) {
|
|
|
19
16
|
});
|
|
20
17
|
}
|
|
21
18
|
|
|
22
|
-
|
|
19
|
+
var NUMBER_BOUNDS = {
|
|
20
|
+
finite: { finite: true }};
|
|
21
|
+
function matchesNumberBounds(value, bounds) {
|
|
22
|
+
if (!Number.isFinite(value)) {
|
|
23
|
+
const direction = value === Infinity ? "positive" : value === -Infinity ? "negative" : null;
|
|
24
|
+
if (direction === null) return false;
|
|
25
|
+
if (bounds.allowInfinity !== "both" && bounds.allowInfinity !== direction) return false;
|
|
26
|
+
}
|
|
27
|
+
if (bounds.min !== void 0 && value < bounds.min) return false;
|
|
28
|
+
if (bounds.max !== void 0 && value > bounds.max) return false;
|
|
29
|
+
if (bounds.exclusiveMin !== void 0 && value <= bounds.exclusiveMin) return false;
|
|
30
|
+
if (bounds.integer && !Number.isInteger(value)) return false;
|
|
31
|
+
if (bounds.allowedValues !== void 0 && !bounds.allowedValues.includes(value)) return false;
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
|
|
23
35
|
function toFiniteNumber(raw) {
|
|
24
|
-
if (
|
|
25
|
-
|
|
36
|
+
if (typeof raw !== "number" && typeof raw !== "string") return null;
|
|
37
|
+
if (typeof raw === "string" && raw.trim().length === 0) return null;
|
|
38
|
+
const value = Number(raw);
|
|
26
39
|
return Number.isFinite(value) ? value : null;
|
|
27
40
|
}
|
|
41
|
+
function readNumber(raw, fallback, bounds) {
|
|
42
|
+
return matchesNumberBounds(raw, bounds) ? raw : fallback;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
var ListenerSet = class {
|
|
46
|
+
#abort = new AbortController();
|
|
47
|
+
add(target, type, handler, options) {
|
|
48
|
+
target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });
|
|
49
|
+
}
|
|
50
|
+
dispose() {
|
|
51
|
+
this.#abort.abort();
|
|
52
|
+
this.#abort = new AbortController();
|
|
53
|
+
}
|
|
54
|
+
};
|
|
28
55
|
|
|
29
|
-
// src/utils/microtask_coalescer.ts
|
|
30
56
|
var MicrotaskCoalescer = class {
|
|
31
57
|
#run;
|
|
32
58
|
#queued = false;
|
|
33
59
|
#active = false;
|
|
34
60
|
#generation = 0;
|
|
35
|
-
/** @param run - the single reconciliation pass, invoked at most once per batch. */
|
|
36
61
|
constructor(run) {
|
|
37
62
|
this.#run = run;
|
|
38
63
|
}
|
|
39
|
-
/** Opens the window in which {@link schedule} is honoured; call from `connect()`. */
|
|
40
64
|
activate() {
|
|
41
65
|
this.#active = true;
|
|
42
66
|
}
|
|
43
|
-
/** Closes the window and drops any pending pass; call from `disconnect()`. */
|
|
44
67
|
cancel() {
|
|
45
68
|
this.#active = false;
|
|
46
69
|
this.#queued = false;
|
|
47
70
|
this.#generation += 1;
|
|
48
71
|
}
|
|
49
|
-
/** Requests one pass after the batch settles. Idempotent; inert outside the window. */
|
|
50
72
|
schedule() {
|
|
51
73
|
if (!this.#active || this.#queued) return;
|
|
52
74
|
this.#queued = true;
|
|
@@ -59,7 +81,55 @@ var MicrotaskCoalescer = class {
|
|
|
59
81
|
}
|
|
60
82
|
};
|
|
61
83
|
|
|
62
|
-
|
|
84
|
+
var MorphRenderWatcher = class {
|
|
85
|
+
#listeners = new ListenerSet();
|
|
86
|
+
#pass;
|
|
87
|
+
#element = null;
|
|
88
|
+
#onMorph = () => this.#pass.schedule();
|
|
89
|
+
constructor(run) {
|
|
90
|
+
this.#pass = new MicrotaskCoalescer(run);
|
|
91
|
+
}
|
|
92
|
+
observe(element) {
|
|
93
|
+
if (this.#element === element) return;
|
|
94
|
+
this.disconnect();
|
|
95
|
+
this.#element = element;
|
|
96
|
+
this.#pass.activate();
|
|
97
|
+
this.#listeners.add(element, "turbo:morph-element", this.#onMorph);
|
|
98
|
+
}
|
|
99
|
+
schedule() {
|
|
100
|
+
this.#pass.schedule();
|
|
101
|
+
}
|
|
102
|
+
disconnect() {
|
|
103
|
+
this.#listeners.dispose();
|
|
104
|
+
this.#pass.cancel();
|
|
105
|
+
this.#element = null;
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
var NumberValueReader = class {
|
|
110
|
+
#lastRejected = /* @__PURE__ */ new Map();
|
|
111
|
+
read(owner, name, raw, fallback, bounds) {
|
|
112
|
+
const resolved = readNumber(raw, fallback, bounds);
|
|
113
|
+
if (matchesNumberBounds(raw, bounds)) {
|
|
114
|
+
this.#lastRejected.delete(name);
|
|
115
|
+
return resolved;
|
|
116
|
+
}
|
|
117
|
+
const attribute = `data-${owner.identifier}-${name.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}-value`;
|
|
118
|
+
const literal = owner.element.getAttribute(attribute);
|
|
119
|
+
if (literal === null) {
|
|
120
|
+
this.#lastRejected.delete(name);
|
|
121
|
+
return resolved;
|
|
122
|
+
}
|
|
123
|
+
if (this.#lastRejected.get(name) !== literal) {
|
|
124
|
+
this.#lastRejected.set(name, literal);
|
|
125
|
+
console.warn(
|
|
126
|
+
`Stimeo UI: "${owner.identifier}" has an invalid number Value "${name}" declaration ${JSON.stringify(literal)}; using ${fallback}.`
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
return resolved;
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
|
|
63
133
|
function rangeFraction(value, min, max) {
|
|
64
134
|
const span = max - min;
|
|
65
135
|
if (!(span > 0)) return 0;
|
|
@@ -75,9 +145,12 @@ function rangeFraction(value, min, max) {
|
|
|
75
145
|
return Math.min(1, Math.max(0, fraction));
|
|
76
146
|
}
|
|
77
147
|
|
|
78
|
-
|
|
79
|
-
var
|
|
80
|
-
|
|
148
|
+
var OWNED_VALUE_TEXT = "owns-valuetext";
|
|
149
|
+
var ProgressController = class _ProgressController extends Controller {
|
|
150
|
+
#numbers = new NumberValueReader();
|
|
151
|
+
get #ownedValueText() {
|
|
152
|
+
return `data-${this.identifier}-${OWNED_VALUE_TEXT}`;
|
|
153
|
+
}
|
|
81
154
|
static targets = ["bar"];
|
|
82
155
|
static values = {
|
|
83
156
|
value: { type: Number, default: 0 },
|
|
@@ -87,29 +160,23 @@ var ProgressController = class extends Controller {
|
|
|
87
160
|
valueText: { type: String, default: "" },
|
|
88
161
|
announceText: { type: String, default: "" }
|
|
89
162
|
};
|
|
163
|
+
static valueConstraints = {
|
|
164
|
+
value: NUMBER_BOUNDS.finite,
|
|
165
|
+
min: NUMBER_BOUNDS.finite,
|
|
166
|
+
max: NUMBER_BOUNDS.finite
|
|
167
|
+
};
|
|
90
168
|
static actions = ["setValue"];
|
|
91
169
|
static events = ["change", "complete"];
|
|
92
|
-
|
|
93
|
-
* Collapses a morph that swaps several render inputs at once into one repaint.
|
|
94
|
-
* A single update usually rewrites the whole set, and each Value would otherwise
|
|
95
|
-
* repaint on its own.
|
|
96
|
-
*/
|
|
97
|
-
#repaint = new MicrotaskCoalescer(() => {
|
|
170
|
+
#repaint = new MorphRenderWatcher(() => {
|
|
98
171
|
this.#render();
|
|
99
172
|
});
|
|
100
173
|
connect() {
|
|
101
|
-
this.#repaint.
|
|
174
|
+
this.#repaint.observe(this.element);
|
|
102
175
|
this.#render();
|
|
103
176
|
}
|
|
104
|
-
/** Closes the window in which a queued repaint may still run. */
|
|
105
177
|
disconnect() {
|
|
106
|
-
this.#repaint.
|
|
178
|
+
this.#repaint.disconnect();
|
|
107
179
|
}
|
|
108
|
-
/**
|
|
109
|
-
* Updates the progress value from an action param (`amount`) or a
|
|
110
|
-
* `detail.value` CustomEvent, normalizes it into range, syncs ARIA, and
|
|
111
|
-
* dispatches `change` (always) plus `complete` when `max` is reached.
|
|
112
|
-
*/
|
|
113
180
|
setValue(event) {
|
|
114
181
|
const next = toFiniteNumber(event.params?.amount ?? event.detail?.value);
|
|
115
182
|
if (next === null) return;
|
|
@@ -118,49 +185,37 @@ var ProgressController = class extends Controller {
|
|
|
118
185
|
this.indeterminateValue = false;
|
|
119
186
|
this.#render();
|
|
120
187
|
this.dispatch("change", { detail: { value, ratio: this.#ratio } });
|
|
121
|
-
if (value >= this
|
|
188
|
+
if (value >= this.#safeMax) {
|
|
122
189
|
this.dispatch("complete", { detail: { value } });
|
|
123
190
|
announce(
|
|
124
191
|
fillTemplate(this.announceTextValue, { value, percent: Math.round(this.#ratio * 100) })
|
|
125
192
|
);
|
|
126
193
|
}
|
|
127
194
|
}
|
|
128
|
-
/** Repaints when application code (or a Turbo morph) changes `indeterminate` at runtime. */
|
|
129
195
|
indeterminateValueChanged() {
|
|
130
196
|
this.#repaint.schedule();
|
|
131
197
|
}
|
|
132
|
-
/** Repaints when application code (or a Turbo morph) changes `value` at runtime. */
|
|
133
198
|
valueValueChanged() {
|
|
134
199
|
this.#repaint.schedule();
|
|
135
200
|
}
|
|
136
|
-
/** Repaints when application code (or a Turbo morph) changes `min` at runtime. */
|
|
137
201
|
minValueChanged() {
|
|
138
202
|
this.#repaint.schedule();
|
|
139
203
|
}
|
|
140
|
-
/** Repaints when application code (or a Turbo morph) changes `max` at runtime. */
|
|
141
204
|
maxValueChanged() {
|
|
142
205
|
this.#repaint.schedule();
|
|
143
206
|
}
|
|
144
|
-
/** Repaints when application code (or a Turbo morph) changes `valueText` at runtime. */
|
|
145
207
|
valueTextValueChanged() {
|
|
146
208
|
this.#repaint.schedule();
|
|
147
209
|
}
|
|
148
|
-
/** Clamps `raw` into the configured `[min, max]` range. */
|
|
149
210
|
#clamp(raw) {
|
|
150
|
-
return Math.min(this
|
|
211
|
+
return Math.min(this.#safeMax, Math.max(this.#safeMin, raw));
|
|
151
212
|
}
|
|
152
|
-
/** Current fraction of the range in `[0, 1]`; `0` when the range is empty. */
|
|
153
213
|
get #ratio() {
|
|
154
|
-
return rangeFraction(this
|
|
214
|
+
return rangeFraction(this.#safeValue, this.#safeMin, this.#safeMax);
|
|
155
215
|
}
|
|
156
|
-
/**
|
|
157
|
-
* Reflects value/range/indeterminate onto ARIA, `data-state`, and the ratio.
|
|
158
|
-
*
|
|
159
|
-
* @stimeoRenderRoot
|
|
160
|
-
*/
|
|
161
216
|
#render() {
|
|
162
|
-
this.element.setAttribute("aria-valuemin", String(this
|
|
163
|
-
this.element.setAttribute("aria-valuemax", String(this
|
|
217
|
+
this.element.setAttribute("aria-valuemin", String(this.#safeMin));
|
|
218
|
+
this.element.setAttribute("aria-valuemax", String(this.#safeMax));
|
|
164
219
|
if (this.indeterminateValue) {
|
|
165
220
|
this.element.removeAttribute("aria-valuenow");
|
|
166
221
|
this.#clearOwnValueText();
|
|
@@ -168,19 +223,12 @@ var ProgressController = class extends Controller {
|
|
|
168
223
|
this.element.setAttribute("data-state", "indeterminate");
|
|
169
224
|
return;
|
|
170
225
|
}
|
|
171
|
-
const value = this.#clamp(this
|
|
226
|
+
const value = this.#clamp(this.#safeValue);
|
|
172
227
|
this.element.setAttribute("aria-valuenow", String(value));
|
|
173
228
|
this.element.style.setProperty("--stimeo--progress-ratio", String(this.#ratio));
|
|
174
229
|
this.element.setAttribute("data-state", "determinate");
|
|
175
230
|
this.#applyValueText(value);
|
|
176
231
|
}
|
|
177
|
-
/**
|
|
178
|
-
* Sets `aria-valuetext` from the consumer-provided template, substituting
|
|
179
|
-
* `{value}` and `{percent}`. Left to the consumer so the human-readable text
|
|
180
|
-
* stays i18n-neutral in the library. With no template the attribute belongs to
|
|
181
|
-
* the consumer, so only a text this controller wrote is taken back
|
|
182
|
-
* ({@link OWNED_VALUE_TEXT}).
|
|
183
|
-
*/
|
|
184
232
|
#applyValueText(value) {
|
|
185
233
|
if (this.valueTextValue.length === 0) {
|
|
186
234
|
this.#clearOwnValueText();
|
|
@@ -189,16 +237,41 @@ var ProgressController = class extends Controller {
|
|
|
189
237
|
const percent = Math.round(this.#ratio * 100);
|
|
190
238
|
const text = this.valueTextValue.replaceAll("{value}", String(value)).replaceAll("{percent}", String(percent));
|
|
191
239
|
this.element.setAttribute("aria-valuetext", text);
|
|
192
|
-
this.element.setAttribute(
|
|
240
|
+
this.element.setAttribute(this.#ownedValueText, "");
|
|
193
241
|
}
|
|
194
|
-
/** Removes `aria-valuetext` only when this controller is the one that wrote it. */
|
|
195
242
|
#clearOwnValueText() {
|
|
196
|
-
if (!this.element.hasAttribute(
|
|
243
|
+
if (!this.element.hasAttribute(this.#ownedValueText)) return;
|
|
197
244
|
this.element.removeAttribute("aria-valuetext");
|
|
198
|
-
this.element.removeAttribute(
|
|
245
|
+
this.element.removeAttribute(this.#ownedValueText);
|
|
246
|
+
}
|
|
247
|
+
get #safeValue() {
|
|
248
|
+
return this.#numbers.read(
|
|
249
|
+
this,
|
|
250
|
+
"value",
|
|
251
|
+
this.valueValue,
|
|
252
|
+
_ProgressController.values.value.default,
|
|
253
|
+
_ProgressController.valueConstraints.value
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
get #safeMin() {
|
|
257
|
+
return this.#numbers.read(
|
|
258
|
+
this,
|
|
259
|
+
"min",
|
|
260
|
+
this.minValue,
|
|
261
|
+
_ProgressController.values.min.default,
|
|
262
|
+
_ProgressController.valueConstraints.min
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
get #safeMax() {
|
|
266
|
+
return this.#numbers.read(
|
|
267
|
+
this,
|
|
268
|
+
"max",
|
|
269
|
+
this.maxValue,
|
|
270
|
+
_ProgressController.values.max.default,
|
|
271
|
+
_ProgressController.valueConstraints.max
|
|
272
|
+
);
|
|
199
273
|
}
|
|
200
274
|
};
|
|
201
275
|
|
|
202
276
|
export { ProgressController };
|
|
203
|
-
//# sourceMappingURL=progress_controller.js.map
|
|
204
277
|
//# sourceMappingURL=progress_controller.js.map
|