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,19 +1,47 @@
1
1
  import { Controller } from '@hotwired/stimulus';
2
2
 
3
- // src/controllers/textarea_autosize_controller.ts
3
+ var ListenerSet = class {
4
+ #abort = new AbortController();
5
+ add(target, type, handler, options) {
6
+ target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });
7
+ }
8
+ dispose() {
9
+ this.#abort.abort();
10
+ this.#abort = new AbortController();
11
+ }
12
+ };
13
+
14
+ var FontLoadWatcher = class {
15
+ #listeners = new ListenerSet();
16
+ #onSettled;
17
+ #fonts = null;
18
+ constructor(run) {
19
+ this.#onSettled = () => run();
20
+ }
21
+ observe(element) {
22
+ const fonts = element.ownerDocument.fonts ?? null;
23
+ if (fonts === this.#fonts) return;
24
+ this.disconnect();
25
+ if (!fonts) return;
26
+ this.#fonts = fonts;
27
+ this.#listeners.add(fonts, "loadingdone", this.#onSettled);
28
+ this.#listeners.add(fonts, "loadingerror", this.#onSettled);
29
+ }
30
+ disconnect() {
31
+ this.#listeners.dispose();
32
+ this.#fonts = null;
33
+ }
34
+ };
4
35
 
5
- // src/utils/layout_observer.ts
6
36
  var LayoutObserver = class {
7
37
  #callback;
8
38
  #resizeObserverFactory;
9
39
  #resizeObserver = null;
10
40
  #observingViewport = false;
11
41
  #loadContainer = null;
12
- /** Stable bound handler so add/removeEventListener target the same reference. */
13
42
  #handleViewportResize = () => {
14
43
  this.#callback();
15
44
  };
16
- /** Stable bound handler for the capture-phase `load`; see {@link observeDescendantLoads}. */
17
45
  #handleDescendantLoad = () => {
18
46
  this.#callback();
19
47
  };
@@ -21,11 +49,6 @@ var LayoutObserver = class {
21
49
  this.#callback = callback;
22
50
  this.#resizeObserverFactory = options.resizeObserverFactory ?? (typeof ResizeObserver === "undefined" ? null : (cb) => new ResizeObserver(cb));
23
51
  }
