mirror of
https://github.com/frappe/books.git
synced 2024-12-23 03:19:01 +00:00
Merge pull request #597 from frappe/ux-improvements
feat(ui/ux): status pills & quick view
This commit is contained in:
commit
a829e23bda
@ -41,6 +41,26 @@ export function isPesa(value: unknown): value is Money {
|
|||||||
return value instanceof Money;
|
return value instanceof Money;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isFalsy(value: unknown): boolean {
|
||||||
|
if (!value) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isPesa(value) && value.isZero()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(value) && value.length === 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof value === 'object' && Object.keys(value).length === 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
export function getActions(doc: Doc): Action[] {
|
export function getActions(doc: Doc): Action[] {
|
||||||
const Model = doc.fyo.models[doc.schemaName];
|
const Model = doc.fyo.models[doc.schemaName];
|
||||||
if (Model === undefined) {
|
if (Model === undefined) {
|
||||||
|
@ -54,12 +54,29 @@
|
|||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
v-if="canLink"
|
v-if="canLink"
|
||||||
class="p-0.5 rounded -me1 bg-transparent"
|
class="p-0.5 rounded -me1 bg-transparent"
|
||||||
|
@mouseenter="showQuickView = true"
|
||||||
|
@mouseleave="showQuickView = false"
|
||||||
@click="routeToLinkedDoc"
|
@click="routeToLinkedDoc"
|
||||||
>
|
>
|
||||||
<FeatherIcon name="chevron-right" class="w-4 h-4 text-gray-600" />
|
<Popover
|
||||||
|
:show-popup="showQuickView"
|
||||||
|
:entry-delay="300"
|
||||||
|
placement="bottom"
|
||||||
|
>
|
||||||
|
<template #target>
|
||||||
|
<feather-icon
|
||||||
|
name="chevron-right"
|
||||||
|
class="w-4 h-4 text-gray-600"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #content>
|
||||||
|
<QuickView :schema-name="linkSchemaName" :name="value" />
|
||||||
|
</template>
|
||||||
|
</Popover>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -71,7 +88,9 @@ import { FieldTypeEnum } from 'schemas/types';
|
|||||||
import Dropdown from 'src/components/Dropdown.vue';
|
import Dropdown from 'src/components/Dropdown.vue';
|
||||||
import { fuzzyMatch } from 'src/utils';
|
import { fuzzyMatch } from 'src/utils';
|
||||||
import { getFormRoute, routeTo } from 'src/utils/ui';
|
import { getFormRoute, routeTo } from 'src/utils/ui';
|
||||||
|
import Popover from '../Popover.vue';
|
||||||
import Base from './Base.vue';
|
import Base from './Base.vue';
|
||||||
|
import QuickView from '../QuickView.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'AutoComplete',
|
name: 'AutoComplete',
|
||||||
@ -79,9 +98,12 @@ export default {
|
|||||||
extends: Base,
|
extends: Base,
|
||||||
components: {
|
components: {
|
||||||
Dropdown,
|
Dropdown,
|
||||||
|
Popover,
|
||||||
|
QuickView,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
showQuickView: false,
|
||||||
linkValue: '',
|
linkValue: '',
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
suggestions: [],
|
suggestions: [],
|
||||||
@ -100,7 +122,23 @@ export default {
|
|||||||
const value = this.linkValue || this.value;
|
const value = this.linkValue || this.value;
|
||||||
this.setLinkValue(this.getLinkValue(value));
|
this.setLinkValue(this.getLinkValue(value));
|
||||||
},
|
},
|
||||||
|
unmounted() {
|
||||||
|
this.showQuickView = false;
|
||||||
|
},
|
||||||
|
deactivated() {
|
||||||
|
this.showQuickView = false;
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
linkSchemaName() {
|
||||||
|
let schemaName = this.df?.target;
|
||||||
|
|
||||||
|
if (!schemaName) {
|
||||||
|
const references = this.df?.references ?? '';
|
||||||
|
schemaName = this.doc?.[references];
|
||||||
|
}
|
||||||
|
|
||||||
|
return schemaName;
|
||||||
|
},
|
||||||
options() {
|
options() {
|
||||||
if (!this.df) {
|
if (!this.df) {
|
||||||
return [];
|
return [];
|
||||||
@ -139,19 +177,12 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async routeToLinkedDoc() {
|
async routeToLinkedDoc() {
|
||||||
let schemaName = this.df?.target;
|
|
||||||
const name = this.value;
|
const name = this.value;
|
||||||
|
if (!this.linkSchemaName || !name) {
|
||||||
if (!schemaName) {
|
|
||||||
const references = this.df?.references ?? '';
|
|
||||||
schemaName = this.doc?.[references];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!schemaName || !name) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const route = getFormRoute(schemaName, name);
|
const route = getFormRoute(this.linkSchemaName, name);
|
||||||
await routeTo(route);
|
await routeTo(route);
|
||||||
},
|
},
|
||||||
setLinkValue(value) {
|
setLinkValue(value) {
|
||||||
|
@ -11,8 +11,9 @@
|
|||||||
flex-shrink-0
|
flex-shrink-0
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<h1>{{ formTitle }}</h1>
|
<h1 v-if="formTitle">{{ formTitle }}</h1>
|
||||||
<p class="text-gray-600">
|
<slot />
|
||||||
|
<p v-if="formSubTitle" class="text-gray-600">
|
||||||
{{ formSubTitle }}
|
{{ formSubTitle }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -7,14 +7,25 @@
|
|||||||
:handleBlur="handleBlur"
|
:handleBlur="handleBlur"
|
||||||
></slot>
|
></slot>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<Transition>
|
||||||
ref="popover"
|
<div
|
||||||
:class="popoverClass"
|
ref="popover"
|
||||||
class="bg-white rounded border shadow-lg popover-container relative z-10"
|
:class="popoverClass"
|
||||||
v-show="isOpen"
|
class="
|
||||||
>
|
bg-white
|
||||||
<slot name="content" :togglePopover="togglePopover"></slot>
|
rounded-md
|
||||||
</div>
|
border
|
||||||
|
shadow-lg
|
||||||
|
popover-container
|
||||||
|
relative
|
||||||
|
z-10
|
||||||
|
"
|
||||||
|
:style="{ 'transition-delay': `${isOpen ? entryDelay : exitDelay}ms` }"
|
||||||
|
v-show="isOpen"
|
||||||
|
>
|
||||||
|
<slot name="content" :togglePopover="togglePopover"></slot>
|
||||||
|
</div>
|
||||||
|
</Transition>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -31,6 +42,8 @@ export default {
|
|||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
right: Boolean,
|
right: Boolean,
|
||||||
|
entryDelay: { type: Number, default: 0 },
|
||||||
|
exitDelay: { type: Number, default: 0 },
|
||||||
placement: {
|
placement: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'bottom-start',
|
default: 'bottom-start',
|
||||||
@ -120,3 +133,14 @@ export default {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
.v-enter-active,
|
||||||
|
.v-leave-active {
|
||||||
|
transition: opacity 150ms ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v-enter-from,
|
||||||
|
.v-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
75
src/components/QuickView.vue
Normal file
75
src/components/QuickView.vue
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
<template>
|
||||||
|
<div style="min-width: 192px; max-width: 300px">
|
||||||
|
<div
|
||||||
|
class="p-2 flex justify-between"
|
||||||
|
:class="values.length ? 'border-b' : ''"
|
||||||
|
>
|
||||||
|
<p
|
||||||
|
class="font-semibold text-base text-gray-900"
|
||||||
|
v-if="schema?.naming !== 'random' && !schema?.isChild"
|
||||||
|
>
|
||||||
|
{{ name }}
|
||||||
|
</p>
|
||||||
|
<p class="font-semibold text-base text-gray-600">
|
||||||
|
{{ schema?.label ?? '' }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div v-if="values.length" class="flex gap-2 p-2 flex-wrap">
|
||||||
|
<p v-for="v of values" :key="v.label" class="pill bg-gray-200">
|
||||||
|
<span class="text-gray-600">{{ v.label }}</span>
|
||||||
|
<span class="text-gray-800 ml-1.5">{{ v.value }}</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts">
|
||||||
|
import { isFalsy } from 'fyo/utils';
|
||||||
|
import { Field } from 'schemas/types';
|
||||||
|
import { defineComponent } from 'vue';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
props: {
|
||||||
|
schemaName: { type: String, required: true },
|
||||||
|
name: { type: String, required: true },
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return { values: [] } as { values: { label: string; value: string }[] };
|
||||||
|
},
|
||||||
|
async mounted() {
|
||||||
|
const fields: Field[] = (this.schema?.fields ?? []).filter(
|
||||||
|
(f) =>
|
||||||
|
f &&
|
||||||
|
f.fieldtype !== 'Table' &&
|
||||||
|
f.fieldtype !== 'AttachImage' &&
|
||||||
|
f.fieldtype !== 'Attachment' &&
|
||||||
|
f.fieldname !== 'name' &&
|
||||||
|
!f.hidden &&
|
||||||
|
!f.meta &&
|
||||||
|
!f.abstract
|
||||||
|
);
|
||||||
|
|
||||||
|
const data = (
|
||||||
|
await this.fyo.db.getAll(this.schemaName, {
|
||||||
|
fields: fields.map((f) => f.fieldname),
|
||||||
|
filters: { name: this.name },
|
||||||
|
})
|
||||||
|
)[0];
|
||||||
|
|
||||||
|
this.values = fields
|
||||||
|
.map((f) => {
|
||||||
|
const value = data[f.fieldname];
|
||||||
|
if (isFalsy(value)) {
|
||||||
|
return { value: '', label: '' };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { value: this.fyo.format(data[f.fieldname], f), label: f.label };
|
||||||
|
})
|
||||||
|
.filter((i) => !!i.value);
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
schema() {
|
||||||
|
return this.fyo.schemaMap[this.schemaName];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
@ -1,27 +0,0 @@
|
|||||||
<template>
|
|
||||||
<Badge
|
|
||||||
class="flex-center"
|
|
||||||
:color="color"
|
|
||||||
v-if="status"
|
|
||||||
:class="defaultSize ? 'text-sm px-3' : ''"
|
|
||||||
>{{ statusLabel }}</Badge
|
|
||||||
>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
import { getStatusText, statusColor } from 'models/helpers';
|
|
||||||
import Badge from './Badge.vue';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'StatusBadge',
|
|
||||||
props: { status: String, defaultSize: { type: Boolean, default: true } },
|
|
||||||
computed: {
|
|
||||||
color() {
|
|
||||||
return statusColor[this.status];
|
|
||||||
},
|
|
||||||
statusLabel() {
|
|
||||||
return getStatusText(this.status) || this.status;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
components: { Badge },
|
|
||||||
};
|
|
||||||
</script>
|
|
109
src/components/StatusPill.vue
Normal file
109
src/components/StatusPill.vue
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
<template>
|
||||||
|
<p class="pill font-medium" :class="styleClass">{{ text }}</p>
|
||||||
|
</template>
|
||||||
|
<script lang="ts">
|
||||||
|
import { Doc } from 'fyo/model/doc';
|
||||||
|
import { isPesa } from 'fyo/utils';
|
||||||
|
import { Invoice } from 'models/baseModels/Invoice/Invoice';
|
||||||
|
import { Party } from 'models/baseModels/Party/Party';
|
||||||
|
import { getBgTextColorClass } from 'src/utils/colors';
|
||||||
|
import { defineComponent } from 'vue';
|
||||||
|
|
||||||
|
type Status = ReturnType<typeof getStatus>;
|
||||||
|
type UIColors = 'gray' | 'orange' | 'red' | 'green' | 'blue';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
props: { doc: { type: Doc, required: true } },
|
||||||
|
computed: {
|
||||||
|
styleClass(): string {
|
||||||
|
return getBgTextColorClass(this.color);
|
||||||
|
},
|
||||||
|
status(): Status {
|
||||||
|
return getStatus(this.doc);
|
||||||
|
},
|
||||||
|
text() {
|
||||||
|
const hasOutstanding = isPesa(this.doc.outstandingAmount);
|
||||||
|
if (hasOutstanding && this.status === 'Outstanding') {
|
||||||
|
const amt = this.fyo.format(this.doc.outstandingAmount, 'Currency');
|
||||||
|
return this.t`Unpaid ${amt}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.doc instanceof Invoice && this.status === 'NotTransferred') {
|
||||||
|
const amt = this.fyo.format(this.doc.stockNotTransferred, 'Float');
|
||||||
|
return this.t`Pending Qty. ${amt}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
Draft: this.t`Draft`,
|
||||||
|
Cancelled: this.t`Cancelled`,
|
||||||
|
Outstanding: this.t`Outstanding`,
|
||||||
|
NotTransferred: this.t`Not Transferred`,
|
||||||
|
NotSaved: this.t`Not Saved`,
|
||||||
|
Paid: this.t`Paid`,
|
||||||
|
Saved: this.t`Saved`,
|
||||||
|
Submitted: this.t`Submitted`,
|
||||||
|
}[this.status];
|
||||||
|
},
|
||||||
|
color(): UIColors {
|
||||||
|
return statusColorMap[this.status];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const statusColorMap: Record<Status, UIColors> = {
|
||||||
|
Draft: 'gray',
|
||||||
|
Cancelled: 'red',
|
||||||
|
Outstanding: 'orange',
|
||||||
|
NotTransferred: 'orange',
|
||||||
|
NotSaved: 'orange',
|
||||||
|
Paid: 'green',
|
||||||
|
Saved: 'blue',
|
||||||
|
Submitted: 'blue',
|
||||||
|
};
|
||||||
|
|
||||||
|
function getStatus(doc: Doc) {
|
||||||
|
if (doc.notInserted) {
|
||||||
|
return 'Draft';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (doc.dirty) {
|
||||||
|
return 'NotSaved';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (doc instanceof Party && doc.outstandingAmount?.isZero() !== true) {
|
||||||
|
return 'Outstanding';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (doc.schema.isSubmittable) {
|
||||||
|
return getSubmittableStatus(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'Saved';
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSubmittableStatus(doc: Doc) {
|
||||||
|
if (doc.isCancelled) {
|
||||||
|
return 'Cancelled';
|
||||||
|
}
|
||||||
|
|
||||||
|
const isInvoice = doc instanceof Invoice;
|
||||||
|
if (isInvoice && doc.outstandingAmount?.isZero() !== true) {
|
||||||
|
return 'Outstanding';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isInvoice && (doc.stockNotTransferred ?? 0) > 0) {
|
||||||
|
return 'NotTransferred';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isInvoice && doc.outstandingAmount?.isZero() === true) {
|
||||||
|
return 'Paid';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (doc.isSubmitted) {
|
||||||
|
return 'Submitted';
|
||||||
|
}
|
||||||
|
|
||||||
|
// no-op
|
||||||
|
return 'NotSaved';
|
||||||
|
}
|
||||||
|
</script>
|
@ -1,7 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<FormContainer>
|
<FormContainer>
|
||||||
<template #header-left v-if="hasDoc">
|
<template #header-left v-if="hasDoc">
|
||||||
<StatusBadge :status="status" class="h-8" />
|
|
||||||
<Barcode
|
<Barcode
|
||||||
class="h-8"
|
class="h-8"
|
||||||
v-if="canShowBarcode"
|
v-if="canShowBarcode"
|
||||||
@ -11,7 +10,7 @@
|
|||||||
}"
|
}"
|
||||||
/>
|
/>
|
||||||
<ExchangeRate
|
<ExchangeRate
|
||||||
v-if="hasDoc && doc.isMultiCurrency"
|
v-if="canShowExchangeRate"
|
||||||
:disabled="doc?.isSubmitted || doc?.isCancelled"
|
:disabled="doc?.isSubmitted || doc?.isCancelled"
|
||||||
:from-currency="fromCurrency"
|
:from-currency="fromCurrency"
|
||||||
:to-currency="toCurrency"
|
:to-currency="toCurrency"
|
||||||
@ -21,6 +20,12 @@
|
|||||||
await doc.set('exchangeRate', exchangeRate)
|
await doc.set('exchangeRate', exchangeRate)
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
|
<p
|
||||||
|
v-if="schema.label && !(canShowBarcode || canShowExchangeRate)"
|
||||||
|
class="text-xl font-semibold items-center text-gray-600"
|
||||||
|
>
|
||||||
|
{{ schema.label }}
|
||||||
|
</p>
|
||||||
</template>
|
</template>
|
||||||
<template #header v-if="hasDoc">
|
<template #header v-if="hasDoc">
|
||||||
<Button
|
<Button
|
||||||
@ -59,11 +64,8 @@
|
|||||||
}}</Button>
|
}}</Button>
|
||||||
</template>
|
</template>
|
||||||
<template #body>
|
<template #body>
|
||||||
<FormHeader
|
<FormHeader :form-title="title" class="sticky top-0 bg-white border-b">
|
||||||
:form-title="title"
|
<StatusPill v-if="hasDoc" :doc="doc" />
|
||||||
:form-sub-title="schema.label"
|
|
||||||
class="sticky top-0 bg-white border-b"
|
|
||||||
>
|
|
||||||
</FormHeader>
|
</FormHeader>
|
||||||
|
|
||||||
<!-- Section Container -->
|
<!-- Section Container -->
|
||||||
@ -155,7 +157,7 @@ import ExchangeRate from 'src/components/Controls/ExchangeRate.vue';
|
|||||||
import DropdownWithActions from 'src/components/DropdownWithActions.vue';
|
import DropdownWithActions from 'src/components/DropdownWithActions.vue';
|
||||||
import FormContainer from 'src/components/FormContainer.vue';
|
import FormContainer from 'src/components/FormContainer.vue';
|
||||||
import FormHeader from 'src/components/FormHeader.vue';
|
import FormHeader from 'src/components/FormHeader.vue';
|
||||||
import StatusBadge from 'src/components/StatusBadge.vue';
|
import StatusPill from 'src/components/StatusPill.vue';
|
||||||
import { getErrorMessage } from 'src/utils';
|
import { getErrorMessage } from 'src/utils';
|
||||||
import { shortcutsKey } from 'src/utils/injectionKeys';
|
import { shortcutsKey } from 'src/utils/injectionKeys';
|
||||||
import { docsPathMap } from 'src/utils/misc';
|
import { docsPathMap } from 'src/utils/misc';
|
||||||
@ -270,6 +272,9 @@ export default defineComponent({
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
return typeof this.doc?.addItem === 'function';
|
return typeof this.doc?.addItem === 'function';
|
||||||
},
|
},
|
||||||
|
canShowExchangeRate(): boolean {
|
||||||
|
return this.hasDoc && !!this.doc.isMultiCurrency;
|
||||||
|
},
|
||||||
exchangeRate(): number {
|
exchangeRate(): number {
|
||||||
if (!this.hasDoc || typeof this.doc.exchangeRate !== 'number') {
|
if (!this.hasDoc || typeof this.doc.exchangeRate !== 'number') {
|
||||||
return 1;
|
return 1;
|
||||||
@ -433,13 +438,13 @@ export default defineComponent({
|
|||||||
FormContainer,
|
FormContainer,
|
||||||
FormHeader,
|
FormHeader,
|
||||||
CommonFormSection,
|
CommonFormSection,
|
||||||
StatusBadge,
|
|
||||||
Button,
|
Button,
|
||||||
DropdownWithActions,
|
DropdownWithActions,
|
||||||
Barcode,
|
Barcode,
|
||||||
ExchangeRate,
|
ExchangeRate,
|
||||||
LinkedEntries,
|
LinkedEntries,
|
||||||
RowEditForm,
|
RowEditForm,
|
||||||
|
StatusPill,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -312,11 +312,6 @@ const linkEntryDisplayFields: Record<string, string[]> = {
|
|||||||
@apply border-0;
|
@apply border-0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pill {
|
|
||||||
@apply py-0.5 px-1.5 rounded-md text-xs;
|
|
||||||
width: fit-content;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pill-container:empty {
|
.pill-container:empty {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
@ -200,3 +200,9 @@ input[type='number']::-webkit-inner-spin-button {
|
|||||||
[dir='rtl'] .custom-scroll::-webkit-scrollbar-track:vertical {
|
[dir='rtl'] .custom-scroll::-webkit-scrollbar-track:vertical {
|
||||||
border-right: solid 1px theme('colors.gray.200');
|
border-right: solid 1px theme('colors.gray.200');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pill {
|
||||||
|
@apply py-0.5 px-1.5 rounded-md text-xs;
|
||||||
|
width: fit-content;
|
||||||
|
height: fit-content;
|
||||||
|
}
|
||||||
|
@ -17,7 +17,6 @@ import { fyo } from 'src/initFyo';
|
|||||||
import router from 'src/router';
|
import router from 'src/router';
|
||||||
import { SelectFileOptions } from 'utils/types';
|
import { SelectFileOptions } from 'utils/types';
|
||||||
import { RouteLocationRaw } from 'vue-router';
|
import { RouteLocationRaw } from 'vue-router';
|
||||||
import { stringifyCircular } from './';
|
|
||||||
import { evaluateHidden } from './doc';
|
import { evaluateHidden } from './doc';
|
||||||
import { showDialog, showToast } from './interactive';
|
import { showDialog, showToast } from './interactive';
|
||||||
import { selectFile } from './ipcCalls';
|
import { selectFile } from './ipcCalls';
|
||||||
|
Loading…
Reference in New Issue
Block a user