defmodule AshHqWeb.Components.CodeExample do
use Surface.LiveComponent
prop text, :string, required: true
prop class, :css_class
prop title, :string
prop start_collapsed, :boolean, default: false
prop collapsible, :boolean, default: false
data collapsed, :string, default: false
data code, :string, default: ""
def render(assigns) do
~F"""
{#if @title}
{@title}
{#if @collapsible}
{/if}
{/if}
{#if !@collapsed}
{#for {_line, no} <- @code}
{no}
{/for}
{#else}
{to_string(List.last(@code) |> elem(0))}
{/if}
{#for {line, _no} <- @code}
{/for}
"""
end
def handle_event("fold", _, socket) do
{:noreply, assign(socket, :collapsed, !socket.assigns.collapsed)}
end
def update(assigns, socket) do
if assigns.start_collapsed && assigns.collapsible do
{:ok,
socket
|> assign(assigns)
|> assign(:collapsed, true)
|> assign(:code, to_code(assigns[:text]))}
else
{:ok,
socket
|> assign(assigns)
|> assign(:collapsed, false)
|> assign(:code, to_code(assigns[:text]))}
end
end
defp to_code(text) do
# TODO: do this at compile time
lines =
text
# this is pretty naive, won't handle things like block comments
|> String.split("\n")
|> Enum.reverse()
|> Enum.drop_while(&(&1 in [""]))
|> Enum.reverse()
|> Enum.map(fn
"" ->
"
"
line ->
Makeup.highlight(line)
end)
count = Enum.count(lines)
padding = String.length(to_string(count))
lines
|> Enum.with_index()
|> Enum.map(fn {line, index} ->
{line, String.pad_leading(to_string(index + 1), padding, " ")}
end)
end
end