@terraware/web-components 5.0.26 → 5.0.27-rc.0

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 (295) hide show
  1. package/components/AntSwitch/index.js +19 -22
  2. package/components/Autocomplete/Autocomplete.js +106 -130
  3. package/components/Badge/index.js +28 -29
  4. package/components/BusySpinner/index.js +40 -45
  5. package/components/Button/Button.js +41 -47
  6. package/components/Checkbox.js +64 -63
  7. package/components/Confirm/index.js +74 -83
  8. package/components/DatePicker/DatePicker.js +138 -141
  9. package/components/DialogBox/DialogBox.js +111 -118
  10. package/components/Divisor.js +18 -18
  11. package/components/Dropdown.js +66 -94
  12. package/components/EditableTable/index.js +211 -271
  13. package/components/ErrorBox/ErrorBox.js +47 -49
  14. package/components/FileChooser/index.js +117 -135
  15. package/components/FormBottomBar/index.js +75 -84
  16. package/components/Icon/Icon.js +12 -20
  17. package/components/Icon/icons/index.js +256 -256
  18. package/components/IconTooltip/index.js +49 -60
  19. package/components/Map/MapBox.js +545 -452
  20. package/components/Map/MapContainer.js +25 -29
  21. package/components/Map/MapDateSliderControl.js +64 -64
  22. package/components/Map/MapDrawer.js +41 -47
  23. package/components/Map/MapLegend.js +176 -172
  24. package/components/Map/MapViewStyleControl.js +59 -63
  25. package/components/Map/ReactMapboxWrapper.js +3 -2
  26. package/components/Map/index.js +161 -180
  27. package/components/Map/types.js +12 -17
  28. package/components/Map/useMaintainLayerOrder.js +16 -26
  29. package/components/Markdown/index.js +25 -26
  30. package/components/Message/Message.js +73 -76
  31. package/components/MultiSelect/index.js +112 -124
  32. package/components/Navbar/NavFooter.js +9 -13
  33. package/components/Navbar/NavItem.js +70 -76
  34. package/components/Navbar/NavSection.js +17 -19
  35. package/components/Navbar/Navbar.js +39 -46
  36. package/components/Navbar/SubNavbar.js +9 -13
  37. package/components/Note.js +18 -22
  38. package/components/OverlayModal/OverlayModal.js +56 -63
  39. package/components/PageForm/index.js +33 -41
  40. package/components/PhotoChooser/index.js +165 -173
  41. package/components/PhotosCarousel/PhotoSlide.js +32 -35
  42. package/components/PhotosCarousel/index.js +80 -85
  43. package/components/PhotosCarousel/usePhotosCarousel.js +69 -76
  44. package/components/Pill/index.js +41 -45
  45. package/components/PillList/index.js +17 -24
  46. package/components/PlacementWrapper/index.js +38 -59
  47. package/components/PopoverMenu/Popover.js +68 -85
  48. package/components/PopoverMenu/index.js +51 -56
  49. package/components/PopoverMultiSelect/index.js +52 -59
  50. package/components/ProgressCircle/ProgressCircle.js +28 -32
  51. package/components/RadioButton.js +17 -19
  52. package/components/Select/Select.js +13 -15
  53. package/components/Select/SelectT.js +235 -250
  54. package/components/Separator/index.js +14 -17
  55. package/components/Size.js +0 -1
  56. package/components/Slider/index.js +43 -56
  57. package/components/SummaryBox.js +70 -72
  58. package/components/Tabs/index.js +133 -135
  59. package/components/TextTruncated/index.js +117 -130
  60. package/components/Textfield/Textfield.js +158 -190
  61. package/components/Textfield/TruncatedTextArea.js +70 -84
  62. package/components/TimelineSlider/TimelineMarker.js +25 -30
  63. package/components/TimelineSlider/TimelineRail.js +8 -11
  64. package/components/TimelineSlider/index.js +64 -66
  65. package/components/TimelineSliderV2/ClusterBadge.js +25 -29
  66. package/components/TimelineSliderV2/TimelineDot.js +31 -35
  67. package/components/TimelineSliderV2/TimelineRail.js +8 -11
  68. package/components/TimelineSliderV2/index.js +121 -103
  69. package/components/TimelineSliderV2/layout.js +124 -147
  70. package/components/TimelineSliderV2/types.js +0 -1
  71. package/components/Tooltip/Tooltip.js +47 -56
  72. package/components/ViewPhotosDialog/index.js +28 -35
  73. package/components/VirtualWalkthrough/Annotation.js +163 -207
  74. package/components/VirtualWalkthrough/AnnotationEditPane.js +198 -184
  75. package/components/VirtualWalkthrough/AnnotationPanel.js +187 -199
  76. package/components/VirtualWalkthrough/Application.js +31 -34
  77. package/components/VirtualWalkthrough/AutoRotator.js +85 -223
  78. package/components/VirtualWalkthrough/BlockingSpinner.js +26 -28
  79. package/components/VirtualWalkthrough/BoundaryRing.js +30 -34
  80. package/components/VirtualWalkthrough/BoundaryWall.js +32 -36
  81. package/components/VirtualWalkthrough/CameraInfo.js +100 -89
  82. package/components/VirtualWalkthrough/ControlsInfoPane.js +254 -195
  83. package/components/VirtualWalkthrough/GradientSky.js +72 -73
  84. package/components/VirtualWalkthrough/SplatControls.js +209 -221
  85. package/components/VirtualWalkthrough/SplatCrop.js +21 -20
  86. package/components/VirtualWalkthrough/SplatFadeCrop.js +21 -20
  87. package/components/VirtualWalkthrough/SplatModel.js +65 -85
  88. package/components/VirtualWalkthrough/SplatRevealRain.js +41 -46
  89. package/components/VirtualWalkthrough/TfAnnotationManager.js +197 -379
  90. package/components/VirtualWalkthrough/TfXrNavigation.js +73 -162
  91. package/components/VirtualWalkthrough/VirtualWalkthroughViewer.js +438 -431
  92. package/components/VirtualWalkthrough/VrAnnotationPanel.js +18 -22
  93. package/components/VirtualWalkthrough/XrAnnotationInteraction.js +14 -17
  94. package/components/VirtualWalkthrough/XrExitButton.js +14 -17
  95. package/components/VirtualWalkthrough/XrGazeDwell.js +14 -17
  96. package/components/VirtualWalkthrough/XrPointerRay.js +14 -17
  97. package/components/VirtualWalkthrough/XrStartPose.js +67 -121
  98. package/components/VirtualWalkthrough/boundary-ring.js +87 -117
  99. package/components/VirtualWalkthrough/boundary-wall.js +175 -258
  100. package/components/VirtualWalkthrough/camera-adoption.js +18 -64
  101. package/components/VirtualWalkthrough/groundPlane.js +32 -47
  102. package/components/VirtualWalkthrough/gsplat-fade-crop-effect.js +35 -35
  103. package/components/VirtualWalkthrough/splatFormat.js +13 -47
  104. package/components/VirtualWalkthrough/useAdoptedCamera.js +23 -44
  105. package/components/VirtualWalkthrough/useRestartOnVr.js +26 -37
  106. package/components/VirtualWalkthrough/useXrRenderTuning.js +33 -54
  107. package/components/VirtualWalkthrough/vr-annotation-panel-layout.js +67 -99
  108. package/components/VirtualWalkthrough/vr-annotation-panel.js +403 -433
  109. package/components/VirtualWalkthrough/vr-annotation-scroll.js +10 -28
  110. package/components/VirtualWalkthrough/walkthrough-camera.js +204 -329
  111. package/components/VirtualWalkthrough/xr-annotation-candidate-buffer.js +39 -80
  112. package/components/VirtualWalkthrough/xr-annotation-candidates.js +6 -18
  113. package/components/VirtualWalkthrough/xr-annotation-interaction.js +31 -56
  114. package/components/VirtualWalkthrough/xr-annotation-targeting.js +22 -38
  115. package/components/VirtualWalkthrough/xr-button-arming.js +7 -17
  116. package/components/VirtualWalkthrough/xr-button-hold.js +22 -36
  117. package/components/VirtualWalkthrough/xr-exit-button-mask.js +22 -36
  118. package/components/VirtualWalkthrough/xr-exit-button.js +148 -207
  119. package/components/VirtualWalkthrough/xr-face-buttons.js +19 -29
  120. package/components/VirtualWalkthrough/xr-gaze-dwell-state.js +23 -34
  121. package/components/VirtualWalkthrough/xr-gaze-dwell.js +107 -144
  122. package/components/VirtualWalkthrough/xr-interactive-ui.js +12 -24
  123. package/components/VirtualWalkthrough/xr-pointer-ray.js +13 -20
  124. package/components/VirtualWalkthrough/xr-progress-pie-material.js +117 -88
  125. package/components/VirtualWalkthrough/xr-progress-pie.js +1 -10
  126. package/components/VirtualWalkthrough/xr-ray-quad.js +16 -30
  127. package/components/VirtualWalkthrough/xr-render-tuning.js +17 -48
  128. package/components/VirtualWalkthrough/xr-scene-bounds.js +21 -37
  129. package/components/VirtualWalkthrough/xr-start-pose.js +27 -73
  130. package/components/VirtualWalkthrough/xr-teleport-gesture.js +16 -42
  131. package/components/svg/BlobbyGrayIconImage.js +41 -39
  132. package/components/svg/BlobbyGrayIconImport.js +41 -39
  133. package/components/svg/BlobbyGrayIconUploadToTheCloud.js +41 -39
  134. package/components/svg/BlobbyIconCloudChecked.js +202 -164
  135. package/components/svg/BlobbyIconExportOrUploadPhoto.js +202 -164
  136. package/components/svg/BlobbyIconFolder.js +202 -164
  137. package/components/svg/BlobbyIconGraphReport.js +202 -164
  138. package/components/svg/BlobbyIconHappy.js +202 -164
  139. package/components/svg/BlobbyIconHeartMonitor.js +202 -164
  140. package/components/svg/BlobbyIconLeaf.js +202 -164
  141. package/components/svg/BlobbyIconLibrary.js +202 -164
  142. package/components/svg/BlobbyIconNursery.js +204 -166
  143. package/components/svg/BlobbyIconOrganization.js +202 -164
  144. package/components/svg/BlobbyIconParchment.js +202 -164
  145. package/components/svg/BlobbyIconPeople.js +202 -164
  146. package/components/svg/BlobbyIconSeedBank.js +202 -164
  147. package/components/svg/BlobbyIconSeedling.js +202 -164
  148. package/components/svg/BlobbyIconSeeds.js +202 -164
  149. package/components/svg/BlobbyIconSite.js +206 -167
  150. package/components/svg/BlobbyIconSparkles.js +202 -164
  151. package/components/svg/BlobbyIconWrench.js +202 -164
  152. package/components/svg/Bug.js +19 -21
  153. package/components/svg/Calendar.js +19 -21
  154. package/components/svg/CaretDown.js +19 -21
  155. package/components/svg/CaretLeft.js +19 -21
  156. package/components/svg/CaretRight.js +19 -21
  157. package/components/svg/CaretUp.js +19 -21
  158. package/components/svg/ChevronDown.js +19 -21
  159. package/components/svg/ChevronUp.js +19 -21
  160. package/components/svg/Close.js +19 -21
  161. package/components/svg/Critical.js +19 -21
  162. package/components/svg/Edit.js +202 -164
  163. package/components/svg/Error.js +19 -21
  164. package/components/svg/Filter.js +19 -21
  165. package/components/svg/Folder.js +19 -21
  166. package/components/svg/Help.js +19 -21
  167. package/components/svg/Home.js +19 -21
  168. package/components/svg/IconAdd.js +19 -21
  169. package/components/svg/IconArrowRight.js +21 -23
  170. package/components/svg/IconBusinessNetwork.js +19 -21
  171. package/components/svg/IconCalendar.js +19 -21
  172. package/components/svg/IconCancel.js +19 -21
  173. package/components/svg/IconChargingBattery.js +19 -21
  174. package/components/svg/IconChecklist.js +21 -23
  175. package/components/svg/IconCheckmark.js +19 -21
  176. package/components/svg/IconCoinInHand.js +19 -21
  177. package/components/svg/IconColumns.js +19 -21
  178. package/components/svg/IconComment.js +20 -22
  179. package/components/svg/IconDashboard.js +19 -21
  180. package/components/svg/IconDataMigration.js +19 -21
  181. package/components/svg/IconDocument.js +21 -23
  182. package/components/svg/IconDownloadFromTheCloud.js +21 -23
  183. package/components/svg/IconEdit.js +21 -23
  184. package/components/svg/IconExpand.js +21 -23
  185. package/components/svg/IconExport.js +19 -21
  186. package/components/svg/IconExternalLink.js +19 -21
  187. package/components/svg/IconEye.js +19 -21
  188. package/components/svg/IconEyeOff.js +19 -21
  189. package/components/svg/IconFile.js +19 -21
  190. package/components/svg/IconFolder.js +19 -21
  191. package/components/svg/IconFullScreen.js +20 -22
  192. package/components/svg/IconFutures.js +19 -21
  193. package/components/svg/IconGraphReport.js +19 -21
  194. package/components/svg/IconGrid.js +19 -21
  195. package/components/svg/IconHeartMonitor.js +19 -21
  196. package/components/svg/IconHelp.js +21 -23
  197. package/components/svg/IconHistory.js +19 -21
  198. package/components/svg/IconImport.js +19 -21
  199. package/components/svg/IconIndex.js +19 -21
  200. package/components/svg/IconLayers.js +19 -21
  201. package/components/svg/IconLibrary.js +20 -22
  202. package/components/svg/IconList.js +19 -21
  203. package/components/svg/IconLivePlant.js +19 -21
  204. package/components/svg/IconLocations.js +19 -21
  205. package/components/svg/IconMail.js +19 -21
  206. package/components/svg/IconManager.js +19 -21
  207. package/components/svg/IconMap.js +19 -21
  208. package/components/svg/IconMarker.js +19 -21
  209. package/components/svg/IconMenu.js +19 -21
  210. package/components/svg/IconMenuHorizontal.js +19 -21
  211. package/components/svg/IconModule.js +19 -21
  212. package/components/svg/IconMyLocation.js +19 -21
  213. package/components/svg/IconNote.js +21 -23
  214. package/components/svg/IconNursery.js +21 -23
  215. package/components/svg/IconOrg.js +19 -21
  216. package/components/svg/IconParchment.js +19 -21
  217. package/components/svg/IconPhoto.js +19 -21
  218. package/components/svg/IconPlantsFilled.js +19 -21
  219. package/components/svg/IconRedo.js +19 -21
  220. package/components/svg/IconRestorationSite.js +19 -21
  221. package/components/svg/IconSeedBank.js +19 -21
  222. package/components/svg/IconSeedling.js +19 -21
  223. package/components/svg/IconSettings.js +19 -21
  224. package/components/svg/IconSlice.js +19 -21
  225. package/components/svg/IconSubmit.js +19 -21
  226. package/components/svg/IconSubtract.js +21 -23
  227. package/components/svg/IconSynced.js +19 -21
  228. package/components/svg/IconTrashCan.js +19 -21
  229. package/components/svg/IconTreasureMap.js +19 -21
  230. package/components/svg/IconUnavailable.js +19 -21
  231. package/components/svg/IconUndo.js +19 -21
  232. package/components/svg/IconVariable.js +19 -21
  233. package/components/svg/IconVideo.js +30 -31
  234. package/components/svg/IconWifi.js +19 -21
  235. package/components/svg/Info.js +19 -21
  236. package/components/svg/Key.js +19 -21
  237. package/components/svg/Leaf.js +19 -21
  238. package/components/svg/Lock.js +19 -21
  239. package/components/svg/Logo.js +48 -43
  240. package/components/svg/MenuVertical.js +19 -21
  241. package/components/svg/Notification.js +19 -21
  242. package/components/svg/Person.js +19 -21
  243. package/components/svg/Plus.js +19 -21
  244. package/components/svg/RestorationSite.js +19 -21
  245. package/components/svg/Search.js +19 -21
  246. package/components/svg/Seeds.js +19 -21
  247. package/components/svg/Site.js +19 -21
  248. package/components/svg/Sparkles.js +19 -21
  249. package/components/svg/Species.js +19 -21
  250. package/components/svg/Species2.js +202 -164
  251. package/components/svg/Spinner.js +54 -48
  252. package/components/svg/Star.js +19 -21
  253. package/components/svg/Success.js +19 -21
  254. package/components/svg/SuccessFilled.js +19 -21
  255. package/components/svg/TerrawareLogoDesktop.js +55 -49
  256. package/components/svg/TerrawareLogoMobile.js +25 -26
  257. package/components/svg/Touchscreen.js +19 -21
  258. package/components/svg/UploadCloud.js +202 -164
  259. package/components/svg/Warning.js +19 -21
  260. package/components/svg/WelcomeClipboard.js +156 -133
  261. package/components/svg/index.js +129 -129
  262. package/components/table/EnhancedTableToolbar.js +68 -74
  263. package/components/table/EnhancedTableToolbarV2.js +31 -41
  264. package/components/table/TableCellRenderer.js +196 -231
  265. package/components/table/TableHeader.js +71 -73
  266. package/components/table/TableHeaderItem.js +97 -102
  267. package/components/table/density.js +18 -19
  268. package/components/table/index.js +350 -369
  269. package/components/table/sort.js +27 -52
  270. package/components/table/types.js +0 -1
  271. package/components/types/index.js +0 -1
  272. package/hooks/cameraEntityContext.js +2 -13
  273. package/hooks/useBoolean.js +17 -13
  274. package/hooks/useCameraPosition.js +56 -56
  275. package/hooks/useDevicePerformance.js +17 -17
  276. package/hooks/useXr.js +85 -87
  277. package/index.js +58 -58
  278. package/package.json +1 -1
  279. package/setupTests.js +0 -4
  280. package/strings/export.js +111 -170
  281. package/strings/index.js +25 -54
  282. package/strings/locales.js +21 -41
  283. package/strings/strings-en.js +49 -51
  284. package/strings/strings-es.js +49 -51
  285. package/strings/strings-fr.js +49 -51
  286. package/strings/strings-gx.js +49 -51
  287. package/style-dictionary-dist/TerrawareTheme.js +186 -186
  288. package/theme.js +74 -76
  289. package/utils/color.js +8 -15
  290. package/utils/date.js +42 -108
  291. package/utils/index.js +4 -4
  292. package/utils/preventDefaultEvent.js +3 -3
  293. package/utils/text.js +11 -11
  294. package/utils/useDeviceInfo.js +42 -79
  295. package/virtualWalkthrough.js +23 -23
