qbs-react-grid 2.2.17 → 2.2.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/css/qbs-react-grid.css +1 -1
- package/dist/css/qbs-react-grid.min.css +1 -1
- package/dist/css/qbs-react-grid.min.css.map +1 -1
- package/es/Cell.js +2 -1
- package/es/Pagination.d.ts +3 -0
- package/es/Pagination.js +8 -3
- package/es/Table.d.ts +3 -0
- package/es/Table.js +18 -12
- package/es/index.d.ts +1 -1
- package/es/index.js +1 -1
- package/es/less/pagination.less +9 -9
- package/es/qbsTable/QbsTable.js +110 -73
- package/es/qbsTable/TableCardList.js +2 -0
- package/es/qbsTable/Toolbar.js +6 -3
- package/es/qbsTable/commontypes.d.ts +21 -12
- package/es/qbsTable/labels.d.ts +6 -2
- package/es/qbsTable/labels.js +10 -1
- package/es/qbsTable/utilities/CardComponent.d.ts +2 -0
- package/es/qbsTable/utilities/CardComponent.js +7 -3
- package/es/qbsTable/utilities/CardMenuDropdown.d.ts +2 -0
- package/es/qbsTable/utilities/CardMenuDropdown.js +7 -7
- package/es/qbsTable/utilities/SearchInput.d.ts +1 -0
- package/es/qbsTable/utilities/SearchInput.js +3 -1
- package/es/qbsTable/utilities/VerticalDropDownMenu.js +36 -23
- package/es/qbsTable/utilities/empty.js +1 -1
- package/es/qbsTable/utilities/tablecalc.d.ts +1 -1
- package/es/qbsTable/utilities/tablecalc.js +7 -2
- package/es/utils/useCellDescriptor.js +0 -1
- package/es/utils/useScrollListener.d.ts +1 -0
- package/es/utils/useScrollListener.js +5 -3
- package/lib/Cell.js +2 -1
- package/lib/Pagination.d.ts +3 -0
- package/lib/Pagination.js +8 -3
- package/lib/Table.d.ts +3 -0
- package/lib/Table.js +18 -12
- package/lib/index.d.ts +1 -1
- package/lib/index.js +3 -1
- package/lib/less/pagination.less +9 -9
- package/lib/qbsTable/QbsTable.js +110 -73
- package/lib/qbsTable/TableCardList.js +2 -0
- package/lib/qbsTable/Toolbar.js +6 -3
- package/lib/qbsTable/commontypes.d.ts +21 -12
- package/lib/qbsTable/labels.d.ts +6 -2
- package/lib/qbsTable/labels.js +13 -2
- package/lib/qbsTable/utilities/CardComponent.d.ts +2 -0
- package/lib/qbsTable/utilities/CardComponent.js +7 -3
- package/lib/qbsTable/utilities/CardMenuDropdown.d.ts +2 -0
- package/lib/qbsTable/utilities/CardMenuDropdown.js +6 -5
- package/lib/qbsTable/utilities/SearchInput.d.ts +1 -0
- package/lib/qbsTable/utilities/SearchInput.js +3 -1
- package/lib/qbsTable/utilities/VerticalDropDownMenu.js +36 -23
- package/lib/qbsTable/utilities/empty.js +1 -1
- package/lib/qbsTable/utilities/tablecalc.d.ts +1 -1
- package/lib/qbsTable/utilities/tablecalc.js +7 -2
- package/lib/utils/useCellDescriptor.js +0 -1
- package/lib/utils/useScrollListener.d.ts +1 -0
- package/lib/utils/useScrollListener.js +5 -3
- package/package.json +2 -2
- package/src/Cell.tsx +3 -1
- package/src/HeaderCell.tsx +0 -1
- package/src/Pagination.tsx +10 -3
- package/src/Table.tsx +23 -10
- package/src/customSelect.tsx +88 -88
- package/src/index.ts +2 -0
- package/src/less/pagination.less +9 -9
- package/src/qbsTable/QbsTable.tsx +84 -39
- package/src/qbsTable/TableCardList.tsx +2 -0
- package/src/qbsTable/Toolbar.tsx +4 -2
- package/src/qbsTable/commontypes.ts +21 -12
- package/src/qbsTable/labels.ts +9 -2
- package/src/qbsTable/utilities/CardComponent.tsx +7 -2
- package/src/qbsTable/utilities/CardMenuDropdown.tsx +11 -6
- package/src/qbsTable/utilities/SearchInput.tsx +3 -1
- package/src/qbsTable/utilities/VerticalDropDownMenu.tsx +42 -30
- package/src/qbsTable/utilities/empty.tsx +2 -2
- package/src/qbsTable/utilities/tablecalc.ts +8 -2
- package/src/utils/useCellDescriptor.ts +0 -1
- package/src/utils/useScrollListener.ts +13 -3
- package/src/utils/useTableRows.ts +1 -1
package/src/Table.tsx
CHANGED
|
@@ -224,6 +224,7 @@ export interface TableProps<Row, Key> extends Omit<StandardProps, 'onScroll'> {
|
|
|
224
224
|
expanded?: boolean
|
|
225
225
|
) => React.ReactNode;
|
|
226
226
|
tableKey?: string;
|
|
227
|
+
infiniteLoading?: boolean;
|
|
227
228
|
/** Customize what you can do to expand a zone */
|
|
228
229
|
renderRowExpanded?: (rowData?: Row) => React.ReactNode;
|
|
229
230
|
|
|
@@ -259,13 +260,16 @@ export interface TableProps<Row, Key> extends Omit<StandardProps, 'onScroll'> {
|
|
|
259
260
|
|
|
260
261
|
/** Callback for the `touchend` event. */
|
|
261
262
|
onTouchEnd?: (event: React.TouchEvent) => void;
|
|
262
|
-
|
|
263
|
+
handleInfiniteScroll?: (value: number) => void;
|
|
263
264
|
/**
|
|
264
265
|
* Callback after table data update.
|
|
265
266
|
* @deprecated use `shouldUpdateScroll` instead
|
|
266
267
|
**/
|
|
267
268
|
onDataUpdated?: (nextData: Row[], scrollTo: (coord: { x: number; y: number }) => void) => void;
|
|
268
269
|
tableBodyRef: React.RefObject<HTMLDivElement>;
|
|
270
|
+
|
|
271
|
+
wheelWrapperRef: React.RefObject<HTMLDivElement | null>;
|
|
272
|
+
|
|
269
273
|
bodyRef?: (ref: HTMLElement) => void;
|
|
270
274
|
}
|
|
271
275
|
|
|
@@ -302,8 +306,7 @@ const Table = React.forwardRef(<Row extends RowDataType, Key>(props: TableProps<
|
|
|
302
306
|
loading: 'Loading...'
|
|
303
307
|
},
|
|
304
308
|
showHeader = true,
|
|
305
|
-
|
|
306
|
-
paginationProps = {},
|
|
309
|
+
|
|
307
310
|
sortColumn,
|
|
308
311
|
rowHeight = ROW_HEIGHT,
|
|
309
312
|
sortType: sortTypeProp,
|
|
@@ -335,21 +338,21 @@ const Table = React.forwardRef(<Row extends RowDataType, Key>(props: TableProps<
|
|
|
335
338
|
onTouchStart,
|
|
336
339
|
onTouchMove,
|
|
337
340
|
onTouchEnd,
|
|
338
|
-
dataTheme,
|
|
339
341
|
tableBodyHeight,
|
|
340
342
|
columns,
|
|
341
343
|
tableBodyRef,
|
|
342
344
|
tableKey,
|
|
345
|
+
handleInfiniteScroll,
|
|
346
|
+
infiniteLoading,
|
|
347
|
+
wheelWrapperRef,
|
|
343
348
|
...rest
|
|
344
349
|
} = props;
|
|
345
|
-
|
|
346
350
|
const {
|
|
347
351
|
withClassPrefix,
|
|
348
352
|
merge: mergeCls,
|
|
349
353
|
prefix
|
|
350
354
|
} = useClassNames(classPrefix, typeof classPrefix !== 'undefined');
|
|
351
355
|
const childTableRef = useRef<HTMLDivElement>(null);
|
|
352
|
-
|
|
353
356
|
// Use `forceUpdate` to force the component to re-render after manipulating the DOM.
|
|
354
357
|
const [, forceUpdate] = useReducer(x => x + 1, 0);
|
|
355
358
|
const [expandedRowKeys, setExpandedRowKeys] = useControlled(
|
|
@@ -408,7 +411,6 @@ const Table = React.forwardRef(<Row extends RowDataType, Key>(props: TableProps<
|
|
|
408
411
|
const affixHeaderWrapperRef = useRef<HTMLDivElement>(null);
|
|
409
412
|
const headerWrapperRef = useRef<HTMLDivElement>(null);
|
|
410
413
|
// const tableBodyRef = useRef<HTMLDivElement>(null);
|
|
411
|
-
const wheelWrapperRef = useRef<HTMLDivElement>(null);
|
|
412
414
|
const scrollbarXRef = useRef<ScrollbarInstance>(null);
|
|
413
415
|
const scrollbarYRef = useRef<ScrollbarInstance>(null);
|
|
414
416
|
|
|
@@ -533,7 +535,8 @@ const Table = React.forwardRef(<Row extends RowDataType, Key>(props: TableProps<
|
|
|
533
535
|
onScroll,
|
|
534
536
|
onTouchStart,
|
|
535
537
|
onTouchMove,
|
|
536
|
-
onTouchEnd
|
|
538
|
+
onTouchEnd,
|
|
539
|
+
handleInfiniteScroll
|
|
537
540
|
});
|
|
538
541
|
|
|
539
542
|
const { headerCells, bodyCells, allColumnsWidth, hasCustomTreeCol } = useCellDescriptor({
|
|
@@ -953,7 +956,9 @@ const Table = React.forwardRef(<Row extends RowDataType, Key>(props: TableProps<
|
|
|
953
956
|
tableId={id}
|
|
954
957
|
style={{ width: tableWidth.current }}
|
|
955
958
|
length={tableWidth.current}
|
|
956
|
-
onScroll={
|
|
959
|
+
onScroll={delta => {
|
|
960
|
+
onScrollHorizontal(delta);
|
|
961
|
+
}}
|
|
957
962
|
scrollLength={contentWidth.current}
|
|
958
963
|
ref={scrollbarXRef}
|
|
959
964
|
/>
|
|
@@ -1132,13 +1137,21 @@ const Table = React.forwardRef(<Row extends RowDataType, Key>(props: TableProps<
|
|
|
1132
1137
|
role="rowgroup"
|
|
1133
1138
|
className={prefix('body-row-wrapper')}
|
|
1134
1139
|
style={bodyStyles}
|
|
1135
|
-
onScroll={
|
|
1140
|
+
onScroll={e => {
|
|
1141
|
+
onScrollBody?.(e); // existing handler
|
|
1142
|
+
}}
|
|
1136
1143
|
>
|
|
1137
1144
|
{!loading && (
|
|
1138
1145
|
<div style={wheelStyles} className={prefix('body-wheel-area')} ref={wheelWrapperRef}>
|
|
1139
1146
|
{topHideHeight ? <Row style={topRowStyles} className="virtualized" /> : null}
|
|
1140
1147
|
{visibleRows.current}
|
|
1141
1148
|
{bottomHideHeight ? <Row style={bottomRowStyles} className="virtualized" /> : null}
|
|
1149
|
+
|
|
1150
|
+
{infiniteLoading && (
|
|
1151
|
+
<div style={{ padding: 12, textAlign: 'center' }}>
|
|
1152
|
+
<span>Loading more rows…</span>
|
|
1153
|
+
</div>
|
|
1154
|
+
)}
|
|
1142
1155
|
</div>
|
|
1143
1156
|
)}
|
|
1144
1157
|
|
package/src/customSelect.tsx
CHANGED
|
@@ -1,88 +1,88 @@
|
|
|
1
|
-
import React, { FC, useState, useEffect, useRef } from 'react';
|
|
2
|
-
|
|
3
|
-
type CustomSelectProps = {
|
|
4
|
-
options: number[];
|
|
5
|
-
selectedValue: number;
|
|
6
|
-
onChange: (value: number) => void;
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
const CustomSelect: FC<CustomSelectProps> = ({ options, selectedValue, onChange }) => {
|
|
10
|
-
const [isOpen, setIsOpen] = useState(false);
|
|
11
|
-
const [dropdownPosition, setDropdownPosition] = useState('bottom');
|
|
12
|
-
const ref = useRef<HTMLDivElement>(null);
|
|
13
|
-
const inputRef = useRef<HTMLDivElement>(null);
|
|
14
|
-
|
|
15
|
-
const adjustDropdownPosition = () => {
|
|
16
|
-
if (inputRef.current) {
|
|
17
|
-
const inputBoxRect = inputRef.current.getBoundingClientRect();
|
|
18
|
-
const viewportHeight = window.innerHeight;
|
|
19
|
-
|
|
20
|
-
const spaceAbove = inputBoxRect.top;
|
|
21
|
-
const spaceBelow = viewportHeight - inputBoxRect.bottom;
|
|
22
|
-
|
|
23
|
-
if (spaceAbove > spaceBelow) {
|
|
24
|
-
setDropdownPosition('top');
|
|
25
|
-
} else {
|
|
26
|
-
setDropdownPosition('bottom');
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
const handleToggle = () => {
|
|
32
|
-
setIsOpen(prevIsOpen => !prevIsOpen);
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
const handleSelect = (value: number) => {
|
|
36
|
-
onChange(value);
|
|
37
|
-
setIsOpen(false);
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
const handleClickOutside = (event: MouseEvent) => {
|
|
41
|
-
if (ref.current && !ref.current.contains(event.target as Node)) {
|
|
42
|
-
setIsOpen(false);
|
|
43
|
-
}
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
useEffect(() => {
|
|
47
|
-
if (isOpen) {
|
|
48
|
-
adjustDropdownPosition();
|
|
49
|
-
window.addEventListener('resize', adjustDropdownPosition);
|
|
50
|
-
} else {
|
|
51
|
-
window.removeEventListener('resize', adjustDropdownPosition);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return () => {
|
|
55
|
-
window.removeEventListener('resize', adjustDropdownPosition);
|
|
56
|
-
};
|
|
57
|
-
}, [isOpen]);
|
|
58
|
-
|
|
59
|
-
useEffect(() => {
|
|
60
|
-
document.addEventListener('mousedown', handleClickOutside);
|
|
61
|
-
return () => {
|
|
62
|
-
document.removeEventListener('mousedown', handleClickOutside);
|
|
63
|
-
};
|
|
64
|
-
}, []);
|
|
65
|
-
|
|
66
|
-
return (
|
|
67
|
-
<div className="custom-select" ref={ref}>
|
|
68
|
-
<div className="custom-select-trigger" onClick={handleToggle} ref={inputRef}>
|
|
69
|
-
{selectedValue}
|
|
70
|
-
</div>
|
|
71
|
-
{isOpen && (
|
|
72
|
-
<ul className={`custom-select-options ${dropdownPosition}`}>
|
|
73
|
-
{options.map(option => (
|
|
74
|
-
<li
|
|
75
|
-
key={option}
|
|
76
|
-
className={`custom-select-option ${option === selectedValue ? 'selected' : ''}`}
|
|
77
|
-
onClick={() => handleSelect(option)}
|
|
78
|
-
>
|
|
79
|
-
{option}
|
|
80
|
-
</li>
|
|
81
|
-
))}
|
|
82
|
-
</ul>
|
|
83
|
-
)}
|
|
84
|
-
</div>
|
|
85
|
-
);
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
export default CustomSelect;
|
|
1
|
+
import React, { FC, useState, useEffect, useRef } from 'react';
|
|
2
|
+
|
|
3
|
+
type CustomSelectProps = {
|
|
4
|
+
options: number[];
|
|
5
|
+
selectedValue: number;
|
|
6
|
+
onChange: (value: number) => void;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const CustomSelect: FC<CustomSelectProps> = ({ options, selectedValue, onChange }) => {
|
|
10
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
11
|
+
const [dropdownPosition, setDropdownPosition] = useState('bottom');
|
|
12
|
+
const ref = useRef<HTMLDivElement>(null);
|
|
13
|
+
const inputRef = useRef<HTMLDivElement>(null);
|
|
14
|
+
|
|
15
|
+
const adjustDropdownPosition = () => {
|
|
16
|
+
if (inputRef.current) {
|
|
17
|
+
const inputBoxRect = inputRef.current.getBoundingClientRect();
|
|
18
|
+
const viewportHeight = window.innerHeight;
|
|
19
|
+
|
|
20
|
+
const spaceAbove = inputBoxRect.top;
|
|
21
|
+
const spaceBelow = viewportHeight - inputBoxRect.bottom;
|
|
22
|
+
|
|
23
|
+
if (spaceAbove > spaceBelow) {
|
|
24
|
+
setDropdownPosition('top');
|
|
25
|
+
} else {
|
|
26
|
+
setDropdownPosition('bottom');
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const handleToggle = () => {
|
|
32
|
+
setIsOpen(prevIsOpen => !prevIsOpen);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const handleSelect = (value: number) => {
|
|
36
|
+
onChange(value);
|
|
37
|
+
setIsOpen(false);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const handleClickOutside = (event: MouseEvent) => {
|
|
41
|
+
if (ref.current && !ref.current.contains(event.target as Node)) {
|
|
42
|
+
setIsOpen(false);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
if (isOpen) {
|
|
48
|
+
adjustDropdownPosition();
|
|
49
|
+
window.addEventListener('resize', adjustDropdownPosition);
|
|
50
|
+
} else {
|
|
51
|
+
window.removeEventListener('resize', adjustDropdownPosition);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return () => {
|
|
55
|
+
window.removeEventListener('resize', adjustDropdownPosition);
|
|
56
|
+
};
|
|
57
|
+
}, [isOpen]);
|
|
58
|
+
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
document.addEventListener('mousedown', handleClickOutside);
|
|
61
|
+
return () => {
|
|
62
|
+
document.removeEventListener('mousedown', handleClickOutside);
|
|
63
|
+
};
|
|
64
|
+
}, []);
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<div className="custom-select" ref={ref}>
|
|
68
|
+
<div className="custom-select-trigger" onClick={handleToggle} ref={inputRef}>
|
|
69
|
+
{selectedValue}
|
|
70
|
+
</div>
|
|
71
|
+
{isOpen && (
|
|
72
|
+
<ul className={`custom-select-options ${dropdownPosition}`}>
|
|
73
|
+
{options.map(option => (
|
|
74
|
+
<li
|
|
75
|
+
key={option}
|
|
76
|
+
className={`custom-select-option ${option === selectedValue ? 'selected' : ''}`}
|
|
77
|
+
onClick={() => handleSelect(option)}
|
|
78
|
+
>
|
|
79
|
+
{option}
|
|
80
|
+
</li>
|
|
81
|
+
))}
|
|
82
|
+
</ul>
|
|
83
|
+
)}
|
|
84
|
+
</div>
|
|
85
|
+
);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export default CustomSelect;
|
package/src/index.ts
CHANGED
|
@@ -15,7 +15,9 @@ export type { QbsTableProps } from './qbsTable/commontypes';
|
|
|
15
15
|
export type { QbsTableLabels } from './qbsTable/labels';
|
|
16
16
|
export {
|
|
17
17
|
mergeQbsTableLabels,
|
|
18
|
+
mergeLabels,
|
|
18
19
|
defaultQbsTableLabels,
|
|
20
|
+
DEFAULT_QBS_TABLE_LABELS,
|
|
19
21
|
formatSelectedItems,
|
|
20
22
|
} from './qbsTable/labels';
|
|
21
23
|
export type {
|
package/src/less/pagination.less
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
min-height: 40px;
|
|
6
6
|
align-items: center;
|
|
7
7
|
border-radius: 0 0 4px 4px;
|
|
8
|
-
border-top: 1px solid #d6d8dc;
|
|
9
|
-
background: #fff;
|
|
8
|
+
border-top: 1px solid var(--base-gray-border, #d6d8dc);
|
|
9
|
+
background: var(--table-bg, #fff);
|
|
10
10
|
&-header {
|
|
11
11
|
border-bottom: 1px solid #eee;
|
|
12
12
|
position: absolute;
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
font-weight: 500;
|
|
28
28
|
line-height: 20px;
|
|
29
29
|
border-radius: 6px;
|
|
30
|
-
border: 1px solid #d6d8dc;
|
|
31
|
-
background: #fff;
|
|
30
|
+
border: 1px solid var(--base-gray-border, #d6d8dc);
|
|
31
|
+
background: var(--gray-light-1, #fff);
|
|
32
32
|
}
|
|
33
33
|
.qbs-table-icon-container {
|
|
34
34
|
padding: 0;
|
|
@@ -109,7 +109,7 @@
|
|
|
109
109
|
font-style: normal;
|
|
110
110
|
font-weight: 500;
|
|
111
111
|
line-height: 20px;
|
|
112
|
-
color: #313131;
|
|
112
|
+
color: var(--gray-dark-3, #313131);
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
115
|
|
|
@@ -125,7 +125,7 @@
|
|
|
125
125
|
.custom-select-trigger {
|
|
126
126
|
padding: 2px 8px;
|
|
127
127
|
cursor: pointer;
|
|
128
|
-
border: 1px solid #d6d8dc;
|
|
128
|
+
border: 1px solid var(--base-gray-border, #d6d8dc);
|
|
129
129
|
border-radius: 4px;
|
|
130
130
|
min-width: 65px;
|
|
131
131
|
position: relative;
|
|
@@ -140,7 +140,7 @@
|
|
|
140
140
|
list-style: none;
|
|
141
141
|
margin: 2px 0 0;
|
|
142
142
|
padding: 0;
|
|
143
|
-
background: #fff;
|
|
143
|
+
background: var(--gray-light-1, #fff);
|
|
144
144
|
border-radius: 8px;
|
|
145
145
|
box-shadow: 0 8px 20px 0 #00000026;
|
|
146
146
|
}
|
|
@@ -158,7 +158,7 @@ ul.custom-select-options.top {
|
|
|
158
158
|
|
|
159
159
|
.custom-select-option:hover,
|
|
160
160
|
.custom-select-option.selected {
|
|
161
|
-
background-color: #f0f0f0;
|
|
161
|
+
background-color: var(--table-row-hover-bg, #f0f0f0);
|
|
162
162
|
}
|
|
163
163
|
|
|
164
164
|
.custom-select-trigger:before {
|
|
@@ -167,7 +167,7 @@ ul.custom-select-options.top {
|
|
|
167
167
|
right: 10px;
|
|
168
168
|
top: 6px;
|
|
169
169
|
font-weight: bold;
|
|
170
|
-
border: solid black;
|
|
170
|
+
border: solid var(--gray-dark-3, black);
|
|
171
171
|
border-width: 0 1px 1px 0;
|
|
172
172
|
display: inline-block;
|
|
173
173
|
padding: 3px;
|
|
@@ -6,6 +6,7 @@ import ColumnGroup from '../ColumnGroup';
|
|
|
6
6
|
import HeaderCell from '../HeaderCell';
|
|
7
7
|
import Pagination from '../Pagination';
|
|
8
8
|
import Table from '../Table';
|
|
9
|
+
import isRTL from '../utils/isRTL';
|
|
9
10
|
import useResponsiveStore from '../utils/useResponsiveStore';
|
|
10
11
|
import { QbsColumnProps, QbsTableProps } from './commontypes';
|
|
11
12
|
import { mergeQbsTableLabels } from './labels';
|
|
@@ -31,6 +32,7 @@ import '../../dist/css/qbs-react-grid.css';
|
|
|
31
32
|
|
|
32
33
|
const CHECKBOX_LINE_HEIGHT = '36px';
|
|
33
34
|
const COLUMN_WIDTH = 250;
|
|
35
|
+
let REFRESH_KEY = 0;
|
|
34
36
|
const QbsTable: React.FC<QbsTableProps> = ({
|
|
35
37
|
handleColumnSort,
|
|
36
38
|
data,
|
|
@@ -52,7 +54,7 @@ const QbsTable: React.FC<QbsTableProps> = ({
|
|
|
52
54
|
minHeight,
|
|
53
55
|
height = 630,
|
|
54
56
|
onExpandChange,
|
|
55
|
-
wordWrap,
|
|
57
|
+
wordWrap: propsWordWrap,
|
|
56
58
|
dataRowKey = 'id',
|
|
57
59
|
defaultExpandAllRows,
|
|
58
60
|
handleRowExpanded,
|
|
@@ -60,6 +62,7 @@ const QbsTable: React.FC<QbsTableProps> = ({
|
|
|
60
62
|
rowExpand = false,
|
|
61
63
|
actionProps = [],
|
|
62
64
|
theme,
|
|
65
|
+
rtl: rtlProp,
|
|
63
66
|
handleMenuActions,
|
|
64
67
|
onRowClick,
|
|
65
68
|
expandedRowKeys,
|
|
@@ -93,33 +96,42 @@ const QbsTable: React.FC<QbsTableProps> = ({
|
|
|
93
96
|
isCustomTableCardView = false,
|
|
94
97
|
handleTableCardView,
|
|
95
98
|
handleCustomCardLoader,
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
+
hasMoreData,
|
|
100
|
+
loadMoreData,
|
|
101
|
+
infiniteLoading,
|
|
102
|
+
infiniteScroll = false,
|
|
99
103
|
rowViewToggle = false,
|
|
100
104
|
defaultRowView = true,
|
|
101
105
|
fullWidthView = false,
|
|
102
106
|
setTableFullView,
|
|
103
107
|
setRowViewToggle,
|
|
104
|
-
|
|
108
|
+
dropType = 'horizondal',
|
|
105
109
|
rowHeight,
|
|
110
|
+
isFullScreen,
|
|
106
111
|
showHeader = true,
|
|
112
|
+
labels: labelsProp
|
|
107
113
|
}) => {
|
|
108
114
|
const labels = useMemo(() => mergeQbsTableLabels(labelsProp), [labelsProp]);
|
|
109
|
-
const
|
|
110
|
-
if (!rowViewToggle) return wordWrap;
|
|
111
|
-
return defaultRowView ? false : 'break-word';
|
|
112
|
-
}, [rowViewToggle, defaultRowView, wordWrap]);
|
|
115
|
+
const [rowViewRefreshKey, setRowViewRefreshKey] = useState(0);
|
|
113
116
|
const [loading, setLoading] = useState(false);
|
|
114
117
|
const [columns, setColumns] = useState(propColumn);
|
|
115
118
|
const [checkedKeys, setCheckedKeys] = useState<(number | string)[]>([]);
|
|
116
|
-
const dataTheme = useMemo(
|
|
119
|
+
const dataTheme = useMemo(
|
|
120
|
+
() => theme ?? (typeof localStorage !== 'undefined' ? localStorage.getItem('theme') : null) ?? 'light',
|
|
121
|
+
[theme]
|
|
122
|
+
);
|
|
123
|
+
const rtl = rtlProp ?? isRTL();
|
|
117
124
|
const [isOpen, setIsOpen] = useState(false);
|
|
118
125
|
const prevColumns = useRef<any | null>(null);
|
|
119
126
|
const [tableViewToggle, setTableViewToggle] = useState(tableView);
|
|
120
127
|
const isMobile = useResponsiveStore();
|
|
121
128
|
const tableBodyRef = useRef<HTMLDivElement>(null);
|
|
122
|
-
const
|
|
129
|
+
const wheelWrapperRef = useRef<HTMLDivElement>(null);
|
|
130
|
+
const [wordWrap, setWordWrap] = useState(propsWordWrap ?? false);
|
|
131
|
+
const effectiveWordWrap = useMemo(() => {
|
|
132
|
+
if (!rowViewToggle) return wordWrap;
|
|
133
|
+
return defaultRowView ? false : 'break-word';
|
|
134
|
+
}, [rowViewToggle, defaultRowView, wordWrap]);
|
|
123
135
|
|
|
124
136
|
useEffect(() => {
|
|
125
137
|
if (rowViewToggle) {
|
|
@@ -178,16 +190,6 @@ const QbsTable: React.FC<QbsTableProps> = ({
|
|
|
178
190
|
[checkedKeys]
|
|
179
191
|
);
|
|
180
192
|
|
|
181
|
-
const rowKeyField = dataRowKey ?? 'id';
|
|
182
|
-
const getRowClassName = useCallback(
|
|
183
|
-
(rowData: Record<string, unknown>) => {
|
|
184
|
-
if (!selection) return '';
|
|
185
|
-
const key = rowData?.[rowKeyField] as string | number | undefined;
|
|
186
|
-
return key !== undefined && checkedKeys?.includes(key) ? 'qbs-table-row-checked' : '';
|
|
187
|
-
},
|
|
188
|
-
[selection, checkedKeys, rowKeyField]
|
|
189
|
-
);
|
|
190
|
-
|
|
191
193
|
const handleToggle = useCallback(
|
|
192
194
|
(columnName: string) => {
|
|
193
195
|
let lastVisibleColumn: any = null;
|
|
@@ -223,6 +225,7 @@ const QbsTable: React.FC<QbsTableProps> = ({
|
|
|
223
225
|
|
|
224
226
|
const handleColumnWidth = useCallback((newWidth?: number, dataKey?: any) => {
|
|
225
227
|
if (newWidth === undefined || dataKey === undefined) return;
|
|
228
|
+
REFRESH_KEY = REFRESH_KEY + 1;
|
|
226
229
|
setColumns(prevColumns =>
|
|
227
230
|
prevColumns.map(column =>
|
|
228
231
|
column.field === dataKey ? { ...column, colWidth: newWidth } : column
|
|
@@ -239,6 +242,14 @@ const QbsTable: React.FC<QbsTableProps> = ({
|
|
|
239
242
|
);
|
|
240
243
|
}
|
|
241
244
|
}, [wordWrap]);
|
|
245
|
+
useEffect(() => {
|
|
246
|
+
if (!defaultRowView) {
|
|
247
|
+
setWordWrap('break-word');
|
|
248
|
+
} else {
|
|
249
|
+
setWordWrap(false);
|
|
250
|
+
}
|
|
251
|
+
REFRESH_KEY = REFRESH_KEY + 1;
|
|
252
|
+
}, [defaultRowView]);
|
|
242
253
|
|
|
243
254
|
const onReorder = useCallback((columns: QbsColumnProps[]) => {
|
|
244
255
|
setColumns(columns);
|
|
@@ -246,7 +257,6 @@ const QbsTable: React.FC<QbsTableProps> = ({
|
|
|
246
257
|
|
|
247
258
|
// useEffect(() => {
|
|
248
259
|
// }, [columns]);
|
|
249
|
-
|
|
250
260
|
const handleClear = ([]) => {
|
|
251
261
|
setCheckedKeys([]);
|
|
252
262
|
handleChecked([]);
|
|
@@ -277,18 +287,31 @@ const QbsTable: React.FC<QbsTableProps> = ({
|
|
|
277
287
|
setTableViewToggle: setTableViewToggle,
|
|
278
288
|
tableViewToggle: tableViewToggle,
|
|
279
289
|
enableTableToggle: enableTableToggle,
|
|
280
|
-
rowViewToggle,
|
|
281
|
-
defaultRowView,
|
|
282
|
-
fullWidthView,
|
|
283
|
-
setTableFullView,
|
|
284
|
-
setRowViewToggle,
|
|
285
|
-
isFullScreen,
|
|
290
|
+
rowViewToggle: rowViewToggle,
|
|
291
|
+
defaultRowView: defaultRowView,
|
|
292
|
+
fullWidthView: fullWidthView,
|
|
293
|
+
setTableFullView: setTableFullView,
|
|
294
|
+
setRowViewToggle: setRowViewToggle,
|
|
295
|
+
isFullScreen: isFullScreen,
|
|
286
296
|
labels,
|
|
287
297
|
rtl,
|
|
288
298
|
};
|
|
289
|
-
const themeToggle = useMemo(() => document.getElementById('themeToggle') as HTMLInputElement, []);
|
|
290
299
|
|
|
291
300
|
useEffect(() => {
|
|
301
|
+
if (!dataTheme || typeof document === 'undefined') return;
|
|
302
|
+
|
|
303
|
+
document.body.setAttribute('data-theme', dataTheme === 'dark' ? 'dark' : 'light');
|
|
304
|
+
document.documentElement.dataset.theme = dataTheme;
|
|
305
|
+
}, [dataTheme]);
|
|
306
|
+
|
|
307
|
+
const themeToggle = useMemo(
|
|
308
|
+
() => (typeof document !== 'undefined' ? document.getElementById('themeToggle') : null),
|
|
309
|
+
[]
|
|
310
|
+
) as HTMLInputElement | null;
|
|
311
|
+
|
|
312
|
+
useEffect(() => {
|
|
313
|
+
if (theme || typeof document === 'undefined') return;
|
|
314
|
+
|
|
292
315
|
const handleThemeToggle = () => {
|
|
293
316
|
if (themeToggle?.checked) {
|
|
294
317
|
document.body.setAttribute('data-theme', 'dark');
|
|
@@ -313,11 +336,10 @@ const QbsTable: React.FC<QbsTableProps> = ({
|
|
|
313
336
|
themeToggle?.removeEventListener('change', handleThemeToggle);
|
|
314
337
|
document.removeEventListener('DOMContentLoaded', handleDOMContentLoaded);
|
|
315
338
|
};
|
|
316
|
-
}, [themeToggle]);
|
|
339
|
+
}, [theme, themeToggle]);
|
|
317
340
|
|
|
318
341
|
const handleExpanded = useCallback(
|
|
319
342
|
(rowData: any) => {
|
|
320
|
-
console.log(rowData);
|
|
321
343
|
const keyValue = dataRowKey as string;
|
|
322
344
|
const key = rowData[keyValue];
|
|
323
345
|
|
|
@@ -607,8 +629,25 @@ const QbsTable: React.FC<QbsTableProps> = ({
|
|
|
607
629
|
[columns, dataTheme]
|
|
608
630
|
);
|
|
609
631
|
|
|
632
|
+
const handleInfiniteScroll = (scroll: number) => {
|
|
633
|
+
if (!infiniteScroll) return;
|
|
634
|
+
const wrapper = wheelWrapperRef.current;
|
|
635
|
+
if (!wrapper || !hasMoreData || infiniteLoading) return;
|
|
636
|
+
|
|
637
|
+
const { scrollTop, clientHeight } = wrapper;
|
|
638
|
+
const scrollHeight = Math.abs(scroll) + (height - headerHeight);
|
|
639
|
+
// Trigger fetch when user scrolls within 100px of bottom
|
|
640
|
+
if (scrollTop + clientHeight >= scrollHeight - 70) {
|
|
641
|
+
loadMoreData?.(); // fetch next page here
|
|
642
|
+
}
|
|
643
|
+
};
|
|
644
|
+
|
|
610
645
|
return (
|
|
611
|
-
<div
|
|
646
|
+
<div
|
|
647
|
+
className={`qbs-table ${classes.tableContainerClass}`}
|
|
648
|
+
data-theme={dataTheme}
|
|
649
|
+
dir={rtl ? 'rtl' : 'ltr'}
|
|
650
|
+
>
|
|
612
651
|
{toolbar && <ToolBar {...toolbarProps} />}
|
|
613
652
|
<div className="qbs-table-border-wrap">
|
|
614
653
|
{tableViewToggle ? (
|
|
@@ -616,16 +655,20 @@ const QbsTable: React.FC<QbsTableProps> = ({
|
|
|
616
655
|
height={autoHeight ? undefined : height}
|
|
617
656
|
key={`${tableKey}-${rowViewRefreshKey}`}
|
|
618
657
|
tableKey={tableKey}
|
|
619
|
-
rtl={rtl}
|
|
620
658
|
data={data}
|
|
659
|
+
rtl={rtl}
|
|
621
660
|
tableBodyRef={tableBodyRef as React.RefObject<HTMLDivElement>}
|
|
622
661
|
dataTheme={dataTheme}
|
|
623
662
|
wordWrap={effectiveWordWrap}
|
|
663
|
+
wheelWrapperRef={wheelWrapperRef}
|
|
664
|
+
rowHeight={rowHeight}
|
|
624
665
|
autoHeight={autoHeight}
|
|
666
|
+
handleInfiniteScroll={handleInfiniteScroll}
|
|
625
667
|
sortColumn={sortColumn}
|
|
626
668
|
style={{ position: 'relative' }}
|
|
627
669
|
sortType={sortType}
|
|
628
670
|
onSortColumn={handleSortColumn}
|
|
671
|
+
infiniteLoading={infiniteLoading}
|
|
629
672
|
onRowClick={onRowClick}
|
|
630
673
|
tableBodyHeight={tableBodyHeight}
|
|
631
674
|
cellBordered={cellBordered}
|
|
@@ -634,13 +677,12 @@ const QbsTable: React.FC<QbsTableProps> = ({
|
|
|
634
677
|
renderEmpty ? (
|
|
635
678
|
renderEmpty(info)
|
|
636
679
|
) : (
|
|
637
|
-
<NoData title={emptyTitle ??
|
|
680
|
+
<NoData title={emptyTitle ?? labels.noDataFound} subtitle={emptySubTitle} />
|
|
638
681
|
)
|
|
639
682
|
}
|
|
640
683
|
columns={columns}
|
|
641
684
|
minHeight={minHeight}
|
|
642
685
|
headerHeight={headerHeight}
|
|
643
|
-
rowHeight={rowHeight}
|
|
644
686
|
rowExpandedHeight={rowExpandedHeight}
|
|
645
687
|
loading={isLoading ?? loading}
|
|
646
688
|
showHeader={showHeader}
|
|
@@ -648,7 +690,6 @@ const QbsTable: React.FC<QbsTableProps> = ({
|
|
|
648
690
|
expandedRowKeys={expandedRowKeys}
|
|
649
691
|
onExpandChange={onExpandChange}
|
|
650
692
|
rowKey={dataRowKey ?? 'id'}
|
|
651
|
-
rowClassName={selection ? getRowClassName : undefined}
|
|
652
693
|
defaultExpandAllRows={defaultExpandAllRows}
|
|
653
694
|
shouldUpdateScroll={shouldUpdateScroll}
|
|
654
695
|
renderRowExpanded={rowData => {
|
|
@@ -790,11 +831,12 @@ const QbsTable: React.FC<QbsTableProps> = ({
|
|
|
790
831
|
</HeaderCell>
|
|
791
832
|
<ActionCell
|
|
792
833
|
tableBodyRef={tableBodyRef}
|
|
834
|
+
dropType={dropType}
|
|
835
|
+
wheelWrapperRef={wheelWrapperRef}
|
|
793
836
|
actionProps={actionProps}
|
|
794
837
|
className={`${classes.cellClass} ${classes.actionCellClass}`}
|
|
795
838
|
handleMenuActions={handleMenuActions}
|
|
796
839
|
dataTheme={dataTheme}
|
|
797
|
-
dropType={dropType}
|
|
798
840
|
/>
|
|
799
841
|
</Column>
|
|
800
842
|
)}
|
|
@@ -816,7 +858,7 @@ const QbsTable: React.FC<QbsTableProps> = ({
|
|
|
816
858
|
>
|
|
817
859
|
{(data?.length === 0 || !data) && !isLoading && (
|
|
818
860
|
<div className="flex flex-col gap-2 p-2 mt-6 card-empty-container">
|
|
819
|
-
<NoData title={emptyTitle ??
|
|
861
|
+
<NoData title={emptyTitle ?? labels.noDataFound} subtitle={emptySubTitle} />
|
|
820
862
|
</div>
|
|
821
863
|
)}
|
|
822
864
|
{isLoading ? (
|
|
@@ -843,6 +885,7 @@ const QbsTable: React.FC<QbsTableProps> = ({
|
|
|
843
885
|
columns={columns}
|
|
844
886
|
tableBodyRef={tableBodyRef}
|
|
845
887
|
actionProps={actionProps}
|
|
888
|
+
labels={labels}
|
|
846
889
|
/>
|
|
847
890
|
)}
|
|
848
891
|
</div>
|
|
@@ -851,7 +894,9 @@ const QbsTable: React.FC<QbsTableProps> = ({
|
|
|
851
894
|
</div>
|
|
852
895
|
)}
|
|
853
896
|
<div>
|
|
854
|
-
{pagination && data?.length > 0 &&
|
|
897
|
+
{pagination && data?.length > 0 && (
|
|
898
|
+
<Pagination paginationProps={paginationProps} labels={labels} dataTheme={dataTheme} />
|
|
899
|
+
)}
|
|
855
900
|
</div>
|
|
856
901
|
</div>
|
|
857
902
|
</div>
|
|
@@ -106,6 +106,7 @@ const QbsTable: React.FC<QbsTableProps> = ({
|
|
|
106
106
|
const [isOpen, setIsOpen] = useState(false);
|
|
107
107
|
const prevColumns = useRef<any | null>(null);
|
|
108
108
|
const tableBodyRef = useRef<HTMLDivElement>(null);
|
|
109
|
+
const wheelWrapperRef = useRef<HTMLDivElement>(null);
|
|
109
110
|
const handleSortColumn = useCallback(
|
|
110
111
|
(sortColumn: any, sortType: any) => {
|
|
111
112
|
setLoading(true);
|
|
@@ -481,6 +482,7 @@ const QbsTable: React.FC<QbsTableProps> = ({
|
|
|
481
482
|
rtl={rtl}
|
|
482
483
|
data={data}
|
|
483
484
|
tableBodyRef={tableBodyRef as React.RefObject<HTMLDivElement>}
|
|
485
|
+
wheelWrapperRef={wheelWrapperRef}
|
|
484
486
|
dataTheme={dataTheme}
|
|
485
487
|
wordWrap={effectiveWordWrap}
|
|
486
488
|
autoHeight={autoHeight}
|