@fluixi-ui/table 0.2.0-alpha.17 → 0.2.0-alpha.19

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/table.js CHANGED
@@ -1,171 +1,276 @@
1
- import { delegateEvents, splitProps, createComponent, createNativeElement, spread, mergeProps, insert } from "@fluixi/dom";
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
- const parts = [];
9
- if (width != null) parts.push(`width:${width}px`);
10
- const pinned = column.getIsPinned?.();
11
- if (pinned) {
12
- parts.push("position:sticky", "z-index:1");
13
- parts.push(pinned === "left" ? `left:${column.getStart?.() ?? 0}px` : `right:${column.getAfter?.() ?? 0}px`);
14
- }
15
- return parts.length ? parts.join(";") : void 0;
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
- const userStr = typeof user === "string" ? user : void 0;
19
- return [own, userStr].filter(Boolean).join(";");
22
+ return [own, typeof user === "string" ? user : void 0].filter(Boolean).join(";");
20
23
  }
21
- const [TableProvider, useTableContext] = createSafeContext("Table");
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
- if (template == null) return null;
24
- return typeof template === "function" ? template(context) : template;
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
- const [local, others] = splitProps(props, ["table", "children", "fx", "style"]);
28
- return createMemo(() => createComponent(TableProvider, { get value() {
29
- return { table: local.table };
30
- }, get children() {
31
- return (() => {
32
- const _el$ = createNativeElement("table");
33
- spread({ element: _el$, props: mergeProps(fxProps(
34
- () => local.fx,
35
- () => local.style
36
- ), others, { "data-table": "" }) });
37
- insert(_el$, local.children);
38
- return _el$;
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
- const [local, others] = splitProps(props, ["children", "fx", "style"]);
44
- return (() => {
45
- const _el$ = createNativeElement("thead");
46
- spread({ element: _el$, props: mergeProps(fxProps(
47
- () => local.fx,
48
- () => local.style
49
- ), others, { "data-table-header": "" }) });
50
- insert(_el$, local.children);
51
- return _el$;
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
- const [local, others] = splitProps(props, ["children", "fx", "style"]);
56
- return (() => {
57
- const _el$ = createNativeElement("tbody");
58
- spread({ element: _el$, props: mergeProps(fxProps(
59
- () => local.fx,
60
- () => local.style
61
- ), others, { "data-table-body": "" }) });
62
- insert(_el$, local.children);
63
- return _el$;
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
- const [local, others] = splitProps(props, ["children", "fx", "style"]);
68
- return (() => {
69
- const _el$ = createNativeElement("tfoot");
70
- spread({ element: _el$, props: mergeProps(fxProps(
71
- () => local.fx,
72
- () => local.style
73
- ), others, { "data-table-footer": "" }) });
74
- insert(_el$, local.children);
75
- return _el$;
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
- const [local, others] = splitProps(props, ["selected", "children", "fx", "style"]);
80
- return (() => {
81
- const _el$ = createNativeElement("tr");
82
- spread({ element: _el$, props: mergeProps(fxProps(
83
- () => local.fx,
84
- () => local.style
85
- ), fxProps(
86
- () => local.fx,
87
- () => local.style
88
- ), others, { "data-table-row": "", "data-selected": () => local.selected ? "" : void 0, "aria-selected": () => local.selected == null ? void 0 : local.selected ? "true" : "false" }) });
89
- insert(_el$, local.children);
90
- return _el$;
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
- const [local, others] = splitProps(props, ["header", "children", "style", "fx"]);
95
- const header = local.header;
96
- const column = header.column;
97
- const sorted = () => column.getCanSort?.() && column.getIsSorted?.();
98
- const resizeRef = (node) => {
99
- const handler = header.getResizeHandler?.();
100
- if (!handler) return;
101
- const onDown = (event) => handler(event);
102
- node.addEventListener("pointerdown", onDown);
103
- onCleanup(() => node.removeEventListener("pointerdown", onDown));
104
- };
105
- return (() => {
106
- const _el$ = createNativeElement("th");
107
- spread({ element: _el$, props: mergeProps(fxProps(
108
- () => local.fx,
109
- // The column's own sizing and pinning, merged last like any raw style.
110
- () => mergeStyle(columnStyle(column, header.getSize?.()), local.style)
111
- ), others, { "data-table-head": "", "data-sortable": () => column.getCanSort?.() ? "" : void 0, "data-sorted": () => sorted() || void 0, "data-pinned": () => column.getIsPinned?.() || void 0, colSpan: () => header.colSpan > 1 ? header.colSpan : void 0, "aria-sort": () => {
112
- const s = sorted();
113
- if (s === "asc") return "ascending";
114
- if (s === "desc") return "descending";
115
- return column.getCanSort?.() ? "none" : void 0;
116
- } }) });
117
- insert(_el$, () => local.children != null ? local.children : header.isPlaceholder ? null : column.getCanSort?.() ? (() => {
118
- const _el$2 = createNativeElement("button");
119
- spread({ element: _el$2, props: { type: "button", "data-table-sort-button": "", onClick: column.getToggleSortingHandler?.() } });
120
- insert(_el$2, () => flexRender(column.columnDef.header, header.getContext()), null);
121
- insert(_el$2, (() => {
122
- const _el$3 = createNativeElement("span");
123
- spread({ element: _el$3, props: { "data-table-sort-indicator": "", "data-direction": () => sorted() || "none", "aria-hidden": "true" } });
124
- return _el$3;
125
- })());
126
- return _el$2;
127
- })() : flexRender(column.columnDef.header, header.getContext()), null);
128
- insert(_el$, createMemo(() => createComponent(Show, { get when() {
129
- return !header.isPlaceholder && (column.getCanResize?.() ?? false);
130
- }, get children() {
131
- return (() => {
132
- const _el$2 = createNativeElement("div");
133
- spread({ element: _el$2, props: { ref: resizeRef, "data-table-resize-handle": "", "data-resizing": () => column.getIsResizing?.() || void 0, "aria-hidden": "true" } });
134
- return _el$2;
135
- })();
136
- } })), null);
137
- return _el$;
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
- const [local, others] = splitProps(props, ["cell", "children", "style", "fx", "style"]);
142
- const renderCell = () => {
143
- const cell = local.cell;
144
- if (!cell) return local.children;
145
- const template = cell.column.columnDef.cell;
146
- return template != null ? flexRender(template, cell.getContext()) : cell.renderValue();
147
- };
148
- return (() => {
149
- const _el$ = createNativeElement("td");
150
- spread({ element: _el$, props: mergeProps(fxProps(
151
- () => local.fx,
152
- () => mergeStyle(
153
- local.cell ? columnStyle(local.cell.column, local.cell.column.getSize?.()) : void 0,
154
- local.style
155
- )
156
- ), others, { "data-table-cell": "", "data-pinned": () => local.cell?.column.getIsPinned?.() || void 0 }) });
157
- insert(_el$, renderCell());
158
- return _el$;
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
- export {
162
- Table,
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 };