feat: add collapsible map controls
This commit is contained in:
@@ -9,6 +9,7 @@ COPY server.js ./
|
|||||||
COPY index.html ./
|
COPY index.html ./
|
||||||
COPY app.js ./
|
COPY app.js ./
|
||||||
COPY styles.css ./
|
COPY styles.css ./
|
||||||
|
COPY assets ./assets
|
||||||
|
|
||||||
ENV HOST=0.0.0.0
|
ENV HOST=0.0.0.0
|
||||||
ENV PORT=4173
|
ENV PORT=4173
|
||||||
|
|||||||
@@ -4,6 +4,10 @@ const API_WMS = "/api/wms";
|
|||||||
const PARCEL_LAYER_NAME = "xpro:parcelas";
|
const PARCEL_LAYER_NAME = "xpro:parcelas";
|
||||||
|
|
||||||
const els = {
|
const els = {
|
||||||
|
appShell: document.querySelector(".app-shell"),
|
||||||
|
sidebar: document.getElementById("sidebar"),
|
||||||
|
sidebarOpen: document.getElementById("sidebar-open"),
|
||||||
|
sidebarClose: document.getElementById("sidebar-close"),
|
||||||
status: document.getElementById("status"),
|
status: document.getElementById("status"),
|
||||||
layerList: document.getElementById("layer-list"),
|
layerList: document.getElementById("layer-list"),
|
||||||
layerCount: document.getElementById("layer-count"),
|
layerCount: document.getElementById("layer-count"),
|
||||||
@@ -77,9 +81,26 @@ let selectedLayers = [];
|
|||||||
|
|
||||||
els.parcelSearchForm.addEventListener("submit", searchParcela);
|
els.parcelSearchForm.addEventListener("submit", searchParcela);
|
||||||
els.backgroundSelect.addEventListener("change", syncBackground);
|
els.backgroundSelect.addEventListener("change", syncBackground);
|
||||||
|
els.sidebarOpen.addEventListener("click", () => setSidebarCollapsed(false));
|
||||||
|
els.sidebarClose.addEventListener("click", () => setSidebarCollapsed(true));
|
||||||
|
document.addEventListener("keydown", (event) => {
|
||||||
|
if (event.key === "Escape" && !els.appShell.classList.contains("sidebar-is-collapsed")) {
|
||||||
|
setSidebarCollapsed(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
loadLayers();
|
loadLayers();
|
||||||
|
|
||||||
|
function setSidebarCollapsed(collapsed) {
|
||||||
|
els.appShell.classList.toggle("sidebar-is-collapsed", collapsed);
|
||||||
|
els.sidebar.inert = collapsed;
|
||||||
|
els.sidebarOpen.setAttribute("aria-expanded", String(!collapsed));
|
||||||
|
|
||||||
|
if (collapsed) {
|
||||||
|
els.sidebarOpen.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function searchParcela(event) {
|
async function searchParcela(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 66.295753 33.036644">
|
||||||
|
<g transform="translate(-15.909682 -167.46353)">
|
||||||
|
<path fill="#1574c3" d="m 51.662106,186.67656 h 1.857378 v -3.80966 h 0.59653 c 1.423537,0 2.074297,-0.10846 2.630155,-0.4474 0.81345,-0.51518 1.315077,-1.51844 1.315077,-2.67082 0,-1.15239 -0.48807,-2.10142 -1.35575,-2.63016 -0.569415,-0.33894 -1.24729,-0.46095 -2.562367,-0.46095 h -2.481023 z m 1.857378,-5.58569 v -2.65727 h 0.623645 c 0.610087,0 0.894795,0.0271 1.179502,0.0949 0.5423,0.14913 0.86768,0.62365 0.86768,1.24729 0,0.48807 -0.21692,0.86768 -0.610087,1.0846 -0.284708,0.14913 -0.786335,0.23048 -1.51844,0.23048 z m 5.500524,5.58569 h 1.803148 v -4.17571 c -0.04067,-1.11172 0.528742,-1.73536 1.613342,-1.77603 v -1.73536 h -0.135575 c -0.772777,0 -1.152387,0.21692 -1.6269,0.90835 v -0.73211 h -1.654015 z m 7.683272,-7.6871 c -2.182758,0 -3.972348,1.77603 -3.972348,3.93167 0,2.1692 1.78959,3.93168 3.985905,3.93168 2.182758,0 3.999463,-1.76248 3.999463,-3.87745 0,-2.23699 -1.748918,-3.9859 -4.01302,-3.9859 z m 0.01356,1.65401 c 1.206618,0 2.182758,1.01681 2.182758,2.27766 0,1.26085 -0.97614,2.27766 -2.1692,2.27766 -1.220175,0 -2.182758,-1.01681 -2.182758,-2.30477 0,-1.23374 0.97614,-2.25055 2.1692,-2.25055 z" transform="matrix(1.729104 0 0 1.729104 -47.763707 -130.17409)"/>
|
||||||
|
<path fill="#f8a621" d="m 41.157585,186.71507 h 2.209873 l 2.33189,-3.45716 2.399677,3.45716 h 2.155643 l -3.47072,-4.98916 3.47072,-5.02983 h -2.155643 l -2.399677,3.49784 -2.33189,-3.49784 h -2.209873 l 3.443605,5.02983 z" transform="matrix(1.729104 0 0 1.729104 -47.763707 -130.17409)"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.6 KiB |
+21
-2
@@ -9,9 +9,28 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="app-shell">
|
<div class="app-shell">
|
||||||
<aside class="sidebar">
|
<button
|
||||||
|
id="sidebar-open"
|
||||||
|
class="sidebar-toggle sidebar-open"
|
||||||
|
type="button"
|
||||||
|
aria-controls="sidebar"
|
||||||
|
aria-expanded="true"
|
||||||
|
>
|
||||||
|
<span aria-hidden="true">☰</span>
|
||||||
|
<span class="visually-hidden">Open map controls</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<aside id="sidebar" class="sidebar" aria-label="Map controls">
|
||||||
|
<button id="sidebar-close" class="sidebar-toggle sidebar-close" type="button">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
<span class="visually-hidden">Close map controls</span>
|
||||||
|
</button>
|
||||||
<div class="brand">
|
<div class="brand">
|
||||||
<h1>XPro Map Visualizer</h1>
|
<h1>
|
||||||
|
<img class="brand-logo" src="./assets/brand/xpro-logo-simple.svg" alt="XPro" />
|
||||||
|
<span class="visually-hidden">XPro</span>
|
||||||
|
</h1>
|
||||||
|
<p class="brand-subtitle">Map Visualizer</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<section class="panel">
|
<section class="panel">
|
||||||
|
|||||||
+122
-8
@@ -1,7 +1,7 @@
|
|||||||
:root {
|
:root {
|
||||||
color-scheme: light;
|
color-scheme: light;
|
||||||
--bg: #f3efe3;
|
--bg: #f3efe3;
|
||||||
--panel: rgba(253, 249, 240, 0.92);
|
--panel: rgba(253, 249, 240, 0.75);
|
||||||
--panel-border: rgba(92, 73, 49, 0.18);
|
--panel-border: rgba(92, 73, 49, 0.18);
|
||||||
--ink: #1f2a2e;
|
--ink: #1f2a2e;
|
||||||
--muted: #5f6b6f;
|
--muted: #5f6b6f;
|
||||||
@@ -30,10 +30,8 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.app-shell {
|
.app-shell {
|
||||||
display: grid;
|
position: relative;
|
||||||
grid-template-columns: minmax(320px, 380px) 1fr;
|
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
gap: 24px;
|
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,9 +41,70 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.sidebar {
|
.sidebar {
|
||||||
|
position: fixed;
|
||||||
|
z-index: 10;
|
||||||
|
top: 24px;
|
||||||
|
bottom: 24px;
|
||||||
|
left: 24px;
|
||||||
|
width: min(266px, calc(100vw - 48px));
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 8px;
|
||||||
|
transition: transform 240ms ease;
|
||||||
|
backdrop-filter: blur(3px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-toggle {
|
||||||
|
position: fixed;
|
||||||
|
z-index: 11;
|
||||||
|
display: grid;
|
||||||
|
width: 42px;
|
||||||
|
height: 42px;
|
||||||
|
padding: 0;
|
||||||
|
place-items: center;
|
||||||
|
border: 1px solid var(--panel-border);
|
||||||
|
border-radius: 14px;
|
||||||
|
background: var(--panel);
|
||||||
|
color: var(--ink);
|
||||||
|
font-size: 1.65rem;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1;
|
||||||
|
box-shadow: var(--shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-toggle:hover {
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-open {
|
||||||
|
top: 32px;
|
||||||
|
left: 32px;
|
||||||
|
opacity: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
pointer-events: none;
|
||||||
|
transition: opacity 180ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-close {
|
||||||
|
position: absolute;
|
||||||
|
top: 18px;
|
||||||
|
right: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-close span[aria-hidden="true"] {
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-shell.sidebar-is-collapsed .sidebar {
|
||||||
|
transform: translateX(calc(-100% - 24px));
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-shell.sidebar-is-collapsed .sidebar-open {
|
||||||
|
opacity: 1;
|
||||||
|
visibility: visible;
|
||||||
|
pointer-events: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.brand,
|
.brand,
|
||||||
@@ -61,6 +120,32 @@ body {
|
|||||||
padding: 24px;
|
padding: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.brand-logo {
|
||||||
|
display: block;
|
||||||
|
width: min(100%, 178px);
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand-subtitle {
|
||||||
|
margin-top: 8px;
|
||||||
|
color: var(--muted);
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.visually-hidden {
|
||||||
|
position: absolute;
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
padding: 0;
|
||||||
|
margin: -1px;
|
||||||
|
overflow: hidden;
|
||||||
|
clip: rect(0, 0, 0, 0);
|
||||||
|
white-space: nowrap;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
h1,
|
h1,
|
||||||
h2,
|
h2,
|
||||||
p {
|
p {
|
||||||
@@ -210,11 +295,12 @@ button:disabled {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.map-wrap {
|
.map-wrap {
|
||||||
position: relative;
|
position: fixed;
|
||||||
|
z-index: 0;
|
||||||
|
inset: 24px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border-radius: 32px;
|
border-radius: 32px;
|
||||||
box-shadow: var(--shadow);
|
box-shadow: var(--shadow);
|
||||||
min-height: 70vh;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#map {
|
#map {
|
||||||
@@ -222,12 +308,40 @@ button:disabled {
|
|||||||
inset: 0;
|
inset: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.map-wrap .ol-zoom {
|
||||||
|
top: 16px;
|
||||||
|
right: 16px;
|
||||||
|
left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 920px) {
|
@media (max-width: 920px) {
|
||||||
.app-shell {
|
.app-shell {
|
||||||
grid-template-columns: 1fr;
|
padding: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.map-wrap {
|
.map-wrap {
|
||||||
min-height: 62vh;
|
inset: 16px;
|
||||||
|
border-radius: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.map-wrap .ol-zoom {
|
||||||
|
top: 12px;
|
||||||
|
right: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
top: 16px;
|
||||||
|
bottom: 16px;
|
||||||
|
left: 16px;
|
||||||
|
width: min(266px, calc(100vw - 32px));
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-open {
|
||||||
|
top: 24px;
|
||||||
|
left: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-shell.sidebar-is-collapsed .sidebar {
|
||||||
|
transform: translateX(calc(-100% - 16px));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user