Splitter New

Un ensemble de panneaux redimensionnables séparés par des poignées glissables.

Utilisation

Utilisez le composant Splitter pour afficher une liste de panneaux redimensionnables séparés par des poignées glissables.

Left
Main
Right
<script setup lang="ts">
import type { SplitterItem } from '@nuxt/ui'

const card = 'bg-elevated/50 border border-default rounded-xl items-center justify-center text-muted font-medium'

const items: SplitterItem[] = [
  { slot: 'left', minSize: 15, defaultSize: 25, class: card },
  { slot: 'main', minSize: 30, defaultSize: 50, class: card },
  { slot: 'right', minSize: 15, defaultSize: 25, class: card }
]
</script>

<template>
  <div class="w-full h-96">
    <USplitter id="splitter-example" :items="items">
      <template #left>
        Left
      </template>

      <template #main>
        Main
      </template>

      <template #right>
        Right
      </template>
    </USplitter>
  </div>
</template>
Le Splitter remplit la hauteur de son conteneur, alors assurez-vous qu 'un élément parent en définit un.

Éléments

Utilisez le prop items comme un tableau d'objets avec les propriétés suivantes:

Xph007xxxdefaultSize?: numberxxxxph000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

  • xminSize?: numberx{lang="ts-type"}
  • xmaxSize?: numberxx{lang="ts-type"}
  • xcollapsible?: booleanxx{lang="ts-type"}
  • xcollapsedSize?: numberxx{lang="ts-type"}
  • xsizeUnit?: '%' | 'px'xx{lang="ts-type"}
  • xorder?: numberx{lang="ts-type"}
  • xid?: stringx{lang="ts-type"}
  • xslot?: stringxx{lang="ts-type"}
  • xclass?: anyx{lang="ts-type"}
  • xui?: { panel?: ClassNameValue }xx{lang="ts-type"}

Utilisez la touche slot pour remplir le contenu d'un panneau et la touche class pour le styliser. Les éléments sans touche slot retombent dans un emplacement panel-{index}. Les tailles sont des pourcentages par défaut, définissez sizeUnit: 'px' sur un élément pour les valeurs de pixels.

Lors du rendu sur le serveur, définissez le prop id et donnez defaultSize à tous les éléments ou à aucun. Les identifiants sont générés automatiquement sinon et le serveur et le client peuvent être en désaccord, ce qui rompt la disposition sur l'hydratation. Un élément sans defaultSize retombe à une part égale sur le serveur, donc mélanger les deux fait sauter les panneaux une fois hydratés. Les tailles de pixels sont mesurées sur le client et changent toujours un peu.
sidebar
principaux
<script setup lang="ts">
import type { SplitterItem } from '@nuxt/ui'

const items = ref<SplitterItem[]>([
  {
    slot: 'sidebar',
    minSize: 15,
    maxSize: 40,
    defaultSize: 25,
    class:
      'bg-elevated/50 border border-default rounded-xl items-center justify-center text-muted font-medium'
  },
  {
    slot: 'main',
    defaultSize: 75,
    class:
      'bg-elevated/50 border border-default rounded-xl items-center justify-center text-muted font-medium'
  }
])
</script>

<template>
  <USplitter id="splitter-items" :items="items">
    <template #sidebar> Sidebar </template>

    <template #main> Main </template>
  </USplitter>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { SplitterItem } from '@nuxt/ui'

const items = ref<SplitterItem[]>([
  {
    slot: 'sidebar',
    minSize: 15,
    maxSize: 40,
    defaultSize: 25,
    class:
      'bg-elevated/50 border border-default rounded-xl items-center justify-center text-muted font-medium'
  },
  {
    slot: 'main',
    defaultSize: 75,
    class:
      'bg-elevated/50 border border-default rounded-xl items-center justify-center text-muted font-medium'
  }
])
</script>

<template>
  <USplitter id="splitter-items" :items="items">
    <template #sidebar> Sidebar </template>

    <template #main> Main </template>
  </USplitter>
</template>

Définition

Utilisez la prop orientation pour changer la direction du séparateur. Par défaut, horizontal.

First one
Deuxième
<script setup lang="ts">
import type { SplitterItem } from '@nuxt/ui'

const items = ref<SplitterItem[]>([
  {
    slot: 'first',
    class:
      'bg-elevated/50 border border-default rounded-xl items-center justify-center text-muted font-medium'
  },
  {
    slot: 'second',
    class:
      'bg-elevated/50 border border-default rounded-xl items-center justify-center text-muted font-medium'
  }
])
</script>

<template>
  <USplitter id="splitter-orientation" orientation="vertical" :items="items">
    <template #first> First </template>

    <template #second> Second </template>
  </USplitter>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { SplitterItem } from '@nuxt/ui'

const items = ref<SplitterItem[]>([
  {
    slot: 'first',
    class:
      'bg-elevated/50 border border-default rounded-xl items-center justify-center text-muted font-medium'
  },
  {
    slot: 'second',
    class:
      'bg-elevated/50 border border-default rounded-xl items-center justify-center text-muted font-medium'
  }
])
</script>

<template>
  <USplitter id="splitter-orientation" orientation="vertical" :items="items">
    <template #first> First </template>

    <template #second> Second </template>
  </USplitter>
</template>

Exemples

Avec panneau pliable

Définissez collapsible: true sur un élément pour le laisser s'effondrer au-delà de son minSize, et utilisez collapsedSize pour garder une partie du panneau visible lorsqu 'il est effondré. L'emplacement du panneau expose collapsed, collapse et expand afin que vous puissiez le contrôler par programmation, et les événements collapse, expand et resize se déclenchent avec l'index du panneau.

Main
<script setup lang="ts">
import type { SplitterItem } from '@nuxt/ui'

