Editor: rebuild the Page module screen on shared components — insert rows, drag and drop, categorised insert dialog, clipboard #171
Labels
No labels
bug
enhancement
needs-triage
ready-for-agent
ready-for-human
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Cloonar/deckle#171
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Parent
#168 — the editing screens overhaul, Direction A (page module). Mockup and decision log:
docs/mockups/editing-screens/(README items 1, 2, 4–7, 9;dist/deckle-page-module.htmlis the clickable reference), merged in #169.Summary
Rebuild the Page module's content screen on a shared component layer: the backend layout grid with equal-height columns, TYPO3-style always-visible insert rows that double as drag-and-drop targets, a categorised insert dialog, a real element menu, cut/copy/paste within and across pages, Content/Properties as docheader tabs, the disabled-page banner, and a pending-changes panel in the top bar.
PagePane.tsx(~2,260 lines) is replaced by small components that every later screen reuses.Current behavior
web/src/components/PagePane.tsxrenders either a flat indented list (no backend layout) or a CSS grid of slot cells, with gap/row/drop markup written twice; the insert picker's Structure/Content lists are copy-pasted; the ⋮ menu offers Move to column…, Move up/down, Duplicate, Delete…; inserting opens the "Insert element" dialog grouped bygroup(structure/content) and navigates to the new element's edit page; drag and drop usesdropzone.ts(before/after/into zones) anddragReorder.ts, and #127 reports drops in a row edge zone silently no-op.?props=1, swapping the list forPageSettings.ModuleScreen.tsxduplicatesPageBadge; four error-strip styles; the page ⋯ menu borrows element-menu classes).Desired behavior
Everything below is shown in the mockup; where this text and the mockup differ, the mockup wins.
Shared component layer (
web/src/components/, names as in the mockup's Components overlay):Docheader(two rows: button bar; crumbs + title + badge; optional tabs),ButtonBar,Tabs,Badge,Menu(popover with items, separators, danger items, keyboard: Escape closes, focus returns to the trigger; built onlayers.ts),Dialog,LayoutGrid→SlotColumn→InsertRow+ElementCard,ElementPicker,ClipboardBar,PageBanner,PendingChanges. Styles use the tokens inweb/src/styles.css(#72). The duplicated badge markup and the error-strip styles that this screen touches are replaced by the shared components; the legacyFormPanewrapper and the stale "no module has been migrated" note inDocheader.tsxgo.Layout grid
LayoutGridwith one slot, so the flat list and the slot grid are one code path. Nested visible children of a block element (where a placement allows them) render as an indented sub-list inside the card, with their own insert rows, using the same components.elementsallowlist when it has one.Insert rows
InsertRowat the top of each editable slot and after every element. It is always visible: a centred small "+ Element" button in a fixed-height row (the row is as tall as the drop zone it becomes, so starting a drag shifts nothing).Insert dialog (
ElementPicker)category(a free label string; elements without one fall under Other;group: inlineandhidden: trueelements never appear). The scaffold declares: Text (hero, text, cta, quote, accordion), Media (text-media, image, video-embed), Lists & teasers (cards, page-teasers; the news bundle's news-list), Forms (contact-form), Structure (spacer).scaffold/site/AGENTS.mddocuments the key.emptyPickerReasonbecomes this per-tile reason.insertable: falseelements do not appear at all (unchanged).Drag and drop
dropzone.ts's before/after/into edge zones, and with it the silent no-op of #127 (reference it in the PR; do not close it from this issue).Element card and menu
#uid, Disabled badge (fromenabled, see the blocked-by issue), two-line summary of the element's content, thumbnails for image items; hover/focus reveals Edit, Enable/Disable, ⋮. Single click or Enter opens the form.Menucomponent.Clipboard
ClipboardBarabove the grid names the element and its source page, with Clear.POST /api/element/moveand/duplicatewith a target page, or add dedicated ops — implementer's call); media references and links inside the element travel unchanged; the result updates both pages' trees and is one git snapshot like every save.Page level
PageSettingsin place (the?props=1route goes away; the form screen issue rebuilds the form itself).PageBannerabove the grid: "This page is disabled. It is not built into the site, and links to it are reported before publishing." with Enable page.PendingChangespanel: the pages and media files with saved, unpublished changes, one line each ("Services — 2 elements changed, 1 added"; "Hosting — new page, not published yet"; "assets/media/… — added"), each row opens the page or the Media module; Publish all (today's Publish) and Discard all… at the bottom.Still true afterwards: the entry editor's Content tab embeds the same grid without chrome (#153); feature modules (News, Categories) keep working; hover on a card still posts the highlight to the preview bridge; the hosted header slot (#139) is untouched.
Decisions
categoryis a free label on the sidecar, not an enum — bundles and site owners name their own categories; "All" and "Other" are the only built-ins.Key interfaces
web/src/components/PagePane.tsx→ replaced byLayoutGrid,SlotColumn,InsertRow,ElementCard,ElementPicker;elementcrud.ts(allowedElements,placementAllowed,emptyPickerReason,moveToIndex,DropVerdict) keeps the rules;dropzone.tsis retired;dragReorder.tsmay stay as the drag state machine.web/src/api/elementops.ts(insertElement,moveElement,duplicateElement) and their handlers ininternal/server/internal/elemops— cross-page variants.internal/element/schema.go— thecategorysidecar key (Category string), surfaced throughweb/src/api/elements.ts.internal/backendlayout— slotelementsallowlist andinherit; the override semantics when a page gains its own content in an inherited slot.web/src/layers.ts(openLayer, Escape order) forMenu,Dialog, the pending-changes panel.web/src/api/publish.ts/internal/server/publish.go— the pending-changes listing.scaffold/site/AGENTS.md—category;internal/element/agentsdoc_test.go.Acceptance criteria
PagePane.tsxis gone; the Page module content screen, the entry editor's Content tab, and the no-layout case all render throughLayoutGrid.categoryand AGENTS.md documents it; the drift test passes.web/.?props=1is gone fromrouting.ts.How to verify
make test; the existingPagePane/ElementCrud/dropzonetests are replaced by tests for the new components (insert positions, drop verdicts per slot allowlist, clipboard transitions, menu contents).make runontestdata/site: walk the flows in the acceptance criteria and compare withdocs/mockups/editing-screens/dist/deckle-page-module.html(sh wrap.shto view it).git log/git diffon the site after a cross-page cut-paste: one commit, both files changed as expected.Assumptions to check
inherit(what the slot wrapper looks like then); if not, implement it as part of the override row.publish.gocan enumerate pending changes per page/media cheaply (it already diffscontent/andassets/media/to publish); the panel needs that list.deckle-hosted(read-only import) embeds the editor: check that nothing there depends on?props=1or thePagePaneDOM.Out of scope
EditPane/PageSettingsas they are and only routes to them.Blocked by