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,15 +1,11 @@
1
1
  import { Controller } from '@hotwired/stimulus';
2
2
 
3
- // src/controllers/form_validation_controller.ts
4
-
5
- // src/utils/default_attribute.ts
6
3
  function setDefaultAttribute(element, name, value) {
7
4
  if (element.hasAttribute(name)) return false;
8
5
  element.setAttribute(name, value);
9
6
  return true;
10
7
  }
11
8
 
12
- // src/utils/focus_candidate.ts
13
9
  function inheritsFieldsetDisabled(control) {
14
10
  let fieldset = control.closest("fieldset[disabled]");
15
11
  while (fieldset) {
@@ -19,8 +15,19 @@ function inheritsFieldsetDisabled(control) {
19
15
  }
20
16
  return false;
21
17
  }
18
+ function unslotted(element) {
19
+ return element.assignedSlot === null && element.parentElement?.shadowRoot != null;
20
+ }
21
+ function flatTreeParent(element) {
22
+ if (unslotted(element)) return null;
23
+ const parent = element.assignedSlot ?? element.parentNode;
24
+ if (parent instanceof ShadowRoot) return parent.host;
25
+ return parent instanceof Element ? parent : null;
26
+ }
22
27
  function canTakeFocus(element) {
23
- if (element.closest("[hidden], [inert]")) return false;
28
+ for (let node = element; node; node = flatTreeParent(node)) {
29
+ if (unslotted(node) || node.matches("[hidden], [inert]")) return false;
30
+ }
24
31
  if (element instanceof HTMLInputElement && element.type === "hidden") return false;
25
32
  if (!("disabled" in element)) return true;
26
33
  if (element.disabled) return false;
@@ -84,7 +91,6 @@ function firstTabStop(root) {
84
91
  return null;
85
92
  }
86
93
 
87
- // src/controllers/form_validation_controller.ts
88
94
  var CONSTRAINT_MESSAGE_KEYS = [
89
95
  ["valueMissing", "value-missing"],
90
96
  ["typeMismatch", "type-mismatch"],
@@ -101,11 +107,9 @@ var DISALLOW_PART = "disallow";
101
107
  var DISALLOW_WHITESPACE_DEFAULT = "Please enter a value that is not only whitespace.";
102
108
  var FORM_FIELD_INVALID_ATTR = "data-stimeo--form-field-invalid";
103
109
  var FormValidationController = class _FormValidationController extends Controller {
104
- /** The generic message override, in the namespace this controller is registered under. */
105
110
  get #messageAttribute() {
106
111
  return `data-${this.identifier}-${MESSAGE_PART}`;
107
112
  }
108
- /** Marks the `novalidate` this controller applied, so an authored one is left alone. */
109
113
  get #novalidateMarker() {
110
114
  return `data-${this.identifier}-${_FormValidationController.#NOVALIDATE_PART}`;
111
115
  }
@@ -118,22 +122,11 @@ var FormValidationController = class _FormValidationController extends Controlle
118
122
  };
119
123
  static actions = ["validate"];
120
124
  static events = ["valid", "invalid"];
121
- /** Suffix of the marker recording that we added `novalidate`, so we only remove our own. */
122
125
  static #NOVALIDATE_PART = "novalidate";
123
- /** Object-backed groups already interacted with — the input revalidation gate. */
124
126
  #touchedGroups = /* @__PURE__ */ new WeakSet();
125
- /** String-backed fallback radio groups already interacted with. */
126
127
  #touchedRadioGroups = /* @__PURE__ */ new Set();
127
- /**
128
- * Last message this controller wrote through `setCustomValidity`, kept as a
129
- * two-layer ownership ledger: a value is cleared only while the live message
130
- * still equals the recorded write. Iterable so disconnect can release every
131
- * surviving loan without touching a consumer's later custom error.
132
- */
133
128
  #ownedCustomErrors = /* @__PURE__ */ new Map();
134
- /** Last invalid message routed to each field, suppressing duplicate reports. */
135
129
  #reportedErrors = /* @__PURE__ */ new WeakMap();
136
- /** Exact document that owns the delegated listeners for this connection. */
137
130
  #listenerDocument = null;
138
131
  #onSubmit = (event) => {
139
132
  if (event.target !== this.element) return;
@@ -172,7 +165,6 @@ var FormValidationController = class _FormValidationController extends Controlle
172
165
  this.#markTouched(group.key);
173
166
  this.#applyGroup(group);
174
167
  };
