@indico-data/design-system 2.48.0 → 2.50.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/lib/components/table/Table.stories.d.ts +1 -0
- package/lib/components/table/components/HorizontalStickyHeader.d.ts +10 -0
- package/lib/components/table/components/__tests__/HorizontalStickyHeader.test.d.ts +1 -0
- package/lib/components/table/components/helpers.d.ts +6 -0
- package/lib/components/table/hooks/usePinnedColumnsManager.d.ts +8 -0
- package/lib/components/table/sampleData.d.ts +4 -0
- package/lib/components/table/types.d.ts +11 -1
- package/lib/components/table/utils/processColumns.d.ts +2 -0
- package/lib/index.css +188 -89
- package/lib/index.d.ts +12 -1
- package/lib/index.esm.css +188 -89
- package/lib/index.esm.js +238 -2
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +238 -2
- package/lib/index.js.map +1 -1
- package/lib/stylesAndAnimations/utilityClasses/UtilityClassesData.d.ts +7 -0
- package/lib/stylesAndAnimations/utilityClasses/UtilityClassesTable.d.ts +1 -0
- package/lib/stylesAndAnimations/utilityClasses/UtilityClassesTable.stories.d.ts +6 -0
- package/lib/utils/getPreviousHeadersWidth.d.ts +1 -0
- package/package.json +1 -1
- package/src/components/table/Table.mdx +134 -0
- package/src/components/table/Table.stories.tsx +71 -2
- package/src/components/table/Table.tsx +16 -1
- package/src/components/table/components/HorizontalStickyHeader.tsx +57 -0
- package/src/components/table/components/__tests__/HorizontalStickyHeader.test.tsx +104 -0
- package/src/components/table/components/helpers.ts +90 -0
- package/src/components/table/hooks/usePinnedColumnsManager.ts +146 -0
- package/src/components/table/{sampleData.ts → sampleData.tsx} +156 -1
- package/src/components/table/styles/Table.scss +32 -15
- package/src/components/table/styles/_variables.scss +2 -0
- package/src/components/table/types.ts +13 -1
- package/src/components/table/utils/processColumns.tsx +35 -0
- package/src/setup/setupTests.ts +8 -0
- package/src/storybookDocs/Permafrost.mdx +22 -11
- package/src/styles/_borders.scss +2 -1
- package/src/stylesAndAnimations/borders/BorderColor.tsx +14 -6
- package/src/stylesAndAnimations/utilityClasses/UtilityClasses.mdx +24 -0
- package/src/stylesAndAnimations/utilityClasses/UtilityClassesData.ts +230 -0
- package/src/stylesAndAnimations/utilityClasses/UtilityClassesTable.stories.tsx +13 -0
- package/src/stylesAndAnimations/utilityClasses/UtilityClassesTable.tsx +146 -0
- package/src/utils/getPreviousHeadersWidth.ts +12 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { useEffect, useCallback, useMemo } from 'react';
|
|
2
|
+
import { TableColumn } from '../types';
|
|
3
|
+
import { sortPinnedColumns, getPreviousHeadersWidth } from '../components/helpers';
|
|
4
|
+
import { processColumns } from '../utils/processColumns';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Hook to manage pinned columns in a table
|
|
8
|
+
* Handles initialization, toggling, positioning and resizing of pinned columns
|
|
9
|
+
*/
|
|
10
|
+
export const usePinnedColumnsManager = <T>(
|
|
11
|
+
columns: TableColumn<T>[],
|
|
12
|
+
canPinColumns: boolean,
|
|
13
|
+
onPinnedColumnsChange?: (pinnedColumnIds: string[]) => void,
|
|
14
|
+
) => {
|
|
15
|
+
const pinnedColumnIds = columns.filter((column) => column.isPinned).map((column) => column.id);
|
|
16
|
+
|
|
17
|
+
// `dataColumnIds` is the list of IDs used as `data-column-id` attributes on the table headers and cells
|
|
18
|
+
const dataColumnIds = useMemo(() => {
|
|
19
|
+
const ids = columns
|
|
20
|
+
.map((column, index) => (column.isPinned ? `sticky-column-${index}` : null))
|
|
21
|
+
.filter((id): id is string => id !== null);
|
|
22
|
+
|
|
23
|
+
return ids.length > 0 ? ['checkbox-column', ...ids] : ids;
|
|
24
|
+
}, [columns]);
|
|
25
|
+
|
|
26
|
+
// Toggle individual column pin state
|
|
27
|
+
const togglePinnedColumn = useCallback(
|
|
28
|
+
(columnId: string) => {
|
|
29
|
+
const prevPinnedColumns = pinnedColumnIds;
|
|
30
|
+
|
|
31
|
+
// Handle unpinning
|
|
32
|
+
if (prevPinnedColumns.some((id) => id === columnId)) {
|
|
33
|
+
onPinnedColumnsChange?.(prevPinnedColumns.filter((id) => id !== columnId));
|
|
34
|
+
} else {
|
|
35
|
+
onPinnedColumnsChange?.(prevPinnedColumns.concat(columnId));
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
[pinnedColumnIds, onPinnedColumnsChange],
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
// Handle resize events and recalculate pinned column positions
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
if (!canPinColumns) return;
|
|
44
|
+
|
|
45
|
+
const recalculatePositions = () => {
|
|
46
|
+
// Reset all column styles and remove last-pinned-column class
|
|
47
|
+
const allCells = document.querySelectorAll('.rdt_TableCol, .rdt_TableCell');
|
|
48
|
+
allCells.forEach((cell) => {
|
|
49
|
+
(cell as HTMLElement).style.position = '';
|
|
50
|
+
(cell as HTMLElement).style.left = '';
|
|
51
|
+
(cell as HTMLElement).style.zIndex = '';
|
|
52
|
+
(cell as HTMLElement).style.backgroundColor = '';
|
|
53
|
+
(cell as HTMLElement).style.borderRight = '';
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// Apply styles to pinned columns
|
|
57
|
+
dataColumnIds.forEach((column, index) => {
|
|
58
|
+
const isLastPinnedColumn = index === dataColumnIds.length - 1;
|
|
59
|
+
|
|
60
|
+
if (column === 'checkbox-column') {
|
|
61
|
+
// Handle header checkbox
|
|
62
|
+
const headerCheckbox = document.querySelector('.rdt_TableCol:not([data-column-id])');
|
|
63
|
+
if (headerCheckbox) {
|
|
64
|
+
(headerCheckbox as HTMLElement).style.position = 'sticky';
|
|
65
|
+
(headerCheckbox as HTMLElement).style.left = '0';
|
|
66
|
+
(headerCheckbox as HTMLElement).style.zIndex = '4';
|
|
67
|
+
(headerCheckbox as HTMLElement).style.backgroundColor =
|
|
68
|
+
'var(--pf-table-background-color)';
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Handle cell checkboxes
|
|
72
|
+
const cellCheckboxes = document.querySelectorAll('.rdt_TableCell:first-child');
|
|
73
|
+
cellCheckboxes.forEach((cell) => {
|
|
74
|
+
(cell as HTMLElement).style.position = 'sticky';
|
|
75
|
+
(cell as HTMLElement).style.left = '0';
|
|
76
|
+
(cell as HTMLElement).style.zIndex = '2';
|
|
77
|
+
(cell as HTMLElement).style.backgroundColor =
|
|
78
|
+
'var(--pf-table-pinned-column-background-color)';
|
|
79
|
+
if (isLastPinnedColumn) {
|
|
80
|
+
(cell as HTMLElement).style.borderRight =
|
|
81
|
+
`2px solid var(--pf-table-pinned-column-border-color)`;
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
} else {
|
|
85
|
+
const columnIndex = parseInt(column.split('-')[2]);
|
|
86
|
+
const left = getPreviousHeadersWidth(columnIndex, dataColumnIds);
|
|
87
|
+
|
|
88
|
+
// Headers
|
|
89
|
+
const headers = document.querySelectorAll(
|
|
90
|
+
`.rdt_TableCol[data-column-id="sticky-column-${columnIndex}"]`,
|
|
91
|
+
);
|
|
92
|
+
headers.forEach((header) => {
|
|
93
|
+
(header as HTMLElement).style.position = 'sticky';
|
|
94
|
+
(header as HTMLElement).style.left = `${left}px`;
|
|
95
|
+
(header as HTMLElement).style.zIndex = '2';
|
|
96
|
+
(header as HTMLElement).style.backgroundColor =
|
|
97
|
+
'var(--pf-table-pinned-column-background-color)';
|
|
98
|
+
if (isLastPinnedColumn) {
|
|
99
|
+
(header as HTMLElement).style.borderRight =
|
|
100
|
+
`2px solid var(--pf-table-pinned-column-border-color)`;
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// Cells
|
|
105
|
+
const cells = document.querySelectorAll(
|
|
106
|
+
`.rdt_TableCell[data-column-id="sticky-column-${columnIndex}"]`,
|
|
107
|
+
);
|
|
108
|
+
cells.forEach((cell) => {
|
|
109
|
+
(cell as HTMLElement).style.position = 'sticky';
|
|
110
|
+
(cell as HTMLElement).style.left = `${left}px`;
|
|
111
|
+
(cell as HTMLElement).style.zIndex = '2';
|
|
112
|
+
(cell as HTMLElement).style.backgroundColor =
|
|
113
|
+
'var(--pf-table-pinned-column-background-color)';
|
|
114
|
+
if (isLastPinnedColumn) {
|
|
115
|
+
(cell as HTMLElement).style.borderRight =
|
|
116
|
+
`2px solid var(--pf-table-pinned-column-border-color)`;
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
// Set up resize observers
|
|
124
|
+
const table = document.querySelector('.rdt_Table');
|
|
125
|
+
const resizeObserver = new ResizeObserver(recalculatePositions);
|
|
126
|
+
|
|
127
|
+
if (table) {
|
|
128
|
+
resizeObserver.observe(table);
|
|
129
|
+
}
|
|
130
|
+
window.addEventListener('resize', recalculatePositions);
|
|
131
|
+
|
|
132
|
+
return () => {
|
|
133
|
+
resizeObserver.disconnect();
|
|
134
|
+
window.removeEventListener('resize', recalculatePositions);
|
|
135
|
+
};
|
|
136
|
+
}, [canPinColumns, dataColumnIds]);
|
|
137
|
+
|
|
138
|
+
// Process columns for rendering with pin state
|
|
139
|
+
const columnsWithPinning = canPinColumns
|
|
140
|
+
? sortPinnedColumns(processColumns(columns, dataColumnIds, togglePinnedColumn), dataColumnIds)
|
|
141
|
+
: columns;
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
columnsWithPinning, // Columns with pin state and handlers applied
|
|
145
|
+
};
|
|
146
|
+
};
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { TableColumn } from './types';
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { Table } from './Table';
|
|
2
4
|
|
|
3
5
|
export interface SampleDataRow {
|
|
4
6
|
name: string;
|
|
@@ -7,6 +9,10 @@ export interface SampleDataRow {
|
|
|
7
9
|
weapon: string;
|
|
8
10
|
backstory: string;
|
|
9
11
|
favoriteMeal: string;
|
|
12
|
+
homeland: string;
|
|
13
|
+
alignment: string;
|
|
14
|
+
specialAbility: string;
|
|
15
|
+
test: string;
|
|
10
16
|
}
|
|
11
17
|
|
|
12
18
|
export const sampleData: SampleDataRow[] = [
|
|
@@ -17,6 +23,10 @@ export const sampleData: SampleDataRow[] = [
|
|
|
17
23
|
weapon: 'Longbow',
|
|
18
24
|
backstory: 'Raised by wolves in the deep forests.',
|
|
19
25
|
favoriteMeal: 'Venison stew',
|
|
26
|
+
homeland: 'Silverleaf Forest',
|
|
27
|
+
alignment: 'Neutral Good',
|
|
28
|
+
specialAbility: 'Beast Speech',
|
|
29
|
+
test: 'test',
|
|
20
30
|
},
|
|
21
31
|
{
|
|
22
32
|
name: 'Brom',
|
|
@@ -25,6 +35,10 @@ export const sampleData: SampleDataRow[] = [
|
|
|
25
35
|
weapon: 'Greatsword',
|
|
26
36
|
backstory: 'A former soldier seeking redemption.',
|
|
27
37
|
favoriteMeal: 'Roasted boar',
|
|
38
|
+
homeland: 'Kingdom of Valorhaven',
|
|
39
|
+
alignment: 'Lawful Good',
|
|
40
|
+
specialAbility: 'Battle Master',
|
|
41
|
+
test: 'test',
|
|
28
42
|
},
|
|
29
43
|
{
|
|
30
44
|
name: 'Elysia',
|
|
@@ -33,6 +47,10 @@ export const sampleData: SampleDataRow[] = [
|
|
|
33
47
|
weapon: 'Mace',
|
|
34
48
|
backstory: 'A devoted follower of the goddess of life.',
|
|
35
49
|
favoriteMeal: 'Vegetable soup',
|
|
50
|
+
homeland: 'Temple of Dawn',
|
|
51
|
+
alignment: 'Lawful Good',
|
|
52
|
+
specialAbility: 'Divine Healing',
|
|
53
|
+
test: 'test',
|
|
36
54
|
},
|
|
37
55
|
{
|
|
38
56
|
name: 'Faelar',
|
|
@@ -41,6 +59,10 @@ export const sampleData: SampleDataRow[] = [
|
|
|
41
59
|
weapon: 'Dagger',
|
|
42
60
|
backstory: 'Grew up on the streets of a bustling city.',
|
|
43
61
|
favoriteMeal: 'Bread and cheese',
|
|
62
|
+
homeland: 'Shadowport City',
|
|
63
|
+
alignment: 'Chaotic Neutral',
|
|
64
|
+
specialAbility: 'Shadow Step',
|
|
65
|
+
test: 'test',
|
|
44
66
|
},
|
|
45
67
|
{
|
|
46
68
|
name: 'Maelis',
|
|
@@ -49,6 +71,10 @@ export const sampleData: SampleDataRow[] = [
|
|
|
49
71
|
weapon: 'Staff',
|
|
50
72
|
backstory: 'A scholar obsessed with ancient magic.',
|
|
51
73
|
favoriteMeal: 'Mushroom stew',
|
|
74
|
+
homeland: 'Crystal Spire Academy',
|
|
75
|
+
alignment: 'Neutral',
|
|
76
|
+
specialAbility: 'Time Manipulation',
|
|
77
|
+
test: 'test',
|
|
52
78
|
},
|
|
53
79
|
{
|
|
54
80
|
name: 'Grimm',
|
|
@@ -57,6 +83,10 @@ export const sampleData: SampleDataRow[] = [
|
|
|
57
83
|
weapon: 'Battleaxe',
|
|
58
84
|
backstory: 'Hails from a tribe in the frozen north.',
|
|
59
85
|
favoriteMeal: 'Grilled salmon',
|
|
86
|
+
homeland: 'Frostpeak Mountains',
|
|
87
|
+
alignment: 'Chaotic Good',
|
|
88
|
+
specialAbility: 'Berserker Rage',
|
|
89
|
+
test: 'test',
|
|
60
90
|
},
|
|
61
91
|
{
|
|
62
92
|
name: 'Seraphina',
|
|
@@ -65,6 +95,10 @@ export const sampleData: SampleDataRow[] = [
|
|
|
65
95
|
weapon: 'Rapier',
|
|
66
96
|
backstory: 'A wandering minstrel with a mysterious past.',
|
|
67
97
|
favoriteMeal: 'Apple pie',
|
|
98
|
+
homeland: 'Wandering Roads',
|
|
99
|
+
alignment: 'Chaotic Good',
|
|
100
|
+
specialAbility: 'Enchanting Voice',
|
|
101
|
+
test: 'test',
|
|
68
102
|
},
|
|
69
103
|
{
|
|
70
104
|
name: 'Kael',
|
|
@@ -73,6 +107,10 @@ export const sampleData: SampleDataRow[] = [
|
|
|
73
107
|
weapon: 'Scimitar',
|
|
74
108
|
backstory: 'A guardian of the natural world.',
|
|
75
109
|
favoriteMeal: 'Berry tart',
|
|
110
|
+
homeland: 'Emerald Grove',
|
|
111
|
+
alignment: 'True Neutral',
|
|
112
|
+
specialAbility: 'Wild Shape',
|
|
113
|
+
test: 'test',
|
|
76
114
|
},
|
|
77
115
|
{
|
|
78
116
|
name: 'Aria',
|
|
@@ -81,6 +119,10 @@ export const sampleData: SampleDataRow[] = [
|
|
|
81
119
|
weapon: 'Longsword',
|
|
82
120
|
backstory: 'A knight on a divine mission.',
|
|
83
121
|
favoriteMeal: 'Roasted chicken',
|
|
122
|
+
homeland: 'Radiant Citadel',
|
|
123
|
+
alignment: 'Lawful Good',
|
|
124
|
+
specialAbility: 'Holy Smite',
|
|
125
|
+
test: 'test',
|
|
84
126
|
},
|
|
85
127
|
{
|
|
86
128
|
name: 'Xan',
|
|
@@ -89,6 +131,10 @@ export const sampleData: SampleDataRow[] = [
|
|
|
89
131
|
weapon: 'Pact Blade',
|
|
90
132
|
backstory: 'Made a pact with a powerful entity for magic.',
|
|
91
133
|
favoriteMeal: 'Stuffed peppers',
|
|
134
|
+
homeland: 'Shadowfell Gate',
|
|
135
|
+
alignment: 'Lawful Evil',
|
|
136
|
+
specialAbility: 'Eldritch Blast',
|
|
137
|
+
test: 'test',
|
|
92
138
|
},
|
|
93
139
|
{
|
|
94
140
|
name: 'Lyra',
|
|
@@ -97,6 +143,10 @@ export const sampleData: SampleDataRow[] = [
|
|
|
97
143
|
weapon: 'Dagger',
|
|
98
144
|
backstory: 'Born with innate magical abilities.',
|
|
99
145
|
favoriteMeal: 'Pancakes with syrup',
|
|
146
|
+
homeland: 'Storm Peaks',
|
|
147
|
+
alignment: 'Chaotic Neutral',
|
|
148
|
+
specialAbility: 'Wild Magic',
|
|
149
|
+
test: 'test',
|
|
100
150
|
},
|
|
101
151
|
{
|
|
102
152
|
name: 'Thorn',
|
|
@@ -105,6 +155,10 @@ export const sampleData: SampleDataRow[] = [
|
|
|
105
155
|
weapon: 'Quarterstaff',
|
|
106
156
|
backstory: 'A hermit who seeks enlightenment.',
|
|
107
157
|
favoriteMeal: 'Rice and vegetables',
|
|
158
|
+
homeland: 'Mountain Sanctuary',
|
|
159
|
+
alignment: 'Lawful Neutral',
|
|
160
|
+
specialAbility: 'Ki Mastery',
|
|
161
|
+
test: 'test',
|
|
108
162
|
},
|
|
109
163
|
{
|
|
110
164
|
name: 'Cassia',
|
|
@@ -113,6 +167,10 @@ export const sampleData: SampleDataRow[] = [
|
|
|
113
167
|
weapon: 'Shortbow',
|
|
114
168
|
backstory: 'A hunter with a keen eye.',
|
|
115
169
|
favoriteMeal: 'Roasted pheasant',
|
|
170
|
+
homeland: 'Misty Vale',
|
|
171
|
+
alignment: 'Neutral Good',
|
|
172
|
+
specialAbility: 'Eagle Eye',
|
|
173
|
+
test: 'test',
|
|
116
174
|
},
|
|
117
175
|
{
|
|
118
176
|
name: 'Darius',
|
|
@@ -121,6 +179,10 @@ export const sampleData: SampleDataRow[] = [
|
|
|
121
179
|
weapon: 'Longsword',
|
|
122
180
|
backstory: 'A mercenary with a heart of gold.',
|
|
123
181
|
favoriteMeal: 'Beef stew',
|
|
182
|
+
homeland: 'Free Cities',
|
|
183
|
+
alignment: 'Neutral Good',
|
|
184
|
+
specialAbility: 'Second Wind',
|
|
185
|
+
test: 'test',
|
|
124
186
|
},
|
|
125
187
|
{
|
|
126
188
|
name: 'Iris',
|
|
@@ -129,6 +191,10 @@ export const sampleData: SampleDataRow[] = [
|
|
|
129
191
|
weapon: 'Warhammer',
|
|
130
192
|
backstory: 'A healer devoted to the god of light.',
|
|
131
193
|
favoriteMeal: 'Fresh bread and soup',
|
|
194
|
+
homeland: 'Sun Temple',
|
|
195
|
+
alignment: 'Lawful Good',
|
|
196
|
+
specialAbility: 'Turn Undead',
|
|
197
|
+
test: 'test',
|
|
132
198
|
},
|
|
133
199
|
{
|
|
134
200
|
name: 'Fenrir',
|
|
@@ -137,6 +203,10 @@ export const sampleData: SampleDataRow[] = [
|
|
|
137
203
|
weapon: 'Greataxe',
|
|
138
204
|
backstory: 'A warrior from a distant land.',
|
|
139
205
|
favoriteMeal: 'Grilled steak',
|
|
206
|
+
homeland: 'Northern Wastes',
|
|
207
|
+
alignment: 'Chaotic Neutral',
|
|
208
|
+
specialAbility: 'Primal Strike',
|
|
209
|
+
test: 'test',
|
|
140
210
|
},
|
|
141
211
|
{
|
|
142
212
|
name: 'Luna',
|
|
@@ -145,6 +215,10 @@ export const sampleData: SampleDataRow[] = [
|
|
|
145
215
|
weapon: 'Sickle',
|
|
146
216
|
backstory: 'A protector of the natural world.',
|
|
147
217
|
favoriteMeal: 'Fruit salad',
|
|
218
|
+
homeland: 'Moongrove Forest',
|
|
219
|
+
alignment: 'Neutral Good',
|
|
220
|
+
specialAbility: 'Natures Blessing',
|
|
221
|
+
test: 'test',
|
|
148
222
|
},
|
|
149
223
|
{
|
|
150
224
|
name: 'Orion',
|
|
@@ -153,6 +227,10 @@ export const sampleData: SampleDataRow[] = [
|
|
|
153
227
|
weapon: 'Halberd',
|
|
154
228
|
backstory: 'A knight on a holy quest.',
|
|
155
229
|
favoriteMeal: 'Roasted lamb',
|
|
230
|
+
homeland: 'Golden Spires',
|
|
231
|
+
alignment: 'Lawful Good',
|
|
232
|
+
specialAbility: 'Divine Shield',
|
|
233
|
+
test: 'test',
|
|
156
234
|
},
|
|
157
235
|
{
|
|
158
236
|
name: 'Astra',
|
|
@@ -161,6 +239,10 @@ export const sampleData: SampleDataRow[] = [
|
|
|
161
239
|
weapon: 'Lute',
|
|
162
240
|
backstory: 'A musician with a magical voice.',
|
|
163
241
|
favoriteMeal: 'Honey cake',
|
|
242
|
+
homeland: 'Singing Valleys',
|
|
243
|
+
alignment: 'Chaotic Good',
|
|
244
|
+
specialAbility: 'Sonic Wave',
|
|
245
|
+
test: 'test',
|
|
164
246
|
},
|
|
165
247
|
{
|
|
166
248
|
name: 'Zephyr',
|
|
@@ -169,6 +251,10 @@ export const sampleData: SampleDataRow[] = [
|
|
|
169
251
|
weapon: 'Shortsword',
|
|
170
252
|
backstory: 'A thief with a quick hand.',
|
|
171
253
|
favoriteMeal: 'Fish and chips',
|
|
254
|
+
homeland: 'Port Haven',
|
|
255
|
+
alignment: 'Chaotic Neutral',
|
|
256
|
+
specialAbility: 'Quick Strike',
|
|
257
|
+
test: 'test',
|
|
172
258
|
},
|
|
173
259
|
{
|
|
174
260
|
name: 'Thalia',
|
|
@@ -177,6 +263,10 @@ export const sampleData: SampleDataRow[] = [
|
|
|
177
263
|
weapon: 'Crystal Staff',
|
|
178
264
|
backstory: 'An archivist of forbidden knowledge.',
|
|
179
265
|
favoriteMeal: 'Spiced wine and cheese',
|
|
266
|
+
homeland: 'Arcane Archives',
|
|
267
|
+
alignment: 'Neutral',
|
|
268
|
+
specialAbility: 'Spell Mastery',
|
|
269
|
+
test: 'test',
|
|
180
270
|
},
|
|
181
271
|
{
|
|
182
272
|
name: 'Ragnar',
|
|
@@ -185,6 +275,10 @@ export const sampleData: SampleDataRow[] = [
|
|
|
185
275
|
weapon: 'War Hammer',
|
|
186
276
|
backstory: 'A dwarven smith turned adventurer.',
|
|
187
277
|
favoriteMeal: 'Mead and roasted mutton',
|
|
278
|
+
homeland: 'Iron Mountains',
|
|
279
|
+
alignment: 'Lawful Neutral',
|
|
280
|
+
specialAbility: 'Master Smith',
|
|
281
|
+
test: 'test',
|
|
188
282
|
},
|
|
189
283
|
{
|
|
190
284
|
name: 'Sylvana',
|
|
@@ -193,6 +287,10 @@ export const sampleData: SampleDataRow[] = [
|
|
|
193
287
|
weapon: 'Elven Bow',
|
|
194
288
|
backstory: 'Guardian of the enchanted forest.',
|
|
195
289
|
favoriteMeal: 'Elvish waybread',
|
|
290
|
+
homeland: 'Eternal Woods',
|
|
291
|
+
alignment: 'Neutral Good',
|
|
292
|
+
specialAbility: 'Forest Walker',
|
|
293
|
+
test: 'test',
|
|
196
294
|
},
|
|
197
295
|
{
|
|
198
296
|
name: 'Magnus',
|
|
@@ -201,6 +299,10 @@ export const sampleData: SampleDataRow[] = [
|
|
|
201
299
|
weapon: 'Cursed Dagger',
|
|
202
300
|
backstory: 'Made a deal with a demon for revenge.',
|
|
203
301
|
favoriteMeal: 'Blood oranges',
|
|
302
|
+
homeland: 'Cursed Lands',
|
|
303
|
+
alignment: 'Neutral Evil',
|
|
304
|
+
specialAbility: 'Dark Pact',
|
|
305
|
+
test: 'test',
|
|
204
306
|
},
|
|
205
307
|
{
|
|
206
308
|
name: 'Echo',
|
|
@@ -209,6 +311,10 @@ export const sampleData: SampleDataRow[] = [
|
|
|
209
311
|
weapon: 'Enchanted Violin',
|
|
210
312
|
backstory: 'Can mimic any sound perfectly.',
|
|
211
313
|
favoriteMeal: 'Sweet rolls',
|
|
314
|
+
homeland: 'Harmony Hall',
|
|
315
|
+
alignment: 'Chaotic Good',
|
|
316
|
+
specialAbility: 'Perfect Pitch',
|
|
317
|
+
test: 'test',
|
|
212
318
|
},
|
|
213
319
|
{
|
|
214
320
|
name: 'Korg',
|
|
@@ -217,6 +323,10 @@ export const sampleData: SampleDataRow[] = [
|
|
|
217
323
|
weapon: 'Stone Maul',
|
|
218
324
|
backstory: 'Last survivor of a petrified tribe.',
|
|
219
325
|
favoriteMeal: 'Raw meat',
|
|
326
|
+
homeland: 'Stone Peaks',
|
|
327
|
+
alignment: 'Neutral',
|
|
328
|
+
specialAbility: 'Stone Skin',
|
|
329
|
+
test: 'test',
|
|
220
330
|
},
|
|
221
331
|
{
|
|
222
332
|
name: 'Celeste',
|
|
@@ -225,6 +335,10 @@ export const sampleData: SampleDataRow[] = [
|
|
|
225
335
|
weapon: 'Starlight Wand',
|
|
226
336
|
backstory: 'Born during a celestial convergence.',
|
|
227
337
|
favoriteMeal: 'Moon cakes',
|
|
338
|
+
homeland: 'Star Tower',
|
|
339
|
+
alignment: 'Chaotic Good',
|
|
340
|
+
specialAbility: 'Celestial Power',
|
|
341
|
+
test: 'test',
|
|
228
342
|
},
|
|
229
343
|
{
|
|
230
344
|
name: 'Raven',
|
|
@@ -233,6 +347,10 @@ export const sampleData: SampleDataRow[] = [
|
|
|
233
347
|
weapon: 'Shadow Blade',
|
|
234
348
|
backstory: 'Professional assassin seeking redemption.',
|
|
235
349
|
favoriteMeal: 'Whatever their mark is having',
|
|
350
|
+
homeland: 'Night City',
|
|
351
|
+
alignment: 'True Neutral',
|
|
352
|
+
specialAbility: 'Death Strike',
|
|
353
|
+
test: 'test',
|
|
236
354
|
},
|
|
237
355
|
{
|
|
238
356
|
name: 'Terra',
|
|
@@ -241,6 +359,10 @@ export const sampleData: SampleDataRow[] = [
|
|
|
241
359
|
weapon: 'Living Wood Staff',
|
|
242
360
|
backstory: 'Speaks with ancient trees.',
|
|
243
361
|
favoriteMeal: 'Wild mushrooms',
|
|
362
|
+
homeland: 'Ancient Grove',
|
|
363
|
+
alignment: 'Neutral Good',
|
|
364
|
+
specialAbility: 'Tree Speech',
|
|
365
|
+
test: 'test',
|
|
244
366
|
},
|
|
245
367
|
{
|
|
246
368
|
name: 'Ash',
|
|
@@ -249,6 +371,10 @@ export const sampleData: SampleDataRow[] = [
|
|
|
249
371
|
weapon: 'Flame Fists',
|
|
250
372
|
backstory: 'Raised by phoenix monks in a volcano.',
|
|
251
373
|
favoriteMeal: 'Spicy noodles',
|
|
374
|
+
homeland: 'Phoenix Monastery',
|
|
375
|
+
alignment: 'Lawful Neutral',
|
|
376
|
+
specialAbility: 'Fire Fist',
|
|
377
|
+
test: 'test',
|
|
252
378
|
},
|
|
253
379
|
];
|
|
254
380
|
|
|
@@ -256,26 +382,55 @@ export const columns: TableColumn<SampleDataRow>[] = [
|
|
|
256
382
|
{
|
|
257
383
|
name: 'Name',
|
|
258
384
|
selector: (row: SampleDataRow) => row.name,
|
|
385
|
+
id: 'name',
|
|
386
|
+
width: '150px',
|
|
259
387
|
},
|
|
260
388
|
{
|
|
261
389
|
name: 'Class',
|
|
262
390
|
selector: (row) => row.class,
|
|
391
|
+
id: 'class',
|
|
392
|
+
width: '150px',
|
|
263
393
|
},
|
|
264
394
|
{
|
|
265
395
|
name: 'Age',
|
|
266
396
|
selector: (row) => row.age,
|
|
267
|
-
|
|
397
|
+
id: 'age',
|
|
398
|
+
width: '150px',
|
|
268
399
|
},
|
|
269
400
|
{
|
|
270
401
|
name: 'Weapon',
|
|
271
402
|
selector: (row) => row.weapon,
|
|
403
|
+
id: 'weapon',
|
|
404
|
+
width: '150px',
|
|
272
405
|
},
|
|
273
406
|
{
|
|
274
407
|
name: 'Backstory',
|
|
275
408
|
selector: (row) => row.backstory,
|
|
409
|
+
id: 'backstory',
|
|
410
|
+
width: '150px',
|
|
276
411
|
},
|
|
277
412
|
{
|
|
278
413
|
name: 'Favorite Meal',
|
|
279
414
|
selector: (row) => row.favoriteMeal,
|
|
415
|
+
id: 'favoriteMeal',
|
|
416
|
+
width: '200px',
|
|
417
|
+
},
|
|
418
|
+
{
|
|
419
|
+
name: 'Homeland',
|
|
420
|
+
selector: (row) => row.homeland,
|
|
421
|
+
id: 'homeland',
|
|
422
|
+
width: '200px',
|
|
423
|
+
},
|
|
424
|
+
{
|
|
425
|
+
name: 'Alignment',
|
|
426
|
+
selector: (row) => row.alignment,
|
|
427
|
+
id: 'alignment',
|
|
428
|
+
width: '200px',
|
|
429
|
+
},
|
|
430
|
+
{
|
|
431
|
+
name: 'Special Ability',
|
|
432
|
+
selector: (row) => row.specialAbility,
|
|
433
|
+
id: 'specialAbility',
|
|
434
|
+
width: '200px',
|
|
280
435
|
},
|
|
281
436
|
];
|
|
@@ -53,8 +53,28 @@
|
|
|
53
53
|
// Firefox scrollbar styles
|
|
54
54
|
scrollbar-width: thin;
|
|
55
55
|
scrollbar-color: var(--pf-table-border-color) var(--pf-table-background-color);
|
|
56
|
+
} // body
|
|
57
|
+
|
|
58
|
+
// Pinned Columns
|
|
59
|
+
.table__column__pin-action {
|
|
60
|
+
padding: 0;
|
|
61
|
+
padding-right: var(--pf-padding-1);
|
|
62
|
+
}
|
|
63
|
+
.table__column--is-pinned {
|
|
64
|
+
opacity: 1;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.table__column--is-not-pinned {
|
|
68
|
+
opacity: 0.3;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.table__header-cell {
|
|
72
|
+
display: flex;
|
|
73
|
+
align-items: center;
|
|
56
74
|
}
|
|
57
75
|
|
|
76
|
+
// End Pinned Columns
|
|
77
|
+
|
|
58
78
|
& > *:nth-child(3) {
|
|
59
79
|
margin-top: auto;
|
|
60
80
|
background-color: transparent;
|
|
@@ -87,33 +107,26 @@
|
|
|
87
107
|
}
|
|
88
108
|
}
|
|
89
109
|
|
|
110
|
+
.rdt_TableHead {
|
|
111
|
+
z-index: 3;
|
|
112
|
+
}
|
|
113
|
+
|
|
90
114
|
.rdt_TableHeader {
|
|
91
115
|
border-radius: var(--pf-rounded) 0;
|
|
92
116
|
border: var(--pf-border-sm) solid var(--pf-table-border-color);
|
|
93
117
|
border-bottom: none;
|
|
94
118
|
}
|
|
95
119
|
|
|
96
|
-
.rdt_TableHeadRow,
|
|
97
|
-
.rdt_TableRow {
|
|
98
|
-
& > :first-child {
|
|
99
|
-
padding-left: var(--pf-padding-4);
|
|
100
|
-
min-width: 60px;
|
|
101
|
-
justify-content: left;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
120
|
.rdt_TableRow {
|
|
106
121
|
border-top: var(--pf-border-sm) solid var(--pf-table-border-color);
|
|
107
122
|
border-bottom: var(--pf-border-sm) solid var(--pf-table-border-color);
|
|
108
123
|
&:hover {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
}
|
|
124
|
+
border-top: var(--pf-border-sm) solid var(--pf-table-border-color);
|
|
125
|
+
border-bottom: var(--pf-border-sm) solid var(--pf-table-border-color);
|
|
126
|
+
outline-color: var(--pf-table-border-color);
|
|
113
127
|
|
|
114
|
-
&:first-child {
|
|
115
128
|
.rdt_TableCell {
|
|
116
|
-
|
|
129
|
+
background-color: var(--pf-table-hover-color) !important;
|
|
117
130
|
}
|
|
118
131
|
}
|
|
119
132
|
}
|
|
@@ -125,6 +138,10 @@
|
|
|
125
138
|
&:not(:first-child) {
|
|
126
139
|
border-left: var(--pf-border-sm) solid var(--pf-table-border-color);
|
|
127
140
|
}
|
|
141
|
+
|
|
142
|
+
&:not(:last-of-type) {
|
|
143
|
+
border-bottom-width: 0;
|
|
144
|
+
}
|
|
128
145
|
}
|
|
129
146
|
|
|
130
147
|
/* Striped: alternating background */
|
|
@@ -28,4 +28,6 @@
|
|
|
28
28
|
--pf-table-highlighted-box-shadow: 0 4px 8px rgba(0, 0, 0, 0.4), 0 8px 16px rgba(0, 0, 0, 0.3);
|
|
29
29
|
--pf-table-font-size: var(--pf-font-size-body2);
|
|
30
30
|
--pf-table-pagination-background-color: var(--pf-primary-color-700);
|
|
31
|
+
--pf-table-pinned-column-border-color: var(--pf-primary-color-100);
|
|
32
|
+
--pf-table-pinned-column-background-color: var(--pf-table-background-color);
|
|
31
33
|
}
|
|
@@ -5,17 +5,29 @@ import {
|
|
|
5
5
|
TableColumn as RDTTableColumn,
|
|
6
6
|
IDataTableProps,
|
|
7
7
|
} from 'react-data-table-component';
|
|
8
|
+
import { CSSObject } from 'styled-components';
|
|
8
9
|
|
|
9
10
|
export type Direction = `${RDTDirection}`;
|
|
10
11
|
export type Alignment = `${RDTAlignment}`;
|
|
11
|
-
|
|
12
|
+
|
|
13
|
+
export interface PinnableColumn<T> extends RDTTableColumn<T> {
|
|
14
|
+
id: string;
|
|
15
|
+
isPinned?: boolean;
|
|
16
|
+
style?: CSSObject;
|
|
17
|
+
position?: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type TableColumn<T> = PinnableColumn<T>;
|
|
12
21
|
|
|
13
22
|
export interface TableProps<T>
|
|
14
23
|
extends Omit<IDataTableProps<T>, 'paginationComponent' | 'direction' | 'subHeaderAlign'> {
|
|
24
|
+
columns: TableColumn<T>[];
|
|
15
25
|
isDisabled?: boolean;
|
|
16
26
|
isLoading?: boolean;
|
|
17
27
|
direction?: Direction;
|
|
18
28
|
subHeaderAlign?: 'left' | 'right' | 'center';
|
|
19
29
|
isFullHeight?: boolean;
|
|
20
30
|
totalEntriesText?: string;
|
|
31
|
+
canPinColumns?: boolean;
|
|
32
|
+
onPinnedColumnsChange?: (pinnedColumnIds: any[]) => void;
|
|
21
33
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { TableColumn } from '../types';
|
|
2
|
+
import HorizontalStickyHeader from '../components/HorizontalStickyHeader';
|
|
3
|
+
import { getPinnedColumnStyles } from '../components/helpers';
|
|
4
|
+
|
|
5
|
+
export const processColumns = <T,>(
|
|
6
|
+
columns: TableColumn<T>[],
|
|
7
|
+
pinnedColumnIds: string[],
|
|
8
|
+
togglePinnedColumn: (id: string) => void,
|
|
9
|
+
): TableColumn<T>[] => {
|
|
10
|
+
return columns.map((column, index) => {
|
|
11
|
+
const dataColumnId = `sticky-column-${index}`;
|
|
12
|
+
const isPinned = pinnedColumnIds.includes(dataColumnId);
|
|
13
|
+
|
|
14
|
+
const headerContent =
|
|
15
|
+
column.isPinned !== undefined ? (
|
|
16
|
+
<HorizontalStickyHeader
|
|
17
|
+
position={index}
|
|
18
|
+
isPinned={isPinned}
|
|
19
|
+
onPinColumn={() => togglePinnedColumn(column.id)}
|
|
20
|
+
pinnedColumnIds={pinnedColumnIds}
|
|
21
|
+
>
|
|
22
|
+
{column.name}
|
|
23
|
+
</HorizontalStickyHeader>
|
|
24
|
+
) : (
|
|
25
|
+
<>{column.name}</>
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
...column,
|
|
30
|
+
name: headerContent,
|
|
31
|
+
id: dataColumnId,
|
|
32
|
+
style: getPinnedColumnStyles(isPinned, index, pinnedColumnIds),
|
|
33
|
+
};
|
|
34
|
+
});
|
|
35
|
+
};
|