@itwin/map-layers 3.4.0-dev.9 → 3.4.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 (168) hide show
  1. package/CHANGELOG.md +54 -1
  2. package/lib/cjs/MapLayerPreferences.d.ts +88 -88
  3. package/lib/cjs/MapLayerPreferences.js +311 -311
  4. package/lib/cjs/MapLayerPreferences.js.map +1 -1
  5. package/lib/cjs/map-layers.d.ts +6 -6
  6. package/lib/cjs/map-layers.js +22 -22
  7. package/lib/cjs/mapLayers.d.ts +44 -44
  8. package/lib/cjs/mapLayers.js +63 -63
  9. package/lib/cjs/mapLayers.js.map +1 -1
  10. package/lib/cjs/ui/FeatureInfoUiItemsProvider.d.ts +10 -10
  11. package/lib/cjs/ui/FeatureInfoUiItemsProvider.js +50 -50
  12. package/lib/cjs/ui/FeatureInfoUiItemsProvider.js.map +1 -1
  13. package/lib/cjs/ui/Interfaces.d.ts +47 -47
  14. package/lib/cjs/ui/Interfaces.js +2 -2
  15. package/lib/cjs/ui/Interfaces.js.map +1 -1
  16. package/lib/cjs/ui/MapFeatureInfoTool.d.ts +13 -13
  17. package/lib/cjs/ui/MapFeatureInfoTool.d.ts.map +1 -1
  18. package/lib/cjs/ui/MapFeatureInfoTool.js +50 -50
  19. package/lib/cjs/ui/MapFeatureInfoTool.js.map +1 -1
  20. package/lib/cjs/ui/MapLayersUiItemsProvider.d.ts +8 -8
  21. package/lib/cjs/ui/MapLayersUiItemsProvider.js +38 -38
  22. package/lib/cjs/ui/MapLayersUiItemsProvider.js.map +1 -1
  23. package/lib/cjs/ui/widget/AttachLayerPopupButton.d.ts +14 -13
  24. package/lib/cjs/ui/widget/AttachLayerPopupButton.d.ts.map +1 -1
  25. package/lib/cjs/ui/widget/AttachLayerPopupButton.js +338 -338
  26. package/lib/cjs/ui/widget/AttachLayerPopupButton.js.map +1 -1
  27. package/lib/cjs/ui/widget/BasemapPanel.d.ts +8 -4
  28. package/lib/cjs/ui/widget/BasemapPanel.d.ts.map +1 -1
  29. package/lib/cjs/ui/widget/BasemapPanel.js +156 -145
  30. package/lib/cjs/ui/widget/BasemapPanel.js.map +1 -1
  31. package/lib/cjs/ui/widget/BasemapPanel.scss +87 -87
  32. package/lib/cjs/ui/widget/ConfirmMessageDialog.d.ts +21 -21
  33. package/lib/cjs/ui/widget/ConfirmMessageDialog.js +25 -25
  34. package/lib/cjs/ui/widget/ConfirmMessageDialog.js.map +1 -1
  35. package/lib/cjs/ui/widget/FeatureInfoDataProvider.d.ts +40 -40
  36. package/lib/cjs/ui/widget/FeatureInfoDataProvider.js +139 -139
  37. package/lib/cjs/ui/widget/FeatureInfoDataProvider.js.map +1 -1
  38. package/lib/cjs/ui/widget/FeatureInfoWidget.d.ts +7 -7
  39. package/lib/cjs/ui/widget/FeatureInfoWidget.js +71 -71
  40. package/lib/cjs/ui/widget/FeatureInfoWidget.js.map +1 -1
  41. package/lib/cjs/ui/widget/MapLayerDroppable.d.ts +19 -18
  42. package/lib/cjs/ui/widget/MapLayerDroppable.d.ts.map +1 -1
  43. package/lib/cjs/ui/widget/MapLayerDroppable.js +86 -86
  44. package/lib/cjs/ui/widget/MapLayerDroppable.js.map +1 -1
  45. package/lib/cjs/ui/widget/MapLayerManager.d.ts +26 -26
  46. package/lib/cjs/ui/widget/MapLayerManager.d.ts.map +1 -1
  47. package/lib/cjs/ui/widget/MapLayerManager.js +375 -367
  48. package/lib/cjs/ui/widget/MapLayerManager.js.map +1 -1
  49. package/lib/cjs/ui/widget/MapLayerManager.scss +389 -383
  50. package/lib/cjs/ui/widget/MapLayerSettingsMenu.d.ts +12 -9
  51. package/lib/cjs/ui/widget/MapLayerSettingsMenu.d.ts.map +1 -1
  52. package/lib/cjs/ui/widget/MapLayerSettingsMenu.js +83 -83
  53. package/lib/cjs/ui/widget/MapLayerSettingsMenu.js.map +1 -1
  54. package/lib/cjs/ui/widget/MapLayerSettingsPopupButton.d.ts +7 -4
  55. package/lib/cjs/ui/widget/MapLayerSettingsPopupButton.d.ts.map +1 -1
  56. package/lib/cjs/ui/widget/MapLayerSettingsPopupButton.js +65 -65
  57. package/lib/cjs/ui/widget/MapLayerSettingsPopupButton.js.map +1 -1
  58. package/lib/cjs/ui/widget/MapLayerSettingsPopupButton.scss +20 -20
  59. package/lib/cjs/ui/widget/MapLayersWidget.d.ts +11 -11
  60. package/lib/cjs/ui/widget/MapLayersWidget.js +26 -26
  61. package/lib/cjs/ui/widget/MapLayersWidget.js.map +1 -1
  62. package/lib/cjs/ui/widget/MapManagerSettings.d.ts +3 -3
  63. package/lib/cjs/ui/widget/MapManagerSettings.js +200 -200
  64. package/lib/cjs/ui/widget/MapManagerSettings.js.map +1 -1
  65. package/lib/cjs/ui/widget/MapManagerSettings.scss +29 -29
  66. package/lib/cjs/ui/widget/MapUrlDialog.d.ts +22 -22
  67. package/lib/cjs/ui/widget/MapUrlDialog.js +530 -530
  68. package/lib/cjs/ui/widget/MapUrlDialog.js.map +1 -1
  69. package/lib/cjs/ui/widget/MapUrlDialog.scss +87 -87
  70. package/lib/cjs/ui/widget/SubLayersDataProvider.d.ts +20 -20
  71. package/lib/cjs/ui/widget/SubLayersDataProvider.js +76 -76
  72. package/lib/cjs/ui/widget/SubLayersDataProvider.js.map +1 -1
  73. package/lib/cjs/ui/widget/SubLayersPopupButton.d.ts +10 -10
  74. package/lib/cjs/ui/widget/SubLayersPopupButton.js +40 -40
  75. package/lib/cjs/ui/widget/SubLayersPopupButton.js.map +1 -1
  76. package/lib/cjs/ui/widget/SubLayersTree.d.ts +15 -15
  77. package/lib/cjs/ui/widget/SubLayersTree.js +414 -414
  78. package/lib/cjs/ui/widget/SubLayersTree.js.map +1 -1
  79. package/lib/cjs/ui/widget/SubLayersTree.scss +69 -69
  80. package/lib/cjs/ui/widget/TransparencyPopupButton.d.ts +14 -13
  81. package/lib/cjs/ui/widget/TransparencyPopupButton.d.ts.map +1 -1
  82. package/lib/cjs/ui/widget/TransparencyPopupButton.js +47 -47
  83. package/lib/cjs/ui/widget/TransparencyPopupButton.js.map +1 -1
  84. package/lib/cjs/ui/widget/TransparencyPopupButton.scss +36 -32
  85. package/lib/esm/MapLayerPreferences.d.ts +88 -88
  86. package/lib/esm/MapLayerPreferences.js +307 -307
  87. package/lib/esm/MapLayerPreferences.js.map +1 -1
  88. package/lib/esm/map-layers.d.ts +6 -6
  89. package/lib/esm/map-layers.js +10 -10
  90. package/lib/esm/mapLayers.d.ts +44 -44
  91. package/lib/esm/mapLayers.js +59 -59
  92. package/lib/esm/mapLayers.js.map +1 -1
  93. package/lib/esm/ui/FeatureInfoUiItemsProvider.d.ts +10 -10
  94. package/lib/esm/ui/FeatureInfoUiItemsProvider.js +46 -46
  95. package/lib/esm/ui/FeatureInfoUiItemsProvider.js.map +1 -1
  96. package/lib/esm/ui/Interfaces.d.ts +47 -47
  97. package/lib/esm/ui/Interfaces.js +1 -1
  98. package/lib/esm/ui/Interfaces.js.map +1 -1
  99. package/lib/esm/ui/MapFeatureInfoTool.d.ts +13 -13
  100. package/lib/esm/ui/MapFeatureInfoTool.d.ts.map +1 -1
  101. package/lib/esm/ui/MapFeatureInfoTool.js +45 -45
  102. package/lib/esm/ui/MapFeatureInfoTool.js.map +1 -1
  103. package/lib/esm/ui/MapLayersUiItemsProvider.d.ts +8 -8
  104. package/lib/esm/ui/MapLayersUiItemsProvider.js +34 -34
  105. package/lib/esm/ui/MapLayersUiItemsProvider.js.map +1 -1
  106. package/lib/esm/ui/widget/AttachLayerPopupButton.d.ts +14 -13
  107. package/lib/esm/ui/widget/AttachLayerPopupButton.d.ts.map +1 -1
  108. package/lib/esm/ui/widget/AttachLayerPopupButton.js +334 -334
  109. package/lib/esm/ui/widget/AttachLayerPopupButton.js.map +1 -1
  110. package/lib/esm/ui/widget/BasemapPanel.d.ts +8 -4
  111. package/lib/esm/ui/widget/BasemapPanel.d.ts.map +1 -1
  112. package/lib/esm/ui/widget/BasemapPanel.js +152 -141
  113. package/lib/esm/ui/widget/BasemapPanel.js.map +1 -1
  114. package/lib/esm/ui/widget/BasemapPanel.scss +87 -87
  115. package/lib/esm/ui/widget/ConfirmMessageDialog.d.ts +21 -21
  116. package/lib/esm/ui/widget/ConfirmMessageDialog.js +21 -21
  117. package/lib/esm/ui/widget/ConfirmMessageDialog.js.map +1 -1
  118. package/lib/esm/ui/widget/FeatureInfoDataProvider.d.ts +40 -40
  119. package/lib/esm/ui/widget/FeatureInfoDataProvider.js +135 -135
  120. package/lib/esm/ui/widget/FeatureInfoDataProvider.js.map +1 -1
  121. package/lib/esm/ui/widget/FeatureInfoWidget.d.ts +7 -7
  122. package/lib/esm/ui/widget/FeatureInfoWidget.js +67 -67
  123. package/lib/esm/ui/widget/FeatureInfoWidget.js.map +1 -1
  124. package/lib/esm/ui/widget/MapLayerDroppable.d.ts +19 -18
  125. package/lib/esm/ui/widget/MapLayerDroppable.d.ts.map +1 -1
  126. package/lib/esm/ui/widget/MapLayerDroppable.js +82 -82
  127. package/lib/esm/ui/widget/MapLayerDroppable.js.map +1 -1
  128. package/lib/esm/ui/widget/MapLayerManager.d.ts +26 -26
  129. package/lib/esm/ui/widget/MapLayerManager.d.ts.map +1 -1
  130. package/lib/esm/ui/widget/MapLayerManager.js +370 -362
  131. package/lib/esm/ui/widget/MapLayerManager.js.map +1 -1
  132. package/lib/esm/ui/widget/MapLayerManager.scss +389 -383
  133. package/lib/esm/ui/widget/MapLayerSettingsMenu.d.ts +12 -9
  134. package/lib/esm/ui/widget/MapLayerSettingsMenu.d.ts.map +1 -1
  135. package/lib/esm/ui/widget/MapLayerSettingsMenu.js +79 -79
  136. package/lib/esm/ui/widget/MapLayerSettingsMenu.js.map +1 -1
  137. package/lib/esm/ui/widget/MapLayerSettingsPopupButton.d.ts +7 -4
  138. package/lib/esm/ui/widget/MapLayerSettingsPopupButton.d.ts.map +1 -1
  139. package/lib/esm/ui/widget/MapLayerSettingsPopupButton.js +61 -61
  140. package/lib/esm/ui/widget/MapLayerSettingsPopupButton.js.map +1 -1
  141. package/lib/esm/ui/widget/MapLayerSettingsPopupButton.scss +20 -20
  142. package/lib/esm/ui/widget/MapLayersWidget.d.ts +11 -11
  143. package/lib/esm/ui/widget/MapLayersWidget.js +22 -22
  144. package/lib/esm/ui/widget/MapLayersWidget.js.map +1 -1
  145. package/lib/esm/ui/widget/MapManagerSettings.d.ts +3 -3
  146. package/lib/esm/ui/widget/MapManagerSettings.js +196 -196
  147. package/lib/esm/ui/widget/MapManagerSettings.js.map +1 -1
  148. package/lib/esm/ui/widget/MapManagerSettings.scss +29 -29
  149. package/lib/esm/ui/widget/MapUrlDialog.d.ts +22 -22
  150. package/lib/esm/ui/widget/MapUrlDialog.js +526 -526
  151. package/lib/esm/ui/widget/MapUrlDialog.js.map +1 -1
  152. package/lib/esm/ui/widget/MapUrlDialog.scss +87 -87
  153. package/lib/esm/ui/widget/SubLayersDataProvider.d.ts +20 -20
  154. package/lib/esm/ui/widget/SubLayersDataProvider.js +72 -72
  155. package/lib/esm/ui/widget/SubLayersDataProvider.js.map +1 -1
  156. package/lib/esm/ui/widget/SubLayersPopupButton.d.ts +10 -10
  157. package/lib/esm/ui/widget/SubLayersPopupButton.js +36 -36
  158. package/lib/esm/ui/widget/SubLayersPopupButton.js.map +1 -1
  159. package/lib/esm/ui/widget/SubLayersTree.d.ts +15 -15
  160. package/lib/esm/ui/widget/SubLayersTree.js +409 -409
  161. package/lib/esm/ui/widget/SubLayersTree.js.map +1 -1
  162. package/lib/esm/ui/widget/SubLayersTree.scss +69 -69
  163. package/lib/esm/ui/widget/TransparencyPopupButton.d.ts +14 -13
  164. package/lib/esm/ui/widget/TransparencyPopupButton.d.ts.map +1 -1
  165. package/lib/esm/ui/widget/TransparencyPopupButton.js +43 -43
  166. package/lib/esm/ui/widget/TransparencyPopupButton.js.map +1 -1
  167. package/lib/esm/ui/widget/TransparencyPopupButton.scss +36 -32
  168. package/package.json +30 -30
