sprintify-ui 0.11.5 → 0.11.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -66,6 +66,13 @@ declare const __VLS_component: import("vue").DefineComponent<import("vue").Extra
66
66
  default: boolean;
67
67
  type: BooleanConstructor;
68
68
  };
69
+ /**
70
+ * Show a browser warning when leaving the page with unsaved changes
71
+ */
72
+ showLeavePageWarning: {
73
+ default: boolean;
74
+ type: BooleanConstructor;
75
+ };
69
76
  /**
70
77
  * Autosave the form every time a input is changed
71
78
  */
@@ -134,6 +141,13 @@ declare const __VLS_component: import("vue").DefineComponent<import("vue").Extra
134
141
  default: boolean;
135
142
  type: BooleanConstructor;
136
143
  };
144
+ /**
145
+ * Show a browser warning when leaving the page with unsaved changes
146
+ */
147
+ showLeavePageWarning: {
148
+ default: boolean;
149
+ type: BooleanConstructor;
150
+ };
137
151
  /**
138
152
  * Autosave the form every time a input is changed
139
153
  */
@@ -155,6 +169,7 @@ declare const __VLS_component: import("vue").DefineComponent<import("vue").Extra
155
169
  showLoadingMask: boolean;
156
170
  showNotificationOnError: boolean;
157
171
  showNotificationOnSuccess: boolean;
172
+ showLeavePageWarning: boolean;
158
173
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
159
174
  declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
160
175
  export default _default;
