@prenta/admin 0.98.1 → 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.
Files changed (267) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/AdminRoot.d.ts.map +1 -1
  3. package/dist/AdminRoot.js +17 -28
  4. package/dist/AdminRoot.js.map +1 -1
  5. package/dist/__tests__/components/seo-score-bands.test.js +0 -3
  6. package/dist/__tests__/components/seo-score-bands.test.js.map +1 -1
  7. package/dist/__tests__/layout/sidebar-posts.test.js +6 -11
  8. package/dist/__tests__/layout/sidebar-posts.test.js.map +1 -1
  9. package/dist/__tests__/layout/sidebar-submenu.render.test.js +3 -2
  10. package/dist/__tests__/layout/sidebar-submenu.render.test.js.map +1 -1
  11. package/dist/__tests__/lib/page-editor-service-api.test.js +26 -23
  12. package/dist/__tests__/lib/page-editor-service-api.test.js.map +1 -1
  13. package/dist/__tests__/views/page-section-editor.test.js +0 -1
  14. package/dist/__tests__/views/page-section-editor.test.js.map +1 -1
  15. package/dist/__tests__/views/page-tab.render.test.js +0 -1
  16. package/dist/__tests__/views/page-tab.render.test.js.map +1 -1
  17. package/dist/index.d.ts +1 -4
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +1 -3
  20. package/dist/index.js.map +1 -1
  21. package/dist/layout/Sidebar.d.ts +3 -4
  22. package/dist/layout/Sidebar.d.ts.map +1 -1
  23. package/dist/layout/Sidebar.js +7 -38
  24. package/dist/layout/Sidebar.js.map +1 -1
  25. package/dist/lib/page-editor-service.d.ts +0 -7
  26. package/dist/lib/page-editor-service.d.ts.map +1 -1
  27. package/dist/lib/page-editor-service.js +9 -9
  28. package/dist/lib/page-editor-service.js.map +1 -1
  29. package/dist/prenta-admin.css +1 -1
  30. package/dist/views/PageEditor.d.ts +2 -2
  31. package/dist/views/PageEditor.js +2 -2
  32. package/dist/views/page-editor/PageSectionEditor.d.ts.map +1 -1
  33. package/dist/views/page-editor/PageSectionEditor.js +9 -15
  34. package/dist/views/page-editor/PageSectionEditor.js.map +1 -1
  35. package/package.json +4 -4
  36. package/src/AdminRoot.tsx +18 -68
  37. package/src/__tests__/components/seo-score-bands.test.ts +0 -3
  38. package/src/__tests__/layout/sidebar-posts.test.ts +6 -11
  39. package/src/__tests__/layout/sidebar-submenu.render.test.tsx +3 -2
  40. package/src/__tests__/lib/page-editor-service-api.test.ts +27 -25
  41. package/src/__tests__/views/page-section-editor.test.tsx +0 -1
  42. package/src/__tests__/views/page-tab.render.test.tsx +0 -1
  43. package/src/index.ts +1 -11
  44. package/src/layout/Sidebar.tsx +6 -41
  45. package/src/lib/page-editor-service.ts +9 -16
  46. package/src/views/PageEditor.tsx +2 -2
  47. package/src/views/page-editor/PageSectionEditor.tsx +8 -37
  48. package/dist/__tests__/views/page-builder-block-renderer.render.test.d.ts +0 -2
  49. package/dist/__tests__/views/page-builder-block-renderer.render.test.d.ts.map +0 -1
  50. package/dist/__tests__/views/page-builder-block-renderer.render.test.js +0 -48
  51. package/dist/__tests__/views/page-builder-block-renderer.render.test.js.map +0 -1
  52. package/dist/__tests__/views/page-builder-form-picker.render.test.d.ts +0 -2
  53. package/dist/__tests__/views/page-builder-form-picker.render.test.d.ts.map +0 -1
  54. package/dist/__tests__/views/page-builder-form-picker.render.test.js +0 -44
  55. package/dist/__tests__/views/page-builder-form-picker.render.test.js.map +0 -1
  56. package/dist/__tests__/views/page-builder-sections-panel.render.test.d.ts +0 -2
  57. package/dist/__tests__/views/page-builder-sections-panel.render.test.d.ts.map +0 -1
  58. package/dist/__tests__/views/page-builder-sections-panel.render.test.js +0 -121
  59. package/dist/__tests__/views/page-builder-sections-panel.render.test.js.map +0 -1
  60. package/dist/__tests__/views/page-builder-seo-panel.render.test.d.ts +0 -2
  61. package/dist/__tests__/views/page-builder-seo-panel.render.test.d.ts.map +0 -1
  62. package/dist/__tests__/views/page-builder-seo-panel.render.test.js +0 -65
  63. package/dist/__tests__/views/page-builder-seo-panel.render.test.js.map +0 -1
  64. package/dist/__tests__/views/page-builder-template-seed.render.test.d.ts +0 -2
  65. package/dist/__tests__/views/page-builder-template-seed.render.test.d.ts.map +0 -1
  66. package/dist/__tests__/views/page-builder-template-seed.render.test.js +0 -48
  67. package/dist/__tests__/views/page-builder-template-seed.render.test.js.map +0 -1
  68. package/dist/__tests__/views/page-templates.render.test.d.ts +0 -2
  69. package/dist/__tests__/views/page-templates.render.test.d.ts.map +0 -1
  70. package/dist/__tests__/views/page-templates.render.test.js +0 -126
  71. package/dist/__tests__/views/page-templates.render.test.js.map +0 -1
  72. package/dist/hooks/useBuilderState.d.ts +0 -49
  73. package/dist/hooks/useBuilderState.d.ts.map +0 -1
  74. package/dist/hooks/useBuilderState.js +0 -238
  75. package/dist/hooks/useBuilderState.js.map +0 -1
  76. package/dist/views/page-builder/AIBlockAssist.d.ts +0 -9
  77. package/dist/views/page-builder/AIBlockAssist.d.ts.map +0 -1
  78. package/dist/views/page-builder/AIBlockAssist.js +0 -40
  79. package/dist/views/page-builder/AIBlockAssist.js.map +0 -1
  80. package/dist/views/page-builder/AIGenerateDialog.d.ts +0 -8
  81. package/dist/views/page-builder/AIGenerateDialog.d.ts.map +0 -1
  82. package/dist/views/page-builder/AIGenerateDialog.js +0 -173
  83. package/dist/views/page-builder/AIGenerateDialog.js.map +0 -1
  84. package/dist/views/page-builder/BlockEditor.d.ts +0 -11
  85. package/dist/views/page-builder/BlockEditor.d.ts.map +0 -1
  86. package/dist/views/page-builder/BlockEditor.js +0 -159
  87. package/dist/views/page-builder/BlockEditor.js.map +0 -1
  88. package/dist/views/page-builder/BlockPicker.d.ts +0 -7
  89. package/dist/views/page-builder/BlockPicker.d.ts.map +0 -1
  90. package/dist/views/page-builder/BlockPicker.js +0 -103
  91. package/dist/views/page-builder/BlockPicker.js.map +0 -1
  92. package/dist/views/page-builder/BottomBar.d.ts +0 -9
  93. package/dist/views/page-builder/BottomBar.d.ts.map +0 -1
  94. package/dist/views/page-builder/BottomBar.js +0 -13
  95. package/dist/views/page-builder/BottomBar.js.map +0 -1
  96. package/dist/views/page-builder/BuilderSectionsPanel.d.ts +0 -20
  97. package/dist/views/page-builder/BuilderSectionsPanel.d.ts.map +0 -1
  98. package/dist/views/page-builder/BuilderSectionsPanel.js +0 -105
  99. package/dist/views/page-builder/BuilderSectionsPanel.js.map +0 -1
  100. package/dist/views/page-builder/BuilderToolbar.d.ts +0 -21
  101. package/dist/views/page-builder/BuilderToolbar.d.ts.map +0 -1
  102. package/dist/views/page-builder/BuilderToolbar.js +0 -18
  103. package/dist/views/page-builder/BuilderToolbar.js.map +0 -1
  104. package/dist/views/page-builder/ContextPanel.d.ts +0 -20
  105. package/dist/views/page-builder/ContextPanel.d.ts.map +0 -1
  106. package/dist/views/page-builder/ContextPanel.js +0 -43
  107. package/dist/views/page-builder/ContextPanel.js.map +0 -1
  108. package/dist/views/page-builder/DesignScore.d.ts +0 -6
  109. package/dist/views/page-builder/DesignScore.d.ts.map +0 -1
  110. package/dist/views/page-builder/DesignScore.js +0 -93
  111. package/dist/views/page-builder/DesignScore.js.map +0 -1
  112. package/dist/views/page-builder/NodeSettings.d.ts +0 -12
  113. package/dist/views/page-builder/NodeSettings.d.ts.map +0 -1
  114. package/dist/views/page-builder/NodeSettings.js +0 -80
  115. package/dist/views/page-builder/NodeSettings.js.map +0 -1
  116. package/dist/views/page-builder/PageBuilder.d.ts +0 -10
  117. package/dist/views/page-builder/PageBuilder.d.ts.map +0 -1
  118. package/dist/views/page-builder/PageBuilder.js +0 -222
  119. package/dist/views/page-builder/PageBuilder.js.map +0 -1
  120. package/dist/views/page-builder/PageSettings.d.ts +0 -7
  121. package/dist/views/page-builder/PageSettings.d.ts.map +0 -1
  122. package/dist/views/page-builder/PageSettings.js +0 -27
  123. package/dist/views/page-builder/PageSettings.js.map +0 -1
  124. package/dist/views/page-builder/PageTemplates.d.ts +0 -5
  125. package/dist/views/page-builder/PageTemplates.d.ts.map +0 -1
  126. package/dist/views/page-builder/PageTemplates.js +0 -146
  127. package/dist/views/page-builder/PageTemplates.js.map +0 -1
  128. package/dist/views/page-builder/RelationshipField.d.ts +0 -15
  129. package/dist/views/page-builder/RelationshipField.d.ts.map +0 -1
  130. package/dist/views/page-builder/RelationshipField.js +0 -65
  131. package/dist/views/page-builder/RelationshipField.js.map +0 -1
  132. package/dist/views/page-builder/SEOPanel.d.ts +0 -9
  133. package/dist/views/page-builder/SEOPanel.d.ts.map +0 -1
  134. package/dist/views/page-builder/SEOPanel.js +0 -38
  135. package/dist/views/page-builder/SEOPanel.js.map +0 -1
  136. package/dist/views/page-builder/SavedSections.d.ts +0 -6
  137. package/dist/views/page-builder/SavedSections.d.ts.map +0 -1
  138. package/dist/views/page-builder/SavedSections.js +0 -141
  139. package/dist/views/page-builder/SavedSections.js.map +0 -1
  140. package/dist/views/page-builder/TemplatePicker.d.ts +0 -7
  141. package/dist/views/page-builder/TemplatePicker.d.ts.map +0 -1
  142. package/dist/views/page-builder/TemplatePicker.js +0 -68
  143. package/dist/views/page-builder/TemplatePicker.js.map +0 -1
  144. package/dist/views/page-builder/Widgets.d.ts +0 -13
  145. package/dist/views/page-builder/Widgets.d.ts.map +0 -1
  146. package/dist/views/page-builder/Widgets.js +0 -15
  147. package/dist/views/page-builder/Widgets.js.map +0 -1
  148. package/dist/views/page-builder/block-renderers/CTAPreview.d.ts +0 -3
  149. package/dist/views/page-builder/block-renderers/CTAPreview.d.ts.map +0 -1
  150. package/dist/views/page-builder/block-renderers/CTAPreview.js +0 -19
  151. package/dist/views/page-builder/block-renderers/CTAPreview.js.map +0 -1
  152. package/dist/views/page-builder/block-renderers/CardsPreview.d.ts +0 -3
  153. package/dist/views/page-builder/block-renderers/CardsPreview.d.ts.map +0 -1
  154. package/dist/views/page-builder/block-renderers/CardsPreview.js +0 -22
  155. package/dist/views/page-builder/block-renderers/CardsPreview.js.map +0 -1
  156. package/dist/views/page-builder/block-renderers/CodePreview.d.ts +0 -3
  157. package/dist/views/page-builder/block-renderers/CodePreview.d.ts.map +0 -1
  158. package/dist/views/page-builder/block-renderers/CodePreview.js +0 -12
  159. package/dist/views/page-builder/block-renderers/CodePreview.js.map +0 -1
  160. package/dist/views/page-builder/block-renderers/FAQPreview.d.ts +0 -3
  161. package/dist/views/page-builder/block-renderers/FAQPreview.d.ts.map +0 -1
  162. package/dist/views/page-builder/block-renderers/FAQPreview.js +0 -27
  163. package/dist/views/page-builder/block-renderers/FAQPreview.js.map +0 -1
  164. package/dist/views/page-builder/block-renderers/FallbackPreview.d.ts +0 -6
  165. package/dist/views/page-builder/block-renderers/FallbackPreview.d.ts.map +0 -1
  166. package/dist/views/page-builder/block-renderers/FallbackPreview.js +0 -7
  167. package/dist/views/page-builder/block-renderers/FallbackPreview.js.map +0 -1
  168. package/dist/views/page-builder/block-renderers/FormPreview.d.ts +0 -9
  169. package/dist/views/page-builder/block-renderers/FormPreview.d.ts.map +0 -1
  170. package/dist/views/page-builder/block-renderers/FormPreview.js +0 -52
  171. package/dist/views/page-builder/block-renderers/FormPreview.js.map +0 -1
  172. package/dist/views/page-builder/block-renderers/GalleryPreview.d.ts +0 -3
  173. package/dist/views/page-builder/block-renderers/GalleryPreview.d.ts.map +0 -1
  174. package/dist/views/page-builder/block-renderers/GalleryPreview.js +0 -19
  175. package/dist/views/page-builder/block-renderers/GalleryPreview.js.map +0 -1
  176. package/dist/views/page-builder/block-renderers/HeroPreview.d.ts +0 -3
  177. package/dist/views/page-builder/block-renderers/HeroPreview.d.ts.map +0 -1
  178. package/dist/views/page-builder/block-renderers/HeroPreview.js +0 -19
  179. package/dist/views/page-builder/block-renderers/HeroPreview.js.map +0 -1
  180. package/dist/views/page-builder/block-renderers/ImagePreview.d.ts +0 -3
  181. package/dist/views/page-builder/block-renderers/ImagePreview.d.ts.map +0 -1
  182. package/dist/views/page-builder/block-renderers/ImagePreview.js +0 -17
  183. package/dist/views/page-builder/block-renderers/ImagePreview.js.map +0 -1
  184. package/dist/views/page-builder/block-renderers/TextPreview.d.ts +0 -3
  185. package/dist/views/page-builder/block-renderers/TextPreview.d.ts.map +0 -1
  186. package/dist/views/page-builder/block-renderers/TextPreview.js +0 -22
  187. package/dist/views/page-builder/block-renderers/TextPreview.js.map +0 -1
  188. package/dist/views/page-builder/block-renderers/VideoPreview.d.ts +0 -3
  189. package/dist/views/page-builder/block-renderers/VideoPreview.d.ts.map +0 -1
  190. package/dist/views/page-builder/block-renderers/VideoPreview.js +0 -18
  191. package/dist/views/page-builder/block-renderers/VideoPreview.js.map +0 -1
  192. package/dist/views/page-builder/block-renderers/index.d.ts +0 -9
  193. package/dist/views/page-builder/block-renderers/index.d.ts.map +0 -1
  194. package/dist/views/page-builder/block-renderers/index.js +0 -25
  195. package/dist/views/page-builder/block-renderers/index.js.map +0 -1
  196. package/dist/views/page-builder/canvas/BlockRenderer.d.ts +0 -8
  197. package/dist/views/page-builder/canvas/BlockRenderer.d.ts.map +0 -1
  198. package/dist/views/page-builder/canvas/BlockRenderer.js +0 -20
  199. package/dist/views/page-builder/canvas/BlockRenderer.js.map +0 -1
  200. package/dist/views/page-builder/canvas/BuilderCanvas.d.ts +0 -10
  201. package/dist/views/page-builder/canvas/BuilderCanvas.d.ts.map +0 -1
  202. package/dist/views/page-builder/canvas/BuilderCanvas.js +0 -26
  203. package/dist/views/page-builder/canvas/BuilderCanvas.js.map +0 -1
  204. package/dist/views/page-builder/canvas/ColumnRenderer.d.ts +0 -8
  205. package/dist/views/page-builder/canvas/ColumnRenderer.d.ts.map +0 -1
  206. package/dist/views/page-builder/canvas/ColumnRenderer.js +0 -32
  207. package/dist/views/page-builder/canvas/ColumnRenderer.js.map +0 -1
  208. package/dist/views/page-builder/canvas/ContainerRenderer.d.ts +0 -8
  209. package/dist/views/page-builder/canvas/ContainerRenderer.d.ts.map +0 -1
  210. package/dist/views/page-builder/canvas/ContainerRenderer.js +0 -29
  211. package/dist/views/page-builder/canvas/ContainerRenderer.js.map +0 -1
  212. package/dist/views/page-builder/canvas/RowRenderer.d.ts +0 -8
  213. package/dist/views/page-builder/canvas/RowRenderer.d.ts.map +0 -1
  214. package/dist/views/page-builder/canvas/RowRenderer.js +0 -28
  215. package/dist/views/page-builder/canvas/RowRenderer.js.map +0 -1
  216. package/dist/views/page-builder/canvas/SectionRenderer.d.ts +0 -8
  217. package/dist/views/page-builder/canvas/SectionRenderer.d.ts.map +0 -1
  218. package/dist/views/page-builder/canvas/SectionRenderer.js +0 -50
  219. package/dist/views/page-builder/canvas/SectionRenderer.js.map +0 -1
  220. package/dist/views/page-builder/canvas/index.d.ts +0 -3
  221. package/dist/views/page-builder/canvas/index.d.ts.map +0 -1
  222. package/dist/views/page-builder/canvas/index.js +0 -2
  223. package/dist/views/page-builder/canvas/index.js.map +0 -1
  224. package/src/__tests__/views/page-builder-block-renderer.render.test.tsx +0 -66
  225. package/src/__tests__/views/page-builder-form-picker.render.test.tsx +0 -54
  226. package/src/__tests__/views/page-builder-sections-panel.render.test.tsx +0 -138
  227. package/src/__tests__/views/page-builder-seo-panel.render.test.tsx +0 -77
  228. package/src/__tests__/views/page-builder-template-seed.render.test.tsx +0 -59
  229. package/src/__tests__/views/page-templates.render.test.tsx +0 -182
  230. package/src/hooks/useBuilderState.ts +0 -361
  231. package/src/views/page-builder/AIBlockAssist.tsx +0 -64
  232. package/src/views/page-builder/AIGenerateDialog.tsx +0 -593
  233. package/src/views/page-builder/BlockEditor.tsx +0 -523
  234. package/src/views/page-builder/BlockPicker.tsx +0 -324
  235. package/src/views/page-builder/BottomBar.tsx +0 -65
  236. package/src/views/page-builder/BuilderSectionsPanel.tsx +0 -304
  237. package/src/views/page-builder/BuilderToolbar.tsx +0 -221
  238. package/src/views/page-builder/ContextPanel.tsx +0 -144
  239. package/src/views/page-builder/DesignScore.tsx +0 -251
  240. package/src/views/page-builder/NodeSettings.tsx +0 -500
  241. package/src/views/page-builder/PageBuilder.tsx +0 -405
  242. package/src/views/page-builder/PageSettings.tsx +0 -162
  243. package/src/views/page-builder/PageTemplates.tsx +0 -435
  244. package/src/views/page-builder/RelationshipField.tsx +0 -113
  245. package/src/views/page-builder/SEOPanel.tsx +0 -222
  246. package/src/views/page-builder/SavedSections.tsx +0 -480
  247. package/src/views/page-builder/TemplatePicker.tsx +0 -197
  248. package/src/views/page-builder/Widgets.tsx +0 -44
  249. package/src/views/page-builder/block-renderers/CTAPreview.tsx +0 -79
  250. package/src/views/page-builder/block-renderers/CardsPreview.tsx +0 -69
  251. package/src/views/page-builder/block-renderers/CodePreview.tsx +0 -46
  252. package/src/views/page-builder/block-renderers/FAQPreview.tsx +0 -87
  253. package/src/views/page-builder/block-renderers/FallbackPreview.tsx +0 -18
  254. package/src/views/page-builder/block-renderers/FormPreview.tsx +0 -117
  255. package/src/views/page-builder/block-renderers/GalleryPreview.tsx +0 -87
  256. package/src/views/page-builder/block-renderers/HeroPreview.tsx +0 -90
  257. package/src/views/page-builder/block-renderers/ImagePreview.tsx +0 -54
  258. package/src/views/page-builder/block-renderers/TextPreview.tsx +0 -81
  259. package/src/views/page-builder/block-renderers/VideoPreview.tsx +0 -73
  260. package/src/views/page-builder/block-renderers/index.ts +0 -34
  261. package/src/views/page-builder/canvas/BlockRenderer.tsx +0 -56
  262. package/src/views/page-builder/canvas/BuilderCanvas.tsx +0 -87
  263. package/src/views/page-builder/canvas/ColumnRenderer.tsx +0 -82
  264. package/src/views/page-builder/canvas/ContainerRenderer.tsx +0 -67
  265. package/src/views/page-builder/canvas/RowRenderer.tsx +0 -68
  266. package/src/views/page-builder/canvas/SectionRenderer.tsx +0 -93
  267. package/src/views/page-builder/canvas/index.ts +0 -2
