gpui-router 0.3.0

A router for GPUI App.
Documentation
use gpui::prelude::*;
use gpui::{App, Application, Context, Window, WindowOptions, div, rgb, white};
use gpui_router::{IntoLayout, NavLink, Outlet, Route, Routes, init as router_init, use_params};

struct NestedRouter {}

impl Render for NestedRouter {
  fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
    div()
      .flex()
      .flex_col()
      .gap_2()
      .size_full()
      .p_2()
      .bg(rgb(0x2e7d32))
      .text_color(white())
      .child(div().text_xl().child("Nested Router Example"))
      .child(
        Routes::new().child(
          Route::new()
            .layout(Nav::new())
            .child(Route::new().index().element(|_, _| home()))
            .child(Route::new().path("about").element(|_, _| about()))
            .child(
              Route::new()
                .path("user")
                .layout(UserLayout::new())
                .child(Route::new().index().element(|_, _| user_list()))
                .child(Route::new().path("{id}").element(|_, _| User {})),
            )
            .child(Route::new().path("{*not_match}").element(|_, _| not_match())),
        ),
      )
  }
}

#[derive(IntoElement, IntoLayout)]
pub struct Nav {
  outlet: Outlet,
}

impl Nav {
  pub fn new() -> Self {
    Self { outlet: Outlet::new() }
  }
}

impl RenderOnce for Nav {
  fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
    div()
      .child(
        div()
          .flex()
          .gap_4()
          .text_lg()
          .child(NavLink::new().to("/").child(div().child("Home")))
          .child(NavLink::new().to("/about").child(div().child("About")))
          .child(NavLink::new().to("/user").child(div().child("User")))
          .child(NavLink::new().to("/user/1").child(div().child("User1")))
          .child(NavLink::new().to("/nothing-here").child(div().child("Not Match"))),
      )
      .child(self.outlet)
  }
}

#[derive(IntoElement, IntoLayout)]
pub struct UserLayout {
  outlet: Outlet,
}

impl UserLayout {
  pub fn new() -> Self {
    Self { outlet: Outlet::new() }
  }
}

impl RenderOnce for UserLayout {
  fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
    div()
      .child(div().flex().gap_4().text_lg().child("User Layout"))
      .child(self.outlet)
  }
}

fn home() -> impl IntoElement {
  div().child("Home")
}

fn about() -> impl IntoElement {
  div().child("About")
}

fn user_list() -> impl IntoElement {
  div()
    .flex()
    .flex_col()
    .gap_2()
    .child(NavLink::new().to("/user/1").child(div().child("User1")))
    .child(NavLink::new().to("/user/2").child(div().child("User2")))
    .child(NavLink::new().to("/user/3").child(div().child("User3")))
}

#[derive(IntoElement)]
pub struct User {}

impl RenderOnce for User {
  fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
    let params = use_params(cx);
    div().child(format!("User: {}", params.get("id").unwrap()))
  }
}

fn not_match() -> impl IntoElement {
  div().id("not_match").child(div().child("Nothing to see here!")).child(
    NavLink::new()
      .to("/")
      .child(div().text_decoration_1().child("Go to the home page")),
  )
}

fn main() {
  Application::new().run(|cx: &mut App| {
    router_init(cx);

    cx.on_window_closed(|cx| {
      if cx.windows().is_empty() {
        cx.quit();
      }
    })
    .detach();

    cx.activate(true);
    cx.open_window(WindowOptions::default(), |_, cx| cx.new(|_cx| NestedRouter {}))
      .unwrap();
  });
}