@prenta/admin 0.98.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/AdminRoot.d.ts.map +1 -1
- package/dist/AdminRoot.js +17 -28
- package/dist/AdminRoot.js.map +1 -1
- package/dist/__tests__/components/seo-score-bands.test.js +0 -10
- package/dist/__tests__/components/seo-score-bands.test.js.map +1 -1
- package/dist/__tests__/layout/sidebar-posts.test.js +6 -11
- package/dist/__tests__/layout/sidebar-posts.test.js.map +1 -1
- package/dist/__tests__/layout/sidebar-submenu.render.test.js +3 -2
- package/dist/__tests__/layout/sidebar-submenu.render.test.js.map +1 -1
- package/dist/__tests__/lib/page-audit-input.test.js +77 -1
- package/dist/__tests__/lib/page-audit-input.test.js.map +1 -1
- package/dist/__tests__/lib/page-editor-service-api.test.js +26 -23
- package/dist/__tests__/lib/page-editor-service-api.test.js.map +1 -1
- package/dist/__tests__/views/page-section-editor.test.js +0 -1
- package/dist/__tests__/views/page-section-editor.test.js.map +1 -1
- package/dist/__tests__/views/page-tab.render.test.js +0 -1
- package/dist/__tests__/views/page-tab.render.test.js.map +1 -1
- package/dist/index.d.ts +1 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -3
- package/dist/index.js.map +1 -1
- package/dist/layout/Sidebar.d.ts +3 -4
- package/dist/layout/Sidebar.d.ts.map +1 -1
- package/dist/layout/Sidebar.js +7 -38
- package/dist/layout/Sidebar.js.map +1 -1
- package/dist/lib/page-audit-input.d.ts +14 -1
- package/dist/lib/page-audit-input.d.ts.map +1 -1
- package/dist/lib/page-audit-input.js +19 -0
- package/dist/lib/page-audit-input.js.map +1 -1
- package/dist/lib/page-editor-service.d.ts +0 -7
- package/dist/lib/page-editor-service.d.ts.map +1 -1
- package/dist/lib/page-editor-service.js +9 -9
- package/dist/lib/page-editor-service.js.map +1 -1
- package/dist/prenta-admin.css +1 -1
- package/dist/views/PageEditor.d.ts +2 -2
- package/dist/views/PageEditor.js +2 -2
- package/dist/views/page-editor/PageSectionEditor.d.ts.map +1 -1
- package/dist/views/page-editor/PageSectionEditor.js +9 -15
- package/dist/views/page-editor/PageSectionEditor.js.map +1 -1
- package/package.json +4 -4
- package/src/AdminRoot.tsx +18 -68
- package/src/__tests__/components/seo-score-bands.test.ts +0 -10
- package/src/__tests__/layout/sidebar-posts.test.ts +6 -11
- package/src/__tests__/layout/sidebar-submenu.render.test.tsx +3 -2
- package/src/__tests__/lib/page-audit-input.test.ts +91 -1
- package/src/__tests__/lib/page-editor-service-api.test.ts +27 -25
- package/src/__tests__/views/page-section-editor.test.tsx +0 -1
- package/src/__tests__/views/page-tab.render.test.tsx +0 -1
- package/src/index.ts +1 -11
- package/src/layout/Sidebar.tsx +6 -41
- package/src/lib/page-audit-input.ts +36 -1
- package/src/lib/page-editor-service.ts +9 -16
- package/src/views/PageEditor.tsx +2 -2
- package/src/views/page-editor/PageSectionEditor.tsx +8 -37
- package/dist/__tests__/views/page-builder-block-renderer.render.test.d.ts +0 -2
- package/dist/__tests__/views/page-builder-block-renderer.render.test.d.ts.map +0 -1
- package/dist/__tests__/views/page-builder-block-renderer.render.test.js +0 -48
- package/dist/__tests__/views/page-builder-block-renderer.render.test.js.map +0 -1
- package/dist/__tests__/views/page-builder-form-picker.render.test.d.ts +0 -2
- package/dist/__tests__/views/page-builder-form-picker.render.test.d.ts.map +0 -1
- package/dist/__tests__/views/page-builder-form-picker.render.test.js +0 -44
- package/dist/__tests__/views/page-builder-form-picker.render.test.js.map +0 -1
- package/dist/__tests__/views/page-builder-sections-panel.render.test.d.ts +0 -2
- package/dist/__tests__/views/page-builder-sections-panel.render.test.d.ts.map +0 -1
- package/dist/__tests__/views/page-builder-sections-panel.render.test.js +0 -121
- package/dist/__tests__/views/page-builder-sections-panel.render.test.js.map +0 -1
- package/dist/__tests__/views/page-builder-template-seed.render.test.d.ts +0 -2
- package/dist/__tests__/views/page-builder-template-seed.render.test.d.ts.map +0 -1
- package/dist/__tests__/views/page-builder-template-seed.render.test.js +0 -48
- package/dist/__tests__/views/page-builder-template-seed.render.test.js.map +0 -1
- package/dist/__tests__/views/page-templates.render.test.d.ts +0 -2
- package/dist/__tests__/views/page-templates.render.test.d.ts.map +0 -1
- package/dist/__tests__/views/page-templates.render.test.js +0 -126
- package/dist/__tests__/views/page-templates.render.test.js.map +0 -1
- package/dist/hooks/useBuilderState.d.ts +0 -49
- package/dist/hooks/useBuilderState.d.ts.map +0 -1
- package/dist/hooks/useBuilderState.js +0 -238
- package/dist/hooks/useBuilderState.js.map +0 -1
- package/dist/views/page-builder/AIBlockAssist.d.ts +0 -9
- package/dist/views/page-builder/AIBlockAssist.d.ts.map +0 -1
- package/dist/views/page-builder/AIBlockAssist.js +0 -40
- package/dist/views/page-builder/AIBlockAssist.js.map +0 -1
- package/dist/views/page-builder/AIGenerateDialog.d.ts +0 -8
- package/dist/views/page-builder/AIGenerateDialog.d.ts.map +0 -1
- package/dist/views/page-builder/AIGenerateDialog.js +0 -173
- package/dist/views/page-builder/AIGenerateDialog.js.map +0 -1
- package/dist/views/page-builder/BlockEditor.d.ts +0 -11
- package/dist/views/page-builder/BlockEditor.d.ts.map +0 -1
- package/dist/views/page-builder/BlockEditor.js +0 -159
- package/dist/views/page-builder/BlockEditor.js.map +0 -1
- package/dist/views/page-builder/BlockPicker.d.ts +0 -7
- package/dist/views/page-builder/BlockPicker.d.ts.map +0 -1
- package/dist/views/page-builder/BlockPicker.js +0 -103
- package/dist/views/page-builder/BlockPicker.js.map +0 -1
- package/dist/views/page-builder/BottomBar.d.ts +0 -9
- package/dist/views/page-builder/BottomBar.d.ts.map +0 -1
- package/dist/views/page-builder/BottomBar.js +0 -13
- package/dist/views/page-builder/BottomBar.js.map +0 -1
- package/dist/views/page-builder/BuilderSectionsPanel.d.ts +0 -20
- package/dist/views/page-builder/BuilderSectionsPanel.d.ts.map +0 -1
- package/dist/views/page-builder/BuilderSectionsPanel.js +0 -105
- package/dist/views/page-builder/BuilderSectionsPanel.js.map +0 -1
- package/dist/views/page-builder/BuilderToolbar.d.ts +0 -21
- package/dist/views/page-builder/BuilderToolbar.d.ts.map +0 -1
- package/dist/views/page-builder/BuilderToolbar.js +0 -18
- package/dist/views/page-builder/BuilderToolbar.js.map +0 -1
- package/dist/views/page-builder/ContextPanel.d.ts +0 -20
- package/dist/views/page-builder/ContextPanel.d.ts.map +0 -1
- package/dist/views/page-builder/ContextPanel.js +0 -43
- package/dist/views/page-builder/ContextPanel.js.map +0 -1
- package/dist/views/page-builder/DesignScore.d.ts +0 -6
- package/dist/views/page-builder/DesignScore.d.ts.map +0 -1
- package/dist/views/page-builder/DesignScore.js +0 -93
- package/dist/views/page-builder/DesignScore.js.map +0 -1
- package/dist/views/page-builder/NodeSettings.d.ts +0 -12
- package/dist/views/page-builder/NodeSettings.d.ts.map +0 -1
- package/dist/views/page-builder/NodeSettings.js +0 -80
- package/dist/views/page-builder/NodeSettings.js.map +0 -1
- package/dist/views/page-builder/PageBuilder.d.ts +0 -10
- package/dist/views/page-builder/PageBuilder.d.ts.map +0 -1
- package/dist/views/page-builder/PageBuilder.js +0 -222
- package/dist/views/page-builder/PageBuilder.js.map +0 -1
- package/dist/views/page-builder/PageSettings.d.ts +0 -7
- package/dist/views/page-builder/PageSettings.d.ts.map +0 -1
- package/dist/views/page-builder/PageSettings.js +0 -27
- package/dist/views/page-builder/PageSettings.js.map +0 -1
- package/dist/views/page-builder/PageTemplates.d.ts +0 -5
- package/dist/views/page-builder/PageTemplates.d.ts.map +0 -1
- package/dist/views/page-builder/PageTemplates.js +0 -146
- package/dist/views/page-builder/PageTemplates.js.map +0 -1
- package/dist/views/page-builder/RelationshipField.d.ts +0 -15
- package/dist/views/page-builder/RelationshipField.d.ts.map +0 -1
- package/dist/views/page-builder/RelationshipField.js +0 -65
- package/dist/views/page-builder/RelationshipField.js.map +0 -1
- package/dist/views/page-builder/SEOPanel.d.ts +0 -10
- package/dist/views/page-builder/SEOPanel.d.ts.map +0 -1
- package/dist/views/page-builder/SEOPanel.js +0 -103
- package/dist/views/page-builder/SEOPanel.js.map +0 -1
- package/dist/views/page-builder/SavedSections.d.ts +0 -6
- package/dist/views/page-builder/SavedSections.d.ts.map +0 -1
- package/dist/views/page-builder/SavedSections.js +0 -141
- package/dist/views/page-builder/SavedSections.js.map +0 -1
- package/dist/views/page-builder/TemplatePicker.d.ts +0 -7
- package/dist/views/page-builder/TemplatePicker.d.ts.map +0 -1
- package/dist/views/page-builder/TemplatePicker.js +0 -68
- package/dist/views/page-builder/TemplatePicker.js.map +0 -1
- package/dist/views/page-builder/Widgets.d.ts +0 -13
- package/dist/views/page-builder/Widgets.d.ts.map +0 -1
- package/dist/views/page-builder/Widgets.js +0 -15
- package/dist/views/page-builder/Widgets.js.map +0 -1
- package/dist/views/page-builder/block-renderers/CTAPreview.d.ts +0 -3
- package/dist/views/page-builder/block-renderers/CTAPreview.d.ts.map +0 -1
- package/dist/views/page-builder/block-renderers/CTAPreview.js +0 -19
- package/dist/views/page-builder/block-renderers/CTAPreview.js.map +0 -1
- package/dist/views/page-builder/block-renderers/CardsPreview.d.ts +0 -3
- package/dist/views/page-builder/block-renderers/CardsPreview.d.ts.map +0 -1
- package/dist/views/page-builder/block-renderers/CardsPreview.js +0 -22
- package/dist/views/page-builder/block-renderers/CardsPreview.js.map +0 -1
- package/dist/views/page-builder/block-renderers/CodePreview.d.ts +0 -3
- package/dist/views/page-builder/block-renderers/CodePreview.d.ts.map +0 -1
- package/dist/views/page-builder/block-renderers/CodePreview.js +0 -12
- package/dist/views/page-builder/block-renderers/CodePreview.js.map +0 -1
- package/dist/views/page-builder/block-renderers/FAQPreview.d.ts +0 -3
- package/dist/views/page-builder/block-renderers/FAQPreview.d.ts.map +0 -1
- package/dist/views/page-builder/block-renderers/FAQPreview.js +0 -27
- package/dist/views/page-builder/block-renderers/FAQPreview.js.map +0 -1
- package/dist/views/page-builder/block-renderers/FallbackPreview.d.ts +0 -6
- package/dist/views/page-builder/block-renderers/FallbackPreview.d.ts.map +0 -1
- package/dist/views/page-builder/block-renderers/FallbackPreview.js +0 -7
- package/dist/views/page-builder/block-renderers/FallbackPreview.js.map +0 -1
- package/dist/views/page-builder/block-renderers/FormPreview.d.ts +0 -9
- package/dist/views/page-builder/block-renderers/FormPreview.d.ts.map +0 -1
- package/dist/views/page-builder/block-renderers/FormPreview.js +0 -52
- package/dist/views/page-builder/block-renderers/FormPreview.js.map +0 -1
- package/dist/views/page-builder/block-renderers/GalleryPreview.d.ts +0 -3
- package/dist/views/page-builder/block-renderers/GalleryPreview.d.ts.map +0 -1
- package/dist/views/page-builder/block-renderers/GalleryPreview.js +0 -19
- package/dist/views/page-builder/block-renderers/GalleryPreview.js.map +0 -1
- package/dist/views/page-builder/block-renderers/HeroPreview.d.ts +0 -3
- package/dist/views/page-builder/block-renderers/HeroPreview.d.ts.map +0 -1
- package/dist/views/page-builder/block-renderers/HeroPreview.js +0 -19
- package/dist/views/page-builder/block-renderers/HeroPreview.js.map +0 -1
- package/dist/views/page-builder/block-renderers/ImagePreview.d.ts +0 -3
- package/dist/views/page-builder/block-renderers/ImagePreview.d.ts.map +0 -1
- package/dist/views/page-builder/block-renderers/ImagePreview.js +0 -17
- package/dist/views/page-builder/block-renderers/ImagePreview.js.map +0 -1
- package/dist/views/page-builder/block-renderers/TextPreview.d.ts +0 -3
- package/dist/views/page-builder/block-renderers/TextPreview.d.ts.map +0 -1
- package/dist/views/page-builder/block-renderers/TextPreview.js +0 -22
- package/dist/views/page-builder/block-renderers/TextPreview.js.map +0 -1
- package/dist/views/page-builder/block-renderers/VideoPreview.d.ts +0 -3
- package/dist/views/page-builder/block-renderers/VideoPreview.d.ts.map +0 -1
- package/dist/views/page-builder/block-renderers/VideoPreview.js +0 -18
- package/dist/views/page-builder/block-renderers/VideoPreview.js.map +0 -1
- package/dist/views/page-builder/block-renderers/index.d.ts +0 -9
- package/dist/views/page-builder/block-renderers/index.d.ts.map +0 -1
- package/dist/views/page-builder/block-renderers/index.js +0 -25
- package/dist/views/page-builder/block-renderers/index.js.map +0 -1
- package/dist/views/page-builder/canvas/BlockRenderer.d.ts +0 -8
- package/dist/views/page-builder/canvas/BlockRenderer.d.ts.map +0 -1
- package/dist/views/page-builder/canvas/BlockRenderer.js +0 -20
- package/dist/views/page-builder/canvas/BlockRenderer.js.map +0 -1
- package/dist/views/page-builder/canvas/BuilderCanvas.d.ts +0 -10
- package/dist/views/page-builder/canvas/BuilderCanvas.d.ts.map +0 -1
- package/dist/views/page-builder/canvas/BuilderCanvas.js +0 -26
- package/dist/views/page-builder/canvas/BuilderCanvas.js.map +0 -1
- package/dist/views/page-builder/canvas/ColumnRenderer.d.ts +0 -8
- package/dist/views/page-builder/canvas/ColumnRenderer.d.ts.map +0 -1
- package/dist/views/page-builder/canvas/ColumnRenderer.js +0 -32
- package/dist/views/page-builder/canvas/ColumnRenderer.js.map +0 -1
- package/dist/views/page-builder/canvas/ContainerRenderer.d.ts +0 -8
- package/dist/views/page-builder/canvas/ContainerRenderer.d.ts.map +0 -1
- package/dist/views/page-builder/canvas/ContainerRenderer.js +0 -29
- package/dist/views/page-builder/canvas/ContainerRenderer.js.map +0 -1
- package/dist/views/page-builder/canvas/RowRenderer.d.ts +0 -8
- package/dist/views/page-builder/canvas/RowRenderer.d.ts.map +0 -1
- package/dist/views/page-builder/canvas/RowRenderer.js +0 -28
- package/dist/views/page-builder/canvas/RowRenderer.js.map +0 -1
- package/dist/views/page-builder/canvas/SectionRenderer.d.ts +0 -8
- package/dist/views/page-builder/canvas/SectionRenderer.d.ts.map +0 -1
- package/dist/views/page-builder/canvas/SectionRenderer.js +0 -50
- package/dist/views/page-builder/canvas/SectionRenderer.js.map +0 -1
- package/dist/views/page-builder/canvas/index.d.ts +0 -3
- package/dist/views/page-builder/canvas/index.d.ts.map +0 -1
- package/dist/views/page-builder/canvas/index.js +0 -2
- package/dist/views/page-builder/canvas/index.js.map +0 -1
- package/src/__tests__/views/page-builder-block-renderer.render.test.tsx +0 -66
- package/src/__tests__/views/page-builder-form-picker.render.test.tsx +0 -54
- package/src/__tests__/views/page-builder-sections-panel.render.test.tsx +0 -138
- package/src/__tests__/views/page-builder-template-seed.render.test.tsx +0 -59
- package/src/__tests__/views/page-templates.render.test.tsx +0 -182
- package/src/hooks/useBuilderState.ts +0 -361
- package/src/views/page-builder/AIBlockAssist.tsx +0 -64
- package/src/views/page-builder/AIGenerateDialog.tsx +0 -593
- package/src/views/page-builder/BlockEditor.tsx +0 -523
- package/src/views/page-builder/BlockPicker.tsx +0 -324
- package/src/views/page-builder/BottomBar.tsx +0 -65
- package/src/views/page-builder/BuilderSectionsPanel.tsx +0 -304
- package/src/views/page-builder/BuilderToolbar.tsx +0 -221
- package/src/views/page-builder/ContextPanel.tsx +0 -145
- package/src/views/page-builder/DesignScore.tsx +0 -251
- package/src/views/page-builder/NodeSettings.tsx +0 -500
- package/src/views/page-builder/PageBuilder.tsx +0 -405
- package/src/views/page-builder/PageSettings.tsx +0 -162
- package/src/views/page-builder/PageTemplates.tsx +0 -435
- package/src/views/page-builder/RelationshipField.tsx +0 -113
- package/src/views/page-builder/SEOPanel.tsx +0 -464
- package/src/views/page-builder/SavedSections.tsx +0 -480
- package/src/views/page-builder/TemplatePicker.tsx +0 -197
- package/src/views/page-builder/Widgets.tsx +0 -44
- package/src/views/page-builder/block-renderers/CTAPreview.tsx +0 -79
- package/src/views/page-builder/block-renderers/CardsPreview.tsx +0 -69
- package/src/views/page-builder/block-renderers/CodePreview.tsx +0 -46
- package/src/views/page-builder/block-renderers/FAQPreview.tsx +0 -87
- package/src/views/page-builder/block-renderers/FallbackPreview.tsx +0 -18
- package/src/views/page-builder/block-renderers/FormPreview.tsx +0 -117
- package/src/views/page-builder/block-renderers/GalleryPreview.tsx +0 -87
- package/src/views/page-builder/block-renderers/HeroPreview.tsx +0 -90
- package/src/views/page-builder/block-renderers/ImagePreview.tsx +0 -54
- package/src/views/page-builder/block-renderers/TextPreview.tsx +0 -81
- package/src/views/page-builder/block-renderers/VideoPreview.tsx +0 -73
- package/src/views/page-builder/block-renderers/index.ts +0 -34
- package/src/views/page-builder/canvas/BlockRenderer.tsx +0 -56
- package/src/views/page-builder/canvas/BuilderCanvas.tsx +0 -87
- package/src/views/page-builder/canvas/ColumnRenderer.tsx +0 -82
- package/src/views/page-builder/canvas/ContainerRenderer.tsx +0 -67
- package/src/views/page-builder/canvas/RowRenderer.tsx +0 -68
- package/src/views/page-builder/canvas/SectionRenderer.tsx +0 -93
- package/src/views/page-builder/canvas/index.ts +0 -2
|
@@ -1,54 +0,0 @@
|
|
|
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={`bg-muted flex items-center justify-center ${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="border-border grid grid-cols-2 gap-3 rounded-md border p-4">
|
|
38
|
-
<ImageElement />
|
|
39
|
-
<ImagePlaceholder />
|
|
40
|
-
</div>
|
|
41
|
-
)
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return (
|
|
45
|
-
<figure className="border-border overflow-hidden rounded-md border p-4">
|
|
46
|
-
<ImageElement />
|
|
47
|
-
{(variant === 'captioned' || caption) && (
|
|
48
|
-
<figcaption className="text-muted-foreground mt-2 text-center text-sm">
|
|
49
|
-
{caption || 'Image caption'}
|
|
50
|
-
</figcaption>
|
|
51
|
-
)}
|
|
52
|
-
</figure>
|
|
53
|
-
)
|
|
54
|
-
}
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import type { BlockPreviewProps } from './index.js'
|
|
4
|
-
|
|
5
|
-
export function TextPreview({ data, variant = 'prose' }: BlockPreviewProps) {
|
|
6
|
-
const body = (data.body as string) || ''
|
|
7
|
-
const heading = (data.heading as string) || ''
|
|
8
|
-
const headingLevel = (data.headingLevel as string) || 'h2'
|
|
9
|
-
|
|
10
|
-
const truncatedBody = body.length > 300 ? body.slice(0, 300) + '…' : body
|
|
11
|
-
|
|
12
|
-
const validLevels = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'] as const
|
|
13
|
-
const HeadingTag = (validLevels.includes(headingLevel as any) ? headingLevel : 'h2') as
|
|
14
|
-
| 'h1'
|
|
15
|
-
| 'h2'
|
|
16
|
-
| 'h3'
|
|
17
|
-
| 'h4'
|
|
18
|
-
| 'h5'
|
|
19
|
-
| 'h6'
|
|
20
|
-
const headingSizeClass =
|
|
21
|
-
HeadingTag === 'h1' ? 'text-2xl' : HeadingTag === 'h2' ? 'text-xl' : 'text-lg'
|
|
22
|
-
|
|
23
|
-
if (variant === 'two-column') {
|
|
24
|
-
const midpoint = Math.ceil(truncatedBody.length / 2)
|
|
25
|
-
const col1 = truncatedBody.slice(0, midpoint)
|
|
26
|
-
const col2 = truncatedBody.slice(midpoint)
|
|
27
|
-
|
|
28
|
-
return (
|
|
29
|
-
<div className="border-border rounded-md border p-5">
|
|
30
|
-
{heading && (
|
|
31
|
-
<HeadingTag className={`${headingSizeClass} text-foreground mb-3 font-medium`}>
|
|
32
|
-
{heading}
|
|
33
|
-
</HeadingTag>
|
|
34
|
-
)}
|
|
35
|
-
<div className="grid grid-cols-2 gap-4">
|
|
36
|
-
<p className="text-muted-foreground text-sm leading-relaxed">
|
|
37
|
-
{col1 || 'Column one text content…'}
|
|
38
|
-
</p>
|
|
39
|
-
<p className="text-muted-foreground text-sm leading-relaxed">
|
|
40
|
-
{col2 || 'Column two text content…'}
|
|
41
|
-
</p>
|
|
42
|
-
</div>
|
|
43
|
-
</div>
|
|
44
|
-
)
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (variant === 'with-sidebar') {
|
|
48
|
-
return (
|
|
49
|
-
<div className="border-border grid grid-cols-[1fr_200px] gap-4 rounded-md border p-5">
|
|
50
|
-
<div>
|
|
51
|
-
{heading && (
|
|
52
|
-
<HeadingTag className={`${headingSizeClass} text-foreground mb-3 font-medium`}>
|
|
53
|
-
{heading}
|
|
54
|
-
</HeadingTag>
|
|
55
|
-
)}
|
|
56
|
-
<p className="text-muted-foreground text-sm leading-relaxed">
|
|
57
|
-
{truncatedBody || 'Body text content goes here…'}
|
|
58
|
-
</p>
|
|
59
|
-
</div>
|
|
60
|
-
<aside className="bg-muted rounded-md p-3">
|
|
61
|
-
<div className="bg-border h-3 w-3/4 rounded" />
|
|
62
|
-
<div className="bg-border mt-2 h-2 w-full rounded" />
|
|
63
|
-
<div className="bg-border mt-1 h-2 w-2/3 rounded" />
|
|
64
|
-
</aside>
|
|
65
|
-
</div>
|
|
66
|
-
)
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return (
|
|
70
|
-
<div className="border-border rounded-md border p-5">
|
|
71
|
-
{heading && (
|
|
72
|
-
<HeadingTag className={`${headingSizeClass} text-foreground mb-3 font-medium`}>
|
|
73
|
-
{heading}
|
|
74
|
-
</HeadingTag>
|
|
75
|
-
)}
|
|
76
|
-
<p className="text-muted-foreground text-sm leading-relaxed">
|
|
77
|
-
{truncatedBody || <span className="italic">Body text content goes here…</span>}
|
|
78
|
-
</p>
|
|
79
|
-
</div>
|
|
80
|
-
)
|
|
81
|
-
}
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import { Play } from 'lucide-react'
|
|
4
|
-
import type { BlockPreviewProps } from './index.js'
|
|
5
|
-
|
|
6
|
-
export function VideoPreview({ data, variant = 'inline' }: BlockPreviewProps) {
|
|
7
|
-
const poster = data.poster as string | undefined
|
|
8
|
-
const autoplay = data.autoplay as boolean | undefined
|
|
9
|
-
const loop = data.loop as boolean | undefined
|
|
10
|
-
|
|
11
|
-
const badges: string[] = [autoplay ? 'Autoplay' : '', loop ? 'Loop' : ''].filter(
|
|
12
|
-
(b): b is string => b !== '',
|
|
13
|
-
)
|
|
14
|
-
|
|
15
|
-
const content = (
|
|
16
|
-
<div className="bg-muted relative flex aspect-video items-center justify-center overflow-hidden rounded-md">
|
|
17
|
-
{poster ? (
|
|
18
|
-
<img src={poster} alt="" className="absolute inset-0 h-full w-full object-cover" />
|
|
19
|
-
) : (
|
|
20
|
-
<div className="bg-card absolute inset-0" />
|
|
21
|
-
)}
|
|
22
|
-
<div className="bg-primary/90 relative z-10 flex h-12 w-12 items-center justify-center rounded-full shadow-sm">
|
|
23
|
-
<Play size={20} className="text-primary-foreground" />
|
|
24
|
-
</div>
|
|
25
|
-
{badges.length > 0 && (
|
|
26
|
-
<div className="absolute right-2 bottom-2 z-10 flex gap-1">
|
|
27
|
-
{badges.map((badge) => (
|
|
28
|
-
<span
|
|
29
|
-
key={badge}
|
|
30
|
-
className="bg-background/80 text-muted-foreground rounded px-1.5 py-0.5 text-xs"
|
|
31
|
-
>
|
|
32
|
-
{badge}
|
|
33
|
-
</span>
|
|
34
|
-
))}
|
|
35
|
-
</div>
|
|
36
|
-
)}
|
|
37
|
-
</div>
|
|
38
|
-
)
|
|
39
|
-
|
|
40
|
-
if (variant === 'background') {
|
|
41
|
-
return (
|
|
42
|
-
<div className="border-border overflow-hidden rounded-md border">
|
|
43
|
-
<div className="relative aspect-[21/9]">
|
|
44
|
-
{poster ? (
|
|
45
|
-
<img src={poster} alt="" className="absolute inset-0 h-full w-full object-cover" />
|
|
46
|
-
) : (
|
|
47
|
-
<div className="bg-card absolute inset-0" />
|
|
48
|
-
)}
|
|
49
|
-
<div className="bg-background/30 absolute inset-0 flex items-center justify-center">
|
|
50
|
-
<div className="bg-primary/90 flex h-14 w-14 items-center justify-center rounded-full shadow-sm">
|
|
51
|
-
<Play size={24} className="text-primary-foreground" />
|
|
52
|
-
</div>
|
|
53
|
-
</div>
|
|
54
|
-
</div>
|
|
55
|
-
</div>
|
|
56
|
-
)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (variant === 'lightbox') {
|
|
60
|
-
return (
|
|
61
|
-
<div className="border-border rounded-md border p-4">
|
|
62
|
-
<div className="mx-auto max-w-sm">
|
|
63
|
-
{content}
|
|
64
|
-
<p className="text-muted-foreground mt-2 text-center text-xs">
|
|
65
|
-
Click to play in lightbox
|
|
66
|
-
</p>
|
|
67
|
-
</div>
|
|
68
|
-
</div>
|
|
69
|
-
)
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
return <div className="border-border rounded-md border p-4">{content}</div>
|
|
73
|
-
}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import type { ComponentType } from 'react'
|
|
4
|
-
import { HeroPreview } from './HeroPreview.js'
|
|
5
|
-
import { TextPreview } from './TextPreview.js'
|
|
6
|
-
import { ImagePreview } from './ImagePreview.js'
|
|
7
|
-
import { CardsPreview } from './CardsPreview.js'
|
|
8
|
-
import { CTAPreview } from './CTAPreview.js'
|
|
9
|
-
import { VideoPreview } from './VideoPreview.js'
|
|
10
|
-
import { GalleryPreview } from './GalleryPreview.js'
|
|
11
|
-
import { FAQPreview } from './FAQPreview.js'
|
|
12
|
-
import { FormPreview } from './FormPreview.js'
|
|
13
|
-
import { CodePreview } from './CodePreview.js'
|
|
14
|
-
|
|
15
|
-
export interface BlockPreviewProps {
|
|
16
|
-
data: Record<string, unknown>
|
|
17
|
-
variant?: string
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export const blockRenderers: Record<string, ComponentType<BlockPreviewProps>> = {
|
|
21
|
-
hero: HeroPreview,
|
|
22
|
-
text: TextPreview,
|
|
23
|
-
image: ImagePreview,
|
|
24
|
-
cards: CardsPreview,
|
|
25
|
-
cta: CTAPreview,
|
|
26
|
-
video: VideoPreview,
|
|
27
|
-
gallery: GalleryPreview,
|
|
28
|
-
faq: FAQPreview,
|
|
29
|
-
form: FormPreview,
|
|
30
|
-
code: CodePreview,
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export { FallbackPreview } from './FallbackPreview.js'
|
|
34
|
-
export type { FallbackPreviewProps } from './FallbackPreview.js'
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import { useState } from 'react'
|
|
4
|
-
import type { BlockNode } from '@prenta/core'
|
|
5
|
-
import { blockRenderers, FallbackPreview } from '../block-renderers/index.js'
|
|
6
|
-
|
|
7
|
-
export interface BlockRendererProps {
|
|
8
|
-
node: BlockNode
|
|
9
|
-
selectedNodeId: string | null
|
|
10
|
-
onSelectNode: (id: string | null) => void
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function getBlockLabel(blockType: string): string {
|
|
14
|
-
return blockType.charAt(0).toUpperCase() + blockType.slice(1)
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function BlockRenderer({ node, selectedNodeId, onSelectNode }: BlockRendererProps) {
|
|
18
|
-
const [hovered, setHovered] = useState(false)
|
|
19
|
-
const isSelected = selectedNodeId === node.id
|
|
20
|
-
const blockType = node.settings.blockType
|
|
21
|
-
const label = getBlockLabel(blockType)
|
|
22
|
-
const Preview = blockRenderers[blockType]
|
|
23
|
-
|
|
24
|
-
const handleClick = (e: React.MouseEvent) => {
|
|
25
|
-
e.stopPropagation()
|
|
26
|
-
onSelectNode(node.id)
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
return (
|
|
30
|
-
<div
|
|
31
|
-
data-node-id={node.id}
|
|
32
|
-
data-block-type={blockType}
|
|
33
|
-
className={`relative min-h-[48px] transition-shadow ${
|
|
34
|
-
isSelected ? 'ring-primary ring-2 ring-offset-2' : hovered ? 'ring-primary/50 ring-1' : ''
|
|
35
|
-
}`}
|
|
36
|
-
onClick={handleClick}
|
|
37
|
-
onMouseEnter={() => setHovered(true)}
|
|
38
|
-
onMouseLeave={() => setHovered(false)}
|
|
39
|
-
>
|
|
40
|
-
{(hovered || isSelected) && (
|
|
41
|
-
<span className="bg-primary text-primary-foreground absolute -top-2 -left-1 z-10 rounded px-1.5 py-0.5 text-xs font-medium">
|
|
42
|
-
{label}
|
|
43
|
-
</span>
|
|
44
|
-
)}
|
|
45
|
-
|
|
46
|
-
{/* Previews are display-only on the canvas; the wrapper owns selection. */}
|
|
47
|
-
<div className="pointer-events-none">
|
|
48
|
-
{Preview ? (
|
|
49
|
-
<Preview data={node.data} variant={node.settings.variant} />
|
|
50
|
-
) : (
|
|
51
|
-
<FallbackPreview blockType={blockType} data={node.data} />
|
|
52
|
-
)}
|
|
53
|
-
</div>
|
|
54
|
-
</div>
|
|
55
|
-
)
|
|
56
|
-
}
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import { LayoutGrid } from 'lucide-react'
|
|
4
|
-
import type { PageNode } from '@prenta/core'
|
|
5
|
-
import { SectionRenderer } from './SectionRenderer.js'
|
|
6
|
-
|
|
7
|
-
export interface BuilderCanvasProps {
|
|
8
|
-
tree: PageNode
|
|
9
|
-
selectedNodeId: string | null
|
|
10
|
-
showGridOverlay: boolean
|
|
11
|
-
deviceMode: 'desktop' | 'tablet' | 'mobile'
|
|
12
|
-
onSelectNode: (id: string | null) => void
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const DEVICE_CLASSES: Record<BuilderCanvasProps['deviceMode'], string> = {
|
|
16
|
-
desktop: 'w-full',
|
|
17
|
-
tablet: 'max-w-[768px] mx-auto',
|
|
18
|
-
mobile: 'max-w-[375px] mx-auto',
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function BuilderCanvas({
|
|
22
|
-
tree,
|
|
23
|
-
selectedNodeId,
|
|
24
|
-
showGridOverlay,
|
|
25
|
-
deviceMode,
|
|
26
|
-
onSelectNode,
|
|
27
|
-
}: BuilderCanvasProps) {
|
|
28
|
-
const handleCanvasClick = (e: React.MouseEvent) => {
|
|
29
|
-
if (e.target === e.currentTarget) {
|
|
30
|
-
onSelectNode(null)
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
return (
|
|
35
|
-
<div className="bg-muted relative flex-1 overflow-auto p-6" onClick={handleCanvasClick}>
|
|
36
|
-
{showGridOverlay && <GridOverlay deviceMode={deviceMode} />}
|
|
37
|
-
|
|
38
|
-
<div
|
|
39
|
-
className={`bg-background relative min-h-full shadow-sm ${DEVICE_CLASSES[deviceMode]}`}
|
|
40
|
-
onClick={handleCanvasClick}
|
|
41
|
-
>
|
|
42
|
-
{tree.children.map((child) => {
|
|
43
|
-
if (child.type === 'section') {
|
|
44
|
-
return (
|
|
45
|
-
<SectionRenderer
|
|
46
|
-
key={child.id}
|
|
47
|
-
node={child}
|
|
48
|
-
selectedNodeId={selectedNodeId}
|
|
49
|
-
onSelectNode={onSelectNode}
|
|
50
|
-
/>
|
|
51
|
-
)
|
|
52
|
-
}
|
|
53
|
-
return null
|
|
54
|
-
})}
|
|
55
|
-
|
|
56
|
-
{tree.children.length === 0 && (
|
|
57
|
-
<div className="flex min-h-[400px] flex-col items-center justify-center text-center">
|
|
58
|
-
<LayoutGrid size={32} className="text-muted-foreground mb-3" />
|
|
59
|
-
<p className="text-foreground mb-1 text-sm font-medium">No sections yet</p>
|
|
60
|
-
<p className="text-muted-foreground text-xs">
|
|
61
|
-
Click "Add Section" below to start building your page
|
|
62
|
-
</p>
|
|
63
|
-
</div>
|
|
64
|
-
)}
|
|
65
|
-
</div>
|
|
66
|
-
</div>
|
|
67
|
-
)
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function GridOverlay({ deviceMode }: { deviceMode: BuilderCanvasProps['deviceMode'] }) {
|
|
71
|
-
return (
|
|
72
|
-
<div
|
|
73
|
-
className={`pointer-events-none absolute inset-0 z-50 p-6 ${
|
|
74
|
-
deviceMode !== 'desktop' ? 'flex justify-center' : ''
|
|
75
|
-
}`}
|
|
76
|
-
aria-hidden="true"
|
|
77
|
-
>
|
|
78
|
-
<div
|
|
79
|
-
className={`grid h-full grid-cols-12 gap-4 opacity-[0.08] ${DEVICE_CLASSES[deviceMode]}`}
|
|
80
|
-
>
|
|
81
|
-
{Array.from({ length: 12 }).map((_, i) => (
|
|
82
|
-
<div key={i} className="bg-primary h-full rounded-sm" />
|
|
83
|
-
))}
|
|
84
|
-
</div>
|
|
85
|
-
</div>
|
|
86
|
-
)
|
|
87
|
-
}
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import { useState } from 'react'
|
|
4
|
-
import type { ColumnNode } from '@prenta/core'
|
|
5
|
-
import { BlockRenderer } from './BlockRenderer.js'
|
|
6
|
-
import { ContainerRenderer } from './ContainerRenderer.js'
|
|
7
|
-
|
|
8
|
-
export interface ColumnRendererProps {
|
|
9
|
-
node: ColumnNode
|
|
10
|
-
selectedNodeId: string | null
|
|
11
|
-
onSelectNode: (id: string | null) => void
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export function ColumnRenderer({ node, selectedNodeId, onSelectNode }: ColumnRendererProps) {
|
|
15
|
-
const [hovered, setHovered] = useState(false)
|
|
16
|
-
const isSelected = selectedNodeId === node.id
|
|
17
|
-
const width = node.settings.width
|
|
18
|
-
const isEmpty = node.children.length === 0
|
|
19
|
-
|
|
20
|
-
const handleClick = (e: React.MouseEvent) => {
|
|
21
|
-
e.stopPropagation()
|
|
22
|
-
onSelectNode(node.id)
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return (
|
|
26
|
-
<div
|
|
27
|
-
data-node-id={node.id}
|
|
28
|
-
className={`relative transition-shadow ${
|
|
29
|
-
isSelected ? 'ring-primary ring-2 ring-offset-2' : hovered ? 'ring-primary/50 ring-1' : ''
|
|
30
|
-
}`}
|
|
31
|
-
style={{
|
|
32
|
-
gridColumn: `span ${width}`,
|
|
33
|
-
padding: node.settings.padding,
|
|
34
|
-
background: node.settings.background,
|
|
35
|
-
}}
|
|
36
|
-
onClick={handleClick}
|
|
37
|
-
onMouseEnter={(e) => {
|
|
38
|
-
e.stopPropagation()
|
|
39
|
-
setHovered(true)
|
|
40
|
-
}}
|
|
41
|
-
onMouseLeave={() => setHovered(false)}
|
|
42
|
-
>
|
|
43
|
-
{(hovered || isSelected) && (
|
|
44
|
-
<span className="bg-primary text-primary-foreground absolute -top-2 -left-1 z-10 rounded px-1.5 py-0.5 text-xs font-medium">
|
|
45
|
-
Col {width}
|
|
46
|
-
</span>
|
|
47
|
-
)}
|
|
48
|
-
|
|
49
|
-
{isEmpty ? (
|
|
50
|
-
<div className="border-border text-muted-foreground flex min-h-[64px] items-center justify-center rounded-md border-2 border-dashed text-sm">
|
|
51
|
-
Empty column
|
|
52
|
-
</div>
|
|
53
|
-
) : (
|
|
54
|
-
<div className="flex flex-col gap-2">
|
|
55
|
-
{node.children.map((child) => {
|
|
56
|
-
if (child.type === 'block') {
|
|
57
|
-
return (
|
|
58
|
-
<BlockRenderer
|
|
59
|
-
key={child.id}
|
|
60
|
-
node={child}
|
|
61
|
-
selectedNodeId={selectedNodeId}
|
|
62
|
-
onSelectNode={onSelectNode}
|
|
63
|
-
/>
|
|
64
|
-
)
|
|
65
|
-
}
|
|
66
|
-
if (child.type === 'container') {
|
|
67
|
-
return (
|
|
68
|
-
<ContainerRenderer
|
|
69
|
-
key={child.id}
|
|
70
|
-
node={child}
|
|
71
|
-
selectedNodeId={selectedNodeId}
|
|
72
|
-
onSelectNode={onSelectNode}
|
|
73
|
-
/>
|
|
74
|
-
)
|
|
75
|
-
}
|
|
76
|
-
return null
|
|
77
|
-
})}
|
|
78
|
-
</div>
|
|
79
|
-
)}
|
|
80
|
-
</div>
|
|
81
|
-
)
|
|
82
|
-
}
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import { useState } from 'react'
|
|
4
|
-
import type { ContainerNode } from '@prenta/core'
|
|
5
|
-
import { RowRenderer } from './RowRenderer.js'
|
|
6
|
-
|
|
7
|
-
export interface ContainerRendererProps {
|
|
8
|
-
node: ContainerNode
|
|
9
|
-
selectedNodeId: string | null
|
|
10
|
-
onSelectNode: (id: string | null) => void
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function ContainerRenderer({ node, selectedNodeId, onSelectNode }: ContainerRendererProps) {
|
|
14
|
-
const [hovered, setHovered] = useState(false)
|
|
15
|
-
const isSelected = selectedNodeId === node.id
|
|
16
|
-
const maxWidth = node.settings.maxWidth ?? '1200px'
|
|
17
|
-
const alignment = node.settings.alignment ?? 'center'
|
|
18
|
-
const padding = node.settings.padding
|
|
19
|
-
|
|
20
|
-
const marginMap: Record<string, string> = {
|
|
21
|
-
left: '0 auto 0 0',
|
|
22
|
-
center: '0 auto',
|
|
23
|
-
right: '0 0 0 auto',
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const handleClick = (e: React.MouseEvent) => {
|
|
27
|
-
e.stopPropagation()
|
|
28
|
-
onSelectNode(node.id)
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
return (
|
|
32
|
-
<div
|
|
33
|
-
data-node-id={node.id}
|
|
34
|
-
className={`relative transition-shadow ${
|
|
35
|
-
isSelected ? 'ring-primary ring-2 ring-offset-2' : hovered ? 'ring-primary/50 ring-1' : ''
|
|
36
|
-
}`}
|
|
37
|
-
style={{
|
|
38
|
-
maxWidth,
|
|
39
|
-
margin: marginMap[alignment] ?? '0 auto',
|
|
40
|
-
padding,
|
|
41
|
-
}}
|
|
42
|
-
onClick={handleClick}
|
|
43
|
-
onMouseEnter={(e) => {
|
|
44
|
-
e.stopPropagation()
|
|
45
|
-
setHovered(true)
|
|
46
|
-
}}
|
|
47
|
-
onMouseLeave={() => setHovered(false)}
|
|
48
|
-
>
|
|
49
|
-
{(hovered || isSelected) && (
|
|
50
|
-
<span className="bg-primary text-primary-foreground absolute -top-2 -left-1 z-10 rounded px-1.5 py-0.5 text-xs font-medium">
|
|
51
|
-
Container
|
|
52
|
-
</span>
|
|
53
|
-
)}
|
|
54
|
-
|
|
55
|
-
<div className="flex flex-col gap-4">
|
|
56
|
-
{node.children.map((row) => (
|
|
57
|
-
<RowRenderer
|
|
58
|
-
key={row.id}
|
|
59
|
-
node={row}
|
|
60
|
-
selectedNodeId={selectedNodeId}
|
|
61
|
-
onSelectNode={onSelectNode}
|
|
62
|
-
/>
|
|
63
|
-
))}
|
|
64
|
-
</div>
|
|
65
|
-
</div>
|
|
66
|
-
)
|
|
67
|
-
}
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import { useState } from 'react'
|
|
4
|
-
import type { RowNode } from '@prenta/core'
|
|
5
|
-
import { ColumnRenderer } from './ColumnRenderer.js'
|
|
6
|
-
|
|
7
|
-
export interface RowRendererProps {
|
|
8
|
-
node: RowNode
|
|
9
|
-
selectedNodeId: string | null
|
|
10
|
-
onSelectNode: (id: string | null) => void
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function RowRenderer({ node, selectedNodeId, onSelectNode }: RowRendererProps) {
|
|
14
|
-
const [hovered, setHovered] = useState(false)
|
|
15
|
-
const isSelected = selectedNodeId === node.id
|
|
16
|
-
const gap = node.settings.gap ?? '16px'
|
|
17
|
-
const verticalAlign = node.settings.verticalAlign ?? 'stretch'
|
|
18
|
-
|
|
19
|
-
const alignMap: Record<string, string> = {
|
|
20
|
-
top: 'start',
|
|
21
|
-
center: 'center',
|
|
22
|
-
bottom: 'end',
|
|
23
|
-
stretch: 'stretch',
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const handleClick = (e: React.MouseEvent) => {
|
|
27
|
-
e.stopPropagation()
|
|
28
|
-
onSelectNode(node.id)
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
return (
|
|
32
|
-
<div
|
|
33
|
-
data-node-id={node.id}
|
|
34
|
-
className={`relative transition-shadow ${
|
|
35
|
-
isSelected ? 'ring-primary ring-2 ring-offset-2' : hovered ? 'ring-primary/50 ring-1' : ''
|
|
36
|
-
}`}
|
|
37
|
-
onClick={handleClick}
|
|
38
|
-
onMouseEnter={(e) => {
|
|
39
|
-
e.stopPropagation()
|
|
40
|
-
setHovered(true)
|
|
41
|
-
}}
|
|
42
|
-
onMouseLeave={() => setHovered(false)}
|
|
43
|
-
>
|
|
44
|
-
{(hovered || isSelected) && (
|
|
45
|
-
<span className="bg-primary text-primary-foreground absolute -top-2 -left-1 z-10 rounded px-1.5 py-0.5 text-xs font-medium">
|
|
46
|
-
Row
|
|
47
|
-
</span>
|
|
48
|
-
)}
|
|
49
|
-
|
|
50
|
-
<div
|
|
51
|
-
className="grid grid-cols-12"
|
|
52
|
-
style={{
|
|
53
|
-
gap,
|
|
54
|
-
alignItems: alignMap[verticalAlign] ?? 'stretch',
|
|
55
|
-
}}
|
|
56
|
-
>
|
|
57
|
-
{node.children.map((col) => (
|
|
58
|
-
<ColumnRenderer
|
|
59
|
-
key={col.id}
|
|
60
|
-
node={col}
|
|
61
|
-
selectedNodeId={selectedNodeId}
|
|
62
|
-
onSelectNode={onSelectNode}
|
|
63
|
-
/>
|
|
64
|
-
))}
|
|
65
|
-
</div>
|
|
66
|
-
</div>
|
|
67
|
-
)
|
|
68
|
-
}
|