2
0
mirror of https://github.com/frappe/books.git synced 2024-11-08 14:50:56 +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:
18alantom 2023-03-09 21:46:31 +05:30
parent bac00b5a36
commit 0614f99ea1
5 changed files with 101 additions and 26 deletions

View File

@ -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 '⌘';

View File

@ -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 {

View File

@ -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;
},

View File

@ -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;

View File

@ -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',