@topconsultnpm/sdkui-react-beta 6.13.75 → 6.13.77

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.
@@ -50,6 +50,7 @@ const TMFileManager = (props) => {
50
50
  return;
51
51
  event.preventDefault();
52
52
  setTreeViewAnchor(event.currentTarget);
53
+ handleFocusedFolder?.(undefined);
53
54
  };
54
55
  // Handle closing the context menu
55
56
  const closeTreeViewContextMenu = useCallback(() => {
@@ -2,6 +2,7 @@ import React from 'react';
2
2
  interface ITMDcmtBlogProps {
3
3
  tid: number | undefined;
4
4
  did: number | undefined;
5
+ isVisible?: boolean;
5
6
  }
6
7
  declare const TMDcmtBlog: React.FC<ITMDcmtBlogProps>;
7
8
  export default TMDcmtBlog;
@@ -7,27 +7,42 @@ import TMSpinner from '../../base/TMSpinner';
7
7
  import TMBlogs from '../../grids/TMBlogs';
8
8
  import { TMNothingToShow } from './TMDcmtPreview';
9
9
  import { IconBoard } from '../../../helper';
10
- const TMDcmtBlog = ({ tid, did }) => {
10
+ const TMDcmtBlog = ({ tid, did, isVisible }) => {
11
11
  const [blogsDatasource, setBlogsDatasource] = useState([]);
12
+ const [hasLoadedDataOnce, setHasLoadedDataOnce] = useState(false); //traccia se *qualsiasi* dato è stato caricato per la prima volta
13
+ const [lastLoadedDid, setLastLoadedDid] = useState(undefined); // `lastLoadedDid` tiene traccia dell'ultimo `did` per cui abbiamo caricato i dati
12
14
  useEffect(() => {
13
- if (!tid || !did)
15
+ if (!tid || !did) {
16
+ setBlogsDatasource([]);
17
+ setLastLoadedDid(undefined); // Reset per consentire un nuovo caricamento quando tid/did diventano validi
14
18
  return;
15
- loadDataAsync(tid, did);
16
- }, [did]);
17
- const loadDataAsync = async (tid, did) => {
18
- try {
19
- TMSpinner.show({ description: 'Caricamento - Bacheca...' });
20
- let res = await SDK_Globals.tmSession?.NewSearchEngine().BlogRetrieveAsync(tid, did);
21
- setBlogsDatasource(res ?? []);
22
19
  }
23
- catch (e) {
24
- let err = e;
25
- TMExceptionBoxManager.show({ exception: err });
20
+ // Condizione per eseguire il fetch:
21
+ // 1. Il pannello è visibile
22
+ // 2. E (non abbiamo ancora caricato dati O il `did` è cambiato rispetto all'ultima volta)
23
+ const shouldFetch = isVisible && (!hasLoadedDataOnce || did !== lastLoadedDid);
24
+ // Esegui la chiamata API solo se il pannello è visibile E i dati non sono già stati caricati
25
+ // O, se vuoi ricaricare ogni volta che diventa visibile (ma è meno efficiente per "pesante")
26
+ if (shouldFetch) {
27
+ const fetchDataAsync = async (tid, did) => {
28
+ try {
29
+ TMSpinner.show({ description: 'Caricamento - Bacheca...' });
30
+ let res = await SDK_Globals.tmSession?.NewSearchEngine().BlogRetrieveAsync(tid, did);
31
+ setBlogsDatasource(res ?? []);
32
+ setHasLoadedDataOnce(true); // Marca che abbiamo caricato dati almeno una volta
33
+ setLastLoadedDid(did); // Memorizza il `did` per cui abbiamo caricato
34
+ }
35
+ catch (e) {
36
+ let err = e;
37
+ TMExceptionBoxManager.show({ exception: err });
38
+ }
39
+ finally {
40
+ TMSpinner.hide();
41
+ }
42
+ };
43
+ fetchDataAsync(tid, did);
26
44
  }
27
- finally {
28
- TMSpinner.hide();
29
- }
30
- };
45
+ }, [tid, did, isVisible, hasLoadedDataOnce, lastLoadedDid]);
31
46
  return (_jsx(StyledContainer, { children: _jsx(StyledSectionContainer, { style: { position: 'relative' }, children: _jsx(StyledBoardContainer, { children: !did ? _jsx(TMNothingToShow, { text: 'Nessun documento selezionato.', secondText: 'Bacheca non disponibile.', icon: _jsx(IconBoard, { fontSize: 96 }) }) :
32
47
  _jsx(TMBlogs, { id: "dcmt-blog", allData: blogsDatasource, showExtendedAttachments: false }) }) }) }));
33
48
  };
@@ -446,10 +446,10 @@ const TMDcmtForm = ({ showHeader = true, onSaveRecents, layoutMode = LayoutModes
446
446
  'tmDcmtPreview': true,
447
447
  };
448
448
  const initialPanelDimensions = {
449
- 'tmDcmtForm': { width: '25%', height: '100%' },
450
- 'tmBlog': { width: '25%', height: '100%' },
451
- 'tmSysMetadata': { width: '25%', height: '100%' },
452
- 'tmDcmtPreview': { width: '25%', height: '100%' },
449
+ 'tmDcmtForm': { width: '20%', height: '100%' },
450
+ 'tmBlog': { width: '30%', height: '100%' },
451
+ 'tmSysMetadata': { width: '20%', height: '100%' },
452
+ 'tmDcmtPreview': { width: '30%', height: '100%' },
453
453
  };
454
454
  const initialPanels = useMemo(() => [
455
455
  {
@@ -493,24 +493,6 @@ const TMDcmtForm = ({ showHeader = true, onSaveRecents, layoutMode = LayoutModes
493
493
  toolbarOptions: { icon: _jsx(IconShow, { fontSize: 24 }), disabled: isPreviewDisabled, visible: true, orderNumber: 4, isActive: allInitialPanelVisibility['tmDcmtPreview'] }
494
494
  }
495
495
  ], [tmDcmtForm, tmBlog, tmSysMetadata, tmDcmtPreview, isPreviewDisabled, isBoardDisabled, isSysMetadataDisabled, isClosable]);
496
- // {showDcmtFormSidebar && <TMCommandsPanel
497
- // isMobile={deviceType === DeviceType.MOBILE}
498
- // items={[
499
- // ...(layoutMode === LayoutModes.Ark ? [
500
- // { icon: <IconRoundFileUpload />, selected: isOpenPreview, disabled: isPreviewDisabled, onClick: () => { if (!isPreviewDisabled) setIsOpenPreview(!isOpenPreview); } }
501
- // ] : []),
502
- // ...(layoutMode !== LayoutModes.Ark ? [
503
- // ...(deviceType === DeviceType.MOBILE ? [{ icon: <IconArrowLeft />, onClick: isClosable ? undefined : handleClose }] : []),
504
- // { icon: <IconPreview />, selected: isOpenDcmtForm, onClick: () => { setIsOpenDcmtForm(!isOpenDcmtForm); } },
505
- // { icon: <IconShow />, selected: isOpenPreview, disabled: isPreviewDisabled, onClick: () => { if (!isPreviewDisabled) setIsOpenPreview(!isOpenPreview); } },
506
- // { icon: <IconBoard />, selected: isOpenBoard, disabled: isBoardDisabled, onClick: () => { if (!isBoardDisabled) { closeMiddlePanel(); setIsOpenBoard(!isOpenBoard); } } },
507
- // { icon: <IconDcmtTypeSys />, selected: isOpenSysMetadata, disabled: isSysMetadataDisabled, onClick: () => { if (!isSysMetadataDisabled) { closeMiddlePanel(); setIsOpenSysMetadata(!isOpenSysMetadata); } } },
508
- // ] : []),
509
- // ...(allowRelations && currentTIDHasMasterRelations ? [{ icon: <IconDetailDcmts />, selected: isOpenMaster, disabled: isMasterDisabled, onClick: () => { if (!isMasterDisabled) setIsOpenMaster(!isOpenMaster); } }] : []),
510
- // ...(allowRelations && currentTIDHasDetailRelations ? [{ icon: <IconDetailDcmts transform='scale(-1, 1)' />, selected: isOpenDetails, disabled: isDetailsDisabled, onClick: () => { if (!isDetailsDisabled) setIsOpenDetails(!isOpenDetails); } }] : []),
511
- // ...customRightSidebarItems
512
- // ]}
513
- // />}
514
496
  const renderDcmtForm = () => {
515
497
  return (_jsxs("div", { style: {
516
498
  display: 'flex',
@@ -603,9 +585,9 @@ const ToppyImage = styled.img `
603
585
  export const ToppyHelpCenter = ({ content, onClick, deviceType, top = -200 }) => {
604
586
  return (_jsxs(ToppyContainer, { children: [_jsx(ToppyImage, { "$isMobile": deviceType === DeviceType.MOBILE, onClick: onClick, src: toppy, alt: "Toppy" }), _jsx("div", { style: { top: deviceType === DeviceType.MOBILE ? -180 : top, right: deviceType === DeviceType.MOBILE ? 20 : 1, transform: 'rotate(20deg)', position: 'absolute', width: 'max-content', height: 'max-content' }, children: content })] }));
605
587
  };
606
- const TMDcmtPreviewWrapper = ({ currentDcmt, layoutMode, fromDTD, dcmtFile, deviceType, onFileUpload }) => {
588
+ const TMDcmtPreviewWrapper = ({ currentDcmt, layoutMode, fromDTD, dcmtFile, deviceType, isVisible, onFileUpload }) => {
607
589
  const { setPanelVisibilityById, toggleMaximize, isResizingActive } = useTMPanelManagerContext();
608
590
  return (layoutMode === LayoutModes.Update ?
609
- _jsx(TMDcmtPreview, { onClosePanel: () => setPanelVisibilityById('tmDcmtPreview', false), onMaximizePanel: () => toggleMaximize('tmDcmtPreview'), dcmtData: currentDcmt, isResizingActive: isResizingActive }) :
591
+ _jsx(TMDcmtPreview, { isVisible: isVisible, onClosePanel: () => setPanelVisibilityById('tmDcmtPreview', false), onMaximizePanel: () => toggleMaximize('tmDcmtPreview'), dcmtData: currentDcmt, isResizingActive: isResizingActive }) :
610
592
  _jsx(TMFileUploader, { onFileUpload: onFileUpload, onClose: () => setPanelVisibilityById('tmDcmtPreview', false), isRequired: fromDTD?.archiveConstraint === ArchiveConstraints.ContentCompulsory && dcmtFile === null, defaultBlob: dcmtFile, deviceType: deviceType, isResizingActive: isResizingActive }));
611
593
  };
@@ -5,6 +5,7 @@ interface ITMDcmtPreviewProps {
5
5
  canNext?: boolean;
6
6
  canPrev?: boolean;
7
7
  isResizingActive?: boolean;
8
+ isVisible?: boolean;
8
9
  onNext?: () => void;
9
10
  onPrev?: () => void;
10
11
  onClosePanel?: () => void;
@@ -15,7 +15,7 @@ import { TMSaveFormButtonPrevious, TMSaveFormButtonNext } from '../../forms/TMSa
15
15
  import { StyledAnimatedComponentOpacity } from '../../base/Styled';
16
16
  import TMPanel from '../../base/TMPanel';
17
17
  import { DeviceType, useDeviceType } from '../../base/TMDeviceProvider';
18
- const TMDcmtPreview = ({ dcmtData, isResizingActive, onClosePanel, canNext, canPrev, onNext, onPrev, onMaximizePanel }) => {
18
+ const TMDcmtPreview = ({ dcmtData, isResizingActive, isVisible, canNext, canPrev, onClosePanel, onNext, onPrev, onMaximizePanel }) => {
19
19
  const [dcmtBlob, setDcmtBlob] = useState(undefined);
20
20
  const [showPreview, setShowPreview] = useState(false);
21
21
  const [isFromCache, setIsFromCache] = useState(false);
@@ -24,24 +24,37 @@ const TMDcmtPreview = ({ dcmtData, isResizingActive, onClosePanel, canNext, canP
24
24
  const cacheKey = dcmtData ? `${dcmtData.tid}-${dcmtData.did}` : '00';
25
25
  const deviceType = useDeviceType();
26
26
  const isMobile = deviceType === DeviceType.MOBILE;
27
+ const [hasLoadedDataOnce, setHasLoadedDataOnce] = useState(false);
28
+ const [lastLoadedDid, setLastLoadedDid] = useState(undefined);
27
29
  useEffect(() => {
28
- setDcmtBlob(undefined);
29
- setError('');
30
- if (!dcmtData)
31
- return;
32
- if (isDcmtFileInCache(cacheKey)) {
33
- loadDocumentWithCache();
34
- setShowPreview(true);
30
+ if (!dcmtData) {
31
+ setLastLoadedDid(undefined); // Reset
32
+ setDcmtBlob(undefined);
33
+ setError('');
34
+ setShowPreview(false);
35
35
  return;
36
36
  }
37
- if ((extensionHandler(dcmtData.fileExt) !== FileExtensionHandler.NONE) && ((dcmtData.fileSize ?? 0) <= (SDKUI_Globals.userSettings.searchSettings.previewThreshold * 1024))) {
37
+ const currentCacheKey = `${dcmtData.tid}-${dcmtData.did}`;
38
+ const shouldFetch = isVisible && (!hasLoadedDataOnce || currentCacheKey !== lastLoadedDid);
39
+ if (isDcmtFileInCache(currentCacheKey)) {
38
40
  loadDocumentWithCache();
39
41
  setShowPreview(true);
42
+ return;
40
43
  }
41
- else {
42
- setShowPreview(false);
44
+ if (shouldFetch) {
45
+ setDcmtBlob(undefined);
46
+ setError('');
47
+ if ((extensionHandler(dcmtData.fileExt) !== FileExtensionHandler.NONE) && ((dcmtData.fileSize ?? 0) <= (SDKUI_Globals.userSettings.searchSettings.previewThreshold * 1024))) {
48
+ loadDocumentWithCache();
49
+ setShowPreview(true);
50
+ }
51
+ else {
52
+ setShowPreview(false);
53
+ }
54
+ setHasLoadedDataOnce(true);
55
+ setLastLoadedDid(currentCacheKey);
43
56
  }
44
- }, [dcmtData]);
57
+ }, [dcmtData?.did, isVisible, hasLoadedDataOnce, lastLoadedDid]);
45
58
  const loadDocumentWithCache = async () => {
46
59
  const rfo = new RetrieveFileOptions();
47
60
  rfo.retrieveReason = DcmtOpers.None;
@@ -696,7 +696,7 @@ const TMSearchResultSelector = ({ searchResults = [], onSelectionChanged }) => {
696
696
  return (_jsx("div", { style: { height: '100%', width: '100%', overflow: 'auto' }, children: sortedCategories.map((category) => (_jsxs("div", { children: [_jsxs(StyledGroupTemplate, { onClick: () => toggleCategory(category), children: [activeCategories.includes(category) ? _jsx(IconChevronDown, {}) : _jsx(IconChevronDown, { transform: 'scale(-1, 1)' }), renderGroupTemplate(category)] }, category), activeCategories.includes(category) && (_jsx("div", { style: { padding: '5px' }, children: groupedResults[category].map((result, index) => (_jsx(StyledItemTemplate, { "$isSelected": selectedResult === result, onClick: () => handleSelect(result), children: renderItemTemplate(result) }, index))) }))] }, category))) }));
697
697
  };
698
698
  //#endregion TMSearchResultSelector
699
- const TMDcmtPreviewWrapper = ({ currentDcmt }) => {
699
+ const TMDcmtPreviewWrapper = ({ currentDcmt, isVisible }) => {
700
700
  const { setPanelVisibilityById, toggleMaximize, isResizingActive } = useTMPanelManagerContext();
701
- return (_jsx(TMDcmtPreview, { onClosePanel: () => setPanelVisibilityById('tmDcmtPreview', false), onMaximizePanel: () => toggleMaximize('tmDcmtPreview'), dcmtData: currentDcmt, isResizingActive: isResizingActive }));
701
+ return (_jsx(TMDcmtPreview, { onClosePanel: () => setPanelVisibilityById('tmDcmtPreview', false), onMaximizePanel: () => toggleMaximize('tmDcmtPreview'), dcmtData: currentDcmt, isResizingActive: isResizingActive, isVisible: isVisible }));
702
702
  };
@@ -78,6 +78,9 @@ export { default as TMFileManagerThumbnailItems } from "./base/TMFileManagerThum
78
78
  export { default as TMCounterContainer } from "./base/TMCounterContainer";
79
79
  export * from "./base/TMCounterContainer";
80
80
  export { default as TMAreaManager } from "./base/TMAreaManager";
81
- export * from "./layout/panel/TMPanelContext";
82
- export * from "./layout/panel/TMPanelToolbar";
83
- export * from "./layout/panel/useResizablePanels";
81
+ export * from "./layout/panelManager/TMPanelManagerContainer";
82
+ export * from "./layout/panelManager/TMPanelManagerContext";
83
+ export * from "./layout/panelManager/TMPanelManagerToolbar";
84
+ export * from "./layout/panelManager/TMPanelWrapper";
85
+ export * from "./layout/panelManager/types";
86
+ export * from "./layout/panelManager/utils";
@@ -93,7 +93,10 @@ export { default as TMFileManagerThumbnailItems } from "./base/TMFileManagerThum
93
93
  export { default as TMCounterContainer } from "./base/TMCounterContainer";
94
94
  export * from "./base/TMCounterContainer";
95
95
  export { default as TMAreaManager } from "./base/TMAreaManager";
96
- // layout
97
- export * from "./layout/panel/TMPanelContext";
98
- export * from "./layout/panel/TMPanelToolbar";
99
- export * from "./layout/panel/useResizablePanels";
96
+ // panel manager
97
+ export * from "./layout/panelManager/TMPanelManagerContainer";
98
+ export * from "./layout/panelManager/TMPanelManagerContext";
99
+ export * from "./layout/panelManager/TMPanelManagerToolbar";
100
+ export * from "./layout/panelManager/TMPanelWrapper";
101
+ export * from "./layout/panelManager/types";
102
+ export * from "./layout/panelManager/utils";
@@ -4,5 +4,5 @@ interface TMPanelWrapperProps {
4
4
  panel: TMPanelDefinition;
5
5
  children: ReactNode;
6
6
  }
7
- declare const TMPanelWrapper: (props: TMPanelWrapperProps) => import("react/jsx-runtime").JSX.Element;
7
+ declare const TMPanelWrapper: (props: TMPanelWrapperProps) => import("react/jsx-runtime").JSX.Element | null;
8
8
  export default TMPanelWrapper;
@@ -1,7 +1,7 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { useMemo } from 'react';
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ import React, { useMemo, useState, useEffect } from 'react';
3
3
  import { useTMPanelManagerContext } from './TMPanelManagerContext';
4
- import { DeviceType, useDeviceType } from '../../base/TMDeviceProvider';
4
+ import { useDeviceType, DeviceType } from '../../base/TMDeviceProvider';
5
5
  import TMPanel from '../../base/TMPanel';
6
6
  const TMPanelWrapper = (props) => {
7
7
  const { panel, children } = props;
@@ -16,15 +16,23 @@ const TMPanelWrapper = (props) => {
16
16
  // Extract panel dimensions based on panel id
17
17
  const width = panelDimensions[panel.id].width;
18
18
  const height = panelDimensions[panel.id].height;
19
- // Determine visibility:
19
+ // Determine visibility:
20
20
  // - If any panels are maximized, only show those maximized panels
21
21
  // - Otherwise, rely on the normal panel visibility state
22
- const isVisible = maximizedPanels.length > 0 ? maximizedPanels.includes(panel.id) : panelVisibility[panel.id];
22
+ const isCurrentlyVisible = maximizedPanels.length > 0 ? maximizedPanels.includes(panel.id) : panelVisibility[panel.id];
23
+ // NUOVO STATO: Traccia se il pannello è mai stato reso visibile
24
+ const [hasBeenRenderedOnce, setHasBeenRenderedOnce] = useState(isCurrentlyVisible);
25
+ // NUOVO useEffect: Aggiorna hasBeenRenderedOnce quando il pannello diventa visibile per la prima volta
26
+ useEffect(() => {
27
+ if (isCurrentlyVisible && !hasBeenRenderedOnce) {
28
+ setHasBeenRenderedOnce(true);
29
+ }
30
+ }, [isCurrentlyVisible, hasBeenRenderedOnce]);
23
31
  const panelStyles = {
24
32
  margin: '0',
25
- // overflow: 'hidden',
26
33
  boxSizing: 'border-box',
27
- display: isVisible ? 'flex' : 'none',
34
+ // Applica 'flex' o 'none' solo se è già stato reso visibile almeno una volta
35
+ display: isCurrentlyVisible ? 'flex' : 'none',
28
36
  flexDirection: 'column',
29
37
  minWidth: '50px',
30
38
  minHeight: '50px',
@@ -32,7 +40,22 @@ const TMPanelWrapper = (props) => {
32
40
  height: height,
33
41
  pointerEvents: 'auto',
34
42
  };
43
+ // Rende il pannello solo se è attualmente visibile O se è già stato reso visibile una volta.
44
+ // Se non è mai stato reso visibile E non è attualmente visibile, non renderizza nulla (o un placeholder vuoto).
45
+ if (!isCurrentlyVisible && !hasBeenRenderedOnce) {
46
+ return null;
47
+ }
48
+ // Clona il child e passa una prop 'isVisible'
49
+ const childrenWithProps = React.Children.map(children, child => {
50
+ if (React.isValidElement(child)) {
51
+ return React.cloneElement(child, { isVisible: isCurrentlyVisible });
52
+ }
53
+ return child;
54
+ });
35
55
  return (_jsx("div", { "data-panel-id": panel.id, style: panelStyles, children: panel.contentOptions?.panelContainer ?
36
- _jsx(TMPanel, { ...panel.contentOptions.panelContainer, allowMaximize: !isMobile, onHeaderDoubleClick: isMaximizable ? () => toggleMaximize(panel.id) : undefined, onMaximize: isMaximizable ? () => toggleMaximize(panel.id) : undefined, onClose: isClosable ? () => togglePanelVisibility(panel.id) : undefined, children: children }) : children }));
56
+ _jsxs(TMPanel, { ...panel.contentOptions.panelContainer, allowMaximize: !isMobile, onHeaderDoubleClick: isMaximizable ? () => toggleMaximize(panel.id) : undefined, onMaximize: isMaximizable ? () => toggleMaximize(panel.id) : undefined, onClose: isClosable ? () => togglePanelVisibility(panel.id) : undefined, children: [childrenWithProps, " "] })
57
+ :
58
+ childrenWithProps // Usa i children clonati
59
+ }));
37
60
  };
38
61
  export default TMPanelWrapper;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@topconsultnpm/sdkui-react-beta",
3
- "version": "6.13.75",
3
+ "version": "6.13.77",
4
4
  "description": "",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1",
@@ -1,39 +0,0 @@
1
- import React, { ReactNode } from 'react';
2
- type TMPanelManagerNewProps = {
3
- children: ReactNode;
4
- showToolbar?: boolean;
5
- emptyContent?: ReactNode;
6
- };
7
- type TMPanelGroupProps = {
8
- orientation?: 'horizontal' | 'vertical';
9
- allowItemResize?: boolean;
10
- gutters?: number;
11
- panelVisibility?: boolean[];
12
- onTogglePanel?: (idx: number) => void;
13
- panelLabels?: string[];
14
- children: ReactNode;
15
- parentVisibility?: boolean;
16
- parentPanelCount?: number;
17
- parentPanelVisibility?: boolean[];
18
- parentOnTogglePanel?: (idx: number, path: number[]) => void;
19
- path?: number[];
20
- };
21
- type TMPanelItemProps = {
22
- width?: string;
23
- height?: string;
24
- minWidth?: string;
25
- minHeight?: string;
26
- children: ReactNode;
27
- style?: React.CSSProperties;
28
- label?: string;
29
- };
30
- export declare const TMPanelItem: React.FC<TMPanelItemProps>;
31
- export declare const TMPanelGroup: React.FC<TMPanelGroupProps>;
32
- declare const TMPanelLayout: React.FC<TMPanelManagerNewProps>;
33
- export default TMPanelLayout;
34
- type TMPanelLayoutToolbarProps = {
35
- panelLabels: string[];
36
- panelVisibility: boolean[];
37
- onTogglePanel: (idx: number) => void;
38
- };
39
- export declare const TMPanelLayoutToolbar: React.FC<TMPanelLayoutToolbarProps>;
@@ -1,318 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import React, { useRef, useState, Children, cloneElement, isValidElement, useEffect, useMemo, } from 'react';
3
- import { SDKUI_Globals } from '../../helper';
4
- import { useDeviceType } from '../base/TMDeviceProvider';
5
- export const TMPanelItem = (props) => (_jsx("div", { style: {
6
- flexBasis: props.width || props.height || 'auto',
7
- minWidth: props.minWidth,
8
- minHeight: props.minHeight,
9
- flexGrow: 0,
10
- flexShrink: 0,
11
- height: '100%',
12
- width: '100%',
13
- // overflow: 'auto',
14
- position: 'relative',
15
- boxSizing: 'border-box',
16
- display: 'flex',
17
- flexDirection: 'column',
18
- ...props.style,
19
- }, children: props.children }));
20
- // Utility: recursively determine if a child is a group
21
- function isGroup(child) {
22
- return child?.type?.displayName === 'TMPanelGroup';
23
- }
24
- TMPanelItem.displayName = 'TMPanelItem';
25
- export const TMPanelGroup = ({ orientation = 'horizontal', allowItemResize = false, gutters = SDKUI_Globals.userSettings.themeSettings.gutters, panelVisibility, onTogglePanel, panelLabels, children, parentVisibility = true, parentPanelCount, parentPanelVisibility, parentOnTogglePanel, path = [], ...rest }) => {
26
- const childArray = Children.toArray(children).filter(Boolean);
27
- const panelCount = childArray.length;
28
- // Initial sizes in percent
29
- const [sizes, setSizes] = useState(childArray.map((child) => orientation === 'horizontal'
30
- ? child.props.width
31
- ? parseFloat(child.props.width)
32
- : 100 / panelCount
33
- : child.props.height
34
- ? parseFloat(child.props.height)
35
- : 100 / panelCount));
36
- // Compute effective visibility for each child (recursively for groups)
37
- const effectiveVisibility = childArray.map((child, idx) => {
38
- if (isValidElement(child) && isGroup(child)) {
39
- // For nested groups, pass down visibility
40
- return !panelVisibility || panelVisibility[idx];
41
- }
42
- return !panelVisibility || panelVisibility[idx];
43
- });
44
- // Recursively render children, and collect which are visible
45
- const renderedPanels = [];
46
- const renderedSizes = [];
47
- const renderedPaths = [];
48
- let accHidden = 0;
49
- for (let i = 0; i < panelCount; i++) {
50
- const child = childArray[i];
51
- const visible = effectiveVisibility[i];
52
- let childIsVisible = visible;
53
- let renderedChild = child;
54
- let childPath = [...path, i];
55
- if (isValidElement(child) && isGroup(child)) {
56
- // Recursively render group
57
- renderedChild = cloneElement(child, {
58
- parentVisibility: visible,
59
- parentPanelCount: panelCount,
60
- parentPanelVisibility: panelVisibility,
61
- parentOnTogglePanel: parentOnTogglePanel || onTogglePanel,
62
- path: childPath,
63
- });
64
- // If the group renders nothing, treat as hidden
65
- // We'll check after rendering below
66
- }
67
- // For groups, check if they actually rendered anything
68
- let actuallyVisible = childIsVisible;
69
- if (isValidElement(child) && isGroup(child)) {
70
- // If the group is not visible (returns null), treat as hidden
71
- // We'll check after rendering below
72
- // For now, optimistically add, and filter after
73
- renderedPanels.push(renderedChild);
74
- renderedSizes.push(sizes[i] + accHidden);
75
- renderedPaths.push(childPath);
76
- accHidden = 0;
77
- }
78
- else if (childIsVisible) {
79
- renderedPanels.push(renderedChild);
80
- renderedSizes.push(sizes[i] + accHidden);
81
- renderedPaths.push(childPath);
82
- accHidden = 0;
83
- }
84
- else {
85
- accHidden += sizes[i];
86
- }
87
- }
88
- // Remove panels that are null (hidden nested groups)
89
- let filteredPanels = [];
90
- let filteredSizes = [];
91
- let filteredPaths = [];
92
- let filteredAccHidden = 0;
93
- for (let i = 0; i < renderedPanels.length; i++) {
94
- if (renderedPanels[i] !== null && renderedPanels[i] !== undefined) {
95
- filteredPanels.push(renderedPanels[i]);
96
- filteredSizes.push(renderedSizes[i] + filteredAccHidden);
97
- filteredPaths.push(renderedPaths[i]);
98
- filteredAccHidden = 0;
99
- }
100
- else {
101
- filteredAccHidden += renderedSizes[i];
102
- }
103
- }
104
- // --- CORRECTED MERGING LOGIC: merge hidden into previous visible, or into the first visible if at the start ---
105
- // Merge hidden panel sizes into the nearest visible sibling (prefer previous, else next)
106
- const mergedPanels = [];
107
- const mergedSizes = [];
108
- const mergedPaths = [];
109
- let lastVisibleIdx = -1;
110
- for (let i = 0; i < filteredPanels.length; i++) {
111
- if (filteredPanels[i] !== null && filteredPanels[i] !== undefined) {
112
- // If there were hidden panels before the first visible, merge their sizes into this first visible
113
- if (lastVisibleIdx === -1 && i > 0) {
114
- let acc = 0;
115
- for (let j = 0; j < i; j++)
116
- acc += filteredSizes[j];
117
- mergedSizes.push(filteredSizes[i] + acc);
118
- }
119
- else {
120
- mergedSizes.push(filteredSizes[i]);
121
- }
122
- mergedPanels.push(filteredPanels[i]);
123
- mergedPaths.push(filteredPaths[i]);
124
- lastVisibleIdx = mergedSizes.length - 1;
125
- }
126
- else {
127
- // Hidden panel: merge its size into the last visible panel (if any)
128
- if (lastVisibleIdx >= 0) {
129
- mergedSizes[lastVisibleIdx] += filteredSizes[i];
130
- }
131
- }
132
- }
133
- // If nothing is visible, tell parent to treat this group as hidden
134
- if (mergedPanels.length === 0 || parentVisibility === false) {
135
- return null;
136
- }
137
- // Resizing logic (same as before, but for visible panels)
138
- const containerRef = useRef(null);
139
- const [dragIndex, setDragIndex] = useState(null);
140
- const [startPos, setStartPos] = useState(0);
141
- const [startSizes, setStartSizes] = useState([]);
142
- const handleMouseDown = (idx) => (e) => {
143
- setDragIndex(idx);
144
- setStartSizes([...mergedSizes]);
145
- setStartPos(orientation === 'horizontal' ? e.clientX : e.clientY);
146
- document.body.style.userSelect = 'none';
147
- };
148
- useEffect(() => {
149
- if (dragIndex === null)
150
- return;
151
- const handleMouseMove = (e) => {
152
- if (!containerRef.current)
153
- return;
154
- const rect = containerRef.current.getBoundingClientRect();
155
- const total = orientation === 'horizontal' ? rect.width : rect.height;
156
- const currentPos = orientation === 'horizontal' ? e.clientX : e.clientY;
157
- const deltaPx = currentPos - startPos;
158
- const deltaPercent = (deltaPx / total) * 100;
159
- let newSizes = [...startSizes];
160
- let left = Math.max(5, startSizes[dragIndex] + deltaPercent);
161
- let right = Math.max(5, startSizes[dragIndex + 1] - deltaPercent);
162
- // Prevent overflow
163
- const sum = startSizes[dragIndex] + startSizes[dragIndex + 1];
164
- if (left + right > sum) {
165
- if (left > sum - 5)
166
- left = sum - 5;
167
- if (right > sum - 5)
168
- right = sum - 5;
169
- }
170
- newSizes[dragIndex] = left;
171
- newSizes[dragIndex + 1] = right;
172
- // Update the original sizes array for all panels
173
- let newAllSizes = [...sizes];
174
- let visIdx = 0;
175
- for (let k = 0; k < panelCount; k++) {
176
- if (effectiveVisibility[k]) {
177
- // Only update for visible panels
178
- newAllSizes[k] = newSizes[visIdx++];
179
- }
180
- }
181
- setSizes(newAllSizes);
182
- };
183
- const handleMouseUp = () => {
184
- setDragIndex(null);
185
- document.body.style.userSelect = '';
186
- };
187
- window.addEventListener('mousemove', handleMouseMove);
188
- window.addEventListener('mouseup', handleMouseUp);
189
- return () => {
190
- window.removeEventListener('mousemove', handleMouseMove);
191
- window.removeEventListener('mouseup', handleMouseUp);
192
- };
193
- }, [dragIndex, orientation, startPos, startSizes, effectiveVisibility, sizes, panelCount]);
194
- return (_jsx("div", { ref: containerRef, style: {
195
- display: 'flex',
196
- flexDirection: orientation === 'horizontal' ? 'row' : 'column',
197
- width: '100%',
198
- height: '100%',
199
- position: 'relative',
200
- overflow: 'hidden',
201
- gap: allowItemResize ? 0 : gutters,
202
- }, ...rest, children: mergedPanels.map((child, idx) => (_jsxs(React.Fragment, { children: [isValidElement(child) && !isGroup(child)
203
- ? cloneElement(child, {
204
- style: {
205
- ...(child.props.style || {}),
206
- flexBasis: `${mergedSizes[idx]}%`,
207
- height: orientation === 'vertical'
208
- ? `${mergedSizes[idx]}%`
209
- : '100%',
210
- width: orientation === 'horizontal'
211
- ? `${mergedSizes[idx]}%`
212
- : '100%',
213
- },
214
- })
215
- : child, allowItemResize && idx < mergedPanels.length - 1 && (_jsx("div", { style: {
216
- cursor: orientation === 'horizontal'
217
- ? 'col-resize'
218
- : 'row-resize',
219
- background: 'transparent',
220
- width: orientation === 'horizontal' ? gutters : '100%',
221
- height: orientation === 'vertical' ? gutters : '100%',
222
- zIndex: 10,
223
- userSelect: 'none',
224
- flexShrink: 0,
225
- flexGrow: 0,
226
- borderRadius: 3,
227
- minHeight: orientation === 'horizontal' ? '100%' : gutters,
228
- minWidth: orientation === 'vertical' ? '100%' : gutters,
229
- }, onMouseDown: handleMouseDown(idx) }))] }, mergedPaths[idx].join('-')))) }));
230
- };
231
- TMPanelGroup.displayName = 'TMPanelGroup';
232
- function getAllPanelPaths(children, path = []) {
233
- const arr = Children.toArray(children).filter(Boolean);
234
- let result = [];
235
- arr.forEach((child, idx) => {
236
- if (isValidElement(child) && isGroup(child)) {
237
- result = result.concat(getAllPanelPaths(child.props.children, [...path, idx]));
238
- }
239
- else if (isValidElement(child)) {
240
- result.push({ label: child.props?.label || `Panel ${path.concat(idx).join('-')}`, path: path.concat(idx) });
241
- }
242
- });
243
- return result;
244
- }
245
- const TMPanelLayout = ({ children, showToolbar = true, emptyContent, }) => {
246
- // Only support a single TMPanelGroup as direct child for simplicity
247
- const group = Children.only(children);
248
- const childArray = Children.toArray(group.props.children).filter(Boolean);
249
- // Build a flat list of all panel paths and labels for the toolbar
250
- const allPanels = useMemo(() => getAllPanelPaths(group.props.children), [group.props.children]);
251
- const [panelVisibility, setPanelVisibility] = useState(childArray.map(() => true));
252
- // Helper to check if all panels (recursively) are hidden
253
- function isAllPanelsHidden() {
254
- // For now, just check top-level
255
- return panelVisibility.every(v => !v);
256
- }
257
- // Toggle panel visibility by path (only top-level supported for now)
258
- const handleTogglePanel = (idx) => {
259
- setPanelVisibility((prev) => {
260
- if (prev.filter(Boolean).length === 1 && prev[idx])
261
- return prev;
262
- const newArr = [...prev];
263
- newArr[idx] = !newArr[idx];
264
- return newArr;
265
- });
266
- };
267
- // For nested: you can extend this to track nested visibility state if needed
268
- const allHidden = isAllPanelsHidden();
269
- const deviceType = useDeviceType();
270
- const isMobile = useMemo(() => deviceType === 'mobile', [deviceType]);
271
- return (_jsx("div", { style: {
272
- width: '100%',
273
- height: '100%',
274
- position: 'relative',
275
- overflow: 'hidden',
276
- display: 'flex',
277
- flexDirection: 'column',
278
- }, children: _jsxs("div", { style: { display: 'flex', flexDirection: isMobile ? 'column' : 'row', height: '100%', width: '100%', gap: !isMobile ? SDKUI_Globals.userSettings.themeSettings.gutters : 0 }, children: [!allHidden &&
279
- cloneElement(group, {
280
- panelVisibility,
281
- onTogglePanel: handleTogglePanel,
282
- panelLabels: allPanels.map(p => p.label),
283
- }), showToolbar && (_jsx("div", { style: {
284
- display: 'flex',
285
- flexDirection: isMobile ? 'row' : 'column',
286
- alignItems: 'center',
287
- width: isMobile ? '100%' : '50px',
288
- height: isMobile ? '50px' : 'max-content',
289
- background: 'transparent linear-gradient(90deg, #CCE0F4 0%, #7EC1E7 14%, #39A6DB 28%, #1E9CD7 35%, #0075BE 78%, #005B97 99%) 0% 0% no-repeat padding-box',
290
- borderRadius: isMobile ? '10px' : '10px 0px 0px 10px',
291
- padding: '10px',
292
- gap: '10px'
293
- }, children: _jsx(TMPanelLayoutToolbar, { panelLabels: allPanels.map(p => p.label), panelVisibility: panelVisibility, onTogglePanel: handleTogglePanel }) })), allHidden && (_jsx("div", { style: {
294
- position: 'absolute',
295
- left: 0,
296
- top: 0,
297
- width: '100%',
298
- height: '100%',
299
- background: '#fafbfc',
300
- display: 'flex',
301
- alignItems: 'center',
302
- justifyContent: 'center',
303
- zIndex: 1,
304
- }, children: emptyContent || (_jsx("span", { style: { color: '#888', fontSize: 18 }, children: "No panels visible" })) }))] }) }));
305
- };
306
- export default TMPanelLayout;
307
- export const TMPanelLayoutToolbar = ({ panelLabels, panelVisibility, onTogglePanel, }) => {
308
- return (_jsx("div", { children: panelLabels.map((label, idx) => (_jsxs("button", { style: {
309
- margin: 2,
310
- padding: '4px 10px',
311
- borderRadius: 4,
312
- border: panelVisibility[idx] ? '2px solid #1976d2' : '1px solid #bbb',
313
- background: panelVisibility[idx] ? '#1976d2' : '#eee',
314
- color: panelVisibility[idx] ? '#fff' : '#555',
315
- cursor: 'pointer',
316
- opacity: 1,
317
- }, onClick: () => onTogglePanel(idx), children: [panelVisibility[idx] ? 'Hide' : 'Show', " ", label] }, idx))) }));
318
- };
@@ -1,45 +0,0 @@
1
- import { ReactNode } from 'react';
2
- export interface TMPanelManagerItemContext {
3
- id: string;
4
- name: string;
5
- contentOptions: {
6
- width: string;
7
- height: string;
8
- };
9
- toolbarOptions: {
10
- icon: string | JSX.Element;
11
- visible: boolean;
12
- isActive: boolean;
13
- disabled?: boolean;
14
- orderNumber?: number;
15
- beginGroup?: boolean;
16
- alwaysActiveColor?: boolean;
17
- };
18
- children: Array<TMPanelManagerItemContext>;
19
- }
20
- interface ITMPanelManagerContext {
21
- panelTree: Array<TMPanelManagerItemContext>;
22
- visibility: Record<string, boolean>;
23
- calculateEffectiveVisibility: Record<string, boolean>;
24
- setPanelVisibility: (id: string, visible: boolean) => void;
25
- togglePanel: (id: string) => void;
26
- findPanelById: (id: string) => TMPanelManagerItemContext | undefined;
27
- getPanelDimensions: (id: string) => {
28
- width: string;
29
- height: string;
30
- };
31
- toggleMaximizePanel: (id: string) => void;
32
- maximizedPanelId: string | null;
33
- updatePanelSize: (id: string, width: string | number, height: string | number) => void;
34
- setPanelVisibleById: (id: string, visible: boolean) => void;
35
- setPanelDisabledById: (id: string, disabled: boolean) => void;
36
- hasVisiblePanels: () => boolean;
37
- }
38
- export declare function useTMPanelContext(): ITMPanelManagerContext;
39
- interface TMPanelContextProviderProps {
40
- panels: Array<TMPanelManagerItemContext>;
41
- initialMobilePanelId: string;
42
- children: ReactNode;
43
- }
44
- export declare const TMPanelManagerContextProvider: (props: TMPanelContextProviderProps) => import("react/jsx-runtime").JSX.Element;
45
- export {};
@@ -1,314 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { createContext, useContext, useMemo, useState, useEffect, } from 'react';
3
- import { DeviceType, useDeviceType } from '../../base/TMDeviceProvider';
4
- // Creazione del context
5
- const TMPanelManagerContext = createContext(undefined);
6
- // Hook custom per accedere al context
7
- export function useTMPanelContext() {
8
- const context = useContext(TMPanelManagerContext);
9
- if (!context) {
10
- throw new Error('useTMPanelContext must be used within a TMPanelContextProvider');
11
- }
12
- return context;
13
- }
14
- // Provider del context
15
- export const TMPanelManagerContextProvider = (props) => {
16
- const { panels, initialMobilePanelId, children } = props;
17
- const [panelTree, setPanelTree] = useState(panels);
18
- const [maximizedPanelId, setMaximizedPanelId] = useState(null);
19
- const deviceType = useDeviceType();
20
- let isMobile = useMemo(() => { return deviceType === DeviceType.MOBILE; }, [deviceType]);
21
- useEffect(() => {
22
- if (isMobile) {
23
- setVisibility(prev => updatePanelVisibility(initialMobilePanelId, true, prev));
24
- }
25
- }, [isMobile]);
26
- // Stato iniziale per dimensioni pannelli
27
- const [panelSizes, setPanelSizes] = useState(() => {
28
- const sizes = {};
29
- const flattenPanels = (panels) => panels.reduce((acc, p) => [...acc, p, ...flattenPanels(p.children)], []);
30
- const allPanels = flattenPanels(panelTree);
31
- allPanels.forEach(panel => {
32
- sizes[panel.id] = { width: panel.contentOptions.width, height: panel.contentOptions.height };
33
- });
34
- return sizes;
35
- });
36
- // Trova un pannello tramite ID
37
- const findPanelById = (id) => {
38
- const findRecursive = (panels) => {
39
- for (const panel of panels) {
40
- if (panel.id === id)
41
- return panel;
42
- const found = findRecursive(panel.children);
43
- if (found)
44
- return found;
45
- }
46
- return undefined;
47
- };
48
- return findRecursive(panelTree);
49
- };
50
- // Mappa figlio -> genitori
51
- const parentMap = useMemo(() => {
52
- const map = new Map();
53
- const buildMap = (panels, parents = []) => {
54
- for (const panel of panels) {
55
- map.set(panel.id, [...parents]);
56
- buildMap(panel.children, [...parents, panel.id]);
57
- }
58
- };
59
- buildMap(panelTree);
60
- return map;
61
- }, [panelTree]);
62
- // Mappa genitore -> figli
63
- const parentToChildrenMap = useMemo(() => {
64
- const map = new Map();
65
- const buildMap = (panels) => {
66
- for (const panel of panels) {
67
- map.set(panel.id, panel.children.map(c => c.id));
68
- buildMap(panel.children);
69
- }
70
- };
71
- buildMap(panelTree);
72
- return map;
73
- }, [panelTree]);
74
- // Stato visibilità iniziale
75
- const getInitialVisibility = (panels) => {
76
- const vis = {};
77
- const collect = (panels) => {
78
- for (const panel of panels) {
79
- vis[panel.id] = panel.toolbarOptions?.isActive ?? false;
80
- collect(panel.children);
81
- }
82
- };
83
- collect(panels);
84
- return vis;
85
- };
86
- const [visibility, setVisibility] = useState(() => getInitialVisibility(panelTree));
87
- const getParentsOfPanel = (id) => parentMap.get(id) || [];
88
- // Visibilità effettiva tenendo conto della gerarchia
89
- const calculateEffectiveVisibility = useMemo(() => {
90
- if (maximizedPanelId) {
91
- return { ...visibility };
92
- }
93
- const updatedVisibility = { ...visibility };
94
- const propagateToParents = (id) => {
95
- for (const parent of parentMap.get(id) || []) {
96
- if (!updatedVisibility[parent]) {
97
- updatedVisibility[parent] = true;
98
- propagateToParents(parent);
99
- }
100
- }
101
- };
102
- Object.entries(updatedVisibility).forEach(([id, isVisible]) => {
103
- if (isVisible)
104
- propagateToParents(id);
105
- });
106
- const deactivateIfNoVisibleChildren = (parentId) => {
107
- const children = parentToChildrenMap.get(parentId) || [];
108
- if (children.length === 0)
109
- return;
110
- const anyVisible = children.some(id => updatedVisibility[id]);
111
- if (!anyVisible && updatedVisibility[parentId]) {
112
- updatedVisibility[parentId] = false;
113
- for (const grand of parentMap.get(parentId) || []) {
114
- deactivateIfNoVisibleChildren(grand);
115
- }
116
- }
117
- };
118
- for (const parentId of parentToChildrenMap.keys()) {
119
- deactivateIfNoVisibleChildren(parentId);
120
- }
121
- return updatedVisibility;
122
- }, [visibility, parentMap, parentToChildrenMap, maximizedPanelId]);
123
- // Calcolo delle dimensioni dinamiche in base alla visibilità
124
- const dynamicSizesMap = useMemo(() => {
125
- const flattenPanels = (panels) => panels.reduce((acc, p) => [...acc, p, ...flattenPanels(p.children)], []);
126
- const allPanels = flattenPanels(panelTree);
127
- const originalHeights = new Map();
128
- const originalWidths = new Map();
129
- allPanels.forEach(panel => {
130
- originalHeights.set(panel.id, parseFloat(panel.contentOptions.height) || 0);
131
- originalWidths.set(panel.id, parseFloat(panel.contentOptions.width) || 0);
132
- });
133
- if (maximizedPanelId) {
134
- const parents = getParentsOfPanel(maximizedPanelId);
135
- const allowed = new Set([maximizedPanelId, ...parents]);
136
- const sizesMap = {};
137
- allPanels.forEach(panel => {
138
- sizesMap[panel.id] = allowed.has(panel.id)
139
- ? { width: '100%', height: '100%' }
140
- : { width: '0%', height: '0%' };
141
- });
142
- return sizesMap;
143
- }
144
- const calcSiblingSizes = (siblings, originalSizes) => {
145
- const visibleSiblings = siblings.filter(s => calculateEffectiveVisibility[s.id]);
146
- const totalVisible = visibleSiblings.reduce((sum, s) => sum + (originalSizes.get(s.id) || 0), 0);
147
- const totalAll = siblings.reduce((sum, s) => sum + (originalSizes.get(s.id) || 0), 0);
148
- const map = {};
149
- visibleSiblings.forEach(s => {
150
- const orig = originalSizes.get(s.id) || 0;
151
- map[s.id] = totalVisible === 0 ? 0 : (orig / totalVisible) * totalAll;
152
- });
153
- siblings.forEach(s => {
154
- if (!calculateEffectiveVisibility[s.id])
155
- map[s.id] = 0;
156
- });
157
- return map;
158
- };
159
- const widthSizesMap = {};
160
- const heightSizesMap = {};
161
- Object.assign(widthSizesMap, calcSiblingSizes(panelTree, originalWidths));
162
- Object.assign(heightSizesMap, calcSiblingSizes(panelTree, originalHeights));
163
- const processChildrenSizes = (panels) => {
164
- panels.forEach(panel => {
165
- if (panel.children.length > 0) {
166
- Object.assign(widthSizesMap, calcSiblingSizes(panel.children, originalWidths));
167
- Object.assign(heightSizesMap, calcSiblingSizes(panel.children, originalHeights));
168
- processChildrenSizes(panel.children);
169
- }
170
- });
171
- };
172
- processChildrenSizes(panelTree);
173
- const sizesMap = {};
174
- allPanels.forEach(panel => {
175
- const width = Math.min(widthSizesMap[panel.id] ?? 0, 100);
176
- const height = Math.min(heightSizesMap[panel.id] ?? 0, 100);
177
- sizesMap[panel.id] = {
178
- width: calculateEffectiveVisibility[panel.id] ? `${width.toFixed(2)}%` : '0%',
179
- height: calculateEffectiveVisibility[panel.id] ? `${height.toFixed(2)}%` : '0%',
180
- };
181
- });
182
- return sizesMap;
183
- }, [calculateEffectiveVisibility, panelTree, maximizedPanelId]);
184
- // Ottieni dimensioni di un pannello (override -> dinamico -> default)
185
- const getPanelDimensions = (id) => {
186
- if (panelSizes[id])
187
- return panelSizes[id];
188
- if (dynamicSizesMap[id])
189
- return dynamicSizesMap[id];
190
- const panel = findPanelById(id);
191
- return panel ? { width: panel.contentOptions.width, height: panel.contentOptions.height } : { width: 'auto', height: 'auto' };
192
- };
193
- // Imposta manualmente le dimensioni di un pannello
194
- const updatePanelSize = (id, width, height) => {
195
- setPanelSizes(prev => ({
196
- ...prev,
197
- [id]: {
198
- width: typeof width === 'number' ? `${width}%` : width,
199
- height: typeof height === 'number' ? `${height}%` : height,
200
- },
201
- }));
202
- };
203
- // Quando cambia visibilità o massimizzazione, resetta override
204
- useEffect(() => {
205
- setPanelSizes({});
206
- }, [visibility, maximizedPanelId]);
207
- // Massimizza o ripristina un pannello
208
- const toggleMaximizePanel = (id) => {
209
- setMaximizedPanelId(prev => (prev === id ? null : id));
210
- };
211
- const updatePanelVisibility = (id, visible, currentVisibility) => {
212
- let next = { ...currentVisibility };
213
- if (!visible && maximizedPanelId === id) {
214
- setMaximizedPanelId(null);
215
- }
216
- // Se siamo su mobile, resettiamo tutto tranne il pannello selezionato e i suoi parent
217
- if (isMobile && visible) {
218
- const parents = parentMap.get(id) || [];
219
- // Imposta tutti i pannelli come invisibili tranne il selezionato e i suoi parent
220
- Object.keys(next).forEach(panelId => {
221
- next[panelId] = (panelId === id || parents.includes(panelId));
222
- });
223
- // Applichiamo propagazione verso l’alto (attivazione dei genitori)
224
- const activateParents = (childId) => {
225
- for (const parent of parentMap.get(childId) || []) {
226
- if (!next[parent]) {
227
- next[parent] = true;
228
- activateParents(parent);
229
- }
230
- }
231
- };
232
- activateParents(id);
233
- return next;
234
- }
235
- // Normal behavior (desktop)
236
- next[id] = visible;
237
- if (visible) {
238
- const activateParents = (childId) => {
239
- for (const parent of parentMap.get(childId) || []) {
240
- if (!next[parent]) {
241
- next[parent] = true;
242
- activateParents(parent);
243
- }
244
- }
245
- };
246
- activateParents(id);
247
- }
248
- else {
249
- const deactivateParents = (childId) => {
250
- for (const parent of parentMap.get(childId) || []) {
251
- const children = parentToChildrenMap.get(parent) || [];
252
- const anyVisible = children.some(cId => next[cId]);
253
- if (!anyVisible && next[parent]) {
254
- next[parent] = false;
255
- deactivateParents(parent);
256
- }
257
- }
258
- };
259
- deactivateParents(id);
260
- }
261
- return next;
262
- };
263
- // Attiva/disattiva visibilità di un pannello
264
- const togglePanel = (id) => {
265
- setVisibility(prev => updatePanelVisibility(id, !prev[id], prev));
266
- };
267
- // Imposta visibilità esplicita di un pannello
268
- const setPanelVisibility = (id, visible) => {
269
- setVisibility(prev => updatePanelVisibility(id, visible, prev));
270
- };
271
- // Cambia la visibilità direttamente nel pannello (opzionale, ma richiesto da te)
272
- const setPanelVisibleById = (id, visible) => {
273
- setPanelTree(prevTree => {
274
- const update = (panels) => panels.map(panel => {
275
- if (panel.id === id) {
276
- return { ...panel, toolbarOptions: { ...panel.toolbarOptions, visible } };
277
- }
278
- return { ...panel, children: update(panel.children) };
279
- });
280
- return update(prevTree);
281
- });
282
- };
283
- // Cambia il valore di disabled direttamente nel pannello
284
- const setPanelDisabledById = (id, disabled) => {
285
- setPanelTree(prevTree => {
286
- const update = (panels) => panels.map(panel => {
287
- if (panel.id === id) {
288
- return { ...panel, toolbarOptions: { ...panel.toolbarOptions, disabled, } };
289
- }
290
- return { ...panel, children: update(panel.children) };
291
- });
292
- return update(prevTree);
293
- });
294
- };
295
- const hasVisiblePanels = () => {
296
- return Object.values(calculateEffectiveVisibility).some(visible => visible);
297
- };
298
- // Valori esposti dal context
299
- return (_jsx(TMPanelManagerContext.Provider, { value: {
300
- panelTree,
301
- visibility,
302
- calculateEffectiveVisibility,
303
- setPanelVisibility,
304
- togglePanel,
305
- findPanelById,
306
- getPanelDimensions,
307
- toggleMaximizePanel,
308
- maximizedPanelId,
309
- updatePanelSize,
310
- setPanelVisibleById,
311
- setPanelDisabledById,
312
- hasVisiblePanels
313
- }, children: children }));
314
- };
@@ -1,6 +0,0 @@
1
- import React from 'react';
2
- export declare const TMPanelToolbar: () => import("react/jsx-runtime").JSX.Element;
3
- export declare const StyledToolbarButton: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components/dist/types").Substitute<React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, {
4
- $isActive?: boolean;
5
- $isDisabled?: boolean;
6
- }>> & string;
@@ -1,66 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { useMemo } from 'react';
3
- import { useTMPanelContext } from './TMPanelContext';
4
- import styled from 'styled-components';
5
- import { DeviceType, useDeviceType } from '../../base/TMDeviceProvider';
6
- import TMTooltip from '../../base/TMTooltip';
7
- export const TMPanelToolbar = () => {
8
- const { panelTree, togglePanel, calculateEffectiveVisibility } = useTMPanelContext();
9
- const deviceType = useDeviceType();
10
- let isMobile = useMemo(() => { return deviceType === DeviceType.MOBILE; }, [deviceType]);
11
- // Ricorsivamente trova tutti i pannelli parent (hanno figli)
12
- const getParentIds = (panels, parents = new Set()) => {
13
- for (const panel of panels) {
14
- if (panel.children.length > 0) {
15
- parents.add(panel.id);
16
- getParentIds(panel.children, parents);
17
- }
18
- }
19
- return parents;
20
- };
21
- // Ricorsivamente trova tutti i pannelli foglia (senza figli)
22
- const getLeafPanels = (panels, leaves = []) => {
23
- for (const panel of panels) {
24
- if (panel.children.length === 0) {
25
- leaves.push(panel);
26
- }
27
- else {
28
- getLeafPanels(panel.children, leaves);
29
- }
30
- }
31
- return leaves;
32
- };
33
- const parentIds = getParentIds(panelTree);
34
- const leafPanels = getLeafPanels(panelTree);
35
- return (_jsx("div", { style: {
36
- display: 'flex',
37
- flexDirection: isMobile ? 'row' : 'column',
38
- gap: '6px',
39
- alignItems: 'center',
40
- justifyContent: isMobile ? 'center' : 'flex-start',
41
- width: '100%',
42
- height: '100%'
43
- }, children: leafPanels.filter(panel => panel.toolbarOptions.visible).map((item) => {
44
- const isActive = calculateEffectiveVisibility[item.id];
45
- return _jsx(TMTooltip, { content: item.name, position: isMobile ? 'top' : 'left', children: _jsx(StyledToolbarButton, { "$isActive": isActive || item.toolbarOptions?.alwaysActiveColor, "$isDisabled": item.toolbarOptions?.disabled, disabled: item.toolbarOptions?.disabled, onClick: () => { togglePanel(item.id); }, children: typeof item.toolbarOptions?.icon === 'string' ? (_jsx("i", { className: `dx-icon dx-icon-${item.toolbarOptions?.icon}` })) : (item.toolbarOptions?.icon) }) }, item.id);
46
- }) }));
47
- };
48
- export const StyledToolbarButton = styled.button `
49
- display: flex;
50
- align-items: center;
51
- justify-content: center;
52
- height: 32px;
53
- width: 32px;
54
- border: none;
55
- border-radius: 8px;
56
- font-size: 18px;
57
- padding: 0px;
58
- color: #fff;
59
- transition: transform 0.2s ease, box-shadow 0.2s ease;
60
- cursor: ${({ $isDisabled }) => ($isDisabled ? 'not-allowed' : 'pointer')};
61
- opacity: ${({ $isDisabled }) => ($isDisabled ? 0.6 : 1)};
62
- background: ${({ $isActive }) => $isActive ? 'rgba(255,255,255,0.35)' : 'transparent'};
63
- &:hover {
64
- background: ${({ $isDisabled }) => !$isDisabled ? 'rgba(255,255,255,0.35)' : undefined};
65
- }
66
- `;
@@ -1,3 +0,0 @@
1
- export declare function useResizablePanels(containerRef: React.RefObject<HTMLDivElement | null>): {
2
- onMouseDown: (e: React.MouseEvent, panelBeforeId: string, panelAfterId: string, isRow: boolean) => void;
3
- };
@@ -1,67 +0,0 @@
1
- import { useRef, useCallback } from 'react';
2
- import { useTMPanelContext } from './TMPanelContext';
3
- export function useResizablePanels(containerRef) {
4
- const { getPanelDimensions, updatePanelSize } = useTMPanelContext();
5
- const resizingRef = useRef(null);
6
- const getSizePercent = useCallback((panelId, isRow) => {
7
- const dim = getPanelDimensions(panelId);
8
- return isRow
9
- ? parseFloat(dim.width.replace('%', ''))
10
- : parseFloat(dim.height.replace('%', ''));
11
- }, [getPanelDimensions]);
12
- const getContainerSize = useCallback((isRow) => {
13
- if (!containerRef.current)
14
- return 0;
15
- return isRow ? containerRef.current.clientWidth : containerRef.current.clientHeight;
16
- }, [containerRef]);
17
- const updateSize = useCallback((panelId, sizePercent, otherDimension, isRow) => {
18
- if (isRow) {
19
- updatePanelSize(panelId, `${sizePercent.toFixed(2)}%`, otherDimension);
20
- }
21
- else {
22
- updatePanelSize(panelId, otherDimension, `${sizePercent.toFixed(2)}%`);
23
- }
24
- }, [updatePanelSize]);
25
- const onMouseMove = useCallback((e) => {
26
- const data = resizingRef.current;
27
- if (!data || !containerRef.current)
28
- return;
29
- const { panelBeforeId, panelAfterId, startPos, startSizeBefore, startSizeAfter, isRow } = data;
30
- const containerSize = getContainerSize(isRow);
31
- const currentPos = isRow ? e.clientX : e.clientY;
32
- const delta = currentPos - startPos;
33
- const sizeBeforePx = (startSizeBefore / 100) * containerSize + delta;
34
- const sizeAfterPx = (startSizeAfter / 100) * containerSize - delta;
35
- if (sizeBeforePx < 50 || sizeAfterPx < 50)
36
- return;
37
- const newSizeBefore = (sizeBeforePx / containerSize) * 100;
38
- const newSizeAfter = (sizeAfterPx / containerSize) * 100;
39
- const dimBefore = getPanelDimensions(panelBeforeId);
40
- const dimAfter = getPanelDimensions(panelAfterId);
41
- const otherDimBefore = isRow ? dimBefore.height : dimBefore.width;
42
- const otherDimAfter = isRow ? dimAfter.height : dimAfter.width;
43
- updateSize(panelBeforeId, newSizeBefore, otherDimBefore, isRow);
44
- updateSize(panelAfterId, newSizeAfter, otherDimAfter, isRow);
45
- }, [getContainerSize, getPanelDimensions, updateSize, containerRef]);
46
- const onMouseUp = useCallback(() => {
47
- resizingRef.current = null;
48
- window.removeEventListener('mousemove', onMouseMove);
49
- window.removeEventListener('mouseup', onMouseUp);
50
- }, [onMouseMove]);
51
- const onMouseDown = useCallback((e, panelBeforeId, panelAfterId, isRow) => {
52
- e.preventDefault();
53
- if (!containerRef.current)
54
- return;
55
- resizingRef.current = {
56
- panelBeforeId,
57
- panelAfterId,
58
- isRow,
59
- startPos: isRow ? e.clientX : e.clientY,
60
- startSizeBefore: getSizePercent(panelBeforeId, isRow),
61
- startSizeAfter: getSizePercent(panelAfterId, isRow),
62
- };
63
- window.addEventListener('mousemove', onMouseMove);
64
- window.addEventListener('mouseup', onMouseUp);
65
- }, [getSizePercent, onMouseMove, onMouseUp, containerRef]);
66
- return { onMouseDown };
67
- }