@qoretechnologies/reqore 0.34.13 → 0.35.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/__tests__/helpers/table.test.tsx +47 -0
- package/__tests__/table.test.tsx +158 -123
- package/dist/components/Button/index.d.ts +2 -0
- package/dist/components/Button/index.d.ts.map +1 -1
- package/dist/components/Button/index.js +7 -4
- package/dist/components/Button/index.js.map +1 -1
- package/dist/components/Collection/index.d.ts +2 -1
- package/dist/components/Collection/index.d.ts.map +1 -1
- package/dist/components/Collection/index.js +5 -11
- package/dist/components/Collection/index.js.map +1 -1
- package/dist/components/ControlGroup/index.d.ts.map +1 -1
- package/dist/components/ControlGroup/index.js +4 -3
- package/dist/components/ControlGroup/index.js.map +1 -1
- package/dist/components/Dropdown/index.d.ts +4 -1
- package/dist/components/Dropdown/index.d.ts.map +1 -1
- package/dist/components/Dropdown/index.js +2 -2
- package/dist/components/Dropdown/index.js.map +1 -1
- package/dist/components/Dropdown/list.d.ts +4 -1
- package/dist/components/Dropdown/list.d.ts.map +1 -1
- package/dist/components/Dropdown/list.js +10 -5
- package/dist/components/Dropdown/list.js.map +1 -1
- package/dist/components/Input/index.js +4 -4
- package/dist/components/Input/index.js.map +1 -1
- package/dist/components/Panel/index.d.ts +4 -1
- package/dist/components/Panel/index.d.ts.map +1 -1
- package/dist/components/Panel/index.js.map +1 -1
- package/dist/components/Table/cell.d.ts +12 -0
- package/dist/components/Table/cell.d.ts.map +1 -0
- package/dist/components/Table/cell.js +89 -0
- package/dist/components/Table/cell.js.map +1 -0
- package/dist/components/Table/header.d.ts +15 -2
- package/dist/components/Table/header.d.ts.map +1 -1
- package/dist/components/Table/header.js +31 -14
- package/dist/components/Table/header.js.map +1 -1
- package/dist/components/Table/headerCell.d.ts +8 -6
- package/dist/components/Table/headerCell.d.ts.map +1 -1
- package/dist/components/Table/headerCell.js +60 -51
- package/dist/components/Table/headerCell.js.map +1 -1
- package/dist/components/Table/helpers.d.ts +39 -1
- package/dist/components/Table/helpers.d.ts.map +1 -1
- package/dist/components/Table/helpers.js +145 -1
- package/dist/components/Table/helpers.js.map +1 -1
- package/dist/components/Table/index.d.ts +39 -25
- package/dist/components/Table/index.d.ts.map +1 -1
- package/dist/components/Table/index.js +139 -52
- package/dist/components/Table/index.js.map +1 -1
- package/dist/components/Table/row.d.ts +9 -4
- package/dist/components/Table/row.d.ts.map +1 -1
- package/dist/components/Table/row.js +30 -54
- package/dist/components/Table/row.js.map +1 -1
- package/dist/hooks/useQueryWithDelay.d.ts +8 -0
- package/dist/hooks/useQueryWithDelay.d.ts.map +1 -0
- package/dist/hooks/useQueryWithDelay.js +25 -0
- package/dist/hooks/useQueryWithDelay.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/Button/index.tsx +6 -1
- package/src/components/Collection/index.tsx +9 -15
- package/src/components/ControlGroup/index.tsx +10 -5
- package/src/components/Dropdown/index.tsx +9 -0
- package/src/components/Dropdown/list.tsx +18 -6
- package/src/components/Input/index.tsx +3 -3
- package/src/components/Panel/index.tsx +3 -1
- package/src/components/Table/cell.tsx +117 -0
- package/src/components/Table/header.tsx +102 -45
- package/src/components/Table/headerCell.tsx +123 -80
- package/src/components/Table/helpers.ts +206 -1
- package/src/components/Table/index.tsx +288 -90
- package/src/components/Table/row.tsx +47 -125
- package/src/hooks/useQueryWithDelay.ts +30 -0
- package/src/index.tsx +3 -0
- package/src/mock/tableData.ts +40 -20
- package/src/stories/Breadcrumbs/Breadcrumbs.stories.tsx +1 -1
- package/src/stories/Button/Button.stories.tsx +1 -1
- package/src/stories/Checkbox/Checkbox.stories.tsx +1 -1
- package/src/stories/Collection/Collection.stories.tsx +19 -1
- package/src/stories/Columns/Columns.stories.tsx +1 -1
- package/src/stories/Comment/Comment.stories.tsx +1 -1
- package/src/stories/ControlGroup/ControlGroup.stories.tsx +1 -1
- package/src/stories/Drawer/Drawer.stories.tsx +5 -5
- package/src/stories/Dropdown/Dropdown.stories.tsx +11 -1
- package/src/stories/Header/Header.stories.tsx +1 -1
- package/src/stories/Icon/Icon.stories.tsx +1 -1
- package/src/stories/Input/Input.stories.tsx +1 -1
- package/src/stories/Menu/Menu.stories.tsx +1 -1
- package/src/stories/Message/Message.stories.tsx +1 -1
- package/src/stories/Modal/Modal.stories.tsx +1 -1
- package/src/stories/MultiSelect/MultiSelect.stories.tsx +1 -1
- package/src/stories/Navbar/Navbar.stories.tsx +1 -1
- package/src/stories/Notifications/Interactive.stories.tsx +1 -1
- package/src/stories/Notifications/Notification.stories.tsx +1 -1
- package/src/stories/Notifications/Notifications.stories.tsx +1 -1
- package/src/stories/Paging/Paging.stories.tsx +1 -1
- package/src/stories/Panel/Panel.stories.tsx +5 -5
- package/src/stories/Paragraph/Paragraph.stories.tsx +1 -1
- package/src/stories/Popover/Popover.stories.tsx +2 -2
- package/src/stories/RadioGroup/RadioGroup.stories.tsx +1 -1
- package/src/stories/Sidebar/Sidebar.stories.tsx +2 -2
- package/src/stories/Spacer/Spacer.stories.tsx +1 -1
- package/src/stories/Spinner/Spinner.stories.tsx +1 -1
- package/src/stories/Table/Table.stories.tsx +277 -78
- package/src/stories/Tabs/Tabs.stories.tsx +1 -1
- package/src/stories/Tag/Tag.stories.tsx +1 -1
- package/src/stories/TextArea/TextArea.stories.tsx +2 -2
- package/src/stories/Tree/Tree.stories.tsx +2 -2
- package/src/stories/utils/args.ts +1 -1
- package/tests.json +1 -1
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
import { size } from 'lodash';
|
|
1
2
|
import { firstBy } from 'thenby';
|
|
2
|
-
import { IReqoreTableSort } from '.';
|
|
3
|
+
import { IReqoreTableColumn, IReqoreTableSort } from '.';
|
|
4
|
+
import { ICON_FROM_SIZE, SIZE_TO_PX, TSizes } from '../../constants/sizes';
|
|
5
|
+
import { IReqoreIconName } from '../../types/icons';
|
|
6
|
+
import { IReqorePanelAction } from '../Panel';
|
|
3
7
|
|
|
4
8
|
export const flipSortDirection = (direction: 'asc' | 'desc'): 'asc' | 'desc' =>
|
|
5
9
|
direction === 'asc' ? 'desc' : 'asc';
|
|
@@ -23,3 +27,204 @@ export const sortTableData = (data: any[], sort: IReqoreTableSort) => {
|
|
|
23
27
|
|
|
24
28
|
return [...data].sort(firstBy(by, { ignoreCase: true, direction }));
|
|
25
29
|
};
|
|
30
|
+
|
|
31
|
+
export const updateColumnData = (
|
|
32
|
+
columns: IReqoreTableColumn[],
|
|
33
|
+
columnId: string,
|
|
34
|
+
key: keyof IReqoreTableColumn,
|
|
35
|
+
value: any
|
|
36
|
+
): IReqoreTableColumn[] => {
|
|
37
|
+
const newColumns: IReqoreTableColumn[] = columns.map((column): IReqoreTableColumn => {
|
|
38
|
+
if (column.dataId === columnId) {
|
|
39
|
+
return { ...column, [key]: value };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (column.header?.columns) {
|
|
43
|
+
return {
|
|
44
|
+
...column,
|
|
45
|
+
header: {
|
|
46
|
+
...column.header,
|
|
47
|
+
columns: updateColumnData(column.header.columns, columnId, key, value),
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return column;
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
return newColumns;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export const sizeToZoom = {
|
|
59
|
+
tiny: 0,
|
|
60
|
+
small: 0.5,
|
|
61
|
+
normal: 1,
|
|
62
|
+
big: 1.5,
|
|
63
|
+
huge: 2,
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export const zoomToSize = {
|
|
67
|
+
0: 'tiny',
|
|
68
|
+
0.5: 'small',
|
|
69
|
+
1: 'normal',
|
|
70
|
+
1.5: 'big',
|
|
71
|
+
2: 'huge',
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export const zoomToWidth = {
|
|
75
|
+
0: '200px',
|
|
76
|
+
0.5: '300px',
|
|
77
|
+
1: '400px',
|
|
78
|
+
1.5: '500px',
|
|
79
|
+
2: '600px',
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// This code converts a zoom level to a label.
|
|
83
|
+
export const zoomToLabel = {
|
|
84
|
+
0: '0.6x',
|
|
85
|
+
0.5: '0.8x',
|
|
86
|
+
1: '1x',
|
|
87
|
+
1.5: '1.2x',
|
|
88
|
+
2: '1.4x',
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
export const getZoomActions = (
|
|
92
|
+
type: string,
|
|
93
|
+
zoom: number,
|
|
94
|
+
setter: (zoom: number) => void
|
|
95
|
+
): IReqorePanelAction[] => [
|
|
96
|
+
{
|
|
97
|
+
icon: 'ZoomInLine',
|
|
98
|
+
tooltip: 'Zoom in',
|
|
99
|
+
disabled: zoom === 2,
|
|
100
|
+
className: `${type}-zoom-in`,
|
|
101
|
+
onClick: () => {
|
|
102
|
+
setter(zoom + 0.5);
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
icon: 'RestartLine',
|
|
107
|
+
label: zoomToLabel[zoom],
|
|
108
|
+
tooltip: 'Reset zoom',
|
|
109
|
+
disabled: zoom === 1,
|
|
110
|
+
className: `${type}-zoom-reset`,
|
|
111
|
+
onClick: () => {
|
|
112
|
+
setter(1);
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
icon: 'ZoomOutLine',
|
|
117
|
+
tooltip: 'Zoom out',
|
|
118
|
+
disabled: zoom === 0,
|
|
119
|
+
className: `${type}-zoom-out`,
|
|
120
|
+
onClick: () => {
|
|
121
|
+
setter(zoom - 0.5);
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
];
|
|
125
|
+
|
|
126
|
+
export const getColumnsCount = (columns: IReqoreTableColumn[]): number => {
|
|
127
|
+
let count = 0;
|
|
128
|
+
|
|
129
|
+
columns.forEach((column) => {
|
|
130
|
+
if (column.header.columns) {
|
|
131
|
+
count += getColumnsCount(column.header.columns);
|
|
132
|
+
} else {
|
|
133
|
+
count += 1;
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
return count;
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
export const hasHiddenColumns = (columns: IReqoreTableColumn[]): boolean => {
|
|
141
|
+
return columns.some((column) => {
|
|
142
|
+
if (column.header.columns) {
|
|
143
|
+
return column.header.columns.some((subColumn) => subColumn.show === false);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return column.show === false;
|
|
147
|
+
});
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
export const getOnlyShownColumns = (columns: IReqoreTableColumn[]): IReqoreTableColumn[] => {
|
|
151
|
+
return columns.reduce((newColumns, column) => {
|
|
152
|
+
if (column.header.columns) {
|
|
153
|
+
const subColumns = getOnlyShownColumns(column.header.columns);
|
|
154
|
+
|
|
155
|
+
if (size(subColumns)) {
|
|
156
|
+
return [
|
|
157
|
+
...newColumns,
|
|
158
|
+
{
|
|
159
|
+
...column,
|
|
160
|
+
header: { ...column.header, columns: getOnlyShownColumns(column.header.columns) },
|
|
161
|
+
},
|
|
162
|
+
];
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return newColumns;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (column.show !== false) {
|
|
169
|
+
return [...newColumns, column];
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return newColumns;
|
|
173
|
+
}, []);
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
export const prepareColumns = (
|
|
177
|
+
columns: IReqoreTableColumn[],
|
|
178
|
+
size: TSizes = 'normal'
|
|
179
|
+
): IReqoreTableColumn[] => {
|
|
180
|
+
// We need to set the width of each column
|
|
181
|
+
return columns.map((column) => {
|
|
182
|
+
if (column.header.columns) {
|
|
183
|
+
return {
|
|
184
|
+
...column,
|
|
185
|
+
header: { ...column.header, columns: prepareColumns(column.header.columns) },
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
let newWidth = calculateMinimumCellWidth(
|
|
190
|
+
column.width || 50,
|
|
191
|
+
size,
|
|
192
|
+
column.header.icon,
|
|
193
|
+
column.sortable,
|
|
194
|
+
column.filterable,
|
|
195
|
+
column.hideable,
|
|
196
|
+
column.resizable
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
return {
|
|
200
|
+
...column,
|
|
201
|
+
width: newWidth,
|
|
202
|
+
};
|
|
203
|
+
});
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
export const calculateMinimumCellWidth = (
|
|
207
|
+
currentWidth: number = 50,
|
|
208
|
+
size?: TSizes,
|
|
209
|
+
icon?: IReqoreIconName,
|
|
210
|
+
sortable?: boolean,
|
|
211
|
+
filterable?: boolean,
|
|
212
|
+
hideable?: boolean,
|
|
213
|
+
resizable?: boolean
|
|
214
|
+
): number => {
|
|
215
|
+
let width = currentWidth;
|
|
216
|
+
|
|
217
|
+
if (icon) {
|
|
218
|
+
width += ICON_FROM_SIZE[size];
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (sortable) {
|
|
222
|
+
width += ICON_FROM_SIZE[size];
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (filterable || hideable || resizable) {
|
|
226
|
+
width += SIZE_TO_PX[size];
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return width;
|
|
230
|
+
};
|
|
@@ -1,19 +1,32 @@
|
|
|
1
1
|
/* @flow */
|
|
2
|
-
import { size as count } from 'lodash';
|
|
3
|
-
import React, { useState } from 'react';
|
|
2
|
+
import { size as count, isArray } from 'lodash';
|
|
3
|
+
import React, { useCallback, useMemo, useState } from 'react';
|
|
4
4
|
import { useMeasure, useUpdateEffect } from 'react-use';
|
|
5
|
-
import
|
|
5
|
+
import { ReqoreMessage, ReqorePanel, ReqoreVerticalSpacer } from '../..';
|
|
6
6
|
import { TABLE_SIZE_TO_PX, TSizes } from '../../constants/sizes';
|
|
7
7
|
import { IReqoreTheme, TReqoreIntent } from '../../constants/theme';
|
|
8
|
-
import
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import
|
|
12
|
-
import {
|
|
8
|
+
import { useQueryWithDelay } from '../../hooks/useQueryWithDelay';
|
|
9
|
+
import { IReqoreIntent, IReqoreTooltip } from '../../types/global';
|
|
10
|
+
import { IReqoreButtonProps, TReqoreBadge } from '../Button';
|
|
11
|
+
import ReqoreInput, { IReqoreInputProps } from '../Input';
|
|
12
|
+
import { IReqorePanelAction, IReqorePanelProps, IReqorePanelSubAction } from '../Panel';
|
|
13
13
|
import ReqoreTableBody from './body';
|
|
14
|
-
import ReqoreTableHeader, {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
14
|
+
import ReqoreTableHeader, { IReqoreCustomHeaderCellComponent } from './header';
|
|
15
|
+
import { IReqoreTableHeaderCellProps } from './headerCell';
|
|
16
|
+
import {
|
|
17
|
+
fixSort,
|
|
18
|
+
flipSortDirection,
|
|
19
|
+
getColumnsCount,
|
|
20
|
+
getOnlyShownColumns,
|
|
21
|
+
getZoomActions,
|
|
22
|
+
hasHiddenColumns,
|
|
23
|
+
prepareColumns,
|
|
24
|
+
sizeToZoom,
|
|
25
|
+
sortTableData,
|
|
26
|
+
updateColumnData,
|
|
27
|
+
zoomToSize,
|
|
28
|
+
} from './helpers';
|
|
29
|
+
import { IReqoreTableRowOptions } from './row';
|
|
17
30
|
|
|
18
31
|
export type TReqoreTableColumnContent =
|
|
19
32
|
| React.FC<{ [key: string]: any; _selectId?: string | number }>
|
|
@@ -28,19 +41,31 @@ export type TReqoreTableColumnContent =
|
|
|
28
41
|
|
|
29
42
|
export interface IReqoreTableColumn extends IReqoreIntent {
|
|
30
43
|
dataId: string;
|
|
31
|
-
|
|
44
|
+
show?: boolean;
|
|
32
45
|
grow?: 1 | 2 | 3 | 4;
|
|
33
46
|
width?: number;
|
|
34
|
-
|
|
35
|
-
props?: React.HTMLAttributes<HTMLDivElement>;
|
|
47
|
+
resizedWidth?: number;
|
|
36
48
|
align?: 'center' | 'left' | 'right';
|
|
37
|
-
|
|
49
|
+
|
|
50
|
+
resizable?: boolean;
|
|
38
51
|
sortable?: boolean;
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
52
|
+
hideable?: boolean;
|
|
53
|
+
|
|
54
|
+
filterable?: boolean;
|
|
55
|
+
filterPlaceholder?: string;
|
|
56
|
+
filter?: string | number;
|
|
57
|
+
|
|
58
|
+
header?: {
|
|
59
|
+
columns?: IReqoreTableColumn[];
|
|
60
|
+
component?: React.FC<IReqoreTableHeaderCellProps>;
|
|
61
|
+
} & IReqoreButtonProps;
|
|
62
|
+
|
|
63
|
+
cell?: {
|
|
64
|
+
onClick?: (cellValue: any) => void;
|
|
65
|
+
tooltip?: (cellValue: any) => string | IReqoreTooltip;
|
|
66
|
+
content?: TReqoreTableColumnContent;
|
|
67
|
+
intent?: TReqoreIntent;
|
|
68
|
+
};
|
|
44
69
|
}
|
|
45
70
|
|
|
46
71
|
export interface IReqoreTableRowData {
|
|
@@ -53,28 +78,38 @@ export interface IReqoreTableRowData {
|
|
|
53
78
|
export type IReqoreTableRowClick = (data: IReqoreTableRowData) => void;
|
|
54
79
|
export type IReqoreTableData = IReqoreTableRowData[];
|
|
55
80
|
|
|
56
|
-
export interface IReqoreTableProps
|
|
57
|
-
extends React.HTMLAttributes<HTMLDivElement>,
|
|
58
|
-
IReqoreIntent,
|
|
59
|
-
IWithReqoreFlat,
|
|
60
|
-
IWithReqoreCustomTheme {
|
|
81
|
+
export interface IReqoreTableProps extends IReqorePanelProps {
|
|
61
82
|
columns: IReqoreTableColumn[];
|
|
62
83
|
data?: IReqoreTableData;
|
|
63
|
-
|
|
84
|
+
|
|
64
85
|
width?: number;
|
|
65
86
|
height?: number;
|
|
87
|
+
wrapperSize?: TSizes;
|
|
88
|
+
|
|
66
89
|
sort?: IReqoreTableSort;
|
|
67
|
-
|
|
90
|
+
onSortChange?: (sort?: IReqoreTableSort) => void;
|
|
91
|
+
|
|
92
|
+
filterable?: boolean;
|
|
93
|
+
filterProps?: (data: IReqoreTableData) => IReqoreInputProps;
|
|
94
|
+
filter?: string;
|
|
95
|
+
onFilterChange?: (query: string) => void;
|
|
96
|
+
|
|
97
|
+
zoomable?: boolean;
|
|
98
|
+
defaultZoom?: number;
|
|
99
|
+
|
|
68
100
|
selectable?: boolean;
|
|
69
101
|
selected?: string[];
|
|
70
102
|
selectedRowIntent?: TReqoreIntent;
|
|
71
|
-
onSortChange?: (sort?: IReqoreTableSort) => void;
|
|
72
103
|
onSelectedChange?: (selected?: any[]) => void;
|
|
73
104
|
selectToggleTooltip?: string;
|
|
105
|
+
|
|
106
|
+
striped?: boolean;
|
|
107
|
+
emptyMessage?: string;
|
|
108
|
+
|
|
74
109
|
onRowClick?: IReqoreTableRowClick;
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
110
|
+
headerCellComponent?: IReqoreCustomHeaderCellComponent;
|
|
111
|
+
rowComponent?: IReqoreTableRowOptions['rowComponent'];
|
|
112
|
+
bodyCellComponent?: IReqoreTableRowOptions['cellComponent'];
|
|
78
113
|
}
|
|
79
114
|
|
|
80
115
|
export interface IReqoreTableStyle {
|
|
@@ -93,35 +128,6 @@ export interface IReqoreTableSort {
|
|
|
93
128
|
direction?: 'asc' | 'desc';
|
|
94
129
|
}
|
|
95
130
|
|
|
96
|
-
const StyledTableWrapper = styled.div<IReqoreTableStyle>`
|
|
97
|
-
${({ theme, width, rounded, flat, fill }: IReqoreTableStyle) => css`
|
|
98
|
-
width: ${width ? `${width}px` : '100%'};
|
|
99
|
-
height: ${fill ? '100%' : 'auto'};
|
|
100
|
-
|
|
101
|
-
position: relative;
|
|
102
|
-
clear: both;
|
|
103
|
-
overflow: hidden;
|
|
104
|
-
z-index: 1;
|
|
105
|
-
|
|
106
|
-
display: flex;
|
|
107
|
-
flex-flow: column;
|
|
108
|
-
|
|
109
|
-
border-radius: ${rounded ? '10px' : undefined};
|
|
110
|
-
|
|
111
|
-
background-color: ${theme.main};
|
|
112
|
-
color: ${getReadableColor(theme, undefined, undefined, true)};
|
|
113
|
-
|
|
114
|
-
${!flat &&
|
|
115
|
-
css`
|
|
116
|
-
${StyledTableHeader}, ${StyledColumnGroupHeader} {
|
|
117
|
-
${({ theme }: IReqoreTableHeaderStyle) => css`
|
|
118
|
-
border-bottom: 1px solid ${changeLightness(theme.main, 0.07)};
|
|
119
|
-
`}
|
|
120
|
-
}
|
|
121
|
-
`}
|
|
122
|
-
`}
|
|
123
|
-
`;
|
|
124
|
-
|
|
125
131
|
const ReqoreTable = ({
|
|
126
132
|
className,
|
|
127
133
|
height = 300,
|
|
@@ -135,13 +141,23 @@ const ReqoreTable = ({
|
|
|
135
141
|
onSelectedChange,
|
|
136
142
|
selectToggleTooltip,
|
|
137
143
|
customTheme,
|
|
138
|
-
rounded,
|
|
139
144
|
onRowClick,
|
|
140
145
|
striped,
|
|
141
146
|
selectedRowIntent,
|
|
142
|
-
size,
|
|
147
|
+
size = 'normal',
|
|
148
|
+
wrapperSize = 'normal',
|
|
143
149
|
intent,
|
|
144
150
|
fill,
|
|
151
|
+
filterable,
|
|
152
|
+
zoomable,
|
|
153
|
+
filter,
|
|
154
|
+
actions = [],
|
|
155
|
+
onFilterChange,
|
|
156
|
+
filterProps,
|
|
157
|
+
emptyMessage = 'No data in this table, try changing your search query or filters',
|
|
158
|
+
headerCellComponent,
|
|
159
|
+
rowComponent,
|
|
160
|
+
bodyCellComponent,
|
|
145
161
|
...rest
|
|
146
162
|
}: IReqoreTableProps) => {
|
|
147
163
|
const [leftScroll, setLeftScroll] = useState<number>(0);
|
|
@@ -149,8 +165,53 @@ const ReqoreTable = ({
|
|
|
149
165
|
const [_sort, setSort] = useState<IReqoreTableSort>(fixSort(sort));
|
|
150
166
|
const [_selected, setSelected] = useState<(string | number)[]>([]);
|
|
151
167
|
const [_selectedQuant, setSelectedQuant] = useState<'all' | 'none' | 'some'>('none');
|
|
168
|
+
const [internalColumns, setColumns] = useState<IReqoreTableColumn[]>(prepareColumns(columns));
|
|
169
|
+
const [zoom, setZoom] = useState<number>(sizeToZoom[size]);
|
|
170
|
+
|
|
152
171
|
const [wrapperRef, sizes] = useMeasure();
|
|
153
|
-
const
|
|
172
|
+
const { query, preQuery, setQuery, setPreQuery } = useQueryWithDelay(filter, 300, onFilterChange);
|
|
173
|
+
|
|
174
|
+
const filters: { [key: string]: string } = useMemo(() => {
|
|
175
|
+
const getFilters = (columnsToTransform: IReqoreTableColumn[]) =>
|
|
176
|
+
columnsToTransform.reduce((filterObject, column) => {
|
|
177
|
+
if (column.header?.columns) {
|
|
178
|
+
return {
|
|
179
|
+
...filterObject,
|
|
180
|
+
...getFilters(column.header.columns),
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (column.filter) {
|
|
185
|
+
return {
|
|
186
|
+
...filterObject,
|
|
187
|
+
[column.dataId]: column.filter,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return filterObject;
|
|
192
|
+
}, {});
|
|
193
|
+
|
|
194
|
+
return getFilters(internalColumns);
|
|
195
|
+
}, [internalColumns]);
|
|
196
|
+
|
|
197
|
+
const transformedData = useMemo(() => {
|
|
198
|
+
// Filter by global query
|
|
199
|
+
let filteredData = _data.filter((datum) =>
|
|
200
|
+
JSON.stringify(datum).toLowerCase().includes(query.toLowerCase())
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
// Filter by column filters
|
|
204
|
+
filteredData = filteredData.filter((datum) => {
|
|
205
|
+
return Object.keys(filters).every((filterKey) => {
|
|
206
|
+
const filterValue = filters[filterKey];
|
|
207
|
+
const datumValue = datum[filterKey];
|
|
208
|
+
|
|
209
|
+
return datumValue?.toString().toLowerCase().includes(filterValue.toString().toLowerCase());
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
return _sort ? sortTableData(filteredData, _sort) : filteredData;
|
|
214
|
+
}, [_data, _sort, query, filters]);
|
|
154
215
|
|
|
155
216
|
useUpdateEffect(() => {
|
|
156
217
|
if (onSortChange) {
|
|
@@ -158,6 +219,10 @@ const ReqoreTable = ({
|
|
|
158
219
|
}
|
|
159
220
|
}, [_sort]);
|
|
160
221
|
|
|
222
|
+
useUpdateEffect(() => {
|
|
223
|
+
setColumns(prepareColumns(columns));
|
|
224
|
+
}, [columns]);
|
|
225
|
+
|
|
161
226
|
useUpdateEffect(() => {
|
|
162
227
|
setData(data);
|
|
163
228
|
}, [data]);
|
|
@@ -173,7 +238,9 @@ const ReqoreTable = ({
|
|
|
173
238
|
onSelectedChange(_selected);
|
|
174
239
|
}
|
|
175
240
|
|
|
176
|
-
const selectableData: IReqoreTableData =
|
|
241
|
+
const selectableData: IReqoreTableData = transformedData.filter(
|
|
242
|
+
(datum) => datum._selectId ?? false
|
|
243
|
+
);
|
|
177
244
|
|
|
178
245
|
if (count(_selected)) {
|
|
179
246
|
if (count(_selected) === count(selectableData)) {
|
|
@@ -217,7 +284,7 @@ const ReqoreTable = ({
|
|
|
217
284
|
switch (_selectedQuant) {
|
|
218
285
|
case 'none':
|
|
219
286
|
case 'some': {
|
|
220
|
-
const selectableData: (string | number)[] =
|
|
287
|
+
const selectableData: (string | number)[] = transformedData
|
|
221
288
|
.filter((datum) => datum._selectId ?? false)
|
|
222
289
|
.map((datum) => datum._selectId);
|
|
223
290
|
|
|
@@ -231,30 +298,159 @@ const ReqoreTable = ({
|
|
|
231
298
|
}
|
|
232
299
|
};
|
|
233
300
|
|
|
301
|
+
const handleColumnsUpdate = useCallback(
|
|
302
|
+
<T extends keyof IReqoreTableColumn>(id: string, key: T, value: IReqoreTableColumn[T]) => {
|
|
303
|
+
setColumns(updateColumnData(internalColumns, id, key, value));
|
|
304
|
+
},
|
|
305
|
+
[internalColumns]
|
|
306
|
+
);
|
|
307
|
+
|
|
308
|
+
const handlePreQueryChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
|
|
309
|
+
setPreQuery(event.target.value);
|
|
310
|
+
}, []);
|
|
311
|
+
|
|
312
|
+
const columnsList = useMemo(() => {
|
|
313
|
+
const _columnsList: IReqorePanelSubAction[] = [
|
|
314
|
+
{
|
|
315
|
+
divider: true,
|
|
316
|
+
label: 'Show / hide columns',
|
|
317
|
+
},
|
|
318
|
+
];
|
|
319
|
+
|
|
320
|
+
const addColumn = (column: IReqoreTableColumn) => {
|
|
321
|
+
_columnsList.push({
|
|
322
|
+
label: typeof column.header.label === 'string' ? column.header.label : column.dataId,
|
|
323
|
+
selected: column.show !== false,
|
|
324
|
+
onClick: () =>
|
|
325
|
+
handleColumnsUpdate(column.dataId, 'show', column.show !== false ? false : true),
|
|
326
|
+
intent: column.show !== false ? 'info' : undefined,
|
|
327
|
+
});
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
internalColumns.forEach((column) => {
|
|
331
|
+
if (column.header?.columns) {
|
|
332
|
+
column.header?.columns.forEach((subColumn) => {
|
|
333
|
+
addColumn(subColumn);
|
|
334
|
+
});
|
|
335
|
+
} else {
|
|
336
|
+
addColumn(column);
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
return _columnsList;
|
|
341
|
+
}, [internalColumns]);
|
|
342
|
+
|
|
343
|
+
const tableActions = useMemo<IReqorePanelAction[]>(() => {
|
|
344
|
+
const finalActions: IReqorePanelAction[] = [...actions];
|
|
345
|
+
|
|
346
|
+
finalActions.push({
|
|
347
|
+
label: 'Columns',
|
|
348
|
+
icon: 'LayoutColumnLine',
|
|
349
|
+
className: 'reqore-table-columns-options',
|
|
350
|
+
badge: getColumnsCount(getOnlyShownColumns(internalColumns)),
|
|
351
|
+
intent: hasHiddenColumns(internalColumns) ? 'info' : undefined,
|
|
352
|
+
multiSelect: true,
|
|
353
|
+
actions: columnsList,
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
if (filterable) {
|
|
357
|
+
finalActions.push({
|
|
358
|
+
as: ReqoreInput,
|
|
359
|
+
props: {
|
|
360
|
+
key: 'search',
|
|
361
|
+
fixed: false,
|
|
362
|
+
placeholder: `Search in ${count(transformedData)} items...`,
|
|
363
|
+
onClearClick: () => {
|
|
364
|
+
setQuery('');
|
|
365
|
+
setPreQuery('');
|
|
366
|
+
},
|
|
367
|
+
onChange: handlePreQueryChange,
|
|
368
|
+
value: preQuery,
|
|
369
|
+
icon: 'Search2Line',
|
|
370
|
+
disabled: !query && !count(transformedData),
|
|
371
|
+
minimal: false,
|
|
372
|
+
...filterProps?.(transformedData),
|
|
373
|
+
},
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
if (zoomable) {
|
|
378
|
+
finalActions.push({
|
|
379
|
+
fluid: false,
|
|
380
|
+
group: getZoomActions('reqore-table', zoom, setZoom),
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
return finalActions;
|
|
385
|
+
}, [
|
|
386
|
+
preQuery,
|
|
387
|
+
transformedData,
|
|
388
|
+
actions,
|
|
389
|
+
filterable,
|
|
390
|
+
filterProps,
|
|
391
|
+
internalColumns,
|
|
392
|
+
zoomable,
|
|
393
|
+
zoom,
|
|
394
|
+
columnsList,
|
|
395
|
+
]);
|
|
396
|
+
|
|
397
|
+
const badge = useMemo(() => {
|
|
398
|
+
const badgeList: TReqoreBadge[] = rest.label ? [count(transformedData)] : undefined;
|
|
399
|
+
|
|
400
|
+
if (rest.badge) {
|
|
401
|
+
if (isArray(rest.badge)) {
|
|
402
|
+
badgeList.push(...rest.badge);
|
|
403
|
+
} else {
|
|
404
|
+
badgeList.push(rest.badge);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
return badgeList;
|
|
409
|
+
}, [transformedData, rest.badge]);
|
|
410
|
+
|
|
234
411
|
return (
|
|
235
|
-
<
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
412
|
+
<ReqorePanel
|
|
413
|
+
transparent
|
|
414
|
+
flat
|
|
415
|
+
padded={false}
|
|
416
|
+
contentStyle={{ display: 'flex', flexFlow: 'column', overflow: 'hidden' }}
|
|
417
|
+
{...rest}
|
|
418
|
+
size={wrapperSize}
|
|
419
|
+
actions={tableActions}
|
|
420
|
+
fill={fill}
|
|
421
|
+
className={`${className || ''} reqore-table`}
|
|
422
|
+
style={{ width, ...(rest.style || {}) }}
|
|
423
|
+
getContentRef={wrapperRef}
|
|
424
|
+
badge={badge}
|
|
425
|
+
>
|
|
426
|
+
<ReqoreTableHeader
|
|
427
|
+
size={zoomToSize[zoom]}
|
|
428
|
+
columns={internalColumns}
|
|
429
|
+
leftScroll={leftScroll}
|
|
430
|
+
onSortChange={handleSortChange}
|
|
431
|
+
sortData={_sort}
|
|
432
|
+
selectable={selectable}
|
|
433
|
+
selectedQuant={_selectedQuant}
|
|
434
|
+
onToggleSelectClick={handleToggleSelectClick}
|
|
435
|
+
hasVerticalScroll={count(transformedData) * TABLE_SIZE_TO_PX[size] > height}
|
|
436
|
+
selectToggleTooltip={selectToggleTooltip}
|
|
437
|
+
onColumnsUpdate={handleColumnsUpdate}
|
|
438
|
+
onFilterChange={(dataId: string, value: any) => {
|
|
439
|
+
handleColumnsUpdate(dataId, 'filter', value);
|
|
440
|
+
}}
|
|
441
|
+
component={headerCellComponent}
|
|
442
|
+
/>
|
|
443
|
+
{count(transformedData) === 0 ? (
|
|
444
|
+
<>
|
|
445
|
+
<ReqoreVerticalSpacer height={10} />
|
|
446
|
+
<ReqoreMessage flat size={size} icon='Search2Line'>
|
|
447
|
+
{emptyMessage}
|
|
448
|
+
</ReqoreMessage>
|
|
449
|
+
</>
|
|
450
|
+
) : (
|
|
255
451
|
<ReqoreTableBody
|
|
256
|
-
data={
|
|
257
|
-
columns={
|
|
452
|
+
data={transformedData}
|
|
453
|
+
columns={internalColumns}
|
|
258
454
|
setLeftScroll={setLeftScroll}
|
|
259
455
|
height={fill ? sizes.height : height}
|
|
260
456
|
selectable={selectable}
|
|
@@ -262,12 +458,14 @@ const ReqoreTable = ({
|
|
|
262
458
|
onRowClick={onRowClick}
|
|
263
459
|
selected={_selected}
|
|
264
460
|
selectedRowIntent={selectedRowIntent}
|
|
265
|
-
size={
|
|
461
|
+
size={zoomToSize[zoom]}
|
|
266
462
|
striped={striped}
|
|
267
463
|
flat={rest.flat}
|
|
464
|
+
rowComponent={rowComponent}
|
|
465
|
+
cellComponent={bodyCellComponent}
|
|
268
466
|
/>
|
|
269
|
-
|
|
270
|
-
</
|
|
467
|
+
)}
|
|
468
|
+
</ReqorePanel>
|
|
271
469
|
);
|
|
272
470
|
};
|
|
273
471
|
|