🚧 [base] Añade nuevo componente menu
This commit is contained in:
parent
744bd700fc
commit
c0577a0773
15 changed files with 1249 additions and 7 deletions
|
@ -1,5 +1,53 @@
|
|||
//! Componentes nativos proporcionados por PageTop.
|
||||
|
||||
use crate::AutoDefault;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
// **< FontSize >***********************************************************************************
|
||||
|
||||
#[derive(AutoDefault)]
|
||||
pub enum FontSize {
|
||||
ExtraLarge,
|
||||
XxLarge,
|
||||
XLarge,
|
||||
Large,
|
||||
Medium,
|
||||
#[default]
|
||||
Normal,
|
||||
Small,
|
||||
XSmall,
|
||||
XxSmall,
|
||||
ExtraSmall,
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
impl FontSize {
|
||||
#[inline]
|
||||
pub const fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
FontSize::ExtraLarge => "fs__x3l",
|
||||
FontSize::XxLarge => "fs__x2l",
|
||||
FontSize::XLarge => "fs__xl",
|
||||
FontSize::Large => "fs__l",
|
||||
FontSize::Medium => "fs__m",
|
||||
FontSize::Normal => "",
|
||||
FontSize::Small => "fs__s",
|
||||
FontSize::XSmall => "fs__xs",
|
||||
FontSize::XxSmall => "fs__x2s",
|
||||
FontSize::ExtraSmall => "fs__x3s",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for FontSize {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
// *************************************************************************************************
|
||||
|
||||
mod html;
|
||||
pub use html::Html;
|
||||
|
||||
|
@ -11,3 +59,5 @@ pub use poweredby::PoweredBy;
|
|||
|
||||
mod icon;
|
||||
pub use icon::{Icon, IconKind};
|
||||
|
||||
pub mod menu;
|
||||
|
|
17
src/base/component/menu.rs
Normal file
17
src/base/component/menu.rs
Normal file
|
@ -0,0 +1,17 @@
|
|||
mod menu_menu;
|
||||
pub use menu_menu::Menu;
|
||||
|
||||
mod item;
|
||||
pub use item::{Item, ItemKind};
|
||||
|
||||
mod submenu;
|
||||
pub use submenu::Submenu;
|
||||
|
||||
mod megamenu;
|
||||
pub use megamenu::Megamenu;
|
||||
|
||||
mod group;
|
||||
pub use group::Group;
|
||||
|
||||
mod element;
|
||||
pub use element::{Element, ElementType};
|
56
src/base/component/menu/element.rs
Normal file
56
src/base/component/menu/element.rs
Normal file
|
@ -0,0 +1,56 @@
|
|||
use crate::prelude::*;
|
||||
|
||||
type Content = Typed<Html>;
|
||||
type SubmenuItems = Typed<menu::Submenu>;
|
||||
|
||||
#[derive(AutoDefault)]
|
||||
pub enum ElementType {
|
||||
#[default]
|
||||
Void,
|
||||
Html(Content),
|
||||
Submenu(SubmenuItems),
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[derive(AutoDefault)]
|
||||
pub struct Element {
|
||||
element_type: ElementType,
|
||||
}
|
||||
|
||||
impl Component for Element {
|
||||
fn new() -> Self {
|
||||
Element::default()
|
||||
}
|
||||
|
||||
fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup {
|
||||
match self.element_type() {
|
||||
ElementType::Void => PrepareMarkup::None,
|
||||
ElementType::Html(content) => PrepareMarkup::With(html! {
|
||||
(content.render(cx))
|
||||
}),
|
||||
ElementType::Submenu(submenu) => PrepareMarkup::With(html! {
|
||||
(submenu.render(cx))
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Element {
|
||||
pub fn html(content: Html) -> Self {
|
||||
Element {
|
||||
element_type: ElementType::Html(Content::with(content)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn submenu(submenu: menu::Submenu) -> Self {
|
||||
Element {
|
||||
element_type: ElementType::Submenu(SubmenuItems::with(submenu)),
|
||||
}
|
||||
}
|
||||
|
||||
// **< Element GETTERS >************************************************************************
|
||||
|
||||
pub fn element_type(&self) -> &ElementType {
|
||||
&self.element_type
|
||||
}
|
||||
}
|
58
src/base/component/menu/group.rs
Normal file
58
src/base/component/menu/group.rs
Normal file
|
@ -0,0 +1,58 @@
|
|||
use crate::prelude::*;
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[derive(AutoDefault)]
|
||||
pub struct Group {
|
||||
id : AttrId,
|
||||
elements: Children,
|
||||
}
|
||||
|
||||
impl Component for Group {
|
||||
fn new() -> Self {
|
||||
Group::default()
|
||||
}
|
||||
|
||||
fn id(&self) -> Option<String> {
|
||||
self.id.get()
|
||||
}
|
||||
|
||||
fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup {
|
||||
PrepareMarkup::With(html! {
|
||||
div id=[self.id()] class="menu-group" {
|
||||
(self.elements().render(cx))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Group {
|
||||
// **< Group BUILDER >**************************************************************************
|
||||
|
||||
/// Establece el identificador único (`id`) del grupo.
|
||||
#[builder_fn]
|
||||
pub fn with_id(mut self, id: impl AsRef<str>) -> Self {
|
||||
self.id.alter_value(id);
|
||||
self
|
||||
}
|
||||
|
||||
/// Añade un nuevo elemento al menú.
|
||||
pub fn add_element(mut self, element: menu::Element) -> Self {
|
||||
self.elements
|
||||
.alter_typed(TypedOp::Add(Typed::with(element)));
|
||||
self
|
||||
}
|
||||
|
||||
/// Modifica la lista de elementos (`children`) aplicando una operación [`TypedOp`].
|
||||
#[builder_fn]
|
||||
pub fn with_elements(mut self, op: TypedOp<menu::Element>) -> Self {
|
||||
self.elements.alter_typed(op);
|
||||
self
|
||||
}
|
||||
|
||||
// **< Group GETTERS >**************************************************************************
|
||||
|
||||
/// Devuelve la lista de elementos (`children`) del grupo.
|
||||
pub fn elements(&self) -> &Children {
|
||||
&self.elements
|
||||
}
|
||||
}
|
183
src/base/component/menu/item.rs
Normal file
183
src/base/component/menu/item.rs
Normal file
|
@ -0,0 +1,183 @@
|
|||
use crate::prelude::*;
|
||||
|
||||
//use super::{Megamenu, Submenu};
|
||||
|
||||
type Label = L10n;
|
||||
type Content = Typed<Html>;
|
||||
type SubmenuItems = Typed<menu::Submenu>;
|
||||
//type MegamenuGroups = Typed<Megamenu>;
|
||||
|
||||
#[derive(AutoDefault)]
|
||||
pub enum ItemKind {
|
||||
#[default]
|
||||
Void,
|
||||
Label(Label),
|
||||
Link(Label, FnPathByContext),
|
||||
LinkBlank(Label, FnPathByContext),
|
||||
Html(Content),
|
||||
Submenu(Label, SubmenuItems),
|
||||
// Megamenu(Label, MegamenuGroups),
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[derive(AutoDefault)]
|
||||
pub struct Item {
|
||||
item_kind : ItemKind,
|
||||
description: AttrL10n,
|
||||
left_icon : Typed<Icon>,
|
||||
right_icon : Typed<Icon>,
|
||||
}
|
||||
|
||||
impl Component for Item {
|
||||
fn new() -> Self {
|
||||
Item::default()
|
||||
}
|
||||
|
||||
fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup {
|
||||
let description = self.description().lookup(cx);
|
||||
let left_icon = self.left_icon().render(cx);
|
||||
let right_icon = self.right_icon().render(cx);
|
||||
|
||||
match self.item_kind() {
|
||||
ItemKind::Void => PrepareMarkup::None,
|
||||
ItemKind::Label(label) => PrepareMarkup::With(html! {
|
||||
li class="menu__label" {
|
||||
span title=[description] {
|
||||
(left_icon)
|
||||
(label.using(cx))
|
||||
(right_icon)
|
||||
}
|
||||
}
|
||||
}),
|
||||
ItemKind::Link(label, path) => PrepareMarkup::With(html! {
|
||||
li class="menu__link" {
|
||||
a href=(path(cx)) title=[description] {
|
||||
(left_icon)
|
||||
(label.using(cx))
|
||||
(right_icon)
|
||||
}
|
||||
}
|
||||
}),
|
||||
ItemKind::LinkBlank(label, path) => PrepareMarkup::With(html! {
|
||||
li class="menu__link" {
|
||||
a href=(path(cx)) title=[description] target="_blank" {
|
||||
(left_icon)
|
||||
(label.using(cx))
|
||||
(right_icon)
|
||||
}
|
||||
}
|
||||
}),
|
||||
ItemKind::Html(content) => PrepareMarkup::With(html! {
|
||||
li class="menu__html" {
|
||||
(content.render(cx))
|
||||
}
|
||||
}),
|
||||
ItemKind::Submenu(label, submenu) => PrepareMarkup::With(html! {
|
||||
li class="menu__children" {
|
||||
a href="#" title=[description] {
|
||||
(left_icon)
|
||||
(label.using(cx)) i class="menu__icon bi-chevron-down" {}
|
||||
}
|
||||
div class="menu__subs" {
|
||||
(submenu.render(cx))
|
||||
}
|
||||
}
|
||||
}),
|
||||
/*
|
||||
ItemKind::Megamenu(label, megamenu) => PrepareMarkup::With(html! {
|
||||
li class="menu__children" {
|
||||
a href="#" title=[description] {
|
||||
(left_icon)
|
||||
(label.escaped(cx.langid())) i class="menu__icon bi-chevron-down" {}
|
||||
}
|
||||
div class="menu__subs menu__mega" {
|
||||
(megamenu.render(cx))
|
||||
}
|
||||
}
|
||||
}),
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Item {
|
||||
pub fn label(label: L10n) -> Self {
|
||||
Item {
|
||||
item_kind: ItemKind::Label(label),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn link(label: L10n, path: FnPathByContext) -> Self {
|
||||
Item {
|
||||
item_kind: ItemKind::Link(label, path),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn link_blank(label: L10n, path: FnPathByContext) -> Self {
|
||||
Item {
|
||||
item_kind: ItemKind::LinkBlank(label, path),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn html(content: Html) -> Self {
|
||||
Item {
|
||||
item_kind: ItemKind::Html(Content::with(content)),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn submenu(label: L10n, submenu: menu::Submenu) -> Self {
|
||||
Item {
|
||||
item_kind: ItemKind::Submenu(label, SubmenuItems::with(submenu)),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
/*
|
||||
pub fn megamenu(label: L10n, megamenu: Megamenu) -> Self {
|
||||
Item {
|
||||
item_kind: ItemKind::Megamenu(label, MegamenuGroups::with(megamenu)),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
*/
|
||||
// **< Item BUILDER >***************************************************************************
|
||||
|
||||
#[builder_fn]
|
||||
pub fn with_description(mut self, text: L10n) -> Self {
|
||||
self.description.alter_value(text);
|
||||
self
|
||||
}
|
||||
|
||||
#[builder_fn]
|
||||
pub fn with_left_icon<I: Into<Icon>>(mut self, icon: Option<I>) -> Self {
|
||||
self.left_icon.alter_component(icon.map(Into::into));
|
||||
self
|
||||
}
|
||||
|
||||
#[builder_fn]
|
||||
pub fn with_right_icon<I: Into<Icon>>(mut self, icon: Option<I>) -> Self {
|
||||
self.right_icon.alter_component(icon.map(Into::into));
|
||||
self
|
||||
}
|
||||
|
||||
// **< Item GETTERS >***************************************************************************
|
||||
|
||||
pub fn item_kind(&self) -> &ItemKind {
|
||||
&self.item_kind
|
||||
}
|
||||
|
||||
pub fn description(&self) -> &AttrL10n {
|
||||
&self.description
|
||||
}
|
||||
|
||||
pub fn left_icon(&self) -> &Typed<Icon> {
|
||||
&self.left_icon
|
||||
}
|
||||
|
||||
pub fn right_icon(&self) -> &Typed<Icon> {
|
||||
&self.right_icon
|
||||
}
|
||||
}
|
57
src/base/component/menu/megamenu.rs
Normal file
57
src/base/component/menu/megamenu.rs
Normal file
|
@ -0,0 +1,57 @@
|
|||
use crate::prelude::*;
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[derive(AutoDefault)]
|
||||
pub struct Megamenu {
|
||||
id : AttrId,
|
||||
groups: Children,
|
||||
}
|
||||
|
||||
impl Component for Megamenu {
|
||||
fn new() -> Self {
|
||||
Megamenu::default()
|
||||
}
|
||||
|
||||
fn id(&self) -> Option<String> {
|
||||
self.id.get()
|
||||
}
|
||||
|
||||
fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup {
|
||||
PrepareMarkup::With(html! {
|
||||
div id=[self.id()] class="menu__groups" {
|
||||
(self.groups().render(cx))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Megamenu {
|
||||
// **< Megamenu BUILDER >***********************************************************************
|
||||
|
||||
/// Establece el identificador único (`id`) del megamenú.
|
||||
#[builder_fn]
|
||||
pub fn with_id(mut self, id: impl AsRef<str>) -> Self {
|
||||
self.id.alter_value(id);
|
||||
self
|
||||
}
|
||||
|
||||
/// Añade un nuevo grupo al menú.
|
||||
pub fn add_group(mut self, group: menu::Group) -> Self {
|
||||
self.groups.alter_typed(TypedOp::Add(Typed::with(group)));
|
||||
self
|
||||
}
|
||||
|
||||
/// Modifica la lista de grupos (`children`) aplicando una operación [`TypedOp`].
|
||||
#[builder_fn]
|
||||
pub fn with_groups(mut self, op: TypedOp<menu::Group>) -> Self {
|
||||
self.groups.alter_typed(op);
|
||||
self
|
||||
}
|
||||
|
||||
// **< Megamenu GETTERS >***********************************************************************
|
||||
|
||||
/// Devuelve la lista de grupos (`children`) del megamenú.
|
||||
pub fn groups(&self) -> &Children {
|
||||
&self.groups
|
||||
}
|
||||
}
|
106
src/base/component/menu/menu_menu.rs
Normal file
106
src/base/component/menu/menu_menu.rs
Normal file
|
@ -0,0 +1,106 @@
|
|||
use crate::prelude::*;
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[derive(AutoDefault)]
|
||||
pub struct Menu {
|
||||
id : AttrId,
|
||||
classes: AttrClasses,
|
||||
items : Children,
|
||||
}
|
||||
|
||||
impl Component for Menu {
|
||||
fn new() -> Self {
|
||||
Menu::default()
|
||||
}
|
||||
|
||||
fn id(&self) -> Option<String> {
|
||||
self.id.get()
|
||||
}
|
||||
|
||||
fn setup_before_prepare(&mut self, _cx: &mut Context) {
|
||||
self.alter_classes(ClassesOp::Prepend, "menu");
|
||||
}
|
||||
|
||||
fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup {
|
||||
// cx.set_param::<bool>(PARAM_BASE_INCLUDE_MENU_ASSETS, &true);
|
||||
// cx.set_param::<bool>(PARAM_BASE_INCLUDE_ICONS, &true);
|
||||
|
||||
PrepareMarkup::With(html! {
|
||||
div id=[self.id()] class=[self.classes().get()] {
|
||||
div class="menu__wrapper" {
|
||||
div class="menu__panel" {
|
||||
div class="menu__overlay" {}
|
||||
nav class="menu__nav" {
|
||||
div class="menu__header" {
|
||||
button type="button" class="menu__back" {
|
||||
(Icon::svg(html! {
|
||||
path fill-rule="evenodd" d="M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0" {}
|
||||
}).render(cx))
|
||||
}
|
||||
div class="menu__title" {}
|
||||
button type="button" class="menu__close" {
|
||||
(Icon::svg(html! {
|
||||
path d="M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8z" {}
|
||||
}).render(cx))
|
||||
}
|
||||
}
|
||||
ul class="menu__list" {
|
||||
(self.items().render(cx))
|
||||
}
|
||||
}
|
||||
}
|
||||
button
|
||||
type="button"
|
||||
class="menu__trigger"
|
||||
title=[L10n::l("menu_toggle").lookup(cx)]
|
||||
{
|
||||
span {} span {} span {}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Menu {
|
||||
// **< Menu BUILDER >***************************************************************************
|
||||
|
||||
/// Establece el identificador único (`id`) del menú.
|
||||
#[builder_fn]
|
||||
pub fn with_id(mut self, id: impl AsRef<str>) -> Self {
|
||||
self.id.alter_value(id);
|
||||
self
|
||||
}
|
||||
|
||||
/// Modifica la lista de clases CSS aplicadas al menú.
|
||||
#[builder_fn]
|
||||
pub fn with_classes(mut self, op: ClassesOp, classes: impl AsRef<str>) -> Self {
|
||||
self.classes.alter_value(op, classes);
|
||||
self
|
||||
}
|
||||
|
||||
/// Añade un nuevo ítem al menú.
|
||||
pub fn add_item(mut self, item: menu::Item) -> Self {
|
||||
self.items.alter_typed(TypedOp::Add(Typed::with(item)));
|
||||
self
|
||||
}
|
||||
|
||||
/// Modifica la lista de ítems (`children`) aplicando una operación [`TypedOp`].
|
||||
#[builder_fn]
|
||||
pub fn with_items(mut self, op: TypedOp<menu::Item>) -> Self {
|
||||
self.items.alter_typed(op);
|
||||
self
|
||||
}
|
||||
|
||||
// **< Menu GETTERS >***************************************************************************
|
||||
|
||||
/// Devuelve las clases CSS asociadas al menú.
|
||||
pub fn classes(&self) -> &AttrClasses {
|
||||
&self.classes
|
||||
}
|
||||
|
||||
/// Devuelve la lista de ítems (`children`) del menú.
|
||||
pub fn items(&self) -> &Children {
|
||||
&self.items
|
||||
}
|
||||
}
|
73
src/base/component/menu/submenu.rs
Normal file
73
src/base/component/menu/submenu.rs
Normal file
|
@ -0,0 +1,73 @@
|
|||
use crate::prelude::*;
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[derive(AutoDefault)]
|
||||
pub struct Submenu {
|
||||
id : AttrId,
|
||||
title: AttrL10n,
|
||||
items: Children,
|
||||
}
|
||||
|
||||
impl Component for Submenu {
|
||||
fn new() -> Self {
|
||||
Submenu::default()
|
||||
}
|
||||
|
||||
fn id(&self) -> Option<String> {
|
||||
self.id.get()
|
||||
}
|
||||
|
||||
fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup {
|
||||
PrepareMarkup::With(html! {
|
||||
div id=[self.id()] class="menu__items" {
|
||||
@if let Some(title) = self.title().lookup(cx) {
|
||||
h4 class="menu__title" { (title) }
|
||||
}
|
||||
ul {
|
||||
(self.items().render(cx))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Submenu {
|
||||
// **< Submenu BUILDER >************************************************************************
|
||||
|
||||
/// Establece el identificador único (`id`) del submenú.
|
||||
#[builder_fn]
|
||||
pub fn with_id(mut self, id: impl AsRef<str>) -> Self {
|
||||
self.id.alter_value(id);
|
||||
self
|
||||
}
|
||||
|
||||
#[builder_fn]
|
||||
pub fn with_title(mut self, title: L10n) -> Self {
|
||||
self.title.alter_value(title);
|
||||
self
|
||||
}
|
||||
|
||||
/// Añade un nuevo ítem al submenú.
|
||||
pub fn add_item(mut self, item: menu::Item) -> Self {
|
||||
self.items.alter_typed(TypedOp::Add(Typed::with(item)));
|
||||
self
|
||||
}
|
||||
|
||||
/// Modifica la lista de ítems (`children`) aplicando una operación [`TypedOp`].
|
||||
#[builder_fn]
|
||||
pub fn with_items(mut self, op: TypedOp<menu::Item>) -> Self {
|
||||
self.items.alter_typed(op);
|
||||
self
|
||||
}
|
||||
|
||||
// **< Submenu GETTERS >************************************************************************
|
||||
|
||||
pub fn title(&self) -> &AttrL10n {
|
||||
&self.title
|
||||
}
|
||||
|
||||
/// Devuelve la lista de ítems (`children`) del submenú.
|
||||
pub fn items(&self) -> &Children {
|
||||
&self.items
|
||||
}
|
||||
}
|
|
@ -51,14 +51,35 @@ impl Theme for Basic {
|
|||
"PageTopIntro" => "/css/intro.css",
|
||||
_ => "/css/basic.css",
|
||||
};
|
||||
let pkg_version = env!("CARGO_PKG_VERSION");
|
||||
page.alter_assets(AssetsOp::AddStyleSheet(
|
||||
StyleSheet::from("/css/normalize.css")
|
||||
.with_version("8.0.1")
|
||||
.with_weight(-99),
|
||||
))
|
||||
.alter_assets(AssetsOp::AddStyleSheet(
|
||||
StyleSheet::from("/css/root.css")
|
||||
.with_version(pkg_version)
|
||||
.with_weight(-99),
|
||||
))
|
||||
.alter_assets(AssetsOp::AddStyleSheet(
|
||||
StyleSheet::from("/css/components.css")
|
||||
.with_version(pkg_version)
|
||||
.with_weight(-99),
|
||||
))
|
||||
.alter_assets(AssetsOp::AddStyleSheet(
|
||||
StyleSheet::from("/css/menu.css")
|
||||
.with_version(pkg_version)
|
||||
.with_weight(-99),
|
||||
))
|
||||
.alter_assets(AssetsOp::AddStyleSheet(
|
||||
StyleSheet::from(styles)
|
||||
.with_version(env!("CARGO_PKG_VERSION"))
|
||||
.with_version(pkg_version)
|
||||
.with_weight(-99),
|
||||
))
|
||||
.alter_assets(AssetsOp::AddJavaScript(
|
||||
JavaScript::defer("/js/menu.js")
|
||||
.with_version(pkg_version)
|
||||
.with_weight(-99),
|
||||
));
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ pub use assets::{Asset, Assets};
|
|||
|
||||
mod context;
|
||||
pub use context::{AssetsOp, Context, Contextual, ErrorParam};
|
||||
pub type FnPathByContext = fn(cx: &Context) -> &str;
|
||||
|
||||
// **< HTML ATTRIBUTES >****************************************************************************
|
||||
|
||||
|
|
|
@ -3,9 +3,3 @@
|
|||
.region--footer {
|
||||
padding-bottom: 2rem;
|
||||
}
|
||||
|
||||
/* PoweredBy component */
|
||||
|
||||
.poweredby {
|
||||
text-align: center;
|
||||
}
|
||||
|
|
12
static/css/components.css
Normal file
12
static/css/components.css
Normal file
|
@ -0,0 +1,12 @@
|
|||
/* Icon component */
|
||||
|
||||
.icon {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
}
|
||||
|
||||
/* PoweredBy component */
|
||||
|
||||
.poweredby {
|
||||
text-align: center;
|
||||
}
|
309
static/css/menu.css
Normal file
309
static/css/menu.css
Normal file
|
@ -0,0 +1,309 @@
|
|||
.menu {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
z-index: 9999;
|
||||
border: none;
|
||||
outline: none;
|
||||
background: var(--val-menu--color-bg);
|
||||
}
|
||||
|
||||
.menu__wrapper {
|
||||
padding-right: var(--val-gap);
|
||||
}
|
||||
.menu__wrapper a,
|
||||
.menu__wrapper button {
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
background: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.menu__nav ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.menu__nav li {
|
||||
display: inline-block;
|
||||
margin: 0 0 0 1.5rem;
|
||||
padding: var(--val-menu--line-padding) 0;
|
||||
line-height: var(--val-menu--line-height);
|
||||
list-style: none;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.menu__nav li.menu__label,
|
||||
.menu__nav li > a {
|
||||
position: relative;
|
||||
font-weight: 500;
|
||||
color: var(--val-color--text);
|
||||
text-rendering: optimizeLegibility;
|
||||
}
|
||||
.menu__nav li > a {
|
||||
border: none;
|
||||
transition: color 0.3s ease-in-out;
|
||||
}
|
||||
.menu__nav li:hover > a,
|
||||
.menu__nav li > a:focus {
|
||||
color: var(--val-menu--color-highlight);
|
||||
}
|
||||
.menu__nav li > a > i.menu__icon {
|
||||
margin-left: 0.25rem;
|
||||
}
|
||||
|
||||
.menu__nav li .menu__subs {
|
||||
position: absolute;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
padding: 1rem 2rem;
|
||||
border: none;
|
||||
outline: none;
|
||||
background: var(--val-menu--color-bg);
|
||||
border-radius: var(--val-menu--border-radius);
|
||||
border-top: 3px solid var(--val-menu--color-highlight);
|
||||
z-index: 500;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
box-shadow: 0 4px 6px -1px var(--val-menu--color-border), 0 2px 4px -1px var(--val-menu--color-shadow);
|
||||
transition: all 0.5s ease-in-out;
|
||||
}
|
||||
|
||||
.menu__nav li.menu__children:hover > .menu__subs,
|
||||
.menu__nav li.menu__children > a:focus + .menu__subs,
|
||||
.menu__nav li.menu__children .menu__subs:focus-within {
|
||||
margin-top: 0.4rem;
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.menu__nav li .menu__items {
|
||||
min-width: var(--val-menu--item-width-min);
|
||||
max-width: var(--val-menu--item-width-max);
|
||||
}
|
||||
.menu__nav li .menu__items .menu__title {
|
||||
font-family: inherit;
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
margin: 0;
|
||||
padding: var(--val-menu--line-padding) 0;
|
||||
line-height: var(--val-menu--line-height);
|
||||
border: none;
|
||||
outline: none;
|
||||
color: var(--val-menu--color-highlight);
|
||||
text-transform: uppercase;
|
||||
text-rendering: optimizeLegibility;
|
||||
}
|
||||
.menu__nav li .menu__items li {
|
||||
display: block;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.menu__nav li .menu__mega {
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.menu__nav li .menu__groups {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.menu__header,
|
||||
.menu__trigger {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Applies <= 992px */
|
||||
@media only screen and (max-width: 62rem) {
|
||||
.menu {
|
||||
border-radius: var(--val-border-radius);
|
||||
}
|
||||
.menu__wrapper {
|
||||
padding-right: var(--val-gap-0-5);
|
||||
}
|
||||
.menu__wrapper button {
|
||||
margin: var(--val-gap-0-5) 0 var(--val-gap-0-5) var(--val-gap-0-5);
|
||||
}
|
||||
.menu__trigger {
|
||||
cursor: pointer;
|
||||
width: var(--val-menu--trigger-width);
|
||||
height: var(--val-menu--item-height);
|
||||
border: none;
|
||||
outline: none;
|
||||
background: none;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
.menu__trigger span {
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
margin: 12.675% 0;
|
||||
border-radius: var(--val-border-radius);
|
||||
background: var(--val-color--text);
|
||||
}
|
||||
|
||||
.menu__nav {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: var(--val-menu--side-width);
|
||||
height: 100%;
|
||||
z-index: 9099;
|
||||
overflow: hidden;
|
||||
background: var(--val-menu--color-bg);
|
||||
transform: translate(-100%);
|
||||
transition: all 0.5s ease-in-out;
|
||||
}
|
||||
.menu__panel .menu__nav.active {
|
||||
transform: translate(0%);
|
||||
}
|
||||
|
||||
.menu__nav li {
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.menu__nav li.menu__label,
|
||||
.menu__nav li > a {
|
||||
display: block;
|
||||
padding: var(--val-menu--line-padding) var(--val-menu--item-height) var(--val-menu--line-padding) var(--val-menu--item-gap);
|
||||
border-bottom: 1px solid var(--val-menu--color-border);
|
||||
}
|
||||
.menu__nav li ul li.menu__label,
|
||||
.menu__nav li ul li > a {
|
||||
border-bottom: 0;
|
||||
}
|
||||
.menu__nav li > a > i.menu__icon {
|
||||
position: absolute;
|
||||
top: var(--val-menu--line-padding);
|
||||
right: var(--val-menu--line-padding);
|
||||
font-size: 1.25rem;
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
.menu__nav li .menu__subs {
|
||||
position: absolute;
|
||||
display: none;
|
||||
top: 0;
|
||||
left: 0;
|
||||
max-width: none;
|
||||
min-width: auto;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0 !important;
|
||||
padding: 0;
|
||||
border-top: 0;
|
||||
opacity: 1;
|
||||
overflow-y: auto;
|
||||
visibility: visible;
|
||||
transform: translateX(0%);
|
||||
box-shadow: none;
|
||||
}
|
||||
.menu__nav li .menu__subs.active {
|
||||
display: block;
|
||||
}
|
||||
.menu__nav li .menu__subs > :first-child {
|
||||
margin-top: 4rem;
|
||||
}
|
||||
|
||||
.menu__nav li .menu__items .menu__title {
|
||||
padding: var(--val-menu--line-padding) var(--val-menu--item-height) var(--val-menu--line-padding) var(--val-menu--item-gap);
|
||||
}
|
||||
|
||||
.menu__nav li .menu__groups {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.menu__nav .menu__header {
|
||||
position: sticky;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
top: 0;
|
||||
height: var(--val-menu--item-height);
|
||||
border-bottom: 1px solid var(--val-menu--color-border);
|
||||
background: var(--val-menu--color-bg);
|
||||
z-index: 501;
|
||||
}
|
||||
.menu__nav .menu__header .menu__title {
|
||||
padding: var(--val-menu--line-padding);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.menu__nav .menu__header .menu__close,
|
||||
.menu__nav .menu__header .menu__back {
|
||||
width: var(--val-menu--item-height);
|
||||
min-width: var(--val-menu--item-height);
|
||||
height: var(--val-menu--item-height);
|
||||
line-height: var(--val-menu--item-height);
|
||||
color: var(--val-color--text);
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.menu__nav .menu__header .menu__close {
|
||||
font-size: 2.25rem;
|
||||
border-left: 1px solid var(--val-menu--color-border);
|
||||
}
|
||||
.menu__nav .menu__header .menu__back {
|
||||
font-size: 1.25rem;
|
||||
border-right: 1px solid var(--val-menu--color-border);
|
||||
display: none;
|
||||
}
|
||||
.menu__nav .menu__header.active .menu__back {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.menu__nav .menu__list {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.menu__overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 9098;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
background: rgba(0, 0, 0, 0.55);
|
||||
transition: all 0.5s ease-in-out;
|
||||
}
|
||||
.menu__overlay.active {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
|
||||
/* ANIMATIONS */
|
||||
|
||||
@keyframes slideLeft {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateX(100%);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateX(0%);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideRight {
|
||||
0% {
|
||||
opacity: 1;
|
||||
transform: translateX(0%);
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
transform: translateX(100%);
|
||||
}
|
||||
}
|
211
static/css/root.css
Normal file
211
static/css/root.css
Normal file
|
@ -0,0 +1,211 @@
|
|||
:root {
|
||||
--val-font-sans: system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";
|
||||
--val-font-serif: "Lora","georgia",serif;
|
||||
--val-font-monospace: SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;
|
||||
--val-font-family: var(--val-font-sans);
|
||||
|
||||
/* Font size */
|
||||
--val-fs--x3l: 2.5rem;
|
||||
--val-fs--x2l: 2rem;
|
||||
--val-fs--xl: 1.75rem;
|
||||
--val-fs--l: 1.5rem;
|
||||
--val-fs--m: 1.25rem;
|
||||
--val-fs--base: 1rem;
|
||||
--val-fs--s: 0.875rem;
|
||||
--val-fs--xs: 0.75rem;
|
||||
--val-fs--x2s: 0.5625rem;
|
||||
--val-fs--x3s: 0.375rem;
|
||||
|
||||
/* Font weight */
|
||||
--val-fw--light: 300;
|
||||
--val-fw--base: 400;
|
||||
--val-fw--bold: 500;
|
||||
|
||||
/* Line height */
|
||||
--val-lh--base: 1.5;
|
||||
--val-lh--header: 1.2;
|
||||
|
||||
--val-max-width: 90rem;
|
||||
/*
|
||||
--val-color-rgb: 33,37,41;
|
||||
--val-main--bg-rgb: 255,255,255;
|
||||
--val-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));
|
||||
|
||||
--line-height-base: 1.6875rem;
|
||||
--line-height-s: 1.125rem;
|
||||
--max-bg-color: 98.125rem;
|
||||
*/
|
||||
--val-gap: 1.125rem;
|
||||
/*
|
||||
--content-left: 5.625rem;
|
||||
--site-header-height-wide: var(--val-gap10);
|
||||
--container-padding: var(--val-gap);
|
||||
*/
|
||||
}
|
||||
/*
|
||||
@media (min-width: 75rem) {
|
||||
:root {
|
||||
--container-padding:var(--val-gap2);
|
||||
}
|
||||
}
|
||||
|
||||
:root {
|
||||
--scrollbar-width: 0px;
|
||||
--grid-col-count: 6;
|
||||
--grid-gap: var(--val-gap);
|
||||
--grid-gap-count: calc(var(--grid-col-count) - 1);
|
||||
--grid-full-width: calc(100vw - var(--val-gap2) - var(--scrollbar-width));
|
||||
--grid-col-width: calc((var(--grid-full-width) - (var(--grid-gap-count) * var(--grid-gap))) / var(--grid-col-count));
|
||||
}
|
||||
|
||||
@media (min-width: 43.75rem) {
|
||||
:root {
|
||||
--grid-col-count:14;
|
||||
--grid-gap: var(--val-gap2);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 62.5rem) {
|
||||
:root {
|
||||
--scrollbar-width:0.9375rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 75rem) {
|
||||
:root {
|
||||
--grid-full-width:calc(100vw - var(--scrollbar-width) - var(--content-left) - var(--val-gap4));
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 90rem) {
|
||||
:root {
|
||||
--grid-full-width:calc(var(--max-width) - var(--val-gap4));
|
||||
}
|
||||
}
|
||||
*/
|
||||
:root {
|
||||
--val-gap-0-15: calc(0.15 * var(--val-gap));
|
||||
--val-gap-0-25: calc(0.25 * var(--val-gap));
|
||||
--val-gap-0-35: calc(0.35 * var(--val-gap));
|
||||
--val-gap-0-5: calc(0.5 * var(--val-gap));
|
||||
--val-gap-0-75: calc(0.75 * var(--val-gap));
|
||||
--val-gap-1-5: calc(1.5 * var(--val-gap));
|
||||
--val-gap-2: calc(2 * var(--val-gap));
|
||||
|
||||
--primary-hue: 216;
|
||||
--primary-sat: 60%;
|
||||
--val-color--primary: hsl(var(--primary-hue), var(--primary-sat), 50%);
|
||||
--val-color--primary-light: hsl(var(--primary-hue), var(--primary-sat), 60%);
|
||||
--val-color--primary-dark: hsl(var(--primary-hue), var(--primary-sat), 40%);
|
||||
--val-color--primary-link: hsl(var(--primary-hue), var(--primary-sat), 55%);
|
||||
--val-color--primary-link-hover: hsl(var(--primary-hue), var(--primary-sat), 30%);
|
||||
--val-color--primary-link-active: hsl(var(--primary-hue), var(--primary-sat), 70%);
|
||||
|
||||
--info-hue: 190;
|
||||
--info-sat: 90%;
|
||||
--val-color--info: hsl(var(--info-hue), var(--info-sat), 54%);
|
||||
--val-color--info-light: hsl(var(--info-hue), var(--info-sat), 70%);
|
||||
--val-color--info-dark: hsl(var(--info-hue), var(--info-sat), 45%);
|
||||
--val-color--info-link: hsl(var(--info-hue), var(--info-sat), 30%);
|
||||
--val-color--info-link-hover: hsl(var(--info-hue), var(--info-sat), 20%);
|
||||
--val-color--info-link-active: hsl(var(--info-hue), var(--info-sat), 40%);
|
||||
|
||||
--success-hue: 150;
|
||||
--success-sat: 50%;
|
||||
--val-color--success: hsl(var(--success-hue), var(--success-sat), 50%);
|
||||
--val-color--success-light: hsl(var(--success-hue), var(--success-sat), 68%);
|
||||
--val-color--success-dark: hsl(var(--success-hue), var(--success-sat), 38%);
|
||||
--val-color--success-link: hsl(var(--success-hue), var(--success-sat), 26%);
|
||||
--val-color--success-link-hover: hsl(var(--success-hue), var(--success-sat), 18%);
|
||||
--val-color--success-link-active: hsl(var(--success-hue), var(--success-sat), 36%);
|
||||
|
||||
--warning-hue: 44;
|
||||
--warning-sat: 100%;
|
||||
--val-color--warning: hsl(var(--warning-hue), var(--warning-sat), 50%);
|
||||
--val-color--warning-light: hsl(var(--warning-hue), var(--warning-sat), 60%);
|
||||
--val-color--warning-dark: hsl(var(--warning-hue), var(--warning-sat), 40%);
|
||||
--val-color--warning-link: hsl(var(--warning-hue), var(--warning-sat), 30%);
|
||||
--val-color--warning-link-hover: hsl(var(--warning-hue), var(--warning-sat), 20%);
|
||||
--val-color--warning-link-active: hsl(var(--warning-hue), var(--warning-sat), 38%);
|
||||
|
||||
--danger-hue: 348;
|
||||
--danger-sat: 86%;
|
||||
--val-color--danger: hsl(var(--danger-hue), var(--danger-sat), 50%);
|
||||
--val-color--danger-light: hsl(var(--danger-hue), var(--danger-sat), 60%);
|
||||
--val-color--danger-dark: hsl(var(--danger-hue), var(--danger-sat), 35%);
|
||||
--val-color--danger-link: hsl(var(--danger-hue), var(--danger-sat), 25%);
|
||||
--val-color--danger-link-hover: hsl(var(--danger-hue), var(--danger-sat), 10%);
|
||||
--val-color--danger-link-active: hsl(var(--danger-hue), var(--danger-sat), 30%);
|
||||
|
||||
--light-hue: 0;
|
||||
--light-sat: 0%;
|
||||
--val-color--light: hsl(var(--light-hue), var(--light-sat), 96%);
|
||||
--val-color--light-light: hsl(var(--light-hue), var(--light-sat), 98%);
|
||||
--val-color--light-dark: hsl(var(--light-hue), var(--light-sat), 92%);
|
||||
|
||||
--dark-hue: 0;
|
||||
--dark-sat: 0%;
|
||||
--val-color--dark: hsl(var(--dark-hue), var(--dark-sat), 25%);
|
||||
--val-color--dark-light: hsl(var(--dark-hue), var(--dark-sat), 40%);
|
||||
--val-color--dark-dark: hsl(var(--dark-hue), var(--dark-sat), 8%);
|
||||
--val-color--dark-link: hsl(var(--dark-hue), var(--dark-sat), 90%);
|
||||
--val-color--dark-link-hover: hsl(var(--dark-hue), var(--dark-sat), 100%);
|
||||
--val-color--dark-link-active: hsl(var(--dark-hue), var(--dark-sat), 70%);
|
||||
|
||||
|
||||
|
||||
|
||||
--gray-hue: 201;
|
||||
--gray-sat: 15%;
|
||||
--val-color--gray-5: hsl(var(--gray-hue), var(--gray-sat), 5%);
|
||||
--val-color--gray-10: hsl(var(--gray-hue), var(--gray-sat) ,11%);
|
||||
--val-color--gray-20: hsl(var(--gray-hue), var(--gray-sat),20%);
|
||||
--val-color--gray-45: hsl(var(--gray-hue), var(--gray-sat), 44%);
|
||||
--val-color--gray-60: hsl(var(--gray-hue), var(--gray-sat), 57%);
|
||||
--val-color--gray-65: hsl(var(--gray-hue), var(--gray-sat), 63%);
|
||||
--val-color--gray-70: hsl(var(--gray-hue), var(--gray-sat), 72%);
|
||||
--val-color--gray-90: hsl(var(--gray-hue), var(--gray-sat), 88%);
|
||||
--val-color--gray-95: hsl(var(--gray-hue), var(--gray-sat), 93%);
|
||||
--val-color--gray-100: hsl(var(--gray-hue), var(--gray-sat), 97%);
|
||||
|
||||
|
||||
|
||||
|
||||
--val-color--bg: #fafafa;
|
||||
--val-color--text: #212529;
|
||||
--val-color--white: #fff;
|
||||
|
||||
/*
|
||||
|
||||
|
||||
--color-text-neutral-soft: var(--color--gray-45);
|
||||
--color-text-neutral-medium: var(--color--gray-20);
|
||||
--color-text-neutral-loud: var(--color--gray-5);
|
||||
--color-text-primary-medium: var(--val-color--primary-40);
|
||||
--color-text-primary-loud: var(--val-color--primary-30);
|
||||
--color--black: #000;
|
||||
*/
|
||||
/*
|
||||
--color--red: #e33f1e;
|
||||
--color--gold: #fdca40;
|
||||
--color--green: #3fa21c;
|
||||
--header-height-wide-when-fixed: calc(6 * var(--val-gap));
|
||||
--mobile-nav-width: 31.25rem;
|
||||
*/
|
||||
--val-border-radius: 0.375rem;
|
||||
|
||||
/* Menu component */
|
||||
--val-menu--color-bg: var(--val-color--bg);
|
||||
--val-menu--color-highlight: #e91e63;
|
||||
--val-menu--color-border: rgba(0, 0, 0, 0.1);
|
||||
--val-menu--color-shadow: rgba(0, 0, 0, 0.06);
|
||||
--val-menu--line-padding: 0.625rem;
|
||||
--val-menu--line-height: calc(1.875rem + 1px);
|
||||
--val-menu--item-height: calc(var(--val-menu--line-padding) + var(--val-menu--line-height));
|
||||
--val-menu--item-width-min: 14rem;
|
||||
--val-menu--item-width-max: 20rem;
|
||||
--val-menu--item-gap: 1rem;
|
||||
--val-menu--border-radius: 0.625rem;
|
||||
--val-menu--trigger-width: var(--val-menu--item-height);
|
||||
--val-menu--side-width: 20rem;
|
||||
}
|
94
static/js/menu.js
Normal file
94
static/js/menu.js
Normal file
|
@ -0,0 +1,94 @@
|
|||
function menu__showChildren(nav, children) {
|
||||
let submenu = children[0].querySelector('.menu__subs');
|
||||
submenu.classList.add('active');
|
||||
submenu.style.animation = 'slideLeft 0.5s ease forwards';
|
||||
|
||||
let title = children[0].querySelector('i').parentNode.childNodes[0].textContent;
|
||||
nav.querySelector('.menu__title').innerHTML = title;
|
||||
nav.querySelector('.menu__header').classList.add('active');
|
||||
}
|
||||
|
||||
function menu__hideChildren(nav, children) {
|
||||
let submenu = children[0].querySelector('.menu__subs');
|
||||
submenu.style.animation = 'slideRight 0.5s ease forwards';
|
||||
setTimeout(() => {
|
||||
submenu.classList.remove('active');
|
||||
submenu.style.removeProperty('animation');
|
||||
}, 300);
|
||||
|
||||
children.shift();
|
||||
if (children.length > 0) {
|
||||
let title = children[0].querySelector('i').parentNode.childNodes[0].textContent;
|
||||
nav.querySelector('.menu__title').innerHTML = title;
|
||||
} else {
|
||||
nav.querySelector('.menu__header').classList.remove('active');
|
||||
nav.querySelector('.menu__title').innerHTML = '';
|
||||
}
|
||||
}
|
||||
|
||||
function menu__toggle(nav, overlay) {
|
||||
nav.classList.toggle('active');
|
||||
overlay.classList.toggle('active');
|
||||
}
|
||||
|
||||
function menu__reset(menu, nav, overlay) {
|
||||
menu__toggle(nav, overlay);
|
||||
setTimeout(() => {
|
||||
nav.querySelector('.menu__header').classList.remove('active');
|
||||
nav.querySelector('.menu__title').innerHTML = '';
|
||||
menu.querySelectorAll('.menu__subs').forEach(submenu => {
|
||||
submenu.classList.remove('active');
|
||||
submenu.style.removeProperty('animation');
|
||||
});
|
||||
}, 300);
|
||||
return [];
|
||||
}
|
||||
|
||||
document.querySelectorAll('.menu').forEach(menu => {
|
||||
|
||||
let menuChildren = [];
|
||||
const menuNav = menu.querySelector('.menu__nav');
|
||||
const menuOverlay = menu.querySelector('.menu__overlay');
|
||||
|
||||
menu.querySelector('.menu__list').addEventListener('click', (e) => {
|
||||
if (menuNav.classList.contains('active')) {
|
||||
let target = e.target.closest('.menu__children');
|
||||
if (target && target != menuChildren[0]) {
|
||||
menuChildren.unshift(target);
|
||||
menu__showChildren(menuNav, menuChildren);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
menu.querySelector('.menu__back').addEventListener('click', () => {
|
||||
menu__hideChildren(menuNav, menuChildren);
|
||||
});
|
||||
|
||||
menu.querySelector('.menu__close').addEventListener('click', () => {
|
||||
menuChildren = menu__reset(menu, menuNav, menuOverlay);
|
||||
});
|
||||
|
||||
menu.querySelectorAll('.menu__link > a[target="_blank"]').forEach(link => {
|
||||
link.addEventListener('click', (e) => {
|
||||
menuChildren = menu__reset(menu, menuNav, menuOverlay);
|
||||
e.target.blur();
|
||||
});
|
||||
});
|
||||
|
||||
menu.querySelector('.menu__trigger').addEventListener('click', () => {
|
||||
menu__toggle(menuNav, menuOverlay);
|
||||
});
|
||||
|
||||
menuOverlay.addEventListener('click', () => {
|
||||
menu__toggle(menuNav, menuOverlay);
|
||||
});
|
||||
|
||||
window.onresize = function () {
|
||||
if (menuNav.classList.contains('active')) {
|
||||
var fontSizeRoot = parseFloat(getComputedStyle(document.documentElement).fontSize);
|
||||
if (this.innerWidth >= 62 * fontSizeRoot) {
|
||||
menuChildren = menu__reset(menu, menuNav, menuOverlay);
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue