@ramstack/alpinegear-main 1.4.2 → 1.4.4
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/README.md +163 -187
- package/alpinegear-main.esm.js +623 -615
- package/alpinegear-main.esm.min.js +1 -1
- package/alpinegear-main.js +623 -624
- package/alpinegear-main.min.js +1 -1
- package/package.json +9 -3
package/alpinegear-main.js
CHANGED
|
@@ -1,6 +1,45 @@
|
|
|
1
1
|
(function () {
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
+
const warn = (...args) => console.warn("alpinegear.js:", ...args);
|
|
5
|
+
const is_array = Array.isArray;
|
|
6
|
+
const is_nullish = value => value === null || value === undefined;
|
|
7
|
+
const is_checkable_input = el => el.type === "checkbox" || el.type === "radio";
|
|
8
|
+
const is_numeric_input = el => el.type === "number" || el.type === "range";
|
|
9
|
+
const is_template = el => el.matches("template");
|
|
10
|
+
const is_element = el => el.nodeType === Node.ELEMENT_NODE;
|
|
11
|
+
const as_array = value => is_array(value) ? value : [value];
|
|
12
|
+
const loose_equal = (a, b) => a == b;
|
|
13
|
+
const loose_index_of = (array, value) => array.findIndex(v => v == value);
|
|
14
|
+
const has_modifier = (modifiers, modifier) => modifiers.includes(modifier);
|
|
15
|
+
|
|
16
|
+
function assert(value, message) {
|
|
17
|
+
if (!value) {
|
|
18
|
+
throw new Error(message);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const listen = (target, type, listener, options) => {
|
|
23
|
+
target.addEventListener(type, listener, options);
|
|
24
|
+
return () => target.removeEventListener(type, listener, options);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const clone = value =>
|
|
28
|
+
typeof value === "object"
|
|
29
|
+
? JSON.parse(JSON.stringify(value))
|
|
30
|
+
: value;
|
|
31
|
+
|
|
32
|
+
const closest = (el, callback) => {
|
|
33
|
+
while (el && !callback(el)) {
|
|
34
|
+
el = (el._x_teleportBack ?? el).parentElement;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return el;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const create_map = keys => new Map(
|
|
41
|
+
keys.split(",").map(v => [v.trim().toLowerCase(), v.trim()]));
|
|
42
|
+
|
|
4
43
|
function create_getter(evaluate_later, ...args) {
|
|
5
44
|
const evaluate = evaluate_later(...args);
|
|
6
45
|
return () => {
|
|
@@ -40,71 +79,32 @@
|
|
|
40
79
|
return typeof value?.set === "function";
|
|
41
80
|
}
|
|
42
81
|
|
|
43
|
-
const key = Symbol();
|
|
44
|
-
let observer;
|
|
45
|
-
|
|
46
|
-
function observe_resize(el, listener) {
|
|
47
|
-
observer ??= new ResizeObserver(entries => {
|
|
48
|
-
for (const e of entries) {
|
|
49
|
-
for (const callback of e.target[key]?.values() ?? []) {
|
|
50
|
-
callback(e);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
el[key] ??= new Set();
|
|
56
|
-
el[key].add(listener);
|
|
57
|
-
|
|
58
|
-
observer.observe(el);
|
|
59
|
-
|
|
60
|
-
return () => {
|
|
61
|
-
el[key].delete(listener);
|
|
62
|
-
|
|
63
|
-
if (!el[key].size) {
|
|
64
|
-
observer.unobserve(el);
|
|
65
|
-
el[key] = null;
|
|
66
|
-
}
|
|
67
|
-
};
|
|
68
|
-
}
|
|
82
|
+
const key = Symbol();
|
|
83
|
+
let observer;
|
|
69
84
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const clone = value =>
|
|
94
|
-
typeof value === "object"
|
|
95
|
-
? JSON.parse(JSON.stringify(value))
|
|
96
|
-
: value;
|
|
97
|
-
|
|
98
|
-
const closest = (el, callback) => {
|
|
99
|
-
while (el && !callback(el)) {
|
|
100
|
-
el = (el._x_teleportBack ?? el).parentElement;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
return el;
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
const create_map = keys => new Map(
|
|
107
|
-
keys.split(",").map(v => [v.trim().toLowerCase(), v.trim()]));
|
|
85
|
+
function observe_resize(el, listener) {
|
|
86
|
+
observer ??= new ResizeObserver(entries => {
|
|
87
|
+
for (const e of entries) {
|
|
88
|
+
for (const callback of e.target[key]?.values() ?? []) {
|
|
89
|
+
callback(e);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
el[key] ??= new Set();
|
|
95
|
+
el[key].add(listener);
|
|
96
|
+
|
|
97
|
+
observer.observe(el);
|
|
98
|
+
|
|
99
|
+
return () => {
|
|
100
|
+
el[key].delete(listener);
|
|
101
|
+
|
|
102
|
+
if (!el[key].size) {
|
|
103
|
+
observer.unobserve(el);
|
|
104
|
+
el[key] = null;
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
108
|
|
|
109
109
|
function watch(get_value, callback, options = null) {
|
|
110
110
|
assert(Alpine, "Alpine is not defined");
|
|
@@ -131,7 +131,7 @@
|
|
|
131
131
|
setTimeout(() => {
|
|
132
132
|
callback(new_value, old_value);
|
|
133
133
|
old_value = new_value;
|
|
134
|
-
}
|
|
134
|
+
});
|
|
135
135
|
}
|
|
136
136
|
|
|
137
137
|
initialized = true;
|
|
@@ -140,580 +140,579 @@
|
|
|
140
140
|
return () => release(handle);
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
-
const canonical_names = create_map(
|
|
144
|
-
"value,checked,files," +
|
|
145
|
-
"innerHTML,innerText,textContent," +
|
|
146
|
-
"videoHeight,videoWidth," +
|
|
147
|
-
"naturalHeight,naturalWidth," +
|
|
148
|
-
"clientHeight,clientWidth,offsetHeight,offsetWidth," +
|
|
149
|
-
"indeterminate," +
|
|
150
|
-
"open," +
|
|
151
|
-
"group");
|
|
152
|
-
|
|
153
|
-
function plugin$5({ directive, entangle, evaluateLater, mapAttributes, mutateDom, prefixed }) {
|
|
154
|
-
// creating a shortcut for the directive,
|
|
155
|
-
// when an attribute name starting with & will refer to our directive,
|
|
156
|
-
// allowing us to write like this: &value="prop",
|
|
157
|
-
// which is equivalent to x-bound:value="prop"
|
|
158
|
-
mapAttributes(attr => ({
|
|
159
|
-
name: attr.name.replace(/^&/, prefixed("bound:")),
|
|
160
|
-
value: attr.value
|
|
161
|
-
}));
|
|
162
|
-
|
|
163
|
-
directive("bound", (el, { expression, value, modifiers }, { effect, cleanup }) => {
|
|
164
|
-
if (!value) {
|
|
165
|
-
warn("x-bound directive expects the presence of a bound property name");
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
const tag_name = el.tagName.toUpperCase();
|
|
170
|
-
|
|
171
|
-
expression = expression?.trim();
|
|
172
|
-
|
|
173
|
-
// since attributes come in a lowercase,
|
|
174
|
-
// we need to convert the bound property name to its canonical form
|
|
175
|
-
const property_name = canonical_names.get(value.trim().replace("-", "").toLowerCase());
|
|
176
|
-
|
|
177
|
-
// if the expression is omitted, then we assume it corresponds
|
|
178
|
-
// to the bound property name, allowing us to write expressions more concisely,
|
|
179
|
-
// and write &value instead of &value="value"
|
|
180
|
-
expression ||= property_name;
|
|
181
|
-
|
|
182
|
-
const get_value = create_getter(evaluateLater, el, expression);
|
|
183
|
-
const set_value = create_setter(evaluateLater, el, expression);
|
|
184
|
-
|
|
185
|
-
const update_property = () => loose_equal(el[property_name], get_value()) || mutateDom(() => el[property_name] = get_value());
|
|
186
|
-
const update_variable = () => set_value(is_numeric_input(el) ? to_number(el[property_name]) : el[property_name]);
|
|
187
|
-
|
|
188
|
-
let processed;
|
|
189
|
-
|
|
190
|
-
switch (property_name) {
|
|
191
|
-
case "value":
|
|
192
|
-
process_value();
|
|
193
|
-
break;
|
|
194
|
-
|
|
195
|
-
case "checked":
|
|
196
|
-
process_checked();
|
|
197
|
-
break;
|
|
198
|
-
|
|
199
|
-
case "files":
|
|
200
|
-
process_files();
|
|
201
|
-
break;
|
|
202
|
-
|
|
203
|
-
case "innerHTML":
|
|
204
|
-
case "innerText":
|
|
205
|
-
case "textContent":
|
|
206
|
-
process_contenteditable();
|
|
207
|
-
break;
|
|
208
|
-
|
|
209
|
-
case "videoHeight":
|
|
210
|
-
case "videoWidth":
|
|
211
|
-
process_media_resize("VIDEO", "resize");
|
|
212
|
-
break;
|
|
213
|
-
|
|
214
|
-
case "naturalHeight":
|
|
215
|
-
case "naturalWidth":
|
|
216
|
-
process_media_resize("IMG", "load");
|
|
217
|
-
break;
|
|
218
|
-
|
|
219
|
-
case "clientHeight":
|
|
220
|
-
case "clientWidth":
|
|
221
|
-
case "offsetHeight":
|
|
222
|
-
case "offsetWidth":
|
|
223
|
-
process_dimensions();
|
|
224
|
-
break;
|
|
225
|
-
|
|
226
|
-
case "indeterminate":
|
|
227
|
-
process_indeterminate();
|
|
228
|
-
break;
|
|
229
|
-
|
|
230
|
-
case "open":
|
|
231
|
-
process_open_attribute();
|
|
232
|
-
break;
|
|
233
|
-
|
|
234
|
-
case "group":
|
|
235
|
-
process_group();
|
|
236
|
-
break;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
if (!processed) {
|
|
240
|
-
const modifier =
|
|
241
|
-
has_modifier(modifiers, "in") ? "in" :
|
|
242
|
-
has_modifier(modifiers, "out") ? "out" : "inout";
|
|
243
|
-
|
|
244
|
-
const source_el = expression === value
|
|
245
|
-
? closest(el.parentNode, node => node._x_dataStack)
|
|
246
|
-
: el;
|
|
247
|
-
|
|
248
|
-
if (!el._x_dataStack) {
|
|
249
|
-
warn("x-bound directive requires the presence of the x-data directive to bind component properties");
|
|
250
|
-
return;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
if (!source_el) {
|
|
254
|
-
warn(`x-bound directive cannot find the parent scope where the '${ value }' property is defined`);
|
|
255
|
-
return;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
const source = {
|
|
259
|
-
get: create_getter(evaluateLater, source_el, expression),
|
|
260
|
-
set: create_setter(evaluateLater, source_el, expression)
|
|
261
|
-
};
|
|
262
|
-
|
|
263
|
-
const target = {
|
|
264
|
-
get: create_getter(evaluateLater, el, value),
|
|
265
|
-
set: create_setter(evaluateLater, el, value)
|
|
266
|
-
};
|
|
267
|
-
|
|
268
|
-
switch (modifier) {
|
|
269
|
-
case "in":
|
|
270
|
-
cleanup(watch(() => source.get(), v => target.set(clone(v))));
|
|
271
|
-
break;
|
|
272
|
-
case "out":
|
|
273
|
-
cleanup(watch(() => target.get(), v => source.set(clone(v))));
|
|
274
|
-
break;
|
|
275
|
-
default:
|
|
276
|
-
cleanup(entangle(source, target));
|
|
277
|
-
break;
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
function process_value() {
|
|
282
|
-
switch (tag_name) {
|
|
283
|
-
case "INPUT":
|
|
284
|
-
case "TEXTAREA":
|
|
285
|
-
// if the value of the bound property is "null" or "undefined",
|
|
286
|
-
// we initialize it with the value from the element.
|
|
287
|
-
is_nullish(get_value()) && update_variable();
|
|
288
|
-
|
|
289
|
-
effect(update_property);
|
|
290
|
-
cleanup(listen(el, "input", update_variable));
|
|
291
|
-
|
|
292
|
-
processed = true;
|
|
293
|
-
break;
|
|
294
|
-
|
|
295
|
-
case "SELECT":
|
|
296
|
-
// WORKAROUND:
|
|
297
|
-
// For the "select" element, there might be a situation
|
|
298
|
-
// where options are generated dynamically using the "x-for" directive,
|
|
299
|
-
// and in this case, attempting to set the "value" property
|
|
300
|
-
// will have no effect since there are no options yet.
|
|
301
|
-
// Therefore, we use a small trick to set the value a bit later
|
|
302
|
-
// when the "x-for" directive has finished its work.
|
|
303
|
-
setTimeout(() => {
|
|
304
|
-
// if the value of the bound property is "null" or "undefined",
|
|
305
|
-
// we initialize it with the value from the element.
|
|
306
|
-
is_nullish(get_value()) && update_variable();
|
|
307
|
-
|
|
308
|
-
effect(() => apply_select_values(el, as_array(get_value() ?? [])));
|
|
309
|
-
cleanup(listen(el, "change", () => set_value(collect_selected_values(el))));
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
processed = true;
|
|
313
|
-
break;
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
function process_checked() {
|
|
318
|
-
if (is_checkable_input(el)) {
|
|
319
|
-
effect(update_property);
|
|
320
|
-
cleanup(listen(el, "change", update_variable));
|
|
321
|
-
processed = true;
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
function process_indeterminate() {
|
|
326
|
-
if (el.type === "checkbox") {
|
|
327
|
-
is_nullish(get_value()) && update_variable();
|
|
328
|
-
effect(update_property);
|
|
329
|
-
cleanup(listen(el, "change", update_variable));
|
|
330
|
-
processed = true;
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
function process_files() {
|
|
335
|
-
if (el.type === "file") {
|
|
336
|
-
get_value() instanceof FileList || update_variable();
|
|
337
|
-
|
|
338
|
-
effect(update_property);
|
|
339
|
-
cleanup(listen(el, "input", update_variable));
|
|
340
|
-
processed = true;
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
function process_contenteditable() {
|
|
345
|
-
if (el.isContentEditable) {
|
|
346
|
-
is_nullish(get_value()) && update_variable();
|
|
347
|
-
|
|
348
|
-
effect(update_property);
|
|
349
|
-
cleanup(listen(el, "input", update_variable));
|
|
350
|
-
processed = true;
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
function process_media_resize(name, event_name) {
|
|
355
|
-
if (tag_name === name) {
|
|
356
|
-
update_variable();
|
|
357
|
-
cleanup(listen(el, event_name, update_variable));
|
|
358
|
-
processed = true;
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
function process_dimensions() {
|
|
363
|
-
cleanup(observe_resize(el, update_variable));
|
|
364
|
-
processed = true;
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
function process_open_attribute() {
|
|
368
|
-
const [is_details, is_dialog] = [tag_name === "DETAILS", tag_name === "DIALOG"];
|
|
369
|
-
|
|
370
|
-
if (is_details || is_dialog) {
|
|
371
|
-
//
|
|
372
|
-
// <details>:
|
|
373
|
-
// Supports safe two-way binding via the "open" attribute,
|
|
374
|
-
// so we initialize from the element only if the bound value
|
|
375
|
-
// is null or undefined.
|
|
376
|
-
//
|
|
377
|
-
// <dialog>:
|
|
378
|
-
// Directly setting element.open is discouraged by the spec,
|
|
379
|
-
// as it breaks native dialog behavior and the "close" event.
|
|
380
|
-
// Therefore, we always initialize state from the element
|
|
381
|
-
// and treat it as a one-way source of truth.
|
|
382
|
-
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement/open#value
|
|
383
|
-
//
|
|
384
|
-
(is_dialog || is_nullish(get_value())) && update_variable();
|
|
385
|
-
|
|
386
|
-
//
|
|
387
|
-
// Enable two-way binding only for "<details>"
|
|
388
|
-
//
|
|
389
|
-
is_details && effect(update_property);
|
|
390
|
-
cleanup(listen(el, "toggle", update_variable));
|
|
391
|
-
processed = true;
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
function process_group() {
|
|
396
|
-
if (is_checkable_input(el)) {
|
|
397
|
-
el.name || mutateDom(() => el.name = expression);
|
|
398
|
-
|
|
399
|
-
effect(() =>
|
|
400
|
-
mutateDom(() =>
|
|
401
|
-
apply_group_values(el, get_value() ?? [])));
|
|
402
|
-
|
|
403
|
-
cleanup(listen(el, "input", () => set_value(collect_group_values(el, get_value()))));
|
|
404
|
-
processed = true;
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
});
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
function to_number(value) {
|
|
411
|
-
return value === "" ? null : +value;
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
function apply_select_values(el, values) {
|
|
415
|
-
for (const option of el.options) {
|
|
416
|
-
option.selected = loose_index_of(values, option.value) >= 0;
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
function collect_selected_values(el) {
|
|
421
|
-
if (el.multiple) {
|
|
422
|
-
return [...el.selectedOptions].map(o => o.value);
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
return el.value;
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
function apply_group_values(el, values) {
|
|
429
|
-
el.checked = is_array(values)
|
|
430
|
-
? loose_index_of(values, el.value) >= 0
|
|
431
|
-
: loose_equal(el.value, values);
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
function collect_group_values(el, values) {
|
|
435
|
-
if (el.type === "radio") {
|
|
436
|
-
return el.value;
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
values = as_array(values);
|
|
440
|
-
const index = loose_index_of(values, el.value);
|
|
441
|
-
|
|
442
|
-
if (el.checked) {
|
|
443
|
-
index >= 0 || values.push(el.value);
|
|
444
|
-
}
|
|
445
|
-
else {
|
|
446
|
-
index >= 0 && values.splice(index, 1);
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
return values;
|
|
143
|
+
const canonical_names = create_map(
|
|
144
|
+
"value,checked,files," +
|
|
145
|
+
"innerHTML,innerText,textContent," +
|
|
146
|
+
"videoHeight,videoWidth," +
|
|
147
|
+
"naturalHeight,naturalWidth," +
|
|
148
|
+
"clientHeight,clientWidth,offsetHeight,offsetWidth," +
|
|
149
|
+
"indeterminate," +
|
|
150
|
+
"open," +
|
|
151
|
+
"group");
|
|
152
|
+
|
|
153
|
+
function plugin$5({ directive, entangle, evaluateLater, mapAttributes, mutateDom, prefixed }) {
|
|
154
|
+
// creating a shortcut for the directive,
|
|
155
|
+
// when an attribute name starting with & will refer to our directive,
|
|
156
|
+
// allowing us to write like this: &value="prop",
|
|
157
|
+
// which is equivalent to x-bound:value="prop"
|
|
158
|
+
mapAttributes(attr => ({
|
|
159
|
+
name: attr.name.replace(/^&/, prefixed("bound:")),
|
|
160
|
+
value: attr.value
|
|
161
|
+
}));
|
|
162
|
+
|
|
163
|
+
directive("bound", (el, { expression, value, modifiers }, { effect, cleanup }) => {
|
|
164
|
+
if (!value) {
|
|
165
|
+
warn("x-bound directive expects the presence of a bound property name");
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const tag_name = el.tagName.toUpperCase();
|
|
170
|
+
|
|
171
|
+
expression = expression?.trim();
|
|
172
|
+
|
|
173
|
+
// since attributes come in a lowercase,
|
|
174
|
+
// we need to convert the bound property name to its canonical form
|
|
175
|
+
const property_name = canonical_names.get(value.trim().replace("-", "").toLowerCase());
|
|
176
|
+
|
|
177
|
+
// if the expression is omitted, then we assume it corresponds
|
|
178
|
+
// to the bound property name, allowing us to write expressions more concisely,
|
|
179
|
+
// and write &value instead of &value="value"
|
|
180
|
+
expression ||= property_name;
|
|
181
|
+
|
|
182
|
+
const get_value = create_getter(evaluateLater, el, expression);
|
|
183
|
+
const set_value = create_setter(evaluateLater, el, expression);
|
|
184
|
+
|
|
185
|
+
const update_property = () => loose_equal(el[property_name], get_value()) || mutateDom(() => el[property_name] = get_value());
|
|
186
|
+
const update_variable = () => set_value(is_numeric_input(el) ? to_number(el[property_name]) : el[property_name]);
|
|
187
|
+
|
|
188
|
+
let processed;
|
|
189
|
+
|
|
190
|
+
switch (property_name) {
|
|
191
|
+
case "value":
|
|
192
|
+
process_value();
|
|
193
|
+
break;
|
|
194
|
+
|
|
195
|
+
case "checked":
|
|
196
|
+
process_checked();
|
|
197
|
+
break;
|
|
198
|
+
|
|
199
|
+
case "files":
|
|
200
|
+
process_files();
|
|
201
|
+
break;
|
|
202
|
+
|
|
203
|
+
case "innerHTML":
|
|
204
|
+
case "innerText":
|
|
205
|
+
case "textContent":
|
|
206
|
+
process_contenteditable();
|
|
207
|
+
break;
|
|
208
|
+
|
|
209
|
+
case "videoHeight":
|
|
210
|
+
case "videoWidth":
|
|
211
|
+
process_media_resize("VIDEO", "resize");
|
|
212
|
+
break;
|
|
213
|
+
|
|
214
|
+
case "naturalHeight":
|
|
215
|
+
case "naturalWidth":
|
|
216
|
+
process_media_resize("IMG", "load");
|
|
217
|
+
break;
|
|
218
|
+
|
|
219
|
+
case "clientHeight":
|
|
220
|
+
case "clientWidth":
|
|
221
|
+
case "offsetHeight":
|
|
222
|
+
case "offsetWidth":
|
|
223
|
+
process_dimensions();
|
|
224
|
+
break;
|
|
225
|
+
|
|
226
|
+
case "indeterminate":
|
|
227
|
+
process_indeterminate();
|
|
228
|
+
break;
|
|
229
|
+
|
|
230
|
+
case "open":
|
|
231
|
+
process_open_attribute();
|
|
232
|
+
break;
|
|
233
|
+
|
|
234
|
+
case "group":
|
|
235
|
+
process_group();
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (!processed) {
|
|
240
|
+
const modifier =
|
|
241
|
+
has_modifier(modifiers, "in") ? "in" :
|
|
242
|
+
has_modifier(modifiers, "out") ? "out" : "inout";
|
|
243
|
+
|
|
244
|
+
const source_el = expression === value
|
|
245
|
+
? closest(el.parentNode, node => node._x_dataStack)
|
|
246
|
+
: el;
|
|
247
|
+
|
|
248
|
+
if (!el._x_dataStack) {
|
|
249
|
+
warn("x-bound directive requires the presence of the x-data directive to bind component properties");
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (!source_el) {
|
|
254
|
+
warn(`x-bound directive cannot find the parent scope where the '${ value }' property is defined`);
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const source = {
|
|
259
|
+
get: create_getter(evaluateLater, source_el, expression),
|
|
260
|
+
set: create_setter(evaluateLater, source_el, expression)
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
const target = {
|
|
264
|
+
get: create_getter(evaluateLater, el, value),
|
|
265
|
+
set: create_setter(evaluateLater, el, value)
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
switch (modifier) {
|
|
269
|
+
case "in":
|
|
270
|
+
cleanup(watch(() => source.get(), v => target.set(clone(v))));
|
|
271
|
+
break;
|
|
272
|
+
case "out":
|
|
273
|
+
cleanup(watch(() => target.get(), v => source.set(clone(v))));
|
|
274
|
+
break;
|
|
275
|
+
default:
|
|
276
|
+
cleanup(entangle(source, target));
|
|
277
|
+
break;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function process_value() {
|
|
282
|
+
switch (tag_name) {
|
|
283
|
+
case "INPUT":
|
|
284
|
+
case "TEXTAREA":
|
|
285
|
+
// if the value of the bound property is "null" or "undefined",
|
|
286
|
+
// we initialize it with the value from the element.
|
|
287
|
+
is_nullish(get_value()) && update_variable();
|
|
288
|
+
|
|
289
|
+
effect(update_property);
|
|
290
|
+
cleanup(listen(el, "input", update_variable));
|
|
291
|
+
|
|
292
|
+
processed = true;
|
|
293
|
+
break;
|
|
294
|
+
|
|
295
|
+
case "SELECT":
|
|
296
|
+
// WORKAROUND:
|
|
297
|
+
// For the "select" element, there might be a situation
|
|
298
|
+
// where options are generated dynamically using the "x-for" directive,
|
|
299
|
+
// and in this case, attempting to set the "value" property
|
|
300
|
+
// will have no effect since there are no options yet.
|
|
301
|
+
// Therefore, we use a small trick to set the value a bit later
|
|
302
|
+
// when the "x-for" directive has finished its work.
|
|
303
|
+
setTimeout(() => {
|
|
304
|
+
// if the value of the bound property is "null" or "undefined",
|
|
305
|
+
// we initialize it with the value from the element.
|
|
306
|
+
is_nullish(get_value()) && update_variable();
|
|
307
|
+
|
|
308
|
+
effect(() => apply_select_values(el, as_array(get_value() ?? [])));
|
|
309
|
+
cleanup(listen(el, "change", () => set_value(collect_selected_values(el))));
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
processed = true;
|
|
313
|
+
break;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function process_checked() {
|
|
318
|
+
if (is_checkable_input(el)) {
|
|
319
|
+
effect(update_property);
|
|
320
|
+
cleanup(listen(el, "change", update_variable));
|
|
321
|
+
processed = true;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function process_indeterminate() {
|
|
326
|
+
if (el.type === "checkbox") {
|
|
327
|
+
is_nullish(get_value()) && update_variable();
|
|
328
|
+
effect(update_property);
|
|
329
|
+
cleanup(listen(el, "change", update_variable));
|
|
330
|
+
processed = true;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
function process_files() {
|
|
335
|
+
if (el.type === "file") {
|
|
336
|
+
get_value() instanceof FileList || update_variable();
|
|
337
|
+
|
|
338
|
+
effect(update_property);
|
|
339
|
+
cleanup(listen(el, "input", update_variable));
|
|
340
|
+
processed = true;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function process_contenteditable() {
|
|
345
|
+
if (el.isContentEditable) {
|
|
346
|
+
is_nullish(get_value()) && update_variable();
|
|
347
|
+
|
|
348
|
+
effect(update_property);
|
|
349
|
+
cleanup(listen(el, "input", update_variable));
|
|
350
|
+
processed = true;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function process_media_resize(name, event_name) {
|
|
355
|
+
if (tag_name === name) {
|
|
356
|
+
update_variable();
|
|
357
|
+
cleanup(listen(el, event_name, update_variable));
|
|
358
|
+
processed = true;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function process_dimensions() {
|
|
363
|
+
cleanup(observe_resize(el, update_variable));
|
|
364
|
+
processed = true;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function process_open_attribute() {
|
|
368
|
+
const [is_details, is_dialog] = [tag_name === "DETAILS", tag_name === "DIALOG"];
|
|
369
|
+
|
|
370
|
+
if (is_details || is_dialog) {
|
|
371
|
+
//
|
|
372
|
+
// <details>:
|
|
373
|
+
// Supports safe two-way binding via the "open" attribute,
|
|
374
|
+
// so we initialize from the element only if the bound value
|
|
375
|
+
// is null or undefined.
|
|
376
|
+
//
|
|
377
|
+
// <dialog>:
|
|
378
|
+
// Directly setting element.open is discouraged by the spec,
|
|
379
|
+
// as it breaks native dialog behavior and the "close" event.
|
|
380
|
+
// Therefore, we always initialize state from the element
|
|
381
|
+
// and treat it as a one-way source of truth.
|
|
382
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement/open#value
|
|
383
|
+
//
|
|
384
|
+
(is_dialog || is_nullish(get_value())) && update_variable();
|
|
385
|
+
|
|
386
|
+
//
|
|
387
|
+
// Enable two-way binding only for "<details>"
|
|
388
|
+
//
|
|
389
|
+
is_details && effect(update_property);
|
|
390
|
+
cleanup(listen(el, "toggle", update_variable));
|
|
391
|
+
processed = true;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function process_group() {
|
|
396
|
+
if (is_checkable_input(el)) {
|
|
397
|
+
el.name || mutateDom(() => el.name = expression);
|
|
398
|
+
|
|
399
|
+
effect(() =>
|
|
400
|
+
mutateDom(() =>
|
|
401
|
+
apply_group_values(el, get_value() ?? [])));
|
|
402
|
+
|
|
403
|
+
cleanup(listen(el, "input", () => set_value(collect_group_values(el, get_value()))));
|
|
404
|
+
processed = true;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
});
|
|
450
408
|
}
|
|
451
409
|
|
|
452
|
-
function
|
|
453
|
-
|
|
454
|
-
const placeholder_regex = /{{(?<expr>.+?)}}/g;
|
|
455
|
-
const is_once = has_modifier(modifiers, "once");
|
|
456
|
-
const has_format_attr = el => el.hasAttribute("x-format");
|
|
457
|
-
|
|
458
|
-
process(el);
|
|
459
|
-
|
|
460
|
-
function update(callback) {
|
|
461
|
-
if (is_once) {
|
|
462
|
-
mutateDom(() => callback());
|
|
463
|
-
}
|
|
464
|
-
else {
|
|
465
|
-
effect(() => mutateDom(() => callback()));
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
function process(node) {
|
|
470
|
-
switch (node.nodeType) {
|
|
471
|
-
case Node.TEXT_NODE:
|
|
472
|
-
process_text_node(node);
|
|
473
|
-
break;
|
|
474
|
-
|
|
475
|
-
case Node.ELEMENT_NODE:
|
|
476
|
-
if (node !== el) {
|
|
477
|
-
//
|
|
478
|
-
// When we encounter an element with the "x-data" attribute, its properties
|
|
479
|
-
// are not yet initialized, and the Alpine context is unavailable.
|
|
480
|
-
// Attempting to use these properties will result in
|
|
481
|
-
// an "Alpine Expression Error: [expression] is not defined".
|
|
482
|
-
//
|
|
483
|
-
// Workaround:
|
|
484
|
-
// To avoid this, we manually add our "x-format" directive to the element.
|
|
485
|
-
// Alpine evaluates "x-format" directive once the context is initialized.
|
|
486
|
-
// In the current loop, we skip these elements to defer their processing.
|
|
487
|
-
//
|
|
488
|
-
// This also handles cases where the user manually adds the "x-format" attribute.
|
|
489
|
-
//
|
|
490
|
-
if (node.hasAttribute("x-data") && !has_format_attr(node)) {
|
|
491
|
-
node.setAttribute("x-format", "");
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
if (has_format_attr(node)) {
|
|
495
|
-
break;
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
process_nodes(node);
|
|
501
|
-
process_attributes(node);
|
|
502
|
-
break;
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
function process_text_node(node) {
|
|
507
|
-
const tokens = node.textContent.split(placeholder_regex);
|
|
508
|
-
|
|
509
|
-
if (tokens.length > 1) {
|
|
510
|
-
const fragment = new DocumentFragment();
|
|
511
|
-
|
|
512
|
-
for (let i = 0; i < tokens.length; i++) {
|
|
513
|
-
if ((i % 2) === 0) {
|
|
514
|
-
fragment.appendChild(document.createTextNode(tokens[i]));
|
|
515
|
-
}
|
|
516
|
-
else {
|
|
517
|
-
const get_value = create_getter(evaluateLater, node.parentNode, tokens[i]);
|
|
518
|
-
const text = document.createTextNode("");
|
|
519
|
-
|
|
520
|
-
fragment.append(text);
|
|
521
|
-
update(() => text.textContent = get_value());
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
mutateDom(() =>
|
|
526
|
-
node.parentElement.replaceChild(fragment, node));
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
function process_attributes(node) {
|
|
531
|
-
for (let attr of node.attributes) {
|
|
532
|
-
const matches = [...attr.value.matchAll(placeholder_regex)];
|
|
533
|
-
if (matches.length) {
|
|
534
|
-
const template = attr.value;
|
|
535
|
-
update(() => attr.value = template.replace(placeholder_regex, (_, expr) => create_getter(evaluateLater, node, expr)()));
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
function process_nodes(node) {
|
|
541
|
-
for (let child of node.childNodes) {
|
|
542
|
-
process(child);
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
});
|
|
410
|
+
function to_number(value) {
|
|
411
|
+
return value === "" ? null : +value;
|
|
546
412
|
}
|
|
547
413
|
|
|
548
|
-
function
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
initialize();
|
|
554
|
-
|
|
555
|
-
let nodes = is_template(template)
|
|
556
|
-
? [...template.content.cloneNode(true).childNodes]
|
|
557
|
-
: [template.cloneNode(true)];
|
|
558
|
-
|
|
559
|
-
mutateDom(() => {
|
|
560
|
-
for (let node of nodes) {
|
|
561
|
-
is_element(node) && addScopeToNode(node, scope, el);
|
|
562
|
-
el.parentElement.insertBefore(node, el);
|
|
563
|
-
is_element(node) && initTree(node);
|
|
564
|
-
}
|
|
565
|
-
});
|
|
566
|
-
|
|
567
|
-
el._r_block = {
|
|
568
|
-
template,
|
|
569
|
-
update() {
|
|
570
|
-
mutateDom(() => {
|
|
571
|
-
for (let node of nodes ?? []) {
|
|
572
|
-
el.parentElement.insertBefore(node, el);
|
|
573
|
-
}
|
|
574
|
-
});
|
|
575
|
-
},
|
|
576
|
-
delete() {
|
|
577
|
-
el._r_block = null;
|
|
578
|
-
for (let node of nodes ?? []) {
|
|
579
|
-
node.remove();
|
|
580
|
-
}
|
|
581
|
-
nodes = null;
|
|
582
|
-
}
|
|
583
|
-
};
|
|
584
|
-
|
|
585
|
-
cleanup(() => el._r_block?.delete());
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
function initialize() {
|
|
589
|
-
document.body._r_block ??= (() => {
|
|
590
|
-
const observer = new MutationObserver(mutations => {
|
|
591
|
-
for (let mutation of mutations) {
|
|
592
|
-
for (let node of mutation.addedNodes) {
|
|
593
|
-
node._r_block?.update();
|
|
594
|
-
}
|
|
595
|
-
}
|
|
596
|
-
});
|
|
597
|
-
|
|
598
|
-
observer.observe(document.body, { childList: true, subtree: true });
|
|
599
|
-
return observer;
|
|
600
|
-
})();
|
|
414
|
+
function apply_select_values(el, values) {
|
|
415
|
+
for (const option of el.options) {
|
|
416
|
+
option.selected = loose_index_of(values, option.value) >= 0;
|
|
417
|
+
}
|
|
601
418
|
}
|
|
602
419
|
|
|
603
|
-
function
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
anchor_block(el, el, { addScopeToNode, cleanup, initTree, mutateDom });
|
|
611
|
-
});
|
|
420
|
+
function collect_selected_values(el) {
|
|
421
|
+
if (el.multiple) {
|
|
422
|
+
return [...el.selectedOptions].map(o => o.value);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
return el.value;
|
|
612
426
|
}
|
|
613
427
|
|
|
614
|
-
function
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
return;
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
const branches = [];
|
|
622
|
-
const has_default_case = () => branches.some(b => b.default);
|
|
623
|
-
|
|
624
|
-
for (let node of el.content.children) {
|
|
625
|
-
const expr = node.getAttribute("x-case");
|
|
626
|
-
if (expr !== null) {
|
|
627
|
-
has_default_case() && warn("The x-case directive cannot be appear after x-default");
|
|
628
|
-
branches.push({ el: node, get_value: create_getter(evaluateLater, expr) });
|
|
629
|
-
}
|
|
630
|
-
else if (node.hasAttribute("x-default")) {
|
|
631
|
-
has_default_case() && warn("Only one x-default directive is allowed");
|
|
632
|
-
branches.push({ el: node, get_value: () => true, default: true });
|
|
633
|
-
}
|
|
634
|
-
else {
|
|
635
|
-
warn("Element has no x-case or x-default directive and will be ignored", node);
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
const activate = branch => {
|
|
640
|
-
if (el._r_block?.template !== branch.el) {
|
|
641
|
-
clear();
|
|
642
|
-
anchor_block(el, branch.el, {
|
|
643
|
-
addScopeToNode,
|
|
644
|
-
cleanup,
|
|
645
|
-
initTree,
|
|
646
|
-
mutateDom
|
|
647
|
-
});
|
|
648
|
-
}
|
|
649
|
-
};
|
|
650
|
-
|
|
651
|
-
const clear = () => el._r_block?.delete();
|
|
652
|
-
|
|
653
|
-
effect(() => {
|
|
654
|
-
let active;
|
|
655
|
-
|
|
656
|
-
for (let branch of branches) {
|
|
657
|
-
if (branch.get_value() && !active) {
|
|
658
|
-
active = branch;
|
|
659
|
-
}
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
active ? activate(active) : clear();
|
|
663
|
-
});
|
|
664
|
-
});
|
|
428
|
+
function apply_group_values(el, values) {
|
|
429
|
+
el.checked = is_array(values)
|
|
430
|
+
? loose_index_of(values, el.value) >= 0
|
|
431
|
+
: loose_equal(el.value, values);
|
|
665
432
|
}
|
|
666
433
|
|
|
667
|
-
function
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
// This is important because manipulation can occur within the mutateDom function
|
|
684
|
-
// when mutation observing is disabled, preventing proper context propagation
|
|
685
|
-
// for cloned elements.
|
|
686
|
-
queueMicrotask(() => {
|
|
687
|
-
el.innerHTML = "";
|
|
688
|
-
el.append(tpl.content.cloneNode(true));
|
|
689
|
-
});
|
|
690
|
-
});
|
|
434
|
+
function collect_group_values(el, values) {
|
|
435
|
+
if (el.type === "radio") {
|
|
436
|
+
return el.value;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
values = as_array(values);
|
|
440
|
+
const index = loose_index_of(values, el.value);
|
|
441
|
+
|
|
442
|
+
if (el.checked) {
|
|
443
|
+
index >= 0 || values.push(el.value);
|
|
444
|
+
}
|
|
445
|
+
else {
|
|
446
|
+
index >= 0 && values.splice(index, 1);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
return values;
|
|
691
450
|
}
|
|
692
451
|
|
|
693
|
-
function plugin({
|
|
694
|
-
directive("
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
452
|
+
function plugin$4({ directive, evaluateLater, mutateDom }) {
|
|
453
|
+
directive("format", (el, { modifiers }, { effect }) => {
|
|
454
|
+
const placeholder_regex = /{{(?<expr>.+?)}}/g;
|
|
455
|
+
const is_once = has_modifier(modifiers, "once");
|
|
456
|
+
const has_format_attr = el => el.hasAttribute("x-format");
|
|
457
|
+
|
|
458
|
+
process(el);
|
|
459
|
+
|
|
460
|
+
function update(callback) {
|
|
461
|
+
if (is_once) {
|
|
462
|
+
mutateDom(() => callback());
|
|
463
|
+
}
|
|
464
|
+
else {
|
|
465
|
+
effect(() => mutateDom(() => callback()));
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
function process(node) {
|
|
470
|
+
switch (node.nodeType) {
|
|
471
|
+
case Node.TEXT_NODE:
|
|
472
|
+
process_text_node(node);
|
|
473
|
+
break;
|
|
474
|
+
|
|
475
|
+
case Node.ELEMENT_NODE:
|
|
476
|
+
if (node !== el) {
|
|
477
|
+
//
|
|
478
|
+
// When we encounter an element with the "x-data" attribute, its properties
|
|
479
|
+
// are not yet initialized, and the Alpine context is unavailable.
|
|
480
|
+
// Attempting to use these properties will result in
|
|
481
|
+
// an "Alpine Expression Error: [expression] is not defined".
|
|
482
|
+
//
|
|
483
|
+
// Workaround:
|
|
484
|
+
// To avoid this, we manually add our "x-format" directive to the element.
|
|
485
|
+
// Alpine evaluates "x-format" directive once the context is initialized.
|
|
486
|
+
// In the current loop, we skip these elements to defer their processing.
|
|
487
|
+
//
|
|
488
|
+
// This also handles cases where the user manually adds the "x-format" attribute.
|
|
489
|
+
//
|
|
490
|
+
if (node.hasAttribute("x-data") && !has_format_attr(node)) {
|
|
491
|
+
node.setAttribute("x-format", "");
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
if (has_format_attr(node)) {
|
|
495
|
+
break;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
process_nodes(node);
|
|
500
|
+
process_attributes(node);
|
|
501
|
+
break;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
function process_text_node(node) {
|
|
506
|
+
const tokens = node.textContent.split(placeholder_regex);
|
|
507
|
+
|
|
508
|
+
if (tokens.length > 1) {
|
|
509
|
+
const fragment = new DocumentFragment();
|
|
510
|
+
|
|
511
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
512
|
+
if ((i % 2) === 0) {
|
|
513
|
+
fragment.appendChild(document.createTextNode(tokens[i]));
|
|
514
|
+
}
|
|
515
|
+
else {
|
|
516
|
+
const get_value = create_getter(evaluateLater, node.parentNode, tokens[i]);
|
|
517
|
+
const text = document.createTextNode("");
|
|
518
|
+
|
|
519
|
+
fragment.append(text);
|
|
520
|
+
update(() => text.textContent = get_value());
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
mutateDom(() =>
|
|
525
|
+
node.parentElement.replaceChild(fragment, node));
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
function process_attributes(node) {
|
|
530
|
+
for (let attr of node.attributes) {
|
|
531
|
+
const matches = [...attr.value.matchAll(placeholder_regex)];
|
|
532
|
+
if (matches.length) {
|
|
533
|
+
const template = attr.value;
|
|
534
|
+
update(() => attr.value = template.replace(placeholder_regex, (_, expr) => create_getter(evaluateLater, node, expr)()));
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
function process_nodes(node) {
|
|
540
|
+
for (let child of node.childNodes) {
|
|
541
|
+
process(child);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
function anchor_block(el, template, { addScopeToNode, cleanup, initTree, mutateDom, scope = {} }) {
|
|
548
|
+
if (el._r_block) {
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
initialize();
|
|
553
|
+
|
|
554
|
+
let nodes = is_template(template)
|
|
555
|
+
? [...template.content.cloneNode(true).childNodes]
|
|
556
|
+
: [template.cloneNode(true)];
|
|
557
|
+
|
|
558
|
+
mutateDom(() => {
|
|
559
|
+
for (let node of nodes) {
|
|
560
|
+
is_element(node) && addScopeToNode(node, scope, el);
|
|
561
|
+
el.parentElement.insertBefore(node, el);
|
|
562
|
+
is_element(node) && initTree(node);
|
|
563
|
+
}
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
el._r_block = {
|
|
567
|
+
template,
|
|
568
|
+
update() {
|
|
569
|
+
mutateDom(() => {
|
|
570
|
+
for (let node of nodes ?? []) {
|
|
571
|
+
el.parentElement.insertBefore(node, el);
|
|
572
|
+
}
|
|
573
|
+
});
|
|
574
|
+
},
|
|
575
|
+
delete() {
|
|
576
|
+
el._r_block = null;
|
|
577
|
+
for (let node of nodes ?? []) {
|
|
578
|
+
node.remove();
|
|
579
|
+
}
|
|
580
|
+
nodes = null;
|
|
581
|
+
}
|
|
582
|
+
};
|
|
583
|
+
|
|
584
|
+
cleanup(() => el._r_block?.delete());
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
function initialize() {
|
|
588
|
+
document.body._r_block ??= (() => {
|
|
589
|
+
const observer = new MutationObserver(mutations => {
|
|
590
|
+
for (let mutation of mutations) {
|
|
591
|
+
for (let node of mutation.addedNodes) {
|
|
592
|
+
node._r_block?.update();
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
observer.observe(document.body, { childList: true, subtree: true });
|
|
598
|
+
return observer;
|
|
599
|
+
})();
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
function plugin$3({ addScopeToNode, directive, initTree, mutateDom }) {
|
|
603
|
+
directive("fragment", (el, {}, { cleanup }) => {
|
|
604
|
+
if (!is_template(el)) {
|
|
605
|
+
warn("x-fragment can only be used on a 'template' tag");
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
anchor_block(el, el, { addScopeToNode, cleanup, initTree, mutateDom });
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
function plugin$2({ addScopeToNode, directive, initTree, mutateDom }) {
|
|
614
|
+
directive("match", (el, { }, { cleanup, effect, evaluateLater }) => {
|
|
615
|
+
if (!is_template(el)) {
|
|
616
|
+
warn("x-match can only be used on a 'template' tag");
|
|
617
|
+
return;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
const branches = [];
|
|
621
|
+
const has_default_case = () => branches.some(b => b.default);
|
|
622
|
+
|
|
623
|
+
for (let node of el.content.children) {
|
|
624
|
+
const expr = node.getAttribute("x-case");
|
|
625
|
+
if (expr !== null) {
|
|
626
|
+
has_default_case() && warn("The x-case directive cannot be appear after x-default");
|
|
627
|
+
branches.push({ el: node, get_value: create_getter(evaluateLater, expr) });
|
|
628
|
+
}
|
|
629
|
+
else if (node.hasAttribute("x-default")) {
|
|
630
|
+
has_default_case() && warn("Only one x-default directive is allowed");
|
|
631
|
+
branches.push({ el: node, get_value: () => true, default: true });
|
|
632
|
+
}
|
|
633
|
+
else {
|
|
634
|
+
warn("Element has no x-case or x-default directive and will be ignored", node);
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
const activate = branch => {
|
|
639
|
+
if (el._r_block?.template !== branch.el) {
|
|
640
|
+
clear();
|
|
641
|
+
anchor_block(el, branch.el, {
|
|
642
|
+
addScopeToNode,
|
|
643
|
+
cleanup,
|
|
644
|
+
initTree,
|
|
645
|
+
mutateDom
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
};
|
|
649
|
+
|
|
650
|
+
const clear = () => el._r_block?.delete();
|
|
651
|
+
|
|
652
|
+
effect(() => {
|
|
653
|
+
let active;
|
|
654
|
+
|
|
655
|
+
for (let branch of branches) {
|
|
656
|
+
if (branch.get_value() && !active) {
|
|
657
|
+
active = branch;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
active ? activate(active) : clear();
|
|
662
|
+
});
|
|
663
|
+
});
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
function plugin$1(alpine) {
|
|
667
|
+
alpine.directive("template", (el, { expression }) => {
|
|
668
|
+
if (is_template(el)) {
|
|
669
|
+
warn("x-template cannot be used on a 'template' tag");
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
const tpl = document.getElementById(expression);
|
|
674
|
+
|
|
675
|
+
if (!is_template(tpl)) {
|
|
676
|
+
warn("x-template directive can only reference the template tag");
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
// Adding a queued task ensures asynchronous content update, allowing Alpine.js
|
|
681
|
+
// to handle context propagation for cloned elements properly.
|
|
682
|
+
// This is important because manipulation can occur within the mutateDom function
|
|
683
|
+
// when mutation observing is disabled, preventing proper context propagation
|
|
684
|
+
// for cloned elements.
|
|
685
|
+
queueMicrotask(() => {
|
|
686
|
+
el.innerHTML = "";
|
|
687
|
+
el.append(tpl.content.cloneNode(true));
|
|
688
|
+
});
|
|
689
|
+
});
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
function plugin({ addScopeToNode, directive, initTree, mutateDom }) {
|
|
693
|
+
directive("when", (el, { expression }, { cleanup, effect, evaluateLater }) => {
|
|
694
|
+
if (!is_template(el)) {
|
|
695
|
+
warn("x-when can only be used on a 'template' tag");
|
|
696
|
+
return;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
const activate = () => anchor_block(el, el, { addScopeToNode, cleanup, initTree, mutateDom });
|
|
700
|
+
const clear = () => el._r_block?.delete();
|
|
701
|
+
|
|
702
|
+
const get = create_getter(evaluateLater, expression);
|
|
703
|
+
effect(() => get() ? activate() : clear());
|
|
704
|
+
});
|
|
706
705
|
}
|
|
707
706
|
|
|
708
|
-
function __main(alpine) {
|
|
709
|
-
plugin$5(alpine);
|
|
710
|
-
plugin$4(alpine);
|
|
711
|
-
plugin$3(alpine);
|
|
712
|
-
plugin$2(alpine);
|
|
713
|
-
plugin$1(alpine);
|
|
714
|
-
plugin(alpine);
|
|
707
|
+
function __main(alpine) {
|
|
708
|
+
plugin$5(alpine);
|
|
709
|
+
plugin$4(alpine);
|
|
710
|
+
plugin$3(alpine);
|
|
711
|
+
plugin$2(alpine);
|
|
712
|
+
plugin$1(alpine);
|
|
713
|
+
plugin(alpine);
|
|
715
714
|
}
|
|
716
715
|
|
|
717
|
-
document
|
|
716
|
+
listen(document, "alpine:init", () => Alpine.plugin(__main));
|
|
718
717
|
|
|
719
718
|
})();
|