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.
Files changed (95) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/pb_advanced_table/Components/RegularTableView.tsx +127 -0
  3. data/app/pb_kits/playbook/pb_advanced_table/Components/TableActionBar.tsx +55 -0
  4. data/app/pb_kits/playbook/pb_advanced_table/Components/TablePagination.tsx +33 -0
  5. data/app/pb_kits/playbook/pb_advanced_table/Components/VirtualizedTableView.tsx +275 -0
  6. data/app/pb_kits/playbook/pb_advanced_table/Context/AdvancedTableContext.tsx +143 -3
  7. data/app/pb_kits/playbook/pb_advanced_table/Hooks/useTableActions.ts +66 -0
  8. data/app/pb_kits/playbook/pb_advanced_table/Hooks/useTableState.ts +195 -0
  9. data/app/pb_kits/playbook/pb_advanced_table/SubKits/TableBody.tsx +45 -99
  10. data/app/pb_kits/playbook/pb_advanced_table/Utilities/CellRendererUtils.tsx +73 -0
  11. data/app/pb_kits/playbook/pb_advanced_table/Utilities/RowUtils.ts +52 -0
  12. data/app/pb_kits/playbook/pb_advanced_table/Utilities/TableContainerStyles.ts +80 -0
  13. data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.scss +123 -7
  14. data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.tsx +153 -299
  15. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_infinite_scroll.jsx +50 -0
  16. data/app/pb_kits/playbook/pb_advanced_table/docs/advanced_table_mock_data_infinite_scroll.json +152002 -0
  17. data/app/pb_kits/playbook/pb_card/_card.tsx +2 -1
  18. data/app/pb_kits/playbook/pb_date_picker/date_picker.html.erb +4 -1
  19. data/app/pb_kits/playbook/pb_date_picker/date_picker.rb +2 -0
  20. data/app/pb_kits/playbook/pb_date_picker/index.ts +38 -0
  21. data/app/pb_kits/playbook/pb_drawer/_drawer.scss +19 -3
  22. data/app/pb_kits/playbook/pb_drawer/docs/_drawer_borders.jsx +3 -3
  23. data/app/pb_kits/playbook/pb_drawer/docs/_drawer_breakpoints.jsx +20 -37
  24. data/app/pb_kits/playbook/pb_drawer/docs/_drawer_menu.jsx +6 -6
  25. data/app/pb_kits/playbook/pb_drawer/docs/_drawer_overlay.jsx +1 -0
  26. data/app/pb_kits/playbook/pb_drawer/docs/example.yml +1 -0
  27. data/app/pb_kits/playbook/pb_filter/Filter/CurrentFilters.tsx +5 -4
  28. data/app/pb_kits/playbook/pb_filter/Filter/FilterSingle.tsx +2 -2
  29. data/app/pb_kits/playbook/pb_form/docs/_form_form_with_validate.html.erb +1 -1
  30. data/app/pb_kits/playbook/pb_form_pill/_form_pill.scss +9 -2
  31. data/app/pb_kits/playbook/pb_form_pill/_form_pill.tsx +4 -0
  32. data/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_wrapped.html.erb +40 -0
  33. data/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_wrapped.jsx +50 -0
  34. data/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_wrapped.md +3 -0
  35. data/app/pb_kits/playbook/pb_form_pill/docs/example.yml +2 -0
  36. data/app/pb_kits/playbook/pb_form_pill/docs/index.js +1 -0
  37. data/app/pb_kits/playbook/pb_form_pill/form_pill.rb +7 -1
  38. data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.scss +7 -0
  39. data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.tsx +13 -3
  40. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_disabled.html.erb +72 -0
  41. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_disabled.jsx +91 -0
  42. data/app/pb_kits/playbook/pb_multi_level_select/docs/example.yml +2 -1
  43. data/app/pb_kits/playbook/pb_multi_level_select/docs/index.js +1 -0
  44. data/app/pb_kits/playbook/pb_multi_level_select/multi_level_select.rb +6 -0
  45. data/app/pb_kits/playbook/pb_popover/_popover.tsx +1 -1
  46. data/app/pb_kits/playbook/pb_radio/_radio.tsx +85 -74
  47. data/app/pb_kits/playbook/pb_radio/docs/_radio_react_hook.jsx +60 -0
  48. data/app/pb_kits/playbook/pb_radio/docs/_radio_react_hook.md +1 -0
  49. data/app/pb_kits/playbook/pb_radio/docs/example.yml +2 -1
  50. data/app/pb_kits/playbook/pb_radio/docs/index.js +1 -0
  51. data/app/pb_kits/playbook/pb_radio/radio.test.js +16 -0
  52. data/app/pb_kits/playbook/pb_select/docs/_select_react_hook.jsx +58 -0
  53. data/app/pb_kits/playbook/pb_select/docs/_select_react_hook.md +1 -0
  54. data/app/pb_kits/playbook/pb_select/docs/example.yml +1 -0
  55. data/app/pb_kits/playbook/pb_select/docs/index.js +1 -0
  56. data/app/pb_kits/playbook/pb_select/select.html.erb +3 -5
  57. data/app/pb_kits/playbook/pb_selectable_card/selectable_card.html.erb +1 -5
  58. data/app/pb_kits/playbook/pb_selectable_card_icon/selectable_card_icon.html.erb +1 -4
  59. data/app/pb_kits/playbook/pb_selectable_icon/selectable_icon.html.erb +1 -5
  60. data/app/pb_kits/playbook/pb_timeline/_timeline.scss +2 -2
  61. data/app/pb_kits/playbook/pb_title/_title.scss +32 -0
  62. data/app/pb_kits/playbook/pb_title/_title.tsx +10 -1
  63. data/app/pb_kits/playbook/pb_title/docs/_title_default.html.erb +1 -2
  64. data/app/pb_kits/playbook/pb_title/docs/_title_default.jsx +1 -1
  65. data/app/pb_kits/playbook/pb_title/docs/_title_display_size.html.erb +7 -0
  66. data/app/pb_kits/playbook/pb_title/docs/_title_display_size.jsx +54 -0
  67. data/app/pb_kits/playbook/pb_title/docs/_title_display_size.md +1 -0
  68. data/app/pb_kits/playbook/pb_title/docs/example.yml +2 -0
  69. data/app/pb_kits/playbook/pb_title/docs/index.js +1 -0
  70. data/app/pb_kits/playbook/pb_title/title.rb +10 -1
  71. data/app/pb_kits/playbook/pb_tooltip/_tooltip.tsx +25 -0
  72. data/app/pb_kits/playbook/pb_tooltip/docs/_tooltip_sizing.jsx +69 -0
  73. data/app/pb_kits/playbook/pb_tooltip/docs/_tooltip_sizing.md +3 -0
  74. data/app/pb_kits/playbook/pb_tooltip/docs/example.yml +1 -1
  75. data/app/pb_kits/playbook/pb_tooltip/docs/index.js +1 -0
  76. data/app/pb_kits/playbook/pb_typeahead/_typeahead.tsx +2 -1
  77. data/app/pb_kits/playbook/pb_typeahead/components/MultiValue.tsx +5 -1
  78. data/app/pb_kits/playbook/pb_typeahead/typeahead.rb +4 -1
  79. data/app/pb_kits/playbook/utilities/object.test.js +99 -0
  80. data/app/pb_kits/playbook/utilities/object.ts +29 -1
  81. data/dist/chunks/_typeahead-BhfaW1J9.js +36 -0
  82. data/dist/chunks/_weekday_stacked-CKRIELiF.js +45 -0
  83. data/dist/chunks/lazysizes-DHz07jlL.js +1 -0
  84. data/dist/chunks/{lib-Dmay5Z6U.js → lib-5OzNgeeu.js} +2 -2
  85. data/dist/chunks/{pb_form_validation-DdP7BnVX.js → pb_form_validation-DGhKbZtO.js} +1 -1
  86. data/dist/chunks/vendor.js +1 -1
  87. data/dist/playbook-doc.js +1 -1
  88. data/dist/playbook-rails-react-bindings.js +1 -1
  89. data/dist/playbook-rails.js +1 -1
  90. data/dist/playbook.css +1 -1
  91. data/lib/playbook/version.rb +1 -1
  92. metadata +34 -7
  93. data/dist/chunks/_typeahead-NXKDTf__.js +0 -36
  94. data/dist/chunks/_weekday_stacked-DtCYkCXM.js +0 -45
  95. data/dist/chunks/lazysizes-B7xYodB-.js +0 -1
