qbs-react-grid 2.2.4 → 2.2.6

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 (100) hide show
  1. package/dist/css/qbs-react-grid.css +1 -1
  2. package/dist/css/qbs-react-grid.min.css +1 -1
  3. package/dist/css/qbs-react-grid.min.css.map +1 -1
  4. package/es/Cell.js +1 -2
  5. package/es/Pagination.d.ts +0 -3
  6. package/es/Pagination.js +3 -8
  7. package/es/Table.d.ts +0 -3
  8. package/es/Table.js +12 -18
  9. package/es/index.d.ts +1 -1
  10. package/es/less/pagination.less +9 -9
  11. package/es/less/qbs-table.less +73 -205
  12. package/es/qbsTable/CustomTableCell.js +15 -11
  13. package/es/qbsTable/QbsTable.js +48 -124
  14. package/es/qbsTable/TableCardList.js +41 -52
  15. package/es/qbsTable/Toolbar.js +13 -62
  16. package/es/qbsTable/commontypes.d.ts +3 -30
  17. package/es/qbsTable/utilities/CardComponent.d.ts +0 -2
  18. package/es/qbsTable/utilities/CardComponent.js +3 -7
  19. package/es/qbsTable/utilities/CardMenuDropdown.d.ts +0 -2
  20. package/es/qbsTable/utilities/CardMenuDropdown.js +7 -7
  21. package/es/qbsTable/utilities/ColumShowHide.d.ts +0 -4
  22. package/es/qbsTable/utilities/ColumShowHide.js +6 -9
  23. package/es/qbsTable/utilities/SearchInput.d.ts +0 -1
  24. package/es/qbsTable/utilities/SearchInput.js +1 -3
  25. package/es/qbsTable/utilities/ToolTip.d.ts +1 -8
  26. package/es/qbsTable/utilities/ToolTip.js +31 -107
  27. package/es/qbsTable/utilities/VerticalDropDownMenu.d.ts +7 -9
  28. package/es/qbsTable/utilities/VerticalDropDownMenu.js +71 -63
  29. package/es/qbsTable/utilities/empty.js +1 -1
  30. package/es/qbsTable/utilities/icons.d.ts +0 -3
  31. package/es/qbsTable/utilities/icons.js +1 -65
  32. package/es/qbsTable/utilities/tablecalc.d.ts +1 -1
  33. package/es/qbsTable/utilities/tablecalc.js +2 -7
  34. package/es/utils/useCellDescriptor.js +1 -0
  35. package/es/utils/useScrollListener.d.ts +0 -1
  36. package/es/utils/useScrollListener.js +3 -5
  37. package/lib/Cell.js +1 -2
  38. package/lib/Pagination.d.ts +0 -3
  39. package/lib/Pagination.js +3 -8
  40. package/lib/Table.d.ts +0 -3
  41. package/lib/Table.js +12 -18
  42. package/lib/index.d.ts +1 -1
  43. package/lib/less/pagination.less +9 -9
  44. package/lib/less/qbs-table.less +73 -205
  45. package/lib/qbsTable/CustomTableCell.js +15 -11
  46. package/lib/qbsTable/QbsTable.js +48 -124
  47. package/lib/qbsTable/TableCardList.js +41 -52
  48. package/lib/qbsTable/Toolbar.js +12 -61
  49. package/lib/qbsTable/commontypes.d.ts +3 -30
  50. package/lib/qbsTable/utilities/CardComponent.d.ts +0 -2
  51. package/lib/qbsTable/utilities/CardComponent.js +3 -7
  52. package/lib/qbsTable/utilities/CardMenuDropdown.d.ts +0 -2
  53. package/lib/qbsTable/utilities/CardMenuDropdown.js +5 -6
  54. package/lib/qbsTable/utilities/ColumShowHide.d.ts +0 -4
  55. package/lib/qbsTable/utilities/ColumShowHide.js +6 -9
  56. package/lib/qbsTable/utilities/SearchInput.d.ts +0 -1
  57. package/lib/qbsTable/utilities/SearchInput.js +1 -3
  58. package/lib/qbsTable/utilities/ToolTip.d.ts +1 -8
  59. package/lib/qbsTable/utilities/ToolTip.js +30 -107
  60. package/lib/qbsTable/utilities/VerticalDropDownMenu.d.ts +7 -9
  61. package/lib/qbsTable/utilities/VerticalDropDownMenu.js +71 -63
  62. package/lib/qbsTable/utilities/empty.js +1 -1
  63. package/lib/qbsTable/utilities/icons.d.ts +0 -3
  64. package/lib/qbsTable/utilities/icons.js +3 -70
  65. package/lib/qbsTable/utilities/tablecalc.d.ts +1 -1
  66. package/lib/qbsTable/utilities/tablecalc.js +2 -7
  67. package/lib/utils/useCellDescriptor.js +1 -0
  68. package/lib/utils/useScrollListener.d.ts +0 -1
  69. package/lib/utils/useScrollListener.js +3 -5
  70. package/package.json +10 -2
  71. package/src/Cell.tsx +1 -3
  72. package/src/HeaderCell.tsx +1 -0
  73. package/src/Pagination.tsx +3 -10
  74. package/src/Table.tsx +10 -23
  75. package/src/customSelect.tsx +88 -88
  76. package/src/index.ts +1 -1
  77. package/src/less/pagination.less +9 -9
  78. package/src/less/qbs-table.less +73 -205
  79. package/src/qbsTable/CustomTableCell.tsx +23 -27
  80. package/src/qbsTable/QbsTable.tsx +26 -100
  81. package/src/qbsTable/TableCardList.tsx +20 -34
  82. package/src/qbsTable/Toolbar.tsx +11 -53
  83. package/src/qbsTable/commontypes.ts +2 -32
  84. package/src/qbsTable/utilities/CardComponent.tsx +2 -7
  85. package/src/qbsTable/utilities/CardMenuDropdown.tsx +6 -11
  86. package/src/qbsTable/utilities/ColumShowHide.tsx +6 -33
  87. package/src/qbsTable/utilities/SearchInput.tsx +1 -3
  88. package/src/qbsTable/utilities/ToolTip.tsx +27 -138
  89. package/src/qbsTable/utilities/VerticalDropDownMenu.tsx +89 -74
  90. package/src/qbsTable/utilities/empty.tsx +2 -2
  91. package/src/qbsTable/utilities/icons.tsx +1 -78
  92. package/src/qbsTable/utilities/tablecalc.ts +2 -8
  93. package/src/utils/useCellDescriptor.ts +1 -0
  94. package/src/utils/useScrollListener.ts +3 -13
  95. package/src/utils/useTableRows.ts +1 -1
  96. package/es/qbsTable/labels.d.ts +0 -48
  97. package/es/qbsTable/labels.js +0 -34
  98. package/lib/qbsTable/labels.d.ts +0 -48
  99. package/lib/qbsTable/labels.js +0 -42
  100. package/src/qbsTable/labels.ts +0 -58
