playbook_ui 14.15.0.pre.rc.4 → 14.15.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.
- checksums.yaml +4 -4
- data/app/pb_kits/playbook/pb_advanced_table/Components/RegularTableView.tsx +127 -0
- data/app/pb_kits/playbook/pb_advanced_table/Components/TableActionBar.tsx +55 -0
- data/app/pb_kits/playbook/pb_advanced_table/Components/TablePagination.tsx +33 -0
- data/app/pb_kits/playbook/pb_advanced_table/Components/VirtualizedTableView.tsx +275 -0
- data/app/pb_kits/playbook/pb_advanced_table/Context/AdvancedTableContext.tsx +143 -3
- data/app/pb_kits/playbook/pb_advanced_table/Hooks/useTableActions.ts +66 -0
- data/app/pb_kits/playbook/pb_advanced_table/Hooks/useTableState.ts +195 -0
- data/app/pb_kits/playbook/pb_advanced_table/SubKits/TableBody.tsx +45 -99
- data/app/pb_kits/playbook/pb_advanced_table/Utilities/CellRendererUtils.tsx +73 -0
- data/app/pb_kits/playbook/pb_advanced_table/Utilities/RowUtils.ts +52 -0
- data/app/pb_kits/playbook/pb_advanced_table/Utilities/TableContainerStyles.ts +80 -0
- data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.scss +123 -7
- data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.tsx +153 -299
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_infinite_scroll.jsx +50 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/advanced_table_mock_data_infinite_scroll.json +152002 -0
- data/app/pb_kits/playbook/pb_card/_card.tsx +2 -1
- data/app/pb_kits/playbook/pb_date_picker/date_picker.html.erb +4 -1
- data/app/pb_kits/playbook/pb_date_picker/date_picker.rb +2 -0
- data/app/pb_kits/playbook/pb_date_picker/index.ts +38 -0
- data/app/pb_kits/playbook/pb_drawer/_drawer.scss +19 -3
- data/app/pb_kits/playbook/pb_drawer/docs/_drawer_borders.jsx +3 -3
- data/app/pb_kits/playbook/pb_drawer/docs/_drawer_breakpoints.jsx +20 -37
- data/app/pb_kits/playbook/pb_drawer/docs/_drawer_menu.jsx +6 -6
- data/app/pb_kits/playbook/pb_drawer/docs/_drawer_overlay.jsx +1 -0
- data/app/pb_kits/playbook/pb_drawer/docs/example.yml +1 -0
- data/app/pb_kits/playbook/pb_filter/Filter/CurrentFilters.tsx +5 -4
- data/app/pb_kits/playbook/pb_filter/Filter/FilterSingle.tsx +2 -2
- data/app/pb_kits/playbook/pb_form/docs/_form_form_with_validate.html.erb +1 -1
- data/app/pb_kits/playbook/pb_form_pill/_form_pill.scss +9 -2
- data/app/pb_kits/playbook/pb_form_pill/_form_pill.tsx +4 -0
- data/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_wrapped.html.erb +40 -0
- data/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_wrapped.jsx +50 -0
- data/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_wrapped.md +3 -0
- data/app/pb_kits/playbook/pb_form_pill/docs/example.yml +2 -0
- data/app/pb_kits/playbook/pb_form_pill/docs/index.js +1 -0
- data/app/pb_kits/playbook/pb_form_pill/form_pill.rb +7 -1
- data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.scss +7 -0
- data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.tsx +13 -3
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_disabled.html.erb +72 -0
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_disabled.jsx +91 -0
- data/app/pb_kits/playbook/pb_multi_level_select/docs/example.yml +2 -1
- data/app/pb_kits/playbook/pb_multi_level_select/docs/index.js +1 -0
- data/app/pb_kits/playbook/pb_multi_level_select/multi_level_select.rb +6 -0
- data/app/pb_kits/playbook/pb_popover/_popover.tsx +1 -1
- data/app/pb_kits/playbook/pb_radio/_radio.tsx +85 -74
- data/app/pb_kits/playbook/pb_radio/docs/_radio_react_hook.jsx +60 -0
- data/app/pb_kits/playbook/pb_radio/docs/_radio_react_hook.md +1 -0
- data/app/pb_kits/playbook/pb_radio/docs/example.yml +2 -1
- data/app/pb_kits/playbook/pb_radio/docs/index.js +1 -0
- data/app/pb_kits/playbook/pb_radio/radio.test.js +16 -0
- data/app/pb_kits/playbook/pb_select/docs/_select_react_hook.jsx +58 -0
- data/app/pb_kits/playbook/pb_select/docs/_select_react_hook.md +1 -0
- data/app/pb_kits/playbook/pb_select/docs/example.yml +1 -0
- data/app/pb_kits/playbook/pb_select/docs/index.js +1 -0
- data/app/pb_kits/playbook/pb_select/select.html.erb +3 -5
- data/app/pb_kits/playbook/pb_selectable_card/selectable_card.html.erb +1 -5
- data/app/pb_kits/playbook/pb_selectable_card_icon/selectable_card_icon.html.erb +1 -4
- data/app/pb_kits/playbook/pb_selectable_icon/selectable_icon.html.erb +1 -5
- data/app/pb_kits/playbook/pb_timeline/_timeline.scss +2 -2
- data/app/pb_kits/playbook/pb_title/_title.scss +32 -0
- data/app/pb_kits/playbook/pb_title/_title.tsx +10 -1
- data/app/pb_kits/playbook/pb_title/docs/_title_default.html.erb +1 -2
- data/app/pb_kits/playbook/pb_title/docs/_title_default.jsx +1 -1
- data/app/pb_kits/playbook/pb_title/docs/_title_display_size.html.erb +7 -0
- data/app/pb_kits/playbook/pb_title/docs/_title_display_size.jsx +54 -0
- data/app/pb_kits/playbook/pb_title/docs/_title_display_size.md +1 -0
- data/app/pb_kits/playbook/pb_title/docs/example.yml +2 -0
- data/app/pb_kits/playbook/pb_title/docs/index.js +1 -0
- data/app/pb_kits/playbook/pb_title/title.rb +10 -1
- data/app/pb_kits/playbook/pb_tooltip/_tooltip.tsx +25 -0
- data/app/pb_kits/playbook/pb_tooltip/docs/_tooltip_sizing.jsx +69 -0
- data/app/pb_kits/playbook/pb_tooltip/docs/_tooltip_sizing.md +3 -0
- data/app/pb_kits/playbook/pb_tooltip/docs/example.yml +1 -1
- data/app/pb_kits/playbook/pb_tooltip/docs/index.js +1 -0
- data/app/pb_kits/playbook/pb_typeahead/_typeahead.tsx +2 -1
- data/app/pb_kits/playbook/pb_typeahead/components/MultiValue.tsx +5 -1
- data/app/pb_kits/playbook/pb_typeahead/typeahead.rb +4 -1
- data/app/pb_kits/playbook/utilities/object.test.js +99 -0
- data/app/pb_kits/playbook/utilities/object.ts +29 -1
- data/dist/chunks/_typeahead-BhfaW1J9.js +36 -0
- data/dist/chunks/_weekday_stacked-CKRIELiF.js +45 -0
- data/dist/chunks/lazysizes-DHz07jlL.js +1 -0
- data/dist/chunks/{lib-Dmay5Z6U.js → lib-5OzNgeeu.js} +2 -2
- data/dist/chunks/{pb_form_validation-DdP7BnVX.js → pb_form_validation-DGhKbZtO.js} +1 -1
- data/dist/chunks/vendor.js +1 -1
- data/dist/playbook-doc.js +1 -1
- data/dist/playbook-rails-react-bindings.js +1 -1
- data/dist/playbook-rails.js +1 -1
- data/dist/playbook.css +1 -1
- data/lib/playbook/version.rb +1 -1
- metadata +34 -7
- data/dist/chunks/_typeahead-NXKDTf__.js +0 -36
- data/dist/chunks/_weekday_stacked-DtCYkCXM.js +0 -45
- data/dist/chunks/lazysizes-B7xYodB-.js +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 988b1ee65101fb13b487e7fb3f8589b7b06a4510503b9a1d41dd84d1887ade9b
|
4
|
+
data.tar.gz: 2ebbaf39afdeca0386f701dc867be16170b39f8f8cbcba101e92ca8df1d9bd35
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e500e0257cd7da6adb7a707ea08e797d8322204341eaff40613dee843ee6c79d47db18c76192204a2b29576b3b3eb9ca13d8cb5ab5c2b401a7d4e775dec39450
|
7
|
+
data.tar.gz: dbf426e01b04c62e96af16a1fc4769659c7111a0a276c6a5a2f05ae1fa2948be612447dfea2c39d5115e068ee71cc90f16c24de34a4f2791aa719d03cdb3cda2
|
@@ -0,0 +1,127 @@
|
|
1
|
+
import React, { useContext } from "react"
|
2
|
+
import classnames from "classnames"
|
3
|
+
import { flexRender, Row, Cell } from "@tanstack/react-table"
|
4
|
+
|
5
|
+
import { GenericObject } from "../../types"
|
6
|
+
import { isChrome } from "../Utilities/BrowserCheck"
|
7
|
+
|
8
|
+
import LoadingInline from "../../pb_loading_inline/_loading_inline"
|
9
|
+
import Checkbox from "../../pb_checkbox/_checkbox"
|
10
|
+
|
11
|
+
import { SubRowHeaderRow } from "../Components/SubRowHeaderRow"
|
12
|
+
import { LoadingCell } from "../Components/LoadingCell"
|
13
|
+
import { renderCollapsibleTrail } from "../Components/CollapsibleTrail"
|
14
|
+
|
15
|
+
import AdvancedTableContext from "../Context/AdvancedTableContext"
|
16
|
+
|
17
|
+
type RegularTableViewProps = {
|
18
|
+
collapsibleTrail?: boolean
|
19
|
+
subRowHeaders?: string[]
|
20
|
+
}
|
21
|
+
|
22
|
+
export const RegularTableView = ({
|
23
|
+
collapsibleTrail = true,
|
24
|
+
subRowHeaders,
|
25
|
+
}: RegularTableViewProps) => {
|
26
|
+
const {
|
27
|
+
enableToggleExpansion,
|
28
|
+
handleExpandOrCollapse,
|
29
|
+
inlineRowLoading,
|
30
|
+
loading,
|
31
|
+
responsive,
|
32
|
+
table,
|
33
|
+
selectableRows,
|
34
|
+
hasAnySubRows,
|
35
|
+
isPinnedLeft = false,
|
36
|
+
} = useContext(AdvancedTableContext)
|
37
|
+
|
38
|
+
const columnPinning = table.getState().columnPinning || { left: [] };
|
39
|
+
const columnDefinitions = table.options.meta?.columnDefinitions || [];
|
40
|
+
|
41
|
+
return (
|
42
|
+
<>
|
43
|
+
{table.getRowModel().rows.map((row: Row<GenericObject>) => {
|
44
|
+
const isExpandable = row.getIsExpanded();
|
45
|
+
const isFirstChildofSubrow = row.depth > 0 && row.index === 0;
|
46
|
+
const rowHasNoChildren = row.original?.children && !row.original.children.length ? true : false;
|
47
|
+
const numberOfColumns = table.getAllFlatColumns().length;
|
48
|
+
const isDataLoading = isExpandable && (inlineRowLoading && rowHasNoChildren) && (row.depth < columnDefinitions[0]?.cellAccessors?.length);
|
49
|
+
const rowBackground = isExpandable && ((!inlineRowLoading && row.getCanExpand()) || (inlineRowLoading && rowHasNoChildren));
|
50
|
+
const rowColor = row.getIsSelected() ? "bg-row-selection" : rowBackground ? "bg-silver" : "bg-white";
|
51
|
+
|
52
|
+
return (
|
53
|
+
<React.Fragment key={`${row.index}-${row.id}-${row.depth}-row`}>
|
54
|
+
{isFirstChildofSubrow && subRowHeaders && (
|
55
|
+
<SubRowHeaderRow
|
56
|
+
collapsibleTrail={collapsibleTrail}
|
57
|
+
enableToggleExpansion={enableToggleExpansion}
|
58
|
+
onClick={handleExpandOrCollapse}
|
59
|
+
row={row}
|
60
|
+
subRowHeaders={subRowHeaders}
|
61
|
+
table={table}
|
62
|
+
/>
|
63
|
+
)}
|
64
|
+
|
65
|
+
<tr
|
66
|
+
className={`${rowColor} ${row.depth > 0 ? `depth-sub-row-${row.depth}` : ""}`}
|
67
|
+
id={`${row.index}-${row.id}-${row.depth}-row`}
|
68
|
+
>
|
69
|
+
{/* Render custom checkbox column when we want selectableRows for non-expanding tables */}
|
70
|
+
{selectableRows && !hasAnySubRows && (
|
71
|
+
<td className="checkbox-cell">
|
72
|
+
<Checkbox
|
73
|
+
checked={row.getIsSelected()}
|
74
|
+
disabled={!row.getCanSelect()}
|
75
|
+
indeterminate={row.getIsSomeSelected()}
|
76
|
+
name={row.id}
|
77
|
+
onChange={row.getToggleSelectedHandler()}
|
78
|
+
/>
|
79
|
+
</td>
|
80
|
+
)}
|
81
|
+
|
82
|
+
{row.getVisibleCells().map((cell: Cell<GenericObject, unknown>, i: number) => {
|
83
|
+
const isPinnedLeft = columnPinning.left.includes(cell.column.id);
|
84
|
+
const isLastCell = cell.column.parent?.columns?.at(-1)?.id === cell.column.id;
|
85
|
+
|
86
|
+
return (
|
87
|
+
<td
|
88
|
+
align="right"
|
89
|
+
className={classnames(
|
90
|
+
`${cell.id}-cell position_relative`,
|
91
|
+
isChrome() ? "chrome-styles" : "",
|
92
|
+
isPinnedLeft && 'pinned-left',
|
93
|
+
isLastCell && 'last-cell',
|
94
|
+
)}
|
95
|
+
key={`${cell.id}-data`}
|
96
|
+
>
|
97
|
+
{collapsibleTrail && i === 0 && row.depth > 0 && renderCollapsibleTrail(row.depth)}
|
98
|
+
<span id={`${cell.id}-span`}>
|
99
|
+
{loading ? (
|
100
|
+
<LoadingCell />
|
101
|
+
) : (
|
102
|
+
flexRender(cell.column.columnDef.cell, cell.getContext())
|
103
|
+
)}
|
104
|
+
</span>
|
105
|
+
</td>
|
106
|
+
);
|
107
|
+
})}
|
108
|
+
</tr>
|
109
|
+
|
110
|
+
{/* Display LoadingInline if Row Data is querying and there are no children already */}
|
111
|
+
{isDataLoading && (
|
112
|
+
<tr key={`${row.id}-row`}>
|
113
|
+
<td colSpan={numberOfColumns}
|
114
|
+
style={{ paddingLeft: `${row.depth === 0 ? 0.5 : (row.depth * 2)}em` }}
|
115
|
+
>
|
116
|
+
<LoadingInline />
|
117
|
+
</td>
|
118
|
+
</tr>
|
119
|
+
)}
|
120
|
+
</React.Fragment>
|
121
|
+
);
|
122
|
+
})}
|
123
|
+
</>
|
124
|
+
);
|
125
|
+
}
|
126
|
+
|
127
|
+
export default RegularTableView;
|
@@ -0,0 +1,55 @@
|
|
1
|
+
import React, { useEffect, useRef } from "react";
|
2
|
+
import Card from "../../pb_card/_card";
|
3
|
+
import Caption from "../../pb_caption/_caption";
|
4
|
+
import Flex from "../../pb_flex/_flex";
|
5
|
+
import FlexItem from "../../pb_flex/_flex_item";
|
6
|
+
import { showActionBar, hideActionBar } from "../Utilities/ActionBarAnimationHelper";
|
7
|
+
|
8
|
+
interface TableActionBarProps {
|
9
|
+
isVisible: boolean;
|
10
|
+
selectedCount: number;
|
11
|
+
actions?: React.ReactNode[] | React.ReactNode;
|
12
|
+
}
|
13
|
+
|
14
|
+
const TableActionBar: React.FC<TableActionBarProps> = ({
|
15
|
+
isVisible,
|
16
|
+
selectedCount,
|
17
|
+
actions
|
18
|
+
}) => {
|
19
|
+
const cardRef = useRef(null);
|
20
|
+
|
21
|
+
useEffect(() => {
|
22
|
+
if (cardRef.current) {
|
23
|
+
if (isVisible) {
|
24
|
+
showActionBar(cardRef.current);
|
25
|
+
} else {
|
26
|
+
hideActionBar(cardRef.current);
|
27
|
+
}
|
28
|
+
}
|
29
|
+
}, [isVisible]);
|
30
|
+
|
31
|
+
return (
|
32
|
+
<Card
|
33
|
+
borderNone={!isVisible}
|
34
|
+
className={`${isVisible && "show-action-card row-selection-actions-card"}`}
|
35
|
+
htmlOptions={{ ref: cardRef as any }}
|
36
|
+
padding={`${isVisible ? "xs" : "none"}`}
|
37
|
+
>
|
38
|
+
<Flex
|
39
|
+
alignItems="center"
|
40
|
+
justify="between"
|
41
|
+
>
|
42
|
+
<Caption
|
43
|
+
color="light"
|
44
|
+
paddingLeft="xs"
|
45
|
+
size="xs"
|
46
|
+
>
|
47
|
+
{selectedCount} Selected
|
48
|
+
</Caption>
|
49
|
+
<FlexItem>{actions}</FlexItem>
|
50
|
+
</Flex>
|
51
|
+
</Card>
|
52
|
+
);
|
53
|
+
};
|
54
|
+
|
55
|
+
export default TableActionBar;
|
@@ -0,0 +1,33 @@
|
|
1
|
+
import React from "react";
|
2
|
+
import Pagination from "../../pb_pagination/_pagination";
|
3
|
+
|
4
|
+
interface TablePaginationProps {
|
5
|
+
table: any;
|
6
|
+
onChange: (page: number) => void;
|
7
|
+
position: "top" | "bottom";
|
8
|
+
range?: number;
|
9
|
+
}
|
10
|
+
|
11
|
+
const TablePagination: React.FC<TablePaginationProps> = ({
|
12
|
+
table,
|
13
|
+
onChange,
|
14
|
+
position,
|
15
|
+
range = 5
|
16
|
+
}) => {
|
17
|
+
const current = table.getState().pagination.pageIndex + 1;
|
18
|
+
const total = table.getPageCount();
|
19
|
+
|
20
|
+
return (
|
21
|
+
<Pagination
|
22
|
+
current={current}
|
23
|
+
key={`pagination-${position}-${current}`}
|
24
|
+
marginBottom={position === "top" ? "xs" : undefined}
|
25
|
+
marginTop={position === "bottom" ? "xs" : undefined}
|
26
|
+
onChange={onChange}
|
27
|
+
range={range}
|
28
|
+
total={total}
|
29
|
+
/>
|
30
|
+
);
|
31
|
+
};
|
32
|
+
|
33
|
+
export default TablePagination;
|
@@ -0,0 +1,275 @@
|
|
1
|
+
import React, { useContext, useLayoutEffect, useState, useEffect } from "react"
|
2
|
+
import classnames from "classnames"
|
3
|
+
import { flexRender, Cell } from "@tanstack/react-table"
|
4
|
+
import { VirtualItem } from "@tanstack/react-virtual"
|
5
|
+
|
6
|
+
import { GenericObject } from "../../types"
|
7
|
+
|
8
|
+
import { isChrome } from "../Utilities/BrowserCheck"
|
9
|
+
import { getVirtualizedRowStyle } from "../Utilities/TableContainerStyles"
|
10
|
+
|
11
|
+
import LoadingInline from "../../pb_loading_inline/_loading_inline"
|
12
|
+
import Checkbox from "../../pb_checkbox/_checkbox"
|
13
|
+
|
14
|
+
import { SubRowHeaderRow } from "../Components/SubRowHeaderRow"
|
15
|
+
import { LoadingCell } from "../Components/LoadingCell"
|
16
|
+
import { renderCollapsibleTrail } from "../Components/CollapsibleTrail"
|
17
|
+
|
18
|
+
import AdvancedTableContext from "../Context/AdvancedTableContext"
|
19
|
+
|
20
|
+
type VirtualizedTableViewProps = {
|
21
|
+
collapsibleTrail?: boolean
|
22
|
+
subRowHeaders?: string[]
|
23
|
+
}
|
24
|
+
|
25
|
+
export const VirtualizedTableView = ({
|
26
|
+
collapsibleTrail = true,
|
27
|
+
subRowHeaders,
|
28
|
+
}: VirtualizedTableViewProps) => {
|
29
|
+
const {
|
30
|
+
enableToggleExpansion,
|
31
|
+
handleExpandOrCollapse,
|
32
|
+
inlineRowLoading,
|
33
|
+
loading,
|
34
|
+
table,
|
35
|
+
selectableRows,
|
36
|
+
hasAnySubRows,
|
37
|
+
virtualizer,
|
38
|
+
flattenedItems,
|
39
|
+
} = useContext(AdvancedTableContext)
|
40
|
+
|
41
|
+
const columnPinning = table.getState().columnPinning || { left: [] };
|
42
|
+
const sortingState = JSON.stringify(table.getState().sorting || []);
|
43
|
+
|
44
|
+
// Store column widths extracted from header
|
45
|
+
const [columnWidths, setColumnWidths] = useState<{[key: string]: string}>({});
|
46
|
+
|
47
|
+
// Function to get header cell widths
|
48
|
+
const getHeaderCellWidths = () => {
|
49
|
+
const widths: {[key: string]: string} = {};
|
50
|
+
|
51
|
+
// Get all header cells
|
52
|
+
const headerCells = document.querySelectorAll('.table-header-cells, .table-header-cells-custom');
|
53
|
+
|
54
|
+
// If checkbox is present in header
|
55
|
+
if (selectableRows && !hasAnySubRows && headerCells.length > 0) {
|
56
|
+
widths['checkbox'] = `${headerCells[0].getBoundingClientRect().width}px`;
|
57
|
+
}
|
58
|
+
|
59
|
+
// Process regular header cells
|
60
|
+
table.getFlatHeaders().forEach((header: any, index: number) => {
|
61
|
+
// Adjust index if checkbox column exists
|
62
|
+
const headerIndex = (selectableRows && !hasAnySubRows) ? index + 1 : index;
|
63
|
+
|
64
|
+
if (headerCells[headerIndex]) {
|
65
|
+
const width = headerCells[headerIndex].getBoundingClientRect().width;
|
66
|
+
widths[header.id] = `${width}px`;
|
67
|
+
}
|
68
|
+
});
|
69
|
+
|
70
|
+
return widths;
|
71
|
+
};
|
72
|
+
|
73
|
+
// Debounce function to prevent too many updates during resize
|
74
|
+
const debounce = <T extends (...args: any[]) => any>(func: T, wait: number): ((...args: Parameters<T>) => void) => {
|
75
|
+
let timeout: ReturnType<typeof setTimeout>;
|
76
|
+
return function executedFunction(...args: Parameters<T>) {
|
77
|
+
const later = () => {
|
78
|
+
clearTimeout(timeout);
|
79
|
+
func(...args);
|
80
|
+
};
|
81
|
+
clearTimeout(timeout);
|
82
|
+
timeout = setTimeout(later, wait);
|
83
|
+
};
|
84
|
+
};
|
85
|
+
|
86
|
+
// Update column widths when component mounts and when sorting changes
|
87
|
+
useLayoutEffect(() => {
|
88
|
+
// Apply widths after a small delay to ensure header is rendered
|
89
|
+
const timer = setTimeout(() => {
|
90
|
+
setColumnWidths(getHeaderCellWidths());
|
91
|
+
}, 0);
|
92
|
+
|
93
|
+
return () => clearTimeout(timer);
|
94
|
+
}, [table, selectableRows, hasAnySubRows, sortingState]);
|
95
|
+
|
96
|
+
// Add window resize listener to update widths on window resize
|
97
|
+
useEffect(() => {
|
98
|
+
// Create debounced version of the width measurement function
|
99
|
+
const handleResize = debounce(() => {
|
100
|
+
setColumnWidths(getHeaderCellWidths());
|
101
|
+
}, 0);
|
102
|
+
|
103
|
+
// Add the event listener
|
104
|
+
window.addEventListener('resize', handleResize);
|
105
|
+
|
106
|
+
// Cleanup
|
107
|
+
return () => {
|
108
|
+
window.removeEventListener('resize', handleResize);
|
109
|
+
};
|
110
|
+
}, [table, selectableRows, hasAnySubRows]);
|
111
|
+
|
112
|
+
// Safety check
|
113
|
+
if (!virtualizer || !flattenedItems) {
|
114
|
+
return (
|
115
|
+
<tr>
|
116
|
+
<td colSpan={table.getAllFlatColumns().length || 1}>
|
117
|
+
No data to display.
|
118
|
+
</td>
|
119
|
+
</tr>
|
120
|
+
);
|
121
|
+
}
|
122
|
+
|
123
|
+
// Get virtual items
|
124
|
+
let virtualItems: VirtualItem[] = [];
|
125
|
+
try {
|
126
|
+
virtualItems = virtualizer.getVirtualItems();
|
127
|
+
} catch (err) {
|
128
|
+
return (
|
129
|
+
<tr>
|
130
|
+
<td colSpan={table.getAllFlatColumns().length || 1}>
|
131
|
+
Error loading virtualized data.
|
132
|
+
</td>
|
133
|
+
</tr>
|
134
|
+
);
|
135
|
+
}
|
136
|
+
|
137
|
+
if (!virtualItems.length) {
|
138
|
+
return (
|
139
|
+
<tr>
|
140
|
+
<td colSpan={table.getAllFlatColumns().length || 1}>
|
141
|
+
No items to display.
|
142
|
+
</td>
|
143
|
+
</tr>
|
144
|
+
);
|
145
|
+
}
|
146
|
+
|
147
|
+
return (
|
148
|
+
<>
|
149
|
+
{virtualItems.map((virtualRow: VirtualItem) => {
|
150
|
+
const item = flattenedItems[virtualRow.index];
|
151
|
+
if (!item) return null;
|
152
|
+
|
153
|
+
// Use consistent row styling
|
154
|
+
const virtualItemStyle = getVirtualizedRowStyle(virtualRow.start);
|
155
|
+
|
156
|
+
if (item.type === 'header') {
|
157
|
+
return (
|
158
|
+
<tr
|
159
|
+
className="virtualized-table-row virtualized-header-row"
|
160
|
+
key={`header-${item.id}-sort-${sortingState}`}
|
161
|
+
style={virtualItemStyle}
|
162
|
+
>
|
163
|
+
<td colSpan={table.getAllFlatColumns().length}>
|
164
|
+
<SubRowHeaderRow
|
165
|
+
collapsibleTrail={collapsibleTrail}
|
166
|
+
enableToggleExpansion={enableToggleExpansion}
|
167
|
+
onClick={handleExpandOrCollapse}
|
168
|
+
row={item.row}
|
169
|
+
subRowHeaders={subRowHeaders}
|
170
|
+
table={table}
|
171
|
+
/>
|
172
|
+
</td>
|
173
|
+
</tr>
|
174
|
+
);
|
175
|
+
}
|
176
|
+
|
177
|
+
if (item.type === 'row') {
|
178
|
+
const row = item.row;
|
179
|
+
const isExpandable = row.getIsExpanded();
|
180
|
+
const rowHasNoChildren = row.original?.children && !row.original.children.length ? true : false;
|
181
|
+
const rowBackground = isExpandable && ((!inlineRowLoading && row.getCanExpand()) || (inlineRowLoading && rowHasNoChildren));
|
182
|
+
const rowColor = row.getIsSelected() ? "bg-row-selection" : rowBackground ? "bg-silver" : "bg-white";
|
183
|
+
|
184
|
+
return (
|
185
|
+
<tr
|
186
|
+
className={`virtualized-table-row ${rowColor} ${row.depth > 0 ? `depth-sub-row-${row.depth}` : ""}`}
|
187
|
+
data-index={virtualRow.index}
|
188
|
+
key={`row-${item.id}-sort-${sortingState}`}
|
189
|
+
ref={(node) => {
|
190
|
+
if (node) {
|
191
|
+
try {
|
192
|
+
virtualizer.measureElement(node);
|
193
|
+
} catch (err) {
|
194
|
+
// Silent error handling
|
195
|
+
}
|
196
|
+
}
|
197
|
+
}}
|
198
|
+
style={virtualItemStyle}
|
199
|
+
>
|
200
|
+
{/* Render custom checkbox column when we want selectableRows for non-expanding tables */}
|
201
|
+
{selectableRows && !hasAnySubRows && (
|
202
|
+
<td
|
203
|
+
className="checkbox-cell"
|
204
|
+
style={{ width: columnWidths['checkbox'] || 'auto' }}
|
205
|
+
>
|
206
|
+
<Checkbox
|
207
|
+
checked={row.getIsSelected()}
|
208
|
+
disabled={!row.getCanSelect()}
|
209
|
+
indeterminate={row.getIsSomeSelected()}
|
210
|
+
name={row.id}
|
211
|
+
onChange={row.getToggleSelectedHandler()}
|
212
|
+
/>
|
213
|
+
</td>
|
214
|
+
)}
|
215
|
+
|
216
|
+
{row.getVisibleCells().map((cell: Cell<GenericObject, unknown>, i: number) => {
|
217
|
+
const isPinnedLeft = columnPinning.left.includes(cell.column.id);
|
218
|
+
const isLastCell = cell.column.parent?.columns?.at(-1)?.id === cell.column.id;
|
219
|
+
const cellWidth = columnWidths[cell.column.id] || 'auto';
|
220
|
+
|
221
|
+
return (
|
222
|
+
<td
|
223
|
+
align="right"
|
224
|
+
className={classnames(
|
225
|
+
`${cell.id}-cell position_relative`,
|
226
|
+
isChrome() ? "chrome-styles" : "",
|
227
|
+
isPinnedLeft && 'pinned-left',
|
228
|
+
isLastCell && 'last-cell',
|
229
|
+
)}
|
230
|
+
key={`${cell.id}-data`}
|
231
|
+
style={{ width: cellWidth }}
|
232
|
+
>
|
233
|
+
{collapsibleTrail && i === 0 && row.depth > 0 && renderCollapsibleTrail(row.depth)}
|
234
|
+
<span id={`${cell.id}-span`}>
|
235
|
+
{loading ? (
|
236
|
+
<LoadingCell />
|
237
|
+
) : (
|
238
|
+
flexRender(cell.column.columnDef.cell, cell.getContext())
|
239
|
+
)}
|
240
|
+
</span>
|
241
|
+
</td>
|
242
|
+
);
|
243
|
+
})}
|
244
|
+
</tr>
|
245
|
+
);
|
246
|
+
}
|
247
|
+
|
248
|
+
if (item.type === 'loading') {
|
249
|
+
// Render loading indicator
|
250
|
+
const row = item.row;
|
251
|
+
const numberOfColumns = table.getAllFlatColumns().length;
|
252
|
+
|
253
|
+
return (
|
254
|
+
<tr
|
255
|
+
className="virtualized-table-row virtualized-loading-row"
|
256
|
+
key={`loading-${item.id}-sort-${sortingState}`}
|
257
|
+
style={virtualItemStyle}
|
258
|
+
>
|
259
|
+
<td
|
260
|
+
colSpan={numberOfColumns}
|
261
|
+
style={{ paddingLeft: `${row.depth === 0 ? 0.5 : (row.depth * 2)}em` }}
|
262
|
+
>
|
263
|
+
<LoadingInline />
|
264
|
+
</td>
|
265
|
+
</tr>
|
266
|
+
);
|
267
|
+
}
|
268
|
+
|
269
|
+
return null;
|
270
|
+
})}
|
271
|
+
</>
|
272
|
+
);
|
273
|
+
}
|
274
|
+
|
275
|
+
export default VirtualizedTableView;
|
@@ -1,5 +1,145 @@
|
|
1
|
-
import { createContext } from
|
1
|
+
import React, { createContext, useRef, useMemo, useEffect } from 'react';
|
2
|
+
import { useVirtualizer } from '@tanstack/react-virtual';
|
2
3
|
|
3
|
-
|
4
|
+
import { Row } from "@tanstack/react-table";
|
5
|
+
import { GenericObject } from "../../types";
|
6
|
+
import { getRowHeightEstimate } from '../Utilities/TableContainerStyles';
|
4
7
|
|
5
|
-
|
8
|
+
const AdvancedTableContext = createContext<any>({});
|
9
|
+
|
10
|
+
interface FlattenedItem {
|
11
|
+
type: 'header' | 'row' | 'loading';
|
12
|
+
row: Row<GenericObject>;
|
13
|
+
id: string;
|
14
|
+
}
|
15
|
+
|
16
|
+
export const AdvancedTableProvider = ({ children, ...props }: {
|
17
|
+
children: React.ReactNode,
|
18
|
+
[key: string]: any
|
19
|
+
}) => {
|
20
|
+
// Always initialize refs and state unconditionally, even if not used
|
21
|
+
const tableContainerRef = useRef(null);
|
22
|
+
const containerRef = props.tableContainerRef || tableContainerRef;
|
23
|
+
|
24
|
+
const table = props.table;
|
25
|
+
const isVirtualized = props.virtualizedRows || props.enableVirtualization;
|
26
|
+
|
27
|
+
// Create a flattened data array that includes ALL components for virtualization
|
28
|
+
const flattenedItems = useMemo(() => {
|
29
|
+
if (!isVirtualized || !table) {
|
30
|
+
return [];
|
31
|
+
}
|
32
|
+
|
33
|
+
const tableRows = table.getRowModel().rows;
|
34
|
+
const items: FlattenedItem[] = [];
|
35
|
+
const subRowHeaders = props.subRowHeaders;
|
36
|
+
const inlineRowLoading = props.inlineRowLoading;
|
37
|
+
const columnDefinitions = props.columnDefinitions;
|
38
|
+
|
39
|
+
// Process each row and insert special components
|
40
|
+
tableRows.forEach((row: Row<GenericObject>, index: number) => {
|
41
|
+
const isFirstChildofSubrow = row.depth > 0 && row.index === 0;
|
42
|
+
|
43
|
+
if (isFirstChildofSubrow && subRowHeaders) {
|
44
|
+
items.push({
|
45
|
+
type: 'header',
|
46
|
+
row: row,
|
47
|
+
id: `header-${row.id}-${index}`,
|
48
|
+
});
|
49
|
+
}
|
50
|
+
|
51
|
+
items.push({
|
52
|
+
type: 'row',
|
53
|
+
row: row,
|
54
|
+
id: `row-${row.id}-${index}`
|
55
|
+
});
|
56
|
+
|
57
|
+
const isExpandable = row.getIsExpanded();
|
58
|
+
const rowHasNoChildren = row.original?.children && !row.original.children.length ? true : false;
|
59
|
+
const isDataLoading = isExpandable && (inlineRowLoading && rowHasNoChildren) &&
|
60
|
+
(row.depth < (columnDefinitions[0]?.cellAccessors?.length || 0));
|
61
|
+
|
62
|
+
if (isDataLoading) {
|
63
|
+
items.push({
|
64
|
+
type: 'loading',
|
65
|
+
row: row,
|
66
|
+
id: `loading-${row.id}-${index}`
|
67
|
+
});
|
68
|
+
}
|
69
|
+
});
|
70
|
+
|
71
|
+
return items;
|
72
|
+
}, [
|
73
|
+
isVirtualized,
|
74
|
+
table,
|
75
|
+
props.subRowHeaders,
|
76
|
+
props.inlineRowLoading,
|
77
|
+
props.columnDefinitions,
|
78
|
+
// Add dependency on row model hash to refresh when data changes
|
79
|
+
table?.getRowModel().rows.length,
|
80
|
+
// Important: Add sorting state as a dependency to refresh when sorting changes
|
81
|
+
JSON.stringify(table?.getState().sorting || []),
|
82
|
+
// Also add expanded state as a dependency
|
83
|
+
JSON.stringify(table?.getState().expanded || {}),
|
84
|
+
]);
|
85
|
+
|
86
|
+
// Always initialize the virtualizer, even if we don't use it
|
87
|
+
// This satisfies the React hooks rules by ensuring hooks are called unconditionally
|
88
|
+
const virtualizer = useVirtualizer({
|
89
|
+
count: isVirtualized && flattenedItems.length > 0 ? flattenedItems.length : 0,
|
90
|
+
getScrollElement: () => containerRef.current,
|
91
|
+
estimateSize: (index) => {
|
92
|
+
// Skip if virtualization isn't enabled
|
93
|
+
if (!isVirtualized || flattenedItems.length === 0) return 0;
|
94
|
+
|
95
|
+
// Use consistent row height estimates
|
96
|
+
const item = flattenedItems[index];
|
97
|
+
if (!item) return getRowHeightEstimate('row'); // Default
|
98
|
+
|
99
|
+
return getRowHeightEstimate(item.type);
|
100
|
+
},
|
101
|
+
overscan: 10, // Load more items for smoother scrolling
|
102
|
+
getItemKey: (index) => {
|
103
|
+
if (!isVirtualized || flattenedItems.length === 0 || index >= flattenedItems.length) {
|
104
|
+
return `item-${index}`;
|
105
|
+
}
|
106
|
+
return flattenedItems[index]?.id || `item-${index}`;
|
107
|
+
},
|
108
|
+
});
|
109
|
+
|
110
|
+
// Reset virtualizer scroll position when important state changes
|
111
|
+
useEffect(() => {
|
112
|
+
if (isVirtualized && virtualizer && table && containerRef.current) {
|
113
|
+
// Force recalculation of all virtual items
|
114
|
+
virtualizer.measure();
|
115
|
+
|
116
|
+
// Reset scroll position when sorting changes
|
117
|
+
containerRef.current.scrollTop = 0;
|
118
|
+
}
|
119
|
+
}, [
|
120
|
+
isVirtualized,
|
121
|
+
virtualizer,
|
122
|
+
table,
|
123
|
+
containerRef,
|
124
|
+
JSON.stringify(table?.getState().sorting || []),
|
125
|
+
JSON.stringify(table?.getState().expanded || {})
|
126
|
+
]);
|
127
|
+
|
128
|
+
const contextValue = {
|
129
|
+
...props,
|
130
|
+
table,
|
131
|
+
tableContainerRef: containerRef,
|
132
|
+
virtualizer: isVirtualized ? virtualizer : null,
|
133
|
+
flattenedItems,
|
134
|
+
virtualizedRows: isVirtualized,
|
135
|
+
enableVirtualization: isVirtualized
|
136
|
+
};
|
137
|
+
|
138
|
+
return (
|
139
|
+
<AdvancedTableContext.Provider value={contextValue}>
|
140
|
+
{children}
|
141
|
+
</AdvancedTableContext.Provider>
|
142
|
+
);
|
143
|
+
};
|
144
|
+
|
145
|
+
export default AdvancedTableContext;
|