diff --git a/packages/builder/src/components/common/Colorpicker.svelte b/packages/builder/src/components/common/Colorpicker.svelte
index 9cc3b3926a..28ca92554d 100644
--- a/packages/builder/src/components/common/Colorpicker.svelte
+++ b/packages/builder/src/components/common/Colorpicker.svelte
@@ -2,7 +2,7 @@
import { onMount, beforeUpdate, afterUpdate } from "svelte"
export let value = null
- export let onChanged = () => {}
+ export let onChange = () => {}
export let swatches = []
let picker
@@ -58,13 +58,10 @@
onMount(() => {
getRecentColors()
createPicker()
-
- picker.on("save", (colour, instance) => {
- let color = colour.toHEXA().toString()
- onChanged(color)
- setRecentColor(color)
- picker.hide()
- })
+ return () => {
+ picker.destroyAndRemove()
+ picker = null
+ }
})
diff --git a/packages/builder/src/components/common/Input.svelte b/packages/builder/src/components/common/Input.svelte
index 50b07f41f0..bc6741e0d8 100644
--- a/packages/builder/src/components/common/Input.svelte
+++ b/packages/builder/src/components/common/Input.svelte
@@ -1,8 +1,11 @@
-
+
diff --git a/packages/builder/src/components/userInterface/ComponentPropertiesPanel.svelte b/packages/builder/src/components/userInterface/ComponentPropertiesPanel.svelte
index 76d0c5605c..5c07895792 100644
--- a/packages/builder/src/components/userInterface/ComponentPropertiesPanel.svelte
+++ b/packages/builder/src/components/userInterface/ComponentPropertiesPanel.svelte
@@ -13,23 +13,60 @@
import LayoutEditor from "./LayoutEditor.svelte"
import EventsEditor from "./EventsEditor"
- let current_view = "props"
- let codeEditor
+ import panelStructure from "./temporaryPanelStructure.js"
+ import CategoryTab from "./CategoryTab.svelte"
+ import DesignView from "./DesignView.svelte"
+
+ let current_view = "design"
+ let codeEditor
+ let flattenedPanel = flattenComponents(panelStructure.categories)
+ let categories = [
+ { name: "Design" },
+ { name: "Settings" },
+ { name: "Actions" },
+ ]
+ let selectedCategory = categories[0]
- $: component = $store.currentComponentInfo
- $: originalName = component.name
- $: name =
- $store.currentView === "detail"
- ? $store.currentPreviewItem.name
- : component._component
- $: description = component.description
$: components = $store.components
+ $: componentInstance = $store.currentComponentInfo
+ $: componentDefinition = $store.components.find(
+ c => c.name === componentInstance._component
+ )
+
+ $: panelDefinition = flattenedPanel.find(
+ //use for getting controls for each component property
+ c => c._component === componentInstance._component
+ )
+
+ // SCREEN PROPS =============================================
$: screen_props =
$store.currentFrontEndType === "page"
? getProps($store.currentPreviewItem, ["name", "favicon"])
: getProps($store.currentPreviewItem, ["name", "description", "route"])
const onStyleChanged = store.setComponentStyle
+ const onPropChanged = store.onPropChanged
+
+ function walkProps(component, action) {
+ action(component)
+ if (component.children) {
+ for (let child of component.children) {
+ walkProps(child, action)
+ }
+ }
+ }
+
+ function flattenComponents(props) {
+ const components = []
+ props.forEach(comp =>
+ walkProps(comp, c => {
+ if ("_component" in c) {
+ components.push(c)
+ }
+ })
+ )
+ return components
+ }
function getProps(obj, keys) {
return keys.map((key, i) => [key, obj[key], obj.props._id + i])
@@ -37,116 +74,31 @@
-
- -
-
-
- -
-
-
- {#if !component._component.startsWith('##')}
- -
-
-
- -
-
-
- {/if}
-
+
+
(selectedCategory = category)}
+ {categories}
+ {selectedCategory} />
+
-
- {#if current_view === 'props'}
- {#if $store.currentView === 'detail'}
- {#each screen_props as [k, v, id] (id)}
-
-
- store.setMetadataProp(k, target.value)} />
-
- {/each}
-
- {:else}
-
- {/if}
- {:else if current_view === 'layout'}
-
- {:else if current_view === 'events'}
-
+ {#if current_view === 'design'}
+
{/if}
-
-
diff --git a/packages/builder/src/components/userInterface/ComponentSelectionList.svelte b/packages/builder/src/components/userInterface/ComponentSelectionList.svelte
index 169c3ab284..3ffab198e9 100644
--- a/packages/builder/src/components/userInterface/ComponentSelectionList.svelte
+++ b/packages/builder/src/components/userInterface/ComponentSelectionList.svelte
@@ -2,6 +2,7 @@
import { splitName } from "./pagesParsing/splitRootComponentName.js"
import components from "./temporaryPanelStructure.js"
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
+ import CategoryTab from "./CategoryTab.svelte"
import {
find,
sortBy,
@@ -36,15 +37,12 @@
-
- {#each categories as category}
- - (selectedCategory = category)}
- class:active={selectedCategory === category}>
- {category.name}
-
- {/each}
-
+
+
(selectedCategory = category)}
+ {selectedCategory}
+ {categories} />
+
selectTab(COMPONENT_SELECTION_TAB)}>
- Components
+ Add
@@ -55,6 +55,7 @@
display: flex;
flex-direction: column;
padding: 20px 0;
+ border-left: solid 1px #e8e8ef;
}
.switcher {
diff --git a/packages/builder/src/components/userInterface/DesignView.svelte b/packages/builder/src/components/userInterface/DesignView.svelte
new file mode 100644
index 0000000000..7fc6af5312
--- /dev/null
+++ b/packages/builder/src/components/userInterface/DesignView.svelte
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
+ {#each propertyGroupNames as groupName}
+
+ {/each}
+
+
+
+
diff --git a/packages/builder/src/components/userInterface/FlatButton.svelte b/packages/builder/src/components/userInterface/FlatButton.svelte
new file mode 100644
index 0000000000..abc4a3dc28
--- /dev/null
+++ b/packages/builder/src/components/userInterface/FlatButton.svelte
@@ -0,0 +1,38 @@
+
+
+ onClick(value || text)}>
+ {#if useIcon}
+
+ {:else}
+ {text}
+ {/if}
+
+
+
diff --git a/packages/builder/src/components/userInterface/FlatButtonGroup.svelte b/packages/builder/src/components/userInterface/FlatButtonGroup.svelte
new file mode 100644
index 0000000000..f16d832c67
--- /dev/null
+++ b/packages/builder/src/components/userInterface/FlatButtonGroup.svelte
@@ -0,0 +1,54 @@
+
+
+
+
+
diff --git a/packages/builder/src/components/userInterface/OptionSelect.svelte b/packages/builder/src/components/userInterface/OptionSelect.svelte
new file mode 100644
index 0000000000..b3cfa5e54b
--- /dev/null
+++ b/packages/builder/src/components/userInterface/OptionSelect.svelte
@@ -0,0 +1,33 @@
+
+
+
diff --git a/packages/builder/src/components/userInterface/PropertyControl.svelte b/packages/builder/src/components/userInterface/PropertyControl.svelte
new file mode 100644
index 0000000000..23dd223de6
--- /dev/null
+++ b/packages/builder/src/components/userInterface/PropertyControl.svelte
@@ -0,0 +1,41 @@
+
+
+
+
+
diff --git a/packages/builder/src/components/userInterface/PropertyGroup.svelte b/packages/builder/src/components/userInterface/PropertyGroup.svelte
new file mode 100644
index 0000000000..2d88557777
--- /dev/null
+++ b/packages/builder/src/components/userInterface/PropertyGroup.svelte
@@ -0,0 +1,101 @@
+
+
+
+
+
(show = !show)}>
+
+
+
+
{capitalize(name)}
+
+
+
+ {#each propertyDefinition as [key, definition]}
+
+
onChange(key, value)}
+ props={{ ...excludeProps(definition, ['control']) }} />
+
+ {/each}
+
+
+
+
+
diff --git a/packages/builder/src/components/userInterface/UserInterfaceRoot.svelte b/packages/builder/src/components/userInterface/UserInterfaceRoot.svelte
new file mode 100644
index 0000000000..13d7a7e2a1
--- /dev/null
+++ b/packages/builder/src/components/userInterface/UserInterfaceRoot.svelte
@@ -0,0 +1,240 @@
+
+
+
+
+
+
+
+
+
+
+ {#if $store.currentFrontEndType === 'screen' || $store.currentFrontEndType === 'page'}
+
+
+
+ {/if}
+
+
+
+
+
+
+ store.deleteComponent(componentToDelete)} />
+
+
diff --git a/packages/builder/src/components/userInterface/propertyCategories.js b/packages/builder/src/components/userInterface/propertyCategories.js
new file mode 100644
index 0000000000..08503b4414
--- /dev/null
+++ b/packages/builder/src/components/userInterface/propertyCategories.js
@@ -0,0 +1,177 @@
+import Input from "../common/Input.svelte"
+import OptionSelect from "./OptionSelect.svelte"
+/*
+ TODO: Allow for default values for all properties
+*/
+
+export const general = {
+ text: { control: Input },
+}
+
+export const layout = {
+ flexDirection: {
+ label: "Direction",
+ control: OptionSelect,
+ initialValue: "columnReverse",
+ options: [
+ { label: "row" },
+ { label: "row-reverse", value: "rowReverse" },
+ { label: "column" },
+ { label: "column-reverse", value: "columnReverse" },
+ ],
+ },
+ justifyContent: { label: "Justify", control: Input },
+ alignItems: { label: "Align", control: Input },
+ flexWrap: {
+ label: "Wrap",
+ control: OptionSelect,
+ options: [{ label: "wrap" }, { label: "no wrap", value: "noWrap" }],
+ },
+}
+
+export const spacing = {
+ padding: { control: Input },
+ margin: { control: Input },
+}
+
+export const size = {
+ width: { control: Input },
+ height: { control: Input },
+ minWidth: { label: "Min W", control: Input },
+ minHeight: { label: "Min H", control: Input },
+ maxWidth: { label: "Max W", control: Input },
+ maxHeight: { label: "Max H", control: Input },
+ overflow: {
+ control: OptionSelect,
+ options: [
+ { label: "visible" },
+ { label: "auto" },
+ { label: "hidden" },
+ { label: "auto" },
+ { label: "scroll" },
+ ],
+ }, //custom
+}
+
+export const position = {
+ position: {
+ control: OptionSelect,
+ options: [
+ { label: "static" },
+ { label: "relative" },
+ { label: "fixed" },
+ { label: "absolute" },
+ { label: "sticky" },
+ ],
+ },
+}
+
+export const typography = {
+ fontFamily: {
+ label: "Font",
+ control: OptionSelect,
+ options: [
+ { label: "initial" },
+ { label: "Times New Roman" },
+ { label: "Georgia" },
+ { label: "Arial" },
+ { label: "Arial Black" },
+ { label: "Comic Sans MS" },
+ { label: "Impact" },
+ { label: "Lucida Sans Unicode" },
+ ],
+ styleBindingProperty: "font-family",
+ },
+ fontWeight: {
+ label: "weight",
+ control: OptionSelect,
+ options: [
+ { label: "normal" },
+ { label: "bold" },
+ { label: "bolder" },
+ { label: "lighter" },
+ ],
+ },
+ fontSize: { label: "size", control: Input },
+ lineHeight: { label: "Line H", control: Input },
+ color: {
+ control: OptionSelect,
+ options: [
+ { label: "black" },
+ { label: "red" },
+ { label: "white" },
+ { label: "blue" },
+ { label: "green" },
+ ],
+ },
+ textAlign: {
+ label: "align",
+ control: OptionSelect,
+ options: [
+ { label: "initial" },
+ { label: "left" },
+ { label: "right" },
+ { label: "center" },
+ { label: "justify" },
+ ],
+ }, //custom
+ textTransform: { label: "transform", control: Input }, //custom
+ fontStyle: { label: "style", control: Input }, //custom
+}
+
+export const background = {
+ backgroundColor: {
+ label: "Background Color",
+ control: OptionSelect,
+ options: [
+ { label: "white" },
+ { label: "red" },
+ { label: "blue" },
+ { label: "green" },
+ { label: "black" },
+ ],
+ },
+ image: { control: Input }, //custom
+}
+
+export const border = {
+ borderRadius: { label: "radius", control: Input },
+ borderWidth: { label: "width", control: Input }, //custom
+ borderColor: { label: "color", control: Input },
+ borderStyle: { label: "style", control: Input },
+}
+
+export const effects = {
+ opacity: { control: Input },
+ rotate: { control: Input },
+ shadow: { control: Input },
+}
+
+export const transitions = {
+ property: { control: Input },
+ duration: { control: Input },
+ ease: { control: Input },
+}
+
+export const all = {
+ general,
+ layout,
+ spacing,
+ size,
+ position,
+ typography,
+ background,
+ border,
+ effects,
+ transitions,
+}
+
+export function excludeProps(props, propsToExclude) {
+ const modifiedProps = {}
+ for (const prop in props) {
+ if (!propsToExclude.includes(prop)) {
+ modifiedProps[prop] = props[prop]
+ }
+ }
+ return modifiedProps
+}
diff --git a/packages/builder/src/components/userInterface/temporaryPanelStructure.js b/packages/builder/src/components/userInterface/temporaryPanelStructure.js
index ce64b37945..918deacfc2 100644
--- a/packages/builder/src/components/userInterface/temporaryPanelStructure.js
+++ b/packages/builder/src/components/userInterface/temporaryPanelStructure.js
@@ -1,3 +1,13 @@
+import {
+ general,
+ layout,
+ typography,
+ border,
+ size,
+ background,
+ all,
+} from "./propertyCategories.js"
+
export default {
categories: [
{
@@ -20,6 +30,7 @@ export default {
icon: "ri-layout-row-fill",
commonProps: {},
children: [],
+ properties: { background, size },
},
{
name: "Text",
@@ -32,23 +43,18 @@ export default {
name: "Headline",
description: "A component for displaying heading text",
icon: "ri-heading",
- props: {
- type: {
- type: "options",
- options: ["h1", "h2", "h3", "h4", "h5", "h6"],
- default: "h1",
- },
- text: "string",
+ properties: {
+ ...all
},
},
{
_component: "@budibase/standard-components/text",
name: "Paragraph",
description: "A component for displaying paragraph text.",
- icon: "ri-paragraph",
- props: {},
- },
- ],
+ icon: 'ri-paragraph',
+ properties: { general, typography },
+ }
+ ]
},
{
name: "Input",
@@ -59,60 +65,58 @@ export default {
{
_component: "@budibase/standard-components/input",
name: "Textfield",
- description:
- "A textfield component that allows the user to input text.",
- icon: "ri-edit-box-line",
- props: {},
+ description: "A textfield component that allows the user to input text.",
+ icon: 'ri-edit-box-line',
+ properties: {}
},
{
_component: "@budibase/standard-components/checkbox",
name: "Checkbox",
description: "A selectable checkbox component",
- icon: "ri-checkbox-line",
- props: {},
+ icon: 'ri-checkbox-line',
+ properties: {}
},
{
_component: "@budibase/standard-components/radiobutton",
name: "Radiobutton",
description: "A selectable radiobutton component",
- icon: "ri-radio-button-line",
- props: {},
+ icon: 'ri-radio-button-line',
+ properties: {}
},
{
_component: "@budibase/standard-components/select",
name: "Select",
- description:
- "A select component for choosing from different options",
- icon: "ri-file-list-line",
- props: {},
- },
- ],
+ description: "A select component for choosing from different options",
+ icon: 'ri-file-list-line',
+ properties: {}
+ }
+ ]
},
{
_component: "@budibase/standard-components/button",
- name: "Button",
- description: "A basic html button that is ready for styling",
- icon: "ri-radio-button-fill",
- commonProps: {},
+ name: 'Button',
+ description: 'A basic html button that is ready for styling',
+ icon: 'ri-radio-button-fill',
children: [],
+ properties: { background, typography, border, size },
},
{
_component: "@budibase/standard-components/icon",
- name: "Icon",
- description: "A basic component for displaying icons",
- icon: "ri-sun-fill",
- commonProps: {},
- children: [],
+ name: 'Icon',
+ description: 'A basic component for displaying icons',
+ icon: 'ri-sun-fill',
+ properties: {},
+ children: []
},
{
_component: "@budibase/standard-components/link",
- name: "Link",
- description: "A basic link component for internal and external links",
- icon: "ri-link",
- commonProps: {},
- children: [],
- },
- ],
+ name: 'Link',
+ description: 'A basic link component for internal and external links',
+ icon: 'ri-link',
+ properties: {},
+ children: []
+ }
+ ]
},
{
name: "Blocks",
@@ -120,21 +124,18 @@ export default {
children: [
{
_component: "@budibase/materialdesign-components/BasicCard",
- name: "Card",
- description:
- "A basic card component that can contain content and actions.",
- icon: "ri-layout-bottom-line",
- commonProps: {},
+ name: 'Card',
+ description: 'A basic card component that can contain content and actions.',
+ icon: 'ri-layout-bottom-line',
children: [],
+ properties: { size, background, border },
},
{
- _component: "@budibase/standard-components/login",
- name: "Login",
- description:
- "A component that automatically generates a login screen for your app.",
- icon: "ri-login-box-fill",
- commonProps: {},
- children: [],
+ name: 'Login',
+ description: 'A component that automatically generates a login screen for your app.',
+ icon: 'ri-login-box-fill',
+ properties: {},
+ children: []
},
{
name: "Navigation Bar",
@@ -142,30 +143,28 @@ export default {
description:
"A component for handling the navigation within your app.",
icon: "ri-navigation-fill",
- commonProps: {},
- children: [],
- },
- ],
+ properties: {},
+ children: []
+ }
+ ]
},
{
name: "Data",
isCategory: true,
children: [
{
- name: "Table",
- _component: "@budibase/materialdesign-components/Datatable",
- description: "A component that generates a table from your data.",
- icon: "ri-archive-drawer-fill",
- commonProps: {},
- children: [],
+ name: 'Table',
+ description: 'A component that generates a table from your data.',
+ icon: 'ri-archive-drawer-fill',
+ properties: {},
+ children: []
},
{
+ name: 'Form',
+ description: 'A component that generates a form from your data.',
+ icon: 'ri-file-edit-fill',
+ properties: {},
_component: "@budibase/materialdesign-components/Form",
- name: "Form",
- description: "A component that generates a form from your data.",
- icon: "ri-file-edit-fill",
- commonProps: {},
- component: "@budibase/materialdesign-components/Form",
template: {
component: "@budibase/materialdesign-components/Form",
description: "Form for saving a record",
diff --git a/packages/standard-components/components.json b/packages/standard-components/components.json
index e3cde3586a..6795a01e57 100644
--- a/packages/standard-components/components.json
+++ b/packages/standard-components/components.json
@@ -386,6 +386,8 @@
},
"backgroundColor": "string",
"color": "string",
+ "height": "string",
+ "width": "string",
"borderWidth": "string",
"borderColor": "string",
"borderStyle": {
@@ -403,7 +405,7 @@
],
"default": "none"
}
- },
+ },
"container": true,
"tags": [
"div",
diff --git a/packages/standard-components/src/Button.svelte b/packages/standard-components/src/Button.svelte
index 3d50c22df5..60c022f4ea 100644
--- a/packages/standard-components/src/Button.svelte
+++ b/packages/standard-components/src/Button.svelte
@@ -3,7 +3,7 @@
import { buildStyle } from "./buildStyle"
export let className = "default"
export let disabled = false
- export let contentText
+ export let text
export let onClick
export let background
export let color
@@ -66,9 +66,7 @@
disabled={disabled || false}
on:click={clickHandler}
style={buttonStyles}>
- {#if !_bb.props._children || _bb.props._children.length === 0}
- {contentText}
- {/if}
+ {#if !_bb.props._children || _bb.props._children.length === 0}{text}{/if}