stimeo-ui 0.16.0 → 0.18.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 +136 -0
- data/README.md +27 -11
- data/dist/cable/index.js +384 -449
- data/dist/controllers/accordion_controller.js +174 -55
- data/dist/controllers/alert_dialog_controller.js +903 -235
- data/dist/controllers/announcer_controller.js +238 -205
- data/dist/controllers/aspect_ratio_controller.js +0 -10
- data/dist/controllers/auto_submit_controller.js +414 -145
- data/dist/controllers/avatar_controller.js +185 -107
- data/dist/controllers/breadcrumb_controller.js +0 -130
- data/dist/controllers/bulk_select_controller.js +282 -58
- data/dist/controllers/calendar_controller.js +228 -260
- data/dist/controllers/carousel_controller.js +536 -436
- data/dist/controllers/character_counter_controller.js +493 -207
- data/dist/controllers/checkbox_controller.js +38 -74
- data/dist/controllers/clipboard_controller.js +261 -149
- data/dist/controllers/collapsible_controller.js +304 -120
- data/dist/controllers/color_picker_controller.js +178 -146
- data/dist/controllers/combobox_controller.js +522 -156
- data/dist/controllers/command_palette_controller.js +1156 -395
- data/dist/controllers/conditional_fields_controller.js +300 -171
- data/dist/controllers/confirm_controller.js +980 -262
- data/dist/controllers/context_menu_controller.js +346 -153
- data/dist/controllers/count_up_controller.js +73 -21
- data/dist/controllers/countdown_controller.js +281 -158
- data/dist/controllers/currency_input_controller.js +575 -225
- data/dist/controllers/data_grid_controller.js +248 -214
- data/dist/controllers/date_range_picker_controller.js +338 -235
- data/dist/controllers/dialog_controller.js +899 -237
- data/dist/controllers/direct_upload_controller.js +97 -167
- data/dist/controllers/dirty_form_controller.js +72 -69
- data/dist/controllers/dismissible_controller.js +196 -24
- data/dist/controllers/drawer_controller.js +974 -355
- data/dist/controllers/dropdown_controller.js +339 -96
- data/dist/controllers/editable_controller.js +315 -88
- data/dist/controllers/empty_state_controller.js +79 -58
- data/dist/controllers/file_dropzone_controller.js +322 -211
- data/dist/controllers/filter_controller.js +77 -42
- data/dist/controllers/flash_controller.js +238 -347
- data/dist/controllers/focus_controller.js +679 -240
- data/dist/controllers/form_field_controller.js +156 -132
- data/dist/controllers/form_validation_controller.js +31 -97
- data/dist/controllers/frame_loading_controller.js +275 -217
- data/dist/controllers/highlight_controller.js +107 -75
- data/dist/controllers/hover_card_controller.js +327 -130
- data/dist/controllers/idle_controller.js +271 -101
- data/dist/controllers/input_mask_controller.js +483 -97
- data/dist/controllers/intersection_controller.js +150 -106
- data/dist/controllers/lazy_frame_controller.js +24 -68
- data/dist/controllers/listbox_controller.js +523 -240
- data/dist/controllers/local_time_controller.js +102 -77
- data/dist/controllers/masonry_controller.js +80 -96
- data/dist/controllers/menu_controller.js +302 -188
- data/dist/controllers/menubar_controller.js +232 -345
- data/dist/controllers/meter_controller.js +417 -87
- data/dist/controllers/multi_select_controller.js +785 -393
- data/dist/controllers/navigation_menu_controller.js +190 -250
- data/dist/controllers/nested_form_controller.js +396 -192
- data/dist/controllers/network_status_controller.js +268 -62
- data/dist/controllers/number_input_controller.js +610 -303
- data/dist/controllers/optimistic_controller.js +45 -87
- data/dist/controllers/otp_controller.js +658 -255
- data/dist/controllers/overflow_indicator_controller.js +122 -113
- data/dist/controllers/overflow_menu_controller.js +470 -295
- data/dist/controllers/pagination_controller.js +332 -123
- data/dist/controllers/password_reveal_controller.js +392 -121
- data/dist/controllers/password_strength_controller.js +111 -158
- data/dist/controllers/persist_controller.js +165 -111
- data/dist/controllers/pointer_drag_controller.js +434 -188
- data/dist/controllers/popover_controller.js +269 -113
- data/dist/controllers/portal_controller.js +215 -76
- data/dist/controllers/preview_guard_controller.js +243 -116
- data/dist/controllers/progress_controller.js +391 -78
- data/dist/controllers/radio_group_controller.js +278 -211
- data/dist/controllers/range_slider_controller.js +220 -113
- data/dist/controllers/rating_controller.js +458 -211
- data/dist/controllers/read_more_controller.js +138 -63
- data/dist/controllers/reading_progress_controller.js +166 -137
- data/dist/controllers/relative_time_controller.js +211 -112
- data/dist/controllers/{reset_before_cache_controller.js → reset_on_restore_controller.js} +103 -38
- data/dist/controllers/resizable_controller.js +299 -150
- data/dist/controllers/roving_controller.js +49 -88
- data/dist/controllers/scroll_area_controller.js +490 -246
- data/dist/controllers/scroll_restore_controller.js +14 -55
- data/dist/controllers/scroll_visibility_controller.js +432 -149
- data/dist/controllers/scrollspy_controller.js +193 -248
- data/dist/controllers/separator_controller.js +285 -142
- data/dist/controllers/sidebar_controller.js +1069 -404
- data/dist/controllers/skeleton_controller.js +259 -101
- data/dist/controllers/slider_controller.js +187 -86
- data/dist/controllers/smart_sticky_header_controller.js +173 -66
- data/dist/controllers/sortable_controller.js +26 -125
- data/dist/controllers/spinner_controller.js +288 -173
- data/dist/controllers/step_indicator_controller.js +105 -46
- data/dist/controllers/stepper_controller.js +120 -73
- data/dist/controllers/stick_to_bottom_controller.js +275 -140
- data/dist/controllers/sticky_observer_controller.js +220 -49
- data/dist/controllers/submit_once_controller.js +376 -227
- data/dist/controllers/switch_controller.js +201 -84
- data/dist/controllers/tabs_controller.js +41 -30
- data/dist/controllers/tags_input_controller.js +293 -173
- data/dist/controllers/textarea_autosize_controller.js +108 -74
- data/dist/controllers/theme_controller.js +140 -116
- data/dist/controllers/time_picker_controller.js +331 -127
- data/dist/controllers/toast_controller.js +244 -324
- data/dist/controllers/toggle_group_controller.js +252 -191
- data/dist/controllers/toolbar_controller.js +219 -120
- data/dist/controllers/tooltip_controller.js +158 -124
- data/dist/controllers/transition_controller.js +123 -119
- data/dist/controllers/tree_view_controller.js +283 -369
- data/dist/index.js +8573 -10410
- data/dist/positioning/index.js +154 -47
- data/lib/generators/stimeo/install/install_generator.rb +7 -2
- data/lib/generators/stimeo/install/templates/stimeo.js +19 -6
- 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;
|
|
@@ -17,8 +13,17 @@ function isReservedArrowChord(event, allow = []) {
|
|
|
17
13
|
if (!event.key.startsWith("Arrow")) return false;
|
|
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
|
}
|
|
16
|
+
function hasModifierChord(event) {
|
|
17
|
+
return event.altKey || event.ctrlKey || event.metaKey || event.shiftKey;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function setAttributeIfChanged(element, name, value, beforeWrite) {
|
|
21
|
+
if (element.getAttribute(name) === value) return false;
|
|
22
|
+
if (value === null) element.removeAttribute(name);
|
|
23
|
+
else element.setAttribute(name, value);
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
20
26
|
|
|
21
|
-
// src/utils/field_mirror.ts
|
|
22
27
|
function writeField(field, value) {
|
|
23
28
|
if (field.value === value) return false;
|
|
24
29
|
field.value = value;
|
|
@@ -28,27 +33,36 @@ function commitField(target) {
|
|
|
28
33
|
target.dispatchEvent(new Event("change", { bubbles: true }));
|
|
29
34
|
}
|
|
30
35
|
|
|
31
|
-
|
|
36
|
+
var ListenerSet = class {
|
|
37
|
+
#abort = new AbortController();
|
|
38
|
+
add(target, type, handler, options) {
|
|
39
|
+
target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });
|
|
40
|
+
}
|
|
41
|
+
dispose() {
|
|
42
|
+
this.#abort.abort();
|
|
43
|
+
this.#abort = new AbortController();
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
32
47
|
var MicrotaskCoalescer = class {
|
|
33
48
|
#run;
|
|
34
49
|
#queued = false;
|
|
35
50
|
#active = false;
|
|
36
51
|
#generation = 0;
|
|
37
|
-
/** @param run - the single reconciliation pass, invoked at most once per batch. */
|
|
38
52
|
constructor(run) {
|
|
39
53
|
this.#run = run;
|
|
40
54
|
}
|
|
41
|
-
/** Opens the window in which {@link schedule} is honoured; call from `connect()`. */
|
|
42
55
|
activate() {
|
|
43
56
|
this.#active = true;
|
|
44
57
|
}
|
|
45
|
-
/** Closes the window and drops any pending pass; call from `disconnect()`. */
|
|
46
58
|
cancel() {
|
|
47
59
|
this.#active = false;
|
|
48
60
|
this.#queued = false;
|
|
49
61
|
this.#generation += 1;
|
|
50
62
|
}
|
|
51
|
-
|
|
63
|
+
get pending() {
|
|
64
|
+
return this.#queued;
|
|
65
|
+
}
|
|
52
66
|
schedule() {
|
|
53
67
|
if (!this.#active || this.#queued) return;
|
|
54
68
|
this.#queued = true;
|
|
@@ -61,7 +75,90 @@ var MicrotaskCoalescer = class {
|
|
|
61
75
|
}
|
|
62
76
|
};
|
|
63
77
|
|
|
64
|
-
|
|
78
|
+
var MorphRenderWatcher = class {
|
|
79
|
+
#listeners = new ListenerSet();
|
|
80
|
+
#pass;
|
|
81
|
+
#element = null;
|
|
82
|
+
#onMorph = () => this.#pass.schedule();
|
|
83
|
+
constructor(run) {
|
|
84
|
+
this.#pass = new MicrotaskCoalescer(run);
|
|
85
|
+
}
|
|
86
|
+
observe(element) {
|
|
87
|
+
if (this.#element === element) return;
|
|
88
|
+
this.disconnect();
|
|
89
|
+
this.#element = element;
|
|
90
|
+
this.#pass.activate();
|
|
91
|
+
this.#listeners.add(element, "turbo:morph-element", this.#onMorph);
|
|
92
|
+
}
|
|
93
|
+
schedule() {
|
|
94
|
+
this.#pass.schedule();
|
|
95
|
+
}
|
|
96
|
+
get pending() {
|
|
97
|
+
return this.#pass.pending;
|
|
98
|
+
}
|
|
99
|
+
disconnect() {
|
|
100
|
+
this.#listeners.dispose();
|
|
101
|
+
this.#pass.cancel();
|
|
102
|
+
this.#element = null;
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
var MoveCounter = class {
|
|
107
|
+
#sequence = 0;
|
|
108
|
+
record() {
|
|
109
|
+
this.#sequence += 1;
|
|
110
|
+
return this.#sequence;
|
|
111
|
+
}
|
|
112
|
+
isLatest(token) {
|
|
113
|
+
return token === this.#sequence;
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
var NUMBER_BOUNDS = {
|
|
118
|
+
finite: { finite: true },
|
|
119
|
+
positive: { finite: true, exclusiveMin: 0 }};
|
|
120
|
+
function matchesNumberBounds(value, bounds) {
|
|
121
|
+
if (!Number.isFinite(value)) {
|
|
122
|
+
const direction = value === Infinity ? "positive" : value === -Infinity ? "negative" : null;
|
|
123
|
+
if (direction === null) return false;
|
|
124
|
+
if (bounds.allowInfinity !== "both" && bounds.allowInfinity !== direction) return false;
|
|
125
|
+
}
|
|
126
|
+
if (bounds.min !== void 0 && value < bounds.min) return false;
|
|
127
|
+
if (bounds.max !== void 0 && value > bounds.max) return false;
|
|
128
|
+
if (bounds.exclusiveMin !== void 0 && value <= bounds.exclusiveMin) return false;
|
|
129
|
+
if (bounds.integer && !Number.isInteger(value)) return false;
|
|
130
|
+
if (bounds.allowedValues !== void 0 && !bounds.allowedValues.includes(value)) return false;
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function readNumber(raw, fallback, bounds) {
|
|
135
|
+
return matchesNumberBounds(raw, bounds) ? raw : fallback;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
var NumberValueReader = class {
|
|
139
|
+
#lastRejected = /* @__PURE__ */ new Map();
|
|
140
|
+
read(owner, name, raw, fallback, bounds) {
|
|
141
|
+
const resolved = readNumber(raw, fallback, bounds);
|
|
142
|
+
if (matchesNumberBounds(raw, bounds)) {
|
|
143
|
+
this.#lastRejected.delete(name);
|
|
144
|
+
return resolved;
|
|
145
|
+
}
|
|
146
|
+
const attribute = `data-${owner.identifier}-${name.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}-value`;
|
|
147
|
+
const literal = owner.element.getAttribute(attribute);
|
|
148
|
+
if (literal === null) {
|
|
149
|
+
this.#lastRejected.delete(name);
|
|
150
|
+
return resolved;
|
|
151
|
+
}
|
|
152
|
+
if (this.#lastRejected.get(name) !== literal) {
|
|
153
|
+
this.#lastRejected.set(name, literal);
|
|
154
|
+
console.warn(
|
|
155
|
+
`Stimeo UI: "${owner.identifier}" has an invalid number Value "${name}" declaration ${JSON.stringify(literal)}; using ${fallback}.`
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
return resolved;
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
|
|
65
162
|
var OwnedPointerSession = class {
|
|
66
163
|
#pointerId;
|
|
67
164
|
#owner;
|
|
@@ -81,7 +178,6 @@ var OwnedPointerSession = class {
|
|
|
81
178
|
} catch {
|
|
82
179
|
}
|
|
83
180
|
}
|
|
84
|
-
/** Releases capture and listeners, then reports the end exactly once. */
|
|
85
181
|
end() {
|
|
86
182
|
this.#finish("teardown");
|
|
87
183
|
}
|
|
@@ -106,11 +202,10 @@ var OwnedPointerSession = class {
|
|
|
106
202
|
};
|
|
107
203
|
};
|
|
108
204
|
|
|
109
|
-
// src/utils/range.ts
|
|
110
205
|
function rangeFraction(value, min, max) {
|
|
111
206
|
const span = max - min;
|
|
112
207
|
if (!(span > 0)) return 0;
|
|
113
|
-
const clamped =
|
|
208
|
+
const clamped = clampRange(value, min, max);
|
|
114
209
|
let fraction;
|
|
115
210
|
if (Number.isFinite(span)) {
|
|
116
211
|
fraction = (clamped - min) / span;
|
|
@@ -121,8 +216,10 @@ function rangeFraction(value, min, max) {
|
|
|
121
216
|
if (!Number.isFinite(fraction)) return 0;
|
|
122
217
|
return Math.min(1, Math.max(0, fraction));
|
|
123
218
|
}
|
|
219
|
+
function clampRange(value, min, max) {
|
|
220
|
+
return Math.min(max, Math.max(min, value));
|
|
221
|
+
}
|
|
124
222
|
|
|
125
|
-
// src/utils/stepped_value.ts
|
|
126
223
|
function effectiveStep(step) {
|
|
127
224
|
return Number.isFinite(step) && step > 0 ? step : 1;
|
|
128
225
|
}
|
|
@@ -228,12 +325,15 @@ function finiteFallback(min, max) {
|
|
|
228
325
|
return 0;
|
|
229
326
|
}
|
|
230
327
|
|
|
231
|
-
// src/controllers/range_slider_controller.ts
|
|
232
328
|
var START_PROPERTY = "--stimeo--range-slider-start";
|
|
233
329
|
var END_PROPERTY = "--stimeo--range-slider-end";
|
|
234
330
|
var DEFAULT_MIN = 0;
|
|
235
331
|
var DEFAULT_MAX = 100;
|
|
236
|
-
var RangeSliderController = class extends Controller {
|
|
332
|
+
var RangeSliderController = class _RangeSliderController extends Controller {
|
|
333
|
+
#numbers = new NumberValueReader();
|
|
334
|
+
#fieldWrites = /* @__PURE__ */ new WeakMap();
|
|
335
|
+
#moves = new MoveCounter();
|
|
336
|
+
#move = 0;
|
|
237
337
|
static targets = ["track", "startThumb", "endThumb", "startField", "endField"];
|
|
238
338
|
static values = {
|
|
239
339
|
min: { type: Number, default: DEFAULT_MIN },
|
|
@@ -243,59 +343,45 @@ var RangeSliderController = class extends Controller {
|
|
|
243
343
|
end: { type: Number, default: 100 },
|
|
244
344
|
logicalTrack: { type: Boolean, default: false }
|
|
245
345
|
};
|
|
346
|
+
static valueConstraints = {
|
|
347
|
+
min: NUMBER_BOUNDS.finite,
|
|
348
|
+
max: NUMBER_BOUNDS.finite,
|
|
349
|
+
step: NUMBER_BOUNDS.positive,
|
|
350
|
+
start: NUMBER_BOUNDS.finite,
|
|
351
|
+
end: NUMBER_BOUNDS.finite
|
|
352
|
+
};
|
|
246
353
|
static actions = ["onKeydown", "onPointerDown"];
|
|
247
354
|
static events = ["change", "reconcile"];
|
|
248
|
-
/** One initiating pointer owns each live drag and its stable target snapshot. */
|
|
249
355
|
#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
356
|
#settled = { start: DEFAULT_MIN, end: DEFAULT_MAX };
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
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
|
-
*/
|
|
357
|
+
get #directionElement() {
|
|
358
|
+
return this.hasTrackTarget ? this.trackTarget : this.element;
|
|
359
|
+
}
|
|
360
|
+
#repaint = new MorphRenderWatcher(() => this.#render());
|
|
268
361
|
connect() {
|
|
269
|
-
this.#repaint.
|
|
362
|
+
this.#repaint.observe(this.element);
|
|
270
363
|
this.#settled = this.#currentPair(this.#effectiveRange);
|
|
271
364
|
this.#render();
|
|
272
365
|
}
|
|
273
|
-
/** Cancels any active pointer drag so document listeners never leak. */
|
|
274
366
|
disconnect() {
|
|
275
|
-
this.#repaint.
|
|
367
|
+
this.#repaint.disconnect();
|
|
276
368
|
this.#endDrag();
|
|
277
369
|
}
|
|
278
|
-
/** Repaints when application code (or a Turbo morph) changes `min` at runtime. */
|
|
279
370
|
minValueChanged() {
|
|
280
371
|
this.#repaint.schedule();
|
|
281
372
|
}
|
|
282
|
-
/** Repaints when application code (or a Turbo morph) changes `max` at runtime. */
|
|
283
373
|
maxValueChanged() {
|
|
284
374
|
this.#repaint.schedule();
|
|
285
375
|
}
|
|
286
|
-
/** Repaints when application code (or a Turbo morph) changes `step` at runtime. */
|
|
287
376
|
stepValueChanged() {
|
|
288
377
|
this.#repaint.schedule();
|
|
289
378
|
}
|
|
290
|
-
/** Repaints when application code (or a Turbo morph) changes `start` at runtime. */
|
|
291
379
|
startValueChanged() {
|
|
292
380
|
this.#repaint.schedule();
|
|
293
381
|
}
|
|
294
|
-
/** Repaints when application code (or a Turbo morph) changes `end` at runtime. */
|
|
295
382
|
endValueChanged() {
|
|
296
383
|
this.#repaint.schedule();
|
|
297
384
|
}
|
|
298
|
-
/** Hydrates a replacement start thumb and restores live-drag focus ownership. */
|
|
299
385
|
startThumbTargetConnected(thumb) {
|
|
300
386
|
const range = this.#effectiveRange;
|
|
301
387
|
const pair = this.#currentPair(range);
|
|
@@ -307,11 +393,10 @@ var RangeSliderController = class extends Controller {
|
|
|
307
393
|
}
|
|
308
394
|
this.#repaint.schedule();
|
|
309
395
|
}
|
|
310
|
-
/** Drops a stale start-thumb reference without orphaning the track gesture. */
|
|
311
396
|
startThumbTargetDisconnected(thumb) {
|
|
312
397
|
if (this.#drag?.kind === "start" && this.#drag.thumb === thumb) this.#drag.thumb = null;
|
|
398
|
+
this.#repaint.schedule();
|
|
313
399
|
}
|
|
314
|
-
/** Hydrates a replacement end thumb and restores live-drag focus ownership. */
|
|
315
400
|
endThumbTargetConnected(thumb) {
|
|
316
401
|
const range = this.#effectiveRange;
|
|
317
402
|
const pair = this.#currentPair(range);
|
|
@@ -323,28 +408,32 @@ var RangeSliderController = class extends Controller {
|
|
|
323
408
|
}
|
|
324
409
|
this.#repaint.schedule();
|
|
325
410
|
}
|
|
326
|
-
/** Fills a start field inserted or replaced at runtime on the next repaint. */
|
|
327
411
|
startFieldTargetConnected() {
|
|
328
412
|
this.#repaint.schedule();
|
|
329
413
|
}
|
|
330
|
-
|
|
414
|
+
startFieldTargetDisconnected() {
|
|
415
|
+
this.#repaint.schedule();
|
|
416
|
+
}
|
|
331
417
|
endFieldTargetConnected() {
|
|
332
418
|
this.#repaint.schedule();
|
|
333
419
|
}
|
|
334
|
-
|
|
420
|
+
endFieldTargetDisconnected() {
|
|
421
|
+
this.#repaint.schedule();
|
|
422
|
+
}
|
|
335
423
|
endThumbTargetDisconnected(thumb) {
|
|
336
424
|
if (this.#drag?.kind === "end" && this.#drag.thumb === thumb) this.#drag.thumb = null;
|
|
425
|
+
this.#repaint.schedule();
|
|
337
426
|
}
|
|
338
|
-
/** Ends a gesture whose geometry target disappeared or ceased being a target. */
|
|
339
427
|
trackTargetDisconnected(track) {
|
|
340
428
|
if (this.#drag?.track === track) this.#endDrag();
|
|
341
429
|
}
|
|
342
|
-
/** Keyboard stepping for whichever thumb is focused (the action's element). */
|
|
343
430
|
onKeydown(event) {
|
|
344
431
|
if (isReservedArrowChord(event)) return;
|
|
432
|
+
if ((event.key === "Home" || event.key === "End" || event.key === "PageUp" || event.key === "PageDown") && hasModifierChord(event))
|
|
433
|
+
return;
|
|
345
434
|
const thumb = event.currentTarget;
|
|
346
|
-
const isStart = this.
|
|
347
|
-
const isEnd = this.
|
|
435
|
+
const isStart = this.startThumbTargets.includes(thumb);
|
|
436
|
+
const isEnd = this.endThumbTargets.includes(thumb);
|
|
348
437
|
const effectiveRange = this.#effectiveRange;
|
|
349
438
|
const pair = this.#currentPair(effectiveRange);
|
|
350
439
|
let kind;
|
|
@@ -371,7 +460,7 @@ var RangeSliderController = class extends Controller {
|
|
|
371
460
|
};
|
|
372
461
|
}
|
|
373
462
|
let next = null;
|
|
374
|
-
switch (this
|
|
463
|
+
switch (this.logicalTrackValue ? logicalArrowKey(event.key, this.#directionElement) : event.key) {
|
|
375
464
|
case "ArrowRight":
|
|
376
465
|
case "ArrowUp":
|
|
377
466
|
next = stepSteppedValue(current, 1, range);
|
|
@@ -398,13 +487,12 @@ var RangeSliderController = class extends Controller {
|
|
|
398
487
|
event.preventDefault();
|
|
399
488
|
this.#moveThumb(kind, next);
|
|
400
489
|
}
|
|
401
|
-
/** Begins a pointer drag on the track, moving the thumb nearest the press. */
|
|
402
490
|
onPointerDown(event) {
|
|
403
491
|
if (event.button !== 0 || this.#drag || !this.hasTrackTarget || !this.hasStartThumbTarget || !this.hasEndThumbTarget) {
|
|
404
492
|
return;
|
|
405
493
|
}
|
|
406
494
|
const track = this.trackTarget;
|
|
407
|
-
const mirrored = this
|
|
495
|
+
const mirrored = this.logicalTrackValue && isRtl(track);
|
|
408
496
|
const value = this.#valueFromClientX(event.clientX, mirrored, track);
|
|
409
497
|
if (value === null) return;
|
|
410
498
|
event.preventDefault();
|
|
@@ -431,7 +519,6 @@ var RangeSliderController = class extends Controller {
|
|
|
431
519
|
});
|
|
432
520
|
this.#drag = drag;
|
|
433
521
|
}
|
|
434
|
-
/** Maps a pointer X coordinate to a raw value using the track geometry. */
|
|
435
522
|
#valueFromClientX(clientX, mirrored, track) {
|
|
436
523
|
const rect = track.getBoundingClientRect();
|
|
437
524
|
if (rect.width === 0) return null;
|
|
@@ -440,12 +527,6 @@ var RangeSliderController = class extends Controller {
|
|
|
440
527
|
const range = this.#effectiveRange;
|
|
441
528
|
return range.min + fraction * (range.max - range.min);
|
|
442
529
|
}
|
|
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
530
|
#nearestThumb(value, pair) {
|
|
450
531
|
const startDistance = Math.abs(value - pair.start);
|
|
451
532
|
const endDistance = Math.abs(value - pair.end);
|
|
@@ -453,7 +534,6 @@ var RangeSliderController = class extends Controller {
|
|
|
453
534
|
if (pair.start === pair.end && value > pair.start) return "end";
|
|
454
535
|
return "start";
|
|
455
536
|
}
|
|
456
|
-
/** Moves one thumb to a new raw value, keeping the pair ordered. */
|
|
457
537
|
#moveThumb(kind, raw) {
|
|
458
538
|
const pair = this.#currentPair(this.#effectiveRange);
|
|
459
539
|
if (kind === "start") {
|
|
@@ -462,17 +542,6 @@ var RangeSliderController = class extends Controller {
|
|
|
462
542
|
this.#commit(pair.start, raw, "end");
|
|
463
543
|
}
|
|
464
544
|
}
|
|
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
545
|
#commit(start, end, moving) {
|
|
477
546
|
const range = this.#effectiveRange;
|
|
478
547
|
let nextStart = snapSteppedValue(start, range);
|
|
@@ -484,47 +553,53 @@ var RangeSliderController = class extends Controller {
|
|
|
484
553
|
const pair = { start: nextStart, end: nextEnd };
|
|
485
554
|
const reported = this.#settled;
|
|
486
555
|
this.#settled = pair;
|
|
556
|
+
const changed = nextStart !== reported.start || nextEnd !== reported.end;
|
|
557
|
+
if (changed) this.#move = this.#moves.record();
|
|
558
|
+
const move = this.#move;
|
|
487
559
|
if (!Object.is(this.startValue, nextStart)) this.startValue = nextStart;
|
|
488
560
|
if (!Object.is(this.endValue, nextEnd)) this.endValue = nextEnd;
|
|
489
561
|
this.#renderPair(pair, range);
|
|
490
|
-
this.#mirrorFields(pair
|
|
491
|
-
|
|
562
|
+
const fields = this.#mirrorFields(pair);
|
|
563
|
+
for (const [field, value, write] of fields) {
|
|
564
|
+
if (field.value === value && this.#fieldWrites.get(field) === write) commitField(field);
|
|
565
|
+
}
|
|
566
|
+
if (!this.#moves.isLatest(move)) return;
|
|
567
|
+
if (changed) {
|
|
492
568
|
this.dispatch("change", { detail: { start: nextStart, end: nextEnd } });
|
|
493
569
|
}
|
|
494
570
|
}
|
|
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
571
|
#render() {
|
|
503
572
|
const range = this.#effectiveRange;
|
|
504
573
|
const pair = this.#currentPair(range);
|
|
505
574
|
this.#renderPair(pair, range);
|
|
506
|
-
this.#mirrorFields(pair
|
|
575
|
+
this.#mirrorFields(pair);
|
|
507
576
|
const settled = this.#settled;
|
|
508
577
|
if (pair.start === settled.start && pair.end === settled.end) return;
|
|
509
578
|
this.#settled = pair;
|
|
579
|
+
this.#move = this.#moves.record();
|
|
510
580
|
this.dispatch("reconcile", { detail: { start: pair.start, end: pair.end } });
|
|
511
581
|
}
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
#mirrorFields(pair
|
|
582
|
+
#writeField(field, value) {
|
|
583
|
+
if (!writeField(field, value)) return false;
|
|
584
|
+
this.#fieldWrites.set(field, (this.#fieldWrites.get(field) ?? 0) + 1);
|
|
585
|
+
return true;
|
|
586
|
+
}
|
|
587
|
+
#mirrorFields(pair) {
|
|
588
|
+
const fields = [];
|
|
518
589
|
if (this.hasStartFieldTarget) {
|
|
519
590
|
const field = this.startFieldTarget;
|
|
520
|
-
|
|
591
|
+
const value = String(pair.start);
|
|
592
|
+
if (this.#writeField(field, value))
|
|
593
|
+
fields.push([field, value, this.#fieldWrites.get(field) ?? 0]);
|
|
521
594
|
}
|
|
522
595
|
if (this.hasEndFieldTarget) {
|
|
523
596
|
const field = this.endFieldTarget;
|
|
524
|
-
|
|
597
|
+
const value = String(pair.end);
|
|
598
|
+
if (this.#writeField(field, value))
|
|
599
|
+
fields.push([field, value, this.#fieldWrites.get(field) ?? 0]);
|
|
525
600
|
}
|
|
601
|
+
return fields;
|
|
526
602
|
}
|
|
527
|
-
/** Reflects one normalized pair onto both thumbs and CSS properties. */
|
|
528
603
|
#renderPair(pair, range) {
|
|
529
604
|
if (this.hasStartThumbTarget) {
|
|
530
605
|
this.#renderStartThumb(this.startThumbTarget, pair, range);
|
|
@@ -534,24 +609,19 @@ var RangeSliderController = class extends Controller {
|
|
|
534
609
|
}
|
|
535
610
|
this.#renderFractions(pair, range);
|
|
536
611
|
}
|
|
537
|
-
/** Writes only changed ARIA for the lower thumb. */
|
|
538
612
|
#renderStartThumb(thumb, pair, range) {
|
|
539
613
|
this.#setAria(thumb, "aria-valuemin", range.min);
|
|
540
614
|
this.#setAria(thumb, "aria-valuemax", pair.end);
|
|
541
615
|
this.#setAria(thumb, "aria-valuenow", pair.start);
|
|
542
616
|
}
|
|
543
|
-
/** Writes only changed ARIA for the upper thumb. */
|
|
544
617
|
#renderEndThumb(thumb, pair, range) {
|
|
545
618
|
this.#setAria(thumb, "aria-valuemin", pair.start);
|
|
546
619
|
this.#setAria(thumb, "aria-valuemax", range.max);
|
|
547
620
|
this.#setAria(thumb, "aria-valuenow", pair.end);
|
|
548
621
|
}
|
|
549
|
-
/** Writes one numeric ARIA attribute only when its serialized value changed. */
|
|
550
622
|
#setAria(thumb, name, value) {
|
|
551
|
-
|
|
552
|
-
if (thumb.getAttribute(name) !== next) thumb.setAttribute(name, next);
|
|
623
|
+
setAttributeIfChanged(thumb, name, String(value));
|
|
553
624
|
}
|
|
554
|
-
/** Writes the two behavior-only fraction hooks only when they changed. */
|
|
555
625
|
#renderFractions(pair, range) {
|
|
556
626
|
const start = String(rangeFraction(pair.start, range.min, range.max));
|
|
557
627
|
const end = String(rangeFraction(pair.end, range.min, range.max));
|
|
@@ -562,33 +632,70 @@ var RangeSliderController = class extends Controller {
|
|
|
562
632
|
this.element.style.setProperty(END_PROPERTY, end);
|
|
563
633
|
}
|
|
564
634
|
}
|
|
565
|
-
/** Current normalized and ordered pair derived from live declarative Values. */
|
|
566
635
|
#currentPair(range) {
|
|
567
|
-
const rawStart =
|
|
568
|
-
const rawEnd =
|
|
636
|
+
const rawStart = this.#safeStart;
|
|
637
|
+
const rawEnd = this.#safeEnd;
|
|
569
638
|
const start = snapSteppedValue(rawStart, range);
|
|
570
639
|
const end = snapSteppedValue(rawEnd, range);
|
|
571
640
|
if (start <= end) return { start, end };
|
|
572
641
|
return { start: end, end: start };
|
|
573
642
|
}
|
|
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
643
|
get #effectiveRange() {
|
|
580
|
-
const min =
|
|
581
|
-
const authoredMax =
|
|
582
|
-
return { min, max: Math.max(min, authoredMax), step: this
|
|
644
|
+
const min = this.#safeMin;
|
|
645
|
+
const authoredMax = this.#safeMax;
|
|
646
|
+
return { min, max: Math.max(min, authoredMax), step: this.#safeStep, base: min };
|
|
583
647
|
}
|
|
584
|
-
/** Ends the current pointer session without dispatching another change. */
|
|
585
648
|
#endDrag() {
|
|
586
649
|
const drag = this.#drag;
|
|
587
650
|
this.#drag = null;
|
|
588
651
|
drag?.pointer?.end();
|
|
589
652
|
}
|
|
653
|
+
get #safeMin() {
|
|
654
|
+
return this.#numbers.read(
|
|
655
|
+
this,
|
|
656
|
+
"min",
|
|
657
|
+
this.minValue,
|
|
658
|
+
_RangeSliderController.values.min.default,
|
|
659
|
+
_RangeSliderController.valueConstraints.min
|
|
660
|
+
);
|
|
661
|
+
}
|
|
662
|
+
get #safeMax() {
|
|
663
|
+
return this.#numbers.read(
|
|
664
|
+
this,
|
|
665
|
+
"max",
|
|
666
|
+
this.maxValue,
|
|
667
|
+
_RangeSliderController.values.max.default,
|
|
668
|
+
_RangeSliderController.valueConstraints.max
|
|
669
|
+
);
|
|
670
|
+
}
|
|
671
|
+
get #safeStep() {
|
|
672
|
+
return this.#numbers.read(
|
|
673
|
+
this,
|
|
674
|
+
"step",
|
|
675
|
+
this.stepValue,
|
|
676
|
+
_RangeSliderController.values.step.default,
|
|
677
|
+
_RangeSliderController.valueConstraints.step
|
|
678
|
+
);
|
|
679
|
+
}
|
|
680
|
+
get #safeStart() {
|
|
681
|
+
return this.#numbers.read(
|
|
682
|
+
this,
|
|
683
|
+
"start",
|
|
684
|
+
this.startValue,
|
|
685
|
+
this.#effectiveRange.min,
|
|
686
|
+
_RangeSliderController.valueConstraints.start
|
|
687
|
+
);
|
|
688
|
+
}
|
|
689
|
+
get #safeEnd() {
|
|
690
|
+
return this.#numbers.read(
|
|
691
|
+
this,
|
|
692
|
+
"end",
|
|
693
|
+
this.endValue,
|
|
694
|
+
this.#effectiveRange.max,
|
|
695
|
+
_RangeSliderController.valueConstraints.end
|
|
696
|
+
);
|
|
697
|
+
}
|
|
590
698
|
};
|
|
591
699
|
|
|
592
700
|
export { RangeSliderController };
|
|
593
|
-
//# sourceMappingURL=range_slider_controller.js.map
|
|
594
701
|
//# sourceMappingURL=range_slider_controller.js.map
|