<template> <div class="grid-view-column" style="width: 200px;" @click="select($event)"> <component :is="getFieldComponent(field.type)" ref="column" :field="field" :value="row['field_' + field.id]" :selected="selected" @update="update" /> </div> </template> <script> import { isElement } from '@baserow/modules/core/utils/dom' import { notifyIf } from '@baserow/modules/core/utils/error' export default { name: 'GridViewField', props: { table: { type: Object, required: true, }, field: { type: Object, required: true, }, row: { type: Object, required: true, }, }, data() { return { /** * Indicates whether the field is selected. */ selected: false, /** * Timestamp of the last the time the user clicked on the field. We need this to * check if it was double clicked. */ clickTimestamp: null, } }, /** * Because the component can be destroyed if it moves out of the viewport we might * need to take some action if the the component is in a selected state. */ beforeDestroy() { if (this.selected) { this.unselect() } }, methods: { getFieldComponent(type) { return this.$registry.get('field', type).getGridViewFieldComponent() }, /** * If the grid field component emits an update event this method will be called * which will actually update the value via the store. */ update(value, oldValue) { this.$store .dispatch('view/grid/updateValue', { table: this.table, row: this.row, field: this.field, value, oldValue, }) .catch((error) => { notifyIf(error, 'column') }) .then(() => { this.$forceUpdate() }) // This is needed because in some cases we do have a value yet, so a watcher of // the value is not guaranteed. This will make sure the component shows the // latest value. this.$forceUpdate() }, /** * Method that is called when a user clicks on the grid field. It wil * @TODO improve speed somehow, maybe with the fastclick library. */ select(event) { const timestamp = new Date().getTime() if (this.selected) { // If the field is already selected we will check if the click is a doubleclick // if it was within 200 ms. The double click event can be useful for components // because they might want to change the editing state. if ( this.clickTimestamp !== null && timestamp - this.clickTimestamp < 200 ) { this.$refs.column.doubleClick() } } else { // If the field is not yet selected we can change the state to selected. this.selected = true this.$nextTick(() => { // Call the select method on the next tick because we want to wait for all // changes to have rendered. this.$refs.column.select() }) // Register a body click event listener so that we can detect if a user has // clicked outside the field. If that happens we want to unselect the field and // possibly save the value. this.$el.clickOutsideEvent = (event) => { if ( // Check if the column is still selected. this.selected && // If the click was outside the column element. !isElement(this.$el, event.target) ) { this.unselect() } } document.body.addEventListener('click', this.$el.clickOutsideEvent) } this.clickTimestamp = timestamp }, unselect() { this.$refs.column.beforeUnSelect() this.$nextTick(() => { this.selected = false }) document.body.removeEventListener('click', this.$el.clickOutsideEvent) }, }, } </script>