Commit Graph

4 Commits

Author SHA1 Message Date
shadowdao b88b242b6a refactor(builder): extract shared Modal component for the three editor modals
New src/ui/Modal.tsx ({ open, onClose, title?, children, width?,
closeOnEscape?, closeOnBackdropClick?, backdropStyle?, backdropProps? })
owns the backdrop, Escape-to-close, backdrop-click-to-close, and a new
body-scroll-lock while open. Adopted by TemplateModal, HeadCodeModal, and
SitesmithModal; each keeps its own panel styling/header/footer as children
since those differ per modal.

- TemplateModal: Escape/backdrop-click still close the confirm-template
  sub-dialog first via a wrapped onClose passed to Modal; the header's X
  button keeps using the raw onClose prop (always fully closes), matching
  prior asymmetric behavior.
- HeadCodeModal: straightforward adoption, no prior custom close logic.
- SitesmithModal: previously had no `open` prop, no Escape-to-close, and no
  backdrop-click-to-close. Preserved via open (always mounted-open by its
  parent already), closeOnEscape={false}, closeOnBackdropClick={false}; role
  and aria-modal are passed through via backdropProps to keep them on the
  same backdrop element as before.

Body scroll-lock while a modal is open is a small new addition (requested by
the task) applied uniformly; it has no visible effect since each modal's
opaque fixed-position backdrop already fully covers the viewport.

Added a light Modal.test.tsx (renders children, Escape/backdrop-click close
behavior, scroll-lock) using the existing react-dom/client + act harness.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 15:22:05 -07:00
shadowdao 46ebd253f3 a11y: keyboard-operable editor chrome + topbar aria-labels
Clickable <div> rows/tiles (page list, layer tree, template cards, asset
picker grid) now expose role="button", a tab stop, and Enter/Space
activation via a shared clickableProps() helper, matching their existing
onClick behavior. TopBar icon-only controls (device switcher, undo/redo,
save, publish, templates, code, preview, back) gain aria-label alongside
their existing title tooltips.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 14:50:50 -07:00
shadowdao 7973ee9ba8 fix(builder): await TemplateModal's load sequence before closing (D5)
The template-load sequence (clear canvas -> add components -> add
pages -> apply header/footer) ran as a chain of setTimeout callbacks,
but onClose() fired synchronously right after kicking it off. The
modal disappeared immediately, so "Loading..." never had a chance to
show, and the staged mutations kept running against a dialog the
caller had already dismissed.

Convert the staged steps to async/await (a promise-based `wait()`
replaces the setTimeout chaining) and only call onClose() in a
finally block once the whole sequence has settled. Add a mountedRef
guard so the trailing setLoading(false) is skipped if the component
happens to unmount mid-sequence. End result (pages/header/footer/
design applied) is unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 13:26:46 -07:00
shadowdao 91a6b6f34b Add Craft.js site builder (v2) - complete rebuild from GrapesJS
Rebuilt the visual site builder from scratch using Craft.js, React 18,
and TypeScript. The new editor renders directly in the DOM (no iframe),
supports 40+ components, multi-page with shared header/footer, 16
templates, full-spectrum color/gradient controls, custom head code
injection, save/publish workflow, and auto-save.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 18:31:16 -07:00