ezidam: new menu system, with main and submenus
This commit is contained in:
parent
ddf6f25dd2
commit
efebe2fa80
9 changed files with 133 additions and 120 deletions
|
|
@ -2,38 +2,40 @@ mod icons;
|
|||
mod items;
|
||||
mod template;
|
||||
|
||||
pub use items::{Admin, User};
|
||||
pub use template::Template;
|
||||
pub use self::items::{AdminMenu, UserMenu};
|
||||
pub use self::template::{MainItem, SubItem};
|
||||
|
||||
pub enum Menu {
|
||||
User(User),
|
||||
Admin(Admin),
|
||||
pub enum MenuWithActiveItem {
|
||||
User(UserMenu),
|
||||
Admin(AdminMenu),
|
||||
}
|
||||
|
||||
impl Menu {
|
||||
impl MenuWithActiveItem {
|
||||
/// ID of selected menu item
|
||||
pub fn selected(&self) -> &'static str {
|
||||
match self {
|
||||
Menu::User(user) => user.id(),
|
||||
Menu::Admin(admin) => admin.id(),
|
||||
MenuWithActiveItem::User(selected) => selected.id(),
|
||||
MenuWithActiveItem::Admin(selected) => selected.id(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn list(&self) -> Vec<Template> {
|
||||
/// List of all items of menu
|
||||
pub fn list(&self) -> Vec<MainItem> {
|
||||
match self {
|
||||
Menu::User(user) => user.list(),
|
||||
Menu::Admin(admin) => admin.list(),
|
||||
MenuWithActiveItem::User(_) => UserMenu::list(),
|
||||
MenuWithActiveItem::Admin(_) => AdminMenu::list(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<User> for Menu {
|
||||
fn from(value: User) -> Self {
|
||||
impl From<UserMenu> for MenuWithActiveItem {
|
||||
fn from(value: UserMenu) -> Self {
|
||||
Self::User(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Admin> for Menu {
|
||||
fn from(value: Admin) -> Self {
|
||||
impl From<AdminMenu> for MenuWithActiveItem {
|
||||
fn from(value: AdminMenu) -> Self {
|
||||
Self::Admin(value)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
/// Find icons on https://tabler-icons.io
|
||||
pub const HOME: &str = r#"<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-home" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M5 12l-2 0l9 -9l9 9l-2 0"></path><path d="M5 12v7a2 2 0 0 0 2 2h10a2 2 0 0 0 2 -2v-7"></path><path d="M9 21v-6a2 2 0 0 1 2 -2h2a2 2 0 0 1 2 2v6"></path></svg>"#;
|
||||
pub const LOGOUT: &str = r#"<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-logout" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M14 8v-2a2 2 0 0 0 -2 -2h-7a2 2 0 0 0 -2 2v12a2 2 0 0 0 2 2h7a2 2 0 0 0 2 -2v-2"></path><path d="M7 12h14l-3 -3m0 6l3 -3"></path></svg>"#;
|
||||
pub const SETTINGS: &str = r#"<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-settings" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M10.325 4.317c.426 -1.756 2.924 -1.756 3.35 0a1.724 1.724 0 0 0 2.573 1.066c1.543 -.94 3.31 .826 2.37 2.37a1.724 1.724 0 0 0 1.065 2.572c1.756 .426 1.756 2.924 0 3.35a1.724 1.724 0 0 0 -1.066 2.573c.94 1.543 -.826 3.31 -2.37 2.37a1.724 1.724 0 0 0 -2.572 1.065c-.426 1.756 -2.924 1.756 -3.35 0a1.724 1.724 0 0 0 -2.573 -1.066c-1.543 .94 -3.31 -.826 -2.37 -2.37a1.724 1.724 0 0 0 -1.065 -2.572c-1.756 -.426 -1.756 -2.924 0 -3.35a1.724 1.724 0 0 0 1.066 -2.573c-.94 -1.543 .826 -3.31 2.37 -2.37c1 .608 2.296 .07 2.572 -1.065z"></path><path d="M9 12a3 3 0 1 0 6 0a3 3 0 0 0 -6 0"></path></svg>"#;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
mod admin;
|
||||
mod user;
|
||||
|
||||
pub use admin::Admin;
|
||||
pub use user::User;
|
||||
pub use admin::AdminMenu;
|
||||
pub use user::UserMenu;
|
||||
|
|
|
|||
|
|
@ -1,43 +1,54 @@
|
|||
use super::super::icons;
|
||||
use crate::menu::Template;
|
||||
use crate::menu::{icons, MainItem, SubItem};
|
||||
use crate::routes;
|
||||
use rocket::uri;
|
||||
|
||||
const LIST: &[Admin] = &[Admin::ExitAdmin, Admin::AdminDashboard];
|
||||
|
||||
pub enum Admin {
|
||||
ExitAdmin,
|
||||
AdminDashboard,
|
||||
}
|
||||
|
||||
impl Admin {
|
||||
pub fn list(&self) -> Vec<Template> {
|
||||
LIST.iter().map(Template::from).collect()
|
||||
pub enum AdminMenu {
|
||||
Exit,
|
||||
Dashboard,
|
||||
Settings,
|
||||
}
|
||||
|
||||
impl AdminMenu {
|
||||
pub fn id(&self) -> &'static str {
|
||||
match self {
|
||||
Self::ExitAdmin => "exit_admin",
|
||||
Self::AdminDashboard => "admin_dashboard",
|
||||
AdminMenu::Exit => "exit",
|
||||
AdminMenu::Dashboard => "dashboard",
|
||||
AdminMenu::Settings => "settings",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn label(&self) -> &'static str {
|
||||
match self {
|
||||
Self::ExitAdmin => "Exit admin panel",
|
||||
Self::AdminDashboard => "Admin dashboard",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn link(&self) -> &'static str {
|
||||
match self {
|
||||
Self::ExitAdmin => "/",
|
||||
Self::AdminDashboard => "/admin",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn icon(&self) -> &'static str {
|
||||
match self {
|
||||
Self::ExitAdmin => icons::LOGOUT,
|
||||
Self::AdminDashboard => icons::HOME,
|
||||
}
|
||||
pub fn list() -> Vec<MainItem> {
|
||||
vec![
|
||||
MainItem {
|
||||
id: AdminMenu::Exit.id(),
|
||||
label: "Exit admin panel",
|
||||
link: uri!(routes::root::homepage).to_string(),
|
||||
icon: icons::LOGOUT,
|
||||
sub: None,
|
||||
},
|
||||
MainItem {
|
||||
id: AdminMenu::Dashboard.id(),
|
||||
label: "Admin dashboard",
|
||||
link: uri!(routes::admin::dashboard::admin_dashboard).to_string(),
|
||||
icon: icons::HOME,
|
||||
sub: None,
|
||||
},
|
||||
MainItem {
|
||||
id: AdminMenu::Settings.id(),
|
||||
label: "Server settings",
|
||||
link: uri!(routes::admin::settings::admin_settings).to_string(),
|
||||
icon: icons::SETTINGS,
|
||||
// sub: None,
|
||||
sub: Some(vec![
|
||||
SubItem {
|
||||
label: "Branding",
|
||||
link: uri!(routes::admin::settings::admin_settings).to_string(),
|
||||
},
|
||||
SubItem {
|
||||
label: "Security",
|
||||
link: uri!(routes::setup::setup).to_string(),
|
||||
},
|
||||
]),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,38 +1,24 @@
|
|||
use super::super::icons;
|
||||
use crate::menu::Template;
|
||||
use crate::menu::{icons, MainItem};
|
||||
use crate::routes;
|
||||
use rocket::uri;
|
||||
|
||||
const LIST: &[User] = &[User::Home];
|
||||
|
||||
pub enum User {
|
||||
pub enum UserMenu {
|
||||
Home,
|
||||
}
|
||||
|
||||
impl User {
|
||||
pub fn list(&self) -> Vec<Template> {
|
||||
LIST.iter().map(Template::from).collect()
|
||||
}
|
||||
|
||||
impl UserMenu {
|
||||
pub fn id(&self) -> &'static str {
|
||||
match self {
|
||||
User::Home => "home",
|
||||
UserMenu::Home => "home",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn label(&self) -> &'static str {
|
||||
match self {
|
||||
User::Home => "Home",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn link(&self) -> &'static str {
|
||||
match self {
|
||||
User::Home => "/",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn icon(&self) -> &'static str {
|
||||
match self {
|
||||
User::Home => icons::HOME,
|
||||
}
|
||||
pub fn list() -> Vec<MainItem> {
|
||||
vec![MainItem {
|
||||
id: UserMenu::Home.id(),
|
||||
label: "Home",
|
||||
link: uri!(routes::root::homepage).to_string(),
|
||||
icon: icons::HOME,
|
||||
sub: None,
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,33 +1,18 @@
|
|||
use super::{Admin, User};
|
||||
use rocket::serde::Serialize;
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(crate = "rocket::serde")]
|
||||
pub struct Template {
|
||||
id: &'static str,
|
||||
label: &'static str,
|
||||
link: &'static str,
|
||||
icon: &'static str,
|
||||
pub struct MainItem {
|
||||
pub id: &'static str,
|
||||
pub label: &'static str,
|
||||
pub link: String,
|
||||
pub icon: &'static str,
|
||||
pub sub: Option<Vec<SubItem>>,
|
||||
}
|
||||
|
||||
impl From<&User> for Template {
|
||||
fn from(value: &User) -> Self {
|
||||
Self {
|
||||
id: value.id(),
|
||||
label: value.label(),
|
||||
link: value.link(),
|
||||
icon: value.icon(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Admin> for Template {
|
||||
fn from(value: &Admin) -> Self {
|
||||
Self {
|
||||
id: value.id(),
|
||||
label: value.label(),
|
||||
link: value.link(),
|
||||
icon: value.icon(),
|
||||
}
|
||||
}
|
||||
#[derive(Serialize)]
|
||||
#[serde(crate = "rocket::serde")]
|
||||
pub struct SubItem {
|
||||
pub label: &'static str,
|
||||
pub link: String,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,9 +4,8 @@ mod responder;
|
|||
mod template;
|
||||
|
||||
use self::content::*;
|
||||
use crate::menu::Menu;
|
||||
use crate::menu::MenuWithActiveItem;
|
||||
use erased_serde::Serialize;
|
||||
|
||||
pub use flash::FlashKind;
|
||||
|
||||
pub enum Page {
|
||||
|
|
@ -44,9 +43,8 @@ impl Page {
|
|||
}
|
||||
|
||||
/// Show menu with active item
|
||||
fn menu(&self) -> Option<Menu> {
|
||||
use crate::menu::Admin as AdminMenu;
|
||||
use crate::menu::User as UserMenu;
|
||||
fn menu_with_active_item(&self) -> Option<MenuWithActiveItem> {
|
||||
use crate::menu::{AdminMenu, UserMenu};
|
||||
|
||||
match self {
|
||||
Page::Error(_) => None,
|
||||
|
|
@ -54,7 +52,8 @@ impl Page {
|
|||
Page::Homepage(_) => Some(UserMenu::Home.into()),
|
||||
Page::Authorize(_) => None,
|
||||
Page::Redirect(_) => None,
|
||||
Page::AdminDashboard(_) => Some(AdminMenu::AdminDashboard.into()),
|
||||
Page::AdminDashboard(_) => Some(AdminMenu::Dashboard.into()),
|
||||
Page::AdminSettings(_) => Some(AdminMenu::Settings.into()),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use super::Page;
|
||||
use crate::menu::Template as MenuTemplate;
|
||||
use crate::menu::MainItem;
|
||||
use rocket::request::FlashMessage;
|
||||
use rocket::serde::Serialize;
|
||||
use rocket_dyn_templates::Template;
|
||||
|
|
@ -9,7 +9,7 @@ use rocket_dyn_templates::Template;
|
|||
struct TemplateContent<S: Serialize> {
|
||||
title: &'static str,
|
||||
version: &'static str,
|
||||
menu: Option<(&'static str, Vec<MenuTemplate>)>,
|
||||
menu: Option<(&'static str, Vec<MainItem>)>,
|
||||
flash: Option<(String, String)>,
|
||||
|
||||
#[serde(flatten)]
|
||||
|
|
@ -22,7 +22,9 @@ fn render(p: Page, flash: Option<(String, String)>) -> Template {
|
|||
TemplateContent {
|
||||
title: p.page_title(),
|
||||
version: env!("CARGO_PKG_VERSION"),
|
||||
menu: p.menu().map(|menu| (menu.selected(), menu.list())),
|
||||
menu: p
|
||||
.menu_with_active_item()
|
||||
.map(|menu| (menu.selected(), menu.list())),
|
||||
flash,
|
||||
content: p.content(),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -3,17 +3,44 @@
|
|||
<ul class="navbar-nav">
|
||||
{% for item in menu.1 %}
|
||||
|
||||
{# Define css classes #}
|
||||
{% set nav_class = "nav-item" %}
|
||||
|
||||
{# Active #}
|
||||
{% if item.id == menu.0 %}
|
||||
<li class="nav-item active">
|
||||
{% else %}
|
||||
<li class="nav-item">
|
||||
{% set nav_class = nav_class ~ " active" %}
|
||||
{% endif %}
|
||||
|
||||
{# Submenu #}
|
||||
{% if item.sub %}
|
||||
{% set nav_class = nav_class ~ " dropdown" %}
|
||||
{% endif %}
|
||||
|
||||
{# Display css classes #}
|
||||
<li class="{{ nav_class }}">
|
||||
|
||||
{# Display primary menu item #}
|
||||
|
||||
{% if item.sub %}
|
||||
{# Contains a submenu #}
|
||||
<a class="nav-link dropdown-toggle" href="#navbar-{{ item.id }}" data-bs-toggle="dropdown" data-bs-auto-close="outside" role="button" aria-expanded="false">
|
||||
{% else %}
|
||||
{# Primary menu item only #}
|
||||
<a class="nav-link" href="{{ item.link }}">
|
||||
{% endif %}
|
||||
<span class="nav-link-icon d-md-none d-lg-inline-block">{{ item.icon | safe }}</span>
|
||||
<span class="nav-link-title">{{ item.label }}</span>
|
||||
</a>
|
||||
|
||||
{# Display submenu items #}
|
||||
{% if item.sub %}
|
||||
<div class="dropdown-menu">
|
||||
{% for sub_item in item.sub %}
|
||||
<a class="dropdown-item" href="{{ sub_item.link }}">{{ sub_item.label }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</li>
|
||||
|
||||
{% endfor %}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue