forked from tsai/budibase
17 changed files with 415 additions and 542 deletions
@ -1,4 +1,4 @@ |
|||||
packages/builder/src/userInterface/CurrentItemPreview.svelte |
packages/builder/src/components/design/AppPreview/CurrentItemPreview.svelte |
||||
public |
public |
||||
dist |
dist |
||||
packages/server/builder |
packages/server/builder |
||||
|
|||||
@ -0,0 +1,97 @@ |
|||||
|
import { get } from "svelte/store" |
||||
|
import { backendUiStore, store } from "builderStore" |
||||
|
import { findAllMatchingComponents, findComponentPath } from "./storeUtils" |
||||
|
|
||||
|
/** |
||||
|
* Gets all bindable data context fields and instance fields. |
||||
|
*/ |
||||
|
export const getBindableProperties = (rootComponent, componentId) => { |
||||
|
const bindableContexts = getBindableContexts(rootComponent, componentId) |
||||
|
const bindableComponents = getBindableComponents(rootComponent) |
||||
|
return [...bindableContexts, ...bindableComponents] |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Gets all bindable data contexts. These are fields of schemas of data contexts |
||||
|
* provided by data provider components, such as lists or row detail components. |
||||
|
*/ |
||||
|
export const getBindableContexts = (rootComponent, componentId) => { |
||||
|
if (!rootComponent || !componentId) { |
||||
|
return [] |
||||
|
} |
||||
|
|
||||
|
// Get the component tree leading up to this component
|
||||
|
const path = findComponentPath(rootComponent, componentId) |
||||
|
path.pop() |
||||
|
|
||||
|
// Extract any components which provide data contexts
|
||||
|
const dataProviders = path.filter(component => { |
||||
|
const def = store.actions.components.getDefinition(component._component) |
||||
|
return def?.dataProvider |
||||
|
}) |
||||
|
|
||||
|
let contexts = [] |
||||
|
dataProviders.forEach(provider => { |
||||
|
if (!provider.datasource) { |
||||
|
return |
||||
|
} |
||||
|
const { schema, table } = getSchemaForDatasource(provider.datasource) |
||||
|
Object.entries(schema).forEach(([key, schema]) => { |
||||
|
// Replace certain bindings with a new property to help display components
|
||||
|
let runtimeBoundKey = key |
||||
|
if (schema.type === "link") { |
||||
|
runtimeBoundKey = `${key}_count` |
||||
|
} else if (schema.type === "attachment") { |
||||
|
runtimeBoundKey = `${key}_first` |
||||
|
} |
||||
|
|
||||
|
contexts.push({ |
||||
|
type: "context", |
||||
|
runtimeBinding: `${provider._id}.${runtimeBoundKey}`, |
||||
|
readableBinding: `${provider._instanceName}.${table.name}.${key}`, |
||||
|
fieldSchema: schema, |
||||
|
providerId: provider._id, |
||||
|
}) |
||||
|
}) |
||||
|
}) |
||||
|
return contexts |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Gets all bindable components. These are form components which allow their |
||||
|
* values to be bound to. |
||||
|
*/ |
||||
|
export const getBindableComponents = rootComponent => { |
||||
|
if (!rootComponent) { |
||||
|
return [] |
||||
|
} |
||||
|
const componentSelector = component => { |
||||
|
const type = component._component |
||||
|
const definition = store.actions.components.getDefinition(type) |
||||
|
return definition.bindable |
||||
|
} |
||||
|
const components = findAllMatchingComponents(rootComponent, componentSelector) |
||||
|
console.log(components) |
||||
|
|
||||
|
return [] |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Gets a schema for a datasource object. |
||||
|
*/ |
||||
|
const getSchemaForDatasource = datasource => { |
||||
|
const tables = get(backendUiStore).tables |
||||
|
const { type } = datasource |
||||
|
const table = tables.find(table => table._id === datasource.tableId) |
||||
|
let schema = {} |
||||
|
if (table) { |
||||
|
if (type === "table") { |
||||
|
schema = table.schema ?? {} |
||||
|
} else if (type === "view") { |
||||
|
schema = table.views?.[datasource.name]?.schema ?? {} |
||||
|
} else if (type === "link") { |
||||
|
schema = table.schema ?? {} |
||||
|
} |
||||
|
} |
||||
|
return { schema, table } |
||||
|
} |
||||
@ -1,48 +0,0 @@ |
|||||
import { walkProps } from "./storeUtils" |
|
||||
import { get_capitalised_name } from "../helpers" |
|
||||
import { get } from "svelte/store" |
|
||||
import { allScreens } from "builderStore" |
|
||||
import { FrontendTypes } from "../constants" |
|
||||
import { currentAsset } from "." |
|
||||
|
|
||||
export default function(component, state) { |
|
||||
const capitalised = get_capitalised_name( |
|
||||
component.name || component._component |
|
||||
) |
|
||||
|
|
||||
const matchingComponents = [] |
|
||||
|
|
||||
const findMatches = props => { |
|
||||
walkProps(props, c => { |
|
||||
const thisInstanceName = get_capitalised_name(c._instanceName) |
|
||||
if ((thisInstanceName || "").startsWith(capitalised)) { |
|
||||
matchingComponents.push(thisInstanceName) |
|
||||
} |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
// check layouts first
|
|
||||
for (let layout of state.layouts) { |
|
||||
findMatches(layout.props) |
|
||||
} |
|
||||
|
|
||||
// if viewing screen, check current screen for duplicate
|
|
||||
if (state.currentFrontEndType === FrontendTypes.SCREEN) { |
|
||||
findMatches(get(currentAsset).props) |
|
||||
} else { |
|
||||
// viewing a layout - need to find against all screens
|
|
||||
for (let screen of get(allScreens)) { |
|
||||
findMatches(screen.props) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
let index = 1 |
|
||||
let name |
|
||||
while (!name) { |
|
||||
const tryName = `${capitalised || "Copy"} ${index}` |
|
||||
if (!matchingComponents.includes(tryName)) name = tryName |
|
||||
index++ |
|
||||
} |
|
||||
|
|
||||
return name |
|
||||
} |
|
||||
@ -1,12 +1,32 @@ |
|||||
[ |
[ |
||||
|
"container", |
||||
|
"grid", |
||||
|
"list", |
||||
{ |
{ |
||||
"name": "Category", |
"name": "Cards", |
||||
"icon": "ri-file-edit-line", |
"icon": "ri-file-edit-line", |
||||
"children": [ |
"children": [ |
||||
"container" |
"card", |
||||
|
"stackedlist" |
||||
] |
] |
||||
}, |
}, |
||||
"grid", |
{ |
||||
"screenslot", |
"name": "Forms", |
||||
"button" |
"icon": "ri-file-edit-line", |
||||
|
"children": [ |
||||
|
"form", |
||||
|
"input", |
||||
|
"richtext", |
||||
|
"datepicker" |
||||
|
] |
||||
|
}, |
||||
|
"button", |
||||
|
"text", |
||||
|
{ |
||||
|
"name": "Other", |
||||
|
"icon": "ri-file-edit-line", |
||||
|
"children": [ |
||||
|
"screenslot" |
||||
|
] |
||||
|
} |
||||
] |
] |
||||
Loading…
Reference in new issue