@inceptionbg/iui 2.0.8 → 2.0.10

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 (138) hide show
  1. package/dist/icons/index.d.ts +2 -2
  2. package/dist/icons/index.js +1 -1
  3. package/dist/index.d.ts +286 -259
  4. package/dist/index.js +1 -1
  5. package/dist/index.js.map +1 -1
  6. package/dist/iui.css +1 -1
  7. package/idea/GridTable/GridTable.tsx +119 -0
  8. package/idea/GridTable/gridTable.scss +42 -0
  9. package/{src/components → idea}/Table/Components/Print/CustomTablePrint.tsx +2 -2
  10. package/{src/components → idea}/Table/Components/Print/TablePrint.tsx +2 -2
  11. package/{src/components → idea}/Table/Components/SetTableFilter.tsx +1 -1
  12. package/{src/components → idea}/Table/Components/TableOptions.tsx +4 -4
  13. package/idea/{Table2 → Table}/Table.tsx +151 -281
  14. package/idea/Table/hooks/useDefaultTemplate.ts +20 -0
  15. package/{src/components → idea}/Table/hooks/useTableKeyboard.ts +1 -2
  16. package/idea/Table/hooks/useTableSelect.ts +11 -0
  17. package/package.json +1 -1
  18. package/src/assets/icons/index.ts +1 -1
  19. package/src/assets/icons/light/faClipboardCheck.ts +15 -0
  20. package/src/assets/icons/light/faHouse.ts +15 -15
  21. package/src/assets/icons/light/faIdBadge.ts +15 -15
  22. package/src/assets/icons/light/faPen.ts +15 -0
  23. package/src/components/Button/IconButton.tsx +3 -1
  24. package/src/components/Dialog/Dialog.tsx +59 -123
  25. package/src/components/Dialog/components/DialogFooter.tsx +92 -0
  26. package/src/components/Dialog/hooks/useDialogKeyboard.ts +6 -5
  27. package/src/components/Header/Header.tsx +1 -1
  28. package/src/components/Inputs/DateInput/DateInput.tsx +108 -102
  29. package/src/components/Inputs/DateInput/components/DatePartInput.tsx +7 -3
  30. package/src/components/Inputs/InputWrapper.tsx +6 -1
  31. package/src/components/Inputs/SearchInput.tsx +9 -4
  32. package/src/components/Inputs/Select2/Select.tsx +65 -30
  33. package/src/components/Inputs/Select2/select.scss +13 -14
  34. package/src/components/Inputs/Selects/components/SelectWrapper.tsx +4 -2
  35. package/src/components/Inputs/Selects/utils/selectStyles.ts +9 -12
  36. package/src/components/Menu/Menu.tsx +10 -2
  37. package/src/components/Menu/MenuItem.tsx +11 -10
  38. package/src/components/Menu/hooks/useMenuPosition.tsx +23 -6
  39. package/src/components/Pullover/Pullover.tsx +122 -59
  40. package/src/components/Table/Table.tsx +78 -342
  41. package/src/components/Table/components/edit/TableEditRow.tsx +69 -0
  42. package/src/components/Table/components/filters/FilterItem.tsx +15 -0
  43. package/src/components/Table/components/filters/TableFilters.tsx +125 -0
  44. package/src/components/Table/components/footer/TableFooter.tsx +128 -0
  45. package/src/components/Table/components/header/TableHeader.tsx +42 -0
  46. package/src/components/Table/components/header/TableHeaderRow.tsx +47 -0
  47. package/src/components/Table/components/items/TableItemActions.tsx +66 -0
  48. package/src/components/Table/components/select/TableSelect.tsx +49 -0
  49. package/src/components/Table/components/sort/TableSort.tsx +52 -0
  50. package/src/components/Table/contexts/TableContext.tsx +123 -0
  51. package/src/components/Table/hooks/localHooks/useLocalTableColumns.tsx +73 -0
  52. package/src/components/Table/hooks/localHooks/useLocalTableData.tsx +78 -0
  53. package/src/components/Table/hooks/localHooks/useLocalTableKeyboard.ts +173 -0
  54. package/src/components/Table/hooks/localHooks/useLocalTablePagination.ts +12 -0
  55. package/src/components/Table/hooks/useTableEdit.tsx +111 -0
  56. package/src/components/Table/hooks/useTableFilterFields.tsx +150 -0
  57. package/src/components/Table/hooks/useTablePagination.ts +16 -0
  58. package/src/components/Table/hooks/useTableSearch.ts +29 -0
  59. package/src/components/Table/hooks/useTableSort.ts +8 -0
  60. package/src/components/Tooltip/Tooltip.tsx +1 -1
  61. package/src/components/Wrappers/PageLayout.tsx +9 -12
  62. package/src/hooks/useGetFocusableElements.ts +42 -0
  63. package/src/hooks/useLocalPopoverControl.ts +38 -0
  64. package/src/hooks/usePopupControl.ts +13 -0
  65. package/src/index.ts +53 -39
  66. package/src/styles/components/_accordions.scss +1 -1
  67. package/src/styles/components/_badge.scss +4 -3
  68. package/src/styles/components/_card.scss +1 -1
  69. package/src/styles/components/_dialog.scss +8 -8
  70. package/src/styles/components/_input.scss +1 -1
  71. package/src/styles/components/_inputCheckbox.scss +1 -1
  72. package/src/styles/components/_inputDateTime.scss +2 -2
  73. package/src/styles/components/_inputRadio.scss +1 -1
  74. package/src/styles/components/_inputSelect.scss +6 -4
  75. package/src/styles/components/_menu-v2.scss +1 -1
  76. package/src/styles/components/_menu.scss +23 -15
  77. package/src/styles/components/_pullover.scss +74 -18
  78. package/src/styles/components/_table.scss +151 -142
  79. package/src/styles/components/_widget.scss +1 -1
  80. package/src/styles/variables/_bp.scss +1 -0
  81. package/src/styles/variables/_variables.scss +4 -2
  82. package/src/types/IKeyboard.ts +2 -1
  83. package/src/types/IMenu.ts +1 -0
  84. package/src/types/ISelect.ts +1 -0
  85. package/src/types/ITable.ts +87 -80
  86. package/src/utils/fileUtils.ts +3 -3
  87. package/src/utils/i18n/i18nIUICyrilic.ts +4 -0
  88. package/src/utils/i18n/i18nIUILatin.ts +3 -1
  89. package/src/utils/i18n/i18nIUIMe.ts +4 -0
  90. package/src/utils/{ObjectUtils.ts → objectUtils.ts} +8 -8
  91. package/src/utils/popupUtils.ts +82 -0
  92. package/src/utils/{TableUtils.ts → tableUtils.ts} +5 -5
  93. package/src/utils/{Toasts.ts → toasts.ts} +6 -6
  94. package/src/utils/{UrlUtils.ts → urlUtils.ts} +4 -4
  95. package/idea/Table2/Components/Columns/ColumnsList.tsx +0 -56
  96. package/idea/Table2/Components/Columns/SetColumnsList.tsx +0 -107
  97. package/idea/Table2/Components/Edit/ItemActionsMenu.tsx +0 -87
  98. package/idea/Table2/Components/Edit/ItemEditOptionsButtons.tsx +0 -32
  99. package/idea/Table2/Components/Edit/TableEditRow.tsx +0 -56
  100. package/idea/Table2/Components/FilterItem.tsx +0 -20
  101. package/idea/Table2/Components/Header/TableHeader.tsx +0 -35
  102. package/idea/Table2/Components/Header/TableHeaderRow.tsx +0 -37
  103. package/idea/Table2/Components/Print/CustomTablePrint.tsx +0 -119
  104. package/idea/Table2/Components/Print/TablePrint.tsx +0 -208
  105. package/idea/Table2/Components/SetSortList.tsx +0 -33
  106. package/idea/Table2/Components/SetTableFilter.tsx +0 -90
  107. package/idea/Table2/Components/TableFooter.tsx +0 -107
  108. package/idea/Table2/Components/TableOptions.tsx +0 -211
  109. package/idea/Table2/Components/Templates/TemplateCreate.tsx +0 -75
  110. package/idea/Table2/Components/Templates/TemplateCreateDefault.tsx +0 -45
  111. package/idea/Table2/Components/Templates/TemplateList.tsx +0 -167
  112. package/idea/Table2/Components/Templates/repo/TemplateRepo.ts +0 -51
  113. package/idea/Table2/_table.scss +0 -300
  114. package/idea/Table2/hooks/useDefaultTemplate.ts +0 -22
  115. package/idea/Table2/hooks/useTableKeyboard.ts +0 -115
  116. package/src/assets/icons/light/faPenField.ts +0 -15
  117. package/src/assets/icons/solid/faMagnifyingGlass.ts +0 -15
  118. package/src/components/Dialog/DeleteItemDialog.tsx +0 -52
  119. package/src/components/Dialog/hooks/useDialogObserver.ts +0 -21
  120. /package/{src/components → idea}/Table/Components/Columns/ColumnsList.tsx +0 -0
  121. /package/{src/components → idea}/Table/Components/Columns/SetColumnsList.tsx +0 -0
  122. /package/{src/components → idea}/Table/Components/Edit/ItemActionsMenu.tsx +0 -0
  123. /package/{src/components → idea}/Table/Components/Edit/ItemEditOptionsButtons.tsx +0 -0
  124. /package/{src/components → idea}/Table/Components/Edit/TableEditRow.tsx +0 -0
  125. /package/{src/components → idea}/Table/Components/FilterItem.tsx +0 -0
  126. /package/{src/components → idea}/Table/Components/Header/TableHeader.tsx +0 -0
  127. /package/{src/components → idea}/Table/Components/Header/TableHeaderRow.tsx +0 -0
  128. /package/{src/components → idea}/Table/Components/SetSortList.tsx +0 -0
  129. /package/{src/components → idea}/Table/Components/TableFooter.tsx +0 -0
  130. /package/{src/components → idea}/Table/Components/Templates/TemplateCreate.tsx +0 -0
  131. /package/{src/components → idea}/Table/Components/Templates/TemplateCreateDefault.tsx +0 -0
  132. /package/{src/components → idea}/Table/Components/Templates/TemplateList.tsx +0 -0
  133. /package/{src/components → idea}/Table/Components/Templates/repo/TemplateRepo.ts +0 -0
  134. /package/src/utils/{DateUtils.ts → dateUtils.ts} +0 -0
  135. /package/src/utils/{LocalStorageHelper.ts → localStorageHelper.ts} +0 -0
  136. /package/src/utils/{NumberUtils.ts → numberUtils.ts} +0 -0
  137. /package/src/utils/{RootDir.ts → rootDir.ts} +0 -0
  138. /package/src/utils/{StringUtils.ts → stringUtils.ts} +0 -0