175
- /** Suppresses native bubbles and binds the submit / blur / input listeners. */
176
168
  connect() {
177
169
  if (setDefaultAttribute(this.element, "novalidate", "")) {
178
170
  this.element.setAttribute(this.#novalidateMarker, "");
@@ -183,7 +175,6 @@ var FormValidationController = class _FormValidationController extends Controlle
183
175
  this.#listenerDocument.addEventListener("input", this.#onInput);
184
176
  this.#listenerDocument.addEventListener("change", this.#onChange);
185
177
  }
186
- /** Tears down listeners and restores `novalidate` if we added it. */
187
178
  disconnect() {
188
179
  this.#listenerDocument?.removeEventListener("submit", this.#onSubmit, true);
189
180
  this.#listenerDocument?.removeEventListener("focusout", this.#onFocusOut);
@@ -194,28 +185,14 @@ var FormValidationController = class _FormValidationController extends Controlle
194
185
  if (control.validationMessage === message) control.setCustomValidity("");
195
186
  }
196
187
  this.#ownedCustomErrors.clear();
197
- this.#touchedRadioGroups.clear();
198
188
  if (this.element.hasAttribute(this.#novalidateMarker)) {
199
189
  this.element.removeAttribute("novalidate");
200
190
  this.element.removeAttribute(this.#novalidateMarker);
201
191
  }
202
192
  }
203
- /**
204
- * Validates every control now, rendering or clearing each field's message, and
205
- * returns whether the whole form is valid. Marks every field/group touched so
206
- * a later input from any sibling re-validates it. Bound via `data-action`
207
- * (`#validate`) or callable directly (e.g. before a programmatic submit).
208
- */
209
193
  validate() {
210
194
  return this.#validateAll().length === 0;
211
195
  }
212
- /**
213
- * Validates every control and returns one invalid control per field. Controls
214
- * are grouped by field first (see {@link #keyFor}) so a field with several
215
- * controls — a radio group, or a mirror plus its visible control — reflects
216
- * *all* of them: a valid sibling must never clear an invalid one's message.
217
- * Each group's first invalid control supplies the message and the focus target.
218
- */
219
196
  #validateAll() {
220
197
  const invalid = [];
221
198
  for (const group of this.#snapshot().groups.values()) {
@@ -225,7 +202,6 @@ var FormValidationController = class _FormValidationController extends Controlle
225
202
  }
226
203
  return invalid;
227
204
  }
228
- /** Builds the current field table in one pass over form controls and DOM depth. */
229
205
  #snapshot() {
230
206
  const fields = this.#fieldOutlets();
231
207
  const groups = /* @__PURE__ */ new Map();
@@ -243,7 +219,6 @@ var FormValidationController = class _FormValidationController extends Controlle
243
219
  }
244
220
  return { groups, groupByControl };
245
221
  }
246
- /** Records that a whole field/group, rather than one sibling control, was visited. */
247
222
  #markTouched(key) {
248
223
  if (typeof key === "string") {
249
224
  this.#touchedRadioGroups.add(key);
@@ -254,13 +229,6 @@ var FormValidationController = class _FormValidationController extends Controlle
254
229
  #isTouched(key) {
255
230
  return typeof key === "string" ? this.#touchedRadioGroups.has(key) : this.#touchedGroups.has(key);
256
231
  }
257
- /**
258
- * Runs native constraint validation across a field's controls and routes the
259
- * result to its `stimeo--form-field` outlet: the first invalid control's
260
- * `validationMessage` is shown, an all-valid field is cleared. Returns the
261
- * first invalid control (for the invalid list / focus), or `null` when valid.
262
- * Routing goes through the outlet, so the ARIA wiring is never duplicated here.
263
- */
264
232
  #applyGroup(group) {
265
233
  for (const control of group.controls) this.#syncCustomValidity(control);
266
234
  const firstInvalid = group.controls.find((control) => !control.checkValidity()) ?? null;
@@ -277,14 +245,6 @@ var FormValidationController = class _FormValidationController extends Controlle
277
245
  }
278
246
  return firstInvalid;
279
247
  }
280
- /**
281
- * Resolves the message to show for an invalid control: a per-constraint
282
- * override (`data-stimeo--form-validation-message-<constraint>`) for the first failing
283
- * `ValidityState` flag, then a generic `data-stimeo--form-validation-message`
284
- * override, then the browser's native `validationMessage`. Authoring an override
285
- * gives controlled, localizable, theme-able wording with **no consumer JS** —
286
- * and sidesteps headless browsers that return an empty native message.
287
- */
288
248
  #messageFor(control) {
289
249
  if (control.validity.customError) return control.validationMessage;
290
250
  for (const [flag, key] of CONSTRAINT_MESSAGE_KEYS) {
@@ -294,17 +254,6 @@ var FormValidationController = class _FormValidationController extends Controlle
294
254
  }
295
255
  return control.validationMessage || this.#authoredMessage(control, this.#messageAttribute) || "";
296
256
  }
