1
0
Fork 0
mirror of https://gitlab.com/bramw/baserow.git synced 2025-05-12 04:11:49 +00:00

Multi-Cell Paste

This commit is contained in:
Zuhair Rayyes 2022-05-05 07:42:53 +00:00 committed by Bram Wiepjes
parent 37a27a795a
commit 22ee2f79a5
22 changed files with 456 additions and 185 deletions
web-frontend/modules/database/components/view/grid

View file

@ -34,6 +34,7 @@
@cell-mouseup-left="multiSelectStop"
@add-row="addRow()"
@update="updateValue"
@paste="multiplePasteFromCell"
@edit="editValue"
@selected="selectedCell($event)"
@unselected="unselectedCell($event)"
@ -79,6 +80,7 @@
@row-context="showRowContext($event.event, $event.row)"
@add-row="addRow()"
@update="updateValue"
@paste="multiplePasteFromCell"
@edit="editValue"
@cell-mousedown-left="multiSelectStart"
@cell-mouseover="multiSelectHold"
@ -176,6 +178,7 @@
<script>
import { mapGetters } from 'vuex'
import Papa from 'papaparse'
import { notifyIf } from '@baserow/modules/core/utils/error'
import GridViewSection from '@baserow/modules/database/components/view/grid/GridViewSection'
@ -184,8 +187,8 @@ import GridViewRowDragging from '@baserow/modules/database/components/view/grid/
import RowEditModal from '@baserow/modules/database/components/row/RowEditModal'
import gridViewHelpers from '@baserow/modules/database/mixins/gridViewHelpers'
import { maxPossibleOrderValue } from '@baserow/modules/database/viewTypes'
import { isElement } from '@baserow/modules/core/utils/dom'
import viewHelpers from '@baserow/modules/database/mixins/viewHelpers'
import { isElement } from '@baserow/modules/core/utils/dom'
export default {
name: 'GridView',
@ -326,7 +329,8 @@ export default {
window.addEventListener('resize', this.$el.resizeEvent)
window.addEventListener('keydown', this.arrowEvent)
window.addEventListener('copy', this.exportMultiSelect)
window.addEventListener('click', this.cancelMultiSelect)
window.addEventListener('paste', this.pasteFromMultipleCellSelection)
window.addEventListener('click', this.cancelMultiSelectIfActive)
window.addEventListener('mouseup', this.multiSelectStop)
this.$refs.left.$el.addEventListener(
'scroll',
@ -341,14 +345,14 @@ export default {
window.removeEventListener('resize', this.$el.resizeEvent)
window.removeEventListener('keydown', this.arrowEvent)
window.removeEventListener('copy', this.exportMultiSelect)
window.removeEventListener('click', this.cancelMultiSelect)
window.removeEventListener('paste', this.pasteFromMultipleCellSelection)
window.removeEventListener('click', this.cancelMultiSelectIfActive)
window.removeEventListener('mouseup', this.multiSelectStop)
this.$bus.$off('field-deleted', this.fieldDeleted)
this.$store.dispatch(
this.storePrefix + 'view/grid/setMultiSelectActive',
false
this.storePrefix + 'view/grid/clearAndDisableMultiSelect'
)
this.$store.dispatch(this.storePrefix + 'view/grid/clearMultiSelect')
},
methods: {
/**
@ -749,10 +753,8 @@ export default {
}
this.$store.dispatch(
this.storePrefix + 'view/grid/setMultiSelectActive',
false
this.storePrefix + 'view/grid/clearAndDisableMultiSelect'
)
this.$store.dispatch(this.storePrefix + 'view/grid/clearMultiSelect')
this.$store.dispatch(this.storePrefix + 'view/grid/setSelectedCell', {
rowId: nextRowId,
@ -773,46 +775,44 @@ export default {
this.fieldsUpdated()
})
},
/*
Called when mouse is clicked and held on a GridViewCell component.
Starts multi-select by setting the head and tail index to the currently
selected cell.
*/
/**
* Called when mouse is clicked and held on a GridViewCell component.
* Starts multi-select by setting the head and tail index to the currently
* selected cell.
*/
multiSelectStart({ event, row, field }) {
this.$store.dispatch(this.storePrefix + 'view/grid/multiSelectStart', {
rowId: row.id,
fieldIndex: this.visibleFields.findIndex((f) => f.id === field.id) + 1,
})
},
/*
Called when mouse hovers over a GridViewCell component.
Updates the current multi-select grid by updating the tail index
with the last cell hovered over.
*/
/**
* Called when mouse hovers over a GridViewCell component.
* Updates the current multi-select grid by updating the tail index
* with the last cell hovered over.
*/
multiSelectHold({ event, row, field }) {
this.$store.dispatch(this.storePrefix + 'view/grid/multiSelectHold', {
rowId: row.id,
fieldIndex: this.visibleFields.findIndex((f) => f.id === field.id) + 1,
})
},
/*
Called when the mouse is unpressed over a GridViewCell component.
Stop multi-select.
*/
/**
* Called when the mouse is unpressed over a GridViewCell component.
* Stop multi-select.
*/
multiSelectStop({ event, row, field }) {
this.$store.dispatch(
this.storePrefix + 'view/grid/setMultiSelectHolding',
false
)
},
/*
Cancels multi-select if it's currently active.
This function checks if a mouse click event is triggered
outside of GridViewRows. This is done by ensuring that the
target element's class is either 'grid-view', 'grid-view__row',
or 'grid-view__rows'.
*/
cancelMultiSelect(event) {
/**
* Cancels multi-select if it's currently active.
* This function checks if a mouse click event is triggered
* outside of GridViewRows.
*/
cancelMultiSelectIfActive(event) {
if (
this.$store.getters[
this.storePrefix + 'view/grid/isMultiSelectActive'
@ -823,10 +823,8 @@ export default {
))
) {
this.$store.dispatch(
this.storePrefix + 'view/grid/setMultiSelectActive',
false
this.storePrefix + 'view/grid/clearAndDisableMultiSelect'
)
this.$store.dispatch(this.storePrefix + 'view/grid/clearMultiSelect')
}
},
arrowEvent(event) {
@ -841,14 +839,15 @@ export default {
]
) {
this.$store.dispatch(
this.storePrefix + 'view/grid/setMultiSelectActive',
false
this.storePrefix + 'view/grid/clearAndDisableMultiSelect'
)
this.$store.dispatch(this.storePrefix + 'view/grid/clearMultiSelect')
}
}
},
// Prepare and copy the multi-select cells into the clipboard, formatted as TSV
/**
* Prepare and copy the multi-select cells into the clipboard,
* formatted as TSV
*/
async exportMultiSelect(event) {
try {
this.$store.dispatch('notification/setCopying', true)
@ -856,8 +855,10 @@ export default {
this.storePrefix + 'view/grid/exportMultiSelect',
this.leftFields.concat(this.visibleFields)
)
// If the output is undefined, it means that there is no multiple selection.
if (output !== undefined) {
navigator.clipboard.writeText(output)
const tsv = Papa.unparse(output, { delimiter: '\t' })
navigator.clipboard.writeText(tsv)
}
} catch (error) {
notifyIf(error, 'view')
@ -865,6 +866,73 @@ export default {
this.$store.dispatch('notification/setCopying', false)
}
},
/**
* Called when the @paste event is triggered from the `GridViewSection` component.
* This happens when the individual cell doesn't understand the pasted data and
* needs to emit it up. This typically happens when multiple cell values are pasted.
*/
async multiplePasteFromCell({ data, field, row }) {
const rowIndex = this.$store.getters[
this.storePrefix + 'view/grid/getRowIndexById'
](row.id)
const fieldIndex =
this.visibleFields.findIndex((f) => f.id === field.id) + 1
await this.pasteData(data, rowIndex, fieldIndex)
},
/**
* Called when the user pastes data without having an individual cell selected. It
* only works when a multiple selection is active because then we know in which
* cells we can paste the data.
*/
async pasteFromMultipleCellSelection(event) {
if (!this.isMultiSelectActive) {
return
}
const parsed = await Papa.parsePromise(
event.clipboardData.getData('text'),
{ delimiter: '\t' }
)
const data = parsed.data
await this.pasteData(data)
},
/**
* Called when data must be pasted into the grid view. It basically forwards the
* request to a store action which handles the actual updating of rows. It also
* shows a loading animation while busy, so the user knows something is while the
* update is in progress.
*/
async pasteData(data, rowIndex, fieldIndex) {
// If the data is an empty array, we don't have to do anything because there is
// nothing to update. If the view is in read only mode, we can't paste so not
// doing anything.
if (data.length === 0 || data[0].length === 0 || this.readOnly) {
return
}
this.$store.dispatch('notification/setPasting', true)
try {
await this.$store.dispatch(
this.storePrefix + 'view/grid/updateDataIntoCells',
{
table: this.table,
view: this.view,
primary: this.primary,
fields: this.leftFields.concat(this.visibleFields),
getScrollTop: () => this.$refs.left.$refs.body.scrollTop,
data,
rowIndex,
fieldIndex,
}
)
} catch (error) {
notifyIf(error)
}
this.$store.dispatch('notification/setPasting', false)
return true
},
},
}
</script>