Add initial work on responsive nav and customisable layout

This commit is contained in:
Andrew Kingston 2021-06-14 16:19:49 +01:00
parent c451f54289
commit a7aca00c99
4 changed files with 268 additions and 62 deletions

View File

@ -34,6 +34,7 @@
"gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc",
"dependencies": {
"@budibase/bbui": "^0.9.47",
"@spectrum-css/link": "^3.1.3",
"@spectrum-css/page": "^3.0.1",
"@spectrum-css/vars": "^3.0.1",
"apexcharts": "^3.22.1",

View File

@ -1,92 +1,298 @@
<script>
import Navigation from "./Navigation.svelte"
import { ActionButton } from "@budibase/bbui"
import { getContext } from "svelte"
import { ActionButton, Heading } from "@budibase/bbui"
const { styleable, transition } = getContext("sdk")
const { styleable, linkable } = getContext("sdk")
const component = getContext("component")
export let title = ""
export let hideTitle = false
export let logoUrl = "https://i.imgur.com/Xhdt1YP.png"
export let hideLogo = false
export let navigation = "Top"
export let links = [
{ text: "Some Text", url: "/" },
{ text: "Some Text", url: "/" },
]
const navigationTypes = new Map([
["Top", "top"],
["Left", "left"],
["None", ""],
])
const navigationClasses = {
Top: "top",
Left: "left",
None: "none",
}
$: type = navigationClasses[navigation] || "none"
let mobileOpen = false
</script>
<div
class="container {navigationTypes.get(navigation)}"
in:transition={{ type: $component.transition }}
use:styleable={$component.styles}
>
{#if navigationTypes.get(navigation)}
<div class="nav">
<div class="layout layout--{type}" use:styleable={$component.styles}>
{#if type !== "none"}
<div class="nav-wrapper">
<div class="nav nav--{type}">
<div class="burger">
<ActionButton
quiet
icon="ShowMenu"
on:click={() => (mobileOpen = !mobileOpen)}
/>
</div>
<div class="logo">
{#if !hideLogo}
<img src="https://i.imgur.com/Xhdt1YP.png" alt={title} height="48" />
<img src="https://i.imgur.com/Xhdt1YP.png" alt={title} />
{/if}
{#if !hideTitle}
<span>{title}</span>
<Heading>{title}</Heading>
{/if}
</div>
<div class="links">
<Navigation {navigation} {links} />
</div>
<div class="portal">
<ActionButton quiet icon="Apps" on:click />
</div>
<div
class="mobile-click-handler"
class:visible={mobileOpen}
on:click={() => (mobileOpen = false)}
/>
<div class="links" class:visible={mobileOpen}>
{#each links as { text, url, external }}
{#if external}
<a class="link" href={url}>{text}</a>
{:else}
<a class="link" href={url} use:linkable>{text}</a>
{/if}
{/each}
<div class="close">
<ActionButton
quiet
icon="Close"
on:click={() => (mobileOpen = false)}
/>
</div>
</div>
</div>
</div>
{/if}
<div class="main-wrapper">
<div class="main">
<slot />
</div>
</div>
</div>
<style>
.container {
/* Main components */
.layout {
display: grid;
grid-gap: 16px;
grid-template-columns: 1fr;
position: relative;
grid-template-columns: 1fr;
min-height: 100%;
}
.nav-wrapper {
display: flex;
flex-direction: row;
justify-content: center;
align-items: stretch;
background: white;
}
.nav {
flex: 1 1 auto;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-areas: "burger logo portal";
justify-content: space-between;
padding: var(--spacing-m);
padding: var(--spacing-xl);
max-width: 1400px;
grid-template-columns: 1fr auto;
}
.links {
grid-area: burger;
.main-wrapper {
display: flex;
flex-direction: row;
justify-content: center;
align-items: stretch;
}
.logo {
display: grid;
justify-items: center;
grid-area: logo;
}
.portal {
grid-area: portal;
display: grid;
justify-content: end;
.main {
flex: 1 1 auto;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
max-width: 1400px;
}
@media (min-width: 600px) {
.nav,
/* Nav components */
.burger {
display: none;
}
.logo {
display: initial;
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
gap: var(--spacing-xl);
grid-column: 1;
}
.top {
.logo img {
height: 48px;
}
.portal {
display: grid;
justify-items: center;
align-items: center;
grid-column: 2;
}
.links {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
gap: var(--spacing-l);
grid-column: 1 / 3;
grid-row: 2;
}
.link {
color: var(--spectrum-alias-text-color);
font-size: var(--spectrum-alias-font-size-default);
font-weight: 600;
transition: color 130ms ease-out;
}
.link:hover {
color: var(--spectrum-alias-text-color-hover);
}
.close {
display: none;
position: absolute;
top: var(--spacing-m);
right: var(--spacing-m);
}
.mobile-click-handler {
display: none;
}
/* Desktop nav overrides */
@media (min-width: 600px) {
.layout--top {
grid-template-columns: 1fr;
grid-template-rows: auto 1fr;
}
.left {
grid-template-columns: 200px 1fr;
.layout--left {
grid-template-columns: auto 1fr;
grid-template-rows: 1fr;
height: 100%;
}
.nav--top {
grid-template-rows: auto auto;
justify-content: space-between;
gap: var(--spacing-xl);
}
.nav--left {
grid-template-rows: auto 1fr;
width: 250px;
gap: var(--spacing-m);
}
.nav--top .links {
margin-top: var(--spacing-l);
}
.nav--left .logo {
gap: var(--spacing-m);
}
.nav--left .logo img {
height: 28px;
}
.nav--left .logo :global(h1) {
font-size: var(--spectrum-global-dimension-font-size-100);
font-weight: 600;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: 0;
flex: 1 1 auto;
}
.nav--left .links {
margin-top: var(--spacing-l);
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
}
.layout--left .main-wrapper {
height: 100%;
overflow: auto;
}
}
/* Mobile nav overrides */
@media (max-width: 600px) {
/* Always use top layout on mobile */
.layout {
grid-template-columns: 1fr;
grid-template-rows: auto 1fr;
}
/* Show close button in drawer */
.close {
display: block;
}
/* Force standard top bar */
.nav {
justify-content: space-between;
gap: var(--spacing-xl);
grid-template-columns: auto auto auto;
grid-template-rows: auto;
padding: var(--spacing-m);
}
.burger {
display: grid;
place-items: center;
grid-column: 1;
}
.logo {
grid-column: 2;
}
.logo img {
height: 36px;
}
.logo :global(h1) {
display: none;
}
.portal {
grid-column: 3;
}
/* Transform links into drawer */
.links {
position: fixed;
top: 0;
left: -250px;
transform: translateX(0);
width: 250px;
transition: transform 0.26s ease-in-out, opacity 0.26s ease-in-out;
height: 100vh;
opacity: 0;
background: white;
z-index: 999;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
padding: var(--spacing-xl);
}
.link {
width: calc(100% - 30px);
}
.links.visible {
opacity: 1;
transform: translateX(250px);
box-shadow: 0 0 40px 20px rgba(0, 0, 0, 0.1);
}
.mobile-click-handler.visible {
position: fixed;
display: block;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: 998;
}
}
</style>

View File

@ -8,8 +8,7 @@
export let navigation
export let links
let mobileOpen = false
export let mobileOpen = false
</script>
<div class="container">

View File

@ -7,10 +7,10 @@
resolved "https://registry.yarnpkg.com/@adobe/spectrum-css-workflow-icons/-/spectrum-css-workflow-icons-1.2.1.tgz#7e2cb3fcfb5c8b12d7275afafbb6ec44913551b4"
integrity sha512-uVgekyBXnOVkxp+CUssjN/gefARtudZC8duEn1vm0lBQFwGRZFlDEzU1QC+aIRWCrD1Z8OgRpmBYlSZ7QS003w==
"@budibase/bbui@^0.9.41":
version "0.9.45"
resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-0.9.45.tgz#c37e84ffbf99daf0ebaa5c19b9109e7222463591"
integrity sha512-+wojKap0kieYoeCqhah1V9EswVbR+Pg1k9Nlw1n/zTsuGtjA0kUH652yKjz7zNzZgFm+s5oYS1CoPieIMehhSw==
"@budibase/bbui@^0.9.47":
version "0.9.47"
resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-0.9.47.tgz#d8664a05203432d522cd91a0bad1cdd8518baf93"
integrity sha512-LXvJCgUSoc4EJKafBaKfUzU4GUOQGmts/8F4V6LTFtTyMZavgq2/KFAgPbR3QeYvidLsshtwop/pQfoszXTQnQ==
dependencies:
"@adobe/spectrum-css-workflow-icons" "^1.2.1"
"@spectrum-css/actionbutton" "^1.0.1"
@ -140,7 +140,7 @@
resolved "https://registry.yarnpkg.com/@spectrum-css/label/-/label-2.0.10.tgz#2368651d7636a19385b5d300cdf6272db1916001"
integrity sha512-xCbtEiQkZIlLdWFikuw7ifDCC21DOC/KMgVrrVJHXFc4KRQe9LTZSqmGF3tovm+CSq1adE59mYoTbojVQ9YuEQ==
"@spectrum-css/link@^3.1.1":
"@spectrum-css/link@^3.1.1", "@spectrum-css/link@^3.1.3":
version "3.1.3"
resolved "https://registry.yarnpkg.com/@spectrum-css/link/-/link-3.1.3.tgz#b0e560a7e0acdb4a2b329b6669696aa3438f5993"
integrity sha512-8Pmy5d73MwKcATTPaj+fSrZYnIw7GmfX40AvpC1xx5LauOxsbUb4AVNp1kn2H8rrOBmxF7czyhoBBhEiv66QUg==