@terraware/web-components 4.2.13 → 4.2.14-rc.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 (302) hide show
  1. package/components/AntSwitch/index.js +8 -14
  2. package/components/Autocomplete/Autocomplete.js +67 -70
  3. package/components/Badge/index.js +16 -20
  4. package/components/BusySpinner/index.js +30 -35
  5. package/components/Button/Button.js +23 -27
  6. package/components/Button/button.test.js +8 -10
  7. package/components/Checkbox.js +10 -19
  8. package/components/Confirm/index.js +44 -51
  9. package/components/DatePicker/DatePicker.js +116 -117
  10. package/components/DialogBox/DialogBox.js +76 -66
  11. package/components/Divisor.js +15 -20
  12. package/components/Dropdown.js +54 -61
  13. package/components/EditableTable/index.js +129 -135
  14. package/components/ErrorBox/ErrorBox.js +37 -36
  15. package/components/FileChooser/index.js +73 -77
  16. package/components/FormBottomBar/index.js +63 -69
  17. package/components/Icon/Icon.js +14 -21
  18. package/components/Icon/icons/index.js +251 -257
  19. package/components/IconTooltip/index.js +29 -34
  20. package/components/Map/MapBox.js +199 -220
  21. package/components/Map/MapContainer.js +14 -18
  22. package/components/Map/MapDateSliderControl.js +44 -46
  23. package/components/Map/MapDrawer.js +32 -33
  24. package/components/Map/MapLegend.js +96 -95
  25. package/components/Map/MapViewStyleControl.js +38 -48
  26. package/components/Map/ReactMapboxWrapper.js +2 -9
  27. package/components/Map/index.js +47 -55
  28. package/components/Map/types.js +2 -8
  29. package/components/Map/useMaintainLayerOrder.js +4 -11
  30. package/components/Markdown/index.js +14 -21
  31. package/components/Message/Message.js +59 -51
  32. package/components/MultiSelect/MultiSelect.test.js +22 -24
  33. package/components/MultiSelect/index.js +73 -69
  34. package/components/Navbar/NavFooter.js +8 -13
  35. package/components/Navbar/NavItem.js +43 -37
  36. package/components/Navbar/NavSection.js +13 -17
  37. package/components/Navbar/Navbar.js +35 -38
  38. package/components/Navbar/SubNavbar.js +8 -13
  39. package/components/Note.js +14 -19
  40. package/components/OverlayModal/OverlayModal.js +36 -37
  41. package/components/PageForm/index.js +30 -35
  42. package/components/PhotoChooser/index.js +93 -98
  43. package/components/Pill/index.js +29 -31
  44. package/components/PillList/index.js +16 -22
  45. package/components/PlacementWrapper/index.js +20 -25
  46. package/components/PopoverMenu/Popover.js +33 -37
  47. package/components/PopoverMenu/index.js +41 -46
  48. package/components/PopoverMultiSelect/index.js +18 -21
  49. package/components/ProgressCircle/ProgressCircle.js +23 -26
  50. package/components/RadioButton.js +7 -14
  51. package/components/Select/Select.js +8 -13
  52. package/components/Select/SelectT.js +110 -108
  53. package/components/Separator/index.js +8 -14
  54. package/components/Size.js +1 -5
  55. package/components/Slider/index.js +7 -13
  56. package/components/SummaryBox.js +36 -39
  57. package/components/Tabs/index.js +75 -77
  58. package/components/TextTruncated/index.js +64 -65
  59. package/components/Textfield/Textfield.js +100 -95
  60. package/components/Textfield/TruncatedTextArea.js +44 -46
  61. package/components/TimelineSlider/TimelineMarker.js +24 -28
  62. package/components/TimelineSlider/TimelineRail.js +5 -11
  63. package/components/TimelineSlider/index.js +47 -51
  64. package/components/Tooltip/Tooltip.js +21 -26
  65. package/components/ViewPhotosDialog/index.js +58 -62
  66. package/components/VirtualWalkthrough/Annotation.d.ts +41 -0
  67. package/components/VirtualWalkthrough/Annotation.d.ts.map +1 -0
  68. package/components/VirtualWalkthrough/Annotation.js +197 -0
  69. package/components/VirtualWalkthrough/AnnotationEditPane.d.ts +20 -0
  70. package/components/VirtualWalkthrough/AnnotationEditPane.d.ts.map +1 -0
  71. package/components/VirtualWalkthrough/AnnotationEditPane.js +136 -0
  72. package/components/VirtualWalkthrough/AnnotationPanel.d.ts +13 -0
  73. package/components/VirtualWalkthrough/AnnotationPanel.d.ts.map +1 -0
  74. package/components/VirtualWalkthrough/AnnotationPanel.js +196 -0
  75. package/components/VirtualWalkthrough/Application.d.ts +9 -0
  76. package/components/VirtualWalkthrough/Application.d.ts.map +1 -0
  77. package/components/VirtualWalkthrough/Application.js +32 -0
  78. package/components/VirtualWalkthrough/AutoRotator.d.ts +109 -0
  79. package/components/VirtualWalkthrough/AutoRotator.d.ts.map +1 -0
  80. package/components/VirtualWalkthrough/AutoRotator.js +217 -0
  81. package/components/VirtualWalkthrough/BlockingSpinner.d.ts +3 -0
  82. package/components/VirtualWalkthrough/BlockingSpinner.d.ts.map +1 -0
  83. package/components/VirtualWalkthrough/BlockingSpinner.js +27 -0
  84. package/components/VirtualWalkthrough/BoundaryRing.d.ts +9 -0
  85. package/components/VirtualWalkthrough/BoundaryRing.d.ts.map +1 -0
  86. package/components/VirtualWalkthrough/BoundaryRing.js +33 -0
  87. package/components/VirtualWalkthrough/CameraInfo.d.ts +16 -0
  88. package/components/VirtualWalkthrough/CameraInfo.d.ts.map +1 -0
  89. package/components/VirtualWalkthrough/CameraInfo.js +88 -0
  90. package/components/VirtualWalkthrough/ControlsInfoPane.d.ts +37 -0
  91. package/components/VirtualWalkthrough/ControlsInfoPane.d.ts.map +1 -0
  92. package/components/VirtualWalkthrough/ControlsInfoPane.js +196 -0
  93. package/components/VirtualWalkthrough/GradientSky.d.ts +9 -0
  94. package/components/VirtualWalkthrough/GradientSky.d.ts.map +1 -0
  95. package/components/VirtualWalkthrough/GradientSky.js +73 -0
  96. package/components/VirtualWalkthrough/SplatControls.d.ts +50 -0
  97. package/components/VirtualWalkthrough/SplatControls.d.ts.map +1 -0
  98. package/components/VirtualWalkthrough/SplatControls.js +252 -0
  99. package/components/VirtualWalkthrough/SplatCrop.d.ts +8 -0
  100. package/components/VirtualWalkthrough/SplatCrop.d.ts.map +1 -0
  101. package/components/VirtualWalkthrough/SplatCrop.js +18 -0
  102. package/components/VirtualWalkthrough/SplatFadeCrop.d.ts +8 -0
  103. package/components/VirtualWalkthrough/SplatFadeCrop.d.ts.map +1 -0
  104. package/components/VirtualWalkthrough/SplatFadeCrop.js +18 -0
  105. package/components/VirtualWalkthrough/SplatModel.d.ts +15 -0
  106. package/components/VirtualWalkthrough/SplatModel.d.ts.map +1 -0
  107. package/components/VirtualWalkthrough/SplatModel.js +60 -0
  108. package/components/VirtualWalkthrough/SplatRevealRain.d.ts +18 -0
  109. package/components/VirtualWalkthrough/SplatRevealRain.d.ts.map +1 -0
  110. package/components/VirtualWalkthrough/SplatRevealRain.js +38 -0
  111. package/components/VirtualWalkthrough/TfAnnotationManager.d.ts +110 -0
  112. package/components/VirtualWalkthrough/TfAnnotationManager.d.ts.map +1 -0
  113. package/components/VirtualWalkthrough/TfAnnotationManager.js +368 -0
  114. package/components/VirtualWalkthrough/TfXrNavigation.d.ts +15 -0
  115. package/components/VirtualWalkthrough/TfXrNavigation.d.ts.map +1 -0
  116. package/components/VirtualWalkthrough/TfXrNavigation.js +24 -0
  117. package/components/VirtualWalkthrough/VirtualWalkthroughViewer.d.ts +27 -0
  118. package/components/VirtualWalkthrough/VirtualWalkthroughViewer.d.ts.map +1 -0
  119. package/components/VirtualWalkthrough/VirtualWalkthroughViewer.js +282 -0
  120. package/components/VirtualWalkthrough/annotation-styles.css +5 -0
  121. package/components/VirtualWalkthrough/application-styles.css +4 -0
  122. package/components/VirtualWalkthrough/boundary-ring.d.ts +47 -0
  123. package/components/VirtualWalkthrough/boundary-ring.d.ts.map +1 -0
  124. package/components/VirtualWalkthrough/boundary-ring.js +125 -0
  125. package/components/VirtualWalkthrough/boundary-ring.test.js +109 -0
  126. package/components/VirtualWalkthrough/groundPlane.d.ts +14 -0
  127. package/components/VirtualWalkthrough/groundPlane.d.ts.map +1 -0
  128. package/components/VirtualWalkthrough/groundPlane.js +47 -0
  129. package/components/VirtualWalkthrough/groundPlane.test.js +45 -0
  130. package/components/VirtualWalkthrough/gsplat-fade-crop-effect.d.ts +19 -0
  131. package/components/VirtualWalkthrough/gsplat-fade-crop-effect.d.ts.map +1 -0
  132. package/components/VirtualWalkthrough/gsplat-fade-crop-effect.js +119 -0
  133. package/components/VirtualWalkthrough/walkthrough-camera.d.ts +77 -0
  134. package/components/VirtualWalkthrough/walkthrough-camera.d.ts.map +1 -0
  135. package/components/VirtualWalkthrough/walkthrough-camera.js +320 -0
  136. package/components/svg/BlobbyGrayIconImage.js +36 -41
  137. package/components/svg/BlobbyGrayIconImport.js +36 -41
  138. package/components/svg/BlobbyGrayIconUploadToTheCloud.js +36 -41
  139. package/components/svg/BlobbyIconCloudChecked.js +161 -158
  140. package/components/svg/BlobbyIconExportOrUploadPhoto.js +161 -158
  141. package/components/svg/BlobbyIconFolder.js +161 -158
  142. package/components/svg/BlobbyIconGraphReport.js +161 -158
  143. package/components/svg/BlobbyIconHappy.js +161 -158
  144. package/components/svg/BlobbyIconHeartMonitor.js +161 -158
  145. package/components/svg/BlobbyIconLeaf.js +161 -158
  146. package/components/svg/BlobbyIconLibrary.js +161 -158
  147. package/components/svg/BlobbyIconNursery.js +163 -160
  148. package/components/svg/BlobbyIconOrganization.js +161 -158
  149. package/components/svg/BlobbyIconParchment.js +161 -158
  150. package/components/svg/BlobbyIconPeople.js +161 -158
  151. package/components/svg/BlobbyIconSeedBank.js +161 -158
  152. package/components/svg/BlobbyIconSeedling.js +161 -158
  153. package/components/svg/BlobbyIconSeeds.js +161 -158
  154. package/components/svg/BlobbyIconSite.js +164 -161
  155. package/components/svg/BlobbyIconSparkles.js +161 -158
  156. package/components/svg/BlobbyIconWrench.js +161 -158
  157. package/components/svg/Bug.js +18 -27
  158. package/components/svg/Calendar.js +18 -27
  159. package/components/svg/CaretDown.js +18 -27
  160. package/components/svg/CaretLeft.js +18 -27
  161. package/components/svg/CaretRight.js +18 -27
  162. package/components/svg/CaretUp.js +18 -27
  163. package/components/svg/ChevronDown.js +18 -27
  164. package/components/svg/ChevronUp.js +18 -27
  165. package/components/svg/Close.js +18 -27
  166. package/components/svg/Critical.js +18 -27
  167. package/components/svg/Edit.js +161 -158
  168. package/components/svg/Error.js +18 -27
  169. package/components/svg/Filter.js +18 -27
  170. package/components/svg/Folder.js +18 -27
  171. package/components/svg/Help.js +18 -27
  172. package/components/svg/Home.js +18 -27
  173. package/components/svg/IconAdd.js +18 -27
  174. package/components/svg/IconArrowRight.js +19 -28
  175. package/components/svg/IconBusinessNetwork.js +18 -27
  176. package/components/svg/IconCalendar.js +18 -27
  177. package/components/svg/IconCancel.js +18 -27
  178. package/components/svg/IconChargingBattery.js +18 -27
  179. package/components/svg/IconChecklist.js +19 -28
  180. package/components/svg/IconCheckmark.js +18 -27
  181. package/components/svg/IconCoinInHand.js +18 -27
  182. package/components/svg/IconColumns.js +18 -27
  183. package/components/svg/IconComment.js +18 -27
  184. package/components/svg/IconDashboard.js +18 -27
  185. package/components/svg/IconDataMigration.js +18 -27
  186. package/components/svg/IconDocument.js +19 -28
  187. package/components/svg/IconDownloadFromTheCloud.js +19 -28
  188. package/components/svg/IconEdit.js +19 -28
  189. package/components/svg/IconExpand.js +19 -28
  190. package/components/svg/IconExport.js +18 -27
  191. package/components/svg/IconExternalLink.js +18 -27
  192. package/components/svg/IconEye.js +18 -27
  193. package/components/svg/IconEyeOff.js +18 -27
  194. package/components/svg/IconFile.js +18 -27
  195. package/components/svg/IconFolder.js +18 -27
  196. package/components/svg/IconFullScreen.js +18 -27
  197. package/components/svg/IconFutures.js +18 -27
  198. package/components/svg/IconGraphReport.js +18 -27
  199. package/components/svg/IconHeartMonitor.js +18 -27
  200. package/components/svg/IconHelp.js +19 -28
  201. package/components/svg/IconHistory.js +18 -27
  202. package/components/svg/IconImport.js +18 -27
  203. package/components/svg/IconIndex.js +18 -27
  204. package/components/svg/IconLayers.js +18 -27
  205. package/components/svg/IconLibrary.js +18 -27
  206. package/components/svg/IconList.js +18 -27
  207. package/components/svg/IconLivePlant.js +18 -27
  208. package/components/svg/IconLocations.js +18 -27
  209. package/components/svg/IconMail.js +18 -27
  210. package/components/svg/IconManager.js +18 -27
  211. package/components/svg/IconMarker.js +18 -27
  212. package/components/svg/IconMenu.js +18 -27
  213. package/components/svg/IconMenuHorizontal.js +18 -27
  214. package/components/svg/IconModule.js +18 -27
  215. package/components/svg/IconMyLocation.js +18 -27
  216. package/components/svg/IconNote.js +19 -28
  217. package/components/svg/IconNursery.js +18 -27
  218. package/components/svg/IconOrg.js +18 -27
  219. package/components/svg/IconParchment.js +18 -27
  220. package/components/svg/IconPhoto.js +18 -27
  221. package/components/svg/IconPlantsFilled.js +18 -27
  222. package/components/svg/IconRedo.js +18 -27
  223. package/components/svg/IconRestorationSite.js +18 -27
  224. package/components/svg/IconSeedBank.js +18 -27
  225. package/components/svg/IconSeedling.js +18 -27
  226. package/components/svg/IconSettings.js +18 -27
  227. package/components/svg/IconSlice.js +18 -27
  228. package/components/svg/IconSubmit.js +18 -27
  229. package/components/svg/IconSubtract.js +19 -28
  230. package/components/svg/IconSynced.js +18 -27
  231. package/components/svg/IconTrashCan.js +18 -27
  232. package/components/svg/IconTreasureMap.js +18 -27
  233. package/components/svg/IconUnavailable.js +18 -27
  234. package/components/svg/IconUndo.js +18 -27
  235. package/components/svg/IconVariable.js +18 -27
  236. package/components/svg/IconVideo.js +19 -28
  237. package/components/svg/IconWifi.js +18 -27
  238. package/components/svg/Info.js +18 -27
  239. package/components/svg/Key.js +18 -27
  240. package/components/svg/Leaf.js +18 -27
  241. package/components/svg/Lock.js +18 -27
  242. package/components/svg/Logo.js +40 -45
  243. package/components/svg/MenuVertical.js +18 -27
  244. package/components/svg/Notification.js +18 -27
  245. package/components/svg/Person.js +18 -27
  246. package/components/svg/Plus.js +18 -27
  247. package/components/svg/RestorationSite.js +18 -27
  248. package/components/svg/Search.js +18 -27
  249. package/components/svg/Seeds.js +18 -27
  250. package/components/svg/Site.js +18 -27
  251. package/components/svg/Sparkles.js +18 -27
  252. package/components/svg/Species.js +18 -27
  253. package/components/svg/Species2.js +161 -158
  254. package/components/svg/Spinner.js +27 -36
  255. package/components/svg/Star.js +18 -27
  256. package/components/svg/Success.js +18 -27
  257. package/components/svg/SuccessFilled.js +18 -27
  258. package/components/svg/TerrawareLogoDesktop.js +44 -49
  259. package/components/svg/TerrawareLogoMobile.js +20 -29
  260. package/components/svg/Touchscreen.js +18 -27
  261. package/components/svg/UploadCloud.js +161 -158
  262. package/components/svg/Warning.js +18 -27
  263. package/components/svg/WelcomeClipboard.js +130 -135
  264. package/components/svg/index.js +127 -895
  265. package/components/table/EnhancedTableToolbar.js +45 -47
  266. package/components/table/EnhancedTableToolbarV2.js +18 -26
  267. package/components/table/TableCellRenderer.js +137 -143
  268. package/components/table/TableHeader.js +45 -47
  269. package/components/table/TableHeaderItem.js +65 -64
  270. package/components/table/density.js +3 -11
  271. package/components/table/index.js +225 -218
  272. package/components/table/sort.js +8 -18
  273. package/components/table/sort.test.js +6 -8
  274. package/components/table/types.js +1 -5
  275. package/components/types/index.js +1 -5
  276. package/hooks/useBoolean.d.ts +4 -0
  277. package/hooks/useBoolean.d.ts.map +1 -0
  278. package/hooks/useBoolean.js +12 -0
  279. package/hooks/useCameraPosition.d.ts +8 -0
  280. package/hooks/useCameraPosition.d.ts.map +1 -0
  281. package/hooks/useCameraPosition.js +33 -0
  282. package/hooks/useDevicePerformance.d.ts +9 -0
  283. package/hooks/useDevicePerformance.d.ts.map +1 -0
  284. package/hooks/useDevicePerformance.js +16 -0
  285. package/index.js +57 -398
  286. package/license-report.html +1 -1
  287. package/package.json +10 -3
  288. package/style-dictionary-dist/TerrawareTheme.js +1 -7
  289. package/theme.js +7 -14
  290. package/utils/color.d.ts +2 -0
  291. package/utils/color.d.ts.map +1 -0
  292. package/utils/color.js +14 -0
  293. package/utils/date.js +13 -24
  294. package/utils/date.test.js +40 -43
  295. package/utils/index.js +5 -53
  296. package/utils/preventDefaultEvent.js +1 -7
  297. package/utils/text.js +2 -9
  298. package/utils/text.test.js +9 -11
  299. package/utils/useDeviceInfo.js +10 -16
  300. package/virtualWalkthrough.d.ts +34 -0
  301. package/virtualWalkthrough.d.ts.map +1 -0
  302. package/virtualWalkthrough.js +30 -0