@@ -50,6 +50,7 @@ declare const messages: {
50
50
  go_to_page: string;
51
51
  invalid_value: string;
52
52
  just_now: string;
53
+ leave_page_warning: string;
53
54
  maximum_x_decimal_places: string;
54
55
  min_x_characters: string;
55
56
  month: string;
@@ -151,6 +152,7 @@ declare const messages: {
151
152
  go_to_page: string;
152
153
  invalid_value: string;
153
154
  just_now: string;
155
+ leave_page_warning: string;
154
156
  maximum_x_decimal_places: string;
155
157
  min_x_characters: string;
156
158
  month: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sprintify-ui",
3
- "version": "0.11.5",
3
+ "version": "0.11.6",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "build": "rimraf dist && vue-tsc && vite build",
@@ -19,6 +19,7 @@ It also handles errors gracefully—if you're using **BaseFields** (or component
19
19
  - **Sending Requests:** The component can be bound to an `axiosInstance`, which it uses to send POST, PUT, PATCH, or any other method requests to a specified `url`.
20
20
  - **Formatting JSON Payload:** By default, the form data is sent as JSON, but you can switch to `formData` by adjusting the `format` prop.
21
21
  - **Loading State:** A loading spinner can be displayed while the form is submitting. This spinner can have custom Tailwind CSS classes via the `twLoadingMask` prop, and it can be toggled on/off using `showLoadingMask`.
22
+ - **Leave Warning (Opt-in):** When `showLeavePageWarning` is enabled, the component can show a native browser warning before leaving the page if form data has changed and is not saved yet.
22
23
  - **Error and Success Handling:**
23
24
  - The component can display error messages next to fields (if those fields are wrapped in a **BaseField** and `name` prop is set and equivalent to the server response).
24
25
  - It can also show a global success or error notification, which is controlled by `showNotificationOnSuccess` and `showNotificationOnError`.
@@ -85,4 +86,4 @@ In the example below, the form will automatically save after a 400ms delay whene
85
86
 
86
87
  ## API
87
88
 
88
- <Controls />
89
+ <Controls />
@@ -51,7 +51,7 @@ import { serialize } from 'object-to-formdata';
51
51
  import { Method, DataFormat } from '@/types';
52
52
  import { AxiosError, AxiosInstance, AxiosResponse } from 'axios';
53
53
  import { config, useSnackbarsStore } from '@/index';
54
- import { debounce, get, isArray } from 'lodash';
54
+ import { cloneDeep, debounce, get, isArray, isEqual } from 'lodash';
55
55
  import { t } from '@/i18n';
56
56
  import { twMerge } from 'tailwind-merge';
57
57
 
@@ -118,6 +118,13 @@ const props = defineProps({
118
118
  default: true,
119
119
  type: Boolean,
120
120
  },
121
+ /**
122
+ * Show a browser warning when leaving the page with unsaved changes
123
+ */
124
+ showLeavePageWarning: {
125
+ default: false,
126
+ type: Boolean,
127
+ },
121
128
  /**
122
129
  * Autosave the form every time a input is changed
123
130
  */
@@ -132,6 +139,8 @@ const emit = defineEmits(['error', 'success']);
132
139
  const form = ref<null | HTMLFormElement>(null);
133
140
  const loading = ref(false);
134
141
  const errors = ref<Record<string, string[]>>({});
142
+ const dataSnapshot = ref<Record<string, any>>(cloneDeep(props.data));
143
+ const hasUnsavedChanges = ref(false);
135
144
 
136
145
  const httpClient = computed((): AxiosInstance => {
137
146
  if (props.axiosInstance) {
@@ -235,6 +244,7 @@ async function query() {
235
244
  clearErrors();
236
245
 
237
246
  successHandler(response);
247
+ syncDataSnapshot();
238
248
 
239
249
  emit('success', response);
240
250
  })
@@ -322,6 +332,50 @@ function clearErrors(name = null): void {
322
332
  }
323
333
  }
324
334
 
335
+ function syncDataSnapshot(): void {
336
+ dataSnapshot.value = cloneDeep(props.data);
337
+ hasUnsavedChanges.value = false;
338
+ }
339
+
340
+ function handleBeforeUnload(event: BeforeUnloadEvent): string | undefined {
341
+ if (!props.showLeavePageWarning || !hasUnsavedChanges.value) {
342
+ return;
343
+ }
344
+
345
+ const warningMessage = t('sui.leave_page_warning');
346
+
347
+ event.preventDefault();
348
+ event.returnValue = warningMessage;
349
+
350
+ return warningMessage;
351
+ }
352
+
353
+ watch(
354
+ () => props.data,
355
+ (value) => {
356
+ hasUnsavedChanges.value = !isEqual(value, dataSnapshot.value);
357
+ },
358
+ {
359
+ deep: true,
360
+ }
361
+ );
362
+
363
+ watch(() => props.data, syncDataSnapshot);
364
+
365
+ onMounted(() => {
366
+ syncDataSnapshot();
367
+
368
+ if (typeof window !== 'undefined') {
369
+ window.addEventListener('beforeunload', handleBeforeUnload);
370
+ }
371
+ });
372
+
373
+ onUnmounted(() => {
374
+ if (typeof window !== 'undefined') {
375
+ window.removeEventListener('beforeunload', handleBeforeUnload);
376
+ }
377
+ });
378
+
325
379
  const disablingFields = reactive(new Set<string>());
326
380
 
327
381
  function disabledForm(uuid: string) {
package/src/lang/en.json CHANGED
@@ -42,6 +42,7 @@
42
42
  "go_to_page": "Go to ",
43
43
  "invalid_value": "Invalid value",
44
44
  "just_now": "Just now",
45
+ "leave_page_warning": "Changes you made may not be saved.",
45
46
  "maximum_x_decimal_places": "Maximum 1 decimal place|Maximum {count} decimal places",
46
47
  "min_x_characters": "{x} characters minimum",
47
48
  "month": "Month",
package/src/lang/fr.json CHANGED
@@ -42,6 +42,7 @@
42
42
  "go_to_page": "Page",
43
43
  "invalid_value": "Valeur invalide",
44
44
  "just_now": "à l’instant",
45
+ "leave_page_warning": "Les modifications que vous avez apportées pourraient ne pas être enregistrées.",
45
46
  "maximum_x_decimal_places": "Maximum 1 décimale|Maximum {count} décimales",
46
47
  "min_x_characters": "{x} caractères minimum",
47
48
  "month": "Mois",