diff --git a/packages/builder/src/components/backend/DataTable/DataTable.svelte b/packages/builder/src/components/backend/DataTable/DataTable.svelte index 981a619ebd..33db9b60e3 100644 --- a/packages/builder/src/components/backend/DataTable/DataTable.svelte +++ b/packages/builder/src/components/backend/DataTable/DataTable.svelte @@ -44,13 +44,15 @@ + + + {#if isInternal} @@ -65,7 +67,6 @@ {/if} - {#if isUsersTable} diff --git a/packages/builder/src/components/backend/DataTable/buttons/TableFilterButton.svelte b/packages/builder/src/components/backend/DataTable/buttons/TableFilterButton.svelte index 5af8d1bac8..ce6a3f0c51 100644 --- a/packages/builder/src/components/backend/DataTable/buttons/TableFilterButton.svelte +++ b/packages/builder/src/components/backend/DataTable/buttons/TableFilterButton.svelte @@ -14,6 +14,12 @@ $: tempValue = filters || [] $: schemaFields = Object.values(schema || {}) + $: text = getText(filters) + + const getText = filters => { + const count = filters?.length + return count ? `Filter (${count})` : "Filter" + } 0} > - Filter + {text} { filter.set(e.detail || []) } diff --git a/packages/builder/src/components/backend/DataTable/buttons/grid/GridImportButton.svelte b/packages/builder/src/components/backend/DataTable/buttons/grid/GridImportButton.svelte index 5b89d92438..a0881163b4 100644 --- a/packages/builder/src/components/backend/DataTable/buttons/grid/GridImportButton.svelte +++ b/packages/builder/src/components/backend/DataTable/buttons/grid/GridImportButton.svelte @@ -4,12 +4,12 @@ export let disabled = false - const { rows, tableId, tableType } = getContext("grid") + const { rows, tableId, table } = getContext("grid") diff --git a/packages/builder/src/components/backend/DataTable/modals/ImportModal.svelte b/packages/builder/src/components/backend/DataTable/modals/ImportModal.svelte index c020d1a7ac..1696c6ba03 100644 --- a/packages/builder/src/components/backend/DataTable/modals/ImportModal.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/ImportModal.svelte @@ -14,6 +14,7 @@ export let tableId export let tableType + let rows = [] let allValid = false let displayColumn = null diff --git a/packages/builder/src/components/design/settings/controls/ColumnEditor/BasicColumnEditor.svelte b/packages/builder/src/components/design/settings/controls/ColumnEditor/BasicColumnEditor.svelte index 053735cc6d..f051d68a48 100644 --- a/packages/builder/src/components/design/settings/controls/ColumnEditor/BasicColumnEditor.svelte +++ b/packages/builder/src/components/design/settings/controls/ColumnEditor/BasicColumnEditor.svelte @@ -2,9 +2,4 @@ import ColumnEditor from "./ColumnEditor.svelte" - + diff --git a/packages/builder/src/components/design/settings/controls/ColumnEditor/ColumnDrawer.svelte b/packages/builder/src/components/design/settings/controls/ColumnEditor/ColumnDrawer.svelte index 87fbba3b52..6e57d20751 100644 --- a/packages/builder/src/components/design/settings/controls/ColumnEditor/ColumnDrawer.svelte +++ b/packages/builder/src/components/design/settings/controls/ColumnEditor/ColumnDrawer.svelte @@ -142,10 +142,10 @@
- By default, all table columns will automatically be shown. + By default, all columns will automatically be shown.
- You can manually control which columns are included in your table, - and their appearance, by adding them below. + You can manually control which columns are included by adding them + below.
diff --git a/packages/builder/src/components/design/settings/controls/ColumnEditor/ColumnEditor.svelte b/packages/builder/src/components/design/settings/controls/ColumnEditor/ColumnEditor.svelte index 59340d4898..b34edd880a 100644 --- a/packages/builder/src/components/design/settings/controls/ColumnEditor/ColumnEditor.svelte +++ b/packages/builder/src/components/design/settings/controls/ColumnEditor/ColumnEditor.svelte @@ -13,7 +13,6 @@ export let componentInstance export let value = [] export let allowCellEditing = true - export let subject = "Table" const dispatch = createEventDispatcher() @@ -75,11 +74,10 @@ } -Configure columns - - - Configure the columns in your {subject.toLowerCase()}. - +
+ Configure columns +
+ + + diff --git a/packages/builder/src/components/design/settings/controls/FieldConfiguration.svelte b/packages/builder/src/components/design/settings/controls/FieldConfiguration.svelte index 59340d4898..80f4829d71 100644 --- a/packages/builder/src/components/design/settings/controls/FieldConfiguration.svelte +++ b/packages/builder/src/components/design/settings/controls/FieldConfiguration.svelte @@ -1,7 +1,7 @@ -Define filters +
+ {text} +
(tempValue = e.detail)} /> + + diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/components/[componentId]/new/_components/componentStructure.json b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/components/[componentId]/new/_components/componentStructure.json index d35e7cd515..012ab461f0 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/components/[componentId]/new/_components/componentStructure.json +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/components/[componentId]/new/_components/componentStructure.json @@ -3,6 +3,7 @@ "name": "Blocks", "icon": "Article", "children": [ + "gridblock", "tableblock", "cardsblock", "repeaterblock", diff --git a/packages/client/manifest.json b/packages/client/manifest.json index 76d7aff229..355373c854 100644 --- a/packages/client/manifest.json +++ b/packages/client/manifest.json @@ -5230,5 +5230,91 @@ "type": "schema", "suffix": "repeater" } + }, + "gridblock": { + "name": "Grid block", + "icon": "Table", + "styles": ["size"], + "size": { + "width": 600, + "height": 400 + }, + "info": "Grid Blocks are only compatible with internal or SQL tables", + "settings": [ + { + "type": "table", + "label": "Table", + "key": "table", + "required": true + }, + { + "type": "columns/basic", + "label": "Columns", + "key": "columns", + "dependsOn": "table" + }, + { + "type": "filter", + "label": "Filtering", + "key": "initialFilter" + }, + { + "type": "field/sortable", + "label": "Sort column", + "key": "initialSortColumn", + "placeholder": "Default" + }, + { + "type": "select", + "label": "Sort order", + "key": "initialSortOrder", + "options": ["Ascending", "Descending"], + "defaultValue": "Ascending" + }, + { + "type": "select", + "label": "Row height", + "key": "initialRowHeight", + "placeholder": "Default", + "options": [ + { + "label": "Small", + "value": 36 + }, + { + "label": "Medium", + "value": 64 + }, + { + "label": "Large", + "value": 92 + } + ] + }, + { + "type": "boolean", + "label": "Add rows", + "key": "allowAddRows", + "defaultValue": true + }, + { + "type": "boolean", + "label": "Edit rows", + "key": "allowEditRows", + "defaultValue": true + }, + { + "type": "boolean", + "label": "Delete rows", + "key": "allowDeleteRows", + "defaultValue": true + }, + { + "type": "boolean", + "label": "High contrast", + "key": "stripeRows", + "defaultValue": false + } + ] } } diff --git a/packages/client/src/api/api.js b/packages/client/src/api/api.js index b4a137f85d..4062bd3b73 100644 --- a/packages/client/src/api/api.js +++ b/packages/client/src/api/api.js @@ -35,7 +35,8 @@ export const API = createAPIClient({ // We could also log these to sentry. // Or we could check error.status and redirect to login on a 403 etc. onError: error => { - const { status, method, url, message, handled } = error || {} + const { status, method, url, message, handled, suppressErrors } = + error || {} const ignoreErrorUrls = [ "bbtel", "/api/global/self", @@ -49,7 +50,7 @@ export const API = createAPIClient({ } // Notify all errors - if (message) { + if (message && !suppressErrors) { // Don't notify if the URL contains the word analytics as it may be // blocked by browser extensions let ignore = false diff --git a/packages/client/src/components/app/GridBlock.svelte b/packages/client/src/components/app/GridBlock.svelte new file mode 100644 index 0000000000..4314f6e99c --- /dev/null +++ b/packages/client/src/components/app/GridBlock.svelte @@ -0,0 +1,71 @@ + + +
+ +
+ + diff --git a/packages/client/src/components/app/index.js b/packages/client/src/components/app/index.js index 70074790ac..060c15a857 100644 --- a/packages/client/src/components/app/index.js +++ b/packages/client/src/components/app/index.js @@ -36,6 +36,7 @@ export { default as markdownviewer } from "./MarkdownViewer.svelte" export { default as embeddedmap } from "./embedded-map/EmbeddedMap.svelte" export { default as grid } from "./Grid.svelte" export { default as sidepanel } from "./SidePanel.svelte" +export { default as gridblock } from "./GridBlock.svelte" export * from "./charts" export * from "./forms" export * from "./table" diff --git a/packages/frontend-core/src/api/index.js b/packages/frontend-core/src/api/index.js index 739e0fe711..7b823d28c2 100644 --- a/packages/frontend-core/src/api/index.js +++ b/packages/frontend-core/src/api/index.js @@ -75,7 +75,11 @@ export const createAPIClient = config => { let cache = {} // Generates an error object from an API response - const makeErrorFromResponse = async (response, method) => { + const makeErrorFromResponse = async ( + response, + method, + suppressErrors = false + ) => { // Try to read a message from the error let message = response.statusText let json = null @@ -96,6 +100,7 @@ export const createAPIClient = config => { url: response.url, method, handled: true, + suppressErrors, } } @@ -119,6 +124,7 @@ export const createAPIClient = config => { json = true, external = false, parseResponse, + suppressErrors = false, }) => { // Ensure we don't do JSON processing if sending a GET request json = json && method !== "GET" @@ -174,7 +180,7 @@ export const createAPIClient = config => { } } else { delete cache[url] - throw await makeErrorFromResponse(response, method) + throw await makeErrorFromResponse(response, method, suppressErrors) } } @@ -228,6 +234,14 @@ export const createAPIClient = config => { invalidateCache: () => { cache = {} }, + + // Generic utility to extract the current app ID. Assumes that any client + // that exists in an app context will be attaching our app ID header. + getAppID: () => { + let headers = {} + config?.attachHeaders(headers) + return headers?.["x-budibase-app-id"] + }, } // Attach all endpoints diff --git a/packages/frontend-core/src/api/rows.js b/packages/frontend-core/src/api/rows.js index 41ba505d81..8e8570ea2a 100644 --- a/packages/frontend-core/src/api/rows.js +++ b/packages/frontend-core/src/api/rows.js @@ -16,14 +16,16 @@ export const buildRowEndpoints = API => ({ /** * Creates or updates a row in a table. * @param row the row to save + * @param suppressErrors whether or not to suppress error notifications */ - saveRow: async row => { + saveRow: async (row, suppressErrors = false) => { if (!row?.tableId) { return } return await API.post({ url: `/api/${row.tableId}/rows`, body: row, + suppressErrors, }) }, diff --git a/packages/frontend-core/src/components/grid/cells/AttachmentCell.svelte b/packages/frontend-core/src/components/grid/cells/AttachmentCell.svelte index 4d830723c2..c80b12cf1a 100644 --- a/packages/frontend-core/src/components/grid/cells/AttachmentCell.svelte +++ b/packages/frontend-core/src/components/grid/cells/AttachmentCell.svelte @@ -138,10 +138,12 @@ top: 100%; left: 0; width: 320px; - background: var(--background); + background: var(--grid-background-alt); border: var(--cell-border); padding: var(--cell-padding); box-shadow: 0 0 20px -4px rgba(0, 0, 0, 0.15); + border-bottom-left-radius: 2px; + border-bottom-right-radius: 2px; } .dropzone.invertX { left: auto; diff --git a/packages/frontend-core/src/components/grid/cells/GridCell.svelte b/packages/frontend-core/src/components/grid/cells/GridCell.svelte index 9316699743..fe4bd70ba4 100644 --- a/packages/frontend-core/src/components/grid/cells/GridCell.svelte +++ b/packages/frontend-core/src/components/grid/cells/GridCell.svelte @@ -132,7 +132,7 @@ --cell-color: var(--user-color); } .cell.focused { - --cell-color: var(--spectrum-global-color-blue-400); + --cell-color: var(--accent-color); } .cell.error { --cell-color: var(--spectrum-global-color-red-500); diff --git a/packages/frontend-core/src/components/grid/cells/GutterCell.svelte b/packages/frontend-core/src/components/grid/cells/GutterCell.svelte index 56c4c20d54..dd11066b98 100644 --- a/packages/frontend-core/src/components/grid/cells/GutterCell.svelte +++ b/packages/frontend-core/src/components/grid/cells/GutterCell.svelte @@ -9,7 +9,7 @@ export let rowFocused = false export let rowHovered = false export let rowSelected = false - export let disableExpand = false + export let expandable = false export let disableNumber = false export let defaultHeight = false export let disabled = false @@ -24,13 +24,6 @@ selectedRows.actions.toggleRow(id) } } - - const expand = () => { - svelteDispatch("expand") - if (row) { - dispatch("edit-row", row) - } - } - {:else if $config.allowExpandRows} -
- + {:else} +
+ svelteDispatch("expand")} + />
{/if}
diff --git a/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte b/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte index 21ee210233..3604dd6d42 100644 --- a/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte +++ b/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte @@ -163,7 +163,7 @@ Edit column @@ -171,7 +171,7 @@ icon="Label" on:click={makeDisplayColumn} disabled={idx === "sticky" || - !$config.allowEditColumns || + !$config.allowSchemaChanges || bannedDisplayColumnTypes.includes(column.schema.type)} > Use as display column @@ -197,10 +197,12 @@ Move right Hide column + Hide column + @@ -218,7 +220,7 @@ .header-cell :global(.cell) { padding: 0 var(--cell-padding); gap: calc(2 * var(--cell-spacing)); - background: var(--spectrum-global-color-gray-100); + background: var(--grid-background-alt); } .name { diff --git a/packages/frontend-core/src/components/grid/cells/LongFormCell.svelte b/packages/frontend-core/src/components/grid/cells/LongFormCell.svelte index 886d4ef8b0..233533c446 100644 --- a/packages/frontend-core/src/components/grid/cells/LongFormCell.svelte +++ b/packages/frontend-core/src/components/grid/cells/LongFormCell.svelte @@ -102,7 +102,7 @@ top: 0; left: 0; width: calc(100% + var(--max-cell-render-width-overflow)); - height: var(--max-cell-render-height); + height: calc(var(--row-height) + var(--max-cell-render-height)); z-index: 1; border-radius: 2px; resize: none; diff --git a/packages/frontend-core/src/components/grid/cells/OptionsCell.svelte b/packages/frontend-core/src/components/grid/cells/OptionsCell.svelte index 9399617eb7..41335be265 100644 --- a/packages/frontend-core/src/components/grid/cells/OptionsCell.svelte +++ b/packages/frontend-core/src/components/grid/cells/OptionsCell.svelte @@ -132,10 +132,7 @@ {option} {#if values.includes(option)} - + {/if} {/each} @@ -223,6 +220,8 @@ overflow-y: auto; border: var(--cell-border); box-shadow: 0 0 20px -4px rgba(0, 0, 0, 0.15); + border-bottom-left-radius: 2px; + border-bottom-right-radius: 2px; } .options.invertX { left: auto; @@ -240,7 +239,7 @@ justify-content: space-between; align-items: center; gap: var(--cell-spacing); - background-color: var(--background); + background-color: var(--grid-background-alt); } .option:hover, .option.focused { diff --git a/packages/frontend-core/src/components/grid/cells/RelationshipCell.svelte b/packages/frontend-core/src/components/grid/cells/RelationshipCell.svelte index 40b6de5eaa..8dea24f09e 100644 --- a/packages/frontend-core/src/components/grid/cells/RelationshipCell.svelte +++ b/packages/frontend-core/src/components/grid/cells/RelationshipCell.svelte @@ -42,6 +42,8 @@ let candidateIndex let lastSearchId let searching = false + let valuesHeight = 0 + let container $: oneRowOnly = schema?.relationshipType === "one-to-many" $: editable = focused && !readonly @@ -138,6 +140,7 @@ const open = async () => { isOpen = true + valuesHeight = container.getBoundingClientRect().height // Find the primary display for the related table if (!primaryDisplay) { @@ -242,8 +245,14 @@ }) -
-
+
+
1} @@ -290,6 +299,7 @@ class:invertY on:wheel|stopPropagation use:clickOutside={close} + style="--values-height:{valuesHeight}px;" > {#if isRowSelected(row)} - + {/if}
{/each} @@ -340,7 +346,7 @@ min-height: var(--row-height); max-height: var(--row-height); overflow: hidden; - --max-relationship-height: 120px; + --max-relationship-height: 96px; } .wrapper.focused { position: absolute; @@ -352,6 +358,10 @@ max-height: none; overflow: visible; } + .wrapper.invertY { + top: auto; + bottom: 0; + } .container { min-height: var(--row-height); @@ -450,16 +460,17 @@ left: 0; width: 100%; max-height: calc( - var(--max-cell-render-height) + var(--row-height) - - var(--max-relationship-height) + var(--max-cell-render-height) + var(--row-height) - var(--values-height) ); - background: var(--background); + background: var(--grid-background-alt); border: var(--cell-border); box-shadow: 0 0 20px -4px rgba(0, 0, 0, 0.15); display: flex; flex-direction: column; align-items: stretch; padding: 0 0 8px 0; + border-bottom-left-radius: 2px; + border-bottom-right-radius: 2px; } .dropdown.invertY { transform: translateY(-100%); diff --git a/packages/frontend-core/src/components/grid/controls/AddColumnButton.svelte b/packages/frontend-core/src/components/grid/controls/AddColumnButton.svelte index 6ad241eb65..20125bc49c 100644 --- a/packages/frontend-core/src/components/grid/controls/AddColumnButton.svelte +++ b/packages/frontend-core/src/components/grid/controls/AddColumnButton.svelte @@ -10,7 +10,7 @@ quiet size="M" on:click={() => dispatch("add-column")} - disabled={!$config.allowAddColumns} + disabled={!$config.allowSchemaChanges} > Add column diff --git a/packages/frontend-core/src/components/grid/controls/BulkDeleteHandler.svelte b/packages/frontend-core/src/components/grid/controls/BulkDeleteHandler.svelte index f87b529390..3218ef9ffe 100644 --- a/packages/frontend-core/src/components/grid/controls/BulkDeleteHandler.svelte +++ b/packages/frontend-core/src/components/grid/controls/BulkDeleteHandler.svelte @@ -6,15 +6,9 @@ let modal - $: selectedRowCount = Object.values($selectedRows).filter(x => !!x).length + $: selectedRowCount = Object.values($selectedRows).length $: rowsToDelete = Object.entries($selectedRows) - .map(entry => { - if (entry[1] === true) { - return $rows.find(x => x._id === entry[0]) - } else { - return null - } - }) + .map(entry => $rows.find(x => x._id === entry[0])) .filter(x => x != null) // Deletion callback when confirmed diff --git a/packages/frontend-core/src/components/grid/controls/ColumnWidthButton.svelte b/packages/frontend-core/src/components/grid/controls/ColumnWidthButton.svelte deleted file mode 100644 index 5ffd968d30..0000000000 --- a/packages/frontend-core/src/components/grid/controls/ColumnWidthButton.svelte +++ /dev/null @@ -1,92 +0,0 @@ - - -
- (open = !open)} - selected={open} - disabled={!allCols.length} - tooltip={$compact ? "Width" : null} - > - {$compact ? "" : "Width"} - -
- - -
- {#each sizeOptions as option} - changeColumnWidth(option.size)} - selected={option.selected} - > - {option.label} - - {/each} - {#if custom} - Custom - {/if} -
-
- - diff --git a/packages/frontend-core/src/components/grid/controls/HideColumnsButton.svelte b/packages/frontend-core/src/components/grid/controls/HideColumnsButton.svelte index fb66b106c9..b8728156db 100644 --- a/packages/frontend-core/src/components/grid/controls/HideColumnsButton.svelte +++ b/packages/frontend-core/src/components/grid/controls/HideColumnsButton.svelte @@ -3,12 +3,13 @@ import { ActionButton, Popover, Toggle, Icon } from "@budibase/bbui" import { getColumnIcon } from "../lib/utils" - const { columns, stickyColumn, compact } = getContext("grid") + const { columns, stickyColumn } = getContext("grid") let open = false let anchor $: anyHidden = $columns.some(col => !col.visible) + $: text = getText($columns) const toggleVisibility = (column, visible) => { columns.update(state => { @@ -38,6 +39,11 @@ }) columns.actions.saveChanges() } + + const getText = columns => { + const hidden = columns.filter(col => !col.visible).length + return hidden ? `Hide columns (${hidden})` : "Hide columns" + }
@@ -48,13 +54,12 @@ on:click={() => (open = !open)} selected={open || anyHidden} disabled={!$columns.length} - tooltip={$compact ? "Columns" : ""} > - {$compact ? "" : "Columns"} + {text}
- +
{#if $stickyColumn} diff --git a/packages/frontend-core/src/components/grid/controls/RowHeightButton.svelte b/packages/frontend-core/src/components/grid/controls/RowHeightButton.svelte deleted file mode 100644 index e9617ce0b6..0000000000 --- a/packages/frontend-core/src/components/grid/controls/RowHeightButton.svelte +++ /dev/null @@ -1,71 +0,0 @@ - - -
- (open = !open)} - selected={open} - tooltip={$compact ? "Height" : null} - > - {$compact ? "" : "Height"} - -
- - -
- {#each sizeOptions as option} - changeRowHeight(option.size)} - > - {option.label} - - {/each} -
-
- - diff --git a/packages/frontend-core/src/components/grid/controls/SizeButton.svelte b/packages/frontend-core/src/components/grid/controls/SizeButton.svelte new file mode 100644 index 0000000000..eb9bf917d9 --- /dev/null +++ b/packages/frontend-core/src/components/grid/controls/SizeButton.svelte @@ -0,0 +1,135 @@ + + +
+ (open = !open)} + selected={open} + disabled={!allCols.length} + > + Size + +
+ + +
+
+ +
+ {#each rowSizeOptions as option} + changeRowHeight(option.size)} + > + {option.label} + + {/each} +
+
+
+ +
+ {#each columnSizeOptions as option} + columns.actions.changeAllColumnWidths(option.size)} + selected={option.selected} + > + {option.label} + + {/each} + {#if custom} + Custom + {/if} +
+
+
+
+ + diff --git a/packages/frontend-core/src/components/grid/controls/SortButton.svelte b/packages/frontend-core/src/components/grid/controls/SortButton.svelte index 2018e83e44..bd75249216 100644 --- a/packages/frontend-core/src/components/grid/controls/SortButton.svelte +++ b/packages/frontend-core/src/components/grid/controls/SortButton.svelte @@ -2,7 +2,7 @@ import { getContext } from "svelte" import { ActionButton, Popover, Select } from "@budibase/bbui" - const { sort, columns, stickyColumn, compact } = getContext("grid") + const { sort, columns, stickyColumn } = getContext("grid") let open = false let anchor @@ -90,13 +90,12 @@ on:click={() => (open = !open)} selected={open} disabled={!columnOptions.length} - tooltip={$compact ? "Sort" : ""} > - {$compact ? "" : "Sort"} + Sort
- +