@collabdt/core 0.3.1 → 0.3.2
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.
- package/dist/core/components/authentication/ForgotPassword.d.ts.map +1 -1
- package/dist/core/components/authentication/ForgotPassword.js +0 -1
- package/dist/core/components/authentication/ForgotPassword.js.map +1 -1
- package/dist/core/components/ui/Comments/CollapsibleCommentItem.d.ts +12 -2
- package/dist/core/components/ui/Comments/CollapsibleCommentItem.d.ts.map +1 -1
- package/dist/core/components/ui/Comments/CollapsibleCommentItem.js +137 -139
- package/dist/core/components/ui/Comments/CollapsibleCommentItem.js.map +1 -1
- package/dist/core/components/ui/Comments/Comment.d.ts +18 -1
- package/dist/core/components/ui/Comments/Comment.d.ts.map +1 -1
- package/dist/core/components/ui/Comments/Comment.js +94 -23
- package/dist/core/components/ui/Comments/Comment.js.map +1 -1
- package/dist/core/components/ui/Comments/CommentActionButtons.d.ts +34 -0
- package/dist/core/components/ui/Comments/CommentActionButtons.d.ts.map +1 -0
- package/dist/core/components/ui/Comments/CommentActionButtons.js +106 -0
- package/dist/core/components/ui/Comments/CommentActionButtons.js.map +1 -0
- package/dist/core/components/ui/Comments/CommentInput.d.ts.map +1 -1
- package/dist/core/components/ui/Comments/CommentInput.js +5 -2
- package/dist/core/components/ui/Comments/CommentInput.js.map +1 -1
- package/dist/core/components/ui/Comments/CommentsSection.d.ts.map +1 -1
- package/dist/core/components/ui/Comments/CommentsSection.js +86 -42
- package/dist/core/components/ui/Comments/CommentsSection.js.map +1 -1
- package/dist/core/components/ui/Comments/commentUtils.d.ts.map +1 -1
- package/dist/core/components/ui/Comments/commentUtils.js +2 -1
- package/dist/core/components/ui/Comments/commentUtils.js.map +1 -1
- package/dist/core/components/ui/FilesManager/src/useFileUploadWithProgress.d.ts.map +1 -1
- package/dist/core/components/ui/FilesManager/src/useFileUploadWithProgress.js +0 -1
- package/dist/core/components/ui/FilesManager/src/useFileUploadWithProgress.js.map +1 -1
- package/dist/core/components/ui/InfoSidebar/Container.d.ts.map +1 -1
- package/dist/core/components/ui/InfoSidebar/Container.js +16 -12
- package/dist/core/components/ui/InfoSidebar/Container.js.map +1 -1
- package/dist/core/components/ui/Sensors/SensorInput.d.ts.map +1 -1
- package/dist/core/components/ui/Sensors/SensorInput.js +0 -1
- package/dist/core/components/ui/Sensors/SensorInput.js.map +1 -1
- package/dist/core/components/ui/Sensors/SensorsSection.js +0 -2
- package/dist/core/components/ui/Sensors/SensorsSection.js.map +1 -1
- package/dist/core/components/ui/Sidebar.d.ts.map +1 -1
- package/dist/core/components/ui/Sidebar.js +27 -4
- package/dist/core/components/ui/Sidebar.js.map +1 -1
- package/dist/core/components/ui/ToolbarButton.d.ts.map +1 -1
- package/dist/core/components/ui/ToolbarButton.js +0 -1
- package/dist/core/components/ui/ToolbarButton.js.map +1 -1
- package/dist/core/components/viewers/Data/advancedFilter/FiltersDialog.d.ts.map +1 -1
- package/dist/core/components/viewers/Data/advancedFilter/FiltersDialog.js +0 -2
- package/dist/core/components/viewers/Data/advancedFilter/FiltersDialog.js.map +1 -1
- package/dist/core/components/viewers/Data/details/EnergySourcesGrid.d.ts.map +1 -1
- package/dist/core/components/viewers/Data/details/EnergySourcesGrid.js +0 -10
- package/dist/core/components/viewers/Data/details/EnergySourcesGrid.js.map +1 -1
- package/dist/core/components/viewers/Data/files/DatatableFileAdder.d.ts.map +1 -1
- package/dist/core/components/viewers/Data/files/DatatableFileAdder.js +0 -1
- package/dist/core/components/viewers/Data/files/DatatableFileAdder.js.map +1 -1
- package/dist/core/components/viewers/Data/files/utils/fuzzySearch.d.ts.map +1 -1
- package/dist/core/components/viewers/Data/files/utils/fuzzySearch.js +0 -1
- package/dist/core/components/viewers/Data/files/utils/fuzzySearch.js.map +1 -1
- package/dist/core/components/viewers/Data/userDetails/UserMoreOptions.d.ts.map +1 -1
- package/dist/core/components/viewers/Data/userDetails/UserMoreOptions.js +0 -1
- package/dist/core/components/viewers/Data/userDetails/UserMoreOptions.js.map +1 -1
- package/dist/core/components/viewers/Data/utils/filterHelper.d.ts.map +1 -1
- package/dist/core/components/viewers/Data/utils/filterHelper.js +0 -2
- package/dist/core/components/viewers/Data/utils/filterHelper.js.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/CommunicationTab/src/TopicsSection.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/CommunicationTab/src/TopicsSection.js +0 -3
- package/dist/core/components/viewers/bim/src/BimSidebar/src/CommunicationTab/src/TopicsSection.js.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/FilesSection.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/FilesSection.js +1 -7
- package/dist/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/FilesSection.js.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/ModelsSection.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/ModelsSection.js +1 -9
- package/dist/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/ModelsSection.js.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/TabSelector.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/TabSelector.js +4 -3
- package/dist/core/components/viewers/bim/src/BimSidebar/src/TabSelector.js.map +1 -1
- package/dist/core/components/viewers/bim/src/ClippingPlane.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/ClippingPlane.js +0 -1
- package/dist/core/components/viewers/bim/src/ClippingPlane.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimComment.d.ts +16 -10
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimComment.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimComment.js +26 -6
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimComment.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimCommentCluster.d.ts +23 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimCommentCluster.d.ts.map +1 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimCommentCluster.js +108 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimCommentCluster.js.map +1 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/clusterMarkersByScreenSpace.d.ts +44 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/clusterMarkersByScreenSpace.d.ts.map +1 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/clusterMarkersByScreenSpace.js +77 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/clusterMarkersByScreenSpace.js.map +1 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentCameraUtils.d.ts +24 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentCameraUtils.d.ts.map +1 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentCameraUtils.js +27 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentCameraUtils.js.map +1 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/renderCSS2DMarkers.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/renderCSS2DMarkers.js +2 -2
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/renderCSS2DMarkers.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useCommentMarkers.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useCommentMarkers.js +242 -61
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useCommentMarkers.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/ClippingTool/ClippingTool.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/ClippingTool/ClippingTool.js +1 -5
- package/dist/core/components/viewers/bim/src/tools/ClippingTool/ClippingTool.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/ExplodeByLevelTool.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/ExplodeByLevelTool.js +0 -1
- package/dist/core/components/viewers/bim/src/tools/ExplodeByLevelTool.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/measureBimTool.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/measureBimTool.js +1 -2
- package/dist/core/components/viewers/bim/src/tools/measureBimTool.js.map +1 -1
- package/dist/core/components/viewers/map/MapViewer.d.ts.map +1 -1
- package/dist/core/components/viewers/map/MapViewer.js.map +1 -1
- package/dist/core/components/viewers/map/datasets/src/arcGISDatasets.d.ts.map +1 -1
- package/dist/core/components/viewers/map/datasets/src/arcGISDatasets.js +11 -14
- package/dist/core/components/viewers/map/datasets/src/arcGISDatasets.js.map +1 -1
- package/dist/core/components/viewers/map/datasets/src/ckanDatasets.d.ts.map +1 -1
- package/dist/core/components/viewers/map/datasets/src/ckanDatasets.js +2 -5
- package/dist/core/components/viewers/map/datasets/src/ckanDatasets.js.map +1 -1
- package/dist/core/components/viewers/map/datasets/src/localDatasets.d.ts.map +1 -1
- package/dist/core/components/viewers/map/datasets/src/localDatasets.js +0 -3
- package/dist/core/components/viewers/map/datasets/src/localDatasets.js.map +1 -1
- package/dist/core/components/viewers/map/datasets/src/opendatasoftDatasets.d.ts.map +1 -1
- package/dist/core/components/viewers/map/datasets/src/opendatasoftDatasets.js +0 -11
- package/dist/core/components/viewers/map/datasets/src/opendatasoftDatasets.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapFeaturePopoverMenu/src/DatabaseBuildingPopover/src/BuildingsTools.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapFeaturePopoverMenu/src/DatabaseBuildingPopover/src/BuildingsTools.js +0 -1
- package/dist/core/components/viewers/map/src/MapFeaturePopoverMenu/src/DatabaseBuildingPopover/src/BuildingsTools.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapFeaturePopoverMenu/src/NonDatabaseBuildingPopover.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapFeaturePopoverMenu/src/NonDatabaseBuildingPopover.js +0 -1
- package/dist/core/components/viewers/map/src/MapFeaturePopoverMenu/src/NonDatabaseBuildingPopover.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapFeaturePopoverMenu/src/OpenDataFeaturePopover.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapFeaturePopoverMenu/src/OpenDataFeaturePopover.js +0 -1
- package/dist/core/components/viewers/map/src/MapFeaturePopoverMenu/src/OpenDataFeaturePopover.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/CommentLayer/index.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/CommentLayer/index.js +174 -53
- package/dist/core/components/viewers/map/src/MapLayers/src/CommentLayer/index.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/FileLayer/components/MapFileManager.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/FileLayer/components/MapFileManager.js +2 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/FileLayer/components/MapFileManager.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.js +0 -3
- package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/TabSelector.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/TabSelector.js +4 -3
- package/dist/core/components/viewers/map/src/MapSidebar/TabSelector.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/FileTab/src/FilesSection.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/FileTab/src/FilesSection.js +0 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/FileTab/src/FilesSection.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/FileTab/src/ModelsSection.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/FileTab/src/ModelsSection.js +1 -5
- package/dist/core/components/viewers/map/src/MapSidebar/src/FileTab/src/ModelsSection.js.map +1 -1
- package/dist/core/components/viewers/map/src/tools/AddTools/AddFile/utils/uploadToPresignedURLS.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/tools/AddTools/AddFile/utils/uploadToPresignedURLS.js +1 -3
- package/dist/core/components/viewers/map/src/tools/AddTools/AddFile/utils/uploadToPresignedURLS.js.map +1 -1
- package/dist/core/components/viewers/map/utils/MapEventManager/MapClickManager.d.ts.map +1 -1
- package/dist/core/components/viewers/map/utils/MapEventManager/MapClickManager.js +0 -1
- package/dist/core/components/viewers/map/utils/MapEventManager/MapClickManager.js.map +1 -1
- package/dist/core/components/viewers/map/utils/flyToEach.d.ts.map +1 -1
- package/dist/core/components/viewers/map/utils/flyToEach.js +0 -1
- package/dist/core/components/viewers/map/utils/flyToEach.js.map +1 -1
- package/dist/core/components/viewers/map/utils/getOsmIdbyCoordinates.d.ts.map +1 -1
- package/dist/core/components/viewers/map/utils/getOsmIdbyCoordinates.js +0 -2
- package/dist/core/components/viewers/map/utils/getOsmIdbyCoordinates.js.map +1 -1
- package/dist/core/hooks/comments/comments.d.ts +11 -0
- package/dist/core/hooks/comments/comments.d.ts.map +1 -1
- package/dist/core/hooks/comments/comments.js +3 -1
- package/dist/core/hooks/comments/comments.js.map +1 -1
- package/dist/core/hooks/comments/createCommentHooks.d.ts +11 -0
- package/dist/core/hooks/comments/createCommentHooks.d.ts.map +1 -1
- package/dist/core/hooks/comments/createCommentHooks.js +30 -1
- package/dist/core/hooks/comments/createCommentHooks.js.map +1 -1
- package/dist/core/hooks/files/files.d.ts.map +1 -1
- package/dist/core/hooks/files/files.js +0 -1
- package/dist/core/hooks/files/files.js.map +1 -1
- package/dist/core/hooks/ui/useResizableSidebarWidth.d.ts +30 -0
- package/dist/core/hooks/ui/useResizableSidebarWidth.d.ts.map +1 -0
- package/dist/core/hooks/ui/useResizableSidebarWidth.js +75 -0
- package/dist/core/hooks/ui/useResizableSidebarWidth.js.map +1 -0
- package/dist/core/i18n/messages/en.json +5 -0
- package/dist/core/i18n/messages/es.json +6 -1
- package/dist/core/i18n/messages/fr.json +6 -1
- package/dist/core/store/Datasets/reducer.d.ts.map +1 -1
- package/dist/core/store/Datasets/reducer.js +0 -2
- package/dist/core/store/Datasets/reducer.js.map +1 -1
- package/dist/core/store/Files/reducer.js +0 -1
- package/dist/core/store/Files/reducer.js.map +1 -1
- package/dist/core/store/Menus/context.d.ts.map +1 -1
- package/dist/core/store/Menus/context.js +4 -1
- package/dist/core/store/Menus/context.js.map +1 -1
- package/dist/core/store/Menus/reducer.d.ts +20 -0
- package/dist/core/store/Menus/reducer.d.ts.map +1 -1
- package/dist/core/store/Menus/reducer.js +21 -3
- package/dist/core/store/Menus/reducer.js.map +1 -1
- package/dist/core/utils/markerUtils.d.ts +10 -0
- package/dist/core/utils/markerUtils.d.ts.map +1 -1
- package/dist/core/utils/markerUtils.js +9 -1
- package/dist/core/utils/markerUtils.js.map +1 -1
- package/dist/core/utils/memcache.d.ts.map +1 -1
- package/dist/core/utils/memcache.js +0 -7
- package/dist/core/utils/memcache.js.map +1 -1
- package/package.json +4 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../../../../src/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/FilesSection.tsx"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport * as LR from 'lucide-react'\r\nimport { useTranslations } from 'next-intl'\r\nimport * as React from 'react'\r\nimport * as THREE from 'three'\r\n\r\nimport { useUploadFileToBuilding, useDeleteFile, useFile } from '../../../../../../../../hooks/files/files'\r\nimport { BimContext, BuildingsContext, MenusContext } from '../../../../../../../../store'\r\nimport ConfirmDialog from '../../../../../../../ConfirmDialog'\r\nimport { CollapsibleSection } from '../../../../../../../ui/CollapsibleSection'\r\nimport { useFileUploadHandler, useFileDeleteHandler, FileItemComponent, useFileActions, useCommonFileUpload } from '../../../../../../../ui/FilesManager'\r\nimport { GizmoController } from '../../../../../utils/GizmoController'\r\nimport { BCFTopicsManager } from '../../../../BCFTopicsManager'\r\nimport { CurrentWorld } from '../../../../CurrentWorld'\r\nimport { Cursor } from '../../../../Cursor'\r\nimport { DXFManager } from '../../../../DXFLoader'\r\nimport { Highlighter } from '../../../../Highlighter'\r\nimport { IDSManager } from '../../../../IDSManager'\r\nimport { ModelManager } from '../../../../ModelManager'\r\nimport { createFileMarker, removeMarker, type AddedFile } from '../../../../tools/AddToBim/src/FileMarkerUtils'\r\n\r\nimport type { DbFile as IFile } from '../../../../../../../../types/dbTypes'\r\nimport type { CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js'\r\n\r\n// Hoist options arrays so the array identity is stable across renders.\r\n// Inline `options={[...]}` defeats React.memo on FileItemComponent.\r\ntype FileAction = import('../../../../../../../../types/global').FileAction\r\nconst OPTIONS_3D: FileAction[] = ['download', 'view', 'move', 'info', 'delete']\r\nconst OPTIONS_NON_3D: FileAction[] = ['download', 'view', 'delete']\r\n\r\ninterface FilesSectionProps {\r\n files: IFile[]\r\n query?: string\r\n}\r\n\r\nconst is3DFile = (ext?: string | null): boolean => {\r\n if (!ext) return false\r\n return ['glb', 'gltf', 'fbx', 'obj', 'collada'].includes(ext.toLowerCase())\r\n}\r\n\r\n// Files that live in the 3D scene and can be moved/scaled (3D models + DXF drawings).\r\nconst isPlaceable = (ext?: string | null): boolean => is3DFile(ext) || ext?.toLowerCase() === 'dxf'\r\n\r\nexport function FilesSection({ files, query = '' }: FilesSectionProps) {\r\n const t = useTranslations('FileSelection')\r\n\r\n const { state: bimState, dispatch: bimDispatch } = React.useContext(BimContext)\r\n const { bimComponents, fragments } = bimState.bim\r\n const { state: buildingsState } = React.useContext(BuildingsContext)\r\n const { building } = buildingsState.buildings\r\n const buildingId = building?.id || -1\r\n const { dispatch: menusDispatch } = React.useContext(MenusContext)\r\n\r\n const { uploadFile } = useUploadFileToBuilding(buildingId)\r\n const { deleteFile } = useDeleteFile(buildingId)\r\n\r\n const { handleFileUpload } = useFileUploadHandler({\r\n buildingId,\r\n tag: 'file',\r\n uploadFile,\r\n })\r\n\r\n const { handleDeleteFile } = useFileDeleteHandler({\r\n deleteFile,\r\n onDeleteSuccess: () => {\r\n console.log('File deleted successfully')\r\n },\r\n })\r\n\r\n // Local state for file management with isVisible property\r\n const [localFiles, setLocalFiles] = React.useState<(IFile & { isVisible?: boolean })[]>([])\r\n const [activeIDSFileId, setActiveIDSFileId] = React.useState<number | null>(null)\r\n\r\n // Initialize and sync files\r\n React.useEffect(() => {\r\n setLocalFiles(prevFiles => {\r\n const visibilityMap = new Map(\r\n prevFiles.map(f => [f.id, f.isVisible ?? false])\r\n )\r\n // A file is \"visible\" only if it's actually present and shown in the 3D scene\r\n // (a loaded model, a loaded DXF group, or the active IDS). This mirrors the Map\r\n // viewer, which derives visibility from on-scene state (mapFileIds) rather than\r\n // from the persisted `file.isVisible` flag — so nothing shows as visible on open\r\n // unless it has been toggled on or just placed/added into the scene.\r\n const isFileInScene = (file: IFile): boolean => {\r\n if (file.extension === 'ids') return activeIDSFileId === file.id\r\n if (file.extension === 'dxf') {\r\n return dxfGroupsRef.current.get(file.id.toString())?.visible === true\r\n }\r\n if (is3DFile(file.extension)) {\r\n const info = modelManagerRef.current?.getModelByName(file.name)\r\n return info ? info.model.visible !== false : false\r\n }\r\n return false\r\n }\r\n return files\r\n .filter(file => file.tag !== 'user')\r\n .filter(file => (file as any).type !== 'map-file')\r\n .map(file => {\r\n // Preserve the user's in-session toggle for already-tracked files; for files\r\n // seen for the first time, derive visibility from the actual scene state.\r\n let isVisible = visibilityMap.has(file.id) ? visibilityMap.get(file.id)! : isFileInScene(file)\r\n if (file.extension === 'ids' && activeIDSFileId === file.id) {\r\n isVisible = true\r\n } else if (file.extension === 'ids' && activeIDSFileId !== null && activeIDSFileId !== file.id) {\r\n isVisible = false\r\n }\r\n return { ...file, isVisible }\r\n })\r\n .sort((a, b) => a.name.localeCompare(b.name))\r\n })\r\n }, [files, activeIDSFileId])\r\n\r\n // Listen to IDS reset events\r\n React.useEffect(() => {\r\n if (!bimComponents) return\r\n try {\r\n const idsManager = bimComponents.get(IDSManager)\r\n const handleIDSReset = () => {\r\n setActiveIDSFileId(null)\r\n setLocalFiles(prevFiles =>\r\n prevFiles.map(file =>\r\n file.extension === 'ids' ? { ...file, isVisible: false } : file\r\n )\r\n )\r\n }\r\n idsManager.onReset.add(handleIDSReset)\r\n return () => { idsManager.onReset.remove(handleIDSReset) }\r\n } catch {\r\n return\r\n }\r\n }, [bimComponents])\r\n\r\n const modelManager = React.useMemo(() => {\r\n if (!bimComponents) return null\r\n try { return bimComponents.get(ModelManager) } catch { return null }\r\n }, [bimComponents])\r\n const modelManagerRef = React.useRef(modelManager)\r\n React.useEffect(() => { modelManagerRef.current = modelManager }, [modelManager])\r\n\r\n const dxfManager = React.useMemo(() => {\r\n if (!bimComponents) return null\r\n try { return bimComponents.get(DXFManager) } catch { return null }\r\n }, [bimComponents])\r\n // Loaded DXF groups keyed by file id, so visibility toggles can show/hide them.\r\n const dxfGroupsRef = React.useRef<Map<string, THREE.Group>>(new Map())\r\n\r\n // Floating scene markers (pin + actions card) for visible placeable files.\r\n const markersRef = React.useRef<Map<string, { marker: CSS2DObject; file: IFile }>>(new Map())\r\n // Bumped after an object finishes loading so the marker reconcile effect re-runs.\r\n const [loadedTick, setLoadedTick] = React.useState(0)\r\n\r\n const toggleDxfVisibility = React.useCallback(async (file: IFile, visible: boolean) => {\r\n if (!dxfManager || !bimComponents) return\r\n const world = bimComponents.get(CurrentWorld).world\r\n if (!world) return\r\n\r\n const key = file.id.toString()\r\n const existing = dxfGroupsRef.current.get(key)\r\n if (visible) {\r\n if (existing) { existing.visible = true; return }\r\n try {\r\n const res = await fetch(`/api/presignedUrlDownload/${file.id}`)\r\n if (!res.ok) throw new Error(`Failed to get download URL: ${res.status}`)\r\n const { presignedUrl } = await res.json()\r\n const group = await dxfManager.parse(presignedUrl)\r\n group.name = key\r\n const placed = file.x != null && file.y != null && file.z != null\r\n group.position.copy(placed\r\n ? new THREE.Vector3(file.x as number, file.y as number, file.z as number)\r\n : new THREE.Vector3())\r\n group.scale.setScalar(0.001)\r\n if (file.rotation != null) group.rotation.y = file.rotation as number\r\n world.scene.three.add(group)\r\n dxfGroupsRef.current.set(key, group)\r\n setLoadedTick(t => t + 1)\r\n } catch (err) {\r\n console.error(`[FilesSection] Failed to load DXF \"${file.name}\":`, err)\r\n }\r\n } else if (existing) {\r\n existing.visible = false\r\n }\r\n }, [dxfManager, bimComponents])\r\n\r\n // Gizmo controllers keyed by file name (one gizmo per file at most)\r\n const gizmoControllersRef = React.useRef<Map<string, GizmoController>>(new Map())\r\n\r\n // Track the file ID currently being repositioned so useFile can provide updateFile\r\n const [moveFileId, setMoveFileId] = React.useState<number | null>(null)\r\n const { updateFile } = useFile(moveFileId)\r\n const updateFileRef = React.useRef(updateFile)\r\n React.useEffect(() => { updateFileRef.current = updateFile }, [updateFile])\r\n\r\n const fetchDbFileAsFile = async (file: IFile): Promise<globalThis.File> => {\r\n const response = await fetch(file.url)\r\n const blob = await response.blob()\r\n const mimeType = file.extension === 'ids' ? 'application/xml' : 'application/octet-stream'\r\n return new globalThis.File([blob], file.name, { type: mimeType })\r\n }\r\n\r\n // View handler: IDS, BCF, and 3D file visibility\r\n const handleBimView = React.useCallback(async (file: IFile & { isVisible?: boolean }, newVisibility: boolean) => {\r\n // IDS files\r\n if (file.extension === 'ids') {\r\n const idsManager = bimComponents?.get(IDSManager)\r\n if (!idsManager) return\r\n idsManager.enabled = newVisibility\r\n if (newVisibility) {\r\n setActiveIDSFileId(file.id)\r\n try {\r\n const dbFile = await fetchDbFileAsFile(file)\r\n await idsManager.loadFromFile(dbFile)\r\n await idsManager.loadAndTestIDS()\r\n } catch (error) {\r\n console.error('Error loading IDS file:', error)\r\n }\r\n } else {\r\n setActiveIDSFileId(null)\r\n await idsManager.reset()\r\n }\r\n }\r\n\r\n // BCF files\r\n if (file.extension === 'bcf') {\r\n menusDispatch({ type: 'SET_SIDEBAR_SELECTED_TAB', payload: { selectedTab: 'communication' } })\r\n if (!bimComponents) return\r\n let bcfManager: BCFTopicsManager | undefined\r\n try {\r\n bcfManager = bimComponents.get(BCFTopicsManager)\r\n } catch {\r\n bcfManager = new BCFTopicsManager(bimComponents)\r\n }\r\n if (!bcfManager) return\r\n try {\r\n const { topics } = await bcfManager.loadBCFFromUrl(file.url)\r\n if (topics?.length) {\r\n topics.forEach((topic) => {\r\n const exists = bimState.bim.bcfTopics.find(t => t.guid === topic.guid)\r\n if (!exists) {\r\n bimDispatch({ type: 'ADD_BCF_TOPIC', payload: { bcfTopic: topic } })\r\n }\r\n })\r\n }\r\n } catch (error) {\r\n console.error('Failed to load BCF from URL:', error)\r\n }\r\n }\r\n\r\n // 3D model files (GLB/GLTF/FBX/OBJ) managed by ModelManager\r\n if (is3DFile(file.extension) && modelManager) {\r\n const existingModel = modelManager.getModelByName(file.name)\r\n if (newVisibility) {\r\n if (existingModel) {\r\n // Already in scene — just show it\r\n existingModel.model.visible = true\r\n } else {\r\n // Not yet loaded — get a fresh presigned download URL then load\r\n try {\r\n const res = await fetch(`/api/presignedUrlDownload/${file.id}`)\r\n if (!res.ok) throw new Error(`Failed to get download URL: ${res.status}`)\r\n const { presignedUrl } = await res.json()\r\n const position = (file.x != null && file.y != null && file.z != null)\r\n ? new THREE.Vector3(file.x as number, file.y as number, file.z as number)\r\n : new THREE.Vector3(0, 0, 0)\r\n const rotation = file.rotation != null\r\n ? new THREE.Euler(0, file.rotation, 0)\r\n : undefined\r\n await modelManager.load(presignedUrl, file.id.toString(), file.name, {\r\n position,\r\n rotation,\r\n extension: file.extension ?? undefined,\r\n })\r\n } catch (err) {\r\n console.error(`[FilesSection] Failed to load model \"${file.name}\":`, err)\r\n }\r\n }\r\n } else {\r\n if (existingModel) {\r\n existingModel.model.visible = false\r\n }\r\n }\r\n if (fragments) fragments.core.update(true)\r\n }\r\n\r\n // DXF files managed via DXFManager\r\n if (file.extension === 'dxf') {\r\n await toggleDxfVisibility(file, newVisibility)\r\n if (fragments) fragments.core.update(true)\r\n }\r\n setLoadedTick(t => t + 1)\r\n }, [bimComponents, menusDispatch, bimState, bimDispatch, modelManager, fragments, toggleDxfVisibility])\r\n\r\n const highlighter = React.useMemo(() => {\r\n if (!bimComponents) return null\r\n try { return bimComponents.get(Highlighter) } catch { return null }\r\n }, [bimComponents])\r\n\r\n // Raycast into fragments (same logic as useFilePlacement)\r\n const raycast = React.useCallback(async (data: {\r\n camera: THREE.PerspectiveCamera | THREE.OrthographicCamera\r\n mouse: THREE.Vector2\r\n dom: HTMLCanvasElement\r\n }) => {\r\n if (!fragments || !highlighter) return null\r\n const results = []\r\n for (const [modelName, model] of fragments.core.models.list) {\r\n if (highlighter.isModelDisabled(modelName)) continue\r\n const result = await model.raycast(data)\r\n if (result) results.push(result)\r\n }\r\n await Promise.all(results)\r\n if (results.length === 0) return null\r\n let closest = results[0]\r\n for (let i = 1; i < results.length; i++) {\r\n if (results[i].distance < closest.distance) closest = results[i]\r\n }\r\n return closest\r\n }, [fragments, highlighter])\r\n\r\n // Track the file currently being placed (unplaced file → click-to-place)\r\n const [placingFile, setPlacingFile] = React.useState<IFile | null>(null)\r\n\r\n // Click-to-place effect: crosshair + double-click raycast for unplaced files\r\n React.useEffect(() => {\r\n if (!placingFile || !bimComponents) return\r\n\r\n const world = bimComponents.get(CurrentWorld).world\r\n if (!world) return\r\n\r\n const cursor = bimComponents.get(Cursor)\r\n if (cursor) cursor.cursor = 'crosshair'\r\n\r\n const mouse = new THREE.Vector2()\r\n\r\n const handleDblClick = async (e: MouseEvent) => {\r\n mouse.x = e.clientX\r\n mouse.y = e.clientY\r\n\r\n const result = await raycast({\r\n camera: world.camera.three,\r\n mouse,\r\n dom: world.renderer!.three.domElement!,\r\n })\r\n\r\n if (result?.point && (result.point.x !== 0 || result.point.y !== 0 || result.point.z !== 0)) {\r\n const { x, y, z } = result.point\r\n setMoveFileId(placingFile.id)\r\n // Save coordinates to DB\r\n setTimeout(() => {\r\n updateFileRef.current({ x, y, z } as any)\r\n .catch((err: unknown) => console.error(`Failed to save placement for \"${placingFile.name}\":`, err))\r\n }, 50)\r\n // Load model into scene at the placed position (for 3D files)\r\n if (is3DFile(placingFile.extension) && modelManagerRef.current) {\r\n const existing = modelManagerRef.current.getModelByName(placingFile.name)\r\n if (!existing) {\r\n fetch(`/api/presignedUrlDownload/${placingFile.id}`)\r\n .then(res => res.ok ? res.json() : Promise.reject(res.status))\r\n .then(({ presignedUrl }) =>\r\n modelManagerRef.current!.load(\r\n presignedUrl,\r\n placingFile.id.toString(),\r\n placingFile.name,\r\n { position: new THREE.Vector3(x, y, z), extension: placingFile.extension ?? undefined },\r\n )\r\n )\r\n .catch((err: unknown) => console.error(`Failed to load model \"${placingFile.name}\":`, err))\r\n } else {\r\n existing.model.position.set(x, y, z)\r\n existing.model.visible = true\r\n }\r\n }\r\n // Update local state so it shows as placed and visible\r\n placingFile.x = x\r\n placingFile.y = y\r\n placingFile.z = z\r\n setLocalFiles(prev => prev.map(f => f.id === placingFile.id ? { ...f, x, y, z, isVisible: true } : f))\r\n setPlacingFile(null)\r\n if (cursor) cursor.cursor = ''\r\n }\r\n }\r\n\r\n const handleKeyDown = (e: KeyboardEvent) => {\r\n if (e.key === 'Escape') {\r\n setPlacingFile(null)\r\n if (cursor) cursor.cursor = ''\r\n }\r\n }\r\n\r\n document.addEventListener('dblclick', handleDblClick)\r\n document.addEventListener('keydown', handleKeyDown)\r\n\r\n return () => {\r\n document.removeEventListener('dblclick', handleDblClick)\r\n document.removeEventListener('keydown', handleKeyDown)\r\n if (cursor) cursor.cursor = ''\r\n }\r\n }, [placingFile, bimComponents, raycast])\r\n\r\n // Resolve the scene object for a file (loaded 3D model or DXF group), if present.\r\n const getSceneObject = React.useCallback((file: IFile): THREE.Object3D | null => {\r\n if (file.extension === 'dxf') return dxfGroupsRef.current.get(file.id.toString()) ?? null\r\n return modelManager?.getModelByName(file.name)?.model ?? null\r\n }, [modelManager])\r\n\r\n // Attach a transform gizmo to a placed object (or re-set its mode); saves on accept.\r\n const editObject = React.useCallback((file: IFile, mode: 'translate' | 'rotate' | 'scale' = 'translate') => {\r\n if (!bimComponents) return\r\n const world = bimComponents.get(CurrentWorld).world\r\n if (!world) return\r\n const obj = getSceneObject(file)\r\n if (!obj) return\r\n\r\n const key = file.id.toString()\r\n const existing = gizmoControllersRef.current.get(key)\r\n if (existing) { existing.setMode(mode); return }\r\n\r\n const gizmo = new GizmoController(world)\r\n const cleanup = (save: boolean) => {\r\n if (save) {\r\n const { x, y, z } = obj.position\r\n const rotation = obj.rotation.y\r\n updateFileRef.current({ x, y, z, rotation } as any)\r\n .catch((err: unknown) => console.error(`Failed to save position for \"${file.name}\":`, err))\r\n file.x = x; file.y = y; file.z = z; file.rotation = rotation\r\n }\r\n gizmoControllersRef.current.delete(key)\r\n setMoveFileId(null)\r\n }\r\n gizmo.onAccept = () => cleanup(true)\r\n gizmo.onCancel = () => cleanup(false)\r\n gizmo.setMode(mode)\r\n setMoveFileId(file.id)\r\n gizmo.attach(obj)\r\n gizmoControllersRef.current.set(key, gizmo)\r\n }, [bimComponents, getSceneObject])\r\n\r\n // Move handler: unplaced files → click-to-place; placed files → gizmo (load DXF first if needed)\r\n const handleBimMove = React.useCallback((file: IFile) => {\r\n const isPlaced = file.x != null && file.y != null && file.z != null\r\n if (!isPlaced) {\r\n setPlacingFile(file)\r\n setMoveFileId(file.id)\r\n return\r\n }\r\n if (file.extension === 'dxf' && !dxfGroupsRef.current.has(file.id.toString())) {\r\n void toggleDxfVisibility(file, true).then(() => editObject(file, 'translate'))\r\n return\r\n }\r\n editObject(file, 'translate')\r\n }, [editObject, toggleDxfVisibility])\r\n\r\n const { handleAction, deleteDialog } = useFileActions({\r\n files: localFiles,\r\n setFiles: setLocalFiles,\r\n buildingId,\r\n handleDeleteFile,\r\n onView: handleBimView,\r\n onMove: handleBimMove,\r\n })\r\n\r\n const { handleAddFile } = useCommonFileUpload({\r\n buildingId,\r\n acceptedFileTypes: '*',\r\n handleFileUpload: async (domFile: globalThis.File) => {\r\n await handleFileUpload(domFile)\r\n },\r\n onUploadSuccess: () => {\r\n console.log('File uploaded successfully')\r\n },\r\n onUploadError: (error) => {\r\n console.error('Error uploading file:', error)\r\n },\r\n })\r\n\r\n const filteredFiles = React.useMemo(() => {\r\n if (!query.trim()) return localFiles\r\n return localFiles.filter(file =>\r\n file.name.toLowerCase().includes(query.toLowerCase())\r\n )\r\n }, [localFiles, query])\r\n\r\n const areAllHidden = localFiles.every(f => !f.isVisible)\r\n\r\n const handleSwitchVariant = () => ({\r\n checked: !areAllHidden,\r\n onCheckedChange: async (checked: boolean) => {\r\n setLocalFiles(prev => prev.map(f => ({ ...f, isVisible: checked })))\r\n for (const f of localFiles) {\r\n if (is3DFile(f.extension) && modelManager) {\r\n const existingModel = modelManager.getModelByName(f.name)\r\n if (checked) {\r\n if (existingModel) {\r\n existingModel.model.visible = true\r\n } else {\r\n try {\r\n const res = await fetch(`/api/presignedUrlDownload/${f.id}`)\r\n if (res.ok) {\r\n const { presignedUrl } = await res.json()\r\n const position = (f.x != null && f.y != null && f.z != null)\r\n ? new THREE.Vector3(f.x as number, f.y as number, f.z as number)\r\n : new THREE.Vector3(0, 0, 0)\r\n await modelManager.load(presignedUrl, f.id.toString(), f.name, {\r\n position,\r\n extension: f.extension ?? undefined,\r\n })\r\n }\r\n } catch (err) {\r\n console.error(`[FilesSection] Failed to load model \"${f.name}\":`, err)\r\n }\r\n }\r\n } else if (existingModel) {\r\n existingModel.model.visible = false\r\n }\r\n } else if (f.extension === 'dxf') {\r\n await toggleDxfVisibility(f, checked)\r\n }\r\n }\r\n if (fragments) fragments.core.update(true)\r\n setLoadedTick(t => t + 1)\r\n },\r\n })\r\n\r\n // createFileMarker expects an AddedFile; build a lightweight one from the DB file.\r\n const makeMarkerInput = React.useCallback((file: IFile, position: THREE.Vector3): AddedFile => ({\r\n id: file.id.toString(),\r\n file: new File([], file.name, { type: (file as any).mimeType ?? '' }),\r\n position,\r\n }), [])\r\n\r\n // Reconcile floating markers with the visible + loaded placeable files.\r\n React.useEffect(() => {\r\n if (!bimComponents) return\r\n const world = bimComponents.get(CurrentWorld).world\r\n if (!world) return\r\n\r\n const wanted = new Map(\r\n localFiles\r\n .filter(f => f.isVisible && isPlaceable(f.extension))\r\n .map(f => [f.id.toString(), f] as const),\r\n )\r\n\r\n for (const [key, entry] of markersRef.current) {\r\n if (!wanted.has(key)) {\r\n removeMarker(entry.marker, world)\r\n markersRef.current.delete(key)\r\n }\r\n }\r\n\r\n for (const [key, file] of wanted) {\r\n if (markersRef.current.has(key)) continue\r\n const obj = getSceneObject(file)\r\n if (!obj) continue // not loaded yet — picked up once loadedTick bumps\r\n const marker = createFileMarker(makeMarkerInput(file, obj.position.clone()), obj, world, (action) => {\r\n if (action === 'delete') { handleAction('delete', file); return }\r\n editObject(file, action === 'move' ? 'translate' : action)\r\n })\r\n if (marker) markersRef.current.set(key, { marker, file })\r\n }\r\n }, [localFiles, loadedTick, bimComponents, getSceneObject, editObject, handleAction, makeMarkerInput])\r\n\r\n // Markers follow their object each frame and hide while that object's gizmo is active.\r\n React.useEffect(() => {\r\n if (!bimComponents) return\r\n let raf = 0\r\n const worldPos = new THREE.Vector3()\r\n const tick = () => {\r\n markersRef.current.forEach(({ marker, file }, key) => {\r\n const obj = getSceneObject(file)\r\n if (!obj) { marker.visible = false; return }\r\n obj.getWorldPosition(worldPos)\r\n marker.position.set(worldPos.x, worldPos.y + 0.2, worldPos.z)\r\n marker.visible = !gizmoControllersRef.current.has(key)\r\n })\r\n raf = requestAnimationFrame(tick)\r\n }\r\n raf = requestAnimationFrame(tick)\r\n return () => cancelAnimationFrame(raf)\r\n }, [bimComponents, getSceneObject])\r\n\r\n // Clean up all markers on unmount so their DOM/React roots don't leak.\r\n const markersCleanupRef = React.useRef(markersRef.current)\r\n React.useEffect(() => {\r\n const markers = markersCleanupRef.current\r\n return () => {\r\n markers.forEach(({ marker }) => removeMarker(marker))\r\n markers.clear()\r\n }\r\n }, [])\r\n\r\n return (\r\n <>\r\n <CollapsibleSection\r\n title={t('filesTitle')}\r\n icon={LR.FileText}\r\n className=\"max-h-40 overflow-y-auto\"\r\n itemCount={filteredFiles.length}\r\n switchVariant={handleSwitchVariant()}\r\n onAddItem={handleAddFile}\r\n addItemTitle={t('addFileTitle')}\r\n >\r\n <div className=\"space-y-1\">\r\n {filteredFiles.map((item) => (\r\n <FileItemComponent\r\n key={item.id}\r\n file={item}\r\n onAction={handleAction}\r\n options={isPlaceable(item.extension) ? OPTIONS_3D : OPTIONS_NON_3D}\r\n translationKey=\"FileSelection\"\r\n confirmDelete={false}\r\n />\r\n ))}\r\n </div>\r\n </CollapsibleSection>\r\n\r\n <ConfirmDialog\r\n isOpen={deleteDialog.isOpen}\r\n isDeleting={deleteDialog.isDeleting}\r\n onOpenChange={deleteDialog.onOpenChange}\r\n handleConfirm={deleteDialog.onConfirm}\r\n itemName={deleteDialog.itemName}\r\n />\r\n </>\r\n )\r\n}\r\n"],"mappings":"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","names":["t"]}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../../../src/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/FilesSection.tsx"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport * as LR from 'lucide-react'\r\nimport { useTranslations } from 'next-intl'\r\nimport * as React from 'react'\r\nimport * as THREE from 'three'\r\n\r\nimport { useUploadFileToBuilding, useDeleteFile, useFile } from '../../../../../../../../hooks/files/files'\r\nimport { BimContext, BuildingsContext, MenusContext } from '../../../../../../../../store'\r\nimport ConfirmDialog from '../../../../../../../ConfirmDialog'\r\nimport { CollapsibleSection } from '../../../../../../../ui/CollapsibleSection'\r\nimport { useFileUploadHandler, useFileDeleteHandler, FileItemComponent, useFileActions, useCommonFileUpload } from '../../../../../../../ui/FilesManager'\r\nimport { GizmoController } from '../../../../../utils/GizmoController'\r\nimport { BCFTopicsManager } from '../../../../BCFTopicsManager'\r\nimport { CurrentWorld } from '../../../../CurrentWorld'\r\nimport { Cursor } from '../../../../Cursor'\r\nimport { DXFManager } from '../../../../DXFLoader'\r\nimport { Highlighter } from '../../../../Highlighter'\r\nimport { IDSManager } from '../../../../IDSManager'\r\nimport { ModelManager } from '../../../../ModelManager'\r\nimport { createFileMarker, removeMarker, type AddedFile } from '../../../../tools/AddToBim/src/FileMarkerUtils'\r\n\r\nimport type { DbFile as IFile } from '../../../../../../../../types/dbTypes'\r\nimport type { CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js'\r\n\r\n// Hoist options arrays so the array identity is stable across renders.\r\n// Inline `options={[...]}` defeats React.memo on FileItemComponent.\r\ntype FileAction = import('../../../../../../../../types/global').FileAction\r\nconst OPTIONS_3D: FileAction[] = ['download', 'view', 'move', 'info', 'delete']\r\nconst OPTIONS_NON_3D: FileAction[] = ['download', 'view', 'delete']\r\n\r\ninterface FilesSectionProps {\r\n files: IFile[]\r\n query?: string\r\n}\r\n\r\nconst is3DFile = (ext?: string | null): boolean => {\r\n if (!ext) return false\r\n return ['glb', 'gltf', 'fbx', 'obj', 'collada'].includes(ext.toLowerCase())\r\n}\r\n\r\n// Files that live in the 3D scene and can be moved/scaled (3D models + DXF drawings).\r\nconst isPlaceable = (ext?: string | null): boolean => is3DFile(ext) || ext?.toLowerCase() === 'dxf'\r\n\r\nexport function FilesSection({ files, query = '' }: FilesSectionProps) {\r\n const t = useTranslations('FileSelection')\r\n\r\n const { state: bimState, dispatch: bimDispatch } = React.useContext(BimContext)\r\n const { bimComponents, fragments } = bimState.bim\r\n const { state: buildingsState } = React.useContext(BuildingsContext)\r\n const { building } = buildingsState.buildings\r\n const buildingId = building?.id || -1\r\n const { dispatch: menusDispatch } = React.useContext(MenusContext)\r\n\r\n const { uploadFile } = useUploadFileToBuilding(buildingId)\r\n const { deleteFile } = useDeleteFile(buildingId)\r\n\r\n const { handleFileUpload } = useFileUploadHandler({\r\n buildingId,\r\n tag: 'file',\r\n uploadFile,\r\n })\r\n\r\n const { handleDeleteFile } = useFileDeleteHandler({\r\n deleteFile,\r\n })\r\n\r\n // Local state for file management with isVisible property\r\n const [localFiles, setLocalFiles] = React.useState<(IFile & { isVisible?: boolean })[]>([])\r\n const [activeIDSFileId, setActiveIDSFileId] = React.useState<number | null>(null)\r\n\r\n // Initialize and sync files\r\n React.useEffect(() => {\r\n setLocalFiles(prevFiles => {\r\n const visibilityMap = new Map(\r\n prevFiles.map(f => [f.id, f.isVisible ?? false])\r\n )\r\n // A file is \"visible\" only if it's actually present and shown in the 3D scene\r\n // (a loaded model, a loaded DXF group, or the active IDS). This mirrors the Map\r\n // viewer, which derives visibility from on-scene state (mapFileIds) rather than\r\n // from the persisted `file.isVisible` flag — so nothing shows as visible on open\r\n // unless it has been toggled on or just placed/added into the scene.\r\n const isFileInScene = (file: IFile): boolean => {\r\n if (file.extension === 'ids') return activeIDSFileId === file.id\r\n if (file.extension === 'dxf') {\r\n return dxfGroupsRef.current.get(file.id.toString())?.visible === true\r\n }\r\n if (is3DFile(file.extension)) {\r\n const info = modelManagerRef.current?.getModelByName(file.name)\r\n return info ? info.model.visible !== false : false\r\n }\r\n return false\r\n }\r\n return files\r\n .filter(file => file.tag !== 'user')\r\n .filter(file => (file as any).type !== 'map-file')\r\n .map(file => {\r\n // Preserve the user's in-session toggle for already-tracked files; for files\r\n // seen for the first time, derive visibility from the actual scene state.\r\n let isVisible = visibilityMap.has(file.id) ? visibilityMap.get(file.id)! : isFileInScene(file)\r\n if (file.extension === 'ids' && activeIDSFileId === file.id) {\r\n isVisible = true\r\n } else if (file.extension === 'ids' && activeIDSFileId !== null && activeIDSFileId !== file.id) {\r\n isVisible = false\r\n }\r\n return { ...file, isVisible }\r\n })\r\n .sort((a, b) => a.name.localeCompare(b.name))\r\n })\r\n }, [files, activeIDSFileId])\r\n\r\n // Listen to IDS reset events\r\n React.useEffect(() => {\r\n if (!bimComponents) return\r\n try {\r\n const idsManager = bimComponents.get(IDSManager)\r\n const handleIDSReset = () => {\r\n setActiveIDSFileId(null)\r\n setLocalFiles(prevFiles =>\r\n prevFiles.map(file =>\r\n file.extension === 'ids' ? { ...file, isVisible: false } : file\r\n )\r\n )\r\n }\r\n idsManager.onReset.add(handleIDSReset)\r\n return () => { idsManager.onReset.remove(handleIDSReset) }\r\n } catch {\r\n return\r\n }\r\n }, [bimComponents])\r\n\r\n const modelManager = React.useMemo(() => {\r\n if (!bimComponents) return null\r\n try { return bimComponents.get(ModelManager) } catch { return null }\r\n }, [bimComponents])\r\n const modelManagerRef = React.useRef(modelManager)\r\n React.useEffect(() => { modelManagerRef.current = modelManager }, [modelManager])\r\n\r\n const dxfManager = React.useMemo(() => {\r\n if (!bimComponents) return null\r\n try { return bimComponents.get(DXFManager) } catch { return null }\r\n }, [bimComponents])\r\n // Loaded DXF groups keyed by file id, so visibility toggles can show/hide them.\r\n const dxfGroupsRef = React.useRef<Map<string, THREE.Group>>(new Map())\r\n\r\n // Floating scene markers (pin + actions card) for visible placeable files.\r\n const markersRef = React.useRef<Map<string, { marker: CSS2DObject; file: IFile }>>(new Map())\r\n // Bumped after an object finishes loading so the marker reconcile effect re-runs.\r\n const [loadedTick, setLoadedTick] = React.useState(0)\r\n\r\n const toggleDxfVisibility = React.useCallback(async (file: IFile, visible: boolean) => {\r\n if (!dxfManager || !bimComponents) return\r\n const world = bimComponents.get(CurrentWorld).world\r\n if (!world) return\r\n\r\n const key = file.id.toString()\r\n const existing = dxfGroupsRef.current.get(key)\r\n if (visible) {\r\n if (existing) { existing.visible = true; return }\r\n try {\r\n const res = await fetch(`/api/presignedUrlDownload/${file.id}`)\r\n if (!res.ok) throw new Error(`Failed to get download URL: ${res.status}`)\r\n const { presignedUrl } = await res.json()\r\n const group = await dxfManager.parse(presignedUrl)\r\n group.name = key\r\n const placed = file.x != null && file.y != null && file.z != null\r\n group.position.copy(placed\r\n ? new THREE.Vector3(file.x as number, file.y as number, file.z as number)\r\n : new THREE.Vector3())\r\n group.scale.setScalar(0.001)\r\n if (file.rotation != null) group.rotation.y = file.rotation as number\r\n world.scene.three.add(group)\r\n dxfGroupsRef.current.set(key, group)\r\n setLoadedTick(t => t + 1)\r\n } catch (err) {\r\n console.error(`[FilesSection] Failed to load DXF \"${file.name}\":`, err)\r\n }\r\n } else if (existing) {\r\n existing.visible = false\r\n }\r\n }, [dxfManager, bimComponents])\r\n\r\n // Gizmo controllers keyed by file name (one gizmo per file at most)\r\n const gizmoControllersRef = React.useRef<Map<string, GizmoController>>(new Map())\r\n\r\n // Track the file ID currently being repositioned so useFile can provide updateFile\r\n const [moveFileId, setMoveFileId] = React.useState<number | null>(null)\r\n const { updateFile } = useFile(moveFileId)\r\n const updateFileRef = React.useRef(updateFile)\r\n React.useEffect(() => { updateFileRef.current = updateFile }, [updateFile])\r\n\r\n const fetchDbFileAsFile = async (file: IFile): Promise<globalThis.File> => {\r\n const response = await fetch(file.url)\r\n const blob = await response.blob()\r\n const mimeType = file.extension === 'ids' ? 'application/xml' : 'application/octet-stream'\r\n return new globalThis.File([blob], file.name, { type: mimeType })\r\n }\r\n\r\n // View handler: IDS, BCF, and 3D file visibility\r\n const handleBimView = React.useCallback(async (file: IFile & { isVisible?: boolean }, newVisibility: boolean) => {\r\n // IDS files\r\n if (file.extension === 'ids') {\r\n const idsManager = bimComponents?.get(IDSManager)\r\n if (!idsManager) return\r\n idsManager.enabled = newVisibility\r\n if (newVisibility) {\r\n setActiveIDSFileId(file.id)\r\n try {\r\n const dbFile = await fetchDbFileAsFile(file)\r\n await idsManager.loadFromFile(dbFile)\r\n await idsManager.loadAndTestIDS()\r\n } catch (error) {\r\n console.error('Error loading IDS file:', error)\r\n }\r\n } else {\r\n setActiveIDSFileId(null)\r\n await idsManager.reset()\r\n }\r\n }\r\n\r\n // BCF files\r\n if (file.extension === 'bcf') {\r\n menusDispatch({ type: 'SET_SIDEBAR_SELECTED_TAB', payload: { selectedTab: 'communication' } })\r\n if (!bimComponents) return\r\n let bcfManager: BCFTopicsManager | undefined\r\n try {\r\n bcfManager = bimComponents.get(BCFTopicsManager)\r\n } catch {\r\n bcfManager = new BCFTopicsManager(bimComponents)\r\n }\r\n if (!bcfManager) return\r\n try {\r\n const { topics } = await bcfManager.loadBCFFromUrl(file.url)\r\n if (topics?.length) {\r\n topics.forEach((topic) => {\r\n const exists = bimState.bim.bcfTopics.find(t => t.guid === topic.guid)\r\n if (!exists) {\r\n bimDispatch({ type: 'ADD_BCF_TOPIC', payload: { bcfTopic: topic } })\r\n }\r\n })\r\n }\r\n } catch (error) {\r\n console.error('Failed to load BCF from URL:', error)\r\n }\r\n }\r\n\r\n // 3D model files (GLB/GLTF/FBX/OBJ) managed by ModelManager\r\n if (is3DFile(file.extension) && modelManager) {\r\n const existingModel = modelManager.getModelByName(file.name)\r\n if (newVisibility) {\r\n if (existingModel) {\r\n // Already in scene — just show it\r\n existingModel.model.visible = true\r\n } else {\r\n // Not yet loaded — get a fresh presigned download URL then load\r\n try {\r\n const res = await fetch(`/api/presignedUrlDownload/${file.id}`)\r\n if (!res.ok) throw new Error(`Failed to get download URL: ${res.status}`)\r\n const { presignedUrl } = await res.json()\r\n const position = (file.x != null && file.y != null && file.z != null)\r\n ? new THREE.Vector3(file.x as number, file.y as number, file.z as number)\r\n : new THREE.Vector3(0, 0, 0)\r\n const rotation = file.rotation != null\r\n ? new THREE.Euler(0, file.rotation, 0)\r\n : undefined\r\n await modelManager.load(presignedUrl, file.id.toString(), file.name, {\r\n position,\r\n rotation,\r\n extension: file.extension ?? undefined,\r\n })\r\n } catch (err) {\r\n console.error(`[FilesSection] Failed to load model \"${file.name}\":`, err)\r\n }\r\n }\r\n } else {\r\n if (existingModel) {\r\n existingModel.model.visible = false\r\n }\r\n }\r\n if (fragments) fragments.core.update(true)\r\n }\r\n\r\n // DXF files managed via DXFManager\r\n if (file.extension === 'dxf') {\r\n await toggleDxfVisibility(file, newVisibility)\r\n if (fragments) fragments.core.update(true)\r\n }\r\n setLoadedTick(t => t + 1)\r\n }, [bimComponents, menusDispatch, bimState, bimDispatch, modelManager, fragments, toggleDxfVisibility])\r\n\r\n const highlighter = React.useMemo(() => {\r\n if (!bimComponents) return null\r\n try { return bimComponents.get(Highlighter) } catch { return null }\r\n }, [bimComponents])\r\n\r\n // Raycast into fragments (same logic as useFilePlacement)\r\n const raycast = React.useCallback(async (data: {\r\n camera: THREE.PerspectiveCamera | THREE.OrthographicCamera\r\n mouse: THREE.Vector2\r\n dom: HTMLCanvasElement\r\n }) => {\r\n if (!fragments || !highlighter) return null\r\n const results = []\r\n for (const [modelName, model] of fragments.core.models.list) {\r\n if (highlighter.isModelDisabled(modelName)) continue\r\n const result = await model.raycast(data)\r\n if (result) results.push(result)\r\n }\r\n await Promise.all(results)\r\n if (results.length === 0) return null\r\n let closest = results[0]\r\n for (let i = 1; i < results.length; i++) {\r\n if (results[i].distance < closest.distance) closest = results[i]\r\n }\r\n return closest\r\n }, [fragments, highlighter])\r\n\r\n // Track the file currently being placed (unplaced file → click-to-place)\r\n const [placingFile, setPlacingFile] = React.useState<IFile | null>(null)\r\n\r\n // Click-to-place effect: crosshair + double-click raycast for unplaced files\r\n React.useEffect(() => {\r\n if (!placingFile || !bimComponents) return\r\n\r\n const world = bimComponents.get(CurrentWorld).world\r\n if (!world) return\r\n\r\n const cursor = bimComponents.get(Cursor)\r\n if (cursor) cursor.cursor = 'crosshair'\r\n\r\n const mouse = new THREE.Vector2()\r\n\r\n const handleDblClick = async (e: MouseEvent) => {\r\n mouse.x = e.clientX\r\n mouse.y = e.clientY\r\n\r\n const result = await raycast({\r\n camera: world.camera.three,\r\n mouse,\r\n dom: world.renderer!.three.domElement!,\r\n })\r\n\r\n if (result?.point && (result.point.x !== 0 || result.point.y !== 0 || result.point.z !== 0)) {\r\n const { x, y, z } = result.point\r\n setMoveFileId(placingFile.id)\r\n // Save coordinates to DB\r\n setTimeout(() => {\r\n updateFileRef.current({ x, y, z } as any)\r\n .catch((err: unknown) => console.error(`Failed to save placement for \"${placingFile.name}\":`, err))\r\n }, 50)\r\n // Load model into scene at the placed position (for 3D files)\r\n if (is3DFile(placingFile.extension) && modelManagerRef.current) {\r\n const existing = modelManagerRef.current.getModelByName(placingFile.name)\r\n if (!existing) {\r\n fetch(`/api/presignedUrlDownload/${placingFile.id}`)\r\n .then(res => res.ok ? res.json() : Promise.reject(res.status))\r\n .then(({ presignedUrl }) =>\r\n modelManagerRef.current!.load(\r\n presignedUrl,\r\n placingFile.id.toString(),\r\n placingFile.name,\r\n { position: new THREE.Vector3(x, y, z), extension: placingFile.extension ?? undefined },\r\n )\r\n )\r\n .catch((err: unknown) => console.error(`Failed to load model \"${placingFile.name}\":`, err))\r\n } else {\r\n existing.model.position.set(x, y, z)\r\n existing.model.visible = true\r\n }\r\n }\r\n // Update local state so it shows as placed and visible\r\n placingFile.x = x\r\n placingFile.y = y\r\n placingFile.z = z\r\n setLocalFiles(prev => prev.map(f => f.id === placingFile.id ? { ...f, x, y, z, isVisible: true } : f))\r\n setPlacingFile(null)\r\n if (cursor) cursor.cursor = ''\r\n }\r\n }\r\n\r\n const handleKeyDown = (e: KeyboardEvent) => {\r\n if (e.key === 'Escape') {\r\n setPlacingFile(null)\r\n if (cursor) cursor.cursor = ''\r\n }\r\n }\r\n\r\n document.addEventListener('dblclick', handleDblClick)\r\n document.addEventListener('keydown', handleKeyDown)\r\n\r\n return () => {\r\n document.removeEventListener('dblclick', handleDblClick)\r\n document.removeEventListener('keydown', handleKeyDown)\r\n if (cursor) cursor.cursor = ''\r\n }\r\n }, [placingFile, bimComponents, raycast])\r\n\r\n // Resolve the scene object for a file (loaded 3D model or DXF group), if present.\r\n const getSceneObject = React.useCallback((file: IFile): THREE.Object3D | null => {\r\n if (file.extension === 'dxf') return dxfGroupsRef.current.get(file.id.toString()) ?? null\r\n return modelManager?.getModelByName(file.name)?.model ?? null\r\n }, [modelManager])\r\n\r\n // Attach a transform gizmo to a placed object (or re-set its mode); saves on accept.\r\n const editObject = React.useCallback((file: IFile, mode: 'translate' | 'rotate' | 'scale' = 'translate') => {\r\n if (!bimComponents) return\r\n const world = bimComponents.get(CurrentWorld).world\r\n if (!world) return\r\n const obj = getSceneObject(file)\r\n if (!obj) return\r\n\r\n const key = file.id.toString()\r\n const existing = gizmoControllersRef.current.get(key)\r\n if (existing) { existing.setMode(mode); return }\r\n\r\n const gizmo = new GizmoController(world)\r\n const cleanup = (save: boolean) => {\r\n if (save) {\r\n const { x, y, z } = obj.position\r\n const rotation = obj.rotation.y\r\n updateFileRef.current({ x, y, z, rotation } as any)\r\n .catch((err: unknown) => console.error(`Failed to save position for \"${file.name}\":`, err))\r\n file.x = x; file.y = y; file.z = z; file.rotation = rotation\r\n }\r\n gizmoControllersRef.current.delete(key)\r\n setMoveFileId(null)\r\n }\r\n gizmo.onAccept = () => cleanup(true)\r\n gizmo.onCancel = () => cleanup(false)\r\n gizmo.setMode(mode)\r\n setMoveFileId(file.id)\r\n gizmo.attach(obj)\r\n gizmoControllersRef.current.set(key, gizmo)\r\n }, [bimComponents, getSceneObject])\r\n\r\n // Move handler: unplaced files → click-to-place; placed files → gizmo (load DXF first if needed)\r\n const handleBimMove = React.useCallback((file: IFile) => {\r\n const isPlaced = file.x != null && file.y != null && file.z != null\r\n if (!isPlaced) {\r\n setPlacingFile(file)\r\n setMoveFileId(file.id)\r\n return\r\n }\r\n if (file.extension === 'dxf' && !dxfGroupsRef.current.has(file.id.toString())) {\r\n void toggleDxfVisibility(file, true).then(() => editObject(file, 'translate'))\r\n return\r\n }\r\n editObject(file, 'translate')\r\n }, [editObject, toggleDxfVisibility])\r\n\r\n const { handleAction, deleteDialog } = useFileActions({\r\n files: localFiles,\r\n setFiles: setLocalFiles,\r\n buildingId,\r\n handleDeleteFile,\r\n onView: handleBimView,\r\n onMove: handleBimMove,\r\n })\r\n\r\n const { handleAddFile } = useCommonFileUpload({\r\n buildingId,\r\n acceptedFileTypes: '*',\r\n handleFileUpload: async (domFile: globalThis.File) => {\r\n await handleFileUpload(domFile)\r\n },\r\n onUploadError: (error) => {\r\n console.error('Error uploading file:', error)\r\n },\r\n })\r\n\r\n const filteredFiles = React.useMemo(() => {\r\n if (!query.trim()) return localFiles\r\n return localFiles.filter(file =>\r\n file.name.toLowerCase().includes(query.toLowerCase())\r\n )\r\n }, [localFiles, query])\r\n\r\n const areAllHidden = localFiles.every(f => !f.isVisible)\r\n\r\n const handleSwitchVariant = () => ({\r\n checked: !areAllHidden,\r\n onCheckedChange: async (checked: boolean) => {\r\n setLocalFiles(prev => prev.map(f => ({ ...f, isVisible: checked })))\r\n for (const f of localFiles) {\r\n if (is3DFile(f.extension) && modelManager) {\r\n const existingModel = modelManager.getModelByName(f.name)\r\n if (checked) {\r\n if (existingModel) {\r\n existingModel.model.visible = true\r\n } else {\r\n try {\r\n const res = await fetch(`/api/presignedUrlDownload/${f.id}`)\r\n if (res.ok) {\r\n const { presignedUrl } = await res.json()\r\n const position = (f.x != null && f.y != null && f.z != null)\r\n ? new THREE.Vector3(f.x as number, f.y as number, f.z as number)\r\n : new THREE.Vector3(0, 0, 0)\r\n await modelManager.load(presignedUrl, f.id.toString(), f.name, {\r\n position,\r\n extension: f.extension ?? undefined,\r\n })\r\n }\r\n } catch (err) {\r\n console.error(`[FilesSection] Failed to load model \"${f.name}\":`, err)\r\n }\r\n }\r\n } else if (existingModel) {\r\n existingModel.model.visible = false\r\n }\r\n } else if (f.extension === 'dxf') {\r\n await toggleDxfVisibility(f, checked)\r\n }\r\n }\r\n if (fragments) fragments.core.update(true)\r\n setLoadedTick(t => t + 1)\r\n },\r\n })\r\n\r\n // createFileMarker expects an AddedFile; build a lightweight one from the DB file.\r\n const makeMarkerInput = React.useCallback((file: IFile, position: THREE.Vector3): AddedFile => ({\r\n id: file.id.toString(),\r\n file: new File([], file.name, { type: (file as any).mimeType ?? '' }),\r\n position,\r\n }), [])\r\n\r\n // Reconcile floating markers with the visible + loaded placeable files.\r\n React.useEffect(() => {\r\n if (!bimComponents) return\r\n const world = bimComponents.get(CurrentWorld).world\r\n if (!world) return\r\n\r\n const wanted = new Map(\r\n localFiles\r\n .filter(f => f.isVisible && isPlaceable(f.extension))\r\n .map(f => [f.id.toString(), f] as const),\r\n )\r\n\r\n for (const [key, entry] of markersRef.current) {\r\n if (!wanted.has(key)) {\r\n removeMarker(entry.marker, world)\r\n markersRef.current.delete(key)\r\n }\r\n }\r\n\r\n for (const [key, file] of wanted) {\r\n if (markersRef.current.has(key)) continue\r\n const obj = getSceneObject(file)\r\n if (!obj) continue // not loaded yet — picked up once loadedTick bumps\r\n const marker = createFileMarker(makeMarkerInput(file, obj.position.clone()), obj, world, (action) => {\r\n if (action === 'delete') { handleAction('delete', file); return }\r\n editObject(file, action === 'move' ? 'translate' : action)\r\n })\r\n if (marker) markersRef.current.set(key, { marker, file })\r\n }\r\n }, [localFiles, loadedTick, bimComponents, getSceneObject, editObject, handleAction, makeMarkerInput])\r\n\r\n // Markers follow their object each frame and hide while that object's gizmo is active.\r\n React.useEffect(() => {\r\n if (!bimComponents) return\r\n let raf = 0\r\n const worldPos = new THREE.Vector3()\r\n const tick = () => {\r\n markersRef.current.forEach(({ marker, file }, key) => {\r\n const obj = getSceneObject(file)\r\n if (!obj) { marker.visible = false; return }\r\n obj.getWorldPosition(worldPos)\r\n marker.position.set(worldPos.x, worldPos.y + 0.2, worldPos.z)\r\n marker.visible = !gizmoControllersRef.current.has(key)\r\n })\r\n raf = requestAnimationFrame(tick)\r\n }\r\n raf = requestAnimationFrame(tick)\r\n return () => cancelAnimationFrame(raf)\r\n }, [bimComponents, getSceneObject])\r\n\r\n // Clean up all markers on unmount so their DOM/React roots don't leak.\r\n const markersCleanupRef = React.useRef(markersRef.current)\r\n React.useEffect(() => {\r\n const markers = markersCleanupRef.current\r\n return () => {\r\n markers.forEach(({ marker }) => removeMarker(marker))\r\n markers.clear()\r\n }\r\n }, [])\r\n\r\n return (\r\n <>\r\n <CollapsibleSection\r\n title={t('filesTitle')}\r\n icon={LR.FileText}\r\n className=\"max-h-40 overflow-y-auto\"\r\n itemCount={filteredFiles.length}\r\n switchVariant={handleSwitchVariant()}\r\n onAddItem={handleAddFile}\r\n addItemTitle={t('addFileTitle')}\r\n >\r\n <div className=\"space-y-1\">\r\n {filteredFiles.map((item) => (\r\n <FileItemComponent\r\n key={item.id}\r\n file={item}\r\n onAction={handleAction}\r\n options={isPlaceable(item.extension) ? OPTIONS_3D : OPTIONS_NON_3D}\r\n translationKey=\"FileSelection\"\r\n confirmDelete={false}\r\n />\r\n ))}\r\n </div>\r\n </CollapsibleSection>\r\n\r\n <ConfirmDialog\r\n isOpen={deleteDialog.isOpen}\r\n isDeleting={deleteDialog.isDeleting}\r\n onOpenChange={deleteDialog.onOpenChange}\r\n handleConfirm={deleteDialog.onConfirm}\r\n itemName={deleteDialog.itemName}\r\n />\r\n </>\r\n )\r\n}\r\n"],"mappings":"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","names":["t"]}
|
package/dist/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/ModelsSection.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ModelsSection.d.ts","sourceRoot":"","sources":["../../../../../../../../../../src/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/ModelsSection.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAc9B,OAAO,KAAK,EAAE,MAAM,IAAI,MAAM,EAAE,MAAM,uCAAuC,CAAA;AAI7E,UAAU,kBAAkB;IAC1B,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,wBAAgB,aAAa,CAAC,EAAE,KAAK,EAAE,KAAU,EAAE,EAAE,kBAAkB,
|
|
1
|
+
{"version":3,"file":"ModelsSection.d.ts","sourceRoot":"","sources":["../../../../../../../../../../src/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/ModelsSection.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAc9B,OAAO,KAAK,EAAE,MAAM,IAAI,MAAM,EAAE,MAAM,uCAAuC,CAAA;AAI7E,UAAU,kBAAkB;IAC1B,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,wBAAgB,aAAa,CAAC,EAAE,KAAK,EAAE,KAAU,EAAE,EAAE,kBAAkB,qBAgTtE"}
|
|
@@ -143,12 +143,7 @@ function ModelsSection({ files, query = "" }) {
|
|
|
143
143
|
const handleBimDelete = React.useCallback((file) => {
|
|
144
144
|
if (modelManager) {
|
|
145
145
|
const modelIdStr = file.id.toString();
|
|
146
|
-
|
|
147
|
-
console.log("Attempting to remove model", model, modelIdStr);
|
|
148
|
-
const success = modelManager.remove(modelIdStr);
|
|
149
|
-
if (success) {
|
|
150
|
-
console.log(`Model ${modelIdStr} removed from BIM scene`);
|
|
151
|
-
}
|
|
146
|
+
modelManager.remove(modelIdStr);
|
|
152
147
|
}
|
|
153
148
|
if (fragments) {
|
|
154
149
|
const fragModel = fragments.core.models.list.get(file.name);
|
|
@@ -231,9 +226,6 @@ function ModelsSection({ files, query = "" }) {
|
|
|
231
226
|
buildingId,
|
|
232
227
|
acceptedFileTypes: ".ifc,.frag",
|
|
233
228
|
handleFileUpload,
|
|
234
|
-
onUploadSuccess: () => {
|
|
235
|
-
console.log("Model uploaded successfully");
|
|
236
|
-
},
|
|
237
229
|
onUploadError: (error) => {
|
|
238
230
|
console.error("Error uploading model:", error);
|
|
239
231
|
}
|
package/dist/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/ModelsSection.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../../../../src/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/ModelsSection.tsx"],"sourcesContent":["'use client'\r\n\r\n// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport * as LR from 'lucide-react'\r\nimport { useTranslations } from 'next-intl'\r\nimport * as React from 'react'\r\n\r\nimport { useUploadFileToBuilding, useDeleteFile, useFile } from '../../../../../../../../hooks/files/files'\r\nimport { BimContext, BuildingsContext } from '../../../../../../../../store'\r\nimport ConfirmDialog from '../../../../../../../ConfirmDialog'\r\nimport { CollapsibleSection } from '../../../../../../../ui/CollapsibleSection'\r\nimport { useFileUploadHandler, useFileDeleteHandler, FileItemComponent, useFileActions, useCommonFileUpload } from '../../../../../../../ui/FilesManager'\r\nimport { GizmoController } from '../../../../../utils/GizmoController'\r\nimport { BIMManager } from '../../../../BIMManager'\r\nimport { CurrentWorld } from '../../../../CurrentWorld'\r\nimport { GhostMode } from '../../../../GhostMode'\r\nimport { Highlighter } from '../../../../Highlighter'\r\nimport { ModelManager } from '../../../../ModelManager'\r\n\r\nimport type { DbFile as DbFile } from '../../../../../../../../types/dbTypes'\r\n\r\nconst BIM_MODEL_OPTIONS: import('../../../../../../../../types/global').FileAction[] = ['view', 'ghost', 'move', 'info', 'delete']\r\n\r\ninterface ModelsSectionProps {\r\n files: DbFile[]\r\n query?: string\r\n}\r\n\r\nexport function ModelsSection({ files, query = '' }: ModelsSectionProps) {\r\n // Translation\r\n const t = useTranslations('FileItemComponent')\r\n\r\n // Get BIM context\r\n const { state: bimState, dispatch: bimDispatch } = React.useContext(BimContext)\r\n const { bimComponents, fragments, modelId, modelUIState } = bimState.bim\r\n\r\n // Get Buildings context for buildingId\r\n const { state: buildingsState } = React.useContext(BuildingsContext)\r\n const { building } = buildingsState.buildings\r\n const buildingId = building?.id || 0\r\n\r\n // Upload file hook and session\r\n const { uploadFile } = useUploadFileToBuilding(buildingId)\r\n const { deleteFile } = useDeleteFile(buildingId)\r\n\r\n // Use the reusable upload handler\r\n const { handleFileUpload } = useFileUploadHandler({\r\n buildingId,\r\n tag: 'bim-file',\r\n isVisible: true,\r\n uploadFile,\r\n })\r\n\r\n // Use the reusable delete handler\r\n const { handleDeleteFile } = useFileDeleteHandler({\r\n deleteFile,\r\n\r\n })\r\n\r\n // Local state for file management with isVisible property\r\n const [loadedModels, setLoadedModels] = React.useState<(DbFile & { isVisible?: boolean })[]>(\r\n files.map(file => ({\r\n ...file,\r\n isVisible: modelUIState[file.id]?.isVisible ?? (file as any).isVisible,\r\n isGhost: modelUIState[file.id]?.isGhost ?? false,\r\n }))\r\n )\r\n\r\n // Keep local list in sync with incoming props and store UI state changes\r\n React.useEffect(() => {\r\n setLoadedModels(prev => {\r\n const prevMap = new Map(prev.map(f => [f.id, f as any]))\r\n return files.map(file => ({\r\n ...file,\r\n isVisible: modelUIState[file.id]?.isVisible ?? (file as any).isVisible,\r\n isGhost: modelUIState[file.id]?.isGhost ?? prevMap.get(file.id)?.isGhost ?? false,\r\n }))\r\n })\r\n }, [files, modelUIState])\r\n\r\n // Get ModelManager from BIM components\r\n const modelManager = React.useMemo(() => {\r\n if (!bimComponents) return null\r\n try {\r\n return bimComponents.get(ModelManager)\r\n }\r\n catch {\r\n return null\r\n }\r\n }, [bimComponents])\r\n\r\n // Get BIMManager from BIM components\r\n const bimManager = React.useMemo(() => {\r\n if (!bimComponents) return null\r\n try {\r\n return bimComponents.get(BIMManager)\r\n }\r\n catch {\r\n return null\r\n }\r\n }, [bimComponents])\r\n\r\n // Get GhostMode from BIM components\r\n const ghostMode = React.useMemo(() => {\r\n if (!bimComponents) return null\r\n try {\r\n return bimComponents.get(GhostMode)\r\n }\r\n catch {\r\n return null\r\n }\r\n }, [bimComponents])\r\n\r\n // Gizmo controllers keyed by model's file.name (one gizmo per model at most)\r\n const gizmoControllersRef = React.useRef<Map<string, GizmoController>>(new Map())\r\n\r\n // Track the file ID currently being repositioned so useFile can provide updateFile\r\n const [moveFileId, setMoveFileId] = React.useState<number | null>(null)\r\n const { updateFile } = useFile(moveFileId)\r\n const updateFileRef = React.useRef(updateFile)\r\n React.useEffect(() => { updateFileRef.current = updateFile }, [updateFile])\r\n\r\n // Get Highlighter from BIM components\r\n const highlighter = React.useMemo(() => {\r\n if (!bimComponents) return null\r\n try {\r\n return bimComponents.get(Highlighter)\r\n }\r\n catch {\r\n return null\r\n }\r\n }, [bimComponents])\r\n\r\n // Custom handlers for BIM-specific actions (view and delete only - download uses default)\r\n const handleBimView = React.useCallback((file: DbFile, newVisibility: boolean) => {\r\n bimDispatch({ type: 'SET_MODEL_UI_STATE', payload: { fileId: file.id, isVisible: newVisibility } })\r\n\r\n // Disable highlighting for hidden models; re-enable only when visible and not ghosted\r\n if (highlighter) {\r\n const isGhosted = modelUIState[file.id]?.isGhost ?? false\r\n if (!newVisibility || isGhosted) {\r\n highlighter.disableModel(file.name)\r\n } else {\r\n highlighter.enableModel(file.name)\r\n }\r\n }\r\n\r\n // Non-fragment models (gltf/obj/fbx) managed by ModelManager\r\n if (modelManager) {\r\n const modelInfo = modelManager.getModel(file.id.toString())\r\n if (modelInfo) {\r\n modelInfo.model.visible = newVisibility\r\n if (fragments) fragments.core.update(true)\r\n return\r\n }\r\n }\r\n\r\n // Fragment (IFC/FRAG) models — look up directly from fragments list by file.name\r\n if (fragments) {\r\n const fragModel = fragments.core.models.list.get(file.name)\r\n if (fragModel) {\r\n fragModel.object.visible = newVisibility\r\n fragments.core.update(true)\r\n } else {\r\n }\r\n }\r\n }, [modelManager, fragments, highlighter, modelUIState])\r\n\r\n const handleBimDelete = React.useCallback((file: DbFile) => {\r\n // Remove from ModelManager if it exists (gltf/obj/fbx models)\r\n if (modelManager) {\r\n const modelIdStr = file.id.toString()\r\n const model = modelManager.getModel(modelIdStr)\r\n console.log('Attempting to remove model', model, modelIdStr)\r\n const success = modelManager.remove(modelIdStr)\r\n if (success) {\r\n console.log(`Model ${modelIdStr} removed from BIM scene`)\r\n }\r\n }\r\n\r\n // Remove fragment model from scene if it exists (ifc/frag models)\r\n if (fragments) {\r\n const fragModel = fragments.core.models.list.get(file.name)\r\n if (fragModel) {\r\n fragments.core.disposeModel(fragModel.modelId).catch((err: unknown) => {\r\n console.error(`Failed to dispose fragment model \"${file.name}\":`, err)\r\n })\r\n // Also clean up BIMManager tracking\r\n bimManager?.remove(file.name)\r\n }\r\n }\r\n }, [modelManager, bimComponents, fragments, bimManager])\r\n\r\n const handleBimMove = React.useCallback((file: DbFile) => {\r\n if (!bimComponents || !fragments) return\r\n\r\n const world = bimComponents.get(CurrentWorld).world\r\n if (!world) return\r\n\r\n const fragModel = fragments.core.models.list.get(file.name)\r\n if (!fragModel) {\r\n console.warn(`[BimMove] Model not found: \"${file.name}\"`)\r\n return\r\n }\r\n\r\n const existing = gizmoControllersRef.current.get(file.name)\r\n if (existing) {\r\n // Toggle off: destroy the gizmo\r\n existing.dispose()\r\n gizmoControllersRef.current.delete(file.name)\r\n if (gizmoControllersRef.current.size === 0 && highlighter) highlighter.enabled = true\r\n } else {\r\n // Toggle on: create a new gizmo attached to the model's root object\r\n const gizmo = new GizmoController(world)\r\n const cleanupGizmo = (savePosition: boolean) => {\r\n if (savePosition) {\r\n const { x, y, z } = fragModel.object.position\r\n const rotation = fragModel.object.rotation.y\r\n updateFileRef.current({ x, y, z, rotation } as any)\r\n .catch((err: unknown) => console.error(`[BimMove] Failed to save position for \"${file.name}\":`, err))\r\n file.x = x\r\n file.y = y\r\n file.z = z\r\n file.rotation = rotation\r\n }\r\n gizmoControllersRef.current.delete(file.name)\r\n if (gizmoControllersRef.current.size === 0 && highlighter) highlighter.enabled = true\r\n setMoveFileId(null)\r\n }\r\n gizmo.onAccept = () => cleanupGizmo(true)\r\n gizmo.onCancel = () => cleanupGizmo(false)\r\n setMoveFileId(file.id)\r\n if (highlighter) highlighter.enabled = false\r\n gizmo.attach(fragModel.object)\r\n gizmoControllersRef.current.set(file.name, gizmo)\r\n }\r\n }, [bimComponents, fragments, highlighter])\r\n\r\n const handleBimGhost = React.useCallback((file: DbFile, ghostState: boolean) => {\r\n if (!fragments || !ghostMode) return\r\n\r\n const fragModel = fragments.core.models.list.get(file.name)\r\n if (!fragModel) {\r\n console.warn(`[BimGhost] Model not found: \"${file.name}\"`)\r\n return\r\n }\r\n\r\n ghostMode.setModelGhost(fragModel, ghostState)\r\n bimDispatch({ type: 'SET_MODEL_UI_STATE', payload: { fileId: file.id, isGhost: ghostState } })\r\n\r\n // Disable highlighting for ghosted models; re-enable only when visible and not ghosted\r\n if (highlighter) {\r\n const isVisible = modelUIState[file.id]?.isVisible ?? true\r\n if (ghostState || !isVisible) {\r\n highlighter.disableModel(file.name)\r\n } else {\r\n highlighter.enableModel(file.name)\r\n }\r\n }\r\n }, [fragments, ghostMode, bimDispatch, highlighter, modelUIState])\r\n\r\n // Use the common file actions hook (no custom download handler - use default for all files)\r\n const { handleAction, deleteDialog } = useFileActions({\r\n files: loadedModels,\r\n setFiles: setLoadedModels,\r\n buildingId,\r\n handleDeleteFile,\r\n onView: handleBimView,\r\n onDelete: handleBimDelete,\r\n onMove: handleBimMove,\r\n onGhost: handleBimGhost\r\n })\r\n\r\n // Use the common file upload hook\r\n const { handleAddFile } = useCommonFileUpload({\r\n buildingId,\r\n acceptedFileTypes: '.ifc,.frag',\r\n handleFileUpload,\r\n onUploadSuccess: () => {\r\n console.log('Model uploaded successfully')\r\n },\r\n onUploadError: (error) => {\r\n console.error('Error uploading model:', error)\r\n }\r\n })\r\n\r\n // Filter models based on search query\r\n const filteredModels = React.useMemo(() => {\r\n if (!query.trim()) return loadedModels\r\n return loadedModels.filter(file =>\r\n file.name.toLowerCase().includes(query.toLowerCase())\r\n )\r\n }, [loadedModels, query])\r\n\r\n const areAllHidden = loadedModels.every(f => !f.isVisible)\r\n\r\n const handleSwitchVariant = () => ({\r\n checked: !areAllHidden,\r\n onCheckedChange: (checked: boolean) => {\r\n setLoadedModels(prev => prev.map(f => ({ ...f, isVisible: checked })))\r\n // Toggle visibility in 3D scene for all models\r\n for (const file of loadedModels) {\r\n handleBimView(file, checked)\r\n }\r\n },\r\n })\r\n\r\n return (\r\n <>\r\n <CollapsibleSection\r\n title={t('modelsTitles')}\r\n icon={LR.Box}\r\n className=\"max-h-40 overflow-y-auto\"\r\n itemCount={filteredModels.length}\r\n onAddItem={handleAddFile}\r\n addItemTitle={t('addBimTitle')}\r\n switchVariant={handleSwitchVariant()}\r\n >\r\n <div className=\"space-y-1\">\r\n {filteredModels.map((file) => (\r\n <FileItemComponent\r\n key={file.id}\r\n file={file}\r\n onAction={handleAction}\r\n options={BIM_MODEL_OPTIONS}\r\n confirmDelete={false}\r\n />\r\n ))}\r\n </div>\r\n </CollapsibleSection>\r\n\r\n <ConfirmDialog\r\n isOpen={deleteDialog.isOpen}\r\n isDeleting={deleteDialog.isDeleting}\r\n onOpenChange={deleteDialog.onOpenChange}\r\n handleConfirm={deleteDialog.onConfirm}\r\n itemName={deleteDialog.itemName}\r\n />\r\n </>\r\n )\r\n}\r\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../../../src/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/ModelsSection.tsx"],"sourcesContent":["'use client'\r\n\r\n// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport * as LR from 'lucide-react'\r\nimport { useTranslations } from 'next-intl'\r\nimport * as React from 'react'\r\n\r\nimport { useUploadFileToBuilding, useDeleteFile, useFile } from '../../../../../../../../hooks/files/files'\r\nimport { BimContext, BuildingsContext } from '../../../../../../../../store'\r\nimport ConfirmDialog from '../../../../../../../ConfirmDialog'\r\nimport { CollapsibleSection } from '../../../../../../../ui/CollapsibleSection'\r\nimport { useFileUploadHandler, useFileDeleteHandler, FileItemComponent, useFileActions, useCommonFileUpload } from '../../../../../../../ui/FilesManager'\r\nimport { GizmoController } from '../../../../../utils/GizmoController'\r\nimport { BIMManager } from '../../../../BIMManager'\r\nimport { CurrentWorld } from '../../../../CurrentWorld'\r\nimport { GhostMode } from '../../../../GhostMode'\r\nimport { Highlighter } from '../../../../Highlighter'\r\nimport { ModelManager } from '../../../../ModelManager'\r\n\r\nimport type { DbFile as DbFile } from '../../../../../../../../types/dbTypes'\r\n\r\nconst BIM_MODEL_OPTIONS: import('../../../../../../../../types/global').FileAction[] = ['view', 'ghost', 'move', 'info', 'delete']\r\n\r\ninterface ModelsSectionProps {\r\n files: DbFile[]\r\n query?: string\r\n}\r\n\r\nexport function ModelsSection({ files, query = '' }: ModelsSectionProps) {\r\n // Translation\r\n const t = useTranslations('FileItemComponent')\r\n\r\n // Get BIM context\r\n const { state: bimState, dispatch: bimDispatch } = React.useContext(BimContext)\r\n const { bimComponents, fragments, modelId, modelUIState } = bimState.bim\r\n\r\n // Get Buildings context for buildingId\r\n const { state: buildingsState } = React.useContext(BuildingsContext)\r\n const { building } = buildingsState.buildings\r\n const buildingId = building?.id || 0\r\n\r\n // Upload file hook and session\r\n const { uploadFile } = useUploadFileToBuilding(buildingId)\r\n const { deleteFile } = useDeleteFile(buildingId)\r\n\r\n // Use the reusable upload handler\r\n const { handleFileUpload } = useFileUploadHandler({\r\n buildingId,\r\n tag: 'bim-file',\r\n isVisible: true,\r\n uploadFile,\r\n })\r\n\r\n // Use the reusable delete handler\r\n const { handleDeleteFile } = useFileDeleteHandler({\r\n deleteFile,\r\n\r\n })\r\n\r\n // Local state for file management with isVisible property\r\n const [loadedModels, setLoadedModels] = React.useState<(DbFile & { isVisible?: boolean })[]>(\r\n files.map(file => ({\r\n ...file,\r\n isVisible: modelUIState[file.id]?.isVisible ?? (file as any).isVisible,\r\n isGhost: modelUIState[file.id]?.isGhost ?? false,\r\n }))\r\n )\r\n\r\n // Keep local list in sync with incoming props and store UI state changes\r\n React.useEffect(() => {\r\n setLoadedModels(prev => {\r\n const prevMap = new Map(prev.map(f => [f.id, f as any]))\r\n return files.map(file => ({\r\n ...file,\r\n isVisible: modelUIState[file.id]?.isVisible ?? (file as any).isVisible,\r\n isGhost: modelUIState[file.id]?.isGhost ?? prevMap.get(file.id)?.isGhost ?? false,\r\n }))\r\n })\r\n }, [files, modelUIState])\r\n\r\n // Get ModelManager from BIM components\r\n const modelManager = React.useMemo(() => {\r\n if (!bimComponents) return null\r\n try {\r\n return bimComponents.get(ModelManager)\r\n }\r\n catch {\r\n return null\r\n }\r\n }, [bimComponents])\r\n\r\n // Get BIMManager from BIM components\r\n const bimManager = React.useMemo(() => {\r\n if (!bimComponents) return null\r\n try {\r\n return bimComponents.get(BIMManager)\r\n }\r\n catch {\r\n return null\r\n }\r\n }, [bimComponents])\r\n\r\n // Get GhostMode from BIM components\r\n const ghostMode = React.useMemo(() => {\r\n if (!bimComponents) return null\r\n try {\r\n return bimComponents.get(GhostMode)\r\n }\r\n catch {\r\n return null\r\n }\r\n }, [bimComponents])\r\n\r\n // Gizmo controllers keyed by model's file.name (one gizmo per model at most)\r\n const gizmoControllersRef = React.useRef<Map<string, GizmoController>>(new Map())\r\n\r\n // Track the file ID currently being repositioned so useFile can provide updateFile\r\n const [moveFileId, setMoveFileId] = React.useState<number | null>(null)\r\n const { updateFile } = useFile(moveFileId)\r\n const updateFileRef = React.useRef(updateFile)\r\n React.useEffect(() => { updateFileRef.current = updateFile }, [updateFile])\r\n\r\n // Get Highlighter from BIM components\r\n const highlighter = React.useMemo(() => {\r\n if (!bimComponents) return null\r\n try {\r\n return bimComponents.get(Highlighter)\r\n }\r\n catch {\r\n return null\r\n }\r\n }, [bimComponents])\r\n\r\n // Custom handlers for BIM-specific actions (view and delete only - download uses default)\r\n const handleBimView = React.useCallback((file: DbFile, newVisibility: boolean) => {\r\n bimDispatch({ type: 'SET_MODEL_UI_STATE', payload: { fileId: file.id, isVisible: newVisibility } })\r\n\r\n // Disable highlighting for hidden models; re-enable only when visible and not ghosted\r\n if (highlighter) {\r\n const isGhosted = modelUIState[file.id]?.isGhost ?? false\r\n if (!newVisibility || isGhosted) {\r\n highlighter.disableModel(file.name)\r\n } else {\r\n highlighter.enableModel(file.name)\r\n }\r\n }\r\n\r\n // Non-fragment models (gltf/obj/fbx) managed by ModelManager\r\n if (modelManager) {\r\n const modelInfo = modelManager.getModel(file.id.toString())\r\n if (modelInfo) {\r\n modelInfo.model.visible = newVisibility\r\n if (fragments) fragments.core.update(true)\r\n return\r\n }\r\n }\r\n\r\n // Fragment (IFC/FRAG) models — look up directly from fragments list by file.name\r\n if (fragments) {\r\n const fragModel = fragments.core.models.list.get(file.name)\r\n if (fragModel) {\r\n fragModel.object.visible = newVisibility\r\n fragments.core.update(true)\r\n } else {\r\n }\r\n }\r\n }, [modelManager, fragments, highlighter, modelUIState])\r\n\r\n const handleBimDelete = React.useCallback((file: DbFile) => {\r\n // Remove from ModelManager if it exists (gltf/obj/fbx models)\r\n if (modelManager) {\r\n const modelIdStr = file.id.toString()\r\n modelManager.remove(modelIdStr)\r\n }\r\n\r\n // Remove fragment model from scene if it exists (ifc/frag models)\r\n if (fragments) {\r\n const fragModel = fragments.core.models.list.get(file.name)\r\n if (fragModel) {\r\n fragments.core.disposeModel(fragModel.modelId).catch((err: unknown) => {\r\n console.error(`Failed to dispose fragment model \"${file.name}\":`, err)\r\n })\r\n // Also clean up BIMManager tracking\r\n bimManager?.remove(file.name)\r\n }\r\n }\r\n }, [modelManager, bimComponents, fragments, bimManager])\r\n\r\n const handleBimMove = React.useCallback((file: DbFile) => {\r\n if (!bimComponents || !fragments) return\r\n\r\n const world = bimComponents.get(CurrentWorld).world\r\n if (!world) return\r\n\r\n const fragModel = fragments.core.models.list.get(file.name)\r\n if (!fragModel) {\r\n console.warn(`[BimMove] Model not found: \"${file.name}\"`)\r\n return\r\n }\r\n\r\n const existing = gizmoControllersRef.current.get(file.name)\r\n if (existing) {\r\n // Toggle off: destroy the gizmo\r\n existing.dispose()\r\n gizmoControllersRef.current.delete(file.name)\r\n if (gizmoControllersRef.current.size === 0 && highlighter) highlighter.enabled = true\r\n } else {\r\n // Toggle on: create a new gizmo attached to the model's root object\r\n const gizmo = new GizmoController(world)\r\n const cleanupGizmo = (savePosition: boolean) => {\r\n if (savePosition) {\r\n const { x, y, z } = fragModel.object.position\r\n const rotation = fragModel.object.rotation.y\r\n updateFileRef.current({ x, y, z, rotation } as any)\r\n .catch((err: unknown) => console.error(`[BimMove] Failed to save position for \"${file.name}\":`, err))\r\n file.x = x\r\n file.y = y\r\n file.z = z\r\n file.rotation = rotation\r\n }\r\n gizmoControllersRef.current.delete(file.name)\r\n if (gizmoControllersRef.current.size === 0 && highlighter) highlighter.enabled = true\r\n setMoveFileId(null)\r\n }\r\n gizmo.onAccept = () => cleanupGizmo(true)\r\n gizmo.onCancel = () => cleanupGizmo(false)\r\n setMoveFileId(file.id)\r\n if (highlighter) highlighter.enabled = false\r\n gizmo.attach(fragModel.object)\r\n gizmoControllersRef.current.set(file.name, gizmo)\r\n }\r\n }, [bimComponents, fragments, highlighter])\r\n\r\n const handleBimGhost = React.useCallback((file: DbFile, ghostState: boolean) => {\r\n if (!fragments || !ghostMode) return\r\n\r\n const fragModel = fragments.core.models.list.get(file.name)\r\n if (!fragModel) {\r\n console.warn(`[BimGhost] Model not found: \"${file.name}\"`)\r\n return\r\n }\r\n\r\n ghostMode.setModelGhost(fragModel, ghostState)\r\n bimDispatch({ type: 'SET_MODEL_UI_STATE', payload: { fileId: file.id, isGhost: ghostState } })\r\n\r\n // Disable highlighting for ghosted models; re-enable only when visible and not ghosted\r\n if (highlighter) {\r\n const isVisible = modelUIState[file.id]?.isVisible ?? true\r\n if (ghostState || !isVisible) {\r\n highlighter.disableModel(file.name)\r\n } else {\r\n highlighter.enableModel(file.name)\r\n }\r\n }\r\n }, [fragments, ghostMode, bimDispatch, highlighter, modelUIState])\r\n\r\n // Use the common file actions hook (no custom download handler - use default for all files)\r\n const { handleAction, deleteDialog } = useFileActions({\r\n files: loadedModels,\r\n setFiles: setLoadedModels,\r\n buildingId,\r\n handleDeleteFile,\r\n onView: handleBimView,\r\n onDelete: handleBimDelete,\r\n onMove: handleBimMove,\r\n onGhost: handleBimGhost\r\n })\r\n\r\n // Use the common file upload hook\r\n const { handleAddFile } = useCommonFileUpload({\r\n buildingId,\r\n acceptedFileTypes: '.ifc,.frag',\r\n handleFileUpload,\r\n onUploadError: (error) => {\r\n console.error('Error uploading model:', error)\r\n }\r\n })\r\n\r\n // Filter models based on search query\r\n const filteredModels = React.useMemo(() => {\r\n if (!query.trim()) return loadedModels\r\n return loadedModels.filter(file =>\r\n file.name.toLowerCase().includes(query.toLowerCase())\r\n )\r\n }, [loadedModels, query])\r\n\r\n const areAllHidden = loadedModels.every(f => !f.isVisible)\r\n\r\n const handleSwitchVariant = () => ({\r\n checked: !areAllHidden,\r\n onCheckedChange: (checked: boolean) => {\r\n setLoadedModels(prev => prev.map(f => ({ ...f, isVisible: checked })))\r\n // Toggle visibility in 3D scene for all models\r\n for (const file of loadedModels) {\r\n handleBimView(file, checked)\r\n }\r\n },\r\n })\r\n\r\n return (\r\n <>\r\n <CollapsibleSection\r\n title={t('modelsTitles')}\r\n icon={LR.Box}\r\n className=\"max-h-40 overflow-y-auto\"\r\n itemCount={filteredModels.length}\r\n onAddItem={handleAddFile}\r\n addItemTitle={t('addBimTitle')}\r\n switchVariant={handleSwitchVariant()}\r\n >\r\n <div className=\"space-y-1\">\r\n {filteredModels.map((file) => (\r\n <FileItemComponent\r\n key={file.id}\r\n file={file}\r\n onAction={handleAction}\r\n options={BIM_MODEL_OPTIONS}\r\n confirmDelete={false}\r\n />\r\n ))}\r\n </div>\r\n </CollapsibleSection>\r\n\r\n <ConfirmDialog\r\n isOpen={deleteDialog.isOpen}\r\n isDeleting={deleteDialog.isDeleting}\r\n onOpenChange={deleteDialog.onOpenChange}\r\n handleConfirm={deleteDialog.onConfirm}\r\n itemName={deleteDialog.itemName}\r\n />\r\n </>\r\n )\r\n}\r\n"],"mappings":"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","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TabSelector.d.ts","sourceRoot":"","sources":["../../../../../../../../src/core/components/viewers/bim/src/BimSidebar/src/TabSelector.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAI9B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAA;AAE3E,UAAU,gBAAgB;IACxB,SAAS,EAAE,cAAc,CAAA;IACzB,iBAAiB,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,IAAI,CAAA;CACjD;AAED,wBAAgB,WAAW,CAAC,EAAE,SAAS,EAAE,iBAAiB,EAAE,EAAE,gBAAgB,
|
|
1
|
+
{"version":3,"file":"TabSelector.d.ts","sourceRoot":"","sources":["../../../../../../../../src/core/components/viewers/bim/src/BimSidebar/src/TabSelector.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAI9B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAA;AAE3E,UAAU,gBAAgB;IACxB,SAAS,EAAE,cAAc,CAAA;IACzB,iBAAiB,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,IAAI,CAAA;CACjD;AAED,wBAAgB,WAAW,CAAC,EAAE,SAAS,EAAE,iBAAiB,EAAE,EAAE,gBAAgB,qBAuC7E"}
|
|
@@ -13,15 +13,16 @@ function TabSelector({ activeTab, onTabChangeAction }) {
|
|
|
13
13
|
];
|
|
14
14
|
return /* @__PURE__ */ jsxs("div", { children: [
|
|
15
15
|
/* @__PURE__ */ jsx(Separator, {}),
|
|
16
|
-
/* @__PURE__ */ jsx("div", { className: "px-4 py-3", children: /* @__PURE__ */ jsx("div", { className: "flex items-center
|
|
16
|
+
/* @__PURE__ */ jsx("div", { className: "px-4 py-3", children: /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1 w-full", children: tabs.map((tab) => /* @__PURE__ */ jsx(
|
|
17
17
|
"div",
|
|
18
18
|
{
|
|
19
|
-
|
|
19
|
+
title: tab.label,
|
|
20
|
+
className: `flex-1 min-w-0 truncate text-center px-2 py-2 rounded-md cursor-pointer transition-colors text-xs font-medium ${activeTab === tab.id ? "bg-accent text-accent-foreground" : "text-muted-foreground hover:text-foreground hover:bg-accent/50"}`,
|
|
20
21
|
onClick: () => onTabChangeAction(tab.id),
|
|
21
22
|
children: tab.label
|
|
22
23
|
},
|
|
23
24
|
tab.id
|
|
24
|
-
)) }) })
|
|
25
|
+
)) }) }),
|
|
25
26
|
/* @__PURE__ */ jsx(Separator, {})
|
|
26
27
|
] });
|
|
27
28
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../../src/core/components/viewers/bim/src/BimSidebar/src/TabSelector.tsx"],"sourcesContent":["'use client'\r\n\r\n// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport { useTranslations } from 'next-intl'\r\nimport * as React from 'react'\r\n\r\nimport { Separator } from '../../../../../ui/Separator'\r\n\r\nimport type { SidebarTabType } from '../../../../../../store/Menus/reducer'\r\n\r\ninterface TabSelectorProps {\r\n activeTab: SidebarTabType\r\n onTabChangeAction: (tab: SidebarTabType) => void\r\n}\r\n\r\nexport function TabSelector({ activeTab, onTabChangeAction }: TabSelectorProps) {\r\n // Translation\r\n const t = useTranslations('TabSelector')\r\n\r\n const tabs = [\r\n { id: 'file', label: t('fileLabel') },\r\n { id: 'layers', label: t('layersTitle') },\r\n { id: 'communication', label: t('communicationTitle') },\r\n { id: 'sensors', label: t('sensorsTitle') },\r\n { id: 'settings', label: t('settingsTitle') },\r\n\r\n ]\r\n\r\n return (\r\n <div>\r\n <Separator />\r\n\r\n <div className=\"px-4 py-3\">\r\n <div className=\"flex items-center
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../src/core/components/viewers/bim/src/BimSidebar/src/TabSelector.tsx"],"sourcesContent":["'use client'\r\n\r\n// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport { useTranslations } from 'next-intl'\r\nimport * as React from 'react'\r\n\r\nimport { Separator } from '../../../../../ui/Separator'\r\n\r\nimport type { SidebarTabType } from '../../../../../../store/Menus/reducer'\r\n\r\ninterface TabSelectorProps {\r\n activeTab: SidebarTabType\r\n onTabChangeAction: (tab: SidebarTabType) => void\r\n}\r\n\r\nexport function TabSelector({ activeTab, onTabChangeAction }: TabSelectorProps) {\r\n // Translation\r\n const t = useTranslations('TabSelector')\r\n\r\n const tabs = [\r\n { id: 'file', label: t('fileLabel') },\r\n { id: 'layers', label: t('layersTitle') },\r\n { id: 'communication', label: t('communicationTitle') },\r\n { id: 'sensors', label: t('sensorsTitle') },\r\n { id: 'settings', label: t('settingsTitle') },\r\n\r\n ]\r\n\r\n return (\r\n <div>\r\n <Separator />\r\n\r\n <div className=\"px-4 py-3\">\r\n <div className=\"flex items-center gap-1 w-full\">\r\n {tabs.map(tab => (\r\n <div\r\n key={tab.id}\r\n title={tab.label}\r\n className={`flex-1 min-w-0 truncate text-center px-2 py-2 rounded-md cursor-pointer transition-colors text-xs font-medium ${\r\n activeTab === tab.id\r\n ? 'bg-accent text-accent-foreground'\r\n : 'text-muted-foreground hover:text-foreground hover:bg-accent/50'\r\n }`}\r\n onClick={() => onTabChangeAction(tab.id as SidebarTabType)}\r\n >\r\n {tab.label}\r\n </div>\r\n ))}\r\n </div>\r\n </div>\r\n\r\n <Separator />\r\n </div>\r\n )\r\n}\r\n"],"mappings":";AA+BI,SACE,KADF;AA1BJ,SAAS,uBAAuB;AAGhC,SAAS,iBAAiB;AASnB,SAAS,YAAY,EAAE,WAAW,kBAAkB,GAAqB;AAE9E,QAAM,IAAI,gBAAgB,aAAa;AAEvC,QAAM,OAAO;AAAA,IACX,EAAE,IAAI,QAAQ,OAAO,EAAE,WAAW,EAAE;AAAA,IACpC,EAAE,IAAI,UAAU,OAAO,EAAE,aAAa,EAAE;AAAA,IACxC,EAAE,IAAI,iBAAiB,OAAO,EAAE,oBAAoB,EAAE;AAAA,IACtD,EAAE,IAAI,WAAW,OAAO,EAAE,cAAc,EAAE;AAAA,IAC1C,EAAE,IAAI,YAAY,OAAO,EAAE,eAAe,EAAE;AAAA,EAE9C;AAEA,SACE,qBAAC,SACC;AAAA,wBAAC,aAAU;AAAA,IAEX,oBAAC,SAAI,WAAU,aACb,8BAAC,SAAI,WAAU,kCACZ,eAAK,IAAI,SACR;AAAA,MAAC;AAAA;AAAA,QAEC,OAAO,IAAI;AAAA,QACX,WAAW,iHACT,cAAc,IAAI,KACd,qCACA,gEACN;AAAA,QACA,SAAS,MAAM,kBAAkB,IAAI,EAAoB;AAAA,QAExD,cAAI;AAAA;AAAA,MATA,IAAI;AAAA,IAUX,CACD,GACH,GACF;AAAA,IAEA,oBAAC,aAAU;AAAA,KACb;AAEJ;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ClippingPlane.d.ts","sourceRoot":"","sources":["../../../../../../src/core/components/viewers/bim/src/ClippingPlane.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,GAAG,MAAM,sBAAsB,CAAA;AAI3C,qBAAa,aAAc,SAAQ,GAAG,CAAC,SAAS;IAE5C,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAG,sCAAsC,CAAS;IAEtE,OAAO,CAAC,QAAQ,CAAQ;IACxB,OAAO,CAAC,OAAO,CAAa;IAC5B,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,KAAK,CAAyB;IACtC,OAAO,CAAC,SAAS,CAA2B;IAE5C,IAAI,OAAO,IAIQ,OAAO,CAFzB;IAED,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,EASzB;gBAEW,UAAU,EAAE,GAAG,CAAC,UAAU;IAoBtC,OAAO,CAAC,kBAAkB;IAuB1B,OAAO,CAAC,oBAAoB;IAQ5B,OAAO,CAAC,aAAa,
|
|
1
|
+
{"version":3,"file":"ClippingPlane.d.ts","sourceRoot":"","sources":["../../../../../../src/core/components/viewers/bim/src/ClippingPlane.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,GAAG,MAAM,sBAAsB,CAAA;AAI3C,qBAAa,aAAc,SAAQ,GAAG,CAAC,SAAS;IAE5C,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAG,sCAAsC,CAAS;IAEtE,OAAO,CAAC,QAAQ,CAAQ;IACxB,OAAO,CAAC,OAAO,CAAa;IAC5B,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,KAAK,CAAyB;IACtC,OAAO,CAAC,SAAS,CAA2B;IAE5C,IAAI,OAAO,IAIQ,OAAO,CAFzB;IAED,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,EASzB;gBAEW,UAAU,EAAE,GAAG,CAAC,UAAU;IAoBtC,OAAO,CAAC,kBAAkB;IAuB1B,OAAO,CAAC,oBAAoB;IAQ5B,OAAO,CAAC,aAAa,CAQpB;IAED,YAAY;IAMZ,YAAY;IAMZ,OAAO;CAMV"}
|
|
@@ -8,7 +8,6 @@ const _ClippingPlane = class _ClippingPlane extends OBC.Component {
|
|
|
8
8
|
this.world = null;
|
|
9
9
|
this.container = null;
|
|
10
10
|
this.handleKeyDown = (event) => {
|
|
11
|
-
console.log("Key pressed:", event.code, "Enabled:", this.enabled);
|
|
12
11
|
if (event.code === "Delete" || event.code === "Backspace" || event.code === "Escape") {
|
|
13
12
|
if (this.enabled && this.world) {
|
|
14
13
|
event.preventDefault();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../src/core/components/viewers/bim/src/ClippingPlane.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport * as OBC from '@thatopen/components'\r\n\r\nimport { CurrentWorld } from './CurrentWorld';\r\n\r\nexport class ClippingPlane extends OBC.Component {\r\n\r\n static readonly uuid = '29b2eabd-36cd-4b42-a62c-d0509bd67d60' as const\r\n\r\n private _enabled = false\r\n private clipper: OBC.Clipper\r\n private casters: OBC.Raycasters\r\n private world: OBC.World | null = null\r\n private container: HTMLElement | null = null\r\n\r\n get enabled() {\r\n return this._enabled\r\n }\r\n\r\n set enabled(value: boolean) {\r\n this._enabled = value\r\n this.clipper.enabled = value\r\n\r\n if (value) {\r\n this.setupClippingPlane()\r\n } else {\r\n this.removeEventListeners()\r\n }\r\n }\r\n\r\n constructor(components: OBC.Components) {\r\n super(components)\r\n components.add(ClippingPlane.uuid, this)\r\n\r\n // Initialize clipper and raycasters\r\n this.clipper = components.get(OBC.Clipper)\r\n this.casters = components.get(OBC.Raycasters)\r\n this.world = components.get(CurrentWorld).world\r\n\r\n // Initialize the raycaster for the world\r\n if (this.world) {\r\n this.casters.get(this.world)\r\n }\r\n\r\n // Get the container from the world's renderer\r\n if (this.world?.renderer?.three.domElement.parentElement) {\r\n this.container = this.world.renderer.three.domElement.parentElement\r\n }\r\n }\r\n\r\n private setupClippingPlane() {\r\n if (!this.world || !this.container) {\r\n console.warn('World and container must be set before enabling clipping plane')\r\n return\r\n }\r\n\r\n // Add double-click event listener to create clipping planes\r\n this.container.ondblclick = () => {\r\n if (this.enabled && this.world) {\r\n this.clipper.create(this.world)\r\n }\r\n }\r\n\r\n // Make container focusable and focused for keyboard events\r\n this.container.tabIndex = -1\r\n this.container.focus()\r\n\r\n // Add keyboard event listener for deleting clipping planes\r\n this.container.addEventListener('keydown', this.handleKeyDown)\r\n // Also add to window as fallback\r\n window.addEventListener('keydown', this.handleKeyDown)\r\n }\r\n\r\n private removeEventListeners() {\r\n if (this.container) {\r\n this.container.ondblclick = null\r\n this.container.removeEventListener('keydown', this.handleKeyDown)\r\n }\r\n window.removeEventListener('keydown', this.handleKeyDown)\r\n }\r\n\r\n private handleKeyDown = (event: KeyboardEvent) => {\r\n
|
|
1
|
+
{"version":3,"sources":["../../../../../../src/core/components/viewers/bim/src/ClippingPlane.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport * as OBC from '@thatopen/components'\r\n\r\nimport { CurrentWorld } from './CurrentWorld';\r\n\r\nexport class ClippingPlane extends OBC.Component {\r\n\r\n static readonly uuid = '29b2eabd-36cd-4b42-a62c-d0509bd67d60' as const\r\n\r\n private _enabled = false\r\n private clipper: OBC.Clipper\r\n private casters: OBC.Raycasters\r\n private world: OBC.World | null = null\r\n private container: HTMLElement | null = null\r\n\r\n get enabled() {\r\n return this._enabled\r\n }\r\n\r\n set enabled(value: boolean) {\r\n this._enabled = value\r\n this.clipper.enabled = value\r\n\r\n if (value) {\r\n this.setupClippingPlane()\r\n } else {\r\n this.removeEventListeners()\r\n }\r\n }\r\n\r\n constructor(components: OBC.Components) {\r\n super(components)\r\n components.add(ClippingPlane.uuid, this)\r\n\r\n // Initialize clipper and raycasters\r\n this.clipper = components.get(OBC.Clipper)\r\n this.casters = components.get(OBC.Raycasters)\r\n this.world = components.get(CurrentWorld).world\r\n\r\n // Initialize the raycaster for the world\r\n if (this.world) {\r\n this.casters.get(this.world)\r\n }\r\n\r\n // Get the container from the world's renderer\r\n if (this.world?.renderer?.three.domElement.parentElement) {\r\n this.container = this.world.renderer.three.domElement.parentElement\r\n }\r\n }\r\n\r\n private setupClippingPlane() {\r\n if (!this.world || !this.container) {\r\n console.warn('World and container must be set before enabling clipping plane')\r\n return\r\n }\r\n\r\n // Add double-click event listener to create clipping planes\r\n this.container.ondblclick = () => {\r\n if (this.enabled && this.world) {\r\n this.clipper.create(this.world)\r\n }\r\n }\r\n\r\n // Make container focusable and focused for keyboard events\r\n this.container.tabIndex = -1\r\n this.container.focus()\r\n\r\n // Add keyboard event listener for deleting clipping planes\r\n this.container.addEventListener('keydown', this.handleKeyDown)\r\n // Also add to window as fallback\r\n window.addEventListener('keydown', this.handleKeyDown)\r\n }\r\n\r\n private removeEventListeners() {\r\n if (this.container) {\r\n this.container.ondblclick = null\r\n this.container.removeEventListener('keydown', this.handleKeyDown)\r\n }\r\n window.removeEventListener('keydown', this.handleKeyDown)\r\n }\r\n\r\n private handleKeyDown = (event: KeyboardEvent) => {\r\n if (event.code === \"Delete\" || event.code === \"Backspace\" || event.code === \"Escape\") {\r\n if (this.enabled && this.world) {\r\n event.preventDefault();\r\n event.stopPropagation();\r\n this.deletePlanes()\r\n }\r\n }\r\n }\r\n\r\n createPlanes() {\r\n if (this.enabled && this.world) {\r\n this.clipper.create(this.world)\r\n }\r\n }\r\n\r\n deletePlanes() {\r\n if (this.world) {\r\n this.clipper.deleteAll()\r\n }\r\n }\r\n\r\n dispose() {\r\n this.enabled = false\r\n this.removeEventListeners()\r\n this.world = null\r\n this.container = null\r\n }\r\n}"],"mappings":"AAGA,YAAY,SAAS;AAErB,SAAS,oBAAoB;AAEtB,MAAM,iBAAN,MAAM,uBAAsB,IAAI,UAAU;AAAA,EAyB7C,YAAY,YAA4B;AAhC5C;AAiCQ,UAAM,UAAU;AAtBpB,SAAQ,WAAW;AAGnB,SAAQ,QAA0B;AAClC,SAAQ,YAAgC;AAoExC,SAAQ,gBAAgB,CAAC,UAAyB;AAC9C,UAAI,MAAM,SAAS,YAAY,MAAM,SAAS,eAAe,MAAM,SAAS,UAAU;AAClF,YAAI,KAAK,WAAW,KAAK,OAAO;AAC5B,gBAAM,eAAe;AACrB,gBAAM,gBAAgB;AACtB,eAAK,aAAa;AAAA,QACtB;AAAA,MACJ;AAAA,IACJ;AAzDI,eAAW,IAAI,eAAc,MAAM,IAAI;AAGvC,SAAK,UAAU,WAAW,IAAI,IAAI,OAAO;AACzC,SAAK,UAAU,WAAW,IAAI,IAAI,UAAU;AAC5C,SAAK,QAAQ,WAAW,IAAI,YAAY,EAAE;AAG1C,QAAI,KAAK,OAAO;AACZ,WAAK,QAAQ,IAAI,KAAK,KAAK;AAAA,IAC/B;AAGA,SAAI,gBAAK,UAAL,mBAAY,aAAZ,mBAAsB,MAAM,WAAW,eAAe;AACtD,WAAK,YAAY,KAAK,MAAM,SAAS,MAAM,WAAW;AAAA,IAC1D;AAAA,EACJ;AAAA,EAjCA,IAAI,UAAU;AACV,WAAO,KAAK;AAAA,EAChB;AAAA,EAEA,IAAI,QAAQ,OAAgB;AACxB,SAAK,WAAW;AAChB,SAAK,QAAQ,UAAU;AAEvB,QAAI,OAAO;AACP,WAAK,mBAAmB;AAAA,IAC5B,OAAO;AACH,WAAK,qBAAqB;AAAA,IAC9B;AAAA,EACJ;AAAA,EAsBQ,qBAAqB;AACzB,QAAI,CAAC,KAAK,SAAS,CAAC,KAAK,WAAW;AAChC,cAAQ,KAAK,gEAAgE;AAC7E;AAAA,IACJ;AAGA,SAAK,UAAU,aAAa,MAAM;AAC9B,UAAI,KAAK,WAAW,KAAK,OAAO;AAC5B,aAAK,QAAQ,OAAO,KAAK,KAAK;AAAA,MAClC;AAAA,IACJ;AAGA,SAAK,UAAU,WAAW;AAC1B,SAAK,UAAU,MAAM;AAGrB,SAAK,UAAU,iBAAiB,WAAW,KAAK,aAAa;AAE7D,WAAO,iBAAiB,WAAW,KAAK,aAAa;AAAA,EACzD;AAAA,EAEQ,uBAAuB;AAC3B,QAAI,KAAK,WAAW;AAChB,WAAK,UAAU,aAAa;AAC5B,WAAK,UAAU,oBAAoB,WAAW,KAAK,aAAa;AAAA,IACpE;AACA,WAAO,oBAAoB,WAAW,KAAK,aAAa;AAAA,EAC5D;AAAA,EAYA,eAAe;AACX,QAAI,KAAK,WAAW,KAAK,OAAO;AAC5B,WAAK,QAAQ,OAAO,KAAK,KAAK;AAAA,IAClC;AAAA,EACJ;AAAA,EAEA,eAAe;AACX,QAAI,KAAK,OAAO;AACZ,WAAK,QAAQ,UAAU;AAAA,IAC3B;AAAA,EACJ;AAAA,EAEA,UAAU;AACN,SAAK,UAAU;AACf,SAAK,qBAAqB;AAC1B,SAAK,QAAQ;AACb,SAAK,YAAY;AAAA,EACrB;AACJ;AAxGa,eAEO,OAAO;AAFpB,IAAM,gBAAN;","names":[]}
|
|
@@ -1,21 +1,27 @@
|
|
|
1
|
-
import * as React from
|
|
2
|
-
import type
|
|
3
|
-
interface
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { CommentActionLabels } from '../../../../../../ui/Comments/CommentActionButtons';
|
|
3
|
+
interface BimCommentProps {
|
|
4
4
|
userName: string;
|
|
5
5
|
userImage: number | null;
|
|
6
6
|
userImageFileId?: number | null;
|
|
7
|
-
worldCamera: THREE.Camera;
|
|
8
|
-
targetPoint: THREE.Vector3;
|
|
9
|
-
onRemove?: () => void;
|
|
10
|
-
onEdit?: () => void;
|
|
11
|
-
onClose: () => void;
|
|
12
7
|
buildingId?: number;
|
|
13
8
|
timestamp: Date;
|
|
14
9
|
text: string;
|
|
15
|
-
sphere: THREE.Object3D;
|
|
16
10
|
isPending?: boolean;
|
|
17
11
|
highlight?: boolean;
|
|
12
|
+
focused?: boolean;
|
|
13
|
+
showActions?: boolean;
|
|
14
|
+
canReply?: boolean;
|
|
15
|
+
canEdit?: boolean;
|
|
16
|
+
canDelete?: boolean;
|
|
17
|
+
actionLabels?: CommentActionLabels;
|
|
18
|
+
onRemove?: () => void;
|
|
19
|
+
onClose?: () => void;
|
|
20
|
+
onSelect?: () => void;
|
|
21
|
+
onFocus?: () => void;
|
|
22
|
+
onReply?: () => void;
|
|
23
|
+
onEdit?: () => void;
|
|
18
24
|
}
|
|
19
|
-
export default function BimComment({ userName, userImage, userImageFileId,
|
|
25
|
+
export default function BimComment({ userName, userImage, userImageFileId, buildingId, timestamp, text, isPending, highlight, focused, showActions, canReply, canEdit, canDelete, actionLabels, onRemove, onClose, onSelect, onFocus, onReply, onEdit, }: BimCommentProps): React.ReactElement;
|
|
20
26
|
export {};
|
|
21
27
|
//# sourceMappingURL=BimComment.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BimComment.d.ts","sourceRoot":"","sources":["../../../../../../../../../src/core/components/viewers/bim/src/tools/AddToBim/src/BimComment.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,KAAK,MAAM,OAAO,
|
|
1
|
+
{"version":3,"file":"BimComment.d.ts","sourceRoot":"","sources":["../../../../../../../../../src/core/components/viewers/bim/src/tools/AddToBim/src/BimComment.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAI9B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oDAAoD,CAAA;AAE7F,UAAU,eAAe;IACvB,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,IAAI,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,YAAY,CAAC,EAAE,mBAAmB,CAAA;IAClC,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IACpB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IACpB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IACpB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAA;CACpB;AAED,MAAM,CAAC,OAAO,UAAU,UAAU,CAAC,EACjC,QAAQ,EACR,SAAS,EACT,eAAe,EACf,UAAU,EACV,SAAS,EACT,IAAI,EACJ,SAAiB,EACjB,SAAiB,EACjB,OAAe,EACf,WAAmB,EACnB,QAAe,EACf,OAAc,EACd,SAAgB,EAChB,YAAY,EACZ,QAAQ,EACR,OAAO,EACP,QAAQ,EACR,OAAO,EACP,OAAO,EACP,MAAM,GACP,EAAE,eAAe,GAAG,KAAK,CAAC,YAAY,CA2BtC"}
|
|
@@ -5,14 +5,23 @@ function BimComment({
|
|
|
5
5
|
userName,
|
|
6
6
|
userImage,
|
|
7
7
|
userImageFileId,
|
|
8
|
-
onRemove,
|
|
9
|
-
onEdit,
|
|
10
|
-
onClose,
|
|
11
8
|
buildingId,
|
|
12
9
|
timestamp,
|
|
13
10
|
text,
|
|
14
11
|
isPending = false,
|
|
15
|
-
highlight = false
|
|
12
|
+
highlight = false,
|
|
13
|
+
focused = false,
|
|
14
|
+
showActions = false,
|
|
15
|
+
canReply = true,
|
|
16
|
+
canEdit = true,
|
|
17
|
+
canDelete = true,
|
|
18
|
+
actionLabels,
|
|
19
|
+
onRemove,
|
|
20
|
+
onClose,
|
|
21
|
+
onSelect,
|
|
22
|
+
onFocus,
|
|
23
|
+
onReply,
|
|
24
|
+
onEdit
|
|
16
25
|
}) {
|
|
17
26
|
return /* @__PURE__ */ jsx(
|
|
18
27
|
Comment,
|
|
@@ -24,10 +33,21 @@ function BimComment({
|
|
|
24
33
|
text,
|
|
25
34
|
createdAt: timestamp,
|
|
26
35
|
isPending,
|
|
36
|
+
highlight,
|
|
37
|
+
focused,
|
|
38
|
+
showActions,
|
|
39
|
+
canReply,
|
|
40
|
+
canEdit,
|
|
41
|
+
canDelete,
|
|
42
|
+
actionLabels,
|
|
43
|
+
onRemove,
|
|
27
44
|
onClose,
|
|
45
|
+
onSelect,
|
|
46
|
+
onFocus,
|
|
47
|
+
onReply,
|
|
48
|
+
onEdit,
|
|
28
49
|
enableCollapse: true,
|
|
29
|
-
defaultCollapsed: true
|
|
30
|
-
highlight
|
|
50
|
+
defaultCollapsed: true
|
|
31
51
|
}
|
|
32
52
|
);
|
|
33
53
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../../../src/core/components/viewers/bim/src/tools/AddToBim/src/BimComment.tsx"],"sourcesContent":["'use client'\
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../../src/core/components/viewers/bim/src/tools/AddToBim/src/BimComment.tsx"],"sourcesContent":["'use client'\n\n// SPDX-License-Identifier: AGPL-3.0-or-later\n// Copyright (C) 2025 Collab Digital Twins\n\nimport * as React from 'react'\n\nimport Comment from '../../../../../../ui/Comments/Comment'\n\nimport type { CommentActionLabels } from '../../../../../../ui/Comments/CommentActionButtons'\n\ninterface BimCommentProps {\n userName: string\n userImage: number | null\n userImageFileId?: number | null\n buildingId?: number\n timestamp: Date\n text: string\n isPending?: boolean\n highlight?: boolean\n focused?: boolean\n showActions?: boolean\n canReply?: boolean\n canEdit?: boolean\n canDelete?: boolean\n actionLabels?: CommentActionLabels\n onRemove?: () => void\n onClose?: () => void\n onSelect?: () => void\n onFocus?: () => void\n onReply?: () => void\n onEdit?: () => void\n}\n\nexport default function BimComment({\n userName,\n userImage,\n userImageFileId,\n buildingId,\n timestamp,\n text,\n isPending = false,\n highlight = false,\n focused = false,\n showActions = false,\n canReply = true,\n canEdit = true,\n canDelete = true,\n actionLabels,\n onRemove,\n onClose,\n onSelect,\n onFocus,\n onReply,\n onEdit,\n}: BimCommentProps): React.ReactElement {\n return (\n <Comment\n userName={userName}\n userImage={userImage}\n userImageFileId={userImageFileId}\n buildingId={buildingId}\n text={text}\n createdAt={timestamp}\n isPending={isPending}\n highlight={highlight}\n focused={focused}\n showActions={showActions}\n canReply={canReply}\n canEdit={canEdit}\n canDelete={canDelete}\n actionLabels={actionLabels}\n onRemove={onRemove}\n onClose={onClose}\n onSelect={onSelect}\n onFocus={onFocus}\n onReply={onReply}\n onEdit={onEdit}\n enableCollapse\n defaultCollapsed\n />\n )\n}\n"],"mappings":";AAyDI;AAlDJ,OAAO,aAAa;AA2BL,SAAR,WAA4B;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,UAAU;AAAA,EACV,cAAc;AAAA,EACd,WAAW;AAAA,EACX,UAAU;AAAA,EACV,YAAY;AAAA,EACZ;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAwC;AACtC,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW;AAAA,MACX;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,gBAAc;AAAA,MACd,kBAAgB;AAAA;AAAA,EAClB;AAEJ;","names":[]}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export interface ClusterMember {
|
|
3
|
+
id: number;
|
|
4
|
+
userName?: string;
|
|
5
|
+
imageFileId?: number | null;
|
|
6
|
+
}
|
|
7
|
+
interface BimCommentClusterProps {
|
|
8
|
+
members: ClusterMember[];
|
|
9
|
+
highlight?: boolean;
|
|
10
|
+
onSelect?: (id: number) => void;
|
|
11
|
+
onFocus?: (id: number) => void;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Rendered inside a CSS2DObject at a cluster's world center. Shows a numbered circle for
|
|
15
|
+
* overlapping BIM comment markers; hovering fans the members out (animated) so each can be
|
|
16
|
+
* seen and clicked, and collapses back on mouse-out. A short close delay keeps the fan open
|
|
17
|
+
* while the pointer crosses the gap between the bubble and a member (same trick the map
|
|
18
|
+
* cluster expansion uses), so members stay clickable. Members mount only while expanded, so a
|
|
19
|
+
* collapsed cluster is just the bubble — no leftover container behind it.
|
|
20
|
+
*/
|
|
21
|
+
export default function BimCommentCluster({ members, highlight, onSelect, onFocus }: BimCommentClusterProps): React.ReactElement;
|
|
22
|
+
export {};
|
|
23
|
+
//# sourceMappingURL=BimCommentCluster.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BimCommentCluster.d.ts","sourceRoot":"","sources":["../../../../../../../../../src/core/components/viewers/bim/src/tools/AddToBim/src/BimCommentCluster.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAK9B,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAA;IACV,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAC5B;AAED,UAAU,sBAAsB;IAC9B,OAAO,EAAE,aAAa,EAAE,CAAA;IACxB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAC/B,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;CAC/B;AASD;;;;;;;GAOG;AACH,MAAM,CAAC,OAAO,UAAU,iBAAiB,CAAC,EAAE,OAAO,EAAE,SAAiB,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,sBAAsB,GAAG,KAAK,CAAC,YAAY,CAoGvI"}
|