Render And Style Vector Items

Copy Markdown View Source

Committed vector items render from canvas state.

The standard runtime already knows how to place vector items in the world vector layer. Most drawing apps do not need to render SVG vector items by hand.

Standard Runtime Rendering

Use the normal runtime:

<.canvas_runtime {canvas_runtime_attrs(assigns, cursor_mode: @cursor_mode)}>
  <:item :let={context}>
    <article class="item-frame">
      <header {drag_handle_attrs()}>{context.item.label}</header>
      {context.content}
      <.resize_handles :if={context.view.resizable?} />
    </article>
  </:item>
</.canvas_runtime>

Committed vector items are projected before ordinary HTML item frames. They render as world-space SVG vector items, with selection and resize behavior attached to the same item model as the rest of the canvas.

Keep the :item slot focused on ordinary item frames and content. Drawing vector items do not need a LiveView content process.

Drafts Versus Committed Vector Items

Drafts and committed vector items have different lifecycles:

StateOwnerRendered ByPersisted
Active draftBrowser runtimeBrowser draft layerNo
Committed vector itemCanvas stateRuntime vector layerYes

Use set_draw_style_js/3 for the browser draft style. Use drawing commands or attrs for committed vector item style.

Style Committed Vector Items

Use update_style_attrs/3 for application controls:

<button
  :if={@selected_vector_item}
  {update_style_attrs(@selected_vector_item, %{
    color: "#16a34a",
    width: 3,
    fill_color: "none",
    stroke_style: "solid",
    opacity: 100,
    roundness: 0
  })}
>
  Green
</button>

Server equivalent:

socket =
  LivePanels.Drawing.update_style!(socket, item, %{
    color: "#16a34a",
    width: 3,
    fill_color: "none",
    stroke_style: "solid",
    opacity: 100,
    roundness: 0
  })

The style map is complete on purpose. Application palettes should normalize partial UI changes into a full style before sending the update.

Apply Palette Changes To Selected Vector Items

A common drawing app pattern is to update draft style and any selected vector items at the same time:

def handle_event("set_draw_style", style, socket) do
  style = normalize_draw_style(style, socket.assigns.draw_style)
  socket = assign(socket, :draw_style, style)

  socket =
    socket
    |> selected_vector_items()
    |> Enum.reduce(socket, fn item, acc ->
      LivePanels.Drawing.update_style!(acc, item, style)
    end)

  {:noreply, socket}
end

Keep this as application behavior. Some products want palette changes to affect only new vector items, while others expect selected vector items to change immediately.

Manual Vector Item Rendering

Only use manual rendering when you are building a custom render loop or a custom SVG editor.

Render a neutral SVG vector item:

<LivePanels.Toolkit.Integration.Render.vector_item item={context.item} class="annotation-vector" />

Render the canonical world SVG vector item:

<LivePanels.Toolkit.Integration.Render.svg_vector_item
  id={"vector-item-#{context.item.id}"}
  item={context.item}
  selected={false}
  active={false}
/>

The svg_vector_item/1 component includes the runtime protocol attrs needed for dragging, selection bounds, resize handles, and erase hit targets. If you replace it with fully custom SVG, your application must provide the interaction attrs it needs.

Use Vector State For Custom SVG

Use normalized vector state instead of reading raw item maps:

case LivePanels.Drawing.vector_state(item) do
  {:ok, draw} ->
    assign(socket, :draw, draw)

  :error ->
    socket
end

The state includes kind, style, points, text, labels, route presentation, and other drawing fields that the runtime has already normalized.

Z-Order And Visibility

Vector items are canvas items, so normal item ordering and visibility apply. Use the same application controls you use for other item types:

<button {z_order_selection_attrs(:front)}>Front</button>
<button {z_order_selection_attrs(:back)}>Back</button>

If your app layers drawings over a document, keep the document as an item or background layer and let vector items stay in world space. Avoid a second drawing coordinate system unless the product truly needs one.