Skip to content

Resizable

Các nhóm panel có thể thay đổi kích thước với hỗ trợ bàn phím.

Cài đặt

bash
npx @galaxy-stack/nebula-cli@latest add resizable
bash
pnpm dlx @galaxy-stack/nebula-cli@latest add resizable
bash
yarn dlx @galaxy-stack/nebula-cli@latest add resizable
bash
bunx @galaxy-stack/nebula-cli@latest add resizable

Sử dụng

vue
<script setup lang="ts">
import {
  ResizablePanelGroup,
  ResizablePanel,
  ResizableHandle,
} from '@/components/ui/resizable'
</script>

<template>
  <ResizablePanelGroup direction="horizontal" class="max-w-md rounded-lg border">
    <ResizablePanel :default-size="50">
      <div class="flex h-[200px] items-center justify-center p-6">
        <span class="font-semibold">One</span>
      </div>
    </ResizablePanel>
    <ResizableHandle />
    <ResizablePanel :default-size="50">
      <div class="flex h-[200px] items-center justify-center p-6">
        <span class="font-semibold">Two</span>
      </div>
    </ResizablePanel>
  </ResizablePanelGroup>
</template>
tsx
import {
  ResizablePanelGroup,
  ResizablePanel,
  ResizableHandle,
} from "@/components/ui/resizable"

export default function App() {
  return (
    <ResizablePanelGroup direction="horizontal" className="max-w-md rounded-lg border">
      <ResizablePanel defaultSize={50}>
        <div className="flex h-[200px] items-center justify-center p-6">
          <span className="font-semibold">One</span>
        </div>
      </ResizablePanel>
      <ResizableHandle />
      <ResizablePanel defaultSize={50}>
        <div className="flex h-[200px] items-center justify-center p-6">
          <span className="font-semibold">Two</span>
        </div>
      </ResizablePanel>
    </ResizablePanelGroup>
  )
}
typescript
import { Component } from '@angular/core';
import {
  ResizablePanelGroupComponent,
  ResizablePanelComponent,
  ResizableHandleComponent,
} from '@/components/ui/resizable';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [
    ResizablePanelGroupComponent,
    ResizablePanelComponent,
    ResizableHandleComponent,
  ],
  template: `
    <ui-resizable-panel-group direction="horizontal" class="max-w-md rounded-lg border">
      <ui-resizable-panel [defaultSize]="50">
        <div class="flex h-[200px] items-center justify-center p-6">
          <span class="font-semibold">One</span>
        </div>
      </ui-resizable-panel>
      <ui-resizable-handle />
      <ui-resizable-panel [defaultSize]="50">
        <div class="flex h-[200px] items-center justify-center p-6">
          <span class="font-semibold">Two</span>
        </div>
      </ui-resizable-panel>
    </ui-resizable-panel-group>
  `
})
export class AppComponent {}

Ví dụ

Vertical

vue
<ResizablePanelGroup direction="vertical" class="min-h-[200px] max-w-md rounded-lg border">
  <ResizablePanel :default-size="25">
    <div class="flex h-full items-center justify-center p-6">
      <span class="font-semibold">Header</span>
    </div>
  </ResizablePanel>
  <ResizableHandle />
  <ResizablePanel :default-size="75">
    <div class="flex h-full items-center justify-center p-6">
      <span class="font-semibold">Content</span>
    </div>
  </ResizablePanel>
</ResizablePanelGroup>
tsx
<ResizablePanelGroup direction="vertical" className="min-h-[200px] max-w-md rounded-lg border">
  <ResizablePanel defaultSize={25}>
    <div className="flex h-full items-center justify-center p-6">
      <span className="font-semibold">Header</span>
    </div>
  </ResizablePanel>
  <ResizableHandle />
  <ResizablePanel defaultSize={75}>
    <div className="flex h-full items-center justify-center p-6">
      <span className="font-semibold">Content</span>
    </div>
  </ResizablePanel>
</ResizablePanelGroup>
html
<ui-resizable-panel-group direction="vertical" class="min-h-[200px] max-w-md rounded-lg border">
  <ui-resizable-panel [defaultSize]="25">
    <div class="flex h-full items-center justify-center p-6">
      <span class="font-semibold">Header</span>
    </div>
  </ui-resizable-panel>
  <ui-resizable-handle />
  <ui-resizable-panel [defaultSize]="75">
    <div class="flex h-full items-center justify-center p-6">
      <span class="font-semibold">Content</span>
    </div>
  </ui-resizable-panel>