@@ -0,0 +1,66 @@
1
+ import { useCallback, useEffect } from 'react';
2
+ import { Row } from "@tanstack/react-table";
3
+ import { GenericObject } from "../../types";
4
+ import { updateExpandAndCollapseState } from "../Utilities/ExpansionControlHelpers";
5
+
6
+ interface UseTableActionsProps {
7
+ table: any;
8
+ expanded: GenericObject;
9
+ setExpanded: (expanded: GenericObject) => void;
10
+ onToggleExpansionClick?: (arg: Row<GenericObject>) => void;
11
+ onRowSelectionChange?: (rowSelection: any) => void;
12
+ }
13
+
14
+ export function useTableActions({
15
+ table,
16
+ expanded,
17
+ setExpanded,
18
+ onToggleExpansionClick,
19
+ onRowSelectionChange
20
+ }: UseTableActionsProps) {
21
+
22
+ // Handle expand/collapse
23
+ const handleExpandOrCollapse = useCallback(async (row: Row<GenericObject>) => {
24
+ onToggleExpansionClick && onToggleExpansionClick(row);
25
+
26
+ const expandedState = expanded;
27
+ const targetParent = row?.parentId;
28
+ const updatedRows = await updateExpandAndCollapseState(table.getRowModel(), expandedState, targetParent);
29
+ setExpanded(updatedRows);
30
+ }, [expanded, setExpanded, onToggleExpansionClick, table]);
31
+
32
+ // Handle pagination
33
+ const onPageChange = useCallback((page: number) => {
34
+ table.setPageIndex(page - 1);
35
+ }, [table]);
36
+
37
+ // Handle scroll detection for infinite scroll/virtualization
38
+ const fetchMoreOnBottomReached = useCallback((
39
+ containerRefElement: HTMLDivElement | null,
40
+ fetchNextPage: () => void,
41
+ isFetching: boolean,
42
+ totalFetched: number,
43
+ totalDBRowCount: number
44
+ ) => {
45
+ if (containerRefElement) {
46
+ const { scrollHeight, scrollTop, clientHeight } = containerRefElement;
47
+ // If user scrolls near bottom, fetch more data
48
+ if (scrollHeight - scrollTop - clientHeight < 500 && !isFetching && totalFetched < totalDBRowCount) {
49
+ fetchNextPage();
50
+ }
51
+ }
52
+ }, []);
53
+
54
+ // Update selection state
55
+ useEffect(() => {
56
+ if (onRowSelectionChange) {
57
+ onRowSelectionChange(table.getState().rowSelection);
58
+ }
59
+ }, [table.getState().rowSelection, onRowSelectionChange]);
60
+
61
+ return {
62
+ handleExpandOrCollapse,
63
+ onPageChange,
64
+ fetchMoreOnBottomReached
65
+ };
66
+ }
@@ -0,0 +1,195 @@
1
+ import { useState, useCallback, useMemo } from 'react';
2
+ import {
3
+ useReactTable,
4
+ getCoreRowModel,
5
+ getExpandedRowModel,
6
+ getPaginationRowModel,
7
+ getSortedRowModel,
8
+ RowSelectionState,
9
+ Row
10
+ } from "@tanstack/react-table";
11
+ import { GenericObject } from "../../types";
12
+ import { createColumnHelper } from "@tanstack/react-table";
13
+ import { createCellFunction } from "../Utilities/CellRendererUtils";
14
+
15
+ interface UseTableStateProps {
16
+ tableData: GenericObject[];
17
+ columnDefinitions: GenericObject[];
18
+ expandedControl?: GenericObject;
19
+ sortControl?: GenericObject;
20
+ onRowToggleClick?: (arg: Row<GenericObject>) => void;
21
+ selectableRows?: boolean;
22
+ initialLoadingRowsCount?: number;
23
+ loading?: boolean | string;
24
+ pagination?: boolean;
25
+ paginationProps?: GenericObject;
26
+ virtualizedRows?: boolean;
27
+ tableOptions?: GenericObject;
28
+ onRowSelectionChange?: (arg: RowSelectionState) => void;
29
+ }
30
+
31
+ export function useTableState({
32
+ tableData,
33
+ columnDefinitions,
34
+ expandedControl,
35
+ sortControl,
36
+ onRowToggleClick,
37
+ selectableRows,
38
+ initialLoadingRowsCount = 10,
39
+ loading,
40
+ pagination = false,
41
+ paginationProps,
42
+ virtualizedRows = false,
43
+ tableOptions
44
+ }: UseTableStateProps) {
45
+ // Create a local state for expanded and setExpanded if expandedControl not used
46
+ const [localExpanded, setLocalExpanded] = useState({});
47
+ const [loadingStateRowCount, setLoadingStateRowCount] = useState(initialLoadingRowsCount);
48
+ const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
49
+
50
+ // Determine whether to use the prop or the local state
51
+ const expanded = expandedControl ? expandedControl.value : localExpanded;
52
+ const setExpanded = expandedControl ? expandedControl.onChange : setLocalExpanded;
53
+
54
+ // Virtualized data handling (chunked loading)
55
+ const fetchSize = 20; // Number of rows per "page"
56
+ const [fullData] = useState(tableData); // All data from the JSON file
57
+ const [dataChunk, setDataChunk] = useState(fullData.slice(0, fetchSize)); // Initial chunk
58
+ const [totalFetched, setTotalFetched] = useState(fetchSize); // Track loaded rows
59
+ const [isFetching, setIsFetching] = useState(false);
60
+
61
+ // Create column structure
62
+ const columnHelper = createColumnHelper();
63
+
64
+ // Build columns with proper cell renderers
65
+ const buildColumns = useCallback((columnDefinitions: GenericObject[], isRoot = true): any[] => {
66
+ return columnDefinitions?.map((column, index) => {
67
+ const isFirstColumn = isRoot && index === 0;
68
+
69
+ // Handle grouped columns
70
+ if (column.columns && column.columns.length > 0) {
71
+ return {
72
+ header: column.label || "",
73
+ columns: buildColumns(column.columns, false),
74
+ };
75
+ }
76
+
77
+ // Define the base column structure
78
+ const columnStructure = {
79
+ ...columnHelper.accessor(column.accessor, {
80
+ header: column.label || "",
81
+ }),
82
+ };
83
+
84
+ if (column.cellAccessors || column.customRenderer) {
85
+ columnStructure.cell = createCellFunction(
86
+ column.cellAccessors || [],
87
+ column.customRenderer,
88
+ isFirstColumn,
89
+ onRowToggleClick,
90
+ selectableRows
91
+ );
92
+ }
93
+
94
+ return columnStructure;
95
+ }) || [];
96
+ }, [columnHelper, onRowToggleClick, selectableRows]);
97
+
98
+ const columns = useMemo(() => buildColumns(columnDefinitions), [buildColumns, columnDefinitions]);
99
+
100
+ // Sorting configuration
101
+ const sorting = useMemo(() => ([{
102
+ id: columnDefinitions[0].accessor,
103
+ desc: sortControl && sortControl.value !== null ? !sortControl.value.desc : false,
104
+ }]), [columnDefinitions, sortControl]);
105
+
106
+ // Custom state based on features enabled
107
+ const customState = useCallback(() => {
108
+ if (sortControl && selectableRows) {
109
+ return { state: { expanded, sorting, rowSelection } };
110
+ } else if (sortControl) {
111
+ return { state: { expanded, sorting } };
112
+ } else if (selectableRows) {
113
+ return { state: { expanded, rowSelection } };
114
+ } else {
115
+ return { state: { expanded } };
116
+ }
117
+ }, [expanded, rowSelection, sortControl, selectableRows, sorting]);
118
+
119
+ // Pagination configuration
120
+ const paginationInitializer = useMemo(() => {
121
+ if (!pagination) return {};
122
+
123
+ return {
124
+ getPaginationRowModel: getPaginationRowModel(),
125
+ paginateExpandedRows: false,
126
+ initialState: {
127
+ pagination: {
128
+ pageIndex: paginationProps?.pageIndex ?? 0,
129
+ pageSize: paginationProps?.pageSize ?? 20,
130
+ },
131
+ },
132
+ };
133
+ }, [pagination, paginationProps]);
134
+
135
+ // Initialize the table
136
+ const table = useReactTable({
137
+ data: loading ? Array(loadingStateRowCount).fill({}) : (virtualizedRows ? dataChunk : tableData),
138
+ columns,
139
+ onExpandedChange: setExpanded,
140
+ getSubRows: (row: GenericObject) => row.children,
141
+ getCoreRowModel: getCoreRowModel(),
142
+ getExpandedRowModel: getExpandedRowModel(),
143
+ getSortedRowModel: getSortedRowModel(),
144
+ enableSortingRemoval: false,
145
+ sortDescFirst: true,
146
+ onRowSelectionChange: setRowSelection,
147
+ getRowId: selectableRows ? row => row.id : undefined,
148
+ meta: {
149
+ columnDefinitions
150
+ },
151
+ ...customState(),
152
+ ...paginationInitializer,
153
+ ...tableOptions,
154
+ });
155
+
156
+ // Check if table has any sub-rows
157
+ const hasAnySubRows = table.getRowModel().rows.some(row => row.subRows && row.subRows.length > 0);
158
+ const selectedRowsLength = Object.keys(table.getState().rowSelection).length;
159
+
160
+ // Data fetching for virtualized view
161
+ const fetchNextPage = useCallback(() => {
162
+ if (isFetching || totalFetched >= fullData.length) return;
163
+
164
+ setIsFetching(true);
165
+ // Simulate API call delay
166
+ setTimeout(() => {
167
+ const nextChunk = fullData.slice(totalFetched, totalFetched + fetchSize);
168
+ setDataChunk(prev => [...prev, ...nextChunk]);
169
+ setTotalFetched(prev => prev + nextChunk.length);
170
+ setIsFetching(false);
171
+ }, 500);
172
+ }, [isFetching, totalFetched, fullData, fetchSize]);
173
+
174
+ // Update row count for loading state
175
+ const updateLoadingStateRowCount = useCallback(() => {
176
+ const rowsCount = table.getRowModel().rows.length;
177
+ if (rowsCount !== loadingStateRowCount && rowsCount !== 0) {
178
+ setLoadingStateRowCount(rowsCount);
179
+ }
180
+ }, [loadingStateRowCount, table]);
181
+
182
+ return {
183
+ table,
184
+ expanded,
185
+ setExpanded,
186
+ hasAnySubRows,
187
+ selectedRowsLength,
188
+ fetchNextPage,
189
+ updateLoadingStateRowCount,
190
+ rowSelection,
191
+ fullData,
192
+ totalFetched,
193
+ isFetching
194
+ };
195
+ }
@@ -1,21 +1,14 @@
1
1
  import React, { useContext } from "react"
