quasar-ui-danx 0.0.9 → 0.0.11

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.
Files changed (67) hide show
  1. package/package.json +3 -2
  2. package/src/components/ActionTable/ActionTable.vue +135 -0
  3. package/src/components/ActionTable/BatchActionMenu.vue +60 -0
  4. package/src/components/ActionTable/EmptyTableState.vue +33 -0
  5. package/src/components/ActionTable/Filters/CollapsableFiltersSidebar.vue +36 -0
  6. package/src/components/ActionTable/Filters/FilterGroupItem.vue +28 -0
  7. package/src/components/ActionTable/Filters/FilterGroupList.vue +76 -0
  8. package/src/components/ActionTable/Filters/FilterListToggle.vue +50 -0
  9. package/src/components/ActionTable/Filters/FilterableField.vue +141 -0
  10. package/src/components/ActionTable/Form/Fields/BooleanField.vue +37 -0
  11. package/src/components/ActionTable/Form/Fields/ConfirmPasswordField.vue +46 -0
  12. package/src/components/ActionTable/Form/Fields/DateField.vue +59 -0
  13. package/src/components/ActionTable/Form/Fields/DateRangeField.vue +110 -0
  14. package/src/components/ActionTable/Form/Fields/DateTimeField.vue +50 -0
  15. package/src/components/ActionTable/Form/Fields/DateTimePicker.vue +59 -0
  16. package/src/components/ActionTable/Form/Fields/EditableDiv.vue +39 -0
  17. package/src/components/ActionTable/Form/Fields/FieldLabel.vue +32 -0
  18. package/src/components/ActionTable/Form/Fields/FileUploadButton.vue +78 -0
  19. package/src/components/ActionTable/Form/Fields/InlineDateTimeField.vue +44 -0
  20. package/src/components/ActionTable/Form/Fields/IntegerField.vue +26 -0
  21. package/src/components/ActionTable/Form/Fields/LabeledInput.vue +63 -0
  22. package/src/components/ActionTable/Form/Fields/MultiFileField.vue +91 -0
  23. package/src/components/ActionTable/Form/Fields/MultiKeywordField.vue +57 -0
  24. package/src/components/ActionTable/Form/Fields/NewPasswordField.vue +39 -0
  25. package/src/components/ActionTable/Form/Fields/NumberField.vue +94 -0
  26. package/src/components/ActionTable/Form/Fields/NumberRangeField.vue +140 -0
  27. package/src/components/ActionTable/Form/Fields/SelectDrawer.vue +136 -0
  28. package/src/components/ActionTable/Form/Fields/SelectField.vue +318 -0
  29. package/src/components/ActionTable/Form/Fields/SelectWithChildrenField.vue +81 -0
  30. package/src/components/ActionTable/Form/Fields/SingleFileField.vue +78 -0
  31. package/src/components/ActionTable/Form/Fields/TextField.vue +82 -0
  32. package/src/components/ActionTable/Form/Fields/WysiwygField.vue +46 -0
  33. package/src/components/ActionTable/Form/Fields/index.ts +23 -0
  34. package/src/components/ActionTable/Form/RenderedForm.vue +74 -0
  35. package/src/components/ActionTable/RenderComponentColumn.vue +22 -0
  36. package/src/components/ActionTable/TableSummaryRow.vue +95 -0
  37. package/src/components/ActionTable/index.ts +15 -0
  38. package/src/components/ActionTable/listActions.ts +361 -0
  39. package/src/components/ActionTable/tableColumns.ts +72 -0
  40. package/src/components/ActionTable/tableHelpers.ts +83 -0
  41. package/src/components/DragAndDrop/listDragAndDrop.ts +210 -210
  42. package/src/components/Utility/CollapsableSidebar.vue +119 -0
  43. package/src/components/Utility/ContentDrawer.vue +70 -0
  44. package/src/components/Utility/Dialogs/ConfirmDialog.vue +132 -0
  45. package/src/components/Utility/Dialogs/FullScreenDialog.vue +46 -0
  46. package/src/components/Utility/Dialogs/InfoDialog.vue +92 -0
  47. package/src/components/Utility/Dialogs/InputDialog.vue +35 -0
  48. package/src/components/Utility/SvgImg.vue +10 -5
  49. package/src/components/Utility/Transitions/ListTransition.vue +50 -0
  50. package/src/components/Utility/Transitions/SlideTransition.vue +63 -0
  51. package/src/components/Utility/Transitions/StaggeredListTransition.vue +97 -0
  52. package/src/components/Utility/index.ts +9 -0
  53. package/src/components/index.ts +3 -0
  54. package/src/helpers/FileUpload.ts +294 -0
  55. package/src/helpers/FlashMessages.ts +79 -0
  56. package/src/helpers/array.ts +37 -0
  57. package/src/helpers/compatibility.ts +64 -0
  58. package/src/helpers/date.ts +5 -0
  59. package/src/helpers/download.ts +192 -0
  60. package/src/helpers/downloadPdf.ts +92 -0
  61. package/src/helpers/files.ts +52 -0
  62. package/src/helpers/formats.ts +183 -0
  63. package/src/helpers/http.ts +62 -0
  64. package/src/helpers/index.ts +10 -1
  65. package/src/helpers/multiFileUpload.ts +68 -0
  66. package/src/helpers/singleFileUpload.ts +54 -0
  67. package/src/helpers/storage.ts +8 -0
