diff --git a/packages/builder/src/components/deploy/AppActions.svelte b/packages/builder/src/components/deploy/AppActions.svelte index de8b45c98b..1fa4fc7cd6 100644 --- a/packages/builder/src/components/deploy/AppActions.svelte +++ b/packages/builder/src/components/deploy/AppActions.svelte @@ -44,6 +44,7 @@ let appActionPopoverOpen = false let appActionPopoverAnchor let publishing = false + let lastOpened $: filteredApps = $appsStore.apps.filter(app => app.devId === application) $: selectedApp = filteredApps?.length ? filteredApps[0] : null @@ -57,7 +58,7 @@ $appStore.version && $appStore.upgradableVersion !== $appStore.version $: canPublish = !publishing && loaded && $sortedScreens.length > 0 - $: lastDeployed = getLastDeployedString($deploymentStore) + $: lastDeployed = getLastDeployedString($deploymentStore, lastOpened) const initialiseApp = async () => { const applicationPkg = await API.fetchAppPackage($appStore.devId) @@ -201,6 +202,7 @@ class="app-action-button publish app-action-popover" on:click={() => { if (!appActionPopoverOpen) { + lastOpened = new Date() appActionPopover.show() } else { appActionPopover.hide() diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/new/_components/componentStructure.json b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/new/_components/componentStructure.json index 96e8faf93c..87fb5b7bfe 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/new/_components/componentStructure.json +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/new/_components/componentStructure.json @@ -3,8 +3,6 @@ "name": "Blocks", "icon": "Article", "children": [ - "gridblock", - "tableblock", "cardsblock", "repeaterblock", "formblock", @@ -24,7 +22,7 @@ "children": [ "dataprovider", "repeater", - "table", + "gridblock", "spreadsheet", "dynamicfilter", "daterangepicker" diff --git a/packages/builder/src/pages/builder/app/[application]/design/_components/NewScreen/CreateScreenModal.svelte b/packages/builder/src/pages/builder/app/[application]/design/_components/NewScreen/CreateScreenModal.svelte index 8c1a11289d..b49e38d9cd 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/_components/NewScreen/CreateScreenModal.svelte +++ b/packages/builder/src/pages/builder/app/[application]/design/_components/NewScreen/CreateScreenModal.svelte @@ -19,7 +19,8 @@ import { goto } from "@roxi/routify" import { TOUR_KEYS } from "components/portal/onboarding/tours.js" import formScreen from "templates/formScreen" - import rowListScreen from "templates/rowListScreen" + import gridListScreen from "templates/gridListScreen" + import gridDetailsScreen from "templates/gridDetailsScreen" let mode let pendingScreen @@ -127,7 +128,7 @@ screenAccessRole = Roles.BASIC formType = null - if (mode === "table" || mode === "grid" || mode === "form") { + if (mode === "grid" || mode === "gridDetails" || mode === "form") { datasourceModal.show() } else if (mode === "blank") { let templates = getTemplates($tables.list) @@ -153,7 +154,10 @@ // Handler for Datasource Screen Creation const completeDatasourceScreenCreation = async () => { - templates = rowListScreen(selectedDatasources, mode) + templates = + mode === "grid" + ? gridListScreen(selectedDatasources) + : gridDetailsScreen(selectedDatasources) const screens = templates.map(template => { let screenTemplate = template.create() diff --git a/packages/builder/src/pages/builder/app/[application]/design/_components/NewScreen/images/tableDetails.png b/packages/builder/src/pages/builder/app/[application]/design/_components/NewScreen/images/tableDetails.png new file mode 100644 index 0000000000..f67495f3aa Binary files /dev/null and b/packages/builder/src/pages/builder/app/[application]/design/_components/NewScreen/images/tableDetails.png differ diff --git a/packages/builder/src/pages/builder/app/[application]/design/_components/NewScreen/images/tableInline.png b/packages/builder/src/pages/builder/app/[application]/design/_components/NewScreen/images/tableInline.png new file mode 100644 index 0000000000..905294a9ae Binary files /dev/null and b/packages/builder/src/pages/builder/app/[application]/design/_components/NewScreen/images/tableInline.png differ diff --git a/packages/builder/src/pages/builder/app/[application]/design/_components/NewScreen/index.svelte b/packages/builder/src/pages/builder/app/[application]/design/_components/NewScreen/index.svelte index b1ff66cb8d..ef07b277ef 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/_components/NewScreen/index.svelte +++ b/packages/builder/src/pages/builder/app/[application]/design/_components/NewScreen/index.svelte @@ -2,8 +2,8 @@ import { Body } from "@budibase/bbui" import CreationPage from "components/common/CreationPage.svelte" import blankImage from "./images/blank.png" - import tableImage from "./images/table.png" - import gridImage from "./images/grid.png" + import tableInline from "./images/tableInline.png" + import tableDetails from "./images/tableDetails.png" import formImage from "./images/form.png" import CreateScreenModal from "./CreateScreenModal.svelte" import { screenStore } from "stores/builder" @@ -38,23 +38,23 @@ -
createScreenModal.show("table")}> +
createScreenModal.show("grid")}>
- +
- Table - View, edit and delete rows on a table + Table with inline editing + View, edit and delete rows inline
-
createScreenModal.show("grid")}> +
createScreenModal.show("gridDetails")}>
- +
- Grid - View and manipulate rows on a grid + Table with details panel + Manage your row details in a side panel
@@ -113,6 +113,11 @@ width: 100%; } + .card .image { + min-height: 130px; + min-width: 235px; + } + .text { border: 1px solid var(--grey-4); border-radius: 0 0 4px 4px; diff --git a/packages/builder/src/templates/gridDetailsScreen.js b/packages/builder/src/templates/gridDetailsScreen.js new file mode 100644 index 0000000000..35ab651268 --- /dev/null +++ b/packages/builder/src/templates/gridDetailsScreen.js @@ -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() +} diff --git a/packages/builder/src/templates/gridListScreen.js b/packages/builder/src/templates/gridListScreen.js new file mode 100644 index 0000000000..c98d5d4baf --- /dev/null +++ b/packages/builder/src/templates/gridListScreen.js @@ -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() +} diff --git a/packages/builder/src/templates/index.js b/packages/builder/src/templates/index.js index fff31cc070..b00b8cb621 100644 --- a/packages/builder/src/templates/index.js +++ b/packages/builder/src/templates/index.js @@ -1,9 +1,11 @@ -import rowListScreen from "./rowListScreen" +import gridListScreen from "./gridListScreen" +import gridDetailsScreen from "./gridDetailsScreen" import createFromScratchScreen from "./createFromScratchScreen" import formScreen from "./formScreen" const allTemplates = datasources => [ - ...rowListScreen(datasources), + ...gridListScreen(datasources), + ...gridDetailsScreen(datasources), ...formScreen(datasources), ] diff --git a/packages/builder/src/templates/rowListScreen.js b/packages/builder/src/templates/rowListScreen.js deleted file mode 100644 index 7781a3d067..0000000000 --- a/packages/builder/src/templates/rowListScreen.js +++ /dev/null @@ -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() -} diff --git a/packages/client/manifest.json b/packages/client/manifest.json index 531e2c968a..a6e14be4ef 100644 --- a/packages/client/manifest.json +++ b/packages/client/manifest.json @@ -4673,6 +4673,7 @@ } }, "table": { + "deprecated": true, "name": "Table", "icon": "Table", "illegalChildren": ["section"], @@ -5418,6 +5419,7 @@ ] }, "tableblock": { + "deprecated": true, "block": true, "name": "Table Block", "icon": "Table", @@ -6595,7 +6597,7 @@ ] }, "gridblock": { - "name": "Grid Block", + "name": "Table", "icon": "Table", "styles": ["size"], "size": { diff --git a/packages/client/src/components/Component.svelte b/packages/client/src/components/Component.svelte index 7dbe0c0e44..378fa64b73 100644 --- a/packages/client/src/components/Component.svelte +++ b/packages/client/src/components/Component.svelte @@ -246,15 +246,18 @@ return } + const cacheId = `${definition.name}${ + definition?.deprecated === true ? "_deprecated" : "" + }` // Get the settings definition for this component, and cache it - if (SettingsDefinitionCache[definition.name]) { - settingsDefinition = SettingsDefinitionCache[definition.name] - settingsDefinitionMap = SettingsDefinitionMapCache[definition.name] + if (SettingsDefinitionCache[cacheId]) { + settingsDefinition = SettingsDefinitionCache[cacheId] + settingsDefinitionMap = SettingsDefinitionMapCache[cacheId] } else { settingsDefinition = getSettingsDefinition(definition) settingsDefinitionMap = getSettingsDefinitionMap(settingsDefinition) - SettingsDefinitionCache[definition.name] = settingsDefinition - SettingsDefinitionMapCache[definition.name] = settingsDefinitionMap + SettingsDefinitionCache[cacheId] = settingsDefinition + SettingsDefinitionMapCache[cacheId] = settingsDefinitionMap } // Parse the instance settings, and cache them diff --git a/packages/client/src/components/app/blocks/index.js b/packages/client/src/components/app/blocks/index.js index 2c8d81cf96..c1df620285 100644 --- a/packages/client/src/components/app/blocks/index.js +++ b/packages/client/src/components/app/blocks/index.js @@ -1,4 +1,3 @@ -export { default as tableblock } from "./TableBlock.svelte" export { default as cardsblock } from "./CardsBlock.svelte" export { default as repeaterblock } from "./RepeaterBlock.svelte" export { default as formblock } from "./form/FormBlock.svelte" diff --git a/packages/client/src/components/app/blocks/TableBlock.svelte b/packages/client/src/components/app/deprecated/TableBlock.svelte similarity index 100% rename from packages/client/src/components/app/blocks/TableBlock.svelte rename to packages/client/src/components/app/deprecated/TableBlock.svelte diff --git a/packages/client/src/components/app/table/SlotRenderer.svelte b/packages/client/src/components/app/deprecated/table/SlotRenderer.svelte similarity index 100% rename from packages/client/src/components/app/table/SlotRenderer.svelte rename to packages/client/src/components/app/deprecated/table/SlotRenderer.svelte diff --git a/packages/client/src/components/app/table/Table.svelte b/packages/client/src/components/app/deprecated/table/Table.svelte similarity index 98% rename from packages/client/src/components/app/table/Table.svelte rename to packages/client/src/components/app/deprecated/table/Table.svelte index f16e26bc45..fd2e7c030c 100644 --- a/packages/client/src/components/app/table/Table.svelte +++ b/packages/client/src/components/app/deprecated/table/Table.svelte @@ -3,7 +3,7 @@ import { Table } from "@budibase/bbui" import SlotRenderer from "./SlotRenderer.svelte" import { canBeSortColumn } from "@budibase/shared-core" - import Provider from "../../context/Provider.svelte" + import Provider from "components/context/Provider.svelte" export let dataProvider export let columns diff --git a/packages/client/src/components/app/table/index.js b/packages/client/src/components/app/deprecated/table/index.js similarity index 100% rename from packages/client/src/components/app/table/index.js rename to packages/client/src/components/app/deprecated/table/index.js diff --git a/packages/client/src/components/app/index.js b/packages/client/src/components/app/index.js index 97df3741e1..e23e19704c 100644 --- a/packages/client/src/components/app/index.js +++ b/packages/client/src/components/app/index.js @@ -40,11 +40,12 @@ export { default as sidepanel } from "./SidePanel.svelte" export { default as gridblock } from "./GridBlock.svelte" export * from "./charts" export * from "./forms" -export * from "./table" export * from "./blocks" export * from "./dynamic-filter" // 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 cardhorizontal } from "./deprecated/CardHorizontal.svelte" export { default as stackedlist } from "./deprecated/StackedList.svelte"