@ticatec/topside-elements 1.0.6 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -0
- package/dist/app_layout/ClassicLayout.svelte +22 -11
- package/dist/app_layout/ClassicLayout.svelte.d.ts +6 -0
- package/dist/app_layout/ColumnsLayout.svelte +7 -4
- package/dist/app_layout/ColumnsLayout.svelte.d.ts +2 -0
- package/dist/app_layout/HeaderLayout.svelte +11 -2
- package/dist/app_layout/HeaderLayout.svelte.d.ts +4 -0
- package/dist/app_layout/README.md +23 -1
- package/dist/app_layout/README_CN.md +23 -1
- package/dist/app_layout/RowsLayout.svelte +7 -4
- package/dist/app_layout/RowsLayout.svelte.d.ts +2 -0
- package/dist/app_layout/SidebarLayout.svelte +11 -2
- package/dist/app_layout/SidebarLayout.svelte.d.ts +4 -0
- package/dist/data_table/DataTable.svelte +153 -12
- package/dist/data_table/DataTable.svelte.d.ts +22 -2
- package/dist/data_table/README.md +119 -38
- package/dist/data_table/README_CN.md +119 -38
- package/dist/data_table/index.d.ts +5 -0
- package/dist/data_table/index.js +2 -0
- package/dist/data_table/lib/IndicatorColumn.d.ts +2 -1
- package/dist/data_table/lib/RowExpansion.d.ts +84 -0
- package/dist/data_table/lib/RowExpansion.js +43 -0
- package/dist/data_table/parts/ActionsPanel.svelte +4 -0
- package/dist/data_table/parts/ActionsPanel.svelte.d.ts +2 -0
- package/dist/data_table/parts/ActionsRow.svelte +8 -1
- package/dist/data_table/parts/ActionsRow.svelte.d.ts +2 -0
- package/dist/data_table/parts/ContentPanel.svelte +9 -1
- package/dist/data_table/parts/ContentPanel.svelte.d.ts +4 -0
- package/dist/data_table/parts/DataRow.svelte +13 -2
- package/dist/data_table/parts/DataRow.svelte.d.ts +2 -0
- package/dist/data_table/parts/FixedColumnsPanel.svelte +11 -2
- package/dist/data_table/parts/FixedColumnsPanel.svelte.d.ts +6 -1
- package/dist/data_table/parts/FixedRow.svelte +19 -2
- package/dist/data_table/parts/FixedRow.svelte.d.ts +4 -0
- package/docs/components/README.md +6 -6
- package/docs/components/classic-layout/README.md +3 -0
- package/docs/components/columns-layout/README.md +1 -0
- package/docs/components/data-table/README.md +53 -1
- package/docs/components/header-layout/README.md +2 -0
- package/docs/components/rows-layout/README.md +1 -0
- package/docs/components/sidebar-layout/README.md +2 -0
- package/package.json +1 -1
|
@@ -15,6 +15,13 @@
|
|
|
15
15
|
import { buildTableSummary } from "./lib/summary";
|
|
16
16
|
import { isSameColumn } from "./lib/TableUtils";
|
|
17
17
|
import i18nRes from "../i18nRes";
|
|
18
|
+
import type RowExpansion from "./lib/RowExpansion";
|
|
19
|
+
import {
|
|
20
|
+
type RowExpandContext,
|
|
21
|
+
type RowExpandRequest,
|
|
22
|
+
type RowEventHandlers,
|
|
23
|
+
isInteractiveTarget
|
|
24
|
+
} from "./lib/RowExpansion";
|
|
18
25
|
|
|
19
26
|
interface Props {
|
|
20
27
|
columns: Array<DataColumn>;
|
|
@@ -45,6 +52,13 @@
|
|
|
45
52
|
* 分页场景下用它显示服务端算好的全量合计(数字串会按数字格式化)。
|
|
46
53
|
*/
|
|
47
54
|
summaryValues?: Record<string, any> | null;
|
|
55
|
+
/**
|
|
56
|
+
* 行展开配置:展开区组件、是否显示指示列图标(默认显示)、整行单击 / 双击触发等。
|
|
57
|
+
* 不配置图标时可以用组件方法 expandRow / collapseRow / toggleRowExpand,
|
|
58
|
+
* 或在行内(自定义单元格、操作列、展开区)派发 ROW_EXPAND_EVENT 事件来控制。
|
|
59
|
+
* 同一时间只展开一行。
|
|
60
|
+
*/
|
|
61
|
+
rowExpansion?: RowExpansion<T> | null;
|
|
48
62
|
}
|
|
49
63
|
|
|
50
64
|
let {
|
|
@@ -65,6 +79,7 @@
|
|
|
65
79
|
overscan = 8,
|
|
66
80
|
summaryLabel,
|
|
67
81
|
summaryValues = null,
|
|
82
|
+
rowExpansion = null,
|
|
68
83
|
}: Props = $props();
|
|
69
84
|
|
|
70
85
|
const id: string = Math.round(new Date().getTime() + Math.random() * 1000).toString(36);
|
|
@@ -78,13 +93,24 @@
|
|
|
78
93
|
let frozenWidth = $state(0);
|
|
79
94
|
let styleEl: HTMLStyleElement | undefined;
|
|
80
95
|
|
|
81
|
-
let
|
|
96
|
+
let expandedRow = $state<TableRow | null>(null);
|
|
82
97
|
let scrollTop = $state(0);
|
|
83
98
|
let inlineComponent = $state<any>(null);
|
|
84
99
|
|
|
100
|
+
// 有效的行展开配置:rowExpansion 优先,兼容旧的 indicatorColumn.buildInlineComponent
|
|
101
|
+
let expansion = $derived.by<RowExpansion<T> | null>(() => {
|
|
102
|
+
if (rowExpansion) return rowExpansion;
|
|
103
|
+
const legacy = indicatorColumn?.buildInlineComponent;
|
|
104
|
+
return legacy ? { build: legacy } : null;
|
|
105
|
+
});
|
|
106
|
+
let expandable = $derived(expansion != null);
|
|
107
|
+
// 指示列图标:默认显示;需要有 indicatorColumn 才有地方放
|
|
108
|
+
let showExpandToggle = $derived(expandable && indicatorColumn != null && expansion?.showToggle !== false);
|
|
109
|
+
let expandTrigger = $derived(expansion?.trigger ?? null);
|
|
110
|
+
|
|
85
111
|
// Measured by ContentPanel (its scrollable data area); drives the virtual window.
|
|
86
112
|
let viewportHeight = $state(0);
|
|
87
|
-
let expandIndex = $derived(
|
|
113
|
+
let expandIndex = $derived(expandedRow ? rows.indexOf(expandedRow) : -1);
|
|
88
114
|
// One shared window for all three panels (frozen / content / actions), so the
|
|
89
115
|
// slices they render always cover the same absolute row indices.
|
|
90
116
|
let vwindow = $derived(virtual
|
|
@@ -163,7 +189,7 @@
|
|
|
163
189
|
|
|
164
190
|
const sortRows = (compareFun: CompareFn, descending: boolean) => {
|
|
165
191
|
if (!table) return;
|
|
166
|
-
|
|
192
|
+
closeExpanded();
|
|
167
193
|
let oList = list.sort(compareFun);
|
|
168
194
|
table.data = descending ? oList.reverse() : oList;
|
|
169
195
|
rows = table.rows;
|
|
@@ -183,15 +209,125 @@
|
|
|
183
209
|
}
|
|
184
210
|
};
|
|
185
211
|
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
212
|
+
const isExpandableRow = (row: TableRow): boolean =>
|
|
213
|
+
expandable && (expansion?.isExpandable?.(row.data) ?? true);
|
|
214
|
+
|
|
215
|
+
const closeExpanded = () => {
|
|
216
|
+
if (expandedRow == null) return;
|
|
217
|
+
expandedRow = null;
|
|
218
|
+
inlineComponent = null;
|
|
219
|
+
expansion?.onExpandChange?.(null);
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
// 异步 build 期间用户可能又点了别的行:只接受最后一次请求的结果
|
|
223
|
+
let expandSeq = 0;
|
|
224
|
+
const openRow = async (row: TableRow) => {
|
|
225
|
+
if (!isExpandableRow(row)) return;
|
|
226
|
+
const seq = ++expandSeq;
|
|
227
|
+
const comp = await expansion!.build(row.data);
|
|
228
|
+
if (seq !== expandSeq) return;
|
|
229
|
+
inlineComponent = comp;
|
|
230
|
+
expandedRow = row;
|
|
231
|
+
expansion?.onExpandChange?.(row.data);
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
/** 切换某行展开状态;expanded 指定目标状态,不传则切换 */
|
|
235
|
+
const setRowExpanded = async (row: TableRow, expanded?: boolean) => {
|
|
236
|
+
const isOpen = row === expandedRow;
|
|
237
|
+
const target = expanded ?? !isOpen;
|
|
238
|
+
if (target === isOpen) return;
|
|
239
|
+
if (target) {
|
|
240
|
+
await openRow(row);
|
|
190
241
|
} else {
|
|
191
|
-
|
|
192
|
-
|
|
242
|
+
expandSeq++;
|
|
243
|
+
closeExpanded();
|
|
193
244
|
}
|
|
194
245
|
};
|
|
246
|
+
|
|
247
|
+
const onRowExpand = (row: TableRow) => setRowExpanded(row);
|
|
248
|
+
|
|
249
|
+
// 对外方法(bind:this 后调用)用它定位行:按数据对象同一性匹配
|
|
250
|
+
const findRow = (data: T): TableRow | undefined => rows.find(r => r.data === data);
|
|
251
|
+
|
|
252
|
+
/** 展开指定行(按行数据对象匹配);行不存在或不允许展开时不做任何事 */
|
|
253
|
+
export async function expandRow(data: T): Promise<void> {
|
|
254
|
+
const row = findRow(data);
|
|
255
|
+
if (row) await setRowExpanded(row, true);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/** 收起指定行;不传参数则收起当前展开的行 */
|
|
259
|
+
export function collapseRow(data?: T): void {
|
|
260
|
+
if (data === undefined) {
|
|
261
|
+
expandSeq++;
|
|
262
|
+
closeExpanded();
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
const row = findRow(data);
|
|
266
|
+
if (row) void setRowExpanded(row, false);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/** 切换指定行的展开状态 */
|
|
270
|
+
export async function toggleRowExpand(data: T): Promise<void> {
|
|
271
|
+
const row = findRow(data);
|
|
272
|
+
if (row) await setRowExpanded(row);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/** 某行当前是否展开 */
|
|
276
|
+
export function isRowExpanded(data: T): boolean {
|
|
277
|
+
return expandedRow != null && expandedRow.data === data;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/** 当前展开行的数据,没有则为 null */
|
|
281
|
+
export function getExpandedRow(): T | null {
|
|
282
|
+
return expandedRow?.data ?? null;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// ---- 行级触发(整行单击 / 双击) ----
|
|
286
|
+
|
|
287
|
+
const handleRowClick = (row: TableRow, e: MouseEvent) => {
|
|
288
|
+
if (expandTrigger !== 'click' || isInteractiveTarget(e)) return;
|
|
289
|
+
void setRowExpanded(row);
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
const handleRowDblClick = (row: TableRow, e: MouseEvent) => {
|
|
293
|
+
if (expandTrigger !== 'dblclick' || isInteractiveTarget(e)) return;
|
|
294
|
+
e.preventDefault();
|
|
295
|
+
void setRowExpanded(row);
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
// ---- 行内自定义事件:render cell / ActionCell / 操作列 / 展开区内部都可以派发 ----
|
|
299
|
+
|
|
300
|
+
const handleExpandRequest = (row: TableRow, e: Event) => {
|
|
301
|
+
if (!expandable) return;
|
|
302
|
+
e.stopPropagation();
|
|
303
|
+
const detail = (e as CustomEvent<RowExpandRequest>).detail;
|
|
304
|
+
void setRowExpanded(row, detail?.expanded);
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
// 传给展开区组件的上下文
|
|
308
|
+
let expandContext = $derived.by<RowExpandContext<T> | null>(() => {
|
|
309
|
+
const row = expandedRow;
|
|
310
|
+
if (!row) return null;
|
|
311
|
+
const data = row.data as T;
|
|
312
|
+
const actions = actionsColumn?.getActions(data) ?? [];
|
|
313
|
+
return {
|
|
314
|
+
data,
|
|
315
|
+
collapse: () => { void setRowExpanded(row, false); },
|
|
316
|
+
actions,
|
|
317
|
+
invoke: async (label: string) => {
|
|
318
|
+
const action = actions.find(a => a.label === label);
|
|
319
|
+
if (!action || action.disabled) return false;
|
|
320
|
+
await action.handler(data);
|
|
321
|
+
return true;
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
const rowEvents: RowEventHandlers<TableRow> = {
|
|
327
|
+
onRowClick: handleRowClick,
|
|
328
|
+
onRowDblClick: handleRowDblClick,
|
|
329
|
+
onExpandRequest: handleExpandRequest,
|
|
330
|
+
};
|
|
195
331
|
</script>
|
|
196
332
|
|
|
197
333
|
<div id="tab-{id}" {style} class="topside-data-table" class:cell-border={colBorder} class:row-border={rowBorder}>
|
|
@@ -204,7 +340,10 @@
|
|
|
204
340
|
{orderColumn}
|
|
205
341
|
{onCellClick}
|
|
206
342
|
{indicatorColumn}
|
|
207
|
-
|
|
343
|
+
expandRow={expandedRow}
|
|
344
|
+
expandable={showExpandToggle}
|
|
345
|
+
isExpandable={isExpandableRow}
|
|
346
|
+
{rowEvents}
|
|
208
347
|
{rowHeight}
|
|
209
348
|
{headerHeight}
|
|
210
349
|
{fixedCols}
|
|
@@ -227,7 +366,9 @@
|
|
|
227
366
|
{tabWidth}
|
|
228
367
|
bind:rows
|
|
229
368
|
bind:scrollTop
|
|
230
|
-
{
|
|
369
|
+
expandRow={expandedRow}
|
|
370
|
+
{expandContext}
|
|
371
|
+
{rowEvents}
|
|
231
372
|
{rowHeight}
|
|
232
373
|
{headerHeight}
|
|
233
374
|
{list}
|
|
@@ -245,6 +386,6 @@
|
|
|
245
386
|
{/if}
|
|
246
387
|
|
|
247
388
|
{#if actionsColumn}
|
|
248
|
-
<ActionPanel {
|
|
389
|
+
<ActionPanel expandRow={expandedRow} {rowEvents} {rowHeight} {headerHeight} {actionsColumn} {rows} {inlineRowHeight} bind:scrollTop {vwindow} showSummary={tableSummary != null} />
|
|
249
390
|
{/if}
|
|
250
391
|
</div>
|
|
@@ -2,6 +2,7 @@ import type DataColumn from "./lib/DataColumn";
|
|
|
2
2
|
import type ActionsColumn from "./lib/ActionsColumn";
|
|
3
3
|
import type IndicatorColumn from "./lib/IndicatorColumn";
|
|
4
4
|
import type { GetRowFontStyle } from "../types";
|
|
5
|
+
import type RowExpansion from "./lib/RowExpansion";
|
|
5
6
|
declare function $$render<T = any>(): {
|
|
6
7
|
props: {
|
|
7
8
|
columns: Array<DataColumn>;
|
|
@@ -32,8 +33,21 @@ declare function $$render<T = any>(): {
|
|
|
32
33
|
* 分页场景下用它显示服务端算好的全量合计(数字串会按数字格式化)。
|
|
33
34
|
*/
|
|
34
35
|
summaryValues?: Record<string, any> | null;
|
|
36
|
+
/**
|
|
37
|
+
* 行展开配置:展开区组件、是否显示指示列图标(默认显示)、整行单击 / 双击触发等。
|
|
38
|
+
* 不配置图标时可以用组件方法 expandRow / collapseRow / toggleRowExpand,
|
|
39
|
+
* 或在行内(自定义单元格、操作列、展开区)派发 ROW_EXPAND_EVENT 事件来控制。
|
|
40
|
+
* 同一时间只展开一行。
|
|
41
|
+
*/
|
|
42
|
+
rowExpansion?: RowExpansion<T> | null;
|
|
43
|
+
};
|
|
44
|
+
exports: {
|
|
45
|
+
expandRow: (data: T) => Promise<void>;
|
|
46
|
+
collapseRow: (data?: T) => void;
|
|
47
|
+
toggleRowExpand: (data: T) => Promise<void>;
|
|
48
|
+
isRowExpanded: (data: T) => boolean;
|
|
49
|
+
getExpandedRow: () => T | null;
|
|
35
50
|
};
|
|
36
|
-
exports: {};
|
|
37
51
|
bindings: "inlineRowHeight" | "selectedRows";
|
|
38
52
|
slots: {};
|
|
39
53
|
events: {};
|
|
@@ -43,7 +57,13 @@ declare class __sveltets_Render<T = any> {
|
|
|
43
57
|
events(): ReturnType<typeof $$render<T>>['events'];
|
|
44
58
|
slots(): ReturnType<typeof $$render<T>>['slots'];
|
|
45
59
|
bindings(): "inlineRowHeight" | "selectedRows";
|
|
46
|
-
exports(): {
|
|
60
|
+
exports(): {
|
|
61
|
+
expandRow: (data: T) => Promise<void>;
|
|
62
|
+
collapseRow: (data?: T | undefined) => void;
|
|
63
|
+
toggleRowExpand: (data: T) => Promise<void>;
|
|
64
|
+
isRowExpanded: (data: T) => boolean;
|
|
65
|
+
getExpandedRow: () => T | null;
|
|
66
|
+
};
|
|
47
67
|
}
|
|
48
68
|
interface $$IsomorphicComponent {
|
|
49
69
|
new <T = any>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['events']>, ReturnType<__sveltets_Render<T>['slots']>> & {
|
|
@@ -55,6 +55,7 @@ import type { DataColumn, ActionsColumn, IndicatorColumn, RowAction } from "@tic
|
|
|
55
55
|
| `style` | `string` | `""` | Additional CSS styles |
|
|
56
56
|
| `summaryLabel` | `string` | i18n `dataTable.summaryLabel` ("Total") | Label of the summary row; empty string hides it |
|
|
57
57
|
| `summaryValues` | `Record<string, any> \| null` | `null` | External totals keyed by field; override local aggregation (server-side totals for paged data) |
|
|
58
|
+
| `rowExpansion` | `RowExpansion \| null` | `null` | Row expansion config (inline component, toggle icon, click / double-click trigger…), see [Row Expansion](#row-expansion) |
|
|
58
59
|
|
|
59
60
|
## Types
|
|
60
61
|
|
|
@@ -134,7 +135,28 @@ interface IndicatorColumn {
|
|
|
134
135
|
width: number; // Column width in pixels
|
|
135
136
|
selectable?: boolean; // Whether rows are selectable
|
|
136
137
|
showRowNo?: boolean; // Whether to show row numbers
|
|
137
|
-
|
|
138
|
+
/** @deprecated use the DataTable `rowExpansion` prop instead */
|
|
139
|
+
buildInlineComponent?: (data: any) => Promise<any>;
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### RowExpansion
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
interface RowExpansion<T = any> {
|
|
147
|
+
build: (data: T) => Promise<Component> | Component; // inline component builder (may be async)
|
|
148
|
+
showToggle?: boolean; // show the expand icon in the indicator column, default true (needs indicatorColumn)
|
|
149
|
+
trigger?: 'click' | 'dblclick'; // toggle by clicking / double-clicking the row; unset by default
|
|
150
|
+
isExpandable?: (data: T) => boolean; // which rows may expand
|
|
151
|
+
onExpandChange?: (data: T | null) => void; // fired on expand / collapse (null when collapsed)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Props received by the inline component: data (row data) and context
|
|
155
|
+
interface RowExpandContext<T = any> {
|
|
156
|
+
data: T;
|
|
157
|
+
collapse: () => void; // collapse the expanded row
|
|
158
|
+
actions: Array<RowAction<T>>; // the row's actions (from actionsColumn.getActions)
|
|
159
|
+
invoke: (label: string) => Promise<boolean>; // run a row action by label
|
|
138
160
|
}
|
|
139
161
|
```
|
|
140
162
|
|
|
@@ -486,60 +508,36 @@ Using the custom component in column definitions:
|
|
|
486
508
|
import OrderDetails from "./OrderDetails.svelte";
|
|
487
509
|
|
|
488
510
|
const columns = [
|
|
489
|
-
{
|
|
490
|
-
{
|
|
491
|
-
{
|
|
511
|
+
{ title: "Order ID", field: "orderId", width: 120 },
|
|
512
|
+
{ title: "Customer", field: "customer", width: 150 },
|
|
513
|
+
{ title: "Total", field: "total", width: 100, align: "right",
|
|
492
514
|
formatter: (value) => `$${value?.toFixed(2) ?? '0.00'}`
|
|
493
515
|
},
|
|
494
|
-
{
|
|
495
|
-
{ text: "Date", field: "orderDate", width: 120 }
|
|
516
|
+
{ title: "Status", field: "status", width: 120, align: "center" }
|
|
496
517
|
];
|
|
497
518
|
|
|
498
|
-
const indicatorColumn = {
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
519
|
+
const indicatorColumn = { width: 50 };
|
|
520
|
+
|
|
521
|
+
const rowExpansion = {
|
|
522
|
+
build: async (orderData) => {
|
|
523
|
+
// e.g. fetch the details first, then pick the component
|
|
524
|
+
await new Promise(resolve => setTimeout(resolve, 300));
|
|
505
525
|
return OrderDetails;
|
|
506
526
|
}
|
|
507
527
|
};
|
|
508
|
-
|
|
509
|
-
const orders = [
|
|
510
|
-
{
|
|
511
|
-
orderId: "ORD-001",
|
|
512
|
-
customer: "John Doe",
|
|
513
|
-
total: 299.99,
|
|
514
|
-
status: "Shipped",
|
|
515
|
-
orderDate: "2024-01-15",
|
|
516
|
-
items: [
|
|
517
|
-
{ product: "Laptop", quantity: 1, price: 299.99 }
|
|
518
|
-
]
|
|
519
|
-
},
|
|
520
|
-
{
|
|
521
|
-
orderId: "ORD-002",
|
|
522
|
-
customer: "Jane Smith",
|
|
523
|
-
total: 149.98,
|
|
524
|
-
status: "Processing",
|
|
525
|
-
orderDate: "2024-01-14",
|
|
526
|
-
items: [
|
|
527
|
-
{ product: "Mouse", quantity: 2, price: 24.99 },
|
|
528
|
-
{ product: "Keyboard", quantity: 1, price: 99.99 }
|
|
529
|
-
]
|
|
530
|
-
}
|
|
531
|
-
];
|
|
532
528
|
</script>
|
|
533
529
|
|
|
534
530
|
<DataTable
|
|
535
531
|
{columns}
|
|
536
532
|
list={orders}
|
|
537
533
|
{indicatorColumn}
|
|
538
|
-
|
|
534
|
+
{rowExpansion}
|
|
539
535
|
emptyText="No orders found"
|
|
540
536
|
/>
|
|
541
537
|
```
|
|
542
538
|
|
|
539
|
+
See [Row Expansion](#row-expansion) for other triggers and for closing / running row actions from inside the expanded panel.
|
|
540
|
+
|
|
543
541
|
### Table with Frozen Columns
|
|
544
542
|
|
|
545
543
|
```svelte
|
|
@@ -837,6 +835,89 @@ Set `summary` on any column (or pass `summaryValues` for a column's field) and a
|
|
|
837
835
|
- **Paging**: pass server totals via `summaryValues` (keyed by field); they win over local aggregation and work on columns without `summary`.
|
|
838
836
|
- Background: CSS variable `--topside-data-table-summary-bg` (defaults to the header background).
|
|
839
837
|
|
|
838
|
+
## Row Expansion
|
|
839
|
+
|
|
840
|
+
Configured through `rowExpansion`. Only one row is expanded at a time; the inline component receives `data` (the row data) and `context` (a `RowExpandContext`).
|
|
841
|
+
|
|
842
|
+
### Triggers
|
|
843
|
+
|
|
844
|
+
```svelte
|
|
845
|
+
<script>
|
|
846
|
+
import DataTable from "@ticatec/topside-elements/DataTable";
|
|
847
|
+
import OrderDetails from "./OrderDetails.svelte";
|
|
848
|
+
|
|
849
|
+
let table;
|
|
850
|
+
|
|
851
|
+
const rowExpansion = {
|
|
852
|
+
build: async (order) => OrderDetails,
|
|
853
|
+
showToggle: false, // no expand icon in the indicator column (default true)
|
|
854
|
+
trigger: 'dblclick', // double-click the row to toggle; 'click' for single click (clicks on buttons / links / inputs are ignored)
|
|
855
|
+
isExpandable: (order) => order.items?.length > 0,
|
|
856
|
+
onExpandChange: (order) => console.log('expanded:', order) // null when collapsed
|
|
857
|
+
};
|
|
858
|
+
|
|
859
|
+
const actionsColumn = {
|
|
860
|
+
width: 120,
|
|
861
|
+
getActions: (order) => [
|
|
862
|
+
{ label: 'Edit', handler: () => edit(order) },
|
|
863
|
+
// an action button can drive expansion through the component methods
|
|
864
|
+
{ label: 'Details', handler: () => table.toggleRowExpand(order) }
|
|
865
|
+
]
|
|
866
|
+
};
|
|
867
|
+
</script>
|
|
868
|
+
|
|
869
|
+
<DataTable bind:this={table} {columns} {list} {rowExpansion} {actionsColumn} />
|
|
870
|
+
<button onclick={() => table.expandRow(list[0])}>Expand first row</button>
|
|
871
|
+
<button onclick={() => table.collapseRow()}>Collapse</button>
|
|
872
|
+
```
|
|
873
|
+
|
|
874
|
+
Component methods (call them on the `bind:this` instance; rows are matched by data object identity):
|
|
875
|
+
|
|
876
|
+
| Method | Description |
|
|
877
|
+
|--------|-------------|
|
|
878
|
+
| `expandRow(data)` | Expand the row (ignored when already expanded, not allowed by `isExpandable`, or not found); returns a Promise |
|
|
879
|
+
| `collapseRow(data?)` | Collapse the row; without an argument collapses whatever is expanded |
|
|
880
|
+
| `toggleRowExpand(data)` | Toggle the row; returns a Promise |
|
|
881
|
+
| `isRowExpanded(data)` | Whether the row is expanded |
|
|
882
|
+
| `getExpandedRow()` | Data of the expanded row, or `null` |
|
|
883
|
+
|
|
884
|
+
### Triggering from a custom cell
|
|
885
|
+
|
|
886
|
+
A custom `render` component or an `ActionCell` does not need the table instance: dispatch a request from any element inside the row and the DataTable resolves the row itself.
|
|
887
|
+
|
|
888
|
+
```svelte
|
|
889
|
+
<!-- OrderNoCell.svelte -->
|
|
890
|
+
<script>
|
|
891
|
+
import { requestRowToggle } from "@ticatec/topside-elements/DataTable";
|
|
892
|
+
let { data } = $props();
|
|
893
|
+
</script>
|
|
894
|
+
|
|
895
|
+
<a href="#" onclick={(e) => { e.preventDefault(); requestRowToggle(e.currentTarget); }}>{data.orderId}</a>
|
|
896
|
+
```
|
|
897
|
+
|
|
898
|
+
Helpers: `requestRowExpand(el)`, `requestRowCollapse(el)`, `requestRowToggle(el)`. You can also dispatch `ROW_EXPAND_EVENT` (`topside-row-expand`, `detail: { expanded?: boolean }`, `bubbles: true`) yourself. Requests are ignored when no `rowExpansion` is configured.
|
|
899
|
+
|
|
900
|
+
### Closing / running row actions from the expanded panel
|
|
901
|
+
|
|
902
|
+
```svelte
|
|
903
|
+
<!-- OrderDetails.svelte -->
|
|
904
|
+
<script>
|
|
905
|
+
let { data, context } = $props();
|
|
906
|
+
</script>
|
|
907
|
+
|
|
908
|
+
<div>
|
|
909
|
+
{#each context.actions as action}
|
|
910
|
+
<button disabled={action.disabled} onclick={() => action.handler(data)}>{action.label}</button>
|
|
911
|
+
{/each}
|
|
912
|
+
<button onclick={() => context.invoke('Edit')}>Edit</button>
|
|
913
|
+
<button onclick={() => context.collapse()}>Close</button>
|
|
914
|
+
</div>
|
|
915
|
+
```
|
|
916
|
+
|
|
917
|
+
`context.actions` is the result of `actionsColumn.getActions(data)` (an empty array without an `actionsColumn`); deeply nested children can also call `requestRowCollapse(el)` to close their row.
|
|
918
|
+
|
|
919
|
+
> Compatibility: the legacy `indicatorColumn.buildInlineComponent` still works and is equivalent to `rowExpansion = { build }`.
|
|
920
|
+
|
|
840
921
|
## Styling
|
|
841
922
|
|
|
842
923
|
The DataTable component supports extensive styling customization through CSS custom properties and classes:
|
|
@@ -55,6 +55,7 @@ import type { DataColumn, ActionsColumn, IndicatorColumn, RowAction } from "@tic
|
|
|
55
55
|
| `style` | `string` | `""` | 附加 CSS 样式 |
|
|
56
56
|
| `summaryLabel` | `string` | i18n `dataTable.summaryLabel`(合计) | 合计行标签,传空串不显示 |
|
|
57
57
|
| `summaryValues` | `Record<string, any> \| null` | `null` | 外部合计值(按 field),优先于本地计算,用于分页时显示服务端全量合计 |
|
|
58
|
+
| `rowExpansion` | `RowExpansion \| null` | `null` | 行展开配置(展开区组件、是否显示图标、单击 / 双击触发等),见[行展开](#行展开) |
|
|
58
59
|
|
|
59
60
|
## 类型
|
|
60
61
|
|
|
@@ -134,7 +135,28 @@ interface IndicatorColumn {
|
|
|
134
135
|
width: number; // 列宽(像素)
|
|
135
136
|
selectable?: boolean; // 行是否可选择
|
|
136
137
|
showRowNo?: boolean; // 是否显示行号
|
|
137
|
-
|
|
138
|
+
/** @deprecated 请改用 DataTable 的 rowExpansion 属性 */
|
|
139
|
+
buildInlineComponent?: (data: any) => Promise<any>;
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### RowExpansion
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
interface RowExpansion<T = any> {
|
|
147
|
+
build: (data: T) => Promise<Component> | Component; // 展开区组件生成器(可异步)
|
|
148
|
+
showToggle?: boolean; // 指示列是否显示展开图标,默认 true(需配置 indicatorColumn)
|
|
149
|
+
trigger?: 'click' | 'dblclick'; // 整行单击 / 双击切换展开,默认不设置
|
|
150
|
+
isExpandable?: (data: T) => boolean; // 哪些行允许展开
|
|
151
|
+
onExpandChange?: (data: T | null) => void; // 展开 / 收起回调
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// 展开区组件收到的属性:data(行数据)与 context
|
|
155
|
+
interface RowExpandContext<T = any> {
|
|
156
|
+
data: T;
|
|
157
|
+
collapse: () => void; // 关闭当前展开行
|
|
158
|
+
actions: Array<RowAction<T>>; // 本行的行动作(来自 actionsColumn.getActions)
|
|
159
|
+
invoke: (label: string) => Promise<boolean>; // 按 label 执行行动作
|
|
138
160
|
}
|
|
139
161
|
```
|
|
140
162
|
|
|
@@ -486,60 +508,36 @@ interface RowAction {
|
|
|
486
508
|
import OrderDetails from "./OrderDetails.svelte";
|
|
487
509
|
|
|
488
510
|
const columns = [
|
|
489
|
-
{
|
|
490
|
-
{
|
|
491
|
-
{
|
|
511
|
+
{ title: "订单ID", field: "orderId", width: 120 },
|
|
512
|
+
{ title: "客户", field: "customer", width: 150 },
|
|
513
|
+
{ title: "总计", field: "total", width: 100, align: "right",
|
|
492
514
|
formatter: (value) => `¥${value?.toFixed(2) ?? '0.00'}`
|
|
493
515
|
},
|
|
494
|
-
{
|
|
495
|
-
{ text: "日期", field: "orderDate", width: 120 }
|
|
516
|
+
{ title: "状态", field: "status", width: 120, align: "center" }
|
|
496
517
|
];
|
|
497
518
|
|
|
498
|
-
const indicatorColumn = {
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
519
|
+
const indicatorColumn = { width: 50 };
|
|
520
|
+
|
|
521
|
+
const rowExpansion = {
|
|
522
|
+
build: async (orderData) => {
|
|
523
|
+
// 可以先请求明细再决定展开区组件
|
|
524
|
+
await new Promise(resolve => setTimeout(resolve, 300));
|
|
505
525
|
return OrderDetails;
|
|
506
526
|
}
|
|
507
527
|
};
|
|
508
|
-
|
|
509
|
-
const orders = [
|
|
510
|
-
{
|
|
511
|
-
orderId: "ORD-001",
|
|
512
|
-
customer: "张伟",
|
|
513
|
-
total: 2999.99,
|
|
514
|
-
status: "已发货",
|
|
515
|
-
orderDate: "2024-01-15",
|
|
516
|
-
items: [
|
|
517
|
-
{ product: "笔记本电脑", quantity: 1, price: 2999.99 }
|
|
518
|
-
]
|
|
519
|
-
},
|
|
520
|
-
{
|
|
521
|
-
orderId: "ORD-002",
|
|
522
|
-
customer: "李娜",
|
|
523
|
-
total: 1499.98,
|
|
524
|
-
status: "处理中",
|
|
525
|
-
orderDate: "2024-01-14",
|
|
526
|
-
items: [
|
|
527
|
-
{ product: "鼠标", quantity: 2, price: 249.99 },
|
|
528
|
-
{ product: "键盘", quantity: 1, price: 999.99 }
|
|
529
|
-
]
|
|
530
|
-
}
|
|
531
|
-
];
|
|
532
528
|
</script>
|
|
533
529
|
|
|
534
530
|
<DataTable
|
|
535
531
|
{columns}
|
|
536
532
|
list={orders}
|
|
537
533
|
{indicatorColumn}
|
|
538
|
-
|
|
534
|
+
{rowExpansion}
|
|
539
535
|
emptyText="未找到订单"
|
|
540
536
|
/>
|
|
541
537
|
```
|
|
542
538
|
|
|
539
|
+
更多触发方式与展开区内关闭 / 调用行动作见下文[行展开](#行展开)。
|
|
540
|
+
|
|
543
541
|
### 带有冻结列的表格
|
|
544
542
|
|
|
545
543
|
```svelte
|
|
@@ -862,6 +860,89 @@ const columns: DataColumn[] = [
|
|
|
862
860
|
|
|
863
861
|
合计行背景色可用 CSS 变量 `--topside-data-table-summary-bg` 覆盖(默认同表头)。
|
|
864
862
|
|
|
863
|
+
## 行展开
|
|
864
|
+
|
|
865
|
+
通过 `rowExpansion` 配置。同一时间只展开一行;展开区组件收到 `data`(行数据)与 `context`(`RowExpandContext`)两个属性。
|
|
866
|
+
|
|
867
|
+
### 触发方式
|
|
868
|
+
|
|
869
|
+
```svelte
|
|
870
|
+
<script>
|
|
871
|
+
import DataTable from "@ticatec/topside-elements/DataTable";
|
|
872
|
+
import OrderDetails from "./OrderDetails.svelte";
|
|
873
|
+
|
|
874
|
+
let table;
|
|
875
|
+
|
|
876
|
+
const rowExpansion = {
|
|
877
|
+
build: async (order) => OrderDetails,
|
|
878
|
+
showToggle: false, // 不在指示列显示展开图标(默认 true)
|
|
879
|
+
trigger: 'dblclick', // 双击行切换;'click' 为单击(点到按钮 / 链接 / 输入框不会触发)
|
|
880
|
+
isExpandable: (order) => order.items?.length > 0,
|
|
881
|
+
onExpandChange: (order) => console.log('expanded:', order) // 收起时为 null
|
|
882
|
+
};
|
|
883
|
+
|
|
884
|
+
const actionsColumn = {
|
|
885
|
+
width: 120,
|
|
886
|
+
getActions: (order) => [
|
|
887
|
+
{ label: '编辑', handler: () => edit(order) },
|
|
888
|
+
// 操作列按钮通过组件方法控制展开
|
|
889
|
+
{ label: '明细', handler: () => table.toggleRowExpand(order) }
|
|
890
|
+
]
|
|
891
|
+
};
|
|
892
|
+
</script>
|
|
893
|
+
|
|
894
|
+
<DataTable bind:this={table} {columns} {list} {rowExpansion} {actionsColumn} />
|
|
895
|
+
<button onclick={() => table.expandRow(list[0])}>展开第一行</button>
|
|
896
|
+
<button onclick={() => table.collapseRow()}>收起</button>
|
|
897
|
+
```
|
|
898
|
+
|
|
899
|
+
组件方法(`bind:this` 后调用,按行数据对象匹配):
|
|
900
|
+
|
|
901
|
+
| 方法 | 说明 |
|
|
902
|
+
|------|------|
|
|
903
|
+
| `expandRow(data)` | 展开指定行(已展开 / 不允许展开 / 找不到时忽略),返回 Promise |
|
|
904
|
+
| `collapseRow(data?)` | 收起指定行;不传参数收起当前展开行 |
|
|
905
|
+
| `toggleRowExpand(data)` | 切换指定行,返回 Promise |
|
|
906
|
+
| `isRowExpanded(data)` | 该行是否展开 |
|
|
907
|
+
| `getExpandedRow()` | 当前展开行的数据,没有为 `null` |
|
|
908
|
+
|
|
909
|
+
### 在自定义单元格里触发
|
|
910
|
+
|
|
911
|
+
自定义 `render` 组件或 `ActionCell` 不需要拿到表格实例:在行内任意元素上派发事件即可,DataTable 会定位到所在行。
|
|
912
|
+
|
|
913
|
+
```svelte
|
|
914
|
+
<!-- OrderNoCell.svelte -->
|
|
915
|
+
<script>
|
|
916
|
+
import { requestRowToggle } from "@ticatec/topside-elements/DataTable";
|
|
917
|
+
let { data } = $props();
|
|
918
|
+
</script>
|
|
919
|
+
|
|
920
|
+
<a href="#" onclick={(e) => { e.preventDefault(); requestRowToggle(e.currentTarget); }}>{data.orderId}</a>
|
|
921
|
+
```
|
|
922
|
+
|
|
923
|
+
可用的助手:`requestRowExpand(el)`、`requestRowCollapse(el)`、`requestRowToggle(el)`;也可以自己派发 `ROW_EXPAND_EVENT`(`topside-row-expand`,`detail: { expanded?: boolean }`,需要 `bubbles: true`)。没有配置 `rowExpansion` 时这些请求被忽略。
|
|
924
|
+
|
|
925
|
+
### 展开区内关闭 / 调用行动作
|
|
926
|
+
|
|
927
|
+
```svelte
|
|
928
|
+
<!-- OrderDetails.svelte -->
|
|
929
|
+
<script>
|
|
930
|
+
let { data, context } = $props();
|
|
931
|
+
</script>
|
|
932
|
+
|
|
933
|
+
<div>
|
|
934
|
+
{#each context.actions as action}
|
|
935
|
+
<button disabled={action.disabled} onclick={() => action.handler(data)}>{action.label}</button>
|
|
936
|
+
{/each}
|
|
937
|
+
<button onclick={() => context.invoke('编辑')}>编辑</button>
|
|
938
|
+
<button onclick={() => context.collapse()}>关闭</button>
|
|
939
|
+
</div>
|
|
940
|
+
```
|
|
941
|
+
|
|
942
|
+
`context.actions` 即 `actionsColumn.getActions(data)` 的结果(没有 `actionsColumn` 时为空数组);嵌套很深的子组件也可以用 `requestRowCollapse(el)` 关闭所在行。
|
|
943
|
+
|
|
944
|
+
> 兼容:旧写法 `indicatorColumn.buildInlineComponent` 仍然有效,等价于 `rowExpansion = { build }`。
|
|
945
|
+
|
|
865
946
|
## 样式
|
|
866
947
|
|
|
867
948
|
DataTable 组件通过 CSS 自定义属性和类支持广泛的样式定制:
|