using Spry; using Astralis; using Inversion; using Invercargill.DataStructures; /** * TodoListDemo - A todo list demo for the Outlets documentation * * Demonstrates: * - Using spry-outlet for dynamic child components * - set_outlet_children() to populate outlets * - Parent/child component composition */ public class TodoListDemo : Component { private TodoDemoStore store = inject(); private ComponentFactory factory = inject(); private HttpContext http_context = inject(); public override string markup { get { return """

Todo List

"""; }} public override async void prepare() throws Error { // Update count this["count"].text_content = @"$(store.items.length) items"; // Build list items var children = new Series(); foreach (var item in store.items) { var component = factory.create(); component.item_id = item.id; children.add(component); } set_outlet_children("items-outlet", children); } public async override void handle_action(string action) throws Error { if (action == "Add") { var title = http_context.request.query_params.get_any_or_default("title"); if (title != null && title.strip().length > 0) { store.add(title.strip()); } } } } /** * TodoItemDemo - Individual todo item component */ public class TodoItemDemo : Component { private TodoDemoStore store = inject(); private HttpContext http_context = inject(); public int item_id { set; get; } public override string markup { get { return """
  • """; }} public override async void prepare() throws Error { var item = store.get_by_id(item_id); if (item == null) return; this["title"].text_content = item.title; this["item"].set_attribute("hx-vals", @"{\"id\":$item_id}"); if (item.completed) { this["item"].add_class("completed"); this["toggle"].text_content = "↩"; } else { this["toggle"].text_content = "✓"; } } public async override void handle_action(string action) throws Error { var id_str = http_context.request.query_params.get_any_or_default("id"); var id = int.parse(id_str); switch (action) { case "Toggle": store.toggle(id); break; case "Delete": store.remove(id); break; } } } /** * TodoDemoStore - Singleton store for todo items */ public class TodoDemoStore : Object { public Series items { get; set; default = new Series(); } private int _next_id = 1; public TodoDemoStore() { // Add some initial items add("Learn Spry components"); add("Build a web app"); add("Deploy to production"); } public void add(string title) { items.add(new TodoDemoItem(_next_id++, title)); } public TodoDemoItem? get_by_id(int id) { foreach (var item in items) { if (item.id == id) return item; } return null; } public void toggle(int id) { var item = get_by_id(id); if (item != null) { item.completed = !item.completed; } } public void remove(int id) { // Build a new list without the item var new_items = new Series(); foreach (var item in items) { if (item.id != id) { new_items.add(item); } } items = new_items; } } /** * TodoDemoItem - Single todo item data */ public class TodoDemoItem : Object { public int id { get; construct; } public string title { get; construct set; } public bool completed { get; set; default = false; } public TodoDemoItem(int id, string title) { Object(id: id, title: title); } }