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 @@
- + + (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 @@
- + + (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 @@ + + +
+ {#each buttonProps as props} +
+ +
+ {/each} +
+ + 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 @@ + + +
+
{label}
+
+ +
+
+ + 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}