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-ui/alert/alert.js b/bau-ui/alert/alert.js index 378f7b43..b936e14b 100644 --- a/bau-ui/alert/alert.js +++ b/bau-ui/alert/alert.js @@ -92,7 +92,7 @@ export default function (context, options = {}) { onRemove, ...props }, - ...children + children, ] = toPropsAndChildren(args); return div( @@ -111,7 +111,7 @@ export default function (context, options = {}) { role: "alert", }, span({ class: "icon" }, severityMap[color]), - div({ class: "content" }, ...children), + div({ class: "content" }, children), onRemove && CloseIcon({ onclick: onRemove }) ); }; diff --git a/bau-ui/alertStack/alertStack.js b/bau-ui/alertStack/alertStack.js index 60399dee..19cf7ffd 100644 --- a/bau-ui/alertStack/alertStack.js +++ b/bau-ui/alertStack/alertStack.js @@ -45,7 +45,7 @@ export default (context, options = {}) => { } }; - return function AlertStack(props = {}, ...children) { + return function AlertStack(props = {}) { const remove = ({ id }) => { setStatus({ id, status: "removing" }); const idx = messagesState.val.findIndex((message) => message.id === id); diff --git a/bau-ui/autocomplete/autocomplete.js b/bau-ui/autocomplete/autocomplete.js index c845b784..eb043bda 100644 --- a/bau-ui/autocomplete/autocomplete.js +++ b/bau-ui/autocomplete/autocomplete.js @@ -73,7 +73,7 @@ export default function (context, componentOptions = {}) { loading, ...props }, - ...children + children, ] = toPropsAndChildren(args); const Popover = popover(context); diff --git a/bau-ui/avatar/avatar.js b/bau-ui/avatar/avatar.js index 457bb4a3..9d6b54b6 100644 --- a/bau-ui/avatar/avatar.js +++ b/bau-ui/avatar/avatar.js @@ -56,7 +56,7 @@ export default function (context, options = {}) { alt, ...props }, - ...children + children, ] = toPropsAndChildren(args); const Skeleton = skeleton(context, { class: [ diff --git a/bau-ui/badge/badge.js b/bau-ui/badge/badge.js index 5bf9bb3c..95562f84 100644 --- a/bau-ui/badge/badge.js +++ b/bau-ui/badge/badge.js @@ -34,7 +34,7 @@ export default function (context, options = {}) { content, ...props }, - ...children + children, ] = toPropsAndChildren(args); return span( { @@ -42,7 +42,7 @@ export default function (context, options = {}) { class: ["badge", className, options?.class, props?.class], }, span({ class: [color, variant, size] }, content), - ...children + children ); }; } diff --git a/bau-ui/breadcrumbs/breadcrumbs.js b/bau-ui/breadcrumbs/breadcrumbs.js index 1a9d158f..e11ee7a2 100644 --- a/bau-ui/breadcrumbs/breadcrumbs.js +++ b/bau-ui/breadcrumbs/breadcrumbs.js @@ -48,7 +48,7 @@ export default function (context, options = {}) { items, ...props }, - ...children + children, ] = toPropsAndChildren(args); return ul( { diff --git a/bau-ui/button/button.js b/bau-ui/button/button.js index efaa3861..6f4ad1d7 100644 --- a/bau-ui/button/button.js +++ b/bau-ui/button/button.js @@ -91,7 +91,7 @@ export default function (context, options = {}) { href, ...props }, - ...children + children, ] = toPropsAndChildren(args); const tagButton = href ? bau.tags.a : bau.tags.button; diff --git a/bau-ui/buttonGroup/buttonGroup.js b/bau-ui/buttonGroup/buttonGroup.js index fc3ff557..95f8159d 100644 --- a/bau-ui/buttonGroup/buttonGroup.js +++ b/bau-ui/buttonGroup/buttonGroup.js @@ -53,7 +53,7 @@ export default function (context, options = {}) { color = options.color ?? "neutral", ...props }, - ...children + children, ] = toPropsAndChildren(args); return div( @@ -69,7 +69,7 @@ export default function (context, options = {}) { props?.class, ], }, - ...children + children ); }; } diff --git a/bau-ui/calendar/calendar.js b/bau-ui/calendar/calendar.js index 1f844fb6..e94b9913 100644 --- a/bau-ui/calendar/calendar.js +++ b/bau-ui/calendar/calendar.js @@ -33,7 +33,7 @@ export default function (context, options = {}) { color = options.color ?? "neutral", ...props }, - ...children + children, ] = toPropsAndChildren(args); return input( { @@ -49,7 +49,7 @@ export default function (context, options = {}) { props?.class, ], }, - ...children + children ); }; } diff --git a/bau-ui/checkbox/checkbox.js b/bau-ui/checkbox/checkbox.js index d292f66a..011e9355 100644 --- a/bau-ui/checkbox/checkbox.js +++ b/bau-ui/checkbox/checkbox.js @@ -72,7 +72,7 @@ export default function (context, options = {}) { color = options.color ?? "neutral", ...props }, - ...children + children, ] = toPropsAndChildren(args); return input({ diff --git a/bau-ui/chip/chip.js b/bau-ui/chip/chip.js index 0096304b..3ec811b1 100644 --- a/bau-ui/chip/chip.js +++ b/bau-ui/chip/chip.js @@ -38,7 +38,7 @@ export default function (context, options = {}) { onclick, ...props }, - ...children + children, ] = toPropsAndChildren(args); return span( @@ -56,7 +56,7 @@ export default function (context, options = {}) { props.class, ], }, - ...children + children ); }; } diff --git a/bau-ui/divider/divider.js b/bau-ui/divider/divider.js index 9d217b5c..4f6dee17 100644 --- a/bau-ui/divider/divider.js +++ b/bau-ui/divider/divider.js @@ -28,14 +28,14 @@ export default function (context, options = {}) { color = options.color ?? "neutral", ...props }, - ...children + children, ] = toPropsAndChildren(args); return div( { ...props, class: ["divider", size, className, options?.class, props?.class], }, - div({ class: "content" }, ...children) + div({ class: "content" }, children) ); }; } diff --git a/bau-ui/drawer/drawer.js b/bau-ui/drawer/drawer.js index 297b3292..2735f7dc 100644 --- a/bau-ui/drawer/drawer.js +++ b/bau-ui/drawer/drawer.js @@ -48,10 +48,8 @@ export default function (context, options) { `; return function Drawer(...args) { - let [ - { color, variant = "outline", size, openState, ...props }, - ...children - ] = toPropsAndChildren(args); + let [{ color, variant = "outline", size, openState, ...props }, children] = + toPropsAndChildren(args); return div( { class: [className, options?.class, props.class] }, // Overlay diff --git a/bau-ui/dropdownMenu/dropdownMenu.js b/bau-ui/dropdownMenu/dropdownMenu.js index 847011dc..6d4033f2 100644 --- a/bau-ui/dropdownMenu/dropdownMenu.js +++ b/bau-ui/dropdownMenu/dropdownMenu.js @@ -34,7 +34,7 @@ export default function (context, componentOptions = {}) { items, ...props }, - ...children + children, ] = toPropsAndChildren(args); const itemIndexActive = bau.state(0); diff --git a/bau-ui/examples/bau-storybook/src/landingPage.js b/bau-ui/examples/bau-storybook/src/landingPage.js index 628aff56..7e066475 100644 --- a/bau-ui/examples/bau-storybook/src/landingPage.js +++ b/bau-ui/examples/bau-storybook/src/landingPage.js @@ -36,7 +36,7 @@ export default function (context) { gap: 1rem; `, }, - ...children + children ) ); diff --git a/bau-ui/examples/bau-storybook/src/pages/table/tablePagination.examples.ts b/bau-ui/examples/bau-storybook/src/pages/table/tablePagination.examples.ts index 9395ea8e..79129ca9 100644 --- a/bau-ui/examples/bau-storybook/src/pages/table/tablePagination.examples.ts +++ b/bau-ui/examples/bau-storybook/src/pages/table/tablePagination.examples.ts @@ -28,7 +28,7 @@ export default (context: Context) => { } `, }, - ...children + children ); return () => { diff --git a/bau-ui/fileInput/fileInput.js b/bau-ui/fileInput/fileInput.js index 603a972d..f0957f0e 100644 --- a/bau-ui/fileInput/fileInput.js +++ b/bau-ui/fileInput/fileInput.js @@ -39,7 +39,7 @@ export default function (context, options = {}) { `, }; - return function FileInput(props, ...children) { + return function FileInput(props) { const { size = options.size ?? "md", variant = options.variant ?? "outline", diff --git a/bau-ui/form/form.js b/bau-ui/form/form.js index 32d98906..b6fe232c 100644 --- a/bau-ui/form/form.js +++ b/bau-ui/form/form.js @@ -57,7 +57,7 @@ export default function (context, options = {}) { content, ...props }, - ...children + children, ] = toPropsAndChildren(args); return form( { @@ -72,7 +72,7 @@ export default function (context, options = {}) { props?.class, ], }, - ...children + children ); }; } diff --git a/bau-ui/keyValueList/keyValueList.js b/bau-ui/keyValueList/keyValueList.js index fad78bda..3b74bf1e 100644 --- a/bau-ui/keyValueList/keyValueList.js +++ b/bau-ui/keyValueList/keyValueList.js @@ -29,14 +29,14 @@ export default function (context, options = {}) { color = options.color ?? "neutral", ...props }, - ...children + children, ] = toPropsAndChildren(args); return ul( { ...props, class: ["keyValueList", className, options?.class, props?.class], }, - ...children + children ); }; } diff --git a/bau-ui/list/list.js b/bau-ui/list/list.js index 0f427688..2c856443 100644 --- a/bau-ui/list/list.js +++ b/bau-ui/list/list.js @@ -42,7 +42,7 @@ export default function (context, options = {}) { color = options.color ?? "neutral", ...props }, - ...children + children, ] = toPropsAndChildren(args); return ul( { @@ -57,7 +57,7 @@ export default function (context, options = {}) { props?.class.join(" "), ], }, - ...children + children ); }; } diff --git a/bau-ui/loadingButton/loadingButton.js b/bau-ui/loadingButton/loadingButton.js index 5d51c7f2..c661e29c 100644 --- a/bau-ui/loadingButton/loadingButton.js +++ b/bau-ui/loadingButton/loadingButton.js @@ -44,7 +44,7 @@ export default function (context, options = {}) { loading, ...props }, - ...children + children, ] = toPropsAndChildren(args); const Button = button(context); diff --git a/bau-ui/modal/modal.js b/bau-ui/modal/modal.js index f1f7abf2..35235523 100644 --- a/bau-ui/modal/modal.js +++ b/bau-ui/modal/modal.js @@ -94,7 +94,7 @@ export default function (context, options = {}) { color = options.color ?? "neutral", ...props }, - ...children + children, ] = toPropsAndChildren(args); const dialogEl = dialog( diff --git a/bau-ui/multiSelect/multiSelect.js b/bau-ui/multiSelect/multiSelect.js index 222091bb..f22d4756 100644 --- a/bau-ui/multiSelect/multiSelect.js +++ b/bau-ui/multiSelect/multiSelect.js @@ -68,7 +68,7 @@ export default function (context, componentOptions = {}) { loading, ...props }, - ...children + children, ] = toPropsAndChildren(args); const Spinner = spinner(context, { variant, color, size }); diff --git a/bau-ui/paper/paper.js b/bau-ui/paper/paper.js index 3a870799..7a4ed43d 100644 --- a/bau-ui/paper/paper.js +++ b/bau-ui/paper/paper.js @@ -39,14 +39,14 @@ export default function (context, options = {}) { color = options.color ?? "neutral", ...props }, - ...children + children, ] = toPropsAndChildren(args); return div( { ...props, class: ["paper", size, className, options?.class, props?.class], }, - ...children + children ); }; } diff --git a/bau-ui/resizable/resizable.js b/bau-ui/resizable/resizable.js index c6c6152a..57fb0027 100644 --- a/bau-ui/resizable/resizable.js +++ b/bau-ui/resizable/resizable.js @@ -42,7 +42,7 @@ export default function (context, options = {}) { `; function PanelGroup(...args) { - let [props, ...children] = toPropsAndChildren(args); + let [props, children] = toPropsAndChildren(args); return section( { @@ -54,7 +54,7 @@ export default function (context, options = {}) { } function Panel(...args) { - let [props, ...children] = toPropsAndChildren(args); + let [props, children] = toPropsAndChildren(args); return article( { @@ -66,7 +66,7 @@ export default function (context, options = {}) { } function Handle(...args) { - let [props, ...children] = toPropsAndChildren(args); + let [props, children] = toPropsAndChildren(args); // The current position of mouse let _x = 0; diff --git a/bau-ui/select/select.js b/bau-ui/select/select.js index 7219bc49..3b15528b 100644 --- a/bau-ui/select/select.js +++ b/bau-ui/select/select.js @@ -71,7 +71,7 @@ export default function (context, componentOptions = {}) { loading, ...props }, - ...children + children, ] = toPropsAndChildren(args); const Spinner = spinner(context, { variant, color, size }); diff --git a/bau-ui/selectNative/selectNative.js b/bau-ui/selectNative/selectNative.js index 4e94d840..2aa5bef8 100644 --- a/bau-ui/selectNative/selectNative.js +++ b/bau-ui/selectNative/selectNative.js @@ -24,7 +24,7 @@ export default function (context, componentOptions = {}) { color = componentOptions.color ?? "neutral", ...props }, - ...children + children, ] = toPropsAndChildren(args); return select( diff --git a/bau-ui/skeleton/skeleton.js b/bau-ui/skeleton/skeleton.js index b5e2356a..a52bef2e 100644 --- a/bau-ui/skeleton/skeleton.js +++ b/bau-ui/skeleton/skeleton.js @@ -43,14 +43,14 @@ export default function (context, options = {}) { color = options.color ?? "neutral", ...props }, - ...children + children, ] = toPropsAndChildren(args); return div( { ...props, class: ["skeleton", size, className, options?.class, props?.class], }, - ...children + children ); }; } diff --git a/bau-ui/slider/slider.js b/bau-ui/slider/slider.js index 60c4fa16..11506d07 100644 --- a/bau-ui/slider/slider.js +++ b/bau-ui/slider/slider.js @@ -27,7 +27,7 @@ export default function (context, options = {}) { color = options.color ?? "neutral", ...props }, - ...children + children, ] = toPropsAndChildren(args); return input( @@ -44,7 +44,7 @@ export default function (context, options = {}) { props.class, ], }, - ...children + children ); }; } diff --git a/bau-ui/stepper/stepper.js b/bau-ui/stepper/stepper.js index 0a5d0a06..fd912346 100644 --- a/bau-ui/stepper/stepper.js +++ b/bau-ui/stepper/stepper.js @@ -102,7 +102,7 @@ export default function (context, options = {}) { activeStepIndex = bau.state(0), ...props }, - ...children + children, ] = toPropsAndChildren(args); const stepperState = bau.state( stepperDefs.map((stepper, index) => ({ ...stepper, index })) diff --git a/bau-ui/switch/switch.js b/bau-ui/switch/switch.js index 4c63be7e..ad4006e8 100644 --- a/bau-ui/switch/switch.js +++ b/bau-ui/switch/switch.js @@ -115,7 +115,7 @@ export default function (context, options = {}) { color = options.color ?? "neutral", ...props }, - ...children + children, ] = toPropsAndChildren(args); return input( { @@ -131,7 +131,7 @@ export default function (context, options = {}) { ], type: "checkbox", }, - ...children + children ); }; } diff --git a/bau-ui/tableContainer/tableContainer.js b/bau-ui/tableContainer/tableContainer.js index 59353c5d..0fd2aab2 100644 --- a/bau-ui/tableContainer/tableContainer.js +++ b/bau-ui/tableContainer/tableContainer.js @@ -59,13 +59,13 @@ export default function (context, options) { `; return function TableContainer(...args) { - let [{ ...props }, ...children] = toPropsAndChildren(args); + let [props, children] = toPropsAndChildren(args); return div( { ...props, class: ["table-container", className, options?.class, props?.class], }, - ...children + children ); }; } diff --git a/bau-ui/tablePagination/tablePagination.js b/bau-ui/tablePagination/tablePagination.js index 6483aaa1..4b1ff6b5 100644 --- a/bau-ui/tablePagination/tablePagination.js +++ b/bau-ui/tablePagination/tablePagination.js @@ -69,7 +69,6 @@ export default function (context, options = {}) { disableLast = () => isLastPage(page, totalCount, rowsPerPage), ...props }, - ...children ] = toPropsAndChildren(args); const maxPages = Math.max(0, Math.ceil(totalCount / rowsPerPage)); diff --git a/bau-ui/tabs/tabs.js b/bau-ui/tabs/tabs.js index a99a5a3d..cec6a27c 100644 --- a/bau-ui/tabs/tabs.js +++ b/bau-ui/tabs/tabs.js @@ -89,7 +89,6 @@ export default function (context, options = {}) { tabsKey = "tabs", ...props }, - ...children ] = toPropsAndChildren(args); const tabsState = bau.state(tabDefs); diff --git a/bau-ui/themeSwitch/themeSwitch.js b/bau-ui/themeSwitch/themeSwitch.js index c0232292..c138462c 100644 --- a/bau-ui/themeSwitch/themeSwitch.js +++ b/bau-ui/themeSwitch/themeSwitch.js @@ -101,7 +101,7 @@ export default function (context, options = {}) { color = options.color ?? "neutral", ...props }, - ...children + children, ] = toPropsAndChildren(args); return input( @@ -125,7 +125,7 @@ export default function (context, options = {}) { setDataThemeAttribute(event.target.checked ? "dark" : "light"); }, }, - ...children + children ); }; } diff --git a/bau-ui/toggle/toggle.js b/bau-ui/toggle/toggle.js index a10b3d9f..6ed55acb 100644 --- a/bau-ui/toggle/toggle.js +++ b/bau-ui/toggle/toggle.js @@ -73,7 +73,7 @@ export default function (context, options = {}) { onclick, ...props }, - ...children + children, ] = toPropsAndChildren(args); return button( diff --git a/bau-ui/toggleGroup/toggleGroup.js b/bau-ui/toggleGroup/toggleGroup.js index 4a25b5bd..e50d9c76 100644 --- a/bau-ui/toggleGroup/toggleGroup.js +++ b/bau-ui/toggleGroup/toggleGroup.js @@ -53,7 +53,7 @@ export default function (context, options = {}) { onChange = () => {}, ...props }, - ...children + children, ] = toPropsAndChildren(args); const selectedSet = new Set(); @@ -87,7 +87,7 @@ export default function (context, options = {}) { ], onclick, }, - ...children + children ); }; } diff --git a/bau-ui/tooltip/tooltip.js b/bau-ui/tooltip/tooltip.js index 2e16df24..562c50f4 100644 --- a/bau-ui/tooltip/tooltip.js +++ b/bau-ui/tooltip/tooltip.js @@ -96,7 +96,7 @@ export default function (context, options = {}) { color = options.color ?? "neutral", ...props }, - ...children + children, ] = toPropsAndChildren(args); const tooltipContentEl = div( @@ -169,7 +169,7 @@ export default function (context, options = {}) { }, }, - ...children, + children, tooltipContentEl ); 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..42669eab 100644 --- a/bau/bau.js +++ b/bau/bau.js @@ -4,17 +4,14 @@ 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"]; -let renderChildren = (arr, renderItem) => { - const children = new Array(arr.length); - for (let i = 0; i < arr.length; i++) children[i] = renderItem(arr[i], i); - return children; -}; - export const toPropsAndChildren = (args) => - !isState(args[0]) && isObject(args[0]) ? args : [{}, ...args]; + !isState(args[0]) && isObject(args[0]) + ? [args[0], args.slice(1)] + : [{}, args]; export default function Bau(input) { let _window = input?.window ?? window; @@ -37,7 +34,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; @@ -47,11 +47,12 @@ export default function Bau(input) { let updateDom = (state, method, result, args, data, parentProp) => { if (_inBatch) { - _stateSetInBatch.add(state); + _stateSetInBatch.add([state, method, result, args, data, parentProp]); return; } for (let binding of state.bindings) { - let { deps, element, renderInferred, render, renderItem } = binding; + let { deps, element, renderInferred, render, renderItem, isAttribute } = + binding; if (renderItem && method) { methodToActionMapping( element, @@ -67,8 +68,22 @@ export default function Bau(input) { element, }) : render({ element, renderItem })(...deps.map(toVal)); - if (newElement !== element) { - element.replaceWith((binding.element = toDom(newElement))); + if (newElement !== element && !isAttribute) { + 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++; + } } } } @@ -120,8 +135,11 @@ export default function Bau(input) { data, parentProp ) => { - let replaceChildren = () => - element.replaceChildren(...renderChildren(result, renderDomItem)); + let replaceChildren = () => { + element.textContent = ""; + for (let i = 0; i < result.length; i++) + element.appendChild(renderDomItem(result[i], i)); + }; let removeChild = (key) => element[key] && element.removeChild(element[key]); return { @@ -132,13 +150,14 @@ export default function Bau(input) { let index = parentProp[0]; element.children[index]?.replaceWith(renderDomItem(data[index], index)); }, - push: () => - element.append( - ...renderChildren(args, (item, index) => - renderDomItem(item, data.length + index) - ) - ), - unshift: () => element.prepend(...renderChildren(args, renderDomItem)), + push: () => { + for (let i = 0; i < args.length; i++) + element.appendChild(renderDomItem(args[i], data.length + i)); + }, + unshift: () => { + for (let i = args.length - 1; i >= 0; i--) + element.prepend(renderDomItem(args[i])); + }, pop: () => removeChild("lastChild"), shift: () => removeChild("firstChild"), splice: () => { @@ -205,6 +224,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,19 +251,20 @@ 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 = (element, children = []) => { + for (let child of children) { + if (Array.isArray(child)) { + add(element, child); + } else if (child != null) { + const newChild = isState(child) + ? bind({ deps: [child], render: () => (v) => v }) + : isFunction(child) + ? bindInferred(child) + : toDom(child); + Array.isArray(newChild) + ? element.append(...newChild) + : element.appendChild(newChild); + } } }; @@ -279,7 +301,7 @@ export default function Bau(input) { let tagsNS = (namespace) => new Proxy( function createTag(name, ...args) { - let [props, ...children] = toPropsAndChildren(args); + let [props, children] = toPropsAndChildren(args); let element = namespace ? document.createElementNS(namespace, name) : h(name); @@ -294,26 +316,30 @@ export default function Bau(input) { ); if (v == null) { } else if (isState(v)) { - bind({ deps: [v], render: () => () => (setter(v.val), element) }); + bind( + { deps: [v], render: () => () => (setter(v.val), element) }, + true + ); } else if (isFunction(v) && (!k.startsWith("on") || v.isDerived)) { - bindInferred({ - renderInferred: () => (setter(v({ element })), element), - }); + bindInferred(() => (setter(v({ element })), element), true); } else if (v.renderProp) { - bind({ - deps: v["deps"], - render: () => () => ( - setter(v["renderProp"]({ element })(...v["deps"].map(toVal))), - element - ), - }); + bind( + { + deps: v["deps"], + render: () => () => ( + setter(v["renderProp"]({ element })(...v["deps"].map(toVal))), + element + ), + }, + true + ); } else { setter(v); } } props.bauChildMutated && observerChildNode(element, props.bauChildMutated); - add(element, ...children); + add(element, children); element.autofocus && element.focus && _window.requestAnimationFrame(() => element.focus()); @@ -331,8 +357,9 @@ export default function Bau(input) { } ); - let bindFinalize = (binding, deps, newElement) => { + let bindFinalize = (binding, deps, newElement, isAttribute) => { binding.element = toDom(newElement); + binding.isAttribute = isAttribute; for (let dep of deps) { if (isState(dep)) { stateSet.add(dep); @@ -342,17 +369,18 @@ export default function Bau(input) { return binding.element; }; - let bindInferred = ({ renderInferred, element }) => { + let bindInferred = (renderInferred, isAttribute) => { let deps = new Set(); - let newElement = runAndCaptureDeps(renderInferred, deps, { element }); - return bindFinalize({ renderInferred }, deps, newElement); + let newElement = runAndCaptureDeps(renderInferred, deps, {}); + return bindFinalize({ renderInferred }, deps, newElement, isAttribute); }; - let bind = ({ deps, element, render, renderItem }) => + let bind = ({ deps, element, render, renderItem }, isAttribute) => bindFinalize( { deps, render, renderItem }, deps, - render({ element, renderItem })(...deps.map(toVal)) + render({ element, renderItem })(...deps.map(toVal)), + isAttribute ); let loop = (stateArray, container, renderItem) => @@ -360,9 +388,11 @@ export default function Bau(input) { deps: [stateArray], render: ({ renderItem }) => - (arr) => ( - container.append(...renderChildren(arr, renderItem)), container - ), + (arr) => { + for (let i = 0; i < arr.length; i++) + container.appendChild(renderItem(arr[i], i)); + return container; + }, renderItem, }); @@ -370,7 +400,7 @@ export default function Bau(input) { _inBatch = true; const res = await batchFn(); _inBatch = false; - _stateSetInBatch.forEach(updateDom); + _stateSetInBatch.forEach((args) => updateDom(...args)); _stateSetInBatch.clear(); return res; }; diff --git a/bau/examples/bau-ts-test/src/main.ts b/bau/examples/bau-ts-test/src/main.ts index 8ea1490b..02bcb594 100644 --- a/bau/examples/bau-ts-test/src/main.ts +++ b/bau/examples/bau-ts-test/src/main.ts @@ -514,6 +514,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 +846,10 @@ const App = ({}) => { ), section( h1("Derive"), - // TestDerived(), TestDerivedSideEffect(), - TestDeriveText() + TestDeriveText(), + TestDeriveReturnArray() ), section( diff --git a/examples/button/main.js b/examples/button/main.js index 87f7813e..74fb7cb0 100644 --- a/examples/button/main.js +++ b/examples/button/main.js @@ -13,7 +13,7 @@ const app = ({ bau }) => { }, onclick, }, - ...children + children ); const colorState = bau.state("green"); diff --git a/examples/gccd/src/components/formDestroy.ts b/examples/gccd/src/components/formDestroy.ts index 253fd21d..1f0352ab 100644 --- a/examples/gccd/src/components/formDestroy.ts +++ b/examples/gccd/src/components/formDestroy.ts @@ -17,7 +17,7 @@ export default function (context: Context) { `; return function FormDestroy(...args: any[]) { - let [props, ...children] = toPropsAndChildren(args); + let [props, children] = toPropsAndChildren(args); return Form( { class: className, diff --git a/examples/gccd/src/components/page.ts b/examples/gccd/src/components/page.ts index b2a973ee..29fa94a1 100644 --- a/examples/gccd/src/components/page.ts +++ b/examples/gccd/src/components/page.ts @@ -27,7 +27,7 @@ export default function (context: Context) { `; return function Page(...args: any[]) { - let [props, ...children] = toPropsAndChildren(args); + let [props, children] = toPropsAndChildren(args); return Paper( { ...props,