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
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import {Driver} from '../run/types';
|
|
2
|
-
import {init, Module, Options as SnabbdomOptions, VNode
|
|
2
|
+
import {init, Module, Options as SnabbdomOptions, VNode} from './snabbdom';
|
|
3
3
|
import xs, {Stream, Listener} from 'xstream';
|
|
4
|
-
import concat from 'xstream/extra/concat.js';
|
|
5
|
-
import sampleCombine from 'xstream/extra/sampleCombine.js';
|
|
6
4
|
import {MainDOMSource} from './MainDOMSource';
|
|
7
5
|
import {VNodeWrapper} from './VNodeWrapper';
|
|
8
|
-
import {getValidNode, checkValidContainer} from './utils';
|
|
6
|
+
import {getValidNode, checkValidContainer, POKE, flat} from './utils';
|
|
9
7
|
import defaultModules from './modules';
|
|
10
8
|
import {IsolateModule} from './IsolateModule';
|
|
11
9
|
import {EventDelegator} from './EventDelegator';
|
|
@@ -37,17 +35,11 @@ export interface DOMDriverOptions {
|
|
|
37
35
|
snabbdomOptions?: SnabbdomOptions;
|
|
38
36
|
}
|
|
39
37
|
|
|
40
|
-
function dropCompletion<T>(input: Stream<T>): Stream<T> {
|
|
41
|
-
return xs.merge(input, xs.never());
|
|
42
|
-
}
|
|
43
|
-
|
|
44
38
|
function unwrapElementFromVNode(vnode: VNode): Element {
|
|
45
39
|
return vnode.elm as Element;
|
|
46
40
|
}
|
|
47
41
|
|
|
48
|
-
|
|
49
|
-
(console.error || console.log)(err);
|
|
50
|
-
}
|
|
42
|
+
const ERR = 'data-sygnal-error';
|
|
51
43
|
|
|
52
44
|
function makeDOMReady$(): Stream<null> {
|
|
53
45
|
return xs.create<null>({
|
|
@@ -69,11 +61,64 @@ function makeDOMReady$(): Stream<null> {
|
|
|
69
61
|
});
|
|
70
62
|
}
|
|
71
63
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
64
|
+
// G-456 (D217): the app's first patch adopts markup already in the mount point (server HTML, an
|
|
65
|
+
// Astro island, a Vike page). Its old vnode is built from the DOM with the client's vnode as the
|
|
66
|
+
// template, by position (no toVNode): an element with the client's tag takes the client's
|
|
67
|
+
// selector and key (so component roots and Collection items match), and its data is what the
|
|
68
|
+
// modules need to reach the client's from what the server wrote: the dataset (stale keys go:
|
|
69
|
+
// data-sygnal-ssr), the class as the className prop (the className module drops stale classes),
|
|
70
|
+
// and each attribute the client sets as an attribute or a prop, with the server's value (nothing
|
|
71
|
+
// is written again: an iframe's src isn't reloaded). Any other attribute is removed now; so is a
|
|
72
|
+
// style (G-483: the style module writes the client's declarations, so none of the server's
|
|
73
|
+
// stays). A single text child keeps its text node. Whitespace and comments where the client has
|
|
74
|
+
// no text go; any other node that doesn't match stays as a placeholder that snabbdom replaces
|
|
75
|
+
// with the client's node, in place (the lists stay aligned, so the nodes after it are still
|
|
76
|
+
// adopted). Made again that way, as a fresh render makes them: an element whose selector has a
|
|
77
|
+
// class or id (G-486: `h('p.card')`, the Portal placeholder; patching never corrects them), and
|
|
78
|
+
// one whose hook expects a new element (G-485): a create or init hook (a thunk), an insert hook
|
|
79
|
+
// without a postpatch (a Transition's enter, a measured row, the toaster region, a lazy()
|
|
80
|
+
// placeholder), and any insert hook of the user's own (G-521: with a postpatch too; `u` when a
|
|
81
|
+
// ref, autoFocus or widget chained theirs after it; `s` marks a function chainHooks made)
|
|
82
|
+
const adopt = (e: any, v: any): any => {
|
|
83
|
+
// (a hole, `{cond && <X/>}`, has no node: snabbdom skips it. G-481: no fragment either, they
|
|
84
|
+
// are flat by now: their nodes pair with their children in their parent's run)
|
|
85
|
+
const c = (v.children || []).filter((z: any) => z), out: any[] = [];
|
|
86
|
+
for (let j = 0, x = e.firstChild, y: any; x; x = y) {
|
|
87
|
+
const w = c[j], t = x.nodeType, d = w?.data || {}, k = d.hook;
|
|
88
|
+
// a node that doesn't match stays in its place under a selector no vnode has: snabbdom makes
|
|
89
|
+
// the client's node before it and removes it (the lists stay aligned: no other node is paired)
|
|
90
|
+
let n: any = {sel: '', data: {}, elm: x};
|
|
91
|
+
// G-520: client text vnodes next to each other (`Hello, {name}!`) are one server text node:
|
|
92
|
+
// it is split by the client's text when that is its start, so the nodes after stay paired
|
|
93
|
+
t == 3 && c[j + 1]?.text != null && !c[j + 1].sel && w?.text && x.data.startsWith(w.text) && x.splitText(w.text.length);
|
|
94
|
+
y = x.nextSibling;
|
|
95
|
+
if (t == 3 && w && !w.sel && w.text != null) n = {text: x.data, elm: x};
|
|
96
|
+
else if (t == 1 && x.localName == w?.sel && !(k && (k.create || k.init || k.u || k.insert && !(k.postpatch && k.insert.s)))) {
|
|
97
|
+
const p = d.props || {}, a = d.attrs || {}, o: any = {}, at: any = {}, pr: any = {}, f = x.firstChild;
|
|
98
|
+
o.class = 'className';
|
|
99
|
+
for (const m in p) o[m == 'htmlFor' ? 'for' : m.toLowerCase()] = m;
|
|
100
|
+
// G-482: a data-* attribute the client sets as an attribute isn't in the dataset (the
|
|
101
|
+
// dataset module would remove it): it goes, and the attributes module writes it again.
|
|
102
|
+
// G-487: `open` (a <details> the user opened) stays when the client doesn't render it
|
|
103
|
+
for (const {name: m, value: y} of [...x.attributes])
|
|
104
|
+
/^data-/.test(m) ? m in a && x.removeAttribute(m)
|
|
105
|
+
: m in a ? at[m] = y
|
|
106
|
+
: m == 'open' ? 0
|
|
107
|
+
: m in o && !d.ns ? pr[o[m]] = y
|
|
108
|
+
: x.removeAttribute(m);
|
|
109
|
+
// G-484: a textarea's value no longer follows its text, which goes
|
|
110
|
+
x.localName == 'textarea' && (x.value = x.value);
|
|
111
|
+
const s = w.text != null && f?.nodeType == 3 && !f.nextSibling;
|
|
112
|
+
n = {sel: w.sel, key: w.key, data: {dataset: {...x.dataset}, attrs: at, props: pr}, children: s ? undefined : adopt(x, w), text: s ? f.data : undefined, elm: x};
|
|
113
|
+
} else if (t != 1 && (t != 3 || !/\S/.test(x.data))) {
|
|
114
|
+
x.remove();
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
out.push(n);
|
|
118
|
+
j++;
|
|
119
|
+
}
|
|
120
|
+
return out;
|
|
121
|
+
};
|
|
77
122
|
|
|
78
123
|
function makeDOMDriver(
|
|
79
124
|
container: string | Element | DocumentFragment,
|
|
@@ -87,66 +132,79 @@ function makeDOMDriver(
|
|
|
87
132
|
const patch = init([isolateModule.createModule() as Partial<Module>].concat(modules), undefined, snabbdomOptions);
|
|
88
133
|
const domReady$ = makeDOMReady$();
|
|
89
134
|
let vnodeWrapper: VNodeWrapper;
|
|
90
|
-
let mutationObserver: MutationObserver;
|
|
91
|
-
const mutationConfirmed$ = xs.create<null>({
|
|
92
|
-
start(listener) {
|
|
93
|
-
mutationObserver = new MutationObserver(() => listener.next(null));
|
|
94
|
-
},
|
|
95
|
-
stop() {
|
|
96
|
-
mutationObserver.disconnect();
|
|
97
|
-
},
|
|
98
|
-
});
|
|
99
135
|
|
|
100
136
|
function DOMDriver(vnode$: Stream<VNode>, name = 'DOM'): MainDOMSource {
|
|
101
137
|
domDriverInputGuard(vnode$);
|
|
102
138
|
const sanitation$ = xs.create<null>();
|
|
103
139
|
|
|
140
|
+
let r: any;
|
|
104
141
|
const firstRoot$ = domReady$.map(() => {
|
|
105
142
|
const firstRoot = getValidNode(container) || document.body;
|
|
106
143
|
vnodeWrapper = new VNodeWrapper(firstRoot);
|
|
144
|
+
r = {sel: '', data: {isolate: []}, elm: firstRoot};
|
|
107
145
|
return firstRoot;
|
|
108
146
|
});
|
|
109
147
|
|
|
110
148
|
const rememberedVNode$ = vnode$.remember();
|
|
111
149
|
rememberedVNode$.addListener({});
|
|
112
150
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
151
|
+
// P45-C (rec 9, D146): the root element, when the DOM is ready and then after each patch
|
|
152
|
+
// (it was a MutationObserver on the root's subtree: DOM changes made outside a patch no
|
|
153
|
+
// longer emit)
|
|
154
|
+
// G-261: and when Sygnal changes the DOM outside a patch (a Transition's leave, a Portal
|
|
155
|
+
// mounted late): an event that bubbles up to the root. G-276: on the current root (a patch
|
|
156
|
+
// can replace the first one). G-277: the innermost app's root takes it (a nested app or
|
|
157
|
+
// custom element doesn't re-emit the outer app's DOM source)
|
|
158
|
+
let pl: any, cur: any;
|
|
159
|
+
const poke = (e: Event) => pl && (e.stopPropagation(), pl.next(cur));
|
|
160
|
+
const off = () => cur?.removeEventListener(POKE, poke);
|
|
161
|
+
// G-545: run() gives any DOM driver its reporter (onError, phase 'patch') on the isolate
|
|
162
|
+
// module; an explicit reportSnabbdomError comes first
|
|
163
|
+
const rep = (e: any) => (options.reportSnabbdomError || (isolateModule as any).rep || console.error)(e);
|
|
164
|
+
let dead = 0, end = 0;
|
|
165
|
+
const rootElement$ = firstRoot$
|
|
116
166
|
.map(
|
|
117
|
-
firstRoot =>
|
|
118
|
-
xs
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
167
|
+
(firstRoot, P = (o: any, v: any) => (v = flat(v), patch(o == r ? (cur.removeAttribute?.(ERR), {...r, sel: v.sel, key: v.key, children: adopt(firstRoot, v)}) : o, v))) =>
|
|
168
|
+
xs.merge(
|
|
169
|
+
xs
|
|
170
|
+
.merge(rememberedVNode$.endWhen(sanitation$), sanitation$.map(() => (end = 1, null)))
|
|
171
|
+
.map(vnode => vnodeWrapper.call(vnode))
|
|
172
|
+
// the first step gives the root its scope; the second, the app's first patch,
|
|
173
|
+
// adopts the markup in it (G-456). The root keeps its own attributes (G-466)
|
|
174
|
+
.startWith(r)
|
|
175
|
+
// G-540: a patch that throws (a hook, a module) is reported once, and the app's DOM
|
|
176
|
+
// stops updating: snabbdom stopped half way (some elements patched, created or
|
|
177
|
+
// removed, the rest not; insert hooks not run), and no vnode tree matches that DOM
|
|
178
|
+
// (patching on from either tree, or adopting the DOM again, left wrong or duplicated
|
|
179
|
+
// DOM). The stream doesn't end (its events, the root element), and a patch to no
|
|
180
|
+
// children (dispose) still runs, so what the last good tree mounted (a Portal's
|
|
181
|
+
// content) is removed; its errors aren't reported again. G-549: only dispose (`end`:
|
|
182
|
+
// the sanitation signal), not a live render that has no children. G-543 (D224): the
|
|
183
|
+
// root element is marked data-sygnal-error="patch" while its DOM is stopped (CSS can
|
|
184
|
+
// show it; dispose removes it). G-550: marked before the error is reported (onError
|
|
185
|
+
// can read it); the first patch clears a mark a failed, undisposed app left in the
|
|
186
|
+
// container, and dispose removes it only when this app set it; a throw while
|
|
187
|
+
// flattening the tree (a key JSON.stringify can't take) is a patch error too (P
|
|
188
|
+
// flattens). A DocumentFragment mount point has no attributes: no mark
|
|
189
|
+
.fold((o: any, v: any) => {
|
|
190
|
+
if (!dead || end) try { o = P(o, v) } catch (e) { dead++ || (cur.setAttribute?.(ERR, 'patch'), rep(e)) }
|
|
191
|
+
end && dead && cur.removeAttribute?.(ERR)
|
|
192
|
+
return o
|
|
193
|
+
}, {...r, data: {}})
|
|
194
|
+
.drop(1)
|
|
195
|
+
.map(unwrapElementFromVNode)
|
|
196
|
+
.startWith(firstRoot as any)
|
|
197
|
+
.map((el: any) => (off(), el.addEventListener(POKE, poke), cur = el)),
|
|
198
|
+
// never completes (nor does the root element). G-285: the listener moves with the
|
|
199
|
+
// root and goes when the app stops (a disposed app's driver isn't kept by its root)
|
|
200
|
+
xs.create<any>({start: l => pl = l, stop: () => (pl = 0, off())})
|
|
201
|
+
)
|
|
138
202
|
)
|
|
139
|
-
.flatten()
|
|
140
|
-
|
|
141
|
-
const rootElement$ = concat(domReady$, mutationConfirmed$)
|
|
203
|
+
.flatten()
|
|
142
204
|
.endWhen(sanitation$)
|
|
143
|
-
.compose(sampleCombine(elementAfterPatch$))
|
|
144
|
-
.map(arr => arr[1])
|
|
145
205
|
.remember();
|
|
146
206
|
|
|
147
|
-
rootElement$.addListener({
|
|
148
|
-
error: options.reportSnabbdomError || defaultReportSnabbdomError,
|
|
149
|
-
});
|
|
207
|
+
rootElement$.addListener({error: rep});
|
|
150
208
|
|
|
151
209
|
const delegator = new EventDelegator(rootElement$, isolateModule);
|
|
152
210
|
|
|
@@ -3,6 +3,22 @@ import {DevToolEnabledSource, FantasyObservable} from '../run/types';
|
|
|
3
3
|
import {VNode} from './snabbdom';
|
|
4
4
|
import {EventsFnOptions} from './DOMSource';
|
|
5
5
|
import {adapt} from '../run/adapt';
|
|
6
|
+
import {enrichEventStream} from './enrichEventStream';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Optional simulated-event hub (used by renderComponent's simulateEvent): a
|
|
10
|
+
* stream of `{type, event, match(path)}`; each source's events(type) also emits
|
|
11
|
+
* the hub events whose `match` accepts its selector path (isolation scopes
|
|
12
|
+
* included as '.___scope' segments).
|
|
13
|
+
*/
|
|
14
|
+
export type MockEventHub = Stream<{type: string; event: any; match: (path: string[]) => boolean}>;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Optional listener callback (renderComponent): called with `live` undefined when events() is
|
|
18
|
+
* called (SYG103/104 checks), then with true / false when that hub listener is subscribed /
|
|
19
|
+
* unsubscribed (simulateEvent waits for a just-mounted child's listeners, G-039).
|
|
20
|
+
*/
|
|
21
|
+
export type MockOnEvents = (path: string[], eventType: string, live?: boolean) => void;
|
|
6
22
|
|
|
7
23
|
export type MockConfig = {
|
|
8
24
|
[name: string]: FantasyObservable<any> | MockConfig;
|
|
@@ -13,7 +29,12 @@ const SCOPE_PREFIX = '___';
|
|
|
13
29
|
export class MockedDOMSource {
|
|
14
30
|
private _elements: FantasyObservable<any>;
|
|
15
31
|
|
|
16
|
-
constructor(
|
|
32
|
+
constructor(
|
|
33
|
+
private _mockConfig: MockConfig,
|
|
34
|
+
private _hub?: MockEventHub,
|
|
35
|
+
public _path: string[] = [],
|
|
36
|
+
private _onEvents?: MockOnEvents
|
|
37
|
+
) {
|
|
17
38
|
if (_mockConfig.elements) {
|
|
18
39
|
this._elements = _mockConfig.elements as FantasyObservable<any>;
|
|
19
40
|
} else {
|
|
@@ -43,20 +64,54 @@ export class MockedDOMSource {
|
|
|
43
64
|
options?: EventsFnOptions,
|
|
44
65
|
bubbles?: boolean
|
|
45
66
|
): any {
|
|
46
|
-
const
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
67
|
+
const configured = this._mockConfig[eventType] as any;
|
|
68
|
+
const {_hub: hub, _path: path, _onEvents: on} = this;
|
|
69
|
+
if (on) on(path, eventType);
|
|
70
|
+
let hub$: Stream<any> | undefined;
|
|
71
|
+
if (hub) {
|
|
72
|
+
const ev$ = hub.filter(e => e.type === eventType && e.match(path)).map(e => e.event);
|
|
73
|
+
let l: any;
|
|
74
|
+
hub$ = on
|
|
75
|
+
? xs.create({
|
|
76
|
+
start: (x: any) => {
|
|
77
|
+
ev$.addListener(l = {next: (v: any) => x.next(v), error: (e: any) => x.error(e), complete: () => x.complete()});
|
|
78
|
+
on(path, eventType, true);
|
|
79
|
+
},
|
|
80
|
+
stop: () => {
|
|
81
|
+
ev$.removeListener(l);
|
|
82
|
+
on(path, eventType, false);
|
|
83
|
+
},
|
|
84
|
+
})
|
|
85
|
+
: ev$;
|
|
86
|
+
}
|
|
87
|
+
const out: DevToolEnabledSource & FantasyObservable<any> = enrichEventStream(adapt(
|
|
88
|
+
hub$
|
|
89
|
+
? xs.merge(configured ? xs.fromObservable(configured) : xs.empty(), hub$)
|
|
90
|
+
: configured || xs.empty()
|
|
91
|
+
));
|
|
50
92
|
|
|
51
93
|
out._isCycleSource = 'MockedDOM';
|
|
52
94
|
|
|
53
95
|
return out;
|
|
54
96
|
}
|
|
55
97
|
|
|
56
|
-
public select(selector:
|
|
98
|
+
public select(selector: any): MockedDOMSource {
|
|
99
|
+
// CT-1: a control selects by its marker; a component (any other function) is rejected
|
|
100
|
+
// like the real DOM source does (SYG124 in the dev entry names the fixes)
|
|
101
|
+
if (typeof selector == 'function') {
|
|
102
|
+
if (!selector.__sygnalControl) {
|
|
103
|
+
throw new Error(`DOM driver's select() expects the argument to be a string as a CSS selector`);
|
|
104
|
+
}
|
|
105
|
+
selector = '' + selector;
|
|
106
|
+
}
|
|
57
107
|
const mockConfigForSelector = this._mockConfig[selector] || {};
|
|
58
108
|
|
|
59
|
-
return new MockedDOMSource(
|
|
109
|
+
return new MockedDOMSource(
|
|
110
|
+
mockConfigForSelector as MockConfig,
|
|
111
|
+
this._hub,
|
|
112
|
+
this._path.concat(selector),
|
|
113
|
+
this._onEvents
|
|
114
|
+
);
|
|
60
115
|
}
|
|
61
116
|
|
|
62
117
|
public isolateSource(
|
|
@@ -66,9 +121,24 @@ export class MockedDOMSource {
|
|
|
66
121
|
return source.select('.' + SCOPE_PREFIX + scope);
|
|
67
122
|
}
|
|
68
123
|
|
|
124
|
+
/**
|
|
125
|
+
* PLAN-4.6: isolateSink for one vnode (a copy, with the scope class). 4-I G-558: a fragment (a
|
|
126
|
+
* Collection's, `<>…</>`) has no element: each of its top-level elements gets the class (as the
|
|
127
|
+
* real driver's scoped()); a text vnode is left as it is
|
|
128
|
+
*/
|
|
129
|
+
public isolateValue(vnode: any, scope: string): any {
|
|
130
|
+
// G-305: the scope's class token exactly (scope s1 is not s14)
|
|
131
|
+
return !vnode || typeof vnode != 'object' ? vnode
|
|
132
|
+
: !vnode.sel ? (vnode.children ? {...vnode, children: vnode.children.map((c: any) => this.isolateValue(c, scope))} : vnode)
|
|
133
|
+
: vnode.sel.split('.').indexOf(SCOPE_PREFIX + scope) > 0 ? vnode : {...vnode, sel: vnode.sel + `.${SCOPE_PREFIX}${scope}`};
|
|
134
|
+
}
|
|
135
|
+
|
|
69
136
|
public isolateSink(sink: any, scope: string): any {
|
|
70
137
|
return adapt(
|
|
71
138
|
xs.fromObservable<any>(sink).map((vnode: VNode) => {
|
|
139
|
+
// B-025: a Switchable's DOM sink starts with undefined (nothing rendered yet); the
|
|
140
|
+
// real DOM driver's isolateSink passes it through too
|
|
141
|
+
if (!vnode) return vnode;
|
|
72
142
|
if (vnode.sel && vnode.sel.indexOf(SCOPE_PREFIX + scope) !== -1) {
|
|
73
143
|
return vnode;
|
|
74
144
|
} else {
|
|
@@ -80,6 +150,10 @@ export class MockedDOMSource {
|
|
|
80
150
|
}
|
|
81
151
|
}
|
|
82
152
|
|
|
83
|
-
export function mockDOMSource(
|
|
84
|
-
|
|
153
|
+
export function mockDOMSource(
|
|
154
|
+
mockConfig: MockConfig,
|
|
155
|
+
hub?: MockEventHub,
|
|
156
|
+
onEvents?: MockOnEvents
|
|
157
|
+
): MockedDOMSource {
|
|
158
|
+
return new MockedDOMSource(mockConfig, hub, [], onEvents);
|
|
85
159
|
}
|
package/src/cycle/dom/modules.ts
CHANGED
|
@@ -1,16 +1,33 @@
|
|
|
1
|
-
import {Module, classModule,
|
|
1
|
+
import {Module, classModule, attributesModule, datasetModule} from './snabbdom';
|
|
2
2
|
import {styleModule} from './styleModule';
|
|
3
3
|
import {selectModule} from './selectModule';
|
|
4
|
+
import {controlledInputModule} from './controlledInputModule';
|
|
5
|
+
import {classNameModule} from './classNameModule';
|
|
6
|
+
import {propsModule} from './propsModule';
|
|
7
|
+
import {isField} from './controlledInputModule';
|
|
8
|
+
import {sameData} from './utils';
|
|
4
9
|
|
|
5
|
-
const
|
|
10
|
+
const list: Array<Module> = [
|
|
6
11
|
styleModule,
|
|
7
12
|
classModule,
|
|
8
|
-
propsModule,
|
|
13
|
+
propsModule, // Sygnal's: also clears removed and nullish props (B-015, G-109)
|
|
14
|
+
classNameModule, // after propsModule (B-012)
|
|
9
15
|
attributesModule,
|
|
10
16
|
datasetModule,
|
|
11
17
|
selectModule,
|
|
18
|
+
controlledInputModule,
|
|
12
19
|
];
|
|
20
|
+
// P46-P: one update hook for these modules, in their order. A vnode whose data is the same as
|
|
21
|
+
// last time (each bucket shallow-equal) needs none of them, unless it is a form field (the
|
|
22
|
+
// controlled-input and select modules re-sync it every patch)
|
|
23
|
+
const ups = list.map(m => m.update!);
|
|
24
|
+
const modules: Array<Module> = list.map(({update, ...m}) => m as Module).concat({
|
|
25
|
+
update(o: any, v: any) {
|
|
26
|
+
if (!isField(v) && sameData(o.data, v.data)) return;
|
|
27
|
+
for (let i = 0; i < ups.length; i++) ups[i](o, v);
|
|
28
|
+
},
|
|
29
|
+
} as Module);
|
|
13
30
|
|
|
14
|
-
export {styleModule, classModule, propsModule, attributesModule, datasetModule, selectModule};
|
|
31
|
+
export {styleModule, classModule, propsModule, attributesModule, datasetModule, selectModule, controlledInputModule, classNameModule};
|
|
15
32
|
|
|
16
33
|
export default modules;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sygnal's props module: snabbdom's propsModule, plus clearing removed and
|
|
3
|
+
* nullish props (B-015, G-109).
|
|
4
|
+
*
|
|
5
|
+
* Problem: snabbdom's propsModule writes new and changed props but never
|
|
6
|
+
* unsets a prop that disappears, so a reused element keeps `title`, `disabled`,
|
|
7
|
+
* `href`, ... from the previous render. A nullish prop is written as the
|
|
8
|
+
* string "undefined"/"null" (`title={cond ? 'x' : undefined}`, `src={null}`).
|
|
9
|
+
*
|
|
10
|
+
* Fix: write new and changed props except nullish ones (G-109: nothing is ever
|
|
11
|
+
* written as "null"). For each prop that is gone (or became nullish), remove
|
|
12
|
+
* the reflected attribute, which resets the property (numbers such as
|
|
13
|
+
* maxLength fall back to their default too). Without such an attribute, reset
|
|
14
|
+
* the property by its current type (boolean -> false, string -> '', other
|
|
15
|
+
* non-number -> undefined/null) if it differs (G-095). `className` is left
|
|
16
|
+
* to classNameModule (B-012). On form fields a nullish `value`/`checked` clears
|
|
17
|
+
* the field ('' / false, D49); a removed one is left alone (the controlled-input
|
|
18
|
+
* module owns them; removing them hands control back to the user, as in React).
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import type {VNode} from 'snabbdom/build/vnode.js';
|
|
22
|
+
|
|
23
|
+
function clearProp(elm: any, key: string, nullish?: boolean): void {
|
|
24
|
+
if (key === 'className') return;
|
|
25
|
+
if (/^(value|checked)$/.test(key) && /^(INPUT|TEXTAREA|SELECT)$/.test(elm.tagName)) {
|
|
26
|
+
const empty = key === 'value' ? '' : false;
|
|
27
|
+
if (nullish && elm[key] !== empty) elm[key] = empty;
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const attr = key === 'htmlFor' ? 'for' : key.replace(/^aria(?=[A-Z])/, 'aria-').toLowerCase();
|
|
31
|
+
// G-095: a reflected attribute is just removed, which resets the property. Writing it first
|
|
32
|
+
// (src = '') would queue an img/video error event or navigate an iframe to about:blank.
|
|
33
|
+
if (elm.hasAttribute?.(attr)) return elm.removeAttribute(attr);
|
|
34
|
+
const cur = elm[key];
|
|
35
|
+
const t = typeof cur;
|
|
36
|
+
const reset = t === 'boolean' ? false : t === 'string' ? '' : cur === null || t === 'function' ? null : undefined;
|
|
37
|
+
try {
|
|
38
|
+
// numbers (maxLength, ...) are left to their default; unchanged values aren't rewritten
|
|
39
|
+
if (t !== 'number' && cur !== reset) elm[key] = reset;
|
|
40
|
+
} catch (_) {
|
|
41
|
+
// e.g. contentEditable rejects ''
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function updateProps(oldVnode: VNode, vnode: VNode): void {
|
|
46
|
+
const elm: any = vnode.elm;
|
|
47
|
+
const props: any = vnode.data?.props || {};
|
|
48
|
+
const oldProps: any = oldVnode.data?.props || {};
|
|
49
|
+
if (!elm || oldProps === props) return;
|
|
50
|
+
for (const key in oldProps) {
|
|
51
|
+
if (!(key in props)) clearProp(elm, key);
|
|
52
|
+
}
|
|
53
|
+
for (const key in props) {
|
|
54
|
+
const cur = props[key];
|
|
55
|
+
if (cur === oldProps[key]) continue;
|
|
56
|
+
// a nullish className is left to classNameModule, which rebuilds the attribute (B-012)
|
|
57
|
+
if (cur == null) clearProp(elm, key, true);
|
|
58
|
+
// G-553: on create (no old sel) `value` is always written: the create hooks run before the
|
|
59
|
+
// children are appended, so an <option>'s value reads '' (no text yet) and value="" would be
|
|
60
|
+
// skipped, leaving no attribute (the option then submits its label)
|
|
61
|
+
else if (key !== 'value' || elm[key] !== cur || !oldVnode.sel) elm[key] = cur;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export const propsModule = {create: updateProps, update: updateProps};
|
|
@@ -1,33 +1,38 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Snabbdom module that
|
|
2
|
+
* Snabbdom module that applies a <select>'s `value` after its <option>s exist.
|
|
3
3
|
*
|
|
4
|
-
* Problem: snabbdom
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* Problem: snabbdom runs the module create/update hooks (propsModule sets
|
|
5
|
+
* elm.value) BEFORE it creates or patches the element's children. When the
|
|
6
|
+
* matching <option> is new in this patch, the browser ignores the value and
|
|
7
|
+
* the <select> shows the first option. On create that is every <select>; on
|
|
8
|
+
* update it is a value change together with an options change (B-017).
|
|
8
9
|
*
|
|
9
|
-
* Fix: collect <select> elements
|
|
10
|
-
* then re-apply the value in the post hook
|
|
11
|
-
* children have been inserted).
|
|
10
|
+
* Fix: collect <select> elements with a value prop during create and update,
|
|
11
|
+
* then re-apply the value in the post hook, after all children are patched.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import type {VNode} from 'snabbdom/build/vnode.js';
|
|
15
|
+
import {isField} from './controlledInputModule';
|
|
15
16
|
|
|
16
17
|
let pendingSelects: Array<{elm: HTMLSelectElement; value: any}> = [];
|
|
17
18
|
|
|
18
|
-
function
|
|
19
|
-
|
|
20
|
-
if (
|
|
21
|
-
|
|
19
|
+
function queueSelect(_oldVnode: VNode, vnode: VNode): void {
|
|
20
|
+
// P45-A: a form field by the vnode's tag first; most vnodes aren't
|
|
21
|
+
if (!isField(vnode)) return;
|
|
22
|
+
const elm = vnode.elm as HTMLSelectElement;
|
|
23
|
+
const value = vnode.data?.props?.value;
|
|
24
|
+
if (elm && elm.tagName === 'SELECT' && value != null) {
|
|
25
|
+
pendingSelects.push({elm, value});
|
|
22
26
|
}
|
|
23
27
|
}
|
|
24
28
|
|
|
25
29
|
function postPatch(): void {
|
|
26
|
-
|
|
27
|
-
const {elm, value} = pendingSelects[i];
|
|
28
|
-
elm.value = value;
|
|
29
|
-
}
|
|
30
|
+
const list = pendingSelects;
|
|
30
31
|
pendingSelects = [];
|
|
32
|
+
for (let i = 0; i < list.length; i++) {
|
|
33
|
+
const {elm, value} = list[i];
|
|
34
|
+
if (elm.value !== String(value)) elm.value = value;
|
|
35
|
+
}
|
|
31
36
|
}
|
|
32
37
|
|
|
33
|
-
export const selectModule = {create:
|
|
38
|
+
export const selectModule = {create: queueSelect, update: queueSelect, post: postPatch};
|
|
@@ -13,14 +13,9 @@
|
|
|
13
13
|
export {h, fragment as HFragment} from 'snabbdom/build/h.js';
|
|
14
14
|
export {init} from 'snabbdom/build/init.js';
|
|
15
15
|
export type {Options} from 'snabbdom/build/init.js';
|
|
16
|
-
export {toVNode} from 'snabbdom/build/tovnode.js';
|
|
17
16
|
export {vnode} from 'snabbdom/build/vnode.js';
|
|
18
17
|
export type {VNode, VNodeData} from 'snabbdom/build/vnode.js';
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
// Tag Fragment so we can identify it even after minification mangles Function.name
|
|
22
|
-
(_Fragment as any).__sygnalFragment = true;
|
|
23
|
-
export const Fragment = _Fragment;
|
|
18
|
+
export {Fragment} from './fragment';
|
|
24
19
|
|
|
25
20
|
// Module type
|
|
26
21
|
export type {Module} from 'snabbdom/build/modules/module.js';
|
package/src/cycle/dom/thunk.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {h, VNode, VNodeData} from './snabbdom';
|
|
2
|
+
import {flat} from './utils';
|
|
2
3
|
|
|
3
4
|
export interface ThunkData extends VNodeData {
|
|
4
5
|
fn(): VNode;
|
|
@@ -10,6 +11,8 @@ export interface Thunk extends VNode {
|
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
function copyToThunk(vnode: VNode, thunkVNode: Thunk): void {
|
|
14
|
+
// (rendered during the patch: its fragments are flattened here, as the DOM driver does)
|
|
15
|
+
vnode = flat(vnode);
|
|
13
16
|
thunkVNode.elm = vnode.elm;
|
|
14
17
|
(vnode.data as ThunkData).fn = thunkVNode.data.fn;
|
|
15
18
|
(vnode.data as ThunkData).args = thunkVNode.data.args;
|
package/src/cycle/dom/utils.ts
CHANGED
|
@@ -86,3 +86,101 @@ export function makeInsert(
|
|
|
86
86
|
}
|
|
87
87
|
};
|
|
88
88
|
}
|
|
89
|
+
|
|
90
|
+
// G-261: a DOM change Sygnal makes outside a patch (a Transition's leave, a Portal mounted late)
|
|
91
|
+
// re-emits the DOM source of the app whose root contains `el` (makeDOMDriver listens on its root)
|
|
92
|
+
export const POKE = 'sygnal-dom';
|
|
93
|
+
export const pokeDOM = (el: any): any => el?.dispatchEvent(new Event(POKE, {bubbles: true}));
|
|
94
|
+
|
|
95
|
+
const obj = (o: any) => o !== null && typeof o == 'object' && !Array.isArray(o);
|
|
96
|
+
/**
|
|
97
|
+
* P46-P: two vnodes' data are the same for the DOM modules: the same keys, each value identical
|
|
98
|
+
* or a shallow-equal object (a module bucket: props, attrs, class, dataset, style, ...)
|
|
99
|
+
*/
|
|
100
|
+
export function sameData(a: any, b: any): boolean {
|
|
101
|
+
if (a === b) return true;
|
|
102
|
+
if (!a || !b) return false;
|
|
103
|
+
let n = 0;
|
|
104
|
+
for (const k in a) {
|
|
105
|
+
n++;
|
|
106
|
+
const x = a[k], y = b[k];
|
|
107
|
+
if (x !== y) {
|
|
108
|
+
if (!obj(x) || !obj(y)) return false;
|
|
109
|
+
let m = 0;
|
|
110
|
+
for (const j in x) { m++; if (x[j] !== y[j]) return false; }
|
|
111
|
+
for (const _ in y) m--;
|
|
112
|
+
if (m) return false;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
for (const _ in b) n--;
|
|
116
|
+
return !n;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// G-517 / G-518 / G-522: snabbdom never sees a fragment. Before each patch, a fragment's children
|
|
120
|
+
// (through nested ones) are spliced into its parent's children, so they are diffed, moved and
|
|
121
|
+
// adopted at hydration as the parent's own (snabbdom's fragment bounds went stale: an insert into
|
|
122
|
+
// one landed at the parent's end, a keyed one never moved, a later patch threw NotFoundError).
|
|
123
|
+
// The children of a keyed fragment (a Collection item, a component's root) take its key and their
|
|
124
|
+
// own key, else their tag and its count among them: they move with it and stay apart from another
|
|
125
|
+
// item's. G-541: no two paths give the same key (a '/' separator did: fragment "src" + "a/b.ts" and
|
|
126
|
+
// fragment "src/a" + "b.ts"). 3-W G-544: each segment is a JSON string (the key as a string) or
|
|
127
|
+
// array ([tag, count], always last), which ends where it ends, appended to the prefix
|
|
128
|
+
// (`"src""a/b.ts"`): a key grows linearly with the nesting (JSON in JSON doubled at each level),
|
|
129
|
+
// and any key String() takes is fine (G-548: a BigInt; two Symbols with the same description share
|
|
130
|
+
// a key; a number key and its string are one key, as in snabbdom). A vnode with no fragment below is kept by identity, as is one already
|
|
131
|
+
// flattened (a cached subtree), so snabbdom still skips unchanged subtrees; so are a keyed
|
|
132
|
+
// fragment's copies while the fragment and its prefix are the same (G-539; a hit compares the
|
|
133
|
+
// parent's prefix, the fragment's own is made on a miss). A hoisted keyed fragment rendered in two
|
|
134
|
+
// places shares its copies, as any hoisted vnode shares its object (render it once). A copy (a
|
|
135
|
+
// parent with new children, a child with a new key) has its vnode's fields and writes its
|
|
136
|
+
// element there too, so the app's vnodes get their `elm` as before (testing and diagnostics read
|
|
137
|
+
// it); it keeps its own (a vnode object reused under another key: the old copy removes its own).
|
|
138
|
+
const flatOf = new WeakMap<any, any>();
|
|
139
|
+
const cpOf = new WeakMap<any, any>();
|
|
140
|
+
// 4-I G-561: a copy is an instance of one class (the vnode's own fields, `elm` an accessor on its
|
|
141
|
+
// prototype), not Object.create(y) with an own accessor: snabbdom's patch loop read each field
|
|
142
|
+
// through the prototype chain and called a per-object accessor, ~3x slower over a 1k Collection.
|
|
143
|
+
// (y: set again after the fields, as a copy's own y is its vnode's vnode; m starts as the vnode's
|
|
144
|
+
// elm, which snabbdom replaces before reading it)
|
|
145
|
+
class Copy {
|
|
146
|
+
y: any; m: any
|
|
147
|
+
constructor(y: any, o: any) { this.y = y; Object.assign(this, y, o).y = y }
|
|
148
|
+
get elm() { return this.m }
|
|
149
|
+
set elm(e: any) { this.y.elm = this.m = e }
|
|
150
|
+
}
|
|
151
|
+
const seg = (k: any, s: any) => (k || '') + JSON.stringify(typeof s == 'object' ? s : String(s));
|
|
152
|
+
export const flat = (v: any): any => {
|
|
153
|
+
if (!v || v.$p || !v.children) return v;
|
|
154
|
+
let r = flatOf.get(v);
|
|
155
|
+
if (!r) {
|
|
156
|
+
const o: any[] = [];
|
|
157
|
+
let d = 0;
|
|
158
|
+
// (m, s: locals of each call)
|
|
159
|
+
const put = (c: any[], k?: string, n: any = {}, m?: any, s?: any) => {
|
|
160
|
+
for (const x of c) {
|
|
161
|
+
// G-539: a keyed fragment's copies are kept per (fragment, prefix), in flatOf (flat() never
|
|
162
|
+
// takes a fragment): an unchanged one (a Collection item that didn't render again) gives
|
|
163
|
+
// snabbdom the same vnodes
|
|
164
|
+
if (x && !x.sel && x.children) d = 1, x.key == null ? put(x.children, k, n)
|
|
165
|
+
: (m = flatOf.get(x)) && m[0] === k ? o.push(...m[1])
|
|
166
|
+
: (s = o.length, put(x.children, seg(k, x.key)), flatOf.set(x, [k, o.slice(s)]));
|
|
167
|
+
else {
|
|
168
|
+
let y = flat(x);
|
|
169
|
+
y !== x && (d = 1);
|
|
170
|
+
if (k && y) {
|
|
171
|
+
// 4-H: the copy of an unchanged child under the same key is kept (the fragment is new
|
|
172
|
+
// when one of its children is: a Collection's), so snabbdom skips the others. 4-I
|
|
173
|
+
// G-561: a hit compares the prefix and the child's own part (its key, else its count),
|
|
174
|
+
// so the key is only built on a miss
|
|
175
|
+
const s = y.key ?? (n[y.sel] = -~n[y.sel]), c = cpOf.get(y);
|
|
176
|
+
y = c && c[0] === k && c[1] === s ? c[2] : (cpOf.set(y, [k, s, y = new Copy(y, {key: seg(k, y.key ?? [y.sel, s])})]), y);
|
|
177
|
+
}
|
|
178
|
+
o.push(y);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
put(v.children);
|
|
183
|
+
flatOf.set(v, r = d ? new Copy(v, {children: o}) : v);
|
|
184
|
+
}
|
|
185
|
+
return r;
|
|
186
|
+
};
|