@vertexvis/viewer-toolkit-react 0.0.1

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 (134) hide show
  1. package/dist/bundle.cjs.js +2525 -0
  2. package/dist/bundle.cjs.js.map +1 -0
  3. package/dist/bundle.esm.js +2479 -0
  4. package/dist/bundle.esm.js.map +1 -0
  5. package/dist/components/box-selection/__tests__/box-selection-button.spec.d.ts +1 -0
  6. package/dist/components/box-selection/__tests__/box-selection-tool.spec.d.ts +1 -0
  7. package/dist/components/box-selection/box-selection-button.d.ts +1 -0
  8. package/dist/components/box-selection/box-selection-tool.d.ts +1 -0
  9. package/dist/components/common/__tests__/slider.spec.d.ts +1 -0
  10. package/dist/components/common/popup-button.d.ts +14 -0
  11. package/dist/components/common/slider.d.ts +10 -0
  12. package/dist/components/context-menu/__tests__/scene-tree-context-menu.spec.d.ts +1 -0
  13. package/dist/components/context-menu/__tests__/viewer-context-menu.spec.d.ts +1 -0
  14. package/dist/components/context-menu/context-menu.d.ts +13 -0
  15. package/dist/components/context-menu/default-scene-tree-context-menu.d.ts +1 -0
  16. package/dist/components/context-menu/default-viewer-context-menu.d.ts +1 -0
  17. package/dist/components/context-menu/menu-items/fit-selected-menu-item.d.ts +1 -0
  18. package/dist/components/context-menu/menu-items/fly-to-menu-item.d.ts +1 -0
  19. package/dist/components/context-menu/menu-items/hide-all-menu-item.d.ts +1 -0
  20. package/dist/components/context-menu/menu-items/hide-part-menu-item.d.ts +1 -0
  21. package/dist/components/context-menu/menu-items/hide-selected-menu-item.d.ts +1 -0
  22. package/dist/components/context-menu/menu-items/show-all-menu-item.d.ts +1 -0
  23. package/dist/components/context-menu/menu-items/show-only-menu-item.d.ts +1 -0
  24. package/dist/components/context-menu/menu-items/show-only-selected-menu-item.d.ts +1 -0
  25. package/dist/components/context-menu/scene-tree-context-menu.d.ts +5 -0
  26. package/dist/components/context-menu/viewer-context-menu.d.ts +5 -0
  27. package/dist/components/scene-tree/__tests__/vertex-scene-tree-table-layout.spec.d.ts +1 -0
  28. package/dist/components/scene-tree/__tests__/vertex-scene-tree.spec.d.ts +1 -0
  29. package/dist/components/scene-tree/vertex-scene-tree-table-layout.d.ts +3 -0
  30. package/dist/components/scene-tree/vertex-scene-tree.d.ts +14 -0
  31. package/dist/components/toolbar/__tests__/toolbar.spec.d.ts +1 -0
  32. package/dist/components/toolbar/cross-section/__tests__/cross-section-alignment-popover-menu.spec.d.ts +1 -0
  33. package/dist/components/toolbar/cross-section/__tests__/cross-section-axis-popover-menu.spec.d.ts +1 -0
  34. package/dist/components/toolbar/cross-section/__tests__/cross-section-offset-stepper.spec.d.ts +1 -0
  35. package/dist/components/toolbar/cross-section/__tests__/cross-section.spec.d.ts +1 -0
  36. package/dist/components/toolbar/cross-section/cross-section-alignment-popover-menu.d.ts +1 -0
  37. package/dist/components/toolbar/cross-section/cross-section-axis-popover-menu.d.ts +1 -0
  38. package/dist/components/toolbar/cross-section/cross-section-offset-stepper.d.ts +9 -0
  39. package/dist/components/toolbar/cross-section/cross-section-popup-menu.d.ts +6 -0
  40. package/dist/components/toolbar/cross-section/cross-section.d.ts +8 -0
  41. package/dist/components/toolbar/default-toolbar.d.ts +1 -0
  42. package/dist/components/toolbar/fit-all.d.ts +1 -0
  43. package/dist/components/toolbar/pan.d.ts +1 -0
  44. package/dist/components/toolbar/rotate.d.ts +1 -0
  45. package/dist/components/toolbar/toolbar-divider.d.ts +1 -0
  46. package/dist/components/toolbar/toolbar.d.ts +5 -0
  47. package/dist/components/toolbar/zoom.d.ts +1 -0
  48. package/dist/components/viewer/__tests__/vertex-viewer-view-cube.spec.d.ts +1 -0
  49. package/dist/components/viewer/__tests__/vertex-viewer.spec.d.ts +1 -0
  50. package/dist/components/viewer/vertex-viewer-view-cube.d.ts +7 -0
  51. package/dist/components/viewer/vertex-viewer.d.ts +9 -0
  52. package/dist/events-listener.d.ts +7 -0
  53. package/dist/index.css +7 -0
  54. package/dist/index.d.ts +28 -0
  55. package/dist/root.d.ts +9 -0
  56. package/dist/state/box-selection/actions.d.ts +7 -0
  57. package/dist/state/box-selection/box-selection.d.ts +3 -0
  58. package/dist/state/box-selection/index.d.ts +2 -0
  59. package/dist/state/config/config.d.ts +2 -0
  60. package/dist/state/config/index.d.ts +1 -0
  61. package/dist/state/context-menu/actions.d.ts +13 -0
  62. package/dist/state/context-menu/backdrop.d.ts +2 -0
  63. package/dist/state/context-menu/context-menu.d.ts +13 -0
  64. package/dist/state/context-menu/index.d.ts +3 -0
  65. package/dist/state/cross-section/__tests__/actions.spec.d.ts +1 -0
  66. package/dist/state/cross-section/actions.d.ts +24 -0
  67. package/dist/state/cross-section/cross-section.d.ts +30 -0
  68. package/dist/state/cross-section/index.d.ts +2 -0
  69. package/dist/state/hits/__tests__/hits.spec.d.ts +1 -0
  70. package/dist/state/hits/actions.d.ts +6 -0
  71. package/dist/state/hits/event-state.d.ts +9 -0
  72. package/dist/state/hits/hits.d.ts +17 -0
  73. package/dist/state/hits/index.d.ts +3 -0
  74. package/dist/state/keybinding/__tests__/long-press.spec.d.ts +1 -0
  75. package/dist/state/keybinding/index.d.ts +3 -0
  76. package/dist/state/keybinding/keybinding.d.ts +151 -0
  77. package/dist/state/keybinding/long-press.d.ts +7 -0
  78. package/dist/state/keybinding/targets.d.ts +2 -0
  79. package/dist/state/performance/index.d.ts +1 -0
  80. package/dist/state/performance/types.d.ts +37 -0
  81. package/dist/state/scene-tree/columns/columns.d.ts +16 -0
  82. package/dist/state/scene-tree/columns/index.d.ts +1 -0
  83. package/dist/state/scene-tree/core/__tests__/actions.spec.d.ts +1 -0
  84. package/dist/state/scene-tree/core/actions.d.ts +9 -0
  85. package/dist/state/scene-tree/core/core.d.ts +5 -0
  86. package/dist/state/scene-tree/core/index.d.ts +2 -0
  87. package/dist/state/scene-tree/index.d.ts +1 -0
  88. package/dist/state/scene-tree/types.d.ts +14 -0
  89. package/dist/state/selection/__tests__/actions.spec.d.ts +1 -0
  90. package/dist/state/selection/actions.d.ts +25 -0
  91. package/dist/state/selection/event-state.d.ts +8 -0
  92. package/dist/state/selection/index.d.ts +3 -0
  93. package/dist/state/selection/selection.d.ts +16 -0
  94. package/dist/state/viewer/camera/actions.d.ts +7 -0
  95. package/dist/state/viewer/camera/index.d.ts +1 -0
  96. package/dist/state/viewer/core/core.d.ts +5 -0
  97. package/dist/state/viewer/core/index.d.ts +1 -0
  98. package/dist/state/viewer/frame/actions.d.ts +5 -0
  99. package/dist/state/viewer/index.d.ts +3 -0
  100. package/dist/state/viewer/scene/actions.d.ts +14 -0
  101. package/dist/state/viewer/scene/index.d.ts +2 -0
  102. package/dist/state/viewer/scene/item-operations.d.ts +11 -0
  103. package/dist/state/viewer/scene/scene.d.ts +6 -0
  104. package/dist/testing/__setup__/mocks.d.ts +4 -0
  105. package/dist/testing/additional-hooks-wrapper.d.ts +19 -0
  106. package/dist/testing/react.d.ts +13 -0
  107. package/dist/testing/recoil-observer.d.ts +6 -0
  108. package/dist/testing/recoil-state.d.ts +5 -0
  109. package/dist/testing/recoil-wrapper.d.ts +21 -0
  110. package/dist/testing/viewer.d.ts +72 -0
  111. package/dist/util/camera/__tests__/camera.spec.d.ts +1 -0
  112. package/dist/util/camera/camera.d.ts +3 -0
  113. package/dist/util/cross-section/__tests__/interaction-handler.spec.d.ts +1 -0
  114. package/dist/util/cross-section/__tests__/section-plane.spec.d.ts +1 -0
  115. package/dist/util/cross-section/interaction-handler.d.ts +10 -0
  116. package/dist/util/cross-section/section-plane.d.ts +61 -0
  117. package/dist/util/dom/elements.d.ts +1 -0
  118. package/dist/util/geometry/__tests__/bounding-box.spec.d.ts +1 -0
  119. package/dist/util/geometry/__tests__/vector3.spec.d.ts +1 -0
  120. package/dist/util/geometry/bounding-box.d.ts +4 -0
  121. package/dist/util/geometry/vector3.d.ts +2 -0
  122. package/dist/util/logging/__tests__/logger.spec.d.ts +1 -0
  123. package/dist/util/logging/logger.d.ts +8 -0
  124. package/dist/util/measurement/units.d.ts +57 -0
  125. package/dist/util/recoil/__tests__/actions.spec.d.ts +1 -0
  126. package/dist/util/recoil/actions.d.ts +86 -0
  127. package/dist/util/recoil/reducer.d.ts +9 -0
  128. package/dist/util/recoil/synchronized-prop.d.ts +2 -0
  129. package/dist/util/refs/callback-ref.d.ts +10 -0
  130. package/dist/util/refs/recoil-ref.d.ts +6 -0
  131. package/dist/util/scene-tree/customization.d.ts +3 -0
  132. package/dist/util/sdk-mapping/__tests__/config.spec.d.ts +1 -0
  133. package/dist/util/sdk-mapping/config.d.ts +2 -0
  134. package/package.json +80 -0
