@prenta/admin 0.92.0 → 0.94.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 +663 -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__/lib/version-diff.test.d.ts +2 -0
- package/dist/__tests__/lib/version-diff.test.d.ts.map +1 -0
- package/dist/__tests__/lib/version-diff.test.js +230 -0
- package/dist/__tests__/lib/version-diff.test.js.map +1 -0
- package/dist/__tests__/lib/version-history.test.d.ts +2 -0
- package/dist/__tests__/lib/version-history.test.d.ts.map +1 -0
- package/dist/__tests__/lib/version-history.test.js +125 -0
- package/dist/__tests__/lib/version-history.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/responsive-layer.test.d.ts +2 -0
- package/dist/__tests__/styles/responsive-layer.test.d.ts.map +1 -0
- package/dist/__tests__/styles/responsive-layer.test.js +127 -0
- package/dist/__tests__/styles/responsive-layer.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/canvas-review-mode.render.test.d.ts +2 -0
- package/dist/__tests__/views/canvas-review-mode.render.test.d.ts.map +1 -0
- package/dist/__tests__/views/canvas-review-mode.render.test.js +70 -0
- package/dist/__tests__/views/canvas-review-mode.render.test.js.map +1 -0
- package/dist/__tests__/views/editor-top-bar.render.test.js +30 -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/__tests__/views/version-history-panel.render.test.d.ts +2 -0
- package/dist/__tests__/views/version-history-panel.render.test.d.ts.map +1 -0
- package/dist/__tests__/views/version-history-panel.render.test.js +153 -0
- package/dist/__tests__/views/version-history-panel.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 +33 -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/layout/primitives/AdminShell.d.ts.map +1 -1
- package/dist/layout/primitives/AdminShell.js +2 -2
- package/dist/layout/primitives/AdminShell.js.map +1 -1
- 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/lib/version-diff.d.ts +50 -0
- package/dist/lib/version-diff.d.ts.map +1 -0
- package/dist/lib/version-diff.js +294 -0
- package/dist/lib/version-diff.js.map +1 -0
- package/dist/lib/version-history.d.ts +73 -0
- package/dist/lib/version-history.d.ts.map +1 -0
- package/dist/lib/version-history.js +126 -0
- package/dist/lib/version-history.js.map +1 -0
- 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 +47 -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 +21 -2
- package/dist/views/page-editor/EditorTopBar.d.ts.map +1 -1
- package/dist/views/page-editor/EditorTopBar.js +13 -20
- 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 +383 -78
- 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/ReviewBar.d.ts +21 -0
- package/dist/views/page-editor/ReviewBar.d.ts.map +1 -0
- package/dist/views/page-editor/ReviewBar.js +22 -0
- package/dist/views/page-editor/ReviewBar.js.map +1 -0
- package/dist/views/page-editor/SectionInspector.d.ts.map +1 -1
- package/dist/views/page-editor/SectionInspector.js +64 -20
- 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/VersionHistoryPanel.d.ts +29 -0
- package/dist/views/page-editor/VersionHistoryPanel.d.ts.map +1 -0
- package/dist/views/page-editor/VersionHistoryPanel.js +81 -0
- package/dist/views/page-editor/VersionHistoryPanel.js.map +1 -0
- 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 +13 -1
- package/dist/views/page-editor/sections/SectionRenderer.d.ts.map +1 -1
- package/dist/views/page-editor/sections/SectionRenderer.js +66 -26
- 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__/lib/version-diff.test.ts +278 -0
- package/src/__tests__/lib/version-history.test.ts +166 -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/responsive-layer.test.ts +152 -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/canvas-review-mode.render.test.tsx +94 -0
- package/src/__tests__/views/editor-top-bar.render.test.tsx +43 -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/__tests__/views/version-history-panel.render.test.tsx +195 -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 +89 -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/layout/primitives/AdminShell.tsx +13 -1
- 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/lib/version-diff.ts +376 -0
- package/src/lib/version-history.ts +192 -0
- package/src/styles/prenta-tokens.css +120 -42
- package/src/styles/responsive.css +281 -0
- package/src/styles/theme.css +138 -0
- package/src/views/page-editor/CanvasMetaLine.tsx +50 -0
- package/src/views/page-editor/EditorCanvas.tsx +140 -54
- package/src/views/page-editor/EditorTopBar.tsx +55 -51
- package/src/views/page-editor/PageSectionEditor.tsx +575 -185
- package/src/views/page-editor/PageTab.tsx +433 -0
- package/src/views/page-editor/ReviewBar.tsx +76 -0
- package/src/views/page-editor/SectionInspector.tsx +84 -42
- package/src/views/page-editor/SectionsPanel.tsx +282 -141
- package/src/views/page-editor/SitePreviewFrame.tsx +3 -0
- package/src/views/page-editor/VersionHistoryPanel.tsx +302 -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 +115 -34
- 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
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useState } from 'react'
|
|
4
|
+
import * as DropdownMenu from '@radix-ui/react-dropdown-menu'
|
|
5
|
+
import { Check, ChevronDown } from 'lucide-react'
|
|
6
|
+
import { adminPortalContainer } from '../lib/portal-container.js'
|
|
7
|
+
import {
|
|
8
|
+
BREAKPOINT_GROUPS,
|
|
9
|
+
breakpointsInGroup,
|
|
10
|
+
getBreakpoint,
|
|
11
|
+
type BreakpointId,
|
|
12
|
+
} from '../lib/breakpoints.js'
|
|
13
|
+
import { breakpointIcon } from './breakpoint-icons.js'
|
|
14
|
+
|
|
15
|
+
export interface BreakpointPickerProps {
|
|
16
|
+
value: BreakpointId
|
|
17
|
+
onChange: (id: BreakpointId) => void
|
|
18
|
+
disabled?: boolean
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Preview-breakpoint selector for the editor header.
|
|
23
|
+
*
|
|
24
|
+
* A dropdown rather than a segmented row: six presets with names like "Tablet
|
|
25
|
+
* Landscape" do not fit a header strip, and the grouped menu is what makes the
|
|
26
|
+
* DESKTOP / TABLET / MOBILE structure legible.
|
|
27
|
+
*
|
|
28
|
+
* Radix owns the menu behaviour — outside-click, Escape, roving focus, and
|
|
29
|
+
* `aria-expanded` on the trigger — rather than a hand-rolled popover.
|
|
30
|
+
*/
|
|
31
|
+
export function BreakpointPicker({ value, onChange, disabled }: BreakpointPickerProps) {
|
|
32
|
+
const [open, setOpen] = useState(false)
|
|
33
|
+
const active = getBreakpoint(value)
|
|
34
|
+
const ActiveIcon = breakpointIcon(active.icon)
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<DropdownMenu.Root open={open} onOpenChange={setOpen}>
|
|
38
|
+
<DropdownMenu.Trigger asChild>
|
|
39
|
+
<button
|
|
40
|
+
type="button"
|
|
41
|
+
disabled={disabled}
|
|
42
|
+
aria-label={`Preview breakpoint: ${active.name}, ${active.width} pixels`}
|
|
43
|
+
className={`border-border flex h-[34px] items-center gap-1.5 rounded-lg border px-2.5 text-[12.5px] font-medium transition-colors disabled:opacity-50 ${
|
|
44
|
+
open
|
|
45
|
+
? 'border-brand bg-accent text-accent-foreground'
|
|
46
|
+
: 'bg-card text-foreground hover:bg-accent'
|
|
47
|
+
}`}
|
|
48
|
+
>
|
|
49
|
+
<ActiveIcon className={`h-4 w-4 shrink-0 ${open ? '' : 'text-brand'}`} aria-hidden />
|
|
50
|
+
{/* The name is the useful label; below `lg` the header is too tight
|
|
51
|
+
for it, so fall back to the two-letter form rather than truncating. */}
|
|
52
|
+
<span className="hidden lg:inline">{active.name}</span>
|
|
53
|
+
<span className="lg:hidden">{active.abbr}</span>
|
|
54
|
+
<span className="text-muted-foreground hidden text-[11.5px] tabular-nums xl:inline">
|
|
55
|
+
{active.width}
|
|
56
|
+
</span>
|
|
57
|
+
<ChevronDown
|
|
58
|
+
className={`h-3.5 w-3.5 shrink-0 transition-transform ${open ? 'rotate-180' : ''}`}
|
|
59
|
+
aria-hidden
|
|
60
|
+
/>
|
|
61
|
+
</button>
|
|
62
|
+
</DropdownMenu.Trigger>
|
|
63
|
+
|
|
64
|
+
<DropdownMenu.Portal container={adminPortalContainer()}>
|
|
65
|
+
<DropdownMenu.Content
|
|
66
|
+
align="end"
|
|
67
|
+
sideOffset={6}
|
|
68
|
+
className="border-border bg-popover data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:slide-in-from-top-1 z-[120] w-[272px] rounded-[10px] border p-[5px] shadow-[0_12px_34px_rgba(26,24,39,0.17)] duration-[130ms]"
|
|
69
|
+
>
|
|
70
|
+
{BREAKPOINT_GROUPS.map((group) => (
|
|
71
|
+
<DropdownMenu.Group key={group}>
|
|
72
|
+
<DropdownMenu.Label className="text-muted-foreground px-2.5 pt-2 pb-1 text-[10px] font-bold tracking-[0.6px]">
|
|
73
|
+
{group}
|
|
74
|
+
</DropdownMenu.Label>
|
|
75
|
+
{breakpointsInGroup(group).map((bp) => {
|
|
76
|
+
const Icon = breakpointIcon(bp.icon)
|
|
77
|
+
const selected = bp.id === value
|
|
78
|
+
return (
|
|
79
|
+
<DropdownMenu.Item
|
|
80
|
+
key={bp.id}
|
|
81
|
+
onSelect={() => onChange(bp.id)}
|
|
82
|
+
className={`focus:bg-accent flex cursor-pointer items-center gap-2 rounded-[7px] px-2.5 py-[7px] outline-none ${
|
|
83
|
+
selected ? 'bg-accent' : ''
|
|
84
|
+
}`}
|
|
85
|
+
>
|
|
86
|
+
<Icon
|
|
87
|
+
className={`h-4 w-4 shrink-0 ${selected ? 'text-brand' : 'text-muted-foreground'}`}
|
|
88
|
+
aria-hidden
|
|
89
|
+
/>
|
|
90
|
+
<span
|
|
91
|
+
className={`flex-1 text-[13px] ${selected ? 'text-brand font-semibold' : 'text-foreground'}`}
|
|
92
|
+
>
|
|
93
|
+
{bp.name}
|
|
94
|
+
</span>
|
|
95
|
+
<span className="text-muted-foreground text-[11.5px] tabular-nums">
|
|
96
|
+
{bp.width}
|
|
97
|
+
</span>
|
|
98
|
+
{/* Not aria-hidden: the check is the only thing marking the
|
|
99
|
+
current selection, and Radix menu items expose no
|
|
100
|
+
selected state of their own. */}
|
|
101
|
+
<Check
|
|
102
|
+
className={`text-brand h-3.5 w-3.5 shrink-0 ${selected ? '' : 'invisible'}`}
|
|
103
|
+
aria-label={selected ? 'Selected' : undefined}
|
|
104
|
+
/>
|
|
105
|
+
</DropdownMenu.Item>
|
|
106
|
+
)
|
|
107
|
+
})}
|
|
108
|
+
</DropdownMenu.Group>
|
|
109
|
+
))}
|
|
110
|
+
</DropdownMenu.Content>
|
|
111
|
+
</DropdownMenu.Portal>
|
|
112
|
+
</DropdownMenu.Root>
|
|
113
|
+
)
|
|
114
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useEffect, useState } from 'react'
|
|
4
|
+
import { MousePointerClick, X } from 'lucide-react'
|
|
5
|
+
import { dismissCanvasEditTip, isCanvasEditTipDismissed } from '../lib/canvas-edit-chrome.js'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* One-time tip above the page/post canvas explaining how to edit.
|
|
9
|
+
* Dismiss persists in localStorage (`prenta.canvasEditTip.dismissed`).
|
|
10
|
+
*/
|
|
11
|
+
export function CanvasEditTip() {
|
|
12
|
+
const [visible, setVisible] = useState(false)
|
|
13
|
+
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
setVisible(!isCanvasEditTipDismissed())
|
|
16
|
+
}, [])
|
|
17
|
+
|
|
18
|
+
if (!visible) return null
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<div
|
|
22
|
+
role="note"
|
|
23
|
+
className="border-border bg-card text-muted-foreground mx-auto mb-4 flex w-fit max-w-full items-start gap-2 rounded-lg border px-3 py-2 text-xs"
|
|
24
|
+
>
|
|
25
|
+
<MousePointerClick className="text-foreground mt-0.5 h-3.5 w-3.5 shrink-0" aria-hidden />
|
|
26
|
+
<p className="min-w-0 flex-1 leading-[1.5]">
|
|
27
|
+
<span className="text-foreground font-medium">Editing tip</span>
|
|
28
|
+
{' — '}
|
|
29
|
+
Hover sections for a light outline, then click to select. Edit copy and images in the
|
|
30
|
+
inspector. Use <span className="text-foreground font-medium">Site preview</span> to click
|
|
31
|
+
text and images in place.
|
|
32
|
+
</p>
|
|
33
|
+
<button
|
|
34
|
+
type="button"
|
|
35
|
+
aria-label="Dismiss editing tip"
|
|
36
|
+
onClick={() => {
|
|
37
|
+
dismissCanvasEditTip()
|
|
38
|
+
setVisible(false)
|
|
39
|
+
}}
|
|
40
|
+
className="text-muted-foreground hover:bg-accent hover:text-foreground focus-visible:ring-ring -mt-0.5 -mr-0.5 shrink-0 rounded-md p-1 transition-colors focus-visible:ring-2 focus-visible:outline-none"
|
|
41
|
+
>
|
|
42
|
+
<X className="h-3.5 w-3.5" aria-hidden />
|
|
43
|
+
</button>
|
|
44
|
+
</div>
|
|
45
|
+
)
|
|
46
|
+
}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useCallback, useEffect, useRef, useState } from 'react'
|
|
4
|
+
import { PanelLeftClose, PanelLeftOpen } from 'lucide-react'
|
|
5
|
+
import {
|
|
6
|
+
RAIL_COLLAPSED_WIDTH,
|
|
7
|
+
RAIL_DEFAULT_WIDTH,
|
|
8
|
+
RAIL_MAX_WIDTH,
|
|
9
|
+
RAIL_MIN_WIDTH,
|
|
10
|
+
clampRailWidth,
|
|
11
|
+
readRailWidth,
|
|
12
|
+
writeRailWidth,
|
|
13
|
+
} from '../lib/rail-width.js'
|
|
14
|
+
|
|
15
|
+
/** What the rail hands its children so they can render a header. */
|
|
16
|
+
export interface RailState {
|
|
17
|
+
width: number
|
|
18
|
+
dragging: boolean
|
|
19
|
+
collapse: () => void
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface EditorRailProps {
|
|
23
|
+
/** Short label for the collapsed strip — normally the active tab's name. */
|
|
24
|
+
collapsedLabel: string
|
|
25
|
+
children: (state: RailState) => React.ReactNode
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The editor's left rail: resizable, collapsible, and width-persistent.
|
|
30
|
+
*
|
|
31
|
+
* Collapsing UNMOUNTS the rail and swaps in a narrow strip rather than
|
|
32
|
+
* animating its width to zero. A zero-width panel leaves a visible sliver of
|
|
33
|
+
* border and keeps its contents in the accessibility tree, so a screen-reader
|
|
34
|
+
* user lands inside a panel sighted users cannot see.
|
|
35
|
+
*
|
|
36
|
+
* Children are a render prop rather than plain nodes because the panel header
|
|
37
|
+
* needs the live width and drag state — the numbers the rail owns — and
|
|
38
|
+
* threading them through context for one consumer is more machinery than the
|
|
39
|
+
* problem deserves.
|
|
40
|
+
*/
|
|
41
|
+
export function EditorRail({ collapsedLabel, children }: EditorRailProps) {
|
|
42
|
+
const [width, setWidth] = useState(RAIL_DEFAULT_WIDTH)
|
|
43
|
+
const [open, setOpen] = useState(true)
|
|
44
|
+
const [dragging, setDragging] = useState(false)
|
|
45
|
+
const railRef = useRef<HTMLDivElement>(null)
|
|
46
|
+
|
|
47
|
+
// Read persisted width after mount, never during render: there is no
|
|
48
|
+
// localStorage on the server, and seeding initial state from it would
|
|
49
|
+
// hydrate-mismatch.
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
setWidth(readRailWidth())
|
|
52
|
+
}, [])
|
|
53
|
+
|
|
54
|
+
const applyWidth = useCallback((next: number) => {
|
|
55
|
+
const clamped = clampRailWidth(next)
|
|
56
|
+
setWidth(clamped)
|
|
57
|
+
writeRailWidth(clamped)
|
|
58
|
+
}, [])
|
|
59
|
+
|
|
60
|
+
const collapse = useCallback(() => setOpen(false), [])
|
|
61
|
+
|
|
62
|
+
// Pointer listeners go on `window`, not the handle: the pointer routinely
|
|
63
|
+
// outruns a 7px target mid-drag, and a handle-local listener would drop the
|
|
64
|
+
// drag the moment it did.
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
if (!dragging) return
|
|
67
|
+
|
|
68
|
+
const onMove = (event: PointerEvent) => {
|
|
69
|
+
const left = railRef.current?.getBoundingClientRect().left ?? 0
|
|
70
|
+
applyWidth(event.clientX - left)
|
|
71
|
+
}
|
|
72
|
+
const stop = () => setDragging(false)
|
|
73
|
+
|
|
74
|
+
window.addEventListener('pointermove', onMove)
|
|
75
|
+
window.addEventListener('pointerup', stop)
|
|
76
|
+
window.addEventListener('pointercancel', stop)
|
|
77
|
+
|
|
78
|
+
// Lock cursor and selection for the duration. Without this the cursor
|
|
79
|
+
// flickers between col-resize and text as it crosses elements, and the
|
|
80
|
+
// drag selects the panel's copy.
|
|
81
|
+
const { body } = document
|
|
82
|
+
const prevCursor = body.style.cursor
|
|
83
|
+
const prevSelect = body.style.userSelect
|
|
84
|
+
body.style.cursor = 'col-resize'
|
|
85
|
+
body.style.userSelect = 'none'
|
|
86
|
+
|
|
87
|
+
return () => {
|
|
88
|
+
window.removeEventListener('pointermove', onMove)
|
|
89
|
+
window.removeEventListener('pointerup', stop)
|
|
90
|
+
window.removeEventListener('pointercancel', stop)
|
|
91
|
+
body.style.cursor = prevCursor
|
|
92
|
+
body.style.userSelect = prevSelect
|
|
93
|
+
}
|
|
94
|
+
}, [dragging, applyWidth])
|
|
95
|
+
|
|
96
|
+
if (!open) {
|
|
97
|
+
return (
|
|
98
|
+
<div
|
|
99
|
+
className="prenta-editor-rail-collapsed bg-card border-border flex shrink-0 flex-col items-center gap-3 border-r py-3"
|
|
100
|
+
style={{ width: RAIL_COLLAPSED_WIDTH }}
|
|
101
|
+
>
|
|
102
|
+
<button
|
|
103
|
+
type="button"
|
|
104
|
+
onClick={() => setOpen(true)}
|
|
105
|
+
aria-label={`Expand ${collapsedLabel} panel`}
|
|
106
|
+
title={`Expand ${collapsedLabel} panel`}
|
|
107
|
+
className="prenta-touch-target text-muted-foreground hover:bg-accent hover:text-accent-foreground flex h-6.5 w-6.5 items-center justify-center rounded-md transition-colors"
|
|
108
|
+
>
|
|
109
|
+
<PanelLeftOpen className="h-4 w-4" aria-hidden />
|
|
110
|
+
</button>
|
|
111
|
+
{/* Vertical label so the strip still says which panel is hidden. The
|
|
112
|
+
button's accessible name already carries it, hence aria-hidden. */}
|
|
113
|
+
<span
|
|
114
|
+
className="prenta-rail-label text-muted-foreground text-[10px] font-bold tracking-[0.8px] whitespace-nowrap uppercase [writing-mode:vertical-rl]"
|
|
115
|
+
aria-hidden
|
|
116
|
+
>
|
|
117
|
+
{collapsedLabel}
|
|
118
|
+
</span>
|
|
119
|
+
</div>
|
|
120
|
+
)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return (
|
|
124
|
+
<div
|
|
125
|
+
ref={railRef}
|
|
126
|
+
className={`prenta-editor-rail bg-card border-border relative flex shrink-0 flex-col border-r ${
|
|
127
|
+
// A width transition during a drag fights the pointer: the panel
|
|
128
|
+
// chases the cursor a frame behind and the resize feels broken.
|
|
129
|
+
dragging ? '' : 'motion-safe:transition-[width] motion-safe:duration-(--mo-slow)'
|
|
130
|
+
}`}
|
|
131
|
+
style={{ width }}
|
|
132
|
+
>
|
|
133
|
+
<div className="flex min-h-0 flex-1 flex-col">{children({ width, dragging, collapse })}</div>
|
|
134
|
+
|
|
135
|
+
<div
|
|
136
|
+
// `separator` with an orientation and a value range is what makes this
|
|
137
|
+
// operable without a pointer. A bare div with a pointerdown handler is
|
|
138
|
+
// invisible to keyboard and assistive-tech users.
|
|
139
|
+
role="separator"
|
|
140
|
+
aria-orientation="vertical"
|
|
141
|
+
aria-label="Resize panel"
|
|
142
|
+
aria-valuenow={width}
|
|
143
|
+
aria-valuemin={RAIL_MIN_WIDTH}
|
|
144
|
+
aria-valuemax={RAIL_MAX_WIDTH}
|
|
145
|
+
tabIndex={0}
|
|
146
|
+
onPointerDown={(event) => {
|
|
147
|
+
event.preventDefault()
|
|
148
|
+
setDragging(true)
|
|
149
|
+
}}
|
|
150
|
+
onDoubleClick={() => applyWidth(RAIL_DEFAULT_WIDTH)}
|
|
151
|
+
onKeyDown={(event) => {
|
|
152
|
+
const step = event.shiftKey ? 48 : 16
|
|
153
|
+
if (event.key === 'ArrowLeft') {
|
|
154
|
+
event.preventDefault()
|
|
155
|
+
applyWidth(width - step)
|
|
156
|
+
} else if (event.key === 'ArrowRight') {
|
|
157
|
+
event.preventDefault()
|
|
158
|
+
applyWidth(width + step)
|
|
159
|
+
} else if (event.key === 'Home') {
|
|
160
|
+
event.preventDefault()
|
|
161
|
+
applyWidth(RAIL_DEFAULT_WIDTH)
|
|
162
|
+
}
|
|
163
|
+
}}
|
|
164
|
+
className="group absolute inset-y-0 -right-[3px] z-30 w-[7px] cursor-col-resize focus-visible:outline-none"
|
|
165
|
+
>
|
|
166
|
+
{/* The 2px line it draws sits inside a 7px hit area on purpose — a 2px
|
|
167
|
+
pointer target is a coin flip. */}
|
|
168
|
+
<span
|
|
169
|
+
aria-hidden
|
|
170
|
+
className={`pointer-events-none absolute inset-y-0 left-[2.5px] w-0.5 transition-colors ${
|
|
171
|
+
dragging
|
|
172
|
+
? 'bg-brand'
|
|
173
|
+
: 'group-hover:bg-brand group-focus-visible:bg-brand bg-transparent'
|
|
174
|
+
}`}
|
|
175
|
+
/>
|
|
176
|
+
</div>
|
|
177
|
+
</div>
|
|
178
|
+
)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Header strip under the rail's tabs: panel title, the live width while
|
|
183
|
+
* dragging, and the collapse control.
|
|
184
|
+
*/
|
|
185
|
+
export function RailHeader({
|
|
186
|
+
title,
|
|
187
|
+
width,
|
|
188
|
+
dragging,
|
|
189
|
+
onCollapse,
|
|
190
|
+
}: {
|
|
191
|
+
title: string
|
|
192
|
+
width: number
|
|
193
|
+
dragging: boolean
|
|
194
|
+
onCollapse: () => void
|
|
195
|
+
}) {
|
|
196
|
+
return (
|
|
197
|
+
<div className="border-border flex items-center justify-between gap-2 border-b px-3.5 py-2.5">
|
|
198
|
+
<h2 className="text-foreground truncate text-[12.5px] font-semibold">{title}</h2>
|
|
199
|
+
<div className="flex shrink-0 items-center gap-1.5">
|
|
200
|
+
{/* Only while dragging: a permanent pixel readout is noise, but during
|
|
201
|
+
a resize it is the one number the user is actually aiming at. */}
|
|
202
|
+
{dragging && (
|
|
203
|
+
<span className="text-muted-foreground text-[11px] tabular-nums" aria-hidden>
|
|
204
|
+
{width}px
|
|
205
|
+
</span>
|
|
206
|
+
)}
|
|
207
|
+
<button
|
|
208
|
+
type="button"
|
|
209
|
+
onClick={onCollapse}
|
|
210
|
+
aria-label={`Collapse ${title} panel`}
|
|
211
|
+
title={`Collapse ${title} panel`}
|
|
212
|
+
className="prenta-touch-target text-muted-foreground hover:bg-accent hover:text-accent-foreground flex h-6.5 w-6.5 items-center justify-center rounded-md transition-colors"
|
|
213
|
+
>
|
|
214
|
+
<PanelLeftClose className="h-4 w-4" aria-hidden />
|
|
215
|
+
</button>
|
|
216
|
+
</div>
|
|
217
|
+
</div>
|
|
218
|
+
)
|
|
219
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { scoreBand } from '@prenta/core/seo/page-audit'
|
|
4
|
+
|
|
5
|
+
export type RailTabId = 'sections' | 'seo' | 'page'
|
|
6
|
+
|
|
7
|
+
export interface RailTab {
|
|
8
|
+
id: RailTabId
|
|
9
|
+
label: string
|
|
10
|
+
/** Panel title shown in the header below the strip. */
|
|
11
|
+
title: string
|
|
12
|
+
/** Count pill (Sections). */
|
|
13
|
+
count?: number
|
|
14
|
+
/**
|
|
15
|
+
* Score for the dot, banded by the page audit's own scale.
|
|
16
|
+
*
|
|
17
|
+
* `undefined` — not known yet; no dot is shown rather than a misleading one.
|
|
18
|
+
* `null` — deliberately not rated (a gate is off, e.g. the page is
|
|
19
|
+
* noindexed). Renders neutral, never as a low score.
|
|
20
|
+
*/
|
|
21
|
+
score?: number | null
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface EditorRailTabsProps {
|
|
25
|
+
tabs: RailTab[]
|
|
26
|
+
active: RailTabId
|
|
27
|
+
onChange: (id: RailTabId) => void
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Three equal tabs across the top of the rail.
|
|
32
|
+
*
|
|
33
|
+
* Each tab carries a status glyph so the rail reads at a glance without being
|
|
34
|
+
* opened: a count for Sections, a band-coloured dot for SEO. The dot is
|
|
35
|
+
* accompanied by text in the accessible name — colour alone is not a signal
|
|
36
|
+
* for anyone who cannot distinguish these hues.
|
|
37
|
+
*/
|
|
38
|
+
export function EditorRailTabs({ tabs, active, onChange }: EditorRailTabsProps) {
|
|
39
|
+
return (
|
|
40
|
+
<div role="tablist" aria-label="Editor panels" className="border-border flex border-b">
|
|
41
|
+
{tabs.map((tab) => {
|
|
42
|
+
const selected = tab.id === active
|
|
43
|
+
// Bands with `scoreBand` from the audit itself, NOT `gradeFromScore`.
|
|
44
|
+
// The two disagree between 55 and 59 — a page scoring 57 read "fair"
|
|
45
|
+
// in the rings and "poor" on the tab summarising them, which is the
|
|
46
|
+
// same class of disagreement this dot was rewired to eliminate.
|
|
47
|
+
const band = typeof tab.score === 'number' ? scoreBand(tab.score) : null
|
|
48
|
+
const unrated = tab.score === null
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<button
|
|
52
|
+
key={tab.id}
|
|
53
|
+
type="button"
|
|
54
|
+
role="tab"
|
|
55
|
+
id={`rail-tab-${tab.id}`}
|
|
56
|
+
aria-selected={selected}
|
|
57
|
+
aria-controls={`rail-panel-${tab.id}`}
|
|
58
|
+
tabIndex={selected ? 0 : -1}
|
|
59
|
+
onClick={() => onChange(tab.id)}
|
|
60
|
+
onKeyDown={(event) => {
|
|
61
|
+
// Arrow keys move between tabs — expected of a tablist, and the
|
|
62
|
+
// only way to reach a tab whose tabIndex is -1.
|
|
63
|
+
const index = tabs.findIndex((t) => t.id === active)
|
|
64
|
+
if (event.key === 'ArrowRight' || event.key === 'ArrowLeft') {
|
|
65
|
+
event.preventDefault()
|
|
66
|
+
const delta = event.key === 'ArrowRight' ? 1 : -1
|
|
67
|
+
const next = tabs[(index + delta + tabs.length) % tabs.length]
|
|
68
|
+
if (next) {
|
|
69
|
+
onChange(next.id)
|
|
70
|
+
document.getElementById(`rail-tab-${next.id}`)?.focus()
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}}
|
|
74
|
+
className={`flex flex-1 items-center justify-center gap-1.5 border-b-2 pt-2.5 pb-2 text-[12.5px] transition-colors ${
|
|
75
|
+
selected
|
|
76
|
+
? 'border-brand text-brand font-semibold'
|
|
77
|
+
: 'text-muted-foreground hover:text-foreground border-transparent font-medium'
|
|
78
|
+
}`}
|
|
79
|
+
>
|
|
80
|
+
{tab.label}
|
|
81
|
+
|
|
82
|
+
{tab.count !== undefined && (
|
|
83
|
+
<span
|
|
84
|
+
className={`rounded-full px-1.5 text-[10.5px] font-semibold tabular-nums ${
|
|
85
|
+
selected ? 'bg-accent text-accent-foreground' : 'bg-muted text-muted-foreground'
|
|
86
|
+
}`}
|
|
87
|
+
>
|
|
88
|
+
{tab.count}
|
|
89
|
+
</span>
|
|
90
|
+
)}
|
|
91
|
+
|
|
92
|
+
{(band || unrated) && (
|
|
93
|
+
<>
|
|
94
|
+
<span
|
|
95
|
+
className={`h-[7px] w-[7px] shrink-0 rounded-full ${
|
|
96
|
+
band === 'good'
|
|
97
|
+
? 'bg-success'
|
|
98
|
+
: band === 'fair'
|
|
99
|
+
? 'bg-warning'
|
|
100
|
+
: band === 'poor'
|
|
101
|
+
? 'bg-destructive'
|
|
102
|
+
: 'bg-muted-foreground/40'
|
|
103
|
+
}`}
|
|
104
|
+
aria-hidden
|
|
105
|
+
/>
|
|
106
|
+
{/* Colour alone cannot carry the score. */}
|
|
107
|
+
<span className="sr-only">
|
|
108
|
+
{unrated ? 'not rated' : `score ${tab.score} of 100`}
|
|
109
|
+
</span>
|
|
110
|
+
</>
|
|
111
|
+
)}
|
|
112
|
+
</button>
|
|
113
|
+
)
|
|
114
|
+
})}
|
|
115
|
+
</div>
|
|
116
|
+
)
|
|
117
|
+
}
|