Merge branch 'fix/BUDI-7444' of github.com:Budibase/budibase into fix/BUDI-7444

This commit is contained in:
mike12345567 2024-03-22 11:50:24 +00:00
commit ce73832a43
26 changed files with 298 additions and 114 deletions

View File

@ -1,5 +1,5 @@
{ {
"version": "2.22.9", "version": "2.22.11",
"npmClient": "yarn", "npmClient": "yarn",
"packages": [ "packages": [
"packages/*", "packages/*",

View File

@ -44,6 +44,7 @@
let appActionPopoverOpen = false let appActionPopoverOpen = false
let appActionPopoverAnchor let appActionPopoverAnchor
let publishing = false let publishing = false
let lastOpened
$: filteredApps = $appsStore.apps.filter(app => app.devId === application) $: filteredApps = $appsStore.apps.filter(app => app.devId === application)
$: selectedApp = filteredApps?.length ? filteredApps[0] : null $: selectedApp = filteredApps?.length ? filteredApps[0] : null
@ -57,7 +58,7 @@
$appStore.version && $appStore.version &&
$appStore.upgradableVersion !== $appStore.version $appStore.upgradableVersion !== $appStore.version
$: canPublish = !publishing && loaded && $sortedScreens.length > 0 $: canPublish = !publishing && loaded && $sortedScreens.length > 0
$: lastDeployed = getLastDeployedString($deploymentStore) $: lastDeployed = getLastDeployedString($deploymentStore, lastOpened)
const initialiseApp = async () => { const initialiseApp = async () => {
const applicationPkg = await API.fetchAppPackage($appStore.devId) const applicationPkg = await API.fetchAppPackage($appStore.devId)
@ -201,6 +202,7 @@
class="app-action-button publish app-action-popover" class="app-action-button publish app-action-popover"
on:click={() => { on:click={() => {
if (!appActionPopoverOpen) { if (!appActionPopoverOpen) {
lastOpened = new Date()
appActionPopover.show() appActionPopover.show()
} else { } else {
appActionPopover.hide() appActionPopover.hide()

View File

@ -3,8 +3,6 @@
"name": "Blocks", "name": "Blocks",
"icon": "Article", "icon": "Article",
"children": [ "children": [
"gridblock",
"tableblock",
"cardsblock", "cardsblock",
"repeaterblock", "repeaterblock",
"formblock", "formblock",
@ -24,7 +22,7 @@
"children": [ "children": [
"dataprovider", "dataprovider",
"repeater", "repeater",
"table", "gridblock",
"spreadsheet", "spreadsheet",
"dynamicfilter", "dynamicfilter",
"daterangepicker" "daterangepicker"

View File

@ -19,7 +19,8 @@
import { goto } from "@roxi/routify" import { goto } from "@roxi/routify"
import { TOUR_KEYS } from "components/portal/onboarding/tours.js" import { TOUR_KEYS } from "components/portal/onboarding/tours.js"
import formScreen from "templates/formScreen" import formScreen from "templates/formScreen"
import rowListScreen from "templates/rowListScreen" import gridListScreen from "templates/gridListScreen"
import gridDetailsScreen from "templates/gridDetailsScreen"
let mode let mode
let pendingScreen let pendingScreen
@ -127,7 +128,7 @@
screenAccessRole = Roles.BASIC screenAccessRole = Roles.BASIC
formType = null formType = null
if (mode === "table" || mode === "grid" || mode === "form") { if (mode === "grid" || mode === "gridDetails" || mode === "form") {
datasourceModal.show() datasourceModal.show()
} else if (mode === "blank") { } else if (mode === "blank") {
let templates = getTemplates($tables.list) let templates = getTemplates($tables.list)
@ -153,7 +154,10 @@
// Handler for Datasource Screen Creation // Handler for Datasource Screen Creation
const completeDatasourceScreenCreation = async () => { const completeDatasourceScreenCreation = async () => {
templates = rowListScreen(selectedDatasources, mode) templates =
mode === "grid"
? gridListScreen(selectedDatasources)
: gridDetailsScreen(selectedDatasources)
const screens = templates.map(template => { const screens = templates.map(template => {
let screenTemplate = template.create() let screenTemplate = template.create()

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -2,8 +2,8 @@
import { Body } from "@budibase/bbui" import { Body } from "@budibase/bbui"
import CreationPage from "components/common/CreationPage.svelte" import CreationPage from "components/common/CreationPage.svelte"
import blankImage from "./images/blank.png" import blankImage from "./images/blank.png"
import tableImage from "./images/table.png" import tableInline from "./images/tableInline.png"
import gridImage from "./images/grid.png" import tableDetails from "./images/tableDetails.png"
import formImage from "./images/form.png" import formImage from "./images/form.png"
import CreateScreenModal from "./CreateScreenModal.svelte" import CreateScreenModal from "./CreateScreenModal.svelte"
import { screenStore } from "stores/builder" import { screenStore } from "stores/builder"
@ -38,23 +38,23 @@
</div> </div>
</div> </div>
<div class="card" on:click={() => createScreenModal.show("table")}> <div class="card" on:click={() => createScreenModal.show("grid")}>
<div class="image"> <div class="image">
<img alt="" src={tableImage} /> <img alt="" src={tableInline} />
</div> </div>
<div class="text"> <div class="text">
<Body size="S">Table</Body> <Body size="S">Table with inline editing</Body>
<Body size="XS">View, edit and delete rows on a table</Body> <Body size="XS">View, edit and delete rows inline</Body>
</div> </div>
</div> </div>
<div class="card" on:click={() => createScreenModal.show("grid")}> <div class="card" on:click={() => createScreenModal.show("gridDetails")}>
<div class="image"> <div class="image">
<img alt="" src={gridImage} /> <img alt="" src={tableDetails} />
</div> </div>
<div class="text"> <div class="text">
<Body size="S">Grid</Body> <Body size="S">Table with details panel</Body>
<Body size="XS">View and manipulate rows on a grid</Body> <Body size="XS">Manage your row details in a side panel</Body>
</div> </div>
</div> </div>
@ -113,6 +113,11 @@
width: 100%; width: 100%;
} }
.card .image {
min-height: 130px;
min-width: 235px;
}
.text { .text {
border: 1px solid var(--grey-4); border: 1px solid var(--grey-4);
border-radius: 0 0 4px 4px; border-radius: 0 0 4px 4px;

View File

@ -0,0 +1,158 @@
import sanitizeUrl from "helpers/sanitizeUrl"
import { Screen } from "./Screen"
import { Component } from "./Component"
import { generate } from "shortid"
import { makePropSafe as safe } from "@budibase/string-templates"
import { Utils } from "@budibase/frontend-core"
export default function (datasources) {
if (!Array.isArray(datasources)) {
return []
}
return datasources.map(datasource => {
return {
name: `${datasource.label} - List with panel`,
create: () => createScreen(datasource),
id: GRID_DETAILS_TEMPLATE,
resourceId: datasource.resourceId,
}
})
}
export const GRID_DETAILS_TEMPLATE = "GRID_DETAILS_TEMPLATE"
export const gridDetailsUrl = datasource => sanitizeUrl(`/${datasource.label}`)
const createScreen = datasource => {
/*
Create Row
*/
const createRowSidePanel = new Component(
"@budibase/standard-components/sidepanel"
).instanceName("New row side panel")
const buttonGroup = new Component("@budibase/standard-components/buttongroup")
const createButton = new Component("@budibase/standard-components/button")
createButton.customProps({
onClick: [
{
id: 0,
"##eventHandlerType": "Open Side Panel",
parameters: {
id: createRowSidePanel._json._id,
},
},
],
text: "Create row",
type: "cta",
})
buttonGroup.instanceName(`${datasource.label} - Create`).customProps({
hAlign: "right",
buttons: [createButton.json()],
})
const gridHeader = new Component("@budibase/standard-components/container")
.instanceName("Heading container")
.customProps({
direction: "row",
hAlign: "stretch",
})
const heading = new Component("@budibase/standard-components/heading")
.instanceName("Table heading")
.customProps({
text: datasource?.label,
})
gridHeader.addChild(heading)
gridHeader.addChild(buttonGroup)
const createFormBlock = new Component(
"@budibase/standard-components/formblock"
)
createFormBlock.instanceName("Create row form block").customProps({
dataSource: datasource,
labelPosition: "left",
buttonPosition: "top",
actionType: "Create",
title: "Create row",
buttons: Utils.buildFormBlockButtonConfig({
_id: createFormBlock._json._id,
showDeleteButton: false,
showSaveButton: true,
saveButtonLabel: "Save",
actionType: "Create",
dataSource: datasource,
}),
})
createRowSidePanel.addChild(createFormBlock)
/*
Edit Row
*/
const stateKey = `ID_${generate()}`
const detailsSidePanel = new Component(
"@budibase/standard-components/sidepanel"
).instanceName("Edit row side panel")
const editFormBlock = new Component("@budibase/standard-components/formblock")
editFormBlock.instanceName("Edit row form block").customProps({
dataSource: datasource,
labelPosition: "left",
buttonPosition: "top",
actionType: "Update",
title: "Edit",
rowId: `{{ ${safe("state")}.${safe(stateKey)} }}`,
buttons: Utils.buildFormBlockButtonConfig({
_id: editFormBlock._json._id,
showDeleteButton: true,
showSaveButton: true,
saveButtonLabel: "Save",
deleteButtonLabel: "Delete",
actionType: "Update",
dataSource: datasource,
}),
})
detailsSidePanel.addChild(editFormBlock)
const gridBlock = new Component("@budibase/standard-components/gridblock")
gridBlock
.customProps({
table: datasource,
allowAddRows: false,
allowEditRows: false,
allowDeleteRows: false,
onRowClick: [
{
id: 0,
"##eventHandlerType": "Update State",
parameters: {
key: stateKey,
type: "set",
persist: false,
value: `{{ ${safe("eventContext")}.${safe("row")}._id }}`,
},
},
{
id: 1,
"##eventHandlerType": "Open Side Panel",
parameters: {
id: detailsSidePanel._json._id,
},
},
],
})
.instanceName(`${datasource.label} - Table`)
return new Screen()
.route(gridDetailsUrl(datasource))
.instanceName(`${datasource.label} - List and details`)
.addChild(gridHeader)
.addChild(gridBlock)
.addChild(createRowSidePanel)
.addChild(detailsSidePanel)
.json()
}

View File

@ -0,0 +1,41 @@
import sanitizeUrl from "helpers/sanitizeUrl"
import { Screen } from "./Screen"
import { Component } from "./Component"
export default function (datasources) {
if (!Array.isArray(datasources)) {
return []
}
return datasources.map(datasource => {
return {
name: `${datasource.label} - List`,
create: () => createScreen(datasource),
id: GRID_LIST_TEMPLATE,
resourceId: datasource.resourceId,
}
})
}
export const GRID_LIST_TEMPLATE = "GRID_LIST_TEMPLATE"
export const gridListUrl = datasource => sanitizeUrl(`/${datasource.label}`)
const createScreen = datasource => {
const heading = new Component("@budibase/standard-components/heading")
.instanceName("Table heading")
.customProps({
text: datasource?.label,
})
const gridBlock = new Component("@budibase/standard-components/gridblock")
.instanceName(`${datasource.label} - Table`)
.customProps({
table: datasource,
})
return new Screen()
.route(gridListUrl(datasource))
.instanceName(`${datasource.label} - List`)
.addChild(heading)
.addChild(gridBlock)
.json()
}

View File

@ -1,9 +1,11 @@
import rowListScreen from "./rowListScreen" import gridListScreen from "./gridListScreen"
import gridDetailsScreen from "./gridDetailsScreen"
import createFromScratchScreen from "./createFromScratchScreen" import createFromScratchScreen from "./createFromScratchScreen"
import formScreen from "./formScreen" import formScreen from "./formScreen"
const allTemplates = datasources => [ const allTemplates = datasources => [
...rowListScreen(datasources), ...gridListScreen(datasources),
...gridDetailsScreen(datasources),
...formScreen(datasources), ...formScreen(datasources),
] ]

View File

@ -1,63 +0,0 @@
import sanitizeUrl from "helpers/sanitizeUrl"
import { Screen } from "./Screen"
import { Component } from "./Component"
export default function (datasources, mode = "table") {
if (!Array.isArray(datasources)) {
return []
}
return datasources.map(datasource => {
return {
name: `${datasource.label} - List`,
create: () => createScreen(datasource, mode),
id: ROW_LIST_TEMPLATE,
resourceId: datasource.resourceId,
}
})
}
export const ROW_LIST_TEMPLATE = "ROW_LIST_TEMPLATE"
export const rowListUrl = datasource => sanitizeUrl(`/${datasource.label}`)
const generateTableBlock = datasource => {
const tableBlock = new Component("@budibase/standard-components/tableblock")
tableBlock
.customProps({
title: datasource.label,
dataSource: datasource,
sortOrder: "Ascending",
size: "spectrum--medium",
paginate: true,
rowCount: 8,
clickBehaviour: "details",
showTitleButton: true,
titleButtonText: "Create row",
titleButtonClickBehaviour: "new",
sidePanelSaveLabel: "Save",
sidePanelDeleteLabel: "Delete",
})
.instanceName(`${datasource.label} - Table block`)
return tableBlock
}
const generateGridBlock = datasource => {
const gridBlock = new Component("@budibase/standard-components/gridblock")
gridBlock
.customProps({
table: datasource,
})
.instanceName(`${datasource.label} - Grid block`)
return gridBlock
}
const createScreen = (datasource, mode) => {
return new Screen()
.route(rowListUrl(datasource))
.instanceName(`${datasource.label} - List`)
.addChild(
mode === "table"
? generateTableBlock(datasource)
: generateGridBlock(datasource)
)
.json()
}

View File

@ -4673,6 +4673,7 @@
} }
}, },
"table": { "table": {
"deprecated": true,
"name": "Table", "name": "Table",
"icon": "Table", "icon": "Table",
"illegalChildren": ["section"], "illegalChildren": ["section"],
@ -5418,6 +5419,7 @@
] ]
}, },
"tableblock": { "tableblock": {
"deprecated": true,
"block": true, "block": true,
"name": "Table Block", "name": "Table Block",
"icon": "Table", "icon": "Table",
@ -6595,7 +6597,7 @@
] ]
}, },
"gridblock": { "gridblock": {
"name": "Grid Block", "name": "Table",
"icon": "Table", "icon": "Table",
"styles": ["size"], "styles": ["size"],
"size": { "size": {

View File

@ -246,15 +246,18 @@
return return
} }
const cacheId = `${definition.name}${
definition?.deprecated === true ? "_deprecated" : ""
}`
// Get the settings definition for this component, and cache it // Get the settings definition for this component, and cache it
if (SettingsDefinitionCache[definition.name]) { if (SettingsDefinitionCache[cacheId]) {
settingsDefinition = SettingsDefinitionCache[definition.name] settingsDefinition = SettingsDefinitionCache[cacheId]
settingsDefinitionMap = SettingsDefinitionMapCache[definition.name] settingsDefinitionMap = SettingsDefinitionMapCache[cacheId]
} else { } else {
settingsDefinition = getSettingsDefinition(definition) settingsDefinition = getSettingsDefinition(definition)
settingsDefinitionMap = getSettingsDefinitionMap(settingsDefinition) settingsDefinitionMap = getSettingsDefinitionMap(settingsDefinition)
SettingsDefinitionCache[definition.name] = settingsDefinition SettingsDefinitionCache[cacheId] = settingsDefinition
SettingsDefinitionMapCache[definition.name] = settingsDefinitionMap SettingsDefinitionMapCache[cacheId] = settingsDefinitionMap
} }
// Parse the instance settings, and cache them // Parse the instance settings, and cache them

View File

@ -1,4 +1,3 @@
export { default as tableblock } from "./TableBlock.svelte"
export { default as cardsblock } from "./CardsBlock.svelte" export { default as cardsblock } from "./CardsBlock.svelte"
export { default as repeaterblock } from "./RepeaterBlock.svelte" export { default as repeaterblock } from "./RepeaterBlock.svelte"
export { default as formblock } from "./form/FormBlock.svelte" export { default as formblock } from "./form/FormBlock.svelte"

View File

@ -3,7 +3,7 @@
import { Table } from "@budibase/bbui" import { Table } from "@budibase/bbui"
import SlotRenderer from "./SlotRenderer.svelte" import SlotRenderer from "./SlotRenderer.svelte"
import { canBeSortColumn } from "@budibase/shared-core" import { canBeSortColumn } from "@budibase/shared-core"
import Provider from "../../context/Provider.svelte" import Provider from "components/context/Provider.svelte"
export let dataProvider export let dataProvider
export let columns export let columns

View File

@ -40,11 +40,12 @@ export { default as sidepanel } from "./SidePanel.svelte"
export { default as gridblock } from "./GridBlock.svelte" export { default as gridblock } from "./GridBlock.svelte"
export * from "./charts" export * from "./charts"
export * from "./forms" export * from "./forms"
export * from "./table"
export * from "./blocks" export * from "./blocks"
export * from "./dynamic-filter" export * from "./dynamic-filter"
// Deprecated component left for compatibility in old apps // Deprecated component left for compatibility in old apps
export * from "./deprecated/table"
export { default as tableblock } from "./deprecated/TableBlock.svelte"
export { default as navigation } from "./deprecated/Navigation.svelte" export { default as navigation } from "./deprecated/Navigation.svelte"
export { default as cardhorizontal } from "./deprecated/CardHorizontal.svelte" export { default as cardhorizontal } from "./deprecated/CardHorizontal.svelte"
export { default as stackedlist } from "./deprecated/StackedList.svelte" export { default as stackedlist } from "./deprecated/StackedList.svelte"

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,5 @@
const { // eslint-disable-next-line local-rules/no-budibase-imports
getJsHelperList, import { getJsHelperList } from "@budibase/string-templates/src/helpers/list"
} = require("../../../../string-templates/src/helpers/list.js")
export default { export default {
...getJsHelperList(), ...getJsHelperList(),

View File

@ -1,3 +1,3 @@
"use strict";var snippets=(()=>{var u=Object.create;var n=Object.defineProperty;var a=Object.getOwnPropertyDescriptor;var h=Object.getOwnPropertyNames;var x=Object.getPrototypeOf,C=Object.prototype.hasOwnProperty;var l=(i,e)=>()=>(e||i((e={exports:{}}).exports,e),e.exports),W=(i,e)=>{for(var p in e)n(i,p,{get:e[p],enumerable:!0})},f=(i,e,p,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let t of h(e))!C.call(i,t)&&t!==p&&n(i,t,{get:()=>e[t],enumerable:!(r=a(e,t))||r.enumerable});return i};var d=(i,e,p)=>(p=i!=null?u(x(i)):{},f(e||!i||!i.__esModule?n(p,"default",{value:i,enumerable:!0}):p,i)),g=i=>f(n({},"__esModule",{value:!0}),i);var s=l((D,o)=>{o.exports.iifeWrapper=i=>`(function(){ "use strict";var snippets=(()=>{var p=Object.defineProperty;var f=Object.getOwnPropertyDescriptor;var s=Object.getOwnPropertyNames;var c=Object.prototype.hasOwnProperty;var u=(i,e)=>{for(var n in e)p(i,n,{get:e[n],enumerable:!0})},a=(i,e,n,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let t of s(e))!c.call(i,t)&&t!==n&&p(i,t,{get:()=>e[t],enumerable:!(r=f(e,t))||r.enumerable});return i};var h=i=>a(p({},"__esModule",{value:!0}),i);var C={};u(C,{default:()=>x});var o=i=>`(function(){
${i} ${i}
})();`});var w={};W(w,{default:()=>v});var c=d(s()),v=new Proxy({},{get:function(i,e){return e in snippetCache||(snippetCache[e]=[eval][0]((0,c.iifeWrapper)(snippetDefinitions[e]))),snippetCache[e]}});return g(w);})(); })();`;var x=new Proxy({},{get:function(i,e){return e in snippetCache||(snippetCache[e]=[eval][0](o(snippetDefinitions[e]))),snippetCache[e]}});return h(C);})();

View File

@ -18,7 +18,8 @@
"@budibase/backend-core/*": ["../backend-core/*"], "@budibase/backend-core/*": ["../backend-core/*"],
"@budibase/shared-core": ["../shared-core/src"], "@budibase/shared-core": ["../shared-core/src"],
"@budibase/pro": ["../pro/src"], "@budibase/pro": ["../pro/src"],
"@budibase/string-templates": ["../string-templates/src"] "@budibase/string-templates": ["../string-templates/src"],
"@budibase/string-templates/*": ["../string-templates/*"]
}, },
"allowArbitraryExtensions": true "allowArbitraryExtensions": true
}, },

View File

@ -1,17 +1,29 @@
import { date, duration } from "./date" import { date, duration } from "./date"
import { /*
math, @budibase/handlebars-helpers is not treeshakeable, so we can't use the barrel files.
array, Otherwise, we have issues when generating the isolated-vm bundle because of the treeshaking
number, */
url, /* eslint-disable local-rules/no-budibase-imports */
string, // @ts-expect-error
comparison, import math from "@budibase/handlebars-helpers/lib/math"
object, // @ts-expect-error
regex, import array from "@budibase/handlebars-helpers/lib/array"
uuid, // @ts-expect-error
// @ts-expect-error import number from "@budibase/handlebars-helpers/lib/number"
} from "@budibase/handlebars-helpers" // @ts-expect-error
import url from "@budibase/handlebars-helpers/lib/url"
// @ts-expect-error
import string from "@budibase/handlebars-helpers/lib/string"
// @ts-expect-error
import comparison from "@budibase/handlebars-helpers/lib/comparison"
// @ts-expect-error
import object from "@budibase/handlebars-helpers/lib/object"
// @ts-expect-error
import regex from "@budibase/handlebars-helpers/lib/regex"
// @ts-expect-error
import uuid from "@budibase/handlebars-helpers/lib/uuid"
/* eslint-enable local-rules/no-budibase-imports */
// https://github.com/evanw/esbuild/issues/56 // https://github.com/evanw/esbuild/issues/56
const externalCollections = { const externalCollections = {
@ -42,14 +54,14 @@ export function getJsHelperList() {
helpers = {} helpers = {}
for (let collection of Object.values(externalCollections)) { for (let collection of Object.values(externalCollections)) {
for (let [key, func] of Object.entries<any>(collection())) { for (let [key, func] of Object.entries<any>(collection)) {
// Handlebars injects the hbs options to the helpers by default. We are adding an empty {} as a last parameter to simulate it // Handlebars injects the hbs options to the helpers by default. We are adding an empty {} as a last parameter to simulate it
helpers[key] = (...props: any) => func(...props, {}) helpers[key] = (...props: any) => func(...props, {})
} }
} }
helpers = { helpers = {
...helpers, ...helpers,
addedHelpers, ...addedHelpers,
} }
for (const toRemove of helpersToRemoveForJs) { for (const toRemove of helpersToRemoveForJs) {

View File

@ -94,7 +94,7 @@ export async function processObject<T extends Record<string, any>>(
for (const key of Object.keys(object || {})) { for (const key of Object.keys(object || {})) {
if (object[key] != null) { if (object[key] != null) {
const val = object[key] const val = object[key]
let parsedValue let parsedValue = val
if (typeof val === "string") { if (typeof val === "string") {
parsedValue = await processString(object[key], context, opts) parsedValue = await processString(object[key], context, opts)
} else if (typeof val === "object") { } else if (typeof val === "object") {

View File

@ -104,6 +104,26 @@ describe("Test that the object processing works correctly", () => {
} }
expect(error).toBeNull() expect(error).toBeNull()
}) })
it("should be able to handle booleans", async () => {
const output = await processObject(
{
first: true,
second: "true",
third: "another string",
forth: "with {{ template }}",
},
{
template: "value",
}
)
expect(output).toEqual({
first: true,
second: "true",
third: "another string",
forth: "with value",
})
})
}) })
describe("check returning objects", () => { describe("check returning objects", () => {