Packages
corex
0.1.0-rc.1
0.1.2
0.1.1
0.1.0
0.1.0-rc.1
0.1.0-rc.0
0.1.0-beta.5
0.1.0-beta.4
0.1.0-beta.3
0.1.0-beta.2
0.1.0-beta.1
0.1.0-alpha.33
0.1.0-alpha.32
0.1.0-alpha.31
0.1.0-alpha.30
0.1.0-alpha.29
0.1.0-alpha.28
0.1.0-alpha.27
0.1.0-alpha.26
0.1.0-alpha.25
0.1.0-alpha.24
0.1.0-alpha.23
0.1.0-alpha.22
0.1.0-alpha.21
0.1.0-alpha.20
0.1.0-alpha.19
0.1.0-alpha.18
0.1.0-alpha.17
0.1.0-alpha.16
0.1.0-alpha.15
0.1.0-alpha.14
0.1.0-alpha.13
0.1.0-alpha.12
0.1.0-alpha.11
0.1.0-alpha.10
0.1.0-alpha.9
0.1.0-alpha.8
0.1.0-alpha.7
0.1.0-alpha.6
0.1.0-alpha.5
0.1.0-alpha.4
0.1.0-alpha.3
0.1.0-alpha.2
0.1.0-alpha.1
Accessible and unstyled UI components library written in Elixir and TypeScript that integrates Zag.js state machines into the Phoenix Framework.
Current section
Files
Jump to
Current section
Files
lib/components/tree_view/connect.ex
defmodule Corex.TreeView.Connect do
@moduledoc false
alias Corex.Animation.Height
alias Corex.Selectors
alias Corex.TreeView.Anatomy.{
Branch,
BranchContent,
BranchControl,
BranchIndentGuide,
BranchIndicator,
BranchText,
Item,
ItemIndicator,
ItemText,
Label,
Props,
Root,
Tree
}
alias Phoenix.LiveView.JS
import Corex.Helpers,
only: [
validate_value!: 1,
get_boolean: 1,
joined_csv_values: 1
]
defp depth_style(index_path) when is_list(index_path), do: "--depth: #{length(index_path)}"
defp depth_style(_), do: "--depth: 0"
defp path_key([]), do: "root"
defp path_key(index_path) when is_list(index_path) do
Enum.map_join(index_path, "-", &Integer.to_string/1)
end
defp tree_root_id(component_id), do: "tree:#{component_id}:root"
defp tree_label_id(component_id), do: "tree:#{component_id}:label"
defp tree_tree_id(component_id), do: "tree:#{component_id}:tree"
defp tree_node_id(component_id, value),
do: "tree:#{component_id}:node:#{value}"
defp tree_branch_wrapper_id(component_id, value),
do: "tree:#{component_id}:branch:#{value}"
defp tree_branch_part_id(component_id, part, path_key),
do: "tree:#{component_id}:#{part}:#{path_key}"
@spec props(Props.t()) :: map()
def props(assigns) do
animation = Map.get(assigns, :animation, "js")
animation_options = Map.get(assigns, :animation_options, %Height{})
default_expanded_str =
(assigns.expanded_value || []) |> validate_value!() |> joined_csv_values()
default_selected_str = (assigns.value || []) |> validate_value!() |> joined_csv_values()
base = %{
"id" => assigns.id,
"data-tree" => Corex.Dataset.encode_json(assigns.tree),
"data-animation" => animation,
"data-redirect" => get_boolean(assigns.redirect),
"data-default-expanded-value" => default_expanded_str,
"data-default-selected-value" => default_selected_str,
"data-selection-mode" => assigns.selection_mode,
"data-typeahead" => if(Map.get(assigns, :typeahead, true), do: "true", else: "false"),
"data-dir" => assigns.dir,
"data-on-selection-change" => assigns.on_selection_change,
"data-on-selection-change-client" => Map.get(assigns, :on_selection_change_client),
"data-on-expanded-change" => assigns.on_expanded_change,
"data-on-expanded-change-client" => Map.get(assigns, :on_expanded_change_client)
}
if animation == "js" do
Map.merge(base, Height.to_dataset(animation_options))
else
base
end
end
@spec root(Root.t()) :: map()
def root(assigns) do
%{
"data-scope" => "tree-view",
"data-part" => "root",
"dir" => assigns.dir,
"id" => tree_root_id(assigns.id)
}
end
def ignore_root(assigns) do
JS.ignore_attributes(Root.ignored_attrs(),
to: Selectors.css_id(tree_root_id(assigns.id))
)
end
@spec label(Label.t()) :: map()
def label(assigns) do
%{
"data-scope" => "tree-view",
"data-part" => "label",
"dir" => assigns.dir,
"id" => tree_label_id(assigns.id)
}
end
def ignore_label(assigns) do
JS.ignore_attributes(Label.ignored_attrs(),
to: Selectors.css_id(tree_label_id(assigns.id))
)
end
@spec tree(Props.t()) :: map()
def tree(assigns) do
%{
"data-scope" => "tree-view",
"data-part" => "tree",
"dir" => assigns.dir,
"id" => tree_tree_id(assigns.id)
}
end
def ignore_tree(assigns) do
JS.ignore_attributes(Tree.ignored_attrs(),
to: Selectors.css_id(tree_tree_id(assigns.id))
)
end
@spec item(Item.t()) :: map()
def item(assigns) do
base = %{
"data-scope" => "tree-view",
"data-part" => "item",
"data-value" => assigns.value,
"data-path" => encode_index_path(assigns.index_path),
"data-disabled" => get_boolean(assigns.disabled),
"dir" => assigns.dir,
"id" => tree_node_id(assigns.id, assigns.value),
"style" => depth_style(assigns.index_path)
}
base = if Map.get(assigns, :name), do: Map.put(base, "data-name", assigns.name), else: base
base = if Map.get(assigns, :to), do: Map.put(base, "data-to", assigns.to), else: base
base =
case assigns.redirect do
false ->
Map.put(base, "data-redirect", "false")
mode when mode in [:href, :patch, :navigate] ->
Map.put(base, "data-redirect", Atom.to_string(mode))
_ ->
base
end
base = if assigns.new_tab, do: Map.put(base, "data-new-tab", ""), else: base
base = if Map.get(assigns, :selected), do: Map.put(base, "data-selected", ""), else: base
if Map.get(assigns, :focused), do: Map.put(base, "data-focus", ""), else: base
end
def ignore_item(assigns) do
JS.ignore_attributes(Item.ignored_attrs(),
to: Selectors.css_id(tree_node_id(assigns.id, assigns.value))
)
end
@spec branch(Branch.t()) :: map()
def branch(assigns) do
state = if assigns.expanded, do: "open", else: "closed"
base = %{
"data-scope" => "tree-view",
"data-part" => "branch",
"data-value" => assigns.value,
"data-path" => encode_index_path(assigns.index_path),
"data-state" => state,
"data-disabled" => get_boolean(assigns.disabled),
"dir" => assigns.dir,
"id" => tree_branch_wrapper_id(assigns.id, assigns.value)
}
base = if Map.get(assigns, :name), do: Map.put(base, "data-name", assigns.name), else: base
base = if Map.get(assigns, :selected), do: Map.put(base, "data-selected", ""), else: base
if Map.get(assigns, :focused), do: Map.put(base, "data-focus", ""), else: base
end
def ignore_branch(assigns) do
JS.ignore_attributes(Branch.ignored_attrs(),
to: Selectors.css_id(tree_branch_wrapper_id(assigns.id, assigns.value))
)
end
@spec branch_trigger(Branch.t()) :: map()
def branch_trigger(assigns) do
state = if assigns.expanded, do: "open", else: "closed"
base = %{
"data-scope" => "tree-view",
"data-part" => "branch-control",
"data-value" => assigns.value,
"data-path" => encode_index_path(assigns.index_path),
"data-state" => state,
"data-disabled" => get_boolean(assigns.disabled),
"dir" => assigns.dir,
"style" => depth_style(assigns.index_path),
"id" => tree_node_id(assigns.id, assigns.value)
}
base = if Map.get(assigns, :selected), do: Map.put(base, "data-selected", ""), else: base
if Map.get(assigns, :focused), do: Map.put(base, "data-focus", ""), else: base
end
def ignore_branch_trigger(assigns) do
JS.ignore_attributes(BranchControl.ignored_attrs(),
to: Selectors.css_id(tree_node_id(assigns.id, assigns.value))
)
end
@spec branch_content(Branch.t()) :: map()
def branch_content(assigns) do
state = if assigns.expanded, do: "open", else: "closed"
pk = path_key(assigns.index_path)
animation = Map.get(assigns, :animation, "instant")
base = %{
"data-scope" => "tree-view",
"data-part" => "branch-content",
"data-value" => assigns.value,
"data-path" => encode_index_path(assigns.index_path),
"data-state" => state,
"dir" => assigns.dir,
"id" => tree_branch_part_id(assigns.id, "branch-content", pk)
}
cond do
assigns.expanded ->
base
animation == "instant" ->
Map.put(base, "hidden", "")
true ->
base
end
end
def ignore_branch_content(assigns) do
pk = path_key(assigns.index_path)
JS.ignore_attributes(BranchContent.ignored_attrs(),
to: Selectors.css_id(tree_branch_part_id(assigns.id, "branch-content", pk))
)
end
@spec branch_indicator(Branch.t()) :: map()
def branch_indicator(assigns) do
state = if assigns.expanded, do: "open", else: "closed"
pk = path_key(assigns.index_path)
%{
"data-scope" => "tree-view",
"data-part" => "branch-indicator",
"data-value" => assigns.value,
"data-path" => encode_index_path(assigns.index_path),
"data-state" => state,
"data-disabled" => get_boolean(assigns.disabled),
"dir" => assigns.dir,
"id" => tree_branch_part_id(assigns.id, "branch-indicator", pk)
}
end
def ignore_branch_indicator(assigns) do
pk = path_key(assigns.index_path)
JS.ignore_attributes(BranchIndicator.ignored_attrs(),
to: Selectors.css_id(tree_branch_part_id(assigns.id, "branch-indicator", pk))
)
end
@spec branch_text(Branch.t()) :: map()
def branch_text(assigns) do
pk = path_key(assigns.index_path)
%{
"data-scope" => "tree-view",
"data-part" => "branch-text",
"data-value" => assigns.value,
"data-path" => encode_index_path(assigns.index_path),
"dir" => assigns.dir,
"id" => tree_branch_part_id(assigns.id, "branch-text", pk)
}
end
def ignore_branch_text(assigns) do
pk = path_key(assigns.index_path)
JS.ignore_attributes(BranchText.ignored_attrs(),
to: Selectors.css_id(tree_branch_part_id(assigns.id, "branch-text", pk))
)
end
@spec item_text(Item.t()) :: map()
def item_text(assigns) do
pk = path_key(assigns.index_path)
%{
"data-scope" => "tree-view",
"data-part" => "item-text",
"data-value" => assigns.value,
"data-path" => encode_index_path(assigns.index_path),
"id" => tree_branch_part_id(assigns.id, "item-text", pk)
}
end
def ignore_item_text(assigns) do
pk = path_key(assigns.index_path)
JS.ignore_attributes(ItemText.ignored_attrs(),
to: Selectors.css_id(tree_branch_part_id(assigns.id, "item-text", pk))
)
end
@spec item_indicator(Item.t()) :: map()
def item_indicator(assigns) do
pk = path_key(assigns.index_path)
base = %{
"data-scope" => "tree-view",
"data-part" => "item-indicator",
"data-value" => assigns.value,
"data-path" => encode_index_path(assigns.index_path),
"data-disabled" => get_boolean(assigns.disabled),
"dir" => assigns.dir,
"id" => tree_branch_part_id(assigns.id, "item-indicator", pk),
"aria-hidden" => "true"
}
base = if Map.get(assigns, :selected), do: Map.put(base, "data-selected", ""), else: base
base = if Map.get(assigns, :focused), do: Map.put(base, "data-focus", ""), else: base
if Map.get(assigns, :selected) do
base
else
Map.put(base, "hidden", true)
end
end
def ignore_item_indicator(assigns) do
pk = path_key(assigns.index_path)
JS.ignore_attributes(ItemIndicator.ignored_attrs(),
to: Selectors.css_id(tree_branch_part_id(assigns.id, "item-indicator", pk))
)
end
@spec branch_indent_guide(Branch.t()) :: map()
def branch_indent_guide(assigns) do
pk = path_key(assigns.index_path)
%{
"data-scope" => "tree-view",
"data-part" => "branch-indent-guide",
"data-value" => assigns.value,
"data-path" => encode_index_path(assigns.index_path),
"dir" => assigns.dir,
"id" => tree_branch_part_id(assigns.id, "branch-indent-guide", pk)
}
end
def ignore_branch_indent_guide(assigns) do
pk = path_key(assigns.index_path)
JS.ignore_attributes(BranchIndentGuide.ignored_attrs(),
to: Selectors.css_id(tree_branch_part_id(assigns.id, "branch-indent-guide", pk))
)
end
defp encode_index_path(nil), do: nil
defp encode_index_path([]), do: nil
defp encode_index_path(path) when is_list(path), do: Enum.join(path, "/")
end