Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 92 additions & 10 deletions packages/ui/ADVANCED_TABLE_FEATURES.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,97 @@ This document describes the advanced features implementation in ObjectQL UI tabl

### ✅ 已实现的功能 (Implemented Features)

1. **Grouping (分组)** - 按列分组数据显示
2. **Inline Editing (内联编辑)** - Grid 中直接编辑单元格
3. **Bulk Operations (批量操作)** - 批量删除、批量更新
4. **Copy/Paste (复制粘贴)** - 复制选中行到剪贴板
5. **Drag & Drop (拖拽排序)** - 字段拖拽排序
1. **Sorting (排序)** - 单列和多列排序
2. **Grouping (分组)** - 按列分组数据显示
3. **Inline Editing (内联编辑)** - Grid 中直接编辑单元格
4. **Bulk Operations (批量操作)** - 批量删除、批量更新
5. **Copy/Paste (复制粘贴)** - 复制选中行到剪贴板
6. **Drag & Drop (拖拽排序)** - 字段拖拽排序

---

## 1. Grouping (分组)
## 1. Sorting (排序)

### 功能描述 (Feature Description)

行排序功能允许用户对表格数据按一个或多个列进行排序,类似 Airtable 的排序体验。

The row sorting feature allows users to sort table data by one or multiple columns, similar to Airtable's sorting experience.

### 使用方法 (Usage)

#### GridView 组件

```tsx
import { GridView, SortConfig } from '@objectql/ui'

function MyComponent() {
const [sorts, setSorts] = useState<SortConfig[]>([])

return (
<GridView
columns={columns}
data={data}
enableSorting={true}
onSortChange={(newSorts) => setSorts(newSorts)}
/>
)
}
```

### 特性 (Features)

- ✅ 单列排序:点击列标题排序
- ✅ 多列排序:Shift + 点击添加多个排序级别
- ✅ 排序指示器:显示排序方向 (↑ 升序 / ↓ 降序)
- ✅ 排序优先级:多列排序时显示优先级数字 (1, 2, 3...)
- ✅ 智能排序:根据数据类型自动选择排序算法
- ✅ 可配置:可以禁用特定列的排序

### 排序行为 (Sorting Behavior)

**单列排序 (Single Column Sort):**
1. 第一次点击:升序 (A→Z, 0→9, 旧→新)
2. 第二次点击:降序 (Z→A, 9→0, 新→旧)
3. 第三次点击:清除排序

**多列排序 (Multi-Column Sort):**
1. 按住 Shift 键点击列标题
2. 可添加多个排序级别
3. 先按第一个排序字段,再按第二个,以此类推

**数据类型排序 (Data Type Sorting):**
- `text`: 不区分大小写的字母排序
- `number`: 数值比较
- `date`: 按时间顺序
- `boolean`: false 在前,true 在后
- `null/undefined`: 始终排在最后

### API

**SortConfig 类型:**

```typescript
interface SortConfig {
columnId: string
direction: 'asc' | 'desc'
}
```

**Column 属性:**

```typescript
interface Column {
id: string
label: string
sortable?: boolean // 默认 true,设为 false 可禁用该列排序
// ... 其他属性
}
```

---

## 2. Grouping (分组)

### 功能描述 (Feature Description)

Expand Down Expand Up @@ -62,7 +144,7 @@ import { DataTable } from '@objectql/ui'

---

## 2. Inline Editing (内联编辑)
## 3. Inline Editing (内联编辑)

### 功能描述 (Feature Description)

