2
0
mirror of https://github.com/frappe/books.git synced 2024-11-08 14:50:56 +00:00

fix: QuickEditForm

- Create a new document
- Rename a document
- Auto save document on value change
This commit is contained in:
Faris Ansari 2019-10-06 03:11:16 +05:30
parent 9b9181146e
commit eb348c7210
6 changed files with 186 additions and 23 deletions

View File

@ -1,5 +1,5 @@
<template>
<button class="text-sm px-4 py-2 focus:outline-none rounded-lg" :style="style">
<button class="text-sm px-4 py-2 focus:outline-none rounded-lg" :style="style" v-bind="$attrs" v-on="$listeners">
<slot></slot>
</button>
</template>
@ -15,11 +15,12 @@ export default {
computed: {
style() {
return {
'background-image': this.type === 'primary'
? 'linear-gradient(180deg, #4AC3F8 0%, #2490EF 100%)'
: 'linear-gradient(180deg, #FFFFFF 0%, #F4F4F6 100%)'
}
'background-image':
this.type === 'primary'
? 'linear-gradient(180deg, #4AC3F8 0%, #2490EF 100%)'
: 'linear-gradient(180deg, #FFFFFF 0%, #F4F4F6 100%)'
};
}
}
}
};
</script>

View File

@ -0,0 +1,27 @@
<template>
<div>
<input
ref="input"
class="focus:outline-none w-full"
:class="inputClass"
type="text"
:value="value"
@blur="triggerChange"
/>
</div>
</template>
<script>
export default {
name: 'Data',
props: ['df', 'value', 'inputClass'],
methods: {
focus() {
this.$refs.input.focus();
},
triggerChange(e) {
this.$emit('change', e.target.value);
}
}
};
</script>

View File

@ -0,0 +1,23 @@
import Data from './Data';
import Select from './Select';
export default {
name: 'FormControl',
render(h) {
let controls = {
Data,
Select
};
let { df } = this.$attrs;
return h(controls[df.fieldtype] || Data, {
props: this.$attrs,
on: this.$listeners,
ref: 'control'
});
},
methods: {
focus() {
this.$refs.control.focus();
}
}
};

View File

@ -0,0 +1,35 @@
<template>
<div>
<select
class="appearance-none bg-white rounded-none focus:outline-none w-full"
:class="inputClass"
:value="value"
@blur="triggerChange"
>
<option v-for="option in options" :value="option.value">{{ option.label }}</option>
</select>
</div>
</template>
<script>
export default {
name: 'Select',
props: ['df', 'value', 'inputClass'],
methods: {
triggerChange(e) {
this.$emit('change', e.target.value);
}
},
computed: {
options() {
let options = this.df.options;
return options.map(o => {
if (typeof o === 'string') {
return { label: o, value: o };
}
return o;
});
}
}
};
</script>

View File

@ -1,22 +1,39 @@
<template>
<div class="border-l h-full">
<div class="flex justify-end px-4 pt-4">
<Button>
<Button @click="routeToList">
<XIcon class="w-3 h-3 stroke-current text-gray-700" />
</Button>
<Button @click="insertDoc" type="primary" v-if="doc._notInserted" class="ml-2 flex">
<feather-icon name="check" class="text-white" />
</Button>
</div>
<div class="px-4 pt-2 pb-4 border-b">
<h2 class="font-medium">{{ title }}</h2>
<div class="px-4 pt-2 pb-4 border-b flex items-center justify-between">
<FormControl
ref="titleControl"
v-if="titleDocField"
input-class="focus:shadow-outline-px"
:df="titleDocField"
:value="doc[titleDocField.fieldname]"
@change="value => valueChange(titleDocField, value)"
/>
<span v-if="showSaved" class="text-xs text-gray-600">{{ _('Saved') }}</span>
</div>
<div class="text-xs">
<div
class="grid border-b px-4 py-3"
class="grid border-b"
style="grid-template-columns: 1fr 2fr"
v-for="df in fields"
:key="df.fieldname"
>
<div class="text-gray-600">{{ df.label }}</div>
<div class="text-gray-900">{{ doc[df.fieldname] }}</div>
<div class="py-3 pl-4 text-gray-600">{{ df.label }}</div>
<FormControl
class="py-3 pr-4"
input-class="focus:shadow-outline-px"
:df="df"
:value="doc[df.fieldname]"
@change="value => valueChange(df, value)"
/>
</div>
</div>
</div>
@ -26,28 +43,80 @@
import frappe from 'frappejs';
import Button from '@/components/Button';
import XIcon from '@/components/Icons/X';
import FormControl from '@/components/Controls/FormControl';
export default {
name: 'QuickEditForm',
props: ['doctype', 'name'],
components: {
Button,
XIcon
XIcon,
FormControl
},
data() {
return {
title: '',
meta: null,
doc: {},
fields: []
fields: [],
titleDocField: null,
showSaved: false
};
},
async mounted() {
let meta = frappe.getMeta(this.doctype);
this.doc = await frappe.getDoc(this.doctype, this.name);
this.title = this.doc[meta.titleField];
this.fields = meta
this.meta = frappe.getMeta(this.doctype);
this.fields = this.meta
.getQuickEditFields()
.map(fieldname => meta.getField(fieldname));
.map(fieldname => this.meta.getField(fieldname));
this.titleDocField = this.meta.getField(this.meta.titleField);
await this.fetchDoc();
// setup the title field
if (this.doc._notInserted) {
this.doc.set(this.titleDocField.fieldname, '');
}
setTimeout(() => {
this.$refs.titleControl.focus()
}, 300);
},
methods: {
valueChange(df, value) {
if (!value) return;
let oldValue = this.doc.get(df.fieldname);
if (df.fieldname === 'name' && oldValue !== value && !this.doc._notInserted) {
this.doc.rename(value);
this.doc.once('afterRename', () => {
this.$router.push(`/list/${this.doctype}/${this.doc.name}`);
});
return;
}
this.doc.set(df.fieldname, value);
if (this.doc._dirty && !this.doc._notInserted) {
this.updateDoc();
}
},
async fetchDoc() {
this.doc = await frappe.getDoc(this.doctype, this.name);
this.title = this.doc[this.meta.titleField];
},
async updateDoc() {
try {
await this.doc.update();
this.triggerSaved();
} catch (e) {
await this.fetchDoc();
}
},
triggerSaved() {
this.showSaved = true;
setTimeout(() => (this.showSaved = false), 1000);
},
insertDoc() {
this.doc.insert();
},
routeToList() {
this.$router.push(`/list/${this.doctype}`);
}
}
};
</script>

View File

@ -1,12 +1,20 @@
module.exports = {
theme: {
fontFamily: {
'sans': ['Inter var experimental', 'sans-serif'],
sans: ['Inter var experimental', 'sans-serif']
},
extend: { }
extend: {
spacing: {
'72': '18rem',
'80': '20rem'
},
boxShadow: {
'outline-px': '0 0 0 1px rgba(66,153,225,0.5)'
}
}
},
variants: {
margin: ['responsive', 'first', 'hover', 'focus'],
margin: ['responsive', 'first', 'hover', 'focus']
},
plugins: []
}
};