@inceptionbg/iui 2.0.16 → 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 (61) hide show
  1. package/dist/index.d.ts +155 -89
  2. package/dist/index.js +1 -1
  3. package/dist/index.js.map +1 -1
  4. package/dist/iui.css +1 -1
  5. package/package.json +3 -5
  6. package/src/assets/icons/duotone/faBell.ts +17 -0
  7. package/src/assets/icons/duotone/faPen.ts +18 -0
  8. package/src/assets/icons/duotone/faTrashCan.ts +18 -0
  9. package/src/assets/icons/light/faBell.ts +15 -0
  10. package/src/assets/icons/light/faEnvelope.ts +15 -0
  11. package/src/assets/icons/regular/faEllipsisVertical.ts +15 -0
  12. package/src/assets/icons/solid/faEnvelopeDot.ts +15 -0
  13. package/src/components/Button/SplitButton.tsx +5 -5
  14. package/src/components/Header/Components/ModuleSelect.tsx +5 -5
  15. package/src/components/Header/Components/Notifications.tsx +208 -0
  16. package/src/components/Header/Header.tsx +5 -4
  17. package/src/components/Inputs/NumberInput.tsx +3 -0
  18. package/src/components/Inputs/Selects/components/SelectWrapper.tsx +48 -29
  19. package/src/components/Loader/ProgressBar.tsx +1 -1
  20. package/src/components/Pullover/Pullover.tsx +12 -7
  21. package/src/components/Table/Table.tsx +5 -3
  22. package/src/components/Table/components/columns/TableColumnsEdit.tsx +5 -9
  23. package/src/components/Table/components/edit/TableEditRow.tsx +23 -12
  24. package/src/components/Table/components/header/TableHeaderRow.tsx +1 -0
  25. package/src/components/Table/components/items/TableItemActions.tsx +18 -13
  26. package/src/components/Table/components/print/TablePrint.tsx +1 -5
  27. package/src/components/Table/components/templates/CreateTemplateDialog.tsx +48 -0
  28. package/src/components/Table/components/templates/TableTemplates.tsx +24 -4
  29. package/src/components/Table/contexts/TableContext.tsx +33 -24
  30. package/src/components/Table/hooks/localHooks/useLocalTableColumns.tsx +6 -4
  31. package/src/components/Table/hooks/localHooks/useLocalTableData.tsx +17 -11
  32. package/src/components/Table/hooks/localHooks/useLocalTableKeyboard.ts +8 -6
  33. package/src/components/Table/hooks/useTableColumns.ts +1 -3
  34. package/src/components/Table/hooks/useTableEdit.tsx +24 -2
  35. package/src/components/Table/hooks/useTablePrint.ts +12 -4
  36. package/src/components/Table/hooks/useTableSelect.ts +1 -1
  37. package/src/components/Tooltip/Tooltip.tsx +81 -14
  38. package/src/hooks/useIsMenuOpen.ts +3 -3
  39. package/src/hooks/usePopupControl.ts +9 -4
  40. package/src/index.ts +23 -4
  41. package/src/styles/App.scss +1 -0
  42. package/src/styles/components/_badge.scss +7 -0
  43. package/src/styles/components/_header.scss +14 -1
  44. package/src/styles/components/_notifications.scss +71 -0
  45. package/src/styles/components/_page.scss +1 -0
  46. package/src/styles/components/_pullover.scss +1 -1
  47. package/src/styles/components/_sidebar.scss +1 -3
  48. package/src/styles/components/_table.scss +96 -54
  49. package/src/types/IKeyboard.ts +0 -5
  50. package/src/types/IMenu.ts +2 -2
  51. package/src/types/INotifications.ts +15 -0
  52. package/src/types/IPopup.ts +2 -2
  53. package/src/types/ITable.ts +35 -32
  54. package/src/utils/i18n/i18nIUICyrilic.ts +12 -0
  55. package/src/utils/i18n/i18nIUILatin.ts +13 -0
  56. package/src/utils/i18n/i18nIUIMe.ts +12 -0
  57. package/src/utils/objectUtils.ts +19 -0
  58. package/src/utils/tableUtils.ts +1 -1
  59. package/idea/Notifications.tsx +0 -245
  60. package/idea/Table/Components/Columns/ColumnsList.tsx +0 -61
  61. package/idea/Table/Components/Columns/SetColumnsList.tsx +0 -113