@@ -0,0 +1,92 @@
1
+ import { download } from "danx/src/helpers/download";
2
+
3
+ /**
4
+ * Asynchronously load a file from the URL and trigger a download in the browser
5
+ *
6
+ * @param url
7
+ * @param filename
8
+ * @param postParams
9
+ * @returns {Promise<void>}
10
+ */
11
+ export async function downloadFile(url, filename = "", postParams = null) {
12
+ let fetchOptions = undefined;
13
+
14
+ if (postParams) {
15
+ fetchOptions = {
16
+ method: "POST",
17
+ "Content-Type": "application/json",
18
+ body: JSON.stringify(postParams)
19
+ };
20
+ }
21
+
22
+ const response = await fetch(url, fetchOptions);
23
+
24
+ if (!response.ok) {
25
+ throw Error("File download failed: invalid response from server");
26
+ }
27
+
28
+ let errorMessage;
29
+
30
+ // Handle a JSON response (which indicates an error occurred)
31
+ try {
32
+ // @ts-expect-error data is defined on response
33
+ const jsonResponse = JSON.parse(new TextDecoder().decode(response.data));
34
+ console.error("Error downloading file:", jsonResponse);
35
+ errorMessage = jsonResponse.message;
36
+ if (jsonResponse.errors) {
37
+ errorMessage = jsonResponse.errors[0].message;
38
+ }
39
+ } catch (e) {
40
+ // we expect an error thrown for invalid JSON when the response is a file
41
+ }
42
+
43
+ if (errorMessage) {
44
+ throw new Error("Failed to download file: " + errorMessage);
45
+ }
46
+
47
+ await downloadFileResponse(response, filename);
48
+ }
49
+
50
+ /**
51
+ * Downloads a file from a response object w/ a file attachment
52
+ *
53
+ * @param response
54
+ * @param filename
55
+ */
56
+ export async function downloadFileResponse(response, filename = "") {
57
+ const contentDisposition = getResponseHeader(
58
+ response,
59
+ "content-disposition",
60
+ ""
61
+ );
62
+
63
+ const contentType = getResponseHeader(response, "content-type", "");
64
+
65
+ const match = contentDisposition.match(/filename="([^"]+)"/);
66
+
67
+ filename = filename || (match && match[1]) || "download.pdf";
68
+
69
+ let data = response.data;
70
+ if (!data) {
71
+ data = await response.blob();
72
+ }
73
+
74
+ download(data, filename, contentType);
75
+ }
76
+
77
+ /**
78
+ * Get a header from a response object
79
+ * @param response
80
+ * @param header
81
+ * @param defaultValue
82
+ * @returns {*}
83
+ */
84
+ export function getResponseHeader(response, header, defaultValue) {
85
+ if (response.headers) {
86
+ if (typeof response.headers.get === "function") {
87
+ return response.headers.get(header) || defaultValue;
88
+ } else {
89
+ return response.headers[header] || defaultValue;
90
+ }
91
+ }
92
+ }
@@ -0,0 +1,52 @@
1
+ import { FlashMessages, useCompatibility } from "danx/src/helpers";
2
+ import ExifReader from "exifreader";
3
+
4
+ export async function resolveFileLocation(file, waitMessage = null) {
5
+ if (file.location) {
6
+ return file.location;
7
+ }
8
+
9
+ try {
10
+ const tags = await ExifReader.load(file.blobUrl || file.url, {
11
+ expanded: true
12
+ });
13
+ if (tags.gps) {
14
+ return {
15
+ latitude: tags.gps.Latitude,
16
+ longitude: tags.gps.Longitude
17
+ };
18
+ }
19
+
20
+ const { waitForLocation, location } = useCompatibility();
21
+
22
+ // Show a waiting for location message if we have not returned within 1 second
23
+ if (waitMessage) {
24
+ setTimeout(() => {
25
+ if (!location.value && waitMessage) {
26
+ FlashMessages.warning(waitMessage);
27
+ }
28
+ }, 1000);
29
+ }
30
+
31
+ // Wait for the browser to return the location (https only as http will not return a location)
32
+ if (window.location.protocol === "https:") {
33
+ await waitForLocation();
34
+ }
35
+ // Ignore the wait message if we already returned
36
+ waitMessage = false;
37
+ if (!location.value) {
38
+ return null;
39
+ }
40
+
41
+ return {
42
+ latitude: location.value.latitude,
43
+ longitude: location.value.longitude,
44
+ accuracy: location.value.accuracy,
45
+ altitude: location.value.altitude,
46
+ altitudeAccuracy: location.value.altitudeAccuracy
47
+ };
48
+ } catch (error) {
49
+ console.error(error);
50
+ return null;
51
+ }
52
+ }
@@ -0,0 +1,183 @@
1
+ import { DateTime, IANAZone } from "luxon";
2
+
3
+ const SERVER_TZ = new IANAZone("America/Chicago");
4
+
5
+ /**
6
+ * Converts a date string from the server's time zone to the user's time zone.
7
+ * @param {String} dateTimeString
8
+ * @returns {DateTime}
9
+ */
10
+ export function localizedDateTime(dateTimeString) {
11
+ dateTimeString = dateTimeString?.replace("T", " ");
12
+ // noinspection JSCheckFunctionSignatures
13
+ return DateTime.fromSQL(dateTimeString, { zone: SERVER_TZ }).setZone("local");
14
+ }
15
+
16
+ /**
17
+ * Converts a date string from the user's time zone to the server's time zone.
18
+ * @param dateTimeString
19
+ * @returns {DateTime}
20
+ */
21
+ export function remoteDateTime(dateTimeString) {
22
+ dateTimeString = dateTimeString?.replace("T", " ");
23
+ // noinspection JSCheckFunctionSignatures
24
+ return DateTime.fromSQL(dateTimeString, { zone: "local" }).setZone(SERVER_TZ);
25
+ }
26
+
27
+ /**
28
+ * @param {DateTime|String} dateTime
29
+ * @returns {DateTime|*}
30
+ */
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");
37
+ }
38
+
39
+ /**
40
+ * Parses a Quasar formatted date string into a Luxon DateTime object
41
+ * @param date
42
+ * @param format
43
+ * @returns {DateTime}
44
+ */
45
+ export function parseQDate(date, format = "yyyy/MM/dd") {
46
+ return DateTime.fromFormat(date, format);
47
+ }
48
+
49
+ /**
50
+ * Parses a Quasar formatted date/time string into a Luxon DateTime object
51
+ * @param date
52
+ * @param format
53
+ * @returns {DateTime}
54
+ */
55
+ export function parseQDateTime(date, format = "yyyy/MM/dd HH:mm:ss") {
56
+ return DateTime.fromFormat(date, format);
57
+ }
58
+
59
+ /**
60
+ * Formats a Luxon DateTime object into a Quasar formatted date string
61
+ * @param date
62
+ * @returns {string}
63
+ */
64
+ export function fQDate(date) {
65
+ return fDate(date, { format: "yyyy/MM/dd" });
66
+ }
67
+
68
+ /**
69
+ *
70
+ * @param {String} dateTimeString
71
+ * @param options
72
+ * @returns {string}
73
+ */
74
+ export function fLocalizedDateTime(dateTimeString, options = {}) {
75
+ return fDateTime(localizedDateTime(dateTimeString), options);
76
+ }
77
+
78
+ /**
79
+ * Formats a date/time object or string into a human-readable format
80
+ *
81
+ * @param {String|Object} dateTime
82
+ * @param format
83
+ * @param {String|null} empty
84
+ * @returns {string}
85
+ */
86
+ export function fDateTime(
87
+ dateTime = null,
88
+ { format = "M/d/yy h:mma", empty = "- -" } = {}
89
+ ) {
90
+ const formatted = (dateTime ? parseDateTime(dateTime) : DateTime.now()).toFormat(format).toLowerCase();
91
+ return formatted === "invalid datetime" ? empty : formatted;
92
+ }
93
+
94
+ /**
95
+ * Formats a date/time object or string into the best format for DB input
96
+ * @param dateTime
97
+ * @returns {string}
98
+ */
99
+ export function dbDateTime(dateTime = null) {
100
+ return fDateTime(dateTime, { format: "yyyy-MM-dd HH:mm:ss", empty: null });
101
+ }
102
+
103
+ /**
104
+ * Formats a date object or string into a human-readable format
105
+ * @param {String|Object} dateTime
106
+ * @param {String|null} empty
107
+ * @param format
108
+ * @returns {string}
109
+ */
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;
113
+ }
114
+
115
+ /**
116
+ * Formats a number of seconds into Hours / Minutes / Seconds or just Minutes and Seconds
117
+ *
118
+ * @param second
119
+ * @returns {string}
120
+ */
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
+ }
126
+
127
+ /**
128
+ * Formats an amount into USD currency format
129
+ * @param amount
130
+ * @returns {string}
131
+ */
132
+ export function fCurrency(amount) {
133
+ return new Intl.NumberFormat("en-US", {
134
+ style: "currency",
135
+ currency: "USD"
136
+ }).format(amount);
137
+ }
138
+
139
+ /**
140
+ * Formats a number into a human-readable format
141
+ * @param number
142
+ * @param options
143
+ * @returns {string}
144
+ */
145
+ export function fNumber(number, options = {}) {
146
+ return new Intl.NumberFormat("en-US", options).format(number);
147
+ }
148
+
149
+ /**
150
+ * Truncates the string by removing chars from the middle of the string
151
+ * @param str
152
+ * @param maxLength
153
+ * @returns {string|*}
154
+ */
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
+ }
167
+ }
168
+
169
+ /**
170
+ * Formats a number into a percentage
171
+ * @param num
172
+ * @param options
173
+ * @returns {string}
174
+ */
175
+ export function fPercent(num, options = { multiplier: 100, maximumFractionDigits: 1, NaN: "N/A" }) {
176
+ num = parseFloat(num);
177
+
178
+ if (isNaN(num)) {
179
+ return options.NaN;
180
+ }
181
+
182
+ return fNumber(num * options.multiplier, options) + "%";
183
+ }
@@ -0,0 +1,62 @@
1
+ export const request = {
2
+ async get(url, options = {}) {
3
+ return fetch(url, {
4
+ method: "get",
5
+ headers: {
6
+ Accept: "application/json",
7
+ "Content-Type": "application/json"
8
+ },
9
+ ...options
10
+ });
11
+ },
12
+
13
+ async post(url, data = {}, options = {}) {
14
+ return fetch(url, {
15
+ method: "post",
16
+ body: JSON.stringify(data),
17
+ headers: {
18
+ Accept: "application/json",
19
+ "Content-Type": "application/json"
20
+ },
21
+ ...options
22
+ }).then((r) => r.json());
23
+ }
24
+ };
25
+
26
+ /**
27
+ * Fetches a resource list applying the filter. If there is a selected resource,
28
+ * stores that resource from the already populated list. If that resource does not exist
29
+ * also fetches that resource record from the endpoint, then adds it to the list if it
30
+ * does not exist in the filtered list
31
+ *
32
+ * @param fetchFn
33
+ * @param list
34
+ * @param id
35
+ * @param filter
36
+ * @returns {Promise<void>}
37
+ */
38
+ export async function fetchResourceListWithSelected(fetchFn, list, id, filter) {
39
+ // First make sure we have the selected record, so we can always add it to the list
40
+ let selectedResource;
41
+ if (id) {
42
+ selectedResource = list.value.find((c) => c.id === id) || (await fetchFn({ id }))[0];
43
+ }
44
+
45
+ // Get the filtered campaign list
46
+ list.value = await fetchFn(filter);
47
+
48
+ // If our selected campaign is not in the filtered list, add it
49
+ if (selectedResource && !list.value.find((c) => c.id === id)) {
50
+ list.value.push(selectedResource);
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Returns the value of the URL parameter (if it is set)
56
+ * @param key
57
+ * @param url
58
+ */
59
+ export function getUrlParam(key, url = undefined) {
60
+ const params = new URLSearchParams(url?.replace(/.*\?/, "") || window.location.search);
61
+ return params.get(key);
62
+ }
@@ -1 +1,10 @@
1
- export * from './utils';
1
+ export * from "./array";
2
+ export * from "./compatibility";
3
+ export * from "./files";
4
+ export * from "./FileUpload";
5
+ export * from "./FlashMessages";
6
+ export * from "./http";
7
+ export * from "./multiFileUpload";
8
+ export * from "./singleFileUpload";
9
+ export * from "./storage";
10
+ export * from "./utils";
@@ -0,0 +1,68 @@
1
+ import { FileUpload, FileUploadOptions } from "danx/src/helpers/FileUpload";
2
+ import { ref } from "vue";
3
+
4
+ export function useMultiFileUpload(options: FileUploadOptions) {
5
+ const uploadedFiles = ref([]);
6
+ const onCompleteCb = ref(null);
7
+ const onFilesChangeCb = ref(null);
8
+ const onFilesSelected = (e) => {
9
+ uploadedFiles.value = [...uploadedFiles.value, ...e.target.files];
10
+ new FileUpload(e.target.files, options)
11
+ .onProgress(({ file }) => {
12
+ updateFileInList(file);
13
+ })
14
+ .onComplete(({ file, uploadedFile }) => {
15
+ updateFileInList(file, uploadedFile);
16
+ })
17
+ .onAllComplete(() => {
18
+ onCompleteCb.value && onCompleteCb.value();
19
+ onFilesChangeCb.value && onFilesChangeCb.value(uploadedFiles.value);
20
+ })
21
+ .upload();
22
+ };
23
+
24
+ function updateFileInList(file, replace = 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
+ }
31
+
32
+ const onDrop = (e) => {
33
+ onFilesSelected({ target: { files: e.dataTransfer.files } });
34
+ };
35
+
36
+ const onFilesChange = (cb) => {
37
+ onFilesChangeCb.value = cb;
38
+ };
39
+
40
+ const onComplete = (cb) => {
41
+ onCompleteCb.value = cb;
42
+ };
43
+
44
+ const onClear = () => {
45
+ uploadedFiles.value = [];
46
+ onFilesChangeCb.value && onFilesChangeCb.value(uploadedFiles.value);
47
+ onCompleteCb.value && onCompleteCb.value();
48
+ };
49
+
50
+ const onRemove = (file) => {
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
+ };
58
+
59
+ return {
60
+ onClear,
61
+ onRemove,
62
+ onComplete,
63
+ onFilesChange,
64
+ onDrop,
65
+ onFilesSelected,
66
+ uploadedFiles
67
+ };
68
+ }
@@ -0,0 +1,54 @@
1
+ import { FileUpload, FileUploadOptions } from "danx/src/helpers";
2
+ import { computed, ref } from "vue";
3
+
4
+ export function useSingleFileUpload(options: FileUploadOptions) {
5
+ const uploadedFile = ref(null);
6
+ const onCompleteCb = ref(null);
7
+ const onFileChangeCb = ref(null);
8
+
9
+ const onFileSelected = (e) => {
10
+ uploadedFile.value = null;
11
+ new FileUpload(e.target.files[0], options)
12
+ .onProgress(({ file }) => {
13
+ uploadedFile.value = file;
14
+ onFileChangeCb.value && onFileChangeCb.value(uploadedFile.value);
15
+ })
16
+ .onComplete(({ uploadedFile: completedFile }) => {
17
+ uploadedFile.value = completedFile;
18
+ onCompleteCb.value && onCompleteCb.value(uploadedFile.value);
19
+ onFileChangeCb.value && onFileChangeCb.value(uploadedFile.value);
20
+ })
21
+ .upload();
22
+ };
23
+
24
+ const onDrop = (e) => {
25
+ onFileSelected({ target: { files: e.dataTransfer.files } });
26
+ };
27
+
28
+ const isFileUploaded = computed(() => {
29
+ return uploadedFile.value && uploadedFile.value.url;
30
+ });
31
+
32
+ const onFileChange = (cb) => {
33
+ onFileChangeCb.value = cb;
34
+ };
35
+
36
+ const onComplete = (cb) => {
37
+ onCompleteCb.value = cb;
38
+ };
39
+
40
+ const onClear = () => {
41
+ uploadedFile.value = null;
42
+ onFileChangeCb.value && onFileChangeCb.value(uploadedFile.value);
43
+ };
44
+
45
+ return {
46
+ isFileUploaded,
47
+ onClear,
48
+ onComplete,
49
+ onFileChange,
50
+ onDrop,
51
+ onFileSelected,
52
+ uploadedFile
53
+ };
54
+ }
@@ -0,0 +1,8 @@
1
+ export function setItem(key, value) {
2
+ localStorage.setItem(key, JSON.stringify(value));
3
+ }
4
+
5
+ export function getItem(key, defaultValue = null) {
6
+ const item = localStorage.getItem(key);
7
+ return item ? JSON.parse(item) : defaultValue;
8
+ }