@@ -1,7 +1,6 @@
1
1
  import React, { useRef, useState } from 'react';
2
2
 
3
3
  import { QbsColumnProps } from '../commontypes';
4
- import { mergeLabels, type QbsTableLabels } from '../labels';
5
4
  import CardMenuDropdown from './CardMenuDropdown';
6
5
  import { handleCellFormat } from './handleFormatCell';
7
6
  import { ArrowUpIcon } from './icons';
@@ -18,7 +17,6 @@ type Props = {
18
17
  handleMenuActions?: () => void;
19
18
  cardColumLimit?: number;
20
19
  childDetailHeading?: string;
21
- labels?: QbsTableLabels;
22
20
  };
23
21
 
24
22
  const CardComponent: React.FC<Props> = ({
@@ -29,10 +27,8 @@ const CardComponent: React.FC<Props> = ({
29
27
  index,
30
28
  cardColumLimit = 5,
31
29
  handleMenuActions,
32
- childDetailHeading = '',
33
- labels: labelsProp
30
+ childDetailHeading = ''
34
31
  }) => {
35
- const labels = mergeLabels(labelsProp);
36
32
  const [viewMore, setViewMore] = useState(false);
37
33
  const initialDisplayCount = cardColumLimit;
38
34
 
@@ -106,14 +102,13 @@ const CardComponent: React.FC<Props> = ({
106
102
  iconName="more"
107
103
  rowIndex={index}
108
104
  handleMenuActions={handleMenuActions}
109
- labels={labels}
110
105
  />
111
106
  </div>
112
107
 
113
108
  {columns.length > initialDisplayCount && (
114
109
  <TooltipComponent
115
110
  tableBodyRef={useCardRef}
116
- title={viewMore ? labels.viewLess : labels.viewMore}
111
+ title={viewMore ? ' View Less' : 'View More'}
117
112
  enabled={false}
118
113
  >
119
114
  <button
@@ -1,6 +1,7 @@
1
- import React, { useEffect, useRef, useState } from 'react';
1
+ import { useEffect, useRef, useState } from 'react';
2
+ import React from 'react';
3
+
2
4
  import { ActionProps } from '../commontypes';
3
- import { mergeLabels, type QbsTableLabels } from '../labels';
4
5
  import { ThreeDotIcon } from './icons';
5
6
  import TooltipComponent from './ToolTip';
6
7
 
@@ -12,16 +13,9 @@ type Props = {
12
13
  dataTheme?: string;
13
14
  tableBodyRef: React.RefObject<HTMLDivElement>;
14
15
  rowIndex?: number;
15
- labels?: QbsTableLabels;
16
16
  };
17
17
 
18
- const CardMenuDropdown: React.FC<Props> = ({
19
- actionDropDown,
20
- handleMenuActions,
21
- rowData,
22
- labels: labelsProp
23
- }) => {
24
- const labels = mergeLabels(labelsProp);
18
+ const CardMenuDropdown: React.FC<Props> = ({ actionDropDown, handleMenuActions, rowData }) => {
25
19
  const [openMenu, setOpenMenu] = useState(false);
26
20
  const [menuPositionStyles, setMenuPositionStyles] = useState<{
27
21
  top?: string;
@@ -52,6 +46,7 @@ const CardMenuDropdown: React.FC<Props> = ({
52
46
  const dropdownHeight = 200; // Adjust this if your dropdown height varies
53
47
  const spaceBelow = window.innerHeight - buttonRect.bottom;
54
48
  const spaceAbove = buttonRect.top;
49
+ console.log(spaceAbove, spaceBelow, dropdownHeight);
55
50
  if (spaceBelow < dropdownHeight && spaceAbove > spaceBelow) {
56
51
  setMenuPositionStyles({
57
52
  bottom: '30px',
@@ -81,7 +76,7 @@ const CardMenuDropdown: React.FC<Props> = ({
81
76
  return (
82
77
  <div className="dropdown text-black dark:text-white dark:bg-[#424242] bg-white" ref={menuRef}>
83
78
  <button className="dropdown-toggle" onClick={toggleMenu} ref={menuButtonRef}>
84
- <TooltipComponent title={labels.actions} enabled={false}>
79
+ <TooltipComponent title="Actions" enabled={false} ref={menuButtonRef}>
85
80
  <ThreeDotIcon />
86
81
  </TooltipComponent>
87
82
  </button>
@@ -1,7 +1,6 @@
1
1
  import React, { useCallback, useEffect, useRef, useState } from 'react';
2
2
 
3
3
  import { QbsColumnProps } from '../commontypes';
4
- import { mergeLabels, type QbsTableLabels } from '../labels';
5
4
  import { SettingsIcon } from './icons';
6
5
 
7
6
  interface ColumnToggleProps {
@@ -13,9 +12,6 @@ interface ColumnToggleProps {
13
12
  handleColumnToggle?: (columns: QbsColumnProps[]) => void;
14
13
  handleResetColumns?: () => void;
15
14
  tableHeight?: number;
16
- viewMode?: string;
17
- setViewMode?: (value: string) => void;
18
- labels?: QbsTableLabels;
19
15
  }
20
16
 
21
17
  const ColumnToggle: React.FC<ColumnToggleProps> = ({
@@ -26,10 +22,8 @@ const ColumnToggle: React.FC<ColumnToggleProps> = ({
26
22
  setIsOpen,
27
23
  handleResetColumns,
28
24
  handleColumnToggle,
29
- tableHeight = 450,
30
- labels: labelsProp
25
+ tableHeight = 450
31
26
  }) => {
32
- const labels = mergeLabels(labelsProp);
33
27
  const [draggedItem, setDraggedItem] = useState<number | null>(null);
34
28
  const popupRef = useRef<HTMLDivElement | null>(null);
35
29
  const [dragOverPosition, setDragOverPosition] = useState<number | null>();
@@ -189,7 +183,7 @@ const ColumnToggle: React.FC<ColumnToggleProps> = ({
189
183
  >
190
184
  <div className="qbs-table-popup-container">
191
185
  <div className="qbs-table-popup-item">
192
- <div className="qbs-table-popup-label">{labels.fixedColumns}</div>
186
+ <div className="qbs-table-popup-label">FIXED COLUMNS</div>
193
187
  <div className="qbs-table-columns-container">
194
188
  <div className="qbs-table-column">
195
189
  {columns.map((column, index) =>
@@ -200,7 +194,7 @@ const ColumnToggle: React.FC<ColumnToggleProps> = ({
200
194
  </div>
201
195
  <div className="qbs-table-divider"></div>
202
196
  <div className="qbs-table-popup-item">
203
- <div className="qbs-table-popup-label">{labels.visibleColumns}</div>
197
+ <div className="qbs-table-popup-label">VISIBLE COLUMNS</div>
204
198
  <div className="qbs-table-columns-container">
205
199
  <div className="qbs-table-column">
206
200
  {columns.map((column, index) =>
@@ -213,7 +207,7 @@ const ColumnToggle: React.FC<ColumnToggleProps> = ({
213
207
  <>
214
208
  <div className="qbs-table-divider"></div>
215
209
  <div className="qbs-table-popup-item">
216
- <div className="qbs-table-popup-label">{labels.availableColumns}</div>
210
+ <div className="qbs-table-popup-label">AVAILABLE COLUMNS</div>
217
211
  <div className="qbs-table-columns-container">
218
212
  <div className="qbs-table-column">
219
213
  {columns.map((column, index) =>
@@ -237,35 +231,14 @@ const ColumnToggle: React.FC<ColumnToggleProps> = ({
237
231
  href="#"
238
232
  onClick={() => handleResetColumns?.()}
239
233
  >
240
- {labels.resetToDefault}
234
+ Reset to default
241
235
  </a>
242
236
  <a className="qbs-table-reset-link" href="#" onClick={() => handleColToggle()}>
243
- {labels.save}
237
+ Save
244
238
  </a>
245
239
  </div>
246
240
  </>
247
241
  )}
248
- {/* <div className="qbs-table-popup-item">
249
- <label className="flex items-center gap-2">
250
- <input
251
- type="radio"
252
- value="compact"
253
- checked={viewMode === 'compact'}
254
- onChange={() => setViewMode?.('compact')}
255
- />
256
- Compact View
257
- </label>
258
-
259
- <label className="flex items-center gap-2">
260
- <input
261
- type="radio"
262
- value="expanded"
263
- checked={viewMode === 'expanded'}
264
- onChange={() => setViewMode?.('expanded')}
265
- />
266
- Default View
267
- </label>
268
- </div> */}
269
242
  </div>
270
243
  </div>
271
244
  )}
@@ -2,14 +2,12 @@ import React, { memo, useCallback } from 'react';
2
2
 
3
3
  export interface SearchProps {
4
4
  placeholder: string;
5
- searchAriaLabel?: string;
6
5
  handleChange: (value: string) => void;
7
6
  searchValue: string | undefined;
8
7
  handleSearch: (value?: string) => void;
9
8
  }
10
9
  const SearchInput: React.FC<SearchProps> = ({
11
10
  placeholder,
12
- searchAriaLabel = 'Search',
13
11
  handleChange,
14
12
  searchValue,
15
13
  handleSearch
@@ -39,7 +37,7 @@ const SearchInput: React.FC<SearchProps> = ({
39
37
  placeholder={placeholder}
40
38
  value={searchValue ?? ''}
41
39
  onChange={handleInputChange}
42
- aria-label={searchAriaLabel}
40
+ aria-label="Search"
43
41
  />
44
42
  <button
45
43
  className="search-button absolute left-1 bottom-1.5 bg-white text-grey-dark"
@@ -1,149 +1,38 @@
1
- import React, { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';
2
- import ReactDOM from 'react-dom';
3
-
4
- type TooltipComponentProps = {
5
- title?: React.ReactNode;
6
- children: React.ReactNode;
7
- tableBodyRef?: React.RefObject<HTMLDivElement | null>;
8
- /** When false, renders children only (no tooltip). */
9
- enabled?: boolean;
10
- };
11
-
12
- const VIEWPORT_PADDING = 8;
13
- const TOOLTIP_GAP = 8;
14
-
15
- const TooltipComponent: React.FC<TooltipComponentProps> = ({
16
- title,
17
- children,
18
- tableBodyRef,
19
- enabled = true,
20
- }) => {
21
- const [visible, setVisible] = useState(false);
22
- const [positioned, setPositioned] = useState(false);
23
- const [placement, setPlacement] = useState<'up' | 'down'>('down');
24
- const [coords, setCoords] = useState({ top: 0, left: 0 });
25
- const [arrowOffset, setArrowOffset] = useState(0);
26
- const triggerRef = useRef<HTMLSpanElement>(null);
27
- const tooltipRef = useRef<HTMLSpanElement>(null);
28
-
29
- const updatePosition = useCallback(() => {
30
- const trigger = triggerRef.current;
31
- const tooltip = tooltipRef.current;
32
- if (!trigger || !tooltip) {
33
- return;
1
+ import React, { useRef, useState } from 'react';
2
+
3
+ const TooltipComponent: React.FC<any> = ({ title, children, tableBodyRef }) => {
4
+ const [dropdownPosition, setDropdownPosition] = useState('bottom-position');
5
+ const dropRef = useRef(null);
6
+ const menuButtonRef = useRef<HTMLElement>(null);
7
+ const adjustDropdownPosition = () => {
8
+ if (menuButtonRef.current && tableBodyRef?.current) {
9
+ const inputBoxRect = menuButtonRef.current?.getBoundingClientRect();
10
+ const tableRect = tableBodyRef.current.getBoundingClientRect();
11
+ // Calculate positions relative to the table
12
+ const spaceAbove = inputBoxRect.top - tableRect.top;
13
+ const spaceBelow = tableRect.bottom - inputBoxRect.bottom;
14
+
15
+ if (spaceAbove > spaceBelow) {
16
+ setDropdownPosition('top-position');
17
+ } else {
18
+ setDropdownPosition('bottom-position');
19
+ }
34
20
  }
35
-
36
- const triggerRect = trigger.getBoundingClientRect();
37
- const tooltipRect = tooltip.getBoundingClientRect();
38
- const boundaryRect =
39
- tableBodyRef?.current?.getBoundingClientRect() ??
40
- trigger.closest('.qbs-table')?.getBoundingClientRect();
41
-
42
- const spaceAbove = boundaryRect
43
- ? triggerRect.top - boundaryRect.top
44
- : triggerRect.top;
45
- const spaceBelow = boundaryRect
46
- ? boundaryRect.bottom - triggerRect.bottom
47
- : window.innerHeight - triggerRect.bottom;
48
-
49
- const nextPlacement =
50
- spaceBelow >= tooltipRect.height + TOOLTIP_GAP || spaceBelow >= spaceAbove ? 'down' : 'up';
51
-
52
- const triggerCenter = triggerRect.left + triggerRect.width / 2;
53
- let left = triggerCenter - tooltipRect.width / 2;
54
-
55
- if (left < VIEWPORT_PADDING) {
56
- left = VIEWPORT_PADDING;
57
- } else if (left + tooltipRect.width > window.innerWidth - VIEWPORT_PADDING) {
58
- left = window.innerWidth - VIEWPORT_PADDING - tooltipRect.width;
59
- }
60
-
61
- const top =
62
- nextPlacement === 'down'
63
- ? triggerRect.bottom + TOOLTIP_GAP
64
- : triggerRect.top - tooltipRect.height - TOOLTIP_GAP;
65
-
66
- setPlacement(nextPlacement);
67
- setCoords({ top, left });
68
- setArrowOffset(triggerCenter - left);
69
- setPositioned(true);
70
- }, [tableBodyRef]);
71
-
72
- const showTooltip = () => {
73
- setPositioned(false);
74
- setVisible(true);
75
- };
76
-
77
- const hideTooltip = () => {
78
- setVisible(false);
79
- setPositioned(false);
80
21
  };
81
22
 
82
- useLayoutEffect(() => {
83
- if (!visible) {
84
- return;
85
- }
86
-
87
- updatePosition();
88
- const frame = window.requestAnimationFrame(updatePosition);
89
- return () => window.cancelAnimationFrame(frame);
90
- }, [visible, title, updatePosition]);
91
-
92
- useEffect(() => {
93
- if (!visible) {
94
- return;
95
- }
96
-
97
- const handleReposition = () => updatePosition();
98
- window.addEventListener('resize', handleReposition);
99
- window.addEventListener('scroll', handleReposition, true);
100
- return () => {
101
- window.removeEventListener('resize', handleReposition);
102
- window.removeEventListener('scroll', handleReposition, true);
103
- };
104
- }, [visible, updatePosition]);
105
-
106
- if (!title || enabled === false) {
107
- return <>{children}</>;
108
- }
109
-
110
- const portalRoot = typeof document !== 'undefined' ? document.body : null;
111
-
112
23
  return (
113
- <>
24
+ <div className={`qbs-table-tooltip ${dropdownPosition == 'bottom-position' ? 'down' : 'up'} `}>
114
25
  <span
115
- ref={triggerRef}
116
- className="qbs-table-tooltip-trigger"
117
- style={{ display: 'inline-flex' }}
118
- onMouseEnter={showTooltip}
119
- onMouseLeave={hideTooltip}
120
- onFocus={showTooltip}
121
- onBlur={hideTooltip}
26
+ ref={menuButtonRef}
27
+ style={{ display: 'flex' }}
28
+ onMouseEnter={() => adjustDropdownPosition()}
122
29
  >
123
30
  {children}
124
31
  </span>
125
- {visible &&
126
- portalRoot &&
127
- ReactDOM.createPortal(
128
- <span
129
- ref={tooltipRef}
130
- role="tooltip"
131
- className={`qbs-table-tooltip-floating tooltiptext qbs-table-tooltip-floating--${placement} ${
132
- positioned ? 'is-positioned' : ''
133
- }`}
134
- style={
135
- {
136
- top: coords.top,
137
- left: coords.left,
138
- '--tooltip-arrow-offset': `${arrowOffset}px`,
139
- } as React.CSSProperties
140
- }
141
- >
142
- {title}
143
- </span>,
144
- portalRoot,
145
- )}
146
- </>
32
+ <span ref={dropRef} className={'tooltiptext'}>
33
+ {title}
34
+ </span>
35
+ </div>
147
36
  );
148
37
  };
149
38
 
@@ -1,75 +1,119 @@
1
1
  import React, { useEffect, useRef, useState } from 'react';
2
2
  import ReactDOM from 'react-dom';
3
3
 
4
- import { ActionProps } from '../commontypes';
5
4
  import { ThreeDotIcon } from './icons';
6
5
  import TooltipComponent from './ToolTip';
6
+ import type { ActionProps } from '../commontypes';
7
7
 
8
- type Props = {
9
- actionDropDown: ActionProps[];
10
- handleMenuActions?: (slug: ActionProps, rowData?: any) => void;
11
- rowData?: any;
12
- dataTheme?: string;
13
- tableBodyRef: React.RefObject<HTMLDivElement | null>;
8
+ type VerticalMenuDropdownProps = {
9
+ actionDropDown?: readonly ActionProps[];
10
+ handleMenuActions?: (actions: ActionProps, rowData: any) => void;
11
+ rowData: any;
12
+ tableBodyRef?: React.RefObject<HTMLDivElement>;
14
13
  rowIndex?: number;
15
- wheelWrapperRef?: React.RefObject<HTMLDivElement>;
16
14
  };
17
15
 
18
- const VerticalMenuDropdown: React.FC<Props> = ({
16
+ const VerticalMenuDropdown: React.FC<VerticalMenuDropdownProps> = ({
19
17
  actionDropDown,
20
18
  handleMenuActions,
21
19
  rowData,
22
20
  tableBodyRef,
23
- rowIndex
21
+ rowIndex,
24
22
  }) => {
25
23
  const [openMenu, setOpenMenu] = useState(false);
26
24
  const [position, setPosition] = useState({ top: 0, left: 0 });
27
- const menuButtonRef = useRef<HTMLButtonElement | null>(null);
28
- const menuRef = useRef<HTMLDivElement | null>(null);
25
+ const menuButtonRef = useRef<HTMLButtonElement>(null);
26
+ const menuRef = useRef<HTMLDivElement>(null);
27
+
28
+ const updateMenuPosition = () => {
29
+ if (!menuButtonRef.current) return;
30
+
31
+ const rect = menuButtonRef.current.getBoundingClientRect();
32
+ const viewportPadding = 8;
33
+ const menuGap = 4;
34
+ const visibleItems =
35
+ actionDropDown?.filter(
36
+ item =>
37
+ !item.hidden && !(item.hide?.call(item, rowData, rowIndex) ?? false),
38
+ ) ?? [];
39
+ const menuWidth =
40
+ menuRef.current && menuRef.current.offsetWidth > 0
41
+ ? menuRef.current.offsetWidth
42
+ : Math.max(120, visibleItems.length * 48);
43
+ const menuHeight = visibleItems.length * 40;
44
+ const spaceBelow = window.innerHeight - rect.bottom;
45
+ const openBelow = spaceBelow >= menuHeight + menuGap;
46
+
47
+ let left = rect.left;
48
+ if (left + menuWidth > window.innerWidth - viewportPadding) {
49
+ left = Math.max(viewportPadding, rect.right - menuWidth);
50
+ }
51
+
52
+ setPosition({
53
+ top: openBelow ? rect.bottom + menuGap : rect.top - menuHeight - menuGap,
54
+ left,
55
+ });
56
+ };
57
+
58
+ useEffect(() => {
59
+ if (!openMenu) return;
60
+ updateMenuPosition();
61
+ const frame = requestAnimationFrame(() => updateMenuPosition());
62
+ const resizeObserver =
63
+ menuRef.current && typeof ResizeObserver !== 'undefined'
64
+ ? new ResizeObserver(() => updateMenuPosition())
65
+ : null;
66
+ if (resizeObserver && menuRef.current) {
67
+ resizeObserver.observe(menuRef.current);
68
+ }
69
+ return () => {
70
+ cancelAnimationFrame(frame);
71
+ resizeObserver?.disconnect();
72
+ };
73
+ }, [openMenu]);
29
74
 
30
75
  useEffect(() => {
76
+ if (!openMenu) return;
77
+
31
78
  const handleClickOutside = (event: MouseEvent) => {
32
- if (menuRef.current && !menuRef.current.contains(event.target as Node)) {
33
- setOpenMenu(false);
79
+ const target = event.target as Node;
80
+ if (
81
+ menuRef.current?.contains(target) ||
82
+ menuButtonRef.current?.contains(target)
83
+ ) {
84
+ return;
34
85
  }
35
- };
36
- const handleScroll = () => {
37
86
  setOpenMenu(false);
38
87
  };
88
+ const handleScroll = () => setOpenMenu(false);
39
89
 
40
90
  document.addEventListener('click', handleClickOutside);
41
91
  window.addEventListener('scroll', handleScroll, true);
42
- // Use capture phase to catch all scrolls
43
92
 
44
93
  return () => {
45
94
  document.removeEventListener('click', handleClickOutside);
46
95
  window.removeEventListener('scroll', handleScroll, true);
47
96
  };
48
- }, []);
49
- useEffect(() => {
50
- const handleStyleChange = () => {
51
- setOpenMenu(false); // Close the dropdown
52
- };
97
+ }, [openMenu]);
53
98
 
99
+ useEffect(() => {
54
100
  const scrollbarHandle = document.querySelector('.rs-table-scrollbar-handle');
55
101
  if (!scrollbarHandle) return;
56
102
 
57
103
  const observer = new MutationObserver(mutations => {
58
104
  for (const mutation of mutations) {
59
105
  if (mutation.type === 'attributes' && mutation.attributeName === 'style') {
60
- handleStyleChange();
106
+ setOpenMenu(false);
61
107
  }
62
108
  }
63
109
  });
64
110
 
65
111
  observer.observe(scrollbarHandle, {
66
112
  attributes: true,
67
- attributeFilter: ['style']
113
+ attributeFilter: ['style'],
68
114
  });
69
115
 
70
- return () => {
71
- observer.disconnect();
72
- };
116
+ return () => observer.disconnect();
73
117
  }, [openMenu]);
74
118
 
75
119
  const handleMenuItemClick = (slug: ActionProps) => {
@@ -78,63 +122,29 @@ const VerticalMenuDropdown: React.FC<Props> = ({
78
122
  setOpenMenu(false);
79
123
  };
80
124
 
81
- const handleShowHideMenu = () => {
82
- return (
83
- actionDropDown?.filter(item => !item.hidden && !item?.hide?.(rowData, rowIndex))?.length ?? 0
84
- );
85
- };
86
-
87
- const toggleMenu = () => {
88
- if (!openMenu && menuButtonRef.current) {
89
- const rect = menuButtonRef.current.getBoundingClientRect();
90
- const viewportPadding = 8;
91
- const menuGap = 4;
92
- const dropdownWidth = 200;
93
- const visibleItems =
94
- actionDropDown?.filter(item => !item.hidden && !item?.hide?.(rowData, rowIndex)) ?? [];
95
- const menuHeight = visibleItems.length * 40;
96
-
97
- const spaceBelow = window.innerHeight - rect.bottom;
98
- const openBelow = spaceBelow >= menuHeight + menuGap;
125
+ const visibleCount =
126
+ actionDropDown?.filter(
127
+ item => !item.hidden && !(item.hide?.call(item, rowData, rowIndex) ?? false),
128
+ ).length ?? 0;
99
129
 
100
- // Anchor to trigger; prefer opening toward inline-start (left in LTR).
101
- let left = rect.right - dropdownWidth;
102
-
103
- if (left < viewportPadding) {
104
- left = rect.left;
105
- }
106
- if (left + dropdownWidth > window.innerWidth - viewportPadding) {
107
- left = Math.max(viewportPadding, rect.left - dropdownWidth);
108
- }
109
- if (left + dropdownWidth > window.innerWidth - viewportPadding) {
110
- left = window.innerWidth - viewportPadding - dropdownWidth;
111
- }
112
-
113
- setPosition({
114
- top: openBelow ? rect.bottom + menuGap : rect.top - menuHeight - menuGap,
115
- left,
116
- });
117
- }
118
- setTimeout(() => {
119
- setOpenMenu(prev => !prev);
120
- }, 200);
121
- };
130
+ const portalTarget =
131
+ document.getElementById('portal-root') ?? document.body;
122
132
 
123
- const portalTarget = document.getElementById('portal-root');
124
133
  const dropdownContent = (
125
134
  <div
126
135
  className="absolute z-[60] min-w-48 rounded-md vertical-menu-dropdown-content"
127
136
  ref={menuRef}
128
137
  style={{
129
- width: 200,
130
138
  top: position.top,
131
139
  left: position.left,
132
140
  position: 'fixed',
141
+ minWidth: 120,
142
+ width: 'max-content',
133
143
  }}
134
144
  >
135
145
  <div className="py-1">
136
146
  {actionDropDown?.map(item =>
137
- !item?.hidden && !item?.hide?.(rowData, rowIndex) ? (
147
+ !item.hidden && !(item.hide?.call(item, rowData, rowIndex) ?? false) ? (
138
148
  <div
139
149
  key={item.title}
140
150
  className="vertical-menu-item px-4 py-2 text-sm text-base-black hover:bg-gray-light-1 cursor-pointer flex items-center gap-2 transition-colors"
@@ -146,12 +156,12 @@ const VerticalMenuDropdown: React.FC<Props> = ({
146
156
  >
147
157
  <TooltipComponent title={item.toolTip} tableBodyRef={tableBodyRef}>
148
158
  <div className="vertical-menu-icon-title flex items-center gap-2">
149
- {item?.icon && <span className="vertical-menu-icon">{item.icon}</span>}
159
+ {item.icon && <span className="vertical-menu-icon">{item.icon}</span>}
150
160
  <span className="vertical-menu-title">{item.title}</span>
151
161
  </div>
152
162
  </TooltipComponent>
153
163
  </div>
154
- ) : null
164
+ ) : null,
155
165
  )}
156
166
  </div>
157
167
  </div>
@@ -160,10 +170,15 @@ const VerticalMenuDropdown: React.FC<Props> = ({
160
170
  return (
161
171
  <>
162
172
  <div className="inline-block vertical-menu-dropdown-wrapper">
163
- {handleShowHideMenu() > 0 && (
173
+ {visibleCount > 0 && (
164
174
  <button
175
+ type="button"
165
176
  className="vertical-menu-trigger-button p-2 rounded text-base-gray hover:bg-gray-light-1 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary"
166
- onClick={toggleMenu}
177
+ onClick={event => {
178
+ event.stopPropagation();
179
+ if (!openMenu) updateMenuPosition();
180
+ setOpenMenu(prev => !prev);
181
+ }}
167
182
  ref={menuButtonRef}
168
183
  >
169
184
  <ThreeDotIcon />
@@ -18,7 +18,7 @@ const NoData: React.FC<props> = ({ title, subtitle }) => {
18
18
  }}
19
19
  >
20
20
  <div style={{ width: 145, marginBottom: 20 }} className="nodata-img">
21
- <svg
21
+ <svg
22
22
  xmlns="http://www.w3.org/2000/svg"
23
23
  width="148"
24
24
  height="132"
@@ -71,7 +71,7 @@ const NoData: React.FC<props> = ({ title, subtitle }) => {
71
71
  fill="#797979"
72
72
  d="M47.365 57.417a.926.926 0 00.926-.927v-1.777a.926.926 0 10-1.852 0v1.777c0 .512.415.927.926.927zM104.183 57.417a.926.926 0 00.926-.927v-1.777a.926.926 0 00-1.852 0v1.777c0 .512.414.927.926.927zM47.365 52.974a.926.926 0 00.926-.927V50.27a.926.926 0 00-1.852 0v1.777c0 .512.415.927.926.927zM104.183 52.974a.926.926 0 00.926-.927V50.27a.926.926 0 00-1.852 0v1.777c0 .512.414.927.926.927zM47.365 44.088a.926.926 0 00.926-.927v-1.777a.926.926 0 10-1.852 0v1.777c0 .512.415.927.926.927zM104.183 44.088a.926.926 0 00.926-.927v-1.777a.926.926 0 00-1.852 0v1.777c0 .512.414.927.926.927zM47.365 39.645a.926.926 0 00.926-.927v-1.777a.926.926 0 10-1.852 0v1.777c0 .512.415.927.926.927zM104.183 39.645a.926.926 0 00.926-.927v-1.777a.926.926 0 00-1.852 0v1.777c0 .512.414.927.926.927zM47.365 35.202a.926.926 0 00.926-.927v-1.777a.926.926 0 10-1.852 0v1.777c0 .512.415.927.926.927zM104.183 35.202a.926.926 0 00.926-.927v-1.777a.926.926 0 00-1.852 0v1.777c0 .512.414.927.926.927zM47.365 30.758a.926.926 0 00.926-.926v-.888c0-.248.02-.49.057-.726a.926.926 0 00-1.83-.288c-.052.33-.08.67-.08 1.014v.888c0 .512.416.926.927.926zM104.183 30.758a.926.926 0 00.926-.926v-.888c0-.345-.027-.683-.079-1.014a.926.926 0 10-1.83.288c.037.236.057.478.057.726v.888c0 .512.414.926.926.926zM47.881 26.426a.926.926 0 001.294-.204c.286-.394.633-.74 1.026-1.026a.926.926 0 00-1.09-1.498c-.55.4-1.034.884-1.434 1.434a.926.926 0 00.204 1.294zM103.667 26.426a.926.926 0 00.204-1.294 6.509 6.509 0 00-1.434-1.434.926.926 0 00-1.09 1.498c.393.286.74.632 1.026 1.026a.926.926 0 001.294.204zM51.138 23.597c.08.506.554.851 1.059.772a4.67 4.67 0 01.726-.057h.914a.926.926 0 000-1.852h-.914c-.344 0-.683.026-1.014.078a.926.926 0 00-.77 1.06zM100.41 23.597a.927.927 0 00-.771-1.059 6.522 6.522 0 00-1.014-.078h-.914a.926.926 0 100 1.852h.914c.248 0 .49.02.726.057a.926.926 0 001.059-.772zM55.653 23.386c0 .511.415.926.926.926h1.828a.926.926 0 000-1.852H56.58a.926.926 0 00-.926.926zM60.223 23.386c0 .511.415.926.926.926h1.828a.926.926 0 000-1.852H61.15a.926.926 0 00-.926.926zM64.793 23.386c0 .511.415.926.926.926h1.829a.926.926 0 000-1.852h-1.829a.926.926 0 00-.926.926zM69.364 23.386c0 .511.414.926.926.926h1.828a.926.926 0 000-1.852H70.29a.926.926 0 00-.927.926zM73.934 23.386c0 .511.414.926.926.926h1.828a.926.926 0 000-1.852H74.86a.926.926 0 00-.926.926zM78.504 23.386c0 .511.415.926.926.926h1.828a.926.926 0 000-1.852H79.43a.926.926 0 00-.926.926zM83.074 23.386c0 .511.415.926.927.926h1.828a.926.926 0 000-1.852H84a.926.926 0 00-.927.926zM87.644 23.386c0 .511.415.926.927.926h1.828a.926.926 0 000-1.852H88.57a.926.926 0 00-.927.926zM92.215 23.386c0 .511.414.926.926.926h1.828a.926.926 0 000-1.852h-1.828a.926.926 0 00-.926.926zM72.178 36.453a1.39 1.39 0 10-1.965 1.965l3.797 3.797-3.797 3.798a1.39 1.39 0 001.965 1.965l3.797-3.798 3.798 3.798a1.39 1.39 0 001.965-1.965l-3.798-3.798 3.798-3.797a1.39 1.39 0 10-1.965-1.965l-3.798 3.797-3.797-3.797z"
73
73
  ></path>
74
- </svg>{' '}
74
+ </svg> {' '}
75
75
  </div>
76
76
  <p className="text-center text-common font-bold text-blackAlt nodata-title">{title}</p>
77
77
  <span className="text-xxs font-medium text-grey-medium nodata-sub-title">{subtitle}</span>