Thaw UI Documentation

repository·main·Indexed 20 days ago

https://github.com/thaw-ui/thaw

A Leptos-based component library inspired by Fluent Design for web applications. Thaw UI supports Client-Side Rendering (CSR) and Server-Side Rendering (SSR), providing components such as Button, Input, Popover, and Calendar. It requires the ConfigProvider component at the root of the component tree and offers localization support via LocaleConfig.

Tokens
81.4K
Snippets
298
Records
361
Agent score
69%

What's inside Thaw UI

  1. Customize Toast content with slots

    main

    The Toast component supports several slots for rich content:

    • ToastTitle: The main heading. It supports ToastTitleMedia (for icons/spinners) and ToastTitleAction (for buttons/links) via slots.
    • ToastTitleMedia: A slot within ToastTitle for visual elements like a Spinner.
    • ToastBody: The main message area. It supports a ToastBodySubtitle slot for secondary text.
    • ToastFooter: An area at the bottom of the toast for actions like links or buttons.
    <Toast>
        <ToastTitle>
            "Loading"
            <ToastTitleMedia slot>
                <Spinner size=SpinnerSize::Tiny/>
            </ToastTitleMedia>
        </ToastTitle>
        <ToastBody>
            "Uploading file..."
            <ToastBodySubtitle slot>
                "Please wait while we process your request"
            </ToastBodySubtitle>
        </ToastBody>
        <ToastFooter>
            <Link>Cancel</Link>
        </ToastFooter>
    </Toast>
  2. Implement custom parsing and formatting in SpinButton

    main

    If you need to display values in a specific format (e.g., using commas as decimal separators) or parse non-standard string inputs, use the parser and format props.

    • format: A callback that takes the underlying value T and returns a String to be displayed in the input field.
    • parser: A callback that takes a String from the user input and returns Option<T>. This allows you to intercept and transform the raw string before it updates the component's value.

    Note: The parser must return Option<T> to handle invalid inputs gracefully.

    let value = RwSignal::new(0.0);
    
    // Example: Customizing decimal separator to a comma
    let format = move |v: f64| {
        // ... logic to format v as a string with commas ...
        format!("{},{:0<2}", sign, int, dec)
    };
    
    let parser = move |v: String| {
        // ... logic to parse string with commas back into f64 ...
        format!("{:0<1}.{:0<2}", int_part, dec_part).parse::<f64>().ok()
    };
    
    view! {
        <SpinButton<f64> value=value parser=parser format=format step_page=1.0 />
    }
  3. Configure Popover placement and appearance

    main

    You can customize where the popover appears relative to its trigger and how it looks using the position and appearance props on the Popover component.

    Placement

    Use the position prop with PopoverPosition variants to align the popover. Available positions include:

    • PopoverPosition::Top, PopoverPosition::TopStart, PopoverPosition::TopEnd
    • PopoverPosition::Bottom, PopoverPosition::BottomStart, PopoverPosition::BottomEnd
    • PopoverPosition::Left, PopoverPosition::LeftStart, PopoverPosition::LeftEnd
    • PopoverPosition::Right, PopoverPosition::RightStart, PopoverPosition::RightEnd

    Appearance

    Use the appearance prop with PopoverAppearance to change the visual style:

    • PopoverAppearance::Brand: Applies brand-specific styling.
    • PopoverAppearance::Inverted: Applies an inverted color scheme.
    • Default: If not specified, the default theme style is used.
    // Example of custom placement and appearance
    view! {
        <Popover position=PopoverPosition::BottomStart appearance=PopoverAppearance::Brand>
            <PopoverTrigger slot>
                <Button>"Trigger"</Button>
            </PopoverTrigger>
            "Content"
        </Popover>
    }
  4. Implement validation rules with Field and FieldContextProvider

    main

    To manage validation across multiple fields in a form, wrap your fields in a FieldContextProvider. This allows you to trigger validation for the entire group of fields.

    Each input component (like Input, Select, Slider, etc.) accepts a rules prop containing a vector of specific rule types (e.g., InputRule, SelectRule). You can also provide custom validation logic using a .validator() method which returns a Result<(), FieldValidationState::Error>.

    To trigger validation manually (for example, on a submit button click), use FieldContextInjection::expect_context() to access the context and call .validate().

    view! {
        <form>
            <FieldContextProvider>
                <Field label="Username" name="username">
                    <Input rules=vec![InputRule::required(true.into())]/>
                </Field>
                
                <Field label="SpinButton" name="spinbutton">
                    <SpinButton
                        step_page=1
                        rules=vec![SpinButtonRule::validator(move |v, _| {
                            if v % 2 == 0 {
                                Err(FieldValidationState::Error("It has to be odd!".to_string()))
                            } else {
                                Ok(())
                            }
                        })]
                    />
                </Field>
    
                <Button
                    button_type=ButtonType::Submit
                    on_click={ 
                        let field_context = FieldContextInjection::expect_context();
                        move |e: ev::MouseEvent| {
                            if !field_context.validate() {
                                e.prevent_default();
                            }
                        }
                    }
                >
                    "Submit"
                </Button>
            </FieldContextProvider>
        </form>
    }
  5. Configure internationalization with LocaleConfig

    main

    Components like Calendar and DatePicker support localization via a LocaleConfig. You can initialize a LocaleConfig using one of the provided built-in locales or by implementing the LocaleExt trait for a custom struct.

    Using built-in locales

    Import thaw::locales to access predefined locale constants.

    Creating a custom locale

    Implement the LocaleExt trait for your own struct to define custom behavior for locale() and today().

    // Using a built-in locale
    use thaw::locales;
    let locale = RwSignal::new(LocaleConfig::from(locales::EnUS));
    
    // Creating a custom locale
    use thaw::locales::LocaleExt;
    pub struct MyStruct;
    impl LocaleExt for MyStruct {
        fn locale(&self) -> &Locale { todo!() }
        fn today(&self) -> &'static str { todo!() }
    }
    let locale = RwSignal::new(LocaleConfig::from(MyStruct));
  6. Configure AutoComplete size and disabled state

    main

    You can control the visual size of the AutoComplete component using the size prop with AutoCompleteSize variants, and disable user interaction using the disabled prop.

    // Disabled state
    view! {
        <AutoComplete placeholder="Email" disabled=true/>
    }
    
    // Size variants
    view! {
        <Flex vertical=true inline=true>
            <AutoComplete size=AutoCompleteSize::Small/>
            <AutoComplete />
            <AutoComplete size=AutoCompleteSize::Large/>
        </Flex>
    }
  7. Display Avatar with Name, Image, or Initials

    main

    You can customize the content of an Avatar using the following props:

    • Name: Pass a string to the name prop to represent the entity.
    • Image: Pass a URL string to the src prop to display an image.
    • Initials: Use the initials prop to display custom text (e.g., initials) when no image is provided.
    // Using a name
    view! { <Avatar name="Ashley McCarthy" /> }
    
    // Using an image source
    view! { <Avatar src="https://s3.bmp.ovh/imgs/2021/10/723d457d627fe706.jpg" /> }
  8. Implement dismissible Tags with callbacks

    main

    A Tag can be made dismissible by setting the dismissible prop to true. You can handle the removal logic by providing a callback to the on_dismiss prop, which receives a MouseEvent.

    let toaster = ToasterInjection::expect_context();
    
    let on_dismiss = move |_| {
        toaster.dispatch_toast(move || view! {
            <Toast>
                <ToastTitle>"Tag"</ToastTitle>
                <ToastBody>
                    "Tag dismiss"
                </ToastBody>
             }, Default::default());
    };
    
    view! {
        <Tag dismissible=true on_dismiss=on_dismiss>"Default"</Tag>
    }
  9. Configure Input size, disabled, and autofocus states

    main

    The Input component supports several state and appearance props:

    • size: Controls font size and spacing via InputSize (Small, Medium, Large).
    • disabled: Disables the input.
    • autofocus: Automatically focuses the input on mount.
    • placeholder: Displays hint text when empty.
    // Size example
    view! {
        <Flex vertical=true inline=true>
            <Input size=InputSize::Small placeholder="Small input"/>
            <Input placeholder="Medium input"/>
            <Input size=InputSize::Large placeholder="Large input"/>
        </Flex>
    }
    
    // Disabled example
    view! {
        <Input value disabled=true/>
    }
    
    // Autofocus example
    view! {
        <Input autofocus=true/>
    }
  10. Build a tree structure with Tree, TreeItem, and TreeItemLayout

    main

    To create a hierarchical tree structure, compose <Tree> components with <TreeItem> and <TreeItemLayout>.

    • <Tree>: The root container for the tree.
    • <TreeItem>: Represents an individual node. Use the item_type prop to specify if it is a TreeItemType::Branch (can contain children) or TreeItemType::Leaf (cannot contain children).
    • <Tree> (nested): To create nesting, place a new <Tree> component inside a <TreeItem> that is marked as a branch.
    • <TreeItemLayout>: Used inside a <TreeItem> to define the visual content/label of that item.
    view! {
        <Tree>
            <TreeItem item_type=TreeItemType::Branch>
                <TreeItemLayout>"level 1, item 1"</TreeItemLayout>
                <Tree>
                    <TreeItem item_type=TreeItemType::Leaf>
                        <TreeItemLayout>"level 2, item 1"</TreeItemLayout>
                    </TreeItem>
                </Tree>
            </TreeItem>
            <TreeItem item_type=TreeItemType::Leaf>
                <TreeItemLayout>"level 1, item 2"</TreeItemLayout>
            </TreeItem>
        </Tree>
    }