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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +209 -0
- data/README.md +128 -1
- data/dist/cable/index.js +392 -417
- data/dist/controllers/accordion_controller.js +228 -28
- data/dist/controllers/alert_dialog_controller.js +862 -221
- data/dist/controllers/announcer_controller.js +135 -191
- data/dist/controllers/aspect_ratio_controller.js +0 -10
- data/dist/controllers/auto_submit_controller.js +295 -110
- data/dist/controllers/avatar_controller.js +172 -106
- data/dist/controllers/breadcrumb_controller.js +0 -129
- data/dist/controllers/bulk_select_controller.js +264 -53
- data/dist/controllers/calendar_controller.js +346 -180
- data/dist/controllers/carousel_controller.js +587 -300
- data/dist/controllers/character_counter_controller.js +271 -143
- data/dist/controllers/checkbox_controller.js +77 -75
- data/dist/controllers/clipboard_controller.js +187 -108
- data/dist/controllers/collapsible_controller.js +374 -108
- data/dist/controllers/color_picker_controller.js +183 -127
- data/dist/controllers/combobox_controller.js +426 -109
- data/dist/controllers/command_palette_controller.js +994 -350
- data/dist/controllers/conditional_fields_controller.js +212 -125
- data/dist/controllers/confirm_controller.js +939 -245
- data/dist/controllers/context_menu_controller.js +275 -127
- data/dist/controllers/count_up_controller.js +73 -21
- data/dist/controllers/countdown_controller.js +235 -97
- data/dist/controllers/currency_input_controller.js +241 -118
- data/dist/controllers/data_grid_controller.js +229 -135
- data/dist/controllers/date_range_picker_controller.js +359 -165
- data/dist/controllers/dialog_controller.js +887 -225
- data/dist/controllers/direct_upload_controller.js +80 -142
- data/dist/controllers/dirty_form_controller.js +26 -47
- data/dist/controllers/dismissible_controller.js +175 -20
- data/dist/controllers/drawer_controller.js +963 -336
- data/dist/controllers/dropdown_controller.js +342 -85
- data/dist/controllers/editable_controller.js +182 -58
- data/dist/controllers/empty_state_controller.js +67 -53
- data/dist/controllers/file_dropzone_controller.js +426 -229
- data/dist/controllers/filter_controller.js +78 -36
- data/dist/controllers/flash_controller.js +328 -211
- data/dist/controllers/focus_controller.js +654 -239
- data/dist/controllers/form_field_controller.js +153 -136
- data/dist/controllers/form_validation_controller.js +29 -96
- data/dist/controllers/frame_loading_controller.js +263 -198
- data/dist/controllers/highlight_controller.js +119 -70
- data/dist/controllers/hover_card_controller.js +335 -118
- data/dist/controllers/idle_controller.js +316 -65
- data/dist/controllers/input_mask_controller.js +116 -61
- data/dist/controllers/intersection_controller.js +147 -103
- data/dist/controllers/lazy_frame_controller.js +9 -56
- data/dist/controllers/listbox_controller.js +413 -153
- data/dist/controllers/local_time_controller.js +54 -64
- data/dist/controllers/masonry_controller.js +70 -95
- data/dist/controllers/menu_controller.js +254 -167
- data/dist/controllers/menubar_controller.js +206 -331
- data/dist/controllers/meter_controller.js +159 -71
- data/dist/controllers/multi_select_controller.js +797 -388
- data/dist/controllers/navigation_menu_controller.js +156 -231
- data/dist/controllers/nested_form_controller.js +373 -174
- data/dist/controllers/network_status_controller.js +241 -45
- data/dist/controllers/number_input_controller.js +387 -227
- data/dist/controllers/optimistic_controller.js +28 -75
- data/dist/controllers/otp_controller.js +344 -196
- data/dist/controllers/overflow_indicator_controller.js +167 -93
- data/dist/controllers/overflow_menu_controller.js +408 -277
- data/dist/controllers/pagination_controller.js +342 -120
- data/dist/controllers/password_reveal_controller.js +424 -99
- data/dist/controllers/password_strength_controller.js +99 -153
- data/dist/controllers/persist_controller.js +252 -96
- data/dist/controllers/pointer_drag_controller.js +263 -164
- data/dist/controllers/popover_controller.js +267 -94
- data/dist/controllers/portal_controller.js +209 -72
- data/dist/controllers/preview_guard_controller.js +243 -112
- data/dist/controllers/progress_controller.js +132 -59
- data/dist/controllers/radio_group_controller.js +138 -133
- data/dist/controllers/range_slider_controller.js +245 -112
- data/dist/controllers/rating_controller.js +429 -194
- data/dist/controllers/read_more_controller.js +185 -47
- 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 +294 -118
- data/dist/controllers/roving_controller.js +39 -84
- data/dist/controllers/scroll_area_controller.js +428 -221
- data/dist/controllers/scroll_restore_controller.js +0 -53
- data/dist/controllers/scroll_visibility_controller.js +395 -146
- data/dist/controllers/scrollspy_controller.js +181 -236
- data/dist/controllers/separator_controller.js +288 -132
- data/dist/controllers/sidebar_controller.js +1071 -375
- data/dist/controllers/skeleton_controller.js +248 -92
- data/dist/controllers/slider_controller.js +211 -96
- data/dist/controllers/smart_sticky_header_controller.js +163 -54
- data/dist/controllers/sortable_controller.js +13 -110
- data/dist/controllers/spinner_controller.js +277 -161
- data/dist/controllers/step_indicator_controller.js +107 -53
- data/dist/controllers/stepper_controller.js +171 -48
- data/dist/controllers/stick_to_bottom_controller.js +310 -116
- data/dist/controllers/sticky_observer_controller.js +214 -49
- data/dist/controllers/submit_once_controller.js +294 -192
- data/dist/controllers/switch_controller.js +107 -43
- data/dist/controllers/tabs_controller.js +50 -25
- data/dist/controllers/tags_input_controller.js +290 -149
- data/dist/controllers/textarea_autosize_controller.js +71 -58
- data/dist/controllers/theme_controller.js +148 -80
- data/dist/controllers/time_picker_controller.js +162 -81
- data/dist/controllers/toast_controller.js +365 -193
- data/dist/controllers/toggle_group_controller.js +194 -99
- data/dist/controllers/toolbar_controller.js +92 -90
- data/dist/controllers/tooltip_controller.js +185 -125
- data/dist/controllers/transition_controller.js +123 -115
- data/dist/controllers/tree_view_controller.js +248 -290
- data/dist/index.js +8649 -8572
- data/dist/positioning/index.js +148 -45
- data/lib/stimeo/ui/version.rb +1 -1
- metadata +3 -3
|
@@ -1,51 +1,253 @@
|
|
|
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
|
+
var StateRegions = class _StateRegions {
|
|
41
|
+
static #registry = sharedRegistry(
|
|
42
|
+
"stimeo-ui.state-regions.registry.v1",
|
|
43
|
+
() => ({ stacks: /* @__PURE__ */ new WeakMap(), names: /* @__PURE__ */ new Set() })
|
|
44
|
+
);
|
|
45
|
+
#whenTrue;
|
|
46
|
+
#whenFalse;
|
|
47
|
+
#record;
|
|
48
|
+
constructor(options, owner) {
|
|
49
|
+
this.#whenTrue = options.whenTrue;
|
|
50
|
+
this.#whenFalse = options.whenFalse ?? null;
|
|
51
|
+
this.#record = `data-${owner}-hidden-region`;
|
|
52
|
+
_StateRegions.#registry.names.add(this.#record);
|
|
53
|
+
}
|
|
54
|
+
reflect(host, isTrue) {
|
|
55
|
+
const shown = this.#inside(host, this.#whenTrue());
|
|
56
|
+
if (!this.#whenFalse) {
|
|
57
|
+
for (const region of shown) this.#write(region, !isTrue);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const hiddenSide = this.#inside(host, this.#whenFalse());
|
|
61
|
+
if (shown.length === 0 || hiddenSide.length === 0) {
|
|
62
|
+
for (const region of shown) this.#give(region);
|
|
63
|
+
for (const region of hiddenSide) this.#give(region);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
for (const region of shown) this.#write(region, !isTrue);
|
|
67
|
+
for (const region of hiddenSide) this.#write(region, isTrue);
|
|
68
|
+
}
|
|
69
|
+
release(host) {
|
|
70
|
+
for (const region of [host, ...host.querySelectorAll("*")]) {
|
|
71
|
+
if (!(region instanceof HTMLElement)) continue;
|
|
72
|
+
const stack = this.#held(region);
|
|
73
|
+
const hold = stack && this.#take(stack);
|
|
74
|
+
if (!stack || !hold) continue;
|
|
75
|
+
const owns = region.hidden === hold.written && !this.#covered(region, stack, hold);
|
|
76
|
+
const index = stack.indexOf(hold);
|
|
77
|
+
stack.splice(index, 1);
|
|
78
|
+
const above = stack[index];
|
|
79
|
+
if (above) above.linked &&= hold.linked;
|
|
80
|
+
if (owns) {
|
|
81
|
+
const below = hold.linked ? stack[index - 1] : void 0;
|
|
82
|
+
if (below) {
|
|
83
|
+
if (region.hidden !== below.written) region.hidden = below.written;
|
|
84
|
+
} else if (region.getAttribute("hidden") !== hold.authored) {
|
|
85
|
+
if (hold.authored === null) region.removeAttribute("hidden");
|
|
86
|
+
else region.setAttribute("hidden", hold.authored);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
this.#mark(region, stack);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
#write(region, hidden) {
|
|
93
|
+
const stack = this.#stack(region);
|
|
94
|
+
const hold = this.#take(stack) ?? this.#acquire(region, stack);
|
|
95
|
+
const shows = !this.#covered(region, stack, hold);
|
|
96
|
+
hold.written = hidden;
|
|
97
|
+
if (shows && region.hidden !== hidden) region.hidden = hidden;
|
|
98
|
+
this.#mark(region, stack);
|
|
99
|
+
}
|
|
100
|
+
#give(region) {
|
|
101
|
+
const stack = this.#held(region);
|
|
102
|
+
if (stack && this.#take(stack)?.written) this.#write(region, false);
|
|
103
|
+
}
|
|
104
|
+
#held(region) {
|
|
105
|
+
return _StateRegions.#registry.stacks.has(region) || region.hasAttribute(this.#record) ? this.#stack(region) : void 0;
|
|
106
|
+
}
|
|
107
|
+
#stack(region) {
|
|
108
|
+
const stacks = _StateRegions.#registry.stacks;
|
|
109
|
+
const stack = stacks.get(region) ?? this.#seed(region);
|
|
110
|
+
stacks.set(region, stack);
|
|
111
|
+
return stack;
|
|
112
|
+
}
|
|
113
|
+
#seed(region) {
|
|
114
|
+
const found = [];
|
|
115
|
+
for (const record of _StateRegions.#registry.names) {
|
|
116
|
+
const read = this.#recorded(region, record);
|
|
117
|
+
if (read) found.push({ ...read, record });
|
|
118
|
+
}
|
|
119
|
+
const depth = ({ beneath }) => beneath.filter((name) => found.some((other) => other.record === name)).length;
|
|
120
|
+
return found.sort((one, other) => depth(one) - depth(other)).map(({ record, authored, written, linked }, index) => ({
|
|
121
|
+
instance: null,
|
|
122
|
+
record,
|
|
123
|
+
authored,
|
|
124
|
+
written,
|
|
125
|
+
linked: linked && index > 0
|
|
126
|
+
}));
|
|
127
|
+
}
|
|
128
|
+
#take(stack) {
|
|
129
|
+
const hold = stack.find((held) => held.instance === this) ?? stack.find((held) => held.instance === null && held.record === this.#record);
|
|
130
|
+
if (hold) hold.instance = this;
|
|
131
|
+
return hold;
|
|
132
|
+
}
|
|
133
|
+
#covered(region, stack, hold) {
|
|
134
|
+
return stack.slice(stack.indexOf(hold) + 1).some((above) => above.written === region.hidden);
|
|
135
|
+
}
|
|
136
|
+
#acquire(region, stack) {
|
|
137
|
+
const top = stack.at(-1);
|
|
138
|
+
const over = top && region.hidden === top.written ? top : void 0;
|
|
139
|
+
const hold = {
|
|
140
|
+
instance: this,
|
|
141
|
+
record: this.#record,
|
|
142
|
+
authored: over ? over.authored : region.getAttribute("hidden"),
|
|
143
|
+
written: false,
|
|
144
|
+
linked: over !== void 0
|
|
145
|
+
};
|
|
146
|
+
stack.push(hold);
|
|
147
|
+
return hold;
|
|
148
|
+
}
|
|
149
|
+
#mark(region, stack) {
|
|
150
|
+
const records = /* @__PURE__ */ new Map([[this.#record, null]]);
|
|
151
|
+
stack.forEach((hold, index) => {
|
|
152
|
+
const names = stack.slice(0, index).map((under) => under.record);
|
|
153
|
+
const beneath = [...new Set(names.reverse())].filter((name) => name !== hold.record);
|
|
154
|
+
const marker = hold.linked || beneath.length === 0 ? [] : [false];
|
|
155
|
+
records.set(
|
|
156
|
+
hold.record,
|
|
157
|
+
region.getAttribute("hidden") === hold.authored && beneath.length === 0 && index === stack.length - 1 ? null : JSON.stringify([hold.authored, hold.written, ...marker, ...beneath])
|
|
158
|
+
);
|
|
159
|
+
});
|
|
160
|
+
for (const [name, recorded] of records) {
|
|
161
|
+
if (region.getAttribute(name) === recorded) continue;
|
|
162
|
+
if (recorded === null) region.removeAttribute(name);
|
|
163
|
+
else region.setAttribute(name, recorded);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
#recorded(region, name) {
|
|
167
|
+
try {
|
|
168
|
+
const parsed = JSON.parse(region.getAttribute(name) ?? "null");
|
|
169
|
+
if (!Array.isArray(parsed)) return null;
|
|
170
|
+
const [authored, written, ...rest] = parsed;
|
|
171
|
+
if (authored !== null && typeof authored !== "string") return null;
|
|
172
|
+
const linked = rest[0] !== false;
|
|
173
|
+
const beneath = linked ? rest : rest.slice(1);
|
|
174
|
+
if (!beneath.every((under) => typeof under === "string")) return null;
|
|
175
|
+
return typeof written === "boolean" ? { authored, written, linked, beneath } : null;
|
|
176
|
+
} catch {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
#inside(host, regions) {
|
|
181
|
+
return regions.filter((region) => host.contains(region));
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
function targetSelector(identifier, name) {
|
|
186
|
+
return `[data-${identifier}-target~="${name}"]`;
|
|
187
|
+
}
|
|
188
|
+
|
|
12
189
|
var AccordionController = class extends Controller {
|
|
13
|
-
static targets = ["trigger", "panel"];
|
|
190
|
+
static targets = ["trigger", "panel", "expandedLabel", "collapsedLabel"];
|
|
14
191
|
static actions = ["collapseAll", "expandAll", "onKeydown", "toggle"];
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
192
|
+
static events = ["close", "open"];
|
|
193
|
+
#labels = new StateRegions(
|
|
194
|
+
{
|
|
195
|
+
whenTrue: () => this.expandedLabelTargets,
|
|
196
|
+
whenFalse: () => this.collapsedLabelTargets
|
|
197
|
+
},
|
|
198
|
+
this.identifier
|
|
199
|
+
);
|
|
200
|
+
triggerTargetConnected(trigger) {
|
|
201
|
+
this.#reflectLabels(trigger);
|
|
202
|
+
}
|
|
203
|
+
triggerTargetDisconnected(trigger) {
|
|
204
|
+
if (!this.triggerTargets.includes(trigger)) this.#labels.release(trigger);
|
|
205
|
+
}
|
|
206
|
+
expandedLabelTargetConnected(label) {
|
|
207
|
+
this.#settleLabelHost(label);
|
|
208
|
+
}
|
|
209
|
+
collapsedLabelTargetConnected(label) {
|
|
210
|
+
this.#settleLabelHost(label);
|
|
211
|
+
}
|
|
212
|
+
#settleLabelHost(label) {
|
|
213
|
+
const trigger = this.triggerTargets.find((candidate) => candidate.contains(label));
|
|
214
|
+
if (trigger) this.#reflectLabels(trigger);
|
|
215
|
+
}
|
|
216
|
+
toggle(source) {
|
|
217
|
+
const { host, origin, reason } = actionSource(source);
|
|
218
|
+
if (origin && origin.closest(`[data-controller~="${this.identifier}"]`) !== this.element)
|
|
219
|
+
return;
|
|
220
|
+
const trigger = host?.closest(targetSelector(this.identifier, "trigger"));
|
|
221
|
+
if (!trigger || !this.triggerTargets.includes(trigger)) return;
|
|
18
222
|
const panel = this.#panelFor(trigger);
|
|
19
223
|
if (!panel) return;
|
|
20
|
-
this.#setExpanded(trigger, panel, trigger.getAttribute("aria-expanded") !== "true");
|
|
224
|
+
this.#setExpanded(trigger, panel, trigger.getAttribute("aria-expanded") !== "true", reason);
|
|
21
225
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
this.#setAll(true);
|
|
226
|
+
expandAll(event) {
|
|
227
|
+
this.#setAll(true, stateReasonFor(event));
|
|
25
228
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
this.#setAll(false);
|
|
229
|
+
collapseAll(event) {
|
|
230
|
+
this.#setAll(false, stateReasonFor(event));
|
|
29
231
|
}
|
|
30
|
-
|
|
31
|
-
#setAll(open) {
|
|
232
|
+
#setAll(open, reason) {
|
|
32
233
|
for (const trigger of this.triggerTargets) {
|
|
33
234
|
const panel = this.#panelFor(trigger);
|
|
34
|
-
if (panel) this.#setExpanded(trigger, panel, open);
|
|
235
|
+
if (panel) this.#setExpanded(trigger, panel, open, reason);
|
|
35
236
|
}
|
|
36
237
|
}
|
|
37
|
-
|
|
38
|
-
|
|
238
|
+
#setExpanded(trigger, panel, open, reason) {
|
|
239
|
+
const was = trigger.getAttribute("aria-expanded") === "true";
|
|
39
240
|
trigger.setAttribute("aria-expanded", open ? "true" : "false");
|
|
40
241
|
panel.hidden = !open;
|
|
242
|
+
this.#labels.reflect(trigger, open);
|
|
243
|
+
if (was === open) return;
|
|
244
|
+
const detail = { reason, index: this.triggerTargets.indexOf(trigger), trigger, panel };
|
|
245
|
+
if (open) this.dispatch("open", { detail, cancelable: false });
|
|
246
|
+
else this.dispatch("close", { detail, cancelable: false });
|
|
247
|
+
}
|
|
248
|
+
#reflectLabels(trigger) {
|
|
249
|
+
this.#labels.reflect(trigger, trigger.getAttribute("aria-expanded") === "true");
|
|
41
250
|
}
|
|
42
|
-
/**
|
|
43
|
-
* Moves focus between headers per the APG keyboard model, skipping any header
|
|
44
|
-
* that is hidden or nested in a hidden subtree. A consumer may hide whole
|
|
45
|
-
* sections (e.g. an `stimeo--filter` that collapses empty groups), and an
|
|
46
|
-
* unperceivable header must never become an arrow-key target — otherwise
|
|
47
|
-
* `.focus()` lands on nothing and navigation appears to stall.
|
|
48
|
-
*/
|
|
49
251
|
onKeydown(event) {
|
|
50
252
|
if (event.defaultPrevented) return;
|
|
51
253
|
if (isReservedArrowChord(event)) return;
|
|
@@ -74,7 +276,6 @@ var AccordionController = class extends Controller {
|
|
|
74
276
|
event.preventDefault();
|
|
75
277
|
next?.focus();
|
|
76
278
|
}
|
|
77
|
-
/** Resolves the panel a header controls via its `aria-controls` reference. */
|
|
78
279
|
#panelFor(trigger) {
|
|
79
280
|
const id = trigger.getAttribute("aria-controls");
|
|
80
281
|
return id ? this.panelTargets.find((panel) => panel.id === id) ?? null : null;
|
|
@@ -82,5 +283,4 @@ var AccordionController = class extends Controller {
|
|
|
82
283
|
};
|
|
83
284
|
|
|
84
285
|
export { AccordionController };
|
|
85
|
-
//# sourceMappingURL=accordion_controller.js.map
|
|
86
286
|
//# sourceMappingURL=accordion_controller.js.map
|