stimeo-ui 0.15.0 → 0.16.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 +178 -0
- package/README.md +120 -0
- package/dist/cable/index.js +63 -15
- package/dist/cable/index.js.map +1 -1
- package/dist/controllers/accordion_controller.d.ts +30 -3
- package/dist/controllers/accordion_controller.js +98 -9
- package/dist/controllers/accordion_controller.js.map +1 -1
- package/dist/controllers/alert_dialog_controller.d.ts +8 -7
- package/dist/controllers/alert_dialog_controller.js.map +1 -1
- package/dist/controllers/announcer_controller.d.ts +3 -2
- package/dist/controllers/announcer_controller.js +8 -1
- package/dist/controllers/announcer_controller.js.map +1 -1
- package/dist/controllers/auto_submit_controller.js +43 -2
- package/dist/controllers/auto_submit_controller.js.map +1 -1
- package/dist/controllers/avatar_controller.js +1 -1
- package/dist/controllers/avatar_controller.js.map +1 -1
- package/dist/controllers/breadcrumb_controller.d.ts +10 -7
- package/dist/controllers/breadcrumb_controller.js +9 -8
- package/dist/controllers/breadcrumb_controller.js.map +1 -1
- package/dist/controllers/bulk_select_controller.d.ts +4 -4
- package/dist/controllers/bulk_select_controller.js +7 -6
- package/dist/controllers/bulk_select_controller.js.map +1 -1
- package/dist/controllers/calendar_controller.d.ts +85 -23
- package/dist/controllers/calendar_controller.js +323 -123
- package/dist/controllers/calendar_controller.js.map +1 -1
- package/dist/controllers/carousel_controller.d.ts +67 -22
- package/dist/controllers/carousel_controller.js +263 -38
- package/dist/controllers/carousel_controller.js.map +1 -1
- package/dist/controllers/character_counter_controller.d.ts +1 -1
- package/dist/controllers/character_counter_controller.js +40 -2
- package/dist/controllers/character_counter_controller.js.map +1 -1
- package/dist/controllers/checkbox_controller.js +54 -10
- package/dist/controllers/checkbox_controller.js.map +1 -1
- package/dist/controllers/clipboard_controller.d.ts +28 -2
- package/dist/controllers/clipboard_controller.js +63 -5
- package/dist/controllers/clipboard_controller.js.map +1 -1
- package/dist/controllers/collapsible_controller.d.ts +25 -1
- package/dist/controllers/collapsible_controller.js +99 -14
- package/dist/controllers/collapsible_controller.js.map +1 -1
- package/dist/controllers/color_picker_controller.d.ts +29 -8
- package/dist/controllers/color_picker_controller.js +80 -34
- package/dist/controllers/color_picker_controller.js.map +1 -1
- package/dist/controllers/combobox_controller.d.ts +10 -1
- package/dist/controllers/combobox_controller.js +106 -16
- package/dist/controllers/combobox_controller.js.map +1 -1
- package/dist/controllers/command_palette_controller.d.ts +3 -3
- package/dist/controllers/command_palette_controller.js +35 -3
- package/dist/controllers/command_palette_controller.js.map +1 -1
- package/dist/controllers/conditional_fields_controller.js +56 -11
- package/dist/controllers/conditional_fields_controller.js.map +1 -1
- package/dist/controllers/confirm_controller.js +3 -0
- package/dist/controllers/confirm_controller.js.map +1 -1
- package/dist/controllers/context_menu_controller.d.ts +6 -0
- package/dist/controllers/context_menu_controller.js +32 -12
- package/dist/controllers/context_menu_controller.js.map +1 -1
- package/dist/controllers/countdown_controller.d.ts +30 -1
- package/dist/controllers/countdown_controller.js +129 -26
- package/dist/controllers/countdown_controller.js.map +1 -1
- package/dist/controllers/currency_input_controller.d.ts +74 -15
- package/dist/controllers/currency_input_controller.js +205 -58
- package/dist/controllers/currency_input_controller.js.map +1 -1
- package/dist/controllers/data_grid_controller.d.ts +63 -18
- package/dist/controllers/data_grid_controller.js +195 -29
- package/dist/controllers/data_grid_controller.js.map +1 -1
- package/dist/controllers/date_range_picker_controller.d.ts +34 -6
- package/dist/controllers/date_range_picker_controller.js +139 -27
- package/dist/controllers/date_range_picker_controller.js.map +1 -1
- package/dist/controllers/dialog_controller.d.ts +10 -3
- package/dist/controllers/dialog_controller.js +35 -8
- package/dist/controllers/dialog_controller.js.map +1 -1
- package/dist/controllers/direct_upload_controller.js +22 -4
- 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 +14 -1
- package/dist/controllers/dirty_form_controller.js.map +1 -1
- package/dist/controllers/dismissible_controller.js +1 -0
- package/dist/controllers/dismissible_controller.js.map +1 -1
- package/dist/controllers/drawer_controller.d.ts +18 -10
- package/dist/controllers/drawer_controller.js +54 -19
- package/dist/controllers/drawer_controller.js.map +1 -1
- package/dist/controllers/dropdown_controller.d.ts +9 -3
- package/dist/controllers/dropdown_controller.js +36 -9
- package/dist/controllers/dropdown_controller.js.map +1 -1
- package/dist/controllers/editable_controller.js +34 -0
- package/dist/controllers/editable_controller.js.map +1 -1
- package/dist/controllers/file_dropzone_controller.js +144 -51
- 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 +20 -6
- package/dist/controllers/filter_controller.js.map +1 -1
- package/dist/controllers/flash_controller.d.ts +26 -6
- package/dist/controllers/flash_controller.js +305 -46
- package/dist/controllers/flash_controller.js.map +1 -1
- package/dist/controllers/focus_controller.js +1 -0
- package/dist/controllers/focus_controller.js.map +1 -1
- package/dist/controllers/form_field_controller.js +7 -5
- package/dist/controllers/form_field_controller.js.map +1 -1
- package/dist/controllers/form_validation_controller.js +19 -13
- package/dist/controllers/form_validation_controller.js.map +1 -1
- package/dist/controllers/frame_loading_controller.js +16 -2
- package/dist/controllers/frame_loading_controller.js.map +1 -1
- package/dist/controllers/highlight_controller.js +19 -2
- package/dist/controllers/highlight_controller.js.map +1 -1
- package/dist/controllers/hover_card_controller.d.ts +10 -2
- package/dist/controllers/hover_card_controller.js +40 -14
- package/dist/controllers/hover_card_controller.js.map +1 -1
- package/dist/controllers/idle_controller.d.ts +16 -3
- package/dist/controllers/idle_controller.js +90 -5
- 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 +65 -9
- package/dist/controllers/input_mask_controller.js.map +1 -1
- package/dist/controllers/intersection_controller.js +3 -0
- package/dist/controllers/intersection_controller.js.map +1 -1
- package/dist/controllers/lazy_frame_controller.js +11 -2
- package/dist/controllers/lazy_frame_controller.js.map +1 -1
- package/dist/controllers/listbox_controller.d.ts +50 -8
- package/dist/controllers/listbox_controller.js +203 -45
- package/dist/controllers/listbox_controller.js.map +1 -1
- package/dist/controllers/masonry_controller.js +0 -2
- package/dist/controllers/masonry_controller.js.map +1 -1
- package/dist/controllers/menu_controller.d.ts +9 -3
- package/dist/controllers/menu_controller.js +45 -16
- package/dist/controllers/menu_controller.js.map +1 -1
- package/dist/controllers/menubar_controller.d.ts +11 -0
- package/dist/controllers/menubar_controller.js +58 -24
- package/dist/controllers/menubar_controller.js.map +1 -1
- package/dist/controllers/meter_controller.js +9 -5
- package/dist/controllers/meter_controller.js.map +1 -1
- package/dist/controllers/multi_select_controller.d.ts +18 -3
- package/dist/controllers/multi_select_controller.js +278 -104
- package/dist/controllers/multi_select_controller.js.map +1 -1
- package/dist/controllers/navigation_menu_controller.d.ts +11 -0
- package/dist/controllers/navigation_menu_controller.js +48 -15
- package/dist/controllers/navigation_menu_controller.js.map +1 -1
- package/dist/controllers/nested_form_controller.js +37 -8
- package/dist/controllers/nested_form_controller.js.map +1 -1
- package/dist/controllers/network_status_controller.js +9 -1
- package/dist/controllers/network_status_controller.js.map +1 -1
- package/dist/controllers/number_input_controller.d.ts +36 -8
- package/dist/controllers/number_input_controller.js +124 -21
- package/dist/controllers/number_input_controller.js.map +1 -1
- package/dist/controllers/optimistic_controller.js +14 -1
- package/dist/controllers/optimistic_controller.js.map +1 -1
- package/dist/controllers/otp_controller.d.ts +22 -7
- package/dist/controllers/otp_controller.js +198 -55
- package/dist/controllers/otp_controller.js.map +1 -1
- package/dist/controllers/overflow_indicator_controller.d.ts +8 -12
- package/dist/controllers/overflow_indicator_controller.js +84 -13
- package/dist/controllers/overflow_indicator_controller.js.map +1 -1
- package/dist/controllers/overflow_menu_controller.d.ts +26 -6
- package/dist/controllers/overflow_menu_controller.js +112 -41
- package/dist/controllers/overflow_menu_controller.js.map +1 -1
- package/dist/controllers/pagination_controller.d.ts +19 -11
- package/dist/controllers/pagination_controller.js +74 -28
- package/dist/controllers/pagination_controller.js.map +1 -1
- package/dist/controllers/password_reveal_controller.d.ts +15 -1
- package/dist/controllers/password_reveal_controller.js +59 -2
- package/dist/controllers/password_reveal_controller.js.map +1 -1
- package/dist/controllers/persist_controller.js +30 -8
- package/dist/controllers/persist_controller.js.map +1 -1
- package/dist/controllers/pointer_drag_controller.js +131 -52
- package/dist/controllers/pointer_drag_controller.js.map +1 -1
- package/dist/controllers/popover_controller.d.ts +9 -3
- package/dist/controllers/popover_controller.js +45 -11
- package/dist/controllers/popover_controller.js.map +1 -1
- package/dist/controllers/portal_controller.d.ts +1 -1
- package/dist/controllers/portal_controller.js +6 -2
- package/dist/controllers/portal_controller.js.map +1 -1
- package/dist/controllers/preview_guard_controller.js +16 -1
- package/dist/controllers/preview_guard_controller.js.map +1 -1
- package/dist/controllers/progress_controller.js +8 -4
- package/dist/controllers/progress_controller.js.map +1 -1
- package/dist/controllers/radio_group_controller.d.ts +6 -4
- package/dist/controllers/radio_group_controller.js +42 -17
- package/dist/controllers/radio_group_controller.js.map +1 -1
- package/dist/controllers/range_slider_controller.d.ts +49 -1
- package/dist/controllers/range_slider_controller.js +88 -42
- package/dist/controllers/range_slider_controller.js.map +1 -1
- package/dist/controllers/rating_controller.d.ts +14 -3
- package/dist/controllers/rating_controller.js +39 -15
- package/dist/controllers/rating_controller.js.map +1 -1
- package/dist/controllers/read_more_controller.d.ts +24 -2
- package/dist/controllers/read_more_controller.js +69 -2
- package/dist/controllers/read_more_controller.js.map +1 -1
- package/dist/controllers/resizable_controller.d.ts +16 -2
- package/dist/controllers/resizable_controller.js +82 -22
- package/dist/controllers/resizable_controller.js.map +1 -1
- package/dist/controllers/scroll_area_controller.js +5 -1
- package/dist/controllers/scroll_area_controller.js.map +1 -1
- package/dist/controllers/scroll_visibility_controller.js +0 -1
- package/dist/controllers/scroll_visibility_controller.js.map +1 -1
- package/dist/controllers/scrollspy_controller.d.ts +3 -2
- package/dist/controllers/scrollspy_controller.js +6 -0
- package/dist/controllers/scrollspy_controller.js.map +1 -1
- package/dist/controllers/separator_controller.d.ts +41 -14
- package/dist/controllers/separator_controller.js +66 -37
- package/dist/controllers/separator_controller.js.map +1 -1
- package/dist/controllers/sidebar_controller.d.ts +20 -3
- package/dist/controllers/sidebar_controller.js +77 -18
- package/dist/controllers/sidebar_controller.js.map +1 -1
- package/dist/controllers/skeleton_controller.js +6 -1
- package/dist/controllers/skeleton_controller.js.map +1 -1
- package/dist/controllers/slider_controller.d.ts +45 -7
- package/dist/controllers/slider_controller.js +82 -47
- package/dist/controllers/slider_controller.js.map +1 -1
- package/dist/controllers/smart_sticky_header_controller.js +11 -1
- package/dist/controllers/smart_sticky_header_controller.js.map +1 -1
- package/dist/controllers/sortable_controller.js +17 -2
- package/dist/controllers/sortable_controller.js.map +1 -1
- package/dist/controllers/spinner_controller.js +10 -2
- package/dist/controllers/spinner_controller.js.map +1 -1
- package/dist/controllers/step_indicator_controller.d.ts +19 -17
- package/dist/controllers/step_indicator_controller.js +18 -17
- package/dist/controllers/step_indicator_controller.js.map +1 -1
- package/dist/controllers/stepper_controller.d.ts +34 -9
- package/dist/controllers/stepper_controller.js +101 -19
- package/dist/controllers/stepper_controller.js.map +1 -1
- package/dist/controllers/stick_to_bottom_controller.d.ts +19 -0
- package/dist/controllers/stick_to_bottom_controller.js +75 -5
- package/dist/controllers/stick_to_bottom_controller.js.map +1 -1
- package/dist/controllers/submit_once_controller.d.ts +3 -2
- package/dist/controllers/submit_once_controller.js +16 -3
- package/dist/controllers/submit_once_controller.js.map +1 -1
- package/dist/controllers/switch_controller.d.ts +29 -7
- package/dist/controllers/switch_controller.js +101 -10
- package/dist/controllers/switch_controller.js.map +1 -1
- package/dist/controllers/tabs_controller.d.ts +12 -0
- package/dist/controllers/tabs_controller.js +21 -2
- package/dist/controllers/tabs_controller.js.map +1 -1
- package/dist/controllers/tags_input_controller.d.ts +15 -3
- package/dist/controllers/tags_input_controller.js +209 -59
- package/dist/controllers/tags_input_controller.js.map +1 -1
- package/dist/controllers/theme_controller.d.ts +20 -3
- package/dist/controllers/theme_controller.js +64 -14
- package/dist/controllers/theme_controller.js.map +1 -1
- package/dist/controllers/time_picker_controller.js +23 -8
- package/dist/controllers/time_picker_controller.js.map +1 -1
- package/dist/controllers/toast_controller.d.ts +52 -15
- package/dist/controllers/toast_controller.js +337 -54
- package/dist/controllers/toast_controller.js.map +1 -1
- package/dist/controllers/toggle_group_controller.d.ts +49 -7
- package/dist/controllers/toggle_group_controller.js +159 -23
- package/dist/controllers/toggle_group_controller.js.map +1 -1
- package/dist/controllers/toolbar_controller.js +32 -0
- package/dist/controllers/toolbar_controller.js.map +1 -1
- package/dist/controllers/tooltip_controller.d.ts +8 -0
- package/dist/controllers/tooltip_controller.js +39 -13
- package/dist/controllers/tooltip_controller.js.map +1 -1
- package/dist/controllers/transition_controller.js +4 -0
- package/dist/controllers/transition_controller.js.map +1 -1
- package/dist/controllers/tree_view_controller.d.ts +39 -8
- package/dist/controllers/tree_view_controller.js +169 -16
- package/dist/controllers/tree_view_controller.js.map +1 -1
- package/dist/index.d.ts +28 -1
- package/dist/index.js +4588 -1535
- package/dist/index.js.map +1 -1
- package/dist/inspector/cli.d.ts +72 -6
- package/dist/inspector/cli.js +262 -51
- package/dist/inspector/cli.js.map +1 -1
- package/dist/inspector/cli_bin.js +309 -51
- package/dist/inspector/cli_bin.js.map +1 -1
- package/dist/inspector/examples.json +40 -40
- package/dist/inspector/manifest.json +525 -59
- package/dist/positioning/index.js +2 -0
- package/dist/positioning/index.js.map +1 -1
- package/package.json +2 -2
|
@@ -6,14 +6,16 @@ import { Controller } from '@hotwired/stimulus';
|
|
|
6
6
|
*
|
|
7
7
|
* Markup contract (identifier: `stimeo--data-grid`):
|
|
8
8
|
* <table data-controller="stimeo--data-grid" role="grid" aria-label="Users"
|
|
9
|
-
* data-stimeo--data-grid-selection-value="single"
|
|
9
|
+
* data-stimeo--data-grid-selection-value="single"
|
|
10
|
+
* data-stimeo--data-grid-name-value="user_id">
|
|
11
|
+
* <caption><div data-stimeo--data-grid-target="fields"></div></caption>
|
|
10
12
|
* <thead><tr role="row">
|
|
11
13
|
* <th role="columnheader" aria-sort="none" tabindex="-1"
|
|
12
14
|
* data-stimeo--data-grid-target="columnHeader"
|
|
13
15
|
* data-action="click->stimeo--data-grid#sort
|
|
14
16
|
* keydown->stimeo--data-grid#onKeydown">Name</th>
|
|
15
17
|
* </tr></thead>
|
|
16
|
-
* <tbody><tr role="row" aria-selected="false"
|
|
18
|
+
* <tbody><tr role="row" aria-selected="false" data-value="7"
|
|
17
19
|
* data-stimeo--data-grid-target="row">
|
|
18
20
|
* <td role="gridcell" tabindex="0" data-stimeo--data-grid-target="cell"
|
|
19
21
|
* data-action="keydown->stimeo--data-grid#onKeydown">Jane</td>
|
|
@@ -25,15 +27,38 @@ import { Controller } from '@hotwired/stimulus';
|
|
|
25
27
|
* `-1`); arrow keys move both DOM focus and that tabbable position. Sort state is
|
|
26
28
|
* exposed via `aria-sort` on headers, selection via `aria-selected` on rows.
|
|
27
29
|
*
|
|
28
|
-
* `selectionchange` dispatches `{ rows: HTMLElement[] }
|
|
30
|
+
* `selectionchange` dispatches `{ rows: HTMLElement[] }` when the user changes the
|
|
31
|
+
* selection; `sort` dispatches
|
|
29
32
|
* `{ column: HTMLElement, direction: "ascending" | "descending" | "none" }`.
|
|
30
33
|
*
|
|
34
|
+
* `reconcile` dispatches `{ rows: HTMLElement[] }` — the selected rows, as
|
|
35
|
+
* `selectionchange` carries them — when the page moves the selection instead: a
|
|
36
|
+
* selected row removed or dropped from the targets, a morph or a script writing
|
|
37
|
+
* a row's `aria-selected` or a selected row's `data-value`, a selected row that
|
|
38
|
+
* arrives ahead of the selection in `single` mode, or a runtime `selection` that
|
|
39
|
+
* collapses it. One batch of such changes reports once, and only when the
|
|
40
|
+
* selected rows or the values they submit differ from the selection last settled
|
|
41
|
+
* — on connect, by the user, or by the previous `reconcile` — so connecting
|
|
42
|
+
* reports nothing.
|
|
43
|
+
*
|
|
44
|
+
* With a `fields` target the selected rows are mirrored into `name`d hidden
|
|
45
|
+
* inputs so the selection can be submitted and read server-side — one per
|
|
46
|
+
* selected row that carries a `data-value`, in DOM order; a row without one
|
|
47
|
+
* submits nothing. `form` points them at a `<form>` by id when the container
|
|
48
|
+
* sits outside it. A selection the user made emits a native bubbling `change`
|
|
49
|
+
* from the container, the way a form control does, so `stimeo--auto-submit` and
|
|
50
|
+
* form-level validation hear it. The mirror is refreshed silently on connect,
|
|
51
|
+
* on a replacement container, on row churn, and whenever `selection`, `name` or
|
|
52
|
+
* `form` changes; a selection the page moved is reported by `reconcile` alone.
|
|
53
|
+
*
|
|
31
54
|
* @remarks
|
|
32
55
|
* Behavior only — the consumer performs the actual data sort/render in response to
|
|
33
|
-
* the `sort` event and owns all styling.
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
56
|
+
* the `sort` event and owns all styling. While connected a `MutationObserver`
|
|
57
|
+
* watches the rows' `aria-selected` and `data-value`, which a morph can rewrite
|
|
58
|
+
* without adding or removing a target; `disconnect()` releases it and drops a
|
|
59
|
+
* pending pass. `connect()` rebuilds the single tab stop idempotently from the
|
|
60
|
+
* DOM, and the target callbacks rebuild it again after rows or cells are added or
|
|
61
|
+
* removed at runtime.
|
|
37
62
|
*
|
|
38
63
|
* Behavior provided:
|
|
39
64
|
* - `Arrow*` move between cells (clamped at edges); `Home`/`End` to the row's
|
|
@@ -62,33 +87,53 @@ declare class DataGridController extends Controller<HTMLElement> {
|
|
|
62
87
|
type: StringConstructor;
|
|
63
88
|
default: string;
|
|
64
89
|
};
|
|
90
|
+
name: {
|
|
91
|
+
type: StringConstructor;
|
|
92
|
+
default: string;
|
|
93
|
+
};
|
|
94
|
+
form: {
|
|
95
|
+
type: StringConstructor;
|
|
96
|
+
default: string;
|
|
97
|
+
};
|
|
65
98
|
};
|
|
66
99
|
static actions: readonly ["onKeydown", "sort", "toggleSelect"];
|
|
67
|
-
static events: readonly ["selectionchange", "sort"];
|
|
100
|
+
static events: readonly ["selectionchange", "sort", "reconcile"];
|
|
68
101
|
readonly columnHeaderTargets: HTMLElement[];
|
|
69
102
|
readonly rowTargets: HTMLElement[];
|
|
70
103
|
readonly cellTargets: HTMLElement[];
|
|
104
|
+
readonly fieldsTarget: HTMLElement;
|
|
105
|
+
readonly hasFieldsTarget: boolean;
|
|
71
106
|
selectionValue: string;
|
|
107
|
+
nameValue: string;
|
|
108
|
+
formValue: string;
|
|
72
109
|
/**
|
|
73
|
-
* Establishes a single tab stop across all navigable cells/headers
|
|
74
|
-
*
|
|
110
|
+
* Establishes a single tab stop across all navigable cells/headers, brings the
|
|
111
|
+
* rows to their baseline, and settles the selection without reporting it.
|
|
75
112
|
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
* cached
|
|
79
|
-
*
|
|
113
|
+
* The whole baseline runs here rather than in the Value callbacks: Stimulus
|
|
114
|
+
* delivers those before `connect()`, where the pass is refused, and a
|
|
115
|
+
* re-attached element reuses its cached context, whose value observer does not
|
|
116
|
+
* fire again.
|
|
80
117
|
*/
|
|
81
118
|
connect(): void;
|
|
82
|
-
/**
|
|
119
|
+
/** Releases the row observer and drops a queued pass, so neither outlives the element. */
|
|
83
120
|
disconnect(): void;
|
|
84
121
|
/**
|
|
85
|
-
*
|
|
86
|
-
* (
|
|
87
|
-
*
|
|
122
|
+
* Re-renders `aria-multiselectable`, the rows and the fields when application
|
|
123
|
+
* code (or a Turbo morph) changes `selection` at runtime; a selection the new
|
|
124
|
+
* mode collapses is reported as `reconcile`.
|
|
88
125
|
*/
|
|
89
126
|
selectionValueChanged(): void;
|
|
127
|
+
/** Rebuilds the submitted fields when the public name changes at runtime. */
|
|
128
|
+
nameValueChanged(): void;
|
|
129
|
+
/** Repoints the submitted fields when the owning form changes at runtime. */
|
|
130
|
+
formValueChanged(): void;
|
|
131
|
+
/** Seeds a fields container inserted after connect from the current selection. */
|
|
132
|
+
fieldsTargetConnected(): void;
|
|
90
133
|
/** Re-establishes the baselines for a row added after connect. */
|
|
91
134
|
rowTargetConnected(): void;
|
|
135
|
+
/** Settles the selection again when a row leaves the grid or drops its target token. */
|
|
136
|
+
rowTargetDisconnected(): void;
|
|
92
137
|
/** Re-establishes the tab stop when a cell joins the grid after connect. */
|
|
93
138
|
cellTargetConnected(): void;
|
|
94
139
|
/** Re-establishes the tab stop when a cell leaves the grid. */
|
|
@@ -18,6 +18,29 @@ function isReservedArrowChord(event, allow = []) {
|
|
|
18
18
|
return event.altKey && !allow.includes("alt") || event.ctrlKey && !allow.includes("ctrl") || event.metaKey && !allow.includes("meta") || event.shiftKey && !allow.includes("shift");
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
// src/utils/field_mirror.ts
|
|
22
|
+
function writeFields(container, values, { name, form = "" }) {
|
|
23
|
+
const current = [...container.children];
|
|
24
|
+
const submits = current.length === values.length && current.every(
|
|
25
|
+
(child, index) => child instanceof HTMLInputElement && child.type === "hidden" && child.value === values[index] && child.name === name && (child.getAttribute("form") ?? "") === form
|
|
26
|
+
);
|
|
27
|
+
if (submits) return false;
|
|
28
|
+
container.replaceChildren(
|
|
29
|
+
...values.map((value) => {
|
|
30
|
+
const input = document.createElement("input");
|
|
31
|
+
input.type = "hidden";
|
|
32
|
+
input.name = name;
|
|
33
|
+
input.value = value;
|
|
34
|
+
if (form !== "") input.setAttribute("form", form);
|
|
35
|
+
return input;
|
|
36
|
+
})
|
|
37
|
+
);
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
function commitField(target) {
|
|
41
|
+
target.dispatchEvent(new Event("change", { bubbles: true }));
|
|
42
|
+
}
|
|
43
|
+
|
|
21
44
|
// src/utils/interactive_host.ts
|
|
22
45
|
var INTERACTIVE_HOST_SELECTOR = "button, input, select, textarea, label, a[href], area[href], summary, details, audio[controls], video[controls], iframe, object, embed";
|
|
23
46
|
function isInteractiveHost(element) {
|
|
@@ -70,48 +93,71 @@ var MicrotaskCoalescer = class {
|
|
|
70
93
|
|
|
71
94
|
// src/controllers/data_grid_controller.ts
|
|
72
95
|
var SORT_CYCLE = ["none", "ascending", "descending"];
|
|
96
|
+
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
|
+
function sameSelection(a, b) {
|
|
101
|
+
if (a.size !== b.size) return false;
|
|
102
|
+
for (const [row, value] of a) {
|
|
103
|
+
if (!b.has(row) || b.get(row) !== value) return false;
|
|
104
|
+
}
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
73
107
|
function nextSortDirection(current) {
|
|
74
108
|
const index = SORT_CYCLE.indexOf(current);
|
|
75
109
|
const from = index < 0 ? 0 : index;
|
|
76
110
|
return SORT_CYCLE[(from + 1) % SORT_CYCLE.length] ?? "ascending";
|
|
77
111
|
}
|
|
78
112
|
var DataGridController = class extends Controller {
|
|
79
|
-
static targets = ["columnHeader", "row", "cell"];
|
|
113
|
+
static targets = ["columnHeader", "row", "cell", "fields"];
|
|
80
114
|
static values = {
|
|
81
|
-
selection: { type: String, default: "none" }
|
|
115
|
+
selection: { type: String, default: "none" },
|
|
116
|
+
name: { type: String, default: "rows[]" },
|
|
117
|
+
form: { type: String, default: "" }
|
|
82
118
|
};
|
|
83
119
|
static actions = ["onKeydown", "sort", "toggleSelect"];
|
|
84
|
-
static events = ["selectionchange", "sort"];
|
|
120
|
+
static events = ["selectionchange", "sort", "reconcile"];
|
|
85
121
|
/**
|
|
86
|
-
* Collapses the
|
|
87
|
-
*
|
|
122
|
+
* Collapses the target callbacks, Value changes and observed row writes of one
|
|
123
|
+
* DOM mutation into a single pass, and refuses to run before `connect()` or
|
|
124
|
+
* after `disconnect()`.
|
|
88
125
|
*
|
|
89
126
|
* Stimulus reports every target one at a time, so an ungated pass would re-walk
|
|
90
127
|
* the whole grid once per authored cell on mount and once per streamed cell
|
|
91
128
|
* afterwards — quadratic in the cell count both times.
|
|
92
129
|
*/
|
|
93
|
-
#reconcile = new MicrotaskCoalescer(() => this.#
|
|
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. */
|
|
134
|
+
#settled = /* @__PURE__ */ new Map();
|
|
94
135
|
/**
|
|
95
|
-
* Establishes a single tab stop across all navigable cells/headers
|
|
96
|
-
*
|
|
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.
|
|
97
138
|
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
* cached
|
|
101
|
-
*
|
|
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.
|
|
102
143
|
*/
|
|
103
144
|
connect() {
|
|
104
145
|
this.#restoreBaseline();
|
|
146
|
+
this.#settled = this.#selection();
|
|
105
147
|
this.#reconcile.activate();
|
|
148
|
+
this.#observeRows();
|
|
106
149
|
}
|
|
107
|
-
/**
|
|
150
|
+
/** Releases the row observer and drops a queued pass, so neither outlives the element. */
|
|
108
151
|
disconnect() {
|
|
109
152
|
this.#reconcile.cancel();
|
|
153
|
+
this.#observer?.disconnect();
|
|
154
|
+
this.#observer = null;
|
|
110
155
|
}
|
|
111
156
|
/**
|
|
112
|
-
* Rebuilds
|
|
113
|
-
* cell is in the Tab sequence,
|
|
114
|
-
* `aria-selected
|
|
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.
|
|
115
161
|
*
|
|
116
162
|
* The tab stop keeps whichever cell already holds it, so a rebuild triggered by
|
|
117
163
|
* an unrelated row arriving does not throw the user's position away; only when
|
|
@@ -124,21 +170,60 @@ var DataGridController = class extends Controller {
|
|
|
124
170
|
const cells = this.#navigableCells();
|
|
125
171
|
const active = cells.find((cell) => cell.tabIndex === 0) ?? cells[0];
|
|
126
172
|
if (active) this.#setActiveCell(active, { focus: false }, cells);
|
|
173
|
+
this.#syncSelectable();
|
|
174
|
+
const pageWrote = this.#takePageRecords();
|
|
127
175
|
this.#normalizeSelection();
|
|
176
|
+
this.#dropOwnRecords(pageWrote);
|
|
177
|
+
this.#mirrorFields(false);
|
|
178
|
+
}
|
|
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
|
+
#reconcileRows() {
|
|
185
|
+
this.#restoreBaseline();
|
|
186
|
+
this.#reportMove();
|
|
187
|
+
}
|
|
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
|
+
#reportMove() {
|
|
194
|
+
const selection = this.#selection();
|
|
195
|
+
if (sameSelection(selection, this.#settled)) return;
|
|
196
|
+
this.#settled = selection;
|
|
197
|
+
this.dispatch("reconcile", { detail: { rows: [...selection.keys()] } });
|
|
128
198
|
}
|
|
129
199
|
/**
|
|
130
|
-
*
|
|
131
|
-
* (
|
|
132
|
-
*
|
|
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`.
|
|
133
203
|
*/
|
|
134
204
|
selectionValueChanged() {
|
|
135
|
-
this.#
|
|
136
|
-
|
|
205
|
+
this.#reconcile.schedule();
|
|
206
|
+
}
|
|
207
|
+
/** Rebuilds the submitted fields when the public name changes at runtime. */
|
|
208
|
+
nameValueChanged() {
|
|
209
|
+
this.#reconcile.schedule();
|
|
210
|
+
}
|
|
211
|
+
/** Repoints the submitted fields when the owning form changes at runtime. */
|
|
212
|
+
formValueChanged() {
|
|
213
|
+
this.#reconcile.schedule();
|
|
214
|
+
}
|
|
215
|
+
/** Seeds a fields container inserted after connect from the current selection. */
|
|
216
|
+
fieldsTargetConnected() {
|
|
217
|
+
this.#reconcile.schedule();
|
|
137
218
|
}
|
|
138
219
|
/** Re-establishes the baselines for a row added after connect. */
|
|
139
220
|
rowTargetConnected() {
|
|
140
221
|
this.#reconcile.schedule();
|
|
141
222
|
}
|
|
223
|
+
/** Settles the selection again when a row leaves the grid or drops its target token. */
|
|
224
|
+
rowTargetDisconnected() {
|
|
225
|
+
this.#reconcile.schedule();
|
|
226
|
+
}
|
|
142
227
|
/** Re-establishes the tab stop when a cell joins the grid after connect. */
|
|
143
228
|
cellTargetConnected() {
|
|
144
229
|
this.#reconcile.schedule();
|
|
@@ -165,6 +250,8 @@ var DataGridController = class extends Controller {
|
|
|
165
250
|
* A grid that declares `selection="none"` has no selectable rows, so the
|
|
166
251
|
* attribute is removed rather than written: in ARIA its absence is what "not
|
|
167
252
|
* selectable" looks like.
|
|
253
|
+
*
|
|
254
|
+
* @stimeoRenderRoot
|
|
168
255
|
*/
|
|
169
256
|
#normalizeSelection() {
|
|
170
257
|
const rows = this.rowTargets;
|
|
@@ -176,9 +263,9 @@ var DataGridController = class extends Controller {
|
|
|
176
263
|
const first = rows.find((row) => row.getAttribute("aria-selected") === "true");
|
|
177
264
|
for (const row of rows) {
|
|
178
265
|
if (single) {
|
|
179
|
-
row
|
|
266
|
+
setAttributeIfChanged(row, "aria-selected", row === first ? "true" : "false");
|
|
180
267
|
} else if (row.getAttribute("aria-selected") !== "true") {
|
|
181
|
-
row
|
|
268
|
+
setAttributeIfChanged(row, "aria-selected", "false");
|
|
182
269
|
}
|
|
183
270
|
}
|
|
184
271
|
}
|
|
@@ -186,10 +273,12 @@ var DataGridController = class extends Controller {
|
|
|
186
273
|
* Mirrors `selection="multiple"` onto `aria-multiselectable` (APG Grid) so SRs
|
|
187
274
|
* announce that more than one row can be selected; cleared for single/none so a
|
|
188
275
|
* grid never carries a misleading attribute.
|
|
276
|
+
*
|
|
277
|
+
* @stimeoRenderRoot
|
|
189
278
|
*/
|
|
190
279
|
#syncSelectable() {
|
|
191
280
|
if (this.selectionValue === "multiple") {
|
|
192
|
-
this.element
|
|
281
|
+
setAttributeIfChanged(this.element, "aria-multiselectable", "true");
|
|
193
282
|
} else {
|
|
194
283
|
this.element.removeAttribute("aria-multiselectable");
|
|
195
284
|
}
|
|
@@ -288,7 +377,12 @@ var DataGridController = class extends Controller {
|
|
|
288
377
|
if (control && host.contains(control)) return control;
|
|
289
378
|
return isInteractiveHost(source) ? source : null;
|
|
290
379
|
}
|
|
291
|
-
/**
|
|
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
|
+
*/
|
|
292
386
|
#activate(cell) {
|
|
293
387
|
if (this.columnHeaderTargets.includes(cell)) {
|
|
294
388
|
this.#cycleSort(cell);
|
|
@@ -306,17 +400,89 @@ var DataGridController = class extends Controller {
|
|
|
306
400
|
}
|
|
307
401
|
this.dispatch("sort", { detail: { column: header, direction } });
|
|
308
402
|
}
|
|
309
|
-
/**
|
|
403
|
+
/**
|
|
404
|
+
* Toggles a row's `aria-selected`, honoring single vs. multiple selection.
|
|
405
|
+
*
|
|
406
|
+
* @stimeoRuntimeOnly `selection` decides how this one toggle treats the other rows.
|
|
407
|
+
*/
|
|
310
408
|
#toggleRow(row) {
|
|
311
409
|
const selected = row.getAttribute("aria-selected") === "true";
|
|
410
|
+
const pageWrote = this.#takePageRecords();
|
|
312
411
|
if (this.selectionValue === "single") {
|
|
313
412
|
for (const other of this.rowTargets) {
|
|
314
|
-
if (other !== row) other
|
|
413
|
+
if (other !== row) setAttributeIfChanged(other, "aria-selected", "false");
|
|
315
414
|
}
|
|
316
415
|
}
|
|
317
416
|
row.setAttribute("aria-selected", selected ? "false" : "true");
|
|
318
|
-
|
|
319
|
-
this
|
|
417
|
+
this.#dropOwnRecords(pageWrote);
|
|
418
|
+
this.#settled = this.#selection();
|
|
419
|
+
this.#mirrorFields(true);
|
|
420
|
+
this.dispatch("selectionchange", { detail: { rows: [...this.#settled.keys()] } });
|
|
421
|
+
}
|
|
422
|
+
/** The selected rows in DOM order, each with the `data-value` it submits, if any. */
|
|
423
|
+
#selection() {
|
|
424
|
+
return new Map(
|
|
425
|
+
this.rowTargets.filter((row) => row.getAttribute("aria-selected") === "true").map((row) => [row, row.dataset.value])
|
|
426
|
+
);
|
|
427
|
+
}
|
|
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
|
+
#observeRows() {
|
|
435
|
+
const observer = new MutationObserver((records) => {
|
|
436
|
+
if (this.#concernsRows(records)) this.#reconcile.schedule();
|
|
437
|
+
});
|
|
438
|
+
observer.observe(this.element, {
|
|
439
|
+
subtree: true,
|
|
440
|
+
attributes: true,
|
|
441
|
+
attributeFilter: OBSERVED_ATTRIBUTES
|
|
442
|
+
});
|
|
443
|
+
this.#observer = observer;
|
|
444
|
+
}
|
|
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
|
+
#takePageRecords() {
|
|
452
|
+
const records = this.#observer?.takeRecords() ?? [];
|
|
453
|
+
return records.length > 0 && this.#concernsRows(records);
|
|
454
|
+
}
|
|
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
|
+
#concernsRows(records) {
|
|
461
|
+
const rows = new Set(this.rowTargets);
|
|
462
|
+
return records.some((record) => rows.has(record.target));
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Drops the records the grid's own writes just queued, so the observer never
|
|
466
|
+
* takes them for the page's, then schedules the pass the page's records owe.
|
|
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
|
+
*/
|
|
479
|
+
#mirrorFields(notify) {
|
|
480
|
+
if (!this.hasFieldsTarget) return;
|
|
481
|
+
const values = this.rowTargets.filter((row) => row.getAttribute("aria-selected") === "true").map((row) => row.dataset.value).filter((value) => value !== void 0);
|
|
482
|
+
const options = { name: this.nameValue, form: this.formValue };
|
|
483
|
+
if (writeFields(this.fieldsTarget, values, options) && notify) {
|
|
484
|
+
commitField(this.fieldsTarget);
|
|
485
|
+
}
|
|
320
486
|
}
|
|
321
487
|
/**
|
|
322
488
|
* Makes `cell` the single tabbable cell (roving) and optionally focuses it.
|