Skip to content

Commit

Permalink
Fixed issue that would close the new maplayer dialog when a new layer…
Browse files Browse the repository at this point in the history
… type was picked. (#3313)

* Fixed issue that would close the new maplayer dialog when a new layer type was picked.

* No need to resume outside click handling  when the whole panel is unloaded.

* changelog

(cherry picked from commit 01a67ef)
  • Loading branch information
mdastous-bentley authored and mergify-bot committed Mar 8, 2022
1 parent abc2121 commit 62045ee
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 34 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@itwin/map-layers",
"comment": "Fixed issue that would close the new maplayer dialog when a new layer type was picked.",
"type": "none"
}
],
"packageName": "@itwin/map-layers"
}
94 changes: 61 additions & 33 deletions extensions/map-layers/src/ui/widget/AttachLayerPopupButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,19 @@ import { MapLayersUI } from "../../mapLayers";

// cSpell:ignore droppable Sublayer

enum LayerAction {
Attached,
Edited
}

interface AttachLayerPanelProps {
isOverlay: boolean;
onLayerAttached: () => void;
onHandleOutsideClick?: (shouldHandle: boolean) => void;
}

// eslint-disable-next-line @typescript-eslint/naming-convention
function AttachLayerPanel({ isOverlay, onLayerAttached }: AttachLayerPanelProps) {
function AttachLayerPanel({ isOverlay, onLayerAttached, onHandleOutsideClick }: AttachLayerPanelProps) {
const [layerNameToAdd, setLayerNameToAdd] = React.useState<string | undefined>();
const [sourceFilterString, setSourceFilterString] = React.useState<string | undefined>();

Expand All @@ -41,6 +47,12 @@ function AttachLayerPanel({ isOverlay, onLayerAttached }: AttachLayerPanelProps)
const [loading, setLoading] = React.useState(false);
const [layerNameUnderCursor, setLayerNameUnderCursor] = React.useState<string | undefined>();

const resumeOutsideClick = React.useCallback(() => {
if (onHandleOutsideClick) {
onHandleOutsideClick(true);
}
}, [onHandleOutsideClick]);

// 'isMounted' is used to prevent any async operation once the hook has been
// unloaded. Otherwise we get a 'Can't perform a React state update on an unmounted component.' warning in the console.
const isMounted = React.useRef(false);
Expand Down Expand Up @@ -75,16 +87,21 @@ function AttachLayerPanel({ isOverlay, onLayerAttached }: AttachLayerPanelProps)
return false;
}, [backgroundLayers, overlayLayers]);

const handleModalUrlDialogOk = React.useCallback(() => {
const handleModalUrlDialogOk = React.useCallback((action: LayerAction) => {
if (LayerAction.Attached === action) {
// close popup and refresh UI
onLayerAttached();
}, [onLayerAttached]);
onLayerAttached();
}

resumeOutsideClick();
}, [onLayerAttached, resumeOutsideClick]);

const handleModalUrlDialogCancel = React.useCallback(() => {
// close popup and refresh UI
setLoading(false);
ModalDialogManager.closeDialog();
}, []);
resumeOutsideClick();
}, [resumeOutsideClick]);

React.useEffect(() => {
async function attemptToAddLayer(layerName: string) {
Expand Down Expand Up @@ -129,10 +146,13 @@ function AttachLayerPanel({ isOverlay, onLayerAttached }: AttachLayerPanelProps)
activeViewport={activeViewport}
isOverlay={isOverlay}
layerRequiringCredentials={mapLayerSettings.toJSON()}
onOkResult={handleModalUrlDialogOk}
onOkResult={()=>handleModalUrlDialogOk(LayerAction.Attached)}
onCancelResult={handleModalUrlDialogCancel}
mapTypesOptions={mapTypesOptions} />
);
if (onHandleOutsideClick) {
onHandleOutsideClick(false);
}
}

} else {
Expand Down Expand Up @@ -162,7 +182,7 @@ function AttachLayerPanel({ isOverlay, onLayerAttached }: AttachLayerPanelProps)
setLayerNameToAdd(undefined);
}
}
}, [setLayerNameToAdd, layerNameToAdd, activeViewport, sources, backgroundLayers, isOverlay, overlayLayers, onLayerAttached, handleModalUrlDialogOk, mapTypesOptions, handleModalUrlDialogCancel]);
}, [setLayerNameToAdd, layerNameToAdd, activeViewport, sources, backgroundLayers, isOverlay, overlayLayers, onLayerAttached, handleModalUrlDialogOk, mapTypesOptions, handleModalUrlDialogCancel, onHandleOutsideClick]);