297
- /**
298
- * Applies (or clears) a declarative custom constraint via `setCustomValidity`,
299
- * for controls that opt in with `data-stimeo--form-validation-disallow`. The supported
300
- * rule is `"whitespace"` — a value that is non-empty but blank
301
- * after trimming (which slips past `required` / `minlength`); its message follows
302
- * the whitespace-specific → generic → default chain.
303
- *
304
- * Don't-clobber-authored-state: an unknown/absent rule is never written, an
305
- * existing consumer custom error wins, and a controller error is only cleared
306
- * while the live message still equals the recorded controller write.
307
- */
308
257
  #syncCustomValidity(control) {
309
258
  const recorded = this.#ownedCustomErrors.get(control);
310
259
  const ownsCurrent = recorded !== void 0 && control.validationMessage === recorded;
@@ -319,16 +268,10 @@ var FormValidationController = class _FormValidationController extends Controlle
319
268
  if (!ownsCurrent || recorded !== message) control.setCustomValidity(message);
320
269
  this.#ownedCustomErrors.set(control, message);
321
270
  }
322
- /** Returns a non-blank authored message, otherwise falls through to a fallback. */
323
271
  #authoredMessage(control, attribute) {
324
272
  const message = control.getAttribute(attribute);
325
273
  return message && message.trim().length > 0 ? message : null;
326
274
  }
327
- /**
328
- * A grouping key that collects controls belonging to the same field: the owning
329
- * `stimeo--form-field` when present, else a radio group's shared `name`, else
330
- * the control itself (always distinct).
331
- */
332
275
  #keyFor(control, field) {
333
276
  if (field) return field;
334
277
  if (control instanceof HTMLInputElement && control.type === "radio" && control.name) {
@@ -336,15 +279,6 @@ var FormValidationController = class _FormValidationController extends Controlle
336
279
  }
337
280
  return control;
338
281
  }
339
- /**
340
- * Where focus should land for an invalid control. A visible control is focused
341
- * directly — the case for native fields and radios. A validatable mirror
342
- * (the `hidden` attribute) cannot receive focus, so focus is delegated to the
343
- * visible widget: the owning field's `control` target when it is itself
344
- * focusable, else its first focusable descendant (e.g. a roving-tabindex
345
- * member). Resolved structurally — never by probing `focus()` — so behavior
346
- * is deterministic and CSS-independent.
347
- */
348
282
  #focusTargetFor(control) {
349
283
  if (canTakeFocus(control)) return control;
350
284
  const field = this.#fieldFor(control, this.#fieldOutlets());
@@ -353,7 +287,6 @@ var FormValidationController = class _FormValidationController extends Controlle
353
287
  if (isTabStop(root)) return root;
354
288
  return firstTabStop(root);
355
289
  }
356
- /** Pairs each live outlet element with its controller once per operation. */
357
290
  #fieldOutlets() {
358
291
  const fields = /* @__PURE__ */ new Map();
359
292
  const elements = this.stimeoFormFieldOutletElements;
@@ -365,7 +298,6 @@ var FormValidationController = class _FormValidationController extends Controlle
365
298
  }
366
299
  return fields;
367
300
  }
368
- /** The nearest configured field ancestor of `control`, if any. */
369
301
  #fieldFor(control, fields) {
370
302
  let ancestor = control;
371
303
  while (ancestor) {
@@ -375,7 +307,6 @@ var FormValidationController = class _FormValidationController extends Controlle
375
307
  }
376
308
  return void 0;
377
309
  }
378
- /** This form's native controls that participate in constraint validation. */
379
310
  get #controls() {
380
311
  const controls = [];
381
312
  for (const element of Array.from(this.element.elements)) {
@@ -383,18 +314,14 @@ var FormValidationController = class _FormValidationController extends Controlle
383
314
  }
384
315
  return controls;
385
316
  }
386
- /** Narrows an event target to a validatable control. */
387
317
  #controlFrom(target) {
388
318
  return target instanceof Element && this.#isValidatable(target) && target.form === this.element ? target : null;
389
319
  }
390
320
  #isValidatable(element) {
391
- return (element instanceof HTMLInputElement || element instanceof HTMLSelectElement || element instanceof HTMLTextAreaElement) && // `willValidate` already excludes disabled, read-only, input[type=hidden],
392
- // and button-type controls. The `hidden` attribute intentionally does not
393
- // bar a text mirror from constraint validation.
321
+ return (element instanceof HTMLInputElement || element instanceof HTMLSelectElement || element instanceof HTMLTextAreaElement) &&
394
322
  element.willValidate;
395
323
  }
396
324
  };
397
325
 
398
326
  export { FormValidationController };
399
- //# sourceMappingURL=form_validation_controller.js.map
400
327
  //# sourceMappingURL=form_validation_controller.js.map