@inceptionbg/iui 2.0.15 → 2.0.17

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 (76) hide show
  1. package/dist/NoAccessPage-BmizYfw0.js +2 -0
  2. package/dist/{NoAccessPage-DBq5IzIf.js.map → NoAccessPage-BmizYfw0.js.map} +1 -1
  3. package/dist/NotFoundPage-Cv544vAr.js +2 -0
  4. package/dist/{NotFoundPage-DM-I96ar.js.map → NotFoundPage-Cv544vAr.js.map} +1 -1
  5. package/dist/index.d.ts +160 -91
  6. package/dist/index.js +1 -1
  7. package/dist/index.js.map +1 -1
  8. package/dist/iui.css +1 -1
  9. package/package.json +4 -7
  10. package/src/assets/icons/duotone/faBell.ts +17 -0
  11. package/src/assets/icons/duotone/faPen.ts +18 -0
  12. package/src/assets/icons/duotone/faTrashCan.ts +18 -0
  13. package/src/assets/icons/light/faBell.ts +15 -0
  14. package/src/assets/icons/light/faEnvelope.ts +15 -0
  15. package/src/assets/icons/regular/faEllipsisVertical.ts +15 -0
  16. package/src/assets/icons/solid/faEnvelopeDot.ts +15 -0
  17. package/src/components/Button/SplitButton.tsx +5 -5
  18. package/src/components/Dialog/Dialog.tsx +3 -3
  19. package/src/components/Header/Components/ModuleSelect.tsx +5 -5
  20. package/src/components/Header/Components/Notifications.tsx +208 -0
  21. package/src/components/Header/Components/UserMenu.tsx +15 -14
  22. package/src/components/Header/Header.tsx +5 -4
  23. package/src/components/Inputs/NumberInput.tsx +3 -0
  24. package/src/components/Inputs/Selects/components/SelectWrapper.tsx +48 -29
  25. package/src/components/Inputs/TextInput.tsx +1 -0
  26. package/src/components/List/ListItem.tsx +1 -1
  27. package/src/components/Loader/ProgressBar.tsx +1 -1
  28. package/src/components/Menu/Menu.tsx +3 -0
  29. package/src/components/Pullover/Pullover.tsx +38 -39
  30. package/src/components/Table/Table.tsx +23 -32
  31. package/{idea/Table/Components/Columns → src/components/Table/components/columns}/ColumnsList.tsx +12 -14
  32. package/src/components/Table/components/columns/TableColumnsEdit.tsx +112 -0
  33. package/src/components/Table/components/edit/TableEditRow.tsx +26 -21
  34. package/src/components/Table/components/header/TableHeaderRow.tsx +2 -1
  35. package/src/components/Table/components/items/TableItemActions.tsx +18 -13
  36. package/src/components/Table/components/print/TablePrint.tsx +1 -5
  37. package/src/components/Table/components/templates/CreateTemplateDialog.tsx +48 -0
  38. package/src/components/Table/components/templates/TableTemplates.tsx +24 -4
  39. package/src/components/Table/contexts/TableContext.tsx +34 -33
  40. package/src/components/Table/hooks/localHooks/useLocalTableColumns.tsx +27 -28
  41. package/src/components/Table/hooks/localHooks/useLocalTableData.tsx +17 -11
  42. package/src/components/Table/hooks/localHooks/useLocalTableKeyboard.ts +8 -6
  43. package/src/components/Table/hooks/useTableColumns.ts +16 -10
  44. package/src/components/Table/hooks/useTableEdit.tsx +24 -2
  45. package/src/components/Table/hooks/useTablePrint.ts +12 -4
  46. package/src/components/Table/hooks/useTableSelect.ts +1 -1
  47. package/src/components/Tabs/Tabs.tsx +1 -0
  48. package/src/components/Tooltip/Tooltip.tsx +81 -14
  49. package/src/hooks/useIsMenuOpen.ts +3 -3
  50. package/src/hooks/usePopupControl.ts +9 -4
  51. package/src/index.ts +24 -5
  52. package/src/styles/App.scss +1 -0
  53. package/src/styles/components/_badge.scss +8 -1
  54. package/src/styles/components/_header.scss +18 -8
  55. package/src/styles/components/_list.scss +1 -1
  56. package/src/styles/components/_notifications.scss +71 -0
  57. package/src/styles/components/_page.scss +1 -0
  58. package/src/styles/components/_pullover.scss +1 -1
  59. package/src/styles/components/_sidebar.scss +1 -3
  60. package/src/styles/components/_table.scss +110 -57
  61. package/src/styles/variables/_variables.scss +9 -0
  62. package/src/types/IHeader.ts +1 -1
  63. package/src/types/IKeyboard.ts +0 -5
  64. package/src/types/IMenu.ts +2 -2
  65. package/src/types/INotifications.ts +15 -0
  66. package/src/types/IPopup.ts +2 -2
  67. package/src/types/ITable.ts +36 -32
  68. package/src/utils/i18n/i18nIUICyrilic.ts +12 -0
  69. package/src/utils/i18n/i18nIUILatin.ts +13 -0
  70. package/src/utils/i18n/i18nIUIMe.ts +12 -0
  71. package/src/utils/objectUtils.ts +19 -0
  72. package/src/utils/tableUtils.ts +1 -1
  73. package/dist/NoAccessPage-DBq5IzIf.js +0 -2
  74. package/dist/NotFoundPage-DM-I96ar.js +0 -2
  75. package/idea/Notifications.tsx +0 -245
  76. package/idea/Table/Components/Columns/SetColumnsList.tsx +0 -113
