From 353c9c81951a72d8b0a7737615f10b66936d53ee Mon Sep 17 00:00:00 2001 From: Michael Shanks Date: Mon, 1 Jun 2020 10:18:45 +0100 Subject: [PATCH] component nav dropdown menu --- .../builder/src/builderStore/store/index.js | 78 ++++++++++-- .../src/components/common/Icons/More.svelte | 8 ++ .../src/components/common/Icons/index.js | 1 + .../ComponentDropdownMenu.svelte | 116 ++++++++++++++++++ .../ComponentsHierarchyChildren.svelte | 54 +------- .../[application]/frontend/_layout.svelte | 1 + 6 files changed, 197 insertions(+), 61 deletions(-) create mode 100644 packages/builder/src/components/common/Icons/More.svelte create mode 100644 packages/builder/src/components/userInterface/ComponentDropdownMenu.svelte diff --git a/packages/builder/src/builderStore/store/index.js b/packages/builder/src/builderStore/store/index.js index f4d47064be..6755bc0a7c 100644 --- a/packages/builder/src/builderStore/store/index.js +++ b/packages/builder/src/builderStore/store/index.js @@ -64,11 +64,16 @@ export const getStore = () => { store.getPathToComponent = getPathToComponent(store) store.addTemplatedComponent = addTemplatedComponent(store) store.setMetadataProp = setMetadataProp(store) + store.storeComponentForCopy = storeComponentForCopy(store) + store.pasteComponent = pasteComponent(store) return store } export default getStore +export const getComponentDefinition = (state, name) => + name.startsWith("##") ? getBuiltin(name) : state.components[name] + const setPackage = (store, initial) => async pkg => { const [main_screens, unauth_screens] = await Promise.all([ api @@ -317,8 +322,6 @@ const setCurrentPage = store => pageName => { }) } -// const getComponentDefinition = (components, name) => components.find(c => c.name === name) - /** * @param {string} componentToAdd - name of the component to add to the application * @param {string} presetName - name of the component preset if defined @@ -344,9 +347,7 @@ const addChildComponent = store => (componentToAdd, presetName) => { return state } - const component = componentToAdd.startsWith("##") - ? getBuiltin(componentToAdd) - : state.components[componentToAdd] + const component = getComponentDefinition(componentToAdd) const presetProps = presetName ? component.presets[presetName] : {} @@ -398,14 +399,18 @@ const addTemplatedComponent = store => props => { }) } +const _selectComponent = (state, component) => { + const componentDef = component._component.startsWith("##") + ? component + : state.components[component._component] + state.currentComponentInfo = makePropsSafe(componentDef, component) + state.currentView = "component" + return state +} + const selectComponent = store => component => { store.update(state => { - const componentDef = component._component.startsWith("##") - ? component - : state.components[component._component] - state.currentComponentInfo = makePropsSafe(componentDef, component) - state.currentView = "component" - return state + return _selectComponent(state, component) }) } @@ -534,7 +539,6 @@ const copyComponent = store => component => { p._id = uuid() }) parent._children = [...parent._children, copiedComponent] - s.curren _saveCurrentPreviewItem(s) s.currentComponentInfo = copiedComponent return s @@ -572,10 +576,58 @@ const getPathToComponent = store => component => { return path } +const generateNewIdsForComponent = component => + walkProps(component, p => { + p._id = uuid() + }) + +const storeComponentForCopy = store => (component, 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 pasteComponent = store => (targetComponent, mode) => { + store.update(s => { + if (!s.componentToPaste) return s + + const componentToPaste = cloneDeep(s.componentToPaste) + generateNewIdsForComponent(componentToPaste) + delete componentToPaste._cutId + + if (mode === "inside") { + targetComponent._children.push(componentToPaste) + return s + } + + const parent = getParent(s.currentPreviewItem.props, targetComponent) + + const targetIndex = parent._children.indexOf(targetComponent) + const index = mode === "above" ? targetIndex : targetIndex + 1 + parent._children.splice(index, 0, cloneDeep(componentToPaste)) + + _saveCurrentPreviewItem(s) + _selectComponent(s, componentToPaste) + + return s + }) +} + const getParent = (rootProps, child) => { let parent walkProps(rootProps, (p, breakWalk) => { - if (p._children && p._children.includes(child)) { + if ( + p._children && + (p._children.includes(child) || p._children.some(c => c._id === child)) + ) { parent = p breakWalk() } diff --git a/packages/builder/src/components/common/Icons/More.svelte b/packages/builder/src/components/common/Icons/More.svelte new file mode 100644 index 0000000000..f072c48976 --- /dev/null +++ b/packages/builder/src/components/common/Icons/More.svelte @@ -0,0 +1,8 @@ + + + + diff --git a/packages/builder/src/components/common/Icons/index.js b/packages/builder/src/components/common/Icons/index.js index 69b78421da..4e4b41082d 100644 --- a/packages/builder/src/components/common/Icons/index.js +++ b/packages/builder/src/components/common/Icons/index.js @@ -31,3 +31,4 @@ export { default as EmailIcon } from "./Email.svelte" export { default as TwitterIcon } from "./Twitter.svelte" export { default as InfoIcon } from "./Info.svelte" export { default as CloseIcon } from "./Close.svelte" +export { default as MoreIcon } from "./More.svelte" diff --git a/packages/builder/src/components/userInterface/ComponentDropdownMenu.svelte b/packages/builder/src/components/userInterface/ComponentDropdownMenu.svelte new file mode 100644 index 0000000000..5a485f9680 --- /dev/null +++ b/packages/builder/src/components/userInterface/ComponentDropdownMenu.svelte @@ -0,0 +1,116 @@ + + +
{}}> + + +
+ + + + store.deleteComponent(component)} /> + + \ No newline at end of file diff --git a/packages/builder/src/components/userInterface/ComponentsHierarchyChildren.svelte b/packages/builder/src/components/userInterface/ComponentsHierarchyChildren.svelte index eafc1834d0..a61d6960f2 100644 --- a/packages/builder/src/components/userInterface/ComponentsHierarchyChildren.svelte +++ b/packages/builder/src/components/userInterface/ComponentsHierarchyChildren.svelte @@ -3,6 +3,7 @@ import { store } from "builderStore" import { last } from "lodash/fp" import { pipe } from "components/common/core" + import ComponentDropdownMenu from "./ComponentDropdownMenu.svelte" import { XCircleIcon, ChevronUpIcon, @@ -51,30 +52,9 @@ class:selected={currentComponent === component} style="padding-left: {level * 20 + 53}px">
{get_capitalised_name(component._component)}
-
- {#if index > 0} - - {/if} - {#if index < components.length - 1} - - {/if} +
+
- -
{#if component._children} @@ -111,7 +91,7 @@ font-size: 13px; } - .item button { + .actions { display: none; height: 20px; width: 28px; @@ -120,37 +100,15 @@ border-style: none; background: rgba(0, 0, 0, 0); cursor: pointer; - } - - .item button.copy { - width: 26px; + position: relative; } .item:hover { background: #fafafa; cursor: pointer; } - .item:hover button { + .item:hover .actions { display: block; } - .item:hover button:hover { - color: var(--button-text); - } - - .reorder-buttons { - display: flex; - flex-direction: column; - height: 100%; - } - - .reorder-buttons > button { - flex: 1 1 auto; - width: 30px; - height: 15px; - } - - .reorder-buttons > button.solo { - padding-top: 2px; - } diff --git a/packages/builder/src/pages/[application]/frontend/_layout.svelte b/packages/builder/src/pages/[application]/frontend/_layout.svelte index 536da887d6..9b38d64f5d 100644 --- a/packages/builder/src/pages/[application]/frontend/_layout.svelte +++ b/packages/builder/src/pages/[application]/frontend/_layout.svelte @@ -103,6 +103,7 @@ overflow: scroll; display: flex; flex-direction: column; + z-index: 5; } .preview-pane {