Browse Source

feat: Child creation from has many cell

Signed-off-by: Pranav C <61551451+pranavxc@users.noreply.github.com>
pull/341/head
Pranav C 3 years ago
parent
commit
8dd4807610
  1. 20
      packages/nc-gui/components/project/spreadsheet/components/cell.vue
  2. 23
      packages/nc-gui/components/project/spreadsheet/components/editableCell.vue
  3. 53
      packages/nc-gui/components/project/spreadsheet/components/editableCell/textAreaCell.vue
  4. 95
      packages/nc-gui/components/project/spreadsheet/components/editableCell/textAreaCellOld.vue
  5. 17
      packages/nc-gui/components/project/spreadsheet/components/expandedForm.vue
  6. 2
      packages/nc-gui/components/project/spreadsheet/components/headerCell.vue
  7. 11
      packages/nc-gui/components/project/spreadsheet/components/virtualCell.vue
  8. 6
      packages/nc-gui/components/project/spreadsheet/components/virtualCell/belogsToCell.vue
  9. 183
      packages/nc-gui/components/project/spreadsheet/components/virtualCell/hasManyCell.vue
  10. 8
      packages/nc-gui/components/project/spreadsheet/components/virtualCell/manyToManyCell.vue
  11. 16
      packages/nc-gui/components/project/spreadsheet/rowsXcDataTable.vue
  12. 47
      packages/nc-gui/components/project/spreadsheet/views/xcGridView.vue
  13. 6
      packages/nc-gui/config/vuetify.options.js
  14. 7
      packages/nocodb/src/lib/noco/common/BaseApiBuilder.ts

20
packages/nc-gui/components/project/spreadsheet/components/tableCell.vue → packages/nc-gui/components/project/spreadsheet/components/cell.vue

@ -1,12 +1,14 @@
<template>
<editable-attachment-cell
:isLocked="isLocked"
:db-alias="dbAlias"
@click.stop="$emit('enableedit')" v-if="isAttachment" :value="value" :column="column"></editable-attachment-cell>
<set-list-cell @click.stop="$emit('enableedit')" v-else-if="isSet" :value="value" :column="column"></set-list-cell>
<!-- <enum-list-editable-cell @click.stop="$emit('enableedit')" v-else-if="isEnum && selected" :value="value" :column="column"></enum-list-editable-cell>-->
<enum-cell @click.stop="$emit('enableedit')" v-else-if="isEnum" :value="value" :column="column"></enum-cell>
<span v-else>{{ value }}</span>
<v-lazy>
<editable-attachment-cell
:isLocked="isLocked"
:db-alias="dbAlias"
@click.stop="$emit('enableedit')" v-if="isAttachment" :value="value" :column="column"></editable-attachment-cell>
<set-list-cell @click.stop="$emit('enableedit')" v-else-if="isSet" :value="value" :column="column"></set-list-cell>
<!-- <enum-list-editable-cell @click.stop="$emit('enableedit')" v-else-if="isEnum && selected" :value="value" :column="column"></enum-list-editable-cell>-->
<enum-cell @click.stop="$emit('enableedit')" v-else-if="isEnum" :value="value" :column="column"></enum-cell>
<span v-else>{{ value }}</span>
</v-lazy>
</template>
<script>
@ -20,7 +22,7 @@ import EnumListEditableCell from "@/components/project/spreadsheet/components/ed
export default {
name: "tableCell",
components: {EnumListEditableCell, EditableAttachmentCell, AttachmentCell, EnumCell, SetListCell},
props: ['value','dbAlias','isLocked','selected'],
props: ['value', 'dbAlias', 'isLocked', 'selected'],
mixins: [cell],
computed: {}
}

23
packages/nc-gui/components/project/spreadsheet/components/editableCell.vue