@@ -0,0 +1,125 @@
1
+ import { useMemo, useState, type FC } from 'react';
2
+ import { useTableContext } from '../../contexts/TableContext';
3
+ import { useTranslation } from 'react-i18next';
4
+ import { IconButton } from '../../../Button/IconButton';
5
+ import { NotificationBadge } from '../../../Badge/NotificationBadge';
6
+ import {
7
+ getActiveFilterNumber,
8
+ getVisibleColumnsIds,
9
+ } from '../../../../utils/tableUtils';
10
+ import { faFilter } from '../../../../assets/icons/light/faFilter';
11
+ import { ITableFilterItem } from '../../../../types/ITable';
12
+ import { Pullover } from '../../../Pullover/Pullover';
13
+ import { FilterItem } from './FilterItem';
14
+ import {
15
+ deleteEmptyPropsIncludingArray,
16
+ deleteProps,
17
+ } from '../../../../utils/objectUtils';
18
+ import { faFilterCircleXmark } from '../../../../assets/icons/regular/faFilterCircleXmark';
19
+ import { usePopupControl } from '../../../../hooks/usePopupControl';
20
+
21
+ export const TableFilters: FC = () => {
22
+ const { controlRef, isOpen, onClose, onOpen } = usePopupControl();
23
+
24
+ const [filterSearch, setFilterSearch] = useState('');
25
+
26
+ const { t } = useTranslation();
27
+ const { columns, filterData } = useTableContext();
28
+
29
+ const columnIds = getVisibleColumnsIds(columns);
30
+
31
+ const {
32
+ filters,
33
+ additionalFilters,
34
+ excludeFromSearch,
35
+ search = {},
36
+ searchData = {},
37
+ setSearchData,
38
+ defaultSearch = {},
39
+ onSearch,
40
+ activeFilterNo,
41
+ customFilterIdList,
42
+ } = filterData ?? {};
43
+
44
+ const visibleFilters = useMemo(
45
+ () =>
46
+ filters
47
+ ? (customFilterIdList ?? columnIds).reduce(
48
+ (arr: ITableFilterItem[], col) =>
49
+ !!filters[col] ? arr.concat(filters[col]) : arr,
50
+ []
51
+ )
52
+ : [],
53
+ [columnIds, customFilterIdList, filters]
54
+ );
55
+
56
+ const filteredFilters = useMemo(
57
+ () =>
58
+ visibleFilters.filter(e =>
59
+ e?.label.toLowerCase().includes(filterSearch.toLowerCase())
60
+ ),
61
+ [visibleFilters, filterSearch]
62
+ );
63
+
64
+ return filterData ? (
65
+ <>
66
+ <NotificationBadge number={activeFilterNo ?? getActiveFilterNumber(search)}>
67
+ <IconButton
68
+ tooltip={t('Filter')}
69
+ icon={faFilter}
70
+ active={isOpen}
71
+ onClick={onOpen}
72
+ size="s"
73
+ variant="outlined"
74
+ />
75
+ </NotificationBadge>
76
+
77
+ <Pullover
78
+ controlRef={controlRef}
79
+ header={{
80
+ title: t('Filter'),
81
+ onSearch: visibleFilters.length > 2 ? setFilterSearch : undefined,
82
+ }}
83
+ onFormSubmit={() => {
84
+ const data = deleteEmptyPropsIncludingArray(searchData);
85
+ onSearch?.(excludeFromSearch ? deleteProps(data, excludeFromSearch) : data);
86
+ onClose();
87
+ }}
88
+ footer={{
89
+ confirmButton: {
90
+ label: t('ApplyFilters'),
91
+ type: 'submit',
92
+ },
93
+ additionalButton: {
94
+ label: t('ResetFilter'),
95
+ icon: faFilterCircleXmark,
96
+ onClick: () => {
97
+ setSearchData?.(defaultSearch);
98
+ onSearch?.(defaultSearch);
99
+ onClose();
100
+ },
101
+ disabled: defaultSearch
102
+ ? JSON.stringify(searchData) === JSON.stringify(defaultSearch)
103
+ : Object.keys(searchData).length === 0,
104
+ },
105
+ // onClose,
106
+ }}
107
+ contentClassName="iui-filters-list"
108
+ onCloseCallback={() => setFilterSearch('')}
109
+ size="w600"
110
+ >
111
+ {filteredFilters?.map(item => (
112
+ <FilterItem key={item.label} item={item} />
113
+ ))}
114
+ {additionalFilters && <div className="pb-1">{additionalFilters}</div>}
115
+
116
+ {/* {filteredFilters?.map(item => (
117
+ <FilterItem key={item.label + 1} item={item} />
118
+ ))}
119
+ {filteredFilters?.map(item => (
120
+ <FilterItem key={item.label + 2} item={item} />
121
+ ))} */}
122
+ </Pullover>
123
+ </>
124
+ ) : null;
125
+ };
@@ -0,0 +1,128 @@
1
+ import { IconDefinition } from '@fortawesome/fontawesome-svg-core';
2
+ import { FC, MouseEventHandler, useEffect, useState } from 'react';
3
+ import { faAngleLeft } from '../../../../assets/icons/solid/faAngleLeft';
4
+ import { faAngleRight } from '../../../../assets/icons/solid/faAngleRight';
5
+ import { faRotateRight } from '../../../../assets/icons/solid/faRotateRight';
6
+ import { IconButton } from '../../../Button/IconButton';
7
+ import { useTranslation } from 'react-i18next';
8
+ import { IPaginationControl, tableStandardLimit } from '../../../../types/ITable';
9
+ import { Select } from '../../../Inputs/Selects/Select';
10
+ import { ISelectData } from '../../../../types/ISelect';
11
+ import { TextInput } from '../../../Inputs/TextInput';
12
+ import { formatCurrencyNoDecimals } from '../../../../utils/numberUtils';
13
+
14
+ interface Props {
15
+ pagination: IPaginationControl;
16
+ noTotalRows?: boolean;
17
+ dataLength: number;
18
+ footerAction?: {
19
+ icon?: IconDefinition;
20
+ onClick: MouseEventHandler<HTMLButtonElement>;
21
+ };
22
+ totalRows?: number;
23
+ customLimit?: number[];
24
+ }
25
+
26
+ export const TableFooter: FC<Props> = ({
27
+ pagination: { limit, offset, setLimit, setOffset },
28
+ totalRows = 0,
29
+ noTotalRows,
30
+ dataLength,
31
+ footerAction,
32
+ customLimit,
33
+ }) => {
34
+ const [tempOffset, setTempOffset] = useState(offset + 1);
35
+
36
+ const { t } = useTranslation();
37
+
38
+ useEffect(() => {
39
+ setTempOffset(offset + 1);
40
+ }, [offset]);
41
+
42
+ const handleChangeLimit = (value: ISelectData) => {
43
+ const newLimit = Math.floor(+value.value);
44
+ setLimit(newLimit);
45
+ setOffset(0);
46
+ };
47
+
48
+ const maxOffset = Math.ceil(totalRows / limit);
49
+
50
+ return (
51
+ <div className={`table-footer ${footerAction ? 'justify-between' : 'justify-right'}`}>
52
+ {!!footerAction && (
53
+ <IconButton
54
+ icon={footerAction.icon || faRotateRight}
55
+ onClick={footerAction.onClick}
56
+ tooltip={t('RefreshData')}
57
+ variant="outlined"
58
+ size="s"
59
+ />
60
+ )}
61
+ <div className="pagination">
62
+ <div className="flex align-center gap-2">
63
+ <p>{t('rowsPerPage')}</p>
64
+ <Select
65
+ value={{ label: `${limit}`, value: limit }}
66
+ size="s"
67
+ minWidth={20}
68
+ onChange={handleChangeLimit}
69
+ options={(customLimit || tableStandardLimit).map(e => ({
70
+ label: `${e}`,
71
+ value: e ?? t('allResults'),
72
+ }))}
73
+ isClearable={false}
74
+ />
75
+ {!noTotalRows && (
76
+ <p>
77
+ {limit
78
+ ? `${offset * limit + 1}-${
79
+ offset * limit + limit <= totalRows
80
+ ? offset * limit + limit
81
+ : totalRows
82
+ } ${t('of')} ${formatCurrencyNoDecimals(totalRows)}`
83
+ : `${t('allResults')} ${t('of')} ${formatCurrencyNoDecimals(totalRows)} `}
84
+ </p>
85
+ )}
86
+ </div>
87
+ {!noTotalRows && <p>|</p>}
88
+ <div className="flex gap-1 align-center">
89
+ <p>{t('page')}:</p>
90
+ <IconButton
91
+ disabled={offset < 1}
92
+ icon={faAngleLeft}
93
+ onClick={() => setOffset(offset - 1)}
94
+ className="p-1"
95
+ size="s"
96
+ />
97
+ <TextInput
98
+ className="offset-input"
99
+ value={`${tempOffset}`}
100
+ setValue={e => !isNaN(+e) && setTempOffset(+e)}
101
+ isClearable={false}
102
+ inputProps={{
103
+ onFocus: e => e.target.select(),
104
+ onBlur: () => setTempOffset(offset + 1),
105
+ onKeyUp: e => {
106
+ if (e.key === 'Enter') {
107
+ const value = tempOffset;
108
+ if (typeof value === 'number' && value >= 0 && value <= maxOffset) {
109
+ setOffset(value - 1);
110
+ } else {
111
+ setTempOffset(offset + 1);
112
+ }
113
+ }
114
+ },
115
+ }}
116
+ />
117
+ <IconButton
118
+ disabled={noTotalRows ? limit > dataLength : offset + 1 >= maxOffset}
119
+ icon={faAngleRight}
120
+ onClick={() => setOffset(offset + 1)}
121
+ size="s"
122
+ />
123
+ {!noTotalRows && <p>{`${t('of')} ${formatCurrencyNoDecimals(maxOffset)}`}</p>}
124
+ </div>
125
+ </div>
126
+ </div>
127
+ );
128
+ };
@@ -0,0 +1,42 @@
1
+ import { FC, Fragment } from 'react';
2
+ import clsx from 'clsx';
3
+ import { TableHeaderRow } from './TableHeaderRow';
4
+ import { useTableContext } from '../../contexts/TableContext';
5
+
6
+ export const HeaderTable: FC = () => {
7
+ const { columns, customHeader, headerWrap, maxHeight } = useTableContext();
8
+ const isSticky = !!maxHeight;
9
+
10
+ return (
11
+ <thead
12
+ className={clsx({
13
+ 'no-wrap': !headerWrap,
14
+ 'sticky-header': isSticky,
15
+ })}
16
+ >
17
+ <BorderRow span={columns.length} />
18
+ {customHeader ? (
19
+ customHeader?.map((row, i) => (
20
+ // No array manipulation will be performed, so using key 'i' is acceptable.
21
+ <Fragment key={i}>
22
+ <TableHeaderRow row={row} />
23
+ {(isSticky || i + 1 < customHeader.length) && (
24
+ <BorderRow span={columns.length} />
25
+ )}
26
+ </Fragment>
27
+ ))
28
+ ) : (
29
+ <>
30
+ <TableHeaderRow row={columns} />
31
+ <BorderRow span={columns.length} />
32
+ </>
33
+ )}
34
+ </thead>
35
+ );
36
+ };
37
+
38
+ const BorderRow: FC<{ span?: number }> = ({ span }) => (
39
+ <tr>
40
+ <th className="border-row" colSpan={span} />
41
+ </tr>
42
+ );
@@ -0,0 +1,47 @@
1
+ import { FC } from 'react';
2
+ import { ITableColumn } from '../../../../types/ITable';
3
+ import clsx from 'clsx';
4
+
5
+ interface Props {
6
+ row: ITableColumn[];
7
+ }
8
+
9
+ export const TableHeaderRow: FC<Props> = ({ row }) => {
10
+ // const { sortData } = useTableContext();
11
+ // const { sort, setSort, sortOptions } = sortData ?? {};
12
+ // const activeSortOption = sortOptions?.find(e => sort?.startsWith(e.value));
13
+
14
+ return (
15
+ <tr>
16
+ {row.map(col => (
17
+ <th
18
+ key={col.id}
19
+ colSpan={col.colSpan}
20
+ rowSpan={col.rowSpan}
21
+ className={clsx(col.color, col.className)}
22
+ // onClick={() => {
23
+ // const sortValue = sortOptions?.find(e => col.label === e.label)?.value;
24
+ // sortValue &&
25
+ // setSort?.(sort === sortValue + desc ? sortValue + asc : sortValue + desc);
26
+ // }}
27
+ style={
28
+ col.minWidth
29
+ ? { minWidth: col.minWidth }
30
+ : col.width
31
+ ? { width: col.width }
32
+ : undefined
33
+ }
34
+ hidden={col.hidden}
35
+ >
36
+ <div className="flex align-center">
37
+ {typeof col.label === 'string' ? (
38
+ <p className={`full-width text-${col.align || 'left'}`}>{col.label}</p>
39
+ ) : (
40
+ <div className={`full-width text-${col.align || 'left'}`}>{col.label}</div>
41
+ )}
42
+ </div>
43
+ </th>
44
+ ))}
45
+ </tr>
46
+ );
47
+ };
@@ -0,0 +1,66 @@
1
+ import { type FC } from 'react';
2
+ import { useTableContext } from '../../contexts/TableContext';
3
+ import { IconButton } from '../../../Button/IconButton';
4
+ import { faEllipsisVertical, faPen, faTrashCan } from '../../../../assets/icons';
5
+ import { useIsMenuOpen } from '../../../../hooks/useIsMenuOpen';
6
+ import { Menu } from '../../../Menu/Menu';
7
+ import { ITableDataItem } from '../../../../types/ITable';
8
+
9
+ export const TableItemActions: FC<{ tableDataItem: ITableDataItem }> = ({
10
+ tableDataItem,
11
+ }) => {
12
+ const { isMenuOpen, onMenuClose, onMenuOpen } = useIsMenuOpen();
13
+
14
+ const { dataActions, editable } = useTableContext();
15
+
16
+ if (!dataActions?.hasItemActions) {
17
+ return null;
18
+ }
19
+
20
+ return (
21
+ <div className="table-item-actions">
22
+ {dataActions.isEditable && (
23
+ <IconButton
24
+ icon={faPen}
25
+ onClick={() => editable?.setSelectedItem(tableDataItem.item ?? null)}
26
+ size="s"
27
+ />
28
+ )}
29
+ {dataActions.isDeletable && (
30
+ <IconButton
31
+ icon={faTrashCan}
32
+ onClick={() => dataActions.delete?.onClick?.(tableDataItem.uuid)}
33
+ size="s"
34
+ color="danger"
35
+ />
36
+ )}
37
+
38
+ {!!dataActions.actions?.length && (
39
+ <Menu
40
+ isOpen={isMenuOpen}
41
+ onClose={onMenuClose}
42
+ placement="bottom-right"
43
+ renderButton={ref => (
44
+ <IconButton
45
+ ref={ref}
46
+ icon={faEllipsisVertical}
47
+ active={isMenuOpen}
48
+ onClick={onMenuOpen}
49
+ size="s"
50
+ />
51
+ )}
52
+ items={
53
+ dataActions.actions?.map(action => ({
54
+ label: action.label,
55
+ hidden: !action.hasAccess,
56
+ onClick: () => {
57
+ action.onClick(tableDataItem);
58
+ onMenuClose();
59
+ },
60
+ })) ?? []
61
+ }
62
+ />
63
+ )}
64
+ </div>
65
+ );
66
+ };
@@ -0,0 +1,49 @@
1
+ import { useState, type FC } from 'react';
2
+ import { useTableContext } from '../../contexts/TableContext';
3
+ import { Menu } from '../../../Menu/Menu';
4
+ import { useTranslation } from 'react-i18next';
5
+ import { IconButton } from '../../../Button/IconButton';
6
+ import { faClipboardCheck } from '../../../../assets/icons/light/faClipboardCheck';
7
+ import { NotificationBadge } from '../../../Badge/NotificationBadge';
8
+ import { Tooltip } from '../../../Tooltip/Tooltip';
9
+
10
+ export const TableSelect: FC = () => {
11
+ const [isOpen, setIsOpen] = useState(false);
12
+
13
+ const { t } = useTranslation();
14
+ const { rowSelect, selectActions } = useTableContext();
15
+
16
+ return !!rowSelect && selectActions.length > 0 ? (
17
+ <Menu
18
+ isOpen={isOpen}
19
+ onClose={() => setIsOpen(false)}
20
+ placement="bottom-left"
21
+ renderButton={ref => (
22
+ <NotificationBadge number={rowSelect.selectedRows.size}>
23
+ <Tooltip
24
+ ref={ref}
25
+ label={t('TableSelect')}
26
+ disabled={!rowSelect.selectedRows.size}
27
+ >
28
+ <IconButton
29
+ icon={faClipboardCheck}
30
+ active={isOpen}
31
+ onClick={() => setIsOpen(!isOpen)}
32
+ disabled={!rowSelect.selectedRows.size}
33
+ variant="outlined"
34
+ size="s"
35
+ />
36
+ </Tooltip>
37
+ </NotificationBadge>
38
+ )}
39
+ items={selectActions.map(action => ({
40
+ ...action,
41
+ onClick: () => {
42
+ action.onClick!(rowSelect.selectedRows);
43
+ setIsOpen(false);
44
+ action.clearSelected && rowSelect.setSelectedRows(new Set<string>());
45
+ },
46
+ }))}
47
+ />
48
+ ) : null;
49
+ };
@@ -0,0 +1,52 @@
1
+ import { useState, type FC } from 'react';
2
+ import { Menu } from '../../../Menu/Menu';
3
+ import { Tooltip } from '../../../Tooltip/Tooltip';
4
+ import { IconButton } from '../../../Button/IconButton';
5
+ import { faArrowUpArrowDown } from '../../../../assets/icons/light/faArrowUpArrowDown';
6
+ import { useTableContext } from '../../contexts/TableContext';
7
+ import { useTranslation } from 'react-i18next';
8
+ import { faArrowDownWideShort } from '../../../../assets/icons/light/faArrowDownWideShort';
9
+ import { faArrowDownShortWide } from '../../../../assets/icons/light/faArrowDownShortWide';
10
+
11
+ export const TableSort: FC = () => {
12
+ const [isOpen, setIsOpen] = useState(false);
13
+
14
+ const { t } = useTranslation();
15
+ const { sortData } = useTableContext();
16
+ const { sort, setSort, sortOptions } = sortData ?? {};
17
+
18
+ return sortData ? (
19
+ <Menu
20
+ isOpen={isOpen}
21
+ onClose={() => setIsOpen(false)}
22
+ renderButton={ref => (
23
+ <Tooltip label={t('Sort')} ref={ref}>
24
+ <IconButton
25
+ icon={faArrowUpArrowDown}
26
+ active={isOpen}
27
+ onClick={() => setIsOpen(!isOpen)}
28
+ size="s"
29
+ variant="outlined"
30
+ />
31
+ </Tooltip>
32
+ )}
33
+ size={sortData.menuSize}
34
+ items={sortOptions?.map(e => {
35
+ const valueAsc = e.value + '_ASC';
36
+ const valueDesc = e.value + '_DESC';
37
+
38
+ return {
39
+ label: e.label,
40
+ onClick: () => setSort?.(sort === valueDesc ? valueAsc : valueDesc),
41
+ active: sort?.startsWith(e.value),
42
+ icon:
43
+ sort === valueDesc
44
+ ? faArrowDownWideShort
45
+ : sort === valueAsc
46
+ ? faArrowDownShortWide
47
+ : undefined,
48
+ };
49
+ })}
50
+ />
51
+ ) : null;
52
+ };
@@ -0,0 +1,123 @@
1
+ import {
2
+ Dispatch,
3
+ ReactNode,
4
+ SetStateAction,
5
+ createContext,
6
+ useContext,
7
+ useMemo,
8
+ useState,
9
+ } from 'react';
10
+ import {
11
+ IPaginationControl,
12
+ ITable,
13
+ ITableColumn,
14
+ ITableEdit,
15
+ ITableSelectedActions,
16
+ } from '../../../types/ITable';
17
+ import { useLocalTablePagination } from '../hooks/localHooks/useLocalTablePagination';
18
+ import { useLocalTableColumns } from '../hooks/localHooks/useLocalTableColumns';
19
+ import { useLocalTableData } from '../hooks/localHooks/useLocalTableData';
20
+
21
+ interface ITableContext extends ITable {
22
+ setColumns: (columns: ITableColumn[]) => void;
23
+ selectActions: ITableSelectedActions[];
24
+ dataActions?: ITable['dataActions'] & {
25
+ hasItemActions: boolean;
26
+ isEditable: boolean;
27
+ isDeletable: boolean;
28
+ };
29
+ editable?: ITableEdit & {
30
+ isAdding: boolean;
31
+ setIsAdding: Dispatch<SetStateAction<boolean>>;
32
+ };
33
+ footer?: ITable['footer'] & {
34
+ paginationControl: IPaginationControl;
35
+ noTotalRows: boolean;
36
+ };
37
+ }
38
+
39
+ const TableContext = createContext<ITableContext>({
40
+ setColumns: () => {},
41
+ selectActions: [],
42
+ data: [],
43
+ columns: [],
44
+ });
45
+
46
+ interface Props extends ITable {
47
+ children: ReactNode;
48
+ }
49
+
50
+ export const TableProvider = ({
51
+ columns: initialColumns,
52
+ data: initialData,
53
+ dataActions,
54
+ editable,
55
+ footer,
56
+ children,
57
+ ...rest
58
+ }: Props) => {
59
+ const [isAdding, setIsAdding] = useState(false);
60
+
61
+ const { rowSelect } = rest;
62
+
63
+ const selectActions = useMemo(
64
+ () => rowSelect?.actions.filter(e => !e.hidden) || [],
65
+ [rowSelect?.actions]
66
+ );
67
+ const updatedDataActions = useMemo(() => {
68
+ const isEditable = Boolean(dataActions?.edit?.hasAccess);
69
+ const isDeletable = Boolean(dataActions?.delete?.hasAccess);
70
+ const filteredDataActions = dataActions?.actions?.filter(e => e.hasAccess) ?? [];
71
+ return {
72
+ ...dataActions,
73
+ actions: filteredDataActions,
74
+ hasItemActions: isEditable || isDeletable || filteredDataActions.length > 0,
75
+ isEditable,
76
+ isDeletable,
77
+ };
78
+ }, [dataActions]);
79
+
80
+ const localPagination = useLocalTablePagination({
81
+ defaultLimit: footer?.customPagination?.defaultLimit,
82
+ });
83
+
84
+ const { data } = useLocalTableData({
85
+ initialData,
86
+ localPagination,
87
+ controledPagination: !!footer?.paginationControl,
88
+ dataActions,
89
+ rowSelect,
90
+ });
91
+
92
+ const { columns, setColumns } = useLocalTableColumns({
93
+ initialColumns,
94
+ hasSelect: selectActions.length > 0,
95
+ data,
96
+ rowSelect,
97
+ hasItemActions: updatedDataActions.hasItemActions,
98
+ });
99
+
100
+ const tableContextValue: ITableContext = {
101
+ ...rest,
102
+ data,
103
+ dataActions: updatedDataActions,
104
+ columns,
105
+ setColumns,
106
+ selectActions,
107
+ };
108
+
109
+ if (editable) tableContextValue.editable = { ...editable, isAdding, setIsAdding };
110
+ if (footer)
111
+ tableContextValue.footer = {
112
+ ...footer,
113
+ paginationControl: footer?.paginationControl ?? localPagination,
114
+ totalRows: footer?.totalRows ?? initialData.length,
115
+ noTotalRows: footer?.totalRows === undefined,
116
+ };
117
+
118
+ return (
119
+ <TableContext.Provider value={tableContextValue}>{children}</TableContext.Provider>
120
+ );
121
+ };
122
+
123
+ export const useTableContext = () => useContext(TableContext);