stimeo-ui 0.16.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 +31 -0
- data/README.md +8 -1
- data/dist/cable/index.js +363 -436
- data/dist/controllers/accordion_controller.js +166 -55
- data/dist/controllers/alert_dialog_controller.js +862 -221
- data/dist/controllers/announcer_controller.js +135 -198
- data/dist/controllers/aspect_ratio_controller.js +0 -10
- data/dist/controllers/auto_submit_controller.js +271 -127
- data/dist/controllers/avatar_controller.js +171 -105
- data/dist/controllers/breadcrumb_controller.js +0 -130
- data/dist/controllers/bulk_select_controller.js +262 -52
- data/dist/controllers/calendar_controller.js +175 -209
- data/dist/controllers/carousel_controller.js +461 -399
- data/dist/controllers/character_counter_controller.js +246 -156
- data/dist/controllers/checkbox_controller.js +32 -74
- data/dist/controllers/clipboard_controller.js +168 -147
- data/dist/controllers/collapsible_controller.js +301 -120
- data/dist/controllers/color_picker_controller.js +149 -139
- data/dist/controllers/combobox_controller.js +360 -133
- data/dist/controllers/command_palette_controller.js +970 -358
- data/dist/controllers/conditional_fields_controller.js +166 -124
- data/dist/controllers/confirm_controller.js +939 -248
- data/dist/controllers/context_menu_controller.js +256 -128
- data/dist/controllers/count_up_controller.js +73 -21
- data/dist/controllers/countdown_controller.js +183 -148
- data/dist/controllers/currency_input_controller.js +134 -158
- data/dist/controllers/data_grid_controller.js +114 -186
- data/dist/controllers/date_range_picker_controller.js +288 -206
- data/dist/controllers/dialog_controller.js +858 -223
- data/dist/controllers/direct_upload_controller.js +73 -153
- data/dist/controllers/dirty_form_controller.js +14 -48
- data/dist/controllers/dismissible_controller.js +175 -21
- data/dist/controllers/drawer_controller.js +933 -341
- data/dist/controllers/dropdown_controller.js +312 -82
- data/dist/controllers/editable_controller.js +158 -68
- data/dist/controllers/empty_state_controller.js +67 -53
- data/dist/controllers/file_dropzone_controller.js +311 -207
- data/dist/controllers/filter_controller.js +67 -39
- data/dist/controllers/flash_controller.js +172 -314
- data/dist/controllers/focus_controller.js +654 -240
- data/dist/controllers/form_field_controller.js +147 -132
- data/dist/controllers/form_validation_controller.js +13 -86
- data/dist/controllers/frame_loading_controller.js +264 -213
- data/dist/controllers/highlight_controller.js +107 -75
- data/dist/controllers/hover_card_controller.js +309 -118
- data/dist/controllers/idle_controller.js +267 -101
- data/dist/controllers/input_mask_controller.js +71 -72
- data/dist/controllers/intersection_controller.js +147 -106
- data/dist/controllers/lazy_frame_controller.js +9 -65
- data/dist/controllers/listbox_controller.js +306 -204
- data/dist/controllers/local_time_controller.js +54 -64
- data/dist/controllers/masonry_controller.js +70 -93
- data/dist/controllers/menu_controller.js +214 -156
- data/dist/controllers/menubar_controller.js +170 -329
- data/dist/controllers/meter_controller.js +151 -67
- data/dist/controllers/multi_select_controller.js +593 -358
- data/dist/controllers/navigation_menu_controller.js +124 -232
- data/dist/controllers/nested_form_controller.js +352 -182
- data/dist/controllers/network_status_controller.js +242 -54
- data/dist/controllers/number_input_controller.js +320 -263
- data/dist/controllers/optimistic_controller.js +16 -76
- data/dist/controllers/otp_controller.js +198 -193
- data/dist/controllers/overflow_indicator_controller.js +116 -113
- data/dist/controllers/overflow_menu_controller.js +343 -283
- data/dist/controllers/pagination_controller.js +295 -119
- data/dist/controllers/password_reveal_controller.js +389 -121
- data/dist/controllers/password_strength_controller.js +99 -153
- data/dist/controllers/persist_controller.js +237 -103
- data/dist/controllers/pointer_drag_controller.js +185 -165
- data/dist/controllers/popover_controller.js +236 -97
- data/dist/controllers/portal_controller.js +209 -76
- data/dist/controllers/preview_guard_controller.js +232 -116
- data/dist/controllers/progress_controller.js +125 -56
- data/dist/controllers/radio_group_controller.js +117 -137
- data/dist/controllers/range_slider_controller.js +192 -105
- data/dist/controllers/rating_controller.js +418 -207
- data/dist/controllers/read_more_controller.js +134 -63
- 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 +255 -139
- data/dist/controllers/roving_controller.js +39 -84
- data/dist/controllers/scroll_area_controller.js +428 -225
- data/dist/controllers/scroll_restore_controller.js +0 -53
- data/dist/controllers/scroll_visibility_controller.js +395 -145
- data/dist/controllers/scrollspy_controller.js +181 -242
- data/dist/controllers/separator_controller.js +267 -140
- data/dist/controllers/sidebar_controller.js +1023 -386
- data/dist/controllers/skeleton_controller.js +248 -97
- data/dist/controllers/slider_controller.js +159 -79
- data/dist/controllers/smart_sticky_header_controller.js +162 -63
- data/dist/controllers/sortable_controller.js +0 -112
- data/dist/controllers/spinner_controller.js +277 -169
- data/dist/controllers/step_indicator_controller.js +99 -46
- data/dist/controllers/stepper_controller.js +114 -73
- data/dist/controllers/stick_to_bottom_controller.js +262 -138
- data/dist/controllers/sticky_observer_controller.js +214 -49
- data/dist/controllers/submit_once_controller.js +294 -205
- data/dist/controllers/switch_controller.js +33 -60
- data/dist/controllers/tabs_controller.js +35 -29
- data/dist/controllers/tags_input_controller.js +136 -145
- data/dist/controllers/textarea_autosize_controller.js +71 -58
- data/dist/controllers/theme_controller.js +130 -112
- data/dist/controllers/time_picker_controller.js +146 -80
- data/dist/controllers/toast_controller.js +186 -297
- data/dist/controllers/toggle_group_controller.js +95 -136
- data/dist/controllers/toolbar_controller.js +68 -98
- data/dist/controllers/tooltip_controller.js +158 -124
- data/dist/controllers/transition_controller.js +123 -119
- data/dist/controllers/tree_view_controller.js +150 -345
- data/dist/index.js +6370 -9346
- data/dist/positioning/index.js +148 -47
- 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,7 +14,6 @@ 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
|
-
// src/utils/field_mirror.ts
|
|
22
17
|
function writeField(field, value) {
|
|
23
18
|
if (field.value === value) return false;
|
|
24
19
|
field.value = value;
|
|
@@ -28,27 +23,33 @@ function commitField(target) {
|
|
|
28
23
|
target.dispatchEvent(new Event("change", { bubbles: true }));
|
|
29
24
|
}
|
|
30
25
|
|
|
31
|
-
|
|
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
|
+
|
|
32
37
|
var MicrotaskCoalescer = class {
|
|
33
38
|
#run;
|
|
34
39
|
#queued = false;
|
|
35
40
|
#active = false;
|
|
36
41
|
#generation = 0;
|
|
37
|
-
/** @param run - the single reconciliation pass, invoked at most once per batch. */
|
|
38
42
|
constructor(run) {
|
|
39
43
|
this.#run = run;
|
|
40
44
|
}
|
|
41
|
-
/** Opens the window in which {@link schedule} is honoured; call from `connect()`. */
|
|
42
45
|
activate() {
|
|
43
46
|
this.#active = true;
|
|
44
47
|
}
|
|
45
|
-
/** Closes the window and drops any pending pass; call from `disconnect()`. */
|
|
46
48
|
cancel() {
|
|
47
49
|
this.#active = false;
|
|
48
50
|
this.#queued = false;
|
|
49
51
|
this.#generation += 1;
|
|
50
52
|
}
|
|
51
|
-
/** Requests one pass after the batch settles. Idempotent; inert outside the window. */
|
|
52
53
|
schedule() {
|
|
53
54
|
if (!this.#active || this.#queued) return;
|
|
54
55
|
this.#queued = true;
|
|
@@ -61,7 +62,87 @@ var MicrotaskCoalescer = class {
|
|
|
61
62
|
}
|
|
62
63
|
};
|
|
63
64
|
|
|
64
|
-
|
|
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
|
+
|
|
65
146
|
var OwnedPointerSession = class {
|
|
66
147
|
#pointerId;
|
|
67
148
|
#owner;
|
|
@@ -81,7 +162,6 @@ var OwnedPointerSession = class {
|
|
|
81
162
|
} catch {
|
|
82
163
|
}
|
|
83
164
|
}
|
|
84
|
-
/** Releases capture and listeners, then reports the end exactly once. */
|
|
85
165
|
end() {
|
|
86
166
|
this.#finish("teardown");
|
|
87
167
|
}
|
|
@@ -106,7 +186,6 @@ var OwnedPointerSession = class {
|
|
|
106
186
|
};
|
|
107
187
|
};
|
|
108
188
|
|
|
109
|
-
// src/utils/range.ts
|
|
110
189
|
function rangeFraction(value, min, max) {
|
|
111
190
|
const span = max - min;
|
|
112
191
|
if (!(span > 0)) return 0;
|
|
@@ -122,7 +201,6 @@ function rangeFraction(value, min, max) {
|
|
|
122
201
|
return Math.min(1, Math.max(0, fraction));
|
|
123
202
|
}
|
|
124
203
|
|
|
125
|
-
// src/utils/stepped_value.ts
|
|
126
204
|
function effectiveStep(step) {
|
|
127
205
|
return Number.isFinite(step) && step > 0 ? step : 1;
|
|
128
206
|
}
|
|
@@ -228,12 +306,15 @@ function finiteFallback(min, max) {
|
|
|
228
306
|
return 0;
|
|
229
307
|
}
|
|
230
308
|
|
|
231
|
-
// src/controllers/range_slider_controller.ts
|
|
232
309
|
var START_PROPERTY = "--stimeo--range-slider-start";
|
|
233
310
|
var END_PROPERTY = "--stimeo--range-slider-end";
|
|
234
311
|
var DEFAULT_MIN = 0;
|
|
235
312
|
var DEFAULT_MAX = 100;
|
|
236
|
-
var RangeSliderController = class extends Controller {
|
|
313
|
+
var RangeSliderController = class _RangeSliderController extends Controller {
|
|
314
|
+
#numbers = new NumberValueReader();
|
|
315
|
+
#fieldWrites = /* @__PURE__ */ new WeakMap();
|
|
316
|
+
#moves = new MoveCounter();
|
|
317
|
+
#move = 0;
|
|
237
318
|
static targets = ["track", "startThumb", "endThumb", "startField", "endField"];
|
|
238
319
|
static values = {
|
|
239
320
|
min: { type: Number, default: DEFAULT_MIN },
|
|
@@ -243,59 +324,45 @@ var RangeSliderController = class extends Controller {
|
|
|
243
324
|
end: { type: Number, default: 100 },
|
|
244
325
|
logicalTrack: { type: Boolean, default: false }
|
|
245
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
|
+
};
|
|
246
334
|
static actions = ["onKeydown", "onPointerDown"];
|
|
247
335
|
static events = ["change", "reconcile"];
|
|
248
|
-
/** One initiating pointer owns each live drag and its stable target snapshot. */
|
|
249
336
|
#drag = null;
|
|
250
|
-
/**
|
|
251
|
-
* The pair last published: taken on connect, then moved by each user commit
|
|
252
|
-
* and by each repaint that reports a page-driven move.
|
|
253
|
-
*/
|
|
254
337
|
#settled = { start: DEFAULT_MIN, end: DEFAULT_MAX };
|
|
255
|
-
/** Whether the consumer declared a mirroring track and the direction mirrors it. */
|
|
256
338
|
get #mirrored() {
|
|
257
339
|
return this.logicalTrackValue && isRtl(this.element);
|
|
258
340
|
}
|
|
259
|
-
|
|
260
|
-
* Collapses a morph that swaps render inputs into one repaint, and refuses the
|
|
261
|
-
* pass Stimulus delivers before `connect()`.
|
|
262
|
-
*/
|
|
263
|
-
#repaint = new MicrotaskCoalescer(() => this.#render());
|
|
264
|
-
/**
|
|
265
|
-
* Renders the normalized, ordered pair without writing it back, and takes it
|
|
266
|
-
* as the baseline, so connecting reports nothing.
|
|
267
|
-
*/
|
|
341
|
+
#repaint = new MorphRenderWatcher(() => this.#render());
|
|
268
342
|
connect() {
|
|
269
|
-
this.#repaint.
|
|
343
|
+
this.#repaint.observe(this.element);
|
|
270
344
|
this.#settled = this.#currentPair(this.#effectiveRange);
|
|
271
345
|
this.#render();
|
|
272
346
|
}
|
|
273
|
-
/** Cancels any active pointer drag so document listeners never leak. */
|
|
274
347
|
disconnect() {
|
|
275
|
-
this.#repaint.
|
|
348
|
+
this.#repaint.disconnect();
|
|
276
349
|
this.#endDrag();
|
|
277
350
|
}
|
|
278
|
-
/** Repaints when application code (or a Turbo morph) changes `min` at runtime. */
|
|
279
351
|
minValueChanged() {
|
|
280
352
|
this.#repaint.schedule();
|
|
281
353
|
}
|
|
282
|
-
/** Repaints when application code (or a Turbo morph) changes `max` at runtime. */
|
|
283
354
|
maxValueChanged() {
|
|
284
355
|
this.#repaint.schedule();
|
|
285
356
|
}
|
|
286
|
-
/** Repaints when application code (or a Turbo morph) changes `step` at runtime. */
|
|
287
357
|
stepValueChanged() {
|
|
288
358
|
this.#repaint.schedule();
|
|
289
359
|
}
|
|
290
|
-
/** Repaints when application code (or a Turbo morph) changes `start` at runtime. */
|
|
291
360
|
startValueChanged() {
|
|
292
361
|
this.#repaint.schedule();
|
|
293
362
|
}
|
|
294
|
-
/** Repaints when application code (or a Turbo morph) changes `end` at runtime. */
|
|
295
363
|
endValueChanged() {
|
|
296
364
|
this.#repaint.schedule();
|
|
297
365
|
}
|
|
298
|
-
/** Hydrates a replacement start thumb and restores live-drag focus ownership. */
|
|
299
366
|
startThumbTargetConnected(thumb) {
|
|
300
367
|
const range = this.#effectiveRange;
|
|
301
368
|
const pair = this.#currentPair(range);
|
|
@@ -307,11 +374,10 @@ var RangeSliderController = class extends Controller {
|
|
|
307
374
|
}
|
|
308
375
|
this.#repaint.schedule();
|
|
309
376
|
}
|
|
310
|
-
/** Drops a stale start-thumb reference without orphaning the track gesture. */
|
|
311
377
|
startThumbTargetDisconnected(thumb) {
|
|
312
378
|
if (this.#drag?.kind === "start" && this.#drag.thumb === thumb) this.#drag.thumb = null;
|
|
379
|
+
this.#repaint.schedule();
|
|
313
380
|
}
|
|
314
|
-
/** Hydrates a replacement end thumb and restores live-drag focus ownership. */
|
|
315
381
|
endThumbTargetConnected(thumb) {
|
|
316
382
|
const range = this.#effectiveRange;
|
|
317
383
|
const pair = this.#currentPair(range);
|
|
@@ -323,28 +389,30 @@ var RangeSliderController = class extends Controller {
|
|
|
323
389
|
}
|
|
324
390
|
this.#repaint.schedule();
|
|
325
391
|
}
|
|
326
|
-
/** Fills a start field inserted or replaced at runtime on the next repaint. */
|
|
327
392
|
startFieldTargetConnected() {
|
|
328
393
|
this.#repaint.schedule();
|
|
329
394
|
}
|
|
330
|
-
|
|
395
|
+
startFieldTargetDisconnected() {
|
|
396
|
+
this.#repaint.schedule();
|
|
397
|
+
}
|
|
331
398
|
endFieldTargetConnected() {
|
|
332
399
|
this.#repaint.schedule();
|
|
333
400
|
}
|
|
334
|
-
|
|
401
|
+
endFieldTargetDisconnected() {
|
|
402
|
+
this.#repaint.schedule();
|
|
403
|
+
}
|
|
335
404
|
endThumbTargetDisconnected(thumb) {
|
|
336
405
|
if (this.#drag?.kind === "end" && this.#drag.thumb === thumb) this.#drag.thumb = null;
|
|
406
|
+
this.#repaint.schedule();
|
|
337
407
|
}
|
|
338
|
-
/** Ends a gesture whose geometry target disappeared or ceased being a target. */
|
|
339
408
|
trackTargetDisconnected(track) {
|
|
340
409
|
if (this.#drag?.track === track) this.#endDrag();
|
|
341
410
|
}
|
|
342
|
-
/** Keyboard stepping for whichever thumb is focused (the action's element). */
|
|
343
411
|
onKeydown(event) {
|
|
344
412
|
if (isReservedArrowChord(event)) return;
|
|
345
413
|
const thumb = event.currentTarget;
|
|
346
|
-
const isStart = this.
|
|
347
|
-
const isEnd = this.
|
|
414
|
+
const isStart = this.startThumbTargets.includes(thumb);
|
|
415
|
+
const isEnd = this.endThumbTargets.includes(thumb);
|
|
348
416
|
const effectiveRange = this.#effectiveRange;
|
|
349
417
|
const pair = this.#currentPair(effectiveRange);
|
|
350
418
|
let kind;
|
|
@@ -398,7 +466,6 @@ var RangeSliderController = class extends Controller {
|
|
|
398
466
|
event.preventDefault();
|
|
399
467
|
this.#moveThumb(kind, next);
|
|
400
468
|
}
|
|
401
|
-
/** Begins a pointer drag on the track, moving the thumb nearest the press. */
|
|
402
469
|
onPointerDown(event) {
|
|
403
470
|
if (event.button !== 0 || this.#drag || !this.hasTrackTarget || !this.hasStartThumbTarget || !this.hasEndThumbTarget) {
|
|
404
471
|
return;
|
|
@@ -431,7 +498,6 @@ var RangeSliderController = class extends Controller {
|
|
|
431
498
|
});
|
|
432
499
|
this.#drag = drag;
|
|
433
500
|
}
|
|
434
|
-
/** Maps a pointer X coordinate to a raw value using the track geometry. */
|
|
435
501
|
#valueFromClientX(clientX, mirrored, track) {
|
|
436
502
|
const rect = track.getBoundingClientRect();
|
|
437
503
|
if (rect.width === 0) return null;
|
|
@@ -440,12 +506,6 @@ var RangeSliderController = class extends Controller {
|
|
|
440
506
|
const range = this.#effectiveRange;
|
|
441
507
|
return range.min + fraction * (range.max - range.min);
|
|
442
508
|
}
|
|
443
|
-
/**
|
|
444
|
-
* Chooses the closest thumb while keeping an overlapped pair expandable.
|
|
445
|
-
* Ordinary midpoint ties stay deterministic on `start`; at an overlap, a
|
|
446
|
-
* press above the shared value selects `end` and a press below selects
|
|
447
|
-
* `start`.
|
|
448
|
-
*/
|
|
449
509
|
#nearestThumb(value, pair) {
|
|
450
510
|
const startDistance = Math.abs(value - pair.start);
|
|
451
511
|
const endDistance = Math.abs(value - pair.end);
|
|
@@ -453,7 +513,6 @@ var RangeSliderController = class extends Controller {
|
|
|
453
513
|
if (pair.start === pair.end && value > pair.start) return "end";
|
|
454
514
|
return "start";
|
|
455
515
|
}
|
|
456
|
-
/** Moves one thumb to a new raw value, keeping the pair ordered. */
|
|
457
516
|
#moveThumb(kind, raw) {
|
|
458
517
|
const pair = this.#currentPair(this.#effectiveRange);
|
|
459
518
|
if (kind === "start") {
|
|
@@ -462,17 +521,6 @@ var RangeSliderController = class extends Controller {
|
|
|
462
521
|
this.#commit(pair.start, raw, "end");
|
|
463
522
|
}
|
|
464
523
|
}
|
|
465
|
-
/**
|
|
466
|
-
* Clamps and snaps the pair a user's move of the `moving` thumb produced,
|
|
467
|
-
* stops that thumb at its partner, stores the pair, and reflects it onto the
|
|
468
|
-
* thumbs' ARIA attributes and the range custom properties. The pair is
|
|
469
|
-
* reported as `change` when it differs from the pair last published — not from
|
|
470
|
-
* the pair the move started from — so a page write no repaint has published
|
|
471
|
-
* yet is reported once, as this change, and a move that ends on the pair last
|
|
472
|
-
* published reports nothing. The baseline moves before any report goes out,
|
|
473
|
-
* so the repaint the Value writes schedule — and any move a subscriber makes
|
|
474
|
-
* while the report is dispatched — is measured from this pair.
|
|
475
|
-
*/
|
|
476
524
|
#commit(start, end, moving) {
|
|
477
525
|
const range = this.#effectiveRange;
|
|
478
526
|
let nextStart = snapSteppedValue(start, range);
|
|
@@ -484,47 +532,53 @@ var RangeSliderController = class extends Controller {
|
|
|
484
532
|
const pair = { start: nextStart, end: nextEnd };
|
|
485
533
|
const reported = this.#settled;
|
|
486
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;
|
|
487
538
|
if (!Object.is(this.startValue, nextStart)) this.startValue = nextStart;
|
|
488
539
|
if (!Object.is(this.endValue, nextEnd)) this.endValue = nextEnd;
|
|
489
540
|
this.#renderPair(pair, range);
|
|
490
|
-
this.#mirrorFields(pair
|
|
491
|
-
|
|
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) {
|
|
492
547
|
this.dispatch("change", { detail: { start: nextStart, end: nextEnd } });
|
|
493
548
|
}
|
|
494
549
|
}
|
|
495
|
-
/**
|
|
496
|
-
* Reflects the Values the page supplied without writing them back. A published
|
|
497
|
-
* pair that moved from the last one is reported once as `reconcile`; the
|
|
498
|
-
* fields follow without a native `change`.
|
|
499
|
-
*
|
|
500
|
-
* @stimeoRenderRoot
|
|
501
|
-
*/
|
|
502
550
|
#render() {
|
|
503
551
|
const range = this.#effectiveRange;
|
|
504
552
|
const pair = this.#currentPair(range);
|
|
505
553
|
this.#renderPair(pair, range);
|
|
506
|
-
this.#mirrorFields(pair
|
|
554
|
+
this.#mirrorFields(pair);
|
|
507
555
|
const settled = this.#settled;
|
|
508
556
|
if (pair.start === settled.start && pair.end === settled.end) return;
|
|
509
557
|
this.#settled = pair;
|
|
558
|
+
this.#move = this.#moves.record();
|
|
510
559
|
this.dispatch("reconcile", { detail: { start: pair.start, end: pair.end } });
|
|
511
560
|
}
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
#mirrorFields(pair
|
|
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 = [];
|
|
518
568
|
if (this.hasStartFieldTarget) {
|
|
519
569
|
const field = this.startFieldTarget;
|
|
520
|
-
|
|
570
|
+
const value = String(pair.start);
|
|
571
|
+
if (this.#writeField(field, value))
|
|
572
|
+
fields.push([field, value, this.#fieldWrites.get(field) ?? 0]);
|
|
521
573
|
}
|
|
522
574
|
if (this.hasEndFieldTarget) {
|
|
523
575
|
const field = this.endFieldTarget;
|
|
524
|
-
|
|
576
|
+
const value = String(pair.end);
|
|
577
|
+
if (this.#writeField(field, value))
|
|
578
|
+
fields.push([field, value, this.#fieldWrites.get(field) ?? 0]);
|
|
525
579
|
}
|
|
580
|
+
return fields;
|
|
526
581
|
}
|
|
527
|
-
/** Reflects one normalized pair onto both thumbs and CSS properties. */
|
|
528
582
|
#renderPair(pair, range) {
|
|
529
583
|
if (this.hasStartThumbTarget) {
|
|
530
584
|
this.#renderStartThumb(this.startThumbTarget, pair, range);
|
|
@@ -534,24 +588,20 @@ var RangeSliderController = class extends Controller {
|
|
|
534
588
|
}
|
|
535
589
|
this.#renderFractions(pair, range);
|
|
536
590
|
}
|
|
537
|
-
/** Writes only changed ARIA for the lower thumb. */
|
|
538
591
|
#renderStartThumb(thumb, pair, range) {
|
|
539
592
|
this.#setAria(thumb, "aria-valuemin", range.min);
|
|
540
593
|
this.#setAria(thumb, "aria-valuemax", pair.end);
|
|
541
594
|
this.#setAria(thumb, "aria-valuenow", pair.start);
|
|
542
595
|
}
|
|
543
|
-
/** Writes only changed ARIA for the upper thumb. */
|
|
544
596
|
#renderEndThumb(thumb, pair, range) {
|
|
545
597
|
this.#setAria(thumb, "aria-valuemin", pair.start);
|
|
546
598
|
this.#setAria(thumb, "aria-valuemax", range.max);
|
|
547
599
|
this.#setAria(thumb, "aria-valuenow", pair.end);
|
|
548
600
|
}
|
|
549
|
-
/** Writes one numeric ARIA attribute only when its serialized value changed. */
|
|
550
601
|
#setAria(thumb, name, value) {
|
|
551
602
|
const next = String(value);
|
|
552
603
|
if (thumb.getAttribute(name) !== next) thumb.setAttribute(name, next);
|
|
553
604
|
}
|
|
554
|
-
/** Writes the two behavior-only fraction hooks only when they changed. */
|
|
555
605
|
#renderFractions(pair, range) {
|
|
556
606
|
const start = String(rangeFraction(pair.start, range.min, range.max));
|
|
557
607
|
const end = String(rangeFraction(pair.end, range.min, range.max));
|
|
@@ -562,33 +612,70 @@ var RangeSliderController = class extends Controller {
|
|
|
562
612
|
this.element.style.setProperty(END_PROPERTY, end);
|
|
563
613
|
}
|
|
564
614
|
}
|
|
565
|
-
/** Current normalized and ordered pair derived from live declarative Values. */
|
|
566
615
|
#currentPair(range) {
|
|
567
|
-
const rawStart =
|
|
568
|
-
const rawEnd =
|
|
616
|
+
const rawStart = this.#safeStart;
|
|
617
|
+
const rawEnd = this.#safeEnd;
|
|
569
618
|
const start = snapSteppedValue(rawStart, range);
|
|
570
619
|
const end = snapSteppedValue(rawEnd, range);
|
|
571
620
|
if (start <= end) return { start, end };
|
|
572
621
|
return { start: end, end: start };
|
|
573
622
|
}
|
|
574
|
-
/**
|
|
575
|
-
* Finite ordered range used by ARIA, keyboard, pointer, and CSS reflection.
|
|
576
|
-
* Invalid endpoints fall back to the public defaults; an authored `max`
|
|
577
|
-
* below `min` collapses to the finite minimum.
|
|
578
|
-
*/
|
|
579
623
|
get #effectiveRange() {
|
|
580
|
-
const min =
|
|
581
|
-
const authoredMax =
|
|
582
|
-
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 };
|
|
583
627
|
}
|
|
584
|
-
/** Ends the current pointer session without dispatching another change. */
|
|
585
628
|
#endDrag() {
|
|
586
629
|
const drag = this.#drag;
|
|
587
630
|
this.#drag = null;
|
|
588
631
|
drag?.pointer?.end();
|
|
589
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
|
+
}
|
|
590
678
|
};
|
|
591
679
|
|
|
592
680
|
export { RangeSliderController };
|
|
593
|
-
//# sourceMappingURL=range_slider_controller.js.map
|
|
594
681
|
//# sourceMappingURL=range_slider_controller.js.map
|