dioxus-compose 가이드

스크롤과 오버레이

스크롤 컨테이너, 클립, 층

무엇이 스크롤되고 무엇이 잘리는지는 overflow 가, 무엇이 페이지 위에 놓이는지는 위치 지정이 정합니다. Host 는 스크롤 컨테이너의 내용 전체를 한 번 배치하고, 스크롤은 렌더러가 합니다. Rust 가 스크롤 위치를 가지면 늘 한 프레임 늦기 때문입니다.

스크롤 컨테이너

어떤 축에 overflow: auto 나 scroll 이 있는 상자는 그 축으로 스크롤됩니다. 그리기 목록의 항목에는 스크롤 컨테이너가 붙습니다. 들여다보는 뷰포트, 안에 든 것 전체의 크기, 스크롤되는 축입니다. 다른 축의 overflow: hidden 은 스크롤 없이 자르기만 합니다. 안에 있는 상자는 모두 가장 가까운 스크롤 조상을 기록하므로, 렌더러는 스크롤 위치마다 무엇이 같이 움직이는지 압니다.

samples/chat/src/lib.rs
.chat { display: flex; flex-direction: column; height: 100%; }

.messages {
    flex: 1;
    min-height: 0;
    overflow-x: hidden;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 12px;
}

.composer { display: flex; gap: 8px; padding: 12px; border-top: 1px solid #e2e8f0; }

samples/chat 에서 메시지 목록은 입력 줄 위의 공간을 차지하고 세로로만 스크롤됩니다. 브라우저에서처럼 min-height: 0 이 중요합니다. 없으면 flex 항목이 내용보다 작아지지 않아서 목록이 입력 줄을 아래로 밀어냅니다.

samples/table 은 반대 방향으로 스크롤됩니다. 표의 최소 폭이 640px 이므로, 480px 페이지에서는 432px 폭의 감싼 상자가 640px 전체를 옆으로 스크롤합니다.

samples/table/src/lib.rs
.table-wrap {
    overflow-x: auto;
    overflow-y: hidden;
    border-radius: 8px;
}

.invoices { width: 100%; min-width: 640px; border-collapse: collapse; }

길어지는 대화

어시스턴트의 답은 조각조각 도착합니다. samples/chat 은 대화를 컨텍스트로 제공한 시그널에 두고, Chat::receive 가 조각마다 마지막 메시지에 덧붙입니다. 바뀌는 것은 그 메시지의 항목 하나, 그 글자와 크기뿐입니다. 목록의 다른 것은 다시 보내지 않습니다.

samples/chat/src/lib.rs, 줄임
pub fn receive(mut self, chunk: &str) {
    let mut messages = self.messages.write();
    if let Some(last) = messages
        .last_mut()
        .filter(|last| last.from == Sender::Assistant)
    {
        last.text.push_str(chunk);
        return;
    }
    // A piece that arrives after the user has spoken starts a new reply.
}

테스트는 "Sure" 에 ", where to?" 를 덧붙이고, 레이아웃 diff 에 바뀐 항목이 답 하나뿐이고 삭제도 순서 변경도 없음을 확인합니다.

클리핑

overflow 가 visible 이 아닌 상자는 자손을 자기 패딩 상자와 둥근 모서리로 자릅니다. 자손의 항목에는 조상이 거는 클립이 기록되고, 그 클립 밖을 누르면 자손에 닿지 않습니다. samples/overlay 에서 240 x 80 미리보기 안의 400 x 160 배너는 미리보기 크기로 잘리고, 미리보기 밖에서 배너가 있었을 자리를 눌러도 배너에 닿지 않습니다.

samples/overlay/src/lib.rs
.preview {
    width: 240px;
    height: 80px;
    overflow: hidden;
    border-radius: 8px;
}
.preview-banner { width: 400px; height: 160px; background-color: #6366f1; }

메뉴와 대화 상자

오버레이는 보통의 HTML 입니다. if 로 보이고, 위치 지정으로 놓고, z-index 로 쌓습니다. 그리는 순서와 적중 판정이 모두 쌓임을 따르므로, 배경을 누르면 그 아래 버튼이 아니라 배경에 닿습니다.

samples/overlay/src/lib.rs, 줄임
if show_dialog {
    div { id: "backdrop", class: "backdrop", onclick: move |_| dialog_open.set(false) }
    div { id: "dialog", class: "dialog", role: "dialog",
        h2 { id: "dialog-title", "Delete project?" }
        p { "This cannot be undone." }
        div { class: "actions",
            button { id: "cancel", onclick: move |_| dialog_open.set(false), "Cancel" }
            button {
                id: "confirm",
                class: "danger",
                onclick: move |_| {
                    deleted.set(true);
                    dialog_open.set(false);
                },
                "Delete"
            }
        }
    }
}
samples/overlay/src/lib.rs
.backdrop {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    z-index: 100;
    background-color: #0f172a;
    opacity: 0.5;
}

position: fixed 상자는 문서 어디에 있든 뷰포트를 기준으로 놓입니다. 그래서 배경은 창 전체를 덮고, 대화 상자는 그 가운데에 놓입니다.

아직 없는 것