diff --git a/packages/frontend-core/src/components/grid/controls/BulkDeleteHandler.svelte b/packages/frontend-core/src/components/grid/controls/BulkDeleteHandler.svelte index cb90f12293..1f835c3991 100644 --- a/packages/frontend-core/src/components/grid/controls/BulkDeleteHandler.svelte +++ b/packages/frontend-core/src/components/grid/controls/BulkDeleteHandler.svelte @@ -2,7 +2,8 @@ import { Modal, ModalContent } from "@budibase/bbui" import { getContext, onMount } from "svelte" - const { selectedRows, rows, subscribe, notifications } = getContext("grid") + const { selectedRows, rows, subscribe, notifications, menu } = + getContext("grid") let modal @@ -16,6 +17,9 @@ const count = rowsToDelete.length await rows.actions.deleteRows(rowsToDelete) $notifications.success(`Deleted ${count} row${count === 1 ? "" : "s"}`) + + // Ensure menu is closed, as we may have triggered this from there + menu.actions.close() } onMount(() => subscribe("request-bulk-delete", () => modal?.show())) diff --git a/packages/frontend-core/src/components/grid/overlays/MenuOverlay.svelte b/packages/frontend-core/src/components/grid/overlays/MenuOverlay.svelte index 4a413d7dde..db6e82293d 100644 --- a/packages/frontend-core/src/components/grid/overlays/MenuOverlay.svelte +++ b/packages/frontend-core/src/components/grid/overlays/MenuOverlay.svelte @@ -20,6 +20,7 @@ focusedRowId, notifications, hasBudibaseIdentifiers, + selectedRowCount, } = getContext("grid") let anchor @@ -37,6 +38,10 @@ $notifications.success("Deleted 1 row") } + const bulkDelete = () => { + dispatch("request-bulk-delete") + } + const duplicate = async () => { menu.actions.close() const newRow = await rows.actions.duplicateRow($focusedRow) @@ -58,59 +63,80 @@ {#key style} - - Copy - - - Paste - - dispatch("edit-row", $focusedRow)} - on:click={menu.actions.close} - > - Edit row in modal - - copyToClipboard($focusedRow?._id)} - on:click={menu.actions.close} - > - Copy row _id - - copyToClipboard($focusedRow?._rev)} - on:click={menu.actions.close} - > - Copy row _rev - - - Duplicate row - - - Delete row - + {#if $menu.multiRowMode} + + Duplicate {$selectedRowCount} rows + + + Delete {$selectedRowCount} rows + + {:else} + + Copy + + + Paste + + dispatch("edit-row", $focusedRow)} + on:click={menu.actions.close} + > + Edit row in modal + + copyToClipboard($focusedRow?._id)} + on:click={menu.actions.close} + > + Copy row _id + + copyToClipboard($focusedRow?._rev)} + on:click={menu.actions.close} + > + Copy row _rev + + + Duplicate row + + + Delete row + + {/if} {/key} diff --git a/packages/frontend-core/src/components/grid/stores/menu.js b/packages/frontend-core/src/components/grid/stores/menu.js index d7f0e21b84..9120ad4a9f 100644 --- a/packages/frontend-core/src/components/grid/stores/menu.js +++ b/packages/frontend-core/src/components/grid/stores/menu.js @@ -1,4 +1,5 @@ -import { writable } from "svelte/store" +import { writable, get } from "svelte/store" +import { parseCellID } from "../lib/utils" export const createStores = () => { const menu = writable({ @@ -13,7 +14,8 @@ export const createStores = () => { } export const createActions = context => { - const { menu, focusedCellId, gridID } = context + const { menu, focusedCellId, gridID, selectedRows, selectedRowCount } = + context const open = (cellId, e) => { e.preventDefault() @@ -29,11 +31,26 @@ export const createActions = context => { // Compute bounds of cell relative to outer data node const targetBounds = e.target.getBoundingClientRect() const dataBounds = dataNode.getBoundingClientRect() - focusedCellId.set(cellId) + + // Check if there are multiple rows selected, and this is one of them + let multiRowMode = false + if (get(selectedRowCount) > 1) { + const rowId = parseCellID(cellId).id + if (get(selectedRows)[rowId]) { + multiRowMode = true + } + } + + // Only focus this cell if not in multi row mode + if (!multiRowMode) { + focusedCellId.set(cellId) + } + menu.set({ left: targetBounds.left - dataBounds.left + e.offsetX, top: targetBounds.top - dataBounds.top + e.offsetY, visible: true, + multiRowMode, }) } diff --git a/packages/frontend-core/src/components/grid/stores/ui.js b/packages/frontend-core/src/components/grid/stores/ui.js index 37cdf67007..6280b05eca 100644 --- a/packages/frontend-core/src/components/grid/stores/ui.js +++ b/packages/frontend-core/src/components/grid/stores/ui.js @@ -85,7 +85,7 @@ export const deriveStores = context => { }) // Derive we have any selected rows or not - const hasSelectedRows = derived(selectedRows, $selectedRows => { + const selectedRowCount = derived(selectedRows, $selectedRows => { return Object.keys($selectedRows).length }) @@ -94,7 +94,7 @@ export const deriveStores = context => { focusedRow, contentLines, compact, - hasSelectedRows, + selectedRowCount, } } @@ -105,7 +105,7 @@ export const createActions = context => { selectedRows, rowLookupMap, rows, - hasSelectedRows, + selectedRowCount, } = context // Keep the last selected index to use with bulk selection let lastSelectedIndex = null @@ -133,7 +133,7 @@ export const createActions = context => { } const bulkSelectRows = id => { - if (!get(hasSelectedRows)) { + if (!get(selectedRowCount)) { toggleSelectedRow(id) return } @@ -187,7 +187,7 @@ export const initialise = context => { definition, rowHeight, fixedRowHeight, - hasSelectedRows, + selectedRowCount, } = context // Ensure we clear invalid rows from state if they disappear @@ -245,7 +245,7 @@ export const initialise = context => { } // Clear row selection when focusing a cell - if (id && get(hasSelectedRows)) { + if (id && get(selectedRowCount)) { selectedRows.set({}) } }) @@ -267,8 +267,8 @@ export const initialise = context => { }) // Clear focused cell when selecting rows - hasSelectedRows.subscribe(selected => { - if (get(focusedCellId) && selected) { + selectedRowCount.subscribe(count => { + if (get(focusedCellId) && count) { focusedCellId.set(null) } })