From cbcad943d50c60775584c012e04c1f8923580e9a Mon Sep 17 00:00:00 2001 From: Frederic Heem Date: Fri, 15 Nov 2024 14:30:44 -0300 Subject: [PATCH] can return an array of elements --- README.md | 1 + bau/bau.d.ts | 2 +- bau/bau.js | 57 ++++++++++++++++++++-------- bau/examples/bau-ts-test/src/main.ts | 28 +++++++++++++- 4 files changed, 70 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 7e8493be..6784b2c9 100644 --- a/README.md +++ b/README.md @@ -110,5 +110,6 @@ const bau = Bau(); const { div } = bau.tags; ``` +- Bau reactive functions can return an array of elements, the equivalent of a React Fragment. - Bau promotes only one paradigm: views derive from the state. Van could mix paradigms, imperative and state derived view. The imperative way is when your code directly maniplates the DOM, in the same way as vanilla js and jquery. This style of programming is error prone, therefore, preventing its use makes bau _hard to misuse_ - Bau supports undefined or null attribute, see [issue 39](https://github.com/vanjs-org/van/pull/39) diff --git a/bau/bau.d.ts b/bau/bau.d.ts index d4e5a97b..9f679a59 100644 --- a/bau/bau.d.ts +++ b/bau/bau.d.ts @@ -119,7 +119,7 @@ export type PropsAll = export type BindElementFunc = (input?: { element: HTMLElement; -}) => HTMLElement | Primitive; +}) => HTMLElement | Primitive | (HTMLElement | Primitive)[]; export type ChildDom = | Primitive diff --git a/bau/bau.js b/bau/bau.js index f07766cc..a24c888c 100644 --- a/bau/bau.js +++ b/bau/bau.js @@ -4,6 +4,7 @@ let isFunction = (obj) => getType(obj) == "Function"; let isArrayOrObject = (obj) => ["Object", "Array"].includes(getType(obj)); let protoOf = Object.getPrototypeOf; let toVal = (state) => (isState(state) ? state.val : state); +let toArray = (el) => (Array.isArray(el) ? el : [el]); let isState = (state) => state?.__isState; let METHODS = ["splice", "push", "pop", "shift", "unshift", "sort", "reverse"]; @@ -37,7 +38,10 @@ export default function Bau(input) { if (!_debounce) { _debounce = _window.requestAnimationFrame(() => { stateSet.forEach((state) => { - state.bindings = state.bindings.filter((b) => b.element?.isConnected); + state.bindings = state.bindings.filter( + ({ element }) => + (Array.isArray(element) ? element[0] : element)?.isConnected + ); !state.bindings.length && !state.computed && stateSet.delete(state); }); _debounce = undefined; @@ -68,7 +72,21 @@ export default function Bau(input) { }) : render({ element, renderItem })(...deps.map(toVal)); if (newElement !== element) { - element.replaceWith((binding.element = toDom(newElement))); + let newEls = toArray((binding.element = toDom(newElement))); + let oldEls = toArray(element); + let i = 0; + for (; i < oldEls.length && i < newEls.length; i++) { + oldEls[i].replaceWith(toDom(newEls[i])); + } + let newI = i; + while (newEls.length > newI) { + newEls[newI - 1].after(newEls[newI]); + newI++; + } + while (oldEls.length > i) { + oldEls[i].remove(); + i++; + } } } } @@ -205,6 +223,8 @@ export default function Bau(input) { return spanEl; } else if (v.nodeType) { return v; + } else if (Array.isArray(v)) { + return v.map(toDom); } else { return document.createTextNode(v); } @@ -230,22 +250,29 @@ export default function Bau(input) { } }; - let add = (element, ...children) => { - if (children.length) { - let childrenDom = []; - for (let child of children.flat(Infinity)) - child != null && - childrenDom.push( - isState(child) - ? bind({ deps: [child], render: () => (v) => v }) - : isFunction(child) - ? bindInferred({ renderInferred: child }) - : toDom(child) - ); - element.append(...childrenDom); + let _add = (childrenDom, children = []) => { + for (let child of children) { + if (Array.isArray(child)) { + _add(childrenDom, child); + } else if (child != null) { + const newChild = isState(child) + ? bind({ deps: [child], render: () => (v) => v }) + : isFunction(child) + ? bindInferred({ renderInferred: child }) + : toDom(child); + Array.isArray(newChild) + ? childrenDom.push(...newChild) + : childrenDom.push(newChild); + } } }; + let add = (element, ...children) => { + let childrenDom = []; + _add(childrenDom, children); + element.append(...childrenDom); + }; + let isSettablePropCache = {}; let getPropDescriptor = (proto, key) => diff --git a/bau/examples/bau-ts-test/src/main.ts b/bau/examples/bau-ts-test/src/main.ts index 8ea1490b..796f8e26 100644 --- a/bau/examples/bau-ts-test/src/main.ts +++ b/bau/examples/bau-ts-test/src/main.ts @@ -15,6 +15,7 @@ const { span, input, h1, + h2, p, article, label, @@ -514,6 +515,29 @@ const TestDeriveText = () => { ) ); }; +const TestDeriveReturnArray = () => { + const showState = bau.state(true); + + return article( + h1("Derive returns an array of element"), + button( + { + onclick: () => (showState.val = !showState.val), + }, + "TOOGLE" + ), + p("First element object, second is an array"), + div(() => (showState.val ? div("1") : [div("3"), div("4"), "Text node"])), + p("First element is an array, second is a smaller array"), + div(() => + showState.val ? [div("A"), div("B"), "Text node"] : ["Other Text node"] + ), + p("First element is an array, second is a object"), + div(() => + showState.val ? [div("A"), div("B"), "Text node"] : "Other Text node" + ) + ); +}; const TestButtonClickInline = () => { return section( @@ -823,10 +847,10 @@ const App = ({}) => { ), section( h1("Derive"), - // TestDerived(), TestDerivedSideEffect(), - TestDeriveText() + TestDeriveText(), + TestDeriveReturnArray() ), section(