defmodule Examples.SvelteTodoApp do @moduledoc """ A todo application demonstrating advanced Svelte-style patterns. Features: - Reactive stores - Two-way data binding - Derived state - Array operations - Conditional rendering Usage: terminal = Raxol.Terminal.new() app = Examples.SvelteTodoApp.mount(terminal) """ use Raxol.Svelte.Component, optimize: :compile_time use Raxol.Svelte.Reactive # State state(:todos, []) state(:new_todo_text, "") # :all, :active, :completed state(:filter, :all) state(:editing_id, nil) # Reactive derived state reactive :total_count do length(@todos) end reactive :active_count do @todos |> Enum.count(fn todo -> !todo.completed end) end reactive :completed_count do @todos |> Enum.count(fn todo -> todo.completed end) end reactive :filtered_todos do case @filter do :all -> @todos :active -> Enum.filter(@todos, fn todo -> !todo.completed end) :completed -> Enum.filter(@todos, fn todo -> todo.completed end) end end reactive :all_completed do @total_count > 0 && @active_count == 0 end # Reactive statements reactive_block do # Auto-save to localStorage-equivalent when todos change reactive_stmt( case @total_count > 0 do true -> save_todos(@todos) false -> nil end ) # Status updates reactive_stmt( status = cond do @total_count == 0 -> "No todos yet" @active_count == 0 -> "All done! 🎉" @active_count == 1 -> "1 item left" true -> "#{@active_count} items left" end ) end # Event handlers def add_todo do text = String.trim(get_state(:new_todo_text)) case text do "" -> nil _ -> new_todo = %{ id: :crypto.strong_rand_bytes(8) |> Base.encode64(), text: text, completed: false, created_at: DateTime.utc_now() } update_state(:todos, fn todos -> [new_todo | todos] end) set_state(:new_todo_text, "") end end def toggle_todo(id) do update_state(:todos, fn todos -> Enum.map(todos, fn todo -> case todo.id == id do true -> %{todo | completed: !todo.completed} false -> todo end end) end) end def remove_todo(id) do update_state(:todos, fn todos -> Enum.reject(todos, fn todo -> todo.id == id end) end) end def edit_todo(id) do set_state(:editing_id, id) end def save_todo(id, new_text) do text = String.trim(new_text) case text do "" -> remove_todo(id) _ -> update_state(:todos, fn todos -> Enum.map(todos, fn todo -> case todo.id == id do true -> %{todo | text: text} false -> todo end end) end) end set_state(:editing_id, nil) end def cancel_edit do set_state(:editing_id, nil) end def set_filter(new_filter) do set_state(:filter, new_filter) end def toggle_all do all_completed = get_state(:all_completed) update_state(:todos, fn todos -> Enum.map(todos, fn todo -> %{todo | completed: !all_completed} end) end) end def clear_completed do update_state(:todos, fn todos -> Enum.reject(todos, fn todo -> todo.completed end) end) end # Template helpers defp filter_button_style(current_filter, button_filter) do case current_filter == button_filter do true -> "selected" false -> "normal" end end # Persistence defp save_todos(todos) do # In a real app, this would save to persistent storage IO.puts("Saving #{length(todos)} todos...") end # Render function def render(assigns) do ~H""" Todo App {status} set_state(:new_todo_text, text) end} on_enter={&add_todo/0} flex={1} /> {#if @total_count > 0} Total: {@total_count} Active: {@active_count} Done: {@completed_count} {/if} {#if @total_count > 0} Show: {#if @completed_count > 0} {/if} {/if} {#each @filtered_todos as todo} {#if @editing_id == todo.id} save_todo(todo.id, text) end} on_cancel={&cancel_edit/0} /> {:else} toggle_todo(todo.id) end} on_remove={fn -> remove_todo(todo.id) end} on_edit={fn -> edit_todo(todo.id) end} /> {/if} {/each} {#if length(@filtered_todos) == 0 && @total_count > 0} No {Atom.to_string(@filter)} todos {/if} {#if @total_count == 0} Add your first todo above {/if} """ end end # Helper components defmodule Examples.SvelteTodoApp.TodoItem do use Raxol.Svelte.Component def render(%{ todo: todo, on_toggle: on_toggle, on_remove: on_remove, on_edit: on_edit }) do ~H""" {todo.text} """ end end defmodule Examples.SvelteTodoApp.EditableTodoItem do use Raxol.Svelte.Component use Raxol.Svelte.Reactive state(:edit_text, "") def init_with_todo(todo) do set_state(:edit_text, todo.text) end def save(on_save) do on_save.(get_state(:edit_text)) end def cancel(on_cancel) do on_cancel.() end def render(%{todo: todo, on_save: on_save, on_cancel: on_cancel}) do ~H""" set_state(:edit_text, text) end} on_enter={fn -> save(on_save) end} on_escape={fn -> cancel(on_cancel) end} focus flex={1} /> """ end end