Skip to content

feat: maximize and fold tabsets (core, React, Solid) - #83

Merged
akash-aman merged 5 commits into
mainfrom
feat/maximize-and-fold-core
Oct 5, 2026
Merged

akash-aman merged 5 commits into
mainfrom
feat/maximize-and-fold-core

Conversation

@akash-aman

@akash-aman akash-aman commented Oct 4, 2026 •

Copy link
Copy Markdown
Collaborator

LeetCode-style maximize and fold for tabsets, end to end: core engine, React and Solid UI. I studied the behaviour on leetcode.com, which uses FlexLayout, and tested it in agent-browser on both examples before raising this PR.

What you get (on by default)

  • Hover toolbar at the end of every tab bar: Maximize / Restore and Fold / Unfold, with accessible labels and tooltips.
  • Maximize: one tabset fills the layout. The others are hidden but stay mounted. Restore brings back the exact previous split.
  • Fold: the tabset shrinks to its tab bar. In side-by-side rows it becomes a rotated vertical strip with the toolbar at the bottom; in stacked rows it becomes a horizontal strip. Unfold restores the previous size (LeetCode resets to equal halves).
  • Double-click a tab bar to maximize or restore. ⌥ + maximizes and ⌥ − folds the last touched tabset.
  • Folding the last open panel in a row unfolds its most recently folded sibling (LeetCode swap).
  • State persists: nodFold on tabsets and nodMaxd on the root in updateJSON.

API

  • Core: maximize, restore, toggleMaximize, maximizedId, collapse, expand, toggleCollapse, the collapsedSize option, getTabbarPlacement / placeTabbar. Computed options gain nodFold, nodMaxd, nodHidden, nodLocked, nodFoldable and nodMaximizable.
  • React / Solid props: enableMaximize, enableCollapse, enableDoubleClickMaximize, keyboardShortcuts, TabsetToolbar.

Rules

  • Bonds next to a folded tabset are locked.
  • Any drag-and-drop move ends maximize, and starting a drag while maximized restores first.
  • A tabset that's dragged, or that receives a dropped tab, unfolds.
  • Buttons are hidden where they don't apply: a lone tabset can't fold, and maximize needs at least two tabsets.

Fixes included

  • A layout built from a single tab used only half the width (an empty row sibling took the rest).
  • Drops at the right end of a tab bar broke once the toolbar was added, because it was treated as the last tab. Only tab labels count now.
  • Hidden state uses a data-dx-hidden attribute instead of inline display, so custom tab bars keep their own display value (an earlier draft stretched tab labels in the React demo).

Verification

  • pnpm run build, pnpm run lint (0 warnings) and pnpm run test (3766 tests). There are 17 new core tests, including a randomized 400-step mix of moves, folds and maximizes.
  • agent-browser, React demo:
    • The toolbar is hidden until hover and looks like LeetCode's.
    • Maximize hides 4 tab bars and 4 bonds, keeps all 3 Monaco editors mounted, and restore is exact.
    • Double-click works and is ignored on the toolbar.
    • ⌥ + and ⌥ − work.
    • Fold gives a 40 px rotated strip with upright icons, a hidden body and a locked bond; unfold is exact.
    • In the stacked column, fold gives a horizontal strip, and folding both swaps them.
    • Drop into a folded panel unfolds it, and dragging while maximized restores.
    • A resize sweep keeps the strip at 40 px with the rotated bar exactly covering it.
  • agent-browser, Solid example: maximize/restore, fold/unfold, shortcuts and drop-into-folded all pass.

Release

Changesets bump all packages a minor version: @dynamix-layout/core, @dynamix-layout/react and @dynamix-layout/solid.

- New view-state module: maximize/restore/toggleMaximize and
  collapse/expand/toggleCollapse, plus normalizeViewState which keeps
  the state valid after any tree change (a lone tabset cannot stay
  folded, every row keeps one open child, a maximize whose tabset left
  the tree is cleared).
- Maximize only changes the computed output: the maximized tabset and
  its tabs get the root dims, everything else is flagged nodHidden.
  Node dims and parts are untouched, so restore is exact.
- Fold: a folded tabset's minimum along its parent row is collapsedSize
  and it takes no share of extra space; its part is preserved so unfold
  restores the previous size. Folding the last open child of a row
  unfolds the most recently folded sibling (LeetCode behaviour).
- Bonds next to a folded tabset are locked (nodLocked) and ignored by
  the slider; nothing moves while maximized. Any updateTree ends
  maximize; dragged and drop-target tabsets unfold.
- nodFold / nodMaxd round-trip through toJSON and the constructor.
- Fix: a single-tab layout no longer creates an empty row sibling that
  took half of the width.

Tests: view-state suite (maximize, exact restore, fold in both axes,
exact unfold, last-open swap, locked bonds, resize sweep, JSON round
trip, drag-and-drop interplay, randomized moves/folds/maximizes) and a
single-tab regression.
@vercel

vercel Bot commented Oct 4, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
dynamix-layout-react Ready Ready Preview Oct 5, 2026 2:58am UTC
dynamix-layout-site Ready Ready Preview Oct 5, 2026 2:58am UTC
dynamix-layout-solid Ready Ready Preview Oct 5, 2026 2:58am UTC

- DefaultTabsetToolbar (React and Solid): Maximize/Restore and
  Fold/Unfold buttons at the end of every tab bar, shown on hover (and
  always on folded strips), with aria labels and shortcut tooltips.
  Replaceable through the TabsetToolbar prop.
- Folded tabsets in side-by-side rows render their tab bar rotated 90deg
  as a vertical strip (core getTabbarPlacement/placeTabbar); icons are
  counter-rotated so they read upright. Folded bodies stay mounted but
  hidden.
- Maximized mode hides the other tab bars and bonds via a data-dx-hidden
  attribute + CSS, so components keep their own display values.
- Double-click a tab bar to maximize/restore; Alt/Option + "+" / "-"
  toggle maximize/fold for the last touched tabset.
- Drag start while maximized restores first; locked bonds ignore the
  pointer; collapsedSize follows tabHeadHeight.
- Fix: drop targeting in a tab bar only considers tab labels (the
  toolbar was treated as the last tab, breaking drops on the right).
- New props: enableMaximize, enableCollapse, enableDoubleClickMaximize,
  keyboardShortcuts, TabsetToolbar (all on by default).
@akash-aman akash-aman changed the title feat(core): maximize and fold tabsets feat: maximize and fold tabsets (core, React, Solid) Oct 4, 2026
… bars

- Dragging over a folded (rotated) strip used the rotated tab label
  boxes for the insertion marker, producing a thin vertical sliver and an
  unreliable target. Rotated tab bars (data-rotated) now highlight the
  whole strip and drop into the tabset, which unfolds it.
- In narrow tabsets the overflowing tab labels ran under the toolbar.
  The toolbar now inherits the tab bar background (labels slide under it)
  and stays pinned to the visible end while the tab bar scrolls.
@akash-aman
akash-aman merged commit 9f5d60c into main Oct 5, 2026
4 checks passed
@github-actions github-actions Bot mentioned this pull request Oct 5, 2026

This branch was successfully deployed

3 active (2 outdated) deployments
Preview – dynamix-layout-react — c7692cc7 Deployed Oct 5, 2026 by vercel[bot]
Preview – dynamix-layout-solid — 4f01ca47 Deployed Oct 4, 2026 by vercel[bot]
Preview – dynamix-layout-site — 4f01ca47 Deployed Oct 4, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant