sygnal 5.3.7 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +646 -0
- package/README.md +77 -47
- package/dist/astro/client.cjs.js +33 -8114
- package/dist/astro/client.cjs.js.map +1 -0
- package/dist/astro/client.mjs +33 -8114
- package/dist/astro/client.mjs.map +1 -0
- package/dist/astro/index.cjs.js +1039 -9
- package/dist/astro/index.cjs.js.map +1 -0
- package/dist/astro/index.mjs +1038 -9
- package/dist/astro/index.mjs.map +1 -0
- package/dist/astro/server.cjs.js +3400 -166
- package/dist/astro/server.cjs.js.map +1 -0
- package/dist/astro/server.mjs +3400 -166
- package/dist/astro/server.mjs.map +1 -0
- package/dist/devtools.cjs.js +1431 -0
- package/dist/devtools.cjs.js.map +1 -0
- package/dist/devtools.esm.js +1419 -0
- package/dist/devtools.esm.js.map +1 -0
- package/dist/diagnostics.cjs.js +4033 -0
- package/dist/diagnostics.cjs.js.map +1 -0
- package/dist/diagnostics.esm.js +4003 -0
- package/dist/diagnostics.esm.js.map +1 -0
- package/dist/element.cjs.js +230 -0
- package/dist/element.cjs.js.map +1 -0
- package/dist/element.esm.js +228 -0
- package/dist/element.esm.js.map +1 -0
- package/dist/guide/accessibility.md +257 -0
- package/dist/guide/adapters.md +221 -0
- package/dist/guide/behaviors.md +312 -0
- package/dist/guide/browser-sources.md +203 -0
- package/dist/guide/drag-and-drop.md +342 -0
- package/dist/guide/element-commands.md +306 -0
- package/dist/guide/error-boundaries.md +169 -0
- package/dist/guide/forms-reference.md +381 -0
- package/dist/guide/forms.md +250 -0
- package/dist/guide/http.md +299 -0
- package/dist/guide/inputs.md +173 -0
- package/dist/guide/persistence.md +245 -0
- package/dist/guide/recipes/carousel.md +155 -0
- package/dist/guide/recipes/charts.md +177 -0
- package/dist/guide/recipes/code-editor.md +135 -0
- package/dist/guide/recipes/data-grid.md +155 -0
- package/dist/guide/recipes/data-table.md +196 -0
- package/dist/guide/recipes/i18n.md +222 -0
- package/dist/guide/recipes/icons.md +115 -0
- package/dist/guide/recipes/overview.md +33 -0
- package/dist/guide/recipes/rich-text.md +154 -0
- package/dist/guide/resources.md +471 -0
- package/dist/guide/ssr.md +255 -0
- package/dist/guide/timers.md +178 -0
- package/dist/guide/ui/accordion.md +93 -0
- package/dist/guide/ui/combobox.md +144 -0
- package/dist/guide/ui/dialog.md +124 -0
- package/dist/guide/ui/disclosure.md +60 -0
- package/dist/guide/ui/menu.md +125 -0
- package/dist/guide/ui/overview.md +166 -0
- package/dist/guide/ui/popover.md +101 -0
- package/dist/guide/ui/select.md +114 -0
- package/dist/guide/ui/tabs.md +161 -0
- package/dist/guide/ui/toaster.md +152 -0
- package/dist/guide/ui/tooltip.md +103 -0
- package/dist/guide/undo.md +178 -0
- package/dist/guide/virtual-collections.md +183 -0
- package/dist/guide/web-components.md +179 -0
- package/dist/guide/widgets.md +217 -0
- package/dist/index.cjs.js +15283 -6279
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.d.ts +3739 -370
- package/dist/index.esm.js +15230 -6274
- package/dist/index.esm.js.map +1 -0
- package/dist/jsx-dev-runtime.cjs.js +27 -329
- package/dist/jsx-dev-runtime.cjs.js.map +1 -0
- package/dist/jsx-dev-runtime.esm.js +20 -322
- package/dist/jsx-dev-runtime.esm.js.map +1 -0
- package/dist/jsx-runtime.cjs.js +27 -329
- package/dist/jsx-runtime.cjs.js.map +1 -0
- package/dist/jsx-runtime.esm.js +20 -322
- package/dist/jsx-runtime.esm.js.map +1 -0
- package/dist/jsx.cjs.js +11 -309
- package/dist/jsx.cjs.js.map +1 -0
- package/dist/jsx.esm.js +9 -310
- package/dist/jsx.esm.js.map +1 -0
- package/dist/react.cjs.js +117 -0
- package/dist/react.cjs.js.map +1 -0
- package/dist/react.esm.js +115 -0
- package/dist/react.esm.js.map +1 -0
- package/dist/shims/globalthis.cjs +20 -0
- package/dist/sygnal.min.js +2 -1
- package/dist/sygnal.min.js.map +1 -0
- package/dist/ui-combobox.cjs.js +169 -0
- package/dist/ui-combobox.cjs.js.map +1 -0
- package/dist/ui-combobox.esm.js +148 -0
- package/dist/ui-combobox.esm.js.map +1 -0
- package/dist/ui-menu.cjs.js +87 -0
- package/dist/ui-menu.cjs.js.map +1 -0
- package/dist/ui-menu.esm.js +66 -0
- package/dist/ui-menu.esm.js.map +1 -0
- package/dist/ui-select.cjs.js +104 -0
- package/dist/ui-select.cjs.js.map +1 -0
- package/dist/ui-select.esm.js +83 -0
- package/dist/ui-select.esm.js.map +1 -0
- package/dist/ui.cjs.js +738 -0
- package/dist/ui.cjs.js.map +1 -0
- package/dist/ui.esm.js +727 -0
- package/dist/ui.esm.js.map +1 -0
- package/dist/vike/ClientOnly.cjs.js +5 -14
- package/dist/vike/ClientOnly.cjs.js.map +1 -0
- package/dist/vike/ClientOnly.mjs +5 -14
- package/dist/vike/ClientOnly.mjs.map +1 -0
- package/dist/vike/{+config.js → config/+config.js} +10 -1
- package/dist/vike/config/+config.js.map +1 -0
- package/dist/vike/config/package.json +6 -0
- package/dist/vike/onRenderClient.cjs.js +166 -95
- package/dist/vike/onRenderClient.cjs.js.map +1 -0
- package/dist/vike/onRenderClient.mjs +166 -95
- package/dist/vike/onRenderClient.mjs.map +1 -0
- package/dist/vike/onRenderHtml.cjs.js +60 -24
- package/dist/vike/onRenderHtml.cjs.js.map +1 -0
- package/dist/vike/onRenderHtml.mjs +61 -25
- package/dist/vike/onRenderHtml.mjs.map +1 -0
- package/dist/vite/plugin.cjs.js +926 -65
- package/dist/vite/plugin.cjs.js.map +1 -0
- package/dist/vite/plugin.mjs +925 -65
- package/dist/vite/plugin.mjs.map +1 -0
- package/dist/zag.cjs.js +408 -0
- package/dist/zag.cjs.js.map +1 -0
- package/dist/zag.esm.js +405 -0
- package/dist/zag.esm.js.map +1 -0
- package/llms.txt +313 -0
- package/package.json +109 -16
- package/src/astro/client.ts +40 -18
- package/src/astro/index.d.ts +48 -1
- package/src/astro/index.ts +106 -9
- package/src/astro/server.ts +11 -3
- package/src/collection.ts +6 -90
- package/src/core/actions.ts +134 -0
- package/src/core/cell.ts +202 -0
- package/src/core/debug.ts +23 -0
- package/src/core/define.ts +242 -0
- package/src/core/hooks.ts +314 -0
- package/src/core/hosts/collection.ts +325 -0
- package/src/core/hosts/switchable.ts +146 -0
- package/src/core/instance.ts +592 -0
- package/src/core/markers/clientonly.ts +13 -0
- package/src/core/markers/lazy.ts +40 -0
- package/src/core/markers/portal.ts +96 -0
- package/src/core/markers/suspense.ts +41 -0
- package/src/core/markers/transition.ts +70 -0
- package/src/core/registry.ts +25 -0
- package/src/core/runtime.ts +573 -0
- package/src/core/statics.ts +104 -0
- package/src/core/teardown.ts +60 -0
- package/src/core/view.ts +37 -0
- package/src/cycle/dom/DocumentDOMSource.ts +13 -8
- package/src/cycle/dom/ElementFinder.ts +7 -9
- package/src/cycle/dom/EventDelegator.ts +174 -223
- package/src/cycle/dom/IsolateModule.ts +65 -35
- package/src/cycle/dom/MainDOMSource.ts +13 -2
- package/src/cycle/dom/PriorityQueue.ts +4 -2
- package/src/cycle/dom/SymbolTree.ts +18 -47
- package/src/cycle/dom/classNameModule.ts +76 -0
- package/src/cycle/dom/controlledInputModule.ts +50 -0
- package/src/cycle/dom/enrichEventStream.ts +29 -45
- package/src/cycle/dom/fragment.ts +7 -0
- package/src/cycle/dom/isolate.ts +30 -27
- package/src/cycle/dom/makeDOMDriver.ts +115 -57
- package/src/cycle/dom/mockDOMSource.ts +83 -9
- package/src/cycle/dom/modules.ts +21 -4
- package/src/cycle/dom/propsModule.ts +65 -0
- package/src/cycle/dom/selectModule.ts +22 -17
- package/src/cycle/dom/snabbdom.ts +1 -6
- package/src/cycle/dom/thunk.ts +3 -0
- package/src/cycle/dom/utils.ts +98 -0
- package/src/cycle/dom/viewTransition.ts +43 -0
- package/src/cycle/state/StateSource.ts +20 -4
- package/src/cycle/state/objIsEqual.ts +50 -0
- package/src/cycle/state/types.ts +0 -10
- package/src/defineComponent.ts +34 -0
- package/src/devtools.d.ts +151 -0
- package/src/devtools.ts +34 -0
- package/src/element.d.ts +59 -0
- package/src/element.ts +235 -0
- package/src/extra/backoff.ts +9 -0
- package/src/extra/behaviors.ts +169 -0
- package/src/extra/browserSignals.ts +23 -0
- package/src/extra/browserSources.ts +326 -0
- package/src/extra/command.ts +3 -3
- package/src/extra/controls.ts +47 -0
- package/src/extra/copyAsTest.ts +286 -0
- package/src/extra/devtools.ts +109 -31
- package/src/extra/devtoolsActions.ts +379 -0
- package/src/extra/devtoolsHook.ts +9 -0
- package/src/extra/devtoolsNext.ts +83 -0
- package/src/extra/diagnostics/checks/actionLog.ts +108 -0
- package/src/extra/diagnostics/checks/behaviors.ts +49 -0
- package/src/extra/diagnostics/checks/browserSources.ts +96 -0
- package/src/extra/diagnostics/checks/collections.ts +67 -0
- package/src/extra/diagnostics/checks/controls.ts +148 -0
- package/src/extra/diagnostics/checks/dataset.ts +57 -0
- package/src/extra/diagnostics/checks/dom.ts +250 -0
- package/src/extra/diagnostics/checks/elementCommands.ts +175 -0
- package/src/extra/diagnostics/checks/events.ts +83 -0
- package/src/extra/diagnostics/checks/fetch.ts +135 -0
- package/src/extra/diagnostics/checks/forms.ts +163 -0
- package/src/extra/diagnostics/checks/index.ts +202 -0
- package/src/extra/diagnostics/checks/inspect.ts +427 -0
- package/src/extra/diagnostics/checks/next.ts +417 -0
- package/src/extra/diagnostics/checks/persist.ts +54 -0
- package/src/extra/diagnostics/checks/props.ts +56 -0
- package/src/extra/diagnostics/checks/public.d.ts +302 -0
- package/src/extra/diagnostics/checks/replies.ts +128 -0
- package/src/extra/diagnostics/checks/router.ts +100 -0
- package/src/extra/diagnostics/checks/rxjsHints.ts +88 -0
- package/src/extra/diagnostics/checks/shared.ts +203 -0
- package/src/extra/diagnostics/checks/shorthand.ts +115 -0
- package/src/extra/diagnostics/checks/sortable.ts +129 -0
- package/src/extra/diagnostics/checks/state.ts +148 -0
- package/src/extra/diagnostics/checks/statics.ts +48 -0
- package/src/extra/diagnostics/checks/strict.ts +55 -0
- package/src/extra/diagnostics/checks/timers.ts +80 -0
- package/src/extra/diagnostics/checks/viewTransitions.ts +47 -0
- package/src/extra/diagnostics/checks/virtual.ts +73 -0
- package/src/extra/diagnostics/checks/widgets.ts +109 -0
- package/src/extra/diagnostics/checks/wiring.ts +121 -0
- package/src/extra/diagnostics/codes.ts +477 -0
- package/src/extra/diagnostics/index.ts +353 -0
- package/src/extra/diagnostics/legacy.ts +76 -0
- package/src/extra/driverFactories.ts +172 -60
- package/src/extra/elementCommands.ts +53 -0
- package/src/extra/eventDriver.ts +4 -0
- package/src/extra/fetchDriver.ts +519 -0
- package/src/extra/flatten.ts +75 -0
- package/src/extra/focusWithin.ts +27 -0
- package/src/extra/form.ts +268 -0
- package/src/extra/formHelpers.ts +139 -0
- package/src/extra/head.ts +106 -0
- package/src/extra/hmr.ts +2 -3
- package/src/extra/owned.ts +24 -0
- package/src/extra/pager.ts +50 -0
- package/src/extra/persist.ts +133 -0
- package/src/extra/pwa.ts +1 -1
- package/src/extra/queryCache.ts +107 -0
- package/src/extra/reducers.ts +13 -6
- package/src/extra/reduxDevtools.ts +92 -0
- package/src/extra/replies.ts +54 -0
- package/src/extra/router.ts +323 -0
- package/src/extra/run.ts +94 -210
- package/src/extra/selection.ts +76 -0
- package/src/extra/socketDriver.ts +265 -0
- package/src/extra/sortable.ts +377 -0
- package/src/extra/ssr.ts +427 -160
- package/src/extra/standardSchema.ts +27 -0
- package/src/extra/testing.ts +3158 -190
- package/src/extra/timers.ts +95 -0
- package/src/extra/undo.ts +261 -0
- package/src/extra/viewTransitions.ts +38 -0
- package/src/extra/virtual.ts +513 -0
- package/src/extra/widget.ts +201 -0
- package/src/extra/xstreamExtras.ts +269 -0
- package/src/index.d.ts +3012 -87
- package/src/index.ts +32 -10
- package/src/jsx-runtime.ts +15 -1
- package/src/jsx.ts +2 -1
- package/src/lazy.ts +50 -7
- package/src/portal.ts +3 -1
- package/src/pragma/index.ts +262 -134
- package/src/react-peers.d.ts +5 -0
- package/src/react.d.ts +43 -0
- package/src/react.ts +110 -0
- package/src/shared.ts +48 -0
- package/src/slot.ts +1 -1
- package/src/suspense.ts +3 -1
- package/src/switchable.ts +6 -119
- package/src/transition.ts +3 -1
- package/src/ui/accordion.ts +64 -0
- package/src/ui/dialog.ts +129 -0
- package/src/ui/disclosure.ts +35 -0
- package/src/ui/popover.ts +45 -0
- package/src/ui/shared.ts +94 -0
- package/src/ui/tabs.ts +82 -0
- package/src/ui/toaster.ts +198 -0
- package/src/ui/tooltip.ts +70 -0
- package/src/ui/zag/combobox.ts +115 -0
- package/src/ui/zag/menu.ts +40 -0
- package/src/ui/zag/select.ts +54 -0
- package/src/ui/zag/shared.ts +43 -0
- package/src/ui-combobox.d.ts +20 -0
- package/src/ui-combobox.ts +6 -0
- package/src/ui-menu.d.ts +30 -0
- package/src/ui-menu.ts +6 -0
- package/src/ui-select.d.ts +17 -0
- package/src/ui-select.ts +6 -0
- package/src/ui-zag-types.d.ts +42 -0
- package/src/ui.d.ts +208 -0
- package/src/ui.ts +15 -0
- package/src/vike/+config.ts +9 -1
- package/src/vike/ClientOnly.ts +1 -1
- package/src/vike/onRenderClient.ts +144 -96
- package/src/vike/onRenderHtml.ts +69 -26
- package/src/vike/types.ts +27 -4
- package/src/vite/globalthis-shim.ts +18 -0
- package/src/vite/globalthis.ts +56 -0
- package/src/vite/plugin.d.ts +114 -2
- package/src/vite/plugin.ts +920 -56
- package/src/zag.d.ts +64 -0
- package/src/zag.ts +238 -0
- package/dist/astro/astro/client.d.ts +0 -5
- package/dist/astro/astro/index.d.ts +0 -16
- package/dist/astro/astro/server.d.ts +0 -12
- package/dist/astro/client.d.ts +0 -5
- package/dist/astro/client.esm.js +0 -4665
- package/dist/astro/collection.d.ts +0 -12
- package/dist/astro/component.d.ts +0 -22
- package/dist/astro/cycle/dom/BodyDOMSource.d.ts +0 -10
- package/dist/astro/cycle/dom/DOMSource.d.ts +0 -11
- package/dist/astro/cycle/dom/DocumentDOMSource.d.ts +0 -12
- package/dist/astro/cycle/dom/ElementFinder.d.ts +0 -8
- package/dist/astro/cycle/dom/EventDelegator.d.ts +0 -34
- package/dist/astro/cycle/dom/IsolateModule.d.ts +0 -23
- package/dist/astro/cycle/dom/MainDOMSource.d.ts +0 -32
- package/dist/astro/cycle/dom/PriorityQueue.d.ts +0 -7
- package/dist/astro/cycle/dom/ScopeChecker.d.ts +0 -9
- package/dist/astro/cycle/dom/SymbolTree.d.ts +0 -9
- package/dist/astro/cycle/dom/VNodeWrapper.d.ts +0 -8
- package/dist/astro/cycle/dom/enrichEventStream.d.ts +0 -24
- package/dist/astro/cycle/dom/fromEvent.d.ts +0 -6
- package/dist/astro/cycle/dom/index.d.ts +0 -8
- package/dist/astro/cycle/dom/isolate.d.ts +0 -9
- package/dist/astro/cycle/dom/makeDOMDriver.d.ts +0 -11
- package/dist/astro/cycle/dom/mockDOMSource.d.ts +0 -17
- package/dist/astro/cycle/dom/modules.d.ts +0 -5
- package/dist/astro/cycle/dom/snabbdom.d.ts +0 -22
- package/dist/astro/cycle/dom/styleModule.d.ts +0 -13
- package/dist/astro/cycle/dom/thunk.d.ts +0 -11
- package/dist/astro/cycle/dom/utils.d.ts +0 -8
- package/dist/astro/cycle/isolate/index.d.ts +0 -42
- package/dist/astro/cycle/run/adapt.d.ts +0 -6
- package/dist/astro/cycle/run/index.d.ts +0 -36
- package/dist/astro/cycle/run/internals.d.ts +0 -8
- package/dist/astro/cycle/run/types.d.ts +0 -49
- package/dist/astro/cycle/state/Collection.d.ts +0 -29
- package/dist/astro/cycle/state/StateSource.d.ts +0 -20
- package/dist/astro/cycle/state/index.d.ts +0 -5
- package/dist/astro/cycle/state/pickCombine.d.ts +0 -3
- package/dist/astro/cycle/state/pickMerge.d.ts +0 -3
- package/dist/astro/cycle/state/types.d.ts +0 -18
- package/dist/astro/cycle/state/withState.d.ts +0 -17
- package/dist/astro/extra/classes.d.ts +0 -7
- package/dist/astro/extra/devtools.d.ts +0 -78
- package/dist/astro/extra/dragDriver.d.ts +0 -20
- package/dist/astro/extra/driverFactories.d.ts +0 -12
- package/dist/astro/extra/eventDriver.d.ts +0 -9
- package/dist/astro/extra/exactState.d.ts +0 -1
- package/dist/astro/extra/hmr.d.ts +0 -17
- package/dist/astro/extra/logDriver.d.ts +0 -2
- package/dist/astro/extra/processDrag.d.ts +0 -19
- package/dist/astro/extra/processForm.d.ts +0 -15
- package/dist/astro/extra/run.d.ts +0 -13
- package/dist/astro/extra/xstreamCompat.d.ts +0 -5
- package/dist/astro/index.d.ts +0 -19
- package/dist/astro/index.esm.js +0 -27
- package/dist/astro/jsx-dev-runtime.d.ts +0 -1
- package/dist/astro/jsx-runtime.d.ts +0 -4
- package/dist/astro/jsx.d.ts +0 -2
- package/dist/astro/pragma/fn.d.ts +0 -7
- package/dist/astro/pragma/index.d.ts +0 -7
- package/dist/astro/pragma/is.d.ts +0 -10
- package/dist/astro/server.d.ts +0 -12
- package/dist/astro/server.esm.js +0 -25
- package/dist/astro/switchable.d.ts +0 -7
- package/dist/collection.d.ts +0 -12
- package/dist/component.d.ts +0 -22
- package/dist/cycle/dom/BodyDOMSource.d.ts +0 -10
- package/dist/cycle/dom/DOMSource.d.ts +0 -11
- package/dist/cycle/dom/DocumentDOMSource.d.ts +0 -12
- package/dist/cycle/dom/ElementFinder.d.ts +0 -8
- package/dist/cycle/dom/EventDelegator.d.ts +0 -34
- package/dist/cycle/dom/IsolateModule.d.ts +0 -23
- package/dist/cycle/dom/MainDOMSource.d.ts +0 -32
- package/dist/cycle/dom/PriorityQueue.d.ts +0 -7
- package/dist/cycle/dom/ScopeChecker.d.ts +0 -9
- package/dist/cycle/dom/SymbolTree.d.ts +0 -9
- package/dist/cycle/dom/VNodeWrapper.d.ts +0 -8
- package/dist/cycle/dom/enrichEventStream.d.ts +0 -24
- package/dist/cycle/dom/fromEvent.d.ts +0 -6
- package/dist/cycle/dom/index.d.ts +0 -8
- package/dist/cycle/dom/isolate.d.ts +0 -9
- package/dist/cycle/dom/makeDOMDriver.d.ts +0 -11
- package/dist/cycle/dom/mockDOMSource.d.ts +0 -17
- package/dist/cycle/dom/modules.d.ts +0 -5
- package/dist/cycle/dom/snabbdom.d.ts +0 -22
- package/dist/cycle/dom/styleModule.d.ts +0 -13
- package/dist/cycle/dom/thunk.d.ts +0 -11
- package/dist/cycle/dom/utils.d.ts +0 -8
- package/dist/cycle/isolate/index.d.ts +0 -42
- package/dist/cycle/run/adapt.d.ts +0 -6
- package/dist/cycle/run/index.d.ts +0 -36
- package/dist/cycle/run/internals.d.ts +0 -8
- package/dist/cycle/run/types.d.ts +0 -49
- package/dist/cycle/state/Collection.d.ts +0 -29
- package/dist/cycle/state/StateSource.d.ts +0 -20
- package/dist/cycle/state/index.d.ts +0 -5
- package/dist/cycle/state/pickCombine.d.ts +0 -3
- package/dist/cycle/state/pickMerge.d.ts +0 -3
- package/dist/cycle/state/types.d.ts +0 -18
- package/dist/cycle/state/withState.d.ts +0 -17
- package/dist/extra/classes.d.ts +0 -7
- package/dist/extra/devtools.d.ts +0 -78
- package/dist/extra/dragDriver.d.ts +0 -20
- package/dist/extra/driverFactories.d.ts +0 -12
- package/dist/extra/eventDriver.d.ts +0 -9
- package/dist/extra/exactState.d.ts +0 -1
- package/dist/extra/hmr.d.ts +0 -17
- package/dist/extra/logDriver.d.ts +0 -2
- package/dist/extra/processDrag.d.ts +0 -19
- package/dist/extra/processForm.d.ts +0 -15
- package/dist/extra/run.d.ts +0 -13
- package/dist/extra/xstreamCompat.d.ts +0 -5
- package/dist/jsx-dev-runtime.d.ts +0 -1
- package/dist/jsx-dev-runtime.js +0 -224
- package/dist/jsx-runtime.d.ts +0 -4
- package/dist/jsx-runtime.js +0 -224
- package/dist/jsx.d.ts +0 -2
- package/dist/pragma/fn.d.ts +0 -7
- package/dist/pragma/index.d.ts +0 -7
- package/dist/pragma/is.d.ts +0 -10
- package/dist/switchable.d.ts +0 -7
- package/dist/vike/+config.cjs.js +0 -65
- package/src/component.ts +0 -2256
- package/src/cycle/isolate/index.ts +0 -196
- package/src/cycle/run/index.ts +0 -151
- package/src/cycle/run/internals.ts +0 -143
- package/src/cycle/state/Collection.ts +0 -174
- package/src/cycle/state/index.ts +0 -5
- package/src/cycle/state/pickCombine.ts +0 -167
- package/src/cycle/state/pickMerge.ts +0 -122
- package/src/cycle/state/withState.ts +0 -47
- package/src/pragma/fn.ts +0 -55
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PLAN-4.6 R0: the extension contract of the component core (types only: 0 bytes in any bundle).
|
|
3
|
+
* R1-R4 implemented it; R5 deleted the old core's instance patching it replaces. The prose
|
|
4
|
+
* contract, with the consumers mapped to these hooks, is
|
|
5
|
+
* dev-plans/research/core-rewrite/04-hooks-contract.md.
|
|
6
|
+
*
|
|
7
|
+
* Three layers:
|
|
8
|
+
* 1. Registries (module level, filled on import): what a feature module adds to the core without
|
|
9
|
+
* the core naming it: hosts, markers (pre/post processors), view resolvers, definition hooks,
|
|
10
|
+
* statics. An app that never imports the module pays nothing (D157).
|
|
11
|
+
* 2. Hooks (per app, from run() / renderComponent / the dev entries): observation and wrapping
|
|
12
|
+
* points for diagnostics, devtools, testing and the dev action log. Every hook is optional and
|
|
13
|
+
* the core calls it as `hooks.x?.(...)`.
|
|
14
|
+
* 3. The runtime API handed to hooks: inspection views and `setState`, so no consumer needs a raw
|
|
15
|
+
* instance or a stream internal (`shamefullySendNext`, `_v`, `_n`).
|
|
16
|
+
*
|
|
17
|
+
* Invariants the hooks must not break (the flush contract, PLAN-4.6 §2 change 4):
|
|
18
|
+
* - Actions drain run-to-completion, FIFO: an action dispatched while another is processed (a
|
|
19
|
+
* driver emitting synchronously during sink delivery, an EFFECT, a PARENT, a hook) is appended
|
|
20
|
+
* to the queue, never run nested.
|
|
21
|
+
* - A STATE reducer is applied when its action is processed. Every other sink of that action gets
|
|
22
|
+
* the state from before it.
|
|
23
|
+
* - A flush (one microtask after the first commit of a batch) repeats { render top-down, recompute
|
|
24
|
+
* statics, drain the queue } until nothing changed, then emits ONE root vnode (one patch), then
|
|
25
|
+
* runs ELEMENT commands and dispatches BOOTSTRAP for the instances created in it.
|
|
26
|
+
* - Loop guard: at most 100 passes per flush and 100 flushes per macrotask; past that the next
|
|
27
|
+
* flush waits for a MessageChannel message. No timer anywhere in startup, flush or teardown.
|
|
28
|
+
* - Hooks run synchronously inside these steps. A hook may dispatch (it is queued), but must not
|
|
29
|
+
* render, patch or dispose.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
// ---------------------------------------------------------------------------------- shared types
|
|
33
|
+
|
|
34
|
+
/** A component function with its statics (view + `.intent`, `.model`, `.initialState`, ...) */
|
|
35
|
+
export type ComponentFn = ((input: any) => any) & Record<string, any>
|
|
36
|
+
|
|
37
|
+
/** One normalized model entry: the sink and its handler (a function, or a constant value) */
|
|
38
|
+
export type Handler = [sink: string, fn: any]
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* What a definition hook may read and edit: the component's statics, before normalization.
|
|
42
|
+
* Behaviors' existing `merge(component, key)` runs on a shim with these fields (spike finding 5),
|
|
43
|
+
* so `uses`, `undo`, `selection`, `pager` and `persist` work unchanged, once per component
|
|
44
|
+
* function instead of once per instance.
|
|
45
|
+
*/
|
|
46
|
+
export interface DefSource {
|
|
47
|
+
model: Record<string, any> | undefined
|
|
48
|
+
intent: ((sources: any) => Record<string, any>) | undefined
|
|
49
|
+
initialState: any
|
|
50
|
+
/** defaults of missing state keys (a behavior's slice or a resource's `{status: 'idle'}` on a sub-component) */
|
|
51
|
+
idle: Record<string, any> | null
|
|
52
|
+
calculated: Record<string, any> | undefined
|
|
53
|
+
context: Record<string, (state: any) => any> | undefined
|
|
54
|
+
/** action -> behavior key, for action logs (`pager.NEXT` comes from `pager`) */
|
|
55
|
+
behaviorActions: Record<string, string>
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** The normalized definition: computed once per component function (WeakMap) */
|
|
59
|
+
export interface Def {
|
|
60
|
+
readonly name: string
|
|
61
|
+
readonly view: ComponentFn
|
|
62
|
+
readonly intent?: (sources: any) => Record<string, any>
|
|
63
|
+
/** action -> handlers in model order; a function entry is STATE */
|
|
64
|
+
readonly handlers: ReadonlyMap<string, readonly Handler[]>
|
|
65
|
+
readonly sinks: ReadonlySet<string>
|
|
66
|
+
readonly initialState: any
|
|
67
|
+
readonly idle: Record<string, any> | null
|
|
68
|
+
readonly isolated: boolean
|
|
69
|
+
/** calculated fields in topological order (SYG206 / SYG209 thrown at definition) */
|
|
70
|
+
readonly calculated: ReadonlyArray<[field: string, fn: (s: any) => any, deps: string[] | null]> | null
|
|
71
|
+
readonly context: ReadonlyArray<[key: string, fn: (s: any) => any]> | null
|
|
72
|
+
/** the statics this definition declares that some driver takes (`__sygnalStatic`) */
|
|
73
|
+
readonly statics: ReadonlyArray<string>
|
|
74
|
+
readonly behaviorActions: Readonly<Record<string, string>>
|
|
75
|
+
readonly onError?: (error: any, info: {componentName: string}) => any
|
|
76
|
+
/**
|
|
77
|
+
* R4 (renderComponent's root only): the model actions the test may dispatch with
|
|
78
|
+
* simulateAction that the intent doesn't name (wiring's SYG101/SYG102, inspect(); today's
|
|
79
|
+
* `__sygnalTestActions` on the intent object)
|
|
80
|
+
*/
|
|
81
|
+
readonly testActions?: readonly string[]
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Where an action came from (testing's t.actions `cause`, devtools, the action log) */
|
|
85
|
+
export type ActionCause = 'intent' | 'next' | 'reply' | 'built-in' | 'simulateAction' | 'behavior' | 'parent' | 'setState'
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* A read-only view of a live instance: everything devtools, diagnostics and testing read today
|
|
89
|
+
* from raw instance fields (`_componentNumber`, `name`, `currentState`, `currentProps`,
|
|
90
|
+
* `currentContext`, `isSubComponent`, `sources.__parentComponentNumber`, `_disposed`, ...).
|
|
91
|
+
* Getters are live; the object is stable for the instance's life.
|
|
92
|
+
*/
|
|
93
|
+
export interface InstanceView {
|
|
94
|
+
/** stable id (inspect()'s component id; today `_componentNumber`) */
|
|
95
|
+
readonly id: number
|
|
96
|
+
readonly parentId: number | undefined
|
|
97
|
+
readonly name: string
|
|
98
|
+
readonly def: Def
|
|
99
|
+
/** the state the view gets (calculated fields included; `currentState` + `addCalculated`) */
|
|
100
|
+
readonly state: any
|
|
101
|
+
readonly props: Record<string, any>
|
|
102
|
+
readonly context: Record<string, any>
|
|
103
|
+
readonly isRoot: boolean
|
|
104
|
+
/** 'item' (Collection), 'page' (Switchable), 'child' (tag), 'root' */
|
|
105
|
+
readonly kind: 'root' | 'child' | 'item' | 'page'
|
|
106
|
+
/** Switchable page: shown now (hidden pages skip render, keep actions and background statics) */
|
|
107
|
+
readonly shown: boolean
|
|
108
|
+
readonly disposed: boolean
|
|
109
|
+
/** the uid() root of this instance (`__uid`) */
|
|
110
|
+
readonly uid: string
|
|
111
|
+
/** its children, in render order */
|
|
112
|
+
children(): InstanceView[]
|
|
113
|
+
/** the sources its intent got (after wrapSources), e.g. to compare with run()'s sources */
|
|
114
|
+
readonly sources: Readonly<Record<string, any>>
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** An action as the queue holds it */
|
|
118
|
+
export interface ActionRecord {
|
|
119
|
+
readonly type: string
|
|
120
|
+
readonly data: any
|
|
121
|
+
readonly cause: ActionCause
|
|
122
|
+
/** the instance that runs it */
|
|
123
|
+
readonly target: InstanceView
|
|
124
|
+
/** a reply: the source (driver name) that delivered it (R4: devtools' replySink) */
|
|
125
|
+
readonly source?: string
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// ---------------------------------------------------------------------------------- 2. hooks
|
|
129
|
+
|
|
130
|
+
export interface Hooks {
|
|
131
|
+
// ------------------------------------------------------------------ definition time
|
|
132
|
+
/**
|
|
133
|
+
* Called once per component function, before normalization, with the definition's statics.
|
|
134
|
+
* Return a new (or the same, edited) DefSource. Runs for every app on the page (definitions are
|
|
135
|
+
* shared), so it must depend only on `view`. Behaviors, undo, selection, pager, persist's model
|
|
136
|
+
* rewrite and resources' RESOURCE entry use the module registry (`defHooks`) instead; this hook
|
|
137
|
+
* is for a per-app consumer that needs the same point (diagnostics' SYG222 reducer wrapping,
|
|
138
|
+
* strict checks of the model's shape).
|
|
139
|
+
*/
|
|
140
|
+
transformDef?(src: DefSource, view: ComponentFn): DefSource | void
|
|
141
|
+
|
|
142
|
+
// ------------------------------------------------------------------ instance lifetime
|
|
143
|
+
/** after the instance's state cell and INITIALIZE, before its intent subscribes */
|
|
144
|
+
onCreate?(inst: InstanceView): void
|
|
145
|
+
/** at the start of dispose (DISPOSE has been queued; its streams are not stopped yet) */
|
|
146
|
+
onDispose?(inst: InstanceView): void
|
|
147
|
+
/** after the instance's view ran and its children were injected (only when it re-rendered) */
|
|
148
|
+
onRender?(inst: InstanceView, vnode: any): void
|
|
149
|
+
/** after the root vnode was patched (one per flush that changed the vnode) */
|
|
150
|
+
onPatch?(vnode: any): void
|
|
151
|
+
|
|
152
|
+
// ------------------------------------------------------------------ sources
|
|
153
|
+
/**
|
|
154
|
+
* The sources an instance's intent gets. Return a wrapper (a Proxy that records selectors, a
|
|
155
|
+
* fake for a driverless sink, a checking DOM source). Called once per instance, before the
|
|
156
|
+
* intent; `fake(name)` style sources added here take part in replies/statics detection, which
|
|
157
|
+
* happens after this hook (today testing mutates `sources`/`sourceNames` from onIntent).
|
|
158
|
+
*/
|
|
159
|
+
wrapSources?(inst: InstanceView, sources: Record<string, any>): Record<string, any>
|
|
160
|
+
/** the intent's action names (the wiring checks, inspect()) */
|
|
161
|
+
onIntent?(inst: InstanceView, actionNames: string[]): void
|
|
162
|
+
|
|
163
|
+
// ------------------------------------------------------------------ actions
|
|
164
|
+
/** an action is about to be processed (dequeued); `cause` says where it came from */
|
|
165
|
+
onAction?(inst: InstanceView, action: ActionRecord): void
|
|
166
|
+
/**
|
|
167
|
+
* Wrap one handler of an action. `fn` is the model entry (a function or a constant). Return a
|
|
168
|
+
* replacement with the same contract; ABORT / the same state still mean "no change". Used by
|
|
169
|
+
* the action log (which sinks produced a value), SYG222 checks, devtools.
|
|
170
|
+
*/
|
|
171
|
+
wrapHandler?(inst: InstanceView, type: string, sink: string, fn: any): any
|
|
172
|
+
/** the STATE reducer of an action ran (`next` is what was written; `prev === next`: no change) */
|
|
173
|
+
onReducer?(inst: InstanceView, type: string, prev: any, next: any): void
|
|
174
|
+
/**
|
|
175
|
+
* a non-STATE sink produced a value (before it is stamped with `__emitterId`/`__emitterName`).
|
|
176
|
+
* R4: a declaration static sent to a driver (`{ resources }`, `{ connections }`, `{ timers }`,
|
|
177
|
+
* ...) comes here too, with `type` null
|
|
178
|
+
*/
|
|
179
|
+
onSink?(inst: InstanceView, type: string | null, sink: string, value: any): void
|
|
180
|
+
/**
|
|
181
|
+
* R4 (04 §4 #3): a host's props when it is created and each time its owner re-renders it
|
|
182
|
+
* (`sel`: the marker's, a Collection's or a VirtualCollection's), before it renders; the Collection checks (SYG401
|
|
183
|
+
* for a missing `from`) read them here. `data`: the marker's vnode data (4-H: the buckets a
|
|
184
|
+
* removed wrapper element would have had: style, class, attrs, ...)
|
|
185
|
+
*/
|
|
186
|
+
onHostProps?(owner: InstanceView, sel: string, props: Record<string, any>, data?: Record<string, any>): void
|
|
187
|
+
/** a model next(type, data, ms) was scheduled (replaces testing's NEXT_LOG debug-text parsing) */
|
|
188
|
+
onNext?(inst: InstanceView, type: string, data: any, ms: number): void
|
|
189
|
+
/**
|
|
190
|
+
* an ELEMENT command (or an array of them) was sent; it runs after the next patch (testing
|
|
191
|
+
* records, diagnostics checks). `false`: not run (renderComponent's mock DOM records only)
|
|
192
|
+
*/
|
|
193
|
+
onElementCommand?(inst: InstanceView, command: any): void | false
|
|
194
|
+
|
|
195
|
+
// ------------------------------------------------------------------ state, context, readiness
|
|
196
|
+
/** an instance's (calculated) state changed in this flush (devtools onStateChanged) */
|
|
197
|
+
onStateChanged?(inst: InstanceView, state: any): void
|
|
198
|
+
/** an instance's props changed (devtools onPropsChanged, SYG props checks) */
|
|
199
|
+
onPropsChanged?(inst: InstanceView, props: any): void
|
|
200
|
+
/** an instance's context changed; `keys`: the keys that changed (D168) */
|
|
201
|
+
onContextChanged?(inst: InstanceView, context: any, keys: string[]): void
|
|
202
|
+
/**
|
|
203
|
+
* D174: an `isolatedState` child bound to a slice that already exists (state="key" or a lens,
|
|
204
|
+
* without `resetState`) keeps the parent's data instead of its initialState. R4's dev warning
|
|
205
|
+
* reports the keys `initialState` defines that the slice lacks.
|
|
206
|
+
*/
|
|
207
|
+
onStateSeed?(inst: InstanceView, slice: any, initialState: any): void
|
|
208
|
+
/** D169: a Collection item key (id, or raw index) that appears more than once; only its first element renders. R4's dev warning */
|
|
209
|
+
onDuplicateKey?(owner: InstanceView, key: any): void
|
|
210
|
+
/** a child's READY flag changed (devtools, Suspense diagnostics) */
|
|
211
|
+
onReady?(inst: InstanceView, child: InstanceView, ready: boolean): void
|
|
212
|
+
/**
|
|
213
|
+
* D168 safety net (dev only; R4): context tracking skipped this view's re-render (the context
|
|
214
|
+
* changed, but no key the view read). `render(ctx)` calls the view again with the instance's
|
|
215
|
+
* current inputs and that context (a fresh template, nothing injected or patched), so the
|
|
216
|
+
* consumer can compare `render(prev)` with `render(next)` on a sample of skips and report a
|
|
217
|
+
* difference (sygnal/diagnostics: SYG423). `keysRead`: the keys the last view call read.
|
|
218
|
+
*/
|
|
219
|
+
onContextSkip?(inst: InstanceView, render: (context: any) => any, prev: any, next: any, keysRead: string[]): void
|
|
220
|
+
|
|
221
|
+
// ------------------------------------------------------------------ errors
|
|
222
|
+
/** the app-level error hook (run()'s `onError`, GS-11), with the phase the error came from */
|
|
223
|
+
onError?(error: any, info: {componentName: string; phase: 'view' | 'model' | 'intent' | 'calculated' | 'context' | 'widget' | 'driver'; action?: string}): void
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* What the runtime hands to hooks and tools (devtools, testing, sygnal/element, HMR, Vike). It
|
|
228
|
+
* replaces every `sinks.STATE.shamefullySendNext(...)` and `STATE.stream._v` reach-in.
|
|
229
|
+
*/
|
|
230
|
+
export interface RuntimeAPI {
|
|
231
|
+
/** the root instance (devtools tree, inspect()) */
|
|
232
|
+
readonly root: InstanceView
|
|
233
|
+
/** find an instance by id (devtools selection, time travel) */
|
|
234
|
+
get(id: number): InstanceView | undefined
|
|
235
|
+
/** the current root state, synchronously (today `STATE.stream._v`) */
|
|
236
|
+
getState(): any
|
|
237
|
+
/**
|
|
238
|
+
* Replace the state of `target` (the root, or an instance's own cell: a Collection item, an
|
|
239
|
+
* isolatedState component) as a 'setState' action: queued, applied run-to-completion, one patch.
|
|
240
|
+
* A function gets the current state. Devtools time travel, element prop sync, HMR state
|
|
241
|
+
* restore, Vike page swaps, persist RESTORE.
|
|
242
|
+
*/
|
|
243
|
+
setState(target: 'root' | InstanceView | number, state: any | ((current: any) => any)): void
|
|
244
|
+
/** dispatch an action to an instance, as if its intent emitted it (testing's simulateAction) */
|
|
245
|
+
dispatch(target: 'root' | InstanceView | number, type: string, data?: any, cause?: ActionCause): void
|
|
246
|
+
/** add / remove hooks on a live app (devtools connecting late; sygnal/diagnostics installed after run()) */
|
|
247
|
+
addHooks(hooks: Hooks): () => void
|
|
248
|
+
/** a resolved promise after the current flush (testing's settle without polling) */
|
|
249
|
+
flushed(): Promise<void>
|
|
250
|
+
/** R4: debug logging for one instance on / off (the DevTools toggle; core/debug.ts) */
|
|
251
|
+
setDebug(target: 'root' | InstanceView | number, on: boolean): void
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// ---------------------------------------------------------------------------------- 1. registries
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* A child that renders in place of a marker vnode (Collection, Switchable). The registry entries
|
|
258
|
+
* are core modules: they get the owner's raw instance (R2), unlike hooks.
|
|
259
|
+
*/
|
|
260
|
+
export interface Host {
|
|
261
|
+
/** render with the latest props; return the vnode to inject (the cached one when nothing changed) */
|
|
262
|
+
render(): any
|
|
263
|
+
/** the latest props and children; `marker` / `id`: the marker vnode and its id in the owner */
|
|
264
|
+
setProps(props: Record<string, any>, children: any[], marker?: any, id?: string): void
|
|
265
|
+
dispose(): void
|
|
266
|
+
/** READY of its instances (Suspense); Collection and Switchable stay ready, as today */
|
|
267
|
+
readonly ready: boolean
|
|
268
|
+
/** its instances (byId, InstanceView.children) */
|
|
269
|
+
insts(): any[]
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
export interface Registry {
|
|
273
|
+
/** marker sel -> host factory (Collection, Switchable) */
|
|
274
|
+
hosts: Record<string, (owner: any, props: Record<string, any>, children: any[], id: string, marker: any) => Host>
|
|
275
|
+
/**
|
|
276
|
+
* marker sel -> template rewrite during the reconcile walk (Portal, Transition, ClientOnly,
|
|
277
|
+
* Slot): replace the marker with a plain vnode and keep walking its children.
|
|
278
|
+
*/
|
|
279
|
+
pres: Record<string, (vnode: any, owner: any) => any>
|
|
280
|
+
/** marker sel -> post-processor of the injected vnode of an instance whose template had it (Suspense) */
|
|
281
|
+
posts: Record<string, (vnode: any, owner: any) => any>
|
|
282
|
+
/**
|
|
283
|
+
* component function -> the function to instantiate (lazy: the loaded component once resolved;
|
|
284
|
+
* `owner.refresh()` renders the owner again when it loads)
|
|
285
|
+
*/
|
|
286
|
+
resolvers: Array<(view: ComponentFn, owner: any) => ComponentFn | undefined>
|
|
287
|
+
/** definition time, every app: behaviors (`uses`), undo, selection, pager, persist, resources */
|
|
288
|
+
defHooks: Array<(src: DefSource, view: ComponentFn) => DefSource | void>
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* The statics contract (unchanged from today's generic path, `__sygnalStatic`): a driver source
|
|
293
|
+
* that names a static (`__sygnalStatic: 'timers'`) gets each declaring instance's value of
|
|
294
|
+
* `view[static](state)` on its sink, recomputed on commits that changed that instance's state,
|
|
295
|
+
* dropped when deep-equal to the last (objIsEqual), filtered to `background: true` entries while
|
|
296
|
+
* the instance is on a hidden Switchable page, stamped with `__emitterId` / `__emitterName`.
|
|
297
|
+
* A source with `__sygnalReplies: true` and `replies(emitterId)` sends reply actions back to
|
|
298
|
+
* exactly that instance. Optional `isolateValue(value, scope)` lets a driver scope a sink value
|
|
299
|
+
* without a stream per instance (03 §5); without it the core falls back to `isolateSink`.
|
|
300
|
+
*/
|
|
301
|
+
export interface StaticsSource {
|
|
302
|
+
__sygnalStatic?: string
|
|
303
|
+
__sygnalReplies?: true
|
|
304
|
+
replies?(emitterId: number): any
|
|
305
|
+
isolateSource?(source: any, scope: string): any
|
|
306
|
+
isolateSink?(sink$: any, scope: string): any
|
|
307
|
+
isolateValue?(value: any, scope: string): any
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/** Options of run() the core reads; `hooks` is internal (dev entries, testing), not documented */
|
|
311
|
+
export interface InternalRunOptions {
|
|
312
|
+
/** hooks of this app (testing, devtools, diagnostics install theirs through these) */
|
|
313
|
+
__hooks?: Hooks
|
|
314
|
+
}
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PLAN-4.6 core: the Collection host (03-proposal §2 hosts/, R2). Registered on import (the
|
|
3
|
+
* public `Collection` module imports this file); the core only looks `hosts.collection` up.
|
|
4
|
+
*
|
|
5
|
+
* It stands where the `<Collection>` marker was in its owner's template and renders the items
|
|
6
|
+
* inside the owner's render: no stream of its own, no pickCombine.
|
|
7
|
+
* - `of`: a component function (D163: no names). `from`: a state key (SYG409 on a calculated
|
|
8
|
+
* one), a `{ get, set }` lens (SYG412 without get(); SYG401 for a non-array) or none (the
|
|
9
|
+
* owner's state is the array).
|
|
10
|
+
* - Items are keyed by `id`, an id-less item by its raw index (D169); one key -> index map per
|
|
11
|
+
* array identity serves every item (cell.ts indexer). A duplicate key renders its first element
|
|
12
|
+
* only; the dev warning is R4's (hooks.onDuplicateKey is the hook point).
|
|
13
|
+
* - `filter` / `sort` (every form today's core accepts; SYG418 for an invalid one) change what
|
|
14
|
+
* renders and in which order; the state array keeps its order, and an item writes back to its
|
|
15
|
+
* own element. An item returning `undefined` removes itself.
|
|
16
|
+
* - The other props go to every item, and the Collection's children are each item's children.
|
|
17
|
+
* - 4-H (D229): no element of its own. It renders a keyed fragment of the items' vnodes (its key:
|
|
18
|
+
* the marker's, else its id), which the DOM driver splices into the parent's children before
|
|
19
|
+
* each patch (utils.ts flat): the items are the parent element's own children, and their keys
|
|
20
|
+
* take the fragment's, so two sibling Collections' items never collide. The wrapper `div` of
|
|
21
|
+
* 5.x is gone: `className` / `style` / `class` / ... on the marker are a removed-form error in development
|
|
22
|
+
* (checks/next.ts) and are ignored.
|
|
23
|
+
* - Removed items are disposed synchronously in the render that drops them (G-257: a move between
|
|
24
|
+
* two Collections is one patch).
|
|
25
|
+
* - PLAN-5 A-1: `viewTransitionName="card"` styles each keyed item's root element with
|
|
26
|
+
* `view-transition-name: card-<id>` (shared.ts vtStyle) and `view-transition-class:
|
|
27
|
+
* card`, the item's own style winning, so a `viewTransitions` action animates each item between
|
|
28
|
+
* its places, across Collections with the same prefix too. Id-less items (keyed by index) and
|
|
29
|
+
* fragment roots get none.
|
|
30
|
+
*/
|
|
31
|
+
import {hosts, resolvers} from '../registry'
|
|
32
|
+
import {Inst, shallowEq} from '../instance'
|
|
33
|
+
import {Cell, Index, indexer, itemCell, keyCell, keyOf, keyName} from '../cell'
|
|
34
|
+
import {CoreDef, isObj} from '../define'
|
|
35
|
+
import {uidPart, vtStyle} from '../../shared'
|
|
36
|
+
import {viewOf} from '../view'
|
|
37
|
+
import {warn, error as logError, fail} from '../../extra/diagnostics/legacy'
|
|
38
|
+
|
|
39
|
+
const SORT_FIX = "Use a field name, { field: 'asc'|'desc'|1|-1 }, or a function"
|
|
40
|
+
const sortError = (msg: string, value: any): undefined => void logError('SYG418', 'Collection', msg, SORT_FIX, value)
|
|
41
|
+
const base = (a: any, b: any, asc = true) => (a > b ? (asc ? 1 : -1) : a < b ? (asc ? -1 : 1) : 0)
|
|
42
|
+
|
|
43
|
+
function fromObj(item: Record<string, any>): ((a: any, b: any) => number) | undefined {
|
|
44
|
+
const e = Object.entries(item)
|
|
45
|
+
if (e.length > 1) return sortError('sort object must have one key; ignored', item)
|
|
46
|
+
const [f, d] = e[0]
|
|
47
|
+
let asc
|
|
48
|
+
if (typeof d == 'string') {
|
|
49
|
+
const x = d.toLowerCase()
|
|
50
|
+
if (x != 'asc' && x != 'desc') return sortError("sort direction must be 'asc' or 'desc'; ignored", item)
|
|
51
|
+
asc = x == 'asc'
|
|
52
|
+
} else if (typeof d == 'number') {
|
|
53
|
+
if (d !== 1 && d !== -1) return sortError('sort direction must be 1 or -1; ignored', item)
|
|
54
|
+
asc = d === 1
|
|
55
|
+
} else return sortError('sort direction must be a string or number; ignored', item)
|
|
56
|
+
return (a, b) => base(a[f], b[f], asc)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** the comparator of a `sort` prop (today's sortFunctionFromProp) */
|
|
60
|
+
export function sortFn(p: any): ((a: any, b: any) => number) | undefined {
|
|
61
|
+
if (!p) return
|
|
62
|
+
if (typeof p == 'function') return p
|
|
63
|
+
if (typeof p == 'string') {
|
|
64
|
+
const d = p.toLowerCase()
|
|
65
|
+
if (d == 'asc' || d == 'desc') return (a, b) => base(a, b, d == 'asc')
|
|
66
|
+
return (a, b) => base(a[p], b[p])
|
|
67
|
+
}
|
|
68
|
+
if (Array.isArray(p)) {
|
|
69
|
+
const fs: any[] = p.map(i => typeof i == 'function' ? i
|
|
70
|
+
: typeof i == 'string' ? (['asc', 'desc'].includes(i.toLowerCase()) ? undefined : (a: any, b: any) => base(a[i], b[i]))
|
|
71
|
+
: isObj(i) ? fromObj(i) : undefined).filter(f => typeof f == 'function')
|
|
72
|
+
return (a, b) => fs.reduce((r, f) => (r !== 0 ? r : f(a, b)), 0)
|
|
73
|
+
}
|
|
74
|
+
if (isObj(p)) return fromObj(p)
|
|
75
|
+
return sortError('Invalid sort prop; ignored', p)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** the Collection's own props (`idfield`, removed in 6.0 (D164), is still kept off the items) */
|
|
79
|
+
const OWN = new Set(['of', 'from', 'filter', 'sort', 'idfield', 'className', 'viewTransitionName'])
|
|
80
|
+
const NONE: any[] = []
|
|
81
|
+
|
|
82
|
+
/** the array cell for `from` (D178: a missing key renders once it appears), or null for an invalid `from` (renders nothing) */
|
|
83
|
+
function arrayCell(owner: Inst, from: any): Cell | null {
|
|
84
|
+
const oc = owner.cell, name = owner.def.name
|
|
85
|
+
if (from === undefined) {
|
|
86
|
+
// the owner's state is the array (or its `value`, as today)
|
|
87
|
+
return {
|
|
88
|
+
get: () => { const s = oc.get(); return !Array.isArray(s) && Array.isArray(s?.value) ? s.value : s },
|
|
89
|
+
set: (v) => oc.set(v),
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (typeof from == 'string') {
|
|
93
|
+
const cs = oc.get(), calc = owner.def.calcNames?.has(from)
|
|
94
|
+
if (isObj(cs) && !(from in cs) && !calc) {
|
|
95
|
+
const arrays = Object.keys(cs).filter(k => Array.isArray(cs[k]))
|
|
96
|
+
warn('SYG401', name, `Collection from="${from}" is not in state${arrays.length ? ` (array fields: '${arrays.join("', '")}')` : ''}; it renders nothing until it exists`, 'Set it to an array in initialState')
|
|
97
|
+
} else if (isObj(cs) && !Array.isArray(cs[from])) warn('SYG401', name, `Collection 'from' field '${from}' is not an array; it renders nothing`, 'Set it to an array in initialState')
|
|
98
|
+
return keyCell(oc, from, calc && name, undefined, 'Collection')
|
|
99
|
+
}
|
|
100
|
+
if (isObj(from) && typeof from.get == 'function') {
|
|
101
|
+
let lp: any = {}, lv: any
|
|
102
|
+
return {
|
|
103
|
+
get() {
|
|
104
|
+
const p = oc.get()
|
|
105
|
+
if (p !== lp) {
|
|
106
|
+
lp = p
|
|
107
|
+
let g: any
|
|
108
|
+
try { g = from.get(p) } catch (e) {
|
|
109
|
+
// G-298: keeps the items it had
|
|
110
|
+
logError('SYG412', name, "Collection 'from' getter threw; it keeps its items", 'Guard the getter against missing data', e)
|
|
111
|
+
owner.app.appError(owner, e, 'view')
|
|
112
|
+
return lv
|
|
113
|
+
}
|
|
114
|
+
lv = g
|
|
115
|
+
if (!Array.isArray(lv)) { warn('SYG401', name, "Collection 'from' getter returned a non-array; it renders nothing", 'Return an array from get()', lv); lv = NONE }
|
|
116
|
+
}
|
|
117
|
+
return lv
|
|
118
|
+
},
|
|
119
|
+
set(v) {
|
|
120
|
+
if (typeof from.set != 'function') return
|
|
121
|
+
const p = oc.get(), n = from.set(p, v)
|
|
122
|
+
if (n !== p) oc.set(n)
|
|
123
|
+
},
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
logError('SYG412', name, "Collection 'from' prop is invalid; it renders nothing", 'Use a state key string or { get, set }', from)
|
|
127
|
+
return null
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** a component function through the view resolvers (lazy: the loaded one; the owner renders again when it loads) */
|
|
131
|
+
export function resolve(view: any, owner: Inst) {
|
|
132
|
+
for (const r of resolvers) view = r(view, owner) || view
|
|
133
|
+
return view
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export class CollectionHost {
|
|
137
|
+
/** key -> item instance */
|
|
138
|
+
items = new Map<any, Inst>()
|
|
139
|
+
/** the items shown, in render order */
|
|
140
|
+
shown: Inst[] = []
|
|
141
|
+
arr: Cell | null
|
|
142
|
+
index: Index | null
|
|
143
|
+
def!: CoreDef
|
|
144
|
+
ready = true
|
|
145
|
+
disposed = false
|
|
146
|
+
outv: any; last: any; lr = true
|
|
147
|
+
ip: Record<string, any> = {}
|
|
148
|
+
kids: any[] = NONE
|
|
149
|
+
props!: Record<string, any>
|
|
150
|
+
// the inputs of the last item list: array, filter, sort prop, comparator
|
|
151
|
+
la: any; lf: any; ls: any; cmp: any
|
|
152
|
+
uidBase: string
|
|
153
|
+
/** the fragment's key (the marker's, else the Collection's id) */
|
|
154
|
+
key: any
|
|
155
|
+
tr: any
|
|
156
|
+
/** the app's render epoch it last rendered at (G-311) */
|
|
157
|
+
ep = 0
|
|
158
|
+
|
|
159
|
+
constructor(public owner: Inst, props: Record<string, any>, children: any[], id: string, marker: any) {
|
|
160
|
+
this.hostProps(props, marker)
|
|
161
|
+
const of = props.of
|
|
162
|
+
if (!of) fail('SYG411', owner, "Collection is missing 'of'", 'Use of={ItemComponent}')
|
|
163
|
+
if (typeof of != 'function') fail('SYG411', owner, `Collection 'of' is a ${typeof of}`, 'Use of={ItemComponent}')
|
|
164
|
+
this.uidBase = owner.uid(uidPart(id.replace(/.*::(r\.)?/, '')))
|
|
165
|
+
this.arr = arrayCell(owner, props.from)
|
|
166
|
+
this.index = this.arr && indexer(this.arr)
|
|
167
|
+
this.def = owner.app.def(this.view = resolve(of, owner))
|
|
168
|
+
this.setProps(props, children, marker, id)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* onHostProps (R4): the Collection checks of the dev entry (SYG401 for a missing `from`, D173's
|
|
173
|
+
* string `of`, 4-H's wrapper props: the marker's data); the marker's sel tells a
|
|
174
|
+
* VirtualCollection from a Collection
|
|
175
|
+
*/
|
|
176
|
+
hostProps(props: Record<string, any>, m: any) {
|
|
177
|
+
const H = this.owner.app.hooks
|
|
178
|
+
if (H.onHostProps) H.onHostProps(viewOf(this.owner), m.sel, props, m.data)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** the item component (a lazy() one: the loaded component once it has loaded, G-317) */
|
|
182
|
+
view: any
|
|
183
|
+
|
|
184
|
+
setProps(props: Record<string, any>, children: any[], marker?: any, id?: string) {
|
|
185
|
+
// (the constructor called the hook before its own checks)
|
|
186
|
+
if (this.props) this.hostProps(props, marker)
|
|
187
|
+
const v = typeof props.of == 'function' ? resolve(props.of, this.owner) : this.view
|
|
188
|
+
if (v !== this.view) {
|
|
189
|
+
// another item component (or a lazy one loaded): the items are made again
|
|
190
|
+
this.def = this.owner.app.def(this.view = v)
|
|
191
|
+
this.clear()
|
|
192
|
+
}
|
|
193
|
+
this.props = props
|
|
194
|
+
if (marker) {
|
|
195
|
+
// tr: a <Transition> around it, applied to each item (4-I G-559, markers/transition.ts):
|
|
196
|
+
// the fragment is made again on each of the owner's renders while there is one (or was)
|
|
197
|
+
const k = marker.key ?? id
|
|
198
|
+
if (this.tr !== (this.tr = marker.data?.tr) || k !== this.key) { this.key = k; this.outv = undefined }
|
|
199
|
+
}
|
|
200
|
+
const ip: Record<string, any> = {}
|
|
201
|
+
for (const k in props) if (!OWN.has(k)) ip[k] = props[k]
|
|
202
|
+
const kids = children.length ? children : NONE
|
|
203
|
+
if (!shallowEq(ip, this.ip) || kids !== this.kids) {
|
|
204
|
+
this.ip = ip
|
|
205
|
+
this.kids = kids
|
|
206
|
+
this.items.forEach(i => i.setProps(ip, kids))
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
clear() {
|
|
211
|
+
this.items.forEach(i => i.dispose())
|
|
212
|
+
this.items.clear()
|
|
213
|
+
this.shown = []
|
|
214
|
+
this.la = undefined
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/** the item instances in the order shown (byId, InstanceView.children) */
|
|
218
|
+
insts(): Inst[] { return this.shown }
|
|
219
|
+
|
|
220
|
+
/** the visible keys in order: filter and sort over the raw array; a duplicate key once */
|
|
221
|
+
list(a: any[], m: Map<any, number>): any[] {
|
|
222
|
+
const {filter, sort} = this.props
|
|
223
|
+
const cmp = sort !== this.ls ? sortFn(sort) : this.cmp
|
|
224
|
+
let idx: number[] | null = null
|
|
225
|
+
if (typeof filter == 'function' || cmp) {
|
|
226
|
+
idx = []
|
|
227
|
+
for (let i = 0; i < a.length; i++) if (typeof filter != 'function' || filter(a[i], i, a)) idx.push(i)
|
|
228
|
+
if (cmp) idx.sort((x, y) => cmp(a[x], a[y]))
|
|
229
|
+
}
|
|
230
|
+
// G-311: the inputs are kept only once the list was made (a throwing filter / sort is retried)
|
|
231
|
+
this.ls = sort; this.cmp = cmp
|
|
232
|
+
const n = idx ? idx.length : a.length, keys: any[] = []
|
|
233
|
+
for (let x = 0; x < n; x++) {
|
|
234
|
+
const i = idx ? idx[x] : x, k = keyOf(a[i], i)
|
|
235
|
+
// D169: a duplicate key renders its first element (R4 reports it)
|
|
236
|
+
if (m.get(k) !== i) { const H = this.owner.app.hooks; H.onDuplicateKey && H.onDuplicateKey(viewOf(this.owner), k[0] == '\0' ? i : a[i].id); continue }
|
|
237
|
+
keys.push(k)
|
|
238
|
+
}
|
|
239
|
+
return keys
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* G-319: on a hidden Switchable page (no render): the items follow the array (removed ones are
|
|
244
|
+
* disposed, new ones created, so their background statics and replies run), with no view call
|
|
245
|
+
*/
|
|
246
|
+
sync() {
|
|
247
|
+
if (this.disposed) return
|
|
248
|
+
this.items_()
|
|
249
|
+
for (const i of this.shown) i.sync()
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/** the item instances for the current array, filter and sort; true when the list changed */
|
|
253
|
+
items_(): boolean {
|
|
254
|
+
if (!this.index) return false
|
|
255
|
+
const app = this.owner.app
|
|
256
|
+
let changed = false
|
|
257
|
+
{
|
|
258
|
+
const [a, m] = this.index()
|
|
259
|
+
const {filter, sort} = this.props
|
|
260
|
+
if (a !== this.la || filter !== this.lf || sort !== this.ls) {
|
|
261
|
+
const keys = this.list(a, m), seen = new Set(keys)
|
|
262
|
+
this.la = a; this.lf = filter
|
|
263
|
+
for (const [k, inst] of this.items) if (!seen.has(k)) { inst.dispose(); this.items.delete(k) }
|
|
264
|
+
const o = this.owner, shown: Inst[] = []
|
|
265
|
+
for (const k of keys) {
|
|
266
|
+
let inst = this.items.get(k)
|
|
267
|
+
if (!inst) {
|
|
268
|
+
const scope = app.scope()
|
|
269
|
+
inst = new Inst(app, this.def, o, itemCell(this.arr!, this.index, k), o.dom && o.dom.isolateSource(o.dom, scope),
|
|
270
|
+
this.ip, this.kids, scope, this.uidBase + '-' + keyName(k), 'item')
|
|
271
|
+
this.items.set(k, inst);
|
|
272
|
+
// its key (A-1's view-transition name)
|
|
273
|
+
(inst as any).k = k
|
|
274
|
+
}
|
|
275
|
+
shown.push(inst)
|
|
276
|
+
}
|
|
277
|
+
if (shown.length != this.shown.length || shown.some((s, i) => s !== this.shown[i])) changed = true
|
|
278
|
+
this.shown = shown
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
return changed
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
render(): any {
|
|
285
|
+
if (this.disposed) return this.outv
|
|
286
|
+
// G-311: after a render that threw, every container is made again once
|
|
287
|
+
const app = this.owner.app
|
|
288
|
+
let changed = !this.outv || this.ep !== app.ep
|
|
289
|
+
this.ep = app.ep
|
|
290
|
+
if (this.items_()) changed = true
|
|
291
|
+
const shown = this.shown, out: any[] = Array(shown.length), vn = this.props.viewTransitionName
|
|
292
|
+
let j = 0
|
|
293
|
+
for (let i = 0; i < shown.length; i++) {
|
|
294
|
+
const inst: any = shown[i], v = inst.render()
|
|
295
|
+
if (v !== inst.last) { inst.last = v; changed = true }
|
|
296
|
+
// an item without state yet (or a removed one) is left out
|
|
297
|
+
if (v !== undefined) out[j++] = this.tr ? this.tr(vn ? named(inst, v, vn) : v) : vn ? named(inst, v, vn) : v
|
|
298
|
+
}
|
|
299
|
+
out.length = j
|
|
300
|
+
if (!changed) return this.outv
|
|
301
|
+
return (this.outv = {sel: undefined, data: {}, children: out, text: undefined, elm: undefined, key: this.key})
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
dispose() {
|
|
305
|
+
if (this.disposed) return
|
|
306
|
+
this.disposed = true
|
|
307
|
+
this.clear()
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* A-1: the item's vnode with its view-transition name (cached per item, so an unchanged item
|
|
313
|
+
* keeps its vnode). VirtualCollection's rows too (extra/virtual.ts, G-417)
|
|
314
|
+
*/
|
|
315
|
+
export function named(inst: any, v: any, p: string): any {
|
|
316
|
+
const k = inst.k
|
|
317
|
+
if (!v.sel || k[0] == '\0') return v
|
|
318
|
+
if (inst.vi !== v || inst.vp !== p) {
|
|
319
|
+
inst.vi = v; inst.vp = p
|
|
320
|
+
inst.vo = {...v, data: {...v.data, style: vtStyle(p, k, v.data?.style)}}
|
|
321
|
+
}
|
|
322
|
+
return inst.vo
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
hosts.collection = (owner, props, children, id, marker) => new CollectionHost(owner, props, children, id, marker)
|