<template> <div class="grid-view-column" @click="select()"> <component :is="getFieldComponent(field.type)" ref="field" :field="field" :value="row['field_' + field.id]" :selected="selected" @update="update" /> </div> </template> <script> import { isElement } from '@baserow/modules/core/utils/dom' export default { name: 'GridViewField', props: { 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.$emit('update', { row: this.row, field: this.field, value, oldValue, }) }, /** * Method that is called when a user clicks on the grid field. It wil * @TODO improve speed somehow, maybe with the fastclick library. */ select() { 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.field.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.field.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) // If the tab key is pressed want to select the next field. This is however out // of the scope of the component so we emit the selectNext event that the // GridView can handle. this.$el.tabPressedEvent = (event) => { if (event.keyCode !== 9) { return } event.preventDefault() this.$emit(event.shiftKey ? 'selectPrevious' : 'selectNext') } document.body.addEventListener('keydown', this.$el.tabPressedEvent) // Emit the selected event so that the parent component can take an action like // making sure that the element fits in the viewport. this.$emit('selected', { component: this, }) } this.clickTimestamp = timestamp }, unselect() { this.$refs.field.beforeUnSelect() this.$nextTick(() => { this.selected = false }) document.body.removeEventListener('click', this.$el.clickOutsideEvent) document.body.removeEventListener('keydown', this.$el.tabPressedEvent) }, }, } </script>