Skip to content

Commit 28d7f26

Browse files
committed
update: address more comments, handle unsaved changes with a nicer modal for SPA navigation.
1 parent 715cc89 commit 28d7f26

3 files changed

Lines changed: 85 additions & 20 deletions

File tree

src/lib/helpers/unsavedChanges.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
1-
import { beforeNavigate } from '$app/navigation';
1+
import { beforeNavigate, goto } from '$app/navigation';
22
import type { BeforeNavigate } from '@sveltejs/kit';
33

44
type UnsavedChangesGuardOptions = {
55
message?: string;
66
hasUnsavedChanges: () => boolean;
77
onConfirmNavigate?: () => void;
88
shouldBlockNavigation?: (navigation: BeforeNavigate) => boolean;
9+
onShowConfirmModal?: (url: string, onConfirm: () => void | Promise<void>) => void;
910
};
1011

1112
export const setupUnsavedChangesGuard = ({
1213
message,
1314
hasUnsavedChanges,
1415
onConfirmNavigate,
15-
shouldBlockNavigation
16+
shouldBlockNavigation,
17+
onShowConfirmModal
1618
}: UnsavedChangesGuardOptions) => {
1719
message = message ?? 'You have unsaved changes. Are you sure you want to leave?';
1820

@@ -27,6 +29,22 @@ export const setupUnsavedChangesGuard = ({
2729
if (!hasUnsavedChanges()) return;
2830
if (shouldBlockNavigation && !shouldBlockNavigation(navigation)) return;
2931

32+
// If custom modal handler is provided, use it
33+
if (onShowConfirmModal && navigation.to?.url) {
34+
navigation.cancel();
35+
const targetUrl = navigation.to.url.href;
36+
const handleConfirm = async () => {
37+
onConfirmNavigate?.();
38+
39+
// eslint-disable-next-line
40+
await goto(targetUrl);
41+
};
42+
43+
onShowConfirmModal(targetUrl, handleConfirm);
44+
return;
45+
}
46+
47+
// Fallback to native confirm dialog
3048
if (!confirm(message)) {
3149
navigation.cancel();
3250
return;

src/lib/layout/progress.svelte

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@
7474
7575
let progressBarStartTimeout: ReturnType<typeof setTimeout> | null = null;
7676
beforeNavigate((nav) => {
77+
if (running) {
78+
complete();
79+
}
80+
7781
if (progressBarStartTimeout) {
7882
clearTimeout(progressBarStartTimeout);
7983
progressBarStartTimeout = null;

src/routes/(console)/project-[region]-[project]/databases/database-[database]/collection-[collection]/spreadsheet.svelte

Lines changed: 61 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,9 @@
104104
let showDelete = false;
105105
let selectedDocumentForDelete: Models.Document['$id'] | null = null;
106106
107+
let showUnsavedChangesModal = false;
108+
let confirmNavigation: (() => void | Promise<void>) | null = null;
109+
107110
async function loadRemoteDocument() {
108111
try {
109112
noSqlDocument.update({ show: true, loading: true });
@@ -566,6 +569,10 @@
566569
shouldBlockNavigation: (navigation) => {
567570
const nextPath = navigation.to?.url?.pathname;
568571
return Boolean(nextPath && nextPath !== page.url.pathname);
572+
},
573+
onShowConfirmModal: (_, onConfirm) => {
574+
confirmNavigation = onConfirm;
575+
showUnsavedChangesModal = true;
569576
}
570577
});
571578
</script>
@@ -861,24 +868,60 @@
861868
{/if}
862869
</SpreadsheetContainer>
863870

864-
<Confirm
865-
confirmDeletion
866-
bind:open={showDelete}
867-
onSubmit={handleDelete}
868-
title={selectedDocuments.length === 1 ? 'Delete document' : 'Delete documents'}>
869-
{@const isSingle = selectedDocumentForDelete !== null}
870-
871-
<p>
872-
{#if isSingle}
873-
Are you sure you want to delete this document from <b>{collection.name}</b>?
874-
{:else}
875-
Are you sure you want to delete <b>{selectedDocuments.length}</b>
876-
{selectedDocuments.length > 1 ? 'documents' : 'document'} from <b>{collection.name}</b>?
877-
{/if}
878-
</p>
879-
880-
<p class="u-bold">This action is irreversible.</p>
881-
</Confirm>
871+
{#if showDelete}
872+
<Confirm
873+
confirmDeletion
874+
bind:open={showDelete}
875+
onSubmit={handleDelete}
876+
title={selectedDocuments.length === 1 ? 'Delete document' : 'Delete documents'}>
877+
{@const isSingle = selectedDocumentForDelete !== null}
878+
879+
<p>
880+
{#if isSingle}
881+
Are you sure you want to delete this document from <b>{collection.name}</b>?
882+
{:else}
883+
Are you sure you want to delete <b>{selectedDocuments.length}</b>
884+
{selectedDocuments.length > 1 ? 'documents' : 'document'} from
885+
<b>{collection.name}</b>?
886+
{/if}
887+
</p>
888+
889+
<p class="u-bold">This action is irreversible.</p>
890+
</Confirm>
891+
{/if}
892+
893+
{#if showUnsavedChangesModal}
894+
<Confirm
895+
bind:open={showUnsavedChangesModal}
896+
title="Unsaved changes"
897+
onSubmit={(e) => {
898+
e.preventDefault();
899+
confirmNavigation?.();
900+
showUnsavedChangesModal = false;
901+
}}>
902+
<svelte:fragment slot="footer">
903+
<Button.Button
904+
size="s"
905+
variant="text"
906+
on:click={() => {
907+
confirmNavigation = null;
908+
showUnsavedChangesModal = false;
909+
}}>
910+
Keep editing
911+
</Button.Button>
912+
913+
<Button.Button
914+
size="s"
915+
variant="secondary"
916+
on:click={() => {
917+
confirmNavigation?.();
918+
showUnsavedChangesModal = false;
919+
}}>Discard changes</Button.Button>
920+
</svelte:fragment>
921+
922+
<p>You have unsaved changes that haven't been saved.</p>
923+
</Confirm>
924+
{/if}
882925

883926
<style lang="scss">
884927
.floating-action-bar {

0 commit comments

Comments
 (0)