Comparing version 1.3.5 to 1.4.0
# Changelog | ||
## 1.4.0- 2021-05-06 | ||
- Refactor h function. Breaking Change: Does not really support SVG anymore. Use html function for this case. | ||
- Performance improvements | ||
- Add non-keyed solution and default to it | ||
## 1.3.5- 2021-05-03 | ||
@@ -4,0 +10,0 @@ |
@@ -19,2 +19,3 @@ // Safari Polyfills | ||
const bindMap = new WeakMap(); // Bind an Element to Data. If the Data is being unset, the DOM Element disappears too. | ||
const tmpSwap = new WeakMap(); // Take over keyToNodeMap if new value is a hydro Proxy. Save old reactivityMap entry here, in case for a swap operation. [if reuseElements] | ||
const onRenderMap = new WeakMap(); // Lifecycle Hook that is being called after rendering | ||
@@ -28,2 +29,3 @@ const onCleanupMap = new WeakMap(); // Lifecycle Hook that is being called when unmount function is being called | ||
let shouldSetReactivity = true; | ||
let viewElements = false; | ||
const reactivityRegex = /\{\{((\s|.)*?)\}\}/; | ||
@@ -177,3 +179,20 @@ const HTML_FIND_INVALID = /<(\/?)(html|head|body)(>|\s.*?>)/g; | ||
const DOM = parser(DOMString); | ||
const root = document.createNodeIterator(DOM, NodeFilter.SHOW_ELEMENT, { | ||
// Delay Elemen iteration and manipulation after the elements have been added to the DOM. | ||
if (viewElements) { | ||
onRender(fillDOM, DOM.firstChild, DOM.firstChild, insertNodes, eventFunctions); | ||
} | ||
else { | ||
fillDOM(DOM, insertNodes, eventFunctions); | ||
} | ||
// Return DocumentFragment | ||
if (DOM.childNodes.length > 1) | ||
return DOM; | ||
// Return Text Node | ||
if (!DOM.firstChild) | ||
return document.createTextNode(""); | ||
// Return Element | ||
return DOM.firstChild; | ||
} | ||
function fillDOM(elem, insertNodes, eventFunctions) { | ||
const root = document.createNodeIterator(elem, NodeFilter.SHOW_ELEMENT, { | ||
acceptNode(element) { | ||
@@ -185,23 +204,18 @@ return element.localName.endsWith("-dummy" /* dummy */) | ||
}); | ||
let elem; | ||
while ((elem = root.nextNode())) { | ||
const tag = elem.localName.replace("-dummy" /* dummy */, ""); | ||
let nextNode; | ||
while ((nextNode = root.nextNode())) { | ||
const tag = nextNode.localName.replace("-dummy" /* dummy */, ""); | ||
const replacement = document.createElement(tag); | ||
//@ts-ignore | ||
replacement.append(...elem.childNodes); | ||
elem.replaceWith(replacement); | ||
replacement.append(...nextNode.childNodes); | ||
nextNode.replaceWith(replacement); | ||
} | ||
// Insert HTML Elements, which were stored in insertNodes | ||
DOM.querySelectorAll("template[id^=lbInsertNodes]").forEach((template) => template.replaceWith(insertNodes.shift())); | ||
elem | ||
.querySelectorAll("template[id^=lbInsertNodes]") | ||
.forEach((template) => template.replaceWith(insertNodes.shift())); | ||
if (shouldSetReactivity) | ||
setReactivity(DOM, eventFunctions); | ||
// Return DocumentFragment | ||
if (DOM.childNodes.length > 1) | ||
return DOM; | ||
// Return Text Node | ||
if (!DOM.firstChild) | ||
return document.createTextNode(""); | ||
// Return Element | ||
return DOM.firstChild; | ||
setReactivity(elem, eventFunctions); | ||
} | ||
/* c8 ignore start */ | ||
function h(name, props, ...children) { | ||
@@ -211,8 +225,14 @@ if (isFunction(name)) | ||
const flatChildren = children | ||
.map((child) => | ||
/* c8 ignore next 1 */ | ||
isObject(child) && !isNode(child) ? Object.values(child) : child) | ||
.map((child) => isObject(child) && !isNode(child) ? Object.values(child) : child) | ||
.flat(); | ||
return html `<${name} ${props || {}}>${flatChildren}</${name}>`; | ||
const elem = document.createElement(name); | ||
for (let i in props) { | ||
//@ts-ignore | ||
i in elem ? (elem[i] = props[i]) : setAttribute(elem, i, props[i]); | ||
} | ||
elem.append(...flatChildren); | ||
onRender(setReactivity, elem, elem); | ||
return elem; | ||
} | ||
/* c8 ignore end */ | ||
function setReactivity(DOM, eventFunctions) { | ||
@@ -569,6 +589,9 @@ // Set events and reactive behaviour(checks for {{ key }} where key is on hydro) | ||
const fn = lifecyleMap.get(node); | ||
/* c8 ignore next 3 */ | ||
if (globalSchedule) { | ||
if (viewElements) { | ||
schedule(fn); | ||
/* c8 ignore next 3 */ | ||
} | ||
else if (globalSchedule) { | ||
window.requestIdleCallback(fn); | ||
} | ||
else { | ||
@@ -948,2 +971,3 @@ fn(); | ||
// Remove item from array | ||
/* c8 ignore next 4 */ | ||
if (!internReset && Array.isArray(receiver)) { | ||
@@ -967,4 +991,5 @@ receiver.splice(key, 1); | ||
let subItem = receiver[key][i]; | ||
/* c8 ignore next 3 */ | ||
if (isObject(subItem) && isProxy(subItem)) { | ||
subItem = null; | ||
receiver[key][i] = null; | ||
} | ||
@@ -1002,5 +1027,7 @@ } | ||
else { | ||
if (Array.isArray(receiver) && | ||
if (!reuseElements && | ||
Array.isArray(receiver) && | ||
receiver.includes(oldVal) && | ||
receiver.includes(val) && | ||
/* c8 ignore start */ | ||
bindMap.has(val)) { | ||
@@ -1023,2 +1050,3 @@ const [elem] = bindMap.get(val); | ||
else { | ||
/* c8 ignore end */ | ||
returnSet = Reflect.set(target, key, val, receiver); | ||
@@ -1039,3 +1067,11 @@ } | ||
if (reactivityMap.has(oldVal)) { | ||
reactivityMap.set(oldVal, reactivityMap.get(val)); | ||
// Store old reactivityMap if it is a swap operation | ||
reuseElements && tmpSwap.set(oldVal, reactivityMap.get(oldVal)); | ||
if (tmpSwap.has(val)) { | ||
reactivityMap.set(oldVal, tmpSwap.get(val)); | ||
tmpSwap.delete(val); | ||
} | ||
else { | ||
reactivityMap.set(oldVal, reactivityMap.get(val)); | ||
} | ||
} | ||
@@ -1050,3 +1086,3 @@ } | ||
// If oldVal is a Proxy - clean it | ||
schedule(cleanProxy, oldVal); | ||
!reuseElements && cleanProxy(oldVal); | ||
return returnSet; | ||
@@ -1116,2 +1152,3 @@ }, | ||
} | ||
/* c8 ignore next 3 */ | ||
} | ||
@@ -1136,2 +1173,3 @@ else { | ||
reactivityMap.delete(proxy); | ||
/* c8 ignore next 4 */ | ||
if (bindMap.has(proxy)) { | ||
@@ -1262,31 +1300,40 @@ bindMap.get(proxy).forEach(removeElement); | ||
} | ||
function template(elem, placeholders, events) { | ||
let wrapper = elem.content.cloneNode(true).firstChild; | ||
let innerHTML = wrapper.innerHTML; | ||
for (const [key, value] of Object.entries(placeholders)) { | ||
innerHTML = innerHTML.replace(key, String(value)); | ||
} | ||
wrapper.innerHTML = innerHTML; | ||
setReactivity(wrapper.firstChild, events); | ||
return wrapper.firstChild; | ||
} | ||
function view(root, data, renderFunction) { | ||
getValue(data).forEach((item, i) => { | ||
const elem = renderFunction(item, i); | ||
$(root).appendChild(elem); | ||
}); | ||
viewElements = true; | ||
const rootElem = $(root); | ||
const elements = getValue(data).map(renderFunction); | ||
rootElem.append(...elements); | ||
elements.forEach((elem) => runLifecyle(elem, onRenderMap)); | ||
viewElements = false; | ||
observe(data, (newData, oldData) => { | ||
const rootElem = $(root); | ||
if (!newData?.length || oldData?.[0] !== newData?.[0]) { | ||
rootElem.innerHTML = ""; | ||
/* c8 ignore start */ | ||
viewElements = true; | ||
// Reset or re-use | ||
if (!newData?.length || | ||
(!reuseElements && newData?.length === oldData?.length)) { | ||
rootElem.textContent = ""; | ||
} | ||
else if (reuseElements) { | ||
for (let i = 0; i < oldData?.length && newData?.length; i++) { | ||
oldData[i].id = newData[i].id; | ||
oldData[i].label = newData[i].label; | ||
newData[i] = oldData[i]; | ||
} | ||
} | ||
// Add to existing | ||
if (oldData?.length && newData?.length > oldData?.length) { | ||
rootElem.append(...newData.slice(oldData.length).map(renderFunction)); | ||
const length = oldData.length; | ||
const slicedData = newData.slice(length); | ||
const newElements = slicedData.map((item, i) => renderFunction(item, i + length)); | ||
rootElem.append(...newElements); | ||
newElements.forEach((elem) => runLifecyle(elem, onRenderMap)); | ||
} | ||
else { | ||
newData?.forEach((item, i) => { | ||
const elem = renderFunction(item, i); | ||
rootElem.insertBefore(elem, null); | ||
}); | ||
// Add new | ||
else if (oldData?.length === 0 || (!reuseElements && newData?.length)) { | ||
const elements = newData.map(renderFunction); | ||
rootElem.append(...elements); | ||
elements.forEach((elem) => runLifecyle(elem, onRenderMap)); | ||
} | ||
viewElements = false; | ||
/* c8 ignore end */ | ||
}); | ||
@@ -1300,2 +1347,2 @@ } | ||
}; | ||
module.exports = { render, html, h, hydro, setGlobalSchedule, setReuseElements, setInsertDiffing, setShouldSetReactivity, reactive, unset, setAsyncUpdate, unobserve, observe, ternary, emit, watchEffect, internals, getValue, onRender, onCleanup, setReactivity, $, $$, template, view, }; | ||
module.exports = { render, html, h, hydro, setGlobalSchedule, setReuseElements, setInsertDiffing, setShouldSetReactivity, reactive, unset, setAsyncUpdate, unobserve, observe, ternary, emit, watchEffect, internals, getValue, onRender, onCleanup, setReactivity, $, $$, view, }; |
@@ -67,3 +67,2 @@ declare global { | ||
declare function onCleanup(fn: Function, elem: ReturnType<typeof html>, ...args: Array<any>): void; | ||
declare function template(elem: HTMLTemplateElement, placeholders: Record<string, any>, events: Record<string, any>): Element; | ||
declare function view(root: string, data: reactiveObject<Array<any>>, renderFunction: (value: any, index: number) => Node): void; | ||
@@ -84,2 +83,2 @@ declare const hydro: hydroObject; | ||
}; | ||
export { render, html, h, hydro, setGlobalSchedule, setReuseElements, setInsertDiffing, setShouldSetReactivity, reactive, unset, setAsyncUpdate, unobserve, observe, ternary, emit, watchEffect, internals, getValue, onRender, onCleanup, setReactivity, $, $$, template, view, }; | ||
export { render, html, h, hydro, setGlobalSchedule, setReuseElements, setInsertDiffing, setShouldSetReactivity, reactive, unset, setAsyncUpdate, unobserve, observe, ternary, emit, watchEffect, internals, getValue, onRender, onCleanup, setReactivity, $, $$, view, }; |
@@ -19,2 +19,3 @@ // Safari Polyfills | ||
const bindMap = new WeakMap(); // Bind an Element to Data. If the Data is being unset, the DOM Element disappears too. | ||
const tmpSwap = new WeakMap(); // Take over keyToNodeMap if new value is a hydro Proxy. Save old reactivityMap entry here, in case for a swap operation. [if reuseElements] | ||
const onRenderMap = new WeakMap(); // Lifecycle Hook that is being called after rendering | ||
@@ -28,2 +29,3 @@ const onCleanupMap = new WeakMap(); // Lifecycle Hook that is being called when unmount function is being called | ||
let shouldSetReactivity = true; | ||
let viewElements = false; | ||
const reactivityRegex = /\{\{((\s|.)*?)\}\}/; | ||
@@ -177,3 +179,20 @@ const HTML_FIND_INVALID = /<(\/?)(html|head|body)(>|\s.*?>)/g; | ||
const DOM = parser(DOMString); | ||
const root = document.createNodeIterator(DOM, NodeFilter.SHOW_ELEMENT, { | ||
// Delay Elemen iteration and manipulation after the elements have been added to the DOM. | ||
if (viewElements) { | ||
onRender(fillDOM, DOM.firstChild, DOM.firstChild, insertNodes, eventFunctions); | ||
} | ||
else { | ||
fillDOM(DOM, insertNodes, eventFunctions); | ||
} | ||
// Return DocumentFragment | ||
if (DOM.childNodes.length > 1) | ||
return DOM; | ||
// Return Text Node | ||
if (!DOM.firstChild) | ||
return document.createTextNode(""); | ||
// Return Element | ||
return DOM.firstChild; | ||
} | ||
function fillDOM(elem, insertNodes, eventFunctions) { | ||
const root = document.createNodeIterator(elem, NodeFilter.SHOW_ELEMENT, { | ||
acceptNode(element) { | ||
@@ -185,23 +204,18 @@ return element.localName.endsWith("-dummy" /* dummy */) | ||
}); | ||
let elem; | ||
while ((elem = root.nextNode())) { | ||
const tag = elem.localName.replace("-dummy" /* dummy */, ""); | ||
let nextNode; | ||
while ((nextNode = root.nextNode())) { | ||
const tag = nextNode.localName.replace("-dummy" /* dummy */, ""); | ||
const replacement = document.createElement(tag); | ||
//@ts-ignore | ||
replacement.append(...elem.childNodes); | ||
elem.replaceWith(replacement); | ||
replacement.append(...nextNode.childNodes); | ||
nextNode.replaceWith(replacement); | ||
} | ||
// Insert HTML Elements, which were stored in insertNodes | ||
DOM.querySelectorAll("template[id^=lbInsertNodes]").forEach((template) => template.replaceWith(insertNodes.shift())); | ||
elem | ||
.querySelectorAll("template[id^=lbInsertNodes]") | ||
.forEach((template) => template.replaceWith(insertNodes.shift())); | ||
if (shouldSetReactivity) | ||
setReactivity(DOM, eventFunctions); | ||
// Return DocumentFragment | ||
if (DOM.childNodes.length > 1) | ||
return DOM; | ||
// Return Text Node | ||
if (!DOM.firstChild) | ||
return document.createTextNode(""); | ||
// Return Element | ||
return DOM.firstChild; | ||
setReactivity(elem, eventFunctions); | ||
} | ||
/* c8 ignore start */ | ||
function h(name, props, ...children) { | ||
@@ -211,8 +225,14 @@ if (isFunction(name)) | ||
const flatChildren = children | ||
.map((child) => | ||
/* c8 ignore next 1 */ | ||
isObject(child) && !isNode(child) ? Object.values(child) : child) | ||
.map((child) => isObject(child) && !isNode(child) ? Object.values(child) : child) | ||
.flat(); | ||
return html `<${name} ${props || {}}>${flatChildren}</${name}>`; | ||
const elem = document.createElement(name); | ||
for (let i in props) { | ||
//@ts-ignore | ||
i in elem ? (elem[i] = props[i]) : setAttribute(elem, i, props[i]); | ||
} | ||
elem.append(...flatChildren); | ||
onRender(setReactivity, elem, elem); | ||
return elem; | ||
} | ||
/* c8 ignore end */ | ||
function setReactivity(DOM, eventFunctions) { | ||
@@ -569,6 +589,9 @@ // Set events and reactive behaviour(checks for {{ key }} where key is on hydro) | ||
const fn = lifecyleMap.get(node); | ||
/* c8 ignore next 3 */ | ||
if (globalSchedule) { | ||
if (viewElements) { | ||
schedule(fn); | ||
/* c8 ignore next 3 */ | ||
} | ||
else if (globalSchedule) { | ||
window.requestIdleCallback(fn); | ||
} | ||
else { | ||
@@ -948,2 +971,3 @@ fn(); | ||
// Remove item from array | ||
/* c8 ignore next 4 */ | ||
if (!internReset && Array.isArray(receiver)) { | ||
@@ -967,4 +991,5 @@ receiver.splice(key, 1); | ||
let subItem = receiver[key][i]; | ||
/* c8 ignore next 3 */ | ||
if (isObject(subItem) && isProxy(subItem)) { | ||
subItem = null; | ||
receiver[key][i] = null; | ||
} | ||
@@ -1002,5 +1027,7 @@ } | ||
else { | ||
if (Array.isArray(receiver) && | ||
if (!reuseElements && | ||
Array.isArray(receiver) && | ||
receiver.includes(oldVal) && | ||
receiver.includes(val) && | ||
/* c8 ignore start */ | ||
bindMap.has(val)) { | ||
@@ -1023,2 +1050,3 @@ const [elem] = bindMap.get(val); | ||
else { | ||
/* c8 ignore end */ | ||
returnSet = Reflect.set(target, key, val, receiver); | ||
@@ -1039,3 +1067,11 @@ } | ||
if (reactivityMap.has(oldVal)) { | ||
reactivityMap.set(oldVal, reactivityMap.get(val)); | ||
// Store old reactivityMap if it is a swap operation | ||
reuseElements && tmpSwap.set(oldVal, reactivityMap.get(oldVal)); | ||
if (tmpSwap.has(val)) { | ||
reactivityMap.set(oldVal, tmpSwap.get(val)); | ||
tmpSwap.delete(val); | ||
} | ||
else { | ||
reactivityMap.set(oldVal, reactivityMap.get(val)); | ||
} | ||
} | ||
@@ -1050,3 +1086,3 @@ } | ||
// If oldVal is a Proxy - clean it | ||
schedule(cleanProxy, oldVal); | ||
!reuseElements && cleanProxy(oldVal); | ||
return returnSet; | ||
@@ -1116,2 +1152,3 @@ }, | ||
} | ||
/* c8 ignore next 3 */ | ||
} | ||
@@ -1136,2 +1173,3 @@ else { | ||
reactivityMap.delete(proxy); | ||
/* c8 ignore next 4 */ | ||
if (bindMap.has(proxy)) { | ||
@@ -1262,31 +1300,40 @@ bindMap.get(proxy).forEach(removeElement); | ||
} | ||
function template(elem, placeholders, events) { | ||
let wrapper = elem.content.cloneNode(true).firstChild; | ||
let innerHTML = wrapper.innerHTML; | ||
for (const [key, value] of Object.entries(placeholders)) { | ||
innerHTML = innerHTML.replace(key, String(value)); | ||
} | ||
wrapper.innerHTML = innerHTML; | ||
setReactivity(wrapper.firstChild, events); | ||
return wrapper.firstChild; | ||
} | ||
function view(root, data, renderFunction) { | ||
getValue(data).forEach((item, i) => { | ||
const elem = renderFunction(item, i); | ||
$(root).appendChild(elem); | ||
}); | ||
viewElements = true; | ||
const rootElem = $(root); | ||
const elements = getValue(data).map(renderFunction); | ||
rootElem.append(...elements); | ||
elements.forEach((elem) => runLifecyle(elem, onRenderMap)); | ||
viewElements = false; | ||
observe(data, (newData, oldData) => { | ||
const rootElem = $(root); | ||
if (!newData?.length || oldData?.[0] !== newData?.[0]) { | ||
rootElem.innerHTML = ""; | ||
/* c8 ignore start */ | ||
viewElements = true; | ||
// Reset or re-use | ||
if (!newData?.length || | ||
(!reuseElements && newData?.length === oldData?.length)) { | ||
rootElem.textContent = ""; | ||
} | ||
else if (reuseElements) { | ||
for (let i = 0; i < oldData?.length && newData?.length; i++) { | ||
oldData[i].id = newData[i].id; | ||
oldData[i].label = newData[i].label; | ||
newData[i] = oldData[i]; | ||
} | ||
} | ||
// Add to existing | ||
if (oldData?.length && newData?.length > oldData?.length) { | ||
rootElem.append(...newData.slice(oldData.length).map(renderFunction)); | ||
const length = oldData.length; | ||
const slicedData = newData.slice(length); | ||
const newElements = slicedData.map((item, i) => renderFunction(item, i + length)); | ||
rootElem.append(...newElements); | ||
newElements.forEach((elem) => runLifecyle(elem, onRenderMap)); | ||
} | ||
else { | ||
newData?.forEach((item, i) => { | ||
const elem = renderFunction(item, i); | ||
rootElem.insertBefore(elem, null); | ||
}); | ||
// Add new | ||
else if (oldData?.length === 0 || (!reuseElements && newData?.length)) { | ||
const elements = newData.map(renderFunction); | ||
rootElem.append(...elements); | ||
elements.forEach((elem) => runLifecyle(elem, onRenderMap)); | ||
} | ||
viewElements = false; | ||
/* c8 ignore end */ | ||
}); | ||
@@ -1300,2 +1347,2 @@ } | ||
}; | ||
export { render, html, h, hydro, setGlobalSchedule, setReuseElements, setInsertDiffing, setShouldSetReactivity, reactive, unset, setAsyncUpdate, unobserve, observe, ternary, emit, watchEffect, internals, getValue, onRender, onCleanup, setReactivity, $, $$, template, view, }; | ||
export { render, html, h, hydro, setGlobalSchedule, setReuseElements, setInsertDiffing, setShouldSetReactivity, reactive, unset, setAsyncUpdate, unobserve, observe, ternary, emit, watchEffect, internals, getValue, onRender, onCleanup, setReactivity, $, $$, view, }; |
{ | ||
"name": "hydro-js", | ||
"version": "1.3.5", | ||
"version": "1.4.0", | ||
"description": "A lightweight reactive library", | ||
@@ -33,3 +33,3 @@ "type": "module", | ||
"@types/concurrently": "^6.0.1", | ||
"@web/test-runner": "^0.13.3", | ||
"@web/test-runner": "^0.13.4", | ||
"@web/test-runner-playwright": "^0.8.5", | ||
@@ -36,0 +36,0 @@ "concurrently": "^6.0.2", |
@@ -1,6 +0,6 @@ | ||
<img align="right" alt="97% Coverage" src="coverage.svg"> | ||
<img align="right" alt="100% Coverage" src="coverage.svg"> | ||
# hydro-js | ||
> A lightweight (~4.5K <em>compressed</em>) reactive UI library via template literal tags.<br> Support in all modern Browsers. | ||
> A lightweight (below 5K <em>compressed</em>) reactive UI library via template literal tags.<br> Support in all modern Browsers. | ||
@@ -300,2 +300,19 @@ ## Installation | ||
### view | ||
Render the elements whenever the data changes. It will handle the operation for deletion, addition, swapping etc. This defaults to a non-keyed solution but it can be changed by calling `setReuseElements` with false. | ||
args: | ||
- root: `string` (CSS selector)<br> | ||
- data: `ReturnType<typeof reactive>`<br> | ||
- renderFunction: `function`, args: item: `any`, i: `number`<br> | ||
#### Example | ||
```js | ||
const data = reactive([{ id: 4, label: "Red Onions" }]) | ||
view('.table', data, (item, i) => <tr>Reactive: {data[i].id}, Non-reactive: {item.id}<tr>) | ||
``` | ||
### emit | ||
@@ -302,0 +319,0 @@ |
@@ -101,2 +101,3 @@ declare const window: any; | ||
const bindMap = new WeakMap<hydroObject, Array<Element>>(); // Bind an Element to Data. If the Data is being unset, the DOM Element disappears too. | ||
const tmpSwap = new WeakMap<hydroObject, keyToNodeMap>(); // Take over keyToNodeMap if new value is a hydro Proxy. Save old reactivityMap entry here, in case for a swap operation. [if reuseElements] | ||
const onRenderMap = new WeakMap<ReturnType<typeof html>, Function>(); // Lifecycle Hook that is being called after rendering | ||
@@ -111,2 +112,3 @@ const onCleanupMap = new WeakMap<ReturnType<typeof html>, Function>(); // Lifecycle Hook that is being called when unmount function is being called | ||
let shouldSetReactivity = true; | ||
let viewElements = false; | ||
@@ -285,3 +287,30 @@ const reactivityRegex = /\{\{((\s|.)*?)\}\}/; | ||
const root = document.createNodeIterator(DOM, NodeFilter.SHOW_ELEMENT, { | ||
// Delay Elemen iteration and manipulation after the elements have been added to the DOM. | ||
if (viewElements) { | ||
onRender( | ||
fillDOM, | ||
DOM.firstChild as Element, | ||
DOM.firstChild, | ||
insertNodes, | ||
eventFunctions | ||
); | ||
} else { | ||
fillDOM(DOM, insertNodes, eventFunctions); | ||
} | ||
// Return DocumentFragment | ||
if (DOM.childNodes.length > 1) return DOM; | ||
// Return Text Node | ||
if (!DOM.firstChild) return document.createTextNode(""); | ||
// Return Element | ||
return DOM.firstChild as Element; | ||
} | ||
function fillDOM( | ||
elem: DocumentFragment | Element, | ||
insertNodes: Node[], | ||
eventFunctions: eventFunctions | ||
) { | ||
const root = document.createNodeIterator(elem, NodeFilter.SHOW_ELEMENT, { | ||
acceptNode(element: Element) { | ||
@@ -293,28 +322,20 @@ return element.localName.endsWith(Placeholder.dummy) | ||
}); | ||
let elem; | ||
while ((elem = root.nextNode())) { | ||
const tag = (elem as Element).localName.replace(Placeholder.dummy, ""); | ||
let nextNode; | ||
while ((nextNode = root.nextNode())) { | ||
const tag = (nextNode as Element).localName.replace(Placeholder.dummy, ""); | ||
const replacement = document.createElement(tag); | ||
//@ts-ignore | ||
replacement.append(...(elem as Element).childNodes); | ||
(elem as Element).replaceWith(replacement); | ||
replacement.append(...(nextNode as Element).childNodes); | ||
(nextNode as Element).replaceWith(replacement); | ||
} | ||
// Insert HTML Elements, which were stored in insertNodes | ||
DOM.querySelectorAll("template[id^=lbInsertNodes]").forEach((template) => | ||
template.replaceWith(insertNodes.shift()!) | ||
); | ||
elem | ||
.querySelectorAll("template[id^=lbInsertNodes]") | ||
.forEach((template) => template.replaceWith(insertNodes.shift()!)); | ||
if (shouldSetReactivity) setReactivity(DOM, eventFunctions); | ||
// Return DocumentFragment | ||
if (DOM.childNodes.length > 1) return DOM; | ||
// Return Text Node | ||
if (!DOM.firstChild) return document.createTextNode(""); | ||
// Return Element | ||
return DOM.firstChild as Element; | ||
if (shouldSetReactivity) setReactivity(elem, eventFunctions); | ||
} | ||
/* c8 ignore start */ | ||
function h( | ||
@@ -328,9 +349,17 @@ name: string | ((...args: any[]) => ReturnType<typeof h>), | ||
const flatChildren = children | ||
.map((child) => | ||
/* c8 ignore next 1 */ | ||
.map((child: any) => | ||
isObject(child) && !isNode(child as Node) ? Object.values(child) : child | ||
) | ||
.flat(); | ||
return html`<${name} ${props || {}}>${flatChildren}</${name}>`; | ||
const elem = document.createElement(name); | ||
for (let i in props) { | ||
//@ts-ignore | ||
i in elem ? (elem[i] = props[i]) : setAttribute(elem, i, props[i]); | ||
} | ||
elem.append(...flatChildren); | ||
onRender(setReactivity, elem, elem); | ||
return elem; | ||
} | ||
/* c8 ignore end */ | ||
function setReactivity(DOM: Node, eventFunctions?: eventFunctions) { | ||
@@ -776,5 +805,7 @@ // Set events and reactive behaviour(checks for {{ key }} where key is on hydro) | ||
/* c8 ignore next 3 */ | ||
if (globalSchedule) { | ||
if (viewElements) { | ||
schedule(fn); | ||
/* c8 ignore next 3 */ | ||
} else if (globalSchedule) { | ||
window.requestIdleCallback(fn); | ||
} else { | ||
@@ -1226,2 +1257,3 @@ fn(); | ||
// Remove item from array | ||
/* c8 ignore next 4 */ | ||
if (!internReset && Array.isArray(receiver)) { | ||
@@ -1249,4 +1281,5 @@ receiver.splice((key as unknown) as number, 1); | ||
let subItem = receiver[key][i]; | ||
/* c8 ignore next 3 */ | ||
if (isObject(subItem) && isProxy(subItem)) { | ||
subItem = null; | ||
receiver[key][i] = null; | ||
} | ||
@@ -1284,5 +1317,7 @@ } | ||
if ( | ||
!reuseElements && | ||
Array.isArray(receiver) && | ||
receiver.includes(oldVal) && | ||
receiver.includes(val) && | ||
/* c8 ignore start */ | ||
bindMap.has(val) | ||
@@ -1308,2 +1343,3 @@ ) { | ||
} else { | ||
/* c8 ignore end */ | ||
returnSet = Reflect.set(target, key, val, receiver); | ||
@@ -1326,3 +1362,11 @@ } | ||
if (reactivityMap.has(oldVal)) { | ||
reactivityMap.set(oldVal, reactivityMap.get(val)!); | ||
// Store old reactivityMap if it is a swap operation | ||
reuseElements && tmpSwap.set(oldVal, reactivityMap.get(oldVal)!); | ||
if (tmpSwap.has(val)) { | ||
reactivityMap.set(oldVal, tmpSwap.get(val)!); | ||
tmpSwap.delete(val); | ||
} else { | ||
reactivityMap.set(oldVal, reactivityMap.get(val)!); | ||
} | ||
} | ||
@@ -1339,3 +1383,3 @@ } | ||
// If oldVal is a Proxy - clean it | ||
schedule(cleanProxy, oldVal); | ||
!reuseElements && cleanProxy(oldVal); | ||
@@ -1408,2 +1452,3 @@ return returnSet; | ||
} | ||
/* c8 ignore next 3 */ | ||
} else { | ||
@@ -1429,2 +1474,3 @@ map.clear(); | ||
reactivityMap.delete(proxy); | ||
/* c8 ignore next 4 */ | ||
if (bindMap.has(proxy)) { | ||
@@ -1566,16 +1612,2 @@ bindMap.get(proxy)!.forEach(removeElement); | ||
} | ||
function template( | ||
elem: HTMLTemplateElement, | ||
placeholders: Record<string, any>, | ||
events: Record<string, any> | ||
) { | ||
let wrapper = elem.content.cloneNode(true).firstChild as HTMLElement; | ||
let innerHTML = wrapper.innerHTML; | ||
for (const [key, value] of Object.entries(placeholders)) { | ||
innerHTML = innerHTML.replace(key, String(value)); | ||
} | ||
wrapper.innerHTML = innerHTML; | ||
setReactivity(wrapper.firstChild!, events); | ||
return wrapper.firstChild as Element; | ||
} | ||
function view( | ||
@@ -1586,21 +1618,45 @@ root: string, | ||
) { | ||
getValue(data).forEach((item: any, i: number) => { | ||
const elem = renderFunction(item, i); | ||
$(root)!.appendChild(elem); | ||
}); | ||
viewElements = true; | ||
const rootElem = $(root)!; | ||
const elements = getValue(data).map(renderFunction); | ||
rootElem.append(...elements); | ||
elements.forEach((elem) => runLifecyle(elem as Element, onRenderMap)); | ||
viewElements = false; | ||
observe(data, (newData: typeof data, oldData: typeof data) => { | ||
/* c8 ignore start */ | ||
viewElements = true; | ||
observe(data, (newData: Array<any>, oldData: Array<any>) => { | ||
const rootElem = $(root)!; | ||
if (!newData?.length || oldData?.[0] !== newData?.[0]) { | ||
rootElem.innerHTML = ""; | ||
// Reset or re-use | ||
if ( | ||
!newData?.length || | ||
(!reuseElements && newData?.length === oldData?.length) | ||
) { | ||
rootElem.textContent = ""; | ||
} else if (reuseElements) { | ||
for (let i = 0; i < oldData?.length && newData?.length; i++) { | ||
oldData[i].id = newData[i].id; | ||
oldData[i].label = newData[i].label; | ||
newData[i] = oldData[i]; | ||
} | ||
} | ||
// Add to existing | ||
if (oldData?.length && newData?.length > oldData?.length) { | ||
rootElem.append(...newData.slice(oldData.length).map(renderFunction)); | ||
} else { | ||
newData?.forEach((item, i) => { | ||
const elem = renderFunction(item, i); | ||
rootElem.insertBefore(elem, null); | ||
}); | ||
const length = oldData.length; | ||
const slicedData = newData.slice(length); | ||
const newElements = slicedData.map((item, i) => | ||
renderFunction(item, i + length) | ||
); | ||
rootElem.append(...newElements); | ||
newElements.forEach((elem) => runLifecyle(elem as Element, onRenderMap)); | ||
} | ||
// Add new | ||
else if (oldData?.length === 0 || (!reuseElements && newData?.length)) { | ||
const elements = newData.map(renderFunction); | ||
rootElem.append(...elements); | ||
elements.forEach((elem) => runLifecyle(elem as Element, onRenderMap)); | ||
} | ||
viewElements = false; | ||
/* c8 ignore end */ | ||
}); | ||
@@ -1640,4 +1696,3 @@ } | ||
$$, | ||
template, | ||
view, | ||
}; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
327116
6331
387