@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,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-prop responsive style CSS generation (block.style values vary by breakpoint).
|
|
3
|
+
*
|
|
4
|
+
* `block.style` sits inline and can express a single value per property; it
|
|
5
|
+
* cannot express media queries. A style prop whose value is a
|
|
6
|
+
* `ResponsiveValue<string>` map (`{ xs?, sm?, md?, lg?, xl?, '2xl'? }`) instead
|
|
7
|
+
* of a flat string declares a value that varies per breakpoint. The renderer
|
|
8
|
+
* flattens the base (`xs` / smallest defined) value inline so SSR / no-JS get a
|
|
9
|
+
* sane default; this collector emits the LARGER-breakpoint values as scoped,
|
|
10
|
+
* mobile-first `min-width` media-query rules — keyed by the block's
|
|
11
|
+
* `data-block-id` — that override upward. Same mechanism `layout.hideOn`/
|
|
12
|
+
* `showOn` (visibility-css) and `block.states` (states-css) use.
|
|
13
|
+
*
|
|
14
|
+
* For a block with `style.padding = { xs:'8px', md:'24px', lg:'48px' }`:
|
|
15
|
+
*
|
|
16
|
+
* `@media (min-width:768px){[data-pw-surface] [data-block-id="<id>"]{padding:24px}}`
|
|
17
|
+
* `@media (min-width:1024px){[data-pw-surface] [data-block-id="<id>"]{padding:48px}}`
|
|
18
|
+
*
|
|
19
|
+
* The `xs` (base) value is NOT emitted here — it is rendered inline by the
|
|
20
|
+
* renderer. Flat string values emit zero bytes (the inline path owns them).
|
|
21
|
+
* Blocks without any responsive style value emit zero bytes so existing
|
|
22
|
+
* surfaces are byte-identical.
|
|
23
|
+
*
|
|
24
|
+
* @module @pwfabric/runtime/render/responsive-style-css
|
|
25
|
+
*/
|
|
26
|
+
import type { Block } from '@pwfabric/core';
|
|
27
|
+
/**
|
|
28
|
+
* Emit the scoped per-breakpoint media-query CSS for a single block's
|
|
29
|
+
* responsive style props. Targets `[data-block-id="<id>"]` scoped under the
|
|
30
|
+
* surface root so it cannot leak to the host page. Only breakpoints LARGER than
|
|
31
|
+
* `xs` are emitted (the `xs`/base value is inline); each emitted breakpoint key
|
|
32
|
+
* becomes a `@media (min-width:<px>)` rule. Returns '' when the block declares
|
|
33
|
+
* no responsive style value.
|
|
34
|
+
*/
|
|
35
|
+
export declare function blockResponsiveStyleCss(blockId: string, style: NonNullable<Block['style']>, surfaceId?: string): string;
|
|
36
|
+
/**
|
|
37
|
+
* Walk a surface block tree and concatenate the per-block responsive style CSS
|
|
38
|
+
* for every block declaring a `ResponsiveValue` style prop. Blocks with only
|
|
39
|
+
* flat string style values (or no style) are skipped (zero bytes emitted) so
|
|
40
|
+
* existing surfaces are byte-identical.
|
|
41
|
+
*/
|
|
42
|
+
export declare function collectResponsiveStyleCss(blocks: readonly Block[], surfaceId?: string): string;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-prop responsive style CSS generation (block.style values vary by breakpoint).
|
|
3
|
+
*
|
|
4
|
+
* `block.style` sits inline and can express a single value per property; it
|
|
5
|
+
* cannot express media queries. A style prop whose value is a
|
|
6
|
+
* `ResponsiveValue<string>` map (`{ xs?, sm?, md?, lg?, xl?, '2xl'? }`) instead
|
|
7
|
+
* of a flat string declares a value that varies per breakpoint. The renderer
|
|
8
|
+
* flattens the base (`xs` / smallest defined) value inline so SSR / no-JS get a
|
|
9
|
+
* sane default; this collector emits the LARGER-breakpoint values as scoped,
|
|
10
|
+
* mobile-first `min-width` media-query rules — keyed by the block's
|
|
11
|
+
* `data-block-id` — that override upward. Same mechanism `layout.hideOn`/
|
|
12
|
+
* `showOn` (visibility-css) and `block.states` (states-css) use.
|
|
13
|
+
*
|
|
14
|
+
* For a block with `style.padding = { xs:'8px', md:'24px', lg:'48px' }`:
|
|
15
|
+
*
|
|
16
|
+
* `@media (min-width:768px){[data-pw-surface] [data-block-id="<id>"]{padding:24px}}`
|
|
17
|
+
* `@media (min-width:1024px){[data-pw-surface] [data-block-id="<id>"]{padding:48px}}`
|
|
18
|
+
*
|
|
19
|
+
* The `xs` (base) value is NOT emitted here — it is rendered inline by the
|
|
20
|
+
* renderer. Flat string values emit zero bytes (the inline path owns them).
|
|
21
|
+
* Blocks without any responsive style value emit zero bytes so existing
|
|
22
|
+
* surfaces are byte-identical.
|
|
23
|
+
*
|
|
24
|
+
* @module @pwfabric/runtime/render/responsive-style-css
|
|
25
|
+
*/
|
|
26
|
+
import { BREAKPOINTS, BREAKPOINT_ORDER } from '@pwfabric/core';
|
|
27
|
+
import { blockSelector, camelToKebab, collectScopedCss, LAYOUT_HOIST_KEYS, WRAPPER_ATTR } from './scoped-css.js';
|
|
28
|
+
/** Breakpoint name → `minWidth` px (same thresholds the span/visibility systems use). */
|
|
29
|
+
function minWidthFor(bp) {
|
|
30
|
+
return BREAKPOINTS.find((b) => b.name === bp)?.minWidth ?? 0;
|
|
31
|
+
}
|
|
32
|
+
/** A style prop value is responsive when it is a non-null object (not a flat string). */
|
|
33
|
+
function isResponsiveValue(value) {
|
|
34
|
+
return typeof value === 'object' && value !== null;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Emit the scoped per-breakpoint media-query CSS for a single block's
|
|
38
|
+
* responsive style props. Targets `[data-block-id="<id>"]` scoped under the
|
|
39
|
+
* surface root so it cannot leak to the host page. Only breakpoints LARGER than
|
|
40
|
+
* `xs` are emitted (the `xs`/base value is inline); each emitted breakpoint key
|
|
41
|
+
* becomes a `@media (min-width:<px>)` rule. Returns '' when the block declares
|
|
42
|
+
* no responsive style value.
|
|
43
|
+
*/
|
|
44
|
+
export function blockResponsiveStyleCss(blockId, style, surfaceId) {
|
|
45
|
+
const selector = blockSelector(blockId, surfaceId);
|
|
46
|
+
// Group declarations by breakpoint so one prop's md value and another prop's
|
|
47
|
+
// md value share a single `@media` rule (mirrors how a mobile-first stylesheet
|
|
48
|
+
// would be authored). The base (smallest defined breakpoint per prop) is a
|
|
49
|
+
// NON-media rule so ALL breakpoints live in the stylesheet — never inline.
|
|
50
|
+
// (Inline always beats stylesheet rules incl. @media, so a flattened-inline
|
|
51
|
+
// base would pin the value and the larger @media overrides would never apply.)
|
|
52
|
+
// THE HOISTING RULE APPLIES ON THE SELECTOR SIDE TOO.
|
|
53
|
+
//
|
|
54
|
+
// `[data-block-id]` matches BOTH the wrapper AND the component root. For flat
|
|
55
|
+
// values the renderer hoists margin/maxWidth/flex to the wrapper and removes
|
|
56
|
+
// them from the root; for responsive values the value leaves the inline style
|
|
57
|
+
// and lands here, so without the same split here the rule would apply TWICE
|
|
58
|
+
// (`marginTop:'-40%'` → both -335px and -256px).
|
|
59
|
+
//
|
|
60
|
+
// Hence two sets: hoisted keys go ONLY to the wrapper, the rest ONLY to the
|
|
61
|
+
// component root.
|
|
62
|
+
const HOIST = new Set(LAYOUT_HOIST_KEYS);
|
|
63
|
+
const baseDecls = [];
|
|
64
|
+
const byBreakpoint = new Map();
|
|
65
|
+
const baseHoist = [];
|
|
66
|
+
const byBreakpointHoist = new Map();
|
|
67
|
+
for (const [prop, value] of Object.entries(style)) {
|
|
68
|
+
if (!isResponsiveValue(value))
|
|
69
|
+
continue; // flat string → inline path owns it
|
|
70
|
+
let baseTaken = false;
|
|
71
|
+
// BREAKPOINT_ORDER is mobile-first ascending → first defined key is the base.
|
|
72
|
+
for (const bp of BREAKPOINT_ORDER) {
|
|
73
|
+
const v = value[bp];
|
|
74
|
+
if (v === undefined)
|
|
75
|
+
continue;
|
|
76
|
+
// `!important`: a block COMPONENT often applies the same CSS property
|
|
77
|
+
// inline with its own default (e.g. the text block sets
|
|
78
|
+
// `font-size: var(--pw-font-size-base)`). Inline beats any stylesheet rule,
|
|
79
|
+
// so without `!important` the author's responsive value would be pinned to
|
|
80
|
+
// the component default. Scoped to `[data-block-id]`, this only overrides
|
|
81
|
+
// the one block's own responsive props. Base + larger are both `!important`
|
|
82
|
+
// at equal specificity → source order (larger emitted later) wins on match.
|
|
83
|
+
const hoisted = HOIST.has(prop);
|
|
84
|
+
const bag = hoisted ? baseHoist : baseDecls;
|
|
85
|
+
const map = hoisted ? byBreakpointHoist : byBreakpoint;
|
|
86
|
+
if (!baseTaken) {
|
|
87
|
+
bag.push(`${camelToKebab(prop)}:${v} !important`);
|
|
88
|
+
baseTaken = true;
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
const decls = map.get(bp) ?? [];
|
|
92
|
+
decls.push(`${camelToKebab(prop)}:${v} !important`);
|
|
93
|
+
map.set(bp, decls);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
let css = '';
|
|
98
|
+
// Base (non-media) first, then ascending breakpoint order so larger
|
|
99
|
+
// breakpoints (later in source) win the cascade at equal specificity.
|
|
100
|
+
const emit = (sel, base, map) => {
|
|
101
|
+
if (base.length > 0)
|
|
102
|
+
css += `${sel}{${base.join(';')}}`;
|
|
103
|
+
for (const bp of BREAKPOINT_ORDER) {
|
|
104
|
+
const decls = map.get(bp);
|
|
105
|
+
if (!decls || decls.length === 0)
|
|
106
|
+
continue;
|
|
107
|
+
css += `@media (min-width:${minWidthFor(bp)}px){${sel}{${decls.join(';')}}}`;
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
emit(`${selector}:not(${WRAPPER_ATTR})`, baseDecls, byBreakpoint);
|
|
111
|
+
emit(`${selector}${WRAPPER_ATTR}`, baseHoist, byBreakpointHoist);
|
|
112
|
+
return css;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Walk a surface block tree and concatenate the per-block responsive style CSS
|
|
116
|
+
* for every block declaring a `ResponsiveValue` style prop. Blocks with only
|
|
117
|
+
* flat string style values (or no style) are skipped (zero bytes emitted) so
|
|
118
|
+
* existing surfaces are byte-identical.
|
|
119
|
+
*/
|
|
120
|
+
export function collectResponsiveStyleCss(blocks, surfaceId) {
|
|
121
|
+
return collectScopedCss(blocks, (block) => block.style ? blockResponsiveStyleCss(block.id, block.style, surfaceId) : '');
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=responsive-style-css.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"responsive-style-css.js","sourceRoot":"","sources":["../../src/render/responsive-style-css.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Per-prop responsive style CSS generation (block.style values vary by breakpoint).\n *\n * `block.style` sits inline and can express a single value per property; it\n * cannot express media queries. A style prop whose value is a\n * `ResponsiveValue<string>` map (`{ xs?, sm?, md?, lg?, xl?, '2xl'? }`) instead\n * of a flat string declares a value that varies per breakpoint. The renderer\n * flattens the base (`xs` / smallest defined) value inline so SSR / no-JS get a\n * sane default; this collector emits the LARGER-breakpoint values as scoped,\n * mobile-first `min-width` media-query rules — keyed by the block's\n * `data-block-id` — that override upward. Same mechanism `layout.hideOn`/\n * `showOn` (visibility-css) and `block.states` (states-css) use.\n *\n * For a block with `style.padding = { xs:'8px', md:'24px', lg:'48px' }`:\n *\n * `@media (min-width:768px){[data-pw-surface] [data-block-id=\"<id>\"]{padding:24px}}`\n * `@media (min-width:1024px){[data-pw-surface] [data-block-id=\"<id>\"]{padding:48px}}`\n *\n * The `xs` (base) value is NOT emitted here — it is rendered inline by the\n * renderer. Flat string values emit zero bytes (the inline path owns them).\n * Blocks without any responsive style value emit zero bytes so existing\n * surfaces are byte-identical.\n *\n * @module @pwfabric/runtime/render/responsive-style-css\n */\n\nimport type { Block } from '@pwfabric/core'\nimport { BREAKPOINTS, BREAKPOINT_ORDER } from '@pwfabric/core'\nimport type { BreakpointName } from '@pwfabric/core'\nimport { blockSelector, camelToKebab, collectScopedCss, LAYOUT_HOIST_KEYS, WRAPPER_ATTR } from './scoped-css.js'\n\n/** Breakpoint name → `minWidth` px (same thresholds the span/visibility systems use). */\nfunction minWidthFor(bp: BreakpointName): number {\n return BREAKPOINTS.find((b) => b.name === bp)?.minWidth ?? 0\n}\n\n/** A per-breakpoint value map (the responsive form of a style prop value). */\ntype ResponsiveStyleValue = Partial<Record<BreakpointName, string>>\n\n/** A style prop value is responsive when it is a non-null object (not a flat string). */\nfunction isResponsiveValue(value: unknown): value is ResponsiveStyleValue {\n return typeof value === 'object' && value !== null\n}\n\n/**\n * Emit the scoped per-breakpoint media-query CSS for a single block's\n * responsive style props. Targets `[data-block-id=\"<id>\"]` scoped under the\n * surface root so it cannot leak to the host page. Only breakpoints LARGER than\n * `xs` are emitted (the `xs`/base value is inline); each emitted breakpoint key\n * becomes a `@media (min-width:<px>)` rule. Returns '' when the block declares\n * no responsive style value.\n */\nexport function blockResponsiveStyleCss(\n blockId: string,\n style: NonNullable<Block['style']>,\n surfaceId?: string\n): string {\n const selector = blockSelector(blockId, surfaceId)\n // Group declarations by breakpoint so one prop's md value and another prop's\n // md value share a single `@media` rule (mirrors how a mobile-first stylesheet\n // would be authored). The base (smallest defined breakpoint per prop) is a\n // NON-media rule so ALL breakpoints live in the stylesheet — never inline.\n // (Inline always beats stylesheet rules incl. @media, so a flattened-inline\n // base would pin the value and the larger @media overrides would never apply.)\n // THE HOISTING RULE APPLIES ON THE SELECTOR SIDE TOO.\n //\n // `[data-block-id]` matches BOTH the wrapper AND the component root. For flat\n // values the renderer hoists margin/maxWidth/flex to the wrapper and removes\n // them from the root; for responsive values the value leaves the inline style\n // and lands here, so without the same split here the rule would apply TWICE\n // (`marginTop:'-40%'` → both -335px and -256px).\n //\n // Hence two sets: hoisted keys go ONLY to the wrapper, the rest ONLY to the\n // component root.\n const HOIST = new Set<string>(LAYOUT_HOIST_KEYS)\n const baseDecls: string[] = []\n const byBreakpoint = new Map<BreakpointName, string[]>()\n const baseHoist: string[] = []\n const byBreakpointHoist = new Map<BreakpointName, string[]>()\n\n for (const [prop, value] of Object.entries(style)) {\n if (!isResponsiveValue(value)) continue // flat string → inline path owns it\n let baseTaken = false\n // BREAKPOINT_ORDER is mobile-first ascending → first defined key is the base.\n for (const bp of BREAKPOINT_ORDER) {\n const v = value[bp]\n if (v === undefined) continue\n // `!important`: a block COMPONENT often applies the same CSS property\n // inline with its own default (e.g. the text block sets\n // `font-size: var(--pw-font-size-base)`). Inline beats any stylesheet rule,\n // so without `!important` the author's responsive value would be pinned to\n // the component default. Scoped to `[data-block-id]`, this only overrides\n // the one block's own responsive props. Base + larger are both `!important`\n // at equal specificity → source order (larger emitted later) wins on match.\n const hoisted = HOIST.has(prop)\n const bag = hoisted ? baseHoist : baseDecls\n const map = hoisted ? byBreakpointHoist : byBreakpoint\n if (!baseTaken) {\n bag.push(`${camelToKebab(prop)}:${v} !important`)\n baseTaken = true\n } else {\n const decls = map.get(bp) ?? []\n decls.push(`${camelToKebab(prop)}:${v} !important`)\n map.set(bp, decls)\n }\n }\n }\n\n let css = ''\n // Base (non-media) first, then ascending breakpoint order so larger\n // breakpoints (later in source) win the cascade at equal specificity.\n const emit = (sel: string, base: string[], map: Map<BreakpointName, string[]>): void => {\n if (base.length > 0) css += `${sel}{${base.join(';')}}`\n for (const bp of BREAKPOINT_ORDER) {\n const decls = map.get(bp)\n if (!decls || decls.length === 0) continue\n css += `@media (min-width:${minWidthFor(bp)}px){${sel}{${decls.join(';')}}}`\n }\n }\n emit(`${selector}:not(${WRAPPER_ATTR})`, baseDecls, byBreakpoint)\n emit(`${selector}${WRAPPER_ATTR}`, baseHoist, byBreakpointHoist)\n return css\n}\n\n/**\n * Walk a surface block tree and concatenate the per-block responsive style CSS\n * for every block declaring a `ResponsiveValue` style prop. Blocks with only\n * flat string style values (or no style) are skipped (zero bytes emitted) so\n * existing surfaces are byte-identical.\n */\nexport function collectResponsiveStyleCss(blocks: readonly Block[], surfaceId?: string): string {\n return collectScopedCss(blocks, (block) =>\n block.style ? blockResponsiveStyleCss(block.id, block.style, surfaceId) : ''\n )\n}\n"]}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Responsive Rendering Utilities
|
|
3
|
+
*
|
|
4
|
+
* Viewport-aware layout resolution for runtime rendering.
|
|
5
|
+
* Re-exports pure functions from @pwfabric/core and provides
|
|
6
|
+
* React-specific viewport hook.
|
|
7
|
+
*
|
|
8
|
+
* @module @pwfabric/runtime/render/responsive
|
|
9
|
+
*/
|
|
10
|
+
import * as React from 'react';
|
|
11
|
+
import type { BlockLayout, BreakpointName } from '@pwfabric/core';
|
|
12
|
+
export { resolveResponsiveValue, resolveSpanAtBreakpoint, getColumnsAtBreakpoint, getBreakpointForWidth, } from '@pwfabric/core';
|
|
13
|
+
/**
|
|
14
|
+
* Resolve the complete block layout at a given viewport breakpoint.
|
|
15
|
+
* Returns span, offset, order, and hidden values.
|
|
16
|
+
*/
|
|
17
|
+
export declare function resolveBlockLayout(layout: BlockLayout, viewport: BreakpointName): {
|
|
18
|
+
span: number;
|
|
19
|
+
offset: number | undefined;
|
|
20
|
+
order: number | undefined;
|
|
21
|
+
hidden: boolean | undefined;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Surface root — carries NO MEASURE.
|
|
25
|
+
*
|
|
26
|
+
* If the root carried both the 12-column grid and
|
|
27
|
+
* `maxWidth: var(--pw-layout-container-max-width)` + `margin: 0 auto`,
|
|
28
|
+
* persistent chrome would sit inside the centred box too (a persistent rail
|
|
29
|
+
* would start well in from the screen edge, as part of the narrower container).
|
|
30
|
+
*
|
|
31
|
+
* The measure goes to the inner container through `resolveMeasureStyle`;
|
|
32
|
+
* the root is flow only.
|
|
33
|
+
*/
|
|
34
|
+
export declare function resolveSurfaceRootStyle(gap?: string | number, padding?: string | number): React.CSSProperties;
|
|
35
|
+
/**
|
|
36
|
+
* Measured container — the wrapper of `container`-scoped blocks.
|
|
37
|
+
*
|
|
38
|
+
* The 12-column grid and `containerMaxWidth` live HERE. Blocks that declare
|
|
39
|
+
* `widthScope:'viewport'` are drawn as SIBLINGS of this container, not its
|
|
40
|
+
* children — the escape is placement, not a CSS trick (see render/width-scope.ts).
|
|
41
|
+
*/
|
|
42
|
+
export declare function resolveMeasureStyle(viewport: BreakpointName, gap?: string | number, padding?: string | number, containerMaxWidth?: string): React.CSSProperties;
|
|
43
|
+
/**
|
|
44
|
+
* React hook that returns the current viewport breakpoint.
|
|
45
|
+
* Uses matchMedia for efficient viewport detection.
|
|
46
|
+
*
|
|
47
|
+
* Returns 'lg' on server (SSR fallback).
|
|
48
|
+
*/
|
|
49
|
+
export declare function useViewport(): BreakpointName;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Responsive Rendering Utilities
|
|
3
|
+
*
|
|
4
|
+
* Viewport-aware layout resolution for runtime rendering.
|
|
5
|
+
* Re-exports pure functions from @pwfabric/core and provides
|
|
6
|
+
* React-specific viewport hook.
|
|
7
|
+
*
|
|
8
|
+
* @module @pwfabric/runtime/render/responsive
|
|
9
|
+
*/
|
|
10
|
+
import * as React from 'react';
|
|
11
|
+
import { BREAKPOINTS, resolveSpanAtBreakpoint, resolveResponsiveValue, getColumnsAtBreakpoint, getBreakpointForWidth, } from '@pwfabric/core';
|
|
12
|
+
// Re-export pure functions for convenience
|
|
13
|
+
export { resolveResponsiveValue, resolveSpanAtBreakpoint, getColumnsAtBreakpoint, getBreakpointForWidth, } from '@pwfabric/core';
|
|
14
|
+
// ============================================================
|
|
15
|
+
// BLOCK STYLE RESOLUTION
|
|
16
|
+
// ============================================================
|
|
17
|
+
/**
|
|
18
|
+
* Resolve the complete block layout at a given viewport breakpoint.
|
|
19
|
+
* Returns span, offset, order, and hidden values.
|
|
20
|
+
*/
|
|
21
|
+
export function resolveBlockLayout(layout, viewport) {
|
|
22
|
+
return {
|
|
23
|
+
span: resolveSpanAtBreakpoint(layout, viewport),
|
|
24
|
+
offset: resolveResponsiveValue(layout.offset, viewport),
|
|
25
|
+
order: resolveResponsiveValue(layout.order, viewport),
|
|
26
|
+
hidden: resolveResponsiveValue(layout.hidden, viewport),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Surface root — carries NO MEASURE.
|
|
31
|
+
*
|
|
32
|
+
* If the root carried both the 12-column grid and
|
|
33
|
+
* `maxWidth: var(--pw-layout-container-max-width)` + `margin: 0 auto`,
|
|
34
|
+
* persistent chrome would sit inside the centred box too (a persistent rail
|
|
35
|
+
* would start well in from the screen edge, as part of the narrower container).
|
|
36
|
+
*
|
|
37
|
+
* The measure goes to the inner container through `resolveMeasureStyle`;
|
|
38
|
+
* the root is flow only.
|
|
39
|
+
*/
|
|
40
|
+
export function resolveSurfaceRootStyle(gap = 16, padding = 0) {
|
|
41
|
+
return {
|
|
42
|
+
display: 'flex',
|
|
43
|
+
flexDirection: 'column',
|
|
44
|
+
gap: typeof gap === 'number' ? `${gap}px` : gap,
|
|
45
|
+
padding: typeof padding === 'number' ? `${padding}px` : padding,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Measured container — the wrapper of `container`-scoped blocks.
|
|
50
|
+
*
|
|
51
|
+
* The 12-column grid and `containerMaxWidth` live HERE. Blocks that declare
|
|
52
|
+
* `widthScope:'viewport'` are drawn as SIBLINGS of this container, not its
|
|
53
|
+
* children — the escape is placement, not a CSS trick (see render/width-scope.ts).
|
|
54
|
+
*/
|
|
55
|
+
export function resolveMeasureStyle(viewport, gap = 16, padding = 24, containerMaxWidth = 'var(--pw-layout-container-max-width)') {
|
|
56
|
+
const columns = getColumnsAtBreakpoint(viewport);
|
|
57
|
+
return {
|
|
58
|
+
display: 'grid',
|
|
59
|
+
gridTemplateColumns: `repeat(${columns}, 1fr)`,
|
|
60
|
+
gap: typeof gap === 'number' ? `${gap}px` : gap,
|
|
61
|
+
padding: typeof padding === 'number' ? `${padding}px` : padding,
|
|
62
|
+
maxWidth: containerMaxWidth,
|
|
63
|
+
margin: '0 auto',
|
|
64
|
+
width: '100%',
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
// ============================================================
|
|
68
|
+
// VIEWPORT HOOK
|
|
69
|
+
// ============================================================
|
|
70
|
+
/**
|
|
71
|
+
* React hook that returns the current viewport breakpoint.
|
|
72
|
+
* Uses matchMedia for efficient viewport detection.
|
|
73
|
+
*
|
|
74
|
+
* Returns 'lg' on server (SSR fallback).
|
|
75
|
+
*/
|
|
76
|
+
export function useViewport() {
|
|
77
|
+
const [breakpoint, setBreakpoint] = React.useState('lg');
|
|
78
|
+
React.useEffect(() => {
|
|
79
|
+
// Set initial value from current window width
|
|
80
|
+
setBreakpoint(getBreakpointForWidth(window.innerWidth));
|
|
81
|
+
// Create media query listeners for each breakpoint transition
|
|
82
|
+
const listeners = [];
|
|
83
|
+
for (const bp of BREAKPOINTS) {
|
|
84
|
+
const mql = window.matchMedia(`(min-width: ${bp.minWidth}px)`);
|
|
85
|
+
const handler = () => {
|
|
86
|
+
setBreakpoint(getBreakpointForWidth(window.innerWidth));
|
|
87
|
+
};
|
|
88
|
+
mql.addEventListener('change', handler);
|
|
89
|
+
listeners.push({ mql, handler });
|
|
90
|
+
}
|
|
91
|
+
return () => {
|
|
92
|
+
for (const { mql, handler } of listeners) {
|
|
93
|
+
mql.removeEventListener('change', handler);
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
}, []);
|
|
97
|
+
return breakpoint;
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=responsive.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"responsive.js","sourceRoot":"","sources":["../../src/render/responsive.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Responsive Rendering Utilities\n *\n * Viewport-aware layout resolution for runtime rendering.\n * Re-exports pure functions from @pwfabric/core and provides\n * React-specific viewport hook.\n *\n * @module @pwfabric/runtime/render/responsive\n */\n\nimport * as React from 'react'\nimport type { BlockLayout, BreakpointName } from '@pwfabric/core'\nimport {\n BREAKPOINTS,\n resolveSpanAtBreakpoint,\n resolveResponsiveValue,\n getColumnsAtBreakpoint,\n getBreakpointForWidth,\n} from '@pwfabric/core'\n\n// Re-export pure functions for convenience\nexport {\n resolveResponsiveValue,\n resolveSpanAtBreakpoint,\n getColumnsAtBreakpoint,\n getBreakpointForWidth,\n} from '@pwfabric/core'\n\n// ============================================================\n// BLOCK STYLE RESOLUTION\n// ============================================================\n\n/**\n * Resolve the complete block layout at a given viewport breakpoint.\n * Returns span, offset, order, and hidden values.\n */\nexport function resolveBlockLayout(\n layout: BlockLayout,\n viewport: BreakpointName\n): {\n span: number\n offset: number | undefined\n order: number | undefined\n hidden: boolean | undefined\n} {\n return {\n span: resolveSpanAtBreakpoint(layout, viewport),\n offset: resolveResponsiveValue(layout.offset, viewport),\n order: resolveResponsiveValue(layout.order, viewport),\n hidden: resolveResponsiveValue(layout.hidden, viewport),\n }\n}\n\n/**\n * Surface root — carries NO MEASURE.\n *\n * If the root carried both the 12-column grid and\n * `maxWidth: var(--pw-layout-container-max-width)` + `margin: 0 auto`,\n * persistent chrome would sit inside the centred box too (a persistent rail\n * would start well in from the screen edge, as part of the narrower container).\n *\n * The measure goes to the inner container through `resolveMeasureStyle`;\n * the root is flow only.\n */\nexport function resolveSurfaceRootStyle(\n gap: string | number = 16,\n padding: string | number = 0\n): React.CSSProperties {\n return {\n display: 'flex',\n flexDirection: 'column',\n gap: typeof gap === 'number' ? `${gap}px` : gap,\n padding: typeof padding === 'number' ? `${padding}px` : padding,\n }\n}\n\n/**\n * Measured container — the wrapper of `container`-scoped blocks.\n *\n * The 12-column grid and `containerMaxWidth` live HERE. Blocks that declare\n * `widthScope:'viewport'` are drawn as SIBLINGS of this container, not its\n * children — the escape is placement, not a CSS trick (see render/width-scope.ts).\n */\nexport function resolveMeasureStyle(\n viewport: BreakpointName,\n gap: string | number = 16,\n padding: string | number = 24,\n containerMaxWidth = 'var(--pw-layout-container-max-width)'\n): React.CSSProperties {\n const columns = getColumnsAtBreakpoint(viewport)\n return {\n display: 'grid',\n gridTemplateColumns: `repeat(${columns}, 1fr)`,\n gap: typeof gap === 'number' ? `${gap}px` : gap,\n padding: typeof padding === 'number' ? `${padding}px` : padding,\n maxWidth: containerMaxWidth,\n margin: '0 auto',\n width: '100%',\n }\n}\n\n// ============================================================\n// VIEWPORT HOOK\n// ============================================================\n\n/**\n * React hook that returns the current viewport breakpoint.\n * Uses matchMedia for efficient viewport detection.\n *\n * Returns 'lg' on server (SSR fallback).\n */\nexport function useViewport(): BreakpointName {\n const [breakpoint, setBreakpoint] = React.useState<BreakpointName>('lg')\n\n React.useEffect(() => {\n // Set initial value from current window width\n setBreakpoint(getBreakpointForWidth(window.innerWidth))\n\n // Create media query listeners for each breakpoint transition\n const listeners: { mql: MediaQueryList; handler: () => void }[] = []\n\n for (const bp of BREAKPOINTS) {\n const mql = window.matchMedia(`(min-width: ${bp.minWidth}px)`)\n const handler = (): void => {\n setBreakpoint(getBreakpointForWidth(window.innerWidth))\n }\n mql.addEventListener('change', handler)\n listeners.push({ mql, handler })\n }\n\n return () => {\n for (const { mql, handler } of listeners) {\n mql.removeEventListener('change', handler)\n }\n }\n }, [])\n\n return breakpoint\n}\n"]}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared helpers for the scoped per-block CSS collectors.
|
|
3
|
+
*
|
|
4
|
+
* The runtime emits several kinds of scoped `<style>` rules keyed by a block's
|
|
5
|
+
* stable `data-block-id` (visibility / interaction-states / responsive-style /
|
|
6
|
+
* conditional-states / group-states). They all share the SAME primitives:
|
|
7
|
+
*
|
|
8
|
+
* - `cssEscapeId(id)` — conservative attribute-value escaper for block ids.
|
|
9
|
+
* - `camelToKebab(prop)` — CSS camelCase property name → kebab-case.
|
|
10
|
+
* - `blockSelector(id)` — `[data-pw-surface] [data-block-id="<escaped>"]`,
|
|
11
|
+
* the scoped base selector every rule attaches to.
|
|
12
|
+
* - `collectScopedCss(blocks, perBlockFn)` — walk the block tree (including
|
|
13
|
+
* children) and concatenate each block's CSS as
|
|
14
|
+
* produced by `perBlockFn(block)`.
|
|
15
|
+
*
|
|
16
|
+
* Centralising these keeps the per-collector files focused on their own
|
|
17
|
+
* selector/declaration semantics and guarantees byte-identical output across
|
|
18
|
+
* the family (the same escaper, the same selector string, the same walk order).
|
|
19
|
+
*
|
|
20
|
+
* @module @pwfabric/runtime/render/scoped-css
|
|
21
|
+
*/
|
|
22
|
+
import type { Block } from '@pwfabric/core';
|
|
23
|
+
/** Conservative CSS attribute-selector value escaper (block ids are slugs, but be safe). */
|
|
24
|
+
export declare function cssEscapeId(id: string): string;
|
|
25
|
+
/** camelCase CSS property → kebab-case (e.g. `backgroundColor` → `background-color`). */
|
|
26
|
+
export declare function camelToKebab(prop: string): string;
|
|
27
|
+
/** The bare `[data-block-id="<escaped-id>"]` attribute selector (no surface scope). */
|
|
28
|
+
export declare function blockIdAttr(id: string): string;
|
|
29
|
+
/**
|
|
30
|
+
* The surface-root selector a scoped rule hangs off.
|
|
31
|
+
*
|
|
32
|
+
* With a `surfaceId` this is `[data-pw-surface="<id>"]` — scope carried by the
|
|
33
|
+
* attribute's VALUE, not merely its presence. That matters because the embed
|
|
34
|
+
* autoloader hydrates EVERY `[data-pw-surface]` element in a document, so a host
|
|
35
|
+
* page can hold several surfaces at once; a value-less selector makes each
|
|
36
|
+
* surface's rules apply to all of them, and the last one in document order wins.
|
|
37
|
+
*
|
|
38
|
+
* Without a `surfaceId` it degrades to the historical `[data-pw-surface]`, which
|
|
39
|
+
* is correct for rules whose content is identical for every surface (the base
|
|
40
|
+
* CSS) and for unit tests that render a single surface.
|
|
41
|
+
*/
|
|
42
|
+
export declare function surfaceScope(surfaceId?: string): string;
|
|
43
|
+
/**
|
|
44
|
+
* The scoped base selector a per-block rule attaches to:
|
|
45
|
+
* `[data-pw-surface="<sid>"] [data-block-id="<escaped-id>"]`. Scoped under the
|
|
46
|
+
* surface root so a rule can never leak to the host page — and, when the surface
|
|
47
|
+
* id is supplied, never to a SIBLING surface that happens to reuse a block id.
|
|
48
|
+
*/
|
|
49
|
+
export declare function blockSelector(id: string, surfaceId?: string): string;
|
|
50
|
+
/**
|
|
51
|
+
* Walk a surface block tree (depth-first, children after their parent — the
|
|
52
|
+
* order all collectors used) and concatenate the per-block CSS produced by
|
|
53
|
+
* `perBlockFn(block)`. Blocks for which `perBlockFn` returns '' contribute zero
|
|
54
|
+
* bytes, so existing surfaces stay byte-identical.
|
|
55
|
+
*/
|
|
56
|
+
export declare function collectScopedCss(blocks: readonly Block[], perBlockFn: (block: Block) => string): string;
|
|
57
|
+
/**
|
|
58
|
+
* Style keys HOISTED to the wrapper — kept here so the renderer and the
|
|
59
|
+
* responsive CSS generator see the SAME list.
|
|
60
|
+
*
|
|
61
|
+
* This list is a RULE: these keys apply to the outer wrapper, not the
|
|
62
|
+
* component root. For flat values the renderer hoists them; for responsive
|
|
63
|
+
* values the value leaves the inline style entirely and lands in CSS, so the
|
|
64
|
+
* same rule has to be expressed on the selector side — otherwise the rule
|
|
65
|
+
* matches both the wrapper and the root and the value is applied TWICE
|
|
66
|
+
* (`marginTop:'-40%'` would move content by both -335px and -256px).
|
|
67
|
+
*/
|
|
68
|
+
export declare const FLEX_HOIST_KEYS: readonly ["flex", "flexGrow", "flexShrink", "flexBasis"];
|
|
69
|
+
export declare const LAYOUT_HOIST_KEYS: readonly ["margin", "marginTop", "marginRight", "marginBottom", "marginLeft", "maxWidth", "minWidth", "flex", "flexGrow", "flexShrink", "flexBasis"];
|
|
70
|
+
/** The attribute that tells the wrapper from the component root (the same marker states-css uses). */
|
|
71
|
+
export declare const WRAPPER_ATTR = "[data-block-wrapper]";
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared helpers for the scoped per-block CSS collectors.
|
|
3
|
+
*
|
|
4
|
+
* The runtime emits several kinds of scoped `<style>` rules keyed by a block's
|
|
5
|
+
* stable `data-block-id` (visibility / interaction-states / responsive-style /
|
|
6
|
+
* conditional-states / group-states). They all share the SAME primitives:
|
|
7
|
+
*
|
|
8
|
+
* - `cssEscapeId(id)` — conservative attribute-value escaper for block ids.
|
|
9
|
+
* - `camelToKebab(prop)` — CSS camelCase property name → kebab-case.
|
|
10
|
+
* - `blockSelector(id)` — `[data-pw-surface] [data-block-id="<escaped>"]`,
|
|
11
|
+
* the scoped base selector every rule attaches to.
|
|
12
|
+
* - `collectScopedCss(blocks, perBlockFn)` — walk the block tree (including
|
|
13
|
+
* children) and concatenate each block's CSS as
|
|
14
|
+
* produced by `perBlockFn(block)`.
|
|
15
|
+
*
|
|
16
|
+
* Centralising these keeps the per-collector files focused on their own
|
|
17
|
+
* selector/declaration semantics and guarantees byte-identical output across
|
|
18
|
+
* the family (the same escaper, the same selector string, the same walk order).
|
|
19
|
+
*
|
|
20
|
+
* @module @pwfabric/runtime/render/scoped-css
|
|
21
|
+
*/
|
|
22
|
+
/** Conservative CSS attribute-selector value escaper (block ids are slugs, but be safe). */
|
|
23
|
+
export function cssEscapeId(id) {
|
|
24
|
+
return id.replace(/["\\]/g, '\\$&');
|
|
25
|
+
}
|
|
26
|
+
/** camelCase CSS property → kebab-case (e.g. `backgroundColor` → `background-color`). */
|
|
27
|
+
export function camelToKebab(prop) {
|
|
28
|
+
return prop.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);
|
|
29
|
+
}
|
|
30
|
+
/** The bare `[data-block-id="<escaped-id>"]` attribute selector (no surface scope). */
|
|
31
|
+
export function blockIdAttr(id) {
|
|
32
|
+
return `[data-block-id="${cssEscapeId(id)}"]`;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The surface-root selector a scoped rule hangs off.
|
|
36
|
+
*
|
|
37
|
+
* With a `surfaceId` this is `[data-pw-surface="<id>"]` — scope carried by the
|
|
38
|
+
* attribute's VALUE, not merely its presence. That matters because the embed
|
|
39
|
+
* autoloader hydrates EVERY `[data-pw-surface]` element in a document, so a host
|
|
40
|
+
* page can hold several surfaces at once; a value-less selector makes each
|
|
41
|
+
* surface's rules apply to all of them, and the last one in document order wins.
|
|
42
|
+
*
|
|
43
|
+
* Without a `surfaceId` it degrades to the historical `[data-pw-surface]`, which
|
|
44
|
+
* is correct for rules whose content is identical for every surface (the base
|
|
45
|
+
* CSS) and for unit tests that render a single surface.
|
|
46
|
+
*/
|
|
47
|
+
export function surfaceScope(surfaceId) {
|
|
48
|
+
return surfaceId ? `[data-pw-surface="${cssEscapeId(surfaceId)}"]` : '[data-pw-surface]';
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The scoped base selector a per-block rule attaches to:
|
|
52
|
+
* `[data-pw-surface="<sid>"] [data-block-id="<escaped-id>"]`. Scoped under the
|
|
53
|
+
* surface root so a rule can never leak to the host page — and, when the surface
|
|
54
|
+
* id is supplied, never to a SIBLING surface that happens to reuse a block id.
|
|
55
|
+
*/
|
|
56
|
+
export function blockSelector(id, surfaceId) {
|
|
57
|
+
return `${surfaceScope(surfaceId)} ${blockIdAttr(id)}`;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Walk a surface block tree (depth-first, children after their parent — the
|
|
61
|
+
* order all collectors used) and concatenate the per-block CSS produced by
|
|
62
|
+
* `perBlockFn(block)`. Blocks for which `perBlockFn` returns '' contribute zero
|
|
63
|
+
* bytes, so existing surfaces stay byte-identical.
|
|
64
|
+
*/
|
|
65
|
+
export function collectScopedCss(blocks, perBlockFn) {
|
|
66
|
+
let css = '';
|
|
67
|
+
const walk = (block) => {
|
|
68
|
+
css += perBlockFn(block);
|
|
69
|
+
if (block.children) {
|
|
70
|
+
for (const child of block.children)
|
|
71
|
+
walk(child);
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
for (const block of blocks)
|
|
75
|
+
walk(block);
|
|
76
|
+
return css;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Style keys HOISTED to the wrapper — kept here so the renderer and the
|
|
80
|
+
* responsive CSS generator see the SAME list.
|
|
81
|
+
*
|
|
82
|
+
* This list is a RULE: these keys apply to the outer wrapper, not the
|
|
83
|
+
* component root. For flat values the renderer hoists them; for responsive
|
|
84
|
+
* values the value leaves the inline style entirely and lands in CSS, so the
|
|
85
|
+
* same rule has to be expressed on the selector side — otherwise the rule
|
|
86
|
+
* matches both the wrapper and the root and the value is applied TWICE
|
|
87
|
+
* (`marginTop:'-40%'` would move content by both -335px and -256px).
|
|
88
|
+
*/
|
|
89
|
+
export const FLEX_HOIST_KEYS = ['flex', 'flexGrow', 'flexShrink', 'flexBasis'];
|
|
90
|
+
export const LAYOUT_HOIST_KEYS = [
|
|
91
|
+
'margin',
|
|
92
|
+
'marginTop',
|
|
93
|
+
'marginRight',
|
|
94
|
+
'marginBottom',
|
|
95
|
+
'marginLeft',
|
|
96
|
+
'maxWidth',
|
|
97
|
+
'minWidth',
|
|
98
|
+
...FLEX_HOIST_KEYS,
|
|
99
|
+
];
|
|
100
|
+
/** The attribute that tells the wrapper from the component root (the same marker states-css uses). */
|
|
101
|
+
export const WRAPPER_ATTR = '[data-block-wrapper]';
|
|
102
|
+
//# sourceMappingURL=scoped-css.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scoped-css.js","sourceRoot":"","sources":["../../src/render/scoped-css.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH,4FAA4F;AAC5F,MAAM,UAAU,WAAW,CAAC,EAAU;IACpC,OAAO,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;AACrC,CAAC;AAED,yFAAyF;AACzF,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,CAAA;AAC7D,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,WAAW,CAAC,EAAU;IACpC,OAAO,mBAAmB,WAAW,CAAC,EAAE,CAAC,IAAI,CAAA;AAC/C,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,YAAY,CAAC,SAAkB;IAC7C,OAAO,SAAS,CAAC,CAAC,CAAC,qBAAqB,WAAW,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,mBAAmB,CAAA;AAC1F,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,EAAU,EAAE,SAAkB;IAC1D,OAAO,GAAG,YAAY,CAAC,SAAS,CAAC,IAAI,WAAW,CAAC,EAAE,CAAC,EAAE,CAAA;AACxD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAC9B,MAAwB,EACxB,UAAoC;IAEpC,IAAI,GAAG,GAAG,EAAE,CAAA;IACZ,MAAM,IAAI,GAAG,CAAC,KAAY,EAAQ,EAAE;QAClC,GAAG,IAAI,UAAU,CAAC,KAAK,CAAC,CAAA;QACxB,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;YACnB,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,QAAQ;gBAAE,IAAI,CAAC,KAAK,CAAC,CAAA;QACjD,CAAC;IACH,CAAC,CAAA;IACD,KAAK,MAAM,KAAK,IAAI,MAAM;QAAE,IAAI,CAAC,KAAK,CAAC,CAAA;IACvC,OAAO,GAAG,CAAA;AACZ,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,MAAM,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW,CAAU,CAAA;AAEvF,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,QAAQ;IACR,WAAW;IACX,aAAa;IACb,cAAc;IACd,YAAY;IACZ,UAAU;IACV,UAAU;IACV,GAAG,eAAe;CACV,CAAA;AAEV,sGAAsG;AACtG,MAAM,CAAC,MAAM,YAAY,GAAG,sBAAsB,CAAA","sourcesContent":["/**\n * Shared helpers for the scoped per-block CSS collectors.\n *\n * The runtime emits several kinds of scoped `<style>` rules keyed by a block's\n * stable `data-block-id` (visibility / interaction-states / responsive-style /\n * conditional-states / group-states). They all share the SAME primitives:\n *\n * - `cssEscapeId(id)` — conservative attribute-value escaper for block ids.\n * - `camelToKebab(prop)` — CSS camelCase property name → kebab-case.\n * - `blockSelector(id)` — `[data-pw-surface] [data-block-id=\"<escaped>\"]`,\n * the scoped base selector every rule attaches to.\n * - `collectScopedCss(blocks, perBlockFn)` — walk the block tree (including\n * children) and concatenate each block's CSS as\n * produced by `perBlockFn(block)`.\n *\n * Centralising these keeps the per-collector files focused on their own\n * selector/declaration semantics and guarantees byte-identical output across\n * the family (the same escaper, the same selector string, the same walk order).\n *\n * @module @pwfabric/runtime/render/scoped-css\n */\n\nimport type { Block } from '@pwfabric/core'\n\n/** Conservative CSS attribute-selector value escaper (block ids are slugs, but be safe). */\nexport function cssEscapeId(id: string): string {\n return id.replace(/[\"\\\\]/g, '\\\\$&')\n}\n\n/** camelCase CSS property → kebab-case (e.g. `backgroundColor` → `background-color`). */\nexport function camelToKebab(prop: string): string {\n return prop.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`)\n}\n\n/** The bare `[data-block-id=\"<escaped-id>\"]` attribute selector (no surface scope). */\nexport function blockIdAttr(id: string): string {\n return `[data-block-id=\"${cssEscapeId(id)}\"]`\n}\n\n/**\n * The surface-root selector a scoped rule hangs off.\n *\n * With a `surfaceId` this is `[data-pw-surface=\"<id>\"]` — scope carried by the\n * attribute's VALUE, not merely its presence. That matters because the embed\n * autoloader hydrates EVERY `[data-pw-surface]` element in a document, so a host\n * page can hold several surfaces at once; a value-less selector makes each\n * surface's rules apply to all of them, and the last one in document order wins.\n *\n * Without a `surfaceId` it degrades to the historical `[data-pw-surface]`, which\n * is correct for rules whose content is identical for every surface (the base\n * CSS) and for unit tests that render a single surface.\n */\nexport function surfaceScope(surfaceId?: string): string {\n return surfaceId ? `[data-pw-surface=\"${cssEscapeId(surfaceId)}\"]` : '[data-pw-surface]'\n}\n\n/**\n * The scoped base selector a per-block rule attaches to:\n * `[data-pw-surface=\"<sid>\"] [data-block-id=\"<escaped-id>\"]`. Scoped under the\n * surface root so a rule can never leak to the host page — and, when the surface\n * id is supplied, never to a SIBLING surface that happens to reuse a block id.\n */\nexport function blockSelector(id: string, surfaceId?: string): string {\n return `${surfaceScope(surfaceId)} ${blockIdAttr(id)}`\n}\n\n/**\n * Walk a surface block tree (depth-first, children after their parent — the\n * order all collectors used) and concatenate the per-block CSS produced by\n * `perBlockFn(block)`. Blocks for which `perBlockFn` returns '' contribute zero\n * bytes, so existing surfaces stay byte-identical.\n */\nexport function collectScopedCss(\n blocks: readonly Block[],\n perBlockFn: (block: Block) => string\n): string {\n let css = ''\n const walk = (block: Block): void => {\n css += perBlockFn(block)\n if (block.children) {\n for (const child of block.children) walk(child)\n }\n }\n for (const block of blocks) walk(block)\n return css\n}\n\n/**\n * Style keys HOISTED to the wrapper — kept here so the renderer and the\n * responsive CSS generator see the SAME list.\n *\n * This list is a RULE: these keys apply to the outer wrapper, not the\n * component root. For flat values the renderer hoists them; for responsive\n * values the value leaves the inline style entirely and lands in CSS, so the\n * same rule has to be expressed on the selector side — otherwise the rule\n * matches both the wrapper and the root and the value is applied TWICE\n * (`marginTop:'-40%'` would move content by both -335px and -256px).\n */\nexport const FLEX_HOIST_KEYS = ['flex', 'flexGrow', 'flexShrink', 'flexBasis'] as const\n\nexport const LAYOUT_HOIST_KEYS = [\n 'margin',\n 'marginTop',\n 'marginRight',\n 'marginBottom',\n 'marginLeft',\n 'maxWidth',\n 'minWidth',\n ...FLEX_HOIST_KEYS,\n] as const\n\n/** The attribute that tells the wrapper from the component root (the same marker states-css uses). */\nexport const WRAPPER_ATTR = '[data-block-wrapper]'\n"]}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shell selection — the state a SPLIT shell runs on.
|
|
3
|
+
*
|
|
4
|
+
* WHY THIS IS IN THE RUNTIME AND NOT IN THE BLOCK. A split surface (list on the
|
|
5
|
+
* left, detail on the right) has exactly one piece of state: which item is
|
|
6
|
+
* selected. Kept inside the block it is invisible to everything else — the URL
|
|
7
|
+
* does not move, so the page cannot be linked to, the browser Back button does
|
|
8
|
+
* nothing, and a second block on the same surface (a header showing the
|
|
9
|
+
* selected record's title, a toolbar acting on it) has no way to read it.
|
|
10
|
+
*
|
|
11
|
+
* So selection is a SURFACE-level fact with a URL projection:
|
|
12
|
+
*
|
|
13
|
+
* ?<key>=<itemId>
|
|
14
|
+
*
|
|
15
|
+
* and this module is the single place that reads and writes it.
|
|
16
|
+
*
|
|
17
|
+
* THE URL IS THE SOURCE OF TRUTH when syncing is on. React state mirrors it
|
|
18
|
+
* rather than the other way round, which is what makes a pasted link, a Back
|
|
19
|
+
* press and a click on the list all land in the same place — three paths, one
|
|
20
|
+
* rule.
|
|
21
|
+
*
|
|
22
|
+
* INERT HOSTS DO NOT NAVIGATE. An editor or preview canvas renders the same
|
|
23
|
+
* block with `interactionMode: 'inert'`; there the selection stays in memory and
|
|
24
|
+
* the host's own URL is never touched. That mirrors the existing rule for blocks
|
|
25
|
+
* that navigate or write data.
|
|
26
|
+
*/
|
|
27
|
+
import * as React from 'react';
|
|
28
|
+
type Listener = () => void;
|
|
29
|
+
export interface ShellSelectionStore {
|
|
30
|
+
/** Current selection for a key, or undefined when nothing is selected. */
|
|
31
|
+
get(key: string): string | undefined;
|
|
32
|
+
/** Select an item. Writes the URL when this store syncs. */
|
|
33
|
+
set(key: string, id: string | undefined): void;
|
|
34
|
+
subscribe(listener: Listener): () => void;
|
|
35
|
+
readonly syncUrl: boolean;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Create a selection store.
|
|
39
|
+
*
|
|
40
|
+
* `syncUrl: false` gives an in-memory store with identical semantics — the same
|
|
41
|
+
* hook, the same cross-block sharing, no address-bar side effects.
|
|
42
|
+
*/
|
|
43
|
+
export declare function createShellSelectionStore(syncUrl: boolean): ShellSelectionStore;
|
|
44
|
+
export declare const ShellSelectionContext: React.Context<ShellSelectionStore | null>;
|
|
45
|
+
export interface ShellSelectionProviderProps {
|
|
46
|
+
/** Off for editor/preview canvases — selection works, the address bar does not move. */
|
|
47
|
+
readonly syncUrl?: boolean;
|
|
48
|
+
readonly children: React.ReactNode;
|
|
49
|
+
}
|
|
50
|
+
export declare function ShellSelectionProvider({ syncUrl, children, }: ShellSelectionProviderProps): React.ReactElement;
|
|
51
|
+
export interface ShellSelectionOptions {
|
|
52
|
+
/** Selected when the URL carries nothing — normally the first list item. */
|
|
53
|
+
readonly fallback?: string | undefined;
|
|
54
|
+
/**
|
|
55
|
+
* Override the provider. A block rendered inert must not move the host's URL
|
|
56
|
+
* even when the surrounding surface is live.
|
|
57
|
+
*/
|
|
58
|
+
readonly syncUrl?: boolean;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Read and write the shell selection for `key`.
|
|
62
|
+
*
|
|
63
|
+
* Works with OR without a provider: with no `ShellSelectionProvider` above it,
|
|
64
|
+
* the hook makes its own store, so a block dropped into a bare React tree keeps
|
|
65
|
+
* behaving instead of throwing. The provider only widens the scope — one store
|
|
66
|
+
* per surface means two blocks reading the same key see the same value.
|
|
67
|
+
*/
|
|
68
|
+
export declare function useShellSelection(key: string, options?: ShellSelectionOptions): readonly [string | undefined, (id: string | undefined) => void];
|
|
69
|
+
export {};
|