feat(maps): add location recenter control
This commit is contained in:
parent
bcbe417a79
commit
51de2d829b
|
|
@ -167,6 +167,22 @@ html {
|
|||
text-underline-offset: 0.12em;
|
||||
}
|
||||
|
||||
.map-recenter-control {
|
||||
display: grid !important;
|
||||
place-items: center;
|
||||
color: #1f2937;
|
||||
}
|
||||
|
||||
.map-recenter-icon {
|
||||
width: 1.125rem;
|
||||
height: 1.125rem;
|
||||
fill: none;
|
||||
stroke: currentColor;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
.request-discovery-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
|
|||
|
|
@ -147,6 +147,59 @@ const mapRetryButton = (element, retry) => {
|
|||
return button
|
||||
}
|
||||
|
||||
const mapRecenterControl = (element, recenter) => {
|
||||
let container = null
|
||||
let button = null
|
||||
|
||||
const handleClick = event => {
|
||||
event.preventDefault()
|
||||
recenter()
|
||||
}
|
||||
|
||||
return {
|
||||
onAdd() {
|
||||
const label = element.dataset.mapRecenterLabel || "Show location on map"
|
||||
const iconNamespace = "http://www.w3.org/2000/svg"
|
||||
|
||||
container = document.createElement("div")
|
||||
container.className = "maplibregl-ctrl maplibregl-ctrl-group"
|
||||
|
||||
button = document.createElement("button")
|
||||
button.type = "button"
|
||||
button.className = "map-recenter-control"
|
||||
button.dataset.mapRecenter = "true"
|
||||
button.title = label
|
||||
button.setAttribute("aria-label", label)
|
||||
|
||||
const icon = document.createElementNS(iconNamespace, "svg")
|
||||
icon.classList.add("map-recenter-icon")
|
||||
icon.setAttribute("viewBox", "0 0 24 24")
|
||||
icon.setAttribute("aria-hidden", "true")
|
||||
|
||||
const circle = document.createElementNS(iconNamespace, "circle")
|
||||
circle.setAttribute("cx", "12")
|
||||
circle.setAttribute("cy", "12")
|
||||
circle.setAttribute("r", "4.5")
|
||||
|
||||
const crosshair = document.createElementNS(iconNamespace, "path")
|
||||
crosshair.setAttribute("d", "M12 2v4m0 12v4M2 12h4m12 0h4")
|
||||
|
||||
icon.append(circle, crosshair)
|
||||
button.append(icon)
|
||||
button.addEventListener("click", handleClick)
|
||||
container.append(button)
|
||||
|
||||
return container
|
||||
},
|
||||
onRemove() {
|
||||
button?.removeEventListener("click", handleClick)
|
||||
container?.remove()
|
||||
button = null
|
||||
container = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const reportMapUnavailable = (element, reason, error = null) => {
|
||||
element.dataset.mapUnavailable = "true"
|
||||
element.dataset.mapUnavailableReason = reason
|
||||
|
|
@ -169,6 +222,8 @@ const createAidMap = element => {
|
|||
markers: [],
|
||||
markerElements: new Map(),
|
||||
initializedViewport: false,
|
||||
locationBounds: null,
|
||||
recenterControl: null,
|
||||
areaSourceId: `${element.id || "aid-map"}-areas`,
|
||||
areaFillId: `${element.id || "aid-map"}-area-fill`,
|
||||
areaLineId: `${element.id || "aid-map"}-area-line`,
|
||||
|
|
@ -348,6 +403,18 @@ const createAidMap = element => {
|
|||
)
|
||||
}
|
||||
|
||||
state.fitMarkers = (duration = 0) => {
|
||||
if (!state.active || !state.map || !state.locationBounds) return false
|
||||
|
||||
state.map.fitBounds(state.locationBounds, {
|
||||
padding: 52,
|
||||
maxZoom: 13,
|
||||
duration
|
||||
})
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
state.renderMarkers = () => {
|
||||
if (!state.map) return
|
||||
|
||||
|
|
@ -361,6 +428,12 @@ const createAidMap = element => {
|
|||
)
|
||||
const areaFeatures = areaFeatureCollection(points)
|
||||
const clusterFeatures = clusterFeatureCollection(points)
|
||||
const bounds = new maplibregl.LngLatBounds()
|
||||
|
||||
exactPoints.forEach(point => bounds.extend([point.longitude, point.latitude]))
|
||||
areaFeatures.features.forEach(feature => extendBoundsWithFeature(bounds, feature))
|
||||
clusterFeatures.features.forEach(feature => bounds.extend(feature.geometry.coordinates))
|
||||
state.locationBounds = points.length > 0 ? bounds : null
|
||||
|
||||
if (state.map.isStyleLoaded()) {
|
||||
const areaSource = state.map.getSource(state.areaSourceId)
|
||||
|
|
@ -456,13 +529,7 @@ const createAidMap = element => {
|
|||
state.markers.push(marker)
|
||||
})
|
||||
|
||||
if (points.length > 0 && state.element.dataset.autoFit !== "false") {
|
||||
const bounds = new maplibregl.LngLatBounds()
|
||||
exactPoints.forEach(point => bounds.extend([point.longitude, point.latitude]))
|
||||
areaFeatures.features.forEach(feature => extendBoundsWithFeature(bounds, feature))
|
||||
clusterFeatures.features.forEach(feature => bounds.extend(feature.geometry.coordinates))
|
||||
state.map.fitBounds(bounds, {padding: 52, maxZoom: 13, duration: 0})
|
||||
}
|
||||
if (state.element.dataset.autoFit !== "false") state.fitMarkers()
|
||||
}
|
||||
|
||||
state.mount = () => {
|
||||
|
|
@ -501,6 +568,11 @@ const createAidMap = element => {
|
|||
demoMap ? "bottom-right" : "top-right"
|
||||
)
|
||||
|
||||
if (state.element.dataset.mapRecenterLabel) {
|
||||
state.recenterControl = mapRecenterControl(state.element, () => state.fitMarkers(450))
|
||||
state.map.addControl(state.recenterControl, "top-right")
|
||||
}
|
||||
|
||||
if (state.element.dataset.discoveryMap === "true" && navigator.geolocation) {
|
||||
state.map.addControl(
|
||||
new maplibregl.GeolocateControl({
|
||||
|
|
@ -572,6 +644,8 @@ const createAidMap = element => {
|
|||
window.removeEventListener("wnh:request-highlight", state.onRequestHighlight)
|
||||
state.map?.remove()
|
||||
state.map = null
|
||||
state.locationBounds = null
|
||||
state.recenterControl = null
|
||||
}
|
||||
|
||||
return state
|
||||
|
|
|
|||
|
|
@ -284,6 +284,7 @@ defmodule WhoNeedHelpWeb.ActivityLive.Index do
|
|||
gettext("The map is unavailable in this browser. Request details remain usable.")
|
||||
}
|
||||
data-map-retry-label={gettext("Try map again")}
|
||||
data-map-recenter-label={gettext("Show location on map")}
|
||||
class="aid-map"
|
||||
/>
|
||||
</aside>
|
||||
|
|
|
|||
|
|
@ -540,6 +540,7 @@ defmodule WhoNeedHelpWeb.ActivityLive.Show do
|
|||
gettext("The map is unavailable in this browser. Request details remain usable.")
|
||||
}
|
||||
data-map-retry-label={gettext("Try map again")}
|
||||
data-map-recenter-label={gettext("Show location on map")}
|
||||
class="aid-map"
|
||||
/>
|
||||
<div class="rounded-2xl bg-base-200 p-5 text-sm">
|
||||
|
|
|
|||
|
|
@ -1282,6 +1282,7 @@ defmodule WhoNeedHelpWeb.RequestLive.Show do
|
|||
gettext("The map is unavailable in this browser. Request details remain usable.")
|
||||
}
|
||||
data-map-retry-label={gettext("Try map again")}
|
||||
data-map-recenter-label={gettext("Show location on map")}
|
||||
class="aid-map"
|
||||
/>
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -5421,3 +5421,10 @@ msgstr ""
|
|||
#, elixir-autogen, elixir-format
|
||||
msgid "WebGL is unavailable in this browser, so the full map cannot start. Select an example below or retry the map."
|
||||
msgstr ""
|
||||
|
||||
#: lib/who_need_help_web/live/activity_live/index.ex
|
||||
#: lib/who_need_help_web/live/activity_live/show.ex
|
||||
#: lib/who_need_help_web/live/request_live/show.ex
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Show location on map"
|
||||
msgstr ""
|
||||
|
|
|
|||
|
|
@ -5421,3 +5421,10 @@ msgstr "Selected example:"
|
|||
#, elixir-autogen, elixir-format
|
||||
msgid "WebGL is unavailable in this browser, so the full map cannot start. Select an example below or retry the map."
|
||||
msgstr "WebGL is unavailable in this browser, so the full map cannot start. Select an example below or retry the map."
|
||||
|
||||
#: lib/who_need_help_web/live/activity_live/index.ex
|
||||
#: lib/who_need_help_web/live/activity_live/show.ex
|
||||
#: lib/who_need_help_web/live/request_live/show.ex
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Show location on map"
|
||||
msgstr "Show location on map"
|
||||
|
|
|
|||
|
|
@ -5573,3 +5573,10 @@ msgstr "Выбранный пример:"
|
|||
#, elixir-autogen, elixir-format
|
||||
msgid "WebGL is unavailable in this browser, so the full map cannot start. Select an example below or retry the map."
|
||||
msgstr "WebGL недоступен в этом браузере, поэтому полная карта не может запуститься. Выберите пример ниже или попробуйте запустить карту ещё раз."
|
||||
|
||||
#: lib/who_need_help_web/live/activity_live/index.ex
|
||||
#: lib/who_need_help_web/live/activity_live/show.ex
|
||||
#: lib/who_need_help_web/live/request_live/show.ex
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Show location on map"
|
||||
msgstr "Показать место на карте"
|
||||
|
|
|
|||
|
|
@ -5565,3 +5565,10 @@ msgstr "Вибраний приклад:"
|
|||
#, elixir-autogen, elixir-format
|
||||
msgid "WebGL is unavailable in this browser, so the full map cannot start. Select an example below or retry the map."
|
||||
msgstr "WebGL недоступний у цьому браузері, тому повна карта не може запуститися. Виберіть приклад нижче або спробуйте запустити карту ще раз."
|
||||
|
||||
#: lib/who_need_help_web/live/activity_live/index.ex
|
||||
#: lib/who_need_help_web/live/activity_live/show.ex
|
||||
#: lib/who_need_help_web/live/request_live/show.ex
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "Show location on map"
|
||||
msgstr "Показати місце на мапі"
|
||||
|
|
|
|||
|
|
@ -322,6 +322,7 @@ defmodule WhoNeedHelpWeb.MutualAidLiveTest do
|
|||
|> Enum.find(&(&1.slug == "coffee-meetup"))
|
||||
|
||||
{:ok, view, _html} = build_conn() |> log_in_user(viewer) |> live(~p"/activities")
|
||||
assert has_element?(view, "#activity-map[data-map-recenter-label='Show location on map']")
|
||||
|
||||
{:ok, _activity} =
|
||||
Activities.create_activity(Accounts.Scope.for_user(organizer), %{
|
||||
|
|
@ -661,6 +662,11 @@ defmodule WhoNeedHelpWeb.MutualAidLiveTest do
|
|||
{:ok, activity} = Activities.create_activity(organizer_scope, attrs)
|
||||
{:ok, organizer_view, _html} = live(organizer_conn, ~p"/activities/#{activity.id}")
|
||||
{:ok, participant_view, html} = live(participant_conn, ~p"/activities/#{activity.id}")
|
||||
assert has_element?(
|
||||
participant_view,
|
||||
"#activity-detail-map[data-map-recenter-label='Show location on map']"
|
||||
)
|
||||
|
||||
assert html =~ "Request to join"
|
||||
assert html =~ "Safety controls"
|
||||
assert html =~ "Block organizer"
|
||||
|
|
@ -783,6 +789,7 @@ defmodule WhoNeedHelpWeb.MutualAidLiveTest do
|
|||
{:ok, request} = Help.create_request(requester_scope, request_attrs(category))
|
||||
{:ok, requester_view, _html} = live(requester_conn, ~p"/requests/#{request.id}")
|
||||
{:ok, helper_view, _html} = live(helper_conn, ~p"/requests/#{request.id}")
|
||||
assert has_element?(requester_view, "#show-map[data-map-recenter-label='Show location on map']")
|
||||
|
||||
helper_view
|
||||
|> element("button[phx-click='accept']")
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user