@@ -1,400 +1,218 @@
1
- import { createElement } from 'react';
2
- import { renderToStaticMarkup } from 'react-dom/server';
3
- import { Texture, FILTER_LINEAR, PIXELFORMAT_RGBA8 } from 'playcanvas';
4
- import { AnnotationManager } from 'playcanvas/scripts/esm/annotations.mjs';
5
- import Icon from '../Icon/Icon.js';
6
-
7
- // Maps each annotation icon type to a @terraware/web-components icon.
1
+ import { createElement } from "react";
2
+ import { renderToStaticMarkup } from "react-dom/server";
3
+ import { FILTER_LINEAR, PIXELFORMAT_RGBA8, Texture } from "playcanvas";
4
+ import { AnnotationManager } from "playcanvas/scripts/esm/annotations.mjs";
5
+ import Icon from "../Icon/Icon.js";
8
6
  const ANNOTATION_ICONS = {
9
- text: 'iconComment',
10
- image: 'iconPhoto',
11
- video: 'iconVideo'
7
+ text: 'iconComment',
8
+ image: 'iconPhoto',
9
+ video: 'iconVideo'
12
10
  };
13
-
14
- // Pixel dimensions of the (square) hotspot texture canvas at rest.
15
11
  const HOTSPOT_TEXTURE_SIZE = 64;
