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:
parent
48e9f1b668
commit
4b65fbb50d
@ -84,5 +84,5 @@
|
|||||||
"country",
|
"country",
|
||||||
"postalCode"
|
"postalCode"
|
||||||
],
|
],
|
||||||
"inlineEditDisplayField": "addressDisplay"
|
"linkDisplayField": "addressDisplay"
|
||||||
}
|
}
|
||||||
|
@ -74,7 +74,7 @@
|
|||||||
"label": "Address",
|
"label": "Address",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"target": "Address",
|
"target": "Address",
|
||||||
"inline": true
|
"create": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "taxId",
|
"fieldname": "taxId",
|
||||||
|
@ -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"
|
||||||
},
|
},
|
||||||
|
@ -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"]
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,8 +72,7 @@
|
|||||||
"fieldname": "address",
|
"fieldname": "address",
|
||||||
"label": "Address",
|
"label": "Address",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"target": "Address",
|
"target": "Address"
|
||||||
"inline": true
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"quickEditFields": [
|
"quickEditFields": [
|
||||||
|
@ -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.
|
||||||
|
@ -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();
|
||||||
},
|
},
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user