Edit Points, Labels, Text, And Arrowheads

Copy Markdown View Source

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
end

Use 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.