Add initial work on responsive nav and customisable layout
This commit is contained in:
parent
c451f54289
commit
a7aca00c99
|
@ -34,6 +34,7 @@
|
||||||
"gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc",
|
"gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/bbui": "^0.9.47",
|
"@budibase/bbui": "^0.9.47",
|
||||||
|
"@spectrum-css/link": "^3.1.3",
|
||||||
"@spectrum-css/page": "^3.0.1",
|
"@spectrum-css/page": "^3.0.1",
|
||||||
"@spectrum-css/vars": "^3.0.1",
|
"@spectrum-css/vars": "^3.0.1",
|
||||||
"apexcharts": "^3.22.1",
|
"apexcharts": "^3.22.1",
|
||||||
|
|
|
@ -1,92 +1,298 @@
|
||||||
<script>
|
<script>
|
||||||
import Navigation from "./Navigation.svelte"
|
|
||||||
import { ActionButton } from "@budibase/bbui"
|
|
||||||
import { getContext } from "svelte"
|
import { getContext } from "svelte"
|
||||||
|
import { ActionButton, Heading } from "@budibase/bbui"
|
||||||
|
|
||||||
const { styleable, transition } = getContext("sdk")
|
const { styleable, linkable } = getContext("sdk")
|
||||||
const component = getContext("component")
|
const component = getContext("component")
|
||||||
|
|
||||||
export let title = ""
|
export let title = ""
|
||||||
export let hideTitle = false
|
export let hideTitle = false
|
||||||
export let logoUrl = "https://i.imgur.com/Xhdt1YP.png"
|
export let logoUrl = "https://i.imgur.com/Xhdt1YP.png"
|
||||||
export let hideLogo = false
|
export let hideLogo = false
|
||||||
export let navigation = "Top"
|
export let navigation = "Top"
|
||||||
|
|
||||||
export let links = [
|
export let links = [
|
||||||
{ text: "Some Text", url: "/" },
|
{ text: "Some Text", url: "/" },
|
||||||
{ text: "Some Text", url: "/" },
|
{ text: "Some Text", url: "/" },
|
||||||
]
|
]
|
||||||
const navigationTypes = new Map([
|
|
||||||
["Top", "top"],
|
const navigationClasses = {
|
||||||
["Left", "left"],
|
Top: "top",
|
||||||
["None", ""],
|
Left: "left",
|
||||||
])
|
None: "none",
|
||||||
|
}
|
||||||
|
|
||||||
|
$: type = navigationClasses[navigation] || "none"
|
||||||
|
let mobileOpen = false
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div class="layout layout--{type}" use:styleable={$component.styles}>
|
||||||
class="container {navigationTypes.get(navigation)}"
|
{#if type !== "none"}
|
||||||
in:transition={{ type: $component.transition }}
|
<div class="nav-wrapper">
|
||||||
use:styleable={$component.styles}
|
<div class="nav nav--{type}">
|
||||||
>
|
<div class="burger">
|
||||||
{#if navigationTypes.get(navigation)}
|
<ActionButton
|
||||||
<div class="nav">
|
quiet
|
||||||
|
icon="ShowMenu"
|
||||||
|
on:click={() => (mobileOpen = !mobileOpen)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="logo">
|
<div class="logo">
|
||||||
{#if !hideLogo}
|
{#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}
|
||||||
{#if !hideTitle}
|
{#if !hideTitle}
|
||||||
<span>{title}</span>
|
<Heading>{title}</Heading>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<div class="links">
|
|
||||||
<Navigation {navigation} {links} />
|
|
||||||
</div>
|
|
||||||
<div class="portal">
|
<div class="portal">
|
||||||
<ActionButton quiet icon="Apps" on:click />
|
<ActionButton quiet icon="Apps" on:click />
|
||||||
</div>
|
</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>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
<div class="main-wrapper">
|
||||||
<div class="main">
|
<div class="main">
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.container {
|
/* Main components */
|
||||||
|
.layout {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-gap: 16px;
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
position: relative;
|
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 {
|
.nav {
|
||||||
|
flex: 1 1 auto;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1fr 1fr;
|
padding: var(--spacing-xl);
|
||||||
grid-template-areas: "burger logo portal";
|
max-width: 1400px;
|
||||||
justify-content: space-between;
|
grid-template-columns: 1fr auto;
|
||||||
padding: var(--spacing-m);
|
|
||||||
}
|
}
|
||||||
.links {
|
.main-wrapper {
|
||||||
grid-area: burger;
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: stretch;
|
||||||
}
|
}
|
||||||
.logo {
|
.main {
|
||||||
display: grid;
|
flex: 1 1 auto;
|
||||||
justify-items: center;
|
display: flex;
|
||||||
grid-area: logo;
|
flex-direction: column;
|
||||||
}
|
justify-content: flex-start;
|
||||||
.portal {
|
align-items: stretch;
|
||||||
grid-area: portal;
|
max-width: 1400px;
|
||||||
display: grid;
|
|
||||||
justify-content: end;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 600px) {
|
/* Nav components */
|
||||||
.nav,
|
.burger {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
.logo {
|
.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-columns: 1fr;
|
||||||
|
grid-template-rows: auto 1fr;
|
||||||
}
|
}
|
||||||
.left {
|
.layout--left {
|
||||||
grid-template-columns: 200px 1fr;
|
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>
|
</style>
|
||||||
|
|
|
@ -8,8 +8,7 @@
|
||||||
|
|
||||||
export let navigation
|
export let navigation
|
||||||
export let links
|
export let links
|
||||||
|
export let mobileOpen = false
|
||||||
let mobileOpen = false
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
|
|
@ -7,10 +7,10 @@
|
||||||
resolved "https://registry.yarnpkg.com/@adobe/spectrum-css-workflow-icons/-/spectrum-css-workflow-icons-1.2.1.tgz#7e2cb3fcfb5c8b12d7275afafbb6ec44913551b4"
|
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==
|
integrity sha512-uVgekyBXnOVkxp+CUssjN/gefARtudZC8duEn1vm0lBQFwGRZFlDEzU1QC+aIRWCrD1Z8OgRpmBYlSZ7QS003w==
|
||||||
|
|
||||||
"@budibase/bbui@^0.9.41":
|
"@budibase/bbui@^0.9.47":
|
||||||
version "0.9.45"
|
version "0.9.47"
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-0.9.45.tgz#c37e84ffbf99daf0ebaa5c19b9109e7222463591"
|
resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-0.9.47.tgz#d8664a05203432d522cd91a0bad1cdd8518baf93"
|
||||||
integrity sha512-+wojKap0kieYoeCqhah1V9EswVbR+Pg1k9Nlw1n/zTsuGtjA0kUH652yKjz7zNzZgFm+s5oYS1CoPieIMehhSw==
|
integrity sha512-LXvJCgUSoc4EJKafBaKfUzU4GUOQGmts/8F4V6LTFtTyMZavgq2/KFAgPbR3QeYvidLsshtwop/pQfoszXTQnQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@adobe/spectrum-css-workflow-icons" "^1.2.1"
|
"@adobe/spectrum-css-workflow-icons" "^1.2.1"
|
||||||
"@spectrum-css/actionbutton" "^1.0.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"
|
resolved "https://registry.yarnpkg.com/@spectrum-css/label/-/label-2.0.10.tgz#2368651d7636a19385b5d300cdf6272db1916001"
|
||||||
integrity sha512-xCbtEiQkZIlLdWFikuw7ifDCC21DOC/KMgVrrVJHXFc4KRQe9LTZSqmGF3tovm+CSq1adE59mYoTbojVQ9YuEQ==
|
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"
|
version "3.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/@spectrum-css/link/-/link-3.1.3.tgz#b0e560a7e0acdb4a2b329b6669696aa3438f5993"
|
resolved "https://registry.yarnpkg.com/@spectrum-css/link/-/link-3.1.3.tgz#b0e560a7e0acdb4a2b329b6669696aa3438f5993"
|
||||||
integrity sha512-8Pmy5d73MwKcATTPaj+fSrZYnIw7GmfX40AvpC1xx5LauOxsbUb4AVNp1kn2H8rrOBmxF7czyhoBBhEiv66QUg==
|
integrity sha512-8Pmy5d73MwKcATTPaj+fSrZYnIw7GmfX40AvpC1xx5LauOxsbUb4AVNp1kn2H8rrOBmxF7czyhoBBhEiv66QUg==
|
||||||
|
|
Loading…
Reference in New Issue