mirror of
https://github.com/directus/directus.git
synced 2026-01-30 08:47:57 -05:00
Create composable use-edits-guard (#11018)
This commit is contained in:
@@ -1,4 +0,0 @@
|
||||
import unsavedChanges from './unsaved-changes';
|
||||
|
||||
export { unsavedChanges };
|
||||
export default unsavedChanges;
|
||||
@@ -1,18 +0,0 @@
|
||||
import { onBeforeMount, onBeforeUnmount, Ref } from 'vue';
|
||||
|
||||
export default function unsavedChanges(isSavable: Ref<boolean>): void {
|
||||
onBeforeMount(() => {
|
||||
window.addEventListener('beforeunload', beforeUnload);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('beforeunload', beforeUnload);
|
||||
});
|
||||
function beforeUnload(event: BeforeUnloadEvent) {
|
||||
if (isSavable.value) {
|
||||
event.preventDefault();
|
||||
event.returnValue = '';
|
||||
return '';
|
||||
}
|
||||
}
|
||||
}
|
||||
4
app/src/composables/use-edits-guard/index.ts
Normal file
4
app/src/composables/use-edits-guard/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import { useEditsGuard } from './use-edits-guard';
|
||||
|
||||
export { useEditsGuard };
|
||||
export default useEditsGuard;
|
||||
36
app/src/composables/use-edits-guard/use-edits-guard.ts
Normal file
36
app/src/composables/use-edits-guard/use-edits-guard.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { ref, Ref, onBeforeMount, onBeforeUnmount } from 'vue';
|
||||
import { onBeforeRouteUpdate, onBeforeRouteLeave, NavigationGuard } from 'vue-router';
|
||||
|
||||
export function useEditsGuard(isSavable: Ref<boolean>) {
|
||||
const confirmLeave = ref(false);
|
||||
const leaveTo = ref<string | null>(null);
|
||||
|
||||
const beforeUnload = (event: BeforeUnloadEvent) => {
|
||||
if (isSavable.value) {
|
||||
event.preventDefault();
|
||||
event.returnValue = '';
|
||||
return '';
|
||||
}
|
||||
};
|
||||
|
||||
const editsGuard: NavigationGuard = (to) => {
|
||||
if (isSavable.value) {
|
||||
confirmLeave.value = true;
|
||||
leaveTo.value = to.fullPath;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
onBeforeMount(() => {
|
||||
window.addEventListener('beforeunload', beforeUnload);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('beforeunload', beforeUnload);
|
||||
});
|
||||
|
||||
onBeforeRouteUpdate(editsGuard);
|
||||
onBeforeRouteLeave(editsGuard);
|
||||
|
||||
return { confirmLeave, leaveTo };
|
||||
}
|
||||
@@ -212,9 +212,9 @@ import SharesSidebarDetail from '@/views/private/components/shares-sidebar-detai
|
||||
import useItem from '@/composables/use-item';
|
||||
import SaveOptions from '@/views/private/components/save-options';
|
||||
import useShortcut from '@/composables/use-shortcut';
|
||||
import { useRouter, onBeforeRouteUpdate, onBeforeRouteLeave, NavigationGuard } from 'vue-router';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { usePermissions } from '@/composables/use-permissions';
|
||||
import unsavedChanges from '@/composables/unsaved-changes';
|
||||
import useEditsGuard from '@/composables/use-edits-guard';
|
||||
import { useTitle } from '@/composables/use-title';
|
||||
import { renderStringTemplate } from '@/utils/render-string-template';
|
||||
import useTemplateData from '@/composables/use-template-data';
|
||||
@@ -302,14 +302,10 @@ export default defineComponent({
|
||||
return hasEdits.value;
|
||||
});
|
||||
|
||||
unsavedChanges(isSavable);
|
||||
|
||||
const { confirmLeave, leaveTo } = useEditsGuard(isSavable);
|
||||
const confirmDelete = ref(false);
|
||||
const confirmArchive = ref(false);
|
||||
|
||||
const confirmLeave = ref(false);
|
||||
const leaveTo = ref<string | null>(null);
|
||||
|
||||
const title = computed(() => {
|
||||
if (te(`collection_names_singular.${props.collection}`)) {
|
||||
return isNew.value
|
||||
@@ -349,16 +345,6 @@ export default defineComponent({
|
||||
useShortcut('meta+s', saveAndStay, form);
|
||||
useShortcut('meta+shift+s', saveAndAddNew, form);
|
||||
|
||||
const editsGuard: NavigationGuard = (to) => {
|
||||
if (hasEdits.value) {
|
||||
confirmLeave.value = true;
|
||||
leaveTo.value = to.fullPath;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
onBeforeRouteUpdate(editsGuard);
|
||||
onBeforeRouteLeave(editsGuard);
|
||||
|
||||
const { deleteAllowed, archiveAllowed, saveAllowed, updateAllowed, shareAllowed, fields, revisionsAllowed } =
|
||||
usePermissions(collection, item, isNew);
|
||||
|
||||
|
||||
@@ -88,11 +88,11 @@
|
||||
</v-button>
|
||||
|
||||
<v-button
|
||||
v-tooltip.bottom="saveAllowed ? t('save') : t('not_allowed')"
|
||||
v-tooltip.bottom="isSavable ? t('save') : t('not_allowed')"
|
||||
rounded
|
||||
icon
|
||||
:loading="saving"
|
||||
:disabled="hasEdits === false || saveAllowed === false"
|
||||
:disabled="!isSavable"
|
||||
@click="saveAndQuit"
|
||||
>
|
||||
<v-icon name="check" />
|
||||
@@ -180,7 +180,7 @@
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { defineComponent, computed, toRefs, ref, watch, ComponentPublicInstance } from 'vue';
|
||||
import FilesNavigation from '../components/navigation.vue';
|
||||
import { useRouter, onBeforeRouteUpdate, onBeforeRouteLeave, NavigationGuard } from 'vue-router';
|
||||
import { useRouter } from 'vue-router';
|
||||
import RevisionsDrawerDetail from '@/views/private/components/revisions-drawer-detail';
|
||||
import CommentsSidebarDetail from '@/views/private/components/comments-sidebar-detail';
|
||||
import useItem from '@/composables/use-item';
|
||||
@@ -198,7 +198,7 @@ import ReplaceFile from '../components/replace-file.vue';
|
||||
import { usePermissions } from '@/composables/use-permissions';
|
||||
import { notify } from '@/utils/notify';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
import unsavedChanges from '@/composables/unsaved-changes';
|
||||
import useEditsGuard from '@/composables/use-edits-guard';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'FilesItem',
|
||||
@@ -249,7 +249,9 @@ export default defineComponent({
|
||||
validationErrors,
|
||||
} = useItem(ref('directus_files'), primaryKey);
|
||||
|
||||
unsavedChanges(hasEdits);
|
||||
const isSavable = computed(() => saveAllowed.value && hasEdits.value);
|
||||
|
||||
const { confirmLeave, leaveTo } = useEditsGuard(isSavable);
|
||||
|
||||
const confirmDelete = ref(false);
|
||||
const editActive = ref(false);
|
||||
@@ -285,23 +287,10 @@ export default defineComponent({
|
||||
else return '/files';
|
||||
});
|
||||
|
||||
const confirmLeave = ref(false);
|
||||
const leaveTo = ref<string | null>(null);
|
||||
|
||||
const { moveToDialogActive, moveToFolder, moving, selectedFolder } = useMovetoFolder();
|
||||
|
||||
useShortcut('meta+s', saveAndStay, form);
|
||||
|
||||
const editsGuard: NavigationGuard = (to) => {
|
||||
if (hasEdits.value) {
|
||||
confirmLeave.value = true;
|
||||
leaveTo.value = to.fullPath;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
onBeforeRouteUpdate(editsGuard);
|
||||
onBeforeRouteLeave(editsGuard);
|
||||
|
||||
const { deleteAllowed, saveAllowed, updateAllowed, fields, revisionsAllowed } = usePermissions(
|
||||
ref('directus_files'),
|
||||
item,
|
||||
@@ -353,6 +342,7 @@ export default defineComponent({
|
||||
fieldsFiltered,
|
||||
revisionsAllowed,
|
||||
validationErrors,
|
||||
isSavable,
|
||||
};
|
||||
|
||||
function useBreadcrumb() {
|
||||
|
||||
@@ -106,10 +106,10 @@ import { useCollection } from '@directus/shared/composables';
|
||||
import FieldsManagement from './components/fields-management.vue';
|
||||
|
||||
import useItem from '@/composables/use-item';
|
||||
import { useRouter, onBeforeRouteUpdate, onBeforeRouteLeave, NavigationGuard } from 'vue-router';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useCollectionsStore, useFieldsStore } from '@/stores';
|
||||
import useShortcut from '@/composables/use-shortcut';
|
||||
import unsavedChanges from '@/composables/unsaved-changes';
|
||||
import useEditsGuard from '@/composables/use-edits-guard';
|
||||
|
||||
export default defineComponent({
|
||||
components: { SettingsNavigation, FieldsManagement },
|
||||
@@ -160,20 +160,7 @@ export default defineComponent({
|
||||
return hasEdits.value;
|
||||
});
|
||||
|
||||
unsavedChanges(isSavable);
|
||||
|
||||
const confirmLeave = ref(false);
|
||||
const leaveTo = ref<string | null>(null);
|
||||
|
||||
const editsGuard: NavigationGuard = (to) => {
|
||||
if (hasEdits.value) {
|
||||
confirmLeave.value = true;
|
||||
leaveTo.value = to.fullPath;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
onBeforeRouteUpdate(editsGuard);
|
||||
onBeforeRouteLeave(editsGuard);
|
||||
const { confirmLeave, leaveTo } = useEditsGuard(isSavable);
|
||||
|
||||
return {
|
||||
t,
|
||||
|
||||
@@ -141,11 +141,11 @@ import { Preset, Filter } from '@directus/shared/types';
|
||||
import api from '@/api';
|
||||
import { useCollectionsStore, usePresetsStore } from '@/stores';
|
||||
import { getLayouts } from '@/layouts';
|
||||
import { useRouter, onBeforeRouteUpdate, onBeforeRouteLeave, NavigationGuard } from 'vue-router';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
import { useLayout } from '@directus/shared/composables';
|
||||
import useShortcut from '@/composables/use-shortcut';
|
||||
import unsavedChanges from '@/composables/unsaved-changes';
|
||||
import useEditsGuard from '@/composables/use-edits-guard';
|
||||
import { isEqual } from 'lodash';
|
||||
|
||||
type FormattedPreset = {
|
||||
@@ -210,20 +210,7 @@ export default defineComponent({
|
||||
return hasEdits.value;
|
||||
});
|
||||
|
||||
unsavedChanges(isSavable);
|
||||
|
||||
const confirmLeave = ref(false);
|
||||
const leaveTo = ref<string | null>(null);
|
||||
|
||||
const editsGuard: NavigationGuard = (to) => {
|
||||
if (hasEdits.value) {
|
||||
confirmLeave.value = true;
|
||||
leaveTo.value = to.fullPath;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
onBeforeRouteUpdate(editsGuard);
|
||||
onBeforeRouteLeave(editsGuard);
|
||||
const { confirmLeave, leaveTo } = useEditsGuard(isSavable);
|
||||
|
||||
return {
|
||||
t,
|
||||
|
||||
@@ -49,8 +49,8 @@ import { useSettingsStore, useServerStore } from '@/stores';
|
||||
import ProjectInfoSidebarDetail from './components/project-info-sidebar-detail.vue';
|
||||
import { clone } from 'lodash';
|
||||
import useShortcut from '@/composables/use-shortcut';
|
||||
import unsavedChanges from '@/composables/unsaved-changes';
|
||||
import { useRouter, onBeforeRouteUpdate, onBeforeRouteLeave, NavigationGuard } from 'vue-router';
|
||||
import useEditsGuard from '@/composables/use-edits-guard';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
export default defineComponent({
|
||||
components: { SettingsNavigation, ProjectInfoSidebarDetail },
|
||||
@@ -81,20 +81,7 @@ export default defineComponent({
|
||||
return noEdits.value;
|
||||
});
|
||||
|
||||
unsavedChanges(isSavable);
|
||||
|
||||
const confirmLeave = ref(false);
|
||||
const leaveTo = ref<string | null>(null);
|
||||
|
||||
const editsGuard: NavigationGuard = (to) => {
|
||||
if (!noEdits.value) {
|
||||
confirmLeave.value = true;
|
||||
leaveTo.value = to.fullPath;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
onBeforeRouteUpdate(editsGuard);
|
||||
onBeforeRouteLeave(editsGuard);
|
||||
const { confirmLeave, leaveTo } = useEditsGuard(isSavable);
|
||||
|
||||
return {
|
||||
t,
|
||||
|
||||
@@ -107,7 +107,7 @@ import { useI18n } from 'vue-i18n';
|
||||
import { defineComponent, computed, toRefs, ref } from 'vue';
|
||||
|
||||
import SettingsNavigation from '../../../components/navigation.vue';
|
||||
import { useRouter, onBeforeRouteUpdate, onBeforeRouteLeave, NavigationGuard } from 'vue-router';
|
||||
import { useRouter } from 'vue-router';
|
||||
import RevisionsDrawerDetail from '@/views/private/components/revisions-drawer-detail';
|
||||
import useItem from '@/composables/use-item';
|
||||
import { useUserStore } from '@/stores/';
|
||||
@@ -115,7 +115,7 @@ import RoleInfoSidebarDetail from './components/role-info-sidebar-detail.vue';
|
||||
import PermissionsOverview from './components/permissions-overview.vue';
|
||||
import UsersInvite from '@/views/private/components/users-invite';
|
||||
import useShortcut from '@/composables/use-shortcut';
|
||||
import unsavedChanges from '@/composables/unsaved-changes';
|
||||
import useEditsGuard from '@/composables/use-edits-guard';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'RolesItem',
|
||||
@@ -173,20 +173,7 @@ export default defineComponent({
|
||||
return hasEdits.value;
|
||||
});
|
||||
|
||||
unsavedChanges(isSavable);
|
||||
|
||||
const confirmLeave = ref(false);
|
||||
const leaveTo = ref<string | null>(null);
|
||||
|
||||
const editsGuard: NavigationGuard = (to) => {
|
||||
if (hasEdits.value) {
|
||||
confirmLeave.value = true;
|
||||
leaveTo.value = to.fullPath;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
onBeforeRouteUpdate(editsGuard);
|
||||
onBeforeRouteLeave(editsGuard);
|
||||
const { confirmLeave, leaveTo } = useEditsGuard(isSavable);
|
||||
|
||||
return {
|
||||
t,
|
||||
|
||||
@@ -88,12 +88,12 @@ import { useI18n } from 'vue-i18n';
|
||||
import { defineComponent, computed, toRefs, ref } from 'vue';
|
||||
|
||||
import SettingsNavigation from '../../components/navigation.vue';
|
||||
import { useRouter, onBeforeRouteUpdate, onBeforeRouteLeave, NavigationGuard } from 'vue-router';
|
||||
import { useRouter } from 'vue-router';
|
||||
import RevisionsDrawerDetail from '@/views/private/components/revisions-drawer-detail';
|
||||
import useItem from '@/composables/use-item';
|
||||
import SaveOptions from '@/views/private/components/save-options';
|
||||
import useShortcut from '@/composables/use-shortcut';
|
||||
import unsavedChanges from '@/composables/unsaved-changes';
|
||||
import useEditsGuard from '@/composables/use-edits-guard';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'WebhooksItem',
|
||||
@@ -148,20 +148,7 @@ export default defineComponent({
|
||||
return hasEdits.value;
|
||||
});
|
||||
|
||||
unsavedChanges(isSavable);
|
||||
|
||||
const confirmLeave = ref(false);
|
||||
const leaveTo = ref<string | null>(null);
|
||||
|
||||
const editsGuard: NavigationGuard = (to) => {
|
||||
if (hasEdits.value) {
|
||||
confirmLeave.value = true;
|
||||
leaveTo.value = to.fullPath;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
onBeforeRouteUpdate(editsGuard);
|
||||
onBeforeRouteLeave(editsGuard);
|
||||
const { confirmLeave, leaveTo } = useEditsGuard(isSavable);
|
||||
|
||||
return {
|
||||
t,
|
||||
|
||||
@@ -74,11 +74,11 @@
|
||||
</v-dialog>
|
||||
|
||||
<v-button
|
||||
v-tooltip.bottom="saveAllowed ? t('save') : t('not_allowed')"
|
||||
v-tooltip.bottom="isSavable ? t('save') : t('not_allowed')"
|
||||
rounded
|
||||
icon
|
||||
:loading="saving"
|
||||
:disabled="hasEdits === false || saveAllowed === false"
|
||||
:disabled="!isSavable"
|
||||
@click="saveAndQuit"
|
||||
>
|
||||
<v-icon name="check" />
|
||||
@@ -185,7 +185,7 @@ import { defineComponent, computed, toRefs, ref, watch, ComponentPublicInstance
|
||||
|
||||
import UsersNavigation from '../components/navigation.vue';
|
||||
import { setLanguage } from '@/lang/set-language';
|
||||
import { useRouter, onBeforeRouteUpdate, onBeforeRouteLeave, NavigationGuard } from 'vue-router';
|
||||
import { useRouter } from 'vue-router';
|
||||
import RevisionsDrawerDetail from '@/views/private/components/revisions-drawer-detail';
|
||||
import CommentsSidebarDetail from '@/views/private/components/comments-sidebar-detail';
|
||||
import useItem from '@/composables/use-item';
|
||||
@@ -203,7 +203,7 @@ import { usePermissions } from '@/composables/use-permissions';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
import { addTokenToURL } from '@/api';
|
||||
import { useUserStore } from '@/stores';
|
||||
import unsavedChanges from '@/composables/unsaved-changes';
|
||||
import useEditsGuard from '@/composables/use-edits-guard';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'UsersItem',
|
||||
@@ -260,7 +260,9 @@ export default defineComponent({
|
||||
};
|
||||
}
|
||||
|
||||
unsavedChanges(hasEdits);
|
||||
const isSavable = computed(() => saveAllowed.value && hasEdits.value);
|
||||
|
||||
const { confirmLeave, leaveTo } = useEditsGuard(isSavable);
|
||||
|
||||
const confirmDelete = ref(false);
|
||||
const confirmArchive = ref(false);
|
||||
@@ -280,19 +282,6 @@ export default defineComponent({
|
||||
|
||||
const { loading: previewLoading, avatarSrc, roleName } = useUserPreview();
|
||||
|
||||
const confirmLeave = ref(false);
|
||||
const leaveTo = ref<string | null>(null);
|
||||
|
||||
const editsGuard: NavigationGuard = (to) => {
|
||||
if (hasEdits.value) {
|
||||
confirmLeave.value = true;
|
||||
leaveTo.value = to.fullPath;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
onBeforeRouteUpdate(editsGuard);
|
||||
onBeforeRouteLeave(editsGuard);
|
||||
|
||||
const { deleteAllowed, archiveAllowed, saveAllowed, updateAllowed, revisionsAllowed, fields } = usePermissions(
|
||||
ref('directus_users'),
|
||||
item,
|
||||
@@ -376,6 +365,7 @@ export default defineComponent({
|
||||
validationErrors,
|
||||
revert,
|
||||
avatarError,
|
||||
isSavable,
|
||||
};
|
||||
|
||||
function useBreadcrumb() {
|
||||
|
||||
Reference in New Issue
Block a user