@brandup/ui 1.0.44 → 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +482 -33
- package/dist/cjs/constants.js +14 -0
- package/dist/cjs/constants.js.map +1 -0
- package/dist/cjs/dom/bind-each.js +90 -0
- package/dist/cjs/dom/bind-each.js.map +1 -0
- package/dist/cjs/dom/bind.js +29 -0
- package/dist/cjs/dom/bind.js.map +1 -0
- package/dist/cjs/dom/binding-cleanup.js +162 -0
- package/dist/cjs/dom/binding-cleanup.js.map +1 -0
- package/dist/cjs/dom/dom.js +184 -0
- package/dist/cjs/dom/dom.js.map +1 -0
- package/dist/cjs/dom/helpers.js +33 -0
- package/dist/cjs/dom/helpers.js.map +1 -0
- package/dist/cjs/dom/index.js +14 -0
- package/dist/cjs/dom/index.js.map +1 -0
- package/dist/cjs/dom/tag.js +207 -0
- package/dist/cjs/dom/tag.js.map +1 -0
- package/dist/cjs/element.js +265 -0
- package/dist/cjs/element.js.map +1 -0
- package/dist/cjs/events.js +204 -0
- package/dist/cjs/events.js.map +1 -0
- package/dist/cjs/ext.js +20 -0
- package/dist/cjs/ext.js.map +1 -0
- package/dist/cjs/index.js +37 -313
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/reactive/computed.js +36 -0
- package/dist/cjs/reactive/computed.js.map +1 -0
- package/dist/cjs/reactive/effect.js +197 -0
- package/dist/cjs/reactive/effect.js.map +1 -0
- package/dist/cjs/reactive/reactive.js +106 -0
- package/dist/cjs/reactive/reactive.js.map +1 -0
- package/dist/mjs/constants.js +10 -0
- package/dist/mjs/constants.js.map +1 -0
- package/dist/mjs/dom/bind-each.js +86 -0
- package/dist/mjs/dom/bind-each.js.map +1 -0
- package/dist/mjs/dom/bind.js +26 -0
- package/dist/mjs/dom/bind.js.map +1 -0
- package/dist/mjs/dom/binding-cleanup.js +156 -0
- package/dist/mjs/dom/binding-cleanup.js.map +1 -0
- package/dist/mjs/dom/dom.js +169 -0
- package/dist/mjs/dom/dom.js.map +1 -0
- package/dist/mjs/dom/helpers.js +29 -0
- package/dist/mjs/dom/helpers.js.map +1 -0
- package/dist/mjs/dom/index.js +12 -0
- package/dist/mjs/dom/index.js.map +1 -0
- package/dist/mjs/dom/tag.js +203 -0
- package/dist/mjs/dom/tag.js.map +1 -0
- package/dist/mjs/element.js +260 -0
- package/dist/mjs/element.js.map +1 -0
- package/dist/mjs/events.js +202 -0
- package/dist/mjs/events.js.map +1 -0
- package/dist/mjs/ext.js +18 -0
- package/dist/mjs/ext.js.map +1 -0
- package/dist/mjs/index.js +11 -314
- package/dist/mjs/index.js.map +1 -1
- package/dist/mjs/reactive/computed.js +33 -0
- package/dist/mjs/reactive/computed.js.map +1 -0
- package/dist/mjs/reactive/effect.js +187 -0
- package/dist/mjs/reactive/effect.js.map +1 -0
- package/dist/mjs/reactive/reactive.js +102 -0
- package/dist/mjs/reactive/reactive.js.map +1 -0
- package/dist/types.d.ts +489 -14
- package/package.json +9 -1
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A reactive binding for use as a {@link tag} child. Its `compute` function is
|
|
5
|
+
* re-evaluated and re-rendered whenever the reactive state it reads changes.
|
|
6
|
+
*/
|
|
7
|
+
class Binding {
|
|
8
|
+
compute;
|
|
9
|
+
constructor(compute) {
|
|
10
|
+
this.compute = compute;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Create a reactive {@link Binding} that can be passed as a `tag` child. The
|
|
15
|
+
* compute function is tracked: it re-runs (updating the DOM in place) whenever
|
|
16
|
+
* any reactive value it reads changes.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* const state = reactive({ name: "Alice" });
|
|
20
|
+
* DOM.tag("div", null, "Hi, ", bind(() => state.name));
|
|
21
|
+
* state.name = "Bob"; // the text updates in place
|
|
22
|
+
*/
|
|
23
|
+
function bind(compute) {
|
|
24
|
+
return new Binding(compute);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
exports.Binding = Binding;
|
|
28
|
+
exports.bind = bind;
|
|
29
|
+
//# sourceMappingURL=bind.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bind.js","sources":["../../../../source/dom/bind.ts"],"sourcesContent":[null],"names":[],"mappings":";;AAKA;;;AAGG;MACU,OAAO,CAAA;AACE,IAAA,OAAA;AAArB,IAAA,WAAA,CAAqB,OAA2B,EAAA;QAA3B,IAAA,CAAA,OAAO,GAAP,OAAO;IAAwB;AACpD;AAED;;;;;;;;;AASG;AACG,SAAU,IAAI,CAAC,OAA2B,EAAA;AAC/C,IAAA,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC;AAC5B;;;;;"}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// UIElements are located via the data attribute `UIElement.setElement` already sets
|
|
4
|
+
// (`elem.dataset.uiElement` → `data-ui-element`), so no extra marker is needed for them.
|
|
5
|
+
const UIELEM_SELECTOR = "[data-ui-element]";
|
|
6
|
+
// Marker placed on every element a reactive binding renders into, so a removed
|
|
7
|
+
// subtree (or a cleared container) can be queried for affected bindings.
|
|
8
|
+
const BINDING_ATTR = "data-bui-binding";
|
|
9
|
+
const BINDING_SELECTOR = "[data-bui-binding]";
|
|
10
|
+
// node → UIElement auto-destroy entry
|
|
11
|
+
const trackedElements = new Map();
|
|
12
|
+
// container element → bindings rendered into it (one marker per container)
|
|
13
|
+
const bindingsByContainer = new Map();
|
|
14
|
+
let observer;
|
|
15
|
+
function ensureObserver() {
|
|
16
|
+
if (typeof MutationObserver !== "undefined" && !observer) {
|
|
17
|
+
observer = new MutationObserver(onMutations);
|
|
18
|
+
observer.observe(document, { childList: true, subtree: true });
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function disconnectIfEmpty() {
|
|
22
|
+
if (trackedElements.size === 0 && bindingsByContainer.size === 0 && observer) {
|
|
23
|
+
observer.disconnect();
|
|
24
|
+
observer = undefined;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* React only to *removed* nodes (insertions never disconnect anything). Two cases:
|
|
29
|
+
* - a removed element subtree → dispose the tracked UIElements/bindings inside it;
|
|
30
|
+
* - children removed from a surviving container (e.g. `container.innerHTML = ""`) →
|
|
31
|
+
* re-check the bindings rendered directly into that container.
|
|
32
|
+
* Work stays proportional to what changed, not to the total number tracked.
|
|
33
|
+
*/
|
|
34
|
+
function onMutations(mutations) {
|
|
35
|
+
for (const mutation of mutations) {
|
|
36
|
+
mutation.removedNodes.forEach(node => {
|
|
37
|
+
if (node instanceof HTMLElement)
|
|
38
|
+
disposeDisconnectedWithin(node);
|
|
39
|
+
});
|
|
40
|
+
// A binding's managed node (a text/comment) can be removed while its container
|
|
41
|
+
// stays connected (a cleared/replaced container). The container itself is then
|
|
42
|
+
// the mutation target, not a removed node, so check its bindings here.
|
|
43
|
+
const target = mutation.target;
|
|
44
|
+
if (mutation.removedNodes.length && target instanceof HTMLElement && target.hasAttribute(BINDING_ATTR))
|
|
45
|
+
disposeDisconnectedBindings(target);
|
|
46
|
+
}
|
|
47
|
+
disconnectIfEmpty();
|
|
48
|
+
}
|
|
49
|
+
/** Apply `fn` to `root` itself (when it matches) and every descendant matching `selector`. */
|
|
50
|
+
function forEachSelfAndMatches(root, selector, fn) {
|
|
51
|
+
if (root.matches(selector))
|
|
52
|
+
fn(root);
|
|
53
|
+
root.querySelectorAll(selector).forEach(el => fn(el));
|
|
54
|
+
}
|
|
55
|
+
/** Destroy/stop tracked entries inside a removed subtree that are no longer in the document. */
|
|
56
|
+
function disposeDisconnectedWithin(removed) {
|
|
57
|
+
// UIElements first: destroying one cascades to its nested UIElements and bindings.
|
|
58
|
+
forEachSelfAndMatches(removed, UIELEM_SELECTOR, el => {
|
|
59
|
+
// `isConnected` guards against moves (removed from one place, re-inserted in another).
|
|
60
|
+
if (!el.isConnected) {
|
|
61
|
+
const entry = trackedElements.get(el);
|
|
62
|
+
if (entry)
|
|
63
|
+
entry.destroy(); // destroy() untracks itself, cascades, and disposes its bindings
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
forEachSelfAndMatches(removed, BINDING_SELECTOR, disposeDisconnectedBindings);
|
|
67
|
+
}
|
|
68
|
+
/** Stop the bindings of `container` whose managed node has left the document. */
|
|
69
|
+
function disposeDisconnectedBindings(container) {
|
|
70
|
+
const set = bindingsByContainer.get(container);
|
|
71
|
+
if (!set)
|
|
72
|
+
return;
|
|
73
|
+
for (const binding of [...set]) {
|
|
74
|
+
if (!binding.getNode().isConnected) {
|
|
75
|
+
set.delete(binding);
|
|
76
|
+
binding.effect.stop();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (!set.size) {
|
|
80
|
+
bindingsByContainer.delete(container);
|
|
81
|
+
container.removeAttribute(BINDING_ATTR);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/** Stop and forget every binding of `container`, unconditionally (owner is being destroyed). */
|
|
85
|
+
function stopAllBindings(container) {
|
|
86
|
+
const set = bindingsByContainer.get(container);
|
|
87
|
+
if (!set)
|
|
88
|
+
return;
|
|
89
|
+
bindingsByContainer.delete(container);
|
|
90
|
+
container.removeAttribute(BINDING_ATTR);
|
|
91
|
+
set.forEach(binding => binding.effect.stop());
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Track a binding so its reactive effect is stopped automatically once its managed node has
|
|
95
|
+
* been mounted and then removed from the document (via a shared `MutationObserver`), and so
|
|
96
|
+
* {@link disposeBindingsWithin} can stop it when its owning UIElement is destroyed.
|
|
97
|
+
*
|
|
98
|
+
* @param container Element the binding renders into (indexed/marked for subtree queries).
|
|
99
|
+
* @param getNode Returns the binding's current managed node — its connectivity drives disposal.
|
|
100
|
+
* @param effect The reactive effect to stop on disposal.
|
|
101
|
+
*/
|
|
102
|
+
function autoDisposeBinding(container, getNode, effect) {
|
|
103
|
+
let set = bindingsByContainer.get(container);
|
|
104
|
+
if (!set) {
|
|
105
|
+
bindingsByContainer.set(container, set = new Set());
|
|
106
|
+
container.setAttribute(BINDING_ATTR, "");
|
|
107
|
+
}
|
|
108
|
+
set.add({ getNode, container, effect });
|
|
109
|
+
ensureObserver();
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Register a `UIElement`'s DOM node for auto-destroy: once the node has been mounted
|
|
113
|
+
* into the document and then removed, `destroy` is called automatically.
|
|
114
|
+
* @internal — called by `UIElement.setElement`.
|
|
115
|
+
*/
|
|
116
|
+
function trackAutoDestroy(node, destroy) {
|
|
117
|
+
trackedElements.set(node, { node, destroy });
|
|
118
|
+
ensureObserver();
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Remove a node from auto-destroy tracking (called when `UIElement.destroy` is
|
|
122
|
+
* invoked explicitly so the entry does not linger).
|
|
123
|
+
* @internal — called by `UIElement.destroy`.
|
|
124
|
+
*/
|
|
125
|
+
function untrackAutoDestroy(node) {
|
|
126
|
+
trackedElements.delete(node);
|
|
127
|
+
disconnectIfEmpty();
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Destroy every tracked `UIElement` nested within `root` (excluding `root` itself),
|
|
131
|
+
* deepest first. Located via the `data-ui-element` marker, so the cost is proportional
|
|
132
|
+
* to the subtree rather than to the total number of tracked elements.
|
|
133
|
+
* @internal — called by `UIElement.destroy`.
|
|
134
|
+
*/
|
|
135
|
+
function destroyUIElementsWithin(root) {
|
|
136
|
+
const victims = [];
|
|
137
|
+
root.querySelectorAll(UIELEM_SELECTOR).forEach(el => {
|
|
138
|
+
if (trackedElements.has(el))
|
|
139
|
+
victims.push(el);
|
|
140
|
+
});
|
|
141
|
+
// querySelectorAll yields document order (ancestors before descendants);
|
|
142
|
+
// iterate in reverse so deeper / nested elements are destroyed first.
|
|
143
|
+
for (let i = victims.length - 1; i >= 0; i--)
|
|
144
|
+
trackedElements.get(victims[i])?.destroy();
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Stop and forget every tracked binding rendered within `root` (used when a UIElement is
|
|
148
|
+
* destroyed). Unlike the observer path this is unconditional — it does not check connectivity,
|
|
149
|
+
* because the owner is being torn down.
|
|
150
|
+
*/
|
|
151
|
+
function disposeBindingsWithin(root) {
|
|
152
|
+
if (root instanceof HTMLElement)
|
|
153
|
+
forEachSelfAndMatches(root, BINDING_SELECTOR, stopAllBindings);
|
|
154
|
+
disconnectIfEmpty();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
exports.autoDisposeBinding = autoDisposeBinding;
|
|
158
|
+
exports.destroyUIElementsWithin = destroyUIElementsWithin;
|
|
159
|
+
exports.disposeBindingsWithin = disposeBindingsWithin;
|
|
160
|
+
exports.trackAutoDestroy = trackAutoDestroy;
|
|
161
|
+
exports.untrackAutoDestroy = untrackAutoDestroy;
|
|
162
|
+
//# sourceMappingURL=binding-cleanup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"binding-cleanup.js","sources":["../../../../source/dom/binding-cleanup.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var helpers = require('./helpers.js');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Finds an element by its `id` within the whole document.
|
|
7
|
+
* @param id The element id to look up.
|
|
8
|
+
* @returns The matching element, or `null` if none exists.
|
|
9
|
+
*/
|
|
10
|
+
function getById(id) {
|
|
11
|
+
return document.getElementById(id);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Returns the first descendant of `container` that has the given class.
|
|
15
|
+
* @param container Element to search within.
|
|
16
|
+
* @param className Single class name to match.
|
|
17
|
+
* @returns The first matching element, or `null` if none found.
|
|
18
|
+
*/
|
|
19
|
+
function getByClass(container, className) {
|
|
20
|
+
const elements = container.getElementsByClassName(className);
|
|
21
|
+
if (elements.length === 0)
|
|
22
|
+
return null;
|
|
23
|
+
return elements.item(0);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Returns the first element in the document with the given `name` attribute.
|
|
27
|
+
* @param name The `name` attribute value to look up.
|
|
28
|
+
* @returns The first matching element, or `null` if none found.
|
|
29
|
+
*/
|
|
30
|
+
function getByName(name) {
|
|
31
|
+
const elements = document.getElementsByName(name);
|
|
32
|
+
if (elements.length === 0)
|
|
33
|
+
return null;
|
|
34
|
+
return elements.item(0);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Returns the first descendant of `container` with the given tag name.
|
|
38
|
+
* @param container Element to search within.
|
|
39
|
+
* @param tagName Tag name to match (e.g. `"input"`).
|
|
40
|
+
* @returns The first matching element, or `null` if none found.
|
|
41
|
+
*/
|
|
42
|
+
function getElementByTagName(container, tagName) {
|
|
43
|
+
const elements = container.getElementsByTagName(tagName);
|
|
44
|
+
if (elements.length === 0)
|
|
45
|
+
return null;
|
|
46
|
+
return elements.item(0);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Returns all descendants of `container` with the given tag name as a live collection.
|
|
50
|
+
* @param container Element to search within.
|
|
51
|
+
* @param tagName Tag name to match (e.g. `"li"`).
|
|
52
|
+
* @returns A live `HTMLCollection` of matching elements.
|
|
53
|
+
*/
|
|
54
|
+
function getElementsByTagName(container, tagName) {
|
|
55
|
+
return container.getElementsByTagName(tagName);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Returns the first descendant of `container` matching the CSS selector.
|
|
59
|
+
* @param container Element to search within.
|
|
60
|
+
* @param query CSS selector.
|
|
61
|
+
* @returns The first matching element, or `null` if none found.
|
|
62
|
+
*/
|
|
63
|
+
function queryElement(container, query) {
|
|
64
|
+
return container.querySelector(query);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Returns all descendants of `container` matching the CSS selector.
|
|
68
|
+
* @param container Element to search within.
|
|
69
|
+
* @param query CSS selector.
|
|
70
|
+
* @returns A static `NodeList` of matching elements.
|
|
71
|
+
*/
|
|
72
|
+
function queryElements(container, query) {
|
|
73
|
+
return container.querySelectorAll(query);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Walks forward through the following siblings of `current` and returns the first one that has the given class.
|
|
77
|
+
* @param current Element to start from.
|
|
78
|
+
* @param className Class name to match.
|
|
79
|
+
* @returns The first matching following sibling, or `null` if none found.
|
|
80
|
+
*/
|
|
81
|
+
function nextElementByClass(current, className) {
|
|
82
|
+
let elem = current.nextSibling;
|
|
83
|
+
while (elem) {
|
|
84
|
+
if (elem.nodeType === Node.ELEMENT_NODE && elem instanceof HTMLElement && elem.classList.contains(className))
|
|
85
|
+
return elem;
|
|
86
|
+
elem = elem.nextSibling;
|
|
87
|
+
}
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Walks backward through the preceding siblings of `current` and returns the first one that has the given class.
|
|
92
|
+
* @param current Element to start from.
|
|
93
|
+
* @param className Class name to match.
|
|
94
|
+
* @returns The first matching preceding sibling, or `null` if none found.
|
|
95
|
+
*/
|
|
96
|
+
function prevElementByClass(current, className) {
|
|
97
|
+
let elem = current.previousSibling;
|
|
98
|
+
while (elem) {
|
|
99
|
+
if (elem.nodeType === Node.ELEMENT_NODE && elem instanceof HTMLElement && elem.classList.contains(className))
|
|
100
|
+
return elem;
|
|
101
|
+
elem = elem.previousSibling;
|
|
102
|
+
}
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Returns the nearest preceding sibling element of `current`, skipping non-element nodes (e.g. text nodes).
|
|
107
|
+
* @param current Element to start from.
|
|
108
|
+
* @returns The previous sibling element, or `null` if none found.
|
|
109
|
+
*/
|
|
110
|
+
function prevElement(current) {
|
|
111
|
+
let elem = current.previousSibling;
|
|
112
|
+
while (elem) {
|
|
113
|
+
if (elem.nodeType === Node.ELEMENT_NODE && elem instanceof HTMLElement)
|
|
114
|
+
return elem;
|
|
115
|
+
elem = elem.previousSibling;
|
|
116
|
+
}
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Returns the nearest following sibling element of `current`, skipping non-element nodes (e.g. text nodes).
|
|
121
|
+
* @param current Element to start from.
|
|
122
|
+
* @returns The next sibling element, or `null` if none found.
|
|
123
|
+
*/
|
|
124
|
+
function nextElement(current) {
|
|
125
|
+
let elem = current.nextSibling;
|
|
126
|
+
while (elem) {
|
|
127
|
+
if (elem.nodeType === Node.ELEMENT_NODE && elem instanceof HTMLElement)
|
|
128
|
+
return elem;
|
|
129
|
+
elem = elem.nextSibling;
|
|
130
|
+
}
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Adds the given CSS class(es) to every descendant of `container` matching the selector. No-op when `container` or `cssClass` is falsy.
|
|
135
|
+
* @param container Element to search within (ignored when null/undefined).
|
|
136
|
+
* @param selectors CSS selector for the elements to modify.
|
|
137
|
+
* @param cssClass Class name(s) to add.
|
|
138
|
+
*/
|
|
139
|
+
function addClass(container, selectors, cssClass) {
|
|
140
|
+
if (!container || !cssClass)
|
|
141
|
+
return;
|
|
142
|
+
const nodes = container.querySelectorAll(selectors);
|
|
143
|
+
nodes.forEach(node => helpers.default.addCssClass(node, cssClass));
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Removes the given CSS class(es) from every descendant of `container` matching the selector. No-op when `container` or `cssClass` is falsy.
|
|
147
|
+
* @param container Element to search within (ignored when null/undefined).
|
|
148
|
+
* @param selectors CSS selector for the elements to modify.
|
|
149
|
+
* @param cssClass Class name(s) to remove.
|
|
150
|
+
*/
|
|
151
|
+
function removeClass(container, selectors, cssClass) {
|
|
152
|
+
if (!container || !cssClass)
|
|
153
|
+
return;
|
|
154
|
+
const nodes = container.querySelectorAll(selectors);
|
|
155
|
+
nodes.forEach(elem => helpers.default.removeCssClass(elem, cssClass));
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Removes all child nodes from `container`, leaving it empty. No-op when `container` is null/undefined.
|
|
159
|
+
* @param container Element to clear.
|
|
160
|
+
*/
|
|
161
|
+
function empty(container) {
|
|
162
|
+
if (!container)
|
|
163
|
+
return;
|
|
164
|
+
while (container.hasChildNodes()) {
|
|
165
|
+
if (container.firstChild)
|
|
166
|
+
container.removeChild(container.firstChild);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
exports.addClass = addClass;
|
|
171
|
+
exports.empty = empty;
|
|
172
|
+
exports.getByClass = getByClass;
|
|
173
|
+
exports.getById = getById;
|
|
174
|
+
exports.getByName = getByName;
|
|
175
|
+
exports.getElementByTagName = getElementByTagName;
|
|
176
|
+
exports.getElementsByTagName = getElementsByTagName;
|
|
177
|
+
exports.nextElement = nextElement;
|
|
178
|
+
exports.nextElementByClass = nextElementByClass;
|
|
179
|
+
exports.prevElement = prevElement;
|
|
180
|
+
exports.prevElementByClass = prevElementByClass;
|
|
181
|
+
exports.queryElement = queryElement;
|
|
182
|
+
exports.queryElements = queryElements;
|
|
183
|
+
exports.removeClass = removeClass;
|
|
184
|
+
//# sourceMappingURL=dom.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dom.js","sources":["../../../../source/dom/dom.ts"],"sourcesContent":[null],"names":["helpers"],"mappings":";;;;AAGA;;;;AAIG;AACH,SAAS,OAAO,CAA6C,EAAU,EAAA;AACtE,IAAA,OAAO,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAa;AAC/C;AAEA;;;;;AAKG;AACH,SAAS,UAAU,CAA6C,SAAkB,EAAE,SAAiB,EAAA;IACpG,MAAM,QAAQ,GAAG,SAAS,CAAC,sBAAsB,CAAC,SAAS,CAAC;AAC5D,IAAA,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;AACxB,QAAA,OAAO,IAAI;AACZ,IAAA,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAa;AACpC;AAEA;;;;AAIG;AACH,SAAS,SAAS,CAA6C,IAAY,EAAA;IAC1E,MAAM,QAAQ,GAAG,QAAQ,CAAC,iBAAiB,CAAC,IAAI,CAAC;AACjD,IAAA,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;AACxB,QAAA,OAAO,IAAI;AACZ,IAAA,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAa;AACpC;AAEA;;;;;AAKG;AACH,SAAS,mBAAmB,CAA6C,SAAkB,EAAE,OAAe,EAAA;IAC3G,MAAM,QAAQ,GAAG,SAAS,CAAC,oBAAoB,CAAC,OAAO,CAAC;AACxD,IAAA,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;AACxB,QAAA,OAAO,IAAI;AACZ,IAAA,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAa;AACpC;AAEA;;;;;AAKG;AACH,SAAS,oBAAoB,CAAC,SAAkB,EAAE,OAAe,EAAA;AAChE,IAAA,OAAO,SAAS,CAAC,oBAAoB,CAAC,OAAO,CAAC;AAC/C;AAEA;;;;;AAKG;AACH,SAAS,YAAY,CAA6C,SAAkB,EAAE,KAAa,EAAA;AAClG,IAAA,OAAO,SAAS,CAAC,aAAa,CAAC,KAAK,CAAC;AACtC;AAEA;;;;;AAKG;AACH,SAAS,aAAa,CAA6C,SAAkB,EAAE,KAAa,EAAA;AACnG,IAAA,OAAO,SAAS,CAAC,gBAAgB,CAAC,KAAK,CAAyB;AACjE;AAEA;;;;;AAKG;AACH,SAAS,kBAAkB,CAA6C,OAAgB,EAAE,SAAiB,EAAA;AAC1G,IAAA,IAAI,IAAI,GAAG,OAAO,CAAC,WAAW;IAC9B,OAAO,IAAI,EAAE;AACZ,QAAA,IAAI,IAAI,CAAC,QAAQ,KAAK,IAAI,CAAC,YAAY,IAAI,IAAI,YAAY,WAAW,IAAI,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,SAAS,CAAC;AAC3G,YAAA,OAAO,IAAgB;AAExB,QAAA,IAAI,GAAG,IAAI,CAAC,WAAW;IACxB;AACA,IAAA,OAAO,IAAI;AACZ;AAEA;;;;;AAKG;AACH,SAAS,kBAAkB,CAA6C,OAAgB,EAAE,SAAiB,EAAA;AAC1G,IAAA,IAAI,IAAI,GAAG,OAAO,CAAC,eAAe;IAClC,OAAO,IAAI,EAAE;AACZ,QAAA,IAAI,IAAI,CAAC,QAAQ,KAAK,IAAI,CAAC,YAAY,IAAI,IAAI,YAAY,WAAW,IAAI,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,SAAS,CAAC;AAC3G,YAAA,OAAO,IAAgB;AAExB,QAAA,IAAI,GAAG,IAAI,CAAC,eAAe;IAC5B;AACA,IAAA,OAAO,IAAI;AACZ;AAEA;;;;AAIG;AACH,SAAS,WAAW,CAA6C,OAAgB,EAAA;AAChF,IAAA,IAAI,IAAI,GAAG,OAAO,CAAC,eAAe;IAClC,OAAO,IAAI,EAAE;QACZ,IAAI,IAAI,CAAC,QAAQ,KAAK,IAAI,CAAC,YAAY,IAAI,IAAI,YAAY,WAAW;AACrE,YAAA,OAAO,IAAgB;AAExB,QAAA,IAAI,GAAG,IAAI,CAAC,eAAe;IAC5B;AACA,IAAA,OAAO,IAAI;AACZ;AAEA;;;;AAIG;AACH,SAAS,WAAW,CAA6C,OAAgB,EAAA;AAChF,IAAA,IAAI,IAAI,GAAG,OAAO,CAAC,WAAW;IAC9B,OAAO,IAAI,EAAE;QACZ,IAAI,IAAI,CAAC,QAAQ,KAAK,IAAI,CAAC,YAAY,IAAI,IAAI,YAAY,WAAW;AACrE,YAAA,OAAO,IAAgB;AAExB,QAAA,IAAI,GAAG,IAAI,CAAC,WAAW;IACxB;AACA,IAAA,OAAO,IAAI;AACZ;AAEA;;;;;AAKG;AACH,SAAS,QAAQ,CAAC,SAAqC,EAAE,SAAiB,EAAE,QAAkB,EAAA;AAC7F,IAAA,IAAI,CAAC,SAAS,IAAI,CAAC,QAAQ;QAC1B;IAED,MAAM,KAAK,GAAG,SAAS,CAAC,gBAAgB,CAAC,SAAS,CAAC;AACnD,IAAA,KAAK,CAAC,OAAO,CAAC,IAAI,IAAIA,eAAO,CAAC,WAAW,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AAC3D;AAEA;;;;;AAKG;AACH,SAAS,WAAW,CAAC,SAAqC,EAAE,SAAiB,EAAE,QAAkB,EAAA;AAChG,IAAA,IAAI,CAAC,SAAS,IAAI,CAAC,QAAQ;QAC1B;IAED,MAAM,KAAK,GAAG,SAAS,CAAC,gBAAgB,CAAC,SAAS,CAAC;AACnD,IAAA,KAAK,CAAC,OAAO,CAAC,IAAI,IAAIA,eAAO,CAAC,cAAc,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AAC9D;AAEA;;;AAGG;AACH,SAAS,KAAK,CAAC,SAAqC,EAAA;AACnD,IAAA,IAAI,CAAC,SAAS;QACb;AAED,IAAA,OAAO,SAAS,CAAC,aAAa,EAAE,EAAE;QACjC,IAAI,SAAS,CAAC,UAAU;AACvB,YAAA,SAAS,CAAC,WAAW,CAAC,SAAS,CAAC,UAAU,CAAC;IAC7C;AACD;;;;;;;;;;;;;;;;;"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @internal
|
|
7
|
+
* Adds one or more CSS classes to a single element. A string is split on spaces. No-op when `cssClass` is falsy.
|
|
8
|
+
*/
|
|
9
|
+
const addCssClass = (elem, cssClass) => {
|
|
10
|
+
if (!cssClass)
|
|
11
|
+
return;
|
|
12
|
+
const tokens = (Array.isArray(cssClass) ? cssClass : cssClass.split(' ')).filter(Boolean);
|
|
13
|
+
if (tokens.length)
|
|
14
|
+
elem.classList.add(...tokens);
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* @internal
|
|
18
|
+
* Removes one or more CSS classes from a single element. A string is split on spaces. No-op when `cssClass` is falsy.
|
|
19
|
+
*/
|
|
20
|
+
const removeCssClass = (elem, cssClass) => {
|
|
21
|
+
if (!cssClass)
|
|
22
|
+
return;
|
|
23
|
+
const tokens = (Array.isArray(cssClass) ? cssClass : cssClass.split(' ')).filter(Boolean);
|
|
24
|
+
if (tokens.length)
|
|
25
|
+
elem.classList.remove(...tokens);
|
|
26
|
+
};
|
|
27
|
+
var helpers = {
|
|
28
|
+
addCssClass,
|
|
29
|
+
removeCssClass
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
exports.default = helpers;
|
|
33
|
+
//# sourceMappingURL=helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.js","sources":["../../../../source/dom/helpers.ts"],"sourcesContent":[null],"names":[],"mappings":";;;;AAEA;;;AAGG;AACH,MAAM,WAAW,GAAG,CAAC,IAAa,EAAE,QAAkB,KAAI;AACzD,IAAA,IAAI,CAAC,QAAQ;QACZ;AAED,IAAA,MAAM,MAAM,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC;IACzF,IAAI,MAAM,CAAC,MAAM;QAChB,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC;AAC/B,CAAC;AAED;;;AAGG;AACH,MAAM,cAAc,GAAG,CAAC,IAAa,EAAE,QAAkB,KAAI;AAC5D,IAAA,IAAI,CAAC,QAAQ;QACZ;AAED,IAAA,MAAM,MAAM,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC;IACzF,IAAI,MAAM,CAAC,MAAM;QAChB,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;AAClC,CAAC;AAED,cAAe;IACd,WAAW;IACX;CACA;;;;"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
require('../reactive/effect.js');
|
|
4
|
+
var dom = require('./dom.js');
|
|
5
|
+
var tag = require('./tag.js');
|
|
6
|
+
|
|
7
|
+
/** Collection of DOM helper functions: element queries/traversal ({@link getById}, {@link queryElement}, {@link nextElement}, ...), class manipulation ({@link addClass}, {@link removeClass}), {@link empty}, and element creation via {@link tag}. */
|
|
8
|
+
const DOM = {
|
|
9
|
+
...dom,
|
|
10
|
+
...tag
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
exports.DOM = DOM;
|
|
14
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../../../source/dom/index.ts"],"sourcesContent":[null],"names":["DomHelpers","TagHelpers"],"mappings":";;;;;;AAMA;AACO,MAAM,GAAG,GAAG;AAClB,IAAA,GAAGA,GAAU;AACb,IAAA,GAAGC;;;;;"}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var element = require('../element.js');
|
|
4
|
+
var bind = require('./bind.js');
|
|
5
|
+
var bindEach = require('./bind-each.js');
|
|
6
|
+
var effect = require('../reactive/effect.js');
|
|
7
|
+
var bindingCleanup = require('./binding-cleanup.js');
|
|
8
|
+
var helpers = require('./helpers.js');
|
|
9
|
+
|
|
10
|
+
/** Returns `true` when `value` should be treated as options (or absent options), `false` when it is the first child. */
|
|
11
|
+
const isOptionsArg = (value) => {
|
|
12
|
+
if (value === null || value === undefined)
|
|
13
|
+
return true;
|
|
14
|
+
if (typeof value !== "object")
|
|
15
|
+
return false; // string, number, boolean, function → child
|
|
16
|
+
if (Array.isArray(value))
|
|
17
|
+
return false; // array → children
|
|
18
|
+
if (value instanceof Element || value instanceof element.UIElement || value instanceof bind.Binding || value instanceof bindEach.BindingEach || value instanceof Promise)
|
|
19
|
+
return false;
|
|
20
|
+
return true; // plain object → ElementOptions
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Creates an HTML element, optionally applying options and appending children.
|
|
24
|
+
*
|
|
25
|
+
* The second argument is **options** when it is `null` or a plain {@link ElementOptions} object.
|
|
26
|
+
* It is treated as the **first child** for any {@link TagFirstChild} value — strings, numbers,
|
|
27
|
+
* elements, bindings, arrays, etc. — so `tag("div", "hello")` appends "hello" as HTML text,
|
|
28
|
+
* and `tag("ul", bindEach(...))` works without a leading `null`.
|
|
29
|
+
* To apply a CSS class use `{ class: "name" }` in options.
|
|
30
|
+
*/
|
|
31
|
+
function tag(tagName, optionsOrChild, ...rest) {
|
|
32
|
+
const elem = document.createElement(tagName);
|
|
33
|
+
if (isOptionsArg(optionsOrChild)) {
|
|
34
|
+
applyOptions(elem, optionsOrChild);
|
|
35
|
+
appendChild(elem, rest);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
appendChild(elem, optionsOrChild !== undefined ? [optionsOrChild, ...rest] : rest);
|
|
39
|
+
}
|
|
40
|
+
return elem;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Applies element options to an existing element. A string or array is treated as a {@link CssClass}; otherwise each {@link ElementOptions} key is applied (`id`, `styles`, `class`, `command`, `dataset`, `events`, or a plain attribute). `undefined` values are skipped.
|
|
44
|
+
* @param elem Target element to mutate.
|
|
45
|
+
* @param options Options object, {@link CssClass} shorthand, or `null`/`undefined` for none.
|
|
46
|
+
*/
|
|
47
|
+
const applyOptions = (elem, options) => {
|
|
48
|
+
if (!options)
|
|
49
|
+
return;
|
|
50
|
+
if (typeof options === "string" || Array.isArray(options))
|
|
51
|
+
helpers.default.addCssClass(elem, options);
|
|
52
|
+
else {
|
|
53
|
+
for (const key in options) {
|
|
54
|
+
const value = options[key];
|
|
55
|
+
if (value === undefined)
|
|
56
|
+
continue;
|
|
57
|
+
switch (key) {
|
|
58
|
+
case "id":
|
|
59
|
+
elem.id = value;
|
|
60
|
+
break;
|
|
61
|
+
case "styles": {
|
|
62
|
+
if (value) {
|
|
63
|
+
for (const sKey in value)
|
|
64
|
+
elem.style[sKey] = value[sKey];
|
|
65
|
+
}
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
case "class": {
|
|
69
|
+
helpers.default.addCssClass(elem, value);
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
case "command": {
|
|
73
|
+
elem.dataset["command"] = value;
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
case "dataset": {
|
|
77
|
+
if (value) {
|
|
78
|
+
for (const dataName in value)
|
|
79
|
+
elem.dataset[dataName] = value[dataName];
|
|
80
|
+
}
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
case "events": {
|
|
84
|
+
if (value) {
|
|
85
|
+
for (const eventName in value)
|
|
86
|
+
elem.addEventListener(eventName, value[eventName]);
|
|
87
|
+
}
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
default: {
|
|
91
|
+
if (value === null)
|
|
92
|
+
elem.setAttribute(key, "");
|
|
93
|
+
else if (typeof value === "object")
|
|
94
|
+
elem.setAttribute(key, JSON.stringify(value));
|
|
95
|
+
else
|
|
96
|
+
elem.setAttribute(key, String(value));
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* Appends one or more children to a container, recursively resolving arrays, promises and factory functions. Elements are appended as-is; a {@link UIElement} appends its bound element (or defers until `setElement` binds one); a reactive {@link Binding} renders and live-updates in place; strings/numbers/booleans are inserted as HTML; `null`/`undefined` are ignored.
|
|
105
|
+
* @param container Element to append the children to.
|
|
106
|
+
* @param children Child or children to append. See {@link TagChildrenLike}.
|
|
107
|
+
* @throws Error When a child resolves to an unsupported type.
|
|
108
|
+
*/
|
|
109
|
+
const appendChild = (container, children) => {
|
|
110
|
+
if (children === null || children === undefined)
|
|
111
|
+
return;
|
|
112
|
+
if (children instanceof Array)
|
|
113
|
+
children.forEach(child => appendChild(container, child));
|
|
114
|
+
else if (children instanceof Element)
|
|
115
|
+
container.append(children);
|
|
116
|
+
else if (children instanceof element.UIElement) {
|
|
117
|
+
if (children.element)
|
|
118
|
+
container.append(children.element);
|
|
119
|
+
else {
|
|
120
|
+
// reserve the position now; replace the placeholder once setElement
|
|
121
|
+
// binds the element (after _onRenderElement), keeping child order
|
|
122
|
+
const placeholder = document.createComment("");
|
|
123
|
+
container.append(placeholder);
|
|
124
|
+
children.once("rendered", () => {
|
|
125
|
+
if (children.element)
|
|
126
|
+
placeholder.replaceWith(children.element);
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
else if (children instanceof bind.Binding)
|
|
131
|
+
appendBinding(container, children);
|
|
132
|
+
else if (children instanceof bindEach.BindingEach)
|
|
133
|
+
bindEach.appendBindingEach(container, children);
|
|
134
|
+
else if (children instanceof Promise)
|
|
135
|
+
children.then((child) => appendChild(container, child));
|
|
136
|
+
else {
|
|
137
|
+
const typeName = typeof children;
|
|
138
|
+
let html;
|
|
139
|
+
switch (typeName) {
|
|
140
|
+
case "string":
|
|
141
|
+
html = children;
|
|
142
|
+
break;
|
|
143
|
+
case "number":
|
|
144
|
+
case "boolean":
|
|
145
|
+
html = children.toString();
|
|
146
|
+
break;
|
|
147
|
+
case "function":
|
|
148
|
+
const child = children(container);
|
|
149
|
+
appendChild(container, child);
|
|
150
|
+
return;
|
|
151
|
+
default:
|
|
152
|
+
throw new Error(`Not support child type of ${typeName}.`);
|
|
153
|
+
}
|
|
154
|
+
container.insertAdjacentHTML("beforeend", html);
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
/**
|
|
158
|
+
* Renders a reactive {@link Binding} child and keeps it up to date: a reactive
|
|
159
|
+
* effect re-evaluates the binding and updates the DOM in place — reusing a text
|
|
160
|
+
* node for text values and swapping the node when an element/UIElement is returned.
|
|
161
|
+
*/
|
|
162
|
+
const appendBinding = (container, binding) => {
|
|
163
|
+
let current = document.createTextNode("");
|
|
164
|
+
let textNode = current;
|
|
165
|
+
container.append(current);
|
|
166
|
+
const eff = effect.effect(() => {
|
|
167
|
+
const value = binding.compute();
|
|
168
|
+
if (value instanceof Element || value instanceof element.UIElement) {
|
|
169
|
+
const next = (value instanceof element.UIElement ? value.element : value) ?? document.createComment("");
|
|
170
|
+
current.replaceWith(next);
|
|
171
|
+
current = next;
|
|
172
|
+
textNode = null;
|
|
173
|
+
// deferred UIElement: its element is bound later — swap the placeholder
|
|
174
|
+
// for the real element once setElement raises "rendered"
|
|
175
|
+
if (value instanceof element.UIElement && !value.element) {
|
|
176
|
+
const placeholder = next;
|
|
177
|
+
value.once("rendered", () => {
|
|
178
|
+
// skip if the binding has since re-rendered to a different node
|
|
179
|
+
if (value.element && current === placeholder) {
|
|
180
|
+
placeholder.replaceWith(value.element);
|
|
181
|
+
current = value.element;
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
// null/undefined/false render as empty text
|
|
188
|
+
const text = (value === null || value === undefined || value === false) ? "" : String(value);
|
|
189
|
+
if (textNode && textNode === current) {
|
|
190
|
+
textNode.textContent = text;
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
const next = document.createTextNode(text);
|
|
194
|
+
current.replaceWith(next);
|
|
195
|
+
current = next;
|
|
196
|
+
textNode = next;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
// stop the effect when the rendered node leaves the document
|
|
201
|
+
bindingCleanup.autoDisposeBinding(container, () => current, eff);
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
exports.appendChild = appendChild;
|
|
205
|
+
exports.applyOptions = applyOptions;
|
|
206
|
+
exports.tag = tag;
|
|
207
|
+
//# sourceMappingURL=tag.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tag.js","sources":["../../../../source/dom/tag.ts"],"sourcesContent":[null],"names":["UIElement","Binding","BindingEach","helpers","appendBindingEach","effect","autoDisposeBinding"],"mappings":"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"}
|