const options = React.useMemo(() => sources?.filter((source) => !styleContainsLayer(source.name)), [sources, styleContainsLayer]);
const filteredOptions = React.useMemo(() => {
Expand All @@ -174,9 +194,17 @@ function AttachLayerPanel({ isOverlay, onLayerAttached }: AttachLayerPanelProps)
}, [options, sourceFilterString]);

const handleAddNewMapSource = React.useCallback(() => {
ModalDialogManager.openDialog(<MapUrlDialog activeViewport={activeViewport} isOverlay={isOverlay} onOkResult={handleModalUrlDialogOk} mapTypesOptions={mapTypesOptions} />);
ModalDialogManager.openDialog(<MapUrlDialog
activeViewport={activeViewport}
isOverlay={isOverlay}
onOkResult={()=>handleModalUrlDialogOk(LayerAction.Attached)}
onCancelResult={handleModalUrlDialogCancel}
mapTypesOptions={mapTypesOptions} />);
if (onHandleOutsideClick) {
onHandleOutsideClick(false);
}
return;
}, [activeViewport, handleModalUrlDialogOk, isOverlay, mapTypesOptions]);
}, [activeViewport, handleModalUrlDialogCancel, handleModalUrlDialogOk, isOverlay, mapTypesOptions, onHandleOutsideClick]);

const handleAttach = React.useCallback((mapName: string) => {
setLayerNameToAdd(mapName);
Expand All @@ -199,7 +227,8 @@ function AttachLayerPanel({ isOverlay, onLayerAttached }: AttachLayerPanelProps)

const handleNoConfirmation = React.useCallback((_layerName: string) => {
ModalDialogManager.closeDialog();
}, []);
resumeOutsideClick();
}, [resumeOutsideClick]);

const handleYesConfirmation = React.useCallback(async (source: MapLayerSource) => {
const layerName = source.name;
Expand All @@ -215,7 +244,8 @@ function AttachLayerPanel({ isOverlay, onLayerAttached }: AttachLayerPanelProps)
}

ModalDialogManager.closeDialog();
}, [iTwinId, iModelId]);
resumeOutsideClick();
}, [iTwinId, iModelId, resumeOutsideClick]);

