Initial work on nested nav links
This commit is contained in:
parent
cecd5a1908
commit
9fe027b3db
|
@ -14,7 +14,6 @@
|
||||||
export let key
|
export let key
|
||||||
export let nested
|
export let nested
|
||||||
export let max
|
export let max
|
||||||
export let context
|
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
|
|
|
@ -126,7 +126,7 @@
|
||||||
<div class="right-content">
|
<div class="right-content">
|
||||||
<svelte:component
|
<svelte:component
|
||||||
this={listType}
|
this={listType}
|
||||||
anchor={anchors[draggableItem.item._id]}
|
anchor={anchors[draggableItem.id]}
|
||||||
item={draggableItem.item}
|
item={draggableItem.item}
|
||||||
{...listTypeProps}
|
{...listTypeProps}
|
||||||
on:change={onItemChanged}
|
on:change={onItemChanged}
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
<script>
|
||||||
|
import { Icon, Popover, Layout } from "@budibase/bbui"
|
||||||
|
import { createEventDispatcher, getContext } from "svelte"
|
||||||
|
|
||||||
|
export let anchor
|
||||||
|
export let navItem
|
||||||
|
|
||||||
|
const draggable = getContext("draggable")
|
||||||
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
|
let popover
|
||||||
|
let drawers = []
|
||||||
|
let open = false
|
||||||
|
|
||||||
|
$: console.log(anchor)
|
||||||
|
|
||||||
|
// Auto hide the component when another item is selected
|
||||||
|
$: if (open && $draggable.selected !== navItem.id) {
|
||||||
|
popover.hide()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open automatically if the component is marked as selected
|
||||||
|
$: if (!open && $draggable.selected === navItem.id && popover) {
|
||||||
|
popover.show()
|
||||||
|
open = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateNavItem = async (setting, value) => {
|
||||||
|
dispatch("change", {
|
||||||
|
...navItem,
|
||||||
|
[setting]: value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Icon
|
||||||
|
name="Settings"
|
||||||
|
hoverable
|
||||||
|
size="S"
|
||||||
|
on:click={() => {
|
||||||
|
if (!open) {
|
||||||
|
popover.show()
|
||||||
|
open = true
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Popover
|
||||||
|
bind:this={popover}
|
||||||
|
on:open={() => {
|
||||||
|
drawers = []
|
||||||
|
$draggable.actions.select(navItem.id)
|
||||||
|
}}
|
||||||
|
on:close={() => {
|
||||||
|
open = false
|
||||||
|
if ($draggable.selected === navItem.id) {
|
||||||
|
$draggable.actions.select()
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
{anchor}
|
||||||
|
align="left-outside"
|
||||||
|
showPopover={drawers.length === 0}
|
||||||
|
clickOutsideOverride={drawers.length > 0}
|
||||||
|
maxHeight={600}
|
||||||
|
offset={18}
|
||||||
|
>
|
||||||
|
<span class="popover-wrap">
|
||||||
|
<Layout noPadding />
|
||||||
|
</span>
|
||||||
|
</Popover>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.popover-wrap {
|
||||||
|
background-color: var(--spectrum-alias-background-color-primary);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,30 +0,0 @@
|
||||||
<script>
|
|
||||||
import { Button, Drawer } from "@budibase/bbui"
|
|
||||||
import NavigationLinksDrawer from "./LinksDrawer.svelte"
|
|
||||||
import { cloneDeep } from "lodash/fp"
|
|
||||||
import { navigationStore } from "stores/builder"
|
|
||||||
|
|
||||||
let drawer
|
|
||||||
let links
|
|
||||||
|
|
||||||
const openDrawer = () => {
|
|
||||||
links = cloneDeep($navigationStore.links || [])
|
|
||||||
drawer.show()
|
|
||||||
}
|
|
||||||
|
|
||||||
const save = async () => {
|
|
||||||
let navigation = $navigationStore
|
|
||||||
navigation.links = links
|
|
||||||
await navigationStore.save(navigation)
|
|
||||||
drawer.hide()
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<Button cta on:click={openDrawer}>Configure Links</Button>
|
|
||||||
<Drawer bind:this={drawer} title={"Navigation Links"}>
|
|
||||||
<svelte:fragment slot="description">
|
|
||||||
Configure the links in your navigation bar.
|
|
||||||
</svelte:fragment>
|
|
||||||
<Button cta slot="buttons" on:click={save}>Save</Button>
|
|
||||||
<NavigationLinksDrawer slot="body" bind:links />
|
|
||||||
</Drawer>
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
<script>
|
||||||
|
import EditNavItemPopover from "./EditNavItemPopover.svelte"
|
||||||
|
import { Icon } from "@budibase/bbui"
|
||||||
|
|
||||||
|
export let item
|
||||||
|
export let removeNavItem
|
||||||
|
export let anchor
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="list-item-body">
|
||||||
|
<div class="list-item-left">
|
||||||
|
<EditNavItemPopover {anchor} navItem={item} on:change />
|
||||||
|
<div class="field-label">{item.text}</div>
|
||||||
|
</div>
|
||||||
|
<div class="list-item-right">
|
||||||
|
<Icon
|
||||||
|
size="S"
|
||||||
|
name="Close"
|
||||||
|
hoverable
|
||||||
|
on:click={e => {
|
||||||
|
e.stopPropagation()
|
||||||
|
removeNavItem(item.id)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.field-label {
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.list-item-body,
|
||||||
|
.list-item-left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--spacing-m);
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.list-item-body {
|
||||||
|
margin-top: 8px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
.list-item-right :global(div.spectrum-Switch) {
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
.list-item-body {
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,113 @@
|
||||||
|
<script>
|
||||||
|
import { navigationStore } from "stores/builder"
|
||||||
|
import DraggableList from "components/design/settings/controls/DraggableList/DraggableList.svelte"
|
||||||
|
import NavItem from "./NavItem.svelte"
|
||||||
|
import { generate } from "shortid"
|
||||||
|
import { getSequentialName } from "helpers/duplicate"
|
||||||
|
import { Constants } from "@budibase/frontend-core"
|
||||||
|
|
||||||
|
$: navItems = enrichNavItems($navigationStore.links)
|
||||||
|
$: navItemProps = {
|
||||||
|
removeNavItem,
|
||||||
|
}
|
||||||
|
|
||||||
|
const enrichNavItems = links => {
|
||||||
|
return (links || []).map(link => ({
|
||||||
|
...link,
|
||||||
|
id: link.id || generate(),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
const save = async links => {
|
||||||
|
await navigationStore.save({ ...$navigationStore, links })
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleNavItemUpdate = async e => {
|
||||||
|
const newNavItem = e.detail
|
||||||
|
const newLinks = [...navItems]
|
||||||
|
const idx = newLinks.findIndex(link => {
|
||||||
|
return link.id === newNavItem?.id
|
||||||
|
})
|
||||||
|
if (idx === -1) {
|
||||||
|
newLinks.push(newNavItem)
|
||||||
|
} else {
|
||||||
|
newLinks[idx] = newNavItem
|
||||||
|
}
|
||||||
|
await save(newLinks)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleListUpdate = async e => {
|
||||||
|
await save([...e.detail])
|
||||||
|
}
|
||||||
|
|
||||||
|
const addLink = async () => {
|
||||||
|
await save([
|
||||||
|
...navItems,
|
||||||
|
{
|
||||||
|
id: generate(),
|
||||||
|
text: getSequentialName(navItems, "Nav Item ", x => x.text),
|
||||||
|
url: "",
|
||||||
|
roleId: Constants.Roles.BASIC,
|
||||||
|
type: "link",
|
||||||
|
},
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
const removeNavItem = async id => {
|
||||||
|
await save(navItems.filter(navItem => navItem.id !== id))
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
|
<div class="link-configuration">
|
||||||
|
{#if navItems.length}
|
||||||
|
<DraggableList
|
||||||
|
on:change={handleListUpdate}
|
||||||
|
on:itemChange={handleNavItemUpdate}
|
||||||
|
items={navItems}
|
||||||
|
listItemKey="id"
|
||||||
|
listType={NavItem}
|
||||||
|
listTypeProps={navItemProps}
|
||||||
|
draggable={navItems.length > 1}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<div class="list-footer" on:click={addLink} class:empty={!navItems.length}>
|
||||||
|
<div class="add-button">Add nav item</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.list-footer {
|
||||||
|
width: 100%;
|
||||||
|
border-bottom-left-radius: 4px;
|
||||||
|
border-bottom-right-radius: 4px;
|
||||||
|
background-color: var(
|
||||||
|
--spectrum-table-background-color,
|
||||||
|
var(--spectrum-global-color-gray-50)
|
||||||
|
);
|
||||||
|
transition: background-color ease-in-out 130ms;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
border: 1px solid var(--spectrum-alias-border-color-mid);
|
||||||
|
border-top: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.list-footer.empty {
|
||||||
|
border-top: 1px solid var(--spectrum-alias-border-color-mid);
|
||||||
|
}
|
||||||
|
.list-footer.empty {
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
.list-footer:hover {
|
||||||
|
background-color: var(
|
||||||
|
--spectrum-table-row-background-color-hover,
|
||||||
|
var(--spectrum-alias-highlight-hover)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-button {
|
||||||
|
margin: var(--spacing-s);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,5 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import LinksEditor from "./LinksEditor.svelte"
|
import NavItemConfiguration from "./NavItemConfiguration.svelte"
|
||||||
import { get } from "svelte/store"
|
import { get } from "svelte/store"
|
||||||
import Panel from "components/design/Panel.svelte"
|
import Panel from "components/design/Panel.svelte"
|
||||||
import {
|
import {
|
||||||
|
@ -75,7 +75,7 @@
|
||||||
|
|
||||||
{#if $selectedScreen?.showNavigation}
|
{#if $selectedScreen?.showNavigation}
|
||||||
<DetailSummary name="Customize" initiallyShow collapsible={false}>
|
<DetailSummary name="Customize" initiallyShow collapsible={false}>
|
||||||
<LinksEditor />
|
<NavItemConfiguration />
|
||||||
<div class="settings">
|
<div class="settings">
|
||||||
<PropertyControl
|
<PropertyControl
|
||||||
label="Position"
|
label="Position"
|
||||||
|
|
|
@ -105,7 +105,7 @@
|
||||||
|
|
||||||
const getValidLinks = (allLinks, userRoleHierarchy) => {
|
const getValidLinks = (allLinks, userRoleHierarchy) => {
|
||||||
// Strip links missing required info
|
// Strip links missing required info
|
||||||
let validLinks = (allLinks || []).filter(link => link.text && link.url)
|
let validLinks = (allLinks || []).filter(link => link.text)
|
||||||
// Filter to only links allowed by the current role
|
// Filter to only links allowed by the current role
|
||||||
return validLinks.filter(link => {
|
return validLinks.filter(link => {
|
||||||
const role = link.roleId || Constants.Roles.BASIC
|
const role = link.roleId || Constants.Roles.BASIC
|
||||||
|
|
Loading…
Reference in New Issue