@omniharmonic/openspace-renderer 0.0.0-stage → 0.1.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/LICENSE +26 -0
- package/README.md +10 -2
- package/dist/base-css-BgaJFrjD.d.ts +19 -0
- package/dist/base-css-CCRQ8Nzw.d.ts +19 -0
- package/dist/behaviors/accordion.js +1 -0
- package/dist/behaviors/announcement.js +1 -0
- package/dist/behaviors/beacon.js +1 -0
- package/dist/behaviors/before-after.js +1 -0
- package/dist/behaviors/carousel.js +1 -0
- package/dist/behaviors/cart.js +1 -0
- package/dist/behaviors/countdown.js +1 -0
- package/dist/behaviors/form.js +1 -0
- package/dist/behaviors/lightbox.js +1 -0
- package/dist/behaviors/marquee.js +1 -0
- package/dist/behaviors/menu.js +1 -0
- package/dist/behaviors/newsletter.js +1 -0
- package/dist/behaviors/os-behaviors.js +1 -0
- package/dist/behaviors/popup.js +1 -0
- package/dist/behaviors/reveal.js +1 -0
- package/dist/behaviors/search.js +1 -0
- package/dist/behaviors/tabs.js +1 -0
- package/dist/behaviors/video-lite.js +1 -0
- package/dist/chunk-RO5BKIKN.js +510 -0
- package/dist/chunk-RO5BKIKN.js.map +1 -0
- package/dist/chunk-VMFXBMCF.js +483 -0
- package/dist/chunk-VMFXBMCF.js.map +1 -0
- package/dist/index.d.ts +798 -0
- package/dist/index.js +2023 -0
- package/dist/index.js.map +1 -0
- package/dist/node.d.ts +42 -0
- package/dist/node.js +82 -0
- package/dist/node.js.map +1 -0
- package/dist/os.css +446 -0
- package/package.json +67 -3
package/dist/index.js
ADDED
|
@@ -0,0 +1,2023 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BASE_CSS,
|
|
3
|
+
BEHAVIORS,
|
|
4
|
+
BEHAVIOR_BUDGETS,
|
|
5
|
+
BEHAVIOR_LOADER,
|
|
6
|
+
behaviorsInHtml,
|
|
7
|
+
isBehaviorName
|
|
8
|
+
} from "./chunk-RO5BKIKN.js";
|
|
9
|
+
|
|
10
|
+
// src/block.tsx
|
|
11
|
+
import {
|
|
12
|
+
migrateBlock,
|
|
13
|
+
resolve as resolve2
|
|
14
|
+
} from "@omniharmonic/openspace-schema";
|
|
15
|
+
import { imageTreatmentClass, TOKEN_VARS as TOKEN_VARS2, themeClass } from "@omniharmonic/openspace-theme";
|
|
16
|
+
import { Component, useContext as useContext2 } from "react";
|
|
17
|
+
|
|
18
|
+
// src/context.tsx
|
|
19
|
+
import { createContext, useContext } from "react";
|
|
20
|
+
var DEFAULT_CONTEXT = {
|
|
21
|
+
doc: void 0,
|
|
22
|
+
page: void 0,
|
|
23
|
+
mode: "production",
|
|
24
|
+
basePath: "",
|
|
25
|
+
hubUrl: void 0
|
|
26
|
+
};
|
|
27
|
+
var RenderContext = createContext(DEFAULT_CONTEXT);
|
|
28
|
+
function useRenderContext() {
|
|
29
|
+
return useContext(RenderContext);
|
|
30
|
+
}
|
|
31
|
+
var ImageResolverContext = createContext({});
|
|
32
|
+
function useImageMap() {
|
|
33
|
+
return useContext(ImageResolverContext);
|
|
34
|
+
}
|
|
35
|
+
var BlockRegistryContext = createContext(void 0);
|
|
36
|
+
var EditorContext = createContext(void 0);
|
|
37
|
+
function useEditor() {
|
|
38
|
+
return useContext(EditorContext);
|
|
39
|
+
}
|
|
40
|
+
var BlockFieldsContext = createContext(void 0);
|
|
41
|
+
function fieldPathOf(fields, value, depth = 4) {
|
|
42
|
+
if (!value || typeof value !== "object") return void 0;
|
|
43
|
+
const visit = (node, path, d) => {
|
|
44
|
+
if (node === value) return path.join(".");
|
|
45
|
+
if (d === 0 || !node || typeof node !== "object") return void 0;
|
|
46
|
+
if (node.type === "doc") return void 0;
|
|
47
|
+
for (const [k, v] of Object.entries(node)) {
|
|
48
|
+
if (v && typeof v === "object") {
|
|
49
|
+
const found = visit(v, [...path, k], d - 1);
|
|
50
|
+
if (found) return found;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return void 0;
|
|
54
|
+
};
|
|
55
|
+
for (const [k, v] of Object.entries(fields)) {
|
|
56
|
+
const found = visit(v, [k], depth);
|
|
57
|
+
if (found) return found;
|
|
58
|
+
}
|
|
59
|
+
return void 0;
|
|
60
|
+
}
|
|
61
|
+
var SectionContext = createContext(void 0);
|
|
62
|
+
|
|
63
|
+
// src/cx.ts
|
|
64
|
+
function cx(...parts) {
|
|
65
|
+
return parts.filter(Boolean).join(" ");
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// src/layout-vars.ts
|
|
69
|
+
import {
|
|
70
|
+
GRID_COLUMNS,
|
|
71
|
+
resolve
|
|
72
|
+
} from "@omniharmonic/openspace-schema";
|
|
73
|
+
import { colorVar, TOKEN_VARS } from "@omniharmonic/openspace-theme";
|
|
74
|
+
import { fallbackMobileRects } from "@omniharmonic/openspace-schema";
|
|
75
|
+
var PREFIX = { d: "", t: "t", m: "m" };
|
|
76
|
+
var BPS = ["d", "t", "m"];
|
|
77
|
+
function responsiveVars(name2, value, map) {
|
|
78
|
+
const out = {};
|
|
79
|
+
if (!value) return out;
|
|
80
|
+
for (const bp of BPS) out[`--${PREFIX[bp]}${name2}`] = map(resolve(value, bp));
|
|
81
|
+
return out;
|
|
82
|
+
}
|
|
83
|
+
var spaceVar = (token) => `var(${TOKEN_VARS.space[token] ?? TOKEN_VARS.space.md})`;
|
|
84
|
+
function sectionStyleVars(style) {
|
|
85
|
+
return {
|
|
86
|
+
...responsiveVars("pad", style.padding, spaceVar),
|
|
87
|
+
...responsiveVars("minh", style.minHeight, (v) => v)
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
var fr = (cols) => cols.map((c) => `minmax(0, ${c}fr)`).join(" ");
|
|
91
|
+
function stackVars(layout) {
|
|
92
|
+
return {
|
|
93
|
+
...responsiveVars("cols", layout.columns, fr),
|
|
94
|
+
...layout.gap ? { "--gap": spaceVar(layout.gap) } : {}
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function stackColumnVars(layout, index) {
|
|
98
|
+
const out = {};
|
|
99
|
+
const t = layout.order?.t;
|
|
100
|
+
const m = layout.order?.m ?? t;
|
|
101
|
+
if (t) out["--to"] = t.includes(index) ? t.indexOf(index) : t.length + index;
|
|
102
|
+
if (m) out["--mo"] = m.includes(index) ? m.indexOf(index) : m.length + index;
|
|
103
|
+
return out;
|
|
104
|
+
}
|
|
105
|
+
function blocksByColumn(layout, blocks2) {
|
|
106
|
+
const count = layout.columns.d.length;
|
|
107
|
+
const cols = Array.from({ length: count }, () => []);
|
|
108
|
+
for (const b of blocks2) {
|
|
109
|
+
const i = Math.min(Math.max(b.column ?? 0, 0), count - 1);
|
|
110
|
+
cols[i]?.push(b);
|
|
111
|
+
}
|
|
112
|
+
return cols;
|
|
113
|
+
}
|
|
114
|
+
function gridVars(layout) {
|
|
115
|
+
return responsiveVars("rows", layout.rows, (v) => v);
|
|
116
|
+
}
|
|
117
|
+
function rectVars(rect, bp) {
|
|
118
|
+
const p = PREFIX[bp];
|
|
119
|
+
const out = {
|
|
120
|
+
[`--${p}x`]: rect.x,
|
|
121
|
+
[`--${p}y`]: rect.y,
|
|
122
|
+
[`--${p}w`]: rect.w,
|
|
123
|
+
[`--${p}h`]: rect.h
|
|
124
|
+
};
|
|
125
|
+
if (rect.z !== void 0) out[`--${p}z`] = rect.z;
|
|
126
|
+
if (rect.bleed === "left" || rect.bleed === "both") out[`--${p}cs`] = "full-start";
|
|
127
|
+
if (rect.bleed === "right" || rect.bleed === "both") out[`--${p}ce`] = "full-end";
|
|
128
|
+
if (rect.pin === "top") out[`--${p}rs`] = 1;
|
|
129
|
+
if (rect.pin === "bottom") {
|
|
130
|
+
out[`--${p}rs`] = `span ${rect.h}`;
|
|
131
|
+
out[`--${p}re`] = -1;
|
|
132
|
+
}
|
|
133
|
+
return out;
|
|
134
|
+
}
|
|
135
|
+
function gridItemVars(layout, id, mobileFallback) {
|
|
136
|
+
const item = layout.items[id];
|
|
137
|
+
if (!item) return {};
|
|
138
|
+
const m = item.m ?? mobileFallback?.[id] ?? { ...item.d, x: 1, w: GRID_COLUMNS.m };
|
|
139
|
+
return {
|
|
140
|
+
...rectVars(item.d, "d"),
|
|
141
|
+
...rectVars(item.t ?? item.d, "t"),
|
|
142
|
+
...rectVars(clampRect(m, GRID_COLUMNS.m), "m")
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
function clampRect(rect, cols) {
|
|
146
|
+
if (rect.x + rect.w - 1 <= cols) return rect;
|
|
147
|
+
const x = Math.min(rect.x, cols);
|
|
148
|
+
return { ...rect, x, w: Math.max(1, cols - x + 1) };
|
|
149
|
+
}
|
|
150
|
+
function mobileRows(layout, fallback) {
|
|
151
|
+
let rows = resolve(layout.rows, "m");
|
|
152
|
+
for (const r of Object.values(fallback)) rows = Math.max(rows, r.y + r.h - 1);
|
|
153
|
+
return rows;
|
|
154
|
+
}
|
|
155
|
+
function colorValue(value) {
|
|
156
|
+
return /^[a-z]/.test(value) && !/^(?:oklch|oklab|rgb|rgba|hsl|hsla|lab|lch)\(/.test(value) ? `var(${colorVar(value)})` : value;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// src/registry.ts
|
|
160
|
+
var blocks = /* @__PURE__ */ new Map();
|
|
161
|
+
function registerBlocks(defs) {
|
|
162
|
+
for (const def of defs) blocks.set(def.type, def);
|
|
163
|
+
}
|
|
164
|
+
function getBlock(type) {
|
|
165
|
+
return blocks.get(type);
|
|
166
|
+
}
|
|
167
|
+
function listBlocks() {
|
|
168
|
+
return [...blocks.values()];
|
|
169
|
+
}
|
|
170
|
+
function unregisterBlocks(...types) {
|
|
171
|
+
if (types.length === 0) blocks.clear();
|
|
172
|
+
for (const t of types) blocks.delete(t);
|
|
173
|
+
}
|
|
174
|
+
var globalRegistry = { get: (type) => blocks.get(type) };
|
|
175
|
+
|
|
176
|
+
// src/block.tsx
|
|
177
|
+
import { Fragment, jsx } from "react/jsx-runtime";
|
|
178
|
+
var blockTypeClass = (type) => `os-block--${type.replace(/\//g, "-")}`;
|
|
179
|
+
var spaceVar2 = (t) => `var(${TOKEN_VARS2.space[t] ?? TOKEN_VARS2.space.md})`;
|
|
180
|
+
function alignClasses(align) {
|
|
181
|
+
return `os-align-${resolve2(align, "d")} os-talign-${resolve2(align, "t")} os-malign-${resolve2(align, "m")}`;
|
|
182
|
+
}
|
|
183
|
+
function blockStyleProps(style, mode) {
|
|
184
|
+
const vars = {};
|
|
185
|
+
const attrs = {};
|
|
186
|
+
if (!style) return { className: "", vars, attrs };
|
|
187
|
+
Object.assign(vars, responsiveVars("bpad", style.padding, spaceVar2));
|
|
188
|
+
if (style.radius) vars["--radius"] = `var(${TOKEN_VARS2.radius[style.radius]})`;
|
|
189
|
+
if (style.shadow) vars["--shadow"] = `var(${TOKEN_VARS2.shadow[style.shadow]})`;
|
|
190
|
+
const entrance = style.animation?.entrance;
|
|
191
|
+
if (entrance && entrance !== "none" && mode !== "editor") {
|
|
192
|
+
attrs["data-os-behavior"] = "reveal";
|
|
193
|
+
attrs["data-os-reveal"] = entrance;
|
|
194
|
+
}
|
|
195
|
+
const className = cx(
|
|
196
|
+
style.colorTheme && `${themeClass(style.colorTheme)} os-themed`,
|
|
197
|
+
style.imageTreatment && imageTreatmentClass(style.imageTreatment),
|
|
198
|
+
style.padding && "has-pad",
|
|
199
|
+
style.align && alignClasses(style.align),
|
|
200
|
+
style.radius && "has-radius",
|
|
201
|
+
style.shadow && style.shadow !== "none" && "has-shadow",
|
|
202
|
+
style.border && style.border !== "none" && `os-border-${style.border}`
|
|
203
|
+
);
|
|
204
|
+
return { className, vars, attrs };
|
|
205
|
+
}
|
|
206
|
+
function MissingBlock({ block, reason }) {
|
|
207
|
+
const editor = useEditor();
|
|
208
|
+
const { mode } = useRenderContext();
|
|
209
|
+
if (mode === "production") return null;
|
|
210
|
+
if (editor?.renderMissingBlock) return /* @__PURE__ */ jsx(Fragment, { children: editor.renderMissingBlock({ block, reason }) });
|
|
211
|
+
return /* @__PURE__ */ jsx("div", { className: "os-block-missing", role: "note", children: reason });
|
|
212
|
+
}
|
|
213
|
+
function prepare(def, raw, mode) {
|
|
214
|
+
if (!def) return { ok: false, reason: `Unknown block type \u201C${raw.type}\u201D` };
|
|
215
|
+
let block = raw;
|
|
216
|
+
try {
|
|
217
|
+
if (raw.v < def.version) block = migrateBlock(def, raw);
|
|
218
|
+
} catch (err) {
|
|
219
|
+
return { ok: false, reason: `Could not upgrade this ${def.label} block: ${String(err)}` };
|
|
220
|
+
}
|
|
221
|
+
const parsed = def.fieldsSchema.safeParse(block.fields);
|
|
222
|
+
if (parsed.success)
|
|
223
|
+
return { ok: true, def, block, fields: parsed.data };
|
|
224
|
+
if (mode !== "production")
|
|
225
|
+
return {
|
|
226
|
+
ok: false,
|
|
227
|
+
reason: `This ${def.label} block has invalid content: ${parsed.error.issues[0]?.message ?? "unknown error"}`
|
|
228
|
+
};
|
|
229
|
+
const repaired = repairFields(def, block.fields, parsed.error.issues);
|
|
230
|
+
const again = def.fieldsSchema.safeParse(repaired);
|
|
231
|
+
if (again.success) return { ok: true, def, block, fields: again.data };
|
|
232
|
+
return { ok: false, reason: `This ${def.label} block has invalid content` };
|
|
233
|
+
}
|
|
234
|
+
function repairFields(def, fields, issues) {
|
|
235
|
+
const out = { ...fields };
|
|
236
|
+
const defaults = def.meta.defaults.fields;
|
|
237
|
+
for (const issue of issues) {
|
|
238
|
+
const key = issue.path[0];
|
|
239
|
+
if (typeof key !== "string") continue;
|
|
240
|
+
if (defaults[key] !== void 0) out[key] = structuredClone(defaults[key]);
|
|
241
|
+
else delete out[key];
|
|
242
|
+
}
|
|
243
|
+
return out;
|
|
244
|
+
}
|
|
245
|
+
function RenderBlock(props) {
|
|
246
|
+
const { def, block, fields } = props;
|
|
247
|
+
try {
|
|
248
|
+
return /* @__PURE__ */ jsx(BlockFieldsContext.Provider, { value: { block, fields }, children: def.render({
|
|
249
|
+
id: block.id,
|
|
250
|
+
fields,
|
|
251
|
+
variant: block.variant ?? def.defaults.variant,
|
|
252
|
+
style: block.style,
|
|
253
|
+
block
|
|
254
|
+
}) });
|
|
255
|
+
} catch (err) {
|
|
256
|
+
return /* @__PURE__ */ jsx(MissingBlock, { block, reason: `This block failed to render: ${String(err)}` });
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
var BlockErrorBoundary = class extends Component {
|
|
260
|
+
state = { error: null };
|
|
261
|
+
static getDerivedStateFromError(err) {
|
|
262
|
+
return { error: String(err) };
|
|
263
|
+
}
|
|
264
|
+
render() {
|
|
265
|
+
if (this.state.error)
|
|
266
|
+
return /* @__PURE__ */ jsx(
|
|
267
|
+
MissingBlock,
|
|
268
|
+
{
|
|
269
|
+
block: this.props.block,
|
|
270
|
+
reason: `This block failed to render: ${this.state.error}`
|
|
271
|
+
}
|
|
272
|
+
);
|
|
273
|
+
return this.props.children;
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
function BlockView({ block, section }) {
|
|
277
|
+
const registry = useContext2(BlockRegistryContext) ?? globalRegistry;
|
|
278
|
+
const ctx = useRenderContext();
|
|
279
|
+
const editor = useEditor();
|
|
280
|
+
const def = registry.get(block.type);
|
|
281
|
+
if (block.hidden && ctx.mode !== "editor") return null;
|
|
282
|
+
const prepared = prepare(def, block, ctx.mode);
|
|
283
|
+
const content = prepared.ok ? /* @__PURE__ */ jsx(BlockErrorBoundary, { block, children: /* @__PURE__ */ jsx(RenderBlock, { def: prepared.def, block: prepared.block, fields: prepared.fields }) }) : /* @__PURE__ */ jsx(MissingBlock, { block, reason: prepared.reason });
|
|
284
|
+
if (!prepared.ok && ctx.mode === "production" && !editor) return null;
|
|
285
|
+
const variant = prepared.ok ? prepared.block.variant ?? prepared.def.defaults.variant : void 0;
|
|
286
|
+
const s = blockStyleProps(block.style, ctx.mode);
|
|
287
|
+
const el = /* @__PURE__ */ jsx(
|
|
288
|
+
"div",
|
|
289
|
+
{
|
|
290
|
+
className: cx(
|
|
291
|
+
"os-block",
|
|
292
|
+
blockTypeClass(block.type),
|
|
293
|
+
variant && `is-${variant}`,
|
|
294
|
+
s.className
|
|
295
|
+
),
|
|
296
|
+
"data-os-id": block.id,
|
|
297
|
+
"data-os-block": block.type,
|
|
298
|
+
"data-os-hidden": block.hidden ? "" : void 0,
|
|
299
|
+
style: Object.keys(s.vars).length ? s.vars : void 0,
|
|
300
|
+
...s.attrs,
|
|
301
|
+
children: content
|
|
302
|
+
}
|
|
303
|
+
);
|
|
304
|
+
return editor?.wrapBlock ? editor.wrapBlock({ block, section, children: el }) : el;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// src/chrome.tsx
|
|
308
|
+
import { themeClass as themeClass3 } from "@omniharmonic/openspace-theme";
|
|
309
|
+
import { useContext as useContext5 } from "react";
|
|
310
|
+
|
|
311
|
+
// src/image.tsx
|
|
312
|
+
import { imageTreatmentClass as imageTreatmentClass2 } from "@omniharmonic/openspace-theme";
|
|
313
|
+
import { createContext as createContext2, useContext as useContext3 } from "react";
|
|
314
|
+
|
|
315
|
+
// src/link.tsx
|
|
316
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
317
|
+
function normalizeBasePath(basePath) {
|
|
318
|
+
if (!basePath) return "";
|
|
319
|
+
const trimmed = basePath.replace(/^\/+|\/+$/g, "");
|
|
320
|
+
return trimmed ? `/${trimmed}` : "";
|
|
321
|
+
}
|
|
322
|
+
function pagePath(page, basePath = "") {
|
|
323
|
+
const base = normalizeBasePath(basePath);
|
|
324
|
+
return page.slug ? `${base}/${page.slug}/` : `${base}/`;
|
|
325
|
+
}
|
|
326
|
+
function itemPath(collection, slug, basePath = "") {
|
|
327
|
+
return `${normalizeBasePath(basePath)}/${collection}/${slug}/`;
|
|
328
|
+
}
|
|
329
|
+
function mediaUrl(src, basePath = "") {
|
|
330
|
+
if (/^https?:\/\//.test(src) || src.startsWith("data:")) return src;
|
|
331
|
+
return `${normalizeBasePath(basePath)}/${src.replace(/^\/+/, "")}`;
|
|
332
|
+
}
|
|
333
|
+
function withBase(href, basePath = "") {
|
|
334
|
+
const base = normalizeBasePath(basePath);
|
|
335
|
+
if (!base || !href.startsWith("/") || href.startsWith("//")) return href;
|
|
336
|
+
if (href === base || href.startsWith(`${base}/`)) return href;
|
|
337
|
+
return `${base}${href}`;
|
|
338
|
+
}
|
|
339
|
+
function sectionFragment(section) {
|
|
340
|
+
return section.anchor ?? section.id;
|
|
341
|
+
}
|
|
342
|
+
function resolveLink(link, opts = {}) {
|
|
343
|
+
const base = opts.basePath ?? "";
|
|
344
|
+
const ok = (href, newTab = false, external = false) => ({
|
|
345
|
+
href,
|
|
346
|
+
newTab,
|
|
347
|
+
broken: false,
|
|
348
|
+
external
|
|
349
|
+
});
|
|
350
|
+
switch (link.type) {
|
|
351
|
+
case "page": {
|
|
352
|
+
const page = opts.doc?.pages[link.page];
|
|
353
|
+
if (opts.doc && !page) return { href: "#", newTab: false, broken: true, external: false };
|
|
354
|
+
const path = page ? pagePath(page, base) : `${normalizeBasePath(base)}/`;
|
|
355
|
+
const hash = link.anchor ? `#${link.anchor}` : "";
|
|
356
|
+
if (hash && link.page === opts.currentPageId) return ok(hash);
|
|
357
|
+
return ok(path + hash);
|
|
358
|
+
}
|
|
359
|
+
case "item": {
|
|
360
|
+
const exists = opts.doc ? Boolean(opts.doc.collections[link.collection]?.[link.slug]) : true;
|
|
361
|
+
return {
|
|
362
|
+
href: itemPath(link.collection, link.slug, base),
|
|
363
|
+
newTab: false,
|
|
364
|
+
broken: !exists,
|
|
365
|
+
external: false
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
case "url": {
|
|
369
|
+
const external = /^https?:\/\//.test(link.url);
|
|
370
|
+
return ok(external ? link.url : withBase(link.url, base), link.newTab ?? false, external);
|
|
371
|
+
}
|
|
372
|
+
case "email": {
|
|
373
|
+
const subject = link.subject ? `?subject=${encodeURIComponent(link.subject)}` : "";
|
|
374
|
+
return ok(`mailto:${link.email}${subject}`);
|
|
375
|
+
}
|
|
376
|
+
case "phone":
|
|
377
|
+
return ok(`tel:${link.phone.replace(/[^+0-9]/g, "")}`);
|
|
378
|
+
case "file":
|
|
379
|
+
return ok(mediaUrl(link.src, base), link.newTab ?? false, /^https?:\/\//.test(link.src));
|
|
380
|
+
case "anchor": {
|
|
381
|
+
if (!opts.doc) return ok(`#${link.section}`);
|
|
382
|
+
for (const page of Object.values(opts.doc.pages)) {
|
|
383
|
+
const section = page.sections.find((s) => s.id === link.section);
|
|
384
|
+
if (!section) continue;
|
|
385
|
+
const hash = `#${sectionFragment(section)}`;
|
|
386
|
+
return ok(page.id === opts.currentPageId ? hash : pagePath(page, base) + hash);
|
|
387
|
+
}
|
|
388
|
+
return { href: "#", newTab: false, broken: true, external: false };
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
function Link({ to, children, ...rest }) {
|
|
393
|
+
const ctx = useRenderContext();
|
|
394
|
+
const r = resolveLink(to, { doc: ctx.doc, basePath: ctx.basePath, currentPageId: ctx.page?.id });
|
|
395
|
+
const extra = {};
|
|
396
|
+
if (r.newTab) {
|
|
397
|
+
extra.target = "_blank";
|
|
398
|
+
extra.rel = "noopener";
|
|
399
|
+
}
|
|
400
|
+
if (r.broken && ctx.mode !== "production") extra["data-os-broken-link"] = "";
|
|
401
|
+
return /* @__PURE__ */ jsx2("a", { href: r.href, ...extra, ...rest, children });
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// src/image.tsx
|
|
405
|
+
import { jsx as jsx3, jsxs } from "react/jsx-runtime";
|
|
406
|
+
var REMOTE_WIDTHS = [480, 640, 768, 960, 1080, 1440, 1920, 2400];
|
|
407
|
+
function remoteSrcset(src, intrinsicWidth) {
|
|
408
|
+
let url;
|
|
409
|
+
try {
|
|
410
|
+
url = new URL(src);
|
|
411
|
+
} catch {
|
|
412
|
+
return void 0;
|
|
413
|
+
}
|
|
414
|
+
if (url.hostname !== "images.unsplash.com" && !url.hostname.endsWith(".imgix.net"))
|
|
415
|
+
return void 0;
|
|
416
|
+
const widths = REMOTE_WIDTHS.filter((w) => !intrinsicWidth || w <= intrinsicWidth);
|
|
417
|
+
if (widths.length === 0) return void 0;
|
|
418
|
+
return widths.map((w) => {
|
|
419
|
+
const u = new URL(url.toString());
|
|
420
|
+
u.searchParams.set("w", String(w));
|
|
421
|
+
if (!u.searchParams.has("auto")) u.searchParams.set("auto", "format");
|
|
422
|
+
if (!u.searchParams.has("fit")) u.searchParams.set("fit", "max");
|
|
423
|
+
return `${u.toString()} ${w}w`;
|
|
424
|
+
}).join(", ");
|
|
425
|
+
}
|
|
426
|
+
function resolveImage(src, opts = {}) {
|
|
427
|
+
const hit = opts.imageMap?.[src];
|
|
428
|
+
if (hit) {
|
|
429
|
+
return {
|
|
430
|
+
src: hit.src,
|
|
431
|
+
srcset: hit.srcset,
|
|
432
|
+
sources: hit.sources ?? [],
|
|
433
|
+
width: hit.width,
|
|
434
|
+
height: hit.height,
|
|
435
|
+
placeholder: hit.placeholder
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
const meta = opts.doc?.media[src];
|
|
439
|
+
const width = opts.width ?? meta?.width;
|
|
440
|
+
const height = opts.height ?? meta?.height;
|
|
441
|
+
return {
|
|
442
|
+
src: mediaUrl(src, opts.basePath),
|
|
443
|
+
srcset: remoteSrcset(src, width),
|
|
444
|
+
sources: [],
|
|
445
|
+
width,
|
|
446
|
+
height,
|
|
447
|
+
placeholder: meta?.placeholder
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
function ratio(aspect) {
|
|
451
|
+
if (!aspect || aspect === "original") return void 0;
|
|
452
|
+
const [w, h] = aspect.split(":");
|
|
453
|
+
return `${w} / ${h}`;
|
|
454
|
+
}
|
|
455
|
+
var pct = (n) => `${Math.round(n * 1e3) / 10}%`;
|
|
456
|
+
var AboveFoldContext = createContext2(null);
|
|
457
|
+
function Img(props) {
|
|
458
|
+
const ctx = useRenderContext();
|
|
459
|
+
const imageMap = useImageMap();
|
|
460
|
+
const value = typeof props.src === "string" ? void 0 : props.src;
|
|
461
|
+
const src = typeof props.src === "string" ? props.src : props.src.src;
|
|
462
|
+
const alt = props.alt ?? value?.alt ?? "";
|
|
463
|
+
const focal = props.focal ?? value?.focal ?? ctx.doc?.media[src]?.focal;
|
|
464
|
+
const r = resolveImage(src, {
|
|
465
|
+
imageMap,
|
|
466
|
+
doc: ctx.doc,
|
|
467
|
+
basePath: ctx.basePath,
|
|
468
|
+
width: props.width ?? value?.width,
|
|
469
|
+
height: props.height ?? value?.height
|
|
470
|
+
});
|
|
471
|
+
const sizes = props.sizes ?? "100vw";
|
|
472
|
+
const aspect = props.fill ? void 0 : ratio(props.aspect);
|
|
473
|
+
const style = {};
|
|
474
|
+
if (aspect) style["--aspect"] = aspect;
|
|
475
|
+
if (focal) style["--focal"] = `${pct(focal[0])} ${pct(focal[1])}`;
|
|
476
|
+
const imgStyle = r.placeholder ? { backgroundImage: `url("${r.placeholder}")` } : void 0;
|
|
477
|
+
const fold = useContext3(AboveFoldContext);
|
|
478
|
+
const high = props.priority ?? fold?.has(src) ?? false;
|
|
479
|
+
const eager = high;
|
|
480
|
+
return /* @__PURE__ */ jsx3(
|
|
481
|
+
"span",
|
|
482
|
+
{
|
|
483
|
+
className: cx(
|
|
484
|
+
"os-img",
|
|
485
|
+
props.treatment && imageTreatmentClass2(props.treatment),
|
|
486
|
+
aspect && "has-aspect",
|
|
487
|
+
props.fit === "contain" && "is-contain",
|
|
488
|
+
props.fill && "is-fill",
|
|
489
|
+
!props.fill && !aspect && (r.width === void 0 || r.height === void 0) && "is-unsized",
|
|
490
|
+
props.className
|
|
491
|
+
),
|
|
492
|
+
style: Object.keys(style).length ? style : void 0,
|
|
493
|
+
children: /* @__PURE__ */ jsxs("picture", { children: [
|
|
494
|
+
r.sources.map((s) => /* @__PURE__ */ jsx3("source", { type: s.type, srcSet: s.srcset, sizes }, s.type)),
|
|
495
|
+
/* @__PURE__ */ jsx3(
|
|
496
|
+
"img",
|
|
497
|
+
{
|
|
498
|
+
className: cx("os-img__img", r.placeholder && "has-placeholder"),
|
|
499
|
+
src: r.src,
|
|
500
|
+
srcSet: r.srcset,
|
|
501
|
+
sizes: r.srcset || r.sources.length ? sizes : void 0,
|
|
502
|
+
width: r.width,
|
|
503
|
+
height: r.height,
|
|
504
|
+
alt,
|
|
505
|
+
loading: eager ? "eager" : "lazy",
|
|
506
|
+
decoding: high ? "sync" : "async",
|
|
507
|
+
fetchPriority: high ? "high" : void 0,
|
|
508
|
+
style: imgStyle
|
|
509
|
+
}
|
|
510
|
+
)
|
|
511
|
+
] })
|
|
512
|
+
}
|
|
513
|
+
);
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// src/primitives.tsx
|
|
517
|
+
import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
518
|
+
function Button({
|
|
519
|
+
to,
|
|
520
|
+
role = "primary",
|
|
521
|
+
type = "button",
|
|
522
|
+
className,
|
|
523
|
+
children,
|
|
524
|
+
attrs
|
|
525
|
+
}) {
|
|
526
|
+
const cls = cx("os-button", `os-button--${role}`, className);
|
|
527
|
+
const label = /* @__PURE__ */ jsx4("span", { className: "os-button__label", children });
|
|
528
|
+
if (to)
|
|
529
|
+
return /* @__PURE__ */ jsx4(Link, { to, className: cls, ...attrs, children: label });
|
|
530
|
+
return /* @__PURE__ */ jsx4("button", { type, className: cls, ...attrs, children: label });
|
|
531
|
+
}
|
|
532
|
+
var ICONS = {
|
|
533
|
+
instagram: /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
534
|
+
/* @__PURE__ */ jsx4("rect", { x: "3", y: "3", width: "18", height: "18", rx: "5" }),
|
|
535
|
+
/* @__PURE__ */ jsx4("circle", { cx: "12", cy: "12", r: "4" }),
|
|
536
|
+
/* @__PURE__ */ jsx4("circle", { cx: "17.5", cy: "6.5", r: "0.6", fill: "currentColor" })
|
|
537
|
+
] }),
|
|
538
|
+
facebook: /* @__PURE__ */ jsx4("path", { d: "M14 8h3V4h-3a4 4 0 0 0-4 4v3H7v4h3v7h4v-7h3l1-4h-4V8z" }),
|
|
539
|
+
x: /* @__PURE__ */ jsx4("path", { d: "M4 4l16 16M20 4L4 20" }),
|
|
540
|
+
threads: /* @__PURE__ */ jsx4("path", { d: "M16 11c-1-4-8-4-8 1s7 5 7 1-5-3-5-1M12 21a9 9 0 1 1 8-12" }),
|
|
541
|
+
bluesky: /* @__PURE__ */ jsx4("path", { d: "M12 11C10 7 6 4 4 5s0 7 4 8c-3 1-3 4 0 5s4-3 4-5c0 2 1 6 4 5s3-4 0-5c4-1 6-7 4-8s-6 2-8 6z" }),
|
|
542
|
+
linkedin: /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
543
|
+
/* @__PURE__ */ jsx4("rect", { x: "3", y: "3", width: "18", height: "18", rx: "3" }),
|
|
544
|
+
/* @__PURE__ */ jsx4("path", { d: "M8 10v7M8 7v.01M12 17v-4a2 2 0 0 1 4 0v4M12 10v7" })
|
|
545
|
+
] }),
|
|
546
|
+
youtube: /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
547
|
+
/* @__PURE__ */ jsx4("rect", { x: "2", y: "5", width: "20", height: "14", rx: "4" }),
|
|
548
|
+
/* @__PURE__ */ jsx4("path", { d: "M10 9l5 3-5 3z", fill: "currentColor" })
|
|
549
|
+
] }),
|
|
550
|
+
tiktok: /* @__PURE__ */ jsx4("path", { d: "M14 3v11a4 4 0 1 1-4-4M14 3c0 3 2 5 5 5" }),
|
|
551
|
+
email: /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
552
|
+
/* @__PURE__ */ jsx4("rect", { x: "3", y: "5", width: "18", height: "14", rx: "2" }),
|
|
553
|
+
/* @__PURE__ */ jsx4("path", { d: "M3 7l9 6 9-6" })
|
|
554
|
+
] }),
|
|
555
|
+
github: /* @__PURE__ */ jsx4("path", { d: "M9 19c-4 1.5-4-2-6-2m12 4v-3.5c0-1 .1-1.4-.5-2 2.8-.3 5.5-1.4 5.5-6a4.6 4.6 0 0 0-1.3-3.2 4.2 4.2 0 0 0-.1-3.2s-1.1-.3-3.5 1.3a12 12 0 0 0-6.2 0C6.5 2.8 5.4 3.1 5.4 3.1a4.2 4.2 0 0 0-.1 3.2A4.6 4.6 0 0 0 4 9.5c0 4.6 2.7 5.7 5.5 6-.6.6-.6 1.2-.5 2V21" })
|
|
556
|
+
};
|
|
557
|
+
var GENERIC_ICON = /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
558
|
+
/* @__PURE__ */ jsx4("circle", { cx: "12", cy: "12", r: "9" }),
|
|
559
|
+
/* @__PURE__ */ jsx4("path", { d: "M3 12h18M12 3a14 14 0 0 1 0 18M12 3a14 14 0 0 0 0 18" })
|
|
560
|
+
] });
|
|
561
|
+
var NETWORK_LABEL = {
|
|
562
|
+
x: "X",
|
|
563
|
+
linkedin: "LinkedIn",
|
|
564
|
+
youtube: "YouTube",
|
|
565
|
+
tiktok: "TikTok",
|
|
566
|
+
github: "GitHub",
|
|
567
|
+
soundcloud: "SoundCloud",
|
|
568
|
+
tripadvisor: "Tripadvisor"
|
|
569
|
+
};
|
|
570
|
+
function networkLabel(network) {
|
|
571
|
+
return NETWORK_LABEL[network] ?? network.charAt(0).toUpperCase() + network.slice(1);
|
|
572
|
+
}
|
|
573
|
+
function SocialIcon({ network }) {
|
|
574
|
+
return /* @__PURE__ */ jsx4(
|
|
575
|
+
"svg",
|
|
576
|
+
{
|
|
577
|
+
className: "os-icon",
|
|
578
|
+
viewBox: "0 0 24 24",
|
|
579
|
+
width: "20",
|
|
580
|
+
height: "20",
|
|
581
|
+
fill: "none",
|
|
582
|
+
stroke: "currentColor",
|
|
583
|
+
strokeWidth: "1.6",
|
|
584
|
+
strokeLinecap: "round",
|
|
585
|
+
strokeLinejoin: "round",
|
|
586
|
+
"aria-hidden": "true",
|
|
587
|
+
focusable: "false",
|
|
588
|
+
children: ICONS[network] ?? GENERIC_ICON
|
|
589
|
+
}
|
|
590
|
+
);
|
|
591
|
+
}
|
|
592
|
+
function SocialLinks({
|
|
593
|
+
profiles,
|
|
594
|
+
className
|
|
595
|
+
}) {
|
|
596
|
+
if (profiles.length === 0) return null;
|
|
597
|
+
return /* @__PURE__ */ jsx4("ul", { className: cx("os-social", className), children: profiles.map((p) => /* @__PURE__ */ jsx4("li", { children: /* @__PURE__ */ jsxs2(
|
|
598
|
+
"a",
|
|
599
|
+
{
|
|
600
|
+
href: p.url,
|
|
601
|
+
className: "os-social__link",
|
|
602
|
+
rel: "me noopener",
|
|
603
|
+
target: p.network === "email" ? void 0 : "_blank",
|
|
604
|
+
children: [
|
|
605
|
+
/* @__PURE__ */ jsx4(SocialIcon, { network: p.network }),
|
|
606
|
+
/* @__PURE__ */ jsx4("span", { className: "os-visually-hidden", children: networkLabel(p.network) })
|
|
607
|
+
]
|
|
608
|
+
}
|
|
609
|
+
) }, p.url)) });
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// src/section.tsx
|
|
613
|
+
import { themeClass as themeClass2 } from "@omniharmonic/openspace-theme";
|
|
614
|
+
import { createContext as createContext3, useContext as useContext4 } from "react";
|
|
615
|
+
|
|
616
|
+
// src/image-map.ts
|
|
617
|
+
function collectImageSrcs(value, out = /* @__PURE__ */ new Set()) {
|
|
618
|
+
if (typeof value === "string") {
|
|
619
|
+
if (/^media\/.+\.(?:jpe?g|png|webp|avif|gif|svg)$/i.test(value) || isRemoteImage(value))
|
|
620
|
+
out.add(value);
|
|
621
|
+
} else if (Array.isArray(value)) {
|
|
622
|
+
for (const v of value) collectImageSrcs(v, out);
|
|
623
|
+
} else if (value && typeof value === "object") {
|
|
624
|
+
for (const v of Object.values(value)) collectImageSrcs(v, out);
|
|
625
|
+
}
|
|
626
|
+
return out;
|
|
627
|
+
}
|
|
628
|
+
function isRemoteImage(value) {
|
|
629
|
+
return /^https:\/\/images\.unsplash\.com\//.test(value) || /^https:\/\/\S+\.(?:jpe?g|png|webp|avif|gif)(?:\?\S*)?$/i.test(value);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
// src/section.tsx
|
|
633
|
+
import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
634
|
+
var FirstSectionContext = createContext3(false);
|
|
635
|
+
function overlayVars(overlay) {
|
|
636
|
+
if (!overlay) return void 0;
|
|
637
|
+
return { "--overlay": colorValue(overlay.color), "--overlay-opacity": overlay.opacity };
|
|
638
|
+
}
|
|
639
|
+
function backgroundVars(bg) {
|
|
640
|
+
if (!bg) return {};
|
|
641
|
+
if (bg.type === "color") return { "--bg": colorValue(bg.color) };
|
|
642
|
+
if (bg.type === "gradient") {
|
|
643
|
+
const stops = [bg.from, bg.via, bg.to].filter(Boolean).map((c) => colorValue(c));
|
|
644
|
+
return { "--bg": `linear-gradient(${bg.angle ?? 180}deg, ${stops.join(", ")})` };
|
|
645
|
+
}
|
|
646
|
+
return {};
|
|
647
|
+
}
|
|
648
|
+
function SectionBackgroundLayer({
|
|
649
|
+
background
|
|
650
|
+
}) {
|
|
651
|
+
const { basePath } = useRenderContext();
|
|
652
|
+
const first = useContext4(FirstSectionContext);
|
|
653
|
+
if (!background) return null;
|
|
654
|
+
if (background.type === "image") {
|
|
655
|
+
return /* @__PURE__ */ jsxs3(
|
|
656
|
+
"div",
|
|
657
|
+
{
|
|
658
|
+
className: cx("os-section__bg", background.parallax && "is-parallax"),
|
|
659
|
+
"aria-hidden": "true",
|
|
660
|
+
children: [
|
|
661
|
+
/* @__PURE__ */ jsx5(
|
|
662
|
+
Img,
|
|
663
|
+
{
|
|
664
|
+
src: background.src,
|
|
665
|
+
alt: "",
|
|
666
|
+
sizes: "100vw",
|
|
667
|
+
treatment: "none",
|
|
668
|
+
fill: true,
|
|
669
|
+
...background.focal ? { focal: background.focal } : {},
|
|
670
|
+
priority: first,
|
|
671
|
+
className: "os-section__media"
|
|
672
|
+
}
|
|
673
|
+
),
|
|
674
|
+
background.overlay && /* @__PURE__ */ jsx5("div", { className: "os-section__overlay", style: overlayVars(background.overlay) })
|
|
675
|
+
]
|
|
676
|
+
}
|
|
677
|
+
);
|
|
678
|
+
}
|
|
679
|
+
if (background.type === "video") {
|
|
680
|
+
const focal = background.focal;
|
|
681
|
+
return /* @__PURE__ */ jsxs3("div", { className: "os-section__bg", "aria-hidden": "true", children: [
|
|
682
|
+
/* @__PURE__ */ jsx5(
|
|
683
|
+
"video",
|
|
684
|
+
{
|
|
685
|
+
className: "os-section__media os-section__video",
|
|
686
|
+
"data-os-behavior": "video-lite",
|
|
687
|
+
"data-os-autoplay": "",
|
|
688
|
+
muted: true,
|
|
689
|
+
loop: true,
|
|
690
|
+
playsInline: true,
|
|
691
|
+
preload: "none",
|
|
692
|
+
poster: background.poster ? mediaUrl(background.poster, basePath) : void 0,
|
|
693
|
+
style: focal ? { objectPosition: `${focal[0] * 100}% ${focal[1] * 100}%` } : void 0,
|
|
694
|
+
children: /* @__PURE__ */ jsx5("source", { src: mediaUrl(background.src, basePath) })
|
|
695
|
+
}
|
|
696
|
+
),
|
|
697
|
+
background.overlay && /* @__PURE__ */ jsx5("div", { className: "os-section__overlay", style: overlayVars(background.overlay) })
|
|
698
|
+
] });
|
|
699
|
+
}
|
|
700
|
+
return null;
|
|
701
|
+
}
|
|
702
|
+
function StackBody({ section }) {
|
|
703
|
+
const { layout } = section;
|
|
704
|
+
const { mode } = useRenderContext();
|
|
705
|
+
const cols = blocksByColumn(
|
|
706
|
+
layout,
|
|
707
|
+
section.blocks.filter((b) => !b.hidden || mode === "editor")
|
|
708
|
+
);
|
|
709
|
+
return /* @__PURE__ */ jsx5(
|
|
710
|
+
"div",
|
|
711
|
+
{
|
|
712
|
+
className: cx(
|
|
713
|
+
"os-section__inner os-stack",
|
|
714
|
+
`os-w-${layout.width ?? "content"}`,
|
|
715
|
+
layout.align && `is-align-${layout.align}`,
|
|
716
|
+
cols.length > 1 && "is-multi"
|
|
717
|
+
),
|
|
718
|
+
style: stackVars(layout),
|
|
719
|
+
children: cols.map((blocks2, i) => {
|
|
720
|
+
const vars = stackColumnVars(layout, i);
|
|
721
|
+
return /* @__PURE__ */ jsx5(
|
|
722
|
+
"div",
|
|
723
|
+
{
|
|
724
|
+
className: "os-col",
|
|
725
|
+
"data-os-col": i,
|
|
726
|
+
style: Object.keys(vars).length ? vars : void 0,
|
|
727
|
+
children: blocks2.map((b) => /* @__PURE__ */ jsx5(BlockView, { block: b, section }, b.id))
|
|
728
|
+
},
|
|
729
|
+
i
|
|
730
|
+
);
|
|
731
|
+
})
|
|
732
|
+
}
|
|
733
|
+
);
|
|
734
|
+
}
|
|
735
|
+
function GridBody({ section }) {
|
|
736
|
+
const { layout } = section;
|
|
737
|
+
const { mode } = useRenderContext();
|
|
738
|
+
const fallback = fallbackMobileRects(layout);
|
|
739
|
+
const vars = { ...gridVars(layout), "--mrows": mobileRows(layout, fallback) };
|
|
740
|
+
const ordered = section.blocks.filter((b) => !b.hidden || mode === "editor").sort((a, b) => {
|
|
741
|
+
const ra = layout.items[a.id]?.d;
|
|
742
|
+
const rb = layout.items[b.id]?.d;
|
|
743
|
+
if (!ra || !rb) return 0;
|
|
744
|
+
return ra.y - rb.y || ra.x - rb.x;
|
|
745
|
+
});
|
|
746
|
+
return /* @__PURE__ */ jsx5(
|
|
747
|
+
"div",
|
|
748
|
+
{
|
|
749
|
+
className: cx("os-section__inner os-grid", `os-w-${layout.width ?? "content"}`),
|
|
750
|
+
style: vars,
|
|
751
|
+
children: ordered.map(
|
|
752
|
+
(b) => layout.items[b.id] ? /* @__PURE__ */ jsx5(
|
|
753
|
+
"div",
|
|
754
|
+
{
|
|
755
|
+
className: "os-grid__item",
|
|
756
|
+
"data-os-item": b.id,
|
|
757
|
+
style: gridItemVars(layout, b.id, fallback),
|
|
758
|
+
children: /* @__PURE__ */ jsx5(BlockView, { block: b, section })
|
|
759
|
+
},
|
|
760
|
+
b.id
|
|
761
|
+
) : mode !== "production" ? /* @__PURE__ */ jsx5("div", { className: "os-grid__item os-grid__item--unplaced", "data-os-unplaced": b.id, children: /* @__PURE__ */ jsx5(MissingBlock, { block: b, reason: "This block has no position in the grid" }) }, b.id) : null
|
|
762
|
+
)
|
|
763
|
+
}
|
|
764
|
+
);
|
|
765
|
+
}
|
|
766
|
+
function leadImages(section, count = 2) {
|
|
767
|
+
const layout = section.layout;
|
|
768
|
+
const order = (bp) => layout.mode === "stack" ? [...section.blocks].sort((a, b) => (a.column ?? 0) - (b.column ?? 0)) : section.blocks.map((b, i) => {
|
|
769
|
+
const item = layout.items[b.id];
|
|
770
|
+
return { b, i, r: bp === "m" ? item?.m ?? item?.d : item?.d };
|
|
771
|
+
}).sort(
|
|
772
|
+
(p, q) => p.r && q.r ? p.r.y - q.r.y || p.r.x - q.r.x || p.i - q.i : p.r ? -1 : q.r ? 1 : p.i - q.i
|
|
773
|
+
).map((p) => p.b);
|
|
774
|
+
const images = (blocks2) => {
|
|
775
|
+
const srcs = /* @__PURE__ */ new Set();
|
|
776
|
+
for (const b of blocks2) collectImageSrcs(b.fields, srcs);
|
|
777
|
+
return [...srcs];
|
|
778
|
+
};
|
|
779
|
+
const desktop = images(order("d"));
|
|
780
|
+
const phone = layout.mode === "grid" ? images(order("m")) : desktop;
|
|
781
|
+
const out = [];
|
|
782
|
+
const bg = section.style.background;
|
|
783
|
+
if (bg?.type === "image" && bg.src) out.push(bg.src);
|
|
784
|
+
for (const src of [phone[0], desktop[0], ...desktop])
|
|
785
|
+
if (src && !out.includes(src) && out.length < count) out.push(src);
|
|
786
|
+
return new Set(out);
|
|
787
|
+
}
|
|
788
|
+
function SectionView({ section, as: Tag = "section", className }) {
|
|
789
|
+
const { mode } = useRenderContext();
|
|
790
|
+
const editor = useEditor();
|
|
791
|
+
const first = useContext4(FirstSectionContext);
|
|
792
|
+
if (section.hidden && mode !== "editor") return null;
|
|
793
|
+
const { style, layout } = section;
|
|
794
|
+
const vars = { ...sectionStyleVars(style), ...backgroundVars(style.background) };
|
|
795
|
+
const bgType = style.background?.type ?? "none";
|
|
796
|
+
const entrance = style.animation?.entrance;
|
|
797
|
+
const reveal = entrance && entrance !== "none" && mode !== "editor" ? {
|
|
798
|
+
"data-os-behavior": "reveal",
|
|
799
|
+
"data-os-reveal": entrance,
|
|
800
|
+
...style.animation?.stagger ? { "data-os-stagger": "" } : {}
|
|
801
|
+
} : {};
|
|
802
|
+
const el = /* @__PURE__ */ jsx5(
|
|
803
|
+
Tag,
|
|
804
|
+
{
|
|
805
|
+
id: sectionFragment(section),
|
|
806
|
+
className: cx(
|
|
807
|
+
"os-section",
|
|
808
|
+
themeClass2(style.colorTheme ?? "light"),
|
|
809
|
+
`os-section--${layout.mode}`,
|
|
810
|
+
bgType !== "none" && `has-bg-${bgType}`,
|
|
811
|
+
style.minHeight && "has-minh",
|
|
812
|
+
style.padding && "has-pad",
|
|
813
|
+
first && "os-section--first",
|
|
814
|
+
className
|
|
815
|
+
),
|
|
816
|
+
"data-os-id": section.id,
|
|
817
|
+
"data-os-section": "",
|
|
818
|
+
"data-os-hidden": section.hidden ? "" : void 0,
|
|
819
|
+
style: Object.keys(vars).length ? vars : void 0,
|
|
820
|
+
...reveal,
|
|
821
|
+
children: /* @__PURE__ */ jsx5(SectionContext.Provider, { value: section, children: /* @__PURE__ */ jsxs3(AboveFoldContext.Provider, { value: first ? leadImages(section) : null, children: [
|
|
822
|
+
/* @__PURE__ */ jsx5(SectionBackgroundLayer, { background: style.background }),
|
|
823
|
+
layout.mode === "stack" ? /* @__PURE__ */ jsx5(StackBody, { section }) : /* @__PURE__ */ jsx5(GridBody, { section })
|
|
824
|
+
] }) })
|
|
825
|
+
}
|
|
826
|
+
);
|
|
827
|
+
return editor?.wrapSection ? editor.wrapSection({ section, children: el }) : el;
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
// src/chrome.tsx
|
|
831
|
+
import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
832
|
+
function shortHash(text) {
|
|
833
|
+
let h = 2166136261;
|
|
834
|
+
for (let i = 0; i < text.length; i++) {
|
|
835
|
+
h ^= text.charCodeAt(i);
|
|
836
|
+
h = Math.imul(h, 16777619);
|
|
837
|
+
}
|
|
838
|
+
return (h >>> 0).toString(36);
|
|
839
|
+
}
|
|
840
|
+
function CloseIcon() {
|
|
841
|
+
return /* @__PURE__ */ jsx6("svg", { viewBox: "0 0 24 24", width: "20", height: "20", "aria-hidden": "true", focusable: "false", children: /* @__PURE__ */ jsx6(
|
|
842
|
+
"path",
|
|
843
|
+
{
|
|
844
|
+
d: "M6 6l12 12M18 6L6 18",
|
|
845
|
+
fill: "none",
|
|
846
|
+
stroke: "currentColor",
|
|
847
|
+
strokeWidth: "1.6",
|
|
848
|
+
strokeLinecap: "round"
|
|
849
|
+
}
|
|
850
|
+
) });
|
|
851
|
+
}
|
|
852
|
+
function AnnouncementBar({
|
|
853
|
+
announcement
|
|
854
|
+
}) {
|
|
855
|
+
if (!announcement?.enabled) return null;
|
|
856
|
+
const dismissible = announcement.dismissible ?? true;
|
|
857
|
+
return /* @__PURE__ */ jsxs4(
|
|
858
|
+
"aside",
|
|
859
|
+
{
|
|
860
|
+
className: cx("os-announcement", themeClass3(announcement.colorTheme ?? "dark-accent")),
|
|
861
|
+
"aria-label": "Announcement",
|
|
862
|
+
"data-os-behavior": dismissible ? "announcement" : void 0,
|
|
863
|
+
"data-os-key": shortHash(announcement.message),
|
|
864
|
+
children: [
|
|
865
|
+
/* @__PURE__ */ jsx6("p", { className: "os-announcement__text", children: announcement.link ? /* @__PURE__ */ jsx6(Link, { to: announcement.link, className: "os-announcement__link", children: announcement.message }) : announcement.message }),
|
|
866
|
+
dismissible && /* @__PURE__ */ jsxs4("button", { type: "button", className: "os-announcement__close", "data-os-dismiss": "", children: [
|
|
867
|
+
/* @__PURE__ */ jsx6(CloseIcon, {}),
|
|
868
|
+
/* @__PURE__ */ jsx6("span", { className: "os-visually-hidden", children: "Dismiss announcement" })
|
|
869
|
+
] })
|
|
870
|
+
]
|
|
871
|
+
}
|
|
872
|
+
);
|
|
873
|
+
}
|
|
874
|
+
function Footer({ site, children }) {
|
|
875
|
+
return /* @__PURE__ */ jsxs4("footer", { className: "os-footer", children: [
|
|
876
|
+
/* @__PURE__ */ jsx6(SectionView, { section: site.footer, as: "div", className: "os-footer__section" }),
|
|
877
|
+
children
|
|
878
|
+
] });
|
|
879
|
+
}
|
|
880
|
+
function Popup({
|
|
881
|
+
id,
|
|
882
|
+
trigger,
|
|
883
|
+
frequencyDays = 7,
|
|
884
|
+
title,
|
|
885
|
+
colorTheme = "light",
|
|
886
|
+
children
|
|
887
|
+
}) {
|
|
888
|
+
const triggerAttrs = { "data-os-trigger": trigger.type };
|
|
889
|
+
if (trigger.type === "time") triggerAttrs["data-os-delay"] = String(trigger.seconds);
|
|
890
|
+
if (trigger.type === "scroll") triggerAttrs["data-os-scroll"] = String(trigger.percent);
|
|
891
|
+
const titleId = `${id}-title`;
|
|
892
|
+
return /* @__PURE__ */ jsxs4(
|
|
893
|
+
"dialog",
|
|
894
|
+
{
|
|
895
|
+
id,
|
|
896
|
+
className: cx("os-popup", themeClass3(colorTheme)),
|
|
897
|
+
"aria-labelledby": titleId,
|
|
898
|
+
"data-os-behavior": "popup",
|
|
899
|
+
"data-os-frequency": String(frequencyDays),
|
|
900
|
+
...triggerAttrs,
|
|
901
|
+
children: [
|
|
902
|
+
/* @__PURE__ */ jsxs4("div", { className: "os-popup__body", children: [
|
|
903
|
+
/* @__PURE__ */ jsx6("h2", { id: titleId, className: "os-popup__title os-type-h3", children: title }),
|
|
904
|
+
children
|
|
905
|
+
] }),
|
|
906
|
+
/* @__PURE__ */ jsxs4("button", { type: "button", className: "os-popup__close", "data-os-close": "", children: [
|
|
907
|
+
/* @__PURE__ */ jsx6(CloseIcon, {}),
|
|
908
|
+
/* @__PURE__ */ jsx6("span", { className: "os-visually-hidden", children: "Close" })
|
|
909
|
+
] })
|
|
910
|
+
]
|
|
911
|
+
}
|
|
912
|
+
);
|
|
913
|
+
}
|
|
914
|
+
function popupTrigger(popup) {
|
|
915
|
+
if (popup.trigger === "scroll") return { type: "scroll", percent: popup.scrollPercent ?? 50 };
|
|
916
|
+
if (popup.trigger === "exit-intent") return { type: "exit" };
|
|
917
|
+
return { type: "time", seconds: popup.delaySeconds ?? 8 };
|
|
918
|
+
}
|
|
919
|
+
var POPUP_NEWSLETTER_ID = "blk_popupnews0";
|
|
920
|
+
function SitePopup({ popup }) {
|
|
921
|
+
const registry = useContext5(BlockRegistryContext) ?? globalRegistry;
|
|
922
|
+
if (!popup?.enabled) return null;
|
|
923
|
+
const newsletter = popup.newsletter && registry.get("newsletter") ? {
|
|
924
|
+
id: POPUP_NEWSLETTER_ID,
|
|
925
|
+
type: "newsletter",
|
|
926
|
+
v: 1,
|
|
927
|
+
variant: "stacked",
|
|
928
|
+
fields: {
|
|
929
|
+
askName: false,
|
|
930
|
+
placeholder: "you@example.com",
|
|
931
|
+
buttonLabel: "Subscribe",
|
|
932
|
+
successMessage: "Almost there \u2014 check your inbox to confirm your subscription."
|
|
933
|
+
}
|
|
934
|
+
} : null;
|
|
935
|
+
return /* @__PURE__ */ jsxs4(
|
|
936
|
+
Popup,
|
|
937
|
+
{
|
|
938
|
+
id: `os-popup-${shortHash(popup.heading)}`,
|
|
939
|
+
trigger: popupTrigger(popup),
|
|
940
|
+
frequencyDays: popup.frequencyDays,
|
|
941
|
+
title: popup.heading,
|
|
942
|
+
colorTheme: popup.colorTheme ?? "light",
|
|
943
|
+
children: [
|
|
944
|
+
popup.image ? /* @__PURE__ */ jsx6(Img, { src: popup.image, className: "os-popup__image", sizes: "34rem", aspect: "16:9" }) : null,
|
|
945
|
+
popup.body ? /* @__PURE__ */ jsx6("p", { className: "os-popup__text", children: popup.body }) : null,
|
|
946
|
+
popup.cta ? /* @__PURE__ */ jsx6("div", { className: "os-popup__actions", children: /* @__PURE__ */ jsx6(Button, { to: popup.cta.link, children: popup.cta.label }) }) : null,
|
|
947
|
+
newsletter ? /* @__PURE__ */ jsx6(BlockView, { block: newsletter }) : null
|
|
948
|
+
]
|
|
949
|
+
}
|
|
950
|
+
);
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
// src/collections.tsx
|
|
954
|
+
import { createContext as createContext4, useContext as useContext6 } from "react";
|
|
955
|
+
|
|
956
|
+
// src/recurrence.ts
|
|
957
|
+
var DAYS = ["SU", "MO", "TU", "WE", "TH", "FR", "SA"];
|
|
958
|
+
var WEEK = ["MO", "TU", "WE", "TH", "FR", "SA", "SU"];
|
|
959
|
+
var DAY_MS = 864e5;
|
|
960
|
+
function parseRecurrence(rule) {
|
|
961
|
+
const map = /* @__PURE__ */ new Map();
|
|
962
|
+
for (const part of rule.replace(/^RRULE:/i, "").split(";")) {
|
|
963
|
+
const [k, v] = part.split("=");
|
|
964
|
+
if (k && v) map.set(k.toUpperCase(), v.toUpperCase());
|
|
965
|
+
}
|
|
966
|
+
const freq = map.get("FREQ");
|
|
967
|
+
if (freq !== "DAILY" && freq !== "WEEKLY" && freq !== "MONTHLY" && freq !== "YEARLY") return null;
|
|
968
|
+
const interval = Math.max(1, Number(map.get("INTERVAL") ?? 1) || 1);
|
|
969
|
+
const out = { freq, interval, byDay: [] };
|
|
970
|
+
for (const d of (map.get("BYDAY") ?? "").split(",").filter(Boolean)) {
|
|
971
|
+
const m = /^([+-]?\d)?(MO|TU|WE|TH|FR|SA|SU)$/.exec(d);
|
|
972
|
+
if (!m) continue;
|
|
973
|
+
if (m[1] && freq === "MONTHLY") out.nth = { n: Number(m[1]), day: m[2] };
|
|
974
|
+
else out.byDay.push(m[2]);
|
|
975
|
+
}
|
|
976
|
+
const bmd = Number(map.get("BYMONTHDAY"));
|
|
977
|
+
if (Number.isInteger(bmd) && bmd > 0) out.byMonthDay = bmd;
|
|
978
|
+
const count = Number(map.get("COUNT"));
|
|
979
|
+
if (Number.isInteger(count) && count > 0) out.count = count;
|
|
980
|
+
const until = map.get("UNTIL");
|
|
981
|
+
if (until && /^\d{8}/.test(until))
|
|
982
|
+
out.until = `${until.slice(0, 4)}-${until.slice(4, 6)}-${until.slice(6, 8)}`;
|
|
983
|
+
return out;
|
|
984
|
+
}
|
|
985
|
+
var utc = (y, m, d) => new Date(Date.UTC(y, m - 1, d));
|
|
986
|
+
var iso = (d) => d.toISOString().slice(0, 10);
|
|
987
|
+
function occurrenceDates(rule, start, options = {}) {
|
|
988
|
+
const r = typeof rule === "string" ? parseRecurrence(rule) : rule;
|
|
989
|
+
const m = /^(\d{4})-(\d{2})-(\d{2})/.exec(start);
|
|
990
|
+
if (!m) return [];
|
|
991
|
+
const y = Number(m[1]);
|
|
992
|
+
const mo = Number(m[2]);
|
|
993
|
+
const d = Number(m[3]);
|
|
994
|
+
const first = utc(y, mo, d);
|
|
995
|
+
const from = options.from ?? iso(first);
|
|
996
|
+
const max = options.max ?? 500;
|
|
997
|
+
if (!r) return iso(first) >= from && max > 0 ? [iso(first)] : [];
|
|
998
|
+
const last = [r.until, options.through].filter(Boolean).sort()[0];
|
|
999
|
+
const over = (date) => last ? iso(date) > last : false;
|
|
1000
|
+
const out = [];
|
|
1001
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1002
|
+
let total = 0;
|
|
1003
|
+
const take = (date) => {
|
|
1004
|
+
const s = iso(date);
|
|
1005
|
+
if (date < first || seen.has(s)) return;
|
|
1006
|
+
seen.add(s);
|
|
1007
|
+
total += 1;
|
|
1008
|
+
if (s >= from) out.push(s);
|
|
1009
|
+
};
|
|
1010
|
+
const done = () => out.length >= max || r.count !== void 0 && total >= r.count;
|
|
1011
|
+
let i0 = 0;
|
|
1012
|
+
if (r.count === void 0 && from > iso(first)) {
|
|
1013
|
+
const f = /* @__PURE__ */ new Date(`${from}T00:00:00Z`);
|
|
1014
|
+
const days = Math.floor((f.getTime() - first.getTime()) / DAY_MS);
|
|
1015
|
+
const months = (f.getUTCFullYear() - y) * 12 + (f.getUTCMonth() + 1 - mo);
|
|
1016
|
+
const periods = r.freq === "DAILY" ? days / r.interval : r.freq === "WEEKLY" ? days / (7 * r.interval) : r.freq === "MONTHLY" ? months / r.interval : (f.getUTCFullYear() - y) / r.interval;
|
|
1017
|
+
i0 = Math.max(0, Math.floor(periods) - 1);
|
|
1018
|
+
}
|
|
1019
|
+
if (i0 === 0) take(first);
|
|
1020
|
+
for (let i = i0; !done() && i < i0 + 2e4; i++) {
|
|
1021
|
+
const batch = [];
|
|
1022
|
+
if (r.freq === "DAILY") batch.push(utc(y, mo, d + i * r.interval));
|
|
1023
|
+
else if (r.freq === "WEEKLY") {
|
|
1024
|
+
const monday = utc(y, mo, d - (first.getUTCDay() + 6) % 7 + i * 7 * r.interval);
|
|
1025
|
+
const days = r.byDay.length ? r.byDay : [DAYS[first.getUTCDay()]];
|
|
1026
|
+
for (const day of WEEK)
|
|
1027
|
+
if (days.includes(day))
|
|
1028
|
+
batch.push(
|
|
1029
|
+
utc(
|
|
1030
|
+
monday.getUTCFullYear(),
|
|
1031
|
+
monday.getUTCMonth() + 1,
|
|
1032
|
+
monday.getUTCDate() + WEEK.indexOf(day)
|
|
1033
|
+
)
|
|
1034
|
+
);
|
|
1035
|
+
} else if (r.freq === "MONTHLY") {
|
|
1036
|
+
const month = utc(y, mo + i * r.interval, 1);
|
|
1037
|
+
if (r.nth) {
|
|
1038
|
+
const want = DAYS.indexOf(r.nth.day);
|
|
1039
|
+
if (r.nth.n > 0) {
|
|
1040
|
+
const offset = (want - month.getUTCDay() + 7) % 7;
|
|
1041
|
+
const date = utc(
|
|
1042
|
+
month.getUTCFullYear(),
|
|
1043
|
+
month.getUTCMonth() + 1,
|
|
1044
|
+
1 + offset + (r.nth.n - 1) * 7
|
|
1045
|
+
);
|
|
1046
|
+
if (date.getUTCMonth() === month.getUTCMonth()) batch.push(date);
|
|
1047
|
+
} else {
|
|
1048
|
+
const end = utc(month.getUTCFullYear(), month.getUTCMonth() + 2, 0);
|
|
1049
|
+
const back = (end.getUTCDay() - want + 7) % 7;
|
|
1050
|
+
batch.push(utc(end.getUTCFullYear(), end.getUTCMonth() + 1, end.getUTCDate() - back));
|
|
1051
|
+
}
|
|
1052
|
+
} else {
|
|
1053
|
+
const day = r.byMonthDay ?? d;
|
|
1054
|
+
const date = utc(month.getUTCFullYear(), month.getUTCMonth() + 1, day);
|
|
1055
|
+
if (date.getUTCDate() === day) batch.push(date);
|
|
1056
|
+
}
|
|
1057
|
+
} else {
|
|
1058
|
+
const date = utc(y + i * r.interval, mo, d);
|
|
1059
|
+
if (date.getUTCDate() === d) batch.push(date);
|
|
1060
|
+
}
|
|
1061
|
+
let stop = false;
|
|
1062
|
+
for (const date of batch) {
|
|
1063
|
+
if (done()) break;
|
|
1064
|
+
if (over(date)) {
|
|
1065
|
+
stop = true;
|
|
1066
|
+
break;
|
|
1067
|
+
}
|
|
1068
|
+
take(date);
|
|
1069
|
+
}
|
|
1070
|
+
if (stop) break;
|
|
1071
|
+
}
|
|
1072
|
+
return out;
|
|
1073
|
+
}
|
|
1074
|
+
function zoneOffsetMinutes(timeZone, at) {
|
|
1075
|
+
try {
|
|
1076
|
+
const parts = new Intl.DateTimeFormat("en-US", {
|
|
1077
|
+
timeZone,
|
|
1078
|
+
hourCycle: "h23",
|
|
1079
|
+
year: "numeric",
|
|
1080
|
+
month: "2-digit",
|
|
1081
|
+
day: "2-digit",
|
|
1082
|
+
hour: "2-digit",
|
|
1083
|
+
minute: "2-digit",
|
|
1084
|
+
second: "2-digit"
|
|
1085
|
+
}).formatToParts(new Date(at));
|
|
1086
|
+
const n = (t) => Number(parts.find((p) => p.type === t)?.value);
|
|
1087
|
+
const wall = Date.UTC(n("year"), n("month") - 1, n("day"), n("hour"), n("minute"), n("second"));
|
|
1088
|
+
return Math.round((wall - at) / 6e4);
|
|
1089
|
+
} catch {
|
|
1090
|
+
return null;
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
var fmtOffset = (min) => {
|
|
1094
|
+
const sign = min < 0 ? "-" : "+";
|
|
1095
|
+
const a = Math.abs(min);
|
|
1096
|
+
return `${sign}${String(Math.floor(a / 60)).padStart(2, "0")}:${String(a % 60).padStart(2, "0")}`;
|
|
1097
|
+
};
|
|
1098
|
+
function shiftDateTime(value, days, timeZone) {
|
|
1099
|
+
const day = new Date(Date.parse(`${value.slice(0, 10)}T00:00:00Z`) + days * DAY_MS).toISOString().slice(0, 10);
|
|
1100
|
+
const m = /^\d{4}-\d{2}-\d{2}T(\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?)(Z|[+-]\d{2}:\d{2})?$/.exec(
|
|
1101
|
+
value
|
|
1102
|
+
);
|
|
1103
|
+
if (!m) return `${day}${value.slice(10)}`;
|
|
1104
|
+
const time2 = m[1];
|
|
1105
|
+
if (!timeZone) return `${day}T${time2}${m[2] ?? ""}`;
|
|
1106
|
+
const wall = Date.parse(`${day}T${time2.length === 5 ? `${time2}:00` : time2}Z`);
|
|
1107
|
+
let off = zoneOffsetMinutes(timeZone, wall);
|
|
1108
|
+
if (off === null) return `${day}T${time2}${m[2] ?? ""}`;
|
|
1109
|
+
off = zoneOffsetMinutes(timeZone, wall - off * 6e4) ?? off;
|
|
1110
|
+
return `${day}T${time2}${fmtOffset(off)}`;
|
|
1111
|
+
}
|
|
1112
|
+
function eventOn(item, date) {
|
|
1113
|
+
const fm = item.frontmatter;
|
|
1114
|
+
const days = Math.round(
|
|
1115
|
+
(Date.parse(`${date}T00:00:00Z`) - Date.parse(`${fm.start.slice(0, 10)}T00:00:00Z`)) / DAY_MS
|
|
1116
|
+
);
|
|
1117
|
+
const move = (v) => shiftDateTime(v, days, fm.timezone);
|
|
1118
|
+
return {
|
|
1119
|
+
...item,
|
|
1120
|
+
frontmatter: { ...fm, start: move(fm.start), ...fm.end ? { end: move(fm.end) } : {} }
|
|
1121
|
+
};
|
|
1122
|
+
}
|
|
1123
|
+
function eventLengthDays(item) {
|
|
1124
|
+
if (item.kind !== "events" || !item.frontmatter.end) return 0;
|
|
1125
|
+
const fm = item.frontmatter;
|
|
1126
|
+
return Math.max(
|
|
1127
|
+
0,
|
|
1128
|
+
Math.round(
|
|
1129
|
+
(Date.parse(`${fm.end.slice(0, 10)}T00:00:00Z`) - Date.parse(`${fm.start.slice(0, 10)}T00:00:00Z`)) / DAY_MS
|
|
1130
|
+
)
|
|
1131
|
+
);
|
|
1132
|
+
}
|
|
1133
|
+
function expandOccurrences(items, window) {
|
|
1134
|
+
const out = [];
|
|
1135
|
+
for (const item of items) {
|
|
1136
|
+
if (item.kind !== "events" || !item.frontmatter.recurrence) {
|
|
1137
|
+
out.push(item);
|
|
1138
|
+
continue;
|
|
1139
|
+
}
|
|
1140
|
+
const fm = item.frontmatter;
|
|
1141
|
+
const lengthDays = eventLengthDays(item);
|
|
1142
|
+
const fromStart = iso(new Date(Date.parse(`${window.from}T00:00:00Z`) - lengthDays * DAY_MS));
|
|
1143
|
+
const per = window.perEvent ?? 12;
|
|
1144
|
+
const all = occurrenceDates(fm.recurrence, fm.start, {
|
|
1145
|
+
from: fromStart,
|
|
1146
|
+
through: window.through,
|
|
1147
|
+
max: window.newest ? 2e3 : per
|
|
1148
|
+
});
|
|
1149
|
+
const dates = window.newest ? all.slice(-per) : all;
|
|
1150
|
+
for (const date of dates) out.push(eventOn(item, date));
|
|
1151
|
+
}
|
|
1152
|
+
return out;
|
|
1153
|
+
}
|
|
1154
|
+
function itemKey(item) {
|
|
1155
|
+
return item.kind === "events" && item.frontmatter.recurrence ? `${item.slug}@${item.frontmatter.start}` : item.slug;
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
// src/collections.tsx
|
|
1159
|
+
var CollectionsContext = createContext4(void 0);
|
|
1160
|
+
function feedKey(network, handle) {
|
|
1161
|
+
return `${network}:${handle.replace(/^@/, "")}`.toLowerCase();
|
|
1162
|
+
}
|
|
1163
|
+
function useCollections() {
|
|
1164
|
+
const ctx = useRenderContext();
|
|
1165
|
+
const value = useContext6(CollectionsContext);
|
|
1166
|
+
return {
|
|
1167
|
+
collections: value?.collections ?? ctx.doc?.collections ?? {},
|
|
1168
|
+
configs: value?.configs ?? ctx.doc?.collectionConfigs ?? {},
|
|
1169
|
+
now: value?.now === void 0 ? /* @__PURE__ */ new Date() : new Date(value.now),
|
|
1170
|
+
includeDrafts: value?.includeDrafts ?? ctx.mode !== "production",
|
|
1171
|
+
feeds: value?.feeds ?? {}
|
|
1172
|
+
};
|
|
1173
|
+
}
|
|
1174
|
+
function itemDate(item) {
|
|
1175
|
+
const fm = item.frontmatter;
|
|
1176
|
+
return item.kind === "events" ? fm.start : fm.date;
|
|
1177
|
+
}
|
|
1178
|
+
function isLive(item, now) {
|
|
1179
|
+
const fm = item.frontmatter;
|
|
1180
|
+
if (fm.status === "published") return true;
|
|
1181
|
+
if (fm.status === "scheduled" && fm.publishAt) return Date.parse(fm.publishAt) <= now.getTime();
|
|
1182
|
+
return false;
|
|
1183
|
+
}
|
|
1184
|
+
function todayIn(now, timeZone) {
|
|
1185
|
+
for (const tz of [timeZone, "UTC"]) {
|
|
1186
|
+
try {
|
|
1187
|
+
return new Intl.DateTimeFormat("en-CA", {
|
|
1188
|
+
timeZone: tz ?? "UTC",
|
|
1189
|
+
year: "numeric",
|
|
1190
|
+
month: "2-digit",
|
|
1191
|
+
day: "2-digit"
|
|
1192
|
+
}).format(now);
|
|
1193
|
+
} catch {
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
return now.toISOString().slice(0, 10);
|
|
1197
|
+
}
|
|
1198
|
+
function isUpcoming(item, now) {
|
|
1199
|
+
if (item.kind !== "events") return true;
|
|
1200
|
+
const fm = item.frontmatter;
|
|
1201
|
+
const last = fm.end ?? fm.start;
|
|
1202
|
+
if (/^\d{4}-\d{2}-\d{2}$/.test(last)) return last >= todayIn(now, fm.timezone);
|
|
1203
|
+
return Date.parse(last) >= now.getTime();
|
|
1204
|
+
}
|
|
1205
|
+
var time = (item) => {
|
|
1206
|
+
const d = itemDate(item);
|
|
1207
|
+
return d ? Date.parse(d) : 0;
|
|
1208
|
+
};
|
|
1209
|
+
var isoDay = (d) => d.toISOString().slice(0, 10);
|
|
1210
|
+
var plusDays = (d, n) => isoDay(new Date(d.getTime() + n * 864e5));
|
|
1211
|
+
var RECURRENCE_HORIZON_DAYS = 366;
|
|
1212
|
+
function nextOccurrence(item, now) {
|
|
1213
|
+
if (item.kind !== "events" || !item.frontmatter.recurrence) return item;
|
|
1214
|
+
const rule = item.frontmatter.recurrence;
|
|
1215
|
+
const start = item.frontmatter.start;
|
|
1216
|
+
const soon = occurrenceDates(rule, start, {
|
|
1217
|
+
from: plusDays(now, -(eventLengthDays(item) + 1)),
|
|
1218
|
+
through: plusDays(now, RECURRENCE_HORIZON_DAYS),
|
|
1219
|
+
max: 4
|
|
1220
|
+
});
|
|
1221
|
+
const upcoming = soon.map((d) => eventOn(item, d)).find((e) => isUpcoming(e, now));
|
|
1222
|
+
if (upcoming) return upcoming;
|
|
1223
|
+
for (let back = 1; back <= 50; back++) {
|
|
1224
|
+
const past = occurrenceDates(rule, start, {
|
|
1225
|
+
from: plusDays(now, -RECURRENCE_HORIZON_DAYS * back),
|
|
1226
|
+
through: isoDay(now)
|
|
1227
|
+
});
|
|
1228
|
+
if (past.length) return eventOn(item, past[past.length - 1]);
|
|
1229
|
+
if (plusDays(now, -RECURRENCE_HORIZON_DAYS * back) < start.slice(0, 10)) break;
|
|
1230
|
+
}
|
|
1231
|
+
return item;
|
|
1232
|
+
}
|
|
1233
|
+
function queryCollection(ref, collections, opts = {}) {
|
|
1234
|
+
const now = opts.now ?? /* @__PURE__ */ new Date();
|
|
1235
|
+
const all = collections[ref.collection] ?? {};
|
|
1236
|
+
const visible = (item) => !!item && (opts.includeDrafts === true || isLive(item, now));
|
|
1237
|
+
let items;
|
|
1238
|
+
if (ref.items) {
|
|
1239
|
+
items = ref.items.map((slug) => all[slug]).filter(visible).map((item) => nextOccurrence(item, now));
|
|
1240
|
+
} else {
|
|
1241
|
+
const f = ref.filter;
|
|
1242
|
+
const live = Object.values(all).filter(visible);
|
|
1243
|
+
const expanded = f?.upcoming === true ? expandOccurrences(live, {
|
|
1244
|
+
from: isoDay(now),
|
|
1245
|
+
through: plusDays(now, RECURRENCE_HORIZON_DAYS)
|
|
1246
|
+
}) : f?.upcoming === false ? expandOccurrences(live, {
|
|
1247
|
+
from: plusDays(now, -RECURRENCE_HORIZON_DAYS),
|
|
1248
|
+
through: isoDay(now),
|
|
1249
|
+
newest: true
|
|
1250
|
+
}) : live.map((item) => nextOccurrence(item, now));
|
|
1251
|
+
items = expanded.filter((item) => {
|
|
1252
|
+
const fm = item.frontmatter;
|
|
1253
|
+
if (f?.categories?.length && !f.categories.some((c) => fm.categories?.includes(c)))
|
|
1254
|
+
return false;
|
|
1255
|
+
if (f?.tags?.length) {
|
|
1256
|
+
const tags = (fm.tags ?? []).map((t) => t.toLowerCase());
|
|
1257
|
+
if (!f.tags.some((t) => tags.includes(t.toLowerCase()))) return false;
|
|
1258
|
+
}
|
|
1259
|
+
if (f?.featured !== void 0 && fm.featured === true !== f.featured) return false;
|
|
1260
|
+
if (f?.upcoming !== void 0 && isUpcoming(item, now) !== f.upcoming) return false;
|
|
1261
|
+
return true;
|
|
1262
|
+
});
|
|
1263
|
+
}
|
|
1264
|
+
const sort = ref.sort ?? (ref.items ? "manual" : ref.filter?.upcoming ? "date-asc" : "date-desc");
|
|
1265
|
+
if (sort === "date-desc") items.sort((a, b) => time(b) - time(a));
|
|
1266
|
+
else if (sort === "date-asc") items.sort((a, b) => time(a) - time(b));
|
|
1267
|
+
else if (sort === "title")
|
|
1268
|
+
items.sort((a, b) => a.frontmatter.title.localeCompare(b.frontmatter.title));
|
|
1269
|
+
return ref.limit ? items.slice(0, ref.limit) : items;
|
|
1270
|
+
}
|
|
1271
|
+
function useCollectionQuery(ref) {
|
|
1272
|
+
const { collections, now, includeDrafts } = useCollections();
|
|
1273
|
+
return queryCollection(ref, collections, { now, includeDrafts });
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
// src/header.tsx
|
|
1277
|
+
import { themeClass as themeClass4 } from "@omniharmonic/openspace-theme";
|
|
1278
|
+
import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1279
|
+
var DRAWER_VARIANTS = ["minimal-burger", "sidebar-drawer"];
|
|
1280
|
+
function effectiveHeader(site, page) {
|
|
1281
|
+
const o = page?.header;
|
|
1282
|
+
const variant = o?.variant ?? site.header.variant;
|
|
1283
|
+
const transparentOnHero = o?.transparentOnHero ?? site.header.transparentOnHero;
|
|
1284
|
+
const hasHero = (page?.sections.filter((s) => !s.hidden).length ?? 0) > 0;
|
|
1285
|
+
return {
|
|
1286
|
+
...site.header,
|
|
1287
|
+
variant,
|
|
1288
|
+
transparentOnHero,
|
|
1289
|
+
hidden: o?.hidden ?? false,
|
|
1290
|
+
overlay: hasHero && (variant === "transparent-overlay" || transparentOnHero)
|
|
1291
|
+
};
|
|
1292
|
+
}
|
|
1293
|
+
function resolveLeaf(item, doc, basePath, currentId) {
|
|
1294
|
+
if (item.hidden) return null;
|
|
1295
|
+
switch (item.type) {
|
|
1296
|
+
case "page": {
|
|
1297
|
+
const page = doc?.pages[item.page];
|
|
1298
|
+
if (doc && !page) return null;
|
|
1299
|
+
return {
|
|
1300
|
+
kind: "leaf",
|
|
1301
|
+
label: item.label ?? page?.title ?? "Page",
|
|
1302
|
+
href: page ? pagePath(page, basePath) : `${normalizeBasePath(basePath)}/`,
|
|
1303
|
+
current: item.page === currentId,
|
|
1304
|
+
newTab: false
|
|
1305
|
+
};
|
|
1306
|
+
}
|
|
1307
|
+
case "collection":
|
|
1308
|
+
return {
|
|
1309
|
+
kind: "leaf",
|
|
1310
|
+
label: item.label,
|
|
1311
|
+
href: `${normalizeBasePath(basePath)}/${item.collection}/`,
|
|
1312
|
+
current: false,
|
|
1313
|
+
newTab: false
|
|
1314
|
+
};
|
|
1315
|
+
case "link":
|
|
1316
|
+
return {
|
|
1317
|
+
kind: "leaf",
|
|
1318
|
+
label: item.label,
|
|
1319
|
+
href: item.href,
|
|
1320
|
+
current: false,
|
|
1321
|
+
newTab: item.newTab ?? false
|
|
1322
|
+
};
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
function resolveNav(nav, doc, basePath, currentId) {
|
|
1326
|
+
const out = [];
|
|
1327
|
+
for (const item of nav) {
|
|
1328
|
+
if (item.hidden || item.type === "button") continue;
|
|
1329
|
+
if (item.type === "folder") {
|
|
1330
|
+
const items = [];
|
|
1331
|
+
if (item.page) {
|
|
1332
|
+
const landing = resolveLeaf({ type: "page", page: item.page }, doc, basePath, currentId);
|
|
1333
|
+
if (landing) items.push(landing);
|
|
1334
|
+
}
|
|
1335
|
+
for (const leaf of item.items) {
|
|
1336
|
+
const r2 = resolveLeaf(leaf, doc, basePath, currentId);
|
|
1337
|
+
if (r2) items.push(r2);
|
|
1338
|
+
}
|
|
1339
|
+
if (items.length)
|
|
1340
|
+
out.push({
|
|
1341
|
+
kind: "folder",
|
|
1342
|
+
label: item.label,
|
|
1343
|
+
current: items.some((i) => i.current),
|
|
1344
|
+
items
|
|
1345
|
+
});
|
|
1346
|
+
continue;
|
|
1347
|
+
}
|
|
1348
|
+
const r = resolveLeaf(item, doc, basePath, currentId);
|
|
1349
|
+
if (r) out.push(r);
|
|
1350
|
+
}
|
|
1351
|
+
return out;
|
|
1352
|
+
}
|
|
1353
|
+
function NavLeafLink({ item, className }) {
|
|
1354
|
+
return /* @__PURE__ */ jsx7(
|
|
1355
|
+
"a",
|
|
1356
|
+
{
|
|
1357
|
+
className,
|
|
1358
|
+
href: item.href,
|
|
1359
|
+
"aria-current": item.current ? "page" : void 0,
|
|
1360
|
+
target: item.newTab ? "_blank" : void 0,
|
|
1361
|
+
rel: item.newTab ? "noopener" : void 0,
|
|
1362
|
+
children: item.label
|
|
1363
|
+
}
|
|
1364
|
+
);
|
|
1365
|
+
}
|
|
1366
|
+
function NavList({ items, className }) {
|
|
1367
|
+
return /* @__PURE__ */ jsx7("ul", { className: cx("os-nav__list", className), children: items.map(
|
|
1368
|
+
(item, i) => item.kind === "leaf" ? (
|
|
1369
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: nav order is positional
|
|
1370
|
+
/* @__PURE__ */ jsx7("li", { className: "os-nav__item", children: /* @__PURE__ */ jsx7(NavLeafLink, { item, className: "os-nav__link" }) }, i)
|
|
1371
|
+
) : (
|
|
1372
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: nav order is positional
|
|
1373
|
+
/* @__PURE__ */ jsx7("li", { className: "os-nav__item os-nav__item--folder", children: /* @__PURE__ */ jsxs5("details", { className: "os-nav__folder", "data-os-folder": "", children: [
|
|
1374
|
+
/* @__PURE__ */ jsxs5("summary", { className: cx("os-nav__link", item.current && "is-current"), children: [
|
|
1375
|
+
item.label,
|
|
1376
|
+
/* @__PURE__ */ jsx7(
|
|
1377
|
+
"svg",
|
|
1378
|
+
{
|
|
1379
|
+
className: "os-nav__chevron",
|
|
1380
|
+
viewBox: "0 0 12 12",
|
|
1381
|
+
width: "12",
|
|
1382
|
+
height: "12",
|
|
1383
|
+
"aria-hidden": "true",
|
|
1384
|
+
focusable: "false",
|
|
1385
|
+
children: /* @__PURE__ */ jsx7("path", { d: "M3 4.5l3 3 3-3", fill: "none", stroke: "currentColor", strokeWidth: "1.5" })
|
|
1386
|
+
}
|
|
1387
|
+
)
|
|
1388
|
+
] }),
|
|
1389
|
+
/* @__PURE__ */ jsx7("ul", { className: "os-nav__sub", children: item.items.map((leaf, j) => (
|
|
1390
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: nav order is positional
|
|
1391
|
+
/* @__PURE__ */ jsx7("li", { children: /* @__PURE__ */ jsx7(NavLeafLink, { item: leaf, className: "os-nav__sublink" }) }, j)
|
|
1392
|
+
)) })
|
|
1393
|
+
] }) }, i)
|
|
1394
|
+
)
|
|
1395
|
+
) });
|
|
1396
|
+
}
|
|
1397
|
+
function Brand({ site, className }) {
|
|
1398
|
+
const { basePath } = useRenderContext();
|
|
1399
|
+
const logo = site.logo;
|
|
1400
|
+
return /* @__PURE__ */ jsx7("a", { className: cx("os-header__brand", className), href: `${normalizeBasePath(basePath)}/`, children: logo ? /* @__PURE__ */ jsx7(
|
|
1401
|
+
Img,
|
|
1402
|
+
{
|
|
1403
|
+
src: logo.src,
|
|
1404
|
+
alt: logo.alt || site.title,
|
|
1405
|
+
...logo.width ? { width: logo.width } : {},
|
|
1406
|
+
...logo.height ? { height: logo.height } : {},
|
|
1407
|
+
sizes: "240px",
|
|
1408
|
+
treatment: "none",
|
|
1409
|
+
priority: true,
|
|
1410
|
+
className: "os-header__logo"
|
|
1411
|
+
}
|
|
1412
|
+
) : /* @__PURE__ */ jsx7("span", { className: "os-header__title", children: site.title }) });
|
|
1413
|
+
}
|
|
1414
|
+
function MenuIcon() {
|
|
1415
|
+
return /* @__PURE__ */ jsx7(
|
|
1416
|
+
"svg",
|
|
1417
|
+
{
|
|
1418
|
+
className: "os-header__burger",
|
|
1419
|
+
viewBox: "0 0 24 24",
|
|
1420
|
+
width: "24",
|
|
1421
|
+
height: "24",
|
|
1422
|
+
"aria-hidden": "true",
|
|
1423
|
+
focusable: "false",
|
|
1424
|
+
children: /* @__PURE__ */ jsx7(
|
|
1425
|
+
"path",
|
|
1426
|
+
{
|
|
1427
|
+
d: "M4 7h16M4 12h16M4 17h16",
|
|
1428
|
+
fill: "none",
|
|
1429
|
+
stroke: "currentColor",
|
|
1430
|
+
strokeWidth: "1.6",
|
|
1431
|
+
strokeLinecap: "round"
|
|
1432
|
+
}
|
|
1433
|
+
)
|
|
1434
|
+
}
|
|
1435
|
+
);
|
|
1436
|
+
}
|
|
1437
|
+
function CartButton() {
|
|
1438
|
+
return /* @__PURE__ */ jsxs5(
|
|
1439
|
+
"button",
|
|
1440
|
+
{
|
|
1441
|
+
type: "button",
|
|
1442
|
+
className: "os-header__cart",
|
|
1443
|
+
"data-os-behavior": "cart",
|
|
1444
|
+
"data-os-cart-toggle": "",
|
|
1445
|
+
"aria-label": "Cart",
|
|
1446
|
+
children: [
|
|
1447
|
+
/* @__PURE__ */ jsx7("svg", { viewBox: "0 0 24 24", width: "22", height: "22", "aria-hidden": "true", focusable: "false", children: /* @__PURE__ */ jsx7(
|
|
1448
|
+
"path",
|
|
1449
|
+
{
|
|
1450
|
+
d: "M5 7h14l-1.5 12h-11zM9 7a3 3 0 0 1 6 0",
|
|
1451
|
+
fill: "none",
|
|
1452
|
+
stroke: "currentColor",
|
|
1453
|
+
strokeWidth: "1.6",
|
|
1454
|
+
strokeLinejoin: "round"
|
|
1455
|
+
}
|
|
1456
|
+
) }),
|
|
1457
|
+
/* @__PURE__ */ jsx7("span", { className: "os-header__cart-count", "data-os-cart-count": "" })
|
|
1458
|
+
]
|
|
1459
|
+
}
|
|
1460
|
+
);
|
|
1461
|
+
}
|
|
1462
|
+
function Header({ site, page, heroTheme, cart }) {
|
|
1463
|
+
const ctx = useRenderContext();
|
|
1464
|
+
const h = effectiveHeader(site, page);
|
|
1465
|
+
if (h.hidden) return null;
|
|
1466
|
+
const nav = resolveNav(site.nav, ctx.doc, ctx.basePath, page?.id ?? ctx.page?.id);
|
|
1467
|
+
const buttons = site.nav.filter(
|
|
1468
|
+
(i) => i.type === "button" && !i.hidden
|
|
1469
|
+
);
|
|
1470
|
+
const social = h.showSocial ? site.social ?? [] : [];
|
|
1471
|
+
const theme = h.overlay && heroTheme ? heroTheme : h.colorTheme ?? "light";
|
|
1472
|
+
const drawer = DRAWER_VARIANTS.includes(h.variant);
|
|
1473
|
+
const split = h.variant === "split-nav";
|
|
1474
|
+
const half = Math.ceil(nav.length / 2);
|
|
1475
|
+
const menuId = "os-menu";
|
|
1476
|
+
const actions = /* @__PURE__ */ jsxs5("div", { className: "os-header__actions", children: [
|
|
1477
|
+
social.length > 0 && /* @__PURE__ */ jsx7(SocialLinks, { profiles: social, className: "os-header__social" }),
|
|
1478
|
+
buttons.map((b, i) => (
|
|
1479
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: nav order is positional
|
|
1480
|
+
/* @__PURE__ */ jsx7(Button, { to: b.link, role: b.style, className: "os-header__button", children: b.label }, i)
|
|
1481
|
+
))
|
|
1482
|
+
] });
|
|
1483
|
+
let desktopNav = null;
|
|
1484
|
+
if (!drawer && nav.length) {
|
|
1485
|
+
desktopNav = split ? /* @__PURE__ */ jsxs5("nav", { className: "os-nav os-nav--split", "aria-label": "Main", children: [
|
|
1486
|
+
/* @__PURE__ */ jsx7(NavList, { items: nav.slice(0, half), className: "os-nav__list--start" }),
|
|
1487
|
+
/* @__PURE__ */ jsx7(Brand, { site, className: "os-header__brand--split" }),
|
|
1488
|
+
/* @__PURE__ */ jsx7(NavList, { items: nav.slice(half), className: "os-nav__list--end" })
|
|
1489
|
+
] }) : /* @__PURE__ */ jsx7("nav", { className: "os-nav", "aria-label": "Main", children: /* @__PURE__ */ jsx7(NavList, { items: nav }) });
|
|
1490
|
+
}
|
|
1491
|
+
return /* @__PURE__ */ jsxs5(
|
|
1492
|
+
"header",
|
|
1493
|
+
{
|
|
1494
|
+
className: cx(
|
|
1495
|
+
"os-header",
|
|
1496
|
+
`os-header--${h.variant}`,
|
|
1497
|
+
themeClass4(theme),
|
|
1498
|
+
h.sticky && "is-sticky",
|
|
1499
|
+
h.overlay && "is-overlay",
|
|
1500
|
+
drawer && "is-drawer",
|
|
1501
|
+
`has-menu-${h.variant === "sidebar-drawer" ? "drawer" : h.mobileMenu}`
|
|
1502
|
+
),
|
|
1503
|
+
"data-os-header": "",
|
|
1504
|
+
"data-os-behavior": "menu",
|
|
1505
|
+
children: [
|
|
1506
|
+
/* @__PURE__ */ jsxs5("div", { className: "os-header__bar", children: [
|
|
1507
|
+
/* @__PURE__ */ jsx7(Brand, { site, className: split ? "os-header__brand--main" : void 0 }),
|
|
1508
|
+
desktopNav,
|
|
1509
|
+
actions,
|
|
1510
|
+
cart && /* @__PURE__ */ jsx7(CartButton, {}),
|
|
1511
|
+
/* @__PURE__ */ jsxs5(
|
|
1512
|
+
"button",
|
|
1513
|
+
{
|
|
1514
|
+
type: "button",
|
|
1515
|
+
className: "os-header__toggle",
|
|
1516
|
+
"aria-expanded": "false",
|
|
1517
|
+
"aria-controls": menuId,
|
|
1518
|
+
"data-os-menu-toggle": "",
|
|
1519
|
+
children: [
|
|
1520
|
+
/* @__PURE__ */ jsx7(MenuIcon, {}),
|
|
1521
|
+
/* @__PURE__ */ jsx7("span", { className: "os-visually-hidden", children: "Menu" })
|
|
1522
|
+
]
|
|
1523
|
+
}
|
|
1524
|
+
)
|
|
1525
|
+
] }),
|
|
1526
|
+
/* @__PURE__ */ jsxs5(
|
|
1527
|
+
"div",
|
|
1528
|
+
{
|
|
1529
|
+
id: menuId,
|
|
1530
|
+
className: cx(
|
|
1531
|
+
"os-menu",
|
|
1532
|
+
`os-menu--${h.variant === "sidebar-drawer" ? "drawer" : h.mobileMenu}`,
|
|
1533
|
+
themeClass4(h.colorTheme ?? "light")
|
|
1534
|
+
),
|
|
1535
|
+
"data-os-menu": "",
|
|
1536
|
+
children: [
|
|
1537
|
+
/* @__PURE__ */ jsxs5("div", { className: "os-menu__head", children: [
|
|
1538
|
+
/* @__PURE__ */ jsx7("span", { className: "os-menu__title", children: site.title }),
|
|
1539
|
+
/* @__PURE__ */ jsxs5("button", { type: "button", className: "os-menu__close", "data-os-menu-close": "", children: [
|
|
1540
|
+
/* @__PURE__ */ jsx7("svg", { viewBox: "0 0 24 24", width: "24", height: "24", "aria-hidden": "true", focusable: "false", children: /* @__PURE__ */ jsx7(
|
|
1541
|
+
"path",
|
|
1542
|
+
{
|
|
1543
|
+
d: "M6 6l12 12M18 6L6 18",
|
|
1544
|
+
fill: "none",
|
|
1545
|
+
stroke: "currentColor",
|
|
1546
|
+
strokeWidth: "1.6",
|
|
1547
|
+
strokeLinecap: "round"
|
|
1548
|
+
}
|
|
1549
|
+
) }),
|
|
1550
|
+
/* @__PURE__ */ jsx7("span", { className: "os-visually-hidden", children: "Close menu" })
|
|
1551
|
+
] })
|
|
1552
|
+
] }),
|
|
1553
|
+
/* @__PURE__ */ jsx7("nav", { className: "os-menu__nav", "aria-label": "Menu", children: /* @__PURE__ */ jsx7(NavList, { items: nav, className: "os-menu__list" }) }),
|
|
1554
|
+
(buttons.length > 0 || (site.social?.length ?? 0) > 0) && /* @__PURE__ */ jsxs5("div", { className: "os-menu__foot", children: [
|
|
1555
|
+
buttons.map((b, i) => (
|
|
1556
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: nav order is positional
|
|
1557
|
+
/* @__PURE__ */ jsx7(Button, { to: b.link, role: b.style, children: b.label }, i)
|
|
1558
|
+
)),
|
|
1559
|
+
site.social && /* @__PURE__ */ jsx7(SocialLinks, { profiles: site.social })
|
|
1560
|
+
] })
|
|
1561
|
+
]
|
|
1562
|
+
}
|
|
1563
|
+
)
|
|
1564
|
+
]
|
|
1565
|
+
}
|
|
1566
|
+
);
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
// src/html.ts
|
|
1570
|
+
import {
|
|
1571
|
+
chosenCustomFont,
|
|
1572
|
+
customFontFaces,
|
|
1573
|
+
customFontPreloads,
|
|
1574
|
+
googleFontsUrl
|
|
1575
|
+
} from "@omniharmonic/openspace-theme";
|
|
1576
|
+
import { createElement } from "react";
|
|
1577
|
+
import { renderToStaticMarkup } from "react-dom/server";
|
|
1578
|
+
|
|
1579
|
+
// src/html-meta.ts
|
|
1580
|
+
import { compileStyles } from "@omniharmonic/openspace-theme";
|
|
1581
|
+
var ESC = {
|
|
1582
|
+
"&": "&",
|
|
1583
|
+
"<": "<",
|
|
1584
|
+
">": ">",
|
|
1585
|
+
'"': """,
|
|
1586
|
+
"'": "'"
|
|
1587
|
+
};
|
|
1588
|
+
var escapeHtml = (s) => s.replace(/[&<>"']/g, (ch) => ESC[ch]);
|
|
1589
|
+
function jsonForScript(value) {
|
|
1590
|
+
return JSON.stringify(value).replace(/</g, "\\u003c").replace(LINE_SEP, "\\u2028").replace(PARA_SEP, "\\u2029");
|
|
1591
|
+
}
|
|
1592
|
+
var LINE_SEP = new RegExp(String.fromCharCode(8232), "g");
|
|
1593
|
+
var PARA_SEP = new RegExp(String.fromCharCode(8233), "g");
|
|
1594
|
+
var safeCss = (css) => css.replace(/<\/style/gi, "<\\/style");
|
|
1595
|
+
var compiled = /* @__PURE__ */ new WeakMap();
|
|
1596
|
+
function compiledStylesFor(styles) {
|
|
1597
|
+
let hit = compiled.get(styles);
|
|
1598
|
+
if (!hit) {
|
|
1599
|
+
hit = compileStyles(styles);
|
|
1600
|
+
compiled.set(styles, hit);
|
|
1601
|
+
}
|
|
1602
|
+
return hit;
|
|
1603
|
+
}
|
|
1604
|
+
function siteUrlFor(doc, override) {
|
|
1605
|
+
const raw = override ?? (doc.config.deploy.customDomain ? `https://${doc.config.deploy.customDomain}` : doc.config.deploy.url);
|
|
1606
|
+
return raw.endsWith("/") ? raw : `${raw}/`;
|
|
1607
|
+
}
|
|
1608
|
+
function absolute(siteUrl, path) {
|
|
1609
|
+
if (/^https?:\/\//.test(path)) return path;
|
|
1610
|
+
return new URL(path.replace(/^\/+/, ""), siteUrl).toString();
|
|
1611
|
+
}
|
|
1612
|
+
function ogImageUrl(src, siteUrl, imageMap) {
|
|
1613
|
+
const mapped = imageMap?.[src]?.src;
|
|
1614
|
+
if (mapped) return new URL(mapped, siteUrl).toString();
|
|
1615
|
+
return absolute(siteUrl, mediaUrl(src));
|
|
1616
|
+
}
|
|
1617
|
+
function pageTitle(doc, page) {
|
|
1618
|
+
const own = page.seo?.title;
|
|
1619
|
+
if (own) return own;
|
|
1620
|
+
const template = doc.site.seo?.titleTemplate;
|
|
1621
|
+
if (page.slug === "") {
|
|
1622
|
+
return doc.site.tagline ? `${doc.site.title} \u2014 ${doc.site.tagline}` : doc.site.title;
|
|
1623
|
+
}
|
|
1624
|
+
return template ? template.replace("%s", page.title) : `${page.title} \u2014 ${doc.site.title}`;
|
|
1625
|
+
}
|
|
1626
|
+
function pageDescription(doc, page) {
|
|
1627
|
+
return page.seo?.description ?? doc.site.seo?.description ?? doc.site.tagline ?? void 0;
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1630
|
+
// src/page.tsx
|
|
1631
|
+
import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1632
|
+
function resolvePage(doc, page) {
|
|
1633
|
+
if (typeof page !== "string") return page;
|
|
1634
|
+
const found = doc.pages[page];
|
|
1635
|
+
if (!found) throw new Error(`Page ${page} does not exist`);
|
|
1636
|
+
return found;
|
|
1637
|
+
}
|
|
1638
|
+
function PageView(props) {
|
|
1639
|
+
const { doc, mode = "production", imageMap = {}, extras } = props;
|
|
1640
|
+
const page = resolvePage(doc, props.page);
|
|
1641
|
+
const basePath = normalizeBasePath(props.basePath);
|
|
1642
|
+
const hubUrl = props.hubUrl ?? doc.config.hub;
|
|
1643
|
+
const visible = page.sections.filter((s) => !s.hidden || mode === "editor");
|
|
1644
|
+
const header = effectiveHeader(doc.site, page);
|
|
1645
|
+
const heroTheme = visible[0]?.style.colorTheme ?? "light";
|
|
1646
|
+
const cart = doc.site.integrations?.stripeConnected ?? false;
|
|
1647
|
+
const logoVars = header.logoHeight ? responsiveVars("logo-h", header.logoHeight, (px) => `${px}px`) : void 0;
|
|
1648
|
+
const content = /* @__PURE__ */ jsx8(RenderContext.Provider, { value: { doc, page, mode, basePath, hubUrl }, children: /* @__PURE__ */ jsx8(ImageResolverContext.Provider, { value: imageMap, children: /* @__PURE__ */ jsxs6(
|
|
1649
|
+
"div",
|
|
1650
|
+
{
|
|
1651
|
+
className: cx("os-page", header.overlay && !header.hidden && "has-overlay-header"),
|
|
1652
|
+
"data-os-page": page.id,
|
|
1653
|
+
style: logoVars,
|
|
1654
|
+
children: [
|
|
1655
|
+
/* @__PURE__ */ jsx8("a", { className: "os-skip-link", href: "#main", children: "Skip to content" }),
|
|
1656
|
+
/* @__PURE__ */ jsx8(AnnouncementBar, { announcement: doc.site.announcement }),
|
|
1657
|
+
/* @__PURE__ */ jsx8(Header, { site: doc.site, page, heroTheme, cart }),
|
|
1658
|
+
/* @__PURE__ */ jsx8("main", { id: "main", className: "os-main", tabIndex: -1, children: visible.map((section, i) => /* @__PURE__ */ jsx8(FirstSectionContext.Provider, { value: i === 0, children: /* @__PURE__ */ jsx8(SectionView, { section }) }, section.id)) }),
|
|
1659
|
+
/* @__PURE__ */ jsx8(Footer, { site: doc.site }),
|
|
1660
|
+
mode !== "editor" ? /* @__PURE__ */ jsx8(SitePopup, { popup: doc.site.popup }) : null,
|
|
1661
|
+
extras
|
|
1662
|
+
]
|
|
1663
|
+
}
|
|
1664
|
+
) }) });
|
|
1665
|
+
return props.editor ? /* @__PURE__ */ jsx8(EditorContext.Provider, { value: props.editor, children: content }) : content;
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1668
|
+
// src/html.ts
|
|
1669
|
+
function monogramIcon(title, brand) {
|
|
1670
|
+
const letter = escapeHtml((title.trim().match(/\p{L}|\p{N}/u)?.[0] ?? "\u2022").toUpperCase());
|
|
1671
|
+
const bg = brand?.background ?? "#222222";
|
|
1672
|
+
const fg = brand?.heading ?? "#ffffff";
|
|
1673
|
+
const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><rect width="64" height="64" rx="14" fill="${bg}"/><text x="32" y="43" font-family="system-ui,sans-serif" font-size="34" font-weight="600" text-anchor="middle" fill="${fg}">${letter}</text></svg>`;
|
|
1674
|
+
return `data:image/svg+xml,${encodeURIComponent(svg)}`;
|
|
1675
|
+
}
|
|
1676
|
+
var attr = (tag, name2) => new RegExp(`\\s${name2}="([^"]*)"`).exec(tag)?.[1];
|
|
1677
|
+
function phoneShare(sizes) {
|
|
1678
|
+
const last = (sizes ?? "100vw").split(",").pop()?.trim() ?? "100vw";
|
|
1679
|
+
const vw = /^(\d+(?:\.\d+)?)vw$/.exec(last);
|
|
1680
|
+
if (vw) return Number(vw[1]) / 100;
|
|
1681
|
+
const times = /\*\s*(\d+(?:\.\d+)?)\s*\)$/.exec(last);
|
|
1682
|
+
if (times) return Number(times[1]);
|
|
1683
|
+
return /^calc\(100vw/.test(last) ? 1 : 0.5;
|
|
1684
|
+
}
|
|
1685
|
+
function lcpImagePreload(body) {
|
|
1686
|
+
const imgs = [...body.matchAll(/<img\b[^>]*\bfetchpriority="high"[^>]*>/gi)].slice(0, 4);
|
|
1687
|
+
let best = null;
|
|
1688
|
+
for (const m of imgs) {
|
|
1689
|
+
const share = phoneShare(attr(m[0], "sizes"));
|
|
1690
|
+
const w = Number(attr(m[0], "width")) || 1;
|
|
1691
|
+
const h = Number(attr(m[0], "height")) || w;
|
|
1692
|
+
const score = share * share * (h / w);
|
|
1693
|
+
if (!best || score > best.score) best = { m, score };
|
|
1694
|
+
}
|
|
1695
|
+
if (!best || best.m.index === void 0) return "";
|
|
1696
|
+
const img = best.m;
|
|
1697
|
+
const at = best.m.index;
|
|
1698
|
+
const pictureStart = body.lastIndexOf("<picture", at);
|
|
1699
|
+
const between = pictureStart >= 0 ? body.slice(pictureStart, at) : "";
|
|
1700
|
+
const source = pictureStart >= 0 && !between.includes("</picture>") ? /<source\b[^>]*>/i.exec(between)?.[0] : void 0;
|
|
1701
|
+
const tag = source ?? img[0];
|
|
1702
|
+
const srcset = attr(tag, "srcset") ?? attr(tag, "srcSet");
|
|
1703
|
+
const sizes = attr(tag, "sizes");
|
|
1704
|
+
const src = attr(img[0], "src");
|
|
1705
|
+
const type = source ? attr(source, "type") : void 0;
|
|
1706
|
+
if (!srcset && !src) return "";
|
|
1707
|
+
return `<link rel="preload" as="image"${srcset ? ` imagesrcset="${srcset}"` : ` href="${src}"`}${srcset && sizes ? ` imagesizes="${sizes}"` : ""}${type ? ` type="${type}"` : ""} fetchpriority="high">`;
|
|
1708
|
+
}
|
|
1709
|
+
function renderPageHtml(opts) {
|
|
1710
|
+
const { doc } = opts;
|
|
1711
|
+
const mode = opts.mode ?? "production";
|
|
1712
|
+
const page = resolvePage(doc, opts.pageId);
|
|
1713
|
+
const basePath = normalizeBasePath(opts.basePath);
|
|
1714
|
+
const hubUrl = (opts.hubUrl ?? doc.config.hub).replace(/\/+$/, "");
|
|
1715
|
+
const behaviorsUrl = (opts.behaviorsUrl ?? `${basePath}/_os/behaviors`).replace(/\/+$/, "");
|
|
1716
|
+
const siteUrl = siteUrlFor(doc, opts.siteUrl);
|
|
1717
|
+
const site = doc.site;
|
|
1718
|
+
const styles = compiledStylesFor(doc.styles);
|
|
1719
|
+
const body = renderToStaticMarkup(
|
|
1720
|
+
createElement(PageView, {
|
|
1721
|
+
doc,
|
|
1722
|
+
page,
|
|
1723
|
+
mode,
|
|
1724
|
+
basePath,
|
|
1725
|
+
imageMap: opts.imageMap ?? {},
|
|
1726
|
+
hubUrl
|
|
1727
|
+
})
|
|
1728
|
+
);
|
|
1729
|
+
const analytics = mode === "production" && doc.config.features.analytics && site.integrations?.analytics !== false;
|
|
1730
|
+
const behaviors = behaviorsInHtml(body);
|
|
1731
|
+
if (analytics) behaviors.push("beacon");
|
|
1732
|
+
const title = pageTitle(doc, page);
|
|
1733
|
+
const description = pageDescription(doc, page);
|
|
1734
|
+
const canonical = page.seo?.canonical ?? absolute(siteUrl, pagePath(page));
|
|
1735
|
+
const noindex = mode === "preview" || page.seo?.noindex || site.seo?.noindex;
|
|
1736
|
+
const ogImageSrc = page.seo?.image ?? site.seo?.image;
|
|
1737
|
+
const ogImage = ogImageSrc ? ogImageUrl(ogImageSrc, siteUrl, opts.imageMap) : void 0;
|
|
1738
|
+
const customHeading = chosenCustomFont(doc.styles, doc.styles.choices.headingFont);
|
|
1739
|
+
const customBody = chosenCustomFont(doc.styles, doc.styles.choices.bodyFont);
|
|
1740
|
+
const fonts = customHeading && customBody ? [] : opts.fontStylesheets ?? [googleFontsUrl(styles.tokens.pairing)];
|
|
1741
|
+
const fontFaces = customFontFaces(doc.styles, (src) => mediaUrl(src, basePath));
|
|
1742
|
+
const fontPreloads = customFontPreloads(doc.styles, {
|
|
1743
|
+
heading: styles.tokens.pairing.headingWeight,
|
|
1744
|
+
body: styles.tokens.pairing.bodyWeight
|
|
1745
|
+
});
|
|
1746
|
+
const usesGoogle = fonts.some((f) => f.startsWith("https://fonts.googleapis.com/"));
|
|
1747
|
+
const meta = (name2, content, attr2 = "name") => content ? `<meta ${attr2}="${name2}" content="${escapeHtml(content)}">` : "";
|
|
1748
|
+
const head = [
|
|
1749
|
+
'<meta charset="utf-8">',
|
|
1750
|
+
'<meta name="viewport" content="width=device-width, initial-scale=1">',
|
|
1751
|
+
`<title>${escapeHtml(title)}</title>`,
|
|
1752
|
+
// The LCP image first: discovered before the stylesheets and fonts below.
|
|
1753
|
+
lcpImagePreload(body),
|
|
1754
|
+
meta("description", description),
|
|
1755
|
+
noindex ? '<meta name="robots" content="noindex">' : "",
|
|
1756
|
+
`<link rel="canonical" href="${escapeHtml(canonical)}">`,
|
|
1757
|
+
meta("og:type", "website", "property"),
|
|
1758
|
+
meta("og:site_name", site.title, "property"),
|
|
1759
|
+
meta("og:title", page.seo?.title ?? page.title, "property"),
|
|
1760
|
+
meta("og:description", description, "property"),
|
|
1761
|
+
meta("og:url", canonical, "property"),
|
|
1762
|
+
meta("og:image", ogImage, "property"),
|
|
1763
|
+
meta("og:locale", site.locale.replace("-", "_"), "property"),
|
|
1764
|
+
meta("twitter:card", ogImage ? "summary_large_image" : "summary"),
|
|
1765
|
+
`<link rel="icon" href="${escapeHtml(site.favicon ? mediaUrl(site.favicon, basePath) : monogramIcon(site.title, styles.tokens.themes.brand))}">`,
|
|
1766
|
+
'<meta name="generator" content="OpenSpace">',
|
|
1767
|
+
"<script>document.documentElement.classList.add('os-js')</script>",
|
|
1768
|
+
usesGoogle ? '<link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>' : "",
|
|
1769
|
+
...fontPreloads.map(
|
|
1770
|
+
(src) => `<link rel="preload" as="font" type="font/woff2" href="${escapeHtml(mediaUrl(src, basePath))}" crossorigin>`
|
|
1771
|
+
),
|
|
1772
|
+
...fonts.map((href) => `<link rel="stylesheet" href="${escapeHtml(href)}">`),
|
|
1773
|
+
fontFaces ? `<style data-os="fonts">${safeCss(fontFaces)}</style>` : "",
|
|
1774
|
+
opts.stylesheets?.tokens ? `<link rel="stylesheet" href="${escapeHtml(opts.stylesheets.tokens)}">` : `<style data-os="tokens">${styles.css}</style>`,
|
|
1775
|
+
opts.stylesheets?.base ? `<link rel="stylesheet" href="${escapeHtml(opts.stylesheets.base)}">` : `<style data-os="base">${BASE_CSS}</style>`,
|
|
1776
|
+
site.customCss ? `<style data-os="custom">${safeCss(site.customCss)}</style>` : "",
|
|
1777
|
+
...[opts.jsonLd ?? []].flat().map((ld) => `<script type="application/ld+json">${jsonForScript(ld)}</script>`),
|
|
1778
|
+
...behaviors.map(
|
|
1779
|
+
(b) => `<link rel="modulepreload" href="${escapeHtml(`${behaviorsUrl}/${b}.js`)}">`
|
|
1780
|
+
),
|
|
1781
|
+
opts.extraHead ?? "",
|
|
1782
|
+
site.headInjection ?? "",
|
|
1783
|
+
page.headInjection ?? ""
|
|
1784
|
+
];
|
|
1785
|
+
const bodyAttrs = [
|
|
1786
|
+
`class="os-body"`,
|
|
1787
|
+
`data-os-site="${escapeHtml(doc.config.siteId)}"`,
|
|
1788
|
+
`data-os-hub="${escapeHtml(hubUrl)}"`,
|
|
1789
|
+
`data-os-base="${escapeHtml(basePath)}"`,
|
|
1790
|
+
`data-os-mode="${mode}"`,
|
|
1791
|
+
analytics ? `data-os-behavior="beacon"` : ""
|
|
1792
|
+
].filter(Boolean).join(" ");
|
|
1793
|
+
const scripts = behaviors.length ? `<script type="module" src="${escapeHtml(`${behaviorsUrl}/${BEHAVIOR_LOADER}.js`)}"></script>` : "";
|
|
1794
|
+
return `<!doctype html>
|
|
1795
|
+
<html lang="${escapeHtml(site.locale)}">
|
|
1796
|
+
<head>
|
|
1797
|
+
${head.filter(Boolean).join("\n")}
|
|
1798
|
+
</head>
|
|
1799
|
+
<body ${bodyAttrs}>
|
|
1800
|
+
${body}
|
|
1801
|
+
${scripts}${site.footerInjection ?? ""}${page.footerInjection ?? ""}
|
|
1802
|
+
</body>
|
|
1803
|
+
</html>
|
|
1804
|
+
`;
|
|
1805
|
+
}
|
|
1806
|
+
function behaviorsForPage(input) {
|
|
1807
|
+
if (typeof input === "string") return behaviorsInHtml(input);
|
|
1808
|
+
return behaviorsInHtml(renderPageHtml(input));
|
|
1809
|
+
}
|
|
1810
|
+
|
|
1811
|
+
// src/richtext.tsx
|
|
1812
|
+
import { colorVar as colorVar2 } from "@omniharmonic/openspace-theme";
|
|
1813
|
+
import { Fragment as Fragment3, useContext as useContext7 } from "react";
|
|
1814
|
+
import { Fragment as Fragment4, jsx as jsx9 } from "react/jsx-runtime";
|
|
1815
|
+
var typeClass = (preset) => `os-type-${preset}`;
|
|
1816
|
+
var MARK_ORDER = [
|
|
1817
|
+
"code",
|
|
1818
|
+
"strike",
|
|
1819
|
+
"underline",
|
|
1820
|
+
"italic",
|
|
1821
|
+
"bold",
|
|
1822
|
+
"highlight",
|
|
1823
|
+
"link"
|
|
1824
|
+
];
|
|
1825
|
+
function wrapMark(mark, child, key, basePath) {
|
|
1826
|
+
switch (mark.type) {
|
|
1827
|
+
case "bold":
|
|
1828
|
+
return /* @__PURE__ */ jsx9("strong", { children: child }, key);
|
|
1829
|
+
case "italic":
|
|
1830
|
+
return /* @__PURE__ */ jsx9("em", { children: child }, key);
|
|
1831
|
+
case "underline":
|
|
1832
|
+
return /* @__PURE__ */ jsx9("u", { children: child }, key);
|
|
1833
|
+
case "strike":
|
|
1834
|
+
return /* @__PURE__ */ jsx9("s", { children: child }, key);
|
|
1835
|
+
case "code":
|
|
1836
|
+
return /* @__PURE__ */ jsx9("code", { children: child }, key);
|
|
1837
|
+
case "highlight": {
|
|
1838
|
+
const color = mark.attrs?.color;
|
|
1839
|
+
return /* @__PURE__ */ jsx9(
|
|
1840
|
+
"mark",
|
|
1841
|
+
{
|
|
1842
|
+
className: "os-highlight",
|
|
1843
|
+
style: color ? { "--hl": `var(${colorVar2(color)})` } : void 0,
|
|
1844
|
+
children: child
|
|
1845
|
+
},
|
|
1846
|
+
key
|
|
1847
|
+
);
|
|
1848
|
+
}
|
|
1849
|
+
case "link": {
|
|
1850
|
+
const { href, title, newTab } = mark.attrs;
|
|
1851
|
+
return /* @__PURE__ */ jsx9(
|
|
1852
|
+
"a",
|
|
1853
|
+
{
|
|
1854
|
+
href: withBase(href, basePath),
|
|
1855
|
+
title,
|
|
1856
|
+
target: newTab ? "_blank" : void 0,
|
|
1857
|
+
rel: newTab ? "noopener" : void 0,
|
|
1858
|
+
children: child
|
|
1859
|
+
},
|
|
1860
|
+
key
|
|
1861
|
+
);
|
|
1862
|
+
}
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1865
|
+
function renderInline(nodes, basePath) {
|
|
1866
|
+
if (!nodes) return [];
|
|
1867
|
+
return nodes.map((node, i) => {
|
|
1868
|
+
if (node.type === "hardBreak") return /* @__PURE__ */ jsx9("br", {}, i);
|
|
1869
|
+
const marks = [...node.marks ?? []].sort(
|
|
1870
|
+
(a, b) => MARK_ORDER.indexOf(a.type) - MARK_ORDER.indexOf(b.type)
|
|
1871
|
+
);
|
|
1872
|
+
let out = node.text;
|
|
1873
|
+
for (const m of marks) out = wrapMark(m, out, i, basePath);
|
|
1874
|
+
return marks.length ? out : /* @__PURE__ */ jsx9(Fragment3, { children: node.text }, i);
|
|
1875
|
+
});
|
|
1876
|
+
}
|
|
1877
|
+
function renderBlock(node, key, basePath) {
|
|
1878
|
+
switch (node.type) {
|
|
1879
|
+
case "paragraph": {
|
|
1880
|
+
const preset = node.attrs?.preset;
|
|
1881
|
+
const children = renderInline(node.content, basePath);
|
|
1882
|
+
return /* @__PURE__ */ jsx9("p", { className: preset && preset !== "body" ? typeClass(preset) : void 0, children: children.length ? children : /* @__PURE__ */ jsx9("br", {}) }, key);
|
|
1883
|
+
}
|
|
1884
|
+
case "heading": {
|
|
1885
|
+
const { level, preset } = node.attrs;
|
|
1886
|
+
const Tag = `h${level}`;
|
|
1887
|
+
return /* @__PURE__ */ jsx9(Tag, { className: typeClass(preset ?? `h${level}`), children: renderInline(node.content, basePath) }, key);
|
|
1888
|
+
}
|
|
1889
|
+
case "bulletList":
|
|
1890
|
+
return /* @__PURE__ */ jsx9("ul", { children: node.content.map((li, i) => (
|
|
1891
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: rich text nodes have no ids
|
|
1892
|
+
/* @__PURE__ */ jsx9("li", { children: li.content.map((c, j) => renderBlock(c, j, basePath)) }, i)
|
|
1893
|
+
)) }, key);
|
|
1894
|
+
case "orderedList":
|
|
1895
|
+
return /* @__PURE__ */ jsx9("ol", { start: node.attrs?.start, children: node.content.map((li, i) => (
|
|
1896
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: rich text nodes have no ids
|
|
1897
|
+
/* @__PURE__ */ jsx9("li", { children: li.content.map((c, j) => renderBlock(c, j, basePath)) }, i)
|
|
1898
|
+
)) }, key);
|
|
1899
|
+
case "blockquote":
|
|
1900
|
+
return /* @__PURE__ */ jsx9("blockquote", { children: node.content.map((c, j) => renderBlock(c, j, basePath)) }, key);
|
|
1901
|
+
}
|
|
1902
|
+
}
|
|
1903
|
+
function RichText({ doc, className, as: Tag = "div" }) {
|
|
1904
|
+
const { basePath } = useRenderContext();
|
|
1905
|
+
const editor = useEditor();
|
|
1906
|
+
const owner = useContext7(BlockFieldsContext);
|
|
1907
|
+
const out = !doc || doc.content.length === 0 ? null : /* @__PURE__ */ jsx9(Tag, { className: cx("os-rich", className), children: doc.content.map((node, i) => renderBlock(node, i, basePath)) });
|
|
1908
|
+
if (editor?.renderRichText && owner && doc) {
|
|
1909
|
+
const fieldPath = fieldPathOf(owner.fields, doc);
|
|
1910
|
+
if (fieldPath)
|
|
1911
|
+
return /* @__PURE__ */ jsx9(Fragment4, { children: editor.renderRichText({
|
|
1912
|
+
block: owner.block,
|
|
1913
|
+
fieldPath,
|
|
1914
|
+
doc,
|
|
1915
|
+
className,
|
|
1916
|
+
as: Tag,
|
|
1917
|
+
children: out
|
|
1918
|
+
}) });
|
|
1919
|
+
}
|
|
1920
|
+
return out;
|
|
1921
|
+
}
|
|
1922
|
+
|
|
1923
|
+
// src/index.ts
|
|
1924
|
+
var name = "@omniharmonic/openspace-renderer";
|
|
1925
|
+
export {
|
|
1926
|
+
AboveFoldContext,
|
|
1927
|
+
AnnouncementBar,
|
|
1928
|
+
BASE_CSS,
|
|
1929
|
+
BEHAVIORS,
|
|
1930
|
+
BEHAVIOR_BUDGETS,
|
|
1931
|
+
BEHAVIOR_LOADER,
|
|
1932
|
+
BlockFieldsContext,
|
|
1933
|
+
BlockRegistryContext,
|
|
1934
|
+
BlockView,
|
|
1935
|
+
Button,
|
|
1936
|
+
CollectionsContext,
|
|
1937
|
+
DRAWER_VARIANTS,
|
|
1938
|
+
EditorContext,
|
|
1939
|
+
FirstSectionContext,
|
|
1940
|
+
Footer,
|
|
1941
|
+
Header,
|
|
1942
|
+
ImageResolverContext,
|
|
1943
|
+
Img,
|
|
1944
|
+
Link,
|
|
1945
|
+
PageView,
|
|
1946
|
+
Popup,
|
|
1947
|
+
RECURRENCE_HORIZON_DAYS,
|
|
1948
|
+
REMOTE_WIDTHS,
|
|
1949
|
+
RenderContext,
|
|
1950
|
+
RichText,
|
|
1951
|
+
SectionBackgroundLayer,
|
|
1952
|
+
SectionContext,
|
|
1953
|
+
SectionView,
|
|
1954
|
+
SitePopup,
|
|
1955
|
+
SocialIcon,
|
|
1956
|
+
SocialLinks,
|
|
1957
|
+
alignClasses,
|
|
1958
|
+
backgroundVars,
|
|
1959
|
+
behaviorsForPage,
|
|
1960
|
+
behaviorsInHtml,
|
|
1961
|
+
blockStyleProps,
|
|
1962
|
+
blockTypeClass,
|
|
1963
|
+
blocksByColumn,
|
|
1964
|
+
collectImageSrcs,
|
|
1965
|
+
colorValue,
|
|
1966
|
+
compiledStylesFor,
|
|
1967
|
+
cx,
|
|
1968
|
+
effectiveHeader,
|
|
1969
|
+
escapeHtml,
|
|
1970
|
+
eventOn,
|
|
1971
|
+
expandOccurrences,
|
|
1972
|
+
fallbackMobileRects,
|
|
1973
|
+
feedKey,
|
|
1974
|
+
fieldPathOf,
|
|
1975
|
+
getBlock,
|
|
1976
|
+
globalRegistry,
|
|
1977
|
+
gridItemVars,
|
|
1978
|
+
gridVars,
|
|
1979
|
+
isBehaviorName,
|
|
1980
|
+
isLive,
|
|
1981
|
+
isUpcoming,
|
|
1982
|
+
itemDate,
|
|
1983
|
+
itemKey,
|
|
1984
|
+
itemPath,
|
|
1985
|
+
jsonForScript,
|
|
1986
|
+
listBlocks,
|
|
1987
|
+
mediaUrl,
|
|
1988
|
+
mobileRows,
|
|
1989
|
+
name,
|
|
1990
|
+
networkLabel,
|
|
1991
|
+
nextOccurrence,
|
|
1992
|
+
normalizeBasePath,
|
|
1993
|
+
occurrenceDates,
|
|
1994
|
+
pageDescription,
|
|
1995
|
+
pagePath,
|
|
1996
|
+
pageTitle,
|
|
1997
|
+
parseRecurrence,
|
|
1998
|
+
popupTrigger,
|
|
1999
|
+
queryCollection,
|
|
2000
|
+
registerBlocks,
|
|
2001
|
+
remoteSrcset,
|
|
2002
|
+
renderPageHtml,
|
|
2003
|
+
resolveImage,
|
|
2004
|
+
resolveLink,
|
|
2005
|
+
resolveNav,
|
|
2006
|
+
resolvePage,
|
|
2007
|
+
responsiveVars,
|
|
2008
|
+
sectionFragment,
|
|
2009
|
+
sectionStyleVars,
|
|
2010
|
+
shortHash,
|
|
2011
|
+
siteUrlFor,
|
|
2012
|
+
stackColumnVars,
|
|
2013
|
+
stackVars,
|
|
2014
|
+
typeClass,
|
|
2015
|
+
unregisterBlocks,
|
|
2016
|
+
useCollectionQuery,
|
|
2017
|
+
useCollections,
|
|
2018
|
+
useEditor,
|
|
2019
|
+
useImageMap,
|
|
2020
|
+
useRenderContext,
|
|
2021
|
+
withBase
|
|
2022
|
+
};
|
|
2023
|
+
//# sourceMappingURL=index.js.map
|