v2.12.0

Splitter New

Panels that share one area and are resized by dragging the divider between them.

Usage

Use the Splitter component to display a list of resizable panels separated by draggable handles.

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

const card = 'bg-(--ui-color-bg-content-secondary) border border-(--ui-color-divider-default) rounded-(--ui-border-radius-md) items-center justify-center text-description font-(--ui-font-weight-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">
    <B24Splitter id="splitter-example" :items="items">
      <template #left>
        Left
      </template>

      <template #main>
        Main
      </template>

      <template #right>
        Right
      </template>
    </B24Splitter>
  </div>
</template>
The Splitter fills the height of its container, so make sure a parent element defines one.

Items

Use the items prop as an array of objects with the following properties:

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

Use the slot key to fill the content of a panel and the class key to style it. Items without a slot key fall back to a panel-{index} slot. Sizes are percentages by default, set sizeUnit: 'px' on an item for pixel values.

When rendering on the server, set the id prop and give defaultSize to all items or to none. Ids are generated automatically otherwise and the server and the client can disagree, which breaks the layout on hydration. An item without a defaultSize falls back to an equal share on the server, so mixing the two makes panels jump once hydrated. Pixel sizes are measured on the client and always shift a little.
Sidebar
Main
<script setup lang="ts">
import type { SplitterItem } from '@bitrix24/b24ui-nuxt'

const items = ref<SplitterItem[]>([
  {
    slot: 'sidebar',
    minSize: 15,
    maxSize: 40,
    defaultSize: 25,
    class:
      'bg-(--ui-color-bg-content-secondary) border border-(--ui-color-divider-default) rounded-(--ui-border-radius-md) items-center justify-center text-description font-(--ui-font-weight-medium)'
  },
  {
    slot: 'main',
    defaultSize: 75,
    class:
      'bg-(--ui-color-bg-content-secondary) border border-(--ui-color-divider-default) rounded-(--ui-border-radius-md) items-center justify-center text-description font-(--ui-font-weight-medium)'
  }
])
</script>

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

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

const items = ref<SplitterItem[]>([
  {
    slot: 'sidebar',
    minSize: 15,
    maxSize: 40,
    defaultSize: 25,
    class:
      'bg-(--ui-color-bg-content-secondary) border border-(--ui-color-divider-default) rounded-(--ui-border-radius-md) items-center justify-center text-description font-(--ui-font-weight-medium)'
  },
  {
    slot: 'main',
    defaultSize: 75,
    class:
      'bg-(--ui-color-bg-content-secondary) border border-(--ui-color-divider-default) rounded-(--ui-border-radius-md) items-center justify-center text-description font-(--ui-font-weight-medium)'
  }
])
</script>

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

    <template #main> Main </template>
  </B24Splitter>
</template>

Orientation

Use the orientation prop to change the direction of the splitter. Defaults to horizontal.

First
Second
<script setup lang="ts">
import type { SplitterItem } from '@bitrix24/b24ui-nuxt'

const items = ref<SplitterItem[]>([
  {
    slot: 'first',
    class:
      'bg-(--ui-color-bg-content-secondary) border border-(--ui-color-divider-default) rounded-(--ui-border-radius-md) items-center justify-center text-description font-(--ui-font-weight-medium)'
  },
  {
    slot: 'second',
    class:
      'bg-(--ui-color-bg-content-secondary) border border-(--ui-color-divider-default) rounded-(--ui-border-radius-md) items-center justify-center text-description font-(--ui-font-weight-medium)'
  }
])
</script>

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

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

const items = ref<SplitterItem[]>([
  {
    slot: 'first',
    class:
      'bg-(--ui-color-bg-content-secondary) border border-(--ui-color-divider-default) rounded-(--ui-border-radius-md) items-center justify-center text-description font-(--ui-font-weight-medium)'
  },
  {
    slot: 'second',
    class:
      'bg-(--ui-color-bg-content-secondary) border border-(--ui-color-divider-default) rounded-(--ui-border-radius-md) items-center justify-center text-description font-(--ui-font-weight-medium)'
  }
])
</script>

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

    <template #second> Second </template>
  </B24Splitter>
