@styleflow.app/flowpress-astro-core 0.2.0-beta.9 → 1.0.0-beta.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/CHANGELOG.md +109 -0
- package/LICENSE +21 -0
- package/README.md +90 -3
- package/dist/astro/BaseLayout.astro +7 -2
- package/dist/astro/DefaultTemplate.astro +5 -7
- package/dist/astro/GutenbergRenderer.astro +132 -28
- package/dist/astro/blocks/GridBlock.astro +1 -1
- package/dist/astro/blocks/HeroBlock.astro +8 -2
- package/dist/astro/blocks/ImageBlock.astro +90 -0
- package/dist/astro/blocks/NumberSpinnerBlock.astro +130 -0
- package/dist/astro/blocks/TextBlock.astro +19 -6
- package/dist/astro/blocks/helpers.d.ts +10 -0
- package/dist/astro/blocks/helpers.d.ts.map +1 -1
- package/dist/astro/blocks/helpers.js +86 -5
- package/dist/astro/blocks/helpers.js.map +1 -1
- package/dist/astro/blocks/helpers.ts +111 -5
- package/dist/block-renderers.d.ts +22 -0
- package/dist/block-renderers.d.ts.map +1 -0
- package/dist/block-renderers.js +2 -0
- package/dist/block-renderers.js.map +1 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +26 -1
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/number-spinner.d.ts +6 -0
- package/dist/number-spinner.d.ts.map +1 -0
- package/dist/number-spinner.js +136 -0
- package/dist/number-spinner.js.map +1 -0
- package/dist/style-classes.d.ts +9 -0
- package/dist/style-classes.d.ts.map +1 -0
- package/dist/style-classes.js +49 -0
- package/dist/style-classes.js.map +1 -0
- package/dist/styleflow.d.ts +37 -8
- package/dist/styleflow.d.ts.map +1 -1
- package/dist/styleflow.js +85 -42
- package/dist/styleflow.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types.d.ts +8 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +6 -5
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,115 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 1.0.0-beta.1
|
|
6
|
+
|
|
7
|
+
- Updated the exact `@styleflow.app/astro` dependency to `1.0.0-beta.1`,
|
|
8
|
+
keeping consumer trees on one runtime while adopting deterministic CLI
|
|
9
|
+
reports. Runtime behavior is unchanged.
|
|
10
|
+
|
|
11
|
+
## 1.0.0-beta.0
|
|
12
|
+
|
|
13
|
+
- Adopted the grouped Styleflow vNext project contract and the exact Astro v1
|
|
14
|
+
runtime dependency, removing the consumer dependency on Styleflow Core.
|
|
15
|
+
- Added project-aware validation for tone/intensity and type/variant pairs,
|
|
16
|
+
theme propagation and diagnostic-only explicit fallbacks.
|
|
17
|
+
- Made invalid CMS coordinates blocking in strict builds while preserving all
|
|
18
|
+
existing recursive renderers, forms, responsive media, trusted code, SEO,
|
|
19
|
+
RichText spacing and accessibility behavior.
|
|
20
|
+
|
|
21
|
+
## 0.2.0-beta.23
|
|
22
|
+
|
|
23
|
+
- Added validated Gutenberg text alignment for FlowPress and core text blocks.
|
|
24
|
+
- Added a typed project icon renderer, recursive propagation, standalone icon
|
|
25
|
+
blocks and ordered button icons with the StyleFlow `control` layout role.
|
|
26
|
+
- Treated `foreground=inherit` as omission, matching inherited tone and
|
|
27
|
+
intensity behavior.
|
|
28
|
+
- Rendered trusted `sf/code` documents in the main DOM only when WordPress
|
|
29
|
+
serialization supplies `codeApproved=true`; unapproved documents fail closed.
|
|
30
|
+
|
|
31
|
+
## 0.2.0-beta.22
|
|
32
|
+
|
|
33
|
+
- Added a dedicated `sf/image` renderer with WordPress responsive `srcset`,
|
|
34
|
+
authored or attachment-default `sizes`, intrinsic dimensions, per-image CSS
|
|
35
|
+
classes and validated loading, decoding and fetch-priority controls.
|
|
36
|
+
- Included per-image CSS classes in the recursive CMS class collector used by
|
|
37
|
+
consumer Tailwind safelists.
|
|
38
|
+
|
|
39
|
+
## 0.2.0-beta.21
|
|
40
|
+
|
|
41
|
+
- Forced locale-aware thousands grouping for four-digit number spinner values,
|
|
42
|
+
matching the grouping already applied to larger values.
|
|
43
|
+
|
|
44
|
+
## 0.2.0-beta.20
|
|
45
|
+
|
|
46
|
+
- Added typed installable feature capabilities and normalized public JSON
|
|
47
|
+
configuration returned by FlowPress WordPress Core.
|
|
48
|
+
- Added deterministic CSP, consent and tag-manager head slots before the
|
|
49
|
+
existing project-owned `head` slot.
|
|
50
|
+
|
|
51
|
+
## 0.2.0-beta.19
|
|
52
|
+
|
|
53
|
+
- Let the number spinner's visual and accessible value spans inherit the
|
|
54
|
+
selected number typography instead of assigning the StyleFlow `body-md`
|
|
55
|
+
fallback to both technical descendants.
|
|
56
|
+
|
|
57
|
+
## 0.2.0-beta.18
|
|
58
|
+
|
|
59
|
+
- Updated the exact StyleFlow Astro dependency to `0.3.0-beta.6`, matching
|
|
60
|
+
the final StyleFlow release that includes the corrected importer and the
|
|
61
|
+
branded Figma plugin bundle.
|
|
62
|
+
|
|
63
|
+
## 0.2.0-beta.17
|
|
64
|
+
|
|
65
|
+
- Updated the exact StyleFlow Astro dependency to `0.3.0-beta.5`, keeping
|
|
66
|
+
consumers on one runtime aligned with the corrected Figma contract import
|
|
67
|
+
release.
|
|
68
|
+
|
|
69
|
+
## 0.2.0-beta.16
|
|
70
|
+
|
|
71
|
+
- Updated the exact StyleFlow Astro dependency to `0.3.0-beta.4` so consumers
|
|
72
|
+
use the responsive compiler contract and a single matching runtime.
|
|
73
|
+
|
|
74
|
+
## 0.2.0-beta.15
|
|
75
|
+
|
|
76
|
+
- Applied Hero image and video `object-position` from a sanitized pair of
|
|
77
|
+
percentage coordinates, with a safe centered fallback.
|
|
78
|
+
|
|
79
|
+
## 0.2.0-beta.14
|
|
80
|
+
|
|
81
|
+
- Added a recursive project block-renderer registry to
|
|
82
|
+
`GutenbergRenderer.astro`, including imported `sf/template` and resolved
|
|
83
|
+
query-item descendants.
|
|
84
|
+
|
|
85
|
+
## 0.2.0-beta.13
|
|
86
|
+
|
|
87
|
+
- Kept number spinners static on their final values when
|
|
88
|
+
`IntersectionObserver` or motion preference detection is unavailable,
|
|
89
|
+
rather than starting outside a verifiable viewport intersection.
|
|
90
|
+
|
|
91
|
+
## 0.2.0-beta.12
|
|
92
|
+
|
|
93
|
+
- Added the reusable `sf/number-spinner` Astro renderer with independently
|
|
94
|
+
styled prefix, number and suffix text segments.
|
|
95
|
+
- Started counting only on first viewport intersection, retained the final
|
|
96
|
+
target in no-JavaScript markup and disabled animation for reduced-motion
|
|
97
|
+
preferences, including preference changes during an active count.
|
|
98
|
+
- Kept the final numeric value permanently available to assistive technology
|
|
99
|
+
while visual frame updates remain hidden from accessibility APIs.
|
|
100
|
+
|
|
101
|
+
## 0.2.0-beta.11
|
|
102
|
+
|
|
103
|
+
- Rendered empty Gutenberg and FlowPress text blocks with an internal
|
|
104
|
+
` `, preserving their typographic line height without visible
|
|
105
|
+
characters or synthetic break elements.
|
|
106
|
+
|
|
107
|
+
## 0.2.0-beta.10
|
|
108
|
+
|
|
109
|
+
- Preserved intentionally empty core Gutenberg paragraphs as visible line
|
|
110
|
+
breaks, including paragraphs resolved through FlowPress templates.
|
|
111
|
+
- Exported safe recursive collection of CMS-authored block class names for
|
|
112
|
+
Tailwind `@source inline()` generation.
|
|
113
|
+
|
|
5
114
|
## 0.2.0-beta.9
|
|
6
115
|
|
|
7
116
|
- Preserved repeated Gutenberg RichText spacing across `<br>` sequences,
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Styleflow
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -9,11 +9,21 @@ The package must not import from an application or project plugin.
|
|
|
9
9
|
## Install
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
pnpm add @styleflow.app/flowpress-astro-core@0.
|
|
12
|
+
pnpm add @styleflow.app/flowpress-astro-core@1.0.0-beta.1 \
|
|
13
|
+
@styleflow.app/astro@1.0.0-beta.1
|
|
13
14
|
```
|
|
14
15
|
|
|
15
16
|
Prereleases are pinned exactly. The package expects Astro 7 or newer and the
|
|
16
|
-
|
|
17
|
+
exact Styleflow Astro v1 dependency. `styleflowAstro()` must be installed in
|
|
18
|
+
the project configuration and the bundle must first be compiled by the v1
|
|
19
|
+
CLI.
|
|
20
|
+
|
|
21
|
+
CMS coordinates are validated against the grouped project contract. Valid
|
|
22
|
+
tone/intensity and type/variant pairs are project-specific; numeric intensity,
|
|
23
|
+
v0.x density and browser states passed as component props are rejected.
|
|
24
|
+
`theme` is propagated as a semantic scope. A fallback is permitted only when
|
|
25
|
+
the catalog or `agentPolicy` declares an explicit default, always emits a
|
|
26
|
+
diagnostic, and becomes blocking in strict production builds.
|
|
17
27
|
|
|
18
28
|
The client reports the GraphQL operation, sanitized endpoint, HTTP/content
|
|
19
29
|
type failures and underlying network/TLS cause. Upstream GraphQL debug details
|
|
@@ -37,7 +47,80 @@ to the protected FlowPress REST endpoint, so the same component works in
|
|
|
37
47
|
static and server output modes. Gutenberg `anchor` and `className` attributes
|
|
38
48
|
are applied to the rendered block root as HTML `id` and `class`. Rich Text
|
|
39
49
|
preserves intentional double, triple and longer line breaks, including content
|
|
40
|
-
saved by older editor versions as empty paragraphs.
|
|
50
|
+
saved by older editor versions as empty paragraphs. Empty textual blocks render
|
|
51
|
+
an internal ` `, preserving their typographic line height without adding
|
|
52
|
+
visible characters. The exported
|
|
53
|
+
`collectFlowpressClassNames()` helper recursively extracts safe class tokens
|
|
54
|
+
from rendered CMS blocks so consumers can generate Tailwind
|
|
55
|
+
`@source inline()` directives before compiling their frontend CSS.
|
|
56
|
+
|
|
57
|
+
Heading, rich-text, quote and matching core blocks accept Gutenberg alignment
|
|
58
|
+
from either `style.typography.textAlign` or the FlowPress `textAlign` field.
|
|
59
|
+
Only `left`, `center`, `right`, `justify`, `start` and `end` reach CSS.
|
|
60
|
+
`foreground=inherit` is omitted so icons and text inherit the surrounding
|
|
61
|
+
foreground instead of serializing an invalid StyleFlow role.
|
|
62
|
+
|
|
63
|
+
Hero image and video backgrounds apply the catalog `objectPosition` value as
|
|
64
|
+
two percentages (for example `77% 100%`). Invalid or missing CMS values safely
|
|
65
|
+
fall back to `50% 50%`.
|
|
66
|
+
|
|
67
|
+
`sf/image` renders the responsive attachment data serialized by WordPress as a
|
|
68
|
+
single `<img>` with `srcset`, `sizes` and positive intrinsic dimensions. The
|
|
69
|
+
block-root `className` remains on its StyleFlow wrapper while `imageClassName`
|
|
70
|
+
targets the image. Loading defaults to `lazy`, decoding to `async` and fetch
|
|
71
|
+
priority to `auto`; invalid stored values fail closed to those defaults.
|
|
72
|
+
|
|
73
|
+
Projects can add renderers for project-owned Gutenberg blocks without copying
|
|
74
|
+
or patching the core renderer. Pass a `FlowpressBlockRenderers` map keyed by the
|
|
75
|
+
full block name; the registry is propagated through every recursive rendering
|
|
76
|
+
path, including imported `sf/template` content and resolved query-item
|
|
77
|
+
templates.
|
|
78
|
+
|
|
79
|
+
```astro
|
|
80
|
+
---
|
|
81
|
+
import GutenbergRenderer from "@styleflow.app/flowpress-astro-core/GutenbergRenderer.astro";
|
|
82
|
+
import type {
|
|
83
|
+
FlowpressBlockRenderers
|
|
84
|
+
} from "@styleflow.app/flowpress-astro-core";
|
|
85
|
+
import ProjectTimelineBlock from "./ProjectTimelineBlock.astro";
|
|
86
|
+
|
|
87
|
+
const blockRenderers = {
|
|
88
|
+
"sf/timeline": ProjectTimelineBlock
|
|
89
|
+
} satisfies FlowpressBlockRenderers;
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
<GutenbergRenderer blocks={blocks} blockRenderers={blockRenderers} />
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Each custom renderer receives the original `block` and optional StyleFlow
|
|
96
|
+
`contract`. Its default slot contains recursively rendered child blocks, so a
|
|
97
|
+
container override can place `<slot />` where its children belong.
|
|
98
|
+
|
|
99
|
+
Projects provide their SVG implementation through the typed `iconRenderer`
|
|
100
|
+
prop. It is used by `sf/icon`, propagated through all recursive paths and used
|
|
101
|
+
for optional button icons in `before` or `after` position.
|
|
102
|
+
|
|
103
|
+
```astro
|
|
104
|
+
---
|
|
105
|
+
import GutenbergRenderer from "@styleflow.app/flowpress-astro-core/GutenbergRenderer.astro";
|
|
106
|
+
import ProjectIcon from "@sf/ui/Icon.astro";
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
<GutenbergRenderer blocks={blocks} iconRenderer={ProjectIcon} />
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
`sf/code` is deliberately a trusted-code boundary, not a sandbox. The renderer
|
|
113
|
+
emits its complete HTML fragment (including style, script, iframe and embed
|
|
114
|
+
elements) in the main document only when the serialized block contains the
|
|
115
|
+
server-issued boolean `codeApproved=true`. The WordPress editor must never
|
|
116
|
+
execute the document, and consumers must not synthesize this flag.
|
|
117
|
+
|
|
118
|
+
`sf/number-spinner` renders prefix, target number and suffix with independent
|
|
119
|
+
semantic tags and StyleFlow text coordinates. Its numeric value counts from
|
|
120
|
+
zero only when it enters the viewport. The final target remains available to
|
|
121
|
+
assistive technology and without JavaScript; `prefers-reduced-motion: reduce`
|
|
122
|
+
skips the animation entirely. Browsers without viewport observation keep the
|
|
123
|
+
server-rendered final value instead of guessing when to start.
|
|
41
124
|
|
|
42
125
|
## Release
|
|
43
126
|
|
|
@@ -59,5 +142,9 @@ The TypeScript template-hierarchy suite is self-contained. Its additional PHP
|
|
|
59
142
|
parity test runs when the sibling `flowpress-wordpress-core` checkout is
|
|
60
143
|
available locally and is skipped in a standalone package checkout.
|
|
61
144
|
|
|
145
|
+
Publish v1 betas on `next` only after Components and WordPress Core are
|
|
146
|
+
available. Inspect `npm view`, `gitHead`, integrity and the downloaded tarball.
|
|
147
|
+
Promote to `latest` only after the DMO canary and rollback gate.
|
|
148
|
+
|
|
62
149
|
Release activity reports are indexed in
|
|
63
150
|
[`docs/activities/README.md`](docs/activities/README.md).
|
|
@@ -6,12 +6,14 @@ interface Props {
|
|
|
6
6
|
lang?: string;
|
|
7
7
|
seo?: SfSeo | null;
|
|
8
8
|
siteName?: string;
|
|
9
|
+
theme?: string;
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
const {
|
|
12
13
|
lang = "it",
|
|
13
14
|
seo = null,
|
|
14
|
-
siteName = import.meta.env.PROJECT_NAME ?? "FlowPress"
|
|
15
|
+
siteName = import.meta.env.PROJECT_NAME ?? "FlowPress",
|
|
16
|
+
theme
|
|
15
17
|
} = Astro.props;
|
|
16
18
|
const title = seo?.title || siteName;
|
|
17
19
|
const description = seo?.description || "";
|
|
@@ -42,10 +44,13 @@ const jsonLd = seo?.jsonLd ? JSON.stringify(seo.jsonLd) : null;
|
|
|
42
44
|
{seo?.twitter?.image?.url && <meta name="twitter:image" content={seo.twitter.image.url} />}
|
|
43
45
|
{seo?.twitter?.image?.alt && <meta name="twitter:image:alt" content={seo.twitter.image.alt} />}
|
|
44
46
|
{jsonLd && <script type="application/ld+json" set:html={jsonLd} />}
|
|
47
|
+
<slot name="head-csp" />
|
|
48
|
+
<slot name="head-consent" />
|
|
49
|
+
<slot name="head-tag-manager" />
|
|
45
50
|
<slot name="head" />
|
|
46
51
|
</head>
|
|
47
52
|
<body>
|
|
48
|
-
<S.main
|
|
53
|
+
<S.main theme={theme} surface="default" layoutRole="none" density="regular">
|
|
49
54
|
<slot />
|
|
50
55
|
</S.main>
|
|
51
56
|
</body>
|
|
@@ -11,7 +11,7 @@ const hasGutembergBlocks = Array.isArray(content?.contentBlocks) && content.cont
|
|
|
11
11
|
//TODO SET GLOBAL LOCAL ON ENV
|
|
12
12
|
---
|
|
13
13
|
|
|
14
|
-
<BaseLayout lang={route.locale || "it"} seo={route.seo}>
|
|
14
|
+
<BaseLayout lang={route.locale || "it"} seo={route.seo} theme={styleflowContract?.defaultTheme}>
|
|
15
15
|
|
|
16
16
|
{hasGutembergBlocks ? (
|
|
17
17
|
<GutenbergRenderer
|
|
@@ -19,9 +19,9 @@ const hasGutembergBlocks = Array.isArray(content?.contentBlocks) && content.cont
|
|
|
19
19
|
contract={styleflowContract}
|
|
20
20
|
/>
|
|
21
21
|
) : (
|
|
22
|
-
<S.section
|
|
23
|
-
<S.h1
|
|
24
|
-
{content?.excerpt && <S.p
|
|
22
|
+
<S.section surface="default" layoutRole="section" density="regular">
|
|
23
|
+
<S.h1>{isNotFound ? "Pagina non trovata" : title}</S.h1>
|
|
24
|
+
{content?.excerpt && <S.p>{content.excerpt}</S.p>}
|
|
25
25
|
{Array.isArray(content?.contentBlocks) && content.contentBlocks.length > 0 ? (
|
|
26
26
|
<GutenbergRenderer
|
|
27
27
|
blocks={content.contentBlocks as import("../types.js").FlowpressBlock[]}
|
|
@@ -29,11 +29,9 @@ const hasGutembergBlocks = Array.isArray(content?.contentBlocks) && content.cont
|
|
|
29
29
|
/>
|
|
30
30
|
) : content?.content && (
|
|
31
31
|
<S.div
|
|
32
|
-
tone="neutral"
|
|
33
|
-
intensity={1}
|
|
34
32
|
surface="default"
|
|
35
33
|
layoutRole="stack"
|
|
36
|
-
density="
|
|
34
|
+
density="regular"
|
|
37
35
|
set:html={String(content.content)}
|
|
38
36
|
/>
|
|
39
37
|
)}
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
---
|
|
2
2
|
import S from "@styleflow.app/astro/S.astro";
|
|
3
|
+
import type {
|
|
4
|
+
FlowpressBlockRenderers,
|
|
5
|
+
FlowpressIconRenderer
|
|
6
|
+
} from "../block-renderers.js";
|
|
3
7
|
import type { StyleflowContract } from "../styleflow.js";
|
|
4
8
|
import type { FlowpressBlock } from "../types.js";
|
|
5
9
|
import ColumnBlock from "./blocks/ColumnBlock.astro";
|
|
6
10
|
import FormBlock from "./blocks/FormBlock.astro";
|
|
7
11
|
import GridBlock from "./blocks/GridBlock.astro";
|
|
8
12
|
import HeroBlock from "./blocks/HeroBlock.astro";
|
|
13
|
+
import ImageBlock from "./blocks/ImageBlock.astro";
|
|
9
14
|
import ListItemFallback from "./blocks/ListItemFallback.astro";
|
|
15
|
+
import NumberSpinnerBlock from "./blocks/NumberSpinnerBlock.astro";
|
|
10
16
|
import TextBlock from "./blocks/TextBlock.astro";
|
|
11
17
|
import {
|
|
12
18
|
blockHtmlAttributes,
|
|
@@ -19,9 +25,11 @@ import {
|
|
|
19
25
|
interface Props {
|
|
20
26
|
blocks: FlowpressBlock[];
|
|
21
27
|
contract?: StyleflowContract;
|
|
28
|
+
blockRenderers?: FlowpressBlockRenderers;
|
|
29
|
+
iconRenderer?: FlowpressIconRenderer;
|
|
22
30
|
}
|
|
23
31
|
|
|
24
|
-
const { blocks, contract } = Astro.props;
|
|
32
|
+
const { blocks, contract, blockRenderers = {}, iconRenderer: IconRenderer } = Astro.props;
|
|
25
33
|
|
|
26
34
|
function youtubeEmbedUrl(value: unknown): string | null {
|
|
27
35
|
try {
|
|
@@ -46,11 +54,30 @@ function youtubeEmbedUrl(value: unknown): string | null {
|
|
|
46
54
|
const children = block.innerBlocks ?? [];
|
|
47
55
|
const semantic = semanticProps(block.attributes, contract);
|
|
48
56
|
const htmlAttributes = blockHtmlAttributes(block.attributes);
|
|
57
|
+
const CustomBlockRenderer = blockRenderers[block.name];
|
|
58
|
+
|
|
59
|
+
if (CustomBlockRenderer) {
|
|
60
|
+
return (
|
|
61
|
+
<CustomBlockRenderer block={block} contract={contract}>
|
|
62
|
+
<Astro.self
|
|
63
|
+
blocks={children}
|
|
64
|
+
contract={contract}
|
|
65
|
+
blockRenderers={blockRenderers}
|
|
66
|
+
iconRenderer={IconRenderer}
|
|
67
|
+
/>
|
|
68
|
+
</CustomBlockRenderer>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
49
71
|
|
|
50
72
|
if (name === "hero") {
|
|
51
73
|
return (
|
|
52
74
|
<HeroBlock block={block} contract={contract}>
|
|
53
|
-
<Astro.self
|
|
75
|
+
<Astro.self
|
|
76
|
+
blocks={children}
|
|
77
|
+
contract={contract}
|
|
78
|
+
blockRenderers={blockRenderers}
|
|
79
|
+
iconRenderer={IconRenderer}
|
|
80
|
+
/>
|
|
54
81
|
</HeroBlock>
|
|
55
82
|
);
|
|
56
83
|
}
|
|
@@ -58,7 +85,12 @@ function youtubeEmbedUrl(value: unknown): string | null {
|
|
|
58
85
|
if (name === "grid") {
|
|
59
86
|
return (
|
|
60
87
|
<GridBlock block={block} contract={contract}>
|
|
61
|
-
<Astro.self
|
|
88
|
+
<Astro.self
|
|
89
|
+
blocks={children}
|
|
90
|
+
contract={contract}
|
|
91
|
+
blockRenderers={blockRenderers}
|
|
92
|
+
iconRenderer={IconRenderer}
|
|
93
|
+
/>
|
|
62
94
|
</GridBlock>
|
|
63
95
|
);
|
|
64
96
|
}
|
|
@@ -66,11 +98,20 @@ function youtubeEmbedUrl(value: unknown): string | null {
|
|
|
66
98
|
if (name === "column") {
|
|
67
99
|
return (
|
|
68
100
|
<ColumnBlock block={block} contract={contract}>
|
|
69
|
-
<Astro.self
|
|
101
|
+
<Astro.self
|
|
102
|
+
blocks={children}
|
|
103
|
+
contract={contract}
|
|
104
|
+
blockRenderers={blockRenderers}
|
|
105
|
+
iconRenderer={IconRenderer}
|
|
106
|
+
/>
|
|
70
107
|
</ColumnBlock>
|
|
71
108
|
);
|
|
72
109
|
}
|
|
73
110
|
|
|
111
|
+
if (name === "number-spinner") {
|
|
112
|
+
return <NumberSpinnerBlock block={block} contract={contract} />;
|
|
113
|
+
}
|
|
114
|
+
|
|
74
115
|
if ([
|
|
75
116
|
"heading",
|
|
76
117
|
"rich-text",
|
|
@@ -83,7 +124,12 @@ function youtubeEmbedUrl(value: unknown): string | null {
|
|
|
83
124
|
].includes(name)) {
|
|
84
125
|
return (
|
|
85
126
|
<TextBlock block={block} contract={contract} hasChildren={children.length > 0}>
|
|
86
|
-
<Astro.self
|
|
127
|
+
<Astro.self
|
|
128
|
+
blocks={children}
|
|
129
|
+
contract={contract}
|
|
130
|
+
blockRenderers={blockRenderers}
|
|
131
|
+
iconRenderer={IconRenderer}
|
|
132
|
+
/>
|
|
87
133
|
</TextBlock>
|
|
88
134
|
);
|
|
89
135
|
}
|
|
@@ -92,40 +138,71 @@ function youtubeEmbedUrl(value: unknown): string | null {
|
|
|
92
138
|
const href = linkValue(block, "href") || "#";
|
|
93
139
|
const link = objectValue(block, "href");
|
|
94
140
|
const opensInNewTab = Boolean(link.opensInNewTab);
|
|
141
|
+
const icon = String(block.attributes.icon ?? "").trim();
|
|
142
|
+
const iconPosition = ["after", "right"].includes(
|
|
143
|
+
String(block.attributes.iconPosition ?? "before")
|
|
144
|
+
) ? "after" : "before";
|
|
145
|
+
const buttonSemantic = semanticProps(block.attributes, contract, {
|
|
146
|
+
layoutRole: "control",
|
|
147
|
+
density: "regular"
|
|
148
|
+
});
|
|
149
|
+
const buttonIcon = IconRenderer && icon ? (
|
|
150
|
+
<IconRenderer name={icon} display="inline" size={4} />
|
|
151
|
+
) : null;
|
|
95
152
|
return (
|
|
96
153
|
<S
|
|
97
154
|
as="a"
|
|
98
155
|
href={href}
|
|
99
156
|
target={opensInNewTab ? "_blank" : undefined}
|
|
100
157
|
rel={opensInNewTab ? "noopener noreferrer" : undefined}
|
|
101
|
-
{...
|
|
158
|
+
{...buttonSemantic}
|
|
102
159
|
{...htmlAttributes}
|
|
103
160
|
>
|
|
161
|
+
{iconPosition === "before" ? buttonIcon : null}
|
|
104
162
|
{textValue(block, "label")}
|
|
163
|
+
{iconPosition === "after" ? buttonIcon : null}
|
|
105
164
|
</S>
|
|
106
165
|
);
|
|
107
166
|
}
|
|
108
167
|
|
|
109
|
-
if (name === "
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
168
|
+
if (name === "icon") {
|
|
169
|
+
const icon = String(block.attributes.icon ?? "").trim();
|
|
170
|
+
const display = block.attributes.display === "block" ? "block" : "inline";
|
|
171
|
+
const numericSize = Number(block.attributes.size ?? 4);
|
|
172
|
+
const size = Number.isFinite(numericSize) && numericSize > 0
|
|
173
|
+
? numericSize
|
|
174
|
+
: 4;
|
|
175
|
+
const iconSemantic = semanticProps({
|
|
176
|
+
foreground: block.attributes.foreground,
|
|
177
|
+
tone: block.attributes.tone,
|
|
178
|
+
intensity: block.attributes.intensity
|
|
179
|
+
}, contract, {});
|
|
180
|
+
return IconRenderer && icon ? (
|
|
181
|
+
<IconRenderer
|
|
182
|
+
name={icon}
|
|
183
|
+
display={display}
|
|
184
|
+
size={size}
|
|
185
|
+
label={String(block.attributes.label ?? "").trim() || undefined}
|
|
186
|
+
{...iconSemantic}
|
|
187
|
+
{...htmlAttributes}
|
|
188
|
+
/>
|
|
189
|
+
) : null;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (name === "code") {
|
|
193
|
+
return block.attributes.codeApproved === true ? (
|
|
194
|
+
<div
|
|
195
|
+
data-flowpress-trusted-code
|
|
196
|
+
{...htmlAttributes}
|
|
197
|
+
set:html={String(block.attributes.document ?? "")}
|
|
198
|
+
/>
|
|
126
199
|
) : null;
|
|
127
200
|
}
|
|
128
201
|
|
|
202
|
+
if (name === "image") {
|
|
203
|
+
return <ImageBlock block={block} contract={contract} />;
|
|
204
|
+
}
|
|
205
|
+
|
|
129
206
|
if (name === "gallery") {
|
|
130
207
|
const images = Array.isArray(block.attributes.images)
|
|
131
208
|
? block.attributes.images as Array<Record<string, unknown>>
|
|
@@ -211,7 +288,12 @@ function youtubeEmbedUrl(value: unknown): string | null {
|
|
|
211
288
|
{items.map((item, index) => (
|
|
212
289
|
<S as="li">
|
|
213
290
|
{resolvedBlocks[index]?.length ? (
|
|
214
|
-
<Astro.self
|
|
291
|
+
<Astro.self
|
|
292
|
+
blocks={resolvedBlocks[index]}
|
|
293
|
+
contract={contract}
|
|
294
|
+
blockRenderers={blockRenderers}
|
|
295
|
+
iconRenderer={IconRenderer}
|
|
296
|
+
/>
|
|
215
297
|
) : (
|
|
216
298
|
<ListItemFallback item={item} />
|
|
217
299
|
)}
|
|
@@ -224,10 +306,20 @@ function youtubeEmbedUrl(value: unknown): string | null {
|
|
|
224
306
|
if (name === "template") {
|
|
225
307
|
return Object.keys(htmlAttributes).length > 0 ? (
|
|
226
308
|
<div {...htmlAttributes}>
|
|
227
|
-
<Astro.self
|
|
309
|
+
<Astro.self
|
|
310
|
+
blocks={children}
|
|
311
|
+
contract={contract}
|
|
312
|
+
blockRenderers={blockRenderers}
|
|
313
|
+
iconRenderer={IconRenderer}
|
|
314
|
+
/>
|
|
228
315
|
</div>
|
|
229
316
|
) : (
|
|
230
|
-
<Astro.self
|
|
317
|
+
<Astro.self
|
|
318
|
+
blocks={children}
|
|
319
|
+
contract={contract}
|
|
320
|
+
blockRenderers={blockRenderers}
|
|
321
|
+
iconRenderer={IconRenderer}
|
|
322
|
+
/>
|
|
231
323
|
);
|
|
232
324
|
}
|
|
233
325
|
|
|
@@ -240,7 +332,12 @@ function youtubeEmbedUrl(value: unknown): string | null {
|
|
|
240
332
|
<S as="div" {...semantic} {...htmlAttributes}>
|
|
241
333
|
<details>
|
|
242
334
|
<summary>{String(block.attributes.title ?? "")}</summary>
|
|
243
|
-
<Astro.self
|
|
335
|
+
<Astro.self
|
|
336
|
+
blocks={children}
|
|
337
|
+
contract={contract}
|
|
338
|
+
blockRenderers={blockRenderers}
|
|
339
|
+
iconRenderer={IconRenderer}
|
|
340
|
+
/>
|
|
244
341
|
</details>
|
|
245
342
|
</S>
|
|
246
343
|
);
|
|
@@ -291,7 +388,14 @@ function youtubeEmbedUrl(value: unknown): string | null {
|
|
|
291
388
|
<S as={layoutTag} {...semantic} {...htmlAttributes}>
|
|
292
389
|
{block.innerHtml && children.length === 0
|
|
293
390
|
? <S as="div" set:html={block.innerHtml} />
|
|
294
|
-
:
|
|
391
|
+
: (
|
|
392
|
+
<Astro.self
|
|
393
|
+
blocks={children}
|
|
394
|
+
contract={contract}
|
|
395
|
+
blockRenderers={blockRenderers}
|
|
396
|
+
iconRenderer={IconRenderer}
|
|
397
|
+
/>
|
|
398
|
+
)}
|
|
295
399
|
</S>
|
|
296
400
|
);
|
|
297
401
|
})}
|
|
@@ -25,7 +25,7 @@ const align = ["start", "center", "end", "stretch"].includes(
|
|
|
25
25
|
const semantic = semanticProps(block.attributes, contract, {
|
|
26
26
|
surface: "default",
|
|
27
27
|
layoutRole: "container",
|
|
28
|
-
density: "
|
|
28
|
+
density: "regular"
|
|
29
29
|
});
|
|
30
30
|
const htmlAttributes = blockHtmlAttributes(block.attributes, "flowpress-grid");
|
|
31
31
|
const style = [
|
|
@@ -4,6 +4,7 @@ import type { FlowpressBlock } from "../../types.js";
|
|
|
4
4
|
import type { StyleflowContract } from "../../styleflow.js";
|
|
5
5
|
import {
|
|
6
6
|
blockHtmlAttributes,
|
|
7
|
+
heroObjectPosition,
|
|
7
8
|
objectValue,
|
|
8
9
|
semanticProps
|
|
9
10
|
} from "./helpers.js";
|
|
@@ -18,7 +19,7 @@ const attributes = block.attributes;
|
|
|
18
19
|
const semantic = semanticProps(attributes, contract, {
|
|
19
20
|
surface: "default",
|
|
20
21
|
layoutRole: "region",
|
|
21
|
-
density: "
|
|
22
|
+
density: "regular"
|
|
22
23
|
});
|
|
23
24
|
const htmlAttributes = blockHtmlAttributes(attributes, "flowpress-hero");
|
|
24
25
|
const backgroundType = ["color", "image", "video"].includes(
|
|
@@ -28,6 +29,7 @@ const image = objectValue(block, "backgroundImage");
|
|
|
28
29
|
const video = objectValue(block, "backgroundVideo");
|
|
29
30
|
const imageSrc = String(image.url ?? image.sourceUrl ?? "");
|
|
30
31
|
const videoSrc = String(video.url ?? video.sourceUrl ?? video.mediaItemUrl ?? "");
|
|
32
|
+
const objectPosition = heroObjectPosition(attributes.objectPosition);
|
|
31
33
|
const scrimEnabled = Boolean(attributes.scrimEnabled) && backgroundType !== "color";
|
|
32
34
|
const scrimIntensity = Math.min(10, Math.max(1, Number(attributes.scrimIntensity) || 6));
|
|
33
35
|
---
|
|
@@ -36,7 +38,10 @@ const scrimIntensity = Math.min(10, Math.max(1, Number(attributes.scrimIntensity
|
|
|
36
38
|
as="section"
|
|
37
39
|
{...semantic}
|
|
38
40
|
{...htmlAttributes}
|
|
39
|
-
style={
|
|
41
|
+
style={[
|
|
42
|
+
`--flowpress-hero-object-position:${objectPosition}`,
|
|
43
|
+
`--flowpress-scrim-opacity:${scrimIntensity * 10}%`
|
|
44
|
+
].join(";")}
|
|
40
45
|
>
|
|
41
46
|
{backgroundType === "image" && imageSrc ? (
|
|
42
47
|
<img
|
|
@@ -87,6 +92,7 @@ const scrimIntensity = Math.min(10, Math.max(1, Number(attributes.scrimIntensity
|
|
|
87
92
|
.flowpress-hero__media {
|
|
88
93
|
z-index: -2;
|
|
89
94
|
object-fit: cover;
|
|
95
|
+
object-position: var(--flowpress-hero-object-position);
|
|
90
96
|
}
|
|
91
97
|
|
|
92
98
|
.flowpress-hero__scrim {
|