From 29fe7ef1d86af55a8218c7282c936d99cf3294d2 Mon Sep 17 00:00:00 2001 From: Mel O'Hagan Date: Wed, 28 Sep 2022 13:49:35 +0100 Subject: [PATCH 01/69] Add screen input to CloseScreenModal --- .../actions/CloseScreenModal.svelte | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/CloseScreenModal.svelte b/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/CloseScreenModal.svelte index 873c9ccf65..5f3b3ef639 100644 --- a/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/CloseScreenModal.svelte +++ b/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/CloseScreenModal.svelte @@ -1,16 +1,31 @@ +Navigate To screen, or leave blank. +
- This action doesn't require any additional settings. - - This action won't do anything if there isn't a screen modal open. - + + (parameters.url = value.detail)} + {bindings} + />
From df5d609298d7686b8a18d34c397bc6cf6fecbd9f Mon Sep 17 00:00:00 2001 From: Mel O'Hagan Date: Wed, 28 Sep 2022 13:50:23 +0100 Subject: [PATCH 02/69] Handle navigation on close modal --- packages/client/src/utils/buttonActions.js | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/packages/client/src/utils/buttonActions.js b/packages/client/src/utils/buttonActions.js index a534ee8326..74c4504ca6 100644 --- a/packages/client/src/utils/buttonActions.js +++ b/packages/client/src/utils/buttonActions.js @@ -224,7 +224,27 @@ const changeFormStepHandler = async (action, context) => { ) } -const closeScreenModalHandler = () => { +const closeScreenModalHandler = action => { + let { url } = action.parameters + if (url) { + window.parent.addEventListener("message", event => { + const location = event.target.location + //remove any trailing slash + if (url.charAt(url.length - 1) === "/") { + url = url.substring(0, url.length - 1) + } + //need to reload if hash route has not changed + let shouldReload = + `#${url.substring(0, url.lastIndexOf("/"))}` === + location.hash?.substring(0, location.hash.lastIndexOf("/")) + + window.parent.location.href = `${location.origin}${location.pathname}#${url}` + if (shouldReload) { + window.parent.location.reload() + } + }) + } + // Emit this as a window event, so parent screens which are iframing us in // can close the modal window.parent.postMessage({ type: "close-screen-modal" }) From b470e57a29c0d6308a4a118bc0c164aa81e6857e Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 28 Sep 2022 14:16:09 +0100 Subject: [PATCH 03/69] Expand component trees with padding when dragging and dropping --- packages/client/src/components/Component.svelte | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/client/src/components/Component.svelte b/packages/client/src/components/Component.svelte index 2d586df24d..2a6c46feec 100644 --- a/packages/client/src/components/Component.svelte +++ b/packages/client/src/components/Component.svelte @@ -453,6 +453,7 @@ class:interactive class:editing class:block={isBlock} + class:explode={children.length && !isLayout && $builderStore.isDragging} data-id={id} data-name={name} data-icon={icon} @@ -481,15 +482,19 @@ .component { display: contents; } - + .component :global(> *) { + transition: padding 250ms ease, border 250ms ease; + } + .component.explode :global(> *) { + padding: 12px 4px !important; + border: 2px dashed var(--spectrum-global-color-gray-400) !important; + } .interactive :global(*:hover) { cursor: pointer; } - .draggable :global(*:hover) { cursor: grab; } - .editing :global(*:hover) { cursor: auto; } From 9a9ea26cc4086bbf86d739f7770ce48b524b1599 Mon Sep 17 00:00:00 2001 From: Mel O'Hagan Date: Thu, 29 Sep 2022 14:10:34 +0100 Subject: [PATCH 04/69] Navigate in PeekScreenDisplay --- .../overlay/PeekScreenDisplay.svelte | 3 +++ packages/client/src/utils/buttonActions.js | 21 +------------------ 2 files changed, 4 insertions(+), 20 deletions(-) diff --git a/packages/client/src/components/overlay/PeekScreenDisplay.svelte b/packages/client/src/components/overlay/PeekScreenDisplay.svelte index d6da9ca3f0..b9f4914624 100644 --- a/packages/client/src/components/overlay/PeekScreenDisplay.svelte +++ b/packages/client/src/components/overlay/PeekScreenDisplay.svelte @@ -45,6 +45,9 @@ }, [MessageTypes.CLOSE_SCREEN_MODAL]: () => { peekStore.actions.hidePeek() + if (message.data?.url) { + routeStore.actions.navigate(message.data.url) + } }, [MessageTypes.INVALIDATE_DATASOURCE]: () => { proxyInvalidation(message.data) diff --git a/packages/client/src/utils/buttonActions.js b/packages/client/src/utils/buttonActions.js index 74c4504ca6..e44ed2451c 100644 --- a/packages/client/src/utils/buttonActions.js +++ b/packages/client/src/utils/buttonActions.js @@ -226,28 +226,9 @@ const changeFormStepHandler = async (action, context) => { const closeScreenModalHandler = action => { let { url } = action.parameters - if (url) { - window.parent.addEventListener("message", event => { - const location = event.target.location - //remove any trailing slash - if (url.charAt(url.length - 1) === "/") { - url = url.substring(0, url.length - 1) - } - //need to reload if hash route has not changed - let shouldReload = - `#${url.substring(0, url.lastIndexOf("/"))}` === - location.hash?.substring(0, location.hash.lastIndexOf("/")) - - window.parent.location.href = `${location.origin}${location.pathname}#${url}` - if (shouldReload) { - window.parent.location.reload() - } - }) - } - // Emit this as a window event, so parent screens which are iframing us in // can close the modal - window.parent.postMessage({ type: "close-screen-modal" }) + window.parent.postMessage({ type: "close-screen-modal", url }) } const updateStateHandler = action => { From 428b78618423dab0e08a91b52172c719158542de Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Thu, 6 Oct 2022 09:17:26 +0100 Subject: [PATCH 05/69] Don't clear drop target on invalid selection --- .../client/src/components/Component.svelte | 6 +- .../src/components/preview/DNDHandler.svelte | 89 +++++++++++++++---- packages/client/src/stores/builder.js | 18 ++++ packages/client/src/stores/components.js | 10 ++- packages/client/src/stores/screens.js | 41 ++++++++- 5 files changed, 139 insertions(+), 25 deletions(-) diff --git a/packages/client/src/components/Component.svelte b/packages/client/src/components/Component.svelte index 2a6c46feec..f3c3e4b25c 100644 --- a/packages/client/src/components/Component.svelte +++ b/packages/client/src/components/Component.svelte @@ -96,6 +96,7 @@ $: selected = $builderStore.inBuilder && $builderStore.selectedComponentId === id $: inSelectedPath = $componentStore.selectedComponentPath?.includes(id) + $: inDropPath = $componentStore.dropPath?.includes(id) $: inDragPath = inSelectedPath && $builderStore.editMode // Derive definition properties which can all be optional, so need to be @@ -108,7 +109,7 @@ // Interactive components can be selected, dragged and highlighted inside // the builder preview $: builderInteractive = - $builderStore.inBuilder && insideScreenslot && !isBlock + $builderStore.inBuilder && insideScreenslot && !isBlock && !instance.static $: devToolsInteractive = $devToolsStore.allowSelection && !isBlock $: interactive = builderInteractive || devToolsInteractive $: editing = editable && selected && $builderStore.editMode @@ -453,10 +454,11 @@ class:interactive class:editing class:block={isBlock} - class:explode={children.length && !isLayout && $builderStore.isDragging} + class:explode={children.length && !isLayout && inDropPath && false} data-id={id} data-name={name} data-icon={icon} + data-placeholder={id === "placeholder"} > {#if hasMissingRequiredSettings} diff --git a/packages/client/src/components/preview/DNDHandler.svelte b/packages/client/src/components/preview/DNDHandler.svelte index c37eb93afa..03ee97d610 100644 --- a/packages/client/src/components/preview/DNDHandler.svelte +++ b/packages/client/src/components/preview/DNDHandler.svelte @@ -77,19 +77,16 @@ builderStore.actions.setDragging(false) } - // Callback when on top of a component - const onDragOver = e => { - // Skip if we aren't validly dragging currently - if (!dragInfo || !dropInfo) { - return + const validateDrop = (dropInfo, e) => { + if (!dropInfo) { + return null } - e.preventDefault() const { droppableInside, bounds } = dropInfo const { top, left, height, width } = bounds const mouseY = e.clientY const mouseX = e.clientX - const snapFactor = droppableInside ? 0.33 : 0.5 + const snapFactor = droppableInside ? 0.25 : 0.5 const snapLimitV = Math.min(40, height * snapFactor) const snapLimitH = Math.min(40, width * snapFactor) @@ -108,20 +105,32 @@ // When no edges match, drop inside if possible if (!sides.length) { - dropInfo.mode = droppableInside ? "inside" : null - dropInfo.side = null - return + if (droppableInside) { + return { + ...dropInfo, + mode: "inside", + side: null, + } + } else { + return null + } } // When one edge matches, use that edge if (sides.length === 1) { - dropInfo.side = sides[0] if ([Sides.Top, Sides.Left].includes(sides[0])) { - dropInfo.mode = "above" + return { + ...dropInfo, + mode: "above", + side: sides[0], + } } else { - dropInfo.mode = "below" + return { + ...dropInfo, + mode: "below", + side: sides[0], + } } - return } // When 2 edges match, work out which is closer @@ -134,9 +143,34 @@ const edge = delta1 < delta2 ? sides[0] : sides[1] dropInfo.side = edge if ([Sides.Top, Sides.Left].includes(edge)) { - dropInfo.mode = "above" + return { + ...dropInfo, + mode: "above", + side: edge, + } } else { - dropInfo.mode = "below" + return { + ...dropInfo, + mode: "below", + side: edge, + } + } + } + + // Callback when on top of a component + const onDragOver = e => { + // Skip if we aren't validly dragging currently + if (!dragInfo || !dropInfo) { + return + } + + e.preventDefault() + + const nextDropInfo = validateDrop(dropInfo, e) + if (nextDropInfo) { + console.log("set from over") + dropInfo = nextDropInfo + console.log(dropInfo.mode, dropInfo.target) } } @@ -147,6 +181,16 @@ return } + // Update drop target + const dropTarget = e.target.closest(".component") + builderStore.actions.setDropTarget(dropTarget?.dataset.id) + + // // Do nothing if this is the placeholder + // if (element.dataset.id === "placeholder") { + // console.log("placeholder") + // return + // } + const element = e.target.closest(".component:not(.block)") if ( element && @@ -172,15 +216,20 @@ // dragOver const child = getDOMNodeForComponent(e.target) const bounds = child.getBoundingClientRect() - dropInfo = { + let nextDropInfo = { target, name: element.dataset.name, icon: element.dataset.icon, droppableInside: element.classList.contains("empty"), bounds, } + nextDropInfo = validateDrop(nextDropInfo, e) + if (nextDropInfo) { + console.log("set from enter") + dropInfo = nextDropInfo + } } else { - dropInfo = null + // dropInfo = null } } @@ -200,6 +249,10 @@ } } + $: mode = dropInfo?.mode + $: target = dropInfo?.target + $: builderStore.actions.updateDNDPlaceholder(mode, target) + onMount(() => { // Events fired on the draggable target document.addEventListener("dragstart", onDragStart, false) diff --git a/packages/client/src/stores/builder.js b/packages/client/src/stores/builder.js index fea070c27c..f935c580b6 100644 --- a/packages/client/src/stores/builder.js +++ b/packages/client/src/stores/builder.js @@ -1,6 +1,7 @@ import { writable, get } from "svelte/store" import { API } from "api" import { devToolsStore } from "./devTools.js" +import { findComponentPathById } from "../utils/components.js" const dispatchEvent = (type, data = {}) => { window.parent.postMessage({ type, data }) @@ -20,6 +21,9 @@ const createBuilderStore = () => { navigation: null, hiddenComponentIds: [], usedPlugins: null, + dndMode: null, + dndTarget: null, + dropTarget: null, // Legacy - allow the builder to specify a layout layout: null, @@ -102,6 +106,20 @@ const createBuilderStore = () => { // Notify the builder so we can reload component definitions dispatchEvent("reload-plugin") }, + updateDNDPlaceholder: (mode, target) => { + console.log(mode, target) + store.update(state => { + state.dndMode = mode + state.dndTarget = target + return state + }) + }, + setDropTarget: target => { + store.update(state => { + state.dropTarget = target + return state + }) + }, } return { ...store, diff --git a/packages/client/src/stores/components.js b/packages/client/src/stores/components.js index 98edfaddae..ee75be484a 100644 --- a/packages/client/src/stores/components.js +++ b/packages/client/src/stores/components.js @@ -36,8 +36,13 @@ const createComponentStore = () => { const definition = getComponentDefinition(component?._component) // Derive the selected component path - const path = + const selectedPath = findComponentPathById(asset?.props, selectedComponentId) || [] + let dropPath = [] + if ($builderState.isDragging) { + dropPath = + findComponentPathById(asset?.props, $builderState.dropTarget) || [] + } return { customComponentManifest: $store.customComponentManifest, @@ -45,9 +50,10 @@ const createComponentStore = () => { $store.mountedComponents[selectedComponentId], selectedComponent: component, selectedComponentDefinition: definition, - selectedComponentPath: path?.map(component => component._id), + selectedComponentPath: selectedPath?.map(component => component._id), mountedComponentCount: Object.keys($store.mountedComponents).length, currentAsset: asset, + dropPath: dropPath?.map(component => component._id), } } ) diff --git a/packages/client/src/stores/screens.js b/packages/client/src/stores/screens.js index 84cd4000c1..2920ee333e 100644 --- a/packages/client/src/stores/screens.js +++ b/packages/client/src/stores/screens.js @@ -3,6 +3,11 @@ import { routeStore } from "./routes" import { builderStore } from "./builder" import { appStore } from "./app" import { RoleUtils } from "@budibase/frontend-core" +import { + findComponentById, + findComponentPathById, +} from "../utils/components.js" +import { Helpers } from "@budibase/bbui" const createScreenStore = () => { const store = derived( @@ -13,7 +18,7 @@ const createScreenStore = () => { if ($builderStore.inBuilder) { // Use builder defined definitions if inside the builder preview - activeScreen = $builderStore.screen + activeScreen = Helpers.cloneDeep($builderStore.screen) screens = [activeScreen] // Legacy - allow the builder to specify a layout @@ -24,8 +29,10 @@ const createScreenStore = () => { // Find the correct screen by matching the current route screens = $appStore.screens || [] if ($routeStore.activeRoute) { - activeScreen = screens.find( - screen => screen._id === $routeStore.activeRoute.screenId + activeScreen = Helpers.cloneDeep( + screens.find( + screen => screen._id === $routeStore.activeRoute.screenId + ) ) } @@ -40,6 +47,34 @@ const createScreenStore = () => { } } + // Insert DND placeholder if required + const { dndTarget, dndMode, selectedComponentId } = $builderStore + const insert = false + if (insert && activeScreen && dndTarget && dndMode) { + let selectedComponent = findComponentById( + activeScreen.props, + selectedComponentId + ) + const placeholder = { + ...selectedComponent, + _id: "placeholder", + static: true, + } + // delete selectedComponent._component + if (dndMode === "inside") { + const target = findComponentById(activeScreen.props, dndTarget) + target._children = [placeholder] + } else { + const path = findComponentPathById(activeScreen.props, dndTarget) + const parent = path?.[path.length - 2] + if (parent) { + const idx = parent._children.findIndex(x => x._id === dndTarget) + const delta = dndMode === "below" ? 1 : -1 + parent._children.splice(idx + delta, 0, placeholder) + } + } + } + // Assign ranks to screens, preferring higher roles and home screens screens.forEach(screen => { const roleId = screen.routing.roleId From 4322345aca988febff85f651d378bb33b1eb7e06 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 7 Oct 2022 08:05:44 +0100 Subject: [PATCH 06/69] Rewrite drag and drop from scratch using mouse position heuristics --- .../client/src/components/Component.svelte | 20 +- .../src/components/preview/DNDHandler.svelte | 351 +++++++++++------- .../src/components/preview/Placeholder.svelte | 11 + .../preview/PlaceholderOverlay.svelte | 43 +++ packages/client/src/index.js | 1 + packages/client/src/stores/builder.js | 19 +- packages/client/src/stores/components.js | 11 +- packages/client/src/stores/screens.js | 32 +- 8 files changed, 315 insertions(+), 173 deletions(-) create mode 100644 packages/client/src/components/preview/Placeholder.svelte create mode 100644 packages/client/src/components/preview/PlaceholderOverlay.svelte diff --git a/packages/client/src/components/Component.svelte b/packages/client/src/components/Component.svelte index f3c3e4b25c..93620606c8 100644 --- a/packages/client/src/components/Component.svelte +++ b/packages/client/src/components/Component.svelte @@ -27,6 +27,8 @@ export let isLayout = false export let isScreen = false export let isBlock = false + export let parent = null + export let index = 0 // Get parent contexts const context = getContext("context") @@ -96,7 +98,7 @@ $: selected = $builderStore.inBuilder && $builderStore.selectedComponentId === id $: inSelectedPath = $componentStore.selectedComponentPath?.includes(id) - $: inDropPath = $componentStore.dropPath?.includes(id) + $: isDndParent = $componentStore.dndParent === id $: inDragPath = inSelectedPath && $builderStore.editMode // Derive definition properties which can all be optional, so need to be @@ -119,7 +121,7 @@ !isLayout && !isScreen && definition?.draggable !== false - $: droppable = interactive && !isLayout && !isScreen + $: droppable = interactive $: builderHidden = $builderStore.inBuilder && $builderStore.hiddenComponentIds?.includes(id) @@ -451,21 +453,24 @@ class:draggable class:droppable class:empty + class:parent={hasChildren} class:interactive class:editing class:block={isBlock} - class:explode={children.length && !isLayout && inDropPath && false} + class:explode={interactive && hasChildren && $builderStore.isDragging} + class:placeholder={id === "placeholder"} data-id={id} data-name={name} data-icon={icon} - data-placeholder={id === "placeholder"} + data-parent={parent} + data-index={index} > {#if hasMissingRequiredSettings} {:else if children.length} - {#each children as child (child._id)} - + {#each children as child, idx (child._id)} + {/each} {:else if emptyState} {#if isScreen} @@ -488,8 +493,9 @@ transition: padding 250ms ease, border 250ms ease; } .component.explode :global(> *) { - padding: 12px 4px !important; + padding: 16px !important; border: 2px dashed var(--spectrum-global-color-gray-400) !important; + border-radius: 4px !important; } .interactive :global(*:hover) { cursor: pointer; diff --git a/packages/client/src/components/preview/DNDHandler.svelte b/packages/client/src/components/preview/DNDHandler.svelte index 03ee97d610..37bd9d3402 100644 --- a/packages/client/src/components/preview/DNDHandler.svelte +++ b/packages/client/src/components/preview/DNDHandler.svelte @@ -12,10 +12,12 @@ import { get } from "svelte/store" import IndicatorSet from "./IndicatorSet.svelte" import DNDPositionIndicator from "./DNDPositionIndicator.svelte" - import { builderStore } from "stores" + import { builderStore, componentStore } from "stores" + import PlaceholderOverlay from "./PlaceholderOverlay.svelte" let dragInfo let dropInfo + let placeholderInfo const getEdges = (bounds, mousePoint) => { const { width, height, top, left } = bounds @@ -33,39 +35,37 @@ return Math.sqrt(deltaX * deltaX + deltaY * deltaY) } - const getDOMNodeForComponent = component => { - const parent = component.closest(".component") - const children = Array.from(parent.children) - return children[0] + const getDOMNode = id => { + const component = document.getElementsByClassName(id)[0] + return [...component.children][0] } // Callback when initially starting a drag on a draggable component const onDragStart = e => { - const parent = e.target.closest(".component") - if (!parent?.classList.contains("draggable")) { + const component = e.target.closest(".component") + if (!component?.classList.contains("draggable")) { return } // Update state dragInfo = { - target: parent.dataset.id, - parent: parent.dataset.parent, + target: component.dataset.id, } builderStore.actions.selectComponent(dragInfo.target) builderStore.actions.setDragging(true) // Highlight being dragged by setting opacity - const child = getDOMNodeForComponent(e.target) + const child = getDOMNode(component.dataset.id) if (child) { child.style.opacity = "0.5" } } // Callback when drag stops (whether dropped or not) - const onDragEnd = e => { + const onDragEnd = () => { // Reset opacity style if (dragInfo) { - const child = getDOMNodeForComponent(e.target) + const child = getDOMNode(dragInfo.target) if (child) { child.style.opacity = "" } @@ -77,84 +77,179 @@ builderStore.actions.setDragging(false) } - const validateDrop = (dropInfo, e) => { + const variance = arr => { + const mean = arr.reduce((a, b) => a + b, 0) / arr.length + let squareSum = 0 + arr.forEach(value => { + const delta = value - mean + squareSum += delta * delta + }) + return squareSum / arr.length + } + + const handleEvent = e => { if (!dropInfo) { return null } + e.preventDefault() - const { droppableInside, bounds } = dropInfo - const { top, left, height, width } = bounds + let { id, parent, node, index, acceptsChildren, empty } = dropInfo const mouseY = e.clientY const mouseX = e.clientX - const snapFactor = droppableInside ? 0.25 : 0.5 - const snapLimitV = Math.min(40, height * snapFactor) - const snapLimitH = Math.min(40, width * snapFactor) - // Determine all sies we are within snap range of - let sides = [] - if (mouseY <= top + snapLimitV) { - sides.push(Sides.Top) - } else if (mouseY >= top + height - snapLimitV) { - sides.push(Sides.Bottom) - } - if (mouseX < left + snapLimitH) { - sides.push(Sides.Left) - } else if (mouseX > left + width - snapLimitH) { - sides.push(Sides.Right) + // if (!dropInfo.bounds) { + // } else { + // dropInfo.bounds.top = node.offsetTop + // dropInfo.bounds.left = node.offsetLeft + // console.log(node.offsetTop) + // } + + // console.log("calc") + // dropInfo.bounds = bounds + + // If we're over something that does not accept children then we must go + // above or below this component + if (!acceptsChildren) { + id = parent + acceptsChildren = true + empty = false + node = getDOMNode(parent) + // + // + // const bounds = node.getBoundingClientRect() + // const { top, left, height, width } = bounds + // const snapFactor = 0.5 + // const snapLimitV = Math.min(40, height * snapFactor) + // const snapLimitH = Math.min(40, width * snapFactor) + // + // // Determine all sides we are within snap range of + // let sides = [] + // if (mouseY <= top + snapLimitV) { + // sides.push(Sides.Top) + // } else if (mouseY >= top + height - snapLimitV) { + // sides.push(Sides.Bottom) + // } + // if (mouseX < left + snapLimitH) { + // sides.push(Sides.Left) + // } else if (mouseX > left + width - snapLimitH) { + // sides.push(Sides.Right) + // } + // + // // If we're somehow not in range of any side, do nothing + // if (!sides.length) { + // console.log("no sides match") + // return + // } + // + // let side + // if (sides.length === 1) { + // // When one edge matches, use that edge + // side = sides[0] + // } else { + // // When 2 edges match, work out which is closer + // const mousePoint = [mouseX, mouseY] + // const edges = getEdges(bounds, mousePoint) + // const edge1 = edges[sides[0]] + // const delta1 = calculatePointDelta(mousePoint, edge1) + // const edge2 = edges[sides[1]] + // const delta2 = calculatePointDelta(mousePoint, edge2) + // side = delta1 < delta2 ? sides[0] : sides[1] + // } + // if ([Sides.Top, Sides.Left].includes(side)) { + // // Before, so use the current index + // console.log("before") + // placeholderInfo = { + // parent: parent, + // index: index, + // } + // } else { + // console.log("after") + // // After, so use the next index + // placeholderInfo = { + // parent: parent, + // index: index + 1, + // } + // } } - // When no edges match, drop inside if possible - if (!sides.length) { - if (droppableInside) { - return { - ...dropInfo, - mode: "inside", - side: null, - } - } else { - return null + // We're now hovering over something which does accept children. + // If it is empty, just go inside it + if (empty) { + placeholderInfo = { + parent: id, + index: 0, } + return } - // When one edge matches, use that edge - if (sides.length === 1) { - if ([Sides.Top, Sides.Left].includes(sides[0])) { - return { - ...dropInfo, - mode: "above", - side: sides[0], - } - } else { - return { - ...dropInfo, - mode: "below", - side: sides[0], - } - } + // We're now hovering over something which accepts children and is not + // empty, so we need to work out where to inside the placeholder + + // Check we're actually inside + // if ( + // mouseY < top || + // mouseY > top + height || + // mouseX < left || + // mouseX > left + width + // ) { + // console.log("not inside") + // return + // } + + // Get all DOM nodes of children of this component. + // Filter out the placeholder as we don't want it to affect the index of + // the new placeholder. + const children = [...(node.children || [])] + .filter(x => !x.classList.contains("placeholder")) + .map(x => x.children[0]) + + // Calculate centers of each child + const centers = children.map(child => { + const childBounds = child.getBoundingClientRect() + return [ + childBounds.left + childBounds.width / 2, + childBounds.top + childBounds.height / 2, + ] + }) + + // Calculate variance of X and Y centers to determine layout + const xCoords = centers.map(x => x[0]) + const yCoords = centers.map(x => x[1]) + const xVariance = variance(xCoords) + const yVariance = variance(yCoords) + const column = xVariance <= yVariance + console.log(column ? "COL" : "ROW") + + // Now that we know the layout, find which children in this axis we are + // between + const childPositions = column ? yCoords : xCoords + const mousePosition = column ? mouseY : mouseX + + let idx = 0 + while (idx < children.length && childPositions[idx] < mousePosition) { + idx++ } - // When 2 edges match, work out which is closer - const mousePoint = [mouseX, mouseY] - const edges = getEdges(bounds, mousePoint) - const edge1 = edges[sides[0]] - const delta1 = calculatePointDelta(mousePoint, edge1) - const edge2 = edges[sides[1]] - const delta2 = calculatePointDelta(mousePoint, edge2) - const edge = delta1 < delta2 ? sides[0] : sides[1] - dropInfo.side = edge - if ([Sides.Top, Sides.Left].includes(edge)) { - return { - ...dropInfo, - mode: "above", - side: edge, - } - } else { - return { - ...dropInfo, - mode: "below", - side: edge, - } + placeholderInfo = { + parent: id, + index: idx, } + // // When no edges match, drop inside if possible + // if (!sides.length) { + // if (empty) { + // console.log("allowed inside") + // return { + // ...dropInfo, + // mode: "inside", + // side: null, + // bounds, + // } + // } else { + // // No sides but also not empty? + // console.log("no sides match, but not empty") + // return null + // } + // } } // Callback when on top of a component @@ -163,15 +258,7 @@ if (!dragInfo || !dropInfo) { return } - - e.preventDefault() - - const nextDropInfo = validateDrop(dropInfo, e) - if (nextDropInfo) { - console.log("set from over") - dropInfo = nextDropInfo - console.log(dropInfo.mode, dropInfo.target) - } + handleEvent(e) } // Callback when entering a potential drop target @@ -181,53 +268,53 @@ return } - // Update drop target - const dropTarget = e.target.closest(".component") - builderStore.actions.setDropTarget(dropTarget?.dataset.id) - - // // Do nothing if this is the placeholder + // Do nothing if this is the placeholder // if (element.dataset.id === "placeholder") { // console.log("placeholder") // return // } - const element = e.target.closest(".component:not(.block)") + const component = e.target.closest(".component:not(.block)") if ( - element && - element.classList.contains("droppable") && - element.dataset.id !== dragInfo.target + component && + component.classList.contains("droppable") && + component.dataset.id !== dragInfo.target ) { // Do nothing if this is the same target - if (element.dataset.id === dropInfo?.target) { + if (component.dataset.id === dropInfo?.target) { return } // Ensure the dragging flag is always set. // There's a bit of a race condition between the app reinitialisation // after selecting the DND component and setting this the first time - if (!get(builderStore).isDragging) { - builderStore.actions.setDragging(true) - } - - // Store target ID - const target = element.dataset.id + // if (!get(builderStore).isDragging) { + // builderStore.actions.setDragging(true) + // } // Precompute and store some info to avoid recalculating everything in // dragOver - const child = getDOMNodeForComponent(e.target) - const bounds = child.getBoundingClientRect() - let nextDropInfo = { - target, - name: element.dataset.name, - icon: element.dataset.icon, - droppableInside: element.classList.contains("empty"), - bounds, - } - nextDropInfo = validateDrop(nextDropInfo, e) - if (nextDropInfo) { - console.log("set from enter") - dropInfo = nextDropInfo + dropInfo = { + id: component.dataset.id, + parent: component.dataset.parent, + index: parseInt(component.dataset.index), + node: getDOMNode(component.dataset.id), + empty: component.classList.contains("empty"), + acceptsChildren: component.classList.contains("parent"), } + + // console.log( + // "enter", + // component.dataset.name, + // "id", + // dropInfo.id, + // "parent", + // dropInfo.parent, + // "index", + // dropInfo.index + // ) + + handleEvent(e) } else { // dropInfo = null } @@ -240,18 +327,22 @@ // Callback when dropping a drag on top of some component const onDrop = e => { e.preventDefault() + dropInfo = null + placeholderInfo = null + dragInfo = null + builderStore.actions.setDragging(false) if (dropInfo?.mode) { - builderStore.actions.moveComponent( - dragInfo.target, - dropInfo.target, - dropInfo.mode - ) + // builderStore.actions.moveComponent( + // dragInfo.target, + // dropInfo.target, + // dropInfo.mode + // ) } } - $: mode = dropInfo?.mode - $: target = dropInfo?.target - $: builderStore.actions.updateDNDPlaceholder(mode, target) + $: parent = placeholderInfo?.parent + $: index = placeholderInfo?.index + $: builderStore.actions.updateDNDPlaceholder(parent, index) onMount(() => { // Events fired on the draggable target @@ -279,16 +370,20 @@ - +{#if $builderStore.isDragging} + +{/if} + + + + + + + diff --git a/packages/client/src/components/preview/Placeholder.svelte b/packages/client/src/components/preview/Placeholder.svelte new file mode 100644 index 0000000000..3457a49e6d --- /dev/null +++ b/packages/client/src/components/preview/Placeholder.svelte @@ -0,0 +1,11 @@ +
+ + diff --git a/packages/client/src/components/preview/PlaceholderOverlay.svelte b/packages/client/src/components/preview/PlaceholderOverlay.svelte new file mode 100644 index 0000000000..86950c533e --- /dev/null +++ b/packages/client/src/components/preview/PlaceholderOverlay.svelte @@ -0,0 +1,43 @@ + + +{#if left != null} +
+{/if} + + diff --git a/packages/client/src/index.js b/packages/client/src/index.js index 0e8ab8c258..e31de18e4a 100644 --- a/packages/client/src/index.js +++ b/packages/client/src/index.js @@ -24,6 +24,7 @@ let app const loadBudibase = async () => { // Update builder store with any builder flags builderStore.set({ + ...get(builderStore), inBuilder: !!window["##BUDIBASE_IN_BUILDER##"], layout: window["##BUDIBASE_PREVIEW_LAYOUT##"], screen: window["##BUDIBASE_PREVIEW_SCREEN##"], diff --git a/packages/client/src/stores/builder.js b/packages/client/src/stores/builder.js index f935c580b6..0930ef62b8 100644 --- a/packages/client/src/stores/builder.js +++ b/packages/client/src/stores/builder.js @@ -21,9 +21,9 @@ const createBuilderStore = () => { navigation: null, hiddenComponentIds: [], usedPlugins: null, - dndMode: null, - dndTarget: null, - dropTarget: null, + + dndParent: null, + dndIndex: null, // Legacy - allow the builder to specify a layout layout: null, @@ -106,17 +106,10 @@ const createBuilderStore = () => { // Notify the builder so we can reload component definitions dispatchEvent("reload-plugin") }, - updateDNDPlaceholder: (mode, target) => { - console.log(mode, target) + updateDNDPlaceholder: (parent, index) => { store.update(state => { - state.dndMode = mode - state.dndTarget = target - return state - }) - }, - setDropTarget: target => { - store.update(state => { - state.dropTarget = target + state.dndParent = parent + state.dndIndex = index return state }) }, diff --git a/packages/client/src/stores/components.js b/packages/client/src/stores/components.js index ee75be484a..e73b498648 100644 --- a/packages/client/src/stores/components.js +++ b/packages/client/src/stores/components.js @@ -5,6 +5,7 @@ import { devToolsStore } from "./devTools" import { screenStore } from "./screens" import { builderStore } from "./builder" import Router from "../components/Router.svelte" +import Placeholder from "../components/preview/Placeholder.svelte" import * as AppComponents from "../components/app/index.js" const budibasePrefix = "@budibase/standard-components/" @@ -38,11 +39,6 @@ const createComponentStore = () => { // Derive the selected component path const selectedPath = findComponentPathById(asset?.props, selectedComponentId) || [] - let dropPath = [] - if ($builderState.isDragging) { - dropPath = - findComponentPathById(asset?.props, $builderState.dropTarget) || [] - } return { customComponentManifest: $store.customComponentManifest, @@ -53,7 +49,6 @@ const createComponentStore = () => { selectedComponentPath: selectedPath?.map(component => component._id), mountedComponentCount: Object.keys($store.mountedComponents).length, currentAsset: asset, - dropPath: dropPath?.map(component => component._id), } } ) @@ -113,6 +108,8 @@ const createComponentStore = () => { // Screenslot is an edge case if (type === "screenslot") { type = `${budibasePrefix}${type}` + } else if (type === "placeholder") { + return {} } // Handle built-in components @@ -132,6 +129,8 @@ const createComponentStore = () => { } if (type === "screenslot") { return Router + } else if (type === "placeholder") { + return Placeholder } // Handle budibase components diff --git a/packages/client/src/stores/screens.js b/packages/client/src/stores/screens.js index 2920ee333e..e642fa9004 100644 --- a/packages/client/src/stores/screens.js +++ b/packages/client/src/stores/screens.js @@ -48,30 +48,24 @@ const createScreenStore = () => { } // Insert DND placeholder if required - const { dndTarget, dndMode, selectedComponentId } = $builderStore - const insert = false - if (insert && activeScreen && dndTarget && dndMode) { - let selectedComponent = findComponentById( - activeScreen.props, - selectedComponentId - ) + const { dndParent, dndIndex } = $builderStore + const insert = true + if (insert && activeScreen && dndParent && dndIndex != null) { + // let selectedComponent = findComponentById( + // activeScreen.props, + // selectedComponentId + // ) + // delete selectedComponent._component const placeholder = { - ...selectedComponent, + _component: "placeholder", _id: "placeholder", static: true, } - // delete selectedComponent._component - if (dndMode === "inside") { - const target = findComponentById(activeScreen.props, dndTarget) - target._children = [placeholder] + let parent = findComponentById(activeScreen.props, dndParent) + if (!parent._children?.length) { + parent._children = [placeholder] } else { - const path = findComponentPathById(activeScreen.props, dndTarget) - const parent = path?.[path.length - 2] - if (parent) { - const idx = parent._children.findIndex(x => x._id === dndTarget) - const delta = dndMode === "below" ? 1 : -1 - parent._children.splice(idx + delta, 0, placeholder) - } + parent._children.splice(dndIndex, 0, placeholder) } } From 9df86362b921e4877309e7e7f09ef7e96215b1e5 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 7 Oct 2022 08:20:51 +0100 Subject: [PATCH 07/69] Only explode components when dragging over them --- packages/client/src/components/Component.svelte | 4 ++-- packages/client/src/stores/components.js | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/client/src/components/Component.svelte b/packages/client/src/components/Component.svelte index 93620606c8..ba5ca95f5a 100644 --- a/packages/client/src/components/Component.svelte +++ b/packages/client/src/components/Component.svelte @@ -98,8 +98,8 @@ $: selected = $builderStore.inBuilder && $builderStore.selectedComponentId === id $: inSelectedPath = $componentStore.selectedComponentPath?.includes(id) - $: isDndParent = $componentStore.dndParent === id $: inDragPath = inSelectedPath && $builderStore.editMode + $: inDndPath = $componentStore.dndPath?.includes(id) // Derive definition properties which can all be optional, so need to be // coerced to booleans @@ -457,7 +457,7 @@ class:interactive class:editing class:block={isBlock} - class:explode={interactive && hasChildren && $builderStore.isDragging} + class:explode={interactive && hasChildren && inDndPath} class:placeholder={id === "placeholder"} data-id={id} data-name={name} diff --git a/packages/client/src/stores/components.js b/packages/client/src/stores/components.js index e73b498648..43bc1c2ff6 100644 --- a/packages/client/src/stores/components.js +++ b/packages/client/src/stores/components.js @@ -39,6 +39,8 @@ const createComponentStore = () => { // Derive the selected component path const selectedPath = findComponentPathById(asset?.props, selectedComponentId) || [] + const dndPath = + findComponentPathById(asset?.props, $builderState.dndParent) || [] return { customComponentManifest: $store.customComponentManifest, @@ -49,6 +51,7 @@ const createComponentStore = () => { selectedComponentPath: selectedPath?.map(component => component._id), mountedComponentCount: Object.keys($store.mountedComponents).length, currentAsset: asset, + dndPath: dndPath?.map(component => component._id), } } ) From fd3662e6b2b7de06b6478bdb9366c3521c13376a Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 7 Oct 2022 08:46:38 +0100 Subject: [PATCH 08/69] Improve row vs column detection to fix any edge cases --- .../client/src/components/Component.svelte | 6 +- .../src/components/preview/DNDHandler.svelte | 219 ++++-------------- .../preview/DNDPositionIndicator.svelte | 66 ------ 3 files changed, 44 insertions(+), 247 deletions(-) delete mode 100644 packages/client/src/components/preview/DNDPositionIndicator.svelte diff --git a/packages/client/src/components/Component.svelte b/packages/client/src/components/Component.svelte index ba5ca95f5a..0f60c7f707 100644 --- a/packages/client/src/components/Component.svelte +++ b/packages/client/src/components/Component.svelte @@ -28,7 +28,6 @@ export let isScreen = false export let isBlock = false export let parent = null - export let index = 0 // Get parent contexts const context = getContext("context") @@ -463,14 +462,13 @@ data-name={name} data-icon={icon} data-parent={parent} - data-index={index} > {#if hasMissingRequiredSettings} {:else if children.length} - {#each children as child, idx (child._id)} - + {#each children as child (child._id)} + {/each} {:else if emptyState} {#if isScreen} diff --git a/packages/client/src/components/preview/DNDHandler.svelte b/packages/client/src/components/preview/DNDHandler.svelte index 37bd9d3402..50ea287a9b 100644 --- a/packages/client/src/components/preview/DNDHandler.svelte +++ b/packages/client/src/components/preview/DNDHandler.svelte @@ -9,32 +9,14 @@ - -{#key renderKey} - {#if dimensions && dropInfo?.mode !== "inside"} - - {/if} -{/key} From cb6a13fafd431cc4fee5298d5492c38a8bcc2b5f Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 7 Oct 2022 12:45:22 +0100 Subject: [PATCH 09/69] Fix issue with layout determination --- .../src/components/preview/DNDHandler.svelte | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/client/src/components/preview/DNDHandler.svelte b/packages/client/src/components/preview/DNDHandler.svelte index 50ea287a9b..f7783990a5 100644 --- a/packages/client/src/components/preview/DNDHandler.svelte +++ b/packages/client/src/components/preview/DNDHandler.svelte @@ -117,19 +117,22 @@ }) // Calculate the variance between each set of positions on the children - const variances = Object.keys(childCoords[0]).map(key => { - const coords = childCoords.map(x => x[key]) - return { - variance: variance(coords), - side: key, - } - }) + const variances = Object.keys(childCoords[0]) + .filter(x => x !== "placeholder") + .map(key => { + const coords = childCoords.map(x => x[key]) + return { + variance: variance(coords), + side: key, + } + }) // Sort by variance. The lowest variance position indicates whether we are // in a row or column layout variances.sort((a, b) => { return a.variance < b.variance ? -1 : 1 }) + console.log(variances[0].side) const column = ["centerX", "left", "right"].includes(variances[0].side) console.log(column ? "COL" : "ROW") From 4a0be4523b45a4406f4f482edad080aa89b2b983 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 7 Oct 2022 14:34:47 +0100 Subject: [PATCH 10/69] Hide the selected component when dragging --- packages/client/src/stores/screens.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/client/src/stores/screens.js b/packages/client/src/stores/screens.js index e642fa9004..d5525d15c7 100644 --- a/packages/client/src/stores/screens.js +++ b/packages/client/src/stores/screens.js @@ -48,14 +48,14 @@ const createScreenStore = () => { } // Insert DND placeholder if required - const { dndParent, dndIndex } = $builderStore + const { dndParent, dndIndex, selectedComponentId } = $builderStore const insert = true if (insert && activeScreen && dndParent && dndIndex != null) { - // let selectedComponent = findComponentById( - // activeScreen.props, - // selectedComponentId - // ) - // delete selectedComponent._component + let selectedComponent = findComponentById( + activeScreen.props, + selectedComponentId + ) + delete selectedComponent._component const placeholder = { _component: "placeholder", _id: "placeholder", From ab4eebc0cf0876e3bfd03212f17090f18a394cb7 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 7 Oct 2022 20:00:25 +0100 Subject: [PATCH 11/69] Reduce jank by computing symmetrical component breakpoint whens considering DND candidate positions and ignoring the hidden selected component --- .../client/src/components/Component.svelte | 7 + .../src/components/preview/DNDHandler.svelte | 172 +++++++++--------- packages/client/src/stores/screens.js | 3 + 3 files changed, 93 insertions(+), 89 deletions(-) diff --git a/packages/client/src/components/Component.svelte b/packages/client/src/components/Component.svelte index 0f60c7f707..005eb12f2d 100644 --- a/packages/client/src/components/Component.svelte +++ b/packages/client/src/components/Component.svelte @@ -407,6 +407,7 @@ } const scrollIntoView = () => { + return const node = document.getElementsByClassName(id)?.[0]?.children[0] if (!node) { return @@ -458,6 +459,9 @@ class:block={isBlock} class:explode={interactive && hasChildren && inDndPath} class:placeholder={id === "placeholder"} + class:screen={isScreen} + class:dragging={$builderStore.selectedComponentId === id && + $builderStore.isDragging} data-id={id} data-name={name} data-icon={icon} @@ -504,4 +508,7 @@ .editing :global(*:hover) { cursor: auto; } + .dragging { + pointer-events: none; + } diff --git a/packages/client/src/components/preview/DNDHandler.svelte b/packages/client/src/components/preview/DNDHandler.svelte index f7783990a5..fbfdf06310 100644 --- a/packages/client/src/components/preview/DNDHandler.svelte +++ b/packages/client/src/components/preview/DNDHandler.svelte @@ -1,12 +1,3 @@ - - diff --git a/packages/client/src/stores/screens.js b/packages/client/src/stores/screens.js index d5525d15c7..85293cd0ce 100644 --- a/packages/client/src/stores/screens.js +++ b/packages/client/src/stores/screens.js @@ -65,6 +65,9 @@ const createScreenStore = () => { if (!parent._children?.length) { parent._children = [placeholder] } else { + parent._children = parent._children.filter( + x => x._id !== selectedComponentId + ) parent._children.splice(dndIndex, 0, placeholder) } } From e48bbb451e315741cde790da95c4a43b7f9a7e2b Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Sat, 8 Oct 2022 15:03:44 +0100 Subject: [PATCH 12/69] Add throttle utility as an improved debounce --- packages/frontend-core/src/utils/utils.js | 36 +++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/packages/frontend-core/src/utils/utils.js b/packages/frontend-core/src/utils/utils.js index 587d057351..ed76f13494 100644 --- a/packages/frontend-core/src/utils/utils.js +++ b/packages/frontend-core/src/utils/utils.js @@ -4,6 +4,8 @@ * @param fn the async function to run * @return {Promise} a sequential version of the function */ +import { lastIndexOf } from "lodash/array" + export const sequential = fn => { let queue = [] return async (...params) => { @@ -40,3 +42,37 @@ export const debounce = (callback, minDelay = 1000) => { }) } } + +/** + * Utility to throttle invocations of a synchronous function. This is better + * than a simple debounce invocation for a number of reasons. Features include: + * - First invocation is immediate (no initial delay) + * - Every invocation has the latest params (no stale params) + * - There will always be a final invocation with the last params (no missing + * final update) + * @param callback + * @param minDelay + * @returns {Function} a throttled version function + */ +export const throttle = (callback, minDelay = 1000) => { + let lastParams + let stalled = false + let pending = false + const invoke = (...params) => { + lastParams = params + if (stalled) { + pending = true + return + } + callback(...lastParams) + stalled = true + setTimeout(() => { + stalled = false + if (pending) { + pending = false + invoke(...lastParams) + } + }, minDelay) + } + return invoke +} From e9dfc9ad9f646c3e546f598115fa78c2a03be95e Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Sat, 8 Oct 2022 15:04:18 +0100 Subject: [PATCH 13/69] Throttle updates to prevent all jank and revert to component center breakboints for DND candidates --- .../client/src/components/Component.svelte | 3 +- .../src/components/preview/DNDHandler.svelte | 111 ++++++++++++++---- 2 files changed, 89 insertions(+), 25 deletions(-) diff --git a/packages/client/src/components/Component.svelte b/packages/client/src/components/Component.svelte index 005eb12f2d..e6dbbf8579 100644 --- a/packages/client/src/components/Component.svelte +++ b/packages/client/src/components/Component.svelte @@ -495,7 +495,8 @@ transition: padding 250ms ease, border 250ms ease; } .component.explode :global(> *) { - padding: 16px !important; + padding: 32px !important; + gap: 16px !important; border: 2px dashed var(--spectrum-global-color-gray-400) !important; border-radius: 4px !important; } diff --git a/packages/client/src/components/preview/DNDHandler.svelte b/packages/client/src/components/preview/DNDHandler.svelte index fbfdf06310..20bbc8bada 100644 --- a/packages/client/src/components/preview/DNDHandler.svelte +++ b/packages/client/src/components/preview/DNDHandler.svelte @@ -3,6 +3,7 @@ import IndicatorSet from "./IndicatorSet.svelte" import { builderStore } from "stores" import PlaceholderOverlay from "./PlaceholderOverlay.svelte" + import { Utils } from "@budibase/frontend-core" let dragInfo let dropInfo @@ -75,15 +76,34 @@ }, 0) } - const handleEvent = e => { + let lastX + let lastY + let lastTime + + const processEvent = (mouseX, mouseY) => { if (!dropInfo) { return null } - e.preventDefault() let { id, parent, node, acceptsChildren, empty } = dropInfo - const mouseY = e.clientY - const mouseX = e.clientX + + // Debounce by 10px difference + // if (lastX != null && lastY != null) { + // const delta = Math.abs(mouseY - lastY) + Math.abs(mouseX - lastX) + // if (delta < 10) { + // console.log("delta fail") + // return + // } + // } + // lastX = mouseX + // lastY = mouseY + + // Debounce by time + // if (Date.now() - (lastTime || 0) < 100) { + // console.log("time fail") + // return + // } + // lastTime = Date.now() // If we're over something that does not accept children then we go up a // level and consider the mouse position relative to the parent @@ -112,7 +132,7 @@ const childCoords = [...(node.children || [])].map(node => { const bounds = node.children[0].getBoundingClientRect() return { - // placeholder: node.classList.contains("placeholder"), + placeholder: node.classList.contains("placeholder"), centerX: bounds.left + bounds.width / 2, centerY: bounds.top + bounds.height / 2, left: bounds.left, @@ -139,36 +159,79 @@ const column = ["centerX", "left", "right"].includes(variances[0].side) console.log(column ? "COL" : "ROW") - // Calculate breakpoints between children - let midpoints = [] - for (let i = 0; i < childCoords.length - 1; i++) { - const child1 = childCoords[i] - const child2 = childCoords[i + 1] - let midpoint - if (column) { - const top = Math.min(child1.top, child2.top) - const bottom = Math.max(child1.bottom, child2.bottom) - midpoint = (top + bottom) / 2 - } else { - const left = Math.min(child1.left, child2.left) - const right = Math.max(child1.right, child2.right) - midpoint = (left + right) / 2 - } - midpoints.push(midpoint) - } - // let midpoints = childCoords.map(x => (column ? x.centerY : x.centerX)) + /** SYMMETRICAL BREAKPOINTS **/ + // let breakpoints = [] + // for (let i = 0; i < childCoords.length - 1; i++) { + // const child1 = childCoords[i] + // const child2 = childCoords[i + 1] + // let breakpoint + // if (column) { + // const top = Math.min(child1.top, child2.top) + // const bottom = Math.max(child1.bottom, child2.bottom) + // breakpoint = (top + bottom) / 2 + // } else { + // const left = Math.min(child1.left, child2.left) + // const right = Math.max(child1.right, child2.right) + // breakpoint = (left + right) / 2 + // } + // breakpoints.push(breakpoint) + // } + + /** CENTER BREAKPOINTS **/ + let breakpoints = childCoords + .filter(x => !x.placeholder) + .map(x => { + return column ? x.centerY : x.centerX + }) + + /** NEXT EDGE BREAKPOINTS **/ + // let breakpoints = [] + // for (let i = 0; i < childCoords.length; i++) { + // let breakpoint + // if (column) { + // if (mouseY > childCoords[i].top && mouseY < childCoords[i].bottom) { + // // Inside this container + // if (childCoords[i + 1]) { + // breakpoint = childCoords[i + 1].top + // } else { + // breakpoint = childCoords[i].top + // } + // } else { + // breakpoint = + // mouseY < childCoords[i].bottom + // ? childCoords[i].top + // : childCoords[i].bottom + // } + // } else { + // breakpoint = + // mouseX < childCoords[i].left + // ? childCoords[i].left + // : childCoords[i].right + // } + // breakpoints.push(breakpoint) + // } // Determine the index to drop the component in const mousePosition = column ? mouseY : mouseX + let idx = 0 - while (idx < midpoints.length && midpoints[idx] < mousePosition) { + while (idx < breakpoints.length && breakpoints[idx] < mousePosition) { idx++ } + + // console.log(mousePosition, breakpoints.map(Math.round), idx) + placeholderInfo = { parent: id, index: idx, } } + const throttledProcessEvent = Utils.throttle(processEvent, 250) + + const handleEvent = e => { + e.preventDefault() + throttledProcessEvent(e.clientX, e.clientY) + } // Callback when on top of a component const onDragOver = e => { From 6f73ecdc4cf2d4b5145e66c8a100f9800808a8dd Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Sat, 8 Oct 2022 15:18:05 +0100 Subject: [PATCH 14/69] Fix row/column detection, add comments and remove old code --- .../src/components/preview/DNDHandler.svelte | 112 +++--------------- 1 file changed, 17 insertions(+), 95 deletions(-) diff --git a/packages/client/src/components/preview/DNDHandler.svelte b/packages/client/src/components/preview/DNDHandler.svelte index 20bbc8bada..625c139e5e 100644 --- a/packages/client/src/components/preview/DNDHandler.svelte +++ b/packages/client/src/components/preview/DNDHandler.svelte @@ -76,35 +76,12 @@ }, 0) } - let lastX - let lastY - let lastTime - const processEvent = (mouseX, mouseY) => { if (!dropInfo) { return null } - let { id, parent, node, acceptsChildren, empty } = dropInfo - // Debounce by 10px difference - // if (lastX != null && lastY != null) { - // const delta = Math.abs(mouseY - lastY) + Math.abs(mouseX - lastX) - // if (delta < 10) { - // console.log("delta fail") - // return - // } - // } - // lastX = mouseX - // lastY = mouseY - - // Debounce by time - // if (Date.now() - (lastTime || 0) < 100) { - // console.log("time fail") - // return - // } - // lastTime = Date.now() - // If we're over something that does not accept children then we go up a // level and consider the mouse position relative to the parent if (!acceptsChildren) { @@ -114,7 +91,7 @@ } // We're now hovering over something which does accept children. - // If it is empty, just go inside it + // If it is empty, just go inside it. if (empty) { placeholderInfo = { parent: id, @@ -143,13 +120,15 @@ }) // Calculate the variance between each set of positions on the children - const variances = Object.keys(childCoords[0]).map(key => { - const coords = childCoords.map(x => x[key]) - return { - variance: variance(coords), - side: key, - } - }) + const variances = Object.keys(childCoords[0]) + .filter(x => x !== "placeholder") + .map(key => { + const coords = childCoords.map(x => x[key]) + return { + variance: variance(coords), + side: key, + } + }) // Sort by variance. The lowest variance position indicates whether we are // in a row or column layout @@ -157,76 +136,29 @@ return a.variance < b.variance ? -1 : 1 }) const column = ["centerX", "left", "right"].includes(variances[0].side) - console.log(column ? "COL" : "ROW") - /** SYMMETRICAL BREAKPOINTS **/ - // let breakpoints = [] - // for (let i = 0; i < childCoords.length - 1; i++) { - // const child1 = childCoords[i] - // const child2 = childCoords[i + 1] - // let breakpoint - // if (column) { - // const top = Math.min(child1.top, child2.top) - // const bottom = Math.max(child1.bottom, child2.bottom) - // breakpoint = (top + bottom) / 2 - // } else { - // const left = Math.min(child1.left, child2.left) - // const right = Math.max(child1.right, child2.right) - // breakpoint = (left + right) / 2 - // } - // breakpoints.push(breakpoint) - // } - - /** CENTER BREAKPOINTS **/ + // Calculate breakpoints between child components so we can determine the + // index to drop the component in. + // We want to ignore the placeholder from this calculation as it should not + // be considered a real child of the parent. let breakpoints = childCoords .filter(x => !x.placeholder) .map(x => { return column ? x.centerY : x.centerX }) - /** NEXT EDGE BREAKPOINTS **/ - // let breakpoints = [] - // for (let i = 0; i < childCoords.length; i++) { - // let breakpoint - // if (column) { - // if (mouseY > childCoords[i].top && mouseY < childCoords[i].bottom) { - // // Inside this container - // if (childCoords[i + 1]) { - // breakpoint = childCoords[i + 1].top - // } else { - // breakpoint = childCoords[i].top - // } - // } else { - // breakpoint = - // mouseY < childCoords[i].bottom - // ? childCoords[i].top - // : childCoords[i].bottom - // } - // } else { - // breakpoint = - // mouseX < childCoords[i].left - // ? childCoords[i].left - // : childCoords[i].right - // } - // breakpoints.push(breakpoint) - // } - // Determine the index to drop the component in const mousePosition = column ? mouseY : mouseX - let idx = 0 while (idx < breakpoints.length && breakpoints[idx] < mousePosition) { idx++ } - - // console.log(mousePosition, breakpoints.map(Math.round), idx) - placeholderInfo = { parent: id, index: idx, } } - const throttledProcessEvent = Utils.throttle(processEvent, 250) + const throttledProcessEvent = Utils.throttle(processEvent, 200) const handleEvent = e => { e.preventDefault() @@ -235,7 +167,6 @@ // Callback when on top of a component const onDragOver = e => { - // Skip if we aren't validly dragging currently if (!dragInfo || !dropInfo) { return } @@ -244,20 +175,12 @@ // Callback when entering a potential drop target const onDragEnter = e => { - // Skip if we aren't validly dragging currently - if (!dragInfo || !e.target.closest) { + if (!dragInfo) { return } - const component = e.target.closest(".component:not(.block)") + const component = e.target?.closest?.(".component:not(.block)") if (component && component.classList.contains("droppable")) { - // Do nothing if this is the same target - if (component.dataset.id === dropInfo?.target) { - return - } - - // Precompute and store some info to avoid recalculating everything in - // dragOver dropInfo = { id: component.dataset.id, parent: component.dataset.parent, @@ -265,7 +188,6 @@ empty: component.classList.contains("empty"), acceptsChildren: component.classList.contains("parent"), } - handleEvent(e) } } From cf0891c91118c035da96f7ace5fe20c8b761aaaf Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Sat, 8 Oct 2022 15:28:22 +0100 Subject: [PATCH 15/69] Rename DND state variables for clarity --- .../src/components/preview/DNDHandler.svelte | 42 ++++++++++--------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/packages/client/src/components/preview/DNDHandler.svelte b/packages/client/src/components/preview/DNDHandler.svelte index 625c139e5e..783578ad21 100644 --- a/packages/client/src/components/preview/DNDHandler.svelte +++ b/packages/client/src/components/preview/DNDHandler.svelte @@ -5,12 +5,14 @@ import PlaceholderOverlay from "./PlaceholderOverlay.svelte" import { Utils } from "@budibase/frontend-core" - let dragInfo + let sourceId + let targetInfo let dropInfo - let placeholderInfo - $: parent = placeholderInfo?.parent - $: index = placeholderInfo?.index + // These reactive statements are just a trick to only update the store when + // the value of one of the properties actually changes + $: parent = dropInfo?.parent + $: index = dropInfo?.index $: builderStore.actions.updateDNDPlaceholder(parent, index) // Util to get the inner DOM node by a component ID @@ -35,14 +37,14 @@ console.log("END") // Reset state - dragInfo = null + sourceId = null + targetInfo = null dropInfo = null - placeholderInfo = null builderStore.actions.setDragging(false) // Reset listener - if (dragInfo?.target) { - const component = document.getElementsByClassName(dragInfo.target)[0] + if (sourceId) { + const component = document.getElementsByClassName(sourceId)[0] if (component) { component.removeEventListener("dragend", stopDragging) } @@ -63,10 +65,8 @@ component.addEventListener("dragend", stopDragging) // Update state - dragInfo = { - target: component.dataset.id, - } - builderStore.actions.selectComponent(dragInfo.target) + sourceId = component.dataset.id + builderStore.actions.selectComponent(sourceId) builderStore.actions.setDragging(true) // Execute this asynchronously so we don't kill the drag event by hiding @@ -76,11 +76,13 @@ }, 0) } + // Core logic for handling drop events and determining where to render the + // drop target placeholder const processEvent = (mouseX, mouseY) => { - if (!dropInfo) { + if (!targetInfo) { return null } - let { id, parent, node, acceptsChildren, empty } = dropInfo + let { id, parent, node, acceptsChildren, empty } = targetInfo // If we're over something that does not accept children then we go up a // level and consider the mouse position relative to the parent @@ -93,7 +95,7 @@ // We're now hovering over something which does accept children. // If it is empty, just go inside it. if (empty) { - placeholderInfo = { + dropInfo = { parent: id, index: 0, } @@ -153,7 +155,7 @@ while (idx < breakpoints.length && breakpoints[idx] < mousePosition) { idx++ } - placeholderInfo = { + dropInfo = { parent: id, index: idx, } @@ -167,7 +169,7 @@ // Callback when on top of a component const onDragOver = e => { - if (!dragInfo || !dropInfo) { + if (!sourceId || !targetInfo) { return } handleEvent(e) @@ -175,13 +177,15 @@ // Callback when entering a potential drop target const onDragEnter = e => { - if (!dragInfo) { + if (!sourceId) { return } + // Find the next valid component to consider dropping over, ignoring nested + // block components const component = e.target?.closest?.(".component:not(.block)") if (component && component.classList.contains("droppable")) { - dropInfo = { + targetInfo = { id: component.dataset.id, parent: component.dataset.parent, node: getDOMNode(component.dataset.id), From 9f7504c53fa8e5fb0b2892d942f4143468caeffc Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Sat, 8 Oct 2022 15:32:36 +0100 Subject: [PATCH 16/69] Reduce padding when dragging over a component tree and remove unused component class names --- packages/client/src/components/Component.svelte | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/packages/client/src/components/Component.svelte b/packages/client/src/components/Component.svelte index e6dbbf8579..c3d6de5cae 100644 --- a/packages/client/src/components/Component.svelte +++ b/packages/client/src/components/Component.svelte @@ -459,9 +459,6 @@ class:block={isBlock} class:explode={interactive && hasChildren && inDndPath} class:placeholder={id === "placeholder"} - class:screen={isScreen} - class:dragging={$builderStore.selectedComponentId === id && - $builderStore.isDragging} data-id={id} data-name={name} data-icon={icon} @@ -495,7 +492,7 @@ transition: padding 250ms ease, border 250ms ease; } .component.explode :global(> *) { - padding: 32px !important; + padding: 16px !important; gap: 16px !important; border: 2px dashed var(--spectrum-global-color-gray-400) !important; border-radius: 4px !important; @@ -509,7 +506,4 @@ .editing :global(*:hover) { cursor: auto; } - .dragging { - pointer-events: none; - } From e1c55a2da72e2d3a0a059cc4f8affac0f48dc836 Mon Sep 17 00:00:00 2001 From: tomamplius Date: Sun, 9 Oct 2022 09:35:21 +0200 Subject: [PATCH 17/69] remove move on / --- scripts/cleanup.sh | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/scripts/cleanup.sh b/scripts/cleanup.sh index 94a3d7e2be..adfc3408dc 100644 --- a/scripts/cleanup.sh +++ b/scripts/cleanup.sh @@ -1,16 +1,4 @@ #!/bin/bash -dir=$(pwd) -declare -a keep=("dist" "package.json" "yarn.lock" "client" "builder" "build" "pm2.config.js" "docker_run.sh") -for moveDir in "${keep[@]}" -do - mv $moveDir / 2>/dev/null -done -cd / -rm -r $dir -mkdir $dir -for keepDir in "${keep[@]}" -do - mv /$keepDir $dir/ 2>/dev/null -done -cd $dir +KEEP="dist package.json yarn.lock client builder build pm2.config.js docker_run.sh" +ls | grep -v $(echo ${KEEP} | awk '{split($0,a," ");for (i in a) printf "-e ^"a[i]"$ "}') | xargs rm -fr NODE_ENV=production yarn From 6cf3a0af5b1ebcb88cb9e1ef77667b23b240d08b Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Mon, 10 Oct 2022 09:22:47 +0100 Subject: [PATCH 18/69] Tune DND throttle rate --- packages/client/src/components/preview/DNDHandler.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/src/components/preview/DNDHandler.svelte b/packages/client/src/components/preview/DNDHandler.svelte index 783578ad21..a4e737214f 100644 --- a/packages/client/src/components/preview/DNDHandler.svelte +++ b/packages/client/src/components/preview/DNDHandler.svelte @@ -160,7 +160,7 @@ index: idx, } } - const throttledProcessEvent = Utils.throttle(processEvent, 200) + const throttledProcessEvent = Utils.throttle(processEvent, 130) const handleEvent = e => { e.preventDefault() From c5b36863d2252b3930ad2546c2416b254aaf3d73 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Mon, 10 Oct 2022 09:36:17 +0100 Subject: [PATCH 19/69] Make DND work again by converting new parent+index params into old target+mode --- .../src/components/preview/DNDHandler.svelte | 43 +++++++++++++++---- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/packages/client/src/components/preview/DNDHandler.svelte b/packages/client/src/components/preview/DNDHandler.svelte index a4e737214f..04503f8020 100644 --- a/packages/client/src/components/preview/DNDHandler.svelte +++ b/packages/client/src/components/preview/DNDHandler.svelte @@ -1,9 +1,11 @@ + +{#if style} +
+
+
+{/if} + + diff --git a/packages/client/src/components/preview/PlaceholderOverlay.svelte b/packages/client/src/components/preview/DNDPlaceholderOverlay.svelte similarity index 88% rename from packages/client/src/components/preview/PlaceholderOverlay.svelte rename to packages/client/src/components/preview/DNDPlaceholderOverlay.svelte index 86950c533e..a762f35319 100644 --- a/packages/client/src/components/preview/PlaceholderOverlay.svelte +++ b/packages/client/src/components/preview/DNDPlaceholderOverlay.svelte @@ -1,11 +1,12 @@
@@ -206,6 +214,9 @@
{category.name}
{#each category.children as component}
onDragStart(component.component)} + on:dragend={onDragEnd} data-cy={`component-${component.name}`} class="component" class:selected={selectedIndex === diff --git a/packages/client/src/components/preview/DNDHandler.svelte b/packages/client/src/components/preview/DNDHandler.svelte index 40f52dd1ae..361f11dff2 100644 --- a/packages/client/src/components/preview/DNDHandler.svelte +++ b/packages/client/src/components/preview/DNDHandler.svelte @@ -2,21 +2,22 @@ import { onMount, onDestroy } from "svelte" import { get } from "svelte/store" import IndicatorSet from "./IndicatorSet.svelte" - import { builderStore, componentStore } from "stores" + import { + builderStore, + componentStore, + dndStore, + dndParent, + isDragging, + } from "stores" import DNDPlaceholderOverlay from "./DNDPlaceholderOverlay.svelte" import { Utils } from "@budibase/frontend-core" import { findComponentById } from "utils/components.js" - let sourceInfo - let targetInfo - let dropInfo - - // These reactive statements are just a trick to only update the store when - // the value of one of the properties actually changes - $: parent = dropInfo?.parent - $: index = dropInfo?.index - $: bounds = sourceInfo?.bounds - $: builderStore.actions.updateDNDPlaceholder(parent, index, bounds) + // Cache some dnd store state as local variables as it massively helps + // performance. It lets us avoid calling svelte getters on every DOM action. + $: source = $dndStore.source + $: target = $dndStore.target + $: drop = $dndStore.drop // Util to get the inner DOM node by a component ID const getDOMNode = id => { @@ -37,19 +38,16 @@ // Callback when drag stops (whether dropped or not) const stopDragging = () => { - // Reset state - sourceInfo = null - targetInfo = null - dropInfo = null - builderStore.actions.setDragging(false) - // Reset listener - if (sourceInfo) { - const component = document.getElementsByClassName(sourceInfo.id)[0] + if (source?.id) { + const component = document.getElementsByClassName(source?.id)[0] if (component) { component.removeEventListener("dragend", stopDragging) } } + + // Reset state + dndStore.actions.reset() } // Callback when initially starting a drag on a draggable component @@ -66,6 +64,7 @@ component.addEventListener("dragend", stopDragging) // Update state + const id = component.dataset.id const parentId = component.dataset.parent const parent = findComponentById( get(componentStore).currentAsset.props, @@ -74,14 +73,13 @@ const index = parent._children.findIndex( x => x._id === component.dataset.id ) - sourceInfo = { - id: component.dataset.id, + dndStore.actions.startDragging({ + id, bounds: component.children[0].getBoundingClientRect(), parent: parentId, index, - } - builderStore.actions.selectComponent(sourceInfo.id) - builderStore.actions.setDragging(true) + }) + builderStore.actions.selectComponent(id) // Set initial drop info to show placeholder exactly where the dragged // component is. @@ -89,20 +87,20 @@ // the same handler as selecting a new component (which causes a client // re-initialisation). setTimeout(() => { - dropInfo = { + dndStore.actions.updateDrop({ parent: parentId, index, - } + }) }, 0) } // Core logic for handling drop events and determining where to render the // drop target placeholder const processEvent = (mouseX, mouseY) => { - if (!targetInfo) { + if (!target) { return null } - let { id, parent, node, acceptsChildren, empty } = targetInfo + let { id, parent, node, acceptsChildren, empty } = target // If we're over something that does not accept children then we go up a // level and consider the mouse position relative to the parent @@ -115,10 +113,10 @@ // We're now hovering over something which does accept children. // If it is empty, just go inside it. if (empty) { - dropInfo = { + dndStore.actions.updateDrop({ parent: id, index: 0, - } + }) return } @@ -188,10 +186,10 @@ while (idx < breakpoints.length && breakpoints[idx] < mousePosition) { idx++ } - dropInfo = { + dndStore.actions.updateDrop({ parent: id, index: idx, - } + }) } const throttledProcessEvent = Utils.throttle(processEvent, 130) @@ -202,7 +200,7 @@ // Callback when on top of a component const onDragOver = e => { - if (!sourceInfo || !targetInfo) { + if (!source || !target) { return } handleEvent(e) @@ -210,69 +208,80 @@ // Callback when entering a potential drop target const onDragEnter = e => { - if (!sourceInfo) { + if (!source) { return } // Find the next valid component to consider dropping over, ignoring nested // block components const component = e.target?.closest?.( - `.component:not(.block):not(.${sourceInfo.id})` + `.component:not(.block):not(.${source.id})` ) if (component && component.classList.contains("droppable")) { - targetInfo = { + dndStore.actions.updateTarget({ id: component.dataset.id, parent: component.dataset.parent, node: getDOMNode(component.dataset.id), empty: component.classList.contains("empty"), acceptsChildren: component.classList.contains("parent"), - } + }) handleEvent(e) } } // Callback when dropping a drag on top of some component const onDrop = () => { - let target, mode - - // Convert parent + index into target + mode - if (sourceInfo && dropInfo?.parent && dropInfo.index != null) { - const parent = findComponentById( - get(componentStore).currentAsset?.props, - dropInfo.parent - ) - if (!parent) { - return - } - - // Do nothing if we didn't change the location - if ( - sourceInfo.parent === dropInfo.parent && - sourceInfo.index === dropInfo.index - ) { - return - } - - // Filter out source component and placeholder from consideration - const children = parent._children?.filter( - x => x._id !== "placeholder" && x._id !== sourceInfo.id - ) - - // Use inside if no existing children - if (!children?.length) { - target = parent._id - mode = "inside" - } else if (dropInfo.index === 0) { - target = children[0]?._id - mode = "above" - } else { - target = children[dropInfo.index - 1]?._id - mode = "below" - } + if (!source || !drop?.parent || drop?.index == null) { + return } - if (target && mode) { - builderStore.actions.moveComponent(sourceInfo.id, target, mode) + // Check if we're adding a new component rather than moving one + if (source.newComponentType) { + builderStore.actions.dropNewComponent( + source.newComponentType, + drop.parent, + drop.index + ) + } + + // Convert parent + index into target + mode + let legacyDropTarget, legacyDropMode + const parent = findComponentById( + get(componentStore).currentAsset?.props, + drop.parent + ) + if (!parent) { + return + } + + // Do nothing if we didn't change the location + if (source.parent === drop.parent && source.index === drop.index) { + return + } + + // Filter out source component and placeholder from consideration + const children = parent._children?.filter( + x => x._id !== "placeholder" && x._id !== source.id + ) + + // Use inside if no existing children + if (!children?.length) { + legacyDropTarget = parent._id + legacyDropMode = "inside" + } else if (drop.index === 0) { + legacyDropTarget = children[0]?._id + legacyDropMode = "above" + } else { + legacyDropTarget = children[drop.index - 1]?._id + legacyDropMode = "below" + } + + if (legacyDropTarget && legacyDropMode) { + builderStore.actions.moveComponent( + source.id, + legacyDropTarget, + legacyDropMode + ) } } @@ -298,13 +307,13 @@ -{#if $builderStore.isDragging} +{#if $isDragging} {/if} diff --git a/packages/client/src/components/preview/DNDPlaceholder.svelte b/packages/client/src/components/preview/DNDPlaceholder.svelte index 9efabdbc27..3725f9e06e 100644 --- a/packages/client/src/components/preview/DNDPlaceholder.svelte +++ b/packages/client/src/components/preview/DNDPlaceholder.svelte @@ -1,8 +1,8 @@ - import { builderStore } from "stores" + import { builderStore, isDragging } from "stores" import IndicatorSet from "./IndicatorSet.svelte" $: color = $builderStore.editMode @@ -8,7 +8,7 @@ { diff --git a/packages/client/src/index.js b/packages/client/src/index.js index 526ed1c10d..17f1c94359 100644 --- a/packages/client/src/index.js +++ b/packages/client/src/index.js @@ -6,6 +6,7 @@ import { blockStore, componentStore, environmentStore, + dndStore, } from "./stores" import loadSpectrumIcons from "@budibase/bbui/spectrum-icons-rollup.js" import { get } from "svelte/store" @@ -60,6 +61,24 @@ const loadBudibase = async () => { if (name === "eject-block") { const block = blockStore.actions.getBlock(payload) block?.eject() + } else if (name === "dragging-new-component") { + const { dragging, component } = payload + if (dragging) { + dndStore.actions.startDragging({ + id: null, + parent: null, + bounds: { + height: 64, + width: 64, + }, + index: null, + newComponentType: component, + }) + builderStore.actions.setDraggingNewComponent(true) + } else { + dndStore.actions.reset() + builderStore.actions.setDraggingNewComponent(false) + } } } diff --git a/packages/client/src/stores/builder.js b/packages/client/src/stores/builder.js index 0bb5693edb..135c08343e 100644 --- a/packages/client/src/stores/builder.js +++ b/packages/client/src/stores/builder.js @@ -16,13 +16,10 @@ const createBuilderStore = () => { theme: null, customTheme: null, previewDevice: "desktop", - isDragging: false, + draggingNewComponent: false, navigation: null, hiddenComponentIds: [], usedPlugins: null, - dndParent: null, - dndIndex: null, - dndBounds: null, // Legacy - allow the builder to specify a layout layout: null, @@ -70,11 +67,19 @@ const createBuilderStore = () => { mode, }) }, - setDragging: dragging => { - if (dragging === get(store).isDragging) { + dropNewComponent: (component, parent, index) => { + console.log("dispatch", component, parent, index) + dispatchEvent("drop-new-component", { + component, + parent, + index, + }) + }, + setDraggingNewComponent: draggingNewComponent => { + if (draggingNewComponent === get(store).draggingNewComponent) { return } - store.update(state => ({ ...state, isDragging: dragging })) + store.update(state => ({ ...state, draggingNewComponent })) }, setEditMode: enabled => { if (enabled === get(store).editMode) { @@ -111,14 +116,6 @@ const createBuilderStore = () => { // Notify the builder so we can reload component definitions dispatchEvent("reload-plugin") }, - updateDNDPlaceholder: (parent, index, bounds) => { - store.update(state => { - state.dndParent = parent - state.dndIndex = index - state.dndBounds = bounds - return state - }) - }, } return { ...store, diff --git a/packages/client/src/stores/components.js b/packages/client/src/stores/components.js index e1d6012150..71b7deb5a6 100644 --- a/packages/client/src/stores/components.js +++ b/packages/client/src/stores/components.js @@ -4,6 +4,7 @@ import { findComponentById, findComponentPathById } from "../utils/components" import { devToolsStore } from "./devTools" import { screenStore } from "./screens" import { builderStore } from "./builder" +import { dndParent } from "./dnd.js" import Router from "../components/Router.svelte" import DNDPlaceholder from "../components/preview/DNDPlaceholder.svelte" import * as AppComponents from "../components/app/index.js" @@ -19,8 +20,8 @@ const createComponentStore = () => { }) const derivedStore = derived( - [store, builderStore, devToolsStore, screenStore], - ([$store, $builderState, $devToolsState, $screenState]) => { + [store, builderStore, devToolsStore, screenStore, dndParent], + ([$store, $builderState, $devToolsState, $screenState, $dndParent]) => { // Avoid any of this logic if we aren't in the builder preview if (!$builderState.inBuilder && !$devToolsState.visible) { return {} @@ -40,8 +41,7 @@ const createComponentStore = () => { // Derive the selected component path const selectedPath = findComponentPathById(asset?.props, selectedComponentId) || [] - const dndPath = - findComponentPathById(asset?.props, $builderState.dndParent) || [] + const dndPath = findComponentPathById(asset?.props, $dndParent) || [] return { customComponentManifest: $store.customComponentManifest, diff --git a/packages/client/src/stores/derived/currentRole.js b/packages/client/src/stores/derived/currentRole.js new file mode 100644 index 0000000000..28287e1ea4 --- /dev/null +++ b/packages/client/src/stores/derived/currentRole.js @@ -0,0 +1,11 @@ +import { derived } from "svelte/store" +import { devToolsStore } from "../devTools.js" +import { authStore } from "../auth.js" + +// Derive the current role of the logged-in user +export const currentRole = derived( + [devToolsStore, authStore], + ([$devToolsStore, $authStore]) => { + return ($devToolsStore.enabled && $devToolsStore.role) || $authStore?.roleId + } +) diff --git a/packages/client/src/stores/derived/index.js b/packages/client/src/stores/derived/index.js new file mode 100644 index 0000000000..85df1aaafa --- /dev/null +++ b/packages/client/src/stores/derived/index.js @@ -0,0 +1,2 @@ +export { isDragging } from "./isDragging.js" +export { currentRole } from "./currentRole.js" diff --git a/packages/client/src/stores/derived/isDragging.js b/packages/client/src/stores/derived/isDragging.js new file mode 100644 index 0000000000..d3edd586c6 --- /dev/null +++ b/packages/client/src/stores/derived/isDragging.js @@ -0,0 +1,10 @@ +import { derived } from "svelte/store" +import { dndStore } from "../dnd" +import { builderStore } from "../builder.js" + +// Derive whether we are dragging or not +export const isDragging = derived( + [dndStore, builderStore], + ([$dndStore, $builderStore]) => + $dndStore.source != null || $builderStore.draggingNewComponent +) diff --git a/packages/client/src/stores/dnd.js b/packages/client/src/stores/dnd.js new file mode 100644 index 0000000000..805ec8c339 --- /dev/null +++ b/packages/client/src/stores/dnd.js @@ -0,0 +1,60 @@ +import { writable, derived } from "svelte/store" + +const createDndStore = () => { + const initialState = { + // Info about the dragged component + source: null, + + // Info about the target component being hovered over + target: null, + + // Info about where the component would be dropped + drop: null, + } + const store = writable(initialState) + + // newComponentType is an optional field to signify we are creating a new + // component rather than moving an existing one + const startDragging = ({ id, parent, bounds, index, newComponentType }) => { + store.set({ + ...initialState, + source: { id, parent, bounds, index, newComponentType }, + }) + } + + const updateTarget = ({ id, parent, node, empty, acceptsChildren }) => { + store.update(state => { + state.target = { id, parent, node, empty, acceptsChildren } + return state + }) + } + + const updateDrop = ({ parent, index }) => { + store.update(state => { + state.drop = { parent, index } + return state + }) + } + + const reset = () => { + store.set(initialState) + } + + return { + subscribe: store.subscribe, + actions: { + startDragging, + updateTarget, + updateDrop, + reset, + }, + } +} + +export const dndStore = createDndStore() +export const dndParent = derived(dndStore, $dndStore => $dndStore.drop?.parent) +export const dndIndex = derived(dndStore, $dndStore => $dndStore.drop?.index) +export const dndBounds = derived( + dndStore, + $dndStore => $dndStore.source?.bounds +) diff --git a/packages/client/src/stores/index.js b/packages/client/src/stores/index.js index 5b77762223..f86c0a0517 100644 --- a/packages/client/src/stores/index.js +++ b/packages/client/src/stores/index.js @@ -1,7 +1,3 @@ -import { derived } from "svelte/store" -import { devToolsStore } from "./devTools.js" -import { authStore } from "./auth.js" - export { authStore } from "./auth" export { appStore } from "./app" export { notificationStore } from "./notification" @@ -19,6 +15,7 @@ export { uploadStore } from "./uploads.js" export { rowSelectionStore } from "./rowSelection.js" export { blockStore } from "./blocks.js" export { environmentStore } from "./environment" +export { dndStore, dndIndex, dndParent, dndBounds } from "./dnd" // Context stores are layered and duplicated, so it is not a singleton export { createContextStore } from "./context" @@ -26,10 +23,5 @@ export { createContextStore } from "./context" // Initialises an app by loading screens and routes export { initialise } from "./initialise" -// Derive the current role of the logged-in user -export const currentRole = derived( - [devToolsStore, authStore], - ([$devToolsStore, $authStore]) => { - return ($devToolsStore.enabled && $devToolsStore.role) || $authStore?.roleId - } -) +// Derived state +export * from "./derived" diff --git a/packages/client/src/stores/screens.js b/packages/client/src/stores/screens.js index 5c630d2adf..b0bc06eee9 100644 --- a/packages/client/src/stores/screens.js +++ b/packages/client/src/stores/screens.js @@ -2,6 +2,7 @@ import { derived } from "svelte/store" import { routeStore } from "./routes" import { builderStore } from "./builder" import { appStore } from "./app" +import { dndIndex, dndParent } from "./dnd.js" import { RoleUtils } from "@budibase/frontend-core" import { findComponentById, findComponentParent } from "../utils/components.js" import { Helpers } from "@budibase/bbui" @@ -9,8 +10,8 @@ import { DNDPlaceholderID, DNDPlaceholderType } from "constants" const createScreenStore = () => { const store = derived( - [appStore, routeStore, builderStore], - ([$appStore, $routeStore, $builderStore]) => { + [appStore, routeStore, builderStore, dndParent, dndIndex], + ([$appStore, $routeStore, $builderStore, $dndParent, $dndIndex]) => { let activeLayout, activeScreen let screens @@ -46,31 +47,33 @@ const createScreenStore = () => { } // Insert DND placeholder if required - const { dndParent, dndIndex, selectedComponentId } = $builderStore - if (activeScreen && dndParent && dndIndex != null) { - // Remove selected component from tree - let selectedParent = findComponentParent( - activeScreen.props, - selectedComponentId - ) - selectedParent._children = selectedParent._children?.filter( - x => x._id !== selectedComponentId - ) + if (activeScreen && $dndParent && $dndIndex != null) { + // Remove selected component from tree if we are moving an existing + // component + const { selectedComponentId, draggingNewComponent } = $builderStore + if (!draggingNewComponent) { + let selectedParent = findComponentParent( + activeScreen.props, + selectedComponentId + ) + if (selectedParent) { + selectedParent._children = selectedParent._children?.filter( + x => x._id !== selectedComponentId + ) + } + } - // Insert placeholder + // Insert placeholder component const placeholder = { _component: DNDPlaceholderID, _id: DNDPlaceholderType, static: true, } - let parent = findComponentById(activeScreen.props, dndParent) + let parent = findComponentById(activeScreen.props, $dndParent) if (!parent._children?.length) { parent._children = [placeholder] } else { - parent._children = parent._children.filter( - x => x._id !== selectedComponentId - ) - parent._children.splice(dndIndex, 0, placeholder) + parent._children.splice($dndIndex, 0, placeholder) } } From 23eb09ab6aebba56dd983ac5a7bfec5144f02acf Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 14 Oct 2022 15:45:02 +0100 Subject: [PATCH 30/69] Don't reset component padded state for DND until dragging stops, to prevent jankiness due to losing space --- .../client/src/components/Component.svelte | 22 ++++++++++++++----- .../src/components/preview/DNDHandler.svelte | 4 +++- packages/client/src/index.js | 2 +- packages/client/src/stores/components.js | 1 + 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/packages/client/src/components/Component.svelte b/packages/client/src/components/Component.svelte index 4f82523ec0..3d65db7206 100644 --- a/packages/client/src/components/Component.svelte +++ b/packages/client/src/components/Component.svelte @@ -16,7 +16,13 @@ propsAreSame, getSettingsDefinition, } from "utils/componentProps" - import { builderStore, devToolsStore, componentStore, appStore } from "stores" + import { + builderStore, + devToolsStore, + componentStore, + appStore, + isDragging, + } from "stores" import { Helpers } from "@budibase/bbui" import { getActiveConditions, reduceConditionActions } from "utils/conditions" import Placeholder from "components/app/Placeholder.svelte" @@ -152,6 +158,12 @@ // Scroll the selected element into view $: selected && scrollIntoView() + // When dragging and dropping, pad components to allow dropping between + // nested layers. Only reset this when dragging stops. + let pad = false + $: pad = pad || (interactive && hasChildren && inDndPath) + $: $isDragging, (pad = false) + // Update component context $: store.set({ id, @@ -454,11 +466,11 @@ class:draggable class:droppable class:empty - class:parent={hasChildren} class:interactive class:editing + class:pad + class:parent={hasChildren} class:block={isBlock} - class:explode={interactive && hasChildren && inDndPath} class:placeholder={id === DNDPlaceholderID} data-id={id} data-name={name} @@ -490,9 +502,9 @@ display: contents; } .component :global(> *) { - transition: padding 260ms ease, border 260ms ease; + transition: padding 260ms ease-in, border 260ms ease-in; } - .component.explode :global(> *) { + .component.pad :global(> *) { padding: 12px !important; gap: 12px !important; border: 2px dotted var(--spectrum-global-color-gray-400) !important; diff --git a/packages/client/src/components/preview/DNDHandler.svelte b/packages/client/src/components/preview/DNDHandler.svelte index 361f11dff2..e2222300e0 100644 --- a/packages/client/src/components/preview/DNDHandler.svelte +++ b/packages/client/src/components/preview/DNDHandler.svelte @@ -13,6 +13,8 @@ import { Utils } from "@budibase/frontend-core" import { findComponentById } from "utils/components.js" + const ThrottleRate = 130 + // Cache some dnd store state as local variables as it massively helps // performance. It lets us avoid calling svelte getters on every DOM action. $: source = $dndStore.source @@ -191,7 +193,7 @@ index: idx, }) } - const throttledProcessEvent = Utils.throttle(processEvent, 130) + const throttledProcessEvent = Utils.throttle(processEvent, ThrottleRate) const handleEvent = e => { e.preventDefault() diff --git a/packages/client/src/index.js b/packages/client/src/index.js index 17f1c94359..383421cc35 100644 --- a/packages/client/src/index.js +++ b/packages/client/src/index.js @@ -69,7 +69,7 @@ const loadBudibase = async () => { parent: null, bounds: { height: 64, - width: 64, + width: 128, }, index: null, newComponentType: component, diff --git a/packages/client/src/stores/components.js b/packages/client/src/stores/components.js index 71b7deb5a6..e246299741 100644 --- a/packages/client/src/stores/components.js +++ b/packages/client/src/stores/components.js @@ -53,6 +53,7 @@ const createComponentStore = () => { mountedComponentCount: Object.keys($store.mountedComponents).length, currentAsset: asset, dndPath: dndPath?.map(component => component._id), + dndDepth: dndPath?.length || 0, } } ) From d02fb96e6eb4c446ea217fc02440de0752ac68a0 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 14 Oct 2022 18:16:19 +0100 Subject: [PATCH 31/69] Invert some client store dependencies to improve performance and prevent dependency cycles --- packages/client/manifest.json | 4 ++ .../client/src/components/Component.svelte | 11 +++-- .../src/components/preview/DNDHandler.svelte | 19 ++++---- .../components/preview/HoverIndicator.svelte | 4 +- .../preview/SelectionIndicator.svelte | 4 +- .../src/components/preview/SettingsBar.svelte | 4 +- packages/client/src/index.js | 15 ++----- packages/client/src/stores/builder.js | 8 ---- packages/client/src/stores/components.js | 30 +++++-------- .../src/stores/derived/dndComponentPath.js | 13 ++++++ packages/client/src/stores/derived/index.js | 5 ++- .../client/src/stores/derived/isDragging.js | 10 ----- packages/client/src/stores/dnd.js | 45 ++++++++++++++++--- packages/client/src/stores/index.js | 9 +++- packages/client/src/stores/screens.js | 24 +++++++--- 15 files changed, 122 insertions(+), 83 deletions(-) create mode 100644 packages/client/src/stores/derived/dndComponentPath.js delete mode 100644 packages/client/src/stores/derived/isDragging.js diff --git a/packages/client/manifest.json b/packages/client/manifest.json index 0edb5c0f39..14092b7b4d 100644 --- a/packages/client/manifest.json +++ b/packages/client/manifest.json @@ -85,6 +85,10 @@ "icon": "Selection", "hasChildren": true, "showSettingsBar": true, + "size": { + "width": 400, + "height": 200 + }, "styles": [ "padding", "size", diff --git a/packages/client/src/components/Component.svelte b/packages/client/src/components/Component.svelte index 3d65db7206..fe5f363152 100644 --- a/packages/client/src/components/Component.svelte +++ b/packages/client/src/components/Component.svelte @@ -21,14 +21,14 @@ devToolsStore, componentStore, appStore, - isDragging, + dndIsDragging, + dndComponentPath, } from "stores" import { Helpers } from "@budibase/bbui" import { getActiveConditions, reduceConditionActions } from "utils/conditions" import Placeholder from "components/app/Placeholder.svelte" import ScreenPlaceholder from "components/app/ScreenPlaceholder.svelte" import ComponentPlaceholder from "components/app/ComponentPlaceholder.svelte" - import { DNDPlaceholderID } from "constants" export let instance = {} export let isLayout = false @@ -105,7 +105,7 @@ $builderStore.inBuilder && $builderStore.selectedComponentId === id $: inSelectedPath = $componentStore.selectedComponentPath?.includes(id) $: inDragPath = inSelectedPath && $builderStore.editMode - $: inDndPath = $componentStore.dndPath?.includes(id) + $: inDndPath = $dndComponentPath?.includes(id) // Derive definition properties which can all be optional, so need to be // coerced to booleans @@ -162,7 +162,7 @@ // nested layers. Only reset this when dragging stops. let pad = false $: pad = pad || (interactive && hasChildren && inDndPath) - $: $isDragging, (pad = false) + $: $dndIsDragging, (pad = false) // Update component context $: store.set({ @@ -471,7 +471,6 @@ class:pad class:parent={hasChildren} class:block={isBlock} - class:placeholder={id === DNDPlaceholderID} data-id={id} data-name={name} data-icon={icon} @@ -502,7 +501,7 @@ display: contents; } .component :global(> *) { - transition: padding 260ms ease-in, border 260ms ease-in; + transition: padding 260ms ease-out, border 260ms ease-out; } .component.pad :global(> *) { padding: 12px !important; diff --git a/packages/client/src/components/preview/DNDHandler.svelte b/packages/client/src/components/preview/DNDHandler.svelte index e2222300e0..392cdceb36 100644 --- a/packages/client/src/components/preview/DNDHandler.svelte +++ b/packages/client/src/components/preview/DNDHandler.svelte @@ -4,14 +4,15 @@ import IndicatorSet from "./IndicatorSet.svelte" import { builderStore, - componentStore, + screenStore, dndStore, dndParent, - isDragging, + dndIsDragging, } from "stores" import DNDPlaceholderOverlay from "./DNDPlaceholderOverlay.svelte" import { Utils } from "@budibase/frontend-core" import { findComponentById } from "utils/components.js" + import { DNDPlaceholderID } from "constants" const ThrottleRate = 130 @@ -69,13 +70,13 @@ const id = component.dataset.id const parentId = component.dataset.parent const parent = findComponentById( - get(componentStore).currentAsset.props, + get(screenStore).activeScreen?.props, parentId ) const index = parent._children.findIndex( x => x._id === component.dataset.id ) - dndStore.actions.startDragging({ + dndStore.actions.startDraggingExistingComponent({ id, bounds: component.children[0].getBoundingClientRect(), parent: parentId, @@ -127,7 +128,7 @@ let ephemeralDiv if (node.children.length === 1) { ephemeralDiv = document.createElement("div") - ephemeralDiv.classList.add("placeholder") + ephemeralDiv.dataset.id = DNDPlaceholderID node.appendChild(ephemeralDiv) } @@ -138,7 +139,7 @@ const child = node.children?.[0] || node const bounds = child.getBoundingClientRect() return { - placeholder: node.classList.contains("placeholder"), + placeholder: node.dataset.id === DNDPlaceholderID, centerX: bounds.left + bounds.width / 2, centerY: bounds.top + bounds.height / 2, left: bounds.left, @@ -249,7 +250,7 @@ // Convert parent + index into target + mode let legacyDropTarget, legacyDropMode const parent = findComponentById( - get(componentStore).currentAsset?.props, + get(screenStore).activeScreen?.props, drop.parent ) if (!parent) { @@ -263,7 +264,7 @@ // Filter out source component and placeholder from consideration const children = parent._children?.filter( - x => x._id !== "placeholder" && x._id !== source.id + x => x._id !== DNDPlaceholderID && x._id !== source.id ) // Use inside if no existing children @@ -316,6 +317,6 @@ prefix="Inside" /> -{#if $isDragging} +{#if $dndIsDragging} {/if} diff --git a/packages/client/src/components/preview/HoverIndicator.svelte b/packages/client/src/components/preview/HoverIndicator.svelte index ed28938d4e..d5583ed3db 100644 --- a/packages/client/src/components/preview/HoverIndicator.svelte +++ b/packages/client/src/components/preview/HoverIndicator.svelte @@ -1,7 +1,7 @@ - import { builderStore, isDragging } from "stores" + import { builderStore, dndIsDragging } from "stores" import IndicatorSet from "./IndicatorSet.svelte" $: color = $builderStore.editMode @@ -8,7 +8,7 @@ { diff --git a/packages/client/src/index.js b/packages/client/src/index.js index 383421cc35..706cb4fc9f 100644 --- a/packages/client/src/index.js +++ b/packages/client/src/index.js @@ -64,20 +64,11 @@ const loadBudibase = async () => { } else if (name === "dragging-new-component") { const { dragging, component } = payload if (dragging) { - dndStore.actions.startDragging({ - id: null, - parent: null, - bounds: { - height: 64, - width: 128, - }, - index: null, - newComponentType: component, - }) - builderStore.actions.setDraggingNewComponent(true) + const definition = + componentStore.actions.getComponentDefinition(component) + dndStore.actions.startDraggingNewComponent({ component, definition }) } else { dndStore.actions.reset() - builderStore.actions.setDraggingNewComponent(false) } } } diff --git a/packages/client/src/stores/builder.js b/packages/client/src/stores/builder.js index 135c08343e..b937b7f696 100644 --- a/packages/client/src/stores/builder.js +++ b/packages/client/src/stores/builder.js @@ -16,7 +16,6 @@ const createBuilderStore = () => { theme: null, customTheme: null, previewDevice: "desktop", - draggingNewComponent: false, navigation: null, hiddenComponentIds: [], usedPlugins: null, @@ -68,19 +67,12 @@ const createBuilderStore = () => { }) }, dropNewComponent: (component, parent, index) => { - console.log("dispatch", component, parent, index) dispatchEvent("drop-new-component", { component, parent, index, }) }, - setDraggingNewComponent: draggingNewComponent => { - if (draggingNewComponent === get(store).draggingNewComponent) { - return - } - store.update(state => ({ ...state, draggingNewComponent })) - }, setEditMode: enabled => { if (enabled === get(store).editMode) { return diff --git a/packages/client/src/stores/components.js b/packages/client/src/stores/components.js index e246299741..b34dfe375d 100644 --- a/packages/client/src/stores/components.js +++ b/packages/client/src/stores/components.js @@ -4,7 +4,6 @@ import { findComponentById, findComponentPathById } from "../utils/components" import { devToolsStore } from "./devTools" import { screenStore } from "./screens" import { builderStore } from "./builder" -import { dndParent } from "./dnd.js" import Router from "../components/Router.svelte" import DNDPlaceholder from "../components/preview/DNDPlaceholder.svelte" import * as AppComponents from "../components/app/index.js" @@ -20,28 +19,22 @@ const createComponentStore = () => { }) const derivedStore = derived( - [store, builderStore, devToolsStore, screenStore, dndParent], - ([$store, $builderState, $devToolsState, $screenState, $dndParent]) => { + [store, builderStore, devToolsStore, screenStore], + ([$store, $builderStore, $devToolsStore, $screenStore]) => { + const { inBuilder, selectedComponentId } = $builderStore + // Avoid any of this logic if we aren't in the builder preview - if (!$builderState.inBuilder && !$devToolsState.visible) { + if (!inBuilder && !$devToolsStore.visible) { return {} } - // Derive the selected component instance and definition - let asset - const { screen, selectedComponentId } = $builderState - if ($builderState.inBuilder) { - asset = screen - } else { - asset = $screenState.activeScreen - } - const component = findComponentById(asset?.props, selectedComponentId) + const root = $screenStore.activeScreen?.props + const component = findComponentById(root, selectedComponentId) const definition = getComponentDefinition(component?._component) // Derive the selected component path const selectedPath = - findComponentPathById(asset?.props, selectedComponentId) || [] - const dndPath = findComponentPathById(asset?.props, $dndParent) || [] + findComponentPathById(root, selectedComponentId) || [] return { customComponentManifest: $store.customComponentManifest, @@ -51,9 +44,6 @@ const createComponentStore = () => { selectedComponentDefinition: definition, selectedComponentPath: selectedPath?.map(component => component._id), mountedComponentCount: Object.keys($store.mountedComponents).length, - currentAsset: asset, - dndPath: dndPath?.map(component => component._id), - dndDepth: dndPath?.length || 0, } } ) @@ -101,8 +91,8 @@ const createComponentStore = () => { } const getComponentById = id => { - const asset = get(derivedStore).currentAsset - return findComponentById(asset?.props, id) + const root = get(screenStore).activeScreen?.props + return findComponentById(root, id) } const getComponentDefinition = type => { diff --git a/packages/client/src/stores/derived/dndComponentPath.js b/packages/client/src/stores/derived/dndComponentPath.js new file mode 100644 index 0000000000..58fb395dd6 --- /dev/null +++ b/packages/client/src/stores/derived/dndComponentPath.js @@ -0,0 +1,13 @@ +import { derived } from "svelte/store" +import { findComponentPathById } from "utils/components.js" +import { dndParent } from "../dnd.js" +import { screenStore } from "../screens.js" + +export const dndComponentPath = derived( + [dndParent, screenStore], + ([$dndParent, $screenStore]) => { + const root = $screenStore.activeScreen?.props + const path = findComponentPathById(root, $dndParent) || [] + return path?.map(component => component._id) + } +) diff --git a/packages/client/src/stores/derived/index.js b/packages/client/src/stores/derived/index.js index 85df1aaafa..4f6a6ab91d 100644 --- a/packages/client/src/stores/derived/index.js +++ b/packages/client/src/stores/derived/index.js @@ -1,2 +1,5 @@ -export { isDragging } from "./isDragging.js" +// These derived stores are pulled out from their parent stores to avoid +// dependency loops. By inverting store dependencies and extracting them +// separately we can keep our actual stores lean and performant. export { currentRole } from "./currentRole.js" +export { dndComponentPath } from "./dndComponentPath.js" diff --git a/packages/client/src/stores/derived/isDragging.js b/packages/client/src/stores/derived/isDragging.js deleted file mode 100644 index d3edd586c6..0000000000 --- a/packages/client/src/stores/derived/isDragging.js +++ /dev/null @@ -1,10 +0,0 @@ -import { derived } from "svelte/store" -import { dndStore } from "../dnd" -import { builderStore } from "../builder.js" - -// Derive whether we are dragging or not -export const isDragging = derived( - [dndStore, builderStore], - ([$dndStore, $builderStore]) => - $dndStore.source != null || $builderStore.draggingNewComponent -) diff --git a/packages/client/src/stores/dnd.js b/packages/client/src/stores/dnd.js index 805ec8c339..1e7d7a6d50 100644 --- a/packages/client/src/stores/dnd.js +++ b/packages/client/src/stores/dnd.js @@ -2,6 +2,10 @@ import { writable, derived } from "svelte/store" const createDndStore = () => { const initialState = { + // Flags for whether we are inserting a new component or not + isNewComponent: false, + newComponentType: null, + // Info about the dragged component source: null, @@ -13,12 +17,32 @@ const createDndStore = () => { } const store = writable(initialState) - // newComponentType is an optional field to signify we are creating a new - // component rather than moving an existing one - const startDragging = ({ id, parent, bounds, index, newComponentType }) => { + const startDraggingExistingComponent = ({ id, parent, bounds, index }) => { store.set({ ...initialState, - source: { id, parent, bounds, index, newComponentType }, + source: { id, parent, bounds, index }, + }) + } + + const startDraggingNewComponent = ({ type, definition }) => { + if (!type || !definition) { + return + } + + // Get size of new component so we can show a properly sized placeholder + const width = definition.size?.width || 128 + const height = definition.size?.height || 64 + + store.set({ + ...initialState, + isNewComponent: true, + newComponentType: type, + source: { + id: null, + parent: null, + bounds: { height, width }, + index: null, + }, }) } @@ -43,7 +67,8 @@ const createDndStore = () => { return { subscribe: store.subscribe, actions: { - startDragging, + startDraggingExistingComponent, + startDraggingNewComponent, updateTarget, updateDrop, reset, @@ -52,9 +77,19 @@ const createDndStore = () => { } export const dndStore = createDndStore() + +// The DND store is updated extremely frequently, so we can greatly improve +// performance by deriving any state that needs to be externally observed. +// By doing this and using primitives, we can avoid invalidating other stores +// or components which depend on DND state unless values actually change. +export const dndIsDragging = derived(dndStore, $dndStore => !!$dndStore.source) export const dndParent = derived(dndStore, $dndStore => $dndStore.drop?.parent) export const dndIndex = derived(dndStore, $dndStore => $dndStore.drop?.index) export const dndBounds = derived( dndStore, $dndStore => $dndStore.source?.bounds ) +export const dndIsNewComponent = derived( + dndStore, + $dndStore => $dndStore.isNewComponent +) diff --git a/packages/client/src/stores/index.js b/packages/client/src/stores/index.js index f86c0a0517..c431302d43 100644 --- a/packages/client/src/stores/index.js +++ b/packages/client/src/stores/index.js @@ -15,7 +15,14 @@ export { uploadStore } from "./uploads.js" export { rowSelectionStore } from "./rowSelection.js" export { blockStore } from "./blocks.js" export { environmentStore } from "./environment" -export { dndStore, dndIndex, dndParent, dndBounds } from "./dnd" +export { + dndStore, + dndIndex, + dndParent, + dndBounds, + dndIsNewComponent, + dndIsDragging, +} from "./dnd" // Context stores are layered and duplicated, so it is not a singleton export { createContextStore } from "./context" diff --git a/packages/client/src/stores/screens.js b/packages/client/src/stores/screens.js index b0bc06eee9..0787610d80 100644 --- a/packages/client/src/stores/screens.js +++ b/packages/client/src/stores/screens.js @@ -2,7 +2,7 @@ import { derived } from "svelte/store" import { routeStore } from "./routes" import { builderStore } from "./builder" import { appStore } from "./app" -import { dndIndex, dndParent } from "./dnd.js" +import { dndIndex, dndParent, dndIsNewComponent } from "./dnd.js" import { RoleUtils } from "@budibase/frontend-core" import { findComponentById, findComponentParent } from "../utils/components.js" import { Helpers } from "@budibase/bbui" @@ -10,8 +10,22 @@ import { DNDPlaceholderID, DNDPlaceholderType } from "constants" const createScreenStore = () => { const store = derived( - [appStore, routeStore, builderStore, dndParent, dndIndex], - ([$appStore, $routeStore, $builderStore, $dndParent, $dndIndex]) => { + [ + appStore, + routeStore, + builderStore, + dndParent, + dndIndex, + dndIsNewComponent, + ], + ([ + $appStore, + $routeStore, + $builderStore, + $dndParent, + $dndIndex, + $dndIsNewComponent, + ]) => { let activeLayout, activeScreen let screens @@ -50,8 +64,8 @@ const createScreenStore = () => { if (activeScreen && $dndParent && $dndIndex != null) { // Remove selected component from tree if we are moving an existing // component - const { selectedComponentId, draggingNewComponent } = $builderStore - if (!draggingNewComponent) { + const { selectedComponentId } = $builderStore + if (!$dndIsNewComponent) { let selectedParent = findComponentParent( activeScreen.props, selectedComponentId From 121eddaa5a50e08bba8f6b5e073610699b165106 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 14 Oct 2022 18:17:02 +0100 Subject: [PATCH 32/69] Fix crash when dragging from client preview into component tree --- .../[componentId]/_components/navigation/ComponentTree.svelte | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/components/[componentId]/_components/navigation/ComponentTree.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/components/[componentId]/_components/navigation/ComponentTree.svelte index 427fd98775..5cb6d31345 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/components/[componentId]/_components/navigation/ComponentTree.svelte +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/components/[componentId]/_components/navigation/ComponentTree.svelte @@ -76,6 +76,9 @@ const compDef = store.actions.components.getDefinition( $dndStore.source?._component ) + if (!compDef) { + return + } const compTypeName = compDef.name.toLowerCase() const path = findComponentPath(currentScreen.props, component._id) From 78a5f9c891c6f7bcd7cde5015f91ac6486abcc19 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 14 Oct 2022 18:24:47 +0100 Subject: [PATCH 33/69] Fix issues dropping components from new component panel --- .../client/src/components/preview/DNDHandler.svelte | 1 + packages/client/src/stores/dnd.js | 13 ++++--------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/packages/client/src/components/preview/DNDHandler.svelte b/packages/client/src/components/preview/DNDHandler.svelte index 392cdceb36..61b34eefd3 100644 --- a/packages/client/src/components/preview/DNDHandler.svelte +++ b/packages/client/src/components/preview/DNDHandler.svelte @@ -245,6 +245,7 @@ drop.parent, drop.index ) + return } // Convert parent + index into target + mode diff --git a/packages/client/src/stores/dnd.js b/packages/client/src/stores/dnd.js index 1e7d7a6d50..cebdb7c6ae 100644 --- a/packages/client/src/stores/dnd.js +++ b/packages/client/src/stores/dnd.js @@ -2,10 +2,6 @@ import { writable, derived } from "svelte/store" const createDndStore = () => { const initialState = { - // Flags for whether we are inserting a new component or not - isNewComponent: false, - newComponentType: null, - // Info about the dragged component source: null, @@ -24,8 +20,8 @@ const createDndStore = () => { }) } - const startDraggingNewComponent = ({ type, definition }) => { - if (!type || !definition) { + const startDraggingNewComponent = ({ component, definition }) => { + if (!component || !definition) { return } @@ -35,13 +31,12 @@ const createDndStore = () => { store.set({ ...initialState, - isNewComponent: true, - newComponentType: type, source: { id: null, parent: null, bounds: { height, width }, index: null, + newComponentType: component, }, }) } @@ -91,5 +86,5 @@ export const dndBounds = derived( ) export const dndIsNewComponent = derived( dndStore, - $dndStore => $dndStore.isNewComponent + $dndStore => $dndStore.source?.newComponentType != null ) From 2d64ae59c68ad21d32d5cc007efdfd1921b0ff1c Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 14 Oct 2022 18:59:23 +0100 Subject: [PATCH 34/69] Add default size for every component type --- packages/client/manifest.json | 296 ++++++++++++++++++++++++---------- 1 file changed, 208 insertions(+), 88 deletions(-) diff --git a/packages/client/manifest.json b/packages/client/manifest.json index 14092b7b4d..763ac46b3f 100644 --- a/packages/client/manifest.json +++ b/packages/client/manifest.json @@ -87,7 +87,7 @@ "showSettingsBar": true, "size": { "width": 400, - "height": 200 + "height": 100 }, "styles": [ "padding", @@ -259,6 +259,10 @@ "section" ], "showEmptyState": false, + "size": { + "width": 400, + "height": 100 + }, "settings": [ { "type": "section", @@ -280,6 +284,10 @@ "icon": "Button", "editable": true, "showSettingsBar": true, + "size": { + "width": 105, + "height": 35 + }, "settings": [ { "type": "text", @@ -372,6 +380,10 @@ "illegalChildren": [ "section" ], + "size": { + "width": 400, + "height": 10 + }, "settings": [ { "type": "select", @@ -409,6 +421,10 @@ ], "hasChildren": true, "showSettingsBar": true, + "size": { + "width": 400, + "height": 100 + }, "settings": [ { "type": "dataProvider", @@ -588,6 +604,7 @@ ] }, "card": { + "deprecated": true, "name": "Vertical Card", "description": "A basic card component that can contain content and actions.", "icon": "ViewColumn", @@ -668,6 +685,10 @@ ], "showSettingsBar": true, "editable": true, + "size": { + "width": 400, + "height": 30 + }, "settings": [ { "type": "text", @@ -790,6 +811,10 @@ ], "showSettingsBar": true, "editable": true, + "size": { + "width": 400, + "height": 40 + }, "settings": [ { "type": "text", @@ -907,6 +932,10 @@ "name": "Tag", "icon": "Label", "showSettingsBar": true, + "size": { + "width": 100, + "height": 25 + }, "settings": [ { "type": "text", @@ -958,12 +987,13 @@ "name": "Image", "description": "A basic component for displaying images", "icon": "Image", - "illegalChildren": [ - "section" - ], "styles": [ "size" ], + "size": { + "width": 400, + "height": 300 + }, "settings": [ { "type": "text", @@ -980,9 +1010,10 @@ "styles": [ "size" ], - "illegalChildren": [ - "section" - ], + "size": { + "width": 400, + "height": 300 + }, "settings": [ { "type": "text", @@ -1040,9 +1071,10 @@ "name": "Icon", "description": "A basic component for displaying icons", "icon": "Shapes", - "illegalChildren": [ - "section" - ], + "size": { + "width": 25, + "height": 25 + }, "settings": [ { "type": "icon", @@ -1159,9 +1191,10 @@ "icon": "Link", "showSettingsBar": true, "editable": true, - "illegalChildren": [ - "section" - ], + "size": { + "width": 200, + "height": 30 + }, "settings": [ { "type": "text", @@ -1271,12 +1304,10 @@ ] }, "cardhorizontal": { + "deprecated": true, "name": "Horizontal Card", "description": "A basic card component that can contain content and actions.", "icon": "ViewRow", - "illegalChildren": [ - "section" - ], "settings": [ { "type": "text", @@ -1367,27 +1398,31 @@ "name": "Stat Card", "description": "A card component for displaying numbers.", "icon": "Card", - "illegalChildren": [ - "section" - ], + "size": { + "width": 260, + "height": 143 + }, "settings": [ { "type": "text", "label": "Title", "key": "title", - "placeholder": "Total Revenue" + "placeholder": "Total Revenue", + "defaultValue": "Title" }, { "type": "text", "label": "Value", "key": "value", - "placeholder": "$1,981,983" + "placeholder": "$1,981,983", + "defaultValue": "Value" }, { "type": "text", "label": "Label", "key": "label", - "placeholder": "Stripe" + "placeholder": "Stripe", + "defaultValue": "Label" } ] }, @@ -1395,12 +1430,13 @@ "name": "Embed", "icon": "Code", "description": "Embed content from 3rd party sources", - "illegalChildren": [ - "section" - ], "styles": [ "size" ], + "size": { + "width": 400, + "height": 100 + }, "settings": [ { "type": "text", @@ -1414,9 +1450,10 @@ "name": "Bar Chart", "description": "Bar chart", "icon": "GraphBarVertical", - "illegalChildren": [ - "section" - ], + "size": { + "width": 600, + "height": 400 + }, "settings": [ { "type": "text", @@ -1575,9 +1612,10 @@ "name": "Line Chart", "description": "Line chart", "icon": "GraphTrend", - "illegalChildren": [ - "section" - ], + "size": { + "width": 600, + "height": 400 + }, "settings": [ { "type": "text", @@ -1735,9 +1773,10 @@ "name": "Area Chart", "description": "Line chart", "icon": "GraphAreaStacked", - "illegalChildren": [ - "section" - ], + "size": { + "width": 600, + "height": 400 + }, "settings": [ { "type": "text", @@ -1907,9 +1946,10 @@ "name": "Pie Chart", "description": "Pie chart", "icon": "GraphPie", - "illegalChildren": [ - "section" - ], + "size": { + "width": 600, + "height": 400 + }, "settings": [ { "type": "text", @@ -2035,9 +2075,10 @@ "name": "Donut Chart", "description": "Donut chart", "icon": "GraphDonut", - "illegalChildren": [ - "section" - ], + "size": { + "width": 600, + "height": 400 + }, "settings": [ { "type": "text", @@ -2163,9 +2204,10 @@ "name": "Candlestick Chart", "description": "Candlestick chart", "icon": "GraphBarVerticalStacked", - "illegalChildren": [ - "section" - ], + "size": { + "width": 600, + "height": 400 + }, "settings": [ { "type": "text", @@ -2270,6 +2312,10 @@ "styles": [ "size" ], + "size": { + "width": 400, + "height": 400 + }, "settings": [ { "type": "select", @@ -2356,6 +2402,10 @@ "styles": [ "size" ], + "size": { + "width": 400, + "height": 400 + }, "settings": [ { "type": "number", @@ -2376,6 +2426,10 @@ "size" ], "hasChildren": true, + "size": { + "width": 400, + "height": 400 + }, "settings": [ { "type": "select", @@ -2402,13 +2456,14 @@ "stringfield": { "name": "Text Field", "icon": "Text", - "illegalChildren": [ - "section" - ], "styles": [ "size" ], "editable": true, + "size": { + "width": 400, + "height": 50 + }, "settings": [ { "type": "field/string", @@ -2496,9 +2551,10 @@ "size" ], "editable": true, - "illegalChildren": [ - "section" - ], + "size": { + "width": 400, + "height": 50 + }, "settings": [ { "type": "field/number", @@ -2552,9 +2608,10 @@ "size" ], "editable": true, - "illegalChildren": [ - "section" - ], + "size": { + "width": 400, + "height": 50 + }, "settings": [ { "type": "field/string", @@ -2608,9 +2665,10 @@ "size" ], "editable": true, - "illegalChildren": [ - "section" - ], + "size": { + "width": 400, + "height": 50 + }, "settings": [ { "type": "field/options", @@ -2775,9 +2833,10 @@ "size" ], "editable": true, - "illegalChildren": [ - "section" - ], + "size": { + "width": 400, + "height": 50 + }, "settings": [ { "type": "field/array", @@ -2933,9 +2992,10 @@ "name": "Checkbox", "icon": "SelectBox", "editable": true, - "illegalChildren": [ - "section" - ], + "size": { + "width": 400, + "height": 50 + }, "settings": [ { "type": "field/boolean", @@ -3013,6 +3073,10 @@ "size" ], "editable": true, + "size": { + "width": 400, + "height": 150 + }, "settings": [ { "type": "field/longform", @@ -3088,9 +3152,10 @@ "size" ], "editable": true, - "illegalChildren": [ - "section" - ], + "size": { + "width": 400, + "height": 50 + }, "settings": [ { "type": "field/datetime", @@ -3167,9 +3232,10 @@ "styles": [ "size" ], - "illegalChildren": [ - "section" - ], + "size": { + "width": 400, + "height": 50 + }, "settings": [ { "type": "field/barcode/qr", @@ -3218,29 +3284,27 @@ "size" ], "draggable": false, - "illegalChildren": [ - "section" - ], + "size": { + "width": 400, + "height": 320 + }, "settings": [ { "type": "dataProvider", "label": "Provider", - "key": "dataProvider", - "required": true + "key": "dataProvider" }, { "type": "field", "label": "Latitude Key", "key": "latitudeKey", - "dependsOn": "dataProvider", - "required": true + "dependsOn": "dataProvider" }, { "type": "field", "label": "Longitude Key", "key": "longitudeKey", - "dependsOn": "dataProvider", - "required": true + "dependsOn": "dataProvider" }, { "type": "field", @@ -3334,9 +3398,10 @@ "size" ], "editable": true, - "illegalChildren": [ - "section" - ], + "size": { + "width": 400, + "height": 200 + }, "settings": [ { "type": "field/attachment", @@ -3391,9 +3456,10 @@ "size" ], "editable": true, - "illegalChildren": [ - "section" - ], + "size": { + "width": 400, + "height": 50 + }, "settings": [ { "type": "field/link", @@ -3453,6 +3519,10 @@ "size" ], "editable": true, + "size": { + "width": 400, + "height": 100 + }, "settings": [ { "type": "field/json", @@ -3501,6 +3571,10 @@ "size" ], "editable": true, + "size": { + "width": 400, + "height": 200 + }, "settings": [ { "type": "field/attachment", @@ -3563,6 +3637,10 @@ "actions": [ "RefreshDatasource" ], + "size": { + "width": 400, + "height": 100 + }, "settings": [ { "type": "dataSource", @@ -3643,6 +3721,10 @@ ], "hasChildren": true, "showEmptyState": false, + "size": { + "width": 600, + "height": 400 + }, "settings": [ { "type": "dataProvider", @@ -3741,6 +3823,10 @@ "size" ], "hasChildren": false, + "size": { + "width": 200, + "height": 50 + }, "settings": [ { "type": "dataProvider", @@ -3777,21 +3863,28 @@ "styles": [ "size" ], + "size": { + "width": 300, + "height": 120 + }, "settings": [ { "type": "text", "key": "title", - "label": "Title" + "label": "Title", + "defaultValue": "Title" }, { "type": "text", "key": "subtitle", - "label": "Subtitle" + "label": "Subtitle", + "defaultValue": "Subtitle" }, { "type": "text", "key": "description", - "label": "Description" + "label": "Description", + "defaultValue": "Description" }, { "type": "text", @@ -3835,6 +3928,10 @@ "name": "Dynamic Filter", "icon": "Filter", "showSettingsBar": true, + "size": { + "width": 100, + "height": 35 + }, "settings": [ { "type": "dataProvider", @@ -3882,6 +3979,10 @@ "styles": [ "size" ], + "size": { + "width": 600, + "height": 400 + }, "settings": [ { "type": "text", @@ -4047,6 +4148,10 @@ "styles": [ "size" ], + "size": { + "width": 600, + "height": 400 + }, "settings": [ { "type": "text", @@ -4105,19 +4210,22 @@ "type": "text", "key": "cardTitle", "label": "Title", - "nested": true + "nested": true, + "defaultValue": "Title" }, { "type": "text", "key": "cardSubtitle", "label": "Subtitle", - "nested": true + "nested": true, + "defaultValue": "Subtitle" }, { "type": "text", "key": "cardDescription", "label": "Description", - "nested": true + "nested": true, + "defaultValue": "Description" }, { "type": "text", @@ -4219,6 +4327,10 @@ ], "hasChildren": true, "showSettingsBar": true, + "size": { + "width": 400, + "height": 100 + }, "settings": [ { "type": "dataSource", @@ -4441,6 +4553,10 @@ "styles": [ "size" ], + "size": { + "width": 400, + "height": 100 + }, "settings": [ { "type": "text", @@ -4458,6 +4574,10 @@ ], "block": true, "info": "Form blocks are only compatible with internal or SQL tables", + "size": { + "width": 400, + "height": 400 + }, "settings": [ { "type": "select", From bdae511371c54aad027907843a7ad0871144ecdb Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 14 Oct 2022 18:59:32 +0100 Subject: [PATCH 35/69] Fix double empty state around blocks --- packages/client/src/components/Component.svelte | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/client/src/components/Component.svelte b/packages/client/src/components/Component.svelte index fe5f363152..6d341b2eb8 100644 --- a/packages/client/src/components/Component.svelte +++ b/packages/client/src/components/Component.svelte @@ -135,8 +135,9 @@ // Empty states can be shown for these components, but can be disabled // in the component manifest. $: empty = - (interactive && !children.length && hasChildren) || - hasMissingRequiredSettings + !isBlock && + ((interactive && !children.length && hasChildren) || + hasMissingRequiredSettings) $: emptyState = empty && showEmptyState // Enrich component settings From 5f7e0cb8955f75d0ad27c22613d0e28ba14af66a Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 14 Oct 2022 19:04:05 +0100 Subject: [PATCH 36/69] Prevent showing placeholder dot when placeholder is invisible --- .../client/src/components/preview/DNDPlaceholderOverlay.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/src/components/preview/DNDPlaceholderOverlay.svelte b/packages/client/src/components/preview/DNDPlaceholderOverlay.svelte index a762f35319..12103e3904 100644 --- a/packages/client/src/components/preview/DNDPlaceholderOverlay.svelte +++ b/packages/client/src/components/preview/DNDPlaceholderOverlay.svelte @@ -25,7 +25,7 @@ }) -{#if left != null} +{#if left != null && top != null && width && height}
Date: Fri, 14 Oct 2022 19:13:44 +0100 Subject: [PATCH 37/69] Use block name in placeholders inside blocks --- packages/client/src/components/Block.svelte | 3 +++ packages/client/src/components/app/Placeholder.svelte | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/client/src/components/Block.svelte b/packages/client/src/components/Block.svelte index 05d92f208c..75474dfd6f 100644 --- a/packages/client/src/components/Block.svelte +++ b/packages/client/src/components/Block.svelte @@ -67,6 +67,9 @@ // any depth id: $component.id, + // Name can be used down the tree in placeholders + name: $component.name, + // We register block components with their raw props so that we can eject // blocks later on registerComponent: registerBlockComponent, diff --git a/packages/client/src/components/app/Placeholder.svelte b/packages/client/src/components/app/Placeholder.svelte index 203071e0b1..54553cb934 100644 --- a/packages/client/src/components/app/Placeholder.svelte +++ b/packages/client/src/components/app/Placeholder.svelte @@ -3,13 +3,14 @@ const { builderStore } = getContext("sdk") const component = getContext("component") + const block = getContext("block") export let text {#if $builderStore.inBuilder}
- {text || $component.name || "Placeholder"} + {text || block?.name || $component.name || "Placeholder"}
{/if} From 011ba2676945a618090ee7166098c01887416169 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 14 Oct 2022 19:34:23 +0100 Subject: [PATCH 38/69] Fix DND not working in field groups and any other component where children are not rendered inside the first DOM node --- packages/client/src/components/preview/DNDHandler.svelte | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/client/src/components/preview/DNDHandler.svelte b/packages/client/src/components/preview/DNDHandler.svelte index 61b34eefd3..a9c52f0099 100644 --- a/packages/client/src/components/preview/DNDHandler.svelte +++ b/packages/client/src/components/preview/DNDHandler.svelte @@ -123,6 +123,14 @@ return } + // As the first DOM node in a component may not necessarily contain the + // child components, we can find to try the parent of the first child + // component and use that as the real parent DOM node + const childNode = node.getElementsByClassName("component")[0] + if (childNode?.parentNode) { + node = childNode.parentNode + } + // Append an ephemeral div to allow us to determine layout if only one // child exists let ephemeralDiv From adc7e8e5fc1ca7f5a99d7f1bb210d2bc9f87b34c Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 14 Oct 2022 19:39:38 +0100 Subject: [PATCH 39/69] Fix unused plugins not being able to be dragged into the preview --- packages/client/src/stores/dnd.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/client/src/stores/dnd.js b/packages/client/src/stores/dnd.js index cebdb7c6ae..a1f85af92c 100644 --- a/packages/client/src/stores/dnd.js +++ b/packages/client/src/stores/dnd.js @@ -21,13 +21,13 @@ const createDndStore = () => { } const startDraggingNewComponent = ({ component, definition }) => { - if (!component || !definition) { + if (!component) { return } // Get size of new component so we can show a properly sized placeholder - const width = definition.size?.width || 128 - const height = definition.size?.height || 64 + const width = definition?.size?.width || 128 + const height = definition?.size?.height || 64 store.set({ ...initialState, From d166cbb466599afeeb48652b21ddec4a3cfc213f Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 14 Oct 2022 19:45:47 +0100 Subject: [PATCH 40/69] Lint and prevent scrolling to selected component when starting dragging --- packages/builder/src/builderStore/store/frontend.js | 2 +- packages/client/src/components/Component.svelte | 6 +++++- packages/frontend-core/src/utils/utils.js | 2 -- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/builder/src/builderStore/store/frontend.js b/packages/builder/src/builderStore/store/frontend.js index 4122954303..848dd4405a 100644 --- a/packages/builder/src/builderStore/store/frontend.js +++ b/packages/builder/src/builderStore/store/frontend.js @@ -1,6 +1,6 @@ import { get, writable } from "svelte/store" import { cloneDeep } from "lodash/fp" -import { selectedScreen, selectedComponent, store } from "builderStore" +import { selectedScreen, selectedComponent } from "builderStore" import { datasources, integrations, diff --git a/packages/client/src/components/Component.svelte b/packages/client/src/components/Component.svelte index 6d341b2eb8..5996b182eb 100644 --- a/packages/client/src/components/Component.svelte +++ b/packages/client/src/components/Component.svelte @@ -421,7 +421,11 @@ } const scrollIntoView = () => { - return + // Don't scroll into view if we selected this component because we were + // starting dragging on it + if (get(dndIsDragging)) { + return + } const node = document.getElementsByClassName(id)?.[0]?.children[0] if (!node) { return diff --git a/packages/frontend-core/src/utils/utils.js b/packages/frontend-core/src/utils/utils.js index ed76f13494..8aa49392fb 100644 --- a/packages/frontend-core/src/utils/utils.js +++ b/packages/frontend-core/src/utils/utils.js @@ -4,8 +4,6 @@ * @param fn the async function to run * @return {Promise} a sequential version of the function */ -import { lastIndexOf } from "lodash/array" - export const sequential = fn => { let queue = [] return async (...params) => { From 3325b7b62bbbe153b036815f2d459c58c7b7984d Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 14 Oct 2022 20:30:58 +0100 Subject: [PATCH 41/69] Update styles of padded DND components --- packages/client/src/components/Component.svelte | 6 +++--- packages/client/src/utils/styleable.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/client/src/components/Component.svelte b/packages/client/src/components/Component.svelte index 5996b182eb..c9872f246b 100644 --- a/packages/client/src/components/Component.svelte +++ b/packages/client/src/components/Component.svelte @@ -509,9 +509,9 @@ transition: padding 260ms ease-out, border 260ms ease-out; } .component.pad :global(> *) { - padding: 12px !important; - gap: 12px !important; - border: 2px dotted var(--spectrum-global-color-gray-400) !important; + padding: var(--spacing-l) !important; + gap: var(--spacing-l) !important; + border: 2px dashed var(--spectrum-global-color-gray-400) !important; border-radius: 4px !important; } .interactive :global(*) { diff --git a/packages/client/src/utils/styleable.js b/packages/client/src/utils/styleable.js index b07a3213d9..9ad17ceff0 100644 --- a/packages/client/src/utils/styleable.js +++ b/packages/client/src/utils/styleable.js @@ -27,7 +27,7 @@ export const styleable = (node, styles = {}) => { const setupStyles = (newStyles = {}) => { let baseStyles = {} if (newStyles.empty) { - baseStyles.border = "2px dashed var(--spectrum-global-color-gray-600)" + baseStyles.border = "2px dashed var(--spectrum-global-color-gray-400)" baseStyles.padding = "var(--spacing-l)" baseStyles.overflow = "hidden" } From 6656a89f1820db0c143bd667f6e810c394ca8499 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Mon, 17 Oct 2022 08:46:09 +0100 Subject: [PATCH 42/69] Add ability to drag new blocks into app preview --- .../[componentId]/new/_components/NewComponentPanel.svelte | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/components/[componentId]/new/_components/NewComponentPanel.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/components/[componentId]/new/_components/NewComponentPanel.svelte index 07781441c5..778a14ffff 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/components/[componentId]/new/_components/NewComponentPanel.svelte +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/components/[componentId]/new/_components/NewComponentPanel.svelte @@ -240,8 +240,11 @@ {#each blocks as block}
addComponent(block.component)} + on:dragstart={() => onDragStart(block.component)} + on:dragend={onDragEnd} > {block.name} From 554b219d6255a202b8951215fe7a3734865a789a Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Mon, 17 Oct 2022 08:48:32 +0100 Subject: [PATCH 43/69] Use requestAnimationFrame for DND overlay placeholder updates to further improve performance --- .../preview/DNDPlaceholderOverlay.svelte | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/packages/client/src/components/preview/DNDPlaceholderOverlay.svelte b/packages/client/src/components/preview/DNDPlaceholderOverlay.svelte index 12103e3904..6ed2df6a87 100644 --- a/packages/client/src/components/preview/DNDPlaceholderOverlay.svelte +++ b/packages/client/src/components/preview/DNDPlaceholderOverlay.svelte @@ -1,24 +1,27 @@
@@ -131,7 +138,7 @@
{#if open}
(open = false)} + use:clickOutside={handleOutsideClick} transition:fly={{ y: -20, duration: 200 }} class="spectrum-Popover spectrum-Popover--bottom spectrum-Picker-popover is-open" class:spectrum-Popover--align-right={alignRight} diff --git a/packages/bbui/src/Form/Core/Picker.svelte b/packages/bbui/src/Form/Core/Picker.svelte index d80ca98153..16d13ef2cc 100644 --- a/packages/bbui/src/Form/Core/Picker.svelte +++ b/packages/bbui/src/Form/Core/Picker.svelte @@ -85,7 +85,7 @@ class:is-invalid={!!error} class:is-open={open} aria-haspopup="listbox" - on:mousedown={onClick} + on:click={onClick} > {#if fieldIcon} diff --git a/packages/bbui/src/Form/Core/PickerDropdown.svelte b/packages/bbui/src/Form/Core/PickerDropdown.svelte index 1607876b46..604e446099 100644 --- a/packages/bbui/src/Form/Core/PickerDropdown.svelte +++ b/packages/bbui/src/Form/Core/PickerDropdown.svelte @@ -87,6 +87,20 @@ updateValue(event.target.value) } } + + const handlePrimaryOutsideClick = event => { + if (primaryOpen) { + event.stopPropagation() + primaryOpen = false + } + } + + const handleSecondaryOutsideClick = event => { + if (secondaryOpen) { + event.stopPropagation() + secondaryOpen = false + } + }
{#if primaryOpen}
(primaryOpen = false)} + use:clickOutside={handlePrimaryOutsideClick} transition:fly|local={{ y: -20, duration: 200 }} class="spectrum-Popover spectrum-Popover--bottom spectrum-Picker-popover is-open" class:auto-width={autoWidth} @@ -256,7 +270,7 @@ {disabled} class:is-open={secondaryOpen} aria-haspopup="listbox" - on:mousedown={onClickSecondary} + on:click={onClickSecondary} > {#if secondaryFieldIcon} @@ -281,7 +295,7 @@ {#if secondaryOpen}
(secondaryOpen = false)} + use:clickOutside={handleSecondaryOutsideClick} transition:fly|local={{ y: -20, duration: 200 }} class="spectrum-Popover spectrum-Popover--bottom spectrum-Picker-popover is-open" style="width: 30%" diff --git a/packages/bbui/src/IconPicker/IconPicker.svelte b/packages/bbui/src/IconPicker/IconPicker.svelte index 0e71be2c33..2b42da61b1 100644 --- a/packages/bbui/src/IconPicker/IconPicker.svelte +++ b/packages/bbui/src/IconPicker/IconPicker.svelte @@ -50,6 +50,13 @@ dispatch("change", value) open = false } + + const handleOutsideClick = event => { + if (open) { + event.stopPropagation() + open = false + } + }
@@ -64,7 +71,7 @@
{#if open}
(open = false)} + use:clickOutside={handleOutsideClick} transition:fly={{ y: -20, duration: 200 }} class="spectrum-Popover spectrum-Popover--bottom spectrum-Picker-popover is-open" class:spectrum-Popover--align-right={alignRight} From 49e103e8ef31362fe7b21cbd359c4a35d2644a1c Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 21 Oct 2022 18:52:39 +0100 Subject: [PATCH 56/69] Fixing test case. --- .../src/api/routes/tests/backup.spec.js | 2 +- .../server/src/sdk/app/backups/exports.ts | 10 +++- .../internal-api/TestConfiguration/screens.ts | 38 +++++++------- .../config/internal-api/fixtures/screens.ts | 50 +++++++++---------- .../internal-api/screens/screens.spec.ts | 33 ++++++++---- 5 files changed, 75 insertions(+), 58 deletions(-) diff --git a/packages/server/src/api/routes/tests/backup.spec.js b/packages/server/src/api/routes/tests/backup.spec.js index 7f92a4f3d1..7863129c75 100644 --- a/packages/server/src/api/routes/tests/backup.spec.js +++ b/packages/server/src/api/routes/tests/backup.spec.js @@ -21,7 +21,7 @@ describe("/backups", () => { .set(config.defaultHeaders()) .expect(200) expect(res.text).toBeDefined() - expect(res.text.includes(`"db_name":"${config.getAppId()}"`)).toEqual(true) + expect(res.headers["content-type"]).toEqual("application/gzip") expect(events.app.exported.mock.calls.length).toBe(1) }) diff --git a/packages/server/src/sdk/app/backups/exports.ts b/packages/server/src/sdk/app/backups/exports.ts index 26277fcd66..8de51ed1e6 100644 --- a/packages/server/src/sdk/app/backups/exports.ts +++ b/packages/server/src/sdk/app/backups/exports.ts @@ -1,7 +1,7 @@ import { db as dbCore } from "@budibase/backend-core" import { budibaseTempDir } from "../../../utilities/budibaseDir" import { retrieveDirectory } from "../../../utilities/fileSystem/utilities" -import { streamFile } from "../../../utilities/fileSystem" +import { streamFile, createTempFolder } from "../../../utilities/fileSystem" import { ObjectStoreBuckets } from "../../../constants" import { LINK_USER_METADATA_PREFIX, @@ -11,6 +11,7 @@ import { import { DB_EXPORT_FILE, GLOBAL_DB_EXPORT_FILE } from "./constants" import fs from "fs" import { join } from "path" +import env from "../../../environment" const uuid = require("uuid/v4") const tar = require("tar") const MemoryStream = require("memorystream") @@ -85,7 +86,12 @@ export async function exportApp(appId: string, config?: ExportOpts) { const prodAppId = dbCore.getProdAppID(appId) const appPath = `${prodAppId}/` // export bucket contents - const tmpPath = await retrieveDirectory(ObjectStoreBuckets.APPS, appPath) + let tmpPath + if (!env.isTest()) { + tmpPath = await retrieveDirectory(ObjectStoreBuckets.APPS, appPath) + } else { + tmpPath = createTempFolder(uuid()) + } const downloadedPath = join(tmpPath, appPath) if (fs.existsSync(downloadedPath)) { const allFiles = fs.readdirSync(downloadedPath) diff --git a/qa-core/src/config/internal-api/TestConfiguration/screens.ts b/qa-core/src/config/internal-api/TestConfiguration/screens.ts index 30c688022a..7814ffdfbc 100644 --- a/qa-core/src/config/internal-api/TestConfiguration/screens.ts +++ b/qa-core/src/config/internal-api/TestConfiguration/screens.ts @@ -1,23 +1,23 @@ -import { Screen } from "@budibase/types" +import { Screen } from "@budibase/types" import { Response } from "node-fetch" import InternalAPIClient from "./InternalAPIClient" - - export default class ScreenApi { - api: InternalAPIClient - - constructor(apiClient: InternalAPIClient) { - this.api = apiClient - } - - async create(body: any): Promise<[Response, Screen]> { - const response = await this.api.post(`/screens`, { body }) - const json = await response.json() - return [response, json] - } - async delete(screenId: string, rev: string): Promise<[Response, Screen]> { - const response = await this.api.del(`/screens/${screenId}/${rev}`) - const json = await response.json() - return [response, json] - } +export default class ScreenApi { + api: InternalAPIClient + + constructor(apiClient: InternalAPIClient) { + this.api = apiClient } + + async create(body: any): Promise<[Response, Screen]> { + const response = await this.api.post(`/screens`, { body }) + const json = await response.json() + return [response, json] + } + + async delete(screenId: string, rev: string): Promise<[Response, Screen]> { + const response = await this.api.del(`/screens/${screenId}/${rev}`) + const json = await response.json() + return [response, json] + } +} diff --git a/qa-core/src/config/internal-api/fixtures/screens.ts b/qa-core/src/config/internal-api/fixtures/screens.ts index 1ebc1eb5c8..044a35ba83 100644 --- a/qa-core/src/config/internal-api/fixtures/screens.ts +++ b/qa-core/src/config/internal-api/fixtures/screens.ts @@ -3,32 +3,32 @@ import generator from "../../generator" const randomId = generator.guid() const generateScreen = (roleId: string): any => ({ - showNavigation: true, - width: "Large", - name: randomId, - template: "createFromScratch", - props: { - _id: randomId, - _component: - "@budibase/standard-components/container", - _styles: { - normal: {}, - hover: {}, - active: {}, - selected: {} - }, - _children: [], - _instanceName: "New Screen", - direction: "column", - hAlign: "stretch", - vAlign: "top", - size: "grow", - gap: "M" - }, routing: { - route: "/test", - roleId: roleId, - homeScreen: false + showNavigation: true, + width: "Large", + name: randomId, + template: "createFromScratch", + props: { + _id: randomId, + _component: "@budibase/standard-components/container", + _styles: { + normal: {}, + hover: {}, + active: {}, + selected: {}, }, + _children: [], + _instanceName: "New Screen", + direction: "column", + hAlign: "stretch", + vAlign: "top", + size: "grow", + gap: "M", + }, + routing: { + route: "/test", + roleId: roleId, + homeScreen: false, + }, }) export default generateScreen diff --git a/qa-core/src/tests/internal-api/screens/screens.spec.ts b/qa-core/src/tests/internal-api/screens/screens.spec.ts index 68e1022cb4..1e0d647c3c 100644 --- a/qa-core/src/tests/internal-api/screens/screens.spec.ts +++ b/qa-core/src/tests/internal-api/screens/screens.spec.ts @@ -2,10 +2,9 @@ import TestConfiguration from "../../../config/internal-api/TestConfiguration" import { App } from "@budibase/types" import InternalAPIClient from "../../../config/internal-api/TestConfiguration/InternalAPIClient" import generateApp from "../../../config/internal-api/fixtures/applications" -import { Screen } from "@budibase/types" +import { Screen } from "@budibase/types" import generateScreen from "../../../config/internal-api/fixtures/screens" - describe("Internal API - /screens endpoints", () => { const api = new InternalAPIClient() const config = new TestConfiguration(api) @@ -21,13 +20,17 @@ describe("Internal API - /screens endpoints", () => { it("POST - Create a screen with each role type", async () => { // Create app - const [appResponse, app] = await appConfig.applications.create(generateApp()) - + const [appResponse, app] = await appConfig.applications.create( + generateApp() + ) + // Create Screen const roleArray = ["BASIC", "POWER", "ADMIN", "PUBLIC"] appConfig.applications.api.appId = app.appId for (let role in roleArray) { - const [response, screen] = await config.screen.create(generateScreen(roleArray[role])) + const [response, screen] = await config.screen.create( + generateScreen(roleArray[role]) + ) expect(response).toHaveStatusCode(200) expect(screen.routing.roleId).toEqual(roleArray[role]) } @@ -35,11 +38,15 @@ describe("Internal API - /screens endpoints", () => { it("GET - Fetch screens", async () => { // Create app - const [appResponse, app] = await appConfig.applications.create(generateApp()) - + const [appResponse, app] = await appConfig.applications.create( + generateApp() + ) + // Create Screen appConfig.applications.api.appId = app.appId - const [response, screen] = await config.screen.create(generateScreen("BASIC")) + const [response, screen] = await config.screen.create( + generateScreen("BASIC") + ) // Check screen exists const [routesResponse, routes] = await appConfig.applications.getRoutes() @@ -49,11 +56,15 @@ describe("Internal API - /screens endpoints", () => { it("DELETE - Delete a screen", async () => { // Create app - const [appResponse, app] = await appConfig.applications.create(generateApp()) - + const [appResponse, app] = await appConfig.applications.create( + generateApp() + ) + // Create Screen appConfig.applications.api.appId = app.appId - const [screenResponse, screen] = await config.screen.create(generateScreen("BASIC")) + const [screenResponse, screen] = await config.screen.create( + generateScreen("BASIC") + ) // Delete Screen const [response] = await config.screen.delete(screen._id!, screen._rev!) From 012a007116f3fc3c524a54b212c930d3dd33ff8b Mon Sep 17 00:00:00 2001 From: tomamplius Date: Fri, 21 Oct 2022 21:09:03 +0200 Subject: [PATCH 57/69] use egrep to replace grep + awk --- scripts/cleanup.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/cleanup.sh b/scripts/cleanup.sh index adfc3408dc..e13d7ff81c 100644 --- a/scripts/cleanup.sh +++ b/scripts/cleanup.sh @@ -1,4 +1,5 @@ #!/bin/bash -KEEP="dist package.json yarn.lock client builder build pm2.config.js docker_run.sh" -ls | grep -v $(echo ${KEEP} | awk '{split($0,a," ");for (i in a) printf "-e ^"a[i]"$ "}') | xargs rm -fr +KEEP="dist|package.json|yarn.lock|client|builder|build|pm2.config.js|docker_run.sh" +echo "Removing unneeded build files:" +ls | egrep -v $KEEP | xargs rm -rf NODE_ENV=production yarn From 1f29047958e8f69a06103a2a5b4e239b6dc19b0c Mon Sep 17 00:00:00 2001 From: tomamplius Date: Fri, 21 Oct 2022 21:09:54 +0200 Subject: [PATCH 58/69] add verbose to rm --- scripts/cleanup.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/cleanup.sh b/scripts/cleanup.sh index e13d7ff81c..aa4286c7e7 100644 --- a/scripts/cleanup.sh +++ b/scripts/cleanup.sh @@ -1,5 +1,5 @@ #!/bin/bash KEEP="dist|package.json|yarn.lock|client|builder|build|pm2.config.js|docker_run.sh" echo "Removing unneeded build files:" -ls | egrep -v $KEEP | xargs rm -rf +ls | egrep -v $KEEP | xargs rm -rfv NODE_ENV=production yarn From 6d7bb61bf461aa475d3e79c19b18cf1b41ac428a Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Fri, 21 Oct 2022 19:44:23 +0000 Subject: [PATCH 59/69] v2.0.30-alpha.11 --- lerna.json | 2 +- packages/backend-core/package.json | 4 ++-- packages/bbui/package.json | 4 ++-- packages/builder/package.json | 10 +++++----- packages/cli/package.json | 8 ++++---- packages/client/package.json | 8 ++++---- packages/frontend-core/package.json | 4 ++-- packages/sdk/package.json | 2 +- packages/server/package.json | 10 +++++----- packages/string-templates/package.json | 2 +- packages/types/package.json | 2 +- packages/worker/package.json | 8 ++++---- 12 files changed, 32 insertions(+), 32 deletions(-) diff --git a/lerna.json b/lerna.json index 0aadebb725..6de652bf42 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.0.30-alpha.10", + "version": "2.0.30-alpha.11", "npmClient": "yarn", "packages": [ "packages/*" diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json index 01dc0fdef4..482ab5ea6f 100644 --- a/packages/backend-core/package.json +++ b/packages/backend-core/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/backend-core", - "version": "2.0.30-alpha.10", + "version": "2.0.30-alpha.11", "description": "Budibase backend core libraries used in server and worker", "main": "dist/src/index.js", "types": "dist/src/index.d.ts", @@ -20,7 +20,7 @@ "test:watch": "jest --watchAll" }, "dependencies": { - "@budibase/types": "2.0.30-alpha.10", + "@budibase/types": "2.0.30-alpha.11", "@shopify/jest-koa-mocks": "5.0.1", "@techpass/passport-openidconnect": "0.3.2", "aws-sdk": "2.1030.0", diff --git a/packages/bbui/package.json b/packages/bbui/package.json index 91d5825bbf..c18d28779a 100644 --- a/packages/bbui/package.json +++ b/packages/bbui/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/bbui", "description": "A UI solution used in the different Budibase projects.", - "version": "2.0.30-alpha.10", + "version": "2.0.30-alpha.11", "license": "MPL-2.0", "svelte": "src/index.js", "module": "dist/bbui.es.js", @@ -38,7 +38,7 @@ ], "dependencies": { "@adobe/spectrum-css-workflow-icons": "^1.2.1", - "@budibase/string-templates": "2.0.30-alpha.10", + "@budibase/string-templates": "2.0.30-alpha.11", "@spectrum-css/actionbutton": "^1.0.1", "@spectrum-css/actiongroup": "^1.0.1", "@spectrum-css/avatar": "^3.0.2", diff --git a/packages/builder/package.json b/packages/builder/package.json index 246a97e843..b5a8f4cdc7 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/builder", - "version": "2.0.30-alpha.10", + "version": "2.0.30-alpha.11", "license": "GPL-3.0", "private": true, "scripts": { @@ -71,10 +71,10 @@ } }, "dependencies": { - "@budibase/bbui": "2.0.30-alpha.10", - "@budibase/client": "2.0.30-alpha.10", - "@budibase/frontend-core": "2.0.30-alpha.10", - "@budibase/string-templates": "2.0.30-alpha.10", + "@budibase/bbui": "2.0.30-alpha.11", + "@budibase/client": "2.0.30-alpha.11", + "@budibase/frontend-core": "2.0.30-alpha.11", + "@budibase/string-templates": "2.0.30-alpha.11", "@sentry/browser": "5.19.1", "@spectrum-css/page": "^3.0.1", "@spectrum-css/vars": "^3.0.1", diff --git a/packages/cli/package.json b/packages/cli/package.json index 91d0952b62..6103d4ac7d 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/cli", - "version": "2.0.30-alpha.10", + "version": "2.0.30-alpha.11", "description": "Budibase CLI, for developers, self hosting and migrations.", "main": "src/index.js", "bin": { @@ -26,9 +26,9 @@ "outputPath": "build" }, "dependencies": { - "@budibase/backend-core": "2.0.30-alpha.10", - "@budibase/string-templates": "2.0.30-alpha.10", - "@budibase/types": "2.0.30-alpha.10", + "@budibase/backend-core": "2.0.30-alpha.11", + "@budibase/string-templates": "2.0.30-alpha.11", + "@budibase/types": "2.0.30-alpha.11", "axios": "0.21.2", "chalk": "4.1.0", "cli-progress": "3.11.2", diff --git a/packages/client/package.json b/packages/client/package.json index 6dc56bf484..c2d9682a8b 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/client", - "version": "2.0.30-alpha.10", + "version": "2.0.30-alpha.11", "license": "MPL-2.0", "module": "dist/budibase-client.js", "main": "dist/budibase-client.js", @@ -19,9 +19,9 @@ "dev:builder": "rollup -cw" }, "dependencies": { - "@budibase/bbui": "2.0.30-alpha.10", - "@budibase/frontend-core": "2.0.30-alpha.10", - "@budibase/string-templates": "2.0.30-alpha.10", + "@budibase/bbui": "2.0.30-alpha.11", + "@budibase/frontend-core": "2.0.30-alpha.11", + "@budibase/string-templates": "2.0.30-alpha.11", "@spectrum-css/button": "^3.0.3", "@spectrum-css/card": "^3.0.3", "@spectrum-css/divider": "^1.0.3", diff --git a/packages/frontend-core/package.json b/packages/frontend-core/package.json index c80090ec31..ce00b391f1 100644 --- a/packages/frontend-core/package.json +++ b/packages/frontend-core/package.json @@ -1,12 +1,12 @@ { "name": "@budibase/frontend-core", - "version": "2.0.30-alpha.10", + "version": "2.0.30-alpha.11", "description": "Budibase frontend core libraries used in builder and client", "author": "Budibase", "license": "MPL-2.0", "svelte": "src/index.js", "dependencies": { - "@budibase/bbui": "2.0.30-alpha.10", + "@budibase/bbui": "2.0.30-alpha.11", "lodash": "^4.17.21", "svelte": "^3.46.2" } diff --git a/packages/sdk/package.json b/packages/sdk/package.json index a62c74c7af..31696601ea 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/sdk", - "version": "2.0.30-alpha.10", + "version": "2.0.30-alpha.11", "description": "Budibase Public API SDK", "author": "Budibase", "license": "MPL-2.0", diff --git a/packages/server/package.json b/packages/server/package.json index 9caed3dfa8..fd21344c49 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/server", "email": "hi@budibase.com", - "version": "2.0.30-alpha.10", + "version": "2.0.30-alpha.11", "description": "Budibase Web Server", "main": "src/index.ts", "repository": { @@ -77,11 +77,11 @@ "license": "GPL-3.0", "dependencies": { "@apidevtools/swagger-parser": "10.0.3", - "@budibase/backend-core": "2.0.30-alpha.10", - "@budibase/client": "2.0.30-alpha.10", + "@budibase/backend-core": "2.0.30-alpha.11", + "@budibase/client": "2.0.30-alpha.11", "@budibase/pro": "2.0.30-alpha.10", - "@budibase/string-templates": "2.0.30-alpha.10", - "@budibase/types": "2.0.30-alpha.10", + "@budibase/string-templates": "2.0.30-alpha.11", + "@budibase/types": "2.0.30-alpha.11", "@bull-board/api": "3.7.0", "@bull-board/koa": "3.9.4", "@elastic/elasticsearch": "7.10.0", diff --git a/packages/string-templates/package.json b/packages/string-templates/package.json index 78e5803316..adc2dfce14 100644 --- a/packages/string-templates/package.json +++ b/packages/string-templates/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/string-templates", - "version": "2.0.30-alpha.10", + "version": "2.0.30-alpha.11", "description": "Handlebars wrapper for Budibase templating.", "main": "src/index.cjs", "module": "dist/bundle.mjs", diff --git a/packages/types/package.json b/packages/types/package.json index 09c31556e0..064e026322 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/types", - "version": "2.0.30-alpha.10", + "version": "2.0.30-alpha.11", "description": "Budibase types", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/packages/worker/package.json b/packages/worker/package.json index 50fa1db46a..b548b81a7d 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/worker", "email": "hi@budibase.com", - "version": "2.0.30-alpha.10", + "version": "2.0.30-alpha.11", "description": "Budibase background service", "main": "src/index.ts", "repository": { @@ -36,10 +36,10 @@ "author": "Budibase", "license": "GPL-3.0", "dependencies": { - "@budibase/backend-core": "2.0.30-alpha.10", + "@budibase/backend-core": "2.0.30-alpha.11", "@budibase/pro": "2.0.30-alpha.10", - "@budibase/string-templates": "2.0.30-alpha.10", - "@budibase/types": "2.0.30-alpha.10", + "@budibase/string-templates": "2.0.30-alpha.11", + "@budibase/types": "2.0.30-alpha.11", "@koa/router": "8.0.8", "@sentry/node": "6.17.7", "@techpass/passport-openidconnect": "0.3.2", From f4d9b16f2d5b4b6de3c7cd3912e56f83a2160c35 Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Fri, 21 Oct 2022 19:47:38 +0000 Subject: [PATCH 60/69] Update pro version to 2.0.30-alpha.11 --- packages/server/package.json | 2 +- packages/server/yarn.lock | 30 +++++++++++++++--------------- packages/worker/package.json | 2 +- packages/worker/yarn.lock | 30 +++++++++++++++--------------- 4 files changed, 32 insertions(+), 32 deletions(-) diff --git a/packages/server/package.json b/packages/server/package.json index fd21344c49..91db403231 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -79,7 +79,7 @@ "@apidevtools/swagger-parser": "10.0.3", "@budibase/backend-core": "2.0.30-alpha.11", "@budibase/client": "2.0.30-alpha.11", - "@budibase/pro": "2.0.30-alpha.10", + "@budibase/pro": "2.0.30-alpha.11", "@budibase/string-templates": "2.0.30-alpha.11", "@budibase/types": "2.0.30-alpha.11", "@bull-board/api": "3.7.0", diff --git a/packages/server/yarn.lock b/packages/server/yarn.lock index 3e89dab309..c9dad52e98 100644 --- a/packages/server/yarn.lock +++ b/packages/server/yarn.lock @@ -1094,12 +1094,12 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@budibase/backend-core@2.0.30-alpha.10": - version "2.0.30-alpha.10" - resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.0.30-alpha.10.tgz#ff11327ad0456d1af40839f3a89002487d0e3173" - integrity sha512-KGX0k5h1IBE5hNmPBRC58hk0XfXzeBmFcXzveA8YAXboeYo0Rzo2xQr5oSbT9766JaNaHTwF9J1luyxq0Zr7KA== +"@budibase/backend-core@2.0.30-alpha.11": + version "2.0.30-alpha.11" + resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.0.30-alpha.11.tgz#84dc986537b2fdab6b01b1747a2805571f066149" + integrity sha512-njN81Ml+cDcYgOemhFX7bhAjBHUfxJAyjQwmt5qMjABAww4JM5hqPqW55kzC0++tYAIPh2gnIqZYfHpGAcDf9g== dependencies: - "@budibase/types" "2.0.30-alpha.10" + "@budibase/types" "2.0.30-alpha.11" "@shopify/jest-koa-mocks" "5.0.1" "@techpass/passport-openidconnect" "0.3.2" aws-sdk "2.1030.0" @@ -1180,13 +1180,13 @@ svelte-flatpickr "^3.2.3" svelte-portal "^1.0.0" -"@budibase/pro@2.0.30-alpha.10": - version "2.0.30-alpha.10" - resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.0.30-alpha.10.tgz#2bd878840f705a1d344da5fac1d588a06e5eefed" - integrity sha512-cXnbYsrx9Y9t4XAnzoFnNXJaJfd+vnIFaX5ULnJYTPsmouDij/A/qU8lNDtpxciss4dgxO9AnZH31ZXFVlJj0Q== +"@budibase/pro@2.0.30-alpha.11": + version "2.0.30-alpha.11" + resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.0.30-alpha.11.tgz#e3a9c33f254cda3e850ba1f47fe948bd7c85bc22" + integrity sha512-+OEHVObJTzM15YRDAjjavxsNDt09QRn8Fs7mQL/IdqZUg8Vylu4JoSvoMTzg4QTC8SuR9VEjBgDosoZf6f37xQ== dependencies: - "@budibase/backend-core" "2.0.30-alpha.10" - "@budibase/types" "2.0.30-alpha.10" + "@budibase/backend-core" "2.0.30-alpha.11" + "@budibase/types" "2.0.30-alpha.11" "@koa/router" "8.0.8" joi "17.6.0" node-fetch "^2.6.1" @@ -1209,10 +1209,10 @@ svelte-apexcharts "^1.0.2" svelte-flatpickr "^3.1.0" -"@budibase/types@2.0.30-alpha.10": - version "2.0.30-alpha.10" - resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.0.30-alpha.10.tgz#9b0ecc9d68ad5694a10f4390dd0ca64b93127e5b" - integrity sha512-qaXoUdHWhvym085dHt0WB+U7Lap1mqwh2zRo+2e+kfvEqzOyvj2H7ILK3wjVGYlrFrr7qceQaMnDCAEzad6aMg== +"@budibase/types@2.0.30-alpha.11": + version "2.0.30-alpha.11" + resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.0.30-alpha.11.tgz#0fe7f42656d8293f5b3080ba323064d395ebdc9f" + integrity sha512-bLSPmewXtXH5aPo8bgXKr5hwWNzaQJKokkfsRmS5dTE+/XbH+AJU7uLmscLrhw1XlT9qZMcXS26Q/EULsp+Ywg== "@bull-board/api@3.7.0": version "3.7.0" diff --git a/packages/worker/package.json b/packages/worker/package.json index b548b81a7d..684225432f 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -37,7 +37,7 @@ "license": "GPL-3.0", "dependencies": { "@budibase/backend-core": "2.0.30-alpha.11", - "@budibase/pro": "2.0.30-alpha.10", + "@budibase/pro": "2.0.30-alpha.11", "@budibase/string-templates": "2.0.30-alpha.11", "@budibase/types": "2.0.30-alpha.11", "@koa/router": "8.0.8", diff --git a/packages/worker/yarn.lock b/packages/worker/yarn.lock index 8f7503d6ac..3ffd9a9bce 100644 --- a/packages/worker/yarn.lock +++ b/packages/worker/yarn.lock @@ -291,12 +291,12 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@budibase/backend-core@2.0.30-alpha.10": - version "2.0.30-alpha.10" - resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.0.30-alpha.10.tgz#ff11327ad0456d1af40839f3a89002487d0e3173" - integrity sha512-KGX0k5h1IBE5hNmPBRC58hk0XfXzeBmFcXzveA8YAXboeYo0Rzo2xQr5oSbT9766JaNaHTwF9J1luyxq0Zr7KA== +"@budibase/backend-core@2.0.30-alpha.11": + version "2.0.30-alpha.11" + resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.0.30-alpha.11.tgz#84dc986537b2fdab6b01b1747a2805571f066149" + integrity sha512-njN81Ml+cDcYgOemhFX7bhAjBHUfxJAyjQwmt5qMjABAww4JM5hqPqW55kzC0++tYAIPh2gnIqZYfHpGAcDf9g== dependencies: - "@budibase/types" "2.0.30-alpha.10" + "@budibase/types" "2.0.30-alpha.11" "@shopify/jest-koa-mocks" "5.0.1" "@techpass/passport-openidconnect" "0.3.2" aws-sdk "2.1030.0" @@ -327,21 +327,21 @@ uuid "8.3.2" zlib "1.0.5" -"@budibase/pro@2.0.30-alpha.10": - version "2.0.30-alpha.10" - resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.0.30-alpha.10.tgz#2bd878840f705a1d344da5fac1d588a06e5eefed" - integrity sha512-cXnbYsrx9Y9t4XAnzoFnNXJaJfd+vnIFaX5ULnJYTPsmouDij/A/qU8lNDtpxciss4dgxO9AnZH31ZXFVlJj0Q== +"@budibase/pro@2.0.30-alpha.11": + version "2.0.30-alpha.11" + resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.0.30-alpha.11.tgz#e3a9c33f254cda3e850ba1f47fe948bd7c85bc22" + integrity sha512-+OEHVObJTzM15YRDAjjavxsNDt09QRn8Fs7mQL/IdqZUg8Vylu4JoSvoMTzg4QTC8SuR9VEjBgDosoZf6f37xQ== dependencies: - "@budibase/backend-core" "2.0.30-alpha.10" - "@budibase/types" "2.0.30-alpha.10" + "@budibase/backend-core" "2.0.30-alpha.11" + "@budibase/types" "2.0.30-alpha.11" "@koa/router" "8.0.8" joi "17.6.0" node-fetch "^2.6.1" -"@budibase/types@2.0.30-alpha.10": - version "2.0.30-alpha.10" - resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.0.30-alpha.10.tgz#9b0ecc9d68ad5694a10f4390dd0ca64b93127e5b" - integrity sha512-qaXoUdHWhvym085dHt0WB+U7Lap1mqwh2zRo+2e+kfvEqzOyvj2H7ILK3wjVGYlrFrr7qceQaMnDCAEzad6aMg== +"@budibase/types@2.0.30-alpha.11": + version "2.0.30-alpha.11" + resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.0.30-alpha.11.tgz#0fe7f42656d8293f5b3080ba323064d395ebdc9f" + integrity sha512-bLSPmewXtXH5aPo8bgXKr5hwWNzaQJKokkfsRmS5dTE+/XbH+AJU7uLmscLrhw1XlT9qZMcXS26Q/EULsp+Ywg== "@cspotcode/source-map-consumer@0.8.0": version "0.8.0" From 1e1d9ef0b8d2118a6bd5a905c2e146d905a8c8ad Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Mon, 24 Oct 2022 08:01:46 +0000 Subject: [PATCH 61/69] v2.0.30-alpha.12 --- lerna.json | 2 +- packages/backend-core/package.json | 4 ++-- packages/bbui/package.json | 4 ++-- packages/builder/package.json | 10 +++++----- packages/cli/package.json | 8 ++++---- packages/client/package.json | 8 ++++---- packages/frontend-core/package.json | 4 ++-- packages/sdk/package.json | 2 +- packages/server/package.json | 10 +++++----- packages/string-templates/package.json | 2 +- packages/types/package.json | 2 +- packages/worker/package.json | 8 ++++---- 12 files changed, 32 insertions(+), 32 deletions(-) diff --git a/lerna.json b/lerna.json index 6de652bf42..5600db4cf3 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.0.30-alpha.11", + "version": "2.0.30-alpha.12", "npmClient": "yarn", "packages": [ "packages/*" diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json index 482ab5ea6f..2e358f07b5 100644 --- a/packages/backend-core/package.json +++ b/packages/backend-core/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/backend-core", - "version": "2.0.30-alpha.11", + "version": "2.0.30-alpha.12", "description": "Budibase backend core libraries used in server and worker", "main": "dist/src/index.js", "types": "dist/src/index.d.ts", @@ -20,7 +20,7 @@ "test:watch": "jest --watchAll" }, "dependencies": { - "@budibase/types": "2.0.30-alpha.11", + "@budibase/types": "2.0.30-alpha.12", "@shopify/jest-koa-mocks": "5.0.1", "@techpass/passport-openidconnect": "0.3.2", "aws-sdk": "2.1030.0", diff --git a/packages/bbui/package.json b/packages/bbui/package.json index c18d28779a..65da2c2cea 100644 --- a/packages/bbui/package.json +++ b/packages/bbui/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/bbui", "description": "A UI solution used in the different Budibase projects.", - "version": "2.0.30-alpha.11", + "version": "2.0.30-alpha.12", "license": "MPL-2.0", "svelte": "src/index.js", "module": "dist/bbui.es.js", @@ -38,7 +38,7 @@ ], "dependencies": { "@adobe/spectrum-css-workflow-icons": "^1.2.1", - "@budibase/string-templates": "2.0.30-alpha.11", + "@budibase/string-templates": "2.0.30-alpha.12", "@spectrum-css/actionbutton": "^1.0.1", "@spectrum-css/actiongroup": "^1.0.1", "@spectrum-css/avatar": "^3.0.2", diff --git a/packages/builder/package.json b/packages/builder/package.json index b5a8f4cdc7..e99c7ac995 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/builder", - "version": "2.0.30-alpha.11", + "version": "2.0.30-alpha.12", "license": "GPL-3.0", "private": true, "scripts": { @@ -71,10 +71,10 @@ } }, "dependencies": { - "@budibase/bbui": "2.0.30-alpha.11", - "@budibase/client": "2.0.30-alpha.11", - "@budibase/frontend-core": "2.0.30-alpha.11", - "@budibase/string-templates": "2.0.30-alpha.11", + "@budibase/bbui": "2.0.30-alpha.12", + "@budibase/client": "2.0.30-alpha.12", + "@budibase/frontend-core": "2.0.30-alpha.12", + "@budibase/string-templates": "2.0.30-alpha.12", "@sentry/browser": "5.19.1", "@spectrum-css/page": "^3.0.1", "@spectrum-css/vars": "^3.0.1", diff --git a/packages/cli/package.json b/packages/cli/package.json index 6103d4ac7d..7f607508bc 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/cli", - "version": "2.0.30-alpha.11", + "version": "2.0.30-alpha.12", "description": "Budibase CLI, for developers, self hosting and migrations.", "main": "src/index.js", "bin": { @@ -26,9 +26,9 @@ "outputPath": "build" }, "dependencies": { - "@budibase/backend-core": "2.0.30-alpha.11", - "@budibase/string-templates": "2.0.30-alpha.11", - "@budibase/types": "2.0.30-alpha.11", + "@budibase/backend-core": "2.0.30-alpha.12", + "@budibase/string-templates": "2.0.30-alpha.12", + "@budibase/types": "2.0.30-alpha.12", "axios": "0.21.2", "chalk": "4.1.0", "cli-progress": "3.11.2", diff --git a/packages/client/package.json b/packages/client/package.json index c2d9682a8b..d0a049feee 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/client", - "version": "2.0.30-alpha.11", + "version": "2.0.30-alpha.12", "license": "MPL-2.0", "module": "dist/budibase-client.js", "main": "dist/budibase-client.js", @@ -19,9 +19,9 @@ "dev:builder": "rollup -cw" }, "dependencies": { - "@budibase/bbui": "2.0.30-alpha.11", - "@budibase/frontend-core": "2.0.30-alpha.11", - "@budibase/string-templates": "2.0.30-alpha.11", + "@budibase/bbui": "2.0.30-alpha.12", + "@budibase/frontend-core": "2.0.30-alpha.12", + "@budibase/string-templates": "2.0.30-alpha.12", "@spectrum-css/button": "^3.0.3", "@spectrum-css/card": "^3.0.3", "@spectrum-css/divider": "^1.0.3", diff --git a/packages/frontend-core/package.json b/packages/frontend-core/package.json index ce00b391f1..880c804beb 100644 --- a/packages/frontend-core/package.json +++ b/packages/frontend-core/package.json @@ -1,12 +1,12 @@ { "name": "@budibase/frontend-core", - "version": "2.0.30-alpha.11", + "version": "2.0.30-alpha.12", "description": "Budibase frontend core libraries used in builder and client", "author": "Budibase", "license": "MPL-2.0", "svelte": "src/index.js", "dependencies": { - "@budibase/bbui": "2.0.30-alpha.11", + "@budibase/bbui": "2.0.30-alpha.12", "lodash": "^4.17.21", "svelte": "^3.46.2" } diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 31696601ea..302a1f5207 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/sdk", - "version": "2.0.30-alpha.11", + "version": "2.0.30-alpha.12", "description": "Budibase Public API SDK", "author": "Budibase", "license": "MPL-2.0", diff --git a/packages/server/package.json b/packages/server/package.json index 91db403231..81486310bf 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/server", "email": "hi@budibase.com", - "version": "2.0.30-alpha.11", + "version": "2.0.30-alpha.12", "description": "Budibase Web Server", "main": "src/index.ts", "repository": { @@ -77,11 +77,11 @@ "license": "GPL-3.0", "dependencies": { "@apidevtools/swagger-parser": "10.0.3", - "@budibase/backend-core": "2.0.30-alpha.11", - "@budibase/client": "2.0.30-alpha.11", + "@budibase/backend-core": "2.0.30-alpha.12", + "@budibase/client": "2.0.30-alpha.12", "@budibase/pro": "2.0.30-alpha.11", - "@budibase/string-templates": "2.0.30-alpha.11", - "@budibase/types": "2.0.30-alpha.11", + "@budibase/string-templates": "2.0.30-alpha.12", + "@budibase/types": "2.0.30-alpha.12", "@bull-board/api": "3.7.0", "@bull-board/koa": "3.9.4", "@elastic/elasticsearch": "7.10.0", diff --git a/packages/string-templates/package.json b/packages/string-templates/package.json index adc2dfce14..4438565dc3 100644 --- a/packages/string-templates/package.json +++ b/packages/string-templates/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/string-templates", - "version": "2.0.30-alpha.11", + "version": "2.0.30-alpha.12", "description": "Handlebars wrapper for Budibase templating.", "main": "src/index.cjs", "module": "dist/bundle.mjs", diff --git a/packages/types/package.json b/packages/types/package.json index 064e026322..167a82bde4 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/types", - "version": "2.0.30-alpha.11", + "version": "2.0.30-alpha.12", "description": "Budibase types", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/packages/worker/package.json b/packages/worker/package.json index 684225432f..4f27ada7dd 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/worker", "email": "hi@budibase.com", - "version": "2.0.30-alpha.11", + "version": "2.0.30-alpha.12", "description": "Budibase background service", "main": "src/index.ts", "repository": { @@ -36,10 +36,10 @@ "author": "Budibase", "license": "GPL-3.0", "dependencies": { - "@budibase/backend-core": "2.0.30-alpha.11", + "@budibase/backend-core": "2.0.30-alpha.12", "@budibase/pro": "2.0.30-alpha.11", - "@budibase/string-templates": "2.0.30-alpha.11", - "@budibase/types": "2.0.30-alpha.11", + "@budibase/string-templates": "2.0.30-alpha.12", + "@budibase/types": "2.0.30-alpha.12", "@koa/router": "8.0.8", "@sentry/node": "6.17.7", "@techpass/passport-openidconnect": "0.3.2", From 72245e7062bf01b876fb5d9b904a0de055086624 Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Mon, 24 Oct 2022 08:04:57 +0000 Subject: [PATCH 62/69] Update pro version to 2.0.30-alpha.12 --- packages/server/package.json | 2 +- packages/server/yarn.lock | 30 +++++++++++++++--------------- packages/worker/package.json | 2 +- packages/worker/yarn.lock | 30 +++++++++++++++--------------- 4 files changed, 32 insertions(+), 32 deletions(-) diff --git a/packages/server/package.json b/packages/server/package.json index 81486310bf..1392d4c0a4 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -79,7 +79,7 @@ "@apidevtools/swagger-parser": "10.0.3", "@budibase/backend-core": "2.0.30-alpha.12", "@budibase/client": "2.0.30-alpha.12", - "@budibase/pro": "2.0.30-alpha.11", + "@budibase/pro": "2.0.30-alpha.12", "@budibase/string-templates": "2.0.30-alpha.12", "@budibase/types": "2.0.30-alpha.12", "@bull-board/api": "3.7.0", diff --git a/packages/server/yarn.lock b/packages/server/yarn.lock index c9dad52e98..1d9d619f96 100644 --- a/packages/server/yarn.lock +++ b/packages/server/yarn.lock @@ -1094,12 +1094,12 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@budibase/backend-core@2.0.30-alpha.11": - version "2.0.30-alpha.11" - resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.0.30-alpha.11.tgz#84dc986537b2fdab6b01b1747a2805571f066149" - integrity sha512-njN81Ml+cDcYgOemhFX7bhAjBHUfxJAyjQwmt5qMjABAww4JM5hqPqW55kzC0++tYAIPh2gnIqZYfHpGAcDf9g== +"@budibase/backend-core@2.0.30-alpha.12": + version "2.0.30-alpha.12" + resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.0.30-alpha.12.tgz#adeb3e3f043c7b85a1a4aab3d3e33832d1ab8dfd" + integrity sha512-e/+tvvn1rcLTw/D/OlL9HYV1hw87x2mgofCV5Y1th6r1Tvum7Nr7revcU8CQIpVOc+iz6Eg1vAbMjqDsGr5YUw== dependencies: - "@budibase/types" "2.0.30-alpha.11" + "@budibase/types" "2.0.30-alpha.12" "@shopify/jest-koa-mocks" "5.0.1" "@techpass/passport-openidconnect" "0.3.2" aws-sdk "2.1030.0" @@ -1180,13 +1180,13 @@ svelte-flatpickr "^3.2.3" svelte-portal "^1.0.0" -"@budibase/pro@2.0.30-alpha.11": - version "2.0.30-alpha.11" - resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.0.30-alpha.11.tgz#e3a9c33f254cda3e850ba1f47fe948bd7c85bc22" - integrity sha512-+OEHVObJTzM15YRDAjjavxsNDt09QRn8Fs7mQL/IdqZUg8Vylu4JoSvoMTzg4QTC8SuR9VEjBgDosoZf6f37xQ== +"@budibase/pro@2.0.30-alpha.12": + version "2.0.30-alpha.12" + resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.0.30-alpha.12.tgz#52a26671aad4248fa2a6482a262f07c3f8154b7b" + integrity sha512-qp/plMQCpCabfDGeKZlOL74epNFzH1leZX7K/cqGwos0kYSElg6zPv/BayMRgID0oxllvuDq5M/fllXtF1QMig== dependencies: - "@budibase/backend-core" "2.0.30-alpha.11" - "@budibase/types" "2.0.30-alpha.11" + "@budibase/backend-core" "2.0.30-alpha.12" + "@budibase/types" "2.0.30-alpha.12" "@koa/router" "8.0.8" joi "17.6.0" node-fetch "^2.6.1" @@ -1209,10 +1209,10 @@ svelte-apexcharts "^1.0.2" svelte-flatpickr "^3.1.0" -"@budibase/types@2.0.30-alpha.11": - version "2.0.30-alpha.11" - resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.0.30-alpha.11.tgz#0fe7f42656d8293f5b3080ba323064d395ebdc9f" - integrity sha512-bLSPmewXtXH5aPo8bgXKr5hwWNzaQJKokkfsRmS5dTE+/XbH+AJU7uLmscLrhw1XlT9qZMcXS26Q/EULsp+Ywg== +"@budibase/types@2.0.30-alpha.12": + version "2.0.30-alpha.12" + resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.0.30-alpha.12.tgz#90912b6f6731d67f135787bc5cdeec562b30e336" + integrity sha512-PFO8BgScyaesA060ickUhiJTlKvOVIn6mUtc2rr3jrWZ5OTGGi31+eq+QhVcUS45BWuoEoPc0AhQPn7WtrvRQw== "@bull-board/api@3.7.0": version "3.7.0" diff --git a/packages/worker/package.json b/packages/worker/package.json index 4f27ada7dd..14f2fc9100 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -37,7 +37,7 @@ "license": "GPL-3.0", "dependencies": { "@budibase/backend-core": "2.0.30-alpha.12", - "@budibase/pro": "2.0.30-alpha.11", + "@budibase/pro": "2.0.30-alpha.12", "@budibase/string-templates": "2.0.30-alpha.12", "@budibase/types": "2.0.30-alpha.12", "@koa/router": "8.0.8", diff --git a/packages/worker/yarn.lock b/packages/worker/yarn.lock index 3ffd9a9bce..a6b041c08e 100644 --- a/packages/worker/yarn.lock +++ b/packages/worker/yarn.lock @@ -291,12 +291,12 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@budibase/backend-core@2.0.30-alpha.11": - version "2.0.30-alpha.11" - resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.0.30-alpha.11.tgz#84dc986537b2fdab6b01b1747a2805571f066149" - integrity sha512-njN81Ml+cDcYgOemhFX7bhAjBHUfxJAyjQwmt5qMjABAww4JM5hqPqW55kzC0++tYAIPh2gnIqZYfHpGAcDf9g== +"@budibase/backend-core@2.0.30-alpha.12": + version "2.0.30-alpha.12" + resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.0.30-alpha.12.tgz#adeb3e3f043c7b85a1a4aab3d3e33832d1ab8dfd" + integrity sha512-e/+tvvn1rcLTw/D/OlL9HYV1hw87x2mgofCV5Y1th6r1Tvum7Nr7revcU8CQIpVOc+iz6Eg1vAbMjqDsGr5YUw== dependencies: - "@budibase/types" "2.0.30-alpha.11" + "@budibase/types" "2.0.30-alpha.12" "@shopify/jest-koa-mocks" "5.0.1" "@techpass/passport-openidconnect" "0.3.2" aws-sdk "2.1030.0" @@ -327,21 +327,21 @@ uuid "8.3.2" zlib "1.0.5" -"@budibase/pro@2.0.30-alpha.11": - version "2.0.30-alpha.11" - resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.0.30-alpha.11.tgz#e3a9c33f254cda3e850ba1f47fe948bd7c85bc22" - integrity sha512-+OEHVObJTzM15YRDAjjavxsNDt09QRn8Fs7mQL/IdqZUg8Vylu4JoSvoMTzg4QTC8SuR9VEjBgDosoZf6f37xQ== +"@budibase/pro@2.0.30-alpha.12": + version "2.0.30-alpha.12" + resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.0.30-alpha.12.tgz#52a26671aad4248fa2a6482a262f07c3f8154b7b" + integrity sha512-qp/plMQCpCabfDGeKZlOL74epNFzH1leZX7K/cqGwos0kYSElg6zPv/BayMRgID0oxllvuDq5M/fllXtF1QMig== dependencies: - "@budibase/backend-core" "2.0.30-alpha.11" - "@budibase/types" "2.0.30-alpha.11" + "@budibase/backend-core" "2.0.30-alpha.12" + "@budibase/types" "2.0.30-alpha.12" "@koa/router" "8.0.8" joi "17.6.0" node-fetch "^2.6.1" -"@budibase/types@2.0.30-alpha.11": - version "2.0.30-alpha.11" - resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.0.30-alpha.11.tgz#0fe7f42656d8293f5b3080ba323064d395ebdc9f" - integrity sha512-bLSPmewXtXH5aPo8bgXKr5hwWNzaQJKokkfsRmS5dTE+/XbH+AJU7uLmscLrhw1XlT9qZMcXS26Q/EULsp+Ywg== +"@budibase/types@2.0.30-alpha.12": + version "2.0.30-alpha.12" + resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.0.30-alpha.12.tgz#90912b6f6731d67f135787bc5cdeec562b30e336" + integrity sha512-PFO8BgScyaesA060ickUhiJTlKvOVIn6mUtc2rr3jrWZ5OTGGi31+eq+QhVcUS45BWuoEoPc0AhQPn7WtrvRQw== "@cspotcode/source-map-consumer@0.8.0": version "0.8.0" From 6edffb0d5e7001c17ed57e736878a327108bf17b Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 24 Oct 2022 10:04:14 +0100 Subject: [PATCH 63/69] Addressing majority of PR comments. --- packages/backend-core/package.json | 2 +- .../src/events/publishers/backup.ts | 4 ++-- packages/backend-core/src/queue/listeners.ts | 12 +++++----- packages/backend-core/src/queue/queue.ts | 4 ++-- packages/backend-core/yarn.lock | 23 ++++--------------- packages/server/package.json | 2 +- packages/server/src/api/controllers/cloud.js | 6 ++--- packages/server/src/sdk/index.ts | 6 ++--- packages/server/yarn.lock | 16 +------------ packages/types/src/core/index.ts | 1 - packages/types/src/{core => sdk}/db.ts | 0 packages/types/src/sdk/events/backup.ts | 2 +- packages/types/src/sdk/index.ts | 1 + packages/worker/src/sdk/users/users.ts | 3 --- 14 files changed, 26 insertions(+), 56 deletions(-) delete mode 100644 packages/types/src/core/index.ts rename packages/types/src/{core => sdk}/db.ts (100%) diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json index e9c9261f62..3ca2281bd2 100644 --- a/packages/backend-core/package.json +++ b/packages/backend-core/package.json @@ -64,7 +64,7 @@ }, "devDependencies": { "@types/chance": "1.1.3", - "@types/ioredis": "4.28.10", + "@types/ioredis": "4.28.0", "@types/jest": "27.5.1", "@types/koa": "2.0.52", "@types/lodash": "4.14.180", diff --git a/packages/backend-core/src/events/publishers/backup.ts b/packages/backend-core/src/events/publishers/backup.ts index bd346cad64..0fc81da259 100644 --- a/packages/backend-core/src/events/publishers/backup.ts +++ b/packages/backend-core/src/events/publishers/backup.ts @@ -1,8 +1,8 @@ -import { AppBackup, AppBackupRevertEvent, Event } from "@budibase/types" +import { AppBackup, AppBackupRestoreEvent, Event } from "@budibase/types" import { publishEvent } from "../events" export async function appBackupRestored(backup: AppBackup) { - const properties: AppBackupRevertEvent = { + const properties: AppBackupRestoreEvent = { appId: backup.appId, backupName: backup.name!, backupCreatedAt: backup.timestamp, diff --git a/packages/backend-core/src/queue/listeners.ts b/packages/backend-core/src/queue/listeners.ts index f264c3a84c..e1975b5d06 100644 --- a/packages/backend-core/src/queue/listeners.ts +++ b/packages/backend-core/src/queue/listeners.ts @@ -6,18 +6,18 @@ export type StalledFn = (job: Job) => Promise export function addListeners( queue: Queue, jobQueue: JobQueue, - removeStalled?: StalledFn + removeStalledCb?: StalledFn ) { logging(queue, jobQueue) - if (removeStalled) { - handleStalled(queue, removeStalled) + if (removeStalledCb) { + handleStalled(queue, removeStalledCb) } } -function handleStalled(queue: Queue, removeStalled?: StalledFn) { +function handleStalled(queue: Queue, removeStalledCb?: StalledFn) { queue.on("stalled", async (job: Job) => { - if (removeStalled) { - await removeStalled(job) + if (removeStalledCb) { + await removeStalledCb(job) } else if (job.opts.repeat) { const jobId = job.id const repeatJobs = await queue.getRepeatableJobs() diff --git a/packages/backend-core/src/queue/queue.ts b/packages/backend-core/src/queue/queue.ts index 7eaafe38a8..e2f9ea9d94 100644 --- a/packages/backend-core/src/queue/queue.ts +++ b/packages/backend-core/src/queue/queue.ts @@ -18,7 +18,7 @@ async function cleanup() { export function createQueue( jobQueue: JobQueue, - removeStalled?: StalledFn + opts: { removeStalledCb?: StalledFn } ): BullQueue.Queue { const queueConfig: any = redisProtocolUrl || { redis: opts } let queue: any @@ -27,7 +27,7 @@ export function createQueue( } else { queue = new InMemoryQueue(jobQueue, queueConfig) } - addListeners(queue, jobQueue, removeStalled) + addListeners(queue, jobQueue, opts?.removeStalledCb) QUEUES.push(queue) if (!cleanupInterval) { cleanupInterval = setInterval(cleanup, CLEANUP_PERIOD_MS) diff --git a/packages/backend-core/yarn.lock b/packages/backend-core/yarn.lock index d2831ca8fe..d301526ba1 100644 --- a/packages/backend-core/yarn.lock +++ b/packages/backend-core/yarn.lock @@ -291,11 +291,6 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@budibase/types@2.0.30-alpha.3": - version "2.0.30-alpha.3" - resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.0.30-alpha.3.tgz#cb55bcced75b711cc8a675284fbacaa8ebf1c0f2" - integrity sha512-rHeFVuNbSSE4fMnX6uyrM2r47m+neqFXlVNOkhHU9i7KoIcIZbEYInU8CjUFR2da3ruST9ajXjJ5UenX2+MnTg== - "@hapi/hoek@^9.0.0": version "9.3.0" resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.3.0.tgz#8368869dcb735be2e7f5cb7647de78e167a251fb" @@ -698,14 +693,6 @@ "@types/connect" "*" "@types/node" "*" -"@types/bull@^3.15.9": - version "3.15.9" - resolved "https://registry.yarnpkg.com/@types/bull/-/bull-3.15.9.tgz#e10e0901ec3762bff85716b3c580277960751c93" - integrity sha512-MPUcyPPQauAmynoO3ezHAmCOhbB0pWmYyijr/5ctaCqhbKWsjW0YCod38ZcLzUBprosfZ9dPqfYIcfdKjk7RNQ== - dependencies: - "@types/ioredis" "*" - "@types/redis" "^2.8.0" - "@types/chance@1.1.3": version "1.1.3" resolved "https://registry.yarnpkg.com/@types/chance/-/chance-1.1.3.tgz#d19fe9391288d60fdccd87632bfc9ab2b4523fea" @@ -776,10 +763,10 @@ resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-1.8.2.tgz#7315b4c4c54f82d13fa61c228ec5c2ea5cc9e0e1" integrity sha512-EqX+YQxINb+MeXaIqYDASb6U6FCHbWjkj4a1CKDBks3d/QiB2+PqBLyO72vLDgAO1wUI4O+9gweRcQK11bTL/w== -"@types/ioredis@*", "@types/ioredis@^4.28.10": - version "4.28.10" - resolved "https://registry.yarnpkg.com/@types/ioredis/-/ioredis-4.28.10.tgz#40ceb157a4141088d1394bb87c98ed09a75a06ff" - integrity sha512-69LyhUgrXdgcNDv7ogs1qXZomnfOEnSmrmMFqKgt1XMJxmoOSG/u3wYy13yACIfKuMJ8IhKgHafDO3sx19zVQQ== +"@types/ioredis@4.28.0": + version "4.28.0" + resolved "https://registry.yarnpkg.com/@types/ioredis/-/ioredis-4.28.0.tgz#609b2ea0d91231df2dd7f67dd77436bc72584911" + integrity sha512-HSA/JQivJgV0e+353gvgu6WVoWvGRe0HyHOnAN2AvbVIhUlJBhNnnkP8gEEokrDWrxywrBkwo8NuDZ6TVPL9XA== dependencies: "@types/node" "*" @@ -1547,7 +1534,7 @@ buffer@^5.5.0, buffer@^5.6.0: base64-js "^1.3.1" ieee754 "^1.1.13" -bull@^4.10.1: +bull@4.10.1: version "4.10.1" resolved "https://registry.yarnpkg.com/bull/-/bull-4.10.1.tgz#f14974b6089358b62b495a2cbf838aadc098e43f" integrity sha512-Fp21tRPb2EaZPVfmM+ONZKVz2RA+to+zGgaTLyCKt3JMSU8OOBqK8143OQrnGuGpsyE5G+9FevFAGhdZZfQP2g== diff --git a/packages/server/package.json b/packages/server/package.json index 80d72723fb..88ee16dab3 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -163,7 +163,7 @@ "@types/google-spreadsheet": "3.1.5", "@types/ioredis": "4.28.10", "@types/jest": "27.5.1", - "@types/koa": "2.13.5", + "@types/koa": "2.13.4", "@types/koa__router": "8.0.11", "@types/lodash": "4.14.180", "@types/node": "14.18.20", diff --git a/packages/server/src/api/controllers/cloud.js b/packages/server/src/api/controllers/cloud.js index 27b576f181..0a111eae83 100644 --- a/packages/server/src/api/controllers/cloud.js +++ b/packages/server/src/api/controllers/cloud.js @@ -35,9 +35,9 @@ async function getAllDocType(db, docType) { } exports.exportApps = async ctx => { - // if (env.SELF_HOSTED || !env.MULTI_TENANCY) { - // ctx.throw(400, "Exporting only allowed in multi-tenant cloud environments.") - // } + if (env.SELF_HOSTED || !env.MULTI_TENANCY) { + ctx.throw(400, "Exporting only allowed in multi-tenant cloud environments.") + } const apps = await getAllApps({ all: true }) const globalDBString = await sdk.backups.exportDB(getGlobalDBName(), { filter: doc => !doc._id.startsWith(DocumentType.USER), diff --git a/packages/server/src/sdk/index.ts b/packages/server/src/sdk/index.ts index 85c01cdb44..8bdc4f8e77 100644 --- a/packages/server/src/sdk/index.ts +++ b/packages/server/src/sdk/index.ts @@ -1,13 +1,13 @@ import { default as backups } from "./app/backups" import { default as tables } from "./app/tables" -const toExport = { +const sdk = { backups, tables, } // default export for TS -export default toExport +export default sdk // default export for JS -module.exports = toExport +module.exports = sdk diff --git a/packages/server/yarn.lock b/packages/server/yarn.lock index 4d07fc6450..5fa18745e7 100644 --- a/packages/server/yarn.lock +++ b/packages/server/yarn.lock @@ -2831,7 +2831,7 @@ dependencies: "@types/koa" "*" -"@types/koa@*": +"@types/koa@*", "@types/koa@2.13.4": version "2.13.4" resolved "https://registry.yarnpkg.com/@types/koa/-/koa-2.13.4.tgz#10620b3f24a8027ef5cbae88b393d1b31205726b" integrity sha512-dfHYMfU+z/vKtQB7NUrthdAEiSvnLebvBjwHtfFmpZmB7em2N3WVQdHgnFq+xvyVgxW5jKDmjWfLD3lw4g4uTw== @@ -2845,20 +2845,6 @@ "@types/koa-compose" "*" "@types/node" "*" -"@types/koa@2.13.5": - version "2.13.5" - resolved "https://registry.yarnpkg.com/@types/koa/-/koa-2.13.5.tgz#64b3ca4d54e08c0062e89ec666c9f45443b21a61" - integrity sha512-HSUOdzKz3by4fnqagwthW/1w/yJspTgppyyalPVbgZf8jQWvdIXcVW5h2DGtw4zYntOaeRGx49r1hxoPWrD4aA== - dependencies: - "@types/accepts" "*" - "@types/content-disposition" "*" - "@types/cookies" "*" - "@types/http-assert" "*" - "@types/http-errors" "*" - "@types/keygrip" "*" - "@types/koa-compose" "*" - "@types/node" "*" - "@types/koa__router@8.0.11": version "8.0.11" resolved "https://registry.yarnpkg.com/@types/koa__router/-/koa__router-8.0.11.tgz#d7b37e6db934fc072ea1baa2ab92bc8ac4564f3e" diff --git a/packages/types/src/core/index.ts b/packages/types/src/core/index.ts deleted file mode 100644 index 9071393365..0000000000 --- a/packages/types/src/core/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./db" diff --git a/packages/types/src/core/db.ts b/packages/types/src/sdk/db.ts similarity index 100% rename from packages/types/src/core/db.ts rename to packages/types/src/sdk/db.ts diff --git a/packages/types/src/sdk/events/backup.ts b/packages/types/src/sdk/events/backup.ts index f3ddafcafc..8d3e2b1afa 100644 --- a/packages/types/src/sdk/events/backup.ts +++ b/packages/types/src/sdk/events/backup.ts @@ -1,6 +1,6 @@ import { BaseEvent } from "./event" -export interface AppBackupRevertEvent extends BaseEvent { +export interface AppBackupRestoreEvent extends BaseEvent { appId: string backupName: string backupCreatedAt: string diff --git a/packages/types/src/sdk/index.ts b/packages/types/src/sdk/index.ts index 0c374dd105..724b152303 100644 --- a/packages/types/src/sdk/index.ts +++ b/packages/types/src/sdk/index.ts @@ -8,3 +8,4 @@ export * from "./search" export * from "./koa" export * from "./auth" export * from "./locks" +export * from "./db" diff --git a/packages/worker/src/sdk/users/users.ts b/packages/worker/src/sdk/users/users.ts index ce03a12587..b0290507fc 100644 --- a/packages/worker/src/sdk/users/users.ts +++ b/packages/worker/src/sdk/users/users.ts @@ -79,9 +79,6 @@ export const paginatedUsers = async ({ } else if (email) { userList = await usersCore.searchGlobalUsersByEmail(email, opts) property = "email" - } - if (userIds) { - // TODO: search users by userIds } else { // no search, query allDocs const response = await db.allDocs(dbUtils.getGlobalUserParams(null, opts)) From 147799ba0a692c1243085d559b2eb4191c5391d3 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 24 Oct 2022 10:14:35 +0100 Subject: [PATCH 64/69] Moving statistics under backups sdk subfolder. --- packages/server/src/sdk/app/backups/backup.ts | 2 +- packages/server/src/sdk/app/backups/index.ts | 2 ++ .../src/sdk/app/{statistics/index.ts => backups/statistics.ts} | 0 3 files changed, 3 insertions(+), 1 deletion(-) rename packages/server/src/sdk/app/{statistics/index.ts => backups/statistics.ts} (100%) diff --git a/packages/server/src/sdk/app/backups/backup.ts b/packages/server/src/sdk/app/backups/backup.ts index abb2272a99..ac85b2940f 100644 --- a/packages/server/src/sdk/app/backups/backup.ts +++ b/packages/server/src/sdk/app/backups/backup.ts @@ -8,7 +8,7 @@ import { } from "@budibase/types" import { exportApp } from "./exports" import { importApp } from "./imports" -import { calculateBackupStats } from "../statistics" +import { calculateBackupStats } from "./statistics" import { Job } from "bull" import fs from "fs" import env from "../../../environment" diff --git a/packages/server/src/sdk/app/backups/index.ts b/packages/server/src/sdk/app/backups/index.ts index 94210c2c4c..c2dd7a7b71 100644 --- a/packages/server/src/sdk/app/backups/index.ts +++ b/packages/server/src/sdk/app/backups/index.ts @@ -1,9 +1,11 @@ import * as exportApps from "./exports" import * as importApps from "./imports" import * as backup from "./backup" +import * as statistics from "./statistics" export default { ...exportApps, ...importApps, ...backup, + ...statistics, } diff --git a/packages/server/src/sdk/app/statistics/index.ts b/packages/server/src/sdk/app/backups/statistics.ts similarity index 100% rename from packages/server/src/sdk/app/statistics/index.ts rename to packages/server/src/sdk/app/backups/statistics.ts From e31ce3f8a911bcd644cd3c68a1ed34d943f342cd Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 24 Oct 2022 12:06:50 +0100 Subject: [PATCH 65/69] Some fixes based on PR comments. --- packages/backend-core/src/queue/queue.ts | 6 +++--- packages/types/src/index.ts | 1 - packages/types/src/sdk/koa.ts | 20 ++++++++------------ 3 files changed, 11 insertions(+), 16 deletions(-) diff --git a/packages/backend-core/src/queue/queue.ts b/packages/backend-core/src/queue/queue.ts index e2f9ea9d94..b4eeeb31aa 100644 --- a/packages/backend-core/src/queue/queue.ts +++ b/packages/backend-core/src/queue/queue.ts @@ -4,7 +4,7 @@ import { JobQueue } from "./constants" import InMemoryQueue from "./inMemoryQueue" import BullQueue from "bull" import { addListeners, StalledFn } from "./listeners" -const { opts, redisProtocolUrl } = getRedisOptions() +const { opts: redisOpts, redisProtocolUrl } = getRedisOptions() const CLEANUP_PERIOD_MS = 60 * 1000 let QUEUES: BullQueue.Queue[] | InMemoryQueue[] = [] @@ -18,9 +18,9 @@ async function cleanup() { export function createQueue( jobQueue: JobQueue, - opts: { removeStalledCb?: StalledFn } + opts: { removeStalledCb?: StalledFn } = {} ): BullQueue.Queue { - const queueConfig: any = redisProtocolUrl || { redis: opts } + const queueConfig: any = redisProtocolUrl || { redis: redisOpts } let queue: any if (!env.isTest()) { queue = new BullQueue(jobQueue, queueConfig) diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 92d2ceb050..4adb2fda97 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -1,4 +1,3 @@ export * from "./documents" export * from "./sdk" export * from "./api" -export * from "./core" diff --git a/packages/types/src/sdk/koa.ts b/packages/types/src/sdk/koa.ts index 56666f7e2a..8004ba72ae 100644 --- a/packages/types/src/sdk/koa.ts +++ b/packages/types/src/sdk/koa.ts @@ -1,4 +1,4 @@ -import { Context } from "koa" +import { Context, Request } from "koa" import { User } from "../documents" import { License } from "../sdk" @@ -7,15 +7,11 @@ export interface ContextUser extends User { license: License } -export interface BBContext { - user?: ContextUser - status?: number - request: { - body: any - } - params: any - body?: any - redirect?: any - attachment: any - throw: any +export interface BBRequest extends Request { + body: any +} + +export interface BBContext extends Context { + request: BBRequest + user?: ContextUser } From 0274b6755a20ec330c8a6321108f851e615ac43c Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Mon, 24 Oct 2022 13:05:40 +0100 Subject: [PATCH 66/69] Replace isTest check with mock for triggering app backup --- packages/server/specs/openapi.json | 3 ++ packages/server/specs/openapi.yaml | 3 ++ .../src/api/controllers/deploy/index.ts | 16 ++------ .../src/api/routes/tests/deployment.spec.js | 25 ------------ .../src/api/routes/tests/deployment.spec.ts | 40 +++++++++++++++++++ .../src/tests/utilities/TestConfiguration.js | 7 +++- 6 files changed, 55 insertions(+), 39 deletions(-) delete mode 100644 packages/server/src/api/routes/tests/deployment.spec.js create mode 100644 packages/server/src/api/routes/tests/deployment.spec.ts diff --git a/packages/server/specs/openapi.json b/packages/server/specs/openapi.json index ce410823ec..62f59e9113 100644 --- a/packages/server/specs/openapi.json +++ b/packages/server/specs/openapi.json @@ -783,6 +783,7 @@ "type": "string", "enum": [ "string", + "barcodeqr", "longform", "options", "number", @@ -986,6 +987,7 @@ "type": "string", "enum": [ "string", + "barcodeqr", "longform", "options", "number", @@ -1200,6 +1202,7 @@ "type": "string", "enum": [ "string", + "barcodeqr", "longform", "options", "number", diff --git a/packages/server/specs/openapi.yaml b/packages/server/specs/openapi.yaml index ed13ac01f4..25069f40a4 100644 --- a/packages/server/specs/openapi.yaml +++ b/packages/server/specs/openapi.yaml @@ -579,6 +579,7 @@ components: type: string enum: - string + - barcodeqr - longform - options - number @@ -741,6 +742,7 @@ components: type: string enum: - string + - barcodeqr - longform - options - number @@ -910,6 +912,7 @@ components: type: string enum: - string + - barcodeqr - longform - options - number diff --git a/packages/server/src/api/controllers/deploy/index.ts b/packages/server/src/api/controllers/deploy/index.ts index b3b875e397..a1cb905930 100644 --- a/packages/server/src/api/controllers/deploy/index.ts +++ b/packages/server/src/api/controllers/deploy/index.ts @@ -20,7 +20,6 @@ import { import { events } from "@budibase/backend-core" import { backups } from "@budibase/pro" import { AppBackupTrigger } from "@budibase/types" -import env from "../../../environment" // the max time we can wait for an invalidation to complete before considering it failed const MAX_PENDING_TIME_MS = 30 * 60000 @@ -108,17 +107,10 @@ async function deployApp(deployment: any, userId: string) { const devAppId = getDevelopmentAppID(appId) const productionAppId = getProdAppID(appId) - // can't do this in test - if (!env.isTest()) { - // trigger backup initially - await backups.triggerAppBackup( - productionAppId, - AppBackupTrigger.PUBLISH, - { - createdBy: userId, - } - ) - } + // trigger backup initially + await backups.triggerAppBackup(productionAppId, AppBackupTrigger.PUBLISH, { + createdBy: userId, + }) const config: any = { source: devAppId, diff --git a/packages/server/src/api/routes/tests/deployment.spec.js b/packages/server/src/api/routes/tests/deployment.spec.js deleted file mode 100644 index be126fa239..0000000000 --- a/packages/server/src/api/routes/tests/deployment.spec.js +++ /dev/null @@ -1,25 +0,0 @@ -const setup = require("./utilities") -const { events } = require("@budibase/backend-core") - -describe("/deployments", () => { - let request = setup.getRequest() - let config = setup.getConfig() - - afterAll(setup.afterAll) - - beforeEach(async () => { - await config.init() - jest.clearAllMocks() - }) - - describe("deploy", () => { - it("should deploy the application", async () => { - await request - .post(`/api/deploy`) - .set(config.defaultHeaders()) - .expect("Content-Type", /json/) - .expect(200) - expect(events.app.published.mock.calls.length).toBe(1) - }) - }) -}) \ No newline at end of file diff --git a/packages/server/src/api/routes/tests/deployment.spec.ts b/packages/server/src/api/routes/tests/deployment.spec.ts new file mode 100644 index 0000000000..e659ff1a69 --- /dev/null +++ b/packages/server/src/api/routes/tests/deployment.spec.ts @@ -0,0 +1,40 @@ +const triggerAppBackupMock = jest.fn() +jest.mock("@budibase/pro", () => ({ + ...jest.requireActual("@budibase/pro"), + backups: { + triggerAppBackup: triggerAppBackupMock, + addAppBackupProcessors: jest.fn(), + }, +})) +import setup from "./utilities" +import { events } from "@budibase/backend-core" +import { AppBackupTrigger } from "@budibase/types" + +describe("/deployments", () => { + let request = setup.getRequest() + let config = setup.getConfig() + + afterAll(setup.afterAll) + + beforeEach(async () => { + await config.init() + jest.clearAllMocks() + }) + + describe("deploy", () => { + it("should deploy the application", async () => { + await request + .post(`/api/deploy`) + .set(config.defaultHeaders()) + .expect("Content-Type", /json/) + .expect(200) + expect(triggerAppBackupMock).toBeCalledTimes(1) + expect(triggerAppBackupMock).toBeCalledWith( + config.prodAppId, + AppBackupTrigger.PUBLISH, + { createdBy: config.userMetadataId } + ) + expect((events.app.published as jest.Mock).mock.calls.length).toBe(1) + }) + }) +}) diff --git a/packages/server/src/tests/utilities/TestConfiguration.js b/packages/server/src/tests/utilities/TestConfiguration.js index 38aa84be2d..097b2eabaf 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.js +++ b/packages/server/src/tests/utilities/TestConfiguration.js @@ -25,7 +25,7 @@ const newid = require("../../db/newid") const context = require("@budibase/backend-core/context") const { generateDevInfoID, SEPARATOR } = require("@budibase/backend-core/db") const { encrypt } = require("@budibase/backend-core/encryption") -const { DocumentType } = require("../../db/utils") +const { DocumentType, generateUserMetadataID } = require("../../db/utils") const GLOBAL_USER_ID = "us_uuid1" const EMAIL = "babs@babs.com" @@ -95,7 +95,10 @@ class TestConfiguration { // use a new id as the name to avoid name collisions async init(appName = newid()) { - await this.globalUser() + this.user = await this.globalUser() + this.globalUserId = this.user._id + this.userMetadataId = generateUserMetadataID(this.globalUserId) + return this.createApp(appName) } From 12d0187211341216c187e649d814035570bc5537 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 24 Oct 2022 13:33:36 +0100 Subject: [PATCH 67/69] Adding a check to see if app backups feature is enabled. --- .../src/api/controllers/deploy/index.ts | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/packages/server/src/api/controllers/deploy/index.ts b/packages/server/src/api/controllers/deploy/index.ts index b3b875e397..f1700aef87 100644 --- a/packages/server/src/api/controllers/deploy/index.ts +++ b/packages/server/src/api/controllers/deploy/index.ts @@ -110,14 +110,17 @@ async function deployApp(deployment: any, userId: string) { // can't do this in test if (!env.isTest()) { - // trigger backup initially - await backups.triggerAppBackup( - productionAppId, - AppBackupTrigger.PUBLISH, - { - createdBy: userId, - } - ) + // don't try this if feature isn't allowed, will error + if (!(await backups.isEnabled())) { + // trigger backup initially + await backups.triggerAppBackup( + productionAppId, + AppBackupTrigger.PUBLISH, + { + createdBy: userId, + } + ) + } } const config: any = { From 20dffc0a07b1ea8814d0028e3eda2f7e40369cf8 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Mon, 24 Oct 2022 14:23:16 +0100 Subject: [PATCH 68/69] Remove pro mock and invert if condition on backups enabled --- .../server/src/api/controllers/deploy/index.ts | 2 +- .../src/api/routes/tests/deployment.spec.ts | 15 --------------- 2 files changed, 1 insertion(+), 16 deletions(-) diff --git a/packages/server/src/api/controllers/deploy/index.ts b/packages/server/src/api/controllers/deploy/index.ts index 4a7cc5a754..cb4534a1a5 100644 --- a/packages/server/src/api/controllers/deploy/index.ts +++ b/packages/server/src/api/controllers/deploy/index.ts @@ -108,7 +108,7 @@ async function deployApp(deployment: any, userId: string) { const productionAppId = getProdAppID(appId) // don't try this if feature isn't allowed, will error - if (!(await backups.isEnabled())) { + if (await backups.isEnabled()) { // trigger backup initially await backups.triggerAppBackup( productionAppId, diff --git a/packages/server/src/api/routes/tests/deployment.spec.ts b/packages/server/src/api/routes/tests/deployment.spec.ts index e659ff1a69..0219e3f2b4 100644 --- a/packages/server/src/api/routes/tests/deployment.spec.ts +++ b/packages/server/src/api/routes/tests/deployment.spec.ts @@ -1,14 +1,5 @@ -const triggerAppBackupMock = jest.fn() -jest.mock("@budibase/pro", () => ({ - ...jest.requireActual("@budibase/pro"), - backups: { - triggerAppBackup: triggerAppBackupMock, - addAppBackupProcessors: jest.fn(), - }, -})) import setup from "./utilities" import { events } from "@budibase/backend-core" -import { AppBackupTrigger } from "@budibase/types" describe("/deployments", () => { let request = setup.getRequest() @@ -28,12 +19,6 @@ describe("/deployments", () => { .set(config.defaultHeaders()) .expect("Content-Type", /json/) .expect(200) - expect(triggerAppBackupMock).toBeCalledTimes(1) - expect(triggerAppBackupMock).toBeCalledWith( - config.prodAppId, - AppBackupTrigger.PUBLISH, - { createdBy: config.userMetadataId } - ) expect((events.app.published as jest.Mock).mock.calls.length).toBe(1) }) }) From efedc50f4bfbe22130e70f8e69f2f04db52d262d Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 24 Oct 2022 15:43:48 +0100 Subject: [PATCH 69/69] Adding in retention days for app backups. --- packages/types/src/sdk/licensing/quota.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/types/src/sdk/licensing/quota.ts b/packages/types/src/sdk/licensing/quota.ts index 74777d4590..86a092c6f5 100644 --- a/packages/types/src/sdk/licensing/quota.ts +++ b/packages/types/src/sdk/licensing/quota.ts @@ -25,6 +25,7 @@ export enum MonthlyQuotaName { export enum ConstantQuotaName { AUTOMATION_LOG_RETENTION_DAYS = "automationLogRetentionDays", + APP_BACKUPS_RETENTION_DAYS = "appBackupRetentionDays", } export type MeteredQuotaName = StaticQuotaName | MonthlyQuotaName @@ -76,6 +77,7 @@ export type StaticQuotas = { export type ConstantQuotas = { [ConstantQuotaName.AUTOMATION_LOG_RETENTION_DAYS]: Quota + [ConstantQuotaName.APP_BACKUPS_RETENTION_DAYS]: Quota } export type Quotas = {