stimeo-ui 0.16.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +31 -0
- data/README.md +8 -1
- data/dist/cable/index.js +363 -436
- data/dist/controllers/accordion_controller.js +166 -55
- data/dist/controllers/alert_dialog_controller.js +862 -221
- data/dist/controllers/announcer_controller.js +135 -198
- data/dist/controllers/aspect_ratio_controller.js +0 -10
- data/dist/controllers/auto_submit_controller.js +271 -127
- data/dist/controllers/avatar_controller.js +171 -105
- data/dist/controllers/breadcrumb_controller.js +0 -130
- data/dist/controllers/bulk_select_controller.js +262 -52
- data/dist/controllers/calendar_controller.js +175 -209
- data/dist/controllers/carousel_controller.js +461 -399
- data/dist/controllers/character_counter_controller.js +246 -156
- data/dist/controllers/checkbox_controller.js +32 -74
- data/dist/controllers/clipboard_controller.js +168 -147
- data/dist/controllers/collapsible_controller.js +301 -120
- data/dist/controllers/color_picker_controller.js +149 -139
- data/dist/controllers/combobox_controller.js +360 -133
- data/dist/controllers/command_palette_controller.js +970 -358
- data/dist/controllers/conditional_fields_controller.js +166 -124
- data/dist/controllers/confirm_controller.js +939 -248
- data/dist/controllers/context_menu_controller.js +256 -128
- data/dist/controllers/count_up_controller.js +73 -21
- data/dist/controllers/countdown_controller.js +183 -148
- data/dist/controllers/currency_input_controller.js +134 -158
- data/dist/controllers/data_grid_controller.js +114 -186
- data/dist/controllers/date_range_picker_controller.js +288 -206
- data/dist/controllers/dialog_controller.js +858 -223
- data/dist/controllers/direct_upload_controller.js +73 -153
- data/dist/controllers/dirty_form_controller.js +14 -48
- data/dist/controllers/dismissible_controller.js +175 -21
- data/dist/controllers/drawer_controller.js +933 -341
- data/dist/controllers/dropdown_controller.js +312 -82
- data/dist/controllers/editable_controller.js +158 -68
- data/dist/controllers/empty_state_controller.js +67 -53
- data/dist/controllers/file_dropzone_controller.js +311 -207
- data/dist/controllers/filter_controller.js +67 -39
- data/dist/controllers/flash_controller.js +172 -314
- data/dist/controllers/focus_controller.js +654 -240
- data/dist/controllers/form_field_controller.js +147 -132
- data/dist/controllers/form_validation_controller.js +13 -86
- data/dist/controllers/frame_loading_controller.js +264 -213
- data/dist/controllers/highlight_controller.js +107 -75
- data/dist/controllers/hover_card_controller.js +309 -118
- data/dist/controllers/idle_controller.js +267 -101
- data/dist/controllers/input_mask_controller.js +71 -72
- data/dist/controllers/intersection_controller.js +147 -106
- data/dist/controllers/lazy_frame_controller.js +9 -65
- data/dist/controllers/listbox_controller.js +306 -204
- data/dist/controllers/local_time_controller.js +54 -64
- data/dist/controllers/masonry_controller.js +70 -93
- data/dist/controllers/menu_controller.js +214 -156
- data/dist/controllers/menubar_controller.js +170 -329
- data/dist/controllers/meter_controller.js +151 -67
- data/dist/controllers/multi_select_controller.js +593 -358
- data/dist/controllers/navigation_menu_controller.js +124 -232
- data/dist/controllers/nested_form_controller.js +352 -182
- data/dist/controllers/network_status_controller.js +242 -54
- data/dist/controllers/number_input_controller.js +320 -263
- data/dist/controllers/optimistic_controller.js +16 -76
- data/dist/controllers/otp_controller.js +198 -193
- data/dist/controllers/overflow_indicator_controller.js +116 -113
- data/dist/controllers/overflow_menu_controller.js +343 -283
- data/dist/controllers/pagination_controller.js +295 -119
- data/dist/controllers/password_reveal_controller.js +389 -121
- data/dist/controllers/password_strength_controller.js +99 -153
- data/dist/controllers/persist_controller.js +237 -103
- data/dist/controllers/pointer_drag_controller.js +185 -165
- data/dist/controllers/popover_controller.js +236 -97
- data/dist/controllers/portal_controller.js +209 -76
- data/dist/controllers/preview_guard_controller.js +232 -116
- data/dist/controllers/progress_controller.js +125 -56
- data/dist/controllers/radio_group_controller.js +117 -137
- data/dist/controllers/range_slider_controller.js +192 -105
- data/dist/controllers/rating_controller.js +418 -207
- data/dist/controllers/read_more_controller.js +134 -63
- data/dist/controllers/reading_progress_controller.js +147 -135
- data/dist/controllers/relative_time_controller.js +145 -110
- data/dist/controllers/{reset_before_cache_controller.js → reset_on_restore_controller.js} +97 -38
- data/dist/controllers/resizable_controller.js +255 -139
- data/dist/controllers/roving_controller.js +39 -84
- data/dist/controllers/scroll_area_controller.js +428 -225
- data/dist/controllers/scroll_restore_controller.js +0 -53
- data/dist/controllers/scroll_visibility_controller.js +395 -145
- data/dist/controllers/scrollspy_controller.js +181 -242
- data/dist/controllers/separator_controller.js +267 -140
- data/dist/controllers/sidebar_controller.js +1023 -386
- data/dist/controllers/skeleton_controller.js +248 -97
- data/dist/controllers/slider_controller.js +159 -79
- data/dist/controllers/smart_sticky_header_controller.js +162 -63
- data/dist/controllers/sortable_controller.js +0 -112
- data/dist/controllers/spinner_controller.js +277 -169
- data/dist/controllers/step_indicator_controller.js +99 -46
- data/dist/controllers/stepper_controller.js +114 -73
- data/dist/controllers/stick_to_bottom_controller.js +262 -138
- data/dist/controllers/sticky_observer_controller.js +214 -49
- data/dist/controllers/submit_once_controller.js +294 -205
- data/dist/controllers/switch_controller.js +33 -60
- data/dist/controllers/tabs_controller.js +35 -29
- data/dist/controllers/tags_input_controller.js +136 -145
- data/dist/controllers/textarea_autosize_controller.js +71 -58
- data/dist/controllers/theme_controller.js +130 -112
- data/dist/controllers/time_picker_controller.js +146 -80
- data/dist/controllers/toast_controller.js +186 -297
- data/dist/controllers/toggle_group_controller.js +95 -136
- data/dist/controllers/toolbar_controller.js +68 -98
- data/dist/controllers/tooltip_controller.js +158 -124
- data/dist/controllers/transition_controller.js +123 -119
- data/dist/controllers/tree_view_controller.js +150 -345
- data/dist/index.js +6370 -9346
- data/dist/positioning/index.js +148 -47
- data/lib/stimeo/ui/version.rb +1 -1
- metadata +3 -3
|
@@ -1,15 +1,11 @@
|
|
|
1
1
|
import { Controller } from '@hotwired/stimulus';
|
|
2
2
|
|
|
3
|
-
// src/controllers/switch_controller.ts
|
|
4
|
-
|
|
5
|
-
// src/utils/default_attribute.ts
|
|
6
3
|
function setDefaultAttribute(element, name, value) {
|
|
7
4
|
if (element.hasAttribute(name)) return false;
|
|
8
5
|
element.setAttribute(name, value);
|
|
9
6
|
return true;
|
|
10
7
|
}
|
|
11
8
|
|
|
12
|
-
// src/utils/field_mirror.ts
|
|
13
9
|
function writeField(field, value) {
|
|
14
10
|
if (field.value === value) return false;
|
|
15
11
|
field.value = value;
|
|
@@ -19,7 +15,6 @@ function commitField(target) {
|
|
|
19
15
|
target.dispatchEvent(new Event("change", { bubbles: true }));
|
|
20
16
|
}
|
|
21
17
|
|
|
22
|
-
// src/utils/focus_candidate.ts
|
|
23
18
|
function inheritsFieldsetDisabled(control) {
|
|
24
19
|
let fieldset = control.closest("fieldset[disabled]");
|
|
25
20
|
while (fieldset) {
|
|
@@ -30,7 +25,6 @@ function inheritsFieldsetDisabled(control) {
|
|
|
30
25
|
return false;
|
|
31
26
|
}
|
|
32
27
|
|
|
33
|
-
// src/utils/interactive_host.ts
|
|
34
28
|
var INTERACTIVE_HOST_SELECTOR = "button, input, select, textarea, label, a[href], area[href], summary, details, audio[controls], video[controls], iframe, object, embed";
|
|
35
29
|
function isInteractiveHost(element) {
|
|
36
30
|
if (element.matches(INTERACTIVE_HOST_SELECTOR)) return true;
|
|
@@ -47,27 +41,22 @@ function isInteractiveHost(element) {
|
|
|
47
41
|
return false;
|
|
48
42
|
}
|
|
49
43
|
|
|
50
|
-
// src/utils/microtask_coalescer.ts
|
|
51
44
|
var MicrotaskCoalescer = class {
|
|
52
45
|
#run;
|
|
53
46
|
#queued = false;
|
|
54
47
|
#active = false;
|
|
55
48
|
#generation = 0;
|
|
56
|
-
/** @param run - the single reconciliation pass, invoked at most once per batch. */
|
|
57
49
|
constructor(run) {
|
|
58
50
|
this.#run = run;
|
|
59
51
|
}
|
|
60
|
-
/** Opens the window in which {@link schedule} is honoured; call from `connect()`. */
|
|
61
52
|
activate() {
|
|
62
53
|
this.#active = true;
|
|
63
54
|
}
|
|
64
|
-
/** Closes the window and drops any pending pass; call from `disconnect()`. */
|
|
65
55
|
cancel() {
|
|
66
56
|
this.#active = false;
|
|
67
57
|
this.#queued = false;
|
|
68
58
|
this.#generation += 1;
|
|
69
59
|
}
|
|
70
|
-
/** Requests one pass after the batch settles. Idempotent; inert outside the window. */
|
|
71
60
|
schedule() {
|
|
72
61
|
if (!this.#active || this.#queued) return;
|
|
73
62
|
this.#queued = true;
|
|
@@ -80,7 +69,17 @@ var MicrotaskCoalescer = class {
|
|
|
80
69
|
}
|
|
81
70
|
};
|
|
82
71
|
|
|
83
|
-
|
|
72
|
+
var MoveCounter = class {
|
|
73
|
+
#sequence = 0;
|
|
74
|
+
record() {
|
|
75
|
+
this.#sequence += 1;
|
|
76
|
+
return this.#sequence;
|
|
77
|
+
}
|
|
78
|
+
isLatest(token) {
|
|
79
|
+
return token === this.#sequence;
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
|
|
84
83
|
var OBSERVED_ATTRIBUTES = [
|
|
85
84
|
"role",
|
|
86
85
|
"aria-checked",
|
|
@@ -92,32 +91,25 @@ var OBSERVED_ATTRIBUTES = [
|
|
|
92
91
|
];
|
|
93
92
|
var OBSERVED_ANCESTOR_ATTRIBUTES = ["contenteditable"];
|
|
94
93
|
var SwitchController = class extends Controller {
|
|
94
|
+
#moves = new MoveCounter();
|
|
95
95
|
static targets = ["field"];
|
|
96
96
|
static actions = ["onKeydown", "toggle"];
|
|
97
97
|
static events = ["change", "reconcile"];
|
|
98
|
-
|
|
99
|
-
#ownedDefaults = /* @__PURE__ */ new Set();
|
|
100
|
-
/** Controller writes that must not be mistaken for authored morph changes. */
|
|
98
|
+
#ownedDefaults = /* @__PURE__ */ new Map();
|
|
101
99
|
#internalAttributeValues = /* @__PURE__ */ new Map();
|
|
102
|
-
/** One pass per batch of retained-element changes and field arrivals. */
|
|
103
100
|
#pass = new MicrotaskCoalescer(() => this.#reconcile());
|
|
104
|
-
/** The checked state last published: read on connect, then committed or reported. */
|
|
105
101
|
#committedChecked = false;
|
|
106
102
|
#attributeObserver = new MutationObserver((records) => {
|
|
107
103
|
this.#releaseAuthoredDefaults(records);
|
|
108
104
|
this.#pass.schedule();
|
|
109
105
|
});
|
|
110
|
-
/** Blocks disabled pointer/native-key activation before consumer click handlers. */
|
|
111
106
|
#onClickCapture = (event) => {
|
|
112
107
|
if (!this.#isSupportedHost || !this.#isActivationDisabled) return;
|
|
113
108
|
event.preventDefault();
|
|
114
109
|
event.stopImmediatePropagation();
|
|
115
110
|
};
|
|
116
|
-
/**
|
|
117
|
-
* Ensures the switch exposes a role and is keyboard-reachable, and takes the
|
|
118
|
-
* state it finds as the one a later move is measured from, reporting nothing.
|
|
119
|
-
*/
|
|
120
111
|
connect() {
|
|
112
|
+
this.#releaseDefaultsRewrittenWhileAway();
|
|
121
113
|
this.#reconcileDefaults();
|
|
122
114
|
this.#mirrorField(false);
|
|
123
115
|
this.#committedChecked = this.#checked;
|
|
@@ -125,28 +117,22 @@ var SwitchController = class extends Controller {
|
|
|
125
117
|
this.#pass.activate();
|
|
126
118
|
this.#observeAttributes();
|
|
127
119
|
}
|
|
128
|
-
/** Fills a form field inserted or replaced at runtime with the current state. */
|
|
129
120
|
fieldTargetConnected() {
|
|
130
121
|
this.#pass.schedule();
|
|
131
122
|
}
|
|
132
|
-
|
|
123
|
+
fieldTargetDisconnected() {
|
|
124
|
+
this.#pass.schedule();
|
|
125
|
+
}
|
|
133
126
|
disconnect() {
|
|
134
127
|
this.#pass.cancel();
|
|
135
128
|
this.element.removeEventListener("click", this.#onClickCapture, true);
|
|
136
129
|
this.#attributeObserver.disconnect();
|
|
137
130
|
this.#internalAttributeValues.clear();
|
|
138
131
|
}
|
|
139
|
-
/** Toggles the checked state. Bound via `data-action` (click). */
|
|
140
132
|
toggle() {
|
|
141
133
|
if (!this.#isSupportedHost || this.#isActivationDisabled) return;
|
|
142
134
|
this.#commit(!this.#checked);
|
|
143
135
|
}
|
|
144
|
-
/**
|
|
145
|
-
* Activates the switch on Space/Enter for non-native hosts and suppresses key
|
|
146
|
-
* repeat. Bound via `data-action` (keydown). A native `<button type="button">`
|
|
147
|
-
* owns the initial key-to-click synthesis; repeated keydowns are canceled before
|
|
148
|
-
* the browser can synthesize additional clicks.
|
|
149
|
-
*/
|
|
150
136
|
onKeydown(event) {
|
|
151
137
|
if (event.defaultPrevented) return;
|
|
152
138
|
if (!this.#isSupportedHost) return;
|
|
@@ -164,13 +150,6 @@ var SwitchController = class extends Controller {
|
|
|
164
150
|
if (event.repeat) return;
|
|
165
151
|
this.toggle();
|
|
166
152
|
}
|
|
167
|
-
/**
|
|
168
|
-
* Re-derives the defaults and the field after the page changed the host, then
|
|
169
|
-
* reports a checked state that moved. Records still queued are read first, so
|
|
170
|
-
* an author's write is not lost when observation is suspended for this pass's
|
|
171
|
-
* own writes; observation resumes before the report, so an edit a listener
|
|
172
|
-
* makes is seen by the next pass.
|
|
173
|
-
*/
|
|
174
153
|
#reconcile() {
|
|
175
154
|
this.#releaseAuthoredDefaults(this.#attributeObserver.takeRecords());
|
|
176
155
|
this.#attributeObserver.disconnect();
|
|
@@ -179,14 +158,12 @@ var SwitchController = class extends Controller {
|
|
|
179
158
|
this.#observeAttributes();
|
|
180
159
|
this.#reportMove();
|
|
181
160
|
}
|
|
182
|
-
/** Reports a checked state that moved since the one this switch last published. */
|
|
183
161
|
#reportMove() {
|
|
184
162
|
const checked = this.#checked;
|
|
185
163
|
if (checked === this.#committedChecked) return;
|
|
186
164
|
this.#committedChecked = checked;
|
|
187
165
|
this.dispatch("reconcile", { detail: { checked } });
|
|
188
166
|
}
|
|
189
|
-
/** Re-adds only missing defaults, preserving every authored attribute value. */
|
|
190
167
|
#reconcileDefaults() {
|
|
191
168
|
if (!this.#isSupportedHost) {
|
|
192
169
|
this.#removeOwnedDefaults();
|
|
@@ -198,20 +175,20 @@ var SwitchController = class extends Controller {
|
|
|
198
175
|
this.#setOwnedDefault("tabindex", "0");
|
|
199
176
|
}
|
|
200
177
|
}
|
|
201
|
-
/** Mirrors the checked state into the optional form field. */
|
|
202
178
|
#mirrorField(notify) {
|
|
203
179
|
if (!this.hasFieldTarget) return;
|
|
204
180
|
const moved = writeField(this.fieldTarget, this.#checked ? "true" : "false");
|
|
205
181
|
if (moved && notify) commitField(this.fieldTarget);
|
|
206
182
|
}
|
|
207
|
-
/** Records a newly introduced default without claiming authored markup. */
|
|
208
183
|
#setOwnedDefault(name, value) {
|
|
209
|
-
if (setDefaultAttribute(this.element, name, value)) this.#ownedDefaults.
|
|
184
|
+
if (setDefaultAttribute(this.element, name, value)) this.#ownedDefaults.set(name, value);
|
|
185
|
+
}
|
|
186
|
+
#releaseDefaultsRewrittenWhileAway() {
|
|
187
|
+
for (const [name, written] of this.#ownedDefaults) {
|
|
188
|
+
const value = this.element.getAttribute(name);
|
|
189
|
+
if (value !== null && value !== written) this.#ownedDefaults.delete(name);
|
|
190
|
+
}
|
|
210
191
|
}
|
|
211
|
-
/**
|
|
212
|
-
* Gives ownership back when a retained-element morph authors a value. Missing
|
|
213
|
-
* owned attributes stay owned so reconciliation can restore them.
|
|
214
|
-
*/
|
|
215
192
|
#releaseAuthoredDefaults(records) {
|
|
216
193
|
for (const record of records) {
|
|
217
194
|
if (record.target === this.element) {
|
|
@@ -226,13 +203,10 @@ var SwitchController = class extends Controller {
|
|
|
226
203
|
}
|
|
227
204
|
this.#internalAttributeValues.clear();
|
|
228
205
|
}
|
|
229
|
-
/** Removes only defaults introduced by this instance when a host becomes invalid. */
|
|
230
206
|
#removeOwnedDefaults() {
|
|
231
|
-
for (const name of this.#ownedDefaults) this.element.removeAttribute(name);
|
|
207
|
+
for (const name of this.#ownedDefaults.keys()) this.element.removeAttribute(name);
|
|
232
208
|
this.#ownedDefaults.clear();
|
|
233
|
-
this.#internalAttributeValues.clear();
|
|
234
209
|
}
|
|
235
|
-
/** Watches retained host attributes that Turbo can morph without reconnecting. */
|
|
236
210
|
#observeAttributes() {
|
|
237
211
|
this.#attributeObserver.observe(this.element, {
|
|
238
212
|
attributes: true,
|
|
@@ -247,12 +221,10 @@ var SwitchController = class extends Controller {
|
|
|
247
221
|
ancestor = ancestor.parentElement;
|
|
248
222
|
}
|
|
249
223
|
}
|
|
250
|
-
/** Whether the host has one activation model that this controller can own. */
|
|
251
224
|
get #isSupportedHost() {
|
|
252
225
|
if (this.element instanceof HTMLButtonElement) return this.element.type === "button";
|
|
253
226
|
return !isInteractiveHost(this.element);
|
|
254
227
|
}
|
|
255
|
-
/** Whether ARIA or native HTML semantics make the supported switch inoperable. */
|
|
256
228
|
get #isActivationDisabled() {
|
|
257
229
|
let current = this.element;
|
|
258
230
|
while (current) {
|
|
@@ -262,25 +234,26 @@ var SwitchController = class extends Controller {
|
|
|
262
234
|
if (!(this.element instanceof HTMLButtonElement)) return false;
|
|
263
235
|
return this.element.disabled || inheritsFieldsetDisabled(this.element);
|
|
264
236
|
}
|
|
265
|
-
/** Whether the switch is currently on. */
|
|
266
237
|
get #checked() {
|
|
267
238
|
return this.element.getAttribute("aria-checked") === "true";
|
|
268
239
|
}
|
|
269
|
-
/**
|
|
270
|
-
* Applies one toggle the user made. The state is taken as published before
|
|
271
|
-
* either report goes out, so a listener that toggles again or writes the
|
|
272
|
-
* attribute is measured from it.
|
|
273
|
-
*/
|
|
274
240
|
#commit(value) {
|
|
275
241
|
const reflected = value ? "true" : "false";
|
|
276
242
|
this.#internalAttributeValues.set("aria-checked", reflected);
|
|
243
|
+
if (this.#ownedDefaults.has("aria-checked")) this.#ownedDefaults.set("aria-checked", reflected);
|
|
277
244
|
this.element.setAttribute("aria-checked", reflected);
|
|
245
|
+
const changed = value !== this.#committedChecked;
|
|
278
246
|
this.#committedChecked = value;
|
|
247
|
+
if (!changed) {
|
|
248
|
+
this.#mirrorField(false);
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
const token = this.#moves.record();
|
|
279
252
|
this.#mirrorField(true);
|
|
253
|
+
if (!this.#moves.isLatest(token)) return;
|
|
280
254
|
this.dispatch("change", { detail: { checked: value } });
|
|
281
255
|
}
|
|
282
256
|
};
|
|
283
257
|
|
|
284
258
|
export { SwitchController };
|
|
285
|
-
//# sourceMappingURL=switch_controller.js.map
|
|
286
259
|
//# sourceMappingURL=switch_controller.js.map
|
|
@@ -1,33 +1,45 @@
|
|
|
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 isReservedArrowChord(event, allow = []) {
|
|
12
30
|
if (!event.key.startsWith("Arrow")) return false;
|
|
13
31
|
return event.altKey && !allow.includes("alt") || event.ctrlKey && !allow.includes("ctrl") || event.metaKey && !allow.includes("meta") || event.shiftKey && !allow.includes("shift");
|
|
14
32
|
}
|
|
15
33
|
|
|
16
|
-
|
|
34
|
+
function targetSelector(identifier, name) {
|
|
35
|
+
return `[data-${identifier}-target~="${name}"]`;
|
|
36
|
+
}
|
|
37
|
+
|
|
17
38
|
var TabsController = class extends Controller {
|
|
18
39
|
static targets = ["tab", "panel", "list"];
|
|
19
40
|
static actions = ["onKeydown", "select"];
|
|
20
41
|
static events = ["change"];
|
|
21
|
-
/** Whether selection moves are reported: set once `connect()` settled the baseline. */
|
|
22
42
|
#reporting = false;
|
|
23
|
-
/**
|
|
24
|
-
* Selects the initially active tab: the pre-selected one, else the first.
|
|
25
|
-
*
|
|
26
|
-
* `findIndex` makes this first-wins when the author marked several — the first
|
|
27
|
-
* in DOM order is the only deterministic reading of "which one did they mean" —
|
|
28
|
-
* and `#selectIndex` then writes an explicit value onto every tab, so a
|
|
29
|
-
* forgotten `aria-selected` cannot leave a tab looking unselectable.
|
|
30
|
-
*/
|
|
31
43
|
connect() {
|
|
32
44
|
const preselected = this.tabTargets.findIndex(
|
|
33
45
|
(tab) => tab.getAttribute("aria-selected") === "true"
|
|
@@ -35,19 +47,18 @@ var TabsController = class extends Controller {
|
|
|
35
47
|
this.#selectIndex(preselected === -1 ? 0 : preselected, { focus: false });
|
|
36
48
|
this.#reporting = true;
|
|
37
49
|
}
|
|
38
|
-
/**
|
|
39
|
-
* Stops reporting until the next `connect()` has settled its baseline, so that
|
|
40
|
-
* normalization stays silent.
|
|
41
|
-
*/
|
|
42
50
|
disconnect() {
|
|
43
51
|
this.#reporting = false;
|
|
44
52
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const
|
|
48
|
-
if (
|
|
53
|
+
select(source) {
|
|
54
|
+
const { host, origin, reason } = actionSource(source);
|
|
55
|
+
const tab = host?.closest(targetSelector(this.identifier, "tab"));
|
|
56
|
+
if (!tab || !this.tabTargets.includes(tab)) return;
|
|
57
|
+
if (origin && origin.closest(`[data-controller~="${this.identifier}"]`) !== this.element)
|
|
58
|
+
return;
|
|
59
|
+
const index = this.tabTargets.indexOf(tab);
|
|
60
|
+
this.#selectIndex(index, { focus: false }, reason);
|
|
49
61
|
}
|
|
50
|
-
/** Implements arrow/Home/End navigation with automatic activation. */
|
|
51
62
|
onKeydown(event) {
|
|
52
63
|
if (event.defaultPrevented) return;
|
|
53
64
|
if (isReservedArrowChord(event)) return;
|
|
@@ -75,11 +86,7 @@ var TabsController = class extends Controller {
|
|
|
75
86
|
event.preventDefault();
|
|
76
87
|
this.#selectIndex(nextIndex, { focus: true });
|
|
77
88
|
}
|
|
78
|
-
|
|
79
|
-
* Activates the tab/panel pair at `index`: updates `aria-selected`, the roving
|
|
80
|
-
* `tabindex`, and panel visibility. Optionally moves focus to the new tab.
|
|
81
|
-
*/
|
|
82
|
-
#selectIndex(index, { focus }) {
|
|
89
|
+
#selectIndex(index, { focus }, reason = "user") {
|
|
83
90
|
const tabs = this.tabTargets;
|
|
84
91
|
const previous = tabs.findIndex((tab) => tab.getAttribute("aria-selected") === "true");
|
|
85
92
|
tabs.forEach((tab, i) => {
|
|
@@ -92,7 +99,7 @@ var TabsController = class extends Controller {
|
|
|
92
99
|
});
|
|
93
100
|
if (previous !== index && this.#reporting) {
|
|
94
101
|
this.dispatch("change", {
|
|
95
|
-
detail: { index, total: tabs.length, previous },
|
|
102
|
+
detail: { index, total: tabs.length, previous, reason },
|
|
96
103
|
cancelable: false
|
|
97
104
|
});
|
|
98
105
|
}
|
|
@@ -101,5 +108,4 @@ var TabsController = class extends Controller {
|
|
|
101
108
|
};
|
|
102
109
|
|
|
103
110
|
export { TabsController };
|
|
104
|
-
//# sourceMappingURL=tabs_controller.js.map
|
|
105
111
|
//# sourceMappingURL=tabs_controller.js.map
|