Skip to content

Replace legacy SplitPanel with v6 SplitView #11013#11016

Open
ashklianko wants to merge 12 commits into
masterfrom
issue-11013
Open

Replace legacy SplitPanel with v6 SplitView #11013#11016
ashklianko wants to merge 12 commits into
masterfrom
issue-11013

Conversation

@ashklianko

@ashklianko ashklianko commented Jul 3, 2026

Copy link
Copy Markdown
Member

Replaces the legacy lib-admin-ui SplitPanel — the imperative, class-based split panel that structured the whole browse and wizard UI — with a modern Preact SplitView component built on react-resizable-panels, and migrates every product screen onto it. The legacy panel blocked porting the browse/wizard layouts to v6 and carried a large amount of hand-rolled drag/resize/responsive code with no keyboard accessibility. This PR delivers the component and moves browse, wizard, and settings onto it behind the existing v6 stores, so the layouts become declarative, testable, keyboard-resizable, and their sizes persist.

The migration follows a strangler pattern: the v6 SplitView owns the layout while the not-yet-ported legacy panels (grid, preview iframe, context widgets, filter) are hosted inside it via a new LegacyElementHost wrapper. This lets us delete the split machinery now without waiting for those panels to be rewritten.

What changed

  • New SplitView (v6/shared/ui/split-view): compound Root/Panel/Handle on react-resizable-panels 4.x, with pixel-based sizing, controlled collapse, an iframe drag shield, opt-in layout persistence, and the ARIA window-splitter pattern (first keyboard-resizable splitter in the app).
  • LegacyElementHost (v6/shared/ui): hosts a legacy lib-admin-ui Element inside the Preact tree, firing its onAdded lifecycle while leaving ownership with the legacy shell.
  • Browse (v6/pages/browse/layout/BrowseLayout): grid, preview and context laid out on SplitView; the filter is nested inside the grid region; docked/floating/mobile context modes driven by a new contextPanelMode store; $isContextOpen is now the single source of truth (the bidirectional legacy sync is gone).
  • Wizard (v6/pages/wizard/ui/layout/WizardLayout): form, live-edit and context on SplitView; view modes (form/split/live) and minimize are collapse state, so the live-edit iframe is never remounted.
  • Settings (v6/pages/settings/ui/layout/SettingsLayout): tree and item panels on SplitView.
  • Removed the legacy ContextSplitPanel/ContentWizardContextSplitPanel hierarchy, NonMobileContextPanelToggleButton, and the associated split-panel styles and layout tokens. No product code in this repo constructs a legacy SplitPanel anymore (the lib-admin-ui SplitPanel itself is a separate downstream removal, still used by settings' base class and other apps).

Notable decisions

  • Built on react-resizable-panels rather than reimplementing drag/keyboard/collapse; verified under Preact via preact/compat.
  • Single layout tree per screen: mobile and view-mode changes collapse panels instead of swapping subtrees, so preview and live-edit iframes survive resizes without reloading.
  • Splitter positions persist per screen (autoSaveId); the filter panel is resizable (was fixed-width); the forced 46/38 wizard form resize on context toggle was dropped in favor of proportional resizing.

Behavior parity notes

Several browse geometry values had to match legacy exactly, verified against a running instance: the initial context auto-open is measured against the full window width (not the sidebar-reduced layout area), the docked context panel keeps its ~360px minimum width, and the filter is nested in the grid region so opening it shrinks the grid rather than the preview. Getting these wrong is what kept the Selenium suites red; all ten suites now pass.

Closes #11007
Closes #11008
Closes #11009
Closes #11010
Closes #11011
Closes #11012
Closes #11024

Part of the epic #11013.

Drafted with AI assistance

@ashklianko ashklianko marked this pull request as draft July 3, 2026 10:08
@ashklianko ashklianko changed the title Issue 11013 Replace legacy SplitPanel with v6 SplitView #11013 Jul 5, 2026
ashklianko and others added 12 commits July 7, 2026 17:17
…eact/compat gate #11007

Added pinned `react-resizable-panels@4.12.0` to `modules/lib` dependencies.
Inlined the package and aliased it to its ESM build in `vitest.config.mjs`,
since the CJS entry requires real react past the preact/compat alias.
Verified compatibility under preact/compat; gate result recorded in #11007.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Added `SplitView` compound component (`Root`/`Panel`/`Handle`) in
`v6/shared/ui/split-view/` on top of react-resizable-panels 4.x.
Implemented controlled `collapsed` with change detection via group layout
events, iframe drag shield replacing the legacy `DragMask`, cva handle
variants (5px/1px), and opt-in layout persistence via `storageId`.
Added `field.splitView.resize` phrase for the default handle label.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Added `BrowseLayout` (v6, pages/browse) composing grid, preview, and context
regions on `SplitView`, hosting legacy panels via new `LegacyElementHost`.
Added `contextPanelMode.store` replicating the docked/floating/mobile machine
and `browseLayout.store` for the mobile preview overlay.
Rewired `ResponsiveBrowsePanel` to mount `BrowseLayoutElement` instead of
building `gridAndItemsSplitPanel`/`ContextSplitPanel`; removed the
`$isContextOpen` sync block and the legacy context toggle button in browse.
Routed `InspectEvent` to stores; kept `ResponsiveManager.fireResizeEvent`
bridging on panel resizes; trimmed dead mobile split styles.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…layout #11010

Dissolved the legacy filter/grid SplitPanel: the filter is now a resizable
SplitView panel (min 300px, pixel-preserving) rendered by BrowseLayout,
with a fullscreen overlay in mobile mode.
Made `$isContentFilterOpen` the single source of truth; rerouted legacy
show/hide/toggle methods and filter panel buttons to the store and removed
the sync override in `ContentBrowsePanel`.
Replaced legacy CSS overrides with Tailwind `LEGACY_PANEL_OVERRIDES` on the
layout root (`!` needed against unlayered legacy styles) and fixed the
filter panel width to follow its host.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…11011

Added `WizardLayout` (v6) replacing the wizard's nested legacy SplitPanels:
form and live-edit are collapsible SplitView panels (view modes collapse
instead of unmounting, keeping the live-edit iframe alive), the context
panel docks, floats, or stacks per wizard thresholds in
`wizardLayout.store` (`$wizardViewMode`, `$wizardContextPanelMode`).
Extracted the resizable `FloatingContextPanel` shared by browse and wizard;
lowered overlay z-indexes to the legacy scale so dialogs and the home app
cover the panel.
Rewired `ShowFormAction`/`ShowLiveEditAction` and `toggleMinimize` to
stores, removed the forced 46/38 form resize, deleted
`ContentWizardContextSplitPanel`, and re-scoped the minimized-rail styles.
Restored measurement-driven auto-open of the context panel on wide screens
in both browse and wizard.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
#11012

Deleted `ContextSplitPanel`, `ContextPanelState`, and
`NonMobileContextPanelToggleButton` together with their styles
(`context-split-panel.less`, toolbar and theme rules, splitter and
ghost-dragger blocks, `minimize-edit` remnants, mobile parking hacks).
Removed the `LiveFormPanel` context no-op methods and the
`splitterThickness`/`animationDelayMs` layout tokens.
Guarded layout metrics against zero-width reports so a hidden app
(e.g. Archive in CS+) cannot clobber the shared context panel mode.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Added `SettingsLayout` (v6) hosting the legacy settings tree and item
panels on `SplitView`, with the legacy hide/restore hysteresis for the
item panel on narrow screens.
Rewired `SettingsBrowsePanel` to mount it, bypassing the lib-admin-ui
`BrowsePanel` split construction.
With the last legacy `SplitPanel` consumer gone, dropped the `.splitter`
and `.ghost-dragger` shims from `tailwind.css` and re-scoped the toolbar
theming off the dead `.split-panel` ancestor.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Restructured BrowseLayout and WizardLayout into a single tree: mobile is
collapse state of the always-mounted panels, so preview and live-edit
iframes survive mode changes; mobile overlays remain only for iframe-free
content. Unified the wizard mobile definition on `$wizardContextPanelMode`
and made the 'live' view mode reachable; a minimized form now keeps its
rail with the live edit visible on narrow screens.
Wired `onCollapsedChange` so drag-collapse syncs `$isContentFormExpanded`;
raised the wizard form minimum width to 360px.
Guarded the InspectEvent open path against double widget refresh; made
drag-shield listeners pointer-scoped and self-removing; coalesced sticky
toolbar updates per frame; gave the browse layout a `storageId` override
seam for CS+; restored the legacy settings item panel threshold and the
settings wizard toolbar border; removed the reader-less
`$isPreviewPanelVisible` store.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The docked browse context panel auto-opens on wide screens only when the
initial-collapse threshold (1920px) is not tripped. That threshold was
compared against the layout area width, which excludes the ~60px sidebar,
so at a 1970px window the 1910px layout area fell at/below 1920 and the
panel stayed closed — regressing the legacy behavior (which measured the
full Body width) and leaving the context widgets absent for browse specs
that read them after selecting content. Measure the threshold against the
full window width, matching legacy; the docked/floating mode still uses the
layout area. Verified locally: the panel auto-opens at 1970px and its widget
selector is present after selection.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The flat grid|preview|context SplitView applied the 25% docked width to the
whole layout, so the context panel took ~25% (477px at 1970) and squeezed
the preview to ~25% — half the legacy ~595px. The narrow preview overflowed
its toolbar (emulator dropdown, widget selector), failing preview specs.
Legacy sized the context panel to ~360px (its min), leaving the preview
wide. Default the docked context to its min width with preserve-pixel-size
so the preview keeps the legacy ~595px. Verified locally: context 360px,
preview 594px at a 1970 window.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The flat grid|preview|context split made the filter panel a top-level
sibling, so opening it (filter 300px + grid 50% + context 360px) squeezed
the preview to its 300px minimum. The narrow preview collapsed its toolbar
into the sm:hidden container state, hiding the widget selector that browse
preview specs read via findAndSelectItem (which opens the filter).
Nest filter and grid in a left-region sub-split so the filter shrinks the
grid, leaving the preview at its legacy width. Preview and context stay
outer siblings, so the preview iframe still survives mode changes.
Verified locally: with the filter open, preview stays 594px and the widget
selector renders at 120px outside sm:hidden.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Cleanup, no behavior change (verified locally): extracted the inline panel
size literals into named constants (grid region/default, panel min width,
filter width, minimized rail, form default), aligned the grid minimum
(was 280px vs 300px elsewhere) to a single PANEL_MIN_WIDTH, and dropped the
stale 25% fallback for the context width estimate — the docked panel renders
at its min width, so the estimate uses that. Made the context mode store
pure by passing the window width through its metrics instead of reading
window.innerWidth inside the selector.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@ashklianko ashklianko marked this pull request as ready for review July 7, 2026 15:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment