diff --git a/README.md b/README.md index 692c6b7100..2cfc967c4c 100644 --- a/README.md +++ b/README.md @@ -172,7 +172,7 @@ For more information, see [CONTRIBUTING.md](https://github.com/Budibase/budibase ## 📝 License -Budibase is open-source. The builder is licensed [AGPL v3](https://www.gnu.org/licenses/agpl-3.0.en.html), the server is licensed [GPL v3](https://www.gnu.org/licenses/gpl-3.0.en.html), and the client is licensed [MPL](https://directory.fsf.org/wiki/License:MPL-2.0). +Budibase is open-source, licensed as [GPL v3](https://www.gnu.org/licenses/gpl-3.0.en.html). The client and component libraries are licensed as [MPL](https://directory.fsf.org/wiki/License:MPL-2.0) - so the apps that you build can be licensed however you like. --- diff --git a/lerna.json b/lerna.json index 2dcb285c16..48ff5f5bf7 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "0.9.45", + "version": "0.9.48", "npmClient": "yarn", "packages": [ "packages/*" diff --git a/packages/auth/package.json b/packages/auth/package.json index 734ce46328..4b03435e62 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/auth", - "version": "0.9.45", + "version": "0.9.48", "description": "Authentication middlewares for budibase builder and apps", "main": "src/index.js", "author": "Budibase", diff --git a/packages/bbui/package.json b/packages/bbui/package.json index 006de5ae05..569c2780ec 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": "0.9.45", + "version": "0.9.48", "license": "AGPL-3.0", "svelte": "src/index.js", "module": "dist/bbui.es.js", diff --git a/packages/bbui/src/ActionMenu/ActionMenu.svelte b/packages/bbui/src/ActionMenu/ActionMenu.svelte index 8f17f260a8..45bb65b527 100644 --- a/packages/bbui/src/ActionMenu/ActionMenu.svelte +++ b/packages/bbui/src/ActionMenu/ActionMenu.svelte @@ -5,6 +5,7 @@ export let disabled = false export let align = "left" + export let portalTarget let anchor let dropdown @@ -32,7 +33,7 @@
- + diff --git a/packages/bbui/src/Popover/Popover.svelte b/packages/bbui/src/Popover/Popover.svelte index e07dc28ccf..6c9c6cc9a3 100644 --- a/packages/bbui/src/Popover/Popover.svelte +++ b/packages/bbui/src/Popover/Popover.svelte @@ -9,6 +9,7 @@ export let anchor export let align = "right" + export let portalTarget export const show = () => { dispatch("open") @@ -30,7 +31,7 @@ {#if open} - +
{ // The builder preview pages don't have a real URL, so all we can do // is check that we were able to bind to the property, and that the // component exists on the page - cy.getComponent(componentId).should("have.text", "") + cy.getComponent(componentId).should("have.text", "Placeholder text") }) }) diff --git a/packages/builder/package.json b/packages/builder/package.json index e9a2028856..4fb88c6da4 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/builder", - "version": "0.9.45", + "version": "0.9.48", "license": "AGPL-3.0", "private": true, "scripts": { @@ -65,10 +65,10 @@ } }, "dependencies": { - "@budibase/bbui": "^0.9.45", - "@budibase/client": "^0.9.45", + "@budibase/bbui": "^0.9.48", + "@budibase/client": "^0.9.48", "@budibase/colorpicker": "1.1.2", - "@budibase/string-templates": "^0.9.45", + "@budibase/string-templates": "^0.9.48", "@sentry/browser": "5.19.1", "@spectrum-css/page": "^3.0.1", "@spectrum-css/vars": "^3.0.1", diff --git a/packages/builder/src/builderStore/store/screenTemplates/createFromScratchScreen.js b/packages/builder/src/builderStore/store/screenTemplates/createFromScratchScreen.js index 1f30d974ef..817dfeceea 100644 --- a/packages/builder/src/builderStore/store/screenTemplates/createFromScratchScreen.js +++ b/packages/builder/src/builderStore/store/screenTemplates/createFromScratchScreen.js @@ -6,9 +6,5 @@ export default { } const createScreen = () => { - return new Screen() - .mainType("div") - .component("@budibase/standard-components/container") - .instanceName("New Screen") - .json() + return new Screen().instanceName("New Screen").json() } diff --git a/packages/builder/src/builderStore/store/screenTemplates/newRowScreen.js b/packages/builder/src/builderStore/store/screenTemplates/newRowScreen.js index 980f71742b..23f2fd846f 100644 --- a/packages/builder/src/builderStore/store/screenTemplates/newRowScreen.js +++ b/packages/builder/src/builderStore/store/screenTemplates/newRowScreen.js @@ -28,8 +28,10 @@ function generateTitleContainer(table, formId) { const createScreen = table => { const screen = new Screen() - .component("@budibase/standard-components/container") .instanceName(`${table.name} - New`) + .customProps({ + hAlign: "center", + }) .route(newRowUrl(table)) const form = makeMainForm() diff --git a/packages/builder/src/builderStore/store/screenTemplates/rowDetailScreen.js b/packages/builder/src/builderStore/store/screenTemplates/rowDetailScreen.js index 4292e29674..3bc131524e 100644 --- a/packages/builder/src/builderStore/store/screenTemplates/rowDetailScreen.js +++ b/packages/builder/src/builderStore/store/screenTemplates/rowDetailScreen.js @@ -149,9 +149,11 @@ const createScreen = table => { provider.addChild(repeater) return new Screen() - .component("@budibase/standard-components/container") .instanceName(`${table.name} - Detail`) .route(rowDetailUrl(table)) + .customProps({ + hAlign: "center", + }) .addChild(provider) .json() } diff --git a/packages/builder/src/builderStore/store/screenTemplates/rowListScreen.js b/packages/builder/src/builderStore/store/screenTemplates/rowListScreen.js index da966132b5..3cfbe8f2fe 100644 --- a/packages/builder/src/builderStore/store/screenTemplates/rowListScreen.js +++ b/packages/builder/src/builderStore/store/screenTemplates/rowListScreen.js @@ -56,14 +56,15 @@ function generateTitleContainer(table) { .text(table.name) return new Component("@budibase/standard-components/container") - .type("div") .normalStyle({ - display: "flex", - "flex-direction": "row", - "justify-content": "space-between", - "align-items": "center", "margin-bottom": "32px", }) + .customProps({ + direction: "row", + hAlign: "stretch", + vAlign: "middle", + size: "shrink", + }) .instanceName("Title Container") .addChild(heading) .addChild(newButton) @@ -79,7 +80,8 @@ const createScreen = table => { tableId: table._id, type: "table", }, - paginate: false, + paginate: true, + limit: 8, }) const spectrumTable = new Component("@budibase/standard-components/table") @@ -126,7 +128,6 @@ const createScreen = table => { background: "white", "border-radius": "0.5rem", "box-shadow": "0 1px 2px 0 rgba(0, 0, 0, 0.05)", - margin: "auto", "margin-top": "20px", "border-width": "2px", "border-color": "rgba(0, 0, 0, 0.1)", @@ -137,13 +138,17 @@ const createScreen = table => { "padding-left": "48px", "margin-bottom": "20px", }) - .type("div") + .customProps({ + direction: "column", + hAlign: "stretch", + vAlign: "top", + size: "shrink", + }) .instanceName("Container") .addChild(generateTitleContainer(table)) .addChild(provider) return new Screen() - .component("@budibase/standard-components/container") .route(rowListUrl(table)) .instanceName(`${table.name} - List`) .addChild(mainContainer) diff --git a/packages/builder/src/builderStore/store/screenTemplates/utils/Screen.js b/packages/builder/src/builderStore/store/screenTemplates/utils/Screen.js index 79e1632ad2..239a4dfb50 100644 --- a/packages/builder/src/builderStore/store/screenTemplates/utils/Screen.js +++ b/packages/builder/src/builderStore/store/screenTemplates/utils/Screen.js @@ -8,7 +8,7 @@ export class Screen extends BaseStructure { layoutId: "layout_private_master", props: { _id: uuid(), - _component: "", + _component: "@budibase/standard-components/container", _styles: { normal: {}, hover: {}, @@ -17,6 +17,10 @@ export class Screen extends BaseStructure { }, _children: [], _instanceName: "", + direction: "column", + hAlign: "stretch", + vAlign: "top", + size: "grow", }, routing: { route: "", @@ -41,11 +45,6 @@ export class Screen extends BaseStructure { return this } - mainType(type) { - this._json.type = type - return this - } - route(route) { this._json.routing.route = route return this @@ -60,4 +59,11 @@ export class Screen extends BaseStructure { this._json.props._instanceName = name return this } + + customProps(props) { + for (let key of Object.keys(props)) { + this._json.props[key] = props[key] + } + return this + } } diff --git a/packages/builder/src/builderStore/store/screenTemplates/utils/commonComponents.js b/packages/builder/src/builderStore/store/screenTemplates/utils/commonComponents.js index e234a1a770..3771ec21b8 100644 --- a/packages/builder/src/builderStore/store/screenTemplates/utils/commonComponents.js +++ b/packages/builder/src/builderStore/store/screenTemplates/utils/commonComponents.js @@ -35,13 +35,11 @@ export function makeLinkComponent(tableName) { export function makeMainForm() { return new Component("@budibase/standard-components/form") - .type("div") .normalStyle({ width: "700px", padding: "0px", "border-radius": "0.5rem", "box-shadow": "0 1px 2px 0 rgba(0, 0, 0, 0.05)", - margin: "auto", "margin-top": "20px", "padding-top": "48px", "padding-bottom": "48px", @@ -79,11 +77,16 @@ export function makeBreadcrumbContainer(tableName, text, capitalise = false) { .instanceName("Identifier") return new Component("@budibase/standard-components/container") - .type("div") .normalStyle({ "font-size": "14px", color: "#757575", }) + .customProps({ + direction: "row", + hAlign: "left", + vAlign: "middle", + size: "shrink", + }) .instanceName("Breadcrumbs") .addChild(link) .addChild(arrowText) @@ -149,15 +152,16 @@ export function makeTitleContainer(title) { .text(title) return new Component("@budibase/standard-components/container") - .type("div") .normalStyle({ - display: "flex", - "flex-direction": "row", - "justify-content": "space-between", - "align-items": "center", "margin-top": "32px", "margin-bottom": "32px", }) + .customProps({ + direction: "row", + hAlign: "stretch", + vAlign: "middle", + size: "shrink", + }) .instanceName("Title Container") .addChild(heading) } diff --git a/packages/builder/src/components/backend/DataTable/modals/CreateEditUser.svelte b/packages/builder/src/components/backend/DataTable/modals/CreateEditUser.svelte index 62985d4285..32f369ce3d 100644 --- a/packages/builder/src/components/backend/DataTable/modals/CreateEditUser.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/CreateEditUser.svelte @@ -104,6 +104,7 @@ options={$roles} getOptionLabel={role => role.name} getOptionValue={role => role._id} + disabled={!creating} /> {#each customSchemaKeys as [key, meta]} {#if !meta.autocolumn} diff --git a/packages/builder/src/components/design/AppPreview/CurrentItemPreview.svelte b/packages/builder/src/components/design/AppPreview/CurrentItemPreview.svelte index 69287fac38..4929cf5db2 100644 --- a/packages/builder/src/components/design/AppPreview/CurrentItemPreview.svelte +++ b/packages/builder/src/components/design/AppPreview/CurrentItemPreview.svelte @@ -70,9 +70,16 @@ { once: true } ) - // Add listener to select components - iframe.contentWindow.addEventListener("bb-select-component", data => { - store.actions.components.select({ _id: data.detail }) + // Add listener for events sent by cliebt library in preview + iframe.contentWindow.addEventListener("bb-event", event => { + const { type, data } = event.detail + if (type === "select-component") { + store.actions.components.select({ _id: data.id }) + } else if (type === "update-prop") { + store.actions.components.updateProp(data.prop, data.value) + } else { + console.log(data) + } }) }) @@ -94,12 +101,12 @@ overflow: hidden; margin: auto; height: 100%; - background-color: white; } .component-container iframe { border: 0; left: 0; top: 0; width: 100%; + background-color: transparent; } diff --git a/packages/builder/src/components/design/AppPreview/iframeTemplate.js b/packages/builder/src/components/design/AppPreview/iframeTemplate.js index da39a6477b..c715cfc814 100644 --- a/packages/builder/src/components/design/AppPreview/iframeTemplate.js +++ b/packages/builder/src/components/design/AppPreview/iframeTemplate.js @@ -14,10 +14,14 @@ export default ` diff --git a/packages/client/src/components/Component.svelte b/packages/client/src/components/Component.svelte index 4d2dddffcc..6f5c070358 100644 --- a/packages/client/src/components/Component.svelte +++ b/packages/client/src/components/Component.svelte @@ -6,8 +6,10 @@ import { enrichProps, propsAreSame } from "../utils/componentProps" import { builderStore } from "../store" import { hashString } from "../utils/hash" + import Manifest from "@budibase/standard-components/manifest.json" + import { Placeholder } from "@budibase/standard-components" - export let definition = {} + export let instance = {} // Props that will be passed to the component instance let componentProps @@ -23,25 +25,39 @@ // Get contexts const context = getContext("context") + const insideScreenslot = !!getContext("screenslot") // Create component context const componentStore = writable({}) setContext("component", componentStore) - // Extract component definition info - $: constructor = getComponentConstructor(definition._component) - $: children = definition._children || [] - $: id = definition._id - $: updateComponentProps(definition, $context) - $: styles = definition._styles - $: transition = definition._transition + // Extract component instance info + $: constructor = getComponentConstructor(instance._component) + $: definition = getComponentDefinition(instance._component) + $: children = instance._children || [] + $: id = instance._id + $: name = instance._instanceName + $: empty = + !children.length && + definition?.hasChildren && + definition?.showEmptyState !== false && + $builderStore.inBuilder + $: updateComponentProps(instance, $context) + $: selected = + $builderStore.inBuilder && + $builderStore.selectedComponentId === instance._id + $: interactive = $builderStore.previewType === "layout" || insideScreenslot // Update component context $: componentStore.set({ id, children: children.length, - styles: { ...styles, id }, - transition, + styles: { ...instance._styles, id, empty, interactive }, + empty, + transition: instance._transition, + selected, + props: componentProps, + name, }) // Gets the component constructor for the specified component @@ -54,14 +70,20 @@ return ComponentLibrary[name] } + const getComponentDefinition = component => { + const prefix = "@budibase/standard-components/" + const type = component?.replace(prefix, "") + return type ? Manifest[type] : null + } + // Enriches any string component props using handlebars - const updateComponentProps = (definition, context) => { + const updateComponentProps = (instance, context) => { // Record the timestamp so we can reference it after enrichment latestUpdateTime = Date.now() const enrichmentTime = latestUpdateTime // Enrich props with context - const enrichedProps = enrichProps(definition, context) + const enrichedProps = enrichProps(instance, context) // Abandon this update if a newer update has started if (enrichmentTime !== latestUpdateTime) { @@ -94,14 +116,29 @@ } -{#if constructor && componentProps} +
{#key propsHash} - - {#if children.length} - {#each children as child (child._id)} - - {/each} - {/if} - + {#if constructor && componentProps} + + {#if children.length} + {#each children as child (child._id)} + + {/each} + {:else if empty} + + {/if} + + {/if} {/key} -{/if} +
+ + diff --git a/packages/client/src/components/Router.svelte b/packages/client/src/components/Router.svelte index 545fae79d6..f9539e9fe5 100644 --- a/packages/client/src/components/Router.svelte +++ b/packages/client/src/components/Router.svelte @@ -1,12 +1,12 @@ + + diff --git a/packages/client/src/components/preview/Indicator.svelte b/packages/client/src/components/preview/Indicator.svelte new file mode 100644 index 0000000000..fb1b5f320f --- /dev/null +++ b/packages/client/src/components/preview/Indicator.svelte @@ -0,0 +1,61 @@ + + +
+ {#if text} +
+ {text} +
+ {/if} +
+ + diff --git a/packages/client/src/components/preview/IndicatorSet.svelte b/packages/client/src/components/preview/IndicatorSet.svelte new file mode 100644 index 0000000000..74c9f06f0d --- /dev/null +++ b/packages/client/src/components/preview/IndicatorSet.svelte @@ -0,0 +1,118 @@ + + +{#key componentId} + {#each visibleIndicators as indicator, idx} + + {/each} +{/key} diff --git a/packages/client/src/components/preview/SelectionIndicator.svelte b/packages/client/src/components/preview/SelectionIndicator.svelte new file mode 100644 index 0000000000..bb71856211 --- /dev/null +++ b/packages/client/src/components/preview/SelectionIndicator.svelte @@ -0,0 +1,11 @@ + + + diff --git a/packages/client/src/components/preview/SettingsBar.svelte b/packages/client/src/components/preview/SettingsBar.svelte new file mode 100644 index 0000000000..76cdd8fe8b --- /dev/null +++ b/packages/client/src/components/preview/SettingsBar.svelte @@ -0,0 +1,131 @@ + + +{#if showBar} +
+ {#each settings as setting, idx} + {#if setting.type === "select"} + {#each setting.options as option} + + {/each} + {/if} + {#if idx < settings.length - 1} +
+ {/if} + {/each} +
+{/if} + + diff --git a/packages/client/src/components/preview/SettingsButton.svelte b/packages/client/src/components/preview/SettingsButton.svelte new file mode 100644 index 0000000000..35cc8aa14c --- /dev/null +++ b/packages/client/src/components/preview/SettingsButton.svelte @@ -0,0 +1,50 @@ + + +
{ + if (prop) { + const newValue = bool ? !currentValue : value + builderStore.actions.updateProp(prop, newValue) + } + }} +> + +
+ + diff --git a/packages/client/src/store/builder.js b/packages/client/src/store/builder.js index 295bb6ccc9..57b6b289f0 100644 --- a/packages/client/src/store/builder.js +++ b/packages/client/src/store/builder.js @@ -1,4 +1,32 @@ -import { writable } from "svelte/store" +import { writable, derived } from "svelte/store" +import Manifest from "@budibase/standard-components/manifest.json" + +const dispatchEvent = (type, data) => { + window.dispatchEvent( + new CustomEvent("bb-event", { + detail: { type, data }, + }) + ) +} + +const findComponentById = (component, componentId) => { + if (!component || !componentId) { + return null + } + if (component._id === componentId) { + return component + } + if (!component._children?.length) { + return null + } + for (let child of component._children) { + const result = findComponentById(child, componentId) + if (result) { + return result + } + } + return null +} const createBuilderStore = () => { const initialState = { @@ -10,18 +38,35 @@ const createBuilderStore = () => { previewId: null, previewType: null, } - const store = writable(initialState) + const writableStore = writable(initialState) + const derivedStore = derived(writableStore, $state => { + // Derive the selected component instance and definition + const { layout, screen, previewType, selectedComponentId } = $state + const asset = previewType === "layout" ? layout : screen + const component = findComponentById(asset?.props, selectedComponentId) + const prefix = "@budibase/standard-components/" + const type = component?._component?.replace(prefix, "") + const definition = type ? Manifest[type] : null + return { + ...$state, + selectedComponent: component, + selectedComponentDefinition: definition, + } + }) + const actions = { selectComponent: id => { if (id) { - window.dispatchEvent( - new CustomEvent("bb-select-component", { detail: id }) - ) + dispatchEvent("select-component", { id }) } }, + updateProp: (prop, value) => { + dispatchEvent("update-prop", { prop, value }) + }, } return { - ...store, + ...writableStore, + subscribe: derivedStore.subscribe, actions, } } diff --git a/packages/client/src/utils/domDebounce.js b/packages/client/src/utils/domDebounce.js new file mode 100644 index 0000000000..b7fc017247 --- /dev/null +++ b/packages/client/src/utils/domDebounce.js @@ -0,0 +1,12 @@ +export const domDebounce = callback => { + let active = false + return e => { + if (!active) { + window.requestAnimationFrame(() => { + callback(e) + active = false + }) + active = true + } + } +} diff --git a/packages/client/src/utils/styleable.js b/packages/client/src/utils/styleable.js index 9a89f1d273..27589b2bba 100644 --- a/packages/client/src/utils/styleable.js +++ b/packages/client/src/utils/styleable.js @@ -14,25 +14,6 @@ const buildStyleString = (styleObject, customStyles) => { return str + (customStyles || "") } -/** - * Applies styles to enrich the builder preview. - * Applies styles to highlight the selected component, and allows pointer - * events for any selectable components (overriding the blanket ban on pointer - * events in the iframe HTML). - */ -const addBuilderPreviewStyles = (node, styleString, componentId) => { - if (componentId === get(builderStore).selectedComponentId) { - const style = window.getComputedStyle(node) - const property = style?.display === "table-row" ? "outline" : "border" - return ( - styleString + - `;${property}: 2px solid #4285f4 !important; border-radius: 4px !important;` - ) - } else { - return styleString - } -} - /** * Svelte action to apply correct component styles. * This also applies handlers for selecting components from the builder preview. @@ -44,9 +25,17 @@ export const styleable = (node, styles = {}) => { // Creates event listeners and applies initial styles const setupStyles = (newStyles = {}) => { + // Use empty state styles as base styles if required, but let them, get + // overridden by any user specified styles + let baseStyles = {} + if (newStyles.empty) { + baseStyles.border = "2px dashed var(--grey-5)" + baseStyles.padding = "var(--spacing-l)" + } + const componentId = newStyles.id const customStyles = newStyles.custom || "" - const normalStyles = newStyles.normal || {} + const normalStyles = { ...baseStyles, ...newStyles.normal } const hoverStyles = { ...normalStyles, ...(newStyles.hover || {}), @@ -54,7 +43,7 @@ export const styleable = (node, styles = {}) => { // Applies a style string to a DOM node const applyStyles = styleString => { - node.style = addBuilderPreviewStyles(node, styleString, componentId) + node.style = styleString node.dataset.componentId = componentId } @@ -71,7 +60,9 @@ export const styleable = (node, styles = {}) => { // Handler to select a component in the builder when clicking it in the // builder preview selectComponent = event => { - builderStore.actions.selectComponent(componentId) + if (newStyles.interactive) { + builderStore.actions.selectComponent(componentId) + } event.preventDefault() event.stopPropagation() return false diff --git a/packages/client/yarn.lock b/packages/client/yarn.lock index c8d5f4f17b..bec1dd83cb 100644 --- a/packages/client/yarn.lock +++ b/packages/client/yarn.lock @@ -23,6 +23,113 @@ chalk "^2.0.0" js-tokens "^4.0.0" +<<<<<<< HEAD +======= +"@budibase/bbui@^0.9.46": + version "0.9.46" + resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-0.9.46.tgz#3109666b618daa65b29d1c7c45549420c62e6489" + integrity sha512-PRW8kR9+QrMiom6hVzisMYd268dj03ojC0ruzEkDhKMONg2I021ST62hzKXdb7zh5LgoYXtapmM9qsKwoHfkPg== + dependencies: + "@adobe/spectrum-css-workflow-icons" "^1.2.1" + "@spectrum-css/actionbutton" "^1.0.1" + "@spectrum-css/actiongroup" "^1.0.1" + "@spectrum-css/avatar" "^3.0.2" + "@spectrum-css/button" "^3.0.1" + "@spectrum-css/buttongroup" "^3.0.2" + "@spectrum-css/checkbox" "^3.0.2" + "@spectrum-css/dialog" "^3.0.1" + "@spectrum-css/divider" "^1.0.1" + "@spectrum-css/dropzone" "^3.0.2" + "@spectrum-css/fieldgroup" "^3.0.2" + "@spectrum-css/fieldlabel" "^3.0.1" + "@spectrum-css/icon" "^3.0.1" + "@spectrum-css/illustratedmessage" "^3.0.2" + "@spectrum-css/inputgroup" "^3.0.2" + "@spectrum-css/label" "^2.0.10" + "@spectrum-css/link" "^3.1.1" + "@spectrum-css/menu" "^3.0.1" + "@spectrum-css/modal" "^3.0.1" + "@spectrum-css/pagination" "^3.0.3" + "@spectrum-css/picker" "^1.0.1" + "@spectrum-css/popover" "^3.0.1" + "@spectrum-css/progressbar" "^1.0.2" + "@spectrum-css/progresscircle" "^1.0.2" + "@spectrum-css/radio" "^3.0.2" + "@spectrum-css/search" "^3.0.2" + "@spectrum-css/sidenav" "^3.0.2" + "@spectrum-css/statuslight" "^3.0.2" + "@spectrum-css/switch" "^1.0.2" + "@spectrum-css/table" "^3.0.1" + "@spectrum-css/tabs" "^3.0.1" + "@spectrum-css/tags" "^3.0.2" + "@spectrum-css/textfield" "^3.0.1" + "@spectrum-css/toast" "^3.0.1" + "@spectrum-css/tooltip" "^3.0.3" + "@spectrum-css/treeview" "^3.0.2" + "@spectrum-css/typography" "^3.0.1" + "@spectrum-css/underlay" "^2.0.9" + "@spectrum-css/vars" "^3.0.1" + dayjs "^1.10.4" + svelte-flatpickr "^3.1.0" + svelte-portal "^1.0.0" + +"@budibase/handlebars-helpers@^0.11.4": + version "0.11.4" + resolved "https://registry.yarnpkg.com/@budibase/handlebars-helpers/-/handlebars-helpers-0.11.4.tgz#8acfa2ee84134f7be4b2906e710fce6d25c5d000" + integrity sha512-AJNJYlJnxZmn9QJ8tBl8nrm4YxbwHP4AR0pbiVGK+EoOylkNBlUnZ/QDL1VyqM5fTkAE/Z2IZVLKrrG3kxuWLA== + dependencies: + arr-flatten "^1.1.0" + array-sort "^0.1.4" + define-property "^1.0.0" + extend-shallow "^3.0.2" + "falsey" "^0.3.2" + for-in "^1.0.2" + for-own "^1.0.0" + get-object "^0.2.0" + get-value "^2.0.6" + handlebars "^4.0.11" + handlebars-utils "^1.0.6" + has-value "^1.0.0" + helper-date "^1.0.1" + helper-markdown "^1.0.0" + helper-md "^0.2.2" + html-tag "^2.0.0" + is-even "^1.0.0" + is-glob "^4.0.0" + is-number "^4.0.0" + kind-of "^6.0.0" + logging-helpers "^1.0.0" + micromatch "^3.1.4" + relative "^3.0.2" + striptags "^3.1.0" + to-gfm-code-block "^0.1.1" + year "^0.2.1" + +"@budibase/standard-components@^0.9.46": + version "0.9.46" + resolved "https://registry.yarnpkg.com/@budibase/standard-components/-/standard-components-0.9.46.tgz#a31a253ca51a2029c3aaf5d8aca5c953358e1d67" + integrity sha512-QjW4tukMw4Xa477wGTle2UPz85ygodQ3KG+WEdPAWKq7j0IDv0Fad0oDmWtzLvGxxB+AiRbEnM6T1QV6X1ItCA== + dependencies: + "@budibase/bbui" "^0.9.46" + "@spectrum-css/page" "^3.0.1" + "@spectrum-css/vars" "^3.0.1" + apexcharts "^3.22.1" + dayjs "^1.10.5" + svelte-apexcharts "^1.0.2" + svelte-flatpickr "^3.1.0" + +"@budibase/string-templates@^0.9.46": + version "0.9.46" + resolved "https://registry.yarnpkg.com/@budibase/string-templates/-/string-templates-0.9.46.tgz#e43f87513977879a892ae52f3941d3320cb9ff88" + integrity sha512-yOVS7Y/QLATj31QuBu8KP78Oyzhs60V09JEQKa7n4vRP8TBemcev/LFShln8Iiv70YZSpdJviguQuJ6Ow0aUNA== + dependencies: + "@budibase/handlebars-helpers" "^0.11.4" + dayjs "^1.10.4" + handlebars "^4.7.6" + handlebars-utils "^1.0.6" + lodash "^4.17.20" + +>>>>>>> 81e794065761192883767a80679d7d94d67afcc2 "@rollup/plugin-commonjs@^18.0.0": version "18.0.0" resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-18.0.0.tgz#50dc7518b5aa9e66a270e529ea85115d269825c4" diff --git a/packages/server/package.json b/packages/server/package.json index 2189d0ed34..385dff9b27 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/server", "email": "hi@budibase.com", - "version": "0.9.45", + "version": "0.9.48", "description": "Budibase Web Server", "main": "src/electron.js", "repository": { @@ -55,9 +55,9 @@ "author": "Budibase", "license": "AGPL-3.0-or-later", "dependencies": { - "@budibase/auth": "^0.9.45", - "@budibase/client": "^0.9.45", - "@budibase/string-templates": "^0.9.45", + "@budibase/auth": "^0.9.48", + "@budibase/client": "^0.9.48", + "@budibase/string-templates": "^0.9.48", "@elastic/elasticsearch": "7.10.0", "@koa/router": "8.0.0", "@sendgrid/mail": "7.1.1", @@ -110,7 +110,7 @@ "devDependencies": { "@babel/core": "^7.14.3", "@babel/preset-env": "^7.14.4", - "@budibase/standard-components": "^0.9.45", + "@budibase/standard-components": "^0.9.48", "@jest/test-sequencer": "^24.8.0", "babel-jest": "^27.0.2", "docker-compose": "^0.23.6", diff --git a/packages/server/src/api/controllers/user.js b/packages/server/src/api/controllers/user.js index a7dcc190ba..6778f983c2 100644 --- a/packages/server/src/api/controllers/user.js +++ b/packages/server/src/api/controllers/user.js @@ -4,7 +4,6 @@ const { getUserMetadataParams, } = require("../../db/utils") const { InternalTables } = require("../../db/utils") -const { addAppRoleToUser } = require("../../utilities/workerRequests") const { getGlobalUsers } = require("../../utilities/global") const { getFullUser } = require("../../utilities/users") @@ -53,9 +52,6 @@ exports.updateMetadata = async function (ctx) { const appId = ctx.appId const db = new CouchDB(appId) const user = removeGlobalProps(ctx.request.body) - if (user.roleId) { - await addAppRoleToUser(ctx, appId, user.roleId, user._id) - } const metadata = { tableId: InternalTables.USER_METADATA, ...user, diff --git a/packages/server/src/constants/layouts.js b/packages/server/src/constants/layouts.js index a80170f327..3d8c6d08c4 100644 --- a/packages/server/src/constants/layouts.js +++ b/packages/server/src/constants/layouts.js @@ -23,9 +23,8 @@ const EMPTY_LAYOUT = { "justify-content": "flex-start", "align-items": "stretch", "max-width": "100%", - "margin-left": "20px", - "margin-right": "20px", width: "1400px", + padding: "20px", }, hover: {}, active: {}, @@ -34,24 +33,19 @@ const EMPTY_LAYOUT = { _children: [], }, ], - type: "div", _styles: { active: {}, hover: {}, normal: { - display: "flex", - "flex-direction": "column", - "align-items": "center", - "justify-content": "flex-start", - "margin-right": "auto", - "margin-left": "auto", "min-height": "100%", "background-image": "#f5f5f5", }, selected: {}, }, - className: "", - onLoad: [], + direction: "column", + hAlign: "center", + vAlign: "top", + size: "grow", }, } @@ -72,10 +66,6 @@ const BASE_LAYOUTS = [ _component: "@budibase/standard-components/container", _styles: { normal: { - display: "flex", - "flex-direction": "row", - "justify-content": "flex-start", - "align-items": "flex-start", background: "#fff", width: "100%", "box-shadow": "0 1px 2px 0 rgba(0, 0, 0, 0.05)", @@ -84,9 +74,6 @@ const BASE_LAYOUTS = [ active: {}, selected: {}, }, - className: "", - onLoad: [], - type: "div", _instanceName: "Header", _children: [ { @@ -95,8 +82,6 @@ const BASE_LAYOUTS = [ _styles: { normal: { "max-width": "1400px", - "margin-left": "auto", - "margin-right": "auto", padding: "20px", "font-weight": "400", "font-size": "16px", @@ -143,6 +128,10 @@ const BASE_LAYOUTS = [ ], }, ], + direction: "row", + hAlign: "center", + vAlign: "middle", + size: "shrink", }, { _id: "7fcf11e4-6f5b-4085-8e0d-9f3d44c98967", @@ -155,8 +144,6 @@ const BASE_LAYOUTS = [ "justify-content": "flex-start", "align-items": "stretch", "max-width": "100%", - "margin-left": "20px", - "margin-right": "20px", width: "1400px", padding: "20px", }, @@ -167,24 +154,19 @@ const BASE_LAYOUTS = [ _children: [], }, ], - type: "div", _styles: { active: {}, hover: {}, normal: { - display: "flex", - "flex-direction": "column", - "align-items": "center", - "justify-content": "flex-start", - "margin-right": "auto", - "margin-left": "auto", "min-height": "100%", background: "#f5f5f5", }, selected: {}, }, - className: "", - onLoad: [], + direction: "column", + hAlign: "center", + vAlign: "top", + size: "grow", }, }, { @@ -209,8 +191,6 @@ const BASE_LAYOUTS = [ "justify-content": "flex-start", "align-items": "stretch", "max-width": "100%", - "margin-left": "20px", - "margin-right": "20px", width: "1400px", padding: "20px", }, @@ -221,24 +201,19 @@ const BASE_LAYOUTS = [ _children: [], }, ], - type: "div", _styles: { active: {}, hover: {}, normal: { - display: "flex", - "flex-direction": "column", - "align-items": "center", - "justify-content": "center", - "margin-right": "auto", - "margin-left": "auto", "min-height": "100%", background: "#f5f5f5", }, selected: {}, }, - className: "", - onLoad: [], + direction: "column", + hAlign: "center", + vAlign: "top", + size: "grow", }, }, ] diff --git a/packages/server/src/constants/screens.js b/packages/server/src/constants/screens.js index eb9dc8f702..8e3bda5cb8 100644 --- a/packages/server/src/constants/screens.js +++ b/packages/server/src/constants/screens.js @@ -9,19 +9,12 @@ exports.createHomeScreen = () => ({ _id: "d834fea2-1b3e-4320-ab34-f9009f5ecc59", _component: "@budibase/standard-components/container", _styles: { - normal: { - flex: "1 1 auto", - display: "flex", - "flex-direction": "column", - "justify-content": "flex-start", - "align-items": "stretch", - }, + normal: {}, hover: {}, active: {}, selected: {}, }, _transition: "fade", - type: "div", _children: [ { _id: "ef60083f-4a02-4df3-80f3-a0d3d16847e7", @@ -41,6 +34,10 @@ exports.createHomeScreen = () => ({ }, ], _instanceName: "Home", + direction: "column", + hAlign: "stretch", + vAlign: "top", + size: "grow", }, routing: { route: "/", diff --git a/packages/server/src/utilities/global.js b/packages/server/src/utilities/global.js index eda0e61cff..17ce066551 100644 --- a/packages/server/src/utilities/global.js +++ b/packages/server/src/utilities/global.js @@ -12,14 +12,14 @@ exports.updateAppRole = (appId, user) => { if (!user.roles) { return user } - if (user.builder && user.builder.global) { + + // always use the deployed app + user.roleId = user.roles[getDeployedAppID(appId)] + // if a role wasn't found then either set as admin (builder) or public (everyone else) + if (!user.roleId && user.builder && user.builder.global) { user.roleId = BUILTIN_ROLE_IDS.ADMIN - } else { - // always use the deployed app - user.roleId = user.roles[getDeployedAppID(appId)] - if (!user.roleId) { - user.roleId = BUILTIN_ROLE_IDS.PUBLIC - } + } else if (!user.roleId) { + user.roleId = BUILTIN_ROLE_IDS.PUBLIC } delete user.roles return user diff --git a/packages/standard-components/manifest.json b/packages/standard-components/manifest.json index 81aa63e1e3..24f910872b 100644 --- a/packages/standard-components/manifest.json +++ b/packages/standard-components/manifest.json @@ -6,7 +6,117 @@ "hasChildren": true, "styleable": true, "transitionable": true, - "settings": [] + "showSettingsBar": true, + "settings": [ + { + "type": "select", + "label": "Direction", + "key": "direction", + "showInBar": true, + "options": [ + { + "label": "Column", + "value": "column", + "barIcon": "ViewRow", + "barTitle": "Column layout" + }, + { + "label": "Row", + "value": "row", + "barIcon": "ViewColumn", + "barTitle": "Row layout" + } + ], + "defaultValue": "column" + }, + { + "type": "select", + "label": "Horiz. Align", + "key": "hAlign", + "showInBar": true, + "options": [ + { + "label": "Left", + "value": "left", + "barIcon": "AlignLeft", + "barTitle": "Align left" + }, + { + "label": "Center", + "value": "center", + "barIcon": "AlignCenter", + "barTitle": "Align center" + }, + { + "label": "Right", + "value": "right", + "barIcon": "AlignRight", + "barTitle": "Align right" + }, + { + "label": "Stretch", + "value": "stretch", + "barIcon": "MoveLeftRight", + "barTitle": "Align stretched horizontally" + } + ], + "defaultValue": "stretch" + }, + { + "type": "select", + "label": "Vert. Align", + "key": "vAlign", + "showInBar": "true", + "options": [ + { + "label": "Top", + "value": "top", + "barIcon": "AlignTop", + "barTitle": "Align top" + }, + { + "label": "Middle", + "value": "middle", + "barIcon": "AlignMiddle", + "barTitle": "Align middle" + }, + { + "label": "Bottom", + "value": "bottom", + "barIcon": "AlignBottom", + "barTitle": "Align bottom" + }, + { + "label": "Stretch", + "value": "stretch", + "barIcon": "MoveUpDown", + "barTitle": "Align stretched vertically" + } + ], + "defaultValue": "top" + }, + { + "type": "select", + "label": "Size", + "key": "size", + "showInBar": true, + "options": [ + { + "label": "Shrink", + "value": "shrink", + "barIcon": "Minimize", + "barTitle": "Shrink container" + }, + { + "label": "Grow", + "value": "grow", + "barIcon": "Maximize", + "barTitle": "Grow container" + } + ], + "defaultValue": "shrink" + } + ] }, "screenslot": { "name": "Screenslot", @@ -53,7 +163,7 @@ "type": "text", "label": "Empty Text", "key": "noRowsMessage", - "defaultValue": "No rows found." + "defaultValue": "No rows found" }, { "type": "filter", @@ -1438,6 +1548,7 @@ "icon": "Table", "styleable": true, "hasChildren": true, + "showEmptyState": false, "settings": [ { "type": "dataProvider", diff --git a/packages/standard-components/package.json b/packages/standard-components/package.json index ffa399d075..75660eb68f 100644 --- a/packages/standard-components/package.json +++ b/packages/standard-components/package.json @@ -29,11 +29,11 @@ "keywords": [ "svelte" ], - "version": "0.9.45", + "version": "0.9.48", "license": "MIT", "gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc", "dependencies": { - "@budibase/bbui": "^0.9.45", + "@budibase/bbui": "^0.9.48", "@spectrum-css/page": "^3.0.1", "@spectrum-css/vars": "^3.0.1", "apexcharts": "^3.22.1", diff --git a/packages/standard-components/src/Container.svelte b/packages/standard-components/src/Container.svelte index 03870cb79f..cb4a982643 100644 --- a/packages/standard-components/src/Container.svelte +++ b/packages/standard-components/src/Container.svelte @@ -1,13 +1,87 @@
+ + diff --git a/packages/standard-components/src/DataProvider.svelte b/packages/standard-components/src/DataProvider.svelte index 7b759b5348..bce66af7a8 100644 --- a/packages/standard-components/src/DataProvider.svelte +++ b/packages/standard-components/src/DataProvider.svelte @@ -7,6 +7,7 @@ luceneSort, luceneLimit, } from "./lucene" + import Placeholder from "./Placeholder.svelte" export let dataSource export let filter @@ -22,7 +23,8 @@ let loading = false // Loading flag for the initial load - let loaded = false + // Mark as loaded if we have no datasource so we don't stall forever + let loaded = !dataSource let schemaLoaded = false // Provider state @@ -87,6 +89,7 @@ // bindings, but are used internally by other components id: $component?.id, state: { query }, + loaded, } const getSortType = (schema, sortColumn) => { @@ -231,7 +234,11 @@
{:else} - + {#if !$component.children} + + {:else} + + {/if} {#if paginate && internalTable}