feat: Routing fuer Workspace-Auswahl und Editor aufbauen
Zwei Router: WorkspaceRoute (Workspace anlegen/oeffnen) und EditorRoute (Home/Editor), umgeschaltet ueber ein Signal in App. Die Navigationslinks werden nicht mehr einzeln ausgeschrieben, sondern aus dem Routen-Enum erzeugt (strum EnumIter). Die Beschriftung haengt per #[strum(message = "...")] an der Variante selbst, damit eine neue Route nur an einer Stelle gepflegt werden muss. CustomLink kapselt Link und markiert den aktiven Eintrag ueber active_class statt ueber eigenen Klick-Zustand - das funktioniert auch bei Deep-Links und dem Zurueck-Button.
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
use dioxus::prelude::*;
|
||||
use crate::components::custom_link::custom_link_props::CustomLinkProps;
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub fn CustomLink(props: CustomLinkProps) -> Element {
|
||||
rsx! {
|
||||
Link {
|
||||
class: "{props.css}",
|
||||
to: props.href,
|
||||
active_class: "active",
|
||||
{ props.label },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
#[derive(Props, Clone, PartialEq)]
|
||||
pub struct CustomLinkProps {
|
||||
#[props(into)]
|
||||
pub label: String,
|
||||
#[props(into)]
|
||||
pub href: NavigationTarget,
|
||||
#[props(into)]
|
||||
pub css: String,
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
pub mod custom_link_component;
|
||||
pub mod custom_link_props;
|
||||
@@ -0,0 +1 @@
|
||||
pub mod custom_link;
|
||||
|
||||
43
src/main.rs
Normal file
43
src/main.rs
Normal file
@@ -0,0 +1,43 @@
|
||||
mod views;
|
||||
mod components;
|
||||
|
||||
use dioxus::prelude::*;
|
||||
use crate::views::editor::dionotes_editor::EditorRoute;
|
||||
use crate::views::workspace_selector::selector::WorkspaceRoute;
|
||||
|
||||
const FAVICON: Asset = asset!("/assets/favicon.ico");
|
||||
|
||||
// Linked individually rather than via @import in a single entry file: asset!()
|
||||
// only emits files it is given by name, so an @import chain would 404 at runtime.
|
||||
// Order matters — tokens, then baseline, then layout, then components.
|
||||
const TAILWIND_CSS: Asset = asset!("/assets/tailwind.css");
|
||||
const TOKENS_CSS: Asset = asset!("/assets/css/tokens.css");
|
||||
const BASE_CSS: Asset = asset!("/assets/css/base.css");
|
||||
const LAYOUT_CSS: Asset = asset!("/assets/css/layout.css");
|
||||
const COMPONENTS_CSS: Asset = asset!("/assets/css/components.css");
|
||||
|
||||
fn main() {
|
||||
launch(App);
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn App() -> Element {
|
||||
let workspace_selected: Signal<bool> = use_signal(|| false);
|
||||
rsx! {
|
||||
document::Link { rel: "icon", href: FAVICON }
|
||||
// Tailwind first: its preflight lives in @layer base, our unlayered
|
||||
// rules win the cascade regardless, but this keeps source order honest.
|
||||
document::Link { rel: "stylesheet", href: TAILWIND_CSS }
|
||||
document::Link { rel: "stylesheet", href: TOKENS_CSS }
|
||||
document::Link { rel: "stylesheet", href: BASE_CSS }
|
||||
document::Link { rel: "stylesheet", href: LAYOUT_CSS }
|
||||
document::Link { rel: "stylesheet", href: COMPONENTS_CSS }
|
||||
if workspace_selected() {
|
||||
Router::<EditorRoute> {}
|
||||
} else {
|
||||
Router::<WorkspaceRoute> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_code::Theme;
|
||||
use dioxus_code_editor::{CodeEditor, Language};
|
||||
use crate::components::custom_link::custom_link_component::CustomLink;
|
||||
use strum::{EnumMessage, IntoEnumIterator};
|
||||
use strum_macros::EnumIter;
|
||||
|
||||
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 {
|
||||
rsx! {
|
||||
Hero {}
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
pub fn Hero() -> Element {
|
||||
rsx! {
|
||||
div {
|
||||
id: "hero",
|
||||
img { src: HEADER_SVG, id: "header" }
|
||||
div { id: "links",
|
||||
a { href: "https://dioxuslabs.com/learn/0.7/", "📚 Learn Dioxus" }
|
||||
a { href: "https://dioxuslabs.com/awesome", "🚀 Awesome Dioxus" }
|
||||
a { href: "https://github.com/dioxus-community/", "📡 Community Libraries" }
|
||||
a { href: "https://github.com/DioxusLabs/sdk", "⚙️ Dioxus Development Kit" }
|
||||
a { href: "https://marketplace.visualstudio.com/items?itemName=DioxusLabs.dioxus", "💫 VSCode Extension" }
|
||||
a { href: "https://discord.gg/XgGxMSkvUM", "👋 Community Discord" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn Editor() -> Element {
|
||||
let mut source = use_signal(String::new);
|
||||
|
||||
rsx! {
|
||||
CodeEditor {
|
||||
class: "editor-class",
|
||||
value: source(),
|
||||
language: Language::Markdown,
|
||||
theme: Theme::CATPPUCCIN_MOCHA,
|
||||
oninput: move |value| source.set(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Shared navbar component.
|
||||
#[component]
|
||||
fn Navbar() -> Element {
|
||||
rsx! {
|
||||
div {
|
||||
class: "layout-container",
|
||||
div {
|
||||
class: "files-bar",
|
||||
for entry in EditorRoute::iter() {
|
||||
CustomLink {
|
||||
key: "{entry}",
|
||||
label: entry.get_message().unwrap_or_default(),
|
||||
href: entry.clone(),
|
||||
css: "file-selector"
|
||||
}
|
||||
}
|
||||
}
|
||||
div {
|
||||
class: "editor-container",
|
||||
Outlet::<EditorRoute> { }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1
src/views/editor/mod.rs
Normal file
1
src/views/editor/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod dionotes_editor;
|
||||
2
src/views/mod.rs
Normal file
2
src/views/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
pub mod editor;
|
||||
pub mod workspace_selector;
|
||||
@@ -0,0 +1,11 @@
|
||||
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 {}
|
||||
}
|
||||
}
|
||||
2
src/views/workspace_selector/mod.rs
Normal file
2
src/views/workspace_selector/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
pub mod selector;
|
||||
pub mod create_new_workspace;
|
||||
@@ -0,0 +1,61 @@
|
||||
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
|
||||
#[component]
|
||||
fn NewWorkspace() -> 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-view",
|
||||
Outlet::<WorkspaceRoute> { }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user