</ui-resizable-panel-group>

Handle

Bạn có thể hiển thị tay cầm bằng withHandle.

vue
<ResizablePanelGroup direction="horizontal" class="max-w-md rounded-lg border">
  <ResizablePanel :default-size="50">
    <div class="flex h-[200px] items-center justify-center p-6">
      <span class="font-semibold">Sidebar</span>
    </div>
  </ResizablePanel>
  <ResizableHandle :with-handle="true" />
  <ResizablePanel :default-size="50">
    <div class="flex h-[200px] items-center justify-center p-6">
      <span class="font-semibold">Content</span>
    </div>
  </ResizablePanel>
</ResizablePanelGroup>
tsx
<ResizablePanelGroup direction="horizontal" className="max-w-md rounded-lg border">
  <ResizablePanel defaultSize={50}>
    <div className="flex h-[200px] items-center justify-center p-6">
      <span className="font-semibold">Sidebar</span>
    </div>
  </ResizablePanel>
  <ResizableHandle withHandle />
  <ResizablePanel defaultSize={50}>
    <div className="flex h-[200px] items-center justify-center p-6">
      <span className="font-semibold">Content</span>
    </div>
  </ResizablePanel>
</ResizablePanelGroup>
html
<ui-resizable-panel-group direction="horizontal" class="max-w-md rounded-lg border">
  <ui-resizable-panel [defaultSize]="50">
    <div class="flex h-[200px] items-center justify-center p-6">
      <span class="font-semibold">Sidebar</span>
    </div>
  </ui-resizable-panel>
  <ui-resizable-handle [withHandle]="true" />
  <ui-resizable-panel [defaultSize]="50">
    <div class="flex h-[200px] items-center justify-center p-6">
      <span class="font-semibold">Content</span>
    </div>
  </ui-resizable-panel>
</ui-resizable-panel-group>

API Reference

ResizablePanelGroup

PropVueReactAngularMô tả
direction'horizontal' | 'vertical''horizontal' | 'vertical''horizontal' | 'vertical'Hướng của panel group
autoSaveIdstringstring-Lưu layout vào local storage
keyboardResizeBynumbernumber-Bước resize khi dùng bàn phím
gutterSize--numberKích thước gutter theo pixel cho angular-split
class / classNamestringstringstringClass cho container
layout / onLayout / layoutChange(sizes: number[]) => void(sizes: number[]) => void(sizes: number[]) => voidEvent khi layout thay đổi

ResizablePanel

PropVueReactAngularMô tả
defaultSizenumbernumbernumberKích thước khởi tạo theo phần trăm
size--numberAlias tương thích ngược của Angular cho kích thước khởi tạo
minSizenumbernumbernumberKích thước tối thiểu
maxSizenumbernumbernumberKích thước tối đa
collapsiblebooleanbooleanbooleanCho phép panel thu gọn
collapsedSizenumbernumbernumberKích thước khi thu gọn
ordernumbernumbernumberThứ tự panel
class / classNamestringstringstringClass cho panel
resize / onResize(size: number, prevSize?: number) => voidevent của primitive-Event khi panel thay đổi kích thước
collapse / expand() => voidevent của primitive-Event khi panel thu gọn hoặc mở lại

ResizableHandle

PropVueReactAngularMô tả
disabledbooleanbooleanbooleanTắt resize qua handle hoặc gutter
withHandlebooleanbooleanbooleanHiển thị tay cầm trực quan
class / classNamestringstringstringClass cho handle
dragging(dragging: boolean) => voidevent của primitive-Event khi đang kéo

Ghi chú

  • Vue hiện dùng Radix Vue Splitter.
  • React dùng react-resizable-panels.
  • Angular dùng angular-split và được chuẩn hoá để gần API chung nhất có thể.
  • Angular vẫn giữ size như alias tương thích ngược của defaultSize.
  • Angular đã expose collapsiblecollapsedSize theo schema chung, nhưng wrapper hiện chưa enforce đầy đủ hai prop này.

Khả năng tiếp cận

Tương tác bàn phím

PhímMô tả
ArrowLeft / ArrowRightResize layout ngang
ArrowUp / ArrowDownResize layout dọc
EnterReset gần về kích thước mặc định nếu primitive nền hỗ trợ
Home / EndDi chuyển nhanh về min/max nếu primitive nền hỗ trợ

Giấy phép

MIT © 2025 Bùi Trọng Hiếu (kevinbui)

Phát hành theo Giấy phép MIT.