@styleflow.app/flowpress-astro-core 0.2.0-beta.0 → 0.2.0-beta.10
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 +53 -0
- package/README.md +20 -1
- package/dist/astro/BaseLayout.astro +4 -1
- package/dist/astro/DefaultTemplate.astro +31 -21
- package/dist/astro/GutenbergRenderer.astro +93 -120
- package/dist/astro/blocks/ColumnBlock.astro +83 -0
- package/dist/astro/blocks/FormBlock.astro +233 -0
- package/dist/astro/blocks/GridBlock.astro +70 -0
- package/dist/astro/blocks/HeroBlock.astro +113 -0
- package/dist/astro/blocks/ListItemFallback.astro +28 -0
- package/dist/astro/blocks/TextBlock.astro +79 -0
- package/dist/astro/blocks/helpers.d.ts +24 -0
- package/dist/astro/blocks/helpers.d.ts.map +1 -0
- package/dist/astro/blocks/helpers.js +104 -0
- package/dist/astro/blocks/helpers.js.map +1 -0
- package/dist/astro/blocks/helpers.ts +143 -0
- package/dist/client.js.map +1 -1
- package/dist/env.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/style-classes.d.ts +9 -0
- package/dist/style-classes.d.ts.map +1 -0
- package/dist/style-classes.js +47 -0
- package/dist/style-classes.js.map +1 -0
- package/dist/styleflow.d.ts.map +1 -1
- package/dist/styleflow.js +12 -0
- package/dist/styleflow.js.map +1 -1
- package/dist/template-hierarchy.js.map +1 -1
- package/dist/templates.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +4 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,58 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
## 0.2.0-beta.10
|
|
6
|
+
|
|
7
|
+
- Preserved intentionally empty core Gutenberg paragraphs as visible line
|
|
8
|
+
breaks, including paragraphs resolved through FlowPress templates.
|
|
9
|
+
- Exported safe recursive collection of CMS-authored block class names for
|
|
10
|
+
Tailwind `@source inline()` generation.
|
|
11
|
+
|
|
12
|
+
## 0.2.0-beta.9
|
|
13
|
+
|
|
14
|
+
- Preserved repeated Gutenberg RichText spacing across `<br>` sequences,
|
|
15
|
+
legacy empty paragraphs and literal double/triple newlines.
|
|
16
|
+
|
|
17
|
+
## 0.2.0-beta.8
|
|
18
|
+
|
|
19
|
+
- Applied Gutenberg `anchor` and `className` attributes to each rendered
|
|
20
|
+
block root, merging custom classes with the required FlowPress renderer
|
|
21
|
+
classes and preserving transparent template blocks when neither is set.
|
|
22
|
+
|
|
23
|
+
## 0.2.0-beta.5
|
|
24
|
+
|
|
25
|
+
- Split Gutenberg rendering into dedicated hero, responsive layout, text,
|
|
26
|
+
list/template and CF7 modules.
|
|
27
|
+
- Added semantic foreground/modifier propagation and core text block
|
|
28
|
+
rendering.
|
|
29
|
+
- Added responsive 12-column inheritance, token-based partial containers,
|
|
30
|
+
hero media/scrim rendering and reduced-motion handling.
|
|
31
|
+
- Added repeated query-item template rendering with the previous plain-list
|
|
32
|
+
fallback.
|
|
33
|
+
- Added an accessible Turnstile/CF7 browser form compatible with static and
|
|
34
|
+
server builds.
|
|
35
|
+
- Added Astro component checking to the release gate.
|
|
36
|
+
|
|
37
|
+
## 0.2.0-beta.4
|
|
38
|
+
|
|
39
|
+
- Updated the exact StyleFlow Astro dependency to `0.3.0-beta.2`.
|
|
40
|
+
- Validated the npm Trusted Publisher path with a new OIDC-only publication.
|
|
41
|
+
|
|
42
|
+
## 0.2.0-beta.3
|
|
43
|
+
|
|
44
|
+
- Updated the exact StyleFlow Astro dependency to `0.3.0-beta.1`.
|
|
45
|
+
- Made the optional PHP parity test compatible with standalone CI checkouts;
|
|
46
|
+
the TypeScript hierarchy tests remain mandatory.
|
|
47
|
+
|
|
48
|
+
## 0.2.0-beta.2
|
|
49
|
+
|
|
50
|
+
- Made Gutenberg color axes inherit by omission until authors explicitly set
|
|
51
|
+
a valid tone or intensity.
|
|
52
|
+
- Applied explicit color axes to heading, button, and quote renderers.
|
|
53
|
+
- Embedded source contents in generated sourcemaps for clean Astro/Vite
|
|
54
|
+
diagnostics when the package is installed from npm.
|
|
55
|
+
|
|
3
56
|
## 0.2.0-beta.0
|
|
4
57
|
|
|
5
58
|
- Made the repository buildable outside the original monorepo.
|
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ 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.2.0-beta.
|
|
12
|
+
pnpm add @styleflow.app/flowpress-astro-core@0.2.0-beta.10
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
Prereleases are pinned exactly. The package expects Astro 7 or newer and the
|
|
@@ -30,6 +30,18 @@ For local Cloudflare development, prefer an HTTP loopback endpoint dedicated
|
|
|
30
30
|
to server-to-server traffic. Keep the public HTTPS backend URL separate for
|
|
31
31
|
canonical URL rewriting.
|
|
32
32
|
|
|
33
|
+
`GutenbergRenderer.astro` renders the responsive hero/grid/column contract,
|
|
34
|
+
StyleFlow text axes and resolved content/query templates. CF7 schemas
|
|
35
|
+
serialized by WordPress become accessible client forms that submit directly
|
|
36
|
+
to the protected FlowPress REST endpoint, so the same component works in
|
|
37
|
+
static and server output modes. Gutenberg `anchor` and `className` attributes
|
|
38
|
+
are applied to the rendered block root as HTML `id` and `class`. Rich Text
|
|
39
|
+
preserves intentional double, triple and longer line breaks, including content
|
|
40
|
+
saved by older editor versions as empty paragraphs. The exported
|
|
41
|
+
`collectFlowpressClassNames()` helper recursively extracts safe class tokens
|
|
42
|
+
from rendered CMS blocks so consumers can generate Tailwind
|
|
43
|
+
`@source inline()` directives before compiling their frontend CSS.
|
|
44
|
+
|
|
33
45
|
## Release
|
|
34
46
|
|
|
35
47
|
```bash
|
|
@@ -45,3 +57,10 @@ release and in CI.
|
|
|
45
57
|
The first publish requires npm organization access and 2FA. Subsequent
|
|
46
58
|
publishes use `.github/workflows/publish.yml` with npm trusted publishing
|
|
47
59
|
(OIDC); no long-lived publish token belongs in this repository.
|
|
60
|
+
|
|
61
|
+
The TypeScript template-hierarchy suite is self-contained. Its additional PHP
|
|
62
|
+
parity test runs when the sibling `flowpress-wordpress-core` checkout is
|
|
63
|
+
available locally and is skipped in a standalone package checkout.
|
|
64
|
+
|
|
65
|
+
Release activity reports are indexed in
|
|
66
|
+
[`docs/activities/README.md`](docs/activities/README.md).
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
import type { SfSeo } from "../types.js";
|
|
3
|
+
import S from "@styleflow.app/astro/S.astro";
|
|
3
4
|
|
|
4
5
|
interface Props {
|
|
5
6
|
lang?: string;
|
|
@@ -44,6 +45,8 @@ const jsonLd = seo?.jsonLd ? JSON.stringify(seo.jsonLd) : null;
|
|
|
44
45
|
<slot name="head" />
|
|
45
46
|
</head>
|
|
46
47
|
<body>
|
|
47
|
-
<
|
|
48
|
+
<S.main tone="neutral" intensity={1} surface="default" layoutRole="none" density="md">
|
|
49
|
+
<slot />
|
|
50
|
+
</S.main>
|
|
48
51
|
</body>
|
|
49
52
|
</html>
|
|
@@ -7,28 +7,38 @@ import type { FlowpressTemplateProps } from "../types.js";
|
|
|
7
7
|
const { route, content, styleflowContract } = Astro.props as FlowpressTemplateProps;
|
|
8
8
|
const title = content?.title || route.seo?.title || "Contenuto";
|
|
9
9
|
const isNotFound = route.queryType === "404" || route.status === 404;
|
|
10
|
+
const hasGutembergBlocks = Array.isArray(content?.contentBlocks) && content.contentBlocks.length > 0 ? true : false;
|
|
11
|
+
//TODO SET GLOBAL LOCAL ON ENV
|
|
10
12
|
---
|
|
11
13
|
|
|
12
14
|
<BaseLayout lang={route.locale || "it"} seo={route.seo}>
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
15
|
+
|
|
16
|
+
{hasGutembergBlocks ? (
|
|
17
|
+
<GutenbergRenderer
|
|
18
|
+
blocks={content?.contentBlocks as import("../types.js").FlowpressBlock[]}
|
|
19
|
+
contract={styleflowContract}
|
|
20
|
+
/>
|
|
21
|
+
) : (
|
|
22
|
+
<S.section tone="neutral" intensity={1} surface="default" layoutRole="section" density="md">
|
|
23
|
+
<S.h1 ty="heading" v="1" w="strong">{isNotFound ? "Pagina non trovata" : title}</S.h1>
|
|
24
|
+
{content?.excerpt && <S.p ty="body" v="md" w="default">{content.excerpt}</S.p>}
|
|
25
|
+
{Array.isArray(content?.contentBlocks) && content.contentBlocks.length > 0 ? (
|
|
26
|
+
<GutenbergRenderer
|
|
27
|
+
blocks={content.contentBlocks as import("../types.js").FlowpressBlock[]}
|
|
28
|
+
contract={styleflowContract}
|
|
29
|
+
/>
|
|
30
|
+
) : content?.content && (
|
|
31
|
+
<S.div
|
|
32
|
+
tone="neutral"
|
|
33
|
+
intensity={1}
|
|
34
|
+
surface="default"
|
|
35
|
+
layoutRole="stack"
|
|
36
|
+
density="md"
|
|
37
|
+
set:html={String(content.content)}
|
|
38
|
+
/>
|
|
39
|
+
)}
|
|
40
|
+
</S.section>
|
|
41
|
+
)}
|
|
42
|
+
|
|
43
|
+
|
|
34
44
|
</BaseLayout>
|
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
---
|
|
2
2
|
import S from "@styleflow.app/astro/S.astro";
|
|
3
|
-
import {
|
|
4
|
-
resolveDynamicStyleflowProps,
|
|
5
|
-
type StyleflowContract
|
|
6
|
-
} from "../styleflow.js";
|
|
3
|
+
import type { StyleflowContract } from "../styleflow.js";
|
|
7
4
|
import type { FlowpressBlock } from "../types.js";
|
|
5
|
+
import ColumnBlock from "./blocks/ColumnBlock.astro";
|
|
6
|
+
import FormBlock from "./blocks/FormBlock.astro";
|
|
7
|
+
import GridBlock from "./blocks/GridBlock.astro";
|
|
8
|
+
import HeroBlock from "./blocks/HeroBlock.astro";
|
|
9
|
+
import ListItemFallback from "./blocks/ListItemFallback.astro";
|
|
10
|
+
import TextBlock from "./blocks/TextBlock.astro";
|
|
11
|
+
import {
|
|
12
|
+
blockHtmlAttributes,
|
|
13
|
+
linkValue,
|
|
14
|
+
objectValue,
|
|
15
|
+
semanticProps,
|
|
16
|
+
textValue
|
|
17
|
+
} from "./blocks/helpers.js";
|
|
8
18
|
|
|
9
19
|
interface Props {
|
|
10
20
|
blocks: FlowpressBlock[];
|
|
@@ -13,59 +23,6 @@ interface Props {
|
|
|
13
23
|
|
|
14
24
|
const { blocks, contract } = Astro.props;
|
|
15
25
|
|
|
16
|
-
const defaults = {
|
|
17
|
-
tone: "neutral",
|
|
18
|
-
intensity: 1,
|
|
19
|
-
surface: "default",
|
|
20
|
-
layoutRole: "stack",
|
|
21
|
-
density: "md"
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
function styleflowProps(attributes: Record<string, unknown>) {
|
|
25
|
-
if (!contract) return defaults;
|
|
26
|
-
return resolveDynamicStyleflowProps(attributes, defaults, contract);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function dynamicProps(
|
|
30
|
-
attributes: Record<string, unknown>,
|
|
31
|
-
fallback: Record<string, unknown>
|
|
32
|
-
) {
|
|
33
|
-
if (!contract) return fallback;
|
|
34
|
-
return resolveDynamicStyleflowProps(attributes, fallback, contract);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function text(block: FlowpressBlock, key = "text"): string {
|
|
38
|
-
return String(block.attributes[key] ?? "");
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function objectValue(
|
|
42
|
-
block: FlowpressBlock,
|
|
43
|
-
key: string
|
|
44
|
-
): Record<string, unknown> {
|
|
45
|
-
const value = block.attributes[key];
|
|
46
|
-
return value && typeof value === "object" && !Array.isArray(value)
|
|
47
|
-
? value as Record<string, unknown>
|
|
48
|
-
: {};
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function linkValue(block: FlowpressBlock, key = "link"): string {
|
|
52
|
-
const value = block.attributes[key];
|
|
53
|
-
const href = typeof value === "string"
|
|
54
|
-
? value
|
|
55
|
-
: String(objectValue(block, key).url ?? "");
|
|
56
|
-
if (/^(?:\/(?!\/)|#|https?:\/\/|mailto:|tel:)/i.test(href)) {
|
|
57
|
-
return href;
|
|
58
|
-
}
|
|
59
|
-
return "";
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function itemImage(item: Record<string, unknown>): Record<string, unknown> {
|
|
63
|
-
const value = item.image;
|
|
64
|
-
return value && typeof value === "object" && !Array.isArray(value)
|
|
65
|
-
? value as Record<string, unknown>
|
|
66
|
-
: {};
|
|
67
|
-
}
|
|
68
|
-
|
|
69
26
|
function youtubeEmbedUrl(value: unknown): string | null {
|
|
70
27
|
try {
|
|
71
28
|
const url = new URL(String(value ?? ""));
|
|
@@ -87,50 +44,64 @@ function youtubeEmbedUrl(value: unknown): string | null {
|
|
|
87
44
|
{blocks.map((block) => {
|
|
88
45
|
const name = block.name.replace(/^sf\//, "");
|
|
89
46
|
const children = block.innerBlocks ?? [];
|
|
90
|
-
const semantic =
|
|
47
|
+
const semantic = semanticProps(block.attributes, contract);
|
|
48
|
+
const htmlAttributes = blockHtmlAttributes(block.attributes);
|
|
91
49
|
|
|
92
|
-
if (name === "
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
ty: "heading",
|
|
98
|
-
v: String(block.attributes.variant ?? level),
|
|
99
|
-
w: String(block.attributes.weight ?? "strong")
|
|
100
|
-
},
|
|
101
|
-
{ ty: "heading", v: String(level), w: "strong" }
|
|
50
|
+
if (name === "hero") {
|
|
51
|
+
return (
|
|
52
|
+
<HeroBlock block={block} contract={contract}>
|
|
53
|
+
<Astro.self blocks={children} contract={contract} />
|
|
54
|
+
</HeroBlock>
|
|
102
55
|
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (name === "grid") {
|
|
103
59
|
return (
|
|
104
|
-
<
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
set:html={text(block)}
|
|
108
|
-
/>
|
|
60
|
+
<GridBlock block={block} contract={contract}>
|
|
61
|
+
<Astro.self blocks={children} contract={contract} />
|
|
62
|
+
</GridBlock>
|
|
109
63
|
);
|
|
110
64
|
}
|
|
111
65
|
|
|
112
|
-
if (name === "
|
|
113
|
-
|
|
114
|
-
|
|
66
|
+
if (name === "column") {
|
|
67
|
+
return (
|
|
68
|
+
<ColumnBlock block={block} contract={contract}>
|
|
69
|
+
<Astro.self blocks={children} contract={contract} />
|
|
70
|
+
</ColumnBlock>
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if ([
|
|
75
|
+
"heading",
|
|
76
|
+
"rich-text",
|
|
77
|
+
"quote",
|
|
78
|
+
"core/paragraph",
|
|
79
|
+
"core/heading",
|
|
80
|
+
"core/list",
|
|
81
|
+
"core/list-item",
|
|
82
|
+
"core/quote"
|
|
83
|
+
].includes(name)) {
|
|
84
|
+
return (
|
|
85
|
+
<TextBlock block={block} contract={contract} hasChildren={children.length > 0}>
|
|
86
|
+
<Astro.self blocks={children} contract={contract} />
|
|
87
|
+
</TextBlock>
|
|
88
|
+
);
|
|
115
89
|
}
|
|
116
90
|
|
|
117
91
|
if (name === "button") {
|
|
118
92
|
const href = linkValue(block, "href") || "#";
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
interactivePriority: String(
|
|
122
|
-
block.attributes.interactivePriority ?? "primary"
|
|
123
|
-
)
|
|
124
|
-
},
|
|
125
|
-
{ interactivePriority: "primary" }
|
|
126
|
-
);
|
|
93
|
+
const link = objectValue(block, "href");
|
|
94
|
+
const opensInNewTab = Boolean(link.opensInNewTab);
|
|
127
95
|
return (
|
|
128
96
|
<S
|
|
129
97
|
as="a"
|
|
130
98
|
href={href}
|
|
131
|
-
{
|
|
99
|
+
target={opensInNewTab ? "_blank" : undefined}
|
|
100
|
+
rel={opensInNewTab ? "noopener noreferrer" : undefined}
|
|
101
|
+
{...semantic}
|
|
102
|
+
{...htmlAttributes}
|
|
132
103
|
>
|
|
133
|
-
{
|
|
104
|
+
{textValue(block, "label")}
|
|
134
105
|
</S>
|
|
135
106
|
);
|
|
136
107
|
}
|
|
@@ -144,7 +115,7 @@ function youtubeEmbedUrl(value: unknown): string | null {
|
|
|
144
115
|
} | undefined;
|
|
145
116
|
const src = image?.url ?? image?.sourceUrl;
|
|
146
117
|
return src ? (
|
|
147
|
-
<S as="div" {...semantic}>
|
|
118
|
+
<S as="div" {...semantic} {...htmlAttributes}>
|
|
148
119
|
<img
|
|
149
120
|
src={src}
|
|
150
121
|
alt={String(block.attributes.alt ?? image?.alt ?? image?.altText ?? "")}
|
|
@@ -160,7 +131,7 @@ function youtubeEmbedUrl(value: unknown): string | null {
|
|
|
160
131
|
? block.attributes.images as Array<Record<string, unknown>>
|
|
161
132
|
: [];
|
|
162
133
|
return (
|
|
163
|
-
<S as="div" {...semantic}>
|
|
134
|
+
<S as="div" {...semantic} {...htmlAttributes}>
|
|
164
135
|
{images.map((image) => image.url ? (
|
|
165
136
|
<img
|
|
166
137
|
src={String(image.url)}
|
|
@@ -177,7 +148,7 @@ function youtubeEmbedUrl(value: unknown): string | null {
|
|
|
177
148
|
const video = objectValue(block, "video");
|
|
178
149
|
const poster = objectValue(block, "poster");
|
|
179
150
|
return video.url ? (
|
|
180
|
-
<S as="div" {...semantic}>
|
|
151
|
+
<S as="div" {...semantic} {...htmlAttributes}>
|
|
181
152
|
<video
|
|
182
153
|
src={String(video.url)}
|
|
183
154
|
poster={poster.url ? String(poster.url) : undefined}
|
|
@@ -193,7 +164,7 @@ function youtubeEmbedUrl(value: unknown): string | null {
|
|
|
193
164
|
if (name === "video-youtube") {
|
|
194
165
|
const embed = youtubeEmbedUrl(block.attributes.url);
|
|
195
166
|
return embed ? (
|
|
196
|
-
<S as="div" {...semantic}>
|
|
167
|
+
<S as="div" {...semantic} {...htmlAttributes}>
|
|
197
168
|
<iframe
|
|
198
169
|
src={embed}
|
|
199
170
|
title={String(block.attributes.title ?? "YouTube video")}
|
|
@@ -205,24 +176,19 @@ function youtubeEmbedUrl(value: unknown): string | null {
|
|
|
205
176
|
) : null;
|
|
206
177
|
}
|
|
207
178
|
|
|
208
|
-
if (name === "
|
|
179
|
+
if (name === "chip") {
|
|
209
180
|
return (
|
|
210
|
-
<
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
</blockquote>
|
|
181
|
+
<S as="span" {...semantic} {...htmlAttributes}>
|
|
182
|
+
{textValue(block, "label")}
|
|
183
|
+
</S>
|
|
214
184
|
);
|
|
215
185
|
}
|
|
216
186
|
|
|
217
|
-
if (name === "chip") {
|
|
218
|
-
return <S as="span" {...semantic}>{text(block, "label")}</S>;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
187
|
if (name === "card") {
|
|
222
188
|
const media = objectValue(block, "image");
|
|
223
189
|
const href = linkValue(block);
|
|
224
190
|
return (
|
|
225
|
-
<S as="article" {...semantic}>
|
|
191
|
+
<S as="article" {...semantic} {...htmlAttributes}>
|
|
226
192
|
{media.url && (
|
|
227
193
|
<img src={String(media.url)} alt={String(media.alt ?? "")} loading="lazy" />
|
|
228
194
|
)}
|
|
@@ -237,34 +203,41 @@ function youtubeEmbedUrl(value: unknown): string | null {
|
|
|
237
203
|
const items = Array.isArray(block.attributes.resolvedItems)
|
|
238
204
|
? block.attributes.resolvedItems as Array<Record<string, unknown>>
|
|
239
205
|
: [];
|
|
206
|
+
const resolvedBlocks = Array.isArray(block.attributes.resolvedBlocks)
|
|
207
|
+
? block.attributes.resolvedBlocks as FlowpressBlock[][]
|
|
208
|
+
: [];
|
|
240
209
|
return (
|
|
241
|
-
<S as="ul" {...semantic}>
|
|
242
|
-
{items.map((item) => (
|
|
210
|
+
<S as="ul" {...semantic} {...htmlAttributes}>
|
|
211
|
+
{items.map((item, index) => (
|
|
243
212
|
<S as="li">
|
|
244
|
-
{
|
|
245
|
-
<
|
|
246
|
-
src={String(itemImage(item).url)}
|
|
247
|
-
alt={String(itemImage(item).alt ?? "")}
|
|
248
|
-
loading="lazy"
|
|
249
|
-
decoding="async"
|
|
250
|
-
/>
|
|
251
|
-
)}
|
|
252
|
-
{item.uri ? (
|
|
253
|
-
<S as="a" href={String(item.uri)}>{String(item.title ?? "")}</S>
|
|
213
|
+
{resolvedBlocks[index]?.length ? (
|
|
214
|
+
<Astro.self blocks={resolvedBlocks[index]} contract={contract} />
|
|
254
215
|
) : (
|
|
255
|
-
<
|
|
216
|
+
<ListItemFallback item={item} />
|
|
256
217
|
)}
|
|
257
|
-
{item.excerpt && <S as="p">{String(item.excerpt)}</S>}
|
|
258
|
-
{item.priceHtml && <S as="div" set:html={String(item.priceHtml)} />}
|
|
259
218
|
</S>
|
|
260
219
|
))}
|
|
261
220
|
</S>
|
|
262
221
|
);
|
|
263
222
|
}
|
|
264
223
|
|
|
224
|
+
if (name === "template") {
|
|
225
|
+
return Object.keys(htmlAttributes).length > 0 ? (
|
|
226
|
+
<div {...htmlAttributes}>
|
|
227
|
+
<Astro.self blocks={children} contract={contract} />
|
|
228
|
+
</div>
|
|
229
|
+
) : (
|
|
230
|
+
<Astro.self blocks={children} contract={contract} />
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (name === "form") {
|
|
235
|
+
return <FormBlock block={block} contract={contract} />;
|
|
236
|
+
}
|
|
237
|
+
|
|
265
238
|
if (name === "accordion-item") {
|
|
266
239
|
return (
|
|
267
|
-
<S as="div" {...semantic}>
|
|
240
|
+
<S as="div" {...semantic} {...htmlAttributes}>
|
|
268
241
|
<details>
|
|
269
242
|
<summary>{String(block.attributes.title ?? "")}</summary>
|
|
270
243
|
<Astro.self blocks={children} contract={contract} />
|
|
@@ -290,7 +263,7 @@ function youtubeEmbedUrl(value: unknown): string | null {
|
|
|
290
263
|
+ `?key=${encodeURIComponent(googleMapsApiKey)}`
|
|
291
264
|
+ `&q=${encodeURIComponent(query)}`;
|
|
292
265
|
return (
|
|
293
|
-
<S as="div" {...semantic}>
|
|
266
|
+
<S as="div" {...semantic} {...htmlAttributes}>
|
|
294
267
|
<iframe
|
|
295
268
|
src={source}
|
|
296
269
|
title={address || "Map"}
|
|
@@ -301,7 +274,7 @@ function youtubeEmbedUrl(value: unknown): string | null {
|
|
|
301
274
|
);
|
|
302
275
|
}
|
|
303
276
|
return query ? (
|
|
304
|
-
<S as="div" {...semantic}>
|
|
277
|
+
<S as="div" {...semantic} {...htmlAttributes}>
|
|
305
278
|
<S
|
|
306
279
|
as="a"
|
|
307
280
|
href={`https://www.openstreetmap.org/search?query=${encodeURIComponent(query)}`}
|
|
@@ -313,9 +286,9 @@ function youtubeEmbedUrl(value: unknown): string | null {
|
|
|
313
286
|
) : null;
|
|
314
287
|
}
|
|
315
288
|
|
|
316
|
-
const layoutTag =
|
|
289
|
+
const layoutTag = name === "section" ? "section" : "div";
|
|
317
290
|
return (
|
|
318
|
-
<S as={layoutTag} {...semantic}>
|
|
291
|
+
<S as={layoutTag} {...semantic} {...htmlAttributes}>
|
|
319
292
|
{block.innerHtml && children.length === 0
|
|
320
293
|
? <S as="div" set:html={block.innerHtml} />
|
|
321
294
|
: <Astro.self blocks={children} contract={contract} />}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
---
|
|
2
|
+
import S from "@styleflow.app/astro/S.astro";
|
|
3
|
+
import type { FlowpressBlock } from "../../types.js";
|
|
4
|
+
import type { StyleflowContract } from "../../styleflow.js";
|
|
5
|
+
import {
|
|
6
|
+
blockHtmlAttributes,
|
|
7
|
+
responsiveValues,
|
|
8
|
+
semanticProps
|
|
9
|
+
} from "./helpers.js";
|
|
10
|
+
|
|
11
|
+
interface Props {
|
|
12
|
+
block: FlowpressBlock;
|
|
13
|
+
contract?: StyleflowContract;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const { block, contract } = Astro.props;
|
|
17
|
+
const values = responsiveValues(block.attributes, "span", 1);
|
|
18
|
+
const partial = ["start", "end"].includes(String(block.attributes.partialContainer))
|
|
19
|
+
? String(block.attributes.partialContainer)
|
|
20
|
+
: "none";
|
|
21
|
+
const partialBreakpoint = ["base", "sm", "md", "lg", "xl", "2xl"].includes(
|
|
22
|
+
String(block.attributes.partialContainerBreakpoint)
|
|
23
|
+
) ? String(block.attributes.partialContainerBreakpoint) : "lg";
|
|
24
|
+
const semantic = semanticProps(block.attributes, contract);
|
|
25
|
+
const htmlAttributes = blockHtmlAttributes(block.attributes, "flowpress-column");
|
|
26
|
+
const style = [
|
|
27
|
+
`--flowpress-span-base:${values.base}`,
|
|
28
|
+
`--flowpress-span-sm:${values.sm}`,
|
|
29
|
+
`--flowpress-span-md:${values.md}`,
|
|
30
|
+
`--flowpress-span-lg:${values.lg}`,
|
|
31
|
+
`--flowpress-span-xl:${values.xl}`,
|
|
32
|
+
`--flowpress-span-2xl:${values["2xl"]}`
|
|
33
|
+
].join(";");
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
<S
|
|
37
|
+
as="div"
|
|
38
|
+
{...semantic}
|
|
39
|
+
{...htmlAttributes}
|
|
40
|
+
data-partial={partial}
|
|
41
|
+
data-partial-breakpoint={partialBreakpoint}
|
|
42
|
+
style={style}
|
|
43
|
+
>
|
|
44
|
+
<slot />
|
|
45
|
+
</S>
|
|
46
|
+
|
|
47
|
+
<style>
|
|
48
|
+
.flowpress-column {
|
|
49
|
+
grid-column: span var(--flowpress-span-base) / span var(--flowpress-span-base);
|
|
50
|
+
min-width: 0;
|
|
51
|
+
}
|
|
52
|
+
.flowpress-column[data-partial="start"][data-partial-breakpoint="base"] {
|
|
53
|
+
padding-inline-start: max(var(--sf-padding-inline, 0px), calc((100vw - var(--sf-container-max-width, 100vw)) / 2));
|
|
54
|
+
}
|
|
55
|
+
.flowpress-column[data-partial="end"][data-partial-breakpoint="base"] {
|
|
56
|
+
padding-inline-end: max(var(--sf-padding-inline, 0px), calc((100vw - var(--sf-container-max-width, 100vw)) / 2));
|
|
57
|
+
}
|
|
58
|
+
@media (min-width: 40rem) {
|
|
59
|
+
.flowpress-column { grid-column: span var(--flowpress-span-sm) / span var(--flowpress-span-sm); }
|
|
60
|
+
.flowpress-column[data-partial="start"][data-partial-breakpoint="sm"] { padding-inline-start: max(var(--sf-padding-inline, 0px), calc((100vw - var(--sf-container-max-width, 100vw)) / 2)); }
|
|
61
|
+
.flowpress-column[data-partial="end"][data-partial-breakpoint="sm"] { padding-inline-end: max(var(--sf-padding-inline, 0px), calc((100vw - var(--sf-container-max-width, 100vw)) / 2)); }
|
|
62
|
+
}
|
|
63
|
+
@media (min-width: 48rem) {
|
|
64
|
+
.flowpress-column { grid-column: span var(--flowpress-span-md) / span var(--flowpress-span-md); }
|
|
65
|
+
.flowpress-column[data-partial="start"][data-partial-breakpoint="md"] { padding-inline-start: max(var(--sf-padding-inline, 0px), calc((100vw - var(--sf-container-max-width, 100vw)) / 2)); }
|
|
66
|
+
.flowpress-column[data-partial="end"][data-partial-breakpoint="md"] { padding-inline-end: max(var(--sf-padding-inline, 0px), calc((100vw - var(--sf-container-max-width, 100vw)) / 2)); }
|
|
67
|
+
}
|
|
68
|
+
@media (min-width: 64rem) {
|
|
69
|
+
.flowpress-column { grid-column: span var(--flowpress-span-lg) / span var(--flowpress-span-lg); }
|
|
70
|
+
.flowpress-column[data-partial="start"][data-partial-breakpoint="lg"] { padding-inline-start: max(var(--sf-padding-inline, 0px), calc((100vw - var(--sf-container-max-width, 100vw)) / 2)); }
|
|
71
|
+
.flowpress-column[data-partial="end"][data-partial-breakpoint="lg"] { padding-inline-end: max(var(--sf-padding-inline, 0px), calc((100vw - var(--sf-container-max-width, 100vw)) / 2)); }
|
|
72
|
+
}
|
|
73
|
+
@media (min-width: 80rem) {
|
|
74
|
+
.flowpress-column { grid-column: span var(--flowpress-span-xl) / span var(--flowpress-span-xl); }
|
|
75
|
+
.flowpress-column[data-partial="start"][data-partial-breakpoint="xl"] { padding-inline-start: max(var(--sf-padding-inline, 0px), calc((100vw - var(--sf-container-max-width, 100vw)) / 2)); }
|
|
76
|
+
.flowpress-column[data-partial="end"][data-partial-breakpoint="xl"] { padding-inline-end: max(var(--sf-padding-inline, 0px), calc((100vw - var(--sf-container-max-width, 100vw)) / 2)); }
|
|
77
|
+
}
|
|
78
|
+
@media (min-width: 96rem) {
|
|
79
|
+
.flowpress-column { grid-column: span var(--flowpress-span-2xl) / span var(--flowpress-span-2xl); }
|
|
80
|
+
.flowpress-column[data-partial="start"][data-partial-breakpoint="2xl"] { padding-inline-start: max(var(--sf-padding-inline, 0px), calc((100vw - var(--sf-container-max-width, 100vw)) / 2)); }
|
|
81
|
+
.flowpress-column[data-partial="end"][data-partial-breakpoint="2xl"] { padding-inline-end: max(var(--sf-padding-inline, 0px), calc((100vw - var(--sf-container-max-width, 100vw)) / 2)); }
|
|
82
|
+
}
|
|
83
|
+
</style>
|