React 19 · 8 ko compressé · aucune dépendance

Le châssis
sous votre outil.

Des rails d'icônes sur les bords, des zones redimensionnables autour d'un centre qui est le vôtre, et une disposition qui survit au rechargement. Sans rendu en dessous, repeignable au-dessus.

Voir les exemples
En direct. Faites glisser les gouttières entre les surfaces, cliquez une icône du rail ou déplacez-la dans une autre moitié, redimensionnez la fenêtre.

Quatre portes d’entrée

Chacun tourne dans votre navigateur et tout son code est à l’écran. Partez du plus proche.

Minimal

Le plus petit châssis qui fonctionne. Deux colonnes, une bande, un centre — et un en-tête à vous qui le pilote.

import { Panels, Panel } from '@pasquelin/panels'
import '@pasquelin/panels/styles.css'

<Panels<PanelId>>
  <Panel<PanelId> id="files" zone="left" title="Files">
    <FileTree />
  </Panel>

  <Panel<PanelId> id="notes" zone="right" opens={380}>
    <Notes />
  </Panel>

  <Panels.Center>
    <YourApp />
  </Panels.Center>
</Panels>

Conseil. Deux panneaux qui partagent une zone et une moitié prennent leur tour ; le rail bascule entre eux. Donnez-lui la moitié secondaire pour l’empiler sous le premier. Ici, les icônes du rail se déplacent aussi d’une moitié à l’autre.

Ouvrir Minimal

React Router

Le centre est une route. Naviguer change le milieu et rien d’autre — les colonnes gardent leur largeur, les panneaux ouverts le restent.

function Layout() {
  return (
    <Panels<PanelId>>
      <Panel<PanelId> id="sites" zone="left" title="Sites">
        <SiteList />
      </Panel>

      <Panels.Center>
        <Outlet />
      </Panels.Center>
    </Panels>
  )
}

Conseil. Déclarez les panneaux dans la route de mise en page, au-dessus de la route enfant. Déclarés par page, ils seraient démontés à chaque navigation et perdraient ce qu’ils tenaient.

Ouvrir React Router

Onglets de documents

Le centre porte des documents sur Dockview — des onglets qu’on déplace et qu’on partage — pendant que les panneaux restent sur les bords.

import { DockviewCenter } from '@pasquelin/panels/dockview'

<Panels.Center>
  <DockviewCenter
    documents={{ editor: Editor }}
    empty={NothingOpen}
    onReady={setApi}
  />
</Panels.Center>

Conseil. Importez-le depuis l’entrée dockview : son poids ne pèse alors que sur les projets qui veulent des onglets. Un panneau n’entre jamais au centre — un document a un nom, un panneau a une icône.

Ouvrir Onglets de documents

Repeint

Le même châssis sous quatre palettes. Couleurs, rayon, largeur du rail, hauteur d’en-tête — tout est une propriété personnalisée.

.pnl-root {
  --pnl-chassis: #2e2b26;
  --pnl-panel: #1b1916;
  --pnl-accent: #d99a2b;
  --pnl-radius: 12px;
  --pnl-rail: 60px;
}

Conseil. Posez le jeton d’accent sur n’importe quel ancêtre et le châssis prend votre identité au lieu d’imposer la sienne. Besoin d’aller plus loin ? Chaque pièce est exportée et remplaçable seule.

Ouvrir Repeint

Toute la surface

Il n’y en a pas beaucoup, et c’est le but. Six choses à savoir.

Déclarer

Un panneau est un descripteur. Où il s’accroche, comment il s’appelle, ce qu’il dessine.

<Panel id="files" zone="left" title="Files" icon={<Icon />}>
  <FileTree />
</Panel>
Piloter

Un seul hook pour tout en-tête, raccourci ou menu qui doit agir sur les panneaux.

const { reveal, close, toggle, isShown } = usePanels<PanelId>()
Réarranger

Une prop, et le lecteur fait glisser une icône de rail vers une autre moitié. L’endroit qu’il choisit est retenu avec le reste de la disposition.

<Panels<PanelId> draggablePanels>
Piloter hors de React

Construisez le store vous-même et un socket, un menu natif ou un worker ouvre un panneau.

const store = createPanelsStore<PanelId>()
socket.on('alert', () => store.getState().show('alerts'))
Repeindre

Chaque valeur est une propriété personnalisée. Posez l’accent et les rails suivent votre identité.

.pnl-root {
  --pnl-panel: #101418;
  --pnl-accent: #47965c;
  --pnl-radius: 10px;
}
Ou ne prendre que la logique

Les composants sont bâtis sur des hooks qui ne dessinent rien. Dessinez votre propre cadre dessus.

const zone = useZone('left')
useContainerFit(ref)