@prenta/admin 0.92.0 → 0.93.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 +552 -0
- package/design-system/tokens.css +173 -71
- package/design-system/tokens.json +244 -63
- package/design-system/tokens.scss +203 -98
- package/design-system/tokens.tailwind.cjs +93 -38
- package/dist/AdminRoot.d.ts +27 -6
- package/dist/AdminRoot.d.ts.map +1 -1
- package/dist/AdminRoot.js.map +1 -1
- package/dist/__tests__/components/breakpoint-controls.render.test.d.ts +2 -0
- package/dist/__tests__/components/breakpoint-controls.render.test.d.ts.map +1 -0
- package/dist/__tests__/components/breakpoint-controls.render.test.js +51 -0
- package/dist/__tests__/components/breakpoint-controls.render.test.js.map +1 -0
- package/dist/__tests__/components/canvas-edit-tip.render.test.d.ts +2 -0
- package/dist/__tests__/components/canvas-edit-tip.render.test.d.ts.map +1 -0
- package/dist/__tests__/components/canvas-edit-tip.render.test.js +37 -0
- package/dist/__tests__/components/canvas-edit-tip.render.test.js.map +1 -0
- package/dist/__tests__/components/editor-rail.render.test.d.ts +2 -0
- package/dist/__tests__/components/editor-rail.render.test.d.ts.map +1 -0
- package/dist/__tests__/components/editor-rail.render.test.js +138 -0
- package/dist/__tests__/components/editor-rail.render.test.js.map +1 -0
- package/dist/__tests__/components/media-block.render.test.d.ts +2 -0
- package/dist/__tests__/components/media-block.render.test.d.ts.map +1 -0
- package/dist/__tests__/components/media-block.render.test.js +108 -0
- package/dist/__tests__/components/media-block.render.test.js.map +1 -0
- package/dist/__tests__/components/page-audit-panel.render.test.d.ts +2 -0
- package/dist/__tests__/components/page-audit-panel.render.test.d.ts.map +1 -0
- package/dist/__tests__/components/page-audit-panel.render.test.js +59 -0
- package/dist/__tests__/components/page-audit-panel.render.test.js.map +1 -0
- package/dist/__tests__/components/reflow-meter.render.test.d.ts +2 -0
- package/dist/__tests__/components/reflow-meter.render.test.d.ts.map +1 -0
- package/dist/__tests__/components/reflow-meter.render.test.js +94 -0
- package/dist/__tests__/components/reflow-meter.render.test.js.map +1 -0
- package/dist/__tests__/components/seo-char-count.test.js +24 -6
- package/dist/__tests__/components/seo-char-count.test.js.map +1 -1
- package/dist/__tests__/components/serp-preview.render.test.d.ts +2 -0
- package/dist/__tests__/components/serp-preview.render.test.d.ts.map +1 -0
- package/dist/__tests__/components/serp-preview.render.test.js +25 -0
- package/dist/__tests__/components/serp-preview.render.test.js.map +1 -0
- package/dist/__tests__/lib/breakpoints.test.d.ts +2 -0
- package/dist/__tests__/lib/breakpoints.test.d.ts.map +1 -0
- package/dist/__tests__/lib/breakpoints.test.js +67 -0
- package/dist/__tests__/lib/breakpoints.test.js.map +1 -0
- package/dist/__tests__/lib/canvas-edit-chrome.test.d.ts +2 -0
- package/dist/__tests__/lib/canvas-edit-chrome.test.d.ts.map +1 -0
- package/dist/__tests__/lib/canvas-edit-chrome.test.js +44 -0
- package/dist/__tests__/lib/canvas-edit-chrome.test.js.map +1 -0
- package/dist/__tests__/lib/media-format.test.d.ts +2 -0
- package/dist/__tests__/lib/media-format.test.d.ts.map +1 -0
- package/dist/__tests__/lib/media-format.test.js +70 -0
- package/dist/__tests__/lib/media-format.test.js.map +1 -0
- package/dist/__tests__/lib/page-audit-input.test.d.ts +2 -0
- package/dist/__tests__/lib/page-audit-input.test.d.ts.map +1 -0
- package/dist/__tests__/lib/page-audit-input.test.js +122 -0
- package/dist/__tests__/lib/page-audit-input.test.js.map +1 -0
- package/dist/__tests__/lib/page-editor-service.test.js +2 -0
- package/dist/__tests__/lib/page-editor-service.test.js.map +1 -1
- package/dist/__tests__/lib/page-redirect.test.d.ts +2 -0
- package/dist/__tests__/lib/page-redirect.test.d.ts.map +1 -0
- package/dist/__tests__/lib/page-redirect.test.js +26 -0
- package/dist/__tests__/lib/page-redirect.test.js.map +1 -0
- package/dist/__tests__/lib/rail-width.test.d.ts +2 -0
- package/dist/__tests__/lib/rail-width.test.d.ts.map +1 -0
- package/dist/__tests__/lib/rail-width.test.js +69 -0
- package/dist/__tests__/lib/rail-width.test.js.map +1 -0
- package/dist/__tests__/lib/reflow-baseline.test.d.ts +2 -0
- package/dist/__tests__/lib/reflow-baseline.test.d.ts.map +1 -0
- package/dist/__tests__/lib/reflow-baseline.test.js +56 -0
- package/dist/__tests__/lib/reflow-baseline.test.js.map +1 -0
- package/dist/__tests__/lib/reflow-probe.test.d.ts +2 -0
- package/dist/__tests__/lib/reflow-probe.test.d.ts.map +1 -0
- package/dist/__tests__/lib/reflow-probe.test.js +73 -0
- package/dist/__tests__/lib/reflow-probe.test.js.map +1 -0
- package/dist/__tests__/styles/canvas-container-queries.test.d.ts +2 -0
- package/dist/__tests__/styles/canvas-container-queries.test.d.ts.map +1 -0
- package/dist/__tests__/styles/canvas-container-queries.test.js +87 -0
- package/dist/__tests__/styles/canvas-container-queries.test.js.map +1 -0
- package/dist/__tests__/styles/packaging.test.d.ts +2 -0
- package/dist/__tests__/styles/packaging.test.d.ts.map +1 -0
- package/dist/__tests__/styles/packaging.test.js +31 -0
- package/dist/__tests__/styles/packaging.test.js.map +1 -0
- package/dist/__tests__/styles/tokens-drift.test.d.ts +2 -0
- package/dist/__tests__/styles/tokens-drift.test.d.ts.map +1 -0
- package/dist/__tests__/styles/tokens-drift.test.js +92 -0
- package/dist/__tests__/styles/tokens-drift.test.js.map +1 -0
- package/dist/__tests__/views/admin-renderer-compat.test.d.ts +2 -0
- package/dist/__tests__/views/admin-renderer-compat.test.d.ts.map +1 -0
- package/dist/__tests__/views/admin-renderer-compat.test.js +43 -0
- package/dist/__tests__/views/admin-renderer-compat.test.js.map +1 -0
- package/dist/__tests__/views/admin-section-renderers.test.js +52 -4
- package/dist/__tests__/views/admin-section-renderers.test.js.map +1 -1
- package/dist/__tests__/views/editor-top-bar.render.test.js +1 -1
- package/dist/__tests__/views/editor-top-bar.render.test.js.map +1 -1
- package/dist/__tests__/views/page-tab.render.test.d.ts +2 -0
- package/dist/__tests__/views/page-tab.render.test.d.ts.map +1 -0
- package/dist/__tests__/views/page-tab.render.test.js +151 -0
- package/dist/__tests__/views/page-tab.render.test.js.map +1 -0
- package/dist/__tests__/views/section-class-parity.test.d.ts +2 -0
- package/dist/__tests__/views/section-class-parity.test.d.ts.map +1 -0
- package/dist/__tests__/views/section-class-parity.test.js +81 -0
- package/dist/__tests__/views/section-class-parity.test.js.map +1 -0
- package/dist/__tests__/views/sections-panel-design-owned.render.test.d.ts +2 -0
- package/dist/__tests__/views/sections-panel-design-owned.render.test.d.ts.map +1 -0
- package/dist/__tests__/views/sections-panel-design-owned.render.test.js +86 -0
- package/dist/__tests__/views/sections-panel-design-owned.render.test.js.map +1 -0
- package/dist/components/BreakpointPicker.d.ts +18 -0
- package/dist/components/BreakpointPicker.d.ts.map +1 -0
- package/dist/components/BreakpointPicker.js +31 -0
- package/dist/components/BreakpointPicker.js.map +1 -0
- package/dist/components/CanvasEditTip.d.ts +6 -0
- package/dist/components/CanvasEditTip.d.ts.map +1 -0
- package/dist/components/CanvasEditTip.js +22 -0
- package/dist/components/CanvasEditTip.js.map +1 -0
- package/dist/components/EditorRail.d.ts +36 -0
- package/dist/components/EditorRail.d.ts.map +1 -0
- package/dist/components/EditorRail.js +104 -0
- package/dist/components/EditorRail.js.map +1 -0
- package/dist/components/EditorRailTabs.d.ts +32 -0
- package/dist/components/EditorRailTabs.d.ts.map +1 -0
- package/dist/components/EditorRailTabs.js +45 -0
- package/dist/components/EditorRailTabs.js.map +1 -0
- package/dist/components/MediaBlock.d.ts +49 -0
- package/dist/components/MediaBlock.d.ts.map +1 -0
- package/dist/components/MediaBlock.js +50 -0
- package/dist/components/MediaBlock.js.map +1 -0
- package/dist/components/ReflowMeter.d.ts +28 -0
- package/dist/components/ReflowMeter.d.ts.map +1 -0
- package/dist/components/ReflowMeter.js +42 -0
- package/dist/components/ReflowMeter.js.map +1 -0
- package/dist/components/ZoomChip.d.ts +17 -0
- package/dist/components/ZoomChip.d.ts.map +1 -0
- package/dist/components/ZoomChip.js +17 -0
- package/dist/components/ZoomChip.js.map +1 -0
- package/dist/components/breakpoint-icons.d.ts +3 -0
- package/dist/components/breakpoint-icons.d.ts.map +1 -0
- package/dist/components/breakpoint-icons.js +16 -0
- package/dist/components/breakpoint-icons.js.map +1 -0
- package/dist/components/page-tab/AuthorPicker.d.ts +31 -0
- package/dist/components/page-tab/AuthorPicker.d.ts.map +1 -0
- package/dist/components/page-tab/AuthorPicker.js +95 -0
- package/dist/components/page-tab/AuthorPicker.js.map +1 -0
- package/dist/components/page-tab/DirectiveSwitch.d.ts +38 -0
- package/dist/components/page-tab/DirectiveSwitch.d.ts.map +1 -0
- package/dist/components/page-tab/DirectiveSwitch.js +29 -0
- package/dist/components/page-tab/DirectiveSwitch.js.map +1 -0
- package/dist/components/seo/EditorSeoScoreBadge.d.ts +8 -1
- package/dist/components/seo/EditorSeoScoreBadge.d.ts.map +1 -1
- package/dist/components/seo/EditorSeoScoreBadge.js +2 -2
- package/dist/components/seo/EditorSeoScoreBadge.js.map +1 -1
- package/dist/components/seo/PageAuditPanel.d.ts +14 -0
- package/dist/components/seo/PageAuditPanel.d.ts.map +1 -0
- package/dist/components/seo/PageAuditPanel.js +59 -0
- package/dist/components/seo/PageAuditPanel.js.map +1 -0
- package/dist/components/seo/SeoCharCount.d.ts +19 -7
- package/dist/components/seo/SeoCharCount.d.ts.map +1 -1
- package/dist/components/seo/SeoCharCount.js +30 -16
- package/dist/components/seo/SeoCharCount.js.map +1 -1
- package/dist/components/seo/SeoEditorPane.d.ts +15 -1
- package/dist/components/seo/SeoEditorPane.d.ts.map +1 -1
- package/dist/components/seo/SeoEditorPane.js +118 -21
- package/dist/components/seo/SeoEditorPane.js.map +1 -1
- package/dist/components/seo/SerpPreview.d.ts +20 -0
- package/dist/components/seo/SerpPreview.d.ts.map +1 -0
- package/dist/components/seo/SerpPreview.js +6 -0
- package/dist/components/seo/SerpPreview.js.map +1 -0
- package/dist/hooks/usePageAudit.d.ts +41 -0
- package/dist/hooks/usePageAudit.d.ts.map +1 -0
- package/dist/hooks/usePageAudit.js +68 -0
- package/dist/hooks/usePageAudit.js.map +1 -0
- package/dist/hooks/useReflowGuard.d.ts +47 -0
- package/dist/hooks/useReflowGuard.d.ts.map +1 -0
- package/dist/hooks/useReflowGuard.js +52 -0
- package/dist/hooks/useReflowGuard.js.map +1 -0
- package/dist/lib/breakpoints.d.ts +51 -0
- package/dist/lib/breakpoints.d.ts.map +1 -0
- package/dist/lib/breakpoints.js +84 -0
- package/dist/lib/breakpoints.js.map +1 -0
- package/dist/lib/canvas-edit-chrome.d.ts +22 -0
- package/dist/lib/canvas-edit-chrome.d.ts.map +1 -0
- package/dist/lib/canvas-edit-chrome.js +81 -0
- package/dist/lib/canvas-edit-chrome.js.map +1 -0
- package/dist/lib/media-format.d.ts +55 -0
- package/dist/lib/media-format.d.ts.map +1 -0
- package/dist/lib/media-format.js +93 -0
- package/dist/lib/media-format.js.map +1 -0
- package/dist/lib/page-audit-input.d.ts +45 -0
- package/dist/lib/page-audit-input.d.ts.map +1 -0
- package/dist/lib/page-audit-input.js +53 -0
- package/dist/lib/page-audit-input.js.map +1 -0
- package/dist/lib/page-editor-service.d.ts +12 -0
- package/dist/lib/page-editor-service.d.ts.map +1 -1
- package/dist/lib/page-editor-service.js +6 -0
- package/dist/lib/page-editor-service.js.map +1 -1
- package/dist/lib/page-redirect.d.ts +39 -0
- package/dist/lib/page-redirect.d.ts.map +1 -0
- package/dist/lib/page-redirect.js +38 -0
- package/dist/lib/page-redirect.js.map +1 -0
- package/dist/lib/rail-width.d.ts +23 -0
- package/dist/lib/rail-width.d.ts.map +1 -0
- package/dist/lib/rail-width.js +59 -0
- package/dist/lib/rail-width.js.map +1 -0
- package/dist/lib/reflow-baseline.d.ts +45 -0
- package/dist/lib/reflow-baseline.d.ts.map +1 -0
- package/dist/lib/reflow-baseline.js +38 -0
- package/dist/lib/reflow-baseline.js.map +1 -0
- package/dist/lib/reflow-probe.d.ts +159 -0
- package/dist/lib/reflow-probe.d.ts.map +1 -0
- package/dist/lib/reflow-probe.js +228 -0
- package/dist/lib/reflow-probe.js.map +1 -0
- package/dist/lib/seo-service.d.ts +10 -0
- package/dist/lib/seo-service.d.ts.map +1 -1
- package/dist/lib/seo-service.js.map +1 -1
- package/dist/prenta-admin.css +1 -1
- package/dist/views/page-editor/CanvasMetaLine.d.ts +19 -0
- package/dist/views/page-editor/CanvasMetaLine.d.ts.map +1 -0
- package/dist/views/page-editor/CanvasMetaLine.js +23 -0
- package/dist/views/page-editor/CanvasMetaLine.js.map +1 -0
- package/dist/views/page-editor/EditorCanvas.d.ts +34 -8
- package/dist/views/page-editor/EditorCanvas.d.ts.map +1 -1
- package/dist/views/page-editor/EditorCanvas.js +68 -34
- package/dist/views/page-editor/EditorCanvas.js.map +1 -1
- package/dist/views/page-editor/EditorTopBar.d.ts +14 -1
- package/dist/views/page-editor/EditorTopBar.d.ts.map +1 -1
- package/dist/views/page-editor/EditorTopBar.js +8 -17
- package/dist/views/page-editor/EditorTopBar.js.map +1 -1
- package/dist/views/page-editor/PageSectionEditor.d.ts.map +1 -1
- package/dist/views/page-editor/PageSectionEditor.js +275 -53
- package/dist/views/page-editor/PageSectionEditor.js.map +1 -1
- package/dist/views/page-editor/PageTab.d.ts +36 -0
- package/dist/views/page-editor/PageTab.d.ts.map +1 -0
- package/dist/views/page-editor/PageTab.js +65 -0
- package/dist/views/page-editor/PageTab.js.map +1 -0
- package/dist/views/page-editor/SectionInspector.d.ts.map +1 -1
- package/dist/views/page-editor/SectionInspector.js +63 -19
- package/dist/views/page-editor/SectionInspector.js.map +1 -1
- package/dist/views/page-editor/SectionsPanel.d.ts +30 -5
- package/dist/views/page-editor/SectionsPanel.d.ts.map +1 -1
- package/dist/views/page-editor/SectionsPanel.js +38 -7
- package/dist/views/page-editor/SectionsPanel.js.map +1 -1
- package/dist/views/page-editor/SitePreviewFrame.d.ts.map +1 -1
- package/dist/views/page-editor/SitePreviewFrame.js +3 -0
- package/dist/views/page-editor/SitePreviewFrame.js.map +1 -1
- package/dist/views/page-editor/reflow-context.d.ts +28 -0
- package/dist/views/page-editor/reflow-context.d.ts.map +1 -0
- package/dist/views/page-editor/reflow-context.js +14 -0
- package/dist/views/page-editor/reflow-context.js.map +1 -0
- package/dist/views/page-editor/sections/ByTheNumbersSection.d.ts.map +1 -1
- package/dist/views/page-editor/sections/ByTheNumbersSection.js +1 -1
- package/dist/views/page-editor/sections/ByTheNumbersSection.js.map +1 -1
- package/dist/views/page-editor/sections/CtaBannerSection.d.ts.map +1 -1
- package/dist/views/page-editor/sections/CtaBannerSection.js +1 -1
- package/dist/views/page-editor/sections/CtaBannerSection.js.map +1 -1
- package/dist/views/page-editor/sections/FeatureHighlightSection.d.ts.map +1 -1
- package/dist/views/page-editor/sections/FeatureHighlightSection.js +2 -2
- package/dist/views/page-editor/sections/FeatureHighlightSection.js.map +1 -1
- package/dist/views/page-editor/sections/GallerySection.d.ts.map +1 -1
- package/dist/views/page-editor/sections/GallerySection.js +7 -3
- package/dist/views/page-editor/sections/GallerySection.js.map +1 -1
- package/dist/views/page-editor/sections/HeroBannerSection.js +1 -1
- package/dist/views/page-editor/sections/HeroBannerSection.js.map +1 -1
- package/dist/views/page-editor/sections/MissionSection.d.ts.map +1 -1
- package/dist/views/page-editor/sections/MissionSection.js +1 -1
- package/dist/views/page-editor/sections/MissionSection.js.map +1 -1
- package/dist/views/page-editor/sections/PullQuoteSection.d.ts.map +1 -1
- package/dist/views/page-editor/sections/PullQuoteSection.js +1 -1
- package/dist/views/page-editor/sections/PullQuoteSection.js.map +1 -1
- package/dist/views/page-editor/sections/RelatedPostsSection.d.ts.map +1 -1
- package/dist/views/page-editor/sections/RelatedPostsSection.js +1 -1
- package/dist/views/page-editor/sections/RelatedPostsSection.js.map +1 -1
- package/dist/views/page-editor/sections/SectionRenderer.d.ts.map +1 -1
- package/dist/views/page-editor/sections/SectionRenderer.js +49 -24
- package/dist/views/page-editor/sections/SectionRenderer.js.map +1 -1
- package/dist/views/page-editor/sections/admin-renderers.d.ts +47 -15
- package/dist/views/page-editor/sections/admin-renderers.d.ts.map +1 -1
- package/dist/views/page-editor/sections/admin-renderers.js +27 -15
- package/dist/views/page-editor/sections/admin-renderers.js.map +1 -1
- package/dist/views/page-editor/sections/parts.d.ts +0 -1
- package/dist/views/page-editor/sections/parts.d.ts.map +1 -1
- package/dist/views/page-editor/sections/parts.js +41 -16
- package/dist/views/page-editor/sections/parts.js.map +1 -1
- package/dist/views/page-editor/viewports.d.ts +7 -14
- package/dist/views/page-editor/viewports.d.ts.map +1 -1
- package/dist/views/page-editor/viewports.js +7 -12
- package/dist/views/page-editor/viewports.js.map +1 -1
- package/dist/views/post-editor/PostEditorCanvas.d.ts +2 -1
- package/dist/views/post-editor/PostEditorCanvas.d.ts.map +1 -1
- package/dist/views/post-editor/PostEditorCanvas.js +5 -2
- package/dist/views/post-editor/PostEditorCanvas.js.map +1 -1
- package/dist/views/post-editor/PostSectionEditor.d.ts.map +1 -1
- package/dist/views/post-editor/PostSectionEditor.js +2 -20
- package/dist/views/post-editor/PostSectionEditor.js.map +1 -1
- package/dist/views/post-editor/PostTemplateEditor.d.ts.map +1 -1
- package/dist/views/post-editor/PostTemplateEditor.js +4 -1
- package/dist/views/post-editor/PostTemplateEditor.js.map +1 -1
- package/package.json +5 -3
- package/src/AdminRoot.tsx +27 -6
- package/src/__tests__/components/breakpoint-controls.render.test.tsx +57 -0
- package/src/__tests__/components/canvas-edit-tip.render.test.tsx +41 -0
- package/src/__tests__/components/editor-rail.render.test.tsx +192 -0
- package/src/__tests__/components/media-block.render.test.tsx +126 -0
- package/src/__tests__/components/page-audit-panel.render.test.tsx +64 -0
- package/src/__tests__/components/reflow-meter.render.test.tsx +144 -0
- package/src/__tests__/components/seo-char-count.test.tsx +26 -6
- package/src/__tests__/components/serp-preview.render.test.tsx +32 -0
- package/src/__tests__/lib/breakpoints.test.ts +84 -0
- package/src/__tests__/lib/canvas-edit-chrome.test.ts +56 -0
- package/src/__tests__/lib/media-format.test.ts +89 -0
- package/src/__tests__/lib/page-audit-input.test.ts +144 -0
- package/src/__tests__/lib/page-editor-service.test.ts +2 -0
- package/src/__tests__/lib/page-redirect.test.ts +29 -0
- package/src/__tests__/lib/rail-width.test.ts +86 -0
- package/src/__tests__/lib/reflow-baseline.test.ts +64 -0
- package/src/__tests__/lib/reflow-probe.test.ts +95 -0
- package/src/__tests__/styles/canvas-container-queries.test.ts +103 -0
- package/src/__tests__/styles/packaging.test.ts +38 -0
- package/src/__tests__/styles/tokens-drift.test.ts +110 -0
- package/src/__tests__/views/admin-renderer-compat.test.ts +48 -0
- package/src/__tests__/views/admin-section-renderers.test.tsx +70 -4
- package/src/__tests__/views/editor-top-bar.render.test.tsx +1 -1
- package/src/__tests__/views/page-tab.render.test.tsx +226 -0
- package/src/__tests__/views/section-class-parity.test.ts +98 -0
- package/src/__tests__/views/sections-panel-design-owned.render.test.tsx +104 -0
- package/src/components/BreakpointPicker.tsx +114 -0
- package/src/components/CanvasEditTip.tsx +46 -0
- package/src/components/EditorRail.tsx +219 -0
- package/src/components/EditorRailTabs.tsx +117 -0
- package/src/components/MediaBlock.tsx +223 -0
- package/src/components/ReflowMeter.tsx +125 -0
- package/src/components/ZoomChip.tsx +39 -0
- package/src/components/breakpoint-icons.ts +17 -0
- package/src/components/page-tab/AuthorPicker.tsx +240 -0
- package/src/components/page-tab/DirectiveSwitch.tsx +86 -0
- package/src/components/seo/EditorSeoScoreBadge.tsx +9 -1
- package/src/components/seo/PageAuditPanel.tsx +192 -0
- package/src/components/seo/SeoCharCount.tsx +55 -24
- package/src/components/seo/SeoEditorPane.tsx +213 -31
- package/src/components/seo/SerpPreview.tsx +41 -0
- package/src/hooks/usePageAudit.ts +98 -0
- package/src/hooks/useReflowGuard.ts +96 -0
- package/src/lib/breakpoints.ts +107 -0
- package/src/lib/canvas-edit-chrome.ts +82 -0
- package/src/lib/media-format.ts +99 -0
- package/src/lib/page-audit-input.ts +104 -0
- package/src/lib/page-editor-service.ts +18 -0
- package/src/lib/page-redirect.ts +60 -0
- package/src/lib/rail-width.ts +58 -0
- package/src/lib/reflow-baseline.ts +70 -0
- package/src/lib/reflow-probe.ts +299 -0
- package/src/lib/seo-service.ts +10 -0
- package/src/styles/prenta-tokens.css +120 -42
- package/src/styles/theme.css +130 -0
- package/src/views/page-editor/CanvasMetaLine.tsx +50 -0
- package/src/views/page-editor/EditorCanvas.tsx +109 -50
- package/src/views/page-editor/EditorTopBar.tsx +35 -43
- package/src/views/page-editor/PageSectionEditor.tsx +419 -148
- package/src/views/page-editor/PageTab.tsx +433 -0
- package/src/views/page-editor/SectionInspector.tsx +82 -41
- package/src/views/page-editor/SectionsPanel.tsx +281 -140
- package/src/views/page-editor/SitePreviewFrame.tsx +3 -0
- package/src/views/page-editor/reflow-context.ts +43 -0
- package/src/views/page-editor/sections/ByTheNumbersSection.tsx +3 -2
- package/src/views/page-editor/sections/CtaBannerSection.tsx +3 -1
- package/src/views/page-editor/sections/FeatureHighlightSection.tsx +3 -2
- package/src/views/page-editor/sections/GallerySection.tsx +7 -3
- package/src/views/page-editor/sections/HeroBannerSection.tsx +2 -2
- package/src/views/page-editor/sections/MissionSection.tsx +4 -2
- package/src/views/page-editor/sections/PullQuoteSection.tsx +3 -1
- package/src/views/page-editor/sections/RelatedPostsSection.tsx +2 -1
- package/src/views/page-editor/sections/SectionRenderer.tsx +69 -29
- package/src/views/page-editor/sections/admin-renderers.tsx +54 -15
- package/src/views/page-editor/sections/parts.tsx +46 -18
- package/src/views/page-editor/viewports.ts +13 -26
- package/src/views/post-editor/PostEditorCanvas.tsx +18 -11
- package/src/views/post-editor/PostSectionEditor.tsx +1 -56
- package/src/views/post-editor/PostTemplateEditor.tsx +3 -0
|
@@ -37,24 +37,161 @@ interface SectionsPanelProps {
|
|
|
37
37
|
selectedId: string | null
|
|
38
38
|
canEdit: boolean
|
|
39
39
|
onSelect: (id: string) => void
|
|
40
|
-
onReorder: (orderedIds: string[]) => void
|
|
41
40
|
onToggleVisibility: (id: string) => void
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
/**
|
|
42
|
+
* Permit structural editing — add, reorder, duplicate, delete.
|
|
43
|
+
*
|
|
44
|
+
* Defaults to FALSE. Pages and posts are design-owned: their section list
|
|
45
|
+
* comes from the template in code, and an editor changes copy, CTAs and
|
|
46
|
+
* imagery, never layout or order. Only the template editor — the surface
|
|
47
|
+
* that authors the template itself — opts in.
|
|
48
|
+
*
|
|
49
|
+
* The default is the restrictive one on purpose: a new consumer of this
|
|
50
|
+
* panel that forgets the flag gets the safe behaviour, not an editor that
|
|
51
|
+
* can quietly restructure a design-owned page.
|
|
52
|
+
*/
|
|
53
|
+
structural?: boolean
|
|
54
|
+
/** Required when `structural`. */
|
|
55
|
+
onReorder?: (orderedIds: string[]) => void
|
|
56
|
+
onDuplicate?: (id: string) => void
|
|
57
|
+
onRequestDelete?: (id: string) => void
|
|
58
|
+
onAdd?: () => void
|
|
45
59
|
}
|
|
46
60
|
|
|
47
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Standalone panel — its own aside, header, and width. Used by the post
|
|
63
|
+
* TEMPLATE editor, which has no rail. The page and post editors render
|
|
64
|
+
* `SectionsList` inside `EditorRail` instead, so the rail owns the chrome.
|
|
65
|
+
*/
|
|
66
|
+
export function SectionsPanel(props: SectionsPanelProps) {
|
|
67
|
+
return (
|
|
68
|
+
<aside
|
|
69
|
+
className="bg-card border-border flex h-full w-72 shrink-0 flex-col border-r"
|
|
70
|
+
aria-label="Page sections"
|
|
71
|
+
>
|
|
72
|
+
<div className="border-border flex items-center justify-between border-b px-6 py-4">
|
|
73
|
+
<div className="flex items-center gap-2">
|
|
74
|
+
<h2 className="text-foreground text-sm font-medium">Sections</h2>
|
|
75
|
+
<span className="bg-muted text-muted-foreground rounded-full px-2 py-0.5 text-xs font-medium tabular-nums">
|
|
76
|
+
{props.sections.length}
|
|
77
|
+
</span>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
<SectionsList {...props} />
|
|
81
|
+
</aside>
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* The section outline itself — list plus footer. Fills whatever container it
|
|
87
|
+
* is given, so it works both inside the standalone panel above and inside the
|
|
88
|
+
* editor rail's Sections tab.
|
|
89
|
+
*/
|
|
90
|
+
export function SectionsList({
|
|
48
91
|
sections,
|
|
49
92
|
selectedId,
|
|
50
93
|
canEdit,
|
|
51
94
|
onSelect,
|
|
52
|
-
onReorder,
|
|
53
95
|
onToggleVisibility,
|
|
96
|
+
structural = false,
|
|
97
|
+
onReorder,
|
|
54
98
|
onDuplicate,
|
|
55
99
|
onRequestDelete,
|
|
56
100
|
onAdd,
|
|
57
101
|
}: SectionsPanelProps) {
|
|
102
|
+
return (
|
|
103
|
+
<>
|
|
104
|
+
<div className="min-h-0 flex-1 overflow-y-auto p-2">
|
|
105
|
+
{sections.length === 0 ? (
|
|
106
|
+
<p className="text-muted-foreground px-2 py-6 text-center text-sm">
|
|
107
|
+
{structural
|
|
108
|
+
? 'No sections yet. Add one below to get started.'
|
|
109
|
+
: 'This template renders no sections.'}
|
|
110
|
+
</p>
|
|
111
|
+
) : structural ? (
|
|
112
|
+
<StructuralList
|
|
113
|
+
sections={sections}
|
|
114
|
+
selectedId={selectedId}
|
|
115
|
+
canEdit={canEdit}
|
|
116
|
+
onSelect={onSelect}
|
|
117
|
+
onReorder={onReorder}
|
|
118
|
+
onToggleVisibility={onToggleVisibility}
|
|
119
|
+
onDuplicate={onDuplicate}
|
|
120
|
+
onRequestDelete={onRequestDelete}
|
|
121
|
+
/>
|
|
122
|
+
) : (
|
|
123
|
+
<ul className="space-y-1">
|
|
124
|
+
{sections.map((section) => (
|
|
125
|
+
<SectionRow
|
|
126
|
+
key={section.id}
|
|
127
|
+
section={section}
|
|
128
|
+
selected={section.id === selectedId}
|
|
129
|
+
canEdit={canEdit}
|
|
130
|
+
structural={false}
|
|
131
|
+
onSelect={onSelect}
|
|
132
|
+
onToggleVisibility={onToggleVisibility}
|
|
133
|
+
/>
|
|
134
|
+
))}
|
|
135
|
+
</ul>
|
|
136
|
+
)}
|
|
137
|
+
</div>
|
|
138
|
+
|
|
139
|
+
{structural ? (
|
|
140
|
+
<div className="border-border border-t p-3">
|
|
141
|
+
<button
|
|
142
|
+
type="button"
|
|
143
|
+
onClick={onAdd}
|
|
144
|
+
disabled={!canEdit}
|
|
145
|
+
className="border-border text-foreground hover:bg-accent flex w-full items-center justify-center gap-2 rounded-lg border border-dashed px-3 py-2 text-sm font-medium transition-colors disabled:cursor-not-allowed disabled:opacity-50"
|
|
146
|
+
>
|
|
147
|
+
<Plus className="h-4 w-4" aria-hidden />
|
|
148
|
+
Add Section
|
|
149
|
+
</button>
|
|
150
|
+
</div>
|
|
151
|
+
) : (
|
|
152
|
+
<DesignOwnedNote />
|
|
153
|
+
)}
|
|
154
|
+
</>
|
|
155
|
+
)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Explains the absence of add / reorder / delete. Without it the panel reads
|
|
160
|
+
* as broken rather than deliberate — "where did Add Section go" is the first
|
|
161
|
+
* question a returning editor asks.
|
|
162
|
+
*/
|
|
163
|
+
function DesignOwnedNote() {
|
|
164
|
+
return (
|
|
165
|
+
<div className="border-border bg-background text-muted-foreground flex gap-2 border-t px-3 py-3 text-[11.5px] leading-normal">
|
|
166
|
+
<Lock className="mt-0.5 h-3.5 w-3.5 shrink-0" aria-hidden />
|
|
167
|
+
<p>
|
|
168
|
+
<strong className="text-foreground font-semibold">Layout is design-owned.</strong> Sections
|
|
169
|
+
and styling are built in code — here you edit headings, body copy, CTAs, and imagery. Hide a
|
|
170
|
+
section to drop it from the live page.
|
|
171
|
+
</p>
|
|
172
|
+
</div>
|
|
173
|
+
)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function StructuralList({
|
|
177
|
+
sections,
|
|
178
|
+
selectedId,
|
|
179
|
+
canEdit,
|
|
180
|
+
onSelect,
|
|
181
|
+
onReorder,
|
|
182
|
+
onToggleVisibility,
|
|
183
|
+
onDuplicate,
|
|
184
|
+
onRequestDelete,
|
|
185
|
+
}: {
|
|
186
|
+
sections: PageSection[]
|
|
187
|
+
selectedId: string | null
|
|
188
|
+
canEdit: boolean
|
|
189
|
+
onSelect: (id: string) => void
|
|
190
|
+
onReorder?: (orderedIds: string[]) => void
|
|
191
|
+
onToggleVisibility: (id: string) => void
|
|
192
|
+
onDuplicate?: (id: string) => void
|
|
193
|
+
onRequestDelete?: (id: string) => void
|
|
194
|
+
}) {
|
|
58
195
|
const sensors = useSensors(
|
|
59
196
|
useSensor(PointerSensor, { activationConstraint: { distance: 4 } }),
|
|
60
197
|
useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }),
|
|
@@ -70,164 +207,114 @@ export function SectionsPanel({
|
|
|
70
207
|
const next = ids.slice()
|
|
71
208
|
const [moved] = next.splice(from, 1)
|
|
72
209
|
next.splice(to, 0, moved!)
|
|
73
|
-
onReorder(next)
|
|
210
|
+
onReorder?.(next)
|
|
74
211
|
}
|
|
75
212
|
|
|
76
213
|
return (
|
|
77
|
-
<
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
) : (
|
|
96
|
-
<DndContext
|
|
97
|
-
sensors={sensors}
|
|
98
|
-
collisionDetection={closestCenter}
|
|
99
|
-
onDragEnd={handleDragEnd}
|
|
100
|
-
>
|
|
101
|
-
<SortableContext
|
|
102
|
-
items={sections.map((s) => s.id)}
|
|
103
|
-
strategy={verticalListSortingStrategy}
|
|
104
|
-
>
|
|
105
|
-
<ul className="space-y-1">
|
|
106
|
-
{sections.map((section) => (
|
|
107
|
-
<SortableSectionItem
|
|
108
|
-
key={section.id}
|
|
109
|
-
section={section}
|
|
110
|
-
selected={section.id === selectedId}
|
|
111
|
-
canEdit={canEdit}
|
|
112
|
-
onSelect={onSelect}
|
|
113
|
-
onToggleVisibility={onToggleVisibility}
|
|
114
|
-
onDuplicate={onDuplicate}
|
|
115
|
-
onRequestDelete={onRequestDelete}
|
|
116
|
-
/>
|
|
117
|
-
))}
|
|
118
|
-
</ul>
|
|
119
|
-
</SortableContext>
|
|
120
|
-
</DndContext>
|
|
121
|
-
)}
|
|
122
|
-
</div>
|
|
123
|
-
|
|
124
|
-
<div className="border-border border-t p-3">
|
|
125
|
-
<button
|
|
126
|
-
type="button"
|
|
127
|
-
onClick={onAdd}
|
|
128
|
-
disabled={!canEdit}
|
|
129
|
-
className="border-border text-foreground hover:bg-accent flex w-full items-center justify-center gap-2 rounded-lg border border-dashed px-3 py-2 text-sm font-medium transition-colors disabled:cursor-not-allowed disabled:opacity-50"
|
|
130
|
-
>
|
|
131
|
-
<Plus className="h-4 w-4" aria-hidden />
|
|
132
|
-
Add Section
|
|
133
|
-
</button>
|
|
134
|
-
</div>
|
|
135
|
-
</aside>
|
|
214
|
+
<DndContext sensors={sensors} collisionDetection={closestCenter} onDragEnd={handleDragEnd}>
|
|
215
|
+
<SortableContext items={sections.map((s) => s.id)} strategy={verticalListSortingStrategy}>
|
|
216
|
+
<ul className="space-y-1">
|
|
217
|
+
{sections.map((section) => (
|
|
218
|
+
<SortableSectionItem
|
|
219
|
+
key={section.id}
|
|
220
|
+
section={section}
|
|
221
|
+
selected={section.id === selectedId}
|
|
222
|
+
canEdit={canEdit}
|
|
223
|
+
onSelect={onSelect}
|
|
224
|
+
onToggleVisibility={onToggleVisibility}
|
|
225
|
+
onDuplicate={onDuplicate}
|
|
226
|
+
onRequestDelete={onRequestDelete}
|
|
227
|
+
/>
|
|
228
|
+
))}
|
|
229
|
+
</ul>
|
|
230
|
+
</SortableContext>
|
|
231
|
+
</DndContext>
|
|
136
232
|
)
|
|
137
233
|
}
|
|
138
234
|
|
|
139
|
-
interface
|
|
235
|
+
interface RowProps {
|
|
140
236
|
section: PageSection
|
|
141
237
|
selected: boolean
|
|
142
238
|
canEdit: boolean
|
|
239
|
+
structural: boolean
|
|
143
240
|
onSelect: (id: string) => void
|
|
144
241
|
onToggleVisibility: (id: string) => void
|
|
145
|
-
onDuplicate
|
|
146
|
-
onRequestDelete
|
|
242
|
+
onDuplicate?: (id: string) => void
|
|
243
|
+
onRequestDelete?: (id: string) => void
|
|
244
|
+
/** Drag affordance, supplied only by the sortable wrapper. */
|
|
245
|
+
dragHandle?: React.ReactNode
|
|
246
|
+
dragging?: boolean
|
|
147
247
|
}
|
|
148
248
|
|
|
149
|
-
function
|
|
249
|
+
function SectionRow({
|
|
150
250
|
section,
|
|
151
251
|
selected,
|
|
152
252
|
canEdit,
|
|
253
|
+
structural,
|
|
153
254
|
onSelect,
|
|
154
255
|
onToggleVisibility,
|
|
155
256
|
onDuplicate,
|
|
156
257
|
onRequestDelete,
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
258
|
+
dragHandle,
|
|
259
|
+
dragging,
|
|
260
|
+
}: RowProps) {
|
|
261
|
+
// Unmanaged (externally-managed) sections are read-only even in a structural
|
|
262
|
+
// panel: no drag, duplicate, delete, or visibility toggle — only selectable
|
|
263
|
+
// to view the read-only notice.
|
|
160
264
|
const locked = section.unmanaged === true
|
|
161
|
-
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
|
|
162
|
-
id: section.id,
|
|
163
|
-
disabled: !canEdit || locked,
|
|
164
|
-
})
|
|
165
265
|
const Icon = sectionIconFor(section.sectionType)
|
|
166
266
|
|
|
167
|
-
const style = {
|
|
168
|
-
transform: CSS.Transform.toString(transform),
|
|
169
|
-
transition,
|
|
170
|
-
zIndex: isDragging ? 10 : undefined,
|
|
171
|
-
}
|
|
172
|
-
|
|
173
267
|
return (
|
|
174
|
-
<
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
268
|
+
<div
|
|
269
|
+
className={`group flex items-center gap-2 rounded-lg border px-2 py-2 ${
|
|
270
|
+
selected ? 'border-primary bg-accent' : 'hover:bg-accent/60 border-transparent'
|
|
271
|
+
} ${dragging ? 'shadow-md' : ''}`}
|
|
272
|
+
>
|
|
273
|
+
{dragHandle}
|
|
274
|
+
|
|
275
|
+
<button
|
|
276
|
+
type="button"
|
|
277
|
+
onClick={() => onSelect(section.id)}
|
|
278
|
+
className="flex min-w-0 flex-1 items-center gap-2 text-left"
|
|
179
279
|
>
|
|
180
|
-
<
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
{...attributes}
|
|
186
|
-
{...listeners}
|
|
280
|
+
<span className="bg-muted text-muted-foreground flex h-7 w-7 shrink-0 items-center justify-center rounded-md">
|
|
281
|
+
<Icon className="h-4 w-4" aria-hidden />
|
|
282
|
+
</span>
|
|
283
|
+
<span
|
|
284
|
+
className={`truncate text-sm ${section.visible ? 'text-foreground' : 'text-muted-foreground line-through'}`}
|
|
187
285
|
>
|
|
188
|
-
|
|
189
|
-
</
|
|
286
|
+
{section.name}
|
|
287
|
+
</span>
|
|
288
|
+
</button>
|
|
190
289
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
290
|
+
{locked ? (
|
|
291
|
+
<span
|
|
292
|
+
className="text-muted-foreground shrink-0 p-1"
|
|
293
|
+
title="Managed outside the editor — read-only"
|
|
294
|
+
aria-label={`${section.name} is managed outside the editor and is read-only`}
|
|
195
295
|
>
|
|
196
|
-
<
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
className="text-muted-foreground shrink-0 p-1"
|
|
209
|
-
title="Managed outside the editor — read-only"
|
|
210
|
-
aria-label={`${section.name} is managed outside the editor and is read-only`}
|
|
296
|
+
<Lock className="h-4 w-4" aria-hidden />
|
|
297
|
+
</span>
|
|
298
|
+
) : (
|
|
299
|
+
<>
|
|
300
|
+
{/* Visibility survives the design-owned lockdown: `visible` is the one
|
|
301
|
+
structural property the content model gives an editor. */}
|
|
302
|
+
<button
|
|
303
|
+
type="button"
|
|
304
|
+
onClick={() => onToggleVisibility(section.id)}
|
|
305
|
+
disabled={!canEdit}
|
|
306
|
+
aria-pressed={!section.visible}
|
|
307
|
+
aria-label={section.visible ? `Hide ${section.name}` : `Show ${section.name}`}
|
|
308
|
+
className="text-muted-foreground hover:text-foreground shrink-0 rounded p-1 disabled:opacity-40"
|
|
211
309
|
>
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
onClick={() => onToggleVisibility(section.id)}
|
|
219
|
-
disabled={!canEdit}
|
|
220
|
-
aria-pressed={!section.visible}
|
|
221
|
-
aria-label={section.visible ? `Hide ${section.name}` : `Show ${section.name}`}
|
|
222
|
-
className="text-muted-foreground hover:text-foreground shrink-0 rounded p-1 disabled:opacity-40"
|
|
223
|
-
>
|
|
224
|
-
{section.visible ? (
|
|
225
|
-
<Eye className="h-4 w-4" aria-hidden />
|
|
226
|
-
) : (
|
|
227
|
-
<EyeOff className="h-4 w-4" aria-hidden />
|
|
228
|
-
)}
|
|
229
|
-
</button>
|
|
310
|
+
{section.visible ? (
|
|
311
|
+
<Eye className="h-4 w-4" aria-hidden />
|
|
312
|
+
) : (
|
|
313
|
+
<EyeOff className="h-4 w-4" aria-hidden />
|
|
314
|
+
)}
|
|
315
|
+
</button>
|
|
230
316
|
|
|
317
|
+
{structural && (
|
|
231
318
|
<DropdownMenu.Root>
|
|
232
319
|
<DropdownMenu.Trigger asChild>
|
|
233
320
|
<button
|
|
@@ -243,21 +330,75 @@ function SortableSectionItem({
|
|
|
243
330
|
<DropdownMenu.Content
|
|
244
331
|
align="end"
|
|
245
332
|
sideOffset={4}
|
|
246
|
-
className="border-border bg-popover z-
|
|
333
|
+
className="border-border bg-popover z-70 min-w-44 rounded-lg border p-1 shadow-md"
|
|
247
334
|
>
|
|
248
|
-
<MenuItem onSelect={() => onDuplicate(section.id)} icon={Copy}>
|
|
335
|
+
<MenuItem onSelect={() => onDuplicate?.(section.id)} icon={Copy}>
|
|
249
336
|
Duplicate
|
|
250
337
|
</MenuItem>
|
|
251
338
|
<DropdownMenu.Separator className="bg-border my-1 h-px" />
|
|
252
|
-
<MenuItem
|
|
339
|
+
<MenuItem
|
|
340
|
+
onSelect={() => onRequestDelete?.(section.id)}
|
|
341
|
+
icon={Trash2}
|
|
342
|
+
destructive
|
|
343
|
+
>
|
|
253
344
|
Delete
|
|
254
345
|
</MenuItem>
|
|
255
346
|
</DropdownMenu.Content>
|
|
256
347
|
</DropdownMenu.Portal>
|
|
257
348
|
</DropdownMenu.Root>
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
349
|
+
)}
|
|
350
|
+
</>
|
|
351
|
+
)}
|
|
352
|
+
</div>
|
|
353
|
+
)
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function SortableSectionItem({
|
|
357
|
+
section,
|
|
358
|
+
selected,
|
|
359
|
+
canEdit,
|
|
360
|
+
onSelect,
|
|
361
|
+
onToggleVisibility,
|
|
362
|
+
onDuplicate,
|
|
363
|
+
onRequestDelete,
|
|
364
|
+
}: Omit<RowProps, 'structural' | 'dragHandle' | 'dragging'>) {
|
|
365
|
+
const locked = section.unmanaged === true
|
|
366
|
+
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
|
|
367
|
+
id: section.id,
|
|
368
|
+
disabled: !canEdit || locked,
|
|
369
|
+
})
|
|
370
|
+
|
|
371
|
+
const style = {
|
|
372
|
+
transform: CSS.Transform.toString(transform),
|
|
373
|
+
transition,
|
|
374
|
+
zIndex: isDragging ? 10 : undefined,
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
return (
|
|
378
|
+
<li ref={setNodeRef} style={style}>
|
|
379
|
+
<SectionRow
|
|
380
|
+
section={section}
|
|
381
|
+
selected={selected}
|
|
382
|
+
canEdit={canEdit}
|
|
383
|
+
structural
|
|
384
|
+
onSelect={onSelect}
|
|
385
|
+
onToggleVisibility={onToggleVisibility}
|
|
386
|
+
onDuplicate={onDuplicate}
|
|
387
|
+
onRequestDelete={onRequestDelete}
|
|
388
|
+
dragging={isDragging}
|
|
389
|
+
dragHandle={
|
|
390
|
+
<button
|
|
391
|
+
type="button"
|
|
392
|
+
className="text-muted-foreground hover:text-foreground shrink-0 cursor-grab touch-none disabled:cursor-not-allowed disabled:opacity-40"
|
|
393
|
+
aria-label={`Drag to reorder ${section.name}`}
|
|
394
|
+
disabled={!canEdit || locked}
|
|
395
|
+
{...attributes}
|
|
396
|
+
{...listeners}
|
|
397
|
+
>
|
|
398
|
+
<GripVertical className="h-4 w-4" aria-hidden />
|
|
399
|
+
</button>
|
|
400
|
+
}
|
|
401
|
+
/>
|
|
261
402
|
</li>
|
|
262
403
|
)
|
|
263
404
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { useEffect, useLayoutEffect, useRef, useState } from 'react'
|
|
4
4
|
import { Loader2 } from 'lucide-react'
|
|
5
|
+
import { injectPreviewEditChrome } from '../../lib/canvas-edit-chrome.js'
|
|
5
6
|
import {
|
|
6
7
|
canvasOriginFromSrc,
|
|
7
8
|
isPreviewBridgeMessage,
|
|
@@ -145,6 +146,8 @@ export function SitePreviewFrame({
|
|
|
145
146
|
editing: true,
|
|
146
147
|
}
|
|
147
148
|
win.postMessage(msg, canvasOrigin)
|
|
149
|
+
// Same-origin only — cross-origin previews skip silently.
|
|
150
|
+
injectPreviewEditChrome(iframeRef.current)
|
|
148
151
|
}, [canvasOrigin, loaded, canvasReady, reloadKey])
|
|
149
152
|
|
|
150
153
|
// Admin → canvas: highlight the selected section.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { createContext, useContext } from 'react'
|
|
4
|
+
import type { BreakpointId } from '../../lib/breakpoints.js'
|
|
5
|
+
import type { ReflowBaseline } from '../../lib/reflow-baseline.js'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* What the reflow guard needs that a field editor cannot know by itself.
|
|
9
|
+
*
|
|
10
|
+
* A context rather than props because the guard hangs off individual fields
|
|
11
|
+
* several levels down (inspector → field → repeater row → field), and threading
|
|
12
|
+
* five props through every renderer for a feature that only some field types
|
|
13
|
+
* use makes every one of them harder to read.
|
|
14
|
+
*
|
|
15
|
+
* The default is inert — no canvas, no baseline — so a field rendered outside a
|
|
16
|
+
* page editor (a repeater in a modal, a test) simply shows no guard rather than
|
|
17
|
+
* needing a provider to exist.
|
|
18
|
+
*/
|
|
19
|
+
export interface ReflowContextValue {
|
|
20
|
+
/** The live `.prenta-canvas` element the probe clones. */
|
|
21
|
+
canvas: HTMLElement | null
|
|
22
|
+
/** Section the inspector is editing — scopes field lookup. */
|
|
23
|
+
sectionId: string | null
|
|
24
|
+
baseline: ReflowBaseline | null
|
|
25
|
+
/** Breakpoint the canvas is showing. */
|
|
26
|
+
device: BreakpointId
|
|
27
|
+
/** Take the canvas to a breakpoint so the editor can look at it. */
|
|
28
|
+
onProof: (id: BreakpointId) => void
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const INERT: ReflowContextValue = {
|
|
32
|
+
canvas: null,
|
|
33
|
+
sectionId: null,
|
|
34
|
+
baseline: null,
|
|
35
|
+
device: 'wide',
|
|
36
|
+
onProof: () => {},
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const ReflowContext = createContext<ReflowContextValue>(INERT)
|
|
40
|
+
|
|
41
|
+
export function useReflowContext(): ReflowContextValue {
|
|
42
|
+
return useContext(ReflowContext)
|
|
43
|
+
}
|
|
@@ -16,13 +16,14 @@ export function ByTheNumbersSection({ section }: { section: PageSection }) {
|
|
|
16
16
|
return (
|
|
17
17
|
<div className="mx-auto max-w-5xl px-6 text-center">
|
|
18
18
|
{heading && (
|
|
19
|
-
<h2 className="text-foreground text-2xl font-semibold tracking-tight
|
|
19
|
+
<h2 className="text-foreground @pv-md:text-3xl @pv-2xl:text-4xl text-2xl font-semibold tracking-tight">
|
|
20
20
|
{heading}
|
|
21
21
|
</h2>
|
|
22
22
|
)}
|
|
23
23
|
{body && <p className="text-muted-foreground mx-auto mt-4 max-w-2xl text-base">{body}</p>}
|
|
24
|
+
{/* Handoff: stats collapse to 2-up below 900, and to 1-up below 460. */}
|
|
24
25
|
{stats.length > 0 && (
|
|
25
|
-
<dl className="mt-10 grid grid-cols-1 gap-8
|
|
26
|
+
<dl className="@pv-sm:grid-cols-2 @pv-lg:grid-cols-3 mt-10 grid grid-cols-1 gap-8">
|
|
26
27
|
{stats.map((s, i) => (
|
|
27
28
|
<div key={i}>
|
|
28
29
|
<dt className="text-brand text-4xl font-semibold tracking-tight">
|
|
@@ -9,7 +9,9 @@ export function CtaBannerSection({ section }: { section: PageSection }) {
|
|
|
9
9
|
|
|
10
10
|
return (
|
|
11
11
|
<div className="mx-auto max-w-3xl px-6 text-center">
|
|
12
|
-
<h2 className="text-2xl font-semibold tracking-tight
|
|
12
|
+
<h2 className="@pv-md:text-3xl @pv-2xl:text-4xl text-2xl font-semibold tracking-tight">
|
|
13
|
+
{heading}
|
|
14
|
+
</h2>
|
|
13
15
|
{body && (
|
|
14
16
|
<p
|
|
15
17
|
className={`mx-auto mt-3 max-w-xl text-base ${dark ? 'text-brand-foreground/80' : 'text-muted-foreground'}`}
|
|
@@ -18,7 +18,7 @@ export function FeatureHighlightSection({ section }: { section: PageSection }) {
|
|
|
18
18
|
{label}
|
|
19
19
|
</span>
|
|
20
20
|
)}
|
|
21
|
-
<h2 className="text-foreground text-2xl font-semibold tracking-tight
|
|
21
|
+
<h2 className="text-foreground @pv-md:text-3xl @pv-2xl:text-4xl text-2xl font-semibold tracking-tight">
|
|
22
22
|
{heading}
|
|
23
23
|
</h2>
|
|
24
24
|
{body && <p className="text-muted-foreground mt-4 text-base leading-relaxed">{body}</p>}
|
|
@@ -47,7 +47,8 @@ export function FeatureHighlightSection({ section }: { section: PageSection }) {
|
|
|
47
47
|
|
|
48
48
|
return (
|
|
49
49
|
<div className="mx-auto max-w-5xl px-6">
|
|
50
|
-
|
|
50
|
+
{/* Handoff: image+text stacks below 700. */}
|
|
51
|
+
<div className="@pv-md:flex-row flex flex-col items-center gap-10">
|
|
51
52
|
{imageLeft ? (
|
|
52
53
|
<>
|
|
53
54
|
{media}
|
|
@@ -15,12 +15,16 @@ export function GallerySection({ section }: { section: PageSection }) {
|
|
|
15
15
|
const images: GalleryImage[] = Array.isArray(c.images) ? (c.images as GalleryImage[]) : []
|
|
16
16
|
const withUrl = images.filter((img) => img && typeof img.url === 'string' && img.url)
|
|
17
17
|
|
|
18
|
+
// Ascending min-width container queries only: the column count is chosen
|
|
19
|
+
// from content, so mixing min- and max-width variants here would depend on
|
|
20
|
+
// Tailwind's variant sort order to resolve — the exact ambiguity the handoff
|
|
21
|
+
// warns about for data-driven grids.
|
|
18
22
|
const cols =
|
|
19
23
|
columns === 2
|
|
20
|
-
? 'sm:grid-cols-2'
|
|
24
|
+
? '@pv-sm:grid-cols-2'
|
|
21
25
|
: columns === 4
|
|
22
|
-
? 'sm:grid-cols-2
|
|
23
|
-
: 'sm:grid-cols-3'
|
|
26
|
+
? '@pv-sm:grid-cols-2 @pv-xl:grid-cols-4'
|
|
27
|
+
: '@pv-sm:grid-cols-2 @pv-lg:grid-cols-3'
|
|
24
28
|
|
|
25
29
|
return (
|
|
26
30
|
<div className="mx-auto max-w-5xl px-6">
|
|
@@ -17,12 +17,12 @@ export function HeroBannerSection({ section }: { section: PageSection }) {
|
|
|
17
17
|
{eyebrow}
|
|
18
18
|
</p>
|
|
19
19
|
)}
|
|
20
|
-
<h1 className="text-3xl leading-tight font-semibold tracking-tight
|
|
20
|
+
<h1 className="@pv-md:text-5xl @pv-2xl:text-6xl text-3xl leading-tight font-semibold tracking-tight">
|
|
21
21
|
{heading || 'Your headline goes here'}
|
|
22
22
|
</h1>
|
|
23
23
|
{body && (
|
|
24
24
|
<p
|
|
25
|
-
className={
|
|
25
|
+
className={`@pv-md:text-lg mx-auto mt-5 max-w-2xl text-base ${dark ? 'text-brand-foreground/80' : 'text-muted-foreground'}`}
|
|
26
26
|
>
|
|
27
27
|
{body}
|
|
28
28
|
</p>
|
|
@@ -12,12 +12,14 @@ export function MissionSection({ section }: { section: PageSection }) {
|
|
|
12
12
|
return (
|
|
13
13
|
<div className={`max-w-3xl px-6 ${align}`}>
|
|
14
14
|
{heading && (
|
|
15
|
-
<h2 className="text-foreground text-2xl font-semibold tracking-tight
|
|
15
|
+
<h2 className="text-foreground @pv-md:text-3xl @pv-2xl:text-4xl text-2xl font-semibold tracking-tight">
|
|
16
16
|
{heading}
|
|
17
17
|
</h2>
|
|
18
18
|
)}
|
|
19
19
|
{body && (
|
|
20
|
-
<p className="text-muted-foreground mt-4 text-base leading-relaxed
|
|
20
|
+
<p className="text-muted-foreground @pv-md:text-lg mt-4 text-base leading-relaxed">
|
|
21
|
+
{body}
|
|
22
|
+
</p>
|
|
21
23
|
)}
|
|
22
24
|
</div>
|
|
23
25
|
)
|
|
@@ -13,7 +13,9 @@ export function PullQuoteSection({ section }: { section: PageSection }) {
|
|
|
13
13
|
return (
|
|
14
14
|
<figure className="mx-auto max-w-3xl px-6">
|
|
15
15
|
<blockquote className="border-brand border-l-4 pl-6">
|
|
16
|
-
<p className="text-foreground text-xl leading-relaxed font-medium
|
|
16
|
+
<p className="text-foreground @pv-md:text-2xl text-xl leading-relaxed font-medium">
|
|
17
|
+
“{quote}”
|
|
18
|
+
</p>
|
|
17
19
|
</blockquote>
|
|
18
20
|
{(attribution || role) && (
|
|
19
21
|
<figcaption className="text-muted-foreground mt-3 pl-6 text-sm">
|