forked from tsai/budibase
3 changed files with 254 additions and 1 deletions
@ -0,0 +1,139 @@ |
|||
<script> |
|||
import { store } from "builderStore" |
|||
import ComponentDropdownMenu from "../ComponentDropdownMenu.svelte" |
|||
import { DropEffect, DropPosition } from "./dragDropStore" |
|||
import NavItem from "components/common/NavItem.svelte" |
|||
import { TreeView, TreeItem as Item} from '@budibase/bbui' |
|||
|
|||
export let components = [] |
|||
export let currentComponent |
|||
export let dragDropStore |
|||
|
|||
const isScreenslot = name => name?.endsWith("screenslot") |
|||
|
|||
const selectComponent = component => { |
|||
store.actions.components.select(component) |
|||
} |
|||
|
|||
const dragstart = component => e => { |
|||
e.dataTransfer.dropEffect = DropEffect.MOVE |
|||
dragDropStore.actions.dragstart(component) |
|||
} |
|||
|
|||
const dragover = (component, index) => e => { |
|||
const definition = store.actions.components.getDefinition( |
|||
component._component |
|||
) |
|||
const canHaveChildrenButIsEmpty = |
|||
definition?.hasChildren && !component._children?.length |
|||
|
|||
e.dataTransfer.dropEffect = DropEffect.COPY |
|||
|
|||
// how far down the mouse pointer is on the drop target |
|||
const mousePosition = e.offsetY / e.currentTarget.offsetHeight |
|||
|
|||
dragDropStore.actions.dragover({ |
|||
component, |
|||
index, |
|||
canHaveChildrenButIsEmpty, |
|||
mousePosition, |
|||
}) |
|||
|
|||
return false |
|||
} |
|||
</script> |
|||
{#each components as component, index (component._id)} |
|||
{#if $dragDropStore?.targetComponent === component && $dragDropStore.dropPosition === DropPosition.ABOVE} |
|||
<div |
|||
on:drop={dragDropStore.actions.drop} |
|||
ondragover="return false" |
|||
ondragenter="return false" |
|||
class="drop-item" /> |
|||
{/if} |
|||
<Item |
|||
draggable |
|||
on:dragend={dragDropStore.actions.reset} |
|||
on:dragstart={dragstart(component)} |
|||
on:dragover={dragover(component, index)} |
|||
on:drop={dragDropStore.actions.drop} |
|||
on:click={() => selectComponent(component)} |
|||
title={isScreenslot(component._component) ? 'Screenslot' : component._instanceName} |
|||
open |
|||
selected={$store.selectedComponentId === component._id}> |
|||
{#if component._children} |
|||
<svelte:self |
|||
components={component._children} |
|||
{currentComponent} |
|||
{dragDropStore} |
|||
/> |
|||
{/if} |
|||
</Item> |
|||
{#if $dragDropStore?.targetComponent === component && ($dragDropStore.dropPosition === DropPosition.INSIDE || $dragDropStore.dropPosition === DropPosition.BELOW)} |
|||
<div |
|||
on:drop={dragDropStore.actions.drop} |
|||
ondragover="return false" |
|||
ondragenter="return false" |
|||
class="drop-item"/> |
|||
{/if} |
|||
{/each} |
|||
|
|||
<!-- <ul> |
|||
{#each components as component, index (component._id)} |
|||
<li on:click|stopPropagation={() => selectComponent(component)}> |
|||
{#if $dragDropStore?.targetComponent === component && $dragDropStore.dropPosition === DropPosition.ABOVE} |
|||
<div |
|||
on:drop={dragDropStore.actions.drop} |
|||
ondragover="return false" |
|||
ondragenter="return false" |
|||
class="drop-item" |
|||
style="margin-left: {(level + 1) * 16}px" /> |
|||
{/if} |
|||
|
|||
<NavItem |
|||
draggable |
|||
on:dragend={dragDropStore.actions.reset} |
|||
on:dragstart={dragstart(component)} |
|||
on:dragover={dragover(component, index)} |
|||
on:drop={dragDropStore.actions.drop} |
|||
text={isScreenslot(component._component) ? 'Screenslot' : component._instanceName} |
|||
withArrow |
|||
indentLevel={level + 1} |
|||
selected={$store.selectedComponentId === component._id}> |
|||
<ComponentDropdownMenu {component} /> |
|||
</NavItem> |
|||
|
|||
{#if component._children} |
|||
<svelte:self |
|||
components={component._children} |
|||
{currentComponent} |
|||
{onSelect} |
|||
{dragDropStore} |
|||
level={level + 1} /> |
|||
{/if} |
|||
|
|||
{#if $dragDropStore?.targetComponent === component && ($dragDropStore.dropPosition === DropPosition.INSIDE || $dragDropStore.dropPosition === DropPosition.BELOW)} |
|||
<div |
|||
on:drop={dragDropStore.actions.drop} |
|||
ondragover="return false" |
|||
ondragenter="return false" |
|||
class="drop-item" |
|||
style="margin-left: {(level + ($dragDropStore.dropPosition === DropPosition.INSIDE ? 3 : 1)) * 16}px" /> |
|||
{/if} |
|||
</li> |
|||
{/each} |
|||
</ul> --> |
|||
|
|||
<style> |
|||
ul { |
|||
list-style: none; |
|||
padding-left: 0; |
|||
margin: 0; |
|||
} |
|||
|
|||
.drop-item { |
|||
border-radius: var(--border-radius-m); |
|||
height: 32px; |
|||
background: var(--grey-3); |
|||
} |
|||
</style> |
|||
|
|||
@ -0,0 +1,114 @@ |
|||
<script> |
|||
import { TreeItem as Item} from '@budibase/bbui' |
|||
import { |
|||
store, |
|||
selectedComponent, |
|||
currentAsset, |
|||
screenSearchString, |
|||
} from "builderStore" |
|||
import instantiateStore from "./dragDropStore" |
|||
import ComponentTree from "./NewComponentTree.svelte" |
|||
import { get } from "svelte/store" |
|||
|
|||
const ROUTE_NAME_MAP = { |
|||
"/": { |
|||
BASIC: "Home", |
|||
PUBLIC: "Login", |
|||
}, |
|||
} |
|||
|
|||
const dragDropStore = instantiateStore() |
|||
|
|||
export let route |
|||
export let path |
|||
|
|||
let routeManuallyOpened = false |
|||
$: selectedScreen = $currentAsset |
|||
$: allScreens = getAllScreens(route) |
|||
$: filteredScreens = getFilteredScreens(allScreens, $screenSearchString) |
|||
$: hasSearchMatch = $screenSearchString && filteredScreens.length > 0 |
|||
$: noSearchMatch = $screenSearchString && !filteredScreens.length |
|||
$: routeSelected = |
|||
route.subpaths[selectedScreen?.routing?.route] !== undefined |
|||
$: routeOpened = routeManuallyOpened || routeSelected || hasSearchMatch |
|||
|
|||
const changeScreen = screenId => { |
|||
store.actions.screens.select(screenId) |
|||
} |
|||
|
|||
const getAllScreens = route => { |
|||
let screens = [] |
|||
Object.entries(route.subpaths).forEach(([route, subpath]) => { |
|||
Object.entries(subpath.screens).forEach(([role, id]) => { |
|||
screens.push({ id, route, role }) |
|||
}) |
|||
}) |
|||
return screens |
|||
} |
|||
|
|||
const getFilteredScreens = (screens, searchString) => { |
|||
return screens.filter( |
|||
screen => !searchString || screen.route.includes(searchString) |
|||
) |
|||
} |
|||
|
|||
const toggleManuallyOpened = () => { |
|||
if (get(screenSearchString)) { |
|||
return |
|||
} |
|||
routeManuallyOpened = !routeManuallyOpened |
|||
} |
|||
</script> |
|||
|
|||
{#if !noSearchMatch} |
|||
<Item icon="Folder" title={path} open={routeOpened}> |
|||
{#if routeOpened} |
|||
{#each filteredScreens as screen (screen.id)} |
|||
<Item |
|||
icon="ModernGridView" |
|||
selected={$store.selectedScreenId === screen.id} |
|||
open={$store.selectedScreenId === screen.id} |
|||
on:click={toggleManuallyOpened} |
|||
title={ROUTE_NAME_MAP[screen.route]?.[screen.role] || screen.route} |
|||
on:click={() => changeScreen(screen.id)}> |
|||
{#if selectedScreen?._id === screen.id} |
|||
<ComponentTree |
|||
components={selectedScreen.props._children} |
|||
currentComponent={$selectedComponent} |
|||
{dragDropStore} /> |
|||
{/if} |
|||
</Item> |
|||
{/each} |
|||
{/if} |
|||
</Item> |
|||
|
|||
<!-- <NavItem |
|||
icon="ri-folder-line" |
|||
text={path} |
|||
on:click={toggleManuallyOpened} |
|||
opened={routeOpened} |
|||
{border} |
|||
withArrow={route.subpaths} /> |
|||
|
|||
{#if routeOpened} |
|||
{#each filteredScreens as screen (screen.id)} |
|||
<NavItem |
|||
icon="ri-artboard-2-line" |
|||
indentLevel={indent || 1} |
|||
selected={$store.selectedScreenId === screen.id} |
|||
opened={$store.selectedScreenId === screen.id} |
|||
text={ROUTE_NAME_MAP[screen.route]?.[screen.role] || screen.route} |
|||
withArrow={route.subpaths} |
|||
on:click={() => changeScreen(screen.id)}> |
|||
<ScreenDropdownMenu screenId={screen.id} /> |
|||
</NavItem> |
|||
{#if selectedScreen?._id === screen.id} |
|||
<ComponentTree |
|||
level={1} |
|||
components={selectedScreen.props._children} |
|||
currentComponent={$selectedComponent} |
|||
{dragDropStore} /> |
|||
{/if} |
|||
{/each} |
|||
{/if} --> |
|||
{/if} |
|||
Loading…
Reference in new issue