Merge pull request #4987 from Budibase/scrollable-component-tree
Scrollable component tree
This commit is contained in:
commit
38be4979f3
|
@ -3,7 +3,7 @@ import { getAutomationStore } from "./store/automation"
|
||||||
import { getThemeStore } from "./store/theme"
|
import { getThemeStore } from "./store/theme"
|
||||||
import { derived, writable } from "svelte/store"
|
import { derived, writable } from "svelte/store"
|
||||||
import { FrontendTypes, LAYOUT_NAMES } from "../constants"
|
import { FrontendTypes, LAYOUT_NAMES } from "../constants"
|
||||||
import { findComponent } from "./componentUtils"
|
import { findComponent, findComponentPath } from "./componentUtils"
|
||||||
|
|
||||||
export const store = getFrontendStore()
|
export const store = getFrontendStore()
|
||||||
export const automationStore = getAutomationStore()
|
export const automationStore = getAutomationStore()
|
||||||
|
@ -29,6 +29,16 @@ export const selectedComponent = derived(
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
export const selectedComponentPath = derived(
|
||||||
|
[store, currentAsset],
|
||||||
|
([$store, $currentAsset]) => {
|
||||||
|
return findComponentPath(
|
||||||
|
$currentAsset?.props,
|
||||||
|
$store.selectedComponentId
|
||||||
|
).map(component => component._id)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
export const currentAssetId = derived(store, $store => {
|
export const currentAssetId = derived(store, $store => {
|
||||||
return $store.currentFrontEndType === FrontendTypes.SCREEN
|
return $store.currentFrontEndType === FrontendTypes.SCREEN
|
||||||
? $store.selectedScreenId
|
? $store.selectedScreenId
|
||||||
|
|
|
@ -10,12 +10,10 @@
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<Tabs selected="Automations">
|
<Tabs selected="Automations">
|
||||||
<Tab title="Automations">
|
<Tab title="Automations">
|
||||||
<div class="tab-content-padding">
|
<AutomationList />
|
||||||
<AutomationList />
|
<Modal bind:this={modal}>
|
||||||
<Modal bind:this={modal}>
|
<CreateAutomationModal {webhookModal} />
|
||||||
<CreateAutomationModal {webhookModal} />
|
</Modal>
|
||||||
</Modal>
|
|
||||||
</div>
|
|
||||||
</Tab>
|
</Tab>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
<div class="add-button" data-cy="new-screen">
|
<div class="add-button" data-cy="new-screen">
|
||||||
|
@ -24,9 +22,6 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.tab-content-padding {
|
|
||||||
padding: 0 var(--spacing-xl);
|
|
||||||
}
|
|
||||||
.add-button {
|
.add-button {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: var(--spacing-l);
|
top: var(--spacing-l);
|
||||||
|
|
|
@ -22,10 +22,11 @@
|
||||||
const selected = $datasources.selected === datasource._id
|
const selected = $datasources.selected === datasource._id
|
||||||
const open = openDataSources.includes(datasource._id)
|
const open = openDataSources.includes(datasource._id)
|
||||||
const containsSelected = containsActiveEntity(datasource)
|
const containsSelected = containsActiveEntity(datasource)
|
||||||
|
const onlySource = $datasources.list.length === 1
|
||||||
return {
|
return {
|
||||||
...datasource,
|
...datasource,
|
||||||
selected,
|
selected,
|
||||||
open: selected || open || containsSelected,
|
open: selected || open || containsSelected || onlySource,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
: []
|
: []
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { Icon } from "@budibase/bbui"
|
import { Icon } from "@budibase/bbui"
|
||||||
import { createEventDispatcher } from "svelte"
|
import { createEventDispatcher, getContext } from "svelte"
|
||||||
|
|
||||||
export let icon
|
export let icon
|
||||||
export let withArrow = false
|
export let withArrow = false
|
||||||
|
@ -14,29 +14,46 @@
|
||||||
export let iconText
|
export let iconText
|
||||||
export let iconColor
|
export let iconColor
|
||||||
|
|
||||||
|
const scrollApi = getContext("scroll")
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
function onIconClick(event) {
|
let contentRef
|
||||||
event.stopPropagation()
|
$: selected && contentRef && scrollToView()
|
||||||
|
|
||||||
|
const onClick = () => {
|
||||||
|
scrollToView()
|
||||||
|
dispatch("click")
|
||||||
|
}
|
||||||
|
|
||||||
|
const onIconClick = e => {
|
||||||
|
e.stopPropagation()
|
||||||
dispatch("iconClick")
|
dispatch("iconClick")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const scrollToView = () => {
|
||||||
|
if (!scrollApi || !contentRef) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const bounds = contentRef.getBoundingClientRect()
|
||||||
|
scrollApi.scrollTo(bounds)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="nav-item"
|
class="nav-item"
|
||||||
class:border
|
class:border
|
||||||
class:selected
|
class:selected
|
||||||
style={`padding-left: ${indentLevel * 14}px`}
|
style={`padding-left: ${20 + indentLevel * 14}px`}
|
||||||
{draggable}
|
{draggable}
|
||||||
on:dragend
|
on:dragend
|
||||||
on:dragstart
|
on:dragstart
|
||||||
on:dragover
|
on:dragover
|
||||||
on:drop
|
on:drop
|
||||||
on:click
|
on:click={onClick}
|
||||||
ondragover="return false"
|
ondragover="return false"
|
||||||
ondragenter="return false"
|
ondragenter="return false"
|
||||||
>
|
>
|
||||||
<div class="content">
|
<div class="nav-item-content" bind:this={contentRef}>
|
||||||
{#if withArrow}
|
{#if withArrow}
|
||||||
<div class:opened class="icon arrow" on:click={onIconClick}>
|
<div class:opened class="icon arrow" on:click={onIconClick}>
|
||||||
<Icon size="S" name="ChevronRight" />
|
<Icon size="S" name="ChevronRight" />
|
||||||
|
@ -64,11 +81,16 @@
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.nav-item {
|
.nav-item {
|
||||||
border-radius: var(--border-radius-s);
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: var(--grey-7);
|
color: var(--grey-7);
|
||||||
transition: background-color
|
transition: background-color
|
||||||
var(--spectrum-global-animation-duration-100, 130ms) ease-in-out;
|
var(--spectrum-global-animation-duration-100, 130ms) ease-in-out;
|
||||||
|
padding: 0 var(--spacing-m) 0 var(--spacing-xl);
|
||||||
|
height: 32px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: flex-start;
|
||||||
}
|
}
|
||||||
.nav-item.selected {
|
.nav-item.selected {
|
||||||
background-color: var(--grey-2);
|
background-color: var(--grey-2);
|
||||||
|
@ -81,14 +103,14 @@
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.nav-item-content {
|
||||||
padding: 0 var(--spacing-s);
|
flex: 1 1 auto;
|
||||||
height: 32px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: var(--spacing-xs);
|
gap: var(--spacing-xs);
|
||||||
|
width: max-content;
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
|
@ -111,12 +133,13 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.text {
|
.text {
|
||||||
flex: 1 1 auto;
|
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-size: var(--spectrum-global-dimension-font-size-75);
|
font-size: var(--spectrum-global-dimension-font-size-75);
|
||||||
|
white-space: nowrap;
|
||||||
|
max-width: 160px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
flex: 0 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.actions {
|
.actions {
|
||||||
|
@ -125,9 +148,9 @@
|
||||||
height: 20px;
|
height: 20px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
position: relative;
|
position: relative;
|
||||||
flex-direction: row;
|
display: grid;
|
||||||
justify-content: center;
|
margin-left: var(--spacing-s);
|
||||||
align-items: center;
|
place-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.iconText {
|
.iconText {
|
||||||
|
|
|
@ -138,3 +138,10 @@
|
||||||
onOk={deleteComponent}
|
onOk={deleteComponent}
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.icon {
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
import NavItem from "components/common/NavItem.svelte"
|
import NavItem from "components/common/NavItem.svelte"
|
||||||
import { capitalise } from "helpers"
|
import { capitalise } from "helpers"
|
||||||
import { notifications } from "@budibase/bbui"
|
import { notifications } from "@budibase/bbui"
|
||||||
|
import { selectedComponentPath } from "builderStore"
|
||||||
|
|
||||||
export let components = []
|
export let components = []
|
||||||
export let currentComponent
|
export let currentComponent
|
||||||
|
@ -71,10 +72,20 @@
|
||||||
notifications.error("Error saving component")
|
notifications.error("Error saving component")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isOpen = (component, selectedComponentPath, closedNodes) => {
|
||||||
|
if (!component?._children?.length) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (selectedComponentPath.includes(component._id)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return !closedNodes[component._id]
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
{#each components as component, index (component._id)}
|
{#each components || [] as component, index (component._id)}
|
||||||
<li on:click|stopPropagation={() => selectComponent(component)}>
|
<li on:click|stopPropagation={() => selectComponent(component)}>
|
||||||
{#if $dragDropStore?.targetComponent === component && $dragDropStore.dropPosition === DropPosition.ABOVE}
|
{#if $dragDropStore?.targetComponent === component && $dragDropStore.dropPosition === DropPosition.ABOVE}
|
||||||
<div
|
<div
|
||||||
|
@ -97,12 +108,12 @@
|
||||||
withArrow
|
withArrow
|
||||||
indentLevel={level + 1}
|
indentLevel={level + 1}
|
||||||
selected={$store.selectedComponentId === component._id}
|
selected={$store.selectedComponentId === component._id}
|
||||||
opened={!closedNodes[component._id] && component?._children?.length}
|
opened={isOpen(component, $selectedComponentPath, closedNodes)}
|
||||||
>
|
>
|
||||||
<ComponentDropdownMenu {component} />
|
<ComponentDropdownMenu {component} />
|
||||||
</NavItem>
|
</NavItem>
|
||||||
|
|
||||||
{#if component._children && !closedNodes[component._id]}
|
{#if isOpen(component, $selectedComponentPath, closedNodes)}
|
||||||
<svelte:self
|
<svelte:self
|
||||||
components={component._children}
|
components={component._children}
|
||||||
{currentComponent}
|
{currentComponent}
|
||||||
|
@ -133,6 +144,10 @@
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
ul,
|
||||||
|
li {
|
||||||
|
min-width: max-content;
|
||||||
|
}
|
||||||
|
|
||||||
.drop-item {
|
.drop-item {
|
||||||
border-radius: var(--border-radius-m);
|
border-radius: var(--border-radius-m);
|
||||||
|
|
|
@ -65,3 +65,10 @@
|
||||||
<Input thin type="text" label="Name" bind:value={name} />
|
<Input thin type="text" label="Name" bind:value={name} />
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.icon {
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -75,4 +75,8 @@
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
padding-left: var(--spacing-xl);
|
padding-left: var(--spacing-xl);
|
||||||
}
|
}
|
||||||
|
.icon {
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
export let border
|
export let border
|
||||||
|
|
||||||
let routeManuallyOpened = false
|
let routeManuallyOpened = false
|
||||||
|
|
||||||
$: selectedScreen = $currentAsset
|
$: selectedScreen = $currentAsset
|
||||||
$: allScreens = getAllScreens(route)
|
$: allScreens = getAllScreens(route)
|
||||||
$: filteredScreens = getFilteredScreens(allScreens, $screenSearchString)
|
$: filteredScreens = getFilteredScreens(allScreens, $screenSearchString)
|
||||||
|
@ -83,7 +84,8 @@
|
||||||
<NavItem
|
<NavItem
|
||||||
icon="WebPage"
|
icon="WebPage"
|
||||||
indentLevel={indent || 1}
|
indentLevel={indent || 1}
|
||||||
selected={$store.selectedScreenId === screen.id}
|
selected={$store.selectedScreenId === screen.id &&
|
||||||
|
$store.currentView === "detail"}
|
||||||
opened={$store.selectedScreenId === screen.id}
|
opened={$store.selectedScreenId === screen.id}
|
||||||
text={ROUTE_NAME_MAP[screen.route]?.[screen.role] || screen.route}
|
text={ROUTE_NAME_MAP[screen.route]?.[screen.role] || screen.route}
|
||||||
withArrow={route.subpaths}
|
withArrow={route.subpaths}
|
||||||
|
|
|
@ -103,3 +103,10 @@
|
||||||
confirmText="Duplicate"
|
confirmText="Duplicate"
|
||||||
/>
|
/>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.icon {
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -55,11 +55,10 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="root">
|
<div class="root" class:has-screens={!!paths?.length}>
|
||||||
{#each paths as path, idx (path)}
|
{#each paths as path, idx (path)}
|
||||||
<PathTree border={idx > 0} {path} route={routes[path]} />
|
<PathTree border={idx > 0} {path} route={routes[path]} />
|
||||||
{/each}
|
{/each}
|
||||||
|
|
||||||
{#if !paths.length}
|
{#if !paths.length}
|
||||||
<div class="empty">
|
<div class="empty">
|
||||||
There aren't any screens configured with this access role.
|
There aren't any screens configured with this access role.
|
||||||
|
@ -68,9 +67,12 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
.root.has-screens {
|
||||||
|
min-width: max-content;
|
||||||
|
}
|
||||||
div.empty {
|
div.empty {
|
||||||
font-size: var(--font-size-xs);
|
font-size: var(--font-size-s);
|
||||||
color: var(--grey-5);
|
color: var(--grey-5);
|
||||||
padding-top: var(--spacing-xs);
|
padding: var(--spacing-xs) var(--spacing-xl);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import { onMount } from "svelte"
|
import { onMount, setContext } from "svelte"
|
||||||
import { goto, params } from "@roxi/routify"
|
import { goto, params } from "@roxi/routify"
|
||||||
import {
|
import {
|
||||||
store,
|
store,
|
||||||
|
@ -18,11 +18,63 @@
|
||||||
Search,
|
Search,
|
||||||
Tabs,
|
Tabs,
|
||||||
Tab,
|
Tab,
|
||||||
|
Layout as BBUILayout,
|
||||||
notifications,
|
notifications,
|
||||||
} from "@budibase/bbui"
|
} from "@budibase/bbui"
|
||||||
|
|
||||||
export let showModal
|
export let showModal
|
||||||
|
|
||||||
|
let scrollRef
|
||||||
|
|
||||||
|
const scrollTo = bounds => {
|
||||||
|
if (!bounds) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const sidebarWidth = 259
|
||||||
|
const navItemHeight = 32
|
||||||
|
const { scrollLeft, scrollTop, offsetHeight } = scrollRef
|
||||||
|
|
||||||
|
let scrollBounds = scrollRef.getBoundingClientRect()
|
||||||
|
let newOffsets = {}
|
||||||
|
|
||||||
|
// Calculate left offset
|
||||||
|
const offsetX = bounds.left + bounds.width + scrollLeft + 20
|
||||||
|
if (offsetX > sidebarWidth) {
|
||||||
|
newOffsets.left = offsetX - sidebarWidth
|
||||||
|
} else {
|
||||||
|
newOffsets.left = 0
|
||||||
|
}
|
||||||
|
if (newOffsets.left === scrollLeft) {
|
||||||
|
delete newOffsets.left
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate top offset
|
||||||
|
const offsetY = bounds.top - scrollBounds?.top + scrollTop
|
||||||
|
if (offsetY > scrollTop + offsetHeight - 2 * navItemHeight) {
|
||||||
|
newOffsets.top = offsetY - offsetHeight + 2 * navItemHeight
|
||||||
|
} else if (offsetY < scrollTop + navItemHeight) {
|
||||||
|
newOffsets.top = offsetY - navItemHeight
|
||||||
|
} else {
|
||||||
|
delete newOffsets.top
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip if offset is unchanged
|
||||||
|
if (newOffsets.left == null && newOffsets.top == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Smoothly scroll to the offset
|
||||||
|
scrollRef.scroll({
|
||||||
|
...newOffsets,
|
||||||
|
behavior: "smooth",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
setContext("scroll", {
|
||||||
|
scrollTo,
|
||||||
|
})
|
||||||
|
|
||||||
const tabs = [
|
const tabs = [
|
||||||
{
|
{
|
||||||
title: "Screens",
|
title: "Screens",
|
||||||
|
@ -79,7 +131,7 @@
|
||||||
<Tabs {selected} on:select={navigate}>
|
<Tabs {selected} on:select={navigate}>
|
||||||
<Tab title="Screens">
|
<Tab title="Screens">
|
||||||
<div class="tab-content-padding">
|
<div class="tab-content-padding">
|
||||||
<div class="role-select">
|
<BBUILayout noPadding gap="XS">
|
||||||
<Select
|
<Select
|
||||||
on:change={updateAccessRole}
|
on:change={updateAccessRole}
|
||||||
value={$selectedAccessRole}
|
value={$selectedAccessRole}
|
||||||
|
@ -93,17 +145,24 @@
|
||||||
label="Search Screens"
|
label="Search Screens"
|
||||||
bind:value={$screenSearchString}
|
bind:value={$screenSearchString}
|
||||||
/>
|
/>
|
||||||
</div>
|
</BBUILayout>
|
||||||
<div class="nav-items-container">
|
<div class="nav-items-container" bind:this={scrollRef}>
|
||||||
<ComponentNavigationTree />
|
<ComponentNavigationTree />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab title="Layouts">
|
<Tab title="Layouts">
|
||||||
<div class="tab-content-padding">
|
<div class="tab-content-padding">
|
||||||
{#each $store.layouts as layout, idx (layout._id)}
|
<div
|
||||||
<Layout {layout} border={idx > 0} />
|
class="nav-items-container nav-items-container--layouts"
|
||||||
{/each}
|
bind:this={scrollRef}
|
||||||
|
>
|
||||||
|
<div class="layouts-container">
|
||||||
|
{#each $store.layouts as layout, idx (layout._id)}
|
||||||
|
<Layout {layout} border={idx > 0} />
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<Modal bind:this={newLayoutModal}>
|
<Modal bind:this={newLayoutModal}>
|
||||||
<NewLayoutModal />
|
<NewLayoutModal />
|
||||||
</Modal>
|
</Modal>
|
||||||
|
@ -126,23 +185,45 @@
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
flex: 1 1 auto;
|
||||||
}
|
}
|
||||||
|
.title :global(.spectrum-Tabs-content),
|
||||||
|
.title :global(.spectrum-Tabs-content > div),
|
||||||
|
.title :global(.spectrum-Tabs-content > div > div) {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.add-button {
|
.add-button {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: var(--spacing-l);
|
top: var(--spacing-l);
|
||||||
right: var(--spacing-xl);
|
right: var(--spacing-xl);
|
||||||
}
|
}
|
||||||
|
|
||||||
.role-select {
|
.tab-content-padding {
|
||||||
|
padding: 0 var(--spacing-xl);
|
||||||
|
height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
margin-bottom: var(--spacing-m);
|
gap: var(--spacing-xl);
|
||||||
gap: var(--spacing-m);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-content-padding {
|
.nav-items-container {
|
||||||
padding: 0 var(--spacing-xl);
|
border-top: var(--border-light);
|
||||||
|
margin: 0 calc(-1 * var(--spacing-xl));
|
||||||
|
padding: var(--spacing-m) 0;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
overflow: auto;
|
||||||
|
height: 0;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.nav-items-container--layouts {
|
||||||
|
border-top: none;
|
||||||
|
margin-top: calc(-1 * var(--spectrum-global-dimension-static-size-150));
|
||||||
|
}
|
||||||
|
|
||||||
|
.layouts-container {
|
||||||
|
min-width: max-content;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -23,10 +23,8 @@
|
||||||
<div class="nav">
|
<div class="nav">
|
||||||
<Tabs {selected} on:select={selectFirstDatasource}>
|
<Tabs {selected} on:select={selectFirstDatasource}>
|
||||||
<Tab title="Sources">
|
<Tab title="Sources">
|
||||||
<div class="tab-content-padding">
|
<DatasourceNavigator />
|
||||||
<DatasourceNavigator />
|
<CreateDatasourceModal bind:modal />
|
||||||
<CreateDatasourceModal bind:modal />
|
|
||||||
</div>
|
|
||||||
</Tab>
|
</Tab>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
<div
|
<div
|
||||||
|
@ -63,10 +61,6 @@
|
||||||
display: contents;
|
display: contents;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-content-padding {
|
|
||||||
padding: 0 var(--spacing-xl);
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav {
|
.nav {
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
background: var(--background);
|
background: var(--background);
|
||||||
|
|
|
@ -244,8 +244,6 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: var(--spacing-l);
|
gap: var(--spacing-l);
|
||||||
padding: 0 0 60px 0;
|
|
||||||
overflow-y: auto;
|
|
||||||
border-right: var(--border-light);
|
border-right: var(--border-light);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -130,6 +130,7 @@
|
||||||
-webkit-line-clamp: 2;
|
-webkit-line-clamp: 2;
|
||||||
-webkit-box-orient: vertical;
|
-webkit-box-orient: vertical;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
word-break: break-all;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button-container {
|
.button-container {
|
||||||
|
|
Loading…
Reference in New Issue