fix: Responsive Navbar

Missing Burger menu icon
This commit is contained in:
Jan Eitzinger 2023-08-23 21:15:49 +02:00
parent b7aacd1b33
commit 80be78604f
3 changed files with 281 additions and 98 deletions

View File

@ -14,7 +14,7 @@
"chart.js": "^4.3.3", "chart.js": "^4.3.3",
"graphql": "^16.6.0", "graphql": "^16.6.0",
"svelte-chartjs": "^3.1.2", "svelte-chartjs": "^3.1.2",
"sveltestrap": "^5.10.0", "sveltestrap": "^5.11.1",
"uplot": "^1.6.24", "uplot": "^1.6.24",
"wonka": "^6.3.2" "wonka": "^6.3.2"
}, },
@ -138,9 +138,9 @@
} }
}, },
"node_modules/@rollup/plugin-node-resolve": { "node_modules/@rollup/plugin-node-resolve": {
"version": "15.2.0", "version": "15.2.1",
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.1.tgz",
"integrity": "sha512-mKur03xNGT8O9ODO6FtT43ITGqHWZbKPdVJHZb+iV9QYcdlhUUB0wgknvA4KCUmC5oHJF6O2W1EgmyOQyVUI4Q==", "integrity": "sha512-nsbUg588+GDSu8/NS8T4UAshO6xeaOfINNuXeVHcKV02LJtoRaM1SiOacClw4kws1SFiNhdLGxlbMY9ga/zs/w==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@rollup/pluginutils": "^5.0.1", "@rollup/pluginutils": "^5.0.1",
@ -346,9 +346,9 @@
"dev": true "dev": true
}, },
"node_modules/fsevents": { "node_modules/fsevents": {
"version": "2.3.2", "version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true, "dev": true,
"hasInstallScript": true, "hasInstallScript": true,
"optional": true, "optional": true,
@ -547,9 +547,9 @@
} }
}, },
"node_modules/rollup": { "node_modules/rollup": {
"version": "3.28.0", "version": "3.28.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.28.0.tgz", "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.28.1.tgz",
"integrity": "sha512-d7zhvo1OUY2SXSM6pfNjgD5+d0Nz87CUp4mt8l/GgVP3oBsPwzNvSzyu1me6BSG9JIgWNTVcafIXBIyM8yQ3yw==", "integrity": "sha512-R9OMQmIHJm9znrU3m3cpE8uhN0fGdXiawME7aZIpQqvpS/85+Vt1Hq1/yVIcYfOmaQiHjvXkQAoJukvLpau6Yw==",
"devOptional": true, "devOptional": true,
"bin": { "bin": {
"rollup": "dist/bin/rollup" "rollup": "dist/bin/rollup"

View File

@ -21,7 +21,7 @@
"chart.js": "^4.3.3", "chart.js": "^4.3.3",
"graphql": "^16.6.0", "graphql": "^16.6.0",
"svelte-chartjs": "^3.1.2", "svelte-chartjs": "^3.1.2",
"sveltestrap": "^5.10.0", "sveltestrap": "^5.11.1",
"uplot": "^1.6.24", "uplot": "^1.6.24",
"wonka": "^6.3.2" "wonka": "^6.3.2"
} }

View File

@ -1,110 +1,293 @@
<script> <script>
import { Icon, Button, InputGroup, Input, Collapse, import {
Navbar, NavbarBrand, Nav, NavItem, NavLink, NavbarToggler, Icon,
Dropdown, DropdownToggle, DropdownMenu, DropdownItem, InputGroupText } from 'sveltestrap' Button,
InputGroup,
Input,
Collapse,
Navbar,
NavbarBrand,
Nav,
NavItem,
NavLink,
NavbarToggler,
Dropdown,
DropdownToggle,
DropdownMenu,
DropdownItem,
InputGroupText,
} from "sveltestrap";
export let username // empty string if auth. is disabled, otherwise the username as string export let username; // empty string if auth. is disabled, otherwise the username as string
export let authlevel // Integer export let authlevel; // Integer
export let clusters // array of names export let clusters; // array of names
export let roles // Role Enum-Like export let roles; // Role Enum-Like
let isOpen = false let isOpen = false;
let screenSize;
const userviews = [ const jobsTitle = new Map();
{ title: 'My Jobs', href: `/monitoring/user/${username}`, icon: 'bar-chart-line-fill' }, jobsTitle.set(2, "Job Search");
{ title: `Job Search`, href: '/monitoring/jobs/', icon: 'card-list' }, jobsTitle.set(3, "Managed Jobs");
{ title: 'Tags', href: '/monitoring/tags/', icon: 'tags' } jobsTitle.set(4, "Jobs");
] jobsTitle.set(5, "Jobs");
const usersTitle = new Map();
usersTitle.set(3, "Managed Users");
usersTitle.set(4, "Users");
usersTitle.set(5, "Users");
const managerviews = [ const views = [
{ title: 'My Jobs', href: `/monitoring/user/${username}`, icon: 'bar-chart-line-fill' }, {
{ title: `Managed Jobs`, href: '/monitoring/jobs/', icon: 'card-list' }, title: "My Jobs",
{ title: `Managed Users`, href: '/monitoring/users/', icon: 'people-fill' }, requiredRole: roles.user,
{ title: 'Tags', href: '/monitoring/tags/', icon: 'tags' } href: `/monitoring/user/${username}`,
] icon: "bar-chart-line-fill",
perCluster: false,
const supportviews = [ menu: "none",
{ title: 'My Jobs', href: `/monitoring/user/${username}`, icon: 'bar-chart-line-fill' }, },
{ title: 'Jobs', href: '/monitoring/jobs/', icon: 'card-list' }, {
{ title: 'Users', href: '/monitoring/users/', icon: 'people-fill' }, title: jobsTitle.get(authlevel),
{ title: 'Projects', href: '/monitoring/projects/', icon: 'folder' }, requiredRole: roles.user,
{ title: 'Tags', href: '/monitoring/tags/', icon: 'tags' } href: `/monitoring/jobs`,
] icon: "card-list",
perCluster: false,
const adminviews = [ menu: "none",
{ title: 'My Jobs', href: `/monitoring/user/${username}`, icon: 'bar-chart-line-fill' }, },
{ title: 'Jobs', href: '/monitoring/jobs/', icon: 'card-list' }, {
{ title: 'Users', href: '/monitoring/users/', icon: 'people-fill' }, title: usersTitle.get(authlevel),
{ title: 'Projects', href: '/monitoring/projects/', icon: 'folder' }, requiredRole: roles.manager,
{ title: 'Tags', href: '/monitoring/tags/', icon: 'tags' } href: "/monitoring/users/",
] icon: "people-fill",
perCluster: false,
const viewsPerCluster = [ menu: "Groups",
{ title: 'Analysis', requiredRole: roles.support, href: '/monitoring/analysis/', icon: 'graph-up' }, },
{ title: 'Systems', requiredRole: roles.admin, href: '/monitoring/systems/', icon: 'cpu' }, {
{ title: 'Status', requiredRole: roles.admin, href: '/monitoring/status/', icon: 'cpu' }, title: "Projects",
] requiredRole: roles.support,
href: "/monitoring/projects/",
icon: "folder",
perCluster: false,
menu: "Groups",
},
{
title: "Tags",
requiredRole: roles.user,
href: "/monitoring/tags/",
icon: "tags",
perCluster: false,
menu: "Groups",
},
{
title: "Analysis",
requiredRole: roles.support,
href: "/monitoring/analysis/",
icon: "graph-up",
perCluster: true,
menu: "Stats",
},
{
title: "Nodes",
requiredRole: roles.admin,
href: "/monitoring/systems/",
icon: "cpu",
perCluster: true,
menu: "Groups",
},
{
title: "Status",
requiredRole: roles.admin,
href: "/monitoring/status/",
icon: "cpu",
perCluster: true,
menu: "Stats",
},
];
</script> </script>
<Navbar color="light" light expand="lg" fixed="top"> <svelte:window bind:innerWidth={screenSize} />
<Navbar color="light" light expand="sm" fixed="top">
<NavbarBrand href="/"> <NavbarBrand href="/">
<img alt="ClusterCockpit Logo" src="/img/logo.png" height="25rem"> <img alt="ClusterCockpit Logo" src="/img/logo.png" height="25rem" />
</NavbarBrand> </NavbarBrand>
<NavbarToggler on:click={() => (isOpen = !isOpen)} /> <NavbarToggler on:click={() => (isOpen = !isOpen)} />
<Collapse {isOpen} navbar expand="lg" on:update={({ detail }) => (isOpen = detail.isOpen)}> <Collapse
<Nav pills> {isOpen}
{#if authlevel == roles.admin} navbar
{#each adminviews as item} expand="sm"
<NavLink href={item.href} active={window.location.pathname == item.href}><Icon name={item.icon}/> {item.title}</NavLink> on:update={({ detail }) => (isOpen = detail.isOpen)}
{/each} >
{:else if authlevel == roles.support} <Nav navbar>
{#each supportviews as item} {#if screenSize > 1500}
<NavLink href={item.href} active={window.location.pathname == item.href}><Icon name={item.icon}/> {item.title}</NavLink> {#each views.filter((item) => item.requiredRole <= authlevel) as item}
{/each} {#if !item.perCluster}
{:else if authlevel == roles.manager} <NavLink
{#each managerviews as item} href={item.href}
<NavLink href={item.href} active={window.location.pathname == item.href}><Icon name={item.icon}/> {item.title}</NavLink> active={window.location.pathname == item.href}
{/each} ><Icon name={item.icon} /> {item.title}</NavLink
{:else} <!-- Compatibility: Handle "user role" or "no role" as identical--> >
{#each userviews as item} {:else}
<NavLink href={item.href} active={window.location.pathname == item.href}><Icon name={item.icon}/> {item.title}</NavLink>
{/each}
{/if}
{#each viewsPerCluster.filter(item => item.requiredRole <= authlevel) as item}
<NavItem>
<Dropdown nav inNavbar> <Dropdown nav inNavbar>
<DropdownToggle nav caret> <DropdownToggle nav caret>
<Icon name={item.icon}/> {item.title} <Icon name={item.icon} />
{item.title}
</DropdownToggle> </DropdownToggle>
<DropdownMenu> <DropdownMenu class="dropdown-menu-lg-end">
{#each clusters as cluster} {#each clusters as cluster}
<DropdownItem href={item.href + cluster.name} active={window.location.pathname == item.href + cluster.name}> <DropdownItem
href={item.href + cluster.name}
active={window.location.pathname ==
item.href + cluster.name}
>
{cluster.name} {cluster.name}
</DropdownItem> </DropdownItem>
{/each} {/each}
</DropdownMenu> </DropdownMenu>
</Dropdown> </Dropdown>
</NavItem> {/if}
{/each} {/each}
{:else if screenSize > 1300}
{#each views.filter((item) => item.requiredRole <= authlevel && item.menu != "Stats") as item}
{#if !item.perCluster}
<NavLink
href={item.href}
active={window.location.pathname == item.href}
><Icon name={item.icon} /> {item.title}</NavLink
>
{:else}
<Dropdown nav inNavbar>
<DropdownToggle nav caret>
<Icon name={item.icon} />
{item.title}
</DropdownToggle>
<DropdownMenu class="dropdown-menu-lg-end">
{#each clusters as cluster}
<DropdownItem
href={item.href + cluster.name}
active={window.location.pathname ==
item.href + cluster.name}
>
{cluster.name}
</DropdownItem>
{/each}
</DropdownMenu>
</Dropdown>
{/if}
{/each}
<Dropdown nav>
<DropdownToggle nav caret>
<Icon name="graph-up" />
Stats
</DropdownToggle>
<DropdownMenu class="dropdown-menu-lg-end">
{#each views.filter((item) => item.requiredRole <= authlevel && item.menu == "Stats") as item}
<Dropdown nav inNavbar>
<DropdownToggle nav caret>
<Icon name={item.icon} />
{item.title}
</DropdownToggle>
<DropdownMenu class="dropdown-menu-lg-end">
{#each clusters as cluster}
<DropdownItem
href={item.href + cluster.name}
active={window.location.pathname ==
item.href + cluster.name}
>
{cluster.name}
</DropdownItem>
{/each}
</DropdownMenu>
</Dropdown>
{/each}
</DropdownMenu>
</Dropdown>
{:else}
{#each views.filter((item) => item.requiredRole <= authlevel && item.menu == "none") as item}
<NavLink
href={item.href}
active={window.location.pathname == item.href}
><Icon name={item.icon} /> {item.title}</NavLink
>
{/each}
{#each Array("Groups", "Stats") as menu}
<Dropdown nav>
<DropdownToggle nav caret>
{menu}
</DropdownToggle>
<DropdownMenu class="dropdown-menu-lg-end">
{#each views.filter((item) => item.requiredRole <= authlevel && item.menu == menu) as item}
<Dropdown nav inNavbar>
<DropdownToggle nav caret>
<Icon name={item.icon} />
{item.title}
</DropdownToggle>
<DropdownMenu class="dropdown-menu-lg-end">
{#each clusters as cluster}
<DropdownItem
href={item.href + cluster.name}
active={window.location
.pathname ==
item.href + cluster.name}
>
{cluster.name}
</DropdownItem>
{/each}
</DropdownMenu>
</Dropdown>
{/each}
</DropdownMenu>
</Dropdown>
{/each}
{/if}
</Nav> </Nav>
</Collapse> </Collapse>
<div class="d-flex"> <Nav>
<NavItem>
<form method="GET" action="/search"> <form method="GET" action="/search">
<InputGroup> <InputGroup>
<Input type="text" placeholder="Search 'type:<query>' ..." name="searchId"/> <Input
<Button outline type="submit"><Icon name="search"/></Button> type="text"
<InputGroupText style="cursor:help;" title={(authlevel >= roles.support) ? "Example: 'projectId:a100cd', Types are: jobId | jobName | projectId | arrayJobId | username | name" : "Example: 'jobName:myjob', Types are jobId | jobName | projectId | arrayJobId "}><Icon name="info-circle"/></InputGroupText> placeholder="Search 'type:<query>' ..."
name="searchId"
/>
<Button outline type="submit"><Icon name="search" /></Button
>
<InputGroupText
style="cursor:help;"
title={authlevel >= roles.support
? "Example: 'projectId:a100cd', Types are: jobId | jobName | projectId | arrayJobId | username | name"
: "Example: 'jobName:myjob', Types are jobId | jobName | projectId | arrayJobId "}
><Icon name="info-circle" /></InputGroupText
>
</InputGroup> </InputGroup>
</form> </form>
</NavItem>
{#if username} {#if username}
<NavItem>
<form method="POST" action="/logout"> <form method="POST" action="/logout">
<Button outline color="success" type="submit" style="margin-left: 10px;"> <Button
outline
color="success"
type="submit"
style="margin-left: 10px;"
>
{#if screenSize > 1630}
<Icon name="box-arrow-right" /> Logout {username} <Icon name="box-arrow-right" /> Logout {username}
{:else}
<Icon name="box-arrow-right" />
{/if}
</Button> </Button>
</form> </form>
</NavItem>
{/if} {/if}
<Button outline on:click={() => window.location.href = '/config'} style="margin-left: 10px;"> <NavItem>
<Button
outline
on:click={() => (window.location.href = "/config")}
style="margin-left: 10px;"
>
<Icon name="gear" /> <Icon name="gear" />
</Button> </Button>
</div> </NavItem>
</Nav>
</Navbar> </Navbar>