@coreui/react-pro 4.8.0-alpha.3 → 4.9.0-next.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 (82) hide show
  1. package/README.md +1 -1
  2. package/dist/components/calendar/index.d.ts +2 -0
  3. package/dist/components/calendar/utils.d.ts +23 -0
  4. package/dist/components/date-picker/CDatePicker.d.ts +1 -1
  5. package/dist/components/date-range-picker/CDateRangePicker.d.ts +2 -2
  6. package/dist/components/element-cover/CElementCover.d.ts +3 -7
  7. package/dist/components/element-cover/index.d.ts +2 -0
  8. package/dist/components/form/CFormInput.d.ts +1 -1
  9. package/dist/components/form/CFormSwitch.d.ts +1 -1
  10. package/dist/components/index.d.ts +2 -0
  11. package/dist/components/nav/CNavGroup.d.ts +4 -4
  12. package/dist/components/offcanvas/COffcanvas.d.ts +8 -2
  13. package/dist/components/sidebar/CSidebar.d.ts +4 -4
  14. package/dist/components/smart-table/CSmartTableBody.d.ts +6 -5
  15. package/dist/components/smart-table/CSmartTableHead.d.ts +6 -3
  16. package/dist/components/smart-table/CSmartTableInterface.d.ts +46 -109
  17. package/dist/components/smart-table/types.d.ts +53 -0
  18. package/dist/components/smart-table/utils.d.ts +16 -0
  19. package/dist/components/table/CTable.d.ts +5 -5
  20. package/dist/components/time-picker/types.d.ts +15 -0
  21. package/dist/components/time-picker/utils.d.ts +23 -0
  22. package/dist/hooks/index.d.ts +3 -0
  23. package/dist/hooks/useForkedRef.d.ts +7 -0
  24. package/dist/hooks/useStateWithCallback.d.ts +2 -0
  25. package/dist/index.es.js +1246 -1865
  26. package/dist/index.es.js.map +1 -1
  27. package/dist/index.js +1246 -1863
  28. package/dist/index.js.map +1 -1
  29. package/dist/utils/index.d.ts +3 -2
  30. package/dist/utils/isInViewport.d.ts +2 -0
  31. package/dist/utils/isObjectInArray.d.ts +2 -0
  32. package/package.json +14 -14
  33. package/src/components/accordion/CAccordionBody.tsx +1 -1
  34. package/src/components/alert/CAlert.tsx +1 -1
  35. package/src/components/backdrop/CBackdrop.tsx +1 -1
  36. package/src/components/calendar/CCalendar.tsx +351 -240
  37. package/src/components/calendar/index.ts +3 -0
  38. package/src/{utils/calendar.ts → components/calendar/utils.ts} +1 -1
  39. package/src/components/carousel/CCarousel.tsx +4 -4
  40. package/src/components/carousel/CCarouselItem.tsx +1 -1
  41. package/src/components/collapse/CCollapse.tsx +1 -1
  42. package/src/components/date-picker/CDatePicker.tsx +1 -1
  43. package/src/components/date-range-picker/CDateRangePicker.tsx +3 -3
  44. package/src/components/dropdown/CDropdown.tsx +1 -1
  45. package/src/components/dropdown/CDropdownToggle.tsx +1 -1
  46. package/src/components/element-cover/CElementCover.tsx +19 -25
  47. package/src/components/element-cover/index.ts +3 -0
  48. package/src/components/form/CFormCheck.tsx +1 -1
  49. package/src/components/form/CFormControlWrapper.tsx +14 -20
  50. package/src/components/form/CFormInput.tsx +1 -1
  51. package/src/components/form/CFormSwitch.tsx +1 -1
  52. package/src/components/grid/CCol.tsx +4 -0
  53. package/src/components/index.ts +2 -0
  54. package/src/components/modal/CModal.tsx +1 -1
  55. package/src/components/nav/CNavGroup.tsx +4 -4
  56. package/src/components/nav/CNavLink.tsx +2 -1
  57. package/src/components/offcanvas/COffcanvas.tsx +36 -18
  58. package/src/components/sidebar/CSidebar.tsx +10 -10
  59. package/src/components/smart-table/CSmartTable.tsx +279 -259
  60. package/src/components/smart-table/CSmartTableBody.tsx +31 -41
  61. package/src/components/smart-table/CSmartTableCleaner.tsx +1 -1
  62. package/src/components/smart-table/CSmartTableFilter.tsx +1 -3
  63. package/src/components/smart-table/CSmartTableHead.tsx +53 -103
  64. package/src/components/smart-table/CSmartTableInterface.tsx +57 -120
  65. package/src/components/smart-table/CSmartTableItemsPerPageSelector.tsx +1 -2
  66. package/src/components/smart-table/types.tsx +65 -0
  67. package/src/components/smart-table/utils.tsx +199 -0
  68. package/src/components/table/CTable.tsx +12 -9
  69. package/src/components/tabs/CTabPane.tsx +1 -1
  70. package/src/components/time-picker/CTimePicker.tsx +50 -23
  71. package/src/components/time-picker/CTimePickerRollCol.tsx +1 -1
  72. package/src/components/time-picker/types.ts +15 -0
  73. package/src/{utils/time.ts → components/time-picker/utils.ts} +43 -2
  74. package/src/components/toast/CToast.tsx +1 -1
  75. package/src/components/virtual-scroller/CVirtualScroller.tsx +1 -1
  76. package/src/{utils/hooks → hooks}/index.ts +0 -0
  77. package/src/{utils/hooks → hooks}/useForkedRef.ts +0 -0
  78. package/src/{utils/hooks → hooks}/useStateWithCallback.ts +0 -0
  79. package/src/utils/index.ts +3 -2
  80. package/src/utils/isInViewport.ts +11 -0
  81. package/src/utils/isObjectInArray.ts +14 -0
  82. package/src/utils/isVisible.ts +0 -11
