stimeo-ui 0.15.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +209 -0
  3. data/README.md +128 -1
  4. data/dist/cable/index.js +392 -417
  5. data/dist/controllers/accordion_controller.js +228 -28
  6. data/dist/controllers/alert_dialog_controller.js +862 -221
  7. data/dist/controllers/announcer_controller.js +135 -191
  8. data/dist/controllers/aspect_ratio_controller.js +0 -10
  9. data/dist/controllers/auto_submit_controller.js +295 -110
  10. data/dist/controllers/avatar_controller.js +172 -106
  11. data/dist/controllers/breadcrumb_controller.js +0 -129
  12. data/dist/controllers/bulk_select_controller.js +264 -53
  13. data/dist/controllers/calendar_controller.js +346 -180
  14. data/dist/controllers/carousel_controller.js +587 -300
  15. data/dist/controllers/character_counter_controller.js +271 -143
  16. data/dist/controllers/checkbox_controller.js +77 -75
  17. data/dist/controllers/clipboard_controller.js +187 -108
  18. data/dist/controllers/collapsible_controller.js +374 -108
  19. data/dist/controllers/color_picker_controller.js +183 -127
  20. data/dist/controllers/combobox_controller.js +426 -109
  21. data/dist/controllers/command_palette_controller.js +994 -350
  22. data/dist/controllers/conditional_fields_controller.js +212 -125
  23. data/dist/controllers/confirm_controller.js +939 -245
  24. data/dist/controllers/context_menu_controller.js +275 -127
  25. data/dist/controllers/count_up_controller.js +73 -21
  26. data/dist/controllers/countdown_controller.js +235 -97
  27. data/dist/controllers/currency_input_controller.js +241 -118
  28. data/dist/controllers/data_grid_controller.js +229 -135
  29. data/dist/controllers/date_range_picker_controller.js +359 -165
  30. data/dist/controllers/dialog_controller.js +887 -225
  31. data/dist/controllers/direct_upload_controller.js +80 -142
  32. data/dist/controllers/dirty_form_controller.js +26 -47
  33. data/dist/controllers/dismissible_controller.js +175 -20
  34. data/dist/controllers/drawer_controller.js +963 -336
  35. data/dist/controllers/dropdown_controller.js +342 -85
  36. data/dist/controllers/editable_controller.js +182 -58
  37. data/dist/controllers/empty_state_controller.js +67 -53
  38. data/dist/controllers/file_dropzone_controller.js +426 -229
  39. data/dist/controllers/filter_controller.js +78 -36
  40. data/dist/controllers/flash_controller.js +328 -211
  41. data/dist/controllers/focus_controller.js +654 -239
  42. data/dist/controllers/form_field_controller.js +153 -136
  43. data/dist/controllers/form_validation_controller.js +29 -96
  44. data/dist/controllers/frame_loading_controller.js +263 -198
  45. data/dist/controllers/highlight_controller.js +119 -70
  46. data/dist/controllers/hover_card_controller.js +335 -118
  47. data/dist/controllers/idle_controller.js +316 -65
  48. data/dist/controllers/input_mask_controller.js +116 -61
  49. data/dist/controllers/intersection_controller.js +147 -103
  50. data/dist/controllers/lazy_frame_controller.js +9 -56
  51. data/dist/controllers/listbox_controller.js +413 -153
  52. data/dist/controllers/local_time_controller.js +54 -64
  53. data/dist/controllers/masonry_controller.js +70 -95
  54. data/dist/controllers/menu_controller.js +254 -167
  55. data/dist/controllers/menubar_controller.js +206 -331
  56. data/dist/controllers/meter_controller.js +159 -71
  57. data/dist/controllers/multi_select_controller.js +797 -388
  58. data/dist/controllers/navigation_menu_controller.js +156 -231
  59. data/dist/controllers/nested_form_controller.js +373 -174
  60. data/dist/controllers/network_status_controller.js +241 -45
  61. data/dist/controllers/number_input_controller.js +387 -227
  62. data/dist/controllers/optimistic_controller.js +28 -75
  63. data/dist/controllers/otp_controller.js +344 -196
  64. data/dist/controllers/overflow_indicator_controller.js +167 -93
  65. data/dist/controllers/overflow_menu_controller.js +408 -277
  66. data/dist/controllers/pagination_controller.js +342 -120
  67. data/dist/controllers/password_reveal_controller.js +424 -99
  68. data/dist/controllers/password_strength_controller.js +99 -153
  69. data/dist/controllers/persist_controller.js +252 -96
  70. data/dist/controllers/pointer_drag_controller.js +263 -164
  71. data/dist/controllers/popover_controller.js +267 -94
  72. data/dist/controllers/portal_controller.js +209 -72
  73. data/dist/controllers/preview_guard_controller.js +243 -112
  74. data/dist/controllers/progress_controller.js +132 -59
  75. data/dist/controllers/radio_group_controller.js +138 -133
  76. data/dist/controllers/range_slider_controller.js +245 -112
  77. data/dist/controllers/rating_controller.js +429 -194
  78. data/dist/controllers/read_more_controller.js +185 -47
  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 +294 -118
  83. data/dist/controllers/roving_controller.js +39 -84
  84. data/dist/controllers/scroll_area_controller.js +428 -221
  85. data/dist/controllers/scroll_restore_controller.js +0 -53
  86. data/dist/controllers/scroll_visibility_controller.js +395 -146
  87. data/dist/controllers/scrollspy_controller.js +181 -236
  88. data/dist/controllers/separator_controller.js +288 -132
  89. data/dist/controllers/sidebar_controller.js +1071 -375
  90. data/dist/controllers/skeleton_controller.js +248 -92
  91. data/dist/controllers/slider_controller.js +211 -96
  92. data/dist/controllers/smart_sticky_header_controller.js +163 -54
  93. data/dist/controllers/sortable_controller.js +13 -110
  94. data/dist/controllers/spinner_controller.js +277 -161
  95. data/dist/controllers/step_indicator_controller.js +107 -53
  96. data/dist/controllers/stepper_controller.js +171 -48
  97. data/dist/controllers/stick_to_bottom_controller.js +310 -116
  98. data/dist/controllers/sticky_observer_controller.js +214 -49
  99. data/dist/controllers/submit_once_controller.js +294 -192
  100. data/dist/controllers/switch_controller.js +107 -43
  101. data/dist/controllers/tabs_controller.js +50 -25
  102. data/dist/controllers/tags_input_controller.js +290 -149
  103. data/dist/controllers/textarea_autosize_controller.js +71 -58
  104. data/dist/controllers/theme_controller.js +148 -80
  105. data/dist/controllers/time_picker_controller.js +162 -81
  106. data/dist/controllers/toast_controller.js +365 -193
  107. data/dist/controllers/toggle_group_controller.js +194 -99
  108. data/dist/controllers/toolbar_controller.js +92 -90
  109. data/dist/controllers/tooltip_controller.js +185 -125
  110. data/dist/controllers/transition_controller.js +123 -115
  111. data/dist/controllers/tree_view_controller.js +248 -290
  112. data/dist/index.js +8649 -8572
  113. data/dist/positioning/index.js +148 -45
  114. data/lib/stimeo/ui/version.rb +1 -1
  115. metadata +3 -3
