2020-06-01 16:31:55 +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:12:25 +02:00
|
|
|
})
|
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:12:25 +02:00
|
|
|
|
2020-06-01 13:15:44 +02:00
|
|
|
return s
|
|
|
|
})
|
|
|
|
}
|
2020-06-01 13:12:25 +02:00
|
|
|
|
2020-06-01 13:15:44 +02:00
|
|
|
const moveDownComponent = () => {
|
|
|
|
store.update(s => {
|
|
|
|
const parent = getParent(s.currentPreviewItem.props, component)
|
2020-06-01 13:12:25 +02:00
|
|
|
|
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:12:25 +02:00
|
|
|
|
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:12:25 +02:00
|
|
|
|
2020-06-01 13:15:44 +02:00
|
|
|
return s
|
2020-06-01 13:12:25 +02:00
|
|
|
})
|
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:12:25 +02:00
|
|
|
|
2020-06-01 13:15:44 +02:00
|
|
|
const deleteComponent = () => {
|
|
|
|
store.update(state => {
|
|
|
|
const parent = getParent(state.currentPreviewItem.props, component)
|
2020-06-01 13:12:25 +02:00
|
|
|
|
2020-06-01 13:15:44 +02:00
|
|
|
if (parent) {
|
|
|
|
parent._children = parent._children.filter(c => c !== component)
|
|
|
|
}
|
2020-06-01 13:12:25 +02:00
|
|
|
|
2020-06-01 13:15:44 +02:00
|
|
|
saveCurrentPreviewItem(state)
|
2020-06-01 13:12:25 +02:00
|
|
|
|
2020-06-01 13:15:44 +02:00
|
|
|
return state
|
|
|
|
})
|
|
|
|
}
|
2020-06-01 13:12:25 +02:00
|
|
|
|
2020-06-01 13:15:44 +02:00
|
|
|
const generateNewIdsForComponent = c =>
|
|
|
|
walkProps(c, p => {
|
|
|
|
p._id = uuid()
|
|
|
|
})
|
2020-06-01 13:12:25 +02:00
|
|
|
|
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:12:25 +02:00
|
|
|
|
2020-06-01 13:15:44 +02:00
|
|
|
return s
|
|
|
|
})
|
|
|
|
}
|
2020-06-01 13:12:25 +02:00
|
|
|
|
2020-06-01 13:15:44 +02:00
|
|
|
const pasteComponent = mode => {
|
|
|
|
store.update(s => {
|
|
|
|
if (!s.componentToPaste) return s
|
2020-06-01 13:12:25 +02:00
|
|
|
|
2020-06-01 13:15:44 +02:00
|
|
|
const componentToPaste = cloneDeep(s.componentToPaste)
|
|
|
|
generateNewIdsForComponent(componentToPaste)
|
|
|
|
delete componentToPaste._cutId
|
2020-06-01 13:12:25 +02:00
|
|
|
|
2020-06-01 13:15:44 +02:00
|
|
|
if (mode === "inside") {
|
|
|
|
component._children.push(componentToPaste)
|
|
|
|
return s
|
|
|
|
}
|
2020-06-01 13:12:25 +02:00
|
|
|
|
2020-06-01 13:15:44 +02:00
|
|
|
const parent = getParent(s.currentPreviewItem.props, component)
|
2020-06-01 13:12:25 +02:00
|
|
|
|
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:12:25 +02:00
|
|
|
|
2020-06-01 13:15:44 +02:00
|
|
|
saveCurrentPreviewItem(s)
|
|
|
|
selectComponent(s, componentToPaste)
|
2020-06-01 13:12:25 +02:00
|
|
|
|
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 16:31:55 +02:00
|
|
|
<li class="item" on:click={() => confirmDeleteDialog.show()}><i class="icon ri-delete-bin-2-line"></i>Delete</li>
|
|
|
|
<li class="item" on:click={moveUpComponent}><i class="icon ri-arrow-up-line"></i>Move up</li>
|
|
|
|
<li class="item" on:click={moveDownComponent}><i class="icon ri-arrow-down-line"></i>Move down</li>
|
|
|
|
<li class="item" on:click={copyComponent}><i class="icon ri-repeat-one-line"></i>Duplicate</li>
|
|
|
|
<li class="item" on:click={() => storeComponentForCopy(true)}><i class="icon ri-scissors-cut-line"></i>Cut</li>
|
|
|
|
<li class="item" on:click={() => storeComponentForCopy(false)}><i class="icon ri-file-copy-line"></i>Copy</li>
|
|
|
|
<hr class="hr-style">
|
|
|
|
<li class="item" class:disabled={noPaste} on:click={() => pasteComponent('above')}><i class="icon ri-insert-row-top"></i>Paste above</li>
|
2020-06-01 18:17:35 +02:00
|
|
|
<li class="item" class:disabled={noPaste} on:click={() => pasteComponent('below')}><i class="icon ri-insert-row-bottom"></i>Paste below</li>
|
|
|
|
<li class="item" class:disabled={noPaste || noChildrenAllowed} on:click={() => pasteComponent('inside')}><i class="icon ri-insert-column-right"></i>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"
|
2020-06-01 13:12:25 +02:00
|
|
|
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;
|
2020-06-01 16:31:55 +02:00
|
|
|
color: var(--ink);
|
2020-06-01 13:15:44 +02:00
|
|
|
outline: none;
|
|
|
|
}
|
|
|
|
|
|
|
|
.menu {
|
|
|
|
z-index: 100000;
|
|
|
|
overflow: visible;
|
2020-06-01 16:31:55 +02:00
|
|
|
padding: 12px 0px;
|
|
|
|
border-radius: 5px;
|
2020-06-01 13:15:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
.menu li {
|
|
|
|
border-style: none;
|
|
|
|
background-color: transparent;
|
|
|
|
list-style-type: none;
|
2020-06-01 16:31:55 +02:00
|
|
|
padding: 4px 16px;
|
2020-06-01 13:15:44 +02:00
|
|
|
margin: 0;
|
|
|
|
width: 100%;
|
|
|
|
box-sizing: border-box;
|
|
|
|
}
|
|
|
|
|
2020-06-01 16:31:55 +02:00
|
|
|
.item {
|
|
|
|
display: flex;
|
|
|
|
align-items: center;
|
|
|
|
font-size: 14px;
|
|
|
|
}
|
|
|
|
|
|
|
|
.icon {
|
|
|
|
margin-right: 8px;
|
|
|
|
}
|
|
|
|
|
2020-06-01 13:15:44 +02:00
|
|
|
.menu li:not(.disabled) {
|
|
|
|
cursor: pointer;
|
2020-06-01 16:31:55 +02:00
|
|
|
color: var(--ink-light);
|
2020-06-01 13:15:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
.menu li:not(.disabled):hover {
|
2020-06-01 16:31:55 +02:00
|
|
|
color: var(--ink);
|
2020-06-01 13:15:44 +02:00
|
|
|
background-color: var(--grey-light);
|
|
|
|
}
|
|
|
|
|
|
|
|
.disabled {
|
|
|
|
color: var(--grey-dark);
|
|
|
|
cursor: default;
|
|
|
|
}
|
2020-06-01 16:31:55 +02:00
|
|
|
|
|
|
|
.hr-style {
|
|
|
|
margin: 8px 0;
|
|
|
|
color: var(--grey-dark)
|
|
|
|
}
|
2020-06-01 13:15:44 +02:00
|
|
|
</style>
|