@@ -0,0 +1,48 @@
1
+ import { useState, type FC } from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import type { ILocalPopupControl } from '../../../../types/IPopup';
4
+ import type { IReportTemplate } from '../../../../types/ITable';
5
+ import { Dialog } from '../../../Dialog/Dialog';
6
+ import { TextInput } from '../../../Inputs/TextInput';
7
+ import { Checkbox } from '../../../Inputs/Checkbox';
8
+ import { useTableContext } from '../../contexts/TableContext';
9
+
10
+ interface Props {
11
+ control: ILocalPopupControl;
12
+ }
13
+
14
+ export const CreateTemplateDialog: FC<Props> = ({ control }) => {
15
+ const { t } = useTranslation();
16
+ const { templateData: { createItem } = {} } = useTableContext();
17
+
18
+ const [formData, setFormData] = useState<Partial<IReportTemplate>>({});
19
+
20
+ return (
21
+ <Dialog
22
+ title={t('CreateTemplateLong')}
23
+ control={control}
24
+ onFormSubmit={() => createItem?.onCreate(formData)}
25
+ footer={{
26
+ confirmButton: {
27
+ label: t('CreateTemplate'),
28
+ type: 'submit',
29
+ },
30
+ }}
31
+ onCloseCallback={() => setFormData({})}
32
+ >
33
+ <TextInput
34
+ label={t('TemplateName')}
35
+ required
36
+ value={formData.name}
37
+ setValue={name => setFormData({ ...formData, name })}
38
+ />
39
+ {createItem?.allowPublicCreate && (
40
+ <Checkbox
41
+ label={t('PublicTemplate')}
42
+ value={formData.isPublic}
43
+ setValue={isPublic => setFormData({ ...formData, isPublic })}
44
+ />
45
+ )}
46
+ </Dialog>
47
+ );
48
+ };
@@ -1,4 +1,4 @@
1
- import { type FC } from 'react';
1
+ import { useEffect, type FC } from 'react';
2
2
  import { IconButton } from '../../../Button/IconButton';
3
3
  import { useTranslation } from 'react-i18next';
4
4
  import { useTableContext } from '../../contexts/TableContext';
@@ -6,11 +6,30 @@ import { faBookmark, faBookmarkSlash, faTrashCan } from '../../../../assets/icon
6
6
  import { Pullover } from '../../../Pullover/Pullover';
7
7
  import { List } from '../../../List/List';
8
8
  import { setTemplateData } from '../../../../utils/tableUtils';
9
+ import { usePopupControl } from '../../../../hooks/usePopupControl';
10
+ import { CreateTemplateDialog } from './CreateTemplateDialog';
9
11
 
