Aller au contenu

Redimensionnable

Des volets divisibles et déplaçables pour les éditeurs de code, les arborescences de fichiers et les mises en page de tableaux de bord — avec un style néobrutaliste caractérisé par des bordures et des ombres.

import {
  ResizableHandle,
  ResizablePanel,

Le composant resizable scinde une zone en panneaux dont l’utilisateur ajuste les proportions en les faisant glisser — à l’horizontale ou à la verticale, imbricables, avec des poignées pilotables au clavier. Les deux variantes encapsulent react-resizable-panels de bvaughn, stylisé selon la recette néobrutaliste : bordures épaisses, ombres franches et typographie affirmée.

À privilégier dans ces cas :

  • Éditeurs de code et interfaces façon IDE — arborescence de fichiers, éditeur et volet d’aperçu que chacun dimensionne à sa convenance.
  • Vues maître–détail — boîte de réception et fil de discussion, table et inspecteur d’enregistrement, avec le séparateur là où l’utilisateur le veut.
  • Tableaux de bord et consoles — laissez un panneau de journaux ou un terminal prendre de la hauteur sur la vue principale, sans fenêtre modale.

À propos

Le composant Resizable s'appuie sur react-resizable-panels de bvaughn.

Installation

pnpm dlx shadcn@latest add https://neobrutalism.com/r/base/resizable.json
npx shadcn@latest add https://neobrutalism.com/r/base/resizable.json
yarn dlx shadcn@latest add https://neobrutalism.com/r/base/resizable.json
bunx --bun shadcn@latest add https://neobrutalism.com/r/base/resizable.json

Installez les dépendances suivantes :

pnpm add react-resizable-panels
npm install react-resizable-panels
yarn add react-resizable-panels
bun add react-resizable-panels

Copiez et collez le code suivant dans votre projet.

components/ui/resizable.tsx
"use client"

import * as ResizablePrimitive from "react-resizable-panels"

import { cn } from "@/lib/utils"

function ResizablePanelGroup({
  className,
  ...props
}: ResizablePrimitive.GroupProps) {
  return (
    <ResizablePrimitive.Group
      data-slot="resizable-panel-group"
      className={cn(
        "flex h-full w-full aria-[orientation=vertical]:flex-col",
        className
      )}
      {...props}
    />
  )
}

function ResizablePanel({ ...props }: ResizablePrimitive.PanelProps) {
  return <ResizablePrimitive.Panel data-slot="resizable-panel" {...props} />
}

function ResizableHandle({
  withHandle,
  className,
  ...props
}: ResizablePrimitive.SeparatorProps & {
  withHandle?: boolean
}) {
  return (
    <ResizablePrimitive.Separator
      data-slot="resizable-handle"
      className={cn(
        "relative flex w-px items-center justify-center bg-border after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary aria-[orientation=horizontal]:h-px aria-[orientation=horizontal]:w-full aria-[orientation=horizontal]:after:left-0 aria-[orientation=horizontal]:after:h-1 aria-[orientation=horizontal]:after:w-full aria-[orientation=horizontal]:after:translate-x-0 aria-[orientation=horizontal]:after:-translate-y-1/2 [&[aria-orientation=horizontal]>div]:rotate-90",
        className
      )}
      {...props}
    >
      {withHandle && (
        <div className="z-10 flex h-6 w-1 shrink-0 rounded-lg bg-border" />
      )}
    </ResizablePrimitive.Separator>
  )
}

export { ResizableHandle, ResizablePanel, ResizablePanelGroup }

Adaptez les chemins d'import à la structure de votre projet.

Utilisation

import {
  ResizableHandle,
  ResizablePanel,
  ResizablePanelGroup,
} from "@/components/ui/resizable"
<ResizablePanelGroup orientation="horizontal">
  <ResizablePanel>One</ResizablePanel>
  <ResizableHandle />
  <ResizablePanel>Two</ResizablePanel>
</ResizablePanelGroup>

Composition

Utilisez la composition suivante pour construire un ResizablePanelGroup :

ResizablePanelGroup
├── ResizablePanel
├── ResizableHandle
└── ResizablePanel

Exemples

Vertical

Utilisez orientation="vertical" pour un redimensionnement vertical.

import {
  ResizableHandle,
  ResizablePanel,

Poignée

Utilisez la prop withHandle sur ResizableHandle pour afficher une poignée visible.

import {
  ResizableHandle,
  ResizablePanel,

RTL

Pour activer le RTL dans Neobrutalism, consultez le guide de configuration RTL.

"use client"

import * as React from "react"

Accessibilité

La poignée suit le modèle WAI-ARIA Window Splitter : react-resizable-panels rend chaque poignée comme un role="separator" focalisable, avec aria-valuenow / aria-valuemin / aria-valuemax et aria-controls pointant vers le panneau qu’elle redimensionne, si bien que les lecteurs d’écran annoncent la position du partage à mesure qu’elle change.

Interactions au clavier :

ToucheAction
Tab / Shift + TabAmène le focus sur une poignée de redimensionnement / l’en éloigne
ArrowLeft / ArrowRightRedimensionne un groupe horizontal d’un cran
ArrowUp / ArrowDownRedimensionne un groupe vertical d’un cran
HomeRéduit le panneau situé avant la poignée à sa taille minimale
EndAgrandit le panneau situé avant la poignée à sa taille maximale
EnterReplie ou déplie un panneau repliable

Référence API

Consultez la documentation react-resizable-panels.

Changelog

2025-02-02 react-resizable-panels v4

Mise à jour vers react-resizable-panels v4. Consultez les notes de version v4.0.0 pour tous les détails.

Si vous utilisez directement les primitives react-resizable-panels, notez les changements suivants :

v3v4
PanelGroupGroup
PanelResizeHandleSeparator
direction proporientation prop
defaultSize={50}defaultSize="50%"
onLayoutonLayoutChange
ImperativePanelHandlePanelImperativeHandle
ref prop on PanelpanelRef prop
data-panel-group-directionaria-orientation