@qoretechnologies/reqore 0.34.13 → 0.35.1

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.
Files changed (110) hide show
  1. package/__tests__/helpers/table.test.tsx +47 -0
  2. package/__tests__/table.test.tsx +356 -124
  3. package/dist/components/Button/index.d.ts +2 -0
  4. package/dist/components/Button/index.d.ts.map +1 -1
  5. package/dist/components/Button/index.js +7 -4
  6. package/dist/components/Button/index.js.map +1 -1
  7. package/dist/components/Collection/index.d.ts +2 -1
  8. package/dist/components/Collection/index.d.ts.map +1 -1
  9. package/dist/components/Collection/index.js +5 -11
  10. package/dist/components/Collection/index.js.map +1 -1
  11. package/dist/components/ControlGroup/index.d.ts.map +1 -1
  12. package/dist/components/ControlGroup/index.js +4 -3
  13. package/dist/components/ControlGroup/index.js.map +1 -1
  14. package/dist/components/Dropdown/index.d.ts +4 -1
  15. package/dist/components/Dropdown/index.d.ts.map +1 -1
  16. package/dist/components/Dropdown/index.js +2 -2
  17. package/dist/components/Dropdown/index.js.map +1 -1
  18. package/dist/components/Dropdown/list.d.ts +4 -1
  19. package/dist/components/Dropdown/list.d.ts.map +1 -1
  20. package/dist/components/Dropdown/list.js +10 -5
  21. package/dist/components/Dropdown/list.js.map +1 -1
  22. package/dist/components/Input/index.js +4 -4
  23. package/dist/components/Input/index.js.map +1 -1
  24. package/dist/components/Panel/index.d.ts +4 -1
  25. package/dist/components/Panel/index.d.ts.map +1 -1
  26. package/dist/components/Panel/index.js.map +1 -1
  27. package/dist/components/Table/cell.d.ts +13 -0
  28. package/dist/components/Table/cell.d.ts.map +1 -0
  29. package/dist/components/Table/cell.js +89 -0
  30. package/dist/components/Table/cell.js.map +1 -0
  31. package/dist/components/Table/header.d.ts +15 -2
  32. package/dist/components/Table/header.d.ts.map +1 -1
  33. package/dist/components/Table/header.js +31 -14
  34. package/dist/components/Table/header.js.map +1 -1
  35. package/dist/components/Table/headerCell.d.ts +9 -6
  36. package/dist/components/Table/headerCell.d.ts.map +1 -1
  37. package/dist/components/Table/headerCell.js +73 -51
  38. package/dist/components/Table/headerCell.js.map +1 -1
  39. package/dist/components/Table/helpers.d.ts +43 -1
  40. package/dist/components/Table/helpers.d.ts.map +1 -1
  41. package/dist/components/Table/helpers.js +145 -1
  42. package/dist/components/Table/helpers.js.map +1 -1
  43. package/dist/components/Table/index.d.ts +45 -25
  44. package/dist/components/Table/index.d.ts.map +1 -1
  45. package/dist/components/Table/index.js +167 -52
  46. package/dist/components/Table/index.js.map +1 -1
  47. package/dist/components/Table/row.d.ts +11 -5
  48. package/dist/components/Table/row.d.ts.map +1 -1
  49. package/dist/components/Table/row.js +32 -55
  50. package/dist/components/Table/row.js.map +1 -1
  51. package/dist/hooks/useQueryWithDelay.d.ts +8 -0
  52. package/dist/hooks/useQueryWithDelay.d.ts.map +1 -0
  53. package/dist/hooks/useQueryWithDelay.js +25 -0
  54. package/dist/hooks/useQueryWithDelay.js.map +1 -0
  55. package/dist/index.d.ts +3 -0
  56. package/dist/index.d.ts.map +1 -1
  57. package/dist/index.js +7 -1
  58. package/dist/index.js.map +1 -1
  59. package/package.json +1 -1
  60. package/src/components/Button/index.tsx +6 -1
  61. package/src/components/Collection/index.tsx +9 -15
  62. package/src/components/ControlGroup/index.tsx +10 -5
  63. package/src/components/Dropdown/index.tsx +9 -0
  64. package/src/components/Dropdown/list.tsx +18 -6
  65. package/src/components/Input/index.tsx +3 -3
  66. package/src/components/Panel/index.tsx +3 -1
  67. package/src/components/Table/cell.tsx +120 -0
  68. package/src/components/Table/header.tsx +102 -45
  69. package/src/components/Table/headerCell.tsx +133 -80
  70. package/src/components/Table/helpers.ts +211 -1
  71. package/src/components/Table/index.tsx +334 -91
  72. package/src/components/Table/row.tsx +51 -127
  73. package/src/hooks/useQueryWithDelay.ts +30 -0
  74. package/src/index.tsx +3 -0
  75. package/src/mock/tableData.ts +40 -20
  76. package/src/stories/Breadcrumbs/Breadcrumbs.stories.tsx +1 -1
  77. package/src/stories/Button/Button.stories.tsx +1 -1
  78. package/src/stories/Checkbox/Checkbox.stories.tsx +1 -1
  79. package/src/stories/Collection/Collection.stories.tsx +19 -1
  80. package/src/stories/Columns/Columns.stories.tsx +1 -1
  81. package/src/stories/Comment/Comment.stories.tsx +1 -1
  82. package/src/stories/ControlGroup/ControlGroup.stories.tsx +1 -1
  83. package/src/stories/Drawer/Drawer.stories.tsx +5 -5
  84. package/src/stories/Dropdown/Dropdown.stories.tsx +11 -1
  85. package/src/stories/Header/Header.stories.tsx +1 -1
  86. package/src/stories/Icon/Icon.stories.tsx +1 -1
  87. package/src/stories/Input/Input.stories.tsx +1 -1
  88. package/src/stories/Menu/Menu.stories.tsx +1 -1
  89. package/src/stories/Message/Message.stories.tsx +1 -1
  90. package/src/stories/Modal/Modal.stories.tsx +1 -1
  91. package/src/stories/MultiSelect/MultiSelect.stories.tsx +1 -1
  92. package/src/stories/Navbar/Navbar.stories.tsx +1 -1
  93. package/src/stories/Notifications/Interactive.stories.tsx +1 -1
  94. package/src/stories/Notifications/Notification.stories.tsx +1 -1
  95. package/src/stories/Notifications/Notifications.stories.tsx +1 -1
  96. package/src/stories/Paging/Paging.stories.tsx +1 -1
  97. package/src/stories/Panel/Panel.stories.tsx +5 -5
  98. package/src/stories/Paragraph/Paragraph.stories.tsx +1 -1
  99. package/src/stories/Popover/Popover.stories.tsx +2 -2
  100. package/src/stories/RadioGroup/RadioGroup.stories.tsx +1 -1
  101. package/src/stories/Sidebar/Sidebar.stories.tsx +2 -2
  102. package/src/stories/Spacer/Spacer.stories.tsx +1 -1
  103. package/src/stories/Spinner/Spinner.stories.tsx +1 -1
  104. package/src/stories/Table/Table.stories.tsx +297 -78
  105. package/src/stories/Tabs/Tabs.stories.tsx +1 -1
  106. package/src/stories/Tag/Tag.stories.tsx +1 -1
  107. package/src/stories/TextArea/TextArea.stories.tsx +2 -2
  108. package/src/stories/Tree/Tree.stories.tsx +2 -2
  109. package/src/stories/utils/args.ts +1 -1
  110. package/tests.json +1 -1
