@rezi-ui/core 0.1.0-alpha.60 → 0.1.0-alpha.63
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/dist/app/createApp/breadcrumbs.d.ts +38 -0
- package/dist/app/createApp/breadcrumbs.d.ts.map +1 -0
- package/dist/app/createApp/breadcrumbs.js +65 -0
- package/dist/app/createApp/breadcrumbs.js.map +1 -0
- package/dist/app/createApp/config.d.ts +25 -0
- package/dist/app/createApp/config.d.ts.map +1 -0
- package/dist/app/createApp/config.js +130 -0
- package/dist/app/createApp/config.js.map +1 -0
- package/dist/app/createApp/eventLoop.d.ts +95 -0
- package/dist/app/createApp/eventLoop.d.ts.map +1 -0
- package/dist/app/createApp/eventLoop.js +384 -0
- package/dist/app/createApp/eventLoop.js.map +1 -0
- package/dist/app/createApp/guards.d.ts +21 -0
- package/dist/app/createApp/guards.d.ts.map +1 -0
- package/dist/app/createApp/guards.js +54 -0
- package/dist/app/createApp/guards.js.map +1 -0
- package/dist/app/createApp/keybindings.d.ts +28 -0
- package/dist/app/createApp/keybindings.d.ts.map +1 -0
- package/dist/app/createApp/keybindings.js +113 -0
- package/dist/app/createApp/keybindings.js.map +1 -0
- package/dist/app/createApp/renderLoop.d.ts +64 -0
- package/dist/app/createApp/renderLoop.d.ts.map +1 -0
- package/dist/app/createApp/renderLoop.js +305 -0
- package/dist/app/createApp/renderLoop.js.map +1 -0
- package/dist/app/createApp.d.ts +3 -39
- package/dist/app/createApp.d.ts.map +1 -1
- package/dist/app/createApp.js +403 -1205
- package/dist/app/createApp.js.map +1 -1
- package/dist/app/widgetRenderer/constraintState.d.ts +98 -0
- package/dist/app/widgetRenderer/constraintState.d.ts.map +1 -0
- package/dist/app/widgetRenderer/constraintState.js +563 -0
- package/dist/app/widgetRenderer/constraintState.js.map +1 -0
- package/dist/app/widgetRenderer/fileNodeCache.d.ts +2 -0
- package/dist/app/widgetRenderer/fileNodeCache.d.ts.map +1 -1
- package/dist/app/widgetRenderer/fileNodeCache.js +31 -0
- package/dist/app/widgetRenderer/fileNodeCache.js.map +1 -1
- package/dist/app/widgetRenderer/filePickerRouting.d.ts +12 -1
- package/dist/app/widgetRenderer/filePickerRouting.d.ts.map +1 -1
- package/dist/app/widgetRenderer/filePickerRouting.js +63 -14
- package/dist/app/widgetRenderer/filePickerRouting.js.map +1 -1
- package/dist/app/widgetRenderer/focusState.d.ts +46 -0
- package/dist/app/widgetRenderer/focusState.d.ts.map +1 -0
- package/dist/app/widgetRenderer/focusState.js +122 -0
- package/dist/app/widgetRenderer/focusState.js.map +1 -0
- package/dist/app/widgetRenderer/keyboardRouting.d.ts.map +1 -1
- package/dist/app/widgetRenderer/keyboardRouting.js.map +1 -1
- package/dist/app/widgetRenderer/mouseRouting.d.ts +5 -0
- package/dist/app/widgetRenderer/mouseRouting.d.ts.map +1 -1
- package/dist/app/widgetRenderer/mouseRouting.js +78 -8
- package/dist/app/widgetRenderer/mouseRouting.js.map +1 -1
- package/dist/app/widgetRenderer/overlayShortcuts.d.ts.map +1 -1
- package/dist/app/widgetRenderer/overlayShortcuts.js +14 -4
- package/dist/app/widgetRenderer/overlayShortcuts.js.map +1 -1
- package/dist/app/widgetRenderer/overlayState.d.ts +198 -0
- package/dist/app/widgetRenderer/overlayState.d.ts.map +1 -0
- package/dist/app/widgetRenderer/overlayState.js +590 -0
- package/dist/app/widgetRenderer/overlayState.js.map +1 -0
- package/dist/app/widgetRenderer/routeEngineEvent.d.ts +189 -0
- package/dist/app/widgetRenderer/routeEngineEvent.d.ts.map +1 -0
- package/dist/app/widgetRenderer/routeEngineEvent.js +527 -0
- package/dist/app/widgetRenderer/routeEngineEvent.js.map +1 -0
- package/dist/app/widgetRenderer.d.ts +2 -1
- package/dist/app/widgetRenderer.d.ts.map +1 -1
- package/dist/app/widgetRenderer.js +334 -1707
- package/dist/app/widgetRenderer.js.map +1 -1
- package/dist/forms/internal/arrayState.d.ts +35 -0
- package/dist/forms/internal/arrayState.d.ts.map +1 -0
- package/dist/forms/internal/arrayState.js +238 -0
- package/dist/forms/internal/arrayState.js.map +1 -0
- package/dist/forms/internal/bindings.d.ts +46 -0
- package/dist/forms/internal/bindings.d.ts.map +1 -0
- package/dist/forms/internal/bindings.js +161 -0
- package/dist/forms/internal/bindings.js.map +1 -0
- package/dist/forms/internal/dev.d.ts +4 -0
- package/dist/forms/internal/dev.d.ts.map +1 -0
- package/dist/forms/internal/dev.js +21 -0
- package/dist/forms/internal/dev.js.map +1 -0
- package/dist/forms/internal/state.d.ts +52 -0
- package/dist/forms/internal/state.d.ts.map +1 -0
- package/dist/forms/internal/state.js +240 -0
- package/dist/forms/internal/state.js.map +1 -0
- package/dist/forms/internal/submit.d.ts +43 -0
- package/dist/forms/internal/submit.d.ts.map +1 -0
- package/dist/forms/internal/submit.js +165 -0
- package/dist/forms/internal/submit.js.map +1 -0
- package/dist/forms/internal/wizard.d.ts +53 -0
- package/dist/forms/internal/wizard.d.ts.map +1 -0
- package/dist/forms/internal/wizard.js +311 -0
- package/dist/forms/internal/wizard.js.map +1 -0
- package/dist/forms/useForm.d.ts.map +1 -1
- package/dist/forms/useForm.js +90 -1117
- package/dist/forms/useForm.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/keybindings/manager.d.ts.map +1 -1
- package/dist/keybindings/manager.js.map +1 -1
- package/dist/keybindings/parser.d.ts.map +1 -1
- package/dist/keybindings/parser.js +10 -5
- package/dist/keybindings/parser.js.map +1 -1
- package/dist/layout/dropdownGeometry.d.ts +8 -0
- package/dist/layout/dropdownGeometry.d.ts.map +1 -1
- package/dist/layout/dropdownGeometry.js +40 -0
- package/dist/layout/dropdownGeometry.js.map +1 -1
- package/dist/layout/engine/layoutEngine.js +1 -1
- package/dist/layout/engine/layoutEngine.js.map +1 -1
- package/dist/layout/kinds/overlays.d.ts.map +1 -1
- package/dist/layout/kinds/stack.d.ts +1 -3
- package/dist/layout/kinds/stack.d.ts.map +1 -1
- package/dist/layout/kinds/stack.js +11 -1523
- package/dist/layout/kinds/stack.js.map +1 -1
- package/dist/layout/kinds/stackParts/axis.d.ts +32 -0
- package/dist/layout/kinds/stackParts/axis.d.ts.map +1 -0
- package/dist/layout/kinds/stackParts/axis.js +61 -0
- package/dist/layout/kinds/stackParts/axis.js.map +1 -0
- package/dist/layout/kinds/stackParts/constraintPlan.d.ts +18 -0
- package/dist/layout/kinds/stackParts/constraintPlan.d.ts.map +1 -0
- package/dist/layout/kinds/stackParts/constraintPlan.js +434 -0
- package/dist/layout/kinds/stackParts/constraintPlan.js.map +1 -0
- package/dist/layout/kinds/stackParts/layout.d.ts +6 -0
- package/dist/layout/kinds/stackParts/layout.d.ts.map +1 -0
- package/dist/layout/kinds/stackParts/layout.js +376 -0
- package/dist/layout/kinds/stackParts/layout.js.map +1 -0
- package/dist/layout/kinds/stackParts/measure.d.ts +6 -0
- package/dist/layout/kinds/stackParts/measure.d.ts.map +1 -0
- package/dist/layout/kinds/stackParts/measure.js +212 -0
- package/dist/layout/kinds/stackParts/measure.js.map +1 -0
- package/dist/layout/kinds/stackParts/shared.d.ts +31 -0
- package/dist/layout/kinds/stackParts/shared.d.ts.map +1 -0
- package/dist/layout/kinds/stackParts/shared.js +94 -0
- package/dist/layout/kinds/stackParts/shared.js.map +1 -0
- package/dist/layout/kinds/stackParts/wrap.d.ts +26 -0
- package/dist/layout/kinds/stackParts/wrap.d.ts.map +1 -0
- package/dist/layout/kinds/stackParts/wrap.js +374 -0
- package/dist/layout/kinds/stackParts/wrap.js.map +1 -0
- package/dist/layout/validate/interactive.d.ts +106 -0
- package/dist/layout/validate/interactive.d.ts.map +1 -0
- package/dist/layout/validate/interactive.js +430 -0
- package/dist/layout/validate/interactive.js.map +1 -0
- package/dist/layout/validate/layoutConstraints.d.ts +51 -0
- package/dist/layout/validate/layoutConstraints.d.ts.map +1 -0
- package/dist/layout/validate/layoutConstraints.js +100 -0
- package/dist/layout/validate/layoutConstraints.js.map +1 -0
- package/dist/layout/validate/primitives.d.ts +31 -0
- package/dist/layout/validate/primitives.d.ts.map +1 -0
- package/dist/layout/validate/primitives.js +299 -0
- package/dist/layout/validate/primitives.js.map +1 -0
- package/dist/layout/validate/shared.d.ts +23 -0
- package/dist/layout/validate/shared.d.ts.map +1 -0
- package/dist/layout/validate/shared.js +32 -0
- package/dist/layout/validate/shared.js.map +1 -0
- package/dist/layout/validate/spacing.d.ts +21 -0
- package/dist/layout/validate/spacing.d.ts.map +1 -0
- package/dist/layout/validate/spacing.js +33 -0
- package/dist/layout/validate/spacing.js.map +1 -0
- package/dist/layout/validateProps.d.ts +5 -159
- package/dist/layout/validateProps.d.ts.map +1 -1
- package/dist/layout/validateProps.js +1 -832
- package/dist/layout/validateProps.js.map +1 -1
- package/dist/pipeline.js +1 -1
- package/dist/pipeline.js.map +1 -1
- package/dist/renderer/renderToDrawlist/renderTree.d.ts +1 -1
- package/dist/renderer/renderToDrawlist/renderTree.d.ts.map +1 -1
- package/dist/renderer/renderToDrawlist/renderTree.js +3 -3
- package/dist/renderer/renderToDrawlist/renderTree.js.map +1 -1
- package/dist/renderer/renderToDrawlist/types.d.ts +2 -0
- package/dist/renderer/renderToDrawlist/types.d.ts.map +1 -1
- package/dist/renderer/renderToDrawlist/widgets/containers.d.ts.map +1 -1
- package/dist/renderer/renderToDrawlist/widgets/containers.js +1 -1
- package/dist/renderer/renderToDrawlist/widgets/containers.js.map +1 -1
- package/dist/renderer/renderToDrawlist/widgets/files.d.ts +2 -1
- package/dist/renderer/renderToDrawlist/widgets/files.d.ts.map +1 -1
- package/dist/renderer/renderToDrawlist/widgets/files.js +30 -3
- package/dist/renderer/renderToDrawlist/widgets/files.js.map +1 -1
- package/dist/renderer/renderToDrawlist/widgets/overlays.d.ts +1 -1
- package/dist/renderer/renderToDrawlist/widgets/overlays.d.ts.map +1 -1
- package/dist/renderer/renderToDrawlist/widgets/overlays.js +26 -6
- package/dist/renderer/renderToDrawlist/widgets/overlays.js.map +1 -1
- package/dist/renderer/renderToDrawlist/widgets/renderFormWidgets.d.ts.map +1 -1
- package/dist/renderer/renderToDrawlist/widgets/renderFormWidgets.js +52 -18
- package/dist/renderer/renderToDrawlist/widgets/renderFormWidgets.js.map +1 -1
- package/dist/renderer/renderToDrawlist.d.ts +0 -8
- package/dist/renderer/renderToDrawlist.d.ts.map +1 -1
- package/dist/renderer/renderToDrawlist.js +1 -9
- package/dist/renderer/renderToDrawlist.js.map +1 -1
- package/dist/runtime/commit/composite.d.ts +11 -0
- package/dist/runtime/commit/composite.d.ts.map +1 -0
- package/dist/runtime/commit/composite.js +238 -0
- package/dist/runtime/commit/composite.js.map +1 -0
- package/dist/runtime/commit/container.d.ts +7 -0
- package/dist/runtime/commit/container.d.ts.map +1 -0
- package/dist/runtime/commit/container.js +350 -0
- package/dist/runtime/commit/container.js.map +1 -0
- package/dist/runtime/commit/equality.d.ts +20 -0
- package/dist/runtime/commit/equality.d.ts.map +1 -0
- package/dist/runtime/commit/equality.js +436 -0
- package/dist/runtime/commit/equality.js.map +1 -0
- package/dist/runtime/commit/errorBoundary.d.ts +7 -0
- package/dist/runtime/commit/errorBoundary.d.ts.map +1 -0
- package/dist/runtime/commit/errorBoundary.js +53 -0
- package/dist/runtime/commit/errorBoundary.js.map +1 -0
- package/dist/runtime/commit/shared.d.ts +138 -0
- package/dist/runtime/commit/shared.d.ts.map +1 -0
- package/dist/runtime/commit/shared.js +11 -0
- package/dist/runtime/commit/shared.js.map +1 -0
- package/dist/runtime/commit/transitions.d.ts +9 -0
- package/dist/runtime/commit/transitions.d.ts.map +1 -0
- package/dist/runtime/commit/transitions.js +93 -0
- package/dist/runtime/commit/transitions.js.map +1 -0
- package/dist/runtime/commit/validation.d.ts +16 -0
- package/dist/runtime/commit/validation.d.ts.map +1 -0
- package/dist/runtime/commit/validation.js +157 -0
- package/dist/runtime/commit/validation.js.map +1 -0
- package/dist/runtime/commit.d.ts +7 -117
- package/dist/runtime/commit.d.ts.map +1 -1
- package/dist/runtime/commit.js +13 -1394
- package/dist/runtime/commit.js.map +1 -1
- package/dist/runtime/localState.d.ts +4 -0
- package/dist/runtime/localState.d.ts.map +1 -1
- package/dist/runtime/localState.js.map +1 -1
- package/dist/runtime/widgetMeta/collector.d.ts +77 -0
- package/dist/runtime/widgetMeta/collector.d.ts.map +1 -0
- package/dist/runtime/widgetMeta/collector.js +293 -0
- package/dist/runtime/widgetMeta/collector.js.map +1 -0
- package/dist/runtime/widgetMeta/focusContainers.d.ts +44 -0
- package/dist/runtime/widgetMeta/focusContainers.d.ts.map +1 -0
- package/dist/runtime/widgetMeta/focusContainers.js +190 -0
- package/dist/runtime/widgetMeta/focusContainers.js.map +1 -0
- package/dist/runtime/widgetMeta/focusInfo.d.ts +19 -0
- package/dist/runtime/widgetMeta/focusInfo.d.ts.map +1 -0
- package/dist/runtime/widgetMeta/focusInfo.js +172 -0
- package/dist/runtime/widgetMeta/focusInfo.js.map +1 -0
- package/dist/runtime/widgetMeta/helpers.d.ts +47 -0
- package/dist/runtime/widgetMeta/helpers.d.ts.map +1 -0
- package/dist/runtime/widgetMeta/helpers.js +182 -0
- package/dist/runtime/widgetMeta/helpers.js.map +1 -0
- package/dist/runtime/widgetMeta.d.ts +12 -175
- package/dist/runtime/widgetMeta.d.ts.map +1 -1
- package/dist/runtime/widgetMeta.js +6 -847
- package/dist/runtime/widgetMeta.js.map +1 -1
- package/dist/ui/capabilities.d.ts.map +1 -1
- package/dist/ui/designTokens.d.ts.map +1 -1
- package/dist/widgets/accordion.d.ts.map +1 -1
- package/dist/widgets/accordion.js +8 -13
- package/dist/widgets/accordion.js.map +1 -1
- package/dist/widgets/factories/advanced.d.ts +20 -0
- package/dist/widgets/factories/advanced.d.ts.map +1 -0
- package/dist/widgets/factories/advanced.js +75 -0
- package/dist/widgets/factories/advanced.js.map +1 -0
- package/dist/widgets/factories/basic.d.ts +14 -0
- package/dist/widgets/factories/basic.d.ts.map +1 -0
- package/dist/widgets/factories/basic.js +44 -0
- package/dist/widgets/factories/basic.js.map +1 -0
- package/dist/widgets/factories/feedback.d.ts +20 -0
- package/dist/widgets/factories/feedback.d.ts.map +1 -0
- package/dist/widgets/factories/feedback.js +102 -0
- package/dist/widgets/factories/feedback.js.map +1 -0
- package/dist/widgets/factories/helpers.d.ts +41 -0
- package/dist/widgets/factories/helpers.d.ts.map +1 -0
- package/dist/widgets/factories/helpers.js +72 -0
- package/dist/widgets/factories/helpers.js.map +1 -0
- package/dist/widgets/factories/interactive.d.ts +15 -0
- package/dist/widgets/factories/interactive.d.ts.map +1 -0
- package/dist/widgets/factories/interactive.js +46 -0
- package/dist/widgets/factories/interactive.js.map +1 -0
- package/dist/widgets/factories/layoutShell.d.ts +22 -0
- package/dist/widgets/factories/layoutShell.d.ts.map +1 -0
- package/dist/widgets/factories/layoutShell.js +190 -0
- package/dist/widgets/factories/layoutShell.js.map +1 -0
- package/dist/widgets/factories/media.d.ts +14 -0
- package/dist/widgets/factories/media.d.ts.map +1 -0
- package/dist/widgets/factories/media.js +25 -0
- package/dist/widgets/factories/media.js.map +1 -0
- package/dist/widgets/factories/navigation.d.ts +10 -0
- package/dist/widgets/factories/navigation.d.ts.map +1 -0
- package/dist/widgets/factories/navigation.js +24 -0
- package/dist/widgets/factories/navigation.js.map +1 -0
- package/dist/widgets/field.d.ts +6 -1
- package/dist/widgets/field.d.ts.map +1 -1
- package/dist/widgets/field.js +8 -2
- package/dist/widgets/field.js.map +1 -1
- package/dist/widgets/filePicker.d.ts +5 -0
- package/dist/widgets/filePicker.d.ts.map +1 -0
- package/dist/widgets/filePicker.js +136 -0
- package/dist/widgets/filePicker.js.map +1 -0
- package/dist/widgets/protocol.d.ts +0 -6
- package/dist/widgets/protocol.d.ts.map +1 -1
- package/dist/widgets/protocol.js +0 -6
- package/dist/widgets/protocol.js.map +1 -1
- package/dist/widgets/select.js +1 -1
- package/dist/widgets/select.js.map +1 -1
- package/dist/widgets/splitPane.d.ts.map +1 -1
- package/dist/widgets/splitPane.js.map +1 -1
- package/dist/widgets/table.d.ts.map +1 -1
- package/dist/widgets/table.js +43 -1
- package/dist/widgets/table.js.map +1 -1
- package/dist/widgets/tree.d.ts.map +1 -1
- package/dist/widgets/types/advanced.d.ts +611 -0
- package/dist/widgets/types/advanced.d.ts.map +1 -0
- package/dist/widgets/types/advanced.js +2 -0
- package/dist/widgets/types/advanced.js.map +1 -0
- package/dist/widgets/types/base.d.ts +933 -0
- package/dist/widgets/types/base.d.ts.map +1 -0
- package/dist/widgets/types/base.js +2 -0
- package/dist/widgets/types/base.js.map +1 -0
- package/dist/widgets/types/forms.d.ts +136 -0
- package/dist/widgets/types/forms.d.ts.map +1 -0
- package/dist/widgets/types/forms.js +2 -0
- package/dist/widgets/types/forms.js.map +1 -0
- package/dist/widgets/types/navigation.d.ts +83 -0
- package/dist/widgets/types/navigation.d.ts.map +1 -0
- package/dist/widgets/types/navigation.js +2 -0
- package/dist/widgets/types/navigation.js.map +1 -0
- package/dist/widgets/types/overlaysShell.d.ts +223 -0
- package/dist/widgets/types/overlaysShell.d.ts.map +1 -0
- package/dist/widgets/types/overlaysShell.js +2 -0
- package/dist/widgets/types/overlaysShell.js.map +1 -0
- package/dist/widgets/types/table.d.ts +104 -0
- package/dist/widgets/types/table.d.ts.map +1 -0
- package/dist/widgets/types/table.js +2 -0
- package/dist/widgets/types/table.js.map +1 -0
- package/dist/widgets/types/tree.d.ts +64 -0
- package/dist/widgets/types/tree.d.ts.map +1 -0
- package/dist/widgets/types/tree.js +2 -0
- package/dist/widgets/types/tree.js.map +1 -0
- package/dist/widgets/types.d.ts +14 -2123
- package/dist/widgets/types.d.ts.map +1 -1
- package/dist/widgets/ui.d.ts +37 -843
- package/dist/widgets/ui.d.ts.map +1 -1
- package/dist/widgets/ui.js +37 -1262
- package/dist/widgets/ui.js.map +1 -1
- package/package.json +2 -2
- package/dist/constraints/aggregation.d.ts +0 -17
- package/dist/constraints/aggregation.d.ts.map +0 -1
- package/dist/constraints/aggregation.js +0 -59
- package/dist/constraints/aggregation.js.map +0 -1
- package/dist/renderer/renderToDrawlist/overflowCulling.d.ts +0 -3
- package/dist/renderer/renderToDrawlist/overflowCulling.d.ts.map +0 -1
- package/dist/renderer/renderToDrawlist/overflowCulling.js +0 -81
- package/dist/renderer/renderToDrawlist/overflowCulling.js.map +0 -1
- package/dist/widgets/tests/protocol.test.d.ts +0 -2
- package/dist/widgets/tests/protocol.test.d.ts.map +0 -1
- package/dist/widgets/tests/protocol.test.js +0 -120
- package/dist/widgets/tests/protocol.test.js.map +0 -1
|
@@ -1,851 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* packages/core/src/runtime/widgetMeta.ts —
|
|
2
|
+
* packages/core/src/runtime/widgetMeta.ts — Public widget metadata API.
|
|
3
3
|
*
|
|
4
|
-
* Why:
|
|
5
|
-
*
|
|
6
|
-
* deterministic traversal-order collections of widget properties.
|
|
7
|
-
*
|
|
8
|
-
* Collections:
|
|
9
|
-
* - focusableIds: enabled focusable widgets in traversal order
|
|
10
|
-
* - enabledMap: all interactive widgets mapped to enabled state
|
|
11
|
-
* - pressableIds: Buttons that can produce "press" actions
|
|
12
|
-
* - inputMetaById: Input widget metadata (value, cursor, etc.)
|
|
13
|
-
* - focusZones: zone metadata with their contained focusable ids
|
|
14
|
-
* - focusTraps: trap metadata with their contained focusable ids
|
|
15
|
-
*
|
|
16
|
-
* @see docs/guide/runtime-and-layout.md
|
|
17
|
-
*/
|
|
18
|
-
import { ZrUiError } from "../abi.js";
|
|
19
|
-
import { getWidgetProtocol, kindIsFocusable, kindIsPressable } from "../widgets/protocol.js";
|
|
20
|
-
/** Extract interactive widget ID from a node with a valid `id` prop. */
|
|
21
|
-
function readInteractiveId(v) {
|
|
22
|
-
const proto = getWidgetProtocol(v.kind);
|
|
23
|
-
if (!proto.requiresId && !proto.focusable && !proto.pressable)
|
|
24
|
-
return null;
|
|
25
|
-
const id = v.props.id;
|
|
26
|
-
if (typeof id !== "string" || id.length === 0)
|
|
27
|
-
return null;
|
|
28
|
-
return id;
|
|
29
|
-
}
|
|
30
|
-
function isFocusableInteractive(v) {
|
|
31
|
-
const focusable = v.props.focusable;
|
|
32
|
-
if (focusable === false)
|
|
33
|
-
return false;
|
|
34
|
-
// Note: Some interactive widgets require an id for routing (e.g. SplitPane dividers),
|
|
35
|
-
// but are explicitly NOT focusable (PLAN.md).
|
|
36
|
-
return kindIsFocusable(v.kind);
|
|
37
|
-
}
|
|
38
|
-
function isEnabledInteractive(v) {
|
|
39
|
-
const id = readInteractiveId(v);
|
|
40
|
-
if (id === null)
|
|
41
|
-
return null;
|
|
42
|
-
const proto = getWidgetProtocol(v.kind);
|
|
43
|
-
if (proto.disableable) {
|
|
44
|
-
const disabled = v.props.disabled;
|
|
45
|
-
if (disabled === true)
|
|
46
|
-
return null;
|
|
47
|
-
}
|
|
48
|
-
if (proto.openGated) {
|
|
49
|
-
const open = v.props.open;
|
|
50
|
-
if (open !== true)
|
|
51
|
-
return null;
|
|
52
|
-
}
|
|
53
|
-
return id;
|
|
54
|
-
}
|
|
55
|
-
function duplicateFocusContainerDetail(id, firstKind, secondKind) {
|
|
56
|
-
return `Duplicate focus container id "${id}". First: <${firstKind}>, second: <${secondKind}>. Hint: focusZone, focusTrap, and modal ids must be unique across the tree.`;
|
|
57
|
-
}
|
|
58
|
-
function recordFocusContainerId(seen, id, kind) {
|
|
59
|
-
const existing = seen.get(id);
|
|
60
|
-
if (existing !== undefined) {
|
|
61
|
-
throw new ZrUiError("ZRUI_DUPLICATE_ID", duplicateFocusContainerDetail(id, existing, kind));
|
|
62
|
-
}
|
|
63
|
-
seen.set(id, kind);
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* Collect focusable ids from a committed runtime tree.
|
|
67
|
-
*
|
|
68
|
-
* - Order: depth-first preorder
|
|
69
|
-
* - Children: left-to-right
|
|
70
|
-
* - Focusable set: enabled focusable interactive widgets
|
|
71
|
-
*/
|
|
72
|
-
export function collectFocusableIds(tree) {
|
|
73
|
-
const out = [];
|
|
74
|
-
const stack = [tree];
|
|
75
|
-
while (stack.length > 0) {
|
|
76
|
-
const node = stack.pop();
|
|
77
|
-
if (!node)
|
|
78
|
-
continue;
|
|
79
|
-
const id = isFocusableInteractive(node.vnode) ? isEnabledInteractive(node.vnode) : null;
|
|
80
|
-
if (id !== null)
|
|
81
|
-
out.push(id);
|
|
82
|
-
for (let i = node.children.length - 1; i >= 0; i--) {
|
|
83
|
-
const c = node.children[i];
|
|
84
|
-
if (c)
|
|
85
|
-
stack.push(c);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
return Object.freeze(out);
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
* Collect a deterministic enabled map (interactive id -> enabled) from a committed runtime tree.
|
|
92
|
-
*
|
|
93
|
-
* Interactive widgets are always included when their `id` is a non-empty string;
|
|
94
|
-
* enabled is `true` iff `disabled !== true`.
|
|
95
|
-
*/
|
|
96
|
-
export function collectEnabledMap(tree) {
|
|
97
|
-
const m = new Map();
|
|
98
|
-
const stack = [tree];
|
|
99
|
-
while (stack.length > 0) {
|
|
100
|
-
const node = stack.pop();
|
|
101
|
-
if (!node)
|
|
102
|
-
continue;
|
|
103
|
-
const id = readInteractiveId(node.vnode);
|
|
104
|
-
if (id !== null && !m.has(id)) {
|
|
105
|
-
let enabled = true;
|
|
106
|
-
const proto = getWidgetProtocol(node.vnode.kind);
|
|
107
|
-
if (proto.disableable) {
|
|
108
|
-
const disabled = node.vnode.props.disabled;
|
|
109
|
-
enabled = disabled !== true;
|
|
110
|
-
}
|
|
111
|
-
if (proto.openGated) {
|
|
112
|
-
const open = node.vnode.props.open;
|
|
113
|
-
enabled = open === true;
|
|
114
|
-
}
|
|
115
|
-
m.set(id, enabled);
|
|
116
|
-
}
|
|
117
|
-
for (let i = node.children.length - 1; i >= 0; i--) {
|
|
118
|
-
const c = node.children[i];
|
|
119
|
-
if (c)
|
|
120
|
-
stack.push(c);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
return m;
|
|
124
|
-
}
|
|
125
|
-
const EMPTY_ERRORS = Object.freeze([]);
|
|
126
|
-
function readNonEmptyString(v) {
|
|
127
|
-
if (typeof v !== "string")
|
|
128
|
-
return null;
|
|
129
|
-
const trimmed = v.trim();
|
|
130
|
-
return trimmed.length > 0 ? trimmed : null;
|
|
131
|
-
}
|
|
132
|
-
function readFocusableVisibleLabel(vnode) {
|
|
133
|
-
switch (vnode.kind) {
|
|
134
|
-
case "button":
|
|
135
|
-
return readNonEmptyString(vnode.props.label);
|
|
136
|
-
case "link": {
|
|
137
|
-
const props = vnode.props;
|
|
138
|
-
return readNonEmptyString(props.label) ?? readNonEmptyString(props.url);
|
|
139
|
-
}
|
|
140
|
-
case "slider":
|
|
141
|
-
return readNonEmptyString(vnode.props.label);
|
|
142
|
-
case "checkbox":
|
|
143
|
-
return readNonEmptyString(vnode.props.label);
|
|
144
|
-
case "select": {
|
|
145
|
-
const props = vnode.props;
|
|
146
|
-
const value = typeof props.value === "string" ? props.value : "";
|
|
147
|
-
const options = Array.isArray(props.options)
|
|
148
|
-
? props.options
|
|
149
|
-
: [];
|
|
150
|
-
for (const option of options) {
|
|
151
|
-
if (typeof option?.value === "string" &&
|
|
152
|
-
option.value === value &&
|
|
153
|
-
typeof option.label === "string") {
|
|
154
|
-
return readNonEmptyString(option.label);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
return readNonEmptyString(props.placeholder);
|
|
158
|
-
}
|
|
159
|
-
case "radioGroup": {
|
|
160
|
-
const props = vnode.props;
|
|
161
|
-
const value = typeof props.value === "string" ? props.value : "";
|
|
162
|
-
const options = Array.isArray(props.options)
|
|
163
|
-
? props.options
|
|
164
|
-
: [];
|
|
165
|
-
for (const option of options) {
|
|
166
|
-
if (typeof option?.value === "string" &&
|
|
167
|
-
option.value === value &&
|
|
168
|
-
typeof option.label === "string") {
|
|
169
|
-
return readNonEmptyString(option.label);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
return null;
|
|
173
|
-
}
|
|
174
|
-
case "commandPalette":
|
|
175
|
-
return readNonEmptyString(vnode.props.placeholder);
|
|
176
|
-
case "filePicker":
|
|
177
|
-
return readNonEmptyString(vnode.props.rootPath);
|
|
178
|
-
case "diffViewer": {
|
|
179
|
-
const diff = vnode.props.diff;
|
|
180
|
-
if (diff && typeof diff === "object") {
|
|
181
|
-
return readNonEmptyString(diff.newPath) ?? readNonEmptyString(diff.oldPath);
|
|
182
|
-
}
|
|
183
|
-
return null;
|
|
184
|
-
}
|
|
185
|
-
case "toolApprovalDialog": {
|
|
186
|
-
const request = vnode.props.request;
|
|
187
|
-
if (request && typeof request === "object") {
|
|
188
|
-
return readNonEmptyString(request.toolName);
|
|
189
|
-
}
|
|
190
|
-
return null;
|
|
191
|
-
}
|
|
192
|
-
default:
|
|
193
|
-
return null;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
function kindToAnnouncementPrefix(kind) {
|
|
197
|
-
switch (kind) {
|
|
198
|
-
case "button":
|
|
199
|
-
return "Button";
|
|
200
|
-
case "link":
|
|
201
|
-
return "Link";
|
|
202
|
-
case "input":
|
|
203
|
-
return "Input";
|
|
204
|
-
case "slider":
|
|
205
|
-
return "Slider";
|
|
206
|
-
case "virtualList":
|
|
207
|
-
return "List";
|
|
208
|
-
case "table":
|
|
209
|
-
return "Table";
|
|
210
|
-
case "tree":
|
|
211
|
-
return "Tree";
|
|
212
|
-
case "select":
|
|
213
|
-
return "Select";
|
|
214
|
-
case "checkbox":
|
|
215
|
-
return "Checkbox";
|
|
216
|
-
case "radioGroup":
|
|
217
|
-
return "Radio group";
|
|
218
|
-
case "commandPalette":
|
|
219
|
-
return "Command palette";
|
|
220
|
-
case "filePicker":
|
|
221
|
-
return "File picker";
|
|
222
|
-
case "fileTreeExplorer":
|
|
223
|
-
return "File tree explorer";
|
|
224
|
-
case "codeEditor":
|
|
225
|
-
return "Code editor";
|
|
226
|
-
case "diffViewer":
|
|
227
|
-
return "Diff viewer";
|
|
228
|
-
case "toolApprovalDialog":
|
|
229
|
-
return "Tool approval dialog";
|
|
230
|
-
case "logsConsole":
|
|
231
|
-
return "Logs console";
|
|
232
|
-
default:
|
|
233
|
-
return "Widget";
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
function readFieldContext(vnode) {
|
|
237
|
-
if (vnode.kind !== "field")
|
|
238
|
-
return null;
|
|
239
|
-
const props = vnode.props;
|
|
240
|
-
return Object.freeze({
|
|
241
|
-
label: readNonEmptyString(props.label),
|
|
242
|
-
required: props.required === true,
|
|
243
|
-
error: readNonEmptyString(props.error),
|
|
244
|
-
});
|
|
245
|
-
}
|
|
246
|
-
function resolveFieldLabel(fieldStack) {
|
|
247
|
-
for (let i = fieldStack.length - 1; i >= 0; i--) {
|
|
248
|
-
const label = fieldStack[i]?.label;
|
|
249
|
-
if (label)
|
|
250
|
-
return label;
|
|
251
|
-
}
|
|
252
|
-
return null;
|
|
253
|
-
}
|
|
254
|
-
function resolveFieldRequired(fieldStack) {
|
|
255
|
-
for (let i = fieldStack.length - 1; i >= 0; i--) {
|
|
256
|
-
if (fieldStack[i]?.required === true)
|
|
257
|
-
return true;
|
|
258
|
-
}
|
|
259
|
-
return false;
|
|
260
|
-
}
|
|
261
|
-
function resolveFieldErrors(fieldStack) {
|
|
262
|
-
if (fieldStack.length === 0)
|
|
263
|
-
return EMPTY_ERRORS;
|
|
264
|
-
const out = [];
|
|
265
|
-
const seen = new Set();
|
|
266
|
-
for (let i = fieldStack.length - 1; i >= 0; i--) {
|
|
267
|
-
const error = fieldStack[i]?.error;
|
|
268
|
-
if (!error || seen.has(error))
|
|
269
|
-
continue;
|
|
270
|
-
seen.add(error);
|
|
271
|
-
out.push(error);
|
|
272
|
-
}
|
|
273
|
-
return out.length > 0 ? Object.freeze(out) : EMPTY_ERRORS;
|
|
274
|
-
}
|
|
275
|
-
function buildFocusAnnouncement(primary, required, errors) {
|
|
276
|
-
const parts = [primary];
|
|
277
|
-
if (required)
|
|
278
|
-
parts.push("Required");
|
|
279
|
-
if (errors.length === 1) {
|
|
280
|
-
const first = errors[0];
|
|
281
|
-
if (first)
|
|
282
|
-
parts.push(first);
|
|
283
|
-
}
|
|
284
|
-
else if (errors.length > 1) {
|
|
285
|
-
parts.push(`${String(errors.length)} validation errors`);
|
|
286
|
-
}
|
|
287
|
-
return parts.join(" — ");
|
|
288
|
-
}
|
|
289
|
-
function buildFocusInfo(vnode, id, fieldStack) {
|
|
290
|
-
const accessibleLabel = readNonEmptyString(vnode.props.accessibleLabel);
|
|
291
|
-
const widgetLabel = readFocusableVisibleLabel(vnode);
|
|
292
|
-
const fieldLabel = resolveFieldLabel(fieldStack);
|
|
293
|
-
const visibleLabel = widgetLabel ?? fieldLabel;
|
|
294
|
-
const required = resolveFieldRequired(fieldStack);
|
|
295
|
-
const errors = resolveFieldErrors(fieldStack);
|
|
296
|
-
const primary = accessibleLabel ?? visibleLabel ?? `${kindToAnnouncementPrefix(vnode.kind)} ${id}`;
|
|
297
|
-
return Object.freeze({
|
|
298
|
-
id,
|
|
299
|
-
kind: vnode.kind,
|
|
300
|
-
accessibleLabel,
|
|
301
|
-
visibleLabel,
|
|
302
|
-
required,
|
|
303
|
-
errors,
|
|
304
|
-
announcement: buildFocusAnnouncement(primary, required, errors),
|
|
305
|
-
});
|
|
306
|
-
}
|
|
307
|
-
/**
|
|
308
|
-
* Collect ids that can produce a "press" action (Buttons).
|
|
309
|
-
*
|
|
310
|
-
* Includes Buttons with a non-empty string id, regardless of disabled state.
|
|
311
|
-
*/
|
|
312
|
-
export function collectPressableIds(tree) {
|
|
313
|
-
const s = new Set();
|
|
314
|
-
const stack = [tree];
|
|
315
|
-
while (stack.length > 0) {
|
|
316
|
-
const node = stack.pop();
|
|
317
|
-
if (!node)
|
|
318
|
-
continue;
|
|
319
|
-
if (kindIsPressable(node.vnode.kind)) {
|
|
320
|
-
const id = node.vnode.props.id;
|
|
321
|
-
if (typeof id === "string" && id.length > 0)
|
|
322
|
-
s.add(id);
|
|
323
|
-
}
|
|
324
|
-
for (let i = node.children.length - 1; i >= 0; i--) {
|
|
325
|
-
const c = node.children[i];
|
|
326
|
-
if (c)
|
|
327
|
-
stack.push(c);
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
return s;
|
|
331
|
-
}
|
|
332
|
-
/**
|
|
333
|
-
* Collect a deterministic map of Input id -> { instanceId, value, disabled } from a committed runtime tree.
|
|
334
|
-
*
|
|
335
|
-
* Inputs are included when their `id` is a non-empty string and `value` is a string.
|
|
336
|
-
*/
|
|
337
|
-
export function collectInputMetaById(tree) {
|
|
338
|
-
const m = new Map();
|
|
339
|
-
const stack = [tree];
|
|
340
|
-
while (stack.length > 0) {
|
|
341
|
-
const node = stack.pop();
|
|
342
|
-
if (!node)
|
|
343
|
-
continue;
|
|
344
|
-
if (node.vnode.kind === "input") {
|
|
345
|
-
const props = node.vnode.props;
|
|
346
|
-
const id = typeof props.id === "string" && props.id.length > 0 ? props.id : null;
|
|
347
|
-
const value = typeof props.value === "string" ? props.value : null;
|
|
348
|
-
if (id !== null && value !== null && !m.has(id)) {
|
|
349
|
-
const disabled = props.disabled === true;
|
|
350
|
-
const readOnly = props.readOnly === true;
|
|
351
|
-
const multiline = props.multiline === true;
|
|
352
|
-
const rowsRaw = typeof props.rows === "number" && Number.isFinite(props.rows) ? props.rows : 3;
|
|
353
|
-
const rows = multiline ? Math.max(1, Math.trunc(rowsRaw)) : 1;
|
|
354
|
-
const wordWrap = multiline ? props.wordWrap !== false : false;
|
|
355
|
-
const onInput = typeof props.onInput === "function"
|
|
356
|
-
? props.onInput
|
|
357
|
-
: undefined;
|
|
358
|
-
const onBlur = typeof props.onBlur === "function" ? props.onBlur : undefined;
|
|
359
|
-
const metaBase = {
|
|
360
|
-
instanceId: node.instanceId,
|
|
361
|
-
value,
|
|
362
|
-
disabled,
|
|
363
|
-
readOnly,
|
|
364
|
-
multiline,
|
|
365
|
-
rows,
|
|
366
|
-
wordWrap,
|
|
367
|
-
};
|
|
368
|
-
const meta = onInput || onBlur
|
|
369
|
-
? Object.freeze({
|
|
370
|
-
...metaBase,
|
|
371
|
-
...(onInput ? { onInput } : {}),
|
|
372
|
-
...(onBlur ? { onBlur } : {}),
|
|
373
|
-
})
|
|
374
|
-
: Object.freeze(metaBase);
|
|
375
|
-
m.set(id, meta);
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
for (let i = node.children.length - 1; i >= 0; i--) {
|
|
379
|
-
const c = node.children[i];
|
|
380
|
-
if (c)
|
|
381
|
-
stack.push(c);
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
return m;
|
|
385
|
-
}
|
|
386
|
-
/**
|
|
387
|
-
* Collect focusable ids from a subtree (not traversing into nested zones/traps/modals).
|
|
388
|
-
*/
|
|
389
|
-
function collectFocusableIdsInSubtree(node) {
|
|
390
|
-
const out = [];
|
|
391
|
-
const stack = [node];
|
|
392
|
-
while (stack.length > 0) {
|
|
393
|
-
const cur = stack.pop();
|
|
394
|
-
if (!cur)
|
|
395
|
-
continue;
|
|
396
|
-
// Don't traverse into nested zones or traps.
|
|
397
|
-
if (cur.vnode.kind === "focusZone" ||
|
|
398
|
-
cur.vnode.kind === "focusTrap" ||
|
|
399
|
-
cur.vnode.kind === "modal") {
|
|
400
|
-
continue;
|
|
401
|
-
}
|
|
402
|
-
const id = isFocusableInteractive(cur.vnode) ? isEnabledInteractive(cur.vnode) : null;
|
|
403
|
-
if (id !== null)
|
|
404
|
-
out.push(id);
|
|
405
|
-
for (let i = cur.children.length - 1; i >= 0; i--) {
|
|
406
|
-
const c = cur.children[i];
|
|
407
|
-
if (c)
|
|
408
|
-
stack.push(c);
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
return Object.freeze(out);
|
|
412
|
-
}
|
|
413
|
-
function collectFocusableIdsInTrapSubtree(node) {
|
|
414
|
-
const out = [];
|
|
415
|
-
const stack = [node];
|
|
416
|
-
while (stack.length > 0) {
|
|
417
|
-
const current = stack.pop();
|
|
418
|
-
if (!current)
|
|
419
|
-
continue;
|
|
420
|
-
if (current.vnode.kind === "focusTrap" || current.vnode.kind === "modal") {
|
|
421
|
-
continue;
|
|
422
|
-
}
|
|
423
|
-
const id = isFocusableInteractive(current.vnode) ? isEnabledInteractive(current.vnode) : null;
|
|
424
|
-
if (id !== null)
|
|
425
|
-
out.push(id);
|
|
426
|
-
for (let i = current.children.length - 1; i >= 0; i--) {
|
|
427
|
-
const child = current.children[i];
|
|
428
|
-
if (child)
|
|
429
|
-
stack.push(child);
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
return Object.freeze(out);
|
|
433
|
-
}
|
|
434
|
-
/**
|
|
435
|
-
* Collect all focus zones from a committed runtime tree.
|
|
436
|
-
*
|
|
437
|
-
* - Order: depth-first preorder
|
|
438
|
-
* - Each zone contains only the focusable ids directly within it (not in nested zones/traps)
|
|
439
|
-
*/
|
|
440
|
-
export function collectFocusZones(tree) {
|
|
441
|
-
const m = new Map();
|
|
442
|
-
const seen = new Map();
|
|
443
|
-
const stack = [
|
|
444
|
-
{ node: tree, parentZoneId: null },
|
|
445
|
-
];
|
|
446
|
-
while (stack.length > 0) {
|
|
447
|
-
const item = stack.pop();
|
|
448
|
-
if (!item)
|
|
449
|
-
continue;
|
|
450
|
-
const node = item.node;
|
|
451
|
-
if (node.vnode.kind === "focusZone") {
|
|
452
|
-
const props = node.vnode.props;
|
|
453
|
-
const id = typeof props.id === "string" && props.id.length > 0 ? props.id : null;
|
|
454
|
-
if (id !== null) {
|
|
455
|
-
recordFocusContainerId(seen, id, "focusZone");
|
|
456
|
-
const tabIndex = typeof props.tabIndex === "number" ? props.tabIndex : 0;
|
|
457
|
-
const navigation = props.navigation === "linear" ||
|
|
458
|
-
props.navigation === "grid" ||
|
|
459
|
-
props.navigation === "none"
|
|
460
|
-
? props.navigation
|
|
461
|
-
: "linear";
|
|
462
|
-
const columns = typeof props.columns === "number" && props.columns > 0 ? props.columns : 1;
|
|
463
|
-
const wrapAround = props.wrapAround !== false;
|
|
464
|
-
const onEnter = typeof props.onEnter === "function" ? props.onEnter : undefined;
|
|
465
|
-
const onExit = typeof props.onExit === "function" ? props.onExit : undefined;
|
|
466
|
-
// Collect focusable ids from zone children (not traversing into nested zones/traps)
|
|
467
|
-
const focusableIds = [];
|
|
468
|
-
for (const child of node.children) {
|
|
469
|
-
if (child.vnode.kind === "focusZone" ||
|
|
470
|
-
child.vnode.kind === "focusTrap" ||
|
|
471
|
-
child.vnode.kind === "modal") {
|
|
472
|
-
continue;
|
|
473
|
-
}
|
|
474
|
-
const childFocusables = collectFocusableIdsInSubtree(child);
|
|
475
|
-
focusableIds.push(...childFocusables);
|
|
476
|
-
}
|
|
477
|
-
const zone = {
|
|
478
|
-
id,
|
|
479
|
-
tabIndex,
|
|
480
|
-
navigation,
|
|
481
|
-
columns,
|
|
482
|
-
wrapAround,
|
|
483
|
-
focusableIds: Object.freeze(focusableIds),
|
|
484
|
-
};
|
|
485
|
-
if (item.parentZoneId !== null) {
|
|
486
|
-
zone.parentZoneId = item.parentZoneId;
|
|
487
|
-
}
|
|
488
|
-
if (onEnter !== undefined) {
|
|
489
|
-
zone.onEnter = onEnter;
|
|
490
|
-
}
|
|
491
|
-
if (onExit !== undefined) {
|
|
492
|
-
zone.onExit = onExit;
|
|
493
|
-
}
|
|
494
|
-
m.set(id, Object.freeze(zone));
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
// Continue traversing children for nested zones
|
|
498
|
-
let childParentZoneId = item.parentZoneId;
|
|
499
|
-
if (node.vnode.kind === "focusZone") {
|
|
500
|
-
const zoneId = node.vnode.props.id;
|
|
501
|
-
if (typeof zoneId === "string" && zoneId.length > 0) {
|
|
502
|
-
childParentZoneId = zoneId;
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
for (let i = node.children.length - 1; i >= 0; i--) {
|
|
506
|
-
const c = node.children[i];
|
|
507
|
-
if (c)
|
|
508
|
-
stack.push({ node: c, parentZoneId: childParentZoneId ?? null });
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
return m;
|
|
512
|
-
}
|
|
513
|
-
/**
|
|
514
|
-
* Collect all focus traps from a committed runtime tree.
|
|
515
|
-
*
|
|
516
|
-
* - Order: depth-first preorder
|
|
517
|
-
* - Each trap contains only the focusable ids directly within it (not in nested zones/traps)
|
|
518
|
-
*/
|
|
519
|
-
export function collectFocusTraps(tree) {
|
|
520
|
-
const m = new Map();
|
|
521
|
-
const seen = new Map();
|
|
522
|
-
const stack = [tree];
|
|
523
|
-
while (stack.length > 0) {
|
|
524
|
-
const node = stack.pop();
|
|
525
|
-
if (!node)
|
|
526
|
-
continue;
|
|
527
|
-
if (node.vnode.kind === "focusTrap" || node.vnode.kind === "modal") {
|
|
528
|
-
const props = node.vnode.props;
|
|
529
|
-
const id = typeof props.id === "string" && props.id.length > 0 ? props.id : null;
|
|
530
|
-
if (id !== null) {
|
|
531
|
-
recordFocusContainerId(seen, id, node.vnode.kind);
|
|
532
|
-
const active = node.vnode.kind === "modal" ? true : props.active === true;
|
|
533
|
-
const returnFocusTo = typeof props.returnFocusTo === "string" ? props.returnFocusTo : null;
|
|
534
|
-
const initialFocus = typeof props.initialFocus === "string" ? props.initialFocus : null;
|
|
535
|
-
// Collect focusable ids from trap children, including nested zones but
|
|
536
|
-
// excluding nested traps/modals which manage their own focus scope.
|
|
537
|
-
const focusableIds = [];
|
|
538
|
-
for (const child of node.children) {
|
|
539
|
-
if (child.vnode.kind === "focusTrap" || child.vnode.kind === "modal")
|
|
540
|
-
continue;
|
|
541
|
-
const childFocusables = collectFocusableIdsInTrapSubtree(child);
|
|
542
|
-
focusableIds.push(...childFocusables);
|
|
543
|
-
}
|
|
544
|
-
m.set(id, Object.freeze({
|
|
545
|
-
id,
|
|
546
|
-
kind: node.vnode.kind,
|
|
547
|
-
active,
|
|
548
|
-
returnFocusTo,
|
|
549
|
-
initialFocus,
|
|
550
|
-
focusableIds: Object.freeze(focusableIds),
|
|
551
|
-
}));
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
// Continue traversing children for nested traps
|
|
555
|
-
for (let i = node.children.length - 1; i >= 0; i--) {
|
|
556
|
-
const c = node.children[i];
|
|
557
|
-
if (c)
|
|
558
|
-
stack.push(c);
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
return m;
|
|
562
|
-
}
|
|
563
|
-
function requiresRoutingRebuild(vnode) {
|
|
564
|
-
return getWidgetProtocol(vnode.kind).requiresRoutingRebuild;
|
|
565
|
-
}
|
|
566
|
-
/**
|
|
567
|
-
* Reusable metadata collector that pools internal data structures.
|
|
568
|
-
*
|
|
569
|
-
* Instead of allocating new Maps/Sets/arrays on every frame, this class
|
|
570
|
-
* clears and reuses its internal collections, reducing GC pressure.
|
|
571
|
-
*
|
|
572
|
-
* Usage:
|
|
573
|
-
* const collector = createWidgetMetadataCollector();
|
|
574
|
-
* // Each frame:
|
|
575
|
-
* const metadata = collector.collect(committedRoot);
|
|
576
|
-
*/
|
|
577
|
-
export class WidgetMetadataCollector {
|
|
578
|
-
// Output collections (reused)
|
|
579
|
-
_focusableIds = [];
|
|
580
|
-
_focusInfoById = new Map();
|
|
581
|
-
_enabledById = new Map();
|
|
582
|
-
_pressableIds = new Set();
|
|
583
|
-
_inputById = new Map();
|
|
584
|
-
// Zone/trap intermediate data (reused)
|
|
585
|
-
_focusContainerKindsById = new Map();
|
|
586
|
-
_zoneDataById = new Map();
|
|
587
|
-
_trapDataById = new Map();
|
|
588
|
-
_zoneFocusables = new Map();
|
|
589
|
-
_trapFocusables = new Map();
|
|
590
|
-
// Container stack for tracking current zone/trap nesting (reused)
|
|
591
|
-
_containerStack = [];
|
|
592
|
-
_fieldStack = [];
|
|
593
|
-
// Traversal stack (reused)
|
|
594
|
-
_stack = [];
|
|
595
|
-
// Final output maps (reused)
|
|
596
|
-
_zones = new Map();
|
|
597
|
-
_traps = new Map();
|
|
598
|
-
/**
|
|
599
|
-
* Collect all widget metadata in a single tree traversal.
|
|
600
|
-
*
|
|
601
|
-
* Internal collections are cleared and reused to avoid allocations.
|
|
602
|
-
*/
|
|
603
|
-
collect(tree) {
|
|
604
|
-
// Clear all reusable collections
|
|
605
|
-
this._focusableIds.length = 0;
|
|
606
|
-
this._focusInfoById.clear();
|
|
607
|
-
this._enabledById.clear();
|
|
608
|
-
this._pressableIds.clear();
|
|
609
|
-
this._inputById.clear();
|
|
610
|
-
this._focusContainerKindsById.clear();
|
|
611
|
-
this._zoneDataById.clear();
|
|
612
|
-
this._trapDataById.clear();
|
|
613
|
-
this._zoneFocusables.clear();
|
|
614
|
-
this._trapFocusables.clear();
|
|
615
|
-
this._containerStack.length = 0;
|
|
616
|
-
this._fieldStack.length = 0;
|
|
617
|
-
this._stack.length = 0;
|
|
618
|
-
this._zones.clear();
|
|
619
|
-
this._traps.clear();
|
|
620
|
-
let hasRoutingWidgets = false;
|
|
621
|
-
this._stack.push({ type: "node", node: tree });
|
|
622
|
-
while (this._stack.length > 0) {
|
|
623
|
-
const item = this._stack.pop();
|
|
624
|
-
if (!item)
|
|
625
|
-
continue;
|
|
626
|
-
// Handle exit markers.
|
|
627
|
-
if (item.type === "containerExit") {
|
|
628
|
-
this._containerStack.pop();
|
|
629
|
-
continue;
|
|
630
|
-
}
|
|
631
|
-
if (item.type === "fieldExit") {
|
|
632
|
-
this._fieldStack.pop();
|
|
633
|
-
continue;
|
|
634
|
-
}
|
|
635
|
-
const node = item.node;
|
|
636
|
-
const vnode = node.vnode;
|
|
637
|
-
if (!hasRoutingWidgets && requiresRoutingRebuild(vnode)) {
|
|
638
|
-
hasRoutingWidgets = true;
|
|
639
|
-
}
|
|
640
|
-
const fieldContext = readFieldContext(vnode);
|
|
641
|
-
if (fieldContext !== null) {
|
|
642
|
-
this._fieldStack.push(fieldContext);
|
|
643
|
-
this._stack.push({ type: "fieldExit" });
|
|
644
|
-
}
|
|
645
|
-
// --- Collect focusable IDs ---
|
|
646
|
-
const focusableId = isFocusableInteractive(vnode) ? isEnabledInteractive(vnode) : null;
|
|
647
|
-
if (focusableId !== null) {
|
|
648
|
-
this._focusableIds.push(focusableId);
|
|
649
|
-
if (!this._focusInfoById.has(focusableId)) {
|
|
650
|
-
this._focusInfoById.set(focusableId, buildFocusInfo(vnode, focusableId, this._fieldStack));
|
|
651
|
-
}
|
|
652
|
-
for (let i = this._containerStack.length - 1; i >= 0; i--) {
|
|
653
|
-
const container = this._containerStack[i];
|
|
654
|
-
if (container?.kind === "trap") {
|
|
655
|
-
break;
|
|
656
|
-
}
|
|
657
|
-
if (container?.kind === "zone") {
|
|
658
|
-
this._zoneFocusables.get(container.id)?.push(focusableId);
|
|
659
|
-
break;
|
|
660
|
-
}
|
|
661
|
-
}
|
|
662
|
-
for (let i = this._containerStack.length - 1; i >= 0; i--) {
|
|
663
|
-
const container = this._containerStack[i];
|
|
664
|
-
if (container?.kind === "trap") {
|
|
665
|
-
this._trapFocusables.get(container.id)?.push(focusableId);
|
|
666
|
-
break;
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
// --- Collect enabled map ---
|
|
671
|
-
const interactiveId = readInteractiveId(vnode);
|
|
672
|
-
if (interactiveId !== null && !this._enabledById.has(interactiveId)) {
|
|
673
|
-
let enabled = true;
|
|
674
|
-
const proto = getWidgetProtocol(vnode.kind);
|
|
675
|
-
if (proto.disableable) {
|
|
676
|
-
const disabled = vnode.props.disabled;
|
|
677
|
-
enabled = disabled !== true;
|
|
678
|
-
}
|
|
679
|
-
if (proto.openGated) {
|
|
680
|
-
const open = vnode.props.open;
|
|
681
|
-
enabled = open === true;
|
|
682
|
-
}
|
|
683
|
-
this._enabledById.set(interactiveId, enabled);
|
|
684
|
-
}
|
|
685
|
-
// --- Collect pressable IDs ---
|
|
686
|
-
if (vnode.kind === "button" || vnode.kind === "link") {
|
|
687
|
-
const id = vnode.props.id;
|
|
688
|
-
if (typeof id === "string" && id.length > 0) {
|
|
689
|
-
this._pressableIds.add(id);
|
|
690
|
-
}
|
|
691
|
-
}
|
|
692
|
-
// --- Collect input metadata ---
|
|
693
|
-
if (vnode.kind === "input") {
|
|
694
|
-
const props = vnode.props;
|
|
695
|
-
const id = typeof props.id === "string" && props.id.length > 0 ? props.id : null;
|
|
696
|
-
const value = typeof props.value === "string" ? props.value : null;
|
|
697
|
-
if (id !== null && value !== null && !this._inputById.has(id)) {
|
|
698
|
-
const disabled = props.disabled === true;
|
|
699
|
-
const readOnly = props.readOnly === true;
|
|
700
|
-
const multiline = props.multiline === true;
|
|
701
|
-
const rowsRaw = typeof props.rows === "number" && Number.isFinite(props.rows) ? props.rows : 3;
|
|
702
|
-
const rows = multiline ? Math.max(1, Math.trunc(rowsRaw)) : 1;
|
|
703
|
-
const wordWrap = multiline ? props.wordWrap !== false : false;
|
|
704
|
-
const onInput = typeof props.onInput === "function"
|
|
705
|
-
? props.onInput
|
|
706
|
-
: undefined;
|
|
707
|
-
const onBlur = typeof props.onBlur === "function" ? props.onBlur : undefined;
|
|
708
|
-
const metaBase = {
|
|
709
|
-
instanceId: node.instanceId,
|
|
710
|
-
value,
|
|
711
|
-
disabled,
|
|
712
|
-
readOnly,
|
|
713
|
-
multiline,
|
|
714
|
-
rows,
|
|
715
|
-
wordWrap,
|
|
716
|
-
};
|
|
717
|
-
const meta = onInput || onBlur
|
|
718
|
-
? Object.freeze({
|
|
719
|
-
...metaBase,
|
|
720
|
-
...(onInput ? { onInput } : {}),
|
|
721
|
-
...(onBlur ? { onBlur } : {}),
|
|
722
|
-
})
|
|
723
|
-
: Object.freeze(metaBase);
|
|
724
|
-
this._inputById.set(id, meta);
|
|
725
|
-
}
|
|
726
|
-
}
|
|
727
|
-
// --- Collect focus zones ---
|
|
728
|
-
if (vnode.kind === "focusZone") {
|
|
729
|
-
const props = vnode.props;
|
|
730
|
-
const id = typeof props.id === "string" && props.id.length > 0 ? props.id : null;
|
|
731
|
-
if (id !== null) {
|
|
732
|
-
recordFocusContainerId(this._focusContainerKindsById, id, "focusZone");
|
|
733
|
-
const tabIndex = typeof props.tabIndex === "number" ? props.tabIndex : 0;
|
|
734
|
-
const navigation = props.navigation === "linear" ||
|
|
735
|
-
props.navigation === "grid" ||
|
|
736
|
-
props.navigation === "none"
|
|
737
|
-
? props.navigation
|
|
738
|
-
: "linear";
|
|
739
|
-
const columns = typeof props.columns === "number" && props.columns > 0 ? props.columns : 1;
|
|
740
|
-
const wrapAround = props.wrapAround !== false;
|
|
741
|
-
const onEnter = typeof props.onEnter === "function" ? props.onEnter : undefined;
|
|
742
|
-
const onExit = typeof props.onExit === "function" ? props.onExit : undefined;
|
|
743
|
-
let parentZoneId = null;
|
|
744
|
-
for (let i = this._containerStack.length - 1; i >= 0; i--) {
|
|
745
|
-
const container = this._containerStack[i];
|
|
746
|
-
if (container?.kind === "zone") {
|
|
747
|
-
parentZoneId = container.id;
|
|
748
|
-
break;
|
|
749
|
-
}
|
|
750
|
-
}
|
|
751
|
-
const zoneData = { id, tabIndex, navigation, columns, wrapAround };
|
|
752
|
-
if (parentZoneId !== null) {
|
|
753
|
-
zoneData.parentZoneId = parentZoneId;
|
|
754
|
-
}
|
|
755
|
-
if (onEnter !== undefined) {
|
|
756
|
-
zoneData.onEnter = onEnter;
|
|
757
|
-
}
|
|
758
|
-
if (onExit !== undefined) {
|
|
759
|
-
zoneData.onExit = onExit;
|
|
760
|
-
}
|
|
761
|
-
this._zoneDataById.set(id, zoneData);
|
|
762
|
-
this._zoneFocusables.set(id, []);
|
|
763
|
-
// Push exit marker and enter container
|
|
764
|
-
const container = { kind: "zone", id };
|
|
765
|
-
this._containerStack.push(container);
|
|
766
|
-
this._stack.push({ type: "containerExit", container });
|
|
767
|
-
}
|
|
768
|
-
}
|
|
769
|
-
// --- Collect focus traps ---
|
|
770
|
-
if (vnode.kind === "focusTrap" || vnode.kind === "modal") {
|
|
771
|
-
const props = vnode.props;
|
|
772
|
-
const id = typeof props.id === "string" && props.id.length > 0 ? props.id : null;
|
|
773
|
-
if (id !== null) {
|
|
774
|
-
recordFocusContainerId(this._focusContainerKindsById, id, vnode.kind);
|
|
775
|
-
const active = vnode.kind === "modal" ? true : props.active === true;
|
|
776
|
-
const returnFocusTo = typeof props.returnFocusTo === "string" ? props.returnFocusTo : null;
|
|
777
|
-
const initialFocus = typeof props.initialFocus === "string" ? props.initialFocus : null;
|
|
778
|
-
this._trapDataById.set(id, {
|
|
779
|
-
id,
|
|
780
|
-
active,
|
|
781
|
-
returnFocusTo,
|
|
782
|
-
initialFocus,
|
|
783
|
-
});
|
|
784
|
-
this._trapFocusables.set(id, []);
|
|
785
|
-
// Push exit marker and enter container
|
|
786
|
-
const container = { kind: "trap", id };
|
|
787
|
-
this._containerStack.push(container);
|
|
788
|
-
this._stack.push({ type: "containerExit", container });
|
|
789
|
-
}
|
|
790
|
-
}
|
|
791
|
-
// Push children in reverse order for correct traversal order
|
|
792
|
-
for (let i = node.children.length - 1; i >= 0; i--) {
|
|
793
|
-
const c = node.children[i];
|
|
794
|
-
if (c)
|
|
795
|
-
this._stack.push({ type: "node", node: c });
|
|
796
|
-
}
|
|
797
|
-
}
|
|
798
|
-
// Build final zones with focusableIds
|
|
799
|
-
for (const [id, data] of this._zoneDataById) {
|
|
800
|
-
const focusables = this._zoneFocusables.get(id) ?? [];
|
|
801
|
-
this._zones.set(id, Object.freeze({
|
|
802
|
-
...data,
|
|
803
|
-
focusableIds: Object.freeze(focusables.slice()),
|
|
804
|
-
}));
|
|
805
|
-
}
|
|
806
|
-
// Build final traps with focusableIds
|
|
807
|
-
for (const [id, data] of this._trapDataById) {
|
|
808
|
-
const focusables = this._trapFocusables.get(id) ?? [];
|
|
809
|
-
this._traps.set(id, Object.freeze({
|
|
810
|
-
...data,
|
|
811
|
-
focusableIds: Object.freeze(focusables.slice()),
|
|
812
|
-
}));
|
|
813
|
-
}
|
|
814
|
-
return Object.freeze({
|
|
815
|
-
focusableIds: Object.freeze(this._focusableIds.slice()),
|
|
816
|
-
focusInfoById: new Map(this._focusInfoById),
|
|
817
|
-
enabledById: new Map(this._enabledById),
|
|
818
|
-
pressableIds: new Set(this._pressableIds),
|
|
819
|
-
inputById: new Map(this._inputById),
|
|
820
|
-
zones: new Map(this._zones),
|
|
821
|
-
traps: new Map(this._traps),
|
|
822
|
-
hasRoutingWidgets,
|
|
823
|
-
});
|
|
824
|
-
}
|
|
825
|
-
}
|
|
826
|
-
/** Create a reusable widget metadata collector. */
|
|
827
|
-
export function createWidgetMetadataCollector() {
|
|
828
|
-
return new WidgetMetadataCollector();
|
|
829
|
-
}
|
|
830
|
-
/**
|
|
831
|
-
* Collect all widget metadata in a single tree traversal.
|
|
832
|
-
*
|
|
833
|
-
* This replaces:
|
|
834
|
-
* - collectFocusableIds()
|
|
835
|
-
* - collectEnabledMap()
|
|
836
|
-
* - collectPressableIds()
|
|
837
|
-
* - collectInputMetaById()
|
|
838
|
-
* - collectFocusZones()
|
|
839
|
-
* - collectFocusTraps()
|
|
840
|
-
*
|
|
841
|
-
* Performance: O(n) where n = number of nodes in the tree.
|
|
842
|
-
*
|
|
843
|
-
* Note: This function allocates new collections each call. For hot paths,
|
|
844
|
-
* use createWidgetMetadataCollector() to reuse collections across frames.
|
|
4
|
+
* Why: Keep the stable widget metadata entrypoint small while delegating
|
|
5
|
+
* concern-specific logic to internal modules under `runtime/widgetMeta/`.
|
|
845
6
|
*/
|
|
846
|
-
export
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
return collector.collect(tree);
|
|
850
|
-
}
|
|
7
|
+
export { WidgetMetadataCollector, collectAllWidgetMetadata, createWidgetMetadataCollector, } from "./widgetMeta/collector.js";
|
|
8
|
+
export { collectFocusTraps, collectFocusZones } from "./widgetMeta/focusContainers.js";
|
|
9
|
+
export { collectEnabledMap, collectFocusableIds, collectInputMetaById, collectPressableIds, } from "./widgetMeta/helpers.js";
|
|
851
10
|
//# sourceMappingURL=widgetMeta.js.map
|