@styleflow.app/flowpress-astro-core 0.2.0-beta.2 → 0.2.0-beta.21
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 +111 -0
- package/README.md +59 -1
- package/dist/astro/BaseLayout.astro +7 -1
- package/dist/astro/DefaultTemplate.astro +31 -21
- package/dist/astro/GutenbergRenderer.astro +151 -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 +119 -0
- package/dist/astro/blocks/ListItemFallback.astro +28 -0
- package/dist/astro/blocks/NumberSpinnerBlock.astro +130 -0
- package/dist/astro/blocks/TextBlock.astro +83 -0
- package/dist/astro/blocks/helpers.d.ts +32 -0
- package/dist/astro/blocks/helpers.d.ts.map +1 -0
- package/dist/astro/blocks/helpers.js +152 -0
- package/dist/astro/blocks/helpers.js.map +1 -0
- package/dist/astro/blocks/helpers.ts +211 -0
- package/dist/block-renderers.d.ts +10 -0
- package/dist/block-renderers.d.ts.map +1 -0
- package/dist/block-renderers.js +2 -0
- package/dist/block-renderers.js.map +1 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +26 -1
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/number-spinner.d.ts +6 -0
- package/dist/number-spinner.d.ts.map +1 -0
- package/dist/number-spinner.js +136 -0
- package/dist/number-spinner.js.map +1 -0
- package/dist/style-classes.d.ts +9 -0
- package/dist/style-classes.d.ts.map +1 -0
- package/dist/style-classes.js +47 -0
- package/dist/style-classes.js.map +1 -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/dist/types.d.ts +8 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +6 -4
|
@@ -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
|
+
preserveEmptyTextBlock,
|
|
8
|
+
preserveRichTextSpacing,
|
|
9
|
+
semanticProps,
|
|
10
|
+
textValue,
|
|
11
|
+
unwrapCoreHtml
|
|
12
|
+
} from "./helpers.js";
|
|
13
|
+
|
|
14
|
+
interface Props {
|
|
15
|
+
block: FlowpressBlock;
|
|
16
|
+
contract?: StyleflowContract;
|
|
17
|
+
hasChildren?: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const { block, contract, hasChildren = false } = Astro.props;
|
|
21
|
+
const name = block.name.replace(/^sf\//, "");
|
|
22
|
+
const attributes = block.attributes;
|
|
23
|
+
const heading = name === "heading" || name === "core/heading";
|
|
24
|
+
const level = Math.min(6, Math.max(1, Number(attributes.level ?? 2)));
|
|
25
|
+
const headingTag = `h${level}` as "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
|
|
26
|
+
const defaults = heading
|
|
27
|
+
? {
|
|
28
|
+
ty: "heading",
|
|
29
|
+
v: String(attributes.v ?? attributes.variant ?? level),
|
|
30
|
+
w: String(attributes.w ?? attributes.weight ?? "strong"),
|
|
31
|
+
foreground: "primary"
|
|
32
|
+
}
|
|
33
|
+
: {
|
|
34
|
+
ty: "body",
|
|
35
|
+
v: String(attributes.v ?? (name.includes("quote") ? "lg" : "md")),
|
|
36
|
+
w: String(attributes.w ?? "default"),
|
|
37
|
+
foreground: "primary"
|
|
38
|
+
};
|
|
39
|
+
const semantic = semanticProps(attributes, contract, defaults);
|
|
40
|
+
const htmlAttributes = blockHtmlAttributes(attributes);
|
|
41
|
+
const coreHtml = String(
|
|
42
|
+
attributes.content
|
|
43
|
+
?? attributes.value
|
|
44
|
+
?? block.innerHtml
|
|
45
|
+
?? ""
|
|
46
|
+
);
|
|
47
|
+
const normalizedHtml = name === "heading"
|
|
48
|
+
? textValue(block)
|
|
49
|
+
: name === "rich-text"
|
|
50
|
+
? preserveRichTextSpacing(String(attributes.content ?? ""))
|
|
51
|
+
: name === "quote"
|
|
52
|
+
? textValue(block, "quote")
|
|
53
|
+
: unwrapCoreHtml(preserveRichTextSpacing(coreHtml));
|
|
54
|
+
const html = name === "core/list"
|
|
55
|
+
? normalizedHtml
|
|
56
|
+
: preserveEmptyTextBlock(normalizedHtml);
|
|
57
|
+
const listTag = Boolean(attributes.ordered) ? "ol" : "ul";
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
{heading ? (
|
|
61
|
+
<S as={headingTag} {...semantic} {...htmlAttributes} set:html={html} />
|
|
62
|
+
) : name === "core/list" ? (
|
|
63
|
+
<S as={listTag} {...semantic} {...htmlAttributes}>
|
|
64
|
+
{hasChildren ? <slot /> : <Fragment set:html={html} />}
|
|
65
|
+
</S>
|
|
66
|
+
) : name === "core/list-item" ? (
|
|
67
|
+
<S as="li" {...semantic} {...htmlAttributes}>
|
|
68
|
+
<span set:html={html}></span>
|
|
69
|
+
{hasChildren ? <slot /> : null}
|
|
70
|
+
</S>
|
|
71
|
+
) : name === "quote" || name === "core/quote" ? (
|
|
72
|
+
<S as="blockquote" {...semantic} {...htmlAttributes}>
|
|
73
|
+
{hasChildren ? <slot /> : <div set:html={html}></div>}
|
|
74
|
+
{attributes.citation ? <cite>{String(attributes.citation)}</cite> : null}
|
|
75
|
+
</S>
|
|
76
|
+
) : (
|
|
77
|
+
<S
|
|
78
|
+
as={name === "core/paragraph" ? "p" : "div"}
|
|
79
|
+
{...semantic}
|
|
80
|
+
{...htmlAttributes}
|
|
81
|
+
set:html={html}
|
|
82
|
+
/>
|
|
83
|
+
)}
|
|
@@ -0,0 +1,32 @@
|
|
|
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 const DEFAULT_HERO_OBJECT_POSITION = "50% 50%";
|
|
9
|
+
export declare function heroObjectPosition(value: unknown): string;
|
|
10
|
+
export declare function blockHtmlAttributes(attributes: Record<string, unknown>, baseClass?: string): {
|
|
11
|
+
id?: string;
|
|
12
|
+
class?: string;
|
|
13
|
+
};
|
|
14
|
+
export declare const TEXT_HTML_TAGS: readonly ["span", "p", "h1", "h2", "h3", "h4", "h5", "h6", "strong", "em", "small", "mark"];
|
|
15
|
+
export type TextHtmlTag = (typeof TEXT_HTML_TAGS)[number];
|
|
16
|
+
export declare function textHtmlTag(value: unknown): TextHtmlTag;
|
|
17
|
+
export declare function semanticProps(attributes: Record<string, unknown>, contract?: StyleflowContract, defaults?: Record<string, unknown>): Record<string, unknown>;
|
|
18
|
+
export declare function prefixedTextSemanticProps(attributes: Record<string, unknown>, prefix: string, contract?: StyleflowContract, defaults?: Record<string, unknown>): Record<string, unknown>;
|
|
19
|
+
export declare function objectValue(block: FlowpressBlock, key: string): Record<string, unknown>;
|
|
20
|
+
export declare function textValue(block: FlowpressBlock, key?: string): string;
|
|
21
|
+
/**
|
|
22
|
+
* Keep intentional blank lines visible across current and legacy RichText
|
|
23
|
+
* serializations. Gutenberg can represent them either as repeated `<br>` tags,
|
|
24
|
+
* empty paragraph elements, or literal newlines in an attribute value.
|
|
25
|
+
*/
|
|
26
|
+
export declare function preserveRichTextSpacing(html: string): string;
|
|
27
|
+
/** Keep an empty text element at its typographic line height without visible text. */
|
|
28
|
+
export declare function preserveEmptyTextBlock(html: string): string;
|
|
29
|
+
export declare function linkValue(block: FlowpressBlock, key?: string): string;
|
|
30
|
+
export declare function responsiveValues(attributes: Record<string, unknown>, prefix: "columns" | "span", fallback: number): Record<"base" | "sm" | "md" | "lg" | "xl" | "2xl", number>;
|
|
31
|
+
export declare function unwrapCoreHtml(html: string): string;
|
|
32
|
+
//# 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;AAEF,eAAO,MAAM,4BAA4B,YAAY,CAAC;AAEtD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAmBzD;AAED,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,eAAO,MAAM,cAAc,6FAajB,CAAC;AAEX,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAE1D,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,WAAW,CAKvD;AAED,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,yBAAyB,CACvC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnC,MAAM,EAAE,MAAM,EACd,QAAQ,CAAC,EAAE,iBAAiB,EAC5B,QAAQ,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACrC,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;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAoB5D;AAED,sFAAsF;AACtF,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE3D;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,152 @@
|
|
|
1
|
+
import { resolveDynamicStyleflowProps } from "../../styleflow.js";
|
|
2
|
+
export const DEFAULT_SEMANTIC_PROPS = {
|
|
3
|
+
surface: "default",
|
|
4
|
+
layoutRole: "stack",
|
|
5
|
+
density: "md"
|
|
6
|
+
};
|
|
7
|
+
export const DEFAULT_HERO_OBJECT_POSITION = "50% 50%";
|
|
8
|
+
export function heroObjectPosition(value) {
|
|
9
|
+
if (typeof value !== "string") {
|
|
10
|
+
return DEFAULT_HERO_OBJECT_POSITION;
|
|
11
|
+
}
|
|
12
|
+
const match = value.trim().match(/^(\d+(?:\.\d+)?|\.\d+)%\s+(\d+(?:\.\d+)?|\.\d+)%$/);
|
|
13
|
+
if (!match
|
|
14
|
+
|| [match[1], match[2]].some((coordinate) => {
|
|
15
|
+
const numeric = Number(coordinate);
|
|
16
|
+
return !Number.isFinite(numeric) || numeric < 0 || numeric > 100;
|
|
17
|
+
})) {
|
|
18
|
+
return DEFAULT_HERO_OBJECT_POSITION;
|
|
19
|
+
}
|
|
20
|
+
return `${match[1]}% ${match[2]}%`;
|
|
21
|
+
}
|
|
22
|
+
export function blockHtmlAttributes(attributes, baseClass = "") {
|
|
23
|
+
const anchor = typeof attributes.anchor === "string"
|
|
24
|
+
? attributes.anchor.trim()
|
|
25
|
+
: "";
|
|
26
|
+
const customClass = typeof attributes.className === "string"
|
|
27
|
+
? attributes.className.trim()
|
|
28
|
+
: "";
|
|
29
|
+
const className = [baseClass.trim(), customClass]
|
|
30
|
+
.filter(Boolean)
|
|
31
|
+
.join(" ")
|
|
32
|
+
.replace(/\s+/g, " ");
|
|
33
|
+
return {
|
|
34
|
+
...(anchor ? { id: anchor } : {}),
|
|
35
|
+
...(className ? { class: className } : {})
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
const SEMANTIC_KEYS = [
|
|
39
|
+
"tone",
|
|
40
|
+
"intensity",
|
|
41
|
+
"surface",
|
|
42
|
+
"layoutRole",
|
|
43
|
+
"density",
|
|
44
|
+
"ty",
|
|
45
|
+
"v",
|
|
46
|
+
"w",
|
|
47
|
+
"foreground",
|
|
48
|
+
"interactivePriority",
|
|
49
|
+
"noBackground",
|
|
50
|
+
"noBorder",
|
|
51
|
+
"noPadding",
|
|
52
|
+
"noGap",
|
|
53
|
+
"noRadius",
|
|
54
|
+
"noForeground"
|
|
55
|
+
];
|
|
56
|
+
export const TEXT_HTML_TAGS = [
|
|
57
|
+
"span",
|
|
58
|
+
"p",
|
|
59
|
+
"h1",
|
|
60
|
+
"h2",
|
|
61
|
+
"h3",
|
|
62
|
+
"h4",
|
|
63
|
+
"h5",
|
|
64
|
+
"h6",
|
|
65
|
+
"strong",
|
|
66
|
+
"em",
|
|
67
|
+
"small",
|
|
68
|
+
"mark"
|
|
69
|
+
];
|
|
70
|
+
export function textHtmlTag(value) {
|
|
71
|
+
const candidate = String(value ?? "span");
|
|
72
|
+
return TEXT_HTML_TAGS.includes(candidate)
|
|
73
|
+
? candidate
|
|
74
|
+
: "span";
|
|
75
|
+
}
|
|
76
|
+
export function semanticProps(attributes, contract, defaults = DEFAULT_SEMANTIC_PROPS) {
|
|
77
|
+
const input = Object.fromEntries(SEMANTIC_KEYS
|
|
78
|
+
.filter((key) => attributes[key] !== undefined)
|
|
79
|
+
.map((key) => [key, attributes[key]]));
|
|
80
|
+
return contract
|
|
81
|
+
? resolveDynamicStyleflowProps(input, defaults, contract)
|
|
82
|
+
: { ...defaults, ...input };
|
|
83
|
+
}
|
|
84
|
+
export function prefixedTextSemanticProps(attributes, prefix, contract, defaults = {}) {
|
|
85
|
+
const attributeName = (name) => `${prefix}${name.charAt(0).toUpperCase()}${name.slice(1)}`;
|
|
86
|
+
return semanticProps({
|
|
87
|
+
ty: attributes[attributeName("ty")],
|
|
88
|
+
v: attributes[attributeName("v")],
|
|
89
|
+
w: attributes[attributeName("w")],
|
|
90
|
+
foreground: attributes[attributeName("foreground")]
|
|
91
|
+
}, contract, defaults);
|
|
92
|
+
}
|
|
93
|
+
export function objectValue(block, key) {
|
|
94
|
+
const value = block.attributes[key];
|
|
95
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
96
|
+
? value
|
|
97
|
+
: {};
|
|
98
|
+
}
|
|
99
|
+
export function textValue(block, key = "text") {
|
|
100
|
+
return String(block.attributes[key] ?? "");
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Keep intentional blank lines visible across current and legacy RichText
|
|
104
|
+
* serializations. Gutenberg can represent them either as repeated `<br>` tags,
|
|
105
|
+
* empty paragraph elements, or literal newlines in an attribute value.
|
|
106
|
+
*/
|
|
107
|
+
export function preserveRichTextSpacing(html) {
|
|
108
|
+
const visibleEmptyBlocks = html
|
|
109
|
+
.replace(/\r\n?/g, "\n")
|
|
110
|
+
.replace(/<(p|div)(\s[^>]*)?>(?:\s| | )*<\/\1>/gi, (_match, tag, attributes = "") => `<${tag}${attributes}> </${tag}>`);
|
|
111
|
+
return visibleEmptyBlocks
|
|
112
|
+
.split(/(<[^>]+>)/g)
|
|
113
|
+
.map((part) => {
|
|
114
|
+
if (part.startsWith("<") || part.trim() === "") {
|
|
115
|
+
return part;
|
|
116
|
+
}
|
|
117
|
+
return part.replace(/\n(?:[ \t]*\n)+/g, (newlines) => "<br>".repeat((newlines.match(/\n/g) || []).length));
|
|
118
|
+
})
|
|
119
|
+
.join("");
|
|
120
|
+
}
|
|
121
|
+
/** Keep an empty text element at its typographic line height without visible text. */
|
|
122
|
+
export function preserveEmptyTextBlock(html) {
|
|
123
|
+
return html.trim() === "" ? " " : html;
|
|
124
|
+
}
|
|
125
|
+
export function linkValue(block, key = "link") {
|
|
126
|
+
const value = block.attributes[key];
|
|
127
|
+
const href = typeof value === "string"
|
|
128
|
+
? value
|
|
129
|
+
: String(objectValue(block, key).url ?? "");
|
|
130
|
+
return /^(?:\/(?!\/)|#|https?:\/\/|mailto:|tel:)/i.test(href) ? href : "";
|
|
131
|
+
}
|
|
132
|
+
export function responsiveValues(attributes, prefix, fallback) {
|
|
133
|
+
const keys = ["base", "sm", "md", "lg", "xl", "2xl"];
|
|
134
|
+
const result = {};
|
|
135
|
+
let inherited = fallback;
|
|
136
|
+
for (const key of keys) {
|
|
137
|
+
const suffix = key === "base"
|
|
138
|
+
? "Base"
|
|
139
|
+
: key === "2xl" ? "2xl" : key.charAt(0).toUpperCase() + key.slice(1);
|
|
140
|
+
const candidate = Number(attributes[`${prefix}${suffix}`]);
|
|
141
|
+
if (Number.isInteger(candidate) && candidate >= 1 && candidate <= 12) {
|
|
142
|
+
inherited = candidate;
|
|
143
|
+
}
|
|
144
|
+
result[key] = inherited;
|
|
145
|
+
}
|
|
146
|
+
return result;
|
|
147
|
+
}
|
|
148
|
+
export function unwrapCoreHtml(html) {
|
|
149
|
+
const match = html.trim().match(/^<(p|h[1-6]|ul|ol|li|blockquote)(?:\s[^>]*)?>([\s\S]*)<\/\1>$/);
|
|
150
|
+
return match?.[2] ?? html;
|
|
151
|
+
}
|
|
152
|
+
//# 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\nexport const DEFAULT_HERO_OBJECT_POSITION = \"50% 50%\";\n\nexport function heroObjectPosition(value: unknown): string {\n if (typeof value !== \"string\") {\n return DEFAULT_HERO_OBJECT_POSITION;\n }\n\n const match = value.trim().match(\n /^(\\d+(?:\\.\\d+)?|\\.\\d+)%\\s+(\\d+(?:\\.\\d+)?|\\.\\d+)%$/\n );\n if (\n !match\n || [match[1], match[2]].some((coordinate) => {\n const numeric = Number(coordinate);\n return !Number.isFinite(numeric) || numeric < 0 || numeric > 100;\n })\n ) {\n return DEFAULT_HERO_OBJECT_POSITION;\n }\n\n return `${match[1]}% ${match[2]}%`;\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 const TEXT_HTML_TAGS = [\n \"span\",\n \"p\",\n \"h1\",\n \"h2\",\n \"h3\",\n \"h4\",\n \"h5\",\n \"h6\",\n \"strong\",\n \"em\",\n \"small\",\n \"mark\"\n] as const;\n\nexport type TextHtmlTag = (typeof TEXT_HTML_TAGS)[number];\n\nexport function textHtmlTag(value: unknown): TextHtmlTag {\n const candidate = String(value ?? \"span\");\n return TEXT_HTML_TAGS.includes(candidate as TextHtmlTag)\n ? candidate as TextHtmlTag\n : \"span\";\n}\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 prefixedTextSemanticProps(\n attributes: Record<string, unknown>,\n prefix: string,\n contract?: StyleflowContract,\n defaults: Record<string, unknown> = {}\n): Record<string, unknown> {\n const attributeName = (name: string) =>\n `${prefix}${name.charAt(0).toUpperCase()}${name.slice(1)}`;\n return semanticProps({\n ty: attributes[attributeName(\"ty\")],\n v: attributes[attributeName(\"v\")],\n w: attributes[attributeName(\"w\")],\n foreground: attributes[attributeName(\"foreground\")]\n }, contract, defaults);\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\n/**\n * Keep intentional blank lines visible across current and legacy RichText\n * serializations. Gutenberg can represent them either as repeated `<br>` tags,\n * empty paragraph elements, or literal newlines in an attribute value.\n */\nexport function preserveRichTextSpacing(html: string): string {\n const visibleEmptyBlocks = html\n .replace(/\\r\\n?/g, \"\\n\")\n .replace(\n /<(p|div)(\\s[^>]*)?>(?:\\s| | )*<\\/\\1>/gi,\n (_match, tag: string, attributes = \"\") =>\n `<${tag}${attributes}> </${tag}>`\n );\n\n return visibleEmptyBlocks\n .split(/(<[^>]+>)/g)\n .map((part) => {\n if (part.startsWith(\"<\") || part.trim() === \"\") {\n return part;\n }\n return part.replace(/\\n(?:[ \\t]*\\n)+/g, (newlines) =>\n \"<br>\".repeat((newlines.match(/\\n/g) || []).length)\n );\n })\n .join(\"\");\n}\n\n/** Keep an empty text element at its typographic line height without visible text. */\nexport function preserveEmptyTextBlock(html: string): string {\n return html.trim() === \"\" ? \" \" : html;\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,211 @@
|
|
|
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
|
+
export const DEFAULT_HERO_OBJECT_POSITION = "50% 50%";
|
|
14
|
+
|
|
15
|
+
export function heroObjectPosition(value: unknown): string {
|
|
16
|
+
if (typeof value !== "string") {
|
|
17
|
+
return DEFAULT_HERO_OBJECT_POSITION;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const match = value.trim().match(
|
|
21
|
+
/^(\d+(?:\.\d+)?|\.\d+)%\s+(\d+(?:\.\d+)?|\.\d+)%$/
|
|
22
|
+
);
|
|
23
|
+
if (
|
|
24
|
+
!match
|
|
25
|
+
|| [match[1], match[2]].some((coordinate) => {
|
|
26
|
+
const numeric = Number(coordinate);
|
|
27
|
+
return !Number.isFinite(numeric) || numeric < 0 || numeric > 100;
|
|
28
|
+
})
|
|
29
|
+
) {
|
|
30
|
+
return DEFAULT_HERO_OBJECT_POSITION;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return `${match[1]}% ${match[2]}%`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function blockHtmlAttributes(
|
|
37
|
+
attributes: Record<string, unknown>,
|
|
38
|
+
baseClass = ""
|
|
39
|
+
): { id?: string; class?: string } {
|
|
40
|
+
const anchor = typeof attributes.anchor === "string"
|
|
41
|
+
? attributes.anchor.trim()
|
|
42
|
+
: "";
|
|
43
|
+
const customClass = typeof attributes.className === "string"
|
|
44
|
+
? attributes.className.trim()
|
|
45
|
+
: "";
|
|
46
|
+
const className = [baseClass.trim(), customClass]
|
|
47
|
+
.filter(Boolean)
|
|
48
|
+
.join(" ")
|
|
49
|
+
.replace(/\s+/g, " ");
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
...(anchor ? { id: anchor } : {}),
|
|
53
|
+
...(className ? { class: className } : {})
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const SEMANTIC_KEYS = [
|
|
58
|
+
"tone",
|
|
59
|
+
"intensity",
|
|
60
|
+
"surface",
|
|
61
|
+
"layoutRole",
|
|
62
|
+
"density",
|
|
63
|
+
"ty",
|
|
64
|
+
"v",
|
|
65
|
+
"w",
|
|
66
|
+
"foreground",
|
|
67
|
+
"interactivePriority",
|
|
68
|
+
"noBackground",
|
|
69
|
+
"noBorder",
|
|
70
|
+
"noPadding",
|
|
71
|
+
"noGap",
|
|
72
|
+
"noRadius",
|
|
73
|
+
"noForeground"
|
|
74
|
+
] as const;
|
|
75
|
+
|
|
76
|
+
export const TEXT_HTML_TAGS = [
|
|
77
|
+
"span",
|
|
78
|
+
"p",
|
|
79
|
+
"h1",
|
|
80
|
+
"h2",
|
|
81
|
+
"h3",
|
|
82
|
+
"h4",
|
|
83
|
+
"h5",
|
|
84
|
+
"h6",
|
|
85
|
+
"strong",
|
|
86
|
+
"em",
|
|
87
|
+
"small",
|
|
88
|
+
"mark"
|
|
89
|
+
] as const;
|
|
90
|
+
|
|
91
|
+
export type TextHtmlTag = (typeof TEXT_HTML_TAGS)[number];
|
|
92
|
+
|
|
93
|
+
export function textHtmlTag(value: unknown): TextHtmlTag {
|
|
94
|
+
const candidate = String(value ?? "span");
|
|
95
|
+
return TEXT_HTML_TAGS.includes(candidate as TextHtmlTag)
|
|
96
|
+
? candidate as TextHtmlTag
|
|
97
|
+
: "span";
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function semanticProps(
|
|
101
|
+
attributes: Record<string, unknown>,
|
|
102
|
+
contract?: StyleflowContract,
|
|
103
|
+
defaults: Record<string, unknown> = DEFAULT_SEMANTIC_PROPS
|
|
104
|
+
): Record<string, unknown> {
|
|
105
|
+
const input = Object.fromEntries(
|
|
106
|
+
SEMANTIC_KEYS
|
|
107
|
+
.filter((key) => attributes[key] !== undefined)
|
|
108
|
+
.map((key) => [key, attributes[key]])
|
|
109
|
+
);
|
|
110
|
+
return contract
|
|
111
|
+
? resolveDynamicStyleflowProps(input, defaults, contract)
|
|
112
|
+
: { ...defaults, ...input };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function prefixedTextSemanticProps(
|
|
116
|
+
attributes: Record<string, unknown>,
|
|
117
|
+
prefix: string,
|
|
118
|
+
contract?: StyleflowContract,
|
|
119
|
+
defaults: Record<string, unknown> = {}
|
|
120
|
+
): Record<string, unknown> {
|
|
121
|
+
const attributeName = (name: string) =>
|
|
122
|
+
`${prefix}${name.charAt(0).toUpperCase()}${name.slice(1)}`;
|
|
123
|
+
return semanticProps({
|
|
124
|
+
ty: attributes[attributeName("ty")],
|
|
125
|
+
v: attributes[attributeName("v")],
|
|
126
|
+
w: attributes[attributeName("w")],
|
|
127
|
+
foreground: attributes[attributeName("foreground")]
|
|
128
|
+
}, contract, defaults);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function objectValue(
|
|
132
|
+
block: FlowpressBlock,
|
|
133
|
+
key: string
|
|
134
|
+
): Record<string, unknown> {
|
|
135
|
+
const value = block.attributes[key];
|
|
136
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
137
|
+
? value as Record<string, unknown>
|
|
138
|
+
: {};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function textValue(block: FlowpressBlock, key = "text"): string {
|
|
142
|
+
return String(block.attributes[key] ?? "");
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Keep intentional blank lines visible across current and legacy RichText
|
|
147
|
+
* serializations. Gutenberg can represent them either as repeated `<br>` tags,
|
|
148
|
+
* empty paragraph elements, or literal newlines in an attribute value.
|
|
149
|
+
*/
|
|
150
|
+
export function preserveRichTextSpacing(html: string): string {
|
|
151
|
+
const visibleEmptyBlocks = html
|
|
152
|
+
.replace(/\r\n?/g, "\n")
|
|
153
|
+
.replace(
|
|
154
|
+
/<(p|div)(\s[^>]*)?>(?:\s| | )*<\/\1>/gi,
|
|
155
|
+
(_match, tag: string, attributes = "") =>
|
|
156
|
+
`<${tag}${attributes}> </${tag}>`
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
return visibleEmptyBlocks
|
|
160
|
+
.split(/(<[^>]+>)/g)
|
|
161
|
+
.map((part) => {
|
|
162
|
+
if (part.startsWith("<") || part.trim() === "") {
|
|
163
|
+
return part;
|
|
164
|
+
}
|
|
165
|
+
return part.replace(/\n(?:[ \t]*\n)+/g, (newlines) =>
|
|
166
|
+
"<br>".repeat((newlines.match(/\n/g) || []).length)
|
|
167
|
+
);
|
|
168
|
+
})
|
|
169
|
+
.join("");
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** Keep an empty text element at its typographic line height without visible text. */
|
|
173
|
+
export function preserveEmptyTextBlock(html: string): string {
|
|
174
|
+
return html.trim() === "" ? " " : html;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export function linkValue(block: FlowpressBlock, key = "link"): string {
|
|
178
|
+
const value = block.attributes[key];
|
|
179
|
+
const href = typeof value === "string"
|
|
180
|
+
? value
|
|
181
|
+
: String(objectValue(block, key).url ?? "");
|
|
182
|
+
return /^(?:\/(?!\/)|#|https?:\/\/|mailto:|tel:)/i.test(href) ? href : "";
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export function responsiveValues(
|
|
186
|
+
attributes: Record<string, unknown>,
|
|
187
|
+
prefix: "columns" | "span",
|
|
188
|
+
fallback: number
|
|
189
|
+
): Record<"base" | "sm" | "md" | "lg" | "xl" | "2xl", number> {
|
|
190
|
+
const keys = ["base", "sm", "md", "lg", "xl", "2xl"] as const;
|
|
191
|
+
const result = {} as Record<(typeof keys)[number], number>;
|
|
192
|
+
let inherited = fallback;
|
|
193
|
+
for (const key of keys) {
|
|
194
|
+
const suffix = key === "base"
|
|
195
|
+
? "Base"
|
|
196
|
+
: key === "2xl" ? "2xl" : key.charAt(0).toUpperCase() + key.slice(1);
|
|
197
|
+
const candidate = Number(attributes[`${prefix}${suffix}`]);
|
|
198
|
+
if (Number.isInteger(candidate) && candidate >= 1 && candidate <= 12) {
|
|
199
|
+
inherited = candidate;
|
|
200
|
+
}
|
|
201
|
+
result[key] = inherited;
|
|
202
|
+
}
|
|
203
|
+
return result;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export function unwrapCoreHtml(html: string): string {
|
|
207
|
+
const match = html.trim().match(
|
|
208
|
+
/^<(p|h[1-6]|ul|ol|li|blockquote)(?:\s[^>]*)?>([\s\S]*)<\/\1>$/
|
|
209
|
+
);
|
|
210
|
+
return match?.[2] ?? html;
|
|
211
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { AstroComponentFactory } from "astro/runtime/server/index.js";
|
|
2
|
+
import type { StyleflowContract } from "./styleflow.js";
|
|
3
|
+
import type { FlowpressBlock } from "./types.js";
|
|
4
|
+
export interface FlowpressBlockRendererProps {
|
|
5
|
+
block: FlowpressBlock;
|
|
6
|
+
contract?: StyleflowContract;
|
|
7
|
+
}
|
|
8
|
+
export type FlowpressBlockRenderer = AstroComponentFactory;
|
|
9
|
+
export type FlowpressBlockRenderers = Readonly<Record<string, FlowpressBlockRenderer>>;
|
|
10
|
+
//# sourceMappingURL=block-renderers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"block-renderers.d.ts","sourceRoot":"","sources":["../src/block-renderers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AAC3E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,MAAM,WAAW,2BAA2B;IAC1C,KAAK,EAAE,cAAc,CAAC;IACtB,QAAQ,CAAC,EAAE,iBAAiB,CAAC;CAC9B;AAED,MAAM,MAAM,sBAAsB,GAAG,qBAAqB,CAAC;AAE3D,MAAM,MAAM,uBAAuB,GAAG,QAAQ,CAC5C,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAAC,CACvC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"block-renderers.js","sourceRoot":"","sources":["../src/block-renderers.ts"],"names":[],"mappings":"","sourcesContent":["import type { AstroComponentFactory } from \"astro/runtime/server/index.js\";\nimport type { StyleflowContract } from \"./styleflow.js\";\nimport type { FlowpressBlock } from \"./types.js\";\n\nexport interface FlowpressBlockRendererProps {\n block: FlowpressBlock;\n contract?: StyleflowContract;\n}\n\nexport type FlowpressBlockRenderer = AstroComponentFactory;\n\nexport type FlowpressBlockRenderers = Readonly<\n Record<string, FlowpressBlockRenderer>\n>;\n"]}
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,qBAAqB,EACrB,gBAAgB,EAChB,cAAc,EACf,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,qBAAqB;IACpC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE;QACX,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACxB,CAAC;CACH;AAOD,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,qBAAa,qBAAsB,SAAQ,KAAK;IAG5C,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,qBAAqB,EAAE;IACzC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM;IAC/B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM;gBAJ1B,OAAO,EAAE,MAAM,EACN,MAAM,CAAC,EAAE,MAAM,YAAA,EACf,MAAM,CAAC,EAAE,qBAAqB,EAAE,YAAA,EAChC,aAAa,CAAC,EAAE,MAAM,YAAA,EACtB,QAAQ,CAAC,EAAE,MAAM,YAAA,EAC1B,KAAK,CAAC,EAAE,OAAO;CAKlB;AAED,qBAAa,eAAe;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;gBAEZ,OAAO,EAAE,sBAAsB;IAarC,OAAO,CAAC,CAAC,EACb,KAAK,EAAE,MAAM,EACb,SAAS,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACtC,OAAO,CAAC,CAAC,CAAC;IAmFP,eAAe,IAAI,OAAO,CAAC,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,qBAAqB,EACrB,gBAAgB,EAChB,cAAc,EACf,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,qBAAqB;IACpC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE;QACX,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACxB,CAAC;CACH;AAOD,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,qBAAa,qBAAsB,SAAQ,KAAK;IAG5C,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,qBAAqB,EAAE;IACzC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM;IAC/B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM;gBAJ1B,OAAO,EAAE,MAAM,EACN,MAAM,CAAC,EAAE,MAAM,YAAA,EACf,MAAM,CAAC,EAAE,qBAAqB,EAAE,YAAA,EAChC,aAAa,CAAC,EAAE,MAAM,YAAA,EACtB,QAAQ,CAAC,EAAE,MAAM,YAAA,EAC1B,KAAK,CAAC,EAAE,OAAO;CAKlB;AAED,qBAAa,eAAe;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;gBAEZ,OAAO,EAAE,sBAAsB;IAarC,OAAO,CAAC,CAAC,EACb,KAAK,EAAE,MAAM,EACb,SAAS,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACtC,OAAO,CAAC,CAAC,CAAC;IAmFP,eAAe,IAAI,OAAO,CAAC,qBAAqB,CAAC;IA4CjD,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAgCtE,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IA6BrD,UAAU,CACd,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACjC,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC;CA2CpC"}
|
package/dist/client.js
CHANGED
|
@@ -100,9 +100,34 @@ export class FlowpressClient {
|
|
|
100
100
|
contactForm7
|
|
101
101
|
defaultLocale
|
|
102
102
|
locales { code slug homeUrl }
|
|
103
|
+
features { id version enabled capabilities configJson }
|
|
103
104
|
}
|
|
104
105
|
}`);
|
|
105
|
-
return
|
|
106
|
+
return {
|
|
107
|
+
...data.styleflowCapabilities,
|
|
108
|
+
features: (data.styleflowCapabilities.features ?? []).map((feature) => {
|
|
109
|
+
const wire = feature;
|
|
110
|
+
let config = {};
|
|
111
|
+
if (wire.configJson) {
|
|
112
|
+
try {
|
|
113
|
+
const parsed = JSON.parse(wire.configJson);
|
|
114
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
115
|
+
config = parsed;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
config = {};
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
id: feature.id,
|
|
124
|
+
version: feature.version ?? null,
|
|
125
|
+
enabled: feature.enabled,
|
|
126
|
+
capabilities: feature.capabilities ?? [],
|
|
127
|
+
config
|
|
128
|
+
};
|
|
129
|
+
})
|
|
130
|
+
};
|
|
106
131
|
}
|
|
107
132
|
async getRoute(uri, locale) {
|
|
108
133
|
const data = await this.request(`query FlowpressRoute($uri: String!, $locale: String) {
|