2
0
mirror of https://github.com/frappe/books.git synced 2025-01-10 18:24:40 +00:00

More flexible Base, add more controls

- Autocomplete
- Check
- Code
- Currency
- Float
- Link
This commit is contained in:
Faris Ansari 2018-06-04 14:57:10 +05:30
parent 0887c4e74c
commit 7eee7cd24f
11 changed files with 265 additions and 92 deletions

View File

@ -1,5 +1,17 @@
<template>
<form class="frappe-form-layout p-3">
<div class="row" v-if="layout" v-for="(section, i) in layout" :key="i">
<div class="col" v-for="(column, j) in section.columns" :key="j">
<frappe-control
v-for="fieldname in column.fields"
:key="fieldname"
:docfield="getDocField(fieldname)"
:value="$data[fieldname]"
@change="$emit('field-change', fieldname, $event)"
/>
</div>
</div>
<div v-if="!layout">
<frappe-control
v-for="docfield in fields"
:key="docfield.fieldname"
@ -7,6 +19,7 @@
:value="$data[docfield.fieldname]"
@change="$emit('field-change', docfield.fieldname, $event)"
/>
</div>
</form>
</template>
<script>
@ -14,7 +27,7 @@ import FrappeControl from './controls/FrappeControl'
export default {
name: 'FormLayout',
props: ['doc', 'fields'],
props: ['doc', 'fields', 'layout'],
data() {
const dataObj = {};
for (let field of this.fields) {
@ -27,6 +40,11 @@ export default {
this[fieldname] = doc[fieldname];
});
},
methods: {
getDocField(fieldname) {
return this.fields.find(df => df.fieldname === fieldname);
}
},
components: {
FrappeControl
}

View File

@ -4,56 +4,71 @@ import Data from './Data';
export default {
extends: Data,
created() {
data() {
return {
awesomplete: null
}
},
mounted() {
this.setupAwesomplete();
this.awesomplete.container.classList.add('form-control');
this.awesomplete.ul.classList.add('dropdown-menu');
},
methods: {
getInputListeners() {
return {
input: async e => {
this.awesomplete.list = await this.getList(e.target.value);
},
'awesomplete-select': e => {
this.$emit('change', e.text.value);
}
}
},
getList(text) {
return this.docfield.getList(text);
},
setupAwesomplete() {
const input = this.$refs.input;
this.awesomplete = new Awesomplete(input, {
minChars: 0,
maxItems: 99,
filter: () => true,
sort: (a, b) => {
if (a.value === '__newitem' || b.value === '__newitem') {
return -1;
}
return a.value > b.value;
}
});
// rebuild the list on input
this.input.addEventListener('input', async event => {
let list = await this.getList(this.input.value);
// action to add new item
list.push({
label: frappe._('+ New {0}', this.label),
value: '__newItem'
});
this.awesomplete.list = list;
});
// new item action
this.input.addEventListener('awesomplete-select', async e => {
if (e.text && e.text.value === '__newItem') {
e.preventDefault();
const newDoc = await frappe.getNewDoc(this.getTarget());
const formModal = await frappe.desk.showFormModal(
this.getTarget(),
newDoc.name
);
if (formModal.form.doc.meta.hasField('name')) {
formModal.form.doc.set('name', this.input.value);
}
formModal.once('save', async () => {
await this.updateDocValue(formModal.form.doc.name);
});
sort: this.sort(),
item: (text, input) => {
const li = document.createElement('li');
li.classList.add('dropdown-item');
li.classList.add('d-flex');
li.classList.add('align-items-center');
li.innerHTML = text.label;
return li;
}
});
},
sort() {
return null;
}
}
};
</script>
<style lang="scss">
@import "../../styles/variables";
@import "~awesomplete/awesomplete.base";
.awesomplete {
padding: 0;
border: none;
&> ul {
padding: $dropdown-padding-y 0;
}
.dropdown-menu:not([hidden]) {
display: block;
}
.dropdown-item[aria-selected="true"] {
background-color: $dropdown-link-hover-bg;
}
}
</style>

View File

@ -8,12 +8,21 @@ export default {
id() {
return this.docfield.fieldname + '-'
+ document.querySelectorAll(`[data-fieldname="${this.docfield.fieldname}"]`).length;
},
inputClass() {
return [];
},
wrapperClass() {
return [];
},
labelClass() {
return [];
}
},
methods: {
getWrapperElement(h) {
return h('div', {
class: ['form-group'],
class: ['form-group', ...this.wrapperClass],
attrs: {
'data-fieldname': this.docfield.fieldname
}
@ -21,6 +30,7 @@ export default {
},
getLabelElement(h) {
return h('label', {
class: this.labelClass,
attrs: {
for: this.id
},
@ -30,16 +40,19 @@ export default {
});
},
getInputElement(h) {
return h('input', {
class: ['form-control'],
return h(this.getInputTag(), {
class: this.getInputClass(),
attrs: this.getInputAttrs(),
on: {
change: (e) => {
this.$emit('change', e.target.value)
}
},
on: this.getInputListeners(),
domProps: this.getDomProps(),
ref: 'input'
})
}, this.getInputChildren(h));
},
getInputTag() {
return 'input';
},
getInputClass() {
return ['form-control', ...this.inputClass];
},
getInputAttrs() {
return {
@ -48,6 +61,22 @@ export default {
placeholder: '',
value: this.value
}
},
getInputListeners() {
return {
change: (e) => {
this.$emit('change', this.parseValue(e.target.value));
}
};
},
getInputChildren() {
return null;
},
getDomProps() {
return null;
},
parseValue(value) {
return value;
}
}
}

View File

@ -0,0 +1,25 @@
<template>
<div class="form-group">
<div class="custom-control custom-checkbox">
<input class="custom-control-input" type="checkbox" :id="id" v-model="checkboxValue" @change="emitChange">
<label class="custom-control-label" :for="id">{{ docfield.label }}</label>
</div>
</div>
</template>
<script>
import Base from './Base';
export default {
extends: Base,
data() {
return {
checkboxValue: Boolean(this.value)
}
},
methods: {
emitChange(e) {
this.$emit('change', Number(this.checkboxValue));
}
}
}
</script>

View File

@ -0,0 +1,11 @@
<script>
import Text from './Text';
export default {
extends: Text,
computed: {
inputClass() {
return ['text-monospace'];
}
}
}
</script>

View File

@ -0,0 +1,6 @@
<script>
import Float from './Float';
export default {
extends: Float
}
</script>

View File

@ -0,0 +1,24 @@
<script>
import Base from './Base';
export default {
extends: Base,
computed: {
inputClass() {
return ['text-right']
}
},
methods: {
getInputListeners() {
return {
change: e => {
this.$emit('change', e.target.value)
},
focus: e => {
setTimeout(() => this.$refs.input.select(), 100);
}
};
},
}
}
</script>

View File

@ -2,21 +2,30 @@
<component :is="component" :docfield="docfield" :value="value" @change="$emit('change', $event)"/>
</template>
<script>
import Autocomplete from './Autocomplete';
import Check from './Check';
import Code from './Code';
import Currency from './Currency';
import Data from './Data';
import Float from './Float';
import Link from './Link';
import Select from './Select';
import Text from './Text';
import Autocomplete from './Autocomplete';
export default {
props: ['docfield', 'value'],
computed: {
component() {
return {
Autocomplete,
Check,
Code,
Currency,
Data,
Float,
Link,
Select,
Text,
Autocomplete,
Link: Autocomplete
}[this.docfield.fieldtype];
}
}

View File

@ -0,0 +1,45 @@
<script>
import frappe from 'frappejs';
import feather from 'feather-icons';
import Autocomplete from './Autocomplete';
export default {
extends: Autocomplete,
methods: {
async getList(query) {
const list = await frappe.db.getAll({
doctype: this.docfield.target,
filters: {
keywords: ["like", query]
},
fields: ['name'],
limit: 50
});
const plusIcon = feather.icons.plus.toSvg({
class: 'm-1',
width: 16,
height: 16
});
return list
.map(d => ({
label: d.name,
value: d.name
}))
.concat({
label: plusIcon + ' New ' + this.docfield.target,
value: '__newItem'
})
},
sort() {
return (a, b) => {
if (a.value === '__newitem' || b.value === '__newitem') {
return -1;
}
return a.value > b.value;
}
}
}
}
</script>

View File

@ -3,8 +3,16 @@ import Base from './Base';
export default {
extends: Base,
methods: {
getInputElement(h) {
const options = this.docfield.options.map(option =>
getInputTag() {
return 'select';
},
getInputAttrs() {
return {
id: this.id
};
},
getInputChildren(h) {
return this.docfield.options.map(option =>
h('option', {
attrs: {
key: option,
@ -15,19 +23,6 @@ export default {
}
})
);
return h('select', {
class: ['form-control'],
attrs: {
id: this.id
},
on: {
change: (e) => {
this.$emit('change', e.target.value)
}
},
ref: 'input'
}, options)
}
}
}

View File

@ -3,23 +3,19 @@ import Base from './Base';
export default {
extends: Base,
methods: {
getInputElement(h) {
return h('textarea', {
class: ['form-control'],
attrs: {
getInputTag() {
return 'textarea';
},
getInputAttrs() {
return {
id: this.id,
rows: 3
};
},
domProps: {
getDomProps() {
return {
value: this.value
},
on: {
change: (e) => {
this.$emit('change', e.target.value)
}
},
ref: 'input'
});
}
}
}