# Components In SUI, **every page is a component**. Any page can be embedded into another page using the `is` attribute. ## Core Concept When a page is used as a component: 1. The page's HTML becomes the component template 2. The page's CSS is automatically scoped 3. The page's TypeScript becomes the component class 4. The page's `backend.ts` provides server-side logic via `BeforeRender` ## Creating a Component A component is just a page with a single root element: **`/card/card.html`**: ```html

{{ title }}

``` **`/card/card.css`**: ```css .card { border: 1px solid #ddd; border-radius: 8px; padding: 16px; } .card h3 { margin: 0 0 12px; } ``` **`/card/card.ts`**: ```typescript import { Component } from "@yao/sui"; const self = this as Component; // self.root - Root element // self.store - Data store // self.props - Props from attributes ``` ## Using Components ### Basic Usage Use the `is` attribute to embed a page as a component: ```html

Card content goes here

``` ### With Import Alias Use `` for cleaner syntax: ```html

Content

``` ## Props Props are passed as attributes: ```html
``` Access props in the component script: ```typescript import { Component } from "@yao/sui"; const self = this as Component; // Get single prop const name = self.props.Get("name"); // Get all props const allProps = self.props.List(); // { name: "John", email: "john@example.com", avatar: "...", role: "admin" } ``` Access props in backend script: ```typescript function BeforeRender( request: Request, props: Record ): Record { const userId = props.userId; return { user: Process("models.user.Find", userId), }; } ``` ## Children and Slots ### Children Use `` to render child content: **Component (`/panel/panel.html`)**: ```html
{{ title }}
``` **Usage**: ```html

This content appears in the panel body

``` ### Named Slots Use `` for multiple content areas: **Component (`/modal/modal.html`)**: ```html ``` **Usage**: ```html

Confirmation

Are you sure you want to proceed?

``` ## Dynamic Components ### Variable Component Route ```html
``` ### Dynamic Tag ```html ``` ## Component Script ### Structure ```typescript import { $Backend, Component, EventData } from "@yao/sui"; const self = this as Component; // self.root - Root element (HTMLElement) // self.store - Data store (data-* attributes) // self.props - Props (passed attributes) // self.state - State management // State watchers self.watch = { propertyName: (value: any, state: any) => { // React to state changes }, }; // Event handlers (bound to s:on-click="HandleClick") self.HandleClick = async (event: Event, data: EventData) => { const result = await $Backend().Call("Method", data.id); // Handle result }; ``` ### Store API ```typescript import { Component } from "@yao/sui"; const self = this as Component; // String data self.store.Get("key"); self.store.Set("key", "value"); // JSON data self.store.GetJSON("items"); self.store.SetJSON("items", [{ id: 1 }]); // Component data (from BeforeRender) self.store.GetData(); ``` ### Props API ```typescript // Get single prop const value = self.props.Get("propName"); // Get all props const props = self.props.List(); ``` ### State API ```typescript // Set state (triggers watchers) self.state.Set("count", 10); // Watch state changes self.watch = { count: (value: number, state: any) => { self.root.querySelector(".count")!.textContent = String(value); // state.stopPropagation(); // Prevent bubbling to parent }, }; ``` ## Nested Components Components can include other components: ```html
``` ## Component Backend Script **`/user-card/user-card.backend.ts`**: ```typescript function BeforeRender( request: Request, props: Record ): Record { const userId = props.userId; return { user: Process("models.user.Find", userId), permissions: Process("scripts.auth.GetPermissions", userId), }; } function ApiUpdateUser(userId: string, data: any, request: Request): any { return Process("models.user.Save", userId, data); } ``` ## CSS Scoping Component CSS is automatically scoped using namespace attributes: **Original CSS**: ```css .card { border: 1px solid #ddd; } .card h3 { color: #333; } ``` **Compiled CSS** (scoped): ```css [s:ns="ns_abc123"] .card { border: 1px solid #ddd; } [s:ns="ns_abc123"] .card h3 { color: #333; } ``` ## Important Notes 1. **Single Root Element**: Components must have exactly one root element 2. **Scoped Styles**: CSS is automatically scoped to prevent conflicts 3. **Recursive Prevention**: SUI detects and prevents recursive component inclusion 4. **Component Pattern**: Use `const self = this as Component` to access component APIs