@frontlit/page-builder 0.1.0-alpha.1 → 0.1.0-alpha.2
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/README.md +6 -2
- package/dist/blocks/index.d.ts +7 -5
- package/dist/blocks/index.js +4 -3
- package/dist/builder/index.d.ts +5 -7
- package/dist/builder/index.js +6 -5
- package/dist/{button-C3VrMJkr.d.ts → button-Bzt1GDO-.d.ts} +2 -2
- package/dist/{chunk-GTY75HW2.js → chunk-ATNM5JHI.js} +2 -2
- package/dist/chunk-ATNM5JHI.js.map +1 -0
- package/dist/{chunk-7W6Q4WA7.js → chunk-H4P5MFJI.js} +89 -73
- package/dist/chunk-H4P5MFJI.js.map +1 -0
- package/dist/{chunk-SSQWYMQR.js → chunk-HDINUNI2.js} +11 -39
- package/dist/chunk-HDINUNI2.js.map +1 -0
- package/dist/{chunk-MO76KZY2.js → chunk-KQKSLI3L.js} +339 -314
- package/dist/chunk-KQKSLI3L.js.map +1 -0
- package/dist/{chunk-RBJBODQP.js → chunk-SGNRWWG3.js} +1931 -919
- package/dist/chunk-SGNRWWG3.js.map +1 -0
- package/dist/{chunk-7KIX4WAO.js → chunk-UO7XGBGM.js} +439 -535
- package/dist/chunk-UO7XGBGM.js.map +1 -0
- package/dist/chunk-XU7ALPGQ.js +22 -0
- package/dist/chunk-XU7ALPGQ.js.map +1 -0
- package/dist/components/index.d.ts +3 -3
- package/dist/components/index.js +3 -2
- package/dist/data-slots.d.ts +3 -3
- package/dist/data-slots.js +1 -1
- package/dist/{image-D7GlnOio.d.ts → image-DAWVx1zQ.d.ts} +3 -2
- package/dist/index.d.ts +7 -7
- package/dist/index.js +7 -6
- package/dist/media-CnLImjVC.d.ts +21 -0
- package/dist/models/index.d.ts +5 -5
- package/dist/models/index.js +1 -0
- package/dist/{page-DfVl_aHX.d.ts → page-W3hQga4w.d.ts} +2 -3
- package/dist/primitives/index.d.ts +251 -60
- package/dist/primitives/index.js +2 -1
- package/dist/renderer/index.d.ts +3 -3
- package/dist/renderer/index.js +5 -4
- package/dist/{section-background-BgOCp5l3.d.ts → section-background-MsdE40Rk.d.ts} +1 -1
- package/dist/styles.css +3896 -8303
- package/dist/{theme-style-39nxXQy0.d.ts → theme-style-Cbl7NM-T.d.ts} +23 -1
- package/dist/theme.css +174 -191
- package/dist/{widget--uEOpdq3.d.ts → widget-B_RoG6se.d.ts} +10 -6
- package/package.json +21 -10
- package/dist/chunk-7KIX4WAO.js.map +0 -1
- package/dist/chunk-7W6Q4WA7.js.map +0 -1
- package/dist/chunk-GTY75HW2.js.map +0 -1
- package/dist/chunk-MO76KZY2.js.map +0 -1
- package/dist/chunk-RBJBODQP.js.map +0 -1
- package/dist/chunk-SSQWYMQR.js.map +0 -1
- package/dist/fonts/montserrat/montserrat-latin-100-normal.woff2 +0 -0
- package/dist/fonts/montserrat/montserrat-latin-200-normal.woff2 +0 -0
- package/dist/fonts/montserrat/montserrat-latin-300-normal.woff2 +0 -0
- package/dist/fonts/montserrat/montserrat-latin-400-normal.woff2 +0 -0
- package/dist/fonts/montserrat/montserrat-latin-500-normal.woff2 +0 -0
- package/dist/fonts/montserrat/montserrat-latin-600-normal.woff2 +0 -0
- package/dist/fonts/montserrat/montserrat-latin-700-normal.woff2 +0 -0
- package/dist/fonts/montserrat/montserrat-latin-800-normal.woff2 +0 -0
- package/dist/fonts/montserrat/montserrat-latin-900-normal.woff2 +0 -0
- package/dist/fonts/source-sans-3/source-sans-3-latin-400-normal.woff2 +0 -0
- package/dist/media-Njv378j7.d.ts +0 -13
package/README.md
CHANGED
|
@@ -20,6 +20,8 @@ import "@frontlit/page-builder/styles.css";
|
|
|
20
20
|
|
|
21
21
|
That file includes the utilities, default tokens, the theme-picker fonts (Inter, Playfair, and the rest), and the TipTap prose styles from `@frontlit/text-editor`. Hosts that already inject those faces via `next/font` keep working: `--font-inter` on the document still wins.
|
|
22
22
|
|
|
23
|
+
**Future:** split the theme-picker faces out of `styles.css` into an opt-in `@frontlit/page-builder/fonts.css`. Hosts that already load Inter, Playfair, and the rest (for example via `next/font`) can skip that file; everyone else imports it next to `styles.css`. `--font-*` on the document and the system-font fallbacks in `theme.css` stay as they are.
|
|
24
|
+
|
|
23
25
|
If the host already runs Tailwind CSS v4, you can skip `styles.css` and scan the packages instead. Import the prose sheet so rich-text lists and code blocks survive Tailwind preflight:
|
|
24
26
|
|
|
25
27
|
```css
|
|
@@ -40,7 +42,6 @@ import {
|
|
|
40
42
|
} from "@frontlit/page-builder";
|
|
41
43
|
import "@frontlit/page-builder/styles.css";
|
|
42
44
|
|
|
43
|
-
|
|
44
45
|
// Read-only rendering (a published page, a marketing site, a preview):
|
|
45
46
|
<PageRenderer
|
|
46
47
|
layout={createDefaultLayout()}
|
|
@@ -64,7 +65,10 @@ Internal `/...` links render as native `<a>` tags by default, so the package wor
|
|
|
64
65
|
|
|
65
66
|
```tsx
|
|
66
67
|
import Link from "next/link";
|
|
67
|
-
import {
|
|
68
|
+
import {
|
|
69
|
+
PageRenderer,
|
|
70
|
+
type PageBuilderLinkProps,
|
|
71
|
+
} from "@frontlit/page-builder";
|
|
68
72
|
|
|
69
73
|
function AppLink(props: PageBuilderLinkProps) {
|
|
70
74
|
return <Link {...props} />;
|
package/dist/blocks/index.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { M as Media } from '../media-
|
|
1
|
+
import { a as WidgetDefaultSettings, W as Widget } from '../widget-B_RoG6se.js';
|
|
2
|
+
import { M as Media } from '../media-CnLImjVC.js';
|
|
3
3
|
import { A as Alignment, H as HorizontalAlignment } from '../alignment-dZDzN96G.js';
|
|
4
4
|
import { TextEditorContent } from '@frontlit/text-editor';
|
|
5
|
-
import { A as AspectRatio, a as ImageObjectFit } from '../image-
|
|
5
|
+
import { A as AspectRatio, a as ImageObjectFit } from '../image-DAWVx1zQ.js';
|
|
6
6
|
import 'react';
|
|
7
|
-
import '../section-background-
|
|
8
|
-
import '../theme-style-
|
|
7
|
+
import '../section-background-MsdE40Rk.js';
|
|
8
|
+
import '../theme-style-Cbl7NM-T.js';
|
|
9
9
|
|
|
10
10
|
interface HeaderNavLink {
|
|
11
11
|
id: string;
|
|
@@ -16,6 +16,8 @@ interface HeaderNavLink {
|
|
|
16
16
|
interface HeaderSettings extends WidgetDefaultSettings {
|
|
17
17
|
logoText?: string;
|
|
18
18
|
logoImage?: string;
|
|
19
|
+
logoCorners?: number;
|
|
20
|
+
logoRadius?: number;
|
|
19
21
|
links?: HeaderNavLink[];
|
|
20
22
|
ctaLabel?: string;
|
|
21
23
|
ctaHref?: string;
|
package/dist/blocks/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
export { Banner, Content, DataSlot, EmailForm, Embed, Faq, Featured, Footer, Grid, Header, Hero, Marquee, MediaBlock, Pricing, RichText, SupportForm } from '../chunk-
|
|
3
|
-
import '../chunk-
|
|
4
|
-
import '../chunk-
|
|
2
|
+
export { Banner, Content, DataSlot, EmailForm, Embed, Faq, Featured, Footer, Grid, Header, Hero, Marquee, MediaBlock, Pricing, RichText, SupportForm } from '../chunk-UO7XGBGM.js';
|
|
3
|
+
import '../chunk-H4P5MFJI.js';
|
|
4
|
+
import '../chunk-SGNRWWG3.js';
|
|
5
|
+
import '../chunk-XU7ALPGQ.js';
|
|
5
6
|
//# sourceMappingURL=index.js.map
|
|
6
7
|
//# sourceMappingURL=index.js.map
|
package/dist/builder/index.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import React__default from 'react';
|
|
2
|
-
import { T as Theme, S as SectionBackground } from '../section-background-
|
|
3
|
-
import {
|
|
4
|
-
import { a as PageSeo } from '../page-
|
|
2
|
+
import { T as Theme, S as SectionBackground } from '../section-background-MsdE40Rk.js';
|
|
3
|
+
import { c as WidgetInstance, P as PageData, W as Widget } from '../widget-B_RoG6se.js';
|
|
4
|
+
import { a as PageSeo } from '../page-W3hQga4w.js';
|
|
5
5
|
import { P as PageBuilderLinkComponent } from '../nav-link-BfxvQ1s6.js';
|
|
6
6
|
import { LucideIcon } from 'lucide-react';
|
|
7
7
|
import { R as ResolvedColorMode } from '../color-mode-DIEEt9WP.js';
|
|
8
|
-
import { T as ThemeStyle } from '../theme-style-
|
|
9
|
-
import '../media-
|
|
8
|
+
import { T as ThemeStyle } from '../theme-style-Cbl7NM-T.js';
|
|
9
|
+
import '../media-CnLImjVC.js';
|
|
10
10
|
|
|
11
11
|
interface PageBuilderState {
|
|
12
12
|
layout: WidgetInstance[];
|
|
@@ -229,8 +229,6 @@ declare function ThemeEditor({ theme, appliedThemeId, systemThemes, customThemes
|
|
|
229
229
|
/**
|
|
230
230
|
* Page-level SEO settings panel. Like the block and theme editors, changes
|
|
231
231
|
* apply live — the host app persists them via the builder's `onChange`.
|
|
232
|
-
* Adapted from CourseLit's SeoEditor, minus the media library (images are
|
|
233
|
-
* referenced by URL, matching the rest of this package).
|
|
234
232
|
*/
|
|
235
233
|
declare function SeoEditor({ seo, onChange, }: {
|
|
236
234
|
seo: PageSeo;
|
package/dist/builder/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
export { BlockPicker, BuilderTopBar, ColorSelector, ContentPaddingSelector, MaxWidthSelector, PATTERN_PRESETS, PageBuilder, PageBuilderPropertyHeader, PageBuilderSlider, SectionBackgroundPanel, SeoEditor, SettingsPanel, ThemeEditor, VerticalPaddingSelector, getMaxWidthTWClass, getVerticalPaddingTWClass } from '../chunk-
|
|
3
|
-
import '../chunk-
|
|
4
|
-
import '../chunk-
|
|
5
|
-
import '../chunk-
|
|
6
|
-
import '../chunk-
|
|
2
|
+
export { BlockPicker, BuilderTopBar, ColorSelector, ContentPaddingSelector, MaxWidthSelector, PATTERN_PRESETS, PageBuilder, PageBuilderPropertyHeader, PageBuilderSlider, SectionBackgroundPanel, SeoEditor, SettingsPanel, ThemeEditor, VerticalPaddingSelector, getMaxWidthTWClass, getVerticalPaddingTWClass } from '../chunk-KQKSLI3L.js';
|
|
3
|
+
import '../chunk-HDINUNI2.js';
|
|
4
|
+
import '../chunk-UO7XGBGM.js';
|
|
5
|
+
import '../chunk-H4P5MFJI.js';
|
|
6
|
+
import '../chunk-SGNRWWG3.js';
|
|
7
|
+
import '../chunk-XU7ALPGQ.js';
|
|
7
8
|
//# sourceMappingURL=index.js.map
|
|
8
9
|
//# sourceMappingURL=index.js.map
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import React__default from 'react';
|
|
3
|
+
import { T as ThemeStyle } from './theme-style-Cbl7NM-T.js';
|
|
3
4
|
import * as class_variance_authority_types from 'class-variance-authority/types';
|
|
4
5
|
import { VariantProps } from 'class-variance-authority';
|
|
5
|
-
import { T as ThemeStyle } from './theme-style-39nxXQy0.js';
|
|
6
6
|
|
|
7
7
|
declare const buttonVariants: (props?: {
|
|
8
|
-
variant?: "link" | "default" | "
|
|
8
|
+
variant?: "link" | "default" | "secondary" | "destructive" | "outline" | "ghost";
|
|
9
9
|
size?: "default" | "icon" | "sm" | "lg";
|
|
10
10
|
} & class_variance_authority_types.ClassProp) => string;
|
|
11
11
|
interface ButtonProps$1 extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
|
|
@@ -18,5 +18,5 @@ async function loadDataSlots(layout, loaders, pageData) {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
export { loadDataSlots };
|
|
21
|
-
//# sourceMappingURL=chunk-
|
|
22
|
-
//# sourceMappingURL=chunk-
|
|
21
|
+
//# sourceMappingURL=chunk-ATNM5JHI.js.map
|
|
22
|
+
//# sourceMappingURL=chunk-ATNM5JHI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/data-slots.ts"],"names":[],"mappings":";AAyBA,eAAsB,aAAA,CACpB,MAAA,EACA,OAAA,EACA,QAAA,EACoC;AACpC,EAAA,MAAM,QAAQ,MAAA,CAAO,MAAA,CAAO,CAAC,QAAA,KAAa,QAAA,CAAS,SAAS,WAAW,CAAA;AACvE,EAAA,MAAM,QAAA,GAAW,MAAM,OAAA,CAAQ,GAAA;AAAA,IAC7B,KAAA,CAAM,GAAA,CAAI,OAAO,QAAA,KAAa;AAC5B,MAAA,MAAM,IAAA,GAAO,SAAS,QAAA,EAAU,IAAA;AAChC,MAAA,IAAI,OAAO,IAAA,KAAS,QAAA,IAAY,CAAC,MAAM,OAAO,IAAA;AAC9C,MAAA,MAAM,MAAA,GAAS,QAAQ,IAAI,CAAA;AAC3B,MAAA,IAAI,CAAC,QAAQ,OAAO,IAAA;AACpB,MAAA,OAAO,CAAC,MAAM,MAAM,MAAA,CAAO,EAAE,IAAA,EAAM,QAAA,EAAU,QAAA,EAAU,CAAC,CAAA;AAAA,IAC1D,CAAC;AAAA,GACH;AACA,EAAA,MAAM,SAAoC,EAAC;AAC3C,EAAA,KAAA,MAAW,SAAS,QAAA,EAAU;AAC5B,IAAA,IAAI,OAAO,MAAA,CAAO,KAAA,CAAM,CAAC,CAAC,CAAA,GAAI,MAAM,CAAC,CAAA;AAAA,EACvC;AACA,EAAA,OAAO,MAAA;AACT","file":"chunk-ATNM5JHI.js","sourcesContent":["import type { ReactNode } from \"react\";\nimport type { PageData, WidgetInstance } from \"./models\";\n\n/** Context supplied to a host application's data-slot loader. */\nexport interface DataSlotLoadRequest {\n /** The slot identifier declared in `instance.settings.slot`. */\n slot: string;\n /** The full persisted block configuration. */\n instance: WidgetInstance;\n /** Page context supplied by the host renderer. */\n pageData: PageData;\n}\n\n/** A host-owned resolver for one named data slot. */\nexport type DataSlotLoader = (\n request: DataSlotLoadRequest,\n) => ReactNode | Promise<ReactNode>;\n\nexport type DataSlotLoaders = Readonly<Record<string, DataSlotLoader>>;\n\n/**\n * Resolves generic data-slot blocks before passing their output to\n * `PageRenderer`. The page-builder package deliberately does not fetch data\n * or understand a host application's domain model.\n */\nexport async function loadDataSlots(\n layout: readonly WidgetInstance[],\n loaders: DataSlotLoaders,\n pageData: PageData,\n): Promise<Record<string, ReactNode>> {\n const slots = layout.filter((instance) => instance.name === \"data-slot\");\n const resolved = await Promise.all(\n slots.map(async (instance) => {\n const slot = instance.settings?.slot;\n if (typeof slot !== \"string\" || !slot) return null;\n const loader = loaders[slot];\n if (!loader) return null;\n return [slot, await loader({ slot, instance, pageData })] as const;\n }),\n );\n const output: Record<string, ReactNode> = {};\n for (const entry of resolved) {\n if (entry) output[entry[0]] = entry[1];\n }\n return output;\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { Button2, Button } from './chunk-
|
|
2
|
+
import { cn, Button2, Button } from './chunk-SGNRWWG3.js';
|
|
3
3
|
import * as React2 from 'react';
|
|
4
4
|
import { useRef, useState, useId, useEffect } from 'react';
|
|
5
5
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
@@ -101,11 +101,7 @@ var YOUTUBE_HOSTNAMES = /* @__PURE__ */ new Set([
|
|
|
101
101
|
"www.youtube-nocookie.com",
|
|
102
102
|
"youtu.be"
|
|
103
103
|
]);
|
|
104
|
-
var VIMEO_HOSTNAMES = /* @__PURE__ */ new Set([
|
|
105
|
-
"vimeo.com",
|
|
106
|
-
"www.vimeo.com",
|
|
107
|
-
"player.vimeo.com"
|
|
108
|
-
]);
|
|
104
|
+
var VIMEO_HOSTNAMES = /* @__PURE__ */ new Set(["vimeo.com", "www.vimeo.com", "player.vimeo.com"]);
|
|
109
105
|
var isNumeric = (value) => /^\d+$/.test(value);
|
|
110
106
|
var extractYouTubeVideoId = (parsedUrl) => {
|
|
111
107
|
const hostname = parsedUrl.hostname.toLowerCase();
|
|
@@ -259,65 +255,52 @@ function VideoWithPreview({
|
|
|
259
255
|
}
|
|
260
256
|
};
|
|
261
257
|
return /* @__PURE__ */ jsxs("div", { className: aspectRatio === "9/16" ? "max-w-xs mx-auto" : "", children: [
|
|
262
|
-
/* @__PURE__ */ jsx(
|
|
263
|
-
|
|
264
|
-
{
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
Play,
|
|
309
|
-
{
|
|
310
|
-
className: "h-8 w-8",
|
|
311
|
-
fill: "currentColor"
|
|
312
|
-
}
|
|
313
|
-
) })
|
|
314
|
-
] }) })
|
|
315
|
-
]
|
|
316
|
-
}
|
|
317
|
-
)
|
|
318
|
-
)
|
|
319
|
-
}
|
|
320
|
-
),
|
|
258
|
+
/* @__PURE__ */ jsx("div", { className: "relative overflow-hidden", style: aspectRatioStyle(), children: !modal && isPlaying ? (
|
|
259
|
+
// In-place video player
|
|
260
|
+
/* @__PURE__ */ jsx("div", { className: "w-full h-full", children: videoType !== "self-hosted" ? /* @__PURE__ */ jsx(
|
|
261
|
+
"iframe",
|
|
262
|
+
{
|
|
263
|
+
src: getEmbedUrl(),
|
|
264
|
+
className: "w-full h-full",
|
|
265
|
+
allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",
|
|
266
|
+
allowFullScreen: true
|
|
267
|
+
}
|
|
268
|
+
) : /* @__PURE__ */ jsx(
|
|
269
|
+
"video",
|
|
270
|
+
{
|
|
271
|
+
src: videoUrl,
|
|
272
|
+
controls: true,
|
|
273
|
+
controlsList: "nodownload",
|
|
274
|
+
onContextMenu: (e) => e.preventDefault(),
|
|
275
|
+
autoPlay: true,
|
|
276
|
+
className: "w-full h-full",
|
|
277
|
+
children: "Your browser does not support the video tag."
|
|
278
|
+
}
|
|
279
|
+
) })
|
|
280
|
+
) : (
|
|
281
|
+
// Thumbnail with play button
|
|
282
|
+
/* @__PURE__ */ jsxs(
|
|
283
|
+
"div",
|
|
284
|
+
{
|
|
285
|
+
className: "cursor-pointer group w-full h-full",
|
|
286
|
+
onClick: handleThumbnailClick,
|
|
287
|
+
children: [
|
|
288
|
+
/* @__PURE__ */ jsx(
|
|
289
|
+
"img",
|
|
290
|
+
{
|
|
291
|
+
src: getThumbnailUrl() || "/placeholder.svg",
|
|
292
|
+
alt: `Thumbnail for ${title}`,
|
|
293
|
+
className: "w-full h-full object-cover transition-transform duration-300 group-hover:scale-105"
|
|
294
|
+
}
|
|
295
|
+
),
|
|
296
|
+
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-black/30 flex items-center justify-center", children: /* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
297
|
+
/* @__PURE__ */ jsx("span", { className: "absolute -inset-1.5 rounded-full bg-gray-200/50 animate-pulse duration-[3000ms]" }),
|
|
298
|
+
/* @__PURE__ */ jsx("div", { className: "relative z-1 rounded-full bg-primary p-4 text-primary-foreground", children: /* @__PURE__ */ jsx(Play, { className: "h-8 w-8", fill: "currentColor" }) })
|
|
299
|
+
] }) })
|
|
300
|
+
]
|
|
301
|
+
}
|
|
302
|
+
)
|
|
303
|
+
) }),
|
|
321
304
|
modal && isModalOpen && /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 bg-black/95 z-50 flex items-center justify-center p-4 sm:p-6 md:p-10", children: [
|
|
322
305
|
/* @__PURE__ */ jsx(
|
|
323
306
|
"button",
|
|
@@ -364,6 +347,17 @@ function VideoWithPreview({
|
|
|
364
347
|
] })
|
|
365
348
|
] });
|
|
366
349
|
}
|
|
350
|
+
var roundedMap = {
|
|
351
|
+
0: "rounded-none",
|
|
352
|
+
1: "rounded-sm",
|
|
353
|
+
2: "rounded",
|
|
354
|
+
3: "rounded-md",
|
|
355
|
+
4: "rounded-lg",
|
|
356
|
+
5: "rounded-xl",
|
|
357
|
+
6: "rounded-2xl",
|
|
358
|
+
7: "rounded-3xl",
|
|
359
|
+
8: "rounded-full"
|
|
360
|
+
};
|
|
367
361
|
var objectFitClass = {
|
|
368
362
|
cover: "object-cover",
|
|
369
363
|
contain: "object-contain",
|
|
@@ -382,35 +376,57 @@ var Image = (props) => {
|
|
|
382
376
|
classes = "",
|
|
383
377
|
width = "w-full",
|
|
384
378
|
height = "h-auto",
|
|
385
|
-
objectFit = "cover"
|
|
379
|
+
objectFit = "cover",
|
|
380
|
+
borderRadius
|
|
386
381
|
} = props;
|
|
387
382
|
const source = src || defaultImage;
|
|
383
|
+
const radiusClass = typeof borderRadius === "number" ? roundedMap[borderRadius] ?? "rounded" : borderRadius;
|
|
388
384
|
if (!source) {
|
|
389
385
|
if (noDefaultImage) return null;
|
|
390
386
|
return /* @__PURE__ */ jsx(
|
|
391
387
|
"div",
|
|
392
388
|
{
|
|
393
|
-
className:
|
|
389
|
+
className: cn(
|
|
390
|
+
"relative aspect-video overflow-hidden bg-muted",
|
|
391
|
+
width,
|
|
392
|
+
height,
|
|
393
|
+
radiusClass,
|
|
394
|
+
classes,
|
|
395
|
+
className
|
|
396
|
+
)
|
|
394
397
|
}
|
|
395
398
|
);
|
|
396
399
|
}
|
|
397
400
|
return /* @__PURE__ */ jsx(
|
|
398
401
|
"div",
|
|
399
402
|
{
|
|
400
|
-
className:
|
|
403
|
+
className: cn(
|
|
404
|
+
"relative aspect-video overflow-hidden",
|
|
405
|
+
width,
|
|
406
|
+
height,
|
|
407
|
+
radiusClass,
|
|
408
|
+
classes,
|
|
409
|
+
className
|
|
410
|
+
),
|
|
401
411
|
children: /* @__PURE__ */ jsx(
|
|
402
412
|
"img",
|
|
403
413
|
{
|
|
404
414
|
src: source,
|
|
405
415
|
alt,
|
|
406
416
|
loading,
|
|
407
|
-
className:
|
|
417
|
+
className: cn(
|
|
418
|
+
"absolute inset-0 h-full w-full",
|
|
419
|
+
radiusClass,
|
|
420
|
+
objectFitClass[objectFit]
|
|
421
|
+
)
|
|
408
422
|
}
|
|
409
423
|
)
|
|
410
424
|
}
|
|
411
425
|
);
|
|
412
426
|
};
|
|
413
|
-
var PageBuilderLinkContext = React2.createContext(
|
|
427
|
+
var PageBuilderLinkContext = React2.createContext(
|
|
428
|
+
null
|
|
429
|
+
);
|
|
414
430
|
function PageBuilderLinkProvider({
|
|
415
431
|
component,
|
|
416
432
|
children
|
|
@@ -510,6 +526,6 @@ function ThemeModeSwitcher({
|
|
|
510
526
|
);
|
|
511
527
|
}
|
|
512
528
|
|
|
513
|
-
export { Image, NavLink, PageBuilderLinkProvider, SandboxedEmbed, ThemeModeSwitcher, VideoWithPreview, usePageBuilderLinkComponent };
|
|
514
|
-
//# sourceMappingURL=chunk-
|
|
515
|
-
//# sourceMappingURL=chunk-
|
|
529
|
+
export { Image, NavLink, PageBuilderLinkProvider, SandboxedEmbed, ThemeModeSwitcher, VideoWithPreview, roundedMap, usePageBuilderLinkComponent };
|
|
530
|
+
//# sourceMappingURL=chunk-H4P5MFJI.js.map
|
|
531
|
+
//# sourceMappingURL=chunk-H4P5MFJI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/sandboxed-embed.tsx","../src/utils/extract-video-id.ts","../src/components/video-with-preview.tsx","../src/components/image.tsx","../src/components/nav-link.tsx","../src/components/theme-mode-switcher.tsx"],"names":["useState","useEffect","jsx","Button"],"mappings":"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","file":"chunk-H4P5MFJI.js","sourcesContent":["import React, { useEffect, useId, useRef, useState } from \"react\";\n\ninterface SandboxedEmbedProps {\n content: string;\n className?: string;\n style?: React.CSSProperties;\n id?: string;\n}\n\nexport const SandboxedEmbed = ({\n content,\n className,\n style,\n id,\n}: SandboxedEmbedProps) => {\n const iframeRef = useRef<HTMLIFrameElement>(null);\n const [height, setHeight] = useState(\"100px\");\n const generatedId = useId();\n const iframeId = id || generatedId;\n\n useEffect(() => {\n const handleMessage = (event: MessageEvent) => {\n if (\n event.data?.type === \"embed-resize\" &&\n event.data?.id === iframeId &&\n event.data?.height\n ) {\n setHeight(`${event.data.height}px`);\n }\n };\n\n window.addEventListener(\"message\", handleMessage);\n return () => window.removeEventListener(\"message\", handleMessage);\n }, [iframeId]);\n\n const srcDoc = `\n <!DOCTYPE html>\n <html>\n <head>\n <style>\n html, body { margin: 0; padding: 0; overflow: hidden; }\n #content-wrapper { overflow: hidden; height: auto; }\n </style>\n </head>\n <body>\n <div id=\"content-wrapper\">\n ${content || \"\"}\n </div>\n <script>\n const iframeId = \"${iframeId}\";\n \n function sendHeight() {\n const wrapper = document.getElementById('content-wrapper');\n const height = wrapper ? wrapper.scrollHeight : document.body.scrollHeight;\n window.parent.postMessage({ type: \"embed-resize\", id: iframeId, height: height }, \"*\");\n }\n\n // Use a ResizeObserver to detect size changes\n const resizeObserver = new ResizeObserver(entries => {\n sendHeight();\n });\n const target = document.getElementById('content-wrapper') || document.body;\n resizeObserver.observe(target);\n \n // Use MutationObserver to detect DOM changes\n const mutationObserver = new MutationObserver(() => {\n sendHeight();\n });\n mutationObserver.observe(target, { childList: true, subtree: true, attributes: true });\n \n // Poll for a few seconds to catch late-loading content\n let interval = setInterval(sendHeight, 500);\n setTimeout(() => clearInterval(interval), 10000);\n \n window.addEventListener('load', sendHeight);\n window.addEventListener('resize', sendHeight);\n \n // Initial send\n sendHeight();\n </script>\n </body>\n </html>\n `;\n\n return (\n <iframe\n ref={iframeRef}\n srcDoc={srcDoc}\n className={className}\n style={{\n width: \"100%\",\n height: height,\n border: \"none\",\n ...style,\n }}\n sandbox=\"allow-scripts allow-popups allow-forms allow-same-origin\"\n title=\"Embedded Content\"\n />\n );\n};\n","export type SupportedVideoPlatform = \"youtube\" | \"vimeo\";\n\nconst YOUTUBE_HOSTNAMES = new Set([\n \"youtube.com\",\n \"www.youtube.com\",\n \"m.youtube.com\",\n \"music.youtube.com\",\n \"youtube-nocookie.com\",\n \"www.youtube-nocookie.com\",\n \"youtu.be\",\n]);\n\nconst VIMEO_HOSTNAMES = new Set([\"vimeo.com\", \"www.vimeo.com\", \"player.vimeo.com\"]);\n\nconst isNumeric = (value: string) => /^\\d+$/.test(value);\n\nconst extractYouTubeVideoId = (parsedUrl: URL) => {\n const hostname = parsedUrl.hostname.toLowerCase();\n\n if (!YOUTUBE_HOSTNAMES.has(hostname)) {\n return null;\n }\n\n if (hostname === \"youtu.be\") {\n return parsedUrl.pathname.split(\"/\").filter(Boolean)[0] || null;\n }\n\n if (parsedUrl.pathname === \"/watch\") {\n return parsedUrl.searchParams.get(\"v\");\n }\n\n const pathParts = parsedUrl.pathname.split(\"/\").filter(Boolean);\n const firstPart = pathParts[0];\n const secondPart = pathParts[1];\n\n if (\n (firstPart === \"embed\" ||\n firstPart === \"v\" ||\n firstPart === \"shorts\" ||\n firstPart === \"live\") &&\n secondPart\n ) {\n return secondPart;\n }\n\n return null;\n};\n\nconst extractVimeoVideoId = (parsedUrl: URL) => {\n const hostname = parsedUrl.hostname.toLowerCase();\n\n if (!VIMEO_HOSTNAMES.has(hostname)) {\n return null;\n }\n\n const pathParts = parsedUrl.pathname.split(\"/\").filter(Boolean);\n\n for (let index = pathParts.length - 1; index >= 0; index--) {\n if (isNumeric(pathParts[index])) {\n return pathParts[index];\n }\n }\n\n return null;\n};\n\nexport function extractVideoId(url?: string, platform?: SupportedVideoPlatform) {\n if (!url) {\n return null;\n }\n\n const normalizedUrl = /^[a-z][a-z\\d+\\-.]*:\\/\\//i.test(url) ? url : `https://${url}`;\n\n try {\n const parsedUrl = new URL(normalizedUrl);\n\n if (platform === \"youtube\") {\n return extractYouTubeVideoId(parsedUrl);\n }\n\n if (platform === \"vimeo\") {\n return extractVimeoVideoId(parsedUrl);\n }\n\n return extractYouTubeVideoId(parsedUrl) || extractVimeoVideoId(parsedUrl);\n } catch {\n return null;\n }\n}\n","import { useState, useEffect } from \"react\";\nimport { Play, X } from \"lucide-react\";\nimport { extractVideoId } from \"../utils\";\n\ntype VideoType = \"youtube\" | \"vimeo\" | \"self-hosted\";\n\nexport type AspectRatio =\n | \"16/9\" // Widescreen\n | \"4/3\" // Standard\n | \"1/1\" // Square\n | \"21/9\" // Ultrawide\n | \"9/16\" // Vertical\n | \"2/1\" // Panoramic\n | \"3/2\" // Classic\n | string; // Allow custom aspect ratios\n\nexport interface VideoThumbnailProps {\n title?: string;\n thumbnailUrl?: string;\n videoUrl: string;\n aspectRatio?: AspectRatio;\n modal?: boolean;\n}\n\nexport function VideoWithPreview({\n title = \"Video\",\n thumbnailUrl,\n videoUrl = \"https://www.youtube.com/watch?v=7OP2bU9RWVE\",\n aspectRatio = \"16/9\",\n modal = false,\n}: VideoThumbnailProps) {\n const [isModalOpen, setIsModalOpen] = useState(false);\n const [isPlaying, setIsPlaying] = useState(false);\n const [vimeoThumbnail, setVimeoThumbnail] = useState<string | null>(null);\n\n const detectVideoType = (): VideoType => {\n if (extractVideoId(videoUrl, \"youtube\")) {\n return \"youtube\";\n }\n\n if (extractVideoId(videoUrl, \"vimeo\")) {\n return \"vimeo\";\n }\n\n return \"self-hosted\";\n };\n\n const videoType = detectVideoType();\n\n const getVideoId = () => {\n if (videoType === \"youtube\") {\n return extractVideoId(videoUrl, \"youtube\");\n }\n\n if (videoType === \"vimeo\") {\n return extractVideoId(videoUrl, \"vimeo\");\n }\n\n return null;\n };\n\n // Fetch Vimeo thumbnail if needed\n useEffect(() => {\n const fetchVimeoThumbnail = async () => {\n // Skip fetching if a thumbnail URL is already provided\n if (thumbnailUrl || videoType !== \"vimeo\") return;\n\n const videoId = getVideoId();\n if (!videoId) return;\n\n try {\n // Use Vimeo's oEmbed API to get thumbnail (no token required)\n const response = await fetch(\n `https://vimeo.com/api/oembed.json?url=https://vimeo.com/${videoId}`,\n );\n\n if (response.ok) {\n const data = await response.json();\n setVimeoThumbnail(data.thumbnail_url);\n }\n } catch (error) {\n console.error(\"Error fetching Vimeo thumbnail:\", error);\n }\n };\n\n fetchVimeoThumbnail();\n }, [videoUrl, videoType, thumbnailUrl]);\n\n // Get the appropriate thumbnail URL\n const getThumbnailUrl = () => {\n // If a thumbnail URL is explicitly provided, use that\n if (thumbnailUrl) return thumbnailUrl;\n\n // For YouTube videos, generate thumbnail URL\n if (videoType === \"youtube\") {\n const videoId = getVideoId();\n if (videoId) {\n return `https://img.youtube.com/vi/${videoId}/hqdefault.jpg`;\n }\n }\n\n // For Vimeo videos, use the fetched thumbnail\n if (videoType === \"vimeo\" && vimeoThumbnail) {\n return vimeoThumbnail;\n }\n\n // Fall back to placeholder\n return \"/placeholder.svg?height=480&width=854\";\n };\n\n // Lock body scroll when modal is open\n useEffect(() => {\n if (isModalOpen) {\n document.body.style.overflow = \"hidden\";\n } else {\n document.body.style.overflow = \"\";\n }\n\n return () => {\n document.body.style.overflow = \"\";\n };\n }, [isModalOpen]);\n\n // Get embed URL based on video type\n const getEmbedUrl = () => {\n const videoId = getVideoId();\n // Add autoplay parameter for in-place videos\n const shouldAutoplay = isPlaying;\n\n if (videoType === \"youtube\") {\n if (videoId) {\n return `https://www.youtube.com/embed/${videoId}${shouldAutoplay ? \"?autoplay=1\" : \"\"}`;\n }\n\n // If we couldn't extract the ID but it's a YouTube URL, try to use it directly\n const hasParams = videoUrl.includes(\"?\");\n return (\n videoUrl +\n (shouldAutoplay && !hasParams\n ? \"?autoplay=1\"\n : shouldAutoplay && hasParams\n ? \"&autoplay=1\"\n : \"\")\n );\n }\n\n if (videoType === \"vimeo\" && videoId) {\n return `https://player.vimeo.com/video/${videoId}${shouldAutoplay ? \"?autoplay=1\" : \"\"}`;\n }\n\n // For self-hosted videos, return the URL as is\n return videoUrl;\n };\n\n // Calculate aspect ratio style\n const aspectRatioStyle = () => {\n if (!aspectRatio.includes(\"/\")) return {};\n\n const [width, height] = aspectRatio.split(\"/\").map(Number);\n if (!width || !height) return {};\n\n return {\n aspectRatio: `${width}/${height}`,\n };\n };\n\n // Handle thumbnail click\n const handleThumbnailClick = () => {\n if (modal) {\n setIsModalOpen(true);\n } else {\n setIsPlaying(true);\n }\n };\n\n return (\n <div className={aspectRatio === \"9/16\" ? \"max-w-xs mx-auto\" : \"\"}>\n {/* Thumbnail with play button or in-place video */}\n <div className=\"relative overflow-hidden\" style={aspectRatioStyle()}>\n {!modal && isPlaying ? (\n // In-place video player\n <div className=\"w-full h-full\">\n {videoType !== \"self-hosted\" ? (\n <iframe\n src={getEmbedUrl()}\n className=\"w-full h-full\"\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\"\n allowFullScreen\n ></iframe>\n ) : (\n <video\n src={videoUrl}\n controls\n controlsList=\"nodownload\" // eslint-disable-line react/no-unknown-property\n onContextMenu={(e) => e.preventDefault()}\n autoPlay\n className=\"w-full h-full\"\n >\n Your browser does not support the video tag.\n </video>\n )}\n </div>\n ) : (\n // Thumbnail with play button\n <div\n className=\"cursor-pointer group w-full h-full\"\n onClick={handleThumbnailClick}\n >\n <img\n src={getThumbnailUrl() || \"/placeholder.svg\"}\n alt={`Thumbnail for ${title}`}\n className=\"w-full h-full object-cover transition-transform duration-300 group-hover:scale-105\"\n />\n <div className=\"absolute inset-0 bg-black/30 flex items-center justify-center\">\n <div className=\"relative\">\n {/* Smaller grayish concentric wave animation */}\n <span className=\"absolute -inset-1.5 rounded-full bg-gray-200/50 animate-pulse duration-[3000ms]\"></span>\n\n {/* Play button */}\n <div className=\"relative z-1 rounded-full bg-primary p-4 text-primary-foreground\">\n <Play className=\"h-8 w-8\" fill=\"currentColor\" />\n </div>\n </div>\n </div>\n </div>\n )}\n </div>\n\n {/* Custom Video Modal (only used when modal is true) */}\n {modal && isModalOpen && (\n <div className=\"fixed inset-0 bg-black/95 z-50 flex items-center justify-center p-4 sm:p-6 md:p-10\">\n <button\n onClick={() => setIsModalOpen(false)}\n className=\"absolute top-2 right-2 sm:top-4 sm:right-4 text-white hover:text-gray-300 focus:outline-none z-10\"\n aria-label=\"Close\"\n >\n <X className=\"h-6 w-6 sm:h-8 sm:w-8\" />\n </button>\n\n <div\n className=\"w-full max-w-xl sm:max-w-2xl md:max-w-3xl\"\n style={aspectRatioStyle()}\n >\n {videoType !== \"self-hosted\" ? (\n // YouTube or Vimeo video\n <iframe\n src={getEmbedUrl()}\n className=\"w-full h-full\"\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\"\n allowFullScreen\n ></iframe>\n ) : (\n // Self-hosted video\n <video\n src={videoUrl}\n controls\n controlsList=\"nodownload\" // eslint-disable-line react/no-unknown-property\n onContextMenu={(e) => e.preventDefault()}\n autoPlay\n className=\"w-full h-full\"\n >\n Your browser does not support the video tag.\n </video>\n )}\n </div>\n </div>\n )}\n </div>\n );\n}\n","import { cn } from \"../primitives/lib/utils\";\n\nexport type ImageObjectFit = \"cover\" | \"contain\" | \"fill\" | \"none\" | \"scale-down\";\n\nexport const roundedMap: Record<number, string> = {\n 0: \"rounded-none\",\n 1: \"rounded-sm\",\n 2: \"rounded\",\n 3: \"rounded-md\",\n 4: \"rounded-lg\",\n 5: \"rounded-xl\",\n 6: \"rounded-2xl\",\n 7: \"rounded-3xl\",\n 8: \"rounded-full\",\n};\n\ninterface ImgProps {\n src: string;\n classes?: string;\n alt?: string;\n defaultImage?: string;\n loading?: \"eager\" | \"lazy\";\n height?: string;\n sizes?: `${string}vw`;\n width?: string;\n borderRadius?: number | string;\n noDefaultImage?: boolean;\n className?: string;\n objectFit?: ImageObjectFit;\n}\n\nconst objectFitClass: Record<ImageObjectFit, string> = {\n cover: \"object-cover\",\n contain: \"object-contain\",\n fill: \"object-fill\",\n none: \"object-none\",\n \"scale-down\": \"object-scale-down\",\n};\n\nexport const Image = (props: ImgProps) => {\n const {\n src,\n alt = \"\",\n defaultImage,\n loading = \"lazy\",\n noDefaultImage = false,\n className = \"\",\n classes = \"\",\n width = \"w-full\",\n height = \"h-auto\",\n objectFit = \"cover\",\n borderRadius,\n } = props;\n const source = src || defaultImage;\n\n const radiusClass =\n typeof borderRadius === \"number\"\n ? roundedMap[borderRadius] ?? \"rounded\"\n : borderRadius;\n\n if (!source) {\n if (noDefaultImage) return null;\n return (\n <div\n className={cn(\n \"relative aspect-video overflow-hidden bg-muted\",\n width,\n height,\n radiusClass,\n classes,\n className,\n )}\n />\n );\n }\n\n return (\n <div\n className={cn(\n \"relative aspect-video overflow-hidden\",\n width,\n height,\n radiusClass,\n classes,\n className,\n )}\n >\n <img\n src={source}\n alt={alt}\n loading={loading}\n className={cn(\n \"absolute inset-0 h-full w-full\",\n radiusClass,\n objectFitClass[objectFit],\n )}\n />\n </div>\n );\n};\n","\"use client\";\n\nimport * as React from \"react\";\n\nexport interface PageBuilderLinkProps {\n href: string;\n children?: React.ReactNode;\n className?: string;\n style?: React.CSSProperties;\n onClick?: React.MouseEventHandler<HTMLAnchorElement>;\n target?: string;\n rel?: string;\n}\n\n/**\n * Optional host-supplied link. Next.js apps can pass `next/link`; Vite / Remix\n * / plain React hosts can omit it and NavLink will render a native `<a>`.\n */\nexport type PageBuilderLinkComponent = React.ComponentType<PageBuilderLinkProps>;\n\nconst PageBuilderLinkContext = React.createContext<PageBuilderLinkComponent | null>(\n null,\n);\n\nexport function PageBuilderLinkProvider({\n component,\n children,\n}: {\n component?: PageBuilderLinkComponent | null;\n children: React.ReactNode;\n}) {\n return (\n <PageBuilderLinkContext.Provider value={component ?? null}>\n {children}\n </PageBuilderLinkContext.Provider>\n );\n}\n\nexport function usePageBuilderLinkComponent() {\n return React.useContext(PageBuilderLinkContext);\n}\n\ninterface NavLinkProps {\n href: string;\n children?: React.ReactNode;\n openInSameTab?: boolean;\n style?: React.CSSProperties;\n className?: string;\n onClick?: () => void;\n}\n\n/**\n * A navigation-aware link used inside block widgets. In-page anchors scroll\n * to the target. Internal paths (`/...`) use the host `linkComponent` when\n * one is provided (e.g. Next.js `Link`); everything else is a native `<a>`.\n */\nexport function NavLink({\n href,\n children,\n openInSameTab = true,\n style,\n className = \"\",\n onClick,\n}: NavLinkProps) {\n const LinkComponent = usePageBuilderLinkComponent();\n const isInternal = Boolean(href && href.startsWith(\"/\"));\n const isInPageNavigation = Boolean(href && href.startsWith(\"#\"));\n const target = openInSameTab ? undefined : \"_blank\";\n const rel = target === \"_blank\" ? \"noopener noreferrer\" : undefined;\n const handleClick: React.MouseEventHandler<HTMLAnchorElement> = () => {\n onClick?.();\n };\n\n if (!isInPageNavigation && isInternal && LinkComponent) {\n return (\n <LinkComponent\n href={href}\n style={style}\n className={className}\n onClick={handleClick}\n target={target}\n rel={rel}\n >\n {children}\n </LinkComponent>\n );\n }\n\n return (\n <a\n href={href}\n style={style}\n className={className}\n target={target}\n rel={rel}\n onClick={handleClick}\n >\n {children}\n </a>\n );\n}\n","\"use client\";\n\nimport React, { useEffect, useState } from \"react\";\nimport { Moon, Sun } from \"lucide-react\";\nimport { Button, type ButtonProps } from \"../primitives\";\nimport { Button as ChromeButton } from \"../primitives/components/ui/button\";\nimport type { ThemeStyle } from \"../models\";\nimport type { ResolvedColorMode } from \"../utils\";\n\n/**\n * Light / dark toggle. Public sites pass `theme` so the button uses\n * page-builder primitives; the builder chrome omits it and matches the\n * other icon buttons in the top bar.\n *\n * Preference starts as system (OS). The first click pins an explicit\n * light or dark side; later clicks flip between those two.\n */\nexport function ThemeModeSwitcher({\n resolved,\n onToggle,\n theme,\n className,\n size,\n variant,\n}: {\n resolved: ResolvedColorMode;\n onToggle: () => void;\n theme?: ThemeStyle;\n className?: string;\n size?: ButtonProps[\"size\"];\n variant?: ButtonProps[\"variant\"];\n}) {\n const [mounted, setMounted] = useState(false);\n useEffect(() => {\n setMounted(true);\n }, []);\n const painted = mounted ? resolved : \"light\";\n const next = painted === \"dark\" ? \"light\" : \"dark\";\n const label = next === \"dark\" ? \"Switch to dark mode\" : \"Switch to light mode\";\n const Icon = painted === \"dark\" ? Sun : Moon;\n\n if (theme) {\n return (\n <Button\n type=\"button\"\n variant={variant ?? \"outline\"}\n size={size ?? \"sm\"}\n theme={theme}\n title={label}\n aria-label={label}\n className={className}\n onClick={onToggle}\n >\n <Icon className=\"h-4 w-4\" />\n </Button>\n );\n }\n\n return (\n <ChromeButton\n type=\"button\"\n variant=\"outline\"\n size=\"icon\"\n title={label}\n aria-label={label}\n className={className}\n onClick={onToggle}\n >\n <Icon className=\"h-4 w-4\" />\n </ChromeButton>\n );\n}\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { DataSlot, Embed, Marquee, MediaBlock, Grid, RichText, Footer, SupportForm, EmailForm, Banner, Pricing, Faq, Featured, Content, Hero, Header } from './chunk-
|
|
3
|
-
import { PageBuilderLinkProvider } from './chunk-
|
|
4
|
-
import { cn } from './chunk-
|
|
2
|
+
import { DataSlot, Embed, Marquee, MediaBlock, Grid, RichText, Footer, SupportForm, EmailForm, Banner, Pricing, Faq, Featured, Content, Hero, Header } from './chunk-UO7XGBGM.js';
|
|
3
|
+
import { PageBuilderLinkProvider } from './chunk-H4P5MFJI.js';
|
|
4
|
+
import { cn } from './chunk-SGNRWWG3.js';
|
|
5
5
|
import React, { useRef, useId, useEffect } from 'react';
|
|
6
6
|
import { Settings2, ArrowUp, ArrowDown, Plus, Trash2 } from 'lucide-react';
|
|
7
7
|
import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
|
|
@@ -34,9 +34,7 @@ function registerBlock(widget) {
|
|
|
34
34
|
throw new Error("A page-builder block must have a non-empty name.");
|
|
35
35
|
}
|
|
36
36
|
if (blockRegistry[name]) {
|
|
37
|
-
throw new Error(
|
|
38
|
-
`A page-builder block named "${name}" is already registered.`
|
|
39
|
-
);
|
|
37
|
+
throw new Error(`A page-builder block named "${name}" is already registered.`);
|
|
40
38
|
}
|
|
41
39
|
if (NON_ADDABLE_BLOCK_NAMES.includes(name)) {
|
|
42
40
|
throw new Error(
|
|
@@ -154,30 +152,9 @@ function EditableBlock({
|
|
|
154
152
|
),
|
|
155
153
|
/* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute inset-x-0 top-0 z-20 flex justify-center opacity-0 transition-opacity group-hover/block:opacity-100", children: /* @__PURE__ */ jsxs("div", { className: "pointer-events-auto flex items-center gap-1 rounded-b-md border border-t-0 border-border bg-background px-1.5 py-1 shadow-sm", children: [
|
|
156
154
|
label && /* @__PURE__ */ jsx("span", { className: "px-1.5 text-xs font-medium text-muted-foreground", children: label }),
|
|
157
|
-
/* @__PURE__ */ jsx(
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
icon: Settings2,
|
|
161
|
-
label: "Edit settings",
|
|
162
|
-
onClick: onSelect
|
|
163
|
-
}
|
|
164
|
-
),
|
|
165
|
-
/* @__PURE__ */ jsx(
|
|
166
|
-
ToolbarButton,
|
|
167
|
-
{
|
|
168
|
-
icon: ArrowUp,
|
|
169
|
-
label: "Move up",
|
|
170
|
-
onClick: onMoveUp
|
|
171
|
-
}
|
|
172
|
-
),
|
|
173
|
-
/* @__PURE__ */ jsx(
|
|
174
|
-
ToolbarButton,
|
|
175
|
-
{
|
|
176
|
-
icon: ArrowDown,
|
|
177
|
-
label: "Move down",
|
|
178
|
-
onClick: onMoveDown
|
|
179
|
-
}
|
|
180
|
-
),
|
|
155
|
+
/* @__PURE__ */ jsx(ToolbarButton, { icon: Settings2, label: "Edit settings", onClick: onSelect }),
|
|
156
|
+
/* @__PURE__ */ jsx(ToolbarButton, { icon: ArrowUp, label: "Move up", onClick: onMoveUp }),
|
|
157
|
+
/* @__PURE__ */ jsx(ToolbarButton, { icon: ArrowDown, label: "Move down", onClick: onMoveDown }),
|
|
181
158
|
/* @__PURE__ */ jsx(DeleteButton, { onDelete })
|
|
182
159
|
] }) }),
|
|
183
160
|
children,
|
|
@@ -298,11 +275,8 @@ function PageRenderer({
|
|
|
298
275
|
}, [highlightedWidgetId, layout]);
|
|
299
276
|
const header = layout.find((widget) => widget.name === "header");
|
|
300
277
|
const footer = layout.find((widget) => widget.name === "footer");
|
|
301
|
-
const body = layout.filter(
|
|
302
|
-
(widget) => !LAYOUT_BLOCK_NAMES.includes(widget.name)
|
|
303
|
-
);
|
|
278
|
+
const body = layout.filter((widget) => !LAYOUT_BLOCK_NAMES.includes(widget.name));
|
|
304
279
|
const resolvedPageData = {
|
|
305
|
-
pageType: "site",
|
|
306
280
|
...pageData
|
|
307
281
|
};
|
|
308
282
|
function renderInstance(instance, options) {
|
|
@@ -343,9 +317,7 @@ function PageRenderer({
|
|
|
343
317
|
nextTheme,
|
|
344
318
|
themeMode,
|
|
345
319
|
setThemeMode: onThemeModeChange,
|
|
346
|
-
toggleTheme: onThemeModeChange ? () => onThemeModeChange(
|
|
347
|
-
nextTheme === "dark" ? "light" : "dark"
|
|
348
|
-
) : void 0
|
|
320
|
+
toggleTheme: onThemeModeChange ? () => onThemeModeChange(nextTheme === "dark" ? "light" : "dark") : void 0
|
|
349
321
|
}
|
|
350
322
|
)
|
|
351
323
|
},
|
|
@@ -389,5 +361,5 @@ function PageRenderer({
|
|
|
389
361
|
}
|
|
390
362
|
|
|
391
363
|
export { EditableBlock, LAYOUT_BLOCK_NAMES, NON_ADDABLE_BLOCK_NAMES, PageRenderer, blockRegistry, generateThemeStyles, getAddableBlocks, getWidget, registerBlock };
|
|
392
|
-
//# sourceMappingURL=chunk-
|
|
393
|
-
//# sourceMappingURL=chunk-
|
|
364
|
+
//# sourceMappingURL=chunk-HDINUNI2.js.map
|
|
365
|
+
//# sourceMappingURL=chunk-HDINUNI2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/registry.ts","../src/renderer/editable-block.tsx","../src/renderer/generate-theme-styles.ts","../src/renderer/page-renderer.tsx"],"names":["jsx","jsxs"],"mappings":"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","file":"chunk-HDINUNI2.js","sourcesContent":["import type { Widget } from \"./models\";\nimport {\n Header,\n Hero,\n Content,\n Featured,\n Faq,\n Pricing,\n Banner,\n EmailForm,\n SupportForm,\n Footer,\n RichText,\n Grid,\n MediaBlock,\n Marquee,\n Embed,\n DataSlot,\n} from \"./blocks\";\n\n/**\n * Every block available to the page builder, keyed by its\n * `metadata.name`. `header` and `footer` are singleton, layout-level\n * blocks placed outside the normal flow by the renderer.\n */\nexport const blockRegistry: Record<string, Widget<any>> = {\n header: Header,\n hero: Hero,\n content: Content,\n featured: Featured,\n faq: Faq,\n pricing: Pricing,\n banner: Banner,\n \"email-form\": EmailForm,\n \"support-form\": SupportForm,\n footer: Footer,\n \"rich-text\": RichText,\n grid: Grid,\n media: MediaBlock,\n marquee: Marquee,\n embed: Embed,\n \"data-slot\": DataSlot,\n};\n\nexport const LAYOUT_BLOCK_NAMES = [\"header\", \"footer\"];\nexport const NON_ADDABLE_BLOCK_NAMES = [...LAYOUT_BLOCK_NAMES, \"data-slot\"];\n\n/**\n * Adds a host application's block to the shared renderer and builder\n * registry. Register blocks while the application module initializes, before\n * rendering either `PageBuilder` or `PageRenderer`.\n *\n * Built-in names are intentionally protected: a persisted page must always\n * have one stable implementation for a block name. Give host blocks a\n * namespaced name such as `acme-event-list`.\n */\nexport function registerBlock(widget: Widget<any>): void {\n const name = widget.metadata.name.trim();\n if (!name) {\n throw new Error(\"A page-builder block must have a non-empty name.\");\n }\n if (blockRegistry[name]) {\n throw new Error(`A page-builder block named \\\"${name}\\\" is already registered.`);\n }\n if (NON_ADDABLE_BLOCK_NAMES.includes(name)) {\n throw new Error(\n `\\\"${name}\\\" is a reserved page-builder block name and cannot be registered by a host application.`,\n );\n }\n blockRegistry[name] = widget;\n}\n\nexport function getWidget(name: string): Widget<any> | undefined {\n return blockRegistry[name];\n}\n\n/** Blocks a user can add to the body of a page. */\nexport function getAddableBlocks(): Widget<any>[] {\n return Object.values(blockRegistry).filter(\n (widget) => !NON_ADDABLE_BLOCK_NAMES.includes(widget.metadata.name),\n );\n}\n","\"use client\";\n\nimport React from \"react\";\nimport { ArrowDown, ArrowUp, Plus, Settings2, Trash2 } from \"lucide-react\";\nimport { cn } from \"../primitives/lib/utils\";\n\nexport interface EditableBlockProps {\n children: React.ReactNode;\n editing: boolean;\n /** Ref to the wrapper element so the canvas can scroll the block into view. */\n containerRef?: React.Ref<HTMLDivElement>;\n selected?: boolean;\n /** Draws a temporary attention flash, e.g. right after the block was moved. */\n highlighted?: boolean;\n label?: string;\n onSelect?: () => void;\n onMoveUp?: () => void;\n onMoveDown?: () => void;\n onDelete?: () => void;\n onAddBelow?: () => void;\n}\n\nfunction ToolbarButton({\n icon: Icon,\n label,\n onClick,\n}: {\n icon: React.ComponentType<{ className?: string }>;\n label: string;\n onClick?: () => void;\n}) {\n if (!onClick) return null;\n return (\n <button\n type=\"button\"\n title={label}\n aria-label={label}\n className=\"inline-flex h-7 w-7 items-center justify-center rounded bg-background text-foreground shadow-sm hover:bg-muted\"\n onClick={(e) => {\n e.stopPropagation();\n onClick();\n }}\n >\n <Icon className=\"h-3.5 w-3.5\" />\n </button>\n );\n}\n\n/**\n * Two-click delete: first click arms the button (\"Sure?\"), second click\n * actually deletes. Disarms after a short delay or when the pointer leaves.\n */\nfunction DeleteButton({ onDelete }: { onDelete?: () => void }) {\n const [confirming, setConfirming] = React.useState(false);\n\n React.useEffect(() => {\n if (!confirming) return;\n const timeout = setTimeout(() => setConfirming(false), 3000);\n return () => clearTimeout(timeout);\n }, [confirming]);\n\n if (!onDelete) return null;\n\n if (confirming) {\n return (\n <button\n type=\"button\"\n title=\"Confirm delete\"\n aria-label=\"Confirm delete\"\n className=\"inline-flex h-7 items-center justify-center rounded bg-destructive px-2 text-xs font-medium text-destructive-foreground shadow-sm hover:bg-destructive/90\"\n onMouseLeave={() => setConfirming(false)}\n onClick={(e) => {\n e.stopPropagation();\n setConfirming(false);\n onDelete();\n }}\n >\n Sure?\n </button>\n );\n }\n\n return (\n <button\n type=\"button\"\n title=\"Delete\"\n aria-label=\"Delete\"\n className=\"inline-flex h-7 w-7 items-center justify-center rounded bg-background text-foreground shadow-sm hover:bg-muted\"\n onClick={(e) => {\n e.stopPropagation();\n setConfirming(true);\n }}\n >\n <Trash2 className=\"h-3.5 w-3.5\" />\n </button>\n );\n}\n\n/**\n * Wraps a rendered block with hover/selection affordances when the page\n * builder is in editing mode. In non-editing (plain display) mode it's a\n * transparent passthrough.\n */\nexport function EditableBlock({\n children,\n editing,\n containerRef,\n selected,\n highlighted,\n label,\n onSelect,\n onMoveUp,\n onMoveDown,\n onDelete,\n onAddBelow,\n}: EditableBlockProps) {\n if (!editing) {\n return <>{children}</>;\n }\n\n return (\n <div\n ref={containerRef}\n className={cn(\n \"group/block relative outline outline-1 outline-transparent transition-colors hover:outline-primary/40\",\n selected && \"outline-2 outline-primary\",\n highlighted && \"outline-2 outline-primary/70\",\n )}\n onClick={onSelect}\n >\n <div\n aria-hidden\n className={cn(\n \"pointer-events-none absolute inset-0 z-10 bg-primary/10 opacity-0 transition-opacity duration-500\",\n highlighted && \"opacity-100\",\n )}\n />\n <div className=\"pointer-events-none absolute inset-x-0 top-0 z-20 flex justify-center opacity-0 transition-opacity group-hover/block:opacity-100\">\n <div className=\"pointer-events-auto flex items-center gap-1 rounded-b-md border border-t-0 border-border bg-background px-1.5 py-1 shadow-sm\">\n {label && (\n <span className=\"px-1.5 text-xs font-medium text-muted-foreground\">\n {label}\n </span>\n )}\n <ToolbarButton icon={Settings2} label=\"Edit settings\" onClick={onSelect} />\n <ToolbarButton icon={ArrowUp} label=\"Move up\" onClick={onMoveUp} />\n <ToolbarButton icon={ArrowDown} label=\"Move down\" onClick={onMoveDown} />\n <DeleteButton onDelete={onDelete} />\n </div>\n </div>\n {children}\n {onAddBelow && (\n <div className=\"pointer-events-none absolute inset-x-0 -bottom-3 z-20 flex justify-center opacity-0 transition-opacity group-hover/block:opacity-100\">\n <button\n type=\"button\"\n title=\"Add block below\"\n aria-label=\"Add block below\"\n className=\"pointer-events-auto inline-flex h-6 w-6 items-center justify-center rounded-full border border-border bg-background text-foreground shadow-sm hover:bg-primary hover:text-primary-foreground\"\n onClick={(e) => {\n e.stopPropagation();\n onAddBelow();\n }}\n >\n <Plus className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n )}\n </div>\n );\n}\n","import convert, { HSL } from \"color-convert\";\nimport type { Theme } from \"../models\";\n\nfunction formatHSL(hsl: HSL): string {\n return `${hsl[0]} ${hsl[1]}% ${hsl[2]}%`;\n}\n\nfunction cssColorFromHex(hex?: string): string {\n if (!hex) return \"\";\n try {\n return `hsl(${formatHSL(convert.hex.hsl(hex.replace(\"#\", \"\")))})`;\n } catch {\n return \"\";\n }\n}\n\n/**\n * Generates full CSS color values for the custom properties every primitive\n * reads. Full colors (rather than bare HSL channels) let the renderer coexist\n * with Tailwind v4 hosts whose semantic utilities use `var(--primary)` while\n * still accepting CourseLit-derived hex theme data.\n *\n * Callers rendering more than one theme in a document should pass a unique\n * selector so one renderer cannot overwrite another renderer's variables.\n */\nexport function generateThemeStyles(theme: Theme, selector = \".pb-theme\"): string {\n const colors = theme?.theme?.colors;\n if (!colors) {\n return \"\";\n }\n\n const { light, dark } = colors;\n if (!light || !dark) {\n return \"\";\n }\n\n const block = (c: typeof light) => `\n --background: ${cssColorFromHex(c.background)};\n --foreground: ${cssColorFromHex(c.foreground)};\n --card: ${cssColorFromHex(c.card)};\n --card-foreground: ${cssColorFromHex(c.cardForeground)};\n --popover: ${cssColorFromHex(c.popover)};\n --popover-foreground: ${cssColorFromHex(c.popoverForeground)};\n --primary: ${cssColorFromHex(c.primary)};\n --primary-foreground: ${cssColorFromHex(c.primaryForeground)};\n --secondary: ${cssColorFromHex(c.secondary)};\n --secondary-foreground: ${cssColorFromHex(c.secondaryForeground)};\n --muted: ${cssColorFromHex(c.muted)};\n --muted-foreground: ${cssColorFromHex(c.mutedForeground)};\n --accent: ${cssColorFromHex(c.accent)};\n --accent-foreground: ${cssColorFromHex(c.accentForeground)};\n --destructive: ${cssColorFromHex(c.destructive)};\n --border: ${cssColorFromHex(c.border)};\n --input: ${cssColorFromHex(c.input)};\n --ring: ${cssColorFromHex(c.ring)};\n --chart-1: ${cssColorFromHex(c.chart1)};\n --chart-2: ${cssColorFromHex(c.chart2)};\n --chart-3: ${cssColorFromHex(c.chart3)};\n --chart-4: ${cssColorFromHex(c.chart4)};\n --chart-5: ${cssColorFromHex(c.chart5)};\n --sidebar-background: ${cssColorFromHex(c.sidebar)};\n --sidebar-foreground: ${cssColorFromHex(c.sidebarForeground)};\n --sidebar-primary: ${cssColorFromHex(c.sidebarPrimary)};\n --sidebar-primary-foreground: ${cssColorFromHex(c.sidebarPrimaryForeground)};\n --sidebar-accent: ${cssColorFromHex(c.sidebarAccent)};\n --sidebar-accent-foreground: ${cssColorFromHex(c.sidebarAccentForeground)};\n --sidebar-border: ${cssColorFromHex(c.sidebarBorder)};\n --sidebar-ring: ${cssColorFromHex(c.sidebarRing)};\n --shadow-2xs: ${c.shadow2xs ?? \"\"};\n --shadow-xs: ${c.shadowXs ?? \"\"};\n --shadow-sm: ${c.shadowSm ?? \"\"};\n --shadow-md: ${c.shadowMd ?? \"\"};\n --shadow-lg: ${c.shadowLg ?? \"\"};\n --shadow-xl: ${c.shadowXl ?? \"\"};\n --shadow-2xl: ${c.shadow2xl ?? \"\"};\n `;\n\n return `\n ${selector} {\n ${block(light)}\n }\n ${selector}:is(.dark, .dark *) {\n ${block(dark)}\n }\n `;\n}\n","\"use client\";\n\nimport React, { useEffect, useId, useRef } from \"react\";\nimport type { PageData, Theme, WidgetInstance } from \"../models\";\nimport { getWidget, LAYOUT_BLOCK_NAMES } from \"../registry\";\nimport { EditableBlock } from \"./editable-block\";\nimport { generateThemeStyles } from \"./generate-theme-styles\";\nimport { cn } from \"../primitives/lib/utils\";\nimport {\n PageBuilderLinkProvider,\n type PageBuilderLinkComponent,\n} from \"../components/nav-link\";\n\nexport interface PageRendererProps {\n layout: WidgetInstance[];\n theme: Theme;\n pageData?: PageData;\n /** Optional content rendered between the shared header and footer. */\n children?: React.ReactNode;\n /** Host-resolved data keyed by a generic data-slot's configured name. */\n dataSlots?: Record<string, React.ReactNode>;\n /** Resolved palette currently painted (`system` is already applied). */\n nextTheme?: \"light\" | \"dark\";\n /** Visitor or preview preference, including follow-the-OS. */\n themeMode?: \"light\" | \"dark\" | \"system\";\n onThemeModeChange?: (mode: \"light\" | \"dark\" | \"system\") => void;\n /** When true, renders hover controls (edit/move/delete/add) for use inside the builder canvas. */\n editing?: boolean;\n selectedWidgetId?: string;\n /** Widget to briefly highlight, e.g. right after it was moved. */\n highlightedWidgetId?: string;\n onSelectWidget?: (widgetId: string) => void;\n onMoveWidgetUp?: (widgetId: string) => void;\n onMoveWidgetDown?: (widgetId: string) => void;\n onDeleteWidget?: (widgetId: string) => void;\n onAddWidgetBelow?: (widgetId: string) => void;\n /** Injects the theme's generated CSS custom properties as a `<style>` tag. Disable if the host app renders it elsewhere. */\n injectThemeStyles?: boolean;\n /**\n * Optional host link (e.g. Next.js `Link`) used for internal `/...` paths\n * inside block widgets. Omit it to render native `<a>` tags — no\n * framework dependency.\n */\n linkComponent?: PageBuilderLinkComponent;\n className?: string;\n}\n\n/**\n * Renders a page's widget layout using the block registry. Used both for\n * plain (non-editable) display and, with `editing`, as the live canvas\n * inside the page builder.\n */\nexport function PageRenderer({\n layout,\n theme,\n pageData,\n children,\n dataSlots,\n nextTheme = \"light\",\n themeMode = \"light\",\n onThemeModeChange,\n editing = false,\n selectedWidgetId,\n highlightedWidgetId,\n onSelectWidget,\n onMoveWidgetUp,\n onMoveWidgetDown,\n onDeleteWidget,\n onAddWidgetBelow,\n injectThemeStyles = true,\n linkComponent,\n className,\n}: PageRendererProps) {\n // Refs to each block's wrapper, keyed by widget id. Refs (unlike DOM\n // attribute lookups) stay correct even when SSR and client render\n // different generated widget ids.\n const blockRefs = useRef(new Map<string, HTMLDivElement>());\n const themeScope = `pb-${useId().replace(/[^a-zA-Z0-9_-]/g, \"\")}`;\n const themeSelector = `[data-pb-theme=\"${themeScope}\"]`;\n\n // Keep the highlighted (just-moved) block in view. Keyed on `layout` too\n // so repeated move clicks on the same widget re-scroll as it travels.\n useEffect(() => {\n if (!highlightedWidgetId) return;\n blockRefs.current\n .get(highlightedWidgetId)\n ?.scrollIntoView({ behavior: \"smooth\", block: \"nearest\" });\n }, [highlightedWidgetId, layout]);\n\n const header = layout.find((widget) => widget.name === \"header\");\n const footer = layout.find((widget) => widget.name === \"footer\");\n const body = layout.filter((widget) => !LAYOUT_BLOCK_NAMES.includes(widget.name));\n\n const resolvedPageData: PageData = {\n ...pageData,\n };\n\n function renderInstance(\n instance: WidgetInstance,\n options: {\n allowUp: boolean;\n allowDown: boolean;\n locked: boolean;\n allowAddBelow: boolean;\n },\n ) {\n const definition = getWidget(instance.name);\n if (!definition) return null;\n\n const Widget = definition.widget;\n const settings = {\n ...definition.getDefaultSettings?.(),\n ...instance.settings,\n };\n\n return (\n <EditableBlock\n key={instance.widgetId}\n editing={editing}\n containerRef={(node) => {\n if (node) {\n blockRefs.current.set(instance.widgetId, node);\n } else {\n blockRefs.current.delete(instance.widgetId);\n }\n }}\n selected={selectedWidgetId === instance.widgetId}\n highlighted={highlightedWidgetId === instance.widgetId}\n label={definition.metadata.displayName}\n onSelect={() => onSelectWidget?.(instance.widgetId)}\n onMoveUp={\n options.allowUp ? () => onMoveWidgetUp?.(instance.widgetId) : undefined\n }\n onMoveDown={\n options.allowDown ? () => onMoveWidgetDown?.(instance.widgetId) : undefined\n }\n onDelete={\n !options.locked ? () => onDeleteWidget?.(instance.widgetId) : undefined\n }\n onAddBelow={\n options.allowAddBelow && onAddWidgetBelow\n ? () => onAddWidgetBelow(instance.widgetId)\n : undefined\n }\n >\n <Widget\n settings={settings as any}\n state={{ theme }}\n pageData={resolvedPageData}\n data={\n instance.name === \"data-slot\"\n ? dataSlots?.[String(settings.slot ?? \"\")]\n : undefined\n }\n editing={editing}\n nextTheme={nextTheme}\n themeMode={themeMode}\n setThemeMode={onThemeModeChange}\n toggleTheme={\n onThemeModeChange\n ? () => onThemeModeChange(nextTheme === \"dark\" ? \"light\" : \"dark\")\n : undefined\n }\n />\n </EditableBlock>\n );\n }\n\n return (\n <PageBuilderLinkProvider component={linkComponent}>\n <div\n data-pb-theme={themeScope}\n className={cn(\n \"pb-theme flex min-h-full flex-col bg-background text-foreground\",\n nextTheme === \"dark\" && \"dark\",\n className,\n )}\n >\n {header &&\n renderInstance(header, {\n allowUp: false,\n allowDown: false,\n locked: true,\n allowAddBelow: true,\n })}\n <div className=\"flex flex-1 flex-col\">\n {children !== undefined &&\n !body.some((instance) => instance.name === \"data-slot\")\n ? children\n : body.map((instance, index) =>\n renderInstance(instance, {\n allowUp: instance.moveable && index !== 0,\n allowDown: instance.moveable && index !== body.length - 1,\n locked: !instance.deletable,\n allowAddBelow: true,\n }),\n )}\n </div>\n {footer &&\n renderInstance(footer, {\n allowUp: false,\n allowDown: false,\n locked: true,\n allowAddBelow: false,\n })}\n {injectThemeStyles && (\n <style>{generateThemeStyles(theme, themeSelector)}</style>\n )}\n </div>\n </PageBuilderLinkProvider>\n );\n}\n"]}
|