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,13 +1,9 @@
|
|
|
1
1
|
import { Controller } from '@hotwired/stimulus';
|
|
2
2
|
|
|
3
|
-
// src/controllers/range_slider_controller.ts
|
|
4
|
-
|
|
5
|
-
// src/utils/logical_scroll.ts
|
|
6
3
|
function isRtl(element) {
|
|
7
4
|
return window.getComputedStyle(element).direction === "rtl";
|
|
8
5
|
}
|
|
9
6
|
|
|
10
|
-
// src/utils/arrow_step.ts
|
|
11
7
|
function logicalArrowKey(key, element) {
|
|
12
8
|
if (key !== "ArrowRight" && key !== "ArrowLeft") return key;
|
|
13
9
|
if (!isRtl(element)) return key;
|
|
@@ -18,27 +14,42 @@ function isReservedArrowChord(event, allow = []) {
|
|
|
18
14
|
return event.altKey && !allow.includes("alt") || event.ctrlKey && !allow.includes("ctrl") || event.metaKey && !allow.includes("meta") || event.shiftKey && !allow.includes("shift");
|
|
19
15
|
}
|
|
20
16
|
|
|
21
|
-
|
|
17
|
+
function writeField(field, value) {
|
|
18
|
+
if (field.value === value) return false;
|
|
19
|
+
field.value = value;
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
function commitField(target) {
|
|
23
|
+
target.dispatchEvent(new Event("change", { bubbles: true }));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
var ListenerSet = class {
|
|
27
|
+
#abort = new AbortController();
|
|
28
|
+
add(target, type, handler, options) {
|
|
29
|
+
target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });
|
|
30
|
+
}
|
|
31
|
+
dispose() {
|
|
32
|
+
this.#abort.abort();
|
|
33
|
+
this.#abort = new AbortController();
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
22
37
|
var MicrotaskCoalescer = class {
|
|
23
38
|
#run;
|
|
24
39
|
#queued = false;
|
|
25
40
|
#active = false;
|
|
26
41
|
#generation = 0;
|
|
27
|
-
/** @param run - the single reconciliation pass, invoked at most once per batch. */
|
|
28
42
|
constructor(run) {
|
|
29
43
|
this.#run = run;
|
|
30
44
|
}
|
|
31
|
-
/** Opens the window in which {@link schedule} is honoured; call from `connect()`. */
|
|
32
45
|
activate() {
|
|
33
46
|
this.#active = true;
|
|
34
47
|
}
|
|
35
|
-
/** Closes the window and drops any pending pass; call from `disconnect()`. */
|
|
36
48
|
cancel() {
|
|
37
49
|
this.#active = false;
|
|
38
50
|
this.#queued = false;
|
|
39
51
|
this.#generation += 1;
|
|
40
52
|
}
|
|
41
|
-
/** Requests one pass after the batch settles. Idempotent; inert outside the window. */
|
|
42
53
|
schedule() {
|
|
43
54
|
if (!this.#active || this.#queued) return;
|
|
44
55
|
this.#queued = true;
|
|
@@ -51,60 +62,130 @@ var MicrotaskCoalescer = class {
|
|
|
51
62
|
}
|
|
52
63
|
};
|
|
53
64
|
|
|
54
|
-
|
|
65
|
+
var MorphRenderWatcher = class {
|
|
66
|
+
#listeners = new ListenerSet();
|
|
67
|
+
#pass;
|
|
68
|
+
#element = null;
|
|
69
|
+
#onMorph = () => this.#pass.schedule();
|
|
70
|
+
constructor(run) {
|
|
71
|
+
this.#pass = new MicrotaskCoalescer(run);
|
|
72
|
+
}
|
|
73
|
+
observe(element) {
|
|
74
|
+
if (this.#element === element) return;
|
|
75
|
+
this.disconnect();
|
|
76
|
+
this.#element = element;
|
|
77
|
+
this.#pass.activate();
|
|
78
|
+
this.#listeners.add(element, "turbo:morph-element", this.#onMorph);
|
|
79
|
+
}
|
|
80
|
+
schedule() {
|
|
81
|
+
this.#pass.schedule();
|
|
82
|
+
}
|
|
83
|
+
disconnect() {
|
|
84
|
+
this.#listeners.dispose();
|
|
85
|
+
this.#pass.cancel();
|
|
86
|
+
this.#element = null;
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
var MoveCounter = class {
|
|
91
|
+
#sequence = 0;
|
|
92
|
+
record() {
|
|
93
|
+
this.#sequence += 1;
|
|
94
|
+
return this.#sequence;
|
|
95
|
+
}
|
|
96
|
+
isLatest(token) {
|
|
97
|
+
return token === this.#sequence;
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
var NUMBER_BOUNDS = {
|
|
102
|
+
finite: { finite: true },
|
|
103
|
+
positive: { finite: true, exclusiveMin: 0 }};
|
|
104
|
+
function matchesNumberBounds(value, bounds) {
|
|
105
|
+
if (!Number.isFinite(value)) {
|
|
106
|
+
const direction = value === Infinity ? "positive" : value === -Infinity ? "negative" : null;
|
|
107
|
+
if (direction === null) return false;
|
|
108
|
+
if (bounds.allowInfinity !== "both" && bounds.allowInfinity !== direction) return false;
|
|
109
|
+
}
|
|
110
|
+
if (bounds.min !== void 0 && value < bounds.min) return false;
|
|
111
|
+
if (bounds.max !== void 0 && value > bounds.max) return false;
|
|
112
|
+
if (bounds.exclusiveMin !== void 0 && value <= bounds.exclusiveMin) return false;
|
|
113
|
+
if (bounds.integer && !Number.isInteger(value)) return false;
|
|
114
|
+
if (bounds.allowedValues !== void 0 && !bounds.allowedValues.includes(value)) return false;
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function readNumber(raw, fallback, bounds) {
|
|
119
|
+
return matchesNumberBounds(raw, bounds) ? raw : fallback;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
var NumberValueReader = class {
|
|
123
|
+
#lastRejected = /* @__PURE__ */ new Map();
|
|
124
|
+
read(owner, name, raw, fallback, bounds) {
|
|
125
|
+
const resolved = readNumber(raw, fallback, bounds);
|
|
126
|
+
if (matchesNumberBounds(raw, bounds)) {
|
|
127
|
+
this.#lastRejected.delete(name);
|
|
128
|
+
return resolved;
|
|
129
|
+
}
|
|
130
|
+
const attribute = `data-${owner.identifier}-${name.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}-value`;
|
|
131
|
+
const literal = owner.element.getAttribute(attribute);
|
|
132
|
+
if (literal === null) {
|
|
133
|
+
this.#lastRejected.delete(name);
|
|
134
|
+
return resolved;
|
|
135
|
+
}
|
|
136
|
+
if (this.#lastRejected.get(name) !== literal) {
|
|
137
|
+
this.#lastRejected.set(name, literal);
|
|
138
|
+
console.warn(
|
|
139
|
+
`Stimeo UI: "${owner.identifier}" has an invalid number Value "${name}" declaration ${JSON.stringify(literal)}; using ${fallback}.`
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
return resolved;
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
|
|
55
146
|
var OwnedPointerSession = class {
|
|
56
|
-
pointerId;
|
|
147
|
+
#pointerId;
|
|
57
148
|
#owner;
|
|
58
149
|
#handlers;
|
|
59
150
|
#abort = new AbortController();
|
|
60
151
|
#active = true;
|
|
61
152
|
constructor(start, owner, handlers) {
|
|
62
|
-
this
|
|
153
|
+
this.#pointerId = start.pointerId;
|
|
63
154
|
this.#owner = owner;
|
|
64
155
|
this.#handlers = handlers;
|
|
65
156
|
const { signal } = this.#abort;
|
|
66
157
|
owner.ownerDocument.addEventListener("pointermove", this.#onMove, { signal });
|
|
67
158
|
owner.ownerDocument.addEventListener("pointerup", this.#onEndEvent, { signal });
|
|
68
159
|
owner.ownerDocument.addEventListener("pointercancel", this.#onEndEvent, { signal });
|
|
69
|
-
owner.addEventListener("lostpointercapture", this.#onLostCapture, { signal });
|
|
70
160
|
try {
|
|
71
|
-
owner.setPointerCapture?.(this
|
|
161
|
+
owner.setPointerCapture?.(this.#pointerId);
|
|
72
162
|
} catch {
|
|
73
163
|
}
|
|
74
164
|
}
|
|
75
|
-
/** Whether this session still owns its pointer and listeners. */
|
|
76
|
-
get active() {
|
|
77
|
-
return this.#active;
|
|
78
|
-
}
|
|
79
|
-
/** Whether `event` belongs to the initiating pointer of the live session. */
|
|
80
|
-
owns(event) {
|
|
81
|
-
return this.#active && event.pointerId === this.pointerId;
|
|
82
|
-
}
|
|
83
|
-
/** Releases capture/listeners and invokes the end callback exactly once. */
|
|
84
165
|
end() {
|
|
166
|
+
this.#finish("teardown");
|
|
167
|
+
}
|
|
168
|
+
#finish(kind) {
|
|
85
169
|
if (!this.#active) return;
|
|
86
170
|
this.#active = false;
|
|
87
171
|
this.#abort.abort();
|
|
88
172
|
try {
|
|
89
|
-
this.#owner.releasePointerCapture?.(this
|
|
173
|
+
this.#owner.releasePointerCapture?.(this.#pointerId);
|
|
90
174
|
} catch {
|
|
91
175
|
}
|
|
92
|
-
this.#handlers.end?.();
|
|
176
|
+
this.#handlers.end?.(kind);
|
|
177
|
+
}
|
|
178
|
+
#owns(event) {
|
|
179
|
+
return this.#active && event.pointerId === this.#pointerId;
|
|
93
180
|
}
|
|
94
181
|
#onMove = (event) => {
|
|
95
|
-
if (this
|
|
182
|
+
if (this.#owns(event)) this.#handlers.move(event);
|
|
96
183
|
};
|
|
97
184
|
#onEndEvent = (event) => {
|
|
98
|
-
if (this
|
|
99
|
-
};
|
|
100
|
-
#onLostCapture = (event) => {
|
|
101
|
-
const pointerId = event.pointerId;
|
|
102
|
-
if (typeof pointerId === "number" && pointerId !== this.pointerId) return;
|
|
103
|
-
this.end();
|
|
185
|
+
if (this.#owns(event)) this.#finish(event.type === "pointerup" ? "up" : "cancel");
|
|
104
186
|
};
|
|
105
187
|
};
|
|
106
188
|
|
|
107
|
-
// src/utils/range.ts
|
|
108
189
|
function rangeFraction(value, min, max) {
|
|
109
190
|
const span = max - min;
|
|
110
191
|
if (!(span > 0)) return 0;
|
|
@@ -120,7 +201,6 @@ function rangeFraction(value, min, max) {
|
|
|
120
201
|
return Math.min(1, Math.max(0, fraction));
|
|
121
202
|
}
|
|
122
203
|
|
|
123
|
-
// src/utils/stepped_value.ts
|
|
124
204
|
function effectiveStep(step) {
|
|
125
205
|
return Number.isFinite(step) && step > 0 ? step : 1;
|
|
126
206
|
}
|
|
@@ -226,13 +306,16 @@ function finiteFallback(min, max) {
|
|
|
226
306
|
return 0;
|
|
227
307
|
}
|
|
228
308
|
|
|
229
|
-
// src/controllers/range_slider_controller.ts
|
|
230
309
|
var START_PROPERTY = "--stimeo--range-slider-start";
|
|
231
310
|
var END_PROPERTY = "--stimeo--range-slider-end";
|
|
232
311
|
var DEFAULT_MIN = 0;
|
|
233
312
|
var DEFAULT_MAX = 100;
|
|
234
|
-
var RangeSliderController = class extends Controller {
|
|
235
|
-
|
|
313
|
+
var RangeSliderController = class _RangeSliderController extends Controller {
|
|
314
|
+
#numbers = new NumberValueReader();
|
|
315
|
+
#fieldWrites = /* @__PURE__ */ new WeakMap();
|
|
316
|
+
#moves = new MoveCounter();
|
|
317
|
+
#move = 0;
|
|
318
|
+
static targets = ["track", "startThumb", "endThumb", "startField", "endField"];
|
|
236
319
|
static values = {
|
|
237
320
|
min: { type: Number, default: DEFAULT_MIN },
|
|
238
321
|
max: { type: Number, default: DEFAULT_MAX },
|
|
@@ -241,51 +324,45 @@ var RangeSliderController = class extends Controller {
|
|
|
241
324
|
end: { type: Number, default: 100 },
|
|
242
325
|
logicalTrack: { type: Boolean, default: false }
|
|
243
326
|
};
|
|
327
|
+
static valueConstraints = {
|
|
328
|
+
min: NUMBER_BOUNDS.finite,
|
|
329
|
+
max: NUMBER_BOUNDS.finite,
|
|
330
|
+
step: NUMBER_BOUNDS.positive,
|
|
331
|
+
start: NUMBER_BOUNDS.finite,
|
|
332
|
+
end: NUMBER_BOUNDS.finite
|
|
333
|
+
};
|
|
244
334
|
static actions = ["onKeydown", "onPointerDown"];
|
|
245
|
-
static events = ["change"];
|
|
246
|
-
/** One initiating pointer owns each live drag and its stable target snapshot. */
|
|
335
|
+
static events = ["change", "reconcile"];
|
|
247
336
|
#drag = null;
|
|
248
|
-
|
|
337
|
+
#settled = { start: DEFAULT_MIN, end: DEFAULT_MAX };
|
|
249
338
|
get #mirrored() {
|
|
250
339
|
return this.logicalTrackValue && isRtl(this.element);
|
|
251
340
|
}
|
|
252
|
-
|
|
253
|
-
* Collapses a morph that swaps render inputs into one repaint, and refuses the
|
|
254
|
-
* pass Stimulus delivers before `connect()`.
|
|
255
|
-
*/
|
|
256
|
-
#repaint = new MicrotaskCoalescer(() => this.#render());
|
|
341
|
+
#repaint = new MorphRenderWatcher(() => this.#render());
|
|
257
342
|
connect() {
|
|
258
|
-
this.#repaint.
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
this.#commit(pair.start, pair.end, null, false);
|
|
343
|
+
this.#repaint.observe(this.element);
|
|
344
|
+
this.#settled = this.#currentPair(this.#effectiveRange);
|
|
345
|
+
this.#render();
|
|
262
346
|
}
|
|
263
|
-
/** Cancels any active pointer drag so document listeners never leak. */
|
|
264
347
|
disconnect() {
|
|
265
|
-
this.#repaint.
|
|
348
|
+
this.#repaint.disconnect();
|
|
266
349
|
this.#endDrag();
|
|
267
350
|
}
|
|
268
|
-
/** Repaints when application code (or a Turbo morph) changes `min` at runtime. */
|
|
269
351
|
minValueChanged() {
|
|
270
352
|
this.#repaint.schedule();
|
|
271
353
|
}
|
|
272
|
-
/** Repaints when application code (or a Turbo morph) changes `max` at runtime. */
|
|
273
354
|
maxValueChanged() {
|
|
274
355
|
this.#repaint.schedule();
|
|
275
356
|
}
|
|
276
|
-
/** Repaints when application code (or a Turbo morph) changes `step` at runtime. */
|
|
277
357
|
stepValueChanged() {
|
|
278
358
|
this.#repaint.schedule();
|
|
279
359
|
}
|
|
280
|
-
/** Repaints when application code (or a Turbo morph) changes `start` at runtime. */
|
|
281
360
|
startValueChanged() {
|
|
282
361
|
this.#repaint.schedule();
|
|
283
362
|
}
|
|
284
|
-
/** Repaints when application code (or a Turbo morph) changes `end` at runtime. */
|
|
285
363
|
endValueChanged() {
|
|
286
364
|
this.#repaint.schedule();
|
|
287
365
|
}
|
|
288
|
-
/** Hydrates a replacement start thumb and restores live-drag focus ownership. */
|
|
289
366
|
startThumbTargetConnected(thumb) {
|
|
290
367
|
const range = this.#effectiveRange;
|
|
291
368
|
const pair = this.#currentPair(range);
|
|
@@ -297,11 +374,10 @@ var RangeSliderController = class extends Controller {
|
|
|
297
374
|
}
|
|
298
375
|
this.#repaint.schedule();
|
|
299
376
|
}
|
|
300
|
-
/** Drops a stale start-thumb reference without orphaning the track gesture. */
|
|
301
377
|
startThumbTargetDisconnected(thumb) {
|
|
302
378
|
if (this.#drag?.kind === "start" && this.#drag.thumb === thumb) this.#drag.thumb = null;
|
|
379
|
+
this.#repaint.schedule();
|
|
303
380
|
}
|
|
304
|
-
/** Hydrates a replacement end thumb and restores live-drag focus ownership. */
|
|
305
381
|
endThumbTargetConnected(thumb) {
|
|
306
382
|
const range = this.#effectiveRange;
|
|
307
383
|
const pair = this.#currentPair(range);
|
|
@@ -313,20 +389,30 @@ var RangeSliderController = class extends Controller {
|
|
|
313
389
|
}
|
|
314
390
|
this.#repaint.schedule();
|
|
315
391
|
}
|
|
316
|
-
|
|
392
|
+
startFieldTargetConnected() {
|
|
393
|
+
this.#repaint.schedule();
|
|
394
|
+
}
|
|
395
|
+
startFieldTargetDisconnected() {
|
|
396
|
+
this.#repaint.schedule();
|
|
397
|
+
}
|
|
398
|
+
endFieldTargetConnected() {
|
|
399
|
+
this.#repaint.schedule();
|
|
400
|
+
}
|
|
401
|
+
endFieldTargetDisconnected() {
|
|
402
|
+
this.#repaint.schedule();
|
|
403
|
+
}
|
|
317
404
|
endThumbTargetDisconnected(thumb) {
|
|
318
405
|
if (this.#drag?.kind === "end" && this.#drag.thumb === thumb) this.#drag.thumb = null;
|
|
406
|
+
this.#repaint.schedule();
|
|
319
407
|
}
|
|
320
|
-
/** Ends a gesture whose geometry target disappeared or ceased being a target. */
|
|
321
408
|
trackTargetDisconnected(track) {
|
|
322
409
|
if (this.#drag?.track === track) this.#endDrag();
|
|
323
410
|
}
|
|
324
|
-
/** Keyboard stepping for whichever thumb is focused (the action's element). */
|
|
325
411
|
onKeydown(event) {
|
|
326
412
|
if (isReservedArrowChord(event)) return;
|
|
327
413
|
const thumb = event.currentTarget;
|
|
328
|
-
const isStart = this.
|
|
329
|
-
const isEnd = this.
|
|
414
|
+
const isStart = this.startThumbTargets.includes(thumb);
|
|
415
|
+
const isEnd = this.endThumbTargets.includes(thumb);
|
|
330
416
|
const effectiveRange = this.#effectiveRange;
|
|
331
417
|
const pair = this.#currentPair(effectiveRange);
|
|
332
418
|
let kind;
|
|
@@ -380,7 +466,6 @@ var RangeSliderController = class extends Controller {
|
|
|
380
466
|
event.preventDefault();
|
|
381
467
|
this.#moveThumb(kind, next);
|
|
382
468
|
}
|
|
383
|
-
/** Begins a pointer drag on the track, moving the thumb nearest the press. */
|
|
384
469
|
onPointerDown(event) {
|
|
385
470
|
if (event.button !== 0 || this.#drag || !this.hasTrackTarget || !this.hasStartThumbTarget || !this.hasEndThumbTarget) {
|
|
386
471
|
return;
|
|
@@ -413,7 +498,6 @@ var RangeSliderController = class extends Controller {
|
|
|
413
498
|
});
|
|
414
499
|
this.#drag = drag;
|
|
415
500
|
}
|
|
416
|
-
/** Maps a pointer X coordinate to a raw value using the track geometry. */
|
|
417
501
|
#valueFromClientX(clientX, mirrored, track) {
|
|
418
502
|
const rect = track.getBoundingClientRect();
|
|
419
503
|
if (rect.width === 0) return null;
|
|
@@ -422,12 +506,6 @@ var RangeSliderController = class extends Controller {
|
|
|
422
506
|
const range = this.#effectiveRange;
|
|
423
507
|
return range.min + fraction * (range.max - range.min);
|
|
424
508
|
}
|
|
425
|
-
/**
|
|
426
|
-
* Chooses the closest thumb while keeping an overlapped pair expandable.
|
|
427
|
-
* Ordinary midpoint ties stay deterministic on `start`; at an overlap, a
|
|
428
|
-
* press above the shared value selects `end` and a press below selects
|
|
429
|
-
* `start`.
|
|
430
|
-
*/
|
|
431
509
|
#nearestThumb(value, pair) {
|
|
432
510
|
const startDistance = Math.abs(value - pair.start);
|
|
433
511
|
const endDistance = Math.abs(value - pair.end);
|
|
@@ -435,50 +513,72 @@ var RangeSliderController = class extends Controller {
|
|
|
435
513
|
if (pair.start === pair.end && value > pair.start) return "end";
|
|
436
514
|
return "start";
|
|
437
515
|
}
|
|
438
|
-
/** Moves one thumb to a new raw value, keeping the pair ordered. */
|
|
439
516
|
#moveThumb(kind, raw) {
|
|
440
517
|
const pair = this.#currentPair(this.#effectiveRange);
|
|
441
518
|
if (kind === "start") {
|
|
442
|
-
this.#commit(raw, pair.end, "start"
|
|
519
|
+
this.#commit(raw, pair.end, "start");
|
|
443
520
|
} else {
|
|
444
|
-
this.#commit(pair.start, raw, "end"
|
|
521
|
+
this.#commit(pair.start, raw, "end");
|
|
445
522
|
}
|
|
446
523
|
}
|
|
447
|
-
|
|
448
|
-
* Clamps and snaps `start`/`end`, enforces `start ≤ end`, stores the pair, and
|
|
449
|
-
* reflects it onto the thumbs' ARIA attributes and the range custom
|
|
450
|
-
* properties. Dispatches `stimeo--range-slider:change` only when a
|
|
451
|
-
* user-driven update changes the normalized pair, with
|
|
452
|
-
* `{ start: number, end: number }` in `detail`.
|
|
453
|
-
*/
|
|
454
|
-
#commit(start, end, moving, notify) {
|
|
524
|
+
#commit(start, end, moving) {
|
|
455
525
|
const range = this.#effectiveRange;
|
|
456
|
-
const previous = this.#currentPair(range);
|
|
457
526
|
let nextStart = snapSteppedValue(start, range);
|
|
458
527
|
let nextEnd = snapSteppedValue(end, range);
|
|
459
528
|
if (nextStart > nextEnd) {
|
|
460
529
|
if (moving === "start") nextStart = nextEnd;
|
|
461
|
-
else
|
|
462
|
-
else [nextStart, nextEnd] = [nextEnd, nextStart];
|
|
530
|
+
else nextEnd = nextStart;
|
|
463
531
|
}
|
|
532
|
+
const pair = { start: nextStart, end: nextEnd };
|
|
533
|
+
const reported = this.#settled;
|
|
534
|
+
this.#settled = pair;
|
|
535
|
+
const changed = nextStart !== reported.start || nextEnd !== reported.end;
|
|
536
|
+
if (changed) this.#move = this.#moves.record();
|
|
537
|
+
const move = this.#move;
|
|
464
538
|
if (!Object.is(this.startValue, nextStart)) this.startValue = nextStart;
|
|
465
539
|
if (!Object.is(this.endValue, nextEnd)) this.endValue = nextEnd;
|
|
466
|
-
const pair = { start: nextStart, end: nextEnd };
|
|
467
540
|
this.#renderPair(pair, range);
|
|
468
|
-
|
|
541
|
+
const fields = this.#mirrorFields(pair);
|
|
542
|
+
for (const [field, value, write] of fields) {
|
|
543
|
+
if (field.value === value && this.#fieldWrites.get(field) === write) commitField(field);
|
|
544
|
+
}
|
|
545
|
+
if (!this.#moves.isLatest(move)) return;
|
|
546
|
+
if (changed) {
|
|
469
547
|
this.dispatch("change", { detail: { start: nextStart, end: nextEnd } });
|
|
470
548
|
}
|
|
471
549
|
}
|
|
472
|
-
/**
|
|
473
|
-
* Reflects morph-supplied Values without writing them back or dispatching.
|
|
474
|
-
*
|
|
475
|
-
* @stimeoRenderRoot
|
|
476
|
-
*/
|
|
477
550
|
#render() {
|
|
478
551
|
const range = this.#effectiveRange;
|
|
479
|
-
this.#
|
|
552
|
+
const pair = this.#currentPair(range);
|
|
553
|
+
this.#renderPair(pair, range);
|
|
554
|
+
this.#mirrorFields(pair);
|
|
555
|
+
const settled = this.#settled;
|
|
556
|
+
if (pair.start === settled.start && pair.end === settled.end) return;
|
|
557
|
+
this.#settled = pair;
|
|
558
|
+
this.#move = this.#moves.record();
|
|
559
|
+
this.dispatch("reconcile", { detail: { start: pair.start, end: pair.end } });
|
|
560
|
+
}
|
|
561
|
+
#writeField(field, value) {
|
|
562
|
+
if (!writeField(field, value)) return false;
|
|
563
|
+
this.#fieldWrites.set(field, (this.#fieldWrites.get(field) ?? 0) + 1);
|
|
564
|
+
return true;
|
|
565
|
+
}
|
|
566
|
+
#mirrorFields(pair) {
|
|
567
|
+
const fields = [];
|
|
568
|
+
if (this.hasStartFieldTarget) {
|
|
569
|
+
const field = this.startFieldTarget;
|
|
570
|
+
const value = String(pair.start);
|
|
571
|
+
if (this.#writeField(field, value))
|
|
572
|
+
fields.push([field, value, this.#fieldWrites.get(field) ?? 0]);
|
|
573
|
+
}
|
|
574
|
+
if (this.hasEndFieldTarget) {
|
|
575
|
+
const field = this.endFieldTarget;
|
|
576
|
+
const value = String(pair.end);
|
|
577
|
+
if (this.#writeField(field, value))
|
|
578
|
+
fields.push([field, value, this.#fieldWrites.get(field) ?? 0]);
|
|
579
|
+
}
|
|
580
|
+
return fields;
|
|
480
581
|
}
|
|
481
|
-
/** Reflects one normalized pair onto both thumbs and CSS properties. */
|
|
482
582
|
#renderPair(pair, range) {
|
|
483
583
|
if (this.hasStartThumbTarget) {
|
|
484
584
|
this.#renderStartThumb(this.startThumbTarget, pair, range);
|
|
@@ -488,24 +588,20 @@ var RangeSliderController = class extends Controller {
|
|
|
488
588
|
}
|
|
489
589
|
this.#renderFractions(pair, range);
|
|
490
590
|
}
|
|
491
|
-
/** Writes only changed ARIA for the lower thumb. */
|
|
492
591
|
#renderStartThumb(thumb, pair, range) {
|
|
493
592
|
this.#setAria(thumb, "aria-valuemin", range.min);
|
|
494
593
|
this.#setAria(thumb, "aria-valuemax", pair.end);
|
|
495
594
|
this.#setAria(thumb, "aria-valuenow", pair.start);
|
|
496
595
|
}
|
|
497
|
-
/** Writes only changed ARIA for the upper thumb. */
|
|
498
596
|
#renderEndThumb(thumb, pair, range) {
|
|
499
597
|
this.#setAria(thumb, "aria-valuemin", pair.start);
|
|
500
598
|
this.#setAria(thumb, "aria-valuemax", range.max);
|
|
501
599
|
this.#setAria(thumb, "aria-valuenow", pair.end);
|
|
502
600
|
}
|
|
503
|
-
/** Writes one numeric ARIA attribute only when its serialized value changed. */
|
|
504
601
|
#setAria(thumb, name, value) {
|
|
505
602
|
const next = String(value);
|
|
506
603
|
if (thumb.getAttribute(name) !== next) thumb.setAttribute(name, next);
|
|
507
604
|
}
|
|
508
|
-
/** Writes the two behavior-only fraction hooks only when they changed. */
|
|
509
605
|
#renderFractions(pair, range) {
|
|
510
606
|
const start = String(rangeFraction(pair.start, range.min, range.max));
|
|
511
607
|
const end = String(rangeFraction(pair.end, range.min, range.max));
|
|
@@ -516,33 +612,70 @@ var RangeSliderController = class extends Controller {
|
|
|
516
612
|
this.element.style.setProperty(END_PROPERTY, end);
|
|
517
613
|
}
|
|
518
614
|
}
|
|
519
|
-
/** Current normalized and ordered pair derived from live declarative Values. */
|
|
520
615
|
#currentPair(range) {
|
|
521
|
-
const rawStart =
|
|
522
|
-
const rawEnd =
|
|
616
|
+
const rawStart = this.#safeStart;
|
|
617
|
+
const rawEnd = this.#safeEnd;
|
|
523
618
|
const start = snapSteppedValue(rawStart, range);
|
|
524
619
|
const end = snapSteppedValue(rawEnd, range);
|
|
525
620
|
if (start <= end) return { start, end };
|
|
526
621
|
return { start: end, end: start };
|
|
527
622
|
}
|
|
528
|
-
/**
|
|
529
|
-
* Finite ordered range used by ARIA, keyboard, pointer, and CSS reflection.
|
|
530
|
-
* Invalid endpoints fall back to the public defaults; an authored `max`
|
|
531
|
-
* below `min` collapses to the finite minimum.
|
|
532
|
-
*/
|
|
533
623
|
get #effectiveRange() {
|
|
534
|
-
const min =
|
|
535
|
-
const authoredMax =
|
|
536
|
-
return { min, max: Math.max(min, authoredMax), step: this
|
|
624
|
+
const min = this.#safeMin;
|
|
625
|
+
const authoredMax = this.#safeMax;
|
|
626
|
+
return { min, max: Math.max(min, authoredMax), step: this.#safeStep, base: min };
|
|
537
627
|
}
|
|
538
|
-
/** Ends the current pointer session without dispatching another change. */
|
|
539
628
|
#endDrag() {
|
|
540
629
|
const drag = this.#drag;
|
|
541
630
|
this.#drag = null;
|
|
542
631
|
drag?.pointer?.end();
|
|
543
632
|
}
|
|
633
|
+
get #safeMin() {
|
|
634
|
+
return this.#numbers.read(
|
|
635
|
+
this,
|
|
636
|
+
"min",
|
|
637
|
+
this.minValue,
|
|
638
|
+
_RangeSliderController.values.min.default,
|
|
639
|
+
_RangeSliderController.valueConstraints.min
|
|
640
|
+
);
|
|
641
|
+
}
|
|
642
|
+
get #safeMax() {
|
|
643
|
+
return this.#numbers.read(
|
|
644
|
+
this,
|
|
645
|
+
"max",
|
|
646
|
+
this.maxValue,
|
|
647
|
+
_RangeSliderController.values.max.default,
|
|
648
|
+
_RangeSliderController.valueConstraints.max
|
|
649
|
+
);
|
|
650
|
+
}
|
|
651
|
+
get #safeStep() {
|
|
652
|
+
return this.#numbers.read(
|
|
653
|
+
this,
|
|
654
|
+
"step",
|
|
655
|
+
this.stepValue,
|
|
656
|
+
_RangeSliderController.values.step.default,
|
|
657
|
+
_RangeSliderController.valueConstraints.step
|
|
658
|
+
);
|
|
659
|
+
}
|
|
660
|
+
get #safeStart() {
|
|
661
|
+
return this.#numbers.read(
|
|
662
|
+
this,
|
|
663
|
+
"start",
|
|
664
|
+
this.startValue,
|
|
665
|
+
this.#effectiveRange.min,
|
|
666
|
+
_RangeSliderController.valueConstraints.start
|
|
667
|
+
);
|
|
668
|
+
}
|
|
669
|
+
get #safeEnd() {
|
|
670
|
+
return this.#numbers.read(
|
|
671
|
+
this,
|
|
672
|
+
"end",
|
|
673
|
+
this.endValue,
|
|
674
|
+
this.#effectiveRange.max,
|
|
675
|
+
_RangeSliderController.valueConstraints.end
|
|
676
|
+
);
|
|
677
|
+
}
|
|
544
678
|
};
|
|
545
679
|
|
|
546
680
|
export { RangeSliderController };
|
|
547
|
-
//# sourceMappingURL=range_slider_controller.js.map
|
|
548
681
|
//# sourceMappingURL=range_slider_controller.js.map
|