|
|
|
@ -1,149 +1,160 @@ |
|
|
|
<script> |
|
|
|
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 }); |
|
|
|
$: 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) |
|
|
|
|
|
|
|
return s |
|
|
|
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, |
|
|
|
}) |
|
|
|
} |
|
|
|
$: 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) |
|
|
|
|
|
|
|
const moveDownComponent = () => { |
|
|
|
store.update(s => { |
|
|
|
const parent = getParent(s.currentPreviewItem.props, component) |
|
|
|
return s |
|
|
|
}) |
|
|
|
} |
|
|
|
|
|
|
|
if (parent) { |
|
|
|
const currentIndex = parent._children.indexOf(component) |
|
|
|
if (currentIndex === parent._children.length - 1) return s |
|
|
|
const moveDownComponent = () => { |
|
|
|
store.update(s => { |
|
|
|
const parent = getParent(s.currentPreviewItem.props, component) |
|
|
|
|
|
|
|
const newChildren = parent._children.filter(c => c !== component) |
|
|
|
newChildren.splice(currentIndex + 1, 0, component) |
|
|
|
parent._children = newChildren |
|
|
|
} |
|
|
|
s.currentComponentInfo = component |
|
|
|
saveCurrentPreviewItem(s) |
|
|
|
if (parent) { |
|
|
|
const currentIndex = parent._children.indexOf(component) |
|
|
|
if (currentIndex === parent._children.length - 1) return s |
|
|
|
|
|
|
|
return s |
|
|
|
}) |
|
|
|
} |
|
|
|
const newChildren = parent._children.filter(c => c !== component) |
|
|
|
newChildren.splice(currentIndex + 1, 0, component) |
|
|
|
parent._children = newChildren |
|
|
|
} |
|
|
|
s.currentComponentInfo = component |
|
|
|
saveCurrentPreviewItem(s) |
|
|
|
|
|
|
|
const copyComponent = () => { |
|
|
|
store.update(s => { |
|
|
|
const parent = getParent(s.currentPreviewItem.props, component) |
|
|
|
const copiedComponent = cloneDeep(component) |
|
|
|
walkProps(copiedComponent, p => { |
|
|
|
p._id = uuid() |
|
|
|
return s |
|
|
|
}) |
|
|
|
parent._children = [...parent._children, copiedComponent] |
|
|
|
saveCurrentPreviewItem(s) |
|
|
|
s.currentComponentInfo = copiedComponent |
|
|
|
return s |
|
|
|
}) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
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 |
|
|
|
}) |
|
|
|
} |
|
|
|
|
|
|
|
const deleteComponent = () => { |
|
|
|
store.update(state => { |
|
|
|
const parent = getParent(state.currentPreviewItem.props, component) |
|
|
|
const deleteComponent = () => { |
|
|
|
store.update(state => { |
|
|
|
const parent = getParent(state.currentPreviewItem.props, component) |
|
|
|
|
|
|
|
if (parent) { |
|
|
|
parent._children = parent._children.filter( |
|
|
|
c => c !== component |
|
|
|
) |
|
|
|
} |
|
|
|
if (parent) { |
|
|
|
parent._children = parent._children.filter(c => c !== component) |
|
|
|
} |
|
|
|
|
|
|
|
saveCurrentPreviewItem(state) |
|
|
|
saveCurrentPreviewItem(state) |
|
|
|
|
|
|
|
return state |
|
|
|
}) |
|
|
|
} |
|
|
|
return state |
|
|
|
}) |
|
|
|
} |
|
|
|
|
|
|
|
const generateNewIdsForComponent = c => |
|
|
|
walkProps(c, p => { |
|
|
|
p._id = uuid() |
|
|
|
}) |
|
|
|
const generateNewIdsForComponent = c => |
|
|
|
walkProps(c, p => { |
|
|
|
p._id = uuid() |
|
|
|
}) |
|
|
|
|
|
|
|
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) |
|
|
|
} |
|
|
|
|
|
|
|
return s |
|
|
|
}) |
|
|
|
} |
|
|
|
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) |
|
|
|
} |
|
|
|
|
|
|
|
const pasteComponent = mode => { |
|
|
|
store.update(s => { |
|
|
|
if (!s.componentToPaste) return s |
|
|
|
return s |
|
|
|
}) |
|
|
|
} |
|
|
|
|
|
|
|
const componentToPaste = cloneDeep(s.componentToPaste) |
|
|
|
generateNewIdsForComponent(componentToPaste) |
|
|
|
delete componentToPaste._cutId |
|
|
|
const pasteComponent = mode => { |
|
|
|
store.update(s => { |
|
|
|
if (!s.componentToPaste) return s |
|
|
|
|
|
|
|
if (mode === "inside") { |
|
|
|
component._children.push(componentToPaste) |
|
|
|
return s |
|
|
|
} |
|
|
|
const componentToPaste = cloneDeep(s.componentToPaste) |
|
|
|
generateNewIdsForComponent(componentToPaste) |
|
|
|
delete componentToPaste._cutId |
|
|
|
|
|
|
|
const parent = getParent(s.currentPreviewItem.props, component) |
|
|
|
if (mode === "inside") { |
|
|
|
component._children.push(componentToPaste) |
|
|
|
return s |
|
|
|
} |
|
|
|
|
|
|
|
const targetIndex = parent._children.indexOf(component) |
|
|
|
const index = mode === "above" ? targetIndex : targetIndex + 1 |
|
|
|
parent._children.splice(index, 0, cloneDeep(componentToPaste)) |
|
|
|
const parent = getParent(s.currentPreviewItem.props, component) |
|
|
|
|
|
|
|
saveCurrentPreviewItem(s) |
|
|
|
selectComponent(s, componentToPaste) |
|
|
|
const targetIndex = parent._children.indexOf(component) |
|
|
|
const index = mode === "above" ? targetIndex : targetIndex + 1 |
|
|
|
parent._children.splice(index, 0, cloneDeep(componentToPaste)) |
|
|
|
|
|
|
|
return s |
|
|
|
}) |
|
|
|
} |
|
|
|
saveCurrentPreviewItem(s) |
|
|
|
selectComponent(s, componentToPaste) |
|
|
|
|
|
|
|
return s |
|
|
|
}) |
|
|
|
} |
|
|
|
</script> |
|
|
|
|
|
|
|
<div class="root" on:click|stopPropagation={() => {}}> |
|
|
|
<div class="root" on:click|stopPropagation={() => {}}> |
|
|
|
<button> |
|
|
|
<MoreIcon /> |
|
|
|
</button> |
|
|
|
<ul class="menu" bind:this={dropdownEl} on:click={hideDropdown}> |
|
|
|
<ul class="menu" bind:this={dropdownEl} on:click={hideDropdown}> |
|
|
|
<li on:click={() => confirmDeleteDialog.show()}>Delete</li> |
|
|
|
<li on:click={moveUpComponent}>Move up</li> |
|
|
|
<li on:click={moveDownComponent}>Move down</li> |
|
|
|
@ -151,14 +162,20 @@ const pasteComponent = mode => { |
|
|
|
<li on:click={() => storeComponentForCopy(true)}>Cut</li> |
|
|
|
<li on:click={() => storeComponentForCopy(false)}>Copy</li> |
|
|
|
<hr /> |
|
|
|
<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> |
|
|
|
<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> |
|
|
|
</ul> |
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<ConfirmDialog |
|
|
|
bind:this={confirmDeleteDialog} |
|
|
|
title="Confirm Delete" |
|
|
|
@ -167,52 +184,49 @@ const pasteComponent = mode => { |
|
|
|
onOk={deleteComponent} /> |
|
|
|
|
|
|
|
<style> |
|
|
|
|
|
|
|
.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> |
|
|
|
.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> |
|
|
|
|