Initial work on nested nav links

This commit is contained in:
Andrew Kingston 2024-03-27 09:30:11 +00:00
parent cecd5a1908
commit 9fe027b3db
8 changed files with 244 additions and 35 deletions

View File

@ -14,7 +14,6 @@
export let key
export let nested
export let max
export let context
const dispatch = createEventDispatcher()

View File

@ -126,7 +126,7 @@
<div class="right-content">
<svelte:component
this={listType}
anchor={anchors[draggableItem.item._id]}
anchor={anchors[draggableItem.id]}
item={draggableItem.item}
{...listTypeProps}
on:change={onItemChanged}

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -1,5 +1,5 @@
<script>
import LinksEditor from "./LinksEditor.svelte"
import NavItemConfiguration from "./NavItemConfiguration.svelte"
import { get } from "svelte/store"
import Panel from "components/design/Panel.svelte"
import {
@ -75,7 +75,7 @@
{#if $selectedScreen?.showNavigation}
<DetailSummary name="Customize" initiallyShow collapsible={false}>
<LinksEditor />
<NavItemConfiguration />
<div class="settings">
<PropertyControl
label="Position"

View File

@ -105,7 +105,7 @@
const getValidLinks = (allLinks, userRoleHierarchy) => {
// 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
return validLinks.filter(link => {
const role = link.roleId || Constants.Roles.BASIC