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.
Files changed (117) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +136 -0
  3. data/README.md +27 -11
  4. data/dist/cable/index.js +384 -449
  5. data/dist/controllers/accordion_controller.js +174 -55
  6. data/dist/controllers/alert_dialog_controller.js +903 -235
  7. data/dist/controllers/announcer_controller.js +238 -205
  8. data/dist/controllers/aspect_ratio_controller.js +0 -10
  9. data/dist/controllers/auto_submit_controller.js +414 -145
  10. data/dist/controllers/avatar_controller.js +185 -107
  11. data/dist/controllers/breadcrumb_controller.js +0 -130
  12. data/dist/controllers/bulk_select_controller.js +282 -58
  13. data/dist/controllers/calendar_controller.js +228 -260
  14. data/dist/controllers/carousel_controller.js +536 -436
  15. data/dist/controllers/character_counter_controller.js +493 -207
  16. data/dist/controllers/checkbox_controller.js +38 -74
  17. data/dist/controllers/clipboard_controller.js +261 -149
  18. data/dist/controllers/collapsible_controller.js +304 -120
  19. data/dist/controllers/color_picker_controller.js +178 -146
  20. data/dist/controllers/combobox_controller.js +522 -156
  21. data/dist/controllers/command_palette_controller.js +1156 -395
  22. data/dist/controllers/conditional_fields_controller.js +300 -171
  23. data/dist/controllers/confirm_controller.js +980 -262
  24. data/dist/controllers/context_menu_controller.js +346 -153
  25. data/dist/controllers/count_up_controller.js +73 -21
  26. data/dist/controllers/countdown_controller.js +281 -158
  27. data/dist/controllers/currency_input_controller.js +575 -225
  28. data/dist/controllers/data_grid_controller.js +248 -214
  29. data/dist/controllers/date_range_picker_controller.js +338 -235
  30. data/dist/controllers/dialog_controller.js +899 -237
  31. data/dist/controllers/direct_upload_controller.js +97 -167
  32. data/dist/controllers/dirty_form_controller.js +72 -69
  33. data/dist/controllers/dismissible_controller.js +196 -24
  34. data/dist/controllers/drawer_controller.js +974 -355
  35. data/dist/controllers/dropdown_controller.js +339 -96
  36. data/dist/controllers/editable_controller.js +315 -88
  37. data/dist/controllers/empty_state_controller.js +79 -58
  38. data/dist/controllers/file_dropzone_controller.js +322 -211
  39. data/dist/controllers/filter_controller.js +77 -42
  40. data/dist/controllers/flash_controller.js +238 -347
  41. data/dist/controllers/focus_controller.js +679 -240
  42. data/dist/controllers/form_field_controller.js +156 -132
  43. data/dist/controllers/form_validation_controller.js +31 -97
  44. data/dist/controllers/frame_loading_controller.js +275 -217
  45. data/dist/controllers/highlight_controller.js +107 -75
  46. data/dist/controllers/hover_card_controller.js +327 -130
  47. data/dist/controllers/idle_controller.js +271 -101
  48. data/dist/controllers/input_mask_controller.js +483 -97
  49. data/dist/controllers/intersection_controller.js +150 -106
  50. data/dist/controllers/lazy_frame_controller.js +24 -68
  51. data/dist/controllers/listbox_controller.js +523 -240
  52. data/dist/controllers/local_time_controller.js +102 -77
  53. data/dist/controllers/masonry_controller.js +80 -96
  54. data/dist/controllers/menu_controller.js +302 -188
  55. data/dist/controllers/menubar_controller.js +232 -345
  56. data/dist/controllers/meter_controller.js +417 -87
  57. data/dist/controllers/multi_select_controller.js +785 -393
  58. data/dist/controllers/navigation_menu_controller.js +190 -250
  59. data/dist/controllers/nested_form_controller.js +396 -192
  60. data/dist/controllers/network_status_controller.js +268 -62
  61. data/dist/controllers/number_input_controller.js +610 -303
  62. data/dist/controllers/optimistic_controller.js +45 -87
  63. data/dist/controllers/otp_controller.js +658 -255
  64. data/dist/controllers/overflow_indicator_controller.js +122 -113
  65. data/dist/controllers/overflow_menu_controller.js +470 -295
  66. data/dist/controllers/pagination_controller.js +332 -123
  67. data/dist/controllers/password_reveal_controller.js +392 -121
  68. data/dist/controllers/password_strength_controller.js +111 -158
  69. data/dist/controllers/persist_controller.js +165 -111
  70. data/dist/controllers/pointer_drag_controller.js +434 -188
  71. data/dist/controllers/popover_controller.js +269 -113
  72. data/dist/controllers/portal_controller.js +215 -76
  73. data/dist/controllers/preview_guard_controller.js +243 -116
  74. data/dist/controllers/progress_controller.js +391 -78
  75. data/dist/controllers/radio_group_controller.js +278 -211
  76. data/dist/controllers/range_slider_controller.js +220 -113
  77. data/dist/controllers/rating_controller.js +458 -211
  78. data/dist/controllers/read_more_controller.js +138 -63
  79. data/dist/controllers/reading_progress_controller.js +166 -137
  80. data/dist/controllers/relative_time_controller.js +211 -112
  81. data/dist/controllers/{reset_before_cache_controller.js → reset_on_restore_controller.js} +103 -38
  82. data/dist/controllers/resizable_controller.js +299 -150
  83. data/dist/controllers/roving_controller.js +49 -88
  84. data/dist/controllers/scroll_area_controller.js +490 -246
  85. data/dist/controllers/scroll_restore_controller.js +14 -55
  86. data/dist/controllers/scroll_visibility_controller.js +432 -149
  87. data/dist/controllers/scrollspy_controller.js +193 -248
  88. data/dist/controllers/separator_controller.js +285 -142
  89. data/dist/controllers/sidebar_controller.js +1069 -404
  90. data/dist/controllers/skeleton_controller.js +259 -101
  91. data/dist/controllers/slider_controller.js +187 -86
  92. data/dist/controllers/smart_sticky_header_controller.js +173 -66
  93. data/dist/controllers/sortable_controller.js +26 -125
  94. data/dist/controllers/spinner_controller.js +288 -173
  95. data/dist/controllers/step_indicator_controller.js +105 -46
  96. data/dist/controllers/stepper_controller.js +120 -73
  97. data/dist/controllers/stick_to_bottom_controller.js +275 -140
  98. data/dist/controllers/sticky_observer_controller.js +220 -49
  99. data/dist/controllers/submit_once_controller.js +376 -227
  100. data/dist/controllers/switch_controller.js +201 -84
  101. data/dist/controllers/tabs_controller.js +41 -30
  102. data/dist/controllers/tags_input_controller.js +293 -173
  103. data/dist/controllers/textarea_autosize_controller.js +108 -74
  104. data/dist/controllers/theme_controller.js +140 -116
  105. data/dist/controllers/time_picker_controller.js +331 -127
  106. data/dist/controllers/toast_controller.js +244 -324
  107. data/dist/controllers/toggle_group_controller.js +252 -191
  108. data/dist/controllers/toolbar_controller.js +219 -120
  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 +283 -369
  112. data/dist/index.js +8573 -10410
  113. data/dist/positioning/index.js +154 -47
  114. data/lib/generators/stimeo/install/install_generator.rb +7 -2
  115. data/lib/generators/stimeo/install/templates/stimeo.js +19 -6
  116. data/lib/stimeo/ui/version.rb +1 -1
  117. 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,36 @@ 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
