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:
parent
37a27a795a
commit
22ee2f79a5
22 changed files with 456 additions and 185 deletions
web-frontend/modules/database/components/view/grid
|
@ -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>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue