diff --git a/packages/builder/src/builderStore/store/index.js b/packages/builder/src/builderStore/store/index.js index a034114b02..f94cea2399 100644 --- a/packages/builder/src/builderStore/store/index.js +++ b/packages/builder/src/builderStore/store/index.js @@ -1,4 +1,4 @@ -import { cloneDeep, values } from "lodash/fp" +import { values } from "lodash/fp" import { backendUiStore } from "builderStore" import * as backendStoreActions from "./backend" import { writable, get } from "svelte/store" @@ -16,6 +16,14 @@ import { buildCodeForScreens } from "../buildCodeForScreens" import { generate_screen_css } from "../generate_css" import { insertCodeMetadata } from "../insertCodeMetadata" import { uuid } from "../uuid" +import { + selectComponent as _selectComponent, + getParent, + walkProps, + savePage as _savePage, + saveCurrentPreviewItem as _saveCurrentPreviewItem, + saveScreenApi as _saveScreenApi, +} from "../storeUtils" export const getStore = () => { const initial = { @@ -57,15 +65,9 @@ export const getStore = () => { store.setComponentStyle = setComponentStyle(store) store.setComponentCode = setComponentCode(store) store.setScreenType = setScreenType(store) - store.deleteComponent = deleteComponent(store) - store.moveUpComponent = moveUpComponent(store) - store.moveDownComponent = moveDownComponent(store) - store.copyComponent = copyComponent(store) store.getPathToComponent = getPathToComponent(store) store.addTemplatedComponent = addTemplatedComponent(store) store.setMetadataProp = setMetadataProp(store) - store.storeComponentForCopy = storeComponentForCopy(store) - store.pasteComponent = pasteComponent(store) return store } @@ -145,12 +147,6 @@ const _saveScreen = async (store, s, screen) => { return s } -const _saveScreenApi = (screen, s) => { - api - .post(`/_builder/api/${s.appId}/pages/${s.currentPageName}/screen`, screen) - .then(() => _savePage(s)) -} - const createScreen = store => (screenName, route, layoutComponentName) => { store.update(state => { const rootComponent = state.components[layoutComponentName] @@ -282,15 +278,6 @@ const removeStylesheet = store => stylesheet => { }) } -const _savePage = async s => { - const page = s.pages[s.currentPageName] - await api.post(`/_builder/api/${s.appId}/pages/${s.currentPageName}`, { - page: { componentLibraries: s.pages.componentLibraries, ...page }, - uiFunctions: s.currentPageFunctions, - screens: page._screens, - }) -} - const setCurrentPage = store => pageName => { store.update(state => { const current_screens = state.pages[pageName]._screens @@ -399,15 +386,6 @@ 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 => { return _selectComponent(state, component) @@ -477,74 +455,6 @@ const setScreenType = store => type => { }) } -const deleteComponent = store => componentName => { - store.update(state => { - const parent = getParent(state.currentPreviewItem.props, componentName) - - if (parent) { - parent._children = parent._children.filter( - component => component !== componentName - ) - } - - _saveCurrentPreviewItem(state) - - return state - }) -} - -const moveUpComponent = store => component => { - 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 - }) -} - -const moveDownComponent = store => component => { - store.update(s => { - const parent = getParent(s.currentPreviewItem.props, component) - - if (parent) { - const currentIndex = parent._children.indexOf(component) - if (currentIndex === parent._children.length - 1) 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 - }) -} - -const copyComponent = store => component => { - 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 getPathToComponent = store => component => { // Gets all the components to needed to construct a path. const tempStore = get(store) @@ -576,87 +486,9 @@ 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) || p._children.some(c => c._id === child)) - ) { - parent = p - breakWalk() - } - }) - return parent -} - -const walkProps = (props, action, cancelToken = null) => { - cancelToken = cancelToken || { cancelled: false } - action(props, () => { - cancelToken.cancelled = true - }) - - if (props._children) { - for (let child of props._children) { - if (cancelToken.cancelled) return - walkProps(child, action, cancelToken) - } - } -} - const setMetadataProp = store => (name, prop) => { store.update(s => { s.currentPreviewItem[name] = prop return s }) } - -const _saveCurrentPreviewItem = s => - s.currentFrontEndType === "page" - ? _savePage(s) - : _saveScreenApi(s.currentPreviewItem, s) diff --git a/packages/builder/src/builderStore/storeUtils.js b/packages/builder/src/builderStore/storeUtils.js new file mode 100644 index 0000000000..c8c0c7211a --- /dev/null +++ b/packages/builder/src/builderStore/storeUtils.js @@ -0,0 +1,59 @@ +import { makePropsSafe } from "components/userInterface/pagesParsing/createProps" +import api from "./api" + +export const selectComponent = (state, component) => { + const componentDef = component._component.startsWith("##") + ? component + : state.components[component._component] + state.currentComponentInfo = makePropsSafe(componentDef, component) + state.currentView = "component" + return state +} + +export const getParent = (rootProps, child) => { + let parent + walkProps(rootProps, (p, breakWalk) => { + if ( + p._children && + (p._children.includes(child) || p._children.some(c => c._id === child)) + ) { + parent = p + breakWalk() + } + }) + return parent +} + +export const saveCurrentPreviewItem = s => + s.currentFrontEndType === "page" + ? savePage(s) + : saveScreenApi(s.currentPreviewItem, s) + +export const savePage = async s => { + const page = s.pages[s.currentPageName] + await api.post(`/_builder/api/${s.appId}/pages/${s.currentPageName}`, { + page: { componentLibraries: s.pages.componentLibraries, ...page }, + uiFunctions: s.currentPageFunctions, + screens: page._screens, + }) +} + +export const saveScreenApi = (screen, s) => { + api + .post(`/_builder/api/${s.appId}/pages/${s.currentPageName}/screen`, screen) + .then(() => savePage(s)) +} + +export const walkProps = (props, action, cancelToken = null) => { + cancelToken = cancelToken || { cancelled: false } + action(props, () => { + cancelToken.cancelled = true + }) + + if (props._children) { + for (let child of props._children) { + if (cancelToken.cancelled) return + walkProps(child, action, cancelToken) + } + } +} \ No newline at end of file diff --git a/packages/builder/src/components/userInterface/ComponentDropdownMenu.svelte b/packages/builder/src/components/userInterface/ComponentDropdownMenu.svelte index 5672ea6f4c..21ef4b2ab3 100644 --- a/packages/builder/src/components/userInterface/ComponentDropdownMenu.svelte +++ b/packages/builder/src/components/userInterface/ComponentDropdownMenu.svelte @@ -3,8 +3,10 @@ import { MoreIcon } from "components/common/Icons" import { store } from "builderStore" import { getComponentDefinition } from "builderStore/store" import ConfirmDialog from "components/common/ConfirmDialog.svelte" -import { last } from "lodash/fp" +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 @@ -22,6 +24,119 @@ 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 + }) +} + +const moveDownComponent = () => { + store.update(s => { + const parent = getParent(s.currentPreviewItem.props, component) + + if (parent) { + const currentIndex = parent._children.indexOf(component) + if (currentIndex === parent._children.length - 1) 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 + }) +} + +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) + + if (parent) { + parent._children = parent._children.filter( + c => c !== component + ) + } + + saveCurrentPreviewItem(state) + + return state + }) +} + +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 pasteComponent = mode => { + store.update(s => { + if (!s.componentToPaste) return s + + const componentToPaste = cloneDeep(s.componentToPaste) + generateNewIdsForComponent(componentToPaste) + delete componentToPaste._cutId + + if (mode === "inside") { + component._children.push(componentToPaste) + return s + } + + const parent = getParent(s.currentPreviewItem.props, component) + + const targetIndex = parent._children.indexOf(component) + const index = mode === "above" ? targetIndex : targetIndex + 1 + parent._children.splice(index, 0, cloneDeep(componentToPaste)) + + saveCurrentPreviewItem(s) + selectComponent(s, componentToPaste) + + return s + }) +} +