diff --git a/.prettierignore b/.prettierignore
index 3f27c3f9da..13f012fd89 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -1,4 +1,4 @@
-packages/builder/src/userInterface/CurrentItemPreview.svelte
+packages/builder/src/components/design/AppPreview/CurrentItemPreview.svelte
public
dist
packages/server/builder
diff --git a/packages/builder/src/builderStore/dataBinding.js b/packages/builder/src/builderStore/dataBinding.js
new file mode 100644
index 0000000000..647fc194a3
--- /dev/null
+++ b/packages/builder/src/builderStore/dataBinding.js
@@ -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 }
+}
diff --git a/packages/builder/src/builderStore/getNewComponentName.js b/packages/builder/src/builderStore/getNewComponentName.js
deleted file mode 100644
index 98ca05b827..0000000000
--- a/packages/builder/src/builderStore/getNewComponentName.js
+++ /dev/null
@@ -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
-}
diff --git a/packages/builder/src/builderStore/replaceBindings.js b/packages/builder/src/builderStore/replaceBindings.js
index 0bf9f485c9..a82e85d165 100644
--- a/packages/builder/src/builderStore/replaceBindings.js
+++ b/packages/builder/src/builderStore/replaceBindings.js
@@ -30,9 +30,10 @@ export function runtimeToReadableBinding(bindableProperties, textWithBindings) {
const binding = bindableProperties.find(({ runtimeBinding }) => {
return v === `{{ ${runtimeBinding} }}`
})
- if (binding) {
- temp = temp.replace(v, `{{ ${binding.readableBinding} }}`)
- }
+ temp = temp.replace(
+ v,
+ `{{ ${binding?.readableBinding ?? "Invalid binding"} }}`
+ )
})
return temp
diff --git a/packages/builder/src/builderStore/store/frontend.js b/packages/builder/src/builderStore/store/frontend.js
index fcb1224608..4f9f343469 100644
--- a/packages/builder/src/builderStore/store/frontend.js
+++ b/packages/builder/src/builderStore/store/frontend.js
@@ -497,7 +497,10 @@ export const getFrontendStore = () => {
const path = findComponentPath(selectedAsset.props, component._id) || []
// Remove root entry since it's the screen or layout
- return path.slice(1).join("/")
+ return path
+ .slice(1)
+ .map(component => component._id)
+ .join("/")
},
links: {
save: async (url, title) => {
diff --git a/packages/builder/src/builderStore/storeUtils.js b/packages/builder/src/builderStore/storeUtils.js
index 0d13522711..00f5a209a3 100644
--- a/packages/builder/src/builderStore/storeUtils.js
+++ b/packages/builder/src/builderStore/storeUtils.js
@@ -40,22 +40,45 @@ export const findComponentParent = (rootComponent, id, parentComponent) => {
*/
export const findComponentPath = (rootComponent, id, path = []) => {
if (!rootComponent || !id) {
- return null
+ return []
}
if (rootComponent._id === id) {
- return [...path, id]
+ return [...path, rootComponent]
}
if (!rootComponent._children) {
- return null
+ return []
}
for (const child of rootComponent._children) {
- const newPath = [...path, rootComponent._id]
+ const newPath = [...path, rootComponent]
const childResult = findComponentPath(child, id, newPath)
- if (childResult != null) {
+ if (childResult?.length) {
return childResult
}
}
- return null
+ return []
+}
+
+/**
+ * Recurses through the component tree and finds all components of a certain
+ * type.
+ */
+export const findAllMatchingComponents = (rootComponent, selector) => {
+ if (!rootComponent || !selector) {
+ return []
+ }
+ let components = []
+ if (rootComponent._children) {
+ rootComponent._children.forEach(child => {
+ components = [
+ ...components,
+ ...findAllMatchingComponents(child, selector),
+ ]
+ })
+ }
+ if (selector(rootComponent)) {
+ components.push(rootComponent)
+ }
+ return components.reverse()
}
/**
diff --git a/packages/builder/src/components/design/AppPreview/componentStructure.json b/packages/builder/src/components/design/AppPreview/componentStructure.json
index f6c7a7ab2d..2fc80f6829 100644
--- a/packages/builder/src/components/design/AppPreview/componentStructure.json
+++ b/packages/builder/src/components/design/AppPreview/componentStructure.json
@@ -1,12 +1,32 @@
[
+ "container",
+ "grid",
+ "list",
{
- "name": "Category",
+ "name": "Cards",
"icon": "ri-file-edit-line",
"children": [
- "container"
+ "card",
+ "stackedlist"
]
},
- "grid",
- "screenslot",
- "button"
+ {
+ "name": "Forms",
+ "icon": "ri-file-edit-line",
+ "children": [
+ "form",
+ "input",
+ "richtext",
+ "datepicker"
+ ]
+ },
+ "button",
+ "text",
+ {
+ "name": "Other",
+ "icon": "ri-file-edit-line",
+ "children": [
+ "screenslot"
+ ]
+ }
]
\ No newline at end of file
diff --git a/packages/builder/src/components/design/NavigationPanel/ComponentNavigationTree/dragDropStore.js b/packages/builder/src/components/design/NavigationPanel/ComponentNavigationTree/dragDropStore.js
index 04c1142397..37c2f9867e 100644
--- a/packages/builder/src/components/design/NavigationPanel/ComponentNavigationTree/dragDropStore.js
+++ b/packages/builder/src/components/design/NavigationPanel/ComponentNavigationTree/dragDropStore.js
@@ -77,18 +77,16 @@ export default function() {
// Stop if the target and source are the same
if (state.targetComponent === state.dragged) {
- console.log("same component")
return
}
// Stop if the target or source are null
if (!state.targetComponent || !state.dragged) {
- console.log("null component")
return
}
// Stop if the target is a child of source
const path = findComponentPath(state.dragged, state.targetComponent._id)
- if (path?.includes(state.targetComponent._id)) {
- console.log("target is child of course")
+ const ids = path.map(component => component._id)
+ if (ids.includes(state.targetComponent._id)) {
return
}
diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/PropertyControl.svelte b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/PropertyControl.svelte
index f244577102..717c6d39b8 100644
--- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/PropertyControl.svelte
+++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/PropertyControl.svelte
@@ -1,8 +1,8 @@
@@ -84,13 +65,13 @@
handleChange(key, val)}
- onChange={val => handleChange(key, val)}
+ value={safeValue}
+ on:change={handleChange}
+ onChange={handleChange}
{...props}
name={key} />
- {#if bindable && !key.startsWith('_') && control === Input}
+ {#if bindable && !key.startsWith('_') && type === 'text'}
{/if}
-{#if control == Input}
+{#if type === 'text'}
handleChange(temporaryBindableValue)}
bind:this={dropdown}
{anchor}
align="right">
(temporaryBindableValue = e.detail)}
{bindableProperties} />
diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/PropertyGroup.svelte b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/PropertyGroup.svelte
index 315dc9be9e..d97ee7d18d 100644
--- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/PropertyGroup.svelte
+++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/PropertyGroup.svelte
@@ -22,11 +22,12 @@
{#each properties as prop}
onStyleChanged(styleCategory, key, value)}
+ onChange={value => onStyleChanged(styleCategory, prop.key, value)}
props={{ options: prop.options, placeholder: prop.placeholder }} />
{/each}
diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/TableViewSelect.svelte b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/TableViewSelect.svelte
index f7a65965e1..f66333e4c0 100644
--- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/TableViewSelect.svelte
+++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/TableViewSelect.svelte
@@ -2,7 +2,7 @@
import { Icon, DropdownMenu, Heading } from "@budibase/bbui"
import { createEventDispatcher } from "svelte"
import { store, backendUiStore, currentAsset } from "builderStore"
- import fetchBindableProperties from "builderStore/fetchBindableProperties"
+ import { getBindableProperties } from "builderStore/dataBinding"
const dispatch = createEventDispatcher()
let anchorRight, dropdownRight
@@ -31,18 +31,17 @@
return [...acc, ...viewsArr]
}, [])
- $: bindableProperties = fetchBindableProperties({
- componentInstanceId: $store.selectedComponentId,
- components: $store.components,
- screen: $currentAsset,
- tables: $backendUiStore.tables,
- })
+ $: bindableProperties = getBindableProperties(
+ $currentAsset.props,
+ $store.selectedComponentId
+ )
+ $: console.log(bindableProperties)
$: links = bindableProperties
.filter(x => x.fieldSchema?.type === "link")
.map(property => {
return {
- providerId: property.instance._id,
+ providerId: property.providerId,
label: property.readableBinding,
fieldName: property.fieldSchema.name,
name: `all_${property.fieldSchema.tableId}`,
diff --git a/packages/builder/src/components/design/PropertiesPanel/SettingsView.svelte b/packages/builder/src/components/design/PropertiesPanel/SettingsView.svelte
index 0a88af8b0d..bfb3c121c7 100644
--- a/packages/builder/src/components/design/PropertiesPanel/SettingsView.svelte
+++ b/packages/builder/src/components/design/PropertiesPanel/SettingsView.svelte
@@ -25,6 +25,13 @@
export let onScreenPropChange = () => {}
export let showDisplayName = false
+ const layoutDefinition = []
+ const screenDefinition = [
+ { key: "description", label: "Description", control: Input },
+ { key: "routing.route", label: "Route", control: Input },
+ { key: "routing.roleId", label: "Access", control: RoleSelect },
+ { key: "layoutId", label: "Layout", control: LayoutSelect },
+ ]
const assetProps = [
"title",
"description",
@@ -34,12 +41,15 @@
]
$: settings = componentDefinition?.settings ?? []
+ $: isLayout = assetInstance && assetInstance.favicon
+ $: assetDefinition = isLayout ? layoutDefinition : screenDefinition
const controlMap = {
text: Input,
select: OptionSelect,
datasource: TableViewSelect,
- detailURL: DetailScreenSelect,
+ screen: ScreenSelect,
+ detailScreen: DetailScreenSelect,
boolean: Checkbox,
number: Input,
event: EventsEditor,
@@ -48,17 +58,6 @@
return controlMap[type]
}
- const propExistsOnComponentDef = prop =>
- assetProps.includes(prop) || prop in (componentDefinition?.props ?? {})
-
- const layoutDefinition = []
- const screenDefinition = [
- { key: "description", label: "Description", control: Input },
- { key: "routing.route", label: "Route", control: Input },
- { key: "routing.roleId", label: "Access", control: RoleSelect },
- { key: "layoutId", label: "Layout", control: LayoutSelect },
- ]
-
const canRenderControl = setting => {
const control = getControl(setting?.type)
if (!control) {
@@ -70,29 +69,27 @@
return true
}
- $: isLayout = assetInstance && assetInstance.favicon
- $: assetDefinition = isLayout ? layoutDefinition : screenDefinition
-
- const onInstanceNameChange = (_, name) => {
+ const onInstanceNameChange = name => {
onChange("_instanceName", name)
}
{#if assetInstance}
- {#each assetDefinition as def}
+ {#each assetDefinition as def (`${componentInstance._id}-${def.key}`)}
+ onChange={val => onScreenPropChange(def.key, val)} />
{/each}
{/if}
{#if showDisplayName}
0}
- {#each settings as setting}
+ {#each settings as setting (`${componentInstance._id}-${setting.key}`)}
{#if canRenderControl(setting)}
onChange(setting.key, val)}
props={{ options: setting.options }} />
{/if}
{/each}
diff --git a/packages/builder/src/components/design/temporaryPanelStructure.js b/packages/builder/src/components/design/temporaryPanelStructure.js
index f5d0f2ca0a..687d716bd6 100644
--- a/packages/builder/src/components/design/temporaryPanelStructure.js
+++ b/packages/builder/src/components/design/temporaryPanelStructure.js
@@ -1,225 +1,10 @@
-import Input from "./PropertiesPanel/PropertyControls/Input.svelte"
-import OptionSelect from "./PropertiesPanel/PropertyControls/OptionSelect.svelte"
-import MultiTableViewFieldSelect from "./PropertiesPanel/PropertyControls/MultiTableViewFieldSelect.svelte"
-import Checkbox from "../common/Checkbox.svelte"
-import TableSelect from "components/userInterface/PropertyControls/TableSelect.svelte"
-import TableViewSelect from "components/userInterface/PropertyControls/TableViewSelect.svelte"
-import TableViewFieldSelect from "components/userInterface/PropertyControls/TableViewFieldSelect.svelte"
-import Event from "components/userInterface/PropertyControls/EventsEditor/EventPropertyControl.svelte"
-import ScreenSelect from "components/userInterface/PropertyControls/ScreenSelect.svelte"
-import DetailScreenSelect from "components/userInterface/PropertyControls/DetailScreenSelect.svelte"
-import { IconSelect } from "components/userInterface/PropertyControls/IconSelect"
-import Colorpicker from "@budibase/colorpicker"
-
-import { all } from "./propertyCategories.js"
-/*
-{ label: "N/A ", value: "N/A" },
-{ label: "Flex", value: "flex" },
-{ label: "Inline Flex", value: "inline-flex" },
-*/
-
export default {
categories: [
- {
- name: "Repeater",
- _component: "@budibase/standard-components/list",
- description: "Renders all children once per row, of a given table",
- icon: "ri-list-check-2",
- properties: {
- design: { ...all },
- settings: [
- {
- label: "Data",
- key: "datasource",
- control: TableViewSelect,
- },
- ],
- },
- children: [],
- },
-
- {
- name: "Form",
- icon: "ri-file-edit-line",
- isCategory: true,
- children: [
- {
- _component: "@budibase/standard-components/form",
- name: "Form",
- icon: "ri-file-edit-line",
- properties: {
- design: { ...all },
- settings: [],
- },
- },
- {
- _component: "@budibase/standard-components/textfield",
- name: "Text Field",
- description:
- "A textfield component that allows the user to input text.",
- icon: "ri-edit-box-line",
- properties: {
- design: { ...all },
- settings: [
- { label: "Label", key: "label", control: Input },
- {
- label: "Type",
- key: "type",
- control: OptionSelect,
- options: ["text", "password"],
- },
- ],
- },
- },
- // {
- // _component: "@budibase/standard-components/richtext",
- // name: "Rich Text",
- // description:
- // "A component that allows the user to enter long form text.",
- // icon: "ri-edit-box-line",
- // properties: {
- // design: { ...all },
- // settings: [],
- // },
- // },
- // {
- // _component: "@budibase/standard-components/datepicker",
- // name: "Date Picker",
- // description: "A basic date picker component",
- // icon: "ri-calendar-line",
- // children: [],
- // properties: {
- // design: { ...all },
- // settings: [
- // { label: "Placeholder", key: "placeholder", control: Input },
- // ],
- // },
- // },
- ],
- },
{
name: "Card",
icon: "ri-archive-drawer-line",
isCategory: true,
children: [
- {
- _component: "@budibase/standard-components/stackedlist",
- name: "Stacked List",
- icon: "ri-archive-drawer-line",
- description:
- "A basic card component that can contain content and actions.",
- children: [],
- properties: {
- design: { ...all },
- settings: [
- {
- label: "Image",
- key: "imageUrl",
- control: Input,
- placeholder: "{{{context.Image}}}",
- },
- {
- label: "Heading",
- key: "heading",
- control: Input,
- placeholder: "{{context.Heading}}",
- },
- {
- label: "Text 1",
- key: "text1",
- control: Input,
- placeholder: "{{context.Text 1}}",
- },
- {
- label: "Text 2",
- key: "text2",
- control: Input,
- placeholder: "{{context.Text 2}}",
- },
- {
- label: "Text 3",
- key: "text3",
- control: Input,
- placeholder: "{{context.Text 3}}",
- },
- {
- label: "Link URL",
- key: "destinationUrl",
- control: ScreenSelect,
- placeholder: "/table/_id",
- },
- ],
- },
- },
- {
- _component: "@budibase/standard-components/card",
- name: "Vertical",
- description:
- "A basic card component that can contain content and actions.",
- icon: "ri-layout-column-line",
- children: [],
- properties: {
- design: { ...all },
- settings: [
- {
- label: "Image",
- key: "imageUrl",
- control: Input,
- placeholder: "Image",
- },
- {
- label: "Heading",
- key: "heading",
- control: Input,
- placeholder: "Heading",
- },
- {
- label: "Description",
- key: "description",
- control: Input,
- placeholder: "Description",
- },
- {
- label: "Link Text",
- key: "linkText",
- control: Input,
- placeholder: "Link Text",
- },
- {
- label: "Link Url",
- key: "linkUrl",
- control: ScreenSelect,
- placeholder: "Link URL",
- },
- {
- label: "Link Color",
- key: "linkColor",
- control: Colorpicker,
- defaultValue: "#000",
- },
- {
- label: "Hover Color",
- key: "linkHoverColor",
- control: Colorpicker,
- defaultValue: "#222",
- },
- {
- label: "Image Height",
- key: "imageHeight",
- control: OptionSelect,
- options: ["12rem", "16rem", "20rem", "24rem"],
- placeholder: "Image Height",
- },
- {
- label: "Card Width",
- key: "cardWidth",
- control: OptionSelect,
- options: ["16rem", "20rem", "24rem"],
- placeholder: "Card Width",
- },
- ],
- },
- },
{
_component: "@budibase/standard-components/cardhorizontal",
name: "Horizontal",
@@ -957,40 +742,7 @@ export default {
],
},
},
- {
- _component: "@budibase/standard-components/text",
- name: "Paragraph",
- description: "A component for displaying paragraph text.",
- icon: "ri-paragraph",
- properties: {
- design: { ...all },
- settings: [
- {
- label: "Text",
- key: "text",
- control: Input,
- },
- {
- label: "Type",
- key: "type",
- control: OptionSelect,
- options: [
- "none",
- "bold",
- "strong",
- "italic",
- "emphasis",
- "mark",
- "small",
- "del",
- "ins",
- "sub",
- "sup",
- ],
- },
- ],
- },
- },
+
{
_component: "@budibase/standard-components/image",
name: "Image",
diff --git a/packages/standard-components/components.json b/packages/standard-components/components.json
index c4304235fd..a0c8793c07 100644
--- a/packages/standard-components/components.json
+++ b/packages/standard-components/components.json
@@ -36,57 +36,6 @@
"logon"
]
},
- "input": {
- "name": "Textfield",
- "bindable": "value",
- "description": "An HTML input",
- "props": {
- "type": {
- "type": "options",
- "options": [
- "text",
- "password",
- "checkbox",
- "color",
- "date",
- "datetime-local",
- "email",
- "file",
- "hidden",
- "image",
- "month",
- "number",
- "radio",
- "range",
- "reset",
- "search",
- "submit",
- "tel",
- "time",
- "week"
- ],
- "default": "text"
- }
- },
- "tags": [
- "form"
- ]
- },
- "text": {
- "name": "Text",
- "description": "stylable block of text",
- "props": {
- "text": "string",
- "type": {
- "type": "string",
- "default": "none"
- }
- },
- "tags": [
- "div",
- "container"
- ]
- },
"richtext": {
"name": "Rich Text",
"description": "A component that allows the user to enter long form text.",
@@ -109,40 +58,7 @@
}
},
- "dataform": {
- "name": "Form",
- "description": "an HTML table that fetches data from a table or view and displays it.",
- "data": true,
- "props": {}
- },
- "dataformwide": {
- "name": "Form Wide",
- "description": "an HTML table that fetches data from a table or view and displays it.",
- "data": true,
- "props": {}
- },
- "list": {
- "name": "Repeater",
- "description": "A configurable data list that attaches to your backend tables.",
- "context": "datasource",
- "children": true,
- "data": true,
- "props": {
- "datasource": "tables"
- }
- },
- "stackedlist": {
- "name": "Stacked List",
- "description": "A stacked list component for displaying information",
- "props": {
- "imageUrl": "string",
- "heading": "string",
- "text1": "string",
- "text2": "string",
- "text3": "string",
- "destinationUrl": "string"
- }
- },
+
"rowdetail": {
"name": "Row Detail",
"description": "Loads a row, using an ID in the url",
@@ -165,43 +81,6 @@
"table": "tables"
}
},
- "card": {
- "name": "Card",
- "props": {
- "imageUrl": "string",
- "heading": "string",
- "description": "string",
- "linkText": "string",
- "linkUrl": "string",
- "linkColor": {
- "type": "string",
- "default": "#000"
- },
- "linkHoverColor": {
- "type": "string",
- "default": "#000"
- },
- "imageHeight": {
- "type": "options",
- "default": "20rem",
- "options": [
- "12rem",
- "16rem",
- "20rem",
- "24rem"
- ]
- },
- "cardWidth": {
- "type": "options",
- "default": "20rem",
- "options": [
- "16rem",
- "20rem",
- "24rem"
- ]
- }
- }
- },
"cardstat": {
"name": "Stat Card",
"props": {
diff --git a/packages/standard-components/manifest.json b/packages/standard-components/manifest.json
index 3137c43c1e..52ddc24d27 100644
--- a/packages/standard-components/manifest.json
+++ b/packages/standard-components/manifest.json
@@ -42,7 +42,7 @@
"key": "datasource"
},
{
- "type": "detailURL",
+ "type": "detailScreen",
"label": "Detail URL",
"key": "detailUrl"
},
@@ -90,8 +90,7 @@
{
"type": "boolean",
"label": "Disabled",
- "key": "disabled",
- "valueKey": "checked"
+ "key": "disabled"
},
{
"type": "event",
@@ -99,5 +98,176 @@
"key": "onClick"
}
]
+ },
+ "list": {
+ "name": "Repeater",
+ "description": "A configurable data list that attaches to your backend tables.",
+ "icon": "ri-list-check-2",
+ "styleable": true,
+ "hasChildren": true,
+ "dataProvider": true,
+ "settings": [
+ {
+ "type": "datasource",
+ "label": "Data",
+ "key": "datasource"
+ }
+ ]
+ },
+ "form": {
+ "name": "Form",
+ "icon": "ri-file-edit-line",
+ "styleable": true
+ },
+ "input": {
+ "name": "Text Field",
+ "description": "A textfield component that allows the user to input text.",
+ "icon": "ri-edit-box-line",
+ "styleable": true,
+ "bindable": true,
+ "settings": [
+ {
+ "type": "text",
+ "label": "Label",
+ "key": "label"
+ },
+ {
+ "label": "Type",
+ "key": "type",
+ "defaultValue": "text",
+ "options": ["text", "password"]
+ }
+ ]
+ },
+ "richtext": {
+ "name": "Rich Text",
+ "description": "A component that allows the user to enter long form text.",
+ "icon": "ri-edit-box-line",
+ "styleable": true,
+ "bindable": true
+ },
+ "datepicker": {
+ "name": "Date Picker",
+ "description": "A basic date picker component",
+ "icon": "ri-calendar-line",
+ "styleable": true,
+ "bindable": true,
+ "settings": [
+ {
+ "type": "text",
+ "label": "Placeholder",
+ "key": "placeholder"
+ }
+ ]
+ },
+ "stackedlist": {
+ "name": "Stacked List",
+ "icon": "ri-archive-drawer-line",
+ "description": "A basic card component that can contain content and actions.",
+ "styleable": true,
+ "settings": [
+ {
+ "type": "text",
+ "label": "Image",
+ "key": "imageUrl"
+ },
+ {
+ "type": "text",
+ "label": "Heading",
+ "key": "heading"
+ },
+ {
+ "type": "text",
+ "label": "Text 1",
+ "key": "text1"
+ },
+ {
+ "type": "text",
+ "label": "Text 2",
+ "key": "text2"
+ },
+ {
+ "type": "text",
+ "label": "Text 3",
+ "key": "text3"
+ },
+ {
+ "type": "screen",
+ "label": "Link URL",
+ "key": "destinationUrl"
+ }
+ ]
+ },
+ "card": {
+ "name": "Vertical Card",
+ "description": "A basic card component that can contain content and actions.",
+ "icon": "ri-layout-column-line",
+ "styleable": true,
+ "settings": [
+ {
+ "type": "text",
+ "label": "Image",
+ "key": "imageUrl"
+ },
+ {
+ "type": "text",
+ "label": "Heading",
+ "key": "heading"
+ },
+ {
+ "type": "text",
+ "label": "Description",
+ "key": "description"
+ },
+ {
+ "type": "text",
+ "label": "Link Text",
+ "key": "linkText"
+ },
+ {
+ "type": "screen",
+ "label": "Link Url",
+ "key": "linkUrl"
+ },
+ {
+ "type": "color",
+ "label": "Link Color",
+ "key": "linkColor",
+ "defaultValue": "#000"
+ },
+ {
+ "type": "color",
+ "label": "Hover Color",
+ "key": "linkHoverColor",
+ "defaultValue": "#222"
+ },
+ {
+ "type": "select",
+ "label": "Image Height",
+ "key": "imageHeight",
+ "options": ["auto", "12rem", "16rem", "20rem", "24rem"],
+ "defaultValue": "auto"
+ },
+ {
+ "type": "select",
+ "label": "Card Width",
+ "key": "cardWidth",
+ "options": ["16rem", "20rem", "24rem"],
+ "defaultValue": "20rem"
+ }
+ ]
+ },
+ "text": {
+ "name": "Paragraph",
+ "description": "A component for displaying paragraph text.",
+ "icon": "ri-paragraph",
+ "styleable": true,
+ "settings": [
+ {
+ "type": "text",
+ "label": "Text",
+ "key": "text"
+ }
+ ]
}
}
diff --git a/packages/standard-components/src/Card.svelte b/packages/standard-components/src/Card.svelte
index 74121590e3..3252c7a19f 100644
--- a/packages/standard-components/src/Card.svelte
+++ b/packages/standard-components/src/Card.svelte
@@ -1,6 +1,5 @@
-
+
{#if showImage}
diff --git a/packages/standard-components/src/index.js b/packages/standard-components/src/index.js
index bf1dbceb4e..4a0d40bbf8 100644
--- a/packages/standard-components/src/index.js
+++ b/packages/standard-components/src/index.js
@@ -9,23 +9,24 @@ export { default as container } from "./Container.svelte"
export { default as grid } from "./grid/Component.svelte"
export { default as screenslot } from "./ScreenSlot.svelte"
export { default as button } from "./Button.svelte"
+export { default as input } from "./Input.svelte"
+export { default as richtext } from "./RichText.svelte"
+export { default as list } from "./List.svelte"
+export { default as stackedlist } from "./StackedList.svelte"
+export { default as card } from "./Card.svelte"
+export { default as form } from "./DataFormWide.svelte"
+export { default as datepicker } from "./DatePicker.svelte"
+export { default as text } from "./Text.svelte"
-// export { default as text } from "./Text.svelte"
// export { default as heading } from "./Heading.svelte"
-// export { default as input } from "./Input.svelte"
-// export { default as richtext } from "./RichText.svelte"
// export { default as login } from "./Login.svelte"
// export { default as link } from "./Link.svelte"
// export { default as image } from "./Image.svelte"
// export { default as navigation } from "./Navigation.svelte"
-// export { default as list } from "./List.svelte"
// export { default as embed } from "./Embed.svelte"
-// export { default as stackedlist } from "./StackedList.svelte"
-// export { default as card } from "./Card.svelte"
// export { default as cardhorizontal } from "./CardHorizontal.svelte"
// export { default as cardstat } from "./CardStat.svelte"
// export { default as rowdetail } from "./RowDetail.svelte"
// export { default as newrow } from "./NewRow.svelte"
-// export { default as datepicker } from "./DatePicker.svelte"
// export { default as icon } from "./Icon.svelte"
// export * from "./charts"