stimeo-ui 0.15.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.
- package/CHANGELOG.md +209 -0
- package/README.md +128 -1
- package/dist/cable/index.d.ts +79 -23
- package/dist/cable/index.js +392 -417
- package/dist/cable/index.js.map +1 -1
- package/dist/controllers/accordion_controller.d.ts +34 -5
- package/dist/controllers/accordion_controller.js +228 -28
- package/dist/controllers/accordion_controller.js.map +1 -1
- package/dist/controllers/alert_dialog_controller.d.ts +20 -9
- package/dist/controllers/alert_dialog_controller.js +862 -221
- package/dist/controllers/alert_dialog_controller.js.map +1 -1
- package/dist/controllers/announcer_controller.d.ts +38 -7
- package/dist/controllers/announcer_controller.js +135 -191
- 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 +295 -110
- package/dist/controllers/auto_submit_controller.js.map +1 -1
- package/dist/controllers/avatar_controller.js +172 -106
- package/dist/controllers/avatar_controller.js.map +1 -1
- package/dist/controllers/breadcrumb_controller.d.ts +10 -7
- package/dist/controllers/breadcrumb_controller.js +0 -129
- package/dist/controllers/breadcrumb_controller.js.map +1 -1
- package/dist/controllers/bulk_select_controller.d.ts +22 -4
- package/dist/controllers/bulk_select_controller.js +264 -53
- package/dist/controllers/bulk_select_controller.js.map +1 -1
- package/dist/controllers/calendar_controller.d.ts +109 -28
- package/dist/controllers/calendar_controller.js +346 -180
- package/dist/controllers/calendar_controller.js.map +1 -1
- package/dist/controllers/carousel_controller.d.ts +99 -27
- package/dist/controllers/carousel_controller.js +587 -300
- package/dist/controllers/carousel_controller.js.map +1 -1
- package/dist/controllers/character_counter_controller.d.ts +18 -2
- package/dist/controllers/character_counter_controller.js +271 -143
- 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 +77 -75
- package/dist/controllers/checkbox_controller.js.map +1 -1
- package/dist/controllers/clipboard_controller.d.ts +44 -7
- package/dist/controllers/clipboard_controller.js +187 -108
- package/dist/controllers/clipboard_controller.js.map +1 -1
- package/dist/controllers/collapsible_controller.d.ts +41 -5
- package/dist/controllers/collapsible_controller.js +374 -108
- package/dist/controllers/collapsible_controller.js.map +1 -1
- package/dist/controllers/color_picker_controller.d.ts +47 -14
- package/dist/controllers/color_picker_controller.js +183 -127
- package/dist/controllers/color_picker_controller.js.map +1 -1
- package/dist/controllers/combobox_controller.d.ts +33 -6
- package/dist/controllers/combobox_controller.js +426 -109
- package/dist/controllers/combobox_controller.js.map +1 -1
- package/dist/controllers/command_palette_controller.d.ts +45 -14
- package/dist/controllers/command_palette_controller.js +994 -350
- 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 +212 -125
- 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 +939 -245
- package/dist/controllers/confirm_controller.js.map +1 -1
- package/dist/controllers/context_menu_controller.d.ts +36 -3
- package/dist/controllers/context_menu_controller.js +275 -127
- 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 +75 -2
- package/dist/controllers/countdown_controller.js +235 -97
- package/dist/controllers/countdown_controller.js.map +1 -1
- package/dist/controllers/currency_input_controller.d.ts +93 -15
- package/dist/controllers/currency_input_controller.js +241 -118
- package/dist/controllers/currency_input_controller.js.map +1 -1
- package/dist/controllers/data_grid_controller.d.ts +78 -22
- package/dist/controllers/data_grid_controller.js +229 -135
- package/dist/controllers/data_grid_controller.js.map +1 -1
- package/dist/controllers/date_range_picker_controller.d.ts +64 -11
- package/dist/controllers/date_range_picker_controller.js +359 -165
- package/dist/controllers/date_range_picker_controller.js.map +1 -1
- package/dist/controllers/dialog_controller.d.ts +22 -3
- package/dist/controllers/dialog_controller.js +887 -225
- package/dist/controllers/dialog_controller.js.map +1 -1
- package/dist/controllers/direct_upload_controller.d.ts +24 -7
- package/dist/controllers/direct_upload_controller.js +80 -142
- package/dist/controllers/direct_upload_controller.js.map +1 -1
- package/dist/controllers/dirty_form_controller.d.ts +2 -2
- package/dist/controllers/dirty_form_controller.js +26 -47
- 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 +175 -20
- package/dist/controllers/dismissible_controller.js.map +1 -1
- package/dist/controllers/drawer_controller.d.ts +52 -16
- package/dist/controllers/drawer_controller.js +963 -336
- package/dist/controllers/drawer_controller.js.map +1 -1
- package/dist/controllers/dropdown_controller.d.ts +39 -3
- package/dist/controllers/dropdown_controller.js +342 -85
- package/dist/controllers/dropdown_controller.js.map +1 -1
- package/dist/controllers/editable_controller.d.ts +12 -1
- package/dist/controllers/editable_controller.js +182 -58
- package/dist/controllers/editable_controller.js.map +1 -1
- package/dist/controllers/empty_state_controller.js +67 -53
- package/dist/controllers/empty_state_controller.js.map +1 -1
- package/dist/controllers/file_dropzone_controller.d.ts +60 -18
- package/dist/controllers/file_dropzone_controller.js +426 -229
- package/dist/controllers/file_dropzone_controller.js.map +1 -1
- package/dist/controllers/filter_controller.d.ts +10 -4
- package/dist/controllers/filter_controller.js +78 -36
- package/dist/controllers/filter_controller.js.map +1 -1
- package/dist/controllers/flash_controller.d.ts +57 -14
- package/dist/controllers/flash_controller.js +328 -211
- package/dist/controllers/flash_controller.js.map +1 -1
- package/dist/controllers/focus_controller.d.ts +12 -3
- package/dist/controllers/focus_controller.js +654 -239
- package/dist/controllers/focus_controller.js.map +1 -1
- package/dist/controllers/form_field_controller.js +153 -136
- package/dist/controllers/form_field_controller.js.map +1 -1
- package/dist/controllers/form_validation_controller.js +29 -96
- package/dist/controllers/form_validation_controller.js.map +1 -1
- package/dist/controllers/frame_loading_controller.d.ts +41 -9
- package/dist/controllers/frame_loading_controller.js +263 -198
- package/dist/controllers/frame_loading_controller.js.map +1 -1
- package/dist/controllers/highlight_controller.d.ts +22 -2
- package/dist/controllers/highlight_controller.js +119 -70
- package/dist/controllers/highlight_controller.js.map +1 -1
- package/dist/controllers/hover_card_controller.d.ts +65 -4
- package/dist/controllers/hover_card_controller.js +335 -118
- package/dist/controllers/hover_card_controller.js.map +1 -1
- package/dist/controllers/idle_controller.d.ts +58 -4
- package/dist/controllers/idle_controller.js +316 -65
- package/dist/controllers/idle_controller.js.map +1 -1
- package/dist/controllers/input_mask_controller.d.ts +5 -2
- package/dist/controllers/input_mask_controller.js +116 -61
- 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 +147 -103
- package/dist/controllers/intersection_controller.js.map +1 -1
- package/dist/controllers/lazy_frame_controller.d.ts +16 -0
- package/dist/controllers/lazy_frame_controller.js +9 -56
- package/dist/controllers/lazy_frame_controller.js.map +1 -1
- package/dist/controllers/listbox_controller.d.ts +84 -10
- package/dist/controllers/listbox_controller.js +413 -153
- package/dist/controllers/listbox_controller.js.map +1 -1
- package/dist/controllers/local_time_controller.d.ts +1 -1
- package/dist/controllers/local_time_controller.js +54 -64
- 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 +70 -95
- package/dist/controllers/masonry_controller.js.map +1 -1
- package/dist/controllers/menu_controller.d.ts +40 -9
- package/dist/controllers/menu_controller.js +254 -167
- package/dist/controllers/menu_controller.js.map +1 -1
- package/dist/controllers/menubar_controller.d.ts +15 -4
- package/dist/controllers/menubar_controller.js +206 -331
- package/dist/controllers/menubar_controller.js.map +1 -1
- package/dist/controllers/meter_controller.d.ts +20 -0
- package/dist/controllers/meter_controller.js +159 -71
- package/dist/controllers/meter_controller.js.map +1 -1
- package/dist/controllers/multi_select_controller.d.ts +50 -7
- package/dist/controllers/multi_select_controller.js +797 -388
- package/dist/controllers/multi_select_controller.js.map +1 -1
- package/dist/controllers/navigation_menu_controller.d.ts +19 -1
- package/dist/controllers/navigation_menu_controller.js +156 -231
- 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 +373 -174
- 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 +241 -45
- package/dist/controllers/network_status_controller.js.map +1 -1
- package/dist/controllers/number_input_controller.d.ts +62 -10
- package/dist/controllers/number_input_controller.js +387 -227
- package/dist/controllers/number_input_controller.js.map +1 -1
- package/dist/controllers/optimistic_controller.js +28 -75
- package/dist/controllers/optimistic_controller.js.map +1 -1
- package/dist/controllers/otp_controller.d.ts +38 -7
- package/dist/controllers/otp_controller.js +344 -196
- package/dist/controllers/otp_controller.js.map +1 -1
- package/dist/controllers/overflow_indicator_controller.d.ts +13 -12
- package/dist/controllers/overflow_indicator_controller.js +167 -93
- package/dist/controllers/overflow_indicator_controller.js.map +1 -1
- package/dist/controllers/overflow_menu_controller.d.ts +66 -9
- package/dist/controllers/overflow_menu_controller.js +408 -277
- package/dist/controllers/overflow_menu_controller.js.map +1 -1
- package/dist/controllers/pagination_controller.d.ts +52 -15
- package/dist/controllers/pagination_controller.js +342 -120
- package/dist/controllers/pagination_controller.js.map +1 -1
- package/dist/controllers/password_reveal_controller.d.ts +60 -9
- package/dist/controllers/password_reveal_controller.js +424 -99
- package/dist/controllers/password_reveal_controller.js.map +1 -1
- package/dist/controllers/password_strength_controller.d.ts +14 -5
- package/dist/controllers/password_strength_controller.js +99 -153
- 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 +252 -96
- package/dist/controllers/persist_controller.js.map +1 -1
- package/dist/controllers/pointer_drag_controller.d.ts +19 -7
- package/dist/controllers/pointer_drag_controller.js +263 -164
- package/dist/controllers/pointer_drag_controller.js.map +1 -1
- package/dist/controllers/popover_controller.d.ts +46 -4
- package/dist/controllers/popover_controller.js +267 -94
- package/dist/controllers/popover_controller.js.map +1 -1
- package/dist/controllers/portal_controller.d.ts +44 -4
- package/dist/controllers/portal_controller.js +209 -72
- 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 -112
- package/dist/controllers/preview_guard_controller.js.map +1 -1
- package/dist/controllers/progress_controller.d.ts +11 -0
- package/dist/controllers/progress_controller.js +132 -59
- package/dist/controllers/progress_controller.js.map +1 -1
- package/dist/controllers/radio_group_controller.d.ts +19 -8
- package/dist/controllers/radio_group_controller.js +138 -133
- package/dist/controllers/radio_group_controller.js.map +1 -1
- package/dist/controllers/range_slider_controller.d.ts +92 -4
- package/dist/controllers/range_slider_controller.js +245 -112
- package/dist/controllers/range_slider_controller.js.map +1 -1
- package/dist/controllers/rating_controller.d.ts +33 -6
- package/dist/controllers/rating_controller.js +429 -194
- package/dist/controllers/rating_controller.js.map +1 -1
- package/dist/controllers/read_more_controller.d.ts +26 -2
- package/dist/controllers/read_more_controller.js +185 -47
- 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 +147 -135
- package/dist/controllers/reading_progress_controller.js.map +1 -1
- package/dist/controllers/relative_time_controller.d.ts +18 -4
- package/dist/controllers/relative_time_controller.js +145 -110
- 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} +97 -38
- package/dist/controllers/reset_on_restore_controller.js.map +1 -0
- package/dist/controllers/resizable_controller.d.ts +44 -4
- package/dist/controllers/resizable_controller.js +294 -118
- package/dist/controllers/resizable_controller.js.map +1 -1
- package/dist/controllers/roving_controller.js +39 -84
- 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 +428 -221
- package/dist/controllers/scroll_area_controller.js.map +1 -1
- package/dist/controllers/scroll_restore_controller.js +0 -53
- 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 +395 -146
- package/dist/controllers/scroll_visibility_controller.js.map +1 -1
- package/dist/controllers/scrollspy_controller.d.ts +9 -3
- package/dist/controllers/scrollspy_controller.js +181 -236
- package/dist/controllers/scrollspy_controller.js.map +1 -1
- package/dist/controllers/separator_controller.d.ts +59 -17
- package/dist/controllers/separator_controller.js +288 -132
- package/dist/controllers/separator_controller.js.map +1 -1
- package/dist/controllers/sidebar_controller.d.ts +81 -7
- package/dist/controllers/sidebar_controller.js +1071 -375
- package/dist/controllers/sidebar_controller.js.map +1 -1
- package/dist/controllers/skeleton_controller.d.ts +23 -0
- package/dist/controllers/skeleton_controller.js +248 -92
- package/dist/controllers/skeleton_controller.js.map +1 -1
- package/dist/controllers/slider_controller.d.ts +72 -8
- package/dist/controllers/slider_controller.js +211 -96
- package/dist/controllers/slider_controller.js.map +1 -1
- package/dist/controllers/smart_sticky_header_controller.d.ts +21 -3
- package/dist/controllers/smart_sticky_header_controller.js +163 -54
- package/dist/controllers/smart_sticky_header_controller.js.map +1 -1
- package/dist/controllers/sortable_controller.js +13 -110
- package/dist/controllers/sortable_controller.js.map +1 -1
- package/dist/controllers/spinner_controller.d.ts +39 -4
- package/dist/controllers/spinner_controller.js +277 -161
- package/dist/controllers/spinner_controller.js.map +1 -1
- package/dist/controllers/step_indicator_controller.d.ts +24 -17
- package/dist/controllers/step_indicator_controller.js +107 -53
- package/dist/controllers/step_indicator_controller.js.map +1 -1
- package/dist/controllers/stepper_controller.d.ts +43 -9
- package/dist/controllers/stepper_controller.js +171 -48
- package/dist/controllers/stepper_controller.js.map +1 -1
- package/dist/controllers/stick_to_bottom_controller.d.ts +25 -0
- package/dist/controllers/stick_to_bottom_controller.js +310 -116
- 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 +214 -49
- package/dist/controllers/sticky_observer_controller.js.map +1 -1
- package/dist/controllers/submit_once_controller.d.ts +22 -6
- package/dist/controllers/submit_once_controller.js +294 -192
- package/dist/controllers/submit_once_controller.js.map +1 -1
- package/dist/controllers/switch_controller.d.ts +40 -7
- package/dist/controllers/switch_controller.js +107 -43
- package/dist/controllers/switch_controller.js.map +1 -1
- package/dist/controllers/tabs_controller.d.ts +13 -1
- package/dist/controllers/tabs_controller.js +50 -25
- package/dist/controllers/tabs_controller.js.map +1 -1
- package/dist/controllers/tags_input_controller.d.ts +27 -3
- package/dist/controllers/tags_input_controller.js +290 -149
- package/dist/controllers/tags_input_controller.js.map +1 -1
- package/dist/controllers/textarea_autosize_controller.d.ts +10 -0
- package/dist/controllers/textarea_autosize_controller.js +71 -58
- package/dist/controllers/textarea_autosize_controller.js.map +1 -1
- package/dist/controllers/theme_controller.d.ts +37 -8
- package/dist/controllers/theme_controller.js +148 -80
- package/dist/controllers/theme_controller.js.map +1 -1
- package/dist/controllers/time_picker_controller.d.ts +18 -1
- package/dist/controllers/time_picker_controller.js +162 -81
- package/dist/controllers/time_picker_controller.js.map +1 -1
- package/dist/controllers/toast_controller.d.ts +85 -23
- package/dist/controllers/toast_controller.js +365 -193
- package/dist/controllers/toast_controller.js.map +1 -1
- package/dist/controllers/toggle_group_controller.d.ts +60 -9
- package/dist/controllers/toggle_group_controller.js +194 -99
- package/dist/controllers/toggle_group_controller.js.map +1 -1
- package/dist/controllers/toolbar_controller.js +92 -90
- package/dist/controllers/toolbar_controller.js.map +1 -1
- package/dist/controllers/tooltip_controller.d.ts +52 -2
- package/dist/controllers/tooltip_controller.js +185 -125
- 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 -115
- package/dist/controllers/transition_controller.js.map +1 -1
- package/dist/controllers/tree_view_controller.d.ts +59 -18
- package/dist/controllers/tree_view_controller.js +248 -290
- package/dist/controllers/tree_view_controller.js.map +1 -1
- package/dist/index.d.ts +33 -3
- package/dist/index.js +8649 -8572
- package/dist/index.js.map +1 -1
- package/dist/inspector/cli.d.ts +95 -9
- package/dist/inspector/cli.js +343 -77
- package/dist/inspector/cli.js.map +1 -1
- package/dist/inspector/cli_bin.js +390 -96
- package/dist/inspector/cli_bin.js.map +1 -1
- package/dist/inspector/examples.json +45 -45
- package/dist/inspector/manifest.json +1308 -155
- package/dist/positioning/index.d.ts +8 -0
- package/dist/positioning/index.js +148 -45
- package/dist/positioning/index.js.map +1 -1
- package/package.json +7 -7
- package/dist/controllers/reset_before_cache_controller.d.ts +0 -76
- package/dist/controllers/reset_before_cache_controller.js.map +0 -1
|
@@ -53,6 +53,18 @@ import { Controller } from '@hotwired/stimulus';
|
|
|
53
53
|
* exactly one layer. `Tab` lets the browser move focus first, then closes on
|
|
54
54
|
* the next task. An outside click or context-menu invocation closes without
|
|
55
55
|
* stealing focus from its destination.
|
|
56
|
+
* - A region or a menu that takes over — in one task, or after an earlier one
|
|
57
|
+
* leaves in a later task — carries the open state, and the menu that takes over
|
|
58
|
+
* an open one is placed where it was opened; focus stays where the swap left it.
|
|
59
|
+
* With no menu left the region reads closed and the menu leaves the Escape stack.
|
|
60
|
+
* One that stops resolving as the target gets back the `data-state` or `hidden` it
|
|
61
|
+
* carried before this controller wrote on it.
|
|
62
|
+
* - Each move of the open state is reported: `stimeo--context-menu:open` and
|
|
63
|
+
* `stimeo--context-menu:close` dispatch `{ reason: StateReason }`, after the
|
|
64
|
+
* state attributes are written. Both are informational, so neither is
|
|
65
|
+
* cancelable. A call that leaves the state where it already was, the
|
|
66
|
+
* normalization in {@link connect}, a region or a menu that takes over, and
|
|
67
|
+
* {@link disconnect} are all silent.
|
|
56
68
|
*
|
|
57
69
|
* Roving focus skips `hidden` and natively `disabled` items. An
|
|
58
70
|
* `aria-disabled="true"` item stays reachable by arrow keys — APG marks that
|
|
@@ -63,8 +75,11 @@ declare class ContextMenuController extends Controller<HTMLElement> {
|
|
|
63
75
|
#private;
|
|
64
76
|
static targets: string[];
|
|
65
77
|
static actions: readonly ["activate", "onItemKeydown", "onRegionKeydown", "open"];
|
|
78
|
+
static events: readonly ["close", "open"];
|
|
66
79
|
readonly regionTarget: HTMLElement;
|
|
80
|
+
readonly regionTargets: HTMLElement[];
|
|
67
81
|
readonly menuTarget: HTMLElement;
|
|
82
|
+
readonly menuTargets: HTMLElement[];
|
|
68
83
|
readonly itemTargets: HTMLButtonElement[];
|
|
69
84
|
readonly hasRegionTarget: boolean;
|
|
70
85
|
readonly hasMenuTarget: boolean;
|
|
@@ -72,11 +87,29 @@ declare class ContextMenuController extends Controller<HTMLElement> {
|
|
|
72
87
|
connect(): void;
|
|
73
88
|
/** Releases the listeners, stack membership, and pending Tab-close task. */
|
|
74
89
|
disconnect(): void;
|
|
90
|
+
/** Brings a region that arrives after connect to the open state. */
|
|
91
|
+
regionTargetConnected(): void;
|
|
75
92
|
/**
|
|
76
|
-
*
|
|
77
|
-
*
|
|
93
|
+
* Gives a region that no longer resolves as one its own `data-state` back — after
|
|
94
|
+
* `disconnect()` too, since dropping the identifier leaves the element on the page — and
|
|
95
|
+
* brings the region that stays to the open state.
|
|
78
96
|
*/
|
|
79
|
-
|
|
97
|
+
regionTargetDisconnected(region: HTMLElement): void;
|
|
98
|
+
/** Applies the open state to a menu that arrives after connect in front of the others. */
|
|
99
|
+
menuTargetConnected(): void;
|
|
100
|
+
/**
|
|
101
|
+
* Applies the open state to the menu left, then gives a menu that no longer resolves as the
|
|
102
|
+
* target its own `hidden` back — after `disconnect()` too, since dropping the identifier
|
|
103
|
+
* leaves the element on the page. The open state is read off the departing menu first,
|
|
104
|
+
* while it still carries it.
|
|
105
|
+
*/
|
|
106
|
+
menuTargetDisconnected(menu: HTMLElement): void;
|
|
107
|
+
/**
|
|
108
|
+
* Opens at a mouse event's coordinate, an explicit element's center, or the
|
|
109
|
+
* region's center with no argument. Opening focuses the new menu in all paths.
|
|
110
|
+
* An explicit element may be an anchor outside the controller.
|
|
111
|
+
*/
|
|
112
|
+
open(input?: Event | HTMLElement): void;
|
|
80
113
|
/** Keyboard entry on the region: `Shift+F10` / `ContextMenu` open at center. */
|
|
81
114
|
onRegionKeydown(event: KeyboardEvent): void;
|
|
82
115
|
/** Roving focus and closing keys inside the menu. */
|
|
@@ -1,14 +1,179 @@
|
|
|
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/arrow_step.ts
|
|
6
25
|
function isReservedArrowChord(event, allow = []) {
|
|
7
26
|
if (!event.key.startsWith("Arrow")) return false;
|
|
8
27
|
return event.altKey && !allow.includes("alt") || event.ctrlKey && !allow.includes("ctrl") || event.metaKey && !allow.includes("meta") || event.shiftKey && !allow.includes("shift");
|
|
9
28
|
}
|
|
10
29
|
|
|
11
|
-
|
|
30
|
+
function sharedRegistry(key, create) {
|
|
31
|
+
const symbol = Symbol.for(key);
|
|
32
|
+
const scope = globalThis;
|
|
33
|
+
const existing = scope[symbol];
|
|
34
|
+
if (existing !== void 0) return existing;
|
|
35
|
+
const registry = create();
|
|
36
|
+
Object.defineProperty(globalThis, symbol, { value: registry });
|
|
37
|
+
return registry;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function isAttributeValue(value) {
|
|
41
|
+
return value === null || typeof value === "string";
|
|
42
|
+
}
|
|
43
|
+
function readLeaseRecord(element, record) {
|
|
44
|
+
try {
|
|
45
|
+
const parsed = JSON.parse(element.getAttribute(record) ?? "null");
|
|
46
|
+
if (!Array.isArray(parsed)) return void 0;
|
|
47
|
+
const [original, written, ...rest] = parsed;
|
|
48
|
+
const linked = rest[0] !== false;
|
|
49
|
+
const beneath = linked ? rest : rest.slice(1);
|
|
50
|
+
if (!beneath.every((under) => typeof under === "string")) return void 0;
|
|
51
|
+
return isAttributeValue(original) && isAttributeValue(written) ? { original, written, linked, beneath } : void 0;
|
|
52
|
+
} catch {
|
|
53
|
+
return void 0;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
var AttributeLease = class _AttributeLease {
|
|
57
|
+
static #registry = sharedRegistry(
|
|
58
|
+
"stimeo-ui.attribute-lease.registry.v1",
|
|
59
|
+
() => ({ stacks: /* @__PURE__ */ new WeakMap(), names: /* @__PURE__ */ new Map() })
|
|
60
|
+
);
|
|
61
|
+
#attribute;
|
|
62
|
+
#record;
|
|
63
|
+
#base;
|
|
64
|
+
#held = /* @__PURE__ */ new Set();
|
|
65
|
+
constructor(attribute, owner, options = {}) {
|
|
66
|
+
this.#attribute = attribute;
|
|
67
|
+
this.#record = `data-${owner}-${attribute}-lease`;
|
|
68
|
+
this.#base = options.base === true;
|
|
69
|
+
const names = _AttributeLease.#registry.names;
|
|
70
|
+
names.set(attribute, (names.get(attribute) ?? /* @__PURE__ */ new Set()).add(this.#record));
|
|
71
|
+
}
|
|
72
|
+
write(element, value) {
|
|
73
|
+
const stack = this.#stack(element);
|
|
74
|
+
const hold = this.#take(stack) ?? this.#acquire(element, stack);
|
|
75
|
+
this.#held.add(element);
|
|
76
|
+
const shows = !this.#covered(element, stack, hold);
|
|
77
|
+
hold.written = value;
|
|
78
|
+
this.#mark(element, stack);
|
|
79
|
+
if (shows) this.#reflect(element, value);
|
|
80
|
+
}
|
|
81
|
+
return(element) {
|
|
82
|
+
const held = this.#held.delete(element);
|
|
83
|
+
const known = _AttributeLease.#registry.stacks.get(element)?.has(this.#attribute);
|
|
84
|
+
const stack = held || known || element.hasAttribute(this.#record) ? this.#stack(element) : [];
|
|
85
|
+
const hold = this.#take(stack);
|
|
86
|
+
if (!hold) return false;
|
|
87
|
+
const carries = element.getAttribute(this.#attribute) === hold.written;
|
|
88
|
+
const owns = carries && !this.#covered(element, stack, hold);
|
|
89
|
+
const index = stack.indexOf(hold);
|
|
90
|
+
stack.splice(index, 1);
|
|
91
|
+
const above = stack[index];
|
|
92
|
+
if (above) above.linked &&= hold.linked;
|
|
93
|
+
this.#mark(element, stack);
|
|
94
|
+
if (!owns) return true;
|
|
95
|
+
const below = hold.linked ? stack[index - 1] : void 0;
|
|
96
|
+
this.#reflect(element, below ? below.written : hold.original);
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
returnAll() {
|
|
100
|
+
for (const element of Array.from(this.#held)) this.return(element);
|
|
101
|
+
}
|
|
102
|
+
#stack(element) {
|
|
103
|
+
const stacks = _AttributeLease.#registry.stacks;
|
|
104
|
+
const byAttribute = stacks.get(element) ?? /* @__PURE__ */ new Map();
|
|
105
|
+
stacks.set(element, byAttribute);
|
|
106
|
+
const stack = byAttribute.get(this.#attribute) ?? this.#seed(element);
|
|
107
|
+
byAttribute.set(this.#attribute, stack);
|
|
108
|
+
return stack;
|
|
109
|
+
}
|
|
110
|
+
#seed(element) {
|
|
111
|
+
const found = [];
|
|
112
|
+
for (const record of _AttributeLease.#registry.names.get(this.#attribute) ?? []) {
|
|
113
|
+
const read = readLeaseRecord(element, record);
|
|
114
|
+
if (read) found.push({ ...read, record });
|
|
115
|
+
}
|
|
116
|
+
const depth = ({ beneath }) => beneath.filter((name) => found.some((other) => other.record === name)).length;
|
|
117
|
+
return found.sort((one, other) => depth(one) - depth(other)).map(({ record, written, original, linked }, index) => ({
|
|
118
|
+
lease: null,
|
|
119
|
+
record,
|
|
120
|
+
written,
|
|
121
|
+
original,
|
|
122
|
+
linked: linked && index > 0
|
|
123
|
+
}));
|
|
124
|
+
}
|
|
125
|
+
#take(stack) {
|
|
126
|
+
const hold = stack.find((held) => held.lease === this) ?? stack.find((held) => held.lease === null && held.record === this.#record);
|
|
127
|
+
if (hold) hold.lease = this;
|
|
128
|
+
return hold;
|
|
129
|
+
}
|
|
130
|
+
#acquire(element, stack) {
|
|
131
|
+
const current = element.getAttribute(this.#attribute);
|
|
132
|
+
const top = stack.at(-1);
|
|
133
|
+
const bottom = stack[0];
|
|
134
|
+
const over = top?.written === current ? top : void 0;
|
|
135
|
+
const from = this.#base ? bottom : over;
|
|
136
|
+
const hold = {
|
|
137
|
+
lease: this,
|
|
138
|
+
record: this.#record,
|
|
139
|
+
written: current,
|
|
140
|
+
original: from ? from.original : current,
|
|
141
|
+
linked: !this.#base && over !== void 0
|
|
142
|
+
};
|
|
143
|
+
if (this.#base && bottom) {
|
|
144
|
+
bottom.linked = true;
|
|
145
|
+
stack.unshift(hold);
|
|
146
|
+
} else stack.push(hold);
|
|
147
|
+
return hold;
|
|
148
|
+
}
|
|
149
|
+
#covered(element, stack, hold) {
|
|
150
|
+
const current = element.getAttribute(this.#attribute);
|
|
151
|
+
return stack.slice(stack.indexOf(hold) + 1).some((above) => above.written === current);
|
|
152
|
+
}
|
|
153
|
+
#mark(element, stack) {
|
|
154
|
+
const records = /* @__PURE__ */ new Map([[this.#record, null]]);
|
|
155
|
+
stack.forEach((hold, index) => {
|
|
156
|
+
const names = stack.slice(0, index).map((under) => under.record);
|
|
157
|
+
const beneath = [...new Set(names.reverse())].filter((name) => name !== hold.record);
|
|
158
|
+
const marker = hold.linked || beneath.length === 0 ? [] : [false];
|
|
159
|
+
records.set(
|
|
160
|
+
hold.record,
|
|
161
|
+
hold.written === hold.original && beneath.length === 0 && index === stack.length - 1 ? null : JSON.stringify([hold.original, hold.written, ...marker, ...beneath])
|
|
162
|
+
);
|
|
163
|
+
});
|
|
164
|
+
for (const [name, recorded] of records) {
|
|
165
|
+
if (element.getAttribute(name) === recorded) continue;
|
|
166
|
+
if (recorded === null) element.removeAttribute(name);
|
|
167
|
+
else element.setAttribute(name, recorded);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
#reflect(element, value) {
|
|
171
|
+
if (element.getAttribute(this.#attribute) === value) return;
|
|
172
|
+
if (value === null) element.removeAttribute(this.#attribute);
|
|
173
|
+
else element.setAttribute(this.#attribute, value);
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
|
|
12
177
|
function claimsWhileFocusWithin(element) {
|
|
13
178
|
return () => {
|
|
14
179
|
const active = element.ownerDocument.activeElement;
|
|
@@ -16,17 +181,11 @@ function claimsWhileFocusWithin(element) {
|
|
|
16
181
|
};
|
|
17
182
|
}
|
|
18
183
|
var EscapeLayer = class _EscapeLayer {
|
|
19
|
-
static #registries =
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
#claims = null;
|
|
25
|
-
/**
|
|
26
|
-
* Activates this layer at the top of its document's Escape stack, installing
|
|
27
|
-
* the document's shared resolver listener if this is its first layer.
|
|
28
|
-
* Re-activating an already-active layer moves it to the top.
|
|
29
|
-
*/
|
|
184
|
+
static #registries = sharedRegistry(
|
|
185
|
+
"stimeo-ui.escape-layer.registry.v1",
|
|
186
|
+
() => /* @__PURE__ */ new WeakMap()
|
|
187
|
+
);
|
|
188
|
+
#active = null;
|
|
30
189
|
activate(ownerDocument = document, options) {
|
|
31
190
|
this.deactivate();
|
|
32
191
|
let registry = _EscapeLayer.#registries.get(ownerDocument);
|
|
@@ -35,44 +194,31 @@ var EscapeLayer = class _EscapeLayer {
|
|
|
35
194
|
_EscapeLayer.#registries.set(ownerDocument, registry);
|
|
36
195
|
ownerDocument.addEventListener("keydown", registry.onKeydown);
|
|
37
196
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
this.#
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Removes this layer from its document's Escape stack, uninstalling the
|
|
45
|
-
* shared listener when the stack empties. Safe to call when inactive.
|
|
46
|
-
*/
|
|
197
|
+
const entry = { onDismiss: options.onDismiss, claims: options.claims ?? null };
|
|
198
|
+
registry.stack.push(entry);
|
|
199
|
+
this.#active = { document: ownerDocument, entry };
|
|
200
|
+
}
|
|
47
201
|
deactivate() {
|
|
48
|
-
const
|
|
49
|
-
if (!
|
|
50
|
-
const registry = _EscapeLayer.#registries.get(
|
|
202
|
+
const active = this.#active;
|
|
203
|
+
if (!active) return;
|
|
204
|
+
const registry = _EscapeLayer.#registries.get(active.document);
|
|
51
205
|
if (registry) {
|
|
52
|
-
const index = registry.stack.lastIndexOf(
|
|
206
|
+
const index = registry.stack.lastIndexOf(active.entry);
|
|
53
207
|
if (index >= 0) registry.stack.splice(index, 1);
|
|
54
208
|
if (registry.stack.length === 0) {
|
|
55
|
-
|
|
56
|
-
_EscapeLayer.#registries.delete(
|
|
209
|
+
active.document.removeEventListener("keydown", registry.onKeydown);
|
|
210
|
+
_EscapeLayer.#registries.delete(active.document);
|
|
57
211
|
}
|
|
58
212
|
}
|
|
59
|
-
this.#
|
|
60
|
-
|
|
61
|
-
this.#claims = null;
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Whether this active layer would own a press right now: it is the topmost
|
|
65
|
-
* layer whose {@link EscapeLayerOptions.claims} passes. Exposed for tests
|
|
66
|
-
* and diagnostics — production dismissal goes through the shared listener.
|
|
67
|
-
*/
|
|
213
|
+
this.#active = null;
|
|
214
|
+
}
|
|
68
215
|
get ownsEscape() {
|
|
69
|
-
const
|
|
70
|
-
if (!
|
|
71
|
-
const registry = _EscapeLayer.#registries.get(
|
|
216
|
+
const active = this.#active;
|
|
217
|
+
if (!active) return false;
|
|
218
|
+
const registry = _EscapeLayer.#registries.get(active.document);
|
|
72
219
|
if (!registry) return false;
|
|
73
|
-
return _EscapeLayer.#resolveOwner(registry.stack) ===
|
|
220
|
+
return _EscapeLayer.#resolveOwner(registry.stack) === active.entry;
|
|
74
221
|
}
|
|
75
|
-
/** Builds a document's registry with its shared resolver listener. */
|
|
76
222
|
static #createRegistry() {
|
|
77
223
|
const registry = {
|
|
78
224
|
stack: [],
|
|
@@ -81,60 +227,40 @@ var EscapeLayer = class _EscapeLayer {
|
|
|
81
227
|
const owner = _EscapeLayer.#resolveOwner(registry.stack);
|
|
82
228
|
if (!owner) return;
|
|
83
229
|
event.preventDefault();
|
|
84
|
-
owner
|
|
230
|
+
owner.onDismiss();
|
|
85
231
|
}
|
|
86
232
|
};
|
|
87
233
|
return registry;
|
|
88
234
|
}
|
|
89
|
-
/** The topmost stack layer whose claims predicate passes, or `null`. */
|
|
90
235
|
static #resolveOwner(stack) {
|
|
91
236
|
for (let index = stack.length - 1; index >= 0; index--) {
|
|
92
237
|
const layer = stack[index];
|
|
93
238
|
if (!layer) continue;
|
|
94
|
-
if (layer
|
|
239
|
+
if (layer.claims && !layer.claims()) continue;
|
|
95
240
|
return layer;
|
|
96
241
|
}
|
|
97
242
|
return null;
|
|
98
243
|
}
|
|
99
244
|
};
|
|
100
245
|
|
|
101
|
-
// src/utils/safe_timeout.ts
|
|
102
246
|
var TimerRegistry = class {
|
|
103
|
-
/** Live timer ids that have not yet been cleared (or, for timeouts, fired). */
|
|
104
247
|
ids = /* @__PURE__ */ new Set();
|
|
105
|
-
/**
|
|
106
|
-
* Cancels a single tracked timer.
|
|
107
|
-
*
|
|
108
|
-
* No-ops if the id is unknown (already cleared, fired, or never owned by this
|
|
109
|
-
* registry), so callers can clear defensively without guarding.
|
|
110
|
-
*/
|
|
111
248
|
clear(id) {
|
|
112
249
|
if (this.ids.delete(id)) {
|
|
113
250
|
this.cancel(id);
|
|
114
251
|
}
|
|
115
252
|
}
|
|
116
|
-
/**
|
|
117
|
-
* Cancels every tracked timer. Call this from a controller's `disconnect()`
|
|
118
|
-
* to guarantee no timer outlives the element.
|
|
119
|
-
*/
|
|
120
253
|
clearAll() {
|
|
121
254
|
for (const id of this.ids) {
|
|
122
255
|
this.cancel(id);
|
|
123
256
|
}
|
|
124
257
|
this.ids.clear();
|
|
125
258
|
}
|
|
126
|
-
/** Number of timers currently tracked (pending). */
|
|
127
259
|
get size() {
|
|
128
260
|
return this.ids.size;
|
|
129
261
|
}
|
|
130
262
|
};
|
|
131
263
|
var SafeTimeout = class extends TimerRegistry {
|
|
132
|
-
/**
|
|
133
|
-
* Schedules `callback` after `delay` ms and returns the timer id.
|
|
134
|
-
*
|
|
135
|
-
* The id is removed from the registry automatically when the timeout fires,
|
|
136
|
-
* so {@link TimerRegistry.size | size} reflects only still-pending timers.
|
|
137
|
-
*/
|
|
138
264
|
set(callback, delay) {
|
|
139
265
|
const id = this.schedule(() => {
|
|
140
266
|
this.ids.delete(id);
|
|
@@ -151,46 +277,68 @@ var SafeTimeout = class extends TimerRegistry {
|
|
|
151
277
|
}
|
|
152
278
|
};
|
|
153
279
|
|
|
154
|
-
|
|
155
|
-
|
|
280
|
+
var ContextMenuController = class _ContextMenuController extends Controller {
|
|
281
|
+
static #COORDINATE = ["--stimeo--context-menu-x", "--stimeo--context-menu-y"];
|
|
156
282
|
static targets = ["region", "menu", "item"];
|
|
157
283
|
static actions = ["activate", "onItemKeydown", "onRegionKeydown", "open"];
|
|
284
|
+
static events = ["close", "open"];
|
|
158
285
|
#timers = new SafeTimeout();
|
|
159
|
-
/** Escape-stack membership while open; the shared resolver dismisses via it. */
|
|
160
286
|
#escapeLayer = new EscapeLayer();
|
|
161
|
-
|
|
287
|
+
#menuHidden = new AttributeLease("hidden", this.identifier);
|
|
288
|
+
#regionState = new AttributeLease("data-state", this.identifier);
|
|
289
|
+
#menu = null;
|
|
290
|
+
#connected = false;
|
|
291
|
+
#reporting = false;
|
|
162
292
|
connect() {
|
|
163
|
-
this.#closeMenu();
|
|
293
|
+
this.#closeMenu("api");
|
|
164
294
|
this.element.addEventListener("click", this.#onItemClickCapture, true);
|
|
165
295
|
document.addEventListener("click", this.#onOutsidePointer, true);
|
|
166
296
|
document.addEventListener("contextmenu", this.#onOutsidePointer, true);
|
|
297
|
+
this.#connected = true;
|
|
298
|
+
this.#reporting = true;
|
|
167
299
|
}
|
|
168
|
-
/** Releases the listeners, stack membership, and pending Tab-close task. */
|
|
169
300
|
disconnect() {
|
|
301
|
+
this.#connected = false;
|
|
302
|
+
this.#reporting = false;
|
|
170
303
|
this.#timers.clearAll();
|
|
171
304
|
this.#escapeLayer.deactivate();
|
|
172
305
|
this.element.removeEventListener("click", this.#onItemClickCapture, true);
|
|
173
306
|
document.removeEventListener("click", this.#onOutsidePointer, true);
|
|
174
307
|
document.removeEventListener("contextmenu", this.#onOutsidePointer, true);
|
|
175
308
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
309
|
+
regionTargetConnected() {
|
|
310
|
+
if (this.#connected) this.#reflectRegion();
|
|
311
|
+
}
|
|
312
|
+
regionTargetDisconnected(region) {
|
|
313
|
+
if (!this.regionTargets.includes(region)) this.#regionState.return(region);
|
|
314
|
+
if (this.#connected) this.#reflectRegion();
|
|
315
|
+
}
|
|
316
|
+
menuTargetConnected() {
|
|
317
|
+
this.#adoptMenu();
|
|
318
|
+
}
|
|
319
|
+
menuTargetDisconnected(menu) {
|
|
320
|
+
this.#adoptMenu();
|
|
321
|
+
if (!this.menuTargets.includes(menu)) this.#menuHidden.return(menu);
|
|
322
|
+
}
|
|
323
|
+
open(input) {
|
|
324
|
+
const source = actionSource(input);
|
|
325
|
+
source.event?.preventDefault();
|
|
326
|
+
if (source.event instanceof MouseEvent) {
|
|
327
|
+
this.#openAt(source.event.clientX, source.event.clientY, source.reason);
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
const anchor = source.host ?? (this.hasRegionTarget ? this.regionTarget : null);
|
|
331
|
+
const rect = anchor?.getBoundingClientRect() ?? { left: 0, top: 0, width: 0, height: 0 };
|
|
332
|
+
this.#openAt(rect.left + rect.width / 2, rect.top + rect.height / 2, source.reason);
|
|
183
333
|
}
|
|
184
|
-
/** Keyboard entry on the region: `Shift+F10` / `ContextMenu` open at center. */
|
|
185
334
|
onRegionKeydown(event) {
|
|
186
335
|
if (event.defaultPrevented) return;
|
|
187
336
|
const isContextKey = event.key === "ContextMenu" || event.key === "F10" && event.shiftKey;
|
|
188
337
|
if (!isContextKey) return;
|
|
189
338
|
event.preventDefault();
|
|
190
339
|
const rect = this.hasRegionTarget ? this.regionTarget.getBoundingClientRect() : { left: 0, top: 0, width: 0, height: 0 };
|
|
191
|
-
this.#openAt(rect.left + rect.width / 2, rect.top + rect.height / 2);
|
|
340
|
+
this.#openAt(rect.left + rect.width / 2, rect.top + rect.height / 2, stateReasonFor(event));
|
|
192
341
|
}
|
|
193
|
-
/** Roving focus and closing keys inside the menu. */
|
|
194
342
|
onItemKeydown(event) {
|
|
195
343
|
if (event.defaultPrevented) return;
|
|
196
344
|
if (isReservedArrowChord(event)) return;
|
|
@@ -221,55 +369,71 @@ var ContextMenuController = class extends Controller {
|
|
|
221
369
|
break;
|
|
222
370
|
case "Tab":
|
|
223
371
|
this.#timers.clearAll();
|
|
224
|
-
this.#timers.set(() => this.#closeMenu(), 0);
|
|
372
|
+
this.#timers.set(() => this.#closeMenu("focus"), 0);
|
|
225
373
|
break;
|
|
226
374
|
}
|
|
227
375
|
}
|
|
228
|
-
/** Closes after an item is activated and restores focus to the region. */
|
|
229
376
|
activate() {
|
|
230
|
-
this.#closeAndRestore();
|
|
377
|
+
this.#closeAndRestore("select");
|
|
231
378
|
}
|
|
232
|
-
|
|
233
|
-
#openAt(x, y) {
|
|
379
|
+
#openAt(x, y, reason) {
|
|
234
380
|
if (!this.hasMenuTarget) return;
|
|
235
381
|
this.#timers.clearAll();
|
|
382
|
+
const menu = this.menuTarget;
|
|
383
|
+
const was = this.#isOpen;
|
|
236
384
|
this.#escapeLayer.activate(document, {
|
|
237
|
-
onDismiss: () => this.#closeAndRestore(),
|
|
385
|
+
onDismiss: () => this.#closeAndRestore("escape"),
|
|
238
386
|
claims: claimsWhileFocusWithin(this.element)
|
|
239
387
|
});
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
this.
|
|
243
|
-
|
|
388
|
+
menu.style.setProperty(_ContextMenuController.#COORDINATE[0], `${x}px`);
|
|
389
|
+
menu.style.setProperty(_ContextMenuController.#COORDINATE[1], `${y}px`);
|
|
390
|
+
this.#menuHidden.write(menu, null);
|
|
391
|
+
this.#menu = menu;
|
|
392
|
+
if (this.hasRegionTarget) this.#regionState.write(this.regionTarget, "open");
|
|
393
|
+
if (!was && this.#reporting) this.dispatch("open", { detail: { reason }, cancelable: false });
|
|
394
|
+
if (!this.#isOpen) return;
|
|
244
395
|
this.#navigableItems[0]?.focus();
|
|
245
396
|
}
|
|
246
|
-
|
|
247
|
-
#closeMenu() {
|
|
397
|
+
#closeMenu(reason) {
|
|
248
398
|
this.#timers.clearAll();
|
|
249
399
|
this.#escapeLayer.deactivate();
|
|
250
|
-
|
|
251
|
-
this
|
|
252
|
-
if (
|
|
400
|
+
const menu = this.hasMenuTarget ? this.menuTarget : null;
|
|
401
|
+
this.#menu = menu;
|
|
402
|
+
if (!menu) return;
|
|
403
|
+
const was = this.#isOpen;
|
|
404
|
+
this.#menuHidden.write(menu, "");
|
|
405
|
+
if (this.hasRegionTarget) this.#regionState.write(this.regionTarget, "closed");
|
|
406
|
+
if (was && this.#reporting) this.dispatch("close", { detail: { reason }, cancelable: false });
|
|
407
|
+
}
|
|
408
|
+
#adoptMenu() {
|
|
409
|
+
if (!this.#connected) return;
|
|
410
|
+
const menu = this.hasMenuTarget ? this.menuTarget : null;
|
|
411
|
+
const previous = this.#menu;
|
|
412
|
+
this.#menu = menu;
|
|
413
|
+
if (!menu) {
|
|
414
|
+
this.#escapeLayer.deactivate();
|
|
415
|
+
} else if (menu !== previous) {
|
|
416
|
+
const open = previous !== null && !previous.hidden;
|
|
417
|
+
if (open) {
|
|
418
|
+
for (const property of _ContextMenuController.#COORDINATE) {
|
|
419
|
+
menu.style.setProperty(property, previous.style.getPropertyValue(property));
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
this.#menuHidden.write(menu, open ? null : "");
|
|
423
|
+
}
|
|
424
|
+
this.#reflectRegion();
|
|
425
|
+
}
|
|
426
|
+
#reflectRegion() {
|
|
427
|
+
if (!this.hasRegionTarget) return;
|
|
428
|
+
this.#regionState.write(this.regionTarget, this.#isOpen ? "open" : "closed");
|
|
253
429
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
this.#closeMenu();
|
|
430
|
+
#closeAndRestore(reason) {
|
|
431
|
+
this.#closeMenu(reason);
|
|
257
432
|
if (this.hasRegionTarget) this.regionTarget.focus();
|
|
258
433
|
}
|
|
259
|
-
/**
|
|
260
|
-
* Closes when a click or context-menu invocation lands outside this instance.
|
|
261
|
-
* Both subscriptions observe in the capture phase, so the target is judged
|
|
262
|
-
* against the tree the user actually pressed even when a consumer handler
|
|
263
|
-
* detaches it, and application code that stops bubbling cannot leave the menu
|
|
264
|
-
* open.
|
|
265
|
-
*/
|
|
266
434
|
#onOutsidePointer = (event) => {
|
|
267
|
-
if (this.#isOpen && !this.element.contains(event.target)) this.#closeMenu();
|
|
435
|
+
if (this.#isOpen && !this.element.contains(event.target)) this.#closeMenu("outside");
|
|
268
436
|
};
|
|
269
|
-
/**
|
|
270
|
-
* Captures clicks so `aria-disabled` commands cannot reach consumer handlers.
|
|
271
|
-
* Native Enter/Space activation also synthesizes a click and is blocked here.
|
|
272
|
-
*/
|
|
273
437
|
#onItemClickCapture = (event) => {
|
|
274
438
|
const target = event.target;
|
|
275
439
|
if (!(target instanceof Node)) return;
|
|
@@ -280,33 +444,17 @@ var ContextMenuController = class extends Controller {
|
|
|
280
444
|
event.preventDefault();
|
|
281
445
|
event.stopImmediatePropagation();
|
|
282
446
|
};
|
|
283
|
-
/** Menu items eligible for roving focus (excludes hidden / natively disabled). */
|
|
284
447
|
get #navigableItems() {
|
|
285
448
|
return this.itemTargets.filter((item) => this.#isNavigable(item));
|
|
286
449
|
}
|
|
287
|
-
/**
|
|
288
|
-
* An item can take roving focus unless it is `hidden` or a natively `disabled`
|
|
289
|
-
* form control. CSS-only visibility is not detectable here and is the
|
|
290
|
-
* consumer's responsibility.
|
|
291
|
-
*
|
|
292
|
-
* **`aria-disabled="true"` stays reachable.** APG separates the two attributes
|
|
293
|
-
* by intent: `disabled` is for controls whose existence can be inferred from a
|
|
294
|
-
* neighbour (a greyed Next next to a Prev), while `aria-disabled` marks a
|
|
295
|
-
* control that must stay *discoverable* — and it names menu items as the
|
|
296
|
-
* example. Skipping it would hide the command's existence from a keyboard user
|
|
297
|
-
* entirely. Activation is suppressed separately, so the item announces itself
|
|
298
|
-
* and does nothing.
|
|
299
|
-
*/
|
|
300
450
|
#isNavigable(item) {
|
|
301
451
|
if (item.hasAttribute("hidden")) return false;
|
|
302
452
|
return !item.disabled;
|
|
303
453
|
}
|
|
304
|
-
/** Whether the menu is currently visible. */
|
|
305
454
|
get #isOpen() {
|
|
306
455
|
return this.hasMenuTarget && !this.menuTarget.hidden;
|
|
307
456
|
}
|
|
308
457
|
};
|
|
309
458
|
|
|
310
459
|
export { ContextMenuController };
|
|
311
|
-
//# sourceMappingURL=context_menu_controller.js.map
|
|
312
460
|
//# sourceMappingURL=context_menu_controller.js.map
|