Known limitations
What is not there yet, the workaround the examples use, and the Epic that removes it.
Dockable is young. This page lists, honestly, what the public surface is missing today. The numbered gaps come from the project's walking-skeleton report (section 2), with the same numbers, so issues and example comments can refer to them ("gap 10"). Each has the workaround the examples use and the Epic that will remove it, when one is scheduled.
The gaps
| # | missing | workaround (what the examples do) | removed by |
|---|---|---|---|
| 1 | Tab knows selected but not whether its tabset is active, so "the selected tab of the active tabset" has no attribute. | An ancestor selector: .tabset[data-active] .tab[data-selected], or Tailwind's in-data-active: / group-data-active: on a child of the tab. | not scheduled yet |
| 2 | Panels live outside their tabset (in the panel layer), so a tabset's border-radius/overflow cannot clip them, and a panel cannot be styled by its tabset's state. | Rounded tabsets repeat the tabset's inner radius on the panel's bottom corners (it breaks if the strip moves to the bottom), or use square tabsets. The active tabset is marked on its selected tab instead of with a tabset-wide ring. | not scheduled yet |
| 3 | popoutMirrorRoot on Root mirrors <html> and <body> attributes. See Popouts. | None needed. | Epic #20 (done) |
| 4 | dir at runtime leaves the panels at their old positions.dir attributes and repositions the panels. See Right-to-left layouts. | None needed. | Epic #112 (done) |
| 5 | start on the right. See Right-to-left layouts. | None needed. | Epic #112 (done) |
| 6 | Styling every splitter the same way needs renderSplitter on every Row (root, nested, and inside Popout). | Pass renderSplitter to every Row (the examples write one Splitter component and pass it in their recursion), or style [role="separator"] from a stylesheet. | not scheduled yet |
| 7 | Dockable.PopoutTrigger. See Popouts. | None needed. | Epic #20 (done) |
| 8 | data-drop-target and data-drop-location on TabSet, data-drop-target and data-drop-index on TabList. See Drop zones and drop feedback. | None needed. | Epic #19 (done) |
| 9 | No way to make a splitter's hit area wider than its visible thickness. | A 1px splitter with a centred ::after grab area, relative z-10; the engine measures the 1px element, so the split maths is exact. See Splitters. | not scheduled yet |
| 10 | None needed. | Epic #112 (done) | |
| 11 | defaults.layout.edgeDockMargin (lower it for a thin strip), and Dockable.EdgeIndicator shows the bands during a drag. See Borders. | Keep strips about 30px tall, or set defaults.layout.edgeDockMargin. | Epic #21 (done) |
| 12 | The drop indicator paints under the panels: panels are portalled into the root after its other children. | A z-index on the indicator (z-10). | not scheduled yet |
Features not built yet
| feature | status | removed by |
|---|---|---|
| Floats (in-page floating windows) | not built: a tab leaves the main layout only into a popout window | not scheduled yet |
| Tab groups (pills grouping tabs in a strip) | not built: a tabset or border holds tabs only | not scheduled yet |
| Border extras: a pop out button in a border's strip | not rendered; a border's strip is yours to fill (Dockable.PopoutTrigger works in tabsets only) | not scheduled yet |
| Fill and fixed sizes (a tabset that takes the remaining space, a size in pixels) | not built: rows share their space by weight, within each node's minWidth/maxWidth/minHeight/maxHeight | not scheduled yet |
| Cross-window tab drag (a tab dragged to another browser tab or window of the app) | not built: a drag crosses the layouts and popouts of one page, not separate pages | not scheduled yet |
| Several open tabs per border | not built: a border shows one selected tab at a time | not scheduled yet |
A popout document hook for libraries that portal into the global document | not built: give such a library the content's ownerDocument yourself (Popouts) | not scheduled yet |
Inline rename on F2 | the renameTab binding exists in the key map but the primitives do not act on it; build the rename UI, which asks model.can("tab.rename", …) and runs tab.rename with the new label (Tabs) | not scheduled yet |
| Adapters for Angular and Vue | the core is framework-agnostic so they can follow | not scheduled yet |
RTL
Right-to-left layouts render and respond with start on the right: see
Right-to-left layouts. A direction set by CSS alone (no dir attribute) is
the one change the engine does not see: run engine.run("measure-and-position") after it.
Popout edge cases
- Closing a popout runs
window.close, which docks its tabs back into the main layout. A middleware that vetoes it keeps the window layout in the state with its window gone: the engine tries to open it again, and if the browser blocks that, its tabs are unreachable until the layout is loaded again. Do not vetowindow.close. - The browser may block a popout that is reopened without a user gesture (for example when a saved layout with windows is restored at start-up).
Other things to know
- The root needs a size (by design): an unsized root renders nothing visible. See Sizing the root.
- A bottom tab strip is a markup change (render
TabSetContentbefore theTabList), not a style:TabSetis a structural flex column.
Found while writing the examples
The examples hit one more rough edge. It is worked around (and commented) in the example named,
and listed in docs/docs-examples-gaps.md in the repository.
- An input inside a tab gets the tab's keyboard handling and dragging: stop key propagation
and set
draggable={false}while editing. Seerename-tabs.
Found something else?
Open an issue. A gap you work around in your app is exactly what the next Epics need to hear about.