Add vertical scrolling to selected component and support scrolling to selected screen
This commit is contained in:
parent
61e077bf0a
commit
697c43187a
4 changed files with 89 additions and 59 deletions
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import { Icon } from "@budibase/bbui"
|
||||
import { createEventDispatcher } from "svelte"
|
||||
import { createEventDispatcher, getContext } from "svelte"
|
||||
|
||||
export let icon
|
||||
export let withArrow = false
|
||||
|
@ -14,13 +14,25 @@
|
|||
export let iconText
|
||||
export let iconColor
|
||||
|
||||
const scrollApi = getContext("scroll")
|
||||
const dispatch = createEventDispatcher()
|
||||
let textRef
|
||||
|
||||
let contentRef
|
||||
$: selected && contentRef && scrollToView()
|
||||
|
||||
function onIconClick(event) {
|
||||
event.stopPropagation()
|
||||
dispatch("iconClick")
|
||||
}
|
||||
|
||||
const scrollToView = () => {
|
||||
if (!scrollApi || !contentRef) {
|
||||
return
|
||||
}
|
||||
console.log("selected", text)
|
||||
const bounds = contentRef.getBoundingClientRect()
|
||||
scrollApi.scrollTo(bounds)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
|
@ -34,35 +46,33 @@
|
|||
on:dragover
|
||||
on:drop
|
||||
on:click
|
||||
on:mouseover={() => {
|
||||
const size = textRef.getBoundingClientRect()
|
||||
dispatch("mouseover", size.width)
|
||||
}}
|
||||
ondragover="return false"
|
||||
ondragenter="return false"
|
||||
>
|
||||
{#if withArrow}
|
||||
<div class:opened class="icon arrow" on:click={onIconClick}>
|
||||
<Icon size="S" name="ChevronRight" />
|
||||
</div>
|
||||
{/if}
|
||||
<div class="nav-item-content" bind:this={contentRef}>
|
||||
{#if withArrow}
|
||||
<div class:opened class="icon arrow" on:click={onIconClick}>
|
||||
<Icon size="S" name="ChevronRight" />
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<slot name="icon" />
|
||||
{#if iconText}
|
||||
<div class="iconText" style={iconColor ? `color: ${iconColor};` : ""}>
|
||||
{iconText}
|
||||
</div>
|
||||
{:else if icon}
|
||||
<div class="icon">
|
||||
<Icon color={iconColor} size="S" name={icon} />
|
||||
</div>
|
||||
{/if}
|
||||
<div class="text" bind:this={textRef}>{text}</div>
|
||||
{#if withActions}
|
||||
<div class="actions">
|
||||
<slot />
|
||||
</div>
|
||||
{/if}
|
||||
<slot name="icon" />
|
||||
{#if iconText}
|
||||
<div class="iconText" style={iconColor ? `color: ${iconColor};` : ""}>
|
||||
{iconText}
|
||||
</div>
|
||||
{:else if icon}
|
||||
<div class="icon">
|
||||
<Icon color={iconColor} size="S" name={icon} />
|
||||
</div>
|
||||
{/if}
|
||||
<div class="text">{text}</div>
|
||||
{#if withActions}
|
||||
<div class="actions">
|
||||
<slot />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
@ -74,11 +84,9 @@
|
|||
padding: 0 var(--spacing-m) 0 var(--spacing-xl);
|
||||
height: 32px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
gap: var(--spacing-xs);
|
||||
width: calc(max-content - 64px);
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
}
|
||||
.nav-item.selected {
|
||||
background-color: var(--grey-2);
|
||||
|
@ -91,6 +99,16 @@
|
|||
visibility: visible;
|
||||
}
|
||||
|
||||
.nav-item-content {
|
||||
flex: 1 1 auto;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
gap: var(--spacing-xs);
|
||||
width: max-content;
|
||||
}
|
||||
|
||||
.icon {
|
||||
font-size: 16px;
|
||||
flex: 0 0 20px;
|
||||
|
@ -117,6 +135,7 @@
|
|||
max-width: 180px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.actions {
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<script>
|
||||
import { getContext } from "svelte"
|
||||
import { store } from "builderStore"
|
||||
import { DropEffect, DropPosition } from "./dragDropStore"
|
||||
import ComponentDropdownMenu from "./ComponentDropdownMenu.svelte"
|
||||
|
@ -13,8 +12,6 @@
|
|||
export let level = 0
|
||||
export let dragDropStore
|
||||
|
||||
const scrollApi = getContext("scroll")
|
||||
|
||||
let closedNodes = {}
|
||||
|
||||
const selectComponent = component => {
|
||||
|
@ -96,9 +93,6 @@
|
|||
on:dragover={dragover(component, index)}
|
||||
on:iconClick={() => toggleNodeOpen(component._id)}
|
||||
on:drop={onDrop}
|
||||
on:mouseover={e => {
|
||||
scrollApi.scrollTo(level + 1, e.detail)
|
||||
}}
|
||||
text={getComponentText(component)}
|
||||
withArrow
|
||||
indentLevel={level + 1}
|
||||
|
|
|
@ -66,6 +66,9 @@
|
|||
}
|
||||
routeManuallyOpened = !routeManuallyOpened
|
||||
}
|
||||
|
||||
$: console.log($store.selectedComponentId)
|
||||
$: console.log()
|
||||
</script>
|
||||
|
||||
{#if !noSearchMatch}
|
||||
|
@ -76,9 +79,6 @@
|
|||
opened={routeOpened}
|
||||
{border}
|
||||
withArrow={route.subpaths}
|
||||
on:mouseover={e => {
|
||||
scrollApi.scrollTo(0, e.detail)
|
||||
}}
|
||||
>
|
||||
<PathDropdownMenu screens={allScreens} {path} />
|
||||
</NavItem>
|
||||
|
@ -88,14 +88,12 @@
|
|||
<NavItem
|
||||
icon="WebPage"
|
||||
indentLevel={indent || 1}
|
||||
selected={$store.selectedScreenId === screen.id}
|
||||
selected={$store.selectedScreenId === screen.id &&
|
||||
$store.currentView === "detail"}
|
||||
opened={$store.selectedScreenId === screen.id}
|
||||
text={ROUTE_NAME_MAP[screen.route]?.[screen.role] || screen.route}
|
||||
withArrow={route.subpaths}
|
||||
on:click={() => changeScreen(screen.id)}
|
||||
on:mouseover={e => {
|
||||
scrollApi.scrollTo(1, e.detail)
|
||||
}}
|
||||
>
|
||||
<ScreenDropdownMenu screenId={screen.id} />
|
||||
</NavItem>
|
||||
|
|
|
@ -26,32 +26,50 @@
|
|||
|
||||
let scrollRef
|
||||
|
||||
const scrollTo = (indent, width) => {
|
||||
if (!indent) {
|
||||
const scrollTo = bounds => {
|
||||
if (!bounds) {
|
||||
return
|
||||
}
|
||||
|
||||
// Padding left + icon
|
||||
const end = indent * 14 + 20 + 16 + 4 + width + 40
|
||||
let scrollLeft = 0
|
||||
const sidebarWidth = 259
|
||||
const navItemHeight = 32
|
||||
|
||||
if (end > 259) {
|
||||
scrollLeft = end - 259
|
||||
let scrollBounds = scrollRef.getBoundingClientRect()
|
||||
let newScrollOffsets = {}
|
||||
|
||||
// Calculate left offset
|
||||
const offsetX = bounds.left + bounds.width + scrollRef.scrollLeft + 20
|
||||
if (offsetX > sidebarWidth) {
|
||||
newScrollOffsets.left = offsetX - sidebarWidth
|
||||
} else {
|
||||
newScrollOffsets.left = 0
|
||||
}
|
||||
if (newScrollOffsets.left === scrollRef.scrollLeft) {
|
||||
delete newScrollOffsets.left
|
||||
}
|
||||
|
||||
if (scrollLeft === scrollRef.scrollLeft) {
|
||||
// Calculate top offset
|
||||
const offsetY = bounds.top - scrollBounds?.top + scrollRef.scrollTop
|
||||
const upperOffset = 2 * navItemHeight - 8
|
||||
const lowerOffset = navItemHeight + 8
|
||||
if (offsetY > scrollRef.scrollTop + scrollRef.offsetHeight - upperOffset) {
|
||||
newScrollOffsets.top = offsetY - scrollRef.offsetHeight + upperOffset
|
||||
} else if (offsetY < scrollRef.scrollTop + lowerOffset) {
|
||||
newScrollOffsets.top = offsetY - lowerOffset
|
||||
} else {
|
||||
delete newScrollOffsets.top
|
||||
}
|
||||
|
||||
// Skip if offset is unchanged
|
||||
if (newScrollOffsets.left == null && newScrollOffsets.top == null) {
|
||||
return
|
||||
}
|
||||
|
||||
// Smoothly scroll to the offset
|
||||
scrollRef.scroll({
|
||||
left: scrollLeft,
|
||||
...newScrollOffsets,
|
||||
behavior: "smooth",
|
||||
})
|
||||
|
||||
// if (indentLevel === 0) {
|
||||
// } else {
|
||||
// scrollRef.scrollLeft = 4 + indentLevel * 14
|
||||
// }
|
||||
}
|
||||
|
||||
setContext("scroll", {
|
||||
|
@ -192,5 +210,6 @@
|
|||
flex: 1 1 auto;
|
||||
overflow: auto;
|
||||
height: 0;
|
||||
position: relative;
|
||||
}
|
||||
</style>
|
||||
|
|
Loading…
Reference in a new issue