@@ -11,22 +11,24 @@ import {
11
11
  IPaginationControl,
12
12
  ITable,
13
13
  ITableColumnsData,
14
+ ITableDataActions,
14
15
  ITableEdit,
15
16
  ITableSelectedAction,
16
17
  } from '../../../types/ITable';
17
18
  import { useLocalTablePagination } from '../hooks/localHooks/useLocalTablePagination';
18
- import { useLocalTableColumns } from '../hooks/localHooks/useLocalTableColumns';
19
19
  import { useLocalTableData } from '../hooks/localHooks/useLocalTableData';
20
+ import { useTranslation } from 'react-i18next';
20
21
 
21
- interface ITableContext extends ITable {
22
+ interface ITableContext<T = unknown> extends ITable<T> {
22
23
  columnData: ITableColumnsData;
23
24
  selectActions: ITableSelectedAction[];
24
- dataActions?: ITable['dataActions'] & {
25
+ dataActions?: ITableDataActions<T> & {
25
26
  hasItemActions: boolean;
26
27
  isEditable: boolean;
27
28
  isDeletable: boolean;
29
+ filteredActions: ITableSelectedAction[];
28
30
  };
29
- editable?: ITableEdit & {
31
+ editable?: ITableEdit<T> & {
30
32
  isAdding: boolean;
31
33
  setIsAdding: Dispatch<SetStateAction<boolean>>;
32
34
  };
@@ -36,17 +38,13 @@ interface ITableContext extends ITable {
36
38
  };
37
39
  }
38
40
 
39
- const TableContext = createContext<ITableContext>({
41
+ const TableContext = createContext<ITableContext<any>>({
40
42
  columnData: { columns: [], defaultColumns: [] },
41
43
  data: [],
42
44
  selectActions: [],
43
45
  });
44
46
 
45
- interface Props extends ITable {
46
- children: ReactNode;
47
- }
48
-
49
- export const TableProvider = ({
47
+ export const TableProvider = <T,>({
50
48
  columnData,
51
49
  data: initialData,
52
50
  dataActions,
@@ -55,51 +53,54 @@ export const TableProvider = ({
55
53
  children,
56
54
  rowSelect,
57
55
  ...rest
58
- }: Props) => {
56
+ }: ITable<T> & { children: ReactNode }) => {
57
+ const { t } = useTranslation();
59
58
  const [isAdding, setIsAdding] = useState(false);
60
59
 
61
- const selectActions = useMemo(
62
- () => rowSelect?.actions.filter(e => !e.hidden) || [],
63
- [rowSelect?.actions]
64
- );
60
+ const selectActions = useMemo(() => {
61
+ const actions = (rowSelect?.actions ?? []).filter(e => !e.hidden);
62
+ if (rest.itemDeleteData?.hasAccess) {
63
+ actions.push({
64
+ label: t('Delete'),
65
+ onClick: e => rest.itemDeleteData!.setItemToDeleteUuids([...e]),
66
+ });
67
+ }
68
+ return actions;
69
+ }, [rowSelect?.actions, rest.itemDeleteData, t]);
70
+
65
71
  const updatedDataActions = useMemo(() => {
66
- const isEditable = Boolean(dataActions?.edit?.hasAccess);
67
- const isDeletable = Boolean(dataActions?.delete?.hasAccess);
68
- const filteredDataActions = dataActions?.actions?.filter(e => e.hasAccess) ?? [];
72
+ const isEditable = Boolean(dataActions?.hasEditAccess);
73
+ const isDeletable = Boolean(rest.itemDeleteData?.hasAccess);
74
+ const filteredActions = dataActions?.actions?.().filter(e => e.hasAccess) ?? [];
69
75
  return {
70
- ...dataActions,
71
- actions: filteredDataActions,
72
- hasItemActions: isEditable || isDeletable || filteredDataActions.length > 0,
76
+ hasEditAccess: isEditable,
77
+ actions: dataActions?.actions,
78
+ filteredActions,
79
+ hasItemActions: isEditable || isDeletable || filteredActions.length > 0,
73
80
  isEditable,
74
81
  isDeletable,
75
82
  };
76
- }, [dataActions]);
83
+ }, [dataActions, rest.itemDeleteData?.hasAccess]);
77
84
 
78
85
  const localPagination = useLocalTablePagination({
79
86
  defaultLimit: footer?.customPagination?.defaultLimit,
80
87
  });
81
- const data = useLocalTableData({
88
+ const data = useLocalTableData<T>({
82
89
  initialData,
83
90
  localPagination,
84
91
  controledPagination: !!footer?.paginationControl,
85
92
  dataActions: updatedDataActions,
93
+ isDeletable: updatedDataActions.isDeletable,
86
94
  rowSelect,
87
95
  });
88
96
 
89
- useLocalTableColumns({
90
- columnData,
91
- hasSelect: selectActions.length > 0,
92
- data,
93
- rowSelect,
94
- hasItemActions: updatedDataActions.hasItemActions,
95
- });
96
-
97
- const tableContextValue: ITableContext = {
97
+ const tableContextValue: ITableContext<T> = {
98
98
  ...rest,
99
99
  data,
100
100
  dataActions: updatedDataActions,
101
101
  columnData,
102
102
  selectActions,
103
+ rowSelect,
103
104
  };
104
105
 
105
106
  if (editable) tableContextValue.editable = { ...editable, isAdding, setIsAdding };
@@ -107,7 +108,7 @@ export const TableProvider = ({
107
108
  tableContextValue.footer = {
108
109
  ...footer,
109
110
  paginationControl: footer?.paginationControl ?? localPagination,
110
- totalRows: footer?.totalRows ?? initialData.length,
111
+ totalRows: footer?.totalRows ?? initialData?.length ?? 0,
111
112
  noTotalRows: footer?.totalRows === undefined,
112
113
  };
113
114
 
@@ -1,38 +1,33 @@
1
- import { useEffect } from 'react';
2
- import { ITable, ITableColumn, ITableColumnsData } from '../../../../types/ITable';
1
+ import { useMemo } from 'react';
2
+ import { ITableColumn } from '../../../../types/ITable';
3
3
  import { useTranslation } from 'react-i18next';
4
4
  import { Checkbox } from '../../../Inputs/Checkbox';
5
+ import { useTableContext } from '../../contexts/TableContext';
5
6
 
6
- interface Props {
7
- columnData: ITableColumnsData;
8
- hasSelect: boolean;
9
- data: ITable['data'];
10
- rowSelect: ITable['rowSelect'];
11
- hasItemActions?: boolean;
12
- }
13
-
14
- export const useLocalTableColumns = ({
15
- columnData: { defaultColumns, setColumns },
16
- hasSelect,
17
- data,
18
- rowSelect,
19
- hasItemActions,
20
- }: Props) => {
7
+ export const useLocalTableColumns = () => {
21
8
  const { t } = useTranslation();
22
9
 
23
- useEffect(() => {
24
- const activeColumns = defaultColumns.filter(e => !e.hidden && !e.unavailable);
25
- // const activeColumns = defaultColumns.filter(e => !e.unavailable);
10
+ const {
11
+ columnData: { columns },
12
+ selectActions,
13
+ data,
14
+ rowSelect,
15
+ dataActions: { hasItemActions } = {},
16
+ } = useTableContext();
17
+
18
+ const visibleColumns = useMemo(() => {
19
+ const hasSelect = rowSelect && selectActions.length > 0;
20
+ const filteredColumns = columns.filter(column => !column.hidden);
26
21
 
27
22
  let newCols: ITableColumn[] = [];
28
23
 
29
24
  // Select Column
30
- if (hasSelect && rowSelect) {
25
+ if (hasSelect) {
31
26
  const selectableData = data.filter(e => !e.disableSelect);
32
27
  const selectedSome =
33
- !!rowSelect.selectedRows.size &&
28
+ !!rowSelect?.selectedRows.size &&
34
29
  selectableData.some(e => !rowSelect.selectedRows.has(e.uuid));
35
- const selectedAll = !!rowSelect.selectedRows.size && !selectedSome;
30
+ const selectedAll = !!rowSelect?.selectedRows.size && !selectedSome;
36
31
 
37
32
  const selectColumn: ITableColumn = {
38
33
  id: 'select',
@@ -42,7 +37,7 @@ export const useLocalTableColumns = ({
42
37
  setValue={() => {
43
38
  selectedAll
44
39
  ? rowSelect.setSelectedRows(new Set<string>())
45
- : rowSelect.setSelectedRows(oldValue => {
40
+ : rowSelect?.setSelectedRows(oldValue => {
46
41
  let newSet = new Set(oldValue);
47
42
  selectableData.forEach(e => newSet.add(e.uuid!));
48
43
  return newSet;
@@ -57,15 +52,19 @@ export const useLocalTableColumns = ({
57
52
  newCols.push(selectColumn);
58
53
  }
59
54
 
60
- newCols = newCols.concat(activeColumns);
61
- hasItemActions &&
55
+ newCols = newCols.concat(filteredColumns);
56
+ // Actions Column
57
+ if (hasItemActions) {
62
58
  newCols.push({
63
59
  id: 'actions',
64
60
  label: t('Actions'),
65
61
  align: 'center',
66
62
  className: 'actions-col',
67
63
  });
64
+ }
65
+
66
+ return newCols;
67
+ }, [columns, selectActions.length, data, rowSelect, hasItemActions, t]);
68
68
 
69
- setColumns?.(newCols);
70
- }, [defaultColumns, setColumns, hasSelect, data, rowSelect, hasItemActions, t]);
69
+ return { visibleColumns };
71
70
  };
@@ -1,24 +1,31 @@
1
1
  import { useEffect, useState } from 'react';
2
- import { IPaginationControl, ITable, ITableDataItem } from '../../../../types/ITable';
2
+ import {
3
+ IPaginationControl,
4
+ ITable,
5
+ ITableDataActions,
6
+ ITableDataItem,
7
+ } from '../../../../types/ITable';
3
8
  import { Checkbox } from '../../../Inputs/Checkbox';
4
9
  import { TableItemActions } from '../../components/items/TableItemActions';
5
10
 
6
- interface Props {
7
- initialData: ITableDataItem[];
11
+ interface Props<T> {
12
+ initialData: ITableDataItem<T>[];
8
13
  controledPagination: boolean;
9
14
  localPagination: IPaginationControl;
10
- dataActions?: ITable['dataActions'];
15
+ dataActions?: ITableDataActions<T>;
16
+ isDeletable: boolean;
11
17
  rowSelect: ITable['rowSelect'];
12
18
  }
13
19
 
14
- export const useLocalTableData = ({
20
+ export const useLocalTableData = <T,>({
15
21
  initialData,
16
22
  controledPagination,
17
23
  localPagination,
18
24
  dataActions,
25
+ isDeletable,
19
26
  rowSelect,
20
- }: Props) => {
21
- const [data, setData] = useState<ITableDataItem[]>(initialData);
27
+ }: Props<T>) => {
28
+ const [data, setData] = useState<ITableDataItem<T>[]>(initialData);
22
29
 
23
30
  useEffect(() => {
24
31
  const newData =
@@ -29,11 +36,9 @@ export const useLocalTableData = ({
29
36
  localPagination.offset * localPagination.limit + localPagination.limit
30
37
  );
31
38
 
32
- const additionalDataActions = dataActions?.actions?.filter(e => e.hasAccess) ?? [];
39
+ const additionalDataActions = dataActions?.actions?.().filter(e => e.hasAccess) ?? [];
33
40
  const withActions = Boolean(
34
- dataActions?.edit?.hasAccess ||
35
- dataActions?.delete?.hasAccess ||
36
- additionalDataActions.length
41
+ dataActions?.hasEditAccess || isDeletable || additionalDataActions.length
37
42
  );
38
43
 
39
44
  const withAdditionalColumns = Boolean(rowSelect || withActions);
@@ -71,6 +76,7 @@ export const useLocalTableData = ({
71
76
  localPagination.limit,
72
77
  localPagination.offset,
73
78
  rowSelect,
79
+ isDeletable,
74
80
  dataActions,
75
81
  ]);
76
82
 
@@ -7,13 +7,14 @@ import { useTableContext } from '../../contexts/TableContext';
7
7
  export const useLocalTableKeyboard = () => {
8
8
  const [focusedRow, setFocusedRow] = useState<ITableDataItem | null>(null);
9
9
 
10
- const { keyboard, data, editable, dataActions, rowSelect, footer } = useTableContext();
10
+ const { keyboard, data, editable, itemDeleteData, dataActions, rowSelect, footer } =
11
+ useTableContext();
11
12
 
12
13
  useEffect(() => {
13
14
  if (keyboard?.enabled) {
14
15
  const isEditing = !!editable?.selectedItem;
15
16
  const isAdding = !!editable?.isAdding;
16
- const isEditDisabled = !editable || keyboard?.actions.editDisabled;
17
+ const isEditDisabled = !editable || keyboard?.actions?.editDisabled;
17
18
  const isAddDisabled = !!editable?.addDisabled;
18
19
  const isMoveDisabled = !!keyboard?.actions?.moveDisabled;
19
20
  const isBulkAction =
@@ -55,7 +56,7 @@ export const useLocalTableKeyboard = () => {
55
56
  ctrl: true,
56
57
  disabled: isEditing || isEditDisabled || !focusedRow,
57
58
  onAction: () => {
58
- editable?.setSelectedItem(focusedRow?.item ?? null);
59
+ editable?.setSelectedItem(focusedRow);
59
60
  },
60
61
  },
61
62
  {
@@ -83,10 +84,9 @@ export const useLocalTableKeyboard = () => {
83
84
  },
84
85
  {
85
86
  code: 'Delete',
86
- disabled: isEditing || !keyboard?.actions.delete?.enabled || !focusedRow,
87
+ disabled: !dataActions?.isDeletable || isEditing || !focusedRow,
87
88
  onAction: () =>
88
- keyboard?.actions.delete?.onAction ??
89
- keyboard?.actions.delete?.setItemToDeleteUuids(
89
+ itemDeleteData?.setItemToDeleteUuids(
90
90
  isBulkAction ? Array.from(rowSelect.selectedRows) : [focusedRow!.uuid]
91
91
  ),
92
92
  },
@@ -162,6 +162,8 @@ export const useLocalTableKeyboard = () => {
162
162
  editable,
163
163
  keyboard,
164
164
  dataActions?.hasItemActions,
165
+ dataActions?.isDeletable,
166
+ itemDeleteData,
165
167
  focusedRow,
166
168
  footer,
167
169
  rowSelect,
@@ -1,28 +1,34 @@
1
- import { useEffect, useMemo, useState } from 'react';
1
+ import { useLayoutEffect, useMemo, useState } from 'react';
2
2
  import { ITableColumn, ITableColumnsData } from '../../../types/ITable';
3
3
 
4
4
  export interface ITableColumnsProps {
5
5
  defaultColumns: ITableColumn[];
6
- templateColumns?: ITableColumn[];
6
+ enableEdit?: boolean;
7
7
  }
8
8
 
9
9
  export const useTableColumns = ({
10
10
  defaultColumns,
11
- templateColumns,
11
+ enableEdit,
12
12
  }: ITableColumnsProps): ITableColumnsData => {
13
- const [columns, setColumns] = useState<ITableColumn[]>(defaultColumns);
13
+ const [columns, setColumns] = useState<ITableColumn[]>([]);
14
14
 
15
- useEffect(() => {
16
- setColumns(defaultColumns);
17
- }, [defaultColumns, templateColumns]);
15
+ const availableColumns = useMemo(
16
+ () => defaultColumns.filter(e => !e.unavailable),
17
+ [defaultColumns]
18
+ );
19
+
20
+ useLayoutEffect(() => {
21
+ setColumns(availableColumns);
22
+ }, [availableColumns]);
18
23
 
19
24
  const columnData = useMemo(
20
25
  () => ({
21
- defaultColumns,
26
+ defaultColumns: availableColumns,
22
27
  columns,
23
- setColumns,
28
+ setColumns: enableEdit ? setColumns : undefined,
24
29
  }),
25
- [defaultColumns, columns]
30
+ [availableColumns, columns, enableEdit]
26
31
  );
32
+
27
33
  return columnData;
28
34
  };
@@ -1,9 +1,10 @@
1
1
  import { useState } from 'react';
2
- import { ITableEdit } from '../../../types/ITable';
2
+ import { ITableDataItem, ITableEdit } from '../../../types/ITable';
3
3
  import { deleteProps, hasValue } from '../../../utils/objectUtils';
4
4
  import { Select } from '../../Inputs/Selects/Select';
5
5
  import { TextInput } from '../../Inputs/TextInput';
6
6
  import { DateInput } from '../../Inputs/DateInput/DateInput';
7
+ import { NumberInput } from '../../Inputs/NumberInput';
7
8
 
8
9
  interface BaseItemProps<T> {
9
10
  id: keyof T;
@@ -20,7 +21,7 @@ interface SelectProps<T> extends BaseItemProps<T> {
20
21
  }
21
22
 
22
23
  export const useTableEdit = <T extends Record<string, any>>() => {
23
- const [selectedItem, setSelectedItem] = useState<T | null>(null);
24
+ const [selectedItem, setSelectedItem] = useState<ITableDataItem<T> | null>(null);
24
25
  const [editData, setEditData] = useState<Partial<T>>({});
25
26
 
26
27
  // Text
@@ -43,6 +44,26 @@ export const useTableEdit = <T extends Record<string, any>>() => {
43
44
  ),
44
45
  });
45
46
 
47
+ // Number
48
+ const numberEditCell = ({
49
+ placeholder,
50
+ required,
51
+ id,
52
+ additionalClearIds = [],
53
+ }: BaseItemProps<T>) => ({
54
+ value: (
55
+ <NumberInput
56
+ placeholder={placeholder}
57
+ required={required}
58
+ value={editData[id]}
59
+ setValue={e =>
60
+ setEditData(e ? { ...editData, [id]: e } : deleteProps(editData as T, [id]))
61
+ }
62
+ onClear={() => deleteProps(editData as T, [id, ...additionalClearIds])}
63
+ />
64
+ ),
65
+ });
66
+
46
67
  // Date
47
68
  const dateEditCell = ({
48
69
  id,
@@ -105,6 +126,7 @@ export const useTableEdit = <T extends Record<string, any>>() => {
105
126
  return {
106
127
  editable: returnEditable,
107
128
  textEditCell,
129
+ numberEditCell,
108
130
  dateEditCell,
109
131
  selectEditCell,
110
132
  };
@@ -1,9 +1,10 @@
1
- import { useEffect, useState } from 'react';
2
- import { IGetPrintData, IPrintData } from '../../../types/ITable';
1
+ import { useEffect, useMemo, useState } from 'react';
2
+ import { IGetPrintData, IPrintData, ITableDataItem } from '../../../types/ITable';
3
3
  import { usePopupControl } from '../../../hooks/usePopupControl';
4
4
 
5
5
  type Props<T> = {
6
6
  getPrintData: IGetPrintData<T>;
7
+ createTableData?: (data: T[]) => ITableDataItem<T>[];
7
8
  totalRows?: number;
8
9
  };
9
10
 
@@ -12,6 +13,7 @@ const defaultData = { items: [], totalRows: 0 };
12
13
 
13
14
  export const useTablePrint = <T>({
14
15
  getPrintData,
16
+ createTableData,
15
17
  totalRows: initialTotalRows = 0,
16
18
  }: Props<T>) => {
17
19
  const printPopupControl = usePopupControl();
@@ -58,13 +60,19 @@ export const useTablePrint = <T>({
58
60
  // eslint-disable-next-line react-hooks/exhaustive-deps
59
61
  }, [printPopupControl.control.isOpen, initialTotalRows, offset]);
60
62
 
61
- const loaded = data.items.length;
63
+ const loaded = data.items?.length;
62
64
  const progress = data.totalRows ? (loaded / data.totalRows) * 100 : 0;
63
65
 
66
+ const tableData = useMemo(
67
+ () => (!isLoading ? (createTableData?.(data.items) ?? []) : []),
68
+ [isLoading, data.items, createTableData]
69
+ );
70
+
64
71
  const printData: Pick<
65
72
  IPrintData<T>,
66
- 'printPopupControl' | 'isLoading' | 'progress' | 'items'
73
+ 'tableData' | 'printPopupControl' | 'isLoading' | 'progress' | 'items'
67
74
  > = {
75
+ tableData,
68
76
  printPopupControl,
69
77
  isLoading,
70
78
  progress,
@@ -1,7 +1,7 @@
1
1
  import { useMemo, useState } from 'react';
2
2
  import { ITableSelectedAction } from '../../../types/ITable';
3
3
 
4
- export const useTableSelect = ({ actions }: { actions: ITableSelectedAction[] }) => {
4
+ export const useTableSelect = (actions: ITableSelectedAction[] = []) => {
5
5
  const [selectedRows, setSelectedRows] = useState(new Set<string>());
6
6
 
7
7
  const rowSelect = useMemo(
@@ -36,6 +36,7 @@ export const Tabs: FC<Props> = ({
36
36
  <div className="iui-tabs">
37
37
  {filteredTabs.map(tab => (
38
38
  <div
39
+ id={tab.value}
39
40
  key={tab.value}
40
41
  className={clsx('iui-tab clickable', {
41
42
  selected: (control?.value || selected) === tab.value,
@@ -103,31 +103,98 @@ const setAbsolutePosition = (
103
103
  ) => {
104
104
  const containerRect = containerEl.getBoundingClientRect();
105
105
  const tooltipRect = targetEl.getBoundingClientRect();
106
+ const margin = 16; // Distance from container
107
+ const screenPadding = 8; // Minimum distance from screen edges
106
108
 
107
109
  if (position === 'bottom' || position === 'top') {
108
- const bottom = Math.floor(containerRect.bottom + 16);
109
- const top = Math.floor(containerRect.top - tooltipRect.height - 16);
110
- const leftCenter = Math.floor(
110
+ const bottom = Math.floor(containerRect.bottom + margin);
111
+ const top = Math.floor(containerRect.top - tooltipRect.height - margin);
112
+ let leftCenter = Math.floor(
111
113
  containerRect.left + containerRect.width / 2 - tooltipRect.width / 2
112
114
  );
115
+
116
+ // Check if tooltip overflows horizontally and adjust
117
+ if (leftCenter < screenPadding) {
118
+ targetEl.style.width = `${tooltipRect.width + leftCenter}px`;
119
+ leftCenter = screenPadding;
120
+ } else if (leftCenter + tooltipRect.width > window.innerWidth - screenPadding) {
121
+ leftCenter = window.innerWidth - tooltipRect.width - screenPadding;
122
+ }
123
+
113
124
  const reverse =
114
125
  position === 'top' ? top < 0 : bottom + tooltipRect.height > window.innerHeight;
115
126
 
116
127
  setNewPosition(reverse ? (position === 'bottom' ? 'top' : 'bottom') : '');
117
128
 
118
- targetEl.style.top = `${
119
- position === 'bottom' ? (reverse ? top : bottom) : reverse ? bottom : top
120
- }px`;
129
+ let finalTop =
130
+ position === 'bottom' ? (reverse ? top : bottom) : reverse ? bottom : top;
131
+
132
+ // Ensure tooltip doesn't go above screen
133
+ if (finalTop < screenPadding) {
134
+ finalTop = screenPadding;
135
+ }
136
+ // Ensure tooltip doesn't go below screen
137
+ if (finalTop + tooltipRect.height > window.innerHeight - screenPadding) {
138
+ finalTop = window.innerHeight - tooltipRect.height - screenPadding;
139
+ }
140
+
141
+ targetEl.style.top = `${finalTop}px`;
121
142
  targetEl.style.left = `${leftCenter}px`;
122
143
  } else if (position === 'right') {
123
- targetEl.style.top =
124
- Math.floor(containerRect.top + containerRect.height / 2 - tooltipRect.height / 2) +
125
- 'px';
126
- targetEl.style.left = Math.floor(containerRect.right + 16) + 'px';
144
+ let finalTop = Math.floor(
145
+ containerRect.top + containerRect.height / 2 - tooltipRect.height / 2
146
+ );
147
+ let finalLeft = Math.floor(containerRect.right + margin);
148
+
149
+ // Check if tooltip overflows vertically and adjust
150
+ if (finalTop < screenPadding) {
151
+ finalTop = screenPadding;
152
+ } else if (finalTop + tooltipRect.height > window.innerHeight - screenPadding) {
153
+ finalTop = window.innerHeight - tooltipRect.height - screenPadding;
154
+ }
155
+
156
+ // Check if tooltip overflows to the right and flip to left
157
+ if (finalLeft + tooltipRect.width > window.innerWidth - screenPadding) {
158
+ finalLeft = Math.floor(containerRect.left - tooltipRect.width - margin);
159
+ setNewPosition('left');
160
+
161
+ // If it still overflows on the left, position it at the edge
162
+ if (finalLeft < screenPadding) {
163
+ finalLeft = screenPadding;
164
+ }
165
+ } else {
166
+ setNewPosition('');
167
+ }
168
+
169
+ targetEl.style.top = `${finalTop}px`;
170
+ targetEl.style.left = `${finalLeft}px`;
127
171
  } else if (position === 'left') {
128
- targetEl.style.top =
129
- Math.floor(containerRect.top + containerRect.height / 2 - tooltipRect.height / 2) +
130
- 'px';
131
- targetEl.style.left = Math.floor(containerRect.left - tooltipRect.width - 16) + 'px';
172
+ let finalTop = Math.floor(
173
+ containerRect.top + containerRect.height / 2 - tooltipRect.height / 2
174
+ );
175
+ let finalLeft = Math.floor(containerRect.left - tooltipRect.width - margin);
176
+
177
+ // Check if tooltip overflows vertically and adjust
178
+ if (finalTop < screenPadding) {
179
+ finalTop = screenPadding;
180
+ } else if (finalTop + tooltipRect.height > window.innerHeight - screenPadding) {
181
+ finalTop = window.innerHeight - tooltipRect.height - screenPadding;
182
+ }
183
+
184
+ // Check if tooltip overflows to the left and flip to right
185
+ if (finalLeft < screenPadding) {
186
+ finalLeft = Math.floor(containerRect.right + margin);
187
+ setNewPosition('right');
188
+
189
+ // If it still overflows on the right, position it at the edge
190
+ if (finalLeft + tooltipRect.width > window.innerWidth - screenPadding) {
191
+ finalLeft = window.innerWidth - tooltipRect.width - screenPadding;
192
+ }
193
+ } else {
194
+ setNewPosition('');
195
+ }
196
+
197
+ targetEl.style.top = `${finalTop}px`;
198
+ targetEl.style.left = `${finalLeft}px`;
132
199
  }
133
200
  };
@@ -4,8 +4,8 @@ export const useIsMenuOpen = () => {
4
4
  const [isOpen, setIsOpen] = useState(false);
5
5
 
6
6
  return {
7
- isMenuOpen: isOpen,
8
- onMenuOpen: () => setIsOpen(true),
9
- onMenuClose: () => setTimeout(() => setIsOpen(false)),
7
+ isOpen,
8
+ onOpen: () => setIsOpen(true),
9
+ onClose: () => setTimeout(() => setIsOpen(false)),
10
10
  };
11
11
  };
@@ -1,13 +1,18 @@
1
1
  import { useCallback, useRef, useState } from 'react';
2
- import { IPopupControl, PopupControlRef } from '../types/IPopup';
2
+ import { IPopupControl, IPopupControlRef } from '../types/IPopup';
3
3
 
4
- export const usePopupControl: () => IPopupControl = () => {
4
+ export const usePopupControl: (props?: {
5
+ onCloseCallback?: () => void;
6
+ }) => IPopupControl = props => {
5
7
  const [isOpen, setIsOpen] = useState(false);
6
8
 
7
- const controlRef = useRef<PopupControlRef>(null);
9
+ const controlRef = useRef<IPopupControlRef>(null);
8
10
 
9
11
  const onOpen = useCallback(() => controlRef.current?.onOpen(), []);
10
- const onClose = useCallback(() => controlRef.current?.onClose(), []);
12
+ const onClose = useCallback(() => {
13
+ controlRef.current?.onClose();
14
+ props?.onCloseCallback?.();
15
+ }, [props]);
11
16
 
12
17
  return {
13
18
  control: { controlRef, isOpen, setIsOpen },