@topconsultnpm/sdkui-react 6.22.0-dev4.7 → 6.22.0-dev4.9

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.
@@ -3,7 +3,7 @@ import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useSta
3
3
  import styled from 'styled-components';
4
4
  import { LoadIndicator } from 'devextreme-react';
5
5
  import { AppModules, DataColumnTypes, DataListViewModes, DcmtTypeListCacheService, LayoutCacheService, LayoutModes, MetadataDataDomains, SDK_Globals, SystemMIDsAsNumber, UserListCacheService, } from '@topconsultnpm/sdk-ts';
6
- import { customizeSearchByDisplayText, deepCompare, formatDateTimeByMetadataFormat, formatNumberByMetadataFormat, generateUniqueColumnKeys, genUniqueId, getColumnFormatInfo, getDateDisplayText, getNumberDisplayText, getSearchToolbarVisibility, IconAll, IconBoard, IconDcmtTypeSys, IconDelete, IconDuplicate, IconEdit, IconMenuVertical, IconPlatform, IconRefresh, IconSearchCheck, IconShow, isApprovalWorkflowView, isSign4TopEnabled, searchResultDescriptorToSimpleArray, searchResultToMetadataValues, SDKUI_Globals, SDKUI_Localizator, IconCloseOutline, getSearchResultPanelLayout, getSearchResultPanelLayoutSignature, hasSearchResultPanelLayout, saveSearchResultPanelLayout, subscribeSearchResultPanelLayout, VIRTUAL_RENDERING_THRESHOLD } from '../../../helper';
6
+ import { customizeSearchByDisplayText, deepCompare, formatDateTimeByMetadataFormat, formatNumberByMetadataFormat, generateUniqueColumnKeys, genUniqueId, getColumnFormatInfo, getDateDisplayText, getNumberDisplayText, getSearchToolbarVisibility, IconAll, IconBoard, IconDcmtTypeSys, IconDelete, IconDuplicate, IconEdit, IconMenuVertical, IconPlatform, IconRefresh, IconSearchCheck, IconShow, isApprovalWorkflowView, isSign4TopEnabled, searchResultDescriptorToSimpleArray, searchResultToMetadataValues, SDKUI_Globals, SDKUI_Localizator, IconCloseOutline, getSearchResultPanelLayout, getSearchResultPanelLayoutSignature, hasSearchResultPanelLayout, saveSearchResultPanelLayout, subscribeSearchResultPanelLayout, VIRTUAL_RENDERING_THRESHOLD, VIRTUAL_RENDERING_CELL_THRESHOLD } from '../../../helper';
7
7
  import { CounterItemKey, formatCount } from '../../base/TMCounterContainer';
8
8
  import { getDcmtCicoStatus } from '../../../helper/checkinCheckoutManager';
9
9
  import { DcmtOperationTypes, SearchResultContext, } from '../../../ts';
@@ -1403,14 +1403,16 @@ const TMSearchResultGrid = ({ openInOffice, fromDTD, operationItems, allUsers, i
1403
1403
  const gridSorting = useMemo(() => ({ mode: 'multiple' }), []);
1404
1404
  const gridSelection = useMemo(() => ({ mode: allowMultipleSelection ? 'multiple' : 'single' }), [allowMultipleSelection]);
1405
1405
  const useNativeScrollbar = SDKUI_Globals.userSettings?.themeSettings.gridSettings.useNativeScrollbar === 1;
1406
- // Con pagine grandi (oltre VIRTUAL_RENDERING_THRESHOLD righe) il rendering virtuale delle righe evita di creare nel DOM tutte le righe della pagina.
1407
- // Conta le righe effettive della pagina, non pageSize: con pochi risultati la griglia resta in rendering standard.
1406
+ // Rendering virtuale delle righe oltre VIRTUAL_RENDERING_THRESHOLD righe o VIRTUAL_RENDERING_CELL_THRESHOLD celle (righe effettive × colonne)
1408
1407
  const pageRowCount = Math.min(pageSize, dataSource?.length ?? 0);
1408
+ // +1 per la colonna icona (FILEEXT) sempre presente
1409
+ const visibleColumnCount = columns.filter(col => col.visible !== false && col.dataField !== 'FILEEXT').length + 1;
1410
+ const useVirtualRows = pageRowCount > VIRTUAL_RENDERING_THRESHOLD || pageRowCount * visibleColumnCount > VIRTUAL_RENDERING_CELL_THRESHOLD;
1409
1411
  const gridScrolling = useMemo(() => ({
1410
1412
  columnRenderingMode: 'virtual',
1411
- rowRenderingMode: pageRowCount > VIRTUAL_RENDERING_THRESHOLD ? 'virtual' : 'standard',
1413
+ rowRenderingMode: useVirtualRows ? 'virtual' : 'standard',
1412
1414
  useNative: useNativeScrollbar
1413
- }), [useNativeScrollbar, pageRowCount]);
1415
+ }), [useNativeScrollbar, useVirtualRows]);
1414
1416
  // Handles selection change in the data grid
