@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,317 @@
|
|
|
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
|
+
// ============================================================
|
|
11
|
+
// CONSTANTS
|
|
12
|
+
// ============================================================
|
|
13
|
+
const DEFAULT_SELECTOR = '[role="option"], [role="tab"], button, a';
|
|
14
|
+
// ============================================================
|
|
15
|
+
// KEYBOARD NAVIGATION MANAGER
|
|
16
|
+
// ============================================================
|
|
17
|
+
/**
|
|
18
|
+
* Manages keyboard navigation within a composite widget.
|
|
19
|
+
*
|
|
20
|
+
* Listens for arrow key events on a container and moves focus
|
|
21
|
+
* between child items matching a configurable selector. Supports
|
|
22
|
+
* horizontal, vertical, or both directional navigation with
|
|
23
|
+
* optional looping at boundaries.
|
|
24
|
+
*/
|
|
25
|
+
export class KeyboardNavigationManager {
|
|
26
|
+
config;
|
|
27
|
+
currentIndex;
|
|
28
|
+
boundHandleKeyDown;
|
|
29
|
+
constructor() {
|
|
30
|
+
this.config = null;
|
|
31
|
+
this.currentIndex = 0;
|
|
32
|
+
this.boundHandleKeyDown = this.handleKeyDown.bind(this);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Activate keyboard navigation on the given container.
|
|
36
|
+
*
|
|
37
|
+
* @param config - Keyboard navigation configuration
|
|
38
|
+
*/
|
|
39
|
+
activate(config) {
|
|
40
|
+
if (typeof document === 'undefined')
|
|
41
|
+
return;
|
|
42
|
+
if (!config.container)
|
|
43
|
+
return;
|
|
44
|
+
if (this.config) {
|
|
45
|
+
this.deactivate();
|
|
46
|
+
}
|
|
47
|
+
this.config = {
|
|
48
|
+
...config,
|
|
49
|
+
loop: config.loop ?? true,
|
|
50
|
+
selector: config.selector ?? DEFAULT_SELECTOR,
|
|
51
|
+
};
|
|
52
|
+
this.currentIndex = 0;
|
|
53
|
+
this.config.container?.addEventListener('keydown', this.boundHandleKeyDown);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Deactivate keyboard navigation and remove event listeners.
|
|
57
|
+
*/
|
|
58
|
+
deactivate() {
|
|
59
|
+
if (!this.config?.container)
|
|
60
|
+
return;
|
|
61
|
+
this.config.container.removeEventListener('keydown', this.boundHandleKeyDown);
|
|
62
|
+
this.config = null;
|
|
63
|
+
this.currentIndex = 0;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Move focus to the next item in the navigation order.
|
|
67
|
+
*/
|
|
68
|
+
focusNext() {
|
|
69
|
+
const items = this.getItems();
|
|
70
|
+
if (items.length === 0)
|
|
71
|
+
return;
|
|
72
|
+
const nextIndex = this.currentIndex + 1;
|
|
73
|
+
if (nextIndex >= items.length) {
|
|
74
|
+
if (this.config?.loop) {
|
|
75
|
+
this.currentIndex = 0;
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
this.currentIndex = nextIndex;
|
|
83
|
+
}
|
|
84
|
+
items[this.currentIndex]?.focus();
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Move focus to the previous item in the navigation order.
|
|
88
|
+
*/
|
|
89
|
+
focusPrevious() {
|
|
90
|
+
const items = this.getItems();
|
|
91
|
+
if (items.length === 0)
|
|
92
|
+
return;
|
|
93
|
+
const prevIndex = this.currentIndex - 1;
|
|
94
|
+
if (prevIndex < 0) {
|
|
95
|
+
if (this.config?.loop) {
|
|
96
|
+
this.currentIndex = items.length - 1;
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
this.currentIndex = prevIndex;
|
|
104
|
+
}
|
|
105
|
+
items[this.currentIndex]?.focus();
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Move focus to the first item.
|
|
109
|
+
*/
|
|
110
|
+
focusFirst() {
|
|
111
|
+
const items = this.getItems();
|
|
112
|
+
if (items.length === 0)
|
|
113
|
+
return;
|
|
114
|
+
this.currentIndex = 0;
|
|
115
|
+
items[0]?.focus();
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Move focus to the last item.
|
|
119
|
+
*/
|
|
120
|
+
focusLast() {
|
|
121
|
+
const items = this.getItems();
|
|
122
|
+
if (items.length === 0)
|
|
123
|
+
return;
|
|
124
|
+
this.currentIndex = items.length - 1;
|
|
125
|
+
items[this.currentIndex]?.focus();
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Get the current focused item index.
|
|
129
|
+
*
|
|
130
|
+
* @returns The zero-based index of the currently focused item
|
|
131
|
+
*/
|
|
132
|
+
getCurrentIndex() {
|
|
133
|
+
return this.currentIndex;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Dispose of the keyboard navigation manager, removing all listeners.
|
|
137
|
+
*/
|
|
138
|
+
dispose() {
|
|
139
|
+
this.deactivate();
|
|
140
|
+
}
|
|
141
|
+
// ============================================================
|
|
142
|
+
// PRIVATE METHODS
|
|
143
|
+
// ============================================================
|
|
144
|
+
/**
|
|
145
|
+
* Handle keydown events for arrow key navigation and Home/End support.
|
|
146
|
+
*/
|
|
147
|
+
handleKeyDown(event) {
|
|
148
|
+
if (!this.config)
|
|
149
|
+
return;
|
|
150
|
+
const { direction } = this.config;
|
|
151
|
+
let handled = false;
|
|
152
|
+
switch (event.key) {
|
|
153
|
+
case 'ArrowRight':
|
|
154
|
+
if (direction === 'horizontal' || direction === 'both') {
|
|
155
|
+
this.focusNext();
|
|
156
|
+
handled = true;
|
|
157
|
+
}
|
|
158
|
+
break;
|
|
159
|
+
case 'ArrowLeft':
|
|
160
|
+
if (direction === 'horizontal' || direction === 'both') {
|
|
161
|
+
this.focusPrevious();
|
|
162
|
+
handled = true;
|
|
163
|
+
}
|
|
164
|
+
break;
|
|
165
|
+
case 'ArrowDown':
|
|
166
|
+
if (direction === 'vertical' || direction === 'both') {
|
|
167
|
+
this.focusNext();
|
|
168
|
+
handled = true;
|
|
169
|
+
}
|
|
170
|
+
break;
|
|
171
|
+
case 'ArrowUp':
|
|
172
|
+
if (direction === 'vertical' || direction === 'both') {
|
|
173
|
+
this.focusPrevious();
|
|
174
|
+
handled = true;
|
|
175
|
+
}
|
|
176
|
+
break;
|
|
177
|
+
case 'Home':
|
|
178
|
+
this.focusFirst();
|
|
179
|
+
handled = true;
|
|
180
|
+
break;
|
|
181
|
+
case 'End':
|
|
182
|
+
this.focusLast();
|
|
183
|
+
handled = true;
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
if (handled) {
|
|
187
|
+
event.preventDefault();
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Query navigable items from the container, filtered to visible elements.
|
|
192
|
+
*
|
|
193
|
+
* @returns Array of focusable HTMLElements within the container
|
|
194
|
+
*/
|
|
195
|
+
getItems() {
|
|
196
|
+
if (!this.config?.container)
|
|
197
|
+
return [];
|
|
198
|
+
const selector = this.config.selector ?? DEFAULT_SELECTOR;
|
|
199
|
+
const elements = this.config.container.querySelectorAll(selector);
|
|
200
|
+
return Array.from(elements).filter((el) => {
|
|
201
|
+
// Exclude disabled and hidden elements
|
|
202
|
+
if (el.hasAttribute('disabled'))
|
|
203
|
+
return false;
|
|
204
|
+
if (el.getAttribute('aria-hidden') === 'true')
|
|
205
|
+
return false;
|
|
206
|
+
return el.offsetParent !== null || el.offsetWidth > 0 || el.offsetHeight > 0;
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
// ============================================================
|
|
211
|
+
// ROVING TABINDEX
|
|
212
|
+
// ============================================================
|
|
213
|
+
/**
|
|
214
|
+
* Manages roving tabindex for composite widgets.
|
|
215
|
+
*
|
|
216
|
+
* In the roving tabindex pattern, only one item within a group
|
|
217
|
+
* has `tabindex="0"` (making it tabbable), while all other items
|
|
218
|
+
* have `tabindex="-1"`. This allows the group to participate in
|
|
219
|
+
* the page tab order as a single stop, with arrow keys used to
|
|
220
|
+
* navigate between items within the group.
|
|
221
|
+
*
|
|
222
|
+
* @see https://www.w3.org/WAI/ARIA/apg/practices/keyboard-interface/#kbd_roving_tabindex
|
|
223
|
+
*/
|
|
224
|
+
export class RovingTabIndex {
|
|
225
|
+
container;
|
|
226
|
+
selector;
|
|
227
|
+
/**
|
|
228
|
+
* @param selector - CSS selector for items managed by roving tabindex
|
|
229
|
+
*/
|
|
230
|
+
constructor(selector) {
|
|
231
|
+
this.container = null;
|
|
232
|
+
this.selector = selector ?? DEFAULT_SELECTOR;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Attach roving tabindex management to a container.
|
|
236
|
+
*
|
|
237
|
+
* Sets `tabindex="-1"` on all matching items except the first,
|
|
238
|
+
* which gets `tabindex="0"`.
|
|
239
|
+
*
|
|
240
|
+
* @param container - The container element
|
|
241
|
+
*/
|
|
242
|
+
attach(container) {
|
|
243
|
+
if (typeof document === 'undefined')
|
|
244
|
+
return;
|
|
245
|
+
this.container = container;
|
|
246
|
+
const items = this.getItems();
|
|
247
|
+
items.forEach((item, index) => {
|
|
248
|
+
item.setAttribute('tabindex', index === 0 ? '0' : '-1');
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Detach roving tabindex management, restoring all items to `tabindex="0"`.
|
|
253
|
+
*/
|
|
254
|
+
detach() {
|
|
255
|
+
if (!this.container)
|
|
256
|
+
return;
|
|
257
|
+
const items = this.getItems();
|
|
258
|
+
items.forEach((item) => {
|
|
259
|
+
item.setAttribute('tabindex', '0');
|
|
260
|
+
});
|
|
261
|
+
this.container = null;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Set a specific element as the active (tabbable) item.
|
|
265
|
+
*
|
|
266
|
+
* The target receives `tabindex="0"` and all sibling items
|
|
267
|
+
* receive `tabindex="-1"`.
|
|
268
|
+
*
|
|
269
|
+
* @param element - The element to make active
|
|
270
|
+
*/
|
|
271
|
+
setActive(element) {
|
|
272
|
+
if (!this.container)
|
|
273
|
+
return;
|
|
274
|
+
const items = this.getItems();
|
|
275
|
+
for (const item of items) {
|
|
276
|
+
item.setAttribute('tabindex', item === element ? '0' : '-1');
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Dispose of the roving tabindex manager.
|
|
281
|
+
*/
|
|
282
|
+
dispose() {
|
|
283
|
+
this.detach();
|
|
284
|
+
}
|
|
285
|
+
// ============================================================
|
|
286
|
+
// PRIVATE METHODS
|
|
287
|
+
// ============================================================
|
|
288
|
+
/**
|
|
289
|
+
* Query managed items from the container.
|
|
290
|
+
*/
|
|
291
|
+
getItems() {
|
|
292
|
+
if (!this.container)
|
|
293
|
+
return [];
|
|
294
|
+
return Array.from(this.container.querySelectorAll(this.selector));
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
// ============================================================
|
|
298
|
+
// FACTORY FUNCTIONS
|
|
299
|
+
// ============================================================
|
|
300
|
+
/**
|
|
301
|
+
* Create a new KeyboardNavigationManager instance.
|
|
302
|
+
*
|
|
303
|
+
* @returns A new KeyboardNavigationManager
|
|
304
|
+
*/
|
|
305
|
+
export function createKeyboardNav() {
|
|
306
|
+
return new KeyboardNavigationManager();
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Create a new RovingTabIndex instance.
|
|
310
|
+
*
|
|
311
|
+
* @param selector - CSS selector for managed items
|
|
312
|
+
* @returns A new RovingTabIndex
|
|
313
|
+
*/
|
|
314
|
+
export function createRovingTabIndex(selector) {
|
|
315
|
+
return new RovingTabIndex(selector);
|
|
316
|
+
}
|
|
317
|
+
//# sourceMappingURL=keyboard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keyboard.js","sourceRoot":"","sources":["../../src/a11y/keyboard.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Keyboard Navigation Manager\n *\n * Provides arrow-key navigation for composite widgets (tabs, menus,\n * listboxes) and roving tabindex management. All DOM operations are\n * SSR-safe.\n *\n * @module @pwfabric/runtime/a11y/keyboard\n */\n\n// ============================================================\n// TYPES\n// ============================================================\n\n/** Direction(s) in which arrow key navigation operates */\nexport type ArrowDirection = 'horizontal' | 'vertical' | 'both'\n\n/** Configuration for keyboard navigation */\nexport interface KeyboardNavConfig {\n /** Container element holding navigable items */\n container: HTMLElement | null\n /** Direction of arrow key navigation */\n direction: ArrowDirection\n /** Whether navigation wraps around at boundaries (default true) */\n loop?: boolean\n /** CSS selector for navigable items (default '[role=\"option\"], [role=\"tab\"], button, a') */\n selector?: string\n}\n\n// ============================================================\n// CONSTANTS\n// ============================================================\n\nconst DEFAULT_SELECTOR = '[role=\"option\"], [role=\"tab\"], button, a'\n\n// ============================================================\n// KEYBOARD NAVIGATION MANAGER\n// ============================================================\n\n/**\n * Manages keyboard navigation within a composite widget.\n *\n * Listens for arrow key events on a container and moves focus\n * between child items matching a configurable selector. Supports\n * horizontal, vertical, or both directional navigation with\n * optional looping at boundaries.\n */\nexport class KeyboardNavigationManager {\n private config: KeyboardNavConfig | null\n private currentIndex: number\n private readonly boundHandleKeyDown: (event: KeyboardEvent) => void\n\n constructor() {\n this.config = null\n this.currentIndex = 0\n this.boundHandleKeyDown = this.handleKeyDown.bind(this)\n }\n\n /**\n * Activate keyboard navigation on the given container.\n *\n * @param config - Keyboard navigation configuration\n */\n activate(config: KeyboardNavConfig): void {\n if (typeof document === 'undefined') return\n if (!config.container) return\n if (this.config) {\n this.deactivate()\n }\n\n this.config = {\n ...config,\n loop: config.loop ?? true,\n selector: config.selector ?? DEFAULT_SELECTOR,\n }\n this.currentIndex = 0\n\n this.config.container?.addEventListener('keydown', this.boundHandleKeyDown)\n }\n\n /**\n * Deactivate keyboard navigation and remove event listeners.\n */\n deactivate(): void {\n if (!this.config?.container) return\n\n this.config.container.removeEventListener('keydown', this.boundHandleKeyDown)\n this.config = null\n this.currentIndex = 0\n }\n\n /**\n * Move focus to the next item in the navigation order.\n */\n focusNext(): void {\n const items = this.getItems()\n if (items.length === 0) return\n\n const nextIndex = this.currentIndex + 1\n\n if (nextIndex >= items.length) {\n if (this.config?.loop) {\n this.currentIndex = 0\n } else {\n return\n }\n } else {\n this.currentIndex = nextIndex\n }\n\n items[this.currentIndex]?.focus()\n }\n\n /**\n * Move focus to the previous item in the navigation order.\n */\n focusPrevious(): void {\n const items = this.getItems()\n if (items.length === 0) return\n\n const prevIndex = this.currentIndex - 1\n\n if (prevIndex < 0) {\n if (this.config?.loop) {\n this.currentIndex = items.length - 1\n } else {\n return\n }\n } else {\n this.currentIndex = prevIndex\n }\n\n items[this.currentIndex]?.focus()\n }\n\n /**\n * Move focus to the first item.\n */\n focusFirst(): void {\n const items = this.getItems()\n if (items.length === 0) return\n\n this.currentIndex = 0\n items[0]?.focus()\n }\n\n /**\n * Move focus to the last item.\n */\n focusLast(): void {\n const items = this.getItems()\n if (items.length === 0) return\n\n this.currentIndex = items.length - 1\n items[this.currentIndex]?.focus()\n }\n\n /**\n * Get the current focused item index.\n *\n * @returns The zero-based index of the currently focused item\n */\n getCurrentIndex(): number {\n return this.currentIndex\n }\n\n /**\n * Dispose of the keyboard navigation manager, removing all listeners.\n */\n dispose(): void {\n this.deactivate()\n }\n\n // ============================================================\n // PRIVATE METHODS\n // ============================================================\n\n /**\n * Handle keydown events for arrow key navigation and Home/End support.\n */\n private handleKeyDown(event: KeyboardEvent): void {\n if (!this.config) return\n\n const { direction } = this.config\n let handled = false\n\n switch (event.key) {\n case 'ArrowRight':\n if (direction === 'horizontal' || direction === 'both') {\n this.focusNext()\n handled = true\n }\n break\n\n case 'ArrowLeft':\n if (direction === 'horizontal' || direction === 'both') {\n this.focusPrevious()\n handled = true\n }\n break\n\n case 'ArrowDown':\n if (direction === 'vertical' || direction === 'both') {\n this.focusNext()\n handled = true\n }\n break\n\n case 'ArrowUp':\n if (direction === 'vertical' || direction === 'both') {\n this.focusPrevious()\n handled = true\n }\n break\n\n case 'Home':\n this.focusFirst()\n handled = true\n break\n\n case 'End':\n this.focusLast()\n handled = true\n break\n }\n\n if (handled) {\n event.preventDefault()\n }\n }\n\n /**\n * Query navigable items from the container, filtered to visible elements.\n *\n * @returns Array of focusable HTMLElements within the container\n */\n private getItems(): HTMLElement[] {\n if (!this.config?.container) return []\n\n const selector = this.config.selector ?? DEFAULT_SELECTOR\n const elements = this.config.container.querySelectorAll<HTMLElement>(selector)\n\n return Array.from(elements).filter((el) => {\n // Exclude disabled and hidden elements\n if (el.hasAttribute('disabled')) return false\n if (el.getAttribute('aria-hidden') === 'true') return false\n return el.offsetParent !== null || el.offsetWidth > 0 || el.offsetHeight > 0\n })\n }\n}\n\n// ============================================================\n// ROVING TABINDEX\n// ============================================================\n\n/**\n * Manages roving tabindex for composite widgets.\n *\n * In the roving tabindex pattern, only one item within a group\n * has `tabindex=\"0\"` (making it tabbable), while all other items\n * have `tabindex=\"-1\"`. This allows the group to participate in\n * the page tab order as a single stop, with arrow keys used to\n * navigate between items within the group.\n *\n * @see https://www.w3.org/WAI/ARIA/apg/practices/keyboard-interface/#kbd_roving_tabindex\n */\nexport class RovingTabIndex {\n private container: HTMLElement | null\n private selector: string\n\n /**\n * @param selector - CSS selector for items managed by roving tabindex\n */\n constructor(selector?: string) {\n this.container = null\n this.selector = selector ?? DEFAULT_SELECTOR\n }\n\n /**\n * Attach roving tabindex management to a container.\n *\n * Sets `tabindex=\"-1\"` on all matching items except the first,\n * which gets `tabindex=\"0\"`.\n *\n * @param container - The container element\n */\n attach(container: HTMLElement): void {\n if (typeof document === 'undefined') return\n\n this.container = container\n const items = this.getItems()\n\n items.forEach((item, index) => {\n item.setAttribute('tabindex', index === 0 ? '0' : '-1')\n })\n }\n\n /**\n * Detach roving tabindex management, restoring all items to `tabindex=\"0\"`.\n */\n detach(): void {\n if (!this.container) return\n\n const items = this.getItems()\n items.forEach((item) => {\n item.setAttribute('tabindex', '0')\n })\n\n this.container = null\n }\n\n /**\n * Set a specific element as the active (tabbable) item.\n *\n * The target receives `tabindex=\"0\"` and all sibling items\n * receive `tabindex=\"-1\"`.\n *\n * @param element - The element to make active\n */\n setActive(element: HTMLElement): void {\n if (!this.container) return\n\n const items = this.getItems()\n for (const item of items) {\n item.setAttribute('tabindex', item === element ? '0' : '-1')\n }\n }\n\n /**\n * Dispose of the roving tabindex manager.\n */\n dispose(): void {\n this.detach()\n }\n\n // ============================================================\n // PRIVATE METHODS\n // ============================================================\n\n /**\n * Query managed items from the container.\n */\n private getItems(): HTMLElement[] {\n if (!this.container) return []\n return Array.from(this.container.querySelectorAll<HTMLElement>(this.selector))\n }\n}\n\n// ============================================================\n// FACTORY FUNCTIONS\n// ============================================================\n\n/**\n * Create a new KeyboardNavigationManager instance.\n *\n * @returns A new KeyboardNavigationManager\n */\nexport function createKeyboardNav(): KeyboardNavigationManager {\n return new KeyboardNavigationManager()\n}\n\n/**\n * Create a new RovingTabIndex instance.\n *\n * @param selector - CSS selector for managed items\n * @returns A new RovingTabIndex\n */\nexport function createRovingTabIndex(selector?: string): RovingTabIndex {\n return new RovingTabIndex(selector)\n}\n"]}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default Image Adapter
|
|
3
|
+
*
|
|
4
|
+
* Renders images using native HTML img element.
|
|
5
|
+
* This is the fallback adapter used when no framework-specific
|
|
6
|
+
* adapter is provided.
|
|
7
|
+
*
|
|
8
|
+
* @module @pwfabric/runtime/adapters/default-image
|
|
9
|
+
*/
|
|
10
|
+
import type { ImageAdapter } from './types.js';
|
|
11
|
+
/**
|
|
12
|
+
* Default image adapter using native img element
|
|
13
|
+
*
|
|
14
|
+
* Provides basic image rendering with CSS-based styling.
|
|
15
|
+
* Supports lazy loading via the native loading attribute.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```tsx
|
|
19
|
+
* <SurfaceRenderer
|
|
20
|
+
* surface={mySurface}
|
|
21
|
+
* imageAdapter={DefaultImageAdapter}
|
|
22
|
+
* />
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export declare const DefaultImageAdapter: ImageAdapter;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Convert CSS unit value to string
|
|
4
|
+
*/
|
|
5
|
+
function cssUnit(value) {
|
|
6
|
+
if (value === undefined)
|
|
7
|
+
return undefined;
|
|
8
|
+
if (typeof value === 'number')
|
|
9
|
+
return `${value}px`;
|
|
10
|
+
return value;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Default image adapter using native img element
|
|
14
|
+
*
|
|
15
|
+
* Provides basic image rendering with CSS-based styling.
|
|
16
|
+
* Supports lazy loading via the native loading attribute.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```tsx
|
|
20
|
+
* <SurfaceRenderer
|
|
21
|
+
* surface={mySurface}
|
|
22
|
+
* imageAdapter={DefaultImageAdapter}
|
|
23
|
+
* />
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export const DefaultImageAdapter = ({ src, alt, width, height, loading, objectFit, aspectRatio, borderRadius, className, style, blockId, }) => {
|
|
27
|
+
const computedStyle = {
|
|
28
|
+
width: cssUnit(width) ?? '100%',
|
|
29
|
+
height: cssUnit(height) ?? 'auto',
|
|
30
|
+
objectFit: objectFit ?? 'cover',
|
|
31
|
+
aspectRatio,
|
|
32
|
+
borderRadius: cssUnit(borderRadius),
|
|
33
|
+
...style,
|
|
34
|
+
};
|
|
35
|
+
return (_jsx("img", { id: blockId, src: src || '/placeholder.png', alt: alt ?? '', style: computedStyle, className: className, loading: loading ?? 'lazy', "data-block-type": "image" }));
|
|
36
|
+
};
|
|
37
|
+
//# sourceMappingURL=default-image.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"default-image.js","sourceRoot":"","sources":["../../src/adapters/default-image.tsx"],"names":[],"mappings":";AAaA;;GAEG;AACH,SAAS,OAAO,CAAC,KAAkC;IACjD,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAA;IACzC,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,GAAG,KAAK,IAAI,CAAA;IAClD,OAAO,KAAK,CAAA;AACd,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAiB,CAAC,EAChD,GAAG,EACH,GAAG,EACH,KAAK,EACL,MAAM,EACN,OAAO,EACP,SAAS,EACT,WAAW,EACX,YAAY,EACZ,SAAS,EACT,KAAK,EACL,OAAO,GACW,EAAsB,EAAE;IAC1C,MAAM,aAAa,GAAwB;QACzC,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,IAAI,MAAM;QAC/B,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,MAAM;QACjC,SAAS,EAAE,SAAS,IAAI,OAAO;QAC/B,WAAW;QACX,YAAY,EAAE,OAAO,CAAC,YAAY,CAAC;QACnC,GAAG,KAAK;KACT,CAAA;IAED,OAAO,CACL,cACE,EAAE,EAAE,OAAO,EACX,GAAG,EAAE,GAAG,IAAI,kBAAkB,EAC9B,GAAG,EAAE,GAAG,IAAI,EAAE,EACd,KAAK,EAAE,aAAa,EACpB,SAAS,EAAE,SAAS,EACpB,OAAO,EAAE,OAAO,IAAI,MAAM,qBACV,OAAO,GACvB,CACH,CAAA;AACH,CAAC,CAAA","sourcesContent":["/**\n * Default Image Adapter\n *\n * Renders images using native HTML img element.\n * This is the fallback adapter used when no framework-specific\n * adapter is provided.\n *\n * @module @pwfabric/runtime/adapters/default-image\n */\n\nimport * as React from 'react'\nimport type { ImageAdapter, ImageAdapterProps } from './types.js'\n\n/**\n * Convert CSS unit value to string\n */\nfunction cssUnit(value: number | string | undefined): string | undefined {\n if (value === undefined) return undefined\n if (typeof value === 'number') return `${value}px`\n return value\n}\n\n/**\n * Default image adapter using native img element\n *\n * Provides basic image rendering with CSS-based styling.\n * Supports lazy loading via the native loading attribute.\n *\n * @example\n * ```tsx\n * <SurfaceRenderer\n * surface={mySurface}\n * imageAdapter={DefaultImageAdapter}\n * />\n * ```\n */\nexport const DefaultImageAdapter: ImageAdapter = ({\n src,\n alt,\n width,\n height,\n loading,\n objectFit,\n aspectRatio,\n borderRadius,\n className,\n style,\n blockId,\n}: ImageAdapterProps): React.ReactElement => {\n const computedStyle: React.CSSProperties = {\n width: cssUnit(width) ?? '100%',\n height: cssUnit(height) ?? 'auto',\n objectFit: objectFit ?? 'cover',\n aspectRatio,\n borderRadius: cssUnit(borderRadius),\n ...style,\n }\n\n return (\n <img\n id={blockId}\n src={src || '/placeholder.png'}\n alt={alt ?? ''}\n style={computedStyle}\n className={className}\n loading={loading ?? 'lazy'}\n data-block-type=\"image\"\n />\n )\n}\n"]}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image Adapters
|
|
3
|
+
*
|
|
4
|
+
* Framework-specific image optimization adapters for SurfaceRenderer.
|
|
5
|
+
* Enables Next.js Image, Astro Image, etc. while keeping surface
|
|
6
|
+
* definitions framework-agnostic.
|
|
7
|
+
*
|
|
8
|
+
* @module @pwfabric/runtime/adapters
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```tsx
|
|
12
|
+
* import { SurfaceRenderer } from '@pwfabric/runtime'
|
|
13
|
+
* import { NextImageAdapter } from '@pwfabric/runtime/adapters'
|
|
14
|
+
*
|
|
15
|
+
* function MyPage() {
|
|
16
|
+
* return (
|
|
17
|
+
* <SurfaceRenderer
|
|
18
|
+
* surface={mySurface}
|
|
19
|
+
* imageAdapter={NextImageAdapter}
|
|
20
|
+
* />
|
|
21
|
+
* )
|
|
22
|
+
* }
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export type { ImageAdapter, ImageAdapterProps } from './types.js';
|
|
26
|
+
export { DefaultImageAdapter } from './default-image.js';
|
|
27
|
+
export { NextImageAdapter } from './next-image.js';
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image Adapters
|
|
3
|
+
*
|
|
4
|
+
* Framework-specific image optimization adapters for SurfaceRenderer.
|
|
5
|
+
* Enables Next.js Image, Astro Image, etc. while keeping surface
|
|
6
|
+
* definitions framework-agnostic.
|
|
7
|
+
*
|
|
8
|
+
* @module @pwfabric/runtime/adapters
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```tsx
|
|
12
|
+
* import { SurfaceRenderer } from '@pwfabric/runtime'
|
|
13
|
+
* import { NextImageAdapter } from '@pwfabric/runtime/adapters'
|
|
14
|
+
*
|
|
15
|
+
* function MyPage() {
|
|
16
|
+
* return (
|
|
17
|
+
* <SurfaceRenderer
|
|
18
|
+
* surface={mySurface}
|
|
19
|
+
* imageAdapter={NextImageAdapter}
|
|
20
|
+
* />
|
|
21
|
+
* )
|
|
22
|
+
* }
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
// Adapters
|
|
26
|
+
export { DefaultImageAdapter } from './default-image.js';
|
|
27
|
+
export { NextImageAdapter } from './next-image.js';
|
|
28
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/adapters/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAKH,WAAW;AACX,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAA;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA","sourcesContent":["/**\n * Image Adapters\n *\n * Framework-specific image optimization adapters for SurfaceRenderer.\n * Enables Next.js Image, Astro Image, etc. while keeping surface\n * definitions framework-agnostic.\n *\n * @module @pwfabric/runtime/adapters\n *\n * @example\n * ```tsx\n * import { SurfaceRenderer } from '@pwfabric/runtime'\n * import { NextImageAdapter } from '@pwfabric/runtime/adapters'\n *\n * function MyPage() {\n * return (\n * <SurfaceRenderer\n * surface={mySurface}\n * imageAdapter={NextImageAdapter}\n * />\n * )\n * }\n * ```\n */\n\n// Types\nexport type { ImageAdapter, ImageAdapterProps } from './types.js'\n\n// Adapters\nexport { DefaultImageAdapter } from './default-image.js'\nexport { NextImageAdapter } from './next-image.js'\n"]}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Next.js Image Adapter
|
|
3
|
+
*
|
|
4
|
+
* Renders images using Next.js Image component for automatic
|
|
5
|
+
* optimization, responsive images, and modern image formats.
|
|
6
|
+
*
|
|
7
|
+
* NOTE: This adapter requires `next` as a peer dependency.
|
|
8
|
+
* Only import this adapter in Next.js applications.
|
|
9
|
+
*
|
|
10
|
+
* @module @pwfabric/runtime/adapters/next-image
|
|
11
|
+
*/
|
|
12
|
+
import type { ImageAdapter } from './types.js';
|
|
13
|
+
/**
|
|
14
|
+
* Next.js Image adapter for optimized image delivery
|
|
15
|
+
*
|
|
16
|
+
* Leverages Next.js Image component features:
|
|
17
|
+
* - Automatic image optimization
|
|
18
|
+
* - Responsive image generation
|
|
19
|
+
* - Modern format support (WebP, AVIF)
|
|
20
|
+
* - Lazy loading with native browser support
|
|
21
|
+
* - Priority loading for LCP images
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```tsx
|
|
25
|
+
* import { SurfaceRenderer } from '@pwfabric/runtime'
|
|
26
|
+
* import { NextImageAdapter } from '@pwfabric/runtime/adapters'
|
|
27
|
+
*
|
|
28
|
+
* function MyPage() {
|
|
29
|
+
* return (
|
|
30
|
+
* <SurfaceRenderer
|
|
31
|
+
* surface={mySurface}
|
|
32
|
+
* imageAdapter={NextImageAdapter}
|
|
33
|
+
* />
|
|
34
|
+
* )
|
|
35
|
+
* }
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export declare const NextImageAdapter: ImageAdapter;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Next.js Image Adapter
|
|
3
|
+
*
|
|
4
|
+
* Renders images using Next.js Image component for automatic
|
|
5
|
+
* optimization, responsive images, and modern image formats.
|
|
6
|
+
*
|
|
7
|
+
* NOTE: This adapter requires `next` as a peer dependency.
|
|
8
|
+
* Only import this adapter in Next.js applications.
|
|
9
|
+
*
|
|
10
|
+
* @module @pwfabric/runtime/adapters/next-image
|
|
11
|
+
*/
|
|
12
|
+
'use client';
|
|
13
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
14
|
+
import * as React from 'react';
|
|
15
|
+
// Lazy load next/image component to avoid SSG issues
|
|
16
|
+
let NextImageComponent = null;
|
|
17
|
+
function getNextImage() {
|
|
18
|
+
if (NextImageComponent === null) {
|
|
19
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
20
|
+
NextImageComponent = require('next/image')
|
|
21
|
+
.default;
|
|
22
|
+
}
|
|
23
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
24
|
+
return NextImageComponent;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Convert CSS unit value to number if possible
|
|
28
|
+
*/
|
|
29
|
+
function toNumericSize(value) {
|
|
30
|
+
if (value === undefined)
|
|
31
|
+
return undefined;
|
|
32
|
+
if (typeof value === 'number')
|
|
33
|
+
return value;
|
|
34
|
+
// Try to parse numeric strings (e.g., "100" or "100px")
|
|
35
|
+
const parsed = parseInt(value, 10);
|
|
36
|
+
return isNaN(parsed) ? undefined : parsed;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Convert CSS unit value to string
|
|
40
|
+
*/
|
|
41
|
+
function cssUnit(value) {
|
|
42
|
+
if (value === undefined)
|
|
43
|
+
return undefined;
|
|
44
|
+
if (typeof value === 'number')
|
|
45
|
+
return `${value}px`;
|
|
46
|
+
return value;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Next.js Image adapter for optimized image delivery
|
|
50
|
+
*
|
|
51
|
+
* Leverages Next.js Image component features:
|
|
52
|
+
* - Automatic image optimization
|
|
53
|
+
* - Responsive image generation
|
|
54
|
+
* - Modern format support (WebP, AVIF)
|
|
55
|
+
* - Lazy loading with native browser support
|
|
56
|
+
* - Priority loading for LCP images
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```tsx
|
|
60
|
+
* import { SurfaceRenderer } from '@pwfabric/runtime'
|
|
61
|
+
* import { NextImageAdapter } from '@pwfabric/runtime/adapters'
|
|
62
|
+
*
|
|
63
|
+
* function MyPage() {
|
|
64
|
+
* return (
|
|
65
|
+
* <SurfaceRenderer
|
|
66
|
+
* surface={mySurface}
|
|
67
|
+
* imageAdapter={NextImageAdapter}
|
|
68
|
+
* />
|
|
69
|
+
* )
|
|
70
|
+
* }
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
export const NextImageAdapter = ({ src, alt, width, height, loading, objectFit, aspectRatio, borderRadius, className, style, placeholder, blurDataURL, blockId, }) => {
|
|
74
|
+
const numericWidth = toNumericSize(width);
|
|
75
|
+
const numericHeight = toNumericSize(height);
|
|
76
|
+
// Use fill mode if no explicit dimensions provided
|
|
77
|
+
const useFillMode = numericWidth === undefined && numericHeight === undefined;
|
|
78
|
+
const computedStyle = {
|
|
79
|
+
objectFit: objectFit ?? 'cover',
|
|
80
|
+
aspectRatio,
|
|
81
|
+
borderRadius: cssUnit(borderRadius),
|
|
82
|
+
...(numericWidth !== undefined && numericHeight !== undefined
|
|
83
|
+
? { width: `${numericWidth}px`, height: `${numericHeight}px`, maxWidth: '100%' }
|
|
84
|
+
: {}),
|
|
85
|
+
...style,
|
|
86
|
+
};
|
|
87
|
+
// Wrapper style for fill mode
|
|
88
|
+
const wrapperStyle = useFillMode
|
|
89
|
+
? {
|
|
90
|
+
position: 'relative',
|
|
91
|
+
width: cssUnit(width) ?? '100%',
|
|
92
|
+
height: cssUnit(height) ?? 'auto',
|
|
93
|
+
aspectRatio: aspectRatio ?? (numericHeight && numericWidth ? undefined : '16/9'),
|
|
94
|
+
}
|
|
95
|
+
: undefined;
|
|
96
|
+
const nextImageProps = {
|
|
97
|
+
src: src || '/placeholder.png',
|
|
98
|
+
alt: alt ?? '',
|
|
99
|
+
fill: useFillMode,
|
|
100
|
+
priority: loading === 'eager',
|
|
101
|
+
style: computedStyle,
|
|
102
|
+
};
|
|
103
|
+
// Add optional props only if they have values
|
|
104
|
+
if (blockId !== undefined)
|
|
105
|
+
nextImageProps.id = blockId;
|
|
106
|
+
if (!useFillMode && numericWidth !== undefined)
|
|
107
|
+
nextImageProps.width = numericWidth;
|
|
108
|
+
if (!useFillMode && numericHeight !== undefined)
|
|
109
|
+
nextImageProps.height = numericHeight;
|
|
110
|
+
if (placeholder !== undefined)
|
|
111
|
+
nextImageProps.placeholder = placeholder;
|
|
112
|
+
if (blurDataURL !== undefined)
|
|
113
|
+
nextImageProps.blurDataURL = blurDataURL;
|
|
114
|
+
if (className !== undefined)
|
|
115
|
+
nextImageProps.className = className;
|
|
116
|
+
const NextImage = getNextImage();
|
|
117
|
+
const imageElement = React.createElement(NextImage, nextImageProps);
|
|
118
|
+
// Wrap in container for fill mode
|
|
119
|
+
if (useFillMode) {
|
|
120
|
+
return (_jsx("div", { style: wrapperStyle, "data-block-type": "image", children: imageElement }));
|
|
121
|
+
}
|
|
122
|
+
return imageElement;
|
|
123
|
+
};
|
|
124
|
+
//# sourceMappingURL=next-image.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"next-image.js","sourceRoot":"","sources":["../../src/adapters/next-image.tsx"],"names":[],"mappings":"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","sourcesContent":["/**\n * Next.js Image Adapter\n *\n * Renders images using Next.js Image component for automatic\n * optimization, responsive images, and modern image formats.\n *\n * NOTE: This adapter requires `next` as a peer dependency.\n * Only import this adapter in Next.js applications.\n *\n * @module @pwfabric/runtime/adapters/next-image\n */\n\n'use client'\n\nimport * as React from 'react'\nimport type { ImageAdapter, ImageAdapterProps } from './types.js'\n\n// Interface for Next.js Image component props\ninterface NextImageComponentProps {\n src: string\n alt: string\n width?: number\n height?: number\n fill?: boolean\n priority?: boolean\n placeholder?: 'blur' | 'empty'\n blurDataURL?: string\n style?: React.CSSProperties\n className?: string\n id?: string\n}\n\n// Lazy load next/image component to avoid SSG issues\nlet NextImageComponent: React.ComponentType<NextImageComponentProps> | null = null\n\nfunction getNextImage(): React.ComponentType<NextImageComponentProps> {\n if (NextImageComponent === null) {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n NextImageComponent = require('next/image')\n .default as React.ComponentType<NextImageComponentProps>\n }\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n return NextImageComponent!\n}\n\n/**\n * Convert CSS unit value to number if possible\n */\nfunction toNumericSize(value: number | string | undefined): number | undefined {\n if (value === undefined) return undefined\n if (typeof value === 'number') return value\n // Try to parse numeric strings (e.g., \"100\" or \"100px\")\n const parsed = parseInt(value, 10)\n return isNaN(parsed) ? undefined : parsed\n}\n\n/**\n * Convert CSS unit value to string\n */\nfunction cssUnit(value: number | string | undefined): string | undefined {\n if (value === undefined) return undefined\n if (typeof value === 'number') return `${value}px`\n return value\n}\n\n/**\n * Next.js Image adapter for optimized image delivery\n *\n * Leverages Next.js Image component features:\n * - Automatic image optimization\n * - Responsive image generation\n * - Modern format support (WebP, AVIF)\n * - Lazy loading with native browser support\n * - Priority loading for LCP images\n *\n * @example\n * ```tsx\n * import { SurfaceRenderer } from '@pwfabric/runtime'\n * import { NextImageAdapter } from '@pwfabric/runtime/adapters'\n *\n * function MyPage() {\n * return (\n * <SurfaceRenderer\n * surface={mySurface}\n * imageAdapter={NextImageAdapter}\n * />\n * )\n * }\n * ```\n */\nexport const NextImageAdapter: ImageAdapter = ({\n src,\n alt,\n width,\n height,\n loading,\n objectFit,\n aspectRatio,\n borderRadius,\n className,\n style,\n placeholder,\n blurDataURL,\n blockId,\n}: ImageAdapterProps): React.ReactElement => {\n const numericWidth = toNumericSize(width)\n const numericHeight = toNumericSize(height)\n\n // Use fill mode if no explicit dimensions provided\n const useFillMode = numericWidth === undefined && numericHeight === undefined\n\n const computedStyle: React.CSSProperties = {\n objectFit: objectFit ?? 'cover',\n aspectRatio,\n borderRadius: cssUnit(borderRadius),\n ...(numericWidth !== undefined && numericHeight !== undefined\n ? { width: `${numericWidth}px`, height: `${numericHeight}px`, maxWidth: '100%' }\n : {}),\n ...style,\n }\n\n // Wrapper style for fill mode\n const wrapperStyle: React.CSSProperties | undefined = useFillMode\n ? {\n position: 'relative',\n width: cssUnit(width) ?? '100%',\n height: cssUnit(height) ?? 'auto',\n aspectRatio: aspectRatio ?? (numericHeight && numericWidth ? undefined : '16/9'),\n }\n : undefined\n\n // Build NextImage props\n interface NextImagePropsType {\n src: string\n alt: string\n width?: number\n height?: number\n fill?: boolean\n priority?: boolean\n placeholder?: 'blur' | 'empty'\n blurDataURL?: string\n style?: React.CSSProperties\n className?: string\n id?: string\n }\n\n const nextImageProps: NextImagePropsType = {\n src: src || '/placeholder.png',\n alt: alt ?? '',\n fill: useFillMode,\n priority: loading === 'eager',\n style: computedStyle,\n }\n\n // Add optional props only if they have values\n if (blockId !== undefined) nextImageProps.id = blockId\n if (!useFillMode && numericWidth !== undefined) nextImageProps.width = numericWidth\n if (!useFillMode && numericHeight !== undefined) nextImageProps.height = numericHeight\n if (placeholder !== undefined) nextImageProps.placeholder = placeholder\n if (blurDataURL !== undefined) nextImageProps.blurDataURL = blurDataURL\n if (className !== undefined) nextImageProps.className = className\n\n const NextImage = getNextImage()\n const imageElement = React.createElement(NextImage, nextImageProps)\n\n // Wrap in container for fill mode\n if (useFillMode) {\n return (\n <div style={wrapperStyle} data-block-type=\"image\">\n {imageElement}\n </div>\n )\n }\n\n return imageElement\n}\n"]}
|