mirror of
https://github.com/frappe/books.git
synced 2024-11-10 07:40:55 +00:00
feat: add editor mode and apply changes shortcuts
- fix save shortcuts (in CommonForm too) - update shortcuts helper list - add documentation link
This commit is contained in:
parent
bac00b5a36
commit
0614f99ea1
@ -152,9 +152,30 @@ export default defineComponent({
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: t`Template Builder`,
|
||||
description: t`Applicable when Template Builder is open`,
|
||||
collapsed: false,
|
||||
shortcuts: [
|
||||
{
|
||||
shortcut: [this.ctrl, 'Enter'],
|
||||
description: t`Apply and view changes made to the print template`,
|
||||
},
|
||||
{
|
||||
shortcut: [this.ctrl, 'E'],
|
||||
description: t`Toggle Edit Mode`,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
},
|
||||
computed: {
|
||||
ctrl() {
|
||||
if (this.isMac) {
|
||||
return 'control';
|
||||
}
|
||||
return 'Ctrl';
|
||||
},
|
||||
pmod() {
|
||||
if (this.isMac) {
|
||||
return '⌘';
|
||||
|
@ -181,7 +181,9 @@ export default defineComponent({
|
||||
},
|
||||
deactivated(): void {
|
||||
docsPathRef.value = '';
|
||||
focusedDocsRef.add(this.docOrNull);
|
||||
if (this.docOrNull) {
|
||||
focusedDocsRef.delete(this.doc);
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
hasDoc(): boolean {
|
||||
|
@ -139,11 +139,13 @@
|
||||
<!-- Template Editor -->
|
||||
<div class="min-h-0">
|
||||
<TemplateEditor
|
||||
v-if="typeof doc.template === 'string' && hints"
|
||||
ref="templateEditor"
|
||||
class="overflow-auto custom-scroll h-full"
|
||||
v-if="typeof doc.template === 'string'"
|
||||
v-model.lazy="doc.template"
|
||||
:initial-value="doc.template"
|
||||
:disabled="!doc.isCustom"
|
||||
:hints="hints ?? undefined"
|
||||
:hints="hints"
|
||||
@blur="(value: string) => setTemplate(value)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -184,6 +186,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { EditorView } from 'codemirror';
|
||||
import { Doc } from 'fyo/model/doc';
|
||||
import { PrintTemplate } from 'models/baseModels/PrintTemplate';
|
||||
import { ModelNameEnum } from 'models/types';
|
||||
@ -194,12 +197,13 @@ import DropdownWithActions from 'src/components/DropdownWithActions.vue';
|
||||
import HorizontalResizer from 'src/components/HorizontalResizer.vue';
|
||||
import PageHeader from 'src/components/PageHeader.vue';
|
||||
import { handleErrorWithDialog } from 'src/errorHandling';
|
||||
import { docsPathMap } from 'src/utils/misc';
|
||||
import {
|
||||
baseTemplate,
|
||||
getPrintTemplatePropHints,
|
||||
getPrintTemplatePropValues,
|
||||
} from 'src/utils/printTemplates';
|
||||
import { showSidebar } from 'src/utils/refs';
|
||||
import { docsPathRef, focusedDocsRef, showSidebar } from 'src/utils/refs';
|
||||
import { PrintValues } from 'src/utils/types';
|
||||
import {
|
||||
getActionsForDoc,
|
||||
@ -207,6 +211,7 @@ import {
|
||||
openSettings,
|
||||
showToast,
|
||||
} from 'src/utils/ui';
|
||||
import { Shortcuts } from 'src/utils/vueUtils';
|
||||
import { getMapFromList } from 'utils/index';
|
||||
import { computed, defineComponent } from 'vue';
|
||||
import PrintContainer from './PrintContainer.vue';
|
||||
@ -225,6 +230,7 @@ export default defineComponent({
|
||||
FormControl,
|
||||
TemplateBuilderHint,
|
||||
},
|
||||
inject: { shortcutManager: { from: 'shortcuts' } },
|
||||
provide() {
|
||||
return { doc: computed(() => this.doc) };
|
||||
},
|
||||
@ -234,10 +240,8 @@ export default defineComponent({
|
||||
editMode: false,
|
||||
showHints: false,
|
||||
wasSidebarShown: true,
|
||||
showEditor: false,
|
||||
hints: undefined,
|
||||
values: null,
|
||||
helpersCollapsed: true,
|
||||
displayDoc: null,
|
||||
scale: 0.65,
|
||||
panelWidth: 22 /** rem */ * 16 /** px */,
|
||||
@ -248,7 +252,6 @@ export default defineComponent({
|
||||
hints?: Record<string, unknown>;
|
||||
values: null | PrintValues;
|
||||
doc: PrintTemplate | null;
|
||||
showEditor: boolean;
|
||||
displayDoc: PrintTemplate | null;
|
||||
scale: number;
|
||||
panelWidth: number;
|
||||
@ -256,6 +259,7 @@ export default defineComponent({
|
||||
},
|
||||
async mounted() {
|
||||
await this.setDoc();
|
||||
focusedDocsRef.add(this.doc);
|
||||
|
||||
if (this.doc?.template == null) {
|
||||
await this.doc?.set('template', baseTemplate);
|
||||
@ -270,7 +274,39 @@ export default defineComponent({
|
||||
|
||||
this.wasSidebarShown = showSidebar.value;
|
||||
},
|
||||
activated(): void {
|
||||
docsPathRef.value = docsPathMap.PrintTemplate ?? '';
|
||||
this.shortcuts.ctrl.set(['Enter'], this.setTemplate);
|
||||
this.shortcuts.ctrl.set(['KeyE'], this.toggleEditMode);
|
||||
},
|
||||
deactivated(): void {
|
||||
docsPathRef.value = '';
|
||||
if (this.doc instanceof Doc) {
|
||||
focusedDocsRef.delete(this.doc);
|
||||
}
|
||||
this.shortcuts.ctrl.delete(['Enter']);
|
||||
this.shortcuts.ctrl.delete(['KeyE']);
|
||||
},
|
||||
methods: {
|
||||
getTemplateEditorState() {
|
||||
const fallback = this.doc?.template ?? '';
|
||||
|
||||
// @ts-ignore
|
||||
const { view } = this.$refs.templateEditor ?? {};
|
||||
if (!(view instanceof EditorView)) {
|
||||
return fallback;
|
||||
}
|
||||
|
||||
return view.state.doc.toString();
|
||||
},
|
||||
async setTemplate(value?: string) {
|
||||
if (!this.doc?.isCustom) {
|
||||
return;
|
||||
}
|
||||
|
||||
value ??= this.getTemplateEditorState();
|
||||
await this.doc?.set('template', value);
|
||||
},
|
||||
setScale({ target }: Event) {
|
||||
if (!(target instanceof HTMLInputElement)) {
|
||||
return;
|
||||
@ -279,7 +315,16 @@ export default defineComponent({
|
||||
const scale = Number(target.value);
|
||||
this.scale = Math.max(Math.min(scale, 10), 0.15);
|
||||
},
|
||||
toggleEditMode() {
|
||||
async toggleEditMode() {
|
||||
if (!this.doc?.isCustom) {
|
||||
return;
|
||||
}
|
||||
|
||||
let message = this.t`Please set a Display Doc`;
|
||||
if (!this.displayDoc) {
|
||||
return showToast({ type: 'warning', message, duration: 1000 });
|
||||
}
|
||||
|
||||
this.editMode = !this.editMode;
|
||||
|
||||
if (this.editMode) {
|
||||
@ -288,7 +333,7 @@ export default defineComponent({
|
||||
showSidebar.value = this.wasSidebarShown;
|
||||
}
|
||||
|
||||
let message = this.t`Edit Mode Enabled`;
|
||||
message = this.t`Edit Mode Enabled`;
|
||||
if (!this.editMode) {
|
||||
message = this.t`Edit Mode Disabled`;
|
||||
}
|
||||
@ -376,6 +421,16 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
shortcuts(): Shortcuts {
|
||||
// @ts-ignore
|
||||
const shortcutManager = this.shortcutManager;
|
||||
if (shortcutManager instanceof Shortcuts) {
|
||||
return shortcutManager;
|
||||
}
|
||||
|
||||
// no-op (hopefully)
|
||||
throw Error('Shortcuts Not Found');
|
||||
},
|
||||
maxWidth() {
|
||||
return window.innerWidth - 12 * 16 - 100;
|
||||
},
|
||||
|
@ -18,16 +18,14 @@ import { defineComponent, markRaw } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
data() {
|
||||
return { state: null, view: null, compartments: {}, changed: false } as {
|
||||
return { state: null, view: null, compartments: {} } as {
|
||||
state: EditorState | null;
|
||||
view: EditorView | null;
|
||||
compartments: Record<string, Compartment>;
|
||||
changed: boolean;
|
||||
};
|
||||
},
|
||||
props: {
|
||||
modelModifiers: { type: Object, default: () => ({}) },
|
||||
modelValue: { type: String, required: true },
|
||||
initialValue: { type: String, required: true },
|
||||
disabled: { type: Boolean, default: false },
|
||||
hints: { type: Object },
|
||||
},
|
||||
@ -36,11 +34,16 @@ export default defineComponent({
|
||||
this.setDisabled(value);
|
||||
},
|
||||
},
|
||||
emits: ['update:modelValue'],
|
||||
emits: ['input', 'blur'],
|
||||
mounted() {
|
||||
if (!this.view) {
|
||||
this.init();
|
||||
}
|
||||
|
||||
if (this.fyo.store.isDevelopment) {
|
||||
// @ts-ignore
|
||||
window.te = this;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
@ -61,7 +64,7 @@ export default defineComponent({
|
||||
const completions = getCompletionsFromHints(this.hints ?? {});
|
||||
|
||||
const view = new EditorView({
|
||||
doc: this.modelValue,
|
||||
doc: this.initialValue,
|
||||
extensions: [
|
||||
EditorView.updateListener.of(this.updateListener),
|
||||
readOnly.of(EditorState.readOnly.of(this.disabled)),
|
||||
@ -80,19 +83,12 @@ export default defineComponent({
|
||||
},
|
||||
updateListener(update: ViewUpdate) {
|
||||
if (update.docChanged) {
|
||||
this.changed = true;
|
||||
this.$emit('input', this.view?.state.doc.toString() ?? '');
|
||||
}
|
||||
|
||||
if (this.modelModifiers.lazy && !update.focusChanged) {
|
||||
return;
|
||||
if (update.focusChanged && !this.view?.hasFocus) {
|
||||
this.$emit('blur', this.view?.state.doc.toString() ?? '');
|
||||
}
|
||||
|
||||
if (!this.changed) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.$emit('update:modelValue', this.view?.state.doc.toString() ?? '');
|
||||
this.changed = false;
|
||||
},
|
||||
setDisabled(value: boolean) {
|
||||
const { readOnly, editable } = this.compartments;
|
||||
|
@ -115,6 +115,7 @@ export const docsPathMap: Record<string, string | undefined> = {
|
||||
[ModelNameEnum.Party]: 'entries/party',
|
||||
[ModelNameEnum.Item]: 'entries/items',
|
||||
[ModelNameEnum.Tax]: 'entries/taxes',
|
||||
[ModelNameEnum.PrintTemplate]: 'miscellaneous/print-templates',
|
||||
|
||||
// Miscellaneous
|
||||
Search: 'miscellaneous/search',
|
||||
|
Loading…
Reference in New Issue
Block a user