@styleflow.app/flowpress-astro-core 0.2.0-beta.0
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 +16 -0
- package/README.md +47 -0
- package/dist/astro/BaseLayout.astro +49 -0
- package/dist/astro/DefaultTemplate.astro +34 -0
- package/dist/astro/GutenbergRenderer.astro +324 -0
- package/dist/client.d.ts +35 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +233 -0
- package/dist/client.js.map +1 -0
- package/dist/env.d.ts +13 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +39 -0
- package/dist/env.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/styleflow.d.ts +30 -0
- package/dist/styleflow.d.ts.map +1 -0
- package/dist/styleflow.js +47 -0
- package/dist/styleflow.js.map +1 -0
- package/dist/template-hierarchy.d.ts +16 -0
- package/dist/template-hierarchy.d.ts.map +1 -0
- package/dist/template-hierarchy.js +169 -0
- package/dist/template-hierarchy.js.map +1 -0
- package/dist/templates.d.ts +19 -0
- package/dist/templates.d.ts.map +1 -0
- package/dist/templates.js +20 -0
- package/dist/templates.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/types.d.ts +109 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +57 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.2.0-beta.0
|
|
4
|
+
|
|
5
|
+
- Made the repository buildable outside the original monorepo.
|
|
6
|
+
- Added sanitized GraphQL network, timeout, non-JSON and debug diagnostics.
|
|
7
|
+
- Added package release checks and OIDC publishing workflow.
|
|
8
|
+
- Pinned the matching StyleFlow prerelease.
|
|
9
|
+
|
|
10
|
+
## 0.1.0
|
|
11
|
+
|
|
12
|
+
- Initial extractable core package.
|
|
13
|
+
- WordPress-compatible template hierarchy.
|
|
14
|
+
- GraphQL route/capability client.
|
|
15
|
+
- Rank Math SEO types and base document.
|
|
16
|
+
- Runtime Styleflow CMS value resolver.
|
package/README.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# @styleflow.app/flowpress-astro-core
|
|
2
|
+
|
|
3
|
+
Project-agnostic runtime for WordPress route resolution, the WordPress template
|
|
4
|
+
hierarchy, normalized Rank Math data, feature capabilities, and safe Styleflow
|
|
5
|
+
CMS values.
|
|
6
|
+
|
|
7
|
+
The package must not import from an application or project plugin.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pnpm add @styleflow.app/flowpress-astro-core@0.2.0-beta.0
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Prereleases are pinned exactly. The package expects Astro 7 or newer and the
|
|
16
|
+
matching StyleFlow `0.3.0-beta.x` line.
|
|
17
|
+
|
|
18
|
+
The client reports the GraphQL operation, sanitized endpoint, HTTP/content
|
|
19
|
+
type failures and underlying network/TLS cause. Upstream GraphQL debug details
|
|
20
|
+
remain hidden unless `debug: true` is explicitly set for local development.
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
const client = new FlowpressClient({
|
|
24
|
+
endpoint: process.env.BACKEND_GRAPHQL_URL!,
|
|
25
|
+
debug: process.env.APP_ENV === "local"
|
|
26
|
+
});
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
For local Cloudflare development, prefer an HTTP loopback endpoint dedicated
|
|
30
|
+
to server-to-server traffic. Keep the public HTTPS backend URL separate for
|
|
31
|
+
canonical URL rewriting.
|
|
32
|
+
|
|
33
|
+
## Release
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
# First bootstrap, after the StyleFlow prerelease exists on npm:
|
|
37
|
+
npm install
|
|
38
|
+
npm run release:check
|
|
39
|
+
npm run release:publish:next
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Commit the generated `package-lock.json`; use `npm ci` for every subsequent
|
|
43
|
+
release and in CI.
|
|
44
|
+
|
|
45
|
+
The first publish requires npm organization access and 2FA. Subsequent
|
|
46
|
+
publishes use `.github/workflows/publish.yml` with npm trusted publishing
|
|
47
|
+
(OIDC); no long-lived publish token belongs in this repository.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { SfSeo } from "../types.js";
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
lang?: string;
|
|
6
|
+
seo?: SfSeo | null;
|
|
7
|
+
siteName?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const {
|
|
11
|
+
lang = "it",
|
|
12
|
+
seo = null,
|
|
13
|
+
siteName = import.meta.env.PROJECT_NAME ?? "FlowPress"
|
|
14
|
+
} = Astro.props;
|
|
15
|
+
const title = seo?.title || siteName;
|
|
16
|
+
const description = seo?.description || "";
|
|
17
|
+
const robots = seo?.robots?.join(",") || "";
|
|
18
|
+
const jsonLd = seo?.jsonLd ? JSON.stringify(seo.jsonLd) : null;
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
<!doctype html>
|
|
22
|
+
<html lang={lang}>
|
|
23
|
+
<head>
|
|
24
|
+
<meta charset="UTF-8" />
|
|
25
|
+
<meta name="viewport" content="width=device-width" />
|
|
26
|
+
<title>{title}</title>
|
|
27
|
+
{description && <meta name="description" content={description} />}
|
|
28
|
+
{robots && <meta name="robots" content={robots} />}
|
|
29
|
+
{seo?.canonicalUrl && <link rel="canonical" href={seo.canonicalUrl} />}
|
|
30
|
+
{seo?.hreflang?.map((alternate) => (
|
|
31
|
+
<link rel="alternate" hreflang={alternate.locale} href={alternate.url} />
|
|
32
|
+
))}
|
|
33
|
+
{seo?.openGraph?.title && <meta property="og:title" content={seo.openGraph.title} />}
|
|
34
|
+
{seo?.openGraph?.description && <meta property="og:description" content={seo.openGraph.description} />}
|
|
35
|
+
{seo?.canonicalUrl && <meta property="og:url" content={seo.canonicalUrl} />}
|
|
36
|
+
{seo?.openGraph?.image?.url && <meta property="og:image" content={seo.openGraph.image.url} />}
|
|
37
|
+
{seo?.openGraph?.image?.alt && <meta property="og:image:alt" content={seo.openGraph.image.alt} />}
|
|
38
|
+
<meta name="twitter:card" content={seo?.twitter?.image?.url ? "summary_large_image" : "summary"} />
|
|
39
|
+
{seo?.twitter?.title && <meta name="twitter:title" content={seo.twitter.title} />}
|
|
40
|
+
{seo?.twitter?.description && <meta name="twitter:description" content={seo.twitter.description} />}
|
|
41
|
+
{seo?.twitter?.image?.url && <meta name="twitter:image" content={seo.twitter.image.url} />}
|
|
42
|
+
{seo?.twitter?.image?.alt && <meta name="twitter:image:alt" content={seo.twitter.image.alt} />}
|
|
43
|
+
{jsonLd && <script type="application/ld+json" set:html={jsonLd} />}
|
|
44
|
+
<slot name="head" />
|
|
45
|
+
</head>
|
|
46
|
+
<body>
|
|
47
|
+
<slot />
|
|
48
|
+
</body>
|
|
49
|
+
</html>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
import S from "@styleflow.app/astro/S.astro";
|
|
3
|
+
import BaseLayout from "./BaseLayout.astro";
|
|
4
|
+
import GutenbergRenderer from "./GutenbergRenderer.astro";
|
|
5
|
+
import type { FlowpressTemplateProps } from "../types.js";
|
|
6
|
+
|
|
7
|
+
const { route, content, styleflowContract } = Astro.props as FlowpressTemplateProps;
|
|
8
|
+
const title = content?.title || route.seo?.title || "Contenuto";
|
|
9
|
+
const isNotFound = route.queryType === "404" || route.status === 404;
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
<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>
|
|
34
|
+
</BaseLayout>
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
---
|
|
2
|
+
import S from "@styleflow.app/astro/S.astro";
|
|
3
|
+
import {
|
|
4
|
+
resolveDynamicStyleflowProps,
|
|
5
|
+
type StyleflowContract
|
|
6
|
+
} from "../styleflow.js";
|
|
7
|
+
import type { FlowpressBlock } from "../types.js";
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
blocks: FlowpressBlock[];
|
|
11
|
+
contract?: StyleflowContract;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const { blocks, contract } = Astro.props;
|
|
15
|
+
|
|
16
|
+
const defaults = {
|
|
17
|
+
tone: "neutral",
|
|
18
|
+
intensity: 1,
|
|
19
|
+
surface: "default",
|
|
20
|
+
layoutRole: "stack",
|
|
21
|
+
density: "md"
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
function styleflowProps(attributes: Record<string, unknown>) {
|
|
25
|
+
if (!contract) return defaults;
|
|
26
|
+
return resolveDynamicStyleflowProps(attributes, defaults, contract);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function dynamicProps(
|
|
30
|
+
attributes: Record<string, unknown>,
|
|
31
|
+
fallback: Record<string, unknown>
|
|
32
|
+
) {
|
|
33
|
+
if (!contract) return fallback;
|
|
34
|
+
return resolveDynamicStyleflowProps(attributes, fallback, contract);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function text(block: FlowpressBlock, key = "text"): string {
|
|
38
|
+
return String(block.attributes[key] ?? "");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function objectValue(
|
|
42
|
+
block: FlowpressBlock,
|
|
43
|
+
key: string
|
|
44
|
+
): Record<string, unknown> {
|
|
45
|
+
const value = block.attributes[key];
|
|
46
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
47
|
+
? value as Record<string, unknown>
|
|
48
|
+
: {};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function linkValue(block: FlowpressBlock, key = "link"): string {
|
|
52
|
+
const value = block.attributes[key];
|
|
53
|
+
const href = typeof value === "string"
|
|
54
|
+
? value
|
|
55
|
+
: String(objectValue(block, key).url ?? "");
|
|
56
|
+
if (/^(?:\/(?!\/)|#|https?:\/\/|mailto:|tel:)/i.test(href)) {
|
|
57
|
+
return href;
|
|
58
|
+
}
|
|
59
|
+
return "";
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function itemImage(item: Record<string, unknown>): Record<string, unknown> {
|
|
63
|
+
const value = item.image;
|
|
64
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
65
|
+
? value as Record<string, unknown>
|
|
66
|
+
: {};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function youtubeEmbedUrl(value: unknown): string | null {
|
|
70
|
+
try {
|
|
71
|
+
const url = new URL(String(value ?? ""));
|
|
72
|
+
const host = url.hostname.replace(/^www\./, "");
|
|
73
|
+
const id = host === "youtu.be"
|
|
74
|
+
? url.pathname.split("/").filter(Boolean)[0]
|
|
75
|
+
: ["youtube.com", "youtube-nocookie.com"].includes(host)
|
|
76
|
+
? url.searchParams.get("v") || url.pathname.match(/^\/(?:embed|shorts)\/([^/]+)/)?.[1]
|
|
77
|
+
: null;
|
|
78
|
+
return id && /^[A-Za-z0-9_-]+$/.test(id)
|
|
79
|
+
? `https://www.youtube-nocookie.com/embed/${id}`
|
|
80
|
+
: null;
|
|
81
|
+
} catch {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
{blocks.map((block) => {
|
|
88
|
+
const name = block.name.replace(/^sf\//, "");
|
|
89
|
+
const children = block.innerBlocks ?? [];
|
|
90
|
+
const semantic = styleflowProps(block.attributes);
|
|
91
|
+
|
|
92
|
+
if (name === "heading") {
|
|
93
|
+
const level = Math.min(6, Math.max(1, Number(block.attributes.level ?? 2)));
|
|
94
|
+
const headingTag = `h${level}` as "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
|
|
95
|
+
const typography = dynamicProps(
|
|
96
|
+
{
|
|
97
|
+
ty: "heading",
|
|
98
|
+
v: String(block.attributes.variant ?? level),
|
|
99
|
+
w: String(block.attributes.weight ?? "strong")
|
|
100
|
+
},
|
|
101
|
+
{ ty: "heading", v: String(level), w: "strong" }
|
|
102
|
+
);
|
|
103
|
+
return (
|
|
104
|
+
<S
|
|
105
|
+
as={headingTag}
|
|
106
|
+
{...typography}
|
|
107
|
+
set:html={text(block)}
|
|
108
|
+
/>
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (name === "rich-text" || name === "core/paragraph") {
|
|
113
|
+
const html = String(block.attributes.content ?? block.innerHtml ?? "");
|
|
114
|
+
return <S as="div" {...semantic} set:html={html} />;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (name === "button") {
|
|
118
|
+
const href = linkValue(block, "href") || "#";
|
|
119
|
+
const interaction = dynamicProps(
|
|
120
|
+
{
|
|
121
|
+
interactivePriority: String(
|
|
122
|
+
block.attributes.interactivePriority ?? "primary"
|
|
123
|
+
)
|
|
124
|
+
},
|
|
125
|
+
{ interactivePriority: "primary" }
|
|
126
|
+
);
|
|
127
|
+
return (
|
|
128
|
+
<S
|
|
129
|
+
as="a"
|
|
130
|
+
href={href}
|
|
131
|
+
{...interaction}
|
|
132
|
+
>
|
|
133
|
+
{text(block, "label")}
|
|
134
|
+
</S>
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (name === "image") {
|
|
139
|
+
const image = block.attributes.image as {
|
|
140
|
+
url?: string;
|
|
141
|
+
sourceUrl?: string;
|
|
142
|
+
alt?: string;
|
|
143
|
+
altText?: string;
|
|
144
|
+
} | undefined;
|
|
145
|
+
const src = image?.url ?? image?.sourceUrl;
|
|
146
|
+
return src ? (
|
|
147
|
+
<S as="div" {...semantic}>
|
|
148
|
+
<img
|
|
149
|
+
src={src}
|
|
150
|
+
alt={String(block.attributes.alt ?? image?.alt ?? image?.altText ?? "")}
|
|
151
|
+
loading="lazy"
|
|
152
|
+
decoding="async"
|
|
153
|
+
/>
|
|
154
|
+
</S>
|
|
155
|
+
) : null;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (name === "gallery") {
|
|
159
|
+
const images = Array.isArray(block.attributes.images)
|
|
160
|
+
? block.attributes.images as Array<Record<string, unknown>>
|
|
161
|
+
: [];
|
|
162
|
+
return (
|
|
163
|
+
<S as="div" {...semantic}>
|
|
164
|
+
{images.map((image) => image.url ? (
|
|
165
|
+
<img
|
|
166
|
+
src={String(image.url)}
|
|
167
|
+
alt={String(image.alt ?? "")}
|
|
168
|
+
loading="lazy"
|
|
169
|
+
decoding="async"
|
|
170
|
+
/>
|
|
171
|
+
) : null)}
|
|
172
|
+
</S>
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (name === "video") {
|
|
177
|
+
const video = objectValue(block, "video");
|
|
178
|
+
const poster = objectValue(block, "poster");
|
|
179
|
+
return video.url ? (
|
|
180
|
+
<S as="div" {...semantic}>
|
|
181
|
+
<video
|
|
182
|
+
src={String(video.url)}
|
|
183
|
+
poster={poster.url ? String(poster.url) : undefined}
|
|
184
|
+
controls
|
|
185
|
+
autoplay={Boolean(block.attributes.autoplay)}
|
|
186
|
+
muted={Boolean(block.attributes.autoplay)}
|
|
187
|
+
playsinline
|
|
188
|
+
/>
|
|
189
|
+
</S>
|
|
190
|
+
) : null;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (name === "video-youtube") {
|
|
194
|
+
const embed = youtubeEmbedUrl(block.attributes.url);
|
|
195
|
+
return embed ? (
|
|
196
|
+
<S as="div" {...semantic}>
|
|
197
|
+
<iframe
|
|
198
|
+
src={embed}
|
|
199
|
+
title={String(block.attributes.title ?? "YouTube video")}
|
|
200
|
+
loading="lazy"
|
|
201
|
+
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
|
202
|
+
allowfullscreen
|
|
203
|
+
/>
|
|
204
|
+
</S>
|
|
205
|
+
) : null;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (name === "quote") {
|
|
209
|
+
return (
|
|
210
|
+
<blockquote>
|
|
211
|
+
<S as="p" set:html={text(block, "quote")} />
|
|
212
|
+
{block.attributes.citation && <cite>{String(block.attributes.citation)}</cite>}
|
|
213
|
+
</blockquote>
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (name === "chip") {
|
|
218
|
+
return <S as="span" {...semantic}>{text(block, "label")}</S>;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (name === "card") {
|
|
222
|
+
const media = objectValue(block, "image");
|
|
223
|
+
const href = linkValue(block);
|
|
224
|
+
return (
|
|
225
|
+
<S as="article" {...semantic}>
|
|
226
|
+
{media.url && (
|
|
227
|
+
<img src={String(media.url)} alt={String(media.alt ?? "")} loading="lazy" />
|
|
228
|
+
)}
|
|
229
|
+
{block.attributes.title && <S as="h3">{String(block.attributes.title)}</S>}
|
|
230
|
+
{block.attributes.text && <S as="p">{String(block.attributes.text)}</S>}
|
|
231
|
+
{href && <S as="a" href={href}>Scopri</S>}
|
|
232
|
+
</S>
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (name === "content-list" || name === "term-list") {
|
|
237
|
+
const items = Array.isArray(block.attributes.resolvedItems)
|
|
238
|
+
? block.attributes.resolvedItems as Array<Record<string, unknown>>
|
|
239
|
+
: [];
|
|
240
|
+
return (
|
|
241
|
+
<S as="ul" {...semantic}>
|
|
242
|
+
{items.map((item) => (
|
|
243
|
+
<S as="li">
|
|
244
|
+
{itemImage(item).url && (
|
|
245
|
+
<img
|
|
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>
|
|
254
|
+
) : (
|
|
255
|
+
<S as="span">{String(item.title ?? "")}</S>
|
|
256
|
+
)}
|
|
257
|
+
{item.excerpt && <S as="p">{String(item.excerpt)}</S>}
|
|
258
|
+
{item.priceHtml && <S as="div" set:html={String(item.priceHtml)} />}
|
|
259
|
+
</S>
|
|
260
|
+
))}
|
|
261
|
+
</S>
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (name === "accordion-item") {
|
|
266
|
+
return (
|
|
267
|
+
<S as="div" {...semantic}>
|
|
268
|
+
<details>
|
|
269
|
+
<summary>{String(block.attributes.title ?? "")}</summary>
|
|
270
|
+
<Astro.self blocks={children} contract={contract} />
|
|
271
|
+
</details>
|
|
272
|
+
</S>
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if (name === "map") {
|
|
277
|
+
const address = String(block.attributes.address ?? "");
|
|
278
|
+
const latitude = String(block.attributes.latitude ?? "");
|
|
279
|
+
const longitude = String(block.attributes.longitude ?? "");
|
|
280
|
+
const query = latitude && longitude ? `${latitude},${longitude}` : address;
|
|
281
|
+
const provider = String(block.attributes.provider ?? "openstreetmap");
|
|
282
|
+
const googleMapsApiKey = import.meta.env.GOOGLE_MAPS_API_KEY;
|
|
283
|
+
if (provider === "google" && query && !googleMapsApiKey) {
|
|
284
|
+
throw new Error(
|
|
285
|
+
"GOOGLE_MAPS_API_KEY is required by the selected CMS map provider."
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
if (provider === "google" && query) {
|
|
289
|
+
const source = "https://www.google.com/maps/embed/v1/place"
|
|
290
|
+
+ `?key=${encodeURIComponent(googleMapsApiKey)}`
|
|
291
|
+
+ `&q=${encodeURIComponent(query)}`;
|
|
292
|
+
return (
|
|
293
|
+
<S as="div" {...semantic}>
|
|
294
|
+
<iframe
|
|
295
|
+
src={source}
|
|
296
|
+
title={address || "Map"}
|
|
297
|
+
loading="lazy"
|
|
298
|
+
referrerpolicy="no-referrer-when-downgrade"
|
|
299
|
+
/>
|
|
300
|
+
</S>
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
return query ? (
|
|
304
|
+
<S as="div" {...semantic}>
|
|
305
|
+
<S
|
|
306
|
+
as="a"
|
|
307
|
+
href={`https://www.openstreetmap.org/search?query=${encodeURIComponent(query)}`}
|
|
308
|
+
rel="noreferrer"
|
|
309
|
+
>
|
|
310
|
+
{address || query}
|
|
311
|
+
</S>
|
|
312
|
+
</S>
|
|
313
|
+
) : null;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
const layoutTag = ["section", "hero"].includes(name) ? "section" : "div";
|
|
317
|
+
return (
|
|
318
|
+
<S as={layoutTag} {...semantic}>
|
|
319
|
+
{block.innerHtml && children.length === 0
|
|
320
|
+
? <S as="div" set:html={block.innerHtml} />
|
|
321
|
+
: <Astro.self blocks={children} contract={contract} />}
|
|
322
|
+
</S>
|
|
323
|
+
);
|
|
324
|
+
})}
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { FlowpressCapabilities, FlowpressContent, FlowpressRoute } from "./types.js";
|
|
2
|
+
export interface FlowpressGraphqlIssue {
|
|
3
|
+
message?: string;
|
|
4
|
+
extensions?: {
|
|
5
|
+
debugMessage?: string;
|
|
6
|
+
category?: string;
|
|
7
|
+
[key: string]: unknown;
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export interface FlowpressClientOptions {
|
|
11
|
+
endpoint: string;
|
|
12
|
+
timeoutMs?: number;
|
|
13
|
+
headers?: Record<string, string>;
|
|
14
|
+
debug?: boolean;
|
|
15
|
+
}
|
|
16
|
+
export declare class FlowpressGraphqlError extends Error {
|
|
17
|
+
readonly status?: number | undefined;
|
|
18
|
+
readonly errors?: FlowpressGraphqlIssue[] | undefined;
|
|
19
|
+
readonly operationName?: string | undefined;
|
|
20
|
+
readonly endpoint?: string | undefined;
|
|
21
|
+
constructor(message: string, status?: number | undefined, errors?: FlowpressGraphqlIssue[] | undefined, operationName?: string | undefined, endpoint?: string | undefined, cause?: unknown);
|
|
22
|
+
}
|
|
23
|
+
export declare class FlowpressClient {
|
|
24
|
+
readonly endpoint: string;
|
|
25
|
+
readonly timeoutMs: number;
|
|
26
|
+
readonly headers: Record<string, string>;
|
|
27
|
+
readonly debug: boolean;
|
|
28
|
+
constructor(options: FlowpressClientOptions);
|
|
29
|
+
request<T>(query: string, variables?: Record<string, unknown>): Promise<T>;
|
|
30
|
+
getCapabilities(): Promise<FlowpressCapabilities>;
|
|
31
|
+
getRoute(uri: string, locale?: string): Promise<FlowpressRoute | null>;
|
|
32
|
+
getRoutes(locale?: string): Promise<FlowpressRoute[]>;
|
|
33
|
+
getContent(loader: string, variables: Record<string, unknown>): Promise<FlowpressContent | null>;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +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;IAoBjD,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"}
|