@coreui/react-pro 4.8.0 → 4.9.0-next.1

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 (166) hide show
  1. package/README.md +1 -1
  2. package/dist/components/alert/CAlert.d.ts +1 -1
  3. package/dist/components/avatar/CAvatar.d.ts +1 -1
  4. package/dist/components/badge/CBadge.d.ts +1 -1
  5. package/dist/components/button/CButton.d.ts +1 -1
  6. package/dist/components/calendar/index.d.ts +2 -0
  7. package/dist/components/calendar/utils.d.ts +22 -0
  8. package/dist/components/callout/CCallout.d.ts +1 -1
  9. package/dist/components/card/CCard.d.ts +1 -1
  10. package/dist/components/date-picker/CDatePicker.d.ts +1 -1
  11. package/dist/components/date-range-picker/CDateRangePicker.d.ts +3 -3
  12. package/dist/components/date-range-picker/utils.d.ts +1 -0
  13. package/dist/components/dropdown/CDropdown.d.ts +1 -1
  14. package/dist/components/dropdown/CDropdownToggle.d.ts +1 -1
  15. package/dist/components/element-cover/CElementCover.d.ts +0 -4
  16. package/dist/components/element-cover/index.d.ts +2 -0
  17. package/dist/components/form/CFormCheck.d.ts +1 -1
  18. package/dist/components/index.d.ts +1 -0
  19. package/dist/components/list-group/CListGroupItem.d.ts +4 -5
  20. package/dist/components/multi-select/CMultiSelect.d.ts +1 -13
  21. package/dist/components/multi-select/CMultiSelectNativeSelect.d.ts +1 -1
  22. package/dist/components/multi-select/CMultiSelectOptions.d.ts +1 -1
  23. package/dist/components/multi-select/CMultiSelectSelection.d.ts +1 -1
  24. package/dist/components/multi-select/types.d.ts +14 -0
  25. package/dist/components/multi-select/utils.d.ts +6 -0
  26. package/dist/components/navbar/CNavbar.d.ts +1 -1
  27. package/dist/components/offcanvas/COffcanvas.d.ts +7 -1
  28. package/dist/components/picker/CPicker.d.ts +1 -1
  29. package/dist/components/placeholder/CPlaceholder.d.ts +1 -1
  30. package/dist/components/popover/CPopover.d.ts +1 -1
  31. package/dist/components/progress/CProgressBar.d.ts +1 -1
  32. package/dist/components/smart-table/CSmartTableBody.d.ts +6 -5
  33. package/dist/components/smart-table/CSmartTableHead.d.ts +6 -3
  34. package/dist/components/smart-table/CSmartTableInterface.d.ts +38 -59
  35. package/dist/components/smart-table/types.d.ts +53 -0
  36. package/dist/components/smart-table/utils.d.ts +16 -0
  37. package/dist/components/spinner/CSpinner.d.ts +1 -1
  38. package/dist/components/table/CTable.d.ts +2 -18
  39. package/dist/components/table/CTableBody.d.ts +1 -1
  40. package/dist/components/table/CTableDataCell.d.ts +1 -1
  41. package/dist/components/table/CTableFoot.d.ts +1 -1
  42. package/dist/components/table/CTableHead.d.ts +1 -1
  43. package/dist/components/table/CTableHeaderCell.d.ts +1 -1
  44. package/dist/components/table/CTableRow.d.ts +1 -1
  45. package/dist/components/table/types.d.ts +17 -0
  46. package/dist/components/table/utils.d.ts +5 -0
  47. package/dist/components/time-picker/types.d.ts +15 -0
  48. package/dist/components/time-picker/utils.d.ts +23 -0
  49. package/dist/components/toast/CToast.d.ts +1 -1
  50. package/dist/components/toast/CToastClose.d.ts +1 -1
  51. package/dist/components/tooltip/CTooltip.d.ts +1 -1
  52. package/dist/components/virtual-scroller/CVirtualScroller.d.ts +0 -8
  53. package/dist/components/widgets/CWidgetStatsA.d.ts +1 -1
  54. package/dist/components/widgets/CWidgetStatsB.d.ts +1 -1
  55. package/dist/components/widgets/CWidgetStatsC.d.ts +1 -1
  56. package/dist/components/widgets/CWidgetStatsD.d.ts +1 -1
  57. package/dist/components/widgets/CWidgetStatsF.d.ts +1 -1
  58. package/dist/hooks/index.d.ts +3 -0
  59. package/dist/hooks/useForkedRef.d.ts +7 -0
  60. package/dist/hooks/useStateWithCallback.d.ts +2 -0
  61. package/dist/index.es.js +1171 -1109
  62. package/dist/index.es.js.map +1 -1
  63. package/dist/index.js +1171 -1108
  64. package/dist/index.js.map +1 -1
  65. package/dist/props.d.ts +8 -0
  66. package/dist/types.d.ts +7 -0
  67. package/dist/utils/index.d.ts +2 -3
  68. package/dist/utils/isObjectInArray.d.ts +2 -0
  69. package/package.json +11 -11
  70. package/src/components/accordion/CAccordionBody.tsx +1 -0
  71. package/src/components/accordion/CAccordionCollapse.tsx +1 -0
  72. package/src/components/alert/CAlert.tsx +3 -2
  73. package/src/components/avatar/CAvatar.tsx +2 -8
  74. package/src/components/backdrop/CBackdrop.tsx +2 -2
  75. package/src/components/badge/CBadge.tsx +2 -10
  76. package/src/components/button/CButton.tsx +3 -1
  77. package/src/components/calendar/CCalendar.tsx +351 -240
  78. package/src/components/calendar/index.ts +3 -0
  79. package/src/{utils/calendar.ts → components/calendar/utils.ts} +1 -47
  80. package/src/components/callout/CCallout.tsx +2 -1
  81. package/src/components/card/CCard.tsx +2 -1
  82. package/src/components/carousel/CCarousel.tsx +1 -1
  83. package/src/components/carousel/CCarouselItem.tsx +1 -1
  84. package/src/components/collapse/CCollapse.tsx +1 -1
  85. package/src/components/date-picker/CDatePicker.tsx +1 -1
  86. package/src/components/date-range-picker/CDateRangePicker.tsx +4 -4
  87. package/src/components/date-range-picker/utils.ts +49 -0
  88. package/src/components/dropdown/CDropdown.tsx +3 -2
  89. package/src/components/dropdown/CDropdownItem.tsx +1 -2
  90. package/src/components/dropdown/CDropdownMenu.tsx +2 -1
  91. package/src/components/dropdown/CDropdownToggle.tsx +5 -4
  92. package/src/components/element-cover/CElementCover.tsx +16 -22
  93. package/src/components/element-cover/index.ts +3 -0
  94. package/src/components/form/CFormCheck.tsx +3 -3
  95. package/src/components/form/CFormControlValidation.tsx +0 -1
  96. package/src/components/form/CFormControlWrapper.tsx +0 -1
  97. package/src/components/form/CFormInput.tsx +8 -4
  98. package/src/components/form/CFormRange.tsx +1 -2
  99. package/src/components/form/CFormSelect.tsx +1 -2
  100. package/src/components/form/CFormTextarea.tsx +1 -2
  101. package/src/components/index.ts +1 -0
  102. package/src/components/list-group/CListGroupItem.tsx +8 -8
  103. package/src/components/list-group/CListGroupItem.tsx~HEAD +3 -1
  104. package/src/components/modal/CModal.tsx +2 -2
  105. package/src/components/modal/CModalHeader.tsx +3 -2
  106. package/src/components/multi-select/CMultiSelect.tsx +15 -74
  107. package/src/components/multi-select/CMultiSelectNativeSelect.tsx +1 -1
  108. package/src/components/multi-select/CMultiSelectOptions.tsx +2 -2
  109. package/src/components/multi-select/CMultiSelectSelection.tsx +1 -1
  110. package/src/components/multi-select/types.ts +15 -0
  111. package/src/components/multi-select/utils.ts +92 -0
  112. package/src/components/nav/CNavItem.tsx +1 -0
  113. package/src/components/nav/CNavLink.tsx +4 -3
  114. package/src/components/navbar/CNavbar.tsx +2 -1
  115. package/src/components/offcanvas/COffcanvas.tsx +38 -19
  116. package/src/components/picker/CPicker.tsx +1 -1
  117. package/src/components/placeholder/CPlaceholder.tsx +2 -1
  118. package/src/components/popover/CPopover.tsx +2 -1
  119. package/src/components/progress/CProgress.tsx +1 -0
  120. package/src/components/progress/CProgressBar.tsx +2 -1
  121. package/src/components/sidebar/CSidebar.tsx +3 -2
  122. package/src/components/smart-table/CSmartTable.tsx +263 -257
  123. package/src/components/smart-table/CSmartTableBody.tsx +98 -107
  124. package/src/components/smart-table/CSmartTableCleaner.tsx +1 -1
  125. package/src/components/smart-table/CSmartTableFilter.tsx +1 -3
  126. package/src/components/smart-table/CSmartTableHead.tsx +56 -103
  127. package/src/components/smart-table/CSmartTableInterface.tsx +49 -71
  128. package/src/components/smart-table/CSmartTableItemsPerPageSelector.tsx +1 -2
  129. package/src/components/smart-table/types.ts +65 -0
  130. package/src/components/smart-table/utils.ts +199 -0
  131. package/src/components/spinner/CSpinner.tsx +2 -1
  132. package/src/components/table/CTable.tsx +25 -59
  133. package/src/components/table/CTableBody.tsx +2 -1
  134. package/src/components/table/CTableDataCell.tsx +2 -1
  135. package/src/components/table/CTableFoot.tsx +2 -1
  136. package/src/components/table/CTableHead.tsx +2 -1
  137. package/src/components/table/CTableHeaderCell.tsx +2 -1
  138. package/src/components/table/CTableRow.tsx +2 -1
  139. package/src/components/table/types.ts +20 -0
  140. package/src/components/table/utils.ts +29 -0
  141. package/src/components/tabs/CTabPane.tsx +2 -1
  142. package/src/components/time-picker/CTimePicker.tsx +48 -23
  143. package/src/components/time-picker/CTimePickerRollCol.tsx +1 -1
  144. package/src/components/time-picker/types.ts +15 -0
  145. package/src/{utils/time.ts → components/time-picker/utils.ts} +43 -2
  146. package/src/components/toast/CToast.tsx +4 -4
  147. package/src/components/toast/CToastClose.tsx +2 -2
  148. package/src/components/toast/CToaster.tsx +2 -1
  149. package/src/components/tooltip/CTooltip.tsx +3 -2
  150. package/src/components/virtual-scroller/CVirtualScroller.tsx +32 -55
  151. package/src/components/widgets/CWidgetStatsA.tsx +3 -3
  152. package/src/components/widgets/CWidgetStatsB.tsx +4 -4
  153. package/src/components/widgets/CWidgetStatsC.tsx +4 -4
  154. package/src/components/widgets/CWidgetStatsD.tsx +4 -5
  155. package/src/components/widgets/CWidgetStatsE.tsx +1 -2
  156. package/src/components/widgets/CWidgetStatsF.tsx +3 -4
  157. package/src/{components/Types.tsx → props.ts} +1 -68
  158. package/src/types.ts +68 -0
  159. package/src/utils/index.ts +2 -3
  160. package/src/utils/isObjectInArray.ts +14 -0
  161. package/src/components/multi-select/CMultiSelectCleaner.tsx +0 -18
  162. package/src/utils/getNextSibling.ts +0 -18
  163. package/src/utils/getPreviousSibling.ts +0 -18
  164. /package/src/{utils/hooks → hooks}/index.ts +0 -0
  165. /package/src/{utils/hooks → hooks}/useForkedRef.ts +0 -0
  166. /package/src/{utils/hooks → hooks}/useStateWithCallback.ts +0 -0