Expand Down Expand Up @@ -122,7 +204,7 @@ const columns = [

---

## 3. Bulk Operations (批量操作)
## 4. Bulk Operations (批量操作)

### 功能描述 (Feature Description)

Expand Down Expand Up @@ -189,7 +271,7 @@ When rows are selected, a bulk actions toolbar appears with:

---

## 4. Copy/Paste (复制粘贴)
## 5. Copy/Paste (复制粘贴)

### 功能描述 (Feature Description)

Expand Down Expand Up @@ -233,7 +315,7 @@ Mobile App Development Engineering active high

---

## 5. Drag & Drop (拖拽排序)
## 6. Drag & Drop (拖拽排序)

### 功能描述 (Feature Description)

Expand Down
73 changes: 73 additions & 0 deletions packages/ui/AIRTABLE_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@ const columns = [
- `onRowClick`: Callback when row is clicked (optional)
- `onDelete`: Callback for delete action (optional)
- `emptyMessage`: Message shown when no data (optional)
- `enableSorting`: Enable sorting on column headers (default: true)
- `onSortChange`: Callback when sort configuration changes (optional)
- `enableRowSelection`: Enable row selection checkboxes (optional)
- `enableGrouping`: Enable grouping by column (optional)
- `enableCopyPaste`: Enable copy/paste functionality (optional)
- `enableColumnDragDrop`: Enable column reordering via drag & drop (optional)

**Column Types:**
- `text`: Plain text (editable)
Expand All @@ -74,6 +80,73 @@ const columns = [
- `badge`: Status badges with colors
- `boolean`: Checkbox

**Column Properties:**
- `id`: Unique column identifier (required)
- `label`: Column header label (required)
- `type`: Column data type (optional)
- `width`: Column width in pixels or string (optional)
- `editable`: Enable inline editing for this column (optional)
- `sortable`: Enable/disable sorting for this column (default: true when enableSorting is true)
- `options`: Options for badge/select types (optional)

### Sorting

GridView supports single and multi-column sorting similar to Airtable:

**Single Column Sorting:**
- Click on a column header to sort ascending
- Click again to sort descending
- Click a third time to remove sorting

**Multi-Column Sorting:**
- Hold Shift and click column headers to add additional sort levels
- Each sorted column shows its sort direction (↑ for ascending, ↓ for descending)
- Multi-column sorts show sort priority numbers (1, 2, 3, etc.)

**Example:**

```tsx
import { GridView, SortConfig } from '@objectql/ui'

function MyComponent() {
const [sorts, setSorts] = useState<SortConfig[]>([])

const handleSortChange = (newSorts: SortConfig[]) => {
setSorts(newSorts)
console.log('Active sorts:', newSorts)
// Example output: [
// { columnId: 'priority', direction: 'desc' },
// { columnId: 'name', direction: 'asc' }
// ]
}

return (
<GridView
columns={columns}
data={data}
enableSorting={true}
onSortChange={handleSortChange}
/>
)
}
```

**Disabling Sorting for Specific Columns:**

```tsx
const columns = [
{ id: 'name', label: 'Name', sortable: true },
{ id: 'actions', label: 'Actions', sortable: false }, // No sorting for this column
]
```

**Sorting Behavior:**
- Text fields: Case-insensitive alphabetical sorting
- Number fields: Numeric comparison
- Date fields: Chronological sorting
- Boolean fields: false before true
- Null/undefined values: Always sorted to the end

### Toolbar & ViewSwitcher

Toolbar provides a consistent header for your views with title, actions, and view switching.
Expand Down
45 changes: 35 additions & 10 deletions packages/ui/examples/airtable-example.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from "react"
import { GridView } from "../components/grid/GridView"
import { GridView, SortConfig } from "../components/grid/GridView"
import { Toolbar, ViewSwitcher, ToolbarIcons } from "../components/Toolbar"
import { Button } from "../components/Button"
import { Badge } from "../components/Badge"
Expand Down Expand Up @@ -119,6 +119,7 @@ export function AirtableExample() {
const [data, setData] = React.useState(sampleData)
const [activeView, setActiveView] = React.useState('grid')
const [showCreateModal, setShowCreateModal] = React.useState(false)
const [sorts, setSorts] = React.useState<SortConfig[]>([])

const handleCellEdit = (rowIndex: number, columnId: string, value: any) => {
const newData = [...data]
Expand All @@ -141,6 +142,19 @@ export function AirtableExample() {
setShowCreateModal(false)
}

const handleSortChange = (newSorts: SortConfig[]) => {
setSorts(newSorts)
console.log('Sorts changed:', newSorts)
}

const getSortDescription = () => {
if (sorts.length === 0) return 'No sorting applied'
return sorts.map((s, i) => {
const column = sampleColumns.find(c => c.id === s.columnId)
return `${i + 1}. ${column?.label} (${s.direction === 'asc' ? '↑' : '↓'})`
}).join(', ')
}

const views = [
{ id: 'grid', label: 'Grid', icon: <ToolbarIcons.Grid /> },
{ id: 'list', label: 'List', icon: <ToolbarIcons.List /> },
Expand All @@ -166,10 +180,11 @@ export function AirtableExample() {
</Button>
<Button
variant="secondary"
onClick={() => alert('Sort functionality')}
onClick={() => alert(getSortDescription())}
title={getSortDescription()}
>
<ToolbarIcons.Sort />
<span className="ml-2">Sort</span>
<span className="ml-2">Sort {sorts.length > 0 && `(${sorts.length})`}</span>
</Button>
<Button onClick={() => setShowCreateModal(true)}>
<ToolbarIcons.Plus />
Expand All @@ -179,13 +194,23 @@ export function AirtableExample() {

<div className="flex-1 overflow-auto p-6">
{activeView === 'grid' ? (
<GridView
columns={sampleColumns}
data={data}
onCellEdit={handleCellEdit}
onDelete={handleDelete}
emptyMessage="No projects found. Create one to get started!"
/>
<div className="space-y-3">
{sorts.length > 0 && (
<div className="bg-blue-50 border border-blue-200 rounded-lg px-4 py-2 text-sm text-blue-900">
<strong>Active sorts:</strong> {getSortDescription()}
<span className="ml-2 text-xs text-blue-700">(Click column headers to sort, Shift+Click for multi-column)</span>
</div>
)}
<GridView
columns={sampleColumns}
data={data}
onCellEdit={handleCellEdit}
onDelete={handleDelete}
onSortChange={handleSortChange}
enableSorting={true}
emptyMessage="No projects found. Create one to get started!"
/>
</div>
) : (
<div className="bg-white rounded-lg border border-stone-200 p-8 text-center text-stone-500">
<p>List view coming soon...</p>
Expand Down
Loading
Loading