1
0
mirror of https://gitlab.com/bramw/baserow.git synced 2024-11-24 16:36:46 +00:00
bramw_baserow/web-frontend/modules/database/components/row/RowEditModal.vue

376 lines
11 KiB
Vue

<template>
<Modal
ref="modal"
:full-height="hasRightSidebar"
:right-sidebar="hasRightSidebar"
:content-scrollable="hasRightSidebar"
:right-sidebar-scrollable="false"
:collapsible-right-sidebar="true"
@hidden="hidden"
>
<template #actions>
<component
:is="actionComponent"
v-for="(actionComponent, i) in rowModalActionComponents"
:key="i"
:database="database"
:table="table"
:row="row"
>
</component>
</template>
<template #content>
<div v-if="enableNavigation" class="row-edit-modal__navigation">
<div v-if="navigationLoading" class="loading"></div>
<template v-else>
<a
class="row-edit-modal__navigation-item"
@click="$emit('navigate-previous', previousRow)"
>
<i class="iconoir-nav-arrow-up"></i>
</a>
<a
class="row-edit-modal__navigation-item"
@click="$emit('navigate-next', nextRow)"
>
<i class="iconoir-nav-arrow-down"></i>
</a>
</template>
</div>
<div class="box__title">
<h2 class="row-modal__title">
{{ heading }}
</h2>
</div>
<RowEditModalFieldsList
:primary-is-sortable="primaryIsSortable"
:fields="visibleFields"
:sortable="!readOnly && fieldsSortable"
:can-modify-fields="!readOnly && canModifyFields"
:hidden="false"
:read-only="readOnly"
:row="row"
:view="view"
:table="table"
:database="database"
:all-fields-in-table="allFieldsInTable"
@field-updated="$emit('field-updated', $event)"
@field-deleted="$emit('field-deleted')"
@order-fields="$emit('order-fields', $event)"
@toggle-field-visibility="$emit('toggle-field-visibility', $event)"
@update="update"
@refresh-row="$emit('refresh-row', $event)"
></RowEditModalFieldsList>
<RowEditModalHiddenFieldsSection
v-if="hiddenFields.length"
:show-hidden-fields="showHiddenFields"
@toggle-hidden-fields-visibility="
$emit('toggle-hidden-fields-visibility')
"
>
<RowEditModalFieldsList
:primary-is-sortable="primaryIsSortable"
:fields="hiddenFields"
:sortable="false"
:hidden="true"
:read-only="readOnly"
:row="row"
:view="view"
:table="table"
:database="database"
:all-fields-in-table="allFieldsInTable"
@field-updated="$emit('field-updated', $event)"
@field-deleted="$emit('field-deleted')"
@toggle-field-visibility="$emit('toggle-field-visibility', $event)"
@update="update"
@refresh-row="$emit('refresh-row', $event)"
>
</RowEditModalFieldsList>
</RowEditModalHiddenFieldsSection>
<div
v-if="
!readOnly &&
canModifyFields &&
$hasPermission(
'database.table.create_field',
table,
database.workspace.id
)
"
class="actions"
>
<span ref="createFieldContextLink">
<ButtonText
icon="iconoir-plus"
@click="
$refs.createFieldContext.toggle($refs.createFieldContextLink)
"
>
{{ $t('rowEditModal.addField') }}
</ButtonText></span
>
<CreateFieldContext
ref="createFieldContext"
:table="table"
:view="view"
:all-fields-in-table="allFieldsInTable"
:database="database"
@field-created="$emit('field-created', $event)"
@field-created-callback-done="
$emit('field-created-callback-done', $event)
"
></CreateFieldContext>
</div>
</template>
<template #sidebar>
<RowEditModalSidebar
:row="row"
:table="table"
:database="database"
:fields="allFieldsInTable"
:read-only="readOnly"
></RowEditModalSidebar>
</template>
</Modal>
</template>
<script>
import { mapGetters } from 'vuex'
import modal from '@baserow/modules/core/mixins/modal'
import CreateFieldContext from '@baserow/modules/database/components/field/CreateFieldContext'
import RowEditModalFieldsList from './RowEditModalFieldsList.vue'
import RowEditModalHiddenFieldsSection from './RowEditModalHiddenFieldsSection.vue'
import RowEditModalSidebar from './RowEditModalSidebar.vue'
import { getPrimaryOrFirstField } from '@baserow/modules/database/utils/field'
export default {
name: 'RowEditModal',
components: {
CreateFieldContext,
RowEditModalFieldsList,
RowEditModalHiddenFieldsSection,
RowEditModalSidebar,
},
mixins: [modal],
props: {
database: {
type: Object,
required: true,
},
table: {
type: Object,
required: true,
},
view: {
type: [Object, null],
required: false,
default: null,
},
allFieldsInTable: {
type: Array,
required: true,
},
primaryIsSortable: {
type: Boolean,
required: false,
default: false,
},
visibleFields: {
type: Array,
required: true,
},
hiddenFields: {
type: Array,
required: false,
default: () => [],
},
showHiddenFields: {
type: Boolean,
required: false,
default: false,
},
rows: {
type: Array,
required: true,
},
readOnly: {
type: Boolean,
required: true,
},
enableNavigation: {
type: Boolean,
required: false,
default: false,
},
fieldsSortable: {
type: Boolean,
required: false,
default: () => true,
},
canModifyFields: {
type: Boolean,
required: false,
default: () => true,
},
},
computed: {
...mapGetters({
navigationLoading: 'rowModalNavigation/getLoading',
}),
modalRow() {
return this.$store.getters['rowModal/get'](this._uid)
},
rowId() {
return this.modalRow.id
},
rowExists() {
return this.modalRow.exists
},
row() {
return this.modalRow.row
},
rowIndex() {
return this.rows.findIndex((r) => r !== null && r.id === this.rowId)
},
nextRow() {
return this.rowIndex !== -1 && this.rows.length > this.rowIndex + 1
? this.rows[this.rowIndex + 1]
: null
},
previousRow() {
return this.rowIndex > 0 ? this.rows[this.rowIndex - 1] : null
},
heading() {
const field = getPrimaryOrFirstField(this.visibleFields)
if (!field) {
return null
}
const name = `field_${field.id}`
if (Object.prototype.hasOwnProperty.call(this.row, name)) {
return this.$registry
.get('field', field.type)
.toHumanReadableString(field, this.row[name])
} else {
return null
}
},
activeSidebarTypes() {
const allSidebarTypes = this.$registry.getOrderedList('rowModalSidebar')
return allSidebarTypes.filter(
(type) =>
type.isDeactivated(this.database, this.table, this.readOnly) ===
false && type.getComponent()
)
},
hasRightSidebar() {
return this.activeSidebarTypes.length > 0
},
canSubscribeToRowUpdates() {
return (
this.$hasPermission(
'database.table.listen_to_all',
this.table,
this.database.workspace.id
) &&
// If the row ID is not an integer, it could mean that the row hasn't been
// created in the backend yet.
Number.isInteger(this.rowId)
)
},
rowModalActionComponents() {
return this.activeSidebarTypes
.map((type) => type.getActionComponent(this.row))
.filter((actionComponent) => actionComponent !== null)
},
},
watch: {
/**
* It could happen that the view doesn't always have all the rows buffered. When
* the modal is opened, it will find the correct row by looking through all the
* rows of the view. If a filter changes, the existing row could be removed from the
* buffer while the user still wants to edit the row because the modal is open. In
* that case, we will keep a copy in the `rowModal` store, which will also listen
* for real time update events to make sure the latest information is always
* visible. If the buffer of the view changes and the row does exist, we want to
* switch to that version to maintain reactivity between the two.
*/
rows(value) {
const row = value.find((r) => r !== null && r.id === this.rowId)
if (row === undefined && this.rowExists) {
this.$store.dispatch('rowModal/doesNotExist', {
componentId: this._uid,
})
} else if (row !== undefined && !this.rowExists) {
this.$store.dispatch('rowModal/doesExist', {
componentId: this._uid,
row,
})
} else if (row !== undefined) {
// If the row already exists and it has changed, we need to replace it,
// otherwise we might loose reactivity.
this.$store.dispatch('rowModal/replace', {
componentId: this._uid,
row,
})
}
},
rowId(newValue, oldValue) {
if (this.canSubscribeToRowUpdates) {
if (oldValue > 0) {
this.$realtime.unsubscribe('row', {
table_id: this.table.id,
row_id: oldValue,
})
}
if (newValue > 0) {
this.$realtime.subscribe('row', {
table_id: this.table.id,
row_id: newValue,
})
}
}
},
},
methods: {
show(rowId, rowFallback = {}, ...args) {
const row = this.rows.find((r) => r !== null && r.id === rowId)
this.$store.dispatch('rowModal/open', {
tableId: this.table.id,
componentId: this._uid,
id: rowId,
row: row || rowFallback,
exists: !!row,
})
if (this.canSubscribeToRowUpdates) {
this.$realtime.subscribe('row', {
table_id: this.table.id,
row_id: rowId,
})
}
this.getRootModal().show(...args)
},
hidden(...args) {
if (this.canSubscribeToRowUpdates) {
this.$realtime.unsubscribe('row', {
table_id: this.table.id,
row_id: this.rowId,
})
}
this.$store.dispatch('rowModal/clear', { componentId: this._uid })
this.$emit('hidden', { row: this.row })
},
/**
* Because the modal can't update values by himself, an event will be called to
* notify the parent component to actually update the value.
*/
update(context) {
context.table = this.table
this.$emit('update', context)
},
},
}
</script>