@@ -1,28 +1,31 @@
1
1
  import React, { ChangeEvent, forwardRef, useEffect, useMemo, useRef, useState } from 'react'
2
2
  import PropTypes from 'prop-types'
3
3
 
4
- import { cilArrowBottom, cilArrowTop, cilSwapVertical } from '@coreui/icons-pro'
4
+ import { cilArrowBottom, cilArrowTop, cilSwapVertical } from '@coreui/icons'
5
+ import CIcon from '@coreui/icons-react'
5
6
 
6
- import {
7
- ColumnFilterValue,
8
- CSmartTableProps,
9
- Item,
10
- ItemInternal,
11
- SorterValue,
12
- } from './CSmartTableInterface'
13
-
14
- import { CElementCover } from '../element-cover/CElementCover'
15
- import { CSmartPagination } from '../smart-pagination/CSmartPagination'
16
- import { CTable } from '../table/CTable'
17
- import { CTableFoot } from '../table/CTableFoot'
7
+ import { CElementCover } from '../element-cover'
8
+ import { CFormInput, CFormLabel, CFormSelect } from '../form'
9
+ import { CSmartPagination } from '../smart-pagination'
10
+ import { CTable, CTableDataCell, CTableFoot, CTableRow } from '../table'
11
+
12
+ import { CSmartTableProps } from './CSmartTableInterface'
13
+ import type { ColumnFilterValue, FooterItem, Item, SorterValue } from './types'
18
14
 
19
15
  import { CSmartTableBody } from './CSmartTableBody'
20
16
  import { CSmartTableCleaner } from './CSmartTableCleaner'
21
- import { CSmartTableFilter } from './CSmartTableFilter'
22
17
  import { CSmartTableHead } from './CSmartTableHead'
23
- import { CSmartTableItemsPerPageSelector } from './CSmartTableItemsPerPageSelector'
24
18
 
25
- import CIcon from '@coreui/icons-react'
19
+ import { isObjectInArray } from '../../utils'
20
+
21
+ import {
22
+ filterColumns,
23
+ filterTable,
24
+ getColumnNames,
25
+ getColumnNamesFromItems,
26
+ isSortable,
27
+ sortItems,
28
+ } from './utils'
26
29
 
