From 6a758e3b2d0ea0a5ba8681da26bc1e5ff59fc399 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Thu, 14 Jan 2021 15:39:50 +0000 Subject: [PATCH] Add new data bindings for context bindings and most work for component bindings --- .prettierignore | 2 +- .../builder/src/builderStore/dataBinding.js | 97 +++++++ .../src/builderStore/getNewComponentName.js | 48 ---- .../src/builderStore/replaceBindings.js | 7 +- .../src/builderStore/store/frontend.js | 5 +- .../builder/src/builderStore/storeUtils.js | 35 ++- .../design/AppPreview/componentStructure.json | 30 ++- .../ComponentNavigationTree/dragDropStore.js | 6 +- .../PropertyControls/PropertyControl.svelte | 89 +++---- .../PropertyControls/PropertyGroup.svelte | 3 +- .../PropertyControls/TableViewSelect.svelte | 15 +- .../PropertiesPanel/SettingsView.svelte | 38 ++- .../design/temporaryPanelStructure.js | 250 +----------------- packages/standard-components/components.json | 123 +-------- packages/standard-components/manifest.json | 176 +++++++++++- packages/standard-components/src/Card.svelte | 18 +- packages/standard-components/src/index.js | 15 +- 17 files changed, 415 insertions(+), 542 deletions(-) create mode 100644 packages/builder/src/builderStore/dataBinding.js delete mode 100644 packages/builder/src/builderStore/getNewComponentName.js 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"