diff --git a/packages/builder/package.json b/packages/builder/package.json index 81e240d148..8e426deb45 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -51,7 +51,7 @@ "safe-buffer": "^5.1.2", "shortid": "^2.2.8", "string_decoder": "^1.2.0", - "svelte-simple-modal": "^0.3.0", + "svelte-simple-modal": "^0.4.2", "uikit": "^3.1.7" }, "devDependencies": { diff --git a/packages/builder/src/budibase.css b/packages/builder/src/budibase.css index f433b016da..0d74ed7221 100644 --- a/packages/builder/src/budibase.css +++ b/packages/builder/src/budibase.css @@ -57,23 +57,23 @@ .budibase__nav-item { cursor: pointer; - padding: 0 7px 0 3px; + padding: 0 4px 0 2px; height: 35px; - margin: 5px 20px 5px 0px; + margin: 5px 0px 4px 0px; border-radius: 0 5px 5px 0; display: flex; align-items: center; - font-weight: 500; - font-size: 13px; + font-size: 14px; + transition: 0.2s; } .budibase__nav-item.selected { - color: var(--button-text); - background: #f1f4fc; + color: var(--ink); + background: var(--blue-light); } .budibase__nav-item:hover { - background: #fafafa; + background: var(--grey-light); } .budibase__input { diff --git a/packages/builder/src/builderStore/generate_css.js b/packages/builder/src/builderStore/generate_css.js index cd284994f9..5d811c6f37 100644 --- a/packages/builder/src/builderStore/generate_css.js +++ b/packages/builder/src/builderStore/generate_css.js @@ -1,42 +1,41 @@ -export const generate_screen_css = (component_arr) => { - let styles = ''; - for (const { _styles, _id, _children, _component } of component_arr) { - let [ componentName ] = _component.match(/[a-z]*$/); - Object.keys(_styles).forEach((selector) => { - const cssString = generate_css(_styles[selector]); - if (cssString) { - styles += apply_class(_id, componentName, cssString, selector); - } - }); - if (_children && _children.length) { - styles += generate_screen_css(_children) + '\n'; - } - } - return styles.trim(); -}; +export const generate_screen_css = component_arr => { + let styles = "" + for (const { _styles, _id, _children, _component } of component_arr) { + let [componentName] = _component.match(/[a-z]*$/) + Object.keys(_styles).forEach(selector => { + const cssString = generate_css(_styles[selector]) + if (cssString) { + styles += apply_class(_id, componentName, cssString, selector) + } + }) + if (_children && _children.length) { + styles += generate_screen_css(_children) + "\n" + } + } + return styles.trim() +} -export const generate_css = (style) => { - let cssString = Object.entries(style).reduce((str, [ key, value ]) => { - //TODO Handle arrays and objects here also - if (typeof value === 'string') { - if (value) { - return (str += `${key}: ${value};\n`); - } - } else if (Array.isArray(value)) { - if (value.length > 0 && !value.every((v) => v === '')) { - return (str += `${key}: ${value.join(' ')};\n`); - } - } - }, ''); +export const generate_css = style => { + let cssString = Object.entries(style).reduce((str, [key, value]) => { + if (typeof value === "string") { + if (value) { + return (str += `${key}: ${value};\n`) + } + } else if (Array.isArray(value)) { + if (value.length > 0 && !value.every(v => v === "")) { + return (str += `${key}: ${value.join(" ")};\n`) + } + } + }, "") - return (cssString || '').trim(); -}; + return (cssString || "").trim() +} -export const apply_class = (id, name = 'element', styles, selector) => { - if (selector === 'normal') { - return `.${name}-${id} {\n${styles}\n}`; - } else { - let sel = selector === 'selected' ? '::selection' : `:${selector}`; - return `.${name}-${id}${sel} {\n${styles}\n}`; - } -}; +export const apply_class = (id, name = "element", styles, selector) => { + if (selector === "normal") { + return `.${name}-${id} {\n${styles}\n}` + } else { + let sel = selector === "selected" ? "::selection" : `:${selector}` + return `.${name}-${id}${sel} {\n${styles}\n}` + } +} diff --git a/packages/builder/src/builderStore/store/index.js b/packages/builder/src/builderStore/store/index.js index 2943667033..9ac06a0a0d 100644 --- a/packages/builder/src/builderStore/store/index.js +++ b/packages/builder/src/builderStore/store/index.js @@ -1,107 +1,123 @@ -import { cloneDeep, values } from 'lodash/fp'; -import { backendUiStore } from 'builderStore'; -import * as backendStoreActions from './backend'; -import { writable, get } from 'svelte/store'; -import api from '../api'; -import { DEFAULT_PAGES_OBJECT } from '../../constants'; -import { getExactComponent } from 'components/userInterface/pagesParsing/searchComponents'; -import { rename } from 'components/userInterface/pagesParsing/renameScreen'; -import { createProps, makePropsSafe, getBuiltin } from 'components/userInterface/pagesParsing/createProps'; -import { fetchComponentLibDefinitions } from '../loadComponentLibraries'; -import { buildCodeForScreens } from '../buildCodeForScreens'; -import { generate_screen_css } from '../generate_css'; -import { insertCodeMetadata } from '../insertCodeMetadata'; -import { uuid } from '../uuid'; + +import { values } from "lodash/fp" +import { backendUiStore } from "builderStore" +import * as backendStoreActions from "./backend" +import { writable, get } from "svelte/store" +import api from "../api" +import { DEFAULT_PAGES_OBJECT } from "../../constants" +import { getExactComponent } from "components/userInterface/pagesParsing/searchComponents" +import { rename } from "components/userInterface/pagesParsing/renameScreen" +import { + createProps, + makePropsSafe, + getBuiltin, +} from "components/userInterface/pagesParsing/createProps" +import { fetchComponentLibDefinitions } from "../loadComponentLibraries" +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 = { - apps: [], - appname: '', - pages: DEFAULT_PAGES_OBJECT, - mainUi: {}, - unauthenticatedUi: {}, - components: [], - currentPreviewItem: null, - currentComponentInfo: null, - currentFrontEndType: 'none', - currentPageName: '', - currentComponentProps: null, - errors: [], - hasAppPackage: false, - libraries: null, - appId: '' - }; + const initial = { + apps: [], + appname: "", + pages: DEFAULT_PAGES_OBJECT, + mainUi: {}, + unauthenticatedUi: {}, + components: [], + currentPreviewItem: null, + currentComponentInfo: null, + currentFrontEndType: "none", + currentPageName: "", + currentComponentProps: null, + errors: [], + hasAppPackage: false, + libraries: null, + appId: "", + } - const store = writable(initial); + const store = writable(initial) - store.setPackage = setPackage(store, initial); + store.setPackage = setPackage(store, initial) - store.createDatabaseForApp = backendStoreActions.createDatabaseForApp(store); + store.createDatabaseForApp = backendStoreActions.createDatabaseForApp(store) - store.saveScreen = saveScreen(store); - store.renameScreen = renameScreen(store); - store.deleteScreen = deleteScreen(store); - store.setCurrentScreen = setCurrentScreen(store); - store.setCurrentPage = setCurrentPage(store); - store.createScreen = createScreen(store); - store.addStylesheet = addStylesheet(store); - store.removeStylesheet = removeStylesheet(store); - store.savePage = savePage(store); - store.addChildComponent = addChildComponent(store); - store.selectComponent = selectComponent(store); - store.setComponentProp = setComponentProp(store); - 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); - return store; -}; + store.saveScreen = saveScreen(store) + store.renameScreen = renameScreen(store) + store.deleteScreen = deleteScreen(store) + store.setCurrentScreen = setCurrentScreen(store) + store.setCurrentPage = setCurrentPage(store) + store.createScreen = createScreen(store) + store.addStylesheet = addStylesheet(store) + store.removeStylesheet = removeStylesheet(store) + store.savePage = savePage(store) + store.addChildComponent = addChildComponent(store) + store.selectComponent = selectComponent(store) + store.setComponentProp = setComponentProp(store) + store.setComponentStyle = setComponentStyle(store) + store.setComponentCode = setComponentCode(store) + store.setScreenType = setScreenType(store) + store.getPathToComponent = getPathToComponent(store) + store.addTemplatedComponent = addTemplatedComponent(store) + store.setMetadataProp = setMetadataProp(store) + return store +} -export default getStore; +export default getStore -const setPackage = (store, initial) => async (pkg) => { - const [ main_screens, unauth_screens ] = await Promise.all([ - api.get(`/_builder/api/${pkg.application._id}/pages/main/screens`).then((r) => r.json()), - api.get(`/_builder/api/${pkg.application._id}/pages/unauthenticated/screens`).then((r) => r.json()) - ]); +export const getComponentDefinition = (state, name) => + name.startsWith("##") ? getBuiltin(name) : state.components[name] - pkg.pages = { - main: { - ...pkg.pages.main, - _screens: Object.values(main_screens) - }, - unauthenticated: { - ...pkg.pages.unauthenticated, - _screens: Object.values(unauth_screens) - } - }; +const setPackage = (store, initial) => async pkg => { + const [main_screens, unauth_screens] = await Promise.all([ + api + .get(`/_builder/api/${pkg.application._id}/pages/main/screens`) + .then(r => r.json()), + api + .get(`/_builder/api/${pkg.application._id}/pages/unauthenticated/screens`) + .then(r => r.json()), + ]) - initial.libraries = pkg.application.componentLibraries; - initial.components = await fetchComponentLibDefinitions(pkg.application._id); - initial.appname = pkg.application.name; - initial.appId = pkg.application._id; - initial.pages = pkg.pages; - initial.hasAppPackage = true; - initial.screens = values(pkg.screens); - initial.builtins = [ getBuiltin('##builtin/screenslot') ]; - initial.appInstances = pkg.application.instances; - initial.appId = pkg.application._id; + pkg.pages = { + main: { + ...pkg.pages.main, + _screens: Object.values(main_screens), + }, + unauthenticated: { + ...pkg.pages.unauthenticated, + _screens: Object.values(unauth_screens), + }, + } - store.set(initial); - return initial; -}; + initial.libraries = pkg.application.componentLibraries + initial.components = await fetchComponentLibDefinitions(pkg.application._id) + initial.appname = pkg.application.name + initial.appId = pkg.application._id + initial.pages = pkg.pages + initial.hasAppPackage = true + initial.screens = values(pkg.screens) + initial.builtins = [getBuiltin("##builtin/screenslot")] + initial.appInstances = pkg.application.instances + initial.appId = pkg.application._id -const saveScreen = (store) => (screen) => { - store.update((state) => { - return _saveScreen(store, state, screen); - }); -}; + store.set(initial) + return initial +} + +const saveScreen = store => screen => { + store.update(state => { + return _saveScreen(store, state, screen) + }) +} const _saveScreen = async (store, s, screen) => { const currentPageScreens = s.pages[s.currentPageName]._screens; @@ -127,430 +143,349 @@ 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] -const createScreen = (store) => (screenName, route, layoutComponentName) => { - store.update((state) => { - const rootComponent = state.components[layoutComponentName]; + const newScreen = { + name: screenName || "", + description: "", + url: "", + _css: "", + uiFunctions: "", + props: createProps(rootComponent).props, + } - const newScreen = { - name: screenName || '', - description: '', - url: '', - _css: '', - uiFunctions: '', - props: createProps(rootComponent).props - }; + newScreen.route = route + state.currentPreviewItem = newScreen + state.currentComponentInfo = newScreen.props + state.currentFrontEndType = "screen" - newScreen.route = route; - state.currentPreviewItem = newScreen; - state.currentComponentInfo = newScreen.props; - state.currentFrontEndType = 'screen'; + _saveScreen(store, state, newScreen) - _saveScreen(store, state, newScreen); + return state + }) +} - return state; - }); -}; +const setCurrentScreen = store => screenName => { + store.update(s => { + const screen = getExactComponent(s.screens, screenName) + screen._css = generate_screen_css([screen.props]) + s.currentPreviewItem = screen + s.currentFrontEndType = "screen" + s.currentView = "detail" -const setCurrentScreen = (store) => (screenName) => { - store.update((s) => { - const screen = getExactComponent(s.screens, screenName); - screen._css = generate_screen_css([ screen.props ]); - s.currentPreviewItem = screen; - s.currentFrontEndType = 'screen'; - s.currentView = 'detail'; + const safeProps = makePropsSafe( + s.components[screen.props._component], + screen.props + ) + screen.props = safeProps + s.currentComponentInfo = safeProps + setCurrentPageFunctions(s) + return s + }) +} - const safeProps = makePropsSafe(s.components[screen.props._component], screen.props); - screen.props = safeProps; - s.currentComponentInfo = safeProps; - setCurrentPageFunctions(s); - return s; - }); -}; +const deleteScreen = store => name => { + store.update(s => { + const components = s.components.filter(c => c.name !== name) + const screens = s.screens.filter(c => c.name !== name) -const deleteScreen = (store) => (name) => { - store.update((s) => { - const components = s.components.filter((c) => c.name !== name); - const screens = s.screens.filter((c) => c.name !== name); + s.components = components + s.screens = screens + if (s.currentPreviewItem.name === name) { + s.currentPreviewItem = null + s.currentFrontEndType = "" + } - s.components = components; - s.screens = screens; - if (s.currentPreviewItem.name === name) { - s.currentPreviewItem = null; - s.currentFrontEndType = ''; - } + api.delete(`/_builder/api/${s.appId}/screen/${name}`) - api.delete(`/_builder/api/${s.appId}/screen/${name}`); + return s + }) +} - return s; - }); -}; +const renameScreen = store => (oldname, newname) => { + store.update(s => { + const { screens, pages, error, changedScreens } = rename( + s.pages, + s.screens, + oldname, + newname + ) -const renameScreen = (store) => (oldname, newname) => { - store.update((s) => { - const { screens, pages, error, changedScreens } = rename(s.pages, s.screens, oldname, newname); + if (error) { + // should really do something with this + return s + } - if (error) { - // should really do something with this - return s; - } + s.screens = screens + s.pages = pages + if (s.currentPreviewItem.name === oldname) + s.currentPreviewItem.name = newname - s.screens = screens; - s.pages = pages; - if (s.currentPreviewItem.name === oldname) s.currentPreviewItem.name = newname; + const saveAllChanged = async () => { + for (let screenName of changedScreens) { + const changedScreen = getExactComponent(screens, screenName) + await api.post(`/_builder/api/${s.appId}/screen`, changedScreen) + } + } - const saveAllChanged = async () => { - for (let screenName of changedScreens) { - const changedScreen = getExactComponent(screens, screenName); - await api.post(`/_builder/api/${s.appId}/screen`, changedScreen); - } - }; + api + .patch(`/_builder/api/${s.appId}/screen`, { + oldname, + newname, + }) + .then(() => saveAllChanged()) + .then(() => { + _savePage(s) + }) - api - .patch(`/_builder/api/${s.appId}/screen`, { - oldname, - newname - }) - .then(() => saveAllChanged()) - .then(() => { - _savePage(s); - }); + return s + }) +} - return s; - }); -}; +const savePage = store => async page => { + store.update(state => { + if (state.currentFrontEndType !== "page" || !state.currentPageName) { + return state + } -const savePage = (store) => async (page) => { - store.update((state) => { - if (state.currentFrontEndType !== 'page' || !state.currentPageName) { - return state; - } + state.pages[state.currentPageName] = page + _savePage(state) + return state + }) +} - state.pages[state.currentPageName] = page; - _savePage(state); - return state; - }); -}; +const addStylesheet = store => stylesheet => { + store.update(s => { + s.pages.stylesheets.push(stylesheet) + _savePage(s) + return s + }) +} -const addStylesheet = (store) => (stylesheet) => { - store.update((s) => { - s.pages.stylesheets.push(stylesheet); - _savePage(s); - return s; - }); -}; +const removeStylesheet = store => stylesheet => { + store.update(state => { + state.pages.stylesheets = state.pages.stylesheets.filter( + s => s !== stylesheet + ) + _savePage(state) + return state + }) +} -const removeStylesheet = (store) => (stylesheet) => { - store.update((state) => { - state.pages.stylesheets = state.pages.stylesheets.filter((s) => s !== stylesheet); - _savePage(state); - return state; - }); -}; +const setCurrentPage = store => pageName => { + store.update(state => { + const current_screens = state.pages[pageName]._screens -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 currentPage = state.pages[pageName] -const setCurrentPage = (store) => (pageName) => { - store.update((state) => { - const current_screens = state.pages[pageName]._screens; + state.currentFrontEndType = "page" + state.currentPageName = pageName + state.screens = Array.isArray(current_screens) + ? current_screens + : Object.values(current_screens) + const safeProps = makePropsSafe( + state.components[currentPage.props._component], + currentPage.props + ) + state.currentComponentInfo = safeProps + currentPage.props = safeProps + state.currentPreviewItem = state.pages[pageName] + state.currentPreviewItem._css = generate_screen_css([ + state.currentPreviewItem.props, + ]) - const currentPage = state.pages[pageName]; + for (let screen of state.screens) { + screen._css = generate_screen_css([screen.props]) + } - state.currentFrontEndType = 'page'; - state.currentPageName = pageName; - state.screens = Array.isArray(current_screens) ? current_screens : Object.values(current_screens); - const safeProps = makePropsSafe(state.components[currentPage.props._component], currentPage.props); - state.currentComponentInfo = safeProps; - currentPage.props = safeProps; - state.currentPreviewItem = state.pages[pageName]; - state.currentPreviewItem._css = generate_screen_css([ state.currentPreviewItem.props ]); - - for (let screen of state.screens) { - screen._css = generate_screen_css([ screen.props ]); - } - - setCurrentPageFunctions(state); - return state; - }); -}; - -// const getComponentDefinition = (components, name) => components.find(c => c.name === name) + setCurrentPageFunctions(state) + return state + }) +} /** * @param {string} componentToAdd - name of the component to add to the application * @param {string} presetName - name of the component preset if defined */ -const addChildComponent = (store) => (componentToAdd, presetName) => { - store.update((state) => { - function findSlot(component_array) { - for (let i = 0; i < component_array.length; i += 1) { - if (component_array[i]._component === '##builtin/screenslot') { - return true; - } +const addChildComponent = store => (componentToAdd, presetName) => { + store.update(state => { + function findSlot(component_array) { + for (let i = 0; i < component_array.length; i += 1) { + if (component_array[i]._component === "##builtin/screenslot") { + return true + } - if (component_array[i]._children) findSlot(component_array[i]); - } + if (component_array[i]._children) findSlot(component_array[i]) + } - return false; - } + return false + } - if (componentToAdd.startsWith('##') && findSlot(state.pages[state.currentPageName].props._children)) { - return state; - } + if ( + componentToAdd.startsWith("##") && + findSlot(state.pages[state.currentPageName].props._children) + ) { + return state + } - const component = componentToAdd.startsWith('##') - ? getBuiltin(componentToAdd) - : state.components[componentToAdd]; + const component = getComponentDefinition(state, componentToAdd) - const presetProps = presetName ? component.presets[presetName] : {}; + const presetProps = presetName ? component.presets[presetName] : {} - const instanceId = get(backendUiStore).selectedDatabase._id; + const instanceId = get(backendUiStore).selectedDatabase._id - const newComponent = createProps( - component, - { - ...presetProps, - _instanceId: instanceId - }, - state - ); + const newComponent = createProps( + component, + { + ...presetProps, + _instanceId: instanceId, + }, + state + ) - state.currentComponentInfo._children = state.currentComponentInfo._children.concat(newComponent.props); + state.currentComponentInfo._children = state.currentComponentInfo._children.concat( + newComponent.props + ) - state.currentFrontEndType === 'page' ? _savePage(state) : _saveScreenApi(state.currentPreviewItem, state); + state.currentFrontEndType === "page" + ? _savePage(state) + : _saveScreenApi(state.currentPreviewItem, state) - state.currentView = 'component'; - state.currentComponentInfo = newComponent.props; + state.currentView = "component" + state.currentComponentInfo = newComponent.props - return state; - }); -}; + return state + }) +} /** * @param {string} props - props to add, as child of current component */ -const addTemplatedComponent = (store) => (props) => { - store.update((state) => { - walkProps(props, (p) => { - p._id = uuid(); - }); - state.currentComponentInfo._children = state.currentComponentInfo._children.concat(props); - state.currentPreviewItem._css = generate_screen_css([ state.currentPreviewItem.props ]); - setCurrentPageFunctions(state); - _saveCurrentPreviewItem(state); +const addTemplatedComponent = store => props => { + store.update(state => { + walkProps(props, p => { + p._id = uuid() + }) + state.currentComponentInfo._children = state.currentComponentInfo._children.concat( + props + ) + state.currentPreviewItem._css = generate_screen_css([ + state.currentPreviewItem.props, + ]) - return state; - }); -}; + setCurrentPageFunctions(state) + _saveCurrentPreviewItem(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 state + }) +} -const setComponentProp = (store) => (name, value) => { - store.update((state) => { - const current_component = state.currentComponentInfo; - state.currentComponentInfo[name] = value; +const selectComponent = store => component => { + store.update(state => { + return _selectComponent(state, component) + }) +} - _saveCurrentPreviewItem(state); +const setComponentProp = store => (name, value) => { + store.update(state => { + const current_component = state.currentComponentInfo + state.currentComponentInfo[name] = value - state.currentComponentInfo = current_component; - return state; - }); -}; + _saveCurrentPreviewItem(state) -const setComponentStyle = (store) => (type, name, value) => { - store.update((state) => { - if (!state.currentComponentInfo._styles) { - state.currentComponentInfo._styles = {}; - } - state.currentComponentInfo._styles[type][name] = value; + state.currentComponentInfo = current_component + return state + }) +} - state.currentPreviewItem._css = generate_screen_css([ state.currentPreviewItem.props ]); +const setComponentStyle = store => (type, name, value) => { + store.update(state => { + if (!state.currentComponentInfo._styles) { + state.currentComponentInfo._styles = {} + } + state.currentComponentInfo._styles[type][name] = value - // save without messing with the store - _saveCurrentPreviewItem(state); - return state; - }); -}; + state.currentPreviewItem._css = generate_screen_css([ + state.currentPreviewItem.props, + ]) -const setComponentCode = (store) => (code) => { - store.update((state) => { - state.currentComponentInfo._code = code; + // save without messing with the store + _saveCurrentPreviewItem(state) + return state + }) +} - setCurrentPageFunctions(state); - // save without messing with the store - _saveScreenApi(state.currentPreviewItem, state); +const setComponentCode = store => code => { + store.update(state => { + state.currentComponentInfo._code = code - return state; - }); -}; + setCurrentPageFunctions(state) + // save without messing with the store + _saveScreenApi(state.currentPreviewItem, state) -const setCurrentPageFunctions = (s) => { - s.currentPageFunctions = buildPageCode(s.screens, s.pages[s.currentPageName]); - insertCodeMetadata(s.currentPreviewItem.props); -}; + return state + }) +} -const buildPageCode = (screens, page) => buildCodeForScreens([ page, ...screens ]); +const setCurrentPageFunctions = s => { + s.currentPageFunctions = buildPageCode(s.screens, s.pages[s.currentPageName]) + insertCodeMetadata(s.currentPreviewItem.props) +} -const setScreenType = (store) => (type) => { - store.update((state) => { - state.currentFrontEndType = type; +const buildPageCode = (screens, page) => buildCodeForScreens([page, ...screens]) - const pageOrScreen = - type === 'page' ? state.pages[state.currentPageName] : state.pages[state.currentPageName]._screens[0]; +const setScreenType = store => type => { + store.update(state => { + state.currentFrontEndType = type - state.currentComponentInfo = pageOrScreen ? pageOrScreen.props : null; - state.currentPreviewItem = pageOrScreen; - return state; - }); -}; + const pageOrScreen = + type === "page" + ? state.pages[state.currentPageName] + : state.pages[state.currentPageName]._screens[0] -const deleteComponent = (store) => (componentName) => { - store.update((state) => { - const parent = getParent(state.currentPreviewItem.props, componentName); + state.currentComponentInfo = pageOrScreen ? pageOrScreen.props : null + state.currentPreviewItem = pageOrScreen + return state + }) +} - if (parent) { - parent._children = parent._children.filter((component) => component !== componentName); - } +const getPathToComponent = store => component => { + // Gets all the components to needed to construct a path. + const tempStore = get(store) + let pathComponents = [] + let parent = component + let root = false + while (!root) { + parent = getParent(tempStore.currentPreviewItem.props, parent) + if (!parent) { + root = true + } else { + pathComponents.push(parent) + } + } - _saveCurrentPreviewItem(state); + // Remove root entry since it's the screen or page layout. + // Reverse array since we need the correct order of the IDs + const reversedComponents = pathComponents.reverse().slice(1) - return state; - }); -}; + // Add component + const allComponents = [...reversedComponents, component] -const moveUpComponent = (store) => (component) => { - store.update((s) => { - const parent = getParent(s.currentPreviewItem.props, component); + // Map IDs + const IdList = allComponents.map(c => c._id) - if (parent) { - const currentIndex = parent._children.indexOf(component); - if (currentIndex === 0) return s; + // Construct ID Path: + const path = IdList.join("/") - const newChildren = parent._children.filter((c) => c !== component); - newChildren.splice(currentIndex - 1, 0, component); - parent._children = newChildren; - } - s.currentComponentInfo = component; - _saveCurrentPreviewItem(s); + return path +} - 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 ]; - s.curren; - _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); - let pathComponents = []; - let parent = component; - let root = false; - while (!root) { - parent = getParent(tempStore.currentPreviewItem.props, parent); - if (!parent) { - root = true; - } else { - pathComponents.push(parent); - } - } - - // Remove root entry since it's the screen or page layout. - // Reverse array since we need the correct order of the IDs - const reversedComponents = pathComponents.reverse().slice(1); - - // Add component - const allComponents = [ ...reversedComponents, component ]; - - // Map IDs - const IdList = allComponents.map((c) => c._id); - - // Construct ID Path: - const path = IdList.join('/'); - - return path; -}; - -const getParent = (rootProps, child) => { - let parent; - walkProps(rootProps, (p, breakWalk) => { - if (p._children && p._children.includes(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); +const setMetadataProp = store => (name, prop) => { + store.update(s => { + s.currentPreviewItem[name] = prop + return s + }) +} diff --git a/packages/builder/src/builderStore/storeUtils.js b/packages/builder/src/builderStore/storeUtils.js new file mode 100644 index 0000000000..68e20e56b3 --- /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) + } + } +} diff --git a/packages/builder/src/components/common/AppNotification.svelte b/packages/builder/src/components/common/AppNotification.svelte index e4a43247c9..2e8723ab53 100644 --- a/packages/builder/src/components/common/AppNotification.svelte +++ b/packages/builder/src/components/common/AppNotification.svelte @@ -5,7 +5,7 @@ UIKit.notification({ message: `
-