27
30
  export const CSmartTable = forwardRef<HTMLDivElement, CSmartTableProps>(
28
31
  (
@@ -34,9 +37,11 @@ export const CSmartTable = forwardRef<HTMLDivElement, CSmartTableProps>(
34
37
  columnFilterValue, // TODO: consider to use only columnFilter prop
35
38
  columns,
36
39
  columnSorter,
40
+ elementCover,
37
41
  footer,
38
42
  header = true,
39
43
  items,
44
+ itemsNumber,
40
45
  itemsPerPage = 10,
41
46
  itemsPerPageLabel = 'Items per page:',
42
47
  itemsPerPageOptions = [5, 10, 20, 50],
@@ -48,13 +53,16 @@ export const CSmartTable = forwardRef<HTMLDivElement, CSmartTableProps>(
48
53
  onFilteredItemsChange,
49
54
  onItemsPerPageChange,
50
55
  onRowClick,
56
+ onSelectAll,
51
57
  onSelectedItemsChange,
52
58
  onSorterChange,
53
59
  onTableFilterChange,
54
60
  pagination,
55
61
  paginationProps,
56
62
  scopedColumns,
63
+ selected,
57
64
  selectable,
65
+ selectAll = true,
58
66
  sorterValue,
59
67
  sortingIcon = <CIcon width={18} icon={cilSwapVertical} key="csv" />,
60
68
  sortingIconAscending = <CIcon width={18} icon={cilArrowTop} key="cat" />,
@@ -71,12 +79,12 @@ export const CSmartTable = forwardRef<HTMLDivElement, CSmartTableProps>(
71
79
  },
72
80
  ref,
73
81
  ) => {
74
- // instance data
75
82
  const mountedRef = useRef(false)
76
-
77
- const [_activePage, setActivePage] = useState<number>(activePage)
78
- const [_items, setItems] = useState<ItemInternal[]>([])
83
+ const [_activePage, setActivePage] = useState(activePage)
84
+ const [_items, setItems] = useState<Item[]>([])
85
+ const [_itemsNumber, setItemsNumber] = useState(itemsNumber)
79
86
  const [_itemsPerPage, setItemsPerPage] = useState(itemsPerPage)
87
+ const [_selected, setSelected] = useState<Item[]>([])
80
88
  const [columnFilterState, setColumnFilterState] = useState<ColumnFilterValue>({})
81
89
  const [selectedAll, setSelectedAll] = useState<boolean | string>()
82
90
  const [sorterState, setSorterState] = useState<SorterValue>({})
@@ -88,266 +96,225 @@ export const CSmartTable = forwardRef<HTMLDivElement, CSmartTableProps>(
88
96
  setActivePage(activePage)
89
97
  }, [activePage])
90
98
 
91
- // watch
92
- useMemo(() => {
99
+ useEffect(() => {
93
100
  if (items && items.length < _itemsPerPage * _activePage - _itemsPerPage) {
94
101
  setActivePage(1)
95
102
  }
96
103
 
97
- // Create the internal array of items
98
- Array.isArray(items) &&
99
- setItems(
100
- items?.map((item: Item, index: number) => {
101
- return { ...item, _id: index }
102
- }),
103
- )
104
+ items &&
105
+ items.forEach((item: Item) => {
106
+ if (item._selected) {
107
+ const _item = { ...item }
108
+ delete _item['_cellProps']
109
+ delete _item['_props']
110
+ delete _item['_selected']
111
+ setSelected([..._selected, _item])
112
+ }
113
+ })
114
+
115
+ if (Array.isArray(items)) {
116
+ setItems(items)
117
+ setItemsNumber(itemsNumber || items.length)
118
+ }
104
119
  }, [JSON.stringify(items)])
105
120
 
106
- useMemo(() => {
121
+ useEffect(() => {
122
+ Array.isArray(selected) && setSelected(selected)
123
+ }, [JSON.stringify(selected)])
124
+
125
+ useEffect(() => {
126
+ itemsNumber && setItemsNumber(itemsNumber)
127
+ }, [itemsNumber])
128
+
129
+ useEffect(() => {
107
130
  columnFilterValue && setColumnFilterState(columnFilterValue)
108
131
  }, [JSON.stringify(columnFilterValue)])
109
132
 
110
- useMemo(() => {
133
+ useEffect(() => {
111
134
  setSorterState({ ...sorterValue })
112
135
  }, [JSON.stringify(sorterValue)])
113
136
 
114
- useMemo(() => setItemsPerPage(itemsPerPage), [itemsPerPage])
115
-
116
- // functions
117
-
118
- const isSortable = (i: number): boolean | undefined => {
119
- const isDataColumn = itemsDataColumns.includes(rawColumnNames[i])
120
- let column
121
- if (columns) column = columns[i]
122
- return (
123
- columnSorter &&
124
- (!columns ||
125
- typeof column !== 'object' ||
126
- (typeof column === 'object' &&
127
- (typeof column.sorter === 'undefined' || column.sorter))) &&
128
- isDataColumn
129
- )
130
- }
131
-
132
- const sorterChange = (column: string, index: number): void => {
133
- if (!isSortable(index)) {
134
- return
135
- }
136
- //if column changed or sort was descending change asc to true
137
- const state = sorterState ? sorterState : { column: '', state: '' }
138
-
139
- if (state.column === column) {
140
- if (state.state === 0) {
141
- state.state = 'asc'
142
- } else if (state.state === 'asc') {
143
- state.state = 'desc'
144
- } else {
145
- if (typeof columnSorter === 'object' && !columnSorter.resetable) {
146
- state.state = 'asc'
147
- } else {
148
- state.state = 0
149
- }
150
- }
151
- } else {
152
- state.column = column
153
- state.state = 'asc'
154
- }
155
- setSorterState({ ...state })
156
- }
137
+ useEffect(() => setItemsPerPage(itemsPerPage), [itemsPerPage])
157
138
 
158
- const itemsPerPageChange = (event: ChangeEvent<HTMLSelectElement>): void => {
159
- if (
160
- typeof itemsPerPageSelect !== 'object' ||
161
- (typeof itemsPerPageSelect === 'object' && !itemsPerPageSelect.external)
162
- )
163
- setItemsPerPage(Number((event.target as HTMLSelectElement).value))
164
- }
139
+ useEffect(() => {
140
+ mountedRef.current && onActivePageChange && onActivePageChange(_activePage)
141
+ }, [_activePage])
165
142
 
166
- const columnFilterChange = (colName: string, value: any, type?: string): void => {
167
- const isLazy = columnFilter && typeof columnFilter === 'object' && columnFilter.lazy === true
168
- if ((isLazy && type === 'input') || (!isLazy && type === 'change')) {
169
- return
170
- }
171
- const newState = { ...columnFilterState, [`${colName}`]: value }
172
- setActivePage(1)
173
- setColumnFilterState(newState)
174
- }
143
+ useEffect(() => {
144
+ mountedRef.current && onItemsPerPageChange && onItemsPerPageChange(_itemsPerPage)
145
+ itemsPerPage !== _itemsPerPage && setActivePage(1) // TODO: set proper page after _itemsPerPage update
146
+ }, [_itemsPerPage])
175
147
 
176
- const tableFilterChange = (value: string, type: string): void => {
177
- const isLazy = tableFilter && typeof tableFilter === 'object' && tableFilter.lazy === true
178
- if ((isLazy && type === 'input') || (!isLazy && type === 'change')) {
179
- return
180
- }
181
- setActivePage(1)
182
- setTableFilterState(value)
183
- }
148
+ useEffect(() => {
149
+ mountedRef.current && sorterState && onSorterChange && onSorterChange(sorterState)
150
+ }, [JSON.stringify(sorterState)])
184
151
 
185
- const clean = (): void => {
186
- setTableFilterState('')
187
- setColumnFilterState({})
188
- setSorterState({})
189
- }
152
+ useEffect(() => {
153
+ mountedRef.current && onColumnFilterChange && onColumnFilterChange(columnFilterState)
154
+ }, [columnFilterState])
190
155
 
191
- // computed
156
+ useEffect(() => {
157
+ mountedRef.current && onTableFilterChange && onTableFilterChange(tableFilterState)
158
+ }, [tableFilterState])
192
159
 
193
- const genCols = Object.keys(_items[0] || {}).filter((el) => el.charAt(0) !== '_')
194
- const rawColumnNames = columns
195
- ? columns.map((column: any) => {
196
- if (typeof column === 'object') return column.key
197
- else return column
198
- })
199
- : genCols //! || el
200
- const itemsDataColumns = rawColumnNames.filter((name) => genCols.includes(name))
201
-
202
- // variables
203
- const filteredColumns: ItemInternal[] = useMemo(() => {
204
- let items = _items
205
- if (columnFilter && typeof columnFilter === 'object' && columnFilter.external) {
206
- return items
207
- }
208
- Object.entries(columnFilterState).forEach(([key, value]) => {
209
- if (value instanceof Function) {
210
- items = items.filter((item) => value(item[key]))
160
+ useEffect(() => {
161
+ if (selectable) {
162
+ onSelectedItemsChange && onSelectedItemsChange(_selected)
163
+ if (_selected.length === _itemsNumber) {
164
+ setSelectedAll(true)
211
165
  return
212
166
  }
213
167
 
214
- const columnFilter = String(value).toLowerCase()
215
- if (columnFilter && itemsDataColumns.includes(key)) {
216
- items = items.filter((item) => {
217
- return String(item[key]).toLowerCase().includes(columnFilter)
218
- })
168
+ if (_selected.length === 0) {
169
+ setSelectedAll(false)
170
+ return
219
171
  }
220
- })
221
- return items
222
- }, [columnFilterState, JSON.stringify(_items)])
223
172
 
224
- const filteredTable: ItemInternal[] = useMemo(() => {
225
- let items = filteredColumns
226
- if (
227
- !tableFilterState ||
228
- (tableFilter && typeof tableFilter === 'object' && tableFilter.external)
229
- ) {
230
- return items
231
- }
232
- const filter = tableFilterState.toLowerCase()
233
- const valueContainFilter = (val: any) => String(val).toLowerCase().includes(filter)
234
- items = items.filter((item) => {
235
- return !!itemsDataColumns.find((key) => valueContainFilter(item[key]))
236
- })
237
- return items
238
- }, [tableFilterState, JSON.stringify(tableFilterValue), JSON.stringify(filteredColumns)])
239
-
240
- const sortedItems: ItemInternal[] = useMemo(() => {
241
- const col = sorterState?.column
242
- if (
243
- !col ||
244
- !itemsDataColumns.includes(col) ||
245
- (columnSorter && typeof columnSorter === 'object' && columnSorter.external)
246
- ) {
247
- return filteredTable
173
+ if (_selected.length !== 0 && _selected.length !== _itemsNumber) {
174
+ setSelectedAll('indeterminate')
175
+ }
248
176
  }
177
+ }, [JSON.stringify(_selected), _itemsNumber])
249
178
 
250
- const flip = sorterState.state === 'asc' ? 1 : sorterState.state === 'desc' ? -1 : 0
251
- const sorted = filteredTable.slice().sort((item, item2) => {
252
- const value = item[col]
253
- const value2 = item2[col]
254
- const a = typeof value === 'number' ? value : String(value).toLowerCase()
255
- const b = typeof value2 === 'number' ? value2 : String(value2).toLowerCase()
256
- return a > b ? 1 * flip : b > a ? -1 * flip : 0
257
- })
258
- return sorted
259
- }, [
260
- JSON.stringify(filteredTable),
261
- JSON.stringify(sorterState),
262
- JSON.stringify(columnSorter),
263
- JSON.stringify(filteredColumns),
264
- JSON.stringify(_items),
265
- ])
179
+ const columnNames = useMemo(() => getColumnNames(columns, _items), [columns, _items])
266
180
 
267
- const numberOfPages: number = _itemsPerPage ? Math.ceil(sortedItems.length / _itemsPerPage) : 1
181
+ const itemsDataColumns = useMemo(
182
+ () => columnNames.filter((name) => getColumnNamesFromItems(_items).includes(name)),
183
+ [columnNames, _items],
184
+ )
268
185
 
269
- const firstItemOnActivePageIndex: number = _activePage ? (_activePage - 1) * _itemsPerPage : 0
186
+ const filteredColumns: Item[] = useMemo(
187
+ () => filterColumns(_items, columnFilter, columnFilterState, itemsDataColumns),
188
+ [columnFilterState, JSON.stringify(_items)],
189
+ )
270
190
 
271
- const itemsOnActivePage: ItemInternal[] = sortedItems.slice(
272
- firstItemOnActivePageIndex,
273
- firstItemOnActivePageIndex + _itemsPerPage,
191
+ const filteredTable: Item[] = useMemo(
192
+ () => filterTable(filteredColumns, tableFilter, tableFilterState, itemsDataColumns),
193
+ [tableFilterState, JSON.stringify(tableFilterValue), JSON.stringify(filteredColumns)],
274
194
  )
275
195
 
276
- const currentItems: ItemInternal[] = _activePage ? itemsOnActivePage : sortedItems
196
+ const sortedItems: Item[] = useMemo(
197
+ () => sortItems(columnSorter, filteredTable, itemsDataColumns, sorterState),
198
+ [
199
+ JSON.stringify(filteredTable),
200
+ JSON.stringify(sorterState),
201
+ JSON.stringify(columnSorter),
202
+ JSON.stringify(filteredColumns),
203
+ JSON.stringify(_items),
204
+ ],
205
+ )
277
206
 
278
- const isFiltered: string =
279
- tableFilterState || sorterState?.column || Object.values(columnFilterState).join('')
207
+ const numberOfPages: number = _itemsPerPage ? Math.ceil(sortedItems.length / _itemsPerPage) : 1
280
208
 
281
- // effects
209
+ const firstItemOnActivePageIndex: number = _activePage ? (_activePage - 1) * _itemsPerPage : 0
282
210
 
283
- useEffect(() => {
284
- mountedRef.current && onActivePageChange && onActivePageChange(_activePage)
285
- }, [_activePage])
211
+ const currentItems: Item[] = _activePage
212
+ ? sortedItems.slice(firstItemOnActivePageIndex, firstItemOnActivePageIndex + _itemsPerPage)
213
+ : sortedItems
286
214
 
287
215
  useEffect(() => {
288
- mountedRef.current && onItemsPerPageChange && onItemsPerPageChange(_itemsPerPage)
289
- itemsPerPage !== _itemsPerPage && setActivePage(1) // TODO: set proper page after _itemsPerPage update
290
- }, [_itemsPerPage])
216
+ mountedRef.current && onFilteredItemsChange && onFilteredItemsChange(sortedItems)
217
+ }, [JSON.stringify(sortedItems)])
291
218
 
292
- useEffect(() => {
293
- mountedRef.current && sorterState && onSorterChange && onSorterChange(sorterState)
294
- }, [JSON.stringify(sorterState)])
219
+ const handleClean = (): void => {
220
+ setTableFilterState('')
221
+ setColumnFilterState({})
222
+ setSorterState({})
223
+ }
295
224
 
296
- useEffect(() => {
297
- mountedRef.current && onColumnFilterChange && onColumnFilterChange(columnFilterState)
298
- }, [columnFilterState])
225
+ const handleColumnFilterChange = (colName: string, value: any, type?: string): void => {
226
+ const isLazy = columnFilter && typeof columnFilter === 'object' && columnFilter.lazy === true
227
+ if ((isLazy && type === 'input') || (!isLazy && type === 'change')) {
228
+ return
229
+ }
299
230
 
300
- useEffect(() => {
301
- mountedRef.current && onTableFilterChange && onTableFilterChange(tableFilterState)
302
- }, [tableFilterState])
231
+ const newState = { ...columnFilterState, [`${colName}`]: value }
232
+ setActivePage(1)
233
+ setColumnFilterState(newState)
234
+ }
303
235
 
304
- useEffect(() => {
305
- mountedRef.current && onFilteredItemsChange && onFilteredItemsChange(sortedItems)
306
- }, [JSON.stringify(sortedItems)])
236
+ const handleItemsPerPageChange = (event: ChangeEvent<HTMLSelectElement>): void => {
237
+ if (
238
+ typeof itemsPerPageSelect !== 'object' ||
239
+ (typeof itemsPerPageSelect === 'object' && !itemsPerPageSelect.external)
240
+ ) {
241
+ setItemsPerPage(Number((event.target as HTMLSelectElement).value))
242
+ }
243
+ }
244
+
245
+ const handleRowChecked = (item: Item, value: boolean) => {
246
+ if (value && !isObjectInArray(_selected, item, ['_cellProps', '_props', '_selected'])) {
247
+ setSelected([..._selected, item])
248
+ return
249
+ }
307
250
 
308
- const handleRowChecked = (id: number, value: boolean) => {
309
- const newArr = [..._items]
310
- newArr[id]._selected = value
311
- setItems(newArr)
251
+ setSelected(
252
+ _selected.filter(
253
+ (_item: Item) => !isObjectInArray([_item], item, ['_cellProps', '_props', '_selected']),
254
+ ),
255
+ )
312
256
  }
313
257
 
314
258
  const handleSelectAllChecked = () => {
315
259
  if (selectedAll === true) {
316
- setItems(
317
- _items.map((item: ItemInternal) => {
318
- return { ...item, _selected: false }
319
- }),
320
- )
260
+ setSelected([])
321
261
  return
322
262
  }
323
263
 
324
- setItems(
325
- _items.map((item: ItemInternal) => {
326
- return { ...item, _selected: true }
264
+ onSelectAll && onSelectAll()
265
+
266
+ if (selectAll && typeof selectAll === 'object' && selectAll.external) {
267
+ return
268
+ }
269
+
270
+ setSelected(
271
+ _items.map((item) => {
272
+ delete item['_cellProps']
273
+ delete item['_props']
274
+ delete item['_selected']
275
+
276
+ return item
327
277
  }),
328
278
  )
329
279
  }
330
280
 
331
- useEffect(() => {
332
- if (selectable) {
333
- const selected = _items.filter((item) => item._selected === true)
334
- onSelectedItemsChange && onSelectedItemsChange(selected)
281
+ const handleSorterChange = (column: string, index: number): void => {
282
+ if (!isSortable(index, columns, columnSorter, itemsDataColumns, columnNames)) {
283
+ return
284
+ }
335
285
 
336
- if (selected.length === _items.length) {
337
- setSelectedAll(true)
338
- return
339
- }
286
+ //if column changed or sort was descending change asc to true
287
+ const state = sorterState ? sorterState : { column: '', state: '' }
340
288
 
341
- if (selected.length === 0) {
342
- setSelectedAll(false)
343
- return
289
+ if (state.column === column) {
290
+ if (state.state === 0) {
291
+ state.state = 'asc'
292
+ } else if (state.state === 'asc') {
293
+ state.state = 'desc'
294
+ } else {
295
+ if (typeof columnSorter === 'object' && !columnSorter.resetable) {
296
+ state.state = 'asc'
297
+ } else {
298
+ state.state = 0
299
+ }
344
300
  }
301
+ } else {
302
+ state.column = column
303
+ state.state = 'asc'
304
+ }
345
305
 
346
- if (selected.length !== 0 && selected.length !== _items.length) {
347
- setSelectedAll('indeterminate')
348
- }
306
+ setSorterState({ ...state })
307
+ }
308
+
309
+ const handleTableFilterChange = (value: string, type: string): void => {
310
+ const isLazy = tableFilter && typeof tableFilter === 'object' && tableFilter.lazy === true
311
+ if ((isLazy && type === 'input') || (!isLazy && type === 'change')) {
312
+ return
349
313
  }
350
- }, [_items])
314
+
315
+ setActivePage(1)
316
+ setTableFilterState(value)
317
+ }
351
318
 
352
319
  useEffect(() => {
353
320
  mountedRef.current = true
@@ -362,26 +329,39 @@ export const CSmartTable = forwardRef<HTMLDivElement, CSmartTableProps>(
362
329
  <>
363
330
  <div className="col-auto p-0">
364
331
  {tableFilter && (
365
- <CSmartTableFilter
366
- filterLabel={tableFilterLabel}
367
- filterPlaceholder={tableFilterPlaceholder}
368
- onInput={(e) => {
369
- tableFilterChange((e.target as HTMLInputElement).value, 'input')
370
- }}
371
- onChange={(e) => {
372
- tableFilterChange((e.target as HTMLInputElement).value, 'change')
373
- }}
374
- value={tableFilterState || ''}
375
- />
332
+ <div className="row mb-2">
333
+ <CFormLabel className="col-sm-auto col-form-label">
334
+ {tableFilterLabel}
335
+ </CFormLabel>
336
+ <div className="col-sm-auto">
337
+ <CFormInput
338
+ onInput={(e) => {
339
+ handleTableFilterChange((e.target as HTMLInputElement).value, 'input')
340
+ }}
341
+ onChange={(e) => {
342
+ handleTableFilterChange(
343
+ (e.target as HTMLInputElement).value,
344
+ 'change',
345
+ )
346
+ }}
347
+ placeholder={tableFilterPlaceholder}
348
+ value={tableFilterState || ''}
349
+ />
350
+ </div>
351
+ </div>
376
352
  )}
377
353
  </div>
378
354
  <div className="col-auto p-0">
379
355
  {cleaner && (
380
356
  <CSmartTableCleaner
381
- isFiltered={isFiltered}
382
- onClick={() => clean()}
357
+ isFiltered={
358
+ tableFilterState ||
359
+ sorterState?.column ||
360
+ Object.values(columnFilterState).join('')
361
+ }
362
+ onClick={() => handleClean()}
383
363
  onKeyUp={(event) => {
384
- if (event.key === 'Enter') clean()
364
+ if (event.key === 'Enter') handleClean()
385
365
  }}
386
366
  />
387
367
  )}
@@ -398,20 +378,23 @@ export const CSmartTable = forwardRef<HTMLDivElement, CSmartTableProps>(
398
378
  {...tableHeadProps}
399
379
  columnFilter={columnFilter}
400
380
  columnFilterState={columnFilterState}
401
- columns={columns ? columns : rawColumnNames}
381
+ columns={columns ? columns : columnNames}
402
382
  columnSorter={columnSorter}
403
383
  items={_items}
404
384
  selectable={selectable}
405
- selectAll={selectedAll}
385
+ selectAll={selectAll}
386
+ selectedAll={selectedAll}
406
387
  sorterState={sorterState}
407
388
  sortingIcon={sortingIcon}
408
389
  sortingIconAscending={sortingIconAscending}
409
390
  sortingIconDescending={sortingIconDescending}
410
- handleFilterOnChange={(key, event) => columnFilterChange(key, event, 'change')}
411
- handleFilterOnInput={(key, event) => columnFilterChange(key, event, 'input')}
412
- handleOnCustomFilterChange={(key, event) => columnFilterChange(key, event)}
391
+ handleFilterOnChange={(key, event) =>
392
+ handleColumnFilterChange(key, event, 'change')
393
+ }
394
+ handleFilterOnInput={(key, event) => handleColumnFilterChange(key, event, 'input')}
395
+ handleOnCustomFilterChange={(key, event) => handleColumnFilterChange(key, event)}
413
396
  handleSelectAllChecked={() => handleSelectAllChecked()}
414
- handleSort={(key, index) => sorterChange(key, index)}
397
+ handleSort={(key, index) => handleSorterChange(key, index)}
415
398
  />
416
399
  )}
417
400
  <CSmartTableBody
@@ -422,33 +405,51 @@ export const CSmartTable = forwardRef<HTMLDivElement, CSmartTableProps>(
422
405
  onRowClick={(item, index, columnName, event) =>
423
406
  clickableRows && onRowClick && onRowClick(item, index, columnName, event)
424
407
  }
425
- onRowChecked={(id, value) => handleRowChecked(id, value)}
426
- rawColumnNames={rawColumnNames}
408
+ onRowChecked={(item, value) => handleRowChecked(item, value)}
409
+ columnNames={columnNames}
427
410
  scopedColumns={scopedColumns}
428
411
  selectable={selectable}
412
+ selected={_selected}
429
413
  {...tableBodyProps}
430
414
  />
431
- {footer && (
415
+ {typeof footer === 'boolean' && footer && (
432
416
  <CSmartTableHead
433
417
  component={CTableFoot}
434
418
  {...tableFootProps}
435
419
  columnFilter={false}
436
420
  columnSorter={false}
437
- columns={columns ? columns : rawColumnNames}
421
+ columns={columns ? columns : columnNames}
438
422
  items={_items}
439
423
  handleSelectAllChecked={() => handleSelectAllChecked()}
440
424
  selectable={selectable}
441
- selectAll={selectedAll}
425
+ selectAll={selectAll}
426
+ selectedAll={selectedAll}
442
427
  />
443
428
  )}
429
+ {Array.isArray(footer) && (
430
+ <CTableFoot {...tableFootProps}>
431
+ <CTableRow>
432
+ {footer.map((item: FooterItem | string, index: number) => (
433
+ <CTableDataCell
434
+ {...(typeof item === 'object' && item._props && { ...item._props })}
435
+ key={index}
436
+ >
437
+ {typeof item === 'object' ? item.label : item}
438
+ </CTableDataCell>
439
+ ))}
440
+ </CTableRow>
441
+ </CTableFoot>
442
+ )}
444
443
  </CTable>
445
444
  {loading && (
446
445
  <CElementCover
447
446
  boundaries={[
448
- { sides: ['top'], query: 'td' },
447
+ { sides: ['top'], query: 'tbody' },
449
448
  { sides: ['bottom'], query: 'tbody' },
450
449
  ]}
451
- />
450
+ >
451
+ {elementCover}
452
+ </CElementCover>
452
453
  )}
453
454
  </div>
454
455
 
@@ -470,12 +471,26 @@ export const CSmartTable = forwardRef<HTMLDivElement, CSmartTableProps>(
470
471
  </div>
471
472
  <div className="col-auto ms-auto">
472
473
  {itemsPerPageSelect && (
473
- <CSmartTableItemsPerPageSelector
474
- itemsPerPage={_itemsPerPage}
475
- itemsPerPageLabel={itemsPerPageLabel}
476
- itemsPerPageOptions={itemsPerPageOptions}
477
- onChange={(event: ChangeEvent<HTMLSelectElement>) => itemsPerPageChange(event)}
478
- />
474
+ <div className="row">
475
+ <CFormLabel className="col-auto col-form-label">{itemsPerPageLabel}</CFormLabel>
476
+ <div className="col-auto">
477
+ <CFormSelect
478
+ defaultValue={_itemsPerPage}
479
+ onChange={(event: ChangeEvent<HTMLSelectElement>) =>
480
+ handleItemsPerPageChange(event)
481
+ }
482
+ >
483
+ {itemsPerPageOptions &&
484
+ itemsPerPageOptions.map((number, index) => {
485
+ return (
486
+ <option value={number} key={index}>
487
+ {number}
488
+ </option>
489
+ )
490
+ })}
491
+ </CFormSelect>
492
+ </div>
493
+ </div>
479
494
  )}
480
495
  </div>
481
496
  </div>
@@ -493,9 +508,11 @@ CSmartTable.propTypes = {
493
508
  columnFilterValue: PropTypes.object,
494
509
  columns: PropTypes.array,
495
510
  columnSorter: PropTypes.oneOfType([PropTypes.bool, PropTypes.object]),
496
- footer: PropTypes.bool,
511
+ elementCover: PropTypes.node,
512
+ footer: PropTypes.oneOfType([PropTypes.bool, PropTypes.array]),
497
513
  header: PropTypes.bool,
498
514
  items: PropTypes.array,
515
+ itemsNumber: PropTypes.number,
499
516
  itemsPerPage: PropTypes.number,
500
517
  itemsPerPageLabel: PropTypes.string,
501
518
  itemsPerPageOptions: PropTypes.array,
@@ -507,13 +524,16 @@ CSmartTable.propTypes = {
507
524
  onFilteredItemsChange: PropTypes.func,
508
525
  onItemsPerPageChange: PropTypes.func,
509
526
  onRowClick: PropTypes.func,
527
+ onSelectAll: PropTypes.func,
510
528
  onSelectedItemsChange: PropTypes.func,
511
- onSorterChange: PropTypes.func, // TODO: change to `onColumnSorter` in v5
529
+ onSorterChange: PropTypes.func, // TODO: change to `onColumnSorterChange` in v5
512
530
  onTableFilterChange: PropTypes.func,
513
531
  pagination: PropTypes.oneOfType([PropTypes.bool, PropTypes.object]),
514
532
  paginationProps: PropTypes.any, // TODO: update
515
533
  scopedColumns: PropTypes.object,
516
534
  selectable: PropTypes.bool,
535
+ selectAll: PropTypes.oneOfType([PropTypes.bool, PropTypes.object]),
536
+ selected: PropTypes.array,
517
537
  sorterValue: PropTypes.object,
518
538
  sortingIcon: PropTypes.node,
519
539
  sortingIconAscending: PropTypes.node,