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

fix: CommonForm name field readonly

- CommonForm clear tempname on focus
- remove inline editing of inline docs
- remove "inline" config on schema fields
- show select placeholder only if no label
- rename inlineEditDisplayField to linkDisplayField
This commit is contained in:
18alantom 2023-03-02 13:42:16 +05:30
parent 48e9f1b668
commit 4b65fbb50d
13 changed files with 58 additions and 158 deletions

View File

@ -84,5 +84,5 @@
"country", "country",
"postalCode" "postalCode"
], ],
"inlineEditDisplayField": "addressDisplay" "linkDisplayField": "addressDisplay"
} }

View File

@ -74,7 +74,7 @@
"label": "Address", "label": "Address",
"fieldtype": "Link", "fieldtype": "Link",
"target": "Address", "target": "Address",
"inline": true "create": true
}, },
{ {
"fieldname": "taxId", "fieldname": "taxId",

View File

@ -34,7 +34,6 @@
"label": "Address", "label": "Address",
"fieldtype": "Link", "fieldtype": "Link",
"target": "Address", "target": "Address",
"inline": true,
"create": true, "create": true,
"section": "Contacts" "section": "Contacts"
}, },

View File

@ -16,7 +16,7 @@
"label": "Address", "label": "Address",
"fieldtype": "Link", "fieldtype": "Link",
"target": "Address", "target": "Address",
"inline": true "create": true
} }
], ],
"quickEditFields": ["item", "address"] "quickEditFields": ["item", "address"]

View File

@ -50,8 +50,8 @@ function removeFields(schemaMap: SchemaMap): SchemaMap {
(fn) => fn !== fieldname (fn) => fn !== fieldname
); );
if (schema.inlineEditDisplayField === fieldname) { if (schema.linkDisplayField === fieldname) {
delete schema.inlineEditDisplayField; delete schema.linkDisplayField;
} }
} }

View File

@ -72,8 +72,7 @@
"fieldname": "address", "fieldname": "address",
"label": "Address", "label": "Address",
"fieldtype": "Link", "fieldtype": "Link",
"target": "Address", "target": "Address"
"inline": true
} }
], ],
"quickEditFields": [ "quickEditFields": [

View File

@ -61,7 +61,6 @@ export interface BaseField {
placeholder?: string; // UI Facing config, form field placeholder placeholder?: string; // UI Facing config, form field placeholder
groupBy?: string; // UI Facing used in dropdowns fields groupBy?: string; // UI Facing used in dropdowns fields
meta?: boolean; // Field is a meta field, i.e. only for the db, not UI meta?: boolean; // Field is a meta field, i.e. only for the db, not UI
inline?: boolean; // UI Facing config, whether to display doc inline.
filter?: boolean; // UI Facing config, whether to be used to filter the List. filter?: boolean; // UI Facing config, whether to be used to filter the List.
computed?: boolean; // Computed values are not stored in the database. computed?: boolean; // Computed values are not stored in the database.
section?: string; // UI Facing config, for grouping by sections section?: string; // UI Facing config, for grouping by sections
@ -118,7 +117,7 @@ export interface Schema {
isSubmittable?: boolean; // For transactional types, values considered only after submit isSubmittable?: boolean; // For transactional types, values considered only after submit
keywordFields?: string[]; // Used to get fields that are to be used for search. keywordFields?: string[]; // Used to get fields that are to be used for search.
quickEditFields?: string[]; // Used to get fields for the quickEditForm quickEditFields?: string[]; // Used to get fields for the quickEditForm
inlineEditDisplayField?:string;// Display field if inline editable linkDisplayField?:string;// Display field if inline editable
naming?: Naming; // Used for assigning name, default is 'random' else 'numberSeries' if present naming?: Naming; // Used for assigning name, default is 'random' else 'numberSeries' if present
titleField?: string; // Main display field titleField?: string; // Main display field
removeFields?: string[]; // Used by the builder to remove fields. removeFields?: string[]; // Used by the builder to remove fields.

View File

@ -48,6 +48,12 @@ export default {
}); });
}, },
methods: { methods: {
clear() {
const input = this.$refs.control.$refs.input;
if (input instanceof HTMLInputElement) {
input.value = '';
}
},
focus() { focus() {
this.$refs.control.focus(); this.$refs.control.focus();
}, },

View File

@ -35,7 +35,7 @@ export default {
const value = newValue ?? this.value; const value = newValue ?? this.value;
const { fieldname, target } = this.df ?? {}; const { fieldname, target } = this.df ?? {};
const displayField = fyo.schemaMap[target ?? '']?.inlineEditDisplayField; const displayField = fyo.schemaMap[target ?? '']?.linkDisplayField;
if (!displayField) { if (!displayField) {
return (this.linkValue = value); return (this.linkValue = value);

View File

@ -23,7 +23,12 @@
@change="(e) => triggerChange(e.target.value)" @change="(e) => triggerChange(e.target.value)"
@focus="(e) => $emit('focus', e)" @focus="(e) => $emit('focus', e)"
> >
<option value="" disabled selected v-if="inputPlaceholder"> <option
value=""
disabled
selected
v-if="inputPlaceholder && !showLabel"
>
{{ inputPlaceholder }} {{ inputPlaceholder }}
</option> </option>
<option <option

View File

@ -1,8 +1,8 @@
<template> <template>
<div class="text-sm" :class="{ 'border-t': !noBorder }"> <div class="text-sm border-t">
<template v-for="df in formFields"> <template v-for="df in formFields">
<!-- Table Field Form (Eg: PaymentFor) --> <!-- Table Field Form (Eg: PaymentFor) -->
<FormControl <Table
v-if="df.fieldtype === 'Table'" v-if="df.fieldtype === 'Table'"
:key="`${df.fieldname}-table`" :key="`${df.fieldname}-table`"
ref="controls" ref="controls"
@ -13,53 +13,13 @@
:read-only="readOnly" :read-only="readOnly"
/> />
<!-- Inline Field Form (Eg: Address) -->
<div
v-else-if="renderInline(df)"
class="border-b"
:key="`${df.fieldname}-inline`"
>
<TwoColumnForm
class="overflow-auto custom-scroll"
style="max-height: calc((var(--h-row-mid) + 1px) * 3 - 1px)"
ref="inlineEditForm"
:doc="inlineEditDoc"
:fields="getInlineEditFields(df)"
:column-ratio="columnRatio"
:no-border="true"
:focus-first-input="true"
:autosave="false"
:read-only="readOnly"
@error="(msg) => $emit('error', msg)"
/>
<div
class="flex px-4 py-4 justify-between items-center"
style="max-height: calc(var(--h-row-mid) + 1px)"
>
<Button class="text-gray-900 w-20" @click="stopInlineEditing">
{{ t`Cancel` }}
</Button>
<Button
type="primary"
class="text-white w-20"
@click="saveInlineEditDoc(df)"
>
{{ t`Save` }}
</Button>
</div>
</div>
<!-- Regular Field Form --> <!-- Regular Field Form -->
<div <div
v-else v-else
class="grid items-center" class="grid items-center border-b"
:class="{
'border-b': !noBorder,
}"
:key="`${df.fieldname}-regular`" :key="`${df.fieldname}-regular`"
:style="{ :style="{
...style, ...style,
height: getFieldHeight(df), height: getFieldHeight(df),
}" }"
> >
@ -69,7 +29,6 @@
<div <div
class="py-2 pe-4" class="py-2 pe-4"
@click="activateInlineEditing(df)"
:class="{ :class="{
'ps-2': df.fieldtype === 'AttachImage', 'ps-2': df.fieldtype === 'AttachImage',
}" }"
@ -78,12 +37,11 @@
ref="controls" ref="controls"
size="small" size="small"
:df="df" :df="df"
:value="getRegularValue(df)" :value="doc[df.fieldname]"
:class="{ 'p-2': df.fieldtype === 'Check' }" :class="{ 'p-2': df.fieldtype === 'Check' }"
:read-only="readOnly" :read-only="readOnly"
:text-end="false" :text-end="false"
@change="async (value) => await onChange(df, value)" @change="async (value) => await onChange(df, value)"
@focus="activateInlineEditing(df)"
@new-doc="async (newdoc) => await onChange(df, newdoc.name)" @new-doc="async (newdoc) => await onChange(df, newdoc.name)"
/> />
<div <div
@ -99,12 +57,12 @@
</template> </template>
<script> <script>
import { Doc } from 'fyo/model/doc'; import { Doc } from 'fyo/model/doc';
import Button from 'src/components/Button.vue';
import FormControl from 'src/components/Controls/FormControl.vue'; import FormControl from 'src/components/Controls/FormControl.vue';
import { handleErrorWithDialog } from 'src/errorHandling'; import { handleErrorWithDialog } from 'src/errorHandling';
import { fyo } from 'src/initFyo'; import { fyo } from 'src/initFyo';
import { getErrorMessage } from 'src/utils'; import { getErrorMessage } from 'src/utils';
import { evaluateHidden } from 'src/utils/doc'; import { evaluateHidden } from 'src/utils/doc';
import Table from './Controls/Table.vue';
export default { export default {
name: 'TwoColumnForm', name: 'TwoColumnForm',
@ -121,7 +79,6 @@ export default {
type: Boolean, type: Boolean,
default: false, default: false,
}, },
noBorder: Boolean,
focusFirstInput: Boolean, focusFirstInput: Boolean,
readOnly: { type: [null, Boolean], default: null }, readOnly: { type: [null, Boolean], default: null },
}, },
@ -132,8 +89,6 @@ export default {
}, },
data() { data() {
return { return {
inlineEditDoc: null,
inlineEditField: null,
formFields: [], formFields: [],
errors: {}, errors: {},
}; };
@ -147,8 +102,7 @@ export default {
}, },
components: { components: {
FormControl, FormControl,
Button, Table,
TwoColumnForm: () => TwoColumnForm,
}, },
mounted() { mounted() {
this.setFormFields(); this.setFormFields();
@ -172,51 +126,25 @@ export default {
return 'calc(var(--h-row-mid) + 1px)'; return 'calc(var(--h-row-mid) + 1px)';
}, },
getRegularValue(df) { async onChange(field, value) {
if (!df.inline) { const { fieldname } = field;
return this.doc[df.fieldname]; delete this.errors[fieldname];
}
const link = this.doc.getLink(df.fieldname);
if (!link) {
return this.doc[df.fieldname];
}
const fieldname = link.schema.inlineEditDisplayField ?? 'name';
return link[fieldname];
},
renderInline(df) {
return (
this.inlineEditField?.fieldname === df?.fieldname && this.inlineEditDoc
);
},
async onChange(df, value) {
if (df.inline) {
return;
}
// handle rename
if (this.autosave && df.fieldname === 'name' && this.doc.inserted) {
return this.doc.rename(value);
}
const oldValue = this.doc.get(df.fieldname);
this.errors[df.fieldname] = null;
await this.onChangeCommon(df, value, oldValue);
},
async onChangeCommon(df, value, oldValue) {
let isSet = false; let isSet = false;
const oldValue = this.doc.get(fieldname);
try { try {
isSet = await this.doc.set(df.fieldname, value); isSet = await this.doc.set(fieldname, value);
} catch (err) { } catch (err) {
this.errors[df.fieldname] = getErrorMessage(err, this.doc); if (!(err instanceof Error)) {
return;
}
this.errors[fieldname] = getErrorMessage(err, this.doc);
} }
if (!isSet) { if (isSet) {
return; await this.handlePostSet(field, value, oldValue);
} }
await this.handlePostSet(df, value, oldValue);
}, },
async handlePostSet(df, value, oldValue) { async handlePostSet(df, value, oldValue) {
this.setFormFields(); this.setFormFields();
@ -224,17 +152,16 @@ export default {
this.$emit('change', df, value, oldValue); this.$emit('change', df, value, oldValue);
} }
if (this.autosave && this.doc.dirty) { if (df.fieldtype === 'Table' || !this.doc.dirty || !this.autosave) {
if (df.fieldtype === 'Table') { return;
return;
}
await this.doc.sync();
} }
await this.doc.sync();
}, },
async sync() { async sync() {
try { try {
await this.doc.sync(); await this.doc.sync();
this.setFormFields();
} catch (err) { } catch (err) {
await handleErrorWithDialog(err, this.doc); await handleErrorWithDialog(err, this.doc);
} }
@ -242,57 +169,11 @@ export default {
async submit() { async submit() {
try { try {
await this.doc.submit(); await this.doc.submit();
this.setFormFields();
} catch (err) { } catch (err) {
await handleErrorWithDialog(err, this.doc); await handleErrorWithDialog(err, this.doc);
} }
}, },
async activateInlineEditing(df) {
if (!df.inline) {
return;
}
this.inlineEditField = df;
if (!this.doc[df.fieldname]) {
this.inlineEditDoc = await fyo.doc.getNewDoc(df.target);
} else {
this.inlineEditDoc = this.doc.getLink(df.fieldname);
}
},
getInlineEditFields(df) {
const inlineEditFieldNames =
fyo.schemaMap[df.target].quickEditFields ?? [];
return inlineEditFieldNames.map((fieldname) =>
fyo.getField(df.target, fieldname)
);
},
async saveInlineEditDoc(df) {
if (!this.inlineEditDoc) {
return;
}
try {
await this.inlineEditDoc.sync();
} catch (error) {
return await handleErrorWithDialog(error, this.inlineEditDoc);
}
await this.onChangeCommon(df, this.inlineEditDoc.name);
await this.doc.loadLinks();
if (this.emitChange) {
this.$emit('change', this.inlineEditField);
}
await this.stopInlineEditing();
},
async stopInlineEditing() {
if (this.inlineEditDoc?.dirty && !this.inlineEditDoc?.notInserted) {
await this.inlineEditDoc.load();
}
this.inlineEditDoc = null;
this.inlineEditField = null;
},
setFormFields() { setFormFields() {
let fieldList = this.fields; let fieldList = this.fields;

View File

@ -66,15 +66,19 @@ export default defineComponent({
methods: { methods: {
focusOnNameField() { focusOnNameField() {
const naming = this.fyo.schemaMap[this.doc.schemaName]?.naming; const naming = this.fyo.schemaMap[this.doc.schemaName]?.naming;
if (naming !== 'manual') { if (naming !== 'manual' || this.doc.inserted) {
return; return;
} }
const nameField = (this.$refs.nameField as { focus: Function }[])?.[0]; const nameField = (
this.$refs.nameField as { focus: Function; clear: Function }[]
)?.[0];
if (!nameField) { if (!nameField) {
return; return;
} }
nameField.clear();
nameField.focus(); nameField.focus();
}, },
}, },

View File

@ -2,7 +2,14 @@ import { Doc } from 'fyo/model/doc';
import { Field } from 'schemas/types'; import { Field } from 'schemas/types';
export function evaluateReadOnly(field: Field, doc?: Doc) { export function evaluateReadOnly(field: Field, doc?: Doc) {
if (field.fieldname === 'numberSeries' && !doc?.notInserted) { if (doc?.inserted && field.fieldname === 'numberSeries') {
return true;
}
if (
field.fieldname === 'name' &&
(doc?.inserted || doc?.schema.naming !== 'manual')
) {
return true; return true;
} }