@collabdt/core 0.3.2 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/dist/core/components/ui/Comments/CollapsibleCommentItem.js.map +1 -1
  2. package/dist/core/components/ui/Comments/Comment.js.map +1 -1
  3. package/dist/core/components/ui/Comments/CommentActionButtons.d.ts +5 -1
  4. package/dist/core/components/ui/Comments/CommentActionButtons.d.ts.map +1 -1
  5. package/dist/core/components/ui/Comments/CommentActionButtons.js +37 -9
  6. package/dist/core/components/ui/Comments/CommentActionButtons.js.map +1 -1
  7. package/dist/core/components/ui/Sensors/CollapsibleSensorItem.d.ts +1 -2
  8. package/dist/core/components/ui/Sensors/CollapsibleSensorItem.d.ts.map +1 -1
  9. package/dist/core/components/ui/Sensors/CollapsibleSensorItem.js +66 -94
  10. package/dist/core/components/ui/Sensors/CollapsibleSensorItem.js.map +1 -1
  11. package/dist/core/components/ui/Sensors/Sensor.d.ts +16 -3
  12. package/dist/core/components/ui/Sensors/Sensor.d.ts.map +1 -1
  13. package/dist/core/components/ui/Sensors/Sensor.js +76 -37
  14. package/dist/core/components/ui/Sensors/Sensor.js.map +1 -1
  15. package/dist/core/components/ui/Sensors/SensorChart.d.ts +12 -2
  16. package/dist/core/components/ui/Sensors/SensorChart.d.ts.map +1 -1
  17. package/dist/core/components/ui/Sensors/SensorChart.js +49 -12
  18. package/dist/core/components/ui/Sensors/SensorChart.js.map +1 -1
  19. package/dist/core/components/ui/Sensors/SensorDetailDialog.d.ts +9 -0
  20. package/dist/core/components/ui/Sensors/SensorDetailDialog.d.ts.map +1 -0
  21. package/dist/core/components/ui/Sensors/SensorDetailDialog.js +128 -0
  22. package/dist/core/components/ui/Sensors/SensorDetailDialog.js.map +1 -0
  23. package/dist/core/components/ui/Sensors/SensorInput.d.ts +6 -1
  24. package/dist/core/components/ui/Sensors/SensorInput.d.ts.map +1 -1
  25. package/dist/core/components/ui/Sensors/SensorInput.js +94 -8
  26. package/dist/core/components/ui/Sensors/SensorInput.js.map +1 -1
  27. package/dist/core/components/ui/Sensors/SensorTagsSection.d.ts +3 -1
  28. package/dist/core/components/ui/Sensors/SensorTagsSection.d.ts.map +1 -1
  29. package/dist/core/components/ui/Sensors/SensorTagsSection.js +4 -1
  30. package/dist/core/components/ui/Sensors/SensorTagsSection.js.map +1 -1
  31. package/dist/core/components/ui/Sensors/SensorsSection.d.ts +1 -3
  32. package/dist/core/components/ui/Sensors/SensorsSection.d.ts.map +1 -1
  33. package/dist/core/components/ui/Sensors/SensorsSection.js +33 -8
  34. package/dist/core/components/ui/Sensors/SensorsSection.js.map +1 -1
  35. package/dist/core/components/ui/Sensors/TimeZoneSelect.d.ts +7 -0
  36. package/dist/core/components/ui/Sensors/TimeZoneSelect.d.ts.map +1 -0
  37. package/dist/core/components/ui/Sensors/TimeZoneSelect.js +56 -0
  38. package/dist/core/components/ui/Sensors/TimeZoneSelect.js.map +1 -0
  39. package/dist/core/components/ui/Sensors/sensorData.d.ts +42 -0
  40. package/dist/core/components/ui/Sensors/sensorData.d.ts.map +1 -0
  41. package/dist/core/components/ui/Sensors/sensorData.js +118 -0
  42. package/dist/core/components/ui/Sensors/sensorData.js.map +1 -0
  43. package/dist/core/components/ui/Sensors/sensorRange.d.ts +24 -0
  44. package/dist/core/components/ui/Sensors/sensorRange.d.ts.map +1 -0
  45. package/dist/core/components/ui/Sensors/sensorRange.js +34 -0
  46. package/dist/core/components/ui/Sensors/sensorRange.js.map +1 -0
  47. package/dist/core/components/ui/Sensors/useSensorSeries.d.ts +15 -0
  48. package/dist/core/components/ui/Sensors/useSensorSeries.d.ts.map +1 -0
  49. package/dist/core/components/ui/Sensors/useSensorSeries.js +63 -0
  50. package/dist/core/components/ui/Sensors/useSensorSeries.js.map +1 -0
  51. package/dist/core/components/viewers/Viewer.js +1 -1
  52. package/dist/core/components/viewers/Viewer.js.map +1 -1
  53. package/dist/core/components/viewers/bim/src/BimSidebar/src/SensorsTab/index.d.ts +1 -3
  54. package/dist/core/components/viewers/bim/src/BimSidebar/src/SensorsTab/index.d.ts.map +1 -1
  55. package/dist/core/components/viewers/bim/src/BimSidebar/src/SensorsTab/index.js +2 -2
  56. package/dist/core/components/viewers/bim/src/BimSidebar/src/SensorsTab/index.js.map +1 -1
  57. package/dist/core/components/viewers/bim/src/BimSidebar/src/index.js +1 -1
  58. package/dist/core/components/viewers/bim/src/BimSidebar/src/index.js.map +1 -1
  59. package/dist/core/components/viewers/bim/src/tools/AddToBim/index.d.ts +1 -2
  60. package/dist/core/components/viewers/bim/src/tools/AddToBim/index.d.ts.map +1 -1
  61. package/dist/core/components/viewers/bim/src/tools/AddToBim/index.js +25 -2
  62. package/dist/core/components/viewers/bim/src/tools/AddToBim/index.js.map +1 -1
  63. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimComment.js.map +1 -1
  64. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimCommentCluster.d.ts +1 -22
  65. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimCommentCluster.d.ts.map +1 -1
  66. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimCommentCluster.js +2 -105
  67. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimCommentCluster.js.map +1 -1
  68. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimSensor.d.ts +8 -1
  69. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimSensor.d.ts.map +1 -1
  70. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimSensor.js +15 -3
  71. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimSensor.js.map +1 -1
  72. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/clusterMarkersByScreenSpace.js.map +1 -1
  73. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentCameraUtils.d.ts +2 -23
  74. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentCameraUtils.d.ts.map +1 -1
  75. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentCameraUtils.js +2 -24
  76. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentCameraUtils.js.map +1 -1
  77. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentMarkerConfig.d.ts +33 -0
  78. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentMarkerConfig.d.ts.map +1 -0
  79. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentMarkerConfig.js +90 -0
  80. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentMarkerConfig.js.map +1 -0
  81. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useCommentMarkers.d.ts +7 -1
  82. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useCommentMarkers.d.ts.map +1 -1
  83. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useCommentMarkers.js +26 -230
  84. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useCommentMarkers.js.map +1 -1
  85. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useSensorMarkers.d.ts +7 -1
  86. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useSensorMarkers.d.ts.map +1 -1
  87. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useSensorMarkers.js +39 -11
  88. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useSensorMarkers.js.map +1 -1
  89. package/dist/core/components/viewers/map/MapViewer.d.ts +1 -2
  90. package/dist/core/components/viewers/map/MapViewer.d.ts.map +1 -1
  91. package/dist/core/components/viewers/map/MapViewer.js +2 -2
  92. package/dist/core/components/viewers/map/MapViewer.js.map +1 -1
  93. package/dist/core/components/viewers/map/src/MapLayers/index.d.ts +1 -2
  94. package/dist/core/components/viewers/map/src/MapLayers/index.d.ts.map +1 -1
  95. package/dist/core/components/viewers/map/src/MapLayers/index.js +2 -2
  96. package/dist/core/components/viewers/map/src/MapLayers/index.js.map +1 -1
  97. package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.d.ts +1 -3
  98. package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.d.ts.map +1 -1
  99. package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.js +74 -39
  100. package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.js.map +1 -1
  101. package/dist/core/components/viewers/map/src/MapSidebar/index.js +1 -1
  102. package/dist/core/components/viewers/map/src/MapSidebar/index.js.map +1 -1
  103. package/dist/core/components/viewers/map/src/MapSidebar/src/SensorsTab/index.d.ts +1 -3
  104. package/dist/core/components/viewers/map/src/MapSidebar/src/SensorsTab/index.d.ts.map +1 -1
  105. package/dist/core/components/viewers/map/src/MapSidebar/src/SensorsTab/index.js +2 -2
  106. package/dist/core/components/viewers/map/src/MapSidebar/src/SensorsTab/index.js.map +1 -1
  107. package/dist/core/components/viewers/shared/markers/BimMarkerCluster.d.ts +19 -0
  108. package/dist/core/components/viewers/shared/markers/BimMarkerCluster.d.ts.map +1 -0
  109. package/dist/core/components/viewers/shared/markers/BimMarkerCluster.js +108 -0
  110. package/dist/core/components/viewers/shared/markers/BimMarkerCluster.js.map +1 -0
  111. package/dist/core/components/viewers/shared/markers/computeMarkerLookAt.d.ts +24 -0
  112. package/dist/core/components/viewers/shared/markers/computeMarkerLookAt.d.ts.map +1 -0
  113. package/dist/core/components/viewers/shared/markers/computeMarkerLookAt.js +27 -0
  114. package/dist/core/components/viewers/shared/markers/computeMarkerLookAt.js.map +1 -0
  115. package/dist/core/components/viewers/shared/markers/types.d.ts +66 -0
  116. package/dist/core/components/viewers/shared/markers/types.d.ts.map +1 -0
  117. package/dist/core/components/viewers/shared/markers/types.js +1 -0
  118. package/dist/core/components/viewers/shared/markers/types.js.map +1 -0
  119. package/dist/core/components/viewers/shared/markers/useMarkerLayerBim.d.ts +22 -0
  120. package/dist/core/components/viewers/shared/markers/useMarkerLayerBim.d.ts.map +1 -0
  121. package/dist/core/components/viewers/shared/markers/useMarkerLayerBim.js +233 -0
  122. package/dist/core/components/viewers/shared/markers/useMarkerLayerBim.js.map +1 -0
  123. package/dist/core/hooks/ui/useResizableSidebarWidth.js.map +1 -1
  124. package/dist/core/i18n/messages/en.json +5 -1
  125. package/dist/core/i18n/messages/es.json +5 -1
  126. package/dist/core/i18n/messages/fr.json +5 -1
  127. package/dist/core/store/AppConfig/context.d.ts.map +1 -1
  128. package/dist/core/store/AppConfig/context.js +4 -1
  129. package/dist/core/store/AppConfig/context.js.map +1 -1
  130. package/dist/core/store/AppConfig/reducer.d.ts +10 -0
  131. package/dist/core/store/AppConfig/reducer.d.ts.map +1 -1
  132. package/dist/core/store/AppConfig/reducer.js +10 -0
  133. package/dist/core/store/AppConfig/reducer.js.map +1 -1
  134. package/dist/core/store/Menus/context.d.ts.map +1 -1
  135. package/dist/core/store/Menus/context.js +5 -2
  136. package/dist/core/store/Menus/context.js.map +1 -1
  137. package/dist/core/store/Menus/reducer.d.ts +45 -1
  138. package/dist/core/store/Menus/reducer.d.ts.map +1 -1
  139. package/dist/core/store/Menus/reducer.js +21 -3
  140. package/dist/core/store/Menus/reducer.js.map +1 -1
  141. package/dist/core/utils/timeUtils.d.ts +24 -1
  142. package/dist/core/utils/timeUtils.d.ts.map +1 -1
  143. package/dist/core/utils/timeUtils.js +52 -3
  144. package/dist/core/utils/timeUtils.js.map +1 -1
  145. package/package.json +1 -1
