1
0
Fork 0
mirror of synced 2024-08-31 09:41:13 +12:00

Merge branch 'develop' of github.com:Budibase/budibase into spreadsheet-integration

This commit is contained in:
Andrew Kingston 2023-03-29 12:09:57 +01:00
commit 31f6f9db7b
48 changed files with 862 additions and 572 deletions

View file

@ -1,5 +1,5 @@
{ {
"version": "2.4.27-alpha.9", "version": "2.4.27-alpha.12",
"npmClient": "yarn", "npmClient": "yarn",
"packages": [ "packages": [
"packages/*" "packages/*"

View file

@ -1,6 +1,6 @@
{ {
"name": "@budibase/backend-core", "name": "@budibase/backend-core",
"version": "2.4.27-alpha.9", "version": "2.4.27-alpha.12",
"description": "Budibase backend core libraries used in server and worker", "description": "Budibase backend core libraries used in server and worker",
"main": "dist/src/index.js", "main": "dist/src/index.js",
"types": "dist/src/index.d.ts", "types": "dist/src/index.d.ts",
@ -24,7 +24,7 @@
"dependencies": { "dependencies": {
"@budibase/nano": "10.1.2", "@budibase/nano": "10.1.2",
"@budibase/pouchdb-replication-stream": "1.2.10", "@budibase/pouchdb-replication-stream": "1.2.10",
"@budibase/types": "2.4.27-alpha.9", "@budibase/types": "2.4.27-alpha.12",
"@shopify/jest-koa-mocks": "5.0.1", "@shopify/jest-koa-mocks": "5.0.1",
"@techpass/passport-openidconnect": "0.3.2", "@techpass/passport-openidconnect": "0.3.2",
"aws-cloudfront-sign": "2.2.0", "aws-cloudfront-sign": "2.2.0",

View file

@ -1,7 +1,7 @@
{ {
"name": "@budibase/bbui", "name": "@budibase/bbui",
"description": "A UI solution used in the different Budibase projects.", "description": "A UI solution used in the different Budibase projects.",
"version": "2.4.27-alpha.9", "version": "2.4.27-alpha.12",
"license": "MPL-2.0", "license": "MPL-2.0",
"svelte": "src/index.js", "svelte": "src/index.js",
"module": "dist/bbui.es.js", "module": "dist/bbui.es.js",
@ -38,8 +38,8 @@
], ],
"dependencies": { "dependencies": {
"@adobe/spectrum-css-workflow-icons": "1.2.1", "@adobe/spectrum-css-workflow-icons": "1.2.1",
"@budibase/shared-core": "2.4.27-alpha.9", "@budibase/shared-core": "2.4.27-alpha.12",
"@budibase/string-templates": "2.4.27-alpha.9", "@budibase/string-templates": "2.4.27-alpha.12",
"@spectrum-css/accordion": "3.0.24", "@spectrum-css/accordion": "3.0.24",
"@spectrum-css/actionbutton": "1.0.1", "@spectrum-css/actionbutton": "1.0.1",
"@spectrum-css/actiongroup": "1.0.1", "@spectrum-css/actiongroup": "1.0.1",

View file

@ -7,7 +7,7 @@
export let title export let title
export let fillWidth export let fillWidth
export let left = "314px" export let left = "314px"
export let width = "calc(100% - 576px)" export let width = "calc(100% - 626px)"
let visible = false let visible = false

View file

@ -42,11 +42,12 @@
} }
const getFieldText = (value, options, placeholder) => { const getFieldText = (value, options, placeholder) => {
// Always use placeholder if no value
if (value == null || value === "") { if (value == null || value === "") {
// Explicit false means use no placeholder and allow an empty fields
if (placeholder === false) { if (placeholder === false) {
return "" return ""
} }
// Otherwise we use the placeholder if possible
return placeholder || "Choose an option" return placeholder || "Choose an option"
} }

View file

@ -1,6 +1,6 @@
{ {
"name": "@budibase/builder", "name": "@budibase/builder",
"version": "2.4.27-alpha.9", "version": "2.4.27-alpha.12",
"license": "GPL-3.0", "license": "GPL-3.0",
"private": true, "private": true,
"scripts": { "scripts": {
@ -58,11 +58,11 @@
} }
}, },
"dependencies": { "dependencies": {
"@budibase/bbui": "2.4.27-alpha.9", "@budibase/bbui": "2.4.27-alpha.12",
"@budibase/client": "2.4.27-alpha.9", "@budibase/client": "2.4.27-alpha.12",
"@budibase/frontend-core": "2.4.27-alpha.9", "@budibase/frontend-core": "2.4.27-alpha.12",
"@budibase/shared-core": "2.4.27-alpha.9", "@budibase/shared-core": "2.4.27-alpha.12",
"@budibase/string-templates": "2.4.27-alpha.9", "@budibase/string-templates": "2.4.27-alpha.12",
"@fortawesome/fontawesome-svg-core": "^6.2.1", "@fortawesome/fontawesome-svg-core": "^6.2.1",
"@fortawesome/free-brands-svg-icons": "^6.2.1", "@fortawesome/free-brands-svg-icons": "^6.2.1",
"@fortawesome/free-solid-svg-icons": "^6.2.1", "@fortawesome/free-solid-svg-icons": "^6.2.1",

View file

@ -22,6 +22,7 @@ import {
findComponent, findComponent,
getComponentSettings, getComponentSettings,
makeComponentUnique, makeComponentUnique,
findComponentPath,
} from "../componentUtils" } from "../componentUtils"
import { Helpers } from "@budibase/bbui" import { Helpers } from "@budibase/bbui"
import { Utils } from "@budibase/frontend-core" import { Utils } from "@budibase/frontend-core"
@ -30,7 +31,12 @@ import {
DB_TYPE_INTERNAL, DB_TYPE_INTERNAL,
DB_TYPE_EXTERNAL, DB_TYPE_EXTERNAL,
} from "constants/backend" } from "constants/backend"
import { getSchemaForDatasource } from "builderStore/dataBinding" import {
buildFormSchema,
getSchemaForDatasource,
} from "builderStore/dataBinding"
import { makePropSafe as safe } from "@budibase/string-templates"
import { getComponentFieldOptions } from "helpers/formFields"
const INITIAL_FRONTEND_STATE = { const INITIAL_FRONTEND_STATE = {
apps: [], apps: [],
@ -63,17 +69,19 @@ const INITIAL_FRONTEND_STATE = {
customTheme: {}, customTheme: {},
previewDevice: "desktop", previewDevice: "desktop",
highlightedSettingKey: null, highlightedSettingKey: null,
builderSidePanel: false,
// URL params // URL params
selectedScreenId: null, selectedScreenId: null,
selectedComponentId: null, selectedComponentId: null,
selectedLayoutId: null, selectedLayoutId: null,
// onboarding // Client state
selectedComponentInstance: null,
// Onboarding
onboarding: false, onboarding: false,
tourNodes: null, tourNodes: null,
builderSidePanel: false,
} }
export const getFrontendStore = () => { export const getFrontendStore = () => {
@ -262,22 +270,27 @@ export const getFrontendStore = () => {
} }
}, },
save: async screen => { save: async screen => {
/* // Validate screen structure
Temporarily disabled to accomodate migration issues. // Temporarily disabled to accommodate migration issues
store.actions.screens.validate(screen) // store.actions.screens.validate(screen)
*/
const state = get(store) // Check screen definition for any component settings which need updated
store.actions.screens.enrichEmptySettings(screen)
// Save screen
const creatingNewScreen = screen._id === undefined const creatingNewScreen = screen._id === undefined
const savedScreen = await API.saveScreen(screen) const savedScreen = await API.saveScreen(screen)
const routesResponse = await API.fetchAppRoutes() const routesResponse = await API.fetchAppRoutes()
let usedPlugins = state.usedPlugins
// If plugins changed we need to fetch the latest app metadata // If plugins changed we need to fetch the latest app metadata
const state = get(store)
let usedPlugins = state.usedPlugins
if (savedScreen.pluginAdded) { if (savedScreen.pluginAdded) {
const { application } = await API.fetchAppPackage(state.appId) const { application } = await API.fetchAppPackage(state.appId)
usedPlugins = application.usedPlugins || [] usedPlugins = application.usedPlugins || []
} }
// Update state
store.update(state => { store.update(state => {
// Update screen object // Update screen object
const idx = state.screens.findIndex(x => x._id === savedScreen._id) const idx = state.screens.findIndex(x => x._id === savedScreen._id)
@ -298,7 +311,6 @@ export const getFrontendStore = () => {
// Update used plugins // Update used plugins
state.usedPlugins = usedPlugins state.usedPlugins = usedPlugins
return state return state
}) })
return savedScreen return savedScreen
@ -406,6 +418,17 @@ export const getFrontendStore = () => {
} }
await store.actions.screens.patch(patch, screen._id) await store.actions.screens.patch(patch, screen._id)
}, },
enrichEmptySettings: screen => {
// Flatten the recursive component tree
const components = findAllMatchingComponents(screen.props, x => x)
// Iterate over all components and run checks
components.forEach(component => {
store.actions.components.enrichEmptySettings(component, {
screen,
})
})
},
}, },
preview: { preview: {
setDevice: device => { setDevice: device => {
@ -493,65 +516,155 @@ export const getFrontendStore = () => {
} }
return get(store).components[componentName] return get(store).components[componentName]
}, },
createInstance: (componentName, presetProps) => { getDefaultDatasource: () => {
// Ignore users table
const validTables = get(tables).list.filter(x => x._id !== "ta_users")
// Try to use their own internal table first
let table = validTables.find(table => {
return (
table.sourceId !== BUDIBASE_INTERNAL_DB_ID &&
table.type === DB_TYPE_INTERNAL
)
})
if (table) {
return table
}
// Then try sample data
table = validTables.find(table => {
return (
table.sourceId === BUDIBASE_INTERNAL_DB_ID &&
table.type === DB_TYPE_INTERNAL
)
})
if (table) {
return table
}
// Finally try an external table
return validTables.find(table => table.type === DB_TYPE_EXTERNAL)
},
enrichEmptySettings: (component, opts) => {
if (!component?._component) {
return
}
const defaultDS = store.actions.components.getDefaultDatasource()
const settings = getComponentSettings(component._component)
const { parent, screen, useDefaultValues } = opts || {}
const treeId = parent?._id || component._id
if (!screen) {
return
}
settings.forEach(setting => {
const value = component[setting.key]
// Fill empty settings
if (value == null || value === "") {
if (setting.type === "multifield" && setting.selectAllFields) {
// Select all schema fields where required
component[setting.key] = Object.keys(defaultDS?.schema || {})
} else if (
(setting.type === "dataSource" || setting.type === "table") &&
defaultDS
) {
// Select default datasource where required
component[setting.key] = {
label: defaultDS.name,
tableId: defaultDS._id,
type: "table",
}
} else if (setting.type === "dataProvider") {
// Pick closest data provider where required
const path = findComponentPath(screen.props, treeId)
const providers = path.filter(component =>
component._component?.endsWith("/dataprovider")
)
if (providers.length) {
const id = providers[providers.length - 1]?._id
component[setting.key] = `{{ literal ${safe(id)} }}`
}
} else if (setting.type.startsWith("field/")) {
// Autofill form field names
// Get all available field names in this form schema
let fieldOptions = getComponentFieldOptions(
screen.props,
treeId,
setting.type,
false
)
// Get all currently used fields
const form = findClosestMatchingComponent(
screen.props,
treeId,
x => x._component === "@budibase/standard-components/form"
)
const usedFields = Object.keys(buildFormSchema(form) || {})
// Filter out already used fields
fieldOptions = fieldOptions.filter(x => !usedFields.includes(x))
// Set field name and also assume we have a label setting
if (fieldOptions[0]) {
component[setting.key] = fieldOptions[0]
component.label = fieldOptions[0]
}
} else if (useDefaultValues && setting.defaultValue !== undefined) {
// Use default value where required
component[setting.key] = setting.defaultValue
}
}
// Validate non-empty settings
else {
if (setting.type === "dataProvider") {
// Validate data provider exists, or else clear it
const treeId = parent?._id || component._id
const path = findComponentPath(screen?.props, treeId)
const providers = path.filter(component =>
component._component?.endsWith("/dataprovider")
)
// Validate non-empty values
const valid = providers?.some(dp => value.includes?.(dp._id))
if (!valid) {
if (providers.length) {
const id = providers[providers.length - 1]?._id
component[setting.key] = `{{ literal ${safe(id)} }}`
} else {
delete component[setting.key]
}
}
}
}
})
},
createInstance: (componentName, presetProps, parent) => {
const definition = store.actions.components.getDefinition(componentName) const definition = store.actions.components.getDefinition(componentName)
if (!definition) { if (!definition) {
return null return null
} }
// Flattened settings // Generate basic component structure
const settings = getComponentSettings(componentName) let instance = {
_id: Helpers.uuid(),
let dataSourceField = settings.find( _component: definition.component,
setting => setting.type == "dataSource" || setting.type == "table" _styles: {
) normal: {},
hover: {},
let defaultDatasource active: {},
if (dataSourceField) { },
const _tables = get(tables) _instanceName: `New ${definition.friendlyName || definition.name}`,
const filteredTables = _tables.list.filter( ...presetProps,
table => table._id != "ta_users"
)
const internalTable = filteredTables.find(
table =>
table.sourceId === BUDIBASE_INTERNAL_DB_ID &&
table.type == DB_TYPE_INTERNAL
)
const defaultSourceTable = filteredTables.find(
table =>
table.sourceId !== BUDIBASE_INTERNAL_DB_ID &&
table.type == DB_TYPE_INTERNAL
)
const defaultExternalTable = filteredTables.find(
table => table.type == DB_TYPE_EXTERNAL
)
defaultDatasource =
defaultSourceTable || internalTable || defaultExternalTable
} }
// Generate default props // Enrich empty settings
let props = { ...presetProps } store.actions.components.enrichEmptySettings(instance, {
settings.forEach(setting => { parent,
if (setting.type === "multifield" && setting.selectAllFields) { screen: get(selectedScreen),
props[setting.key] = Object.keys(defaultDatasource.schema || {}) useDefaultValues: true,
} else if (setting.defaultValue !== undefined) {
props[setting.key] = setting.defaultValue
}
}) })
// Set a default datasource
if (dataSourceField && defaultDatasource) {
props[dataSourceField.key] = {
label: defaultDatasource.name,
tableId: defaultDatasource._id,
type: "table",
}
}
// Add any extra properties the component needs // Add any extra properties the component needs
let extras = {} let extras = {}
if (definition.hasChildren) { if (definition.hasChildren) {
@ -569,17 +682,8 @@ export const getFrontendStore = () => {
extras.step = formSteps.length + 1 extras.step = formSteps.length + 1
extras._instanceName = `Step ${formSteps.length + 1}` extras._instanceName = `Step ${formSteps.length + 1}`
} }
return { return {
_id: Helpers.uuid(), ...cloneDeep(instance),
_component: definition.component,
_styles: {
normal: {},
hover: {},
active: {},
},
_instanceName: `New ${definition.friendlyName || definition.name}`,
...cloneDeep(props),
...extras, ...extras,
} }
}, },
@ -587,7 +691,8 @@ export const getFrontendStore = () => {
const state = get(store) const state = get(store)
const componentInstance = store.actions.components.createInstance( const componentInstance = store.actions.components.createInstance(
componentName, componentName,
presetProps presetProps,
parent
) )
if (!componentInstance) { if (!componentInstance) {
return return
@ -1123,6 +1228,52 @@ export const getFrontendStore = () => {
}) })
} }
}, },
addParent: async (componentId, parentType) => {
if (!componentId || !parentType) {
return
}
// Create new parent instance
const newParentDefinition = store.actions.components.createInstance(
parentType,
null,
parent
)
if (!newParentDefinition) {
return
}
// Replace component with a version wrapped in a new parent
await store.actions.screens.patch(screen => {
// Get this component definition and parent definition
let definition = findComponent(screen.props, componentId)
let oldParentDefinition = findComponentParent(
screen.props,
componentId
)
if (!definition || !oldParentDefinition) {
return false
}
// Replace component with parent
const index = oldParentDefinition._children.findIndex(
component => component._id === componentId
)
if (index === -1) {
return false
}
oldParentDefinition._children[index] = {
...newParentDefinition,
_children: [definition],
}
})
// Select the new parent
store.update(state => {
state.selectedComponentId = newParentDefinition._id
return state
})
},
}, },
links: { links: {
save: async (url, title) => { save: async (url, title) => {

View file

@ -3,7 +3,6 @@
export let title export let title
export let icon export let icon
export let expandable = false
export let showAddButton = false export let showAddButton = false
export let showBackButton = false export let showBackButton = false
export let showCloseButton = false export let showCloseButton = false
@ -12,8 +11,8 @@
export let onClickCloseButton export let onClickCloseButton
export let borderLeft = false export let borderLeft = false
export let borderRight = false export let borderRight = false
export let wide = false
let wide = false
$: customHeaderContent = $$slots["panel-header-content"] $: customHeaderContent = $$slots["panel-header-content"]
</script> </script>
@ -28,13 +27,6 @@
<div class="title"> <div class="title">
<Heading size="XXS">{title || ""}</Heading> <Heading size="XXS">{title || ""}</Heading>
</div> </div>
{#if expandable}
<Icon
name={wide ? "Minimize" : "Maximize"}
hoverable
on:click={() => (wide = !wide)}
/>
{/if}
{#if showAddButton} {#if showAddButton}
<div class="add-button" on:click={onClickAddButton}> <div class="add-button" on:click={onClickAddButton}>
<Icon name="Add" /> <Icon name="Add" />
@ -74,8 +66,8 @@
border-right: var(--border-light); border-right: var(--border-light);
} }
.panel.wide { .panel.wide {
width: 420px; width: 310px;
flex: 0 0 420px; flex: 0 0 310px;
} }
.header { .header {
flex: 0 0 48px; flex: 0 0 48px;

View file

@ -27,7 +27,7 @@
: enrichedSchemaFields?.map(field => field.name) : enrichedSchemaFields?.map(field => field.name)
$: sanitisedValue = getValidColumns(value, options) $: sanitisedValue = getValidColumns(value, options)
$: updateBoundValue(sanitisedValue) $: updateBoundValue(sanitisedValue)
$: enrichedSchemaFields = getFields(Object.values(schema) || [], { $: enrichedSchemaFields = getFields(Object.values(schema || {}), {
allowLinks: true, allowLinks: true,
}) })

View file

@ -3,23 +3,13 @@
import { makePropSafe } from "@budibase/string-templates" import { makePropSafe } from "@budibase/string-templates"
import { currentAsset, store } from "builderStore" import { currentAsset, store } from "builderStore"
import { findComponentPath } from "builderStore/componentUtils" import { findComponentPath } from "builderStore/componentUtils"
import { createEventDispatcher, onMount } from "svelte"
export let value export let value
const dispatch = createEventDispatcher()
const getValue = component => `{{ literal ${makePropSafe(component._id)} }}` const getValue = component => `{{ literal ${makePropSafe(component._id)} }}`
$: path = findComponentPath($currentAsset?.props, $store.selectedComponentId) $: path = findComponentPath($currentAsset?.props, $store.selectedComponentId)
$: providers = path.filter(c => c._component?.endsWith("/dataprovider")) $: providers = path.filter(c => c._component?.endsWith("/dataprovider"))
// Set initial value to closest data provider
onMount(() => {
const valid = value && providers.find(x => getValue(x) === value) != null
if (!valid && providers.length) {
dispatch("change", getValue(providers[providers.length - 1]))
}
})
</script> </script>
<Select <Select

View file

@ -1,43 +1,17 @@
<script> <script>
import { Combobox } from "@budibase/bbui" import { Combobox } from "@budibase/bbui"
import {
getDatasourceForProvider,
getSchemaForDatasource,
} from "builderStore/dataBinding"
import { currentAsset } from "builderStore" import { currentAsset } from "builderStore"
import { findClosestMatchingComponent } from "builderStore/componentUtils" import { getComponentFieldOptions } from "helpers/formFields"
export let componentInstance export let componentInstance
export let value export let value
export let type export let type
$: form = findClosestMatchingComponent( $: options = getComponentFieldOptions(
$currentAsset?.props, $currentAsset?.props,
componentInstance._id, componentInstance?._id,
component => component._component === "@budibase/standard-components/form" type
) )
$: datasource = getDatasourceForProvider($currentAsset, form)
$: schema = getSchemaForDatasource($currentAsset, datasource, {
formSchema: true,
}).schema
$: options = getOptions(schema, type)
const getOptions = (schema, type) => {
let entries = Object.entries(schema ?? {})
let types = []
if (type === "field/options" || type === "field/longform") {
// allow options and longform to be used on string fields as well
types = [type, "field/string"]
} else {
types = [type]
}
types = types.map(type => type.slice(type.indexOf("/") + 1))
entries = entries.filter(entry => types.includes(entry[1].type))
return entries.map(entry => entry[0])
}
</script> </script>
<Combobox on:change {value} {options} /> <Combobox on:change {value} {options} />

View file

@ -74,11 +74,13 @@
}) })
</script> </script>
<div class="property-control" class:highlighted={highlighted && nullishValue}> <div
{#if type !== "boolean" && label} class="property-control"
<div class="label"> class:wide={!label}
<Label>{label}</Label> class:highlighted={highlighted && nullishValue}
</div> >
{#if label}
<Label size="M">{label}</Label>
{/if} {/if}
<div id={`${key}-prop-control`} class="control"> <div id={`${key}-prop-control`} class="control">
<svelte:component <svelte:component
@ -90,7 +92,6 @@
onChange={handleChange} onChange={handleChange}
bindings={allBindings} bindings={allBindings}
name={key} name={key}
text={label}
{nested} {nested}
{key} {key}
{type} {type}
@ -105,28 +106,34 @@
<style> <style>
.property-control { .property-control {
position: relative; position: relative;
display: flex; display: grid;
flex-direction: column; grid-template-columns: 90px 1fr;
justify-content: flex-start; align-items: center;
align-items: stretch;
transition: background 130ms ease-out, border-color 130ms ease-out; transition: background 130ms ease-out, border-color 130ms ease-out;
border-left: 4px solid transparent; border-left: 4px solid transparent;
margin: -6px calc(-1 * var(--spacing-xl)); margin: 0 calc(-1 * var(--spacing-xl));
padding: 6px var(--spacing-xl) 6px calc(var(--spacing-xl) - 4px); padding: 0 var(--spacing-xl) 0 calc(var(--spacing-xl) - 4px);
gap: 8px;
}
.property-control :global(.spectrum-FieldLabel) {
white-space: normal;
} }
.property-control.highlighted { .property-control.highlighted {
background: var(--spectrum-global-color-gray-300); background: var(--spectrum-global-color-gray-300);
border-color: var(--spectrum-global-color-blue-400); border-color: var(--spectrum-global-color-static-red-600);
}
.label {
padding-bottom: var(--spectrum-global-dimension-size-65);
} }
.control { .control {
position: relative; position: relative;
} }
.property-control.wide .control {
grid-column: 1 / -1;
}
.text { .text {
margin-top: var(--spectrum-global-dimension-size-65);
font-size: var(--spectrum-global-dimension-font-size-75); font-size: var(--spectrum-global-dimension-font-size-75);
color: var(--grey-6); color: var(--grey-6);
grid-column: 2 / 2;
}
.property-control.wide .text {
grid-column: 1 / -1;
} }
</style> </style>

View file

@ -0,0 +1,32 @@
import { findClosestMatchingComponent } from "builderStore/componentUtils"
import {
getDatasourceForProvider,
getSchemaForDatasource,
} from "builderStore/dataBinding"
export const getComponentFieldOptions = (asset, id, type, loose = true) => {
const form = findClosestMatchingComponent(
asset,
id,
component => component._component === "@budibase/standard-components/form"
)
const datasource = getDatasourceForProvider(asset, form)
const schema = getSchemaForDatasource(asset, datasource, {
formSchema: true,
}).schema
// Get valid types for this field
let types = [type]
if (loose) {
if (type === "field/options" || type === "field/longform") {
// Allow options and longform to be used on string fields as well
types = [type, "field/string"]
}
}
types = types.map(type => type.slice(type.indexOf("/") + 1))
// Find fields of valid types
return Object.entries(schema || {})
.filter(entry => types.includes(entry[1].type))
.map(entry => entry[0])
}

View file

@ -41,13 +41,13 @@
return return
} }
if (user.tenantId !== urlTenantId) { if (urlTenantId && user.tenantId !== urlTenantId) {
// user should not be here - play it safe and log them out // user should not be here - play it safe and log them out
try { try {
await auth.logout() await auth.logout()
await auth.setOrganisation(null) await auth.setOrganisation(null)
} catch (error) { } catch (error) {
// Swallow error and do nothing console.error("Tenant mis-match, logout.")
} }
} }
} else { } else {

View file

@ -220,6 +220,9 @@
} else if (type === "drop-new-component") { } else if (type === "drop-new-component") {
const { component, parent, index } = data const { component, parent, index } = data
await store.actions.components.create(component, null, parent, index) await store.actions.components.create(component, null, parent, index)
} else if (type === "add-parent-component") {
const { componentId, parentType } = data
await store.actions.components.addParent(componentId, parentType)
} else { } else {
console.warn(`Client sent unknown event type: ${type}`) console.warn(`Client sent unknown event type: ${type}`)
} }

View file

@ -37,7 +37,7 @@
{#if $selectedComponent} {#if $selectedComponent}
{#key $selectedComponent._id} {#key $selectedComponent._id}
<Panel {title} icon={componentDefinition?.icon} borderLeft> <Panel {title} icon={componentDefinition?.icon} borderLeft wide>
<span slot="panel-header-content"> <span slot="panel-header-content">
<div class="settings-tabs"> <div class="settings-tabs">
{#each tabs as tab} {#each tabs as tab}

View file

@ -117,6 +117,7 @@
{#each sections as section, idx (section.name)} {#each sections as section, idx (section.name)}
{#if section.visible} {#if section.visible}
<DetailSummary name={section.name} collapsible={false}> <DetailSummary name={section.name} collapsible={false}>
<div class="settings">
{#if idx === 0 && !componentInstance._component.endsWith("/layout") && !isScreen} {#if idx === 0 && !componentInstance._component.endsWith("/layout") && !isScreen}
<PropertyControl <PropertyControl
control={Input} control={Input}
@ -160,6 +161,7 @@
{#if idx === 0 && componentDefinition?.component?.endsWith("/fieldgroup")} {#if idx === 0 && componentDefinition?.component?.endsWith("/fieldgroup")}
<ResetFieldsButton {componentInstance} /> <ResetFieldsButton {componentInstance} />
{/if} {/if}
</div>
</DetailSummary> </DetailSummary>
{/if} {/if}
{/each} {/each}
@ -168,3 +170,13 @@
<EjectBlockButton /> <EjectBlockButton />
</DetailSummary> </DetailSummary>
{/if} {/if}
<style>
.settings {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
gap: 8px;
}
</style>

View file

@ -27,7 +27,6 @@
<StyleSection <StyleSection
{style} {style}
name={style.label} name={style.label}
columns={style.columns}
properties={style.settings} properties={style.settings}
{componentInstance} {componentInstance}
{bindings} {bindings}

View file

@ -4,7 +4,6 @@
import { store } from "builderStore" import { store } from "builderStore"
export let name export let name
export let columns
export let properties export let properties
export let componentInstance export let componentInstance
export let bindings = [] export let bindings = []
@ -34,9 +33,8 @@
</script> </script>
<DetailSummary collapsible={false} name={`${name}${changed ? " *" : ""}`}> <DetailSummary collapsible={false} name={`${name}${changed ? " *" : ""}`}>
<div class="group-content" style="grid-template-columns: {columns || '1fr'}"> <div class="styles">
{#each properties as prop (`${componentInstance._id}-${prop.key}-${prop.label}`)} {#each properties as prop (`${componentInstance._id}-${prop.key}-${prop.label}`)}
<div style="grid-column: {prop.column || 'auto'}">
<PropertyControl <PropertyControl
label={`${prop.label}${hasPropChanged(style, prop) ? " *" : ""}`} label={`${prop.label}${hasPropChanged(style, prop) ? " *" : ""}`}
control={prop.control} control={prop.control}
@ -46,15 +44,16 @@
props={getControlProps(prop)} props={getControlProps(prop)}
{bindings} {bindings}
/> />
</div>
{/each} {/each}
</div> </div>
</DetailSummary> </DetailSummary>
<style> <style>
.group-content { .styles {
display: grid; display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch; align-items: stretch;
gap: var(--spacing-l); gap: 8px;
} }
</style> </style>

View file

@ -3,7 +3,6 @@ import ColorPicker from "components/design/settings/controls/ColorPicker.svelte"
export const margin = { export const margin = {
label: "Margin", label: "Margin",
columns: "1fr 1fr",
settings: [ settings: [
{ {
label: "Top", label: "Top",
@ -90,7 +89,6 @@ export const margin = {
export const padding = { export const padding = {
label: "Padding", label: "Padding",
columns: "1fr 1fr",
settings: [ settings: [
{ {
label: "Top", label: "Top",
@ -177,7 +175,6 @@ export const padding = {
export const size = { export const size = {
label: "Size", label: "Size",
columns: "1fr 1fr",
settings: [ settings: [
{ {
label: "Width", label: "Width",
@ -196,7 +193,6 @@ export const size = {
export const background = { export const background = {
label: "Background", label: "Background",
columns: "auto 1fr",
settings: [ settings: [
{ {
label: "Color", label: "Color",
@ -285,7 +281,6 @@ export const background = {
export const border = { export const border = {
label: "Border", label: "Border",
columns: "1fr 1fr",
settings: [ settings: [
{ {
label: "Color", label: "Color",

View file

@ -1,22 +1,13 @@
<script> <script>
import Panel from "components/design/Panel.svelte" import Panel from "components/design/Panel.svelte"
import { goto } from "@roxi/routify" import { goto } from "@roxi/routify"
import { import { Layout, Search, Icon, Body, notifications } from "@budibase/bbui"
Layout,
ActionGroup,
ActionButton,
Search,
Icon,
Body,
notifications,
} from "@budibase/bbui"
import structure from "./componentStructure.json" import structure from "./componentStructure.json"
import { store, selectedComponent, selectedScreen } from "builderStore" import { store, selectedComponent, selectedScreen } from "builderStore"
import { onMount } from "svelte" import { onMount } from "svelte"
import { fly } from "svelte/transition" import { fly } from "svelte/transition"
import { findComponentPath } from "builderStore/componentUtils" import { findComponentPath } from "builderStore/componentUtils"
let section = "components"
let searchString let searchString
let searchRef let searchRef
let selectedIndex let selectedIndex
@ -37,7 +28,6 @@
allowedComponents, allowedComponents,
searchString searchString
) )
$: blocks = enrichedStructure.find(x => x.name === "Blocks").children
$: orderMap = createComponentOrderMap(componentList) $: orderMap = createComponentOrderMap(componentList)
const getAllowedComponents = (allComponents, screen, component) => { const getAllowedComponents = (allComponents, screen, component) => {
@ -127,6 +117,11 @@
} }
}) })
// Swap blocks and plugins
let tmp = enrichedStructure[1]
enrichedStructure[1] = enrichedStructure[0]
enrichedStructure[0] = tmp
return enrichedStructure return enrichedStructure
} }
@ -137,11 +132,6 @@
return [] return []
} }
// Remove blocks if there is no search string
if (!search) {
structure = structure.filter(category => category.name !== "Blocks")
}
// Return only items which match the search string // Return only items which match the search string
let filteredStructure = [] let filteredStructure = []
structure.forEach(category => { structure.forEach(category => {
@ -225,6 +215,7 @@
showCloseButton showCloseButton
onClickCloseButton={() => $goto("../")} onClickCloseButton={() => $goto("../")}
borderLeft borderLeft
wide
> >
<Layout paddingX="L" paddingY="XL" gap="S"> <Layout paddingX="L" paddingY="XL" gap="S">
<Search <Search
@ -233,21 +224,6 @@
on:change={e => (searchString = e.detail)} on:change={e => (searchString = e.detail)}
bind:inputRef={searchRef} bind:inputRef={searchRef}
/> />
{#if !searchString}
<ActionGroup compact justified>
<ActionButton
fullWidth
selected={section === "components"}
on:click={() => (section = "components")}>Components</ActionButton
>
<ActionButton
fullWidth
selected={section === "blocks"}
on:click={() => (section = "blocks")}>Blocks</ActionButton
>
</ActionGroup>
{/if}
{#if searchString || section === "components"}
{#if filteredStructure.length} {#if filteredStructure.length}
{#each filteredStructure as category} {#each filteredStructure as category}
<Layout noPadding gap="XS"> <Layout noPadding gap="XS">
@ -258,8 +234,7 @@
on:dragstart={() => onDragStart(component.component)} on:dragstart={() => onDragStart(component.component)}
on:dragend={onDragEnd} on:dragend={onDragEnd}
class="component" class="component"
class:selected={selectedIndex === class:selected={selectedIndex === orderMap[component.component]}
orderMap[component.component]}
on:click={() => addComponent(component.component)} on:click={() => addComponent(component.component)}
on:mouseover={() => (selectedIndex = null)} on:mouseover={() => (selectedIndex = null)}
on:focus on:focus
@ -275,23 +250,6 @@
There aren't any components matching the current filter There aren't any components matching the current filter
</Body> </Body>
{/if} {/if}
{:else}
<Body size="S">Blocks are collections of pre-built components</Body>
<Layout noPadding gap="XS">
{#each blocks as block}
<div
draggable="true"
class="component"
on:click={() => addComponent(block.component)}
on:dragstart={() => onDragStart(block.component)}
on:dragend={onDragEnd}
>
<Icon name={block.icon} />
<Body size="XS">{block.name}</Body>
</div>
{/each}
</Layout>
{/if}
</Layout> </Layout>
</Panel> </Panel>
</div> </div>

View file

@ -1,6 +1,6 @@
{ {
"name": "@budibase/cli", "name": "@budibase/cli",
"version": "2.4.27-alpha.9", "version": "2.4.27-alpha.12",
"description": "Budibase CLI, for developers, self hosting and migrations.", "description": "Budibase CLI, for developers, self hosting and migrations.",
"main": "dist/index.js", "main": "dist/index.js",
"bin": { "bin": {
@ -29,9 +29,9 @@
"outputPath": "build" "outputPath": "build"
}, },
"dependencies": { "dependencies": {
"@budibase/backend-core": "2.4.27-alpha.9", "@budibase/backend-core": "2.4.27-alpha.12",
"@budibase/string-templates": "2.4.27-alpha.9", "@budibase/string-templates": "2.4.27-alpha.12",
"@budibase/types": "2.4.27-alpha.9", "@budibase/types": "2.4.27-alpha.12",
"axios": "0.21.2", "axios": "0.21.2",
"chalk": "4.1.0", "chalk": "4.1.0",
"cli-progress": "3.11.2", "cli-progress": "3.11.2",

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
{ {
"name": "@budibase/client", "name": "@budibase/client",
"version": "2.4.27-alpha.9", "version": "2.4.27-alpha.12",
"license": "MPL-2.0", "license": "MPL-2.0",
"module": "dist/budibase-client.js", "module": "dist/budibase-client.js",
"main": "dist/budibase-client.js", "main": "dist/budibase-client.js",
@ -19,11 +19,11 @@
"dev:builder": "rollup -cw" "dev:builder": "rollup -cw"
}, },
"dependencies": { "dependencies": {
"@budibase/bbui": "2.4.27-alpha.9", "@budibase/bbui": "2.4.27-alpha.12",
"@budibase/frontend-core": "2.4.27-alpha.9", "@budibase/frontend-core": "2.4.27-alpha.12",
"@budibase/shared-core": "2.4.27-alpha.9", "@budibase/shared-core": "2.4.27-alpha.12",
"@budibase/string-templates": "2.4.27-alpha.9", "@budibase/string-templates": "2.4.27-alpha.12",
"@budibase/types": "2.4.27-alpha.9", "@budibase/types": "2.4.27-alpha.12",
"@spectrum-css/button": "^3.0.3", "@spectrum-css/button": "^3.0.3",
"@spectrum-css/card": "^3.0.3", "@spectrum-css/card": "^3.0.3",
"@spectrum-css/divider": "^1.0.3", "@spectrum-css/divider": "^1.0.3",

View file

@ -37,7 +37,7 @@
// Provide contexts // Provide contexts
setContext("sdk", SDK) setContext("sdk", SDK)
setContext("component", writable({})) setContext("component", writable({ id: null, ancestors: [] }))
setContext("context", createContextStore()) setContext("context", createContextStore())
let dataLoaded = false let dataLoaded = false

View file

@ -26,19 +26,20 @@
} from "stores" } from "stores"
import { Helpers } from "@budibase/bbui" import { Helpers } from "@budibase/bbui"
import { getActiveConditions, reduceConditionActions } from "utils/conditions" import { getActiveConditions, reduceConditionActions } from "utils/conditions"
import Placeholder from "components/app/Placeholder.svelte" import EmptyPlaceholder from "components/app/EmptyPlaceholder.svelte"
import ScreenPlaceholder from "components/app/ScreenPlaceholder.svelte" import ScreenPlaceholder from "components/app/ScreenPlaceholder.svelte"
import ComponentPlaceholder from "components/app/ComponentPlaceholder.svelte" import ComponentErrorState from "components/error-states/ComponentErrorState.svelte"
import { BudibasePrefix } from "../stores/components.js"
export let instance = {} export let instance = {}
export let isLayout = false export let isLayout = false
export let isScreen = false export let isScreen = false
export let isBlock = false export let isBlock = false
export let parent = null
// Get parent contexts // Get parent contexts
const context = getContext("context") const context = getContext("context")
const insideScreenslot = !!getContext("screenslot") const insideScreenslot = !!getContext("screenslot")
const component = getContext("component")
// Create component context // Create component context
const store = writable({}) const store = writable({})
@ -120,6 +121,12 @@
$: showEmptyState = definition?.showEmptyState !== false $: showEmptyState = definition?.showEmptyState !== false
$: hasMissingRequiredSettings = missingRequiredSettings?.length > 0 $: hasMissingRequiredSettings = missingRequiredSettings?.length > 0
$: editable = !!definition?.editable && !hasMissingRequiredSettings $: editable = !!definition?.editable && !hasMissingRequiredSettings
$: requiredAncestors = definition?.requiredAncestors || []
$: missingRequiredAncestors = requiredAncestors.filter(
ancestor => !$component.ancestors.includes(`${BudibasePrefix}${ancestor}`)
)
$: hasMissingRequiredAncestors = missingRequiredAncestors?.length > 0
$: errorState = hasMissingRequiredSettings || hasMissingRequiredAncestors
// Interactive components can be selected, dragged and highlighted inside // Interactive components can be selected, dragged and highlighted inside
// the builder preview // the builder preview
@ -183,6 +190,7 @@
custom: customCSS, custom: customCSS,
id, id,
empty: emptyState, empty: emptyState,
selected,
interactive, interactive,
draggable, draggable,
editable, editable,
@ -193,7 +201,9 @@
name, name,
editing, editing,
type: instance._component, type: instance._component,
missingRequiredSettings, errorState,
parent: id,
ancestors: [...$component?.ancestors, instance._component],
}) })
const initialise = (instance, force = false) => { const initialise = (instance, force = false) => {
@ -482,6 +492,7 @@
getDataContext: () => get(context), getDataContext: () => get(context),
reload: () => initialise(instance, true), reload: () => initialise(instance, true),
setEphemeralStyles: styles => (ephemeralStyles = styles), setEphemeralStyles: styles => (ephemeralStyles = styles),
state: store,
}) })
} }
}) })
@ -509,24 +520,28 @@
class:pad class:pad
class:parent={hasChildren} class:parent={hasChildren}
class:block={isBlock} class:block={isBlock}
class:error={errorState}
data-id={id} data-id={id}
data-name={name} data-name={name}
data-icon={icon} data-icon={icon}
data-parent={parent} data-parent={$component.id}
> >
{#if hasMissingRequiredSettings} {#if errorState}
<ComponentPlaceholder /> <ComponentErrorState
{missingRequiredSettings}
{missingRequiredAncestors}
/>
{:else} {:else}
<svelte:component this={constructor} bind:this={ref} {...initialSettings}> <svelte:component this={constructor} bind:this={ref} {...initialSettings}>
{#if children.length} {#if children.length}
{#each children as child (child._id)} {#each children as child (child._id)}
<svelte:self instance={child} parent={id} /> <svelte:self instance={child} />
{/each} {/each}
{:else if emptyState} {:else if emptyState}
{#if isScreen} {#if isScreen}
<ScreenPlaceholder /> <ScreenPlaceholder />
{:else} {:else}
<Placeholder /> <EmptyPlaceholder />
{/if} {/if}
{:else if isBlock} {:else if isBlock}
<slot /> <slot />

View file

@ -1,42 +0,0 @@
<script>
import { getContext } from "svelte"
import { builderStore } from "stores"
const component = getContext("component")
const { styleable } = getContext("sdk")
$: requiredSetting = $component.missingRequiredSettings?.[0]
</script>
{#if $builderStore.inBuilder && requiredSetting}
<div class="component-placeholder" use:styleable={$component.styles}>
<span>
Add the <mark>{requiredSetting.label}</mark> setting to start using your component
-
</span>
<span
class="spectrum-Link"
on:click={() => {
builderStore.actions.highlightSetting(requiredSetting.key)
}}
>
Show me
</span>
</div>
{/if}
<style>
.component-placeholder {
color: var(--spectrum-global-color-gray-600);
font-size: var(--font-size-s);
padding: var(--spacing-xs);
}
.component-placeholder mark {
background-color: var(--spectrum-global-color-gray-400);
padding: 0 4px;
border-radius: 2px;
}
.component-placeholder .spectrum-Link {
cursor: pointer;
}
</style>

View file

@ -0,0 +1,45 @@
<script>
import { getContext } from "svelte"
import { Icon } from "@budibase/bbui"
const component = getContext("component")
const { builderStore, componentStore } = getContext("sdk")
$: definition = componentStore.actions.getComponentDefinition($component.type)
</script>
{#if $builderStore.inBuilder}
<div class="component-placeholder">
<Icon name="Help" color="var(--spectrum-global-color-blue-600)" />
<span
class="spectrum-Link"
on:click={() => {
builderStore.actions.requestAddComponent()
}}
>
Add components inside your {definition?.name || $component.type}
</span>
</div>
{/if}
<style>
.component-placeholder {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
color: var(--spectrum-global-color-gray-600);
font-size: var(--font-size-s);
gap: var(--spacing-s);
}
/* Common styles for all error states to use */
.component-placeholder :global(mark) {
background-color: var(--spectrum-global-color-gray-400);
padding: 0 4px;
border-radius: 2px;
}
.component-placeholder :global(.spectrum-Link) {
cursor: pointer;
}
</style>

View file

@ -4,7 +4,7 @@
let width = window.innerWidth let width = window.innerWidth
let height = window.innerHeight let height = window.innerHeight
const tabletBreakpoint = 768 const tabletBreakpoint = 720
const desktopBreakpoint = 1280 const desktopBreakpoint = 1280
const resizeObserver = new ResizeObserver(entries => { const resizeObserver = new ResizeObserver(entries => {
if (entries?.[0]) { if (entries?.[0]) {

View file

@ -0,0 +1,52 @@
<script>
import { getContext } from "svelte"
import { Icon } from "@budibase/bbui"
import MissingRequiredSetting from "./MissingRequiredSetting.svelte"
import MissingRequiredAncestor from "./MissingRequiredAncestor.svelte"
export let missingRequiredSettings
export let missingRequiredAncestors
const component = getContext("component")
const { styleable, builderStore } = getContext("sdk")
$: styles = { ...$component.styles, normal: {}, custom: null, empty: true }
$: requiredSetting = missingRequiredSettings?.[0]
$: requiredAncestor = missingRequiredAncestors?.[0]
</script>
{#if $builderStore.inBuilder}
{#if $component.errorState}
<div class="component-placeholder" use:styleable={styles}>
<Icon name="Alert" color="var(--spectrum-global-color-static-red-600)" />
{#if requiredAncestor}
<MissingRequiredAncestor {requiredAncestor} />
{:else if requiredSetting}
<MissingRequiredSetting {requiredSetting} />
{/if}
</div>
{/if}
{/if}
<style>
.component-placeholder {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
color: var(--spectrum-global-color-gray-600);
font-size: var(--font-size-s);
padding: var(--spacing-xs);
gap: var(--spacing-s);
}
/* Common styles for all error states to use */
.component-placeholder :global(mark) {
background-color: var(--spectrum-global-color-gray-400);
padding: 0 4px;
border-radius: 2px;
}
.component-placeholder :global(.spectrum-Link) {
cursor: pointer;
}
</style>

View file

@ -0,0 +1,41 @@
<script>
import { getContext } from "svelte"
import { BudibasePrefix } from "stores/components"
export let requiredAncestor
const component = getContext("component")
const { builderStore, componentStore } = getContext("sdk")
$: definition = componentStore.actions.getComponentDefinition($component.type)
$: fullAncestorType = `${BudibasePrefix}${requiredAncestor}`
$: ancestorDefinition =
componentStore.actions.getComponentDefinition(fullAncestorType)
$: pluralName = getPluralName(definition?.name, $component.type)
$: ancestorName = getAncestorName(ancestorDefinition?.name, requiredAncestor)
const getPluralName = (name, type) => {
if (!name) {
name = type.replace(BudibasePrefix, "")
}
return name.endsWith("s") ? `${name}'` : `${name}s`
}
const getAncestorName = name => {
return name || requiredAncestor
}
</script>
<span>
{pluralName} need to be inside a
<mark>{ancestorName}</mark>
</span>
<span>-</span>
<span
class="spectrum-Link"
on:click={() => {
builderStore.actions.addParentComponent($component.id, fullAncestorType)
}}
>
Add {ancestorName}
</span>

View file

@ -0,0 +1,20 @@
<script>
import { getContext } from "svelte"
export let requiredSetting
const { builderStore } = getContext("sdk")
</script>
<span>
Add the <mark>{requiredSetting.label}</mark> setting to start using your component
</span>
<span>-</span>
<span
class="spectrum-Link"
on:click={() => {
builderStore.actions.highlightSetting(requiredSetting.key)
}}
>
Show me
</span>

View file

@ -12,7 +12,8 @@
$: id = dragInfo?.id || id $: id = dragInfo?.id || id
// Set ephemeral grid styles on the dragged component // Set ephemeral grid styles on the dragged component
$: componentStore.actions.getComponentInstance(id)?.setEphemeralStyles({ $: instance = componentStore.actions.getComponentInstance(id)
$: $instance?.setEphemeralStyles({
...gridStyles, ...gridStyles,
...(gridStyles ? { "z-index": 999 } : null), ...(gridStyles ? { "z-index": 999 } : null),
}) })

View file

@ -16,6 +16,7 @@
let text let text
let icon let icon
let insideGrid = false let insideGrid = false
let errorState = false
$: visibleIndicators = indicators.filter(x => x.visible) $: visibleIndicators = indicators.filter(x => x.visible)
$: offset = $builderStore.inBuilder ? 0 : 2 $: offset = $builderStore.inBuilder ? 0 : 2
@ -85,6 +86,7 @@
icon = parents[0].dataset.icon icon = parents[0].dataset.icon
} }
} }
errorState = parents?.[0]?.classList.contains("error")
// Batch reads to minimize reflow // Batch reads to minimize reflow
const scrollX = window.scrollX const scrollX = window.scrollX
@ -152,10 +154,10 @@
text={idx === 0 ? text : null} text={idx === 0 ? text : null}
icon={idx === 0 ? icon : null} icon={idx === 0 ? icon : null}
showResizeAnchors={allowResizeAnchors && insideGrid} showResizeAnchors={allowResizeAnchors && insideGrid}
color={errorState ? "var(--spectrum-global-color-static-red-600)" : color}
{componentId} {componentId}
{transition} {transition}
{zIndex} {zIndex}
{color}
/> />
{/each} {/each}
{/key} {/key}

View file

@ -15,17 +15,22 @@
let self let self
let measured = false let measured = false
$: id = $builderStore.selectedComponentId
$: instance = componentStore.actions.getComponentInstance(id)
$: state = $instance?.state
$: definition = $componentStore.selectedComponentDefinition $: definition = $componentStore.selectedComponentDefinition
$: showBar = $: showBar =
definition?.showSettingsBar !== false && !$dndIsDragging && definition definition?.showSettingsBar !== false &&
!$dndIsDragging &&
definition &&
!$state?.errorState
$: { $: {
if (!showBar) { if (!showBar) {
measured = false measured = false
} }
} }
$: settings = getBarSettings(definition) $: settings = getBarSettings(definition)
$: isScreen = $: isScreen = id === $builderStore.screen?.props?._id
$builderStore.selectedComponentId === $builderStore.screen?.props?._id
const getBarSettings = definition => { const getBarSettings = definition => {
let allSettings = [] let allSettings = []

View file

@ -109,6 +109,12 @@ const createBuilderStore = () => {
// Notify the builder so we can reload component definitions // Notify the builder so we can reload component definitions
eventStore.actions.dispatchEvent("reload-plugin") eventStore.actions.dispatchEvent("reload-plugin")
}, },
addParentComponent: (componentId, parentType) => {
eventStore.actions.dispatchEvent("add-parent-component", {
componentId,
parentType,
})
},
} }
return { return {
...store, ...store,

View file

@ -8,7 +8,7 @@ import Router from "../components/Router.svelte"
import * as AppComponents from "../components/app/index.js" import * as AppComponents from "../components/app/index.js"
import { ScreenslotType } from "../constants.js" import { ScreenslotType } from "../constants.js"
const budibasePrefix = "@budibase/standard-components/" export const BudibasePrefix = "@budibase/standard-components/"
const createComponentStore = () => { const createComponentStore = () => {
const store = writable({ const store = writable({
@ -107,12 +107,12 @@ const createComponentStore = () => {
// Screenslot is an edge case // Screenslot is an edge case
if (type === ScreenslotType) { if (type === ScreenslotType) {
type = `${budibasePrefix}${type}` type = `${BudibasePrefix}${type}`
} }
// Handle built-in components // Handle built-in components
if (type.startsWith(budibasePrefix)) { if (type.startsWith(BudibasePrefix)) {
type = type.replace(budibasePrefix, "") type = type.replace(BudibasePrefix, "")
return type ? Manifest[type] : null return type ? Manifest[type] : null
} }
@ -130,7 +130,7 @@ const createComponentStore = () => {
} }
// Handle budibase components // Handle budibase components
if (type.startsWith(budibasePrefix)) { if (type.startsWith(BudibasePrefix)) {
const split = type.split("/") const split = type.split("/")
const name = split[split.length - 1] const name = split[split.length - 1]
return AppComponents[name] return AppComponents[name]
@ -145,7 +145,7 @@ const createComponentStore = () => {
if (!id) { if (!id) {
return null return null
} }
return get(store).mountedComponents[id] return derived(store, $store => $store.mountedComponents[id])
} }
const registerCustomComponent = ({ Component, schema, version }) => { const registerCustomComponent = ({ Component, schema, version }) => {

View file

@ -29,9 +29,13 @@ export const styleable = (node, styles = {}) => {
let baseStyles = {} let baseStyles = {}
if (newStyles.empty) { if (newStyles.empty) {
baseStyles.border = "2px dashed var(--spectrum-global-color-gray-400)"
baseStyles.padding = "var(--spacing-l)" baseStyles.padding = "var(--spacing-l)"
baseStyles.overflow = "hidden" baseStyles.overflow = "hidden"
if (newStyles.selected) {
baseStyles.border = "2px solid transparent"
} else {
baseStyles.border = "2px dashed var(--spectrum-global-color-gray-400)"
}
} }
const componentId = newStyles.id const componentId = newStyles.id

View file

@ -1,13 +1,13 @@
{ {
"name": "@budibase/frontend-core", "name": "@budibase/frontend-core",
"version": "2.4.27-alpha.9", "version": "2.4.27-alpha.12",
"description": "Budibase frontend core libraries used in builder and client", "description": "Budibase frontend core libraries used in builder and client",
"author": "Budibase", "author": "Budibase",
"license": "MPL-2.0", "license": "MPL-2.0",
"svelte": "src/index.js", "svelte": "src/index.js",
"dependencies": { "dependencies": {
"@budibase/bbui": "2.4.27-alpha.9", "@budibase/bbui": "2.4.27-alpha.12",
"@budibase/shared-core": "2.4.27-alpha.9", "@budibase/shared-core": "2.4.27-alpha.12",
"dayjs": "^1.11.7", "dayjs": "^1.11.7",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"socket.io-client": "^4.6.1", "socket.io-client": "^4.6.1",

View file

@ -78,6 +78,8 @@
reorder.actions.moveColumnRight(column.name) reorder.actions.moveColumnRight(column.name)
open = false open = false
} }
const makeDisplayColumn = () => {}
</script> </script>
<div <div
@ -148,6 +150,9 @@
<MenuItem disabled={!canMoveRight} icon="ArrowRight" on:click={moveRight}> <MenuItem disabled={!canMoveRight} icon="ArrowRight" on:click={moveRight}>
Move right Move right
</MenuItem> </MenuItem>
<MenuItem icon="Label" on:click={makeDisplayColumn}
>Use as display column</MenuItem
>
</Menu> </Menu>
</Popover> </Popover>

View file

@ -1,6 +1,6 @@
{ {
"name": "@budibase/sdk", "name": "@budibase/sdk",
"version": "2.4.27-alpha.9", "version": "2.4.27-alpha.12",
"description": "Budibase Public API SDK", "description": "Budibase Public API SDK",
"author": "Budibase", "author": "Budibase",
"license": "MPL-2.0", "license": "MPL-2.0",

View file

@ -1,7 +1,7 @@
{ {
"name": "@budibase/server", "name": "@budibase/server",
"email": "hi@budibase.com", "email": "hi@budibase.com",
"version": "2.4.27-alpha.9", "version": "2.4.27-alpha.12",
"description": "Budibase Web Server", "description": "Budibase Web Server",
"main": "src/index.ts", "main": "src/index.ts",
"repository": { "repository": {
@ -44,12 +44,12 @@
"license": "GPL-3.0", "license": "GPL-3.0",
"dependencies": { "dependencies": {
"@apidevtools/swagger-parser": "10.0.3", "@apidevtools/swagger-parser": "10.0.3",
"@budibase/backend-core": "2.4.27-alpha.9", "@budibase/backend-core": "2.4.27-alpha.12",
"@budibase/client": "2.4.27-alpha.9", "@budibase/client": "2.4.27-alpha.12",
"@budibase/pro": "2.4.27-alpha.9", "@budibase/pro": "2.4.27-alpha.12",
"@budibase/shared-core": "2.4.27-alpha.9", "@budibase/shared-core": "2.4.27-alpha.12",
"@budibase/string-templates": "2.4.27-alpha.9", "@budibase/string-templates": "2.4.27-alpha.12",
"@budibase/types": "2.4.27-alpha.9", "@budibase/types": "2.4.27-alpha.12",
"@bull-board/api": "3.7.0", "@bull-board/api": "3.7.0",
"@bull-board/koa": "3.9.4", "@bull-board/koa": "3.9.4",
"@elastic/elasticsearch": "7.10.0", "@elastic/elasticsearch": "7.10.0",

View file

@ -1290,14 +1290,14 @@
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
"@budibase/backend-core@2.4.27-alpha.9": "@budibase/backend-core@2.4.27-alpha.12":
version "2.4.27-alpha.9" version "2.4.27-alpha.12"
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.4.27-alpha.9.tgz#76dee792f6928f4a133b25f4842048cdb5e40cc9" resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.4.27-alpha.12.tgz#655367eaa16a8bf1a28ff38eb0ab2127decb5b23"
integrity sha512-dAMqy8I+bmClDEyYgB/Frsq9VQvTiWXzSrKdCfzoAGPFIWQSLaXBzhvj32YI/q7j1oo5MHtECuPKERXd/umh3w== integrity sha512-qCszwPDuYvMYZLl54T82ZrpM5xHD6nTEzPAkPQFrZIDtkW1dfKd2z+E2OirIOYb1CRN4GwrnIDjR0+u231Qo5w==
dependencies: dependencies:
"@budibase/nano" "10.1.2" "@budibase/nano" "10.1.2"
"@budibase/pouchdb-replication-stream" "1.2.10" "@budibase/pouchdb-replication-stream" "1.2.10"
"@budibase/types" "2.4.27-alpha.9" "@budibase/types" "2.4.27-alpha.12"
"@shopify/jest-koa-mocks" "5.0.1" "@shopify/jest-koa-mocks" "5.0.1"
"@techpass/passport-openidconnect" "0.3.2" "@techpass/passport-openidconnect" "0.3.2"
aws-cloudfront-sign "2.2.0" aws-cloudfront-sign "2.2.0"
@ -1429,14 +1429,14 @@
pouchdb-promise "^6.0.4" pouchdb-promise "^6.0.4"
through2 "^2.0.0" through2 "^2.0.0"
"@budibase/pro@2.4.27-alpha.9": "@budibase/pro@2.4.27-alpha.12":
version "2.4.27-alpha.9" version "2.4.27-alpha.12"
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.4.27-alpha.9.tgz#68e74c0541abda731ab2785bd9e33e21b78f6328" resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.4.27-alpha.12.tgz#c3f74e4ef04ea1355bdc1333dd033f4a47087ac2"
integrity sha512-BUFpjAcySWdhxosNtTQa6nOYbVKvX0ifrVCwXlveBE4+YXYK+e8vsVn56oMtkffYfhrhHx1D/u5NSNPKCJLHZw== integrity sha512-Hl/zalqmtlINgylFqQz3zWG/4dTXm/Skck2Q/j69gyqy0DotWXfRko03CbDQCyHGWhM/oXXiYJK54HIkn+kOTw==
dependencies: dependencies:
"@budibase/backend-core" "2.4.27-alpha.9" "@budibase/backend-core" "2.4.27-alpha.12"
"@budibase/string-templates" "2.3.20" "@budibase/string-templates" "2.3.20"
"@budibase/types" "2.4.27-alpha.9" "@budibase/types" "2.4.27-alpha.12"
"@koa/router" "8.0.8" "@koa/router" "8.0.8"
bull "4.10.1" bull "4.10.1"
joi "17.6.0" joi "17.6.0"
@ -1475,10 +1475,10 @@
lodash "^4.17.20" lodash "^4.17.20"
vm2 "^3.9.4" vm2 "^3.9.4"
"@budibase/types@2.4.27-alpha.9": "@budibase/types@2.4.27-alpha.12":
version "2.4.27-alpha.9" version "2.4.27-alpha.12"
resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.4.27-alpha.9.tgz#fabf342ea50986532b395d01bfa2c55dcbe790f8" resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.4.27-alpha.12.tgz#4c0dd66434f52c41080c17b810262ff8f195cb6c"
integrity sha512-geugj3sHPHEoA+p9j9vNWeyINNxp0Pv0iCxEUrQKzFcj8nypchJZFOxEByzxCsy2SU2w/TPzWKJZzouanWcoiA== integrity sha512-mpzlQM49WWQyWnysFcMkJDD3jY6Wf5HG7R8/eb5PRbSWx92+17g0iCbhuaswfCJdz0GIi73hebmvaFeuQ/glMg==
"@bull-board/api@3.7.0": "@bull-board/api@3.7.0":
version "3.7.0" version "3.7.0"

View file

@ -1,6 +1,6 @@
{ {
"name": "@budibase/shared-core", "name": "@budibase/shared-core",
"version": "2.4.27-alpha.9", "version": "2.4.27-alpha.12",
"description": "Shared data utils", "description": "Shared data utils",
"main": "dist/cjs/src/index.js", "main": "dist/cjs/src/index.js",
"types": "dist/mjs/src/index.d.ts", "types": "dist/mjs/src/index.d.ts",
@ -20,7 +20,7 @@
"dev:builder": "yarn prebuild && concurrently \"tsc -p tsconfig.build.json --watch\" \"tsc -p tsconfig-cjs.build.json --watch\"" "dev:builder": "yarn prebuild && concurrently \"tsc -p tsconfig.build.json --watch\" \"tsc -p tsconfig-cjs.build.json --watch\""
}, },
"dependencies": { "dependencies": {
"@budibase/types": "2.4.27-alpha.9" "@budibase/types": "2.4.27-alpha.12"
}, },
"devDependencies": { "devDependencies": {
"concurrently": "^7.6.0", "concurrently": "^7.6.0",

View file

@ -1,6 +1,6 @@
{ {
"name": "@budibase/string-templates", "name": "@budibase/string-templates",
"version": "2.4.27-alpha.9", "version": "2.4.27-alpha.12",
"description": "Handlebars wrapper for Budibase templating.", "description": "Handlebars wrapper for Budibase templating.",
"main": "src/index.cjs", "main": "src/index.cjs",
"module": "dist/bundle.mjs", "module": "dist/bundle.mjs",

View file

@ -1,6 +1,6 @@
{ {
"name": "@budibase/types", "name": "@budibase/types",
"version": "2.4.27-alpha.9", "version": "2.4.27-alpha.12",
"description": "Budibase types", "description": "Budibase types",
"main": "dist/cjs/index.js", "main": "dist/cjs/index.js",
"types": "dist/mjs/index.d.ts", "types": "dist/mjs/index.d.ts",

View file

@ -1,7 +1,7 @@
{ {
"name": "@budibase/worker", "name": "@budibase/worker",
"email": "hi@budibase.com", "email": "hi@budibase.com",
"version": "2.4.27-alpha.9", "version": "2.4.27-alpha.12",
"description": "Budibase background service", "description": "Budibase background service",
"main": "src/index.ts", "main": "src/index.ts",
"repository": { "repository": {
@ -36,10 +36,10 @@
"author": "Budibase", "author": "Budibase",
"license": "GPL-3.0", "license": "GPL-3.0",
"dependencies": { "dependencies": {
"@budibase/backend-core": "2.4.27-alpha.9", "@budibase/backend-core": "2.4.27-alpha.12",
"@budibase/pro": "2.4.27-alpha.9", "@budibase/pro": "2.4.27-alpha.12",
"@budibase/string-templates": "2.4.27-alpha.9", "@budibase/string-templates": "2.4.27-alpha.12",
"@budibase/types": "2.4.27-alpha.9", "@budibase/types": "2.4.27-alpha.12",
"@koa/router": "8.0.8", "@koa/router": "8.0.8",
"@sentry/node": "6.17.7", "@sentry/node": "6.17.7",
"@techpass/passport-openidconnect": "0.3.2", "@techpass/passport-openidconnect": "0.3.2",

View file

@ -475,14 +475,14 @@
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
"@budibase/backend-core@2.4.27-alpha.9": "@budibase/backend-core@2.4.27-alpha.12":
version "2.4.27-alpha.9" version "2.4.27-alpha.12"
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.4.27-alpha.9.tgz#76dee792f6928f4a133b25f4842048cdb5e40cc9" resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.4.27-alpha.12.tgz#655367eaa16a8bf1a28ff38eb0ab2127decb5b23"
integrity sha512-dAMqy8I+bmClDEyYgB/Frsq9VQvTiWXzSrKdCfzoAGPFIWQSLaXBzhvj32YI/q7j1oo5MHtECuPKERXd/umh3w== integrity sha512-qCszwPDuYvMYZLl54T82ZrpM5xHD6nTEzPAkPQFrZIDtkW1dfKd2z+E2OirIOYb1CRN4GwrnIDjR0+u231Qo5w==
dependencies: dependencies:
"@budibase/nano" "10.1.2" "@budibase/nano" "10.1.2"
"@budibase/pouchdb-replication-stream" "1.2.10" "@budibase/pouchdb-replication-stream" "1.2.10"
"@budibase/types" "2.4.27-alpha.9" "@budibase/types" "2.4.27-alpha.12"
"@shopify/jest-koa-mocks" "5.0.1" "@shopify/jest-koa-mocks" "5.0.1"
"@techpass/passport-openidconnect" "0.3.2" "@techpass/passport-openidconnect" "0.3.2"
aws-cloudfront-sign "2.2.0" aws-cloudfront-sign "2.2.0"
@ -564,14 +564,14 @@
pouchdb-promise "^6.0.4" pouchdb-promise "^6.0.4"
through2 "^2.0.0" through2 "^2.0.0"
"@budibase/pro@2.4.27-alpha.9": "@budibase/pro@2.4.27-alpha.12":
version "2.4.27-alpha.9" version "2.4.27-alpha.12"
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.4.27-alpha.9.tgz#68e74c0541abda731ab2785bd9e33e21b78f6328" resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.4.27-alpha.12.tgz#c3f74e4ef04ea1355bdc1333dd033f4a47087ac2"
integrity sha512-BUFpjAcySWdhxosNtTQa6nOYbVKvX0ifrVCwXlveBE4+YXYK+e8vsVn56oMtkffYfhrhHx1D/u5NSNPKCJLHZw== integrity sha512-Hl/zalqmtlINgylFqQz3zWG/4dTXm/Skck2Q/j69gyqy0DotWXfRko03CbDQCyHGWhM/oXXiYJK54HIkn+kOTw==
dependencies: dependencies:
"@budibase/backend-core" "2.4.27-alpha.9" "@budibase/backend-core" "2.4.27-alpha.12"
"@budibase/string-templates" "2.3.20" "@budibase/string-templates" "2.3.20"
"@budibase/types" "2.4.27-alpha.9" "@budibase/types" "2.4.27-alpha.12"
"@koa/router" "8.0.8" "@koa/router" "8.0.8"
bull "4.10.1" bull "4.10.1"
joi "17.6.0" joi "17.6.0"
@ -592,10 +592,10 @@
lodash "^4.17.20" lodash "^4.17.20"
vm2 "^3.9.4" vm2 "^3.9.4"
"@budibase/types@2.4.27-alpha.9": "@budibase/types@2.4.27-alpha.12":
version "2.4.27-alpha.9" version "2.4.27-alpha.12"
resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.4.27-alpha.9.tgz#fabf342ea50986532b395d01bfa2c55dcbe790f8" resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.4.27-alpha.12.tgz#4c0dd66434f52c41080c17b810262ff8f195cb6c"
integrity sha512-geugj3sHPHEoA+p9j9vNWeyINNxp0Pv0iCxEUrQKzFcj8nypchJZFOxEByzxCsy2SU2w/TPzWKJZzouanWcoiA== integrity sha512-mpzlQM49WWQyWnysFcMkJDD3jY6Wf5HG7R8/eb5PRbSWx92+17g0iCbhuaswfCJdz0GIi73hebmvaFeuQ/glMg==
"@cspotcode/source-map-support@^0.8.0": "@cspotcode/source-map-support@^0.8.0":
version "0.8.1" version "0.8.1"