</template>

Examples

With collapsible panel

Set collapsible: true on an item to let it collapse past its minSize, and use collapsedSize to keep part of the panel visible when collapsed. The panel slot exposes collapsed, collapse and expand so you can control it programmatically, and the collapse, expand and resize events fire with the panel index.

Main
<script setup lang="ts">
import type { SplitterItem } from '@bitrix24/b24ui-nuxt'
import CloseChatIcon from '@bitrix24/b24icons-vue/outline/CloseChatIcon'
import OpenChatIcon from '@bitrix24/b24icons-vue/outline/OpenChatIcon'

const items: SplitterItem[] = [
  { slot: 'sidebar', sizeUnit: 'px', minSize: 150, defaultSize: 250, collapsible: true, collapsedSize: 48, class: 'bg-(--ui-color-bg-content-secondary) border border-(--ui-color-divider-default) rounded-(--ui-border-radius-md)' },
  { slot: 'main', class: 'bg-(--ui-color-bg-content-secondary) border border-(--ui-color-divider-default) rounded-(--ui-border-radius-md) items-center justify-center text-description font-(--ui-font-weight-medium)' }
]
</script>

<template>
  <div class="w-full h-96">
    <B24Splitter id="splitter-collapsible-example" :items="items">
      <template #sidebar="{ collapsed, collapse, expand }">
        <div class="flex-1 flex items-center justify-center p-2">
          <B24Button
            :icon="collapsed ? OpenChatIcon : CloseChatIcon"
            :label="collapsed ? undefined : 'Collapse'"
            :aria-label="collapsed ? 'Expand' : undefined"
            color="air-secondary"
            size="sm"
            @click="collapsed ? expand() : collapse()"
          />
        </div>
      </template>

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

With nested splitters

Nest a Splitter inside a panel to build two-dimensional, IDE-style layouts.

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

const card = 'bg-(--ui-color-bg-content-secondary) border border-(--ui-color-divider-default) rounded-(--ui-border-radius-md) items-center justify-center text-description font-(--ui-font-weight-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">
    <B24Splitter id="splitter-nested-example" :items="items">
      <template #left>
        Left
      </template>

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

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

With custom handle

The handle is invisible by default. Use the b24ui prop to restyle it, for example as a visible divider for flush layouts, and the resize-handle slot to render content inside it like a grip.

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

const panel = 'items-center justify-center text-description font-(--ui-font-weight-medium)'

const items: SplitterItem[] = [
  { slot: 'left', minSize: 20, defaultSize: 30, class: panel },
  { slot: 'right', defaultSize: 70, class: panel }
]
</script>

<template>
  <div class="w-full h-96">
    <B24Splitter
      id="splitter-custom-handle-example"
      :items="items"
      :b24ui="{ handle: 'data-[orientation=horizontal]:w-px data-[orientation=vertical]:h-px bg-(--ui-color-divider-default) transition-colors data-[state=hover]:bg-(--ui-color-accent-main-primary) data-[state=drag]:bg-(--ui-color-accent-main-primary)' }"
      class="rounded-(--ui-border-radius-md) border border-(--ui-color-divider-default) overflow-hidden"
    >
      <template #left>
        Left
      </template>

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

With persistence

Provide an auto-save-id to persist the layout to localStorage and restore it on reload.

<template>
  <B24Splitter id="my-layout" auto-save-id="my-layout" :items="items">
    <!-- ... -->
  </B24Splitter>
</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[]
disabledfalseboolean

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

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

Slots

Slot Type
resize-handle{ index: number; b24ui: 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

https://github.com/bitrix24/b24ui/tree/main/src/theme/splitter.ts
export default {
  slots: {
    root: '',
    panel: 'flex',
    handle: 'group relative shrink-0 outline-transparent focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--ui-color-design-outline-focused-stroke) 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