@@ -0,0 +1,63 @@
1
+ "use client";
2
+ var __defProp = Object.defineProperty;
3
+ var __defProps = Object.defineProperties;
4
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
5
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
8
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
9
+ var __spreadValues = (a, b) => {
10
+ for (var prop in b || (b = {}))
11
+ if (__hasOwnProp.call(b, prop))
12
+ __defNormalProp(a, prop, b[prop]);
13
+ if (__getOwnPropSymbols)
14
+ for (var prop of __getOwnPropSymbols(b)) {
15
+ if (__propIsEnum.call(b, prop))
16
+ __defNormalProp(a, prop, b[prop]);
17
+ }
18
+ return a;
19
+ };
20
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
21
+ import * as React from "react";
22
+ import { parseSensorSeries } from "./sensorData";
23
+ const MIN_POLL_MS = 1e3;
24
+ function useSensorSeries(dataUrl, dataFormat, updateFrequencyMs, opts) {
25
+ const { enabled } = opts;
26
+ const [series, setSeries] = React.useState({ points: [] });
27
+ const [isLoading, setIsLoading] = React.useState(false);
28
+ React.useEffect(() => {
29
+ if (!enabled || !dataUrl) {
30
+ setSeries({ points: [] });
31
+ setIsLoading(false);
32
+ return;
33
+ }
34
+ let cancelled = false;
35
+ const controller = new AbortController();
36
+ const load = async (showLoading) => {
37
+ if (showLoading) setIsLoading(true);
38
+ try {
39
+ const res = await fetch(dataUrl, { signal: controller.signal });
40
+ const text = await res.text();
41
+ if (cancelled) return;
42
+ setSeries(parseSensorSeries(text, dataFormat));
43
+ } catch (err) {
44
+ if (cancelled || controller.signal.aborted) return;
45
+ console.error("Error fetching sensor data:", err);
46
+ } finally {
47
+ if (!cancelled && showLoading) setIsLoading(false);
48
+ }
49
+ };
50
+ void load(true);
51
+ const timer = setInterval(() => void load(false), Math.max(updateFrequencyMs || 0, MIN_POLL_MS));
52
+ return () => {
53
+ cancelled = true;
54
+ controller.abort();
55
+ clearInterval(timer);
56
+ };
57
+ }, [dataUrl, dataFormat, updateFrequencyMs, enabled]);
58
+ return __spreadProps(__spreadValues({}, series), { isLoading });
59
+ }
60
+ export {
61
+ useSensorSeries
62
+ };
63
+ //# sourceMappingURL=useSensorSeries.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../src/core/components/ui/Sensors/useSensorSeries.ts"],"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 React from 'react'\r\n\r\nimport { parseSensorSeries, type SensorSeries } from './sensorData'\r\n\r\nimport type { SensorDataFormat } from '../../../types/dbTypes'\r\n\r\nconst MIN_POLL_MS = 1000\r\n\r\nexport interface UseSensorSeriesResult extends SensorSeries {\r\n isLoading: boolean\r\n}\r\n\r\n/**\r\n * Fetch `dataUrl`, parse it (CSV or OGC JSON via parseSensorSeries), and re-fetch on\r\n * `updateFrequencyMs` (floored at 1s) while `enabled`. Each tick replaces the series (the\r\n * synthetic API serves a rolling window ending \"now\"). Aborts in-flight fetches and clears\r\n * the interval on disable/unmount/param change; a failed poll keeps the last good series.\r\n */\r\nexport function useSensorSeries(\r\n dataUrl: string,\r\n dataFormat: SensorDataFormat | `${SensorDataFormat}`,\r\n updateFrequencyMs: number,\r\n opts: { enabled: boolean },\r\n): UseSensorSeriesResult {\r\n const { enabled } = opts\r\n const [series, setSeries] = React.useState<SensorSeries>({ points: [] })\r\n const [isLoading, setIsLoading] = React.useState(false)\r\n\r\n React.useEffect(() => {\r\n if (!enabled || !dataUrl) {\r\n setSeries({ points: [] })\r\n setIsLoading(false)\r\n return\r\n }\r\n\r\n let cancelled = false\r\n const controller = new AbortController()\r\n\r\n const load = async (showLoading: boolean) => {\r\n if (showLoading) setIsLoading(true)\r\n try {\r\n const res = await fetch(dataUrl, { signal: controller.signal })\r\n const text = await res.text()\r\n if (cancelled) return\r\n setSeries(parseSensorSeries(text, dataFormat))\r\n } catch (err) {\r\n if (cancelled || controller.signal.aborted) return\r\n console.error('Error fetching sensor data:', err)\r\n // keep last good series\r\n } finally {\r\n if (!cancelled && showLoading) setIsLoading(false)\r\n }\r\n }\r\n\r\n void load(true)\r\n const timer = setInterval(() => void load(false), Math.max(updateFrequencyMs || 0, MIN_POLL_MS))\r\n\r\n return () => {\r\n cancelled = true\r\n controller.abort()\r\n clearInterval(timer)\r\n }\r\n }, [dataUrl, dataFormat, updateFrequencyMs, enabled])\r\n\r\n return { ...series, isLoading }\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAKA,YAAY,WAAW;AAEvB,SAAS,yBAA4C;AAIrD,MAAM,cAAc;AAYb,SAAS,gBACd,SACA,YACA,mBACA,MACuB;AACvB,QAAM,EAAE,QAAQ,IAAI;AACpB,QAAM,CAAC,QAAQ,SAAS,IAAI,MAAM,SAAuB,EAAE,QAAQ,CAAC,EAAE,CAAC;AACvE,QAAM,CAAC,WAAW,YAAY,IAAI,MAAM,SAAS,KAAK;AAEtD,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,WAAW,CAAC,SAAS;AACxB,gBAAU,EAAE,QAAQ,CAAC,EAAE,CAAC;AACxB,mBAAa,KAAK;AAClB;AAAA,IACF;AAEA,QAAI,YAAY;AAChB,UAAM,aAAa,IAAI,gBAAgB;AAEvC,UAAM,OAAO,OAAO,gBAAyB;AAC3C,UAAI,YAAa,cAAa,IAAI;AAClC,UAAI;AACF,cAAM,MAAM,MAAM,MAAM,SAAS,EAAE,QAAQ,WAAW,OAAO,CAAC;AAC9D,cAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,YAAI,UAAW;AACf,kBAAU,kBAAkB,MAAM,UAAU,CAAC;AAAA,MAC/C,SAAS,KAAK;AACZ,YAAI,aAAa,WAAW,OAAO,QAAS;AAC5C,gBAAQ,MAAM,+BAA+B,GAAG;AAAA,MAElD,UAAE;AACA,YAAI,CAAC,aAAa,YAAa,cAAa,KAAK;AAAA,MACnD;AAAA,IACF;AAEA,SAAK,KAAK,IAAI;AACd,UAAM,QAAQ,YAAY,MAAM,KAAK,KAAK,KAAK,GAAG,KAAK,IAAI,qBAAqB,GAAG,WAAW,CAAC;AAE/F,WAAO,MAAM;AACX,kBAAY;AACZ,iBAAW,MAAM;AACjB,oBAAc,KAAK;AAAA,IACrB;AAAA,EACF,GAAG,CAAC,SAAS,YAAY,mBAAmB,OAAO,CAAC;AAEpD,SAAO,iCAAK,SAAL,EAAa,UAAU;AAChC;","names":[]}
@@ -98,7 +98,7 @@ function Viewer({ organization, minioBaseUrl, martinBaseUrl, pointcloudApiUrl, m
98
98
  const selectedViewer = /* @__PURE__ */ jsxs(Fragment, { children: [
99
99
  [ViewerNames.map, ViewerNames.bim, ViewerNames.pointcloud].includes(validViewer) && /* @__PURE__ */ jsx(SidebarTrigger, {}),
100
100
  /* @__PURE__ */ jsxs("div", { style: { height: "100%", width: "100%", position: "relative" }, children: [
101
- /* @__PURE__ */ jsx("div", { style: { display: validViewer === ViewerNames.map ? "block" : "none", width: "100%", height: "100%" }, children: /* @__PURE__ */ jsx(MapViewer, { organization, minioBaseUrl, maptilerKey }) }),
101
+ /* @__PURE__ */ jsx("div", { style: { display: validViewer === ViewerNames.map ? "block" : "none", width: "100%", height: "100%" }, children: /* @__PURE__ */ jsx(MapViewer, { organization, maptilerKey }) }),
102
102
  validViewer === ViewerNames.bim && /* @__PURE__ */ jsx(BimViewer, {}),
103
103
  validViewer === ViewerNames.pointcloud && /* @__PURE__ */ jsx(PointCloudViewer, { pointcloudApiUrl }),
104
104
  [ViewerNames.buildings, ViewerNames.sites, ViewerNames.files, ViewerNames.land, ViewerNames.infrastructure, ViewerNames.extensions, ViewerNames.users].includes(validViewer) && /* @__PURE__ */ jsx(DataMenu, { currentViewer: validViewer, organization, geocodeEarthApiKey, geocoderUrl }),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/core/components/viewers/Viewer.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\n// Dependencies\r\nimport { Loader2 } from 'lucide-react'\r\nimport dynamic from 'next/dynamic'\r\nimport { useSearchParams, useRouter, usePathname } from 'next/navigation'\r\nimport * as React from 'react'\r\n\r\nimport { BuildingsContext, MenusContext } from '../../store'\r\nimport { ViewerNames } from '../../types'\r\n\r\n// Deep file import (not the ui barrel): the barrel re-exports heavy modules\r\n// (InfoSidebar, useFileUploadHandler, …) that would land in the eager map-route bundle.\r\nimport { switchLanguage } from '../../utils/utils'\r\nimport { UserSettings } from '../settings'\r\nimport { Toolbar } from '../Toolbar'\r\nimport { SidebarTrigger } from '../ui/Sidebar'\r\n\r\nimport { MapViewer } from './map/MapViewer'\r\n\r\nimport type { Organization } from '../../types/dbTypes'\r\n\r\n// Code-split the heavy viewers: BimViewer pulls in ~456 KB gzipped of\r\n// @thatopen/*, PointCloudViewer the Potree loader stack — loaded only when\r\n// selected, so map-only sessions never pay their cost. MapViewer stays static\r\n// because every user lands on it.\r\nconst BimViewer = dynamic(\r\n () => import('./bim/BimViewer').then(m => ({ default: m.BimViewer })),\r\n { ssr: false, loading: () => <ViewerLoadingFallback label=\"Loading BIM viewer…\" /> },\r\n)\r\nconst PointCloudViewer = dynamic(\r\n () => import('./pointcloud/PointCloudViewer').then(m => ({ default: m.PointCloudViewer })),\r\n { ssr: false, loading: () => <ViewerLoadingFallback label=\"Loading point cloud viewer…\" /> },\r\n)\r\n\r\n// DataMenu transitively imports BimViewer (via FilePreview), so keep it lazy\r\n// too — a static import would pull @thatopen + three/webgpu back into the eager\r\n// bundle. Only rendered for the data viewers, so map-only sessions skip it.\r\nconst DataMenu = dynamic(\r\n () => import('./Data/DataMenu').then(m => ({ default: m.DataMenu })),\r\n { ssr: false, loading: () => <ViewerLoadingFallback label=\"Loading data…\" /> },\r\n)\r\n\r\n// Styled to match the in-viewer loading card (see BimLoadingState): a soft\r\n// backdrop-blurred overlay with a bordered card, spinner + label. Uses only\r\n// Tailwind tokens + one lucide icon, so it adds no meaningful weight to the\r\n// eager map-route bundle (Viewer.tsx is statically imported).\r\nfunction ViewerLoadingFallback({ label }: { label: string }) {\r\n return (\r\n <div className=\"absolute inset-0 z-10 flex items-center justify-center bg-background/50 backdrop-blur-[1px]\">\r\n <div className=\"flex items-center gap-3 rounded-md border border-border bg-card px-5 py-4 shadow-sm\">\r\n <Loader2 className=\"h-5 w-5 animate-spin text-muted-foreground\" />\r\n <span className=\"text-foreground text-base font-medium font-['Inter'] leading-7\">{label}</span>\r\n </div>\r\n </div>\r\n )\r\n}\r\n\r\ninterface ViewerProps {\r\n organization: Organization\r\n minioBaseUrl?: string\r\n martinBaseUrl?: string\r\n pointcloudApiUrl?: string\r\n maptilerKey?: string\r\n geocodeEarthApiKey?: string\r\n geocoderUrl?: string\r\n}\r\n\r\nexport function Viewer({ organization, minioBaseUrl, martinBaseUrl, pointcloudApiUrl, maptilerKey, geocodeEarthApiKey, geocoderUrl }: ViewerProps) {\r\n\r\n const searchParams = useSearchParams()\r\n const viewer = (searchParams.get('viewer') as ViewerNames) || ViewerNames.map\r\n\r\n const { dispatch: menusDispatch, state: menusState } = React.useContext(MenusContext)\r\n const { state: buildingState } = React.useContext(BuildingsContext)\r\n // const { dispatch: appConfigDispatch } = React.useContext(AppConfigContext)\r\n const { building } = buildingState.buildings\r\n const { currentViewer } = menusState.menus\r\n const router = useRouter()\r\n const pathname = usePathname()\r\n const [isMounted, setIsMounted] = React.useState(false)\r\n\r\n // Use a ref to track if we're currently updating -> prevent circular updates\r\n const isUpdatingRef = React.useRef(false)\r\n\r\n const { appContent } = organization\r\n\r\n // Check if viewer is valid based on appContent\r\n const isViewerValid = appContent.length === 0 || appContent.includes(viewer)\r\n\r\n // Use default viewer (map) if current viewer is not valid\r\n const validViewer = isViewerValid ? viewer : ViewerNames.map\r\n\r\n // Update URL if viewer was not available for the organization\r\n React.useEffect(() => {\r\n if (isMounted && !isViewerValid && viewer !== ViewerNames.map) {\r\n const newSearchParams = new URLSearchParams(searchParams.toString())\r\n newSearchParams.delete('viewer')\r\n if (building?.id) {\r\n newSearchParams.set('buildingId', String(building.id))\r\n }\r\n router.replace(`${pathname}?${newSearchParams.toString()}`, { scroll: false })\r\n }\r\n }, [isMounted, isViewerValid, viewer, searchParams, pathname, router, building?.id])\r\n\r\n // On first mount, respond to URL\r\n React.useEffect(() => {\r\n if (!isMounted) {\r\n menusDispatch({\r\n type: 'SET_VIEWER',\r\n payload: { currentViewer: validViewer },\r\n })\r\n setIsMounted(true)\r\n }\r\n }, [isMounted, validViewer, menusDispatch])\r\n\r\n React.useEffect(() => {\r\n if (!organization) return\r\n const defaultLanguage = organization.languages?.[0] || 'En'\r\n if (defaultLanguage) switchLanguage(defaultLanguage)\r\n }, [organization])\r\n\r\n // Handle context changes (from sidebar, HeaderButtons, etc.)\r\n React.useEffect(() => {\r\n if (!isMounted || !currentViewer || currentViewer === validViewer || isUpdatingRef.current) {\r\n return\r\n }\r\n\r\n isUpdatingRef.current = true\r\n\r\n // Create URLSearchParams to preserve existing search params\r\n\r\n const newSearchParams = new URLSearchParams(searchParams.toString())\r\n newSearchParams.set('viewer', currentViewer)\r\n if (building?.id) {\r\n newSearchParams.set('buildingId', String(building.id))\r\n }\r\n\r\n\r\n router.replace(`${pathname}?${newSearchParams.toString()}`, { scroll: false })\r\n\r\n // Reset the flag after a brief delay to allow the navigation to complete\r\n setTimeout(() => {\r\n isUpdatingRef.current = false\r\n }, 100)\r\n }, [currentViewer, isMounted, pathname, router, viewer, searchParams])\r\n\r\n // Handle URL changes (direct navigation)\r\n React.useEffect(() => {\r\n if (isMounted && currentViewer !== validViewer && !isUpdatingRef.current) {\r\n isUpdatingRef.current = true\r\n\r\n menusDispatch({\r\n type: 'SET_VIEWER',\r\n payload: { currentViewer: validViewer },\r\n })\r\n\r\n // Reset the flag after a brief delay\r\n setTimeout(() => {\r\n isUpdatingRef.current = false\r\n }, 100)\r\n }\r\n }, [validViewer, menusDispatch, isMounted, currentViewer])\r\n\r\n const selectedViewer = (\r\n <>\r\n {[ViewerNames.map, ViewerNames.bim, ViewerNames.pointcloud].includes(validViewer) && <SidebarTrigger />}\r\n <div style={{ height: '100%', width: '100%', position: 'relative' }}>\r\n <div style={{ display: validViewer === ViewerNames.map ? 'block' : 'none', width: '100%', height: '100%' }}>\r\n <MapViewer organization={organization} minioBaseUrl={minioBaseUrl} maptilerKey={maptilerKey} />\r\n </div>\r\n {validViewer === ViewerNames.bim && <BimViewer />}\r\n {validViewer === ViewerNames.pointcloud && <PointCloudViewer pointcloudApiUrl={pointcloudApiUrl} />}\r\n {[ViewerNames.buildings, ViewerNames.sites, ViewerNames.files, ViewerNames.land, ViewerNames.infrastructure, ViewerNames.extensions, ViewerNames.users].includes(validViewer) && (\r\n <DataMenu currentViewer={validViewer} organization={organization} geocodeEarthApiKey={geocodeEarthApiKey} geocoderUrl={geocoderUrl} />\r\n )}\r\n {[ViewerNames.settings].includes(validViewer) && (\r\n <UserSettings minioBaseUrl={minioBaseUrl} />\r\n )}\r\n </div>\r\n </>\r\n )\r\n\r\n return (\r\n <>\r\n {selectedViewer}\r\n <Toolbar viewer={validViewer} minioBaseUrl={minioBaseUrl} martinBaseUrl={martinBaseUrl} organization={organization} geocodeEarthApiKey={geocodeEarthApiKey} geocoderUrl={geocoderUrl} />\r\n </>\r\n )\r\n}"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../../src/core/components/viewers/Viewer.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\n// Dependencies\r\nimport { Loader2 } from 'lucide-react'\r\nimport dynamic from 'next/dynamic'\r\nimport { useSearchParams, useRouter, usePathname } from 'next/navigation'\r\nimport * as React from 'react'\r\n\r\nimport { BuildingsContext, MenusContext } from '../../store'\r\nimport { ViewerNames } from '../../types'\r\n\r\n// Deep file import (not the ui barrel): the barrel re-exports heavy modules\r\n// (InfoSidebar, useFileUploadHandler, …) that would land in the eager map-route bundle.\r\nimport { switchLanguage } from '../../utils/utils'\r\nimport { UserSettings } from '../settings'\r\nimport { Toolbar } from '../Toolbar'\r\nimport { SidebarTrigger } from '../ui/Sidebar'\r\n\r\nimport { MapViewer } from './map/MapViewer'\r\n\r\nimport type { Organization } from '../../types/dbTypes'\r\n\r\n// Code-split the heavy viewers: BimViewer pulls in ~456 KB gzipped of\r\n// @thatopen/*, PointCloudViewer the Potree loader stack — loaded only when\r\n// selected, so map-only sessions never pay their cost. MapViewer stays static\r\n// because every user lands on it.\r\nconst BimViewer = dynamic(\r\n () => import('./bim/BimViewer').then(m => ({ default: m.BimViewer })),\r\n { ssr: false, loading: () => <ViewerLoadingFallback label=\"Loading BIM viewer…\" /> },\r\n)\r\nconst PointCloudViewer = dynamic(\r\n () => import('./pointcloud/PointCloudViewer').then(m => ({ default: m.PointCloudViewer })),\r\n { ssr: false, loading: () => <ViewerLoadingFallback label=\"Loading point cloud viewer…\" /> },\r\n)\r\n\r\n// DataMenu transitively imports BimViewer (via FilePreview), so keep it lazy\r\n// too — a static import would pull @thatopen + three/webgpu back into the eager\r\n// bundle. Only rendered for the data viewers, so map-only sessions skip it.\r\nconst DataMenu = dynamic(\r\n () => import('./Data/DataMenu').then(m => ({ default: m.DataMenu })),\r\n { ssr: false, loading: () => <ViewerLoadingFallback label=\"Loading data…\" /> },\r\n)\r\n\r\n// Styled to match the in-viewer loading card (see BimLoadingState): a soft\r\n// backdrop-blurred overlay with a bordered card, spinner + label. Uses only\r\n// Tailwind tokens + one lucide icon, so it adds no meaningful weight to the\r\n// eager map-route bundle (Viewer.tsx is statically imported).\r\nfunction ViewerLoadingFallback({ label }: { label: string }) {\r\n return (\r\n <div className=\"absolute inset-0 z-10 flex items-center justify-center bg-background/50 backdrop-blur-[1px]\">\r\n <div className=\"flex items-center gap-3 rounded-md border border-border bg-card px-5 py-4 shadow-sm\">\r\n <Loader2 className=\"h-5 w-5 animate-spin text-muted-foreground\" />\r\n <span className=\"text-foreground text-base font-medium font-['Inter'] leading-7\">{label}</span>\r\n </div>\r\n </div>\r\n )\r\n}\r\n\r\ninterface ViewerProps {\r\n organization: Organization\r\n minioBaseUrl?: string\r\n martinBaseUrl?: string\r\n pointcloudApiUrl?: string\r\n maptilerKey?: string\r\n geocodeEarthApiKey?: string\r\n geocoderUrl?: string\r\n}\r\n\r\nexport function Viewer({ organization, minioBaseUrl, martinBaseUrl, pointcloudApiUrl, maptilerKey, geocodeEarthApiKey, geocoderUrl }: ViewerProps) {\r\n\r\n const searchParams = useSearchParams()\r\n const viewer = (searchParams.get('viewer') as ViewerNames) || ViewerNames.map\r\n\r\n const { dispatch: menusDispatch, state: menusState } = React.useContext(MenusContext)\r\n const { state: buildingState } = React.useContext(BuildingsContext)\r\n // const { dispatch: appConfigDispatch } = React.useContext(AppConfigContext)\r\n const { building } = buildingState.buildings\r\n const { currentViewer } = menusState.menus\r\n const router = useRouter()\r\n const pathname = usePathname()\r\n const [isMounted, setIsMounted] = React.useState(false)\r\n\r\n // Use a ref to track if we're currently updating -> prevent circular updates\r\n const isUpdatingRef = React.useRef(false)\r\n\r\n const { appContent } = organization\r\n\r\n // Check if viewer is valid based on appContent\r\n const isViewerValid = appContent.length === 0 || appContent.includes(viewer)\r\n\r\n // Use default viewer (map) if current viewer is not valid\r\n const validViewer = isViewerValid ? viewer : ViewerNames.map\r\n\r\n // Update URL if viewer was not available for the organization\r\n React.useEffect(() => {\r\n if (isMounted && !isViewerValid && viewer !== ViewerNames.map) {\r\n const newSearchParams = new URLSearchParams(searchParams.toString())\r\n newSearchParams.delete('viewer')\r\n if (building?.id) {\r\n newSearchParams.set('buildingId', String(building.id))\r\n }\r\n router.replace(`${pathname}?${newSearchParams.toString()}`, { scroll: false })\r\n }\r\n }, [isMounted, isViewerValid, viewer, searchParams, pathname, router, building?.id])\r\n\r\n // On first mount, respond to URL\r\n React.useEffect(() => {\r\n if (!isMounted) {\r\n menusDispatch({\r\n type: 'SET_VIEWER',\r\n payload: { currentViewer: validViewer },\r\n })\r\n setIsMounted(true)\r\n }\r\n }, [isMounted, validViewer, menusDispatch])\r\n\r\n React.useEffect(() => {\r\n if (!organization) return\r\n const defaultLanguage = organization.languages?.[0] || 'En'\r\n if (defaultLanguage) switchLanguage(defaultLanguage)\r\n }, [organization])\r\n\r\n // Handle context changes (from sidebar, HeaderButtons, etc.)\r\n React.useEffect(() => {\r\n if (!isMounted || !currentViewer || currentViewer === validViewer || isUpdatingRef.current) {\r\n return\r\n }\r\n\r\n isUpdatingRef.current = true\r\n\r\n // Create URLSearchParams to preserve existing search params\r\n\r\n const newSearchParams = new URLSearchParams(searchParams.toString())\r\n newSearchParams.set('viewer', currentViewer)\r\n if (building?.id) {\r\n newSearchParams.set('buildingId', String(building.id))\r\n }\r\n\r\n\r\n router.replace(`${pathname}?${newSearchParams.toString()}`, { scroll: false })\r\n\r\n // Reset the flag after a brief delay to allow the navigation to complete\r\n setTimeout(() => {\r\n isUpdatingRef.current = false\r\n }, 100)\r\n }, [currentViewer, isMounted, pathname, router, viewer, searchParams])\r\n\r\n // Handle URL changes (direct navigation)\r\n React.useEffect(() => {\r\n if (isMounted && currentViewer !== validViewer && !isUpdatingRef.current) {\r\n isUpdatingRef.current = true\r\n\r\n menusDispatch({\r\n type: 'SET_VIEWER',\r\n payload: { currentViewer: validViewer },\r\n })\r\n\r\n // Reset the flag after a brief delay\r\n setTimeout(() => {\r\n isUpdatingRef.current = false\r\n }, 100)\r\n }\r\n }, [validViewer, menusDispatch, isMounted, currentViewer])\r\n\r\n const selectedViewer = (\r\n <>\r\n {[ViewerNames.map, ViewerNames.bim, ViewerNames.pointcloud].includes(validViewer) && <SidebarTrigger />}\r\n <div style={{ height: '100%', width: '100%', position: 'relative' }}>\r\n <div style={{ display: validViewer === ViewerNames.map ? 'block' : 'none', width: '100%', height: '100%' }}>\r\n <MapViewer organization={organization} maptilerKey={maptilerKey} />\r\n </div>\r\n {validViewer === ViewerNames.bim && <BimViewer />}\r\n {validViewer === ViewerNames.pointcloud && <PointCloudViewer pointcloudApiUrl={pointcloudApiUrl} />}\r\n {[ViewerNames.buildings, ViewerNames.sites, ViewerNames.files, ViewerNames.land, ViewerNames.infrastructure, ViewerNames.extensions, ViewerNames.users].includes(validViewer) && (\r\n <DataMenu currentViewer={validViewer} organization={organization} geocodeEarthApiKey={geocodeEarthApiKey} geocoderUrl={geocoderUrl} />\r\n )}\r\n {[ViewerNames.settings].includes(validViewer) && (\r\n <UserSettings minioBaseUrl={minioBaseUrl} />\r\n )}\r\n </div>\r\n </>\r\n )\r\n\r\n return (\r\n <>\r\n {selectedViewer}\r\n <Toolbar viewer={validViewer} minioBaseUrl={minioBaseUrl} martinBaseUrl={martinBaseUrl} organization={organization} geocodeEarthApiKey={geocodeEarthApiKey} geocoderUrl={geocoderUrl} />\r\n </>\r\n )\r\n}"],"mappings":"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","names":[]}
@@ -5,7 +5,5 @@ export interface FileItem {
5
5
  icon: React.ElementType;
6
6
  visible: boolean;
7
7
  }
8
- export declare function SensorsTab({ minioBaseUrl }: {
9
- minioBaseUrl?: string;
10
- }): React.JSX.Element;
8
+ export declare function SensorsTab(): React.JSX.Element;
11
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../../../src/core/components/viewers/bim/src/BimSidebar/src/SensorsTab/index.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAI9B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oCAAoC,CAAA;AAChE,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,KAAK,CAAC,WAAW,CAAA;IACvB,OAAO,EAAE,OAAO,CAAA;CACjB;AAED,wBAAgB,UAAU,CAAC,EAAE,YAAY,EAAE,EAAE;IAAE,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,qBAMrE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../../../src/core/components/viewers/bim/src/BimSidebar/src/SensorsTab/index.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAI9B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oCAAoC,CAAA;AAChE,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,KAAK,CAAC,WAAW,CAAA;IACvB,OAAO,EAAE,OAAO,CAAA;CACjB;AAED,wBAAgB,UAAU,sBAMzB"}
@@ -1,8 +1,8 @@
1
1
  "use client";
2
2
  import { jsx } from "react/jsx-runtime";
3
3
  import { SensorsSection } from "../../../../../../ui/Sensors/SensorsSection";
4
- function SensorsTab({ minioBaseUrl }) {
5
- return /* @__PURE__ */ jsx("div", { className: "flex-1 flex flex-col space-y-6 py-4 overflow-hidden", children: /* @__PURE__ */ jsx(SensorsSection, { minioBaseUrl }) });
4
+ function SensorsTab() {
5
+ return /* @__PURE__ */ jsx("div", { className: "flex-1 flex flex-col space-y-6 py-4 overflow-hidden", children: /* @__PURE__ */ jsx(SensorsSection, {}) });
6
6
  }
7
7
  export {
8
8
  SensorsTab
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../../../src/core/components/viewers/bim/src/BimSidebar/src/SensorsTab/index.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 React from 'react'\r\n\r\nimport { SensorsSection } from '../../../../../../ui/Sensors/SensorsSection'\r\n\r\nimport type { DbFile } from '../../../../../../../types/dbTypes'\r\nexport interface FileItem {\r\n task: DbFile\r\n icon: React.ElementType\r\n visible: boolean\r\n}\r\n\r\nexport function SensorsTab({ minioBaseUrl }: { minioBaseUrl?: string }) {\r\n return (\r\n <div className=\"flex-1 flex flex-col space-y-6 py-4 overflow-hidden\">\r\n <SensorsSection minioBaseUrl={minioBaseUrl} />\r\n </div>\r\n )\r\n}\r\n"],"mappings":";AAmBM;AAZN,SAAS,sBAAsB;AASxB,SAAS,WAAW,EAAE,aAAa,GAA8B;AACtE,SACE,oBAAC,SAAI,WAAU,uDACb,8BAAC,kBAAe,cAA4B,GAC9C;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../../../../../../../src/core/components/viewers/bim/src/BimSidebar/src/SensorsTab/index.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 React from 'react'\r\n\r\nimport { SensorsSection } from '../../../../../../ui/Sensors/SensorsSection'\r\n\r\nimport type { DbFile } from '../../../../../../../types/dbTypes'\r\nexport interface FileItem {\r\n task: DbFile\r\n icon: React.ElementType\r\n visible: boolean\r\n}\r\n\r\nexport function SensorsTab() {\r\n return (\r\n <div className=\"flex-1 flex flex-col space-y-6 py-4 overflow-hidden\">\r\n <SensorsSection />\r\n </div>\r\n )\r\n}\r\n"],"mappings":";AAmBM;AAZN,SAAS,sBAAsB;AASxB,SAAS,aAAa;AAC3B,SACE,oBAAC,SAAI,WAAU,uDACb,8BAAC,kBAAe,GAClB;AAEJ;","names":[]}
@@ -32,7 +32,7 @@ function BimSidebar({ minioBaseUrl, organization }) {
32
32
  selectedTab === "file" && /* @__PURE__ */ jsx(FileTab, {}),
33
33
  selectedTab === "layers" && /* @__PURE__ */ jsx(LayersTab, { modelId }),
34
34
  selectedTab === "communication" && /* @__PURE__ */ jsx(CommunicationTab, {}),
35
- selectedTab === "sensors" && /* @__PURE__ */ jsx(SensorsTab, { minioBaseUrl }),
35
+ selectedTab === "sensors" && /* @__PURE__ */ jsx(SensorsTab, {}),
36
36
  selectedTab === "settings" && /* @__PURE__ */ jsx(SettingsTab, {})
37
37
  ]
38
38
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../../src/core/components/viewers/bim/src/BimSidebar/src/index.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 React from 'react'\r\n\r\nimport { BimContext, MenusContext } from '../../../../../../store'\r\nimport { InfoSidebarContainer } from '../../../../../ui/InfoSidebar/Container'\r\n\r\nimport { CommunicationTab } from './CommunicationTab'\r\nimport { FileTab } from './FileTab'\r\nimport { LayersTab } from './LayersTab'\r\nimport { SensorsTab } from './SensorsTab'\r\nimport { SettingsTab } from './SettingsTab'\r\nimport { TabSelector } from './TabSelector'\r\n\r\nimport type { SidebarTabType } from '../../../../../../store/Menus/reducer'\r\nimport type { Organization } from '../../../../../../types/dbTypes'\r\n\r\nexport function BimSidebar({ minioBaseUrl, organization }: { minioBaseUrl?: string; organization?: Organization }) {\r\n const { state: bimState } = React.useContext(BimContext)\r\n const { modelId } = bimState.bim\r\n const { state: menusState, dispatch: menusDispatch } = React.useContext(MenusContext)\r\n const { selectedTab } = menusState.menus\r\n\r\n const handleTabChange = (selectedTab: SidebarTabType) => {\r\n menusDispatch({ type: 'SET_SIDEBAR_SELECTED_TAB', payload: { selectedTab } })\r\n }\r\n\r\n return (\r\n <InfoSidebarContainer\r\n organization={organization}\r\n tabSelector={\r\n <TabSelector\r\n activeTab={selectedTab}\r\n onTabChangeAction={handleTabChange}\r\n />\r\n }\r\n >\r\n {selectedTab === 'file' && <FileTab />}\r\n {selectedTab === 'layers' && <LayersTab modelId={modelId} />}\r\n {selectedTab === 'communication' && <CommunicationTab />}\r\n {selectedTab === 'sensors' && <SensorsTab minioBaseUrl={minioBaseUrl} />}\r\n {selectedTab === 'settings' && <SettingsTab />}\r\n </InfoSidebarContainer>\r\n )\r\n}"],"mappings":";AA+BI,SAGI,KAHJ;AA1BJ,YAAY,WAAW;AAEvB,SAAS,YAAY,oBAAoB;AACzC,SAAS,4BAA4B;AAErC,SAAS,wBAAwB;AACjC,SAAS,eAAe;AACxB,SAAS,iBAAiB;AAC1B,SAAS,kBAAkB;AAC3B,SAAS,mBAAmB;AAC5B,SAAS,mBAAmB;AAKrB,SAAS,WAAW,EAAE,cAAc,aAAa,GAA2D;AACjH,QAAM,EAAE,OAAO,SAAS,IAAI,MAAM,WAAW,UAAU;AACvD,QAAM,EAAE,QAAQ,IAAI,SAAS;AAC7B,QAAM,EAAE,OAAO,YAAY,UAAU,cAAc,IAAI,MAAM,WAAW,YAAY;AACpF,QAAM,EAAE,YAAY,IAAI,WAAW;AAEnC,QAAM,kBAAkB,CAACA,iBAAgC;AACvD,kBAAc,EAAE,MAAM,4BAA4B,SAAS,EAAE,aAAAA,aAAY,EAAE,CAAC;AAAA,EAC9E;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,aACE;AAAA,QAAC;AAAA;AAAA,UACC,WAAW;AAAA,UACX,mBAAmB;AAAA;AAAA,MACrB;AAAA,MAGD;AAAA,wBAAgB,UAAU,oBAAC,WAAQ;AAAA,QACnC,gBAAgB,YAAY,oBAAC,aAAU,SAAkB;AAAA,QACzD,gBAAgB,mBAAmB,oBAAC,oBAAiB;AAAA,QACrD,gBAAgB,aAAa,oBAAC,cAAW,cAA4B;AAAA,QACrE,gBAAgB,cAAc,oBAAC,eAAY;AAAA;AAAA;AAAA,EAC9C;AAEJ;","names":["selectedTab"]}
1
+ {"version":3,"sources":["../../../../../../../../src/core/components/viewers/bim/src/BimSidebar/src/index.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 React from 'react'\r\n\r\nimport { BimContext, MenusContext } from '../../../../../../store'\r\nimport { InfoSidebarContainer } from '../../../../../ui/InfoSidebar/Container'\r\n\r\nimport { CommunicationTab } from './CommunicationTab'\r\nimport { FileTab } from './FileTab'\r\nimport { LayersTab } from './LayersTab'\r\nimport { SensorsTab } from './SensorsTab'\r\nimport { SettingsTab } from './SettingsTab'\r\nimport { TabSelector } from './TabSelector'\r\n\r\nimport type { SidebarTabType } from '../../../../../../store/Menus/reducer'\r\nimport type { Organization } from '../../../../../../types/dbTypes'\r\n\r\nexport function BimSidebar({ minioBaseUrl, organization }: { minioBaseUrl?: string; organization?: Organization }) {\r\n const { state: bimState } = React.useContext(BimContext)\r\n const { modelId } = bimState.bim\r\n const { state: menusState, dispatch: menusDispatch } = React.useContext(MenusContext)\r\n const { selectedTab } = menusState.menus\r\n\r\n const handleTabChange = (selectedTab: SidebarTabType) => {\r\n menusDispatch({ type: 'SET_SIDEBAR_SELECTED_TAB', payload: { selectedTab } })\r\n }\r\n\r\n return (\r\n <InfoSidebarContainer\r\n organization={organization}\r\n tabSelector={\r\n <TabSelector\r\n activeTab={selectedTab}\r\n onTabChangeAction={handleTabChange}\r\n />\r\n }\r\n >\r\n {selectedTab === 'file' && <FileTab />}\r\n {selectedTab === 'layers' && <LayersTab modelId={modelId} />}\r\n {selectedTab === 'communication' && <CommunicationTab />}\r\n {selectedTab === 'sensors' && <SensorsTab />}\r\n {selectedTab === 'settings' && <SettingsTab />}\r\n </InfoSidebarContainer>\r\n )\r\n}"],"mappings":";AA+BI,SAGI,KAHJ;AA1BJ,YAAY,WAAW;AAEvB,SAAS,YAAY,oBAAoB;AACzC,SAAS,4BAA4B;AAErC,SAAS,wBAAwB;AACjC,SAAS,eAAe;AACxB,SAAS,iBAAiB;AAC1B,SAAS,kBAAkB;AAC3B,SAAS,mBAAmB;AAC5B,SAAS,mBAAmB;AAKrB,SAAS,WAAW,EAAE,cAAc,aAAa,GAA2D;AACjH,QAAM,EAAE,OAAO,SAAS,IAAI,MAAM,WAAW,UAAU;AACvD,QAAM,EAAE,QAAQ,IAAI,SAAS;AAC7B,QAAM,EAAE,OAAO,YAAY,UAAU,cAAc,IAAI,MAAM,WAAW,YAAY;AACpF,QAAM,EAAE,YAAY,IAAI,WAAW;AAEnC,QAAM,kBAAkB,CAACA,iBAAgC;AACvD,kBAAc,EAAE,MAAM,4BAA4B,SAAS,EAAE,aAAAA,aAAY,EAAE,CAAC;AAAA,EAC9E;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,aACE;AAAA,QAAC;AAAA;AAAA,UACC,WAAW;AAAA,UACX,mBAAmB;AAAA;AAAA,MACrB;AAAA,MAGD;AAAA,wBAAgB,UAAU,oBAAC,WAAQ;AAAA,QACnC,gBAAgB,YAAY,oBAAC,aAAU,SAAkB;AAAA,QACzD,gBAAgB,mBAAmB,oBAAC,oBAAiB;AAAA,QACrD,gBAAgB,aAAa,oBAAC,cAAW;AAAA,QACzC,gBAAgB,cAAc,oBAAC,eAAY;AAAA;AAAA;AAAA,EAC9C;AAEJ;","names":["selectedTab"]}
@@ -2,9 +2,8 @@ import * as React from "react";
2
2
  import type { Tool } from "../../../../../../types/tools";
3
3
  interface AddToBimProps {
4
4
  tool: Tool;
5
- minioBaseUrl?: string;
6
5
  [key: string]: unknown;
7
6
  }
8
- export default function AddToBim({ tool, minioBaseUrl }: AddToBimProps): React.JSX.Element;
7
+ export default function AddToBim({ tool }: AddToBimProps): React.JSX.Element;
9
8
  export {};
10
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../../src/core/components/viewers/bim/src/tools/AddToBim/index.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAsB9B,OAAO,KAAK,EAAE,IAAI,EAAmB,MAAM,+BAA+B,CAAA;AAI1E,UAAU,aAAa;IACrB,IAAI,EAAE,IAAI,CAAA;IACV,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AAkCD,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,aAAa,qBA2PrE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../../src/core/components/viewers/bim/src/tools/AddToBim/index.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAuB9B,OAAO,KAAK,EAAE,IAAI,EAAmB,MAAM,+BAA+B,CAAA;AAI1E,UAAU,aAAa;IACrB,IAAI,EAAE,IAAI,CAAA;IACV,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AAkCD,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,EAAE,IAAI,EAAE,EAAE,aAAa,qBA+QvD"}
@@ -11,6 +11,7 @@ import { ViewerNames } from "../../../../../../types/dbTypes";
11
11
  import { Button, useSidebar } from "../../../../../ui";
12
12
  import { CommentInput } from "../../../../../ui/Comments/CommentInput";
13
13
  import { ViewerContextMenu } from "../../../../../ui/FilesManager";
14
+ import { SensorDetailDialog } from "../../../../../ui/Sensors/SensorDetailDialog";
14
15
  import { SensorInput } from "../../../../../ui/Sensors/SensorInput";
15
16
  import { FileAdderDialog } from "../../../../map/src/tools/AddTools/AddFile/FileAdder";
16
17
  import { AddToBimToolbar } from "./src/AddToBimToolbar";
@@ -43,7 +44,7 @@ const FilePreview = ({ file, mousePosition }) => {
43
44
  }
44
45
  );
45
46
  };
46
- function AddToBim({ tool, minioBaseUrl }) {
47
+ function AddToBim({ tool }) {
47
48
  var _a, _b;
48
49
  const t = useTranslations("AddToBim");
49
50
  const { dispatch: toolsDispatch, state: toolsState } = React.useContext(ToolsContext);
@@ -56,7 +57,7 @@ function AddToBim({ tool, minioBaseUrl }) {
56
57
  const [addingMode, setAddingMode] = React.useState(null);
57
58
  const [contextMenu, setContextMenu] = React.useState(null);
58
59
  const { addPendingComment, removePendingComment, commentCount } = useCommentMarkers(world, buildingId);
59
- const { addPendingSensor, removePendingSensor, sensorCount } = useSensorMarkers(world, buildingId, minioBaseUrl);
60
+ const { addPendingSensor, removePendingSensor, sensorCount, detailSensor, detailSensorType, closeSensorDetail, editSensor, closeSensorEdit } = useSensorMarkers(world, buildingId);
60
61
  const { uploadFile } = useUploadFileToBuilding(buildingId);
61
62
  const { deleteFile } = useDeleteFile(buildingId);
62
63
  const filesData = useFilesByBuildingId(buildingId).files || [];
@@ -258,6 +259,28 @@ function AddToBim({ tool, minioBaseUrl }) {
258
259
  }
259
260
  }
260
261
  ),
262
+ /* @__PURE__ */ jsx(
263
+ SensorInput,
264
+ {
265
+ viewer: ViewerNames.bim,
266
+ layout: "dialog",
267
+ isOpen: !!editSensor,
268
+ editSensor: editSensor != null ? editSensor : void 0,
269
+ onCancel: closeSensorEdit,
270
+ onSaved: closeSensorEdit
271
+ }
272
+ ),
273
+ detailSensor && /* @__PURE__ */ jsx(
274
+ SensorDetailDialog,
275
+ {
276
+ open: !!detailSensor,
277
+ onOpenChange: (o) => {
278
+ if (!o) closeSensorDetail();
279
+ },
280
+ sensor: detailSensor,
281
+ sensorType: detailSensorType
282
+ }
283
+ ),
261
284
  contextMenu && /* @__PURE__ */ jsx(
262
285
  ViewerContextMenu,
263
286
  {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../../src/core/components/viewers/bim/src/tools/AddToBim/index.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 Image from \"next/image\"\r\nimport { useTranslations } from 'next-intl'\r\nimport * as React from \"react\"\r\n\r\n\r\nimport { useFilesByBuildingId, useUploadFileToBuilding, useDeleteFile } from \"../../../../../../hooks/files/files\"\r\nimport { BuildingsContext, MenusContext, ToolsContext } from \"../../../../../../store\"\r\nimport { BimContext } from \"../../../../../../store/BIM/context\"\r\nimport { ViewerNames } from \"../../../../../../types/dbTypes\"\r\nimport { Button, useSidebar } from \"../../../../../ui\"\r\nimport { CommentInput } from \"../../../../../ui/Comments/CommentInput\"\r\nimport { ViewerContextMenu } from \"../../../../../ui/FilesManager\"\r\nimport { SensorInput } from \"../../../../../ui/Sensors/SensorInput\"\r\nimport { FileAdderDialog } from \"../../../../map/src/tools/AddTools/AddFile/FileAdder\"\r\n\r\nimport { AddToBimToolbar } from \"./src/AddToBimToolbar\"\r\nimport { initializeCSS2DRenderer } from \"./src/FileMarkerUtils\"\r\nimport Position3DCard from \"./src/Position3DCard\"\r\nimport { useCommentMarkers } from \"./src/useCommentMarkers\"\r\nimport { useFilePlacement } from \"./src/useFilePlacement\"\r\nimport { useSensorMarkers } from \"./src/useSensorMarkers\"\r\n\r\nimport type { DbFile } from \"../../../../../../types/dbTypes\"\r\nimport type { FileAction } from \"../../../../../../types/global\"\r\nimport type { Tool, ToolbarToolType } from \"../../../../../../types/tools\"\r\nimport type { FileMarkerAction } from \"../../../../../ui/FilesManager/src/FileMarker\"\r\nimport type { BimToolbarToolsType } from \"../bimToolbar\"\r\n\r\ninterface AddToBimProps {\r\n tool: Tool\r\n minioBaseUrl?: string\r\n [key: string]: unknown\r\n}\r\n\r\nconst FilePreview: React.FC<{\r\n file: File\r\n mousePosition: { x: number; y: number }\r\n}> = ({ file, mousePosition }) => {\r\n const [imageUrl, setImageUrl] = React.useState<string | null>(null)\r\n const isImage = file.type.startsWith(\"image/\")\r\n\r\n React.useEffect(() => {\r\n if (isImage) {\r\n const url = URL.createObjectURL(file)\r\n setImageUrl(url)\r\n return () => URL.revokeObjectURL(url)\r\n }\r\n }, [file, isImage])\r\n\r\n return (\r\n <Button\r\n variant=\"outline\"\r\n size=\"sm\"\r\n className=\"fixed pointer-events-none z-[9999] bg-white/95 flex items-center gap-2 cursor-crosshair text-xs\"\r\n style={{ left: mousePosition.x + 10, top: mousePosition.y - 10 }}\r\n >\r\n {isImage && imageUrl ? (\r\n <Image src={imageUrl} alt={file.name} width={16} height={16} className=\"object-cover rounded\" />\r\n ) : (\r\n <LR.File size={16} className=\"text-black\" />\r\n )}\r\n <span className=\"overflow-hidden text-ellipsis whitespace-nowrap flex-1\">{file.name}</span>\r\n </Button>\r\n )\r\n}\r\n\r\nexport default function AddToBim({ tool, minioBaseUrl }: AddToBimProps) {\r\n const t = useTranslations('AddToBim')\r\n const { dispatch: toolsDispatch, state: toolsState } = React.useContext(ToolsContext)\r\n const { state: bimState } = React.useContext(BimContext)\r\n const { bimComponents, world, fragments } = bimState.bim\r\n const { state: buildingState } = React.useContext(BuildingsContext)\r\n const buildingId = buildingState?.buildings?.building?.id || -1\r\n const { dispatch: menusDispatch } = React.useContext(MenusContext)\r\n const { setOpenInfo } = useSidebar()\r\n\r\n const [addingMode, setAddingMode] = React.useState<BimToolbarToolsType>(null)\r\n\r\n // Context menu state for right-click on BIM scene objects\r\n const [contextMenu, setContextMenu] = React.useState<{ x: number; y: number; file: DbFile } | null>(null)\r\n\r\n // Extracted hooks\r\n const { addPendingComment, removePendingComment, commentCount } = useCommentMarkers(world, buildingId)\r\n const { addPendingSensor, removePendingSensor, sensorCount } = useSensorMarkers(world, buildingId, minioBaseUrl)\r\n const { uploadFile } = useUploadFileToBuilding(buildingId)\r\n const { deleteFile } = useDeleteFile(buildingId)\r\n\r\n // Visible (added-to-scene) non-BIM files; also used to resolve a marker to its DB record.\r\n const filesData = useFilesByBuildingId(buildingId).files || []\r\n const filesDataRef = React.useRef(filesData)\r\n filesDataRef.current = filesData\r\n const visibleFileCount = filesData.filter(f => {\r\n const ext = f.extension?.toLowerCase()\r\n return ext !== \"ifc\" && ext !== \"frag\" && f.tag !== \"user\" && f.tag !== \"bim-file\" && f.isVisible\r\n }).length\r\n\r\n // Marker actions reach into the placement API after it is created, so route through a ref.\r\n const placementRef = React.useRef<ReturnType<typeof useFilePlacement> | null>(null)\r\n const handleMarkerAction = React.useCallback((id: string, action: FileMarkerAction) => {\r\n const api = placementRef.current\r\n if (!api) return\r\n if (action === \"delete\") {\r\n const placed = api.getPlacedFile(id)\r\n api.removePlacedFile(id)\r\n const dbFile = placed\r\n ? filesDataRef.current.find(f => f.name === placed.name && (f.id ?? 0) > 0)\r\n : undefined\r\n if (dbFile?.id) deleteFile(dbFile.id).catch(() => { })\r\n return\r\n }\r\n api.editPlacedFile(id, action === \"move\" ? \"translate\" : action)\r\n }, [deleteFile])\r\n\r\n const filePlacement = useFilePlacement(\r\n bimComponents, world, fragments, toolsDispatch, buildingId, uploadFile,\r\n handleMarkerAction,\r\n )\r\n placementRef.current = filePlacement\r\n\r\n // Initialize CSS2D renderer\r\n React.useEffect(() => {\r\n if (world) initializeCSS2DRenderer(world)\r\n }, [world])\r\n\r\n // Canvas-level right-click: raycast to detect hit, show context menu\r\n React.useEffect(() => {\r\n if (!world || !fragments) return\r\n const canvas = world.renderer?.three?.domElement\r\n if (!canvas) return\r\n\r\n const handleContextMenu = async (e: MouseEvent) => {\r\n // Prevent the browser default menu immediately, regardless of what we hit\r\n e.preventDefault()\r\n\r\n const mouse = new (await import(\"three\")).Vector2(e.clientX, e.clientY)\r\n const result = await filePlacement.raycast({\r\n camera: world.camera.three,\r\n mouse,\r\n dom: canvas,\r\n })\r\n\r\n // Try to identify which file was hit by matching model names in fragments\r\n const hitModelId = (result as any)?.modelId ?? (result as any)?.fragments?.modelId\r\n const matchedFile = hitModelId\r\n ? filesDataRef.current.find(f => f.name === hitModelId)\r\n : null\r\n\r\n setContextMenu({\r\n x: e.clientX,\r\n y: e.clientY,\r\n file: matchedFile ?? { id: 0, name: t('bimObject'), url: \"\", type: \"bim-file\" } as DbFile,\r\n })\r\n }\r\n\r\n canvas.addEventListener(\"contextmenu\", handleContextMenu)\r\n return () => canvas.removeEventListener(\"contextmenu\", handleContextMenu)\r\n }, [world, fragments, filePlacement.raycast])\r\n\r\n // Allow other UI to trigger AddToBim modes via currentToolId\r\n const { currentToolId } = toolsState.tools\r\n React.useEffect(() => {\r\n const bimAddModes: BimToolbarToolsType[] = [\r\n \"bim-add-file\", \"bim-add-comment\", \"bim-add-ids\",\r\n \"bim-add-ifc\", \"bim-add-bcf\", \"bim-add-cad\", \"bim-add-sensor\",\r\n ]\r\n if (bimAddModes.includes(currentToolId as BimToolbarToolsType)) {\r\n setAddingMode(currentToolId as BimToolbarToolsType)\r\n }\r\n }, [currentToolId])\r\n\r\n const startAdding = React.useCallback((mode: BimToolbarToolsType) => {\r\n setAddingMode(mode)\r\n toolsDispatch({ type: \"SET-TOOL\", payload: { currentToolId: mode } })\r\n // Crosshair is set by useFilePlacement.handleFileSelect after the user picks a file,\r\n // not here — so the cursor stays normal while the file picker is open.\r\n }, [toolsDispatch])\r\n\r\n const cancelAdding = React.useCallback(() => {\r\n setAddingMode(null)\r\n filePlacement.cancelPlacement()\r\n }, [filePlacement])\r\n\r\n // Sidebar navigation callbacks\r\n const openFileTab = React.useCallback(() => {\r\n setOpenInfo(true)\r\n menusDispatch({ type: \"SET_SIDEBAR_SELECTED_TAB\", payload: { selectedTab: \"file\" } })\r\n }, [setOpenInfo, menusDispatch])\r\n\r\n const openCommentsTab = React.useCallback(() => {\r\n setOpenInfo(true)\r\n menusDispatch({ type: \"SET_SIDEBAR_SELECTED_TAB\", payload: { selectedTab: \"communication\" } })\r\n }, [setOpenInfo, menusDispatch])\r\n\r\n const openSensorsTab = React.useCallback(() => {\r\n setOpenInfo(true)\r\n menusDispatch({ type: \"SET_SIDEBAR_SELECTED_TAB\", payload: { selectedTab: \"sensors\" } })\r\n }, [setOpenInfo, menusDispatch])\r\n\r\n const handleContextMenuAction = React.useCallback((_action: FileAction, _file: DbFile) => {\r\n openFileTab()\r\n }, [openFileTab])\r\n\r\n return (\r\n <>\r\n {/* File preview tooltip following cursor during placement */}\r\n {filePlacement.selectedFile && filePlacement.isPlacingFile && (\r\n <FilePreview file={filePlacement.selectedFile} mousePosition={filePlacement.mousePosition} />\r\n )}\r\n\r\n {/* Toolbar submenu */}\r\n <AddToBimToolbar\r\n tool={tool}\r\n onStartFile={() => startAdding(\"bim-add-file\")}\r\n onStartComment={() => startAdding(\"bim-add-comment\")}\r\n onStartCAD={() => startAdding(\"bim-add-cad\")}\r\n onStartSensor={() => startAdding(\"bim-add-sensor\")}\r\n fileCount={visibleFileCount}\r\n commentCount={commentCount}\r\n sensorCount={sensorCount}\r\n onFileBadgeClick={openFileTab}\r\n onCommentBadgeClick={openCommentsTab}\r\n onSensorBadgeClick={openSensorsTab}\r\n />\r\n\r\n {/* 3D position/scale card for DXF/GLB placement */}\r\n {filePlacement.show3DScaleCard && filePlacement.selectedFile && filePlacement.current3DFileType && (\r\n <Position3DCard\r\n fileType={filePlacement.current3DFileType}\r\n fileName={filePlacement.selectedFile.name}\r\n scale={filePlacement.fileScale}\r\n rotation={filePlacement.fileRotation}\r\n onScaleChange={filePlacement.setFileScale}\r\n onRotationChange={filePlacement.setFileRotation}\r\n onConfirm={() => {\r\n filePlacement.confirmPlacement()\r\n setAddingMode(null)\r\n }}\r\n onCancel={cancelAdding}\r\n />\r\n )}\r\n\r\n {/* File input card */}\r\n {addingMode === \"bim-add-file\" && !filePlacement.show3DScaleCard && (\r\n <FileAdderDialog\r\n isOpen={true}\r\n onClose={cancelAdding}\r\n title={t('addFile')}\r\n icon={LR.FilePlus}\r\n accept=\"*/*\"\r\n onFileSelect={(e) => filePlacement.handleFileSelect(e, addingMode)}\r\n onFileDrop={(file) => filePlacement.handleFileDrop(file, addingMode)}\r\n dropZoneTitle={t('dropZoneTitle')}\r\n dropZoneSubtext={t('dropZoneSubtext')}\r\n hide={filePlacement.isPlacingFile && !!filePlacement.selectedFile}\r\n />\r\n )}\r\n\r\n {addingMode === \"bim-add-cad\" && !filePlacement.show3DScaleCard && (\r\n <FileAdderDialog\r\n isOpen={true}\r\n onClose={cancelAdding}\r\n title={t('addCadFile')}\r\n icon={LR.DraftingCompass}\r\n accept=\".dxf,.dwg\"\r\n onFileSelect={(e) => filePlacement.handleFileSelect(e, addingMode)}\r\n onFileDrop={(file) => filePlacement.handleFileDrop(file, addingMode)}\r\n dropZoneTitle={t('dropZoneTitleCad')}\r\n dropZoneSubtext={t('dropZoneSubtextCad')}\r\n hide={filePlacement.isPlacingFile && !!filePlacement.selectedFile}\r\n />\r\n )}\r\n\r\n {/* Comment input */}\r\n <CommentInput\r\n viewer={ViewerNames.bim}\r\n layout=\"dialog\"\r\n isOpen={addingMode === \"bim-add-comment\"}\r\n onCancel={cancelAdding}\r\n bim={{\r\n world,\r\n raycast: filePlacement.raycast,\r\n buildingId,\r\n setCursor: filePlacement.setCursor,\r\n addPendingMarker: addPendingComment,\r\n removePendingMarker: removePendingComment,\r\n }}\r\n />\r\n\r\n {/* Sensor input */}\r\n <SensorInput\r\n viewer={ViewerNames.bim}\r\n layout=\"dialog\"\r\n isOpen={addingMode === \"bim-add-sensor\"}\r\n onCancel={cancelAdding}\r\n bim={{\r\n world,\r\n raycast: filePlacement.raycast,\r\n buildingId,\r\n setCursor: filePlacement.setCursor,\r\n addPendingMarker: addPendingSensor,\r\n removePendingMarker: removePendingSensor,\r\n }}\r\n />\r\n\r\n {/* Right-click context menu on BIM scene objects */}\r\n {contextMenu && (\r\n <ViewerContextMenu\r\n x={contextMenu.x}\r\n y={contextMenu.y}\r\n file={contextMenu.file}\r\n options={['view', 'move', 'delete']}\r\n onAction={handleContextMenuAction}\r\n onClose={() => setContextMenu(null)}\r\n />\r\n )}\r\n </>\r\n )\r\n}\r\n"],"mappings":";AAwDI,SAyJA,UAlJI,KAPJ;AAnDJ,YAAY,QAAQ;AACpB,OAAO,WAAW;AAClB,SAAS,uBAAuB;AAChC,YAAY,WAAW;AAGvB,SAAS,sBAAsB,yBAAyB,qBAAqB;AAC7E,SAAS,kBAAkB,cAAc,oBAAoB;AAC7D,SAAS,kBAAkB;AAC3B,SAAS,mBAAmB;AAC5B,SAAS,QAAQ,kBAAkB;AACnC,SAAS,oBAAoB;AAC7B,SAAS,yBAAyB;AAClC,SAAS,mBAAmB;AAC5B,SAAS,uBAAuB;AAEhC,SAAS,uBAAuB;AAChC,SAAS,+BAA+B;AACxC,OAAO,oBAAoB;AAC3B,SAAS,yBAAyB;AAClC,SAAS,wBAAwB;AACjC,SAAS,wBAAwB;AAcjC,MAAM,cAGD,CAAC,EAAE,MAAM,cAAc,MAAM;AAChC,QAAM,CAAC,UAAU,WAAW,IAAI,MAAM,SAAwB,IAAI;AAClE,QAAM,UAAU,KAAK,KAAK,WAAW,QAAQ;AAE7C,QAAM,UAAU,MAAM;AACpB,QAAI,SAAS;AACX,YAAM,MAAM,IAAI,gBAAgB,IAAI;AACpC,kBAAY,GAAG;AACf,aAAO,MAAM,IAAI,gBAAgB,GAAG;AAAA,IACtC;AAAA,EACF,GAAG,CAAC,MAAM,OAAO,CAAC;AAElB,SACE;AAAA,IAAC;AAAA;AAAA,MACC,SAAQ;AAAA,MACR,MAAK;AAAA,MACL,WAAU;AAAA,MACV,OAAO,EAAE,MAAM,cAAc,IAAI,IAAI,KAAK,cAAc,IAAI,GAAG;AAAA,MAE9D;AAAA,mBAAW,WACV,oBAAC,SAAM,KAAK,UAAU,KAAK,KAAK,MAAM,OAAO,IAAI,QAAQ,IAAI,WAAU,wBAAuB,IAE9F,oBAAC,GAAG,MAAH,EAAQ,MAAM,IAAI,WAAU,cAAa;AAAA,QAE5C,oBAAC,UAAK,WAAU,0DAA0D,eAAK,MAAK;AAAA;AAAA;AAAA,EACtF;AAEJ;AAEe,SAAR,SAA0B,EAAE,MAAM,aAAa,GAAkB;AAxExE;AAyEE,QAAM,IAAI,gBAAgB,UAAU;AACpC,QAAM,EAAE,UAAU,eAAe,OAAO,WAAW,IAAI,MAAM,WAAW,YAAY;AACpF,QAAM,EAAE,OAAO,SAAS,IAAI,MAAM,WAAW,UAAU;AACvD,QAAM,EAAE,eAAe,OAAO,UAAU,IAAI,SAAS;AACrD,QAAM,EAAE,OAAO,cAAc,IAAI,MAAM,WAAW,gBAAgB;AAClE,QAAM,eAAa,0DAAe,cAAf,mBAA0B,aAA1B,mBAAoC,OAAM;AAC7D,QAAM,EAAE,UAAU,cAAc,IAAI,MAAM,WAAW,YAAY;AACjE,QAAM,EAAE,YAAY,IAAI,WAAW;AAEnC,QAAM,CAAC,YAAY,aAAa,IAAI,MAAM,SAA8B,IAAI;AAG5E,QAAM,CAAC,aAAa,cAAc,IAAI,MAAM,SAAwD,IAAI;AAGxG,QAAM,EAAE,mBAAmB,sBAAsB,aAAa,IAAI,kBAAkB,OAAO,UAAU;AACrG,QAAM,EAAE,kBAAkB,qBAAqB,YAAY,IAAI,iBAAiB,OAAO,YAAY,YAAY;AAC/G,QAAM,EAAE,WAAW,IAAI,wBAAwB,UAAU;AACzD,QAAM,EAAE,WAAW,IAAI,cAAc,UAAU;AAG/C,QAAM,YAAY,qBAAqB,UAAU,EAAE,SAAS,CAAC;AAC7D,QAAM,eAAe,MAAM,OAAO,SAAS;AAC3C,eAAa,UAAU;AACvB,QAAM,mBAAmB,UAAU,OAAO,OAAK;AAjGjD,QAAAA;AAkGI,UAAM,OAAMA,MAAA,EAAE,cAAF,gBAAAA,IAAa;AACzB,WAAO,QAAQ,SAAS,QAAQ,UAAU,EAAE,QAAQ,UAAU,EAAE,QAAQ,cAAc,EAAE;AAAA,EAC1F,CAAC,EAAE;AAGH,QAAM,eAAe,MAAM,OAAmD,IAAI;AAClF,QAAM,qBAAqB,MAAM,YAAY,CAAC,IAAY,WAA6B;AACrF,UAAM,MAAM,aAAa;AACzB,QAAI,CAAC,IAAK;AACV,QAAI,WAAW,UAAU;AACvB,YAAM,SAAS,IAAI,cAAc,EAAE;AACnC,UAAI,iBAAiB,EAAE;AACvB,YAAM,SAAS,SACX,aAAa,QAAQ,KAAK,OAAE;AA/GtC,YAAAA;AA+GyC,iBAAE,SAAS,OAAO,UAASA,MAAA,EAAE,OAAF,OAAAA,MAAQ,KAAK;AAAA,OAAC,IACxE;AACJ,UAAI,iCAAQ,GAAI,YAAW,OAAO,EAAE,EAAE,MAAM,MAAM;AAAA,MAAE,CAAC;AACrD;AAAA,IACF;AACA,QAAI,eAAe,IAAI,WAAW,SAAS,cAAc,MAAM;AAAA,EACjE,GAAG,CAAC,UAAU,CAAC;AAEf,QAAM,gBAAgB;AAAA,IACpB;AAAA,IAAe;AAAA,IAAO;AAAA,IAAW;AAAA,IAAe;AAAA,IAAY;AAAA,IAC5D;AAAA,EACF;AACA,eAAa,UAAU;AAGvB,QAAM,UAAU,MAAM;AACpB,QAAI,MAAO,yBAAwB,KAAK;AAAA,EAC1C,GAAG,CAAC,KAAK,CAAC;AAGV,QAAM,UAAU,MAAM;AAnIxB,QAAAA,KAAAC;AAoII,QAAI,CAAC,SAAS,CAAC,UAAW;AAC1B,UAAM,UAASA,OAAAD,MAAA,MAAM,aAAN,gBAAAA,IAAgB,UAAhB,gBAAAC,IAAuB;AACtC,QAAI,CAAC,OAAQ;AAEb,UAAM,oBAAoB,OAAO,MAAkB;AAxIvD,UAAAD,KAAAC;AA0IM,QAAE,eAAe;AAEjB,YAAM,QAAQ,KAAK,MAAM,OAAO,OAAO,GAAG,QAAQ,EAAE,SAAS,EAAE,OAAO;AACtE,YAAM,SAAS,MAAM,cAAc,QAAQ;AAAA,QACzC,QAAQ,MAAM,OAAO;AAAA,QACrB;AAAA,QACA,KAAK;AAAA,MACP,CAAC;AAGD,YAAM,cAAcA,MAAA,iCAAgB,YAAhB,OAAAA,OAA4BD,MAAA,iCAAgB,cAAhB,gBAAAA,IAA2B;AAC3E,YAAM,cAAc,aAChB,aAAa,QAAQ,KAAK,OAAK,EAAE,SAAS,UAAU,IACpD;AAEJ,qBAAe;AAAA,QACb,GAAG,EAAE;AAAA,QACL,GAAG,EAAE;AAAA,QACL,MAAM,oCAAe,EAAE,IAAI,GAAG,MAAM,EAAE,WAAW,GAAG,KAAK,IAAI,MAAM,WAAW;AAAA,MAChF,CAAC;AAAA,IACH;AAEA,WAAO,iBAAiB,eAAe,iBAAiB;AACxD,WAAO,MAAM,OAAO,oBAAoB,eAAe,iBAAiB;AAAA,EAC1E,GAAG,CAAC,OAAO,WAAW,cAAc,OAAO,CAAC;AAG5C,QAAM,EAAE,cAAc,IAAI,WAAW;AACrC,QAAM,UAAU,MAAM;AACpB,UAAM,cAAqC;AAAA,MACzC;AAAA,MAAgB;AAAA,MAAmB;AAAA,MACnC;AAAA,MAAe;AAAA,MAAe;AAAA,MAAe;AAAA,IAC/C;AACA,QAAI,YAAY,SAAS,aAAoC,GAAG;AAC9D,oBAAc,aAAoC;AAAA,IACpD;AAAA,EACF,GAAG,CAAC,aAAa,CAAC;AAElB,QAAM,cAAc,MAAM,YAAY,CAAC,SAA8B;AACnE,kBAAc,IAAI;AAClB,kBAAc,EAAE,MAAM,YAAY,SAAS,EAAE,eAAe,KAAK,EAAE,CAAC;AAAA,EAGtE,GAAG,CAAC,aAAa,CAAC;AAElB,QAAM,eAAe,MAAM,YAAY,MAAM;AAC3C,kBAAc,IAAI;AAClB,kBAAc,gBAAgB;AAAA,EAChC,GAAG,CAAC,aAAa,CAAC;AAGlB,QAAM,cAAc,MAAM,YAAY,MAAM;AAC1C,gBAAY,IAAI;AAChB,kBAAc,EAAE,MAAM,4BAA4B,SAAS,EAAE,aAAa,OAAO,EAAE,CAAC;AAAA,EACtF,GAAG,CAAC,aAAa,aAAa,CAAC;AAE/B,QAAM,kBAAkB,MAAM,YAAY,MAAM;AAC9C,gBAAY,IAAI;AAChB,kBAAc,EAAE,MAAM,4BAA4B,SAAS,EAAE,aAAa,gBAAgB,EAAE,CAAC;AAAA,EAC/F,GAAG,CAAC,aAAa,aAAa,CAAC;AAE/B,QAAM,iBAAiB,MAAM,YAAY,MAAM;AAC7C,gBAAY,IAAI;AAChB,kBAAc,EAAE,MAAM,4BAA4B,SAAS,EAAE,aAAa,UAAU,EAAE,CAAC;AAAA,EACzF,GAAG,CAAC,aAAa,aAAa,CAAC;AAE/B,QAAM,0BAA0B,MAAM,YAAY,CAAC,SAAqB,UAAkB;AACxF,gBAAY;AAAA,EACd,GAAG,CAAC,WAAW,CAAC;AAEhB,SACE,iCAEG;AAAA,kBAAc,gBAAgB,cAAc,iBAC3C,oBAAC,eAAY,MAAM,cAAc,cAAc,eAAe,cAAc,eAAe;AAAA,IAI7F;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,aAAa,MAAM,YAAY,cAAc;AAAA,QAC7C,gBAAgB,MAAM,YAAY,iBAAiB;AAAA,QACnD,YAAY,MAAM,YAAY,aAAa;AAAA,QAC3C,eAAe,MAAM,YAAY,gBAAgB;AAAA,QACjD,WAAW;AAAA,QACX;AAAA,QACA;AAAA,QACA,kBAAkB;AAAA,QAClB,qBAAqB;AAAA,QACrB,oBAAoB;AAAA;AAAA,IACtB;AAAA,IAGC,cAAc,mBAAmB,cAAc,gBAAgB,cAAc,qBAC5E;AAAA,MAAC;AAAA;AAAA,QACC,UAAU,cAAc;AAAA,QACxB,UAAU,cAAc,aAAa;AAAA,QACrC,OAAO,cAAc;AAAA,QACrB,UAAU,cAAc;AAAA,QACxB,eAAe,cAAc;AAAA,QAC7B,kBAAkB,cAAc;AAAA,QAChC,WAAW,MAAM;AACf,wBAAc,iBAAiB;AAC/B,wBAAc,IAAI;AAAA,QACpB;AAAA,QACA,UAAU;AAAA;AAAA,IACZ;AAAA,IAID,eAAe,kBAAkB,CAAC,cAAc,mBAC/C;AAAA,MAAC;AAAA;AAAA,QACC,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,OAAO,EAAE,SAAS;AAAA,QAClB,MAAM,GAAG;AAAA,QACT,QAAO;AAAA,QACP,cAAc,CAAC,MAAM,cAAc,iBAAiB,GAAG,UAAU;AAAA,QACjE,YAAY,CAAC,SAAS,cAAc,eAAe,MAAM,UAAU;AAAA,QACnE,eAAe,EAAE,eAAe;AAAA,QAChC,iBAAiB,EAAE,iBAAiB;AAAA,QACpC,MAAM,cAAc,iBAAiB,CAAC,CAAC,cAAc;AAAA;AAAA,IACvD;AAAA,IAGD,eAAe,iBAAiB,CAAC,cAAc,mBAC9C;AAAA,MAAC;AAAA;AAAA,QACC,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,OAAO,EAAE,YAAY;AAAA,QACrB,MAAM,GAAG;AAAA,QACT,QAAO;AAAA,QACP,cAAc,CAAC,MAAM,cAAc,iBAAiB,GAAG,UAAU;AAAA,QACjE,YAAY,CAAC,SAAS,cAAc,eAAe,MAAM,UAAU;AAAA,QACnE,eAAe,EAAE,kBAAkB;AAAA,QACnC,iBAAiB,EAAE,oBAAoB;AAAA,QACvC,MAAM,cAAc,iBAAiB,CAAC,CAAC,cAAc;AAAA;AAAA,IACvD;AAAA,IAIF;AAAA,MAAC;AAAA;AAAA,QACC,QAAQ,YAAY;AAAA,QACpB,QAAO;AAAA,QACP,QAAQ,eAAe;AAAA,QACvB,UAAU;AAAA,QACV,KAAK;AAAA,UACH;AAAA,UACA,SAAS,cAAc;AAAA,UACvB;AAAA,UACA,WAAW,cAAc;AAAA,UACzB,kBAAkB;AAAA,UAClB,qBAAqB;AAAA,QACvB;AAAA;AAAA,IACF;AAAA,IAGA;AAAA,MAAC;AAAA;AAAA,QACC,QAAQ,YAAY;AAAA,QACpB,QAAO;AAAA,QACP,QAAQ,eAAe;AAAA,QACvB,UAAU;AAAA,QACV,KAAK;AAAA,UACH;AAAA,UACA,SAAS,cAAc;AAAA,UACvB;AAAA,UACA,WAAW,cAAc;AAAA,UACzB,kBAAkB;AAAA,UAClB,qBAAqB;AAAA,QACvB;AAAA;AAAA,IACF;AAAA,IAGC,eACC;AAAA,MAAC;AAAA;AAAA,QACC,GAAG,YAAY;AAAA,QACf,GAAG,YAAY;AAAA,QACf,MAAM,YAAY;AAAA,QAClB,SAAS,CAAC,QAAQ,QAAQ,QAAQ;AAAA,QAClC,UAAU;AAAA,QACV,SAAS,MAAM,eAAe,IAAI;AAAA;AAAA,IACpC;AAAA,KAEJ;AAEJ;","names":["_a","_b"]}
1
+ {"version":3,"sources":["../../../../../../../../src/core/components/viewers/bim/src/tools/AddToBim/index.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 Image from \"next/image\"\r\nimport { useTranslations } from 'next-intl'\r\nimport * as React from \"react\"\r\n\r\n\r\nimport { useFilesByBuildingId, useUploadFileToBuilding, useDeleteFile } from \"../../../../../../hooks/files/files\"\r\nimport { BuildingsContext, MenusContext, ToolsContext } from \"../../../../../../store\"\r\nimport { BimContext } from \"../../../../../../store/BIM/context\"\r\nimport { ViewerNames } from \"../../../../../../types/dbTypes\"\r\nimport { Button, useSidebar } from \"../../../../../ui\"\r\nimport { CommentInput } from \"../../../../../ui/Comments/CommentInput\"\r\nimport { ViewerContextMenu } from \"../../../../../ui/FilesManager\"\r\nimport { SensorDetailDialog } from \"../../../../../ui/Sensors/SensorDetailDialog\"\r\nimport { SensorInput } from \"../../../../../ui/Sensors/SensorInput\"\r\nimport { FileAdderDialog } from \"../../../../map/src/tools/AddTools/AddFile/FileAdder\"\r\n\r\nimport { AddToBimToolbar } from \"./src/AddToBimToolbar\"\r\nimport { initializeCSS2DRenderer } from \"./src/FileMarkerUtils\"\r\nimport Position3DCard from \"./src/Position3DCard\"\r\nimport { useCommentMarkers } from \"./src/useCommentMarkers\"\r\nimport { useFilePlacement } from \"./src/useFilePlacement\"\r\nimport { useSensorMarkers } from \"./src/useSensorMarkers\"\r\n\r\nimport type { DbFile } from \"../../../../../../types/dbTypes\"\r\nimport type { FileAction } from \"../../../../../../types/global\"\r\nimport type { Tool, ToolbarToolType } from \"../../../../../../types/tools\"\r\nimport type { FileMarkerAction } from \"../../../../../ui/FilesManager/src/FileMarker\"\r\nimport type { BimToolbarToolsType } from \"../bimToolbar\"\r\n\r\ninterface AddToBimProps {\r\n tool: Tool\r\n [key: string]: unknown\r\n}\r\n\r\nconst FilePreview: React.FC<{\r\n file: File\r\n mousePosition: { x: number; y: number }\r\n}> = ({ file, mousePosition }) => {\r\n const [imageUrl, setImageUrl] = React.useState<string | null>(null)\r\n const isImage = file.type.startsWith(\"image/\")\r\n\r\n React.useEffect(() => {\r\n if (isImage) {\r\n const url = URL.createObjectURL(file)\r\n setImageUrl(url)\r\n return () => URL.revokeObjectURL(url)\r\n }\r\n }, [file, isImage])\r\n\r\n return (\r\n <Button\r\n variant=\"outline\"\r\n size=\"sm\"\r\n className=\"fixed pointer-events-none z-[9999] bg-white/95 flex items-center gap-2 cursor-crosshair text-xs\"\r\n style={{ left: mousePosition.x + 10, top: mousePosition.y - 10 }}\r\n >\r\n {isImage && imageUrl ? (\r\n <Image src={imageUrl} alt={file.name} width={16} height={16} className=\"object-cover rounded\" />\r\n ) : (\r\n <LR.File size={16} className=\"text-black\" />\r\n )}\r\n <span className=\"overflow-hidden text-ellipsis whitespace-nowrap flex-1\">{file.name}</span>\r\n </Button>\r\n )\r\n}\r\n\r\nexport default function AddToBim({ tool }: AddToBimProps) {\r\n const t = useTranslations('AddToBim')\r\n const { dispatch: toolsDispatch, state: toolsState } = React.useContext(ToolsContext)\r\n const { state: bimState } = React.useContext(BimContext)\r\n const { bimComponents, world, fragments } = bimState.bim\r\n const { state: buildingState } = React.useContext(BuildingsContext)\r\n const buildingId = buildingState?.buildings?.building?.id || -1\r\n const { dispatch: menusDispatch } = React.useContext(MenusContext)\r\n const { setOpenInfo } = useSidebar()\r\n\r\n const [addingMode, setAddingMode] = React.useState<BimToolbarToolsType>(null)\r\n\r\n // Context menu state for right-click on BIM scene objects\r\n const [contextMenu, setContextMenu] = React.useState<{ x: number; y: number; file: DbFile } | null>(null)\r\n\r\n // Extracted hooks\r\n const { addPendingComment, removePendingComment, commentCount } = useCommentMarkers(world, buildingId)\r\n const { addPendingSensor, removePendingSensor, sensorCount, detailSensor, detailSensorType, closeSensorDetail, editSensor, closeSensorEdit } = useSensorMarkers(world, buildingId)\r\n const { uploadFile } = useUploadFileToBuilding(buildingId)\r\n const { deleteFile } = useDeleteFile(buildingId)\r\n\r\n // Visible (added-to-scene) non-BIM files; also used to resolve a marker to its DB record.\r\n const filesData = useFilesByBuildingId(buildingId).files || []\r\n const filesDataRef = React.useRef(filesData)\r\n filesDataRef.current = filesData\r\n const visibleFileCount = filesData.filter(f => {\r\n const ext = f.extension?.toLowerCase()\r\n return ext !== \"ifc\" && ext !== \"frag\" && f.tag !== \"user\" && f.tag !== \"bim-file\" && f.isVisible\r\n }).length\r\n\r\n // Marker actions reach into the placement API after it is created, so route through a ref.\r\n const placementRef = React.useRef<ReturnType<typeof useFilePlacement> | null>(null)\r\n const handleMarkerAction = React.useCallback((id: string, action: FileMarkerAction) => {\r\n const api = placementRef.current\r\n if (!api) return\r\n if (action === \"delete\") {\r\n const placed = api.getPlacedFile(id)\r\n api.removePlacedFile(id)\r\n const dbFile = placed\r\n ? filesDataRef.current.find(f => f.name === placed.name && (f.id ?? 0) > 0)\r\n : undefined\r\n if (dbFile?.id) deleteFile(dbFile.id).catch(() => { })\r\n return\r\n }\r\n api.editPlacedFile(id, action === \"move\" ? \"translate\" : action)\r\n }, [deleteFile])\r\n\r\n const filePlacement = useFilePlacement(\r\n bimComponents, world, fragments, toolsDispatch, buildingId, uploadFile,\r\n handleMarkerAction,\r\n )\r\n placementRef.current = filePlacement\r\n\r\n // Initialize CSS2D renderer\r\n React.useEffect(() => {\r\n if (world) initializeCSS2DRenderer(world)\r\n }, [world])\r\n\r\n // Canvas-level right-click: raycast to detect hit, show context menu\r\n React.useEffect(() => {\r\n if (!world || !fragments) return\r\n const canvas = world.renderer?.three?.domElement\r\n if (!canvas) return\r\n\r\n const handleContextMenu = async (e: MouseEvent) => {\r\n // Prevent the browser default menu immediately, regardless of what we hit\r\n e.preventDefault()\r\n\r\n const mouse = new (await import(\"three\")).Vector2(e.clientX, e.clientY)\r\n const result = await filePlacement.raycast({\r\n camera: world.camera.three,\r\n mouse,\r\n dom: canvas,\r\n })\r\n\r\n // Try to identify which file was hit by matching model names in fragments\r\n const hitModelId = (result as any)?.modelId ?? (result as any)?.fragments?.modelId\r\n const matchedFile = hitModelId\r\n ? filesDataRef.current.find(f => f.name === hitModelId)\r\n : null\r\n\r\n setContextMenu({\r\n x: e.clientX,\r\n y: e.clientY,\r\n file: matchedFile ?? { id: 0, name: t('bimObject'), url: \"\", type: \"bim-file\" } as DbFile,\r\n })\r\n }\r\n\r\n canvas.addEventListener(\"contextmenu\", handleContextMenu)\r\n return () => canvas.removeEventListener(\"contextmenu\", handleContextMenu)\r\n }, [world, fragments, filePlacement.raycast])\r\n\r\n // Allow other UI to trigger AddToBim modes via currentToolId\r\n const { currentToolId } = toolsState.tools\r\n React.useEffect(() => {\r\n const bimAddModes: BimToolbarToolsType[] = [\r\n \"bim-add-file\", \"bim-add-comment\", \"bim-add-ids\",\r\n \"bim-add-ifc\", \"bim-add-bcf\", \"bim-add-cad\", \"bim-add-sensor\",\r\n ]\r\n if (bimAddModes.includes(currentToolId as BimToolbarToolsType)) {\r\n setAddingMode(currentToolId as BimToolbarToolsType)\r\n }\r\n }, [currentToolId])\r\n\r\n const startAdding = React.useCallback((mode: BimToolbarToolsType) => {\r\n setAddingMode(mode)\r\n toolsDispatch({ type: \"SET-TOOL\", payload: { currentToolId: mode } })\r\n // Crosshair is set by useFilePlacement.handleFileSelect after the user picks a file,\r\n // not here — so the cursor stays normal while the file picker is open.\r\n }, [toolsDispatch])\r\n\r\n const cancelAdding = React.useCallback(() => {\r\n setAddingMode(null)\r\n filePlacement.cancelPlacement()\r\n }, [filePlacement])\r\n\r\n // Sidebar navigation callbacks\r\n const openFileTab = React.useCallback(() => {\r\n setOpenInfo(true)\r\n menusDispatch({ type: \"SET_SIDEBAR_SELECTED_TAB\", payload: { selectedTab: \"file\" } })\r\n }, [setOpenInfo, menusDispatch])\r\n\r\n const openCommentsTab = React.useCallback(() => {\r\n setOpenInfo(true)\r\n menusDispatch({ type: \"SET_SIDEBAR_SELECTED_TAB\", payload: { selectedTab: \"communication\" } })\r\n }, [setOpenInfo, menusDispatch])\r\n\r\n const openSensorsTab = React.useCallback(() => {\r\n setOpenInfo(true)\r\n menusDispatch({ type: \"SET_SIDEBAR_SELECTED_TAB\", payload: { selectedTab: \"sensors\" } })\r\n }, [setOpenInfo, menusDispatch])\r\n\r\n const handleContextMenuAction = React.useCallback((_action: FileAction, _file: DbFile) => {\r\n openFileTab()\r\n }, [openFileTab])\r\n\r\n return (\r\n <>\r\n {/* File preview tooltip following cursor during placement */}\r\n {filePlacement.selectedFile && filePlacement.isPlacingFile && (\r\n <FilePreview file={filePlacement.selectedFile} mousePosition={filePlacement.mousePosition} />\r\n )}\r\n\r\n {/* Toolbar submenu */}\r\n <AddToBimToolbar\r\n tool={tool}\r\n onStartFile={() => startAdding(\"bim-add-file\")}\r\n onStartComment={() => startAdding(\"bim-add-comment\")}\r\n onStartCAD={() => startAdding(\"bim-add-cad\")}\r\n onStartSensor={() => startAdding(\"bim-add-sensor\")}\r\n fileCount={visibleFileCount}\r\n commentCount={commentCount}\r\n sensorCount={sensorCount}\r\n onFileBadgeClick={openFileTab}\r\n onCommentBadgeClick={openCommentsTab}\r\n onSensorBadgeClick={openSensorsTab}\r\n />\r\n\r\n {/* 3D position/scale card for DXF/GLB placement */}\r\n {filePlacement.show3DScaleCard && filePlacement.selectedFile && filePlacement.current3DFileType && (\r\n <Position3DCard\r\n fileType={filePlacement.current3DFileType}\r\n fileName={filePlacement.selectedFile.name}\r\n scale={filePlacement.fileScale}\r\n rotation={filePlacement.fileRotation}\r\n onScaleChange={filePlacement.setFileScale}\r\n onRotationChange={filePlacement.setFileRotation}\r\n onConfirm={() => {\r\n filePlacement.confirmPlacement()\r\n setAddingMode(null)\r\n }}\r\n onCancel={cancelAdding}\r\n />\r\n )}\r\n\r\n {/* File input card */}\r\n {addingMode === \"bim-add-file\" && !filePlacement.show3DScaleCard && (\r\n <FileAdderDialog\r\n isOpen={true}\r\n onClose={cancelAdding}\r\n title={t('addFile')}\r\n icon={LR.FilePlus}\r\n accept=\"*/*\"\r\n onFileSelect={(e) => filePlacement.handleFileSelect(e, addingMode)}\r\n onFileDrop={(file) => filePlacement.handleFileDrop(file, addingMode)}\r\n dropZoneTitle={t('dropZoneTitle')}\r\n dropZoneSubtext={t('dropZoneSubtext')}\r\n hide={filePlacement.isPlacingFile && !!filePlacement.selectedFile}\r\n />\r\n )}\r\n\r\n {addingMode === \"bim-add-cad\" && !filePlacement.show3DScaleCard && (\r\n <FileAdderDialog\r\n isOpen={true}\r\n onClose={cancelAdding}\r\n title={t('addCadFile')}\r\n icon={LR.DraftingCompass}\r\n accept=\".dxf,.dwg\"\r\n onFileSelect={(e) => filePlacement.handleFileSelect(e, addingMode)}\r\n onFileDrop={(file) => filePlacement.handleFileDrop(file, addingMode)}\r\n dropZoneTitle={t('dropZoneTitleCad')}\r\n dropZoneSubtext={t('dropZoneSubtextCad')}\r\n hide={filePlacement.isPlacingFile && !!filePlacement.selectedFile}\r\n />\r\n )}\r\n\r\n {/* Comment input */}\r\n <CommentInput\r\n viewer={ViewerNames.bim}\r\n layout=\"dialog\"\r\n isOpen={addingMode === \"bim-add-comment\"}\r\n onCancel={cancelAdding}\r\n bim={{\r\n world,\r\n raycast: filePlacement.raycast,\r\n buildingId,\r\n setCursor: filePlacement.setCursor,\r\n addPendingMarker: addPendingComment,\r\n removePendingMarker: removePendingComment,\r\n }}\r\n />\r\n\r\n {/* Sensor input */}\r\n <SensorInput\r\n viewer={ViewerNames.bim}\r\n layout=\"dialog\"\r\n isOpen={addingMode === \"bim-add-sensor\"}\r\n onCancel={cancelAdding}\r\n bim={{\r\n world,\r\n raycast: filePlacement.raycast,\r\n buildingId,\r\n setCursor: filePlacement.setCursor,\r\n addPendingMarker: addPendingSensor,\r\n removePendingMarker: removePendingSensor,\r\n }}\r\n />\r\n\r\n {/* Sensor edit dialog (opened from a 3D sensor card's edit tool) */}\r\n <SensorInput\r\n viewer={ViewerNames.bim}\r\n layout=\"dialog\"\r\n isOpen={!!editSensor}\r\n editSensor={editSensor ?? undefined}\r\n onCancel={closeSensorEdit}\r\n onSaved={closeSensorEdit}\r\n />\r\n\r\n {/* Sensor detail dialog (hosted here, in the provider tree, since the 3D card root has none) */}\r\n {detailSensor && (\r\n <SensorDetailDialog\r\n open={!!detailSensor}\r\n onOpenChange={(o) => { if (!o) closeSensorDetail() }}\r\n sensor={detailSensor}\r\n sensorType={detailSensorType}\r\n />\r\n )}\r\n\r\n {/* Right-click context menu on BIM scene objects */}\r\n {contextMenu && (\r\n <ViewerContextMenu\r\n x={contextMenu.x}\r\n y={contextMenu.y}\r\n file={contextMenu.file}\r\n options={['view', 'move', 'delete']}\r\n onAction={handleContextMenuAction}\r\n onClose={() => setContextMenu(null)}\r\n />\r\n )}\r\n </>\r\n )\r\n}\r\n"],"mappings":"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","names":["_a","_b"]}
@@ -1 +1 @@
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":[]}
1
+ {"version":3,"sources":["../../../../../../../../../src/core/components/viewers/bim/src/tools/AddToBim/src/BimComment.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 React from 'react'\r\n\r\nimport Comment from '../../../../../../ui/Comments/Comment'\r\n\r\nimport type { CommentActionLabels } from '../../../../../../ui/Comments/CommentActionButtons'\r\n\r\ninterface BimCommentProps {\r\n userName: string\r\n userImage: number | null\r\n userImageFileId?: number | null\r\n buildingId?: number\r\n timestamp: Date\r\n text: string\r\n isPending?: boolean\r\n highlight?: boolean\r\n focused?: boolean\r\n showActions?: boolean\r\n canReply?: boolean\r\n canEdit?: boolean\r\n canDelete?: boolean\r\n actionLabels?: CommentActionLabels\r\n onRemove?: () => void\r\n onClose?: () => void\r\n onSelect?: () => void\r\n onFocus?: () => void\r\n onReply?: () => void\r\n onEdit?: () => void\r\n}\r\n\r\nexport default function BimComment({\r\n userName,\r\n userImage,\r\n userImageFileId,\r\n buildingId,\r\n timestamp,\r\n text,\r\n isPending = false,\r\n highlight = false,\r\n focused = false,\r\n showActions = false,\r\n canReply = true,\r\n canEdit = true,\r\n canDelete = true,\r\n actionLabels,\r\n onRemove,\r\n onClose,\r\n onSelect,\r\n onFocus,\r\n onReply,\r\n onEdit,\r\n}: BimCommentProps): React.ReactElement {\r\n return (\r\n <Comment\r\n userName={userName}\r\n userImage={userImage}\r\n userImageFileId={userImageFileId}\r\n buildingId={buildingId}\r\n text={text}\r\n createdAt={timestamp}\r\n isPending={isPending}\r\n highlight={highlight}\r\n focused={focused}\r\n showActions={showActions}\r\n canReply={canReply}\r\n canEdit={canEdit}\r\n canDelete={canDelete}\r\n actionLabels={actionLabels}\r\n onRemove={onRemove}\r\n onClose={onClose}\r\n onSelect={onSelect}\r\n onFocus={onFocus}\r\n onReply={onReply}\r\n onEdit={onEdit}\r\n enableCollapse\r\n defaultCollapsed\r\n />\r\n )\r\n}\r\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":[]}
@@ -1,23 +1,2 @@
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 {};
1
+ export { BimMarkerCluster as default } from "../../../../../shared/markers/BimMarkerCluster";
23
2
  //# sourceMappingURL=BimCommentCluster.d.ts.map
@@ -1 +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"}
1
+ {"version":3,"file":"BimCommentCluster.d.ts","sourceRoot":"","sources":["../../../../../../../../../src/core/components/viewers/bim/src/tools/AddToBim/src/BimCommentCluster.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,gBAAgB,IAAI,OAAO,EAAE,MAAM,gDAAgD,CAAA"}
@@ -1,108 +1,5 @@
1
- "use client";
2
- import { jsx, jsxs } from "react/jsx-runtime";
3
- import * as React from "react";
4
- import { Avatar } from "../../../../../../ui/Avatar";
5
- import { UserAvatar } from "../../../../../../ui/UserAvatar";
6
- function clusterColor(count) {
7
- if (count >= 750) return "#f28cb1";
8
- if (count >= 100) return "#f1f075";
9
- return "#51bbd6";
10
- }
11
- function BimCommentCluster({ members, highlight = false, onSelect, onFocus }) {
12
- const [expanded, setExpanded] = React.useState(false);
13
- const [fanned, setFanned] = React.useState(false);
14
- const closeTimer = React.useRef(null);
15
- const count = members.length;
16
- const radius = Math.min(64, 26 + count * 4);
17
- React.useEffect(() => {
18
- if (!expanded) {
19
- setFanned(false);
20
- return;
21
- }
22
- const raf = requestAnimationFrame(() => setFanned(true));
23
- return () => cancelAnimationFrame(raf);
24
- }, [expanded]);
25
- React.useEffect(() => () => {
26
- if (closeTimer.current) clearTimeout(closeTimer.current);
27
- }, []);
28
- const open = () => {
29
- if (closeTimer.current) {
30
- clearTimeout(closeTimer.current);
31
- closeTimer.current = null;
32
- }
33
- setExpanded(true);
34
- };
35
- const scheduleClose = () => {
36
- if (closeTimer.current) clearTimeout(closeTimer.current);
37
- closeTimer.current = setTimeout(() => setExpanded(false), 150);
38
- };
39
- return /* @__PURE__ */ jsxs(
40
- "div",
41
- {
42
- className: "relative -translate-x-1/2 -translate-y-1/2 pointer-events-auto",
43
- onMouseEnter: open,
44
- onMouseLeave: scheduleClose,
45
- children: [
46
- /* @__PURE__ */ jsx(
47
- "div",
48
- {
49
- className: "relative flex items-center justify-center rounded-full text-sm font-medium select-none cursor-pointer",
50
- style: {
51
- width: 36,
52
- height: 36,
53
- backgroundColor: clusterColor(count),
54
- color: "#000",
55
- textShadow: "0 0 2px #fff, 0 0 2px #fff",
56
- boxShadow: highlight ? "0 0 0 2px #73cee2, 0 0 8px rgba(115, 206, 226, 0.5)" : "0 0 0 1px white"
57
- },
58
- children: count
59
- }
60
- ),
61
- expanded && /* @__PURE__ */ jsx("div", { className: "absolute left-1/2 top-1/2", children: members.map((member, i) => {
62
- var _a;
63
- const angle = 2 * Math.PI * i / count - Math.PI / 2;
64
- const x = fanned ? Math.cos(angle) * radius : 0;
65
- const y = fanned ? Math.sin(angle) * radius : 0;
66
- return /* @__PURE__ */ jsx(
67
- "div",
68
- {
69
- className: "absolute",
70
- style: {
71
- transform: `translate(-50%, -50%) translate(${x}px, ${y}px)`,
72
- opacity: fanned ? 1 : 0,
73
- transition: "transform 200ms ease, opacity 200ms ease",
74
- transitionDelay: `${i * 20}ms`
75
- },
76
- onMouseEnter: open,
77
- onPointerDown: (e) => {
78
- e.stopPropagation();
79
- onSelect == null ? void 0 : onSelect(member.id);
80
- onFocus == null ? void 0 : onFocus(member.id);
81
- },
82
- children: /* @__PURE__ */ jsx(
83
- "div",
84
- {
85
- className: "h-9 w-9 rounded-full overflow-hidden cursor-pointer bg-card",
86
- style: { boxShadow: "0 0 0 1px white" },
87
- children: /* @__PURE__ */ jsx(Avatar, { className: "h-full w-full", children: /* @__PURE__ */ jsx(
88
- UserAvatar,
89
- {
90
- imageFileId: (_a = member.imageFileId) != null ? _a : null,
91
- name: member.userName,
92
- className: "h-full w-full rounded-full object-cover"
93
- }
94
- ) })
95
- }
96
- )
97
- },
98
- member.id
99
- );
100
- }) })
101
- ]
102
- }
103
- );
104
- }
1
+ import { BimMarkerCluster } from "../../../../../shared/markers/BimMarkerCluster";
105
2
  export {
106
- BimCommentCluster as default
3
+ BimMarkerCluster as default
107
4
  };
108
5
  //# sourceMappingURL=BimCommentCluster.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../../../src/core/components/viewers/bim/src/tools/AddToBim/src/BimCommentCluster.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 { Avatar } from '../../../../../../ui/Avatar'\nimport { UserAvatar } from '../../../../../../ui/UserAvatar'\n\nexport interface ClusterMember {\n id: number\n userName?: string\n imageFileId?: number | null\n}\n\ninterface BimCommentClusterProps {\n members: ClusterMember[]\n highlight?: boolean\n onSelect?: (id: number) => void\n onFocus?: (id: number) => void\n}\n\n/** Cluster bubble colour, matching the map comment cluster palette (mapLayersUtils). */\nfunction clusterColor(count: number): string {\n if (count >= 750) return '#f28cb1'\n if (count >= 100) return '#f1f075'\n return '#51bbd6'\n}\n\n/**\n * Rendered inside a CSS2DObject at a cluster's world center. Shows a numbered circle for\n * overlapping BIM comment markers; hovering fans the members out (animated) so each can be\n * seen and clicked, and collapses back on mouse-out. A short close delay keeps the fan open\n * while the pointer crosses the gap between the bubble and a member (same trick the map\n * cluster expansion uses), so members stay clickable. Members mount only while expanded, so a\n * collapsed cluster is just the bubble — no leftover container behind it.\n */\nexport default function BimCommentCluster({ members, highlight = false, onSelect, onFocus }: BimCommentClusterProps): React.ReactElement {\n const [expanded, setExpanded] = React.useState(false)\n const [fanned, setFanned] = React.useState(false)\n const closeTimer = React.useRef<ReturnType<typeof setTimeout> | null>(null)\n const count = members.length\n const radius = Math.min(64, 26 + count * 4)\n\n // Animate the fan: members mount at the centre, then move out to their radius on the next frame.\n React.useEffect(() => {\n if (!expanded) {\n setFanned(false)\n return\n }\n const raf = requestAnimationFrame(() => setFanned(true))\n return () => cancelAnimationFrame(raf)\n }, [expanded])\n\n React.useEffect(() => () => {\n if (closeTimer.current) clearTimeout(closeTimer.current)\n }, [])\n\n const open = () => {\n if (closeTimer.current) {\n clearTimeout(closeTimer.current)\n closeTimer.current = null\n }\n setExpanded(true)\n }\n\n const scheduleClose = () => {\n if (closeTimer.current) clearTimeout(closeTimer.current)\n closeTimer.current = setTimeout(() => setExpanded(false), 150)\n }\n\n return (\n <div\n className=\"relative -translate-x-1/2 -translate-y-1/2 pointer-events-auto\"\n onMouseEnter={open}\n onMouseLeave={scheduleClose}\n >\n <div\n className=\"relative flex items-center justify-center rounded-full text-sm font-medium select-none cursor-pointer\"\n style={{\n width: 36,\n height: 36,\n backgroundColor: clusterColor(count),\n color: '#000',\n textShadow: '0 0 2px #fff, 0 0 2px #fff',\n boxShadow: highlight\n ? '0 0 0 2px #73cee2, 0 0 8px rgba(115, 206, 226, 0.5)'\n : '0 0 0 1px white',\n }}\n >\n {count}\n </div>\n\n {expanded && (\n <div className=\"absolute left-1/2 top-1/2\">\n {members.map((member, i) => {\n const angle = (2 * Math.PI * i) / count - Math.PI / 2\n const x = fanned ? Math.cos(angle) * radius : 0\n const y = fanned ? Math.sin(angle) * radius : 0\n return (\n <div\n key={member.id}\n className=\"absolute\"\n style={{\n transform: `translate(-50%, -50%) translate(${x}px, ${y}px)`,\n opacity: fanned ? 1 : 0,\n transition: 'transform 200ms ease, opacity 200ms ease',\n transitionDelay: `${i * 20}ms`,\n }}\n onMouseEnter={open}\n // pointerdown (not click): the CSS2D renderer re-transforms this element every\n // frame and it is still animating out, so a browser click frequently never fires.\n onPointerDown={(e) => {\n e.stopPropagation()\n onSelect?.(member.id)\n onFocus?.(member.id)\n }}\n >\n <div\n className=\"h-9 w-9 rounded-full overflow-hidden cursor-pointer bg-card\"\n style={{ boxShadow: '0 0 0 1px white' }}\n >\n <Avatar className=\"h-full w-full\">\n <UserAvatar\n imageFileId={member.imageFileId ?? null}\n name={member.userName}\n className=\"h-full w-full rounded-full object-cover\"\n />\n </Avatar>\n </div>\n </div>\n )\n })}\n </div>\n )}\n </div>\n )\n}\n"],"mappings":";AAyEI,SAKE,KALF;AApEJ,YAAY,WAAW;AAEvB,SAAS,cAAc;AACvB,SAAS,kBAAkB;AAgB3B,SAAS,aAAa,OAAuB;AAC3C,MAAI,SAAS,IAAK,QAAO;AACzB,MAAI,SAAS,IAAK,QAAO;AACzB,SAAO;AACT;AAUe,SAAR,kBAAmC,EAAE,SAAS,YAAY,OAAO,UAAU,QAAQ,GAA+C;AACvI,QAAM,CAAC,UAAU,WAAW,IAAI,MAAM,SAAS,KAAK;AACpD,QAAM,CAAC,QAAQ,SAAS,IAAI,MAAM,SAAS,KAAK;AAChD,QAAM,aAAa,MAAM,OAA6C,IAAI;AAC1E,QAAM,QAAQ,QAAQ;AACtB,QAAM,SAAS,KAAK,IAAI,IAAI,KAAK,QAAQ,CAAC;AAG1C,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,UAAU;AACb,gBAAU,KAAK;AACf;AAAA,IACF;AACA,UAAM,MAAM,sBAAsB,MAAM,UAAU,IAAI,CAAC;AACvD,WAAO,MAAM,qBAAqB,GAAG;AAAA,EACvC,GAAG,CAAC,QAAQ,CAAC;AAEb,QAAM,UAAU,MAAM,MAAM;AAC1B,QAAI,WAAW,QAAS,cAAa,WAAW,OAAO;AAAA,EACzD,GAAG,CAAC,CAAC;AAEL,QAAM,OAAO,MAAM;AACjB,QAAI,WAAW,SAAS;AACtB,mBAAa,WAAW,OAAO;AAC/B,iBAAW,UAAU;AAAA,IACvB;AACA,gBAAY,IAAI;AAAA,EAClB;AAEA,QAAM,gBAAgB,MAAM;AAC1B,QAAI,WAAW,QAAS,cAAa,WAAW,OAAO;AACvD,eAAW,UAAU,WAAW,MAAM,YAAY,KAAK,GAAG,GAAG;AAAA,EAC/D;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,cAAc;AAAA,MACd,cAAc;AAAA,MAEd;AAAA;AAAA,UAAC;AAAA;AAAA,YACC,WAAU;AAAA,YACV,OAAO;AAAA,cACL,OAAO;AAAA,cACP,QAAQ;AAAA,cACR,iBAAiB,aAAa,KAAK;AAAA,cACnC,OAAO;AAAA,cACP,YAAY;AAAA,cACZ,WAAW,YACP,wDACA;AAAA,YACN;AAAA,YAEC;AAAA;AAAA,QACH;AAAA,QAEC,YACC,oBAAC,SAAI,WAAU,6BACZ,kBAAQ,IAAI,CAAC,QAAQ,MAAM;AAhGtC;AAiGY,gBAAM,QAAS,IAAI,KAAK,KAAK,IAAK,QAAQ,KAAK,KAAK;AACpD,gBAAM,IAAI,SAAS,KAAK,IAAI,KAAK,IAAI,SAAS;AAC9C,gBAAM,IAAI,SAAS,KAAK,IAAI,KAAK,IAAI,SAAS;AAC9C,iBACE;AAAA,YAAC;AAAA;AAAA,cAEC,WAAU;AAAA,cACV,OAAO;AAAA,gBACL,WAAW,mCAAmC,CAAC,OAAO,CAAC;AAAA,gBACvD,SAAS,SAAS,IAAI;AAAA,gBACtB,YAAY;AAAA,gBACZ,iBAAiB,GAAG,IAAI,EAAE;AAAA,cAC5B;AAAA,cACA,cAAc;AAAA,cAGd,eAAe,CAAC,MAAM;AACpB,kBAAE,gBAAgB;AAClB,qDAAW,OAAO;AAClB,mDAAU,OAAO;AAAA,cACnB;AAAA,cAEA;AAAA,gBAAC;AAAA;AAAA,kBACC,WAAU;AAAA,kBACV,OAAO,EAAE,WAAW,kBAAkB;AAAA,kBAEtC,8BAAC,UAAO,WAAU,iBAChB;AAAA,oBAAC;AAAA;AAAA,sBACC,cAAa,YAAO,gBAAP,YAAsB;AAAA,sBACnC,MAAM,OAAO;AAAA,sBACb,WAAU;AAAA;AAAA,kBACZ,GACF;AAAA;AAAA,cACF;AAAA;AAAA,YA5BK,OAAO;AAAA,UA6Bd;AAAA,QAEJ,CAAC,GACH;AAAA;AAAA;AAAA,EAEJ;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../../../../../../../src/core/components/viewers/bim/src/tools/AddToBim/src/BimCommentCluster.tsx"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\n// Re-export shim: the cluster bubble moved to the shared marker framework.\r\nexport { BimMarkerCluster as default } from \"../../../../../shared/markers/BimMarkerCluster\"\r\n"],"mappings":"AAIA,SAA6B,wBAAe;","names":[]}
@@ -1,5 +1,6 @@
1
1
  import * as React from "react";
2
2
  import type { SensorDataFormat, SensorType } from '../../../../../../../types/dbTypes';
3
+ import type { CommentActionLabels } from '../../../../../../ui/Comments/CommentActionButtons';
3
4
  import type * as THREE from 'three';
4
5
  interface SensorProps {
5
6
  sensorName: string;
@@ -12,12 +13,18 @@ interface SensorProps {
12
13
  targetPoint: THREE.Vector3;
13
14
  onRemove?: () => void;
14
15
  onEdit?: () => void;
16
+ onExpand?: () => void;
15
17
  onClose: () => void;
16
18
  buildingId?: number;
17
19
  timestamp: Date;
18
20
  sphere: THREE.Object3D;
19
21
  highlight?: boolean;
22
+ timeZone?: string;
23
+ showActions?: boolean;
24
+ canEdit?: boolean;
25
+ canDelete?: boolean;
26
+ actionLabels?: CommentActionLabels;
20
27
  }
21
- export default function BimSensor({ sensorName, sensorType, tags, dataUrl, dataFormat, updateFrequency, onRemove, onEdit, onClose, buildingId, timestamp, sphere, highlight, }: SensorProps): React.ReactElement;
28
+ export default function BimSensor({ sensorName, sensorType, tags, dataUrl, dataFormat, updateFrequency, onRemove, onEdit, onExpand, onClose, buildingId, timestamp, highlight, timeZone, showActions, canEdit, canDelete, actionLabels, }: SensorProps): React.ReactElement;
22
29
  export {};
23
30
  //# sourceMappingURL=BimSensor.d.ts.map