const items: SplitterItem[] = [
  { slot: 'sidebar', sizeUnit: 'px', minSize: 150, defaultSize: 250, collapsible: true, collapsedSize: 48, class: 'bg-elevated/50 border border-default rounded-xl' },
  { slot: 'main', class: 'bg-elevated/50 border border-default rounded-xl items-center justify-center text-muted font-medium' }
]
</script>

<template>
  <div class="w-full h-96">
    <USplitter id="splitter-collapsible-example" :items="items">
      <template #sidebar="{ collapsed, collapse, expand }">
        <div class="flex-1 flex items-center justify-center p-2">
          <UButton
            :icon="collapsed ? 'i-lucide-panel-left-open' : 'i-lucide-panel-left-close'"
            :label="collapsed ? undefined : 'Collapse'"
            :aria-label="collapsed ? 'Expand' : undefined"
            color="neutral"
            variant="subtle"
            @click="collapsed ? expand() : collapse()"
          />
        </div>
      </template>

      <template #main>
        Main
      </template>
    </USplitter>
  </div>
</template>

Avec splitters imbriqués

Installer un Splitter à l'intérieur d'un panneau pour créer des mises en page bidimensionnelles de style IDE.

Left
Top
Bottom
<script setup lang="ts">
import type { SplitterItem } from '@nuxt/ui'

const card = 'bg-elevated/50 border border-default rounded-xl items-center justify-center text-muted font-medium'

const items: SplitterItem[] = [
  { slot: 'left', minSize: 20, class: card },
  { slot: 'right', minSize: 20 }
]

const nested: SplitterItem[] = [
  { slot: 'top', minSize: 20, class: card },
  { slot: 'bottom', minSize: 20, class: card }
]
</script>

<template>
  <div class="w-full h-96">
    <USplitter id="splitter-nested-example" :items="items">
      <template #left>
        Left
      </template>

      <template #right>
        <USplitter id="splitter-nested-example-inner" orientation="vertical" :items="nested">
          <template #top>
            Top
          </template>

          <template #bottom>
            Bottom
          </template>
        </USplitter>
      </template>
    </USplitter>
  </div>
</template>

Avec poignée personnalisée

Utilisez le prop ui pour le restyler, par exemple en tant que diviseur visible pour les mises en page affleurantes, et le slot resize-handle pour rendre le contenu à l'intérieur comme une poignée.

Left
Right
<script setup lang="ts">
import type { SplitterItem } from '@nuxt/ui'

const items: SplitterItem[] = [
  { slot: 'left', minSize: 20, defaultSize: 30, class: 'items-center justify-center text-muted font-medium' },
  { slot: 'right', defaultSize: 70, class: 'items-center justify-center text-muted font-medium' }
]
</script>

<template>
  <div class="w-full h-96">
    <USplitter id="splitter-custom-handle-example" :items="items" :ui="{ handle: 'data-[orientation=horizontal]:w-px data-[orientation=vertical]:h-px bg-border transition-colors data-[state=hover]:bg-primary data-[state=drag]:bg-primary' }" class="rounded-lg border border-default overflow-hidden">
      <template #left>
        Left
      </template>

      <template #right>
        Right
      </template>
    </USplitter>
  </div>
</template>

avec persistance

Fournissez un auto-save-id pour conserver la disposition sur localStorage et la restaurer lors du rechargement.

<template>
  <USplitter id="my-layout" auto-save-id="my-layout" :items="items">
    <!-- ... -->
  </USplitter>
</template>

API

Props équipements

Prop Default Type
as
'div'
any

The element or component this component should render as.

id
string

A unique id for the group, also used to derive the ids of its panels and handles. Set it when rendering on the server, auto-generated ids can differ between the server and the client and break resizing on hydration.

orientation
'horizontal'
"horizontal" | "vertical"

The orientation of the splitter.

items
T[]
disabled
false
boolean

Whether the resize handles are disabled, locking the current layout.

autoSaveId
null | string

Unique id used to auto-save group arrangement via localStorage.

keyboardResizeBy
null | number

Step size when arrow key was pressed.

storage
PanelGroupStorage

Custom storage API; defaults to localStorage

hitAreaMargins
PointerHitAreaMargins

Allow this much margin when determining resizable handle hit detection

ui
{ root?: SlotClass; panel?: SlotClass; handle?: SlotClass; }

Slots

Slot Type
resize-handle
{ index: number; ui: object; }

Emits

Event Type
layout
[val: number[]]
collapse
[index: number]
expand
[index: number]
resize
[index: number, size: number, prevSize?: number | undefined]
dragging
[index: number, dragging: boolean]

Thème

app.config.ts
export default defineAppConfig({
  ui: {
    splitter: {
      slots: {
        root: '',
        panel: 'flex',
        handle: 'group relative shrink-0 focus-visible:outline-2 focus-visible:outline-primary data-[panel-resize-handle-enabled=false]:cursor-default'
      },
      variants: {
        orientation: {
          horizontal: {
            handle: 'w-2 cursor-col-resize'
          },
          vertical: {
            handle: 'h-2 cursor-row-resize'
          }
        }
      }
    }
  }
})
vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'

export default defineConfig({
  plugins: [
    vue(),
    ui({
      ui: {
        splitter: {
          slots: {
            root: '',
            panel: 'flex',
            handle: 'group relative shrink-0 focus-visible:outline-2 focus-visible:outline-primary data-[panel-resize-handle-enabled=false]:cursor-default'
          },
          variants: {
            orientation: {
              horizontal: {
                handle: 'w-2 cursor-col-resize'
              },
              vertical: {
                handle: 'h-2 cursor-row-resize'
              }
            }
          }
        }
      }
    })
  ]
})

Changelog

No recent changes