@fluixi-ui/table 0.2.0-alpha.18 → 0.2.0-alpha.20
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/dist/index.js +3 -15
- package/dist/table-menu.js +266 -147
- package/dist/table.js +251 -146
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/package.json +5 -5
package/dist/table.js
CHANGED
|
@@ -1,171 +1,276 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createComponent, createNativeElement, delegateEvents, insert, mergeProps, splitProps, spread } from "@fluixi/dom";
|
|
2
2
|
import { createMemo, onCleanup } from "@fluixi/reactive/signal";
|
|
3
3
|
import { fxProps } from "@fluixi-ui/fx";
|
|
4
4
|
import { Show } from "@fluixi/core";
|
|
5
5
|
import { createSafeContext } from "@fluixi-ui/kit";
|
|
6
|
+
//#region src/table.tsx
|
|
6
7
|
delegateEvents(["click"]);
|
|
8
|
+
/** Inline style for a leaf column's `<th>`/`<td>`: explicit width (sizing
|
|
9
|
+
* feature) and sticky positioning with a left/right offset (pinning feature).
|
|
10
|
+
* Both features are optional — the style degrades to nothing without them. */
|
|
7
11
|
function columnStyle(column, width) {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
const parts = [];
|
|
13
|
+
if (width != null) parts.push(`width:${width}px`);
|
|
14
|
+
const pinned = column.getIsPinned?.();
|
|
15
|
+
if (pinned) {
|
|
16
|
+
parts.push("position:sticky", "z-index:1");
|
|
17
|
+
parts.push(pinned === "left" ? `left:${column.getStart?.() ?? 0}px` : `right:${column.getAfter?.() ?? 0}px`);
|
|
18
|
+
}
|
|
19
|
+
return parts.length ? parts.join(";") : void 0;
|
|
16
20
|
}
|
|
17
21
|
function mergeStyle(own, user) {
|
|
18
|
-
|
|
19
|
-
return [own, userStr].filter(Boolean).join(";");
|
|
22
|
+
return [own, typeof user === "string" ? user : void 0].filter(Boolean).join(";");
|
|
20
23
|
}
|
|
21
|
-
|
|
24
|
+
var [TableProvider, useTableContext] = createSafeContext("Table");
|
|
25
|
+
/** Resolve a header/cell/footer template: a string renders as-is, a function is
|
|
26
|
+
* called with its render context, anything nullish renders nothing. The escape
|
|
27
|
+
* hatch that lets column defs carry component-agnostic templates. */
|
|
22
28
|
function flexRender(template, context) {
|
|
23
|
-
|
|
24
|
-
|
|
29
|
+
if (template == null) return null;
|
|
30
|
+
return typeof template === "function" ? template(context) : template;
|
|
25
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Root of a data table. Provides the {@link TableInstance} to the structural
|
|
34
|
+
* parts and renders a semantic `<table>`. Compose the header/body parts inside,
|
|
35
|
+
* mapping `table.getHeaderGroups()` and `table.getRowModel().rows`.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```tsx
|
|
39
|
+
* const table = createTable({ data, columns, _features: [SortingFeature] });
|
|
40
|
+
*
|
|
41
|
+
* <Table table={table}>
|
|
42
|
+
* <TableHeader>
|
|
43
|
+
* <For each={table.getHeaderGroups()}>{(group) => (
|
|
44
|
+
* <TableRow>
|
|
45
|
+
* <For each={group.headers}>{(header) => <TableHead header={header} />}</For>
|
|
46
|
+
* </TableRow>
|
|
47
|
+
* )}</For>
|
|
48
|
+
* </TableHeader>
|
|
49
|
+
* <TableBody>
|
|
50
|
+
* <For each={table.getRowModel().rows}>{(row) => (
|
|
51
|
+
* <TableRow>
|
|
52
|
+
* <For each={row.getVisibleCells()}>{(cell) => <TableCell cell={cell} />}</For>
|
|
53
|
+
* </TableRow>
|
|
54
|
+
* )}</For>
|
|
55
|
+
* </TableBody>
|
|
56
|
+
* </Table>
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
26
59
|
function Table(props) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
60
|
+
const [local, others] = splitProps(props, [
|
|
61
|
+
"table",
|
|
62
|
+
"children",
|
|
63
|
+
"fx",
|
|
64
|
+
"style"
|
|
65
|
+
]);
|
|
66
|
+
return createMemo(() => createComponent(TableProvider, {
|
|
67
|
+
get value() {
|
|
68
|
+
return { table: local.table };
|
|
69
|
+
},
|
|
70
|
+
get children() {
|
|
71
|
+
return (() => {
|
|
72
|
+
const _el$ = createNativeElement("table");
|
|
73
|
+
spread({
|
|
74
|
+
element: _el$,
|
|
75
|
+
props: mergeProps(fxProps(() => local.fx, () => local.style), others, { "data-table": "" })
|
|
76
|
+
});
|
|
77
|
+
insert(_el$, local.children, null);
|
|
78
|
+
return _el$;
|
|
79
|
+
})();
|
|
80
|
+
}
|
|
81
|
+
}));
|
|
41
82
|
}
|
|
83
|
+
/** The `<thead>` region; map `table.getHeaderGroups()` into {@link TableRow}s. */
|
|
42
84
|
function TableHeader(props) {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
85
|
+
const [local, others] = splitProps(props, [
|
|
86
|
+
"children",
|
|
87
|
+
"fx",
|
|
88
|
+
"style"
|
|
89
|
+
]);
|
|
90
|
+
return (() => {
|
|
91
|
+
const _el$ = createNativeElement("thead");
|
|
92
|
+
spread({
|
|
93
|
+
element: _el$,
|
|
94
|
+
props: mergeProps(fxProps(() => local.fx, () => local.style), others, { "data-table-header": "" })
|
|
95
|
+
});
|
|
96
|
+
insert(_el$, local.children, null);
|
|
97
|
+
return _el$;
|
|
98
|
+
})();
|
|
53
99
|
}
|
|
100
|
+
/** The `<tbody>` region; map `table.getRowModel().rows` into {@link TableRow}s. */
|
|
54
101
|
function TableBody(props) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
102
|
+
const [local, others] = splitProps(props, [
|
|
103
|
+
"children",
|
|
104
|
+
"fx",
|
|
105
|
+
"style"
|
|
106
|
+
]);
|
|
107
|
+
return (() => {
|
|
108
|
+
const _el$ = createNativeElement("tbody");
|
|
109
|
+
spread({
|
|
110
|
+
element: _el$,
|
|
111
|
+
props: mergeProps(fxProps(() => local.fx, () => local.style), others, { "data-table-body": "" })
|
|
112
|
+
});
|
|
113
|
+
insert(_el$, local.children, null);
|
|
114
|
+
return _el$;
|
|
115
|
+
})();
|
|
65
116
|
}
|
|
117
|
+
/** The `<tfoot>` region; map `table.getFooterGroups()` into {@link TableRow}s. */
|
|
66
118
|
function TableFooter(props) {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
119
|
+
const [local, others] = splitProps(props, [
|
|
120
|
+
"children",
|
|
121
|
+
"fx",
|
|
122
|
+
"style"
|
|
123
|
+
]);
|
|
124
|
+
return (() => {
|
|
125
|
+
const _el$ = createNativeElement("tfoot");
|
|
126
|
+
spread({
|
|
127
|
+
element: _el$,
|
|
128
|
+
props: mergeProps(fxProps(() => local.fx, () => local.style), others, { "data-table-footer": "" })
|
|
129
|
+
});
|
|
130
|
+
insert(_el$, local.children, null);
|
|
131
|
+
return _el$;
|
|
132
|
+
})();
|
|
77
133
|
}
|
|
134
|
+
/** A `<tr>`; pass `selected` to reflect row-selection state. */
|
|
78
135
|
function TableRow(props) {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
136
|
+
const [local, others] = splitProps(props, [
|
|
137
|
+
"selected",
|
|
138
|
+
"children",
|
|
139
|
+
"fx",
|
|
140
|
+
"style"
|
|
141
|
+
]);
|
|
142
|
+
return (() => {
|
|
143
|
+
const _el$ = createNativeElement("tr");
|
|
144
|
+
spread({
|
|
145
|
+
element: _el$,
|
|
146
|
+
props: mergeProps(fxProps(() => local.fx, () => local.style), fxProps(() => local.fx, () => local.style), others, {
|
|
147
|
+
"data-table-row": "",
|
|
148
|
+
"data-selected": () => local.selected ? "" : void 0,
|
|
149
|
+
"aria-selected": () => local.selected == null ? void 0 : local.selected ? "true" : "false"
|
|
150
|
+
})
|
|
151
|
+
});
|
|
152
|
+
insert(_el$, local.children, null);
|
|
153
|
+
return _el$;
|
|
154
|
+
})();
|
|
92
155
|
}
|
|
156
|
+
/**
|
|
157
|
+
* A `<th>` for one header. Renders the column's header template; when the column
|
|
158
|
+
* is sortable, wraps it in a focusable button that toggles sorting and reflects
|
|
159
|
+
* state via `aria-sort` + `data-sorted`. Placeholder headers render empty.
|
|
160
|
+
*/
|
|
93
161
|
function TableHead(props) {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
162
|
+
const [local, others] = splitProps(props, [
|
|
163
|
+
"header",
|
|
164
|
+
"children",
|
|
165
|
+
"style",
|
|
166
|
+
"fx"
|
|
167
|
+
]);
|
|
168
|
+
const header = local.header;
|
|
169
|
+
const column = header.column;
|
|
170
|
+
const sorted = () => column.getCanSort?.() && column.getIsSorted?.();
|
|
171
|
+
const resizeRef = (node) => {
|
|
172
|
+
const handler = header.getResizeHandler?.();
|
|
173
|
+
if (!handler) return;
|
|
174
|
+
const onDown = (event) => handler(event);
|
|
175
|
+
node.addEventListener("pointerdown", onDown);
|
|
176
|
+
onCleanup(() => node.removeEventListener("pointerdown", onDown));
|
|
177
|
+
};
|
|
178
|
+
return (() => {
|
|
179
|
+
const _el$ = createNativeElement("th");
|
|
180
|
+
spread({
|
|
181
|
+
element: _el$,
|
|
182
|
+
props: mergeProps(fxProps(() => local.fx, () => mergeStyle(columnStyle(column, header.getSize?.()), local.style)), others, {
|
|
183
|
+
"data-table-head": "",
|
|
184
|
+
"data-sortable": () => column.getCanSort?.() ? "" : void 0,
|
|
185
|
+
"data-sorted": () => sorted() || void 0,
|
|
186
|
+
"data-pinned": () => column.getIsPinned?.() || void 0,
|
|
187
|
+
colSpan: () => header.colSpan > 1 ? header.colSpan : void 0,
|
|
188
|
+
"aria-sort": () => {
|
|
189
|
+
const s = sorted();
|
|
190
|
+
if (s === "asc") return "ascending";
|
|
191
|
+
if (s === "desc") return "descending";
|
|
192
|
+
return column.getCanSort?.() ? "none" : void 0;
|
|
193
|
+
}
|
|
194
|
+
})
|
|
195
|
+
});
|
|
196
|
+
insert(_el$, () => local.children != null ? local.children : header.isPlaceholder ? null : column.getCanSort?.() ? (() => {
|
|
197
|
+
const _el$ = createNativeElement("button");
|
|
198
|
+
spread({
|
|
199
|
+
element: _el$,
|
|
200
|
+
props: {
|
|
201
|
+
type: "button",
|
|
202
|
+
"data-table-sort-button": "",
|
|
203
|
+
onClick: column.getToggleSortingHandler?.()
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
insert(_el$, () => flexRender(column.columnDef.header, header.getContext()), null);
|
|
207
|
+
insert(_el$, (() => {
|
|
208
|
+
const _el$ = createNativeElement("span");
|
|
209
|
+
spread({
|
|
210
|
+
element: _el$,
|
|
211
|
+
props: {
|
|
212
|
+
"data-table-sort-indicator": "",
|
|
213
|
+
"data-direction": () => sorted() || "none",
|
|
214
|
+
"aria-hidden": "true"
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
return _el$;
|
|
218
|
+
})());
|
|
219
|
+
return _el$;
|
|
220
|
+
})() : flexRender(column.columnDef.header, header.getContext()), null);
|
|
221
|
+
insert(_el$, createMemo(() => createComponent(Show, {
|
|
222
|
+
get when() {
|
|
223
|
+
return !header.isPlaceholder && (column.getCanResize?.() ?? false);
|
|
224
|
+
},
|
|
225
|
+
get children() {
|
|
226
|
+
return (() => {
|
|
227
|
+
const _el$ = createNativeElement("div");
|
|
228
|
+
spread({
|
|
229
|
+
element: _el$,
|
|
230
|
+
props: {
|
|
231
|
+
ref: resizeRef,
|
|
232
|
+
"data-table-resize-handle": "",
|
|
233
|
+
"data-resizing": () => column.getIsResizing?.() || void 0,
|
|
234
|
+
"aria-hidden": "true"
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
return _el$;
|
|
238
|
+
})();
|
|
239
|
+
}
|
|
240
|
+
})), null);
|
|
241
|
+
return _el$;
|
|
242
|
+
})();
|
|
139
243
|
}
|
|
244
|
+
/**
|
|
245
|
+
* A `<td>`. With a `cell` model it renders the column's cell template (defaulting
|
|
246
|
+
* to the raw value); otherwise it renders `children` as a plain cell.
|
|
247
|
+
*/
|
|
140
248
|
function TableCell(props) {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
249
|
+
const [local, others] = splitProps(props, [
|
|
250
|
+
"cell",
|
|
251
|
+
"children",
|
|
252
|
+
"style",
|
|
253
|
+
"fx",
|
|
254
|
+
"style"
|
|
255
|
+
]);
|
|
256
|
+
const renderCell = () => {
|
|
257
|
+
const cell = local.cell;
|
|
258
|
+
if (!cell) return local.children;
|
|
259
|
+
const template = cell.column.columnDef.cell;
|
|
260
|
+
return template != null ? flexRender(template, cell.getContext()) : cell.renderValue();
|
|
261
|
+
};
|
|
262
|
+
return (() => {
|
|
263
|
+
const _el$ = createNativeElement("td");
|
|
264
|
+
spread({
|
|
265
|
+
element: _el$,
|
|
266
|
+
props: mergeProps(fxProps(() => local.fx, () => mergeStyle(local.cell ? columnStyle(local.cell.column, local.cell.column.getSize?.()) : void 0, local.style)), others, {
|
|
267
|
+
"data-table-cell": "",
|
|
268
|
+
"data-pinned": () => local.cell?.column.getIsPinned?.() || void 0
|
|
269
|
+
})
|
|
270
|
+
});
|
|
271
|
+
insert(_el$, renderCell(), null);
|
|
272
|
+
return _el$;
|
|
273
|
+
})();
|
|
160
274
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
TableBody,
|
|
164
|
-
TableCell,
|
|
165
|
-
TableFooter,
|
|
166
|
-
TableHead,
|
|
167
|
-
TableHeader,
|
|
168
|
-
TableRow,
|
|
169
|
-
flexRender,
|
|
170
|
-
useTableContext
|
|
171
|
-
};
|
|
275
|
+
//#endregion
|
|
276
|
+
export { Table, TableBody, TableCell, TableFooter, TableHead, TableHeader, TableRow, flexRender, useTableContext };
|