npm package: @sukritt/datagrid-vue
Description: Custom zero-dependency DataGrid component for Vue 3
Latest version: 1.0.4

# @sukritt/datagrid-vue

DataGrid component สำหรับ Vue 3 — port จาก [@sukritt/datagrid](https://gitlab.bizcarrental.com/component/datagrid) (React version)

## Features

- Virtual scroll — รองรับข้อมูลหลายหมื่นแถว
- Sort — คลิก header เพื่อเรียง asc/desc
- Filter — text, number, date, select (dropdown) per column + global search
- **Column Group Headers** — จัดกลุ่ม columns ใต้ parent header (multi-level)
- Group By — drag header ไปที่ groupby bar, รองรับหลาย level
- Aggregation — sum, avg, count, min, max per group
- Pagination — พร้อม page size selector
- Column resize — ลากขอบ header ปรับขนาด (group width อัปเดตอัตโนมัติ)
- Column reorder — drag & drop สลับ column
- Column visibility — เปิด/ปิด column ผ่าน sidebar
- Freeze column — right-click header → Freeze Column
- Row selection — single / multiple
- Cell editing — double-click เพื่อแก้ไข
- Export CSV / Excel
- Loading overlay
- TypeScript generic `TData`

## Installation

```bash
npm install @sukritt/datagrid-vue
```

## Usage

```vue
<script setup lang="ts">
import { DataGrid } from '@sukritt/datagrid-vue'
import '@sukritt/datagrid-vue/style.css'
import type { ColumnDef } from '@sukritt/datagrid-vue'

interface Employee {
  id: number
  name: string
  department: string
  salary: number
}

const rowData: Employee[] = [
  { id: 1, name: 'สมชาย ใจดี', department: 'IT', salary: 45000 },
  { id: 2, name: 'สมหญิง รักงาน', department: 'HR', salary: 38000 },
]

const columnDefs: ColumnDef<Employee>[] = [
  { field: 'id', headerName: 'รหัส', width: 70, align: 'center' },
  { field: 'name', headerName: 'ชื่อ-นามสกุล', width: 200 },
  { field: 'department', headerName: 'แผนก', width: 130, filter: 'select' },
  {
    field: 'salary',
    headerName: 'เงินเดือน',
    width: 130,
    align: 'right',
    filter: 'number',
    valueFormatter: ({ value }) =>
      Number(value).toLocaleString('th-TH') + ' บาท',
  },
]
</script>

<template>
  <DataGrid
    :row-data="rowData"
    :column-defs="columnDefs"
    :height="500"
  />
</template>
```

## Column Group Headers

จัดกลุ่ม columns ใต้ parent header เดียวกัน โดยใช้ `ColumnGroupDef` ใน `columnDefs`

**หลักการ:** columns ที่ต้องการให้อยู่ใต้ group เดียวกัน ให้ใส่ไว้ใน `children` ของ `ColumnGroupDef` — columns ที่ไม่อยู่ใน `children` จะแสดงเป็น column ปกติ (ไม่มี group)

```text
columnDefs = [
  ColumnDef          ← column ปกติ (ไม่มี group header)
  ColumnDef          ← column ปกติ
  ColumnGroupDef     ← group header "AMOUNT"
    └─ ColumnDef     ← อยู่ใต้ "AMOUNT"
    └─ ColumnDef     ← อยู่ใต้ "AMOUNT"
    └─ ColumnDef     ← อยู่ใต้ "AMOUNT"
]
```

```vue
<script setup lang="ts">
import { DataGrid } from '@sukritt/datagrid-vue'
import '@sukritt/datagrid-vue/style.css'
import type { ColumnDefOrGroup } from '@sukritt/datagrid-vue'

interface SalesRow {
  customer: string
  region: string
  amount2025: number
  amount2026: number
  lifetime: number
}

const columnDefs: ColumnDefOrGroup<SalesRow>[] = [
  // columns ปกติ — ไม่อยู่ใน group ใดๆ
  { field: 'customer', headerName: 'ลูกค้า', width: 200 },
  { field: 'region',   headerName: 'ภูมิภาค', width: 120 },

  // ColumnGroupDef — columns ใน children จะอยู่ใต้ "AMOUNT"
  {
    groupId: 'amount',        // ID ไว้อ้างอิง (ต้อง unique)
    headerName: 'AMOUNT',     // ชื่อที่แสดงบน group header
    headerAlign: 'center',
    children: [
      { field: 'amount2025', headerName: 'ยอดรวมในปี 2025', width: 160, align: 'right' },
      { field: 'amount2026', headerName: 'ยอดรวมในปี 2026', width: 160, align: 'right' },
      { field: 'lifetime',   headerName: 'ตลอดอายุการใช้งาน', width: 180, align: 'right' },
    ],
  },
]
</script>

<template>
  <DataGrid :row-data="salesData" :column-defs="columnDefs" :height="500" />
</template>
```

ผลลัพธ์ที่ได้:

```text
┌──────────┬──────────┬───────────────────────────────────────────┐
│          │          │                  AMOUNT                   │
│  ลูกค้า  │ ภูมิภาค  ├─────────────┬─────────────┬─────────────-┤
│          │          │ ยอดรวมปี 25 │ ยอดรวมปี 26 │ ตลอดอายุฯ  │
└──────────┴──────────┴─────────────┴─────────────┴──────────────┘
```

> **หมายเหตุ:** ชื่อ group header และ column ที่อยู่ใน group สามารถจัดการได้ผ่าน Sidebar (ปุ่ม ⠿ มุมขวาบน) โดยไม่ต้องแก้ code

### จัดการ Column Groups ผ่าน Sidebar

กด **⠿** มุมขวาบนของ grid เพื่อเปิด Sidebar แล้วไปที่ section **Column Groups**

| การกระทำ | วิธี |
| --- | --- |
| สร้าง group ใหม่ | กด **+** → พิมพ์ชื่อ → กด Enter หรือ **+** สีน้ำเงิน |
| เพิ่ม column | ลาก column จาก list ด้านบนมาวางใน drop zone ของ group |
| เอา column ออก | กด **×** ข้างชื่อ column ใน group |
| แก้ชื่อ group | คลิกที่ชื่อ group แล้วพิมพ์ชื่อใหม่ได้เลย |
| ลบ group | กด **×** ข้าง group header (columns กลับเป็นปกติ) |

columns ที่ถูกลากเข้า group จะแสดง badge **▤** ใน column list

### ColumnGroupDef Options

| Option | Type | คำอธิบาย |
|--------|------|----------|
| `groupId` | `string` | ID ของกลุ่ม — ต้อง unique (required) |
| `headerName` | `string` | ชื่อที่แสดงบน group header (required) |
| `children` | `ColumnDef<TData>[]` | columns ที่จะอยู่ใต้ group นี้ (required) |
| `headerAlign` | `'left' \| 'center' \| 'right'` | จัดตำแหน่ง label ของ group header (default: `center`) |
| `headerStyle` | `CSSProperties` | custom style ของ group header cell |

- `columnDefs` รองรับ `ColumnDef` และ `ColumnGroupDef` ปนกันได้ในอาร์เรย์เดียวกัน
- Group header width คำนวณจาก child columns อัตโนมัติ และอัปเดตเมื่อ resize หรือซ่อน column
- Grid ที่ไม่มี group จะแสดง header row เดียวเหมือนเดิม (backward compatible)

## Props

| Prop | Type | Default | คำอธิบาย |
|------|------|---------|----------|
| `rowData` | `TData[]` | — | ข้อมูลทั้งหมด (required) |
| `columnDefs` | `ColumnDef<TData>[]` | — | นิยาม columns (required) |
| `height` | `number \| string` | `500` | ความสูงของ grid (`px`, `%`, `vh`, `'100%'`) |
| `rowHeight` | `number` | `40` | ความสูงต่อ row (px) |
| `headerHeight` | `number` | `40` | ความสูง header (px) |
| `pagination` | `PaginationOptions` | enabled, 20/page | ตั้งค่า pagination |
| `enableFilter` | `boolean` | `true` | เปิด filter |
| `enableColumnResize` | `boolean` | `true` | เปิด column resize |
| `enableColumnReorder` | `boolean` | `true` | เปิด column reorder |
| `enableCellEdit` | `boolean` | `false` | เปิด cell editing |
| `rowSelection` | `'single' \| 'multiple' \| false` | `false` | การเลือก row |
| `loading` | `boolean` | `false` | แสดง loading overlay |
| `noRowsText` | `string` | `'ไม่มีข้อมูล'` | ข้อความเมื่อไม่มีข้อมูล |

## Events

| Event | Payload | คำอธิบาย |
|-------|---------|----------|
| `rowClick` | `(data: TData, rowIndex: number)` | คลิก row |
| `cellValueChanged` | `CellValueChangedParams<TData>` | แก้ไข cell |
| `selectionChanged` | `(selectedRows: TData[])` | selection เปลี่ยน |

```vue
<DataGrid
  :row-data="rowData"
  :column-defs="columnDefs"
  @row-click="(row, idx) => console.log(row, idx)"
  @cell-value-changed="(params) => console.log(params)"
  @selection-changed="(rows) => console.log(rows)"
/>
```

## Expose (Template Ref)

```vue
<script setup>
import { ref } from 'vue'
const grid = ref()
</script>

<template>
  <DataGrid ref="grid" ... />
  <button @click="grid.exportToExcel()">Export Excel</button>
</template>
```

| Method | คำอธิบาย |
|--------|----------|
| `exportToCsv(fileName?)` | Export CSV |
| `exportToExcel(fileName?)` | Export Excel |
| `getSelectedRows()` | ดึง selected rows |
| `selectAll()` | เลือกทุก row |
| `deselectAll()` | ยกเลิก selection ทั้งหมด |
| `autoSizeColumns()` | Auto-size ทุก column |
| `autoSizeColumn(field)` | Auto-size column เดียว |

## ColumnDef Options

| Option | Type | คำอธิบาย |
|--------|------|----------|
| `field` | `string` | key ใน rowData (required) |
| `headerName` | `string` | ชื่อ header |
| `width` | `number` | ความกว้างคงที่ (px) |
| `minWidth` | `number` | ความกว้างต่ำสุด (px) |
| `maxWidth` | `number` | ความกว้างสูงสุด (px) |
| `sortable` | `boolean` | เรียงลำดับได้ |
| `filter` | `boolean \| 'text' \| 'number' \| 'date' \| 'select'` | ประเภท filter |
| `resizable` | `boolean` | ปรับขนาดได้ |
| `editable` | `boolean` | แก้ไขได้ |
| `align` | `'left' \| 'center' \| 'right'` | จัดตำแหน่งข้อความ |
| `hide` | `boolean` | ซ่อน column |
| `cellRenderer` | `(params) => VNode \| string \| null` | custom cell render |
| `valueFormatter` | `(params) => string` | format ค่าก่อนแสดง |
| `cellStyle` | `CSSProperties \| (params) => CSSProperties` | style ของ cell |

## Development

```bash
npm install
npm run dev      # dev server
npm run build    # build library
```

## React Version

[@sukritt/datagrid](https://gitlab.bizcarrental.com/component/datagrid) — React version