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