Packages

shadcn/ui-inspired component library for Phoenix LiveView with eject-based distribution. CSS-first theme system, 829 components across 20+ categories — Calendar, Chart, Animation, DnD, Editor, Collaboration, Typography, Navigation, Background, Surface, and more.

Current section

Files

Jump to
phia_ui lib phiaui_design canvas node.ex
Raw

lib/phiaui_design/canvas/node.ex

defmodule PhiaUiDesign.Canvas.Node do
@moduledoc """
Represents a single node in the design canvas scene graph.
Each node is either a PhiaUI component, a generic HTML element,
a text node, or a frame (grouping container).
"""
@type node_type :: :phia_component | :html_element | :text | :frame
@type family_role :: :root | :child | nil
@type layout_direction :: :vertical | :horizontal | nil
@type size_value :: :fill | :hug | integer() | String.t() | nil
@type t :: %__MODULE__{
id: String.t(),
type: node_type(),
component: atom() | nil,
module: module() | nil,
attrs: map(),
slots: map(),
tag: String.t() | nil,
classes: String.t() | nil,
text_content: String.t() | nil,
children: [String.t()],
parent_id: String.t() | nil,
locked: boolean(),
visible: boolean(),
name: String.t(),
layout: layout_direction(),
gap: integer() | String.t() | nil,
padding: integer() | [integer()] | String.t() | nil,
justify_content: atom() | nil,
align_items: atom() | nil,
wrap: boolean(),
width: size_value(),
height: size_value()
}
@enforce_keys [:id, :type]
defstruct [
:id,
:type,
:component,
:module,
:tag,
:classes,
:text_content,
:parent_id,
:layout,
:gap,
:padding,
:justify_content,
:align_items,
:width,
:height,
attrs: %{},
slots: %{},
children: [],
locked: false,
visible: true,
wrap: false,
name: ""
]
@doc "Create a new PhiaUI component node"
def new_component(component, opts \\ []) do
id = Keyword.get(opts, :id, generate_id())
%__MODULE__{
id: id,
type: :phia_component,
component: component,
module: Keyword.get(opts, :module),
attrs: Keyword.get(opts, :attrs, %{}),
slots: Keyword.get(opts, :slots, %{}),
parent_id: Keyword.get(opts, :parent_id),
name: Keyword.get(opts, :name, to_string(component))
}
end
@doc "Create a new HTML element node"
def new_element(tag, opts \\ []) do
id = Keyword.get(opts, :id, generate_id())
%__MODULE__{
id: id,
type: :html_element,
tag: tag,
classes: Keyword.get(opts, :classes),
parent_id: Keyword.get(opts, :parent_id),
name: Keyword.get(opts, :name, tag),
children: Keyword.get(opts, :children, [])
}
end
@doc "Create a new text node"
def new_text(content, opts \\ []) do
id = Keyword.get(opts, :id, generate_id())
%__MODULE__{
id: id,
type: :text,
text_content: content,
parent_id: Keyword.get(opts, :parent_id),
name: Keyword.get(opts, :name, String.slice(content, 0..20))
}
end
@doc "Create a frame (grouping container)"
def new_frame(opts \\ []) do
id = Keyword.get(opts, :id, generate_id())
%__MODULE__{
id: id,
type: :frame,
classes: Keyword.get(opts, :classes),
parent_id: Keyword.get(opts, :parent_id),
name: Keyword.get(opts, :name, "Frame"),
children: Keyword.get(opts, :children, []),
layout: Keyword.get(opts, :layout),
gap: Keyword.get(opts, :gap),
padding: Keyword.get(opts, :padding),
justify_content: Keyword.get(opts, :justify_content),
align_items: Keyword.get(opts, :align_items),
wrap: Keyword.get(opts, :wrap, false),
width: Keyword.get(opts, :width),
height: Keyword.get(opts, :height)
}
end
defp generate_id do
:crypto.strong_rand_bytes(8) |> Base.url_encode64(padding: false)
end
end