defmodule Bloccs.Web.HexGlyph do @moduledoc """ The bloccs hexagon notation as inline SVG. One function component, `hex_glyph/1`, renders the canonical glyph for a node (the atoms `Bloccs.Introspect.glyph/1` returns) so the dashboard and the marketing notation stay a single visual language. Live state (`:idle | :running | :ok | :failed`) is a CSS class on the outer ``, flipped by an assign — no client animation framework. Brand tokens (kept in sync with `marketing/notation-icons`): fill `#1a1326`, stroke `#7c37ab`, accent `#c98bff`, mark `#fafafa`. """ use Phoenix.Component @hex_path "M0,-52 L45,-26 L45,26 L0,52 L-45,26 L-45,-26 Z" @glyphs ~w(node node_effect source sink split batch join throttle delay)a @doc """ Render the hexagon glyph for `glyph` at optional `x`/`y` (for placement inside a larger topology ``). Falls back to the plain `:node` glyph for an unknown atom so the viewer never crashes on a glyph it doesn't know. ## Attributes * `:glyph` — one of #{inspect(@glyphs)} (required) * `:state` — `:idle | :running | :ok | :failed` (default `:idle`) * `:label` — accessible label / `` (default: the glyph name) * `:x`, `:y` — translate the glyph within a parent SVG (default `0`) * `:size` — rendered px (default `120`) """ attr :glyph, :atom, required: true attr :state, :atom, default: :idle attr :label, :string, default: nil attr :x, :integer, default: 0 attr :y, :integer, default: 0 attr :size, :integer, default: 120 def hex_glyph(assigns) do assigns = assigns |> assign(:glyph, normalize(assigns.glyph)) |> assign(:hex_path, @hex_path) ~H""" {@label || Atom.to_string(@glyph)} {inner(assigns)} """ end # Convenience for templates that want a self-contained, sized . attr :glyph, :atom, required: true attr :state, :atom, default: :idle attr :label, :string, default: nil attr :size, :integer, default: 120 def hex_glyph_svg(assigns) do ~H""" <.hex_glyph glyph={@glyph} state={@state} label={@label} /> """ end # ---- per-glyph inner marks (ports + the distinguishing detail) ---- defp inner(%{glyph: :node} = assigns) do ~H""" <.ports left={[0]} right={[0]} /> """ end defp inner(%{glyph: :node_effect} = assigns) do ~H""" <.ports left={[0]} right={[0]} /> """ end defp inner(%{glyph: :source} = assigns) do ~H""" <.ports right={[0]} /> """ end defp inner(%{glyph: :sink} = assigns) do ~H""" <.ports left={[0]} /> """ end defp inner(%{glyph: :split} = assigns) do ~H""" <.ports left={[0]} right={[-22, 22]} /> """ end defp inner(%{glyph: :batch} = assigns) do ~H""" <.ports left={[0]} right={[0]} /> """ end defp inner(%{glyph: :join} = assigns) do ~H""" <.ports left={[-22, 22]} right={[0]} /> """ end defp inner(%{glyph: :throttle} = assigns) do ~H""" <.ports left={[0]} right={[0]} /> """ end defp inner(%{glyph: :delay} = assigns) do ~H""" <.ports left={[0]} right={[0]} /> """ end # Port nubs on the left (in) and right (out) edges of the hexagon. attr :left, :list, default: [] attr :right, :list, default: [] defp ports(assigns) do ~H""" <%= for y <- @left do %> <% end %> <%= for y <- @right do %> <% end %> """ end @doc "Every glyph this component can render." @spec known() :: [atom()] def known, do: @glyphs defp normalize(glyph) when glyph in @glyphs, do: glyph defp normalize(_), do: :node end