General application & routing setup, apps menu, links in menus, icons in menus, visual improvements.
This commit is contained in:
parent
97cf06bc7f
commit
3305f09d32
22 changed files with 333 additions and 32 deletions
|
@ -2,7 +2,7 @@ import React, {useState} from "react";
|
||||||
import "../index";
|
import "../index";
|
||||||
import {Checkbox} from "../src/Components/Forms/Checkbox";
|
import {Checkbox} from "../src/Components/Forms/Checkbox";
|
||||||
import { Radio } from "../src/Components/Forms/Radio";
|
import { Radio } from "../src/Components/Forms/Radio";
|
||||||
import {FloppyDisk, TrashSimple, XCircle} from "@phosphor-icons/react";
|
import {AirTrafficControl, Basket, FloppyDisk, House, TrashSimple, XCircle} from "@phosphor-icons/react";
|
||||||
import {Card} from "../src/Components/Card";
|
import {Card} from "../src/Components/Card";
|
||||||
import {PasswordInput} from "../src/Components/Forms/PasswordInput";
|
import {PasswordInput} from "../src/Components/Forms/PasswordInput";
|
||||||
import {RequiredField} from "../src/Components/Forms/RequiredField";
|
import {RequiredField} from "../src/Components/Forms/RequiredField";
|
||||||
|
@ -18,7 +18,10 @@ import {MainMenu} from "../src/Components/Menus/MainMenu";
|
||||||
import {SubmenuFloat} from "../src/Components/Menus/SubmenuFloat";
|
import {SubmenuFloat} from "../src/Components/Menus/SubmenuFloat";
|
||||||
import {Submenu} from "../src/Components/Menus/Submenu";
|
import {Submenu} from "../src/Components/Menus/Submenu";
|
||||||
import {SubmenuItem, SubmenuItemSubmenu} from "../src/Components/Menus/SubmenuItem";
|
import {SubmenuItem, SubmenuItemSubmenu} from "../src/Components/Menus/SubmenuItem";
|
||||||
import {MainMenuItem, MainMenuItemSubmenu} from "../src/Components/Menus/MainMenuItem";
|
import {MainMenuItemSubmenu, MainMenuLink} from "../src/Components/Menus/MainMenuItem";
|
||||||
|
import {AppItem, AppLink, AppsMenu} from "../src/Components/Menus/AppsMenu";
|
||||||
|
import {Application} from "../src/Application/Application";
|
||||||
|
import {Outlet} from "react-router-dom";
|
||||||
|
|
||||||
export function DemoApp()
|
export function DemoApp()
|
||||||
{
|
{
|
||||||
|
@ -27,10 +30,10 @@ export function DemoApp()
|
||||||
const [selected, setSelected] = useState(null);
|
const [selected, setSelected] = useState(null);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className={"app"}>
|
<Application>
|
||||||
<MainMenu>
|
<MainMenu>
|
||||||
<MainMenuItem href={"/"}>Home</MainMenuItem>
|
<MainMenuLink to={"/"}><House /> Home</MainMenuLink>
|
||||||
<MainMenuItem href={"#"}>Test</MainMenuItem>
|
<MainMenuLink to={"/test"}><AirTrafficControl /> Test</MainMenuLink>
|
||||||
<MainMenuItemSubmenu submenu={
|
<MainMenuItemSubmenu submenu={
|
||||||
<Submenu>
|
<Submenu>
|
||||||
<SubmenuItem>Test 1</SubmenuItem>
|
<SubmenuItem>Test 1</SubmenuItem>
|
||||||
|
@ -44,12 +47,12 @@ export function DemoApp()
|
||||||
<SubmenuItem href={"#first-last-choice"}>First last choice</SubmenuItem>
|
<SubmenuItem href={"#first-last-choice"}>First last choice</SubmenuItem>
|
||||||
<SubmenuItem>Another last choice</SubmenuItem>
|
<SubmenuItem>Another last choice</SubmenuItem>
|
||||||
</Submenu>
|
</Submenu>
|
||||||
}>Submenu in submenu</SubmenuItemSubmenu>
|
}><Basket /> Submenu in submenu</SubmenuItemSubmenu>
|
||||||
</Submenu>
|
</Submenu>
|
||||||
}>Submenu</SubmenuItemSubmenu>
|
}><Basket /> Submenu</SubmenuItemSubmenu>
|
||||||
</Submenu>
|
</Submenu>
|
||||||
}>
|
}>
|
||||||
Submenu
|
<Basket /> Submenu
|
||||||
</MainMenuItemSubmenu>
|
</MainMenuItemSubmenu>
|
||||||
</MainMenu>
|
</MainMenu>
|
||||||
|
|
||||||
|
@ -58,12 +61,8 @@ export function DemoApp()
|
||||||
<h2>TODO</h2>
|
<h2>TODO</h2>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>Dropdown menus</li>
|
|
||||||
<li>Main menu</li>
|
|
||||||
<li>Tabs / Apps selectors</li>
|
|
||||||
<li>App steps</li>
|
<li>App steps</li>
|
||||||
<li>Pagination</li>
|
<li>Pagination</li>
|
||||||
<li>Apps</li>
|
|
||||||
<li>Global states</li>
|
<li>Global states</li>
|
||||||
<li>Async</li>
|
<li>Async</li>
|
||||||
<li>Subapps</li>
|
<li>Subapps</li>
|
||||||
|
@ -89,9 +88,9 @@ export function DemoApp()
|
||||||
<button type={"button"}>A cool button</button>
|
<button type={"button"}>A cool button</button>
|
||||||
<a className={"button"} href={"#"}>A link button</a>
|
<a className={"button"} href={"#"}>A link button</a>
|
||||||
<button type={"button"} className={"flat"}>A flat button</button>
|
<button type={"button"} className={"flat"}>A flat button</button>
|
||||||
<button type={"button"} className={"validation"}><FloppyDisk weight={"bold"}/> A validation button</button>
|
<button type={"button"} className={"validation"}><FloppyDisk /> A validation button</button>
|
||||||
<button type={"button"} className={"cancel"}><XCircle weight={"bold"}/> A cancellation button</button>
|
<button type={"button"} className={"cancel"}><XCircle /> A cancellation button</button>
|
||||||
<button type={"button"} className={"delete"}><TrashSimple weight={"bold"}/> A deletion button</button>
|
<button type={"button"} className={"delete"}><TrashSimple /> A deletion button</button>
|
||||||
|
|
||||||
<h2>Forms</h2>
|
<h2>Forms</h2>
|
||||||
|
|
||||||
|
@ -303,15 +302,34 @@ export function DemoApp()
|
||||||
<SubmenuItemSubmenu submenu={
|
<SubmenuItemSubmenu submenu={
|
||||||
<Submenu>
|
<Submenu>
|
||||||
<SubmenuItem>Test A</SubmenuItem>
|
<SubmenuItem>Test A</SubmenuItem>
|
||||||
<SubmenuItem>Test B</SubmenuItem>
|
<SubmenuItem><AirTrafficControl /> Test B</SubmenuItem>
|
||||||
</Submenu>
|
</Submenu>
|
||||||
}>
|
}>
|
||||||
Submenu
|
<Basket /> Submenu
|
||||||
</SubmenuItemSubmenu>
|
</SubmenuItemSubmenu>
|
||||||
</Submenu>
|
</Submenu>
|
||||||
} floatingOptions={{ placement: "right-start" }}>
|
} floatingOptions={{ placement: "right-start" }}>
|
||||||
<button>Submenu on a button</button>
|
<button>Submenu on a button</button>
|
||||||
</SubmenuFloat>
|
</SubmenuFloat>
|
||||||
</main>
|
|
||||||
|
<h2>App selectors</h2>
|
||||||
|
|
||||||
|
<AppsMenu>
|
||||||
|
<AppLink to={"/"}>
|
||||||
|
<House />
|
||||||
|
Home
|
||||||
|
</AppLink>
|
||||||
|
<AppLink to={"/test"}>
|
||||||
|
<AirTrafficControl />
|
||||||
|
Test link
|
||||||
|
</AppLink>
|
||||||
|
<AppItem>
|
||||||
|
<Basket />
|
||||||
|
Test 3
|
||||||
|
</AppItem>
|
||||||
|
</AppsMenu>
|
||||||
|
|
||||||
|
<Outlet />
|
||||||
|
</Application>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
14
demo/NavTest.tsx
Normal file
14
demo/NavTest.tsx
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import React from "react";
|
||||||
|
import {Card} from "../src/Components/Card";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigation test component.
|
||||||
|
*/
|
||||||
|
export function NavTest()
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
<Card>
|
||||||
|
This is a navigation test.
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
|
@ -1,11 +1,32 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import {createRoot} from "react-dom/client";
|
import {createRoot} from "react-dom/client";
|
||||||
import {DemoApp} from "./DemoApp";
|
import {DemoApp} from "./DemoApp";
|
||||||
|
import {createBrowserRouter} from "react-router-dom";
|
||||||
|
import {Kernel} from "../src/Application/Kernel";
|
||||||
|
import {NavTest} from "./NavTest";
|
||||||
|
|
||||||
|
// Router initialization.
|
||||||
|
const router = createBrowserRouter([
|
||||||
|
{
|
||||||
|
path: "/",
|
||||||
|
element: <DemoApp />,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "test",
|
||||||
|
element: <NavTest />,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
const demoApp = document.getElementById("demo-app");
|
const demoApp = document.getElementById("demo-app");
|
||||||
|
|
||||||
const root = createRoot(demoApp);
|
const root = createRoot(demoApp);
|
||||||
|
|
||||||
root.render(<DemoApp />);
|
root.render(<Kernel router={router} footer={
|
||||||
|
<footer>
|
||||||
|
Footer test.
|
||||||
|
</footer>
|
||||||
|
} />);
|
||||||
});
|
});
|
||||||
|
|
|
@ -17,7 +17,8 @@
|
||||||
"@fontsource-variable/source-serif-4": "^5.0.19",
|
"@fontsource-variable/source-serif-4": "^5.0.19",
|
||||||
"@phosphor-icons/react": "^2.1.5",
|
"@phosphor-icons/react": "^2.1.5",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-merge-refs": "^2.1.1"
|
"react-merge-refs": "^2.1.1",
|
||||||
|
"react-router-dom": "^6.24.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/react": "^18.3.3",
|
"@types/react": "^18.3.3",
|
||||||
|
|
13
src/Application/Application.tsx
Normal file
13
src/Application/Application.tsx
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main Kernel UI application.
|
||||||
|
*/
|
||||||
|
export function Application({children}: React.PropsWithChildren<{}>)
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
<main className={"app"}>
|
||||||
|
{children}
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
24
src/Application/Kernel.tsx
Normal file
24
src/Application/Kernel.tsx
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import { IconContext } from "@phosphor-icons/react";
|
||||||
|
import React from "react";
|
||||||
|
import {createBrowserRouter, RouterProvider} from "react-router-dom";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main Kernel UI app component which initializes everything.
|
||||||
|
*/
|
||||||
|
export function Kernel({header, footer, router}: {
|
||||||
|
header?: React.ReactNode;
|
||||||
|
footer?: React.ReactNode;
|
||||||
|
router: ReturnType<typeof createBrowserRouter>;
|
||||||
|
})
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
<IconContext.Provider value={{
|
||||||
|
size: 16,
|
||||||
|
weight: "bold",
|
||||||
|
}}>
|
||||||
|
{header}
|
||||||
|
<RouterProvider router={router} />
|
||||||
|
{footer}
|
||||||
|
</IconContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
|
@ -46,7 +46,7 @@ export function Datepicker({date, onDateSelected, locale, className, ...divProps
|
||||||
onDateSelected(newDate);
|
onDateSelected(newDate);
|
||||||
}, [date, onDateSelected])
|
}, [date, onDateSelected])
|
||||||
}>
|
}>
|
||||||
<CaretLeft weight={"bold"}/>
|
<CaretLeft />
|
||||||
</button>
|
</button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip content={nextMonthName}>
|
<Tooltip content={nextMonthName}>
|
||||||
|
@ -57,7 +57,7 @@ export function Datepicker({date, onDateSelected, locale, className, ...divProps
|
||||||
onDateSelected(newDate);
|
onDateSelected(newDate);
|
||||||
}, [date, onDateSelected])
|
}, [date, onDateSelected])
|
||||||
}>
|
}>
|
||||||
<CaretRight weight={"bold"}/>
|
<CaretRight />
|
||||||
</button>
|
</button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -7,7 +7,7 @@ export function Checkbox({children, className, type, ...inputProps}: React.Props
|
||||||
return (
|
return (
|
||||||
<label className={classes("box", className)}>
|
<label className={classes("box", className)}>
|
||||||
<input type={"checkbox"} {...inputProps} />
|
<input type={"checkbox"} {...inputProps} />
|
||||||
<a className={"button"} tabIndex={-1}><Check weight={"bold"} /></a>
|
<a className={"button"} tabIndex={-1}><Check /></a>
|
||||||
{children}
|
{children}
|
||||||
</label>
|
</label>
|
||||||
);
|
);
|
||||||
|
|
|
@ -15,7 +15,7 @@ export function PasswordInput({children, className, type, ...props}: React.Props
|
||||||
setShowPassword(!showPassword);
|
setShowPassword(!showPassword);
|
||||||
}}>
|
}}>
|
||||||
{
|
{
|
||||||
showPassword ? <EyeSlash weight={"bold"} /> : <Eye weight={"bold"} />
|
showPassword ? <EyeSlash /> : <Eye />
|
||||||
}
|
}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -7,7 +7,7 @@ export function Radio({children, className, type, ...inputProps}: React.PropsWit
|
||||||
return (
|
return (
|
||||||
<label className={classes("box", className)}>
|
<label className={classes("box", className)}>
|
||||||
<input type={"radio"} {...inputProps} />
|
<input type={"radio"} {...inputProps} />
|
||||||
<a className={"button"} tabIndex={-1}><Check weight={"bold"} /></a>
|
<a className={"button"} tabIndex={-1}><Check /></a>
|
||||||
{children}
|
{children}
|
||||||
</label>
|
</label>
|
||||||
);
|
);
|
||||||
|
|
54
src/Components/Menus/AppsMenu.tsx
Normal file
54
src/Components/Menus/AppsMenu.tsx
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
import React from "react";
|
||||||
|
import {classes} from "../../Utils";
|
||||||
|
import {IconContext} from "@phosphor-icons/react";
|
||||||
|
import {NavLink, NavLinkProps} from "react-router-dom";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main apps menu component.
|
||||||
|
*/
|
||||||
|
export function AppsMenu({className, children, ...props}: React.HTMLAttributes<HTMLDivElement>)
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
<IconContext.Consumer>
|
||||||
|
{(value) => (
|
||||||
|
<IconContext.Provider value={Object.assign({}, value, {
|
||||||
|
size: 40,
|
||||||
|
})}>
|
||||||
|
<nav className={classes("apps", "menu", className)} {...props}>
|
||||||
|
<ul>
|
||||||
|
{children}
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</IconContext.Provider>
|
||||||
|
)}
|
||||||
|
</IconContext.Consumer>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component of an app item in apps menu.
|
||||||
|
*/
|
||||||
|
export function AppItem({className, children, ...props}: React.HTMLAttributes<HTMLAnchorElement>)
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
<li>
|
||||||
|
<a className={classes("app", "flat button", className)} {...props}>
|
||||||
|
{children}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component of an app link in apps menu.
|
||||||
|
*/
|
||||||
|
export function AppLink({className, children, ...props}: NavLinkProps & React.HTMLAttributes<HTMLAnchorElement>)
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
<li>
|
||||||
|
<NavLink className={classes("app", "flat button", className)} {...props}>
|
||||||
|
{children}
|
||||||
|
</NavLink>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,7 +1,11 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import {classes, Modify} from "../../Utils";
|
import {classes, Modify} from "../../Utils";
|
||||||
import {SubmenuFloat} from "./SubmenuFloat";
|
import {SubmenuFloat} from "./SubmenuFloat";
|
||||||
|
import {NavLink, NavLinkProps} from "react-router-dom";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main menu item properties.
|
||||||
|
*/
|
||||||
export type MainMenuItemProperties = React.PropsWithChildren<Modify<React.AnchorHTMLAttributes<HTMLAnchorElement>, {
|
export type MainMenuItemProperties = React.PropsWithChildren<Modify<React.AnchorHTMLAttributes<HTMLAnchorElement>, {
|
||||||
}>>;
|
}>>;
|
||||||
|
|
||||||
|
@ -17,6 +21,24 @@ export const MainMenuItem = React.forwardRef<HTMLAnchorElement, MainMenuItemProp
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A main menu item link properties.
|
||||||
|
*/
|
||||||
|
export type MainMenuLinkProperties = React.PropsWithChildren<Modify<NavLinkProps & React.AnchorHTMLAttributes<HTMLAnchorElement>, {
|
||||||
|
}>>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A main menu item link.
|
||||||
|
*/
|
||||||
|
export const MainMenuLink = React.forwardRef<HTMLAnchorElement, MainMenuLinkProperties>(function MainMenuLink({children, ...props}: MainMenuLinkProperties, ref)
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
<li>
|
||||||
|
<NavLink ref={ref} {...props}>{children}</NavLink>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A main menu item that open a submenu.
|
* A main menu item that open a submenu.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import {classes, Modify} from "../../Utils";
|
import {classes, Modify} from "../../Utils";
|
||||||
import {SubmenuFloat} from "./SubmenuFloat";
|
import {SubmenuFloat} from "./SubmenuFloat";
|
||||||
|
import {NavLink, NavLinkProps} from "react-router-dom";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submenu item properties.
|
||||||
|
*/
|
||||||
export type SubmenuItemProperties = React.PropsWithChildren<Modify<React.AnchorHTMLAttributes<HTMLAnchorElement>, {
|
export type SubmenuItemProperties = React.PropsWithChildren<Modify<React.AnchorHTMLAttributes<HTMLAnchorElement>, {
|
||||||
}>>;
|
}>>;
|
||||||
|
|
||||||
|
@ -15,6 +19,22 @@ export const SubmenuItem = React.forwardRef<HTMLAnchorElement, SubmenuItemProper
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A submenu item link properties.
|
||||||
|
*/
|
||||||
|
export type SubmenuLinkProperties = React.PropsWithChildren<Modify<NavLinkProps & React.AnchorHTMLAttributes<HTMLAnchorElement>, {
|
||||||
|
}>>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A submenu item link.
|
||||||
|
*/
|
||||||
|
export const SubmenuLink = React.forwardRef<HTMLAnchorElement, SubmenuLinkProperties>(function SubmenuLink({className, children, ...props}: SubmenuLinkProperties, ref)
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
<NavLink ref={ref} className={classes("item", className)} {...props}>{children}</NavLink>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A submenu item that open a submenu.
|
* A submenu item that open a submenu.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -126,7 +126,7 @@ export function OptionsSuggestions<OptionKey extends keyof any, Option>({options
|
||||||
onClick={() => { onSelected(key, option); }}>
|
onClick={() => { onSelected(key, option); }}>
|
||||||
{(renderOption ?? defaultRenderOption)(option)}
|
{(renderOption ?? defaultRenderOption)(option)}
|
||||||
|
|
||||||
<span className={"selected"}><Check weight={"bold"} /></span>
|
<span className={"selected"}><Check /></span>
|
||||||
</a>
|
</a>
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
@ -205,17 +205,17 @@ export function Select<OptionKey extends keyof any, Option>(
|
||||||
{...props} />
|
{...props} />
|
||||||
</Suggestible>
|
</Suggestible>
|
||||||
|
|
||||||
<a className={"button"} tabIndex={-1}><CaretDown weight={"bold"}/></a>
|
<a className={"button"} tabIndex={-1}><CaretDown /></a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ul className={"selected"}>
|
<ul className={"selected"}>
|
||||||
{ // Showing each selected value.
|
{ // Showing each selected value.
|
||||||
selectedOptions.map(([optionKey, option]) => (
|
selectedOptions.map(([optionKey, option]) => (
|
||||||
<li key={String(optionKey)}>
|
<li key={String(optionKey)}>
|
||||||
<Check weight={"bold"}/>
|
<Check />
|
||||||
<div className={"option"}>{(renderOption ?? defaultRenderOption)(option)}</div>
|
<div className={"option"}>{(renderOption ?? defaultRenderOption)(option)}</div>
|
||||||
<button className={"remove flat"} type={"button"} onClick={() => handleDeselectedOption(optionKey)}>
|
<button className={"remove flat"} type={"button"} onClick={() => handleDeselectedOption(optionKey)}>
|
||||||
<X weight={"bold"}/>
|
<X />
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
))
|
))
|
||||||
|
|
|
@ -85,4 +85,5 @@
|
||||||
|
|
||||||
|
|
||||||
@menu-hover: rgba(255, 255, 255, 0.15); --menu-hover: @menu-hover;
|
@menu-hover: rgba(255, 255, 255, 0.15); --menu-hover: @menu-hover;
|
||||||
|
@menu-active: rgba(0, 0, 0, 0.125); --menu-active: @menu-active;
|
||||||
}
|
}
|
||||||
|
|
|
@ -152,13 +152,13 @@ a.button, button, input[type="submit"], input[type="reset"]
|
||||||
}
|
}
|
||||||
|
|
||||||
svg
|
svg
|
||||||
{ // Icon style.*
|
{ // Icon style.
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-top: -0.2em;
|
margin-top: -0.2em;
|
||||||
margin-right: 0.2em;
|
margin-right: 0.2em;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
> svg:last-of-type
|
&.icon-only svg
|
||||||
{
|
{
|
||||||
margin-right: 0.05em;
|
margin-right: 0.05em;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
|
@import "menus/_apps-menu";
|
||||||
@import "menus/_main-menu";
|
@import "menus/_main-menu";
|
||||||
@import "menus/_submenu";
|
@import "menus/_submenu";
|
||||||
|
|
49
src/styles/components/menus/_apps-menu.less
Normal file
49
src/styles/components/menus/_apps-menu.less
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
nav.apps.menu
|
||||||
|
{
|
||||||
|
> ul
|
||||||
|
{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 0.66em;
|
||||||
|
|
||||||
|
margin: auto;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
list-style: none;
|
||||||
|
|
||||||
|
> li
|
||||||
|
{
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a.app
|
||||||
|
{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
margin: auto;
|
||||||
|
padding: 1em;
|
||||||
|
min-width: 10em;
|
||||||
|
|
||||||
|
font-size: 1.1em;
|
||||||
|
|
||||||
|
svg
|
||||||
|
{
|
||||||
|
display: block;
|
||||||
|
margin: 0 auto 0.5em auto;
|
||||||
|
|
||||||
|
color: var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active
|
||||||
|
{
|
||||||
|
outline: solid 2px var(--primary);
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,13 @@
|
||||||
nav.main.menu
|
nav.main.menu
|
||||||
{
|
{
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
|
||||||
background: var(--primary-gradient);
|
background: var(--primary-gradient);
|
||||||
color: var(--background);
|
color: var(--background);
|
||||||
|
|
||||||
|
z-index: 2;
|
||||||
|
|
||||||
.floating
|
.floating
|
||||||
{
|
{
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
|
@ -47,6 +52,19 @@ nav.main.menu
|
||||||
{
|
{
|
||||||
background: var(--menu-hover);
|
background: var(--menu-hover);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.active
|
||||||
|
{
|
||||||
|
background: var(--menu-active);
|
||||||
|
}
|
||||||
|
|
||||||
|
svg
|
||||||
|
{ // Icon style.
|
||||||
|
display: inline-block;
|
||||||
|
margin-top: -0.2em;
|
||||||
|
margin-right: 0.2em;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,6 +86,11 @@ nav.main.menu
|
||||||
{
|
{
|
||||||
background: var(--menu-hover);
|
background: var(--menu-hover);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.active
|
||||||
|
{
|
||||||
|
background: var(--menu-active);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
}
|
}
|
||||||
|
|
||||||
.submenu
|
> .submenu
|
||||||
{
|
{
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
@ -32,6 +32,14 @@
|
||||||
{
|
{
|
||||||
background: var(--background-darker);
|
background: var(--background-darker);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
svg
|
||||||
|
{ // Icon style.
|
||||||
|
display: inline-block;
|
||||||
|
margin-top: -0.2em;
|
||||||
|
margin-right: 0.2em;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
32
yarn.lock
32
yarn.lock
|
@ -651,6 +651,13 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@remix-run/router@npm:1.17.1":
|
||||||
|
version: 1.17.1
|
||||||
|
resolution: "@remix-run/router@npm:1.17.1"
|
||||||
|
checksum: 10c0/bee1631feb03975b64e1c7b574da432a05095dda2ff0f164c737e4952841a58d7b9861de87bd13a977fd970c74dcf8c558fc2d26c6ec01a9ae9041b1b4430869
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@rollup/pluginutils@npm:^5.1.0":
|
"@rollup/pluginutils@npm:^5.1.0":
|
||||||
version: 5.1.0
|
version: 5.1.0
|
||||||
resolution: "@rollup/pluginutils@npm:5.1.0"
|
resolution: "@rollup/pluginutils@npm:5.1.0"
|
||||||
|
@ -1826,6 +1833,7 @@ __metadata:
|
||||||
react: "npm:^18.3.1"
|
react: "npm:^18.3.1"
|
||||||
react-dom: "npm:^18.3.1"
|
react-dom: "npm:^18.3.1"
|
||||||
react-merge-refs: "npm:^2.1.1"
|
react-merge-refs: "npm:^2.1.1"
|
||||||
|
react-router-dom: "npm:^6.24.1"
|
||||||
typescript: "npm:^5.4.5"
|
typescript: "npm:^5.4.5"
|
||||||
vite: "npm:^5.2.11"
|
vite: "npm:^5.2.11"
|
||||||
vite-plugin-dts: "npm:^3.9.1"
|
vite-plugin-dts: "npm:^3.9.1"
|
||||||
|
@ -2313,6 +2321,30 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"react-router-dom@npm:^6.24.1":
|
||||||
|
version: 6.24.1
|
||||||
|
resolution: "react-router-dom@npm:6.24.1"
|
||||||
|
dependencies:
|
||||||
|
"@remix-run/router": "npm:1.17.1"
|
||||||
|
react-router: "npm:6.24.1"
|
||||||
|
peerDependencies:
|
||||||
|
react: ">=16.8"
|
||||||
|
react-dom: ">=16.8"
|
||||||
|
checksum: 10c0/458c6c539304984c47b0ad8d5d5b1f8859cc0845e47591d530cb4fcb13498f70a89b42bc4daeea55d57cfa08408b453bcf601cabb2c987f554cdcac13805caa8
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"react-router@npm:6.24.1":
|
||||||
|
version: 6.24.1
|
||||||
|
resolution: "react-router@npm:6.24.1"
|
||||||
|
dependencies:
|
||||||
|
"@remix-run/router": "npm:1.17.1"
|
||||||
|
peerDependencies:
|
||||||
|
react: ">=16.8"
|
||||||
|
checksum: 10c0/f50c78ca52c5154ab933c17708125e8bf71ccf2072993a80302526a0a23db9ceac6e36d5c891d62ccd16f13e60cd1b6533a2036523d1b09e0148ac49e34b2e83
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"react@npm:^18.3.1":
|
"react@npm:^18.3.1":
|
||||||
version: 18.3.1
|
version: 18.3.1
|
||||||
resolution: "react@npm:18.3.1"
|
resolution: "react@npm:18.3.1"
|
||||||
|
|
Loading…
Reference in a new issue