@styleflow.app/flowpress-astro-core 0.2.0-beta.5 → 0.2.0-beta.8

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 CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.2.0-beta.8
6
+
7
+ - Applied Gutenberg `anchor` and `className` attributes to each rendered
8
+ block root, merging custom classes with the required FlowPress renderer
9
+ classes and preserving transparent template blocks when neither is set.
10
+
5
11
  ## 0.2.0-beta.5
6
12
 
7
13
  - Split Gutenberg rendering into dedicated hero, responsive layout, text,
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.5
12
+ pnpm add @styleflow.app/flowpress-astro-core@0.2.0-beta.8
13
13
  ```
14
14
 
15
15
  Prereleases are pinned exactly. The package expects Astro 7 or newer and the
@@ -34,7 +34,8 @@ canonical URL rewriting.
34
34
  StyleFlow text axes and resolved content/query templates. CF7 schemas
35
35
  serialized by WordPress become accessible client forms that submit directly
36
36
  to the protected FlowPress REST endpoint, so the same component works in
37
- static and server output modes.
37
+ static and server output modes. Gutenberg `anchor` and `className` attributes
38
+ are applied to the rendered block root as HTML `id` and `class`.
38
39
 
39
40
  ## Release
40
41
 
@@ -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
- <slot />
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
- <S.main tone="neutral" intensity={1} surface="default" layoutRole="container" density="md">
14
- <S.section tone="neutral" intensity={1} surface="default" layoutRole="section" density="md">
15
- <S.h1 ty="heading" v="1" w="strong">{isNotFound ? "Pagina non trovata" : title}</S.h1>
16
- {content?.excerpt && <S.p ty="body" v="md" w="default">{content.excerpt}</S.p>}
17
- {Array.isArray(content?.contentBlocks) && content.contentBlocks.length > 0 ? (
18
- <GutenbergRenderer
19
- blocks={content.contentBlocks as import("../types.js").FlowpressBlock[]}
20
- contract={styleflowContract}
21
- />
22
- ) : content?.content && (
23
- <S.div
24
- tone="neutral"
25
- intensity={1}
26
- surface="default"
27
- layoutRole="stack"
28
- density="md"
29
- set:html={String(content.content)}
30
- />
31
- )}
32
- </S.section>
33
- </S.main>
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>
@@ -9,6 +9,7 @@ import HeroBlock from "./blocks/HeroBlock.astro";
9
9
  import ListItemFallback from "./blocks/ListItemFallback.astro";
10
10
  import TextBlock from "./blocks/TextBlock.astro";
11
11
  import {
12
+ blockHtmlAttributes,
12
13
  linkValue,
13
14
  objectValue,
14
15
  semanticProps,
@@ -44,6 +45,7 @@ function youtubeEmbedUrl(value: unknown): string | null {
44
45
  const name = block.name.replace(/^sf\//, "");
45
46
  const children = block.innerBlocks ?? [];
46
47
  const semantic = semanticProps(block.attributes, contract);
48
+ const htmlAttributes = blockHtmlAttributes(block.attributes);
47
49
 
48
50
  if (name === "hero") {
49
51
  return (
@@ -97,6 +99,7 @@ function youtubeEmbedUrl(value: unknown): string | null {
97
99
  target={opensInNewTab ? "_blank" : undefined}
98
100
  rel={opensInNewTab ? "noopener noreferrer" : undefined}
99
101
  {...semantic}
102
+ {...htmlAttributes}
100
103
  >
101
104
  {textValue(block, "label")}
102
105
  </S>
@@ -112,7 +115,7 @@ function youtubeEmbedUrl(value: unknown): string | null {
112
115
  } | undefined;
113
116
  const src = image?.url ?? image?.sourceUrl;
114
117
  return src ? (
115
- <S as="div" {...semantic}>
118
+ <S as="div" {...semantic} {...htmlAttributes}>
116
119
  <img
117
120
  src={src}
118
121
  alt={String(block.attributes.alt ?? image?.alt ?? image?.altText ?? "")}
@@ -128,7 +131,7 @@ function youtubeEmbedUrl(value: unknown): string | null {
128
131
  ? block.attributes.images as Array<Record<string, unknown>>
129
132
  : [];
130
133
  return (
131
- <S as="div" {...semantic}>
134
+ <S as="div" {...semantic} {...htmlAttributes}>
132
135
  {images.map((image) => image.url ? (
133
136
  <img
134
137
  src={String(image.url)}
@@ -145,7 +148,7 @@ function youtubeEmbedUrl(value: unknown): string | null {
145
148
  const video = objectValue(block, "video");
146
149
  const poster = objectValue(block, "poster");
147
150
  return video.url ? (
148
- <S as="div" {...semantic}>
151
+ <S as="div" {...semantic} {...htmlAttributes}>
149
152
  <video
150
153
  src={String(video.url)}
151
154
  poster={poster.url ? String(poster.url) : undefined}
@@ -161,7 +164,7 @@ function youtubeEmbedUrl(value: unknown): string | null {
161
164
  if (name === "video-youtube") {
162
165
  const embed = youtubeEmbedUrl(block.attributes.url);
163
166
  return embed ? (
164
- <S as="div" {...semantic}>
167
+ <S as="div" {...semantic} {...htmlAttributes}>
165
168
  <iframe
166
169
  src={embed}
167
170
  title={String(block.attributes.title ?? "YouTube video")}
@@ -174,14 +177,18 @@ function youtubeEmbedUrl(value: unknown): string | null {
174
177
  }
175
178
 
176
179
  if (name === "chip") {
177
- return <S as="span" {...semantic}>{textValue(block, "label")}</S>;
180
+ return (
181
+ <S as="span" {...semantic} {...htmlAttributes}>
182
+ {textValue(block, "label")}
183
+ </S>
184
+ );
178
185
  }
179
186
 
180
187
  if (name === "card") {
181
188
  const media = objectValue(block, "image");
182
189
  const href = linkValue(block);
183
190
  return (
184
- <S as="article" {...semantic}>
191
+ <S as="article" {...semantic} {...htmlAttributes}>
185
192
  {media.url && (
186
193
  <img src={String(media.url)} alt={String(media.alt ?? "")} loading="lazy" />
187
194
  )}
@@ -200,7 +207,7 @@ function youtubeEmbedUrl(value: unknown): string | null {
200
207
  ? block.attributes.resolvedBlocks as FlowpressBlock[][]
201
208
  : [];
202
209
  return (
203
- <S as="ul" {...semantic}>
210
+ <S as="ul" {...semantic} {...htmlAttributes}>
204
211
  {items.map((item, index) => (
205
212
  <S as="li">
206
213
  {resolvedBlocks[index]?.length ? (
@@ -215,7 +222,13 @@ function youtubeEmbedUrl(value: unknown): string | null {
215
222
  }
216
223
 
217
224
  if (name === "template") {
218
- return <Astro.self blocks={children} contract={contract} />;
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
+ );
219
232
  }
220
233
 
221
234
  if (name === "form") {
@@ -224,7 +237,7 @@ function youtubeEmbedUrl(value: unknown): string | null {
224
237
 
225
238
  if (name === "accordion-item") {
226
239
  return (
227
- <S as="div" {...semantic}>
240
+ <S as="div" {...semantic} {...htmlAttributes}>
228
241
  <details>
229
242
  <summary>{String(block.attributes.title ?? "")}</summary>
230
243
  <Astro.self blocks={children} contract={contract} />
@@ -250,7 +263,7 @@ function youtubeEmbedUrl(value: unknown): string | null {
250
263
  + `?key=${encodeURIComponent(googleMapsApiKey)}`
251
264
  + `&q=${encodeURIComponent(query)}`;
252
265
  return (
253
- <S as="div" {...semantic}>
266
+ <S as="div" {...semantic} {...htmlAttributes}>
254
267
  <iframe
255
268
  src={source}
256
269
  title={address || "Map"}
@@ -261,7 +274,7 @@ function youtubeEmbedUrl(value: unknown): string | null {
261
274
  );
262
275
  }
263
276
  return query ? (
264
- <S as="div" {...semantic}>
277
+ <S as="div" {...semantic} {...htmlAttributes}>
265
278
  <S
266
279
  as="a"
267
280
  href={`https://www.openstreetmap.org/search?query=${encodeURIComponent(query)}`}
