HoloMap.Def (HoloMap v0.1.0)

Copy Markdown View Source

Shared plumbing for the definition components nested inside a HoloMap.Map.

Every child of a map (a source, a layer, a marker, a control) renders the same shape: one empty element carrying its specification as JSON in data attributes. The browser reconciler reads those attributes and does the actual MapLibre work. This module holds the pieces that shape is built from.

The attribute protocol

AttributeMeaning
data-hmThe kind of definition: source, layer, marker, popup, control, terrain, sky
data-hm-keyIdentity. Stable across re-renders; changing it is a remove plus an add
data-hm-specThe MapLibre specification, JSON-encoded
data-hm-eventsEvent name to action-dispatch descriptor, JSON-encoded
data-hm-mapThe cid of the owning map. Debugging aid; the reconciler uses DOM containment

You only need this module to write a definition component of your own. See __using__/1. Using the components in HoloMap requires none of it.

Summary

Functions

Makes the calling module a map-definition component.

The context key HoloMap.Map publishes its cid under.

Encodes event-handler props for a data-hm-events attribute.

Returns the owning map's cid, or raises if there is not one.

Encodes a specification map for a data-hm-spec attribute.

Resolves a visible prop into a MapLibre visibility layout property.

Functions

__using__(opts)

(macro)

Makes the calling module a map-definition component.

Injects use Hologram.Component and the map_cid prop, which is sourced from the context HoloMap.Map publishes.

Pair it with map_cid!/1 in the template. Together they turn "definition component rendered outside a map", otherwise a stray element that silently never becomes part of anything, into a named error that says what to do.

defmodule MyApp.HeatmapLayer do
  use HoloMap.Def

  prop :id, :string, required: true

  def template do
    ~HOLO"""
    <div data-hm="layer" data-hm-key={@id}
         data-hm-map={HoloMap.Def.map_cid!(@map_cid)}
         data-hm-spec={HoloMap.Def.spec(%{"type" => "heatmap"})}></div>
    """
  end
end

context_key()

@spec context_key() :: {module(), atom()}

The context key HoloMap.Map publishes its cid under.

Namespaced by module, as Hologram recommends, so it cannot collide with an application's own context.

events(pairs)

@spec events(keyword()) :: String.t()

Encodes event-handler props for a data-hm-events attribute.

Takes a keyword list of {maplibre_event_name, handler_prop}. Unset handlers are dropped rather than encoded as null, which keeps the attribute stable when a component declares more events than the caller uses. A changed attribute string is what makes the reconciler rebind listeners.

iex> HoloMap.Def.events(click: :parcel_clicked, mouseleave: nil)
~s({"click":{"action":"parcel_clicked","params":{},"target":"page"}})

iex> HoloMap.Def.events(click: nil)
"{}"

map_cid!(cid)

@spec map_cid!(String.t() | nil) :: String.t()

Returns the owning map's cid, or raises if there is not one.

A definition component reads the cid from context, so nil means no HoloMap.Map is above it in the tree. Hologram would otherwise surface that as a bare KeyError on an internal prop name, which says nothing about what the caller did wrong.

iex> HoloMap.Def.map_cid!("explorer")
"explorer"

spec(spec)

@spec spec(map()) :: String.t()

Encodes a specification map for a data-hm-spec attribute.

Drops nil entries, so a component can hand over its full key table and let unset props fall away. See HoloMap.Spec for why an absent key and a null mean different things to MapLibre.

iex> HoloMap.Def.spec(%{"type" => "fill", "source" => "parcels", "filter" => nil})
~s({"source":"parcels","type":"fill"})

visibility(layout, arg2)

@spec visibility(map(), boolean() | nil) :: map()

Resolves a visible prop into a MapLibre visibility layout property.

Visibility is a layout property rather than a top-level layer key, but it is common enough to deserve its own prop. nil leaves the layout map untouched, so a caller setting visibility by hand is not overridden.

iex> HoloMap.Def.visibility(%{"line-cap" => "round"}, false)
%{"line-cap" => "round", "visibility" => "none"}

iex> HoloMap.Def.visibility(%{}, nil)
%{}