@ -1,4 +1,5 @@
<template>
<v-lazy>
<div
@keydown.stop.left
@keydown.stop.right
@ -13,7 +14,6 @@
v-model="localState"></editable-attachment-cell>
<boolean-cell :isForm="isForm" v-else-if="isBoolean" v-on="parentListeners"
v-model="localState" @input="$emit('change');"></boolean-cell>
@ -57,7 +57,8 @@
<text-area-cell
:is-form="isForm"
v-else-if="isTextArea" @input="$emit('save')" v-model="localState"
v-else-if="isTextArea"
v-model="localState"
v-on="parentListeners"
></text-area-cell>
<!--<set-list-checkbox-cell :column="column" v-else-if="isSet" v-model="localState"
@ -65,6 +66,7 @@
<text-cell v-else v-model="localState" v-on="$listeners"></text-cell>
</div>
</v-lazy>
</template>
<script>
@ -85,6 +87,7 @@ import EditableAttachmentCell from "@/components/project/spreadsheet/components/
import EnumCell from "@/components/project/spreadsheet/components/cell/enumCell";
import SetListEditableCell from "@/components/project/spreadsheet/components/editableCell/setListEditableCell";
import SetListCell from "@/components/project/spreadsheet/components/cell/setListCell";
import debounce from "debounce";
export default {
name: "editable-cell",
@ -107,11 +110,13 @@ export default {
meta: Object,
ignoreFocus: Boolean,
isForm: Boolean,
active: Boolean,
active: Boolean
},
data: () => ({
changed: false,
destroyed: false
}),
mounted() {
// this.$refs.input.focus();
},
@ -119,6 +124,17 @@ export default {
if (this.changed && !(this.isAttachment || this.isEnum || this.isBoolean || this.isSet || this.isTime)) {
this.$emit('change');
}
this.destroyed = true;
},
methods: {
syncDataDebounce: debounce(async function (self) {
await self.syncData()
}, 1000),
syncData() {
if (!this.destroyed) {
this.$emit('update')
}
}
},
computed: {
localState: {
@ -128,6 +144,7 @@ export default {
set(val) {
this.changed = true;
this.$emit('input', val);
this.syncDataDebounce(this);
}
},
parentListeners() {

53
packages/nc-gui/components/project/spreadsheet/components/editableCell/textAreaCell.vue

@ -1,15 +1,8 @@
<template>
<div>
<div class="d-flex ma-1" v-if="!isForm">
<v-spacer>
</v-spacer>
<v-btn v-if="!isForm" outlined x-small class="mr-1" @click="$emit('cancel')">Cancel</v-btn>
<v-btn v-if="!isForm" x-small color="primary" @click="save">Save</v-btn>
</div>
<textarea v-on="parentListeners" ref="textarea" v-model="localState" rows="3"
@input="isForm && save()"
@keydown.stop.enter></textarea>
</div>
<textarea v-on="parentListeners" ref="textarea" v-model="localState" rows="3"
@keydown.alt.enter.stop
@keydown.shift.enter.stop
></textarea>
</template>
@ -17,41 +10,31 @@
export default {
name: "textAreaCell",
props: {
value: String,
isForm: Boolean
value: String
},
data: () => ({
localState: ''
}),
created() {
this.localState = this.value;
},
mounted() {
this.$refs.textarea && this.$refs.textarea.focus();
}, watch: {
value(val) {
this.localState = val;
},
localState(val) {
if (this.isForm) {
this.$emit('input', val)
}
}
},
methods: {
save() {
this.$emit('input', this.localState)
}
},
computed:{
computed: {
parentListeners(){
localState: {
get() {
return this.value
},
set(val) {
this.$emit('input', val);
}
},
parentListeners() {
const $listeners = {};
if(this.$listeners.blur){
if (this.$listeners.blur) {
$listeners.blur = this.$listeners.blur;
}
if(this.$listeners.focus){
if (this.$listeners.focus) {
$listeners.focus = this.$listeners.focus;
}
@ -64,7 +47,7 @@ export default {
<style scoped>
input, textarea {
width: 100%;
min-height:60px;
min-height: 60px;
height: calc(100% - 28px);
color: var(--v-textColor-base);
}

95
packages/nc-gui/components/project/spreadsheet/components/editableCell/textAreaCellOld.vue

@ -0,0 +1,95 @@
<template>
<div>
<div class="d-flex ma-1" v-if="!isForm">
<v-spacer>
</v-spacer>
<v-btn v-if="!isForm" outlined x-small class="mr-1" @click="$emit('cancel')">Cancel</v-btn>
<v-btn v-if="!isForm" x-small color="primary" @click="save">Save</v-btn>
</div>
<textarea v-on="parentListeners" ref="textarea" v-model="localState" rows="3"
@input="isForm && save()"
@keydown.stop.enter></textarea>
</div>
</template>
<script>
export default {
name: "textAreaCell",
props: {
value: String,
isForm: Boolean
},
data: () => ({
localState: ''
}),
created() {
this.localState = this.value;
},
mounted() {
this.$refs.textarea && this.$refs.textarea.focus();
}, watch: {
value(val) {
this.localState = val;
},
localState(val) {
if (this.isForm) {
this.$emit('input', val)
}
}
},
methods: {
save() {
this.$emit('input', this.localState)
}
},
computed:{
parentListeners(){
const $listeners = {};
if(this.$listeners.blur){
$listeners.blur = this.$listeners.blur;
}
if(this.$listeners.focus){
$listeners.focus = this.$listeners.focus;
}
return $listeners;
},
}
}
</script>
<style scoped>
input, textarea {
width: 100%;
min-height:60px;
height: calc(100% - 28px);
color: var(--v-textColor-base);
}
</style>
<!--
/**
* @copyright Copyright (c) 2021, Xgene Cloud Ltd
*
* @author Naveen MR <oof1lab@gmail.com>
* @author Pranav C Balan <pranavxc@gmail.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
-->

17
packages/nc-gui/components/project/spreadsheet/components/expandedForm.vue

@ -61,6 +61,7 @@
</label>
<virtual-cell
v-if="col.virtual"
:disabledColumns="disabledColumns"
:column="col"
:row="localState"
:nodes="nodes"
@ -69,12 +70,13 @@
:active="true"
:sql-ui="sqlUi"
@loadTableData="reload"
:is-new="isNew"
></virtual-cell>
<div
style="height:100%; width:100%"
class="caption xc-input"
v-if="col.ai || (col.pk && !selectedRowMeta.new)"
v-if="col.ai || (col.pk && !selectedRowMeta.new) || disabledColumns[col._cn]"
@click="col.ai && $toast.info('Auto Increment field is not editable').goAway(3000)"
>
<input
@ -158,6 +160,8 @@
</v-text-field>
</div>
</v-col>
</v-row>
</v-container>
</v-card-text>
@ -210,7 +214,13 @@ export default {
},
availableColumns: [Object, Array],
nodes: [Object],
queryParams: Object
queryParams: Object,
disabledColumns:{
type:Object,
default(){
return {}
}
}
},
name: "expanded-form",
data: () => ({
@ -376,6 +386,7 @@ export default {
.row-col {
& > div > input,
& > div div >input,
& > div > .xc-input > input,
& > div > select,
& > div > .xc-input > select,
@ -406,6 +417,7 @@ export default {
background: #363636;
.row-col {
& > div div > input,
& > div > input,
& > div > .xc-input > input,
& > div > select,
@ -421,6 +433,7 @@ export default {
.row-col {
& > div > input,
& > div div >input,
& > div > .xc-input > input,
& > div > select,
& > div > .xc-input > select,

2
packages/nc-gui/components/project/spreadsheet/components/headerCell.vue

@ -20,6 +20,8 @@
{{ value }}
<span v-if="column.rqd" class="error--text text--lighten-1">&nbsp;*</span>
<v-spacer>
</v-spacer>

11
packages/nc-gui/components/project/spreadsheet/components/virtualCell.vue

@ -1,5 +1,6 @@
<template>
<div>
<v-lazy>
<has-many-cell
v-if="hm"
:row="row"
@ -9,6 +10,7 @@
:nodes="nodes"
:active="active"
:sql-ui="sqlUi"
:is-new="isNew"
v-on="$listeners"
/>
<many-to-many-cell
@ -20,9 +22,11 @@
:nodes="nodes"
:sql-ui="sqlUi"
:active="active"
:is-new="isNew"
v-on="$listeners"
/>
<belongs-to-cell
:disabled-columns="disabledColumns"
v-else-if="bt"
:active="active"
:row="row"
@ -32,8 +36,10 @@
:nodes="nodes"
:api="api"
:sql-ui="sqlUi"
:is-new="isNew"
v-on="$listeners"
/>
</v-lazy>
</div>
</template>
@ -57,6 +63,11 @@ export default {
api: [Object, Function],
active: Boolean,
sqlUi: [Object, Function],
isNew: {
type: Boolean,
default: false
},
disabledColumns:Object
},
computed: {
hm() {

6
packages/nc-gui/components/project/spreadsheet/components/virtualCell/belogsToCell.vue

@ -8,7 +8,7 @@
</v-chip>
</template>
</div>
<div class=" align-center justify-center px-1 flex-shrink-1" :class="{'d-none': !active, 'd-flex':active }">
<div v-if="!isNew" class=" align-center justify-center px-1 flex-shrink-1" :class="{'d-none': !active, 'd-flex':active }">
<x-icon small :color="['primary','grey']" @click="showNewRecordModal">{{
value ? 'mdi-pencil' : 'mdi-plus'
}}
@ -75,7 +75,9 @@ export default {
row: [Object],
api: [Object, Function],
sqlUi: [Object, Function],
active: Boolean
active: Boolean,
isNew:Boolean,
disabledColumns:Object
},
data: () => ({
newRecordModal: false,

183
packages/nc-gui/components/project/spreadsheet/components/virtualCell/hasManyCell.vue

@ -4,9 +4,22 @@
<template v-if="value">
<v-chip
small
v-for="(v,i) in value.map(v=>Object.values(v)[1])"
:color="colors[i%colors.length]" :key="i">
{{ v }}
v-for="(ch,i) in value"
:key="i"
:color="colors[i%colors.length]"
@click="editChild(ch)"
>
{{ Object.values(ch)[1] }}
<div v-show="active" class="mr-n1 ml-2 mt-n1">
<x-icon
:color="['text' , 'textLight']"
x-small
icon.class="unlink-icon"
@click.stop="unlinkChild(ch)"
>mdi-close-thick
</x-icon>
</div>
</v-chip>
</template>
</div>
@ -15,36 +28,40 @@
<x-icon x-small :color="['primary','grey']" @click="showChildListModal" class="ml-2">mdi-arrow-expand</x-icon>
</div>
<!-- <list-items
:count="10"
:meta="childMeta"
:primary-col="childPrimaryCol"
:primary-key="childPrimaryKey"
v-model="newRecordModal"
:api="childApi"
></list-items>-->
<!-- <list-items
:count="10"
:meta="childMeta"
:primary-col="childPrimaryCol"
:primary-key="childPrimaryKey"
v-model="newRecordModal"
:api="childApi"
></list-items>-->
<v-dialog v-if="newRecordModal" v-model="newRecordModal" width="600">
<v-card width="600" color="backgroundColor">
<v-card-title class="textColor--text mx-2">Add Record
<v-spacer>
</v-spacer>
<v-card-title class="textColor--text mx-2 justify-center">Link Record
<v-btn small class="caption" color="primary">
<v-icon small>mdi-plus</v-icon>&nbsp;
Add New Record
</v-btn>
</v-card-title>
<v-card-text>
<v-card-title>
<v-text-field
hide-details
dense
outlined
placeholder="Search record"
class="mb-2 mx-2 caption"
placeholder="Search records"
class=" caption search-field ml-2"
/>
<v-spacer></v-spacer>
<v-btn small class="caption mr-2" color="primary" @click="insertAndAddNewChildRecord">
<v-icon small>mdi-plus</v-icon>&nbsp;
New Record
</v-btn>
</v-card-title>
<v-card-text>
<div class="items-container">
<template v-if="list">
<template v-if="list && list.list && list.list.length">
<v-card
v-for="(ch,i) in list.list"
class="ma-2 child-card"
@ -53,20 +70,29 @@
@click="addChildToParent(ch)"
:key="i"
>
<v-card-title class="primary-value textColor--text text--lighten-2">{{ ch[childPrimaryCol] }}
<span class="grey--text caption primary-key"
<v-card-text class="primary-value textColor--text text--lighten-2 d-flex">
<span class="font-weight-bold"> {{ ch[childPrimaryCol] }}</span>
<span class="grey--text caption primary-key "
v-if="childPrimaryKey">(Primary Key : {{ ch[childPrimaryKey] }})</span>
</v-card-title>
<v-spacer/>
<v-chip v-if="ch[meta._tn]" x-small>
{{ ch[meta._tn][primaryCol] }}
</v-chip>
</v-card-text>
</v-card>
</template>
<div v-else class="text-center py-15 textLight--text">
No items found
</div>
</div>
</v-card-text>
<v-card-actions class="justify-center py-2 flex-column">
<pagination
v-if="list"
v-if="list && list.list && list.list.length"
:size="listPagination.size"
:count="list.count"
v-model="listPagination.page"
@ -164,7 +190,7 @@
:has-many="childMeta.hasMany"
:belongs-to="childMeta.belongsTo"
@cancel="selectedChild = null"
@input="$emit('loadTableData');showChildListModal();"
@input="$emit('loadTableData');loadChildList();"
:table="childMeta.tn"
v-model="selectedChild"
:old-row="{...selectedChild}"
@ -176,8 +202,11 @@
icon-color="warning"
:nodes="nodes"
:query-params="childQueryParams"
ref="expandedForm"
:is-new="isNewChild"
:disabled-columns="disabledChildColumns"
></component>
{{childQueryParams}}
{{ childQueryParams }}
</v-dialog>
@ -204,7 +233,8 @@ export default {
nodes: [Object],
row: [Object],
sqlUi: [Object, Function],
active: Boolean
active: Boolean,
isNew: Boolean
},
data: () => ({
newRecordModal: false,
@ -224,13 +254,17 @@ export default {
childListPagination: {
page: 1,
size: 10
}
},
isNewChild: false
}),
methods: {
async showChildListModal() {
this.childListModal = true;
await this.getChildMeta();
await this.loadChildMeta();
await this.loadChildList();
},
async loadChildList() {
const pid = this.meta.columns.filter((c) => c.pk).map(c => this.row[c._cn]).join('___');
const _cn = this.childMeta.columns.find(c => c.cn === this.hm.cn)._cn;
this.childList = await this.childApi.paginatedList({
@ -239,6 +273,7 @@ export default {
offset: this.childListPagination.size * (this.childListPagination.page - 1),
...this.childQueryParams
})
},
async deleteChild(child) {
this.dialogShow = true;
@ -257,23 +292,30 @@ export default {
}
},
async unlinkChild(child) {
// todo:
// this.dialogShow = true;
// this.confirmMessage =
// 'Do you want to delete the record?';
// 'Do you want to unlink the record?';
// this.confirmAction = async act => {
// if (act === 'hideDialog') {
// this.dialogShow = false;
// } else {
// const id = this.childMeta.columns.filter((c) => c.pk).map(c => child[c._cn]).join('___');
// await this.childApi.delete(id)
// this.showChildListModal();
// this.dialogShow = false;
// this.$emit('loadTableData')
// }
await this.loadChildMeta();
const column = this.childMeta.columns.find(c => c.cn === this.hm.cn);
if (column.rqd) {
this.$toast.info('Unlink is not possible, add to another record.').goAway(3000)
return
}
const _cn = column._cn;
const id = this.childMeta.columns.filter((c) => c.pk).map(c => child[c._cn]).join('___');
await this.childApi.update(id, {[_cn]: null}, child)
this.$emit('loadTableData')
if (this.childListModal) {
this.showChildListModal()
}
// }
// }
},
async getChildMeta() {
async loadChildMeta() {
// todo: optimize
if (!this.childMeta) {
const childTableData = await this.$store.dispatch('sqlMgr/ActSqlOp', [{
@ -288,32 +330,51 @@ export default {
},
async showNewRecordModal() {
this.newRecordModal = true;
await this.getChildMeta();
await this.loadChildMeta();
const _cn = this.childForeignKey;
this.list = await this.childApi.paginatedList({
...this.childQueryParams,
limit: this.listPagination.size,
offset: this.listPagination.size * (this.listPagination.page - 1)
offset: this.listPagination.size * (this.listPagination.page - 1),
where: `~not(${_cn},eq,${this.parentId})~or(${_cn},is,null)`
})
},
async addChildToParent(child) {
const id = this.childMeta.columns.filter((c) => c.pk).map(c => child[c._cn]).join('___');
const pid = this.meta.columns.filter((c) => c.pk).map(c => this.row[c._cn]).join('___');
const _cn = this.childMeta.columns.find(c => c.cn === this.hm.cn)._cn;
const _cn = this.childForeignKey;
this.newRecordModal = false;
await this.childApi.update(id, {
[_cn]: pid
[_cn]: this.parentId
}, {
[_cn]: child[this.childPrimaryKey]
});
this.$emit('loadTableData')
if(this.childListModal){
if (this.childListModal) {
await this.showChildListModal()
}
},
async editChild(child) {
await this.loadChildMeta();
this.isNewChild = false;
this.selectedChild = child;
this.showExpandModal = true;
setTimeout(() => {
this.$refs.expandedForm && this.$refs.expandedForm.reload()
}, 500)
},
async insertAndAddNewChildRecord() {
this.newRecordModal = false;
await this.loadChildMeta();
this.isNewChild = true;
this.selectedChild = {
[this.childForeignKey]: this.parentId
};
this.showExpandModal = true;
setTimeout(() => {
this.$refs.expandedForm && this.$refs.expandedForm.$set(this.$refs.expandedForm.changedColumns, this.childForeignKey, true)
}, 500)
}
},
computed: {
@ -325,9 +386,18 @@ export default {
childPrimaryCol() {
return this.childMeta && (this.childMeta.columns.find(c => c.pv) || {})._cn
},
primaryCol() {
return this.meta && (this.meta.columns.find(c => c.pv) || {})._cn
},
childPrimaryKey() {
return this.childMeta && (this.childMeta.columns.find(c => c.pk) || {})._cn
},
childForeignKey() {
return this.childMeta && this.childMeta.columns.find(c => c.cn === this.hm.cn)._cn
},
disabledChildColumns() {
return {[this.childForeignKey]: true}
},
// todo:
form() {
return () => import("@/components/project/spreadsheet/components/expandedForm")
@ -352,6 +422,9 @@ export default {
parents: (this.childMeta && this.childMeta.belongsTo && this.childMeta.belongsTo.map(hm => hm.rtn).join()) || '',
many: (this.childMeta && this.childMeta.manyToMany && this.childMeta.manyToMany.map(mm => mm.rtn).join()) || ''
}
},
parentId() {
return this.meta && this.meta.columns ? this.meta.columns.filter((c) => c.pk).map(c => this.row[c._cn]).join('___') : '';
}
}
}
@ -410,6 +483,24 @@ export default {
margin: 3px auto;
}
::v-deep {
.unlink-icon {
padding: 0px 1px 2px 1px;
margin-top: 2px;
margin-right: -2px;
}
.search-field {
input {
max-height: 28px !important;
}
.v-input__slot {
min-height: auto !important;
}
}
}
</style>
<!--
/**

8
packages/nc-gui/components/project/spreadsheet/components/virtualCell/manyToManyCell.vue

@ -13,7 +13,7 @@
</div>
<div class=" align-center justify-center px-1 flex-shrink-1" :class="{'d-none': !active, 'd-flex':active }">
<x-icon small :color="['primary','grey']" @click="showNewRecordModal">mdi-plus</x-icon>
<!-- <x-icon x-small :color="['primary','grey']" @click="showChildListModal" class="ml-2">mdi-arrow-expand</x-icon>-->
<!-- <x-icon x-small :color="['primary','grey']" @click="showChildListModal" class="ml-2">mdi-arrow-expand</x-icon>-->
</div>
@ -150,7 +150,8 @@ export default {
row: [Object],
api: [Object, Function],
sqlUi: [Object, Function],
active: Boolean
active: Boolean,
isNew: Boolean
},
data: () => ({
newRecordModal: false,
@ -161,7 +162,8 @@ export default {
childList: null,
dialogShow: false,
confirmAction: null,
confirmMessage: ''
confirmMessage: '',
selectedChild:null
}),
methods: {

16
packages/nc-gui/components/project/spreadsheet/rowsXcDataTable.vue

@ -699,6 +699,11 @@ export default {
}) && pks.length && pks.every(col => !rowObj[col._cn])) {
return this.$toast.info('Primary column is empty please provide some value').goAway(3000);
}
if (this.availableColumns.some((col) => {
return col.rqd && (rowObj[col._cn] === undefined || rowObj[col._cn] === null) && !col.default
})) {
return;
}
const insertObj = this.availableColumns.reduce((o, col) => {
if (!col.ai && (rowObj && rowObj[col._cn]) !== null) {
@ -728,7 +733,15 @@ export default {
}
}
},
async onCellValueChange(col, row, column) {
onCellValueChangeDebounce: debounce(async function (col, row, column, self) {
await self.onCellValueChangeFn(col, row, column)
}, 300),
onCellValueChange(col, row, column) {
this.onCellValueChangeDebounce(col, row, column, this)
},
async onCellValueChangeFn(col, row, column) {
if (!this.data[row]) return;
const {row: rowObj, rowMeta, oldRow} = this.data[row];
if (rowMeta.new) {
@ -806,6 +819,7 @@ export default {
const {rowMeta} = this.data[this.data.length - 1];
this.expandRow(this.data.length - 1, rowMeta)
}
this.save()
},

47
packages/nc-gui/components/project/spreadsheet/views/xcGridView.vue

@ -14,7 +14,7 @@
:class="$store.state.windows.darkTheme ? 'grey darken-3 grey--text text--lighten-1' : 'grey lighten-4 grey--text text--darken-2'"
v-xc-ver-resize
v-for="(col,i) in availableColumns"
:key="col.cn"
:key="i + '_' + col._cn"
v-show="showFields[col._cn]"
@xcresize="onresize(col._cn,$event)"
@xcresizing="resizingCol = col._cn"
@ -69,7 +69,7 @@
</tr>
</thead>
<tbody>
<tbody v-click-outside="() => {this.selected.col=null;this.selected.row=null}">
<tr
v-for="({row:rowObj, rowMeta, oldRow},row) in data"
:key="row"
@ -105,23 +105,22 @@
</div>
</td>
<td
class="cell pointer"
v-for="(columnObj,col) in availableColumns"
:key="columnObj._cn"
:key="row + '_' + col + columnObj._cn"
:class="{
active : !isPublicView && selected.col === col && selected.row === row && isEditable ,
'active' : !isPublicView && selected.col === col && selected.row === row && isEditable ,
'primary-column' : primaryValueColumn === columnObj._cn,
'text-center': isCentrallyAligned(columnObj)
'text-center': isCentrallyAligned(columnObj),
'required':columnObj.rqd && (rowObj[columnObj._cn]===undefined || rowObj[columnObj._cn]===null) && !columnObj.default
}"
@dblclick="makeEditable(col,row,columnObj.ai)"
@click="makeSelected(col,row);"
v-show="showFields[columnObj._cn]"
:data-col="columnObj._cn"
>
<virtual-cell
v-if="columnObj.virtual"
v-if="columnObj.virtual "
:column="columnObj"
:row="rowObj"
:nodes="nodes"
@ -132,6 +131,12 @@
v-on="$listeners"
></virtual-cell>
<!--
<span
v-if="columnObj.virtual "
></span>
-->
<editable-cell
v-else-if="
!isLocked
@ -146,12 +151,11 @@
@save="editEnabled = {}"
@cancel="editEnabled = {}"
@update="onCellValueChange(col, row, columnObj)"
@blur="onCellValueChange(col, row, columnObj,'blur')"
@change="onCellValueChange(col, row, columnObj)"
:sql-ui="sqlUi"
:db-alias="nodes.dbAlias"
/>
<!-- @change="changed(col,row)"-->
<!-- />-->
<div v-else-if="columnObj.cn in hasMany" class="hasmany-col d-flex ">
{{ rowObj[columnObj._cn] }}
@ -227,7 +231,7 @@
<v-divider
:key="i + idCol + '_div'"></v-divider>
:key="i + '_' + idCol + '_div'"></v-divider>
<v-list-item
class="py-1"
@click="addNewRelationTab(
@ -241,7 +245,7 @@
rowObj,
rowObj[primaryValueColumn]
)"
:key="i + idCol"
:key="i + '_' + idCol"
dense
>
<v-list-item-icon class="mx-1">
@ -301,7 +305,7 @@
import HeaderCell from "@/components/project/spreadsheet/components/headerCell";
import EditableCell from "@/components/project/spreadsheet/components/editableCell";
import EditColumn from "@/components/project/spreadsheet/components/editColumn";
import TableCell from "@/components/project/spreadsheet/components/tableCell";
import TableCell from "@/components/project/spreadsheet/components/cell";
import colors from "@/mixins/colors";
import columnStyling from "@/components/project/spreadsheet/helpers/columnStyling";
import HasManyCell from "@/components/project/spreadsheet/components/virtualCell/hasManyCell";
@ -398,6 +402,15 @@ export default {
case 13:
this.makeEditable(this.selected.col, this.selected.row)
break;
default: {
if (this.editEnabled.col != null && this.editEnabled.row != null) {
return;
}
console.log(this.selected, this.data[this.selected.row], this.availableColumns[this.selected.col], '')
this.$set(this.data[this.selected.row].row, this.availableColumns[this.selected.col]._cn, '')
this.editEnabled = {...this.selected}
}
}
},
onNewColCreation() {
@ -411,8 +424,8 @@ export default {
showRowContextMenu($event, rowObj, rowMeta, row) {
this.$emit('showRowContextMenu', $event, rowObj, rowMeta, row)
},
onCellValueChange(col, row, column) {
this.$emit('onCellValueChange', col, row, column)
onCellValueChange(col, row, column, ev) {
this.$emit('onCellValueChange', col, row, column, ev);
},
addNewRelationTab(...args) {
this.$emit('addNewRelationTab', ...args)
@ -743,6 +756,10 @@ tbody tr:hover {
.cell {
font-size: 13px;
&.required {
box-shadow: inset 0 0 0 1px red;
}
}
th::before {

6
packages/nc-gui/config/vuetify.options.js

@ -18,13 +18,17 @@ export default function ({app}) {
primary: '#0989ff',
'x-active': '#e91e63',
textColor: '#ffffff',
backgroundColor: '#363636',
text: '#ffffff',
textLight: '#b3b3b3',
backgroundColor: '#969696',
backgroundColorDefault: '#1f1f1f'
},
light: {
primary: '#0989ff',
'x-active': '#e91e63',
textColor: '#333333',
text: '#333333',
textLight: '#929292',
backgroundColor: '#f7f7f7',
backgroundColorDefault: '#ffffff',
}

7
packages/nocodb/src/lib/noco/common/BaseApiBuilder.ts

@ -1551,7 +1551,12 @@ export default abstract class BaseApiBuilder<T extends Noco> implements XcDynami
// Update metadata of tables which have manytomany relation
// and recreate basemodel with new meta information
for (const meta of metas) {
meta.v = [...meta.v, ...meta.manyToMany.map(mm => ({mm, _cn:`${mm._tn} <=> ${mm._rtn}`}))]
meta.v = [
...meta.v.filter(vc => vc.bt && meta.manyToMany.some(mm => vc.bt.rtn === mm.vtn)),
...meta.manyToMany.map(mm => ({
mm,
_cn: `${mm._tn} <=> ${mm._rtn}`
}))]
await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', {
meta: JSON.stringify(meta)
}, {title: meta.tn})

Loading…
Cancel
Save