implement unified on-site editor phases 1-2
All checks were successful
deploy / deploy (push) Successful in 1m10s

Add theme editing to the existing PageEditorHook, enabling on-site
theme customisation alongside page editing. The editor panel now has
three tabs (Page, Theme, Settings) and can be collapsed while
keeping editing state intact.

- Add theme editing state and event handlers to PageEditorHook
- Add 3-tab UI with tab switching logic
- Add transparent overlay for click-outside dismiss
- Add mobile drag-to-resize with height persistence
- Fix animation replay on drag release (has-dragged class)
- Preserve panel height across LiveView re-renders
- Default to Page tab on editable pages, Theme otherwise
- Show unsaved changes indicator on FAB when panel collapsed
- Fix handle_event grouping warning in admin theme

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
jamey
2026-03-09 09:01:21 +00:00
parent 74ab6411f7
commit 168b6ce76f
10 changed files with 954 additions and 53 deletions

View File

@@ -2525,6 +2525,15 @@
flex-shrink: 0;
}
/* ── Editor overlay ── */
.editor-overlay {
position: fixed;
inset: 0;
z-index: 999;
background: transparent;
cursor: pointer;
}
/* ── Editor panel ── */
.editor-panel {
position: fixed;
@@ -2533,6 +2542,10 @@
flex-direction: column;
background: var(--t-surface-base);
box-shadow: var(--t-shadow-lg, 0 8px 24px rgba(0, 0, 0, 0.15));
/* Force GPU compositing to prevent paint flickering during drag */
transform: translateZ(0);
backface-visibility: hidden;
isolation: isolate;
}
/* Hidden when collapsed (but not during close animation) */
@@ -2540,6 +2553,34 @@
display: none;
}
/* ── Drag handle (mobile only) ── */
.editor-panel-drag-handle {
display: none;
}
@media (max-width: 767px) {
.editor-panel-drag-handle {
display: flex;
justify-content: center;
align-items: center;
padding: 0.5rem;
cursor: grab;
touch-action: none;
flex-shrink: 0;
}
.editor-panel-drag-handle:active {
cursor: grabbing;
}
.editor-panel-drag-handle-bar {
width: 2.5rem;
height: 4px;
border-radius: 2px;
background: var(--t-border-default);
}
}
/* ── Mobile: bottom sheet ── */
@media (max-width: 767px) {
.editor-panel[data-state="open"],
@@ -2547,13 +2588,26 @@
left: 0;
right: 0;
bottom: 0;
top: 15dvh;
/* Default 50vh, overridden by JS with saved preference */
height: var(--editor-panel-height, 50dvh);
max-height: 90dvh;
min-height: 200px;
border-radius: var(--t-radius-lg, 12px) var(--t-radius-lg, 12px) 0 0;
border: 1px solid var(--t-border-default);
border-bottom: none;
/* Keep layer promoted to prevent flash on resize end */
will-change: transform;
contain: strict;
}
.editor-panel[data-state="open"] {
/* During drag: disable animations and also hint height changes */
.editor-panel.dragging {
transition: none !important;
will-change: transform, height;
}
/* Only animate on initial open, not after drag ends */
.editor-panel[data-state="open"]:not(.dragging):not(.has-dragged) {
animation: editor-panel-slide-up 0.3s cubic-bezier(0.32, 0.72, 0, 1) both;
}
@@ -2640,10 +2694,30 @@
.editor-panel-header-actions {
display: flex;
align-items: center;
gap: 0.25rem;
gap: 0.5rem;
flex-shrink: 0;
}
.editor-panel-close {
display: flex;
align-items: center;
justify-content: center;
width: 2rem;
height: 2rem;
padding: 0;
border: none;
border-radius: 0.375rem;
background: transparent;
color: var(--t-text-secondary);
cursor: pointer;
transition: background-color 0.15s, color 0.15s;
&:hover {
background: var(--t-border-default);
color: var(--t-text-primary);
}
}
/* ── Dirty indicator ── */
.editor-panel-dirty {
display: flex;
@@ -2661,12 +2735,50 @@
background: currentColor;
}
/* ── Tab bar ── */
.editor-tabs {
display: flex;
gap: 0;
border-bottom: 1px solid var(--t-border-default);
padding: 0 0.75rem;
flex-shrink: 0;
}
.editor-tab {
flex: 1;
padding: 0.5rem 0.75rem;
font-size: 0.8125rem;
font-weight: 500;
color: var(--t-text-secondary);
background: none;
border: none;
border-bottom: 2px solid transparent;
cursor: pointer;
transition: color 0.15s, border-color 0.15s;
&:hover:not(:disabled) {
color: var(--t-text-primary);
}
&:disabled {
opacity: 0.4;
cursor: not-allowed;
}
}
.editor-tab-active {
color: var(--t-text-primary);
border-bottom-color: var(--t-accent, oklch(0.6 0.15 250));
}
/* ── Panel content ── */
.editor-panel-content {
flex: 1;
overflow-y: auto;
overscroll-behavior: contain;
padding: 0.75rem;
/* Ensure content is clipped to panel bounds during drag */
contain: paint;
@media (min-width: 768px) {
padding: 1rem;