@@ -0,0 +1,2525 @@
1
+ 'use strict';
2
+
3
+ var jsxRuntime = require('react/jsx-runtime');
4
+ var React = require('react');
5
+ var uiReact = require('@vertexvis/ui-react');
6
+ var utils = require('@vertexvis/utils');
7
+ var recoil = require('recoil');
8
+ var geometry = require('@vertexvis/geometry');
9
+ var Pino = require('pino');
10
+ var viewerReact = require('@vertexvis/viewer-react');
11
+ var classNames = require('classnames');
12
+ var loader = require('@vertexvis/viewer/loader');
13
+
14
+ function _interopNamespaceDefault(e) {
15
+ var n = Object.create(null);
16
+ if (e) {
17
+ Object.keys(e).forEach(function (k) {
18
+ if (k !== 'default') {
19
+ var d = Object.getOwnPropertyDescriptor(e, k);
20
+ Object.defineProperty(n, k, d.get ? d : {
21
+ enumerable: true,
22
+ get: function () { return e[k]; }
23
+ });
24
+ }
25
+ });
26
+ }
27
+ n.default = e;
28
+ return Object.freeze(n);
29
+ }
30
+
31
+ var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
32
+
33
+ // DEPRECATED - avoid if possible
34
+ const useRecoilReducer = ({ reducer, atom, }) => {
35
+ const state = recoil.useRecoilValue(atom);
36
+ const dispatch = useRecoilReducerDispatch({
37
+ reducer,
38
+ atom,
39
+ });
40
+ return [state, dispatch];
41
+ };
42
+ // DEPRECATED - avoid if possible
43
+ const useRecoilReducerDispatch = ({ reducer, atom, }) => {
44
+ const dispatch = recoil.useRecoilCallback(({ set }) => async (action) => {
45
+ set(atom, (previousValue) => reducer(previousValue, action));
46
+ }, []);
47
+ return dispatch;
48
+ };
49
+
50
+ const isInputElement = (target) => {
51
+ const isAutoResizeTextarea = target instanceof HTMLElement &&
52
+ target.tagName === 'VERTEX-AUTO-RESIZE-TEXTAREA';
53
+ const isTextField = target instanceof HTMLElement && target.tagName === 'VERTEX-TEXTFIELD';
54
+ const isContentEditable = target instanceof HTMLElement && target.contentEditable === 'true';
55
+ const isSceneTreeSearch = target instanceof HTMLElement &&
56
+ target.tagName === 'VERTEX-SCENE-TREE-SEARCH';
57
+ const isInput = target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement;
58
+ const isVertexViewerPinTool = target instanceof HTMLElement &&
59
+ target.tagName === 'VERTEX-VIEWER-PIN-TOOL';
60
+ const isVertexViewerTransformWidget = target instanceof HTMLElement &&
61
+ target.tagName === 'VERTEX-VIEWER-TRANSFORM-WIDGET';
62
+ return (isAutoResizeTextarea ||
63
+ isTextField ||
64
+ isContentEditable ||
65
+ isSceneTreeSearch ||
66
+ isInput ||
67
+ isVertexViewerPinTool ||
68
+ isVertexViewerTransformWidget);
69
+ };
70
+
71
+ const keyBindings = recoil.atom({
72
+ key: 'keyBindings',
73
+ default: {
74
+ applyBindings: [],
75
+ bindings: {},
76
+ pressed: {},
77
+ },
78
+ });
79
+ function reducer(state, action) {
80
+ var _a, _b, _c, _d;
81
+ switch (action.type) {
82
+ case 'push-key-binding':
83
+ const toAdd = (_a = state.bindings[action.binding.keyBind]) === null || _a === void 0 ? void 0 : _a.find((b) => b.id === action.binding.id);
84
+ return toAdd != null
85
+ ? state
86
+ : Object.assign(Object.assign({}, state), { bindings: Object.assign(Object.assign({}, state.bindings), { [action.binding.keyBind]: [
87
+ ...((_b = state.bindings[action.binding.keyBind]) !== null && _b !== void 0 ? _b : []),
88
+ action.binding,
89
+ ] }) });
90
+ case 'remove-key-binding':
91
+ const toRemove = (_c = state.bindings[action.binding.keyBind]) === null || _c === void 0 ? void 0 : _c.find((b) => b.id === action.binding.id);
92
+ return toRemove == null
93
+ ? state
94
+ : Object.assign(Object.assign({}, state), { bindings: Object.assign(Object.assign({}, state.bindings), { [action.binding.keyBind]: ((_d = state.bindings[action.binding.keyBind]) !== null && _d !== void 0 ? _d : []).filter((b) => b.id !== action.binding.id) }) });
95
+ case 'add-apply-key-binding':
96
+ return Object.assign(Object.assign({}, state), { applyBindings: [...state.applyBindings, action.binding] });
97
+ case 'remove-apply-key-binding':
98
+ return Object.assign(Object.assign({}, state), { applyBindings: state.applyBindings.filter((binding) => binding.id !== action.id) });
99
+ case 'set-key-pressed':
100
+ return Object.assign(Object.assign({}, state), { applyBindings: state.applyBindings.map((binding) => (Object.assign(Object.assign({}, binding), { active: binding.keyBind != null &&
101
+ allPressed(binding.keyBind, state.pressed) }))), pressed: Object.assign(Object.assign({}, state.pressed), { [action.key]: action.pressed }), lastPressed: action.pressed ? action.key : undefined });
102
+ }
103
+ }
104
+ const useKeyBindingState = () => {
105
+ return useRecoilReducer({
106
+ reducer,
107
+ atom: keyBindings,
108
+ });
109
+ };
110
+ function pressedValue(keyBind, pressed) {
111
+ return keyBind.includes('!')
112
+ ? !pressed[keyBind.replace('!', '')] &&
113
+ !pressed[keyBind.replace('!', '').toLowerCase()]
114
+ : pressed[keyBind] || pressed[keyBind.toLowerCase()];
115
+ }
116
+ function isPressed(keyBind, pressed) {
117
+ const keys = keyBind.split('||');
118
+ return keys.length > 1
119
+ ? keys.some((key) => pressedValue(key, pressed))
120
+ : pressedValue(keys[0], pressed);
121
+ }
122
+ function allPressed(keyBind, pressed) {
123
+ const keys = keyBind.split('+');
124
+ return keys.every((key) => isPressed(key, pressed));
125
+ }
126
+ function matchingSingleFnBindings(applyBindings, pressed, lastPressed) {
127
+ return applyBindings
128
+ .filter((binding) => !binding.repeat)
129
+ .filter((binding) => {
130
+ var _a;
131
+ return binding.keyBind != null &&
132
+ lastPressed != null &&
133
+ ((_a = binding.keyBind) === null || _a === void 0 ? void 0 : _a.includes(lastPressed)) &&
134
+ allPressed(binding.keyBind, pressed);
135
+ })
136
+ .map((binding) => binding.fn);
137
+ }
138
+ function matchingOffBindings(applyBindings, pressed, lastPressed) {
139
+ return applyBindings
140
+ .filter((binding) => binding.off &&
141
+ binding.active &&
142
+ binding.keyBind != null &&
143
+ lastPressed == null &&
144
+ !allPressed(binding.keyBind, pressed))
145
+ .map((binding) => binding.off);
146
+ }
147
+ /**
148
+ * Depends on `useKeyBindings`.
149
+ *
150
+ * Adds a global key binding. Any key binding
151
+ * added this way will listen to global state for pressed
152
+ * keys, and executed whenever the key state matches the
153
+ * provided `keyBind`.
154
+ *
155
+ * @param binding - The `ApplyKeyBinding` to add.
156
+ */
157
+ const useApplyKeyBinding = (binding) => {
158
+ const [state, dispatch] = useKeyBindingState();
159
+ React.useEffect(() => {
160
+ const id = utils.UUID.create();
161
+ dispatch({
162
+ type: 'add-apply-key-binding',
163
+ binding: Object.assign(Object.assign({}, binding), { id, active: binding.keyBind != null && allPressed(binding.keyBind, state.pressed) }),
164
+ });
165
+ return () => {
166
+ dispatch({
167
+ type: 'remove-apply-key-binding',
168
+ id,
169
+ });
170
+ };
171
+ }, [binding.fn, binding.off, binding.keyBind, binding.repeat]);
172
+ };
173
+ /**
174
+ * Depends on `useKeyBindings`.
175
+ *
176
+ * Adds a stack-based key binding. Any key binding
177
+ * added this way will be added to an existing (or new)
178
+ * stack for the specified `keyBind`.
179
+ *
180
+ * Provided `binding`s will be added and cancelled based
181
+ * on the specified `addPredicate` and `cancelPredicate`
182
+ * respectively. These predicates are evaluated any time
183
+ * that the `deps` array changes.
184
+ *
185
+ * @param binding - The `StackKeyBinding` to add.
186
+ * @param deps (optional) - The React dependency list.
187
+ */
188
+ const useStackKeyBinding = (binding, deps = []) => {
189
+ const [, dispatch] = useKeyBindingState();
190
+ React.useEffect(() => {
191
+ const addPredicatePassing = binding.addPredicate == null || binding.addPredicate();
192
+ const cancelPredicatePassing = binding.cancelPredicate != null && binding.cancelPredicate();
193
+ if (addPredicatePassing) {
194
+ dispatch({
195
+ type: 'push-key-binding',
196
+ binding,
197
+ });
198
+ }
199
+ if (cancelPredicatePassing) {
200
+ dispatch({
201
+ type: 'remove-key-binding',
202
+ binding,
203
+ });
204
+ }
205
+ }, deps);
206
+ };
207
+ /**
208
+ * Enables key binding usage.
209
+ *
210
+ * This hook must appear in the tree for `useStackKeyBinding`
211
+ * or `useApplyKeyBinding` to work.
212
+ */
213
+ const useKeyBindings = ({ keydownRepeatInterval = 25, keydownIgnorePredicate = isInputElement, } = {}) => {
214
+ const [state, dispatch] = useKeyBindingState();
215
+ const applyStackKeyBindings = React.useCallback((key) => {
216
+ Object.keys(state.bindings)
217
+ .filter((k) => k.includes(key) && allPressed(k, Object.assign(Object.assign({}, state.pressed), { [key]: true })))
218
+ .forEach((k) => {
219
+ const bindings = state.bindings[k];
220
+ const poppedBinding = bindings.length > 0 ? bindings[bindings.length - 1] : undefined;
221
+ if (poppedBinding != null) {
222
+ poppedBinding.fn();
223
+ dispatch({
224
+ type: 'remove-key-binding',
225
+ binding: poppedBinding,
226
+ });
227
+ }
228
+ });
229
+ }, [state]);
230
+ React.useEffect(() => {
231
+ const handleKeyDown = (event) => {
232
+ if (!event.repeat && !keydownIgnorePredicate(event.target)) {
233
+ dispatch({
234
+ type: 'set-key-pressed',
235
+ key: event.key,
236
+ pressed: true,
237
+ });
238
+ applyStackKeyBindings(event.key);
239
+ // If the key is associated with a keybinding, then prevent default behavior
240
+ const currentPressed = Object.assign(Object.assign({}, state.pressed), { [event.key]: true });
241
+ const activeMatchesSingleFnBindings = matchingSingleFnBindings(state.applyBindings, currentPressed, event.key);
242
+ const activeMatchesOffBindings = matchingOffBindings(state.applyBindings, currentPressed, event.key);
243
+ if (activeMatchesSingleFnBindings.length > 0 ||
244
+ activeMatchesOffBindings.length > 0) {
245
+ event.preventDefault();
246
+ }
247
+ }
248
+ };
249
+ const handleKeyUp = (event) => {
250
+ dispatch({
251
+ type: 'set-key-pressed',
252
+ key: event.key,
253
+ pressed: false,
254
+ });
255
+ };
256
+ window.addEventListener('keydown', handleKeyDown);
257
+ window.addEventListener('keyup', handleKeyUp);
258
+ return () => {
259
+ window.removeEventListener('keydown', handleKeyDown);
260
+ window.removeEventListener('keyup', handleKeyUp);
261
+ };
262
+ }, [applyStackKeyBindings, state.applyBindings, state.pressed]);
263
+ React.useEffect(() => {
264
+ const activeMatchesSingleFnBindings = matchingSingleFnBindings(state.applyBindings, state.pressed, state.lastPressed);
265
+ const activeMatchesOffBindings = matchingOffBindings(state.applyBindings, state.pressed, state.lastPressed);
266
+ activeMatchesSingleFnBindings.forEach((fn) => fn());
267
+ activeMatchesOffBindings.forEach((fn) => fn());
268
+ }, [state.pressed, state.lastPressed]);
269
+ React.useEffect(() => {
270
+ const matchingRepeatBindings = state.applyBindings
271
+ .filter((binding) => binding.repeat &&
272
+ binding.keyBind != null &&
273
+ allPressed(binding.keyBind, state.pressed))
274
+ .map((binding) => binding.fn);
275
+ if (matchingRepeatBindings.length > 0) {
276
+ const interval = setInterval(() => {
277
+ matchingRepeatBindings.forEach((fn) => fn());
278
+ }, keydownRepeatInterval);
279
+ return () => {
280
+ clearInterval(interval);
281
+ };
282
+ }
283
+ else {
284
+ return () => {
285
+ return undefined;
286
+ };
287
+ }
288
+ }, [state.applyBindings, state.lastPressed]);
289
+ };
290
+
291
+ function useLongPress({ target, callback, delay = 500, onMovement, }) {
292
+ const [longPressTimeout, setLongPressTimeout] = React__namespace.useState();
293
+ const [touchStartPosition, setTouchStartPosition] = React__namespace.useState();
294
+ React__namespace.useEffect(() => {
295
+ const handleTouchStart = (e) => {
296
+ const event = e;
297
+ if (event.touches != null && event.touches.length === 1) {
298
+ if (longPressTimeout != null) {
299
+ clearTimeout(longPressTimeout);
300
+ }
301
+ setTouchStartPosition(geometry.Point.create(event.touches[0].clientX, event.touches[0].clientY));
302
+ setLongPressTimeout(setTimeout(() => callback === null || callback === void 0 ? void 0 : callback(event), delay));
303
+ }
304
+ };
305
+ const handleTouchMove = (e) => {
306
+ const event = e;
307
+ if (event.touches != null &&
308
+ event.touches.length > 0 &&
309
+ longPressTimeout != null &&
310
+ touchStartPosition != null &&
311
+ geometry.Point.distance(touchStartPosition, geometry.Point.create(event.touches[0].clientX, event.touches[0].clientY)) >= 2) {
312
+ clearTimeout(longPressTimeout);
313
+ onMovement === null || onMovement === void 0 ? void 0 : onMovement();
314
+ }
315
+ };
316
+ const handleTouchEnd = () => {
317
+ if (longPressTimeout != null) {
318
+ clearTimeout(longPressTimeout);
319
+ }
320
+ };
321
+ target === null || target === void 0 ? void 0 : target.addEventListener('touchstart', handleTouchStart);
322
+ target === null || target === void 0 ? void 0 : target.addEventListener('touchmove', handleTouchMove);
323
+ target === null || target === void 0 ? void 0 : target.addEventListener('touchend', handleTouchEnd);
324
+ return () => {
325
+ target === null || target === void 0 ? void 0 : target.removeEventListener('touchstart', handleTouchStart);
326
+ target === null || target === void 0 ? void 0 : target.removeEventListener('touchmove', handleTouchMove);
327
+ target === null || target === void 0 ? void 0 : target.removeEventListener('touchend', handleTouchEnd);
328
+ };
329
+ }, [target, longPressTimeout, touchStartPosition]);
330
+ }
331
+
332
+ function contextMenuItemIsRow(item) {
333
+ var _a;
334
+ return item != null && ((_a = item === null || item === void 0 ? void 0 : item.node) === null || _a === void 0 ? void 0 : _a.id) != null;
335
+ }
336
+ const contextMenuActive = recoil.atom({
337
+ key: 'contextMenuActive',
338
+ default: undefined,
339
+ });
340
+ const contextMenuPosition = recoil.atom({
341
+ key: 'contextMenuPosition',
342
+ default: undefined,
343
+ });
344
+ const contextMenuTarget = recoil.atom({
345
+ key: 'contextMenuTarget',
346
+ default: undefined,
347
+ });
348
+ const contextMenuItem = recoil.atom({
349
+ key: 'contextMenuItem',
350
+ default: undefined,
351
+ });
352
+ const contextMenuActions = recoil.atom({
353
+ key: 'contextMenuActions',
354
+ default: [],
355
+ });
356
+ const contextMenuActivePosition = recoil.selector({
357
+ key: 'contextMenuActivePosition',
358
+ get: ({ get }) => get(contextMenuActive) != null ? get(contextMenuPosition) : undefined,
359
+ });
360
+
361
+ /******************************************************************************
362
+ Copyright (c) Microsoft Corporation.
363
+
364
+ Permission to use, copy, modify, and/or distribute this software for any
365
+ purpose with or without fee is hereby granted.
366
+
367
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
368
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
369
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
370
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
371
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
372
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
373
+ PERFORMANCE OF THIS SOFTWARE.
374
+ ***************************************************************************** */
375
+ /* global Reflect, Promise, SuppressedError, Symbol, Iterator */
376
+
377
+
378
+ function __rest(s, e) {
379
+ var t = {};
380
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
381
+ t[p] = s[p];
382
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
383
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
384
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
385
+ t[p[i]] = s[p[i]];
386
+ }
387
+ return t;
388
+ }
389
+
390
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
391
+ var e = new Error(message);
392
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
393
+ };
394
+
395
+ // Create a new logger instance
396
+ const pinoLogger = Pino({
397
+ formatters: {
398
+ level(level) {
399
+ return { level };
400
+ },
401
+ },
402
+ });
403
+ const logger = {
404
+ error: (message, error) => {
405
+ if (error != null) {
406
+ pinoLogger.error(error, message);
407
+ }
408
+ else {
409
+ pinoLogger.error(message);
410
+ }
411
+ },
412
+ info: (message, obj) => {
413
+ if (obj != null) {
414
+ pinoLogger.info(obj, message);
415
+ }
416
+ else {
417
+ pinoLogger.info(message);
418
+ }
419
+ },
420
+ warn: (message, err) => {
421
+ if (err != null) {
422
+ pinoLogger.warn(err, message);
423
+ }
424
+ else {
425
+ pinoLogger.warn(message);
426
+ }
427
+ },
428
+ debug: (message, obj) => {
429
+ if (obj != null) {
430
+ pinoLogger.debug(obj, message);
431
+ }
432
+ else {
433
+ pinoLogger.debug(message);
434
+ }
435
+ },
436
+ };
437
+ /* eslint-enable @typescript-eslint/no-explicit-any */
438
+
439
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
440
+ class DefaultRecoilDeps {
441
+ }
442
+ /**
443
+ * Returns a Recoil callback that can be used for action hooks.
444
+ *
445
+ * This uses `useRecoilCallback` but the deps implementation differs from
446
+ * Recoil. If no deps are provided, then it will always return the same function
447
+ * vs returning a new function.
448
+ *
449
+ * If you want to use the original Recoil callback behavior, pass
450
+ * `DefaultRecoilCallbackDepsBehavior`.
451
+ *
452
+ * @param fn The callback.
453
+ * @param deps The deps to memoize over. Defaults to `[]`.
454
+ * @returns A callback function
455
+ * @see https://recoiljs.org/docs/api-reference/core/useRecoilCallback
456
+ */
457
+ function useActionCallback(fn, deps = []) {
458
+ const d = deps instanceof DefaultRecoilDeps ? undefined : deps;
459
+ // eslint-disable-next-line react-hooks/exhaustive-deps
460
+ return recoil.useRecoilCallback((cbInterface) => fn(Object.assign(Object.assign({}, cbInterface), { snapshot: Object.defineProperties(cbInterface.snapshot, {
461
+ getPromiseRequired: {
462
+ configurable: true,
463
+ value: createGetPromiseRequiredWrapper(cbInterface.snapshot),
464
+ },
465
+ tryGetPromise: {
466
+ configurable: true,
467
+ value: createTryGetPromiseWrapper(cbInterface.snapshot),
468
+ },
469
+ }) })), d);
470
+ }
471
+ /**
472
+ * Returns a callback that can be used with `useActionCallback` or `useRecoilCallback`,
473
+ * and automatically handles retention of the `snapshot` object for use with asynchronous
474
+ * selectors.
475
+ *
476
+ * This function expects a callback that would ordinarily be passed to `useActionCallback`
477
+ * or `useRecoilCallback`, but that returns an asynchronous action where the `snapshot` is
478
+ * used.
479
+ *
480
+ * @example
481
+ *
482
+ * const asyncSelector = selector({
483
+ * key: 'asyncSelector',
484
+ * get: async ({ get }) => {
485
+ * return await fetch('/api/item');
486
+ * }
487
+ * })
488
+ *
489
+ * useActionCallback(
490
+ * retainSnapshot(
491
+ * ({ snapshot }) => async () => {
492
+ * const asyncValue = await snapshot.getPromise(asyncSelector);
493
+ * }
494
+ * )
495
+ * )
496
+ *
497
+ * @param fn The callback.
498
+ * @returns The callback wrapped with a retain/release of the underlying snapshot.
499
+ */
500
+ function retainSnapshot(fn) {
501
+ return (_a) => {
502
+ var { snapshot } = _a, cb = __rest(_a, ["snapshot"]);
503
+ return async (...args) => {
504
+ const release = snapshot.retain();
505
+ try {
506
+ const returnValue = await fn(Object.assign(Object.assign({}, cb), { snapshot }))(...args);
507
+ return returnValue;
508
+ }
509
+ finally {
510
+ release();
511
+ }
512
+ };
513
+ };
514
+ }
515
+ function createGetPromiseRequiredWrapper(snapshot) {
516
+ return async (recoilValue, error) => {
517
+ const value = await snapshot.getPromise(recoilValue);
518
+ if (value == null) {
519
+ throw (error !== null && error !== void 0 ? error : new Error(`No value present for required value ${recoilValue.key}`));
520
+ }
521
+ return value;
522
+ };
523
+ }
524
+ function createTryGetPromiseWrapper(snapshot) {
525
+ return async (value) => {
526
+ try {
527
+ return { value: await snapshot.getPromise(value) };
528
+ }
529
+ catch (e) {
530
+ logger.debug(`Error encountered retrieving Recoil value. [key={${value.key}}]`, e);
531
+ return {
532
+ error: e,
533
+ };
534
+ }
535
+ };
536
+ }
537
+
538
+ const viewerElement = recoil.atom({
539
+ key: 'viewerToolkitViewerElement',
540
+ default: undefined,
541
+ dangerouslyAllowMutability: true,
542
+ });
543
+ const viewerInitialSceneReady = recoil.atom({
544
+ key: 'viewerInitialSceneReady',
545
+ default: false,
546
+ });
547
+ const viewerBaseInteractionHandlerProvider = recoil.atom({
548
+ key: 'viewerBaseInteractionHandlerProvider',
549
+ default: Promise.resolve(undefined),
550
+ dangerouslyAllowMutability: true,
551
+ });
552
+ const viewerPrimaryInteractionType = recoil.atom({
553
+ key: 'viewerPrimaryInteractionType',
554
+ default: 'rotate',
555
+ });
556
+
557
+ const viewerSceneProvider = recoil.selector({
558
+ key: 'viewerScene',
559
+ get: ({ get }) => async () => { var _a; return (_a = get(viewerElement)) === null || _a === void 0 ? void 0 : _a.scene(); },
560
+ dangerouslyAllowMutability: true,
561
+ });
562
+ const viewerFrameScene = recoil.atom({
563
+ key: 'viewerFrameScene',
564
+ default: undefined,
565
+ dangerouslyAllowMutability: true,
566
+ });
567
+ const viewerSceneVisibleBoundingBox = recoil.selector({
568
+ key: 'viewerSceneVisibleBoundingBox',
569
+ get: ({ get }) => { var _a; return (_a = get(viewerFrameScene)) === null || _a === void 0 ? void 0 : _a.boundingBox; },
570
+ });
571
+
572
+ function useViewerSceneActions() {
573
+ const executeWithCorrelationId = useActionCallback(({ snapshot }) => async (alterationType, correlationId, ...transforms) => {
574
+ const sceneProvider = await snapshot.getPromise(viewerSceneProvider);
575
+ const scene = await sceneProvider();
576
+ if (transforms.length > 0) {
577
+ // TODO: track performance
578
+ await (scene === null || scene === void 0 ? void 0 : scene.items((op) => transforms.map((t) => t(op))).execute({
579
+ suppliedCorrelationId: correlationId,
580
+ }));
581
+ }
582
+ });
583
+ const selectItemsTransform = React.useCallback((...ids) => (op) => op.where((q) => q.withItemIds(ids)).select(), []);
584
+ const deselectItemsTransform = React.useCallback((...ids) => (op) => op.where((q) => q.withItemIds(ids)).deselect(), []);
585
+ const clearSelectionTransform = React.useCallback((op) => op.where((q) => q.all()).deselect(), []);
586
+ return {
587
+ executeWithCorrelationId,
588
+ execute: useActionCallback(() => async (alterationType, ...transforms) => {
589
+ const correlationId = utils.UUID.create();
590
+ await executeWithCorrelationId(alterationType, correlationId, ...transforms);
591
+ }),
592
+ selectItemsTransform,
593
+ deselectItemsTransform,
594
+ clearSelectionTransform,
595
+ };
596
+ }
597
+
598
+ const DEFAULT_CAMERA_ANIMATION_DURATION = 500;
599
+ function useViewerCameraActions() {
600
+ return {
601
+ flyToById: useActionCallback(({ snapshot }) => async (id, animationMs) => {
602
+ const sceneProvider = await snapshot.getPromise(viewerSceneProvider);
603
+ const scene = await sceneProvider();
604
+ await (scene === null || scene === void 0 ? void 0 : scene.camera().flyTo({
605
+ itemId: id,
606
+ }).render({
607
+ animation: {
608
+ milliseconds: animationMs !== null && animationMs !== void 0 ? animationMs : DEFAULT_CAMERA_ANIMATION_DURATION,
609
+ },
610
+ }));
611
+ }),
612
+ flyToByBoundingBox: useActionCallback(({ snapshot }) => async (boundingBox, animationMs) => {
613
+ const sceneProvider = await snapshot.getPromise(viewerSceneProvider);
614
+ const scene = await sceneProvider();
615
+ await (scene === null || scene === void 0 ? void 0 : scene.camera().flyTo({
616
+ boundingBox,
617
+ }).render({
618
+ animation: {
619
+ milliseconds: animationMs !== null && animationMs !== void 0 ? animationMs : DEFAULT_CAMERA_ANIMATION_DURATION,
620
+ },
621
+ }));
622
+ }),
623
+ };
624
+ }
625
+
626
+ class CrossSectionInteractionHandler {
627
+ constructor() {
628
+ this.interactionStarted = false;
629
+ }
630
+ initialize(element, api) {
631
+ this.api = api;
632
+ }
633
+ dispose() {
634
+ this.endInteraction();
635
+ }
636
+ async beginInteraction() {
637
+ var _a;
638
+ if (!this.interactionStarted) {
639
+ await ((_a = this.api) === null || _a === void 0 ? void 0 : _a.beginInteraction());
640
+ this.interactionStarted = true;
641
+ }
642
+ }
643
+ async endInteraction() {
644
+ var _a;
645
+ await ((_a = this.api) === null || _a === void 0 ? void 0 : _a.endInteraction());
646
+ this.interactionStarted = false;
647
+ }
648
+ }
649
+
650
+ const create = (normal, offset = 0) => ({
651
+ normal,
652
+ offset,
653
+ });
654
+ const forAxis = (axis, offset) => {
655
+ switch (axis) {
656
+ case 'x':
657
+ return x(offset);
658
+ case 'y':
659
+ return y(offset);
660
+ case 'z':
661
+ return z(offset);
662
+ }
663
+ };
664
+ const x = (offset) => create(geometry.Vector3.left(), -offset);
665
+ const y = (offset) => create(geometry.Vector3.down(), -offset);
666
+ const z = (offset) => create(geometry.Vector3.forward(), -offset);
667
+ const getAxis = (axis, axes) => {
668
+ switch (axis) {
669
+ case 'x':
670
+ return xAxis(axes);
671
+ case 'y':
672
+ return yAxis(axes);
673
+ case 'z':
674
+ return zAxis(axes);
675
+ }
676
+ };
677
+ const defaultAxes = {
678
+ x: geometry.Vector3.left(),
679
+ y: geometry.Vector3.down(),
680
+ z: geometry.Vector3.forward(),
681
+ };
682
+ const xAxis = (axes) => axes.x;
683
+ const yAxis = (axes) => axes.y || geometry.Vector3.create(axes.x.z, axes.x.x, axes.x.y);
684
+ const zAxis = (axes) => axes.z || geometry.Vector3.create(axes.x.y, axes.x.z, axes.x.x);
685
+ /**
686
+ * Returns the active axis of a plane based on the normal values
687
+ */
688
+ const axis = (plane, xAxis, yAxis, zAxis) => plane &&
689
+ plane.normal &&
690
+ [
691
+ ['x', xAxis],
692
+ ['y', yAxis],
693
+ ['z', zAxis],
694
+ ]
695
+ .filter((a) => geometry.Vector3.magnitude(geometry.Vector3.cross(plane.normal, a[1])) === 0)
696
+ .map((a) => a[0])
697
+ .pop();
698
+ const isNegativeNormal = (plane) => geometry.Vector3.dot(geometry.Vector3.create(-1, -1, -1), plane.normal) > 0;
699
+ /**
700
+ * Returns an updated section plane where the normal of the section plane is
701
+ * faced towards the given vector. The effect is the internal of the cross
702
+ * section is visible from the given vector.
703
+ */
704
+ const towardsVector = (vector, plane) => {
705
+ const dot = geometry.Vector3.dot(vector, plane.normal);
706
+ if (dot < 0) {
707
+ return invert(plane);
708
+ }
709
+ else {
710
+ return plane;
711
+ }
712
+ };
713
+ /**
714
+ * Returns an updated section plane where the normal of the section plane is
715
+ * faced towards given camera's view vector. The effect is the user sees the
716
+ * internal of the cross section.
717
+ */
718
+ const towardsCamera = (camera, plane) => {
719
+ return towardsVector(camera.viewVector, plane);
720
+ };
721
+ /**
722
+ * Reverses the direction of the section plane's normal to point in the opposite
723
+ * direction.
724
+ */
725
+ const invert = (plane) => {
726
+ return Object.assign(Object.assign({}, plane), { normal: geometry.Vector3.multiply(plane.normal, geometry.Vector3.create(-1, -1, -1)), offset: -plane.offset });
727
+ };
728
+ const fromHit = (hit, camera) => {
729
+ const sectionNormal = geometry.Vector3.normalize(hit.normal);
730
+ const sectionOffset = geometry.Vector3.dot(sectionNormal, hit.position);
731
+ return towardsCamera(camera, create(sectionNormal, sectionOffset));
732
+ };
733
+ const toAxis = (axis, axes, camera, boundingBox, position) => {
734
+ const fitAllCamera = camera.viewAll();
735
+ const sectionAxis = getAxis(axis, axes);
736
+ const plane = boundingBox != null && sectionAxis != null
737
+ ? create(sectionAxis, geometry.Vector3.dot(sectionAxis, position || geometry.BoundingBox.center(boundingBox)))
738
+ : forAxis(axis, fitAllCamera.lookAt[axis]);
739
+ return towardsCamera(camera, plane);
740
+ };
741
+
742
+ function corners(box) {
743
+ return [
744
+ // xMin, yMin, zMin
745
+ geometry.Vector3.create(box.min.x, box.min.y, box.min.z),
746
+ // xMin, yMin, zMax
747
+ geometry.Vector3.create(box.min.x, box.min.y, box.max.z),
748
+ // xMin, yMax, zMin
749
+ geometry.Vector3.create(box.min.x, box.max.y, box.min.z),
750
+ // xMin, yMax, zMax
751
+ geometry.Vector3.create(box.min.x, box.max.y, box.max.z),
752
+ // xMax, yMax, zMax
753
+ geometry.Vector3.create(box.max.x, box.max.y, box.max.z),
754
+ // xMax, yMax, zMin
755
+ geometry.Vector3.create(box.max.x, box.max.y, box.min.z),
756
+ // xMax, yMin, zMax
757
+ geometry.Vector3.create(box.max.x, box.min.y, box.max.z),
758
+ // xMax, yMin, zMin
759
+ geometry.Vector3.create(box.max.x, box.min.y, box.min.z),
760
+ ];
761
+ }
762
+
763
+ const DEFAULT_SLIDER_RANGE = {
764
+ default: 5,
765
+ min: 0,
766
+ max: 10,
767
+ };
768
+ const crossSectioningEnabled = recoil.atom({
769
+ key: 'crossSectioningEnabled',
770
+ default: false,
771
+ });
772
+ const crossSectioningHighlightColor = recoil.atom({
773
+ key: 'crossSectioningHighlightColor',
774
+ default: undefined,
775
+ });
776
+ const crossSectioningBorderWidth = recoil.atom({
777
+ key: 'crossSectioningBorderWidth',
778
+ default: undefined,
779
+ });
780
+ const crossSectioningPlanes = recoil.atom({
781
+ key: 'crossSectioningPlanes',
782
+ default: [],
783
+ });
784
+ const crossSectioningAlignment = recoil.atom({
785
+ key: 'crossSectioningAlignment',
786
+ default: {
787
+ mode: 'global',
788
+ hitPending: false,
789
+ },
790
+ });
791
+ const crossSectioningTargetBoundingBox = recoil.selector({
792
+ key: 'crossSectioningTargetBoundingBox',
793
+ get: async ({ get }) => {
794
+ const visibleBounds = get(viewerSceneVisibleBoundingBox);
795
+ return visibleBounds;
796
+ },
797
+ });
798
+ const crossSectioningIsInteractive = recoil.atom({
799
+ key: 'crossSectioningIsInteractive',
800
+ default: false,
801
+ });
802
+ const crossSectioningActiveAxis = recoil.atom({
803
+ key: 'crossSectioningActiveAxis',
804
+ default: 'x',
805
+ });
806
+ const crossSectioningAvailableAxes = recoil.atom({
807
+ key: 'crossSectioningAvailableAxes',
808
+ default: defaultAxes,
809
+ });
810
+ const crossSectioningOffsetScalar = recoil.selector({
811
+ key: 'crossSectioningOffsetScalar',
812
+ get: ({ get }) => {
813
+ const planes = get(crossSectioningPlanes);
814
+ if (planes.length > 0) {
815
+ return isNegativeNormal(planes[0]) ? -1 : 1;
816
+ }
817
+ return 1;
818
+ },
819
+ });
820
+ const crossSectioningSliderRange = recoil.selector({
821
+ key: 'crossSectioningSliderRange',
822
+ get: ({ get }) => {
823
+ const planes = get(crossSectioningPlanes);
824
+ const bounds = get(crossSectioningTargetBoundingBox);
825
+ const boundsCorners = bounds != null ? corners(bounds) : [];
826
+ if (planes.length > 0) {
827
+ const projectedCorners = boundsCorners.map((c) => geometry.Vector3.dot(c, isNegativeNormal(planes[0])
828
+ ? geometry.Vector3.multiply(planes[0].normal, geometry.Vector3.create(-1, -1, -1))
829
+ : planes[0].normal));
830
+ return {
831
+ default: planes[0].offset,
832
+ min: Math.min(...projectedCorners) - 2,
833
+ max: Math.max(...projectedCorners) + 2,
834
+ };
835
+ }
836
+ return DEFAULT_SLIDER_RANGE;
837
+ },
838
+ });
839
+ const crossSectioningPlaneDisplayOffset = recoil.selector({
840
+ key: 'crossSectioningPlaneDisplayOffset',
841
+ get: ({ get }) => {
842
+ const planes = get(crossSectioningPlanes);
843
+ const offsetScalar = get(crossSectioningOffsetScalar);
844
+ const sliderRange = get(crossSectioningSliderRange);
845
+ if (planes.length > 0) {
846
+ return planes[0].offset * offsetScalar;
847
+ }
848
+ else {
849
+ return sliderRange.default;
850
+ }
851
+ },
852
+ });
853
+ const crossSectioningAdditionalToolsOpen = recoil.atom({
854
+ key: 'crossSectioningAdditionalToolsOpen',
855
+ default: false,
856
+ });
857
+ const crossSectioningAlignmentToolsOpen = recoil.atom({
858
+ key: 'crossSectioningAlignmentToolsOpen',
859
+ default: false,
860
+ });
861
+ const crossSectioningAxisToolsOpen = recoil.atom({
862
+ key: 'crossSectioningAxisToolsOpen',
863
+ default: false,
864
+ });
865
+ const crossSectioningInteractionHandler = recoil.atom({
866
+ key: 'crossSectioningInteractionHandler',
867
+ default: new CrossSectionInteractionHandler(),
868
+ dangerouslyAllowMutability: true,
869
+ });
870
+ function useCrossSectioning(viewerElement, initialized) {
871
+ const interactionHandler = recoil.useRecoilValue(crossSectioningInteractionHandler);
872
+ React.useEffect(() => {
873
+ if (initialized) {
874
+ viewerElement === null || viewerElement === void 0 ? void 0 : viewerElement.registerInteractionHandler(interactionHandler);
875
+ }
876
+ }, [viewerElement, initialized, interactionHandler]);
877
+ }
878
+
879
+ const hitResultsTapDetails = recoil.atom({
880
+ key: 'hitResultsTapDetails',
881
+ default: undefined,
882
+ });
883
+ const hitResultsLongPressDetails = recoil.atom({
884
+ key: 'hitResultsLongPressDetails',
885
+ default: undefined,
886
+ });
887
+ const previousHitResult = recoil.atom({
888
+ key: 'previousHitResult',
889
+ default: undefined,
890
+ });
891
+ const currentHitResult = recoil.atom({
892
+ key: 'currentHitResult',
893
+ default: undefined,
894
+ });
895
+ const hitResult = recoil.selector({
896
+ key: 'hitResult',
897
+ get: async ({ get }) => {
898
+ var _a, _b, _c, _d, _e, _f, _g, _h;
899
+ const tapDetails = get(hitResultsTapDetails);
900
+ const longPressDetails = get(hitResultsLongPressDetails);
901
+ const details = tapDetails !== null && tapDetails !== void 0 ? tapDetails : longPressDetails;
902
+ const sceneProvider = get(viewerSceneProvider);
903
+ const scene = await sceneProvider();
904
+ const viewId = scene === null || scene === void 0 ? void 0 : scene.sceneViewId;
905
+ const raycaster = scene === null || scene === void 0 ? void 0 : scene.raycaster();
906
+ if (details != null && raycaster != null && viewId != null) {
907
+ const res = await raycaster.hitItems(details.position);
908
+ const sdkHit = ((res === null || res === void 0 ? void 0 : res.hits) || [])[0];
909
+ if (sdkHit != null) {
910
+ return {
911
+ id: (_a = sdkHit.itemId) === null || _a === void 0 ? void 0 : _a.hex,
912
+ normal: geometry.Vector3.create(((_b = sdkHit === null || sdkHit === void 0 ? void 0 : sdkHit.hitNormal) === null || _b === void 0 ? void 0 : _b.x) || 0, ((_c = sdkHit === null || sdkHit === void 0 ? void 0 : sdkHit.hitNormal) === null || _c === void 0 ? void 0 : _c.y) || 0, ((_d = sdkHit === null || sdkHit === void 0 ? void 0 : sdkHit.hitNormal) === null || _d === void 0 ? void 0 : _d.z) || 0),
913
+ position: geometry.Vector3.create(((_e = sdkHit === null || sdkHit === void 0 ? void 0 : sdkHit.hitPoint) === null || _e === void 0 ? void 0 : _e.x) || 0, ((_f = sdkHit === null || sdkHit === void 0 ? void 0 : sdkHit.hitPoint) === null || _f === void 0 ? void 0 : _f.y) || 0, ((_g = sdkHit === null || sdkHit === void 0 ? void 0 : sdkHit.hitPoint) === null || _g === void 0 ? void 0 : _g.z) || 0),
914
+ meta: details.metaKey || details.ctrlKey || false,
915
+ metadata: [], // TODO: Include metadata types
916
+ ancestors: ((_h = sdkHit === null || sdkHit === void 0 ? void 0 : sdkHit.ancestors) === null || _h === void 0 ? void 0 : _h.filter((a) => { var _a; return ((_a = a.itemId) === null || _a === void 0 ? void 0 : _a.hex) != null; }).map((a) => { var _a; return (_a = a.itemId) === null || _a === void 0 ? void 0 : _a.hex; })) || [],
917
+ };
918
+ }
919
+ }
920
+ return undefined;
921
+ },
922
+ });
923
+ // Whether to skip the next `tap` event's behavior. This is typically the
924
+ // case when the `pointerdown` is associated to some other action.
925
+ // E.g. a context menu without a backdrop being dismissed.
926
+ const hitResultsSkipNextTap = recoil.atom({
927
+ key: 'hitResultsSkipNextTap',
928
+ default: false,
929
+ });
930
+
931
+ const alignToNormal = (camera, normal, boundingBox) => {
932
+ if (camera.toFrameCamera().isPerspective()) {
933
+ // when cos theta of the camera up vector and plane normal approaches |1|
934
+ // we will have unexpected results and need to find a better value for
935
+ // the new up vector.
936
+ // using negative value of cos0 in calculation for that up vector
937
+ // prevents flipping the model around the up vector
938
+ const cos0 = geometry.Vector3.dot(geometry.Vector3.normalize(camera.up), normal);
939
+ const up = Math.abs(cos0) > 0.98
940
+ ? geometry.Vector3.normalize(geometry.Vector3.scale(-cos0, camera.viewVector))
941
+ : camera.up;
942
+ const position = geometry.Vector3.add(geometry.Vector3.scale(geometry.Vector3.distance(camera.position, camera.lookAt), geometry.Vector3.multiply(normal, geometry.Vector3.create(-1, -1, -1))), camera.lookAt);
943
+ return camera.update({
944
+ up,
945
+ position,
946
+ });
947
+ }
948
+ else {
949
+ const angle = geometry.Vector3.angleTo(geometry.Vector3.normalize(camera.viewVector), normal);
950
+ const rotationPoint = boundingBox != null ? geometry.BoundingBox.center(boundingBox) : camera.lookAt;
951
+ const rotationAxis = geometry.Vector3.normalize(geometry.Vector3.cross(geometry.Vector3.normalize(camera.viewVector), normal));
952
+ return camera.rotateAroundAxisAtPoint(angle, rotationPoint, rotationAxis);
953
+ }
954
+ };
955
+
956
+ function isParallelTo(a, b) {
957
+ const angle = geometry.Angle.toDegrees(geometry.Vector3.angleTo(a, b));
958
+ return angle === 0 || angle === 180;
959
+ }
960
+
961
+ const DEFAULT_ALIGN_TO_PLANE_ANIMATION_DURATION_MS = 500;
962
+ function useCrossSectioningActions() {
963
+ const applyCrossSection = useActionCallback(({ snapshot }) => async () => {
964
+ const borderWidth = await snapshot.getPromise(crossSectioningBorderWidth);
965
+ const highlightColor = await snapshot.getPromise(crossSectioningHighlightColor);
966
+ const planes = await snapshot.getPromise(crossSectioningPlanes);
967
+ const sceneProvider = await snapshot.getPromise(viewerSceneProvider);
968
+ const scene = await sceneProvider();
969
+ await (scene === null || scene === void 0 ? void 0 : scene.crossSectioning().update({
970
+ sectionPlanes: planes,
971
+ highlightColor: highlightColor
972
+ ? utils.Color.fromHexString(highlightColor)
973
+ : undefined,
974
+ lineWidth: borderWidth,
975
+ }));
976
+ });
977
+ const applyCrossSectionGlobalDefault = useActionCallback(({ set, reset, snapshot }) => async () => {
978
+ const sceneProvider = await snapshot.getPromise(viewerSceneProvider);
979
+ const scene = await sceneProvider();
980
+ if (scene != null) {
981
+ const fitAllCamera = scene.camera().viewAll();
982
+ reset(crossSectioningAvailableAxes);
983
+ set(crossSectioningPlanes, [
984
+ towardsCamera(scene.camera(), x(fitAllCamera.lookAt.x)),
985
+ ]);
986
+ applyCrossSection();
987
+ }
988
+ });
989
+ const applyCrossSectionFromSceneHitItem = useActionCallback(retainSnapshot(({ snapshot, set }) => async () => {
990
+ const item = await snapshot.tryGetPromise(hitResult).then((r) => r.value);
991
+ const sceneProvider = await snapshot.getPromise(viewerSceneProvider);
992
+ const scene = await sceneProvider();
993
+ if (item != null && scene != null) {
994
+ const plane = fromHit(item, scene.camera());
995
+ set(crossSectioningPlanes, plane != null ? [plane] : []);
996
+ set(crossSectioningAvailableAxes, plane != null ? { x: plane.normal } : defaultAxes);
997
+ applyCrossSection();
998
+ }
999
+ }));
1000
+ const enable = useActionCallback(({ set, snapshot }) => async () => {
1001
+ const planes = await snapshot.getPromise(crossSectioningPlanes);
1002
+ const isEnabled = await snapshot.getPromise(crossSectioningEnabled);
1003
+ if (!isEnabled) {
1004
+ set(crossSectioningEnabled, true);
1005
+ if (planes.length === 0) {
1006
+ await applyCrossSectionGlobalDefault();
1007
+ }
1008
+ }
1009
+ });
1010
+ const disable = useActionCallback(({ reset, snapshot }) => async () => {
1011
+ const isEnabled = await snapshot.getPromise(crossSectioningEnabled);
1012
+ if (isEnabled) {
1013
+ reset(crossSectioningEnabled);
1014
+ reset(crossSectioningAdditionalToolsOpen);
1015
+ reset(crossSectioningAlignmentToolsOpen);
1016
+ reset(crossSectioningAxisToolsOpen);
1017
+ reset(crossSectioningPlanes);
1018
+ reset(crossSectioningAlignment);
1019
+ reset(crossSectioningActiveAxis);
1020
+ await applyCrossSection();
1021
+ }
1022
+ });
1023
+ return {
1024
+ enable,
1025
+ disable,
1026
+ openAdditionalTools: useActionCallback(({ set, snapshot }) => async () => {
1027
+ const isEnabled = await snapshot.getPromise(crossSectioningEnabled);
1028
+ if (!isEnabled) {
1029
+ enable();
1030
+ }
1031
+ set(crossSectioningAdditionalToolsOpen, true);
1032
+ }),
1033
+ closeAdditionalTools: useActionCallback(({ reset }) => async () => {
1034
+ reset(crossSectioningAdditionalToolsOpen);
1035
+ }),
1036
+ openAlignmentTools: useActionCallback(({ set, reset }) => async () => {
1037
+ reset(crossSectioningAxisToolsOpen);
1038
+ set(crossSectioningAlignmentToolsOpen, true);
1039
+ }),
1040
+ closeAlignmentTools: useActionCallback(({ reset }) => async () => {
1041
+ reset(crossSectioningAlignmentToolsOpen);
1042
+ }),
1043
+ openAxisTools: useActionCallback(({ set, reset }) => async () => {
1044
+ reset(crossSectioningAlignmentToolsOpen);
1045
+ set(crossSectioningAxisToolsOpen, true);
1046
+ }),
1047
+ closeAxisTools: useActionCallback(({ reset }) => async () => {
1048
+ reset(crossSectioningAxisToolsOpen);
1049
+ }),
1050
+ beginInteraction: useActionCallback(({ set, snapshot }) => async () => {
1051
+ const interactionHandler = await snapshot.getPromise(crossSectioningInteractionHandler);
1052
+ set(crossSectioningIsInteractive, true);
1053
+ await interactionHandler.beginInteraction();
1054
+ }),
1055
+ endInteraction: useActionCallback(({ reset, snapshot }) => async () => {
1056
+ const interactionHandler = await snapshot.getPromise(crossSectioningInteractionHandler);
1057
+ reset(crossSectioningIsInteractive);
1058
+ await interactionHandler.endInteraction();
1059
+ }),
1060
+ updatePlanes: useActionCallback(retainSnapshot(({ set, snapshot }) => async (offset) => {
1061
+ const existingPlanes = await snapshot.getPromise(crossSectioningPlanes);
1062
+ if (existingPlanes.length > 0) {
1063
+ const offsetScalar = await snapshot.getPromise(crossSectioningOffsetScalar);
1064
+ const range = await snapshot.getPromise(crossSectioningSliderRange);
1065
+ const minimum = offsetScalar < 0 ? range.max * -1 : range.min;
1066
+ const maximum = offsetScalar < 0 ? range.min * -1 : range.max;
1067
+ const updated = existingPlanes.map((plane) => (Object.assign(Object.assign({}, plane), { offset: Math.min(Math.max(offset * offsetScalar, minimum), maximum) })));
1068
+ set(crossSectioningPlanes, updated);
1069
+ applyCrossSection();
1070
+ }
1071
+ })),
1072
+ flipPlanes: useActionCallback(({ set, snapshot }) => async () => {
1073
+ const existingPlanes = await snapshot.getPromise(crossSectioningPlanes);
1074
+ set(crossSectioningPlanes, existingPlanes.map((plane) => invert(plane)));
1075
+ applyCrossSection();
1076
+ }),
1077
+ alignViewToPlane: useActionCallback(({ snapshot }) => async () => {
1078
+ const existingPlanes = await snapshot.getPromise(crossSectioningPlanes);
1079
+ const el = await snapshot.getPromise(viewerElement);
1080
+ const scene = await (el === null || el === void 0 ? void 0 : el.scene());
1081
+ if (existingPlanes.length > 0 && scene != null) {
1082
+ await alignToNormal(scene.camera(), existingPlanes[0].normal, scene.boundingBox()).render({
1083
+ animation: {
1084
+ milliseconds: DEFAULT_ALIGN_TO_PLANE_ANIMATION_DURATION_MS,
1085
+ },
1086
+ });
1087
+ }
1088
+ }),
1089
+ updateAxis: useActionCallback(retainSnapshot(({ set, reset, snapshot }) => async (axis) => {
1090
+ const existingPlanes = await snapshot.getPromise(crossSectioningPlanes);
1091
+ const availableAxes = await snapshot.getPromise(crossSectioningAvailableAxes);
1092
+ const targetBounds = await snapshot.getPromise(crossSectioningTargetBoundingBox);
1093
+ const alignment = await snapshot.getPromise(crossSectioningAlignment);
1094
+ const el = await snapshot.getPromise(viewerElement);
1095
+ const scene = await (el === null || el === void 0 ? void 0 : el.scene());
1096
+ const chosenAxis = availableAxes[axis];
1097
+ const angleIsParallelOrNaN = chosenAxis != null &&
1098
+ existingPlanes.length > 0 &&
1099
+ isParallelTo(existingPlanes[0].normal, chosenAxis) &&
1100
+ !isNaN(geometry.Vector3.angleTo(existingPlanes[0].normal, chosenAxis));
1101
+ if (scene != null && !angleIsParallelOrNaN) {
1102
+ const currentHitResult = await snapshot
1103
+ .tryGetPromise(hitResult)
1104
+ .then((r) => r.value);
1105
+ set(crossSectioningActiveAxis, axis);
1106
+ set(crossSectioningPlanes, [
1107
+ toAxis(axis, availableAxes, scene.camera(), targetBounds, alignment.mode !== 'global'
1108
+ ? currentHitResult === null || currentHitResult === void 0 ? void 0 : currentHitResult.position
1109
+ : undefined),
1110
+ ]);
1111
+ }
1112
+ reset(crossSectioningAxisToolsOpen);
1113
+ applyCrossSection();
1114
+ })),
1115
+ updateAlignment: useActionCallback(({ set, reset }) => async (alignment, hit) => {
1116
+ reset(crossSectioningAlignmentToolsOpen);
1117
+ set(crossSectioningAlignment, {
1118
+ mode: alignment,
1119
+ hitPending: alignment !== 'global' && hit == null,
1120
+ });
1121
+ if (alignment === 'global') {
1122
+ reset(crossSectioningActiveAxis);
1123
+ reset(crossSectioningAvailableAxes);
1124
+ applyCrossSectionGlobalDefault();
1125
+ }
1126
+ else if (alignment === 'surface' && hit != null) {
1127
+ await enable();
1128
+ applyCrossSectionFromSceneHitItem();
1129
+ }
1130
+ }),
1131
+ cancelCurrentHit: useActionCallback(({ set, snapshot }) => async () => {
1132
+ const alignment = await snapshot.getPromise(crossSectioningAlignment);
1133
+ if (alignment.hitPending) {
1134
+ set(crossSectioningAlignment, Object.assign(Object.assign({}, alignment), { hitPending: false }));
1135
+ }
1136
+ }),
1137
+ sectionCurrentHit: useActionCallback(({ set, snapshot }) => async () => {
1138
+ const alignment = await snapshot.getPromise(crossSectioningAlignment);
1139
+ if (alignment.hitPending) {
1140
+ set(crossSectioningAlignment, Object.assign(Object.assign({}, alignment), { hitPending: false }));
1141
+ if (alignment.mode === 'surface') {
1142
+ applyCrossSectionFromSceneHitItem();
1143
+ }
1144
+ }
1145
+ }),
1146
+ };
1147
+ }
1148
+
1149
+ var index$3 = /*#__PURE__*/Object.freeze({
1150
+ __proto__: null,
1151
+ DEFAULT_ALIGN_TO_PLANE_ANIMATION_DURATION_MS: DEFAULT_ALIGN_TO_PLANE_ANIMATION_DURATION_MS,
1152
+ DEFAULT_SLIDER_RANGE: DEFAULT_SLIDER_RANGE,
1153
+ crossSectioningActiveAxis: crossSectioningActiveAxis,
1154
+ crossSectioningAdditionalToolsOpen: crossSectioningAdditionalToolsOpen,
1155
+ crossSectioningAlignment: crossSectioningAlignment,
1156
+ crossSectioningAlignmentToolsOpen: crossSectioningAlignmentToolsOpen,
1157
+ crossSectioningAvailableAxes: crossSectioningAvailableAxes,
1158
+ crossSectioningAxisToolsOpen: crossSectioningAxisToolsOpen,
1159
+ crossSectioningBorderWidth: crossSectioningBorderWidth,
1160
+ crossSectioningEnabled: crossSectioningEnabled,
1161
+ crossSectioningHighlightColor: crossSectioningHighlightColor,
1162
+ crossSectioningInteractionHandler: crossSectioningInteractionHandler,
1163
+ crossSectioningIsInteractive: crossSectioningIsInteractive,
1164
+ crossSectioningOffsetScalar: crossSectioningOffsetScalar,
1165
+ crossSectioningPlaneDisplayOffset: crossSectioningPlaneDisplayOffset,
1166
+ crossSectioningPlanes: crossSectioningPlanes,
1167
+ crossSectioningSliderRange: crossSectioningSliderRange,
1168
+ crossSectioningTargetBoundingBox: crossSectioningTargetBoundingBox,
1169
+ useCrossSectioning: useCrossSectioning,
1170
+ useCrossSectioningActions: useCrossSectioningActions
1171
+ });
1172
+
1173
+ const useHitActions = () => {
1174
+ const crossSectioningActions = useCrossSectioningActions();
1175
+ const clearMeasurement = useActionCallback(retainSnapshot(({ snapshot }) => async () => {
1176
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1177
+ await snapshot
1178
+ .tryGetPromise(hitResult)
1179
+ .then((r) => r.value);
1180
+ // TODO: clear measurement
1181
+ }));
1182
+ const updateTransformWidgetPosition = useActionCallback(retainSnapshot(({ snapshot }) => async () => {
1183
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1184
+ await snapshot
1185
+ .tryGetPromise(hitResult)
1186
+ .then((r) => r.value);
1187
+ // TODO: update widget position
1188
+ }));
1189
+ const setContextMenuItem = useActionCallback(retainSnapshot(({ set, snapshot }) => async () => {
1190
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1191
+ const currentHitResult = await snapshot
1192
+ .tryGetPromise(hitResult)
1193
+ .then((r) => r.value);
1194
+ set(contextMenuItem, currentHitResult);
1195
+ }));
1196
+ const setCurrentHitResult = useActionCallback(retainSnapshot(({ snapshot, set }) => async () => {
1197
+ set(currentHitResult, await snapshot.tryGetPromise(hitResult).then((r) => r.value));
1198
+ }));
1199
+ return {
1200
+ tap: useActionCallback(retainSnapshot(({ snapshot, set }) => async ({ detail }, ...standardTapActions) => {
1201
+ const currentCrossSectioningAlignment = await snapshot.getPromise(crossSectioningAlignment);
1202
+ const isStandardTap = (detail === null || detail === void 0 ? void 0 : detail.buttons) !== 2 && !(detail === null || detail === void 0 ? void 0 : detail.altKey);
1203
+ detail.altKey ||
1204
+ detail.ctrlKey ||
1205
+ detail.metaKey ||
1206
+ detail.shiftKey;
1207
+ const isValidSelectionTap = isStandardTap && !currentCrossSectioningAlignment.hitPending;
1208
+ const currentHitResult = await snapshot
1209
+ .tryGetPromise(hitResult)
1210
+ .then((r) => r.value);
1211
+ set(previousHitResult, currentHitResult);
1212
+ set(hitResultsTapDetails, detail);
1213
+ clearMeasurement();
1214
+ if (isValidSelectionTap) {
1215
+ standardTapActions.forEach((action) => action());
1216
+ {
1217
+ updateTransformWidgetPosition();
1218
+ }
1219
+ }
1220
+ setContextMenuItem();
1221
+ crossSectioningActions.sectionCurrentHit();
1222
+ setCurrentHitResult();
1223
+ })),
1224
+ longPress: useActionCallback(retainSnapshot(({ snapshot, set, reset }) => async (event) => {
1225
+ reset(hitResultsTapDetails);
1226
+ set(hitResultsLongPressDetails, event.detail);
1227
+ await snapshot.tryGetPromise(hitResult);
1228
+ setContextMenuItem();
1229
+ setCurrentHitResult();
1230
+ })),
1231
+ };
1232
+ };
1233
+
1234
+ const hitState = recoil.selector({
1235
+ key: 'viewerToolkitHitState',
1236
+ get: ({ get }) => {
1237
+ const tapDetails = get(hitResultsTapDetails);
1238
+ const longPressDetails = get(hitResultsLongPressDetails);
1239
+ const previous = get(previousHitResult);
1240
+ const current = get(currentHitResult);
1241
+ const state = {
1242
+ tapDetails,
1243
+ longPressDetails,
1244
+ previousHitResult: previous,
1245
+ currentHitResult: current,
1246
+ };
1247
+ return state;
1248
+ },
1249
+ });
1250
+
1251
+ var index$2 = /*#__PURE__*/Object.freeze({
1252
+ __proto__: null,
1253
+ currentHitResult: currentHitResult,
1254
+ hitResult: hitResult,
1255
+ hitResultsLongPressDetails: hitResultsLongPressDetails,
1256
+ hitResultsSkipNextTap: hitResultsSkipNextTap,
1257
+ hitResultsTapDetails: hitResultsTapDetails,
1258
+ hitState: hitState,
1259
+ previousHitResult: previousHitResult,
1260
+ useHitActions: useHitActions
1261
+ });
1262
+
1263
+ function useContextMenuActions() {
1264
+ return {
1265
+ pointerDown: useActionCallback(({ set }) => async (event, pointOverride) => {
1266
+ var _a, _b;
1267
+ if (event.button === 2) {
1268
+ const xCoordinate = (_a = pointOverride === null || pointOverride === void 0 ? void 0 : pointOverride.x) !== null && _a !== void 0 ? _a : event.clientX;
1269
+ const yCoordinate = (_b = pointOverride === null || pointOverride === void 0 ? void 0 : pointOverride.y) !== null && _b !== void 0 ? _b : event === null || event === void 0 ? void 0 : event.clientY;
1270
+ set(contextMenuPosition, geometry.Point.create(xCoordinate, yCoordinate));
1271
+ set(contextMenuTarget, event.target);
1272
+ }
1273
+ }),
1274
+ pointerUp: useActionCallback(({ snapshot, set }) => async (event, type, predicate, onOpen, pointOverride) => {
1275
+ var _a, _b;
1276
+ const downPosition = await snapshot.getPromise(contextMenuPosition);
1277
+ const active = await snapshot.getPromise(contextMenuActive);
1278
+ if (downPosition != null && active == null) {
1279
+ const xCoordinate = (_a = pointOverride === null || pointOverride === void 0 ? void 0 : pointOverride.x) !== null && _a !== void 0 ? _a : event.clientX;
1280
+ const yCoordinate = (_b = pointOverride === null || pointOverride === void 0 ? void 0 : pointOverride.y) !== null && _b !== void 0 ? _b : event === null || event === void 0 ? void 0 : event.clientY;
1281
+ const point = geometry.Point.create(xCoordinate, yCoordinate);
1282
+ const pointDistance = downPosition != null ? geometry.Point.distance(downPosition, point) : 0;
1283
+ const predicateResult = predicate == null || predicate(event);
1284
+ if (pointDistance < 2 && predicateResult) {
1285
+ onOpen === null || onOpen === void 0 ? void 0 : onOpen(event, downPosition !== null && downPosition !== void 0 ? downPosition : point);
1286
+ set(contextMenuActive, type);
1287
+ }
1288
+ }
1289
+ }),
1290
+ contextMenu: useActionCallback(() => (event, predicate) => {
1291
+ if (predicate == null || predicate(event)) {
1292
+ event.preventDefault();
1293
+ }
1294
+ }),
1295
+ longPress: useActionCallback(({ set }) => (event, type, predicate, onOpen) => {
1296
+ if (predicate == null || predicate(event)) {
1297
+ const point = geometry.Point.create(event.touches[0].clientX, event.touches[0].clientY);
1298
+ set(contextMenuPosition, point);
1299
+ set(contextMenuTarget, event.target);
1300
+ set(contextMenuActive, type);
1301
+ onOpen === null || onOpen === void 0 ? void 0 : onOpen(event, point);
1302
+ }
1303
+ }),
1304
+ clearActiveContextMenu: useActionCallback(({ reset, set }) => (dismissedFromWindowPointerEvent) => {
1305
+ set(hitResultsSkipNextTap, !!dismissedFromWindowPointerEvent);
1306
+ reset(contextMenuActive);
1307
+ reset(contextMenuActions);
1308
+ reset(contextMenuPosition);
1309
+ }),
1310
+ clearDismissedState: useActionCallback(({ reset }) => () => reset(hitResultsSkipNextTap)),
1311
+ };
1312
+ }
1313
+
1314
+ function targetWithinMenu(event) {
1315
+ return (event.target instanceof Element && isChildOf('vertex-menu', event.target));
1316
+ }
1317
+ function targetShouldSkipNextHit(event) {
1318
+ return (event.target instanceof Element && isChildOf('vertex-viewer', event.target));
1319
+ }
1320
+ function isChildOf(elementType, target) {
1321
+ return target.closest(elementType) != null;
1322
+ }
1323
+
1324
+ const VertexContextMenu = ({ targetElement, menuType, disableBackdrop, openPredicate, onOpen, onClose, children, }) => {
1325
+ const contextMenuActions = useContextMenuActions();
1326
+ const activeContextMenu = recoil.useRecoilValue(contextMenuActive);
1327
+ const activePosition = recoil.useRecoilValue(contextMenuActivePosition);
1328
+ const fallbackPlacements = React.useMemo(() => ['bottom-end', 'top-start', 'top-end', 'right', 'left'], []);
1329
+ useStackKeyBinding({
1330
+ id: 'ContextMenu',
1331
+ keyBind: 'Escape',
1332
+ fn: contextMenuActions.clearActiveContextMenu,
1333
+ addPredicate: () => activeContextMenu != null && activeContextMenu === menuType,
1334
+ cancelPredicate: () => activeContextMenu == null,
1335
+ }, [activeContextMenu]);
1336
+ const target = React.useMemo(() => {
1337
+ if (targetElement != null) {
1338
+ return targetElement;
1339
+ }
1340
+ else if (openPredicate != null) {
1341
+ return window;
1342
+ }
1343
+ return undefined;
1344
+ }, [targetElement, openPredicate]);
1345
+ useLongPress({
1346
+ target,
1347
+ callback: (e) => contextMenuActions.longPress(e, menuType, openPredicate, onOpen),
1348
+ onMovement: () => contextMenuActions.clearActiveContextMenu(),
1349
+ });
1350
+ React.useEffect(() => {
1351
+ const handleWindowPointerDown = (event) => {
1352
+ const withinMenu = targetWithinMenu(event);
1353
+ if (!withinMenu &&
1354
+ event.buttons !== 2 &&
1355
+ activeContextMenu === menuType) {
1356
+ const skipNextHit = targetShouldSkipNextHit(event);
1357
+ contextMenuActions.clearActiveContextMenu(skipNextHit);
1358
+ }
1359
+ };
1360
+ if (disableBackdrop) {
1361
+ window.addEventListener('pointerdown', handleWindowPointerDown);
1362
+ return () => {
1363
+ window.removeEventListener('pointerdown', handleWindowPointerDown);
1364
+ };
1365
+ }
1366
+ return undefined;
1367
+ }, [disableBackdrop, menuType, activeContextMenu]);
1368
+ React.useEffect(() => {
1369
+ const handlePointerDown = (event) => {
1370
+ var _a, _b;
1371
+ const pointerEvent = event;
1372
+ const targetElement = target;
1373
+ const boundingBox = targetElement != null ? targetElement.getBoundingClientRect() : undefined;
1374
+ const xCoordinate = pointerEvent.clientX - ((_a = boundingBox === null || boundingBox === void 0 ? void 0 : boundingBox.x) !== null && _a !== void 0 ? _a : 0);
1375
+ const yCoordinate = pointerEvent.clientY - ((_b = boundingBox === null || boundingBox === void 0 ? void 0 : boundingBox.y) !== null && _b !== void 0 ? _b : 0);
1376
+ contextMenuActions.pointerDown(pointerEvent, geometry.Point.create(xCoordinate, yCoordinate));
1377
+ };
1378
+ const handlePointerUp = (event) => {
1379
+ var _a, _b;
1380
+ const pointerEvent = event;
1381
+ const targetElement = target;
1382
+ const boundingBox = targetElement != null ? targetElement.getBoundingClientRect() : undefined;
1383
+ const xCoordinate = pointerEvent.clientX - ((_a = boundingBox === null || boundingBox === void 0 ? void 0 : boundingBox.x) !== null && _a !== void 0 ? _a : 0);
1384
+ const yCoordinate = pointerEvent.clientY - ((_b = boundingBox === null || boundingBox === void 0 ? void 0 : boundingBox.y) !== null && _b !== void 0 ? _b : 0);
1385
+ contextMenuActions.pointerUp(event, menuType, openPredicate, onOpen, geometry.Point.create(xCoordinate, yCoordinate));
1386
+ };
1387
+ const handleContextMenu = (event) => contextMenuActions.contextMenu(event, openPredicate);
1388
+ target === null || target === void 0 ? void 0 : target.addEventListener('pointerdown', handlePointerDown);
1389
+ target === null || target === void 0 ? void 0 : target.addEventListener('pointerup', handlePointerUp);
1390
+ target === null || target === void 0 ? void 0 : target.addEventListener('contextmenu', handleContextMenu);
1391
+ return () => {
1392
+ target === null || target === void 0 ? void 0 : target.removeEventListener('pointerdown', handlePointerDown);
1393
+ target === null || target === void 0 ? void 0 : target.removeEventListener('pointerup', handlePointerUp);
1394
+ target === null || target === void 0 ? void 0 : target.removeEventListener('contextmenu', handleContextMenu);
1395
+ };
1396
+ }, [targetElement, menuType, openPredicate]);
1397
+ return (jsxRuntime.jsx(uiReact.VertexMenu, { "data-testid": `${menuType}-context-menu`, open: activeContextMenu === menuType, position: activePosition, fallbackPlacements: fallbackPlacements, backdrop: !disableBackdrop, onMenuClosed: () => {
1398
+ contextMenuActions.clearActiveContextMenu();
1399
+ onClose === null || onClose === void 0 ? void 0 : onClose();
1400
+ }, children: jsxRuntime.jsx("div", { className: "pt-1", children: children }) }));
1401
+ };
1402
+
1403
+ var SceneAlterationPerformanceType;
1404
+ (function (SceneAlterationPerformanceType) {
1405
+ SceneAlterationPerformanceType["HIDE_ITEM"] = "Hide Item";
1406
+ SceneAlterationPerformanceType["SHOW_ONLY_SELECTED"] = "Show Only Selected";
1407
+ SceneAlterationPerformanceType["HIDE_SELECTED"] = "Hide Selected";
1408
+ SceneAlterationPerformanceType["SHOW_ONLY_ITEM"] = "Show Only Item";
1409
+ SceneAlterationPerformanceType["SHOW_ALL"] = "Show All";
1410
+ SceneAlterationPerformanceType["HIDE_ALL"] = "Hide All";
1411
+ SceneAlterationPerformanceType["SELECT_FILTERED_ITEMS"] = "Select Filtered Items";
1412
+ SceneAlterationPerformanceType["INVERT_SELECTION"] = "Invert Selection";
1413
+ SceneAlterationPerformanceType["CLEAR_ALL_TRANSFORMS"] = "Clear All Transforms";
1414
+ SceneAlterationPerformanceType["CLEAR_SELECTED_TRANSFORMS"] = "Clear Selected Transforms";
1415
+ SceneAlterationPerformanceType["CLEAR_SELECTION"] = "Clear Selection";
1416
+ SceneAlterationPerformanceType["DESELECTING_ITEMS"] = "Deselecting Items";
1417
+ SceneAlterationPerformanceType["SELECTING_ITEMS"] = "Selecting Items";
1418
+ SceneAlterationPerformanceType["SELECTING_ONLY_ITEM"] = "Selecting Only Item";
1419
+ SceneAlterationPerformanceType["SELECTING_ANCESTOR"] = "Selecting Ancestor";
1420
+ SceneAlterationPerformanceType["APPLY_MATERIAL_OVERRIDE_TO_ALL"] = "Apply Material Override To All";
1421
+ SceneAlterationPerformanceType["APPLY_MATERIAL_OVERRIDE_TO_SELECTION"] = "Apply Material Override To Selection";
1422
+ SceneAlterationPerformanceType["CLEAR_ALL_MATERIAL_OVERRIDES"] = "Clear All Material Overrides";
1423
+ SceneAlterationPerformanceType["CLEAR_SELECTED_MATERIAL_OVERRIDES"] = "Clear Selected Material Overrides";
1424
+ // Phantom operations
1425
+ SceneAlterationPerformanceType["SET_PHANTOM_ALL"] = "Set Phantom All";
1426
+ SceneAlterationPerformanceType["SET_PHANTOM_SELECTED_ITEMS"] = "Set Phantom Selected Items";
1427
+ SceneAlterationPerformanceType["CLEAR_PHANTOM_ALL"] = "Clear Phantom All";
1428
+ SceneAlterationPerformanceType["CLEAR_PHANTOM_SELECTED_ITEMS"] = "Clear Phantom Selected Items";
1429
+ SceneAlterationPerformanceType["SET_PHANTOM_NON_SELECTED_ITEMS"] = "Set Phantom Non Selected Items";
1430
+ SceneAlterationPerformanceType["RESTORE_PHANTOM_OVERRIDES"] = "Restore Phantom Overrides";
1431
+ // End item operations
1432
+ SceneAlterationPerformanceType["SET_ITEM_AS_END_ITEM"] = "Set Item as End Item";
1433
+ SceneAlterationPerformanceType["UNSET_ITEM_AS_END_ITEM"] = "Unset Item as End Item";
1434
+ // PMI annotation operations
1435
+ SceneAlterationPerformanceType["SHOW_ANNOTATION"] = "Show Annotation";
1436
+ SceneAlterationPerformanceType["HIDE_ANNOTATION"] = "Hide Annotation";
1437
+ })(SceneAlterationPerformanceType || (SceneAlterationPerformanceType = {}));
1438
+
1439
+ const selectionSelectedItems = recoil.atomFamily({
1440
+ key: 'selectionSelectedItems',
1441
+ default: undefined,
1442
+ });
1443
+ const selectionSelectedItemIds = recoil.atom({
1444
+ key: 'selectionSelectedItemIds',
1445
+ default: [],
1446
+ });
1447
+ const selectionLastSelected = recoil.atom({
1448
+ key: 'selectionLastSelected',
1449
+ default: undefined,
1450
+ });
1451
+ const selectionLastSelectionFromViewer = recoil.atom({
1452
+ key: 'selectionLastSelectionFromViewer',
1453
+ default: false,
1454
+ });
1455
+ const selectionLastSelectWasMultiSelect = recoil.atom({
1456
+ key: 'selectionLastSelectWasMultiSelect',
1457
+ default: false,
1458
+ });
1459
+ const selectionHighestSelectedAncestor = recoil.selector({
1460
+ key: 'selectionHighestSelectedAncestor',
1461
+ get: ({ get }) => {
1462
+ var _a, _b;
1463
+ const lastSelected = get(selectionLastSelected);
1464
+ const selectedIds = get(selectionSelectedItemIds);
1465
+ return ((_b = (_a = lastSelected === null || lastSelected === void 0 ? void 0 : lastSelected.ancestors) === null || _a === void 0 ? void 0 : _a.find((ancestorId) => selectedIds.includes(ancestorId))) !== null && _b !== void 0 ? _b : lastSelected === null || lastSelected === void 0 ? void 0 : lastSelected.id);
1466
+ },
1467
+ });
1468
+ const selectionPreviousVisibleSummary = recoil.atom({
1469
+ key: 'selectionPreviousVisibleSummary',
1470
+ default: undefined,
1471
+ });
1472
+ const selectionVisibleSummary = recoil.atom({
1473
+ key: 'selectionVisibleSummary',
1474
+ default: undefined,
1475
+ });
1476
+ const selectionVisibleCount = recoil.selector({
1477
+ key: 'selectionVisibleCount',
1478
+ get: ({ get }) => { var _a, _b; return (_b = (_a = get(selectionVisibleSummary)) === null || _a === void 0 ? void 0 : _a.count) !== null && _b !== void 0 ? _b : 0; },
1479
+ });
1480
+
1481
+ function useSelectionActions() {
1482
+ const viewerActions = useViewerSceneActions();
1483
+ const clearSelectedIds = useActionCallback(({ snapshot, reset }) => async () => {
1484
+ const selectedIds = await snapshot.getPromise(selectionSelectedItemIds);
1485
+ selectedIds.forEach((id) => reset(selectionSelectedItems(id)));
1486
+ });
1487
+ const select = useActionCallback(({ snapshot, reset, set }) => async (item, options) => {
1488
+ var _a, _b;
1489
+ const existing = await snapshot.getPromise(selectionSelectedItems(item.id));
1490
+ const selectedIds = await snapshot.getPromise(selectionSelectedItemIds);
1491
+ const nextAncestorId = (_b = [...((_a = item.ancestors) !== null && _a !== void 0 ? _a : [])]) === null || _b === void 0 ? void 0 : _b.reverse().find((id) => !selectedIds.includes(id));
1492
+ if (existing != null && nextAncestorId != null) {
1493
+ // If already selected and has a parent, select the parent
1494
+ set(selectionSelectedItems(nextAncestorId), { id: nextAncestorId });
1495
+ set(selectionSelectedItemIds, (previous) => [
1496
+ ...previous,
1497
+ nextAncestorId,
1498
+ ]);
1499
+ viewerActions.execute(SceneAlterationPerformanceType.SELECTING_ANCESTOR, viewerActions.selectItemsTransform(nextAncestorId));
1500
+ }
1501
+ else if (options === null || options === void 0 ? void 0 : options.clear) {
1502
+ // Select only the current item since options.clear is true
1503
+ set(selectionSelectedItems(item.id), item);
1504
+ set(selectionSelectedItemIds, [item.id]);
1505
+ viewerActions.execute(SceneAlterationPerformanceType.SELECTING_ONLY_ITEM, viewerActions.clearSelectionTransform, viewerActions.selectItemsTransform(item.id));
1506
+ }
1507
+ else {
1508
+ // Add item to current selection without clearing previous selection
1509
+ set(selectionSelectedItems(item.id), item);
1510
+ set(selectionSelectedItemIds, (previous) => [...previous, item.id]);
1511
+ viewerActions.execute(SceneAlterationPerformanceType.SELECTING_ITEMS, viewerActions.selectItemsTransform(item.id));
1512
+ }
1513
+ selectForModelViewsPanel(item);
1514
+ set(selectionLastSelected, item);
1515
+ set(selectionLastSelectionFromViewer, true);
1516
+ reset(selectionLastSelectWasMultiSelect);
1517
+ });
1518
+ const deselect = useActionCallback(({ reset, set }) => async (deselectingId) => {
1519
+ reset(selectionSelectedItems(deselectingId));
1520
+ reset(selectionLastSelectWasMultiSelect);
1521
+ set(selectionSelectedItemIds, (previous) => previous.filter((id) => id !== deselectingId));
1522
+ viewerActions.execute(SceneAlterationPerformanceType.DESELECTING_ITEMS, viewerActions.deselectItemsTransform(deselectingId));
1523
+ });
1524
+ const toggleSelection = useActionCallback(({ snapshot }) => async (item) => {
1525
+ const existing = await snapshot.getPromise(selectionSelectedItems(item.id));
1526
+ if (existing != null) {
1527
+ await deselect(existing.id);
1528
+ }
1529
+ else {
1530
+ await select(item);
1531
+ }
1532
+ });
1533
+ const clearAndSelect = useActionCallback(({ snapshot, reset, set }) => async (item, options = {}) => {
1534
+ const existing = await snapshot.getPromise(selectionSelectedItems(item.id));
1535
+ const selectedIds = await snapshot.getPromise(selectionSelectedItemIds);
1536
+ selectedIds
1537
+ .filter((id) => { var _a; return id !== item.id && !((_a = item.ancestors) === null || _a === void 0 ? void 0 : _a.includes(id)); })
1538
+ .forEach((id) => reset(selectionSelectedItems(id)));
1539
+ reset(selectionLastSelectWasMultiSelect);
1540
+ if (!existing) {
1541
+ reset(selectionSelectedItemIds);
1542
+ }
1543
+ else if (options.ignoreAncestorSelection) {
1544
+ reset(selectionSelectedItemIds);
1545
+ reset(selectionSelectedItems(existing.id));
1546
+ }
1547
+ else {
1548
+ const selectedItemAndAncestors = selectedIds.filter((id) => { var _a; return id === item.id || ((_a = item.ancestors) === null || _a === void 0 ? void 0 : _a.includes(id)); });
1549
+ set(selectionSelectedItemIds, selectedItemAndAncestors);
1550
+ }
1551
+ // TODO: clear PMI + transform manipulator orientation
1552
+ await select(item, { clear: true });
1553
+ });
1554
+ const resetSelectionState = useActionCallback(({ reset }) => async () => {
1555
+ await clearSelectedIds();
1556
+ reset(selectionSelectedItemIds);
1557
+ reset(selectionLastSelected);
1558
+ reset(selectionLastSelectionFromViewer);
1559
+ reset(selectionLastSelectWasMultiSelect);
1560
+ // TODO: clear transform manipulator state
1561
+ });
1562
+ const clearSelection = useActionCallback(() => async () => {
1563
+ await resetSelectionState();
1564
+ viewerActions.execute(SceneAlterationPerformanceType.CLEAR_SELECTION, viewerActions.clearSelectionTransform);
1565
+ });
1566
+ const toggleLastSelectWasMultiSelect = useActionCallback(({ set }) => async (multiSelection) => {
1567
+ set(selectionLastSelectWasMultiSelect, multiSelection);
1568
+ });
1569
+ const selectForModelViewsPanel = useActionCallback(({ reset, set }) => (item) => {
1570
+ // TODO: clear loaded PMI
1571
+ });
1572
+ return {
1573
+ select,
1574
+ toggleSelection,
1575
+ clearAndSelect,
1576
+ selectCurrentHit: useActionCallback(retainSnapshot(({ snapshot }) => async () => {
1577
+ const currentHitResult = await snapshot
1578
+ .tryGetPromise(hitResult)
1579
+ .then((r) => r.value);
1580
+ const currentTapDetails = await snapshot.getPromise(hitResultsTapDetails);
1581
+ const appendModifierPressed = (currentTapDetails === null || currentTapDetails === void 0 ? void 0 : currentTapDetails.metaKey) ||
1582
+ (currentTapDetails === null || currentTapDetails === void 0 ? void 0 : currentTapDetails.ctrlKey) ||
1583
+ (currentTapDetails === null || currentTapDetails === void 0 ? void 0 : currentTapDetails.shiftKey);
1584
+ if (currentHitResult != null && !appendModifierPressed) {
1585
+ clearAndSelect(currentHitResult);
1586
+ }
1587
+ else if (currentHitResult != null && appendModifierPressed) {
1588
+ toggleSelection(currentHitResult);
1589
+ }
1590
+ else {
1591
+ clearSelection();
1592
+ }
1593
+ })),
1594
+ syncSelection: useActionCallback(({ set, reset }) => async (item, options) => {
1595
+ if (options.selected) {
1596
+ await clearSelectedIds();
1597
+ set(selectionSelectedItems(item.id), item);
1598
+ if (options.multiSelect) {
1599
+ set(selectionSelectedItemIds, (previous) => [
1600
+ ...previous.filter((id) => id !== item.id),
1601
+ item.id,
1602
+ ]);
1603
+ }
1604
+ else {
1605
+ set(selectionSelectedItemIds, [item.id]);
1606
+ }
1607
+ selectForModelViewsPanel(item);
1608
+ set(selectionLastSelected, item);
1609
+ set(selectionLastSelectionFromViewer, false);
1610
+ set(selectionLastSelectWasMultiSelect, !!options.multiSelect);
1611
+ }
1612
+ else {
1613
+ reset(selectionSelectedItems(item.id));
1614
+ reset(selectionLastSelectWasMultiSelect);
1615
+ set(selectionSelectedItemIds, (previous) => previous.filter((id) => id !== item.id));
1616
+ }
1617
+ }),
1618
+ deselect,
1619
+ clearSelection,
1620
+ resetSelectionState,
1621
+ toggleLastSelectWasMultiSelect,
1622
+ selectForModelViewsPanel,
1623
+ };
1624
+ }
1625
+
1626
+ const selectionState = recoil.selector({
1627
+ key: 'viewerToolkitSelectionState',
1628
+ get: ({ get }) => {
1629
+ const selectedItemIds = get(selectionSelectedItemIds);
1630
+ const lastSelected = get(selectionLastSelected);
1631
+ const highestSelectedAncestorId = get(selectionHighestSelectedAncestor);
1632
+ const state = {
1633
+ selectedItemIds,
1634
+ lastSelected,
1635
+ highestSelectedAncestorId,
1636
+ };
1637
+ return state;
1638
+ },
1639
+ });
1640
+
1641
+ var index$1 = /*#__PURE__*/Object.freeze({
1642
+ __proto__: null,
1643
+ selectionHighestSelectedAncestor: selectionHighestSelectedAncestor,
1644
+ selectionLastSelectWasMultiSelect: selectionLastSelectWasMultiSelect,
1645
+ selectionLastSelected: selectionLastSelected,
1646
+ selectionLastSelectionFromViewer: selectionLastSelectionFromViewer,
1647
+ selectionPreviousVisibleSummary: selectionPreviousVisibleSummary,
1648
+ selectionSelectedItemIds: selectionSelectedItemIds,
1649
+ selectionSelectedItems: selectionSelectedItems,
1650
+ selectionState: selectionState,
1651
+ selectionVisibleCount: selectionVisibleCount,
1652
+ selectionVisibleSummary: selectionVisibleSummary,
1653
+ useSelectionActions: useSelectionActions
1654
+ });
1655
+
1656
+ recoil.atom({
1657
+ key: 'sceneTreeLoaderVisible',
1658
+ default: false,
1659
+ });
1660
+ const sceneTreeElement = recoil.atom({
1661
+ key: 'sceneTreeElement',
1662
+ default: undefined,
1663
+ });
1664
+ const sceneTreeWidth = recoil.atom({
1665
+ key: 'sceneTreeWidth',
1666
+ default: undefined,
1667
+ });
1668
+ recoil.selector({
1669
+ key: 'sceneTreeController',
1670
+ get: ({ get }) => { var _a; return (_a = get(sceneTreeElement)) === null || _a === void 0 ? void 0 : _a.controller; },
1671
+ dangerouslyAllowMutability: true,
1672
+ });
1673
+
1674
+ function useSceneTreeActions() {
1675
+ const selectionActions = useSelectionActions();
1676
+ const viewerCameraActions = useViewerCameraActions();
1677
+ return {
1678
+ toggleSelection: useActionCallback(({ snapshot }) => async (event, node, tree) => {
1679
+ var _a, _b;
1680
+ if (event.defaultPrevented || event.button !== 0) {
1681
+ return;
1682
+ }
1683
+ if (!event.altKey) {
1684
+ if ((event.ctrlKey || event.metaKey) && node.selected) {
1685
+ tree.deselectItem(node);
1686
+ }
1687
+ else if (node.selected) {
1688
+ tree.selectItem(node, {
1689
+ recurseParent: true,
1690
+ });
1691
+ }
1692
+ else if (!node.selected) {
1693
+ tree.selectItem(node, {
1694
+ append: event.ctrlKey || event.metaKey,
1695
+ range: event.shiftKey,
1696
+ });
1697
+ }
1698
+ }
1699
+ if (node.id != null) {
1700
+ const lastSelected = await snapshot.getPromise(selectionLastSelected);
1701
+ selectionActions.syncSelection({
1702
+ id: (_a = node.id) === null || _a === void 0 ? void 0 : _a.hex,
1703
+ ancestors: (_b = lastSelected === null || lastSelected === void 0 ? void 0 : lastSelected.ancestors) !== null && _b !== void 0 ? _b : [],
1704
+ }, {
1705
+ selected: event.metaKey || event.ctrlKey ? !node.selected : true,
1706
+ multiSelect: event.shiftKey || event.metaKey || event.ctrlKey,
1707
+ });
1708
+ // TODO: transform widget interaction
1709
+ }
1710
+ }),
1711
+ toggleExpansion: useActionCallback(() => (event, node, tree) => {
1712
+ tree.toggleExpandItem(node);
1713
+ }),
1714
+ toggleVisibility: useActionCallback(() => (event, node, tree) => {
1715
+ tree.toggleItemVisibility(node);
1716
+ }),
1717
+ flyToRow: useActionCallback(({ snapshot }) => async (rowClientY) => {
1718
+ var _a;
1719
+ const element = await snapshot.getPromise(sceneTreeElement);
1720
+ const row = await (element === null || element === void 0 ? void 0 : element.getRowAtClientY(rowClientY));
1721
+ if (((_a = row === null || row === void 0 ? void 0 : row.node.id) === null || _a === void 0 ? void 0 : _a.hex) != null) {
1722
+ await viewerCameraActions.flyToById(row.node.id.hex);
1723
+ }
1724
+ }),
1725
+ setContextMenuItem: useActionCallback(({ set, snapshot }) => async (rowClientY) => {
1726
+ const element = await snapshot.getPromise(sceneTreeElement);
1727
+ const row = await (element === null || element === void 0 ? void 0 : element.getRowAtClientY(rowClientY));
1728
+ set(contextMenuItem, row);
1729
+ }),
1730
+ };
1731
+ }
1732
+
1733
+ function useSceneItemsOperations({ viewerElement }) {
1734
+ const sceneItemsOperation = React.useCallback(async (f, ids) => {
1735
+ const scene = await (viewerElement === null || viewerElement === void 0 ? void 0 : viewerElement.scene());
1736
+ if (ids == null) {
1737
+ await (scene === null || scene === void 0 ? void 0 : scene.items((op) => f(op.where((q) => q.all()))).execute());
1738
+ }
1739
+ else if (ids.length > 0) {
1740
+ await (scene === null || scene === void 0 ? void 0 : scene.items((op) => f(op.where((q) => q.withItemIds(ids)))).execute());
1741
+ }
1742
+ }, [viewerElement]);
1743
+ useApplyKeyBinding({
1744
+ fn: React.useCallback(async () => {
1745
+ const animation = { milliseconds: 500 };
1746
+ const scene = await (viewerElement === null || viewerElement === void 0 ? void 0 : viewerElement.scene());
1747
+ await (scene === null || scene === void 0 ? void 0 : scene.camera().viewAll().render({ animation }));
1748
+ }, [viewerElement]),
1749
+ keyBind: 'f',
1750
+ });
1751
+ const showOnlySelected = React.useCallback(async () => {
1752
+ const scene = await (viewerElement === null || viewerElement === void 0 ? void 0 : viewerElement.scene());
1753
+ scene === null || scene === void 0 ? void 0 : scene.items((op) => [
1754
+ op.where((q) => q.all()).hide(),
1755
+ op.where((q) => q.withSelected()).show(),
1756
+ ]).execute();
1757
+ }, [viewerElement]);
1758
+ const hideSelected = React.useCallback(async () => {
1759
+ const scene = await (viewerElement === null || viewerElement === void 0 ? void 0 : viewerElement.scene());
1760
+ scene === null || scene === void 0 ? void 0 : scene.items((op) => [op.where((q) => q.withSelected()).hide()]).execute();
1761
+ }, [viewerElement]);
1762
+ const showOnlyItem = React.useCallback(async (id) => {
1763
+ const scene = await (viewerElement === null || viewerElement === void 0 ? void 0 : viewerElement.scene());
1764
+ scene === null || scene === void 0 ? void 0 : scene.items((op) => [
1765
+ op.where((q) => q.all()).hide(),
1766
+ op.where((q) => q.withItemIds([id])).show(),
1767
+ ]).execute();
1768
+ }, [viewerElement]);
1769
+ return {
1770
+ sceneItemsOperation,
1771
+ showOnlySelected,
1772
+ hideSelected,
1773
+ showOnlyItem,
1774
+ };
1775
+ }
1776
+
1777
+ const VertexHideSelectedMenuItem = () => {
1778
+ const viewerHTMLElement = recoil.useRecoilValue(viewerElement);
1779
+ const selectedItems = recoil.useRecoilValue(selectionSelectedItemIds);
1780
+ const sceneItemOperations = useSceneItemsOperations({ viewerElement: viewerHTMLElement !== null && viewerHTMLElement !== void 0 ? viewerHTMLElement : null });
1781
+ return (jsxRuntime.jsxs(uiReact.VertexMenuItem, { "data-testid": "hide-selected-menu-option", onClick: () => sceneItemOperations.hideSelected(), disabled: selectedItems.length === 0, children: [jsxRuntime.jsx(uiReact.VertexIcon, { slot: "icon", name: "visibility-hidden", size: "sm" }), jsxRuntime.jsx("div", { className: "pl-2", children: "Hide Selected" })] }));
1782
+ };
1783
+
1784
+ const VertexHideAllMenuItem = () => {
1785
+ const viewerHTMLElement = recoil.useRecoilValue(viewerElement);
1786
+ const sceneItemOperations = useSceneItemsOperations({ viewerElement: viewerHTMLElement !== null && viewerHTMLElement !== void 0 ? viewerHTMLElement : null });
1787
+ return (jsxRuntime.jsxs(uiReact.VertexMenuItem, { "data-testid": "hide-all-menu-option", onClick: () => {
1788
+ sceneItemOperations.sceneItemsOperation((builder) => builder.hide());
1789
+ }, children: [jsxRuntime.jsx(uiReact.VertexIcon, { slot: "icon", name: "visibility-hidden", size: "sm" }), jsxRuntime.jsx("div", { className: "pl-2", children: "Hide All Parts" })] }));
1790
+ };
1791
+
1792
+ const VertexShowOnlyMenuItem = () => {
1793
+ const viewerHTMLElement = recoil.useRecoilValue(viewerElement);
1794
+ const currentItem = recoil.useRecoilValue(contextMenuItem);
1795
+ const sceneItemOperations = useSceneItemsOperations({ viewerElement: viewerHTMLElement !== null && viewerHTMLElement !== void 0 ? viewerHTMLElement : null });
1796
+ return (jsxRuntime.jsxs(uiReact.VertexMenuItem, { "data-testid": "show-only-menu-option", onClick: () => {
1797
+ var _a;
1798
+ const isRow = contextMenuItemIsRow(currentItem);
1799
+ const itemId = isRow
1800
+ ? (_a = currentItem === null || currentItem === void 0 ? void 0 : currentItem.node.id) === null || _a === void 0 ? void 0 : _a.hex
1801
+ : currentItem === null || currentItem === void 0 ? void 0 : currentItem.id;
1802
+ if (itemId != null) {
1803
+ sceneItemOperations.showOnlyItem(itemId);
1804
+ }
1805
+ }, disabled: currentItem == null, children: [jsxRuntime.jsx(uiReact.VertexIcon, { slot: "icon", name: "visibility-visible", size: "sm" }), jsxRuntime.jsx("div", { className: "pl-2", children: "Show Only Part" })] }));
1806
+ };
1807
+
1808
+ const VertexShowOnlySelectedMenuItem = () => {
1809
+ const viewerHTMLElement = recoil.useRecoilValue(viewerElement);
1810
+ const selectedItems = recoil.useRecoilValue(selectionSelectedItemIds);
1811
+ const sceneItemOperations = useSceneItemsOperations({ viewerElement: viewerHTMLElement !== null && viewerHTMLElement !== void 0 ? viewerHTMLElement : null });
1812
+ return (jsxRuntime.jsxs(uiReact.VertexMenuItem, { "data-testid": "show-only-selected-menu-option", onClick: () => sceneItemOperations.showOnlySelected(), disabled: selectedItems.length === 0, children: [jsxRuntime.jsx(uiReact.VertexIcon, { slot: "icon", name: "visibility-visible", size: "sm" }), jsxRuntime.jsx("div", { className: "pl-2", children: "Show Only Selected" })] }));
1813
+ };
1814
+
1815
+ const VertexShowAllMenuItem = () => {
1816
+ const viewerHTMLElement = recoil.useRecoilValue(viewerElement);
1817
+ const sceneItemOperations = useSceneItemsOperations({ viewerElement: viewerHTMLElement !== null && viewerHTMLElement !== void 0 ? viewerHTMLElement : null });
1818
+ return (jsxRuntime.jsxs(uiReact.VertexMenuItem, { "data-testid": "show-all-menu-option", onClick: () => {
1819
+ sceneItemOperations.sceneItemsOperation((builder) => builder.show());
1820
+ }, children: [jsxRuntime.jsx(uiReact.VertexIcon, { slot: "icon", name: "visibility-visible", size: "sm" }), jsxRuntime.jsx("div", { className: "pl-2", children: "Show All Parts" })] }));
1821
+ };
1822
+
1823
+ const VertexFitSelectedMenuItem = () => {
1824
+ const selectedItems = recoil.useRecoilValue(selectionSelectedItemIds);
1825
+ const selectedSummary = recoil.useRecoilValue(selectionVisibleSummary);
1826
+ const cameraActions = useViewerCameraActions();
1827
+ return (jsxRuntime.jsxs(uiReact.VertexMenuItem, { "data-testid": "fit-selected-menu-option", onClick: () => {
1828
+ if ((selectedSummary === null || selectedSummary === void 0 ? void 0 : selectedSummary.boundingBox) != null) {
1829
+ cameraActions.flyToByBoundingBox(selectedSummary.boundingBox);
1830
+ }
1831
+ }, disabled: selectedItems.length === 0, children: [jsxRuntime.jsx(uiReact.VertexIcon, { slot: "icon", name: "fit-selected", size: "sm" }), jsxRuntime.jsx("div", { className: "pl-2", children: "Fit Selected" })] }));
1832
+ };
1833
+
1834
+ const VertexFlyToMenuItem = () => {
1835
+ const cameraActions = useViewerCameraActions();
1836
+ const currentItem = recoil.useRecoilValue(contextMenuItem);
1837
+ return (jsxRuntime.jsxs(uiReact.VertexMenuItem, { "data-testid": "fly-to-part-menu-option", onClick: () => {
1838
+ var _a;
1839
+ const isRow = contextMenuItemIsRow(currentItem);
1840
+ const itemId = isRow
1841
+ ? (_a = currentItem === null || currentItem === void 0 ? void 0 : currentItem.node.id) === null || _a === void 0 ? void 0 : _a.hex
1842
+ : currentItem === null || currentItem === void 0 ? void 0 : currentItem.id;
1843
+ if (itemId != null) {
1844
+ cameraActions.flyToById(itemId);
1845
+ }
1846
+ }, disabled: currentItem == null, children: [jsxRuntime.jsx(uiReact.VertexIcon, { slot: "icon", name: "paper-airplane", size: "sm" }), jsxRuntime.jsx("div", { className: "pl-2", children: "Fly To" })] }));
1847
+ };
1848
+
1849
+ const DefaultSceneTreeContextMenu = () => {
1850
+ return (jsxRuntime.jsxs("div", { className: "w-56", children: [jsxRuntime.jsx(VertexHideSelectedMenuItem, {}), jsxRuntime.jsx(VertexHideAllMenuItem, {}), jsxRuntime.jsx(VertexShowOnlyMenuItem, {}), jsxRuntime.jsx(VertexShowOnlySelectedMenuItem, {}), jsxRuntime.jsx(VertexShowAllMenuItem, {}), jsxRuntime.jsx(uiReact.VertexMenuDivider, { className: "md:contents hidden short:hidden" }), jsxRuntime.jsx(VertexFitSelectedMenuItem, {}), jsxRuntime.jsx(VertexFlyToMenuItem, {})] }));
1851
+ };
1852
+
1853
+ const VertexSceneTreeContextMenu = ({ children }) => {
1854
+ const sceneTreeActions = useSceneTreeActions();
1855
+ const sceneTreeHTMLElement = recoil.useRecoilValue(sceneTreeElement);
1856
+ const hasDefinedChildren = children != null && children !== false;
1857
+ return (jsxRuntime.jsx(VertexContextMenu, { menuType: "scene-tree", targetElement: sceneTreeHTMLElement, onOpen: async (event, _pt) => {
1858
+ const pointerEvent = event;
1859
+ await sceneTreeActions.setContextMenuItem(pointerEvent.clientY);
1860
+ }, disableBackdrop: true, children: hasDefinedChildren
1861
+ ? (children)
1862
+ : (jsxRuntime.jsx(DefaultSceneTreeContextMenu, {})) }));
1863
+ };
1864
+
1865
+ const VertexHidePartMenuItem = () => {
1866
+ const viewerHTMLElement = recoil.useRecoilValue(viewerElement);
1867
+ const currentItem = recoil.useRecoilValue(contextMenuItem);
1868
+ const sceneItemOperations = useSceneItemsOperations({ viewerElement: viewerHTMLElement !== null && viewerHTMLElement !== void 0 ? viewerHTMLElement : null });
1869
+ return (jsxRuntime.jsxs(uiReact.VertexMenuItem, { "data-testid": "hide-menu-option", onClick: () => {
1870
+ var _a;
1871
+ const isRow = contextMenuItemIsRow(currentItem);
1872
+ const itemId = isRow
1873
+ ? (_a = currentItem === null || currentItem === void 0 ? void 0 : currentItem.node.id) === null || _a === void 0 ? void 0 : _a.hex
1874
+ : currentItem.id;
1875
+ if (itemId != null) {
1876
+ sceneItemOperations.sceneItemsOperation((builder) => builder.hide(), [itemId]);
1877
+ }
1878
+ }, disabled: currentItem == null, children: [jsxRuntime.jsx(uiReact.VertexIcon, { slot: "icon", name: "visibility-hidden", size: "sm" }), jsxRuntime.jsx("div", { className: "pl-2", children: "Hide Part" })] }));
1879
+ };
1880
+
1881
+ const DefaultViewerContextMenu = () => {
1882
+ return (jsxRuntime.jsxs("div", { className: "w-56", children: [jsxRuntime.jsx(VertexHidePartMenuItem, {}), jsxRuntime.jsx(VertexHideSelectedMenuItem, {}), jsxRuntime.jsx(VertexHideAllMenuItem, {}), jsxRuntime.jsx(VertexShowOnlyMenuItem, {}), jsxRuntime.jsx(VertexShowOnlySelectedMenuItem, {}), jsxRuntime.jsx(VertexShowAllMenuItem, {}), jsxRuntime.jsx(uiReact.VertexMenuDivider, { className: "md:contents hidden short:hidden" }), jsxRuntime.jsx(VertexFitSelectedMenuItem, {}), jsxRuntime.jsx(VertexFlyToMenuItem, {})] }));
1883
+ };
1884
+
1885
+ const VertexViewerContextMenu = ({ children }) => {
1886
+ const viewerHTMLElement = recoil.useRecoilValue(viewerElement);
1887
+ const hasDefinedChildren = children != null && children !== false;
1888
+ return (jsxRuntime.jsx(VertexContextMenu, { menuType: "viewer", targetElement: viewerHTMLElement, disableBackdrop: true, children: hasDefinedChildren
1889
+ ? (children)
1890
+ : (jsxRuntime.jsx(DefaultViewerContextMenu, {})) }));
1891
+ };
1892
+
1893
+ const sdkConfig = recoil.atom({
1894
+ key: 'viewerToolkitSdkConfig',
1895
+ default: undefined,
1896
+ });
1897
+
1898
+ const targetIsElement = (target, tagName) => {
1899
+ return target instanceof Element && target.tagName === tagName;
1900
+ };
1901
+
1902
+ function useRecoilRef({ state, }) {
1903
+ const callback = recoil.useRecoilCallback(({ set }) => (node) => {
1904
+ set(state, node);
1905
+ }, []);
1906
+ return callback;
1907
+ }
1908
+
1909
+ var AssemblyFontFace;
1910
+ (function (AssemblyFontFace) {
1911
+ AssemblyFontFace["ROBOTO"] = "ROBOTO";
1912
+ AssemblyFontFace["ROBOTO_MONO"] = "ROBOTO_MONO";
1913
+ })(AssemblyFontFace || (AssemblyFontFace = {}));
1914
+
1915
+ function styleFromOptionalFont(font) {
1916
+ return font != null
1917
+ ? {
1918
+ '--scene-tree-default-font-family': styleFromFontFace(font.fontFace),
1919
+ '--scene-tree-default-font-color': font.color,
1920
+ }
1921
+ : {};
1922
+ }
1923
+ function styleFromOptionalBackgroundColors(backgroundColors) {
1924
+ const depthColors = backgroundColors === null || backgroundColors === void 0 ? void 0 : backgroundColors.depthColors;
1925
+ const depthColorStyles = depthColors != null
1926
+ ? Object.keys(depthColors).reduce((res, key) => (Object.assign(Object.assign({}, res), { [`--scene-tree-row-background-color-depth-${key}`]: depthColors[parseInt(key)] })), {})
1927
+ : {};
1928
+ return backgroundColors != null
1929
+ ? Object.assign(Object.assign({}, depthColorStyles), { '--scene-tree-hovered-row-background-color': backgroundColors.hovered, '--scene-tree-selected-row-background-color': backgroundColors.selected, '--scene-tree-row-background-color': backgroundColors.defaultColor }) : {};
1930
+ }
1931
+ function styleFromFontFace(fontFace) {
1932
+ switch (fontFace) {
1933
+ case AssemblyFontFace.ROBOTO_MONO:
1934
+ return 'var(--vertex-ui-font-family-monospace)';
1935
+ default:
1936
+ return 'var(--vertex-ui-font-family)';
1937
+ }
1938
+ }
1939
+
1940
+ const DEFAULT_NAME_COLUMN = {
1941
+ binding: '{{row.node.name}}',
1942
+ label: 'Name',
1943
+ metadataKeyName: 'VERTEX_SCENE_ITEM_NAME',
1944
+ };
1945
+ const sceneTreeColumnsActiveColumns = recoil.selector({
1946
+ key: 'sceneTreeColumnsActiveColumns',
1947
+ get: ({ get }) => {
1948
+ const labels = []; // TODO: map to configured column list
1949
+ const treeWidth = get(sceneTreeWidth);
1950
+ if (treeWidth != null && treeWidth > 0) {
1951
+ const nameWidth = (1 / (labels.length + 1)) * treeWidth;
1952
+ const availableWidth = (treeWidth - nameWidth) / labels.length;
1953
+ return {
1954
+ Name: Object.assign(Object.assign({}, DEFAULT_NAME_COLUMN), { initialWidth: nameWidth, minWidth: availableWidth }),
1955
+ };
1956
+ }
1957
+ return { Name: DEFAULT_NAME_COLUMN };
1958
+ },
1959
+ });
1960
+ const sceneTreeColumnsActiveColumnNames = recoil.selector({
1961
+ key: 'sceneTreeColumnsActiveColumnNames',
1962
+ get: ({ get }) => Object.keys(get(sceneTreeColumnsActiveColumns)),
1963
+ });
1964
+
1965
+ const VertexSceneTreeTableLayout = (sdkProps) => {
1966
+ const activeColumns = recoil.useRecoilValue(sceneTreeColumnsActiveColumns);
1967
+ const activeColumnNames = recoil.useRecoilValue(sceneTreeColumnsActiveColumnNames);
1968
+ const columnWidths = []; // TODO: map to configured column sizes
1969
+ return (jsxRuntime.jsx(viewerReact.VertexSceneTreeTableLayout, Object.assign({ "data-testid": "scene-tree-table-layout" }, sdkProps, { children: activeColumnNames.map((k, i) => {
1970
+ var _a;
1971
+ const column = activeColumns[k];
1972
+ const initialWidth = (_a = columnWidths[i]) !== null && _a !== void 0 ? _a : column.initialWidth;
1973
+ return (jsxRuntime.jsxs(viewerReact.VertexSceneTreeTableColumn, { "data-testid": `scene-tree-table-column-${i}`, initialWidth: initialWidth, minWidth: column.minWidth, children: [jsxRuntime.jsx("template", { slot: "header", dangerouslySetInnerHTML: {
1974
+ __html: `
1975
+ <div class="text-sm text-neutral-800 my-2">
1976
+ <div class="${classNames('truncate', {
1977
+ 'pl-2': i === 0,
1978
+ 'pr-2': i === activeColumnNames.length - 1,
1979
+ })}">
1980
+ ${column.label}
1981
+ </div>
1982
+ </div>
1983
+ `,
1984
+ } }), jsxRuntime.jsx("template", { dangerouslySetInnerHTML: {
1985
+ __html: `
1986
+ <vertex-scene-tree-table-cell
1987
+ prop:value="${column.binding}"
1988
+ prop:selection-handler="{{row.data.handleSelection}}"
1989
+ prop:expansion-handler="{{row.data.handleExpansion}}"
1990
+ prop:visibility-handler="{{row.data.handleVisibility}}"
1991
+ ${i === 0 ? 'expand-toggle' : ''}
1992
+ ${i === activeColumnNames.length - 1
1993
+ ? 'isolate-button visibility-toggle'
1994
+ : ''}>
1995
+ <div class="flex items-center gap-2">
1996
+ <vertex-tooltip
1997
+ class="truncate"
1998
+ prop:content="${column.binding != null ? column.binding : '--'}"
1999
+ placement="${i !== activeColumnNames.length - 1
2000
+ ? 'right'
2001
+ : 'top'}">
2002
+ <div class="flex items-center w-full">
2003
+ <div class="truncate">${column.binding}</div>
2004
+ </div>
2005
+ </vertex-tooltip>
2006
+ </div>
2007
+
2008
+
2009
+ <div slot="placeholder" class="flex items-center w-full truncate">
2010
+ <div> -- </div>
2011
+ </div>
2012
+ </vertex-scene-tree-table-cell>
2013
+ `,
2014
+ } })] }, column.label));
2015
+ }) })));
2016
+ };
2017
+
2018
+ const VertexSceneTree = (_a) => {
2019
+ var { id, font, backgroundColors, children, style, onPointerDown, onClick, onToggleSelection, onToggleExpansion, onToggleVisibility, className, rowData } = _a, sdkProps = __rest(_a, ["id", "font", "backgroundColors", "children", "style", "onPointerDown", "onClick", "onToggleSelection", "onToggleExpansion", "onToggleVisibility", "className", "rowData"]);
2020
+ const config = recoil.useRecoilValue(sdkConfig);
2021
+ const selectionActions = useSelectionActions();
2022
+ const sceneTreeActions = useSceneTreeActions();
2023
+ const viewer = recoil.useRecoilValue(viewerElement);
2024
+ const callbackRef = useRecoilRef({
2025
+ state: sceneTreeElement,
2026
+ });
2027
+ const wrappedExpansionHandler = (event, node, tree) => {
2028
+ sceneTreeActions.toggleExpansion(event, node, tree);
2029
+ onToggleExpansion === null || onToggleExpansion === void 0 ? void 0 : onToggleExpansion(event, node, tree);
2030
+ };
2031
+ const wrappedSelectionHandler = (event, node, tree) => {
2032
+ sceneTreeActions.toggleSelection(event, node, tree);
2033
+ onToggleSelection === null || onToggleSelection === void 0 ? void 0 : onToggleSelection(event, node, tree);
2034
+ };
2035
+ const wrappedVisibilityHandler = (event, node, tree) => {
2036
+ sceneTreeActions.toggleVisibility(event, node, tree);
2037
+ onToggleVisibility === null || onToggleVisibility === void 0 ? void 0 : onToggleVisibility(event, node, tree);
2038
+ };
2039
+ const handleClick = (event) => {
2040
+ if (event.altKey) {
2041
+ sceneTreeActions.flyToRow(event.clientY);
2042
+ }
2043
+ onClick === null || onClick === void 0 ? void 0 : onClick(event);
2044
+ };
2045
+ const handlePointerDown = (event) => {
2046
+ if (targetIsElement(event.target, 'VERTEX-SCENE-TREE-TABLE-COLUMN')) {
2047
+ selectionActions.clearSelection();
2048
+ }
2049
+ onPointerDown === null || onPointerDown === void 0 ? void 0 : onPointerDown(event);
2050
+ };
2051
+ return (jsxRuntime.jsxs(viewerReact.VertexSceneTree, Object.assign({ id: id !== null && id !== void 0 ? id : 'vertex-scene-tree', "data-testid": "vertex-scene-tree", config: config, ref: callbackRef, className: classNames('shrink-0', className), style: Object.assign(Object.assign(Object.assign({}, styleFromOptionalFont(font)), styleFromOptionalBackgroundColors(backgroundColors)), style), onConnectionError: (e) => {
2052
+ logger.error('Scene Tree Connection Error: ', e.detail);
2053
+ }, viewer: viewer, onPointerDown: handlePointerDown, onClick: handleClick, rowData: (row) => {
2054
+ var _a;
2055
+ const providedRowData = (_a = rowData === null || rowData === void 0 ? void 0 : rowData(row)) !== null && _a !== void 0 ? _a : {};
2056
+ return Object.assign({ handleExpansion: wrappedExpansionHandler, handleSelection: wrappedSelectionHandler, handleVisibility: wrappedVisibilityHandler }, providedRowData);
2057
+ } }, sdkProps, { children: [jsxRuntime.jsx(VertexSceneTreeTableLayout, {}), children] })));
2058
+ };
2059
+
2060
+ const useSynchronizedProp = (state, value) => {
2061
+ const setState = recoil.useSetRecoilState(state);
2062
+ React.useEffect(() => {
2063
+ setState(value);
2064
+ }, [setState, value]);
2065
+ };
2066
+
2067
+ const VertexPopupButton = (_a) => {
2068
+ var { open, children } = _a, uiProps = __rest(_a, ["open", "children"]);
2069
+ const anchor = children.find((c) => c.type === Anchor);
2070
+ const others = children.filter((c) => c.type !== Anchor);
2071
+ if (anchor == null) {
2072
+ return jsxRuntime.jsx(jsxRuntime.Fragment, {});
2073
+ }
2074
+ return (jsxRuntime.jsxs(uiReact.VertexPopover, Object.assign({ className: "flex", placement: "top", backdrop: false, open: open }, uiProps, { children: [anchor, jsxRuntime.jsx("div", { className: classNames('flex mb-1.5', {
2075
+ hidden: !open,
2076
+ }), "data-testid": "align-plane-popover-menu", children: others })] })));
2077
+ };
2078
+ const Anchor = ({ tooltipContent, children }) => {
2079
+ return (jsxRuntime.jsx("div", { slot: "anchor", children: jsxRuntime.jsx(uiReact.VertexTooltip, { className: "cursor-pointer", content: tooltipContent, children: children }) }));
2080
+ };
2081
+ VertexPopupButton.Anchor = Anchor;
2082
+
2083
+ const VertexSlider = (_a) => {
2084
+ var { value, defaultValue, onChange, onValueChange, onValueInput } = _a, uiProps = __rest(_a, ["value", "defaultValue", "onChange", "onValueChange", "onValueInput"]);
2085
+ const [displayedValue, setDisplayedValue] = React.useState(defaultValue);
2086
+ return (jsxRuntime.jsx(uiReact.VertexSlider, Object.assign({ className: "w-52 mx-2 items-center", value: value !== null && value !== void 0 ? value : displayedValue, onValueChange: (e) => {
2087
+ setDisplayedValue(e.detail.value);
2088
+ onChange === null || onChange === void 0 ? void 0 : onChange(e.detail.value);
2089
+ onValueChange === null || onValueChange === void 0 ? void 0 : onValueChange(e);
2090
+ }, onValueInput: (e) => {
2091
+ setDisplayedValue(e.detail.value);
2092
+ onChange === null || onChange === void 0 ? void 0 : onChange(e.detail.value);
2093
+ onValueInput === null || onValueInput === void 0 ? void 0 : onValueInput(e);
2094
+ } }, uiProps)));
2095
+ };
2096
+
2097
+ function VertexViewerToolbarDivider() {
2098
+ return (jsxRuntime.jsx("div", { "data-testid": "toolbar-divider", className: "h-6 w-px bg-neutral-300 mx-1 items-center mt-1" }));
2099
+ }
2100
+
2101
+ function VertexViewerCrossSectionAlignmentPopoverMenu() {
2102
+ const crossSectioningActions = useCrossSectioningActions();
2103
+ const open = recoil.useRecoilValue(crossSectioningAlignmentToolsOpen);
2104
+ const alignment = recoil.useRecoilValue(crossSectioningAlignment);
2105
+ const getBaseIcon = () => {
2106
+ return `align-to-${alignment === null || alignment === void 0 ? void 0 : alignment.mode}`;
2107
+ };
2108
+ useStackKeyBinding({
2109
+ id: 'CrossSectionAlignment',
2110
+ keyBind: 'Escape',
2111
+ fn: crossSectioningActions.cancelCurrentHit,
2112
+ addPredicate: () => { var _a; return (_a = alignment === null || alignment === void 0 ? void 0 : alignment.hitPending) !== null && _a !== void 0 ? _a : false; },
2113
+ cancelPredicate: () => !(alignment === null || alignment === void 0 ? void 0 : alignment.hitPending),
2114
+ }, [alignment === null || alignment === void 0 ? void 0 : alignment.hitPending]);
2115
+ return (jsxRuntime.jsxs(VertexPopupButton, { "data-testid": "section-alignment-popover", placement: "top", backdrop: false, open: open, onDismissed: crossSectioningActions.closeAlignmentTools, children: [jsxRuntime.jsx(VertexPopupButton.Anchor, { tooltipContent: "Section Alignment", children: jsxRuntime.jsx(uiReact.VertexIconButton, { "data-testid": "section-alignment-icon-button", iconName: getBaseIcon(), iconColor: "secondary", onClick: open
2116
+ ? crossSectioningActions.closeAlignmentTools
2117
+ : crossSectioningActions.openAlignmentTools, children: jsxRuntime.jsx(uiReact.VertexIcon, { "data-testid": "section-alignment-icon-chevron", name: "chevron-up", size: "sm" }) }) }), jsxRuntime.jsxs(viewerReact.VertexViewerToolbarGroup, { direction: "vertical", className: "flex justify-evenly items-center py-1 my-1 ring-1 ring-neutral-200 rounded bg-neutral-100 opacity-95", children: [jsxRuntime.jsx(uiReact.VertexIconButton, { iconColor: "secondary", onClick: () => crossSectioningActions.updateAlignment('global'), className: "w-full text-neutral-800 hover:bg-neutral-300 rounded cursor-pointer mx-0.5 py-1 px-2", "data-testid": "section-alignment-align-global", iconName: "align-to-global", variant: "plain", children: jsxRuntime.jsx("div", { className: "text-base ml-2", children: "Align to Global" }) }), jsxRuntime.jsx(uiReact.VertexIconButton, { iconColor: "secondary", onClick: () => crossSectioningActions.updateAlignment('surface'), className: "w-full text-neutral-800 hover:bg-neutral-300 rounded cursor-pointer mx-0.5 py-1 px-2", "data-testid": "section-alignment-align-surface", iconName: "align-to-surface", variant: "plain", children: jsxRuntime.jsx("div", { className: "text-base ml-2", children: "Align to Surface" }) })] })] }));
2118
+ }
2119
+
2120
+ function VertexViewerCrossSectionAxisPopoverMenu() {
2121
+ const crossSectioningActions = useCrossSectioningActions();
2122
+ const open = recoil.useRecoilValue(crossSectioningAxisToolsOpen);
2123
+ const planes = recoil.useRecoilValue(crossSectioningPlanes);
2124
+ const availableAxes = recoil.useRecoilValueLoadable(crossSectioningAvailableAxes);
2125
+ const baseIcon = React.useMemo(() => {
2126
+ const axis$1 = planes.length > 0 && availableAxes.state === 'hasValue'
2127
+ ? axis(planes[0], xAxis(availableAxes.contents), yAxis(availableAxes.contents), zAxis(availableAxes.contents))
2128
+ : 'x';
2129
+ return axis$1 != null ? `axis-${axis$1}` : 'axis-x';
2130
+ }, [availableAxes.state, availableAxes.contents, planes]);
2131
+ return (jsxRuntime.jsxs(VertexPopupButton, { "data-testid": "align-plane-popover", placement: "top", backdrop: false, open: open, onDismissed: crossSectioningActions.closeAxisTools, children: [jsxRuntime.jsx(VertexPopupButton.Anchor, { tooltipContent: "Align Plane", children: jsxRuntime.jsx(uiReact.VertexIconButton, { onClick: open
2132
+ ? crossSectioningActions.closeAxisTools
2133
+ : crossSectioningActions.openAxisTools, iconName: baseIcon, "data-testid": "align-plane-icon", iconColor: "secondary", children: jsxRuntime.jsx(uiReact.VertexIcon, { "data-testid": "align-plane-icon-chevron", name: "chevron-up", size: "sm" }) }) }), jsxRuntime.jsxs(viewerReact.VertexViewerToolbarGroup, { direction: "vertical", className: "flex justify-evenly items-center py-1 my-1 ring-1 ring-neutral-200 rounded bg-neutral-100 opacity-95", children: [jsxRuntime.jsx(uiReact.VertexIconButton, { "data-testid": "align-plane-x-axis-button", className: "w-full text-neutral-800 hover:bg-neutral-300 rounded cursor-pointer mx-0.5 py-1 px-2", onClick: () => crossSectioningActions.updateAxis('x'), variant: "plain", iconColor: "secondary", iconName: "axis-x", children: jsxRuntime.jsx("div", { className: "text-base ml-2", children: "X-axis Plane" }) }), jsxRuntime.jsx(uiReact.VertexIconButton, { className: "w-full text-neutral-800 hover:bg-neutral-300 rounded cursor-pointer mx-0.5 py-1 px-2", variant: "plain", iconColor: "secondary", iconName: "axis-y", "data-testid": "align-plane-y-axis-button", onClick: () => crossSectioningActions.updateAxis('y'), children: jsxRuntime.jsx("div", { className: "text-base ml-2", children: "Y-axis Plane" }) }), jsxRuntime.jsx(uiReact.VertexIconButton, { "data-testid": "align-plane-z-axis-button", className: "w-full text-neutral-800 hover:bg-neutral-300 rounded cursor-pointer mx-0.5 py-1 px-2", onClick: () => crossSectioningActions.updateAxis('z'), variant: "plain", iconColor: "secondary", iconName: "axis-z", children: jsxRuntime.jsx("div", { className: "text-base ml-2", children: "Z-axis Plane" }) })] })] }));
2134
+ }
2135
+
2136
+ /**
2137
+ * Types of distance units
2138
+ */
2139
+ var DistanceUnit;
2140
+ (function (DistanceUnit) {
2141
+ DistanceUnit["mm"] = "millimeters";
2142
+ DistanceUnit["cm"] = "centimeters";
2143
+ DistanceUnit["m"] = "meters";
2144
+ DistanceUnit["in"] = "inches";
2145
+ DistanceUnit["ft"] = "feet";
2146
+ })(DistanceUnit || (DistanceUnit = {}));
2147
+ /**
2148
+ * Conversion factors (based on mm)
2149
+ */
2150
+ var DistanceUnitConversion;
2151
+ (function (DistanceUnitConversion) {
2152
+ DistanceUnitConversion[DistanceUnitConversion["mm"] = 1] = "mm";
2153
+ DistanceUnitConversion[DistanceUnitConversion["cm"] = 0.1] = "cm";
2154
+ DistanceUnitConversion[DistanceUnitConversion["m"] = 0.001] = "m";
2155
+ DistanceUnitConversion[DistanceUnitConversion["in"] = 0.03937007874015748] = "in";
2156
+ DistanceUnitConversion[DistanceUnitConversion["ft"] = 0.0032808398950131233] = "ft";
2157
+ })(DistanceUnitConversion || (DistanceUnitConversion = {}));
2158
+ /**
2159
+ * Converts a value from non-system units (in) to base system units (mm) using supplied conversion factor.
2160
+ *
2161
+ * @param value Value to convert.
2162
+ * @param fromUnit Unit converting from (to base system units)
2163
+ */
2164
+ const convertFrom = (value, fromUnit) => {
2165
+ const conversionFactor = DistanceUnitConversion[fromUnit];
2166
+ return value / conversionFactor;
2167
+ };
2168
+ /**
2169
+ * Converts a value from base system units (mm) to another unit based on supplied conversion factor.
2170
+ *
2171
+ * @param value Value to convert.
2172
+ * @param toUnit Unit to convert to (from base system units)
2173
+ */
2174
+ const convertTo = (value, toUnit) => {
2175
+ const conversionFactor = DistanceUnitConversion[toUnit];
2176
+ return value * conversionFactor;
2177
+ };
2178
+ /**
2179
+ * Formats a numeric value as a string with supplied unit and decimal places.
2180
+ *
2181
+ * @param value
2182
+ * @param unit
2183
+ * @param decimalPlaces
2184
+ */
2185
+ const formatWithUnit = (value, unit, decimalPlaces) => {
2186
+ return `${value.toFixed(decimalPlaces)} ${unit}`;
2187
+ };
2188
+ /**
2189
+ * Formats a (millimeter based) numeric distance value
2190
+ * for display, as converted to specificied units.
2191
+ *
2192
+ * @param value
2193
+ * @param unit
2194
+ * @param decimalPlaces
2195
+ */
2196
+ const formatDistance = (value, unit, decimalPlaces) => {
2197
+ return formatWithUnit(convertTo(value, unit), unit, decimalPlaces);
2198
+ };
2199
+ /**
2200
+ * Returns a distance formatter function for a given pair of unit and decimal place params.
2201
+ *
2202
+ * @param unit
2203
+ * @param decimalPlaces
2204
+ */
2205
+ const createDistanceFormatter = (unit, decimalPlaces) => {
2206
+ return (value) => formatDistance(value, unit, decimalPlaces);
2207
+ };
2208
+
2209
+ function VertexViewerCrossSectionOffsetStepper({ offset, measurementUnit, decimalPlaces, onOffsetChange, }) {
2210
+ const [value, setValue] = React.useState(convertTo(offset, measurementUnit));
2211
+ React.useEffect(() => {
2212
+ setValue(convertTo(offset, measurementUnit));
2213
+ }, [measurementUnit, offset]);
2214
+ const formatter = createDistanceFormatter(measurementUnit, decimalPlaces);
2215
+ const handleIncrementalUpdate = (increment) => {
2216
+ const updated = convertFrom(value + increment, measurementUnit);
2217
+ onOffsetChange(updated);
2218
+ };
2219
+ const handleSubmit = (event) => {
2220
+ event.preventDefault();
2221
+ onOffsetChange(convertFrom(value, measurementUnit));
2222
+ };
2223
+ const handleBlur = () => {
2224
+ onOffsetChange(convertFrom(value, measurementUnit));
2225
+ };
2226
+ return (jsxRuntime.jsx(uiReact.VertexTooltip, { content: "Plane Offset", children: jsxRuntime.jsx("form", { "data-testid": "cross-section-stepper-form", onSubmit: handleSubmit, children: jsxRuntime.jsxs(uiReact.VertexTextfield, { "data-testid": "cross-section-stepper-input", className: "viewer-toolbar-input w-32 border-none ring-0 bg-white", type: "text", value: formatter(parseFloat(offset.toFixed(3))), onInputInput: (event) => {
2227
+ setValue(event.detail.value != null
2228
+ ? parseFloat(event.detail.value)
2229
+ : offset);
2230
+ }, onBlur: handleBlur, children: [jsxRuntime.jsx("div", { "data-testid": "cross-section-stepper-decrement", className: "py-1 cursor-pointer", slot: "left", onClick: () => handleIncrementalUpdate(-1), children: jsxRuntime.jsx(uiReact.VertexIcon, { name: "chevron-left", size: "sm" }) }), jsxRuntime.jsx("div", { "data-testid": "cross-section-stepper-increment", className: "py-1 cursor-pointer", slot: "right", onClick: () => handleIncrementalUpdate(1), children: jsxRuntime.jsx(uiReact.VertexIcon, { name: "chevron-right", size: "sm" }) })] }) }) }));
2231
+ }
2232
+
2233
+ const INTERACTIVITY_TIMEOUT_DEBOUNCE_THRESHOLD = 400;
2234
+ function VertexViewerCrossSectionPopupMenu({ decimalPlaces, measurementUnit, }) {
2235
+ const crossSectioningActions = useCrossSectioningActions();
2236
+ const open = recoil.useRecoilValue(crossSectioningAdditionalToolsOpen);
2237
+ const sliderRange = recoil.useRecoilValueLoadable(crossSectioningSliderRange);
2238
+ const displayOffset = recoil.useRecoilValueLoadable(crossSectioningPlaneDisplayOffset);
2239
+ const interactiveTimeout = React.useRef();
2240
+ const displayedRange = React.useMemo(() => sliderRange.state === 'hasValue'
2241
+ ? sliderRange.contents
2242
+ : DEFAULT_SLIDER_RANGE, [sliderRange.state, sliderRange.contents]);
2243
+ const displayedOffset = React.useMemo(() => (displayOffset.state === 'hasValue' ? displayOffset.contents : 0), [displayOffset.state, displayOffset.contents]);
2244
+ const handleCrossSectionValueChange = (value) => {
2245
+ clearTimeout(interactiveTimeout.current);
2246
+ interactiveTimeout.current = window.setTimeout(() => {
2247
+ crossSectioningActions.endInteraction();
2248
+ }, INTERACTIVITY_TIMEOUT_DEBOUNCE_THRESHOLD);
2249
+ crossSectioningActions.beginInteraction();
2250
+ crossSectioningActions.updatePlanes(value);
2251
+ };
2252
+ return (jsxRuntime.jsxs(VertexPopupButton, { "data-testid": "cross-section-popover", placement: "top", backdrop: false, open: open, onDismissed: crossSectioningActions.closeAdditionalTools, children: [jsxRuntime.jsx(VertexPopupButton.Anchor, { tooltipContent: "Section Tools", children: jsxRuntime.jsx("div", { className: "-ml-px py-1.5", children: jsxRuntime.jsx(uiReact.VertexIcon, { "data-testid": "cross-section-popover-icon", name: "chevron-up", size: "sm", onClick: open
2253
+ ? crossSectioningActions.closeAdditionalTools
2254
+ : crossSectioningActions.openAdditionalTools }) }) }), jsxRuntime.jsxs(viewerReact.VertexViewerToolbarGroup, { className: "relative bottom-3 flex justify-evenly items-center p-1 ring-1 ring-neutral-200 rounded bg-neutral-100 opacity-95", children: [jsxRuntime.jsx(VertexViewerCrossSectionAlignmentPopoverMenu, {}), jsxRuntime.jsx(uiReact.VertexTooltip, { className: "w-auto", content: "Reverse", children: jsxRuntime.jsx(uiReact.VertexIconButton, { iconName: "flip", "data-testid": "viewer-toolbar-flip-button", onClick: crossSectioningActions.flipPlanes, iconColor: "secondary" }) }), jsxRuntime.jsx(uiReact.VertexTooltip, { className: "w-auto", content: "Align View to Plane", children: jsxRuntime.jsx(uiReact.VertexIconButton, { "data-testid": "viewer-toolbar-align-view-to-plane-button", iconName: "align-view-to-plane", onClick: crossSectioningActions.alignViewToPlane, iconColor: "secondary" }) }), jsxRuntime.jsx(VertexViewerToolbarDivider, {}), jsxRuntime.jsx(VertexViewerCrossSectionAxisPopoverMenu, {}), jsxRuntime.jsx(VertexSlider, { value: displayedOffset, defaultValue: displayedRange.default, disabled: sliderRange.state !== 'hasValue', min: displayedRange.min, max: displayedRange.max, step: 0.01, onChange: handleCrossSectionValueChange }), jsxRuntime.jsx("div", { className: "px-4", children: jsxRuntime.jsx(VertexViewerCrossSectionOffsetStepper, { decimalPlaces: decimalPlaces, measurementUnit: measurementUnit, offset: displayOffset.state === 'hasValue' ? displayOffset.contents : 0, onOffsetChange: handleCrossSectionValueChange }) })] })] }));
2255
+ }
2256
+
2257
+ const VertexViewerCrossSectionButton = ({ decimalPlaces, measurementUnit, sectionLineWidth, sectionColor, }) => {
2258
+ const crossSectioningActions = useCrossSectioningActions();
2259
+ const isEnabled = recoil.useRecoilValue(crossSectioningEnabled);
2260
+ useSynchronizedProp(crossSectioningBorderWidth, sectionLineWidth);
2261
+ useSynchronizedProp(crossSectioningHighlightColor, sectionColor);
2262
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(uiReact.VertexTooltip, { content: isEnabled ? 'Hide Section' : 'Show Section', children: jsxRuntime.jsx(uiReact.VertexIconButton, { "data-testid": "viewer-toolbar-cross-section-button", iconName: "cross-section", iconColor: isEnabled ? 'primary' : 'secondary', onClick: () => isEnabled
2263
+ ? crossSectioningActions.disable()
2264
+ : crossSectioningActions.enable() }) }), jsxRuntime.jsx(VertexViewerCrossSectionPopupMenu, { decimalPlaces: decimalPlaces !== null && decimalPlaces !== void 0 ? decimalPlaces : 2, measurementUnit: measurementUnit !== null && measurementUnit !== void 0 ? measurementUnit : 'mm' })] }));
2265
+ };
2266
+
2267
+ const VertexFitAllButton = () => {
2268
+ const viewerHTMLElement = recoil.useRecoilValue(viewerElement);
2269
+ return (jsxRuntime.jsx(uiReact.VertexTooltip, { content: "Fit All", children: jsxRuntime.jsx(uiReact.VertexIconButton, { iconName: "fit-all", iconColor: "secondary", "data-testid": "viewer-toolbar-fit-all-button", onClick: async () => {
2270
+ const scene = await (viewerHTMLElement === null || viewerHTMLElement === void 0 ? void 0 : viewerHTMLElement.scene());
2271
+ await (scene === null || scene === void 0 ? void 0 : scene.camera().viewAll().render({ animation: { milliseconds: 500 } }));
2272
+ } }) }));
2273
+ };
2274
+
2275
+ const VertexPanButton = () => {
2276
+ const viewerHTMLElement = recoil.useRecoilValue(viewerElement);
2277
+ const sceneReady = recoil.useRecoilValue(viewerInitialSceneReady);
2278
+ const [activeTool, setActiveTool] = recoil.useRecoilState(viewerPrimaryInteractionType);
2279
+ const [interactionHandler, setInteractionHandler] = recoil.useRecoilState(viewerBaseInteractionHandlerProvider);
2280
+ React.useEffect(() => {
2281
+ if (viewerHTMLElement != null && sceneReady) {
2282
+ setInteractionHandler(viewerHTMLElement.getBaseInteractionHandler());
2283
+ }
2284
+ }, [viewerHTMLElement, sceneReady, setInteractionHandler]);
2285
+ return (jsxRuntime.jsx(uiReact.VertexTooltip, { content: "Pan", children: jsxRuntime.jsx(uiReact.VertexIconButton, { iconName: "pan", iconColor: activeTool === 'pan' ? 'primary' : 'secondary', "data-testid": "viewer-toolbar-pan-button", onClick: async () => {
2286
+ setActiveTool('pan');
2287
+ if (interactionHandler != null) {
2288
+ const handler = await interactionHandler;
2289
+ handler === null || handler === void 0 ? void 0 : handler.setPrimaryInteractionType('pan');
2290
+ }
2291
+ } }) }));
2292
+ };
2293
+
2294
+ const VertexRotateButton = () => {
2295
+ const viewerHTMLElement = recoil.useRecoilValue(viewerElement);
2296
+ const sceneReady = recoil.useRecoilValue(viewerInitialSceneReady);
2297
+ const [activeTool, setActiveTool] = recoil.useRecoilState(viewerPrimaryInteractionType);
2298
+ const [interactionHandler, setInteractionHandler] = recoil.useRecoilState(viewerBaseInteractionHandlerProvider);
2299
+ React.useEffect(() => {
2300
+ if (viewerHTMLElement != null && sceneReady) {
2301
+ setInteractionHandler(viewerHTMLElement.getBaseInteractionHandler());
2302
+ }
2303
+ }, [viewerHTMLElement, sceneReady, setInteractionHandler]);
2304
+ return (jsxRuntime.jsx(uiReact.VertexTooltip, { content: "Rotate", children: jsxRuntime.jsx(uiReact.VertexIconButton, { iconName: "rotate", iconColor: activeTool === 'rotate' ? 'primary' : 'secondary', "data-testid": "viewer-toolbar-rotate-button", onClick: async () => {
2305
+ setActiveTool('rotate');
2306
+ if (interactionHandler != null) {
2307
+ const handler = await interactionHandler;
2308
+ handler === null || handler === void 0 ? void 0 : handler.setPrimaryInteractionType('rotate');
2309
+ }
2310
+ } }) }));
2311
+ };
2312
+
2313
+ const boxSelectionEnabled = recoil.atom({
2314
+ key: 'boxSelectionEnabled',
2315
+ default: false,
2316
+ });
2317
+ const boxSelectionOperationType = recoil.atom({
2318
+ key: 'boxSelectionOperationType',
2319
+ default: 'clearAndSelect',
2320
+ });
2321
+
2322
+ const useBoxSelectionActions = () => {
2323
+ return {
2324
+ enable: useActionCallback(({ set }) => () => {
2325
+ set(boxSelectionEnabled, true);
2326
+ }),
2327
+ disable: useActionCallback(({ reset }) => async () => {
2328
+ reset(boxSelectionEnabled);
2329
+ reset(boxSelectionOperationType);
2330
+ }),
2331
+ setOperationType: useActionCallback(({ set }) => (operationType) => {
2332
+ set(boxSelectionOperationType, operationType);
2333
+ }),
2334
+ };
2335
+ };
2336
+
2337
+ const VertexBoxSelectionButton = () => {
2338
+ const boxSelectionActions = useBoxSelectionActions();
2339
+ const enabled = recoil.useRecoilValue(boxSelectionEnabled);
2340
+ useStackKeyBinding({
2341
+ id: 'ExitBoxSelection',
2342
+ keyBind: 'Escape',
2343
+ fn: boxSelectionActions.disable,
2344
+ addPredicate: () => enabled,
2345
+ cancelPredicate: () => !enabled,
2346
+ }, [enabled]);
2347
+ return (jsxRuntime.jsx(uiReact.VertexTooltip, { content: "Box Selection", children: jsxRuntime.jsx(uiReact.VertexIconButton, { iconName: "box-cursor", iconColor: enabled ? 'primary' : 'secondary', "data-testid": "viewer-toolbar-enable-box-selection-button", onClick: enabled ? boxSelectionActions.disable : boxSelectionActions.enable }) }));
2348
+ };
2349
+
2350
+ const VertexZoomButton = () => {
2351
+ const viewerHTMLElement = recoil.useRecoilValue(viewerElement);
2352
+ const sceneReady = recoil.useRecoilValue(viewerInitialSceneReady);
2353
+ const [activeTool, setActiveTool] = recoil.useRecoilState(viewerPrimaryInteractionType);
2354
+ const [interactionHandler, setInteractionHandler] = recoil.useRecoilState(viewerBaseInteractionHandlerProvider);
2355
+ React.useEffect(() => {
2356
+ if (viewerHTMLElement != null && sceneReady) {
2357
+ setInteractionHandler(viewerHTMLElement.getBaseInteractionHandler());
2358
+ }
2359
+ }, [viewerHTMLElement, sceneReady, setInteractionHandler]);
2360
+ return (jsxRuntime.jsx(uiReact.VertexTooltip, { content: "Zoom", children: jsxRuntime.jsx(uiReact.VertexIconButton, { iconName: "zoom", iconColor: activeTool === 'zoom' ? 'primary' : 'secondary', "data-testid": "viewer-toolbar-zoom-button", onClick: async () => {
2361
+ setActiveTool('zoom');
2362
+ if (interactionHandler != null) {
2363
+ const handler = await interactionHandler;
2364
+ handler === null || handler === void 0 ? void 0 : handler.setPrimaryInteractionType('zoom');
2365
+ }
2366
+ } }) }));
2367
+ };
2368
+
2369
+ const DefaultToolbar = () => {
2370
+ return (jsxRuntime.jsx(viewerReact.VertexViewerToolbar, { placement: 'bottom-center', children: jsxRuntime.jsxs(viewerReact.VertexViewerToolbarGroup, { className: "p-1 border border-neutral-200 rounded bg-neutral-100 opacity-95", children: [jsxRuntime.jsx(VertexRotateButton, {}), jsxRuntime.jsx(VertexPanButton, {}), jsxRuntime.jsx(VertexZoomButton, {}), jsxRuntime.jsx(VertexFitAllButton, {}), jsxRuntime.jsx(VertexViewerToolbarDivider, {}), jsxRuntime.jsx(VertexBoxSelectionButton, {}), jsxRuntime.jsx(VertexViewerToolbarDivider, {}), jsxRuntime.jsx(VertexViewerCrossSectionButton, {})] }) }));
2371
+ };
2372
+
2373
+ const VertexToolbar = ({ children }) => {
2374
+ const hasDefinedChildren = children != null && children !== false;
2375
+ return (jsxRuntime.jsx(viewerReact.VertexViewerToolbar, { placement: 'bottom-center', children: hasDefinedChildren ? (jsxRuntime.jsx(viewerReact.VertexViewerToolbarGroup, { className: "p-1 border border-neutral-200 rounded bg-neutral-100 opacity-95", children: children })) : (jsxRuntime.jsx(DefaultToolbar, {})) }));
2376
+ };
2377
+
2378
+ function useViewerFrameActions() {
2379
+ return {
2380
+ frameDrawn: useActionCallback(({ set, reset, snapshot }) => async (event) => {
2381
+ const currentSummary = await snapshot.getPromise(selectionVisibleSummary);
2382
+ set(viewerFrameScene, event.detail.scene);
2383
+ set(selectionVisibleSummary, event.detail.scene.sceneViewSummary.selectedVisibleSummary);
2384
+ set(selectionPreviousVisibleSummary, currentSummary);
2385
+ }),
2386
+ };
2387
+ }
2388
+
2389
+ function useCallbackRef(props) {
2390
+ var _a;
2391
+ const [element, setElement] = React.useState((_a = void 0 ) !== null && _a !== void 0 ? _a : null);
2392
+ const callback = React.useCallback((node) => setElement(node), []);
2393
+ return {
2394
+ element,
2395
+ callback,
2396
+ };
2397
+ }
2398
+
2399
+ const BoxSelectionTool = () => {
2400
+ const { callback } = useCallbackRef();
2401
+ const boxSelectionActions = useBoxSelectionActions();
2402
+ const operationType = recoil.useRecoilValue(boxSelectionOperationType);
2403
+ useApplyKeyBinding({
2404
+ keyBind: 'Shift',
2405
+ fn: React__namespace.useCallback(() => boxSelectionActions.setOperationType('select'), []),
2406
+ off: React__namespace.useCallback(() => boxSelectionActions.setOperationType('clearAndSelect'), []),
2407
+ });
2408
+ return (jsxRuntime.jsx(viewerReact.VertexViewerBoxQueryTool, { ref: callback, "data-testid": "viewer-box-query-tool", operationType: operationType }));
2409
+ };
2410
+
2411
+ const VertexViewerViewCube = (_a) => {
2412
+ var { placement } = _a, sdkProps = __rest(_a, ["placement"]);
2413
+ return (jsxRuntime.jsx(viewerReact.VertexViewerToolbar, { "data-testid": "view-cube-toolbar", placement: placement !== null && placement !== void 0 ? placement : 'top-right', children: jsxRuntime.jsx(viewerReact.VertexViewerViewCube, Object.assign({ className: "m-7" }, sdkProps)) }));
2414
+ };
2415
+
2416
+ const VertexViewer = (_a) => {
2417
+ var { id, disableSelection, onTap, onLongpress, onFrameDrawn, viewerRefCallback, children } = _a, sdkProps = __rest(_a, ["id", "disableSelection", "onTap", "onLongpress", "onFrameDrawn", "viewerRefCallback", "children"]);
2418
+ const config = recoil.useRecoilValue(sdkConfig);
2419
+ const callbackRef = useRecoilRef({
2420
+ state: viewerElement,
2421
+ });
2422
+ const currentCrossSectionAlignment = recoil.useRecoilValue(crossSectioningAlignment);
2423
+ const [sceneReady, setSceneReady] = recoil.useRecoilState(viewerInitialSceneReady);
2424
+ const isBoxSelectionEnabled = recoil.useRecoilValue(boxSelectionEnabled);
2425
+ const viewerFrameActions = useViewerFrameActions();
2426
+ const hitActions = useHitActions();
2427
+ const selectionActions = useSelectionActions();
2428
+ const hasDefinedChildren = children != null && children !== false;
2429
+ useKeyBindings();
2430
+ return (jsxRuntime.jsxs(viewerReact.VertexViewer, Object.assign({ id: id !== null && id !== void 0 ? id : 'vertex-viewer', "data-testid": "vertex-viewer", ref: viewerRefCallback !== null && viewerRefCallback !== void 0 ? viewerRefCallback : callbackRef, className: classNames('flex w-full h-full', {
2431
+ 'cursor-crosshair': currentCrossSectionAlignment.hitPending,
2432
+ }), config: config, onTap: (e) => {
2433
+ const actions = [
2434
+ ...(disableSelection ? [] : [selectionActions.selectCurrentHit]),
2435
+ ];
2436
+ hitActions.tap(e, ...actions);
2437
+ onTap === null || onTap === void 0 ? void 0 : onTap(e);
2438
+ }, onLongpress: (e) => {
2439
+ hitActions.longPress(e);
2440
+ onLongpress === null || onLongpress === void 0 ? void 0 : onLongpress(e);
2441
+ }, onFrameDrawn: (e) => {
2442
+ viewerFrameActions.frameDrawn(e);
2443
+ onFrameDrawn === null || onFrameDrawn === void 0 ? void 0 : onFrameDrawn(e);
2444
+ } }, sdkProps, { onSceneReady: () => {
2445
+ if (!sceneReady) {
2446
+ setSceneReady(true);
2447
+ }
2448
+ }, children: [isBoxSelectionEnabled && jsxRuntime.jsx(BoxSelectionTool, {}), hasDefinedChildren ? (children) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(VertexViewerViewCube, {}), jsxRuntime.jsx(VertexToolbar, {})] }))] })));
2449
+ };
2450
+
2451
+ function toSdkConfig(config) {
2452
+ if (isOnlyNetworkConfig(config)) {
2453
+ return { network: config };
2454
+ }
2455
+ return config;
2456
+ }
2457
+ function isOnlyNetworkConfig(config) {
2458
+ const asNetworkConfig = config;
2459
+ if (asNetworkConfig.renderingHost != null) {
2460
+ return true;
2461
+ }
2462
+ return false;
2463
+ }
2464
+
2465
+ const VertexViewerToolkitEventListener = ({ onHitStateChange, onSelectionStateChange, }) => {
2466
+ const hitState$1 = recoil.useRecoilValue(hitState);
2467
+ const selectionState$1 = recoil.useRecoilValue(selectionState);
2468
+ React.useEffect(() => {
2469
+ onHitStateChange === null || onHitStateChange === void 0 ? void 0 : onHitStateChange(hitState$1);
2470
+ }, [hitState$1, onHitStateChange]);
2471
+ React.useEffect(() => {
2472
+ onSelectionStateChange === null || onSelectionStateChange === void 0 ? void 0 : onSelectionStateChange(selectionState$1);
2473
+ }, [selectionState$1, onSelectionStateChange]);
2474
+ return jsxRuntime.jsx(jsxRuntime.Fragment, {});
2475
+ };
2476
+
2477
+ const VertexViewerToolkitRoot = (_a) => {
2478
+ var { override, children, config } = _a, eventWrapperProps = __rest(_a, ["override", "children", "config"]);
2479
+ React.useEffect(() => {
2480
+ loader.defineCustomElements();
2481
+ }, []);
2482
+ return (jsxRuntime.jsx(recoil.RecoilRoot, { override: override, children: jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(VertexViewerToolkitEventListener, Object.assign({}, eventWrapperProps)), jsxRuntime.jsx(VertexViewerToolkitRootConfig, { config: config, children: children })] }) }));
2483
+ };
2484
+ const VertexViewerToolkitRootConfig = ({ config, children, }) => {
2485
+ const setSdkConfig = recoil.useSetRecoilState(sdkConfig);
2486
+ React.useEffect(() => {
2487
+ const parsedConfig = config != null ? toSdkConfig(config) : undefined;
2488
+ setSdkConfig(parsedConfig);
2489
+ }, [config, setSdkConfig]);
2490
+ return jsxRuntime.jsx(jsxRuntime.Fragment, { children: children });
2491
+ };
2492
+
2493
+ var index = /*#__PURE__*/Object.freeze({
2494
+ __proto__: null,
2495
+ get AssemblyFontFace () { return AssemblyFontFace; }
2496
+ });
2497
+
2498
+ exports.CrossSection = index$3;
2499
+ exports.Hits = index$2;
2500
+ exports.SceneTree = index;
2501
+ exports.Selection = index$1;
2502
+ exports.VertexContextMenu = VertexContextMenu;
2503
+ exports.VertexFitAllButton = VertexFitAllButton;
2504
+ exports.VertexFitSelectedMenuItem = VertexFitSelectedMenuItem;
2505
+ exports.VertexFlyToMenuItem = VertexFlyToMenuItem;
2506
+ exports.VertexHideAllMenuItem = VertexHideAllMenuItem;
2507
+ exports.VertexHidePartMenuItem = VertexHidePartMenuItem;
2508
+ exports.VertexHideSelectedMenuItem = VertexHideSelectedMenuItem;
2509
+ exports.VertexPanButton = VertexPanButton;
2510
+ exports.VertexRotateButton = VertexRotateButton;
2511
+ exports.VertexSceneTree = VertexSceneTree;
2512
+ exports.VertexSceneTreeContextMenu = VertexSceneTreeContextMenu;
2513
+ exports.VertexSceneTreeTableLayout = VertexSceneTreeTableLayout;
2514
+ exports.VertexShowAllMenuItem = VertexShowAllMenuItem;
2515
+ exports.VertexShowOnlyMenuItem = VertexShowOnlyMenuItem;
2516
+ exports.VertexShowOnlySelectedMenuItem = VertexShowOnlySelectedMenuItem;
2517
+ exports.VertexToolbar = VertexToolbar;
2518
+ exports.VertexViewer = VertexViewer;
2519
+ exports.VertexViewerContextMenu = VertexViewerContextMenu;
2520
+ exports.VertexViewerCrossSectionButton = VertexViewerCrossSectionButton;
2521
+ exports.VertexViewerToolbarDivider = VertexViewerToolbarDivider;
2522
+ exports.VertexViewerToolkitRoot = VertexViewerToolkitRoot;
2523
+ exports.VertexViewerViewCube = VertexViewerViewCube;
2524
+ exports.VertexZoomButton = VertexZoomButton;
2525
+ //# sourceMappingURL=bundle.cjs.js.map