@actuate-media/cms-admin 0.6.0 → 0.7.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/dist/AdminRoot.d.ts.map +1 -1
- package/dist/AdminRoot.js +13 -0
- package/dist/AdminRoot.js.map +1 -1
- package/dist/actuate-admin.css +1 -1
- package/dist/components/ErrorBoundary.js +1 -1
- package/dist/components/ErrorBoundary.js.map +1 -1
- package/dist/hooks/useBuilderState.d.ts +49 -0
- package/dist/hooks/useBuilderState.d.ts.map +1 -0
- package/dist/hooks/useBuilderState.js +238 -0
- package/dist/hooks/useBuilderState.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/layout/Sidebar.d.ts.map +1 -1
- package/dist/layout/Sidebar.js +2 -2
- package/dist/layout/Sidebar.js.map +1 -1
- package/dist/views/page-builder/AIBlockAssist.d.ts +9 -0
- package/dist/views/page-builder/AIBlockAssist.d.ts.map +1 -0
- package/dist/views/page-builder/AIBlockAssist.js +40 -0
- package/dist/views/page-builder/AIBlockAssist.js.map +1 -0
- package/dist/views/page-builder/AIGenerateDialog.d.ts +8 -0
- package/dist/views/page-builder/AIGenerateDialog.d.ts.map +1 -0
- package/dist/views/page-builder/AIGenerateDialog.js +170 -0
- package/dist/views/page-builder/AIGenerateDialog.js.map +1 -0
- package/dist/views/page-builder/BlockEditor.d.ts +11 -0
- package/dist/views/page-builder/BlockEditor.d.ts.map +1 -0
- package/dist/views/page-builder/BlockEditor.js +67 -0
- package/dist/views/page-builder/BlockEditor.js.map +1 -0
- package/dist/views/page-builder/BlockPicker.d.ts +7 -0
- package/dist/views/page-builder/BlockPicker.d.ts.map +1 -0
- package/dist/views/page-builder/BlockPicker.js +102 -0
- package/dist/views/page-builder/BlockPicker.js.map +1 -0
- package/dist/views/page-builder/BottomBar.d.ts +9 -0
- package/dist/views/page-builder/BottomBar.d.ts.map +1 -0
- package/dist/views/page-builder/BottomBar.js +13 -0
- package/dist/views/page-builder/BottomBar.js.map +1 -0
- package/dist/views/page-builder/BuilderToolbar.d.ts +21 -0
- package/dist/views/page-builder/BuilderToolbar.d.ts.map +1 -0
- package/dist/views/page-builder/BuilderToolbar.js +18 -0
- package/dist/views/page-builder/BuilderToolbar.js.map +1 -0
- package/dist/views/page-builder/ContextPanel.d.ts +20 -0
- package/dist/views/page-builder/ContextPanel.d.ts.map +1 -0
- package/dist/views/page-builder/ContextPanel.js +40 -0
- package/dist/views/page-builder/ContextPanel.js.map +1 -0
- package/dist/views/page-builder/DesignScore.d.ts +6 -0
- package/dist/views/page-builder/DesignScore.d.ts.map +1 -0
- package/dist/views/page-builder/DesignScore.js +93 -0
- package/dist/views/page-builder/DesignScore.js.map +1 -0
- package/dist/views/page-builder/NodeSettings.d.ts +12 -0
- package/dist/views/page-builder/NodeSettings.d.ts.map +1 -0
- package/dist/views/page-builder/NodeSettings.js +80 -0
- package/dist/views/page-builder/NodeSettings.js.map +1 -0
- package/dist/views/page-builder/PageBuilder.d.ts +8 -0
- package/dist/views/page-builder/PageBuilder.d.ts.map +1 -0
- package/dist/views/page-builder/PageBuilder.js +126 -0
- package/dist/views/page-builder/PageBuilder.js.map +1 -0
- package/dist/views/page-builder/PageSettings.d.ts +7 -0
- package/dist/views/page-builder/PageSettings.d.ts.map +1 -0
- package/dist/views/page-builder/PageSettings.js +27 -0
- package/dist/views/page-builder/PageSettings.js.map +1 -0
- package/dist/views/page-builder/SEOPanel.d.ts +10 -0
- package/dist/views/page-builder/SEOPanel.d.ts.map +1 -0
- package/dist/views/page-builder/SEOPanel.js +105 -0
- package/dist/views/page-builder/SEOPanel.js.map +1 -0
- package/dist/views/page-builder/SavedSections.d.ts +6 -0
- package/dist/views/page-builder/SavedSections.d.ts.map +1 -0
- package/dist/views/page-builder/SavedSections.js +145 -0
- package/dist/views/page-builder/SavedSections.js.map +1 -0
- package/dist/views/page-builder/TemplatePicker.d.ts +7 -0
- package/dist/views/page-builder/TemplatePicker.d.ts.map +1 -0
- package/dist/views/page-builder/TemplatePicker.js +68 -0
- package/dist/views/page-builder/TemplatePicker.js.map +1 -0
- package/dist/views/page-builder/block-renderers/CTAPreview.d.ts +3 -0
- package/dist/views/page-builder/block-renderers/CTAPreview.d.ts.map +1 -0
- package/dist/views/page-builder/block-renderers/CTAPreview.js +19 -0
- package/dist/views/page-builder/block-renderers/CTAPreview.js.map +1 -0
- package/dist/views/page-builder/block-renderers/CardsPreview.d.ts +3 -0
- package/dist/views/page-builder/block-renderers/CardsPreview.d.ts.map +1 -0
- package/dist/views/page-builder/block-renderers/CardsPreview.js +22 -0
- package/dist/views/page-builder/block-renderers/CardsPreview.js.map +1 -0
- package/dist/views/page-builder/block-renderers/CodePreview.d.ts +3 -0
- package/dist/views/page-builder/block-renderers/CodePreview.d.ts.map +1 -0
- package/dist/views/page-builder/block-renderers/CodePreview.js +16 -0
- package/dist/views/page-builder/block-renderers/CodePreview.js.map +1 -0
- package/dist/views/page-builder/block-renderers/FAQPreview.d.ts +3 -0
- package/dist/views/page-builder/block-renderers/FAQPreview.d.ts.map +1 -0
- package/dist/views/page-builder/block-renderers/FAQPreview.js +24 -0
- package/dist/views/page-builder/block-renderers/FAQPreview.js.map +1 -0
- package/dist/views/page-builder/block-renderers/FallbackPreview.d.ts +6 -0
- package/dist/views/page-builder/block-renderers/FallbackPreview.d.ts.map +1 -0
- package/dist/views/page-builder/block-renderers/FallbackPreview.js +7 -0
- package/dist/views/page-builder/block-renderers/FallbackPreview.js.map +1 -0
- package/dist/views/page-builder/block-renderers/FormPreview.d.ts +3 -0
- package/dist/views/page-builder/block-renderers/FormPreview.d.ts.map +1 -0
- package/dist/views/page-builder/block-renderers/FormPreview.js +14 -0
- package/dist/views/page-builder/block-renderers/FormPreview.js.map +1 -0
- package/dist/views/page-builder/block-renderers/GalleryPreview.d.ts +3 -0
- package/dist/views/page-builder/block-renderers/GalleryPreview.d.ts.map +1 -0
- package/dist/views/page-builder/block-renderers/GalleryPreview.js +21 -0
- package/dist/views/page-builder/block-renderers/GalleryPreview.js.map +1 -0
- package/dist/views/page-builder/block-renderers/HeroPreview.d.ts +3 -0
- package/dist/views/page-builder/block-renderers/HeroPreview.d.ts.map +1 -0
- package/dist/views/page-builder/block-renderers/HeroPreview.js +19 -0
- package/dist/views/page-builder/block-renderers/HeroPreview.js.map +1 -0
- package/dist/views/page-builder/block-renderers/ImagePreview.d.ts +3 -0
- package/dist/views/page-builder/block-renderers/ImagePreview.d.ts.map +1 -0
- package/dist/views/page-builder/block-renderers/ImagePreview.js +17 -0
- package/dist/views/page-builder/block-renderers/ImagePreview.js.map +1 -0
- package/dist/views/page-builder/block-renderers/TextPreview.d.ts +3 -0
- package/dist/views/page-builder/block-renderers/TextPreview.d.ts.map +1 -0
- package/dist/views/page-builder/block-renderers/TextPreview.js +26 -0
- package/dist/views/page-builder/block-renderers/TextPreview.js.map +1 -0
- package/dist/views/page-builder/block-renderers/VideoPreview.d.ts +3 -0
- package/dist/views/page-builder/block-renderers/VideoPreview.d.ts.map +1 -0
- package/dist/views/page-builder/block-renderers/VideoPreview.js +21 -0
- package/dist/views/page-builder/block-renderers/VideoPreview.js.map +1 -0
- package/dist/views/page-builder/block-renderers/index.d.ts +9 -0
- package/dist/views/page-builder/block-renderers/index.d.ts.map +1 -0
- package/dist/views/page-builder/block-renderers/index.js +25 -0
- package/dist/views/page-builder/block-renderers/index.js.map +1 -0
- package/dist/views/page-builder/canvas/BlockRenderer.d.ts +8 -0
- package/dist/views/page-builder/canvas/BlockRenderer.d.ts.map +1 -0
- package/dist/views/page-builder/canvas/BlockRenderer.js +30 -0
- package/dist/views/page-builder/canvas/BlockRenderer.js.map +1 -0
- package/dist/views/page-builder/canvas/BuilderCanvas.d.ts +10 -0
- package/dist/views/page-builder/canvas/BuilderCanvas.d.ts.map +1 -0
- package/dist/views/page-builder/canvas/BuilderCanvas.js +26 -0
- package/dist/views/page-builder/canvas/BuilderCanvas.js.map +1 -0
- package/dist/views/page-builder/canvas/ColumnRenderer.d.ts +8 -0
- package/dist/views/page-builder/canvas/ColumnRenderer.d.ts.map +1 -0
- package/dist/views/page-builder/canvas/ColumnRenderer.js +36 -0
- package/dist/views/page-builder/canvas/ColumnRenderer.js.map +1 -0
- package/dist/views/page-builder/canvas/ContainerRenderer.d.ts +8 -0
- package/dist/views/page-builder/canvas/ContainerRenderer.d.ts.map +1 -0
- package/dist/views/page-builder/canvas/ContainerRenderer.js +33 -0
- package/dist/views/page-builder/canvas/ContainerRenderer.js.map +1 -0
- package/dist/views/page-builder/canvas/RowRenderer.d.ts +8 -0
- package/dist/views/page-builder/canvas/RowRenderer.d.ts.map +1 -0
- package/dist/views/page-builder/canvas/RowRenderer.js +32 -0
- package/dist/views/page-builder/canvas/RowRenderer.js.map +1 -0
- package/dist/views/page-builder/canvas/SectionRenderer.d.ts +8 -0
- package/dist/views/page-builder/canvas/SectionRenderer.d.ts.map +1 -0
- package/dist/views/page-builder/canvas/SectionRenderer.js +54 -0
- package/dist/views/page-builder/canvas/SectionRenderer.js.map +1 -0
- package/dist/views/page-builder/canvas/index.d.ts +3 -0
- package/dist/views/page-builder/canvas/index.d.ts.map +1 -0
- package/dist/views/page-builder/canvas/index.js +2 -0
- package/dist/views/page-builder/canvas/index.js.map +1 -0
- package/package.json +3 -2
- package/src/AdminRoot.tsx +16 -0
- package/src/components/ErrorBoundary.tsx +3 -3
- package/src/hooks/useBuilderState.ts +328 -0
- package/src/index.ts +4 -0
- package/src/layout/Sidebar.tsx +5 -0
- package/src/views/page-builder/AIBlockAssist.tsx +68 -0
- package/src/views/page-builder/AIGenerateDialog.tsx +574 -0
- package/src/views/page-builder/BlockEditor.tsx +352 -0
- package/src/views/page-builder/BlockPicker.tsx +338 -0
- package/src/views/page-builder/BottomBar.tsx +64 -0
- package/src/views/page-builder/BuilderToolbar.tsx +218 -0
- package/src/views/page-builder/ContextPanel.tsx +145 -0
- package/src/views/page-builder/DesignScore.tsx +258 -0
- package/src/views/page-builder/NodeSettings.tsx +515 -0
- package/src/views/page-builder/PageBuilder.tsx +288 -0
- package/src/views/page-builder/PageSettings.tsx +161 -0
- package/src/views/page-builder/SEOPanel.tsx +485 -0
- package/src/views/page-builder/SavedSections.tsx +486 -0
- package/src/views/page-builder/TemplatePicker.tsx +201 -0
- package/src/views/page-builder/block-renderers/CTAPreview.tsx +81 -0
- package/src/views/page-builder/block-renderers/CardsPreview.tsx +71 -0
- package/src/views/page-builder/block-renderers/CodePreview.tsx +46 -0
- package/src/views/page-builder/block-renderers/FAQPreview.tsx +90 -0
- package/src/views/page-builder/block-renderers/FallbackPreview.tsx +18 -0
- package/src/views/page-builder/block-renderers/FormPreview.tsx +69 -0
- package/src/views/page-builder/block-renderers/GalleryPreview.tsx +93 -0
- package/src/views/page-builder/block-renderers/HeroPreview.tsx +103 -0
- package/src/views/page-builder/block-renderers/ImagePreview.tsx +54 -0
- package/src/views/page-builder/block-renderers/TextPreview.tsx +81 -0
- package/src/views/page-builder/block-renderers/VideoPreview.tsx +78 -0
- package/src/views/page-builder/block-renderers/index.ts +34 -0
- package/src/views/page-builder/canvas/BlockRenderer.tsx +62 -0
- package/src/views/page-builder/canvas/BuilderCanvas.tsx +90 -0
- package/src/views/page-builder/canvas/ColumnRenderer.tsx +86 -0
- package/src/views/page-builder/canvas/ContainerRenderer.tsx +71 -0
- package/src/views/page-builder/canvas/RowRenderer.tsx +72 -0
- package/src/views/page-builder/canvas/SectionRenderer.tsx +97 -0
- package/src/views/page-builder/canvas/index.ts +2 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import type { BlockPreviewProps } from './index.js';
|
|
4
|
+
|
|
5
|
+
export function CTAPreview({ data, variant = 'banner' }: BlockPreviewProps) {
|
|
6
|
+
const heading = (data.heading as string) || '';
|
|
7
|
+
const body = (data.body as string) || '';
|
|
8
|
+
const buttonText = (data.buttonText as string) || '';
|
|
9
|
+
|
|
10
|
+
const button = (
|
|
11
|
+
<span className="inline-block rounded-md bg-primary px-4 py-2 text-sm text-primary-foreground">
|
|
12
|
+
{buttonText || 'Click Here'}
|
|
13
|
+
</span>
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
if (variant === 'inline') {
|
|
17
|
+
return (
|
|
18
|
+
<div className="flex items-center justify-between gap-4 rounded-md border border-border p-4">
|
|
19
|
+
<div className="flex-1">
|
|
20
|
+
<p className="text-sm font-medium text-foreground">
|
|
21
|
+
{heading || <span className="text-muted-foreground">CTA Heading</span>}
|
|
22
|
+
</p>
|
|
23
|
+
{body && (
|
|
24
|
+
<p className="mt-1 text-xs text-muted-foreground">{body}</p>
|
|
25
|
+
)}
|
|
26
|
+
</div>
|
|
27
|
+
{button}
|
|
28
|
+
</div>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (variant === 'floating') {
|
|
33
|
+
return (
|
|
34
|
+
<div className="mx-auto max-w-md rounded-lg border border-border bg-card p-6 shadow-md">
|
|
35
|
+
<div className="text-center">
|
|
36
|
+
<h3 className="text-lg font-medium text-foreground">
|
|
37
|
+
{heading || <span className="text-muted-foreground">CTA Heading</span>}
|
|
38
|
+
</h3>
|
|
39
|
+
{(body || !heading) && (
|
|
40
|
+
<p className="mt-2 text-sm text-muted-foreground">
|
|
41
|
+
{body || 'Supporting text for the call to action'}
|
|
42
|
+
</p>
|
|
43
|
+
)}
|
|
44
|
+
<div className="mt-4">{button}</div>
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (variant === 'split') {
|
|
51
|
+
return (
|
|
52
|
+
<div className="flex items-center gap-6 rounded-md border border-border p-6">
|
|
53
|
+
<div className="flex-1">
|
|
54
|
+
<h3 className="text-lg font-medium text-foreground">
|
|
55
|
+
{heading || <span className="text-muted-foreground">CTA Heading</span>}
|
|
56
|
+
</h3>
|
|
57
|
+
{(body || !heading) && (
|
|
58
|
+
<p className="mt-2 text-sm text-muted-foreground">
|
|
59
|
+
{body || 'Supporting text for the call to action'}
|
|
60
|
+
</p>
|
|
61
|
+
)}
|
|
62
|
+
</div>
|
|
63
|
+
<div className="shrink-0">{button}</div>
|
|
64
|
+
</div>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<div className="rounded-md border border-border bg-accent p-6 text-center">
|
|
70
|
+
<h3 className="text-lg font-medium text-foreground">
|
|
71
|
+
{heading || <span className="text-muted-foreground">CTA Heading</span>}
|
|
72
|
+
</h3>
|
|
73
|
+
{(body || !heading) && (
|
|
74
|
+
<p className="mt-2 text-sm text-muted-foreground">
|
|
75
|
+
{body || 'Supporting text for the call to action'}
|
|
76
|
+
</p>
|
|
77
|
+
)}
|
|
78
|
+
<div className="mt-4">{button}</div>
|
|
79
|
+
</div>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Image } from 'lucide-react';
|
|
4
|
+
import type { BlockPreviewProps } from './index.js';
|
|
5
|
+
|
|
6
|
+
interface CardItem {
|
|
7
|
+
title?: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
image?: string;
|
|
10
|
+
link?: string;
|
|
11
|
+
icon?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function CardsPreview({ data, variant = 'grid-3' }: BlockPreviewProps) {
|
|
15
|
+
const items = (data.items as CardItem[]) || [];
|
|
16
|
+
|
|
17
|
+
const placeholderItems: CardItem[] =
|
|
18
|
+
items.length > 0
|
|
19
|
+
? items
|
|
20
|
+
: [
|
|
21
|
+
{ title: 'Card Title', description: 'Card description text' },
|
|
22
|
+
{ title: 'Card Title', description: 'Card description text' },
|
|
23
|
+
{ title: 'Card Title', description: 'Card description text' },
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
const gridClass =
|
|
27
|
+
variant === 'grid-2'
|
|
28
|
+
? 'grid grid-cols-2 gap-3'
|
|
29
|
+
: variant === 'grid-4'
|
|
30
|
+
? 'grid grid-cols-4 gap-3'
|
|
31
|
+
: variant === 'horizontal'
|
|
32
|
+
? 'flex gap-3 overflow-hidden'
|
|
33
|
+
: 'grid grid-cols-3 gap-3';
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<div className="rounded-md border border-border p-4">
|
|
37
|
+
<div className={gridClass}>
|
|
38
|
+
{placeholderItems.map((item, i) => (
|
|
39
|
+
<div
|
|
40
|
+
key={i}
|
|
41
|
+
className={`flex flex-col overflow-hidden rounded-md border border-border bg-card ${variant === 'horizontal' ? 'min-w-40' : ''}`}
|
|
42
|
+
>
|
|
43
|
+
<div className="flex h-20 items-center justify-center bg-muted">
|
|
44
|
+
{item.image ? (
|
|
45
|
+
<img
|
|
46
|
+
src={item.image}
|
|
47
|
+
alt={item.title || ''}
|
|
48
|
+
className="h-full w-full object-cover"
|
|
49
|
+
/>
|
|
50
|
+
) : (
|
|
51
|
+
<Image size={20} className="text-muted-foreground" />
|
|
52
|
+
)}
|
|
53
|
+
</div>
|
|
54
|
+
<div className="p-3">
|
|
55
|
+
<p className="text-sm font-medium text-foreground">
|
|
56
|
+
{item.title || (
|
|
57
|
+
<span className="text-muted-foreground">Card Title</span>
|
|
58
|
+
)}
|
|
59
|
+
</p>
|
|
60
|
+
{item.description && (
|
|
61
|
+
<p className="mt-1 text-xs text-muted-foreground line-clamp-2">
|
|
62
|
+
{item.description}
|
|
63
|
+
</p>
|
|
64
|
+
)}
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
))}
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Code, Globe, Terminal } from 'lucide-react';
|
|
4
|
+
import type { BlockPreviewProps } from './index.js';
|
|
5
|
+
|
|
6
|
+
export function CodePreview({ data, variant = 'embed' }: BlockPreviewProps) {
|
|
7
|
+
const content = (data.content as string) || '';
|
|
8
|
+
const language = (data.language as string) || '';
|
|
9
|
+
const sandboxed = data.sandboxed as boolean | undefined;
|
|
10
|
+
|
|
11
|
+
const truncated = content.length > 200 ? content.slice(0, 200) + '…' : content;
|
|
12
|
+
|
|
13
|
+
const icon =
|
|
14
|
+
variant === 'html'
|
|
15
|
+
? <Globe size={14} className="text-muted-foreground" />
|
|
16
|
+
: variant === 'script'
|
|
17
|
+
? <Terminal size={14} className="text-muted-foreground" />
|
|
18
|
+
: <Code size={14} className="text-muted-foreground" />;
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<div className="overflow-hidden rounded-md border border-border">
|
|
22
|
+
<div className="flex items-center gap-2 border-b border-border bg-card px-3 py-2">
|
|
23
|
+
{icon}
|
|
24
|
+
<span className="text-xs text-muted-foreground">
|
|
25
|
+
{language || variant}
|
|
26
|
+
</span>
|
|
27
|
+
{sandboxed && (
|
|
28
|
+
<span className="ml-auto rounded bg-muted px-1.5 py-0.5 text-xs text-muted-foreground">
|
|
29
|
+
sandboxed
|
|
30
|
+
</span>
|
|
31
|
+
)}
|
|
32
|
+
</div>
|
|
33
|
+
<div className="bg-card p-4">
|
|
34
|
+
{truncated ? (
|
|
35
|
+
<pre className="overflow-hidden whitespace-pre-wrap font-mono text-xs text-foreground">
|
|
36
|
+
{truncated}
|
|
37
|
+
</pre>
|
|
38
|
+
) : (
|
|
39
|
+
<p className="font-mono text-xs text-muted-foreground italic">
|
|
40
|
+
// Code content goes here…
|
|
41
|
+
</p>
|
|
42
|
+
)}
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { ChevronDown } from 'lucide-react';
|
|
4
|
+
import type { BlockPreviewProps } from './index.js';
|
|
5
|
+
|
|
6
|
+
interface FAQItem {
|
|
7
|
+
question?: string;
|
|
8
|
+
answer?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function FAQPreview({ data, variant = 'accordion' }: BlockPreviewProps) {
|
|
12
|
+
const items = (data.items as FAQItem[]) || [];
|
|
13
|
+
|
|
14
|
+
const placeholderItems: FAQItem[] =
|
|
15
|
+
items.length > 0
|
|
16
|
+
? items
|
|
17
|
+
: [
|
|
18
|
+
{ question: 'What is this?', answer: 'Answer to the first question goes here.' },
|
|
19
|
+
{ question: 'How does it work?', answer: 'Answer to the second question goes here.' },
|
|
20
|
+
{ question: 'Where can I learn more?', answer: 'Answer to the third question goes here.' },
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
if (variant === 'two-column') {
|
|
24
|
+
const mid = Math.ceil(placeholderItems.length / 2);
|
|
25
|
+
const col1 = placeholderItems.slice(0, mid);
|
|
26
|
+
const col2 = placeholderItems.slice(mid);
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<div className="rounded-md border border-border p-4">
|
|
30
|
+
<div className="grid grid-cols-2 gap-4">
|
|
31
|
+
{[col1, col2].map((column, colIdx) => (
|
|
32
|
+
<div key={colIdx} className="flex flex-col gap-3">
|
|
33
|
+
{column.map((item, i) => (
|
|
34
|
+
<div key={i} className="rounded-md border border-border p-3">
|
|
35
|
+
<p className="text-sm font-medium text-foreground">
|
|
36
|
+
{item.question || 'Question?'}
|
|
37
|
+
</p>
|
|
38
|
+
<p className="mt-1 text-xs text-muted-foreground line-clamp-2">
|
|
39
|
+
{item.answer || 'Answer text…'}
|
|
40
|
+
</p>
|
|
41
|
+
</div>
|
|
42
|
+
))}
|
|
43
|
+
</div>
|
|
44
|
+
))}
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (variant === 'list') {
|
|
51
|
+
return (
|
|
52
|
+
<div className="rounded-md border border-border p-4">
|
|
53
|
+
<div className="flex flex-col gap-3">
|
|
54
|
+
{placeholderItems.map((item, i) => (
|
|
55
|
+
<div key={i} className="border-b border-border pb-3 last:border-b-0 last:pb-0">
|
|
56
|
+
<p className="text-sm font-medium text-foreground">
|
|
57
|
+
{item.question || 'Question?'}
|
|
58
|
+
</p>
|
|
59
|
+
<p className="mt-1 text-xs text-muted-foreground line-clamp-2">
|
|
60
|
+
{item.answer || 'Answer text…'}
|
|
61
|
+
</p>
|
|
62
|
+
</div>
|
|
63
|
+
))}
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<div className="rounded-md border border-border p-4">
|
|
71
|
+
<div className="flex flex-col gap-2">
|
|
72
|
+
{placeholderItems.map((item, i) => (
|
|
73
|
+
<div key={i} className="rounded-md border border-border p-3">
|
|
74
|
+
<div className="flex items-center justify-between">
|
|
75
|
+
<p className="text-sm font-medium text-foreground">
|
|
76
|
+
{item.question || 'Question?'}
|
|
77
|
+
</p>
|
|
78
|
+
<ChevronDown size={14} className="shrink-0 text-muted-foreground" />
|
|
79
|
+
</div>
|
|
80
|
+
{i === 0 && (
|
|
81
|
+
<p className="mt-2 text-xs text-muted-foreground">
|
|
82
|
+
{item.answer || 'Answer text…'}
|
|
83
|
+
</p>
|
|
84
|
+
)}
|
|
85
|
+
</div>
|
|
86
|
+
))}
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
);
|
|
90
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Box } from 'lucide-react';
|
|
4
|
+
|
|
5
|
+
export interface FallbackPreviewProps {
|
|
6
|
+
blockType: string;
|
|
7
|
+
data: Record<string, unknown>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function FallbackPreview({ blockType }: FallbackPreviewProps) {
|
|
11
|
+
return (
|
|
12
|
+
<div className="flex flex-col items-center justify-center gap-2 rounded-md border border-dashed border-border bg-muted/50 p-8">
|
|
13
|
+
<Box size={24} className="text-muted-foreground" />
|
|
14
|
+
<p className="text-sm font-medium text-muted-foreground">{blockType}</p>
|
|
15
|
+
<p className="text-xs text-muted-foreground">No preview available</p>
|
|
16
|
+
</div>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { FileText, Mail, Send } from 'lucide-react';
|
|
4
|
+
import type { BlockPreviewProps } from './index.js';
|
|
5
|
+
|
|
6
|
+
export function FormPreview({ data, variant = 'contact' }: BlockPreviewProps) {
|
|
7
|
+
const formId = (data.formId as string) || '';
|
|
8
|
+
|
|
9
|
+
if (variant === 'newsletter') {
|
|
10
|
+
return (
|
|
11
|
+
<div className="rounded-md border border-border p-6 text-center">
|
|
12
|
+
<Mail size={24} className="mx-auto text-muted-foreground" />
|
|
13
|
+
<p className="mt-3 text-sm font-medium text-foreground">Newsletter Signup</p>
|
|
14
|
+
{formId && (
|
|
15
|
+
<p className="mt-1 text-xs text-muted-foreground">Form: {formId}</p>
|
|
16
|
+
)}
|
|
17
|
+
<div className="mx-auto mt-4 flex max-w-xs gap-2">
|
|
18
|
+
<div className="h-9 flex-1 rounded-md border border-border bg-input-background" />
|
|
19
|
+
<div className="flex h-9 items-center rounded-md bg-primary px-3">
|
|
20
|
+
<Send size={14} className="text-primary-foreground" />
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (variant === 'custom') {
|
|
28
|
+
return (
|
|
29
|
+
<div className="rounded-md border border-border p-5">
|
|
30
|
+
<div className="flex items-center gap-2">
|
|
31
|
+
<FileText size={16} className="text-muted-foreground" />
|
|
32
|
+
<p className="text-sm font-medium text-foreground">
|
|
33
|
+
{formId ? `Form: ${formId}` : 'Custom Form'}
|
|
34
|
+
</p>
|
|
35
|
+
</div>
|
|
36
|
+
<div className="mt-3 flex flex-col gap-2">
|
|
37
|
+
<div className="h-8 rounded-md border border-border bg-input-background" />
|
|
38
|
+
<div className="h-8 rounded-md border border-border bg-input-background" />
|
|
39
|
+
<div className="h-20 rounded-md border border-border bg-input-background" />
|
|
40
|
+
</div>
|
|
41
|
+
<div className="mt-3">
|
|
42
|
+
<span className="inline-block rounded-md bg-primary px-4 py-2 text-sm text-primary-foreground">
|
|
43
|
+
Submit
|
|
44
|
+
</span>
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<div className="rounded-md border border-border p-5">
|
|
52
|
+
<p className="text-sm font-medium text-foreground">Contact Form</p>
|
|
53
|
+
{formId && (
|
|
54
|
+
<p className="mt-1 text-xs text-muted-foreground">Form: {formId}</p>
|
|
55
|
+
)}
|
|
56
|
+
<div className="mt-3 grid grid-cols-2 gap-2">
|
|
57
|
+
<div className="h-8 rounded-md border border-border bg-input-background" />
|
|
58
|
+
<div className="h-8 rounded-md border border-border bg-input-background" />
|
|
59
|
+
</div>
|
|
60
|
+
<div className="mt-2 h-8 rounded-md border border-border bg-input-background" />
|
|
61
|
+
<div className="mt-2 h-20 rounded-md border border-border bg-input-background" />
|
|
62
|
+
<div className="mt-3">
|
|
63
|
+
<span className="inline-block rounded-md bg-primary px-4 py-2 text-sm text-primary-foreground">
|
|
64
|
+
Send Message
|
|
65
|
+
</span>
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Image } from 'lucide-react';
|
|
4
|
+
import type { BlockPreviewProps } from './index.js';
|
|
5
|
+
|
|
6
|
+
interface GalleryImage {
|
|
7
|
+
src?: string;
|
|
8
|
+
alt?: string;
|
|
9
|
+
caption?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function GalleryPreview({ data, variant = 'grid' }: BlockPreviewProps) {
|
|
13
|
+
const images = (data.images as GalleryImage[]) || [];
|
|
14
|
+
|
|
15
|
+
const placeholders: GalleryImage[] =
|
|
16
|
+
images.length > 0
|
|
17
|
+
? images
|
|
18
|
+
: Array.from({ length: 6 }, (_, i) => ({ alt: `Image ${i + 1}` }));
|
|
19
|
+
|
|
20
|
+
const Thumbnail = ({ item, className = '' }: { item: GalleryImage; className?: string }) => (
|
|
21
|
+
<div className={`flex items-center justify-center overflow-hidden rounded-md bg-muted ${className}`}>
|
|
22
|
+
{item.src ? (
|
|
23
|
+
<img src={item.src} alt={item.alt || ''} className="h-full w-full object-cover" />
|
|
24
|
+
) : (
|
|
25
|
+
<Image size={20} className="text-muted-foreground" />
|
|
26
|
+
)}
|
|
27
|
+
</div>
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
if (variant === 'masonry') {
|
|
31
|
+
return (
|
|
32
|
+
<div className="rounded-md border border-border p-4">
|
|
33
|
+
<div className="columns-3 gap-3 space-y-3">
|
|
34
|
+
{placeholders.map((item, i) => (
|
|
35
|
+
<Thumbnail
|
|
36
|
+
key={i}
|
|
37
|
+
item={item}
|
|
38
|
+
className={i % 3 === 0 ? 'aspect-square' : i % 3 === 1 ? 'aspect-[3/4]' : 'aspect-[4/3]'}
|
|
39
|
+
/>
|
|
40
|
+
))}
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (variant === 'carousel') {
|
|
47
|
+
return (
|
|
48
|
+
<div className="overflow-hidden rounded-md border border-border p-4">
|
|
49
|
+
<div className="flex gap-3 overflow-hidden">
|
|
50
|
+
{placeholders.slice(0, 4).map((item, i) => (
|
|
51
|
+
<Thumbnail
|
|
52
|
+
key={i}
|
|
53
|
+
item={item}
|
|
54
|
+
className="aspect-square min-w-[120px] flex-1"
|
|
55
|
+
/>
|
|
56
|
+
))}
|
|
57
|
+
{placeholders.length > 4 && (
|
|
58
|
+
<div className="flex min-w-[120px] flex-1 items-center justify-center rounded-md bg-muted">
|
|
59
|
+
<span className="text-sm text-muted-foreground">
|
|
60
|
+
+{placeholders.length - 4}
|
|
61
|
+
</span>
|
|
62
|
+
</div>
|
|
63
|
+
)}
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (variant === 'lightbox') {
|
|
70
|
+
return (
|
|
71
|
+
<div className="rounded-md border border-border p-4">
|
|
72
|
+
<div className="grid grid-cols-3 gap-3">
|
|
73
|
+
{placeholders.map((item, i) => (
|
|
74
|
+
<Thumbnail key={i} item={item} className="aspect-square" />
|
|
75
|
+
))}
|
|
76
|
+
</div>
|
|
77
|
+
<p className="mt-2 text-center text-xs text-muted-foreground">
|
|
78
|
+
Click to open lightbox
|
|
79
|
+
</p>
|
|
80
|
+
</div>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return (
|
|
85
|
+
<div className="rounded-md border border-border p-4">
|
|
86
|
+
<div className="grid grid-cols-3 gap-3">
|
|
87
|
+
{placeholders.map((item, i) => (
|
|
88
|
+
<Thumbnail key={i} item={item} className="aspect-square" />
|
|
89
|
+
))}
|
|
90
|
+
</div>
|
|
91
|
+
</div>
|
|
92
|
+
);
|
|
93
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Image, Play } from 'lucide-react';
|
|
4
|
+
import type { BlockPreviewProps } from './index.js';
|
|
5
|
+
|
|
6
|
+
export function HeroPreview({ data, variant = 'centered' }: BlockPreviewProps) {
|
|
7
|
+
const title = (data.title as string) || '';
|
|
8
|
+
const subtitle = (data.subtitle as string) || '';
|
|
9
|
+
const image = data.image as string | undefined;
|
|
10
|
+
const ctaText = (data.ctaText as string) || '';
|
|
11
|
+
const overlayOpacity = (data.overlayOpacity as number) ?? 0.4;
|
|
12
|
+
|
|
13
|
+
if (variant === 'split-image') {
|
|
14
|
+
return (
|
|
15
|
+
<div className="flex min-h-[200px] overflow-hidden rounded-md border border-border">
|
|
16
|
+
<div className="flex flex-1 flex-col justify-center gap-3 p-6">
|
|
17
|
+
<h2 className="text-2xl font-medium text-foreground">
|
|
18
|
+
{title || <span className="text-muted-foreground">Hero Title</span>}
|
|
19
|
+
</h2>
|
|
20
|
+
{(subtitle || !title) && (
|
|
21
|
+
<p className="text-sm text-muted-foreground">
|
|
22
|
+
{subtitle || 'Subtitle text goes here'}
|
|
23
|
+
</p>
|
|
24
|
+
)}
|
|
25
|
+
{ctaText && (
|
|
26
|
+
<div className="mt-2">
|
|
27
|
+
<span className="inline-block rounded-md bg-primary px-4 py-2 text-sm text-primary-foreground">
|
|
28
|
+
{ctaText}
|
|
29
|
+
</span>
|
|
30
|
+
</div>
|
|
31
|
+
)}
|
|
32
|
+
</div>
|
|
33
|
+
<div className="flex flex-1 items-center justify-center bg-muted">
|
|
34
|
+
{image ? (
|
|
35
|
+
<img src={image} alt="" className="h-full w-full object-cover" />
|
|
36
|
+
) : (
|
|
37
|
+
<Image size={32} className="text-muted-foreground" />
|
|
38
|
+
)}
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (variant === 'minimal') {
|
|
45
|
+
return (
|
|
46
|
+
<div className="flex flex-col items-center gap-3 rounded-md border border-border p-8">
|
|
47
|
+
<h2 className="text-2xl font-medium text-foreground">
|
|
48
|
+
{title || <span className="text-muted-foreground">Hero Title</span>}
|
|
49
|
+
</h2>
|
|
50
|
+
{(subtitle || !title) && (
|
|
51
|
+
<p className="text-sm text-muted-foreground">
|
|
52
|
+
{subtitle || 'Subtitle text goes here'}
|
|
53
|
+
</p>
|
|
54
|
+
)}
|
|
55
|
+
{ctaText && (
|
|
56
|
+
<span className="mt-2 inline-block rounded-md bg-primary px-4 py-2 text-sm text-primary-foreground">
|
|
57
|
+
{ctaText}
|
|
58
|
+
</span>
|
|
59
|
+
)}
|
|
60
|
+
</div>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const isVideo = variant === 'video-bg';
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<div className="relative flex min-h-[220px] flex-col items-center justify-center gap-3 overflow-hidden rounded-md border border-border p-8">
|
|
68
|
+
{image ? (
|
|
69
|
+
<img
|
|
70
|
+
src={image}
|
|
71
|
+
alt=""
|
|
72
|
+
className="absolute inset-0 h-full w-full object-cover"
|
|
73
|
+
/>
|
|
74
|
+
) : (
|
|
75
|
+
<div className="absolute inset-0 bg-muted" />
|
|
76
|
+
)}
|
|
77
|
+
<div
|
|
78
|
+
className="absolute inset-0 bg-background"
|
|
79
|
+
style={{ opacity: overlayOpacity }}
|
|
80
|
+
/>
|
|
81
|
+
<div className="relative z-10 flex flex-col items-center gap-3 text-center">
|
|
82
|
+
{isVideo && (
|
|
83
|
+
<div className="mb-2 flex h-12 w-12 items-center justify-center rounded-full bg-primary/90">
|
|
84
|
+
<Play size={20} className="text-primary-foreground" />
|
|
85
|
+
</div>
|
|
86
|
+
)}
|
|
87
|
+
<h2 className="text-2xl font-medium text-foreground">
|
|
88
|
+
{title || <span className="text-muted-foreground">Hero Title</span>}
|
|
89
|
+
</h2>
|
|
90
|
+
{(subtitle || !title) && (
|
|
91
|
+
<p className="text-sm text-muted-foreground">
|
|
92
|
+
{subtitle || 'Subtitle text goes here'}
|
|
93
|
+
</p>
|
|
94
|
+
)}
|
|
95
|
+
{ctaText && (
|
|
96
|
+
<span className="mt-2 inline-block rounded-md bg-primary px-4 py-2 text-sm text-primary-foreground">
|
|
97
|
+
{ctaText}
|
|
98
|
+
</span>
|
|
99
|
+
)}
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
);
|
|
103
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Image } from 'lucide-react';
|
|
4
|
+
import type { BlockPreviewProps } from './index.js';
|
|
5
|
+
|
|
6
|
+
export function ImagePreview({ data, variant = 'full-width' }: BlockPreviewProps) {
|
|
7
|
+
const src = data.src as string | undefined;
|
|
8
|
+
const alt = (data.alt as string) || '';
|
|
9
|
+
const caption = (data.caption as string) || '';
|
|
10
|
+
const aspectRatio = (data.aspectRatio as string) || '16/9';
|
|
11
|
+
|
|
12
|
+
const roundedClass = variant === 'rounded' ? 'rounded-xl' : 'rounded-md';
|
|
13
|
+
|
|
14
|
+
const ImagePlaceholder = ({ className = '' }: { className?: string }) => (
|
|
15
|
+
<div
|
|
16
|
+
className={`flex items-center justify-center bg-muted ${roundedClass} ${className}`}
|
|
17
|
+
style={{ aspectRatio }}
|
|
18
|
+
>
|
|
19
|
+
<Image size={32} className="text-muted-foreground" />
|
|
20
|
+
</div>
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
const ImageElement = ({ className = '' }: { className?: string }) =>
|
|
24
|
+
src ? (
|
|
25
|
+
<img
|
|
26
|
+
src={src}
|
|
27
|
+
alt={alt}
|
|
28
|
+
className={`h-auto w-full object-cover ${roundedClass} ${className}`}
|
|
29
|
+
style={{ aspectRatio }}
|
|
30
|
+
/>
|
|
31
|
+
) : (
|
|
32
|
+
<ImagePlaceholder className={className} />
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
if (variant === 'side-by-side') {
|
|
36
|
+
return (
|
|
37
|
+
<div className="grid grid-cols-2 gap-3 rounded-md border border-border p-4">
|
|
38
|
+
<ImageElement />
|
|
39
|
+
<ImagePlaceholder />
|
|
40
|
+
</div>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<figure className="overflow-hidden rounded-md border border-border p-4">
|
|
46
|
+
<ImageElement />
|
|
47
|
+
{(variant === 'captioned' || caption) && (
|
|
48
|
+
<figcaption className="mt-2 text-center text-sm text-muted-foreground">
|
|
49
|
+
{caption || 'Image caption'}
|
|
50
|
+
</figcaption>
|
|
51
|
+
)}
|
|
52
|
+
</figure>
|
|
53
|
+
);
|
|
54
|
+
}
|