Keyboard shortcuts

Press or to navigate between chapters

Press ? to show this help

Press Esc to hide this help

Containers & Overlays

Components for organizing content, managing layers, and showing floating UI.


Controlled dialog overlay with backdrop, escape-to-dismiss, and click-outside handling:

#![allow(unused)]
fn main() {
use kael::modal;

modal("confirm-dialog", self.is_open)
    .label("Confirm action")
    .backdrop(hsla(0.0, 0.0, 0.0, 0.5))
    .dismiss_on_escape(true)
    .dismiss_on_click_outside(true)
    .render_with({
        let entity = entity.clone();
        move |state, _window, _cx| {
            div()
                .w(px(400.0))
                .p_6()
                .bg(rgb(0xffffff))
                .rounded(px(12.0))
                .shadow_xl()
                .flex().flex_col().gap_4()
                .child(div().text_lg().child("Are you sure?"))
                .child(div().child("This action cannot be undone."))
                .child(
                    div().flex().justify_end().gap_2()
                        .child(button("cancel").label("Cancel")
                            .on_click({
                                let entity = entity.clone();
                                move |_, _, cx| {
                                    entity.update(cx, |this, cx| {
                                        this.is_open = false;
                                        cx.notify();
                                    });
                                }
                            }))
                        .child(button("confirm").label("Confirm")
                            .on_click({
                                let entity = entity.clone();
                                move |_, _, cx| {
                                    entity.update(cx, |this, cx| {
                                        this.do_action();
                                        this.is_open = false;
                                        cx.notify();
                                    });
                                }
                            }))
                )
                .into_any_element()
        }
    })
    .on_change({
        let entity = entity.clone();
        move |open, _window, cx| {
            entity.update(cx, |this, cx| {
                this.is_open = *open;
                cx.notify();
            });
        }
    })
}

ModalRenderState fields: open, label, focused


Popover

Anchored floating panel with positioning:

#![allow(unused)]
fn main() {
use kael::popover;

popover("color-picker")
    .anchor(|_window, _cx| {
        button("show-colors").label("Colors").into_any_element()
    })
    .popup(|_window, _cx| {
        div()
            .w(px(200.0))
            .p_3()
            .bg(rgb(0xffffff))
            .shadow_lg()
            .rounded(px(8.0))
            .child("Color picker content")
            .into_any_element()
    })
    .dismiss_on_escape(true)
    .dismiss_on_click_outside(true)
}

Tabs

Tabbed content switcher with keyboard navigation:

#![allow(unused)]
fn main() {
use kael::tabs;

#[derive(Clone, Copy, PartialEq, Eq)]
enum EditorTab { Code, Preview, Settings }

tabs("editor-tabs", self.active_tab, [
    TabItem::new(EditorTab::Code, "Code", |_w, _cx| {
        div().child("Code editor here").into_any_element()
    }),
    TabItem::new(EditorTab::Preview, "Preview", |_w, _cx| {
        div().child("Live preview").into_any_element()
    }),
    TabItem::new(EditorTab::Settings, "Settings", |_w, _cx| {
        div().child("Editor settings").into_any_element()
    }),
])
.on_change({
    let entity = entity.clone();
    move |tab, _window, cx| {
        entity.update(cx, |this, cx| {
            this.active_tab = *tab;
            cx.notify();
        });
    }
})
}

TabRenderState fields: value, label, index, tab_count, selected, focused


Disclosure

Collapsible section (accordion):

#![allow(unused)]
fn main() {
use kael::disclosure;

disclosure("advanced-settings", self.expanded)
    .trigger(|_w, _cx| {
        div().child("Advanced Settings ▾").into_any_element()
    })
    .panel(|_w, _cx| {
        div().p_3().child("Hidden content here").into_any_element()
    })
    .on_change({
        let entity = entity.clone();
        move |open, _window, cx| {
            entity.update(cx, |this, cx| {
                this.expanded = *open;
                cx.notify();
            });
        }
    })
}

Splitter

Draggable pane divider for resizable layouts:

#![allow(unused)]
fn main() {
use kael::splitter;

splitter("main-split", self.split_ratio)
    .on_change({
        let entity = entity.clone();
        move |ratio, _window, cx| {
            entity.update(cx, |this, cx| {
                this.split_ratio = *ratio;
                cx.notify();
            });
        }
    })
}

Use the ratio value to size adjacent panes:

#![allow(unused)]
fn main() {
let left_width = self.split_ratio * total_width;
div().flex().flex_row()
    .child(div().w(px(left_width)).child("Left pane"))
    .child(splitter("split", self.split_ratio).on_change(/* ... */))
    .child(div().flex_1().child("Right pane"))
}

Context Menu

Right-click menus via .context_menu() on any Div:

#![allow(unused)]
fn main() {
div()
    .id("file-item")
    .child("document.txt")
    .context_menu(|menu| {
        menu.item("Open", |_w, cx| { /* handle open */ })
            .item("Rename", |_w, cx| { /* handle rename */ })
            .separator()
            .item("Delete", |_w, cx| { /* handle delete */ })
    })
}

Tooltip

Hover information via .tooltip() on any Div:

#![allow(unused)]
fn main() {
div()
    .id("save-icon")
    .child(icon("save"))
    .tooltip("Save file (Cmd+S)")

// Custom tooltip content
div()
    .id("status")
    .child("●")
    .tooltip_element(|| {
        div()
            .p_2()
            .bg(rgb(0x1E1E1E))
            .text_color(rgb(0xffffff))
            .rounded(px(4.0))
            .child("Connected to server")
    })
}

Layer

Managed layer system for in-window modals and popovers:

#![allow(unused)]
fn main() {
use kael::layer;

layer("notification-layer")
    .placement(LayerPlacement::Centered)
    .child(/* floating content */)
}