Packages
mbta_metro
0.1.2
1.1.2
1.1.1
1.1.0
1.0.1
1.0.0
0.3.3
0.3.2
0.3.1
0.3.0
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.21
0.1.20
0.1.19
0.1.18
0.1.17
0.1.16
0.1.15
0.1.14
0.1.13
0.1.12
0.1.11
0.1.10
0.1.9
0.1.8
0.1.7
0.1.6
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
0.1.0
0.0.65
0.0.64
0.0.62
0.0.61
0.0.60
0.0.58
0.0.57
0.0.56
0.0.55
0.0.54
0.0.53
0.0.52
0.0.51
0.0.50
0.0.49
0.0.48
0.0.47
0.0.46
0.0.45
0.0.44
0.0.43
0.0.42
0.0.41
0.0.40
0.0.39
0.0.34
0.0.30
0.0.27
0.0.20
0.0.19
0.0.18
0.0.17
0.0.16
0.0.15
0.0.14
0.0.13
0.0.12
0.0.11
0.0.10
0.0.9
0.0.8
0.0.7
0.0.6
0.0.5
0.0.4
0.0.3
0.0.2
0.0.1
0.0.1-alpha
A Phoenix LiveView component library
Current section
Files
Jump to
Current section
Files
lib/mbta_metro/live/map.ex
defmodule MbtaMetro.Live.Map do
@moduledoc """
A live component that renders a map using [maplibre-gl](https://maplibre.org/maplibre-gl-js/docs/).
You can optionally pass in the following assigns:
* `:class` - The CSS classes to apply to the map container. You'll most likely want to set the height and width of the map here.
* `:config` - The maplibre gl configuration.
* `:click_handler` - A boolean indicating whether to handle map clicks.
* `:lines` - A list of lists of coordinates to draw lines on the map. Each list of coordinates should be a list of two numbers: the longitude and latitude.
* `:pins` - A list of coordinates to place pins on the map. Each coordinate should be a list of two numbers: the longitude and latitude.
* `:points` - A list of coordinates to place points on the map. Each coordinate should be a list of two numbers: the longitude and latitude.
If `:click_handler` is `true`, the component will send a `map-clicked` event to the parent live view when the map is clicked.
`pins` will be assigned a letter A-Z for each marker added to the map. E.g., 0 => A, 1 => B, etc.
"""
use Phoenix.LiveComponent
import MbtaMetro.Components.Icon, only: [icon: 1]
@doc """
We check if the map is loaded; if so, we tell the Hook to update the lines and markers every time the component updates.
If the map is not loaded, we check for for assigns and assign defaults for any not passed into the component.
"""
@impl true
def update(assigns, %{assigns: %{loaded: true}} = socket) do
new_socket =
socket
|> assign(assigns)
|> push_event("update-lines", %{})
|> push_event("update-markers", %{})
{:ok, new_socket}
end
def update(assigns, socket) do
new_socket =
assign(socket,
class: Map.get(assigns, :class, ""),
config: Map.get(assigns, :config, %{}),
lines: Map.get(assigns, :lines, []),
loaded: false,
pins: Map.get(assigns, :pins, []),
points: Map.get(assigns, :points, [])
)
{:ok, new_socket}
end
@doc """
There are three main sections of the map component.
The container will hold the map itself.
The lines will hold the lines to draw on the map.
The markers will hold the points and pins to place on the map.
The associated Hook will take the lines and markers and draw them on the map.
"""
@impl true
def render(assigns) do
~H"""
<div id="mbta-metro-map" class={@class} data-config={Jason.encode!(@config)} phx-hook="Map">
<div
id="mbta-metro-map-container"
class="w-full h-full relative overflow-hidden"
phx-update="ignore"
/>
<div id="mbta-metro-map-lines" class="hidden">
<%= for {line, index} <- Enum.with_index(@lines) do %>
<div id={"mbta-metro-line-#{index}"} data-line={Jason.encode!(line)} />
<% end %>
</div>
<div id="mbta-metro-map-markers" class="hidden">
<%= for {coordinates, index} <- Enum.with_index(@points) do %>
<.icon
id={"mbta-metro-point-#{index}"}
type="metro"
name="point"
class="w-4 h-4 fill-cobalt-50"
data-coordinates={Jason.encode!(coordinates)}
/>
<% end %>
<%= for {coordinates, index} <- Enum.with_index(@pins) do %>
<.icon
id={"mbta-metro-pin-#{index}"}
type="metro"
name={index_to_pin(index)}
class="w-16 h-16 fill-cobalt-50"
data-coordinates={Jason.encode!(coordinates)}
/>
<% end %>
</div>
</div>
"""
end
@doc """
The map has to be loaded before we can draw anything on it.
Once the Hook tells us it has loaded, we tell it to update the lines and markers.
We then update the `loaded` assign to `true` so we know future updates can be drawn on the map.
"""
@impl true
def handle_event("map-loaded", _params, socket) do
new_socket =
socket
|> assign(:loaded, true)
|> push_event("update-lines", %{})
|> push_event("update-markers", %{})
{:noreply, new_socket}
end
defp index_to_pin(idx) when idx >= 0 and idx < 26 do
alphabet = ~w(a b c d e f g h i j k l m n o p q r s t u v w x y z)
"location-pin-#{Enum.at(alphabet, idx)}"
end
end