qbs-react-grid 2.2.0 → 2.2.3

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 (33) 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 +2 -1
  5. package/es/less/pagination.less +9 -9
  6. package/es/less/qbs-table.less +117 -37
  7. package/es/qbsTable/QbsTable.js +6 -1
  8. package/es/qbsTable/commontypes.d.ts +1 -0
  9. package/es/qbsTable/utilities/CardMenuDropdown.js +1 -2
  10. package/es/qbsTable/utilities/ToolTip.d.ts +8 -1
  11. package/es/qbsTable/utilities/ToolTip.js +106 -76
  12. package/es/qbsTable/utilities/VerticalDropDownMenu.js +3 -3
  13. package/es/qbsTable/utilities/icons.js +1 -1
  14. package/lib/Cell.js +2 -1
  15. package/lib/less/pagination.less +9 -9
  16. package/lib/less/qbs-table.less +117 -37
  17. package/lib/qbsTable/QbsTable.js +6 -1
  18. package/lib/qbsTable/commontypes.d.ts +1 -0
  19. package/lib/qbsTable/utilities/CardMenuDropdown.js +1 -2
  20. package/lib/qbsTable/utilities/ToolTip.d.ts +8 -1
  21. package/lib/qbsTable/utilities/ToolTip.js +106 -76
  22. package/lib/qbsTable/utilities/VerticalDropDownMenu.js +3 -3
  23. package/lib/qbsTable/utilities/icons.js +1 -1
  24. package/package.json +1 -1
  25. package/src/Cell.tsx +3 -1
  26. package/src/less/pagination.less +9 -9
  27. package/src/less/qbs-table.less +117 -37
  28. package/src/qbsTable/QbsTable.tsx +9 -1
  29. package/src/qbsTable/commontypes.ts +1 -0
  30. package/src/qbsTable/utilities/CardMenuDropdown.tsx +1 -1
  31. package/src/qbsTable/utilities/ToolTip.tsx +138 -77
  32. package/src/qbsTable/utilities/VerticalDropDownMenu.tsx +3 -3
  33. package/src/qbsTable/utilities/icons.tsx +1 -1
@@ -202,6 +202,59 @@
202
202
  background-color: #262626;
203
203
  color: #f5f5f5;
204
204
  }
205
+
206
+ .qbs-table[data-theme='dark'] {
207
+ .rs-table,
208
+ .rs-table-body-row-wrapper,
209
+ .rs-table-row,
210
+ .rs-table-cell,
211
+ .rs-table-cell-group,
212
+ .rs-table-row-header,
213
+ .rs-table-row-header .rs-table-cell {
214
+ background-color: #262626;
215
+ color: #f5f5f5;
216
+ }
217
+
218
+ .rs-table-cell-content {
219
+ background-color: #262626;
220
+ color: #f5f5f5;
221
+ }
222
+
223
+ .rs-table-row-header .rs-table-cell-content,
224
+ .rs-table-cell-header .rs-table-cell-content {
225
+ background-color: #1b2028;
226
+ color: #f5f5f5;
227
+ }
228
+
229
+ .rs-table-hover .rs-table-body-row-wrapper .rs-table-row:hover,
230
+ .rs-table-hover .rs-table-body-row-wrapper .rs-table-row:hover .rs-table-cell,
231
+ .rs-table-hover .rs-table-body-row-wrapper .rs-table-row:hover .rs-table-cell-content {
232
+ background-color: #1d2633;
233
+ }
234
+
235
+ .rs-table-cell {
236
+ border-color: #3d3d3d;
237
+ }
238
+
239
+ .rs-table-scrollbar {
240
+ background-color: #2d2d2d;
241
+ }
242
+
243
+ .rs-table-scrollbar-handle {
244
+ background-color: #5a5a5a;
245
+ }
246
+ }
247
+
248
+ .qbs-table[dir='rtl'] {
249
+ .rs-table-cell-header .rs-table-cell-content {
250
+ text-align: right;
251
+ }
252
+
253
+ .rs-table-cell-wrap {
254
+ justify-content: flex-start;
255
+ width: 100%;
256
+ }
257
+ }
205
258
  /* Dropdown container */
206
259
  .qbs-table-menu-dropdown {
207
260
  position: relative;
@@ -334,71 +387,98 @@
334
387
  .qbs-table-tooltip {
335
388
  cursor: pointer;
336
389
  position: relative;
337
- width: auto;
338
- display: flex;
390
+ display: inline-flex;
339
391
  }
340
392
 
341
393
  .qbs-table-tooltip .tooltiptext {
342
394
  visibility: hidden;
343
- background-color: black;
344
- color: white;
395
+ background-color: var(--tooltip-bg, #222);
396
+ color: var(--tooltip-text, #fff);
345
397
  text-align: center;
346
- padding: 6px;
347
- border-radius: 4px;
398
+ padding: 6px 10px;
399
+ border-radius: 6px;
348
400
  position: absolute;
349
- z-index: 9999;
401
+ z-index: 10050;
350
402
  opacity: 0;
351
- transition: opacity 0.3s;
403
+ transition: opacity 0.15s ease;
352
404
  font-size: 12px;
353
- font-style: normal;
354
- font-weight: 400;
405
+ font-weight: 500;
355
406
  line-height: 16px;
356
- min-width: 100px;
357
- max-width: 200px;
407
+ white-space: nowrap;
408
+ max-width: min(280px, 90vw);
409
+ pointer-events: none;
410
+ inset-inline-start: 50%;
411
+ translate: -50% 0;
358
412
  }
359
413
 
360
414
  .qbs-table-tooltip.up .tooltiptext {
361
- bottom: calc(100% + 10px);
362
- right: -8px;
363
- left: unset;
415
+ bottom: calc(100% + 8px);
416
+ top: auto;
364
417
  }
365
418
 
366
419
  .qbs-table-tooltip.down .tooltiptext {
367
- right: -10px;
368
420
  top: calc(100% + 8px);
421
+ bottom: auto;
369
422
  }
370
423
 
371
- // .qbs-table-tooltip:hover .tooltiptext {
372
- // visibility: visible;
373
- // opacity: 1;
374
- // }
375
-
376
424
  .qbs-table-tooltip .tooltiptext::after {
377
425
  content: '';
378
426
  position: absolute;
379
- border-width: 5px;
380
- border-style: solid;
427
+ inset-inline-start: 50%;
428
+ translate: -50% 0;
429
+ border: 5px solid transparent;
381
430
  }
382
431
 
383
432
  .qbs-table-tooltip.up .tooltiptext::after {
384
- border-color: black transparent transparent !important;
385
- right: 12px !important;
386
- margin-left: -5px !important;
387
- top: 100% !important;
388
- left: unset !important;
433
+ top: 100%;
434
+ border-top-color: var(--tooltip-bg, #222);
389
435
  }
390
436
 
391
437
  .qbs-table-tooltip.down .tooltiptext::after {
392
- border-color: transparent transparent black;
393
- bottom: 100% !important;
394
- right: 12px !important;
395
- margin-left: -5px !important;
396
- left: unset !important;
438
+ bottom: 100%;
439
+ border-bottom-color: var(--tooltip-bg, #222);
397
440
  }
398
- .qbs-table-tooltip.down .tooltiptext {
399
- top: 145% !important;
400
- right: -10px !important;
401
- left: auto !important;
441
+
442
+ .qbs-table-tooltip-floating.tooltiptext {
443
+ position: fixed;
444
+ z-index: 10050;
445
+ visibility: hidden;
446
+ opacity: 0;
447
+ pointer-events: none;
448
+ background-color: var(--tooltip-bg, #222);
449
+ color: var(--tooltip-text, #fff);
450
+ text-align: center;
451
+ padding: 6px 10px;
452
+ border-radius: 6px;
453
+ font-size: 12px;
454
+ font-weight: 500;
455
+ line-height: 16px;
456
+ white-space: nowrap;
457
+ max-width: min(280px, 90vw);
458
+ transition: opacity 0.15s ease;
459
+ }
460
+
461
+ .qbs-table-tooltip-floating.tooltiptext.is-positioned {
462
+ visibility: visible;
463
+ opacity: 1;
464
+ }
465
+
466
+ .qbs-table-tooltip-floating.tooltiptext::after {
467
+ content: '';
468
+ position: absolute;
469
+ left: var(--tooltip-arrow-offset, 50%);
470
+ translate: -50% 0;
471
+ border: 5px solid transparent;
472
+ }
473
+
474
+ .qbs-table-tooltip-floating--down.tooltiptext::after {
475
+ bottom: 100%;
476
+ border-bottom-color: var(--tooltip-bg, #222);
477
+ }
478
+
479
+ .qbs-table-tooltip-floating--up.tooltiptext::after {
480
+ top: 100%;
481
+ border-top-color: var(--tooltip-bg, #222);
402
482
  }
403
483
  .rs-table-row {
404
484
  overflow: visible !important;
@@ -6,6 +6,7 @@ import ColumnGroup from '../ColumnGroup';
6
6
  import HeaderCell from '../HeaderCell';
7
7
  import Pagination from '../Pagination';
8
8
  import Table from '../Table';
9
+ import isRTL from '../utils/isRTL';
9
10
  import useResponsiveStore from '../utils/useResponsiveStore';
10
11
  import { QbsColumnProps, QbsTableProps } from './commontypes';
11
12
  import { mergeLabels } from './labels';
@@ -61,6 +62,7 @@ const QbsTable: React.FC<QbsTableProps> = ({
61
62
  rowExpand = false,
62
63
  actionProps = [],
63
64
  theme,
65
+ rtl: rtlProp,
64
66
  handleMenuActions,
65
67
  onRowClick,
66
68
  expandedRowKeys,
@@ -118,6 +120,7 @@ const QbsTable: React.FC<QbsTableProps> = ({
118
120
  () => theme ?? (typeof localStorage !== 'undefined' ? localStorage.getItem('theme') : null) ?? 'light',
119
121
  [theme]
120
122
  );
123
+ const rtl = rtlProp ?? isRTL();
121
124
  const [isOpen, setIsOpen] = useState(false);
122
125
  const prevColumns = useRef<any | null>(null);
123
126
  const [tableViewToggle, setTableViewToggle] = useState(tableView);
@@ -629,7 +632,11 @@ const QbsTable: React.FC<QbsTableProps> = ({
629
632
  };
630
633
 
631
634
  return (
632
- <div className={`qbs-table ${classes.tableContainerClass}`} data-theme={dataTheme}>
635
+ <div
636
+ className={`qbs-table ${classes.tableContainerClass}`}
637
+ data-theme={dataTheme}
638
+ dir={rtl ? 'rtl' : 'ltr'}
639
+ >
633
640
  {toolbar && <ToolBar {...toolbarProps} />}
634
641
  <div className="qbs-table-border-wrap">
635
642
  {tableViewToggle ? (
@@ -638,6 +645,7 @@ const QbsTable: React.FC<QbsTableProps> = ({
638
645
  key={tableKey + REFRESH_KEY}
639
646
  tableKey={tableKey}
640
647
  data={data}
648
+ rtl={rtl}
641
649
  tableBodyRef={tableBodyRef as React.RefObject<HTMLDivElement>}
642
650
  dataTheme={dataTheme}
643
651
  wordWrap={wordWrap}
@@ -78,6 +78,7 @@ export interface QbsTableProps {
78
78
  searchValue?: string;
79
79
  handleSearchValue?: (value?: string) => void;
80
80
  theme?: string;
81
+ rtl?: boolean;
81
82
  onRowClick?: (data: any) => void;
82
83
  cellBordered?: boolean;
83
84
  bordered?: boolean;
@@ -81,7 +81,7 @@ const CardMenuDropdown: React.FC<Props> = ({
81
81
  return (
82
82
  <div className="dropdown text-black dark:text-white dark:bg-[#424242] bg-white" ref={menuRef}>
83
83
  <button className="dropdown-toggle" onClick={toggleMenu} ref={menuButtonRef}>
84
- <TooltipComponent title={labels.actions} enabled={false} ref={menuButtonRef}>
84
+ <TooltipComponent title={labels.actions} enabled={false}>
85
85
  <ThreeDotIcon />
86
86
  </TooltipComponent>
87
87
  </button>
@@ -1,88 +1,149 @@
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
- // }
20
- // }
21
- // };
22
-
23
- // return (
24
- // <div className={`qbs-table-tooltip ${dropdownPosition == 'bottom-position' ? 'down' : 'up'} `}>
25
- // <span
26
- // ref={menuButtonRef}
27
- // style={{ display: 'flex' }}
28
- // onMouseEnter={() => adjustDropdownPosition()}
29
- // >
30
- // {children}
31
- // </span>
32
- // <span ref={dropRef} className={'tooltiptext'}>
33
- // {title}
34
- // </span>
35
- // </div>
36
- // );
37
- // };
38
-
39
- // export default TooltipComponent;
40
- import React, { useRef, useState } from 'react';
41
-
42
- const TooltipComponent: React.FC<any> = ({ title, children, tableBodyRef }) => {
43
- const [dropdownPosition, setDropdownPosition] = useState<'up' | 'down'>('down');
44
- const menuButtonRef = useRef<HTMLElement>(null);
45
-
46
- const adjustDropdownPosition = () => {
47
- if (menuButtonRef.current && tableBodyRef?.current) {
48
- const triggerRect = menuButtonRef.current.getBoundingClientRect();
49
- const tableRect = tableBodyRef.current.getBoundingClientRect();
50
-
51
- const spaceAbove = triggerRect.top - tableRect.top;
52
- const spaceBelow = tableRect.bottom - triggerRect.bottom;
53
-
54
- setDropdownPosition(spaceAbove > spaceBelow ? 'up' : 'down');
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;
34
+ }
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;
55
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);
56
80
  };
57
81
 
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
+
58
112
  return (
59
- <div
60
- className={`qbs-table-tooltip ${dropdownPosition}`}
61
- onMouseEnter={adjustDropdownPosition}
62
- onMouseLeave={() => {
63
- const tooltip = menuButtonRef?.current?.querySelector('.tooltiptext') as HTMLElement;
64
- if (tooltip) {
65
- tooltip.style.visibility = 'hidden';
66
- tooltip.style.opacity = '0';
67
- }
68
- }}
69
- >
113
+ <>
70
114
  <span
71
- ref={menuButtonRef}
72
- style={{ display: 'flex' }}
73
- onMouseEnter={() => {
74
- adjustDropdownPosition();
75
- const tooltip = menuButtonRef?.current?.querySelector('.tooltiptext') as HTMLElement;
76
- if (tooltip) {
77
- tooltip.style.visibility = 'visible';
78
- tooltip.style.opacity = '1';
79
- }
80
- }}
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}
81
122
  >
82
123
  {children}
83
- <span className="tooltiptext">{title}</span>
84
124
  </span>
85
- </div>
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
+ </>
86
147
  );
87
148
  };
88
149
 
@@ -132,7 +132,7 @@ const VerticalMenuDropdown: React.FC<Props> = ({
132
132
  const portalTarget = document.getElementById('portal-root');
133
133
  const dropdownContent = (
134
134
  <div
135
- className="absolute z-50 min-w-48 rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 vertical-menu-dropdown-content"
135
+ className="absolute z-[60] min-w-48 rounded-md vertical-menu-dropdown-content"
136
136
  ref={menuRef}
137
137
  style={{
138
138
  width: 200,
@@ -146,7 +146,7 @@ const VerticalMenuDropdown: React.FC<Props> = ({
146
146
  !item?.hidden && !item?.hide?.(rowData, rowIndex) ? (
147
147
  <div
148
148
  key={item.title}
149
- className="vertical-menu-item px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 cursor-pointer flex items-center gap-2 transition-colors"
149
+ 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"
150
150
  onClick={e => {
151
151
  e.preventDefault();
152
152
  item.action?.(item);
@@ -171,7 +171,7 @@ const VerticalMenuDropdown: React.FC<Props> = ({
171
171
  <div className="inline-block vertical-menu-dropdown-wrapper">
172
172
  {handleShowHideMenu() > 0 && (
173
173
  <button
174
- className="vertical-menu-trigger-button p-2 rounded hover:bg-gray-100 transition-colors"
174
+ 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"
175
175
  onClick={toggleMenu}
176
176
  ref={menuButtonRef}
177
177
  >
@@ -5,7 +5,7 @@ export const ThreeDotIcon: React.FC<any> = () => {
5
5
  <svg width="4" height="16" viewBox="0 0 4 16" fill="none" xmlns="http://www.w3.org/2000/svg">
6
6
  <path
7
7
  d="M2 2.16665L2 2.17498M2 7.99998L2 8.00831M2 13.8333L2 13.8416M2 2.99998C1.53976 2.99998 1.16667 2.62688 1.16667 2.16665C1.16667 1.70641 1.53976 1.33331 2 1.33331C2.46024 1.33331 2.83333 1.70641 2.83333 2.16665C2.83333 2.62688 2.46024 2.99998 2 2.99998ZM2 8.83331C1.53976 8.83331 1.16667 8.46022 1.16667 7.99998C1.16667 7.53974 1.53976 7.16665 2 7.16665C2.46024 7.16665 2.83333 7.53974 2.83333 7.99998C2.83333 8.46022 2.46024 8.83331 2 8.83331ZM2 14.6666C1.53976 14.6666 1.16666 14.2935 1.16666 13.8333C1.16666 13.3731 1.53976 13 2 13C2.46024 13 2.83333 13.3731 2.83333 13.8333C2.83333 14.2935 2.46024 14.6666 2 14.6666Z"
8
- stroke="#313131"
8
+ stroke="currentColor"
9
9
  strokeWidth="1.5"
10
10
  strokeLinecap="round"
11
11
  strokeLinejoin="round"