@styleflow.app/flowpress-astro-core 0.2.0-beta.3 → 0.2.0-beta.5
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 +19 -0
- package/README.md +7 -1
- package/dist/astro/GutenbergRenderer.astro +70 -110
- package/dist/astro/blocks/ColumnBlock.astro +78 -0
- package/dist/astro/blocks/FormBlock.astro +232 -0
- package/dist/astro/blocks/GridBlock.astro +65 -0
- package/dist/astro/blocks/HeroBlock.astro +108 -0
- package/dist/astro/blocks/ListItemFallback.astro +28 -0
- package/dist/astro/blocks/TextBlock.astro +61 -0
- package/dist/astro/blocks/helpers.d.ts +14 -0
- package/dist/astro/blocks/helpers.d.ts.map +1 -0
- package/dist/astro/blocks/helpers.js +69 -0
- package/dist/astro/blocks/helpers.js.map +1 -0
- package/dist/astro/blocks/helpers.ts +95 -0
- package/dist/styleflow.d.ts.map +1 -1
- package/dist/styleflow.js +7 -0
- package/dist/styleflow.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -3
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.2.0-beta.5
|
|
6
|
+
|
|
7
|
+
- Split Gutenberg rendering into dedicated hero, responsive layout, text,
|
|
8
|
+
list/template and CF7 modules.
|
|
9
|
+
- Added semantic foreground/modifier propagation and core text block
|
|
10
|
+
rendering.
|
|
11
|
+
- Added responsive 12-column inheritance, token-based partial containers,
|
|
12
|
+
hero media/scrim rendering and reduced-motion handling.
|
|
13
|
+
- Added repeated query-item template rendering with the previous plain-list
|
|
14
|
+
fallback.
|
|
15
|
+
- Added an accessible Turnstile/CF7 browser form compatible with static and
|
|
16
|
+
server builds.
|
|
17
|
+
- Added Astro component checking to the release gate.
|
|
18
|
+
|
|
19
|
+
## 0.2.0-beta.4
|
|
20
|
+
|
|
21
|
+
- Updated the exact StyleFlow Astro dependency to `0.3.0-beta.2`.
|
|
22
|
+
- Validated the npm Trusted Publisher path with a new OIDC-only publication.
|
|
23
|
+
|
|
5
24
|
## 0.2.0-beta.3
|
|
6
25
|
|
|
7
26
|
- Updated the exact StyleFlow Astro dependency to `0.3.0-beta.1`.
|
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.5
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
Prereleases are pinned exactly. The package expects Astro 7 or newer and the
|
|
@@ -30,6 +30,12 @@ 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.
|
|
38
|
+
|
|
33
39
|
## Release
|
|
34
40
|
|
|
35
41
|
```bash
|
|
@@ -1,10 +1,19 @@
|
|
|
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
|
+
linkValue,
|
|
13
|
+
objectValue,
|
|
14
|
+
semanticProps,
|
|
15
|
+
textValue
|
|
16
|
+
} from "./blocks/helpers.js";
|
|
8
17
|
|
|
9
18
|
interface Props {
|
|
10
19
|
blocks: FlowpressBlock[];
|
|
@@ -13,57 +22,6 @@ interface Props {
|
|
|
13
22
|
|
|
14
23
|
const { blocks, contract } = Astro.props;
|
|
15
24
|
|
|
16
|
-
const defaults = {
|
|
17
|
-
surface: "default",
|
|
18
|
-
layoutRole: "stack",
|
|
19
|
-
density: "md"
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
function styleflowProps(attributes: Record<string, unknown>) {
|
|
23
|
-
if (!contract) return defaults;
|
|
24
|
-
return resolveDynamicStyleflowProps(attributes, defaults, contract);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function dynamicProps(
|
|
28
|
-
attributes: Record<string, unknown>,
|
|
29
|
-
fallback: Record<string, unknown>
|
|
30
|
-
) {
|
|
31
|
-
if (!contract) return fallback;
|
|
32
|
-
return resolveDynamicStyleflowProps(attributes, fallback, contract);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function text(block: FlowpressBlock, key = "text"): string {
|
|
36
|
-
return String(block.attributes[key] ?? "");
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function objectValue(
|
|
40
|
-
block: FlowpressBlock,
|
|
41
|
-
key: string
|
|
42
|
-
): Record<string, unknown> {
|
|
43
|
-
const value = block.attributes[key];
|
|
44
|
-
return value && typeof value === "object" && !Array.isArray(value)
|
|
45
|
-
? value as Record<string, unknown>
|
|
46
|
-
: {};
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function linkValue(block: FlowpressBlock, key = "link"): string {
|
|
50
|
-
const value = block.attributes[key];
|
|
51
|
-
const href = typeof value === "string"
|
|
52
|
-
? value
|
|
53
|
-
: String(objectValue(block, key).url ?? "");
|
|
54
|
-
if (/^(?:\/(?!\/)|#|https?:\/\/|mailto:|tel:)/i.test(href)) {
|
|
55
|
-
return href;
|
|
56
|
-
}
|
|
57
|
-
return "";
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function itemImage(item: Record<string, unknown>): Record<string, unknown> {
|
|
61
|
-
const value = item.image;
|
|
62
|
-
return value && typeof value === "object" && !Array.isArray(value)
|
|
63
|
-
? value as Record<string, unknown>
|
|
64
|
-
: {};
|
|
65
|
-
}
|
|
66
|
-
|
|
67
25
|
function youtubeEmbedUrl(value: unknown): string | null {
|
|
68
26
|
try {
|
|
69
27
|
const url = new URL(String(value ?? ""));
|
|
@@ -85,52 +43,62 @@ function youtubeEmbedUrl(value: unknown): string | null {
|
|
|
85
43
|
{blocks.map((block) => {
|
|
86
44
|
const name = block.name.replace(/^sf\//, "");
|
|
87
45
|
const children = block.innerBlocks ?? [];
|
|
88
|
-
const semantic =
|
|
46
|
+
const semantic = semanticProps(block.attributes, contract);
|
|
89
47
|
|
|
90
|
-
if (name === "
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
ty: "heading",
|
|
96
|
-
v: String(block.attributes.variant ?? level),
|
|
97
|
-
w: String(block.attributes.weight ?? "strong")
|
|
98
|
-
},
|
|
99
|
-
{ ty: "heading", v: String(level), w: "strong" }
|
|
48
|
+
if (name === "hero") {
|
|
49
|
+
return (
|
|
50
|
+
<HeroBlock block={block} contract={contract}>
|
|
51
|
+
<Astro.self blocks={children} contract={contract} />
|
|
52
|
+
</HeroBlock>
|
|
100
53
|
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (name === "grid") {
|
|
101
57
|
return (
|
|
102
|
-
<
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
58
|
+
<GridBlock block={block} contract={contract}>
|
|
59
|
+
<Astro.self blocks={children} contract={contract} />
|
|
60
|
+
</GridBlock>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (name === "column") {
|
|
65
|
+
return (
|
|
66
|
+
<ColumnBlock block={block} contract={contract}>
|
|
67
|
+
<Astro.self blocks={children} contract={contract} />
|
|
68
|
+
</ColumnBlock>
|
|
108
69
|
);
|
|
109
70
|
}
|
|
110
71
|
|
|
111
|
-
if (
|
|
112
|
-
|
|
113
|
-
|
|
72
|
+
if ([
|
|
73
|
+
"heading",
|
|
74
|
+
"rich-text",
|
|
75
|
+
"quote",
|
|
76
|
+
"core/paragraph",
|
|
77
|
+
"core/heading",
|
|
78
|
+
"core/list",
|
|
79
|
+
"core/list-item",
|
|
80
|
+
"core/quote"
|
|
81
|
+
].includes(name)) {
|
|
82
|
+
return (
|
|
83
|
+
<TextBlock block={block} contract={contract} hasChildren={children.length > 0}>
|
|
84
|
+
<Astro.self blocks={children} contract={contract} />
|
|
85
|
+
</TextBlock>
|
|
86
|
+
);
|
|
114
87
|
}
|
|
115
88
|
|
|
116
89
|
if (name === "button") {
|
|
117
90
|
const href = linkValue(block, "href") || "#";
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
interactivePriority: String(
|
|
121
|
-
block.attributes.interactivePriority ?? "primary"
|
|
122
|
-
)
|
|
123
|
-
},
|
|
124
|
-
{ interactivePriority: "primary" }
|
|
125
|
-
);
|
|
91
|
+
const link = objectValue(block, "href");
|
|
92
|
+
const opensInNewTab = Boolean(link.opensInNewTab);
|
|
126
93
|
return (
|
|
127
94
|
<S
|
|
128
95
|
as="a"
|
|
129
96
|
href={href}
|
|
97
|
+
target={opensInNewTab ? "_blank" : undefined}
|
|
98
|
+
rel={opensInNewTab ? "noopener noreferrer" : undefined}
|
|
130
99
|
{...semantic}
|
|
131
|
-
{...interaction}
|
|
132
100
|
>
|
|
133
|
-
{
|
|
101
|
+
{textValue(block, "label")}
|
|
134
102
|
</S>
|
|
135
103
|
);
|
|
136
104
|
}
|
|
@@ -205,17 +173,8 @@ function youtubeEmbedUrl(value: unknown): string | null {
|
|
|
205
173
|
) : null;
|
|
206
174
|
}
|
|
207
175
|
|
|
208
|
-
if (name === "quote") {
|
|
209
|
-
return (
|
|
210
|
-
<S as="blockquote" {...semantic}>
|
|
211
|
-
<S as="p" set:html={text(block, "quote")} />
|
|
212
|
-
{block.attributes.citation && <cite>{String(block.attributes.citation)}</cite>}
|
|
213
|
-
</S>
|
|
214
|
-
);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
176
|
if (name === "chip") {
|
|
218
|
-
return <S as="span" {...semantic}>{
|
|
177
|
+
return <S as="span" {...semantic}>{textValue(block, "label")}</S>;
|
|
219
178
|
}
|
|
220
179
|
|
|
221
180
|
if (name === "card") {
|
|
@@ -237,31 +196,32 @@ function youtubeEmbedUrl(value: unknown): string | null {
|
|
|
237
196
|
const items = Array.isArray(block.attributes.resolvedItems)
|
|
238
197
|
? block.attributes.resolvedItems as Array<Record<string, unknown>>
|
|
239
198
|
: [];
|
|
199
|
+
const resolvedBlocks = Array.isArray(block.attributes.resolvedBlocks)
|
|
200
|
+
? block.attributes.resolvedBlocks as FlowpressBlock[][]
|
|
201
|
+
: [];
|
|
240
202
|
return (
|
|
241
203
|
<S as="ul" {...semantic}>
|
|
242
|
-
{items.map((item) => (
|
|
204
|
+
{items.map((item, index) => (
|
|
243
205
|
<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>
|
|
206
|
+
{resolvedBlocks[index]?.length ? (
|
|
207
|
+
<Astro.self blocks={resolvedBlocks[index]} contract={contract} />
|
|
254
208
|
) : (
|
|
255
|
-
<
|
|
209
|
+
<ListItemFallback item={item} />
|
|
256
210
|
)}
|
|
257
|
-
{item.excerpt && <S as="p">{String(item.excerpt)}</S>}
|
|
258
|
-
{item.priceHtml && <S as="div" set:html={String(item.priceHtml)} />}
|
|
259
211
|
</S>
|
|
260
212
|
))}
|
|
261
213
|
</S>
|
|
262
214
|
);
|
|
263
215
|
}
|
|
264
216
|
|
|
217
|
+
if (name === "template") {
|
|
218
|
+
return <Astro.self blocks={children} contract={contract} />;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (name === "form") {
|
|
222
|
+
return <FormBlock block={block} contract={contract} />;
|
|
223
|
+
}
|
|
224
|
+
|
|
265
225
|
if (name === "accordion-item") {
|
|
266
226
|
return (
|
|
267
227
|
<S as="div" {...semantic}>
|
|
@@ -313,7 +273,7 @@ function youtubeEmbedUrl(value: unknown): string | null {
|
|
|
313
273
|
) : null;
|
|
314
274
|
}
|
|
315
275
|
|
|
316
|
-
const layoutTag =
|
|
276
|
+
const layoutTag = name === "section" ? "section" : "div";
|
|
317
277
|
return (
|
|
318
278
|
<S as={layoutTag} {...semantic}>
|
|
319
279
|
{block.innerHtml && children.length === 0
|
|
@@ -0,0 +1,78 @@
|
|
|
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 { responsiveValues, semanticProps } from "./helpers.js";
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
block: FlowpressBlock;
|
|
9
|
+
contract?: StyleflowContract;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const { block, contract } = Astro.props;
|
|
13
|
+
const values = responsiveValues(block.attributes, "span", 1);
|
|
14
|
+
const partial = ["start", "end"].includes(String(block.attributes.partialContainer))
|
|
15
|
+
? String(block.attributes.partialContainer)
|
|
16
|
+
: "none";
|
|
17
|
+
const partialBreakpoint = ["base", "sm", "md", "lg", "xl", "2xl"].includes(
|
|
18
|
+
String(block.attributes.partialContainerBreakpoint)
|
|
19
|
+
) ? String(block.attributes.partialContainerBreakpoint) : "lg";
|
|
20
|
+
const semantic = semanticProps(block.attributes, contract);
|
|
21
|
+
const style = [
|
|
22
|
+
`--flowpress-span-base:${values.base}`,
|
|
23
|
+
`--flowpress-span-sm:${values.sm}`,
|
|
24
|
+
`--flowpress-span-md:${values.md}`,
|
|
25
|
+
`--flowpress-span-lg:${values.lg}`,
|
|
26
|
+
`--flowpress-span-xl:${values.xl}`,
|
|
27
|
+
`--flowpress-span-2xl:${values["2xl"]}`
|
|
28
|
+
].join(";");
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
<S
|
|
32
|
+
as="div"
|
|
33
|
+
{...semantic}
|
|
34
|
+
class="flowpress-column"
|
|
35
|
+
data-partial={partial}
|
|
36
|
+
data-partial-breakpoint={partialBreakpoint}
|
|
37
|
+
style={style}
|
|
38
|
+
>
|
|
39
|
+
<slot />
|
|
40
|
+
</S>
|
|
41
|
+
|
|
42
|
+
<style>
|
|
43
|
+
.flowpress-column {
|
|
44
|
+
grid-column: span var(--flowpress-span-base) / span var(--flowpress-span-base);
|
|
45
|
+
min-width: 0;
|
|
46
|
+
}
|
|
47
|
+
.flowpress-column[data-partial="start"][data-partial-breakpoint="base"] {
|
|
48
|
+
padding-inline-start: max(var(--sf-padding-inline, 0px), calc((100vw - var(--sf-container-max-width, 100vw)) / 2));
|
|
49
|
+
}
|
|
50
|
+
.flowpress-column[data-partial="end"][data-partial-breakpoint="base"] {
|
|
51
|
+
padding-inline-end: max(var(--sf-padding-inline, 0px), calc((100vw - var(--sf-container-max-width, 100vw)) / 2));
|
|
52
|
+
}
|
|
53
|
+
@media (min-width: 40rem) {
|
|
54
|
+
.flowpress-column { grid-column: span var(--flowpress-span-sm) / span var(--flowpress-span-sm); }
|
|
55
|
+
.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)); }
|
|
56
|
+
.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)); }
|
|
57
|
+
}
|
|
58
|
+
@media (min-width: 48rem) {
|
|
59
|
+
.flowpress-column { grid-column: span var(--flowpress-span-md) / span var(--flowpress-span-md); }
|
|
60
|
+
.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)); }
|
|
61
|
+
.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)); }
|
|
62
|
+
}
|
|
63
|
+
@media (min-width: 64rem) {
|
|
64
|
+
.flowpress-column { grid-column: span var(--flowpress-span-lg) / span var(--flowpress-span-lg); }
|
|
65
|
+
.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)); }
|
|
66
|
+
.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)); }
|
|
67
|
+
}
|
|
68
|
+
@media (min-width: 80rem) {
|
|
69
|
+
.flowpress-column { grid-column: span var(--flowpress-span-xl) / span var(--flowpress-span-xl); }
|
|
70
|
+
.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)); }
|
|
71
|
+
.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)); }
|
|
72
|
+
}
|
|
73
|
+
@media (min-width: 96rem) {
|
|
74
|
+
.flowpress-column { grid-column: span var(--flowpress-span-2xl) / span var(--flowpress-span-2xl); }
|
|
75
|
+
.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)); }
|
|
76
|
+
.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)); }
|
|
77
|
+
}
|
|
78
|
+
</style>
|
|
@@ -0,0 +1,232 @@
|
|
|
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 { semanticProps } from "./helpers.js";
|
|
6
|
+
|
|
7
|
+
interface FormField {
|
|
8
|
+
name: string;
|
|
9
|
+
type: string;
|
|
10
|
+
required?: boolean;
|
|
11
|
+
values?: string[];
|
|
12
|
+
labels?: string[];
|
|
13
|
+
options?: string[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface FormSchema {
|
|
17
|
+
id: number;
|
|
18
|
+
title: string;
|
|
19
|
+
fields: FormField[];
|
|
20
|
+
submitUrl: string;
|
|
21
|
+
turnstileSiteKey: string;
|
|
22
|
+
available: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface Props {
|
|
26
|
+
block: FlowpressBlock;
|
|
27
|
+
contract?: StyleflowContract;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const { block, contract } = Astro.props;
|
|
31
|
+
const schema = block.attributes.formSchema as FormSchema | undefined;
|
|
32
|
+
const semantic = semanticProps(block.attributes, contract);
|
|
33
|
+
const labelFor = (field: FormField): string => field.name
|
|
34
|
+
.replace(/[-_]+/g, " ")
|
|
35
|
+
.replace(/\b\w/g, (letter) => letter.toUpperCase());
|
|
36
|
+
const optionLabel = (field: FormField, index: number): string =>
|
|
37
|
+
field.labels?.[index] || field.values?.[index] || "";
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
{schema?.available ? (
|
|
41
|
+
<S as="div" {...semantic}>
|
|
42
|
+
<flowpress-cf7-form>
|
|
43
|
+
<form action={schema.submitUrl} method="post" enctype="multipart/form-data" novalidate>
|
|
44
|
+
{block.attributes.title || schema.title ? (
|
|
45
|
+
<S as="h2">{String(block.attributes.title || schema.title)}</S>
|
|
46
|
+
) : null}
|
|
47
|
+
{schema.fields.map((field) => {
|
|
48
|
+
if (field.type === "submit") {
|
|
49
|
+
return (
|
|
50
|
+
<button type="submit">
|
|
51
|
+
{field.values?.[0] || "Invia"}
|
|
52
|
+
</button>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
if (field.type === "hidden") {
|
|
56
|
+
return <input type="hidden" name={field.name} value={field.values?.[0] || ""} />;
|
|
57
|
+
}
|
|
58
|
+
if (["checkbox", "radio"].includes(field.type)) {
|
|
59
|
+
return (
|
|
60
|
+
<fieldset>
|
|
61
|
+
<legend>{labelFor(field)}</legend>
|
|
62
|
+
{(field.values || []).map((value, index) => (
|
|
63
|
+
<label>
|
|
64
|
+
<input
|
|
65
|
+
type={field.type}
|
|
66
|
+
name={field.type === "checkbox" ? `${field.name}[]` : field.name}
|
|
67
|
+
value={value}
|
|
68
|
+
required={field.required}
|
|
69
|
+
/>
|
|
70
|
+
{optionLabel(field, index)}
|
|
71
|
+
</label>
|
|
72
|
+
))}
|
|
73
|
+
<span data-field-error={field.name}></span>
|
|
74
|
+
</fieldset>
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
if (field.type === "acceptance") {
|
|
78
|
+
return (
|
|
79
|
+
<label>
|
|
80
|
+
<input type="checkbox" name={field.name} value="1" required={field.required} />
|
|
81
|
+
{field.values?.[0] || labelFor(field)}
|
|
82
|
+
<span data-field-error={field.name}></span>
|
|
83
|
+
</label>
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
if (field.type === "select") {
|
|
87
|
+
return (
|
|
88
|
+
<label>
|
|
89
|
+
{labelFor(field)}
|
|
90
|
+
<select
|
|
91
|
+
name={field.options?.includes("multiple") ? `${field.name}[]` : field.name}
|
|
92
|
+
required={field.required}
|
|
93
|
+
multiple={field.options?.includes("multiple")}
|
|
94
|
+
>
|
|
95
|
+
{(field.values || []).map((value, index) => (
|
|
96
|
+
<option value={value}>{optionLabel(field, index)}</option>
|
|
97
|
+
))}
|
|
98
|
+
</select>
|
|
99
|
+
<span data-field-error={field.name}></span>
|
|
100
|
+
</label>
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
if (field.type === "textarea") {
|
|
104
|
+
return (
|
|
105
|
+
<label>
|
|
106
|
+
{labelFor(field)}
|
|
107
|
+
<textarea name={field.name} required={field.required}></textarea>
|
|
108
|
+
<span data-field-error={field.name}></span>
|
|
109
|
+
</label>
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
return (
|
|
113
|
+
<label>
|
|
114
|
+
{labelFor(field)}
|
|
115
|
+
<input
|
|
116
|
+
type={field.type === "file" ? "file" : field.type}
|
|
117
|
+
name={field.name}
|
|
118
|
+
required={field.required}
|
|
119
|
+
/>
|
|
120
|
+
<span data-field-error={field.name}></span>
|
|
121
|
+
</label>
|
|
122
|
+
);
|
|
123
|
+
})}
|
|
124
|
+
<div
|
|
125
|
+
data-turnstile
|
|
126
|
+
data-sitekey={schema.turnstileSiteKey}
|
|
127
|
+
aria-label="Verifica anti-spam"
|
|
128
|
+
></div>
|
|
129
|
+
<p data-form-status role="status" aria-live="polite"></p>
|
|
130
|
+
</form>
|
|
131
|
+
</flowpress-cf7-form>
|
|
132
|
+
</S>
|
|
133
|
+
) : (
|
|
134
|
+
<S as="p" {...semantic} role="status">
|
|
135
|
+
Il modulo non è temporaneamente disponibile.
|
|
136
|
+
</S>
|
|
137
|
+
)}
|
|
138
|
+
|
|
139
|
+
<script>
|
|
140
|
+
declare global {
|
|
141
|
+
interface Window {
|
|
142
|
+
turnstile?: {
|
|
143
|
+
render(target: Element, options: Record<string, unknown>): string;
|
|
144
|
+
reset(widgetId?: string): void;
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
let turnstilePromise: Promise<void> | undefined;
|
|
150
|
+
const loadTurnstile = (): Promise<void> => {
|
|
151
|
+
if (window.turnstile) return Promise.resolve();
|
|
152
|
+
if (turnstilePromise) return turnstilePromise;
|
|
153
|
+
turnstilePromise = new Promise((resolve, reject) => {
|
|
154
|
+
const existing = document.querySelector<HTMLScriptElement>(
|
|
155
|
+
'script[data-flowpress-turnstile]'
|
|
156
|
+
);
|
|
157
|
+
if (existing) {
|
|
158
|
+
existing.addEventListener("load", () => resolve(), { once: true });
|
|
159
|
+
existing.addEventListener("error", () => reject(new Error("Turnstile unavailable")), { once: true });
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
const script = document.createElement("script");
|
|
163
|
+
script.src = "https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit";
|
|
164
|
+
script.async = true;
|
|
165
|
+
script.defer = true;
|
|
166
|
+
script.dataset.flowpressTurnstile = "true";
|
|
167
|
+
script.addEventListener("load", () => resolve(), { once: true });
|
|
168
|
+
script.addEventListener("error", () => reject(new Error("Turnstile unavailable")), { once: true });
|
|
169
|
+
document.head.append(script);
|
|
170
|
+
});
|
|
171
|
+
return turnstilePromise;
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
if (!customElements.get("flowpress-cf7-form")) {
|
|
175
|
+
customElements.define("flowpress-cf7-form", class extends HTMLElement {
|
|
176
|
+
private widgetId?: string;
|
|
177
|
+
|
|
178
|
+
connectedCallback() {
|
|
179
|
+
const form = this.querySelector<HTMLFormElement>("form");
|
|
180
|
+
const target = this.querySelector<HTMLElement>("[data-turnstile]");
|
|
181
|
+
const status = this.querySelector<HTMLElement>("[data-form-status]");
|
|
182
|
+
if (!form || !target || !status) return;
|
|
183
|
+
|
|
184
|
+
loadTurnstile()
|
|
185
|
+
.then(() => {
|
|
186
|
+
if (!window.turnstile || this.widgetId) return;
|
|
187
|
+
this.widgetId = window.turnstile.render(target, {
|
|
188
|
+
sitekey: target.dataset.sitekey
|
|
189
|
+
});
|
|
190
|
+
})
|
|
191
|
+
.catch(() => {
|
|
192
|
+
status.textContent = "La verifica anti-spam non è disponibile.";
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
form.addEventListener("submit", async (event) => {
|
|
196
|
+
event.preventDefault();
|
|
197
|
+
status.textContent = "Invio in corso…";
|
|
198
|
+
form.querySelectorAll<HTMLElement>("[data-field-error]")
|
|
199
|
+
.forEach((node) => { node.textContent = ""; });
|
|
200
|
+
const submit = form.querySelector<HTMLButtonElement>('[type="submit"]');
|
|
201
|
+
if (submit) submit.disabled = true;
|
|
202
|
+
try {
|
|
203
|
+
const response = await fetch(form.action, {
|
|
204
|
+
method: "POST",
|
|
205
|
+
body: new FormData(form),
|
|
206
|
+
headers: { Accept: "application/json" }
|
|
207
|
+
});
|
|
208
|
+
const result = await response.json() as {
|
|
209
|
+
message?: string;
|
|
210
|
+
invalidFields?: Array<{ field?: string; into?: string; message?: string }>;
|
|
211
|
+
};
|
|
212
|
+
status.textContent = result.message || (response.ok ? "Messaggio inviato." : "Invio non riuscito.");
|
|
213
|
+
for (const invalid of result.invalidFields || []) {
|
|
214
|
+
const name = invalid.field || invalid.into?.replace(/^span\./, "") || "";
|
|
215
|
+
const node = name
|
|
216
|
+
? form.querySelector<HTMLElement>(`[data-field-error="${CSS.escape(name)}"]`)
|
|
217
|
+
: null;
|
|
218
|
+
if (node) node.textContent = invalid.message || "";
|
|
219
|
+
}
|
|
220
|
+
if (response.ok) form.reset();
|
|
221
|
+
if (window.turnstile) window.turnstile.reset(this.widgetId);
|
|
222
|
+
} catch {
|
|
223
|
+
status.textContent = "Impossibile inviare il modulo. Riprova.";
|
|
224
|
+
if (window.turnstile) window.turnstile.reset(this.widgetId);
|
|
225
|
+
} finally {
|
|
226
|
+
if (submit) submit.disabled = false;
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
</script>
|
|
@@ -0,0 +1,65 @@
|
|
|
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 { responsiveValues, semanticProps } from "./helpers.js";
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
block: FlowpressBlock;
|
|
9
|
+
contract?: StyleflowContract;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const { block, contract } = Astro.props;
|
|
13
|
+
const values = responsiveValues(
|
|
14
|
+
block.attributes,
|
|
15
|
+
"columns",
|
|
16
|
+
Number(block.attributes.columns) || 12
|
|
17
|
+
);
|
|
18
|
+
const align = ["start", "center", "end", "stretch"].includes(
|
|
19
|
+
String(block.attributes.align)
|
|
20
|
+
) ? String(block.attributes.align) : "stretch";
|
|
21
|
+
const semantic = semanticProps(block.attributes, contract, {
|
|
22
|
+
surface: "default",
|
|
23
|
+
layoutRole: "container",
|
|
24
|
+
density: "md"
|
|
25
|
+
});
|
|
26
|
+
const style = [
|
|
27
|
+
`--flowpress-grid-base:${values.base}`,
|
|
28
|
+
`--flowpress-grid-sm:${values.sm}`,
|
|
29
|
+
`--flowpress-grid-md:${values.md}`,
|
|
30
|
+
`--flowpress-grid-lg:${values.lg}`,
|
|
31
|
+
`--flowpress-grid-xl:${values.xl}`,
|
|
32
|
+
`--flowpress-grid-2xl:${values["2xl"]}`
|
|
33
|
+
].join(";");
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
<S as="div" {...semantic} class="flowpress-grid" data-align={align} style={style}>
|
|
37
|
+
<slot />
|
|
38
|
+
</S>
|
|
39
|
+
|
|
40
|
+
<style>
|
|
41
|
+
.flowpress-grid {
|
|
42
|
+
display: grid;
|
|
43
|
+
grid-template-columns: repeat(var(--flowpress-grid-base), minmax(0, 1fr));
|
|
44
|
+
align-items: var(--flowpress-grid-align, stretch);
|
|
45
|
+
}
|
|
46
|
+
.flowpress-grid[data-align="start"] { --flowpress-grid-align: start; }
|
|
47
|
+
.flowpress-grid[data-align="center"] { --flowpress-grid-align: center; }
|
|
48
|
+
.flowpress-grid[data-align="end"] { --flowpress-grid-align: end; }
|
|
49
|
+
.flowpress-grid[data-align="stretch"] { --flowpress-grid-align: stretch; }
|
|
50
|
+
@media (min-width: 40rem) {
|
|
51
|
+
.flowpress-grid { grid-template-columns: repeat(var(--flowpress-grid-sm), minmax(0, 1fr)); }
|
|
52
|
+
}
|
|
53
|
+
@media (min-width: 48rem) {
|
|
54
|
+
.flowpress-grid { grid-template-columns: repeat(var(--flowpress-grid-md), minmax(0, 1fr)); }
|
|
55
|
+
}
|
|
56
|
+
@media (min-width: 64rem) {
|
|
57
|
+
.flowpress-grid { grid-template-columns: repeat(var(--flowpress-grid-lg), minmax(0, 1fr)); }
|
|
58
|
+
}
|
|
59
|
+
@media (min-width: 80rem) {
|
|
60
|
+
.flowpress-grid { grid-template-columns: repeat(var(--flowpress-grid-xl), minmax(0, 1fr)); }
|
|
61
|
+
}
|
|
62
|
+
@media (min-width: 96rem) {
|
|
63
|
+
.flowpress-grid { grid-template-columns: repeat(var(--flowpress-grid-2xl), minmax(0, 1fr)); }
|
|
64
|
+
}
|
|
65
|
+
</style>
|
|
@@ -0,0 +1,108 @@
|
|
|
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 { objectValue, semanticProps } from "./helpers.js";
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
block: FlowpressBlock;
|
|
9
|
+
contract?: StyleflowContract;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const { block, contract } = Astro.props;
|
|
13
|
+
const attributes = block.attributes;
|
|
14
|
+
const semantic = semanticProps(attributes, contract, {
|
|
15
|
+
surface: "default",
|
|
16
|
+
layoutRole: "region",
|
|
17
|
+
density: "md"
|
|
18
|
+
});
|
|
19
|
+
const backgroundType = ["color", "image", "video"].includes(
|
|
20
|
+
String(attributes.backgroundType)
|
|
21
|
+
) ? String(attributes.backgroundType) : "color";
|
|
22
|
+
const image = objectValue(block, "backgroundImage");
|
|
23
|
+
const video = objectValue(block, "backgroundVideo");
|
|
24
|
+
const imageSrc = String(image.url ?? image.sourceUrl ?? "");
|
|
25
|
+
const videoSrc = String(video.url ?? video.sourceUrl ?? video.mediaItemUrl ?? "");
|
|
26
|
+
const scrimEnabled = Boolean(attributes.scrimEnabled) && backgroundType !== "color";
|
|
27
|
+
const scrimIntensity = Math.min(10, Math.max(1, Number(attributes.scrimIntensity) || 6));
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
<S
|
|
31
|
+
as="section"
|
|
32
|
+
{...semantic}
|
|
33
|
+
class="flowpress-hero"
|
|
34
|
+
style={`--flowpress-scrim-opacity:${scrimIntensity * 10}%`}
|
|
35
|
+
>
|
|
36
|
+
{backgroundType === "image" && imageSrc ? (
|
|
37
|
+
<img
|
|
38
|
+
class="flowpress-hero__media"
|
|
39
|
+
src={imageSrc}
|
|
40
|
+
alt=""
|
|
41
|
+
aria-hidden="true"
|
|
42
|
+
loading="eager"
|
|
43
|
+
decoding="async"
|
|
44
|
+
/>
|
|
45
|
+
) : null}
|
|
46
|
+
{backgroundType === "video" && videoSrc ? (
|
|
47
|
+
<video
|
|
48
|
+
class="flowpress-hero__media"
|
|
49
|
+
src={videoSrc}
|
|
50
|
+
poster={imageSrc || undefined}
|
|
51
|
+
autoplay
|
|
52
|
+
muted
|
|
53
|
+
loop
|
|
54
|
+
playsinline
|
|
55
|
+
preload="metadata"
|
|
56
|
+
aria-hidden="true"
|
|
57
|
+
tabindex="-1"
|
|
58
|
+
/>
|
|
59
|
+
) : null}
|
|
60
|
+
{scrimEnabled ? <span class="flowpress-hero__scrim" aria-hidden="true"></span> : null}
|
|
61
|
+
<div class="flowpress-hero__content">
|
|
62
|
+
<slot />
|
|
63
|
+
</div>
|
|
64
|
+
</S>
|
|
65
|
+
|
|
66
|
+
<style>
|
|
67
|
+
.flowpress-hero {
|
|
68
|
+
position: relative;
|
|
69
|
+
display: grid;
|
|
70
|
+
overflow: hidden;
|
|
71
|
+
isolation: isolate;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.flowpress-hero__media,
|
|
75
|
+
.flowpress-hero__scrim {
|
|
76
|
+
position: absolute;
|
|
77
|
+
inset: 0;
|
|
78
|
+
width: 100%;
|
|
79
|
+
height: 100%;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.flowpress-hero__media {
|
|
83
|
+
z-index: -2;
|
|
84
|
+
object-fit: cover;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.flowpress-hero__scrim {
|
|
88
|
+
z-index: -1;
|
|
89
|
+
background: color-mix(
|
|
90
|
+
in srgb,
|
|
91
|
+
var(--sf-background) var(--flowpress-scrim-opacity),
|
|
92
|
+
transparent
|
|
93
|
+
);
|
|
94
|
+
pointer-events: none;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.flowpress-hero__content {
|
|
98
|
+
position: relative;
|
|
99
|
+
display: grid;
|
|
100
|
+
gap: inherit;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
@media (prefers-reduced-motion: reduce) {
|
|
104
|
+
.flowpress-hero__media:is(video) {
|
|
105
|
+
display: none;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
</style>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
import S from "@styleflow.app/astro/S.astro";
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
item: Record<string, unknown>;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const { item } = Astro.props;
|
|
9
|
+
const image = item.image && typeof item.image === "object" && !Array.isArray(item.image)
|
|
10
|
+
? item.image as Record<string, unknown>
|
|
11
|
+
: {};
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
{image.url ? (
|
|
15
|
+
<img
|
|
16
|
+
src={String(image.url)}
|
|
17
|
+
alt={String(image.alt ?? "")}
|
|
18
|
+
loading="lazy"
|
|
19
|
+
decoding="async"
|
|
20
|
+
/>
|
|
21
|
+
) : null}
|
|
22
|
+
{item.uri ? (
|
|
23
|
+
<S as="a" href={String(item.uri)}>{String(item.title ?? "")}</S>
|
|
24
|
+
) : (
|
|
25
|
+
<S as="span">{String(item.title ?? "")}</S>
|
|
26
|
+
)}
|
|
27
|
+
{item.excerpt ? <S as="p">{String(item.excerpt)}</S> : null}
|
|
28
|
+
{item.priceHtml ? <S as="div" set:html={String(item.priceHtml)} /> : null}
|
|
@@ -0,0 +1,61 @@
|
|
|
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 { semanticProps, textValue, unwrapCoreHtml } from "./helpers.js";
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
block: FlowpressBlock;
|
|
9
|
+
contract?: StyleflowContract;
|
|
10
|
+
hasChildren?: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const { block, contract, hasChildren = false } = Astro.props;
|
|
14
|
+
const name = block.name.replace(/^sf\//, "");
|
|
15
|
+
const attributes = block.attributes;
|
|
16
|
+
const heading = name === "heading" || name === "core/heading";
|
|
17
|
+
const level = Math.min(6, Math.max(1, Number(attributes.level ?? 2)));
|
|
18
|
+
const headingTag = `h${level}` as "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
|
|
19
|
+
const defaults = heading
|
|
20
|
+
? {
|
|
21
|
+
ty: "heading",
|
|
22
|
+
v: String(attributes.v ?? attributes.variant ?? level),
|
|
23
|
+
w: String(attributes.w ?? attributes.weight ?? "strong"),
|
|
24
|
+
foreground: "primary"
|
|
25
|
+
}
|
|
26
|
+
: {
|
|
27
|
+
ty: "body",
|
|
28
|
+
v: String(attributes.v ?? (name.includes("quote") ? "lg" : "md")),
|
|
29
|
+
w: String(attributes.w ?? "default"),
|
|
30
|
+
foreground: "primary"
|
|
31
|
+
};
|
|
32
|
+
const semantic = semanticProps(attributes, contract, defaults);
|
|
33
|
+
const html = name === "heading"
|
|
34
|
+
? textValue(block)
|
|
35
|
+
: name === "rich-text"
|
|
36
|
+
? String(attributes.content ?? "")
|
|
37
|
+
: name === "quote"
|
|
38
|
+
? textValue(block, "quote")
|
|
39
|
+
: unwrapCoreHtml(String(attributes.content ?? attributes.value ?? block.innerHtml ?? ""));
|
|
40
|
+
const listTag = Boolean(attributes.ordered) ? "ol" : "ul";
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
{heading ? (
|
|
44
|
+
<S as={headingTag} {...semantic} set:html={html} />
|
|
45
|
+
) : name === "core/list" ? (
|
|
46
|
+
<S as={listTag} {...semantic}>
|
|
47
|
+
{hasChildren ? <slot /> : <Fragment set:html={html} />}
|
|
48
|
+
</S>
|
|
49
|
+
) : name === "core/list-item" ? (
|
|
50
|
+
<S as="li" {...semantic}>
|
|
51
|
+
<span set:html={html}></span>
|
|
52
|
+
{hasChildren ? <slot /> : null}
|
|
53
|
+
</S>
|
|
54
|
+
) : name === "quote" || name === "core/quote" ? (
|
|
55
|
+
<S as="blockquote" {...semantic}>
|
|
56
|
+
{hasChildren ? <slot /> : <div set:html={html}></div>}
|
|
57
|
+
{attributes.citation ? <cite>{String(attributes.citation)}</cite> : null}
|
|
58
|
+
</S>
|
|
59
|
+
) : (
|
|
60
|
+
<S as={name === "core/paragraph" ? "p" : "div"} {...semantic} set:html={html} />
|
|
61
|
+
)}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type StyleflowContract } from "../../styleflow.js";
|
|
2
|
+
import type { FlowpressBlock } from "../../types.js";
|
|
3
|
+
export declare const DEFAULT_SEMANTIC_PROPS: {
|
|
4
|
+
surface: string;
|
|
5
|
+
layoutRole: string;
|
|
6
|
+
density: string;
|
|
7
|
+
};
|
|
8
|
+
export declare function semanticProps(attributes: Record<string, unknown>, contract?: StyleflowContract, defaults?: Record<string, unknown>): Record<string, unknown>;
|
|
9
|
+
export declare function objectValue(block: FlowpressBlock, key: string): Record<string, unknown>;
|
|
10
|
+
export declare function textValue(block: FlowpressBlock, key?: string): string;
|
|
11
|
+
export declare function linkValue(block: FlowpressBlock, key?: string): string;
|
|
12
|
+
export declare function responsiveValues(attributes: Record<string, unknown>, prefix: "columns" | "span", fallback: number): Record<"base" | "sm" | "md" | "lg" | "xl" | "2xl", number>;
|
|
13
|
+
export declare function unwrapCoreHtml(html: string): string;
|
|
14
|
+
//# sourceMappingURL=helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/astro/blocks/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,iBAAiB,EACvB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAErD,eAAO,MAAM,sBAAsB;;;;CAIlC,CAAC;AAqBF,wBAAgB,aAAa,CAC3B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnC,QAAQ,CAAC,EAAE,iBAAiB,EAC5B,QAAQ,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAA0B,GACzD,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CASzB;AAED,wBAAgB,WAAW,CACzB,KAAK,EAAE,cAAc,EACrB,GAAG,EAAE,MAAM,GACV,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAKzB;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,EAAE,GAAG,SAAS,GAAG,MAAM,CAErE;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,EAAE,GAAG,SAAS,GAAG,MAAM,CAMrE;AAED,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnC,MAAM,EAAE,SAAS,GAAG,MAAM,EAC1B,QAAQ,EAAE,MAAM,GACf,MAAM,CAAC,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,KAAK,EAAE,MAAM,CAAC,CAe5D;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKnD"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { resolveDynamicStyleflowProps } from "../../styleflow.js";
|
|
2
|
+
export const DEFAULT_SEMANTIC_PROPS = {
|
|
3
|
+
surface: "default",
|
|
4
|
+
layoutRole: "stack",
|
|
5
|
+
density: "md"
|
|
6
|
+
};
|
|
7
|
+
const SEMANTIC_KEYS = [
|
|
8
|
+
"tone",
|
|
9
|
+
"intensity",
|
|
10
|
+
"surface",
|
|
11
|
+
"layoutRole",
|
|
12
|
+
"density",
|
|
13
|
+
"ty",
|
|
14
|
+
"v",
|
|
15
|
+
"w",
|
|
16
|
+
"foreground",
|
|
17
|
+
"interactivePriority",
|
|
18
|
+
"noBackground",
|
|
19
|
+
"noBorder",
|
|
20
|
+
"noPadding",
|
|
21
|
+
"noGap",
|
|
22
|
+
"noRadius",
|
|
23
|
+
"noForeground"
|
|
24
|
+
];
|
|
25
|
+
export function semanticProps(attributes, contract, defaults = DEFAULT_SEMANTIC_PROPS) {
|
|
26
|
+
const input = Object.fromEntries(SEMANTIC_KEYS
|
|
27
|
+
.filter((key) => attributes[key] !== undefined)
|
|
28
|
+
.map((key) => [key, attributes[key]]));
|
|
29
|
+
return contract
|
|
30
|
+
? resolveDynamicStyleflowProps(input, defaults, contract)
|
|
31
|
+
: { ...defaults, ...input };
|
|
32
|
+
}
|
|
33
|
+
export function objectValue(block, key) {
|
|
34
|
+
const value = block.attributes[key];
|
|
35
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
36
|
+
? value
|
|
37
|
+
: {};
|
|
38
|
+
}
|
|
39
|
+
export function textValue(block, key = "text") {
|
|
40
|
+
return String(block.attributes[key] ?? "");
|
|
41
|
+
}
|
|
42
|
+
export function linkValue(block, key = "link") {
|
|
43
|
+
const value = block.attributes[key];
|
|
44
|
+
const href = typeof value === "string"
|
|
45
|
+
? value
|
|
46
|
+
: String(objectValue(block, key).url ?? "");
|
|
47
|
+
return /^(?:\/(?!\/)|#|https?:\/\/|mailto:|tel:)/i.test(href) ? href : "";
|
|
48
|
+
}
|
|
49
|
+
export function responsiveValues(attributes, prefix, fallback) {
|
|
50
|
+
const keys = ["base", "sm", "md", "lg", "xl", "2xl"];
|
|
51
|
+
const result = {};
|
|
52
|
+
let inherited = fallback;
|
|
53
|
+
for (const key of keys) {
|
|
54
|
+
const suffix = key === "base"
|
|
55
|
+
? "Base"
|
|
56
|
+
: key === "2xl" ? "2xl" : key.charAt(0).toUpperCase() + key.slice(1);
|
|
57
|
+
const candidate = Number(attributes[`${prefix}${suffix}`]);
|
|
58
|
+
if (Number.isInteger(candidate) && candidate >= 1 && candidate <= 12) {
|
|
59
|
+
inherited = candidate;
|
|
60
|
+
}
|
|
61
|
+
result[key] = inherited;
|
|
62
|
+
}
|
|
63
|
+
return result;
|
|
64
|
+
}
|
|
65
|
+
export function unwrapCoreHtml(html) {
|
|
66
|
+
const match = html.trim().match(/^<(p|h[1-6]|ul|ol|li|blockquote)(?:\s[^>]*)?>([\s\S]*)<\/\1>$/);
|
|
67
|
+
return match?.[2] ?? html;
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../../src/astro/blocks/helpers.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n resolveDynamicStyleflowProps,\n type StyleflowContract\n} from \"../../styleflow.js\";\nimport type { FlowpressBlock } from \"../../types.js\";\n\nexport const DEFAULT_SEMANTIC_PROPS = {\n surface: \"default\",\n layoutRole: \"stack\",\n density: \"md\"\n};\n\nconst SEMANTIC_KEYS = [\n \"tone\",\n \"intensity\",\n \"surface\",\n \"layoutRole\",\n \"density\",\n \"ty\",\n \"v\",\n \"w\",\n \"foreground\",\n \"interactivePriority\",\n \"noBackground\",\n \"noBorder\",\n \"noPadding\",\n \"noGap\",\n \"noRadius\",\n \"noForeground\"\n] as const;\n\nexport function semanticProps(\n attributes: Record<string, unknown>,\n contract?: StyleflowContract,\n defaults: Record<string, unknown> = DEFAULT_SEMANTIC_PROPS\n): Record<string, unknown> {\n const input = Object.fromEntries(\n SEMANTIC_KEYS\n .filter((key) => attributes[key] !== undefined)\n .map((key) => [key, attributes[key]])\n );\n return contract\n ? resolveDynamicStyleflowProps(input, defaults, contract)\n : { ...defaults, ...input };\n}\n\nexport function objectValue(\n block: FlowpressBlock,\n key: string\n): Record<string, unknown> {\n const value = block.attributes[key];\n return value && typeof value === \"object\" && !Array.isArray(value)\n ? value as Record<string, unknown>\n : {};\n}\n\nexport function textValue(block: FlowpressBlock, key = \"text\"): string {\n return String(block.attributes[key] ?? \"\");\n}\n\nexport function linkValue(block: FlowpressBlock, key = \"link\"): string {\n const value = block.attributes[key];\n const href = typeof value === \"string\"\n ? value\n : String(objectValue(block, key).url ?? \"\");\n return /^(?:\\/(?!\\/)|#|https?:\\/\\/|mailto:|tel:)/i.test(href) ? href : \"\";\n}\n\nexport function responsiveValues(\n attributes: Record<string, unknown>,\n prefix: \"columns\" | \"span\",\n fallback: number\n): Record<\"base\" | \"sm\" | \"md\" | \"lg\" | \"xl\" | \"2xl\", number> {\n const keys = [\"base\", \"sm\", \"md\", \"lg\", \"xl\", \"2xl\"] as const;\n const result = {} as Record<(typeof keys)[number], number>;\n let inherited = fallback;\n for (const key of keys) {\n const suffix = key === \"base\"\n ? \"Base\"\n : key === \"2xl\" ? \"2xl\" : key.charAt(0).toUpperCase() + key.slice(1);\n const candidate = Number(attributes[`${prefix}${suffix}`]);\n if (Number.isInteger(candidate) && candidate >= 1 && candidate <= 12) {\n inherited = candidate;\n }\n result[key] = inherited;\n }\n return result;\n}\n\nexport function unwrapCoreHtml(html: string): string {\n const match = html.trim().match(\n /^<(p|h[1-6]|ul|ol|li|blockquote)(?:\\s[^>]*)?>([\\s\\S]*)<\\/\\1>$/\n );\n return match?.[2] ?? html;\n}\n"]}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import {
|
|
2
|
+
resolveDynamicStyleflowProps,
|
|
3
|
+
type StyleflowContract
|
|
4
|
+
} from "../../styleflow.js";
|
|
5
|
+
import type { FlowpressBlock } from "../../types.js";
|
|
6
|
+
|
|
7
|
+
export const DEFAULT_SEMANTIC_PROPS = {
|
|
8
|
+
surface: "default",
|
|
9
|
+
layoutRole: "stack",
|
|
10
|
+
density: "md"
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const SEMANTIC_KEYS = [
|
|
14
|
+
"tone",
|
|
15
|
+
"intensity",
|
|
16
|
+
"surface",
|
|
17
|
+
"layoutRole",
|
|
18
|
+
"density",
|
|
19
|
+
"ty",
|
|
20
|
+
"v",
|
|
21
|
+
"w",
|
|
22
|
+
"foreground",
|
|
23
|
+
"interactivePriority",
|
|
24
|
+
"noBackground",
|
|
25
|
+
"noBorder",
|
|
26
|
+
"noPadding",
|
|
27
|
+
"noGap",
|
|
28
|
+
"noRadius",
|
|
29
|
+
"noForeground"
|
|
30
|
+
] as const;
|
|
31
|
+
|
|
32
|
+
export function semanticProps(
|
|
33
|
+
attributes: Record<string, unknown>,
|
|
34
|
+
contract?: StyleflowContract,
|
|
35
|
+
defaults: Record<string, unknown> = DEFAULT_SEMANTIC_PROPS
|
|
36
|
+
): Record<string, unknown> {
|
|
37
|
+
const input = Object.fromEntries(
|
|
38
|
+
SEMANTIC_KEYS
|
|
39
|
+
.filter((key) => attributes[key] !== undefined)
|
|
40
|
+
.map((key) => [key, attributes[key]])
|
|
41
|
+
);
|
|
42
|
+
return contract
|
|
43
|
+
? resolveDynamicStyleflowProps(input, defaults, contract)
|
|
44
|
+
: { ...defaults, ...input };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function objectValue(
|
|
48
|
+
block: FlowpressBlock,
|
|
49
|
+
key: string
|
|
50
|
+
): Record<string, unknown> {
|
|
51
|
+
const value = block.attributes[key];
|
|
52
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
53
|
+
? value as Record<string, unknown>
|
|
54
|
+
: {};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function textValue(block: FlowpressBlock, key = "text"): string {
|
|
58
|
+
return String(block.attributes[key] ?? "");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function linkValue(block: FlowpressBlock, key = "link"): string {
|
|
62
|
+
const value = block.attributes[key];
|
|
63
|
+
const href = typeof value === "string"
|
|
64
|
+
? value
|
|
65
|
+
: String(objectValue(block, key).url ?? "");
|
|
66
|
+
return /^(?:\/(?!\/)|#|https?:\/\/|mailto:|tel:)/i.test(href) ? href : "";
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function responsiveValues(
|
|
70
|
+
attributes: Record<string, unknown>,
|
|
71
|
+
prefix: "columns" | "span",
|
|
72
|
+
fallback: number
|
|
73
|
+
): Record<"base" | "sm" | "md" | "lg" | "xl" | "2xl", number> {
|
|
74
|
+
const keys = ["base", "sm", "md", "lg", "xl", "2xl"] as const;
|
|
75
|
+
const result = {} as Record<(typeof keys)[number], number>;
|
|
76
|
+
let inherited = fallback;
|
|
77
|
+
for (const key of keys) {
|
|
78
|
+
const suffix = key === "base"
|
|
79
|
+
? "Base"
|
|
80
|
+
: key === "2xl" ? "2xl" : key.charAt(0).toUpperCase() + key.slice(1);
|
|
81
|
+
const candidate = Number(attributes[`${prefix}${suffix}`]);
|
|
82
|
+
if (Number.isInteger(candidate) && candidate >= 1 && candidate <= 12) {
|
|
83
|
+
inherited = candidate;
|
|
84
|
+
}
|
|
85
|
+
result[key] = inherited;
|
|
86
|
+
}
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function unwrapCoreHtml(html: string): string {
|
|
91
|
+
const match = html.trim().match(
|
|
92
|
+
/^<(p|h[1-6]|ul|ol|li|blockquote)(?:\s[^>]*)?>([\s\S]*)<\/\1>$/
|
|
93
|
+
);
|
|
94
|
+
return match?.[2] ?? html;
|
|
95
|
+
}
|
package/dist/styleflow.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"styleflow.d.ts","sourceRoot":"","sources":["../src/styleflow.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE;QACL,KAAK,EAAE,MAAM,EAAE,CAAC;QAChB,WAAW,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;QACpC,QAAQ,EAAE,MAAM,EAAE,CAAC;KACpB,CAAC;IACF,MAAM,EAAE;QACN,KAAK,EAAE,MAAM,EAAE,CAAC;QAChB,SAAS,EAAE,MAAM,EAAE,CAAC;KACrB,CAAC;IACF,UAAU,EAAE;QACV,KAAK,EAAE,MAAM,EAAE,CAAC;QAChB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QACnC,OAAO,EAAE,MAAM,EAAE,CAAC;KACnB,CAAC;IACF,WAAW,EAAE;QACX,UAAU,EAAE,MAAM,EAAE,CAAC;QACrB,MAAM,EAAE,MAAM,EAAE,CAAC;KAClB,CAAC;CACH;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,qBAAqB,CAAC;CAC7B;AAED,MAAM,MAAM,qBAAqB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"styleflow.d.ts","sourceRoot":"","sources":["../src/styleflow.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE;QACL,KAAK,EAAE,MAAM,EAAE,CAAC;QAChB,WAAW,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;QACpC,QAAQ,EAAE,MAAM,EAAE,CAAC;KACpB,CAAC;IACF,MAAM,EAAE;QACN,KAAK,EAAE,MAAM,EAAE,CAAC;QAChB,SAAS,EAAE,MAAM,EAAE,CAAC;KACrB,CAAC;IACF,UAAU,EAAE;QACV,KAAK,EAAE,MAAM,EAAE,CAAC;QAChB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QACnC,OAAO,EAAE,MAAM,EAAE,CAAC;KACnB,CAAC;IACF,WAAW,EAAE;QACX,UAAU,EAAE,MAAM,EAAE,CAAC;QACrB,MAAM,EAAE,MAAM,EAAE,CAAC;KAClB,CAAC;CACH;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,qBAAqB,CAAC;CAC7B;AAED,MAAM,MAAM,qBAAqB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAqB5D;;;GAGG;AACH,wBAAgB,4BAA4B,CAC1C,KAAK,EAAE,qBAAqB,EAC5B,QAAQ,EAAE,qBAAqB,EAC/B,QAAQ,EAAE,iBAAiB,GAC1B,qBAAqB,CA+CvB"}
|
package/dist/styleflow.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const INHERITED_COLOR_PROPS = new Set(["tone", "intensity"]);
|
|
2
|
+
const FOREGROUND_ROLES = ["primary", "secondary", "tertiary", "muted", "accent"];
|
|
2
3
|
const AXIS_BY_PROP = {
|
|
3
4
|
tone: ["color", "tones"],
|
|
4
5
|
intensity: ["color", "intensities"],
|
|
@@ -35,6 +36,12 @@ export function resolveDynamicStyleflowProps(input, defaults, contract) {
|
|
|
35
36
|
: defaults.v;
|
|
36
37
|
continue;
|
|
37
38
|
}
|
|
39
|
+
if (key === "foreground") {
|
|
40
|
+
resolved.foreground = FOREGROUND_ROLES.includes(String(value))
|
|
41
|
+
? value
|
|
42
|
+
: defaults.foreground;
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
38
45
|
const axisPath = AXIS_BY_PROP[key];
|
|
39
46
|
if (!axisPath) {
|
|
40
47
|
resolved[key] = value;
|
package/dist/styleflow.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"styleflow.js","sourceRoot":"","sources":["../src/styleflow.ts"],"names":[],"mappings":"AA2BA,MAAM,qBAAqB,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"styleflow.js","sourceRoot":"","sources":["../src/styleflow.ts"],"names":[],"mappings":"AA2BA,MAAM,qBAAqB,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;AAC7D,MAAM,gBAAgB,GAAG,CAAC,SAAS,EAAE,WAAW,EAAE,UAAU,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;AAEjF,MAAM,YAAY,GAAG;IACnB,IAAI,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC;IACxB,SAAS,EAAE,CAAC,OAAO,EAAE,aAAa,CAAC;IACnC,OAAO,EAAE,CAAC,OAAO,EAAE,UAAU,CAAC;IAC9B,UAAU,EAAE,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC/B,OAAO,EAAE,CAAC,QAAQ,EAAE,WAAW,CAAC;IAChC,EAAE,EAAE,CAAC,YAAY,EAAE,OAAO,CAAC;IAC3B,CAAC,EAAE,CAAC,YAAY,EAAE,SAAS,CAAC;IAC5B,mBAAmB,EAAE,CAAC,aAAa,EAAE,YAAY,CAAC;IAClD,gBAAgB,EAAE,CAAC,aAAa,EAAE,QAAQ,CAAC;CACnC,CAAC;AAEX,SAAS,aAAa,CAAC,SAAkB,EAAE,OAAgB;IACzD,OAAO,MAAM,CAAC,SAAS,CAAC,KAAK,MAAM,CAAC,OAAO,CAAC,CAAC;AAC/C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,4BAA4B,CAC1C,KAA4B,EAC5B,QAA+B,EAC/B,QAA2B;IAE3B,MAAM,QAAQ,GAA0B,EAAE,GAAG,QAAQ,EAAE,CAAC;IAExD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACjD,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;YAC1D,SAAS;QACX,CAAC;QAED,IAAI,qBAAqB,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YAC1D,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC;YACrB,SAAS;QACX,CAAC;QAED,IAAI,GAAG,KAAK,GAAG,EAAE,CAAC;YAChB,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,EAAE,IAAI,QAAQ,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;YACnD,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YAC/D,QAAQ,CAAC,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,aAAa,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;gBACpE,CAAC,CAAC,KAAK;gBACP,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;YACf,SAAS;QACX,CAAC;QAED,IAAI,GAAG,KAAK,YAAY,EAAE,CAAC;YACzB,QAAQ,CAAC,UAAU,GAAG,gBAAgB,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBAC5D,CAAC,CAAC,KAAK;gBACP,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC;YACxB,SAAS;QACX,CAAC;QAED,MAAM,QAAQ,GAAG,YAAY,CAAC,GAAgC,CAAC,CAAC;QAChE,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,QAAQ,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;YACtB,SAAS;QACX,CAAC;QAED,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,QAAQ,CAAC;QAC/B,MAAM,MAAM,GAAG,QAAQ,CAAC,IAGvB,CAAC;QACF,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5C,QAAQ,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,aAAa,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;YAChE,CAAC,CAAC,KAAK;YACP,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IACpB,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC","sourcesContent":["export interface StyleflowContractAxes {\n color: {\n tones: string[];\n intensities: Array<string | number>;\n surfaces: string[];\n };\n layout: {\n roles: string[];\n densities: string[];\n };\n typography: {\n types: string[];\n variants: Record<string, string[]>;\n weights: string[];\n };\n interaction: {\n priorities: string[];\n states: string[];\n };\n}\n\nexport interface StyleflowContract {\n axes: StyleflowContractAxes;\n}\n\nexport type DynamicStyleflowProps = Record<string, unknown>;\n\nconst INHERITED_COLOR_PROPS = new Set([\"tone\", \"intensity\"]);\nconst FOREGROUND_ROLES = [\"primary\", \"secondary\", \"tertiary\", \"muted\", \"accent\"];\n\nconst AXIS_BY_PROP = {\n tone: [\"color\", \"tones\"],\n intensity: [\"color\", \"intensities\"],\n surface: [\"color\", \"surfaces\"],\n layoutRole: [\"layout\", \"roles\"],\n density: [\"layout\", \"densities\"],\n ty: [\"typography\", \"types\"],\n w: [\"typography\", \"weights\"],\n interactivePriority: [\"interaction\", \"priorities\"],\n interactionState: [\"interaction\", \"states\"]\n} as const;\n\nfunction sameAxisValue(candidate: unknown, allowed: unknown): boolean {\n return String(candidate) === String(allowed);\n}\n\n/**\n * CMS values can outlive a contract revision. Invalid values fail closed to\n * component defaults before reaching Styleflow's Astro runtime.\n */\nexport function resolveDynamicStyleflowProps(\n input: DynamicStyleflowProps,\n defaults: DynamicStyleflowProps,\n contract: StyleflowContract\n): DynamicStyleflowProps {\n const resolved: DynamicStyleflowProps = { ...defaults };\n\n for (const [key, value] of Object.entries(input)) {\n if (value === undefined || value === null || value === \"\") {\n continue;\n }\n\n if (INHERITED_COLOR_PROPS.has(key) && value === \"inherit\") {\n delete resolved[key];\n continue;\n }\n\n if (key === \"v\") {\n const type = String(input.ty ?? defaults.ty ?? \"\");\n const variants = contract.axes.typography.variants[type] ?? [];\n resolved.v = variants.some((variant) => sameAxisValue(value, variant))\n ? value\n : defaults.v;\n continue;\n }\n\n if (key === \"foreground\") {\n resolved.foreground = FOREGROUND_ROLES.includes(String(value))\n ? value\n : defaults.foreground;\n continue;\n }\n\n const axisPath = AXIS_BY_PROP[key as keyof typeof AXIS_BY_PROP];\n if (!axisPath) {\n resolved[key] = value;\n continue;\n }\n\n const [group, axis] = axisPath;\n const groups = contract.axes as unknown as Record<\n string,\n Record<string, unknown[]>\n >;\n const allowed = groups[group]?.[axis] ?? [];\n resolved[key] = allowed.some((item) => sameAxisValue(value, item))\n ? value\n : defaults[key];\n }\n\n return resolved;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["../src/astro.d.ts","../src/client.ts","../src/env.ts","../src/index.ts","../src/styleflow.ts","../src/template-hierarchy.ts","../src/templates.ts","../src/types.ts"],"version":"5.9.3"}
|
|
1
|
+
{"root":["../src/astro.d.ts","../src/client.ts","../src/env.ts","../src/index.ts","../src/styleflow.ts","../src/template-hierarchy.ts","../src/templates.ts","../src/types.ts","../src/astro/blocks/helpers.ts"],"version":"5.9.3"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@styleflow.app/flowpress-astro-core",
|
|
3
|
-
"version": "0.2.0-beta.
|
|
3
|
+
"version": "0.2.0-beta.5",
|
|
4
4
|
"description": "Project-agnostic WordPress routing, templates, SEO and GraphQL runtime for FlowPress Astro apps.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
],
|
|
36
36
|
"scripts": {
|
|
37
37
|
"build": "tsc -b && node ./scripts/copy-astro.mjs",
|
|
38
|
-
"typecheck": "tsc -b --pretty false && node ./scripts/copy-astro.mjs",
|
|
38
|
+
"typecheck": "tsc -b --pretty false && node ./scripts/copy-astro.mjs && astro check",
|
|
39
39
|
"test": "vitest run",
|
|
40
40
|
"release:check": "npm run typecheck && npm test && npm pack --dry-run",
|
|
41
41
|
"release:publish:next": "npm publish --access public --tag next",
|
|
@@ -43,12 +43,13 @@
|
|
|
43
43
|
"prepublishOnly": "npm run typecheck && npm test && npm run build"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@styleflow.app/astro": "0.3.0-beta.
|
|
46
|
+
"@styleflow.app/astro": "0.3.0-beta.3"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
49
|
"astro": ">=7.0.0"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
|
+
"@astrojs/check": "^0.9.9",
|
|
52
53
|
"@types/node": "^22.19.3",
|
|
53
54
|
"astro": "^7.0.4",
|
|
54
55
|
"typescript": "^5.9.3",
|