+ get pending() {
58
+ return this.#queued;
59
+ }
57
60
  schedule() {
58
61
  if (!this.#active || this.#queued) return;
59
62
  this.#queued = true;
@@ -66,31 +69,42 @@ var MicrotaskCoalescer = class {
66
69
  }
67
70
  };
68
71
 
69
- // src/utils/roving_tabindex.ts
72
+ var MorphRenderWatcher = class {
73
+ #listeners = new ListenerSet();
74
+ #pass;
75
+ #element = null;
76
+ #onMorph = () => this.#pass.schedule();
77
+ constructor(run) {
78
+ this.#pass = new MicrotaskCoalescer(run);
79
+ }
80
+ observe(element) {
81
+ if (this.#element === element) return;
82
+ this.disconnect();
83
+ this.#element = element;
84
+ this.#pass.activate();
85
+ this.#listeners.add(element, "turbo:morph-element", this.#onMorph);
86
+ }
87
+ schedule() {
88
+ this.#pass.schedule();
89
+ }
90
+ get pending() {
91
+ return this.#pass.pending;
92
+ }
93
+ disconnect() {
94
+ this.#listeners.dispose();
95
+ this.#pass.cancel();
96
+ this.#element = null;
97
+ }
98
+ };
99
+
70
100
  var RovingTabindex = class {
71
- /** Returns the current ordered item elements; called on every operation. */
72
101
  #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
102
  constructor(getItems) {
78
103
  this.#getItems = getItems;
79
104
  }
80
- /** Index of the currently tabbable item (`tabindex="0"`), or `-1` if none. */
81
105
  get activeIndex() {
82
106
  return this.#getItems().findIndex((item) => item.tabIndex === 0);
83
107
  }
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
108
  setActive(index, options = {}) {
95
109
  const { focus = false } = options;
96
110
  const items = options.items ?? this.#getItems();
@@ -107,7 +121,6 @@ function rovingMove(current, length, delta, wrap) {
107
121
  return Math.min(length - 1, Math.max(0, next));
108
122
  }
109
123
 
110
- // src/controllers/roving_controller.ts
111
124
  var STATE_ATTRIBUTES = ["disabled", "hidden"];
112
125
  var RovingController = class extends Controller {
113
126
  static targets = ["item"];
@@ -118,36 +131,33 @@ var RovingController = class extends Controller {
118
131
  };
119
132
  static events = ["change"];
120
133
  #roving = new RovingTabindex(() => this.itemTargets);
121
- #reconcile = new MicrotaskCoalescer(() => this.#ensureTabStop(true));
134
+ #reconcile = new MorphRenderWatcher(() => this.#ensureTabStop(true));
122
135
  #connected = false;
123
136
  #observer = null;
137
+ #listeners = new ListenerSet();
124
138
  connect() {
125
139
  this.#ensureTabStop(false);
126
- this.element.addEventListener("keydown", this.#onKeydown);
127
- this.element.addEventListener("focusin", this.#onFocusin);
140
+ this.#listeners.add(this.element, "keydown", this.#onKeydown);
141
+ this.#listeners.add(this.element, "focusin", this.#onFocusin);
128
142
  this.#watchState();
129
143
  this.#connected = true;
130
- this.#reconcile.activate();
144
+ this.#reconcile.observe(this.element);
131
145
  }
132
146
  disconnect() {
133
147
  this.#connected = false;
134
- this.#reconcile.cancel();
135
- this.element.removeEventListener("keydown", this.#onKeydown);
136
- this.element.removeEventListener("focusin", this.#onFocusin);
148
+ this.#reconcile.disconnect();
149
+ this.#listeners.dispose();
137
150
  this.#observer?.disconnect();
138
151
  this.#observer = null;
139
152
  }
140
- /** Drops a runtime-added item from the Tab sequence before batch reconciliation. */
141
153
  itemTargetConnected(item) {
142
154
  if (!this.#connected) return;
143
155
  item.tabIndex = -1;
144
156
  this.#reconcile.schedule();
145
157
  }
146
- /** Re-establishes the single Tab stop after an item leaves the target set. */
147
158
  itemTargetDisconnected() {
148
159
  this.#reconcile.schedule();
149
160
  }
150
- /** Arrow keys move focus + the tab stop; Home/End jump to the ends. */
151
161
  #onKeydown = (event) => {
152
162
  if (event.defaultPrevented) return;
153
163
  if (isReservedArrowChord(event)) return;
@@ -176,21 +186,14 @@ var RovingController = class extends Controller {
176
186
  event.preventDefault();
177
187
  this.#activate(next, true);
178
188
  };
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
189
  #onFocusin = (event) => {
185
190
  const index = this.#indexOf(event.target);
186
191
  if (index === -1 || !this.#reachable(index)) return;
187
192
  this.#activate(index, false);
188
193
  };
189
- /** Resolves the item index owning an event target (the item or a descendant). */
190
194
  #indexOf(target) {
191
195
  return ownerIndex(this.itemTargets, target);
192
196
  }
193
- /** Makes `index` the tab stop (optionally focusing it), emitting `change` once. */
194
197
  #activate(index, focus) {
195
198
  const previous = this.#roving.activeIndex;
196
199
  this.#roving.setActive(index, { focus });
@@ -198,16 +201,6 @@ var RovingController = class extends Controller {
198
201
  this.dispatch("change", { detail: { index, item: this.itemTargets[index] } });
199
202
  }
200
203
  }
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
204
  #ensureTabStop(followFocus) {
212
205
  const items = this.itemTargets;
213
206
  const focused = followFocus ? items.findIndex((item, i) => item === document.activeElement && this.#reachable(i)) : -1;
@@ -217,15 +210,6 @@ var RovingController = class extends Controller {
217
210
  if (index === -1) return;
218
211
  this.#roving.setActive(index);
219
212
  }
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
213
  #step(current, delta, wrap) {
230
214
  const length = this.itemTargets.length;
231
215
  let index = current;
@@ -237,23 +221,15 @@ var RovingController = class extends Controller {
237
221
  }
238
222
  return this.#reachable(current) ? current : this.#firstReachable();
239
223
  }
240
- /** Index of the first reachable item, or `-1`. */
241
224
  #firstReachable() {
242
225
  return this.itemTargets.findIndex((_item, i) => this.#reachable(i));
243
226
  }
244
- /** Index of the last reachable item, or `-1`. */
245
227
  #lastReachable() {
246
228
  for (let i = this.itemTargets.length - 1; i >= 0; i -= 1) {
247
229
  if (this.#reachable(i)) return i;
248
230
  }
249
231
  return -1;
250
232
  }
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
233
  #reachable(index) {
258
234
  const item = this.itemTargets[index];
259
235
  if (!item) return false;
@@ -262,14 +238,6 @@ var RovingController = class extends Controller {
262
238
  if (item.disabled) return false;
263
239
  return !inheritsFieldsetDisabled(item);
264
240
  }
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
241
  #hidden(item) {
274
242
  let node = item;
275
243
  while (node && node !== this.element) {
@@ -278,12 +246,6 @@ var RovingController = class extends Controller {
278
246
  }
279
247
  return false;
280
248
  }
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
249
  #watchState() {
288
250
  if (typeof MutationObserver === "undefined") return;
289
251
  const observer = new MutationObserver(() => this.#reconcile.schedule());
@@ -300,5 +262,4 @@ var RovingController = class extends Controller {
300
262
  };
301
263
 
302
264
  export { RovingController };
303
- //# sourceMappingURL=roving_controller.js.map
304
265
  //# sourceMappingURL=roving_controller.js.map