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