@@ -1,18 +1,24 @@
1
1
  /* @flow */
2
- import React, { useState } from 'react';
3
- import styled, { css } from 'styled-components';
2
+ import { rgba } from 'polished';
3
+ import { Resizable } from 're-resizable';
4
+ import { useMemo } from 'react';
5
+ import styled from 'styled-components';
4
6
  import { IReqoreTableColumn, IReqoreTableSort } from '.';
7
+ import { ReqoreButton, ReqoreControlGroup, ReqoreDropdown } from '../..';
5
8
  import { IReqoreTheme } from '../../constants/theme';
6
- import { changeLightness, getReadableColor } from '../../helpers/colors';
7
- import { alignToFlexAlign } from '../../helpers/utils';
8
- import { useTooltip } from '../../hooks/useTooltip';
9
- import ReqoreIcon from '../Icon';
9
+ import { getReadableColor } from '../../helpers/colors';
10
+ import { IReqoreButtonProps } from '../Button';
11
+ import { IReqoreDropdownItem } from '../Dropdown/list';
12
+ import { TColumnsUpdater } from './header';
10
13
 
11
14
  export interface IReqoreTableHeaderCellProps
12
- extends React.HTMLAttributes<HTMLDivElement>,
13
- IReqoreTableColumn {
15
+ extends Omit<IReqoreTableColumn, 'cell'>,
16
+ IReqoreButtonProps {
14
17
  onSortChange?: (sort: string) => void;
15
- sortData: IReqoreTableSort;
18
+ sortData?: IReqoreTableSort;
19
+ onColumnsUpdate?: TColumnsUpdater;
20
+ onFilterChange?: (dataId: string, filter: string) => void;
21
+ actions?: IReqoreTableColumn['header']['actions'];
16
22
  }
17
23
 
18
24
  export interface IReqoreTableHeaderStyle {
@@ -23,95 +29,142 @@ export interface IReqoreTableHeaderStyle {
23
29
  interactive?: boolean;
24
30
  }
25
31
 
26
- export const StyledTableHeader = styled.div<IReqoreTableHeaderStyle>`
27
- ${({ width, grow }) =>
28
- css`
29
- width: ${!width || width < 80 ? 80 : width}px;
30
- flex-grow: ${grow || (width ? undefined : 1)};
31
- `};
32
-
33
- ${({ align, theme }) => css`
34
- background-color: ${changeLightness(theme.main, 0.03)};
35
- justify-content: ${align ? alignToFlexAlign(align) : 'flex-start'};
36
- `}
37
-
38
- ${({ interactive, theme }) =>
39
- interactive &&
40
- css`
41
- cursor: pointer;
42
- transition: background-color 0.2s ease-out;
43
-
44
- &:hover {
45
- color: ${getReadableColor(theme, undefined, undefined)};
46
- background-color: ${changeLightness(theme.main, 0.06)};
47
- }
48
- `};
49
- `;
50
-
51
- const StyledTableHeaderLabel = styled.span`
52
- overflow: hidden;
53
- text-overflow: ellipsis;
54
- white-space: nowrap;
32
+ export const StyledTableHeaderResize = styled.div`
33
+ &:before {
34
+ content: '';
35
+ position: absolute;
36
+ left: 50%;
37
+ top: 50%;
38
+ transform: translateX(-50%) translateY(-50%);
39
+ width: 1px;
40
+ height: 70%;
41
+ border-left: 1px dashed ${({ theme }) => rgba(getReadableColor(theme), 0.3)};
42
+ }
55
43
  `;
56
44
 
57
- const ReqoreTableHeaderCell = ({
45
+ export const ReqoreTableHeaderCell = ({
58
46
  width,
47
+ resizedWidth,
59
48
  grow,
60
49
  align,
61
- header,
62
50
  onSortChange,
63
51
  dataId,
64
52
  sortable,
65
53
  sortData,
66
- onClick,
67
54
  className,
68
- icon,
69
- iconSize,
70
- tooltip,
71
- ...props
55
+ onClick,
56
+ onColumnsUpdate,
57
+ resizable = true,
58
+ filterPlaceholder,
59
+ filterable,
60
+ hideable = true,
61
+ filter,
62
+ size,
63
+ onFilterChange,
64
+ actions,
65
+ ...rest
72
66
  }: IReqoreTableHeaderCellProps) => {
73
- const [ref, setRef] = useState(null);
67
+ const items = useMemo(() => {
68
+ let _items: IReqoreDropdownItem[] = [];
69
+
70
+ if (resizable || hideable) {
71
+ _items.push({
72
+ divider: true,
73
+ label: 'Options',
74
+ });
75
+
76
+ if (resizable) {
77
+ _items.push({
78
+ label: 'Reset size',
79
+ icon: 'HistoryLine',
80
+ disabled: !resizedWidth || width === resizedWidth,
81
+ onClick: () => {
82
+ onColumnsUpdate?.(dataId, 'resizedWidth', width);
83
+ },
84
+ });
85
+ }
86
+
87
+ if (hideable) {
88
+ _items.push({
89
+ label: 'Hide column',
90
+ icon: 'EyeCloseLine',
91
+ className: 'reqore-table-header-hide',
92
+ onClick: () => {
93
+ onColumnsUpdate?.(dataId, 'show', false);
94
+ },
95
+ });
96
+ }
97
+ }
74
98
 
75
- useTooltip(ref, { content: tooltip || header, delay: 300 });
99
+ if (actions) {
100
+ _items = [..._items, { divider: true, label: 'Other' }, ...actions];
101
+ }
102
+
103
+ return _items;
104
+ }, [resizable, hideable, width, resizedWidth, onColumnsUpdate, dataId]);
76
105
 
77
106
  return (
78
- <StyledTableHeader
79
- {...props}
80
- className={`${className || ''} reqore-table-header-cell`}
81
- ref={setRef}
82
- width={width}
83
- grow={grow}
84
- align={align}
85
- interactive={sortable || !!onClick}
86
- onClick={(event: React.MouseEvent<HTMLDivElement>) => {
87
- if (sortable) {
88
- onSortChange(dataId);
89
- }
90
- if (onClick) {
91
- onClick(event);
92
- }
107
+ <Resizable
108
+ minWidth={width}
109
+ onResize={(_event, _direction, _component) => {
110
+ onColumnsUpdate?.(dataId, 'resizedWidth', parseInt(_component.style.width));
111
+ }}
112
+ handleComponent={{
113
+ right: <StyledTableHeaderResize />,
114
+ }}
115
+ style={{
116
+ overflow: 'hidden',
117
+ flexGrow: grow,
118
+ }}
119
+ size={{
120
+ width: resizedWidth || width,
121
+ height: undefined,
122
+ }}
123
+ enable={{
124
+ right: resizable,
93
125
  }}
94
126
  >
95
- {icon && (
96
- <ReqoreIcon icon={icon} size={iconSize || '13px'} margin={header ? 'right' : undefined} />
97
- )}
98
- <StyledTableHeaderLabel>{header}</StyledTableHeaderLabel>
99
- {sortable && (
100
- <ReqoreIcon
101
- icon={
102
- `Arrow${sortData.direction === 'desc' ? 'Down' : 'Up'}Fill` as
103
- | 'ArrowDownFill'
104
- | 'ArrowUpFill'
127
+ <ReqoreControlGroup fluid stack rounded={false} fill style={{ height: '100%' }}>
128
+ <ReqoreButton
129
+ {...rest}
130
+ size={size}
131
+ readOnly={!sortable && !onClick}
132
+ className={`${className || ''} reqore-table-header-cell`}
133
+ rounded={false}
134
+ textAlign={align}
135
+ rightIcon={
136
+ sortable && sortData.by === dataId
137
+ ? (`Arrow${sortData.direction === 'desc' ? 'Down' : 'Up'}Fill` as
138
+ | 'ArrowDownFill'
139
+ | 'ArrowUpFill')
140
+ : rest.rightIcon
105
141
  }
106
- size={iconSize || '13px'}
107
- margin='left'
108
- style={{
109
- opacity: sortData.by === dataId ? 1 : 0.2,
142
+ onClick={(e) => {
143
+ if (sortable) {
144
+ onSortChange?.(dataId);
145
+ }
146
+
147
+ onClick?.(e);
110
148
  }}
111
149
  />
112
- )}
113
- </StyledTableHeader>
150
+ {filterable || hideable || resizable ? (
151
+ <ReqoreDropdown<IReqoreButtonProps>
152
+ icon='MoreLine'
153
+ className='reqore-table-header-cell-options'
154
+ fixed
155
+ size={size}
156
+ rounded={false}
157
+ intent={filter ? 'info' : undefined}
158
+ filterable={filterable}
159
+ filterPlaceholder={filterPlaceholder || 'Filter by this column...'}
160
+ filter={filter}
161
+ onFilterChange={(value) => {
162
+ onFilterChange?.(dataId, value);
163
+ }}
164
+ items={items}
165
+ />
166
+ ) : null}
167
+ </ReqoreControlGroup>
168
+ </Resizable>
114
169
  );
115
170
  };
116
-
117
- export default ReqoreTableHeaderCell;
@@ -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,209 @@ 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
+ columnModifiers: { [key: string]: { [key: string]: any } },
179
+ size: TSizes = 'normal'
180
+ ): IReqoreTableColumn[] => {
181
+ // We need to set the width of each column
182
+ return columns.map((column) => {
183
+ if (column.header.columns) {
184
+ return {
185
+ ...column,
186
+ header: {
187
+ ...column.header,
188
+ columns: prepareColumns(column.header.columns, columnModifiers, size),
189
+ },
190
+ };
191
+ }
192
+
193
+ let newWidth = calculateMinimumCellWidth(
194
+ column.width || 50,
195
+ size,
196
+ column.header.icon,
197
+ column.sortable,
198
+ column.filterable,
199
+ column.hideable,
200
+ column.resizable
201
+ );
202
+
203
+ return {
204
+ ...column,
205
+ width: newWidth,
206
+ ...(columnModifiers?.[column.dataId] || {}),
207
+ };
208
+ });
209
+ };
210
+
211
+ export const calculateMinimumCellWidth = (
212
+ currentWidth: number = 50,
213
+ size?: TSizes,
214
+ icon?: IReqoreIconName,
215
+ sortable?: boolean,
216
+ filterable?: boolean,
217
+ hideable?: boolean,
218
+ resizable?: boolean
219
+ ): number => {
220
+ let width = currentWidth;
221
+
222
+ if (icon) {
223
+ width += ICON_FROM_SIZE[size];
224
+ }
225
+
226
+ if (sortable) {
227
+ width += ICON_FROM_SIZE[size];
228
+ }
229
+
230
+ if (filterable || hideable || resizable) {
231
+ width += SIZE_TO_PX[size];
232
+ }
233
+
234
+ return width;
235
+ };