refactor: Router zusammenfuehren und offenen Workspace im Context fuehren
Die App hatte zwei Routable-Enums, zwischen denen ein Signal<bool> umschaltete.
Der Schalter wurde nie gesetzt - beim ersten Umlegen waere aufgefallen, dass
sich beide Router dieselbe History teilen und beide "/" belegen: Wer im
Selector auf "Open Workspace" klickt, steht auf /ws_open, und der
Editor-Router kennt nur / und /editor.
Jetzt ein Enum, der Editor-Teil unter #[nest("/workspace")]. An die Stelle des
bool tritt Signal<Option<Workspace>> in einem Context-Provider - Props
scheiden aus, weil sich durch ein Outlet keine durchreichen lassen. Fehlt der
Workspace, schickt ein Guard im EditorShell-Layout zurueck zum Selector; der
Fall tritt real bei Hot-Reload auf, wo der Context faellt und die Route bleibt.
Der Selector verliert seine Reiter und wird eine einzige Route. Das folgt aus
dem UI-Prototyp in Issue #8: Oeffnen ist der Alltagsfall, Hinzufuegen passiert
pro Workspace einmal - zwei gleichrangige Reiter verlangten bei jedem Start
eine Entscheidung, die fast immer dieselbe ist. Damit entfallen der leere
Stub create_new_workspace.rs, die Tab-Konstante fuer den Selector und die
zugehoerigen CSS-Klassen.
Weil EnumIter nicht mehr gebraucht wird, faellt strum_macros als direkte
Abhaengigkeit weg; strum re-exportiert die Derives bereits.
Entscheidungen dazu in den Issues #5, #6 und #8.
This commit is contained in:
@@ -1,32 +1,24 @@
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_code::Theme;
|
||||
use dioxus_code_editor::{CodeEditor, Language};
|
||||
use strum::EnumMessage;
|
||||
|
||||
use crate::components::custom_link::custom_link_component::CustomLink;
|
||||
use strum::{EnumMessage, IntoEnumIterator};
|
||||
use strum_macros::EnumIter;
|
||||
use crate::route::Route;
|
||||
use crate::workspace::Workspace;
|
||||
|
||||
const HEADER_SVG: Asset = asset!("/assets/header.svg");
|
||||
|
||||
#[derive(Debug, Clone, Routable, PartialEq, EnumIter, EnumMessage)]
|
||||
#[rustfmt::skip]
|
||||
pub enum EditorRoute {
|
||||
#[layout(Navbar)]
|
||||
#[route("/")]
|
||||
#[strum(message = "Home")]
|
||||
Home {},
|
||||
#[route("/editor")]
|
||||
#[strum(message = "Editor")]
|
||||
Editor {},
|
||||
}
|
||||
|
||||
/// Home page
|
||||
#[component]
|
||||
fn Home() -> Element {
|
||||
pub fn Home() -> Element {
|
||||
rsx! {
|
||||
Hero {}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Unveränderter Dioxus-Vorlagentext. Ersetzen, sobald klar ist, was
|
||||
// beim Öffnen eines Workspace angezeigt wird, bevor eine Notiz gewählt wurde.
|
||||
#[component]
|
||||
pub fn Hero() -> Element {
|
||||
rsx! {
|
||||
@@ -46,7 +38,7 @@ pub fn Hero() -> Element {
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn Editor() -> Element {
|
||||
pub fn Editor() -> Element {
|
||||
let mut source = use_signal(String::new);
|
||||
|
||||
rsx! {
|
||||
@@ -60,15 +52,34 @@ fn Editor() -> Element {
|
||||
}
|
||||
}
|
||||
|
||||
/// Shared navbar component.
|
||||
/// Die Editor-Hülle: Datei-Leiste links, Editorfläche rechts.
|
||||
///
|
||||
/// Trägt zugleich den Zustands-Guard — ohne offenen Workspace hat der Editor
|
||||
/// nichts anzuzeigen. Der Fall ist real: Hot-Reload setzt den Context zurück,
|
||||
/// während die Route bestehen bleibt.
|
||||
#[component]
|
||||
fn Navbar() -> Element {
|
||||
pub fn EditorShell() -> Element {
|
||||
let workspace = use_context::<Signal<Option<Workspace>>>();
|
||||
let navigator = use_navigator();
|
||||
|
||||
use_effect(move || {
|
||||
if workspace.read().is_none() {
|
||||
navigator.replace(Route::Selector {});
|
||||
}
|
||||
});
|
||||
|
||||
if workspace.read().is_none() {
|
||||
return rsx! {};
|
||||
}
|
||||
|
||||
rsx! {
|
||||
div {
|
||||
class: "layout-container",
|
||||
div {
|
||||
class: "files-bar",
|
||||
for entry in EditorRoute::iter() {
|
||||
// TODO: Durch den Notiz-Baum des offenen Workspace ersetzen
|
||||
// (rekursiv eingelesen, aufklappbar). Bis dahin die Routen.
|
||||
for entry in Route::EDITOR_TABS {
|
||||
CustomLink {
|
||||
key: "{entry}",
|
||||
label: entry.get_message().unwrap_or_default(),
|
||||
@@ -79,8 +90,8 @@ fn Navbar() -> Element {
|
||||
}
|
||||
div {
|
||||
class: "editor-container",
|
||||
Outlet::<EditorRoute> { }
|
||||
Outlet::<Route> { }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
/// Create new Workspace Layout
|
||||
// TODO: Platzhalter — Formular zum Anlegen eines Workspace (Name, Pfad) und
|
||||
// Verdrahtung als Ziel von WorkspaceRoute::NewWorkspace fehlen noch.
|
||||
#[component]
|
||||
fn NewWorkspaceLayout() -> Element {
|
||||
rsx! {
|
||||
div {}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +1 @@
|
||||
pub mod selector;
|
||||
pub mod create_new_workspace;
|
||||
@@ -1,61 +1,26 @@
|
||||
use dioxus::prelude::*;
|
||||
use strum::{EnumMessage, IntoEnumIterator};
|
||||
use strum_macros::EnumIter;
|
||||
use crate::components::custom_link::custom_link_component::CustomLink;
|
||||
|
||||
#[derive(Debug, Clone, Routable, PartialEq, EnumIter, EnumMessage)]
|
||||
#[rustfmt::skip]
|
||||
pub enum WorkspaceRoute {
|
||||
#[layout(Workspace)]
|
||||
#[route("/")]
|
||||
#[strum(message = "Create new Workspace")]
|
||||
NewWorkspace {},
|
||||
#[route("/ws_open")]
|
||||
#[strum(message = "Open Workspace")]
|
||||
OpenWorkspace {},
|
||||
}
|
||||
|
||||
/// Page to create new Workspace
|
||||
/// Der Bildschirm vor dem Editor: einen bekannten Workspace öffnen oder einen
|
||||
/// hinzufügen. Eine einzige Route, keine Reiter — die Workspace-Liste führt,
|
||||
/// das Hinzufügen ist Nebenaktion.
|
||||
#[component]
|
||||
fn NewWorkspace() -> Element {
|
||||
pub fn Selector() -> Element {
|
||||
rsx! {
|
||||
div {
|
||||
"New Workspace",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Page to open available Workspaces
|
||||
#[component]
|
||||
fn OpenWorkspace() -> Element {
|
||||
rsx! {
|
||||
div {
|
||||
"Open Workspace",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Workspace Layout
|
||||
#[component]
|
||||
fn Workspace() -> Element {
|
||||
rsx! {
|
||||
div {
|
||||
class: "workspace-selector-layout",
|
||||
div {
|
||||
class: "workspace-selector-links",
|
||||
for route in WorkspaceRoute::iter() {
|
||||
CustomLink {
|
||||
key: "{route}",
|
||||
label: route.get_message().unwrap_or_default(),
|
||||
href: route.clone(),
|
||||
css: "workspace-selector",
|
||||
div { class: "workspace-selector-layout",
|
||||
div { class: "workspace-selector-view",
|
||||
// TODO: Workspace-Liste aus workspaces.ron laden und darstellen
|
||||
// (Ordnername, Pfad darunter, Statusabzeichen). Bis dahin nur
|
||||
// der leere Zustand.
|
||||
div { class: "empty-state",
|
||||
span { class: "empty-state-title", "Noch kein Workspace" }
|
||||
span { "Der erste Workspace ist ein Ordner deiner Wahl." }
|
||||
// TODO: Nativen Ordner-Dialog anbinden, Marker anlegen und
|
||||
// den Workspace in den Context schreiben.
|
||||
button { class: "btn btn-primary", disabled: true,
|
||||
"Workspace hinzufügen…"
|
||||
}
|
||||
}
|
||||
}
|
||||
div {
|
||||
class: "workspace-selector-view",
|
||||
Outlet::<WorkspaceRoute> { }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user