@@ -1,30 +1,20 @@
1
1
  import { Controller } from '@hotwired/stimulus';
2
2
 
3
- // src/controllers/breadcrumb_controller.ts
4
-
5
- // src/utils/blur_deferral.ts
6
3
  var BlurDeferral = class {
7
- /** Elements currently holding an update back, mapped to their `blur` listener. */
8
4
  #pending = /* @__PURE__ */ new Map();
9
- /** Called after a pending element blurs and has been detached. */
10
5
  #onRelease;
11
- /** @param onRelease - Invoked once `element` actually blurs; never on `release`. */
12
6
  constructor(onRelease) {
13
7
  this.#onRelease = onRelease;
14
8
  }
15
- /** Number of elements currently holding an update back. */
16
9
  get size() {
17
10
  return this.#pending.size;
18
11
  }
19
- /** Snapshot of the pending elements, safe to iterate while releasing them. */
20
12
  get elements() {
21
13
  return [...this.#pending.keys()];
22
14
  }
23
- /** Whether `element` is currently holding an update back. */
24
15
  has(element) {
25
16
  return this.#pending.has(element);
26
17
  }
27
- /** Holds an update back until `element` blurs. Idempotent (no stacked listeners). */
28
18
  defer(element) {
29
19
  if (this.#pending.has(element)) return;
30
20
  const onBlur = () => {
@@ -34,22 +24,18 @@ var BlurDeferral = class {
34
24
  this.#pending.set(element, onBlur);
35
25
  element.addEventListener("blur", onBlur);
36
26
  }
37
- /** Defers `element` as the only pending entry, cancelling any others. */
38
27
  deferOnly(element) {
39
28
  for (const pending of this.elements) {
40
29
  if (pending !== element) this.#detach(pending);
41
30
  }
42
31
  this.defer(element);
43
32
  }
44
- /** Cancels `element`'s deferral without completing it; no-ops when not pending. */
45
33
  release(element) {
46
34
  this.#detach(element);
47
35
  }
48
- /** Cancels every deferral without completing any of them. */
49
36
  releaseAll() {
50
37
  for (const element of this.elements) this.#detach(element);
51
38
  }
52
- /** Removes the `blur` listener for `element` and forgets it. */
53
39
  #detach(element) {
54
40
  const onBlur = this.#pending.get(element);
55
41
  if (onBlur) element.removeEventListener("blur", onBlur);
@@ -57,18 +43,15 @@ var BlurDeferral = class {
57
43
  }
58
44
  };
59
45
 
60
- // src/utils/layout_observer.ts
61
46
  var LayoutObserver = class {
62
47
  #callback;
63
48
  #resizeObserverFactory;
64
49
  #resizeObserver = null;
65
50
  #observingViewport = false;
66
51
  #loadContainer = null;
67
- /** Stable bound handler so add/removeEventListener target the same reference. */
68
52
  #handleViewportResize = () => {
69
53
  this.#callback();
70
54
  };
71
- /** Stable bound handler for the capture-phase `load`; see {@link observeDescendantLoads}. */
72
55
  #handleDescendantLoad = () => {
73
56
  this.#callback();
74
57
  };
@@ -76,11 +59,6 @@ var LayoutObserver = class {
76
59
  this.#callback = callback;
77
60
  this.#resizeObserverFactory = options.resizeObserverFactory ?? (typeof ResizeObserver === "undefined" ? null : (cb) => new ResizeObserver(cb));
78
61
  }
79
- /**
80
- * Starts observing an element's size. Repeated calls observe additional
81
- * elements through the same shared observer. No-ops when no
82
- * `ResizeObserver` implementation is available.
83
- */
84
62
  observe(element) {
85
63
  if (!this.#resizeObserverFactory) return;
86
64
  if (!this.#resizeObserver) {
@@ -90,47 +68,28 @@ var LayoutObserver = class {
90
68
  }
91
69
  this.#resizeObserver.observe(element);
92
70
  }
93
- /** Stops observing a single element while leaving any others in place. */
94
71
  unobserve(element) {
95
72
  this.#resizeObserver?.unobserve(element);
96
73
  }
97
- /** Starts observing viewport resizes. Idempotent: the listener is added once. */
98
74
  observeViewport() {
99
75
  if (this.#observingViewport) return;
100
76
  this.#observingViewport = true;
101
77
  window.addEventListener("resize", this.#handleViewportResize);
102
78
  }
103
- /** Stops observing viewport resizes without affecting element observation. */
104
79
  unobserveViewport() {
105
80
  if (!this.#observingViewport) return;
106
81
  this.#observingViewport = false;
107
82
  window.removeEventListener("resize", this.#handleViewportResize);
108
83
  }
109
- /**
110
- * Starts reporting a `load` from anywhere inside `container` — an image or a
111
- * frame settling changes the box it sits in, and it measures as zero high until
112
- * then. `load` does not bubble, so the subscription is a capture-phase listener
113
- * on the container itself and nothing the caller spells.
114
- *
115
- * **One container at a time.** A further call moves the observation, so a widget
116
- * whose content element is swapped at runtime releases the element it let go by
117
- * naming the new one — there is no second place for the release to drift from.
118
- */
119
84
  observeDescendantLoads(container) {
120
85
  this.unobserveDescendantLoads();
121
86
  this.#loadContainer = container;
122
87
  container.addEventListener("load", this.#handleDescendantLoad, true);
123
88
  }
124
- /** Stops reporting descendant loads without affecting element or viewport observation. */
125
89
  unobserveDescendantLoads() {
126
90
  this.#loadContainer?.removeEventListener("load", this.#handleDescendantLoad, true);
127
91
  this.#loadContainer = null;
128
92
  }
129
- /**
130
- * Releases every observation: disconnects the {@link ResizeObserver} and removes
131
- * the viewport and descendant-load listeners. Safe to call multiple times. Call
132
- * this from a controller's `disconnect()`.
133
- */
134
93
  disconnect() {
135
94
  this.#resizeObserver?.disconnect();
136
95
  this.#resizeObserver = null;
@@ -139,30 +98,23 @@ var LayoutObserver = class {
139
98
  }
140
99
  };
141
100
 
142
- // src/controllers/breadcrumb_controller.ts
143
101
  var OVERFLOW_EPSILON = 1;
144
102
  var BreadcrumbController = class extends Controller {
145
103
  static targets = ["list", "collapsible", "ellipsis", "trigger"];
146
104
  static actions = ["toggle", "update"];
147
105
  static events = ["toggle"];
148
- /** Guards target callbacks that can fire outside the connected window. */
149
106
  #connected = false;
150
- /** Whether the trail currently overflows its container. */
151
107
  #overflowing = false;
152
- /** Whether the user has expanded the collapsed items via the disclosure. */
153
108
  #expanded = false;
154
- /** The list element currently observed (resize + mutations), if any. */
155
109
  #observedList = null;
156
110
  #listMutations = null;
157
111
  #layout = new LayoutObserver(() => this.update());
158
112
  #onListMutation = () => {
159
113
  this.update();
160
114
  };
161
- /** Holds the ellipsis hide back while it contains focus; re-renders on blur. */
162
115
  #deferredHide = new BlurDeferral(() => {
163
116
  this.#render();
164
117
  });
165
- /** Starts observing for overflow and renders the initial state. */
166
118
  connect() {
167
119
  this.#connected = true;
168
120
  this.#expanded = this.#initialExpanded();
@@ -170,7 +122,6 @@ var BreadcrumbController = class extends Controller {
170
122
  this.#syncListObservation();
171
123
  this.update();
172
124
  }
173
- /** Releases the resize/mutation observation (Turbo navigation included). */
174
125
  disconnect() {
175
126
  this.#connected = false;
176
127
  this.#deferredHide.releaseAll();
@@ -186,13 +137,6 @@ var BreadcrumbController = class extends Controller {
186
137
  collapsibleTargetConnected() {
187
138
  this.#resync();
188
139
  }
189
- /**
190
- * Re-measures when the disclosure set gains or loses a member. The set is a
191
- * precondition for collapsing at all, so it needs the same watching the list
192
- * and items get: without these callbacks a swap that breaks or completes the
193
- * set would only take effect at the next resize or list mutation, which may
194
- * never come.
195
- */
196
140
  ellipsisTargetConnected() {
197
141
  this.#resync();
198
142
  }
@@ -205,69 +149,30 @@ var BreadcrumbController = class extends Controller {
205
149
  triggerTargetDisconnected() {
206
150
  this.#resync();
207
151
  }
208
- /**
209
- * Hands `hidden` back when an element stops being a `collapsible` target.
210
- *
211
- * The marker is the source of truth for what may be collapsed, so an element
212
- * that loses it while the controller is live is an always-visible item again —
213
- * and `#render` only walks the *current* targets, so nothing else would ever
214
- * clear the `hidden` this controller put there.
215
- *
216
- * The `#connected` guard is load-bearing, not defensive. Stimulus fires this
217
- * callback for **every** target during teardown as well (`Context.disconnect()`
218
- * stops the target observer after `disconnect()` has run), and there the
219
- * collapsed DOM must survive untouched so Turbo's cache restores the trail in
220
- * the state the user left it (see the ownership note in {@link BreadcrumbController}).
221
- *
222
- * @param element - the element that just left the `collapsible` target set
223
- */
224
152
  collapsibleTargetDisconnected(element) {
225
153
  if (this.#connected) element.hidden = false;
226
154
  this.#resync();
227
155
  }
228
- /**
229
- * Expands or re-collapses the trail and dispatches `toggle`.
230
- *
231
- * No-op while the trail fits: there is nothing collapsed to reveal, and the
232
- * (hidden) trigger would otherwise keep a stale `aria-expanded="true"` that
233
- * surfaces pre-expanded the next time the trail overflows. No event is
234
- * dispatched in that case.
235
- */
236
156
  toggle() {
237
157
  if (!this.#connected || !this.#overflowing) return;
238
158
  this.#expanded = !this.#expanded;
239
159
  this.#render();
240
160
  this.dispatch("toggle", { detail: { expanded: this.#expanded } });
241
161
  }
242
- /**
243
- * Re-measures overflow and re-renders. Wired to resizes and list mutations;
244
- * exposed as an action so consumers can re-measure after a change the
245
- * observers cannot see (e.g. a web font swap that only alters text width).
246
- */
247
162
  update() {
248
163
  if (!this.#connected) return;
249
164
  this.#overflowing = this.#measureOverflow();
250
165
  if (!this.#overflowing) this.#expanded = false;
251
166
  this.#render();
252
167
  }
253
- /** Re-attaches the list observation (targets may have been swapped) and re-measures. */
254
168
  #resync() {
255
169
  if (!this.#connected) return;
256
170
  this.#syncListObservation();
257
171
  this.update();
258
172
  }
259
- /** Reads the expanded state back from its DOM home (the trigger's `aria-expanded`). */
260
173
  #initialExpanded() {
261
174
  return this.hasTriggerTarget && this.triggerTarget.getAttribute("aria-expanded") === "true";
262
175
  }
263
- /**
264
- * Points the resize + mutation observation at the current `list` target.
265
- *
266
- * The list is re-resolved rather than captured at connect so a target swapped
267
- * at runtime (Turbo Stream replacement of the `<ol>`) is observed too. Only
268
- * `childList` / `subtree` / `characterData` are watched — deliberately **not**
269
- * `attributes`, which the controller's own `hidden` writes would re-trigger.
270
- */
271
176
  #syncListObservation() {
272
177
  const next = this.hasListTarget ? this.listTarget : null;
273
178
  if (next === this.#observedList) return;
@@ -286,26 +191,6 @@ var BreadcrumbController = class extends Controller {
286
191
  this.#listMutations?.disconnect();
287
192
  this.#listMutations = null;
288
193
  }
289
- /**
290
- * Measures overflow against the **fully expanded** layout so hiding items does
291
- * not make the condition oscillate. Reveals every item, then compares the list's
292
- * own scroll width to its own client width; `#render` immediately re-applies
293
- * the correct hidden state afterward.
294
- *
295
- * Both widths are read from the list element itself (not the host `nav`) so the
296
- * check is independent of any padding/border on the host — comparing against the
297
- * host's `clientWidth` would over-report the available space by its padding and
298
- * miss real overflow in a padded container.
299
- *
300
- * With no `collapsible` items there is nothing to collapse, so the trail is
301
- * never reported as overflowing (which also keeps the disclosure out of the
302
- * tab order — a button controlling nothing).
303
- *
304
- * The full disclosure set is a precondition: collapsing without an `ellipsis`
305
- * *and* a `trigger` would hide items behind a control that does not exist, so
306
- * an incomplete set reports "not overflowing" and `#render` degrades the trail
307
- * to a plain breadcrumb. Losing layout is recoverable; losing the path is not.
308
- */
309
194
  #measureOverflow() {
310
195
  if (!this.hasListTarget || this.collapsibleTargets.length === 0) return false;
311
196
  if (!this.hasEllipsisTarget || !this.hasTriggerTarget) return false;
@@ -313,11 +198,6 @@ var BreadcrumbController = class extends Controller {
313
198
  this.ellipsisTarget.hidden = true;
314
199
  return this.listTarget.scrollWidth > this.listTarget.clientWidth + OVERFLOW_EPSILON;
315
200
  }
316
- /**
317
- * Applies the collapsed/expanded state to the items, ellipsis, and trigger.
318
- *
319
- * @stimeoRenderRoot
320
- */
321
201
  #render() {
322
202
  const collapsed = this.#overflowing && !this.#expanded;
323
203
  const showEllipsis = this.#overflowing && this.collapsibleTargets.length > 0;
@@ -330,13 +210,6 @@ var BreadcrumbController = class extends Controller {
330
210
  }
331
211
  if (rescueFocus) this.triggerTarget.focus();
332
212
  }
333
- /**
334
- * Shows or hides the ellipsis item, deferring a hide that would blur the user.
335
- *
336
- * Hiding the element that currently holds focus drops focus to `<body>`, so the
337
- * hide waits for that element's `blur` and is re-applied then (the shared
338
- * `BlurDeferral` registry).
339
- */
340
213
  #applyEllipsisVisibility(show) {
341
214
  const ellipsis = this.ellipsisTarget;
342
215
  if (show) {
@@ -353,7 +226,6 @@ var BreadcrumbController = class extends Controller {
353
226
  this.#deferredHide.releaseAll();
354
227
  ellipsis.hidden = true;
355
228
  }
356
- /** Whether `element` contains (or is) the currently focused element. */
357
229
  #holdsFocus(element) {
358
230
  const active = document.activeElement;
359
231
  return active instanceof HTMLElement && element.contains(active);
@@ -361,5 +233,4 @@ var BreadcrumbController = class extends Controller {
361
233
  };
362
234
 
363
235
  export { BreadcrumbController };
364
- //# sourceMappingURL=breadcrumb_controller.js.map
365
236
  //# sourceMappingURL=breadcrumb_controller.js.map