@prenta/admin 0.98.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (371) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/AdminRoot.d.ts.map +1 -1
  3. package/dist/AdminRoot.js +22 -84
  4. package/dist/AdminRoot.js.map +1 -1
  5. package/dist/__tests__/components/breakpoint-controls.render.test.js +4 -4
  6. package/dist/__tests__/components/breakpoint-controls.render.test.js.map +1 -1
  7. package/dist/__tests__/components/seo-score-bands.test.js +0 -3
  8. package/dist/__tests__/components/seo-score-bands.test.js.map +1 -1
  9. package/dist/__tests__/layout/sidebar-posts.test.js +6 -11
  10. package/dist/__tests__/layout/sidebar-posts.test.js.map +1 -1
  11. package/dist/__tests__/layout/sidebar-submenu.render.test.js +3 -2
  12. package/dist/__tests__/layout/sidebar-submenu.render.test.js.map +1 -1
  13. package/dist/__tests__/lib/open-public-site-tab.test.d.ts +2 -0
  14. package/dist/__tests__/lib/open-public-site-tab.test.d.ts.map +1 -0
  15. package/dist/__tests__/lib/open-public-site-tab.test.js +64 -0
  16. package/dist/__tests__/lib/open-public-site-tab.test.js.map +1 -0
  17. package/dist/__tests__/lib/page-editor-service-api.test.js +26 -23
  18. package/dist/__tests__/lib/page-editor-service-api.test.js.map +1 -1
  19. package/dist/__tests__/lib/view-site-chrome.test.d.ts +2 -0
  20. package/dist/__tests__/lib/view-site-chrome.test.d.ts.map +1 -0
  21. package/dist/__tests__/lib/view-site-chrome.test.js +59 -0
  22. package/dist/__tests__/lib/view-site-chrome.test.js.map +1 -0
  23. package/dist/__tests__/router/admin-routes.test.js +4 -0
  24. package/dist/__tests__/router/admin-routes.test.js.map +1 -1
  25. package/dist/__tests__/views/canvas-surface-shared.test.js +0 -16
  26. package/dist/__tests__/views/canvas-surface-shared.test.js.map +1 -1
  27. package/dist/__tests__/views/editor-top-bar.render.test.js +44 -22
  28. package/dist/__tests__/views/editor-top-bar.render.test.js.map +1 -1
  29. package/dist/__tests__/views/page-section-editor.test.js +30 -10
  30. package/dist/__tests__/views/page-section-editor.test.js.map +1 -1
  31. package/dist/__tests__/views/page-tab.render.test.js +0 -1
  32. package/dist/__tests__/views/page-tab.render.test.js.map +1 -1
  33. package/dist/__tests__/views/post-section-editor.test.js +23 -0
  34. package/dist/__tests__/views/post-section-editor.test.js.map +1 -1
  35. package/dist/components/BreakpointPicker.d.ts +3 -2
  36. package/dist/components/BreakpointPicker.d.ts.map +1 -1
  37. package/dist/components/BreakpointPicker.js +5 -3
  38. package/dist/components/BreakpointPicker.js.map +1 -1
  39. package/dist/components/CanvasEditTip.js +1 -1
  40. package/dist/components/CanvasEditTip.js.map +1 -1
  41. package/dist/index.d.ts +1 -4
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +1 -3
  44. package/dist/index.js.map +1 -1
  45. package/dist/layout/Sidebar.d.ts +3 -4
  46. package/dist/layout/Sidebar.d.ts.map +1 -1
  47. package/dist/layout/Sidebar.js +7 -38
  48. package/dist/layout/Sidebar.js.map +1 -1
  49. package/dist/lib/editor-preview.d.ts +4 -1
  50. package/dist/lib/editor-preview.d.ts.map +1 -1
  51. package/dist/lib/editor-preview.js +4 -1
  52. package/dist/lib/editor-preview.js.map +1 -1
  53. package/dist/lib/open-public-site-tab.d.ts +14 -0
  54. package/dist/lib/open-public-site-tab.d.ts.map +1 -0
  55. package/dist/lib/open-public-site-tab.js +46 -0
  56. package/dist/lib/open-public-site-tab.js.map +1 -0
  57. package/dist/lib/page-editor-service.d.ts +0 -7
  58. package/dist/lib/page-editor-service.d.ts.map +1 -1
  59. package/dist/lib/page-editor-service.js +9 -9
  60. package/dist/lib/page-editor-service.js.map +1 -1
  61. package/dist/lib/view-site-chrome.d.ts +16 -0
  62. package/dist/lib/view-site-chrome.d.ts.map +1 -0
  63. package/dist/lib/view-site-chrome.js +15 -0
  64. package/dist/lib/view-site-chrome.js.map +1 -0
  65. package/dist/prenta-admin.css +1 -1
  66. package/dist/router/admin-routes.d.ts.map +1 -1
  67. package/dist/router/admin-routes.js +0 -2
  68. package/dist/router/admin-routes.js.map +1 -1
  69. package/dist/views/PageEditor.d.ts +2 -2
  70. package/dist/views/PageEditor.js +2 -2
  71. package/dist/views/page-editor/EditorCanvas.d.ts +1 -2
  72. package/dist/views/page-editor/EditorCanvas.d.ts.map +1 -1
  73. package/dist/views/page-editor/EditorCanvas.js +2 -2
  74. package/dist/views/page-editor/EditorCanvas.js.map +1 -1
  75. package/dist/views/page-editor/EditorTopBar.d.ts +13 -17
  76. package/dist/views/page-editor/EditorTopBar.d.ts.map +1 -1
  77. package/dist/views/page-editor/EditorTopBar.js +19 -21
  78. package/dist/views/page-editor/EditorTopBar.js.map +1 -1
  79. package/dist/views/page-editor/PageSectionEditor.d.ts +1 -7
  80. package/dist/views/page-editor/PageSectionEditor.d.ts.map +1 -1
  81. package/dist/views/page-editor/PageSectionEditor.js +38 -110
  82. package/dist/views/page-editor/PageSectionEditor.js.map +1 -1
  83. package/dist/views/page-editor/StructureNotice.d.ts +2 -5
  84. package/dist/views/page-editor/StructureNotice.d.ts.map +1 -1
  85. package/dist/views/page-editor/StructureNotice.js +6 -10
  86. package/dist/views/page-editor/StructureNotice.js.map +1 -1
  87. package/dist/views/post-editor/PostEditorCanvas.d.ts +1 -3
  88. package/dist/views/post-editor/PostEditorCanvas.d.ts.map +1 -1
  89. package/dist/views/post-editor/PostEditorCanvas.js +3 -3
  90. package/dist/views/post-editor/PostEditorCanvas.js.map +1 -1
  91. package/dist/views/post-editor/PostSectionEditor.d.ts +1 -15
  92. package/dist/views/post-editor/PostSectionEditor.d.ts.map +1 -1
  93. package/dist/views/post-editor/PostSectionEditor.js +30 -88
  94. package/dist/views/post-editor/PostSectionEditor.js.map +1 -1
  95. package/package.json +3 -3
  96. package/src/AdminRoot.tsx +18 -138
  97. package/src/__tests__/components/breakpoint-controls.render.test.tsx +6 -4
  98. package/src/__tests__/components/seo-score-bands.test.ts +0 -3
  99. package/src/__tests__/layout/sidebar-posts.test.ts +6 -11
  100. package/src/__tests__/layout/sidebar-submenu.render.test.tsx +3 -2
  101. package/src/__tests__/lib/open-public-site-tab.test.ts +74 -0
  102. package/src/__tests__/lib/page-editor-service-api.test.ts +27 -25
  103. package/src/__tests__/lib/view-site-chrome.test.ts +65 -0
  104. package/src/__tests__/router/admin-routes.test.ts +5 -0
  105. package/src/__tests__/views/canvas-surface-shared.test.ts +0 -19
  106. package/src/__tests__/views/editor-top-bar.render.test.tsx +85 -22
  107. package/src/__tests__/views/page-section-editor.test.tsx +42 -10
  108. package/src/__tests__/views/page-tab.render.test.tsx +0 -1
  109. package/src/__tests__/views/post-section-editor.test.tsx +35 -0
  110. package/src/components/BreakpointPicker.tsx +6 -3
  111. package/src/components/CanvasEditTip.tsx +2 -2
  112. package/src/index.ts +1 -11
  113. package/src/layout/Sidebar.tsx +6 -41
  114. package/src/lib/editor-preview.ts +4 -1
  115. package/src/lib/open-public-site-tab.ts +57 -0
  116. package/src/lib/page-editor-service.ts +9 -16
  117. package/src/lib/view-site-chrome.ts +34 -0
  118. package/src/router/admin-routes.ts +0 -2
  119. package/src/styles/theme.css +4 -4
  120. package/src/views/PageEditor.tsx +2 -2
  121. package/src/views/page-editor/EditorCanvas.tsx +1 -6
  122. package/src/views/page-editor/EditorTopBar.tsx +81 -82
  123. package/src/views/page-editor/PageSectionEditor.tsx +85 -194
  124. package/src/views/page-editor/StructureNotice.tsx +7 -42
  125. package/src/views/post-editor/PostEditorCanvas.tsx +2 -8
  126. package/src/views/post-editor/PostSectionEditor.tsx +66 -138
  127. package/dist/__tests__/views/page-builder-block-renderer.render.test.d.ts +0 -2
  128. package/dist/__tests__/views/page-builder-block-renderer.render.test.d.ts.map +0 -1
  129. package/dist/__tests__/views/page-builder-block-renderer.render.test.js +0 -48
  130. package/dist/__tests__/views/page-builder-block-renderer.render.test.js.map +0 -1
  131. package/dist/__tests__/views/page-builder-form-picker.render.test.d.ts +0 -2
  132. package/dist/__tests__/views/page-builder-form-picker.render.test.d.ts.map +0 -1
  133. package/dist/__tests__/views/page-builder-form-picker.render.test.js +0 -44
  134. package/dist/__tests__/views/page-builder-form-picker.render.test.js.map +0 -1
  135. package/dist/__tests__/views/page-builder-sections-panel.render.test.d.ts +0 -2
  136. package/dist/__tests__/views/page-builder-sections-panel.render.test.d.ts.map +0 -1
  137. package/dist/__tests__/views/page-builder-sections-panel.render.test.js +0 -121
  138. package/dist/__tests__/views/page-builder-sections-panel.render.test.js.map +0 -1
  139. package/dist/__tests__/views/page-builder-seo-panel.render.test.d.ts +0 -2
  140. package/dist/__tests__/views/page-builder-seo-panel.render.test.d.ts.map +0 -1
  141. package/dist/__tests__/views/page-builder-seo-panel.render.test.js +0 -65
  142. package/dist/__tests__/views/page-builder-seo-panel.render.test.js.map +0 -1
  143. package/dist/__tests__/views/page-builder-template-seed.render.test.d.ts +0 -2
  144. package/dist/__tests__/views/page-builder-template-seed.render.test.d.ts.map +0 -1
  145. package/dist/__tests__/views/page-builder-template-seed.render.test.js +0 -48
  146. package/dist/__tests__/views/page-builder-template-seed.render.test.js.map +0 -1
  147. package/dist/__tests__/views/page-templates.render.test.d.ts +0 -2
  148. package/dist/__tests__/views/page-templates.render.test.d.ts.map +0 -1
  149. package/dist/__tests__/views/page-templates.render.test.js +0 -126
  150. package/dist/__tests__/views/page-templates.render.test.js.map +0 -1
  151. package/dist/__tests__/views/use-site-preview.render.test.d.ts +0 -2
  152. package/dist/__tests__/views/use-site-preview.render.test.d.ts.map +0 -1
  153. package/dist/__tests__/views/use-site-preview.render.test.js +0 -120
  154. package/dist/__tests__/views/use-site-preview.render.test.js.map +0 -1
  155. package/dist/hooks/useBuilderState.d.ts +0 -49
  156. package/dist/hooks/useBuilderState.d.ts.map +0 -1
  157. package/dist/hooks/useBuilderState.js +0 -238
  158. package/dist/hooks/useBuilderState.js.map +0 -1
  159. package/dist/views/page-builder/AIBlockAssist.d.ts +0 -9
  160. package/dist/views/page-builder/AIBlockAssist.d.ts.map +0 -1
  161. package/dist/views/page-builder/AIBlockAssist.js +0 -40
  162. package/dist/views/page-builder/AIBlockAssist.js.map +0 -1
  163. package/dist/views/page-builder/AIGenerateDialog.d.ts +0 -8
  164. package/dist/views/page-builder/AIGenerateDialog.d.ts.map +0 -1
  165. package/dist/views/page-builder/AIGenerateDialog.js +0 -173
  166. package/dist/views/page-builder/AIGenerateDialog.js.map +0 -1
  167. package/dist/views/page-builder/BlockEditor.d.ts +0 -11
  168. package/dist/views/page-builder/BlockEditor.d.ts.map +0 -1
  169. package/dist/views/page-builder/BlockEditor.js +0 -159
  170. package/dist/views/page-builder/BlockEditor.js.map +0 -1
  171. package/dist/views/page-builder/BlockPicker.d.ts +0 -7
  172. package/dist/views/page-builder/BlockPicker.d.ts.map +0 -1
  173. package/dist/views/page-builder/BlockPicker.js +0 -103
  174. package/dist/views/page-builder/BlockPicker.js.map +0 -1
  175. package/dist/views/page-builder/BottomBar.d.ts +0 -9
  176. package/dist/views/page-builder/BottomBar.d.ts.map +0 -1
  177. package/dist/views/page-builder/BottomBar.js +0 -13
  178. package/dist/views/page-builder/BottomBar.js.map +0 -1
  179. package/dist/views/page-builder/BuilderSectionsPanel.d.ts +0 -20
  180. package/dist/views/page-builder/BuilderSectionsPanel.d.ts.map +0 -1
  181. package/dist/views/page-builder/BuilderSectionsPanel.js +0 -105
  182. package/dist/views/page-builder/BuilderSectionsPanel.js.map +0 -1
  183. package/dist/views/page-builder/BuilderToolbar.d.ts +0 -21
  184. package/dist/views/page-builder/BuilderToolbar.d.ts.map +0 -1
  185. package/dist/views/page-builder/BuilderToolbar.js +0 -18
  186. package/dist/views/page-builder/BuilderToolbar.js.map +0 -1
  187. package/dist/views/page-builder/ContextPanel.d.ts +0 -20
  188. package/dist/views/page-builder/ContextPanel.d.ts.map +0 -1
  189. package/dist/views/page-builder/ContextPanel.js +0 -43
  190. package/dist/views/page-builder/ContextPanel.js.map +0 -1
  191. package/dist/views/page-builder/DesignScore.d.ts +0 -6
  192. package/dist/views/page-builder/DesignScore.d.ts.map +0 -1
  193. package/dist/views/page-builder/DesignScore.js +0 -93
  194. package/dist/views/page-builder/DesignScore.js.map +0 -1
  195. package/dist/views/page-builder/NodeSettings.d.ts +0 -12
  196. package/dist/views/page-builder/NodeSettings.d.ts.map +0 -1
  197. package/dist/views/page-builder/NodeSettings.js +0 -80
  198. package/dist/views/page-builder/NodeSettings.js.map +0 -1
  199. package/dist/views/page-builder/PageBuilder.d.ts +0 -10
  200. package/dist/views/page-builder/PageBuilder.d.ts.map +0 -1
  201. package/dist/views/page-builder/PageBuilder.js +0 -222
  202. package/dist/views/page-builder/PageBuilder.js.map +0 -1
  203. package/dist/views/page-builder/PageSettings.d.ts +0 -7
  204. package/dist/views/page-builder/PageSettings.d.ts.map +0 -1
  205. package/dist/views/page-builder/PageSettings.js +0 -27
  206. package/dist/views/page-builder/PageSettings.js.map +0 -1
  207. package/dist/views/page-builder/PageTemplates.d.ts +0 -5
  208. package/dist/views/page-builder/PageTemplates.d.ts.map +0 -1
  209. package/dist/views/page-builder/PageTemplates.js +0 -146
  210. package/dist/views/page-builder/PageTemplates.js.map +0 -1
  211. package/dist/views/page-builder/RelationshipField.d.ts +0 -15
  212. package/dist/views/page-builder/RelationshipField.d.ts.map +0 -1
  213. package/dist/views/page-builder/RelationshipField.js +0 -65
  214. package/dist/views/page-builder/RelationshipField.js.map +0 -1
  215. package/dist/views/page-builder/SEOPanel.d.ts +0 -9
  216. package/dist/views/page-builder/SEOPanel.d.ts.map +0 -1
  217. package/dist/views/page-builder/SEOPanel.js +0 -38
  218. package/dist/views/page-builder/SEOPanel.js.map +0 -1
  219. package/dist/views/page-builder/SavedSections.d.ts +0 -6
  220. package/dist/views/page-builder/SavedSections.d.ts.map +0 -1
  221. package/dist/views/page-builder/SavedSections.js +0 -141
  222. package/dist/views/page-builder/SavedSections.js.map +0 -1
  223. package/dist/views/page-builder/TemplatePicker.d.ts +0 -7
  224. package/dist/views/page-builder/TemplatePicker.d.ts.map +0 -1
  225. package/dist/views/page-builder/TemplatePicker.js +0 -68
  226. package/dist/views/page-builder/TemplatePicker.js.map +0 -1
  227. package/dist/views/page-builder/Widgets.d.ts +0 -13
  228. package/dist/views/page-builder/Widgets.d.ts.map +0 -1
  229. package/dist/views/page-builder/Widgets.js +0 -15
  230. package/dist/views/page-builder/Widgets.js.map +0 -1
  231. package/dist/views/page-builder/block-renderers/CTAPreview.d.ts +0 -3
  232. package/dist/views/page-builder/block-renderers/CTAPreview.d.ts.map +0 -1
  233. package/dist/views/page-builder/block-renderers/CTAPreview.js +0 -19
  234. package/dist/views/page-builder/block-renderers/CTAPreview.js.map +0 -1
  235. package/dist/views/page-builder/block-renderers/CardsPreview.d.ts +0 -3
  236. package/dist/views/page-builder/block-renderers/CardsPreview.d.ts.map +0 -1
  237. package/dist/views/page-builder/block-renderers/CardsPreview.js +0 -22
  238. package/dist/views/page-builder/block-renderers/CardsPreview.js.map +0 -1
  239. package/dist/views/page-builder/block-renderers/CodePreview.d.ts +0 -3
  240. package/dist/views/page-builder/block-renderers/CodePreview.d.ts.map +0 -1
  241. package/dist/views/page-builder/block-renderers/CodePreview.js +0 -12
  242. package/dist/views/page-builder/block-renderers/CodePreview.js.map +0 -1
  243. package/dist/views/page-builder/block-renderers/FAQPreview.d.ts +0 -3
  244. package/dist/views/page-builder/block-renderers/FAQPreview.d.ts.map +0 -1
  245. package/dist/views/page-builder/block-renderers/FAQPreview.js +0 -27
  246. package/dist/views/page-builder/block-renderers/FAQPreview.js.map +0 -1
  247. package/dist/views/page-builder/block-renderers/FallbackPreview.d.ts +0 -6
  248. package/dist/views/page-builder/block-renderers/FallbackPreview.d.ts.map +0 -1
  249. package/dist/views/page-builder/block-renderers/FallbackPreview.js +0 -7
  250. package/dist/views/page-builder/block-renderers/FallbackPreview.js.map +0 -1
  251. package/dist/views/page-builder/block-renderers/FormPreview.d.ts +0 -9
  252. package/dist/views/page-builder/block-renderers/FormPreview.d.ts.map +0 -1
  253. package/dist/views/page-builder/block-renderers/FormPreview.js +0 -52
  254. package/dist/views/page-builder/block-renderers/FormPreview.js.map +0 -1
  255. package/dist/views/page-builder/block-renderers/GalleryPreview.d.ts +0 -3
  256. package/dist/views/page-builder/block-renderers/GalleryPreview.d.ts.map +0 -1
  257. package/dist/views/page-builder/block-renderers/GalleryPreview.js +0 -19
  258. package/dist/views/page-builder/block-renderers/GalleryPreview.js.map +0 -1
  259. package/dist/views/page-builder/block-renderers/HeroPreview.d.ts +0 -3
  260. package/dist/views/page-builder/block-renderers/HeroPreview.d.ts.map +0 -1
  261. package/dist/views/page-builder/block-renderers/HeroPreview.js +0 -19
  262. package/dist/views/page-builder/block-renderers/HeroPreview.js.map +0 -1
  263. package/dist/views/page-builder/block-renderers/ImagePreview.d.ts +0 -3
  264. package/dist/views/page-builder/block-renderers/ImagePreview.d.ts.map +0 -1
  265. package/dist/views/page-builder/block-renderers/ImagePreview.js +0 -17
  266. package/dist/views/page-builder/block-renderers/ImagePreview.js.map +0 -1
  267. package/dist/views/page-builder/block-renderers/TextPreview.d.ts +0 -3
  268. package/dist/views/page-builder/block-renderers/TextPreview.d.ts.map +0 -1
  269. package/dist/views/page-builder/block-renderers/TextPreview.js +0 -22
  270. package/dist/views/page-builder/block-renderers/TextPreview.js.map +0 -1
  271. package/dist/views/page-builder/block-renderers/VideoPreview.d.ts +0 -3
  272. package/dist/views/page-builder/block-renderers/VideoPreview.d.ts.map +0 -1
  273. package/dist/views/page-builder/block-renderers/VideoPreview.js +0 -18
  274. package/dist/views/page-builder/block-renderers/VideoPreview.js.map +0 -1
  275. package/dist/views/page-builder/block-renderers/index.d.ts +0 -9
  276. package/dist/views/page-builder/block-renderers/index.d.ts.map +0 -1
  277. package/dist/views/page-builder/block-renderers/index.js +0 -25
  278. package/dist/views/page-builder/block-renderers/index.js.map +0 -1
  279. package/dist/views/page-builder/canvas/BlockRenderer.d.ts +0 -8
  280. package/dist/views/page-builder/canvas/BlockRenderer.d.ts.map +0 -1
  281. package/dist/views/page-builder/canvas/BlockRenderer.js +0 -20
  282. package/dist/views/page-builder/canvas/BlockRenderer.js.map +0 -1
  283. package/dist/views/page-builder/canvas/BuilderCanvas.d.ts +0 -10
  284. package/dist/views/page-builder/canvas/BuilderCanvas.d.ts.map +0 -1
  285. package/dist/views/page-builder/canvas/BuilderCanvas.js +0 -26
  286. package/dist/views/page-builder/canvas/BuilderCanvas.js.map +0 -1
  287. package/dist/views/page-builder/canvas/ColumnRenderer.d.ts +0 -8
  288. package/dist/views/page-builder/canvas/ColumnRenderer.d.ts.map +0 -1
  289. package/dist/views/page-builder/canvas/ColumnRenderer.js +0 -32
  290. package/dist/views/page-builder/canvas/ColumnRenderer.js.map +0 -1
  291. package/dist/views/page-builder/canvas/ContainerRenderer.d.ts +0 -8
  292. package/dist/views/page-builder/canvas/ContainerRenderer.d.ts.map +0 -1
  293. package/dist/views/page-builder/canvas/ContainerRenderer.js +0 -29
  294. package/dist/views/page-builder/canvas/ContainerRenderer.js.map +0 -1
  295. package/dist/views/page-builder/canvas/RowRenderer.d.ts +0 -8
  296. package/dist/views/page-builder/canvas/RowRenderer.d.ts.map +0 -1
  297. package/dist/views/page-builder/canvas/RowRenderer.js +0 -28
  298. package/dist/views/page-builder/canvas/RowRenderer.js.map +0 -1
  299. package/dist/views/page-builder/canvas/SectionRenderer.d.ts +0 -8
  300. package/dist/views/page-builder/canvas/SectionRenderer.d.ts.map +0 -1
  301. package/dist/views/page-builder/canvas/SectionRenderer.js +0 -50
  302. package/dist/views/page-builder/canvas/SectionRenderer.js.map +0 -1
  303. package/dist/views/page-builder/canvas/index.d.ts +0 -3
  304. package/dist/views/page-builder/canvas/index.d.ts.map +0 -1
  305. package/dist/views/page-builder/canvas/index.js +0 -2
  306. package/dist/views/page-builder/canvas/index.js.map +0 -1
  307. package/dist/views/page-editor/PagePreview.d.ts +0 -13
  308. package/dist/views/page-editor/PagePreview.d.ts.map +0 -1
  309. package/dist/views/page-editor/PagePreview.js +0 -46
  310. package/dist/views/page-editor/PagePreview.js.map +0 -1
  311. package/dist/views/page-editor/SitePreviewFrame.d.ts +0 -43
  312. package/dist/views/page-editor/SitePreviewFrame.d.ts.map +0 -1
  313. package/dist/views/page-editor/SitePreviewFrame.js +0 -165
  314. package/dist/views/page-editor/SitePreviewFrame.js.map +0 -1
  315. package/dist/views/page-editor/useSitePreview.d.ts +0 -47
  316. package/dist/views/page-editor/useSitePreview.d.ts.map +0 -1
  317. package/dist/views/page-editor/useSitePreview.js +0 -96
  318. package/dist/views/page-editor/useSitePreview.js.map +0 -1
  319. package/dist/views/post-editor/PostPreview.d.ts +0 -14
  320. package/dist/views/post-editor/PostPreview.d.ts.map +0 -1
  321. package/dist/views/post-editor/PostPreview.js +0 -63
  322. package/dist/views/post-editor/PostPreview.js.map +0 -1
  323. package/src/__tests__/views/page-builder-block-renderer.render.test.tsx +0 -66
  324. package/src/__tests__/views/page-builder-form-picker.render.test.tsx +0 -54
  325. package/src/__tests__/views/page-builder-sections-panel.render.test.tsx +0 -138
  326. package/src/__tests__/views/page-builder-seo-panel.render.test.tsx +0 -77
  327. package/src/__tests__/views/page-builder-template-seed.render.test.tsx +0 -59
  328. package/src/__tests__/views/page-templates.render.test.tsx +0 -182
  329. package/src/__tests__/views/use-site-preview.render.test.tsx +0 -164
  330. package/src/hooks/useBuilderState.ts +0 -361
  331. package/src/views/page-builder/AIBlockAssist.tsx +0 -64
  332. package/src/views/page-builder/AIGenerateDialog.tsx +0 -593
  333. package/src/views/page-builder/BlockEditor.tsx +0 -523
  334. package/src/views/page-builder/BlockPicker.tsx +0 -324
  335. package/src/views/page-builder/BottomBar.tsx +0 -65
  336. package/src/views/page-builder/BuilderSectionsPanel.tsx +0 -304
  337. package/src/views/page-builder/BuilderToolbar.tsx +0 -221
  338. package/src/views/page-builder/ContextPanel.tsx +0 -144
  339. package/src/views/page-builder/DesignScore.tsx +0 -251
  340. package/src/views/page-builder/NodeSettings.tsx +0 -500
  341. package/src/views/page-builder/PageBuilder.tsx +0 -405
  342. package/src/views/page-builder/PageSettings.tsx +0 -162
  343. package/src/views/page-builder/PageTemplates.tsx +0 -435
  344. package/src/views/page-builder/RelationshipField.tsx +0 -113
  345. package/src/views/page-builder/SEOPanel.tsx +0 -222
  346. package/src/views/page-builder/SavedSections.tsx +0 -480
  347. package/src/views/page-builder/TemplatePicker.tsx +0 -197
  348. package/src/views/page-builder/Widgets.tsx +0 -44
  349. package/src/views/page-builder/block-renderers/CTAPreview.tsx +0 -79
  350. package/src/views/page-builder/block-renderers/CardsPreview.tsx +0 -69
  351. package/src/views/page-builder/block-renderers/CodePreview.tsx +0 -46
  352. package/src/views/page-builder/block-renderers/FAQPreview.tsx +0 -87
  353. package/src/views/page-builder/block-renderers/FallbackPreview.tsx +0 -18
  354. package/src/views/page-builder/block-renderers/FormPreview.tsx +0 -117
  355. package/src/views/page-builder/block-renderers/GalleryPreview.tsx +0 -87
  356. package/src/views/page-builder/block-renderers/HeroPreview.tsx +0 -90
  357. package/src/views/page-builder/block-renderers/ImagePreview.tsx +0 -54
  358. package/src/views/page-builder/block-renderers/TextPreview.tsx +0 -81
  359. package/src/views/page-builder/block-renderers/VideoPreview.tsx +0 -73
  360. package/src/views/page-builder/block-renderers/index.ts +0 -34
  361. package/src/views/page-builder/canvas/BlockRenderer.tsx +0 -56
  362. package/src/views/page-builder/canvas/BuilderCanvas.tsx +0 -87
  363. package/src/views/page-builder/canvas/ColumnRenderer.tsx +0 -82
  364. package/src/views/page-builder/canvas/ContainerRenderer.tsx +0 -67
  365. package/src/views/page-builder/canvas/RowRenderer.tsx +0 -68
  366. package/src/views/page-builder/canvas/SectionRenderer.tsx +0 -93
  367. package/src/views/page-builder/canvas/index.ts +0 -2
  368. package/src/views/page-editor/PagePreview.tsx +0 -105
  369. package/src/views/page-editor/SitePreviewFrame.tsx +0 -250
  370. package/src/views/page-editor/useSitePreview.ts +0 -158
  371. package/src/views/post-editor/PostPreview.tsx +0 -125
