Clean up and improve copy/paste flows
This commit is contained in:
parent
a86f891c04
commit
502c2541e5
|
@ -10,7 +10,6 @@
|
|||
focusedCellId,
|
||||
stickyColumn,
|
||||
columns,
|
||||
menu,
|
||||
selectedRowCount,
|
||||
} = getContext("grid")
|
||||
|
||||
|
@ -18,7 +17,6 @@
|
|||
|
||||
// Deletion callback when confirmed
|
||||
const performDuplication = async () => {
|
||||
menu.actions.close()
|
||||
const rowsToDuplicate = Object.keys($selectedRows).map(id => {
|
||||
return rows.actions.getRow(id)
|
||||
})
|
||||
|
@ -27,9 +25,6 @@
|
|||
const column = $stickyColumn?.name || $columns[0].name
|
||||
$focusedCellId = getCellID(newRows[0]._id, column)
|
||||
}
|
||||
|
||||
// Ensure menu is closed, as we may have triggered this from there
|
||||
menu.actions.close()
|
||||
}
|
||||
|
||||
onMount(() => subscribe("request-bulk-duplicate", () => modal?.show()))
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
<script>
|
||||
import { Modal, ModalContent } from "@budibase/bbui"
|
||||
import { getContext, onMount } from "svelte"
|
||||
|
||||
const { clipboard, subscribe, copyAllowed, pasteAllowed, selectedCellCount } =
|
||||
getContext("grid")
|
||||
|
||||
let modal
|
||||
|
||||
const copy = () => {
|
||||
if (!$copyAllowed) {
|
||||
return
|
||||
}
|
||||
clipboard.actions.copy()
|
||||
}
|
||||
|
||||
const paste = async () => {
|
||||
if (!$pasteAllowed) {
|
||||
return
|
||||
}
|
||||
// Prompt if paste will update multiple cells
|
||||
const multiCellPaste = $selectedCellCount > 1
|
||||
const prompt = $clipboard.multiCellCopy || multiCellPaste
|
||||
if (prompt) {
|
||||
modal?.show()
|
||||
} else {
|
||||
clipboard.actions.paste()
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => subscribe("copy", copy))
|
||||
onMount(() => subscribe("paste", paste))
|
||||
</script>
|
||||
|
||||
<Modal bind:this={modal}>
|
||||
<ModalContent
|
||||
title="Confirm bulk paste"
|
||||
confirmText="Continue"
|
||||
cancelText="Cancel"
|
||||
onConfirm={clipboard.actions.paste}
|
||||
size="M"
|
||||
>
|
||||
Are you sure you want to paste values into {$selectedCellCount} cells?
|
||||
</ModalContent>
|
||||
</Modal>
|
|
@ -8,6 +8,7 @@
|
|||
import { attachStores } from "../stores"
|
||||
import BulkDeleteHandler from "../controls/BulkDeleteHandler.svelte"
|
||||
import BulkDuplicationHandler from "../controls/BulkDuplicationHandler.svelte"
|
||||
import ClipboardHandler from "../controls/ClipboardHandler.svelte"
|
||||
import GridBody from "./GridBody.svelte"
|
||||
import ResizeOverlay from "../overlays/ResizeOverlay.svelte"
|
||||
import ReorderOverlay from "../overlays/ReorderOverlay.svelte"
|
||||
|
@ -216,6 +217,7 @@
|
|||
{#if $config.canAddRows}
|
||||
<BulkDuplicationHandler />
|
||||
{/if}
|
||||
<ClipboardHandler />
|
||||
<KeyboardManager />
|
||||
</div>
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
focusedRow,
|
||||
stickyColumn,
|
||||
focusedCellAPI,
|
||||
clipboard,
|
||||
dispatch,
|
||||
selectedRows,
|
||||
config,
|
||||
|
@ -97,12 +96,10 @@
|
|||
if (e.metaKey || e.ctrlKey) {
|
||||
switch (e.key) {
|
||||
case "c":
|
||||
clipboard.actions.copy()
|
||||
dispatch("copy")
|
||||
break
|
||||
case "v":
|
||||
if (!api?.isReadonly()) {
|
||||
clipboard.actions.paste()
|
||||
}
|
||||
dispatch("paste")
|
||||
break
|
||||
case "Enter":
|
||||
if ($config.canAddRows) {
|
||||
|
|
|
@ -13,14 +13,13 @@
|
|||
focusedCellId,
|
||||
stickyColumn,
|
||||
config,
|
||||
copiedCell,
|
||||
clipboard,
|
||||
dispatch,
|
||||
focusedCellAPI,
|
||||
focusedRowId,
|
||||
notifications,
|
||||
hasBudibaseIdentifiers,
|
||||
selectedRowCount,
|
||||
copyAllowed,
|
||||
pasteAllowed,
|
||||
} = getContext("grid")
|
||||
|
||||
let anchor
|
||||
|
@ -33,16 +32,12 @@
|
|||
}
|
||||
|
||||
const deleteRow = () => {
|
||||
rows.actions.deleteRows([$focusedRow])
|
||||
menu.actions.close()
|
||||
rows.actions.deleteRows([$focusedRow])
|
||||
$notifications.success("Deleted 1 row")
|
||||
}
|
||||
|
||||
const bulkDelete = () => {
|
||||
dispatch("request-bulk-delete")
|
||||
}
|
||||
|
||||
const duplicate = async () => {
|
||||
const duplicateRow = async () => {
|
||||
menu.actions.close()
|
||||
const newRow = await rows.actions.duplicateRow($focusedRow)
|
||||
if (newRow) {
|
||||
|
@ -51,10 +46,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
const bulkDuplicate = () => {
|
||||
dispatch("request-bulk-duplicate")
|
||||
}
|
||||
|
||||
const copyToClipboard = async value => {
|
||||
await Helpers.copyToClipboard(value)
|
||||
$notifications.success("Copied to clipboard")
|
||||
|
@ -71,29 +62,32 @@
|
|||
<MenuItem
|
||||
icon="Duplicate"
|
||||
disabled={!$config.canAddRows || $selectedRowCount > 50}
|
||||
on:click={bulkDuplicate}
|
||||
on:click={() => dispatch("request-bulk-duplicate")}
|
||||
on:click={menu.actions.close}
|
||||
>
|
||||
Duplicate {$selectedRowCount} rows
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
icon="Delete"
|
||||
disabled={!$config.canDeleteRows}
|
||||
on:click={bulkDelete}
|
||||
on:click={() => dispatch("request-bulk-delete")}
|
||||
on:click={menu.actions.close}
|
||||
>
|
||||
Delete {$selectedRowCount} rows
|
||||
</MenuItem>
|
||||
{:else if $menu.multiCellMode}
|
||||
<MenuItem
|
||||
icon="Copy"
|
||||
on:click={clipboard.actions.copy}
|
||||
disabled={!$copyAllowed}
|
||||
on:click={() => dispatch("copy")}
|
||||
on:click={menu.actions.close}
|
||||
>
|
||||
Copy
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
icon="Paste"
|
||||
disabled={$copiedCell == null || $focusedCellAPI?.isReadonly()}
|
||||
on:click={clipboard.actions.paste}
|
||||
disabled={!$pasteAllowed}
|
||||
on:click={() => dispatch("paste")}
|
||||
on:click={menu.actions.close}
|
||||
>
|
||||
Paste
|
||||
|
@ -104,15 +98,16 @@
|
|||
{:else}
|
||||
<MenuItem
|
||||
icon="Copy"
|
||||
on:click={clipboard.actions.copy}
|
||||
disabled={!$copyAllowed}
|
||||
on:click={() => dispatch("copy")}
|
||||
on:click={menu.actions.close}
|
||||
>
|
||||
Copy
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
icon="Paste"
|
||||
disabled={$copiedCell == null || $focusedCellAPI?.isReadonly()}
|
||||
on:click={clipboard.actions.paste}
|
||||
disabled={!$pasteAllowed}
|
||||
on:click={() => dispatch("paste")}
|
||||
on:click={menu.actions.close}
|
||||
>
|
||||
Paste
|
||||
|
@ -148,7 +143,7 @@
|
|||
<MenuItem
|
||||
icon="Duplicate"
|
||||
disabled={isNewRow || !$config.canAddRows}
|
||||
on:click={duplicate}
|
||||
on:click={duplicateRow}
|
||||
>
|
||||
Duplicate row
|
||||
</MenuItem>
|
||||
|
|
|
@ -1,20 +1,79 @@
|
|||
import { writable, get } from "svelte/store"
|
||||
import { derived, writable, get } from "svelte/store"
|
||||
import { Helpers } from "@budibase/bbui"
|
||||
|
||||
export const createStores = () => {
|
||||
const copiedCell = writable(null)
|
||||
const clipboard = writable({
|
||||
value: null,
|
||||
multiCellMode: false,
|
||||
})
|
||||
|
||||
return {
|
||||
copiedCell,
|
||||
clipboard,
|
||||
}
|
||||
}
|
||||
|
||||
export const deriveStores = context => {
|
||||
const { clipboard, focusedCellAPI, selectedCellCount } = context
|
||||
|
||||
const copyAllowed = derived(focusedCellAPI, $focusedCellAPI => {
|
||||
return $focusedCellAPI != null
|
||||
})
|
||||
|
||||
const pasteAllowed = derived(
|
||||
[clipboard, focusedCellAPI, selectedCellCount],
|
||||
([$clipboard, $focusedCellAPI, $selectedCellCount]) => {
|
||||
if ($clipboard.value == null || !$focusedCellAPI) {
|
||||
return false
|
||||
}
|
||||
// Prevent pasting into a single cell, if we have a single cell value and
|
||||
// this cell is readonly
|
||||
const multiCellPaste = $selectedCellCount > 1
|
||||
if (
|
||||
!$clipboard.multiCellMode &&
|
||||
!multiCellPaste &&
|
||||
$focusedCellAPI.isReadonly()
|
||||
) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
)
|
||||
|
||||
return {
|
||||
copyAllowed,
|
||||
pasteAllowed,
|
||||
}
|
||||
}
|
||||
|
||||
export const createActions = context => {
|
||||
const { copiedCell, focusedCellAPI } = context
|
||||
const {
|
||||
clipboard,
|
||||
selectedCellCount,
|
||||
focusedCellAPI,
|
||||
copyAllowed,
|
||||
pasteAllowed,
|
||||
} = context
|
||||
|
||||
const copy = () => {
|
||||
const value = get(focusedCellAPI)?.getValue()
|
||||
copiedCell.set(value)
|
||||
if (!get(copyAllowed)) {
|
||||
return
|
||||
}
|
||||
const $selectedCellCount = get(selectedCellCount)
|
||||
const $focusedCellAPI = get(focusedCellAPI)
|
||||
const multiCellMode = $selectedCellCount > 1
|
||||
|
||||
// Multiple values to copy
|
||||
if (multiCellMode) {
|
||||
// TODO
|
||||
return
|
||||
}
|
||||
|
||||
// Single value to copy
|
||||
const value = $focusedCellAPI.getValue()
|
||||
clipboard.set({
|
||||
value,
|
||||
multiCellMode,
|
||||
})
|
||||
|
||||
// Also copy a stringified version to the clipboard
|
||||
let stringified = ""
|
||||
|
@ -26,15 +85,38 @@ export const createActions = context => {
|
|||
}
|
||||
|
||||
const paste = () => {
|
||||
const $copiedCell = get(copiedCell)
|
||||
if (!get(pasteAllowed)) {
|
||||
return
|
||||
}
|
||||
const $clipboard = get(clipboard)
|
||||
const $focusedCellAPI = get(focusedCellAPI)
|
||||
if ($copiedCell != null && $focusedCellAPI) {
|
||||
$focusedCellAPI.setValue($copiedCell)
|
||||
if ($clipboard.value == null || !$focusedCellAPI) {
|
||||
return
|
||||
}
|
||||
|
||||
// Check if we're pasting into one or more cells
|
||||
const $selectedCellCount = get(selectedCellCount)
|
||||
const multiCellPaste = $selectedCellCount > 1
|
||||
|
||||
if ($clipboard.multiCellMode) {
|
||||
if (multiCellPaste) {
|
||||
// Multi to multi (only paste selected cells)
|
||||
} else {
|
||||
// Multi to single (expand to paste all values)
|
||||
}
|
||||
} else {
|
||||
if (multiCellPaste) {
|
||||
// Single to multi (duplicate value in all selected cells)
|
||||
} else {
|
||||
// Single to single
|
||||
$focusedCellAPI.setValue($clipboard.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
clipboard: {
|
||||
...clipboard,
|
||||
actions: {
|
||||
copy,
|
||||
paste,
|
||||
|
|
|
@ -41,11 +41,11 @@ const DependencyOrderedStores = [
|
|||
Users,
|
||||
Menu,
|
||||
Pagination,
|
||||
Selection,
|
||||
Clipboard,
|
||||
Config,
|
||||
Notifications,
|
||||
Cache,
|
||||
Selection,
|
||||
]
|
||||
|
||||
export const attachStores = context => {
|
||||
|
|
Loading…
Reference in New Issue