budibase/packages/builder/src/components/userInterface/ComponentDropdownMenu.svelte

233 lines
5.9 KiB
Svelte
Raw Normal View History

2020-06-01 11:18:45 +02:00
<script>
2020-06-01 13:15:44 +02:00
import { MoreIcon } from "components/common/Icons"
import { store } from "builderStore"
import { getComponentDefinition } from "builderStore/store"
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
import { last, cloneDeep } from "lodash/fp"
import UIkit from "uikit"
import {
selectComponent,
getParent,
walkProps,
saveCurrentPreviewItem,
} from "builderStore/storeUtils"
import { uuid } from "builderStore/uuid"
export let component
let confirmDeleteDialog
let dropdownEl
$: dropdown = UIkit.dropdown(dropdownEl, {
mode: "click",
offset: 0,
pos: "bottom-right",
"delay-hide": 0,
animation: false,
})
2020-06-01 13:15:44 +02:00
$: dropdown && UIkit.util.on(dropdown, "shown", () => (hidden = false))
$: noChildrenAllowed =
!component ||
getComponentDefinition($store, component._component).children === false
$: noPaste =
!$store.componentToPaste || $store.componentToPaste._id === component._id
const lastPartOfName = c => (c ? last(c._component.split("/")) : "")
const hideDropdown = () => {
dropdown.hide()
}
const moveUpComponent = () => {
store.update(s => {
const parent = getParent(s.currentPreviewItem.props, component)
if (parent) {
const currentIndex = parent._children.indexOf(component)
if (currentIndex === 0) return s
const newChildren = parent._children.filter(c => c !== component)
newChildren.splice(currentIndex - 1, 0, component)
parent._children = newChildren
}
s.currentComponentInfo = component
saveCurrentPreviewItem(s)
2020-06-01 13:15:44 +02:00
return s
})
}
2020-06-01 13:15:44 +02:00
const moveDownComponent = () => {
store.update(s => {
const parent = getParent(s.currentPreviewItem.props, component)
2020-06-01 13:15:44 +02:00
if (parent) {
const currentIndex = parent._children.indexOf(component)
if (currentIndex === parent._children.length - 1) return s
2020-06-01 13:15:44 +02:00
const newChildren = parent._children.filter(c => c !== component)
newChildren.splice(currentIndex + 1, 0, component)
parent._children = newChildren
}
s.currentComponentInfo = component
saveCurrentPreviewItem(s)
2020-06-01 13:15:44 +02:00
return s
})
2020-06-01 13:15:44 +02:00
}
const copyComponent = () => {
store.update(s => {
const parent = getParent(s.currentPreviewItem.props, component)
const copiedComponent = cloneDeep(component)
walkProps(copiedComponent, p => {
p._id = uuid()
})
parent._children = [...parent._children, copiedComponent]
saveCurrentPreviewItem(s)
s.currentComponentInfo = copiedComponent
return s
})
}
2020-06-01 13:15:44 +02:00
const deleteComponent = () => {
store.update(state => {
const parent = getParent(state.currentPreviewItem.props, component)
2020-06-01 13:15:44 +02:00
if (parent) {
parent._children = parent._children.filter(c => c !== component)
}
2020-06-01 13:15:44 +02:00
saveCurrentPreviewItem(state)
2020-06-01 13:15:44 +02:00
return state
})
}
2020-06-01 13:15:44 +02:00
const generateNewIdsForComponent = c =>
walkProps(c, p => {
p._id = uuid()
})
2020-06-01 13:15:44 +02:00
const storeComponentForCopy = (cut = false) => {
store.update(s => {
const copiedComponent = cloneDeep(component)
s.componentToPaste = copiedComponent
if (cut) {
const parent = getParent(s.currentPreviewItem.props, component._id)
parent._children = parent._children.filter(c => c._id !== component._id)
selectComponent(s, parent)
}
2020-06-01 13:15:44 +02:00
return s
})
}
2020-06-01 13:15:44 +02:00
const pasteComponent = mode => {
store.update(s => {
if (!s.componentToPaste) return s
2020-06-01 13:15:44 +02:00
const componentToPaste = cloneDeep(s.componentToPaste)
generateNewIdsForComponent(componentToPaste)
delete componentToPaste._cutId
2020-06-01 13:15:44 +02:00
if (mode === "inside") {
component._children.push(componentToPaste)
return s
}
2020-06-01 13:15:44 +02:00
const parent = getParent(s.currentPreviewItem.props, component)
2020-06-01 13:15:44 +02:00
const targetIndex = parent._children.indexOf(component)
const index = mode === "above" ? targetIndex : targetIndex + 1
parent._children.splice(index, 0, cloneDeep(componentToPaste))
2020-06-01 13:15:44 +02:00
saveCurrentPreviewItem(s)
selectComponent(s, componentToPaste)
2020-06-01 13:15:44 +02:00
return s
})
}
2020-06-01 11:18:45 +02:00
</script>
2020-06-01 13:15:44 +02:00
<div class="root" on:click|stopPropagation={() => {}}>
2020-06-01 11:18:45 +02:00
<button>
<MoreIcon />
</button>
2020-06-01 13:15:44 +02:00
<ul class="menu" bind:this={dropdownEl} on:click={hideDropdown}>
2020-06-01 11:18:45 +02:00
<li on:click={() => confirmDeleteDialog.show()}>Delete</li>
<li on:click={moveUpComponent}>Move up</li>
<li on:click={moveDownComponent}>Move down</li>
<li on:click={copyComponent}>Duplicate</li>
<li on:click={() => storeComponentForCopy(true)}>Cut</li>
<li on:click={() => storeComponentForCopy(false)}>Copy</li>
2020-06-01 11:18:45 +02:00
<hr />
2020-06-01 13:15:44 +02:00
<li class:disabled={noPaste} on:click={() => pasteComponent('above')}>
Paste above
</li>
<li class:disabled={noPaste} on:click={() => pasteComponent('below')}>
Paste below
</li>
<li
class:disabled={noPaste || noChildrenAllowed}
on:click={() => pasteComponent('inside')}>
Paste inside
</li>
2020-06-01 11:18:45 +02:00
</ul>
</div>
<ConfirmDialog
bind:this={confirmDeleteDialog}
title="Confirm Delete"
body={`Are you sure you wish to delete this '${lastPartOfName(component)}' component?`}
okText="Delete Component"
onOk={deleteComponent} />
2020-06-01 11:18:45 +02:00
<style>
2020-06-01 13:15:44 +02:00
.root {
overflow: hidden;
z-index: 9;
}
.root button {
border-style: none;
border-radius: 2px;
padding: 5px;
background: transparent;
cursor: pointer;
color: var(--button-text);
outline: none;
}
.menu {
z-index: 100000;
overflow: visible;
padding: 10px 0;
}
.menu li {
border-style: none;
background-color: transparent;
list-style-type: none;
padding: 4px 5px 4px 15px;
margin: 0;
width: 100%;
box-sizing: border-box;
}
.menu li:not(.disabled) {
cursor: pointer;
color: var(--ink);
}
.menu li:not(.disabled):hover {
color: var(--button-text);
background-color: var(--grey-light);
}
.disabled {
color: var(--grey-dark);
cursor: default;
}
</style>