@@ -1,500 +0,0 @@
1
- 'use client'
2
-
3
- import { useState, useCallback } from 'react'
4
- import {
5
- ArrowUp,
6
- ArrowDown,
7
- Copy,
8
- Trash2,
9
- Plus,
10
- AlignLeft,
11
- AlignCenter,
12
- AlignRight,
13
- ArrowUpFromLine,
14
- ArrowDownToLine,
15
- Minus,
16
- } from 'lucide-react'
17
- import { Toggle } from '../../components/ui/Toggle.js'
18
- import type { BuilderNode, SectionNode, ContainerNode, RowNode, ColumnNode } from '@prenta/core'
19
-
20
- export interface NodeSettingsProps {
21
- node: SectionNode | ContainerNode | RowNode | ColumnNode
22
- onUpdateSettings: (id: string, settings: Record<string, unknown>) => void
23
- onRemoveNode: (id: string) => void
24
- onDuplicateNode: (id: string) => void
25
- onMoveNodeUp: (id: string) => void
26
- onMoveNodeDown: (id: string) => void
27
- onAddRow?: (sectionId: string) => void
28
- }
29
-
30
- const INPUT_CLASS =
31
- 'w-full px-3 py-2 text-sm bg-background border border-input rounded-md focus:outline-none focus:ring-2 focus:ring-ring'
32
- const LABEL_CLASS = 'text-sm font-medium text-foreground mb-1 block'
33
- const SECTION_HEADING_CLASS =
34
- 'text-xs font-medium uppercase tracking-wider text-muted-foreground mb-2'
35
-
36
- const COLUMN_PRESETS: { label: string; widths: number[] }[] = [
37
- { label: '12', widths: [12] },
38
- { label: '6 | 6', widths: [6, 6] },
39
- { label: '4 | 4 | 4', widths: [4, 4, 4] },
40
- { label: '3 | 3 | 3 | 3', widths: [3, 3, 3, 3] },
41
- { label: '8 | 4', widths: [8, 4] },
42
- { label: '4 | 8', widths: [4, 8] },
43
- { label: '3 | 6 | 3', widths: [3, 6, 3] },
44
- ]
45
-
46
- export function NodeSettings({
47
- node,
48
- onUpdateSettings,
49
- onRemoveNode,
50
- onDuplicateNode,
51
- onMoveNodeUp,
52
- onMoveNodeDown,
53
- onAddRow,
54
- }: NodeSettingsProps) {
55
- const [confirmDelete, setConfirmDelete] = useState(false)
56
-
57
- const updateSetting = useCallback(
58
- (key: string, value: unknown) => {
59
- onUpdateSettings(node.id, { [key]: value })
60
- },
61
- [node.id, onUpdateSettings],
62
- )
63
-
64
- const handleDelete = useCallback(() => {
65
- if (confirmDelete) {
66
- onRemoveNode(node.id)
67
- setConfirmDelete(false)
68
- } else {
69
- setConfirmDelete(true)
70
- }
71
- }, [confirmDelete, node.id, onRemoveNode])
72
-
73
- const nodeLabel =
74
- node.type === 'section'
75
- ? 'Section'
76
- : node.type === 'container'
77
- ? 'Container'
78
- : node.type === 'row'
79
- ? 'Row'
80
- : 'Column'
81
-
82
- return (
83
- <div className="flex h-full flex-col">
84
- <div className="border-border border-b p-4">
85
- <p className="text-foreground text-sm font-medium">{nodeLabel} Settings</p>
86
- </div>
87
-
88
- <div className="flex-1 overflow-y-auto">
89
- <div className="space-y-4 p-4">
90
- {node.type === 'section' && (
91
- <SectionFields node={node} updateSetting={updateSetting} onAddRow={onAddRow} />
92
- )}
93
- {node.type === 'container' && (
94
- <ContainerFields node={node} updateSetting={updateSetting} />
95
- )}
96
- {node.type === 'row' && <RowFields node={node} updateSetting={updateSetting} />}
97
- {node.type === 'column' && <ColumnFields node={node} updateSetting={updateSetting} />}
98
- </div>
99
- </div>
100
-
101
- <div className="border-border space-y-2 border-t p-4">
102
- <p className={SECTION_HEADING_CLASS}>Actions</p>
103
- <div className="flex gap-2">
104
- <button
105
- type="button"
106
- onClick={() => onMoveNodeUp(node.id)}
107
- aria-label="Move up"
108
- className="bg-background border-input hover:bg-accent flex flex-1 items-center justify-center gap-1.5 rounded-md border px-3 py-2 text-sm transition-colors"
109
- >
110
- <ArrowUp size={14} />
111
- Up
112
- </button>
113
- <button
114
- type="button"
115
- onClick={() => onMoveNodeDown(node.id)}
116
- aria-label="Move down"
117
- className="bg-background border-input hover:bg-accent flex flex-1 items-center justify-center gap-1.5 rounded-md border px-3 py-2 text-sm transition-colors"
118
- >
119
- <ArrowDown size={14} />
120
- Down
121
- </button>
122
- </div>
123
- <button
124
- type="button"
125
- onClick={() => onDuplicateNode(node.id)}
126
- className="bg-background border-input hover:bg-accent flex w-full items-center justify-center gap-2 rounded-md border px-3 py-2 text-sm font-medium transition-colors"
127
- >
128
- <Copy size={14} />
129
- Duplicate
130
- </button>
131
- <button
132
- type="button"
133
- onClick={handleDelete}
134
- onBlur={() => setConfirmDelete(false)}
135
- className={`flex w-full items-center justify-center gap-2 rounded-md px-3 py-2 text-sm font-medium transition-colors ${
136
- confirmDelete
137
- ? 'bg-destructive text-destructive-foreground'
138
- : 'bg-background border-destructive text-destructive hover:bg-destructive/10 border'
139
- }`}
140
- >
141
- <Trash2 size={14} />
142
- {confirmDelete ? 'Click again to confirm' : 'Delete'}
143
- </button>
144
- </div>
145
- </div>
146
- )
147
- }
148
-
149
- function SectionFields({
150
- node,
151
- updateSetting,
152
- onAddRow,
153
- }: {
154
- node: SectionNode
155
- updateSetting: (key: string, value: unknown) => void
156
- onAddRow?: (sectionId: string) => void
157
- }) {
158
- const s = node.settings
159
- return (
160
- <>
161
- <p className={SECTION_HEADING_CLASS}>Background</p>
162
- <div>
163
- <label className={LABEL_CLASS}>Background Color</label>
164
- <div className="flex items-center gap-2">
165
- <input
166
- type="color"
167
- value={s.background ?? '#ffffff'}
168
- onChange={(e) => updateSetting('background', e.target.value)}
169
- className="border-input h-9 w-9 cursor-pointer rounded-md border"
170
- />
171
- <input
172
- type="text"
173
- value={s.background ?? ''}
174
- onChange={(e) => updateSetting('background', e.target.value)}
175
- placeholder="transparent"
176
- className={`${INPUT_CLASS} flex-1`}
177
- />
178
- </div>
179
- </div>
180
-
181
- <p className={SECTION_HEADING_CLASS}>Spacing</p>
182
- <div className="grid grid-cols-2 gap-3">
183
- <div>
184
- <label className={LABEL_CLASS}>Padding Top</label>
185
- <input
186
- type="text"
187
- value={s.paddingTop ?? ''}
188
- onChange={(e) => updateSetting('paddingTop', e.target.value)}
189
- placeholder="0px"
190
- className={INPUT_CLASS}
191
- />
192
- </div>
193
- <div>
194
- <label className={LABEL_CLASS}>Padding Bottom</label>
195
- <input
196
- type="text"
197
- value={s.paddingBottom ?? ''}
198
- onChange={(e) => updateSetting('paddingBottom', e.target.value)}
199
- placeholder="0px"
200
- className={INPUT_CLASS}
201
- />
202
- </div>
203
- <div>
204
- <label className={LABEL_CLASS}>Margin Top</label>
205
- <input
206
- type="text"
207
- value={s.marginTop ?? ''}
208
- onChange={(e) => updateSetting('marginTop', e.target.value)}
209
- placeholder="0px"
210
- className={INPUT_CLASS}
211
- />
212
- </div>
213
- <div>
214
- <label className={LABEL_CLASS}>Margin Bottom</label>
215
- <input
216
- type="text"
217
- value={s.marginBottom ?? ''}
218
- onChange={(e) => updateSetting('marginBottom', e.target.value)}
219
- placeholder="0px"
220
- className={INPUT_CLASS}
221
- />
222
- </div>
223
- </div>
224
-
225
- <p className={SECTION_HEADING_CLASS}>Attributes</p>
226
- <div className="flex items-center justify-between">
227
- <label className="text-foreground text-sm font-medium">Visibility</label>
228
- <Toggle
229
- checked={s.visibility !== 'hidden'}
230
- onCheckedChange={(checked) => updateSetting('visibility', checked ? 'visible' : 'hidden')}
231
- aria-label="Section visibility"
232
- />
233
- </div>
234
- <div>
235
- <label className={LABEL_CLASS}>HTML ID</label>
236
- <input
237
- type="text"
238
- value={s.htmlId ?? ''}
239
- onChange={(e) => updateSetting('htmlId', e.target.value)}
240
- placeholder="section-id"
241
- className={INPUT_CLASS}
242
- />
243
- </div>
244
- <div>
245
- <label className={LABEL_CLASS}>HTML Class</label>
246
- <input
247
- type="text"
248
- value={s.htmlClass ?? ''}
249
- onChange={(e) => updateSetting('htmlClass', e.target.value)}
250
- placeholder="my-class"
251
- className={INPUT_CLASS}
252
- />
253
- </div>
254
-
255
- {onAddRow && (
256
- <button
257
- type="button"
258
- onClick={() => onAddRow(node.id)}
259
- className="bg-primary text-primary-foreground hover:bg-primary/90 flex w-full items-center justify-center gap-2 rounded-md px-3 py-2 text-sm font-medium transition-colors"
260
- >
261
- <Plus size={14} />
262
- Add Row
263
- </button>
264
- )}
265
- </>
266
- )
267
- }
268
-
269
- function ContainerFields({
270
- node,
271
- updateSetting,
272
- }: {
273
- node: ContainerNode
274
- updateSetting: (key: string, value: unknown) => void
275
- }) {
276
- const s = node.settings
277
- return (
278
- <>
279
- <p className={SECTION_HEADING_CLASS}>Layout</p>
280
- <div>
281
- <label className={LABEL_CLASS}>Max Width</label>
282
- <input
283
- type="text"
284
- value={s.maxWidth ?? ''}
285
- onChange={(e) => updateSetting('maxWidth', e.target.value)}
286
- placeholder="1200px"
287
- className={INPUT_CLASS}
288
- />
289
- </div>
290
- <div>
291
- <label className={LABEL_CLASS}>Alignment</label>
292
- <div className="flex gap-1">
293
- {(['left', 'center', 'right'] as const).map((align) => {
294
- const Icon =
295
- align === 'left' ? AlignLeft : align === 'center' ? AlignCenter : AlignRight
296
- return (
297
- <button
298
- key={align}
299
- type="button"
300
- onClick={() => updateSetting('alignment', align)}
301
- aria-label={`Align ${align}`}
302
- className={`flex flex-1 items-center justify-center rounded-md border py-2 transition-colors ${
303
- s.alignment === align
304
- ? 'bg-primary text-primary-foreground border-primary'
305
- : 'bg-background border-input hover:bg-accent'
306
- }`}
307
- >
308
- <Icon size={14} />
309
- </button>
310
- )
311
- })}
312
- </div>
313
- </div>
314
- <div>
315
- <label className={LABEL_CLASS}>Padding</label>
316
- <input
317
- type="text"
318
- value={s.padding ?? ''}
319
- onChange={(e) => updateSetting('padding', e.target.value)}
320
- placeholder="0px"
321
- className={INPUT_CLASS}
322
- />
323
- </div>
324
- </>
325
- )
326
- }
327
-
328
- function RowFields({
329
- node,
330
- updateSetting,
331
- }: {
332
- node: RowNode
333
- updateSetting: (key: string, value: unknown) => void
334
- }) {
335
- const s = node.settings
336
- return (
337
- <>
338
- <p className={SECTION_HEADING_CLASS}>Layout</p>
339
- <div>
340
- <label className={LABEL_CLASS}>Gap</label>
341
- <input
342
- type="text"
343
- value={s.gap ?? ''}
344
- onChange={(e) => updateSetting('gap', e.target.value)}
345
- placeholder="16px"
346
- className={INPUT_CLASS}
347
- />
348
- </div>
349
- <div>
350
- <label className={LABEL_CLASS}>Vertical Align</label>
351
- <div className="flex gap-1">
352
- {(['top', 'center', 'bottom', 'stretch'] as const).map((align) => {
353
- const Icon =
354
- align === 'top'
355
- ? ArrowUpFromLine
356
- : align === 'bottom'
357
- ? ArrowDownToLine
358
- : align === 'center'
359
- ? Minus
360
- : AlignCenter
361
- return (
362
- <button
363
- key={align}
364
- type="button"
365
- onClick={() => updateSetting('verticalAlign', align)}
366
- aria-label={`Align ${align}`}
367
- className={`flex flex-1 items-center justify-center rounded-md border py-2 text-xs transition-colors ${
368
- s.verticalAlign === align
369
- ? 'bg-primary text-primary-foreground border-primary'
370
- : 'bg-background border-input hover:bg-accent'
371
- }`}
372
- >
373
- <Icon size={14} />
374
- </button>
375
- )
376
- })}
377
- </div>
378
- </div>
379
-
380
- <p className={SECTION_HEADING_CLASS}>Mobile</p>
381
- <div className="flex items-center justify-between">
382
- <label className="text-foreground text-sm font-medium">Reverse on Mobile</label>
383
- <Toggle
384
- checked={!!s.reverseOnMobile}
385
- onCheckedChange={(checked) => updateSetting('reverseOnMobile', checked)}
386
- aria-label="Reverse column order on mobile"
387
- />
388
- </div>
389
- <div className="flex items-center justify-between">
390
- <label className="text-foreground text-sm font-medium">Wrap on Mobile</label>
391
- <Toggle
392
- checked={!!s.wrapOnMobile}
393
- onCheckedChange={(checked) => updateSetting('wrapOnMobile', checked)}
394
- aria-label="Wrap columns on mobile"
395
- />
396
- </div>
397
-
398
- <p className={SECTION_HEADING_CLASS}>Column Presets</p>
399
- <div className="grid grid-cols-2 gap-1.5">
400
- {COLUMN_PRESETS.map((preset) => (
401
- <button
402
- key={preset.label}
403
- type="button"
404
- onClick={() => updateSetting('__columnPreset', preset.widths)}
405
- className="border-input bg-background hover:bg-accent rounded-md border px-2 py-2 transition-colors"
406
- >
407
- <div className="flex h-4 gap-0.5">
408
- {preset.widths.map((w, i) => (
409
- <div key={i} className="bg-muted-foreground/30 rounded-sm" style={{ flex: w }} />
410
- ))}
411
- </div>
412
- <p className="text-muted-foreground mt-1 text-center text-xs">
413
- {preset.widths.join(' | ')}
414
- </p>
415
- </button>
416
- ))}
417
- </div>
418
- </>
419
- )
420
- }
421
-
422
- function ColumnFields({
423
- node,
424
- updateSetting,
425
- }: {
426
- node: ColumnNode
427
- updateSetting: (key: string, value: unknown) => void
428
- }) {
429
- const s = node.settings
430
- return (
431
- <>
432
- <p className={SECTION_HEADING_CLASS}>Size</p>
433
- <div>
434
- <label className={LABEL_CLASS}>Width (1-12)</label>
435
- <input
436
- type="number"
437
- min={1}
438
- max={12}
439
- value={s.width}
440
- onChange={(e) => updateSetting('width', Number(e.target.value))}
441
- className={INPUT_CLASS}
442
- />
443
- </div>
444
-
445
- <p className={SECTION_HEADING_CLASS}>Layout</p>
446
- <div>
447
- <label className={LABEL_CLASS}>Vertical Align</label>
448
- <div className="flex gap-1">
449
- {(['top', 'center', 'bottom'] as const).map((align) => {
450
- const Icon =
451
- align === 'top' ? ArrowUpFromLine : align === 'bottom' ? ArrowDownToLine : Minus
452
- return (
453
- <button
454
- key={align}
455
- type="button"
456
- onClick={() => updateSetting('verticalAlign', align)}
457
- aria-label={`Align ${align}`}
458
- className={`flex flex-1 items-center justify-center rounded-md border py-2 transition-colors ${
459
- s.verticalAlign === align
460
- ? 'bg-primary text-primary-foreground border-primary'
461
- : 'bg-background border-input hover:bg-accent'
462
- }`}
463
- >
464
- <Icon size={14} />
465
- </button>
466
- )
467
- })}
468
- </div>
469
- </div>
470
- <div>
471
- <label className={LABEL_CLASS}>Padding</label>
472
- <input
473
- type="text"
474
- value={s.padding ?? ''}
475
- onChange={(e) => updateSetting('padding', e.target.value)}
476
- placeholder="0px"
477
- className={INPUT_CLASS}
478
- />
479
- </div>
480
- <div>
481
- <label className={LABEL_CLASS}>Background Color</label>
482
- <div className="flex items-center gap-2">
483
- <input
484
- type="color"
485
- value={s.background ?? '#ffffff'}
486
- onChange={(e) => updateSetting('background', e.target.value)}
487
- className="border-input h-9 w-9 cursor-pointer rounded-md border"
488
- />
489
- <input
490
- type="text"
491
- value={s.background ?? ''}
492
- onChange={(e) => updateSetting('background', e.target.value)}
493
- placeholder="transparent"
494
- className={`${INPUT_CLASS} flex-1`}
495
- />
496
- </div>
497
- </div>
498
- </>
499
- )
500
- }