@pwfabric/runtime 0.0.0-stage → 1.0.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +90 -0
- package/README.md +13 -2
- package/dist/__tests__/dom-test-setup.d.ts +21 -0
- package/dist/__tests__/dom-test-setup.d.ts.map +1 -0
- package/dist/__tests__/dom-test-setup.js +36 -0
- package/dist/__tests__/dom-test-setup.js.map +1 -0
- package/dist/a11y/announcer.d.ts +66 -0
- package/dist/a11y/announcer.d.ts.map +1 -0
- package/dist/a11y/announcer.js +140 -0
- package/dist/a11y/announcer.js.map +1 -0
- package/dist/a11y/contrast.d.ts +55 -0
- package/dist/a11y/contrast.d.ts.map +1 -0
- package/dist/a11y/contrast.js +93 -0
- package/dist/a11y/contrast.js.map +1 -0
- package/dist/a11y/focus.d.ts +124 -0
- package/dist/a11y/focus.d.ts.map +1 -0
- package/dist/a11y/focus.js +282 -0
- package/dist/a11y/focus.js.map +1 -0
- package/dist/a11y/index.d.ts +22 -0
- package/dist/a11y/index.d.ts.map +1 -0
- package/dist/a11y/index.js +18 -0
- package/dist/a11y/index.js.map +1 -0
- package/dist/a11y/keyboard.d.ts +145 -0
- package/dist/a11y/keyboard.d.ts.map +1 -0
- package/dist/a11y/keyboard.js +317 -0
- package/dist/a11y/keyboard.js.map +1 -0
- package/dist/adapters/default-image.d.ts +26 -0
- package/dist/adapters/default-image.d.ts.map +1 -0
- package/dist/adapters/default-image.js +37 -0
- package/dist/adapters/default-image.js.map +1 -0
- package/dist/adapters/index.d.ts +28 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +28 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/next-image.d.ts +39 -0
- package/dist/adapters/next-image.d.ts.map +1 -0
- package/dist/adapters/next-image.js +124 -0
- package/dist/adapters/next-image.js.map +1 -0
- package/dist/adapters/types.d.ts +62 -0
- package/dist/adapters/types.d.ts.map +1 -0
- package/dist/adapters/types.js +11 -0
- package/dist/adapters/types.js.map +1 -0
- package/dist/analytics/index.d.ts +15 -0
- package/dist/analytics/index.d.ts.map +1 -0
- package/dist/analytics/index.js +15 -0
- package/dist/analytics/index.js.map +1 -0
- package/dist/analytics/tracker.d.ts +66 -0
- package/dist/analytics/tracker.d.ts.map +1 -0
- package/dist/analytics/tracker.js +251 -0
- package/dist/analytics/tracker.js.map +1 -0
- package/dist/analytics/types.d.ts +131 -0
- package/dist/analytics/types.d.ts.map +1 -0
- package/dist/analytics/types.js +11 -0
- package/dist/analytics/types.js.map +1 -0
- package/dist/auth/index.d.ts +16 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +14 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/provider.d.ts +133 -0
- package/dist/auth/provider.d.ts.map +1 -0
- package/dist/auth/provider.js +473 -0
- package/dist/auth/provider.js.map +1 -0
- package/dist/auth/types.d.ts +118 -0
- package/dist/auth/types.d.ts.map +1 -0
- package/dist/auth/types.js +9 -0
- package/dist/auth/types.js.map +1 -0
- package/dist/binding/collection.d.ts +191 -0
- package/dist/binding/collection.d.ts.map +1 -0
- package/dist/binding/collection.js +358 -0
- package/dist/binding/collection.js.map +1 -0
- package/dist/binding/datasource.d.ts +320 -0
- package/dist/binding/datasource.d.ts.map +1 -0
- package/dist/binding/datasource.js +556 -0
- package/dist/binding/datasource.js.map +1 -0
- package/dist/binding/engine.d.ts +66 -0
- package/dist/binding/engine.d.ts.map +1 -0
- package/dist/binding/engine.js +268 -0
- package/dist/binding/engine.js.map +1 -0
- package/dist/binding/expression.d.ts +151 -0
- package/dist/binding/expression.d.ts.map +1 -0
- package/dist/binding/expression.js +787 -0
- package/dist/binding/expression.js.map +1 -0
- package/dist/binding/index.d.ts +20 -0
- package/dist/binding/index.d.ts.map +1 -0
- package/dist/binding/index.js +14 -0
- package/dist/binding/index.js.map +1 -0
- package/dist/binding/two-way.d.ts +150 -0
- package/dist/binding/two-way.d.ts.map +1 -0
- package/dist/binding/two-way.js +462 -0
- package/dist/binding/two-way.js.map +1 -0
- package/dist/binding/types.d.ts +120 -0
- package/dist/binding/types.d.ts.map +1 -0
- package/dist/binding/types.js +9 -0
- package/dist/binding/types.js.map +1 -0
- package/dist/blocks/__test-fixtures__/block-fixtures.d.ts +40 -0
- package/dist/blocks/__test-fixtures__/block-fixtures.d.ts.map +1 -0
- package/dist/blocks/__test-fixtures__/block-fixtures.js +506 -0
- package/dist/blocks/__test-fixtures__/block-fixtures.js.map +1 -0
- package/dist/blocks/component-types.d.ts +82 -0
- package/dist/blocks/component-types.d.ts.map +1 -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 +169 -0
- package/dist/blocks/composition-rules.d.ts.map +1 -0
- package/dist/blocks/composition-rules.js +391 -0
- package/dist/blocks/composition-rules.js.map +1 -0
- package/dist/blocks/hooks.d.ts +32 -0
- package/dist/blocks/hooks.d.ts.map +1 -0
- package/dist/blocks/hooks.js +42 -0
- package/dist/blocks/hooks.js.map +1 -0
- package/dist/blocks/index.d.ts +16 -0
- package/dist/blocks/index.d.ts.map +1 -0
- package/dist/blocks/index.js +24 -0
- package/dist/blocks/index.js.map +1 -0
- package/dist/blocks/registry.d.ts +85 -0
- package/dist/blocks/registry.d.ts.map +1 -0
- package/dist/blocks/registry.js +136 -0
- package/dist/blocks/registry.js.map +1 -0
- package/dist/blocks/rule-engine.d.ts +132 -0
- package/dist/blocks/rule-engine.d.ts.map +1 -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 +14 -0
- package/dist/blocks/surface-event-context.d.ts.map +1 -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 +14 -0
- package/dist/blocks/surface-id-context.d.ts.map +1 -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 +42 -0
- package/dist/bus.d.ts.map +1 -0
- package/dist/bus.js +158 -0
- package/dist/bus.js.map +1 -0
- package/dist/cache/index.d.ts +10 -0
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/index.js +10 -0
- package/dist/cache/index.js.map +1 -0
- package/dist/cache/memory.d.ts +33 -0
- package/dist/cache/memory.d.ts.map +1 -0
- package/dist/cache/memory.js +138 -0
- package/dist/cache/memory.js.map +1 -0
- package/dist/cache/types.d.ts +63 -0
- package/dist/cache/types.d.ts.map +1 -0
- package/dist/cache/types.js +9 -0
- package/dist/cache/types.js.map +1 -0
- package/dist/capability/index.d.ts +73 -0
- package/dist/capability/index.d.ts.map +1 -0
- package/dist/capability/index.js +87 -0
- package/dist/capability/index.js.map +1 -0
- package/dist/capability/kernel-capabilities.d.ts +32 -0
- package/dist/capability/kernel-capabilities.d.ts.map +1 -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 +72 -0
- package/dist/capability/platform-capabilities.d.ts.map +1 -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 +71 -0
- package/dist/capability/type-mapping.d.ts.map +1 -0
- package/dist/capability/type-mapping.js +107 -0
- package/dist/capability/type-mapping.js.map +1 -0
- package/dist/capability/validation.d.ts +47 -0
- package/dist/capability/validation.d.ts.map +1 -0
- package/dist/capability/validation.js +54 -0
- package/dist/capability/validation.js.map +1 -0
- package/dist/connectors/declared-connector.d.ts +49 -0
- package/dist/connectors/declared-connector.d.ts.map +1 -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 +51 -0
- package/dist/connectors/engine-registry.d.ts.map +1 -0
- package/dist/connectors/engine-registry.js +135 -0
- package/dist/connectors/engine-registry.js.map +1 -0
- package/dist/connectors/index.d.ts +15 -0
- package/dist/connectors/index.d.ts.map +1 -0
- package/dist/connectors/index.js +14 -0
- package/dist/connectors/index.js.map +1 -0
- package/dist/connectors/module-loader.d.ts +71 -0
- package/dist/connectors/module-loader.d.ts.map +1 -0
- package/dist/connectors/module-loader.js +143 -0
- package/dist/connectors/module-loader.js.map +1 -0
- package/dist/connectors/types.d.ts +175 -0
- package/dist/connectors/types.d.ts.map +1 -0
- package/dist/connectors/types.js +23 -0
- package/dist/connectors/types.js.map +1 -0
- package/dist/context.d.ts +28 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +38 -0
- package/dist/context.js.map +1 -0
- package/dist/data/DataCapabilityProvider.d.ts +40 -0
- package/dist/data/DataCapabilityProvider.d.ts.map +1 -0
- package/dist/data/DataCapabilityProvider.js +58 -0
- package/dist/data/DataCapabilityProvider.js.map +1 -0
- package/dist/data/index.d.ts +18 -0
- package/dist/data/index.d.ts.map +1 -0
- package/dist/data/index.js +18 -0
- package/dist/data/index.js.map +1 -0
- package/dist/data/manager.d.ts +41 -0
- package/dist/data/manager.d.ts.map +1 -0
- package/dist/data/manager.js +442 -0
- package/dist/data/manager.js.map +1 -0
- package/dist/data/types.d.ts +219 -0
- package/dist/data/types.d.ts.map +1 -0
- package/dist/data/types.js +2 -0
- package/dist/data/types.js.map +1 -0
- package/dist/entity/aggregate.d.ts +9 -0
- package/dist/entity/aggregate.d.ts.map +1 -0
- package/dist/entity/aggregate.js +24 -0
- package/dist/entity/aggregate.js.map +1 -0
- package/dist/entity/apply-query.d.ts +59 -0
- package/dist/entity/apply-query.d.ts.map +1 -0
- package/dist/entity/apply-query.js +264 -0
- package/dist/entity/apply-query.js.map +1 -0
- package/dist/entity/behavior.d.ts +95 -0
- package/dist/entity/behavior.d.ts.map +1 -0
- package/dist/entity/behavior.js +78 -0
- package/dist/entity/behavior.js.map +1 -0
- package/dist/entity/connector.d.ts +142 -0
- package/dist/entity/connector.d.ts.map +1 -0
- package/dist/entity/connector.js +89 -0
- package/dist/entity/connector.js.map +1 -0
- package/dist/entity/connectors/index.d.ts +13 -0
- package/dist/entity/connectors/index.d.ts.map +1 -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 +52 -0
- package/dist/entity/connectors/static.d.ts.map +1 -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 +51 -0
- package/dist/entity/entity-datasource.d.ts.map +1 -0
- package/dist/entity/entity-datasource.js +33 -0
- package/dist/entity/entity-datasource.js.map +1 -0
- package/dist/entity/facade.d.ts +71 -0
- package/dist/entity/facade.d.ts.map +1 -0
- package/dist/entity/facade.js +70 -0
- package/dist/entity/facade.js.map +1 -0
- package/dist/entity/http-connector.d.ts +124 -0
- package/dist/entity/http-connector.d.ts.map +1 -0
- package/dist/entity/http-connector.js +295 -0
- package/dist/entity/http-connector.js.map +1 -0
- package/dist/entity/index.d.ts +39 -0
- package/dist/entity/index.d.ts.map +1 -0
- package/dist/entity/index.js +31 -0
- package/dist/entity/index.js.map +1 -0
- package/dist/entity/postgres-connector.d.ts +65 -0
- package/dist/entity/postgres-connector.d.ts.map +1 -0
- package/dist/entity/postgres-connector.js +239 -0
- package/dist/entity/postgres-connector.js.map +1 -0
- package/dist/entity/registry.d.ts +60 -0
- package/dist/entity/registry.d.ts.map +1 -0
- package/dist/entity/registry.js +90 -0
- package/dist/entity/registry.js.map +1 -0
- package/dist/entity/store.d.ts +67 -0
- package/dist/entity/store.d.ts.map +1 -0
- package/dist/entity/store.js +349 -0
- package/dist/entity/store.js.map +1 -0
- package/dist/entity/types.d.ts +181 -0
- package/dist/entity/types.d.ts.map +1 -0
- package/dist/entity/types.js +10 -0
- package/dist/entity/types.js.map +1 -0
- package/dist/execution-context.d.ts +89 -0
- package/dist/execution-context.d.ts.map +1 -0
- package/dist/execution-context.js +146 -0
- package/dist/execution-context.js.map +1 -0
- package/dist/executor.d.ts +28 -0
- package/dist/executor.d.ts.map +1 -0
- package/dist/executor.js +33 -0
- package/dist/executor.js.map +1 -0
- package/dist/forms/form-entity-bridge.d.ts +95 -0
- package/dist/forms/form-entity-bridge.d.ts.map +1 -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 +35 -0
- package/dist/forms/form-write-target.d.ts.map +1 -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 +23 -0
- package/dist/forms/index.d.ts.map +1 -0
- package/dist/forms/index.js +25 -0
- package/dist/forms/index.js.map +1 -0
- package/dist/forms/named-control-values.d.ts +28 -0
- package/dist/forms/named-control-values.d.ts.map +1 -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 +56 -0
- package/dist/forms/processor.d.ts.map +1 -0
- package/dist/forms/processor.js +154 -0
- package/dist/forms/processor.js.map +1 -0
- package/dist/forms/types.d.ts +175 -0
- package/dist/forms/types.d.ts.map +1 -0
- package/dist/forms/types.js +33 -0
- package/dist/forms/types.js.map +1 -0
- package/dist/forms/validator.d.ts +62 -0
- package/dist/forms/validator.d.ts.map +1 -0
- package/dist/forms/validator.js +218 -0
- package/dist/forms/validator.js.map +1 -0
- package/dist/graph/index.d.ts +11 -0
- package/dist/graph/index.d.ts.map +1 -0
- package/dist/graph/index.js +9 -0
- package/dist/graph/index.js.map +1 -0
- package/dist/graph/resolver.d.ts +67 -0
- package/dist/graph/resolver.d.ts.map +1 -0
- package/dist/graph/resolver.js +243 -0
- package/dist/graph/resolver.js.map +1 -0
- package/dist/graph/types.d.ts +126 -0
- package/dist/graph/types.d.ts.map +1 -0
- package/dist/graph/types.js +9 -0
- package/dist/graph/types.js.map +1 -0
- package/dist/hooks/useInstalledAtoms.d.ts +97 -0
- package/dist/hooks/useInstalledAtoms.d.ts.map +1 -0
- package/dist/hooks/useInstalledAtoms.js +92 -0
- package/dist/hooks/useInstalledAtoms.js.map +1 -0
- package/dist/i18n/index.d.ts +16 -0
- package/dist/i18n/index.d.ts.map +1 -0
- package/dist/i18n/index.js +14 -0
- package/dist/i18n/index.js.map +1 -0
- package/dist/i18n/translator.d.ts +77 -0
- package/dist/i18n/translator.d.ts.map +1 -0
- package/dist/i18n/translator.js +357 -0
- package/dist/i18n/translator.js.map +1 -0
- package/dist/i18n/types.d.ts +94 -0
- package/dist/i18n/types.d.ts.map +1 -0
- package/dist/i18n/types.js +9 -0
- package/dist/i18n/types.js.map +1 -0
- package/dist/index.d.ts +65 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +95 -0
- package/dist/index.js.map +1 -0
- package/dist/kernel.d.ts +48 -0
- package/dist/kernel.d.ts.map +1 -0
- package/dist/kernel.js +258 -0
- package/dist/kernel.js.map +1 -0
- package/dist/perf/budget.d.ts +93 -0
- package/dist/perf/budget.d.ts.map +1 -0
- package/dist/perf/budget.js +122 -0
- package/dist/perf/budget.js.map +1 -0
- package/dist/perf/index.d.ts +17 -0
- package/dist/perf/index.d.ts.map +1 -0
- package/dist/perf/index.js +13 -0
- package/dist/perf/index.js.map +1 -0
- package/dist/perf/render-timer.d.ts +91 -0
- package/dist/perf/render-timer.d.ts.map +1 -0
- package/dist/perf/render-timer.js +136 -0
- package/dist/perf/render-timer.js.map +1 -0
- package/dist/perf/reporter.d.ts +127 -0
- package/dist/perf/reporter.d.ts.map +1 -0
- package/dist/perf/reporter.js +241 -0
- package/dist/perf/reporter.js.map +1 -0
- package/dist/perf/web-vitals.d.ts +112 -0
- package/dist/perf/web-vitals.d.ts.map +1 -0
- package/dist/perf/web-vitals.js +268 -0
- package/dist/perf/web-vitals.js.map +1 -0
- package/dist/registry.d.ts +64 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +126 -0
- package/dist/registry.js.map +1 -0
- package/dist/render/BehaviorSurfaceProvider.d.ts +66 -0
- package/dist/render/BehaviorSurfaceProvider.d.ts.map +1 -0
- package/dist/render/BehaviorSurfaceProvider.js +185 -0
- package/dist/render/BehaviorSurfaceProvider.js.map +1 -0
- package/dist/render/BlockErrorBoundary.d.ts +27 -0
- package/dist/render/BlockErrorBoundary.d.ts.map +1 -0
- package/dist/render/BlockErrorBoundary.js +41 -0
- package/dist/render/BlockErrorBoundary.js.map +1 -0
- package/dist/render/DataBoundSurfaceRenderer.d.ts +79 -0
- package/dist/render/DataBoundSurfaceRenderer.d.ts.map +1 -0
- package/dist/render/DataBoundSurfaceRenderer.js +998 -0
- package/dist/render/DataBoundSurfaceRenderer.js.map +1 -0
- package/dist/render/DynamicBlockWrapper.d.ts +24 -0
- package/dist/render/DynamicBlockWrapper.d.ts.map +1 -0
- package/dist/render/DynamicBlockWrapper.js +117 -0
- package/dist/render/DynamicBlockWrapper.js.map +1 -0
- package/dist/render/appearance-resolver.d.ts +51 -0
- package/dist/render/appearance-resolver.d.ts.map +1 -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 +70 -0
- package/dist/render/capability-gate.d.ts.map +1 -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 +42 -0
- package/dist/render/conditional-states-css.d.ts.map +1 -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 +32 -0
- package/dist/render/connections-context.d.ts.map +1 -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 +37 -0
- package/dist/render/dark-theme-css.d.ts.map +1 -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 +192 -0
- package/dist/render/data-binding-types.d.ts.map +1 -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 +31 -0
- package/dist/render/data-cache.d.ts.map +1 -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 +18 -0
- package/dist/render/derive-dark-theme.d.ts.map +1 -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 +78 -0
- package/dist/render/dynamic-artifact-context.d.ts.map +1 -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 +81 -0
- package/dist/render/dynamic-block-loader.d.ts.map +1 -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 +19 -0
- package/dist/render/endpoint.d.ts.map +1 -0
- package/dist/render/endpoint.js +109 -0
- package/dist/render/endpoint.js.map +1 -0
- package/dist/render/entity-facade-context.d.ts +35 -0
- package/dist/render/entity-facade-context.d.ts.map +1 -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 +42 -0
- package/dist/render/group-states-css.d.ts.map +1 -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 +9 -0
- package/dist/render/hooks/useSurfaceEvents.d.ts.map +1 -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 +9 -0
- package/dist/render/hooks/useSurfaceId.d.ts.map +1 -0
- package/dist/render/hooks/useSurfaceId.js +9 -0
- package/dist/render/hooks/useSurfaceId.js.map +1 -0
- package/dist/render/index.d.ts +49 -0
- package/dist/render/index.d.ts.map +1 -0
- package/dist/render/index.js +61 -0
- package/dist/render/index.js.map +1 -0
- package/dist/render/interaction-rule-engine.d.ts +13 -0
- package/dist/render/interaction-rule-engine.d.ts.map +1 -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 +32 -0
- package/dist/render/keyframes-css.d.ts.map +1 -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 +4 -0
- package/dist/render/query-state.d.ts.map +1 -0
- package/dist/render/query-state.js +70 -0
- package/dist/render/query-state.js.map +1 -0
- package/dist/render/renderer.d.ts +45 -0
- package/dist/render/renderer.d.ts.map +1 -0
- package/dist/render/renderer.js +937 -0
- package/dist/render/renderer.js.map +1 -0
- package/dist/render/resolve-expressions.d.ts +38 -0
- package/dist/render/resolve-expressions.d.ts.map +1 -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 +43 -0
- package/dist/render/responsive-style-css.d.ts.map +1 -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 +50 -0
- package/dist/render/responsive.d.ts.map +1 -0
- package/dist/render/responsive.js +99 -0
- package/dist/render/responsive.js.map +1 -0
- package/dist/render/scoped-css.d.ts +72 -0
- package/dist/render/scoped-css.d.ts.map +1 -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 +70 -0
- package/dist/render/shell-selection.d.ts.map +1 -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 +33 -0
- package/dist/render/states-css.d.ts.map +1 -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 +19 -0
- package/dist/render/surface-data-context.d.ts.map +1 -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 +20 -0
- package/dist/render/surface-event-bus.d.ts.map +1 -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 +12 -0
- package/dist/render/surface-event-context.d.ts.map +1 -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 +9 -0
- package/dist/render/surface-id-context.d.ts.map +1 -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 +15 -0
- package/dist/render/theme-utils.d.ts.map +1 -0
- package/dist/render/theme-utils.js +15 -0
- package/dist/render/theme-utils.js.map +1 -0
- package/dist/render/types.d.ts +283 -0
- package/dist/render/types.d.ts.map +1 -0
- package/dist/render/types.js +9 -0
- package/dist/render/types.js.map +1 -0
- package/dist/render/visibility-css.d.ts +43 -0
- package/dist/render/visibility-css.d.ts.map +1 -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 +35 -0
- package/dist/render/width-scope.d.ts.map +1 -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 +34 -0
- package/dist/render/write-only-sources.d.ts.map +1 -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 +15 -0
- package/dist/routing/index.d.ts.map +1 -0
- package/dist/routing/index.js +15 -0
- package/dist/routing/index.js.map +1 -0
- package/dist/routing/router.d.ts +43 -0
- package/dist/routing/router.d.ts.map +1 -0
- package/dist/routing/router.js +218 -0
- package/dist/routing/router.js.map +1 -0
- package/dist/routing/types.d.ts +125 -0
- package/dist/routing/types.d.ts.map +1 -0
- package/dist/routing/types.js +9 -0
- package/dist/routing/types.js.map +1 -0
- package/dist/seo/generator.d.ts +40 -0
- package/dist/seo/generator.d.ts.map +1 -0
- package/dist/seo/generator.js +236 -0
- package/dist/seo/generator.js.map +1 -0
- package/dist/seo/index.d.ts +15 -0
- package/dist/seo/index.d.ts.map +1 -0
- package/dist/seo/index.js +15 -0
- package/dist/seo/index.js.map +1 -0
- package/dist/seo/types.d.ts +182 -0
- package/dist/seo/types.d.ts.map +1 -0
- package/dist/seo/types.js +9 -0
- package/dist/seo/types.js.map +1 -0
- package/dist/state/index.d.ts +16 -0
- package/dist/state/index.d.ts.map +1 -0
- package/dist/state/index.js +16 -0
- package/dist/state/index.js.map +1 -0
- package/dist/state/manager.d.ts +34 -0
- package/dist/state/manager.d.ts.map +1 -0
- package/dist/state/manager.js +81 -0
- package/dist/state/manager.js.map +1 -0
- package/dist/state/store.d.ts +41 -0
- package/dist/state/store.d.ts.map +1 -0
- package/dist/state/store.js +145 -0
- package/dist/state/store.js.map +1 -0
- package/dist/state/types.d.ts +108 -0
- package/dist/state/types.d.ts.map +1 -0
- package/dist/state/types.js +9 -0
- package/dist/state/types.js.map +1 -0
- package/dist/surface/expression.d.ts +66 -0
- package/dist/surface/expression.d.ts.map +1 -0
- package/dist/surface/expression.js +179 -0
- package/dist/surface/expression.js.map +1 -0
- package/dist/surface/index.d.ts +33 -0
- package/dist/surface/index.d.ts.map +1 -0
- package/dist/surface/index.js +25 -0
- package/dist/surface/index.js.map +1 -0
- package/dist/surface/interpreter.d.ts +97 -0
- package/dist/surface/interpreter.d.ts.map +1 -0
- package/dist/surface/interpreter.js +231 -0
- package/dist/surface/interpreter.js.map +1 -0
- package/dist/surface/layout.d.ts +55 -0
- package/dist/surface/layout.d.ts.map +1 -0
- package/dist/surface/layout.js +288 -0
- package/dist/surface/layout.js.map +1 -0
- package/dist/surface/output.d.ts +91 -0
- package/dist/surface/output.d.ts.map +1 -0
- package/dist/surface/output.js +104 -0
- package/dist/surface/output.js.map +1 -0
- package/dist/surface/primitives.d.ts +181 -0
- package/dist/surface/primitives.d.ts.map +1 -0
- package/dist/surface/primitives.js +339 -0
- package/dist/surface/primitives.js.map +1 -0
- package/dist/surface/renderer.d.ts +97 -0
- package/dist/surface/renderer.d.ts.map +1 -0
- package/dist/surface/renderer.js +190 -0
- package/dist/surface/renderer.js.map +1 -0
- package/dist/surface/schemas.d.ts +182 -0
- package/dist/surface/schemas.d.ts.map +1 -0
- package/dist/surface/schemas.js +142 -0
- package/dist/surface/schemas.js.map +1 -0
- package/dist/surface/transforms.d.ts +145 -0
- package/dist/surface/transforms.d.ts.map +1 -0
- package/dist/surface/transforms.js +120 -0
- package/dist/surface/transforms.js.map +1 -0
- package/dist/surface/types.d.ts +161 -0
- package/dist/surface/types.d.ts.map +1 -0
- package/dist/surface/types.js +51 -0
- package/dist/surface/types.js.map +1 -0
- package/dist/surface/validation.d.ts +124 -0
- package/dist/surface/validation.d.ts.map +1 -0
- package/dist/surface/validation.js +147 -0
- package/dist/surface/validation.js.map +1 -0
- package/dist/validator.d.ts +56 -0
- package/dist/validator.d.ts.map +1 -0
- package/dist/validator.js +100 -0
- package/dist/validator.js.map +1 -0
- package/package.json +105 -3
|
@@ -0,0 +1,937 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Surface Renderer
|
|
4
|
+
*
|
|
5
|
+
* Phase 2A.1: Main Surface-to-React rendering component
|
|
6
|
+
*
|
|
7
|
+
* @module @pwfabric/runtime/render/renderer
|
|
8
|
+
*/
|
|
9
|
+
import * as React from 'react';
|
|
10
|
+
import { FLEX_HOIST_KEYS, LAYOUT_HOIST_KEYS } from './scoped-css.js';
|
|
11
|
+
import { resolveBlockLayout, resolveMeasureStyle, resolveSurfaceRootStyle, useViewport, getColumnsAtBreakpoint, } from './responsive.js';
|
|
12
|
+
import { chunkByWidthScope, resolveWidthScope } from './width-scope.js';
|
|
13
|
+
import { resolveAppearanceVars, resolveAppearanceDarkVars, resolveAppearanceKeyframes, } from './appearance-resolver.js';
|
|
14
|
+
import { collectVisibilityCss } from './visibility-css.js';
|
|
15
|
+
import { collectStatesCss } from './states-css.js';
|
|
16
|
+
import { collectConditionalStatesCss } from './conditional-states-css.js';
|
|
17
|
+
import { collectResponsiveStyleCss } from './responsive-style-css.js';
|
|
18
|
+
import { collectGroupStatesCss } from './group-states-css.js';
|
|
19
|
+
import { buildDarkThemeCss } from './dark-theme-css.js';
|
|
20
|
+
import { resolveAppearanceMode } from '@pwfabric/contracts';
|
|
21
|
+
import { buildKeyframesCss } from './keyframes-css.js';
|
|
22
|
+
import { DEFAULT_BLOCK_LAYOUT } from '@pwfabric/core';
|
|
23
|
+
// `defaultBlockComponents` retired. Production render
|
|
24
|
+
// path goes exclusively through the install pipeline — `customComponents`
|
|
25
|
+
// (typically wired from `useDynamicArtifacts`) supplies the runtime
|
|
26
|
+
// component map; blocks without a descriptor render as
|
|
27
|
+
// "Unknown block" placeholders.
|
|
28
|
+
import { useEntitlements, GatedBlockPlaceholder } from './capability-gate.js';
|
|
29
|
+
import { useDynamicBlocks } from './dynamic-artifact-context.js';
|
|
30
|
+
import { DynamicBlockWrapper } from './DynamicBlockWrapper.js';
|
|
31
|
+
import { BlockErrorBoundary } from './BlockErrorBoundary.js';
|
|
32
|
+
import { BehaviorSurfaceProvider } from './BehaviorSurfaceProvider.js';
|
|
33
|
+
import { isCapability } from '../capability/type-mapping.js';
|
|
34
|
+
import { EntityFacadeContext } from './entity-facade-context.js';
|
|
35
|
+
import { SurfaceConnectionsContext } from './connections-context.js';
|
|
36
|
+
import { ShellSelectionProvider } from './shell-selection.js';
|
|
37
|
+
import { isRepeatExpanded } from './resolve-expressions.js';
|
|
38
|
+
// ============================================================
|
|
39
|
+
// HELPERS
|
|
40
|
+
// ============================================================
|
|
41
|
+
/**
|
|
42
|
+
* Merge component registries, filtering out undefined values
|
|
43
|
+
*/
|
|
44
|
+
function mergeComponentRegistries(base = {}, overrides) {
|
|
45
|
+
const merged = {};
|
|
46
|
+
for (const [key, value] of Object.entries(base)) {
|
|
47
|
+
if (value !== undefined)
|
|
48
|
+
merged[key] = value;
|
|
49
|
+
}
|
|
50
|
+
if (overrides) {
|
|
51
|
+
for (const [key, value] of Object.entries(overrides)) {
|
|
52
|
+
if (value !== undefined)
|
|
53
|
+
merged[key] = value;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return merged;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Project a canonical `SurfaceEventDescriptor.type` (contracts vocabulary, e.g.
|
|
60
|
+
* `'block:click'`, `'form:submit'`, `'input:change'`) onto the renderer's legacy
|
|
61
|
+
* `SurfaceEvent['type']` enum. Anything without a direct legacy equivalent
|
|
62
|
+
* collapses to `'custom'`. Only used to satisfy the `onEvent` handler's shape —
|
|
63
|
+
* the dispatcher reads `blockId`/`payload`, not the projected `type`.
|
|
64
|
+
*/
|
|
65
|
+
function mapCanonicalEventType(type) {
|
|
66
|
+
switch (type) {
|
|
67
|
+
case 'block:click':
|
|
68
|
+
return 'click';
|
|
69
|
+
case 'form:submit':
|
|
70
|
+
return 'submit';
|
|
71
|
+
case 'form:change':
|
|
72
|
+
case 'input:change':
|
|
73
|
+
return 'change';
|
|
74
|
+
default:
|
|
75
|
+
return 'custom';
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Create an ImageBlock component that uses the provided adapter
|
|
80
|
+
*/
|
|
81
|
+
function createImageBlockWithAdapter(adapter) {
|
|
82
|
+
return ({ props, block }) => {
|
|
83
|
+
// Build adapter props, only including defined values
|
|
84
|
+
const adapterProps = {
|
|
85
|
+
src: props.src || '/placeholder.png',
|
|
86
|
+
alt: props.alt ?? '',
|
|
87
|
+
blockId: block.id,
|
|
88
|
+
};
|
|
89
|
+
// Add optional props only if they have values
|
|
90
|
+
if (props.width !== undefined) {
|
|
91
|
+
;
|
|
92
|
+
adapterProps.width = props.width;
|
|
93
|
+
}
|
|
94
|
+
if (props.height !== undefined) {
|
|
95
|
+
;
|
|
96
|
+
adapterProps.height = props.height;
|
|
97
|
+
}
|
|
98
|
+
if (props.loading !== undefined) {
|
|
99
|
+
;
|
|
100
|
+
adapterProps.loading = props.loading;
|
|
101
|
+
adapterProps.priority = props.loading === 'eager';
|
|
102
|
+
}
|
|
103
|
+
if (props.objectFit !== undefined) {
|
|
104
|
+
;
|
|
105
|
+
adapterProps.objectFit =
|
|
106
|
+
props.objectFit;
|
|
107
|
+
}
|
|
108
|
+
if (props.aspectRatio !== undefined) {
|
|
109
|
+
;
|
|
110
|
+
adapterProps.aspectRatio = props.aspectRatio;
|
|
111
|
+
}
|
|
112
|
+
if (props.borderRadius !== undefined) {
|
|
113
|
+
;
|
|
114
|
+
adapterProps.borderRadius = props.borderRadius;
|
|
115
|
+
}
|
|
116
|
+
if (props.placeholder !== undefined) {
|
|
117
|
+
;
|
|
118
|
+
adapterProps.placeholder = props.placeholder;
|
|
119
|
+
}
|
|
120
|
+
if (props.blurDataURL !== undefined) {
|
|
121
|
+
;
|
|
122
|
+
adapterProps.blurDataURL = props.blurDataURL;
|
|
123
|
+
}
|
|
124
|
+
return React.createElement(adapter, adapterProps);
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Emit a block's outermost wrapper. When `renderBlockWrapper` is provided
|
|
129
|
+
* (editor seam) the decorator owns the wrapper element and receives the
|
|
130
|
+
* runtime-computed `wrapperProps` (layout style + selectors). Otherwise the
|
|
131
|
+
* default `<div {...wrapperProps}>` is rendered — byte-identical to the
|
|
132
|
+
* pre-seam runtime (modulo the unified `data-block-id`).
|
|
133
|
+
*/
|
|
134
|
+
/**
|
|
135
|
+
* The attributes EVERY wrapper must carry — in one place.
|
|
136
|
+
*
|
|
137
|
+
* `states-css.ts` and `conditional-states-css.ts` EXCLUDE the wrapper from
|
|
138
|
+
* their state rules with `:not([data-block-wrapper])`. Without the attribute
|
|
139
|
+
* there is no exclusion: a rule matches both the wrapper and the component
|
|
140
|
+
* root, and the defect described in states-css comes back ("the square
|
|
141
|
+
* wrapper takes the hover box-shadow, corners show under the rounded card,
|
|
142
|
+
* transforms stack") — on every block, including blocks installed from a pack.
|
|
143
|
+
*
|
|
144
|
+
* All four call sites derive from this base, so they cannot drift apart.
|
|
145
|
+
*/
|
|
146
|
+
function baseWrapperProps(block, layoutStyle, scopeAttr) {
|
|
147
|
+
return {
|
|
148
|
+
// `exactOptionalPropertyTypes`: an optional field cannot be given an
|
|
149
|
+
// explicit `undefined`; when absent it is left out.
|
|
150
|
+
...(layoutStyle ? { style: layoutStyle } : {}),
|
|
151
|
+
id: block.id,
|
|
152
|
+
'data-block-id': block.id,
|
|
153
|
+
...scopeAttr,
|
|
154
|
+
// `data-block-wrapper`: an ALIAS carrying the same value as `data-block-id`
|
|
155
|
+
// but with a separate job — the one marker that tells the wrapper apart
|
|
156
|
+
// from the component root.
|
|
157
|
+
'data-block-wrapper': block.id,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
function emitBlockWrapper(block, wrapperProps, inner, parentBlock, renderBlockWrapper) {
|
|
161
|
+
if (renderBlockWrapper) {
|
|
162
|
+
return (_jsx(React.Fragment, { children: renderBlockWrapper({
|
|
163
|
+
block,
|
|
164
|
+
children: inner,
|
|
165
|
+
wrapperProps,
|
|
166
|
+
...(parentBlock !== undefined ? { parentBlock } : {}),
|
|
167
|
+
}) }, block.id));
|
|
168
|
+
}
|
|
169
|
+
return (_jsx("div", { ...wrapperProps, children: inner }, block.id));
|
|
170
|
+
}
|
|
171
|
+
// ============================================================
|
|
172
|
+
// RESPONSIVE STYLE FLATTENING
|
|
173
|
+
// ============================================================
|
|
174
|
+
/**
|
|
175
|
+
* Strip a block's responsive (`{ xs?, sm?, … }`) `style` prop values for INLINE
|
|
176
|
+
* application: those are emitted ENTIRELY as scoped stylesheet rules by
|
|
177
|
+
* `collectResponsiveStyleCss` (base as a non-media rule + larger breakpoints as
|
|
178
|
+
* `@media`). They must NOT be applied inline — inline declarations beat
|
|
179
|
+
* stylesheet/@media rules, so an inline base would pin the value and the
|
|
180
|
+
* responsive overrides would never take effect. Flat string values pass through
|
|
181
|
+
* untouched. Returns the SAME block object when no responsive value is present
|
|
182
|
+
* (zero allocation for existing surfaces).
|
|
183
|
+
*/
|
|
184
|
+
function flattenResponsiveStyle(block) {
|
|
185
|
+
const style = block.style;
|
|
186
|
+
if (!style)
|
|
187
|
+
return block;
|
|
188
|
+
let hasResponsive = false;
|
|
189
|
+
const flat = {};
|
|
190
|
+
for (const [prop, value] of Object.entries(style)) {
|
|
191
|
+
if (typeof value === 'string') {
|
|
192
|
+
flat[prop] = value;
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
if (value === null || value === undefined)
|
|
196
|
+
continue;
|
|
197
|
+
// Responsive value map → handled ENTIRELY by responsive-style-css (base as a
|
|
198
|
+
// non-media scoped rule + larger breakpoints as @media). It must NOT be
|
|
199
|
+
// applied inline: inline declarations beat stylesheet/@media rules, so an
|
|
200
|
+
// inline base would pin the value and the responsive overrides would never
|
|
201
|
+
// take effect. Strip it from the inline style here.
|
|
202
|
+
hasResponsive = true;
|
|
203
|
+
}
|
|
204
|
+
if (!hasResponsive)
|
|
205
|
+
return block;
|
|
206
|
+
return { ...block, style: flat };
|
|
207
|
+
}
|
|
208
|
+
// ============================================================
|
|
209
|
+
// BLOCK RENDERER
|
|
210
|
+
// ============================================================
|
|
211
|
+
/**
|
|
212
|
+
* Compute a block's grid/flex layout style. Applied to EVERY render path
|
|
213
|
+
* (static, dynamic, unknown, gated) so installed dynamic blocks lay out
|
|
214
|
+
* identically to static ones — otherwise full-span dynamic sections auto-place
|
|
215
|
+
* side-by-side instead of stacking.
|
|
216
|
+
*/
|
|
217
|
+
/**
|
|
218
|
+
* CHROME IS DECLARED, NOT HARD-CODED.
|
|
219
|
+
*
|
|
220
|
+
* The engine keeps no list of chrome block names (navbar, rail, bottom bar…):
|
|
221
|
+
* such a list would be one pack's vocabulary, and a second pack could not
|
|
222
|
+
* express its own chrome. Instead a block declares its own
|
|
223
|
+
* `layout.widthScope` and the engine applies it. That a piece of chrome
|
|
224
|
+
* inside a row is not stretched to an equal column also follows from that
|
|
225
|
+
* declaration (see `resolveWidthScope` below).
|
|
226
|
+
*
|
|
227
|
+
* The one name kept below, `split-view`, is not pack vocabulary: it is a
|
|
228
|
+
* LAYOUT block that places its own children and keeps the selection in
|
|
229
|
+
* surface-owned state (see `shell-selection`).
|
|
230
|
+
*/
|
|
231
|
+
const CHILD_LAYOUT_OWNER = 'split-view';
|
|
232
|
+
// Style keys hoisted from block.style to the WRAPPER div (the flex/grid item).
|
|
233
|
+
// Module-scoped because renderBlock must strip the SAME keys from the style the
|
|
234
|
+
// component receives — otherwise a margin is applied twice (wrapper + inner):
|
|
235
|
+
// marginTop:'-40%' would move content by -335px AND -256px.
|
|
236
|
+
// FLEX_HOIST_KEYS / LAYOUT_HOIST_KEYS live in scoped-css.ts — the responsive
|
|
237
|
+
// CSS generator must read the SAME list (one hoisting rule, one place).
|
|
238
|
+
/** block.style minus the hoisted keys — what the component itself may apply. */
|
|
239
|
+
function stripHoistedStyle(block) {
|
|
240
|
+
const style = block.style;
|
|
241
|
+
if (!style)
|
|
242
|
+
return block;
|
|
243
|
+
let has = false;
|
|
244
|
+
for (const key of LAYOUT_HOIST_KEYS)
|
|
245
|
+
if (style[key] !== undefined) {
|
|
246
|
+
has = true;
|
|
247
|
+
break;
|
|
248
|
+
}
|
|
249
|
+
if (!has)
|
|
250
|
+
return block;
|
|
251
|
+
const rest = { ...style };
|
|
252
|
+
for (const key of LAYOUT_HOIST_KEYS)
|
|
253
|
+
delete rest[key];
|
|
254
|
+
return { ...block, style: rest };
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* ROW STACKING LAW.
|
|
258
|
+
*
|
|
259
|
+
* A `container` with `direction: 'row'` is a flex row with `nowrap`; without
|
|
260
|
+
* this rule nothing turns it into a column on a phone, so on xs a heading
|
|
261
|
+
* sits squeezed beside a segmented control that overflows, navbar pills run
|
|
262
|
+
* off the edge, and a two-column body becomes two 190px columns.
|
|
263
|
+
*
|
|
264
|
+
* The law: at the `xs` breakpoint a row container whose children include at
|
|
265
|
+
* least TWO non-intrinsic blocks renders as a column. Rows made of intrinsic
|
|
266
|
+
* blocks (avatar + name, icon + label, a button pair) are content rows and
|
|
267
|
+
* keep their direction. Authors opt out with `keepRow: true`; a row that
|
|
268
|
+
* already declares `wrap: true` handles itself. The stacked container carries
|
|
269
|
+
* `stackedFromRow: true` so child layout does not apply column-mode
|
|
270
|
+
* partial-span widths to what used to be row cells.
|
|
271
|
+
*/
|
|
272
|
+
const ROW_DIRECTIONS = new Set(['row', 'row-reverse']);
|
|
273
|
+
function applyRowStacking(block, viewport, components) {
|
|
274
|
+
if (viewport !== 'xs' || block.type !== 'container')
|
|
275
|
+
return block;
|
|
276
|
+
const props = (block.props ?? {});
|
|
277
|
+
if (!ROW_DIRECTIONS.has(String(props.direction)))
|
|
278
|
+
return block;
|
|
279
|
+
if (props.keepRow === true || props.wrap === true)
|
|
280
|
+
return block;
|
|
281
|
+
const children = block.children ?? [];
|
|
282
|
+
const fluid = children.filter((c) => components[c.type]?.sizing !== 'intrinsic').length;
|
|
283
|
+
if (fluid < 2)
|
|
284
|
+
return block;
|
|
285
|
+
return { ...block, props: { ...props, direction: 'column', stackedFromRow: true } };
|
|
286
|
+
}
|
|
287
|
+
const FIXED_PX_WIDTH = /^\s*\d+(?:\.\d+)?\s*px\s*$/i;
|
|
288
|
+
/**
|
|
289
|
+
* A width that is a fixed length — a finite number (px, the props unit) or a
|
|
290
|
+
* `px` string. Percentages, `auto`, `fit-content`, theme vars and
|
|
291
|
+
* responsive maps are NOT fixed: they say nothing about the block keeping
|
|
292
|
+
* its own size in a row.
|
|
293
|
+
*/
|
|
294
|
+
function isFixedWidth(value) {
|
|
295
|
+
if (typeof value === 'number')
|
|
296
|
+
return Number.isFinite(value) && value > 0;
|
|
297
|
+
return typeof value === 'string' && FIXED_PX_WIDTH.test(value) && parseFloat(value) > 0;
|
|
298
|
+
}
|
|
299
|
+
/** The instance states a fixed width in `props.width` or `style.width`. */
|
|
300
|
+
function hasFixedWidth(block) {
|
|
301
|
+
const props = block.props;
|
|
302
|
+
const style = block.style;
|
|
303
|
+
return isFixedWidth(props?.width) || isFixedWidth(style?.width);
|
|
304
|
+
}
|
|
305
|
+
function computeBlockLayoutStyle(block, parentBlock, viewport, sizing) {
|
|
306
|
+
// Skip grid-column when parent manages its own layout (grid, container, form, etc.)
|
|
307
|
+
const parentType = parentBlock?.type;
|
|
308
|
+
// A child that EXPLICITLY declares layout.span keeps it inside a grid parent.
|
|
309
|
+
// Otherwise asymmetric 12-col layouts are impossible: the declaration would
|
|
310
|
+
// be silently swallowed. Grid stays the owner only for span-less children
|
|
311
|
+
// (equal auto columns, the default).
|
|
312
|
+
const childDeclaresSpan = block.layout?.span !== undefined;
|
|
313
|
+
// A parent that expanded a `repeat` (the repeater family, or any block that
|
|
314
|
+
// repeats) lays its clones out with its own grid/columns — the clones must
|
|
315
|
+
// not carry the surface's 12-column span into it. A grid
|
|
316
|
+
// parent keeps its own rule above.
|
|
317
|
+
const parentRepeats = parentType !== 'grid' && isRepeatExpanded(parentBlock);
|
|
318
|
+
const parentOwnsLayout = parentRepeats ||
|
|
319
|
+
(parentType === 'grid' && !childDeclaresSpan) ||
|
|
320
|
+
parentType === 'container' ||
|
|
321
|
+
parentType === 'section' ||
|
|
322
|
+
parentType === 'form' ||
|
|
323
|
+
parentType === 'tabs' ||
|
|
324
|
+
parentType === 'tooltip' ||
|
|
325
|
+
parentType === CHILD_LAYOUT_OWNER;
|
|
326
|
+
const layout = block.layout ?? DEFAULT_BLOCK_LAYOUT;
|
|
327
|
+
const resolved = resolveBlockLayout(layout, viewport);
|
|
328
|
+
// Column container children: apply explicit width when span < surface columns
|
|
329
|
+
const parentDirection = parentBlock?.props?.direction;
|
|
330
|
+
// A row stacked by the ROW STACKING LAW is not an authored column: its
|
|
331
|
+
// children were row cells, so their partial spans must not become widths.
|
|
332
|
+
const parentStackedFromRow = parentBlock?.props?.stackedFromRow === true;
|
|
333
|
+
const isColumnContainer = parentType === 'container' &&
|
|
334
|
+
(parentDirection === 'column' || parentDirection === undefined) &&
|
|
335
|
+
!parentStackedFromRow;
|
|
336
|
+
let columnChildWidth;
|
|
337
|
+
if (isColumnContainer && parentBlock) {
|
|
338
|
+
const surfaceCols = getColumnsAtBreakpoint(viewport);
|
|
339
|
+
const parentCols = resolveBlockLayout(parentBlock.layout ?? DEFAULT_BLOCK_LAYOUT, viewport).span;
|
|
340
|
+
if (resolved.span < surfaceCols) {
|
|
341
|
+
columnChildWidth = `${(resolved.span / parentCols) * 100}%`;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
// Row container children: apply flexBasis so spans translate to proportional widths.
|
|
345
|
+
//
|
|
346
|
+
// Weighted columns: a child's OWN `block.style` flex hints win over the span-derived
|
|
347
|
+
// default. The wrapper is the flex item — so `flex`/`flexGrow`/`flexBasis`/`flexShrink`
|
|
348
|
+
// declared on a child must land on the wrapper, not the inner block. When present they
|
|
349
|
+
// fully replace the default span/equal distribution, letting an author express an
|
|
350
|
+
// arbitrary ratio (e.g. `flex:"1.62 1 0"` + `flex:"1 1 0"` → exact 1.62:1 columns)
|
|
351
|
+
// that the integer-span model cannot. `layout.span` remains the default when no flex
|
|
352
|
+
// hint is given.
|
|
353
|
+
const childStyle = block.style;
|
|
354
|
+
const childHasFlexHint = childStyle !== undefined && FLEX_HOIST_KEYS.some((k) => childStyle[k] !== undefined);
|
|
355
|
+
const isRowContainer = parentType === 'container' && parentDirection === 'row';
|
|
356
|
+
let rowChildStyle;
|
|
357
|
+
// A rail beside a row is NOT one of two equal columns. The default below
|
|
358
|
+
// gives every row child `flex: 1 1 0` — right for two content columns,
|
|
359
|
+
// wrong for chrome: it would stretch a 76px rail to half the row and squeeze
|
|
360
|
+
// the body it exists to sit beside. The block DECLARES its own scope.
|
|
361
|
+
if (isRowContainer && resolveWidthScope(block, viewport) === 'viewport' && !childHasFlexHint) {
|
|
362
|
+
rowChildStyle = { flexGrow: 0, flexShrink: 0, flexBasis: 'auto', alignSelf: 'stretch' };
|
|
363
|
+
}
|
|
364
|
+
else if (isRowContainer &&
|
|
365
|
+
(sizing === 'intrinsic' || hasFixedWidth(block)) &&
|
|
366
|
+
!childHasFlexHint) {
|
|
367
|
+
// The block declared its width is its content (avatar, badge, button…):
|
|
368
|
+
// it keeps that width; whatever sits beside it takes the rest. Without
|
|
369
|
+
// this every such block would take half the row.
|
|
370
|
+
//
|
|
371
|
+
// The same holds for an INSTANCE that states a fixed width (a 56px
|
|
372
|
+
// thumbnail beside a product title). `sizing` is static per block type,
|
|
373
|
+
// so a fluid-by-default block (image) cannot declare it; the instance's
|
|
374
|
+
// own numeric/px width is the declaration. Without it a 56px image took
|
|
375
|
+
// the equal share and squeezed the text beside it into two lines.
|
|
376
|
+
rowChildStyle = { flexGrow: 0, flexShrink: 0, flexBasis: 'auto' };
|
|
377
|
+
}
|
|
378
|
+
else if (isRowContainer && parentBlock && !childHasFlexHint) {
|
|
379
|
+
// No author flex hint → derive proportional width from the integer span.
|
|
380
|
+
const surfaceCols = getColumnsAtBreakpoint(viewport);
|
|
381
|
+
const parentCols = resolveBlockLayout(parentBlock.layout ?? DEFAULT_BLOCK_LAYOUT, viewport).span;
|
|
382
|
+
if (resolved.span < surfaceCols) {
|
|
383
|
+
// Explicit partial span → proportional width
|
|
384
|
+
rowChildStyle = {
|
|
385
|
+
flexBasis: `${(resolved.span / parentCols) * 100}%`,
|
|
386
|
+
flexGrow: 0,
|
|
387
|
+
flexShrink: 0,
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
else {
|
|
391
|
+
// Default/full-width span → equal distribution
|
|
392
|
+
rowChildStyle = { flexGrow: 1, flexShrink: 1, flexBasis: '0' };
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
// Hoist layout-positioning props (margin/maxWidth/minWidth) to the wrapper div so
|
|
396
|
+
// grid-item centering works (the inner block element is usually width:100%). Flex
|
|
397
|
+
// hints hoist here too so a row-container child's authored ratio reaches the flex
|
|
398
|
+
// item (the wrapper) rather than the inner block.
|
|
399
|
+
const hoistedStyle = {};
|
|
400
|
+
if (block.style) {
|
|
401
|
+
for (const key of LAYOUT_HOIST_KEYS) {
|
|
402
|
+
const val = block.style[key];
|
|
403
|
+
if (val !== undefined) {
|
|
404
|
+
;
|
|
405
|
+
hoistedStyle[key] = val;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
// Auto-center when maxWidth is set without an explicit margin.
|
|
410
|
+
if (hoistedStyle.maxWidth !== undefined &&
|
|
411
|
+
hoistedStyle.margin === undefined &&
|
|
412
|
+
hoistedStyle.marginLeft === undefined &&
|
|
413
|
+
hoistedStyle.marginRight === undefined) {
|
|
414
|
+
hoistedStyle.margin = '0 auto';
|
|
415
|
+
// An auto-centred max-width block FILLS up to its max width, as a block
|
|
416
|
+
// box with `max-width` + `margin: 0 auto` does in normal flow. Inside a
|
|
417
|
+
// flex column or a grid cell, auto inline margins cancel `stretch`, so
|
|
418
|
+
// without an explicit width the wrapper shrank to its content and the
|
|
419
|
+
// inner `width:100%` resolved against that shrunken box (
|
|
420
|
+
// a two-pane doc shell collapsed to 307 px and the panes stacked unless
|
|
421
|
+
// the selected text was long, and content columns narrowed).
|
|
422
|
+
// Not applied where the block keeps its own width: an intrinsic block
|
|
423
|
+
// (button, badge), a row cell (its flex basis is the width), a column
|
|
424
|
+
// child with a span-derived width, or an authored non-100% width.
|
|
425
|
+
const authoredWidth = block.style?.width;
|
|
426
|
+
if (sizing !== 'intrinsic' &&
|
|
427
|
+
!isRowContainer &&
|
|
428
|
+
columnChildWidth === undefined &&
|
|
429
|
+
(authoredWidth === undefined || authoredWidth === '100%')) {
|
|
430
|
+
hoistedStyle.width = '100%';
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
// Interaction states (block.states) are rendered as scoped `:hover`/`:active`/
|
|
434
|
+
// `:focus-visible` CSS rules elsewhere; here we add a default `transition` to
|
|
435
|
+
// the base wrapper style so the state change animates. Authors can still
|
|
436
|
+
// override via `block.style.transition`, and `prefers-reduced-motion`
|
|
437
|
+
// (SURFACE_BASE_CSS) neutralises it for users who opt out.
|
|
438
|
+
const hasStates = block.states !== undefined && block.states !== null;
|
|
439
|
+
const transitionStyle = hasStates && block.style?.transition === undefined
|
|
440
|
+
? { transition: 'all var(--pw-duration-fast, 150ms) var(--pw-easing, ease)' }
|
|
441
|
+
: {};
|
|
442
|
+
// GRID items default to min-width:auto, which lets nowrap/max-content content
|
|
443
|
+
// leak its intrinsic width into track sizing even under overflow:hidden
|
|
444
|
+
// (measured: a marquee pushed the whole page 63px). Zero it for grid items
|
|
445
|
+
// only — on FLEX children min-width:auto is load-bearing (equal-distribution
|
|
446
|
+
// rows keep children at least min-content wide; zeroing it made a highlighted
|
|
447
|
+
// word stretch to half the row). An author's own minWidth (hoisted) wins.
|
|
448
|
+
const isGridItem = !parentOwnsLayout || parentType === 'grid';
|
|
449
|
+
return {
|
|
450
|
+
...(isGridItem ? { minWidth: 0 } : {}),
|
|
451
|
+
...(!parentOwnsLayout ? { gridColumn: `span ${resolved.span} / span ${resolved.span}` } : {}),
|
|
452
|
+
...(resolved.offset !== undefined && !parentOwnsLayout
|
|
453
|
+
? { gridColumnStart: resolved.offset + 1 }
|
|
454
|
+
: {}),
|
|
455
|
+
...(columnChildWidth !== undefined ? { width: columnChildWidth } : {}),
|
|
456
|
+
...(rowChildStyle ?? {}),
|
|
457
|
+
...(resolved.order !== undefined ? { order: resolved.order } : {}),
|
|
458
|
+
...(resolved.hidden ? { display: 'none' } : {}),
|
|
459
|
+
...transitionStyle,
|
|
460
|
+
...hoistedStyle,
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* Render a single block recursively.
|
|
465
|
+
* parentBlock is used to determine the CSS strategy for nested blocks.
|
|
466
|
+
* entitlementCheck gates unentitled blocks.
|
|
467
|
+
* dynamicBlocks provides dynamic block descriptors for fallback rendering.
|
|
468
|
+
*/
|
|
469
|
+
function renderBlock({ block: rawBlock, context, components }, parentBlock, entitlementCheck, dynamicBlocks, renderBlockWrapper) {
|
|
470
|
+
// Per-prop responsive styles (block.style values that are `{ xs?, … }` maps)
|
|
471
|
+
// can't be expressed inline as media queries — flatten them to their base
|
|
472
|
+
// value here so the inline path (this block + computeBlockLayoutStyle hoists)
|
|
473
|
+
// sees a flat `Record<string,string>`; the larger-breakpoint values are
|
|
474
|
+
// emitted as scoped media-query CSS by collectResponsiveStyleCss.
|
|
475
|
+
const block = applyRowStacking(flattenResponsiveStyle(rawBlock), (context.viewport ?? 'lg'), components);
|
|
476
|
+
// Layout applies to EVERY path below (static/dynamic/unknown/gated) so installed
|
|
477
|
+
// dynamic blocks get the same grid span/width/centering as static ones. Previously
|
|
478
|
+
// only the static path applied it → full-span dynamic sections collapsed side-by-side.
|
|
479
|
+
const layoutStyle = computeBlockLayoutStyle(block, parentBlock, (context.viewport ?? 'lg'), components[block.type]?.sizing);
|
|
480
|
+
// Scope stamp. A viewport audit needs to see the declaration in the DOM to
|
|
481
|
+
// say "viewport was declared but the block does not reach the edge"; the
|
|
482
|
+
// style alone carries the result, not the declaration.
|
|
483
|
+
const scopeAttr = resolveWidthScope(block, (context.viewport ?? 'lg')) === 'viewport'
|
|
484
|
+
? { 'data-pw-width-scope': 'viewport' }
|
|
485
|
+
: {};
|
|
486
|
+
// Gate check — show placeholder for unentitled blocks
|
|
487
|
+
// Dynamic blocks with descriptors are entitled (installed + built)
|
|
488
|
+
if (entitlementCheck && !entitlementCheck(block.type) && !dynamicBlocks?.has(block.type)) {
|
|
489
|
+
return emitBlockWrapper(block, baseWrapperProps(block, layoutStyle, scopeAttr), _jsx(GatedBlockPlaceholder, { blockId: block.id, blockType: block.type }), parentBlock, renderBlockWrapper);
|
|
490
|
+
}
|
|
491
|
+
// Install-only render — when the world has the atom
|
|
492
|
+
// installed (CDN bundle URL on the descriptor) the dynamic wrapper
|
|
493
|
+
// takes over. `customComponents` may still supply test/preview-time
|
|
494
|
+
// component overrides, but the legacy `defaultBlockComponents` map is
|
|
495
|
+
// gone; unmatched block types render as "Unknown block" placeholders.
|
|
496
|
+
const dynamicDescriptor = dynamicBlocks?.get(block.type);
|
|
497
|
+
if (dynamicDescriptor) {
|
|
498
|
+
let dynamicChildren = null;
|
|
499
|
+
if (block.children && block.children.length > 0) {
|
|
500
|
+
dynamicChildren = block.children.map((child) => renderBlock({ block: child, context, components }, block, entitlementCheck, dynamicBlocks, renderBlockWrapper));
|
|
501
|
+
}
|
|
502
|
+
return emitBlockWrapper(block, { ...baseWrapperProps(block, layoutStyle, scopeAttr), 'data-dynamic-block': block.type }, _jsx(BlockErrorBoundary, { blockId: block.id, blockType: block.type, children: _jsx(DynamicBlockWrapper, { descriptor: dynamicDescriptor, block: stripHoistedStyle(block), context: context, children: dynamicChildren }) }), parentBlock, renderBlockWrapper);
|
|
503
|
+
}
|
|
504
|
+
const Component = components[block.type];
|
|
505
|
+
if (!Component) {
|
|
506
|
+
console.warn(`Unknown block type: ${block.type}`);
|
|
507
|
+
// Render children so a partial subtree still shows when a block type isn't
|
|
508
|
+
// installed (e.g. an uninstalled "Container" wrapper), but DON'T surface a
|
|
509
|
+
// visible "Unknown block: <type>" placeholder to end users on published /
|
|
510
|
+
// preview surfaces — that read as a broken page. The console.warn above and
|
|
511
|
+
// the `data-unknown-type` attribute keep it diagnosable, and the authoring
|
|
512
|
+
// editor uses its own EditableBlock placeholder, so nothing is lost there.
|
|
513
|
+
let unknownChildren = null;
|
|
514
|
+
if (block.children && block.children.length > 0) {
|
|
515
|
+
unknownChildren = block.children.map((child) => renderBlock({ block: child, context, components }, block, entitlementCheck, dynamicBlocks, renderBlockWrapper));
|
|
516
|
+
}
|
|
517
|
+
return emitBlockWrapper(block, { ...baseWrapperProps(block, layoutStyle, scopeAttr), 'data-unknown-type': block.type }, _jsx(_Fragment, { children: unknownChildren }), parentBlock, renderBlockWrapper);
|
|
518
|
+
}
|
|
519
|
+
// Recursively render children — current block becomes the parent
|
|
520
|
+
let children = null;
|
|
521
|
+
if (block.children && block.children.length > 0) {
|
|
522
|
+
children = block.children.map((child) => renderBlock({ block: child, context, components }, block, entitlementCheck, dynamicBlocks, renderBlockWrapper));
|
|
523
|
+
}
|
|
524
|
+
// TODO: When FormBlock contract includes `bindToEntity` prop,
|
|
525
|
+
// call attachFormEntityBridge(bus, entityStore, { formId: block.id, ...block.props.bindToEntity })
|
|
526
|
+
// Requires: platform/entities capability active (platform/input retired).
|
|
527
|
+
// See packages/runtime/src/forms/form-entity-bridge.ts for the bridge implementation.
|
|
528
|
+
return emitBlockWrapper(block, baseWrapperProps(block, layoutStyle, scopeAttr), _jsx(BlockErrorBoundary, { blockId: block.id, blockType: block.type, children: _jsx(Component, { block: stripHoistedStyle(block), props: block.props ?? {}, context: context, children: children }) }), parentBlock, renderBlockWrapper);
|
|
529
|
+
}
|
|
530
|
+
// ============================================================
|
|
531
|
+
// BASE CSS STANDARD (box-sizing reset)
|
|
532
|
+
// ============================================================
|
|
533
|
+
/**
|
|
534
|
+
* Scoped box-sizing reset injected at the surface render root.
|
|
535
|
+
*
|
|
536
|
+
* `box-sizing` is NOT an inherited CSS property, so setting it only on the
|
|
537
|
+
* `[data-pw-surface]` root would leave every descendant at the browser
|
|
538
|
+
* default `content-box`. Under `content-box`, a block declared `width:100%`
|
|
539
|
+
* with padding (e.g. `layout/section` padding:80) computes its border-box
|
|
540
|
+
* width as `100% + padding` and overflows its parent → horizontal scroll.
|
|
541
|
+
* A descendant selector (`[data-pw-surface] *`) is therefore required so
|
|
542
|
+
* every block inside the surface respects its declared width.
|
|
543
|
+
*
|
|
544
|
+
* The reset is scoped to the `[data-pw-surface]` subtree (root + descendants
|
|
545
|
+
* + their generated `::before`/`::after` boxes) rather than emitted as a
|
|
546
|
+
* global `*{}` rule so it cannot leak into and reset a host page that embeds
|
|
547
|
+
* the surface. This makes the standard ship from the runtime itself — a host
|
|
548
|
+
* app, the embed bundle, and a preview tool all inherit it for free.
|
|
549
|
+
*/
|
|
550
|
+
const SURFACE_BASE_CSS = '[data-pw-surface],' +
|
|
551
|
+
'[data-pw-surface] *,' +
|
|
552
|
+
'[data-pw-surface] *::before,' +
|
|
553
|
+
'[data-pw-surface] *::after{box-sizing:border-box}' +
|
|
554
|
+
// Horizontal-overflow guard. Reference pages protect themselves with
|
|
555
|
+
// body{overflow-x:hidden}; a surface must not rely on its host for that.
|
|
556
|
+
// `clip` (not `hidden`) so no scroll container is created and position:sticky
|
|
557
|
+
// descendants keep working.
|
|
558
|
+
'[data-pw-surface]{overflow-x:clip}' +
|
|
559
|
+
// Surface root paints the THEME ground (light/dark). Without this the
|
|
560
|
+
// root stays transparent and content OUTSIDE a card (a bare heading/section)
|
|
561
|
+
// shows the host page's own background — fine in light mode, but in dark mode
|
|
562
|
+
// (derived dark vars) it left light text on a white page → unreadable. Painting
|
|
563
|
+
// background+color from the tokens makes the whole surface follow the mode
|
|
564
|
+
// (the dark <style> block re-resolves --pw-color-* under [data-pw-theme]/@media).
|
|
565
|
+
// Scoped to the root only (not descendants) so blocks still set their own.
|
|
566
|
+
'[data-pw-surface]{background:var(--pw-color-background);color:var(--pw-color-text);' +
|
|
567
|
+
// The BODY FONT, bound on the root like the ground. Only the published
|
|
568
|
+
// theme.css bound it (on html,body), so a serif-bodied theme (rose) read
|
|
569
|
+
// serif on the published page and in the host font on the canvas and in an
|
|
570
|
+
// embed. Blocks inherit it; a block that sets its own font still wins.
|
|
571
|
+
// `inherit` keeps the host font when a theme declares none.
|
|
572
|
+
'font-family:var(--pw-font-family,inherit);font-size:var(--pw-font-size-base,inherit);' +
|
|
573
|
+
'line-height:var(--pw-line-height-body,inherit)}' +
|
|
574
|
+
// Controls take the surface font. Browsers give button, input,
|
|
575
|
+
// select and textarea their OWN system font instead of inheriting, so a
|
|
576
|
+
// themed page showed its body face everywhere except in its controls (a
|
|
577
|
+
// serif theme rendered sans-serif buttons). Family only: sizes and colours stay the
|
|
578
|
+
// block's. :where() keeps specificity at zero so a block's own font wins.
|
|
579
|
+
'[data-pw-surface] :where(button,input,select,textarea,optgroup){font-family:inherit}' +
|
|
580
|
+
// Centralised keyboard focus ring (WCAG 2.4.7/2.4.11) — every focusable element
|
|
581
|
+
// inside the surface gets the theme's --pw-focus-ring-* (with on-brand AA fallbacks)
|
|
582
|
+
// instead of each block hard-coding (or dropping) a ring. :where() = zero
|
|
583
|
+
// specificity so a block can still override. A tenant re-skin changes the ring in
|
|
584
|
+
// one place; it can never silently lose focus visibility.
|
|
585
|
+
'[data-pw-surface] :where(a,button,input,select,textarea,[tabindex]:not([tabindex="-1"])):focus-visible{' +
|
|
586
|
+
'outline:var(--pw-focus-ring-width,2px) solid var(--pw-focus-ring-color,#4f46e5);' +
|
|
587
|
+
'outline-offset:var(--pw-focus-ring-offset,2px)}' +
|
|
588
|
+
// Named animation keyframes shipped from the runtime so a block can drive
|
|
589
|
+
// motion via `block.style.animation: 'pw-pulse 1.5s ease-in-out infinite'` (or
|
|
590
|
+
// pw-marquee / pw-fade-in) WITHOUT each surface defining @keyframes (block.style
|
|
591
|
+
// can't). Names are pw-* to avoid host-page collision. Covers the soundwave-pulse
|
|
592
|
+
// / logo-marquee patterns that previously had no offline-capable expression.
|
|
593
|
+
'@keyframes pw-pulse{0%,100%{opacity:1}50%{opacity:.45}}' +
|
|
594
|
+
'@keyframes pw-fade-in{from{opacity:0}to{opacity:1}}' +
|
|
595
|
+
'@keyframes pw-marquee{from{transform:translateX(0)}to{transform:translateX(-50%)}}' +
|
|
596
|
+
// Batch-1 animation library — entrance/loop motion blocks can drive via
|
|
597
|
+
// block.style.animation without defining @keyframes.
|
|
598
|
+
'@keyframes pw-slide-up{from{transform:translateY(8px);opacity:0}to{transform:translateY(0);opacity:1}}' +
|
|
599
|
+
'@keyframes pw-slide-down{from{transform:translateY(-8px);opacity:0}to{transform:translateY(0);opacity:1}}' +
|
|
600
|
+
'@keyframes pw-scale-in{from{transform:scale(.96);opacity:0}to{transform:scale(1);opacity:1}}' +
|
|
601
|
+
'@keyframes pw-spin{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}' +
|
|
602
|
+
'@keyframes pw-bounce{0%,100%{transform:translateY(0)}50%{transform:translateY(-25%)}}' +
|
|
603
|
+
// Spring ENTRANCE (Dalga 1, core (d)). pw-bounce is a vertical loop and the
|
|
604
|
+
// theme's --pw-easing-bounce token had no entrance keyframe to ride on, so
|
|
605
|
+
// delivered apps reached for pw-pulse. The overshoot is the keyframe's own
|
|
606
|
+
// timing function: `animation: pw-spring-in .4s` springs with no easing
|
|
607
|
+
// named, a theme re-tunes it through the token, and the fallback is the
|
|
608
|
+
// token's canonical default (surface-theme.ts). Reduced-motion gated below.
|
|
609
|
+
'@keyframes pw-spring-in{from{transform:scale(.9);opacity:0;' +
|
|
610
|
+
'animation-timing-function:var(--pw-easing-bounce,cubic-bezier(0.34, 1.56, 0.64, 1))}' +
|
|
611
|
+
'to{transform:scale(1);opacity:1}}' +
|
|
612
|
+
// Interaction-state transitions (theme motion tokens): every interactive
|
|
613
|
+
// element eases its colour/elevation changes instead of snapping — the
|
|
614
|
+
// micro-polish half of hover/focus/pressed feedback that inline block
|
|
615
|
+
// styles cannot express (pseudo-states need a stylesheet). :where() keeps
|
|
616
|
+
// specificity at zero so any block's own transition wins; the property
|
|
617
|
+
// list is colour/elevation-only (no `all`) so layout-affecting properties
|
|
618
|
+
// never animate by accident. Durations/easing come from the theme's motion
|
|
619
|
+
// tokens with the old instant behaviour recoverable per-theme by setting
|
|
620
|
+
// them to 0ms. The reduced-motion rule below already neutralises these.
|
|
621
|
+
'[data-pw-surface] :where(a,button,input,select,textarea,[role="button"]):not(:disabled){' +
|
|
622
|
+
'transition:background-color var(--pw-duration-fast,150ms) var(--pw-easing,ease),' +
|
|
623
|
+
'color var(--pw-duration-fast,150ms) var(--pw-easing,ease),' +
|
|
624
|
+
'border-color var(--pw-duration-fast,150ms) var(--pw-easing,ease),' +
|
|
625
|
+
'box-shadow var(--pw-duration-fast,150ms) var(--pw-easing,ease),' +
|
|
626
|
+
'opacity var(--pw-duration-fast,150ms) var(--pw-easing,ease),' +
|
|
627
|
+
'filter var(--pw-duration-fast,150ms) var(--pw-easing,ease)}' +
|
|
628
|
+
// Honour reduced-motion: neutralise pw-* animations + transitions for users who
|
|
629
|
+
// opt out (a11y). Covers pseudo-elements too.
|
|
630
|
+
'@media (prefers-reduced-motion:reduce){[data-pw-surface] *,[data-pw-surface] *::before,[data-pw-surface] *::after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}}' +
|
|
631
|
+
// iOS safe-area insets. Host apps already set `viewport-fit=cover`, so
|
|
632
|
+
// env(safe-area-inset-*) is live but nothing consumed it → on notch /
|
|
633
|
+
// Dynamic-Island / home-indicator devices sticky navbars tuck under the notch
|
|
634
|
+
// and trailing footers/bottom bars sit under the home indicator. These rules
|
|
635
|
+
// make EVERY published surface (embed, preview, host app) inset-aware at a
|
|
636
|
+
// single point. The surface root takes the horizontal insets; navbar/footer
|
|
637
|
+
// block roots (which emit data-block-type) take the vertical ones. max(N,env)
|
|
638
|
+
// keeps each ≥ its normal value when no inset exists (env resolves to 0px),
|
|
639
|
+
// so a static mid-page navbar only ever gains pad on an actual inset device.
|
|
640
|
+
'[data-pw-surface]{' +
|
|
641
|
+
'padding-left:max(0px,env(safe-area-inset-left));' +
|
|
642
|
+
'padding-right:max(0px,env(safe-area-inset-right))}' +
|
|
643
|
+
'[data-pw-surface] [data-block-type="navbar"]{' +
|
|
644
|
+
'padding-top:max(12px,env(safe-area-inset-top))}' +
|
|
645
|
+
'[data-pw-surface] [data-block-type="footer"]{' +
|
|
646
|
+
'padding-bottom:max(48px,env(safe-area-inset-bottom))}';
|
|
647
|
+
// ============================================================
|
|
648
|
+
// SURFACE RENDERER COMPONENT
|
|
649
|
+
// ============================================================
|
|
650
|
+
/**
|
|
651
|
+
* SurfaceRenderer component
|
|
652
|
+
*
|
|
653
|
+
* Renders a surface definition to React components. surface
|
|
654
|
+
* definitions come from the pack-derived registry (the host's installed
|
|
655
|
+
* blocks and receipts) rather than hardcoded starter exports.
|
|
656
|
+
*/
|
|
657
|
+
export function SurfaceRenderer({ surface, capabilities = [], theme = 'light', projectTheme, projectDarkTheme, basePath = '', onEvent, components: customComponents, className, viewport: viewportProp, gap, padding, imageAdapter, entityFacade, connections, renderBlockWrapper, interactionMode = 'live', }) {
|
|
658
|
+
// Use explicit viewport or auto-detect
|
|
659
|
+
const autoViewport = useViewport();
|
|
660
|
+
const viewport = viewportProp ?? autoViewport;
|
|
661
|
+
// `onEvent` is a pure notification — it ALWAYS flows, even in inert mode, so
|
|
662
|
+
// the host (editor/preview) can observe interactions and give feedback. The
|
|
663
|
+
// `interactionMode` flag (threaded into BlockContext below) is what gates real
|
|
664
|
+
// side-effects: blocks must skip navigation / data writes when `inert`.
|
|
665
|
+
const effectiveOnEvent = onEvent;
|
|
666
|
+
// Canonical bus → renderer `onEvent` bridge. The `BehaviorSurfaceProvider`
|
|
667
|
+
// subscribes this to the bus' global `*` wildcard, so every block-emitted
|
|
668
|
+
// `SurfaceEventDescriptor` (contracts vocabulary: namespaced `type` string +
|
|
669
|
+
// `surfaceId`/`payload`/`timestamp`) reaches the renderer's `onEvent` handler
|
|
670
|
+
// — i.e. `DataBoundSurfaceRenderer.handleEvent`, which dispatches a form's
|
|
671
|
+
// `payload.action` setState and a block's `action` prop. The handler consumes
|
|
672
|
+
// the legacy `SurfaceEvent` shape (`{type, blockId, payload}`), so we project
|
|
673
|
+
// the descriptor onto it: the canonical namespaced type maps to its legacy
|
|
674
|
+
// equivalent, with anything else collapsing to `'custom'` (blockId/payload pass
|
|
675
|
+
// through unchanged — that is all the dispatcher reads).
|
|
676
|
+
const busOnEvent = React.useMemo(() => {
|
|
677
|
+
if (!effectiveOnEvent)
|
|
678
|
+
return undefined;
|
|
679
|
+
return (event) => {
|
|
680
|
+
// Surface lifecycle events (`surface:load`/`surface:unload`) are bus-internal
|
|
681
|
+
// and were never part of the legacy `onEvent` host/dispatcher contract — only
|
|
682
|
+
// block interactions flow to the renderer's handler. Skip them so the host
|
|
683
|
+
// callback (and the DataBound dispatcher) see exactly the block-level events
|
|
684
|
+
// they saw under the retired `context.onEvent` path.
|
|
685
|
+
if (event.type.startsWith('surface:'))
|
|
686
|
+
return;
|
|
687
|
+
effectiveOnEvent({
|
|
688
|
+
type: mapCanonicalEventType(event.type),
|
|
689
|
+
blockId: event.blockId ?? '',
|
|
690
|
+
...(event.payload !== undefined ? { payload: event.payload } : {}),
|
|
691
|
+
canonicalType: event.type,
|
|
692
|
+
});
|
|
693
|
+
};
|
|
694
|
+
}, [effectiveOnEvent]);
|
|
695
|
+
// Build capability map
|
|
696
|
+
const capabilityMap = React.useMemo(() => {
|
|
697
|
+
const map = new Map();
|
|
698
|
+
for (const cap of capabilities) {
|
|
699
|
+
map.set(cap.type, cap);
|
|
700
|
+
}
|
|
701
|
+
return map;
|
|
702
|
+
}, [capabilities]);
|
|
703
|
+
// Build block context (theme removed from BlockContext)
|
|
704
|
+
const context = React.useMemo(() => ({
|
|
705
|
+
capabilities: capabilityMap,
|
|
706
|
+
basePath,
|
|
707
|
+
onEvent: effectiveOnEvent,
|
|
708
|
+
imageAdapter,
|
|
709
|
+
viewport,
|
|
710
|
+
interactionMode,
|
|
711
|
+
}), [capabilityMap, basePath, effectiveOnEvent, imageAdapter, viewport, interactionMode]);
|
|
712
|
+
// Create image component override if adapter is provided
|
|
713
|
+
const imageComponentOverride = React.useMemo(() => {
|
|
714
|
+
if (!imageAdapter)
|
|
715
|
+
return undefined;
|
|
716
|
+
return { image: createImageBlockWithAdapter(imageAdapter) };
|
|
717
|
+
}, [imageAdapter]);
|
|
718
|
+
// Merge component registries (image adapter takes precedence over custom components)
|
|
719
|
+
// `defaultBlockComponents` removed; `customComponents`
|
|
720
|
+
// is now the only static seed for the registry alongside the image override.
|
|
721
|
+
const componentRegistry = React.useMemo(() => mergeComponentRegistries(customComponents, imageComponentOverride), [customComponents, imageComponentOverride]);
|
|
722
|
+
// Read entitlements from context
|
|
723
|
+
const { isBlockEntitled, loaded: entitlementsLoaded } = useEntitlements();
|
|
724
|
+
const entitlementCheck = entitlementsLoaded ? isBlockEntitled : undefined;
|
|
725
|
+
// Read dynamic block descriptors
|
|
726
|
+
const { blocks: dynamicBlockMap } = useDynamicBlocks();
|
|
727
|
+
// The root FLOWS; the measure lives in an INNER container.
|
|
728
|
+
//
|
|
729
|
+
// If the root carried grid + `maxWidth` + `margin:0 auto`, persistent
|
|
730
|
+
// navigation chrome would sit inside the centred box too (at 1536px a rail
|
|
731
|
+
// would start 218px in from the screen edge). The rule: chrome belongs to
|
|
732
|
+
// the viewport, the body to the measure.
|
|
733
|
+
//
|
|
734
|
+
// Gap/padding come from CSS variables so the `surface.theme.layout` tokens
|
|
735
|
+
// take effect; the variables are set on this element through `themeVars`.
|
|
736
|
+
const rootLayoutStyle = resolveSurfaceRootStyle(gap ?? 'var(--pw-layout-surface-gap)', 0);
|
|
737
|
+
const measureStyle = resolveMeasureStyle(viewport, gap ?? 'var(--pw-layout-surface-gap)', padding ?? 'var(--pw-layout-surface-padding)');
|
|
738
|
+
// Resolve appearance atom (theme + layout + styleVariables unified).
|
|
739
|
+
// Theme tokens + custom CSS vars both live inside surface.appearance.config.
|
|
740
|
+
const appearanceVars = React.useMemo(() => resolveAppearanceVars(surface.appearance, projectTheme), [surface.appearance, projectTheme]);
|
|
741
|
+
const rootStyle = React.useMemo(() => ({
|
|
742
|
+
...rootLayoutStyle,
|
|
743
|
+
...appearanceVars,
|
|
744
|
+
}), [rootLayoutStyle, appearanceVars]);
|
|
745
|
+
// Surface-scope key for every emitted <style> rule. The embed autoloader
|
|
746
|
+
// hydrates EVERY [data-pw-surface] element in a document, so a host page can
|
|
747
|
+
// hold several surfaces at once; scoping by the ATTRIBUTE VALUE keeps each
|
|
748
|
+
// surface's rules off its siblings. Undefined only when a surface has no id
|
|
749
|
+
// (isolated test renders), where the historical value-less selector is fine.
|
|
750
|
+
const scopeId = surface.id || undefined;
|
|
751
|
+
// Per-breakpoint visibility (hideOn/showOn). Media queries can't be
|
|
752
|
+
// expressed inline, so the runtime emits scoped CSS rules — keyed by each
|
|
753
|
+
// block's `data-block-id` — into the surface stylesheet (alongside the
|
|
754
|
+
// box-sizing reset). Empty string when no block uses hideOn/showOn → existing
|
|
755
|
+
// surfaces emit zero extra bytes.
|
|
756
|
+
const visibilityCss = React.useMemo(() => collectVisibilityCss(surface.blocks, scopeId), [surface.blocks, scopeId]);
|
|
757
|
+
// Interaction-state styling (block.states). Pseudo-classes (:hover/:active/
|
|
758
|
+
// :focus-visible) can't be expressed inline, so the runtime emits scoped CSS
|
|
759
|
+
// rules — keyed by each block's `data-block-id` — alongside the visibility CSS.
|
|
760
|
+
// Empty string when no block uses `states` → existing surfaces emit zero bytes.
|
|
761
|
+
const statesCss = React.useMemo(() => collectStatesCss(surface.blocks, scopeId), [surface.blocks, scopeId]);
|
|
762
|
+
// Conditional-state styling (block.conditionalStates) — aria/data/disabled/
|
|
763
|
+
// checked/:has variants. Attribute selectors and these pseudo-classes can't be
|
|
764
|
+
// expressed inline, so the runtime emits scoped `[data-block-id]<selector>`
|
|
765
|
+
// rules (with `!important` to beat component inline defaults). Empty string
|
|
766
|
+
// when no block uses `conditionalStates` → existing surfaces emit zero bytes.
|
|
767
|
+
const conditionalStatesCss = React.useMemo(() => collectConditionalStatesCss(surface.blocks, scopeId), [surface.blocks, scopeId]);
|
|
768
|
+
// Per-prop responsive styles (block.style values that vary by breakpoint).
|
|
769
|
+
// The base (xs) value is applied inline by the renderer (flattenResponsiveStyle);
|
|
770
|
+
// the larger breakpoints are emitted here as scoped min-width media-query rules
|
|
771
|
+
// keyed by each block's `data-block-id`. Empty string when no block uses a
|
|
772
|
+
// responsive style value → existing surfaces emit zero bytes.
|
|
773
|
+
const responsiveStyleCss = React.useMemo(() => collectResponsiveStyleCss(surface.blocks, scopeId), [surface.blocks, scopeId]);
|
|
774
|
+
// Group-hover styling (block.groupRoot + descendant block.groupStates.hover) —
|
|
775
|
+
// a parent marked groupRoot, on :hover, restyles descendants declaring
|
|
776
|
+
// groupStates. The ancestor `:hover` + descendant relationship can't be
|
|
777
|
+
// expressed inline, so the runtime emits scoped
|
|
778
|
+
// `[data-block-id="<root>"]:hover [data-block-id="<desc>"]{…}` rules keyed by
|
|
779
|
+
// both block ids. Empty string when no block uses groupStates under a
|
|
780
|
+
// groupRoot → existing surfaces emit zero bytes.
|
|
781
|
+
const groupStatesCss = React.useMemo(() => collectGroupStatesCss(surface.blocks, scopeId), [surface.blocks, scopeId]);
|
|
782
|
+
// Dark theme variant (appearance.config.darkTheme). Light vars stay inline on
|
|
783
|
+
// the root; the dark vars override under BOTH `[data-pw-theme="dark"]` (manual
|
|
784
|
+
// force) AND `@media (prefers-color-scheme:dark)` (auto). Empty string when no
|
|
785
|
+
// darkTheme is declared.
|
|
786
|
+
const darkThemeCss = React.useMemo(() => {
|
|
787
|
+
// Same `projectTheme` middle layer the light vars above resolve with —
|
|
788
|
+
// otherwise a project-themed page is on-brand in light and platform-default
|
|
789
|
+
// in dark.
|
|
790
|
+
const darkVars = resolveAppearanceDarkVars(surface.appearance, projectTheme, projectDarkTheme);
|
|
791
|
+
// `appearance.config.mode.policy` decides whether the dark rule
|
|
792
|
+
// exists at all (light), always applies (dark) or follows the device.
|
|
793
|
+
return buildDarkThemeCss(darkVars, scopeId, resolveAppearanceMode(surface.appearance?.config).policy);
|
|
794
|
+
}, [surface.appearance, projectTheme, projectDarkTheme, scopeId]);
|
|
795
|
+
// Surface keyframe registry (appearance.config.keyframes) — arbitrary named
|
|
796
|
+
// @keyframes a block can reference via `block.style.animation: 'pw-kf-<name> …'`
|
|
797
|
+
// (each declared name is prefixed `pw-kf-`). Empty string when no registry is
|
|
798
|
+
// declared. The prefers-reduced-motion gate in SURFACE_BASE_CSS neutralises
|
|
799
|
+
// these for users who opt out (it matches by animation, not keyframe name).
|
|
800
|
+
const keyframesCss = React.useMemo(() => buildKeyframesCss(resolveAppearanceKeyframes(surface.appearance)), [surface.appearance]);
|
|
801
|
+
// extract form→entity bridge configs from surface capabilities
|
|
802
|
+
const entityFormBindings = React.useMemo(() => {
|
|
803
|
+
// NO LONGER GATED ON THE ENTITY FACADE. A form now writes to a SOURCE, and
|
|
804
|
+
// a rest-backed source has no facade behind it — requiring one here meant
|
|
805
|
+
// a form pointed at anyone else's endpoint silently never attached.
|
|
806
|
+
if (!surface.capabilities?.length)
|
|
807
|
+
return undefined;
|
|
808
|
+
const entityCap = surface.capabilities.find((c) => isCapability(c, 'platform/entities'));
|
|
809
|
+
const formBindings = entityCap?.config?.formBindings;
|
|
810
|
+
if (!Array.isArray(formBindings) || formBindings.length === 0)
|
|
811
|
+
return undefined;
|
|
812
|
+
return formBindings;
|
|
813
|
+
}, [surface.capabilities]);
|
|
814
|
+
/**
|
|
815
|
+
* The read sources, so a form binding's `sourceId` can be resolved.
|
|
816
|
+
*
|
|
817
|
+
* The same declaration the persistence capability already carries — a form
|
|
818
|
+
* writes to something the page also knows how to read, which is what makes
|
|
819
|
+
* "refresh after write" meaningful.
|
|
820
|
+
*/
|
|
821
|
+
const formWriteSources = React.useMemo(() => {
|
|
822
|
+
const cap = surface.capabilities?.find((c) => isCapability(c, 'platform/persistence'));
|
|
823
|
+
const sources = cap?.config?.dataSources;
|
|
824
|
+
return Array.isArray(sources) && sources.length > 0
|
|
825
|
+
? sources
|
|
826
|
+
: undefined;
|
|
827
|
+
}, [surface.capabilities]);
|
|
828
|
+
// Render blocks — in scope groups, keeping source order.
|
|
829
|
+
//
|
|
830
|
+
// `container` groups are wrapped in the measured container; `viewport`
|
|
831
|
+
// groups become DIRECT children of the root. The escape is PLACEMENT, not a
|
|
832
|
+
// CSS trick: `width:100vw` counts the scrollbar too and would produce ~15px
|
|
833
|
+
// of horizontal overflow on every page with a vertical scrollbar.
|
|
834
|
+
const renderOne = (block) => renderBlock({
|
|
835
|
+
block,
|
|
836
|
+
context,
|
|
837
|
+
components: componentRegistry,
|
|
838
|
+
}, undefined, entitlementCheck, dynamicBlockMap, renderBlockWrapper);
|
|
839
|
+
const renderedBlocks = chunkByWidthScope(surface.blocks, viewport).map((chunk, i) => chunk.scope === 'viewport' ? (_jsx(React.Fragment, { children: chunk.blocks.map(renderOne) }, `pw-vp-${i}`)) : (_jsx("div", { "data-pw-measure": "", style: measureStyle, children: chunk.blocks.map(renderOne) }, `pw-ct-${i}`)));
|
|
840
|
+
return (
|
|
841
|
+
// SurfaceConnectionsContext carries the connections
|
|
842
|
+
// context (world connections + engine registry) to blocks on the
|
|
843
|
+
// engine-bridge path. null (host didn't opt in) = today's behavior.
|
|
844
|
+
_jsx(SurfaceConnectionsContext.Provider, { value: connections ?? null, children: _jsx(EntityFacadeContext.Provider, { value: entityFacade ?? null, children: _jsx(BehaviorSurfaceProvider, { surfaceId: surface.id ?? '', ...(entityFormBindings ? { entityFormBindings } : {}), ...(formWriteSources ? { dataSources: formWriteSources } : {}), ...(busOnEvent ? { onEvent: busOnEvent } : {}), blocks: surface.blocks, children: _jsx(ShellSelectionProvider, { syncUrl: interactionMode !== 'inert', children: _jsxs("div", { className: className, style: rootStyle, "data-pw-surface": scopeId ?? '', "data-surface-renderer": true, "data-theme": theme, "data-viewport": viewport, children: [_jsx("style", { "data-pw-base-css": true, children: SURFACE_BASE_CSS }), visibilityCss ? (_jsx("style", { "data-pw-visibility-css": true, children: visibilityCss })) : null, statesCss ? _jsx("style", { "data-pw-states-css": true, children: statesCss }) : null, conditionalStatesCss ? (_jsx("style", { "data-pw-conditional-states-css": true, children: conditionalStatesCss })) : null, responsiveStyleCss ? (_jsx("style", { "data-pw-responsive-style-css": true, children: responsiveStyleCss })) : null, groupStatesCss ? (_jsx("style", { "data-pw-group-states-css": true, children: groupStatesCss })) : null, darkThemeCss ? _jsx("style", { "data-pw-dark-theme-css": true, children: darkThemeCss }) : null, keyframesCss ? _jsx("style", { "data-pw-keyframes-css": true, children: keyframesCss }) : null, renderedBlocks] }) }) }) }) }));
|
|
845
|
+
}
|
|
846
|
+
// ============================================================
|
|
847
|
+
// BLOCK RENDERER (STANDALONE)
|
|
848
|
+
// ============================================================
|
|
849
|
+
/**
|
|
850
|
+
* Standalone block renderer
|
|
851
|
+
*
|
|
852
|
+
* Renders a single block without a surface wrapper.
|
|
853
|
+
*/
|
|
854
|
+
export function BlockRenderer({ block, capabilities = [], theme: _theme = 'light', basePath = '', onEvent, components: customComponents, imageAdapter, }) {
|
|
855
|
+
// Build capability map
|
|
856
|
+
const capabilityMap = React.useMemo(() => {
|
|
857
|
+
const map = new Map();
|
|
858
|
+
for (const cap of capabilities) {
|
|
859
|
+
map.set(cap.type, cap);
|
|
860
|
+
}
|
|
861
|
+
return map;
|
|
862
|
+
}, [capabilities]);
|
|
863
|
+
// Build block context (theme removed from BlockContext)
|
|
864
|
+
const context = React.useMemo(() => ({
|
|
865
|
+
capabilities: capabilityMap,
|
|
866
|
+
basePath,
|
|
867
|
+
onEvent,
|
|
868
|
+
imageAdapter,
|
|
869
|
+
}), [capabilityMap, basePath, onEvent, imageAdapter]);
|
|
870
|
+
// Create image component override if adapter is provided
|
|
871
|
+
const imageComponentOverride = React.useMemo(() => {
|
|
872
|
+
if (!imageAdapter)
|
|
873
|
+
return undefined;
|
|
874
|
+
return { image: createImageBlockWithAdapter(imageAdapter) };
|
|
875
|
+
}, [imageAdapter]);
|
|
876
|
+
// Merge component registries
|
|
877
|
+
// `defaultBlockComponents` retired; `customComponents`
|
|
878
|
+
// is the only static seed (defaulted to {}) alongside the image override.
|
|
879
|
+
const componentRegistry = React.useMemo(() => mergeComponentRegistries(customComponents, imageComponentOverride), [customComponents, imageComponentOverride]);
|
|
880
|
+
return renderBlock({ block, context, components: componentRegistry });
|
|
881
|
+
}
|
|
882
|
+
// ============================================================
|
|
883
|
+
// UTILITY FUNCTIONS
|
|
884
|
+
// ============================================================
|
|
885
|
+
/**
|
|
886
|
+
* Get all block IDs from a surface
|
|
887
|
+
*/
|
|
888
|
+
export function getBlockIds(blocks) {
|
|
889
|
+
const ids = [];
|
|
890
|
+
function collectIds(block) {
|
|
891
|
+
ids.push(block.id);
|
|
892
|
+
if (block.children) {
|
|
893
|
+
for (const child of block.children) {
|
|
894
|
+
collectIds(child);
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
for (const block of blocks) {
|
|
899
|
+
collectIds(block);
|
|
900
|
+
}
|
|
901
|
+
return ids;
|
|
902
|
+
}
|
|
903
|
+
/**
|
|
904
|
+
* Find a block by ID in a surface
|
|
905
|
+
*/
|
|
906
|
+
export function findBlockById(blocks, id) {
|
|
907
|
+
for (const block of blocks) {
|
|
908
|
+
if (block.id === id) {
|
|
909
|
+
return block;
|
|
910
|
+
}
|
|
911
|
+
if (block.children) {
|
|
912
|
+
const found = findBlockById(block.children, id);
|
|
913
|
+
if (found) {
|
|
914
|
+
return found;
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
return undefined;
|
|
919
|
+
}
|
|
920
|
+
/**
|
|
921
|
+
* Update a block by ID in a surface
|
|
922
|
+
*/
|
|
923
|
+
export function updateBlockById(blocks, id, updates) {
|
|
924
|
+
return blocks.map((block) => {
|
|
925
|
+
if (block.id === id) {
|
|
926
|
+
return { ...block, ...updates };
|
|
927
|
+
}
|
|
928
|
+
if (block.children) {
|
|
929
|
+
return {
|
|
930
|
+
...block,
|
|
931
|
+
children: updateBlockById(block.children, id, updates),
|
|
932
|
+
};
|
|
933
|
+
}
|
|
934
|
+
return block;
|
|
935
|
+
});
|
|
936
|
+
}
|
|
937
|
+
//# sourceMappingURL=renderer.js.map
|