quasar-ui-danx 0.4.2 → 0.4.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. package/dist/danx.es.js +7127 -6615
  2. package/dist/danx.es.js.map +1 -1
  3. package/dist/danx.umd.js +11 -5
  4. package/dist/danx.umd.js.map +1 -1
  5. package/dist/style.css +1 -1
  6. package/package.json +3 -1
  7. package/src/components/ActionTable/ActionTable.vue +28 -41
  8. package/src/components/ActionTable/Columns/ActionTableColumn.vue +19 -18
  9. package/src/components/ActionTable/Filters/CollapsableFiltersSidebar.vue +6 -6
  10. package/src/components/ActionTable/Filters/{FilterFieldList.vue → FilterList.vue} +26 -26
  11. package/src/components/ActionTable/Filters/FilterableField.vue +28 -31
  12. package/src/components/ActionTable/Filters/index.ts +2 -2
  13. package/src/components/ActionTable/Form/Fields/EditOnClickTextField.vue +71 -0
  14. package/src/components/ActionTable/Form/Fields/FieldLabel.vue +8 -13
  15. package/src/components/ActionTable/Form/Fields/MultiFileField.vue +48 -44
  16. package/src/components/ActionTable/Form/Fields/SelectField.vue +24 -38
  17. package/src/components/ActionTable/Form/Fields/SelectWithChildrenField.vue +28 -33
  18. package/src/components/ActionTable/Form/Fields/SingleFileField.vue +15 -15
  19. package/src/components/ActionTable/Form/Fields/SliderNumberField.vue +45 -0
  20. package/src/components/ActionTable/Form/Fields/TextField.vue +47 -66
  21. package/src/components/ActionTable/Form/Fields/index.ts +2 -0
  22. package/src/components/ActionTable/Form/RenderedForm.vue +50 -9
  23. package/src/components/ActionTable/Form/Utilities/MaxLengthCounter.vue +17 -0
  24. package/src/components/ActionTable/Form/Utilities/index.ts +1 -0
  25. package/src/components/ActionTable/Form/index.ts +1 -0
  26. package/src/components/ActionTable/Layouts/ActionTableLayout.vue +16 -15
  27. package/src/components/ActionTable/Toolbars/ActionToolbar.vue +6 -6
  28. package/src/components/ActionTable/listControls.ts +106 -166
  29. package/src/components/ActionTable/listHelpers.ts +2 -3
  30. package/src/components/ActionTable/tableColumns.ts +3 -27
  31. package/src/components/AuditHistory/AuditHistoryItemValue.vue +26 -26
  32. package/src/components/PanelsDrawer/PanelsDrawer.vue +17 -4
  33. package/src/components/PanelsDrawer/PanelsDrawerPanels.vue +6 -11
  34. package/src/components/PanelsDrawer/PanelsDrawerTabs.vue +20 -20
  35. package/src/components/Utility/Dialogs/ConfirmActionDialog.vue +39 -0
  36. package/src/components/Utility/Dialogs/ConfirmDialog.vue +10 -24
  37. package/src/components/Utility/Dialogs/DialogLayout.vue +10 -28
  38. package/src/components/Utility/Dialogs/FullscreenCarouselDialog.vue +42 -36
  39. package/src/components/Utility/Dialogs/index.ts +1 -0
  40. package/src/components/Utility/Files/FilePreview.vue +76 -73
  41. package/src/components/Utility/Layouts/ContentDrawer.vue +24 -31
  42. package/src/components/Utility/Tools/ActionVnode.vue +3 -3
  43. package/src/components/Utility/Tools/RenderVnode.vue +1 -1
  44. package/src/components/Utility/Transitions/MaxHeightTransition.vue +26 -0
  45. package/src/components/Utility/Transitions/index.ts +1 -0
  46. package/src/config/index.ts +36 -31
  47. package/src/helpers/FileUpload.ts +295 -297
  48. package/src/helpers/FlashMessages.ts +80 -71
  49. package/src/helpers/actions.ts +102 -82
  50. package/src/helpers/download.ts +189 -189
  51. package/src/helpers/downloadPdf.ts +55 -52
  52. package/src/helpers/formats.ts +151 -109
  53. package/src/helpers/index.ts +2 -0
  54. package/src/helpers/multiFileUpload.ts +72 -58
  55. package/src/helpers/objectStore.ts +52 -0
  56. package/src/helpers/request.ts +70 -51
  57. package/src/helpers/routes.ts +29 -0
  58. package/src/helpers/storage.ts +7 -3
  59. package/src/helpers/utils.ts +47 -29
  60. package/src/styles/quasar-reset.scss +16 -1
  61. package/src/styles/themes/danx/dialogs.scss +4 -0
  62. package/src/types/actions.d.ts +43 -0
  63. package/src/types/config.d.ts +15 -0
  64. package/src/types/controls.d.ts +99 -0
  65. package/src/types/dialogs.d.ts +32 -0
  66. package/src/types/fields.d.ts +20 -0
  67. package/src/types/files.d.ts +54 -0
  68. package/src/types/formats.d.ts +4 -0
  69. package/src/{components/ActionTable/Form/form.d.ts → types/forms.d.ts} +6 -0
  70. package/src/types/index.d.ts +12 -0
  71. package/src/types/requests.d.ts +13 -0
  72. package/src/types/shared.d.ts +15 -0
  73. package/src/types/tables.d.ts +27 -0
  74. package/types/index.d.ts +1 -1
  75. /package/src/components/ActionTable/Filters/{FilterFieldItem.vue → FilterItem.vue} +0 -0