@@ -0,0 +1,368 @@
1
+ import { createElement } from 'react';
2
+ import { renderToStaticMarkup } from 'react-dom/server';
3
+ import { FILTER_LINEAR, PIXELFORMAT_RGBA8, Texture } from 'playcanvas';
4
+ import { AnnotationManager as PcAnnotationManager } from 'playcanvas/scripts/esm/annotations.mjs';
5
+ import Icon from '../Icon/Icon';
6
+ // Maps each annotation icon type to a @terraware/web-components icon.
7
+ const ANNOTATION_ICONS = {
8
+ text: 'iconComment',
9
+ image: 'iconPhoto',
10
+ video: 'iconVideo'
11
+ };
12
+
13
+ // Pixel dimensions of the (square) hotspot texture canvas at rest.
14
+ const HOTSPOT_TEXTURE_SIZE = 64;
15
+
16
+ // How much larger the hotspot appears while hovered.
17
+ const HOTSPOT_HOVER_SCALE = 1.3;
18
+
19
+ /**
20
+ * Extended AnnotationManager that adds max size clamping for annotations
21
+ * and fixes positioning when canvas is not at document origin.
22
+ */
23
+ export class TfAnnotationManager extends PcAnnotationManager {
24
+ static scriptName = 'tfAnnotationManager';
25
+ _maxWorldSize = 1.0;
26
+ _customParentDom = null;
27
+ _clickHandlersAttached = new Set();
28
+ _hotspotBackgroundColor = '#2C8658';
29
+ _iconImages = new Map();
30
+
31
+ /**
32
+ * Maximum world-space size for annotations.
33
+ * This prevents annotations from growing beyond this size regardless of camera distance.
34
+ *
35
+ * @attribute
36
+ * @title Max World Size
37
+ * @type {number}
38
+ * @default 1.0
39
+ */
40
+ set maxWorldSize(value) {
41
+ this._maxWorldSize = value;
42
+ }
43
+ get maxWorldSize() {
44
+ return this._maxWorldSize;
45
+ }
46
+
47
+ /**
48
+ * Background color for the hotspot texture canvas (the circle fill color).
49
+ * This is a hex color string (e.g., '#2C8658') that's drawn on the texture canvas.
50
+ * Note: This is different from hotspotColor and hoverColor, which are PlayCanvas Color objects
51
+ * used for the material's emissive property.
52
+ *
53
+ * @attribute
54
+ * @title Hotspot Background Color
55
+ * @type {string}
56
+ * @default '#2C8658'
57
+ */
58
+ set hotspotBackgroundColor(value) {
59
+ this._hotspotBackgroundColor = value;
60
+ }
61
+ get hotspotBackgroundColor() {
62
+ return this._hotspotBackgroundColor;
63
+ }
64
+
65
+ /**
66
+ * Initialize with custom parent DOM if needed.
67
+ */
68
+ initialize() {
69
+ // Find a container div with data-annotation-container attribute
70
+ const canvas = this.app.graphicsDevice.canvas;
71
+ const container = canvas.parentElement?.querySelector('[data-annotation-container]');
72
+ if (container) {
73
+ this._customParentDom = container;
74
+ this._parentDom = container;
75
+ }
76
+
77
+ // Call parent initialize
78
+ super.initialize();
79
+ this._loadIconImages();
80
+ }
81
+
82
+ /**
83
+ * Renders each web-components icon to an SVG image once. When an image finishes
84
+ * loading, any annotations already using that icon are redrawn.
85
+ * @private
86
+ */
87
+ _loadIconImages() {
88
+ const fillColor = this._hotspotColor?.toString() ?? '#ffffff';
89
+ Object.keys(ANNOTATION_ICONS).forEach(iconType => {
90
+ const name = ANNOTATION_ICONS[iconType];
91
+ const markup = renderToStaticMarkup(/*#__PURE__*/createElement(Icon, {
92
+ name,
93
+ fillColor
94
+ })).replace(/^<svg/, '<svg width="128" height="128"');
95
+ const image = new Image();
96
+ image.onload = () => this._refreshIcon(iconType);
97
+ image.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(markup)}`;
98
+ this._iconImages.set(iconType, image);
99
+ });
100
+ }
101
+
102
+ /**
103
+ * Redraws the texture for all annotations using the given icon, once its image
104
+ * has loaded.
105
+ * @private
106
+ */
107
+ _refreshIcon(iconType) {
108
+ const annotationResources = this._annotationResources;
109
+ if (!annotationResources) {
110
+ return;
111
+ }
112
+ annotationResources.forEach((_resources, annotation) => {
113
+ if ((annotation.icon ?? 'text') === iconType) {
114
+ this._applyAnnotationIcon(annotation);
115
+ }
116
+ });
117
+ }
118
+
119
+ /**
120
+ * Update loop to attach click handlers to annotations and manage visibility.
121
+ */
122
+ update() {
123
+ const annotationResources = this._annotationResources;
124
+ if (!annotationResources) {
125
+ return;
126
+ }
127
+
128
+ // Attach click handlers and manage visibility for all annotations
129
+ annotationResources.forEach((resources, annotation) => {
130
+ if (resources && resources.hotspotDom) {
131
+ // Attach click handler if not already attached
132
+ if (!this._clickHandlersAttached.has(annotation)) {
133
+ const callback = annotation.onClickCallback;
134
+ if (callback) {
135
+ this._clickHandlersAttached.add(annotation);
136
+ resources.hotspotDom.addEventListener('pointerdown', e => {
137
+ callback(e.clientX, e.clientY);
138
+ });
139
+ }
140
+ }
141
+
142
+ // Disable/enable the entity itself based on visibility
143
+ const isVisible = annotation.enabled !== undefined ? annotation.enabled : true;
144
+
145
+ // Hide tooltip if the active annotation is hidden
146
+ if (this._activeAnnotation === annotation && !isVisible) {
147
+ this._tooltipDom.style.visibility = 'hidden';
148
+ this._tooltipDom.style.opacity = '0';
149
+ this._activeAnnotation = null;
150
+ }
151
+ }
152
+ });
153
+ }
154
+
155
+ /**
156
+ * Override annotation registration to draw the media icon onto the hotspot
157
+ * texture once the annotation (and its icon) is available.
158
+ * @private
159
+ */
160
+ _registerAnnotation(annotation) {
161
+ super._registerAnnotation(annotation);
162
+ this._applyAnnotationIcon(annotation);
163
+ }
164
+
165
+ /**
166
+ * Recreates the hotspot texture with the appropriate media icon drawn on it.
167
+ * @private
168
+ */
169
+ _applyAnnotationIcon(annotation) {
170
+ const resources = this._annotationResources.get(annotation);
171
+ if (!resources) {
172
+ return;
173
+ }
174
+ const icon = annotation.icon ?? 'text';
175
+ resources.texture.destroy();
176
+ resources.texture = this._createHotspotTexture(annotation.label, HOTSPOT_TEXTURE_SIZE, 6, icon);
177
+ resources.materials.forEach(material => {
178
+ material.emissiveMap = resources.texture;
179
+ material.opacityMap = resources.texture;
180
+ material.update();
181
+ });
182
+ }
183
+
184
+ /**
185
+ * Draws the icon image onto the hotspot texture canvas, scaled to fit the
186
+ * circle. Does nothing if the image has not finished loading yet; the
187
+ * annotation is redrawn once it has (see `_refreshIcon`).
188
+ * @private
189
+ */
190
+ _drawIcon(ctx, icon, size) {
191
+ const image = this._iconImages.get(icon);
192
+ if (!image || !image.complete || image.naturalWidth === 0) {
193
+ return;
194
+ }
195
+ const iconSize = size * 0.5;
196
+ const offset = (size - iconSize) / 2;
197
+ ctx.drawImage(image, offset, offset, iconSize, iconSize);
198
+ }
199
+
200
+ /**
201
+ * Keep the media icon drawn on the hotspot texture. The label text itself
202
+ * is not rendered on the hotspot, so we simply redraw with the icon.
203
+ * @private
204
+ */
205
+ _onLabelChange(annotation) {
206
+ this._applyAnnotationIcon(annotation);
207
+ }
208
+
209
+ /**
210
+ * The hotspot colors are baked into the texture in `_createHotspotTexture`,
211
+ * so the material emissive is kept white to let those colors render as drawn.
212
+ * @private
213
+ */
214
+ _createHotspotMaterial(texture, options) {
215
+ const material = super._createHotspotMaterial(texture, options);
216
+ material.emissive.set(1, 1, 1);
217
+ material.update();
218
+ return material;
219
+ }
220
+
221
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
222
+ _setAnnotationHover(_annotation, _hover) {
223
+ // No-op: the hover growth is handled in _updateAnnotationRotationAndScale
224
+ // (via _hoverAnnotation); the texture is identical in both states.
225
+ }
226
+ _updateAllAnnotationColors() {
227
+ // No-op: hotspot colors are baked into the texture (see _createHotspotMaterial).
228
+ }
229
+
230
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
231
+ _showTooltip(_annotation) {
232
+ // No-op: AnnotationPanel handles all annotation UI, this removes default behavior
233
+ }
234
+
235
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
236
+ _onTextChange(_annotation, _text) {
237
+ // No-op: AnnotationPanel handles all annotation UI, this removes default behavior
238
+ }
239
+
240
+ /**
241
+ * Override position update to adjust for canvas offset and respect visibility.
242
+ * @private
243
+ */
244
+ _updateAnnotationPositions(annotation, resources, screenPos) {
245
+ const canvas = this.app.graphicsDevice.canvas;
246
+ const rect = canvas.getBoundingClientRect();
247
+
248
+ // If using a custom parent (positioned container), use raw screen coordinates
249
+ // Otherwise, adjust for canvas position in document
250
+ const offsetX = this._customParentDom ? 0 : rect.left + window.scrollX;
251
+ const offsetY = this._customParentDom ? 0 : rect.top + window.scrollY;
252
+
253
+ // Check if annotation is visible (defaults to true if not specified)
254
+ const isVisible = annotation.enabled !== undefined ? annotation.enabled : true;
255
+ resources.hotspotDom.style.display = isVisible ? 'block' : 'none';
256
+ resources.hotspotDom.style.left = `${screenPos.x + offsetX}px`;
257
+ resources.hotspotDom.style.top = `${screenPos.y + offsetY}px`;
258
+ if (this._activeAnnotation === annotation) {
259
+ this._tooltipDom.style.display = isVisible ? 'block' : 'none';
260
+ this._tooltipDom.style.left = `${screenPos.x + offsetX}px`;
261
+ this._tooltipDom.style.top = `${screenPos.y + offsetY}px`;
262
+ }
263
+ if (annotation.onScreenPositionUpdateCallback) {
264
+ // Rendered hotspot diameter (px), set by _updateAnnotationRotationAndScale.
265
+ // Parsed from the style rather than measured to avoid forcing a reflow.
266
+ const hotspotSize = parseFloat(resources.hotspotDom.style.width) || undefined;
267
+ annotation.onScreenPositionUpdateCallback(screenPos.x + offsetX, screenPos.y + offsetY, hotspotSize);
268
+ }
269
+ }
270
+
271
+ /**
272
+ * Override the scale update to clamp the world size to a maximum value.
273
+ * Also scales the hotspot DOM element to match.
274
+ * @private
275
+ */
276
+ _updateAnnotationRotationAndScale(annotation) {
277
+ const cameraRotation = this._camera.getRotation();
278
+ annotation.entity.setRotation(cameraRotation);
279
+ annotation.entity.rotateLocal(90, 0, 0);
280
+ const cameraPos = this._camera.getPosition();
281
+ const distance = annotation.entity.getPosition().distance(cameraPos);
282
+ const canvas = this.app.graphicsDevice.canvas;
283
+ const screenHeight = canvas.clientHeight;
284
+ const projMatrix = this._camera.camera.projectionMatrix;
285
+
286
+ // Calculate world size based on screen size and distance
287
+ const unclamped = this._hotspotSize / screenHeight * (2 * distance / projMatrix.data[5]);
288
+
289
+ // Clamp to maximum world size
290
+ const worldSize = Math.min(unclamped, this._maxWorldSize);
291
+ const hovered = this._hoverAnnotation === annotation;
292
+ const hoverScale = hovered ? HOTSPOT_HOVER_SCALE : 1;
293
+ const entityScale = worldSize * hoverScale;
294
+ annotation.entity.setLocalScale(entityScale, entityScale, entityScale);
295
+
296
+ // Scale the hotspot DOM element proportionally when clamped, including the
297
+ // hover growth so it tracks the visible circle.
298
+ const resources = this._annotationResources.get(annotation);
299
+ if (resources && resources.hotspotDom) {
300
+ const scaleFactor = worldSize / unclamped;
301
+ const baseSize = this._hotspotSize + 5; // Match the +5 from the stylesheet
302
+ const scaledSize = baseSize * scaleFactor * hoverScale;
303
+ resources.hotspotDom.style.width = `${scaledSize}px`;
304
+ resources.hotspotDom.style.height = `${scaledSize}px`;
305
+ }
306
+ }
307
+
308
+ /**
309
+ * Override to create hotspot texture with custom background color.
310
+ * @private
311
+ */
312
+ _createHotspotTexture(label, size = HOTSPOT_TEXTURE_SIZE, borderWidth = 6, icon) {
313
+ const canvas = document.createElement('canvas');
314
+ canvas.width = size;
315
+ canvas.height = size;
316
+ const ctx = canvas.getContext('2d');
317
+ if (!ctx) {
318
+ throw new Error('Failed to get canvas 2d context');
319
+ }
320
+
321
+ // First clear with stroke color at zero alpha
322
+ ctx.fillStyle = 'white';
323
+ ctx.globalAlpha = 0;
324
+ ctx.fillRect(0, 0, size, size);
325
+ ctx.globalAlpha = 1.0;
326
+
327
+ // Draw circle with custom background color
328
+ const centerX = size / 2;
329
+ const centerY = size / 2;
330
+ const radius = size / 2 - 4;
331
+
332
+ // The icon fill and border share the hotspot color
333
+ const foregroundColor = this._hotspotColor?.toString() ?? '#ffffff';
334
+
335
+ // Draw main circle
336
+ ctx.beginPath();
337
+ ctx.arc(centerX, centerY, radius, 0, Math.PI * 2);
338
+ ctx.fillStyle = this._hotspotBackgroundColor;
339
+ ctx.fill();
340
+
341
+ // Draw border
342
+ ctx.beginPath();
343
+ ctx.arc(centerX, centerY, radius, 0, Math.PI * 2);
344
+ ctx.lineWidth = borderWidth;
345
+ ctx.strokeStyle = foregroundColor;
346
+ ctx.stroke();
347
+
348
+ // Draw the media icon inside the circle
349
+ if (icon) {
350
+ this._drawIcon(ctx, icon, size);
351
+ }
352
+
353
+ // Get pixel data
354
+ const imageData = ctx.getImageData(0, 0, size, size);
355
+ const data = imageData.data;
356
+
357
+ // Create and return the texture
358
+ return new Texture(this.app.graphicsDevice, {
359
+ width: size,
360
+ height: size,
361
+ format: PIXELFORMAT_RGBA8,
362
+ magFilter: FILTER_LINEAR,
363
+ minFilter: FILTER_LINEAR,
364
+ mipmaps: false,
365
+ levels: [new Uint8Array(data.buffer)]
366
+ });
367
+ }
368
+ }
@@ -0,0 +1,15 @@
1
+ import { XrNavigation as PcXrNavigation } from 'playcanvas/scripts/esm/xr-navigation.mjs';
2
+ /**
3
+ * Extended XrNavigation that fixes the bug where tryTeleport doesn't respect
4
+ * the enableTeleport flag setting.
5
+ */
6
+ export declare class TfXrNavigation extends PcXrNavigation {
7
+ static scriptName: string;
8
+ /**
9
+ * Override tryTeleport to respect the enableTeleport flag.
10
+ * The base implementation is always called from handleSelectEnd,
11
+ * but we only want to teleport when enableTeleport is true.
12
+ */
13
+ tryTeleport(inputSource: any): void;
14
+ }
15
+ //# sourceMappingURL=TfXrNavigation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TfXrNavigation.d.ts","sourceRoot":"","sources":["../../../src/components/VirtualWalkthrough/TfXrNavigation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,IAAI,cAAc,EAAE,MAAM,0CAA0C,CAAC;AAE1F;;;GAGG;AACH,qBAAa,cAAe,SAAQ,cAAc;IAChD,MAAM,CAAC,UAAU,SAAoB;IAErC;;;;OAIG;IACH,WAAW,CAAC,WAAW,EAAE,GAAG;CAS7B"}
@@ -0,0 +1,24 @@
1
+ import { XrNavigation as PcXrNavigation } from 'playcanvas/scripts/esm/xr-navigation.mjs';
2
+
3
+ /**
4
+ * Extended XrNavigation that fixes the bug where tryTeleport doesn't respect
5
+ * the enableTeleport flag setting.
6
+ */
7
+ export class TfXrNavigation extends PcXrNavigation {
8
+ static scriptName = 'tfXrNavigation';
9
+
10
+ /**
11
+ * Override tryTeleport to respect the enableTeleport flag.
12
+ * The base implementation is always called from handleSelectEnd,
13
+ * but we only want to teleport when enableTeleport is true.
14
+ */
15
+ tryTeleport(inputSource) {
16
+ // Only proceed with teleportation if enableTeleport is true
17
+ if (!this.enableTeleport) {
18
+ return;
19
+ }
20
+
21
+ // Call the parent implementation
22
+ super.tryTeleport(inputSource);
23
+ }
24
+ }
@@ -0,0 +1,27 @@
1
+ import { AnnotationProps } from './Annotation';
2
+ import { SplatControlsStrings } from './SplatControls';
3
+ export interface VirtualWalkthroughViewerProps {
4
+ splatSrc: string;
5
+ origin?: [number, number, number];
6
+ cameraPosition?: [number, number, number];
7
+ sceneBounds?: {
8
+ x: number;
9
+ y: number;
10
+ z: number;
11
+ m: number;
12
+ };
13
+ groundPlane?: [number, number, number][];
14
+ skyColor?: string;
15
+ groundColor?: string;
16
+ averageCameraHeight?: number;
17
+ annotations: AnnotationProps[];
18
+ onSaveAnnotations: (annotations: AnnotationProps[]) => void | Promise<void>;
19
+ strings: SplatControlsStrings;
20
+ editable?: boolean;
21
+ showFreeFly?: boolean;
22
+ isFullScreen?: boolean;
23
+ onToggleFullScreen?: () => void;
24
+ }
25
+ declare const VirtualWalkthroughViewer: ({ splatSrc, origin, cameraPosition, sceneBounds, groundPlane: groundPlaneProp, skyColor, groundColor, averageCameraHeight, annotations, onSaveAnnotations, strings, editable, showFreeFly, isFullScreen, onToggleFullScreen, }: VirtualWalkthroughViewerProps) => import("react/jsx-runtime").JSX.Element;
26
+ export default VirtualWalkthroughViewer;
27
+ //# sourceMappingURL=VirtualWalkthroughViewer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"VirtualWalkthroughViewer.d.ts","sourceRoot":"","sources":["../../../src/components/VirtualWalkthrough/VirtualWalkthroughViewer.tsx"],"names":[],"mappings":"AAWA,OAAmB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAK3D,OAAsB,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAStE,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,WAAW,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7D,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,WAAW,EAAE,eAAe,EAAE,CAAC;IAC/B,iBAAiB,EAAE,CAAC,WAAW,EAAE,eAAe,EAAE,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5E,OAAO,EAAE,oBAAoB,CAAC;IAC9B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAC;CACjC;AAED,QAAA,MAAM,wBAAwB,GAAI,gOAgB/B,6BAA6B,4CAwR/B,CAAC;AAEF,eAAe,wBAAwB,CAAC"}