@@ -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
- FooterItem,
10
- Item,
11
- ItemInternal,
12
- SorterValue,
13
- } from './CSmartTableInterface'
14
-
15
- import { CElementCover } from '../element-cover/CElementCover'
16
- import { CSmartPagination } from '../smart-pagination/CSmartPagination'
7
+ import { CElementCover } from '../element-cover'
8
+ import { CFormInput, CFormLabel, CFormSelect } from '../form'
9
+ import { CSmartPagination } from '../smart-pagination'
17
10
  import { CTable, CTableDataCell, CTableFoot, CTableRow } from '../table'
18
11
 
12
+ import { CSmartTableProps } from './CSmartTableInterface'
13
+ import type { ColumnFilterValue, FooterItem, Item, SorterValue } from './types'
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()}
383
- onKeyUp={(event) => {
384
- if (event.key === 'Enter') clean()
357
+ isFiltered={
358
+ tableFilterState ||
359
+ sorterState?.column ||
360
+ Object.values(columnFilterState).join('')
361
+ }
362
+ onClick={() => handleClean()}
363
+ onKeyDown={(event) => {
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,10 +405,11 @@ 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
415
  {typeof footer === 'boolean' && footer && (
@@ -434,11 +418,12 @@ export const CSmartTable = forwardRef<HTMLDivElement, CSmartTableProps>(
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
  )}
444
429
  {Array.isArray(footer) && (
@@ -459,10 +444,12 @@ export const CSmartTable = forwardRef<HTMLDivElement, CSmartTableProps>(
459
444
  {loading && (
460
445
  <CElementCover
461
446
  boundaries={[
462
- { sides: ['top'], query: 'td' },
447
+ { sides: ['top'], query: 'tbody' },
463
448
  { sides: ['bottom'], query: 'tbody' },
464
449
  ]}
465
- />
450
+ >
451
+ {elementCover}
452
+ </CElementCover>
466
453
  )}
467
454
  </div>
468
455
 
@@ -484,12 +471,26 @@ export const CSmartTable = forwardRef<HTMLDivElement, CSmartTableProps>(
484
471
  </div>
485
472
  <div className="col-auto ms-auto">
486
473
  {itemsPerPageSelect && (
487
- <CSmartTableItemsPerPageSelector
488
- itemsPerPage={_itemsPerPage}
489
- itemsPerPageLabel={itemsPerPageLabel}
490
- itemsPerPageOptions={itemsPerPageOptions}
491
- onChange={(event: ChangeEvent<HTMLSelectElement>) => itemsPerPageChange(event)}
492
- />
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>
493
494
  )}
494
495
  </div>
495
496
  </div>
@@ -507,9 +508,11 @@ CSmartTable.propTypes = {
507
508
  columnFilterValue: PropTypes.object,
508
509
  columns: PropTypes.array,
509
510
  columnSorter: PropTypes.oneOfType([PropTypes.bool, PropTypes.object]),
511
+ elementCover: PropTypes.node,
510
512
  footer: PropTypes.oneOfType([PropTypes.bool, PropTypes.array]),
511
513
  header: PropTypes.bool,
512
514
  items: PropTypes.array,
515
+ itemsNumber: PropTypes.number,
513
516
  itemsPerPage: PropTypes.number,
514
517
  itemsPerPageLabel: PropTypes.string,
515
518
  itemsPerPageOptions: PropTypes.array,
@@ -521,13 +524,16 @@ CSmartTable.propTypes = {
521
524
  onFilteredItemsChange: PropTypes.func,
522
525
  onItemsPerPageChange: PropTypes.func,
523
526
  onRowClick: PropTypes.func,
527
+ onSelectAll: PropTypes.func,
524
528
  onSelectedItemsChange: PropTypes.func,
525
- onSorterChange: PropTypes.func, // TODO: change to `onColumnSorter` in v5
529
+ onSorterChange: PropTypes.func, // TODO: change to `onColumnSorterChange` in v5
526
530
  onTableFilterChange: PropTypes.func,
527
531
  pagination: PropTypes.oneOfType([PropTypes.bool, PropTypes.object]),
528
532
  paginationProps: PropTypes.any, // TODO: update
529
533
  scopedColumns: PropTypes.object,
530
534
  selectable: PropTypes.bool,
535
+ selectAll: PropTypes.oneOfType([PropTypes.bool, PropTypes.object]),
536
+ selected: PropTypes.array,
531
537
  sorterValue: PropTypes.object,
532
538
  sortingIcon: PropTypes.node,
533
539
  sortingIconAscending: PropTypes.node,