@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.
- package/dist/core/components/ui/Comments/CollapsibleCommentItem.js.map +1 -1
- package/dist/core/components/ui/Comments/Comment.js.map +1 -1
- package/dist/core/components/ui/Comments/CommentActionButtons.d.ts +5 -1
- package/dist/core/components/ui/Comments/CommentActionButtons.d.ts.map +1 -1
- package/dist/core/components/ui/Comments/CommentActionButtons.js +37 -9
- package/dist/core/components/ui/Comments/CommentActionButtons.js.map +1 -1
- package/dist/core/components/ui/Sensors/CollapsibleSensorItem.d.ts +1 -2
- package/dist/core/components/ui/Sensors/CollapsibleSensorItem.d.ts.map +1 -1
- package/dist/core/components/ui/Sensors/CollapsibleSensorItem.js +66 -94
- package/dist/core/components/ui/Sensors/CollapsibleSensorItem.js.map +1 -1
- package/dist/core/components/ui/Sensors/Sensor.d.ts +16 -3
- package/dist/core/components/ui/Sensors/Sensor.d.ts.map +1 -1
- package/dist/core/components/ui/Sensors/Sensor.js +76 -37
- package/dist/core/components/ui/Sensors/Sensor.js.map +1 -1
- package/dist/core/components/ui/Sensors/SensorChart.d.ts +12 -2
- package/dist/core/components/ui/Sensors/SensorChart.d.ts.map +1 -1
- package/dist/core/components/ui/Sensors/SensorChart.js +49 -12
- package/dist/core/components/ui/Sensors/SensorChart.js.map +1 -1
- package/dist/core/components/ui/Sensors/SensorDetailDialog.d.ts +9 -0
- package/dist/core/components/ui/Sensors/SensorDetailDialog.d.ts.map +1 -0
- package/dist/core/components/ui/Sensors/SensorDetailDialog.js +128 -0
- package/dist/core/components/ui/Sensors/SensorDetailDialog.js.map +1 -0
- package/dist/core/components/ui/Sensors/SensorInput.d.ts +6 -1
- package/dist/core/components/ui/Sensors/SensorInput.d.ts.map +1 -1
- package/dist/core/components/ui/Sensors/SensorInput.js +94 -8
- package/dist/core/components/ui/Sensors/SensorInput.js.map +1 -1
- package/dist/core/components/ui/Sensors/SensorTagsSection.d.ts +3 -1
- package/dist/core/components/ui/Sensors/SensorTagsSection.d.ts.map +1 -1
- package/dist/core/components/ui/Sensors/SensorTagsSection.js +4 -1
- package/dist/core/components/ui/Sensors/SensorTagsSection.js.map +1 -1
- package/dist/core/components/ui/Sensors/SensorsSection.d.ts +1 -3
- package/dist/core/components/ui/Sensors/SensorsSection.d.ts.map +1 -1
- package/dist/core/components/ui/Sensors/SensorsSection.js +33 -8
- package/dist/core/components/ui/Sensors/SensorsSection.js.map +1 -1
- package/dist/core/components/ui/Sensors/TimeZoneSelect.d.ts +7 -0
- package/dist/core/components/ui/Sensors/TimeZoneSelect.d.ts.map +1 -0
- package/dist/core/components/ui/Sensors/TimeZoneSelect.js +56 -0
- package/dist/core/components/ui/Sensors/TimeZoneSelect.js.map +1 -0
- package/dist/core/components/ui/Sensors/sensorData.d.ts +42 -0
- package/dist/core/components/ui/Sensors/sensorData.d.ts.map +1 -0
- package/dist/core/components/ui/Sensors/sensorData.js +118 -0
- package/dist/core/components/ui/Sensors/sensorData.js.map +1 -0
- package/dist/core/components/ui/Sensors/sensorRange.d.ts +24 -0
- package/dist/core/components/ui/Sensors/sensorRange.d.ts.map +1 -0
- package/dist/core/components/ui/Sensors/sensorRange.js +34 -0
- package/dist/core/components/ui/Sensors/sensorRange.js.map +1 -0
- package/dist/core/components/ui/Sensors/useSensorSeries.d.ts +15 -0
- package/dist/core/components/ui/Sensors/useSensorSeries.d.ts.map +1 -0
- package/dist/core/components/ui/Sensors/useSensorSeries.js +63 -0
- package/dist/core/components/ui/Sensors/useSensorSeries.js.map +1 -0
- package/dist/core/components/viewers/Viewer.js +1 -1
- package/dist/core/components/viewers/Viewer.js.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/SensorsTab/index.d.ts +1 -3
- package/dist/core/components/viewers/bim/src/BimSidebar/src/SensorsTab/index.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/SensorsTab/index.js +2 -2
- package/dist/core/components/viewers/bim/src/BimSidebar/src/SensorsTab/index.js.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/index.js +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/index.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/index.d.ts +1 -2
- package/dist/core/components/viewers/bim/src/tools/AddToBim/index.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/index.js +25 -2
- package/dist/core/components/viewers/bim/src/tools/AddToBim/index.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimComment.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimCommentCluster.d.ts +1 -22
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimCommentCluster.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimCommentCluster.js +2 -105
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimCommentCluster.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimSensor.d.ts +8 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimSensor.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimSensor.js +15 -3
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimSensor.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/clusterMarkersByScreenSpace.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentCameraUtils.d.ts +2 -23
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentCameraUtils.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentCameraUtils.js +2 -24
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentCameraUtils.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentMarkerConfig.d.ts +33 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentMarkerConfig.d.ts.map +1 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentMarkerConfig.js +90 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentMarkerConfig.js.map +1 -0
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useCommentMarkers.d.ts +7 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useCommentMarkers.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useCommentMarkers.js +26 -230
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useCommentMarkers.js.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useSensorMarkers.d.ts +7 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useSensorMarkers.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useSensorMarkers.js +39 -11
- package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useSensorMarkers.js.map +1 -1
- package/dist/core/components/viewers/map/MapViewer.d.ts +1 -2
- package/dist/core/components/viewers/map/MapViewer.d.ts.map +1 -1
- package/dist/core/components/viewers/map/MapViewer.js +2 -2
- package/dist/core/components/viewers/map/MapViewer.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/index.d.ts +1 -2
- package/dist/core/components/viewers/map/src/MapLayers/index.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/index.js +2 -2
- package/dist/core/components/viewers/map/src/MapLayers/index.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.d.ts +1 -3
- package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.js +74 -39
- package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/index.js +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/index.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/SensorsTab/index.d.ts +1 -3
- package/dist/core/components/viewers/map/src/MapSidebar/src/SensorsTab/index.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapSidebar/src/SensorsTab/index.js +2 -2
- package/dist/core/components/viewers/map/src/MapSidebar/src/SensorsTab/index.js.map +1 -1
- package/dist/core/components/viewers/shared/markers/BimMarkerCluster.d.ts +19 -0
- package/dist/core/components/viewers/shared/markers/BimMarkerCluster.d.ts.map +1 -0
- package/dist/core/components/viewers/shared/markers/BimMarkerCluster.js +108 -0
- package/dist/core/components/viewers/shared/markers/BimMarkerCluster.js.map +1 -0
- package/dist/core/components/viewers/shared/markers/computeMarkerLookAt.d.ts +24 -0
- package/dist/core/components/viewers/shared/markers/computeMarkerLookAt.d.ts.map +1 -0
- package/dist/core/components/viewers/shared/markers/computeMarkerLookAt.js +27 -0
- package/dist/core/components/viewers/shared/markers/computeMarkerLookAt.js.map +1 -0
- package/dist/core/components/viewers/shared/markers/types.d.ts +66 -0
- package/dist/core/components/viewers/shared/markers/types.d.ts.map +1 -0
- package/dist/core/components/viewers/shared/markers/types.js +1 -0
- package/dist/core/components/viewers/shared/markers/types.js.map +1 -0
- package/dist/core/components/viewers/shared/markers/useMarkerLayerBim.d.ts +22 -0
- package/dist/core/components/viewers/shared/markers/useMarkerLayerBim.d.ts.map +1 -0
- package/dist/core/components/viewers/shared/markers/useMarkerLayerBim.js +233 -0
- package/dist/core/components/viewers/shared/markers/useMarkerLayerBim.js.map +1 -0
- package/dist/core/hooks/ui/useResizableSidebarWidth.js.map +1 -1
- package/dist/core/i18n/messages/en.json +5 -1
- package/dist/core/i18n/messages/es.json +5 -1
- package/dist/core/i18n/messages/fr.json +5 -1
- package/dist/core/store/AppConfig/context.d.ts.map +1 -1
- package/dist/core/store/AppConfig/context.js +4 -1
- package/dist/core/store/AppConfig/context.js.map +1 -1
- package/dist/core/store/AppConfig/reducer.d.ts +10 -0
- package/dist/core/store/AppConfig/reducer.d.ts.map +1 -1
- package/dist/core/store/AppConfig/reducer.js +10 -0
- package/dist/core/store/AppConfig/reducer.js.map +1 -1
- package/dist/core/store/Menus/context.d.ts.map +1 -1
- package/dist/core/store/Menus/context.js +5 -2
- package/dist/core/store/Menus/context.js.map +1 -1
- package/dist/core/store/Menus/reducer.d.ts +45 -1
- package/dist/core/store/Menus/reducer.d.ts.map +1 -1
- package/dist/core/store/Menus/reducer.js +21 -3
- package/dist/core/store/Menus/reducer.js.map +1 -1
- package/dist/core/utils/timeUtils.d.ts +24 -1
- package/dist/core/utils/timeUtils.d.ts.map +1 -1
- package/dist/core/utils/timeUtils.js +52 -3
- package/dist/core/utils/timeUtils.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,233 @@
|
|
|
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 { createRoot } from "react-dom/client";
|
|
23
|
+
import * as THREE from "three";
|
|
24
|
+
import { CSS2DObject } from "three/addons/renderers/CSS2DRenderer.js";
|
|
25
|
+
import { HooksCtx } from "../../../../hooks/provider";
|
|
26
|
+
import { clusterMarkersByScreenSpace } from "../../bim/src/tools/AddToBim/src/clusterMarkersByScreenSpace";
|
|
27
|
+
import { renderCSS2DMarkers } from "../../bim/src/tools/AddToBim/src/renderCSS2DMarkers";
|
|
28
|
+
import { computeMarkerLookAt } from "./computeMarkerLookAt";
|
|
29
|
+
function makePropsMapper(state) {
|
|
30
|
+
const { config, currentId, focusedId, onSelect, onFocus, onEdit, onRemove } = state;
|
|
31
|
+
return (item) => {
|
|
32
|
+
const highlight = currentId === item.id;
|
|
33
|
+
const focused = focusedId === item.id;
|
|
34
|
+
if (item.isPending) {
|
|
35
|
+
return config.propsMapper(item, {
|
|
36
|
+
highlight,
|
|
37
|
+
focused,
|
|
38
|
+
isAuthor: false,
|
|
39
|
+
onSelect: () => onSelect(item.id),
|
|
40
|
+
onFocus: () => onFocus(item.id)
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
return config.propsMapper(item, {
|
|
44
|
+
highlight,
|
|
45
|
+
focused,
|
|
46
|
+
isAuthor: config.isAuthor(item),
|
|
47
|
+
onSelect: () => onSelect(item.id),
|
|
48
|
+
onFocus: () => onFocus(item.id),
|
|
49
|
+
onEdit: () => onEdit(item.id),
|
|
50
|
+
onRemove: () => onRemove(item.id)
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function clearSingles(world, registry, config) {
|
|
55
|
+
renderCSS2DMarkers(world, {
|
|
56
|
+
items: [],
|
|
57
|
+
markerIdKey: config.markerIdKey,
|
|
58
|
+
registry,
|
|
59
|
+
component: config.component,
|
|
60
|
+
propsMapper: () => ({}),
|
|
61
|
+
sphereColor: config.sphereColor,
|
|
62
|
+
isVisible: false,
|
|
63
|
+
onRemove: () => {
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
function clearClusters(world, registry) {
|
|
68
|
+
registry.current.forEach(({ root, css2dObject }) => {
|
|
69
|
+
world.scene.three.remove(css2dObject);
|
|
70
|
+
const el = css2dObject.element;
|
|
71
|
+
el == null ? void 0 : el.remove();
|
|
72
|
+
root.unmount();
|
|
73
|
+
});
|
|
74
|
+
registry.current.clear();
|
|
75
|
+
}
|
|
76
|
+
function renderMarkers(state, singleRegistry, clusterRegistry, lastSignatureRef) {
|
|
77
|
+
var _a, _b, _c;
|
|
78
|
+
const { world, items, pending, isVisible, currentId, focusedId, coreHooks, config, onRemove, onSelect, onFocus } = state;
|
|
79
|
+
if (!world) return;
|
|
80
|
+
if (!isVisible) {
|
|
81
|
+
if (lastSignatureRef.current === "hidden") return;
|
|
82
|
+
clearSingles(world, singleRegistry, config);
|
|
83
|
+
clearClusters(world, clusterRegistry);
|
|
84
|
+
lastSignatureRef.current = "hidden";
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const camera = (_a = world.camera) == null ? void 0 : _a.three;
|
|
88
|
+
const dom = (_c = (_b = world.renderer) == null ? void 0 : _b.three) == null ? void 0 : _c.domElement;
|
|
89
|
+
if (!camera || !dom) return;
|
|
90
|
+
const width = dom.clientWidth || dom.width || 0;
|
|
91
|
+
const height = dom.clientHeight || dom.height || 0;
|
|
92
|
+
if (!width || !height) return;
|
|
93
|
+
const { clusters, singles } = clusterMarkersByScreenSpace(items, camera, width, height, 44);
|
|
94
|
+
const singleItems = [...singles, ...pending];
|
|
95
|
+
const clusterKeys = clusters.map((c) => c.key);
|
|
96
|
+
const dataHash = [...items, ...pending].map((c) => config.dataHashPart(c)).join(";");
|
|
97
|
+
const signature = `v2|${currentId}|${focusedId}|C:${clusterKeys.join(",")}|S:${singleItems.map((s) => s.id).join(",")}|D:${dataHash}`;
|
|
98
|
+
if (signature === lastSignatureRef.current) return;
|
|
99
|
+
lastSignatureRef.current = signature;
|
|
100
|
+
renderCSS2DMarkers(world, {
|
|
101
|
+
items: singleItems,
|
|
102
|
+
markerIdKey: config.markerIdKey,
|
|
103
|
+
registry: singleRegistry,
|
|
104
|
+
component: config.component,
|
|
105
|
+
hooksContextValue: coreHooks,
|
|
106
|
+
propsMapper: makePropsMapper(state),
|
|
107
|
+
sphereColor: config.sphereColor,
|
|
108
|
+
isVisible: true,
|
|
109
|
+
onRemove
|
|
110
|
+
});
|
|
111
|
+
singleRegistry.current.forEach((ref, id) => {
|
|
112
|
+
const nid = Number(id);
|
|
113
|
+
ref.css2dObject.renderOrder = nid === currentId || nid === focusedId ? 1 : 0;
|
|
114
|
+
});
|
|
115
|
+
const reg = clusterRegistry.current;
|
|
116
|
+
const wanted = new Set(clusterKeys);
|
|
117
|
+
for (const [key, ref] of reg) {
|
|
118
|
+
if (!wanted.has(key)) {
|
|
119
|
+
world.scene.three.remove(ref.css2dObject);
|
|
120
|
+
const el = ref.css2dObject.element;
|
|
121
|
+
el == null ? void 0 : el.remove();
|
|
122
|
+
ref.root.unmount();
|
|
123
|
+
reg.delete(key);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
for (const cluster of clusters) {
|
|
127
|
+
const members = cluster.members.map((m) => config.clusterMemberMapper(m));
|
|
128
|
+
const highlight = currentId != null && members.some((m) => m.id === currentId);
|
|
129
|
+
const element = React.createElement(config.clusterComponent, { members, highlight, onSelect, onFocus });
|
|
130
|
+
const wrapped = coreHooks ? React.createElement(HooksCtx.Provider, { value: coreHooks }, element) : element;
|
|
131
|
+
const existing = reg.get(cluster.key);
|
|
132
|
+
if (existing) {
|
|
133
|
+
existing.root.render(wrapped);
|
|
134
|
+
existing.css2dObject.position.set(cluster.worldCenter.x, cluster.worldCenter.y + 0.2, cluster.worldCenter.z);
|
|
135
|
+
existing.css2dObject.renderOrder = highlight ? 1 : 0;
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
const labelDiv = document.createElement("div");
|
|
139
|
+
labelDiv.style.pointerEvents = "auto";
|
|
140
|
+
const root = createRoot(labelDiv);
|
|
141
|
+
root.render(wrapped);
|
|
142
|
+
const css2dObject = new CSS2DObject(labelDiv);
|
|
143
|
+
css2dObject.position.set(cluster.worldCenter.x, cluster.worldCenter.y + 0.2, cluster.worldCenter.z);
|
|
144
|
+
css2dObject.element.style.pointerEvents = "auto";
|
|
145
|
+
css2dObject.renderOrder = highlight ? 1 : 0;
|
|
146
|
+
world.scene.three.add(css2dObject);
|
|
147
|
+
reg.set(cluster.key, { root, css2dObject });
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
function useMarkerLayerBim(world, items, opts) {
|
|
151
|
+
const { isVisible, store, coreHooks, config } = opts;
|
|
152
|
+
const singleRegistry = React.useRef(/* @__PURE__ */ new Map());
|
|
153
|
+
const clusterRegistry = React.useRef(/* @__PURE__ */ new Map());
|
|
154
|
+
const lastSignatureRef = React.useRef("");
|
|
155
|
+
const stateRef = React.useRef(null);
|
|
156
|
+
const itemsRef = React.useRef([]);
|
|
157
|
+
itemsRef.current = items;
|
|
158
|
+
const handleSelect = React.useCallback((id) => {
|
|
159
|
+
store.setCurrentId(id);
|
|
160
|
+
}, [store]);
|
|
161
|
+
const handleFocus = React.useCallback((id) => {
|
|
162
|
+
store.setCurrentId(id);
|
|
163
|
+
store.setFocusedId(id);
|
|
164
|
+
}, [store]);
|
|
165
|
+
const handleEdit = React.useCallback((id) => {
|
|
166
|
+
store.requestEdit(id);
|
|
167
|
+
}, [store]);
|
|
168
|
+
const [pending, setPending] = React.useState([]);
|
|
169
|
+
const addPending = React.useCallback((position) => {
|
|
170
|
+
const id = -Date.now();
|
|
171
|
+
setPending((prev) => [...prev, __spreadValues({ id }, position)]);
|
|
172
|
+
return id;
|
|
173
|
+
}, []);
|
|
174
|
+
const removePending = React.useCallback((id) => {
|
|
175
|
+
setPending((prev) => prev.filter((p) => p.id !== id));
|
|
176
|
+
}, []);
|
|
177
|
+
stateRef.current = {
|
|
178
|
+
world,
|
|
179
|
+
// Non-pending items carry an explicit isPending:false (parity with the pending placeholders).
|
|
180
|
+
// Cast: spreading a generic T and assigning back to T[] is not provably safe to tsc.
|
|
181
|
+
items: items.map((it) => __spreadProps(__spreadValues({}, it), { isPending: false })),
|
|
182
|
+
pending: pending.map((p) => config.makePendingItem(p.id, { x: p.x, y: p.y, z: p.z })),
|
|
183
|
+
isVisible,
|
|
184
|
+
currentId: store.currentId,
|
|
185
|
+
focusedId: store.focusedId,
|
|
186
|
+
coreHooks,
|
|
187
|
+
config,
|
|
188
|
+
onRemove: config.onRemove,
|
|
189
|
+
onSelect: handleSelect,
|
|
190
|
+
onFocus: handleFocus,
|
|
191
|
+
onEdit: handleEdit
|
|
192
|
+
};
|
|
193
|
+
React.useEffect(() => {
|
|
194
|
+
if (!world) return;
|
|
195
|
+
let raf = 0;
|
|
196
|
+
let frame = 0;
|
|
197
|
+
const loop = () => {
|
|
198
|
+
if ((frame++ & 1) === 0 && stateRef.current) {
|
|
199
|
+
renderMarkers(stateRef.current, singleRegistry, clusterRegistry, lastSignatureRef);
|
|
200
|
+
}
|
|
201
|
+
raf = requestAnimationFrame(loop);
|
|
202
|
+
};
|
|
203
|
+
raf = requestAnimationFrame(loop);
|
|
204
|
+
return () => {
|
|
205
|
+
cancelAnimationFrame(raf);
|
|
206
|
+
clearClusters(world, clusterRegistry);
|
|
207
|
+
clearSingles(world, singleRegistry, config);
|
|
208
|
+
lastSignatureRef.current = "";
|
|
209
|
+
};
|
|
210
|
+
}, [world]);
|
|
211
|
+
React.useEffect(() => {
|
|
212
|
+
var _a, _b;
|
|
213
|
+
if (store.focusedId == null || !world) return;
|
|
214
|
+
const target = itemsRef.current.find((it) => it.id === store.focusedId);
|
|
215
|
+
if (!target || target.x == null || target.y == null || target.z == null) return;
|
|
216
|
+
const controls = (_a = world.camera) == null ? void 0 : _a.controls;
|
|
217
|
+
if (!controls) return;
|
|
218
|
+
const camPos = controls.getPosition(new THREE.Vector3());
|
|
219
|
+
const camTarget = controls.getTarget(new THREE.Vector3());
|
|
220
|
+
const lookAt = computeMarkerLookAt(
|
|
221
|
+
{ x: camPos.x, y: camPos.y, z: camPos.z },
|
|
222
|
+
{ x: camTarget.x, y: camTarget.y, z: camTarget.z },
|
|
223
|
+
{ x: target.x, y: target.y, z: target.z },
|
|
224
|
+
(_b = config.focusDistance) != null ? _b : 8
|
|
225
|
+
);
|
|
226
|
+
void controls.setLookAt(lookAt.camX, lookAt.camY, lookAt.camZ, lookAt.tarX, lookAt.tarY, lookAt.tarZ, true);
|
|
227
|
+
}, [store.focusRequestId, world]);
|
|
228
|
+
return { addPending, removePending };
|
|
229
|
+
}
|
|
230
|
+
export {
|
|
231
|
+
useMarkerLayerBim
|
|
232
|
+
};
|
|
233
|
+
//# sourceMappingURL=useMarkerLayerBim.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../src/core/components/viewers/shared/markers/useMarkerLayerBim.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\nimport { createRoot } from \"react-dom/client\"\r\nimport * as THREE from \"three\"\r\nimport { CSS2DObject } from \"three/addons/renderers/CSS2DRenderer.js\"\r\n\r\nimport { HooksCtx, type HooksBag } from \"../../../../hooks/provider\"\r\nimport { clusterMarkersByScreenSpace } from \"../../bim/src/tools/AddToBim/src/clusterMarkersByScreenSpace\"\r\nimport { renderCSS2DMarkers, type MarkerRef } from \"../../bim/src/tools/AddToBim/src/renderCSS2DMarkers\"\r\n\r\nimport { computeMarkerLookAt } from \"./computeMarkerLookAt\"\r\nimport type { BimMarkerConfig, BimMarkerItem, MarkerStoreAdapter } from \"./types\"\r\n\r\ntype ClusterMarkerRef = { root: ReturnType<typeof createRoot>; css2dObject: CSS2DObject }\r\n\r\ninterface RenderState<T extends BimMarkerItem> {\r\n world: any\r\n /** Non-pending items eligible for clustering. */\r\n items: T[]\r\n /** Pending (being-created) markers — always rendered as singles. */\r\n pending: T[]\r\n isVisible: boolean\r\n currentId: number | null\r\n focusedId: number | null\r\n coreHooks: HooksBag | null\r\n config: BimMarkerConfig<T>\r\n onRemove: (id: number) => void\r\n onSelect: (id: number) => void\r\n onFocus: (id: number) => void\r\n onEdit: (id: number) => void\r\n}\r\n\r\n/**\r\n * Builds the per-item props for the card component, delegating entity-specific shaping to\r\n * `config.propsMapper`. Pending (being-created) items get a reduced context: `isAuthor:false`\r\n * and no author-gated (edit/remove) handlers.\r\n */\r\nfunction makePropsMapper<T extends BimMarkerItem>(state: RenderState<T>) {\r\n const { config, currentId, focusedId, onSelect, onFocus, onEdit, onRemove } = state\r\n return (item: T): Record<string, unknown> => {\r\n const highlight = currentId === item.id\r\n const focused = focusedId === item.id\r\n if (item.isPending) {\r\n return config.propsMapper(item, {\r\n highlight,\r\n focused,\r\n isAuthor: false,\r\n onSelect: () => onSelect(item.id),\r\n onFocus: () => onFocus(item.id),\r\n })\r\n }\r\n return config.propsMapper(item, {\r\n highlight,\r\n focused,\r\n isAuthor: config.isAuthor(item),\r\n onSelect: () => onSelect(item.id),\r\n onFocus: () => onFocus(item.id),\r\n onEdit: () => onEdit(item.id),\r\n onRemove: () => onRemove(item.id),\r\n })\r\n }\r\n}\r\n\r\nfunction clearSingles<T extends BimMarkerItem>(\r\n world: any,\r\n registry: React.MutableRefObject<Map<string, MarkerRef>>,\r\n config: BimMarkerConfig<T>,\r\n) {\r\n renderCSS2DMarkers(world, {\r\n items: [],\r\n markerIdKey: config.markerIdKey,\r\n registry,\r\n component: config.component,\r\n propsMapper: () => ({}),\r\n sphereColor: config.sphereColor,\r\n isVisible: false,\r\n onRemove: () => {},\r\n })\r\n}\r\n\r\nfunction clearClusters(world: any, registry: React.MutableRefObject<Map<string, ClusterMarkerRef>>) {\r\n registry.current.forEach(({ root, css2dObject }) => {\r\n world.scene.three.remove(css2dObject)\r\n const el = css2dObject.element as HTMLElement | undefined\r\n el?.remove()\r\n root.unmount()\r\n })\r\n registry.current.clear()\r\n}\r\n\r\n/**\r\n * Imperative render driven by a rAF loop. Recomputes screen-space clustering\r\n * from the current camera and only rebuilds DOM when the layout/data signature\r\n * changes (individual CSS2DObjects already track the camera every frame).\r\n */\r\nfunction renderMarkers<T extends BimMarkerItem>(\r\n state: RenderState<T>,\r\n singleRegistry: React.MutableRefObject<Map<string, MarkerRef>>,\r\n clusterRegistry: React.MutableRefObject<Map<string, ClusterMarkerRef>>,\r\n lastSignatureRef: React.MutableRefObject<string>,\r\n) {\r\n const { world, items, pending, isVisible, currentId, focusedId, coreHooks, config, onRemove, onSelect, onFocus } = state\r\n if (!world) return\r\n\r\n if (!isVisible) {\r\n if (lastSignatureRef.current === \"hidden\") return\r\n clearSingles(world, singleRegistry, config)\r\n clearClusters(world, clusterRegistry)\r\n lastSignatureRef.current = \"hidden\"\r\n return\r\n }\r\n\r\n const camera = world.camera?.three\r\n const dom = world.renderer?.three?.domElement\r\n if (!camera || !dom) return\r\n\r\n const width = dom.clientWidth || dom.width || 0\r\n const height = dom.clientHeight || dom.height || 0\r\n if (!width || !height) return\r\n\r\n const { clusters, singles } = clusterMarkersByScreenSpace(items, camera, width, height, 44)\r\n const singleItems = [...singles, ...pending]\r\n\r\n const clusterKeys = clusters.map((c) => c.key)\r\n const dataHash = [...items, ...pending].map((c) => config.dataHashPart(c)).join(\";\")\r\n const signature =\r\n `v2|${currentId}|${focusedId}|C:${clusterKeys.join(\",\")}` +\r\n `|S:${singleItems.map((s) => s.id).join(\",\")}|D:${dataHash}`\r\n if (signature === lastSignatureRef.current) return\r\n lastSignatureRef.current = signature\r\n\r\n // Singles (including pending) via the shared per-marker renderer\r\n renderCSS2DMarkers(world, {\r\n items: singleItems,\r\n markerIdKey: config.markerIdKey,\r\n registry: singleRegistry,\r\n component: config.component,\r\n hooksContextValue: coreHooks,\r\n propsMapper: makePropsMapper(state),\r\n sphereColor: config.sphereColor,\r\n isVisible: true,\r\n onRemove,\r\n })\r\n\r\n // Float the highlighted/focused marker above overlapping ones. The CSS2D renderer sorts by\r\n // renderOrder first (then camera distance), so this makes a selected marker that sits behind\r\n // nearer markers come to the front — visible and clickable instead of buried.\r\n singleRegistry.current.forEach((ref, id) => {\r\n const nid = Number(id)\r\n ref.css2dObject.renderOrder = nid === currentId || nid === focusedId ? 1 : 0\r\n })\r\n\r\n // Cluster bubbles, managed imperatively\r\n const reg = clusterRegistry.current\r\n const wanted = new Set(clusterKeys)\r\n for (const [key, ref] of reg) {\r\n if (!wanted.has(key)) {\r\n world.scene.three.remove(ref.css2dObject)\r\n const el = ref.css2dObject.element as HTMLElement | undefined\r\n el?.remove()\r\n ref.root.unmount()\r\n reg.delete(key)\r\n }\r\n }\r\n\r\n for (const cluster of clusters) {\r\n const members = cluster.members.map((m) => config.clusterMemberMapper(m))\r\n const highlight = currentId != null && members.some((m) => m.id === currentId)\r\n const element = React.createElement(config.clusterComponent, { members, highlight, onSelect, onFocus })\r\n const wrapped = coreHooks\r\n ? React.createElement(HooksCtx.Provider, { value: coreHooks }, element)\r\n : element\r\n\r\n const existing = reg.get(cluster.key)\r\n if (existing) {\r\n existing.root.render(wrapped)\r\n existing.css2dObject.position.set(cluster.worldCenter.x, cluster.worldCenter.y + 0.2, cluster.worldCenter.z)\r\n existing.css2dObject.renderOrder = highlight ? 1 : 0\r\n continue\r\n }\r\n\r\n const labelDiv = document.createElement(\"div\")\r\n labelDiv.style.pointerEvents = \"auto\"\r\n const root = createRoot(labelDiv)\r\n root.render(wrapped)\r\n\r\n const css2dObject = new CSS2DObject(labelDiv)\r\n css2dObject.position.set(cluster.worldCenter.x, cluster.worldCenter.y + 0.2, cluster.worldCenter.z)\r\n css2dObject.element.style.pointerEvents = \"auto\"\r\n css2dObject.renderOrder = highlight ? 1 : 0\r\n world.scene.three.add(css2dObject)\r\n\r\n reg.set(cluster.key, { root, css2dObject })\r\n }\r\n}\r\n\r\n/**\r\n * Generic BIM marker layer. Renders `items` as clustered CSS2D markers over a `three` world,\r\n * driving selection/focus/edit/remove through a `MarkerStoreAdapter` and shaping every\r\n * entity-specific detail through `config`. Behaviour is a straight generalization of the\r\n * comment marker layer (screen-space clustering, rAF diffing, double-click focus).\r\n */\r\nexport function useMarkerLayerBim<T extends BimMarkerItem>(\r\n world: any,\r\n items: T[],\r\n opts: {\r\n isVisible: boolean\r\n store: MarkerStoreAdapter\r\n coreHooks: HooksBag | null\r\n config: BimMarkerConfig<T>\r\n },\r\n): { addPending: (pos: { x: number; y: number; z: number }) => number; removePending: (id: number) => void } {\r\n const { isVisible, store, coreHooks, config } = opts\r\n\r\n const singleRegistry = React.useRef<Map<string, MarkerRef>>(new Map())\r\n const clusterRegistry = React.useRef<Map<string, ClusterMarkerRef>>(new Map())\r\n const lastSignatureRef = React.useRef<string>(\"\")\r\n const stateRef = React.useRef<RenderState<T> | null>(null)\r\n const itemsRef = React.useRef<T[]>([])\r\n\r\n itemsRef.current = items\r\n\r\n const handleSelect = React.useCallback((id: number) => {\r\n store.setCurrentId(id)\r\n }, [store])\r\n\r\n const handleFocus = React.useCallback((id: number) => {\r\n store.setCurrentId(id)\r\n store.setFocusedId(id)\r\n }, [store])\r\n\r\n const handleEdit = React.useCallback((id: number) => {\r\n store.requestEdit(id)\r\n }, [store])\r\n\r\n // Pending markers for loading indicators while an item is being created\r\n const [pending, setPending] = React.useState<Array<{ id: number; x: number; y: number; z: number }>>([])\r\n\r\n const addPending = React.useCallback((position: { x: number; y: number; z: number }) => {\r\n const id = -Date.now()\r\n setPending((prev) => [...prev, { id, ...position }])\r\n return id\r\n }, [])\r\n\r\n const removePending = React.useCallback((id: number) => {\r\n setPending((prev) => prev.filter((p) => p.id !== id))\r\n }, [])\r\n\r\n // Keep the latest inputs available to the rAF render loop (avoids stale closures)\r\n stateRef.current = {\r\n world,\r\n // Non-pending items carry an explicit isPending:false (parity with the pending placeholders).\r\n // Cast: spreading a generic T and assigning back to T[] is not provably safe to tsc.\r\n items: items.map((it) => ({ ...it, isPending: false })) as T[],\r\n pending: pending.map((p) => config.makePendingItem(p.id, { x: p.x, y: p.y, z: p.z })),\r\n isVisible,\r\n currentId: store.currentId,\r\n focusedId: store.focusedId,\r\n coreHooks,\r\n config,\r\n onRemove: config.onRemove,\r\n onSelect: handleSelect,\r\n onFocus: handleFocus,\r\n onEdit: handleEdit,\r\n }\r\n\r\n React.useEffect(() => {\r\n if (!world) return\r\n let raf = 0\r\n let frame = 0\r\n const loop = () => {\r\n // Recompute at most every other frame; renderMarkers no-ops unless the\r\n // cluster layout or data actually changed.\r\n if ((frame++ & 1) === 0 && stateRef.current) {\r\n renderMarkers(stateRef.current, singleRegistry, clusterRegistry, lastSignatureRef)\r\n }\r\n raf = requestAnimationFrame(loop)\r\n }\r\n raf = requestAnimationFrame(loop)\r\n\r\n return () => {\r\n cancelAnimationFrame(raf)\r\n clearClusters(world, clusterRegistry)\r\n clearSingles(world, singleRegistry, config)\r\n lastSignatureRef.current = \"\"\r\n }\r\n }, [world])\r\n\r\n // Zoom to a marker when it is focused (double-clicked here or in the sidebar).\r\n React.useEffect(() => {\r\n if (store.focusedId == null || !world) return\r\n const target = itemsRef.current.find((it) => it.id === store.focusedId)\r\n if (!target || target.x == null || target.y == null || target.z == null) return\r\n const controls = world.camera?.controls\r\n if (!controls) return\r\n\r\n const camPos = controls.getPosition(new THREE.Vector3())\r\n const camTarget = controls.getTarget(new THREE.Vector3())\r\n const lookAt = computeMarkerLookAt(\r\n { x: camPos.x, y: camPos.y, z: camPos.z },\r\n { x: camTarget.x, y: camTarget.y, z: camTarget.z },\r\n { x: target.x, y: target.y, z: target.z },\r\n config.focusDistance ?? 8,\r\n )\r\n void controls.setLookAt(lookAt.camX, lookAt.camY, lookAt.camZ, lookAt.tarX, lookAt.tarY, lookAt.tarZ, true)\r\n }, [store.focusRequestId, world])\r\n\r\n return { addPending, removePending }\r\n}\r\n"],"mappings":"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","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/core/hooks/ui/useResizableSidebarWidth.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 { useIsMobile } from './use-mobile'\n\n/** Minimum sidebar width. Equal to the previous fixed width, so it never shrinks below the old default. */\nexport const MIN_SIDEBAR_WIDTH = 410\n/** Maximum sidebar width, roughly half a laptop viewport. */\nexport const MAX_SIDEBAR_WIDTH = 720\nexport const DEFAULT_SIDEBAR_WIDTH = MIN_SIDEBAR_WIDTH\nexport const SIDEBAR_WIDTH_STORAGE_KEY = 'cdt:sidebar-width'\n\n/** Clamp to [MIN, MAX]. NaN falls back to the default; ±Infinity clamps to the bounds. */\nexport function clampSidebarWidth(width: number): number {\n if (Number.isNaN(width)) return DEFAULT_SIDEBAR_WIDTH\n return Math.min(MAX_SIDEBAR_WIDTH, Math.max(MIN_SIDEBAR_WIDTH, width))\n}\n\n/** SSR-safe read of the persisted width, clamped; falls back to the default on missing/corrupt values. */\nexport function readStoredSidebarWidth(): number {\n if (typeof window === 'undefined') return DEFAULT_SIDEBAR_WIDTH\n try {\n const raw = window.localStorage.getItem(SIDEBAR_WIDTH_STORAGE_KEY)\n if (raw === null) return DEFAULT_SIDEBAR_WIDTH\n const parsed = Number.parseFloat(raw)\n if (!Number.isFinite(parsed)) return DEFAULT_SIDEBAR_WIDTH\n return clampSidebarWidth(parsed)\n } catch {\n return DEFAULT_SIDEBAR_WIDTH\n }\n}\n\n/** SSR-safe persist of a clamped width. Swallows storage errors (private mode, quota). */\nexport function writeStoredSidebarWidth(width: number): void {\n if (typeof window === 'undefined') return\n try {\n window.localStorage.setItem(SIDEBAR_WIDTH_STORAGE_KEY, String(clampSidebarWidth(width)))\n } catch {\n // ignore: storage may be unavailable (private mode / quota)\n }\n}\n\nexport interface ResizableSidebarWidth {\n /** Current width in px. Only meaningful when `canResize` is true; mobile stays full-width via CSS. */\n width: number\n /** True while a drag is in progress (for cursor / no-select styling). */\n isResizing: boolean\n /** True on desktop only. Mobile is full-width and not resizable. */\n canResize: boolean\n /** Attach to the drag handle's onPointerDown. */\n startResize: (e: React.PointerEvent) => void\n}\n\n/**\n * Drives a user-resizable sidebar width on desktop. Width is restored from and persisted to\n * localStorage, clamped to [MIN_SIDEBAR_WIDTH, MAX_SIDEBAR_WIDTH]. On mobile (`useIsMobile`)\n * resizing is disabled and the sidebar is expected to render full-width via CSS.\n */\nexport function useResizableSidebarWidth(): ResizableSidebarWidth {\n const isMobile = useIsMobile()\n const canResize = !isMobile\n\n const [width, setWidth] = React.useState<number>(DEFAULT_SIDEBAR_WIDTH)\n const [isResizing, setIsResizing] = React.useState(false)\n\n // Hydrate from storage after mount (avoids SSR/client mismatch on first paint).\n React.useEffect(() => {\n setWidth(readStoredSidebarWidth())\n }, [])\n\n const startResize = React.useCallback(\n (e: React.PointerEvent) => {\n if (!canResize) return\n e.preventDefault()\n setIsResizing(true)\n\n const startX = e.clientX\n const startWidth = width\n\n const onMove = (ev: PointerEvent) => {\n setWidth(clampSidebarWidth(startWidth + (ev.clientX - startX)))\n }\n const onUp = () => {\n setIsResizing(false)\n window.removeEventListener('pointermove', onMove)\n window.removeEventListener('pointerup', onUp)\n // Persist the final width; read the latest via the functional updater.\n setWidth(current => {\n writeStoredSidebarWidth(current)\n return current\n })\n }\n\n window.addEventListener('pointermove', onMove)\n window.addEventListener('pointerup', onUp)\n },\n [canResize, width]\n )\n\n return { width, isResizing, canResize, startResize }\n}\n"],"mappings":";AAKA,YAAY,WAAW;AAEvB,SAAS,mBAAmB;AAGrB,MAAM,oBAAoB;AAE1B,MAAM,oBAAoB;AAC1B,MAAM,wBAAwB;AAC9B,MAAM,4BAA4B;AAGlC,SAAS,kBAAkB,OAAuB;AACvD,MAAI,OAAO,MAAM,KAAK,EAAG,QAAO;AAChC,SAAO,KAAK,IAAI,mBAAmB,KAAK,IAAI,mBAAmB,KAAK,CAAC;AACvE;AAGO,SAAS,yBAAiC;AAC/C,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,MAAI;AACF,UAAM,MAAM,OAAO,aAAa,QAAQ,yBAAyB;AACjE,QAAI,QAAQ,KAAM,QAAO;AACzB,UAAM,SAAS,OAAO,WAAW,GAAG;AACpC,QAAI,CAAC,OAAO,SAAS,MAAM,EAAG,QAAO;AACrC,WAAO,kBAAkB,MAAM;AAAA,EACjC,SAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGO,SAAS,wBAAwB,OAAqB;AAC3D,MAAI,OAAO,WAAW,YAAa;AACnC,MAAI;AACF,WAAO,aAAa,QAAQ,2BAA2B,OAAO,kBAAkB,KAAK,CAAC,CAAC;AAAA,EACzF,SAAQ;AAAA,EAER;AACF;AAkBO,SAAS,2BAAkD;AAChE,QAAM,WAAW,YAAY;AAC7B,QAAM,YAAY,CAAC;AAEnB,QAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,SAAiB,qBAAqB;AACtE,QAAM,CAAC,YAAY,aAAa,IAAI,MAAM,SAAS,KAAK;AAGxD,QAAM,UAAU,MAAM;AACpB,aAAS,uBAAuB,CAAC;AAAA,EACnC,GAAG,CAAC,CAAC;AAEL,QAAM,cAAc,MAAM;AAAA,IACxB,CAAC,MAA0B;AACzB,UAAI,CAAC,UAAW;AAChB,QAAE,eAAe;AACjB,oBAAc,IAAI;AAElB,YAAM,SAAS,EAAE;AACjB,YAAM,aAAa;AAEnB,YAAM,SAAS,CAAC,OAAqB;AACnC,iBAAS,kBAAkB,cAAc,GAAG,UAAU,OAAO,CAAC;AAAA,MAChE;AACA,YAAM,OAAO,MAAM;AACjB,sBAAc,KAAK;AACnB,eAAO,oBAAoB,eAAe,MAAM;AAChD,eAAO,oBAAoB,aAAa,IAAI;AAE5C,iBAAS,aAAW;AAClB,kCAAwB,OAAO;AAC/B,iBAAO;AAAA,QACT,CAAC;AAAA,MACH;AAEA,aAAO,iBAAiB,eAAe,MAAM;AAC7C,aAAO,iBAAiB,aAAa,IAAI;AAAA,IAC3C;AAAA,IACA,CAAC,WAAW,KAAK;AAAA,EACnB;AAEA,SAAO,EAAE,OAAO,YAAY,WAAW,YAAY;AACrD;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/core/hooks/ui/useResizableSidebarWidth.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 { useIsMobile } from './use-mobile'\r\n\r\n/** Minimum sidebar width. Equal to the previous fixed width, so it never shrinks below the old default. */\r\nexport const MIN_SIDEBAR_WIDTH = 410\r\n/** Maximum sidebar width, roughly half a laptop viewport. */\r\nexport const MAX_SIDEBAR_WIDTH = 720\r\nexport const DEFAULT_SIDEBAR_WIDTH = MIN_SIDEBAR_WIDTH\r\nexport const SIDEBAR_WIDTH_STORAGE_KEY = 'cdt:sidebar-width'\r\n\r\n/** Clamp to [MIN, MAX]. NaN falls back to the default; ±Infinity clamps to the bounds. */\r\nexport function clampSidebarWidth(width: number): number {\r\n if (Number.isNaN(width)) return DEFAULT_SIDEBAR_WIDTH\r\n return Math.min(MAX_SIDEBAR_WIDTH, Math.max(MIN_SIDEBAR_WIDTH, width))\r\n}\r\n\r\n/** SSR-safe read of the persisted width, clamped; falls back to the default on missing/corrupt values. */\r\nexport function readStoredSidebarWidth(): number {\r\n if (typeof window === 'undefined') return DEFAULT_SIDEBAR_WIDTH\r\n try {\r\n const raw = window.localStorage.getItem(SIDEBAR_WIDTH_STORAGE_KEY)\r\n if (raw === null) return DEFAULT_SIDEBAR_WIDTH\r\n const parsed = Number.parseFloat(raw)\r\n if (!Number.isFinite(parsed)) return DEFAULT_SIDEBAR_WIDTH\r\n return clampSidebarWidth(parsed)\r\n } catch {\r\n return DEFAULT_SIDEBAR_WIDTH\r\n }\r\n}\r\n\r\n/** SSR-safe persist of a clamped width. Swallows storage errors (private mode, quota). */\r\nexport function writeStoredSidebarWidth(width: number): void {\r\n if (typeof window === 'undefined') return\r\n try {\r\n window.localStorage.setItem(SIDEBAR_WIDTH_STORAGE_KEY, String(clampSidebarWidth(width)))\r\n } catch {\r\n // ignore: storage may be unavailable (private mode / quota)\r\n }\r\n}\r\n\r\nexport interface ResizableSidebarWidth {\r\n /** Current width in px. Only meaningful when `canResize` is true; mobile stays full-width via CSS. */\r\n width: number\r\n /** True while a drag is in progress (for cursor / no-select styling). */\r\n isResizing: boolean\r\n /** True on desktop only. Mobile is full-width and not resizable. */\r\n canResize: boolean\r\n /** Attach to the drag handle's onPointerDown. */\r\n startResize: (e: React.PointerEvent) => void\r\n}\r\n\r\n/**\r\n * Drives a user-resizable sidebar width on desktop. Width is restored from and persisted to\r\n * localStorage, clamped to [MIN_SIDEBAR_WIDTH, MAX_SIDEBAR_WIDTH]. On mobile (`useIsMobile`)\r\n * resizing is disabled and the sidebar is expected to render full-width via CSS.\r\n */\r\nexport function useResizableSidebarWidth(): ResizableSidebarWidth {\r\n const isMobile = useIsMobile()\r\n const canResize = !isMobile\r\n\r\n const [width, setWidth] = React.useState<number>(DEFAULT_SIDEBAR_WIDTH)\r\n const [isResizing, setIsResizing] = React.useState(false)\r\n\r\n // Hydrate from storage after mount (avoids SSR/client mismatch on first paint).\r\n React.useEffect(() => {\r\n setWidth(readStoredSidebarWidth())\r\n }, [])\r\n\r\n const startResize = React.useCallback(\r\n (e: React.PointerEvent) => {\r\n if (!canResize) return\r\n e.preventDefault()\r\n setIsResizing(true)\r\n\r\n const startX = e.clientX\r\n const startWidth = width\r\n\r\n const onMove = (ev: PointerEvent) => {\r\n setWidth(clampSidebarWidth(startWidth + (ev.clientX - startX)))\r\n }\r\n const onUp = () => {\r\n setIsResizing(false)\r\n window.removeEventListener('pointermove', onMove)\r\n window.removeEventListener('pointerup', onUp)\r\n // Persist the final width; read the latest via the functional updater.\r\n setWidth(current => {\r\n writeStoredSidebarWidth(current)\r\n return current\r\n })\r\n }\r\n\r\n window.addEventListener('pointermove', onMove)\r\n window.addEventListener('pointerup', onUp)\r\n },\r\n [canResize, width]\r\n )\r\n\r\n return { width, isResizing, canResize, startResize }\r\n}\r\n"],"mappings":";AAKA,YAAY,WAAW;AAEvB,SAAS,mBAAmB;AAGrB,MAAM,oBAAoB;AAE1B,MAAM,oBAAoB;AAC1B,MAAM,wBAAwB;AAC9B,MAAM,4BAA4B;AAGlC,SAAS,kBAAkB,OAAuB;AACvD,MAAI,OAAO,MAAM,KAAK,EAAG,QAAO;AAChC,SAAO,KAAK,IAAI,mBAAmB,KAAK,IAAI,mBAAmB,KAAK,CAAC;AACvE;AAGO,SAAS,yBAAiC;AAC/C,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,MAAI;AACF,UAAM,MAAM,OAAO,aAAa,QAAQ,yBAAyB;AACjE,QAAI,QAAQ,KAAM,QAAO;AACzB,UAAM,SAAS,OAAO,WAAW,GAAG;AACpC,QAAI,CAAC,OAAO,SAAS,MAAM,EAAG,QAAO;AACrC,WAAO,kBAAkB,MAAM;AAAA,EACjC,SAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGO,SAAS,wBAAwB,OAAqB;AAC3D,MAAI,OAAO,WAAW,YAAa;AACnC,MAAI;AACF,WAAO,aAAa,QAAQ,2BAA2B,OAAO,kBAAkB,KAAK,CAAC,CAAC;AAAA,EACzF,SAAQ;AAAA,EAER;AACF;AAkBO,SAAS,2BAAkD;AAChE,QAAM,WAAW,YAAY;AAC7B,QAAM,YAAY,CAAC;AAEnB,QAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,SAAiB,qBAAqB;AACtE,QAAM,CAAC,YAAY,aAAa,IAAI,MAAM,SAAS,KAAK;AAGxD,QAAM,UAAU,MAAM;AACpB,aAAS,uBAAuB,CAAC;AAAA,EACnC,GAAG,CAAC,CAAC;AAEL,QAAM,cAAc,MAAM;AAAA,IACxB,CAAC,MAA0B;AACzB,UAAI,CAAC,UAAW;AAChB,QAAE,eAAe;AACjB,oBAAc,IAAI;AAElB,YAAM,SAAS,EAAE;AACjB,YAAM,aAAa;AAEnB,YAAM,SAAS,CAAC,OAAqB;AACnC,iBAAS,kBAAkB,cAAc,GAAG,UAAU,OAAO,CAAC;AAAA,MAChE;AACA,YAAM,OAAO,MAAM;AACjB,sBAAc,KAAK;AACnB,eAAO,oBAAoB,eAAe,MAAM;AAChD,eAAO,oBAAoB,aAAa,IAAI;AAE5C,iBAAS,aAAW;AAClB,kCAAwB,OAAO;AAC/B,iBAAO;AAAA,QACT,CAAC;AAAA,MACH;AAEA,aAAO,iBAAiB,eAAe,MAAM;AAC7C,aAAO,iBAAiB,aAAa,IAAI;AAAA,IAC3C;AAAA,IACA,CAAC,WAAW,KAAK;AAAA,EACnB;AAEA,SAAO,EAAE,OAAO,YAAY,WAAW,YAAY;AACrD;","names":[]}
|
|
@@ -1043,7 +1043,10 @@
|
|
|
1043
1043
|
"placeSensorTooltipBim": "Double-click on the model to place the sensor",
|
|
1044
1044
|
"selectType": "Select type",
|
|
1045
1045
|
"selectFormat": "Select format",
|
|
1046
|
-
"tags": "Tags"
|
|
1046
|
+
"tags": "Tags",
|
|
1047
|
+
"previewPoints": "data points",
|
|
1048
|
+
"previewNoData": "No data at this URL",
|
|
1049
|
+
"previewError": "Could not reach this URL"
|
|
1047
1050
|
},
|
|
1048
1051
|
"SensorLayers": {
|
|
1049
1052
|
"sensorEdited": "Sensor edited",
|
|
@@ -1054,6 +1057,7 @@
|
|
|
1054
1057
|
"addSensorTitle": "Add Sensor",
|
|
1055
1058
|
"searchSensors": "Search sensors...",
|
|
1056
1059
|
"editSensor": "Edit sensor",
|
|
1060
|
+
"expandSensor": "Expand",
|
|
1057
1061
|
"sensorEdited": "Sensor edited",
|
|
1058
1062
|
"deleteSensor": "Delete sensor",
|
|
1059
1063
|
"sensorDeleted": "Sensor deleted",
|
|
@@ -1043,7 +1043,10 @@
|
|
|
1043
1043
|
"placeSensorTooltipBim": "Haga doble clic en el modelo para colocar el sensor",
|
|
1044
1044
|
"selectType": "Seleccionar tipo",
|
|
1045
1045
|
"selectFormat": "Seleccionar formato",
|
|
1046
|
-
"tags": "Etiquetas"
|
|
1046
|
+
"tags": "Etiquetas",
|
|
1047
|
+
"previewPoints": "puntos de datos",
|
|
1048
|
+
"previewNoData": "No hay datos en esta URL",
|
|
1049
|
+
"previewError": "No se pudo acceder a esta URL"
|
|
1047
1050
|
},
|
|
1048
1051
|
"SensorLayers": {
|
|
1049
1052
|
"sensorEdited": "Sensor editado",
|
|
@@ -1054,6 +1057,7 @@
|
|
|
1054
1057
|
"addSensorTitle": "Añadir sensor",
|
|
1055
1058
|
"searchSensors": "Buscar sensores...",
|
|
1056
1059
|
"editSensor": "Editar sensor",
|
|
1060
|
+
"expandSensor": "Ampliar",
|
|
1057
1061
|
"sensorEdited": "Sensor editado",
|
|
1058
1062
|
"deleteSensor": "Eliminar sensor",
|
|
1059
1063
|
"sensorDeleted": "Sensor eliminado",
|
|
@@ -1043,7 +1043,10 @@
|
|
|
1043
1043
|
"placeSensorTooltipBim": "Double-cliquez sur le modèle pour placer le capteur",
|
|
1044
1044
|
"selectType": "Sélectionner le type",
|
|
1045
1045
|
"selectFormat": "Sélectionner le format",
|
|
1046
|
-
"tags": "Étiquettes"
|
|
1046
|
+
"tags": "Étiquettes",
|
|
1047
|
+
"previewPoints": "points de données",
|
|
1048
|
+
"previewNoData": "Aucune donnée à cette URL",
|
|
1049
|
+
"previewError": "Impossible d'accéder à cette URL"
|
|
1047
1050
|
},
|
|
1048
1051
|
"SensorLayers": {
|
|
1049
1052
|
"sensorEdited": "Capteur modifié",
|
|
@@ -1054,6 +1057,7 @@
|
|
|
1054
1057
|
"addSensorTitle": "Ajouter un capteur",
|
|
1055
1058
|
"searchSensors": "Rechercher des capteurs...",
|
|
1056
1059
|
"editSensor": "Modifier le capteur",
|
|
1060
|
+
"expandSensor": "Agrandir",
|
|
1057
1061
|
"sensorEdited": "Capteur modifié",
|
|
1058
1062
|
"deleteSensor": "Supprimer le capteur",
|
|
1059
1063
|
"sensorDeleted": "Capteur supprimé",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../../src/core/store/AppConfig/context.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../../src/core/store/AppConfig/context.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAM/B,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAElE,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AAED,KAAK,gBAAgB,GAAG;IACtB,SAAS,EAAE,cAAc,CAAA;IACzB,aAAa,EAAE,aAAa,CAAA;CAC7B,CAAA;AAoBD,eAAO,MAAM,gBAAgB;WACpB,gBAAgB;cACb,KAAK,CAAC,QAAQ,CAAC,gBAAgB,CAAC;EAI1C,CAAA;AAEF,eAAO,MAAM,iBAAiB,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,iBAAiB,CAAC;IAAE,aAAa,CAAC,EAAE,aAAa,CAAA;CAAE,CAAC,CAQlG,CAAA;AAED,eAAO,MAAM,mBAAmB;WAjBvB,gBAAgB;cACb,KAAK,CAAC,QAAQ,CAAC,gBAAgB,CAAC;CAgB+B,CAAA"}
|
|
@@ -20,11 +20,14 @@ var __spreadValues = (a, b) => {
|
|
|
20
20
|
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
21
21
|
import { jsx } from "react/jsx-runtime";
|
|
22
22
|
import * as React from "react";
|
|
23
|
+
import { detectTimeZone } from "../../utils/timeUtils";
|
|
23
24
|
import { AppConfigReducer } from "./reducer";
|
|
24
25
|
const initialState = {
|
|
25
26
|
appConfig: {
|
|
26
27
|
organization: null,
|
|
27
|
-
user: null
|
|
28
|
+
user: null,
|
|
29
|
+
displayTimeZone: detectTimeZone(),
|
|
30
|
+
displayTimeZoneUserSet: false
|
|
28
31
|
},
|
|
29
32
|
runtimeConfig: {}
|
|
30
33
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/core/store/AppConfig/context.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 { AppConfigReducer } from './reducer'\r\n\r\nimport type { AppConfigActions, AppConfigState } from './reducer';\r\n\r\nexport type RuntimeConfig = {\r\n minioUrl?: string\r\n martinUrl?: string\r\n recaptchaSiteKey?: string\r\n pointcloudApiUrl?: string\r\n geocodeEarthApiKey?: string\r\n geocoderUrl?: string\r\n photonUrl?: string\r\n nominatimUrl?: string\r\n maptilerKey?: string\r\n}\r\n\r\ntype InitialStateType = {\r\n appConfig: AppConfigState\r\n runtimeConfig: RuntimeConfig\r\n}\r\n\r\nconst initialState: InitialStateType = {\r\n appConfig: {\r\n organization: null,\r\n user: null,\r\n },\r\n runtimeConfig: {},\r\n}\r\n\r\nconst reducer = (\r\n { appConfig, runtimeConfig }: InitialStateType,\r\n action: AppConfigActions,\r\n): InitialStateType => ({\r\n appConfig: AppConfigReducer(appConfig, action),\r\n runtimeConfig,\r\n})\r\n\r\nexport const AppConfigContext = React.createContext<{\r\n state: InitialStateType\r\n dispatch: React.Dispatch<AppConfigActions>\r\n}>({\r\n state: initialState,\r\n dispatch: () => null,\r\n})\r\n\r\nexport const AppConfigProvider: React.FC<React.PropsWithChildren<{ runtimeConfig?: RuntimeConfig }>> = ({ children, runtimeConfig = {} }) => {\r\n const [state, dispatch] = React.useReducer(reducer, { ...initialState, runtimeConfig })\r\n const value = React.useMemo(() => ({ state, dispatch }), [state])\r\n return (\r\n <AppConfigContext.Provider value={value}>\r\n {children}\r\n </AppConfigContext.Provider>\r\n )\r\n}\r\n\r\nexport const useAppConfigContext = () => React.useContext(AppConfigContext)"],"mappings":";;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"sources":["../../../../src/core/store/AppConfig/context.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 { detectTimeZone } from '../../utils/timeUtils'\r\n\r\nimport { AppConfigReducer } from './reducer'\r\n\r\nimport type { AppConfigActions, AppConfigState } from './reducer';\r\n\r\nexport type RuntimeConfig = {\r\n minioUrl?: string\r\n martinUrl?: string\r\n recaptchaSiteKey?: string\r\n pointcloudApiUrl?: string\r\n geocodeEarthApiKey?: string\r\n geocoderUrl?: string\r\n photonUrl?: string\r\n nominatimUrl?: string\r\n maptilerKey?: string\r\n}\r\n\r\ntype InitialStateType = {\r\n appConfig: AppConfigState\r\n runtimeConfig: RuntimeConfig\r\n}\r\n\r\nconst initialState: InitialStateType = {\r\n appConfig: {\r\n organization: null,\r\n user: null,\r\n displayTimeZone: detectTimeZone(),\r\n displayTimeZoneUserSet: false,\r\n },\r\n runtimeConfig: {},\r\n}\r\n\r\nconst reducer = (\r\n { appConfig, runtimeConfig }: InitialStateType,\r\n action: AppConfigActions,\r\n): InitialStateType => ({\r\n appConfig: AppConfigReducer(appConfig, action),\r\n runtimeConfig,\r\n})\r\n\r\nexport const AppConfigContext = React.createContext<{\r\n state: InitialStateType\r\n dispatch: React.Dispatch<AppConfigActions>\r\n}>({\r\n state: initialState,\r\n dispatch: () => null,\r\n})\r\n\r\nexport const AppConfigProvider: React.FC<React.PropsWithChildren<{ runtimeConfig?: RuntimeConfig }>> = ({ children, runtimeConfig = {} }) => {\r\n const [state, dispatch] = React.useReducer(reducer, { ...initialState, runtimeConfig })\r\n const value = React.useMemo(() => ({ state, dispatch }), [state])\r\n return (\r\n <AppConfigContext.Provider value={value}>\r\n {children}\r\n </AppConfigContext.Provider>\r\n )\r\n}\r\n\r\nexport const useAppConfigContext = () => React.useContext(AppConfigContext)"],"mappings":";;;;;;;;;;;;;;;;;;;;AA4DI;AAvDJ,YAAY,WAAW;AAEvB,SAAS,sBAAsB;AAE/B,SAAS,wBAAwB;AAqBjC,MAAM,eAAiC;AAAA,EACrC,WAAW;AAAA,IACT,cAAc;AAAA,IACd,MAAM;AAAA,IACN,iBAAiB,eAAe;AAAA,IAChC,wBAAwB;AAAA,EAC1B;AAAA,EACA,eAAe,CAAC;AAClB;AAEA,MAAM,UAAU,CACd,EAAE,WAAW,cAAc,GAC3B,YACsB;AAAA,EACtB,WAAW,iBAAiB,WAAW,MAAM;AAAA,EAC7C;AACF;AAEO,MAAM,mBAAmB,MAAM,cAGnC;AAAA,EACD,OAAO;AAAA,EACP,UAAU,MAAM;AAClB,CAAC;AAEM,MAAM,oBAA0F,CAAC,EAAE,UAAU,gBAAgB,CAAC,EAAE,MAAM;AAC3I,QAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,WAAW,SAAS,iCAAK,eAAL,EAAmB,cAAc,EAAC;AACtF,QAAM,QAAQ,MAAM,QAAQ,OAAO,EAAE,OAAO,SAAS,IAAI,CAAC,KAAK,CAAC;AAChE,SACE,oBAAC,iBAAiB,UAAjB,EAA0B,OACxB,UACH;AAEJ;AAEO,MAAM,sBAAsB,MAAM,MAAM,WAAW,gBAAgB;","names":[]}
|
|
@@ -3,11 +3,21 @@ import type { ActionMap } from '../ActionMap';
|
|
|
3
3
|
interface AppConfigTypes {
|
|
4
4
|
organization: Organization | null;
|
|
5
5
|
user: User | null;
|
|
6
|
+
/** Display timezone for sensor time rendering (non-persisted). */
|
|
7
|
+
displayTimeZone: string;
|
|
8
|
+
/** True once the user picked a zone, so location defaults stop overriding it. */
|
|
9
|
+
displayTimeZoneUserSet: boolean;
|
|
6
10
|
}
|
|
7
11
|
export type AppConfigState = AppConfigTypes;
|
|
8
12
|
export type AppConfigPayload = {
|
|
9
13
|
['SET_ORGANIZATION']: Pick<AppConfigTypes, 'organization'>;
|
|
10
14
|
['SET_USER']: Pick<AppConfigTypes, 'user'>;
|
|
15
|
+
['SET_DISPLAY_TIME_ZONE']: {
|
|
16
|
+
displayTimeZone: string;
|
|
17
|
+
};
|
|
18
|
+
['SET_DEFAULT_TIME_ZONE']: {
|
|
19
|
+
displayTimeZone: string;
|
|
20
|
+
};
|
|
11
21
|
};
|
|
12
22
|
export type AppConfigActions = ActionMap<AppConfigPayload>[keyof ActionMap<AppConfigPayload>];
|
|
13
23
|
export declare const AppConfigReducer: (state: AppConfigState, action: AppConfigActions) => AppConfigTypes;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reducer.d.ts","sourceRoot":"","sources":["../../../../src/core/store/AppConfig/reducer.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAA;AAC7D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAE7C,UAAU,cAAc;IACtB,YAAY,EAAE,YAAY,GAAG,IAAI,CAAA;IACjC,IAAI,EAAE,IAAI,GAAG,IAAI,CAAA;
|
|
1
|
+
{"version":3,"file":"reducer.d.ts","sourceRoot":"","sources":["../../../../src/core/store/AppConfig/reducer.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAA;AAC7D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAE7C,UAAU,cAAc;IACtB,YAAY,EAAE,YAAY,GAAG,IAAI,CAAA;IACjC,IAAI,EAAE,IAAI,GAAG,IAAI,CAAA;IACjB,kEAAkE;IAClE,eAAe,EAAE,MAAM,CAAA;IACvB,iFAAiF;IACjF,sBAAsB,EAAE,OAAO,CAAA;CAChC;AAED,MAAM,MAAM,cAAc,GAAG,cAAc,CAAA;AAE3C,MAAM,MAAM,gBAAgB,GAAG;IAC7B,CAAC,kBAAkB,CAAC,EAAG,IAAI,CAAC,cAAc,EAAE,cAAc,CAAC,CAAA;IAC3D,CAAC,UAAU,CAAC,EAAE,IAAI,CAAC,cAAc,EAAE,MAAM,CAAC,CAAA;IAC1C,CAAC,uBAAuB,CAAC,EAAE;QAAE,eAAe,EAAE,MAAM,CAAA;KAAE,CAAA;IACtD,CAAC,uBAAuB,CAAC,EAAE;QAAE,eAAe,EAAE,MAAM,CAAA;KAAE,CAAA;CACvD,CAAA;AAED,MAAM,MAAM,gBAAgB,GACxB,SAAS,CAAC,gBAAgB,CAAC,CAAC,MAAM,SAAS,CAAC,gBAAgB,CAAC,CAAC,CAAA;AAElE,eAAO,MAAM,gBAAgB,GAAI,OAAO,cAAc,EAAE,QAAQ,gBAAgB,mBA+B/E,CAAA"}
|
|
@@ -29,6 +29,16 @@ const AppConfigReducer = (state, action) => {
|
|
|
29
29
|
return __spreadProps(__spreadValues({}, state), {
|
|
30
30
|
user
|
|
31
31
|
});
|
|
32
|
+
case "SET_DISPLAY_TIME_ZONE":
|
|
33
|
+
return __spreadProps(__spreadValues({}, state), {
|
|
34
|
+
displayTimeZone: action.payload.displayTimeZone,
|
|
35
|
+
displayTimeZoneUserSet: true
|
|
36
|
+
});
|
|
37
|
+
case "SET_DEFAULT_TIME_ZONE":
|
|
38
|
+
if (state.displayTimeZoneUserSet) return state;
|
|
39
|
+
return __spreadProps(__spreadValues({}, state), {
|
|
40
|
+
displayTimeZone: action.payload.displayTimeZone
|
|
41
|
+
});
|
|
32
42
|
default:
|
|
33
43
|
return state;
|
|
34
44
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/core/store/AppConfig/reducer.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport type { Organization, User } from '../../types/dbTypes'\r\nimport type { ActionMap } from '../ActionMap'\r\n\r\ninterface AppConfigTypes {\r\n organization: Organization | null\r\n user: User | null\r\n}\r\n\r\nexport type AppConfigState = AppConfigTypes\r\n\r\nexport type AppConfigPayload = {\r\n ['SET_ORGANIZATION']: Pick<AppConfigTypes, 'organization'>\r\n ['SET_USER']: Pick<AppConfigTypes, 'user'>\r\n}\r\n\r\nexport type AppConfigActions\r\n = ActionMap<AppConfigPayload>[keyof ActionMap<AppConfigPayload>]\r\n\r\nexport const AppConfigReducer = (state: AppConfigState, action: AppConfigActions) => {\r\n switch (action.type) {\r\n case 'SET_ORGANIZATION':\r\n const { organization } = action.payload\r\n return {\r\n ...state,\r\n organization,\r\n }\r\n case 'SET_USER':\r\n const { user } = action.payload\r\n return {\r\n ...state,\r\n user,\r\n }\r\n default:\r\n\r\n return state\r\n }\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"sources":["../../../../src/core/store/AppConfig/reducer.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport type { Organization, User } from '../../types/dbTypes'\r\nimport type { ActionMap } from '../ActionMap'\r\n\r\ninterface AppConfigTypes {\r\n organization: Organization | null\r\n user: User | null\r\n /** Display timezone for sensor time rendering (non-persisted). */\r\n displayTimeZone: string\r\n /** True once the user picked a zone, so location defaults stop overriding it. */\r\n displayTimeZoneUserSet: boolean\r\n}\r\n\r\nexport type AppConfigState = AppConfigTypes\r\n\r\nexport type AppConfigPayload = {\r\n ['SET_ORGANIZATION']: Pick<AppConfigTypes, 'organization'>\r\n ['SET_USER']: Pick<AppConfigTypes, 'user'>\r\n ['SET_DISPLAY_TIME_ZONE']: { displayTimeZone: string }\r\n ['SET_DEFAULT_TIME_ZONE']: { displayTimeZone: string }\r\n}\r\n\r\nexport type AppConfigActions\r\n = ActionMap<AppConfigPayload>[keyof ActionMap<AppConfigPayload>]\r\n\r\nexport const AppConfigReducer = (state: AppConfigState, action: AppConfigActions) => {\r\n switch (action.type) {\r\n case 'SET_ORGANIZATION':\r\n const { organization } = action.payload\r\n return {\r\n ...state,\r\n organization,\r\n }\r\n case 'SET_USER':\r\n const { user } = action.payload\r\n return {\r\n ...state,\r\n user,\r\n }\r\n case 'SET_DISPLAY_TIME_ZONE':\r\n return {\r\n ...state,\r\n displayTimeZone: action.payload.displayTimeZone,\r\n displayTimeZoneUserSet: true,\r\n }\r\n case 'SET_DEFAULT_TIME_ZONE':\r\n // Location-derived default: apply only until the user makes an explicit choice.\r\n if (state.displayTimeZoneUserSet) return state\r\n return {\r\n ...state,\r\n displayTimeZone: action.payload.displayTimeZone,\r\n }\r\n default:\r\n\r\n return state\r\n }\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA2BO,MAAM,mBAAmB,CAAC,OAAuB,WAA6B;AACnF,UAAQ,OAAO,MAAM;AAAA,IACnB,KAAK;AACH,YAAM,EAAE,aAAa,IAAI,OAAO;AAChC,aAAO,iCACF,QADE;AAAA,QAEL;AAAA,MACF;AAAA,IACF,KAAK;AACH,YAAM,EAAE,KAAK,IAAI,OAAO;AACxB,aAAO,iCACF,QADE;AAAA,QAEL;AAAA,MACF;AAAA,IACF,KAAK;AACH,aAAO,iCACF,QADE;AAAA,QAEL,iBAAiB,OAAO,QAAQ;AAAA,QAChC,wBAAwB;AAAA,MAC1B;AAAA,IACF,KAAK;AAEH,UAAI,MAAM,uBAAwB,QAAO;AACzC,aAAO,iCACF,QADE;AAAA,QAEL,iBAAiB,OAAO,QAAQ;AAAA,MAClC;AAAA,IACF;AAEE,aAAO;AAAA,EACX;AACF;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../../src/core/store/Menus/context.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAM/B,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC1D,OAAO,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAC/E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAEhD,KAAK,gBAAgB,GAAG;IACpB,KAAK,EAAE,UAAU,CAAA;CACpB,CAAA;
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../../src/core/store/Menus/context.tsx"],"names":[],"mappings":"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"}
|
|
@@ -25,7 +25,10 @@ const getInitialState = (pathname) => {
|
|
|
25
25
|
visibleSensorTypes: {},
|
|
26
26
|
visibleSensorTags: {},
|
|
27
27
|
currentSensorId: null,
|
|
28
|
-
currentSensorTypeId: null
|
|
28
|
+
currentSensorTypeId: null,
|
|
29
|
+
focusedSensorId: null,
|
|
30
|
+
sensorFocusRequestId: 0,
|
|
31
|
+
pendingSensorAction: null
|
|
29
32
|
}
|
|
30
33
|
};
|
|
31
34
|
};
|
|
@@ -33,7 +36,7 @@ const reducer = ({ menus }, action) => ({
|
|
|
33
36
|
menus: MenusReducer(menus, action)
|
|
34
37
|
});
|
|
35
38
|
const MenusContext = React.createContext({
|
|
36
|
-
state: { menus: { currentViewer: ViewerNames.map, rowsPerPage: 10, selectedTab: "file", commentsVisibleInViewer: [], currentCommentId: null, focusedCommentId: null, focusRequestId: 0, pendingCommentAction: null, sensorsVisibleInViewer: [], visibleSensorTypes: {}, visibleSensorTags: {}, currentSensorId: null, currentSensorTypeId: null } },
|
|
39
|
+
state: { menus: { currentViewer: ViewerNames.map, rowsPerPage: 10, selectedTab: "file", commentsVisibleInViewer: [], currentCommentId: null, focusedCommentId: null, focusRequestId: 0, pendingCommentAction: null, sensorsVisibleInViewer: [], visibleSensorTypes: {}, visibleSensorTags: {}, currentSensorId: null, currentSensorTypeId: null, focusedSensorId: null, sensorFocusRequestId: 0, pendingSensorAction: null } },
|
|
37
40
|
dispatch: () => null,
|
|
38
41
|
isSidebarOpen: false,
|
|
39
42
|
setIsSidebarOpen: () => null,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/core/store/Menus/context.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 { usePathname } from 'next/navigation'\r\nimport * as React from \"react\";\r\n\r\nimport { ViewerNames } from '../../types/dbTypes'\r\n\r\nimport { MenusReducer } from './reducer'\r\n\r\nimport type { MenusActions, MenusState } from './reducer';\r\nimport type { Building, Site, User, Infrastructure } from '../../types/dbTypes'\r\nimport type { FileRow } from '../../types/files'\r\n\r\ntype InitialStateType = {\r\n menus: MenusState\r\n}\r\n\r\n// Function to determine if path is an auth page\r\nconst isAuthPath = (pathname?: string): boolean => {\r\n if (!pathname) return false\r\n return pathname.includes('/auth/')\r\n}\r\n\r\n// Get initial state based on current path\r\nconst getInitialState = (pathname?: string): InitialStateType => {\r\n const isAuth = isAuthPath(pathname)\r\n return {\r\n menus: {\r\n currentViewer: isAuth ? ViewerNames.auth : ViewerNames.map,\r\n rowsPerPage: 10, // Default rows per page\r\n selectedTab: 'file',\r\n commentsVisibleInViewer: [],\r\n currentCommentId: null,\r\n focusedCommentId: null,\r\n focusRequestId: 0,\r\n pendingCommentAction: null,\r\n sensorsVisibleInViewer: [],\r\n visibleSensorTypes: {},\r\n visibleSensorTags: {},\r\n currentSensorId: null,\r\n currentSensorTypeId: null,\r\n },\r\n }\r\n}\r\n\r\nconst reducer = ({ menus }: InitialStateType, action: MenusActions) => ({\r\n menus: MenusReducer(menus, action),\r\n})\r\n\r\nexport const MenusContext = React.createContext<{\r\n state: InitialStateType\r\n dispatch: React.Dispatch<MenusActions>\r\n isSidebarOpen: boolean\r\n setIsSidebarOpen: React.Dispatch<React.SetStateAction<boolean>>\r\n view: 'table' | 'detail'\r\n setView: React.Dispatch<React.SetStateAction<'table' | 'detail'>>\r\n selectedItem: Building | null\r\n setSelectedItem: React.Dispatch<React.SetStateAction<Building | null>>\r\n selectedSite: Site | null\r\n setSelectedSite: React.Dispatch<React.SetStateAction<Site | null>>\r\n selectedInfrastructure: Infrastructure | null\r\n setSelectedInfrastructure: React.Dispatch<React.SetStateAction<Infrastructure | null>>\r\n selectedFile: FileRow | null\r\n setSelectedFile: React.Dispatch<React.SetStateAction<FileRow | null>>\r\n selectedUser: Partial<User> | null\r\n setSelectedUser: React.Dispatch<React.SetStateAction<Partial<User> | null>>\r\n isDatasetMenuOpen: boolean\r\n setIsDatasetMenuOpen: React.Dispatch<React.SetStateAction<boolean>>\r\n isAuthPage?: boolean\r\n}>({\r\n state: { menus: { currentViewer: ViewerNames.map, rowsPerPage: 10, selectedTab: 'file', commentsVisibleInViewer: [], currentCommentId: null, focusedCommentId: null, focusRequestId: 0, pendingCommentAction: null, sensorsVisibleInViewer: [], visibleSensorTypes: {}, visibleSensorTags: {}, currentSensorId: null, currentSensorTypeId: null } },\r\n dispatch: () => null,\r\n isSidebarOpen: false,\r\n setIsSidebarOpen: () => null,\r\n view: 'table',\r\n setView: () => null,\r\n selectedItem: null,\r\n setSelectedItem: () => null,\r\n selectedSite: null,\r\n setSelectedSite: () => null,\r\n selectedInfrastructure: null,\r\n setSelectedInfrastructure: () => null,\r\n selectedFile: null,\r\n setSelectedFile: () => null,\r\n selectedUser: null,\r\n setSelectedUser: () => null,\r\n isDatasetMenuOpen: false,\r\n setIsDatasetMenuOpen: () => null,\r\n isAuthPage: true,\r\n})\r\n\r\nexport const MenusProvider: React.FC<React.PropsWithChildren> = ({ children }) => {\r\n const pathname = usePathname()\r\n const [state, dispatch] = React.useReducer(reducer, getInitialState(pathname))\r\n\r\n const [isSidebarOpen, setIsSidebarOpen] = React.useState(false)\r\n const [view, setView] = React.useState<'table' | 'detail'>('table')\r\n const [selectedItem, setSelectedItem] = React.useState<Building | null>(null)\r\n const [selectedSite, setSelectedSite] = React.useState<Site | null>(null)\r\n const [selectedInfrastructure, setSelectedInfrastructure] = React.useState<Infrastructure | null>(null)\r\n const [selectedFile, setSelectedFile] = React.useState<FileRow | null>(null)\r\n const [selectedUser, setSelectedUser] = React.useState<Partial<User> | null>(null)\r\n const [isDatasetMenuOpen, setIsDatasetMenuOpen] = React.useState(false)\r\n // Check if current path is an authentication page\r\n const isAuthPage = isAuthPath(pathname)\r\n\r\n // Update state if pathname changes and auth state changes\r\n React.useEffect(() => {\r\n if (isAuthPage && state.menus.currentViewer !== ViewerNames.auth) {\r\n dispatch({\r\n type: 'SET_VIEWER',\r\n payload: { currentViewer: ViewerNames.auth },\r\n })\r\n setIsSidebarOpen(false)\r\n }\r\n else if (!isAuthPage && state.menus.currentViewer === ViewerNames.auth) {\r\n dispatch({\r\n type: 'SET_VIEWER',\r\n payload: { currentViewer: ViewerNames.map },\r\n })\r\n }\r\n }, [pathname, isAuthPage, state.menus.currentViewer])\r\n\r\n const value = React.useMemo(() => ({\r\n state,\r\n dispatch,\r\n isSidebarOpen,\r\n setIsSidebarOpen,\r\n view,\r\n setView,\r\n selectedItem,\r\n setSelectedItem,\r\n selectedSite,\r\n setSelectedSite,\r\n selectedInfrastructure,\r\n setSelectedInfrastructure,\r\n selectedFile,\r\n setSelectedFile,\r\n selectedUser,\r\n setSelectedUser,\r\n isDatasetMenuOpen,\r\n setIsDatasetMenuOpen,\r\n }), [\r\n state, isSidebarOpen, view, selectedItem, selectedSite,\r\n selectedInfrastructure, selectedFile, selectedUser, isDatasetMenuOpen,\r\n ])\r\n\r\n return (\r\n <MenusContext.Provider value={value}>\r\n { children }\r\n </MenusContext.Provider>\r\n )\r\n}\r\n\r\nexport const useMenusContext = () => React.useContext(MenusContext)\r\n"],"mappings":";
|
|
1
|
+
{"version":3,"sources":["../../../../src/core/store/Menus/context.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 { usePathname } from 'next/navigation'\r\nimport * as React from \"react\";\r\n\r\nimport { ViewerNames } from '../../types/dbTypes'\r\n\r\nimport { MenusReducer } from './reducer'\r\n\r\nimport type { MenusActions, MenusState } from './reducer';\r\nimport type { Building, Site, User, Infrastructure } from '../../types/dbTypes'\r\nimport type { FileRow } from '../../types/files'\r\n\r\ntype InitialStateType = {\r\n menus: MenusState\r\n}\r\n\r\n// Function to determine if path is an auth page\r\nconst isAuthPath = (pathname?: string): boolean => {\r\n if (!pathname) return false\r\n return pathname.includes('/auth/')\r\n}\r\n\r\n// Get initial state based on current path\r\nconst getInitialState = (pathname?: string): InitialStateType => {\r\n const isAuth = isAuthPath(pathname)\r\n return {\r\n menus: {\r\n currentViewer: isAuth ? ViewerNames.auth : ViewerNames.map,\r\n rowsPerPage: 10, // Default rows per page\r\n selectedTab: 'file',\r\n commentsVisibleInViewer: [],\r\n currentCommentId: null,\r\n focusedCommentId: null,\r\n focusRequestId: 0,\r\n pendingCommentAction: null,\r\n sensorsVisibleInViewer: [],\r\n visibleSensorTypes: {},\r\n visibleSensorTags: {},\r\n currentSensorId: null,\r\n currentSensorTypeId: null,\r\n focusedSensorId: null,\r\n sensorFocusRequestId: 0,\r\n pendingSensorAction: null,\r\n },\r\n }\r\n}\r\n\r\nconst reducer = ({ menus }: InitialStateType, action: MenusActions) => ({\r\n menus: MenusReducer(menus, action),\r\n})\r\n\r\nexport const MenusContext = React.createContext<{\r\n state: InitialStateType\r\n dispatch: React.Dispatch<MenusActions>\r\n isSidebarOpen: boolean\r\n setIsSidebarOpen: React.Dispatch<React.SetStateAction<boolean>>\r\n view: 'table' | 'detail'\r\n setView: React.Dispatch<React.SetStateAction<'table' | 'detail'>>\r\n selectedItem: Building | null\r\n setSelectedItem: React.Dispatch<React.SetStateAction<Building | null>>\r\n selectedSite: Site | null\r\n setSelectedSite: React.Dispatch<React.SetStateAction<Site | null>>\r\n selectedInfrastructure: Infrastructure | null\r\n setSelectedInfrastructure: React.Dispatch<React.SetStateAction<Infrastructure | null>>\r\n selectedFile: FileRow | null\r\n setSelectedFile: React.Dispatch<React.SetStateAction<FileRow | null>>\r\n selectedUser: Partial<User> | null\r\n setSelectedUser: React.Dispatch<React.SetStateAction<Partial<User> | null>>\r\n isDatasetMenuOpen: boolean\r\n setIsDatasetMenuOpen: React.Dispatch<React.SetStateAction<boolean>>\r\n isAuthPage?: boolean\r\n}>({\r\n state: { menus: { currentViewer: ViewerNames.map, rowsPerPage: 10, selectedTab: 'file', commentsVisibleInViewer: [], currentCommentId: null, focusedCommentId: null, focusRequestId: 0, pendingCommentAction: null, sensorsVisibleInViewer: [], visibleSensorTypes: {}, visibleSensorTags: {}, currentSensorId: null, currentSensorTypeId: null, focusedSensorId: null, sensorFocusRequestId: 0, pendingSensorAction: null } },\r\n dispatch: () => null,\r\n isSidebarOpen: false,\r\n setIsSidebarOpen: () => null,\r\n view: 'table',\r\n setView: () => null,\r\n selectedItem: null,\r\n setSelectedItem: () => null,\r\n selectedSite: null,\r\n setSelectedSite: () => null,\r\n selectedInfrastructure: null,\r\n setSelectedInfrastructure: () => null,\r\n selectedFile: null,\r\n setSelectedFile: () => null,\r\n selectedUser: null,\r\n setSelectedUser: () => null,\r\n isDatasetMenuOpen: false,\r\n setIsDatasetMenuOpen: () => null,\r\n isAuthPage: true,\r\n})\r\n\r\nexport const MenusProvider: React.FC<React.PropsWithChildren> = ({ children }) => {\r\n const pathname = usePathname()\r\n const [state, dispatch] = React.useReducer(reducer, getInitialState(pathname))\r\n\r\n const [isSidebarOpen, setIsSidebarOpen] = React.useState(false)\r\n const [view, setView] = React.useState<'table' | 'detail'>('table')\r\n const [selectedItem, setSelectedItem] = React.useState<Building | null>(null)\r\n const [selectedSite, setSelectedSite] = React.useState<Site | null>(null)\r\n const [selectedInfrastructure, setSelectedInfrastructure] = React.useState<Infrastructure | null>(null)\r\n const [selectedFile, setSelectedFile] = React.useState<FileRow | null>(null)\r\n const [selectedUser, setSelectedUser] = React.useState<Partial<User> | null>(null)\r\n const [isDatasetMenuOpen, setIsDatasetMenuOpen] = React.useState(false)\r\n // Check if current path is an authentication page\r\n const isAuthPage = isAuthPath(pathname)\r\n\r\n // Update state if pathname changes and auth state changes\r\n React.useEffect(() => {\r\n if (isAuthPage && state.menus.currentViewer !== ViewerNames.auth) {\r\n dispatch({\r\n type: 'SET_VIEWER',\r\n payload: { currentViewer: ViewerNames.auth },\r\n })\r\n setIsSidebarOpen(false)\r\n }\r\n else if (!isAuthPage && state.menus.currentViewer === ViewerNames.auth) {\r\n dispatch({\r\n type: 'SET_VIEWER',\r\n payload: { currentViewer: ViewerNames.map },\r\n })\r\n }\r\n }, [pathname, isAuthPage, state.menus.currentViewer])\r\n\r\n const value = React.useMemo(() => ({\r\n state,\r\n dispatch,\r\n isSidebarOpen,\r\n setIsSidebarOpen,\r\n view,\r\n setView,\r\n selectedItem,\r\n setSelectedItem,\r\n selectedSite,\r\n setSelectedSite,\r\n selectedInfrastructure,\r\n setSelectedInfrastructure,\r\n selectedFile,\r\n setSelectedFile,\r\n selectedUser,\r\n setSelectedUser,\r\n isDatasetMenuOpen,\r\n setIsDatasetMenuOpen,\r\n }), [\r\n state, isSidebarOpen, view, selectedItem, selectedSite,\r\n selectedInfrastructure, selectedFile, selectedUser, isDatasetMenuOpen,\r\n ])\r\n\r\n return (\r\n <MenusContext.Provider value={value}>\r\n { children }\r\n </MenusContext.Provider>\r\n )\r\n}\r\n\r\nexport const useMenusContext = () => React.useContext(MenusContext)\r\n"],"mappings":";AA0JQ;AArJR,SAAS,mBAAmB;AAC5B,YAAY,WAAW;AAEvB,SAAS,mBAAmB;AAE5B,SAAS,oBAAoB;AAW7B,MAAM,aAAa,CAAC,aAA+B;AAC/C,MAAI,CAAC,SAAU,QAAO;AACtB,SAAO,SAAS,SAAS,QAAQ;AACrC;AAGA,MAAM,kBAAkB,CAAC,aAAwC;AAC7D,QAAM,SAAS,WAAW,QAAQ;AAClC,SAAO;AAAA,IACH,OAAO;AAAA,MACH,eAAe,SAAS,YAAY,OAAO,YAAY;AAAA,MACvD,aAAa;AAAA;AAAA,MACb,aAAa;AAAA,MACb,yBAAyB,CAAC;AAAA,MAC1B,kBAAkB;AAAA,MAClB,kBAAkB;AAAA,MAClB,gBAAgB;AAAA,MAChB,sBAAsB;AAAA,MACtB,wBAAwB,CAAC;AAAA,MACzB,oBAAoB,CAAC;AAAA,MACrB,mBAAmB,CAAC;AAAA,MACpB,iBAAiB;AAAA,MACjB,qBAAqB;AAAA,MACrB,iBAAiB;AAAA,MACjB,sBAAsB;AAAA,MACtB,qBAAqB;AAAA,IACzB;AAAA,EACJ;AACJ;AAEA,MAAM,UAAU,CAAC,EAAE,MAAM,GAAqB,YAA0B;AAAA,EACpE,OAAO,aAAa,OAAO,MAAM;AACrC;AAEO,MAAM,eAAe,MAAM,cAoB/B;AAAA,EACC,OAAO,EAAE,OAAO,EAAE,eAAe,YAAY,KAAK,aAAa,IAAI,aAAa,QAAQ,yBAAyB,CAAC,GAAG,kBAAkB,MAAM,kBAAkB,MAAM,gBAAgB,GAAG,sBAAsB,MAAM,wBAAwB,CAAC,GAAG,oBAAoB,CAAC,GAAG,mBAAmB,CAAC,GAAG,iBAAiB,MAAM,qBAAqB,MAAM,iBAAiB,MAAM,sBAAsB,GAAG,qBAAqB,KAAK,EAAE;AAAA,EAC7Z,UAAU,MAAM;AAAA,EAChB,eAAe;AAAA,EACf,kBAAkB,MAAM;AAAA,EACxB,MAAM;AAAA,EACN,SAAS,MAAM;AAAA,EACf,cAAc;AAAA,EACd,iBAAiB,MAAM;AAAA,EACvB,cAAc;AAAA,EACd,iBAAiB,MAAM;AAAA,EACvB,wBAAwB;AAAA,EACxB,2BAA2B,MAAM;AAAA,EACjC,cAAc;AAAA,EACd,iBAAiB,MAAM;AAAA,EACvB,cAAc;AAAA,EACd,iBAAiB,MAAM;AAAA,EACvB,mBAAmB;AAAA,EACnB,sBAAsB,MAAM;AAAA,EAC5B,YAAY;AAChB,CAAC;AAEM,MAAM,gBAAmD,CAAC,EAAE,SAAS,MAAM;AAC9E,QAAM,WAAW,YAAY;AAC7B,QAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,WAAW,SAAS,gBAAgB,QAAQ,CAAC;AAE7E,QAAM,CAAC,eAAe,gBAAgB,IAAI,MAAM,SAAS,KAAK;AAC9D,QAAM,CAAC,MAAM,OAAO,IAAI,MAAM,SAA6B,OAAO;AAClE,QAAM,CAAC,cAAc,eAAe,IAAI,MAAM,SAA0B,IAAI;AAC5E,QAAM,CAAC,cAAc,eAAe,IAAI,MAAM,SAAsB,IAAI;AACxE,QAAM,CAAC,wBAAwB,yBAAyB,IAAI,MAAM,SAAgC,IAAI;AACtG,QAAM,CAAC,cAAc,eAAe,IAAI,MAAM,SAAyB,IAAI;AAC3E,QAAM,CAAC,cAAc,eAAe,IAAI,MAAM,SAA+B,IAAI;AACjF,QAAM,CAAC,mBAAmB,oBAAoB,IAAI,MAAM,SAAS,KAAK;AAEtE,QAAM,aAAa,WAAW,QAAQ;AAGtC,QAAM,UAAU,MAAM;AAClB,QAAI,cAAc,MAAM,MAAM,kBAAkB,YAAY,MAAM;AAC9D,eAAS;AAAA,QACL,MAAM;AAAA,QACN,SAAS,EAAE,eAAe,YAAY,KAAK;AAAA,MAC/C,CAAC;AACD,uBAAiB,KAAK;AAAA,IAC1B,WACS,CAAC,cAAc,MAAM,MAAM,kBAAkB,YAAY,MAAM;AACpE,eAAS;AAAA,QACL,MAAM;AAAA,QACN,SAAS,EAAE,eAAe,YAAY,IAAI;AAAA,MAC9C,CAAC;AAAA,IACL;AAAA,EACJ,GAAG,CAAC,UAAU,YAAY,MAAM,MAAM,aAAa,CAAC;AAEpD,QAAM,QAAQ,MAAM,QAAQ,OAAO;AAAA,IAC/B;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACJ,IAAI;AAAA,IACA;AAAA,IAAO;AAAA,IAAe;AAAA,IAAM;AAAA,IAAc;AAAA,IAC1C;AAAA,IAAwB;AAAA,IAAc;AAAA,IAAc;AAAA,EACxD,CAAC;AAED,SACI,oBAAC,aAAa,UAAb,EAAsB,OACzB,UACF;AAEJ;AAEO,MAAM,kBAAkB,MAAM,MAAM,WAAW,YAAY;","names":[]}
|
|
@@ -8,6 +8,13 @@ export type CommentActionRequest = {
|
|
|
8
8
|
/** Monotonic id so the same request fires again even if the target is unchanged. */
|
|
9
9
|
requestId: number;
|
|
10
10
|
};
|
|
11
|
+
/** Request from a viewer marker to open a sensor's editor in the sidebar. */
|
|
12
|
+
export type SensorActionRequest = {
|
|
13
|
+
sensorId: number;
|
|
14
|
+
action: 'edit';
|
|
15
|
+
/** Monotonic id so the same request fires again even if the target is unchanged. */
|
|
16
|
+
requestId: number;
|
|
17
|
+
};
|
|
11
18
|
interface MenusTypes {
|
|
12
19
|
currentViewer: ViewerNames;
|
|
13
20
|
rowsPerPage: number;
|
|
@@ -24,6 +31,11 @@ interface MenusTypes {
|
|
|
24
31
|
visibleSensorTags: Partial<Record<ViewerNames, string[]>>;
|
|
25
32
|
currentSensorId: number | null;
|
|
26
33
|
currentSensorTypeId: number | null;
|
|
34
|
+
/** Sensor double-clicked to zoom/focus. Drives the thick focus ring + camera move. */
|
|
35
|
+
focusedSensorId: number | null;
|
|
36
|
+
/** Monotonic counter bumped on every sensor focus dispatch so re-focusing re-zooms. */
|
|
37
|
+
sensorFocusRequestId: number;
|
|
38
|
+
pendingSensorAction: SensorActionRequest | null;
|
|
27
39
|
}
|
|
28
40
|
export type MenusState = MenusTypes;
|
|
29
41
|
export type MenusPayload = {
|
|
@@ -68,8 +80,40 @@ export type MenusPayload = {
|
|
|
68
80
|
};
|
|
69
81
|
['SET_CURRENT_SENSOR_ID']: Pick<MenusTypes, 'currentSensorId'>;
|
|
70
82
|
['SET_CURRENT_SENSOR_TYPE_ID']: Pick<MenusTypes, 'currentSensorTypeId'>;
|
|
83
|
+
['SET_FOCUSED_SENSOR_ID']: {
|
|
84
|
+
sensorId: number | null;
|
|
85
|
+
};
|
|
86
|
+
['REQUEST_SENSOR_ACTION']: {
|
|
87
|
+
sensorId: number;
|
|
88
|
+
};
|
|
89
|
+
['CLEAR_PENDING_SENSOR_ACTION']: undefined;
|
|
71
90
|
};
|
|
72
91
|
export type MenusActions = ActionMap<MenusPayload>[keyof ActionMap<MenusPayload>];
|
|
73
|
-
export declare const MenusReducer: (state: MenusState, action: MenusActions) =>
|
|
92
|
+
export declare const MenusReducer: (state: MenusState, action: MenusActions) => {
|
|
93
|
+
pendingSensorAction: {
|
|
94
|
+
sensorId: number;
|
|
95
|
+
action: string;
|
|
96
|
+
requestId: number;
|
|
97
|
+
};
|
|
98
|
+
currentViewer: ViewerNames;
|
|
99
|
+
rowsPerPage: number;
|
|
100
|
+
selectedTab: SidebarTabType;
|
|
101
|
+
commentsVisibleInViewer: ViewerNames[];
|
|
102
|
+
currentCommentId: number | null;
|
|
103
|
+
/** Comment double-clicked to zoom/focus. Drives the thick focus ring + camera move. */
|
|
104
|
+
focusedCommentId: number | null;
|
|
105
|
+
/** Monotonic counter bumped on every focus dispatch so re-focusing the same comment re-zooms. */
|
|
106
|
+
focusRequestId: number;
|
|
107
|
+
pendingCommentAction: CommentActionRequest | null;
|
|
108
|
+
sensorsVisibleInViewer: ViewerNames[];
|
|
109
|
+
visibleSensorTypes: Partial<Record<ViewerNames, number[]>>;
|
|
110
|
+
visibleSensorTags: Partial<Record<ViewerNames, string[]>>;
|
|
111
|
+
currentSensorId: number | null;
|
|
112
|
+
currentSensorTypeId: number | null;
|
|
113
|
+
/** Sensor double-clicked to zoom/focus. Drives the thick focus ring + camera move. */
|
|
114
|
+
focusedSensorId: number | null;
|
|
115
|
+
/** Monotonic counter bumped on every sensor focus dispatch so re-focusing re-zooms. */
|
|
116
|
+
sensorFocusRequestId: number;
|
|
117
|
+
};
|
|
74
118
|
export {};
|
|
75
119
|
//# sourceMappingURL=reducer.d.ts.map
|