@rttui/skin-anocca 1.0.14 → 1.0.16
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 +14 -0
- package/package.json +2 -2
- package/src/RowPinButtons.tsx +40 -0
- package/src/index.tsx +81 -67
- package/src/stories/ReactTanstackTableUiStory.mdx +147 -0
- package/src/stories/ReactTanstackTableUiStory.stories.tsx +150 -132
- package/src/stories/ReactTanstackTableUiStoryComponent.tsx +103 -13
- package/src/stories/ScrollbarWidthSpecified.mdx +42 -0
- package/src/stories/createSourceCode.ts +53 -0
- package/vite.config.ts +19 -3
package/CHANGELOG.md
CHANGED
package/package.json
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
{
|
2
2
|
"name": "@rttui/skin-anocca",
|
3
|
-
"version": "1.0.
|
3
|
+
"version": "1.0.16",
|
4
4
|
"main": "./dist/cjs/index.cjs",
|
5
5
|
"dependencies": {
|
6
|
-
"@rttui/core": "^1.0.
|
6
|
+
"@rttui/core": "^1.0.15"
|
7
7
|
},
|
8
8
|
"module": "./dist/mjs/index.mjs",
|
9
9
|
"types": "./dist/types/index.d.ts",
|
@@ -0,0 +1,40 @@
|
|
1
|
+
import { Row } from "@tanstack/react-table";
|
2
|
+
import { IconButton, Stack } from "@mui/material";
|
3
|
+
import { FiX, FiChevronUp, FiChevronDown } from "react-icons/fi";
|
4
|
+
|
5
|
+
export function RowPinButtons({ row }: { row: Row<any> }) {
|
6
|
+
if (!row.getCanPin()) {
|
7
|
+
return null;
|
8
|
+
}
|
9
|
+
|
10
|
+
if (row.getIsPinned()) {
|
11
|
+
return (
|
12
|
+
<IconButton
|
13
|
+
onClick={() => row.pin(false, true, true)}
|
14
|
+
size="small"
|
15
|
+
sx={{ color: "text.secondary" }}
|
16
|
+
>
|
17
|
+
<FiX size={16} />
|
18
|
+
</IconButton>
|
19
|
+
);
|
20
|
+
}
|
21
|
+
|
22
|
+
return (
|
23
|
+
<Stack direction="row">
|
24
|
+
<IconButton
|
25
|
+
onClick={() => row.pin("top", true, true)}
|
26
|
+
size="small"
|
27
|
+
sx={{ color: "text.secondary" }}
|
28
|
+
>
|
29
|
+
<FiChevronUp size={16} />
|
30
|
+
</IconButton>
|
31
|
+
<IconButton
|
32
|
+
onClick={() => row.pin("bottom", true, true)}
|
33
|
+
size="small"
|
34
|
+
sx={{ color: "text.secondary" }}
|
35
|
+
>
|
36
|
+
<FiChevronDown size={16} />
|
37
|
+
</IconButton>
|
38
|
+
</Stack>
|
39
|
+
);
|
40
|
+
}
|
package/src/index.tsx
CHANGED
@@ -116,9 +116,9 @@ const AnoccaSkin: Skin = {
|
|
116
116
|
);
|
117
117
|
},
|
118
118
|
HeaderRow: TableHeaderRow,
|
119
|
-
HeaderCell: (props) => {
|
120
|
-
return <TableHeaderCell {...props} />;
|
121
|
-
},
|
119
|
+
HeaderCell: React.forwardRef((props, ref) => {
|
120
|
+
return <TableHeaderCell {...props} ref={ref} />;
|
121
|
+
}),
|
122
122
|
TableBody: ({ children }) => {
|
123
123
|
return (
|
124
124
|
<TableBody
|
@@ -127,6 +127,7 @@ const AnoccaSkin: Skin = {
|
|
127
127
|
sx={{
|
128
128
|
position: "relative",
|
129
129
|
width: "var(--table-width)",
|
130
|
+
height: "var(--table-height)",
|
130
131
|
display: "flex",
|
131
132
|
flexDirection: "column",
|
132
133
|
justifyContent: "flex-start",
|
@@ -300,6 +301,8 @@ const AnoccaSkin: Skin = {
|
|
300
301
|
whiteSpace: "nowrap",
|
301
302
|
zIndex: isPinned ? 5 : 0,
|
302
303
|
boxSizing: "border-box",
|
304
|
+
fontSize: "0.875rem",
|
305
|
+
color: "text.primary",
|
303
306
|
alignItems: "center",
|
304
307
|
gap: "8px",
|
305
308
|
display: "flex",
|
@@ -367,70 +370,81 @@ const AnoccaSkin: Skin = {
|
|
367
370
|
},
|
368
371
|
};
|
369
372
|
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
?
|
413
|
-
: "
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
isPinned
|
422
|
-
? (theme) =>
|
423
|
-
:
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
373
|
+
const TableHeaderCell = React.forwardRef<
|
374
|
+
HTMLDivElement,
|
375
|
+
VirtualHeader & {
|
376
|
+
type: "header" | "footer";
|
377
|
+
isLast: boolean;
|
378
|
+
isFirst: boolean;
|
379
|
+
isLastPinned: boolean;
|
380
|
+
isFirstPinned: boolean;
|
381
|
+
isLastCenter: boolean;
|
382
|
+
isFirstCenter: boolean;
|
383
|
+
isMeasuring: boolean;
|
384
|
+
}
|
385
|
+
>(
|
386
|
+
(
|
387
|
+
{
|
388
|
+
headerId,
|
389
|
+
isPinned,
|
390
|
+
width,
|
391
|
+
header,
|
392
|
+
type,
|
393
|
+
isLast,
|
394
|
+
isLastPinned,
|
395
|
+
isLastCenter,
|
396
|
+
isMeasuring,
|
397
|
+
},
|
398
|
+
ref,
|
399
|
+
) => {
|
400
|
+
const { table } = useTableContext();
|
401
|
+
return (
|
402
|
+
<TableCell
|
403
|
+
ref={ref}
|
404
|
+
component="div"
|
405
|
+
className="th"
|
406
|
+
data-header-id={headerId}
|
407
|
+
data-is-pinned={isPinned}
|
408
|
+
sx={{
|
409
|
+
transition: "background-color 0.2s ease",
|
410
|
+
whiteSpace: "nowrap",
|
411
|
+
zIndex: isPinned ? 1 : 0,
|
412
|
+
display: "flex",
|
413
|
+
overflow: "hidden",
|
414
|
+
height: "var(--header-row-height)",
|
415
|
+
width: isMeasuring ? "auto" : width,
|
416
|
+
position: "relative",
|
417
|
+
flexShrink: 0,
|
418
|
+
alignItems: "center",
|
419
|
+
gap: "8px",
|
420
|
+
justifyContent: "space-between",
|
421
|
+
padding: "6px 12px",
|
422
|
+
boxSizing: "border-box",
|
423
|
+
fontWeight: 600,
|
424
|
+
backgroundColor: isPinned
|
425
|
+
? (theme) => theme.palette.background.paper
|
426
|
+
: "transparent",
|
427
|
+
borderRight:
|
428
|
+
((isPinned === "start" && !isLastPinned) || !isPinned) &&
|
429
|
+
!isLast &&
|
430
|
+
!(isLastCenter && table.getIsSomeColumnsPinned("right"))
|
431
|
+
? (theme) => `1px solid ${theme.palette.divider}`
|
432
|
+
: undefined,
|
433
|
+
borderLeft:
|
434
|
+
isPinned === "end" && !isLastPinned
|
435
|
+
? (theme) => `1px solid ${theme.palette.divider}`
|
436
|
+
: undefined,
|
437
|
+
}}
|
438
|
+
>
|
439
|
+
<div style={{ flex: 1, display: "flex", justifyContent: "flex-start" }}>
|
440
|
+
{header && !header.isPlaceholder
|
441
|
+
? flexRender(header.column.columnDef[type], header.getContext())
|
442
|
+
: null}
|
443
|
+
</div>
|
444
|
+
</TableCell>
|
445
|
+
);
|
446
|
+
},
|
447
|
+
);
|
434
448
|
|
435
449
|
export { AnoccaSkin };
|
436
450
|
|
@@ -0,0 +1,147 @@
|
|
1
|
+
import { Canvas, Meta, Story, Source, Controls } from '@storybook/blocks';
|
2
|
+
|
3
|
+
import * as RttuiStories from './ReactTanstackTableUiStory.stories';
|
4
|
+
|
5
|
+
<Meta of={RttuiStories} />
|
6
|
+
|
7
|
+
# Column sizing
|
8
|
+
By default columns uses the size defined in the columns. By default that is 150px in tanstack table.
|
9
|
+
|
10
|
+
You can override this by setting the `size` prop on the column.
|
11
|
+
```tsx
|
12
|
+
columnHelper.accessor("age", {
|
13
|
+
header: "Age",
|
14
|
+
size: 50,
|
15
|
+
})
|
16
|
+
```
|
17
|
+
|
18
|
+
<Canvas of={RttuiStories.Basic} />
|
19
|
+
|
20
|
+
### Auto crush columns
|
21
|
+
|
22
|
+
You can also use the `autoCrushColumns` prop to automatically crush columns to fit the largest header in the column.
|
23
|
+
|
24
|
+
```tsx
|
25
|
+
<ReactTanstackTableUi
|
26
|
+
autoCrushColumns
|
27
|
+
/>
|
28
|
+
```
|
29
|
+
|
30
|
+
To prevent a particular column from being crushed, you can set the `autoCrush` meta option to `false`.
|
31
|
+
|
32
|
+
```tsx
|
33
|
+
columnHelper.accessor("name", {
|
34
|
+
header: "Name",
|
35
|
+
meta: { autoCrush: false },
|
36
|
+
})
|
37
|
+
```
|
38
|
+
|
39
|
+
<Canvas of={RttuiStories.AutoCrushColumnsExceptName} />
|
40
|
+
|
41
|
+
### Crush behavior
|
42
|
+
|
43
|
+
By default the crush behavior is to crush the column based on the largest header.
|
44
|
+
You can change this by setting the `crushMinSizeBy` prop to `header`, `cell` or `both`.
|
45
|
+
|
46
|
+
```tsx
|
47
|
+
<ReactTanstackTableUi
|
48
|
+
autoCrushColumns
|
49
|
+
crushMinSizeBy="cell"
|
50
|
+
/>
|
51
|
+
```
|
52
|
+
|
53
|
+
<Story of={RttuiStories.SizeByLargestHeader} />
|
54
|
+
|
55
|
+
You can also use meta to configure the crush behavior for a particular column.
|
56
|
+
|
57
|
+
```tsx
|
58
|
+
columnHelper.accessor("age", {
|
59
|
+
header: "Age",
|
60
|
+
meta: { crushMinSizeBy: "cell" },
|
61
|
+
});
|
62
|
+
|
63
|
+
<ReactTanstackTableUi
|
64
|
+
autoCrushColumns
|
65
|
+
enableColumnPinning
|
66
|
+
crushMinSizeBy="header"
|
67
|
+
/>
|
68
|
+
```
|
69
|
+
|
70
|
+
<Story of={RttuiStories.SizeByLargestHeaderWithMeta} />
|
71
|
+
|
72
|
+
<br /><br />
|
73
|
+
|
74
|
+
|
75
|
+
### Fill available space after crush
|
76
|
+
|
77
|
+
If you don't have so many columns and you want the columns to fill all the available space,
|
78
|
+
you can set the `autoCrushColumns` prop to `true` and set the `fillAvailableSpaceAfterCrush` prop to `true`.
|
79
|
+
[If you don't want a horizontal scrollbar to appear](?path=/docs/reacttanstacktableui--scrollbarwidthspecified) you also need to specify the `scrollbarWidth` prop which is the width of the vertical scrollbar.
|
80
|
+
|
81
|
+
|
82
|
+
```tsx
|
83
|
+
<ReactTanstackTableUi
|
84
|
+
autoCrushColumns
|
85
|
+
fillAvailableSpaceAfterCrush
|
86
|
+
scrollbarWidth={16}
|
87
|
+
/>
|
88
|
+
```
|
89
|
+
|
90
|
+
To prevent a particular column from being expanded, you can set the `fillAvailableSpaceAfterCrush` meta option to `false`.
|
91
|
+
|
92
|
+
```ts
|
93
|
+
columnHelper.accessor("name", {
|
94
|
+
header: "Name",
|
95
|
+
meta: { fillAvailableSpaceAfterCrush: false },
|
96
|
+
})
|
97
|
+
```
|
98
|
+
|
99
|
+
<Canvas of={RttuiStories.FillAvailableSpaceAfterCrushExceptName} />
|
100
|
+
|
101
|
+
# Column Pinning
|
102
|
+
|
103
|
+
By default columns are pinned relative to the other columns:
|
104
|
+
|
105
|
+
<Story of={RttuiStories.PinRelativeToCols} />
|
106
|
+
|
107
|
+
```tsx
|
108
|
+
<ReactTanstackTableUi
|
109
|
+
autoCrushColumns
|
110
|
+
pinColsRelativeTo="cols"
|
111
|
+
/>
|
112
|
+
```
|
113
|
+
|
114
|
+
You can also pin columns relative to the table:
|
115
|
+
|
116
|
+
<Story of={RttuiStories.PinRelativeToTable} />
|
117
|
+
|
118
|
+
```tsx
|
119
|
+
<ReactTanstackTableUi
|
120
|
+
autoCrushColumns
|
121
|
+
pinColsRelativeTo="table"
|
122
|
+
/>
|
123
|
+
```
|
124
|
+
|
125
|
+
# Row Pinning
|
126
|
+
|
127
|
+
By default rows are pinned relative to the other rows:
|
128
|
+
|
129
|
+
<Story of={RttuiStories.CanPinRowsRelativeToRows} />
|
130
|
+
|
131
|
+
```tsx
|
132
|
+
<ReactTanstackTableUi
|
133
|
+
enableRowPinning
|
134
|
+
pinRowsRelativeTo="rows"
|
135
|
+
/>
|
136
|
+
```
|
137
|
+
|
138
|
+
You can also pin rows relative to the table:
|
139
|
+
|
140
|
+
<Story of={RttuiStories.CanPinRowsRelativeToTable} />
|
141
|
+
|
142
|
+
```tsx
|
143
|
+
<ReactTanstackTableUi
|
144
|
+
enableRowPinning
|
145
|
+
pinRowsRelativeTo="table"
|
146
|
+
/>
|
147
|
+
```
|
@@ -1,31 +1,9 @@
|
|
1
1
|
import type { Meta, StoryObj } from "@storybook/react";
|
2
2
|
|
3
|
-
import {
|
4
|
-
import { decorateColumnHelper } from "@rttui/core";
|
5
|
-
import { createColumnHelper, getCoreRowModel } from "@tanstack/react-table";
|
3
|
+
import { getCoreRowModel } from "@tanstack/react-table";
|
6
4
|
import { AnoccaSkin } from "..";
|
7
|
-
import { HeaderPinButtons } from "../HeaderPinButtons";
|
8
5
|
import { ReactTanstackTableUi } from "./ReactTanstackTableUiStoryComponent";
|
9
|
-
|
10
|
-
type Person = {
|
11
|
-
id: string;
|
12
|
-
name: string;
|
13
|
-
age: number;
|
14
|
-
city: string;
|
15
|
-
};
|
16
|
-
|
17
|
-
const data: Person[] = [
|
18
|
-
{ id: "1", name: "John", age: 20, city: "New York" },
|
19
|
-
{ id: "2", name: "Jane", age: 21, city: "Los Angeles" },
|
20
|
-
{ id: "3", name: "Jim", age: 22, city: "Chicago" },
|
21
|
-
];
|
22
|
-
|
23
|
-
const bigData: Person[] = Array.from({ length: 1000 }, (_, i) => ({
|
24
|
-
id: i.toString(),
|
25
|
-
name: `Person ${i}`,
|
26
|
-
age: 20 + i,
|
27
|
-
city: `City ${i}`,
|
28
|
-
}));
|
6
|
+
import { createSourceCode } from "./createSourceCode";
|
29
7
|
|
30
8
|
// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
|
31
9
|
const meta = {
|
@@ -37,153 +15,193 @@ const meta = {
|
|
37
15
|
},
|
38
16
|
// More on argTypes: https://storybook.js.org/docs/api/argtypes
|
39
17
|
argTypes: {
|
40
|
-
|
41
|
-
|
18
|
+
data: { control: "select" },
|
19
|
+
columns: { control: "select" },
|
20
|
+
pinColsRelativeTo: { control: "select", options: ["cols", "table"] },
|
21
|
+
skin: {
|
22
|
+
control: "object",
|
23
|
+
table: { disable: true },
|
24
|
+
},
|
42
25
|
},
|
43
26
|
// Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args
|
44
27
|
args: {
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
28
|
+
width: 600,
|
29
|
+
height: 400,
|
30
|
+
skin: AnoccaSkin,
|
31
|
+
autoCrushColumns: false,
|
32
|
+
data: "big",
|
33
|
+
columns: "few",
|
34
|
+
getCoreRowModel: getCoreRowModel(),
|
35
|
+
pinColsRelativeTo: "cols",
|
36
|
+
fillAvailableSpaceAfterCrush: false,
|
37
|
+
scrollbarWidth: 16,
|
38
|
+
getRowId: (row) => row.id,
|
56
39
|
},
|
57
40
|
} satisfies Meta<typeof ReactTanstackTableUi>;
|
58
41
|
|
59
42
|
export default meta;
|
60
43
|
type Story = StoryObj<typeof meta>;
|
61
44
|
|
62
|
-
const
|
63
|
-
header: (original) => (
|
64
|
-
<Box sx={{ display: "flex", gap: 2 }}>
|
65
|
-
{original}
|
66
|
-
<HeaderPinButtons />
|
67
|
-
</Box>
|
68
|
-
),
|
69
|
-
});
|
70
|
-
|
71
|
-
const columns = [
|
72
|
-
columnHelper.accessor("name", {
|
73
|
-
header: "Name",
|
74
|
-
}),
|
75
|
-
columnHelper.accessor("age", {
|
76
|
-
header: "Age",
|
77
|
-
}),
|
78
|
-
columnHelper.accessor("city", {
|
79
|
-
header: "City",
|
80
|
-
}),
|
81
|
-
];
|
82
|
-
|
83
|
-
// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
|
84
|
-
export const AutoSizeColumns: Story = {
|
45
|
+
export const Basic: Story = {
|
85
46
|
args: {
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
47
|
+
enableColumnPinning: true,
|
48
|
+
autoCrushColumns: false,
|
49
|
+
fillAvailableSpaceAfterCrush: false,
|
50
|
+
pinColsRelativeTo: "cols",
|
51
|
+
},
|
52
|
+
parameters: {
|
53
|
+
docs: {
|
54
|
+
source: { language: "tsx", code: createSourceCode() },
|
92
55
|
},
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
56
|
+
},
|
57
|
+
};
|
58
|
+
|
59
|
+
export const AutoCrushColumns: Story = {
|
60
|
+
args: {
|
61
|
+
autoCrushColumns: true,
|
62
|
+
},
|
63
|
+
parameters: {
|
64
|
+
docs: {
|
65
|
+
source: {
|
66
|
+
language: "tsx",
|
67
|
+
code: createSourceCode({ props: " autoCrushColumns" }),
|
68
|
+
},
|
98
69
|
},
|
99
70
|
},
|
100
71
|
};
|
101
72
|
|
102
|
-
|
103
|
-
export const AutoSizeColumnsPinRelativeToCols: Story = {
|
73
|
+
export const AutoCrushColumnsExceptName: Story = {
|
104
74
|
args: {
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
75
|
+
autoCrushColumns: true,
|
76
|
+
enableColumnPinning: true,
|
77
|
+
meta: {
|
78
|
+
name: {
|
79
|
+
autoCrush: false,
|
80
|
+
},
|
111
81
|
},
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
82
|
+
},
|
83
|
+
parameters: {
|
84
|
+
docs: {
|
85
|
+
source: {
|
86
|
+
language: "tsx",
|
87
|
+
code: createSourceCode({
|
88
|
+
props: " autoCrushColumns",
|
89
|
+
nameMeta: " autoCrush: false,",
|
90
|
+
}),
|
91
|
+
},
|
118
92
|
},
|
119
93
|
},
|
120
94
|
};
|
121
95
|
|
122
|
-
// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
|
123
96
|
export const PinRelativeToCols: Story = {
|
124
97
|
args: {
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
initialState: {
|
132
|
-
columnPinning: {
|
133
|
-
right: ["city"],
|
134
|
-
},
|
98
|
+
autoCrushColumns: true,
|
99
|
+
pinColsRelativeTo: "cols",
|
100
|
+
enableColumnPinning: true,
|
101
|
+
initialState: {
|
102
|
+
columnPinning: {
|
103
|
+
right: ["city"],
|
135
104
|
},
|
136
105
|
},
|
137
|
-
uiOptions: {
|
138
|
-
width: 600,
|
139
|
-
height: 400,
|
140
|
-
skin: AnoccaSkin,
|
141
|
-
},
|
142
106
|
},
|
143
107
|
};
|
144
108
|
|
145
109
|
export const PinRelativeToTable: Story = {
|
146
110
|
args: {
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
initialState: {
|
154
|
-
columnPinning: {
|
155
|
-
right: ["city"],
|
156
|
-
},
|
111
|
+
autoCrushColumns: true,
|
112
|
+
pinColsRelativeTo: "table",
|
113
|
+
enableColumnPinning: true,
|
114
|
+
initialState: {
|
115
|
+
columnPinning: {
|
116
|
+
right: ["city"],
|
157
117
|
},
|
158
118
|
},
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
119
|
+
},
|
120
|
+
};
|
121
|
+
|
122
|
+
export const FillAvailableSpaceAfterCrush: Story = {
|
123
|
+
args: {
|
124
|
+
autoCrushColumns: true,
|
125
|
+
fillAvailableSpaceAfterCrush: true,
|
126
|
+
},
|
127
|
+
};
|
128
|
+
|
129
|
+
export const FillAvailableSpaceAfterCrushExceptName: Story = {
|
130
|
+
args: {
|
131
|
+
autoCrushColumns: true,
|
132
|
+
fillAvailableSpaceAfterCrush: true,
|
133
|
+
meta: {
|
134
|
+
name: {
|
135
|
+
fillAvailableSpaceAfterCrush: false,
|
136
|
+
},
|
137
|
+
},
|
138
|
+
},
|
139
|
+
parameters: {
|
140
|
+
docs: {
|
141
|
+
source: {
|
142
|
+
language: "tsx",
|
143
|
+
code: createSourceCode({
|
144
|
+
props: " autoCrushColumns\n fillAvailableSpaceAfterCrush",
|
145
|
+
nameMeta: " fillAvailableSpaceAfterCrush: false,",
|
146
|
+
}),
|
147
|
+
},
|
148
|
+
},
|
149
|
+
},
|
150
|
+
};
|
151
|
+
|
152
|
+
export const FillAvailableSpaceAfterCrushWithoutSpecifiedScrollbarWidth: Story =
|
153
|
+
{
|
154
|
+
args: {
|
155
|
+
autoCrushColumns: true,
|
156
|
+
fillAvailableSpaceAfterCrush: true,
|
157
|
+
scrollbarWidth: 0,
|
158
|
+
},
|
159
|
+
};
|
160
|
+
|
161
|
+
export const CanPinRowsRelativeToRows: Story = {
|
162
|
+
args: {
|
163
|
+
enableRowPinning: true,
|
164
|
+
enableColumnPinning: true,
|
165
|
+
data: "small",
|
166
|
+
pinRowsRelativeTo: "rows",
|
167
|
+
initialState: {
|
168
|
+
rowPinning: {
|
169
|
+
bottom: ["3"],
|
170
|
+
},
|
164
171
|
},
|
165
172
|
},
|
166
173
|
};
|
167
174
|
|
168
|
-
export const
|
175
|
+
export const CanPinRowsRelativeToTable: Story = {
|
169
176
|
args: {
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
columnPinning: {
|
178
|
-
right: ["city"],
|
179
|
-
},
|
177
|
+
enableRowPinning: true,
|
178
|
+
enableColumnPinning: true,
|
179
|
+
data: "small",
|
180
|
+
pinRowsRelativeTo: "table",
|
181
|
+
initialState: {
|
182
|
+
rowPinning: {
|
183
|
+
bottom: ["3"],
|
180
184
|
},
|
181
185
|
},
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
186
|
+
},
|
187
|
+
};
|
188
|
+
|
189
|
+
export const SizeByLargestHeader: Story = {
|
190
|
+
args: {
|
191
|
+
autoCrushColumns: true,
|
192
|
+
crushMinSizeBy: "cell",
|
193
|
+
},
|
194
|
+
};
|
195
|
+
|
196
|
+
export const SizeByLargestHeaderWithMeta: Story = {
|
197
|
+
args: {
|
198
|
+
autoCrushColumns: true,
|
199
|
+
crushMinSizeBy: "header",
|
200
|
+
enableColumnPinning: true,
|
201
|
+
meta: {
|
202
|
+
age: {
|
203
|
+
crushMinSizeBy: 'cell',
|
204
|
+
},
|
187
205
|
},
|
188
206
|
},
|
189
207
|
};
|
@@ -1,22 +1,112 @@
|
|
1
|
-
import { ScopedCssBaseline, ThemeProvider } from "@mui/material";
|
1
|
+
import { Box, ScopedCssBaseline, ThemeProvider, Typography } from "@mui/material";
|
2
2
|
import { useTheme } from "@mui/material/styles";
|
3
|
-
import {
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
3
|
+
import {
|
4
|
+
decorateColumnHelper,
|
5
|
+
ReactTanstackTableUi as TableComponent,
|
6
|
+
} from "@rttui/core";
|
7
|
+
import {
|
8
|
+
ColumnDef,
|
9
|
+
ColumnMeta,
|
10
|
+
createColumnHelper,
|
11
|
+
Table,
|
12
|
+
TableOptions,
|
13
|
+
useReactTable,
|
14
|
+
} from "@tanstack/react-table";
|
15
|
+
import { HeaderPinButtons } from "../HeaderPinButtons";
|
16
|
+
import React from "react";
|
17
|
+
import { RowPinButtons } from "../RowPinButtons";
|
18
|
+
type Person = {
|
19
|
+
id: string;
|
20
|
+
name: string;
|
21
|
+
age: number;
|
22
|
+
city: string;
|
23
|
+
} & Record<`col${number}`, string>;
|
24
|
+
|
25
|
+
const smallData: Person[] = [
|
26
|
+
{ id: "1", name: "John", age: 20, city: "New York" },
|
27
|
+
{ id: "2", name: "Jane", age: 21, city: "Los Angeles" },
|
28
|
+
{ id: "3", name: "Jim", age: 22, city: "Chicago" },
|
29
|
+
];
|
30
|
+
|
31
|
+
const bigData: Person[] = Array.from({ length: 1000 }, (_, i) => ({
|
32
|
+
id: i.toString(),
|
33
|
+
name: `Person ${i}`,
|
34
|
+
age: 20 + i,
|
35
|
+
city: `City ${i}`,
|
36
|
+
...Object.fromEntries(
|
37
|
+
Array.from({ length: 100 }, (_, j) => [`col${j}`, `Value ${i}-${j}`]),
|
38
|
+
),
|
39
|
+
}));
|
40
|
+
|
41
|
+
export const ReactTanstackTableUi = (
|
42
|
+
props: {
|
43
|
+
data: "big" | "small";
|
44
|
+
columns: "many" | "few";
|
45
|
+
meta?: Record<string, ColumnMeta<Person, string>>;
|
46
|
+
} & Omit<TableOptions<Person>, "data" | "columns"> &
|
47
|
+
Omit<React.ComponentProps<typeof TableComponent>, "table">,
|
48
|
+
) => {
|
14
49
|
const theme = useTheme();
|
50
|
+
const data: Person[] = props.data === "big" ? bigData : smallData;
|
51
|
+
|
52
|
+
const columns: ColumnDef<Person>[] = React.useMemo(() => {
|
53
|
+
const columnHelper = decorateColumnHelper(createColumnHelper<Person>(), {
|
54
|
+
header: (original) => (
|
55
|
+
<Box sx={{ display: "flex", gap: 2 }}>
|
56
|
+
{original}
|
57
|
+
{props.enableColumnPinning && <HeaderPinButtons />}
|
58
|
+
</Box>
|
59
|
+
),
|
60
|
+
});
|
61
|
+
|
62
|
+
const fewColumns = [
|
63
|
+
columnHelper.accessor("name", {
|
64
|
+
header: "Name",
|
65
|
+
cell: (info) => (
|
66
|
+
<Box sx={{ display: "flex", gap: 2, flex: 1 }}>
|
67
|
+
<Typography variant="body2">{info.getValue()}</Typography>
|
68
|
+
{props.enableRowPinning && <Box sx={{ flexGrow: 1 }} />}
|
69
|
+
{props.enableRowPinning && <RowPinButtons row={info.row} />}
|
70
|
+
</Box>
|
71
|
+
),
|
72
|
+
meta: props.meta?.["name"],
|
73
|
+
}),
|
74
|
+
columnHelper.accessor("age", {
|
75
|
+
header: "Age",
|
76
|
+
meta: props.meta?.["age"],
|
77
|
+
cell: (info) => <Typography variant="body2">{info.getValue()}</Typography>,
|
78
|
+
size: 50,
|
79
|
+
}),
|
80
|
+
columnHelper.accessor("city", {
|
81
|
+
header: "City",
|
82
|
+
meta: props.meta?.["city"],
|
83
|
+
cell: (info) => <Typography variant="body2">{info.getValue()}</Typography>,
|
84
|
+
}),
|
85
|
+
];
|
86
|
+
|
87
|
+
const manyColumns = [
|
88
|
+
...fewColumns,
|
89
|
+
...Array.from({ length: 100 }, (_, i) =>
|
90
|
+
columnHelper.accessor(`col${i}`, {
|
91
|
+
header: `Column ${i}`,
|
92
|
+
cell: (info) => <Typography variant="body2">{info.getValue()}</Typography>,
|
93
|
+
}),
|
94
|
+
),
|
95
|
+
];
|
96
|
+
|
97
|
+
return props.columns === "few" ? fewColumns : manyColumns;
|
98
|
+
}, [props.columns, props.enableColumnPinning, props.enableRowPinning, props.meta]);
|
99
|
+
|
100
|
+
const table = useReactTable({
|
101
|
+
...props,
|
102
|
+
data,
|
103
|
+
columns,
|
104
|
+
});
|
15
105
|
|
16
106
|
return (
|
17
107
|
<ScopedCssBaseline>
|
18
108
|
<ThemeProvider theme={theme}>
|
19
|
-
<
|
109
|
+
<TableComponent {...props} table={table as Table<unknown>} />
|
20
110
|
</ThemeProvider>
|
21
111
|
</ScopedCssBaseline>
|
22
112
|
);
|
@@ -0,0 +1,42 @@
|
|
1
|
+
import { Canvas, Meta, Story, Source, Controls } from '@storybook/blocks';
|
2
|
+
import { Box } from '@mui/material';
|
3
|
+
|
4
|
+
import * as ColumnPinningStories from './ReactTanstackTableUiStory.stories';
|
5
|
+
|
6
|
+
<Meta of={ColumnPinningStories} />
|
7
|
+
|
8
|
+
# ⚠️ No scrollbar width specified
|
9
|
+
We get a horizontal scrollbar because we don't specify the width of the vertical scrollbar.
|
10
|
+
```tsx
|
11
|
+
<ReactTanstackTableUi
|
12
|
+
autoCrushColumns
|
13
|
+
fillAvailableSpaceAfterCrush
|
14
|
+
/>
|
15
|
+
```
|
16
|
+
<Box sx={{
|
17
|
+
'*::-webkit-scrollbar': { width: '16px' },
|
18
|
+
'*::-webkit-scrollbar-track': { backgroundColor: 'yellow' },
|
19
|
+
'*::-webkit-scrollbar-thumb': { backgroundColor: 'blue' },
|
20
|
+
}}
|
21
|
+
>
|
22
|
+
<Story of={ColumnPinningStories.FillAvailableSpaceAfterCrushWithoutSpecifiedScrollbarWidth} />
|
23
|
+
</Box>
|
24
|
+
<br /><br />
|
25
|
+
# ✅ Scrollbar width specified
|
26
|
+
We specify the width of the vertical scrollbar and we correctly only see a vertical scrollbar.
|
27
|
+
```tsx
|
28
|
+
<ReactTanstackTableUi
|
29
|
+
autoCrushColumns
|
30
|
+
fillAvailableSpaceAfterCrush
|
31
|
+
scrollbarWidth={16}
|
32
|
+
/>
|
33
|
+
```
|
34
|
+
<Box sx={{
|
35
|
+
'*::-webkit-scrollbar': { width: '16px' },
|
36
|
+
'*::-webkit-scrollbar-track': { backgroundColor: 'yellow' },
|
37
|
+
'*::-webkit-scrollbar-thumb': { backgroundColor: 'blue' },
|
38
|
+
}}
|
39
|
+
>
|
40
|
+
<Story of={ColumnPinningStories.FillAvailableSpaceAfterCrushExceptName} />
|
41
|
+
</Box>
|
42
|
+
|
@@ -0,0 +1,53 @@
|
|
1
|
+
export const createSourceCode = (opts?: { hookOptions?: string, props?: string, nameMeta?: string }) => {
|
2
|
+
return `import { createColumnHelper, useReactTable, getCoreRowModel } from "@tanstack/react-table";
|
3
|
+
import { Box } from "@mui/material";
|
4
|
+
import { HeaderPinButtons } from "@rttui/skin-anocca";
|
5
|
+
import { ReactTanstackTableUi, decorateColumnHelper } from "@rttui/core";
|
6
|
+
|
7
|
+
type Person = {
|
8
|
+
id: string;
|
9
|
+
name: string;
|
10
|
+
age: number;
|
11
|
+
city: string;
|
12
|
+
};
|
13
|
+
|
14
|
+
const data: Person[] = [
|
15
|
+
{ id: "1", name: "John", age: 20, city: "New York" },
|
16
|
+
{ id: "2", name: "Jane", age: 21, city: "Los Angeles" },
|
17
|
+
{ id: "3", name: "Jim", age: 22, city: "Chicago" },
|
18
|
+
];
|
19
|
+
|
20
|
+
const columnHelper = decorateColumnHelper(createColumnHelper<Person>(), {
|
21
|
+
header: (original) => (
|
22
|
+
<Box sx={{ display: "flex", gap: 2 }}>
|
23
|
+
{original}
|
24
|
+
{props.enableColumnPinning && <HeaderPinButtons />}
|
25
|
+
</Box>
|
26
|
+
),
|
27
|
+
});
|
28
|
+
|
29
|
+
const columns = [
|
30
|
+
columnHelper.accessor("name", {
|
31
|
+
header: "Name",${opts?.nameMeta ? '\n' + opts.nameMeta : ''}
|
32
|
+
}),
|
33
|
+
columnHelper.accessor("age", {
|
34
|
+
header: "Age",
|
35
|
+
size: 50,
|
36
|
+
}),
|
37
|
+
columnHelper.accessor("city", {
|
38
|
+
header: "City",
|
39
|
+
}),
|
40
|
+
];
|
41
|
+
|
42
|
+
const table = useReactTable({
|
43
|
+
data,
|
44
|
+
columns,
|
45
|
+
getCoreRowModel: getCoreRowModel()${opts?.hookOptions ? '\n' + opts.hookOptions : ''}
|
46
|
+
});
|
47
|
+
|
48
|
+
<ReactTanstackTableUi
|
49
|
+
table={table}
|
50
|
+
enableColumnPinning${opts?.props ? '\n' + opts.props : ''}
|
51
|
+
/>
|
52
|
+
`;
|
53
|
+
};
|
package/vite.config.ts
CHANGED
@@ -1,7 +1,23 @@
|
|
1
|
-
import { defineConfig } from
|
2
|
-
import react from
|
1
|
+
import { defineConfig } from "vite";
|
2
|
+
import react from "@vitejs/plugin-react";
|
3
|
+
import path from "path";
|
4
|
+
import fs from "fs";
|
5
|
+
let base: undefined | string;
|
6
|
+
|
7
|
+
try {
|
8
|
+
const packageJson = fs.readFileSync(
|
9
|
+
path.join(__dirname, "package.json"),
|
10
|
+
"utf-8",
|
11
|
+
);
|
12
|
+
const viteBase = JSON.parse(packageJson).viteBase;
|
13
|
+
if (viteBase) {
|
14
|
+
base = viteBase;
|
15
|
+
}
|
16
|
+
} catch (e) {
|
17
|
+
// ignore
|
18
|
+
}
|
3
19
|
|
4
20
|
// https://vite.dev/config/
|
5
21
|
export default defineConfig({
|
6
22
|
plugins: [react()],
|
7
|
-
})
|
23
|
+
});
|