feat: allow dragging feature dialog
This commit is contained in:
@@ -7,6 +7,7 @@ const FEATURE_INFO_LAYER_NAMES = new Set(["parcelas", "predios"]);
|
|||||||
const DEVICE_ORIENTATION_EVENTS = ["deviceorientationabsolute", "deviceorientation"];
|
const DEVICE_ORIENTATION_EVENTS = ["deviceorientationabsolute", "deviceorientation"];
|
||||||
const COMPASS_HEADING_DEADBAND = 2;
|
const COMPASS_HEADING_DEADBAND = 2;
|
||||||
const SMALL_SCREEN_MEDIA_QUERY = "(max-width: 920px)";
|
const SMALL_SCREEN_MEDIA_QUERY = "(max-width: 920px)";
|
||||||
|
const MODAL_VIEWPORT_PADDING = 24;
|
||||||
|
|
||||||
const els = {
|
const els = {
|
||||||
appShell: document.querySelector(".app-shell"),
|
appShell: document.querySelector(".app-shell"),
|
||||||
@@ -21,6 +22,7 @@ const els = {
|
|||||||
parcelSearchStatus: document.getElementById("parcel-search-status"),
|
parcelSearchStatus: document.getElementById("parcel-search-status"),
|
||||||
backgroundSelect: document.getElementById("background-select"),
|
backgroundSelect: document.getElementById("background-select"),
|
||||||
featureModal: document.getElementById("feature-modal"),
|
featureModal: document.getElementById("feature-modal"),
|
||||||
|
featureModalDialog: document.querySelector(".feature-modal-dialog"),
|
||||||
featureModalClose: document.getElementById("feature-modal-close"),
|
featureModalClose: document.getElementById("feature-modal-close"),
|
||||||
featureModalKind: document.getElementById("feature-modal-kind"),
|
featureModalKind: document.getElementById("feature-modal-kind"),
|
||||||
featureArea: document.getElementById("feature-area"),
|
featureArea: document.getElementById("feature-area"),
|
||||||
@@ -108,6 +110,7 @@ let lastCompassHeading;
|
|||||||
let pendingCompassHeading;
|
let pendingCompassHeading;
|
||||||
let compassAnimationFrame;
|
let compassAnimationFrame;
|
||||||
let deviceCompassFallbackTimer;
|
let deviceCompassFallbackTimer;
|
||||||
|
let modalDrag;
|
||||||
|
|
||||||
const map = new ol.Map({
|
const map = new ol.Map({
|
||||||
target: "map",
|
target: "map",
|
||||||
@@ -148,6 +151,10 @@ els.backgroundSelect.addEventListener("change", syncBackground);
|
|||||||
els.sidebarOpen.addEventListener("click", () => setSidebarCollapsed(false));
|
els.sidebarOpen.addEventListener("click", () => setSidebarCollapsed(false));
|
||||||
els.sidebarClose.addEventListener("click", () => setSidebarCollapsed(true));
|
els.sidebarClose.addEventListener("click", () => setSidebarCollapsed(true));
|
||||||
els.featureModalClose.addEventListener("click", closeFeatureModal);
|
els.featureModalClose.addEventListener("click", closeFeatureModal);
|
||||||
|
els.featureModalDialog.addEventListener("pointerdown", startFeatureModalDrag);
|
||||||
|
els.featureModalDialog.addEventListener("pointermove", dragFeatureModal);
|
||||||
|
els.featureModalDialog.addEventListener("pointerup", stopFeatureModalDrag);
|
||||||
|
els.featureModalDialog.addEventListener("pointercancel", stopFeatureModalDrag);
|
||||||
els.featureModal.addEventListener("click", (event) => {
|
els.featureModal.addEventListener("click", (event) => {
|
||||||
if (event.target.matches("[data-feature-modal-close]")) {
|
if (event.target.matches("[data-feature-modal-close]")) {
|
||||||
closeFeatureModal();
|
closeFeatureModal();
|
||||||
@@ -164,6 +171,7 @@ document.addEventListener("keydown", (event) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
window.addEventListener("resize", keepFeatureModalInViewport);
|
||||||
map.on("singleclick", showFeatureInformation);
|
map.on("singleclick", showFeatureInformation);
|
||||||
areaZoomInteraction.on("boxend", zoomToSelectedArea);
|
areaZoomInteraction.on("boxend", zoomToSelectedArea);
|
||||||
areaZoomInteraction.on("boxcancel", () => {
|
areaZoomInteraction.on("boxcancel", () => {
|
||||||
@@ -745,9 +753,75 @@ function unqualifiedLayerName(name) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function closeFeatureModal() {
|
function closeFeatureModal() {
|
||||||
|
resetFeatureModalPosition();
|
||||||
els.featureModal.hidden = true;
|
els.featureModal.hidden = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function startFeatureModalDrag(event) {
|
||||||
|
if (event.button !== 0 || event.target.closest("button, a, input, select, textarea, label")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dialogRect = els.featureModalDialog.getBoundingClientRect();
|
||||||
|
els.featureModalDialog.style.position = "fixed";
|
||||||
|
els.featureModalDialog.style.left = `${dialogRect.left}px`;
|
||||||
|
els.featureModalDialog.style.top = `${dialogRect.top}px`;
|
||||||
|
els.featureModalDialog.style.margin = "0";
|
||||||
|
els.featureModalDialog.classList.add("is-dragging");
|
||||||
|
modalDrag = {
|
||||||
|
pointerId: event.pointerId,
|
||||||
|
offsetX: event.clientX - dialogRect.left,
|
||||||
|
offsetY: event.clientY - dialogRect.top,
|
||||||
|
};
|
||||||
|
els.featureModalDialog.setPointerCapture(event.pointerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
function dragFeatureModal(event) {
|
||||||
|
if (!modalDrag || event.pointerId !== modalDrag.pointerId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dialogRect = els.featureModalDialog.getBoundingClientRect();
|
||||||
|
const maxLeft = Math.max(MODAL_VIEWPORT_PADDING, window.innerWidth - dialogRect.width - MODAL_VIEWPORT_PADDING);
|
||||||
|
const maxTop = Math.max(MODAL_VIEWPORT_PADDING, window.innerHeight - dialogRect.height - MODAL_VIEWPORT_PADDING);
|
||||||
|
const left = Math.min(maxLeft, Math.max(MODAL_VIEWPORT_PADDING, event.clientX - modalDrag.offsetX));
|
||||||
|
const top = Math.min(maxTop, Math.max(MODAL_VIEWPORT_PADDING, event.clientY - modalDrag.offsetY));
|
||||||
|
|
||||||
|
els.featureModalDialog.style.left = `${left}px`;
|
||||||
|
els.featureModalDialog.style.top = `${top}px`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopFeatureModalDrag(event) {
|
||||||
|
if (!modalDrag || event.pointerId !== modalDrag.pointerId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
els.featureModalDialog.releasePointerCapture(event.pointerId);
|
||||||
|
els.featureModalDialog.classList.remove("is-dragging");
|
||||||
|
modalDrag = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function keepFeatureModalInViewport() {
|
||||||
|
if (els.featureModal.hidden || !els.featureModalDialog.style.left) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dialogRect = els.featureModalDialog.getBoundingClientRect();
|
||||||
|
const maxLeft = Math.max(MODAL_VIEWPORT_PADDING, window.innerWidth - dialogRect.width - MODAL_VIEWPORT_PADDING);
|
||||||
|
const maxTop = Math.max(MODAL_VIEWPORT_PADDING, window.innerHeight - dialogRect.height - MODAL_VIEWPORT_PADDING);
|
||||||
|
els.featureModalDialog.style.left = `${Math.min(maxLeft, Math.max(MODAL_VIEWPORT_PADDING, dialogRect.left))}px`;
|
||||||
|
els.featureModalDialog.style.top = `${Math.min(maxTop, Math.max(MODAL_VIEWPORT_PADDING, dialogRect.top))}px`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetFeatureModalPosition() {
|
||||||
|
modalDrag = undefined;
|
||||||
|
els.featureModalDialog.classList.remove("is-dragging");
|
||||||
|
els.featureModalDialog.style.removeProperty("position");
|
||||||
|
els.featureModalDialog.style.removeProperty("left");
|
||||||
|
els.featureModalDialog.style.removeProperty("top");
|
||||||
|
els.featureModalDialog.style.removeProperty("margin");
|
||||||
|
}
|
||||||
|
|
||||||
function formatFeatureArea(feature) {
|
function formatFeatureArea(feature) {
|
||||||
const properties = feature.properties || {};
|
const properties = feature.properties || {};
|
||||||
const areaEntry = Object.entries(properties).find(([name, value]) =>
|
const areaEntry = Object.entries(properties).find(([name, value]) =>
|
||||||
|
|||||||
+9
-2
@@ -335,7 +335,7 @@ button:disabled {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
background: rgba(31, 42, 46, 0.35);
|
background: rgba(31, 42, 46, 0.35);
|
||||||
backdrop-filter: blur(3px);
|
backdrop-filter: blur(1px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.feature-modal-dialog {
|
.feature-modal-dialog {
|
||||||
@@ -345,8 +345,15 @@ button:disabled {
|
|||||||
border: 1px solid var(--panel-border);
|
border: 1px solid var(--panel-border);
|
||||||
border-radius: 24px;
|
border-radius: 24px;
|
||||||
background: var(--panel);
|
background: var(--panel);
|
||||||
backdrop-filter: blur(5px);
|
backdrop-filter: blur(3px);
|
||||||
box-shadow: var(--shadow);
|
box-shadow: var(--shadow);
|
||||||
|
cursor: grab;
|
||||||
|
touch-action: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-modal-dialog.is-dragging {
|
||||||
|
cursor: grabbing;
|
||||||
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.feature-modal-close {
|
.feature-modal-close {
|
||||||
|
|||||||
Reference in New Issue
Block a user