24
- /**
25
- * Starts observing an element's size. Repeated calls observe additional
26
- * elements through the same shared observer. No-ops when no
27
- * `ResizeObserver` implementation is available.
28
- */
29
52
  observe(element) {
30
53
  if (!this.#resizeObserverFactory) return;
31
54
  if (!this.#resizeObserver) {
@@ -35,47 +58,28 @@ var LayoutObserver = class {
35
58
  }
36
59
  this.#resizeObserver.observe(element);
37
60
  }
38
- /** Stops observing a single element while leaving any others in place. */
39
61
  unobserve(element) {
40
62
  this.#resizeObserver?.unobserve(element);
41
63
  }
42
- /** Starts observing viewport resizes. Idempotent: the listener is added once. */
43
64
  observeViewport() {
44
65
  if (this.#observingViewport) return;
45
66
  this.#observingViewport = true;
46
67
  window.addEventListener("resize", this.#handleViewportResize);
47
68
  }
48
- /** Stops observing viewport resizes without affecting element observation. */
49
69
  unobserveViewport() {
50
70
  if (!this.#observingViewport) return;
51
71
  this.#observingViewport = false;
52
72
  window.removeEventListener("resize", this.#handleViewportResize);
53
73
  }
54
- /**
55
- * Starts reporting a `load` from anywhere inside `container` — an image or a
56
- * frame settling changes the box it sits in, and it measures as zero high until
57
- * then. `load` does not bubble, so the subscription is a capture-phase listener
58
- * on the container itself and nothing the caller spells.
59
- *
60
- * **One container at a time.** A further call moves the observation, so a widget
61
- * whose content element is swapped at runtime releases the element it let go by
62
- * naming the new one — there is no second place for the release to drift from.
63
- */
64
74
  observeDescendantLoads(container) {
65
75
  this.unobserveDescendantLoads();
66
76
  this.#loadContainer = container;
67
77
  container.addEventListener("load", this.#handleDescendantLoad, true);
68
78
  }
69
- /** Stops reporting descendant loads without affecting element or viewport observation. */
70
79
  unobserveDescendantLoads() {
71
80
  this.#loadContainer?.removeEventListener("load", this.#handleDescendantLoad, true);
72
81
  this.#loadContainer = null;
73
82
  }
74
- /**
75
- * Releases every observation: disconnects the {@link ResizeObserver} and removes
76
- * the viewport and descendant-load listeners. Safe to call multiple times. Call
77
- * this from a controller's `disconnect()`.
78
- */
79
83
  disconnect() {
80
84
  this.#resizeObserver?.disconnect();
81
85
  this.#resizeObserver = null;
@@ -84,29 +88,70 @@ var LayoutObserver = class {
84
88
  }
85
89
  };
86
90
 
87
- // src/controllers/textarea_autosize_controller.ts
91
+ var NUMBER_BOUNDS = {
92
+ nonNegative: { finite: true, min: 0 }};
93
+ function matchesNumberBounds(value, bounds) {
94
+ if (!Number.isFinite(value)) {
95
+ const direction = value === Infinity ? "positive" : value === -Infinity ? "negative" : null;
96
+ if (direction === null) return false;
97
+ if (bounds.allowInfinity !== "both" && bounds.allowInfinity !== direction) return false;
98
+ }
99
+ if (bounds.min !== void 0 && value < bounds.min) return false;
100
+ if (bounds.max !== void 0 && value > bounds.max) return false;
101
+ if (bounds.exclusiveMin !== void 0 && value <= bounds.exclusiveMin) return false;
102
+ if (bounds.integer && !Number.isInteger(value)) return false;
103
+ if (bounds.allowedValues !== void 0 && !bounds.allowedValues.includes(value)) return false;
104
+ return true;
105
+ }
106
+
107
+ function readNumber(raw, fallback, bounds) {
108
+ return matchesNumberBounds(raw, bounds) ? raw : fallback;
109
+ }
110
+
111
+ var NumberValueReader = class {
112
+ #lastRejected = /* @__PURE__ */ new Map();
113
+ read(owner, name, raw, fallback, bounds) {
114
+ const resolved = readNumber(raw, fallback, bounds);
115
+ if (matchesNumberBounds(raw, bounds)) {
116
+ this.#lastRejected.delete(name);
117
+ return resolved;
118
+ }
119
+ const attribute = `data-${owner.identifier}-${name.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}-value`;
120
+ const literal = owner.element.getAttribute(attribute);
121
+ if (literal === null) {
122
+ this.#lastRejected.delete(name);
123
+ return resolved;
124
+ }
125
+ if (this.#lastRejected.get(name) !== literal) {
126
+ this.#lastRejected.set(name, literal);
127
+ console.warn(
128
+ `Stimeo UI: "${owner.identifier}" has an invalid number Value "${name}" declaration ${JSON.stringify(literal)}; using ${fallback}.`
129
+ );
130
+ }
131
+ return resolved;
132
+ }
133
+ };
134
+
88
135
  function px(value) {
89
136
  const n = Number.parseFloat(value);
90
137
  return Number.isNaN(n) ? 0 : n;
91
138
  }
92
- var TextareaAutosizeController = class extends Controller {
139
+ var TextareaAutosizeController = class _TextareaAutosizeController extends Controller {
140
+ #numbers = new NumberValueReader();
93
141
  static values = {
94
142
  minRows: { type: Number, default: 1 },
95
143
  maxRows: { type: Number, default: 0 }
96
144
  };
145
+ static valueConstraints = {
146
+ minRows: { finite: true, min: 1 },
147
+ maxRows: NUMBER_BOUNDS.nonNegative
148
+ };
97
149
  static actions = ["resize"];
98
150
  static events = ["resize"];
99
151
  #lastHeight = -1;
100
152
  #lastWidth = -1;
101
153
  #started = false;
102
154
  #pending = false;
103
- #fonts = null;
104
- /**
105
- * Content triggers arriving while the box is collapsed (`display: none`
106
- * ancestors) are deferred: `scrollHeight` is 0 there, so measuring would
107
- * clamp the kept height to the `minRows` floor and dispatch a bogus
108
- * `resize`. The next report from a rendered box flushes the deferral.
109
- */
110
155
  #remeasure = () => {
111
156
  if (this.element.clientWidth === 0) {
112
157
  this.#pending = true;
@@ -114,16 +159,7 @@ var TextareaAutosizeController = class extends Controller {
114
159
  }
115
160
  this.resize();
116
161
  };
117
- /**
118
- * Re-measures when the content width changed (re-wrapping, including a
119
- * horizontal padding change under `border-box`), the applied inline height
120
- * was stripped (a Turbo morph syncing attributes from server HTML that has no
121
- * `style`), or a content trigger was deferred while the box was collapsed.
122
- * This instance's own height writes match none of these signals, so they
123
- * cannot re-enter; an authored inline height (e.g. a user dragging the native
124
- * resize handle) is left alone until the next content trigger. Reports from a
125
- * still-collapsed box are skipped (nothing is measurable there).
126
- */
162
+ #fonts = new FontLoadWatcher(this.#remeasure);
127
163
  #layout = new LayoutObserver(() => {
128
164
  if (this.element.clientWidth === 0) return;
129
165
  const width = this.#contentWidth();
@@ -132,32 +168,29 @@ var TextareaAutosizeController = class extends Controller {
132
168
  this.#lastWidth = width;
133
169
  this.resize();
134
170
  });
171
+ #listeners = new ListenerSet();
135
172
  connect() {
136
173
  this.#lastHeight = Math.round(px(this.element.style.height));
137
174
  this.#lastWidth = this.#contentWidth();
138
- this.element.addEventListener("input", this.#remeasure);
139
- this.element.addEventListener("change", this.#remeasure);
175
+ this.#listeners.add(this.element, "input", this.#remeasure);
176
+ this.#listeners.add(this.element, "change", this.#remeasure);
140
177
  this.#layout.observe(this.element);
141
- this.#bindFonts();
178
+ this.#fonts.observe(this.element);
142
179
  this.#started = true;
143
180
  this.resize();
144
181
  }
145
182
  disconnect() {
146
183
  this.#started = false;
147
- this.element.removeEventListener("input", this.#remeasure);
148
- this.element.removeEventListener("change", this.#remeasure);
184
+ this.#listeners.dispose();
149
185
  this.#layout.disconnect();
150
- this.#unbindFonts();
186
+ this.#fonts.disconnect();
151
187
  }
152
- /** Re-clamps when application code or a Turbo morph changes `minRows`. */
153
188
  minRowsValueChanged() {
154
189
  if (this.#started) this.#remeasure();
155
190
  }
156
- /** Re-clamps when application code or a Turbo morph changes `maxRows`. */
157
191
  maxRowsValueChanged() {
158
192
  if (this.#started) this.#remeasure();
159
193
  }
160
- /** Re-measures the content and applies the clamped height. */
161
194
  resize() {
162
195
  this.#pending = false;
163
196
  const el = this.element;
@@ -169,10 +202,10 @@ var TextareaAutosizeController = class extends Controller {
169
202
  el.style.height = "auto";
170
203
  const contentHeight = Math.max(0, el.scrollHeight - paddingV);
171
204
  const rows = Math.max(1, Math.round(contentHeight / lineHeight));
172
- let targetContent = Math.max(contentHeight, this.minRowsValue * lineHeight);
205
+ let targetContent = Math.max(contentHeight, this.#safeMinRows * lineHeight);
173
206
  let atMax = false;
174
- if (this.maxRowsValue > 0) {
175
- const maxContent = this.maxRowsValue * lineHeight;
207
+ if (this.#safeMaxRows > 0) {
208
+ const maxContent = this.#safeMaxRows * lineHeight;
176
209
  if (targetContent > maxContent) {
177
210
  targetContent = maxContent;
178
211
  atMax = true;
@@ -193,34 +226,35 @@ var TextareaAutosizeController = class extends Controller {
193
226
  this.dispatch("resize", { detail: { height, rows } });
194
227
  }
195
228
  }
196
- /** Content-box width — the wrapping input, unaffected by this instance's height writes. */
197
229
  #contentWidth() {
198
230
  const style = window.getComputedStyle(this.element);
199
231
  return Math.max(0, this.element.clientWidth - px(style.paddingLeft) - px(style.paddingRight));
200
232
  }
201
- /** Resolved line height, falling back to ~1.2× font-size when `normal`. */
202
233
  #lineHeight(style) {
203
234
  const lh = px(style.lineHeight);
204
235
  if (lh > 0) return lh;
205
236
  const fontSize = px(style.fontSize);
206
237
  return fontSize > 0 ? fontSize * 1.2 : 16;
207
238
  }
208
- /** Subscribes to font completion when the current engine exposes `document.fonts`. */
209
- #bindFonts() {
210
- const fonts = this.element.ownerDocument.fonts;
211
- if (!fonts) return;
212
- this.#fonts = fonts;
213
- fonts.addEventListener("loadingdone", this.#remeasure);
214
- fonts.addEventListener("loadingerror", this.#remeasure);
239
+ get #safeMinRows() {
240
+ return this.#numbers.read(
241
+ this,
242
+ "minRows",
243
+ this.minRowsValue,
244
+ _TextareaAutosizeController.values.minRows.default,
245
+ _TextareaAutosizeController.valueConstraints.minRows
246
+ );
215
247
  }
216
- /** Removes the font completion subscriptions. */
217
- #unbindFonts() {
218
- this.#fonts?.removeEventListener("loadingdone", this.#remeasure);
219
- this.#fonts?.removeEventListener("loadingerror", this.#remeasure);
220
- this.#fonts = null;
248
+ get #safeMaxRows() {
249
+ return this.#numbers.read(
250
+ this,
251
+ "maxRows",
252
+ this.maxRowsValue,
253
+ _TextareaAutosizeController.values.maxRows.default,
254
+ _TextareaAutosizeController.valueConstraints.maxRows
255
+ );
221
256
  }
222
257
  };
223
258
 
224
259
  export { TextareaAutosizeController };
225
- //# sourceMappingURL=textarea_autosize_controller.js.map
226
260
  //# sourceMappingURL=textarea_autosize_controller.js.map