@@ -275,7 +288,7 @@ function youtubeEmbedUrl(value: unknown): string | null {
275
288
 
276
289
  const layoutTag = name === "section" ? "section" : "div";
277
290
  return (
278
- <S as={layoutTag} {...semantic}>
291
+ <S as={layoutTag} {...semantic} {...htmlAttributes}>
279
292
  {block.innerHtml && children.length === 0
280
293
  ? <S as="div" set:html={block.innerHtml} />
281
294
  : <Astro.self blocks={children} contract={contract} />}
@@ -2,7 +2,11 @@
2
2
  import S from "@styleflow.app/astro/S.astro";
3
3
  import type { FlowpressBlock } from "../../types.js";
4
4
  import type { StyleflowContract } from "../../styleflow.js";
5
- import { responsiveValues, semanticProps } from "./helpers.js";
5
+ import {
6
+ blockHtmlAttributes,
7
+ responsiveValues,
8
+ semanticProps
9
+ } from "./helpers.js";
6
10
 
7
11
  interface Props {
8
12
  block: FlowpressBlock;
@@ -18,6 +22,7 @@ const partialBreakpoint = ["base", "sm", "md", "lg", "xl", "2xl"].includes(
18
22
  String(block.attributes.partialContainerBreakpoint)
19
23
  ) ? String(block.attributes.partialContainerBreakpoint) : "lg";
20
24
  const semantic = semanticProps(block.attributes, contract);
25
+ const htmlAttributes = blockHtmlAttributes(block.attributes, "flowpress-column");
21
26
  const style = [
22
27
  `--flowpress-span-base:${values.base}`,
23
28
  `--flowpress-span-sm:${values.sm}`,
@@ -31,7 +36,7 @@ const style = [
31
36
  <S
32
37
  as="div"
33
38
  {...semantic}
34
- class="flowpress-column"
39
+ {...htmlAttributes}
35
40
  data-partial={partial}
36
41
  data-partial-breakpoint={partialBreakpoint}
37
42
  style={style}
@@ -2,7 +2,7 @@
2
2
  import S from "@styleflow.app/astro/S.astro";
3
3
  import type { FlowpressBlock } from "../../types.js";
4
4
  import type { StyleflowContract } from "../../styleflow.js";
5
- import { semanticProps } from "./helpers.js";
5
+ import { blockHtmlAttributes, semanticProps } from "./helpers.js";
6
6
 
7
7
  interface FormField {
8
8
  name: string;
@@ -30,6 +30,7 @@ interface Props {
30
30
  const { block, contract } = Astro.props;
31
31
  const schema = block.attributes.formSchema as FormSchema | undefined;
32
32
  const semantic = semanticProps(block.attributes, contract);
33
+ const htmlAttributes = blockHtmlAttributes(block.attributes);
33
34
  const labelFor = (field: FormField): string => field.name
34
35
  .replace(/[-_]+/g, " ")
35
36
  .replace(/\b\w/g, (letter) => letter.toUpperCase());
@@ -38,7 +39,7 @@ const optionLabel = (field: FormField, index: number): string =>
38
39
  ---
39
40
 
40
41
  {schema?.available ? (
41
- <S as="div" {...semantic}>
42
+ <S as="div" {...semantic} {...htmlAttributes}>
42
43
  <flowpress-cf7-form>
43
44
  <form action={schema.submitUrl} method="post" enctype="multipart/form-data" novalidate>
44
45
  {block.attributes.title || schema.title ? (
@@ -131,7 +132,7 @@ const optionLabel = (field: FormField, index: number): string =>
131
132
  </flowpress-cf7-form>
132
133
  </S>
133
134
  ) : (
134
- <S as="p" {...semantic} role="status">
135
+ <S as="p" {...semantic} {...htmlAttributes} role="status">
135
136
  Il modulo non è temporaneamente disponibile.
136
137
  </S>
137
138
  )}
@@ -2,7 +2,11 @@
2
2
  import S from "@styleflow.app/astro/S.astro";
3
3
  import type { FlowpressBlock } from "../../types.js";
4
4
  import type { StyleflowContract } from "../../styleflow.js";
5
- import { responsiveValues, semanticProps } from "./helpers.js";
5
+ import {
6
+ blockHtmlAttributes,
7
+ responsiveValues,
8
+ semanticProps
9
+ } from "./helpers.js";
6
10
 
7
11
  interface Props {
8
12
  block: FlowpressBlock;
@@ -23,6 +27,7 @@ const semantic = semanticProps(block.attributes, contract, {
23
27
  layoutRole: "container",
24
28
  density: "md"
25
29
  });
30
+ const htmlAttributes = blockHtmlAttributes(block.attributes, "flowpress-grid");
26
31
  const style = [
27
32
  `--flowpress-grid-base:${values.base}`,
28
33
  `--flowpress-grid-sm:${values.sm}`,
@@ -33,7 +38,7 @@ const style = [
33
38
  ].join(";");
34
39
  ---
35
40
 
36
- <S as="div" {...semantic} class="flowpress-grid" data-align={align} style={style}>
41
+ <S as="div" {...semantic} {...htmlAttributes} data-align={align} style={style}>
37
42
  <slot />
38
43
  </S>
39
44
 
@@ -2,7 +2,11 @@
2
2
  import S from "@styleflow.app/astro/S.astro";
3
3
  import type { FlowpressBlock } from "../../types.js";
4
4
  import type { StyleflowContract } from "../../styleflow.js";
5
- import { objectValue, semanticProps } from "./helpers.js";
5
+ import {
6
+ blockHtmlAttributes,
7
+ objectValue,
8
+ semanticProps
9
+ } from "./helpers.js";
6
10
 
7
11
  interface Props {
8
12
  block: FlowpressBlock;
@@ -16,6 +20,7 @@ const semantic = semanticProps(attributes, contract, {
16
20
  layoutRole: "region",
17
21
  density: "md"
18
22
  });
23
+ const htmlAttributes = blockHtmlAttributes(attributes, "flowpress-hero");
19
24
  const backgroundType = ["color", "image", "video"].includes(
20
25
  String(attributes.backgroundType)
21
26
  ) ? String(attributes.backgroundType) : "color";
@@ -30,7 +35,7 @@ const scrimIntensity = Math.min(10, Math.max(1, Number(attributes.scrimIntensity
30
35
  <S
31
36
  as="section"
32
37
  {...semantic}
33
- class="flowpress-hero"
38
+ {...htmlAttributes}
34
39
  style={`--flowpress-scrim-opacity:${scrimIntensity * 10}%`}
35
40
  >
36
41
  {backgroundType === "image" && imageSrc ? (
@@ -2,7 +2,12 @@
2
2
  import S from "@styleflow.app/astro/S.astro";
3
3
  import type { FlowpressBlock } from "../../types.js";
4
4
  import type { StyleflowContract } from "../../styleflow.js";
5
- import { semanticProps, textValue, unwrapCoreHtml } from "./helpers.js";
5
+ import {
6
+ blockHtmlAttributes,
7
+ semanticProps,
8
+ textValue,
9
+ unwrapCoreHtml
10
+ } from "./helpers.js";
6
11
 
7
12
  interface Props {
8
13
  block: FlowpressBlock;
@@ -30,6 +35,7 @@ const defaults = heading
30
35
  foreground: "primary"
31
36
  };
32
37
  const semantic = semanticProps(attributes, contract, defaults);
38
+ const htmlAttributes = blockHtmlAttributes(attributes);
33
39
  const html = name === "heading"
34
40
  ? textValue(block)
35
41
  : name === "rich-text"
@@ -41,21 +47,26 @@ const listTag = Boolean(attributes.ordered) ? "ol" : "ul";
41
47
  ---
42
48
 
43
49
  {heading ? (
44
- <S as={headingTag} {...semantic} set:html={html} />
50
+ <S as={headingTag} {...semantic} {...htmlAttributes} set:html={html} />
45
51
  ) : name === "core/list" ? (
46
- <S as={listTag} {...semantic}>
52
+ <S as={listTag} {...semantic} {...htmlAttributes}>
47
53
  {hasChildren ? <slot /> : <Fragment set:html={html} />}
48
54
  </S>
49
55
  ) : name === "core/list-item" ? (
50
- <S as="li" {...semantic}>
56
+ <S as="li" {...semantic} {...htmlAttributes}>
51
57
  <span set:html={html}></span>
52
58
  {hasChildren ? <slot /> : null}
53
59
  </S>
54
60
  ) : name === "quote" || name === "core/quote" ? (
55
- <S as="blockquote" {...semantic}>
61
+ <S as="blockquote" {...semantic} {...htmlAttributes}>
56
62
  {hasChildren ? <slot /> : <div set:html={html}></div>}
57
63
  {attributes.citation ? <cite>{String(attributes.citation)}</cite> : null}
58
64
  </S>
59
65
  ) : (
60
- <S as={name === "core/paragraph" ? "p" : "div"} {...semantic} set:html={html} />
66
+ <S
67
+ as={name === "core/paragraph" ? "p" : "div"}
68
+ {...semantic}
69
+ {...htmlAttributes}
70
+ set:html={html}
71
+ />
61
72
  )}
@@ -5,6 +5,10 @@ export declare const DEFAULT_SEMANTIC_PROPS: {
5
5
  layoutRole: string;
6
6
  density: string;
7
7
  };
8
+ export declare function blockHtmlAttributes(attributes: Record<string, unknown>, baseClass?: string): {
9
+ id?: string;
10
+ class?: string;
11
+ };
8
12
  export declare function semanticProps(attributes: Record<string, unknown>, contract?: StyleflowContract, defaults?: Record<string, unknown>): Record<string, unknown>;
9
13
  export declare function objectValue(block: FlowpressBlock, key: string): Record<string, unknown>;
10
14
  export declare function textValue(block: FlowpressBlock, key?: string): string;
@@ -1 +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"}
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;AAEF,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnC,SAAS,SAAK,GACb;IAAE,EAAE,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAgBjC;AAqBD,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"}
@@ -4,6 +4,22 @@ export const DEFAULT_SEMANTIC_PROPS = {
4
4
  layoutRole: "stack",
5
5
  density: "md"
6
6
  };
7
+ export function blockHtmlAttributes(attributes, baseClass = "") {
8
+ const anchor = typeof attributes.anchor === "string"
9
+ ? attributes.anchor.trim()
10
+ : "";
11
+ const customClass = typeof attributes.className === "string"
12
+ ? attributes.className.trim()
13
+ : "";
14
+ const className = [baseClass.trim(), customClass]
15
+ .filter(Boolean)
16
+ .join(" ")
17
+ .replace(/\s+/g, " ");
18
+ return {
19
+ ...(anchor ? { id: anchor } : {}),
20
+ ...(className ? { class: className } : {})
21
+ };
22
+ }
7
23
  const SEMANTIC_KEYS = [
8
24
  "tone",
9
25
  "intensity",
@@ -1 +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"]}
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\nexport function blockHtmlAttributes(\n attributes: Record<string, unknown>,\n baseClass = \"\"\n): { id?: string; class?: string } {\n const anchor = typeof attributes.anchor === \"string\"\n ? attributes.anchor.trim()\n : \"\";\n const customClass = typeof attributes.className === \"string\"\n ? attributes.className.trim()\n : \"\";\n const className = [baseClass.trim(), customClass]\n .filter(Boolean)\n .join(\" \")\n .replace(/\\s+/g, \" \");\n\n return {\n ...(anchor ? { id: anchor } : {}),\n ...(className ? { class: className } : {})\n };\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"]}
@@ -10,6 +10,27 @@ export const DEFAULT_SEMANTIC_PROPS = {
10
10
  density: "md"
11
11
  };
12
12
 
13
+ export function blockHtmlAttributes(
14
+ attributes: Record<string, unknown>,
15
+ baseClass = ""
16
+ ): { id?: string; class?: string } {
17
+ const anchor = typeof attributes.anchor === "string"
18
+ ? attributes.anchor.trim()
19
+ : "";
20
+ const customClass = typeof attributes.className === "string"
21
+ ? attributes.className.trim()
22
+ : "";
23
+ const className = [baseClass.trim(), customClass]
24
+ .filter(Boolean)
25
+ .join(" ")
26
+ .replace(/\s+/g, " ");
27
+
28
+ return {
29
+ ...(anchor ? { id: anchor } : {}),
30
+ ...(className ? { class: className } : {})
31
+ };
32
+ }
33
+
13
34
  const SEMANTIC_KEYS = [
14
35
  "tone",
15
36
  "intensity",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@styleflow.app/flowpress-astro-core",
3
- "version": "0.2.0-beta.5",
3
+ "version": "0.2.0-beta.8",
4
4
  "description": "Project-agnostic WordPress routing, templates, SEO and GraphQL runtime for FlowPress Astro apps.",
5
5
  "type": "module",
6
6
  "private": false,