Skip to content

Commit 90ae69e

Browse files
authored
Merge pull request #18 from objectql/copilot/add-airtable-row-sorting
2 parents 42da27b + 38449b9 commit 90ae69e

File tree

4 files changed

+377
-47
lines changed

4 files changed

+377
-47
lines changed

packages/ui/ADVANCED_TABLE_FEATURES.md

Lines changed: 92 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,97 @@ This document describes the advanced features implementation in ObjectQL UI tabl
88

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

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

1718
---
1819

19-
## 1. Grouping (分组)
20+
## 1. Sorting (排序)
21+
22+
### 功能描述 (Feature Description)
23+
24+
行排序功能允许用户对表格数据按一个或多个列进行排序,类似 Airtable 的排序体验。
25+
26+
The row sorting feature allows users to sort table data by one or multiple columns, similar to Airtable's sorting experience.
27+
28+
### 使用方法 (Usage)
29+
30+
#### GridView 组件
31+
32+
```tsx
33+
import { GridView, SortConfig } from '@objectql/ui'
34+
35+
function MyComponent() {
36+
const [sorts, setSorts] = useState<SortConfig[]>([])
37+
38+
return (
39+
<GridView
40+
columns={columns}
41+
data={data}
42+
enableSorting={true}
43+
onSortChange={(newSorts) => setSorts(newSorts)}
44+
/>
45+
)
46+
}
47+
```
48+
49+
### 特性 (Features)
50+
51+
- ✅ 单列排序:点击列标题排序
52+
- ✅ 多列排序:Shift + 点击添加多个排序级别
53+
- ✅ 排序指示器:显示排序方向 (↑ 升序 / ↓ 降序)
54+
- ✅ 排序优先级:多列排序时显示优先级数字 (1, 2, 3...)
55+
- ✅ 智能排序:根据数据类型自动选择排序算法
56+
- ✅ 可配置:可以禁用特定列的排序
57+
58+
### 排序行为 (Sorting Behavior)
59+
60+
**单列排序 (Single Column Sort):**
61+
1. 第一次点击:升序 (A→Z, 0→9, 旧→新)
62+
2. 第二次点击:降序 (Z→A, 9→0, 新→旧)
63+
3. 第三次点击:清除排序
64+
65+
**多列排序 (Multi-Column Sort):**
66+
1. 按住 Shift 键点击列标题
67+
2. 可添加多个排序级别
68+
3. 先按第一个排序字段,再按第二个,以此类推
69+
70+
**数据类型排序 (Data Type Sorting):**
71+
- `text`: 不区分大小写的字母排序
72+
- `number`: 数值比较
73+
- `date`: 按时间顺序
74+
- `boolean`: false 在前,true 在后
75+
- `null/undefined`: 始终排在最后
76+
77+
### API
78+
79+
**SortConfig 类型:**
80+
81+
```typescript
82+
interface SortConfig {
83+
columnId: string
84+
direction: 'asc' | 'desc'
85+
}
86+
```
87+
88+
**Column 属性:**
89+
90+
```typescript
91+
interface Column {
92+
id: string
93+
label: string
94+
sortable?: boolean // 默认 true,设为 false 可禁用该列排序
95+
// ... 其他属性
96+
}
97+
```
98+
99+
---
100+
101+
## 2. Grouping (分组)
20102

21103
### 功能描述 (Feature Description)
22104

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

63145
---
64146

65-
## 2. Inline Editing (内联编辑)
147+
## 3. Inline Editing (内联编辑)
66148

67149
### 功能描述 (Feature Description)
68150

