@pwfabric/runtime 0.0.0-stage → 1.0.0-rc.1
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/LICENSE +90 -0
- package/README.md +43 -2
- package/dist/a11y/announcer.d.ts +65 -0
- package/dist/a11y/announcer.js +140 -0
- package/dist/a11y/announcer.js.map +1 -0
- package/dist/a11y/contrast.d.ts +54 -0
- package/dist/a11y/contrast.js +93 -0
- package/dist/a11y/contrast.js.map +1 -0
- package/dist/a11y/focus.d.ts +123 -0
- package/dist/a11y/focus.js +282 -0
- package/dist/a11y/focus.js.map +1 -0
- package/dist/a11y/index.d.ts +21 -0
- package/dist/a11y/index.js +18 -0
- package/dist/a11y/index.js.map +1 -0
- package/dist/a11y/keyboard.d.ts +144 -0
- package/dist/a11y/keyboard.js +317 -0
- package/dist/a11y/keyboard.js.map +1 -0
- package/dist/adapters/default-image.d.ts +25 -0
- package/dist/adapters/default-image.js +37 -0
- package/dist/adapters/default-image.js.map +1 -0
- package/dist/adapters/index.d.ts +27 -0
- package/dist/adapters/index.js +28 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/next-image.d.ts +38 -0
- package/dist/adapters/next-image.js +124 -0
- package/dist/adapters/next-image.js.map +1 -0
- package/dist/adapters/types.d.ts +61 -0
- package/dist/adapters/types.js +11 -0
- package/dist/adapters/types.js.map +1 -0
- package/dist/analytics/index.d.ts +14 -0
- package/dist/analytics/index.js +15 -0
- package/dist/analytics/index.js.map +1 -0
- package/dist/analytics/tracker.d.ts +65 -0
- package/dist/analytics/tracker.js +251 -0
- package/dist/analytics/tracker.js.map +1 -0
- package/dist/analytics/types.d.ts +130 -0
- package/dist/analytics/types.js +11 -0
- package/dist/analytics/types.js.map +1 -0
- package/dist/auth/index.d.ts +15 -0
- package/dist/auth/index.js +14 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/provider.d.ts +132 -0
- package/dist/auth/provider.js +473 -0
- package/dist/auth/provider.js.map +1 -0
- package/dist/auth/types.d.ts +117 -0
- package/dist/auth/types.js +9 -0
- package/dist/auth/types.js.map +1 -0
- package/dist/binding/collection.d.ts +190 -0
- package/dist/binding/collection.js +358 -0
- package/dist/binding/collection.js.map +1 -0
- package/dist/binding/datasource.d.ts +319 -0
- package/dist/binding/datasource.js +556 -0
- package/dist/binding/datasource.js.map +1 -0
- package/dist/binding/engine.d.ts +65 -0
- package/dist/binding/engine.js +268 -0
- package/dist/binding/engine.js.map +1 -0
- package/dist/binding/expression.d.ts +150 -0
- package/dist/binding/expression.js +787 -0
- package/dist/binding/expression.js.map +1 -0
- package/dist/binding/index.d.ts +19 -0
- package/dist/binding/index.js +14 -0
- package/dist/binding/index.js.map +1 -0
- package/dist/binding/two-way.d.ts +149 -0
- package/dist/binding/two-way.js +462 -0
- package/dist/binding/two-way.js.map +1 -0
- package/dist/binding/types.d.ts +119 -0
- package/dist/binding/types.js +9 -0
- package/dist/binding/types.js.map +1 -0
- package/dist/blocks/component-types.d.ts +81 -0
- package/dist/blocks/component-types.js +11 -0
- package/dist/blocks/component-types.js.map +1 -0
- package/dist/blocks/composition-rules.d.ts +168 -0
- package/dist/blocks/composition-rules.js +391 -0
- package/dist/blocks/composition-rules.js.map +1 -0
- package/dist/blocks/hooks.d.ts +31 -0
- package/dist/blocks/hooks.js +42 -0
- package/dist/blocks/hooks.js.map +1 -0
- package/dist/blocks/index.d.ts +14 -0
- package/dist/blocks/index.js +23 -0
- package/dist/blocks/index.js.map +1 -0
- package/dist/blocks/registry.d.ts +84 -0
- package/dist/blocks/registry.js +136 -0
- package/dist/blocks/registry.js.map +1 -0
- package/dist/blocks/rule-engine.d.ts +131 -0
- package/dist/blocks/rule-engine.js +321 -0
- package/dist/blocks/rule-engine.js.map +1 -0
- package/dist/blocks/surface-event-context.d.ts +13 -0
- package/dist/blocks/surface-event-context.js +14 -0
- package/dist/blocks/surface-event-context.js.map +1 -0
- package/dist/blocks/surface-id-context.d.ts +13 -0
- package/dist/blocks/surface-id-context.js +15 -0
- package/dist/blocks/surface-id-context.js.map +1 -0
- package/dist/bus.d.ts +41 -0
- package/dist/bus.js +158 -0
- package/dist/bus.js.map +1 -0
- package/dist/cache/index.d.ts +9 -0
- package/dist/cache/index.js +10 -0
- package/dist/cache/index.js.map +1 -0
- package/dist/cache/memory.d.ts +32 -0
- package/dist/cache/memory.js +138 -0
- package/dist/cache/memory.js.map +1 -0
- package/dist/cache/types.d.ts +62 -0
- package/dist/cache/types.js +9 -0
- package/dist/cache/types.js.map +1 -0
- package/dist/capability/index.d.ts +72 -0
- package/dist/capability/index.js +87 -0
- package/dist/capability/index.js.map +1 -0
- package/dist/capability/kernel-capabilities.d.ts +31 -0
- package/dist/capability/kernel-capabilities.js +36 -0
- package/dist/capability/kernel-capabilities.js.map +1 -0
- package/dist/capability/platform-capabilities.d.ts +71 -0
- package/dist/capability/platform-capabilities.js +267 -0
- package/dist/capability/platform-capabilities.js.map +1 -0
- package/dist/capability/type-mapping.d.ts +70 -0
- package/dist/capability/type-mapping.js +107 -0
- package/dist/capability/type-mapping.js.map +1 -0
- package/dist/capability/validation.d.ts +46 -0
- package/dist/capability/validation.js +54 -0
- package/dist/capability/validation.js.map +1 -0
- package/dist/connectors/declared-connector.d.ts +48 -0
- package/dist/connectors/declared-connector.js +333 -0
- package/dist/connectors/declared-connector.js.map +1 -0
- package/dist/connectors/engine-registry.d.ts +50 -0
- package/dist/connectors/engine-registry.js +135 -0
- package/dist/connectors/engine-registry.js.map +1 -0
- package/dist/connectors/index.d.ts +14 -0
- package/dist/connectors/index.js +14 -0
- package/dist/connectors/index.js.map +1 -0
- package/dist/connectors/module-loader.d.ts +70 -0
- package/dist/connectors/module-loader.js +143 -0
- package/dist/connectors/module-loader.js.map +1 -0
- package/dist/connectors/types.d.ts +174 -0
- package/dist/connectors/types.js +23 -0
- package/dist/connectors/types.js.map +1 -0
- package/dist/context.d.ts +27 -0
- package/dist/context.js +38 -0
- package/dist/context.js.map +1 -0
- package/dist/data/DataCapabilityProvider.d.ts +39 -0
- package/dist/data/DataCapabilityProvider.js +58 -0
- package/dist/data/DataCapabilityProvider.js.map +1 -0
- package/dist/data/index.d.ts +17 -0
- package/dist/data/index.js +18 -0
- package/dist/data/index.js.map +1 -0
- package/dist/data/manager.d.ts +40 -0
- package/dist/data/manager.js +442 -0
- package/dist/data/manager.js.map +1 -0
- package/dist/data/types.d.ts +218 -0
- package/dist/data/types.js +2 -0
- package/dist/data/types.js.map +1 -0
- package/dist/entity/aggregate.d.ts +8 -0
- package/dist/entity/aggregate.js +24 -0
- package/dist/entity/aggregate.js.map +1 -0
- package/dist/entity/apply-query.d.ts +58 -0
- package/dist/entity/apply-query.js +264 -0
- package/dist/entity/apply-query.js.map +1 -0
- package/dist/entity/behavior.d.ts +94 -0
- package/dist/entity/behavior.js +78 -0
- package/dist/entity/behavior.js.map +1 -0
- package/dist/entity/connector.d.ts +141 -0
- package/dist/entity/connector.js +89 -0
- package/dist/entity/connector.js.map +1 -0
- package/dist/entity/connectors/index.d.ts +12 -0
- package/dist/entity/connectors/index.js +11 -0
- package/dist/entity/connectors/index.js.map +1 -0
- package/dist/entity/connectors/static.d.ts +51 -0
- package/dist/entity/connectors/static.js +245 -0
- package/dist/entity/connectors/static.js.map +1 -0
- package/dist/entity/entity-datasource.d.ts +50 -0
- package/dist/entity/entity-datasource.js +33 -0
- package/dist/entity/entity-datasource.js.map +1 -0
- package/dist/entity/facade.d.ts +70 -0
- package/dist/entity/facade.js +70 -0
- package/dist/entity/facade.js.map +1 -0
- package/dist/entity/http-connector.d.ts +123 -0
- package/dist/entity/http-connector.js +295 -0
- package/dist/entity/http-connector.js.map +1 -0
- package/dist/entity/index.d.ts +38 -0
- package/dist/entity/index.js +31 -0
- package/dist/entity/index.js.map +1 -0
- package/dist/entity/postgres-connector.d.ts +64 -0
- package/dist/entity/postgres-connector.js +239 -0
- package/dist/entity/postgres-connector.js.map +1 -0
- package/dist/entity/registry.d.ts +59 -0
- package/dist/entity/registry.js +90 -0
- package/dist/entity/registry.js.map +1 -0
- package/dist/entity/store.d.ts +66 -0
- package/dist/entity/store.js +349 -0
- package/dist/entity/store.js.map +1 -0
- package/dist/entity/types.d.ts +180 -0
- package/dist/entity/types.js +10 -0
- package/dist/entity/types.js.map +1 -0
- package/dist/execution-context.d.ts +88 -0
- package/dist/execution-context.js +146 -0
- package/dist/execution-context.js.map +1 -0
- package/dist/executor.d.ts +27 -0
- package/dist/executor.js +33 -0
- package/dist/executor.js.map +1 -0
- package/dist/forms/form-entity-bridge.d.ts +94 -0
- package/dist/forms/form-entity-bridge.js +96 -0
- package/dist/forms/form-entity-bridge.js.map +1 -0
- package/dist/forms/form-write-target.d.ts +34 -0
- package/dist/forms/form-write-target.js +81 -0
- package/dist/forms/form-write-target.js.map +1 -0
- package/dist/forms/index.d.ts +22 -0
- package/dist/forms/index.js +25 -0
- package/dist/forms/index.js.map +1 -0
- package/dist/forms/named-control-values.d.ts +27 -0
- package/dist/forms/named-control-values.js +42 -0
- package/dist/forms/named-control-values.js.map +1 -0
- package/dist/forms/processor.d.ts +55 -0
- package/dist/forms/processor.js +154 -0
- package/dist/forms/processor.js.map +1 -0
- package/dist/forms/types.d.ts +174 -0
- package/dist/forms/types.js +33 -0
- package/dist/forms/types.js.map +1 -0
- package/dist/forms/validator.d.ts +61 -0
- package/dist/forms/validator.js +218 -0
- package/dist/forms/validator.js.map +1 -0
- package/dist/graph/index.d.ts +10 -0
- package/dist/graph/index.js +9 -0
- package/dist/graph/index.js.map +1 -0
- package/dist/graph/resolver.d.ts +66 -0
- package/dist/graph/resolver.js +243 -0
- package/dist/graph/resolver.js.map +1 -0
- package/dist/graph/types.d.ts +125 -0
- package/dist/graph/types.js +9 -0
- package/dist/graph/types.js.map +1 -0
- package/dist/hooks/useInstalledAtoms.d.ts +96 -0
- package/dist/hooks/useInstalledAtoms.js +92 -0
- package/dist/hooks/useInstalledAtoms.js.map +1 -0
- package/dist/i18n/index.d.ts +15 -0
- package/dist/i18n/index.js +14 -0
- package/dist/i18n/index.js.map +1 -0
- package/dist/i18n/translator.d.ts +76 -0
- package/dist/i18n/translator.js +357 -0
- package/dist/i18n/translator.js.map +1 -0
- package/dist/i18n/types.d.ts +93 -0
- package/dist/i18n/types.js +9 -0
- package/dist/i18n/types.js.map +1 -0
- package/dist/index.d.ts +64 -0
- package/dist/index.js +95 -0
- package/dist/index.js.map +1 -0
- package/dist/kernel.d.ts +47 -0
- package/dist/kernel.js +258 -0
- package/dist/kernel.js.map +1 -0
- package/dist/perf/budget.d.ts +92 -0
- package/dist/perf/budget.js +122 -0
- package/dist/perf/budget.js.map +1 -0
- package/dist/perf/index.d.ts +16 -0
- package/dist/perf/index.js +13 -0
- package/dist/perf/index.js.map +1 -0
- package/dist/perf/render-timer.d.ts +90 -0
- package/dist/perf/render-timer.js +136 -0
- package/dist/perf/render-timer.js.map +1 -0
- package/dist/perf/reporter.d.ts +126 -0
- package/dist/perf/reporter.js +241 -0
- package/dist/perf/reporter.js.map +1 -0
- package/dist/perf/web-vitals.d.ts +111 -0
- package/dist/perf/web-vitals.js +268 -0
- package/dist/perf/web-vitals.js.map +1 -0
- package/dist/registry.d.ts +63 -0
- package/dist/registry.js +126 -0
- package/dist/registry.js.map +1 -0
- package/dist/render/BehaviorSurfaceProvider.d.ts +65 -0
- package/dist/render/BehaviorSurfaceProvider.js +185 -0
- package/dist/render/BehaviorSurfaceProvider.js.map +1 -0
- package/dist/render/BlockErrorBoundary.d.ts +26 -0
- package/dist/render/BlockErrorBoundary.js +41 -0
- package/dist/render/BlockErrorBoundary.js.map +1 -0
- package/dist/render/DataBoundSurfaceRenderer.d.ts +78 -0
- package/dist/render/DataBoundSurfaceRenderer.js +998 -0
- package/dist/render/DataBoundSurfaceRenderer.js.map +1 -0
- package/dist/render/DynamicBlockWrapper.d.ts +23 -0
- package/dist/render/DynamicBlockWrapper.js +117 -0
- package/dist/render/DynamicBlockWrapper.js.map +1 -0
- package/dist/render/appearance-resolver.d.ts +50 -0
- package/dist/render/appearance-resolver.js +78 -0
- package/dist/render/appearance-resolver.js.map +1 -0
- package/dist/render/capability-gate.d.ts +69 -0
- package/dist/render/capability-gate.js +94 -0
- package/dist/render/capability-gate.js.map +1 -0
- package/dist/render/conditional-states-css.d.ts +41 -0
- package/dist/render/conditional-states-css.js +82 -0
- package/dist/render/conditional-states-css.js.map +1 -0
- package/dist/render/connections-context.d.ts +31 -0
- package/dist/render/connections-context.js +34 -0
- package/dist/render/connections-context.js.map +1 -0
- package/dist/render/dark-theme-css.d.ts +36 -0
- package/dist/render/dark-theme-css.js +67 -0
- package/dist/render/dark-theme-css.js.map +1 -0
- package/dist/render/data-binding-types.d.ts +191 -0
- package/dist/render/data-binding-types.js +11 -0
- package/dist/render/data-binding-types.js.map +1 -0
- package/dist/render/data-cache.d.ts +30 -0
- package/dist/render/data-cache.js +37 -0
- package/dist/render/data-cache.js.map +1 -0
- package/dist/render/derive-dark-theme.d.ts +17 -0
- package/dist/render/derive-dark-theme.js +18 -0
- package/dist/render/derive-dark-theme.js.map +1 -0
- package/dist/render/dynamic-artifact-context.d.ts +77 -0
- package/dist/render/dynamic-artifact-context.js +89 -0
- package/dist/render/dynamic-artifact-context.js.map +1 -0
- package/dist/render/dynamic-block-loader.d.ts +80 -0
- package/dist/render/dynamic-block-loader.js +197 -0
- package/dist/render/dynamic-block-loader.js.map +1 -0
- package/dist/render/endpoint.d.ts +18 -0
- package/dist/render/endpoint.js +109 -0
- package/dist/render/endpoint.js.map +1 -0
- package/dist/render/entity-facade-context.d.ts +34 -0
- package/dist/render/entity-facade-context.js +37 -0
- package/dist/render/entity-facade-context.js.map +1 -0
- package/dist/render/group-states-css.d.ts +41 -0
- package/dist/render/group-states-css.js +73 -0
- package/dist/render/group-states-css.js.map +1 -0
- package/dist/render/hooks/useSurfaceEvents.d.ts +8 -0
- package/dist/render/hooks/useSurfaceEvents.js +9 -0
- package/dist/render/hooks/useSurfaceEvents.js.map +1 -0
- package/dist/render/hooks/useSurfaceId.d.ts +8 -0
- package/dist/render/hooks/useSurfaceId.js +9 -0
- package/dist/render/hooks/useSurfaceId.js.map +1 -0
- package/dist/render/index.d.ts +48 -0
- package/dist/render/index.js +61 -0
- package/dist/render/index.js.map +1 -0
- package/dist/render/interaction-rule-engine.d.ts +12 -0
- package/dist/render/interaction-rule-engine.js +47 -0
- package/dist/render/interaction-rule-engine.js.map +1 -0
- package/dist/render/keyframes-css.d.ts +31 -0
- package/dist/render/keyframes-css.js +44 -0
- package/dist/render/keyframes-css.js.map +1 -0
- package/dist/render/query-state.d.ts +3 -0
- package/dist/render/query-state.js +70 -0
- package/dist/render/query-state.js.map +1 -0
- package/dist/render/renderer.d.ts +44 -0
- package/dist/render/renderer.js +937 -0
- package/dist/render/renderer.js.map +1 -0
- package/dist/render/resolve-expressions.d.ts +37 -0
- package/dist/render/resolve-expressions.js +309 -0
- package/dist/render/resolve-expressions.js.map +1 -0
- package/dist/render/responsive-style-css.d.ts +42 -0
- package/dist/render/responsive-style-css.js +123 -0
- package/dist/render/responsive-style-css.js.map +1 -0
- package/dist/render/responsive.d.ts +49 -0
- package/dist/render/responsive.js +99 -0
- package/dist/render/responsive.js.map +1 -0
- package/dist/render/scoped-css.d.ts +71 -0
- package/dist/render/scoped-css.js +102 -0
- package/dist/render/scoped-css.js.map +1 -0
- package/dist/render/shell-selection.d.ts +69 -0
- package/dist/render/shell-selection.js +117 -0
- package/dist/render/shell-selection.js.map +1 -0
- package/dist/render/states-css.d.ts +32 -0
- package/dist/render/states-css.js +63 -0
- package/dist/render/states-css.js.map +1 -0
- package/dist/render/surface-data-context.d.ts +18 -0
- package/dist/render/surface-data-context.js +21 -0
- package/dist/render/surface-data-context.js.map +1 -0
- package/dist/render/surface-event-bus.d.ts +19 -0
- package/dist/render/surface-event-bus.js +68 -0
- package/dist/render/surface-event-bus.js.map +1 -0
- package/dist/render/surface-event-context.d.ts +11 -0
- package/dist/render/surface-event-context.js +12 -0
- package/dist/render/surface-event-context.js.map +1 -0
- package/dist/render/surface-id-context.d.ts +8 -0
- package/dist/render/surface-id-context.js +9 -0
- package/dist/render/surface-id-context.js.map +1 -0
- package/dist/render/theme-utils.d.ts +14 -0
- package/dist/render/theme-utils.js +15 -0
- package/dist/render/theme-utils.js.map +1 -0
- package/dist/render/types.d.ts +282 -0
- package/dist/render/types.js +9 -0
- package/dist/render/types.js.map +1 -0
- package/dist/render/visibility-css.d.ts +42 -0
- package/dist/render/visibility-css.js +119 -0
- package/dist/render/visibility-css.js.map +1 -0
- package/dist/render/width-scope.d.ts +34 -0
- package/dist/render/width-scope.js +44 -0
- package/dist/render/width-scope.js.map +1 -0
- package/dist/render/write-only-sources.d.ts +33 -0
- package/dist/render/write-only-sources.js +99 -0
- package/dist/render/write-only-sources.js.map +1 -0
- package/dist/routing/index.d.ts +14 -0
- package/dist/routing/index.js +15 -0
- package/dist/routing/index.js.map +1 -0
- package/dist/routing/router.d.ts +42 -0
- package/dist/routing/router.js +218 -0
- package/dist/routing/router.js.map +1 -0
- package/dist/routing/types.d.ts +124 -0
- package/dist/routing/types.js +9 -0
- package/dist/routing/types.js.map +1 -0
- package/dist/seo/generator.d.ts +39 -0
- package/dist/seo/generator.js +236 -0
- package/dist/seo/generator.js.map +1 -0
- package/dist/seo/index.d.ts +14 -0
- package/dist/seo/index.js +15 -0
- package/dist/seo/index.js.map +1 -0
- package/dist/seo/types.d.ts +181 -0
- package/dist/seo/types.js +9 -0
- package/dist/seo/types.js.map +1 -0
- package/dist/state/index.d.ts +15 -0
- package/dist/state/index.js +16 -0
- package/dist/state/index.js.map +1 -0
- package/dist/state/manager.d.ts +33 -0
- package/dist/state/manager.js +81 -0
- package/dist/state/manager.js.map +1 -0
- package/dist/state/store.d.ts +40 -0
- package/dist/state/store.js +145 -0
- package/dist/state/store.js.map +1 -0
- package/dist/state/types.d.ts +107 -0
- package/dist/state/types.js +9 -0
- package/dist/state/types.js.map +1 -0
- package/dist/surface/expression.d.ts +65 -0
- package/dist/surface/expression.js +179 -0
- package/dist/surface/expression.js.map +1 -0
- package/dist/surface/index.d.ts +32 -0
- package/dist/surface/index.js +25 -0
- package/dist/surface/index.js.map +1 -0
- package/dist/surface/interpreter.d.ts +96 -0
- package/dist/surface/interpreter.js +231 -0
- package/dist/surface/interpreter.js.map +1 -0
- package/dist/surface/layout.d.ts +54 -0
- package/dist/surface/layout.js +288 -0
- package/dist/surface/layout.js.map +1 -0
- package/dist/surface/output.d.ts +90 -0
- package/dist/surface/output.js +104 -0
- package/dist/surface/output.js.map +1 -0
- package/dist/surface/primitives.d.ts +180 -0
- package/dist/surface/primitives.js +339 -0
- package/dist/surface/primitives.js.map +1 -0
- package/dist/surface/renderer.d.ts +96 -0
- package/dist/surface/renderer.js +190 -0
- package/dist/surface/renderer.js.map +1 -0
- package/dist/surface/schemas.d.ts +181 -0
- package/dist/surface/schemas.js +142 -0
- package/dist/surface/schemas.js.map +1 -0
- package/dist/surface/transforms.d.ts +144 -0
- package/dist/surface/transforms.js +120 -0
- package/dist/surface/transforms.js.map +1 -0
- package/dist/surface/types.d.ts +160 -0
- package/dist/surface/types.js +51 -0
- package/dist/surface/types.js.map +1 -0
- package/dist/surface/validation.d.ts +123 -0
- package/dist/surface/validation.js +147 -0
- package/dist/surface/validation.js.map +1 -0
- package/dist/validator.d.ts +55 -0
- package/dist/validator.js +100 -0
- package/dist/validator.js.map +1 -0
- package/package.json +106 -3
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Focus Management Utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides focus trapping, focus restoration, and skip-link helpers
|
|
5
|
+
* for accessible modal dialogs, drawers, and navigation patterns.
|
|
6
|
+
* All DOM operations are SSR-safe.
|
|
7
|
+
*
|
|
8
|
+
* @module @pwfabric/runtime/a11y/focus
|
|
9
|
+
*/
|
|
10
|
+
// ============================================================
|
|
11
|
+
// CONSTANTS
|
|
12
|
+
// ============================================================
|
|
13
|
+
/**
|
|
14
|
+
* Selector for elements that can receive focus.
|
|
15
|
+
* Covers interactive HTML elements and elements with explicit tabindex.
|
|
16
|
+
*/
|
|
17
|
+
const FOCUSABLE_SELECTOR = [
|
|
18
|
+
'a[href]',
|
|
19
|
+
'area[href]',
|
|
20
|
+
'button:not([disabled])',
|
|
21
|
+
'input:not([disabled])',
|
|
22
|
+
'select:not([disabled])',
|
|
23
|
+
'textarea:not([disabled])',
|
|
24
|
+
'[tabindex]:not([tabindex="-1"])',
|
|
25
|
+
'[contenteditable]',
|
|
26
|
+
'details > summary',
|
|
27
|
+
'audio[controls]',
|
|
28
|
+
'video[controls]',
|
|
29
|
+
].join(', ');
|
|
30
|
+
// ============================================================
|
|
31
|
+
// FOCUS TRAP MANAGER
|
|
32
|
+
// ============================================================
|
|
33
|
+
/**
|
|
34
|
+
* Manages focus trapping within a container element.
|
|
35
|
+
*
|
|
36
|
+
* When activated, Tab and Shift+Tab keypresses are intercepted to
|
|
37
|
+
* keep focus cycling within the container's focusable elements.
|
|
38
|
+
* Optionally deactivates on Escape key press.
|
|
39
|
+
*
|
|
40
|
+
* Used for modal dialogs, slide-over panels, and other overlay patterns
|
|
41
|
+
* that require focus to remain within the component for accessibility.
|
|
42
|
+
*/
|
|
43
|
+
export class FocusTrapManager {
|
|
44
|
+
active;
|
|
45
|
+
config;
|
|
46
|
+
previousFocus;
|
|
47
|
+
boundHandleKeyDown;
|
|
48
|
+
constructor() {
|
|
49
|
+
this.active = false;
|
|
50
|
+
this.config = null;
|
|
51
|
+
this.previousFocus = null;
|
|
52
|
+
this.boundHandleKeyDown = this.handleKeyDown.bind(this);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Activate the focus trap with the given configuration.
|
|
56
|
+
*
|
|
57
|
+
* Saves the currently focused element, attaches a keydown listener,
|
|
58
|
+
* and moves focus to the initial focus target (if specified) or the
|
|
59
|
+
* first focusable element in the container.
|
|
60
|
+
*
|
|
61
|
+
* @param config - Focus trap configuration
|
|
62
|
+
*/
|
|
63
|
+
activate(config) {
|
|
64
|
+
if (typeof document === 'undefined')
|
|
65
|
+
return;
|
|
66
|
+
if (!config.container)
|
|
67
|
+
return;
|
|
68
|
+
if (this.active) {
|
|
69
|
+
this.deactivate();
|
|
70
|
+
}
|
|
71
|
+
this.config = {
|
|
72
|
+
...config,
|
|
73
|
+
returnFocus: config.returnFocus ?? true,
|
|
74
|
+
escapeDeactivates: config.escapeDeactivates ?? true,
|
|
75
|
+
};
|
|
76
|
+
this.previousFocus = document.activeElement;
|
|
77
|
+
this.active = true;
|
|
78
|
+
document.addEventListener('keydown', this.boundHandleKeyDown);
|
|
79
|
+
// Determine and apply initial focus
|
|
80
|
+
const initialTarget = this.resolveInitialFocus();
|
|
81
|
+
if (initialTarget) {
|
|
82
|
+
initialTarget.focus();
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
// Fall back to the first focusable element
|
|
86
|
+
const focusable = this.getFocusableElements();
|
|
87
|
+
if (focusable.length > 0) {
|
|
88
|
+
focusable[0]?.focus();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Deactivate the focus trap and optionally return focus to the
|
|
94
|
+
* previously focused element.
|
|
95
|
+
*/
|
|
96
|
+
deactivate() {
|
|
97
|
+
if (typeof document === 'undefined')
|
|
98
|
+
return;
|
|
99
|
+
if (!this.active)
|
|
100
|
+
return;
|
|
101
|
+
this.active = false;
|
|
102
|
+
document.removeEventListener('keydown', this.boundHandleKeyDown);
|
|
103
|
+
if (this.config?.returnFocus && this.previousFocus instanceof HTMLElement) {
|
|
104
|
+
this.previousFocus.focus();
|
|
105
|
+
}
|
|
106
|
+
this.config = null;
|
|
107
|
+
this.previousFocus = null;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Check whether the focus trap is currently active.
|
|
111
|
+
*
|
|
112
|
+
* @returns `true` if the trap is active
|
|
113
|
+
*/
|
|
114
|
+
isActive() {
|
|
115
|
+
return this.active;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Dispose of the focus trap, deactivating it and cleaning up resources.
|
|
119
|
+
*/
|
|
120
|
+
dispose() {
|
|
121
|
+
this.deactivate();
|
|
122
|
+
}
|
|
123
|
+
// ============================================================
|
|
124
|
+
// PRIVATE METHODS
|
|
125
|
+
// ============================================================
|
|
126
|
+
/**
|
|
127
|
+
* Resolve the initial focus target from the config.
|
|
128
|
+
* Handles both HTMLElement references and CSS selector strings.
|
|
129
|
+
*/
|
|
130
|
+
resolveInitialFocus() {
|
|
131
|
+
if (!this.config?.initialFocus || !this.config.container)
|
|
132
|
+
return null;
|
|
133
|
+
if (typeof this.config.initialFocus === 'string') {
|
|
134
|
+
return this.config.container.querySelector(this.config.initialFocus);
|
|
135
|
+
}
|
|
136
|
+
return this.config.initialFocus;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Get all focusable elements within the trap container, sorted in DOM order.
|
|
140
|
+
*
|
|
141
|
+
* @returns Array of focusable HTMLElements
|
|
142
|
+
*/
|
|
143
|
+
getFocusableElements() {
|
|
144
|
+
if (!this.config?.container)
|
|
145
|
+
return [];
|
|
146
|
+
const elements = this.config.container.querySelectorAll(FOCUSABLE_SELECTOR);
|
|
147
|
+
return Array.from(elements).filter((el) => {
|
|
148
|
+
// Filter out elements that are not visible
|
|
149
|
+
return el.offsetParent !== null || el.offsetWidth > 0 || el.offsetHeight > 0;
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Handle keydown events to trap Tab focus and optionally deactivate on Escape.
|
|
154
|
+
*/
|
|
155
|
+
handleKeyDown(event) {
|
|
156
|
+
if (!this.active || !this.config)
|
|
157
|
+
return;
|
|
158
|
+
// Handle Escape
|
|
159
|
+
if (event.key === 'Escape' && this.config.escapeDeactivates) {
|
|
160
|
+
event.preventDefault();
|
|
161
|
+
this.deactivate();
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
// Handle Tab / Shift+Tab
|
|
165
|
+
if (event.key !== 'Tab')
|
|
166
|
+
return;
|
|
167
|
+
const focusable = this.getFocusableElements();
|
|
168
|
+
if (focusable.length === 0) {
|
|
169
|
+
event.preventDefault();
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
const firstElement = focusable[0];
|
|
173
|
+
const lastElement = focusable[focusable.length - 1];
|
|
174
|
+
if (event.shiftKey) {
|
|
175
|
+
// Shift+Tab: wrap from first to last
|
|
176
|
+
if (document.activeElement === firstElement ||
|
|
177
|
+
!this.config.container?.contains(document.activeElement)) {
|
|
178
|
+
event.preventDefault();
|
|
179
|
+
lastElement?.focus();
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
// Tab: wrap from last to first
|
|
184
|
+
if (document.activeElement === lastElement ||
|
|
185
|
+
!this.config.container?.contains(document.activeElement)) {
|
|
186
|
+
event.preventDefault();
|
|
187
|
+
firstElement?.focus();
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// ============================================================
|
|
193
|
+
// FOCUS RESTORER
|
|
194
|
+
// ============================================================
|
|
195
|
+
/**
|
|
196
|
+
* Manages a stack of focus positions for nested focus contexts.
|
|
197
|
+
*
|
|
198
|
+
* Useful when opening nested dialogs or panels where each level
|
|
199
|
+
* needs to save and restore the previous focus target.
|
|
200
|
+
*/
|
|
201
|
+
export class FocusRestorer {
|
|
202
|
+
stack;
|
|
203
|
+
constructor() {
|
|
204
|
+
this.stack = [];
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Save the currently focused element onto the stack.
|
|
208
|
+
*/
|
|
209
|
+
save() {
|
|
210
|
+
if (typeof document === 'undefined')
|
|
211
|
+
return;
|
|
212
|
+
const activeElement = document.activeElement;
|
|
213
|
+
if (activeElement) {
|
|
214
|
+
this.stack.push(activeElement);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Pop the most recently saved element from the stack and restore focus to it.
|
|
219
|
+
*/
|
|
220
|
+
restore() {
|
|
221
|
+
if (typeof document === 'undefined')
|
|
222
|
+
return;
|
|
223
|
+
const element = this.stack.pop();
|
|
224
|
+
if (element instanceof HTMLElement) {
|
|
225
|
+
element.focus();
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
// ============================================================
|
|
230
|
+
// SKIP LINK HELPER
|
|
231
|
+
// ============================================================
|
|
232
|
+
/**
|
|
233
|
+
* Create a skip-to-content link configuration.
|
|
234
|
+
*
|
|
235
|
+
* Returns the href, label, and click handler for a skip link that
|
|
236
|
+
* jumps focus to the element with the specified ID. The target element
|
|
237
|
+
* is given a `tabindex` of `-1` if it does not already have one so
|
|
238
|
+
* it can receive programmatic focus.
|
|
239
|
+
*
|
|
240
|
+
* @param targetId - The ID of the element to skip to
|
|
241
|
+
* @returns Skip link configuration object
|
|
242
|
+
*/
|
|
243
|
+
export function createSkipLink(targetId) {
|
|
244
|
+
return {
|
|
245
|
+
href: `#${targetId}`,
|
|
246
|
+
label: 'Skip to content',
|
|
247
|
+
handleClick: (e) => {
|
|
248
|
+
if (typeof document === 'undefined')
|
|
249
|
+
return;
|
|
250
|
+
e.preventDefault();
|
|
251
|
+
const target = document.getElementById(targetId);
|
|
252
|
+
if (target) {
|
|
253
|
+
// Ensure the element can receive focus
|
|
254
|
+
if (!target.hasAttribute('tabindex')) {
|
|
255
|
+
target.setAttribute('tabindex', '-1');
|
|
256
|
+
}
|
|
257
|
+
target.focus();
|
|
258
|
+
target.scrollIntoView({ behavior: 'smooth' });
|
|
259
|
+
}
|
|
260
|
+
},
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
// ============================================================
|
|
264
|
+
// FACTORY FUNCTIONS
|
|
265
|
+
// ============================================================
|
|
266
|
+
/**
|
|
267
|
+
* Create a new FocusTrapManager instance.
|
|
268
|
+
*
|
|
269
|
+
* @returns A new FocusTrapManager
|
|
270
|
+
*/
|
|
271
|
+
export function createFocusTrap() {
|
|
272
|
+
return new FocusTrapManager();
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Create a new FocusRestorer instance.
|
|
276
|
+
*
|
|
277
|
+
* @returns A new FocusRestorer
|
|
278
|
+
*/
|
|
279
|
+
export function createFocusRestorer() {
|
|
280
|
+
return new FocusRestorer();
|
|
281
|
+
}
|
|
282
|
+
//# sourceMappingURL=focus.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"focus.js","sourceRoot":"","sources":["../../src/a11y/focus.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Focus Management Utilities\n *\n * Provides focus trapping, focus restoration, and skip-link helpers\n * for accessible modal dialogs, drawers, and navigation patterns.\n * All DOM operations are SSR-safe.\n *\n * @module @pwfabric/runtime/a11y/focus\n */\n\n// ============================================================\n// TYPES\n// ============================================================\n\n/** Configuration for a focus trap */\nexport interface FocusTrapConfig {\n /** The container element to trap focus within */\n container: HTMLElement | null\n /** Initial element to focus, or a CSS selector to find it */\n initialFocus?: HTMLElement | string\n /** Whether to return focus to the previously focused element on deactivate (default true) */\n returnFocus?: boolean\n /** Whether pressing Escape deactivates the trap (default true) */\n escapeDeactivates?: boolean\n}\n\n// ============================================================\n// CONSTANTS\n// ============================================================\n\n/**\n * Selector for elements that can receive focus.\n * Covers interactive HTML elements and elements with explicit tabindex.\n */\nconst FOCUSABLE_SELECTOR = [\n 'a[href]',\n 'area[href]',\n 'button:not([disabled])',\n 'input:not([disabled])',\n 'select:not([disabled])',\n 'textarea:not([disabled])',\n '[tabindex]:not([tabindex=\"-1\"])',\n '[contenteditable]',\n 'details > summary',\n 'audio[controls]',\n 'video[controls]',\n].join(', ')\n\n// ============================================================\n// FOCUS TRAP MANAGER\n// ============================================================\n\n/**\n * Manages focus trapping within a container element.\n *\n * When activated, Tab and Shift+Tab keypresses are intercepted to\n * keep focus cycling within the container's focusable elements.\n * Optionally deactivates on Escape key press.\n *\n * Used for modal dialogs, slide-over panels, and other overlay patterns\n * that require focus to remain within the component for accessibility.\n */\nexport class FocusTrapManager {\n private active: boolean\n private config: FocusTrapConfig | null\n private previousFocus: Element | null\n private readonly boundHandleKeyDown: (event: KeyboardEvent) => void\n\n constructor() {\n this.active = false\n this.config = null\n this.previousFocus = null\n this.boundHandleKeyDown = this.handleKeyDown.bind(this)\n }\n\n /**\n * Activate the focus trap with the given configuration.\n *\n * Saves the currently focused element, attaches a keydown listener,\n * and moves focus to the initial focus target (if specified) or the\n * first focusable element in the container.\n *\n * @param config - Focus trap configuration\n */\n activate(config: FocusTrapConfig): void {\n if (typeof document === 'undefined') return\n if (!config.container) return\n if (this.active) {\n this.deactivate()\n }\n\n this.config = {\n ...config,\n returnFocus: config.returnFocus ?? true,\n escapeDeactivates: config.escapeDeactivates ?? true,\n }\n this.previousFocus = document.activeElement\n this.active = true\n\n document.addEventListener('keydown', this.boundHandleKeyDown)\n\n // Determine and apply initial focus\n const initialTarget = this.resolveInitialFocus()\n if (initialTarget) {\n initialTarget.focus()\n } else {\n // Fall back to the first focusable element\n const focusable = this.getFocusableElements()\n if (focusable.length > 0) {\n focusable[0]?.focus()\n }\n }\n }\n\n /**\n * Deactivate the focus trap and optionally return focus to the\n * previously focused element.\n */\n deactivate(): void {\n if (typeof document === 'undefined') return\n if (!this.active) return\n\n this.active = false\n document.removeEventListener('keydown', this.boundHandleKeyDown)\n\n if (this.config?.returnFocus && this.previousFocus instanceof HTMLElement) {\n this.previousFocus.focus()\n }\n\n this.config = null\n this.previousFocus = null\n }\n\n /**\n * Check whether the focus trap is currently active.\n *\n * @returns `true` if the trap is active\n */\n isActive(): boolean {\n return this.active\n }\n\n /**\n * Dispose of the focus trap, deactivating it and cleaning up resources.\n */\n dispose(): void {\n this.deactivate()\n }\n\n // ============================================================\n // PRIVATE METHODS\n // ============================================================\n\n /**\n * Resolve the initial focus target from the config.\n * Handles both HTMLElement references and CSS selector strings.\n */\n private resolveInitialFocus(): HTMLElement | null {\n if (!this.config?.initialFocus || !this.config.container) return null\n\n if (typeof this.config.initialFocus === 'string') {\n return this.config.container.querySelector<HTMLElement>(this.config.initialFocus)\n }\n\n return this.config.initialFocus\n }\n\n /**\n * Get all focusable elements within the trap container, sorted in DOM order.\n *\n * @returns Array of focusable HTMLElements\n */\n private getFocusableElements(): HTMLElement[] {\n if (!this.config?.container) return []\n\n const elements = this.config.container.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR)\n return Array.from(elements).filter((el) => {\n // Filter out elements that are not visible\n return el.offsetParent !== null || el.offsetWidth > 0 || el.offsetHeight > 0\n })\n }\n\n /**\n * Handle keydown events to trap Tab focus and optionally deactivate on Escape.\n */\n private handleKeyDown(event: KeyboardEvent): void {\n if (!this.active || !this.config) return\n\n // Handle Escape\n if (event.key === 'Escape' && this.config.escapeDeactivates) {\n event.preventDefault()\n this.deactivate()\n return\n }\n\n // Handle Tab / Shift+Tab\n if (event.key !== 'Tab') return\n\n const focusable = this.getFocusableElements()\n if (focusable.length === 0) {\n event.preventDefault()\n return\n }\n\n const firstElement = focusable[0]\n const lastElement = focusable[focusable.length - 1]\n\n if (event.shiftKey) {\n // Shift+Tab: wrap from first to last\n if (\n document.activeElement === firstElement ||\n !this.config.container?.contains(document.activeElement)\n ) {\n event.preventDefault()\n lastElement?.focus()\n }\n } else {\n // Tab: wrap from last to first\n if (\n document.activeElement === lastElement ||\n !this.config.container?.contains(document.activeElement)\n ) {\n event.preventDefault()\n firstElement?.focus()\n }\n }\n }\n}\n\n// ============================================================\n// FOCUS RESTORER\n// ============================================================\n\n/**\n * Manages a stack of focus positions for nested focus contexts.\n *\n * Useful when opening nested dialogs or panels where each level\n * needs to save and restore the previous focus target.\n */\nexport class FocusRestorer {\n private stack: Element[]\n\n constructor() {\n this.stack = []\n }\n\n /**\n * Save the currently focused element onto the stack.\n */\n save(): void {\n if (typeof document === 'undefined') return\n\n const activeElement = document.activeElement\n if (activeElement) {\n this.stack.push(activeElement)\n }\n }\n\n /**\n * Pop the most recently saved element from the stack and restore focus to it.\n */\n restore(): void {\n if (typeof document === 'undefined') return\n\n const element = this.stack.pop()\n if (element instanceof HTMLElement) {\n element.focus()\n }\n }\n}\n\n// ============================================================\n// SKIP LINK HELPER\n// ============================================================\n\n/**\n * Create a skip-to-content link configuration.\n *\n * Returns the href, label, and click handler for a skip link that\n * jumps focus to the element with the specified ID. The target element\n * is given a `tabindex` of `-1` if it does not already have one so\n * it can receive programmatic focus.\n *\n * @param targetId - The ID of the element to skip to\n * @returns Skip link configuration object\n */\nexport function createSkipLink(targetId: string): {\n href: string\n label: string\n handleClick: (e: Event) => void\n} {\n return {\n href: `#${targetId}`,\n label: 'Skip to content',\n handleClick: (e: Event) => {\n if (typeof document === 'undefined') return\n\n e.preventDefault()\n const target = document.getElementById(targetId)\n if (target) {\n // Ensure the element can receive focus\n if (!target.hasAttribute('tabindex')) {\n target.setAttribute('tabindex', '-1')\n }\n target.focus()\n target.scrollIntoView({ behavior: 'smooth' })\n }\n },\n }\n}\n\n// ============================================================\n// FACTORY FUNCTIONS\n// ============================================================\n\n/**\n * Create a new FocusTrapManager instance.\n *\n * @returns A new FocusTrapManager\n */\nexport function createFocusTrap(): FocusTrapManager {\n return new FocusTrapManager()\n}\n\n/**\n * Create a new FocusRestorer instance.\n *\n * @returns A new FocusRestorer\n */\nexport function createFocusRestorer(): FocusRestorer {\n return new FocusRestorer()\n}\n"]}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Accessibility (a11y) Module
|
|
3
|
+
*
|
|
4
|
+
* Provides runtime accessibility utilities for the PWFabric platform:
|
|
5
|
+
* - Screen reader announcements via ARIA live regions
|
|
6
|
+
* - Focus trapping, restoration, and skip links
|
|
7
|
+
* - Keyboard navigation for composite widgets
|
|
8
|
+
* - WCAG 2.0 color contrast checking
|
|
9
|
+
*
|
|
10
|
+
* All DOM operations are SSR-safe (no-op when `document` is unavailable).
|
|
11
|
+
*
|
|
12
|
+
* @module @pwfabric/runtime/a11y
|
|
13
|
+
*/
|
|
14
|
+
export type { AnnouncementLevel, LiveRegionConfig } from './announcer.js';
|
|
15
|
+
export { LiveRegionAnnouncer, createAnnouncer } from './announcer.js';
|
|
16
|
+
export type { FocusTrapConfig } from './focus.js';
|
|
17
|
+
export { FocusTrapManager, FocusRestorer, createSkipLink, createFocusTrap, createFocusRestorer, } from './focus.js';
|
|
18
|
+
export type { ArrowDirection, KeyboardNavConfig } from './keyboard.js';
|
|
19
|
+
export { KeyboardNavigationManager, RovingTabIndex, createKeyboardNav, createRovingTabIndex, } from './keyboard.js';
|
|
20
|
+
export type { ContrastResult } from './contrast.js';
|
|
21
|
+
export { hexToRgb, relativeLuminance, contrastRatio, checkContrast, meetsWCAG } from './contrast.js';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Accessibility (a11y) Module
|
|
3
|
+
*
|
|
4
|
+
* Provides runtime accessibility utilities for the PWFabric platform:
|
|
5
|
+
* - Screen reader announcements via ARIA live regions
|
|
6
|
+
* - Focus trapping, restoration, and skip links
|
|
7
|
+
* - Keyboard navigation for composite widgets
|
|
8
|
+
* - WCAG 2.0 color contrast checking
|
|
9
|
+
*
|
|
10
|
+
* All DOM operations are SSR-safe (no-op when `document` is unavailable).
|
|
11
|
+
*
|
|
12
|
+
* @module @pwfabric/runtime/a11y
|
|
13
|
+
*/
|
|
14
|
+
export { LiveRegionAnnouncer, createAnnouncer } from './announcer.js';
|
|
15
|
+
export { FocusTrapManager, FocusRestorer, createSkipLink, createFocusTrap, createFocusRestorer, } from './focus.js';
|
|
16
|
+
export { KeyboardNavigationManager, RovingTabIndex, createKeyboardNav, createRovingTabIndex, } from './keyboard.js';
|
|
17
|
+
export { hexToRgb, relativeLuminance, contrastRatio, checkContrast, meetsWCAG } from './contrast.js';
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/a11y/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAIrE,OAAO,EACL,gBAAgB,EAChB,aAAa,EACb,cAAc,EACd,eAAe,EACf,mBAAmB,GACpB,MAAM,YAAY,CAAA;AAInB,OAAO,EACL,yBAAyB,EACzB,cAAc,EACd,iBAAiB,EACjB,oBAAoB,GACrB,MAAM,eAAe,CAAA;AAItB,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,aAAa,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA","sourcesContent":["/**\n * Accessibility (a11y) Module\n *\n * Provides runtime accessibility utilities for the PWFabric platform:\n * - Screen reader announcements via ARIA live regions\n * - Focus trapping, restoration, and skip links\n * - Keyboard navigation for composite widgets\n * - WCAG 2.0 color contrast checking\n *\n * All DOM operations are SSR-safe (no-op when `document` is unavailable).\n *\n * @module @pwfabric/runtime/a11y\n */\n\n// Announcer - Screen reader live region management\nexport type { AnnouncementLevel, LiveRegionConfig } from './announcer.js'\nexport { LiveRegionAnnouncer, createAnnouncer } from './announcer.js'\n\n// Focus - Focus management utilities\nexport type { FocusTrapConfig } from './focus.js'\nexport {\n FocusTrapManager,\n FocusRestorer,\n createSkipLink,\n createFocusTrap,\n createFocusRestorer,\n} from './focus.js'\n\n// Keyboard - Keyboard navigation manager\nexport type { ArrowDirection, KeyboardNavConfig } from './keyboard.js'\nexport {\n KeyboardNavigationManager,\n RovingTabIndex,\n createKeyboardNav,\n createRovingTabIndex,\n} from './keyboard.js'\n\n// Contrast - WCAG color contrast checking\nexport type { ContrastResult } from './contrast.js'\nexport { hexToRgb, relativeLuminance, contrastRatio, checkContrast, meetsWCAG } from './contrast.js'\n"]}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keyboard Navigation Manager
|
|
3
|
+
*
|
|
4
|
+
* Provides arrow-key navigation for composite widgets (tabs, menus,
|
|
5
|
+
* listboxes) and roving tabindex management. All DOM operations are
|
|
6
|
+
* SSR-safe.
|
|
7
|
+
*
|
|
8
|
+
* @module @pwfabric/runtime/a11y/keyboard
|
|
9
|
+
*/
|
|
10
|
+
/** Direction(s) in which arrow key navigation operates */
|
|
11
|
+
export type ArrowDirection = 'horizontal' | 'vertical' | 'both';
|
|
12
|
+
/** Configuration for keyboard navigation */
|
|
13
|
+
export interface KeyboardNavConfig {
|
|
14
|
+
/** Container element holding navigable items */
|
|
15
|
+
container: HTMLElement | null;
|
|
16
|
+
/** Direction of arrow key navigation */
|
|
17
|
+
direction: ArrowDirection;
|
|
18
|
+
/** Whether navigation wraps around at boundaries (default true) */
|
|
19
|
+
loop?: boolean;
|
|
20
|
+
/** CSS selector for navigable items (default '[role="option"], [role="tab"], button, a') */
|
|
21
|
+
selector?: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Manages keyboard navigation within a composite widget.
|
|
25
|
+
*
|
|
26
|
+
* Listens for arrow key events on a container and moves focus
|
|
27
|
+
* between child items matching a configurable selector. Supports
|
|
28
|
+
* horizontal, vertical, or both directional navigation with
|
|
29
|
+
* optional looping at boundaries.
|
|
30
|
+
*/
|
|
31
|
+
export declare class KeyboardNavigationManager {
|
|
32
|
+
private config;
|
|
33
|
+
private currentIndex;
|
|
34
|
+
private readonly boundHandleKeyDown;
|
|
35
|
+
constructor();
|
|
36
|
+
/**
|
|
37
|
+
* Activate keyboard navigation on the given container.
|
|
38
|
+
*
|
|
39
|
+
* @param config - Keyboard navigation configuration
|
|
40
|
+
*/
|
|
41
|
+
activate(config: KeyboardNavConfig): void;
|
|
42
|
+
/**
|
|
43
|
+
* Deactivate keyboard navigation and remove event listeners.
|
|
44
|
+
*/
|
|
45
|
+
deactivate(): void;
|
|
46
|
+
/**
|
|
47
|
+
* Move focus to the next item in the navigation order.
|
|
48
|
+
*/
|
|
49
|
+
focusNext(): void;
|
|
50
|
+
/**
|
|
51
|
+
* Move focus to the previous item in the navigation order.
|
|
52
|
+
*/
|
|
53
|
+
focusPrevious(): void;
|
|
54
|
+
/**
|
|
55
|
+
* Move focus to the first item.
|
|
56
|
+
*/
|
|
57
|
+
focusFirst(): void;
|
|
58
|
+
/**
|
|
59
|
+
* Move focus to the last item.
|
|
60
|
+
*/
|
|
61
|
+
focusLast(): void;
|
|
62
|
+
/**
|
|
63
|
+
* Get the current focused item index.
|
|
64
|
+
*
|
|
65
|
+
* @returns The zero-based index of the currently focused item
|
|
66
|
+
*/
|
|
67
|
+
getCurrentIndex(): number;
|
|
68
|
+
/**
|
|
69
|
+
* Dispose of the keyboard navigation manager, removing all listeners.
|
|
70
|
+
*/
|
|
71
|
+
dispose(): void;
|
|
72
|
+
/**
|
|
73
|
+
* Handle keydown events for arrow key navigation and Home/End support.
|
|
74
|
+
*/
|
|
75
|
+
private handleKeyDown;
|
|
76
|
+
/**
|
|
77
|
+
* Query navigable items from the container, filtered to visible elements.
|
|
78
|
+
*
|
|
79
|
+
* @returns Array of focusable HTMLElements within the container
|
|
80
|
+
*/
|
|
81
|
+
private getItems;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Manages roving tabindex for composite widgets.
|
|
85
|
+
*
|
|
86
|
+
* In the roving tabindex pattern, only one item within a group
|
|
87
|
+
* has `tabindex="0"` (making it tabbable), while all other items
|
|
88
|
+
* have `tabindex="-1"`. This allows the group to participate in
|
|
89
|
+
* the page tab order as a single stop, with arrow keys used to
|
|
90
|
+
* navigate between items within the group.
|
|
91
|
+
*
|
|
92
|
+
* @see https://www.w3.org/WAI/ARIA/apg/practices/keyboard-interface/#kbd_roving_tabindex
|
|
93
|
+
*/
|
|
94
|
+
export declare class RovingTabIndex {
|
|
95
|
+
private container;
|
|
96
|
+
private selector;
|
|
97
|
+
/**
|
|
98
|
+
* @param selector - CSS selector for items managed by roving tabindex
|
|
99
|
+
*/
|
|
100
|
+
constructor(selector?: string);
|
|
101
|
+
/**
|
|
102
|
+
* Attach roving tabindex management to a container.
|
|
103
|
+
*
|
|
104
|
+
* Sets `tabindex="-1"` on all matching items except the first,
|
|
105
|
+
* which gets `tabindex="0"`.
|
|
106
|
+
*
|
|
107
|
+
* @param container - The container element
|
|
108
|
+
*/
|
|
109
|
+
attach(container: HTMLElement): void;
|
|
110
|
+
/**
|
|
111
|
+
* Detach roving tabindex management, restoring all items to `tabindex="0"`.
|
|
112
|
+
*/
|
|
113
|
+
detach(): void;
|
|
114
|
+
/**
|
|
115
|
+
* Set a specific element as the active (tabbable) item.
|
|
116
|
+
*
|
|
117
|
+
* The target receives `tabindex="0"` and all sibling items
|
|
118
|
+
* receive `tabindex="-1"`.
|
|
119
|
+
*
|
|
120
|
+
* @param element - The element to make active
|
|
121
|
+
*/
|
|
122
|
+
setActive(element: HTMLElement): void;
|
|
123
|
+
/**
|
|
124
|
+
* Dispose of the roving tabindex manager.
|
|
125
|
+
*/
|
|
126
|
+
dispose(): void;
|
|
127
|
+
/**
|
|
128
|
+
* Query managed items from the container.
|
|
129
|
+
*/
|
|
130
|
+
private getItems;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Create a new KeyboardNavigationManager instance.
|
|
134
|
+
*
|
|
135
|
+
* @returns A new KeyboardNavigationManager
|
|
136
|
+
*/
|
|
137
|
+
export declare function createKeyboardNav(): KeyboardNavigationManager;
|
|
138
|
+
/**
|
|
139
|
+
* Create a new RovingTabIndex instance.
|
|
140
|
+
*
|
|
141
|
+
* @param selector - CSS selector for managed items
|
|
142
|
+
* @returns A new RovingTabIndex
|
|
143
|
+
*/
|
|
144
|
+
export declare function createRovingTabIndex(selector?: string): RovingTabIndex;
|