app

Local-first trade for farms and co-ops
git clone https://radroots.dev/git/app.git
Log | Files | Refs | README | LICENSE

commit 9ba7ed427d539732a18b1e541b6bc448352416ce
parent ead3ccc62a182b566252506552269555be31814a
Author: triesap <tyson@radroots.org>
Date:   Tue, 27 Jan 2026 16:44:30 +0000

ui: normalize app layout and ids

Diffstat:
MCargo.lock | 1+
Mapp/app.css | 46++++++++++++++++++++++++++++++++++++++++------
Mapp/src/app.rs | 308+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Mapp/src/logs.rs | 40++++++++++++++++++++--------------------
Mapp/src/settings.rs | 10++++++----
Mapp/src/ui_demo.rs | 96+++++++++++++++++++++++++++++++++++++++++--------------------------------------
6 files changed, 294 insertions(+), 207 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock @@ -2704,6 +2704,7 @@ name = "ui-primitives-leptos" version = "0.1.0" dependencies = [ "leptos", + "send_wrapper", "ui-primitives-core", "wasm-bindgen", "web-sys", diff --git a/app/app.css b/app/app.css @@ -62,8 +62,10 @@ background: var(--bg-app); color: var(--text-primary); height: 100%; + width: 100%; overscroll-behavior: none; touch-action: manipulation; + overflow: hidden; } body { @@ -71,7 +73,9 @@ margin: 0; background: var(--bg-app); height: 100%; + width: 100%; overscroll-behavior: none; + overflow: hidden; } * { @@ -97,23 +101,53 @@ @layer base { #app-root { min-height: 100dvh; - height: 100%; + height: 100dvh; + width: 100%; + display: flex; + flex-direction: column; + overflow: hidden; + overscroll-behavior: none; } #app-shell { - min-height: 100dvh; + width: 100%; + display: flex; + flex-direction: column; + flex: 1 1 auto; + min-height: 0; + overflow: hidden; + overscroll-behavior: none; + } +} + +@layer components { + .app-page { + position: relative; + width: 100%; display: flex; flex-direction: column; + flex: 1 1 auto; + min-height: 0; + overscroll-behavior: none; + } + + .app-page-fixed { + overflow: hidden; } - [data-app-scroll] { + .app-page-scroll { overflow-y: auto; + overflow-x: hidden; -webkit-overflow-scrolling: touch; - overscroll-behavior: contain; + overscroll-behavior: none; + scrollbar-width: none; + -ms-overflow-style: none; + } + + .app-page-scroll::-webkit-scrollbar { + display: none; } -} -@layer components { .app-view { will-change: transform, opacity; } diff --git a/app/src/app.rs b/app/src/app.rs @@ -183,6 +183,7 @@ fn SplashPage() -> impl IntoView { view! { <main id="app-splash" + class="app-page app-page-fixed" style="min-height:100dvh;background:white;display:flex;align-items:center;justify-content:center;" > </main> @@ -192,8 +193,14 @@ fn SplashPage() -> impl IntoView { #[component] fn LogoCircle() -> impl IntoView { view! { - <div class="relative flex flex-col h-[196px] w-full justify-center items-center"> - <div class="relative flex flex-row h-36 w-36 justify-center items-center bg-ly2 rounded-full"> + <div + id="app-logo-circle" + class="relative flex flex-col h-[196px] w-full justify-center items-center" + > + <div + id="app-logo-mark" + class="relative flex flex-row h-36 w-36 justify-center items-center bg-ly2 rounded-full" + > <p class="font-sans font-[900] text-6xl text-ly0-gl -tracking-[0.4rem] -translate-x-[6px]"> "\u{00BB}`," </p> @@ -296,8 +303,7 @@ fn SetupPage() -> impl IntoView { view! { <main id="app-setup" - data-app-scroll - class="relative min-h-[100dvh] h-[100dvh] w-full flex flex-col" + class="app-page app-page-fixed relative w-full flex flex-col" > {move || match setup_step.get() { RadrootsAppSetupStep::Intro => { @@ -305,38 +311,57 @@ fn SetupPage() -> impl IntoView { view! { <section id="app-setup-intro" - class="app-view app-view-enter relative flex flex-col h-[100dvh] w-full justify-start items-center" + class="app-view app-view-enter relative flex flex-col h-[100dvh] w-full justify-start items-center" > - <div class="flex flex-col h-full w-full justify-start items-center"> - <div class="relative flex flex-col h-full w-full justify-center items-center"> - <div class="flex flex-row w-full justify-start items-center -translate-y-16"> + <div + id="app-setup-intro-body" + class="flex flex-col h-full w-full justify-start items-center" + > + <div + id="app-setup-intro-stage" + class="relative flex flex-col h-full w-full justify-center items-center" + > + <header + id="app-setup-intro-header" + class="flex flex-row w-full justify-start items-center -translate-y-16" + > <button type="button" + id="app-setup-intro-logo-button" class="flex flex-row w-full justify-center items-center" on:click=move |_| navigate_home("/", Default::default()) > <LogoCircle /> </button> - </div> - <div class="absolute bottom-0 left-0 flex flex-col h-[20rem] w-full px-10 gap-2 justify-start items-center"> - <div class="flex flex-row w-full justify-start items-center"> - <p class="font-sans font-[400] text-sm uppercase text-ly0-gl-label"> - "Configure" + </header> + <footer + id="app-setup-intro-footer" + class="absolute bottom-0 left-0 flex flex-col h-[20rem] w-full px-10 gap-2 justify-start items-center" + > + <p + id="app-setup-intro-kicker" + class="w-full text-left font-sans font-[400] text-sm uppercase text-ly0-gl-label" + > + "Configure" + </p> + <div + id="app-setup-intro-copy" + class="flex flex-col w-full gap-2 justify-start items-center" + > + <p + id="app-setup-intro-line-welcome" + class="w-full text-left font-mono font-[400] text-[1.1rem] text-ly0-gl" + > + "Welcome to Radroots!" + </p> + <p + id="app-setup-intro-line-body" + class="w-full text-left font-mono font-[400] text-[1.1rem] text-ly0-gl" + > + "Your device will be configured by the setup wizard." </p> </div> - <div class="flex flex-col w-full gap-2 justify-start items-center"> - <div class="flex flex-row w-full justify-start items-center"> - <p class="font-mono font-[400] text-[1.1rem] text-ly0-gl"> - "Welcome to Radroots!" - </p> - </div> - <div class="flex flex-row w-full justify-start items-center"> - <p class="font-mono font-[400] text-[1.1rem] text-ly0-gl"> - "Your device will be configured by the setup wizard." - </p> - </div> - </div> - </div> + </footer> </div> </div> </section> @@ -348,7 +373,7 @@ fn SetupPage() -> impl IntoView { id="app-setup-key-choice" class="app-view app-view-enter flex flex-col w-full gap-4 px-6 pt-10 pb-16" > - <header class="flex flex-col gap-2"> + <header id="app-setup-key-choice-header" class="flex flex-col gap-2"> <p class="font-sans text-sm uppercase tracking-[0.14em] text-ly0-gl-label"> "Setup" </p> @@ -359,11 +384,16 @@ fn SetupPage() -> impl IntoView { "Select how you want to add your Nostr key." </p> </header> - <RadrootsAppUiListView basis=key_choice_list.clone() /> + <div id="app-setup-key-choice-list" class="w-full"> + <RadrootsAppUiListView basis=key_choice_list.clone() /> + </div> </section> }.into_any(), }} - <div class="z-10 absolute bottom-10 left-0 flex flex-col w-full justify-center items-center"> + <footer + id="app-setup-actions" + class="z-10 absolute bottom-4 left-0 flex flex-col w-full justify-center items-center" + > {move || { let step = setup_step.get(); let continue_action = RadrootsAppUiButtonLayoutAction { @@ -381,11 +411,11 @@ fn SetupPage() -> impl IntoView { view! { <RadrootsAppUiButtonLayoutPair continue_action=continue_action - back=Some(back_action) + back=back_action /> } }} - </div> + </footer> </main> } } @@ -486,84 +516,92 @@ fn HomePage() -> impl IntoView { } }; view! { - <main id="app-home" data-app-scroll> - <div>"app"</div> - <div style="margin-top: 8px; display: flex; align-items: center; gap: 8px;"> - <span - style=move || format!( - "display:inline-block;width:10px;height:10px;border-radius:50%;background:{};", - status_color() - ) - ></span> - <span>{move || init_state.get().stage.as_str()}</span> - </div> - <div style="margin-top: 12px; display: flex; align-items: center; gap: 8px;"> - <button - on:click=move |_| { - let config = backends.with_untracked(|value| value.as_ref().map(|backends| backends.config.clone())); - reset_status.set(Some("resetting".to_string())); - health_report.set(RadrootsAppHealthReport::empty()); - active_key.set(None); - notifications_status.set(None); - setup_required.set(Some(true)); - spawn_local(async move { - let Some(config) = config else { - reset_status.set(Some("reset_missing_backends".to_string())); - return; - }; - let datastore = radroots_app_core::datastore::RadrootsClientWebDatastore::new( - Some(config.datastore.idb_config), - ); - let keystore = radroots_app_core::keystore::RadrootsClientWebKeystoreNostr::new( - Some(config.keystore.nostr_store), - ); - match app_init_reset( - Some(&datastore), - Some(&config.datastore.key_maps), - Some(&keystore), - ) - .await - { - Ok(()) => { - let log_datastore = logs_datastore(); - if let Err(err) = log_datastore.reset().await { - let reset_err = RadrootsAppInitError::Datastore(err); - let _ = app_log_error_emit(&reset_err); - reset_status.set(Some(reset_err.to_string())); - return; + <main id="app-home" class="app-page app-page-scroll"> + <header id="app-home-header"> + <h1 id="app-home-title">"app"</h1> + </header> + <section id="app-home-status" aria-label="Status"> + <div id="app-home-status-row" style="margin-top: 8px; display: flex; align-items: center; gap: 8px;"> + <span + style=move || format!( + "display:inline-block;width:10px;height:10px;border-radius:50%;background:{};", + status_color() + ) + ></span> + <span>{move || init_state.get().stage.as_str()}</span> + </div> + </section> + <section id="app-home-reset" aria-label="Reset"> + <div id="app-home-reset-row" style="margin-top: 12px; display: flex; align-items: center; gap: 8px;"> + <button + on:click=move |_| { + let config = backends.with_untracked(|value| value.as_ref().map(|backends| backends.config.clone())); + reset_status.set(Some("resetting".to_string())); + health_report.set(RadrootsAppHealthReport::empty()); + active_key.set(None); + notifications_status.set(None); + setup_required.set(Some(true)); + spawn_local(async move { + let Some(config) = config else { + reset_status.set(Some("reset_missing_backends".to_string())); + return; + }; + let datastore = radroots_app_core::datastore::RadrootsClientWebDatastore::new( + Some(config.datastore.idb_config), + ); + let keystore = radroots_app_core::keystore::RadrootsClientWebKeystoreNostr::new( + Some(config.keystore.nostr_store), + ); + match app_init_reset( + Some(&datastore), + Some(&config.datastore.key_maps), + Some(&keystore), + ) + .await + { + Ok(()) => { + let log_datastore = logs_datastore(); + if let Err(err) = log_datastore.reset().await { + let reset_err = RadrootsAppInitError::Datastore(err); + let _ = app_log_error_emit(&reset_err); + reset_status.set(Some(reset_err.to_string())); + return; + } + reset_status.set(Some("reset_done".to_string())); + spawn_health_checks( + config, + true, + health_report, + health_running, + active_key, + notifications_status, + ); + } + Err(err) => { + let log_datastore = logs_datastore(); + let _ = app_log_error_store( + &log_datastore, + &config.datastore.key_maps, + &err, + ) + .await; + reset_status.set(Some(err.to_string())); } - reset_status.set(Some("reset_done".to_string())); - spawn_health_checks( - config, - true, - health_report, - health_running, - active_key, - notifications_status, - ); - } - Err(err) => { - let log_datastore = logs_datastore(); - let _ = app_log_error_store( - &log_datastore, - &config.datastore.key_maps, - &err, - ) - .await; - reset_status.set(Some(err.to_string())); } - } - }); - } - disabled=reset_disabled - > - "reset" - </button> - <span>{reset_label}</span> - </div> - <div style="margin-top: 16px;"> - <div style="font-weight: 600;">"notifications"</div> - <div style="margin-top: 8px; display: flex; align-items: center; gap: 8px;"> + }); + } + disabled=reset_disabled + > + "reset" + </button> + <span>{reset_label}</span> + </div> + </section> + <section id="app-home-notifications" aria-label="Notifications" style="margin-top: 16px;"> + <header id="app-home-notifications-header"> + <h2 id="app-home-notifications-title" style="font-weight: 600;">"notifications"</h2> + </header> + <div id="app-home-notifications-actions" style="margin-top: 8px; display: flex; align-items: center; gap: 8px;"> <button on:click=move |_| { let config = backends.with_untracked(|value| value.as_ref().map(|backends| backends.config.clone())); @@ -616,10 +654,12 @@ fn HomePage() -> impl IntoView { </button> <span>{notifications_label}</span> </div> - </div> - <div style="margin-top: 16px;"> - <div style="font-weight: 600;">"health checks"</div> - <div style="margin-top: 8px; display: flex; align-items: center; gap: 8px;"> + </section> + <section id="app-home-health" aria-label="Health checks" style="margin-top: 16px;"> + <header id="app-home-health-header"> + <h2 id="app-home-health-title" style="font-weight: 600;">"health checks"</h2> + </header> + <div id="app-home-health-actions" style="margin-top: 8px; display: flex; align-items: center; gap: 8px;"> <button on:click=move |_| { let config = backends.with_untracked(|value| value.as_ref().map(|backends| backends.config.clone())); @@ -643,8 +683,8 @@ fn HomePage() -> impl IntoView { {move || if health_running.get() { "checking" } else { "run checks" }} </button> </div> - <div style="margin-top: 8px; display: grid; gap: 6px;"> - <div style="display: flex; align-items: center; gap: 8px;"> + <ul id="app-home-health-list" style="margin-top: 8px; display: grid; gap: 6px;"> + <li id="app-home-health-key-maps" style="display: flex; align-items: center; gap: 8px;"> <span style=move || format!( "display:inline-block;width:10px;height:10px;border-radius:50%;background:{};", @@ -653,8 +693,8 @@ fn HomePage() -> impl IntoView { ></span> <span>"key_maps"</span> <span>{move || health_result_label(&health_report.get().key_maps)}</span> - </div> - <div style="display: flex; align-items: center; gap: 8px;"> + </li> + <li id="app-home-health-bootstrap-state" style="display: flex; align-items: center; gap: 8px;"> <span style=move || format!( "display:inline-block;width:10px;height:10px;border-radius:50%;background:{};", @@ -663,8 +703,8 @@ fn HomePage() -> impl IntoView { ></span> <span>"bootstrap_state"</span> <span>{move || health_result_label(&health_report.get().bootstrap_state)}</span> - </div> - <div style="display: flex; align-items: center; gap: 8px;"> + </li> + <li id="app-home-health-active-key-state" style="display: flex; align-items: center; gap: 8px;"> <span style=move || format!( "display:inline-block;width:10px;height:10px;border-radius:50%;background:{};", @@ -673,8 +713,8 @@ fn HomePage() -> impl IntoView { ></span> <span>"state_active_key"</span> <span>{move || health_result_label(&health_report.get().state_active_key)}</span> - </div> - <div style="display: flex; align-items: center; gap: 8px;"> + </li> + <li id="app-home-health-notifications" style="display: flex; align-items: center; gap: 8px;"> <span style=move || format!( "display:inline-block;width:10px;height:10px;border-radius:50%;background:{};", @@ -683,8 +723,8 @@ fn HomePage() -> impl IntoView { ></span> <span>"notifications"</span> <span>{move || health_result_label(&health_report.get().notifications)}</span> - </div> - <div style="display: flex; align-items: center; gap: 8px;"> + </li> + <li id="app-home-health-tangle" style="display: flex; align-items: center; gap: 8px;"> <span style=move || format!( "display:inline-block;width:10px;height:10px;border-radius:50%;background:{};", @@ -693,8 +733,8 @@ fn HomePage() -> impl IntoView { ></span> <span>"tangle"</span> <span>{move || health_result_label(&health_report.get().tangle)}</span> - </div> - <div style="display: flex; align-items: center; gap: 8px;"> + </li> + <li id="app-home-health-datastore" style="display: flex; align-items: center; gap: 8px;"> <span style=move || format!( "display:inline-block;width:10px;height:10px;border-radius:50%;background:{};", @@ -703,8 +743,8 @@ fn HomePage() -> impl IntoView { ></span> <span>"datastore_roundtrip"</span> <span>{move || health_result_label(&health_report.get().datastore_roundtrip)}</span> - </div> - <div style="display: flex; align-items: center; gap: 8px;"> + </li> + <li id="app-home-health-keystore" style="display: flex; align-items: center; gap: 8px;"> <span style=move || format!( "display:inline-block;width:10px;height:10px;border-radius:50%;background:{};", @@ -713,13 +753,13 @@ fn HomePage() -> impl IntoView { ></span> <span>"keystore"</span> <span>{move || health_result_label(&health_report.get().keystore)}</span> - </div> - <div style="display: flex; align-items: center; gap: 8px;"> + </li> + <li id="app-home-health-active-key" style="display: flex; align-items: center; gap: 8px;"> <span>"active_key"</span> <span>{move || active_key_label(active_key.get())}</span> - </div> - </div> - </div> + </li> + </ul> + </section> </main> } } @@ -860,7 +900,13 @@ fn AppShell() -> impl IntoView { <A href="/settings">"settings"</A> <A href="/setup">"setup"</A> </nav> - <Routes fallback=|| view! { <div>"not_found"</div> }> + <Routes + fallback=|| view! { + <main id="app-not-found" class="app-page app-page-fixed"> + <p id="app-not-found-label">"not_found"</p> + </main> + } + > <Route path=path!("") view=HomePage /> <Route path=path!("logs") view=RadrootsAppLogsPage /> <Route path=path!("ui") view=RadrootsAppUiDemoPage /> diff --git a/app/src/logs.rs b/app/src/logs.rs @@ -571,17 +571,17 @@ pub fn RadrootsAppLogsPage() -> impl IntoView { total == 0 || page_index.get() + 1 >= total }; view! { - <main> - <div style="display:flex;align-items:center;gap:12px;"> - <div style="font-size:18px;font-weight:600;">"logs"</div> + <main id="app-logs" class="app-page app-page-scroll"> + <header id="app-logs-header" style="display:flex;align-items:center;gap:12px;"> + <h1 id="app-logs-title" style="font-size:18px;font-weight:600;">"logs"</h1> <button on:click=move |_| refresh()>"refresh"</button> <button on:click=move |_| clear()>"clear"</button> <button on:click=move |_| copy_dump() disabled=dump_action_disabled>"copy dump"</button> <button on:click=move |_| download_dump() disabled=dump_action_disabled>"download dump"</button> - <div style="font-size:12px;color:#6b7280;">{status_label}</div> - <div style="font-size:12px;color:#6b7280;">{dump_action_label}</div> - </div> - <div style="margin-top:12px;display:flex;flex-wrap:wrap;gap:12px;align-items:center;"> + <div id="app-logs-status" style="font-size:12px;color:#6b7280;">{status_label}</div> + <div id="app-logs-dump-status" style="font-size:12px;color:#6b7280;">{dump_action_label}</div> + </header> + <section id="app-logs-filters" aria-label="Filters" style="margin-top:12px;display:flex;flex-wrap:wrap;gap:12px;align-items:center;"> <input type="text" placeholder="search code/message/context" @@ -637,7 +637,7 @@ pub fn RadrootsAppLogsPage() -> impl IntoView { <option value="100">"100"</option> <option value="250">"250"</option> </select> - <div style="font-size:12px;color:#6b7280;"> + <div id="app-logs-filter-summary" style="font-size:12px;color:#6b7280;"> {move || { let total = entries.get().len(); let visible = filtered_entries.get().len(); @@ -647,8 +647,8 @@ pub fn RadrootsAppLogsPage() -> impl IntoView { format!("showing {visible} of {total} (limit {limit}) page {page}/{pages}") }} </div> - </div> - <div style="margin-top:8px;display:flex;align-items:center;gap:8px;"> + </section> + <section id="app-logs-pagination" aria-label="Pagination" style="margin-top:8px;display:flex;align-items:center;gap:8px;"> <button on:click=move |_| { let next = page_index.get().saturating_sub(1); @@ -673,12 +673,12 @@ pub fn RadrootsAppLogsPage() -> impl IntoView { <button on:click=move |_| copy_support() disabled=support_disabled> "copy instructions" </button> - <div style="font-size:12px;color:#6b7280;">{support_label}</div> - </div> - <div style="margin-top:12px;display:flex;flex-wrap:wrap;gap:16px;"> - <section style="flex:1 1 520px;min-width:280px;"> - <div style="font-weight:600;font-size:14px;">"entries"</div> - <div style="margin-top:8px;border:1px solid #e5e7eb;border-radius:8px;height:60vh;overflow:auto;padding:10px;display:flex;flex-direction:column;gap:10px;"> + <div id="app-logs-support-status" style="font-size:12px;color:#6b7280;">{support_label}</div> + </section> + <section id="app-logs-content" aria-label="Log content" style="margin-top:12px;display:flex;flex-wrap:wrap;gap:16px;"> + <section id="app-logs-entries" style="flex:1 1 520px;min-width:280px;"> + <h2 id="app-logs-entries-title" style="font-weight:600;font-size:14px;">"entries"</h2> + <div id="app-logs-entries-list" style="margin-top:8px;border:1px solid #e5e7eb;border-radius:8px;height:60vh;overflow:auto;padding:10px;display:flex;flex-direction:column;gap:10px;"> <For each=move || paged_entries.get() key=|entry| entry.id.clone() @@ -722,21 +722,21 @@ pub fn RadrootsAppLogsPage() -> impl IntoView { /> </div> </section> - <section style="flex:1 1 320px;min-width:260px;"> - <div style="font-weight:600;font-size:14px;">"dump (jsonl)"</div> + <section id="app-logs-dump" style="flex:1 1 320px;min-width:260px;"> + <h2 id="app-logs-dump-title" style="font-weight:600;font-size:14px;">"dump (jsonl)"</h2> <textarea readonly prop:value=move || dump_text.get() style="margin-top:8px;width:100%;height:60vh;border:1px solid #e5e7eb;border-radius:8px;padding:8px;font-size:12px;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace;" ></textarea> - <div style="margin-top:12px;font-weight:600;font-size:14px;">"support instructions"</div> + <h3 id="app-logs-support-title" style="margin-top:12px;font-weight:600;font-size:14px;">"support instructions"</h3> <textarea readonly prop:value=move || support_instructions_text() style="margin-top:8px;width:100%;height:140px;border:1px solid #e5e7eb;border-radius:8px;padding:8px;font-size:12px;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace;" ></textarea> </section> - </div> + </section> </main> } } diff --git a/app/src/settings.rs b/app/src/settings.rs @@ -185,12 +185,14 @@ pub fn RadrootsAppSettingsPage() -> impl IntoView { styles: None, }; view! { - <main style="padding: 16px;"> - <div style="font: var(--type-title2); margin-bottom: 12px;">"settings"</div> - <div style="display:flex;flex-direction:column;gap:16px;"> + <main id="app-settings" class="app-page app-page-scroll" style="padding: 16px;"> + <header id="app-settings-header" style="font: var(--type-title2); margin-bottom: 12px;"> + <h1 id="app-settings-title">"settings"</h1> + </header> + <section id="app-settings-content" style="display:flex;flex-direction:column;gap:16px;"> <RadrootsAppUiListView basis=appearance_list /> <RadrootsAppUiListView basis=actions_list /> - </div> + </section> </main> } } diff --git a/app/src/ui_demo.rs b/app/src/ui_demo.rs @@ -181,62 +181,66 @@ pub fn RadrootsAppUiDemoPage() -> impl IntoView { }), }; view! { - <main style="padding: 16px;"> - <div style="font: var(--type-title2); margin-bottom: 12px;">"UI Demo"</div> - <RadrootsAppUiListView basis=list /> + <main id="app-ui-demo" class="app-page app-page-scroll" style="padding: 16px;"> + <header id="app-ui-demo-header" style="font: var(--type-title2); margin-bottom: 12px;"> + <h1 id="app-ui-demo-title">"UI Demo"</h1> + </header> + <section id="app-ui-demo-content"> + <RadrootsAppUiListView basis=list /> - <RadrootsAppUiSheetRoot - open=Some(sheet_open_read) - default_open=false - modal=None - on_open_change=Some(sheet_open_set) - > - <RadrootsAppUiSheetTrigger - disabled=false - class=Some("ui-card".to_string()) - id=None - style=Some("padding:12px 16px; width: 100%; text-align: left;".to_string()) + <RadrootsAppUiSheetRoot + open=Some(sheet_open_read) + default_open=false + modal=None + on_open_change=Some(sheet_open_set) > - "Open Sheet" - </RadrootsAppUiSheetTrigger> - <RadrootsAppUiSheetPortal> - <RadrootsAppUiSheetOverlay - close_on_click=None - class=None + <RadrootsAppUiSheetTrigger + disabled=false + class=Some("ui-card".to_string()) id=None - style=None - /> - <RadrootsAppUiSheetContent - disable_outside_pointer_events=false - show_handle=true - class=None - id=None - style=None + style=Some("padding:12px 16px; width: 100%; text-align: left;".to_string()) > - <RadrootsAppUiSheetTitle + "Open Sheet" + </RadrootsAppUiSheetTrigger> + <RadrootsAppUiSheetPortal> + <RadrootsAppUiSheetOverlay + close_on_click=None class=None id=None style=None - > - "Sheet Preview" - </RadrootsAppUiSheetTitle> - <RadrootsAppUiSheetDescription + /> + <RadrootsAppUiSheetContent + disable_outside_pointer_events=false + show_handle=true class=None id=None - style=Some("margin-top: 6px;".to_string()) - > - "This is a placeholder sheet for iOS styling." - </RadrootsAppUiSheetDescription> - <RadrootsAppUiSheetClose - class=Some("ui-card".to_string()) - id=None - style=Some("margin-top: 16px; padding: 10px 14px;".to_string()) + style=None > - "Close" - </RadrootsAppUiSheetClose> - </RadrootsAppUiSheetContent> - </RadrootsAppUiSheetPortal> - </RadrootsAppUiSheetRoot> + <RadrootsAppUiSheetTitle + class=None + id=None + style=None + > + "Sheet Preview" + </RadrootsAppUiSheetTitle> + <RadrootsAppUiSheetDescription + class=None + id=None + style=Some("margin-top: 6px;".to_string()) + > + "This is a placeholder sheet for iOS styling." + </RadrootsAppUiSheetDescription> + <RadrootsAppUiSheetClose + class=Some("ui-card".to_string()) + id=None + style=Some("margin-top: 16px; padding: 10px 14px;".to_string()) + > + "Close" + </RadrootsAppUiSheetClose> + </RadrootsAppUiSheetContent> + </RadrootsAppUiSheetPortal> + </RadrootsAppUiSheetRoot> + </section> </main> } }