stimeo-ui 0.17.0 → 0.18.1
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 +136 -2
- data/README.md +19 -10
- data/dist/cable/index.js +22 -14
- data/dist/controllers/accordion_controller.js +14 -6
- data/dist/controllers/alert_dialog_controller.js +145 -118
- data/dist/controllers/announcer_controller.js +113 -17
- data/dist/controllers/auto_submit_controller.js +185 -60
- data/dist/controllers/avatar_controller.js +55 -43
- data/dist/controllers/bulk_select_controller.js +64 -50
- data/dist/controllers/calendar_controller.js +55 -53
- data/dist/controllers/carousel_controller.js +124 -86
- data/dist/controllers/character_counter_controller.js +291 -95
- data/dist/controllers/checkbox_controller.js +6 -0
- data/dist/controllers/clipboard_controller.js +118 -27
- data/dist/controllers/collapsible_controller.js +52 -49
- data/dist/controllers/color_picker_controller.js +29 -7
- data/dist/controllers/combobox_controller.js +210 -71
- data/dist/controllers/command_palette_controller.js +290 -140
- data/dist/controllers/conditional_fields_controller.js +145 -58
- data/dist/controllers/confirm_controller.js +145 -118
- data/dist/controllers/context_menu_controller.js +138 -73
- data/dist/controllers/countdown_controller.js +109 -21
- data/dist/controllers/currency_input_controller.js +446 -72
- data/dist/controllers/data_grid_controller.js +134 -28
- data/dist/controllers/date_range_picker_controller.js +93 -72
- data/dist/controllers/dialog_controller.js +145 -118
- data/dist/controllers/direct_upload_controller.js +24 -14
- data/dist/controllers/dirty_form_controller.js +68 -31
- data/dist/controllers/dismissible_controller.js +66 -46
- data/dist/controllers/drawer_controller.js +145 -118
- data/dist/controllers/dropdown_controller.js +76 -63
- data/dist/controllers/editable_controller.js +200 -62
- data/dist/controllers/empty_state_controller.js +13 -6
- data/dist/controllers/file_dropzone_controller.js +55 -48
- data/dist/controllers/filter_controller.js +9 -2
- data/dist/controllers/flash_controller.js +66 -33
- data/dist/controllers/focus_controller.js +39 -14
- data/dist/controllers/form_field_controller.js +52 -43
- data/dist/controllers/form_validation_controller.js +18 -11
- data/dist/controllers/frame_loading_controller.js +55 -48
- data/dist/controllers/hover_card_controller.js +67 -61
- data/dist/controllers/idle_controller.js +10 -6
- data/dist/controllers/input_mask_controller.js +434 -24
- data/dist/controllers/intersection_controller.js +3 -0
- data/dist/controllers/lazy_frame_controller.js +15 -3
- data/dist/controllers/listbox_controller.js +260 -79
- data/dist/controllers/local_time_controller.js +48 -13
- data/dist/controllers/masonry_controller.js +11 -4
- data/dist/controllers/menu_controller.js +138 -82
- data/dist/controllers/menubar_controller.js +65 -19
- data/dist/controllers/meter_controller.js +270 -24
- data/dist/controllers/multi_select_controller.js +272 -95
- data/dist/controllers/navigation_menu_controller.js +67 -19
- data/dist/controllers/nested_form_controller.js +98 -64
- data/dist/controllers/network_status_controller.js +70 -52
- data/dist/controllers/number_input_controller.js +344 -94
- data/dist/controllers/optimistic_controller.js +29 -11
- data/dist/controllers/otp_controller.js +502 -104
- data/dist/controllers/overflow_indicator_controller.js +6 -0
- data/dist/controllers/overflow_menu_controller.js +136 -21
- data/dist/controllers/pagination_controller.js +47 -14
- data/dist/controllers/password_reveal_controller.js +52 -49
- data/dist/controllers/password_strength_controller.js +13 -6
- data/dist/controllers/persist_controller.js +48 -128
- data/dist/controllers/pointer_drag_controller.js +255 -24
- data/dist/controllers/popover_controller.js +91 -65
- data/dist/controllers/portal_controller.js +6 -0
- data/dist/controllers/preview_guard_controller.js +14 -3
- data/dist/controllers/progress_controller.js +271 -27
- data/dist/controllers/radio_group_controller.js +167 -80
- data/dist/controllers/range_slider_controller.js +27 -7
- data/dist/controllers/rating_controller.js +93 -57
- data/dist/controllers/read_more_controller.js +10 -6
- data/dist/controllers/reading_progress_controller.js +22 -5
- data/dist/controllers/relative_time_controller.js +75 -11
- data/dist/controllers/reset_on_restore_controller.js +6 -0
- data/dist/controllers/resizable_controller.js +55 -22
- data/dist/controllers/roving_controller.js +10 -4
- data/dist/controllers/scroll_area_controller.js +129 -88
- data/dist/controllers/scroll_restore_controller.js +14 -2
- data/dist/controllers/scroll_visibility_controller.js +93 -60
- data/dist/controllers/scrollspy_controller.js +11 -5
- data/dist/controllers/separator_controller.js +59 -43
- data/dist/controllers/sidebar_controller.js +150 -122
- data/dist/controllers/skeleton_controller.js +55 -48
- data/dist/controllers/slider_controller.js +27 -6
- data/dist/controllers/smart_sticky_header_controller.js +13 -5
- data/dist/controllers/sortable_controller.js +27 -14
- data/dist/controllers/spinner_controller.js +55 -48
- data/dist/controllers/step_indicator_controller.js +6 -0
- data/dist/controllers/stepper_controller.js +6 -0
- data/dist/controllers/stick_to_bottom_controller.js +19 -8
- data/dist/controllers/sticky_observer_controller.js +49 -43
- data/dist/controllers/submit_once_controller.js +145 -85
- data/dist/controllers/switch_controller.js +172 -28
- data/dist/controllers/tabs_controller.js +6 -1
- data/dist/controllers/tags_input_controller.js +157 -28
- data/dist/controllers/textarea_autosize_controller.js +40 -19
- data/dist/controllers/theme_controller.js +10 -4
- data/dist/controllers/time_picker_controller.js +192 -54
- data/dist/controllers/toast_controller.js +58 -27
- data/dist/controllers/toggle_group_controller.js +158 -56
- data/dist/controllers/toolbar_controller.js +150 -21
- data/dist/controllers/tree_view_controller.js +134 -25
- data/dist/index.js +2515 -1315
- data/dist/positioning/index.js +6 -0
- data/lib/generators/stimeo/install/install_generator.rb +7 -2
- data/lib/generators/stimeo/install/templates/stimeo.js +19 -6
- data/lib/stimeo/ui/version.rb +1 -1
- metadata +2 -2
|
@@ -6,63 +6,182 @@ var COMPOSITION_INPUT_TYPES = /* @__PURE__ */ new Set([
|
|
|
6
6
|
"deleteCompositionText",
|
|
7
7
|
"deleteByComposition"
|
|
8
8
|
]);
|
|
9
|
+
function isCompositionInputType(inputType) {
|
|
10
|
+
return inputType !== void 0 && COMPOSITION_INPUT_TYPES.has(inputType);
|
|
11
|
+
}
|
|
12
|
+
var BEFORE_MORPH_ATTRIBUTE = "turbo:before-morph-attribute";
|
|
13
|
+
var textOf = (element) => element.value;
|
|
14
|
+
var holdsFocus = (element) => element instanceof Node && element.getRootNode().activeElement === element;
|
|
9
15
|
var CompositionTracker = class {
|
|
10
16
|
#observedTargets = /* @__PURE__ */ new Set();
|
|
11
|
-
#
|
|
17
|
+
#composition = null;
|
|
12
18
|
#onStart;
|
|
13
19
|
#onEnd;
|
|
20
|
+
#onAbandon;
|
|
14
21
|
#confirmedTarget = null;
|
|
22
|
+
#keyComposing = false;
|
|
23
|
+
#connected = false;
|
|
24
|
+
#abandoned = null;
|
|
15
25
|
constructor(options = {}) {
|
|
16
26
|
this.#onStart = options.onStart;
|
|
17
27
|
this.#onEnd = options.onEnd;
|
|
28
|
+
this.#onAbandon = options.onAbandon;
|
|
18
29
|
}
|
|
19
30
|
observe(target) {
|
|
31
|
+
this.#connected = true;
|
|
20
32
|
if (this.#observedTargets.has(target)) return;
|
|
21
33
|
target.addEventListener("compositionstart", this.#handleStart);
|
|
22
34
|
target.addEventListener("compositionend", this.#handleEnd);
|
|
23
|
-
target.addEventListener("keydown", this.#handleKeydown);
|
|
35
|
+
target.addEventListener("keydown", this.#handleKeydown, true);
|
|
36
|
+
target.addEventListener("beforeinput", this.#handleBeforeInput, true);
|
|
37
|
+
target.addEventListener("input", this.#handleInput, true);
|
|
38
|
+
target.addEventListener("blur", this.#handleBlur, true);
|
|
39
|
+
target.addEventListener(BEFORE_MORPH_ATTRIBUTE, this.#handleBeforeMorphAttribute);
|
|
24
40
|
this.#observedTargets.add(target);
|
|
25
41
|
}
|
|
26
42
|
unobserve(target) {
|
|
27
43
|
if (!this.#observedTargets.delete(target)) return;
|
|
28
|
-
|
|
29
|
-
target
|
|
30
|
-
target.removeEventListener("keydown", this.#handleKeydown);
|
|
31
|
-
this.#activeTargets.delete(target);
|
|
44
|
+
this.#release(target);
|
|
45
|
+
if (this.#composition?.target === target) this.#letGo();
|
|
32
46
|
if (this.#confirmedTarget === target) this.#confirmedTarget = null;
|
|
33
47
|
}
|
|
34
48
|
disconnect() {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
target.removeEventListener("compositionend", this.#handleEnd);
|
|
38
|
-
target.removeEventListener("keydown", this.#handleKeydown);
|
|
39
|
-
}
|
|
49
|
+
this.#connected = false;
|
|
50
|
+
for (const target of this.#observedTargets) this.#release(target);
|
|
40
51
|
this.#observedTargets.clear();
|
|
41
|
-
this.#
|
|
52
|
+
this.#letGo();
|
|
42
53
|
this.#confirmedTarget = null;
|
|
54
|
+
this.#keyComposing = false;
|
|
55
|
+
this.#abandoned = null;
|
|
43
56
|
}
|
|
44
57
|
isComposing(event) {
|
|
45
|
-
|
|
58
|
+
this.#settle();
|
|
59
|
+
return event?.isComposing === true || this.#composition !== null;
|
|
60
|
+
}
|
|
61
|
+
composingElement() {
|
|
62
|
+
this.#settle();
|
|
63
|
+
return this.#composition?.element ?? null;
|
|
64
|
+
}
|
|
65
|
+
owns(event) {
|
|
66
|
+
const element = this.composingElement();
|
|
67
|
+
return event.isComposing === true || element !== null && element === event.target;
|
|
46
68
|
}
|
|
47
69
|
consumesConfirmedInput(event) {
|
|
48
70
|
const confirmed = this.#confirmedTarget;
|
|
49
71
|
this.#confirmedTarget = null;
|
|
50
72
|
if (confirmed === null || confirmed !== event.target) return false;
|
|
51
73
|
const inputType = event.inputType;
|
|
52
|
-
return !inputType ||
|
|
74
|
+
return !inputType || isCompositionInputType(inputType);
|
|
75
|
+
}
|
|
76
|
+
#release(target) {
|
|
77
|
+
target.removeEventListener("compositionstart", this.#handleStart);
|
|
78
|
+
target.removeEventListener("compositionend", this.#handleEnd);
|
|
79
|
+
target.removeEventListener("keydown", this.#handleKeydown, true);
|
|
80
|
+
target.removeEventListener("beforeinput", this.#handleBeforeInput, true);
|
|
81
|
+
target.removeEventListener("input", this.#handleInput, true);
|
|
82
|
+
target.removeEventListener("blur", this.#handleBlur, true);
|
|
83
|
+
target.removeEventListener(BEFORE_MORPH_ATTRIBUTE, this.#handleBeforeMorphAttribute);
|
|
84
|
+
}
|
|
85
|
+
#letGo() {
|
|
86
|
+
const composition = this.#composition;
|
|
87
|
+
if (composition === null) return;
|
|
88
|
+
if (!holdsFocus(composition.element)) {
|
|
89
|
+
this.#composition = null;
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
queueMicrotask(() => {
|
|
93
|
+
if (this.#composition !== composition || this.#observedTargets.has(composition.target)) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
this.#composition = null;
|
|
97
|
+
if (!this.#connected) return;
|
|
98
|
+
this.#abandoned = { element: composition.element, inPlace: true };
|
|
99
|
+
this.#runAbandon();
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
#settle() {
|
|
103
|
+
const composition = this.#composition;
|
|
104
|
+
if (composition === null) return;
|
|
105
|
+
const { target, element, text } = composition;
|
|
106
|
+
if (target instanceof Node && element instanceof Node && !target.contains(element)) {
|
|
107
|
+
this.#composition = null;
|
|
108
|
+
} else if (textOf(element) !== text) {
|
|
109
|
+
this.#abandon(false);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
#abandon(inPlace) {
|
|
113
|
+
this.#owe(inPlace);
|
|
114
|
+
this.#composition = null;
|
|
115
|
+
queueMicrotask(this.#runAbandon);
|
|
53
116
|
}
|
|
117
|
+
#owe(inPlace) {
|
|
118
|
+
const composition = this.#composition;
|
|
119
|
+
if (composition !== null) this.#abandoned = { element: composition.element, inPlace };
|
|
120
|
+
}
|
|
121
|
+
#runAbandon = () => {
|
|
122
|
+
const abandoned = this.#abandoned;
|
|
123
|
+
if (abandoned === null) return;
|
|
124
|
+
this.#abandoned = null;
|
|
125
|
+
this.#onAbandon?.(abandoned.element, abandoned.inPlace);
|
|
126
|
+
};
|
|
54
127
|
#handleStart = (event) => {
|
|
55
128
|
this.#confirmedTarget = null;
|
|
56
|
-
|
|
129
|
+
this.#settle();
|
|
130
|
+
this.#owe(true);
|
|
131
|
+
const target = event.currentTarget;
|
|
132
|
+
const element = event.target;
|
|
133
|
+
this.#composition = { target, element, text: textOf(element) };
|
|
134
|
+
this.#runAbandon();
|
|
57
135
|
this.#onStart?.(event);
|
|
58
136
|
};
|
|
59
137
|
#handleEnd = (event) => {
|
|
60
|
-
|
|
138
|
+
this.#runAbandon();
|
|
139
|
+
if (this.#composition?.target === event.currentTarget) this.#composition = null;
|
|
61
140
|
this.#confirmedTarget = event.target;
|
|
62
141
|
this.#onEnd?.(event);
|
|
63
142
|
};
|
|
64
|
-
#handleKeydown = () => {
|
|
143
|
+
#handleKeydown = (event) => {
|
|
144
|
+
this.#settle();
|
|
145
|
+
this.#runAbandon();
|
|
65
146
|
this.#confirmedTarget = null;
|
|
147
|
+
this.#keyComposing = event.isComposing === true;
|
|
148
|
+
};
|
|
149
|
+
#handleBeforeInput = (event) => {
|
|
150
|
+
const keyComposing = this.#keyComposing;
|
|
151
|
+
this.#keyComposing = false;
|
|
152
|
+
this.#settle();
|
|
153
|
+
const { isComposing, inputType } = event;
|
|
154
|
+
if (this.#composition !== null && !keyComposing && !isComposing && inputType && !isCompositionInputType(inputType)) {
|
|
155
|
+
this.#abandon(true);
|
|
156
|
+
}
|
|
157
|
+
this.#runAbandon();
|
|
158
|
+
};
|
|
159
|
+
#handleInput = (event) => {
|
|
160
|
+
const composition = this.#composition;
|
|
161
|
+
const { isTrusted, isComposing, inputType } = event;
|
|
162
|
+
if (composition !== null && event.target === composition.element && (isTrusted || isComposing || isCompositionInputType(inputType))) {
|
|
163
|
+
composition.text = textOf(composition.element);
|
|
164
|
+
} else {
|
|
165
|
+
this.#settle();
|
|
166
|
+
}
|
|
167
|
+
this.#runAbandon();
|
|
168
|
+
};
|
|
169
|
+
#handleBlur = (event) => {
|
|
170
|
+
this.#settle();
|
|
171
|
+
this.#runAbandon();
|
|
172
|
+
const composition = this.#composition;
|
|
173
|
+
if (composition === null || composition.target === composition.element) return;
|
|
174
|
+
const { element } = composition;
|
|
175
|
+
if (!(element instanceof Node) || event.target !== element) return;
|
|
176
|
+
const root = element.getRootNode();
|
|
177
|
+
if (root.activeElement !== element) this.#composition = null;
|
|
178
|
+
};
|
|
179
|
+
#handleBeforeMorphAttribute = (event) => {
|
|
180
|
+
this.#settle();
|
|
181
|
+
const composition = this.#composition;
|
|
182
|
+
if (composition?.target !== event.currentTarget || event.target !== composition.element) return;
|
|
183
|
+
const detail = event.detail;
|
|
184
|
+
if (detail?.attributeName === "value") event.preventDefault();
|
|
66
185
|
};
|
|
67
186
|
};
|
|
68
187
|
|
|
@@ -89,6 +208,43 @@ function parseJsonObject(raw) {
|
|
|
89
208
|
return parsed;
|
|
90
209
|
}
|
|
91
210
|
|
|
211
|
+
var DetachGate = class _DetachGate {
|
|
212
|
+
#pending = false;
|
|
213
|
+
get pending() {
|
|
214
|
+
return this.#pending;
|
|
215
|
+
}
|
|
216
|
+
static isDetached(host) {
|
|
217
|
+
return !host.element.isConnected || !_DetachGate.#listed(host);
|
|
218
|
+
}
|
|
219
|
+
disconnected(host, teardown) {
|
|
220
|
+
const carried = _DetachGate.#carried(host);
|
|
221
|
+
if (!carried && _DetachGate.isDetached(host)) {
|
|
222
|
+
this.#pending = false;
|
|
223
|
+
teardown();
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
this.#pending = true;
|
|
227
|
+
const probe = () => {
|
|
228
|
+
if (!this.#pending) return;
|
|
229
|
+
this.#pending = false;
|
|
230
|
+
teardown();
|
|
231
|
+
};
|
|
232
|
+
if (carried) setTimeout(probe, 0);
|
|
233
|
+
else queueMicrotask(probe);
|
|
234
|
+
}
|
|
235
|
+
static #carried(host) {
|
|
236
|
+
const element = host.element;
|
|
237
|
+
return !element.isConnected && element.closest("[data-turbo-permanent]") !== null && _DetachGate.#listed(host);
|
|
238
|
+
}
|
|
239
|
+
static #listed(host) {
|
|
240
|
+
const tokens = (host.element.getAttribute("data-controller") ?? "").split(/\s+/);
|
|
241
|
+
return tokens.includes(host.identifier);
|
|
242
|
+
}
|
|
243
|
+
cancel() {
|
|
244
|
+
this.#pending = false;
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
|
|
92
248
|
function inheritsFieldsetDisabled(control) {
|
|
93
249
|
let fieldset = control.closest("fieldset[disabled]");
|
|
94
250
|
while (fieldset) {
|
|
@@ -99,6 +255,120 @@ function inheritsFieldsetDisabled(control) {
|
|
|
99
255
|
return false;
|
|
100
256
|
}
|
|
101
257
|
|
|
258
|
+
function sharedRegistry(key, create) {
|
|
259
|
+
const symbol = Symbol.for(key);
|
|
260
|
+
const scope = globalThis;
|
|
261
|
+
const existing = scope[symbol];
|
|
262
|
+
if (existing !== void 0) return existing;
|
|
263
|
+
const registry = create();
|
|
264
|
+
Object.defineProperty(globalThis, symbol, { value: registry });
|
|
265
|
+
return registry;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
var PREFIX = "__stimeo_raw_v1_";
|
|
269
|
+
var documents = sharedRegistry(
|
|
270
|
+
"stimeo-ui.form-data-projection.registry.v1",
|
|
271
|
+
() => /* @__PURE__ */ new WeakMap()
|
|
272
|
+
);
|
|
273
|
+
function decodedName(key) {
|
|
274
|
+
if (!key.startsWith(PREFIX)) return null;
|
|
275
|
+
const separator = PREFIX.length + 32;
|
|
276
|
+
if (key[separator] !== ":") return null;
|
|
277
|
+
try {
|
|
278
|
+
const name = JSON.parse(key.slice(separator + 1));
|
|
279
|
+
return typeof name === "string" ? name : null;
|
|
280
|
+
} catch {
|
|
281
|
+
return null;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
function projectedFieldName(name) {
|
|
285
|
+
return decodedName(name) ?? name;
|
|
286
|
+
}
|
|
287
|
+
function forDocument(document2) {
|
|
288
|
+
const existing = documents.get(document2);
|
|
289
|
+
if (existing) return existing;
|
|
290
|
+
const state = {
|
|
291
|
+
fields: /* @__PURE__ */ new Map(),
|
|
292
|
+
byField: /* @__PURE__ */ new WeakMap(),
|
|
293
|
+
handle: (event) => {
|
|
294
|
+
const data = event.formData;
|
|
295
|
+
if (!data) return;
|
|
296
|
+
const entries = [...data.entries()];
|
|
297
|
+
let changed = false;
|
|
298
|
+
const projected = entries.map(([key, value]) => {
|
|
299
|
+
const name = decodedName(key);
|
|
300
|
+
if (name === null) return [key, value];
|
|
301
|
+
changed = true;
|
|
302
|
+
const field = state.fields.get(key);
|
|
303
|
+
const live = field && field.field.name === key && field.field.form === event.target;
|
|
304
|
+
return [name, live ? field.project(value) : ""];
|
|
305
|
+
});
|
|
306
|
+
if (!changed) return;
|
|
307
|
+
for (const key of new Set(data.keys())) data.delete(key);
|
|
308
|
+
for (const [name, value] of projected) data.append(name, value);
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
documents.set(document2, state);
|
|
312
|
+
document2.addEventListener("formdata", state.handle, { capture: true });
|
|
313
|
+
return state;
|
|
314
|
+
}
|
|
315
|
+
function prepareFormDataProjection(form) {
|
|
316
|
+
const state = forDocument(form.ownerDocument);
|
|
317
|
+
form.addEventListener("formdata", state.handle, { capture: true });
|
|
318
|
+
}
|
|
319
|
+
var FormDataProjection = class {
|
|
320
|
+
#held = null;
|
|
321
|
+
hold(field, project) {
|
|
322
|
+
const held = this.#held;
|
|
323
|
+
if (held?.field === field && held.key === field.name && held.document === field.ownerDocument) {
|
|
324
|
+
if (field.form) prepareFormDataProjection(field.form);
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
this.release();
|
|
328
|
+
const name = projectedFieldName(field.name);
|
|
329
|
+
if (name === "" || field.type === "hidden" && name.toLowerCase() === "_charset_") return;
|
|
330
|
+
const state = forDocument(field.ownerDocument);
|
|
331
|
+
if (field.form) prepareFormDataProjection(field.form);
|
|
332
|
+
const previous = state.byField.get(field);
|
|
333
|
+
if (previous) {
|
|
334
|
+
state.fields.delete(previous.key);
|
|
335
|
+
field.removeEventListener("turbo:morph-element", previous.restore);
|
|
336
|
+
}
|
|
337
|
+
const nonce = Array.from(
|
|
338
|
+
crypto.getRandomValues(new Uint8Array(16)),
|
|
339
|
+
(value) => value.toString(16).padStart(2, "0")
|
|
340
|
+
).join("");
|
|
341
|
+
const key = `${PREFIX}${nonce}:${JSON.stringify(name)}`;
|
|
342
|
+
const projection = {
|
|
343
|
+
field,
|
|
344
|
+
key,
|
|
345
|
+
name,
|
|
346
|
+
project,
|
|
347
|
+
document: field.ownerDocument,
|
|
348
|
+
state,
|
|
349
|
+
restore: () => {
|
|
350
|
+
if (field.name === name) field.name = key;
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
state.fields.set(key, projection);
|
|
354
|
+
state.byField.set(field, projection);
|
|
355
|
+
this.#held = projection;
|
|
356
|
+
field.name = key;
|
|
357
|
+
field.addEventListener("turbo:morph-element", projection.restore);
|
|
358
|
+
}
|
|
359
|
+
release() {
|
|
360
|
+
const held = this.#held;
|
|
361
|
+
this.#held = null;
|
|
362
|
+
if (held === null) return;
|
|
363
|
+
held.field.removeEventListener("turbo:morph-element", held.restore);
|
|
364
|
+
const state = held.state;
|
|
365
|
+
if (state.byField.get(held.field) !== held) return;
|
|
366
|
+
state.fields.delete(held.key);
|
|
367
|
+
state.byField.delete(held.field);
|
|
368
|
+
if (held.field.name === held.key) held.field.name = held.name;
|
|
369
|
+
}
|
|
370
|
+
};
|
|
371
|
+
|
|
102
372
|
var FULL_WIDTH_SHIFT = 65248;
|
|
103
373
|
function halfWidthChar(char) {
|
|
104
374
|
if (char >= "\uFF01" && char <= "\uFF5E") {
|
|
@@ -134,6 +404,9 @@ var MicrotaskCoalescer = class {
|
|
|
134
404
|
this.#queued = false;
|
|
135
405
|
this.#generation += 1;
|
|
136
406
|
}
|
|
407
|
+
get pending() {
|
|
408
|
+
return this.#queued;
|
|
409
|
+
}
|
|
137
410
|
schedule() {
|
|
138
411
|
if (!this.#active || this.#queued) return;
|
|
139
412
|
this.#queued = true;
|
|
@@ -164,6 +437,9 @@ var MorphRenderWatcher = class {
|
|
|
164
437
|
schedule() {
|
|
165
438
|
this.#pass.schedule();
|
|
166
439
|
}
|
|
440
|
+
get pending() {
|
|
441
|
+
return this.#pass.pending;
|
|
442
|
+
}
|
|
167
443
|
disconnect() {
|
|
168
444
|
this.#listeners.dispose();
|
|
169
445
|
this.#pass.cancel();
|
|
@@ -171,12 +447,53 @@ var MorphRenderWatcher = class {
|
|
|
171
447
|
}
|
|
172
448
|
};
|
|
173
449
|
|
|
450
|
+
var TimerRegistry = class {
|
|
451
|
+
ids = /* @__PURE__ */ new Set();
|
|
452
|
+
clear(id) {
|
|
453
|
+
if (this.ids.delete(id)) {
|
|
454
|
+
this.cancel(id);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
clearAll() {
|
|
458
|
+
for (const id of this.ids) {
|
|
459
|
+
this.cancel(id);
|
|
460
|
+
}
|
|
461
|
+
this.ids.clear();
|
|
462
|
+
}
|
|
463
|
+
get size() {
|
|
464
|
+
return this.ids.size;
|
|
465
|
+
}
|
|
466
|
+
};
|
|
467
|
+
var SafeTimeout = class extends TimerRegistry {
|
|
468
|
+
set(callback, delay) {
|
|
469
|
+
const id = this.schedule(() => {
|
|
470
|
+
this.ids.delete(id);
|
|
471
|
+
callback();
|
|
472
|
+
}, delay);
|
|
473
|
+
this.ids.add(id);
|
|
474
|
+
return id;
|
|
475
|
+
}
|
|
476
|
+
schedule(callback, delay) {
|
|
477
|
+
return window.setTimeout(callback, delay);
|
|
478
|
+
}
|
|
479
|
+
cancel(id) {
|
|
480
|
+
window.clearTimeout(id);
|
|
481
|
+
}
|
|
482
|
+
};
|
|
483
|
+
|
|
484
|
+
function prepareInputMaskForm(form) {
|
|
485
|
+
prepareFormDataProjection(form);
|
|
486
|
+
}
|
|
174
487
|
var DEFAULT_TOKENS = {
|
|
175
488
|
"9": "\\d",
|
|
176
489
|
a: "[A-Za-z]",
|
|
177
490
|
"*": "[A-Za-z0-9]"
|
|
178
491
|
};
|
|
179
492
|
var UNMASK_ATTR = "unmask";
|
|
493
|
+
var rawWriters = sharedRegistry(
|
|
494
|
+
"stimeo-ui.input-mask-raw-writer.registry.v1",
|
|
495
|
+
() => /* @__PURE__ */ new WeakMap()
|
|
496
|
+
);
|
|
180
497
|
function acceptedForm(regex, char) {
|
|
181
498
|
if (regex.test(char)) return char;
|
|
182
499
|
const half = halfWidthChar(char);
|
|
@@ -265,8 +582,35 @@ var InputMaskController = class extends Controller {
|
|
|
265
582
|
static events = ["change", "reconcile"];
|
|
266
583
|
#lastValue = null;
|
|
267
584
|
#started = false;
|
|
585
|
+
#rawSink = null;
|
|
586
|
+
#rawValue = "";
|
|
587
|
+
#rawSourceForm = null;
|
|
588
|
+
#submission = new FormDataProjection();
|
|
268
589
|
#tokens = compileTokens("");
|
|
269
590
|
#textBeforeComposition = "";
|
|
591
|
+
#formatted = null;
|
|
592
|
+
#shown = { result: null, empty: true };
|
|
593
|
+
#edits = new ListenerSet();
|
|
594
|
+
#onBeforeInput = (event) => {
|
|
595
|
+
const data = event.data;
|
|
596
|
+
if (event.defaultPrevented || !event.cancelable || event.inputType !== "insertText" || data === null || Array.from(data).length !== 1 || !this.#started || this.#composition.isComposing(event))
|
|
597
|
+
return;
|
|
598
|
+
const input = this.element;
|
|
599
|
+
const start = input.selectionStart;
|
|
600
|
+
const end = input.selectionEnd;
|
|
601
|
+
if (start === null || end === null || start === end || input.readOnly || input.disabled || inheritsFieldsetDisabled(input) || !input.isConnected || !this.application.element.contains(input) || !(input.getAttribute("data-controller") ?? "").split(/\s+/).includes(this.identifier) || this.patternValue === "" || this.#formatted !== this.#declared || input.value !== this.#lastValue)
|
|
602
|
+
return;
|
|
603
|
+
const prefix = applyMask(input.value.slice(0, start), this.patternValue, this.#tokens);
|
|
604
|
+
for (const character of Array.from(this.patternValue).slice(prefix.tokenFlags.length)) {
|
|
605
|
+
const token = this.#tokens.get(character);
|
|
606
|
+
if (token) {
|
|
607
|
+
if (acceptedForm(token, data) === null) event.preventDefault();
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
if (halfWidthChar(data) === halfWidthChar(character)) return;
|
|
611
|
+
}
|
|
612
|
+
event.preventDefault();
|
|
613
|
+
};
|
|
270
614
|
#composition = new CompositionTracker({
|
|
271
615
|
onStart: () => {
|
|
272
616
|
this.#textBeforeComposition = this.element.value;
|
|
@@ -274,6 +618,9 @@ var InputMaskController = class extends Controller {
|
|
|
274
618
|
onEnd: () => {
|
|
275
619
|
const committedNothing = this.element.value === this.#textBeforeComposition;
|
|
276
620
|
this.#reformat(committedNothing ? "reconcile" : "edit");
|
|
621
|
+
},
|
|
622
|
+
onAbandon: () => {
|
|
623
|
+
if (this.#formatted !== this.#declared) this.#reformat("reconcile");
|
|
277
624
|
}
|
|
278
625
|
});
|
|
279
626
|
tokensValueChanged() {
|
|
@@ -285,34 +632,55 @@ var InputMaskController = class extends Controller {
|
|
|
285
632
|
}
|
|
286
633
|
unmaskToHiddenValueChanged() {
|
|
287
634
|
if (!this.#started) return;
|
|
288
|
-
if (!this.unmaskToHiddenValue)
|
|
289
|
-
const sink = this.#resolveSink();
|
|
290
|
-
if (sink) sink.value = "";
|
|
291
|
-
}
|
|
635
|
+
if (!this.unmaskToHiddenValue) this.#withdraw();
|
|
292
636
|
this.#reformat("reconcile");
|
|
293
637
|
}
|
|
638
|
+
#withdraw() {
|
|
639
|
+
if (this.#retractRaw() && this.#composition.isComposing()) this.#formatted = null;
|
|
640
|
+
}
|
|
294
641
|
#morph = new MorphRenderWatcher(() => this.#reformat("reconcile"));
|
|
642
|
+
#gate = new DetachGate();
|
|
643
|
+
#adoption = new SafeTimeout();
|
|
295
644
|
connect() {
|
|
645
|
+
this.#gate.cancel();
|
|
296
646
|
this.#morph.observe(this.element);
|
|
297
647
|
this.#started = true;
|
|
298
648
|
this.#composition.observe(this.element);
|
|
649
|
+
this.#edits.add(this.element, "beforeinput", this.#onBeforeInput);
|
|
650
|
+
if (!this.unmaskToHiddenValue) this.#withdraw();
|
|
651
|
+
if (this.#composition.isComposing()) {
|
|
652
|
+
this.#reformat("reconcile");
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
299
655
|
this.#lastValue = this.element.value;
|
|
300
656
|
this.#reformat("reconcile");
|
|
657
|
+
if (this.unmaskToHiddenValue && this.#rawSink === null) {
|
|
658
|
+
this.#adoption.set(() => this.#adoptSink(), 0);
|
|
659
|
+
}
|
|
301
660
|
}
|
|
302
661
|
disconnect() {
|
|
303
662
|
this.#morph.disconnect();
|
|
304
663
|
this.#started = false;
|
|
664
|
+
this.#edits.dispose();
|
|
305
665
|
this.#composition.disconnect();
|
|
666
|
+
this.#adoption.clearAll();
|
|
667
|
+
this.#gate.disconnected(this, () => this.#retractRaw());
|
|
306
668
|
}
|
|
307
669
|
format(event) {
|
|
308
670
|
if (this.#composition.isComposing(event)) return;
|
|
309
671
|
this.#reformat("edit", deletionOf(event));
|
|
310
672
|
}
|
|
311
673
|
#reformat(cause, deletion = null) {
|
|
312
|
-
if (this.#composition.isComposing())
|
|
674
|
+
if (this.#composition.isComposing()) {
|
|
675
|
+
this.#showAgain();
|
|
676
|
+
return;
|
|
677
|
+
}
|
|
678
|
+
this.#formatted = this.#declared;
|
|
313
679
|
const input = this.element;
|
|
314
680
|
const raw = input.value;
|
|
315
681
|
if (this.patternValue === "") {
|
|
682
|
+
this.#retractRaw();
|
|
683
|
+
this.#shown = { result: null, empty: raw.length === 0 };
|
|
316
684
|
this.#flag("data-mask-complete", false);
|
|
317
685
|
this.#flag("data-mask-empty", raw.length === 0);
|
|
318
686
|
this.#lastValue = raw;
|
|
@@ -349,14 +717,56 @@ var InputMaskController = class extends Controller {
|
|
|
349
717
|
if (cause === "edit") this.dispatch("change", { detail });
|
|
350
718
|
else this.dispatch("reconcile", { detail });
|
|
351
719
|
}
|
|
720
|
+
get #declared() {
|
|
721
|
+
return JSON.stringify([this.patternValue, this.tokensValue, this.unmaskToHiddenValue]);
|
|
722
|
+
}
|
|
352
723
|
#publish(result, shown) {
|
|
724
|
+
this.#shown = { result, empty: shown.length === 0 };
|
|
353
725
|
if (this.unmaskToHiddenValue) {
|
|
354
726
|
const sink = this.#resolveSink();
|
|
355
|
-
if (sink
|
|
727
|
+
if (sink !== this.#rawSink) this.#retractRaw();
|
|
728
|
+
if (sink) {
|
|
729
|
+
sink.value = result.unmasked;
|
|
730
|
+
rawWriters.set(sink, this);
|
|
731
|
+
this.#rawSink = sink;
|
|
732
|
+
this.#rawValue = result.unmasked;
|
|
733
|
+
this.#rawSourceForm = this.element.form;
|
|
734
|
+
this.#submission.hold(sink, (value) => this.#submittedValue(sink, value));
|
|
735
|
+
}
|
|
356
736
|
}
|
|
357
737
|
this.#flag("data-mask-complete", result.complete);
|
|
358
738
|
this.#flag("data-mask-empty", shown.length === 0);
|
|
359
739
|
}
|
|
740
|
+
#showAgain() {
|
|
741
|
+
const shown = this.#shown;
|
|
742
|
+
const sink = this.#rawSink;
|
|
743
|
+
if (shown.result !== null && this.unmaskToHiddenValue && sink !== null && rawWriters.get(sink) === this && this.#resolveSink() === sink) {
|
|
744
|
+
sink.value = shown.result.unmasked;
|
|
745
|
+
}
|
|
746
|
+
this.#flag("data-mask-complete", shown.result?.complete ?? false);
|
|
747
|
+
this.#flag("data-mask-empty", shown.empty);
|
|
748
|
+
}
|
|
749
|
+
#retractRaw() {
|
|
750
|
+
const sink = this.#rawSink;
|
|
751
|
+
this.#rawSink = null;
|
|
752
|
+
this.#submission.release();
|
|
753
|
+
if (sink === null || rawWriters.get(sink) !== this) return false;
|
|
754
|
+
rawWriters.delete(sink);
|
|
755
|
+
if (sink.value === this.#rawValue) sink.value = "";
|
|
756
|
+
return true;
|
|
757
|
+
}
|
|
758
|
+
#submittedValue(sink, value) {
|
|
759
|
+
if (rawWriters.get(sink) !== this || sink.value !== this.#rawValue || value !== this.#rawValue)
|
|
760
|
+
return value;
|
|
761
|
+
const source = this.element;
|
|
762
|
+
const listed = (source.getAttribute("data-controller") ?? "").split(/\s+/).includes(this.identifier);
|
|
763
|
+
const associated = source.form === null ? this.#rawSourceForm === null : source.form === sink.form;
|
|
764
|
+
const eligible = source.isConnected && this.application.element.contains(source) && listed && this.unmaskToHiddenValue && associated;
|
|
765
|
+
return eligible ? value : "";
|
|
766
|
+
}
|
|
767
|
+
#adoptSink() {
|
|
768
|
+
if (this.#resolveSink() !== null) this.#reformat("reconcile");
|
|
769
|
+
}
|
|
360
770
|
#significantBefore(prefix) {
|
|
361
771
|
return applyMask(prefix, this.patternValue, this.#tokens).unmasked.length;
|
|
362
772
|
}
|
|
@@ -413,5 +823,5 @@ var InputMaskController = class extends Controller {
|
|
|
413
823
|
}
|
|
414
824
|
};
|
|
415
825
|
|
|
416
|
-
export { InputMaskController, applyMask };
|
|
826
|
+
export { InputMaskController, applyMask, prepareInputMaskForm };
|
|
417
827
|
//# sourceMappingURL=input_mask_controller.js.map
|
|
@@ -81,6 +81,17 @@ var IntersectionWatcher = class {
|
|
|
81
81
|
}
|
|
82
82
|
};
|
|
83
83
|
|
|
84
|
+
var ListenerSet = class {
|
|
85
|
+
#abort = new AbortController();
|
|
86
|
+
add(target, type, handler, options) {
|
|
87
|
+
target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });
|
|
88
|
+
}
|
|
89
|
+
dispose() {
|
|
90
|
+
this.#abort.abort();
|
|
91
|
+
this.#abort = new AbortController();
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
|
|
84
95
|
var LazyFrameController = class extends Controller {
|
|
85
96
|
static values = {
|
|
86
97
|
url: { type: String, default: "" },
|
|
@@ -93,6 +104,7 @@ var LazyFrameController = class extends Controller {
|
|
|
93
104
|
#loaded = false;
|
|
94
105
|
#inside = null;
|
|
95
106
|
#onFocus = () => this.#load();
|
|
107
|
+
#focusListeners = new ListenerSet();
|
|
96
108
|
urlValueChanged() {
|
|
97
109
|
if (!this.#connected || this.#loaded) return;
|
|
98
110
|
if (this.urlValue) this.#arm();
|
|
@@ -123,7 +135,7 @@ var LazyFrameController = class extends Controller {
|
|
|
123
135
|
}
|
|
124
136
|
#arm() {
|
|
125
137
|
if (!this.urlValue) return;
|
|
126
|
-
if (!this.#loaded) this.element
|
|
138
|
+
if (!this.#loaded) this.#focusListeners.add(this.element, "focusin", this.#onFocus);
|
|
127
139
|
this.#observe();
|
|
128
140
|
}
|
|
129
141
|
#observe() {
|
|
@@ -154,7 +166,7 @@ var LazyFrameController = class extends Controller {
|
|
|
154
166
|
this.element.setAttribute("src", this.urlValue);
|
|
155
167
|
this.element.setAttribute("data-lazy-loaded", "true");
|
|
156
168
|
this.dispatch("load", { detail: { url: this.urlValue } });
|
|
157
|
-
this.
|
|
169
|
+
this.#focusListeners.dispose();
|
|
158
170
|
if (this.onceValue) this.#watcher.stop();
|
|
159
171
|
}
|
|
160
172
|
#reload() {
|
|
@@ -171,7 +183,7 @@ var LazyFrameController = class extends Controller {
|
|
|
171
183
|
}
|
|
172
184
|
#stopObserving() {
|
|
173
185
|
this.#watcher.stop();
|
|
174
|
-
this.
|
|
186
|
+
this.#focusListeners.dispose();
|
|
175
187
|
}
|
|
176
188
|
};
|
|
177
189
|
|