mirror of
https://github.com/frappe/books.git
synced 2024-12-23 03:19:01 +00:00
incr: add shortcuts to go back and
- save, submit, cancel and delete a focused doc - fix search filter issue
This commit is contained in:
parent
e4fbecc914
commit
974c95aafc
16
src/App.vue
16
src/App.vue
@ -41,6 +41,7 @@
|
||||
import { ConfigKeys } from 'fyo/core/types';
|
||||
import { RTL_LANGUAGES } from 'fyo/utils/consts';
|
||||
import { ModelNameEnum } from 'models/types';
|
||||
import { systemLanguageRef } from 'src/utils/refs';
|
||||
import { computed } from 'vue';
|
||||
import WindowsTitleBar from './components/WindowsTitleBar.vue';
|
||||
import { handleErrorWithDialog } from './errorHandling';
|
||||
@ -54,8 +55,9 @@ import { initializeInstance } from './utils/initialization';
|
||||
import { checkForUpdates } from './utils/ipcCalls';
|
||||
import { updateConfigFiles } from './utils/misc';
|
||||
import { Search } from './utils/search';
|
||||
import { routeTo, systemLanguage } from './utils/ui';
|
||||
import { getModKeyCode, Shortcuts, useKeys } from './utils/vueUtils';
|
||||
import { setGlobalShortcuts } from './utils/shortcuts';
|
||||
import { routeTo } from './utils/ui';
|
||||
import { Shortcuts, useKeys } from './utils/vueUtils';
|
||||
|
||||
export default {
|
||||
name: 'App',
|
||||
@ -69,7 +71,6 @@ export default {
|
||||
companyName: '',
|
||||
searcher: null,
|
||||
shortcuts: null,
|
||||
modKey: '',
|
||||
};
|
||||
},
|
||||
provide() {
|
||||
@ -77,7 +78,6 @@ export default {
|
||||
languageDirection: computed(() => this.languageDirection),
|
||||
searcher: computed(() => this.searcher),
|
||||
shortcuts: computed(() => this.shortcuts),
|
||||
modKey: computed(() => this.modKey),
|
||||
keys: computed(() => this.keys),
|
||||
};
|
||||
},
|
||||
@ -88,8 +88,8 @@ export default {
|
||||
WindowsTitleBar,
|
||||
},
|
||||
async mounted() {
|
||||
this.modKey = getModKeyCode(this.platform);
|
||||
this.shortcuts = new Shortcuts(this.keys);
|
||||
const shortcuts = new Shortcuts(this.keys);
|
||||
this.shortcuts = shortcuts;
|
||||
const lastSelectedFilePath = fyo.config.get(
|
||||
ConfigKeys.LastSelectedFilePath,
|
||||
null
|
||||
@ -105,10 +105,12 @@ export default {
|
||||
await handleErrorWithDialog(err, undefined, true, true);
|
||||
await this.showDbSelector();
|
||||
}
|
||||
|
||||
setGlobalShortcuts(shortcuts);
|
||||
},
|
||||
computed: {
|
||||
language() {
|
||||
return systemLanguage.value;
|
||||
return systemLanguageRef.value;
|
||||
},
|
||||
languageDirection() {
|
||||
return RTL_LANGUAGES.includes(this.language) ? 'rtl' : 'ltr';
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="flex bg-gray-25">
|
||||
<div class="flex bg-gray-25 overflow-x-auto">
|
||||
<div class="flex flex-1 flex-col">
|
||||
<!-- Page Header (Title, Buttons, etc) -->
|
||||
<PageHeader :title="title" :border="false" :searchborder="searchborder">
|
||||
|
@ -17,14 +17,7 @@
|
||||
v-if="openModal"
|
||||
>
|
||||
<div
|
||||
class="
|
||||
bg-white
|
||||
rounded-lg
|
||||
shadow-2xl
|
||||
border
|
||||
overflow-hidden
|
||||
inner
|
||||
"
|
||||
class="bg-white rounded-lg shadow-2xl border overflow-hidden inner"
|
||||
v-bind="$attrs"
|
||||
@click.stop
|
||||
>
|
||||
|
@ -4,7 +4,7 @@
|
||||
<Button @click="open" class="px-2" :padding="false">
|
||||
<feather-icon name="search" class="w-4 h-4 me-1 text-gray-800" />
|
||||
<p>{{ t`Search` }}</p>
|
||||
<div class="text-gray-500 px-1 ms-4 text-sm">
|
||||
<div class="text-gray-500 px-1 ms-4 text-sm whitespace-nowrap">
|
||||
{{ modKeyText('k') }}
|
||||
</div>
|
||||
</Button>
|
||||
@ -16,8 +16,9 @@
|
||||
@closemodal="close"
|
||||
:set-close-listener="false"
|
||||
>
|
||||
<div class="w-form">
|
||||
<!-- Search Input -->
|
||||
<div class="p-1 w-form">
|
||||
<div class="p-1">
|
||||
<input
|
||||
ref="input"
|
||||
type="search"
|
||||
@ -75,7 +76,9 @@
|
||||
class="text-sm text-end justify-self-end"
|
||||
:class="`text-${groupColorMap[si.group]}-500`"
|
||||
>
|
||||
{{ si.group === 'Docs' ? si.schemaLabel : groupLabelMap[si.group] }}
|
||||
{{
|
||||
si.group === 'Docs' ? si.schemaLabel : groupLabelMap[si.group]
|
||||
}}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@ -137,9 +140,14 @@
|
||||
border-blue-100
|
||||
whitespace-nowrap
|
||||
"
|
||||
:class="{ 'bg-blue-100': searcher.filters.schemaFilters[sf.value] }"
|
||||
:class="{
|
||||
'bg-blue-100': searcher.filters.schemaFilters[sf.value],
|
||||
}"
|
||||
@click="
|
||||
searcher.set(sf.value, !searcher.filters.schemaFilters[sf.value])
|
||||
searcher.set(
|
||||
sf.value,
|
||||
!searcher.filters.schemaFilters[sf.value]
|
||||
)
|
||||
"
|
||||
>
|
||||
{{ sf.label }}
|
||||
@ -187,6 +195,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
</template>
|
||||
<script>
|
||||
@ -211,7 +220,7 @@ export default {
|
||||
allowedLimits: [50, 100, 500, -1],
|
||||
};
|
||||
},
|
||||
inject: ['searcher', 'shortcuts', 'modKey'],
|
||||
inject: ['searcher', 'shortcuts'],
|
||||
components: { Modal, Button },
|
||||
async mounted() {
|
||||
if (fyo.store.isDevelopment) {
|
||||
@ -261,20 +270,12 @@ export default {
|
||||
},
|
||||
setShortcuts() {
|
||||
for (const { shortcut, callback } of this.getShortcuts()) {
|
||||
if (this.platform === 'Mac') {
|
||||
this.shortcuts.meta.set([shortcut], callback);
|
||||
} else {
|
||||
this.shortcuts.ctrl.set([shortcut], callback);
|
||||
}
|
||||
this.shortcuts.pmod.set([shortcut], callback);
|
||||
}
|
||||
},
|
||||
deleteShortcuts() {
|
||||
for (const { shortcut } of this.getShortcuts()) {
|
||||
if (this.platform === 'Mac') {
|
||||
this.shortcuts.meta.delete([shortcut]);
|
||||
} else {
|
||||
this.shortcuts.ctrl.delete([shortcut]);
|
||||
}
|
||||
this.shortcuts.pmod.delete([shortcut]);
|
||||
}
|
||||
},
|
||||
modKeyText(key) {
|
||||
@ -312,7 +313,7 @@ export default {
|
||||
},
|
||||
select(idx) {
|
||||
this.idx = idx ?? this.idx;
|
||||
this.suggestionsthis.idx?.action();
|
||||
this.suggestions[this.idx]?.action();
|
||||
this.close();
|
||||
},
|
||||
scrollToHighlighted() {
|
||||
|
@ -162,8 +162,9 @@ import Button from 'src/components/Button.vue';
|
||||
import { reportIssue } from 'src/errorHandling';
|
||||
import { fyo } from 'src/initFyo';
|
||||
import { openLink } from 'src/utils/ipcCalls';
|
||||
import { docsPathRef } from 'src/utils/refs';
|
||||
import { getSidebarConfig } from 'src/utils/sidebarConfig';
|
||||
import { docsPath, routeTo } from 'src/utils/ui';
|
||||
import { routeTo } from 'src/utils/ui';
|
||||
import router from '../router';
|
||||
import Icon from './Icon.vue';
|
||||
|
||||
@ -200,7 +201,7 @@ export default {
|
||||
routeTo,
|
||||
reportIssue,
|
||||
openDocumentation() {
|
||||
openLink('https://docs.frappebooks.com/' + docsPath.value);
|
||||
openLink('https://docs.frappebooks.com/' + docsPathRef.value);
|
||||
},
|
||||
setActiveGroup() {
|
||||
const { fullPath } = this.$router.currentRoute.value;
|
||||
|
@ -147,7 +147,8 @@ import { ModelNameEnum } from 'models/types';
|
||||
import PageHeader from 'src/components/PageHeader.vue';
|
||||
import { fyo } from 'src/initFyo';
|
||||
import { docsPathMap } from 'src/utils/misc';
|
||||
import { docsPath, openQuickEdit } from 'src/utils/ui';
|
||||
import { docsPathRef } from 'src/utils/refs';
|
||||
import { openQuickEdit } from 'src/utils/ui';
|
||||
import { getMapFromList, removeAtIndex } from 'utils/index';
|
||||
import { nextTick } from 'vue';
|
||||
import Button from '../components/Button.vue';
|
||||
@ -184,7 +185,7 @@ export default {
|
||||
window.coa = this;
|
||||
}
|
||||
|
||||
docsPath.value = docsPathMap.ChartOfAccounts;
|
||||
docsPathRef.value = docsPathMap.ChartOfAccounts;
|
||||
|
||||
if (this.refetchTotals) {
|
||||
await this.setTotalDebitAndCredit();
|
||||
@ -192,7 +193,7 @@ export default {
|
||||
}
|
||||
},
|
||||
deactivated() {
|
||||
docsPath.value = '';
|
||||
docsPathRef.value = '';
|
||||
},
|
||||
methods: {
|
||||
async expand() {
|
||||
|
@ -65,12 +65,12 @@
|
||||
|
||||
<script>
|
||||
import PageHeader from 'src/components/PageHeader.vue';
|
||||
import { docsPath } from 'src/utils/ui';
|
||||
import UnpaidInvoices from './UnpaidInvoices.vue';
|
||||
import Cashflow from './Cashflow.vue';
|
||||
import Expenses from './Expenses.vue';
|
||||
import PeriodSelector from './PeriodSelector.vue';
|
||||
import ProfitAndLoss from './ProfitAndLoss.vue';
|
||||
import { docsPathRef } from 'src/utils/refs';
|
||||
|
||||
export default {
|
||||
name: 'Dashboard',
|
||||
@ -86,10 +86,10 @@ export default {
|
||||
return { period: 'This Year' };
|
||||
},
|
||||
activated() {
|
||||
docsPath.value = 'analytics/dashboard';
|
||||
docsPathRef.value = 'analytics/dashboard';
|
||||
},
|
||||
deactivated() {
|
||||
docsPath.value = '';
|
||||
docsPathRef.value = '';
|
||||
},
|
||||
methods: {
|
||||
handlePeriodChange(period) {
|
||||
|
@ -355,7 +355,8 @@ import { importable, Importer } from 'src/dataImport';
|
||||
import { fyo } from 'src/initFyo';
|
||||
import { getSavePath, saveData, selectFile } from 'src/utils/ipcCalls';
|
||||
import { docsPathMap } from 'src/utils/misc';
|
||||
import { docsPath, showMessageDialog } from 'src/utils/ui';
|
||||
import { docsPathRef } from 'src/utils/refs';
|
||||
import { showMessageDialog } from 'src/utils/ui';
|
||||
import Loading from '../components/Loading.vue';
|
||||
|
||||
export default {
|
||||
@ -484,10 +485,10 @@ export default {
|
||||
},
|
||||
},
|
||||
activated() {
|
||||
docsPath.value = docsPathMap.DataImport;
|
||||
docsPathRef.value = docsPathMap.DataImport;
|
||||
},
|
||||
deactivated() {
|
||||
docsPath.value = '';
|
||||
docsPathRef.value = '';
|
||||
if (!this.complete) {
|
||||
return;
|
||||
}
|
||||
|
@ -130,7 +130,6 @@
|
||||
|
||||
<template #quickedit v-if="quickEditDoc">
|
||||
<QuickEditForm
|
||||
class="w-quick-edit"
|
||||
:name="quickEditDoc.name"
|
||||
:show-name="false"
|
||||
:show-save="false"
|
||||
@ -160,8 +159,8 @@ import FormHeader from 'src/components/FormHeader.vue';
|
||||
import StatusBadge from 'src/components/StatusBadge.vue';
|
||||
import { fyo } from 'src/initFyo';
|
||||
import { docsPathMap } from 'src/utils/misc';
|
||||
import { docsPathRef, focusedDocsRef } from 'src/utils/refs';
|
||||
import {
|
||||
docsPath,
|
||||
getGroupedActionsForDoc,
|
||||
routeTo,
|
||||
showMessageDialog,
|
||||
@ -232,14 +231,17 @@ export default {
|
||||
},
|
||||
},
|
||||
activated() {
|
||||
docsPath.value = docsPathMap[this.schemaName];
|
||||
docsPathRef.value = docsPathMap[this.schemaName];
|
||||
focusedDocsRef.add(this.doc);
|
||||
},
|
||||
deactivated() {
|
||||
docsPath.value = '';
|
||||
docsPathRef.value = '';
|
||||
focusedDocsRef.delete(this.doc);
|
||||
},
|
||||
async mounted() {
|
||||
try {
|
||||
this.doc = await fyo.doc.getDoc(this.schemaName, this.name);
|
||||
focusedDocsRef.add(this.doc);
|
||||
} catch (error) {
|
||||
if (error instanceof fyo.errors.NotFoundError) {
|
||||
routeTo(`/list/${this.schemaName}`);
|
||||
|
@ -273,7 +273,6 @@
|
||||
<Transition name="quickedit">
|
||||
<QuickEditForm
|
||||
v-if="quickEditDoc && !linked"
|
||||
class="w-quick-edit"
|
||||
:name="quickEditDoc.name"
|
||||
:show-name="false"
|
||||
:show-save="false"
|
||||
@ -313,8 +312,8 @@ import StatusBadge from 'src/components/StatusBadge.vue';
|
||||
import LinkedEntryWidget from 'src/components/Widgets/LinkedEntryWidget.vue';
|
||||
import { fyo } from 'src/initFyo';
|
||||
import { docsPathMap } from 'src/utils/misc';
|
||||
import { docsPathRef, focusedDocsRef } from 'src/utils/refs';
|
||||
import {
|
||||
docsPath,
|
||||
getGroupedActionsForDoc,
|
||||
routeTo,
|
||||
showMessageDialog,
|
||||
@ -339,6 +338,7 @@ export default {
|
||||
LinkedEntryWidget,
|
||||
Barcode,
|
||||
},
|
||||
inject: ['shortcuts'],
|
||||
provide() {
|
||||
return {
|
||||
schemaName: this.schemaName,
|
||||
@ -455,14 +455,17 @@ export default {
|
||||
},
|
||||
},
|
||||
activated() {
|
||||
docsPath.value = docsPathMap[this.schemaName];
|
||||
docsPathRef.value = docsPathMap[this.schemaName];
|
||||
focusedDocsRef.add(this.doc);
|
||||
},
|
||||
deactivated() {
|
||||
docsPath.value = '';
|
||||
docsPathRef.value = '';
|
||||
focusedDocsRef.delete(this.doc);
|
||||
},
|
||||
async mounted() {
|
||||
try {
|
||||
this.doc = await fyo.doc.getDoc(this.schemaName, this.name);
|
||||
focusedDocsRef.add(this.doc);
|
||||
} catch (error) {
|
||||
if (error instanceof fyo.errors.NotFoundError) {
|
||||
routeTo(`/list/${this.schemaName}`);
|
||||
|
@ -148,8 +148,8 @@ import FormHeader from 'src/components/FormHeader.vue';
|
||||
import StatusBadge from 'src/components/StatusBadge.vue';
|
||||
import { fyo } from 'src/initFyo';
|
||||
import { docsPathMap } from 'src/utils/misc';
|
||||
import { docsPathRef, focusedDocsRef } from 'src/utils/refs';
|
||||
import {
|
||||
docsPath,
|
||||
getGroupedActionsForDoc,
|
||||
routeTo,
|
||||
showMessageDialog,
|
||||
@ -182,14 +182,17 @@ export default {
|
||||
};
|
||||
},
|
||||
activated() {
|
||||
docsPath.value = docsPathMap.JournalEntry;
|
||||
docsPathRef.value = docsPathMap.JournalEntry;
|
||||
focusedDocsRef.add(this.doc);
|
||||
},
|
||||
deactivated() {
|
||||
docsPath.value = '';
|
||||
docsPathRef.value = '';
|
||||
focusedDocsRef.delete(this.doc);
|
||||
},
|
||||
async mounted() {
|
||||
try {
|
||||
this.doc = await fyo.doc.getDoc(this.schemaName, this.name);
|
||||
focusedDocsRef.add(this.doc);
|
||||
} catch (error) {
|
||||
if (error instanceof fyo.errors.NotFoundError) {
|
||||
routeTo(`/list/${this.schemaName}`);
|
||||
|
@ -51,7 +51,8 @@ import {
|
||||
docsPathMap,
|
||||
getCreateFiltersFromListViewFilters,
|
||||
} from 'src/utils/misc';
|
||||
import { docsPath, openQuickEdit, routeTo } from 'src/utils/ui';
|
||||
import { docsPathRef } from 'src/utils/refs';
|
||||
import { openQuickEdit, routeTo } from 'src/utils/ui';
|
||||
import List from './List.vue';
|
||||
|
||||
export default {
|
||||
@ -82,14 +83,14 @@ export default {
|
||||
}
|
||||
|
||||
this.listConfig = getListConfig(this.schemaName);
|
||||
docsPath.value = docsPathMap[this.schemaName] ?? docsPathMap.Entries;
|
||||
docsPathRef.value = docsPathMap[this.schemaName] ?? docsPathMap.Entries;
|
||||
|
||||
if (this.fyo.store.isDevelopment) {
|
||||
window.lv = this;
|
||||
}
|
||||
},
|
||||
deactivated() {
|
||||
docsPath.value = '';
|
||||
docsPathRef.value = '';
|
||||
},
|
||||
methods: {
|
||||
updatedData(listFilters) {
|
||||
|
@ -105,6 +105,7 @@ import StatusBadge from 'src/components/StatusBadge.vue';
|
||||
import TwoColumnForm from 'src/components/TwoColumnForm.vue';
|
||||
import { fyo } from 'src/initFyo';
|
||||
import { getQuickEditWidget } from 'src/utils/quickEditWidgets';
|
||||
import { focusedDocsRef } from 'src/utils/refs';
|
||||
import { getActionsForDoc, openQuickEdit } from 'src/utils/ui';
|
||||
|
||||
export default {
|
||||
@ -148,17 +149,26 @@ export default {
|
||||
statusText: null,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
async mounted() {
|
||||
if (this.defaults) {
|
||||
this.values = JSON.parse(this.defaults);
|
||||
}
|
||||
|
||||
await this.fetchFieldsAndDoc();
|
||||
focusedDocsRef.add(this.doc);
|
||||
|
||||
if (fyo.store.isDevelopment) {
|
||||
window.qef = this;
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
await this.fetchFieldsAndDoc();
|
||||
activated() {
|
||||
focusedDocsRef.add(this.doc);
|
||||
},
|
||||
deactivated() {
|
||||
focusedDocsRef.delete(this.doc);
|
||||
},
|
||||
unmounted() {
|
||||
focusedDocsRef.delete(this.doc);
|
||||
},
|
||||
computed: {
|
||||
isChild() {
|
||||
|
@ -46,7 +46,7 @@ import PageHeader from 'src/components/PageHeader.vue';
|
||||
import ListReport from 'src/components/Report/ListReport.vue';
|
||||
import { fyo } from 'src/initFyo';
|
||||
import { docsPathMap } from 'src/utils/misc';
|
||||
import { docsPath } from 'src/utils/ui';
|
||||
import { docsPathRef } from 'src/utils/refs';
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
@ -70,7 +70,7 @@ export default defineComponent({
|
||||
},
|
||||
components: { PageHeader, FormControl, ListReport, DropdownWithActions },
|
||||
async activated() {
|
||||
docsPath.value = docsPathMap[this.reportClassName] ?? docsPathMap.Reports;
|
||||
docsPathRef.value = docsPathMap[this.reportClassName] ?? docsPathMap.Reports;
|
||||
await this.setReportData();
|
||||
|
||||
const filters = JSON.parse(this.defaultFilters);
|
||||
@ -88,7 +88,7 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
deactivated() {
|
||||
docsPath.value = '';
|
||||
docsPathRef.value = '';
|
||||
},
|
||||
computed: {
|
||||
title() {
|
||||
|
@ -49,7 +49,8 @@ import Row from 'src/components/Row.vue';
|
||||
import StatusBadge from 'src/components/StatusBadge.vue';
|
||||
import { fyo } from 'src/initFyo';
|
||||
import { docsPathMap } from 'src/utils/misc';
|
||||
import { docsPath, showToast } from 'src/utils/ui';
|
||||
import { docsPathRef } from 'src/utils/refs';
|
||||
import { showToast } from 'src/utils/ui';
|
||||
import { IPC_MESSAGES } from 'utils/messages';
|
||||
import { h, markRaw } from 'vue';
|
||||
import TabBase from './TabBase.vue';
|
||||
@ -112,10 +113,10 @@ export default {
|
||||
},
|
||||
activated() {
|
||||
this.setActiveTab();
|
||||
docsPath.value = docsPathMap.Settings;
|
||||
docsPathRef.value = docsPathMap.Settings;
|
||||
},
|
||||
deactivated() {
|
||||
docsPath.value = '';
|
||||
docsPathRef.value = '';
|
||||
if (this.fieldsChanged.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
@ -82,6 +82,7 @@ input[type='number']::-webkit-inner-spin-button {
|
||||
|
||||
.w-quick-edit {
|
||||
width: var(--w-quick-edit);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.h-form {
|
||||
|
@ -3,7 +3,8 @@ import { DEFAULT_LANGUAGE } from 'fyo/utils/consts';
|
||||
import { setLanguageMapOnTranslationString } from 'fyo/utils/translation';
|
||||
import { fyo } from 'src/initFyo';
|
||||
import { IPC_ACTIONS, IPC_MESSAGES } from 'utils/messages';
|
||||
import { showToast, systemLanguage } from './ui';
|
||||
import { systemLanguageRef } from './refs';
|
||||
import { showToast } from './ui';
|
||||
|
||||
// Language: Language Code in books/translations
|
||||
export const languageCodeMap: Record<string, string> = {
|
||||
@ -42,7 +43,7 @@ export async function setLanguageMap(
|
||||
|
||||
if (success && !usingDefault) {
|
||||
fyo.config.set('language', language);
|
||||
systemLanguage.value = language;
|
||||
systemLanguageRef.value = language;
|
||||
}
|
||||
|
||||
if (!dontReload && success && initLanguage !== oldLanguage) {
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { Fyo } from 'fyo';
|
||||
import { ConfigFile, ConfigKeys } from 'fyo/core/types';
|
||||
import { Doc } from 'fyo/model/doc';
|
||||
import { DateTime } from 'luxon';
|
||||
import { SetupWizard } from 'models/baseModels/SetupWizard/SetupWizard';
|
||||
import { ModelNameEnum } from 'models/types';
|
||||
@ -160,3 +161,45 @@ export function getCreateFiltersFromListViewFilters(filters: QueryFilter) {
|
||||
|
||||
return createFilters;
|
||||
}
|
||||
|
||||
export class FocusedDocContextSet {
|
||||
set: Doc[];
|
||||
constructor() {
|
||||
this.set = [];
|
||||
}
|
||||
|
||||
add(doc: unknown) {
|
||||
if (!(doc instanceof Doc)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const index = this.findIndex(doc);
|
||||
if (index !== -1) {
|
||||
this.delete(index);
|
||||
}
|
||||
|
||||
return this.set.push(doc);
|
||||
}
|
||||
|
||||
delete(index: Doc | number) {
|
||||
if (typeof index !== 'number') {
|
||||
index = this.findIndex(index);
|
||||
}
|
||||
|
||||
if (index === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.set = this.set.filter((_, i) => i !== index);
|
||||
}
|
||||
|
||||
last() {
|
||||
return this.set.at(-1);
|
||||
}
|
||||
|
||||
findIndex(doc: Doc) {
|
||||
return this.set.findIndex(
|
||||
(d) => d.name === doc.name && d.schemaName === doc.schemaName
|
||||
);
|
||||
}
|
||||
}
|
||||
|
8
src/utils/refs.ts
Normal file
8
src/utils/refs.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { reactive, ref } from 'vue';
|
||||
import { FocusedDocContextSet } from './misc';
|
||||
|
||||
export const docsPathRef = ref<string>('');
|
||||
export const systemLanguageRef = ref<string>('');
|
||||
export const focusedDocsRef = reactive<FocusedDocContextSet>(
|
||||
new FocusedDocContextSet()
|
||||
);
|
@ -594,10 +594,7 @@ export class Search {
|
||||
keys.sort((a, b) => safeParseFloat(b) - safeParseFloat(a));
|
||||
const array: SearchItems = [];
|
||||
for (const key of keys) {
|
||||
const keywords = groupedKeywords[key];
|
||||
if (!keywords?.length) {
|
||||
continue;
|
||||
}
|
||||
const keywords = groupedKeywords[key] ?? [];
|
||||
|
||||
this._pushDocSearchItems(keywords, array, input);
|
||||
if (key === '0') {
|
||||
|
78
src/utils/shortcuts.ts
Normal file
78
src/utils/shortcuts.ts
Normal file
@ -0,0 +1,78 @@
|
||||
import { t } from 'fyo';
|
||||
import type { Doc } from 'fyo/model/doc';
|
||||
import { fyo } from 'src/initFyo';
|
||||
import router from 'src/router';
|
||||
import { focusedDocsRef } from './refs';
|
||||
import { showMessageDialog } from './ui';
|
||||
import { Shortcuts } from './vueUtils';
|
||||
|
||||
export function setGlobalShortcuts(shortcuts: Shortcuts) {
|
||||
/**
|
||||
* PMod : if macOS then Meta (⌘) else Ctrl, both Left and Right
|
||||
*
|
||||
* Backspace : Go to the previous page
|
||||
* PMod + S : Save or Submit focused doc if possible
|
||||
* PMod + Backspace : Cancel or Delete focused doc if possible
|
||||
*/
|
||||
shortcuts.set(['Backspace'], async () => {
|
||||
if (document.body !== document.activeElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
router.back();
|
||||
});
|
||||
|
||||
shortcuts.pmod.set(['KeyS'], async () => {
|
||||
const doc = focusedDocsRef.last();
|
||||
if (!doc) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (doc.canSave) {
|
||||
await showDocStateChangeMessageDialog(doc, 'sync');
|
||||
} else if (doc.canSubmit) {
|
||||
await showDocStateChangeMessageDialog(doc, 'submit');
|
||||
}
|
||||
});
|
||||
|
||||
shortcuts.pmod.set(['Backspace'], async () => {
|
||||
const doc = focusedDocsRef.last();
|
||||
if (!doc) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (doc.canCancel) {
|
||||
await showDocStateChangeMessageDialog(doc, 'cancel');
|
||||
} else if (doc.canDelete) {
|
||||
await showDocStateChangeMessageDialog(doc, 'delete');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function showDocStateChangeMessageDialog(
|
||||
doc: Doc,
|
||||
state: 'sync' | 'submit' | 'cancel' | 'delete'
|
||||
) {
|
||||
const label = fyo.schemaMap[doc.schemaName]?.label ?? t`Doc`;
|
||||
const name = doc.name ?? '';
|
||||
const message =
|
||||
{ sync: t`Save`, submit: t`Submit`, cancel: t`Cancel`, delete: t`Delete` }[
|
||||
state
|
||||
] + ` ${label} ${name}`;
|
||||
|
||||
await showMessageDialog({
|
||||
message,
|
||||
buttons: [
|
||||
{
|
||||
label: t`Yes`,
|
||||
async action() {
|
||||
await doc[state]();
|
||||
},
|
||||
},
|
||||
{
|
||||
label: t`No`,
|
||||
action() {},
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
@ -4,7 +4,7 @@
|
||||
*/
|
||||
import { ipcRenderer } from 'electron';
|
||||
import { t } from 'fyo';
|
||||
import { Doc } from 'fyo/model/doc';
|
||||
import type { Doc } from 'fyo/model/doc';
|
||||
import { Action } from 'fyo/model/types';
|
||||
import { getActions } from 'fyo/utils';
|
||||
import { getDbError, LinkValidationError, ValueError } from 'fyo/utils/errors';
|
||||
@ -23,9 +23,6 @@ import {
|
||||
ToastOptions,
|
||||
} from './types';
|
||||
|
||||
export const docsPath = ref('');
|
||||
export const systemLanguage = ref('');
|
||||
|
||||
export async function openQuickEdit({
|
||||
doc,
|
||||
schemaName,
|
||||
|
@ -20,6 +20,7 @@ const mods: Readonly<Mod[]> = ['alt', 'ctrl', 'meta', 'repeat', 'shift'];
|
||||
|
||||
export class Shortcuts {
|
||||
keys: Keys;
|
||||
isMac: boolean;
|
||||
shortcuts: Map<string, ShortcutFunction>;
|
||||
modMap: Partial<Record<Mod, boolean>>;
|
||||
|
||||
@ -27,6 +28,7 @@ export class Shortcuts {
|
||||
this.modMap = {};
|
||||
this.keys = keys ?? useKeys();
|
||||
this.shortcuts = new Map();
|
||||
this.isMac = getIsMac();
|
||||
|
||||
watch(this.keys, (keys) => {
|
||||
this.#trigger(keys);
|
||||
@ -111,10 +113,18 @@ export class Shortcuts {
|
||||
this.modMap['repeat'] = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
get pmod() {
|
||||
if (this.isMac) {
|
||||
return this.meta;
|
||||
} else {
|
||||
return this.ctrl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function useKeys() {
|
||||
const isMac = navigator.userAgent.indexOf('Mac') !== -1;
|
||||
const isMac = getIsMac();
|
||||
const keys: Keys = reactive({
|
||||
pressed: new Set<string>(),
|
||||
alt: false,
|
||||
@ -184,10 +194,6 @@ export function useMouseLocation() {
|
||||
return loc;
|
||||
}
|
||||
|
||||
export function getModKeyCode(platform: 'Windows' | 'Linux' | 'Mac') {
|
||||
if (platform === 'Mac') {
|
||||
return 'MetaLeft';
|
||||
}
|
||||
|
||||
return 'CtrlLeft';
|
||||
function getIsMac() {
|
||||
return navigator.userAgent.indexOf('Mac') !== -1;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user