stimeo-ui 0.2.1 → 0.3.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +104 -0
- data/dist/controllers/accordion_controller.js +10 -0
- data/dist/controllers/breadcrumb_controller.js +225 -13
- data/dist/controllers/calendar_controller.js +89 -22
- data/dist/controllers/carousel_controller.js +47 -6
- data/dist/controllers/collapsible_controller.js +2 -2
- data/dist/controllers/color_picker_controller.js +46 -7
- data/dist/controllers/combobox_controller.js +162 -23
- data/dist/controllers/command_palette_controller.js +194 -17
- data/dist/controllers/context_menu_controller.js +32 -10
- data/dist/controllers/data_grid_controller.js +82 -4
- data/dist/controllers/date_range_picker_controller.js +27 -3
- data/dist/controllers/editable_controller.js +1 -0
- data/dist/controllers/form_validation_controller.js +1 -1
- data/dist/controllers/intersection_controller.js +36 -11
- data/dist/controllers/lazy_frame_controller.js +31 -10
- data/dist/controllers/listbox_controller.js +257 -53
- data/dist/controllers/local_time_controller.js +2 -2
- data/dist/controllers/menu_controller.js +104 -17
- data/dist/controllers/menubar_controller.js +415 -63
- data/dist/controllers/multi_select_controller.js +312 -29
- data/dist/controllers/navigation_menu_controller.js +154 -27
- data/dist/controllers/number_input_controller.js +7 -0
- data/dist/controllers/otp_controller.js +18 -1
- data/dist/controllers/overflow_indicator_controller.js +81 -13
- data/dist/controllers/overflow_menu_controller.js +381 -57
- data/dist/controllers/pagination_controller.js +163 -32
- data/dist/controllers/persist_controller.js +6 -6
- data/dist/controllers/pointer_drag_controller.js +9 -1
- data/dist/controllers/popover_controller.js +2 -2
- data/dist/controllers/radio_group_controller.js +22 -3
- data/dist/controllers/range_slider_controller.js +32 -6
- data/dist/controllers/rating_controller.js +16 -2
- data/dist/controllers/read_more_controller.js +63 -19
- data/dist/controllers/resizable_controller.js +65 -1
- data/dist/controllers/roving_controller.js +17 -2
- data/dist/controllers/scroll_area_controller.js +86 -12
- data/dist/controllers/scroll_restore_controller.js +1 -1
- data/dist/controllers/scroll_visibility_controller.js +33 -3
- data/dist/controllers/scrollspy_controller.js +346 -73
- data/dist/controllers/separator_controller.js +9 -0
- data/dist/controllers/skeleton_controller.js +1 -1
- data/dist/controllers/slider_controller.js +32 -6
- data/dist/controllers/sortable_controller.js +34 -3
- data/dist/controllers/spinner_controller.js +1 -1
- data/dist/controllers/stick_to_bottom_controller.js +2 -1
- data/dist/controllers/sticky_observer_controller.js +32 -11
- data/dist/controllers/switch_controller.js +1 -0
- data/dist/controllers/tabs_controller.js +26 -3
- data/dist/controllers/tags_input_controller.js +22 -2
- data/dist/controllers/theme_controller.js +22 -3
- data/dist/controllers/time_picker_controller.js +20 -1
- data/dist/controllers/toast_controller.js +4 -5
- data/dist/controllers/toggle_group_controller.js +23 -2
- data/dist/controllers/toolbar_controller.js +230 -31
- data/dist/controllers/tree_view_controller.js +467 -51
- data/dist/index.js +3514 -689
- data/lib/stimeo/ui/version.rb +2 -3
- metadata +2 -2
|
@@ -2,6 +2,12 @@ import { Controller } from '@hotwired/stimulus';
|
|
|
2
2
|
|
|
3
3
|
// src/controllers/menu_controller.ts
|
|
4
4
|
|
|
5
|
+
// src/utils/arrow_step.ts
|
|
6
|
+
function isReservedArrowChord(event, allow = []) {
|
|
7
|
+
if (!event.key.startsWith("Arrow")) return false;
|
|
8
|
+
return event.altKey && !allow.includes("alt") || event.ctrlKey && !allow.includes("ctrl") || event.metaKey && !allow.includes("meta") || event.shiftKey && !allow.includes("shift");
|
|
9
|
+
}
|
|
10
|
+
|
|
5
11
|
// src/utils/escape_layer.ts
|
|
6
12
|
function claimsWhileFocusWithin(element) {
|
|
7
13
|
return () => {
|
|
@@ -159,18 +165,30 @@ var MenuController = class extends Controller {
|
|
|
159
165
|
#timers = new SafeTimeout();
|
|
160
166
|
/** Escape-stack membership while open; the shared resolver dismisses via it. */
|
|
161
167
|
#escapeLayer = new EscapeLayer();
|
|
162
|
-
/**
|
|
168
|
+
/**
|
|
169
|
+
* The item a click started on, recorded in the capture pass. Read once by the
|
|
170
|
+
* delegated bubble listener, which must not re-derive it: consumer handlers run
|
|
171
|
+
* in between and may detach the item.
|
|
172
|
+
*/
|
|
173
|
+
#clickOwner = null;
|
|
174
|
+
/** Clicks already turned into an activation, so the two paths run it once. */
|
|
175
|
+
#activated = /* @__PURE__ */ new WeakSet();
|
|
176
|
+
/** Starts closed and registers activation / outside-click listeners. */
|
|
163
177
|
connect() {
|
|
164
178
|
this.close();
|
|
165
179
|
this.element.addEventListener("click", this.#onItemClickCapture, true);
|
|
166
|
-
|
|
180
|
+
this.element.addEventListener("click", this.#onDelegatedItemClick);
|
|
181
|
+
this.element.addEventListener("keydown", this.#onDelegatedItemKeydown);
|
|
182
|
+
document.addEventListener("click", this.#onOutsideClick, true);
|
|
167
183
|
}
|
|
168
184
|
/** Releases the listeners, stack membership, and any pending Tab-close task. */
|
|
169
185
|
disconnect() {
|
|
170
186
|
this.#timers.clearAll();
|
|
171
187
|
this.#escapeLayer.deactivate();
|
|
172
188
|
this.element.removeEventListener("click", this.#onItemClickCapture, true);
|
|
173
|
-
|
|
189
|
+
this.element.removeEventListener("click", this.#onDelegatedItemClick);
|
|
190
|
+
this.element.removeEventListener("keydown", this.#onDelegatedItemKeydown);
|
|
191
|
+
document.removeEventListener("click", this.#onOutsideClick, true);
|
|
174
192
|
}
|
|
175
193
|
/** Toggles the menu open/closed. Bound via `data-action` (click). */
|
|
176
194
|
toggle() {
|
|
@@ -209,6 +227,8 @@ var MenuController = class extends Controller {
|
|
|
209
227
|
* open and then immediately re-toggle the menu.
|
|
210
228
|
*/
|
|
211
229
|
onTriggerKeydown(event) {
|
|
230
|
+
if (event.defaultPrevented) return;
|
|
231
|
+
if (isReservedArrowChord(event)) return;
|
|
212
232
|
if (event.key === "ArrowDown") {
|
|
213
233
|
event.preventDefault();
|
|
214
234
|
this.open();
|
|
@@ -221,8 +241,18 @@ var MenuController = class extends Controller {
|
|
|
221
241
|
}
|
|
222
242
|
/** Implements roving focus and closing keys inside the menu. */
|
|
223
243
|
onItemKeydown(event) {
|
|
244
|
+
this.#handleItemKeydown(event, event.currentTarget);
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* The roving-focus body, shared by the per-element action and its delegated
|
|
248
|
+
* twin. `from` is the item the key belongs to: the bound element for the
|
|
249
|
+
* action, the resolved owner of the event target for the delegate.
|
|
250
|
+
*/
|
|
251
|
+
#handleItemKeydown(event, from) {
|
|
252
|
+
if (event.defaultPrevented) return;
|
|
253
|
+
if (isReservedArrowChord(event)) return;
|
|
224
254
|
const items = this.#navigableItems;
|
|
225
|
-
const currentIndex = items.indexOf(
|
|
255
|
+
const currentIndex = items.indexOf(from);
|
|
226
256
|
switch (event.key) {
|
|
227
257
|
case "ArrowDown":
|
|
228
258
|
event.preventDefault();
|
|
@@ -246,8 +276,23 @@ var MenuController = class extends Controller {
|
|
|
246
276
|
break;
|
|
247
277
|
}
|
|
248
278
|
}
|
|
249
|
-
/**
|
|
250
|
-
|
|
279
|
+
/**
|
|
280
|
+
* Closes the menu after an item is activated. Bound via `data-action`, and
|
|
281
|
+
* also reached from the delegated listener.
|
|
282
|
+
*
|
|
283
|
+
* Markup that carries the per-element action *and* gets the delegate would run
|
|
284
|
+
* this twice for one gesture. `close()` writes the state hooks (`hidden`,
|
|
285
|
+
* `aria-expanded`), and an identical reassign still queues a MutationRecord,
|
|
286
|
+
* so a second pass is observable to anyone watching them. The event is
|
|
287
|
+
* therefore claimed: the path that gets there first does the work, the other
|
|
288
|
+
* one finds it claimed and returns. A programmatic call with no event always
|
|
289
|
+
* runs.
|
|
290
|
+
*/
|
|
291
|
+
activate(event) {
|
|
292
|
+
if (event !== void 0) {
|
|
293
|
+
if (this.#activated.has(event)) return;
|
|
294
|
+
this.#activated.add(event);
|
|
295
|
+
}
|
|
251
296
|
this.#closeAndRestore();
|
|
252
297
|
}
|
|
253
298
|
/** Closes and returns focus to the trigger (Escape / item-activation path). */
|
|
@@ -260,16 +305,59 @@ var MenuController = class extends Controller {
|
|
|
260
305
|
if (this.#isOpen && !this.element.contains(event.target)) this.close();
|
|
261
306
|
};
|
|
262
307
|
/**
|
|
263
|
-
*
|
|
264
|
-
*
|
|
308
|
+
* Delegated twin of {@link onItemKeydown}, bound on the controller element.
|
|
309
|
+
*
|
|
310
|
+
* An item that arrives *after* connect — Overflow Menu moves toolbar controls
|
|
311
|
+
* into this menu at runtime — carries whatever `data-action` its author wrote,
|
|
312
|
+
* and that is exactly the binding a consumer forgets, because the markup that
|
|
313
|
+
* declares the item lives nowhere near the menu. Listening on the container
|
|
314
|
+
* makes membership in the `item` target enough to be operable. The per-element
|
|
315
|
+
* action stays supported and is not double-handled: see
|
|
316
|
+
* {@link #handleItemKeydown}.
|
|
317
|
+
*/
|
|
318
|
+
#onDelegatedItemKeydown = (event) => {
|
|
319
|
+
const item = this.#itemFrom(event.target);
|
|
320
|
+
if (item === null) return;
|
|
321
|
+
this.#handleItemKeydown(event, item);
|
|
322
|
+
};
|
|
323
|
+
/**
|
|
324
|
+
* Delegated twin of {@link activate}, for the same reason as the keydown one.
|
|
325
|
+
*
|
|
326
|
+
* It works from the item the capture pass recorded, not from a fresh lookup:
|
|
327
|
+
* by the time a click bubbles up here, a consumer handler may already have
|
|
328
|
+
* detached the row it lives in, and `itemTargets` is a live query that would
|
|
329
|
+
* then resolve nothing — the activation would be lost and the menu left open
|
|
330
|
+
* over an item that no longer exists. `aria-disabled` items never reach this
|
|
331
|
+
* listener at all: the capture pass stops the click outright.
|
|
332
|
+
*/
|
|
333
|
+
#onDelegatedItemClick = (event) => {
|
|
334
|
+
const item = this.#clickOwner;
|
|
335
|
+
this.#clickOwner = null;
|
|
336
|
+
if (item === null) return;
|
|
337
|
+
this.activate(event);
|
|
338
|
+
};
|
|
339
|
+
/** The item target that is, or contains, `node`; `null` when it is neither. */
|
|
340
|
+
#itemFrom(node) {
|
|
341
|
+
if (!(node instanceof Node)) return null;
|
|
342
|
+
return this.itemTargets.find((item) => item === node || item.contains(node)) ?? null;
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* First look at every click inside the controller, and the only point at which
|
|
346
|
+
* the DOM is still the one the user clicked. Two jobs: record which item owns
|
|
347
|
+
* the click for the delegate above, and stop an `aria-disabled` command before
|
|
348
|
+
* it reaches consumer handlers (native Enter/Space activation synthesizes a
|
|
349
|
+
* click and is blocked here too).
|
|
265
350
|
*/
|
|
266
351
|
#onItemClickCapture = (event) => {
|
|
267
352
|
const target = event.target;
|
|
268
|
-
if (!(target instanceof Node))
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
353
|
+
if (!(target instanceof Node)) {
|
|
354
|
+
this.#clickOwner = null;
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
const item = this.#itemFrom(target);
|
|
358
|
+
this.#clickOwner = item;
|
|
359
|
+
if (item === null || item.getAttribute("aria-disabled") !== "true") return;
|
|
360
|
+
this.#clickOwner = null;
|
|
273
361
|
event.preventDefault();
|
|
274
362
|
event.stopImmediatePropagation();
|
|
275
363
|
};
|
|
@@ -287,13 +375,12 @@ var MenuController = class extends Controller {
|
|
|
287
375
|
return this.itemTargets.filter((item) => this.#isNavigable(item));
|
|
288
376
|
}
|
|
289
377
|
/**
|
|
290
|
-
* An item can take roving focus unless it is `hidden
|
|
291
|
-
*
|
|
292
|
-
*
|
|
378
|
+
* An item can take roving focus unless it is `hidden` or a natively `disabled`
|
|
379
|
+
* form control. `aria-disabled` remains focusable for discoverability; capture
|
|
380
|
+
* suppresses activation. CSS-only visibility is the consumer's responsibility.
|
|
293
381
|
*/
|
|
294
382
|
#isNavigable(item) {
|
|
295
383
|
if (item.hasAttribute("hidden")) return false;
|
|
296
|
-
if (item.getAttribute("aria-disabled") === "true") return false;
|
|
297
384
|
return !item.disabled;
|
|
298
385
|
}
|
|
299
386
|
/** Whether the menu is currently visible. */
|