@@ -20,6 +20,7 @@ import type { ThemeProps } from "../types/theme";
2020import type {
2121 Column ,
2222 DataItem ,
23+ SelectionState ,
2324 TableInstanceWithHooks ,
2425 TableStateWithPagination ,
2526} from "../types/types" ;
@@ -43,6 +44,8 @@ export interface MultiLevelTableProps {
4344 ascendingIcon ?: React . ReactNode ;
4445 descendingIcon ?: React . ReactNode ;
4546 expandIcon ?: React . ReactNode ;
47+ selectable ?: boolean ;
48+ onSelectionChange ?: ( selectedRows : Set < string | number > ) => void ;
4649}
4750
4851/**
@@ -61,9 +64,48 @@ export const MultiLevelTable: React.FC<MultiLevelTableProps> = ({
6164 ascendingIcon,
6265 descendingIcon,
6366 expandIcon,
67+ selectable = false ,
68+ onSelectionChange,
6469} ) => {
6570 const mergedTheme = mergeThemeProps ( defaultTheme , theme ) ;
6671 const [ filterInput , setFilterInput ] = useState ( "" ) ;
72+ const [ selectionState , setSelectionState ] = useState < SelectionState > ( {
73+ selectedRows : new Set ( ) ,
74+ isAllSelected : false ,
75+ } ) ;
76+
77+ // Get all parent row IDs (level 0)
78+ const parentRowIds = useMemo ( ( ) => data . map ( item => item . id ) , [ data ] ) ;
79+
80+ const handleSelectAll = ( ) => {
81+ const newIsAllSelected = ! selectionState . isAllSelected ;
82+ const newSelectedRows = new Set < string | number > ( ) ;
83+
84+ if ( newIsAllSelected ) parentRowIds . forEach ( id => newSelectedRows . add ( id ) ) ;
85+
86+ setSelectionState ( {
87+ selectedRows : newSelectedRows ,
88+ isAllSelected : newIsAllSelected ,
89+ } ) ;
90+
91+ onSelectionChange ?.( newSelectedRows ) ;
92+ } ;
93+
94+ const handleRowSelect = ( rowId : string | number ) => {
95+ const newSelectedRows = new Set ( selectionState . selectedRows ) ;
96+
97+ if ( newSelectedRows . has ( rowId ) ) newSelectedRows . delete ( rowId ) ;
98+ else newSelectedRows . add ( rowId ) ;
99+
100+ const newIsAllSelected = newSelectedRows . size === parentRowIds . length ;
101+
102+ setSelectionState ( {
103+ selectedRows : newSelectedRows ,
104+ isAllSelected : newIsAllSelected ,
105+ } ) ;
106+
107+ onSelectionChange ?.( newSelectedRows ) ;
108+ } ;
67109
68110 /**
69111 * Prepares columns configuration for react-table
@@ -84,7 +126,7 @@ export const MultiLevelTable: React.FC<MultiLevelTableProps> = ({
84126 value : string | number ;
85127 } ) => {
86128 const item = row . original ;
87-
129+
88130 return (
89131 < div > { col . render ? col . render ( value , item ) : value ?. toString ( ) } </ div >
90132 ) ;
@@ -146,7 +188,7 @@ export const MultiLevelTable: React.FC<MultiLevelTableProps> = ({
146188 ) as TableInstanceWithHooks < DataItem > ;
147189
148190 const rowsMap = useMemo ( ( ) => {
149- const map = new Map < number , DataItem [ ] > ( ) ;
191+ const map = new Map < string | number , DataItem [ ] > ( ) ;
150192
151193 const processItem = ( item : DataItem ) => {
152194 if ( item . children ) {
@@ -160,9 +202,9 @@ export const MultiLevelTable: React.FC<MultiLevelTableProps> = ({
160202 return map ;
161203 } , [ data ] ) ;
162204
163- const [ expandedRows , setExpandedRows ] = useState < Set < number > > ( new Set ( ) ) ;
205+ const [ expandedRows , setExpandedRows ] = useState < Set < string | number > > ( new Set ( ) ) ;
164206
165- const toggleRow = ( rowId : number ) => {
207+ const toggleRow = ( rowId : string | number ) => {
166208 setExpandedRows ( ( prev ) => {
167209 const newSet = new Set ( prev ) ;
168210
@@ -175,9 +217,8 @@ export const MultiLevelTable: React.FC<MultiLevelTableProps> = ({
175217 } ) ;
176218 } ;
177219
178- const renderNestedRows = ( parentId : number , level = 1 ) => {
220+ const renderNestedRows = ( parentId : string | number , level = 1 ) => {
179221 if ( ! expandedRows . has ( parentId ) ) return null ;
180-
181222 const children = rowsMap . get ( parentId ) || [ ] ;
182223
183224 return children . map ( ( child ) => {
@@ -194,6 +235,8 @@ export const MultiLevelTable: React.FC<MultiLevelTableProps> = ({
194235 level = { level }
195236 theme = { mergedTheme }
196237 expandIcon = { expandIcon }
238+ selectable = { false }
239+ isRowSelected = { false }
197240 />
198241 { renderNestedRows ( child . id , level + 1 ) }
199242 </ React . Fragment >
@@ -251,8 +294,12 @@ export const MultiLevelTable: React.FC<MultiLevelTableProps> = ({
251294 hasChildren = { hasChildren }
252295 isExpanded = { expandedRows . has ( parentId ) }
253296 onToggle = { ( ) => hasChildren && toggleRow ( parentId ) }
297+ level = { 0 }
254298 theme = { mergedTheme }
255299 expandIcon = { expandIcon }
300+ selectable = { true }
301+ isRowSelected = { selectionState . selectedRows . has ( row . original . id ) }
302+ onRowSelect = { handleRowSelect }
256303 />
257304 { renderNestedRows ( parentId ) }
258305 </ React . Fragment >
@@ -276,6 +323,9 @@ export const MultiLevelTable: React.FC<MultiLevelTableProps> = ({
276323 sortable = { sortable }
277324 ascendingIcon = { ascendingIcon }
278325 descendingIcon = { descendingIcon }
326+ selectable = { selectable }
327+ isAllSelected = { selectionState . isAllSelected }
328+ onSelectAll = { handleSelectAll }
279329 />
280330 { renderTableBody ( ) }
281331 </ table >
0 commit comments