2
2
  import classnames from "classnames"
3
- import { flexRender, Row } from "@tanstack/react-table"
4
3
 
5
4
  import { GenericObject } from "../../types"
6
5
 
7
6
  import { buildCss } from "../../utilities/props"
8
7
  import { globalProps } from "../../utilities/globalProps"
9
- import { isChrome } from "../Utilities/BrowserCheck"
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
8
 
18
9
  import AdvancedTableContext from "../Context/AdvancedTableContext"
10
+ import { RegularTableView } from "../Components/RegularTableView"
11
+ import { VirtualizedTableView } from "../Components/VirtualizedTableView"
19
12
 
20
13
  type TableBodyProps = {
21
14
  className?: string
@@ -35,18 +28,15 @@ export const TableBody = ({
35
28
  }: TableBodyProps) => {
36
29
 
37
30
  const {
38
- columnDefinitions,
39
- enableToggleExpansion,
40
- handleExpandOrCollapse,
41
- isPinnedLeft = false,
42
- inlineRowLoading,
43
- loading,
44
31
  responsive,
45
- table,
46
- selectableRows,
47
- hasAnySubRows
32
+ isPinnedLeft = false,
33
+ virtualizer,
34
+ virtualizedRows,
35
+ enableVirtualization
48
36
  } = useContext(AdvancedTableContext)
49
37
 
38
+ const isVirtualized = virtualizedRows || enableVirtualization;
39
+
50
40
  const classes = classnames(
51
41
  buildCss("pb_advanced_table_body"),
52
42
  { 'pinned-left': responsive === "scroll" && isPinnedLeft },
@@ -54,93 +44,49 @@ export const TableBody = ({
54
44
  className
55
45
  )
56
46
 
57
- const columnPinning = table.getState().columnPinning;
47
+ // Style for virtualized table container
48
+ const style: React.CSSProperties = virtualizer ? {
49
+ height: `${virtualizer.getTotalSize()}px`, // tells scrollbar how big the table is
50
+ position: 'relative', // needed for absolute positioning of rows
51
+ width: '100%',
52
+ } : {};
58
53
 
59
54
  return (
60
55
  <>
61
- <tbody className={classes}
56
+ <tbody
57
+ className={classes}
58
+ data-virtualized={isVirtualized ? 'true' : 'false'}
62
59
  id={id}
60
+ style={style}
63
61
  >
64
- {table.getRowModel().rows.map((row: Row<GenericObject>) => {
65
- const isExpandable = row.getIsExpanded()
66
- const isFirstChildofSubrow = row.depth > 0 && row.index === 0
67
- const rowHasNoChildren = row.original.children && !row.original.children.length ? true : false
68
- const numberOfColumns = table.getAllFlatColumns().length
69
- const isDataLoading = isExpandable && (inlineRowLoading && rowHasNoChildren) && (row.depth < columnDefinitions[0].cellAccessors?.length)
70
- const rowBackground = isExpandable && ((!inlineRowLoading && row.getCanExpand()) || (inlineRowLoading && rowHasNoChildren))
71
- const rowColor = row.getIsSelected() ? "bg-row-selection" : rowBackground ? "bg-silver" : "bg-white"
72
- return (
73
- <React.Fragment key={`${row.index}-${row.id}-${row.depth}-row`}>
74
- {isFirstChildofSubrow && subRowHeaders && (
75
- <SubRowHeaderRow
76
- collapsibleTrail={collapsibleTrail}
77
- enableToggleExpansion={enableToggleExpansion}
78
- onClick={handleExpandOrCollapse}
79
- row={row}
80
- subRowHeaders={subRowHeaders}
81
- table={table}
82
- />
83
- )}
84
- <tr
85
- className={`${rowColor} ${
86
- row.depth > 0 ? `depth-sub-row-${row.depth}` : ""
87
- }`}
88
- id={`${row.index}-${row.id}-${row.depth}-row`}
89
- >
90
- {/* Render custom checkbox column when we want selectableRows for non-expanding tables */}
91
- {selectableRows && !hasAnySubRows && (
92
- <td className="checkbox-cell">
93
- <Checkbox
94
- checked={row.getIsSelected()}
95
- disabled={!row.getCanSelect()}
96
- indeterminate={row.getIsSomeSelected()}
97
- name={row.id}
98
- onChange={row.getToggleSelectedHandler()}
99
- />
100
- </td>
101
- )}
102
- {row.getVisibleCells().map((cell, i) => {
103
- const isPinnedLeft = columnPinning.left.includes(cell.column.id)
104
- const isLastCell = cell.column.parent?.columns.at(-1)?.id === cell.column.id
105
-
106
- return (
107
- <td
108
- align="right"
109
- className={classnames(
110
- `${cell.id}-cell position_relative`,
111
- isChrome() ? "chrome-styles" : "",
112
- isPinnedLeft && 'pinned-left',
113
- isLastCell && 'last-cell',
114
- )}
115
- key={`${cell.id}-data`}
116
- >
117
- {collapsibleTrail && i === 0 && row.depth > 0 && renderCollapsibleTrail(row.depth)}
118
- <span id={`${cell.id}-span`}>
119
- {loading ? (
120
- <LoadingCell />
121
- ) : (
122
- flexRender(cell.column.columnDef.cell, cell.getContext())
123
- )}
124
- </span>
125
- </td>
126
- )
127
- })}
128
- </tr>
62
+ {isVirtualized && virtualizer ? (
63
+ // Virtualized table view
64
+ <VirtualizedTableView
65
+ collapsibleTrail={collapsibleTrail}
66
+ subRowHeaders={subRowHeaders}
67
+ />
68
+ ) : (
69
+ // Regular non-virtualized table view
70
+ <RegularTableView
71
+ collapsibleTrail={collapsibleTrail}
72
+ subRowHeaders={subRowHeaders}
73
+ />
74
+ )}
129
75
 
130
- {/* Display LoadingInline if Row Data is querying and there are no children already */}
131
- {isDataLoading ? (
132
- <tr key={`${row.id}-row`}>
133
- <td colSpan={numberOfColumns}
134
- style={{ paddingLeft: `${row.depth === 0 ? 0.5 : (row.depth * 2)}em` }}
135
- >
136
- <LoadingInline />
137
- </td>
138
- </tr>
139
- ) : null}
140
- </React.Fragment>
141
- )
142
- })}
76
+ {/* Fallback for when virtualization should be enabled but virtualizer isn't available */}
77
+ {isVirtualized && !virtualizer && (
78
+ <tr>
79
+ <td
80
+ colSpan={999}
81
+ style={{ padding: '10px', textAlign: 'center' }}
82
+ >
83
+ <div>No data to display.</div>
84
+ </td>
85
+ </tr>
86
+ )}
143
87
  </tbody>
144
88
  </>
145
- )
89
+ );
146
90
  }
91
+
92
+ export default TableBody;
@@ -0,0 +1,73 @@
1
+ import React from "react";
2
+ import { Row, Getter } from "@tanstack/react-table";
3
+ import { GenericObject } from "../../types";
4
+ import { CustomCell } from "../Components/CustomCell";
5
+
6
+ /**
7
+ * Creates a cell render function for table columns
8
+ *
9
+ * @param cellAccessors Array of accessors to use based on row depth
10
+ * @param customRenderer Optional custom renderer function
11
+ * @param isFirstColumn Whether this is the first column (special handling)
12
+ * @param onRowToggleClick Optional callback for row toggle
13
+ * @param selectableRows Whether rows are selectable
14
+ */
15
+ export const createCellFunction = (
16
+ cellAccessors: string[],
17
+ customRenderer?: (row: Row<GenericObject>, value: any) => JSX.Element,
18
+ isFirstColumn?: boolean,
19
+ onRowToggleClick?: (row: Row<GenericObject>) => void,
20
+ selectableRows?: boolean
21
+ ) => {
22
+ // Add display name to the returned function
23
+ const cellRenderer = ({
24
+ row,
25
+ getValue,
26
+ }: {
27
+ row: Row<GenericObject>
28
+ getValue: Getter<string>
29
+ }) => {
30
+ const rowData = row.original;
31
+
32
+ if (isFirstColumn) {
33
+ switch (row.depth) {
34
+ case 0: {
35
+ return (
36
+ <CustomCell
37
+ customRenderer={customRenderer}
38
+ getValue={getValue}
39
+ onRowToggleClick={onRowToggleClick}
40
+ row={row}
41
+ selectableRows={selectableRows}
42
+ />
43
+ );
44
+ }
45
+ default: {
46
+ // Handle other depths based on cellAccessors
47
+ const depthAccessor = cellAccessors[row.depth - 1]; // Adjust index for depth
48
+ const accessorValue = rowData[depthAccessor];
49
+ return accessorValue ? (
50
+ <CustomCell
51
+ customRenderer={customRenderer}
52
+ onRowToggleClick={onRowToggleClick}
53
+ row={row}
54
+ selectableRows={selectableRows}
55
+ value={accessorValue}
56
+ />
57
+ ) : (
58
+ "N/A"
59
+ );
60
+ }
61
+ }
62
+ }
63
+
64
+ return customRenderer
65
+ ? customRenderer(row, getValue())
66
+ : getValue();
67
+ };
68
+
69
+ // Add a display name to the function to fix the react/display-name warning
70
+ cellRenderer.displayName = 'CellRenderer';
71
+
72
+ return cellRenderer;
73
+ };
@@ -0,0 +1,52 @@
1
+ import { Row } from "@tanstack/react-table"
2
+ import { GenericObject } from "../../types"
3
+
4
+ /**
5
+ * Determines the background color class for a row
6
+ */
7
+ export const getRowColorClass = (
8
+ row: Row<GenericObject>,
9
+ inlineRowLoading: boolean
10
+ ): string => {
11
+ const isExpandable = row.getIsExpanded();
12
+ const rowHasNoChildren = row.original?.children && !row.original.children.length ? true : false;
13
+ const rowBackground = isExpandable && ((!inlineRowLoading && row.getCanExpand()) || (inlineRowLoading && rowHasNoChildren));
14
+
15
+ return row.getIsSelected() ? "bg-row-selection" : rowBackground ? "bg-silver" : "bg-white";
16
+ }
17
+
18
+ /**
19
+ * Determines if loading indicator should be shown for a row
20
+ */
21
+ export const shouldShowLoadingIndicator = (
22
+ row: Row<GenericObject>,
23
+ inlineRowLoading: boolean,
24
+ cellAccessorsLength: number
25
+ ): boolean => {
26
+ const isExpandable = row.getIsExpanded();
27
+ const rowHasNoChildren = row.original?.children && !row.original.children.length ? true : false;
28
+
29
+ return isExpandable &&
30
+ (inlineRowLoading && rowHasNoChildren) &&
31
+ (row.depth < cellAccessorsLength);
32
+ }
33
+
34
+ /**
35
+ * Creates a virtual item style object for virtualized rows
36
+ */
37
+ export const createVirtualItemStyle = (startPosition: number): React.CSSProperties => {
38
+ return {
39
+ position: 'absolute',
40
+ top: 0,
41
+ left: 0,
42
+ width: '100%',
43
+ transform: `translateY(${startPosition}px)`,
44
+ };
45
+ }
46
+
47
+ /**
48
+ * Calculates padding left based on row depth
49
+ */
50
+ export const getDepthPaddingLeft = (depth: number): string => {
51
+ return `${depth === 0 ? 0.5 : (depth * 2)}em`;
52
+ }
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Utility functions to generate styles for table container
3
+ */
4
+
5
+ /**
6
+ * Creates styles for a virtualized table container
7
+ * These styles are critical for the virtualizer to work properly
8
+ */
9
+ export const getVirtualizedContainerStyles = (maxHeight?: string): React.CSSProperties => {
10
+ // Calculate height based on maxHeight prop or use default
11
+ let heightValue = '600px'; // Default height
12
+
13
+ if (maxHeight) {
14
+ switch (maxHeight) {
15
+ case 'xs':
16
+ heightValue = '200px';
17
+ break;
18
+ case 'sm':
19
+ heightValue = '300px';
20
+ break;
21
+ case 'md':
22
+ heightValue = '400px';
23
+ break;
24
+ case 'lg':
25
+ heightValue = '500px';
26
+ break;
27
+ case 'xl':
28
+ heightValue = '600px';
29
+ break;
30
+ case 'xxl':
31
+ heightValue = '700px';
32
+ break;
33
+ case 'xxxl':
34
+ heightValue = '800px';
35
+ break;
36
+ default:
37
+ if (maxHeight !== 'auto') {
38
+ heightValue = maxHeight; // Use custom value if provided
39
+ }
40
+ }
41
+ }
42
+
43
+ return {
44
+ overflow: 'auto', // Critical: must have overflow:auto to enable scrolling
45
+ position: 'relative', // Required for absolute positioning of virtualized rows
46
+ height: heightValue, // Must have fixed height (not auto) for virtualization
47
+ width: '100%', // Fill container width
48
+ };
49
+ };
50
+
51
+ /**
52
+ * Creates consistent row styles for virtualized table rows
53
+ * Matches the virtualized row height to the standard table
54
+ */
55
+ export const getVirtualizedRowStyle = (startPosition: number): React.CSSProperties => {
56
+ return {
57
+ position: 'absolute',
58
+ top: 0,
59
+ left: 0,
60
+ width: '100%',
61
+ height: '40px', // Match standard table row height
62
+ transform: `translateY(${startPosition}px)`,
63
+ tableLayout: 'fixed',
64
+ };
65
+ };
66
+
67
+ /**
68
+ * Get height estimates for different row types
69
+ */
70
+ export const getRowHeightEstimate = (rowType: 'header' | 'row' | 'loading') => {
71
+ switch (rowType) {
72
+ case 'header':
73
+ return 40; // Header height
74
+ case 'loading':
75
+ return 30; // Loading indicator height
76
+ case 'row':
77
+ default:
78
+ return 40; // Standard row height - match this to your design system
79
+ }
80
+ };