Replace legacy SplitPanel with v6 SplitView #11013#11016
Open
ashklianko wants to merge 12 commits into
Open
Conversation
This was
linked to
issues
Jul 3, 2026
…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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Replaces the legacy lib-admin-ui
SplitPanel— the imperative, class-based split panel that structured the whole browse and wizard UI — with a modern PreactSplitViewcomponent built onreact-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
SplitViewowns the layout while the not-yet-ported legacy panels (grid, preview iframe, context widgets, filter) are hosted inside it via a newLegacyElementHostwrapper. This lets us delete the split machinery now without waiting for those panels to be rewritten.What changed
SplitView(v6/shared/ui/split-view): compoundRoot/Panel/Handleon 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-uiElementinside the Preact tree, firing itsonAddedlifecycle while leaving ownership with the legacy shell.v6/pages/browse/layout/BrowseLayout): grid, preview and context laid out onSplitView; the filter is nested inside the grid region; docked/floating/mobile context modes driven by a newcontextPanelModestore;$isContextOpenis now the single source of truth (the bidirectional legacy sync is gone).v6/pages/wizard/ui/layout/WizardLayout): form, live-edit and context onSplitView; view modes (form/split/live) and minimize are collapse state, so the live-edit iframe is never remounted.v6/pages/settings/ui/layout/SettingsLayout): tree and item panels onSplitView.ContextSplitPanel/ContentWizardContextSplitPanelhierarchy,NonMobileContextPanelToggleButton, and the associated split-panel styles and layout tokens. No product code in this repo constructs a legacySplitPanelanymore (the lib-admin-uiSplitPanelitself is a separate downstream removal, still used by settings' base class and other apps).Notable decisions
react-resizable-panelsrather than reimplementing drag/keyboard/collapse; verified under Preact via preact/compat.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