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.
- package/CHANGELOG.md +136 -0
- package/README.md +27 -11
- package/dist/cable/index.d.ts +88 -31
- package/dist/cable/index.js +384 -449
- package/dist/cable/index.js.map +1 -1
- package/dist/coerce-JtZVNG5h.d.ts +20 -0
- package/dist/controllers/accordion_controller.d.ts +4 -2
- package/dist/controllers/accordion_controller.js +174 -55
- package/dist/controllers/accordion_controller.js.map +1 -1
- package/dist/controllers/alert_dialog_controller.d.ts +12 -2
- package/dist/controllers/alert_dialog_controller.js +903 -235
- package/dist/controllers/alert_dialog_controller.js.map +1 -1
- package/dist/controllers/announcer_controller.d.ts +37 -5
- package/dist/controllers/announcer_controller.js +238 -205
- package/dist/controllers/announcer_controller.js.map +1 -1
- package/dist/controllers/aspect_ratio_controller.js +0 -10
- package/dist/controllers/aspect_ratio_controller.js.map +1 -1
- package/dist/controllers/auto_submit_controller.d.ts +30 -6
- package/dist/controllers/auto_submit_controller.js +414 -145
- package/dist/controllers/auto_submit_controller.js.map +1 -1
- package/dist/controllers/avatar_controller.js +185 -107
- package/dist/controllers/avatar_controller.js.map +1 -1
- package/dist/controllers/breadcrumb_controller.js +0 -130
- package/dist/controllers/breadcrumb_controller.js.map +1 -1
- package/dist/controllers/bulk_select_controller.d.ts +21 -3
- package/dist/controllers/bulk_select_controller.js +282 -58
- package/dist/controllers/bulk_select_controller.js.map +1 -1
- package/dist/controllers/calendar_controller.d.ts +38 -16
- package/dist/controllers/calendar_controller.js +228 -260
- package/dist/controllers/calendar_controller.js.map +1 -1
- package/dist/controllers/carousel_controller.d.ts +43 -12
- package/dist/controllers/carousel_controller.js +536 -436
- package/dist/controllers/carousel_controller.js.map +1 -1
- package/dist/controllers/character_counter_controller.d.ts +44 -7
- package/dist/controllers/character_counter_controller.js +493 -207
- package/dist/controllers/character_counter_controller.js.map +1 -1
- package/dist/controllers/checkbox_controller.d.ts +7 -2
- package/dist/controllers/checkbox_controller.js +38 -74
- package/dist/controllers/checkbox_controller.js.map +1 -1
- package/dist/controllers/clipboard_controller.d.ts +16 -5
- package/dist/controllers/clipboard_controller.js +261 -149
- package/dist/controllers/clipboard_controller.js.map +1 -1
- package/dist/controllers/collapsible_controller.d.ts +17 -5
- package/dist/controllers/collapsible_controller.js +304 -120
- package/dist/controllers/collapsible_controller.js.map +1 -1
- package/dist/controllers/color_picker_controller.d.ts +18 -6
- package/dist/controllers/color_picker_controller.js +178 -146
- package/dist/controllers/color_picker_controller.js.map +1 -1
- package/dist/controllers/combobox_controller.d.ts +24 -6
- package/dist/controllers/combobox_controller.js +522 -156
- package/dist/controllers/combobox_controller.js.map +1 -1
- package/dist/controllers/command_palette_controller.d.ts +51 -20
- package/dist/controllers/command_palette_controller.js +1156 -395
- package/dist/controllers/command_palette_controller.js.map +1 -1
- package/dist/controllers/conditional_fields_controller.d.ts +15 -2
- package/dist/controllers/conditional_fields_controller.js +300 -171
- package/dist/controllers/conditional_fields_controller.js.map +1 -1
- package/dist/controllers/confirm_controller.d.ts +49 -12
- package/dist/controllers/confirm_controller.js +980 -262
- package/dist/controllers/confirm_controller.js.map +1 -1
- package/dist/controllers/context_menu_controller.d.ts +80 -18
- package/dist/controllers/context_menu_controller.js +346 -153
- package/dist/controllers/context_menu_controller.js.map +1 -1
- package/dist/controllers/count_up_controller.d.ts +13 -1
- package/dist/controllers/count_up_controller.js +73 -21
- package/dist/controllers/count_up_controller.js.map +1 -1
- package/dist/controllers/countdown_controller.d.ts +47 -3
- package/dist/controllers/countdown_controller.js +281 -158
- package/dist/controllers/countdown_controller.js.map +1 -1
- package/dist/controllers/currency_input_controller.d.ts +93 -10
- package/dist/controllers/currency_input_controller.js +575 -225
- package/dist/controllers/currency_input_controller.js.map +1 -1
- package/dist/controllers/data_grid_controller.d.ts +25 -12
- package/dist/controllers/data_grid_controller.js +248 -214
- package/dist/controllers/data_grid_controller.js.map +1 -1
- package/dist/controllers/date_range_picker_controller.d.ts +42 -14
- package/dist/controllers/date_range_picker_controller.js +338 -235
- package/dist/controllers/date_range_picker_controller.js.map +1 -1
- package/dist/controllers/dialog_controller.d.ts +17 -6
- package/dist/controllers/dialog_controller.js +899 -237
- package/dist/controllers/dialog_controller.js.map +1 -1
- package/dist/controllers/direct_upload_controller.d.ts +25 -8
- package/dist/controllers/direct_upload_controller.js +97 -167
- package/dist/controllers/direct_upload_controller.js.map +1 -1
- package/dist/controllers/dirty_form_controller.d.ts +6 -1
- package/dist/controllers/dirty_form_controller.js +72 -69
- package/dist/controllers/dirty_form_controller.js.map +1 -1
- package/dist/controllers/dismissible_controller.d.ts +19 -0
- package/dist/controllers/dismissible_controller.js +196 -24
- package/dist/controllers/dismissible_controller.js.map +1 -1
- package/dist/controllers/drawer_controller.d.ts +39 -11
- package/dist/controllers/drawer_controller.js +974 -355
- package/dist/controllers/drawer_controller.js.map +1 -1
- package/dist/controllers/dropdown_controller.d.ts +31 -1
- package/dist/controllers/dropdown_controller.js +339 -96
- package/dist/controllers/dropdown_controller.js.map +1 -1
- package/dist/controllers/editable_controller.d.ts +12 -1
- package/dist/controllers/editable_controller.js +315 -88
- package/dist/controllers/editable_controller.js.map +1 -1
- package/dist/controllers/empty_state_controller.js +79 -58
- package/dist/controllers/empty_state_controller.js.map +1 -1
- package/dist/controllers/file_dropzone_controller.d.ts +63 -18
- package/dist/controllers/file_dropzone_controller.js +322 -211
- package/dist/controllers/file_dropzone_controller.js.map +1 -1
- package/dist/controllers/filter_controller.js +77 -42
- package/dist/controllers/filter_controller.js.map +1 -1
- package/dist/controllers/flash_controller.d.ts +35 -12
- package/dist/controllers/flash_controller.js +238 -347
- package/dist/controllers/flash_controller.js.map +1 -1
- package/dist/controllers/focus_controller.d.ts +15 -6
- package/dist/controllers/focus_controller.js +679 -240
- package/dist/controllers/focus_controller.js.map +1 -1
- package/dist/controllers/form_field_controller.js +156 -132
- package/dist/controllers/form_field_controller.js.map +1 -1
- package/dist/controllers/form_validation_controller.d.ts +3 -3
- package/dist/controllers/form_validation_controller.js +31 -97
- package/dist/controllers/form_validation_controller.js.map +1 -1
- package/dist/controllers/frame_loading_controller.d.ts +47 -14
- package/dist/controllers/frame_loading_controller.js +275 -217
- package/dist/controllers/frame_loading_controller.js.map +1 -1
- package/dist/controllers/highlight_controller.d.ts +27 -6
- package/dist/controllers/highlight_controller.js +107 -75
- package/dist/controllers/highlight_controller.js.map +1 -1
- package/dist/controllers/hover_card_controller.d.ts +60 -4
- package/dist/controllers/hover_card_controller.js +327 -130
- package/dist/controllers/hover_card_controller.js.map +1 -1
- package/dist/controllers/idle_controller.d.ts +48 -6
- package/dist/controllers/idle_controller.js +271 -101
- package/dist/controllers/idle_controller.js.map +1 -1
- package/dist/controllers/input_mask_controller.d.ts +30 -13
- package/dist/controllers/input_mask_controller.js +483 -97
- package/dist/controllers/input_mask_controller.js.map +1 -1
- package/dist/controllers/intersection_controller.d.ts +32 -13
- package/dist/controllers/intersection_controller.js +150 -106
- package/dist/controllers/intersection_controller.js.map +1 -1
- package/dist/controllers/lazy_frame_controller.d.ts +19 -2
- package/dist/controllers/lazy_frame_controller.js +24 -68
- package/dist/controllers/lazy_frame_controller.js.map +1 -1
- package/dist/controllers/listbox_controller.d.ts +44 -9
- package/dist/controllers/listbox_controller.js +523 -240
- package/dist/controllers/listbox_controller.js.map +1 -1
- package/dist/controllers/local_time_controller.d.ts +9 -7
- package/dist/controllers/local_time_controller.js +102 -77
- package/dist/controllers/local_time_controller.js.map +1 -1
- package/dist/controllers/masonry_controller.d.ts +11 -3
- package/dist/controllers/masonry_controller.js +80 -96
- package/dist/controllers/masonry_controller.js.map +1 -1
- package/dist/controllers/menu_controller.d.ts +55 -11
- package/dist/controllers/menu_controller.js +302 -188
- package/dist/controllers/menu_controller.js.map +1 -1
- package/dist/controllers/menubar_controller.d.ts +51 -9
- package/dist/controllers/menubar_controller.js +232 -345
- package/dist/controllers/menubar_controller.js.map +1 -1
- package/dist/controllers/meter_controller.d.ts +33 -20
- package/dist/controllers/meter_controller.js +417 -87
- package/dist/controllers/meter_controller.js.map +1 -1
- package/dist/controllers/multi_select_controller.d.ts +48 -18
- package/dist/controllers/multi_select_controller.js +785 -393
- package/dist/controllers/multi_select_controller.js.map +1 -1
- package/dist/controllers/navigation_menu_controller.d.ts +27 -6
- package/dist/controllers/navigation_menu_controller.js +190 -250
- package/dist/controllers/navigation_menu_controller.js.map +1 -1
- package/dist/controllers/nested_form_controller.d.ts +10 -0
- package/dist/controllers/nested_form_controller.js +396 -192
- package/dist/controllers/nested_form_controller.js.map +1 -1
- package/dist/controllers/network_status_controller.d.ts +23 -0
- package/dist/controllers/network_status_controller.js +268 -62
- package/dist/controllers/network_status_controller.js.map +1 -1
- package/dist/controllers/number_input_controller.d.ts +50 -8
- package/dist/controllers/number_input_controller.js +610 -303
- package/dist/controllers/number_input_controller.js.map +1 -1
- package/dist/controllers/optimistic_controller.d.ts +8 -5
- package/dist/controllers/optimistic_controller.js +45 -87
- package/dist/controllers/optimistic_controller.js.map +1 -1
- package/dist/controllers/otp_controller.d.ts +55 -8
- package/dist/controllers/otp_controller.js +658 -255
- package/dist/controllers/otp_controller.js.map +1 -1
- package/dist/controllers/overflow_indicator_controller.d.ts +5 -0
- package/dist/controllers/overflow_indicator_controller.js +122 -113
- package/dist/controllers/overflow_indicator_controller.js.map +1 -1
- package/dist/controllers/overflow_menu_controller.d.ts +58 -20
- package/dist/controllers/overflow_menu_controller.js +470 -295
- package/dist/controllers/overflow_menu_controller.js.map +1 -1
- package/dist/controllers/pagination_controller.d.ts +37 -7
- package/dist/controllers/pagination_controller.js +332 -123
- package/dist/controllers/pagination_controller.js.map +1 -1
- package/dist/controllers/password_reveal_controller.d.ts +46 -8
- package/dist/controllers/password_reveal_controller.js +392 -121
- package/dist/controllers/password_reveal_controller.js.map +1 -1
- package/dist/controllers/password_strength_controller.d.ts +15 -6
- package/dist/controllers/password_strength_controller.js +111 -158
- package/dist/controllers/password_strength_controller.js.map +1 -1
- package/dist/controllers/persist_controller.d.ts +11 -4
- package/dist/controllers/persist_controller.js +165 -111
- package/dist/controllers/persist_controller.js.map +1 -1
- package/dist/controllers/pointer_drag_controller.d.ts +35 -12
- package/dist/controllers/pointer_drag_controller.js +434 -188
- package/dist/controllers/pointer_drag_controller.js.map +1 -1
- package/dist/controllers/popover_controller.d.ts +41 -5
- package/dist/controllers/popover_controller.js +269 -113
- package/dist/controllers/popover_controller.js.map +1 -1
- package/dist/controllers/portal_controller.d.ts +44 -4
- package/dist/controllers/portal_controller.js +215 -76
- package/dist/controllers/portal_controller.js.map +1 -1
- package/dist/controllers/preview_guard_controller.d.ts +10 -4
- package/dist/controllers/preview_guard_controller.js +243 -116
- package/dist/controllers/preview_guard_controller.js.map +1 -1
- package/dist/controllers/progress_controller.d.ts +20 -19
- package/dist/controllers/progress_controller.js +391 -78
- package/dist/controllers/progress_controller.js.map +1 -1
- package/dist/controllers/radio_group_controller.d.ts +21 -9
- package/dist/controllers/radio_group_controller.js +278 -211
- package/dist/controllers/radio_group_controller.js.map +1 -1
- package/dist/controllers/range_slider_controller.d.ts +47 -6
- package/dist/controllers/range_slider_controller.js +220 -113
- package/dist/controllers/range_slider_controller.js.map +1 -1
- package/dist/controllers/rating_controller.d.ts +20 -4
- package/dist/controllers/rating_controller.js +458 -211
- package/dist/controllers/rating_controller.js.map +1 -1
- package/dist/controllers/read_more_controller.d.ts +2 -0
- package/dist/controllers/read_more_controller.js +138 -63
- package/dist/controllers/read_more_controller.js.map +1 -1
- package/dist/controllers/reading_progress_controller.d.ts +1 -1
- package/dist/controllers/reading_progress_controller.js +166 -137
- package/dist/controllers/reading_progress_controller.js.map +1 -1
- package/dist/controllers/relative_time_controller.d.ts +20 -6
- package/dist/controllers/relative_time_controller.js +211 -112
- package/dist/controllers/relative_time_controller.js.map +1 -1
- package/dist/controllers/reset_on_restore_controller.d.ts +85 -0
- package/dist/controllers/{reset_before_cache_controller.js → reset_on_restore_controller.js} +103 -38
- package/dist/controllers/reset_on_restore_controller.js.map +1 -0
- package/dist/controllers/resizable_controller.d.ts +31 -2
- package/dist/controllers/resizable_controller.js +299 -150
- package/dist/controllers/resizable_controller.js.map +1 -1
- package/dist/controllers/roving_controller.d.ts +3 -3
- package/dist/controllers/roving_controller.js +49 -88
- package/dist/controllers/roving_controller.js.map +1 -1
- package/dist/controllers/scroll_area_controller.d.ts +7 -3
- package/dist/controllers/scroll_area_controller.js +490 -246
- package/dist/controllers/scroll_area_controller.js.map +1 -1
- package/dist/controllers/scroll_restore_controller.d.ts +3 -3
- package/dist/controllers/scroll_restore_controller.js +14 -55
- package/dist/controllers/scroll_restore_controller.js.map +1 -1
- package/dist/controllers/scroll_visibility_controller.d.ts +28 -12
- package/dist/controllers/scroll_visibility_controller.js +432 -149
- package/dist/controllers/scroll_visibility_controller.js.map +1 -1
- package/dist/controllers/scrollspy_controller.d.ts +6 -1
- package/dist/controllers/scrollspy_controller.js +193 -248
- package/dist/controllers/scrollspy_controller.js.map +1 -1
- package/dist/controllers/separator_controller.d.ts +20 -5
- package/dist/controllers/separator_controller.js +285 -142
- package/dist/controllers/separator_controller.js.map +1 -1
- package/dist/controllers/sidebar_controller.d.ts +65 -8
- package/dist/controllers/sidebar_controller.js +1069 -404
- package/dist/controllers/sidebar_controller.js.map +1 -1
- package/dist/controllers/skeleton_controller.d.ts +23 -0
- package/dist/controllers/skeleton_controller.js +259 -101
- package/dist/controllers/skeleton_controller.js.map +1 -1
- package/dist/controllers/slider_controller.d.ts +29 -2
- package/dist/controllers/slider_controller.js +187 -86
- package/dist/controllers/slider_controller.js.map +1 -1
- package/dist/controllers/smart_sticky_header_controller.d.ts +22 -4
- package/dist/controllers/smart_sticky_header_controller.js +173 -66
- package/dist/controllers/smart_sticky_header_controller.js.map +1 -1
- package/dist/controllers/sortable_controller.d.ts +4 -4
- package/dist/controllers/sortable_controller.js +26 -125
- package/dist/controllers/sortable_controller.js.map +1 -1
- package/dist/controllers/spinner_controller.d.ts +39 -4
- package/dist/controllers/spinner_controller.js +288 -173
- package/dist/controllers/spinner_controller.js.map +1 -1
- package/dist/controllers/step_indicator_controller.d.ts +9 -4
- package/dist/controllers/step_indicator_controller.js +105 -46
- package/dist/controllers/step_indicator_controller.js.map +1 -1
- package/dist/controllers/stepper_controller.d.ts +14 -6
- package/dist/controllers/stepper_controller.js +120 -73
- package/dist/controllers/stepper_controller.js.map +1 -1
- package/dist/controllers/stick_to_bottom_controller.d.ts +9 -1
- package/dist/controllers/stick_to_bottom_controller.js +275 -140
- package/dist/controllers/stick_to_bottom_controller.js.map +1 -1
- package/dist/controllers/sticky_observer_controller.d.ts +11 -0
- package/dist/controllers/sticky_observer_controller.js +220 -49
- package/dist/controllers/sticky_observer_controller.js.map +1 -1
- package/dist/controllers/submit_once_controller.d.ts +24 -8
- package/dist/controllers/submit_once_controller.js +376 -227
- package/dist/controllers/submit_once_controller.js.map +1 -1
- package/dist/controllers/switch_controller.d.ts +18 -4
- package/dist/controllers/switch_controller.js +201 -84
- package/dist/controllers/switch_controller.js.map +1 -1
- package/dist/controllers/tabs_controller.d.ts +9 -6
- package/dist/controllers/tabs_controller.js +41 -30
- package/dist/controllers/tabs_controller.js.map +1 -1
- package/dist/controllers/tags_input_controller.d.ts +16 -3
- package/dist/controllers/tags_input_controller.js +293 -173
- package/dist/controllers/tags_input_controller.js.map +1 -1
- package/dist/controllers/textarea_autosize_controller.d.ts +11 -1
- package/dist/controllers/textarea_autosize_controller.js +108 -74
- package/dist/controllers/textarea_autosize_controller.js.map +1 -1
- package/dist/controllers/theme_controller.d.ts +24 -11
- package/dist/controllers/theme_controller.js +140 -116
- package/dist/controllers/theme_controller.js.map +1 -1
- package/dist/controllers/time_picker_controller.d.ts +55 -5
- package/dist/controllers/time_picker_controller.js +331 -127
- package/dist/controllers/time_picker_controller.js.map +1 -1
- package/dist/controllers/toast_controller.d.ts +40 -16
- package/dist/controllers/toast_controller.js +244 -324
- package/dist/controllers/toast_controller.js.map +1 -1
- package/dist/controllers/toggle_group_controller.d.ts +14 -6
- package/dist/controllers/toggle_group_controller.js +252 -191
- package/dist/controllers/toggle_group_controller.js.map +1 -1
- package/dist/controllers/toolbar_controller.d.ts +8 -6
- package/dist/controllers/toolbar_controller.js +219 -120
- package/dist/controllers/toolbar_controller.js.map +1 -1
- package/dist/controllers/tooltip_controller.d.ts +49 -4
- package/dist/controllers/tooltip_controller.js +158 -124
- package/dist/controllers/tooltip_controller.js.map +1 -1
- package/dist/controllers/transition_controller.d.ts +22 -4
- package/dist/controllers/transition_controller.js +123 -119
- package/dist/controllers/transition_controller.js.map +1 -1
- package/dist/controllers/tree_view_controller.d.ts +22 -12
- package/dist/controllers/tree_view_controller.js +283 -369
- package/dist/controllers/tree_view_controller.js.map +1 -1
- package/dist/index.d.ts +10 -5
- package/dist/index.js +8573 -10410
- package/dist/index.js.map +1 -1
- package/dist/inspector/cli.d.ts +38 -14
- package/dist/inspector/cli.js +108 -40
- package/dist/inspector/cli.js.map +1 -1
- package/dist/inspector/cli_bin.js +145 -64
- package/dist/inspector/cli_bin.js.map +1 -1
- package/dist/inspector/examples.json +20 -20
- package/dist/inspector/manifest.json +802 -101
- package/dist/positioning/index.d.ts +15 -7
- package/dist/positioning/index.js +154 -47
- package/dist/positioning/index.js.map +1 -1
- package/package.json +11 -10
- package/dist/controllers/reset_before_cache_controller.d.ts +0 -76
- package/dist/controllers/reset_before_cache_controller.js.map +0 -1
|
@@ -1,13 +1,31 @@
|
|
|
1
1
|
import { Controller } from '@hotwired/stimulus';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
var FOCUS_EVENTS = /* @__PURE__ */ new Set(["blur", "focus", "focusin", "focusout"]);
|
|
4
|
+
var POINTER_EVENTS = /* @__PURE__ */ new Set(["mouseenter", "mouseleave", "pointerenter", "pointerleave"]);
|
|
5
|
+
function stateReasonFor(event) {
|
|
6
|
+
if (!event) return "api";
|
|
7
|
+
if (FOCUS_EVENTS.has(event.type)) return "focus";
|
|
8
|
+
if (POINTER_EVENTS.has(event.type)) return "pointer";
|
|
9
|
+
return "user";
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function actionSource(source) {
|
|
13
|
+
if (source instanceof HTMLElement) {
|
|
14
|
+
return { event: null, host: source, origin: source, reason: "api" };
|
|
15
|
+
}
|
|
16
|
+
const event = source ?? null;
|
|
17
|
+
return {
|
|
18
|
+
event,
|
|
19
|
+
host: event?.currentTarget instanceof Element ? event.currentTarget : null,
|
|
20
|
+
origin: event?.target instanceof Element ? event.target : null,
|
|
21
|
+
reason: stateReasonFor(event)
|
|
22
|
+
};
|
|
23
|
+
}
|
|
4
24
|
|
|
5
|
-
// src/utils/logical_scroll.ts
|
|
6
25
|
function isRtl(element) {
|
|
7
26
|
return window.getComputedStyle(element).direction === "rtl";
|
|
8
27
|
}
|
|
9
28
|
|
|
10
|
-
// src/utils/arrow_step.ts
|
|
11
29
|
function logicalArrowKey(key, element) {
|
|
12
30
|
if (key !== "ArrowRight" && key !== "ArrowLeft") return key;
|
|
13
31
|
if (!isRtl(element)) return key;
|
|
@@ -18,7 +36,104 @@ function isReservedArrowChord(event, allow = []) {
|
|
|
18
36
|
return event.altKey && !allow.includes("alt") || event.ctrlKey && !allow.includes("ctrl") || event.metaKey && !allow.includes("meta") || event.shiftKey && !allow.includes("shift");
|
|
19
37
|
}
|
|
20
38
|
|
|
21
|
-
|
|
39
|
+
function setAttributeIfChanged(element, name, value, beforeWrite) {
|
|
40
|
+
if (element.getAttribute(name) === value) return false;
|
|
41
|
+
beforeWrite?.();
|
|
42
|
+
if (value === null) element.removeAttribute(name);
|
|
43
|
+
else element.setAttribute(name, value);
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
var AuthoredMutationWatcher = class {
|
|
48
|
+
#onAuthored;
|
|
49
|
+
#claims = null;
|
|
50
|
+
#observer = null;
|
|
51
|
+
constructor(onAuthored) {
|
|
52
|
+
this.#onAuthored = onAuthored;
|
|
53
|
+
}
|
|
54
|
+
observe(root, options) {
|
|
55
|
+
this.disconnect();
|
|
56
|
+
const observer = new MutationObserver((records) => {
|
|
57
|
+
if (this.#observer !== observer) return;
|
|
58
|
+
const authored = this.#filter(records);
|
|
59
|
+
if (authored.length > 0) this.#onAuthored(authored);
|
|
60
|
+
});
|
|
61
|
+
this.#observer = observer;
|
|
62
|
+
this.#claims = { attributes: /* @__PURE__ */ new Map(), children: /* @__PURE__ */ new Map() };
|
|
63
|
+
observer.observe(root, {
|
|
64
|
+
attributes: true,
|
|
65
|
+
attributeFilter: [...options.attributes],
|
|
66
|
+
subtree: options.subtree ?? false,
|
|
67
|
+
childList: options.childList ?? false
|
|
68
|
+
});
|
|
69
|
+
if (options.ancestorAttributes) {
|
|
70
|
+
let ancestor = root.parentElement;
|
|
71
|
+
while (ancestor) {
|
|
72
|
+
observer.observe(ancestor, {
|
|
73
|
+
attributes: true,
|
|
74
|
+
attributeFilter: [...options.ancestorAttributes]
|
|
75
|
+
});
|
|
76
|
+
ancestor = ancestor.parentElement;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
claim(target, name, value) {
|
|
81
|
+
const claims = this.#claims?.attributes;
|
|
82
|
+
let attributes = claims?.get(target);
|
|
83
|
+
if (!attributes) {
|
|
84
|
+
attributes = /* @__PURE__ */ new Map();
|
|
85
|
+
claims?.set(target, attributes);
|
|
86
|
+
}
|
|
87
|
+
const pending = attributes.get(name);
|
|
88
|
+
attributes.set(name, { value, count: (pending?.count ?? 0) + 1 });
|
|
89
|
+
}
|
|
90
|
+
claimCurrent(target, name) {
|
|
91
|
+
this.claim(target, name, target.getAttribute(name));
|
|
92
|
+
}
|
|
93
|
+
claimChildList(target, added, removed) {
|
|
94
|
+
const claims = this.#claims?.children;
|
|
95
|
+
const pending = claims?.get(target) ?? { added: /* @__PURE__ */ new Map(), removed: /* @__PURE__ */ new Map() };
|
|
96
|
+
for (const node of added) pending.added.set(node, (pending.added.get(node) ?? 0) + 1);
|
|
97
|
+
for (const node of removed) pending.removed.set(node, (pending.removed.get(node) ?? 0) + 1);
|
|
98
|
+
claims?.set(target, pending);
|
|
99
|
+
}
|
|
100
|
+
take() {
|
|
101
|
+
return this.#filter(this.#observer?.takeRecords() ?? []);
|
|
102
|
+
}
|
|
103
|
+
disconnect() {
|
|
104
|
+
this.#observer?.disconnect();
|
|
105
|
+
this.#observer = null;
|
|
106
|
+
this.#claims = null;
|
|
107
|
+
}
|
|
108
|
+
#filter(records) {
|
|
109
|
+
const authored = records.filter((record) => !this.#consume(record));
|
|
110
|
+
this.#claims?.attributes.clear();
|
|
111
|
+
this.#claims?.children.clear();
|
|
112
|
+
return authored;
|
|
113
|
+
}
|
|
114
|
+
#consume(record) {
|
|
115
|
+
if (record.type === "attributes") {
|
|
116
|
+
const target = record.target;
|
|
117
|
+
const pending2 = this.#claims?.attributes.get(target)?.get(record.attributeName);
|
|
118
|
+
if (!pending2 || pending2.count === 0 || target.getAttribute(record.attributeName) !== pending2.value) {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
pending2.count -= 1;
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
const pending = this.#claims?.children.get(record.target);
|
|
125
|
+
if (!pending) return false;
|
|
126
|
+
const added = [...record.addedNodes];
|
|
127
|
+
const removed = [...record.removedNodes];
|
|
128
|
+
if (added.some((node) => !pending.added.get(node)) || removed.some((node) => !pending.removed.get(node))) {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
for (const node of added) pending.added.set(node, (pending.added.get(node) ?? 0) - 1);
|
|
132
|
+
for (const node of removed) pending.removed.set(node, (pending.removed.get(node) ?? 0) - 1);
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
|
|
22
137
|
function writeFields(container, values, { name, form = "" }) {
|
|
23
138
|
const current = [...container.children];
|
|
24
139
|
const submits = current.length === values.length && current.every(
|
|
@@ -41,7 +156,6 @@ function commitField(target) {
|
|
|
41
156
|
target.dispatchEvent(new Event("change", { bubbles: true }));
|
|
42
157
|
}
|
|
43
158
|
|
|
44
|
-
// src/utils/interactive_host.ts
|
|
45
159
|
var INTERACTIVE_HOST_SELECTOR = "button, input, select, textarea, label, a[href], area[href], summary, details, audio[controls], video[controls], iframe, object, embed";
|
|
46
160
|
function isInteractiveHost(element) {
|
|
47
161
|
if (element.matches(INTERACTIVE_HOST_SELECTOR)) return true;
|
|
@@ -58,27 +172,36 @@ function isInteractiveHost(element) {
|
|
|
58
172
|
return false;
|
|
59
173
|
}
|
|
60
174
|
|
|
61
|
-
|
|
175
|
+
var ListenerSet = class {
|
|
176
|
+
#abort = new AbortController();
|
|
177
|
+
add(target, type, handler, options) {
|
|
178
|
+
target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });
|
|
179
|
+
}
|
|
180
|
+
dispose() {
|
|
181
|
+
this.#abort.abort();
|
|
182
|
+
this.#abort = new AbortController();
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
|
|
62
186
|
var MicrotaskCoalescer = class {
|
|
63
187
|
#run;
|
|
64
188
|
#queued = false;
|
|
65
189
|
#active = false;
|
|
66
190
|
#generation = 0;
|
|
67
|
-
/** @param run - the single reconciliation pass, invoked at most once per batch. */
|
|
68
191
|
constructor(run) {
|
|
69
192
|
this.#run = run;
|
|
70
193
|
}
|
|
71
|
-
/** Opens the window in which {@link schedule} is honoured; call from `connect()`. */
|
|
72
194
|
activate() {
|
|
73
195
|
this.#active = true;
|
|
74
196
|
}
|
|
75
|
-
/** Closes the window and drops any pending pass; call from `disconnect()`. */
|
|
76
197
|
cancel() {
|
|
77
198
|
this.#active = false;
|
|
78
199
|
this.#queued = false;
|
|
79
200
|
this.#generation += 1;
|
|
80
201
|
}
|
|
81
|
-
|
|
202
|
+
get pending() {
|
|
203
|
+
return this.#queued;
|
|
204
|
+
}
|
|
82
205
|
schedule() {
|
|
83
206
|
if (!this.#active || this.#queued) return;
|
|
84
207
|
this.#queued = true;
|
|
@@ -91,12 +214,51 @@ var MicrotaskCoalescer = class {
|
|
|
91
214
|
}
|
|
92
215
|
};
|
|
93
216
|
|
|
94
|
-
|
|
217
|
+
var MorphRenderWatcher = class {
|
|
218
|
+
#listeners = new ListenerSet();
|
|
219
|
+
#pass;
|
|
220
|
+
#element = null;
|
|
221
|
+
#onMorph = () => this.#pass.schedule();
|
|
222
|
+
constructor(run) {
|
|
223
|
+
this.#pass = new MicrotaskCoalescer(run);
|
|
224
|
+
}
|
|
225
|
+
observe(element) {
|
|
226
|
+
if (this.#element === element) return;
|
|
227
|
+
this.disconnect();
|
|
228
|
+
this.#element = element;
|
|
229
|
+
this.#pass.activate();
|
|
230
|
+
this.#listeners.add(element, "turbo:morph-element", this.#onMorph);
|
|
231
|
+
}
|
|
232
|
+
schedule() {
|
|
233
|
+
this.#pass.schedule();
|
|
234
|
+
}
|
|
235
|
+
get pending() {
|
|
236
|
+
return this.#pass.pending;
|
|
237
|
+
}
|
|
238
|
+
disconnect() {
|
|
239
|
+
this.#listeners.dispose();
|
|
240
|
+
this.#pass.cancel();
|
|
241
|
+
this.#element = null;
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
var MoveCounter = class {
|
|
246
|
+
#sequence = 0;
|
|
247
|
+
record() {
|
|
248
|
+
this.#sequence += 1;
|
|
249
|
+
return this.#sequence;
|
|
250
|
+
}
|
|
251
|
+
isLatest(token) {
|
|
252
|
+
return token === this.#sequence;
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
function targetSelector(identifier, name) {
|
|
257
|
+
return `[data-${identifier}-target~="${name}"]`;
|
|
258
|
+
}
|
|
259
|
+
|
|
95
260
|
var SORT_CYCLE = ["none", "ascending", "descending"];
|
|
96
261
|
var OBSERVED_ATTRIBUTES = ["aria-selected", "data-value"];
|
|
97
|
-
function setAttributeIfChanged(element, name, value) {
|
|
98
|
-
if (element.getAttribute(name) !== value) element.setAttribute(name, value);
|
|
99
|
-
}
|
|
100
262
|
function sameSelection(a, b) {
|
|
101
263
|
if (a.size !== b.size) return false;
|
|
102
264
|
for (const [row, value] of a) {
|
|
@@ -110,6 +272,9 @@ function nextSortDirection(current) {
|
|
|
110
272
|
return SORT_CYCLE[(from + 1) % SORT_CYCLE.length] ?? "ascending";
|
|
111
273
|
}
|
|
112
274
|
var DataGridController = class extends Controller {
|
|
275
|
+
#moves = new MoveCounter();
|
|
276
|
+
#connectionGeneration = 0;
|
|
277
|
+
#stopHolder = null;
|
|
113
278
|
static targets = ["columnHeader", "row", "cell", "fields"];
|
|
114
279
|
static values = {
|
|
115
280
|
selection: { type: String, default: "none" },
|
|
@@ -118,164 +283,93 @@ var DataGridController = class extends Controller {
|
|
|
118
283
|
};
|
|
119
284
|
static actions = ["onKeydown", "sort", "toggleSelect"];
|
|
120
285
|
static events = ["selectionchange", "sort", "reconcile"];
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
*
|
|
126
|
-
* Stimulus reports every target one at a time, so an ungated pass would re-walk
|
|
127
|
-
* the whole grid once per authored cell on mount and once per streamed cell
|
|
128
|
-
* afterwards — quadratic in the cell count both times.
|
|
129
|
-
*/
|
|
130
|
-
#reconcile = new MicrotaskCoalescer(() => this.#reconcileRows());
|
|
131
|
-
/** Watches the row attributes a page can rewrite in place; set while connected. */
|
|
132
|
-
#observer = null;
|
|
133
|
-
/** The selection last settled: on connect, by the user, or by a reported pass. */
|
|
286
|
+
#reconcile = new MorphRenderWatcher(() => this.#reconcileRows());
|
|
287
|
+
#observer = new AuthoredMutationWatcher((records) => {
|
|
288
|
+
if (this.#concernsRows(records)) this.#reconcile.schedule();
|
|
289
|
+
});
|
|
134
290
|
#settled = /* @__PURE__ */ new Map();
|
|
135
|
-
/**
|
|
136
|
-
* Establishes a single tab stop across all navigable cells/headers, brings the
|
|
137
|
-
* rows to their baseline, and settles the selection without reporting it.
|
|
138
|
-
*
|
|
139
|
-
* The whole baseline runs here rather than in the Value callbacks: Stimulus
|
|
140
|
-
* delivers those before `connect()`, where the pass is refused, and a
|
|
141
|
-
* re-attached element reuses its cached context, whose value observer does not
|
|
142
|
-
* fire again.
|
|
143
|
-
*/
|
|
144
291
|
connect() {
|
|
145
292
|
this.#restoreBaseline();
|
|
146
293
|
this.#settled = this.#selection();
|
|
147
|
-
this.#reconcile.
|
|
294
|
+
this.#reconcile.observe(this.element);
|
|
148
295
|
this.#observeRows();
|
|
149
296
|
}
|
|
150
|
-
/** Releases the row observer and drops a queued pass, so neither outlives the element. */
|
|
151
297
|
disconnect() {
|
|
152
|
-
this.#
|
|
153
|
-
this.#
|
|
154
|
-
this.#observer
|
|
298
|
+
this.#connectionGeneration += 1;
|
|
299
|
+
this.#reconcile.disconnect();
|
|
300
|
+
this.#observer.disconnect();
|
|
155
301
|
}
|
|
156
|
-
/**
|
|
157
|
-
* Rebuilds the DOM-owned baselines from the live grid: exactly one navigable
|
|
158
|
-
* cell is in the Tab sequence, `aria-multiselectable` follows `selection`, every
|
|
159
|
-
* selectable row carries an explicit `aria-selected`, and the fields mirror the
|
|
160
|
-
* selected rows.
|
|
161
|
-
*
|
|
162
|
-
* The tab stop keeps whichever cell already holds it, so a rebuild triggered by
|
|
163
|
-
* an unrelated row arriving does not throw the user's position away; only when
|
|
164
|
-
* no cell holds it — the grid is fresh, or the holder was removed — does the
|
|
165
|
-
* first navigable cell take over. Without that fallback a grid whose active row
|
|
166
|
-
* is removed keeps every cell at `-1` and drops out of the Tab sequence
|
|
167
|
-
* entirely.
|
|
168
|
-
*/
|
|
169
302
|
#restoreBaseline() {
|
|
170
303
|
const cells = this.#navigableCells();
|
|
171
|
-
const
|
|
304
|
+
const holder = this.#stopHolder;
|
|
305
|
+
const kept = holder !== null && holder.tabIndex === 0 && cells.includes(holder);
|
|
306
|
+
const active = (kept ? holder : cells.find((cell) => cell.tabIndex === 0)) ?? cells[0];
|
|
172
307
|
if (active) this.#setActiveCell(active, { focus: false }, cells);
|
|
173
308
|
this.#syncSelectable();
|
|
174
309
|
const pageWrote = this.#takePageRecords();
|
|
175
310
|
this.#normalizeSelection();
|
|
176
|
-
this.#
|
|
311
|
+
this.#flushMutations(pageWrote);
|
|
177
312
|
this.#mirrorFields(false);
|
|
178
313
|
}
|
|
179
|
-
/**
|
|
180
|
-
* The pass the page's changes run: the baselines are rebuilt, then a selection
|
|
181
|
-
* that moved is reported. The report comes last, so rows a subscriber rewrites
|
|
182
|
-
* are the next pass's to settle.
|
|
183
|
-
*/
|
|
184
314
|
#reconcileRows() {
|
|
185
315
|
this.#restoreBaseline();
|
|
186
316
|
this.#reportMove();
|
|
187
317
|
}
|
|
188
|
-
/**
|
|
189
|
-
* Reports the selection as `reconcile` when it differs from the one last
|
|
190
|
-
* settled. The settled selection is replaced before dispatching, so a move a
|
|
191
|
-
* subscriber makes is measured against what it was told.
|
|
192
|
-
*/
|
|
193
318
|
#reportMove() {
|
|
194
319
|
const selection = this.#selection();
|
|
195
320
|
if (sameSelection(selection, this.#settled)) return;
|
|
196
321
|
this.#settled = selection;
|
|
197
322
|
this.dispatch("reconcile", { detail: { rows: [...selection.keys()] } });
|
|
198
323
|
}
|
|
199
|
-
/**
|
|
200
|
-
* Re-renders `aria-multiselectable`, the rows and the fields when application
|
|
201
|
-
* code (or a Turbo morph) changes `selection` at runtime; a selection the new
|
|
202
|
-
* mode collapses is reported as `reconcile`.
|
|
203
|
-
*/
|
|
204
324
|
selectionValueChanged() {
|
|
205
325
|
this.#reconcile.schedule();
|
|
206
326
|
}
|
|
207
|
-
/** Rebuilds the submitted fields when the public name changes at runtime. */
|
|
208
327
|
nameValueChanged() {
|
|
209
328
|
this.#reconcile.schedule();
|
|
210
329
|
}
|
|
211
|
-
/** Repoints the submitted fields when the owning form changes at runtime. */
|
|
212
330
|
formValueChanged() {
|
|
213
331
|
this.#reconcile.schedule();
|
|
214
332
|
}
|
|
215
|
-
/** Seeds a fields container inserted after connect from the current selection. */
|
|
216
333
|
fieldsTargetConnected() {
|
|
217
334
|
this.#reconcile.schedule();
|
|
218
335
|
}
|
|
219
|
-
|
|
336
|
+
fieldsTargetDisconnected() {
|
|
337
|
+
this.#reconcile.schedule();
|
|
338
|
+
}
|
|
220
339
|
rowTargetConnected() {
|
|
221
340
|
this.#reconcile.schedule();
|
|
222
341
|
}
|
|
223
|
-
/** Settles the selection again when a row leaves the grid or drops its target token. */
|
|
224
342
|
rowTargetDisconnected() {
|
|
225
343
|
this.#reconcile.schedule();
|
|
226
344
|
}
|
|
227
|
-
/** Re-establishes the tab stop when a cell joins the grid after connect. */
|
|
228
345
|
cellTargetConnected() {
|
|
229
346
|
this.#reconcile.schedule();
|
|
230
347
|
}
|
|
231
|
-
/** Re-establishes the tab stop when a cell leaves the grid. */
|
|
232
348
|
cellTargetDisconnected() {
|
|
233
349
|
this.#reconcile.schedule();
|
|
234
350
|
}
|
|
235
|
-
/** Re-establishes the tab stop when a header joins the grid after connect. */
|
|
236
351
|
columnHeaderTargetConnected() {
|
|
237
352
|
this.#reconcile.schedule();
|
|
238
353
|
}
|
|
239
|
-
/** Re-establishes the tab stop when a header leaves the grid. */
|
|
240
354
|
columnHeaderTargetDisconnected() {
|
|
241
355
|
this.#reconcile.schedule();
|
|
242
356
|
}
|
|
243
|
-
/**
|
|
244
|
-
* Brings the authored rows to the shape the APG requires, without changing
|
|
245
|
-
* which rows the author chose.
|
|
246
|
-
*
|
|
247
|
-
* Every selectable row gets an explicit value — an absent `aria-selected` means
|
|
248
|
-
* "not selectable" in ARIA, so a forgotten attribute hides a selectable row —
|
|
249
|
-
* and a single-select grid keeps at most one `true`, first in DOM order.
|
|
250
|
-
* A grid that declares `selection="none"` has no selectable rows, so the
|
|
251
|
-
* attribute is removed rather than written: in ARIA its absence is what "not
|
|
252
|
-
* selectable" looks like.
|
|
253
|
-
*
|
|
254
|
-
* @stimeoRenderRoot
|
|
255
|
-
*/
|
|
256
357
|
#normalizeSelection() {
|
|
257
358
|
const rows = this.rowTargets;
|
|
258
359
|
if (this.selectionValue === "none") {
|
|
259
|
-
for (const row of rows) row
|
|
360
|
+
for (const row of rows) this.#writeSelection(row, null);
|
|
260
361
|
return;
|
|
261
362
|
}
|
|
262
363
|
const single = this.selectionValue === "single";
|
|
263
364
|
const first = rows.find((row) => row.getAttribute("aria-selected") === "true");
|
|
264
365
|
for (const row of rows) {
|
|
265
366
|
if (single) {
|
|
266
|
-
|
|
367
|
+
this.#writeSelection(row, row === first ? "true" : "false");
|
|
267
368
|
} else if (row.getAttribute("aria-selected") !== "true") {
|
|
268
|
-
|
|
369
|
+
this.#writeSelection(row, "false");
|
|
269
370
|
}
|
|
270
371
|
}
|
|
271
372
|
}
|
|
272
|
-
/**
|
|
273
|
-
* Mirrors `selection="multiple"` onto `aria-multiselectable` (APG Grid) so SRs
|
|
274
|
-
* announce that more than one row can be selected; cleared for single/none so a
|
|
275
|
-
* grid never carries a misleading attribute.
|
|
276
|
-
*
|
|
277
|
-
* @stimeoRenderRoot
|
|
278
|
-
*/
|
|
279
373
|
#syncSelectable() {
|
|
280
374
|
if (this.selectionValue === "multiple") {
|
|
281
375
|
setAttributeIfChanged(this.element, "aria-multiselectable", "true");
|
|
@@ -283,34 +377,39 @@ var DataGridController = class extends Controller {
|
|
|
283
377
|
this.element.removeAttribute("aria-multiselectable");
|
|
284
378
|
}
|
|
285
379
|
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
380
|
+
sort(source) {
|
|
381
|
+
const { event, host, origin, reason } = actionSource(source);
|
|
382
|
+
if (origin && origin.closest(`[data-controller~="${this.identifier}"]`) !== this.element)
|
|
383
|
+
return;
|
|
384
|
+
const header = host?.closest(targetSelector(this.identifier, "columnHeader"));
|
|
385
|
+
if (!header || !this.columnHeaderTargets.includes(header)) return;
|
|
386
|
+
if (event?.defaultPrevented) return;
|
|
387
|
+
const control = event ? this.#claimingControl(event, header) : null;
|
|
292
388
|
if (control && !(control instanceof HTMLButtonElement)) return;
|
|
293
389
|
const direction = nextSortDirection(header.getAttribute("aria-sort") ?? "none");
|
|
294
390
|
for (const other of this.columnHeaderTargets) {
|
|
295
391
|
other.setAttribute("aria-sort", other === header ? direction : "none");
|
|
296
392
|
}
|
|
297
393
|
this.#setActiveCell(header, { focus: false });
|
|
298
|
-
this.dispatch("sort", { detail: { column: header, direction } });
|
|
394
|
+
this.dispatch("sort", { detail: { column: header, direction, reason } });
|
|
299
395
|
}
|
|
300
|
-
|
|
301
|
-
|
|
396
|
+
toggleSelect(source) {
|
|
397
|
+
const { event, host, origin, reason } = actionSource(source);
|
|
398
|
+
if (origin && origin.closest(`[data-controller~="${this.identifier}"]`) !== this.element)
|
|
399
|
+
return;
|
|
302
400
|
if (this.selectionValue === "none") return;
|
|
303
|
-
if (event
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
if (row && this.rowTargets.includes(row)) this.#toggleRow(row);
|
|
401
|
+
if (event?.defaultPrevented) return;
|
|
402
|
+
if (event && host instanceof HTMLElement && this.#claimedByDescendant(event, host)) return;
|
|
403
|
+
const row = host?.closest(targetSelector(this.identifier, "row"));
|
|
404
|
+
if (row && this.rowTargets.includes(row)) this.#toggleRow(row, reason);
|
|
308
405
|
}
|
|
309
|
-
/** Grid navigation + sort/select activation. Bound to cells and headers. */
|
|
310
406
|
onKeydown(event) {
|
|
311
407
|
if (event.defaultPrevented) return;
|
|
312
408
|
if (isReservedArrowChord(event)) return;
|
|
313
409
|
if (event.isComposing) return;
|
|
410
|
+
if ((event.key === "Home" || event.key === "End") && (event.altKey || event.metaKey || event.shiftKey)) {
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
314
413
|
const cell = event.currentTarget;
|
|
315
414
|
if (this.#claimedByDescendant(event, cell)) return;
|
|
316
415
|
const matrix = this.#matrix();
|
|
@@ -351,38 +450,15 @@ var DataGridController = class extends Controller {
|
|
|
351
450
|
this.#setActiveCell(target, { focus: true }, matrix.flat());
|
|
352
451
|
}
|
|
353
452
|
}
|
|
354
|
-
/**
|
|
355
|
-
* Whether the event was addressed to a control inside `host` rather than to the
|
|
356
|
-
* grid.
|
|
357
|
-
*
|
|
358
|
-
* Cells and headers hold consumer markup, and APG's grid pattern expects that
|
|
359
|
-
* markup to include working controls — a row action button, an inline editor.
|
|
360
|
-
* Those own their own keystrokes and clicks, so the grid stands down entirely
|
|
361
|
-
* rather than acting in parallel. An editable host (its `contenteditable` state
|
|
362
|
-
* is inherited, so the walk is explicit) counts the same way.
|
|
363
|
-
*/
|
|
364
453
|
#claimedByDescendant(event, host) {
|
|
365
454
|
return this.#claimingControl(event, host) !== null;
|
|
366
455
|
}
|
|
367
|
-
/**
|
|
368
|
-
* The nested control this event belongs to, or `null` when the host owns it.
|
|
369
|
-
*
|
|
370
|
-
* Naming the control, rather than answering yes or no, is what lets the click
|
|
371
|
-
* path treat a sortable header's `<button>` as the activation it is while every
|
|
372
|
-
* other control still takes the event away.
|
|
373
|
-
*/
|
|
374
456
|
#claimingControl(event, host) {
|
|
375
457
|
const source = event.target;
|
|
376
458
|
const control = source.closest(INTERACTIVE_HOST_SELECTOR);
|
|
377
459
|
if (control && host.contains(control)) return control;
|
|
378
460
|
return isInteractiveHost(source) ? source : null;
|
|
379
461
|
}
|
|
380
|
-
/**
|
|
381
|
-
* Performs a header's sort or a cell row's selection toggle on activation.
|
|
382
|
-
*
|
|
383
|
-
* @stimeoRuntimeOnly `selection` decides whether this one activation toggles the row; the sort it
|
|
384
|
-
* cycles does not depend on it.
|
|
385
|
-
*/
|
|
386
462
|
#activate(cell) {
|
|
387
463
|
if (this.columnHeaderTargets.includes(cell)) {
|
|
388
464
|
this.#cycleSort(cell);
|
|
@@ -392,90 +468,59 @@ var DataGridController = class extends Controller {
|
|
|
392
468
|
const row = cell.closest("[role='row']");
|
|
393
469
|
if (row && this.rowTargets.includes(row)) this.#toggleRow(row);
|
|
394
470
|
}
|
|
395
|
-
/** Cycles a header's sort on keyboard activation and emits `sort`. */
|
|
396
471
|
#cycleSort(header) {
|
|
397
472
|
const direction = nextSortDirection(header.getAttribute("aria-sort") ?? "none");
|
|
398
473
|
for (const other of this.columnHeaderTargets) {
|
|
399
474
|
other.setAttribute("aria-sort", other === header ? direction : "none");
|
|
400
475
|
}
|
|
401
|
-
this.dispatch("sort", { detail: { column: header, direction } });
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
*
|
|
406
|
-
* @stimeoRuntimeOnly `selection` decides how this one toggle treats the other rows.
|
|
407
|
-
*/
|
|
408
|
-
#toggleRow(row) {
|
|
476
|
+
this.dispatch("sort", { detail: { column: header, direction, reason: "user" } });
|
|
477
|
+
}
|
|
478
|
+
#toggleRow(row, reason = "user") {
|
|
479
|
+
const generation = this.#connectionGeneration;
|
|
409
480
|
const selected = row.getAttribute("aria-selected") === "true";
|
|
410
481
|
const pageWrote = this.#takePageRecords();
|
|
411
482
|
if (this.selectionValue === "single") {
|
|
412
483
|
for (const other of this.rowTargets) {
|
|
413
|
-
if (other !== row)
|
|
484
|
+
if (other !== row) this.#writeSelection(other, "false");
|
|
414
485
|
}
|
|
415
486
|
}
|
|
416
|
-
row
|
|
417
|
-
this.#
|
|
418
|
-
|
|
487
|
+
this.#writeSelection(row, selected ? "false" : "true");
|
|
488
|
+
this.#flushMutations(pageWrote);
|
|
489
|
+
const selection = this.#selection();
|
|
490
|
+
const changed = !sameSelection(selection, this.#settled);
|
|
491
|
+
this.#settled = selection;
|
|
492
|
+
if (!changed) {
|
|
493
|
+
this.#mirrorFields(false);
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
const token = this.#moves.record();
|
|
419
497
|
this.#mirrorFields(true);
|
|
420
|
-
|
|
498
|
+
if (generation !== this.#connectionGeneration || !this.#moves.isLatest(token)) return;
|
|
499
|
+
this.dispatch("selectionchange", { detail: { rows: [...selection.keys()], reason } });
|
|
421
500
|
}
|
|
422
|
-
/** The selected rows in DOM order, each with the `data-value` it submits, if any. */
|
|
423
501
|
#selection() {
|
|
424
502
|
return new Map(
|
|
425
503
|
this.rowTargets.filter((row) => row.getAttribute("aria-selected") === "true").map((row) => [row, row.dataset.value])
|
|
426
504
|
);
|
|
427
505
|
}
|
|
428
|
-
/**
|
|
429
|
-
* Watches the rows' `aria-selected` and `data-value`. Every write the grid makes
|
|
430
|
-
* to them sits between a take and a drop of the observer's queue, so each record
|
|
431
|
-
* the callback receives is the page's, and only a record on one of the grid's own
|
|
432
|
-
* rows schedules a pass.
|
|
433
|
-
*/
|
|
434
506
|
#observeRows() {
|
|
435
|
-
|
|
436
|
-
if (this.#concernsRows(records)) this.#reconcile.schedule();
|
|
437
|
-
});
|
|
438
|
-
observer.observe(this.element, {
|
|
507
|
+
this.#observer.observe(this.element, {
|
|
439
508
|
subtree: true,
|
|
440
|
-
attributes:
|
|
441
|
-
attributeFilter: OBSERVED_ATTRIBUTES
|
|
509
|
+
attributes: OBSERVED_ATTRIBUTES
|
|
442
510
|
});
|
|
443
|
-
this.#observer = observer;
|
|
444
511
|
}
|
|
445
|
-
/**
|
|
446
|
-
* Empties the observer's queue before the grid writes to the rows, and says
|
|
447
|
-
* whether the page had queued anything about them. Those records still owe a
|
|
448
|
-
* pass: a toggle in `multiple` mode rewrites only its own row, so it does not fold
|
|
449
|
-
* in what the page wrote to the others.
|
|
450
|
-
*/
|
|
451
512
|
#takePageRecords() {
|
|
452
|
-
const records = this.#observer
|
|
513
|
+
const records = this.#observer.take();
|
|
453
514
|
return records.length > 0 && this.#concernsRows(records);
|
|
454
515
|
}
|
|
455
|
-
/**
|
|
456
|
-
* Whether any record is about one of this grid's own rows. A widget nested in a
|
|
457
|
-
* cell writes the same attributes on its own elements, and those are no reason
|
|
458
|
-
* to reconcile the grid.
|
|
459
|
-
*/
|
|
460
516
|
#concernsRows(records) {
|
|
461
517
|
const rows = new Set(this.rowTargets);
|
|
462
518
|
return records.some((record) => rows.has(record.target));
|
|
463
519
|
}
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
#dropOwnRecords(pageWrote) {
|
|
469
|
-
this.#observer?.takeRecords();
|
|
470
|
-
if (pageWrote) this.#reconcile.schedule();
|
|
471
|
-
}
|
|
472
|
-
/**
|
|
473
|
-
* Mirrors the selected rows' `data-value` into the optional fields container.
|
|
474
|
-
* A selected row without one submits nothing, so a grid whose rows carry no
|
|
475
|
-
* value submits an empty set rather than a row of blanks.
|
|
476
|
-
*
|
|
477
|
-
* @stimeoRenderRoot
|
|
478
|
-
*/
|
|
520
|
+
#flushMutations(pageWrote) {
|
|
521
|
+
const authored = this.#concernsRows(this.#observer.take());
|
|
522
|
+
if (pageWrote || authored) this.#reconcile.schedule();
|
|
523
|
+
}
|
|
479
524
|
#mirrorFields(notify) {
|
|
480
525
|
if (!this.hasFieldsTarget) return;
|
|
481
526
|
const values = this.rowTargets.filter((row) => row.getAttribute("aria-selected") === "true").map((row) => row.dataset.value).filter((value) => value !== void 0);
|
|
@@ -484,30 +529,17 @@ var DataGridController = class extends Controller {
|
|
|
484
529
|
commitField(this.fieldsTarget);
|
|
485
530
|
}
|
|
486
531
|
}
|
|
487
|
-
/**
|
|
488
|
-
* Makes `cell` the single tabbable cell (roving) and optionally focuses it.
|
|
489
|
-
*
|
|
490
|
-
* `cells` lets a caller that already walked the grid hand its collection over,
|
|
491
|
-
* so one keystroke rebuilds the matrix once instead of twice. The write is
|
|
492
|
-
* skipped where the attribute already holds the wanted value — comparing the
|
|
493
|
-
* attribute rather than the IDL property, because a cell with no `tabindex` at
|
|
494
|
-
* all reports `-1` and would then never receive the attribute it needs to be
|
|
495
|
-
* focusable.
|
|
496
|
-
*/
|
|
497
532
|
#setActiveCell(cell, { focus }, cells) {
|
|
533
|
+
this.#stopHolder = cell;
|
|
498
534
|
for (const candidate of cells ?? this.#navigableCells()) {
|
|
499
535
|
const wanted = candidate === cell ? "0" : "-1";
|
|
500
|
-
|
|
501
|
-
candidate.setAttribute("tabindex", wanted);
|
|
502
|
-
}
|
|
536
|
+
setAttributeIfChanged(candidate, "tabindex", wanted);
|
|
503
537
|
}
|
|
504
538
|
if (focus) cell.focus();
|
|
505
539
|
}
|
|
506
|
-
/** All navigable elements (headers + cells) in DOM order. */
|
|
507
540
|
#navigableCells() {
|
|
508
541
|
return this.#matrix().flat();
|
|
509
542
|
}
|
|
510
|
-
/** The grid as rows of navigable cells, derived from each `role="row"`. */
|
|
511
543
|
#matrix() {
|
|
512
544
|
const navigable = /* @__PURE__ */ new Set([...this.columnHeaderTargets, ...this.cellTargets]);
|
|
513
545
|
const rows = Array.from(this.element.querySelectorAll("[role='row']"));
|
|
@@ -517,7 +549,6 @@ var DataGridController = class extends Controller {
|
|
|
517
549
|
)
|
|
518
550
|
).filter((cells) => cells.length > 0);
|
|
519
551
|
}
|
|
520
|
-
/** Finds `[rowIndex, colIndex]` of `cell` within `matrix`, or null. */
|
|
521
552
|
#locate(matrix, cell) {
|
|
522
553
|
for (let row = 0; row < matrix.length; row++) {
|
|
523
554
|
const col = (matrix[row] ?? []).indexOf(cell);
|
|
@@ -525,14 +556,17 @@ var DataGridController = class extends Controller {
|
|
|
525
556
|
}
|
|
526
557
|
return null;
|
|
527
558
|
}
|
|
528
|
-
/** The cell at `[row, col]`, clamping `col` to that row's last cell. */
|
|
529
559
|
#cellInRow(matrix, row, col) {
|
|
530
560
|
const cells = matrix[row];
|
|
531
561
|
if (!cells || cells.length === 0) return void 0;
|
|
532
562
|
return cells[Math.min(col, cells.length - 1)];
|
|
533
563
|
}
|
|
564
|
+
#writeSelection(target, value) {
|
|
565
|
+
setAttributeIfChanged(target, "aria-selected", value, () => {
|
|
566
|
+
this.#observer.claim(target, "aria-selected", value);
|
|
567
|
+
});
|
|
568
|
+
}
|
|
534
569
|
};
|
|
535
570
|
|
|
536
571
|
export { DataGridController };
|
|
537
|
-
//# sourceMappingURL=data_grid_controller.js.map
|
|
538
572
|
//# sourceMappingURL=data_grid_controller.js.map
|