@@ -1,182 +0,0 @@
1
- // @vitest-environment happy-dom
2
- import { beforeEach, describe, expect, it, vi } from 'vitest'
3
- import { fireEvent, render, screen, waitFor } from '@testing-library/react'
4
-
5
- const cmsApi = vi.fn()
6
- vi.mock('../../lib/api.js', () => ({ cmsApi: (...args: unknown[]) => cmsApi(...args) }))
7
- vi.mock('sonner', () => ({ toast: { success: vi.fn(), error: vi.fn(), loading: vi.fn() } }))
8
-
9
- const { PageTemplates } = await import('../../views/page-builder/PageTemplates.js')
10
-
11
- function section(id: string) {
12
- return { id, type: 'section', settings: {}, children: [] }
13
- }
14
-
15
- const TEMPLATES = [
16
- {
17
- id: 'builtin-landing',
18
- name: 'Landing Page',
19
- description: 'Hero, features cards, CTA, testimonials, and final CTA',
20
- category: 'landing',
21
- builtIn: true,
22
- updatedAt: '2026-05-29T00:00:00.000Z',
23
- thumbnail: null,
24
- tree: { id: 'p1', type: 'page', children: [section('a'), section('b'), section('c')] },
25
- },
26
- {
27
- id: 'custom-1',
28
- name: 'Team Member',
29
- description: 'Avatar hero, full bio, and contact links',
30
- category: 'content',
31
- builtIn: false,
32
- updatedAt: '2026-06-10T00:00:00.000Z',
33
- thumbnail: null,
34
- tree: { id: 'p2', type: 'page', children: [section('a')] },
35
- },
36
- ]
37
-
38
- beforeEach(() => {
39
- cmsApi.mockReset()
40
- cmsApi.mockImplementation(async (endpoint: string, options?: RequestInit) => {
41
- if (endpoint === '/page-templates' && !options?.method) {
42
- return { data: TEMPLATES, status: 200 }
43
- }
44
- return { data: {}, status: 200 }
45
- })
46
- })
47
-
48
- describe('PageTemplates card grid', () => {
49
- it('renders a card per template with name, category, and section-count meta', async () => {
50
- render(<PageTemplates onNavigate={() => {}} />)
51
-
52
- expect(await screen.findByText('Landing Page')).toBeTruthy()
53
- expect(screen.getByText('Team Member')).toBeTruthy()
54
- // Section counts derived from each template's tree.
55
- expect(screen.getByText(/3 sections/)).toBeTruthy()
56
- expect(screen.getByText(/1 section ·/)).toBeTruthy()
57
- // Category labels from the design's palette.
58
- expect(screen.getByText('Landing Page')).toBeTruthy()
59
- expect(screen.getByText('Built-in')).toBeTruthy()
60
- })
61
-
62
- it('hides edit/delete actions on built-in templates (server rejects them)', async () => {
63
- render(<PageTemplates onNavigate={() => {}} />)
64
- await screen.findByText('Landing Page')
65
-
66
- expect(screen.queryByRole('button', { name: 'Edit Landing Page' })).toBeNull()
67
- expect(screen.queryByRole('button', { name: 'Delete Landing Page' })).toBeNull()
68
- expect(screen.getByRole('button', { name: 'Edit Team Member' })).toBeTruthy()
69
- expect(screen.getByRole('button', { name: 'Delete Team Member' })).toBeTruthy()
70
- })
71
-
72
- it('deletes a custom template only after the confirm dialog is accepted', async () => {
73
- render(<PageTemplates onNavigate={() => {}} />)
74
- await screen.findByText('Team Member')
75
-
76
- fireEvent.click(screen.getByRole('button', { name: 'Delete Team Member' }))
77
-
78
- // Confirm dialog is up; nothing deleted yet.
79
- expect(await screen.findByText('Delete template?')).toBeTruthy()
80
- expect(cmsApi).not.toHaveBeenCalledWith(
81
- '/page-templates/custom-1',
82
- expect.objectContaining({ method: 'DELETE' }),
83
- )
84
-
85
- fireEvent.click(screen.getByRole('button', { name: 'Delete' }))
86
-
87
- await waitFor(() =>
88
- expect(cmsApi).toHaveBeenCalledWith(
89
- '/page-templates/custom-1',
90
- expect.objectContaining({ method: 'DELETE' }),
91
- ),
92
- )
93
- })
94
-
95
- it('opens the edit modal seeded with the template and saves via PUT', async () => {
96
- render(<PageTemplates onNavigate={() => {}} />)
97
- await screen.findByText('Team Member')
98
-
99
- fireEvent.click(screen.getByRole('button', { name: 'Edit Team Member' }))
100
-
101
- const nameInput = (await screen.findByPlaceholderText('Template name')) as HTMLInputElement
102
- expect(nameInput.value).toBe('Team Member')
103
-
104
- fireEvent.change(nameInput, { target: { value: 'Team Bio' } })
105
- fireEvent.click(screen.getByRole('button', { name: 'Save changes' }))
106
-
107
- await waitFor(() =>
108
- expect(cmsApi).toHaveBeenCalledWith(
109
- '/page-templates/custom-1',
110
- expect.objectContaining({ method: 'PUT' }),
111
- ),
112
- )
113
- const putCall = cmsApi.mock.calls.find(
114
- (c) => (c[1] as RequestInit | undefined)?.method === 'PUT',
115
- )!
116
- expect(JSON.parse((putCall[1] as RequestInit).body as string)).toMatchObject({
117
- name: 'Team Bio',
118
- category: 'content',
119
- })
120
- })
121
-
122
- it('routes "Use template" into the builder with the template id', async () => {
123
- const onNavigate = vi.fn()
124
- render(<PageTemplates onNavigate={onNavigate} />)
125
- await screen.findByText('Landing Page')
126
-
127
- fireEvent.click(screen.getByRole('button', { name: 'Create page from Landing Page' }))
128
-
129
- expect(onNavigate).toHaveBeenCalledWith('/page-builder/new?template=builtin-landing')
130
- })
131
-
132
- it('duplicates a template (built-ins included) via POST with a "(copy)" name', async () => {
133
- render(<PageTemplates onNavigate={() => {}} />)
134
- await screen.findByText('Landing Page')
135
-
136
- fireEvent.click(screen.getByRole('button', { name: 'Duplicate Landing Page' }))
137
-
138
- await waitFor(() =>
139
- expect(cmsApi).toHaveBeenCalledWith(
140
- '/page-templates',
141
- expect.objectContaining({ method: 'POST' }),
142
- ),
143
- )
144
- const postCall = cmsApi.mock.calls.find(
145
- (c) => (c[1] as RequestInit | undefined)?.method === 'POST',
146
- )!
147
- expect(JSON.parse((postCall[1] as RequestInit).body as string)).toMatchObject({
148
- name: 'Landing Page (copy)',
149
- category: 'landing',
150
- tree: TEMPLATES[0]!.tree,
151
- })
152
- // The grid refetches so the copy appears.
153
- await waitFor(() =>
154
- expect(
155
- cmsApi.mock.calls.filter(
156
- (c) => c[0] === '/page-templates' && !(c[1] as RequestInit | undefined)?.method,
157
- ).length,
158
- ).toBeGreaterThanOrEqual(2),
159
- )
160
- })
161
-
162
- it('filters cards by search text', async () => {
163
- render(<PageTemplates onNavigate={() => {}} />)
164
- await screen.findByText('Team Member')
165
-
166
- fireEvent.change(screen.getByPlaceholderText('Search templates…'), {
167
- target: { value: 'landing' },
168
- })
169
-
170
- await waitFor(() => {
171
- expect(screen.queryByText('Team Member')).toBeNull()
172
- expect(screen.getByText('Landing Page')).toBeTruthy()
173
- })
174
- })
175
-
176
- it('shows the empty state when no templates exist', async () => {
177
- cmsApi.mockResolvedValue({ data: [], status: 200 })
178
- render(<PageTemplates onNavigate={() => {}} />)
179
-
180
- expect(await screen.findByText('No page templates yet')).toBeTruthy()
181
- })
182
- })
@@ -1,164 +0,0 @@
1
- // @vitest-environment happy-dom
2
- import { beforeEach, describe, expect, it, vi } from 'vitest'
3
- import { fireEvent, render, screen, waitFor } from '@testing-library/react'
4
-
5
- /**
6
- * useSitePreview — drives the inline branded preview (iframe of the public
7
- * site rendering a draft). Pins: token minted + URL built on activation,
8
- * unsaved drafts are persisted first (a failed save cancels activation), and
9
- * the frame reloads (reloadKey bump) after each completed save.
10
- */
11
-
12
- const createPreviewToken = vi.fn(async () => ({
13
- token: 'tok-123',
14
- expiresAt: '2026-06-19T00:00:00Z',
15
- }))
16
-
17
- vi.mock('../../lib/preview-link.js', () => ({
18
- createPreviewToken: (...args: unknown[]) => createPreviewToken(...(args as [])),
19
- buildPublicPreviewUrl: ({ siteUrl, urlPrefix, slug, path, token }: any) => {
20
- const p =
21
- path === '/' || path === '' ? '/' : path || `${urlPrefix ? `/${urlPrefix}` : ''}/${slug}`
22
- return p === '/'
23
- ? `${siteUrl}/?preview=${token}`
24
- : `${siteUrl}${p.startsWith('/') ? p : `/${p}`}?preview=${token}`
25
- },
26
- resolveSiteUrl: (u?: string | null) => u ?? 'https://fallback.test',
27
- }))
28
-
29
- vi.mock('sonner', () => ({
30
- toast: { error: vi.fn(), success: vi.fn() },
31
- }))
32
-
33
- const { useSitePreview } = await import('../../views/page-editor/useSitePreview.js')
34
- const { toast } = await import('sonner')
35
-
36
- import type { SaveState } from '../../views/page-editor/EditorTopBar.js'
37
-
38
- function Harness({
39
- documentId = 'doc-1',
40
- slug = 'about',
41
- dirty = false,
42
- saveState = 'idle' as SaveState,
43
- saveDraft = vi.fn(async () => true),
44
- defaultActive = false,
45
- }: {
46
- documentId?: string | null
47
- slug?: string | null
48
- dirty?: boolean
49
- saveState?: SaveState
50
- saveDraft?: () => Promise<boolean>
51
- defaultActive?: boolean
52
- }) {
53
- const sp = useSitePreview({
54
- collection: 'pages',
55
- documentId,
56
- slug,
57
- urlPrefix: '',
58
- // Canvas defaults to window.location.origin (Next builder / CMS host).
59
- dirty,
60
- saveState,
61
- saveDraft,
62
- defaultActive,
63
- })
64
- return (
65
- <div>
66
- <button type="button" onClick={sp.toggle}>
67
- toggle
68
- </button>
69
- <output data-testid="state">
70
- {JSON.stringify({ active: sp.active, url: sp.url, reloadKey: sp.reloadKey })}
71
- </output>
72
- </div>
73
- )
74
- }
75
-
76
- function state(): { active: boolean; url: string | null; reloadKey: number } {
77
- return JSON.parse(screen.getByTestId('state').textContent ?? '{}')
78
- }
79
-
80
- beforeEach(() => {
81
- createPreviewToken.mockClear()
82
- vi.mocked(toast.error).mockClear()
83
- })
84
-
85
- describe('useSitePreview', () => {
86
- it('mints a token and builds the canvas draft URL on activation', async () => {
87
- render(<Harness />)
88
- fireEvent.click(screen.getByText('toggle'))
89
- expect(state().active).toBe(true)
90
- await waitFor(() => {
91
- expect(state().url).toBe(`${window.location.origin}/about?preview=tok-123`)
92
- })
93
- expect(createPreviewToken).toHaveBeenCalledWith('pages', 'doc-1')
94
- })
95
-
96
- it('saves a dirty draft before minting; a failed save cancels activation', async () => {
97
- const saveDraft = vi.fn(async () => false)
98
- render(<Harness dirty saveDraft={saveDraft} />)
99
- fireEvent.click(screen.getByText('toggle'))
100
- await waitFor(() => expect(saveDraft).toHaveBeenCalled())
101
- await waitFor(() => expect(state().active).toBe(false))
102
- expect(createPreviewToken).not.toHaveBeenCalled()
103
- })
104
-
105
- it('refuses to activate for unsaved documents', () => {
106
- render(<Harness documentId={null} />)
107
- fireEvent.click(screen.getByText('toggle'))
108
- expect(state().active).toBe(false)
109
- expect(toast.error).toHaveBeenCalled()
110
- })
111
-
112
- it('toggles back to the canvas on second click', async () => {
113
- render(<Harness />)
114
- fireEvent.click(screen.getByText('toggle'))
115
- await waitFor(() => expect(state().url).not.toBeNull())
116
- fireEvent.click(screen.getByText('toggle'))
117
- expect(state().active).toBe(false)
118
- })
119
-
120
- it('auto-activates when defaultActive is true', async () => {
121
- render(<Harness defaultActive />)
122
- await waitFor(() => {
123
- expect(state().active).toBe(true)
124
- expect(state().url).toBe(`${window.location.origin}/about?preview=tok-123`)
125
- })
126
- expect(createPreviewToken).toHaveBeenCalledWith('pages', 'doc-1')
127
- })
128
-
129
- it('honors an explicit canvasUrl override', async () => {
130
- function CanvasHarness() {
131
- const sp = useSitePreview({
132
- collection: 'pages',
133
- documentId: 'doc-1',
134
- slug: 'about',
135
- urlPrefix: '',
136
- canvasUrl: 'https://cms.test',
137
- dirty: false,
138
- saveState: 'idle',
139
- saveDraft: vi.fn(async () => true),
140
- })
141
- return (
142
- <button type="button" onClick={sp.toggle}>
143
- {sp.url ?? 'none'}
144
- </button>
145
- )
146
- }
147
- render(<CanvasHarness />)
148
- fireEvent.click(screen.getByRole('button'))
149
- await waitFor(() => {
150
- expect(screen.getByRole('button').textContent).toBe('https://cms.test/about?preview=tok-123')
151
- })
152
- })
153
-
154
- it('bumps reloadKey after a save completes while the preview is open', async () => {
155
- const { rerender } = render(<Harness saveState="idle" />)
156
- fireEvent.click(screen.getByText('toggle'))
157
- await waitFor(() => expect(state().url).not.toBeNull())
158
- expect(state().reloadKey).toBe(0)
159
-
160
- rerender(<Harness saveState="saving" />)
161
- rerender(<Harness saveState="saved" />)
162
- await waitFor(() => expect(state().reloadKey).toBe(1))
163
- })
164
- })
@@ -1,361 +0,0 @@
1
- 'use client'
2
-
3
- import { useState, useCallback, useRef, useMemo } from 'react'
4
- import type { PageNode, BuilderNode, BlockNode, SectionNode, ColumnNode } from '@prenta/core'
5
- import {
6
- addNode,
7
- removeNode,
8
- moveNode,
9
- updateNodeSettings,
10
- updateBlockData,
11
- findNode,
12
- findParent,
13
- createEmptyPage,
14
- createSection,
15
- createContainer,
16
- createRow,
17
- createColumn,
18
- createBlock,
19
- hasChildren,
20
- } from '@prenta/core/page-builder'
21
-
22
- export type DeviceMode = 'desktop' | 'tablet' | 'mobile'
23
- export type PanelTab = 'block' | 'node' | 'page' | 'seo' | 'design'
24
-
25
- export interface PageSettings {
26
- title: string
27
- slug: string
28
- template?: string
29
- metaTitle?: string
30
- metaDescription?: string
31
- ogImage?: string
32
- focusKeyphrase?: string
33
- schemaType?: string
34
- }
35
-
36
- export interface BuilderState {
37
- tree: PageNode
38
- selectedNodeId: string | null
39
- selectedNode: BuilderNode | null
40
- deviceMode: DeviceMode
41
- activeTab: PanelTab
42
- pageSettings: PageSettings
43
- dirty: boolean
44
- canUndo: boolean
45
- canRedo: boolean
46
- showGridOverlay: boolean
47
- }
48
-
49
- export interface BuilderActions {
50
- selectNode: (id: string | null) => void
51
- setDeviceMode: (mode: DeviceMode) => void
52
- setActiveTab: (tab: PanelTab) => void
53
- setPageSettings: (settings: Partial<PageSettings>) => void
54
- setShowGridOverlay: (show: boolean) => void
55
-
56
- addSection: () => void
57
- addRowToSection: (sectionId: string) => void
58
- addBlockToColumn: (columnId: string, blockType: string, variant?: string) => void
59
- addNodeAtId: (parentId: string, node: BuilderNode, index?: number) => void
60
- removeNodeById: (id: string) => void
61
- moveNodeById: (id: string, newParentId: string, index?: number) => void
62
- updateSettings: (id: string, settings: Record<string, unknown>) => void
63
- updateBlock: (id: string, data: Record<string, unknown>) => void
64
- duplicateNode: (id: string) => void
65
- moveNodeUp: (id: string) => void
66
- moveNodeDown: (id: string) => void
67
-
68
- undo: () => void
69
- redo: () => void
70
- markClean: () => void
71
- replaceTree: (tree: PageNode) => void
72
- }
73
-
74
- const MAX_HISTORY = 50
75
-
76
- function deepClone<T>(obj: T): T {
77
- return JSON.parse(JSON.stringify(obj))
78
- }
79
-
80
- function regenerateIds(node: BuilderNode): BuilderNode {
81
- const cloned = deepClone(node)
82
- let counter = 0
83
- function walk(n: any) {
84
- n.id = `node_${Date.now().toString(36)}_${(counter++).toString(36)}_${Math.random().toString(36).slice(2, 8)}`
85
- if (Array.isArray(n.children)) {
86
- for (const child of n.children) walk(child)
87
- }
88
- }
89
- walk(cloned)
90
- return cloned
91
- }
92
-
93
- export function useBuilderState(
94
- initialTree?: PageNode,
95
- initialPageSettings?: Partial<PageSettings>,
96
- ): BuilderState & BuilderActions {
97
- const [tree, setTree] = useState<PageNode>(() => initialTree ?? createEmptyPage())
98
- const [selectedNodeId, setSelectedNodeId] = useState<string | null>(null)
99
- const [deviceMode, setDeviceMode] = useState<DeviceMode>('desktop')
100
- const [activeTab, setActiveTab] = useState<PanelTab>('page')
101
- const [showGridOverlay, setShowGridOverlay] = useState(false)
102
- const [pageSettings, setPageSettingsState] = useState<PageSettings>({
103
- title: '',
104
- slug: '',
105
- ...initialPageSettings,
106
- })
107
- const [dirty, setDirty] = useState(false)
108
-
109
- const undoStack = useRef<PageNode[]>([])
110
- const redoStack = useRef<PageNode[]>([])
111
-
112
- const pushHistory = useCallback((currentTree: PageNode) => {
113
- undoStack.current = [...undoStack.current.slice(-MAX_HISTORY + 1), currentTree]
114
- redoStack.current = []
115
- }, [])
116
-
117
- const [canUndo, setCanUndo] = useState(false)
118
- const [canRedo, setCanRedo] = useState(false)
119
-
120
- const applyTreeChange = useCallback(
121
- (fn: (prev: PageNode) => PageNode) => {
122
- setTree((prev) => {
123
- pushHistory(prev)
124
- const next = fn(prev)
125
- setDirty(true)
126
- setCanUndo(true)
127
- setCanRedo(false)
128
- return next
129
- })
130
- },
131
- [pushHistory],
132
- )
133
-
134
- const selectedNode = useMemo(() => {
135
- if (!selectedNodeId) return null
136
- return findNode(tree, selectedNodeId) ?? null
137
- }, [tree, selectedNodeId])
138
-
139
- const selectNode = useCallback(
140
- (id: string | null) => {
141
- setSelectedNodeId(id)
142
- if (id) {
143
- const node = findNode(tree, id)
144
- if (node) {
145
- if (node.type === 'block') setActiveTab('block')
146
- else if (node.type === 'page') setActiveTab('page')
147
- else setActiveTab('node')
148
- }
149
- }
150
- },
151
- [tree],
152
- )
153
-
154
- const setPageSettings = useCallback((settings: Partial<PageSettings>) => {
155
- setPageSettingsState((prev) => ({ ...prev, ...settings }))
156
- setDirty(true)
157
- }, [])
158
-
159
- const addSection = useCallback(() => {
160
- applyTreeChange((prev) => {
161
- const section = createSection({ paddingTop: '64px', paddingBottom: '64px' })
162
- const container = createContainer()
163
- const col = createColumn(12)
164
- const row = createRow([col])
165
- ;(container as any).children = [row]
166
- ;(section as any).children = [container]
167
- return addNode(prev, prev.id, section as BuilderNode)
168
- })
169
- }, [applyTreeChange])
170
-
171
- const addRowToSection = useCallback(
172
- (sectionId: string) => {
173
- applyTreeChange((prev) => {
174
- const section = findNode(prev, sectionId)
175
- if (!section || !hasChildren(section)) return prev
176
- const containers = (section as SectionNode).children.filter((c) => c.type === 'container')
177
- const targetId = containers.length > 0 ? containers[containers.length - 1]!.id : sectionId
178
- const col = createColumn(12)
179
- const row = createRow([col])
180
- return addNode(prev, targetId, row as BuilderNode)
181
- })
182
- },
183
- [applyTreeChange],
184
- )
185
-
186
- const addBlockToColumn = useCallback(
187
- (columnId: string, blockType: string, variant?: string) => {
188
- applyTreeChange((prev) => {
189
- const block = createBlock(blockType, variant)
190
- return addNode(prev, columnId, block as BuilderNode)
191
- })
192
- },
193
- [applyTreeChange],
194
- )
195
-
196
- const addNodeAtId = useCallback(
197
- (parentId: string, node: BuilderNode, index?: number) => {
198
- applyTreeChange((prev) => addNode(prev, parentId, node, index))
199
- },
200
- [applyTreeChange],
201
- )
202
-
203
- const removeNodeById = useCallback(
204
- (id: string) => {
205
- applyTreeChange((prev) => {
206
- const result = removeNode(prev, id)
207
- if (selectedNodeId === id) setSelectedNodeId(null)
208
- return result
209
- })
210
- },
211
- [applyTreeChange, selectedNodeId],
212
- )
213
-
214
- const moveNodeById = useCallback(
215
- (id: string, newParentId: string, index?: number) => {
216
- applyTreeChange((prev) => moveNode(prev, id, newParentId, index))
217
- },
218
- [applyTreeChange],
219
- )
220
-
221
- const updateSettings = useCallback(
222
- (id: string, settings: Record<string, unknown>) => {
223
- applyTreeChange((prev) => updateNodeSettings(prev, id, settings))
224
- },
225
- [applyTreeChange],
226
- )
227
-
228
- const updateBlock = useCallback(
229
- (id: string, data: Record<string, unknown>) => {
230
- applyTreeChange((prev) => updateBlockData(prev, id, data))
231
- },
232
- [applyTreeChange],
233
- )
234
-
235
- const duplicateNode = useCallback(
236
- (id: string) => {
237
- applyTreeChange((prev) => {
238
- const node = findNode(prev, id)
239
- if (!node) return prev
240
- const parent = findParent(prev, id)
241
- if (!parent) return prev
242
- const siblings = (parent as any).children as BuilderNode[]
243
- const idx = siblings.findIndex((c) => c.id === id)
244
- const cloned = regenerateIds(node)
245
- return addNode(prev, parent.id, cloned, idx + 1)
246
- })
247
- },
248
- [applyTreeChange],
249
- )
250
-
251
- const moveNodeUp = useCallback(
252
- (id: string) => {
253
- applyTreeChange((prev) => {
254
- const parent = findParent(prev, id)
255
- if (!parent) return prev
256
- const siblings = (parent as any).children as BuilderNode[]
257
- const idx = siblings.findIndex((c) => c.id === id)
258
- if (idx <= 0) return prev
259
- const withoutNode = removeNode(prev, id)
260
- const node = findNode(prev, id)
261
- if (!node) return prev
262
- return addNode(withoutNode, parent.id, deepClone(node), idx - 1)
263
- })
264
- },
265
- [applyTreeChange],
266
- )
267
-
268
- const moveNodeDown = useCallback(
269
- (id: string) => {
270
- applyTreeChange((prev) => {
271
- const parent = findParent(prev, id)
272
- if (!parent) return prev
273
- const siblings = (parent as any).children as BuilderNode[]
274
- const idx = siblings.findIndex((c) => c.id === id)
275
- if (idx >= siblings.length - 1) return prev
276
- const withoutNode = removeNode(prev, id)
277
- const node = findNode(prev, id)
278
- if (!node) return prev
279
- return addNode(withoutNode, parent.id, deepClone(node), idx + 1)
280
- })
281
- },
282
- [applyTreeChange],
283
- )
284
-
285
- const undo = useCallback(() => {
286
- if (undoStack.current.length === 0) return
287
- const prev = undoStack.current[undoStack.current.length - 1]!
288
- undoStack.current = undoStack.current.slice(0, -1)
289
- setTree((current) => {
290
- redoStack.current = [...redoStack.current, current]
291
- setCanUndo(undoStack.current.length > 0)
292
- setCanRedo(true)
293
- return prev
294
- })
295
- }, [])
296
-
297
- const redo = useCallback(() => {
298
- if (redoStack.current.length === 0) return
299
- const next = redoStack.current[redoStack.current.length - 1]!
300
- redoStack.current = redoStack.current.slice(0, -1)
301
- setTree((current) => {
302
- undoStack.current = [...undoStack.current, current]
303
- setCanRedo(redoStack.current.length > 0)
304
- setCanUndo(true)
305
- return next
306
- })
307
- }, [])
308
-
309
- const markClean = useCallback(() => {
310
- setDirty(false)
311
- }, [])
312
-
313
- const replaceTree = useCallback(
314
- (newTree: PageNode) => {
315
- setTree((prev) => {
316
- pushHistory(prev)
317
- setDirty(true)
318
- setCanUndo(true)
319
- setCanRedo(false)
320
- return newTree
321
- })
322
- },
323
- [pushHistory],
324
- )
325
-
326
- return {
327
- tree,
328
- selectedNodeId,
329
- selectedNode,
330
- deviceMode,
331
- activeTab,
332
- pageSettings,
333
- dirty,
334
- canUndo,
335
- canRedo,
336
- showGridOverlay,
337
-
338
- selectNode,
339
- setDeviceMode,
340
- setActiveTab,
341
- setPageSettings,
342
- setShowGridOverlay,
343
-
344
- addSection,
345
- addRowToSection,
346
- addBlockToColumn,
347
- addNodeAtId,
348
- removeNodeById,
349
- moveNodeById,
350
- updateSettings,
351
- updateBlock,
352
- duplicateNode,
353
- moveNodeUp,
354
- moveNodeDown,
355
-
356
- undo,
357
- redo,
358
- markClean,
359
- replaceTree,
360
- }
361
- }