coldwired 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,883 @@
1
+ import { C as isFormOptionElement, E as isInputElement, L as nextAnimationFrame, O as isLinkElement, R as parseHTMLDocument, S as isFormInputElement, V as partition, W as wait, _ as isElement, a as dispatch, c as focusElement, l as focusNextElement, t as AbortError, u as groupBy, v as isElementOrText, w as isHTMLElement, x as isFormElement, z as parseHTMLFragment } from "./utils-C2S0wWJJ.mjs";
2
+ import morphdom from "morphdom";
3
+ //#region node_modules/tiny-invariant/dist/esm/tiny-invariant.js
4
+ var isProduction = process.env.NODE_ENV === "production";
5
+ var prefix = "Invariant failed";
6
+ function invariant(condition, message) {
7
+ if (condition) return;
8
+ if (isProduction) throw new Error(prefix);
9
+ var provided = typeof message === "function" ? message() : message;
10
+ var value = provided ? "".concat(prefix, ": ").concat(provided) : prefix;
11
+ throw new Error(value);
12
+ }
13
+ //#endregion
14
+ //#region src/actions/attribute-observer.ts
15
+ const attributes = [
16
+ "role",
17
+ "disabled",
18
+ "hidden",
19
+ "tab-index",
20
+ "style",
21
+ "aria-autocomplete",
22
+ "aria-checked",
23
+ "aria-disabled",
24
+ "aria-errormessage",
25
+ "aria-expanded",
26
+ "aria-haspopup",
27
+ "aria-hidden",
28
+ "aria-invalid",
29
+ "aria-label",
30
+ "aria-level",
31
+ "aria-modal",
32
+ "aria-multiline",
33
+ "aria-multiselectable",
34
+ "aria-orientation",
35
+ "aria-placeholder",
36
+ "aria-pressed",
37
+ "aria-readonly",
38
+ "aria-required",
39
+ "aria-selected",
40
+ "aria-sort",
41
+ "aria-valuemax",
42
+ "aria-valuemin",
43
+ "aria-valuenow",
44
+ "aria-valuetext",
45
+ "aria-busy",
46
+ "aria-live",
47
+ "aria-relevant",
48
+ "aria-atomic",
49
+ "aria-dropeffect",
50
+ "aria-grabbed",
51
+ "aria-activedescendant",
52
+ "aria-colcount",
53
+ "aria-colindex",
54
+ "aria-colspan",
55
+ "aria-controls",
56
+ "aria-describedby",
57
+ "aria-description",
58
+ "aria-details",
59
+ "aria-errormessage",
60
+ "aria-flowto",
61
+ "aria-labelledby",
62
+ "aria-owns",
63
+ "aria-posinset",
64
+ "aria-rowcount",
65
+ "aria-rowindex",
66
+ "aria-rowspan",
67
+ "aria-setsize",
68
+ "aria-atomic",
69
+ "aria-busy",
70
+ "aria-controls",
71
+ "aria-current",
72
+ "aria-describedby",
73
+ "aria-description",
74
+ "aria-details",
75
+ "aria-disabled",
76
+ "aria-dropeffect",
77
+ "aria-errormessage",
78
+ "aria-flowto",
79
+ "aria-grabbed",
80
+ "aria-haspopup",
81
+ "aria-hidden",
82
+ "aria-invalid",
83
+ "aria-keyshortcuts",
84
+ "aria-label",
85
+ "aria-labelledby",
86
+ "aria-live",
87
+ "aria-owns",
88
+ "aria-relevant",
89
+ "aria-roledescription"
90
+ ];
91
+ var AttributeObserver = class {
92
+ #element;
93
+ #observer;
94
+ #delegate;
95
+ constructor(element, delegate) {
96
+ this.#element = element;
97
+ this.#observer = new MutationObserver((mutations) => {
98
+ for (const mutation of mutations) this.onAttributeMutation(mutation);
99
+ });
100
+ this.#delegate = delegate;
101
+ }
102
+ observe() {
103
+ this.#observer.observe(this.#element, {
104
+ attributes: true,
105
+ attributeFilter: attributes,
106
+ subtree: true
107
+ });
108
+ }
109
+ disconnect() {
110
+ this.#observer.disconnect();
111
+ }
112
+ onAttributeMutation(mutation) {
113
+ if (isElement(mutation.target) && mutation.attributeName) if (mutation.attributeName == "style") {
114
+ if (isHTMLElement(mutation.target)) this.#delegate.attributeChanged(mutation.target, mutation.attributeName, mutation.target.style.cssText);
115
+ } else {
116
+ const value = mutation.target.getAttribute(mutation.attributeName);
117
+ this.#delegate.attributeChanged(mutation.target, mutation.attributeName, value);
118
+ }
119
+ }
120
+ };
121
+ //#endregion
122
+ //#region src/actions/class-list-observer.ts
123
+ var ClassListObserver = class {
124
+ #element;
125
+ #observer;
126
+ #delegate;
127
+ constructor(element, delegate) {
128
+ this.#element = element;
129
+ this.#observer = new MutationObserver((mutations) => {
130
+ for (const mutation of mutations) this.onAttributeMutation(mutation);
131
+ });
132
+ this.#delegate = delegate;
133
+ }
134
+ observe() {
135
+ this.#observer.observe(this.#element, {
136
+ attributes: true,
137
+ attributeFilter: ["class"],
138
+ attributeOldValue: true,
139
+ subtree: true
140
+ });
141
+ }
142
+ disconnect() {
143
+ this.#observer.disconnect();
144
+ }
145
+ onAttributeMutation(mutation) {
146
+ if (isElement(mutation.target)) if (mutation.oldValue) {
147
+ const classList = new Set(mutation.oldValue.split(/\s/).filter(Boolean));
148
+ this.#delegate.classListChanged(mutation.target, classList);
149
+ } else this.#delegate.classListChanged(mutation.target, /* @__PURE__ */ new Set([]));
150
+ }
151
+ };
152
+ //#endregion
153
+ //#region src/actions/metadata.ts
154
+ var Metadata = class {
155
+ #registry = /* @__PURE__ */ new WeakMap();
156
+ get(element) {
157
+ return this.#registry.get(element) ?? null;
158
+ }
159
+ getOrCreate(element) {
160
+ let metadata = this.#registry.get(element);
161
+ if (!metadata) {
162
+ metadata = {
163
+ addedClassNames: /* @__PURE__ */ new Set(),
164
+ removedClassNames: /* @__PURE__ */ new Set(),
165
+ touched: false,
166
+ attributes: {}
167
+ };
168
+ this.#registry.set(element, metadata);
169
+ }
170
+ return metadata;
171
+ }
172
+ clear() {
173
+ this.#registry = /* @__PURE__ */ new WeakMap();
174
+ }
175
+ };
176
+ //#endregion
177
+ //#region src/actions/morph.ts
178
+ function morph(fromElementOrDocument, toElementOrDocument, options) {
179
+ options?.plugins?.forEach((plugin) => plugin.validate?.(fromElementOrDocument));
180
+ if (fromElementOrDocument instanceof Document) {
181
+ invariant(toElementOrDocument instanceof Document, "Cannot morph document to element");
182
+ morphDocument(fromElementOrDocument, toElementOrDocument, options);
183
+ } else if (fromElementOrDocument instanceof Element) {
184
+ invariant(!(toElementOrDocument instanceof Document), "Cannot morph element to document");
185
+ if (toElementOrDocument instanceof DocumentFragment) morphToDocumentFragment(fromElementOrDocument, toElementOrDocument, options);
186
+ else if (typeof toElementOrDocument == "string") morphToDocumentFragment(fromElementOrDocument, parseHTMLFragment(toElementOrDocument, fromElementOrDocument.ownerDocument), options);
187
+ else morphToElement(fromElementOrDocument, toElementOrDocument, options);
188
+ }
189
+ }
190
+ function morphToDocumentFragment(fromElement, toDocumentFragment, options) {
191
+ toDocumentFragment.normalize();
192
+ if (options?.childrenOnly) {
193
+ if (!options?.plugins?.some((plugin) => plugin.onBeforeUpdateElement?.(fromElement, toDocumentFragment))) {
194
+ const wrapper = toDocumentFragment.ownerDocument.createElement("div");
195
+ wrapper.append(toDocumentFragment);
196
+ morphToElement(fromElement, wrapper, options);
197
+ }
198
+ } else {
199
+ const [firstChild, secondChild, ...children] = [...toDocumentFragment.childNodes].filter(isElementOrText);
200
+ if (isElement(firstChild)) {
201
+ if (secondChild) fromElement.after(secondChild);
202
+ morphToElement(fromElement, firstChild, options);
203
+ } else if (isElement(secondChild)) {
204
+ fromElement.before(firstChild);
205
+ morphToElement(fromElement, secondChild, options);
206
+ } else {
207
+ if (secondChild) fromElement.after(secondChild);
208
+ fromElement.replaceWith(firstChild);
209
+ }
210
+ fromElement.after(...children);
211
+ }
212
+ }
213
+ function morphToElement(fromElement, toElement, options) {
214
+ const forceAttribute = options?.forceAttribute;
215
+ const added = /* @__PURE__ */ new WeakSet();
216
+ morphdom(fromElement, toElement, {
217
+ childrenOnly: options?.childrenOnly,
218
+ onBeforeElUpdated(fromElement, toElement) {
219
+ if (options?.plugins?.some((plugin) => plugin.onBeforeUpdateElement?.(fromElement, toElement))) return false;
220
+ const force = forceAttribute ? !!toElement.closest(`[${forceAttribute}="server"]`) : false;
221
+ const metadata = options?.metadata?.get(fromElement);
222
+ if (force && metadata) {
223
+ if (isFormInputElement(fromElement) || isFormOptionElement(fromElement)) metadata.touched = false;
224
+ }
225
+ if (fromElement.isEqualNode(toElement)) return false;
226
+ if (!force && forceAttribute) {
227
+ if (!!fromElement.closest(`[${forceAttribute}="browser"]`)) return false;
228
+ }
229
+ if (!force && metadata) {
230
+ toElement.classList.add(...metadata.addedClassNames);
231
+ toElement.classList.remove(...metadata.removedClassNames);
232
+ for (const [name, value] of Object.entries(metadata.attributes)) if (value == null) toElement.removeAttribute(name);
233
+ else if (name == "style") {
234
+ if (isHTMLElement(toElement)) toElement.style.cssText = value;
235
+ } else toElement.setAttribute(name, value);
236
+ if (metadata.touched) {
237
+ if (isInputElement(fromElement) && (fromElement.type == "checkbox" || fromElement.type == "radio")) Object.assign(toElement, { checked: fromElement.checked });
238
+ else if (isFormOptionElement(fromElement)) Object.assign(toElement, { selected: fromElement.selected });
239
+ else if (isFormInputElement(fromElement)) Object.assign(toElement, { value: fromElement.value });
240
+ }
241
+ }
242
+ return true;
243
+ },
244
+ onBeforeNodeDiscarded(node) {
245
+ if (isElement(node)) {
246
+ options?.plugins?.forEach((plugin) => plugin.onBeforeDestroyElement?.(node));
247
+ focusNextElement(node, options);
248
+ }
249
+ return true;
250
+ },
251
+ onNodeAdded(node) {
252
+ if (isElement(node) && node.parentElement) {
253
+ if (!added.has(node.parentElement)) options?.plugins?.forEach((plugin) => plugin.onCreateElement?.(node));
254
+ added.add(node);
255
+ }
256
+ return node;
257
+ }
258
+ });
259
+ if (forceAttribute) {
260
+ const forcedElements = fromElement.getAttribute(forceAttribute) == "server" ? [fromElement] : [];
261
+ for (const element of [...forcedElements, ...fromElement.querySelectorAll(`[${forceAttribute}="server"]`)]) element.removeAttribute(forceAttribute);
262
+ }
263
+ }
264
+ function morphDocument(fromDocument, toDocument, options) {
265
+ if (toDocument.head) morphHead(fromDocument.head, fromDocument.adoptNode(toDocument.head));
266
+ morphToElement(fromDocument.body, fromDocument.adoptNode(toDocument.body), options);
267
+ }
268
+ function morphHead(fromHeadElement, toHeadElement) {
269
+ morphdom(fromHeadElement, toHeadElement, {
270
+ childrenOnly: true,
271
+ onBeforeElUpdated(fromElement, toElement) {
272
+ if (fromElement.isEqualNode(toElement)) return false;
273
+ return true;
274
+ },
275
+ onBeforeNodeDiscarded(node) {
276
+ if (isLinkElement(node)) return false;
277
+ return true;
278
+ }
279
+ });
280
+ }
281
+ //#endregion
282
+ //#region src/actions/schema.ts
283
+ const defaultSchema = {
284
+ forceAttribute: "data-turbo-force",
285
+ focusGroupAttribute: "data-turbo-focus-group",
286
+ focusDirectionAttribute: "data-turbo-focus-direction",
287
+ hiddenClassName: "hidden"
288
+ };
289
+ //#endregion
290
+ //#region src/actions/actions.ts
291
+ const voidActionNames = [
292
+ "remove",
293
+ "focus",
294
+ "enable",
295
+ "disable",
296
+ "hide",
297
+ "show",
298
+ "reset",
299
+ "refresh"
300
+ ];
301
+ const fragmentActionNames = [
302
+ "after",
303
+ "before",
304
+ "append",
305
+ "prepend",
306
+ "replace",
307
+ "update"
308
+ ];
309
+ const actionNames = [...voidActionNames, ...fragmentActionNames];
310
+ var Actions = class {
311
+ #element;
312
+ #schema;
313
+ #classListObserver;
314
+ #attributeObserver;
315
+ #delegate;
316
+ #metadata = new Metadata();
317
+ #controller = new AbortController();
318
+ #plugins = [];
319
+ #refreshRequestController;
320
+ #pending = /* @__PURE__ */ new Set();
321
+ #pinned = /* @__PURE__ */ new Map();
322
+ #debug;
323
+ constructor(options) {
324
+ this.#element = options?.element ?? document.documentElement;
325
+ this.#schema = {
326
+ ...defaultSchema,
327
+ ...options?.schema
328
+ };
329
+ this.#debug = options?.debug ?? false;
330
+ this.#plugins = options?.plugins ?? [];
331
+ this.#delegate = {
332
+ handleEvent: this.handleEvent.bind(this),
333
+ classListChanged: this.classListChanged.bind(this),
334
+ attributeChanged: this.attributeChanged.bind(this)
335
+ };
336
+ this.#classListObserver = new ClassListObserver(this.#element, this.#delegate);
337
+ this.#attributeObserver = new AttributeObserver(this.#element, this.#delegate);
338
+ }
339
+ async ready() {
340
+ const pendingPlugins = this.#plugins.map((plugin) => plugin.ready());
341
+ await Promise.all([...this.#pending, ...pendingPlugins]);
342
+ }
343
+ get element() {
344
+ return this.#element;
345
+ }
346
+ get plugins() {
347
+ return this.#plugins;
348
+ }
349
+ observe() {
350
+ this.#classListObserver.observe();
351
+ this.#attributeObserver.observe();
352
+ this.#element.addEventListener("input", this.#delegate);
353
+ this.#element.addEventListener("change", this.#delegate);
354
+ this.#element.addEventListener(ACTIONS_EVENT_TYPE, this.#delegate);
355
+ this.#plugins.forEach((plugin) => plugin.init(this.#element));
356
+ }
357
+ disconnect() {
358
+ this.clear();
359
+ this.#classListObserver.disconnect();
360
+ this.#attributeObserver.disconnect();
361
+ this.#element.removeEventListener("input", this.#delegate);
362
+ this.#element.removeEventListener("change", this.#delegate);
363
+ this.#element.removeEventListener(ACTIONS_EVENT_TYPE, this.#delegate);
364
+ }
365
+ clear() {
366
+ this.#controller.abort();
367
+ this.#controller = new AbortController();
368
+ this.#pending.clear();
369
+ this.#pinned.clear();
370
+ this.#metadata.clear();
371
+ }
372
+ applyActions(actions) {
373
+ const materializedActions = [];
374
+ const unmaterializedActions = [];
375
+ for (const action of actions) if (isMaterializedAction(action)) materializedActions.push(action);
376
+ else unmaterializedActions.push(action);
377
+ this.scheduleMaterializedActions(materializedActions);
378
+ const immediateActions = unmaterializedActions.filter(isImmediateAction);
379
+ const delayedActions = groupBy(unmaterializedActions.filter(isDelayedAction), ({ delay }) => delay);
380
+ this.scheduleActions(immediateActions);
381
+ for (const [delay, actions] of delayedActions) this.scheduleActions(actions, delay);
382
+ }
383
+ applyPinnedActions(element) {
384
+ const actions = [...this.#pinned].flatMap(([, actions]) => actions);
385
+ this._applyActionsInContext(this.materializeActions(actions, element));
386
+ }
387
+ after(params) {
388
+ this.applyActions([{
389
+ action: "after",
390
+ ...params
391
+ }]);
392
+ }
393
+ before(params) {
394
+ this.applyActions([{
395
+ action: "before",
396
+ ...params
397
+ }]);
398
+ }
399
+ append(params) {
400
+ this.applyActions([{
401
+ action: "append",
402
+ ...params
403
+ }]);
404
+ }
405
+ prepend(params) {
406
+ this.applyActions([{
407
+ action: "prepend",
408
+ ...params
409
+ }]);
410
+ }
411
+ replace(params) {
412
+ this.applyActions([{
413
+ action: "replace",
414
+ ...params
415
+ }]);
416
+ }
417
+ update(params) {
418
+ this.applyActions([{
419
+ action: "update",
420
+ ...params
421
+ }]);
422
+ }
423
+ remove(params) {
424
+ this.applyActions([{
425
+ action: "remove",
426
+ ...params
427
+ }]);
428
+ }
429
+ focus(params) {
430
+ this.applyActions([{
431
+ action: "focus",
432
+ ...params
433
+ }]);
434
+ }
435
+ disable(params) {
436
+ this.applyActions([{
437
+ action: "disable",
438
+ ...params
439
+ }]);
440
+ }
441
+ enable(params) {
442
+ this.applyActions([{
443
+ action: "enable",
444
+ ...params
445
+ }]);
446
+ }
447
+ hide(params) {
448
+ this.applyActions([{
449
+ action: "hide",
450
+ ...params
451
+ }]);
452
+ }
453
+ show(params) {
454
+ this.applyActions([{
455
+ action: "show",
456
+ ...params
457
+ }]);
458
+ }
459
+ reset(params) {
460
+ this.applyActions([{
461
+ action: "reset",
462
+ ...params
463
+ }]);
464
+ }
465
+ refresh(params) {
466
+ this.applyActions([{
467
+ action: "refresh",
468
+ ...params
469
+ }]);
470
+ }
471
+ morph(from, to, options) {
472
+ this.#classListObserver.disconnect();
473
+ this.#attributeObserver.disconnect();
474
+ this._morph(from, to, options);
475
+ this.#classListObserver.observe();
476
+ this.#attributeObserver.observe();
477
+ }
478
+ scheduleActions(actions, delay) {
479
+ const promise = this._scheduleActions(actions, delay);
480
+ this.#pending.add(promise);
481
+ promise.finally(() => this.#pending.delete(promise));
482
+ }
483
+ scheduleMaterializedActions(actions) {
484
+ const promise = nextAnimationFrame().then(() => this._applyActionsInContext(actions));
485
+ this.#pending.add(promise);
486
+ promise.finally(() => this.#pending.delete(promise));
487
+ }
488
+ materializeActions(actions, element) {
489
+ this._debugMaterializeActions(actions, element);
490
+ return actions.map((action) => {
491
+ if (action.action == "refresh") return {
492
+ ...action,
493
+ targets: []
494
+ };
495
+ const targets = getTargetElements(element, action.targets);
496
+ return {
497
+ ...action,
498
+ targets
499
+ };
500
+ });
501
+ }
502
+ async _scheduleActions(actions, delay) {
503
+ if (!delay) await nextAnimationFrame();
504
+ else {
505
+ try {
506
+ await wait(delay, this.#controller.signal);
507
+ } catch (error) {
508
+ if (error instanceof AbortError) return;
509
+ throw error;
510
+ }
511
+ if (this.#controller.signal?.aborted) return;
512
+ }
513
+ for (const action of actions) {
514
+ validateAction(action);
515
+ this.pinAction(action);
516
+ }
517
+ let materializedActions = this.materializeActions(actions, this.#element);
518
+ const activeElement = this.#element.ownerDocument.activeElement;
519
+ if (delay && activeElement) materializedActions = materializedActions.filter((action) => {
520
+ if (action.action == "remove" || action.action == "hide" || action.action == "disable") {
521
+ for (const target of action.targets) if (target == activeElement || target.contains(activeElement)) return false;
522
+ }
523
+ return true;
524
+ });
525
+ this._applyActionsInContext(materializedActions);
526
+ }
527
+ _applyActionsInContext(actions) {
528
+ const [observableActions, unobservableActions] = partition(actions, isFragmentAction);
529
+ this._applyActions(unobservableActions);
530
+ if (observableActions.length > 0) {
531
+ this.#classListObserver.disconnect();
532
+ this.#attributeObserver.disconnect();
533
+ this._applyActions(observableActions);
534
+ this.#classListObserver.observe();
535
+ this.#attributeObserver.observe();
536
+ }
537
+ }
538
+ _applyActions(actions) {
539
+ this._debugApplyActions(actions);
540
+ for (const action of actions) if (isFragmentAction(action)) this[`_${action.action}`](action);
541
+ else {
542
+ if (action.action != "focus") action.targets.forEach((element) => {
543
+ this.#plugins.forEach((plugin) => plugin.validate?.(element));
544
+ });
545
+ this[`_${action.action}`](action);
546
+ }
547
+ }
548
+ _debugMaterializeActions(actions, element) {
549
+ if (this.#debug && actions.length > 0) {
550
+ if (actions.length == 1) console.groupCollapsed(`[actions] materialize one action:`);
551
+ else console.groupCollapsed(`[actions] materialize ${actions.length} actions:`);
552
+ for (const action of actions) console.log(`"${action.action}"`, action.targets, element);
553
+ console.groupEnd();
554
+ }
555
+ }
556
+ _debugApplyActions(actions) {
557
+ if (this.#debug && actions.length > 0) {
558
+ if (actions.length == 1) console.groupCollapsed(`[actions] apply one action:`);
559
+ else console.groupCollapsed(`[actions] apply ${actions.length} actions:`);
560
+ for (const action of actions) console.dir(action);
561
+ console.groupEnd();
562
+ }
563
+ }
564
+ pinAction({ delay, pin, ...params }) {
565
+ if (pin && !delay) {
566
+ const key = `${params.action}--${params.targets}`;
567
+ if (pin == "last") this.#pinned.set(key, [params]);
568
+ else {
569
+ let streams = this.#pinned.get(key);
570
+ if (!streams) {
571
+ streams = [];
572
+ this.#pinned.set(key, streams);
573
+ }
574
+ streams.push(params);
575
+ }
576
+ }
577
+ }
578
+ _morph(from, to, options) {
579
+ morph(from, to, {
580
+ metadata: this.#metadata,
581
+ plugins: this.#plugins,
582
+ ...this.#schema,
583
+ ...options
584
+ });
585
+ }
586
+ _after({ targets, fragment }) {
587
+ for (const element of targets) {
588
+ element.after(getDocumentFragment(fragment, element));
589
+ const parent = element.parentElement;
590
+ if (parent) this.plugins.forEach((plugin) => plugin.onCreateElement?.(parent));
591
+ }
592
+ }
593
+ _before({ targets, fragment }) {
594
+ for (const element of targets) {
595
+ element.before(getDocumentFragment(fragment, element));
596
+ const parent = element.parentElement;
597
+ if (parent) this.plugins.forEach((plugin) => plugin.onCreateElement?.(parent));
598
+ }
599
+ }
600
+ _append({ targets, fragment }) {
601
+ for (const element of targets) {
602
+ element.append(getDocumentFragment(fragment, element, true));
603
+ this.plugins.forEach((plugin) => plugin.onCreateElement?.(element));
604
+ }
605
+ }
606
+ _prepend({ targets, fragment }) {
607
+ for (const element of targets) {
608
+ element.prepend(getDocumentFragment(fragment, element, true));
609
+ this.plugins.forEach((plugin) => plugin.onCreateElement?.(element));
610
+ }
611
+ }
612
+ _replace({ targets, fragment }) {
613
+ for (const element of targets) this._morph(element, getDocumentFragment(fragment, element));
614
+ }
615
+ _update({ targets, fragment }) {
616
+ for (const element of targets) this._morph(element, getDocumentFragment(fragment, element), { childrenOnly: true });
617
+ }
618
+ _remove({ targets }) {
619
+ for (const element of targets) {
620
+ focusNextElement(element, this.#schema);
621
+ element.remove();
622
+ }
623
+ }
624
+ _focus({ targets }) {
625
+ const element = targets.at(0);
626
+ if (element) focusElement(element);
627
+ }
628
+ _show({ targets }) {
629
+ for (const element of targets) {
630
+ element.removeAttribute("hidden");
631
+ element.classList.remove(this.#schema.hiddenClassName);
632
+ }
633
+ }
634
+ _hide({ targets }) {
635
+ for (const element of targets) {
636
+ focusNextElement(element, this.#schema);
637
+ element.setAttribute("hidden", "hidden");
638
+ element.classList.add(this.#schema.hiddenClassName);
639
+ }
640
+ }
641
+ _enable({ targets }) {
642
+ for (const element of targets) if ("disabled" in element) element.disabled = false;
643
+ }
644
+ _disable({ targets }) {
645
+ for (const element of targets) if ("disabled" in element) {
646
+ focusNextElement(element, this.#schema);
647
+ element.disabled = true;
648
+ }
649
+ }
650
+ _reset({ targets }) {
651
+ for (const element of targets) if (isFormElement(element)) element.reset();
652
+ else console.warn("reset action is not supported on non-form elements", element);
653
+ }
654
+ _refresh() {
655
+ const currentPath = () => `${window.location.pathname}${window.location.search}`;
656
+ this.#refreshRequestController?.abort();
657
+ const controller = new AbortController();
658
+ this.#refreshRequestController = controller;
659
+ const path = currentPath();
660
+ fetch(path, {
661
+ method: "get",
662
+ signal: controller.signal,
663
+ headers: { accept: "text/html,application/xhtml+xml" },
664
+ credentials: "same-origin",
665
+ redirect: "follow"
666
+ }).then((response) => {
667
+ if (currentPath() == path) if (response.redirected) window.location.href = response.url;
668
+ else response.text().then((html) => {
669
+ const newDocument = parseHTMLDocument(html);
670
+ if (response.ok) this._morph(document.body, newDocument.body);
671
+ else document.body.innerHTML = newDocument.body.innerHTML;
672
+ });
673
+ }, (error) => {
674
+ if (error.name != "AbortError") console.error(`Failed to refresh the page: ${path}`, error);
675
+ }).finally(() => {
676
+ this.#refreshRequestController = void 0;
677
+ });
678
+ }
679
+ handleEvent(event) {
680
+ const target = event.composedPath && event.composedPath()[0] || event.target;
681
+ if (event.type == ACTIONS_EVENT_TYPE) {
682
+ const actions = (event.detail ?? []).map((action) => ({
683
+ ...action,
684
+ targets: [target]
685
+ }));
686
+ this.applyActions(actions);
687
+ } else if (isFormInputElement(target)) this.#metadata.getOrCreate(target).touched = true;
688
+ }
689
+ classListChanged(element, oldClassList) {
690
+ const metadata = this.#metadata.getOrCreate(element);
691
+ const classList = new Set(element.classList);
692
+ const added = difference(classList, oldClassList);
693
+ const removed = difference(oldClassList, classList);
694
+ for (const className of added) {
695
+ metadata.addedClassNames.add(className);
696
+ metadata.removedClassNames.delete(className);
697
+ }
698
+ for (const className of removed) {
699
+ metadata.removedClassNames.add(className);
700
+ metadata.addedClassNames.delete(className);
701
+ }
702
+ }
703
+ attributeChanged(element, attributeName, value) {
704
+ this.#metadata.getOrCreate(element).attributes[attributeName] = value;
705
+ }
706
+ };
707
+ function removeDuplicateTargetChildren(targets, fragment) {
708
+ for (const element of duplicateChildren(targets, fragment)) element.remove();
709
+ }
710
+ function duplicateChildren(targets, fragment) {
711
+ const existingChildren = targets.flatMap((element) => [...element.children]).filter((element) => !!element.id);
712
+ const newChildrenIds = new Set([...fragment.children].filter((element) => !!element.id).map((element) => element.id));
713
+ return existingChildren.filter((element) => newChildrenIds.has(element.id));
714
+ }
715
+ function difference(a, b) {
716
+ return new Set([...a].filter((x) => !b.has(x)));
717
+ }
718
+ function isValidActionName(actionName) {
719
+ return !!actionName && actionNames.includes(actionName);
720
+ }
721
+ function isImmediateAction(action) {
722
+ return !action.delay;
723
+ }
724
+ function isDelayedAction(action) {
725
+ return !!action.delay;
726
+ }
727
+ function isFragmentAction(action) {
728
+ return "fragment" in action;
729
+ }
730
+ function isMaterializedAction(action) {
731
+ return !(typeof action.targets == "string");
732
+ }
733
+ function getTargetElements(element, selector) {
734
+ if (selector == "head") return [element.ownerDocument.head];
735
+ else if (selector == "body") return [element.ownerDocument.body];
736
+ return [...element.querySelectorAll(selector)];
737
+ }
738
+ function getDocumentFragment(fragmentOrHTML, target, deduplicate = false) {
739
+ const fragment = typeof fragmentOrHTML == "string" ? parseHTMLFragment(fragmentOrHTML, target.ownerDocument) : fragmentOrHTML.cloneNode(true);
740
+ if (deduplicate) removeDuplicateTargetChildren([target], fragment);
741
+ return fragment;
742
+ }
743
+ function serializeDocumentFragment(action) {
744
+ if (isFragmentAction(action)) {
745
+ if (action.fragment instanceof DocumentFragment) return new XMLSerializer().serializeToString(action.fragment);
746
+ return action.fragment;
747
+ }
748
+ }
749
+ function getTargets(targets) {
750
+ if (typeof targets == "string") return [...document.querySelectorAll(targets)];
751
+ else if (Array.isArray(targets)) return [...targets];
752
+ return targets ? [targets] : [];
753
+ }
754
+ function validateAction(action) {
755
+ invariant(!(action.delay && action.pin), "[actions] a delayed action cannot be pinned");
756
+ }
757
+ var DispatchEventElement = class extends HTMLElement {
758
+ constructor() {
759
+ super();
760
+ this.style.display = "none";
761
+ }
762
+ connectedCallback() {
763
+ const type = this.getAttribute("type");
764
+ const target = this.parentElement?.tagName == "HEAD" ? this.ownerDocument.documentElement : this.previousElementSibling;
765
+ invariant(type, "[dispatch-event] must have \"type\" attribute");
766
+ invariant(target, "[dispatch-event] must have a target element");
767
+ const content = this.querySelector("script[type=\"application/json\"]")?.textContent;
768
+ dispatch(type, {
769
+ target,
770
+ detail: content ? parseEventDetail(content) : null
771
+ });
772
+ this.remove();
773
+ }
774
+ };
775
+ if (!customElements.get("dispatch-event")) customElements.define("dispatch-event", DispatchEventElement);
776
+ function parseEventDetail(content) {
777
+ const maybeJSON = content.trim().replace(/^<!\[CDATA\[/, "").replace(/\]\]>$/, "").trim();
778
+ if (!maybeJSON) return null;
779
+ try {
780
+ return JSON.parse(maybeJSON);
781
+ } catch {
782
+ return null;
783
+ }
784
+ }
785
+ const ACTIONS_EVENT_TYPE = "__actions__";
786
+ function dispatchActions(actions) {
787
+ const actionsByTarget = groupBy(actions.flatMap((action) => {
788
+ return getTargets(action.targets).filter((target) => target.isConnected).map((target) => [
789
+ target,
790
+ action.action,
791
+ serializeDocumentFragment(action)
792
+ ]);
793
+ }), ([target]) => target);
794
+ for (const [target, actions] of actionsByTarget) dispatch(ACTIONS_EVENT_TYPE, {
795
+ target,
796
+ detail: actions.map(([, action, fragment]) => ({
797
+ action,
798
+ fragment
799
+ }))
800
+ });
801
+ }
802
+ function dispatchAction({ targets, ...action }) {
803
+ dispatchActions([{
804
+ ...action,
805
+ targets: getTargets(targets)
806
+ }]);
807
+ }
808
+ //#endregion
809
+ //#region src/actions.ts
810
+ function hide(targets) {
811
+ dispatchAction({
812
+ action: "hide",
813
+ targets
814
+ });
815
+ }
816
+ function show(targets) {
817
+ dispatchAction({
818
+ action: "show",
819
+ targets
820
+ });
821
+ }
822
+ function disable(targets) {
823
+ dispatchAction({
824
+ action: "disable",
825
+ targets
826
+ });
827
+ }
828
+ function enable(targets) {
829
+ dispatchAction({
830
+ action: "enable",
831
+ targets
832
+ });
833
+ }
834
+ function remove(targets) {
835
+ dispatchAction({
836
+ action: "remove",
837
+ targets
838
+ });
839
+ }
840
+ function append(targets, fragment) {
841
+ dispatchAction({
842
+ action: "append",
843
+ targets,
844
+ fragment
845
+ });
846
+ }
847
+ function prepend(targets, fragment) {
848
+ dispatchAction({
849
+ action: "prepend",
850
+ targets,
851
+ fragment
852
+ });
853
+ }
854
+ function after(targets, fragment) {
855
+ dispatchAction({
856
+ action: "after",
857
+ targets,
858
+ fragment
859
+ });
860
+ }
861
+ function before(targets, fragment) {
862
+ dispatchAction({
863
+ action: "before",
864
+ targets,
865
+ fragment
866
+ });
867
+ }
868
+ function update(targets, fragment) {
869
+ dispatchAction({
870
+ action: "update",
871
+ targets,
872
+ fragment
873
+ });
874
+ }
875
+ function replace(targets, fragment) {
876
+ dispatchAction({
877
+ action: "replace",
878
+ targets,
879
+ fragment
880
+ });
881
+ }
882
+ //#endregion
883
+ export { invariant as _, enable as a, remove as c, update as d, Actions as f, defaultSchema as g, isValidActionName as h, disable as i, replace as l, dispatchActions as m, append as n, hide as o, dispatchAction as p, before as r, prepend as s, after as t, show as u };