# Event Handling SUI provides a declarative event binding system with state management and component communication. ## Event Binding ### Basic Events Use `s:on-` to bind events: ```html
...
``` ### Common Events | Attribute | Event | Description | | ----------------- | ---------- | ------------------ | | `s:on-click` | click | Mouse click | | `s:on-dblclick` | dblclick | Double click | | `s:on-input` | input | Input value change | | `s:on-change` | change | Value changed | | `s:on-submit` | submit | Form submission | | `s:on-focus` | focus | Element focused | | `s:on-blur` | blur | Element lost focus | | `s:on-keydown` | keydown | Key pressed | | `s:on-keyup` | keyup | Key released | | `s:on-mouseenter` | mouseenter | Mouse entered | | `s:on-mouseleave` | mouseleave | Mouse left | ### Multiple Events ```html ``` ## Passing Data ### Data Attributes Use `s:data-*` to pass string data: ```html ``` ### JSON Data Use `s:json-*` to pass complex data: ```html ``` ## Event Handlers ### Handler Signature ```typescript function Page(component: HTMLElement) { this.root = component; this.handleClick = (event: Event, data: any, context: EventContext) => { // event - The DOM event // data - Combined data from s:data-* and s:json-* // context - Event context with element references }; } ``` ### EventContext ```typescript interface EventContext { rootElement: HTMLElement; // Component root element targetElement: HTMLElement; // Element that triggered the event } ``` ### Example ```html
{{ item.name }}
``` ```typescript function ItemList(component: HTMLElement) { this.root = component; this.deleteItem = async (event: Event, data: any, context: EventContext) => { const id = data.id; // String from s:data-id const item = data.item; // Object from s:json-item if (confirm(`Delete ${item.name}?`)) { await this.backend.ApiDeleteItem(id); context.targetElement.closest(".item").remove(); } }; } ``` ## State Management ### State Object ```typescript function Counter(component: HTMLElement) { this.root = component; this.state = new __sui_state(this); // Initial state this.state.Set("count", 0); } ``` ### State Watchers React to state changes with watchers: ```typescript function Counter(component: HTMLElement) { this.root = component; this.state = new __sui_state(this); // Define watchers this.watch = { count: (value: number, state: State) => { this.root.querySelector(".count").textContent = value; }, items: (value: any[], state: State) => { this.renderItems(value); }, }; this.increment = () => { const count = this.state.Get("count") || 0; this.state.Set("count", count + 1); // Triggers watcher }; } ``` ### Stop Propagation Prevent state changes from bubbling to parent: ```typescript this.watch = { localState: (value: any, state: State) => { // Handle locally this.updateUI(value); // Stop propagation to parent components state.stopPropagation(); }, }; ``` ## Store (Data Attributes) Store manages `data-*` attributes on the component: ### Basic Usage ```typescript function Card(component: HTMLElement) { this.root = component; this.store = new __sui_store(component); // Get/Set string values const id = this.store.Get("id"); this.store.Set("id", "123"); // Get/Set JSON values const items = this.store.GetJSON("items"); this.store.SetJSON("items", [{ id: 1 }, { id: 2 }]); } ``` ### Component Data Get data from BeforeRender: ```typescript // Backend returns: { user: { name: "John" }, settings: {...} } const data = this.store.GetData(); console.log(data.user.name); // "John" ``` ## Custom Events ### Emit Events ```typescript function ItemCard(component: HTMLElement) { this.root = component; this.selectItem = () => { const item = this.store.GetJSON("item"); // Emit custom event this.emit("item:selected", { item }); }; } ``` ### Listen to Events ```typescript function ItemList(component: HTMLElement) { this.root = component; // Listen to child events this.root.addEventListener("item:selected", (e: CustomEvent) => { const { item } = e.detail; console.log("Selected:", item); }); } ``` ### State Change Events Parent components can listen to state changes: ```typescript function Parent(component: HTMLElement) { this.root = component; this.root.addEventListener("state:change", (e: CustomEvent) => { const { key, value, target } = e.detail; console.log(`State ${key} changed to ${value} in`, target); }); } ``` ## Form Handling ### Form Submit ```html
``` ```typescript function LoginForm(component: HTMLElement) { this.root = component; this.handleSubmit = async (event: Event) => { event.preventDefault(); const form = event.target as HTMLFormElement; const formData = new FormData(form); const email = formData.get("email"); const password = formData.get("password"); try { await this.backend.ApiLogin(email, password); window.location.href = "/dashboard"; } catch (error) { alert("Login failed"); } }; } ``` ### Input Binding ```html ``` ```typescript function Form(component: HTMLElement) { this.root = component; this.formData = {}; this.handleInput = (event: Event, data: any) => { const input = event.target as HTMLInputElement; this.formData[data.field] = input.value; }; } ``` ## Keyboard Events ```html ``` ```typescript function Search(component: HTMLElement) { this.root = component; this.handleKeydown = (event: KeyboardEvent) => { if (event.key === "Enter") { this.search(); } if (event.key === "Escape") { this.clear(); } }; } ``` ## Complete Example ```html
``` ```typescript function TodoApp(component: HTMLElement) { this.root = component; this.state = new __sui_state(this); this.store = new __sui_store(component); this.watch = { todos: (todos: any[]) => { this.render("todoList", { todos }); }, }; this.addTodo = async (event: Event) => { event.preventDefault(); const form = event.target as HTMLFormElement; const input = form.querySelector("input") as HTMLInputElement; if (input.value.trim()) { const todo = await this.backend.ApiAddTodo(input.value); const todos = this.state.Get("todos") || []; this.state.Set("todos", [...todos, todo]); input.value = ""; } }; this.toggleTodo = async (event: Event, data: any) => { const checkbox = event.target as HTMLInputElement; await this.backend.ApiToggleTodo(data.id, checkbox.checked); }; this.deleteTodo = async (event: Event, data: any) => { await this.backend.ApiDeleteTodo(data.id); const todos = this.state.Get("todos").filter((t) => t.id !== data.id); this.state.Set("todos", todos); }; } ```