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.
- package/LICENSE +21 -0
- package/README.md +168 -0
- package/dist/actions-CQVSvZlC.d.mts +134 -0
- package/dist/actions-k0j8z15o.mjs +883 -0
- package/dist/actions.d.mts +2 -0
- package/dist/actions.mjs +2 -0
- package/dist/react.d.mts +88 -0
- package/dist/react.mjs +635 -0
- package/dist/turbo-stream.d.mts +7 -0
- package/dist/turbo-stream.mjs +72 -0
- package/dist/utils-C2S0wWJJ.mjs +350 -0
- package/dist/utils.d.mts +80 -0
- package/dist/utils.mjs +2 -0
- package/package.json +72 -0
|
@@ -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 };
|