From 9e8fb9a3952bfe7e7c6ca54a656270f99545e6c6 Mon Sep 17 00:00:00 2001 From: Yasutake Yohei <61961825+yasutakeyohei@users.noreply.github.com> Date: Fri, 6 Nov 2026 20:56:59 +0900 Subject: parks: 全画面パネルとアイコン・名前表示を改善 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 全画面パネルに「適用中」表示と下部の注記・閉じるボタンを追加。時計をSVG、水栓を蛇口に変更。高ズームで公園名、公共施設のピン上に名前を表示。市外公園クリックでもキーワードを変えない。 --- src/components/ParkMap.astro | 159 +++++++++++++++++++++++++++++++++------ src/components/ParkMapHelp.astro | 4 +- 2 files changed, 136 insertions(+), 27 deletions(-) (limited to 'src') diff --git a/src/components/ParkMap.astro b/src/components/ParkMap.astro index 6c10d93..07d54bb 100644 --- a/src/components/ParkMap.astro +++ b/src/components/ParkMap.astro @@ -84,6 +84,71 @@ :global(.park-map-filter-body .parks-disclaimer) { margin: 0.3rem 0 0; } + :global(.park-map-filter-status) { + display: inline-block; + margin-left: 0.3rem; + padding: 0 0.35rem; + border-radius: 999px; + background: #1b5e20; + color: #fff; + font-size: 0.7rem; + font-weight: 700; + } + :global(.park-map-filter-footer) { + flex: none; + display: flex; + gap: 0.5rem; + align-items: center; + justify-content: space-between; + padding: 0.35rem 0.6rem; + border-top: 1px solid var(--sl-color-gray-5, #cbd5e1); + font-size: 0.72rem; + color: var(--sl-color-gray-2, #666); + } + :global(.park-map-filter-close) { + flex: none; + padding: 0.25rem 0.8rem; + border: 1px solid var(--sl-color-gray-5, #cbd5e1); + border-radius: 999px; + background: transparent; + color: inherit; + font: inherit; + font-size: 0.78rem; + cursor: pointer; + } + :global(.park-name-marker) { + background: none; + border: 0; + } + :global(.park-name-label) { + position: absolute; + left: 0; + top: 0; + transform: translate(-50%, -170%); + background: rgba(255, 255, 255, 0.92); + color: #1b5e20; + font-size: 11px; + font-weight: 700; + line-height: 1.2; + padding: 1px 4px; + border-radius: 3px; + white-space: nowrap; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25); + pointer-events: none; + } + :global(.park-facility-tip) { + background: rgba(255, 255, 255, 0.96); + color: #4a148c; + border: 1px solid #ce93d8; + border-radius: 4px; + font-size: 11px; + font-weight: 700; + padding: 1px 4px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); + } + :global(.park-facility-tip.leaflet-tooltip-top::before) { + display: none; + } :global(.park-map.park-map-touch.leaflet-container) { touch-action: none; } @@ -324,6 +389,7 @@ background: #0277bd; border: 1px solid #01579b; border-radius: 50%; + font-size: 12px; } :global(.park-fac-clock) { background: #f9a825; @@ -664,52 +730,51 @@ const filterToggle = document.createElement("button"); filterToggle.type = "button"; filterToggle.className = "park-map-filter-toggle"; - const filterToggleLabel = (open: boolean) => - `絞り込み `; - filterToggle.innerHTML = filterToggleLabel(false); + filterToggle.innerHTML = + '絞り込み '; + const filterStatus = filterToggle.querySelector(".park-map-filter-status"); + const filterCaret = filterToggle.querySelector(".park-map-filter-caret"); const filterBody = document.createElement("div"); filterBody.className = "park-map-filter-body"; filterBody.hidden = true; - filterPanel.append(filterToggle, filterBody); + const filterFooter = document.createElement("div"); + filterFooter.className = "park-map-filter-footer"; + filterFooter.innerHTML = "情報には誤りが含まれる場合があります。"; + const filterClose = document.createElement("button"); + filterClose.type = "button"; + filterClose.className = "park-map-filter-close"; + filterClose.textContent = "閉じる"; + filterClose.addEventListener("click", () => { + document + .querySelector(".park-map-fullscreen button") + ?.click(); + }); + filterFooter.appendChild(filterClose); + filterPanel.append(filterToggle, filterBody, filterFooter); L.DomEvent.disableClickPropagation(filterPanel); L.DomEvent.disableScrollPropagation(filterPanel); filterToggle.addEventListener("click", () => { filterBody.hidden = !filterBody.hidden; - filterToggle.innerHTML = filterToggleLabel(!filterBody.hidden); + if (filterCaret) filterCaret.textContent = filterBody.hidden ? "▾" : "▴"; }); - let filterHome: { - parent: Node; - next: Node | null; - disclaimerParent: Node | null; - disclaimerNext: Node | null; - } | null = null; + let filterHome: { parent: Node; next: Node | null } | null = null; const setFiltersInMap = (on: boolean) => { const controls = document.querySelector(".parks-controls"); - const disclaimer = document.querySelector(".parks-disclaimer"); if (on) { if (!controls || filterHome) return; filterHome = { parent: controls.parentNode as Node, next: controls.nextSibling, - disclaimerParent: disclaimer?.parentNode ?? null, - disclaimerNext: disclaimer?.nextSibling ?? null, }; filterBody.append(controls); - if (disclaimer) filterBody.append(disclaimer); map.getContainer().appendChild(filterPanel); filterPanel.hidden = false; filterBody.hidden = false; - filterToggle.innerHTML = filterToggleLabel(true); + if (filterCaret) filterCaret.textContent = "▴"; } else if (filterHome && controls) { filterHome.parent.insertBefore(controls, filterHome.next); - if (disclaimer && filterHome.disclaimerParent) { - filterHome.disclaimerParent.insertBefore( - disclaimer, - filterHome.disclaimerNext, - ); - } filterPanel.remove(); filterPanel.hidden = true; filterHome = null; @@ -741,6 +806,7 @@ else map.dragging.disable(); } window.setTimeout(() => map.invalidateSize(), 60); + window.setTimeout(() => map.invalidateSize(), 350); }; const enterCssMax = () => { target.classList.add("is-maximized"); @@ -1017,7 +1083,10 @@ lyr.on("click", (ev: any) => { if (ev?.originalEvent) L.DomEvent.stopPropagation(ev.originalEvent); if (!name) return; - filterName(name, false); + highlightOnly(name); + document.dispatchEvent( + new CustomEvent("park:select-row", { detail: { name } }), + ); showSelected(name, name, true, city || "市外", nearbyMapsQuery(name, city)); }); }, @@ -1061,6 +1130,14 @@ `${icon} ${f.name}${nearby ? "(市外)" : ""}
${f.type}
${f.hours || ""}${f.closed ? `
休: ${f.closed}` : ""}
トイレ: ${f.toilet || "未確認"} / 給水: ${f.water || "未確認"}`, ) .addTo(publicFacilityLayer); + const rawName = String(f.name || ""); + const shortName = + rawName.length > 12 ? `${rawName.slice(0, 11)}…` : rawName; + marker.bindTooltip(shortName, { + direction: "top", + offset: [0, -12], + className: "park-facility-tip", + }); facilityMarkers.push({ name: String(f.name || ""), layer: marker }); }); }; @@ -1103,13 +1180,13 @@ } if ((p["水飲み場"] || "").trim()) { icons.push( - '', + '🚰', ); } const clock = (p["時計"] || "").trim(); if (clock === "○" || clock === "あり" || clock === "有" || clock === "1") { icons.push( - '⏰', + '', ); } const disaster = p["防災用具"] || ""; @@ -1207,6 +1284,26 @@ playMarkersByName.set(name, list); }); + // 公園名のラベル(高ズーム時のみ) + const escapeHtml = (value: string) => + value.replace(/[&<>"']/g, (ch) => + ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[ch] as string, + ); + const parkNameLayer = L.layerGroup(); + layersByName.forEach((lyr, name) => { + const center = centerOfLayer(lyr); + L.marker([center.lat, center.lng], { + icon: L.divIcon({ + className: "park-name-marker", + html: `${escapeHtml(name)}`, + iconSize: null, + iconAnchor: [0, 0], + }), + interactive: false, + keyboard: false, + }).addTo(parkNameLayer); + }); + // フィルタ中は、該当しない公園の設備・遊具アイコンを隠す const applyIconFilter = (names: Set | null) => { const toggle = (markersByName: Map, layer: any) => { @@ -1234,6 +1331,17 @@ } else if (map.hasLayer(playLayer)) { map.removeLayer(playLayer); } + if (map.getZoom() >= 17) { + if (!map.hasLayer(parkNameLayer)) parkNameLayer.addTo(map); + } else if (map.hasLayer(parkNameLayer)) { + map.removeLayer(parkNameLayer); + } + // 公共施設の名前は少し引いたズームから表示する + const showFacilityLabels = map.getZoom() >= 14; + facilityMarkers.forEach(({ layer }) => { + if (showFacilityLabels) layer.openTooltip(); + else layer.closeTooltip(); + }); }; map.on("zoomend", updateFacilities); updateFacilities(); @@ -1347,6 +1455,7 @@ const detail = (e as CustomEvent).detail || {}; const names = new Set(detail.names || []); const active = Boolean(detail.active); + if (filterStatus) filterStatus.hidden = !active; const fromMap = Boolean(detail.fromMap); if (!fromMap && selectedBar) selectedBar.hidden = true; diff --git a/src/components/ParkMapHelp.astro b/src/components/ParkMapHelp.astro index 5703b8c..e45b87f 100644 --- a/src/components/ParkMapHelp.astro +++ b/src/components/ParkMapHelp.astro @@ -14,8 +14,8 @@ 市外の公共施設 小平市の境界 WCトイレ - 水飲み場(拡大時) - ⏰時計(拡大時) + 🚰水飲み場(拡大時) + 時計(拡大時) 消消火栓(拡大時) 槽防火水槽(拡大時) ?情報未確認(拡大時) -- cgit v1.3.1