@@ -1,16 +1,20 @@
1
1
  import { DateTime, IANAZone } from "luxon";
2
+ import { ActionTargetItem, fDateOptions } from "../types";
3
+ import { isJSON } from "./utils";
2
4
 
3
5
  const SERVER_TZ = new IANAZone("America/Chicago");
4
6
 
7
+ export { DateTime, SERVER_TZ };
8
+
5
9
  /**
6
10
  * Converts a date string from the server's time zone to the user's time zone.
7
11
  * @param {String} dateTimeString
8
12
  * @returns {DateTime}
9
13
  */
10
- export function localizedDateTime(dateTimeString) {
11
- dateTimeString = dateTimeString?.replace("T", " ");
12
- // noinspection JSCheckFunctionSignatures
13
- return DateTime.fromSQL(dateTimeString, { zone: SERVER_TZ }).setZone("local");
14
+ export function localizedDateTime(dateTimeString: string) {
15
+ dateTimeString = dateTimeString?.replace("T", " ");
16
+ // noinspection JSCheckFunctionSignatures
17
+ return DateTime.fromSQL(dateTimeString, { zone: SERVER_TZ }).setZone("local");
14
18
  }
15
19
 
16
20
  /**
@@ -18,22 +22,22 @@ export function localizedDateTime(dateTimeString) {
18
22
  * @param dateTimeString
19
23
  * @returns {DateTime}
20
24
  */
21
- export function remoteDateTime(dateTimeString) {
22
- dateTimeString = dateTimeString?.replace("T", " ");
23
- // noinspection JSCheckFunctionSignatures
24
- return DateTime.fromSQL(dateTimeString, { zone: "local" }).setZone(SERVER_TZ);
25
+ export function remoteDateTime(dateTimeString: string) {
26
+ dateTimeString = dateTimeString?.replace("T", " ");
27
+ // noinspection JSCheckFunctionSignatures
28
+ return DateTime.fromSQL(dateTimeString, { zone: "local" }).setZone(SERVER_TZ);
25
29
  }
26
30
 
27
31
  /**
28
32
  * @param {DateTime|String} dateTime
29
33
  * @returns {DateTime|*}
30
34
  */
31
- export function parseDateTime(dateTime) {
32
- if (typeof dateTime === "string") {
33
- dateTime = dateTime.replace("T", " ").replace(/\//g, "-");
34
- return DateTime.fromSQL(dateTime);
35
- }
36
- return dateTime || DateTime.fromSQL("0000-00-00 00:00:00");
35
+ export function parseDateTime(dateTime: string | DateTime) {
36
+ if (typeof dateTime === "string") {
37
+ dateTime = dateTime.replace("T", " ").replace(/\//g, "-");
38
+ return DateTime.fromSQL(dateTime);
39
+ }
40
+ return dateTime || DateTime.fromSQL("0000-00-00 00:00:00");
37
41
  }
38
42
 
39
43
  /**
@@ -42,8 +46,8 @@ export function parseDateTime(dateTime) {
42
46
  * @param format
43
47
  * @returns {DateTime}
44
48
  */
45
- export function parseQDate(date, format = "yyyy/MM/dd") {
46
- return DateTime.fromFormat(date, format);
49
+ export function parseQDate(date: string, format = "yyyy/MM/dd") {
50
+ return DateTime.fromFormat(date, format);
47
51
  }
48
52
 
49
53
  /**
@@ -52,8 +56,8 @@ export function parseQDate(date, format = "yyyy/MM/dd") {
52
56
  * @param format
53
57
  * @returns {DateTime}
54
58
  */
55
- export function parseQDateTime(date, format = "yyyy/MM/dd HH:mm:ss") {
56
- return DateTime.fromFormat(date, format);
59
+ export function parseQDateTime(date: string, format = "yyyy/MM/dd HH:mm:ss") {
60
+ return DateTime.fromFormat(date, format);
57
61
  }
58
62
 
59
63
  /**
@@ -61,8 +65,8 @@ export function parseQDateTime(date, format = "yyyy/MM/dd HH:mm:ss") {
61
65
  * @param date
62
66
  * @returns {string}
63
67
  */
64
- export function fQDate(date) {
65
- return fDate(date, { format: "yyyy/MM/dd" });
68
+ export function fQDate(date: string) {
69
+ return fDate(date, { format: "yyyy/MM/dd" });
66
70
  }
67
71
 
68
72
  /**
@@ -71,8 +75,8 @@ export function fQDate(date) {
71
75
  * @param options
72
76
  * @returns {string}
73
77
  */
74
- export function fLocalizedDateTime(dateTimeString, options = {}) {
75
- return fDateTime(localizedDateTime(dateTimeString), options);
78
+ export function fLocalizedDateTime(dateTimeString: string, options = {}) {
79
+ return fDateTime(localizedDateTime(dateTimeString), options);
76
80
  }
77
81
 
78
82
  /**
@@ -84,11 +88,11 @@ export function fLocalizedDateTime(dateTimeString, options = {}) {
84
88
  * @returns {string}
85
89
  */
86
90
  export function fDateTime(
87
- dateTime = null,
88
- { format = "M/d/yy h:mma", empty = "- -" } = {}
91
+ dateTime: string | DateTime | null = null,
92
+ { format = "M/d/yy h:mma", empty = "- -" }: fDateOptions = {}
89
93
  ) {
90
- const formatted = (dateTime ? parseDateTime(dateTime) : DateTime.now()).toFormat(format).toLowerCase();
91
- return formatted === "invalid datetime" ? empty : formatted;
94
+ const formatted = (dateTime ? parseDateTime(dateTime) : DateTime.now()).toFormat(format).toLowerCase();
95
+ return formatted === "invalid datetime" ? empty : formatted;
92
96
  }
93
97
 
94
98
  /**
@@ -96,8 +100,8 @@ export function fDateTime(
96
100
  * @param dateTime
97
101
  * @returns {string}
98
102
  */
99
- export function dbDateTime(dateTime = null) {
100
- return fDateTime(dateTime, { format: "yyyy-MM-dd HH:mm:ss", empty: null });
103
+ export function dbDateTime(dateTime: string | DateTime | null = null) {
104
+ return fDateTime(dateTime, { format: "yyyy-MM-dd HH:mm:ss", empty: undefined });
101
105
  }
102
106
 
103
107
  /**
@@ -107,9 +111,9 @@ export function dbDateTime(dateTime = null) {
107
111
  * @param format
108
112
  * @returns {string}
109
113
  */
110
- export function fDate(dateTime, { empty = "--", format = "M/d/yy" } = {}) {
111
- const formatted = parseDateTime(dateTime).toFormat(format);
112
- return ["Invalid DateTime", "invalid datetime"].includes(formatted) ? empty : formatted;
114
+ export function fDate(dateTime: string, { empty = "--", format = "M/d/yy" }: fDateOptions = {}) {
115
+ const formatted = parseDateTime(dateTime).toFormat(format || "M/d/yy");
116
+ return ["Invalid DateTime", "invalid datetime"].includes(formatted) ? empty : formatted;
113
117
  }
114
118
 
115
119
  /**
@@ -118,22 +122,33 @@ export function fDate(dateTime, { empty = "--", format = "M/d/yy" } = {}) {
118
122
  * @param second
119
123
  * @returns {string}
120
124
  */
121
- export function fSecondsToTime(second) {
122
- const time = DateTime.now().setZone("UTC").startOf("year").set({ second });
123
- const hours = Math.floor(second / 3600);
124
- return (hours ? hours + ":" : "") + time.toFormat("mm:ss");
125
+ export function fSecondsToTime(second: number) {
126
+ const time = DateTime.now().setZone("UTC").startOf("year").set({ second });
127
+ const hours = Math.floor(second / 3600);
128
+ return (hours ? hours + ":" : "") + time.toFormat("mm:ss");
129
+ }
130
+
131
+ export function fElapsedTime(start: string, end?: string) {
132
+ const endDateTime = end ? parseDateTime(end) : DateTime.now();
133
+ const diff = endDateTime.diff(parseDateTime(start), ["hours", "minutes", "seconds"]);
134
+ if (!diff.isValid) {
135
+ return "-";
136
+ }
137
+ const hours = Math.floor(diff.hours);
138
+ const minutes = Math.floor(diff.minutes);
139
+ const seconds = Math.floor(diff.seconds);
140
+ return `${hours ? hours + "h " : ""}${minutes ? minutes + "m " : ""}${seconds}s`;
125
141
  }
126
142
 
127
143
  /**
128
144
  * Formats an amount into USD currency format
129
- * @param amount
130
- * @returns {string}
131
145
  */
132
- export function fCurrency(amount) {
133
- return new Intl.NumberFormat("en-US", {
134
- style: "currency",
135
- currency: "USD"
136
- }).format(amount);
146
+ export function fCurrency(amount: number, options?: object) {
147
+ return new Intl.NumberFormat("en-US", {
148
+ style: "currency",
149
+ currency: "USD",
150
+ ...options
151
+ }).format(amount);
137
152
  }
138
153
 
139
154
  /**
@@ -142,8 +157,8 @@ export function fCurrency(amount) {
142
157
  * @param options
143
158
  * @returns {string}
144
159
  */
145
- export function fNumber(number, options = {}) {
146
- return new Intl.NumberFormat("en-US", options).format(number);
160
+ export function fNumber(number: number, options = {}) {
161
+ return new Intl.NumberFormat("en-US", options).format(number);
147
162
  }
148
163
 
149
164
  /**
@@ -152,24 +167,24 @@ export function fNumber(number, options = {}) {
152
167
  * @param maxLength
153
168
  * @returns {string|*}
154
169
  */
155
- export function centerTruncate(str, maxLength) {
156
- if (str.length > maxLength) {
157
- const frontCharCount = Math.floor((maxLength - 3) / 2);
158
- const backCharCount = maxLength - frontCharCount - 3;
159
- return (
160
- str.substring(0, frontCharCount) +
161
- "..." +
162
- str.substring(str.length - backCharCount)
163
- );
164
- } else {
165
- return str;
166
- }
170
+ export function centerTruncate(str: string, maxLength: number) {
171
+ if (str.length > maxLength) {
172
+ const frontCharCount = Math.floor((maxLength - 3) / 2);
173
+ const backCharCount = maxLength - frontCharCount - 3;
174
+ return (
175
+ str.substring(0, frontCharCount) +
176
+ "..." +
177
+ str.substring(str.length - backCharCount)
178
+ );
179
+ } else {
180
+ return str;
181
+ }
167
182
  }
168
183
 
169
184
  interface FPercentOptions {
170
- multiplier?: number,
171
- maximumFractionDigits?: number,
172
- NaN?: string
185
+ multiplier?: number,
186
+ maximumFractionDigits?: number,
187
+ NaN?: string
173
188
  }
174
189
 
175
190
  /**
@@ -179,54 +194,81 @@ interface FPercentOptions {
179
194
  * @returns {string}
180
195
  */
181
196
  export function fPercent(num: string | number, options: FPercentOptions = {}) {
182
- options = { multiplier: 100, maximumFractionDigits: 1, NaN: "N/A", ...options };
183
-
184
- num = parseFloat("" + num);
185
-
186
- if (isNaN(num)) {
187
- return options.NaN;
188
- }
189
-
190
- return fNumber(num * (options.multiplier || 100), options) + "%";
191
- }
192
-
193
-
194
- export function fPhone(value) {
195
- if (!value || typeof value !== "string") {
196
- return value || "";
197
- }
198
-
199
- const input = value.replace(/\D/g, "").split("");
200
- let phone = "";
201
-
202
- const startsWithOne = input.length > 0 && input[0] === "1";
203
- const shift = startsWithOne ? 1 : 0;
204
-
205
- input.map((number, index) => {
206
- switch (index) {
207
- case shift:
208
- phone += "(";
209
- break;
210
- case shift + 3:
211
- phone += ") ";
212
- break;
213
- case shift + 6:
214
- phone += "-";
215
- break;
216
- case shift + 10:
217
- phone += " x";
218
- break;
219
- }
220
- if (index === 0 && number === "1") {
221
- phone += "+1 ";
222
- } else {
223
- phone += number;
224
- }
225
- });
226
-
227
- if (value === "+1 (") {
228
- return "";
229
- }
230
-
231
- return phone;
197
+ options = { multiplier: 100, maximumFractionDigits: 1, NaN: "N/A", ...options };
198
+
199
+ num = parseFloat("" + num);
200
+
201
+ if (isNaN(num)) {
202
+ return options.NaN;
203
+ }
204
+
205
+ return fNumber(num * (options.multiplier || 100), options) + "%";
206
+ }
207
+
208
+
209
+ export function fPhone(value: string | number) {
210
+ if (!value || typeof value !== "string") {
211
+ return value || "";
212
+ }
213
+
214
+ const input = value.replace(/\D/g, "").split("");
215
+ let phone = "";
216
+
217
+ const startsWithOne = input.length > 0 && input[0] === "1";
218
+ const shift = startsWithOne ? 1 : 0;
219
+
220
+ input.map((number, index) => {
221
+ switch (index) {
222
+ case shift:
223
+ phone += "(";
224
+ break;
225
+ case shift + 3:
226
+ phone += ") ";
227
+ break;
228
+ case shift + 6:
229
+ phone += "-";
230
+ break;
231
+ case shift + 10:
232
+ phone += " x";
233
+ break;
234
+ }
235
+ if (index === 0 && number === "1") {
236
+ phone += "+1 ";
237
+ } else {
238
+ phone += number;
239
+ }
240
+ });
241
+
242
+ if (value === "+1 (") {
243
+ return "";
244
+ }
245
+
246
+ return phone;
247
+ }
248
+
249
+ export function fNameOrCount(items: ActionTargetItem[] | ActionTargetItem, label: string) {
250
+ return Array.isArray(items) ? `${items?.length} ${label}` : `${items ? items.title || items.name || items.id : ""}`;
251
+ }
252
+
253
+ export function fJSON(string: string | object) {
254
+ if (!string) {
255
+ return string;
256
+ }
257
+
258
+ try {
259
+ if (typeof string === "object") {
260
+ return JSON.stringify(string, null, 2);
261
+ }
262
+ return JSON.stringify(JSON.parse(string), null, 2);
263
+ } catch (e) {
264
+ return string;
265
+ }
266
+ }
267
+
268
+ export function fMarkdownJSON(string: string | object): string {
269
+ if (isJSON(string)) {
270
+ return `\`\`\`json\n${fJSON(string)}\n\`\`\``;
271
+ }
272
+ // @ts-expect-error Guaranteed to only allow strings here using isJSON check
273
+ return string;
232
274
  }
@@ -10,7 +10,9 @@ export * from "./FlashMessages";
10
10
  export * from "./formats";
11
11
  export * from "./hotkeys";
12
12
  export * from "./multiFileUpload";
13
+ export * from "./objectStore";
13
14
  export * from "./request";
15
+ export * from "./routes";
14
16
  export * from "./singleFileUpload";
15
17
  export * from "./storage";
16
18
  export * from "./styles";
@@ -1,68 +1,82 @@
1
1
  import { Ref, ref } from "vue";
2
- import { FileUpload, FileUploadOptions, UploadedFile } from "./FileUpload";
2
+ import { FlashMessages } from "../helpers";
3
+ import {
4
+ FileUploadCompleteCallback,
5
+ FileUploadOptions,
6
+ OnFilesChangeCallback,
7
+ UploadedFile,
8
+ VoidCallback
9
+ } from "../types";
10
+ import { FileUpload } from "./FileUpload";
3
11
 
4
- export function useMultiFileUpload(options: FileUploadOptions) {
5
- const uploadedFiles: Ref<UploadedFile[]> = ref([]);
6
- const onCompleteCb: Ref<Function | null> = ref(null);
7
- const onFilesChangeCb: Ref<Function | null> = ref(null);
8
- const onFilesSelected = (e: any) => {
9
- uploadedFiles.value = [...uploadedFiles.value, ...e.target.files];
10
- new FileUpload(e.target.files, options)
11
- .onProgress(({ file }: { file: UploadedFile }) => {
12
- updateFileInList(file);
13
- })
14
- .onComplete(({ file, uploadedFile }: { file: UploadedFile, uploadedFile: UploadedFile }) => {
15
- updateFileInList(file, uploadedFile);
16
- })
17
- .onAllComplete(() => {
18
- onCompleteCb.value && onCompleteCb.value();
19
- onFilesChangeCb.value && onFilesChangeCb.value(uploadedFiles.value);
20
- })
21
- .upload();
22
- };
12
+ export function useMultiFileUpload(options?: FileUploadOptions) {
13
+ const uploadedFiles: Ref<UploadedFile[]> = ref([]);
14
+ const onCompleteCb: Ref<FileUploadCompleteCallback | null> = ref(null);
15
+ const onFilesChangeCb: Ref<OnFilesChangeCallback | null> = ref(null);
16
+ const onFilesSelected = (e: any) => {
17
+ uploadedFiles.value = [...uploadedFiles.value, ...e.target.files];
18
+ new FileUpload(e.target.files, options)
19
+ .onProgress(({ file }: { file: UploadedFile }) => {
20
+ updateFileInList(file);
21
+ })
22
+ .onComplete(({ file, uploadedFile }) => {
23
+ file && updateFileInList(file, uploadedFile);
24
+ })
25
+ .onError(({ file }: { file: UploadedFile }) => {
26
+ FlashMessages.error(`Failed to upload ${file.name}`);
27
+ })
28
+ .onAllComplete(() => {
29
+ onCompleteCb.value && onCompleteCb.value({
30
+ file: uploadedFiles.value[0],
31
+ uploadedFile: uploadedFiles.value[0]
32
+ });
33
+ onFilesChangeCb.value && onFilesChangeCb.value(uploadedFiles.value);
34
+ })
35
+ .upload();
36
+ };
23
37
 
24
- function updateFileInList(file: UploadedFile, replace: UploadedFile | null = null) {
25
- const index = uploadedFiles.value.findIndex(f => f.id === file.id);
26
- if (index !== -1) {
27
- uploadedFiles.value.splice(index, 1, replace || file);
28
- }
29
- onFilesChangeCb.value && onFilesChangeCb.value(uploadedFiles.value);
30
- }
38
+ function updateFileInList(file: UploadedFile, replace: UploadedFile | null = null) {
39
+ const index = uploadedFiles.value.findIndex(f => f.id === file.id);
40
+ if (index !== -1) {
41
+ uploadedFiles.value.splice(index, 1, replace || file);
42
+ }
43
+ onFilesChangeCb.value && onFilesChangeCb.value(uploadedFiles.value);
44
+ }
31
45
 
32
- const onDrop = (e: InputEvent) => {
33
- onFilesSelected({ target: { files: e.dataTransfer?.files } });
34
- };
46
+ const onDrop = (e: InputEvent) => {
47
+ onFilesSelected({ target: { files: e.dataTransfer?.files } });
48
+ };
35
49
 
36
- const onFilesChange = (cb: Function) => {
37
- onFilesChangeCb.value = cb;
38
- };
50
+ const onFilesChange = (cb: OnFilesChangeCallback) => {
51
+ onFilesChangeCb.value = cb;
52
+ };
39
53
 
40
- const onComplete = (cb: Function) => {
41
- onCompleteCb.value = cb;
42
- };
54
+ const onComplete = (cb: VoidCallback) => {
55
+ onCompleteCb.value = cb;
56
+ };
43
57
 
44
- const clearUploadedFiles = () => {
45
- uploadedFiles.value = [];
46
- onFilesChangeCb.value && onFilesChangeCb.value(uploadedFiles.value);
47
- onCompleteCb.value && onCompleteCb.value();
48
- };
58
+ const clearUploadedFiles = () => {
59
+ uploadedFiles.value = [];
60
+ onFilesChangeCb.value && onFilesChangeCb.value(uploadedFiles.value);
61
+ onCompleteCb.value && onCompleteCb.value({ file: null, uploadedFile: null });
62
+ };
49
63
 
50
- const onRemove = (file: UploadedFile) => {
51
- const index = uploadedFiles.value.findIndex(f => f.id === file.id);
52
- if (index !== -1) {
53
- uploadedFiles.value.splice(index, 1);
54
- }
55
- onFilesChangeCb.value && onFilesChangeCb.value(uploadedFiles.value);
56
- onCompleteCb.value && onCompleteCb.value();
57
- };
64
+ const onRemove = (file: UploadedFile) => {
65
+ const index = uploadedFiles.value.findIndex(f => f.id === file.id);
66
+ if (index !== -1) {
67
+ uploadedFiles.value.splice(index, 1);
68
+ }
69
+ onFilesChangeCb.value && onFilesChangeCb.value(uploadedFiles.value);
70
+ onCompleteCb.value && onCompleteCb.value({ file, uploadedFile: file });
71
+ };
58
72
 
59
- return {
60
- clearUploadedFiles,
61
- onRemove,
62
- onComplete,
63
- onFilesChange,
64
- onDrop,
65
- onFilesSelected,
66
- uploadedFiles
67
- };
73
+ return {
74
+ clearUploadedFiles,
75
+ onRemove,
76
+ onComplete,
77
+ onFilesChange,
78
+ onDrop,
79
+ onFilesSelected,
80
+ uploadedFiles
81
+ };
68
82
  }
@@ -0,0 +1,52 @@
1
+ import { reactive, UnwrapNestedRefs } from "vue";
2
+ import { TypedObject } from "../types";
3
+
4
+ const store = new Map<string, any>();
5
+
6
+ /**
7
+ * Store an object in the object store via type + id
8
+ * Returns the stored object that should be used instead of the passed object as the returned object is shared across the system
9
+ *
10
+ * @param {TypedObject} newObject
11
+ * @returns {TypedObject}
12
+ */
13
+ export function storeObject<T extends TypedObject>(newObject: T): UnwrapNestedRefs<T> {
14
+ const id = newObject.id || newObject.name;
15
+ const type = newObject.__type;
16
+ if (!id || !type) return reactive(newObject);
17
+
18
+ const objectKey = `${type}:${id}`;
19
+
20
+ const oldObject: UnwrapNestedRefs<T> | undefined = store.get(objectKey);
21
+
22
+ // Apply all properties from newObject to oldObject then store and return the updated object
23
+ if (oldObject) {
24
+ const oldTimestamp = oldObject.__timestamp || oldObject.updated_at;
25
+ const newTimestamp = newObject.__timestamp || newObject.updated_at;
26
+ // If the old object is newer, do not store the new object, just return the old
27
+ if (oldTimestamp && newTimestamp && newTimestamp < oldTimestamp) {
28
+ return oldObject;
29
+ }
30
+ }
31
+
32
+ // Recursively store all the children of the object as well
33
+ for (const key of Object.keys(newObject)) {
34
+ const value = newObject[key];
35
+ if (Array.isArray(value) && value.length > 0 && typeof value[0] === "object") {
36
+ for (const index in value) {
37
+ newObject[key][index] = storeObject(value[index]);
38
+ }
39
+ }
40
+ }
41
+
42
+ // Update the old object with the new object properties
43
+ if (oldObject) {
44
+ // If the new object is newer, apply all properties from the new object to the old object
45
+ Object.assign(oldObject, newObject);
46
+ return oldObject;
47
+ }
48
+
49
+ const reactiveObject = reactive(newObject);
50
+ store.set(objectKey, reactiveObject);
51
+ return reactiveObject;
52
+ }