10
12
  export const TableTemplates: FC = () => {
11
13
  const { t } = useTranslation();
12
14
  const { templateData, columnData, filterData, sortData } = useTableContext();
13
15
 
16
+ const createTemplatePopupController = usePopupControl();
17
+
18
+ useEffect(() => {
19
+ if (templateData?.defaultTemplate) {
20
+ setTemplateData({
21
+ template: templateData.defaultTemplate,
22
+ columnData,
23
+ setFilters: e => {
24
+ filterData?.setSearchData(e);
25
+ filterData?.onSearch(e);
26
+ },
27
+ setSort: sortData?.setSort,
28
+ });
29
+ }
30
+ // eslint-disable-next-line
31
+ }, [templateData?.defaultTemplate]);
32
+
14
33
  if (!templateData) return null;
15
34
 
16
35
  return (
@@ -34,12 +53,12 @@ export const TableTemplates: FC = () => {
34
53
  footer={{
35
54
  confirmButton: {
36
55
  label: t('CreateTemplate'),
37
- onClick: templateData.initiateCreate,
56
+ onClick: createTemplatePopupController.onOpen,
38
57
  splitActions: [
39
58
  {
40
59
  label: t('CreateDefaultTemplate'),
41
60
  icon: faBookmark,
42
- onClick: templateData.initiateCreateDefault,
61
+ onClick: templateData.createItem.onCreateDefault,
43
62
  },
44
63
  ],
45
64
  },
@@ -86,7 +105,8 @@ export const TableTemplates: FC = () => {
86
105
  noItemsMessage={t('NoTemplatesAvailable')}
87
106
  />
88
107
  </Pullover>
89
- {templateData.TemplateNode}
108
+ <CreateTemplateDialog control={createTemplatePopupController.control} />
109
+ {templateData.DeleteDialog}
90
110
  </>
91
111
  );
92
112
  };
@@ -11,21 +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
19
  import { useLocalTableData } from '../hooks/localHooks/useLocalTableData';
20
+ import { useTranslation } from 'react-i18next';
19
21
 
20
- interface ITableContext extends ITable {
22
+ interface ITableContext<T = unknown> extends ITable<T> {
21
23
  columnData: ITableColumnsData;
22
24
  selectActions: ITableSelectedAction[];
23
- dataActions?: ITable['dataActions'] & {
25
+ dataActions?: ITableDataActions<T> & {
24
26
  hasItemActions: boolean;
25
27
  isEditable: boolean;
26
28
  isDeletable: boolean;
29
+ filteredActions: ITableSelectedAction[];
27
30
  };
28
- editable?: ITableEdit & {
31
+ editable?: ITableEdit<T> & {
29
32
  isAdding: boolean;
30
33
  setIsAdding: Dispatch<SetStateAction<boolean>>;
31
34
  };
@@ -35,17 +38,13 @@ interface ITableContext extends ITable {
35
38
  };
36
39
  }
37
40
 
38
- const TableContext = createContext<ITableContext>({
41
+ const TableContext = createContext<ITableContext<any>>({
39
42
  columnData: { columns: [], defaultColumns: [] },
40
43
  data: [],
41
44
  selectActions: [],
42
45
  });
43
46
 
44
- interface Props extends ITable {
45
- children: ReactNode;
46
- }
47
-
48
- export const TableProvider = ({
47
+ export const TableProvider = <T,>({
49
48
  columnData,
50
49
  data: initialData,
51
50
  dataActions,
@@ -54,38 +53,48 @@ export const TableProvider = ({
54
53
  children,
55
54
  rowSelect,
56
55
  ...rest
57
- }: Props) => {
56
+ }: ITable<T> & { children: ReactNode }) => {
57
+ const { t } = useTranslation();
58
58
  const [isAdding, setIsAdding] = useState(false);
59
59
 
60
- const selectActions = useMemo(
61
- () => rowSelect?.actions.filter(e => !e.hidden) || [],
62
- [rowSelect?.actions]
63
- );
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
+
64
71
  const updatedDataActions = useMemo(() => {
65
- const isEditable = Boolean(dataActions?.edit?.hasAccess);
66
- const isDeletable = Boolean(dataActions?.delete?.hasAccess);
67
- 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) ?? [];
68
75
  return {
69
- ...dataActions,
70
- actions: filteredDataActions,
71
- hasItemActions: isEditable || isDeletable || filteredDataActions.length > 0,
76
+ hasEditAccess: isEditable,
77
+ actions: dataActions?.actions,
78
+ filteredActions,
79
+ hasItemActions: isEditable || isDeletable || filteredActions.length > 0,
72
80
  isEditable,
73
81
  isDeletable,
74
82
  };
75
- }, [dataActions]);
83
+ }, [dataActions, rest.itemDeleteData?.hasAccess]);
76
84
 
77
85
  const localPagination = useLocalTablePagination({
78
86
  defaultLimit: footer?.customPagination?.defaultLimit,
79
87
  });
80
- const data = useLocalTableData({
88
+ const data = useLocalTableData<T>({
81
89
  initialData,
82
90
  localPagination,
83
91
  controledPagination: !!footer?.paginationControl,
84
92
  dataActions: updatedDataActions,
93
+ isDeletable: updatedDataActions.isDeletable,
85
94
  rowSelect,
86
95
  });
87
96
 
88
- const tableContextValue: ITableContext = {
97
+ const tableContextValue: ITableContext<T> = {
89
98
  ...rest,
90
99
  data,
91
100
  dataActions: updatedDataActions,
@@ -99,7 +108,7 @@ export const TableProvider = ({
99
108
  tableContextValue.footer = {
100
109
  ...footer,
101
110
  paginationControl: footer?.paginationControl ?? localPagination,
102
- totalRows: footer?.totalRows ?? initialData.length,
111
+ totalRows: footer?.totalRows ?? initialData?.length ?? 0,
103
112
  noTotalRows: footer?.totalRows === undefined,
104
113
  };
105
114
 
@@ -25,9 +25,9 @@ export const useLocalTableColumns = () => {
25
25
  if (hasSelect) {
26
26
  const selectableData = data.filter(e => !e.disableSelect);
27
27
  const selectedSome =
28
- !!rowSelect.selectedRows.size &&
28
+ !!rowSelect?.selectedRows.size &&
29
29
  selectableData.some(e => !rowSelect.selectedRows.has(e.uuid));
30
- const selectedAll = !!rowSelect.selectedRows.size && !selectedSome;
30
+ const selectedAll = !!rowSelect?.selectedRows.size && !selectedSome;
31
31
 
32
32
  const selectColumn: ITableColumn = {
33
33
  id: 'select',
@@ -37,7 +37,7 @@ export const useLocalTableColumns = () => {
37
37
  setValue={() => {
38
38
  selectedAll
39
39
  ? rowSelect.setSelectedRows(new Set<string>())
40
- : rowSelect.setSelectedRows(oldValue => {
40
+ : rowSelect?.setSelectedRows(oldValue => {
41
41
  let newSet = new Set(oldValue);
42
42
  selectableData.forEach(e => newSet.add(e.uuid!));
43
43
  return newSet;
@@ -53,13 +53,15 @@ export const useLocalTableColumns = () => {
53
53
  }
54
54
 
55
55
  newCols = newCols.concat(filteredColumns);
56
- hasItemActions &&
56
+ // Actions Column
57
+ if (hasItemActions) {
57
58
  newCols.push({
58
59
  id: 'actions',
59
60
  label: t('Actions'),
60
61
  align: 'center',
61
62
  className: 'actions-col',
62
63
  });
64
+ }
63
65
 
64
66
  return newCols;
65
67
  }, [columns, selectActions.length, data, rowSelect, hasItemActions, t]);
@@ -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,
@@ -3,13 +3,11 @@ import { ITableColumn, ITableColumnsData } from '../../../types/ITable';
3
3
 
4
4
  export interface ITableColumnsProps {
5
5
  defaultColumns: ITableColumn[];
6
- templateColumns?: ITableColumn[];
7
6
  enableEdit?: boolean;
8
7
  }
9
8
 
10
9
  export const useTableColumns = ({
11
10
  defaultColumns,
12
- templateColumns,
13
11
  enableEdit,
14
12
  }: ITableColumnsProps): ITableColumnsData => {
15
13
  const [columns, setColumns] = useState<ITableColumn[]>([]);
@@ -21,7 +19,7 @@ export const useTableColumns = ({
21
19
 
22
20
  useLayoutEffect(() => {
23
21
  setColumns(availableColumns);
24
- }, [availableColumns, templateColumns]);
22
+ }, [availableColumns]);
25
23
 
26
24
  const columnData = useMemo(
27
25
  () => ({
@@ -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(
@@ -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 },