Skip to content

Geptyro/panels-layout

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 

Repository files navigation

panels-layout

Tab/split panel layout for Svelte 5. Drag-drop tabs between groups, split a group horizontally or vertically, stack mode for grids of small panels, and persist the entire layout tree to localStorage.

Host application provides the panel components; the library handles the layout state, tab rendering, drag-drop, and splitter resizing.

Features

  • Tab groups with reorderable, draggable tabs.
  • Split groups (row / column) with resizable splitters.
  • Stack mode — auto-grid of all tabs in a group, useful for dashboards.
  • Drag a tab to the edge of any group → splits in that direction.
  • Layout tree serialised + restored from localStorage on load.
  • Svelte 5 native (uses $state / $effect runes).
  • ~950 LOC, no runtime dependencies beyond Svelte itself.

Install

# As a git dependency
npm install github:Geptyro/panels-layout

# Or from a monorepo
"panels-layout": "file:../packages/panels-layout"

Requires svelte ^5.0.0 as a peer dependency.

Usage

1. Define your panels

// panels.js
import HomePanel       from '$lib/panels/HomePanel.svelte';
import SettingsPanel   from '$lib/panels/SettingsPanel.svelte';
import LogsPanel       from '$lib/panels/LogsPanel.svelte';

export const panelList = [
  { type: 'home',     label: 'Home' },
  { type: 'settings', label: 'Settings' },
  { type: 'logs',     label: 'Logs' },
];

export const components = {
  home:     HomePanel,
  settings: SettingsPanel,
  logs:     LogsPanel,
};

2. Configure once at app startup

// app.js
import { configureLayout, nodeFactories } from 'panels-layout';
import { panelList, components } from './panels.js';

const { makeGroup, makeSplit } = nodeFactories;

configureLayout({
  panelList,
  components,
  storageKey: 'my-app-layout-v1',
  buildDefault: () => makeSplit('row', 0.3,
    makeGroup(['home']),
    makeGroup(['settings', 'logs']),
  ),
});

3. Render the layout tree

<!-- App.svelte -->
<script>
  import { LayoutNode, layout } from 'panels-layout';
</script>

<div class="app">
  {#if $layout}
    <LayoutNode node={$layout} />
  {/if}
</div>

<style>
  .app { width: 100vw; height: 100vh; }
</style>

That's it. Drag a tab between groups, or to the edge of one to split, or use setGroupMode(groupId, 'stack') for the grid view.

API

import {
  // Components
  LayoutNode, Splitter, TabGroup,

  // Setup
  configureLayout,
  nodeFactories,           // { makeTab, makeGroup, makeSplit }

  // Stores (Svelte writable)
  layout,                  // tree of split/tabs nodes
  activeGroupId,           // currently focused group
  dragState, dropTarget,   // drag-drop state (rarely read directly)

  // Actions
  setActiveGroup, setActiveTab, setGroupMode,
  addTab, closeTab, moveTab,
  setRatio, splitGroup, splitAtAncestor, splitAndMoveTab,
  openOrFocus,
  resetLayout,

  // Helpers
  getPanelList, getPanelComponent, getAllGroups,
} from 'panels-layout';

configureLayout({...}) must be called once before any other API:

Field Type Purpose
panelList [{type, label}] Catalog of available panels
components {type: SvelteComponent} Panel-type → component map
storageKey string localStorage key for persistence
buildDefault () => node Returns the initial tree if nothing's persisted

Layout-tree shape

The layout is a recursive tree of two node kinds:

// Tab group — leaf in the tree.
{
  type: 'tabs',
  id: 'g…',
  mode: 'tabs' | 'stack',
  tabs: [{ id, panelType, title, state? }, ],
  active: 't…',
}

// Split — internal node, exactly two children.
{
  type: 'split',
  id: 's…',
  dir: 'row' | 'column',
  ratio: 0.5,                // 0..1, child A's share
  a: <node>,
  b: <node>,
}

buildDefault() returns one of these, optionally nested. Use nodeFactories helpers (makeGroup, makeSplit, makeTab) instead of building object literals — they generate the IDs and apply the right defaults.

Caveats

  • Single layout instance per app. configureLayout is global — internally a singleton. Multiple independent layouts in one app aren't supported (yet).
  • No SSR. localStorage access happens at module init; mount in a client component / browser-only entry.
  • No keyboard shortcuts. Mouse-only for now.

Development

# In a monorepo, this package has no build step — Svelte components are
# imported directly from src/. Hosting apps consume via the `svelte` field
# in package.json.

License

MIT — see LICENSE.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors