2
0
mirror of https://github.com/frappe/books.git synced 2024-12-23 11:29:03 +00:00

Merge pull request #466 from 18alantom/fix-fields-ui

fix: fields UI/UX
This commit is contained in:
Alan 2022-09-19 04:25:10 -07:00 committed by GitHub
commit 9592ec4bc7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 281 additions and 232 deletions

View File

@ -1,15 +1,11 @@
<template> <template>
<div <div
class=" class="relative bg-white border rounded-full flex-center overflow-hidden"
relative :class="{
bg-white 'w-20 h-20': size !== 'small',
border 'w-12 h-12': size === 'small',
rounded-full 'cursor-pointer': !isReadOnly,
flex-center }"
overflow-hidden
cursor-pointer
"
:class="{ 'w-20 h-20': size !== 'small', 'w-12 h-12': size === 'small' }"
@mouseover="showEdit = true" @mouseover="showEdit = true"
@mouseleave="showEdit = false" @mouseleave="showEdit = false"
@click="openFileSelector" @click="openFileSelector"
@ -48,9 +44,16 @@
</div> </div>
<div <div
v-show="showEdit" v-show="showEdit"
class="absolute bottom-0 text-gray-500 text-center text-xs pt-3 pb-1" class="
absolute
bottom-0
text-gray-500 text-center text-xs
pt-3
pb-1
select-none
"
> >
{{ t`Edit` }} {{ !isReadOnly ? t`Edit` : '' }}
</div> </div>
</div> </div>
</template> </template>
@ -72,6 +75,10 @@ export default {
}, },
methods: { methods: {
async openFileSelector() { async openFileSelector() {
if (this.isReadOnly) {
return;
}
const options = { const options = {
title: fyo.t`Select Image`, title: fyo.t`Select Image`,
properties: ['openFile'], properties: ['openFile'],

View File

@ -8,17 +8,18 @@
selectHighlightedItem, selectHighlightedItem,
}" }"
> >
<div class="text-gray-600 text-sm mb-1" v-if="showLabel"> <div :class="labelClasses" v-if="showLabel">
{{ df.label }} {{ df.label }}
</div> </div>
<div <div
class="flex items-center justify-between pr-2 rounded" class="flex items-center justify-between pr-2 rounded"
:class="isReadOnly ? '' : 'focus-within:bg-gray-200'" :class="containerClasses"
> >
<input <input
ref="input" ref="input"
spellcheck="false" spellcheck="false"
:class="inputClasses" :class="inputClasses"
class="bg-transparent"
type="text" type="text"
:value="linkValue" :value="linkValue"
:placeholder="inputPlaceholder" :placeholder="inputPlaceholder"
@ -38,10 +39,12 @@
style="background: inherit; margin-right: -3px" style="background: inherit; margin-right: -3px"
viewBox="0 0 5 10" viewBox="0 0 5 10"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@click="(e) => !isReadOnly && onFocus(e, toggleDropdown)"
> >
<path <path
d="M1 2.636L2.636 1l1.637 1.636M1 7.364L2.636 9l1.637-1.636" d="M1 2.636L2.636 1l1.637 1.636M1 7.364L2.636 9l1.637-1.636"
stroke="#404040" class="stroke-current"
:class="showMandatory ? 'text-red-500' : 'text-gray-500'"
fill="none" fill="none"
fill-rule="evenodd" fill-rule="evenodd"
stroke-linecap="round" stroke-linecap="round"

View File

@ -1,27 +1,32 @@
<template> <template>
<div> <div>
<div class="text-gray-600 text-sm mb-1" v-if="showLabel"> <div :class="labelClasses" v-if="showLabel">
{{ df.label }} {{ df.label }}
</div> </div>
<input <div :class="showMandatory ? 'show-mandatory' : ''">
spellcheck="false" <input
ref="input" spellcheck="false"
:class="inputClasses" ref="input"
:type="inputType" class="bg-transparent"
:value="value" :class="[inputClasses, containerClasses]"
:placeholder="inputPlaceholder" :type="inputType"
:readonly="isReadOnly" :value="value"
:max="df.maxvalue" :placeholder="inputPlaceholder"
:min="df.minvalue" :readonly="isReadOnly"
@blur="(e) => !isReadOnly && triggerChange(e.target.value)" :max="df.maxvalue"
@focus="(e) => !isReadOnly && $emit('focus', e)" :min="df.minvalue"
@input="(e) => !isReadOnly && $emit('input', e)" @blur="(e) => !isReadOnly && triggerChange(e.target.value)"
/> @focus="(e) => !isReadOnly && $emit('focus', e)"
@input="(e) => !isReadOnly && $emit('input', e)"
/>
</div>
</div> </div>
</template> </template>
<script> <script>
import { isNumeric } from 'src/utils'; import { isNumeric } from 'src/utils';
import { evaluateReadOnly, evaluateRequired } from 'src/utils/doc';
import { getIsNullOrUndef } from 'utils/index';
export default { export default {
name: 'Base', name: 'Base',
@ -29,11 +34,13 @@ export default {
df: Object, df: Object,
value: [String, Number, Boolean, Object], value: [String, Number, Boolean, Object],
inputClass: [Function, String, Object], inputClass: [Function, String, Object],
border: { type: Boolean, default: false },
placeholder: String, placeholder: String,
size: String, size: String,
showLabel: Boolean, showLabel: Boolean,
readOnly: Boolean,
autofocus: Boolean, autofocus: Boolean,
readOnly: { type: [null, Boolean], default: null },
required: { type: [null, Boolean], default: null },
}, },
emits: ['focus', 'input', 'change'], emits: ['focus', 'input', 'change'],
inject: { inject: {
@ -56,28 +63,89 @@ export default {
inputType() { inputType() {
return 'text'; return 'text';
}, },
labelClasses() {
return 'text-gray-600 text-sm mb-1';
},
inputClasses() { inputClasses() {
let classes = [ /**
{ * These classes will be used by components that extend Base
'px-3 py-2': this.size !== 'small', */
'px-2 py-1': this.size === 'small',
}, const classes = [
'focus:outline-none rounded w-full placeholder-gray-500', 'text-base',
this.isReadOnly 'focus:outline-none',
? 'text-gray-800 focus:bg-transparent' 'w-full',
: 'text-gray-900 focus:bg-gray-200', 'placeholder-gray-500',
]; ];
if (isNumeric(this.df)) {
classes.push('text-right');
}
if (this.size === 'small') {
classes.push('px-2 py-1');
} else {
classes.push('px-3 py-2');
}
if (this.isReadOnly) {
classes.push('text-gray-800 cursor-default');
} else {
classes.push('text-gray-900');
}
return this.getInputClassesFromProp(classes); return this.getInputClassesFromProp(classes);
}, },
containerClasses() {
/**
* Used to accomodate extending compoents where the input is contained in
* a div eg AutoComplete
*/
const classes = ['rounded'];
if (!this.isReadOnly) {
classes.push('focus-within:bg-gray-100');
}
if (this.border) {
classes.push('bg-gray-50 border border-gray-200');
}
return classes;
},
inputPlaceholder() { inputPlaceholder() {
return this.placeholder || this.df.placeholder || this.df.label; return this.placeholder || this.df.placeholder || this.df.label;
}, },
showMandatory() {
return this.isEmpty && this.isRequired;
},
isEmpty() {
if (Array.isArray(this.value) && !this.value.length) {
return true;
}
if (typeof this.value === 'string' && !this.value) {
return true;
}
if (getIsNullOrUndef(this.value)) {
return true;
}
return false;
},
isReadOnly() { isReadOnly() {
if (this.readOnly != null) { if (typeof this.readOnly === 'boolean') {
return this.readOnly; return this.readOnly;
} }
return this.df.readOnly;
return evaluateReadOnly(this.df, this.doc);
},
isRequired() {
if (typeof this.required === 'boolean') {
return this.required;
}
return evaluateRequired(this.df, this.doc);
}, },
}, },
methods: { methods: {
@ -112,9 +180,3 @@ export default {
}, },
}; };
</script> </script>
<style>
input[type='number']::-webkit-inner-spin-button {
appearance: none;
}
</style>

View File

@ -1,10 +1,13 @@
<template> <template>
<div> <div :class="[inputClasses, containerClasses]">
<label class="flex items-center"> <label class="flex items-center">
<div class="mr-3 text-gray-600 text-sm" v-if="showLabel && !labelRight"> <div class="mr-3 text-gray-600 text-sm" v-if="showLabel && !labelRight">
{{ df.label }} {{ df.label }}
</div> </div>
<div style="width: 14px; height: 14px; overflow: hidden; cursor: pointer"> <div
style="width: 14px; height: 14px; overflow: hidden"
:class="isReadOnly ? 'cursor-default' : 'cursor-pointer'"
>
<svg <svg
v-if="checked" v-if="checked"
width="14" width="14"
@ -54,7 +57,6 @@
<input <input
ref="input" ref="input"
type="checkbox" type="checkbox"
:class="inputClasses"
:checked="value" :checked="value"
:readonly="isReadOnly" :readonly="isReadOnly"
@change="(e) => !isReadOnly && triggerChange(e.target.checked)" @change="(e) => !isReadOnly && triggerChange(e.target.checked)"
@ -88,9 +90,11 @@ export default {
}; };
}, },
computed: { computed: {
/*
inputClasses() { inputClasses() {
return this.getInputClassesFromProp([]); return this.getInputClassesFromProp([]);
}, },
*/
checked() { checked() {
return this.value; return this.value;
}, },

View File

@ -1,13 +1,13 @@
<template> <template>
<div> <div>
<div class="text-gray-600 text-sm mb-1" v-if="showLabel"> <div :class="labelClasses" v-if="showLabel">
{{ df.label }} {{ df.label }}
</div> </div>
<Popover placement="bottom-end"> <Popover placement="bottom-end">
<template #target="{ togglePopover }"> <template #target="{ togglePopover }">
<div <div
tabindex="0" tabindex="0"
:class="inputClasses" :class="[inputClasses, containerClasses]"
@click="!isReadOnly && togglePopover()" @click="!isReadOnly && togglePopover()"
> >
<div class="flex items-center"> <div class="flex items-center">
@ -26,7 +26,7 @@
</div> </div>
</template> </template>
<template #content> <template #content>
<div class="text-sm py-3 px-2 text-center"> <div class="text-sm p-2 text-center">
<div> <div>
<Row class="border-none" :column-count="5" gap="0.5rem"> <Row class="border-none" :column-count="5" gap="0.5rem">
<div <div
@ -42,10 +42,9 @@
<input <input
type="text" type="text"
:placeholder="t`Custom Hex`" :placeholder="t`Custom Hex`"
:class="inputClasses" :class="[inputClasses, containerClasses]"
:value="value" :value="value"
@change="(e) => setColorValue(e.target.value)" @change="(e) => setColorValue(e.target.value)"
class="bg-gray-100"
/> />
</div> </div>
</div> </div>

View File

@ -1,12 +1,13 @@
<template> <template>
<div> <div>
<div class="text-gray-600 text-sm mb-1" v-if="showLabel"> <div :class="labelClasses" v-if="showLabel">
{{ df.label }} {{ df.label }}
</div> </div>
<input <input
v-show="showInput" v-show="showInput"
ref="input" ref="input"
:class="inputClasses" class="text-right"
:class="[inputClasses, containerClasses]"
:type="inputType" :type="inputType"
:value="value?.round()" :value="value?.round()"
:placeholder="inputPlaceholder" :placeholder="inputPlaceholder"
@ -17,7 +18,8 @@
/> />
<div <div
v-show="!showInput" v-show="!showInput"
:class="[inputClasses, 'cursor-text whitespace-nowrap overflow-x-auto']" class="whitespace-nowrap overflow-x-auto"
:class="[inputClasses, containerClasses, ,]"
@click="activateInput" @click="activateInput"
@focus="activateInput" @focus="activateInput"
tabindex="0" tabindex="0"
@ -61,6 +63,10 @@ export default {
this.triggerChange(value); this.triggerChange(value);
}, },
activateInput() { activateInput() {
if (this.isReadOnly) {
return;
}
this.showInput = true; this.showInput = true;
nextTick(() => { nextTick(() => {
this.focus(); this.focus();

View File

@ -1,12 +1,13 @@
<template> <template>
<div> <div>
<div class="text-gray-600 text-sm mb-1" v-if="showLabel"> <div :class="labelClasses" v-if="showLabel">
{{ df.label }} {{ df.label }}
</div> </div>
<DatePicker <DatePicker
ref="input" ref="input"
:input-class="[inputClasses, 'cursor-text']" :show-mandatory="showMandatory"
:input-class="['bg-transparent', inputClasses, containerClasses]"
:value="value" :value="value"
:placeholder="inputPlaceholder" :placeholder="inputPlaceholder"
:readonly="isReadOnly" :readonly="isReadOnly"

View File

@ -3,7 +3,8 @@
:df="languageDf" :df="languageDf"
:value="value" :value="value"
@change="onChange" @change="onChange"
:input-class="'focus:outline-none rounded ' + inputClass" :border="true"
:input-class="'rounded py-1.5'"
/> />
</template> </template>
<script> <script>
@ -17,11 +18,6 @@ export default {
setLanguageMap, setLanguageMap,
}, },
props: { props: {
inputClass: {
type: String,
default:
'px-3 py-2 text-base',
},
dontReload: { dontReload: {
type: Boolean, type: Boolean,
default: false, default: false,

View File

@ -1,11 +1,11 @@
<template> <template>
<div> <div>
<div class="text-gray-600 text-sm mb-1" v-if="showLabel"> <div :class="labelClasses" v-if="showLabel">
{{ df.label }} {{ df.label }}
</div> </div>
<div <div
class="flex items-center justify-between focus-within:bg-gray-200" class="flex items-center justify-between"
:class="inputClasses" :class="[inputClasses, containerClasses]"
> >
<select <select
class=" class="
@ -17,7 +17,7 @@
" "
:class="{ :class="{
'pointer-events-none': isReadOnly, 'pointer-events-none': isReadOnly,
'text-gray-400': !value, 'text-gray-500': !value,
}" }"
:value="value" :value="value"
@change="(e) => triggerChange(e.target.value)" @change="(e) => triggerChange(e.target.value)"
@ -44,7 +44,8 @@
> >
<path <path
d="M1 2.636L2.636 1l1.637 1.636M1 7.364L2.636 9l1.637-1.636" d="M1 2.636L2.636 1l1.637 1.636M1 7.364L2.636 9l1.637-1.636"
stroke="#404040" class="stroke-current"
:class="showMandatory ? 'text-red-500' : 'text-gray-500'"
fill="none" fill="none"
fill-rule="evenodd" fill-rule="evenodd"
stroke-linecap="round" stroke-linecap="round"

View File

@ -5,7 +5,7 @@
w-full w-full
px-2 px-2
border-b border-b
hover:bg-gray-50 hover:bg-gray-25
group group
flex flex
items-center items-center
@ -32,8 +32,6 @@
<FormControl <FormControl
v-for="df in tableFields" v-for="df in tableFields"
:size="size" :size="size"
:read-only="readOnly"
:input-class="{ 'text-right': isNumeric(df), 'bg-transparent': true }"
:key="df.fieldname" :key="df.fieldname"
:df="df" :df="df"
:value="row[df.fieldname]" :value="row[df.fieldname]"

View File

@ -1,20 +1,22 @@
<template> <template>
<div> <div>
<div class="text-gray-600 text-sm mb-1" v-if="showLabel"> <div :class="labelClasses" v-if="showLabel">
{{ df.label }} {{ df.label }}
</div> </div>
<textarea <div :class="showMandatory ? 'show-mandatory' : ''">
ref="input" <textarea
rows="3" ref="input"
:class="['resize-none', inputClasses]" rows="3"
:value="value" :class="['resize-none', inputClasses, containerClasses]"
:placeholder="inputPlaceholder" :value="value"
style="vertical-align: top" :placeholder="inputPlaceholder"
:readonly="isReadOnly" style="vertical-align: top"
@blur="(e) => triggerChange(e.target.value)" :readonly="isReadOnly"
@focus="(e) => $emit('focus', e)" @blur="(e) => triggerChange(e.target.value)"
@input="(e) => $emit('input', e)" @focus="(e) => $emit('focus', e)"
></textarea> @input="(e) => $emit('input', e)"
></textarea>
</div>
</div> </div>
</template> </template>

View File

@ -1,15 +1,17 @@
<template> <template>
<Popover @open="selectCurrentMonthYear"> <Popover @open="selectCurrentMonthYear">
<template #target="{ togglePopover, handleBlur }"> <template #target="{ togglePopover, handleBlur }">
<input <div :class="showMandatory ? 'show-mandatory' : ''">
type="text" <input
:class="inputClass" type="text"
:value="value && formatValue ? formatValue(value) : value || ''" :class="inputClass"
:placeholder="placeholder" :value="value && formatValue ? formatValue(value) : value || ''"
readonly :placeholder="placeholder"
@focus="!readonly ? togglePopover() : null" readonly
@blur="handleBlur" @focus="!readonly ? togglePopover() : null"
/> @blur="handleBlur"
/>
</div>
</template> </template>
<template #content="{ togglePopover }"> <template #content="{ togglePopover }">
<div class="text-left p-3 select-none"> <div class="text-left p-3 select-none">
@ -63,7 +65,11 @@
{{ d }} {{ d }}
</div> </div>
</div> </div>
<div v-for="(week, i) in datesAsWeeks" :key="`${i}-${Math.random().toString(36)}`" class="mt-1"> <div
v-for="(week, i) in datesAsWeeks"
:key="`${i}-${Math.random().toString(36)}`"
class="mt-1"
>
<div class="flex w-full"> <div class="flex w-full">
<div <div
v-for="date in week" v-for="date in week"
@ -123,7 +129,14 @@ import Popover from '../Popover';
export default { export default {
name: 'DatePicker', name: 'DatePicker',
props: ['value', 'placeholder', 'readonly', 'formatValue', 'inputClass'], props: [
'value',
'placeholder',
'readonly',
'formatValue',
'inputClass',
'showMandatory',
],
emits: ['change'], emits: ['change'],
components: { components: {
Popover, Popover,
@ -256,7 +269,7 @@ export default {
return ''; return '';
} }
return DateTime.fromJSDate(date).toISODate() return DateTime.fromJSDate(date).toISODate();
}, },
getDate(...args) { getDate(...args) {

View File

@ -56,9 +56,9 @@
</span> </span>
</div> </div>
<FormControl <FormControl
:border="true"
size="small" size="small"
class="w-24" class="w-24"
input-class="bg-gray-100"
:df="{ :df="{
placeholder: t`Field`, placeholder: t`Field`,
fieldname: 'fieldname', fieldname: 'fieldname',
@ -69,9 +69,9 @@
@change="(value) => (filter.fieldname = value)" @change="(value) => (filter.fieldname = value)"
/> />
<FormControl <FormControl
:border="true"
size="small" size="small"
class="w-24" class="w-24"
input-class="bg-gray-100"
:df="{ :df="{
placeholder: t`Condition`, placeholder: t`Condition`,
fieldname: 'condition', fieldname: 'condition',
@ -82,9 +82,9 @@
@change="(value) => (filter.condition = value)" @change="(value) => (filter.condition = value)"
/> />
<FormControl <FormControl
:border="true"
size="small" size="small"
class="w-24" class="w-24"
input-class="bg-gray-100"
:df="{ :df="{
placeholder: t`Value`, placeholder: t`Value`,
fieldname: 'value', fieldname: 'value',
@ -124,9 +124,9 @@
</template> </template>
<script> <script>
import { fyo } from 'src/initFyo';
import { t } from 'fyo'; import { t } from 'fyo';
import { FieldTypeEnum } from 'schemas/types'; import { FieldTypeEnum } from 'schemas/types';
import { fyo } from 'src/initFyo';
import { getRandomString } from 'utils'; import { getRandomString } from 'utils';
import Button from './Button'; import Button from './Button';
import FormControl from './Controls/FormControl.vue'; import FormControl from './Controls/FormControl.vue';

View File

@ -9,8 +9,8 @@
size="small" size="small"
:df="df" :df="df"
:value="doc[df.fieldname]" :value="doc[df.fieldname]"
:read-only="evaluateReadOnly(df)"
@change="async (value) => await onChange(df, value)" @change="async (value) => await onChange(df, value)"
:read-only="readOnly"
/> />
<!-- Inline Field Form (Eg: Address) --> <!-- Inline Field Form (Eg: Address) -->
@ -29,6 +29,7 @@
:no-border="true" :no-border="true"
:focus-first-input="true" :focus-first-input="true"
:autosave="false" :autosave="false"
:read-only="readOnly"
@error="(msg) => $emit('error', msg)" @error="(msg) => $emit('error', msg)"
/> />
<div <div
@ -79,8 +80,7 @@
:df="df" :df="df"
:value="getRegularValue(df)" :value="getRegularValue(df)"
:class="{ 'p-2': df.fieldtype === 'Check' }" :class="{ 'p-2': df.fieldtype === 'Check' }"
:read-only="evaluateReadOnly(df)" :read-only="readOnly"
input-class="bg-transparent"
@change="async (value) => await onChange(df, value)" @change="async (value) => await onChange(df, value)"
@focus="activateInlineEditing(df)" @focus="activateInlineEditing(df)"
@new-doc="async (newdoc) => await onChange(df, newdoc.name)" @new-doc="async (newdoc) => await onChange(df, newdoc.name)"
@ -103,7 +103,7 @@ 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, evaluateReadOnly } from 'src/utils/doc'; import { evaluateHidden } from 'src/utils/doc';
export default { export default {
name: 'TwoColumnForm', name: 'TwoColumnForm',
@ -122,6 +122,7 @@ export default {
}, },
noBorder: Boolean, noBorder: Boolean,
focusFirstInput: Boolean, focusFirstInput: Boolean,
readOnly: { type: [null, Boolean], default: null },
}, },
data() { data() {
return { return {
@ -183,9 +184,6 @@ export default {
this.inlineEditField?.fieldname === df?.fieldname && this.inlineEditDoc this.inlineEditField?.fieldname === df?.fieldname && this.inlineEditDoc
); );
}, },
evaluateReadOnly(df) {
return evaluateReadOnly(df, this.doc);
},
async onChange(df, value) { async onChange(df, value) {
if (df.inline) { if (df.inline) {
return; return;

View File

@ -137,17 +137,13 @@
" "
style="top: 100%; transform: translateY(-100%)" style="top: 100%; transform: translateY(-100%)"
> >
<LanguageSelector <LanguageSelector v-show="!creatingDemo" class="text-sm w-28" />
v-show="!creatingDemo"
class="text-sm w-28 bg-gray-100 rounded-md"
input-class="py-1.5 bg-transparent"
/>
<button <button
class=" class="
text-sm text-sm
bg-gray-100 bg-gray-100
hover:bg-gray-200 hover:bg-gray-200
rounded-md rounded
px-4 px-4
py-1.5 py-1.5
w-28 w-28

View File

@ -63,8 +63,8 @@
<!-- Invoice Form Data Entry --> <!-- Invoice Form Data Entry -->
<div class="m-4 grid grid-cols-3 gap-4"> <div class="m-4 grid grid-cols-3 gap-4">
<FormControl <FormControl
class="bg-gray-100 rounded text-base" input-class="font-semibold"
input-class="text-lg font-semibold bg-transparent" :border="true"
:df="getField('party')" :df="getField('party')"
:value="doc.party" :value="doc.party"
@change="(value) => doc.set('party', value)" @change="(value) => doc.set('party', value)"
@ -72,67 +72,28 @@
:read-only="doc?.submitted" :read-only="doc?.submitted"
/> />
<FormControl <FormControl
input-class="bg-gray-100 px-3 py-2 text-base text-right" input-class="text-right"
:border="true"
:df="getField('date')" :df="getField('date')"
:value="doc.date" :value="doc.date"
@change="(value) => doc.set('date', value)" @change="(value) => doc.set('date', value)"
:read-only="doc?.submitted" :read-only="doc?.submitted"
/> />
<FormControl <FormControl
class="text-base bg-gray-100 rounded" input-class="text-right"
input-class="bg-transparent px-3 py-2 text-base text-right" :border="true"
:df="getField('numberSeries')" :df="getField('numberSeries')"
:value="doc.numberSeries" :value="doc.numberSeries"
@change="(value) => doc.set('numberSeries', value)" @change="(value) => doc.set('numberSeries', value)"
:read-only="!doc.notInserted || doc?.submitted" :read-only="!doc.notInserted || doc?.submitted"
/> />
<FormControl <FormControl
class="text-base bg-gray-100 rounded" :border="true"
input-class="px-3 py-2 text-base bg-transparent"
:df="getField('account')" :df="getField('account')"
:value="doc.account" :value="doc.account"
@change="(value) => doc.set('account', value)" @change="(value) => doc.set('account', value)"
:read-only="doc?.submitted" :read-only="doc?.submitted"
/> />
<!--
<FormControl
v-if="doc.enableDiscounting"
:show-label="true"
:label-right="false"
class="
text-base
bg-gray-100
rounded
flex
items-center
justify-center
w-ful
"
input-class="px-3 py-2 text-base bg-transparent text-right"
:df="getField('setDiscountAmount')"
:value="doc.setDiscountAmount"
@change="(value) => doc.set('setDiscountAmount', value)"
:read-only="doc?.submitted"
/>
<FormControl
v-if="doc.enableDiscounting && !doc.setDiscountAmount"
class="text-base bg-gray-100 rounded"
input-class="px-3 py-2 text-base bg-transparent text-right"
:df="getField('discountPercent')"
:value="doc.discountPercent"
@change="(value) => doc.set('discountPercent', value)"
:read-only="doc?.submitted"
/>
<FormControl
v-if="doc.enableDiscounting && doc.setDiscountAmount"
class="text-base bg-gray-100 rounded"
input-class="px-3 py-2 text-base bg-transparent text-right"
:df="getField('discountAmount')"
:value="doc.discountAmount"
@change="(value) => doc.set('discountAmount', value)"
:read-only="doc?.submitted"
/>
-->
</div> </div>
<hr /> <hr />
@ -161,10 +122,10 @@
</p> </p>
<!-- Form Terms--> <!-- Form Terms-->
<FormControl <FormControl
:border="true"
v-if="!doc?.submitted || doc.terms" v-if="!doc?.submitted || doc.terms"
:df="getField('terms')" :df="getField('terms')"
:value="doc.terms" :value="doc.terms"
input-class="bg-gray-100"
class="mt-auto" class="mt-auto"
@change="(value) => doc.set('terms', value)" @change="(value) => doc.set('terms', value)"
:read-only="doc?.submitted" :read-only="doc?.submitted"

View File

@ -47,43 +47,42 @@
<div class="m-4 grid grid-cols-3 gap-y-4 gap-x-4"> <div class="m-4 grid grid-cols-3 gap-y-4 gap-x-4">
<!-- First Column of Fields --> <!-- First Column of Fields -->
<FormControl <FormControl
:border="true"
:df="getField('numberSeries')" :df="getField('numberSeries')"
:value="doc.numberSeries" :value="doc.numberSeries"
@change="(value) => doc.set('numberSeries', value)" @change="(value) => doc.set('numberSeries', value)"
class="bg-gray-100 rounded"
input-class="p-2 text-base bg-transparent"
:read-only="!doc.notInserted || doc.submitted" :read-only="!doc.notInserted || doc.submitted"
/> />
<FormControl <FormControl
:border="true"
:df="getField('date')" :df="getField('date')"
:value="doc.date" :value="doc.date"
:placeholder="'Date'" :placeholder="'Date'"
@change="(value) => doc.set('date', value)" @change="(value) => doc.set('date', value)"
input-class="bg-gray-100 px-3 py-2 text-base"
:read-only="doc.submitted" :read-only="doc.submitted"
/> />
<FormControl <FormControl
:border="true"
:df="getField('entryType')" :df="getField('entryType')"
:value="doc.entryType" :value="doc.entryType"
placeholder="Entry Type" placeholder="Entry Type"
@change="(value) => doc.set('entryType', value)" @change="(value) => doc.set('entryType', value)"
input-class="bg-gray-100 px-3 py-2 text-base"
:read-only="doc.submitted" :read-only="doc.submitted"
/> />
<FormControl <FormControl
:border="true"
:df="getField('referenceNumber')" :df="getField('referenceNumber')"
:value="doc.referenceNumber" :value="doc.referenceNumber"
:placeholder="'Reference Number'" :placeholder="'Reference Number'"
@change="(value) => doc.set('referenceNumber', value)" @change="(value) => doc.set('referenceNumber', value)"
input-class="bg-gray-100 p-2 text-base"
:read-only="doc.submitted" :read-only="doc.submitted"
/> />
<FormControl <FormControl
:border="true"
:df="getField('referenceDate')" :df="getField('referenceDate')"
:value="doc.referenceDate" :value="doc.referenceDate"
:placeholder="'Reference Date'" :placeholder="'Reference Date'"
@change="(value) => doc.set('referenceDate', value)" @change="(value) => doc.set('referenceDate', value)"
input-class="bg-gray-100 px-3 py-2 text-base"
:read-only="doc.submitted" :read-only="doc.submitted"
/> />
</div> </div>
@ -106,9 +105,9 @@
<div class="flex justify-between text-base m-4 gap-12"> <div class="flex justify-between text-base m-4 gap-12">
<!-- User Remark --> <!-- User Remark -->
<FormControl <FormControl
:border="true"
v-if="!doc.submitted || doc.userRemark" v-if="!doc.submitted || doc.userRemark"
class="w-1/2 self-end" class="w-1/2 self-end"
input-class="bg-gray-100"
:df="getField('userRemark')" :df="getField('userRemark')"
:value="doc.userRemark" :value="doc.userRemark"
@change="(value) => doc.set('userRemark', value)" @change="(value) => doc.set('userRemark', value)"

View File

@ -14,15 +14,13 @@
</PageHeader> </PageHeader>
<!-- Filters --> <!-- Filters -->
<div v-if="report" class="grid grid-cols-5 gap-2 p-4 border-b"> <div v-if="report" class="grid grid-cols-5 gap-4 p-4 border-b">
<FormControl <FormControl
v-for="field in report.filters" v-for="field in report.filters"
:border="true"
size="small" size="small"
:show-label="field.fieldtype === 'Check'" :show-label="field.fieldtype === 'Check'"
:key="field.fieldname + '-filter'" :key="field.fieldname + '-filter'"
class="bg-gray-100 rounded"
:class="field.fieldtype === 'Check' ? 'flex pl-2 py-1' : ''"
input-class="bg-transparent text-sm"
:df="field" :df="field"
:value="report.get(field.fieldname)" :value="report.get(field.fieldname)"
:read-only="loading" :read-only="loading"

View File

@ -9,10 +9,7 @@
@change="forwardChangeEvent" @change="forwardChangeEvent"
/> />
<div class="flex p-4 justify-between"> <div class="flex p-4 justify-between">
<LanguageSelector <LanguageSelector class="text-sm w-28" />
class="text-sm w-28 bg-gray-100 rounded-md"
input-class="py-1.5 bg-transparent"
/>
<p class="mt-auto text-gray-600 text-base select-none"> <p class="mt-auto text-gray-600 text-base select-none">
{{ `v${fyo.store.appVersion}` }} {{ `v${fyo.store.appVersion}` }}
</p> </p>

View File

@ -6,8 +6,9 @@
<!-- Setup Wizard Slide --> <!-- Setup Wizard Slide -->
<Slide <Slide
:primary-disabled="!valuesFilled || loading" :primary-disabled="!valuesFilled || loading"
@primary-clicked="handlePrimary" :secondary-disabled="loading"
@secondary-clicked="handleSecondary" @primary-clicked="submit()"
@secondary-clicked="$emit('setup-canceled')"
:class="{ 'window-no-drag': platform !== 'Windows' }" :class="{ 'window-no-drag': platform !== 'Windows' }"
> >
<template #title> <template #title>
@ -21,6 +22,7 @@
<FormControl <FormControl
:df="getField('logo')" :df="getField('logo')"
:value="doc.logo" :value="doc.logo"
:read-only="loading"
@change="(value) => setValue('logo', value)" @change="(value) => setValue('logo', value)"
/> />
<div> <div>
@ -28,9 +30,9 @@
ref="companyField" ref="companyField"
:df="getField('companyName')" :df="getField('companyName')"
:value="doc.companyName" :value="doc.companyName"
:read-only="loading"
@change="(value) => setValue('companyName', value)" @change="(value) => setValue('companyName', value)"
input-class=" input-class="
bg-transparent
font-semibold font-semibold
text-xl text-xl
" "
@ -39,11 +41,8 @@
<FormControl <FormControl
:df="getField('email')" :df="getField('email')"
:value="doc.email" :value="doc.email"
:read-only="loading"
@change="(value) => setValue('email', value)" @change="(value) => setValue('email', value)"
input-class="
text-base
bg-transparent
"
/> />
</div> </div>
</div> </div>
@ -56,11 +55,13 @@
{{ emailError }} {{ emailError }}
</p> </p>
<TwoColumnForm :doc="doc" /> <TwoColumnForm :doc="doc" :read-only="loading" />
</div> </div>
</template> </template>
<template #secondaryButton>{{ t`Cancel` }}</template> <template #secondaryButton>{{ t`Cancel` }}</template>
<template #primaryButton>{{ t`Submit` }}</template> <template #primaryButton>{{
loading ? t`Setting up...` : t`Submit`
}}</template>
</Slide> </Slide>
</div> </div>
</template> </template>
@ -69,7 +70,6 @@
import FormControl from 'src/components/Controls/FormControl.vue'; import FormControl from 'src/components/Controls/FormControl.vue';
import TwoColumnForm from 'src/components/TwoColumnForm.vue'; import TwoColumnForm from 'src/components/TwoColumnForm.vue';
import { getErrorMessage } from 'src/utils'; import { getErrorMessage } from 'src/utils';
import { openLink } from 'src/utils/ipcCalls';
import { getSetupWizardDoc } from 'src/utils/misc'; import { getSetupWizardDoc } from 'src/utils/misc';
import { showMessageDialog } from 'src/utils/ui'; import { showMessageDialog } from 'src/utils/ui';
import Slide from './Slide.vue'; import Slide from './Slide.vue';
@ -106,17 +106,6 @@ export default {
getField(fieldname) { getField(fieldname) {
return this.doc.schema?.fields.find((f) => f.fieldname === fieldname); return this.doc.schema?.fields.find((f) => f.fieldname === fieldname);
}, },
openContributingTranslations() {
openLink(
'https://github.com/frappe/books/wiki/Contributing-Translations'
);
},
handlePrimary() {
this.submit();
},
handleSecondary() {
this.$emit('setup-canceled');
},
setValue(fieldname, value) { setValue(fieldname, value) {
this.emailError = null; this.emailError = null;
this.doc.set(fieldname, value).catch((e) => { this.doc.set(fieldname, value).catch((e) => {
@ -142,14 +131,5 @@ export default {
this.$emit('setup-complete', this.doc.getValidDict()); this.$emit('setup-complete', this.doc.getValidDict());
}, },
}, },
computed: {
buttonText() {
if (this.loading) {
return this.t`Submit`;
}
return this.t`Setting Up...`;
},
},
}; };
</script> </script>

View File

@ -1,5 +1,8 @@
<template> <template>
<div class="w-form shadow-lg rounded-lg border relative bg-white" style="height: 700px"> <div
class="w-form shadow-lg rounded-lg border relative bg-white"
style="height: 700px"
>
<!-- Slide Title --> <!-- Slide Title -->
<div class="p-4"> <div class="p-4">
<h1 class="text-2xl font-semibold select-none"> <h1 class="text-2xl font-semibold select-none">
@ -19,15 +22,16 @@
style="top: 100%; transform: translateY(-100%)" style="top: 100%; transform: translateY(-100%)"
> >
<Button <Button
class="text-sm text-grey-900 w-28" class="text-sm text-grey-900 min-w-28"
@click="$emit('secondary-clicked')" @click="$emit('secondary-clicked')"
:disabled="secondaryDisabled"
> >
<slot name="secondaryButton"></slot> <slot name="secondaryButton"></slot>
</Button> </Button>
<Button <Button
@click="$emit('primary-clicked')" @click="$emit('primary-clicked')"
type="primary" type="primary"
class="text-sm text-white w-28" class="text-sm text-white min-w-28"
:disabled="primaryDisabled" :disabled="primaryDisabled"
> >
<slot name="primaryButton"></slot> <slot name="primaryButton"></slot>
@ -45,6 +49,7 @@ export default {
props: { props: {
usePrimary: { type: Boolean, default: true }, usePrimary: { type: Boolean, default: true },
primaryDisabled: { type: Boolean, default: false }, primaryDisabled: { type: Boolean, default: false },
secondaryDisabled: { type: Boolean, default: false },
}, },
}; };
</script> </script>

View File

@ -23,6 +23,10 @@ html {
color: theme('colors.black'); color: theme('colors.black');
} }
input[type='number']::-webkit-inner-spin-button {
appearance: none;
}
.window-drag { .window-drag {
-webkit-app-region: drag; -webkit-app-region: drag;
} }
@ -75,7 +79,7 @@ html {
} }
.w-quick-edit { .w-quick-edit {
width: var(--w-quick-edit) width: var(--w-quick-edit);
} }
.h-form { .h-form {
@ -109,3 +113,13 @@ html {
.w-desk { .w-desk {
width: var(--w-desk); width: var(--w-desk);
} }
.show-mandatory::after {
content: '*';
display: inline-block;
width: 0px;
height: 0px;
margin-left: -0.875rem;
vertical-align: -3px;
@apply text-red-500;
}

View File

@ -1,40 +1,49 @@
import { Doc } from 'fyo/model/doc'; 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.readOnly !== undefined) { if (field.fieldname === 'numberSeries' && !doc?.notInserted) {
return field.readOnly;
}
if (field.fieldname === 'numberSeries' && !doc.notInserted) {
return true; return true;
} }
if (doc.isSubmitted || doc.parentdoc?.isSubmitted) { if (doc?.isSubmitted || doc?.parentdoc?.isSubmitted) {
return true; return true;
} }
if (doc.isCancelled || doc.parentdoc?.isCancelled) { if (doc?.isCancelled || doc?.parentdoc?.isCancelled) {
return true; return true;
} }
const readOnlyFunc = doc.readOnly[field.fieldname]; return evaluateFieldMeta(field, doc, 'readOnly');
if (readOnlyFunc !== undefined) {
return readOnlyFunc();
}
return false;
} }
export function evaluateHidden(field: Field, doc: Doc) { export function evaluateHidden(field: Field, doc?: Doc) {
if (field.hidden !== undefined) { return evaluateFieldMeta(field, doc, 'hidden');
return field.hidden; }
export function evaluateRequired(field: Field, doc?: Doc) {
return evaluateFieldMeta(field, doc, 'required');
}
function evaluateFieldMeta(
field: Field,
doc?: Doc,
meta?: 'required' | 'hidden' | 'readOnly',
defaultValue: boolean = false
) {
if (meta === undefined) {
return defaultValue;
} }
const hiddenFunction = doc.hidden[field.fieldname]; const value = field[meta];
if (value !== undefined) {
return value;
}
const hiddenFunction = doc?.[meta]?.[field.fieldname];
if (hiddenFunction !== undefined) { if (hiddenFunction !== undefined) {
return hiddenFunction(); return hiddenFunction();
} }
return false; return defaultValue;
} }