New Nav component (#11266)
This commit is contained in:
parent
d32e9d58a0
commit
e3cfdd537a
7 changed files with 351 additions and 34 deletions
21
packages/builder/src/components/design/Pane.svelte
Normal file
21
packages/builder/src/components/design/Pane.svelte
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<script>
|
||||||
|
import { getContext } from "svelte"
|
||||||
|
|
||||||
|
export let title
|
||||||
|
|
||||||
|
const createPaneStore = getContext("createPaneStore")
|
||||||
|
|
||||||
|
$: paneStore = createPaneStore(title)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if $paneStore}
|
||||||
|
<div class="pane">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.pane {
|
||||||
|
padding: 13px;
|
||||||
|
}
|
||||||
|
</style>
|
112
packages/builder/src/components/design/RightPanel.svelte
Normal file
112
packages/builder/src/components/design/RightPanel.svelte
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
<script>
|
||||||
|
import { setContext } from "svelte"
|
||||||
|
import { writable, get } from "svelte/store"
|
||||||
|
import { Icon, Body, Button } from "@budibase/bbui"
|
||||||
|
|
||||||
|
export let icon
|
||||||
|
export let title
|
||||||
|
|
||||||
|
let panes = {}
|
||||||
|
let selectedPaneIdStore = writable(null)
|
||||||
|
|
||||||
|
const createPaneStore = pane => {
|
||||||
|
const id = crypto.randomUUID()
|
||||||
|
|
||||||
|
return {
|
||||||
|
subscribe: callback => {
|
||||||
|
panes[id] = pane
|
||||||
|
|
||||||
|
if (get(selectedPaneIdStore) === null) {
|
||||||
|
selectedPaneIdStore.set(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
const unsubscribeSelectedPaneIdStore = selectedPaneIdStore.subscribe(
|
||||||
|
selectedPaneId => callback(selectedPaneId === id)
|
||||||
|
)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
delete panes[id]
|
||||||
|
panes = panes
|
||||||
|
unsubscribeSelectedPaneIdStore()
|
||||||
|
|
||||||
|
if (get(selectedPaneIdStore) === id) {
|
||||||
|
const ids = Object.keys(panes)
|
||||||
|
|
||||||
|
if (ids.length > 0) {
|
||||||
|
selectedPaneIdStore.set(ids[0])
|
||||||
|
} else {
|
||||||
|
selectedPaneIdStore.set(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setContext("createPaneStore", createPaneStore)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="panel">
|
||||||
|
<div class="header">
|
||||||
|
<div class="icon">
|
||||||
|
<Icon name={icon} />
|
||||||
|
</div>
|
||||||
|
<Body>{title}</Body>
|
||||||
|
</div>
|
||||||
|
<div class="controls">
|
||||||
|
{#each Object.entries(panes) as [id, pane]}
|
||||||
|
<div class="button" class:active={$selectedPaneIdStore === id}>
|
||||||
|
<Button on:click={() => selectedPaneIdStore.set(id)}>{pane}</Button>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
<div class="divider" />
|
||||||
|
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.panel {
|
||||||
|
width: 310px;
|
||||||
|
background: var(--background);
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
padding: 16px 14px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
color: var(--grey-6);
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls {
|
||||||
|
padding: 0 14px 16px;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button:first-child {
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button :global(button) {
|
||||||
|
border-radius: 4px;
|
||||||
|
border: none;
|
||||||
|
background-color: var(--grey-1);
|
||||||
|
color: var(--ink);
|
||||||
|
}
|
||||||
|
|
||||||
|
.button :global(button):hover {
|
||||||
|
background-color: var(--grey-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.button.active :global(button) {
|
||||||
|
background-color: var(--grey-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.divider {
|
||||||
|
border-top: 1px solid var(--grey-3);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,33 +0,0 @@
|
||||||
<script>
|
|
||||||
import Panel from "components/design/Panel.svelte"
|
|
||||||
import { Body, Layout, Banner } from "@budibase/bbui"
|
|
||||||
import { selectedScreen, store } from "builderStore"
|
|
||||||
import { get } from "svelte/store"
|
|
||||||
|
|
||||||
const removeCustomLayout = async () => {
|
|
||||||
return store.actions.screens.removeCustomLayout(get(selectedScreen))
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<Panel borderLeft title="Navigation" icon="InfoOutline" wide>
|
|
||||||
<Layout paddingX="L" paddingY="XL" gap="S">
|
|
||||||
{#if $selectedScreen.layoutId}
|
|
||||||
<Banner
|
|
||||||
type="warning"
|
|
||||||
extraButtonText="Detach custom layout"
|
|
||||||
extraButtonAction={removeCustomLayout}
|
|
||||||
showCloseButton={false}
|
|
||||||
>
|
|
||||||
You can't preview your navigation settings using this screen as it uses
|
|
||||||
a custom layout, which is deprecated
|
|
||||||
</Banner>
|
|
||||||
{/if}
|
|
||||||
<Body size="S">
|
|
||||||
Your navigation is configured for all the screens within your app.
|
|
||||||
</Body>
|
|
||||||
<Body size="S">
|
|
||||||
You can hide and show your navigation for each screen in the screen
|
|
||||||
settings.
|
|
||||||
</Body>
|
|
||||||
</Layout>
|
|
||||||
</Panel>
|
|
|
@ -0,0 +1,176 @@
|
||||||
|
<script>
|
||||||
|
import Pane from "components/design/Pane.svelte"
|
||||||
|
import {
|
||||||
|
ColorPicker,
|
||||||
|
Input,
|
||||||
|
Label,
|
||||||
|
ActionGroup,
|
||||||
|
ActionButton,
|
||||||
|
Checkbox,
|
||||||
|
notifications,
|
||||||
|
Icon,
|
||||||
|
Body,
|
||||||
|
Button,
|
||||||
|
Select,
|
||||||
|
} from "@budibase/bbui"
|
||||||
|
import { DefaultAppTheme } from "constants"
|
||||||
|
import { store } from "builderStore"
|
||||||
|
|
||||||
|
const update = async (key, value) => {
|
||||||
|
try {
|
||||||
|
let navigation = $store.navigation
|
||||||
|
navigation[key] = value
|
||||||
|
await store.actions.navigation.save(navigation)
|
||||||
|
} catch (error) {
|
||||||
|
notifications.error("Error updating navigation settings")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Pane title="Customize">
|
||||||
|
<div class="info">
|
||||||
|
<div class="infoHeader">
|
||||||
|
<Icon name="InfoOutline" />
|
||||||
|
<Body size="S">CHANGES WILL APPLY TO ALL SCREENS</Body>
|
||||||
|
</div>
|
||||||
|
<Body>
|
||||||
|
Your navigation is configured for all the screens within your app.
|
||||||
|
</Body>
|
||||||
|
</div>
|
||||||
|
<div class="configureLinks">
|
||||||
|
<Button cta>Configure Links</Button>
|
||||||
|
</div>
|
||||||
|
<div class="controls">
|
||||||
|
<div class="label">
|
||||||
|
<Label size="M">Position</Label>
|
||||||
|
</div>
|
||||||
|
<ActionGroup quiet>
|
||||||
|
<ActionButton
|
||||||
|
selected={$store.navigation.navigation === "Top"}
|
||||||
|
quiet={$store.navigation.navigation !== "Top"}
|
||||||
|
icon="PaddingTop"
|
||||||
|
on:click={() => update("navigation", "Top")}
|
||||||
|
/>
|
||||||
|
<ActionButton
|
||||||
|
selected={$store.navigation.navigation === "Left"}
|
||||||
|
quiet={$store.navigation.navigation !== "Left"}
|
||||||
|
icon="PaddingLeft"
|
||||||
|
on:click={() => update("navigation", "Left")}
|
||||||
|
/>
|
||||||
|
</ActionGroup>
|
||||||
|
|
||||||
|
{#if $store.navigation.navigation === "Top"}
|
||||||
|
<div class="label">
|
||||||
|
<Label size="M">Sticky header</Label>
|
||||||
|
</div>
|
||||||
|
<Checkbox
|
||||||
|
value={$store.navigation.sticky}
|
||||||
|
on:change={e => update("sticky", e.detail)}
|
||||||
|
/>
|
||||||
|
<div class="label">
|
||||||
|
<Label size="M">Width</Label>
|
||||||
|
</div>
|
||||||
|
<Select
|
||||||
|
options={["Max", "Large", "Medium", "Small"]}
|
||||||
|
plaveholder={null}
|
||||||
|
value={$store.navigation.navWidth}
|
||||||
|
on:change={e => update("navWidth", e.detail)}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
<div class="label">
|
||||||
|
<Label size="M">Show logo</Label>
|
||||||
|
</div>
|
||||||
|
<Checkbox
|
||||||
|
value={!$store.navigation.hideLogo}
|
||||||
|
on:change={e => update("hideLogo", !e.detail)}
|
||||||
|
/>
|
||||||
|
{#if !$store.navigation.hideLogo}
|
||||||
|
<div class="label">
|
||||||
|
<Label size="M">Logo URL</Label>
|
||||||
|
</div>
|
||||||
|
<Input
|
||||||
|
value={$store.navigation.logoUrl}
|
||||||
|
on:change={e => update("logoUrl", e.detail)}
|
||||||
|
updateOnChange={false}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
<div class="label">
|
||||||
|
<Label size="M">Show title</Label>
|
||||||
|
</div>
|
||||||
|
<Checkbox
|
||||||
|
value={!$store.navigation.hideTitle}
|
||||||
|
on:change={e => update("hideTitle", !e.detail)}
|
||||||
|
/>
|
||||||
|
{#if !$store.navigation.hideTitle}
|
||||||
|
<div class="label">
|
||||||
|
<Label size="M">Title</Label>
|
||||||
|
</div>
|
||||||
|
<Input
|
||||||
|
value={$store.navigation.title}
|
||||||
|
on:change={e => update("title", e.detail)}
|
||||||
|
updateOnChange={false}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
<div class="label">
|
||||||
|
<Label>Background</Label>
|
||||||
|
</div>
|
||||||
|
<ColorPicker
|
||||||
|
spectrumTheme={$store.theme}
|
||||||
|
value={$store.navigation.navBackground || DefaultAppTheme.navBackground}
|
||||||
|
on:change={e => update("navBackground", e.detail)}
|
||||||
|
/>
|
||||||
|
<div class="label">
|
||||||
|
<Label>Text</Label>
|
||||||
|
</div>
|
||||||
|
<ColorPicker
|
||||||
|
spectrumTheme={$store.theme}
|
||||||
|
value={$store.navigation.navTextColor || DefaultAppTheme.navTextColor}
|
||||||
|
on:change={e => update("navTextColor", e.detail)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Pane>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.controls {
|
||||||
|
position: relative;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 90px 1fr;
|
||||||
|
align-items: start;
|
||||||
|
transition: background 130ms ease-out, border-color 130ms ease-out;
|
||||||
|
border-left: 4px solid transparent;
|
||||||
|
margin: 0 calc(-1 * var(--spacing-xl));
|
||||||
|
padding: 0 var(--spacing-xl) 0 calc(var(--spacing-xl) - 4px);
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
margin-top: 16px;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
background-color: var(--background-alt);
|
||||||
|
padding: 12px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.infoHeader {
|
||||||
|
display: flex;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.infoHeader :global(svg) {
|
||||||
|
margin-right: 5px;
|
||||||
|
color: var(--grey-6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.infoHeader :global(p) {
|
||||||
|
color: var(--grey-6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.configureLinks :global(button) {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,31 @@
|
||||||
|
<script>
|
||||||
|
import Pane from "components/design/Pane.svelte"
|
||||||
|
import { get } from "svelte/store"
|
||||||
|
import { Toggle, Body } from "@budibase/bbui"
|
||||||
|
import { selectedScreen, store } from "builderStore"
|
||||||
|
|
||||||
|
const updateShowNavigation = async e => {
|
||||||
|
await store.actions.screens.updateSetting(
|
||||||
|
get(selectedScreen),
|
||||||
|
"showNavigation",
|
||||||
|
e.detail
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Pane title="Settings">
|
||||||
|
<div class="toggle">
|
||||||
|
<Toggle
|
||||||
|
on:change={updateShowNavigation}
|
||||||
|
value={$selectedScreen.showNavigation}
|
||||||
|
/>
|
||||||
|
<Body size="S">Show nav on this screen</Body>
|
||||||
|
</div>
|
||||||
|
</Pane>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.toggle {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,10 @@
|
||||||
|
<script>
|
||||||
|
import RightPanel from "components/design/RightPanel.svelte"
|
||||||
|
import CustomizePane from "./CustomizePane.svelte"
|
||||||
|
import SettingsPane from "./SettingsPane.svelte"
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<RightPanel title="Screen" icon="WebPage">
|
||||||
|
<SettingsPane />
|
||||||
|
<CustomizePane />
|
||||||
|
</RightPanel>
|
|
@ -1,6 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import NavigationSettingsPanel from "./_components/NavigationSettingsPanel.svelte"
|
import NavigationSettingsPanel from "./_components/NavigationSettingsPanel.svelte"
|
||||||
import NavigationInfoPanel from "./_components/NavigationInfoPanel.svelte"
|
import NavigationInfoPanel from "./_components/NavigationInfoPanel/index.svelte"
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<NavigationSettingsPanel />
|
<NavigationSettingsPanel />
|
||||||
|
|
Loading…
Reference in a new issue