@@ -1,363 +1,371 @@
1
- /*---------------------------------------------------------------------------------------------
2
- * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
3
- * See LICENSE.md in the project root for license terms and full copyright notice.
4
- *--------------------------------------------------------------------------------------------*/
5
- // cSpell:ignore droppable Sublayer Basemap
6
- // the following quiet warning caused by react-beautiful-dnd package
7
- /* eslint-disable @typescript-eslint/unbound-method */
8
- import { assert, BentleyError } from "@itwin/core-bentley";
9
- import { ImageMapLayerSettings } from "@itwin/core-common";
10
- import { ImageryMapTileTree, IModelApp, MapLayerSources, NotifyMessageDetails, OutputMessagePriority, } from "@itwin/core-frontend";
11
- import { ToggleSwitch } from "@itwin/itwinui-react";
12
- import * as React from "react";
13
- import { DragDropContext } from "react-beautiful-dnd";
14
- import { MapLayerPreferences, MapLayerSourceChangeType } from "../../MapLayerPreferences";
15
- import { AttachLayerPopupButton } from "./AttachLayerPopupButton";
16
- import { BasemapPanel } from "./BasemapPanel";
17
- import { MapLayerDroppable } from "./MapLayerDroppable";
18
- import "./MapLayerManager.scss";
19
- import { MapLayerSettingsPopupButton } from "./MapLayerSettingsPopupButton";
20
- import { MapLayersUI } from "../../mapLayers";
21
- /** @internal */
22
- export const SourceMapContext = React.createContext({
23
- sources: [],
24
- loadingSources: false,
25
- bases: [],
26
- refreshFromStyle: () => { },
27
- });
28
- /** @internal */
29
- export function useSourceMapContext() {
30
- return React.useContext(SourceMapContext);
31
- }
32
- function getSubLayerProps(subLayerSettings) {
33
- return subLayerSettings.map((subLayer) => subLayer.toJSON());
34
- }
35
- function getMapLayerSettingsFromViewport(viewport, getBackgroundMap, populateSubLayers = true) {
36
- const displayStyle = viewport.displayStyle;
37
- if (!displayStyle)
38
- return undefined;
39
- const layers = new Array();
40
- const displayStyleLayers = (getBackgroundMap ? displayStyle.backgroundMapLayers : displayStyle.overlayMapLayers);
41
- for (let layerIdx = 0; layerIdx < displayStyleLayers.length; layerIdx++) {
42
- const layerSettings = displayStyleLayers[layerIdx];
43
- const isOverlay = !getBackgroundMap;
44
- const layerProvider = viewport.getMapLayerImageryProvider(layerIdx, isOverlay);
45
- const popSubLayers = populateSubLayers && (layerSettings instanceof ImageMapLayerSettings);
46
- layers.push({
47
- visible: layerSettings.visible,
48
- name: layerSettings.name,
49
- source: layerSettings.source,
50
- transparency: layerSettings.transparency,
51
- transparentBackground: layerSettings.transparentBackground,
52
- subLayers: popSubLayers ? getSubLayerProps(layerSettings.subLayers) : undefined,
53
- showSubLayers: false,
54
- isOverlay,
55
- provider: layerProvider,
56
- });
57
- }
58
- // since we want to display higher level maps above lower maps in UI reverse their order here.
59
- return layers.reverse();
60
- }
61
- // eslint-disable-next-line @typescript-eslint/naming-convention
62
- export function MapLayerManager(props) {
63
- var _a, _b;
64
- const [mapSources, setMapSources] = React.useState();
65
- const [loadingSources, setLoadingSources] = React.useState(false);
66
- const [baseSources, setBaseSources] = React.useState();
67
- const [overlaysLabel] = React.useState(MapLayersUI.localization.getLocalizedString("mapLayers:Widget.OverlayLayers"));
68
- const [underlaysLabel] = React.useState(MapLayersUI.localization.getLocalizedString("mapLayers:Widget.BackgroundLayers"));
69
- const { activeViewport, mapLayerOptions } = props;
70
- const hideExternalMapLayersSection = (mapLayerOptions === null || mapLayerOptions === void 0 ? void 0 : mapLayerOptions.hideExternalMapLayers) ? mapLayerOptions.hideExternalMapLayers : false;
71
- const fetchPublicMapLayerSources = (mapLayerOptions === null || mapLayerOptions === void 0 ? void 0 : mapLayerOptions.fetchPublicMapLayerSources) ? mapLayerOptions.fetchPublicMapLayerSources : false;
72
- // map layer settings from display style
73
- const [backgroundMapLayers, setBackgroundMapLayers] = React.useState(getMapLayerSettingsFromViewport(activeViewport, true));
74
- const [overlayMapLayers, setOverlayMapLayers] = React.useState(getMapLayerSettingsFromViewport(activeViewport, false));
75
- const loadMapLayerSettingsFromViewport = React.useCallback((viewport) => {
76
- setBackgroundMapLayers(getMapLayerSettingsFromViewport(viewport, true));
77
- setOverlayMapLayers(getMapLayerSettingsFromViewport(viewport, false));
78
- }, [setBackgroundMapLayers, setOverlayMapLayers]);
79
- const [backgroundMapVisible, setBackgroundMapVisible] = React.useState(() => {
80
- if (activeViewport) {
81
- return activeViewport.viewFlags.backgroundMap;
82
- }
83
- return false;
84
- });
85
- // 'isMounted' is used to prevent any async operation once the hook has been
86
- // unloaded. Otherwise we get a 'Can't perform a React state update on an unmounted component.' warning in the console.
87
- const isMounted = React.useRef(false);
88
- React.useEffect(() => {
89
- isMounted.current = true;
90
- return () => {
91
- isMounted.current = false;
92
- };
93
- });
94
- // Setup onTileTreeLoad events listening.
95
- // This is needed because we need to know when the imagery provider
96
- // is created, and be able to monitor to status change.
97
- React.useEffect(() => {
98
- const handleTileTreeLoad = (args) => {
99
- // Ignore non-map tile trees
100
- if (args.tileTree instanceof ImageryMapTileTree) {
101
- loadMapLayerSettingsFromViewport(activeViewport);
102
- }
103
- };
104
- IModelApp.tileAdmin.onTileTreeLoad.addListener(handleTileTreeLoad);
105
- return () => {
106
- IModelApp.tileAdmin.onTileTreeLoad.removeListener(handleTileTreeLoad);
107
- };
108
- }, [activeViewport, loadMapLayerSettingsFromViewport]);
109
- // Setup onMapImageryChanged events listening.
110
- React.useEffect(() => {
111
- const handleMapImageryChanged = (args) => {
112
- if (args.backgroundLayers.length !== (backgroundMapLayers ? backgroundMapLayers.length : 0)
113
- || args.overlayLayers.length !== (overlayMapLayers ? overlayMapLayers.length : 0)) {
114
- loadMapLayerSettingsFromViewport(activeViewport);
115
- }
116
- };
117
- activeViewport === null || activeViewport === void 0 ? void 0 : activeViewport.displayStyle.settings.onMapImageryChanged.addListener(handleMapImageryChanged);
118
- return () => {
119
- activeViewport === null || activeViewport === void 0 ? void 0 : activeViewport.displayStyle.settings.onMapImageryChanged.removeListener(handleMapImageryChanged);
120
- };
121
- }, [activeViewport, backgroundMapLayers, loadMapLayerSettingsFromViewport, overlayMapLayers]);
122
- const handleProviderStatusChanged = React.useCallback((_args) => {
123
- loadMapLayerSettingsFromViewport(activeViewport);
124
- }, [loadMapLayerSettingsFromViewport, activeViewport]);
125
- // Triggered whenever a provider status change
126
- React.useEffect(() => {
127
- backgroundMapLayers === null || backgroundMapLayers === void 0 ? void 0 : backgroundMapLayers.forEach((layer) => { var _a; (_a = layer.provider) === null || _a === void 0 ? void 0 : _a.onStatusChanged.addListener(handleProviderStatusChanged); });
128
- overlayMapLayers === null || overlayMapLayers === void 0 ? void 0 : overlayMapLayers.forEach((layer) => { var _a; (_a = layer.provider) === null || _a === void 0 ? void 0 : _a.onStatusChanged.addListener(handleProviderStatusChanged); });
129
- return () => {
130
- backgroundMapLayers === null || backgroundMapLayers === void 0 ? void 0 : backgroundMapLayers.forEach((layer) => { var _a; (_a = layer.provider) === null || _a === void 0 ? void 0 : _a.onStatusChanged.removeListener(handleProviderStatusChanged); });
131
- overlayMapLayers === null || overlayMapLayers === void 0 ? void 0 : overlayMapLayers.forEach((layer) => { var _a; (_a = layer.provider) === null || _a === void 0 ? void 0 : _a.onStatusChanged.removeListener(handleProviderStatusChanged); });
132
- };
133
- }, [backgroundMapLayers, overlayMapLayers, activeViewport, loadMapLayerSettingsFromViewport, handleProviderStatusChanged]);
134
- React.useEffect(() => {
135
- async function fetchWmsMapData() {
136
- const sources = [];
137
- const bases = [];
138
- const sourceLayers = await MapLayerSources.create(undefined, (fetchPublicMapLayerSources && !hideExternalMapLayersSection));
139
- const iModel = IModelApp.viewManager.selectedView ? IModelApp.viewManager.selectedView.iModel : undefined;
140
- try {
141
- const preferenceSources = ((iModel === null || iModel === void 0 ? void 0 : iModel.iTwinId) === undefined
142
- ? []
143
- : await MapLayerPreferences.getSources(iModel === null || iModel === void 0 ? void 0 : iModel.iTwinId, iModel === null || iModel === void 0 ? void 0 : iModel.iModelId));
144
- for (const source of preferenceSources)
145
- await MapLayerSources.addSourceToMapLayerSources(source);
146
- }
147
- catch (err) {
148
- IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Error, IModelApp.localization.getLocalizedString("mapLayers:CustomAttach.ErrorLoadingLayers"), BentleyError.getErrorMessage(err)));
149
- }
150
- if (!isMounted.current) {
151
- return;
152
- }
153
- // This is where the list of layers first gets populated... I need to update it
154
- // MapUrlDialog gets around knowing MapLayerManager exists and vice versa by affecting the viewports displayStyle which MapLayerManager is listening for
155
- // We know when displayStyle changes we've added a layer, this layer may not be a custom layer
156
- sourceLayers === null || sourceLayers === void 0 ? void 0 : sourceLayers.layers.forEach((source) => { sources.push(source); });
157
- setMapSources(sources);
158
- sourceLayers === null || sourceLayers === void 0 ? void 0 : sourceLayers.bases.forEach((source) => { bases.push(source); });
159
- setBaseSources(bases);
160
- }
161
- setLoadingSources(true);
162
- fetchWmsMapData().then(() => {
163
- if (isMounted.current) {
164
- setLoadingSources(false);
165
- }
166
- }).catch(() => {
167
- if (isMounted.current) {
168
- setLoadingSources(false);
169
- }
170
- });
171
- }, [setMapSources, fetchPublicMapLayerSources, hideExternalMapLayersSection]);
172
- const updateMapSources = React.useCallback(() => {
173
- var _a, _b;
174
- const newSources = [];
175
- (_b = (_a = MapLayerSources.getInstance()) === null || _a === void 0 ? void 0 : _a.layers) === null || _b === void 0 ? void 0 : _b.forEach((sourceLayer) => { newSources.push(sourceLayer); });
176
- setMapSources(newSources);
177
- }, [setMapSources]);
178
- /**
179
- * Handle change events in the MapLayerPreferences
180
- */
181
- React.useEffect(() => {
182
- const handleLayerSourceChange = async (changeType, oldSource, newSource) => {
183
- const removeSourceOnly = (changeType === MapLayerSourceChangeType.Removed);
184
- const removeSource = (changeType === MapLayerSourceChangeType.Replaced || changeType === MapLayerSourceChangeType.Removed);
185
- const addSource = (changeType === MapLayerSourceChangeType.Replaced || changeType === MapLayerSourceChangeType.Added);
186
- if (removeSource) {
187
- if (oldSource) {
188
- const succeeded = MapLayerSources.removeLayerByName(oldSource.name);
189
- assert(succeeded);
190
- if (!succeeded) {
191
- return;
192
- }
193
- if (removeSourceOnly) {
194
- updateMapSources();
195
- return;
196
- }
197
- }
198
- }
199
- if (addSource) {
200
- const sources = await MapLayerSources.addSourceToMapLayerSources(newSource);
201
- assert(sources !== undefined);
202
- if (sources) {
203
- updateMapSources();
204
- }
205
- }
206
- };
207
- MapLayerPreferences.onLayerSourceChanged.addListener(handleLayerSourceChange);
208
- return (() => {
209
- MapLayerPreferences.onLayerSourceChanged.removeListener(handleLayerSourceChange);
210
- });
211
- }, [updateMapSources]);
212
- // update when a different display style is loaded.
213
- React.useEffect(() => {
214
- const handleDisplayStyleChange = (vp) => {
215
- loadMapLayerSettingsFromViewport(vp);
216
- };
217
- activeViewport === null || activeViewport === void 0 ? void 0 : activeViewport.onDisplayStyleChanged.addListener(handleDisplayStyleChange);
218
- return () => {
219
- activeViewport === null || activeViewport === void 0 ? void 0 : activeViewport.onDisplayStyleChanged.removeListener(handleDisplayStyleChange);
220
- };
221
- }, [activeViewport, loadMapLayerSettingsFromViewport]);
222
- const handleOnMenuItemSelection = React.useCallback((action, mapLayerSettings) => {
223
- if (!activeViewport || !activeViewport.displayStyle)
224
- return;
225
- const indexInDisplayStyle = activeViewport.displayStyle.findMapLayerIndexByNameAndSource(mapLayerSettings.name, mapLayerSettings.source, mapLayerSettings.isOverlay);
226
- if (indexInDisplayStyle < 0)
227
- return;
228
- switch (action) {
229
- case "delete":
230
- activeViewport.displayStyle.detachMapLayerByIndex(indexInDisplayStyle, mapLayerSettings.isOverlay);
231
- break;
232
- case "zoom-to-layer":
233
- activeViewport.displayStyle.viewMapLayerRange(indexInDisplayStyle, mapLayerSettings.isOverlay, activeViewport).then((status) => {
234
- if (!status) {
235
- const msg = MapLayersUI.localization.getLocalizedString("mapLayers:Messages.NoRangeDefined");
236
- IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Error, `${msg} [${mapLayerSettings.name}]`));
237
- }
238
- }).catch((_error) => { });
239
- break;
240
- }
241
- activeViewport.invalidateRenderPlan();
242
- // force UI to update
243
- loadMapLayerSettingsFromViewport(activeViewport);
244
- }, [activeViewport, loadMapLayerSettingsFromViewport]);
245
- const handleLayerVisibilityChange = React.useCallback((mapLayerSettings) => {
246
- if (activeViewport) {
247
- const isVisible = !mapLayerSettings.visible;
248
- const displayStyle = activeViewport.displayStyle;
249
- const indexInDisplayStyle = displayStyle.findMapLayerIndexByNameAndSource(mapLayerSettings.name, mapLayerSettings.source, mapLayerSettings.isOverlay);
250
- if (-1 !== indexInDisplayStyle) {
251
- // update the display style
252
- displayStyle.changeMapLayerProps({ visible: isVisible }, indexInDisplayStyle, mapLayerSettings.isOverlay);
253
- activeViewport.invalidateRenderPlan();
254
- // force UI to update
255
- loadMapLayerSettingsFromViewport(activeViewport);
256
- }
257
- }
258
- }, [activeViewport, loadMapLayerSettingsFromViewport]);
259
- const handleMapLayersToggle = React.useCallback(() => {
260
- if (activeViewport) {
261
- const newState = !backgroundMapVisible;
262
- activeViewport.viewFlags = activeViewport.viewFlags.with("backgroundMap", newState);
263
- setBackgroundMapVisible(newState);
264
- }
265
- }, [backgroundMapVisible, setBackgroundMapVisible, activeViewport]);
266
- const handleOnMapLayerDragEnd = React.useCallback((result /* , _provided: ResponderProvided*/) => {
267
- const { destination, source } = result;
268
- if (!destination) // dropped outside of list
269
- return;
270
- // item was not moved
271
- if (destination.droppableId === source.droppableId && destination.index === source.index)
272
- return;
273
- let fromMapLayer;
274
- if (source.droppableId === "overlayMapLayers" && overlayMapLayers)
275
- fromMapLayer = overlayMapLayers[source.index];
276
- else if (source.droppableId === "backgroundMapLayers" && backgroundMapLayers)
277
- fromMapLayer = backgroundMapLayers[source.index];
278
- if (!fromMapLayer || !activeViewport)
279
- return;
280
- const displayStyle = activeViewport.displayStyle;
281
- let toMapLayer;
282
- let toIndexInDisplayStyle = -1;
283
- // If destination.index is undefined then the user dropped the map at the end of list of maps. To get the "actual" index in the style, look up index in style by name.
284
- // We need to do this because the order of layers in UI are reversed so higher layers appear above lower layers.
285
- if (undefined !== destination.index) {
286
- if (destination.droppableId === "overlayMapLayers" && overlayMapLayers)
287
- toMapLayer = overlayMapLayers[destination.index];
288
- else if (destination.droppableId === "backgroundMapLayers" && backgroundMapLayers)
289
- toMapLayer = backgroundMapLayers[destination.index];
290
- if (toMapLayer)
291
- toIndexInDisplayStyle = displayStyle.findMapLayerIndexByNameAndSource(toMapLayer.name, toMapLayer.source, toMapLayer.isOverlay);
292
- }
293
- const fromIndexInDisplayStyle = displayStyle.findMapLayerIndexByNameAndSource(fromMapLayer.name, fromMapLayer.source, fromMapLayer.isOverlay);
294
- if (fromIndexInDisplayStyle < 0)
295
- return;
296
- if (destination.droppableId !== source.droppableId) {
297
- // see if we moved from "overlayMapLayers" to "backgroundMapLayers" or vice-versa
298
- const settings = activeViewport.displayStyle.mapLayerAtIndex(fromIndexInDisplayStyle, fromMapLayer.isOverlay);
299
- if (settings) {
300
- activeViewport.displayStyle.detachMapLayerByIndex(fromIndexInDisplayStyle, fromMapLayer.isOverlay);
301
- // Manually reverse index when moved from one section to the other
302
- if (fromMapLayer.isOverlay && backgroundMapLayers) {
303
- toIndexInDisplayStyle = displayStyle.backgroundMapLayers.length - destination.index;
304
- }
305
- else if (!fromMapLayer.isOverlay && overlayMapLayers) {
306
- toIndexInDisplayStyle = overlayMapLayers.length - destination.index;
307
- }
308
- activeViewport.displayStyle.attachMapLayer({ settings, isOverlay: !fromMapLayer.isOverlay, insertIndex: toIndexInDisplayStyle });
309
- }
310
- }
311
- else {
312
- if (undefined === destination.index) {
313
- displayStyle.moveMapLayerToBottom(fromIndexInDisplayStyle, destination.droppableId === "overlayMapLayers");
314
- }
315
- else {
316
- if (toMapLayer) {
317
- if (toIndexInDisplayStyle !== -1)
318
- displayStyle.moveMapLayerToIndex(fromIndexInDisplayStyle, toIndexInDisplayStyle, toMapLayer.isOverlay);
319
- }
320
- }
321
- }
322
- // apply display style change to view
323
- activeViewport.invalidateRenderPlan();
324
- // force UI to update
325
- loadMapLayerSettingsFromViewport(activeViewport);
326
- }, [loadMapLayerSettingsFromViewport, activeViewport, overlayMapLayers, backgroundMapLayers]);
327
- const handleRefreshFromStyle = React.useCallback(() => {
328
- if (activeViewport)
329
- loadMapLayerSettingsFromViewport(activeViewport);
330
- }, [activeViewport, loadMapLayerSettingsFromViewport]);
331
- const [baseMapPanelLabel] = React.useState(MapLayersUI.localization.getLocalizedString("mapLayers:Basemap.BaseMapPanelTitle"));
332
- return (React.createElement(SourceMapContext.Provider, { value: {
333
- activeViewport,
334
- loadingSources,
335
- sources: mapSources ? mapSources : [],
336
- bases: baseSources ? baseSources : [],
337
- refreshFromStyle: handleRefreshFromStyle,
338
- backgroundLayers: backgroundMapLayers,
339
- overlayLayers: overlayMapLayers,
340
- mapLayerOptions,
341
- } },
342
- React.createElement("div", { className: "map-manager-top-header" },
343
- React.createElement("span", { className: "map-manager-header-label" }, baseMapPanelLabel),
344
- React.createElement("div", { className: "map-manager-header-buttons-group" },
345
- React.createElement(ToggleSwitch, { className: "map-manager-toggle", checked: backgroundMapVisible, onChange: handleMapLayersToggle }),
346
- React.createElement(MapLayerSettingsPopupButton, null))),
347
- React.createElement("div", { className: "map-manager-container" },
348
- React.createElement("div", { className: "map-manager-basemap" },
349
- React.createElement(BasemapPanel, null)),
350
- !hideExternalMapLayersSection &&
351
- React.createElement(DragDropContext, { onDragEnd: handleOnMapLayerDragEnd },
352
- React.createElement("div", { className: "map-manager-layer-wrapper" },
353
- React.createElement("div", { className: "map-manager-underlays" },
354
- React.createElement("span", { className: "map-manager-underlays-label" }, underlaysLabel),
355
- React.createElement(AttachLayerPopupButton, { isOverlay: false })),
356
- React.createElement(MapLayerDroppable, { isOverlay: false, layersList: backgroundMapLayers, mapTypesOptions: (_a = props.mapLayerOptions) === null || _a === void 0 ? void 0 : _a.mapTypeOptions, getContainerForClone: props.getContainerForClone, activeViewport: props.activeViewport, onMenuItemSelected: handleOnMenuItemSelection, onItemVisibilityToggleClicked: handleLayerVisibilityChange, onItemEdited: handleRefreshFromStyle })),
357
- React.createElement("div", { className: "map-manager-layer-wrapper" },
358
- React.createElement("div", { className: "map-manager-overlays" },
359
- React.createElement("span", { className: "map-manager-overlays-label" }, overlaysLabel),
360
- React.createElement(AttachLayerPopupButton, { isOverlay: true })),
361
- React.createElement(MapLayerDroppable, { isOverlay: true, layersList: overlayMapLayers, mapTypesOptions: (_b = props.mapLayerOptions) === null || _b === void 0 ? void 0 : _b.mapTypeOptions, getContainerForClone: props.getContainerForClone, activeViewport: props.activeViewport, onMenuItemSelected: handleOnMenuItemSelection, onItemVisibilityToggleClicked: handleLayerVisibilityChange, onItemEdited: handleRefreshFromStyle }))))));
362
- }
1
+ /*---------------------------------------------------------------------------------------------
2
+ * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
3
+ * See LICENSE.md in the project root for license terms and full copyright notice.
4
+ *--------------------------------------------------------------------------------------------*/
5
+ // cSpell:ignore droppable Sublayer Basemap
6
+ // the following quiet warning caused by react-beautiful-dnd package
7
+ /* eslint-disable @typescript-eslint/unbound-method */
8
+ import { assert, BentleyError } from "@itwin/core-bentley";
9
+ import { ImageMapLayerSettings } from "@itwin/core-common";
10
+ import { ImageryMapTileTree, IModelApp, MapLayerSources, NotifyMessageDetails, OutputMessagePriority, } from "@itwin/core-frontend";
11
+ import { ToggleSwitch } from "@itwin/itwinui-react";
12
+ import * as React from "react";
13
+ import { DragDropContext } from "react-beautiful-dnd";
14
+ import { MapLayerPreferences, MapLayerSourceChangeType } from "../../MapLayerPreferences";
15
+ import { AttachLayerPopupButton } from "./AttachLayerPopupButton";
16
+ import { BasemapPanel } from "./BasemapPanel";
17
+ import { MapLayerDroppable } from "./MapLayerDroppable";
18
+ import "./MapLayerManager.scss";
19
+ import { MapLayerSettingsPopupButton } from "./MapLayerSettingsPopupButton";
20
+ import { MapLayersUI } from "../../mapLayers";
21
+ /** @internal */
22
+ export const SourceMapContext = React.createContext({
23
+ sources: [],
24
+ loadingSources: false,
25
+ bases: [],
26
+ refreshFromStyle: () => { },
27
+ });
28
+ /** @internal */
29
+ export function useSourceMapContext() {
30
+ return React.useContext(SourceMapContext);
31
+ }
32
+ function getSubLayerProps(subLayerSettings) {
33
+ return subLayerSettings.map((subLayer) => subLayer.toJSON());
34
+ }
35
+ function getMapLayerSettingsFromViewport(viewport, getBackgroundMap, populateSubLayers = true) {
36
+ const displayStyle = viewport.displayStyle;
37
+ if (!displayStyle)
38
+ return undefined;
39
+ const layers = new Array();
40
+ const displayStyleLayers = (getBackgroundMap ? displayStyle.backgroundMapLayers : displayStyle.overlayMapLayers);
41
+ for (let layerIdx = 0; layerIdx < displayStyleLayers.length; layerIdx++) {
42
+ const layerSettings = displayStyleLayers[layerIdx];
43
+ const isOverlay = !getBackgroundMap;
44
+ const layerProvider = viewport.getMapLayerImageryProvider(layerIdx, isOverlay);
45
+ const popSubLayers = populateSubLayers && (layerSettings instanceof ImageMapLayerSettings);
46
+ layers.push({
47
+ visible: layerSettings.visible,
48
+ name: layerSettings.name,
49
+ source: layerSettings.source,
50
+ transparency: layerSettings.transparency,
51
+ transparentBackground: layerSettings.transparentBackground,
52
+ subLayers: popSubLayers ? getSubLayerProps(layerSettings.subLayers) : undefined,
53
+ showSubLayers: false,
54
+ isOverlay,
55
+ provider: layerProvider,
56
+ });
57
+ }
58
+ // since we want to display higher level maps above lower maps in UI reverse their order here.
59
+ return layers.reverse();
60
+ }
61
+ // eslint-disable-next-line @typescript-eslint/naming-convention
62
+ export function MapLayerManager(props) {
63
+ var _a, _b;
64
+ const [mapSources, setMapSources] = React.useState();
65
+ const [loadingSources, setLoadingSources] = React.useState(false);
66
+ const [baseSources, setBaseSources] = React.useState();
67
+ const [overlaysLabel] = React.useState(MapLayersUI.localization.getLocalizedString("mapLayers:Widget.OverlayLayers"));
68
+ const [underlaysLabel] = React.useState(MapLayersUI.localization.getLocalizedString("mapLayers:Widget.BackgroundLayers"));
69
+ const { activeViewport, mapLayerOptions } = props;
70
+ const hideExternalMapLayersSection = (mapLayerOptions === null || mapLayerOptions === void 0 ? void 0 : mapLayerOptions.hideExternalMapLayers) ? mapLayerOptions.hideExternalMapLayers : false;
71
+ const fetchPublicMapLayerSources = (mapLayerOptions === null || mapLayerOptions === void 0 ? void 0 : mapLayerOptions.fetchPublicMapLayerSources) ? mapLayerOptions.fetchPublicMapLayerSources : false;
72
+ // map layer settings from display style
73
+ const [backgroundMapLayers, setBackgroundMapLayers] = React.useState(getMapLayerSettingsFromViewport(activeViewport, true));
74
+ const [overlayMapLayers, setOverlayMapLayers] = React.useState(getMapLayerSettingsFromViewport(activeViewport, false));
75
+ const loadMapLayerSettingsFromViewport = React.useCallback((viewport) => {
76
+ setBackgroundMapLayers(getMapLayerSettingsFromViewport(viewport, true));
77
+ setOverlayMapLayers(getMapLayerSettingsFromViewport(viewport, false));
78
+ }, [setBackgroundMapLayers, setOverlayMapLayers]);
79
+ const [backgroundMapVisible, setBackgroundMapVisible] = React.useState(() => {
80
+ if (activeViewport) {
81
+ return activeViewport.viewFlags.backgroundMap;
82
+ }
83
+ return false;
84
+ });
85
+ // 'isMounted' is used to prevent any async operation once the hook has been
86
+ // unloaded. Otherwise we get a 'Can't perform a React state update on an unmounted component.' warning in the console.
87
+ const isMounted = React.useRef(false);
88
+ React.useEffect(() => {
89
+ isMounted.current = true;
90
+ return () => {
91
+ isMounted.current = false;
92
+ };
93
+ });
94
+ // Setup onTileTreeLoad events listening.
95
+ // This is needed because we need to know when the imagery provider
96
+ // is created, and be able to monitor to status change.
97
+ React.useEffect(() => {
98
+ const handleTileTreeLoad = (args) => {
99
+ // Ignore non-map tile trees
100
+ if (args.tileTree instanceof ImageryMapTileTree) {
101
+ loadMapLayerSettingsFromViewport(activeViewport);
102
+ }
103
+ };
104
+ IModelApp.tileAdmin.onTileTreeLoad.addListener(handleTileTreeLoad);
105
+ return () => {
106
+ IModelApp.tileAdmin.onTileTreeLoad.removeListener(handleTileTreeLoad);
107
+ };
108
+ }, [activeViewport, loadMapLayerSettingsFromViewport]);
109
+ // Setup onMapImageryChanged events listening.
110
+ React.useEffect(() => {
111
+ const handleMapImageryChanged = (args) => {
112
+ if (args.backgroundLayers.length !== (backgroundMapLayers ? backgroundMapLayers.length : 0)
113
+ || args.overlayLayers.length !== (overlayMapLayers ? overlayMapLayers.length : 0)) {
114
+ loadMapLayerSettingsFromViewport(activeViewport);
115
+ }
116
+ };
117
+ activeViewport === null || activeViewport === void 0 ? void 0 : activeViewport.displayStyle.settings.onMapImageryChanged.addListener(handleMapImageryChanged);
118
+ return () => {
119
+ activeViewport === null || activeViewport === void 0 ? void 0 : activeViewport.displayStyle.settings.onMapImageryChanged.removeListener(handleMapImageryChanged);
120
+ };
121
+ }, [activeViewport, backgroundMapLayers, loadMapLayerSettingsFromViewport, overlayMapLayers]);
122
+ const handleProviderStatusChanged = React.useCallback((_args) => {
123
+ loadMapLayerSettingsFromViewport(activeViewport);
124
+ }, [loadMapLayerSettingsFromViewport, activeViewport]);
125
+ // Triggered whenever a provider status change
126
+ React.useEffect(() => {
127
+ backgroundMapLayers === null || backgroundMapLayers === void 0 ? void 0 : backgroundMapLayers.forEach((layer) => { var _a; (_a = layer.provider) === null || _a === void 0 ? void 0 : _a.onStatusChanged.addListener(handleProviderStatusChanged); });
128
+ overlayMapLayers === null || overlayMapLayers === void 0 ? void 0 : overlayMapLayers.forEach((layer) => { var _a; (_a = layer.provider) === null || _a === void 0 ? void 0 : _a.onStatusChanged.addListener(handleProviderStatusChanged); });
129
+ return () => {
130
+ backgroundMapLayers === null || backgroundMapLayers === void 0 ? void 0 : backgroundMapLayers.forEach((layer) => { var _a; (_a = layer.provider) === null || _a === void 0 ? void 0 : _a.onStatusChanged.removeListener(handleProviderStatusChanged); });
131
+ overlayMapLayers === null || overlayMapLayers === void 0 ? void 0 : overlayMapLayers.forEach((layer) => { var _a; (_a = layer.provider) === null || _a === void 0 ? void 0 : _a.onStatusChanged.removeListener(handleProviderStatusChanged); });
132
+ };
133
+ }, [backgroundMapLayers, overlayMapLayers, activeViewport, loadMapLayerSettingsFromViewport, handleProviderStatusChanged]);
134
+ // Monitor viewport updates, and refresh the widget accordingly.
135
+ // Note: This is needed for multiple viewport applications.
136
+ React.useEffect(() => {
137
+ // Update background map status
138
+ setBackgroundMapVisible(activeViewport.viewFlags.backgroundMap);
139
+ // Refresh list of layers
140
+ loadMapLayerSettingsFromViewport(activeViewport);
141
+ }, [activeViewport, loadMapLayerSettingsFromViewport]);
142
+ React.useEffect(() => {
143
+ async function fetchWmsMapData() {
144
+ const sources = [];
145
+ const bases = [];
146
+ const sourceLayers = await MapLayerSources.create(undefined, (fetchPublicMapLayerSources && !hideExternalMapLayersSection));
147
+ const iModel = IModelApp.viewManager.selectedView ? IModelApp.viewManager.selectedView.iModel : undefined;
148
+ try {
149
+ const preferenceSources = ((iModel === null || iModel === void 0 ? void 0 : iModel.iTwinId) === undefined
150
+ ? []
151
+ : await MapLayerPreferences.getSources(iModel === null || iModel === void 0 ? void 0 : iModel.iTwinId, iModel === null || iModel === void 0 ? void 0 : iModel.iModelId));
152
+ for (const source of preferenceSources)
153
+ await MapLayerSources.addSourceToMapLayerSources(source);
154
+ }
155
+ catch (err) {
156
+ IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Error, IModelApp.localization.getLocalizedString("mapLayers:CustomAttach.ErrorLoadingLayers"), BentleyError.getErrorMessage(err)));
157
+ }
158
+ if (!isMounted.current) {
159
+ return;
160
+ }
161
+ // This is where the list of layers first gets populated... I need to update it
162
+ // MapUrlDialog gets around knowing MapLayerManager exists and vice versa by affecting the viewports displayStyle which MapLayerManager is listening for
163
+ // We know when displayStyle changes we've added a layer, this layer may not be a custom layer
164
+ sourceLayers === null || sourceLayers === void 0 ? void 0 : sourceLayers.layers.forEach((source) => { sources.push(source); });
165
+ setMapSources(sources);
166
+ sourceLayers === null || sourceLayers === void 0 ? void 0 : sourceLayers.bases.forEach((source) => { bases.push(source); });
167
+ setBaseSources(bases);
168
+ }
169
+ setLoadingSources(true);
170
+ fetchWmsMapData().then(() => {
171
+ if (isMounted.current) {
172
+ setLoadingSources(false);
173
+ }
174
+ }).catch(() => {
175
+ if (isMounted.current) {
176
+ setLoadingSources(false);
177
+ }
178
+ });
179
+ }, [setMapSources, fetchPublicMapLayerSources, hideExternalMapLayersSection]);
180
+ const updateMapSources = React.useCallback(() => {
181
+ var _a, _b;
182
+ const newSources = [];
183
+ (_b = (_a = MapLayerSources.getInstance()) === null || _a === void 0 ? void 0 : _a.layers) === null || _b === void 0 ? void 0 : _b.forEach((sourceLayer) => { newSources.push(sourceLayer); });
184
+ setMapSources(newSources);
185
+ }, [setMapSources]);
186
+ /**
187
+ * Handle change events in the MapLayerPreferences
188
+ */
189
+ React.useEffect(() => {
190
+ const handleLayerSourceChange = async (changeType, oldSource, newSource) => {
191
+ const removeSourceOnly = (changeType === MapLayerSourceChangeType.Removed);
192
+ const removeSource = (changeType === MapLayerSourceChangeType.Replaced || changeType === MapLayerSourceChangeType.Removed);
193
+ const addSource = (changeType === MapLayerSourceChangeType.Replaced || changeType === MapLayerSourceChangeType.Added);
194
+ if (removeSource) {
195
+ if (oldSource) {
196
+ const succeeded = MapLayerSources.removeLayerByName(oldSource.name);
197
+ assert(succeeded);
198
+ if (!succeeded) {
199
+ return;
200
+ }
201
+ if (removeSourceOnly) {
202
+ updateMapSources();
203
+ return;
204
+ }
205
+ }
206
+ }
207
+ if (addSource) {
208
+ const sources = await MapLayerSources.addSourceToMapLayerSources(newSource);
209
+ assert(sources !== undefined);
210
+ if (sources) {
211
+ updateMapSources();
212
+ }
213
+ }
214
+ };
215
+ MapLayerPreferences.onLayerSourceChanged.addListener(handleLayerSourceChange);
216
+ return (() => {
217
+ MapLayerPreferences.onLayerSourceChanged.removeListener(handleLayerSourceChange);
218
+ });
219
+ }, [updateMapSources]);
220
+ // update when a different display style is loaded.
221
+ React.useEffect(() => {
222
+ const handleDisplayStyleChange = (vp) => {
223
+ loadMapLayerSettingsFromViewport(vp);
224
+ };
225
+ activeViewport === null || activeViewport === void 0 ? void 0 : activeViewport.onDisplayStyleChanged.addListener(handleDisplayStyleChange);
226
+ return () => {
227
+ activeViewport === null || activeViewport === void 0 ? void 0 : activeViewport.onDisplayStyleChanged.removeListener(handleDisplayStyleChange);
228
+ };
229
+ }, [activeViewport, loadMapLayerSettingsFromViewport]);
230
+ const handleOnMenuItemSelection = React.useCallback((action, mapLayerSettings) => {
231
+ if (!activeViewport || !activeViewport.displayStyle)
232
+ return;
233
+ const indexInDisplayStyle = activeViewport.displayStyle.findMapLayerIndexByNameAndSource(mapLayerSettings.name, mapLayerSettings.source, mapLayerSettings.isOverlay);
234
+ if (indexInDisplayStyle < 0)
235
+ return;
236
+ switch (action) {
237
+ case "delete":
238
+ activeViewport.displayStyle.detachMapLayerByIndex(indexInDisplayStyle, mapLayerSettings.isOverlay);
239
+ break;
240
+ case "zoom-to-layer":
241
+ activeViewport.displayStyle.viewMapLayerRange(indexInDisplayStyle, mapLayerSettings.isOverlay, activeViewport).then((status) => {
242
+ if (!status) {
243
+ const msg = MapLayersUI.localization.getLocalizedString("mapLayers:Messages.NoRangeDefined");
244
+ IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Error, `${msg} [${mapLayerSettings.name}]`));
245
+ }
246
+ }).catch((_error) => { });
247
+ break;
248
+ }
249
+ activeViewport.invalidateRenderPlan();
250
+ // force UI to update
251
+ loadMapLayerSettingsFromViewport(activeViewport);
252
+ }, [activeViewport, loadMapLayerSettingsFromViewport]);
253
+ const handleLayerVisibilityChange = React.useCallback((mapLayerSettings) => {
254
+ if (activeViewport) {
255
+ const isVisible = !mapLayerSettings.visible;
256
+ const displayStyle = activeViewport.displayStyle;
257
+ const indexInDisplayStyle = displayStyle.findMapLayerIndexByNameAndSource(mapLayerSettings.name, mapLayerSettings.source, mapLayerSettings.isOverlay);
258
+ if (-1 !== indexInDisplayStyle) {
259
+ // update the display style
260
+ displayStyle.changeMapLayerProps({ visible: isVisible }, indexInDisplayStyle, mapLayerSettings.isOverlay);
261
+ activeViewport.invalidateRenderPlan();
262
+ // force UI to update
263
+ loadMapLayerSettingsFromViewport(activeViewport);
264
+ }
265
+ }
266
+ }, [activeViewport, loadMapLayerSettingsFromViewport]);
267
+ const handleMapLayersToggle = React.useCallback(() => {
268
+ if (activeViewport) {
269
+ const newState = !backgroundMapVisible;
270
+ activeViewport.viewFlags = activeViewport.viewFlags.with("backgroundMap", newState);
271
+ setBackgroundMapVisible(newState);
272
+ }
273
+ }, [backgroundMapVisible, setBackgroundMapVisible, activeViewport]);
274
+ const handleOnMapLayerDragEnd = React.useCallback((result /* , _provided: ResponderProvided*/) => {
275
+ const { destination, source } = result;
276
+ if (!destination) // dropped outside of list
277
+ return;
278
+ // item was not moved
279
+ if (destination.droppableId === source.droppableId && destination.index === source.index)
280
+ return;
281
+ let fromMapLayer;
282
+ if (source.droppableId === "overlayMapLayers" && overlayMapLayers)
283
+ fromMapLayer = overlayMapLayers[source.index];
284
+ else if (source.droppableId === "backgroundMapLayers" && backgroundMapLayers)
285
+ fromMapLayer = backgroundMapLayers[source.index];
286
+ if (!fromMapLayer || !activeViewport)
287
+ return;
288
+ const displayStyle = activeViewport.displayStyle;
289
+ let toMapLayer;
290
+ let toIndexInDisplayStyle = -1;
291
+ // If destination.index is undefined then the user dropped the map at the end of list of maps. To get the "actual" index in the style, look up index in style by name.
292
+ // We need to do this because the order of layers in UI are reversed so higher layers appear above lower layers.
293
+ if (undefined !== destination.index) {
294
+ if (destination.droppableId === "overlayMapLayers" && overlayMapLayers)
295
+ toMapLayer = overlayMapLayers[destination.index];
296
+ else if (destination.droppableId === "backgroundMapLayers" && backgroundMapLayers)
297
+ toMapLayer = backgroundMapLayers[destination.index];
298
+ if (toMapLayer)
299
+ toIndexInDisplayStyle = displayStyle.findMapLayerIndexByNameAndSource(toMapLayer.name, toMapLayer.source, toMapLayer.isOverlay);
300
+ }
301
+ const fromIndexInDisplayStyle = displayStyle.findMapLayerIndexByNameAndSource(fromMapLayer.name, fromMapLayer.source, fromMapLayer.isOverlay);
302
+ if (fromIndexInDisplayStyle < 0)
303
+ return;
304
+ if (destination.droppableId !== source.droppableId) {
305
+ // see if we moved from "overlayMapLayers" to "backgroundMapLayers" or vice-versa
306
+ const settings = activeViewport.displayStyle.mapLayerAtIndex(fromIndexInDisplayStyle, fromMapLayer.isOverlay);
307
+ if (settings) {
308
+ activeViewport.displayStyle.detachMapLayerByIndex(fromIndexInDisplayStyle, fromMapLayer.isOverlay);
309
+ // Manually reverse index when moved from one section to the other
310
+ if (fromMapLayer.isOverlay && backgroundMapLayers) {
311
+ toIndexInDisplayStyle = displayStyle.backgroundMapLayers.length - destination.index;
312
+ }
313
+ else if (!fromMapLayer.isOverlay && overlayMapLayers) {
314
+ toIndexInDisplayStyle = overlayMapLayers.length - destination.index;
315
+ }
316
+ activeViewport.displayStyle.attachMapLayer({ settings, isOverlay: !fromMapLayer.isOverlay, insertIndex: toIndexInDisplayStyle });
317
+ }
318
+ }
319
+ else {
320
+ if (undefined === destination.index) {
321
+ displayStyle.moveMapLayerToBottom(fromIndexInDisplayStyle, destination.droppableId === "overlayMapLayers");
322
+ }
323
+ else {
324
+ if (toMapLayer) {
325
+ if (toIndexInDisplayStyle !== -1)
326
+ displayStyle.moveMapLayerToIndex(fromIndexInDisplayStyle, toIndexInDisplayStyle, toMapLayer.isOverlay);
327
+ }
328
+ }
329
+ }
330
+ // apply display style change to view
331
+ activeViewport.invalidateRenderPlan();
332
+ // force UI to update
333
+ loadMapLayerSettingsFromViewport(activeViewport);
334
+ }, [loadMapLayerSettingsFromViewport, activeViewport, overlayMapLayers, backgroundMapLayers]);
335
+ const handleRefreshFromStyle = React.useCallback(() => {
336
+ if (activeViewport)
337
+ loadMapLayerSettingsFromViewport(activeViewport);
338
+ }, [activeViewport, loadMapLayerSettingsFromViewport]);
339
+ const [baseMapPanelLabel] = React.useState(MapLayersUI.localization.getLocalizedString("mapLayers:Basemap.BaseMapPanelTitle"));
340
+ return (React.createElement(SourceMapContext.Provider, { value: {
341
+ activeViewport,
342
+ loadingSources,
343
+ sources: mapSources ? mapSources : [],
344
+ bases: baseSources ? baseSources : [],
345
+ refreshFromStyle: handleRefreshFromStyle,
346
+ backgroundLayers: backgroundMapLayers,
347
+ overlayLayers: overlayMapLayers,
348
+ mapLayerOptions,
349
+ } },
350
+ React.createElement("div", { className: "map-manager-top-header" },
351
+ React.createElement("span", { className: "map-manager-header-label" }, baseMapPanelLabel),
352
+ React.createElement("div", { className: "map-manager-header-buttons-group" },
353
+ React.createElement(ToggleSwitch, { className: "map-manager-toggle", checked: backgroundMapVisible, onChange: handleMapLayersToggle }),
354
+ React.createElement(MapLayerSettingsPopupButton, { disabled: !backgroundMapVisible }))),
355
+ React.createElement("div", { className: "map-manager-container" },
356
+ React.createElement("div", { className: "map-manager-basemap" },
357
+ React.createElement(BasemapPanel, { disabled: !backgroundMapVisible })),
358
+ !hideExternalMapLayersSection &&
359
+ React.createElement(DragDropContext, { onDragEnd: handleOnMapLayerDragEnd },
360
+ React.createElement("div", { className: "map-manager-layer-wrapper" },
361
+ React.createElement("div", { className: "map-manager-underlays" },
362
+ React.createElement("span", { className: "map-manager-underlays-label" }, underlaysLabel),
363
+ React.createElement(AttachLayerPopupButton, { disabled: !backgroundMapVisible, isOverlay: false })),
364
+ React.createElement(MapLayerDroppable, { disabled: !backgroundMapVisible, isOverlay: false, layersList: backgroundMapLayers, mapTypesOptions: (_a = props.mapLayerOptions) === null || _a === void 0 ? void 0 : _a.mapTypeOptions, getContainerForClone: props.getContainerForClone, activeViewport: props.activeViewport, onMenuItemSelected: handleOnMenuItemSelection, onItemVisibilityToggleClicked: handleLayerVisibilityChange, onItemEdited: handleRefreshFromStyle })),
365
+ React.createElement("div", { className: "map-manager-layer-wrapper" },
366
+ React.createElement("div", { className: "map-manager-overlays" },
367
+ React.createElement("span", { className: "map-manager-overlays-label" }, overlaysLabel),
368
+ React.createElement(AttachLayerPopupButton, { disabled: !backgroundMapVisible, isOverlay: true })),
369
+ React.createElement(MapLayerDroppable, { disabled: !backgroundMapVisible, isOverlay: true, layersList: overlayMapLayers, mapTypesOptions: (_b = props.mapLayerOptions) === null || _b === void 0 ? void 0 : _b.mapTypeOptions, getContainerForClone: props.getContainerForClone, activeViewport: props.activeViewport, onMenuItemSelected: handleOnMenuItemSelection, onItemVisibilityToggleClicked: handleLayerVisibilityChange, onItemEdited: handleRefreshFromStyle }))))));
370
+ }
363
371
  //# sourceMappingURL=MapLayerManager.js.map