mirror of
https://gitlab.com/bramw/baserow.git
synced 2024-11-24 16:36:46 +00:00
376 lines
11 KiB
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>
|