1415
1417
  const handleSelectionChange = useCallback((e) => {
1416
1418
  if (onSelectionChanged === undefined)
@@ -1421,8 +1423,14 @@ const TMSearchResultGrid = ({ openInOffice, fromDTD, operationItems, allUsers, i
1421
1423
  // Handles focus change in the data grid
1422
1424
  const handleFocusedRowChange = useCallback((e) => {
1423
1425
  // Non bloccare mai questo handler: è chiamato quando l'utente clicca su una riga
1424
- onFocusedItemChanged?.(e.row?.data);
1425
- }, [onFocusedItemChanged]);
1426
+ // Con il rendering virtuale la riga su cui il cambio pagina sposta il fuoco può non essere ancora disegnata:
1427
+ // e.row è undefined e il documento si ricava dalla chiave (altrimenti focusedRowKey ricadrebbe su 0, cioè pagina 1)
1428
+ const rowData = e.row?.data ?? dataSource?.find(item => item.rowIndex === e.component.option('focusedRowKey'));
1429
+ // La prop controllata va allineata subito: finché resta sulla riga precedente, il wrapper React
1430
+ // riporta lì il fuoco e autoNavigateToFocusedRow riporta la griglia alla sua pagina
1431
+ setFocusedItem(rowData);
1432
+ onFocusedItemChanged?.(rowData);
1433
+ }, [onFocusedItemChanged, dataSource]);
1426
1434
  // Handler for double-click row event
1427
1435
  const onRowDblClick = useCallback((e) => {
1428
1436
  if (onDblClick === undefined)
@@ -1536,9 +1544,9 @@ const TMSearchResultGrid = ({ openInOffice, fromDTD, operationItems, allUsers, i
1536
1544
  return _jsxs("div", { style: { width: "100%", height: "100%" }, children: [!isDataGridReady && (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', height: '100%', width: '100%', gap: '10px' }, children: [_jsx(LoadIndicator, { height: 60, width: 60 }), _jsx("div", { children: SDKUI_Localizator.Loading })] })), isDataGridReady && _jsx(TMDataGrid, { ref: dataGridRef, id: "tm-search-result", keyExpr: "rowIndex", dataColumns: dataColumns, dataSource: dataSource, customizeColumns: customizeColumns, repaintChangesOnly: true, selectedRowKeys: selectedRowKeys, focusedRowKey: disableAutoFocus ? undefined : Number(focusedItem?.rowIndex ?? 0), showSearchPanel: showSearchTMDatagrid, showFilterPanel: true, sorting: gridSorting, selection: gridSelection, scrolling: gridScrolling, pageSize: pageSize, compactPager: compactPager, onSelectionChanged: handleSelectionChange, onFocusedRowChanged: handleFocusedRowChange, onRowDblClick: onRowDblClick, onContentReady: onContentReady, showHeaderColumnChooser: true, onKeyDown: onKeyDown, customContextMenuItems: operationItems, counterConfig: gridCounterConfig })] });
1537
1545
  };
1538
1546
  //#region TMSearchResultSelector
1539
- const StyledItemTemplate = styled.div `
1540
- background: ${(props) => props.$isSelected ? 'oklch(from var(--dx-color-primary) l c h / .2) !important' : 'transparent'};
1541
- cursor: pointer;
1547
+ const StyledItemTemplate = styled.div `
1548
+ background: ${(props) => props.$isSelected ? 'oklch(from var(--dx-color-primary) l c h / .2) !important' : 'transparent'};
1549
+ cursor: pointer;
1542
1550
  `;
1543
1551
  const MemoizedStyledItemTemplate = React.memo(StyledItemTemplate);
1544
1552
  const TMSearchResultSelector = ({ searchResults = [], disableAccordionIfSingleCategory = false, selectedTID, selectedSearchResult, autoSelectFirst = true, onSelectionChanged }) => {
@@ -1745,85 +1753,85 @@ const TMDcmtPreviewWrapper = ({ refreshPreviewTrigger, currentDcmt, isVisible, i
1745
1753
  return (_jsx(TMDcmtPreview, { dcmtData: currentDcmt, onClosePanel: (!isMobile && countVisibleLeafPanels() > 1) ? () => setPanelVisibilityById('tmDcmtPreview', false) : undefined, onBack: onBack, allowMaximize: !isMobile && countVisibleLeafPanels() > 1, onMaximizePanel: (!isMobile && countVisibleLeafPanels() > 1) ? () => toggleMaximize("tmDcmtPreview") : undefined, isResizingActive: isResizingActive, isVisible: isVisible && isPageVisible }, refreshPreviewTrigger));
1746
1754
  };
1747
1755
  // Styled Components
1748
- const StyledPlaceholder = styled.div `
1749
- padding: 20px;
1750
- text-align: center;
1751
- color: #888;
1756
+ const StyledPlaceholder = styled.div `
1757
+ padding: 20px;
1758
+ text-align: center;
1759
+ color: #888;
1752
1760
  `;
1753
- const StyledIndexingInfoSection = styled.div `
1754
- padding: 15px;
1755
- border-top: 1px solid #e0e0e0;
1756
- background: linear-gradient(to bottom, #f9f9f9, #f5f5f5);
1757
- display: flex;
1758
- flex-direction: column;
1759
- gap: 10px;
1761
+ const StyledIndexingInfoSection = styled.div `
1762
+ padding: 15px;
1763
+ border-top: 1px solid #e0e0e0;
1764
+ background: linear-gradient(to bottom, #f9f9f9, #f5f5f5);
1765
+ display: flex;
1766
+ flex-direction: column;
1767
+ gap: 10px;
1760
1768
  `;
1761
- const StyledIndexingToggle = styled.button `
1762
- display: flex;
1763
- align-items: center;
1764
- justify-content: space-between;
1765
- width: 100%;
1766
- padding: 10px 16px;
1767
- background: white;
1768
- border: 1px solid #d0d0d0;
1769
- border-radius: 6px;
1770
- cursor: ${props => props.disabled ? 'not-allowed' : 'pointer'};
1771
- transition: all 0.2s ease;
1772
- font-size: 14px;
1773
- font-weight: 500;
1774
- color: #333;
1775
- opacity: ${props => props.disabled ? 0.6 : 1};
1776
- box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
1777
-
1778
- &:hover:not(:disabled) {
1779
- background: #f8f8f8;
1780
- border-color: #2196F3;
1781
- box-shadow: 0 2px 4px rgba(33, 150, 243, 0.2);
1782
- }
1783
-
1784
- &:active:not(:disabled) {
1785
- transform: translateY(1px);
1786
- box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
1787
- }
1769
+ const StyledIndexingToggle = styled.button `
1770
+ display: flex;
1771
+ align-items: center;
1772
+ justify-content: space-between;
1773
+ width: 100%;
1774
+ padding: 10px 16px;
1775
+ background: white;
1776
+ border: 1px solid #d0d0d0;
1777
+ border-radius: 6px;
1778
+ cursor: ${props => props.disabled ? 'not-allowed' : 'pointer'};
1779
+ transition: all 0.2s ease;
1780
+ font-size: 14px;
1781
+ font-weight: 500;
1782
+ color: #333;
1783
+ opacity: ${props => props.disabled ? 0.6 : 1};
1784
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
1785
+
1786
+ &:hover:not(:disabled) {
1787
+ background: #f8f8f8;
1788
+ border-color: #2196F3;
1789
+ box-shadow: 0 2px 4px rgba(33, 150, 243, 0.2);
1790
+ }
1791
+
1792
+ &:active:not(:disabled) {
1793
+ transform: translateY(1px);
1794
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
1795
+ }
1788
1796
  `;
1789
- const StyledLeftContent = styled.div `
1790
- display: flex;
1791
- align-items: center;
1792
- gap: 8px;
1797
+ const StyledLeftContent = styled.div `
1798
+ display: flex;
1799
+ align-items: center;
1800
+ gap: 8px;
1793
1801
  `;
1794
- const StyledRightContent = styled.div `
1795
- display: flex;
1796
- align-items: center;
1797
- gap: 8px;
1802
+ const StyledRightContent = styled.div `
1803
+ display: flex;
1804
+ align-items: center;
1805
+ gap: 8px;
1798
1806
  `;
1799
- const StyledChevron = styled.span `
1800
- transition: transform 0.2s ease;
1801
- transform: ${props => props.$isOpen ? 'rotate(180deg)' : 'rotate(0deg)'};
1802
- color: #666;
1803
- font-size: 12px;
1807
+ const StyledChevron = styled.span `
1808
+ transition: transform 0.2s ease;
1809
+ transform: ${props => props.$isOpen ? 'rotate(180deg)' : 'rotate(0deg)'};
1810
+ color: #666;
1811
+ font-size: 12px;
1804
1812
  `;
1805
- const StyledIndexingInfoBox = styled.div `
1806
- position: relative;
1807
- background: white;
1808
- border: 1px solid #e0e0e0;
1809
- border-radius: 6px;
1810
- padding: 12px;
1811
- max-height: 200px;
1812
- overflow: auto;
1813
- font-family: 'Courier New', monospace;
1814
- font-size: 12px;
1815
- color: #333;
1816
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
1817
- animation: slideDown 0.2s ease;
1818
-
1819
- @keyframes slideDown {
1820
- from {
1821
- opacity: 0;
1822
- transform: translateY(-10px);
1823
- }
1824
- to {
1825
- opacity: 1;
1826
- transform: translateY(0);
1827
- }
1828
- }
1813
+ const StyledIndexingInfoBox = styled.div `
1814
+ position: relative;
1815
+ background: white;
1816
+ border: 1px solid #e0e0e0;
1817
+ border-radius: 6px;
1818
+ padding: 12px;
1819
+ max-height: 200px;
1820
+ overflow: auto;
1821
+ font-family: 'Courier New', monospace;
1822
+ font-size: 12px;
1823
+ color: #333;
1824
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
1825
+ animation: slideDown 0.2s ease;
1826
+
1827
+ @keyframes slideDown {
1828
+ from {
1829
+ opacity: 0;
1830
+ transform: translateY(-10px);
1831
+ }
1832
+ to {
1833
+ opacity: 1;
1834
+ transform: translateY(0);
1835
+ }
1836
+ }
1829
1837
  `;
@@ -69,6 +69,9 @@ export declare const DEFAULT_PREVIEW_THRESHOLD = 500;
69
69
  export declare const DEFAULT_PAGE_SIZE = 200;
70
70
  /** Oltre questo numero di righe per pagina la griglia dei risultati usa il rendering virtuale delle righe */
71
71
  export declare const VIRTUAL_RENDERING_THRESHOLD = 500;
72
+ /** Oltre questo numero di celle per pagina (righe × colonne visibili) la griglia dei risultati usa il rendering virtuale delle righe:
73
+ * il costo di rendering, selezione e repaint cresce con le celle, non solo con le righe */
74
+ export declare const VIRTUAL_RENDERING_CELL_THRESHOLD = 3000;
72
75
  export declare const DEFAULT_MAX_DCMTS_TO_BE_RETURNED = 1000;
73
76
  export declare const DEFAULT_QUICK_SEARCH_MAX_DCMTS = 1000;
74
77
  /** Ricerca rapida configurata come sorgente alternativa dei valori distinti di un campo */
@@ -132,6 +132,9 @@ export const DEFAULT_PREVIEW_THRESHOLD = 500; // KB
132
132
  export const DEFAULT_PAGE_SIZE = 200;
133
133
  /** Oltre questo numero di righe per pagina la griglia dei risultati usa il rendering virtuale delle righe */
134
134
  export const VIRTUAL_RENDERING_THRESHOLD = 500;
135
+ /** Oltre questo numero di celle per pagina (righe × colonne visibili) la griglia dei risultati usa il rendering virtuale delle righe:
136
+ * il costo di rendering, selezione e repaint cresce con le celle, non solo con le righe */
137
+ export const VIRTUAL_RENDERING_CELL_THRESHOLD = 3000;
135
138
  export const DEFAULT_MAX_DCMTS_TO_BE_RETURNED = 1000;
136
139
  export const DEFAULT_QUICK_SEARCH_MAX_DCMTS = 1000;
137
140
  /** Ricerca rapida configurata come sorgente alternativa dei valori distinti di un campo */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@topconsultnpm/sdkui-react",
3
- "version": "6.22.0-dev4.7",
3
+ "version": "6.22.0-dev4.9",
4
4
  "description": "",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" \u0026\u0026 exit 1",