@@ -122,7 +204,7 @@ const columns = [
122204

123205
---
124206

125-
## 3. Bulk Operations (批量操作)
207+
## 4. Bulk Operations (批量操作)
126208

127209
### 功能描述 (Feature Description)
128210

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

190272
---
191273

192-
## 4. Copy/Paste (复制粘贴)
274+
## 5. Copy/Paste (复制粘贴)
193275

194276
### 功能描述 (Feature Description)
195277

@@ -233,7 +315,7 @@ Mobile App Development Engineering active high
233315

234316
---
235317

236-
## 5. Drag & Drop (拖拽排序)
318+
## 6. Drag & Drop (拖拽排序)
237319

238320
### 功能描述 (Feature Description)
239321

packages/ui/AIRTABLE_GUIDE.md

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@ const columns = [
6565
- `onRowClick`: Callback when row is clicked (optional)
6666
- `onDelete`: Callback for delete action (optional)
6767
- `emptyMessage`: Message shown when no data (optional)
68+
- `enableSorting`: Enable sorting on column headers (default: true)
69+
- `onSortChange`: Callback when sort configuration changes (optional)
70+
- `enableRowSelection`: Enable row selection checkboxes (optional)
71+
- `enableGrouping`: Enable grouping by column (optional)
72+
- `enableCopyPaste`: Enable copy/paste functionality (optional)
73+
- `enableColumnDragDrop`: Enable column reordering via drag & drop (optional)
6874

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

83+
**Column Properties:**
84+
- `id`: Unique column identifier (required)
85+
- `label`: Column header label (required)
86+
- `type`: Column data type (optional)
87+
- `width`: Column width in pixels or string (optional)
88+
- `editable`: Enable inline editing for this column (optional)
89+
- `sortable`: Enable/disable sorting for this column (default: true when enableSorting is true)
90+
- `options`: Options for badge/select types (optional)
91+
92+
### Sorting
93+
94+
GridView supports single and multi-column sorting similar to Airtable:
95+
96+
**Single Column Sorting:**
97+
- Click on a column header to sort ascending
98+
- Click again to sort descending
99+
- Click a third time to remove sorting
100+
101+
**Multi-Column Sorting:**
102+
- Hold Shift and click column headers to add additional sort levels
103+
- Each sorted column shows its sort direction (↑ for ascending, ↓ for descending)
104+
- Multi-column sorts show sort priority numbers (1, 2, 3, etc.)
105+
106+
**Example:**
107+
108+
```tsx
109+
import { GridView, SortConfig } from '@objectql/ui'
110+
111+
function MyComponent() {
112+
const [sorts, setSorts] = useState<SortConfig[]>([])
113+
114+
const handleSortChange = (newSorts: SortConfig[]) => {
115+
setSorts(newSorts)
116+
console.log('Active sorts:', newSorts)
117+
// Example output: [
118+
// { columnId: 'priority', direction: 'desc' },
119+
// { columnId: 'name', direction: 'asc' }
120+
// ]
121+
}
122+
123+
return (
124+
<GridView
125+
columns={columns}
126+
data={data}
127+
enableSorting={true}
128+
onSortChange={handleSortChange}
129+
/>
130+
)
131+
}
132+
```
133+
134+
**Disabling Sorting for Specific Columns:**
135+
136+
```tsx
137+
const columns = [
138+
{ id: 'name', label: 'Name', sortable: true },
139+
{ id: 'actions', label: 'Actions', sortable: false }, // No sorting for this column
140+
]
141+
```
142+
143+
**Sorting Behavior:**
144+
- Text fields: Case-insensitive alphabetical sorting
145+
- Number fields: Numeric comparison
146+
- Date fields: Chronological sorting
147+
- Boolean fields: false before true
148+
- Null/undefined values: Always sorted to the end
149+
77150
### Toolbar & ViewSwitcher
78151

79152
Toolbar provides a consistent header for your views with title, actions, and view switching.

packages/ui/examples/airtable-example.tsx

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as React from "react"
2-
import { GridView } from "../components/grid/GridView"
2+
import { GridView, SortConfig } from "../components/grid/GridView"
33
import { Toolbar, ViewSwitcher, ToolbarIcons } from "../components/Toolbar"
44
import { Button } from "../components/Button"
55
import { Badge } from "../components/Badge"
@@ -119,6 +119,7 @@ export function AirtableExample() {
119119
const [data, setData] = React.useState(sampleData)
120120
const [activeView, setActiveView] = React.useState('grid')
121121
const [showCreateModal, setShowCreateModal] = React.useState(false)
122+
const [sorts, setSorts] = React.useState<SortConfig[]>([])
122123

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

145+
const handleSortChange = (newSorts: SortConfig[]) => {
146+
setSorts(newSorts)
147+
console.log('Sorts changed:', newSorts)
148+
}
149+
150+
const getSortDescription = () => {
151+
if (sorts.length === 0) return 'No sorting applied'
152+
return sorts.map((s, i) => {
153+
const column = sampleColumns.find(c => c.id === s.columnId)
154+
return `${i + 1}. ${column?.label} (${s.direction === 'asc' ? '↑' : '↓'})`
155+
}).join(', ')
156+
}
157+
144158
const views = [
145159
{ id: 'grid', label: 'Grid', icon: <ToolbarIcons.Grid /> },
146160
{ id: 'list', label: 'List', icon: <ToolbarIcons.List /> },
@@ -166,10 +180,11 @@ export function AirtableExample() {
166180
</Button>
167181
<Button
168182
variant="secondary"
169-
onClick={() => alert('Sort functionality')}
183+
onClick={() => alert(getSortDescription())}
184+
title={getSortDescription()}
170185
>
171186
<ToolbarIcons.Sort />
172-
<span className="ml-2">Sort</span>
187+
<span className="ml-2">Sort {sorts.length > 0 && `(${sorts.length})`}</span>
173188
</Button>
174189
<Button onClick={() => setShowCreateModal(true)}>
175190
<ToolbarIcons.Plus />
@@ -179,13 +194,23 @@ export function AirtableExample() {
179194

180195
<div className="flex-1 overflow-auto p-6">
181196
{activeView === 'grid' ? (
182-
<GridView
183-
columns={sampleColumns}
184-
data={data}
185-
onCellEdit={handleCellEdit}
186-
onDelete={handleDelete}
187-
emptyMessage="No projects found. Create one to get started!"
188-
/>
197+
<div className="space-y-3">
198+
{sorts.length > 0 && (
199+
<div className="bg-blue-50 border border-blue-200 rounded-lg px-4 py-2 text-sm text-blue-900">
200+
<strong>Active sorts:</strong> {getSortDescription()}
201+
<span className="ml-2 text-xs text-blue-700">(Click column headers to sort, Shift+Click for multi-column)</span>
202+
</div>
203+
)}
204+
<GridView
205+
columns={sampleColumns}
206+
data={data}
207+
onCellEdit={handleCellEdit}
208+
onDelete={handleDelete}
209+
onSortChange={handleSortChange}
210+
enableSorting={true}
211+
emptyMessage="No projects found. Create one to get started!"
212+
/>
213+
</div>
189214
) : (
190215
<div className="bg-white rounded-lg border border-stone-200 p-8 text-center text-stone-500">
191216
<p>List view coming soon...</p>

0 commit comments

Comments
 (0)