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 items;
|
||||||
mod template;
|
mod template;
|
||||||
|
|
||||||
pub use items::{Admin, User};
|
pub use self::items::{AdminMenu, UserMenu};
|
||||||
pub use template::Template;
|
pub use self::template::{MainItem, SubItem};
|
||||||
|
|
||||||
pub enum Menu {
|
pub enum MenuWithActiveItem {
|
||||||
User(User),
|
User(UserMenu),
|
||||||
Admin(Admin),
|
Admin(AdminMenu),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Menu {
|
impl MenuWithActiveItem {
|
||||||
|
/// ID of selected menu item
|
||||||
pub fn selected(&self) -> &'static str {
|
pub fn selected(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
Menu::User(user) => user.id(),
|
MenuWithActiveItem::User(selected) => selected.id(),
|
||||||
Menu::Admin(admin) => admin.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 {
|
match self {
|
||||||
Menu::User(user) => user.list(),
|
MenuWithActiveItem::User(_) => UserMenu::list(),
|
||||||
Menu::Admin(admin) => admin.list(),
|
MenuWithActiveItem::Admin(_) => AdminMenu::list(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<User> for Menu {
|
impl From<UserMenu> for MenuWithActiveItem {
|
||||||
fn from(value: User) -> Self {
|
fn from(value: UserMenu) -> Self {
|
||||||
Self::User(value)
|
Self::User(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Admin> for Menu {
|
impl From<AdminMenu> for MenuWithActiveItem {
|
||||||
fn from(value: Admin) -> Self {
|
fn from(value: AdminMenu) -> Self {
|
||||||
Self::Admin(value)
|
Self::Admin(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
/// Find icons on https://tabler-icons.io
|
/// 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 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 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 admin;
|
||||||
mod user;
|
mod user;
|
||||||
|
|
||||||
pub use admin::Admin;
|
pub use admin::AdminMenu;
|
||||||
pub use user::User;
|
pub use user::UserMenu;
|
||||||
|
|
|
||||||
|
|
@ -1,43 +1,54 @@
|
||||||
use super::super::icons;
|
use crate::menu::{icons, MainItem, SubItem};
|
||||||
use crate::menu::Template;
|
use crate::routes;
|
||||||
|
use rocket::uri;
|
||||||
|
|
||||||
const LIST: &[Admin] = &[Admin::ExitAdmin, Admin::AdminDashboard];
|
pub enum AdminMenu {
|
||||||
|
Exit,
|
||||||
pub enum Admin {
|
Dashboard,
|
||||||
ExitAdmin,
|
Settings,
|
||||||
AdminDashboard,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Admin {
|
|
||||||
pub fn list(&self) -> Vec<Template> {
|
|
||||||
LIST.iter().map(Template::from).collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AdminMenu {
|
||||||
pub fn id(&self) -> &'static str {
|
pub fn id(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
Self::ExitAdmin => "exit_admin",
|
AdminMenu::Exit => "exit",
|
||||||
Self::AdminDashboard => "admin_dashboard",
|
AdminMenu::Dashboard => "dashboard",
|
||||||
|
AdminMenu::Settings => "settings",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn list() -> Vec<MainItem> {
|
||||||
pub fn label(&self) -> &'static str {
|
vec![
|
||||||
match self {
|
MainItem {
|
||||||
Self::ExitAdmin => "Exit admin panel",
|
id: AdminMenu::Exit.id(),
|
||||||
Self::AdminDashboard => "Admin dashboard",
|
label: "Exit admin panel",
|
||||||
}
|
link: uri!(routes::root::homepage).to_string(),
|
||||||
}
|
icon: icons::LOGOUT,
|
||||||
|
sub: None,
|
||||||
pub fn link(&self) -> &'static str {
|
},
|
||||||
match self {
|
MainItem {
|
||||||
Self::ExitAdmin => "/",
|
id: AdminMenu::Dashboard.id(),
|
||||||
Self::AdminDashboard => "/admin",
|
label: "Admin dashboard",
|
||||||
}
|
link: uri!(routes::admin::dashboard::admin_dashboard).to_string(),
|
||||||
}
|
icon: icons::HOME,
|
||||||
|
sub: None,
|
||||||
pub fn icon(&self) -> &'static str {
|
},
|
||||||
match self {
|
MainItem {
|
||||||
Self::ExitAdmin => icons::LOGOUT,
|
id: AdminMenu::Settings.id(),
|
||||||
Self::AdminDashboard => icons::HOME,
|
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::{icons, MainItem};
|
||||||
use crate::menu::Template;
|
use crate::routes;
|
||||||
|
use rocket::uri;
|
||||||
|
|
||||||
const LIST: &[User] = &[User::Home];
|
pub enum UserMenu {
|
||||||
|
|
||||||
pub enum User {
|
|
||||||
Home,
|
Home,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl User {
|
impl UserMenu {
|
||||||
pub fn list(&self) -> Vec<Template> {
|
|
||||||
LIST.iter().map(Template::from).collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn id(&self) -> &'static str {
|
pub fn id(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
User::Home => "home",
|
UserMenu::Home => "home",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn list() -> Vec<MainItem> {
|
||||||
pub fn label(&self) -> &'static str {
|
vec![MainItem {
|
||||||
match self {
|
id: UserMenu::Home.id(),
|
||||||
User::Home => "Home",
|
label: "Home",
|
||||||
}
|
link: uri!(routes::root::homepage).to_string(),
|
||||||
}
|
icon: icons::HOME,
|
||||||
|
sub: None,
|
||||||
pub fn link(&self) -> &'static str {
|
}]
|
||||||
match self {
|
|
||||||
User::Home => "/",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn icon(&self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
User::Home => icons::HOME,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,33 +1,18 @@
|
||||||
use super::{Admin, User};
|
|
||||||
use rocket::serde::Serialize;
|
use rocket::serde::Serialize;
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
#[serde(crate = "rocket::serde")]
|
#[serde(crate = "rocket::serde")]
|
||||||
pub struct Template {
|
pub struct MainItem {
|
||||||
id: &'static str,
|
pub id: &'static str,
|
||||||
label: &'static str,
|
pub label: &'static str,
|
||||||
link: &'static str,
|
pub link: String,
|
||||||
icon: &'static str,
|
pub icon: &'static str,
|
||||||
|
pub sub: Option<Vec<SubItem>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&User> for Template {
|
#[derive(Serialize)]
|
||||||
fn from(value: &User) -> Self {
|
#[serde(crate = "rocket::serde")]
|
||||||
Self {
|
pub struct SubItem {
|
||||||
id: value.id(),
|
pub label: &'static str,
|
||||||
label: value.label(),
|
pub link: String,
|
||||||
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(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,8 @@ mod responder;
|
||||||
mod template;
|
mod template;
|
||||||
|
|
||||||
use self::content::*;
|
use self::content::*;
|
||||||
use crate::menu::Menu;
|
use crate::menu::MenuWithActiveItem;
|
||||||
use erased_serde::Serialize;
|
use erased_serde::Serialize;
|
||||||
|
|
||||||
pub use flash::FlashKind;
|
pub use flash::FlashKind;
|
||||||
|
|
||||||
pub enum Page {
|
pub enum Page {
|
||||||
|
|
@ -44,9 +43,8 @@ impl Page {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Show menu with active item
|
/// Show menu with active item
|
||||||
fn menu(&self) -> Option<Menu> {
|
fn menu_with_active_item(&self) -> Option<MenuWithActiveItem> {
|
||||||
use crate::menu::Admin as AdminMenu;
|
use crate::menu::{AdminMenu, UserMenu};
|
||||||
use crate::menu::User as UserMenu;
|
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Page::Error(_) => None,
|
Page::Error(_) => None,
|
||||||
|
|
@ -54,7 +52,8 @@ impl Page {
|
||||||
Page::Homepage(_) => Some(UserMenu::Home.into()),
|
Page::Homepage(_) => Some(UserMenu::Home.into()),
|
||||||
Page::Authorize(_) => None,
|
Page::Authorize(_) => None,
|
||||||
Page::Redirect(_) => 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 super::Page;
|
||||||
use crate::menu::Template as MenuTemplate;
|
use crate::menu::MainItem;
|
||||||
use rocket::request::FlashMessage;
|
use rocket::request::FlashMessage;
|
||||||
use rocket::serde::Serialize;
|
use rocket::serde::Serialize;
|
||||||
use rocket_dyn_templates::Template;
|
use rocket_dyn_templates::Template;
|
||||||
|
|
@ -9,7 +9,7 @@ use rocket_dyn_templates::Template;
|
||||||
struct TemplateContent<S: Serialize> {
|
struct TemplateContent<S: Serialize> {
|
||||||
title: &'static str,
|
title: &'static str,
|
||||||
version: &'static str,
|
version: &'static str,
|
||||||
menu: Option<(&'static str, Vec<MenuTemplate>)>,
|
menu: Option<(&'static str, Vec<MainItem>)>,
|
||||||
flash: Option<(String, String)>,
|
flash: Option<(String, String)>,
|
||||||
|
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
|
|
@ -22,7 +22,9 @@ fn render(p: Page, flash: Option<(String, String)>) -> Template {
|
||||||
TemplateContent {
|
TemplateContent {
|
||||||
title: p.page_title(),
|
title: p.page_title(),
|
||||||
version: env!("CARGO_PKG_VERSION"),
|
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,
|
flash,
|
||||||
content: p.content(),
|
content: p.content(),
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -3,17 +3,44 @@
|
||||||
<ul class="navbar-nav">
|
<ul class="navbar-nav">
|
||||||
{% for item in menu.1 %}
|
{% for item in menu.1 %}
|
||||||
|
|
||||||
|
{# Define css classes #}
|
||||||
|
{% set nav_class = "nav-item" %}
|
||||||
|
|
||||||
|
{# Active #}
|
||||||
{% if item.id == menu.0 %}
|
{% if item.id == menu.0 %}
|
||||||
<li class="nav-item active">
|
{% set nav_class = nav_class ~ " active" %}
|
||||||
{% else %}
|
|
||||||
<li class="nav-item">
|
|
||||||
{% endif %}
|
{% 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 }}">
|
<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-icon d-md-none d-lg-inline-block">{{ item.icon | safe }}</span>
|
||||||
<span class="nav-link-title">{{ item.label }}</span>
|
<span class="nav-link-title">{{ item.label }}</span>
|
||||||
</a>
|
</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>
|
</li>
|
||||||
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue