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.
Files changed (115) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +31 -0
  3. data/README.md +8 -1
  4. data/dist/cable/index.js +363 -436
  5. data/dist/controllers/accordion_controller.js +166 -55
  6. data/dist/controllers/alert_dialog_controller.js +862 -221
  7. data/dist/controllers/announcer_controller.js +135 -198
  8. data/dist/controllers/aspect_ratio_controller.js +0 -10
  9. data/dist/controllers/auto_submit_controller.js +271 -127
  10. data/dist/controllers/avatar_controller.js +171 -105
  11. data/dist/controllers/breadcrumb_controller.js +0 -130
  12. data/dist/controllers/bulk_select_controller.js +262 -52
  13. data/dist/controllers/calendar_controller.js +175 -209
  14. data/dist/controllers/carousel_controller.js +461 -399
  15. data/dist/controllers/character_counter_controller.js +246 -156
  16. data/dist/controllers/checkbox_controller.js +32 -74
  17. data/dist/controllers/clipboard_controller.js +168 -147
  18. data/dist/controllers/collapsible_controller.js +301 -120
  19. data/dist/controllers/color_picker_controller.js +149 -139
  20. data/dist/controllers/combobox_controller.js +360 -133
  21. data/dist/controllers/command_palette_controller.js +970 -358
  22. data/dist/controllers/conditional_fields_controller.js +166 -124
  23. data/dist/controllers/confirm_controller.js +939 -248
  24. data/dist/controllers/context_menu_controller.js +256 -128
  25. data/dist/controllers/count_up_controller.js +73 -21
  26. data/dist/controllers/countdown_controller.js +183 -148
  27. data/dist/controllers/currency_input_controller.js +134 -158
  28. data/dist/controllers/data_grid_controller.js +114 -186
  29. data/dist/controllers/date_range_picker_controller.js +288 -206
  30. data/dist/controllers/dialog_controller.js +858 -223
  31. data/dist/controllers/direct_upload_controller.js +73 -153
  32. data/dist/controllers/dirty_form_controller.js +14 -48
  33. data/dist/controllers/dismissible_controller.js +175 -21
  34. data/dist/controllers/drawer_controller.js +933 -341
  35. data/dist/controllers/dropdown_controller.js +312 -82
  36. data/dist/controllers/editable_controller.js +158 -68
  37. data/dist/controllers/empty_state_controller.js +67 -53
  38. data/dist/controllers/file_dropzone_controller.js +311 -207
  39. data/dist/controllers/filter_controller.js +67 -39
  40. data/dist/controllers/flash_controller.js +172 -314
  41. data/dist/controllers/focus_controller.js +654 -240
  42. data/dist/controllers/form_field_controller.js +147 -132
  43. data/dist/controllers/form_validation_controller.js +13 -86
  44. data/dist/controllers/frame_loading_controller.js +264 -213
  45. data/dist/controllers/highlight_controller.js +107 -75
  46. data/dist/controllers/hover_card_controller.js +309 -118
  47. data/dist/controllers/idle_controller.js +267 -101
  48. data/dist/controllers/input_mask_controller.js +71 -72
  49. data/dist/controllers/intersection_controller.js +147 -106
  50. data/dist/controllers/lazy_frame_controller.js +9 -65
  51. data/dist/controllers/listbox_controller.js +306 -204
  52. data/dist/controllers/local_time_controller.js +54 -64
  53. data/dist/controllers/masonry_controller.js +70 -93
  54. data/dist/controllers/menu_controller.js +214 -156
  55. data/dist/controllers/menubar_controller.js +170 -329
  56. data/dist/controllers/meter_controller.js +151 -67
  57. data/dist/controllers/multi_select_controller.js +593 -358
  58. data/dist/controllers/navigation_menu_controller.js +124 -232
  59. data/dist/controllers/nested_form_controller.js +352 -182
  60. data/dist/controllers/network_status_controller.js +242 -54
  61. data/dist/controllers/number_input_controller.js +320 -263
  62. data/dist/controllers/optimistic_controller.js +16 -76
  63. data/dist/controllers/otp_controller.js +198 -193
  64. data/dist/controllers/overflow_indicator_controller.js +116 -113
  65. data/dist/controllers/overflow_menu_controller.js +343 -283
  66. data/dist/controllers/pagination_controller.js +295 -119
  67. data/dist/controllers/password_reveal_controller.js +389 -121
  68. data/dist/controllers/password_strength_controller.js +99 -153
  69. data/dist/controllers/persist_controller.js +237 -103
  70. data/dist/controllers/pointer_drag_controller.js +185 -165
  71. data/dist/controllers/popover_controller.js +236 -97
  72. data/dist/controllers/portal_controller.js +209 -76
  73. data/dist/controllers/preview_guard_controller.js +232 -116
  74. data/dist/controllers/progress_controller.js +125 -56
  75. data/dist/controllers/radio_group_controller.js +117 -137
  76. data/dist/controllers/range_slider_controller.js +192 -105
  77. data/dist/controllers/rating_controller.js +418 -207
  78. data/dist/controllers/read_more_controller.js +134 -63
  79. data/dist/controllers/reading_progress_controller.js +147 -135
  80. data/dist/controllers/relative_time_controller.js +145 -110
  81. data/dist/controllers/{reset_before_cache_controller.js → reset_on_restore_controller.js} +97 -38
  82. data/dist/controllers/resizable_controller.js +255 -139
  83. data/dist/controllers/roving_controller.js +39 -84
  84. data/dist/controllers/scroll_area_controller.js +428 -225
  85. data/dist/controllers/scroll_restore_controller.js +0 -53
  86. data/dist/controllers/scroll_visibility_controller.js +395 -145
  87. data/dist/controllers/scrollspy_controller.js +181 -242
  88. data/dist/controllers/separator_controller.js +267 -140
  89. data/dist/controllers/sidebar_controller.js +1023 -386
  90. data/dist/controllers/skeleton_controller.js +248 -97
  91. data/dist/controllers/slider_controller.js +159 -79
  92. data/dist/controllers/smart_sticky_header_controller.js +162 -63
  93. data/dist/controllers/sortable_controller.js +0 -112
  94. data/dist/controllers/spinner_controller.js +277 -169
  95. data/dist/controllers/step_indicator_controller.js +99 -46
  96. data/dist/controllers/stepper_controller.js +114 -73
  97. data/dist/controllers/stick_to_bottom_controller.js +262 -138
  98. data/dist/controllers/sticky_observer_controller.js +214 -49
  99. data/dist/controllers/submit_once_controller.js +294 -205
  100. data/dist/controllers/switch_controller.js +33 -60
  101. data/dist/controllers/tabs_controller.js +35 -29
  102. data/dist/controllers/tags_input_controller.js +136 -145
  103. data/dist/controllers/textarea_autosize_controller.js +71 -58
  104. data/dist/controllers/theme_controller.js +130 -112
  105. data/dist/controllers/time_picker_controller.js +146 -80
  106. data/dist/controllers/toast_controller.js +186 -297
  107. data/dist/controllers/toggle_group_controller.js +95 -136
  108. data/dist/controllers/toolbar_controller.js +68 -98
  109. data/dist/controllers/tooltip_controller.js +158 -124
  110. data/dist/controllers/transition_controller.js +123 -119
  111. data/dist/controllers/tree_view_controller.js +150 -345
  112. data/dist/index.js +6370 -9346
  113. data/dist/positioning/index.js +148 -47
  114. data/lib/stimeo/ui/version.rb +1 -1
  115. metadata +3 -3
@@ -1,104 +1,184 @@
1
1
  import { Controller } from '@hotwired/stimulus';
2
2
 
3
- // src/controllers/separator_controller.ts
4
-
5
- // src/utils/arrow_step.ts
6
3
  function isReservedArrowChord(event, allow = []) {
7
4
  if (!event.key.startsWith("Arrow")) return false;
8
5
  return event.altKey && !allow.includes("alt") || event.ctrlKey && !allow.includes("ctrl") || event.metaKey && !allow.includes("meta") || event.shiftKey && !allow.includes("shift");
9
6
  }
10
7
 
11
- // src/utils/attribute_lease.ts
12
- var AttributeLease = class {
8
+ function sharedRegistry(key, create) {
9
+ const symbol = Symbol.for(key);
10
+ const scope = globalThis;
11
+ const existing = scope[symbol];
12
+ if (existing !== void 0) return existing;
13
+ const registry = create();
14
+ Object.defineProperty(globalThis, symbol, { value: registry });
15
+ return registry;
16
+ }
17
+
18
+ function isAttributeValue(value) {
19
+ return value === null || typeof value === "string";
20
+ }
21
+ function readLeaseRecord(element, record) {
22
+ try {
23
+ const parsed = JSON.parse(element.getAttribute(record) ?? "null");
24
+ if (!Array.isArray(parsed)) return void 0;
25
+ const [original, written, ...rest] = parsed;
26
+ const linked = rest[0] !== false;
27
+ const beneath = linked ? rest : rest.slice(1);
28
+ if (!beneath.every((under) => typeof under === "string")) return void 0;
29
+ return isAttributeValue(original) && isAttributeValue(written) ? { original, written, linked, beneath } : void 0;
30
+ } catch {
31
+ return void 0;
32
+ }
33
+ }
34
+ var AttributeLease = class _AttributeLease {
35
+ static #registry = sharedRegistry(
36
+ "stimeo-ui.attribute-lease.registry.v1",
37
+ () => ({ stacks: /* @__PURE__ */ new WeakMap(), names: /* @__PURE__ */ new Map() })
38
+ );
13
39
  #attribute;
14
- #records = /* @__PURE__ */ new Map();
15
- /** @param attribute - The attribute whose temporary values this lease owns. */
16
- constructor(attribute) {
40
+ #record;
41
+ #base;
42
+ #held = /* @__PURE__ */ new Set();
43
+ constructor(attribute, owner, options = {}) {
17
44
  this.#attribute = attribute;
45
+ this.#record = `data-${owner}-${attribute}-lease`;
46
+ this.#base = options.base === true;
47
+ const names = _AttributeLease.#registry.names;
48
+ names.set(attribute, (names.get(attribute) ?? /* @__PURE__ */ new Set()).add(this.#record));
18
49
  }
19
- /** Writes or removes the leased attribute while preserving its authored value. */
20
50
  write(element, value) {
21
- const existing = this.#records.get(element);
22
- if (existing) {
23
- existing.written = value;
24
- } else {
25
- this.#records.set(element, {
26
- original: element.getAttribute(this.#attribute),
27
- written: value
28
- });
29
- }
30
- this.#reflect(element, value);
51
+ const stack = this.#stack(element);
52
+ const hold = this.#take(stack) ?? this.#acquire(element, stack);
53
+ this.#held.add(element);
54
+ const shows = !this.#covered(element, stack, hold);
55
+ hold.written = value;
56
+ this.#mark(element, stack);
57
+ if (shows) this.#reflect(element, value);
31
58
  }
32
- /** Returns one lease without overwriting a value subsequently authored by a consumer. */
33
59
  return(element) {
34
- const record = this.#records.get(element);
35
- if (!record) return;
36
- this.#records.delete(element);
37
- const stillOwned = element.getAttribute(this.#attribute) === record.written;
38
- if (stillOwned) this.#reflect(element, record.original);
60
+ const held = this.#held.delete(element);
61
+ const known = _AttributeLease.#registry.stacks.get(element)?.has(this.#attribute);
62
+ const stack = held || known || element.hasAttribute(this.#record) ? this.#stack(element) : [];
63
+ const hold = this.#take(stack);
64
+ if (!hold) return false;
65
+ const carries = element.getAttribute(this.#attribute) === hold.written;
66
+ const owns = carries && !this.#covered(element, stack, hold);
67
+ const index = stack.indexOf(hold);
68
+ stack.splice(index, 1);
69
+ const above = stack[index];
70
+ if (above) above.linked &&= hold.linked;
71
+ this.#mark(element, stack);
72
+ if (!owns) return true;
73
+ const below = hold.linked ? stack[index - 1] : void 0;
74
+ this.#reflect(element, below ? below.written : hold.original);
75
+ return true;
76
+ }
77
+ returnAll() {
78
+ for (const element of Array.from(this.#held)) this.return(element);
79
+ }
80
+ #stack(element) {
81
+ const stacks = _AttributeLease.#registry.stacks;
82
+ const byAttribute = stacks.get(element) ?? /* @__PURE__ */ new Map();
83
+ stacks.set(element, byAttribute);
84
+ const stack = byAttribute.get(this.#attribute) ?? this.#seed(element);
85
+ byAttribute.set(this.#attribute, stack);
86
+ return stack;
87
+ }
88
+ #seed(element) {
89
+ const found = [];
90
+ for (const record of _AttributeLease.#registry.names.get(this.#attribute) ?? []) {
91
+ const read = readLeaseRecord(element, record);
92
+ if (read) found.push({ ...read, record });
93
+ }
94
+ const depth = ({ beneath }) => beneath.filter((name) => found.some((other) => other.record === name)).length;
95
+ return found.sort((one, other) => depth(one) - depth(other)).map(({ record, written, original, linked }, index) => ({
96
+ lease: null,
97
+ record,
98
+ written,
99
+ original,
100
+ linked: linked && index > 0
101
+ }));
102
+ }
103
+ #take(stack) {
104
+ const hold = stack.find((held) => held.lease === this) ?? stack.find((held) => held.lease === null && held.record === this.#record);
105
+ if (hold) hold.lease = this;
106
+ return hold;
107
+ }
108
+ #acquire(element, stack) {
109
+ const current = element.getAttribute(this.#attribute);
110
+ const top = stack.at(-1);
111
+ const bottom = stack[0];
112
+ const over = top?.written === current ? top : void 0;
113
+ const from = this.#base ? bottom : over;
114
+ const hold = {
115
+ lease: this,
116
+ record: this.#record,
117
+ written: current,
118
+ original: from ? from.original : current,
119
+ linked: !this.#base && over !== void 0
120
+ };
121
+ if (this.#base && bottom) {
122
+ bottom.linked = true;
123
+ stack.unshift(hold);
124
+ } else stack.push(hold);
125
+ return hold;
126
+ }
127
+ #covered(element, stack, hold) {
128
+ const current = element.getAttribute(this.#attribute);
129
+ return stack.slice(stack.indexOf(hold) + 1).some((above) => above.written === current);
130
+ }
131
+ #mark(element, stack) {
132
+ const records = /* @__PURE__ */ new Map([[this.#record, null]]);
133
+ stack.forEach((hold, index) => {
134
+ const names = stack.slice(0, index).map((under) => under.record);
135
+ const beneath = [...new Set(names.reverse())].filter((name) => name !== hold.record);
136
+ const marker = hold.linked || beneath.length === 0 ? [] : [false];
137
+ records.set(
138
+ hold.record,
139
+ hold.written === hold.original && beneath.length === 0 && index === stack.length - 1 ? null : JSON.stringify([hold.original, hold.written, ...marker, ...beneath])
140
+ );
141
+ });
142
+ for (const [name, recorded] of records) {
143
+ if (element.getAttribute(name) === recorded) continue;
144
+ if (recorded === null) element.removeAttribute(name);
145
+ else element.setAttribute(name, recorded);
146
+ }
39
147
  }
40
- /** Reflects only a real value transition, avoiding self-triggered mutation work. */
41
148
  #reflect(element, value) {
42
149
  if (element.getAttribute(this.#attribute) === value) return;
43
150
  if (value === null) element.removeAttribute(this.#attribute);
44
151
  else element.setAttribute(this.#attribute, value);
45
152
  }
46
- /** Returns every outstanding lease using the same ownership check as {@link return}. */
47
- returnAll() {
48
- for (const element of Array.from(this.#records.keys())) this.return(element);
49
- }
50
153
  };
51
154
 
52
- // src/utils/before_cache_reset.ts
53
- var BeforeCacheReset = class _BeforeCacheReset {
54
- /** Every subscribed instance, iterated by the one shared document listener. */
55
- static #subscribers = /* @__PURE__ */ new Set();
56
- /** The shared listener; installed while at least one instance is subscribed. */
57
- static #onBeforeCache = () => {
58
- for (const subscriber of _BeforeCacheReset.#subscribers) subscriber.#rewind();
59
- };
60
- #rewind;
61
- /** @param rewind - the pass that returns this controller's state to its initial form. */
62
- constructor(rewind) {
63
- this.#rewind = rewind;
155
+ var ListenerSet = class {
156
+ #abort = new AbortController();
157
+ add(target, type, handler, options) {
158
+ target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });
64
159
  }
65
- /** Subscribes to `turbo:before-cache`; call from `connect()`. Idempotent. */
66
- activate() {
67
- const first = _BeforeCacheReset.#subscribers.size === 0;
68
- _BeforeCacheReset.#subscribers.add(this);
69
- if (first) {
70
- document.addEventListener("turbo:before-cache", _BeforeCacheReset.#onBeforeCache);
71
- }
72
- }
73
- /** Unsubscribes; call from `disconnect()`. Safe when never subscribed. */
74
- deactivate() {
75
- _BeforeCacheReset.#subscribers.delete(this);
76
- if (_BeforeCacheReset.#subscribers.size > 0) return;
77
- document.removeEventListener("turbo:before-cache", _BeforeCacheReset.#onBeforeCache);
160
+ dispose() {
161
+ this.#abort.abort();
162
+ this.#abort = new AbortController();
78
163
  }
79
164
  };
80
165
 
81
- // src/utils/microtask_coalescer.ts
82
166
  var MicrotaskCoalescer = class {
83
167
  #run;
84
168
  #queued = false;
85
169
  #active = false;
86
170
  #generation = 0;
87
- /** @param run - the single reconciliation pass, invoked at most once per batch. */
88
171
  constructor(run) {
89
172
  this.#run = run;
90
173
  }
91
- /** Opens the window in which {@link schedule} is honoured; call from `connect()`. */
92
174
  activate() {
93
175
  this.#active = true;
94
176
  }
95
- /** Closes the window and drops any pending pass; call from `disconnect()`. */
96
177
  cancel() {
97
178
  this.#active = false;
98
179
  this.#queued = false;
99
180
  this.#generation += 1;
100
181
  }
101
- /** Requests one pass after the batch settles. Idempotent; inert outside the window. */
102
182
  schedule() {
103
183
  if (!this.#active || this.#queued) return;
104
184
  this.#queued = true;
@@ -111,7 +191,76 @@ var MicrotaskCoalescer = class {
111
191
  }
112
192
  };
113
193
 
114
- // src/utils/stepped_value.ts
194
+ var MorphRenderWatcher = class {
195
+ #listeners = new ListenerSet();
196
+ #pass;
197
+ #element = null;
198
+ #onMorph = () => this.#pass.schedule();
199
+ constructor(run) {
200
+ this.#pass = new MicrotaskCoalescer(run);
201
+ }
202
+ observe(element) {
203
+ if (this.#element === element) return;
204
+ this.disconnect();
205
+ this.#element = element;
206
+ this.#pass.activate();
207
+ this.#listeners.add(element, "turbo:morph-element", this.#onMorph);
208
+ }
209
+ schedule() {
210
+ this.#pass.schedule();
211
+ }
212
+ disconnect() {
213
+ this.#listeners.dispose();
214
+ this.#pass.cancel();
215
+ this.#element = null;
216
+ }
217
+ };
218
+
219
+ var NUMBER_BOUNDS = {
220
+ finite: { finite: true },
221
+ positive: { finite: true, exclusiveMin: 0 }};
222
+ function matchesNumberBounds(value, bounds) {
223
+ if (!Number.isFinite(value)) {
224
+ const direction = value === Infinity ? "positive" : value === -Infinity ? "negative" : null;
225
+ if (direction === null) return false;
226
+ if (bounds.allowInfinity !== "both" && bounds.allowInfinity !== direction) return false;
227
+ }
228
+ if (bounds.min !== void 0 && value < bounds.min) return false;
229
+ if (bounds.max !== void 0 && value > bounds.max) return false;
230
+ if (bounds.exclusiveMin !== void 0 && value <= bounds.exclusiveMin) return false;
231
+ if (bounds.integer && !Number.isInteger(value)) return false;
232
+ if (bounds.allowedValues !== void 0 && !bounds.allowedValues.includes(value)) return false;
233
+ return true;
234
+ }
235
+
236
+ function readNumber(raw, fallback, bounds) {
237
+ return matchesNumberBounds(raw, bounds) ? raw : fallback;
238
+ }
239
+
240
+ var NumberValueReader = class {
241
+ #lastRejected = /* @__PURE__ */ new Map();
242
+ read(owner, name, raw, fallback, bounds) {
243
+ const resolved = readNumber(raw, fallback, bounds);
244
+ if (matchesNumberBounds(raw, bounds)) {
245
+ this.#lastRejected.delete(name);
246
+ return resolved;
247
+ }
248
+ const attribute = `data-${owner.identifier}-${name.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}-value`;
249
+ const literal = owner.element.getAttribute(attribute);
250
+ if (literal === null) {
251
+ this.#lastRejected.delete(name);
252
+ return resolved;
253
+ }
254
+ if (this.#lastRejected.get(name) !== literal) {
255
+ this.#lastRejected.set(name, literal);
256
+ console.warn(
257
+ `Stimeo UI: "${owner.identifier}" has an invalid number Value "${name}" declaration ${JSON.stringify(literal)}; using ${fallback}.`
258
+ );
259
+ }
260
+ return resolved;
261
+ }
262
+ };
263
+
115
264
  function effectiveStep(step) {
116
265
  return Number.isFinite(step) && step > 0 ? step : 1;
117
266
  }
@@ -217,10 +366,10 @@ function finiteFallback(min, max) {
217
366
  return 0;
218
367
  }
219
368
 
220
- // src/controllers/separator_controller.ts
221
369
  var DEFAULT_MIN = 0;
222
370
  var DEFAULT_MAX = 100;
223
- var SeparatorController = class extends Controller {
371
+ var SeparatorController = class _SeparatorController extends Controller {
372
+ #numbers = new NumberValueReader();
224
373
  static values = {
225
374
  orientation: { type: String, default: "horizontal" },
226
375
  focusable: { type: Boolean, default: false },
@@ -229,76 +378,55 @@ var SeparatorController = class extends Controller {
229
378
  step: { type: Number, default: 1 },
230
379
  value: { type: Number, default: DEFAULT_MIN }
231
380
  };
381
+ static valueConstraints = {
382
+ min: NUMBER_BOUNDS.finite,
383
+ max: NUMBER_BOUNDS.finite,
384
+ step: NUMBER_BOUNDS.positive,
385
+ value: NUMBER_BOUNDS.finite
386
+ };
232
387
  static actions = ["onKeydown"];
233
388
  static events = ["change", "reconcile"];
234
- #role = new AttributeLease("role");
235
- #tabindex = new AttributeLease("tabindex");
236
- #orientation = new AttributeLease("aria-orientation");
237
- #minimum = new AttributeLease("aria-valuemin");
238
- #maximum = new AttributeLease("aria-valuemax");
239
- #current = new AttributeLease("aria-valuenow");
240
- /** Collapses a morph that changes several render Values into one repaint. */
241
- #repaint = new MicrotaskCoalescer(() => this.#render());
242
- /** Restores authored semantics before Turbo snapshots the element. */
243
- #beforeCache = new BeforeCacheReset(() => this.#rewindForCache());
244
- /**
245
- * The baseline a published value is compared with: the value last published
246
- * through `aria-valuenow`, or `null` while this connection has published none.
247
- * It moves to each value a key the user presses or a repaint publishes. While
248
- * nothing is published — the separator is decorative — it keeps the last
249
- * value published, so a value that moved meanwhile is reported once when the
250
- * separator publishes again; one that connected decorative keeps `null`, so
251
- * its first published value is only taken as the baseline.
252
- */
389
+ #role = new AttributeLease("role", this.identifier);
390
+ #tabindex = new AttributeLease("tabindex", this.identifier);
391
+ #orientation = new AttributeLease("aria-orientation", this.identifier);
392
+ #minimum = new AttributeLease("aria-valuemin", this.identifier);
393
+ #maximum = new AttributeLease("aria-valuemax", this.identifier);
394
+ #current = new AttributeLease("aria-valuenow", this.identifier);
395
+ #repaint = new MorphRenderWatcher(() => this.#render());
253
396
  #settled = null;
254
- /**
255
- * Hydrates authored range ARIA into absent Values once and renders them
256
- * without writing the normalized value back. What the first render publishes
257
- * is the baseline, so connecting reports nothing.
258
- */
259
397
  connect() {
260
398
  this.#hydrateValues();
261
- this.#repaint.activate();
262
- this.#beforeCache.activate();
399
+ this.#repaint.observe(this.element);
263
400
  this.#settled = null;
264
401
  this.#render();
265
402
  }
266
- /** Cancels repaint work and returns every semantic attribute this instance controlled. */
267
403
  disconnect() {
268
- this.#repaint.cancel();
269
- this.#beforeCache.deactivate();
404
+ this.#repaint.disconnect();
270
405
  this.#returnAttributes();
271
406
  }
272
- /** Repaints an orientation changed by application code or a Turbo morph. */
273
407
  orientationValueChanged() {
274
408
  this.#repaint.schedule();
275
409
  }
276
- /** Adds or removes the focusable range semantics at runtime. */
277
410
  focusableValueChanged() {
278
411
  this.#repaint.schedule();
279
412
  }
280
- /** Repaints a minimum changed by application code or a Turbo morph. */
281
413
  minValueChanged() {
282
414
  this.#repaint.schedule();
283
415
  }
284
- /** Repaints a maximum changed by application code or a Turbo morph. */
285
416
  maxValueChanged() {
286
417
  this.#repaint.schedule();
287
418
  }
288
- /** Repaints a step changed by application code or a Turbo morph. */
289
419
  stepValueChanged() {
290
420
  this.#repaint.schedule();
291
421
  }
292
- /** Repaints a current value changed by application code or a Turbo morph. */
293
422
  valueValueChanged() {
294
423
  this.#repaint.schedule();
295
424
  }
296
- /** Adjusts the value on arrow / Home / End keys (focusable variant only). */
297
425
  onKeydown(event) {
298
426
  if (isReservedArrowChord(event) || !this.focusableValue) return;
299
427
  const horizontal = this.#effectiveOrientation === "horizontal";
300
428
  const range = this.#effectiveRange;
301
- const declared = this.valueValue;
429
+ const declared = this.#safeValue;
302
430
  let next = null;
303
431
  switch (event.key) {
304
432
  case "ArrowUp":
@@ -326,16 +454,6 @@ var SeparatorController = class extends Controller {
326
454
  event.preventDefault();
327
455
  this.#commit(next);
328
456
  }
329
- /**
330
- * Stores a normalized user value, renders immediately, and reports it as
331
- * `change` when it differs from the value last published. The comparison is
332
- * with the value last published, not with the value the key started from, so
333
- * a page write no repaint has published yet is reported once, as this change,
334
- * and a key that ends where the value was last published reports nothing. The
335
- * baseline moves before the render and the report, so neither the repaint the
336
- * Value write schedules nor a move a subscriber makes while `change` is
337
- * dispatched is measured from the value this key replaced.
338
- */
339
457
  #commit(raw) {
340
458
  const value = snapSteppedValue(raw, this.#effectiveRange);
341
459
  const reported = this.#settled;
@@ -344,20 +462,10 @@ var SeparatorController = class extends Controller {
344
462
  this.#render();
345
463
  if (value !== reported) this.dispatch("change", { detail: { value } });
346
464
  }
347
- /**
348
- * Reflects live Values into the connected element without writing them back,
349
- * then compares what it published with the baseline. A decorative separator
350
- * publishes no value (`null` here): the baseline stays the last value
351
- * published. A value is reported once as `reconcile` only when it moved from a
352
- * published baseline, so the first value a connection publishes is only taken
353
- * as the baseline.
354
- *
355
- * @stimeoRenderRoot
356
- */
357
465
  #render() {
358
466
  const range = this.#effectiveRange;
359
467
  const focusable = this.focusableValue;
360
- const published = focusable ? snapSteppedValue(this.valueValue, range) : null;
468
+ const published = focusable ? snapSteppedValue(this.#safeValue, range) : null;
361
469
  this.#role.write(this.element, "separator");
362
470
  this.#orientation.write(this.element, this.#effectiveOrientation);
363
471
  this.#tabindex.write(this.element, focusable ? "0" : null);
@@ -370,27 +478,19 @@ var SeparatorController = class extends Controller {
370
478
  this.dispatch("reconcile", { detail: { value: published } });
371
479
  }
372
480
  }
373
- /** Effective orientation; unknown strings fall back to the horizontal contract. */
374
481
  get #effectiveOrientation() {
375
482
  return this.orientationValue === "vertical" ? "vertical" : "horizontal";
376
483
  }
377
- /** Finite, ordered range shared by rendering and keyboard stepping. */
378
484
  get #effectiveRange() {
379
- const min = Number.isFinite(this.minValue) ? this.minValue : DEFAULT_MIN;
380
- const candidateMax = Number.isFinite(this.maxValue) ? this.maxValue : DEFAULT_MAX;
485
+ const min = this.#safeMin;
486
+ const candidateMax = this.#safeMax;
381
487
  return {
382
488
  min,
383
489
  max: Math.max(min, candidateMax),
384
- step: this.stepValue,
490
+ step: this.#safeStep,
385
491
  base: min
386
492
  };
387
493
  }
388
- /**
389
- * Copies valid authored ARIA into Values only when no explicit Value exists.
390
- * This fill is the one Value write `connect()` makes: it never overwrites a
391
- * declaration, and no Value callback runs it, so a declaration the page
392
- * changes later is only ever read.
393
- */
394
494
  #hydrateValues() {
395
495
  if (!this.#hasInput("orientation")) {
396
496
  const orientation = this.element.getAttribute("aria-orientation");
@@ -405,18 +505,15 @@ var SeparatorController = class extends Controller {
405
505
  const value = this.#finiteAttribute("aria-valuenow");
406
506
  if (!this.#hasInput("value") && value !== null) this.valueValue = value;
407
507
  }
408
- /** Whether the author supplied the namespaced Value instead of using its default. */
409
508
  #hasInput(name) {
410
509
  return this.element.hasAttribute(`data-${this.identifier}-${name}-value`);
411
510
  }
412
- /** Strict finite-number parser for the one-time progressive-enhancement bridge. */
413
511
  #finiteAttribute(name) {
414
512
  const raw = this.element.getAttribute(name);
415
513
  if (raw === null || raw.trim() === "") return null;
416
514
  const value = Number(raw);
417
515
  return Number.isFinite(value) ? value : null;
418
516
  }
419
- /** Returns attributes without overwriting values a consumer changed after render. */
420
517
  #returnAttributes() {
421
518
  this.#role.return(this.element);
422
519
  this.#tabindex.return(this.element);
@@ -425,13 +522,43 @@ var SeparatorController = class extends Controller {
425
522
  this.#maximum.return(this.element);
426
523
  this.#current.return(this.element);
427
524
  }
428
- /** Prevents a queued Value repaint from racing Turbo's restored snapshot. */
429
- #rewindForCache() {
430
- this.#repaint.cancel();
431
- this.#returnAttributes();
525
+ get #safeMin() {
526
+ return this.#numbers.read(
527
+ this,
528
+ "min",
529
+ this.minValue,
530
+ _SeparatorController.values.min.default,
531
+ _SeparatorController.valueConstraints.min
532
+ );
533
+ }
534
+ get #safeMax() {
535
+ return this.#numbers.read(
536
+ this,
537
+ "max",
538
+ this.maxValue,
539
+ _SeparatorController.values.max.default,
540
+ _SeparatorController.valueConstraints.max
541
+ );
542
+ }
543
+ get #safeStep() {
544
+ return this.#numbers.read(
545
+ this,
546
+ "step",
547
+ this.stepValue,
548
+ _SeparatorController.values.step.default,
549
+ _SeparatorController.valueConstraints.step
550
+ );
551
+ }
552
+ get #safeValue() {
553
+ return this.#numbers.read(
554
+ this,
555
+ "value",
556
+ this.valueValue,
557
+ this.#effectiveRange.min,
558
+ _SeparatorController.valueConstraints.value
559
+ );
432
560
  }
433
561
  };
434
562
 
435
563
  export { SeparatorController };
436
- //# sourceMappingURL=separator_controller.js.map
437
564
  //# sourceMappingURL=separator_controller.js.map