Committed vector items can be inspected and edited because their vector state is part of canvas state.
The application decides which edit controls are visible and when. LivePanels provides commands and attrs for the common vector item edits.
Read Vector State
Start with the selected item and ask LivePanels for normalized vector state:
defp selected_vector_item(item) do
case LivePanels.Drawing.vector_state(item) do
{:ok, draw} -> %{item: item, draw: draw}
:error -> nil
end
endUse that data to choose the inspector:
<aside :if={@selected_vector_item} class="vector-item-inspector">
<p>{@selected_vector_item.draw.kind}</p>
</aside>Point Handles
Point-backed vector items include freehand, polyline, arrow, and line vector items.
When rendering a custom SVG editor, add point handle attrs to handles that share the vector item coordinate system:
<circle
:for={{point, index} <- Enum.with_index(@draw.points)}
cx={point.x}
cy={point.y}
r="5"
{vector_point_handle_attrs(@item, index)}
/>The browser drags the handle locally and pushes the committed point update on release.
Server equivalent:
socket =
LivePanels.Drawing.replace_point!(socket, item, index, %{x: 120, y: 48})Add Points
For polyline-like editors, render midpoint add handles:
<circle
:for={{point, index} <- Enum.with_index(@add_points)}
cx={point.x}
cy={point.y}
r="4"
{vector_point_add_handle_attrs(@item, index)}
/>Or use an explicit control:
<button {add_point_attrs(@item, 2, %{x: 180, y: 72})}>
Add point
</button>Server equivalent:
socket =
LivePanels.Drawing.Point.add!(socket, item, 2, %{x: 180, y: 72})Remove Points
Use remove_point_attrs/3 for application controls:
<button {remove_point_attrs(@item, index)}>
Remove point
</button>Server equivalent:
socket = LivePanels.Drawing.Point.remove!(socket, item, index)The server validates that the resulting vector item still has valid point topology.
Text Vector Items
Update committed text vector items through the drawing command path:
socket =
LivePanels.Drawing.update_text!(socket, item, %{
text: "Needs review",
font: %{size: 22, family: "Inter"},
align: :center,
vertical_align: :middle
})For a simple text-only update:
socket = LivePanels.Drawing.update_text!(socket, item, "Needs review")Use this for drawing-owned text. Use a normal item LiveView when the user is editing rich product content.
Bound Labels
Line, arrow, and box-shape vector items can carry a bound label:
socket =
LivePanels.Drawing.update_label!(socket, item, %{
text: "approved",
align: :center,
vertical_align: :middle
})Clear it:
socket = LivePanels.Drawing.clear_label!(socket, item)Labels are vector item presentation. If a label is the only copy of a domain value, store it in your app and write the vector item label as a projection of that value.
Arrowheads And Routes
Arrowheads and route presentation are accepted when creating or importing linear vector items:
LivePanels.Drawing.vector_item!(socket, :arrow, placement,
points: [%{x: 0, y: 40}, %{x: 280, y: 40}],
arrowheads: %{start: false, end: true},
route: :straight,
style: style
)Use graph connectors when endpoints and topology matter. Use drawing arrows when the line is visual annotation.
Edit In Transactions
Group related edits in a transaction when one UI action should create one history entry:
socket =
LivePanels.Canvas.transaction!(socket, fn tx ->
tx
|> LivePanels.Drawing.update_style!(item, style)
|> LivePanels.Drawing.update_label!(item, "review")
end)This is useful for inspector forms that submit several vector item fields at once.