@itwin/map-layers 4.0.0-dev.28 → 4.0.0-dev.32
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.
- package/lib/cjs/MapLayerPreferences.d.ts +88 -88
- package/lib/cjs/MapLayerPreferences.js +311 -311
- package/lib/cjs/MapLayerPreferences.js.map +1 -1
- package/lib/cjs/map-layers.d.ts +6 -6
- package/lib/cjs/map-layers.js +22 -22
- package/lib/cjs/mapLayers.d.ts +44 -44
- package/lib/cjs/mapLayers.js +61 -61
- package/lib/cjs/mapLayers.js.map +1 -1
- package/lib/cjs/ui/FeatureInfoUiItemsProvider.d.ts +11 -11
- package/lib/cjs/ui/FeatureInfoUiItemsProvider.js +51 -51
- package/lib/cjs/ui/FeatureInfoUiItemsProvider.js.map +1 -1
- package/lib/cjs/ui/Interfaces.d.ts +50 -50
- package/lib/cjs/ui/Interfaces.js +2 -2
- package/lib/cjs/ui/Interfaces.js.map +1 -1
- package/lib/cjs/ui/MapFeatureInfoTool.d.ts +13 -13
- package/lib/cjs/ui/MapFeatureInfoTool.js +50 -50
- package/lib/cjs/ui/MapFeatureInfoTool.js.map +1 -1
- package/lib/cjs/ui/MapLayersUiItemsProvider.d.ts +8 -8
- package/lib/cjs/ui/MapLayersUiItemsProvider.js +38 -38
- package/lib/cjs/ui/MapLayersUiItemsProvider.js.map +1 -1
- package/lib/cjs/ui/widget/AttachLayerPopupButton.d.ts +14 -14
- package/lib/cjs/ui/widget/AttachLayerPopupButton.js +335 -335
- package/lib/cjs/ui/widget/BasemapPanel.d.ts +8 -8
- package/lib/cjs/ui/widget/BasemapPanel.js +156 -156
- package/lib/cjs/ui/widget/BasemapPanel.js.map +1 -1
- package/lib/cjs/ui/widget/BasemapPanel.scss +87 -87
- package/lib/cjs/ui/widget/ConfirmMessageDialog.d.ts +21 -21
- package/lib/cjs/ui/widget/ConfirmMessageDialog.js +22 -22
- package/lib/cjs/ui/widget/ConfirmMessageDialog.js.map +1 -1
- package/lib/cjs/ui/widget/FeatureInfoDataProvider.d.ts +40 -40
- package/lib/cjs/ui/widget/FeatureInfoDataProvider.js +138 -138
- package/lib/cjs/ui/widget/FeatureInfoDataProvider.js.map +1 -1
- package/lib/cjs/ui/widget/FeatureInfoWidget.d.ts +7 -7
- package/lib/cjs/ui/widget/FeatureInfoWidget.js +65 -65
- package/lib/cjs/ui/widget/FeatureInfoWidget.js.map +1 -1
- package/lib/cjs/ui/widget/MapLayerDroppable.d.ts +19 -19
- package/lib/cjs/ui/widget/MapLayerDroppable.js +85 -85
- package/lib/cjs/ui/widget/MapLayerDroppable.js.map +1 -1
- package/lib/cjs/ui/widget/MapLayerManager.d.ts +26 -26
- package/lib/cjs/ui/widget/MapLayerManager.js +401 -401
- package/lib/cjs/ui/widget/MapLayerManager.js.map +1 -1
- package/lib/cjs/ui/widget/MapLayerManager.scss +409 -409
- package/lib/cjs/ui/widget/MapLayerSettingsMenu.d.ts +12 -12
- package/lib/cjs/ui/widget/MapLayerSettingsMenu.js +82 -82
- package/lib/cjs/ui/widget/MapLayerSettingsMenu.js.map +1 -1
- package/lib/cjs/ui/widget/MapLayerSettingsPopupButton.d.ts +7 -7
- package/lib/cjs/ui/widget/MapLayerSettingsPopupButton.js +65 -65
- package/lib/cjs/ui/widget/MapLayerSettingsPopupButton.js.map +1 -1
- package/lib/cjs/ui/widget/MapLayerSettingsPopupButton.scss +20 -20
- package/lib/cjs/ui/widget/MapLayersWidget.d.ts +11 -11
- package/lib/cjs/ui/widget/MapLayersWidget.js +31 -31
- package/lib/cjs/ui/widget/MapLayersWidget.js.map +1 -1
- package/lib/cjs/ui/widget/MapManagerSettings.d.ts +3 -3
- package/lib/cjs/ui/widget/MapManagerSettings.js +200 -200
- package/lib/cjs/ui/widget/MapManagerSettings.js.map +1 -1
- package/lib/cjs/ui/widget/MapManagerSettings.scss +29 -29
- package/lib/cjs/ui/widget/MapUrlDialog.d.ts +23 -23
- package/lib/cjs/ui/widget/MapUrlDialog.d.ts.map +1 -1
- package/lib/cjs/ui/widget/MapUrlDialog.js +530 -524
- package/lib/cjs/ui/widget/MapUrlDialog.js.map +1 -1
- package/lib/cjs/ui/widget/MapUrlDialog.scss +100 -100
- package/lib/cjs/ui/widget/SelectMapFormat.d.ts +18 -18
- package/lib/cjs/ui/widget/SelectMapFormat.js +54 -54
- package/lib/cjs/ui/widget/SelectMapFormat.js.map +1 -1
- package/lib/cjs/ui/widget/SubLayersDataProvider.d.ts +20 -20
- package/lib/cjs/ui/widget/SubLayersDataProvider.js +75 -75
- package/lib/cjs/ui/widget/SubLayersDataProvider.js.map +1 -1
- package/lib/cjs/ui/widget/SubLayersPopupButton.d.ts +10 -10
- package/lib/cjs/ui/widget/SubLayersPopupButton.js +40 -40
- package/lib/cjs/ui/widget/SubLayersPopupButton.js.map +1 -1
- package/lib/cjs/ui/widget/SubLayersTree.d.ts +15 -15
- package/lib/cjs/ui/widget/SubLayersTree.js +413 -413
- package/lib/cjs/ui/widget/SubLayersTree.js.map +1 -1
- package/lib/cjs/ui/widget/SubLayersTree.scss +69 -69
- package/lib/cjs/ui/widget/TransparencyPopupButton.d.ts +14 -14
- package/lib/cjs/ui/widget/TransparencyPopupButton.js +47 -47
- package/lib/cjs/ui/widget/TransparencyPopupButton.js.map +1 -1
- package/lib/cjs/ui/widget/TransparencyPopupButton.scss +36 -36
- package/lib/esm/MapLayerPreferences.d.ts +88 -88
- package/lib/esm/MapLayerPreferences.js +307 -307
- package/lib/esm/MapLayerPreferences.js.map +1 -1
- package/lib/esm/map-layers.d.ts +6 -6
- package/lib/esm/map-layers.js +10 -10
- package/lib/esm/mapLayers.d.ts +44 -44
- package/lib/esm/mapLayers.js +57 -57
- package/lib/esm/mapLayers.js.map +1 -1
- package/lib/esm/ui/FeatureInfoUiItemsProvider.d.ts +11 -11
- package/lib/esm/ui/FeatureInfoUiItemsProvider.js +47 -47
- package/lib/esm/ui/FeatureInfoUiItemsProvider.js.map +1 -1
- package/lib/esm/ui/Interfaces.d.ts +50 -50
- package/lib/esm/ui/Interfaces.js +1 -1
- package/lib/esm/ui/Interfaces.js.map +1 -1
- package/lib/esm/ui/MapFeatureInfoTool.d.ts +13 -13
- package/lib/esm/ui/MapFeatureInfoTool.js +45 -45
- package/lib/esm/ui/MapFeatureInfoTool.js.map +1 -1
- package/lib/esm/ui/MapLayersUiItemsProvider.d.ts +8 -8
- package/lib/esm/ui/MapLayersUiItemsProvider.js +34 -34
- package/lib/esm/ui/MapLayersUiItemsProvider.js.map +1 -1
- package/lib/esm/ui/widget/AttachLayerPopupButton.d.ts +14 -14
- package/lib/esm/ui/widget/AttachLayerPopupButton.js +331 -331
- package/lib/esm/ui/widget/BasemapPanel.d.ts +8 -8
- package/lib/esm/ui/widget/BasemapPanel.js +152 -152
- package/lib/esm/ui/widget/BasemapPanel.js.map +1 -1
- package/lib/esm/ui/widget/BasemapPanel.scss +87 -87
- package/lib/esm/ui/widget/ConfirmMessageDialog.d.ts +21 -21
- package/lib/esm/ui/widget/ConfirmMessageDialog.js +18 -18
- package/lib/esm/ui/widget/ConfirmMessageDialog.js.map +1 -1
- package/lib/esm/ui/widget/FeatureInfoDataProvider.d.ts +40 -40
- package/lib/esm/ui/widget/FeatureInfoDataProvider.js +134 -134
- package/lib/esm/ui/widget/FeatureInfoDataProvider.js.map +1 -1
- package/lib/esm/ui/widget/FeatureInfoWidget.d.ts +7 -7
- package/lib/esm/ui/widget/FeatureInfoWidget.js +61 -61
- package/lib/esm/ui/widget/FeatureInfoWidget.js.map +1 -1
- package/lib/esm/ui/widget/MapLayerDroppable.d.ts +19 -19
- package/lib/esm/ui/widget/MapLayerDroppable.js +81 -81
- package/lib/esm/ui/widget/MapLayerDroppable.js.map +1 -1
- package/lib/esm/ui/widget/MapLayerManager.d.ts +26 -26
- package/lib/esm/ui/widget/MapLayerManager.js +396 -396
- package/lib/esm/ui/widget/MapLayerManager.js.map +1 -1
- package/lib/esm/ui/widget/MapLayerManager.scss +409 -409
- package/lib/esm/ui/widget/MapLayerSettingsMenu.d.ts +12 -12
- package/lib/esm/ui/widget/MapLayerSettingsMenu.js +78 -78
- package/lib/esm/ui/widget/MapLayerSettingsMenu.js.map +1 -1
- package/lib/esm/ui/widget/MapLayerSettingsPopupButton.d.ts +7 -7
- package/lib/esm/ui/widget/MapLayerSettingsPopupButton.js +61 -61
- package/lib/esm/ui/widget/MapLayerSettingsPopupButton.js.map +1 -1
- package/lib/esm/ui/widget/MapLayerSettingsPopupButton.scss +20 -20
- package/lib/esm/ui/widget/MapLayersWidget.d.ts +11 -11
- package/lib/esm/ui/widget/MapLayersWidget.js +27 -27
- package/lib/esm/ui/widget/MapLayersWidget.js.map +1 -1
- package/lib/esm/ui/widget/MapManagerSettings.d.ts +3 -3
- package/lib/esm/ui/widget/MapManagerSettings.js +196 -196
- package/lib/esm/ui/widget/MapManagerSettings.js.map +1 -1
- package/lib/esm/ui/widget/MapManagerSettings.scss +29 -29
- package/lib/esm/ui/widget/MapUrlDialog.d.ts +23 -23
- package/lib/esm/ui/widget/MapUrlDialog.d.ts.map +1 -1
- package/lib/esm/ui/widget/MapUrlDialog.js +526 -520
- package/lib/esm/ui/widget/MapUrlDialog.js.map +1 -1
- package/lib/esm/ui/widget/MapUrlDialog.scss +100 -100
- package/lib/esm/ui/widget/SelectMapFormat.d.ts +18 -18
- package/lib/esm/ui/widget/SelectMapFormat.js +50 -50
- package/lib/esm/ui/widget/SelectMapFormat.js.map +1 -1
- package/lib/esm/ui/widget/SubLayersDataProvider.d.ts +20 -20
- package/lib/esm/ui/widget/SubLayersDataProvider.js +71 -71
- package/lib/esm/ui/widget/SubLayersDataProvider.js.map +1 -1
- package/lib/esm/ui/widget/SubLayersPopupButton.d.ts +10 -10
- package/lib/esm/ui/widget/SubLayersPopupButton.js +36 -36
- package/lib/esm/ui/widget/SubLayersPopupButton.js.map +1 -1
- package/lib/esm/ui/widget/SubLayersTree.d.ts +15 -15
- package/lib/esm/ui/widget/SubLayersTree.js +408 -408
- package/lib/esm/ui/widget/SubLayersTree.js.map +1 -1
- package/lib/esm/ui/widget/SubLayersTree.scss +69 -69
- package/lib/esm/ui/widget/TransparencyPopupButton.d.ts +14 -14
- package/lib/esm/ui/widget/TransparencyPopupButton.js +43 -43
- package/lib/esm/ui/widget/TransparencyPopupButton.js.map +1 -1
- package/lib/esm/ui/widget/TransparencyPopupButton.scss +36 -36
- package/package.json +16 -16
|
@@ -1,332 +1,332 @@
|
|
|
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
|
-
import * as React from "react";
|
|
6
|
-
import { IModelApp, MapLayerSourceStatus, NotifyMessageDetails, OutputMessagePriority } from "@itwin/core-frontend";
|
|
7
|
-
import { RelativePosition } from "@itwin/appui-abstract";
|
|
8
|
-
import * as UiCore from "@itwin/core-react";
|
|
9
|
-
import { UiFramework } from "@itwin/appui-react";
|
|
10
|
-
import { useSourceMapContext } from "./MapLayerManager";
|
|
11
|
-
import { MapUrlDialog } from "./MapUrlDialog";
|
|
12
|
-
import { ConfirmMessageDialog } from "./ConfirmMessageDialog";
|
|
13
|
-
import { Button, Input } from "@itwin/itwinui-react";
|
|
14
|
-
import { MapLayerPreferences } from "../../MapLayerPreferences";
|
|
15
|
-
import { MapLayersUI } from "../../mapLayers";
|
|
16
|
-
// cSpell:ignore droppable Sublayer
|
|
17
|
-
var LayerAction;
|
|
18
|
-
(function (LayerAction) {
|
|
19
|
-
LayerAction[LayerAction["Attached"] = 0] = "Attached";
|
|
20
|
-
LayerAction[LayerAction["Edited"] = 1] = "Edited";
|
|
21
|
-
})(LayerAction || (LayerAction = {}));
|
|
22
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
23
|
-
function AttachLayerPanel({ isOverlay, onLayerAttached, onHandleOutsideClick }) {
|
|
24
|
-
const [layerNameToAdd, setLayerNameToAdd] = React.useState();
|
|
25
|
-
const [sourceFilterString, setSourceFilterString] = React.useState();
|
|
26
|
-
const { placeholderLabel, addCustomLayerLabel, addCustomLayerToolTip, loadingMapSources, removeLayerDefButtonTitle, editLayerDefButtonTitle, removeLayerDefDialogTitle } = React.useMemo(() => {
|
|
27
|
-
return {
|
|
28
|
-
placeholderLabel: MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.SearchPlaceholder"),
|
|
29
|
-
addCustomLayerLabel: MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.Custom"),
|
|
30
|
-
addCustomLayerToolTip: MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.AttachCustomLayer"),
|
|
31
|
-
loadingMapSources: MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.LoadingMapSources"),
|
|
32
|
-
removeLayerDefButtonTitle: MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.RemoveLayerDefButtonTitle"),
|
|
33
|
-
editLayerDefButtonTitle: MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.EditLayerDefButtonTitle"),
|
|
34
|
-
removeLayerDefDialogTitle: MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.RemoveLayerDefDialogTitle"),
|
|
35
|
-
};
|
|
36
|
-
}, []);
|
|
37
|
-
const [loading, setLoading] = React.useState(false);
|
|
38
|
-
const [layerNameUnderCursor, setLayerNameUnderCursor] = React.useState();
|
|
39
|
-
const resumeOutsideClick = React.useCallback(() => {
|
|
40
|
-
if (onHandleOutsideClick) {
|
|
41
|
-
onHandleOutsideClick(true);
|
|
42
|
-
}
|
|
43
|
-
}, [onHandleOutsideClick]);
|
|
44
|
-
// 'isMounted' is used to prevent any async operation once the hook has been
|
|
45
|
-
// unloaded. Otherwise we get a 'Can't perform a React state update on an unmounted component.' warning in the console.
|
|
46
|
-
const isMounted = React.useRef(false);
|
|
47
|
-
React.useEffect(() => {
|
|
48
|
-
isMounted.current = true;
|
|
49
|
-
return () => {
|
|
50
|
-
isMounted.current = false;
|
|
51
|
-
// We close any open dialogs that we might have opened
|
|
52
|
-
// This was added because the modal dialog remained remained displayed after the session expired.
|
|
53
|
-
UiFramework.dialogs.modal.close();
|
|
54
|
-
};
|
|
55
|
-
}, []);
|
|
56
|
-
const handleFilterTextChanged = React.useCallback((event) => {
|
|
57
|
-
setSourceFilterString(event.target.value);
|
|
58
|
-
}, []);
|
|
59
|
-
const { loadingSources, sources, activeViewport, backgroundLayers, overlayLayers, mapLayerOptions } = useSourceMapContext();
|
|
60
|
-
const mapTypesOptions = mapLayerOptions?.mapTypeOptions;
|
|
61
|
-
const iTwinId = activeViewport?.iModel?.iTwinId;
|
|
62
|
-
const iModelId = activeViewport?.iModel?.iModelId;
|
|
63
|
-
const styleContainsLayer = React.useCallback((name) => {
|
|
64
|
-
if (backgroundLayers) {
|
|
65
|
-
if (-1 !== backgroundLayers.findIndex((layer) => layer.name === name))
|
|
66
|
-
return true;
|
|
67
|
-
}
|
|
68
|
-
if (overlayLayers) {
|
|
69
|
-
if (-1 !== overlayLayers.findIndex((layer) => layer.name === name))
|
|
70
|
-
return true;
|
|
71
|
-
}
|
|
72
|
-
return false;
|
|
73
|
-
}, [backgroundLayers, overlayLayers]);
|
|
74
|
-
const handleModalUrlDialogOk = React.useCallback((action) => {
|
|
75
|
-
if (LayerAction.Attached === action) {
|
|
76
|
-
// close popup and refresh UI
|
|
77
|
-
onLayerAttached();
|
|
78
|
-
}
|
|
79
|
-
resumeOutsideClick();
|
|
80
|
-
}, [onLayerAttached, resumeOutsideClick]);
|
|
81
|
-
const handleModalUrlDialogCancel = React.useCallback(() => {
|
|
82
|
-
// close popup and refresh UI
|
|
83
|
-
setLoading(false);
|
|
84
|
-
UiFramework.dialogs.modal.close();
|
|
85
|
-
resumeOutsideClick();
|
|
86
|
-
}, [resumeOutsideClick]);
|
|
87
|
-
React.useEffect(() => {
|
|
88
|
-
async function attemptToAddLayer(layerName) {
|
|
89
|
-
if (layerName && activeViewport) {
|
|
90
|
-
// if the layer is not in the style add it now.
|
|
91
|
-
if (undefined === backgroundLayers?.find((layer) => layerName === layer.name) && undefined === overlayLayers?.find((layer) => layerName === layer.name)) {
|
|
92
|
-
const mapLayerSettings = sources?.find((source) => source.name === layerName);
|
|
93
|
-
if (mapLayerSettings === undefined) {
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
try {
|
|
97
|
-
if (isMounted.current) {
|
|
98
|
-
setLoading(true);
|
|
99
|
-
}
|
|
100
|
-
const { status, subLayers } = await mapLayerSettings.validateSource();
|
|
101
|
-
if (status === MapLayerSourceStatus.Valid || status === MapLayerSourceStatus.RequireAuth) {
|
|
102
|
-
if (status === MapLayerSourceStatus.Valid) {
|
|
103
|
-
const settings = mapLayerSettings.toLayerSettings(subLayers);
|
|
104
|
-
if (settings) {
|
|
105
|
-
activeViewport.displayStyle.attachMapLayer({ settings, isOverlay });
|
|
106
|
-
activeViewport.invalidateRenderPlan();
|
|
107
|
-
const msg = IModelApp.localization.getLocalizedString("mapLayers:Messages.MapLayerAttached", { sourceName: settings.name, sourceUrl: settings.url });
|
|
108
|
-
IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Info, msg));
|
|
109
|
-
}
|
|
110
|
-
if (isMounted.current) {
|
|
111
|
-
setLoading(false);
|
|
112
|
-
}
|
|
113
|
-
if (onLayerAttached) {
|
|
114
|
-
onLayerAttached();
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
else if (status === MapLayerSourceStatus.RequireAuth && isMounted.current) {
|
|
118
|
-
UiFramework.dialogs.modal.open(React.createElement(MapUrlDialog, { activeViewport: activeViewport, isOverlay: isOverlay, layerRequiringCredentials: mapLayerSettings.toJSON(), onOkResult: () => handleModalUrlDialogOk(LayerAction.Attached), onCancelResult: handleModalUrlDialogCancel, mapTypesOptions: mapTypesOptions }));
|
|
119
|
-
if (onHandleOutsideClick) {
|
|
120
|
-
onHandleOutsideClick(false);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
else {
|
|
125
|
-
const msg = IModelApp.localization.getLocalizedString("mapLayers:Messages.MapLayerValidationFailed", { sourceUrl: mapLayerSettings.url });
|
|
126
|
-
IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Error, msg));
|
|
127
|
-
if (isMounted.current) {
|
|
128
|
-
setLoading(false);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
catch (err) {
|
|
133
|
-
if (isMounted.current) {
|
|
134
|
-
setLoading(false);
|
|
135
|
-
}
|
|
136
|
-
const msg = IModelApp.localization.getLocalizedString("mapLayers:Messages.MapLayerAttachError", { error: err, sourceUrl: mapLayerSettings.url });
|
|
137
|
-
IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Error, msg));
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
if (layerNameToAdd) {
|
|
144
|
-
attemptToAddLayer(layerNameToAdd); // eslint-disable-line @typescript-eslint/no-floating-promises
|
|
145
|
-
if (isMounted.current) {
|
|
146
|
-
setLayerNameToAdd(undefined);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
}, [setLayerNameToAdd, layerNameToAdd, activeViewport, sources, backgroundLayers, isOverlay, overlayLayers, onLayerAttached, handleModalUrlDialogOk, mapTypesOptions, handleModalUrlDialogCancel, onHandleOutsideClick]);
|
|
150
|
-
const options = React.useMemo(() => sources?.filter((source) => !styleContainsLayer(source.name)), [sources, styleContainsLayer]);
|
|
151
|
-
const filteredOptions = React.useMemo(() => {
|
|
152
|
-
if (undefined === sourceFilterString || 0 === sourceFilterString.length) {
|
|
153
|
-
return options;
|
|
154
|
-
}
|
|
155
|
-
else {
|
|
156
|
-
return options?.filter((option) => option.name.toLowerCase().includes(sourceFilterString?.toLowerCase()));
|
|
157
|
-
}
|
|
158
|
-
}, [options, sourceFilterString]);
|
|
159
|
-
const handleAddNewMapSource = React.useCallback(() => {
|
|
160
|
-
UiFramework.dialogs.modal.open(React.createElement(MapUrlDialog, { activeViewport: activeViewport, isOverlay: isOverlay, onOkResult: () => handleModalUrlDialogOk(LayerAction.Attached), onCancelResult: handleModalUrlDialogCancel, mapTypesOptions: mapTypesOptions }));
|
|
161
|
-
if (onHandleOutsideClick) {
|
|
162
|
-
onHandleOutsideClick(false);
|
|
163
|
-
}
|
|
164
|
-
return;
|
|
165
|
-
}, [activeViewport, handleModalUrlDialogCancel, handleModalUrlDialogOk, isOverlay, mapTypesOptions, onHandleOutsideClick]);
|
|
166
|
-
const handleAttach = React.useCallback((mapName) => {
|
|
167
|
-
setLayerNameToAdd(mapName);
|
|
168
|
-
}, []);
|
|
169
|
-
const handleKeypressOnSourceList = React.useCallback((event) => {
|
|
170
|
-
const key = event.key;
|
|
171
|
-
if (key === "Enter") {
|
|
172
|
-
event.preventDefault();
|
|
173
|
-
const mapName = event.currentTarget?.dataset?.value;
|
|
174
|
-
if (mapName && mapName.length) {
|
|
175
|
-
handleAttach(mapName);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
}, [handleAttach]);
|
|
179
|
-
const onListboxValueChange = React.useCallback((mapName) => {
|
|
180
|
-
setLayerNameToAdd(mapName);
|
|
181
|
-
}, []);
|
|
182
|
-
const handleNoConfirmation = React.useCallback((_layerName) => {
|
|
183
|
-
UiFramework.dialogs.modal.close();
|
|
184
|
-
resumeOutsideClick();
|
|
185
|
-
}, [resumeOutsideClick]);
|
|
186
|
-
const handleYesConfirmation = React.useCallback(async (source) => {
|
|
187
|
-
const layerName = source.name;
|
|
188
|
-
if (!!iTwinId) {
|
|
189
|
-
try {
|
|
190
|
-
await MapLayerPreferences.deleteByName(source, iTwinId, iModelId);
|
|
191
|
-
const msg = MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.RemoveLayerDefSuccess", { layerName });
|
|
192
|
-
IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Info, msg));
|
|
193
|
-
}
|
|
194
|
-
catch (err) {
|
|
195
|
-
const msg = MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.RemoveLayerDefError", { layerName });
|
|
196
|
-
IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Error, msg));
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
UiFramework.dialogs.modal.close();
|
|
200
|
-
resumeOutsideClick();
|
|
201
|
-
}, [iTwinId, iModelId, resumeOutsideClick]);
|
|
202
|
-
/*
|
|
203
|
-
Handle Remove layer button clicked
|
|
204
|
-
*/
|
|
205
|
-
const onItemRemoveButtonClicked = React.useCallback((source, event) => {
|
|
206
|
-
event.stopPropagation(); // We don't want the owning ListBox to react on mouse click.
|
|
207
|
-
const layerName = source.name;
|
|
208
|
-
const msg = MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.RemoveLayerDefDialogMessage", { layerName });
|
|
209
|
-
UiFramework.dialogs.modal.open(React.createElement(ConfirmMessageDialog, { className: "map-sources-delete-confirmation", title: removeLayerDefDialogTitle, message: msg, maxWidth: 400, onClose: () => handleNoConfirmation(layerName), onEscape: () => handleNoConfirmation(layerName), onYesResult: async () => handleYesConfirmation(source), onNoResult: () => handleNoConfirmation(layerName) }));
|
|
210
|
-
if (onHandleOutsideClick) {
|
|
211
|
-
onHandleOutsideClick(false);
|
|
212
|
-
}
|
|
213
|
-
}, [handleNoConfirmation, handleYesConfirmation, onHandleOutsideClick, removeLayerDefDialogTitle]);
|
|
214
|
-
/*
|
|
215
|
-
Handle Edit layer button clicked
|
|
216
|
-
*/
|
|
217
|
-
const onItemEditButtonClicked = React.useCallback((event) => {
|
|
218
|
-
event.stopPropagation(); // We don't want the owning ListBox to react on mouse click.
|
|
219
|
-
const targetLayerName = event?.currentTarget?.parentNode?.dataset?.value;
|
|
220
|
-
const matchingSource = sources.find((layerSource) => layerSource.name === targetLayerName);
|
|
221
|
-
// we expect a single layer source matching this name
|
|
222
|
-
if (matchingSource === undefined) {
|
|
223
|
-
return;
|
|
224
|
-
}
|
|
225
|
-
UiFramework.dialogs.modal.open(React.createElement(MapUrlDialog, { activeViewport: activeViewport, isOverlay: isOverlay, mapLayerSourceToEdit: matchingSource, onOkResult: () => handleModalUrlDialogOk(LayerAction.Edited), onCancelResult: handleModalUrlDialogCancel, mapTypesOptions: mapTypesOptions }));
|
|
226
|
-
if (onHandleOutsideClick) {
|
|
227
|
-
onHandleOutsideClick(false);
|
|
228
|
-
}
|
|
229
|
-
}, [activeViewport, handleModalUrlDialogCancel, handleModalUrlDialogOk, isOverlay, mapTypesOptions, onHandleOutsideClick, sources]);
|
|
230
|
-
return (React.createElement("div", { className: "map-manager-header" },
|
|
231
|
-
(loading || loadingSources) && React.createElement(UiCore.LoadingSpinner, { message: loadingMapSources }),
|
|
232
|
-
React.createElement("div", { className: "map-manager-source-listbox-header" },
|
|
233
|
-
React.createElement(Input, { type: "text", className: "map-manager-source-list-filter", placeholder: placeholderLabel, value: sourceFilterString, onChange: handleFilterTextChanged, size: "small" }),
|
|
234
|
-
React.createElement(Button, { className: "map-manager-add-source-button", title: addCustomLayerToolTip, onClick: handleAddNewMapSource }, addCustomLayerLabel)),
|
|
235
|
-
React.createElement("div", { className: "map-manager-sources" },
|
|
236
|
-
React.createElement(UiCore.Listbox, { id: "map-sources", selectedValue: layerNameToAdd, className: "map-manager-source-list", onKeyPress: handleKeypressOnSourceList, onListboxValueChange: onListboxValueChange }, filteredOptions?.map((source) => React.createElement(UiCore.ListboxItem, { key: source.name, className: "map-source-list-entry", value: source.name, onMouseEnter: () => setLayerNameUnderCursor(source.name), onMouseLeave: () => setLayerNameUnderCursor(undefined) },
|
|
237
|
-
React.createElement("span", { className: "map-source-list-entry-name", title: source.name }, source.name),
|
|
238
|
-
// otherwise list feels cluttered.
|
|
239
|
-
(!!iTwinId && layerNameUnderCursor && layerNameUnderCursor === source.name) &&
|
|
240
|
-
React.createElement(React.Fragment, null,
|
|
241
|
-
React.createElement(Button, { size: "small", styleType: "borderless", className: "map-source-list-entry-button", title: editLayerDefButtonTitle, onClick: onItemEditButtonClicked },
|
|
242
|
-
React.createElement(UiCore.Icon, { iconSpec: "icon-edit" })),
|
|
243
|
-
React.createElement(Button, { size: "small", styleType: "borderless", className: "map-source-list-entry-button", title: removeLayerDefButtonTitle, onClick: (event) => { onItemRemoveButtonClicked(source, event); } },
|
|
244
|
-
React.createElement(UiCore.Icon, { iconSpec: "icon-delete" })))))))));
|
|
245
|
-
}
|
|
246
|
-
/** @internal */
|
|
247
|
-
export var AttachLayerButtonType;
|
|
248
|
-
(function (AttachLayerButtonType) {
|
|
249
|
-
AttachLayerButtonType[AttachLayerButtonType["Primary"] = 0] = "Primary";
|
|
250
|
-
AttachLayerButtonType[AttachLayerButtonType["Blue"] = 1] = "Blue";
|
|
251
|
-
AttachLayerButtonType[AttachLayerButtonType["Icon"] = 2] = "Icon";
|
|
252
|
-
})(AttachLayerButtonType || (AttachLayerButtonType = {}));
|
|
253
|
-
/** @internal */
|
|
254
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
255
|
-
export function AttachLayerPopupButton(props) {
|
|
256
|
-
const { showAttachLayerLabel, hideAttachLayerLabel, addCustomLayerButtonLabel } = React.useMemo(() => {
|
|
257
|
-
return {
|
|
258
|
-
showAttachLayerLabel: MapLayersUI.localization.getLocalizedString("mapLayers:AttachLayerPopup.Attach"),
|
|
259
|
-
hideAttachLayerLabel: MapLayersUI.localization.getLocalizedString("mapLayers:AttachLayerPopup.Close"),
|
|
260
|
-
addCustomLayerButtonLabel: MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.AddCustomLayerButtonLabel"),
|
|
261
|
-
};
|
|
262
|
-
}, []);
|
|
263
|
-
const [handleOutsideClick, setHandleOutsideClick] = React.useState(true);
|
|
264
|
-
const [popupOpen, setPopupOpen] = React.useState(false);
|
|
265
|
-
const buttonRef = React.useRef(null);
|
|
266
|
-
const panelRef = React.useRef(null);
|
|
267
|
-
// 'isMounted' is used to prevent any async operation once the hook has been
|
|
268
|
-
// unloaded. Otherwise we get a 'Can't perform a React state update on an unmounted component.' warning in the console.
|
|
269
|
-
const isMounted = React.useRef(false);
|
|
270
|
-
React.useEffect(() => {
|
|
271
|
-
isMounted.current = true;
|
|
272
|
-
return () => {
|
|
273
|
-
isMounted.current = false;
|
|
274
|
-
};
|
|
275
|
-
}, []);
|
|
276
|
-
const togglePopup = React.useCallback(() => {
|
|
277
|
-
setPopupOpen(!popupOpen);
|
|
278
|
-
}, [popupOpen]);
|
|
279
|
-
const handleClosePopup = React.useCallback(() => {
|
|
280
|
-
setPopupOpen(false);
|
|
281
|
-
}, []);
|
|
282
|
-
const onHandleOutsideClick = React.useCallback((event) => {
|
|
283
|
-
if (!handleOutsideClick) {
|
|
284
|
-
return;
|
|
285
|
-
}
|
|
286
|
-
// If clicking on button that open panel - don't trigger outside click processing
|
|
287
|
-
if (buttonRef?.current && buttonRef?.current.contains(event.target)) {
|
|
288
|
-
return;
|
|
289
|
-
}
|
|
290
|
-
// If clicking the panel, this is not an outside clicked
|
|
291
|
-
if (panelRef.current && panelRef?.current.contains(event.target)) {
|
|
292
|
-
return;
|
|
293
|
-
}
|
|
294
|
-
// If we reach this point, we got an outside clicked, no close the popup
|
|
295
|
-
setPopupOpen(false);
|
|
296
|
-
}, [handleOutsideClick]);
|
|
297
|
-
const { refreshFromStyle } = useSourceMapContext();
|
|
298
|
-
const handleLayerAttached = React.useCallback(() => {
|
|
299
|
-
if (!isMounted.current) {
|
|
300
|
-
return;
|
|
301
|
-
}
|
|
302
|
-
setPopupOpen(false);
|
|
303
|
-
refreshFromStyle();
|
|
304
|
-
}, [refreshFromStyle]);
|
|
305
|
-
function renderButton() {
|
|
306
|
-
let button;
|
|
307
|
-
if (props.buttonType === undefined || props.buttonType === AttachLayerButtonType.Icon) {
|
|
308
|
-
button = (React.createElement(Button, { disabled: props.disabled, size: "small", styleType: "borderless", ref: buttonRef, className: "map-manager-attach-layer-button", title: popupOpen ? hideAttachLayerLabel : showAttachLayerLabel, onClick: togglePopup },
|
|
309
|
-
React.createElement(UiCore.WebFontIcon, { iconName: "icon-add" })));
|
|
310
|
-
}
|
|
311
|
-
else {
|
|
312
|
-
const determineStyleType = () => {
|
|
313
|
-
switch (props.buttonType) {
|
|
314
|
-
case AttachLayerButtonType.Blue:
|
|
315
|
-
return "high-visibility";
|
|
316
|
-
case AttachLayerButtonType.Primary:
|
|
317
|
-
default:
|
|
318
|
-
return "cta";
|
|
319
|
-
}
|
|
320
|
-
};
|
|
321
|
-
const styleType = determineStyleType();
|
|
322
|
-
button = (React.createElement(Button, { disabled: props.disabled, ref: buttonRef, styleType: styleType, title: popupOpen ? hideAttachLayerLabel : showAttachLayerLabel, onClick: togglePopup }, addCustomLayerButtonLabel));
|
|
323
|
-
}
|
|
324
|
-
return button;
|
|
325
|
-
}
|
|
326
|
-
return (React.createElement(React.Fragment, null,
|
|
327
|
-
renderButton(),
|
|
328
|
-
React.createElement(UiCore.Popup, { isOpen: popupOpen, position: RelativePosition.BottomRight, onClose: handleClosePopup, onOutsideClick: onHandleOutsideClick, target: buttonRef.current, closeOnEnter: false, closeOnContextMenu: false },
|
|
329
|
-
React.createElement("div", { ref: panelRef, className: "map-sources-popup-panel" },
|
|
330
|
-
React.createElement(AttachLayerPanel, { isOverlay: props.isOverlay, onLayerAttached: handleLayerAttached, onHandleOutsideClick: setHandleOutsideClick })))));
|
|
331
|
-
}
|
|
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
|
+
import * as React from "react";
|
|
6
|
+
import { IModelApp, MapLayerSourceStatus, NotifyMessageDetails, OutputMessagePriority } from "@itwin/core-frontend";
|
|
7
|
+
import { RelativePosition } from "@itwin/appui-abstract";
|
|
8
|
+
import * as UiCore from "@itwin/core-react";
|
|
9
|
+
import { UiFramework } from "@itwin/appui-react";
|
|
10
|
+
import { useSourceMapContext } from "./MapLayerManager";
|
|
11
|
+
import { MapUrlDialog } from "./MapUrlDialog";
|
|
12
|
+
import { ConfirmMessageDialog } from "./ConfirmMessageDialog";
|
|
13
|
+
import { Button, Input } from "@itwin/itwinui-react";
|
|
14
|
+
import { MapLayerPreferences } from "../../MapLayerPreferences";
|
|
15
|
+
import { MapLayersUI } from "../../mapLayers";
|
|
16
|
+
// cSpell:ignore droppable Sublayer
|
|
17
|
+
var LayerAction;
|
|
18
|
+
(function (LayerAction) {
|
|
19
|
+
LayerAction[LayerAction["Attached"] = 0] = "Attached";
|
|
20
|
+
LayerAction[LayerAction["Edited"] = 1] = "Edited";
|
|
21
|
+
})(LayerAction || (LayerAction = {}));
|
|
22
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
23
|
+
function AttachLayerPanel({ isOverlay, onLayerAttached, onHandleOutsideClick }) {
|
|
24
|
+
const [layerNameToAdd, setLayerNameToAdd] = React.useState();
|
|
25
|
+
const [sourceFilterString, setSourceFilterString] = React.useState();
|
|
26
|
+
const { placeholderLabel, addCustomLayerLabel, addCustomLayerToolTip, loadingMapSources, removeLayerDefButtonTitle, editLayerDefButtonTitle, removeLayerDefDialogTitle } = React.useMemo(() => {
|
|
27
|
+
return {
|
|
28
|
+
placeholderLabel: MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.SearchPlaceholder"),
|
|
29
|
+
addCustomLayerLabel: MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.Custom"),
|
|
30
|
+
addCustomLayerToolTip: MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.AttachCustomLayer"),
|
|
31
|
+
loadingMapSources: MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.LoadingMapSources"),
|
|
32
|
+
removeLayerDefButtonTitle: MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.RemoveLayerDefButtonTitle"),
|
|
33
|
+
editLayerDefButtonTitle: MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.EditLayerDefButtonTitle"),
|
|
34
|
+
removeLayerDefDialogTitle: MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.RemoveLayerDefDialogTitle"),
|
|
35
|
+
};
|
|
36
|
+
}, []);
|
|
37
|
+
const [loading, setLoading] = React.useState(false);
|
|
38
|
+
const [layerNameUnderCursor, setLayerNameUnderCursor] = React.useState();
|
|
39
|
+
const resumeOutsideClick = React.useCallback(() => {
|
|
40
|
+
if (onHandleOutsideClick) {
|
|
41
|
+
onHandleOutsideClick(true);
|
|
42
|
+
}
|
|
43
|
+
}, [onHandleOutsideClick]);
|
|
44
|
+
// 'isMounted' is used to prevent any async operation once the hook has been
|
|
45
|
+
// unloaded. Otherwise we get a 'Can't perform a React state update on an unmounted component.' warning in the console.
|
|
46
|
+
const isMounted = React.useRef(false);
|
|
47
|
+
React.useEffect(() => {
|
|
48
|
+
isMounted.current = true;
|
|
49
|
+
return () => {
|
|
50
|
+
isMounted.current = false;
|
|
51
|
+
// We close any open dialogs that we might have opened
|
|
52
|
+
// This was added because the modal dialog remained remained displayed after the session expired.
|
|
53
|
+
UiFramework.dialogs.modal.close();
|
|
54
|
+
};
|
|
55
|
+
}, []);
|
|
56
|
+
const handleFilterTextChanged = React.useCallback((event) => {
|
|
57
|
+
setSourceFilterString(event.target.value);
|
|
58
|
+
}, []);
|
|
59
|
+
const { loadingSources, sources, activeViewport, backgroundLayers, overlayLayers, mapLayerOptions } = useSourceMapContext();
|
|
60
|
+
const mapTypesOptions = mapLayerOptions?.mapTypeOptions;
|
|
61
|
+
const iTwinId = activeViewport?.iModel?.iTwinId;
|
|
62
|
+
const iModelId = activeViewport?.iModel?.iModelId;
|
|
63
|
+
const styleContainsLayer = React.useCallback((name) => {
|
|
64
|
+
if (backgroundLayers) {
|
|
65
|
+
if (-1 !== backgroundLayers.findIndex((layer) => layer.name === name))
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
if (overlayLayers) {
|
|
69
|
+
if (-1 !== overlayLayers.findIndex((layer) => layer.name === name))
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
return false;
|
|
73
|
+
}, [backgroundLayers, overlayLayers]);
|
|
74
|
+
const handleModalUrlDialogOk = React.useCallback((action) => {
|
|
75
|
+
if (LayerAction.Attached === action) {
|
|
76
|
+
// close popup and refresh UI
|
|
77
|
+
onLayerAttached();
|
|
78
|
+
}
|
|
79
|
+
resumeOutsideClick();
|
|
80
|
+
}, [onLayerAttached, resumeOutsideClick]);
|
|
81
|
+
const handleModalUrlDialogCancel = React.useCallback(() => {
|
|
82
|
+
// close popup and refresh UI
|
|
83
|
+
setLoading(false);
|
|
84
|
+
UiFramework.dialogs.modal.close();
|
|
85
|
+
resumeOutsideClick();
|
|
86
|
+
}, [resumeOutsideClick]);
|
|
87
|
+
React.useEffect(() => {
|
|
88
|
+
async function attemptToAddLayer(layerName) {
|
|
89
|
+
if (layerName && activeViewport) {
|
|
90
|
+
// if the layer is not in the style add it now.
|
|
91
|
+
if (undefined === backgroundLayers?.find((layer) => layerName === layer.name) && undefined === overlayLayers?.find((layer) => layerName === layer.name)) {
|
|
92
|
+
const mapLayerSettings = sources?.find((source) => source.name === layerName);
|
|
93
|
+
if (mapLayerSettings === undefined) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
if (isMounted.current) {
|
|
98
|
+
setLoading(true);
|
|
99
|
+
}
|
|
100
|
+
const { status, subLayers } = await mapLayerSettings.validateSource();
|
|
101
|
+
if (status === MapLayerSourceStatus.Valid || status === MapLayerSourceStatus.RequireAuth) {
|
|
102
|
+
if (status === MapLayerSourceStatus.Valid) {
|
|
103
|
+
const settings = mapLayerSettings.toLayerSettings(subLayers);
|
|
104
|
+
if (settings) {
|
|
105
|
+
activeViewport.displayStyle.attachMapLayer({ settings, isOverlay });
|
|
106
|
+
activeViewport.invalidateRenderPlan();
|
|
107
|
+
const msg = IModelApp.localization.getLocalizedString("mapLayers:Messages.MapLayerAttached", { sourceName: settings.name, sourceUrl: settings.url });
|
|
108
|
+
IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Info, msg));
|
|
109
|
+
}
|
|
110
|
+
if (isMounted.current) {
|
|
111
|
+
setLoading(false);
|
|
112
|
+
}
|
|
113
|
+
if (onLayerAttached) {
|
|
114
|
+
onLayerAttached();
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
else if (status === MapLayerSourceStatus.RequireAuth && isMounted.current) {
|
|
118
|
+
UiFramework.dialogs.modal.open(React.createElement(MapUrlDialog, { activeViewport: activeViewport, isOverlay: isOverlay, layerRequiringCredentials: mapLayerSettings.toJSON(), onOkResult: () => handleModalUrlDialogOk(LayerAction.Attached), onCancelResult: handleModalUrlDialogCancel, mapTypesOptions: mapTypesOptions }));
|
|
119
|
+
if (onHandleOutsideClick) {
|
|
120
|
+
onHandleOutsideClick(false);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
const msg = IModelApp.localization.getLocalizedString("mapLayers:Messages.MapLayerValidationFailed", { sourceUrl: mapLayerSettings.url });
|
|
126
|
+
IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Error, msg));
|
|
127
|
+
if (isMounted.current) {
|
|
128
|
+
setLoading(false);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
catch (err) {
|
|
133
|
+
if (isMounted.current) {
|
|
134
|
+
setLoading(false);
|
|
135
|
+
}
|
|
136
|
+
const msg = IModelApp.localization.getLocalizedString("mapLayers:Messages.MapLayerAttachError", { error: err, sourceUrl: mapLayerSettings.url });
|
|
137
|
+
IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Error, msg));
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
if (layerNameToAdd) {
|
|
144
|
+
attemptToAddLayer(layerNameToAdd); // eslint-disable-line @typescript-eslint/no-floating-promises
|
|
145
|
+
if (isMounted.current) {
|
|
146
|
+
setLayerNameToAdd(undefined);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}, [setLayerNameToAdd, layerNameToAdd, activeViewport, sources, backgroundLayers, isOverlay, overlayLayers, onLayerAttached, handleModalUrlDialogOk, mapTypesOptions, handleModalUrlDialogCancel, onHandleOutsideClick]);
|
|
150
|
+
const options = React.useMemo(() => sources?.filter((source) => !styleContainsLayer(source.name)), [sources, styleContainsLayer]);
|
|
151
|
+
const filteredOptions = React.useMemo(() => {
|
|
152
|
+
if (undefined === sourceFilterString || 0 === sourceFilterString.length) {
|
|
153
|
+
return options;
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
return options?.filter((option) => option.name.toLowerCase().includes(sourceFilterString?.toLowerCase()));
|
|
157
|
+
}
|
|
158
|
+
}, [options, sourceFilterString]);
|
|
159
|
+
const handleAddNewMapSource = React.useCallback(() => {
|
|
160
|
+
UiFramework.dialogs.modal.open(React.createElement(MapUrlDialog, { activeViewport: activeViewport, isOverlay: isOverlay, onOkResult: () => handleModalUrlDialogOk(LayerAction.Attached), onCancelResult: handleModalUrlDialogCancel, mapTypesOptions: mapTypesOptions }));
|
|
161
|
+
if (onHandleOutsideClick) {
|
|
162
|
+
onHandleOutsideClick(false);
|
|
163
|
+
}
|
|
164
|
+
return;
|
|
165
|
+
}, [activeViewport, handleModalUrlDialogCancel, handleModalUrlDialogOk, isOverlay, mapTypesOptions, onHandleOutsideClick]);
|
|
166
|
+
const handleAttach = React.useCallback((mapName) => {
|
|
167
|
+
setLayerNameToAdd(mapName);
|
|
168
|
+
}, []);
|
|
169
|
+
const handleKeypressOnSourceList = React.useCallback((event) => {
|
|
170
|
+
const key = event.key;
|
|
171
|
+
if (key === "Enter") {
|
|
172
|
+
event.preventDefault();
|
|
173
|
+
const mapName = event.currentTarget?.dataset?.value;
|
|
174
|
+
if (mapName && mapName.length) {
|
|
175
|
+
handleAttach(mapName);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}, [handleAttach]);
|
|
179
|
+
const onListboxValueChange = React.useCallback((mapName) => {
|
|
180
|
+
setLayerNameToAdd(mapName);
|
|
181
|
+
}, []);
|
|
182
|
+
const handleNoConfirmation = React.useCallback((_layerName) => {
|
|
183
|
+
UiFramework.dialogs.modal.close();
|
|
184
|
+
resumeOutsideClick();
|
|
185
|
+
}, [resumeOutsideClick]);
|
|
186
|
+
const handleYesConfirmation = React.useCallback(async (source) => {
|
|
187
|
+
const layerName = source.name;
|
|
188
|
+
if (!!iTwinId) {
|
|
189
|
+
try {
|
|
190
|
+
await MapLayerPreferences.deleteByName(source, iTwinId, iModelId);
|
|
191
|
+
const msg = MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.RemoveLayerDefSuccess", { layerName });
|
|
192
|
+
IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Info, msg));
|
|
193
|
+
}
|
|
194
|
+
catch (err) {
|
|
195
|
+
const msg = MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.RemoveLayerDefError", { layerName });
|
|
196
|
+
IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Error, msg));
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
UiFramework.dialogs.modal.close();
|
|
200
|
+
resumeOutsideClick();
|
|
201
|
+
}, [iTwinId, iModelId, resumeOutsideClick]);
|
|
202
|
+
/*
|
|
203
|
+
Handle Remove layer button clicked
|
|
204
|
+
*/
|
|
205
|
+
const onItemRemoveButtonClicked = React.useCallback((source, event) => {
|
|
206
|
+
event.stopPropagation(); // We don't want the owning ListBox to react on mouse click.
|
|
207
|
+
const layerName = source.name;
|
|
208
|
+
const msg = MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.RemoveLayerDefDialogMessage", { layerName });
|
|
209
|
+
UiFramework.dialogs.modal.open(React.createElement(ConfirmMessageDialog, { className: "map-sources-delete-confirmation", title: removeLayerDefDialogTitle, message: msg, maxWidth: 400, onClose: () => handleNoConfirmation(layerName), onEscape: () => handleNoConfirmation(layerName), onYesResult: async () => handleYesConfirmation(source), onNoResult: () => handleNoConfirmation(layerName) }));
|
|
210
|
+
if (onHandleOutsideClick) {
|
|
211
|
+
onHandleOutsideClick(false);
|
|
212
|
+
}
|
|
213
|
+
}, [handleNoConfirmation, handleYesConfirmation, onHandleOutsideClick, removeLayerDefDialogTitle]);
|
|
214
|
+
/*
|
|
215
|
+
Handle Edit layer button clicked
|
|
216
|
+
*/
|
|
217
|
+
const onItemEditButtonClicked = React.useCallback((event) => {
|
|
218
|
+
event.stopPropagation(); // We don't want the owning ListBox to react on mouse click.
|
|
219
|
+
const targetLayerName = event?.currentTarget?.parentNode?.dataset?.value;
|
|
220
|
+
const matchingSource = sources.find((layerSource) => layerSource.name === targetLayerName);
|
|
221
|
+
// we expect a single layer source matching this name
|
|
222
|
+
if (matchingSource === undefined) {
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
UiFramework.dialogs.modal.open(React.createElement(MapUrlDialog, { activeViewport: activeViewport, isOverlay: isOverlay, mapLayerSourceToEdit: matchingSource, onOkResult: () => handleModalUrlDialogOk(LayerAction.Edited), onCancelResult: handleModalUrlDialogCancel, mapTypesOptions: mapTypesOptions }));
|
|
226
|
+
if (onHandleOutsideClick) {
|
|
227
|
+
onHandleOutsideClick(false);
|
|
228
|
+
}
|
|
229
|
+
}, [activeViewport, handleModalUrlDialogCancel, handleModalUrlDialogOk, isOverlay, mapTypesOptions, onHandleOutsideClick, sources]);
|
|
230
|
+
return (React.createElement("div", { className: "map-manager-header" },
|
|
231
|
+
(loading || loadingSources) && React.createElement(UiCore.LoadingSpinner, { message: loadingMapSources }),
|
|
232
|
+
React.createElement("div", { className: "map-manager-source-listbox-header" },
|
|
233
|
+
React.createElement(Input, { type: "text", className: "map-manager-source-list-filter", placeholder: placeholderLabel, value: sourceFilterString, onChange: handleFilterTextChanged, size: "small" }),
|
|
234
|
+
React.createElement(Button, { className: "map-manager-add-source-button", title: addCustomLayerToolTip, onClick: handleAddNewMapSource }, addCustomLayerLabel)),
|
|
235
|
+
React.createElement("div", { className: "map-manager-sources" },
|
|
236
|
+
React.createElement(UiCore.Listbox, { id: "map-sources", selectedValue: layerNameToAdd, className: "map-manager-source-list", onKeyPress: handleKeypressOnSourceList, onListboxValueChange: onListboxValueChange }, filteredOptions?.map((source) => React.createElement(UiCore.ListboxItem, { key: source.name, className: "map-source-list-entry", value: source.name, onMouseEnter: () => setLayerNameUnderCursor(source.name), onMouseLeave: () => setLayerNameUnderCursor(undefined) },
|
|
237
|
+
React.createElement("span", { className: "map-source-list-entry-name", title: source.name }, source.name),
|
|
238
|
+
// otherwise list feels cluttered.
|
|
239
|
+
(!!iTwinId && layerNameUnderCursor && layerNameUnderCursor === source.name) &&
|
|
240
|
+
React.createElement(React.Fragment, null,
|
|
241
|
+
React.createElement(Button, { size: "small", styleType: "borderless", className: "map-source-list-entry-button", title: editLayerDefButtonTitle, onClick: onItemEditButtonClicked },
|
|
242
|
+
React.createElement(UiCore.Icon, { iconSpec: "icon-edit" })),
|
|
243
|
+
React.createElement(Button, { size: "small", styleType: "borderless", className: "map-source-list-entry-button", title: removeLayerDefButtonTitle, onClick: (event) => { onItemRemoveButtonClicked(source, event); } },
|
|
244
|
+
React.createElement(UiCore.Icon, { iconSpec: "icon-delete" })))))))));
|
|
245
|
+
}
|
|
246
|
+
/** @internal */
|
|
247
|
+
export var AttachLayerButtonType;
|
|
248
|
+
(function (AttachLayerButtonType) {
|
|
249
|
+
AttachLayerButtonType[AttachLayerButtonType["Primary"] = 0] = "Primary";
|
|
250
|
+
AttachLayerButtonType[AttachLayerButtonType["Blue"] = 1] = "Blue";
|
|
251
|
+
AttachLayerButtonType[AttachLayerButtonType["Icon"] = 2] = "Icon";
|
|
252
|
+
})(AttachLayerButtonType || (AttachLayerButtonType = {}));
|
|
253
|
+
/** @internal */
|
|
254
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
255
|
+
export function AttachLayerPopupButton(props) {
|
|
256
|
+
const { showAttachLayerLabel, hideAttachLayerLabel, addCustomLayerButtonLabel } = React.useMemo(() => {
|
|
257
|
+
return {
|
|
258
|
+
showAttachLayerLabel: MapLayersUI.localization.getLocalizedString("mapLayers:AttachLayerPopup.Attach"),
|
|
259
|
+
hideAttachLayerLabel: MapLayersUI.localization.getLocalizedString("mapLayers:AttachLayerPopup.Close"),
|
|
260
|
+
addCustomLayerButtonLabel: MapLayersUI.localization.getLocalizedString("mapLayers:CustomAttach.AddCustomLayerButtonLabel"),
|
|
261
|
+
};
|
|
262
|
+
}, []);
|
|
263
|
+
const [handleOutsideClick, setHandleOutsideClick] = React.useState(true);
|
|
264
|
+
const [popupOpen, setPopupOpen] = React.useState(false);
|
|
265
|
+
const buttonRef = React.useRef(null);
|
|
266
|
+
const panelRef = React.useRef(null);
|
|
267
|
+
// 'isMounted' is used to prevent any async operation once the hook has been
|
|
268
|
+
// unloaded. Otherwise we get a 'Can't perform a React state update on an unmounted component.' warning in the console.
|
|
269
|
+
const isMounted = React.useRef(false);
|
|
270
|
+
React.useEffect(() => {
|
|
271
|
+
isMounted.current = true;
|
|
272
|
+
return () => {
|
|
273
|
+
isMounted.current = false;
|
|
274
|
+
};
|
|
275
|
+
}, []);
|
|
276
|
+
const togglePopup = React.useCallback(() => {
|
|
277
|
+
setPopupOpen(!popupOpen);
|
|
278
|
+
}, [popupOpen]);
|
|
279
|
+
const handleClosePopup = React.useCallback(() => {
|
|
280
|
+
setPopupOpen(false);
|
|
281
|
+
}, []);
|
|
282
|
+
const onHandleOutsideClick = React.useCallback((event) => {
|
|
283
|
+
if (!handleOutsideClick) {
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
// If clicking on button that open panel - don't trigger outside click processing
|
|
287
|
+
if (buttonRef?.current && buttonRef?.current.contains(event.target)) {
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
// If clicking the panel, this is not an outside clicked
|
|
291
|
+
if (panelRef.current && panelRef?.current.contains(event.target)) {
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
// If we reach this point, we got an outside clicked, no close the popup
|
|
295
|
+
setPopupOpen(false);
|
|
296
|
+
}, [handleOutsideClick]);
|
|
297
|
+
const { refreshFromStyle } = useSourceMapContext();
|
|
298
|
+
const handleLayerAttached = React.useCallback(() => {
|
|
299
|
+
if (!isMounted.current) {
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
setPopupOpen(false);
|
|
303
|
+
refreshFromStyle();
|
|
304
|
+
}, [refreshFromStyle]);
|
|
305
|
+
function renderButton() {
|
|
306
|
+
let button;
|
|
307
|
+
if (props.buttonType === undefined || props.buttonType === AttachLayerButtonType.Icon) {
|
|
308
|
+
button = (React.createElement(Button, { disabled: props.disabled, size: "small", styleType: "borderless", ref: buttonRef, className: "map-manager-attach-layer-button", title: popupOpen ? hideAttachLayerLabel : showAttachLayerLabel, onClick: togglePopup },
|
|
309
|
+
React.createElement(UiCore.WebFontIcon, { iconName: "icon-add" })));
|
|
310
|
+
}
|
|
311
|
+
else {
|
|
312
|
+
const determineStyleType = () => {
|
|
313
|
+
switch (props.buttonType) {
|
|
314
|
+
case AttachLayerButtonType.Blue:
|
|
315
|
+
return "high-visibility";
|
|
316
|
+
case AttachLayerButtonType.Primary:
|
|
317
|
+
default:
|
|
318
|
+
return "cta";
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
const styleType = determineStyleType();
|
|
322
|
+
button = (React.createElement(Button, { disabled: props.disabled, ref: buttonRef, styleType: styleType, title: popupOpen ? hideAttachLayerLabel : showAttachLayerLabel, onClick: togglePopup }, addCustomLayerButtonLabel));
|
|
323
|
+
}
|
|
324
|
+
return button;
|
|
325
|
+
}
|
|
326
|
+
return (React.createElement(React.Fragment, null,
|
|
327
|
+
renderButton(),
|
|
328
|
+
React.createElement(UiCore.Popup, { isOpen: popupOpen, position: RelativePosition.BottomRight, onClose: handleClosePopup, onOutsideClick: onHandleOutsideClick, target: buttonRef.current, closeOnEnter: false, closeOnContextMenu: false },
|
|
329
|
+
React.createElement("div", { ref: panelRef, className: "map-sources-popup-panel" },
|
|
330
|
+
React.createElement(AttachLayerPanel, { isOverlay: props.isOverlay, onLayerAttached: handleLayerAttached, onHandleOutsideClick: setHandleOutsideClick })))));
|
|
331
|
+
}
|
|
332
332
|
//# sourceMappingURL=AttachLayerPopupButton.js.map
|