16
-
17
- // How much larger the hotspot appears while hovered.
18
12
  const HOTSPOT_HOVER_SCALE = 1.3;
19
-
20
- /**
21
- * Extended AnnotationManager that adds max size clamping for annotations
22
- * and fixes positioning when canvas is not at document origin.
23
- */
24
13
  class TfAnnotationManager extends AnnotationManager {
25
- static scriptName = 'tfAnnotationManager';
26
- _maxWorldSize = 1.0;
27
- _customParentDom = null;
28
- _clickHandlersAttached = new Set();
29
- _hotspotBackgroundColor = '#2C8658';
30
- _iconImages = new Map();
31
-
32
- /**
33
- * Maximum world-space size for annotations.
34
- * This prevents annotations from growing beyond this size regardless of camera distance.
35
- *
36
- * @attribute
37
- * @title Max World Size
38
- * @type {number}
39
- * @default 1.0
40
- */
41
- set maxWorldSize(value) {
42
- this._maxWorldSize = value;
43
- }
44
- get maxWorldSize() {
45
- return this._maxWorldSize;
46
- }
47
-
48
- /**
49
- * Background color for the hotspot texture canvas (the circle fill color).
50
- * This is a hex color string (e.g., '#2C8658') that's drawn on the texture canvas.
51
- * Note: This is different from hotspotColor and hoverColor, which are PlayCanvas Color objects
52
- * used for the material's emissive property.
53
- *
54
- * @attribute
55
- * @title Hotspot Background Color
56
- * @type {string}
57
- * @default '#2C8658'
58
- */
59
- set hotspotBackgroundColor(value) {
60
- this._hotspotBackgroundColor = value;
61
- }
62
- get hotspotBackgroundColor() {
63
- return this._hotspotBackgroundColor;
64
- }
65
-
66
- /**
67
- * Initialize with custom parent DOM if needed.
68
- */
69
- initialize() {
70
- // Find a container div with data-annotation-container attribute
71
- const canvas = this.app.graphicsDevice.canvas;
72
- const container = canvas.parentElement?.querySelector('[data-annotation-container]');
73
- if (container) {
74
- this._customParentDom = container;
75
- this._parentDom = container;
14
+ static scriptName = 'tfAnnotationManager';
15
+ _maxWorldSize = 1.0;
16
+ _customParentDom = null;
17
+ _clickHandlersAttached = new Set();
18
+ _hotspotBackgroundColor = '#2C8658';
19
+ _iconImages = new Map();
20
+ set maxWorldSize(value) {
21
+ this._maxWorldSize = value;
76
22
  }
77
-
78
- // Call parent initialize
79
- super.initialize();
80
- this._loadIconImages();
81
- }
82
-
83
- /**
84
- * Renders each web-components icon to an SVG image once. When an image finishes
85
- * loading, any annotations already using that icon are redrawn.
86
- * @private
87
- */
88
- _loadIconImages() {
89
- const fillColor = this._hotspotColor?.toString() ?? '#ffffff';
90
- Object.keys(ANNOTATION_ICONS).forEach(iconType => {
91
- const name = ANNOTATION_ICONS[iconType];
92
- const markup = renderToStaticMarkup(/*#__PURE__*/createElement(Icon, {
93
- name,
94
- fillColor
95
- })).replace(/^<svg/, '<svg width="128" height="128"');
96
- const image = new Image();
97
- image.onload = () => this._refreshIcon(iconType);
98
- image.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(markup)}`;
99
- this._iconImages.set(iconType, image);
100
- });
101
- }
102
-
103
- /**
104
- * Redraws the texture for all annotations using the given icon, once its image
105
- * has loaded.
106
- * @private
107
- */
108
- _refreshIcon(iconType) {
109
- const annotationResources = this._annotationResources;
110
- if (!annotationResources) {
111
- return;
23
+ get maxWorldSize() {
24
+ return this._maxWorldSize;
112
25
  }
113
- annotationResources.forEach((_resources, annotation) => {
114
- if ((annotation.icon ?? 'text') === iconType) {
115
- this._applyAnnotationIcon(annotation);
116
- }
117
- });
118
- }
119
-
120
- /**
121
- * Update loop to attach click handlers to annotations and manage visibility.
122
- */
123
- update() {
124
- const annotationResources = this._annotationResources;
125
- if (!annotationResources) {
126
- return;
26
+ set hotspotBackgroundColor(value) {
27
+ this._hotspotBackgroundColor = value;
28
+ }
29
+ get hotspotBackgroundColor() {
30
+ return this._hotspotBackgroundColor;
127
31
  }
128
-
129
- // Attach click handlers and manage visibility for all annotations
130
- annotationResources.forEach((resources, annotation) => {
131
- if (resources && resources.hotspotDom) {
132
- // Attach click handler if not already attached
133
- if (!this._clickHandlersAttached.has(annotation)) {
134
- const callback = annotation.onClickCallback;
135
- if (callback) {
136
- this._clickHandlersAttached.add(annotation);
137
- resources.hotspotDom.addEventListener('pointerdown', e => {
138
- callback(e.clientX, e.clientY);
139
- });
140
- }
32
+ initialize() {
33
+ const canvas = this.app.graphicsDevice.canvas;
34
+ const container = canvas.parentElement?.querySelector('[data-annotation-container]');
35
+ if (container) {
36
+ this._customParentDom = container;
37
+ this._parentDom = container;
141
38
  }
142
-
143
- // Disable/enable the entity itself based on visibility
144
- const isVisible = annotation.enabled !== undefined ? annotation.enabled : true;
145
-
146
- // Hide tooltip if the active annotation is hidden
147
- if (this._activeAnnotation === annotation && !isVisible) {
148
- this._tooltipDom.style.visibility = 'hidden';
149
- this._tooltipDom.style.opacity = '0';
150
- this._activeAnnotation = null;
39
+ super.initialize();
40
+ this._loadIconImages();
41
+ }
42
+ _loadIconImages() {
43
+ const fillColor = this._hotspotColor?.toString() ?? '#ffffff';
44
+ Object.keys(ANNOTATION_ICONS).forEach((iconType)=>{
45
+ const name = ANNOTATION_ICONS[iconType];
46
+ const markup = renderToStaticMarkup(createElement(Icon, {
47
+ name,
48
+ fillColor
49
+ })).replace(/^<svg/, '<svg width="128" height="128"');
50
+ const image = new Image();
51
+ image.onload = ()=>this._refreshIcon(iconType);
52
+ image.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(markup)}`;
53
+ this._iconImages.set(iconType, image);
54
+ });
55
+ }
56
+ _refreshIcon(iconType) {
57
+ const annotationResources = this._annotationResources;
58
+ if (!annotationResources) return;
59
+ annotationResources.forEach((_resources, annotation)=>{
60
+ if ((annotation.icon ?? 'text') === iconType) this._applyAnnotationIcon(annotation);
61
+ });
62
+ }
63
+ update() {
64
+ const annotationResources = this._annotationResources;
65
+ if (!annotationResources) return;
66
+ annotationResources.forEach((resources, annotation)=>{
67
+ if (resources && resources.hotspotDom) {
68
+ if (!this._clickHandlersAttached.has(annotation)) {
69
+ const callback = annotation.onClickCallback;
70
+ if (callback) {
71
+ this._clickHandlersAttached.add(annotation);
72
+ resources.hotspotDom.addEventListener('pointerdown', (e)=>{
73
+ callback(e.clientX, e.clientY);
74
+ });
75
+ }
76
+ }
77
+ const isVisible = void 0 !== annotation.enabled ? annotation.enabled : true;
78
+ if (this._activeAnnotation === annotation && !isVisible) {
79
+ this._tooltipDom.style.visibility = 'hidden';
80
+ this._tooltipDom.style.opacity = '0';
81
+ this._activeAnnotation = null;
82
+ }
83
+ }
84
+ });
85
+ }
86
+ _registerAnnotation(annotation) {
87
+ super._registerAnnotation(annotation);
88
+ this._applyAnnotationIcon(annotation);
89
+ const resources = this._annotationResources.get(annotation);
90
+ if (resources) {
91
+ resources.baseEntity.enabled = false;
92
+ resources.overlayEntity.enabled = false;
151
93
  }
152
- }
153
- });
154
- }
155
-
156
- /**
157
- * Override annotation registration to draw the media icon onto the hotspot
158
- * texture once the annotation (and its icon) is available.
159
- * @private
160
- */
161
- _registerAnnotation(annotation) {
162
- super._registerAnnotation(annotation);
163
- this._applyAnnotationIcon(annotation);
164
-
165
- // Keep the hotspot mesh hidden until the first scale pass sizes it. The annotation entity
166
- // starts at unit scale, so the plane would render a metre across for the frames before
167
- // _updateAnnotationRotationAndScale runs.
168
- const resources = this._annotationResources.get(annotation);
169
- if (resources) {
170
- resources.baseEntity.enabled = false;
171
- resources.overlayEntity.enabled = false;
172
94
  }
173
- }
174
-
175
- /**
176
- * Recreates the hotspot texture with the appropriate media icon drawn on it.
177
- * @private
178
- */
179
- _applyAnnotationIcon(annotation) {
180
- const resources = this._annotationResources.get(annotation);
181
- if (!resources) {
182
- return;
95
+ _applyAnnotationIcon(annotation) {
96
+ const resources = this._annotationResources.get(annotation);
97
+ if (!resources) return;
98
+ const icon = annotation.icon ?? 'text';
99
+ resources.texture.destroy();
100
+ resources.texture = this._createHotspotTexture(annotation.label, HOTSPOT_TEXTURE_SIZE, 6, icon);
101
+ resources.materials.forEach((material)=>{
102
+ material.emissiveMap = resources.texture;
103
+ material.opacityMap = resources.texture;
104
+ material.update();
105
+ });
183
106
  }
184
- const icon = annotation.icon ?? 'text';
185
- resources.texture.destroy();
186
- resources.texture = this._createHotspotTexture(annotation.label, HOTSPOT_TEXTURE_SIZE, 6, icon);
187
- resources.materials.forEach(material => {
188
- material.emissiveMap = resources.texture;
189
- material.opacityMap = resources.texture;
190
- material.update();
191
- });
192
- }
193
-
194
- /**
195
- * Draws the icon image onto the hotspot texture canvas, scaled to fit the
196
- * circle. Does nothing if the image has not finished loading yet; the
197
- * annotation is redrawn once it has (see `_refreshIcon`).
198
- * @private
199
- */
200
- _drawIcon(ctx, icon, size) {
201
- const image = this._iconImages.get(icon);
202
- if (!image || !image.complete || image.naturalWidth === 0) {
203
- return;
107
+ _drawIcon(ctx, icon, size) {
108
+ const image = this._iconImages.get(icon);
109
+ if (!image || !image.complete || 0 === image.naturalWidth) return;
110
+ const iconSize = 0.5 * size;
111
+ const offset = (size - iconSize) / 2;
112
+ ctx.drawImage(image, offset, offset, iconSize, iconSize);
204
113
  }
205
- const iconSize = size * 0.5;
206
- const offset = (size - iconSize) / 2;
207
- ctx.drawImage(image, offset, offset, iconSize, iconSize);
208
- }
209
-
210
- /**
211
- * Keep the media icon drawn on the hotspot texture. The label text itself
212
- * is not rendered on the hotspot, so we simply redraw with the icon.
213
- * @private
214
- */
215
- _onLabelChange(annotation) {
216
- this._applyAnnotationIcon(annotation);
217
- }
218
-
219
- /**
220
- * The hotspot colors are baked into the texture in `_createHotspotTexture`,
221
- * so the material emissive is kept white to let those colors render as drawn.
222
- * @private
223
- */
224
- _createHotspotMaterial(texture, options) {
225
- const material = super._createHotspotMaterial(texture, options);
226
- material.emissive.set(1, 1, 1);
227
- material.update();
228
- return material;
229
- }
230
-
231
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
232
- _setAnnotationHover(_annotation, _hover) {
233
- // No-op: the hover growth is handled in _updateAnnotationRotationAndScale
234
- // (via _hoverAnnotation); the texture is identical in both states.
235
- }
236
- _updateAllAnnotationColors() {
237
- // No-op: hotspot colors are baked into the texture (see _createHotspotMaterial).
238
- }
239
-
240
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
241
- _showTooltip(_annotation) {
242
- // No-op: AnnotationPanel handles all annotation UI, this removes default behavior
243
- }
244
-
245
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
246
- _onTextChange(_annotation, _text) {
247
- // No-op: AnnotationPanel handles all annotation UI, this removes default behavior
248
- }
249
-
250
- /**
251
- * Override position update to adjust for canvas offset and respect visibility.
252
- * @private
253
- */
254
- _updateAnnotationPositions(annotation, resources, screenPos) {
255
- const canvas = this.app.graphicsDevice.canvas;
256
- const rect = canvas.getBoundingClientRect();
257
-
258
- // If using a custom parent (positioned container), use raw screen coordinates
259
- // Otherwise, adjust for canvas position in document
260
- const offsetX = this._customParentDom ? 0 : rect.left + window.scrollX;
261
- const offsetY = this._customParentDom ? 0 : rect.top + window.scrollY;
262
-
263
- // Check if annotation is visible (defaults to true if not specified)
264
- const isVisible = annotation.enabled !== undefined ? annotation.enabled : true;
265
- resources.hotspotDom.style.display = isVisible ? 'block' : 'none';
266
- resources.hotspotDom.style.left = `${screenPos.x + offsetX}px`;
267
- resources.hotspotDom.style.top = `${screenPos.y + offsetY}px`;
268
- if (this._activeAnnotation === annotation) {
269
- this._tooltipDom.style.display = isVisible ? 'block' : 'none';
270
- this._tooltipDom.style.left = `${screenPos.x + offsetX}px`;
271
- this._tooltipDom.style.top = `${screenPos.y + offsetY}px`;
114
+ _onLabelChange(annotation) {
115
+ this._applyAnnotationIcon(annotation);
272
116
  }
273
- if (annotation.onScreenPositionUpdateCallback) {
274
- // Rendered hotspot diameter (px), set by _updateAnnotationRotationAndScale.
275
- // Parsed from the style rather than measured to avoid forcing a reflow.
276
- const hotspotSize = parseFloat(resources.hotspotDom.style.width) || undefined;
277
- annotation.onScreenPositionUpdateCallback(screenPos.x + offsetX, screenPos.y + offsetY, hotspotSize);
117
+ _createHotspotMaterial(texture, options) {
118
+ const material = super._createHotspotMaterial(texture, options);
119
+ material.emissive.set(1, 1, 1);
120
+ material.update();
121
+ return material;
278
122
  }
279
- }
280
-
281
- /**
282
- * Override to also disable the hotspot mesh (not just the DOM) when the annotation is behind the
283
- * camera. The base implementation leaves the mesh enabled, so a not-yet-scaled plane would stay
284
- * in view at its full unit size until the camera moves the anchor in front.
285
- * @private
286
- */
287
- _hideAnnotationElements(annotation, resources) {
288
- super._hideAnnotationElements(annotation, resources);
289
- resources.baseEntity.enabled = false;
290
- resources.overlayEntity.enabled = false;
291
- }
292
-
293
- /**
294
- * Override the scale update to clamp the world size to a maximum value.
295
- * Also scales the hotspot DOM element to match.
296
- * @private
297
- */
298
- _updateAnnotationRotationAndScale(annotation) {
299
- const cameraRotation = this._camera.getRotation();
300
- annotation.entity.setRotation(cameraRotation);
301
- annotation.entity.rotateLocal(90, 0, 0);
302
- const cameraPos = this._camera.getPosition();
303
- const distance = annotation.entity.getPosition().distance(cameraPos);
304
- const canvas = this.app.graphicsDevice.canvas;
305
- const screenHeight = canvas.clientHeight;
306
- const projMatrix = this._camera.camera.projectionMatrix;
307
-
308
- // The world size that projects to _hotspotSize screen pixels at this distance.
309
- const targetWorldSize = this._hotspotSize / screenHeight * (2 * distance / projMatrix.data[5]);
310
-
311
- // Annotations hang off an unscaled parent, so local scale is world scale and maxWorldSize
312
- // clamps the hotspot at a size in metres.
313
- const clampedScale = Math.min(targetWorldSize, this._maxWorldSize);
314
- const hovered = this._hoverAnnotation === annotation;
315
- const hoverScale = hovered ? HOTSPOT_HOVER_SCALE : 1;
316
- const entityScale = clampedScale * hoverScale;
317
- annotation.entity.setLocalScale(entityScale, entityScale, entityScale);
318
-
319
- // Scale the hotspot DOM element proportionally when clamped, including the
320
- // hover growth so it tracks the visible circle. This is a pure screen-space
321
- // size, so it uses the true _hotspotSize and the clamp ratio only.
322
- const resources = this._annotationResources.get(annotation);
323
- if (resources) {
324
- // This branch only runs for annotations in front of the camera, and the scale above is now
325
- // clamped, so it is safe to reveal the mesh (hidden on register / when behind the camera).
326
- resources.baseEntity.enabled = true;
327
- resources.overlayEntity.enabled = true;
123
+ _setAnnotationHover(_annotation, _hover) {}
124
+ _updateAllAnnotationColors() {}
125
+ _showTooltip(_annotation) {}
126
+ _onTextChange(_annotation, _text) {}
127
+ _updateAnnotationPositions(annotation, resources, screenPos) {
128
+ const canvas = this.app.graphicsDevice.canvas;
129
+ const rect = canvas.getBoundingClientRect();
130
+ const offsetX = this._customParentDom ? 0 : rect.left + window.scrollX;
131
+ const offsetY = this._customParentDom ? 0 : rect.top + window.scrollY;
132
+ const isVisible = void 0 !== annotation.enabled ? annotation.enabled : true;
133
+ resources.hotspotDom.style.display = isVisible ? 'block' : 'none';
134
+ resources.hotspotDom.style.left = `${screenPos.x + offsetX}px`;
135
+ resources.hotspotDom.style.top = `${screenPos.y + offsetY}px`;
136
+ if (this._activeAnnotation === annotation) {
137
+ this._tooltipDom.style.display = isVisible ? 'block' : 'none';
138
+ this._tooltipDom.style.left = `${screenPos.x + offsetX}px`;
139
+ this._tooltipDom.style.top = `${screenPos.y + offsetY}px`;
140
+ }
141
+ if (annotation.onScreenPositionUpdateCallback) {
142
+ const hotspotSize = parseFloat(resources.hotspotDom.style.width) || void 0;
143
+ annotation.onScreenPositionUpdateCallback(screenPos.x + offsetX, screenPos.y + offsetY, hotspotSize);
144
+ }
328
145
  }
329
- if (resources && resources.hotspotDom) {
330
- const clampRatio = clampedScale / targetWorldSize;
331
- const baseSize = this._hotspotSize + 5; // Match the +5 from the stylesheet
332
- const scaledSize = baseSize * clampRatio * hoverScale;
333
- resources.hotspotDom.style.width = `${scaledSize}px`;
334
- resources.hotspotDom.style.height = `${scaledSize}px`;
146
+ _hideAnnotationElements(annotation, resources) {
147
+ super._hideAnnotationElements(annotation, resources);
148
+ resources.baseEntity.enabled = false;
149
+ resources.overlayEntity.enabled = false;
335
150
  }
336
- }
337
-
338
- /**
339
- * Override to create hotspot texture with custom background color.
340
- * @private
341
- */
342
- _createHotspotTexture(label, size = HOTSPOT_TEXTURE_SIZE, borderWidth = 6, icon) {
343
- const canvas = document.createElement('canvas');
344
- canvas.width = size;
345
- canvas.height = size;
346
- const ctx = canvas.getContext('2d');
347
- if (!ctx) {
348
- throw new Error('Failed to get canvas 2d context');
151
+ _updateAnnotationRotationAndScale(annotation) {
152
+ const cameraRotation = this._camera.getRotation();
153
+ annotation.entity.setRotation(cameraRotation);
154
+ annotation.entity.rotateLocal(90, 0, 0);
155
+ const cameraPos = this._camera.getPosition();
156
+ const distance = annotation.entity.getPosition().distance(cameraPos);
157
+ const canvas = this.app.graphicsDevice.canvas;
158
+ const screenHeight = canvas.clientHeight;
159
+ const projMatrix = this._camera.camera.projectionMatrix;
160
+ const targetWorldSize = this._hotspotSize / screenHeight * (2 * distance / projMatrix.data[5]);
161
+ const clampedScale = Math.min(targetWorldSize, this._maxWorldSize);
162
+ const hovered = this._hoverAnnotation === annotation;
163
+ const hoverScale = hovered ? HOTSPOT_HOVER_SCALE : 1;
164
+ const entityScale = clampedScale * hoverScale;
165
+ annotation.entity.setLocalScale(entityScale, entityScale, entityScale);
166
+ const resources = this._annotationResources.get(annotation);
167
+ if (resources) {
168
+ resources.baseEntity.enabled = true;
169
+ resources.overlayEntity.enabled = true;
170
+ }
171
+ if (resources && resources.hotspotDom) {
172
+ const clampRatio = clampedScale / targetWorldSize;
173
+ const baseSize = this._hotspotSize + 5;
174
+ const scaledSize = baseSize * clampRatio * hoverScale;
175
+ resources.hotspotDom.style.width = `${scaledSize}px`;
176
+ resources.hotspotDom.style.height = `${scaledSize}px`;
177
+ }
349
178
  }
350
-
351
- // First clear with stroke color at zero alpha
352
- ctx.fillStyle = 'white';
353
- ctx.globalAlpha = 0;
354
- ctx.fillRect(0, 0, size, size);
355
- ctx.globalAlpha = 1.0;
356
-
357
- // Draw circle with custom background color
358
- const centerX = size / 2;
359
- const centerY = size / 2;
360
- const radius = size / 2 - 4;
361
-
362
- // The icon fill and border share the hotspot color
363
- const foregroundColor = this._hotspotColor?.toString() ?? '#ffffff';
364
-
365
- // Draw main circle
366
- ctx.beginPath();
367
- ctx.arc(centerX, centerY, radius, 0, Math.PI * 2);
368
- ctx.fillStyle = this._hotspotBackgroundColor;
369
- ctx.fill();
370
-
371
- // Draw border
372
- ctx.beginPath();
373
- ctx.arc(centerX, centerY, radius, 0, Math.PI * 2);
374
- ctx.lineWidth = borderWidth;
375
- ctx.strokeStyle = foregroundColor;
376
- ctx.stroke();
377
-
378
- // Draw the media icon inside the circle
379
- if (icon) {
380
- this._drawIcon(ctx, icon, size);
179
+ _createHotspotTexture(label, size = HOTSPOT_TEXTURE_SIZE, borderWidth = 6, icon) {
180
+ const canvas = document.createElement('canvas');
181
+ canvas.width = size;
182
+ canvas.height = size;
183
+ const ctx = canvas.getContext('2d');
184
+ if (!ctx) throw new Error('Failed to get canvas 2d context');
185
+ ctx.fillStyle = 'white';
186
+ ctx.globalAlpha = 0;
187
+ ctx.fillRect(0, 0, size, size);
188
+ ctx.globalAlpha = 1.0;
189
+ const centerX = size / 2;
190
+ const centerY = size / 2;
191
+ const radius = size / 2 - 4;
192
+ const foregroundColor = this._hotspotColor?.toString() ?? '#ffffff';
193
+ ctx.beginPath();
194
+ ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI);
195
+ ctx.fillStyle = this._hotspotBackgroundColor;
196
+ ctx.fill();
197
+ ctx.beginPath();
198
+ ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI);
199
+ ctx.lineWidth = borderWidth;
200
+ ctx.strokeStyle = foregroundColor;
201
+ ctx.stroke();
202
+ if (icon) this._drawIcon(ctx, icon, size);
203
+ const imageData = ctx.getImageData(0, 0, size, size);
204
+ const data = imageData.data;
205
+ return new Texture(this.app.graphicsDevice, {
206
+ width: size,
207
+ height: size,
208
+ format: PIXELFORMAT_RGBA8,
209
+ magFilter: FILTER_LINEAR,
210
+ minFilter: FILTER_LINEAR,
211
+ mipmaps: false,
212
+ levels: [
213
+ new Uint8Array(data.buffer)
214
+ ]
215
+ });
381
216
  }
382
-
383
- // Get pixel data
384
- const imageData = ctx.getImageData(0, 0, size, size);
385
- const data = imageData.data;
386
-
387
- // Create and return the texture
388
- return new Texture(this.app.graphicsDevice, {
389
- width: size,
390
- height: size,
391
- format: PIXELFORMAT_RGBA8,
392
- magFilter: FILTER_LINEAR,
393
- minFilter: FILTER_LINEAR,
394
- mipmaps: false,
395
- levels: [new Uint8Array(data.buffer)]
396
- });
397
- }
398
217
  }
399
-
400
218
  export { TfAnnotationManager };