Something went wrong. Try again.
Rich, web-friendly editor UI framework for SolidJS + TypeScript
Something went wrong. Try again.
Feature request: sections with their own width in floating panels #
- Status: RESOLVED (2026-11-02), with a simpler design than proposed below. No new
SectionDeffields: a section is designed for its home panel, so its width is that panel'sdefaultLeftWidth/defaultRightWidth, or the library default. A new floating panel opens at that width. The widest designed width is a hard minimum for the panel, and nothing shrinks a panel by itself, so a resize drag can only narrow one once its widest section has left. The viewer's info sections get their width from its existingdefaultRightWidthwith no code change. Seedocs/dnd.md. - Component:
layout/sectionLayout.ts,layout/StageFloatHost.tsx; docs indocs/dnd.md - Pulled by: dsts-viewer (its info sections are designed for 360px, its controls for the default panel width)
- Severity: medium. Sections end up at widths they weren't designed for, and stay there
- Date: 2026-11-01
What happens today #
Found by hand in the viewer, with sections of different widths:
- Merging: dragging one floating panel onto another makes the arrivals take the target's width, however narrow, which can squeeze them into a layout they were never designed for.
- Pulling apart: a section pulled off a floating panel stays at the width of the panel it left, even though it's now free to return to its own size.
Cause #
A section has no width of its own. SectionDef has no width field, so width exists only on the
container: a side panel's track, or FloatingPanel.w.
onDocDragEndseeds every new floating panel with the width the section measured at the moment of the drag:layout.floatOut(id, x, y, sectionEl.getBoundingClientRect().width). That avoids a jump when leaving a side panel, but can't tell a section's natural width from one it only had because of the panel it was in.dockPanelmoves the sections and prunes the source panel, with no width logic. The target keeps its width and the arrivals fill it.moveinto a floating dock behaves the same way.
Proposed API #
Additive on SectionDef. Omitting both keeps today's behavior, so existing consumers don't change.
interface SectionDef {
// ...existing
/** Width (px) this section is designed for when floating. */
readonly preferredWidth?: number;
/** Narrowest width (px) its content works at. Floors any floating panel holding it. */
readonly minWidth?: number;
}
The names are suggestions.
Proposed behavior #
Derive a floating panel's width rather than copying it around, so that merging and pulling apart need no special handling:
FloatingPanel.wmeans the user resized this panel. Only a resize drag sets it for sections with apreferredWidth.- With no
w, a panel is as wide as the widestpreferredWidthof its sections, falling back to the default CSS width when none has one. - With a
w, that width stands, even when a section with a largerpreferredWidthjoins. - Either way, the panel never goes below the largest
minWidthof its sections. This also floors the resize drag, in place ofMIN_FLOAT_WIDTHwhere it is larger.
| Action | Result |
|---|---|
| Pull a section off a side panel | Opens at its preferredWidth |
| Pull a section off a floating panel | Opens at its preferredWidth; the old panel's resize doesn't follow it |
| Merge two unresized panels | Takes the widest preferredWidth of the combined sections |
| Merge into a resized panel | Keeps its width, raised if needed to fit each minWidth |
| A wide section leaves an unresized panel | The panel narrows to fit the sections left |
A section with no preferredWidth keeps today's seeding when it leaves a side panel, so it doesn't
jump in width. See question 1 for one leaving a floating panel.
Design questions #
- Where does a section with no
preferredWidthgo when pulled off a floating panel? Today it keeps the panel's width, which is the second bug above. Recommended: treat its home dock's width as its natural width when that dock can be measured, otherwise the default CSS width. The home dock is already the placeredocksends it, so this fits the model. If that's too costly, the default CSS width alone is still better than keeping a leftover width. - Does
wstay set for resized panels after every section but one has left? Recommended: yes. The user sized that panel on purpose, and a new floating panel is what starts fresh.
Details to get right #
- Persistence:
wis already persisted, so a stored width from before this change reads as a user resize. That's harmless, and a consumer can bumpversionto discard it. A derived width shouldn't be stored at all, so a changedpreferredWidthin code takes effect on next load. - Region limits: a
minWidthorpreferredWidthwider than the float region must not push the panel off-screen.max-width: 100%already caps it, and that cap should win. - No width jump on drag: the drag image is built at the section's docked width, and the panel
lands at
preferredWidth, so a section whose two widths differ will change width on drop. That's expected, but the grab offset should still keep the panel's top-left where the ghost's was. - Reactivity: a derived width should follow membership changes without a remount, as sections arrive or leave.
Acceptance #
- Merging panels with sections of different
preferredWidths gives the widest, and pulling one back off returns each to its own width. - A panel resized by hand keeps that width through a merge, but is raised to fit a
minWidth. - A resize drag can't go below the largest
minWidthof the panel's sections. - Sections with neither field behave as they do today when leaving a side panel.
InfoIcon, field validation and everything else outside floating panels is unaffected.docs/dnd.mddocuments both fields, and tests cover the table above.
Out of scope #
- Side panel widths. These stay the consumer's call, and a section docked in a panel narrower
than its
minWidthisn't widened by this request. In the viewer that's a real case: the digivolution tree needs about 240px and the controls panel is 230px, so docking it there clips it. That's worth a separate look if it matters in practice.