/*
Handle Remove layer button clicked
Expand All @@ -238,7 +268,10 @@ function AttachLayerPanel({ isOverlay, onLayerAttached }: AttachLayerPanelProps)
onNoResult={() => handleNoConfirmation(layerName)}
/>
);
}, [handleNoConfirmation, handleYesConfirmation, removeLayerDefDialogTitle]);
if (onHandleOutsideClick) {
onHandleOutsideClick(false);
}
}, [handleNoConfirmation, handleYesConfirmation, onHandleOutsideClick, removeLayerDefDialogTitle]);

/*
Handle Edit layer button clicked
Expand All @@ -257,9 +290,14 @@ function AttachLayerPanel({ isOverlay, onLayerAttached }: AttachLayerPanelProps)
activeViewport={activeViewport}
isOverlay={isOverlay}
mapLayerSourceToEdit={matchingSource}
onOkResult={handleModalUrlDialogOk}
onOkResult={()=>handleModalUrlDialogOk(LayerAction.Edited)}
onCancelResult={handleModalUrlDialogCancel}
mapTypesOptions={mapTypesOptions} />);
}, [activeViewport, handleModalUrlDialogOk, isOverlay, mapTypesOptions, sources]);

if (onHandleOutsideClick) {
onHandleOutsideClick(false);
}
}, [activeViewport, handleModalUrlDialogCancel, handleModalUrlDialogOk, isOverlay, mapTypesOptions, onHandleOutsideClick, sources]);

return (
<div className="map-manager-header">
Expand Down Expand Up @@ -340,6 +378,7 @@ export function AttachLayerPopupButton(props: AttachLayerPopupButtonProps) {
};
}, []);

const [handleOutsideClick, setHandleOutsideClick] = React.useState(true);
const [popupOpen, setPopupOpen] = React.useState(false);
const buttonRef = React.useRef<HTMLButtonElement>(null);
const panelRef = React.useRef<HTMLDivElement>(null);
Expand All @@ -362,22 +401,8 @@ export function AttachLayerPopupButton(props: AttachLayerPopupButtonProps) {
setPopupOpen(false);
}, []);

const isInsideCoreDialog = React.useCallback((element: HTMLElement) => {
if (element.nodeName === "DIV") {
if (element.classList && element.classList.contains("core-dialog"))
return true;
if (element.parentElement && isInsideCoreDialog(element.parentElement))
return true;
} else {
// istanbul ignore else
if (element.parentElement && isInsideCoreDialog(element.parentElement))
return true;
}
return false;
}, []);

const handleOutsideClick = React.useCallback((event: MouseEvent) => {
if (isInsideCoreDialog(event.target as HTMLElement)) {
const onHandleOutsideClick = React.useCallback((event: MouseEvent) => {
if (!handleOutsideClick) {
return;
}

Expand All @@ -394,7 +419,7 @@ export function AttachLayerPopupButton(props: AttachLayerPopupButtonProps) {
// If we reach this point, we got an outside clicked, no close the popup
setPopupOpen(false);

}, [isInsideCoreDialog]);
}, [handleOutsideClick]);

const { refreshFromStyle } = useSourceMapContext();

Expand Down Expand Up @@ -443,13 +468,16 @@ export function AttachLayerPopupButton(props: AttachLayerPopupButtonProps) {
isOpen={popupOpen}
position={RelativePosition.BottomRight}
onClose={handleClosePopup}
onOutsideClick={handleOutsideClick}
onOutsideClick={onHandleOutsideClick}
target={buttonRef.current}
closeOnEnter={false}
closeOnContextMenu={false}
>
<div ref={panelRef} className="map-sources-popup-panel" >
<AttachLayerPanel isOverlay={props.isOverlay} onLayerAttached={handleLayerAttached} />
<AttachLayerPanel
isOverlay={props.isOverlay}
onLayerAttached={handleLayerAttached}
onHandleOutsideClick={setHandleOutsideClick}/>
</div>
</UiCore.Popup >
</>
Expand Down
5 changes: 4 additions & 1 deletion extensions/map-layers/src/ui/widget/MapUrlDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,7 @@ export function MapUrlDialog(props: MapUrlDialogProps) {
if (source === undefined || props.mapLayerSourceToEdit) {

ModalDialogManager.closeDialog();
onOkResult();

if (source === undefined) {
// Close the dialog and inform end user something went wrong.
Expand Down Expand Up @@ -361,13 +362,15 @@ export function MapUrlDialog(props: MapUrlDialogProps) {
// AttachLayerPanel's 'onOkResult' handler. We close it here just in case.
if (closeDialog) {
ModalDialogManager.closeDialog();
onOkResult();
}
} catch (_error) {
onOkResult();
ModalDialogManager.closeDialog();
}
})();

}, [createSource, props.mapLayerSourceToEdit, props.activeViewport, mapUrl, isSettingsStorageAvailable, attemptAttachSource]);
}, [createSource, props.mapLayerSourceToEdit, props.activeViewport, onOkResult, mapUrl, isSettingsStorageAvailable, attemptAttachSource]);

// The first time the dialog is loaded and we already know the layer requires auth. (i.e ImageryProvider already made an attempt)
// makes a request to discover the authentification types and adjust UI accordingly (i.e. username/password fields, Oauth popup)
Expand Down

0 comments on commit 62045ee

Please sign in to comment.