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,13 +1,9 @@
1
1
  import { Controller } from '@hotwired/stimulus';
2
2
 
3
- // src/controllers/roving_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 isReservedArrowChord(event, allow = []) {
12
8
  if (!event.key.startsWith("Arrow")) return false;
13
9
  return event.altKey && !allow.includes("alt") || event.ctrlKey && !allow.includes("ctrl") || event.metaKey && !allow.includes("meta") || event.shiftKey && !allow.includes("shift");
@@ -16,13 +12,11 @@ function hasModifierChord(event) {
16
12
  return event.altKey || event.ctrlKey || event.metaKey || event.shiftKey;
17
13
  }
18
14
 
19
- // src/utils/event_owner.ts
20
15
  function ownerIndex(candidates, node) {
21
16
  if (!(node instanceof Node)) return -1;
22
17
  return candidates.findIndex((candidate) => candidate.contains(node));
23
18
  }
24
19
 
25
- // src/utils/focus_candidate.ts
26
20
  function inheritsFieldsetDisabled(control) {
27
21
  let fieldset = control.closest("fieldset[disabled]");
28
22
  while (fieldset) {
@@ -33,27 +27,33 @@ function inheritsFieldsetDisabled(control) {
33
27
  return false;
34
28
  }
35
29
 
36
- // src/utils/microtask_coalescer.ts
30
+ var ListenerSet = class {
31
+ #abort = new AbortController();
32
+ add(target, type, handler, options) {
33
+ target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });
34
+ }
35
+ dispose() {
36
+ this.#abort.abort();
37
+ this.#abort = new AbortController();
38
+ }
39
+ };
40
+
37
41
  var MicrotaskCoalescer = class {
38
42
  #run;
39
43
  #queued = false;
40
44
  #active = false;
41
45
  #generation = 0;
42
- /** @param run - the single reconciliation pass, invoked at most once per batch. */
43
46
  constructor(run) {
44
47
  this.#run = run;
45
48
  }
46
- /** Opens the window in which {@link schedule} is honoured; call from `connect()`. */
47
49
  activate() {
48
50
  this.#active = true;
49
51
  }
50
- /** Closes the window and drops any pending pass; call from `disconnect()`. */
51
52
  cancel() {
52
53
  this.#active = false;
53
54
  this.#queued = false;
54
55
  this.#generation += 1;
55
56
  }
56
- /** Requests one pass after the batch settles. Idempotent; inert outside the window. */
57
57
  schedule() {
58
58
  if (!this.#active || this.#queued) return;
59
59
  this.#queued = true;
@@ -66,31 +66,39 @@ var MicrotaskCoalescer = class {
66
66
  }
67
67
  };
68
68
 
69
- // src/utils/roving_tabindex.ts
69
+ var MorphRenderWatcher = class {
70
+ #listeners = new ListenerSet();
71
+ #pass;
72
+ #element = null;
73
+ #onMorph = () => this.#pass.schedule();
74
+ constructor(run) {
75
+ this.#pass = new MicrotaskCoalescer(run);
76
+ }
77
+ observe(element) {
78
+ if (this.#element === element) return;
79
+ this.disconnect();
80
+ this.#element = element;
81
+ this.#pass.activate();
82
+ this.#listeners.add(element, "turbo:morph-element", this.#onMorph);
83
+ }
84
+ schedule() {
85
+ this.#pass.schedule();
86
+ }
87
+ disconnect() {
88
+ this.#listeners.dispose();
89
+ this.#pass.cancel();
90
+ this.#element = null;
91
+ }
92
+ };
93
+
70
94
  var RovingTabindex = class {
71
- /** Returns the current ordered item elements; called on every operation. */
72
95
  #getItems;
73
- /**
74
- * @param getItems - Returns the current ordered item elements. Called on every
75
- * operation so the live target list is always used.
76
- */
77
96
  constructor(getItems) {
78
97
  this.#getItems = getItems;
79
98
  }
80
- /** Index of the currently tabbable item (`tabindex="0"`), or `-1` if none. */
81
99
  get activeIndex() {
82
100
  return this.#getItems().findIndex((item) => item.tabIndex === 0);
83
101
  }
84
- /**
85
- * Makes exactly the item at `index` tabbable (`tabindex="0"`) and removes every
86
- * other item from the Tab sequence (`tabindex="-1"`). An out-of-range `index`
87
- * (e.g. `-1`) leaves all items at `-1`, which a controller can use to express
88
- * "nothing is currently tabbable".
89
- *
90
- * @param index - Position of the item to make tabbable.
91
- * @param options - Pass `{ focus: true }` to also move DOM focus to that item,
92
- * and `items` to reuse an event-scoped collection snapshot.
93
- */
94
102
  setActive(index, options = {}) {
95
103
  const { focus = false } = options;
96
104
  const items = options.items ?? this.#getItems();
@@ -107,7 +115,6 @@ function rovingMove(current, length, delta, wrap) {
107
115
  return Math.min(length - 1, Math.max(0, next));
108
116
  }
109
117
 
110
- // src/controllers/roving_controller.ts
111
118
  var STATE_ATTRIBUTES = ["disabled", "hidden"];
112
119
  var RovingController = class extends Controller {
113
120
  static targets = ["item"];
@@ -118,7 +125,7 @@ var RovingController = class extends Controller {
118
125
  };
119
126
  static events = ["change"];
120
127
  #roving = new RovingTabindex(() => this.itemTargets);
121
- #reconcile = new MicrotaskCoalescer(() => this.#ensureTabStop(true));
128
+ #reconcile = new MorphRenderWatcher(() => this.#ensureTabStop(true));
122
129
  #connected = false;
123
130
  #observer = null;
124
131
  connect() {
@@ -127,27 +134,24 @@ var RovingController = class extends Controller {
127
134
  this.element.addEventListener("focusin", this.#onFocusin);
128
135
  this.#watchState();
129
136
  this.#connected = true;
130
- this.#reconcile.activate();
137
+ this.#reconcile.observe(this.element);
131
138
  }
132
139
  disconnect() {
133
140
  this.#connected = false;
134
- this.#reconcile.cancel();
141
+ this.#reconcile.disconnect();
135
142
  this.element.removeEventListener("keydown", this.#onKeydown);
136
143
  this.element.removeEventListener("focusin", this.#onFocusin);
137
144
  this.#observer?.disconnect();
138
145
  this.#observer = null;
139
146
  }
140
- /** Drops a runtime-added item from the Tab sequence before batch reconciliation. */
141
147
  itemTargetConnected(item) {
142
148
  if (!this.#connected) return;
143
149
  item.tabIndex = -1;
144
150
  this.#reconcile.schedule();
145
151
  }
146
- /** Re-establishes the single Tab stop after an item leaves the target set. */
147
152
  itemTargetDisconnected() {
148
153
  this.#reconcile.schedule();
149
154
  }
150
- /** Arrow keys move focus + the tab stop; Home/End jump to the ends. */
151
155
  #onKeydown = (event) => {
152
156
  if (event.defaultPrevented) return;
153
157
  if (isReservedArrowChord(event)) return;
@@ -176,21 +180,14 @@ var RovingController = class extends Controller {
176
180
  event.preventDefault();
177
181
  this.#activate(next, true);
178
182
  };
179
- /**
180
- * Syncs the single tab stop to an item that received focus by other means
181
- * (click, programmatic `focus()`), so returning via Tab lands on it. The
182
- * keyboard path's own `focus()` re-enters here but is a no-op (index unchanged).
183
- */
184
183
  #onFocusin = (event) => {
185
184
  const index = this.#indexOf(event.target);
186
185
  if (index === -1 || !this.#reachable(index)) return;
187
186
  this.#activate(index, false);
188
187
  };
189
- /** Resolves the item index owning an event target (the item or a descendant). */
190
188
  #indexOf(target) {
191
189
  return ownerIndex(this.itemTargets, target);
192
190
  }
193
- /** Makes `index` the tab stop (optionally focusing it), emitting `change` once. */
194
191
  #activate(index, focus) {
195
192
  const previous = this.#roving.activeIndex;
196
193
  this.#roving.setActive(index, { focus });
@@ -198,16 +195,6 @@ var RovingController = class extends Controller {
198
195
  this.dispatch("change", { detail: { index, item: this.itemTargets[index] } });
199
196
  }
200
197
  }
201
- /**
202
- * Keeps the Tab stop on a reachable item.
203
- *
204
- * `followFocus` is on for re-establishment only: an item added and focused in
205
- * the same task has already claimed the stop through `focusin`, and the batch
206
- * that follows must not hand it back to the first item. `connect()` passes it
207
- * off so the authored DOM decides the initial stop. With nothing reachable the
208
- * DOM is left as it is — a group inside a collapsed region gets its stop back
209
- * when the region opens, instead of losing it for good.
210
- */
211
198
  #ensureTabStop(followFocus) {
212
199
  const items = this.itemTargets;
213
200
  const focused = followFocus ? items.findIndex((item, i) => item === document.activeElement && this.#reachable(i)) : -1;
@@ -217,15 +204,6 @@ var RovingController = class extends Controller {
217
204
  if (index === -1) return;
218
205
  this.#roving.setActive(index);
219
206
  }
220
- /**
221
- * Resolves the next reachable index in `delta`'s direction, honouring `wrap`.
222
- *
223
- * An arrow on the widget's own axis is the widget's to consume even when the
224
- * position does not change, so a clamped end resolves to the current item
225
- * rather than to nothing. An unreachable origin escapes to the first reachable
226
- * item instead of sitting in a dead end, and `-1` is left for the one case that
227
- * really is not ours: no reachable item anywhere.
228
- */
229
207
  #step(current, delta, wrap) {
230
208
  const length = this.itemTargets.length;
231
209
  let index = current;
@@ -237,23 +215,15 @@ var RovingController = class extends Controller {
237
215
  }
238
216
  return this.#reachable(current) ? current : this.#firstReachable();
239
217
  }
240
- /** Index of the first reachable item, or `-1`. */
241
218
  #firstReachable() {
242
219
  return this.itemTargets.findIndex((_item, i) => this.#reachable(i));
243
220
  }
244
- /** Index of the last reachable item, or `-1`. */
245
221
  #lastReachable() {
246
222
  for (let i = this.itemTargets.length - 1; i >= 0; i -= 1) {
247
223
  if (this.#reachable(i)) return i;
248
224
  }
249
225
  return -1;
250
226
  }
251
- /**
252
- * Whether the item at `index` can hold the Tab stop and take focus.
253
- *
254
- * `aria-disabled` is deliberately not consulted: it keeps an item reachable and
255
- * only suppresses activation, which this part does not own.
256
- */
257
227
  #reachable(index) {
258
228
  const item = this.itemTargets[index];
259
229
  if (!item) return false;
@@ -262,14 +232,6 @@ var RovingController = class extends Controller {
262
232
  if (item.disabled) return false;
263
233
  return !inheritsFieldsetDisabled(item);
264
234
  }
265
- /**
266
- * Whether `item`, or anything between it and the container, is `hidden`.
267
- *
268
- * The walk stops at the container on purpose: a group inside a hidden region is
269
- * already out of the page's Tab order, and calling every item unreachable there
270
- * would drop the stop with nothing left to restore it — the ancestor lies
271
- * outside the subtree whose state attributes are watched.
272
- */
273
235
  #hidden(item) {
274
236
  let node = item;
275
237
  while (node && node !== this.element) {
@@ -278,12 +240,6 @@ var RovingController = class extends Controller {
278
240
  }
279
241
  return false;
280
242
  }
281
- /**
282
- * Watches the attributes that decide reachability, so disabling the item that
283
- * holds the Tab stop hands it to another one instead of taking the whole set
284
- * out of the Tab sequence. An enclosing `fieldset` disables items from outside
285
- * the observed subtree, so each one's own `disabled` is watched too.
286
- */
287
243
  #watchState() {
288
244
  if (typeof MutationObserver === "undefined") return;
289
245
  const observer = new MutationObserver(() => this.#reconcile.schedule());
@@ -300,5 +256,4 @@ var RovingController = class extends Controller {
300
256
  };
301
257
 
302
258
  export { RovingController };
303
- //# sourceMappingURL=roving_controller.js.map
304
259
  //# sourceMappingURL=roving_controller.js.map