diff --git a/packages/bbui/src/ColorPicker/ColorPicker.svelte b/packages/bbui/src/ColorPicker/ColorPicker.svelte index 2f4b79b91a..4d248d6190 100644 --- a/packages/bbui/src/ColorPicker/ColorPicker.svelte +++ b/packages/bbui/src/ColorPicker/ColorPicker.svelte @@ -9,10 +9,10 @@ export let value export let size = "M" + export let spectrumTheme let open = false - $: color = value || "transparent" $: customValue = getCustomValue(value) $: checkColor = getCheckColor(value) @@ -21,7 +21,8 @@ { label: "Grays", colors: [ - "white", + "gray-50", + "gray-75", "gray-100", "gray-200", "gray-300", @@ -31,7 +32,6 @@ "gray-700", "gray-800", "gray-900", - "black", ], }, { @@ -86,7 +86,7 @@ return value } let found = false - const comparisonValue = value.substring(35, value.length - 1) + const comparisonValue = value.substring(28, value.length - 1) for (let category of categories) { found = category.colors.includes(comparisonValue) if (found) { @@ -102,17 +102,19 @@ const getCheckColor = value => { return /^.*(white|(gray-(50|75|100|200|300|400|500)))\)$/.test(value) - ? "black" - : "white" + ? "var(--spectrum-global-color-gray-900)" + : "var(--spectrum-global-color-gray-50)" }
-
(open = true)} - /> +
(open = true)}> +
+
{#if open}
(open = false)} @@ -126,15 +128,19 @@ {#each category.colors as color}
{ - onChange(`var(--spectrum-global-color-static-${color})`) + onChange(`var(--spectrum-global-color-${color})`) }} class="color" - style="background: var(--spectrum-global-color-static-{color}); color: {checkColor};" title={prettyPrint(color)} > - {#if value === `var(--spectrum-global-color-static-${color})`} - - {/if} +
+ {#if value === `var(--spectrum-global-color-${color})`} + + {/if} +
{/each}
@@ -170,12 +176,43 @@ width: 32px; height: 32px; border-radius: 100%; + position: relative; transition: border-color 130ms ease-in-out; - box-shadow: 0 0 0 1px var(--spectrum-global-color-gray-300); + box-shadow: 0 0 0 1px var(--spectrum-global-color-gray-400); } .preview:hover { cursor: pointer; - box-shadow: 0 0 2px 2px var(--spectrum-global-color-gray-300); + box-shadow: 0 0 2px 2px var(--spectrum-global-color-gray-400); + } + .fill { + width: 100%; + height: 100%; + border-radius: 100%; + position: absolute; + top: 0; + left: 0; + display: grid; + place-items: center; + } + .fill.placeholder { + background-position: 0 0, 10px 10px; + background-size: 20px 20px; + background-image: linear-gradient( + 45deg, + #eee 25%, + transparent 25%, + transparent 75%, + #eee 75%, + #eee 100% + ), + linear-gradient( + 45deg, + #eee 25%, + white 25%, + white 75%, + #eee 75%, + #eee 100% + ); } .size--S { width: 20px; @@ -219,8 +256,7 @@ width: 16px; border-radius: 100%; box-shadow: 0 0 0 1px var(--spectrum-global-color-gray-300); - display: grid; - place-items: center; + position: relative; } .color:hover { cursor: pointer; @@ -236,4 +272,8 @@ .category--custom .heading { margin-bottom: var(--spacing-xs); } + + .spectrum-wrapper { + background-color: transparent; + } diff --git a/packages/bbui/src/Form/Core/DatePicker.svelte b/packages/bbui/src/Form/Core/DatePicker.svelte index c8f8bfc6c9..44b77e0bbe 100644 --- a/packages/bbui/src/Form/Core/DatePicker.svelte +++ b/packages/bbui/src/Form/Core/DatePicker.svelte @@ -145,4 +145,7 @@ height: 100vh; z-index: 999; } + :global(.flatpickr-calendar) { + font-family: "Source Sans Pro", sans-serif; + } diff --git a/packages/builder/assets/error.svg b/packages/builder/assets/error.svg new file mode 100644 index 0000000000..4cc1d753c9 --- /dev/null +++ b/packages/builder/assets/error.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/builder/cypress/integration/createBinding.spec.js b/packages/builder/cypress/integration/createBinding.spec.js index 04b7b0d6de..3f85526b11 100644 --- a/packages/builder/cypress/integration/createBinding.spec.js +++ b/packages/builder/cypress/integration/createBinding.spec.js @@ -29,7 +29,7 @@ context("Create Bindings", () => { // The builder preview pages don't have a real URL, so all we can do // is check that we were able to bind to the property, and that the // component exists on the page - cy.getComponent(componentId).should("have.text", "Placeholder text") + cy.getComponent(componentId).should("have.text", "New Paragraph") }) }) diff --git a/packages/builder/src/builderStore/store/frontend.js b/packages/builder/src/builderStore/store/frontend.js index a462167ce2..9746b43103 100644 --- a/packages/builder/src/builderStore/store/frontend.js +++ b/packages/builder/src/builderStore/store/frontend.js @@ -8,7 +8,6 @@ import { selectedComponent, selectedAccessRole, } from "builderStore" -// Backendstores import { datasources, integrations, @@ -43,6 +42,7 @@ const INITIAL_FRONTEND_STATE = { appId: "", routes: {}, clientLibPath: "", + theme: "", } export const getFrontendStore = () => { @@ -62,6 +62,7 @@ export const getFrontendStore = () => { url: application.url, layouts, screens, + theme: application.theme, hasAppPackage: true, appInstance: application.instance, clientLibPath, @@ -79,6 +80,20 @@ export const getFrontendStore = () => { database.set(application.instance) tables.init() }, + theme: { + save: async theme => { + const appId = get(store).appId + const response = await api.put(`/api/applications/${appId}`, { theme }) + if (response.status === 200) { + store.update(state => { + state.theme = theme + return state + }) + } else { + throw new Error("Error updating theme") + } + }, + }, routing: { fetch: async () => { const response = await api.get("/api/routing") @@ -196,6 +211,11 @@ export const getFrontendStore = () => { const response = await api.post(`/api/layouts`, layoutToSave) const savedLayout = await response.json() + // Abort if saving failed + if (response.status !== 200) { + return + } + store.update(state => { const layoutIdx = state.layouts.findIndex( stateLayout => stateLayout._id === savedLayout._id @@ -313,16 +333,6 @@ export const getFrontendStore = () => { create: async (componentName, presetProps) => { const selected = get(selectedComponent) const asset = get(currentAsset) - const state = get(store) - - // Only allow one screen slot, and in the layout - if (componentName.endsWith("screenslot")) { - const isLayout = state.currentFrontEndType === FrontendTypes.LAYOUT - const slot = findComponentType(asset.props, componentName) - if (!isLayout || slot != null) { - return - } - } // Create new component const componentInstance = store.actions.components.createInstance( diff --git a/packages/builder/src/builderStore/store/screenTemplates/newRowScreen.js b/packages/builder/src/builderStore/store/screenTemplates/newRowScreen.js index 23f2fd846f..b890d42d54 100644 --- a/packages/builder/src/builderStore/store/screenTemplates/newRowScreen.js +++ b/packages/builder/src/builderStore/store/screenTemplates/newRowScreen.js @@ -38,8 +38,6 @@ const createScreen = table => { .instanceName("Form") .customProps({ actionType: "Create", - theme: "spectrum--lightest", - size: "spectrum--medium", dataSource: { label: table.name, tableId: table._id, diff --git a/packages/builder/src/builderStore/store/screenTemplates/rowDetailScreen.js b/packages/builder/src/builderStore/store/screenTemplates/rowDetailScreen.js index cb97bd53db..21be879b14 100644 --- a/packages/builder/src/builderStore/store/screenTemplates/rowDetailScreen.js +++ b/packages/builder/src/builderStore/store/screenTemplates/rowDetailScreen.js @@ -8,7 +8,6 @@ import { makeTitleContainer, makeSaveButton, makeMainForm, - spectrumColor, makeDatasourceFormComponents, } from "./utils/commonComponents" @@ -26,36 +25,13 @@ export const ROW_DETAIL_TEMPLATE = "ROW_DETAIL_TEMPLATE" export const rowDetailUrl = table => sanitizeUrl(`/${table.name}/:id`) function generateTitleContainer(table, title, formId, repeaterId) { - // have to override style for this, its missing margin - const saveButton = makeSaveButton(table, formId).normalStyle({ - background: "#000000", - "border-width": "0", - "border-style": "None", - color: "#fff", - "font-weight": "600", - "font-size": "14px", - }) - + const saveButton = makeSaveButton(table, formId) const deleteButton = new Component("@budibase/standard-components/button") - .normalStyle({ - background: "transparent", - "border-width": "0", - "border-style": "None", - color: "#9e9e9e", - "font-weight": "600", - "font-size": "14px", - "margin-right": "8px", - "margin-left": "16px", - }) - .hoverStyle({ - background: "transparent", - color: "#4285f4", - }) - .customStyle(spectrumColor(700)) .text("Delete") .customProps({ - className: "", - disabled: false, + type: "secondary", + quiet: true, + size: "M", onClick: [ { parameters: { @@ -76,7 +52,19 @@ function generateTitleContainer(table, title, formId, repeaterId) { }) .instanceName("Delete Button") - return makeTitleContainer(title).addChild(deleteButton).addChild(saveButton) + const buttons = new Component("@budibase/standard-components/container") + .instanceName("Button Container") + .customProps({ + direction: "row", + hAlign: "right", + vAlign: "middle", + size: "shrink", + gap: "M", + }) + .addChild(deleteButton) + .addChild(saveButton) + + return makeTitleContainer(title).addChild(buttons) } const createScreen = table => { diff --git a/packages/builder/src/builderStore/store/screenTemplates/rowListScreen.js b/packages/builder/src/builderStore/store/screenTemplates/rowListScreen.js index 83b6e6a502..ccf1fceb29 100644 --- a/packages/builder/src/builderStore/store/screenTemplates/rowListScreen.js +++ b/packages/builder/src/builderStore/store/screenTemplates/rowListScreen.js @@ -19,21 +19,10 @@ export const rowListUrl = table => sanitizeUrl(`/${table.name}`) function generateTitleContainer(table) { const newButton = new Component("@budibase/standard-components/button") - .normalStyle({ - background: "#000000", - "border-width": "0", - "border-style": "None", - color: "#fff", - "font-weight": "600", - "font-size": "14px", - }) - .hoverStyle({ - background: "#4285f4", - }) .text("Create New") .customProps({ - className: "", - disabled: false, + size: "M", + type: "primary", onClick: [ { parameters: { @@ -46,12 +35,6 @@ function generateTitleContainer(table) { .instanceName("New Button") const heading = new Component("@budibase/standard-components/heading") - .normalStyle({ - margin: "0px", - flex: "1 1 auto", - "text-transform": "capitalize", - }) - .type("h2") .instanceName("Title") .text(table.name) .customProps({ @@ -60,14 +43,12 @@ function generateTitleContainer(table) { }) return new Component("@budibase/standard-components/container") - .normalStyle({ - "margin-bottom": "32px", - }) .customProps({ direction: "row", hAlign: "stretch", vAlign: "middle", size: "shrink", + gap: "M", }) .instanceName("Title Container") .addChild(heading) @@ -91,68 +72,35 @@ const createScreen = table => { const spectrumTable = new Component("@budibase/standard-components/table") .customProps({ dataProvider: `{{ literal ${makePropSafe(provider._json._id)} }}`, - theme: "spectrum--lightest", showAutoColumns: false, - quiet: true, - size: "spectrum--medium", + quiet: false, rowCount: 8, }) .instanceName(`${table.name} Table`) const safeTableId = makePropSafe(spectrumTable._json._id) const safeRowId = makePropSafe("_id") - const viewButton = new Component("@budibase/standard-components/button") + const viewLink = new Component("@budibase/standard-components/link") .customProps({ text: "View", - onClick: [ - { - "##eventHandlerType": "Navigate To", - parameters: { - url: `${rowListUrl(table)}/{{ ${safeTableId}.${safeRowId} }}`, - }, - }, - ], + url: `${rowListUrl(table)}/{{ ${safeTableId}.${safeRowId} }}`, + size: "S", + color: "var(--spectrum-global-color-gray-600)", + align: "left", }) - .instanceName("View Button") .normalStyle({ - background: "transparent", - "font-weight": "600", - color: "#888", - "border-width": "0", - }) - .hoverStyle({ - color: "#4285f4", + ["margin-left"]: "16px", + ["margin-right"]: "16px", }) + .instanceName("View Link") - spectrumTable.addChild(viewButton) + spectrumTable.addChild(viewLink) provider.addChild(spectrumTable) - const mainContainer = new Component("@budibase/standard-components/container") - .normalStyle({ - background: "white", - "border-radius": "0.5rem", - "box-shadow": "0 1px 2px 0 rgba(0, 0, 0, 0.05)", - "border-width": "2px", - "border-color": "rgba(0, 0, 0, 0.1)", - "border-style": "None", - "padding-top": "48px", - "padding-bottom": "48px", - "padding-right": "48px", - "padding-left": "48px", - }) - .customProps({ - direction: "column", - hAlign: "stretch", - vAlign: "top", - size: "shrink", - }) - .instanceName("Container") - .addChild(generateTitleContainer(table)) - .addChild(provider) - return new Screen() .route(rowListUrl(table)) .instanceName(`${table.name} - List`) - .addChild(mainContainer) + .addChild(generateTitleContainer(table)) + .addChild(provider) .json() } diff --git a/packages/builder/src/builderStore/store/screenTemplates/utils/commonComponents.js b/packages/builder/src/builderStore/store/screenTemplates/utils/commonComponents.js index 93361e6c11..aaf25f6d03 100644 --- a/packages/builder/src/builderStore/store/screenTemplates/utils/commonComponents.js +++ b/packages/builder/src/builderStore/store/screenTemplates/utils/commonComponents.js @@ -8,23 +8,16 @@ export function spectrumColor(number) { // God knows why. It seems to think optional chaining further down the // file is invalid if the word g-l-o-b-a-l is found - hence the reason this // statement is split into parts. - return "color: var(--spectrum-glo" + `bal-color-gray-${number});` + return "var(--spectrum-glo" + `bal-color-gray-${number})` } export function makeLinkComponent(tableName) { return new Component("@budibase/standard-components/link") - .normalStyle({ - color: "#757575", - "text-transform": "capitalize", - }) - .hoverStyle({ - color: "#4285f4", - }) - .customStyle(spectrumColor(700)) .text(tableName) .customProps({ url: `/${tableName.toLowerCase()}`, openInNewTab: false, + color: spectrumColor(700), size: "S", align: "left", }) @@ -33,19 +26,12 @@ export function makeLinkComponent(tableName) { export function makeMainForm() { return new Component("@budibase/standard-components/form") .normalStyle({ - width: "700px", - padding: "0px", - "border-radius": "0.5rem", - "box-shadow": "0 1px 2px 0 rgba(0, 0, 0, 0.05)", - "padding-top": "48px", - "padding-bottom": "48px", - "padding-right": "48px", - "padding-left": "48px", + width: "600px", }) .instanceName("Form") } -export function makeBreadcrumbContainer(tableName, text, capitalise = false) { +export function makeBreadcrumbContainer(tableName, text) { const link = makeLinkComponent(tableName).instanceName("Back Link") const arrowText = new Component("@budibase/standard-components/text") @@ -53,42 +39,27 @@ export function makeBreadcrumbContainer(tableName, text, capitalise = false) { .normalStyle({ "margin-right": "4px", "margin-left": "4px", - "margin-top": "0px", - "margin-bottom": "0px", }) - .customStyle(spectrumColor(700)) .text(">") .instanceName("Arrow") .customProps({ + color: spectrumColor(700), size: "S", align: "left", }) - const textStyling = { - color: "#000000", - "margin-top": "0px", - "margin-bottom": "0px", - } - if (capitalise) { - textStyling["text-transform"] = "capitalize" - } const identifierText = new Component("@budibase/standard-components/text") - .type("none") - .normalStyle(textStyling) - .customStyle(spectrumColor(700)) .text(text) .instanceName("Identifier") .customProps({ + color: spectrumColor(700), size: "S", align: "left", }) return new Component("@budibase/standard-components/container") - .normalStyle({ - "font-size": "14px", - color: "#757575", - }) .customProps({ + gap: "N", direction: "row", hAlign: "left", vAlign: "middle", @@ -102,22 +73,10 @@ export function makeBreadcrumbContainer(tableName, text, capitalise = false) { export function makeSaveButton(table, formId) { return new Component("@budibase/standard-components/button") - .normalStyle({ - background: "#000000", - "border-width": "0", - "border-style": "None", - color: "#fff", - "font-weight": "600", - "font-size": "14px", - "margin-left": "16px", - }) - .hoverStyle({ - background: "#4285f4", - }) .text("Save") .customProps({ - className: "", - disabled: false, + type: "primary", + size: "M", onClick: [ { "##eventHandlerType": "Validate Form", @@ -145,12 +104,6 @@ export function makeSaveButton(table, formId) { export function makeTitleContainer(title) { const heading = new Component("@budibase/standard-components/heading") - .normalStyle({ - margin: "0px", - flex: "1 1 auto", - }) - .customStyle(spectrumColor(900)) - .type("h2") .instanceName("Title") .text(title) .customProps({ @@ -168,6 +121,7 @@ export function makeTitleContainer(title) { hAlign: "stretch", vAlign: "middle", size: "shrink", + gap: "M", }) .instanceName("Title Container") .addChild(heading) diff --git a/packages/builder/src/components/common/bindings/DrawerBindableCombobox.svelte b/packages/builder/src/components/common/bindings/DrawerBindableCombobox.svelte new file mode 100644 index 0000000000..38f8693f51 --- /dev/null +++ b/packages/builder/src/components/common/bindings/DrawerBindableCombobox.svelte @@ -0,0 +1,97 @@ + + +
+ onChange(event.detail)} + {placeholder} + {options} + /> + {#if !disabled} +
+ +
+ {/if} +
+ + + Add the objects on the left to enrich your text. + + + (tempValue = event.detail)} + bindableProperties={bindings} + /> + + + diff --git a/packages/builder/src/components/design/AppPreview/AppThemeSelect.svelte b/packages/builder/src/components/design/AppPreview/AppThemeSelect.svelte new file mode 100644 index 0000000000..c9d937593b --- /dev/null +++ b/packages/builder/src/components/design/AppPreview/AppThemeSelect.svelte @@ -0,0 +1,38 @@ + + +
+