ドラッグ可能なハンドルで区切られたサイズ変更可能なパネルのセット。

使用法

分割コンポーネントを使用して、ドラッグ可能なハンドルで区切られたサイズ変更可能なパネルのリストを表示します。

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>
Splitterはコンテナの高さを埋めますので、親要素が定義していることを確認してください。

アイテム

itemsプロパティを次のプロパティを持つオブジェクトの配列として使用します。

  • defaultSize?: number
  • minSize?: number
  • maxSize?: number
  • collapsible?: boolean
  • collapsedSize?: number
  • sizeUnit?: '%' | 'px'
  • order?: number
  • id?: string
  • slot?: string
  • class?: any
  • ui?: { panel?: ClassNameValue }

slotキーを使用してパネルの内容を入力し、classキーを使用してスタイルを設定します。slotキーがないアイテムはpanel-{index}スロットに戻ります。サイズはデフォルトでパーセンテージです。ピクセル値の項目にsizeUnit: 'px'を設定します。

サーバー上でレンダリングするとき、idプロパティを設定し、defaultSizeをすべてのアイテムまたはなしに指定します。そうでなければIDは自動的に生成され、サーバーとクライアントは一致しない可能性があり、ハイドレーション時のレイアウトが崩れます。defaultSizeがないアイテムはサーバー上で等しいシェアに戻ります。そのため、2つを混ぜると、ハイドレーション後にパネルがジャンプします。ピクセルサイズはクライアントで測定され、常に少しずれます。
サイトマップ
メイン
<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>

Orientation

orientationプロパティを使用して、スプリッタの方向を変更します。デフォルトはhorizontalです。

ファースト
セカンド
<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>

例

折りたたみパネル付き

collapsible: trueをアイテムに設定すると、minSizeを超えて折りたたまれるようになります。collapsedSizeを使用して、折りたたまれたときにパネルの一部が見えるようにします。パネルスロットはcollapsed、collapse、expandを公開しているので、プログラムで制御できます。collapse、expand、resizeイベントはパネルインデックスとともに発生します。

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>

ネストされたスプリッタ

パネル内にSplitterをネストして、2次元の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>

カスタムハンドル付き

ハンドルはデフォルトでは見えません。uiプロパティを使用してスタイルを変更します。例えば、フラッシュレイアウトの可視分割器として、resize-handleスロットを使用してハンドル内のコンテンツをグリップのようにレンダリングします。

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>

持続性

レイアウトをlocalStorageに保持し、リロード時に復元するためにauto-save-idを指定します。

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

API

Props

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; }

スロット

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]

Theme

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