quasar-ui-danx 0.2.32 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,8 +5,8 @@ import { FlashMessages } from "./FlashMessages";
5
5
 
6
6
  export type FileUploadOptions = {
7
7
  directory?: string,
8
- presignedUploadUrl?: (...params: any) => "",
9
- uploadCompletedUrl?: (...params: any) => "",
8
+ presignedUploadUrl?: Function | null;
9
+ uploadCompletedUrl?: Function | null;
10
10
  };
11
11
 
12
12
  export type UploadedFile = {
@@ -16,23 +16,21 @@ export type UploadedFile = {
16
16
  type: string,
17
17
  progress: number,
18
18
  location: string,
19
- blobUrl: string
19
+ blobUrl: string,
20
+ url: string,
20
21
  }
21
22
 
22
23
  export class FileUpload {
23
24
  files: UploadedFile[] = [];
24
- fileUploads = [];
25
- onErrorCb = null;
26
- onProgressCb = null;
27
- onCompleteCb = null;
28
- onAllCompleteCb = null;
29
- options: FileUploadOptions = {};
30
-
31
- constructor(files: UploadedFile[] | UploadedFile, options: FileUploadOptions = {}) {
32
- if (!Array.isArray(files) && !(files instanceof FileList)) {
33
- files = [files];
34
- }
35
- this.files = files;
25
+ fileUploads: UploadedFile[] = [];
26
+ onErrorCb: Function | null = null;
27
+ onProgressCb: Function | null = null;
28
+ onCompleteCb: Function | null = null;
29
+ onAllCompleteCb: Function | null = null;
30
+ options: FileUploadOptions | null = {};
31
+
32
+ constructor(files: UploadedFile[] | UploadedFile, options: FileUploadOptions | null = {}) {
33
+ this.files = !Array.isArray(files) && !(files instanceof FileList) ? [files] : files;
36
34
  this.fileUploads = [];
37
35
  this.onErrorCb = null;
38
36
  this.onProgressCb = null;
@@ -81,7 +79,7 @@ export class FileUpload {
81
79
  /**
82
80
  * Callback for when all files have been uploaded
83
81
  */
84
- onAllComplete(cb) {
82
+ onAllComplete(cb: Function) {
85
83
  this.onAllCompleteCb = cb;
86
84
  return this;
87
85
  }
@@ -91,7 +89,7 @@ export class FileUpload {
91
89
  * @param cb
92
90
  * @returns {FileUpload}
93
91
  */
94
- onComplete(cb) {
92
+ onComplete(cb: Function) {
95
93
  this.onCompleteCb = cb;
96
94
  return this;
97
95
  }
@@ -101,7 +99,7 @@ export class FileUpload {
101
99
  * @param cb
102
100
  * @returns {FileUpload}
103
101
  */
104
- onProgress(cb) {
102
+ onProgress(cb: Function) {
105
103
  this.onProgressCb = cb;
106
104
  return this;
107
105
  }
@@ -111,7 +109,7 @@ export class FileUpload {
111
109
  * @param cb
112
110
  * @returns {FileUpload}
113
111
  */
114
- onError(cb) {
112
+ onError(cb: Function) {
115
113
  this.onErrorCb = cb;
116
114
  return this;
117
115
  }
@@ -122,7 +120,7 @@ export class FileUpload {
122
120
  * @param file
123
121
  * @param error
124
122
  */
125
- errorHandler(e, file, error = null) {
123
+ errorHandler(e: InputEvent, file: UploadedFile, error = null) {
126
124
  if (this.onErrorCb) {
127
125
  this.onErrorCb({ e, file, error });
128
126
  }
@@ -1,17 +1,23 @@
1
- import { ref, shallowRef, VNode } from "vue";
1
+ import { useDebounceFn } from "@vueuse/core";
2
+ import { Ref, shallowRef, VNode } from "vue";
2
3
  import { FlashMessages } from "./FlashMessages";
3
4
 
4
- type ActionTarget = object[] | object;
5
+ export type ActionTargetItem = {
6
+ id: number | string;
7
+ isSaving: Ref<boolean>;
8
+ [key: string]: any;
9
+ };
10
+ export type ActionTarget = ActionTargetItem[] | ActionTargetItem;
5
11
 
6
- interface ActionOptions {
12
+ export interface ActionOptions {
7
13
  name?: string;
8
14
  label?: string;
9
15
  menu?: boolean;
10
16
  batch?: boolean;
11
17
  category?: string;
12
18
  class?: string;
19
+ debounce?: number;
13
20
  trigger?: (target: ActionTarget, input: any) => Promise<any>;
14
- activeTarget?: any;
15
21
  vnode?: (target: ActionTarget) => VNode;
16
22
  enabled?: (target: object) => boolean;
17
23
  batchEnabled?: (targets: object[]) => boolean;
@@ -24,7 +30,7 @@ interface ActionOptions {
24
30
  onFinish?: (result: any, targets: ActionTarget, input: any) => any;
25
31
  }
26
32
 
27
- export const activeActionVnode: object = shallowRef(null);
33
+ export const activeActionVnode: Ref = shallowRef(null);
28
34
 
29
35
  /**
30
36
  * Hook to perform an action on a set of targets
@@ -36,31 +42,25 @@ export const activeActionVnode: object = shallowRef(null);
36
42
  export function useActions(actions: ActionOptions[], globalOptions: ActionOptions | null = null) {
37
43
  const mappedActions = actions.map(action => {
38
44
  const mappedAction: ActionOptions = { ...globalOptions, ...action };
39
- if (!mappedAction.trigger) {
40
- mappedAction.trigger = (target, input) => performAction(mappedAction, target, input);
41
- mappedAction.activeTarget = ref(null);
45
+ if (mappedAction.debounce) {
46
+ mappedAction.trigger = useDebounceFn((target, input) => performAction(mappedAction, target, input, true), mappedAction.debounce);
47
+ } else if (!mappedAction.trigger) {
48
+ mappedAction.trigger = (target, input) => performAction(mappedAction, target, input, true);
42
49
  }
43
50
  return mappedAction;
44
51
  });
45
52
 
46
53
  /**
47
- * Check if the provided target is currently being saved by any of the actions
54
+ * Set the reactive saving state of a target
48
55
  */
49
- function isSavingTarget(target: any): boolean {
50
- if (!target) return false;
51
-
52
- for (const action of mappedActions) {
53
- const activeTargets = (Array.isArray(action.activeTarget.value) ? action.activeTarget.value : [action.activeTarget.value]).filter(t => t);
54
- if (activeTargets.length === 0) continue;
55
-
56
- for (const activeTarget of activeTargets) {
57
- if (activeTarget === target || (activeTarget.id && activeTarget.id === target.id)) {
58
- return true;
59
- }
56
+ function setTargetSavingState(target: ActionTarget, saving: boolean) {
57
+ if (Array.isArray(target)) {
58
+ for (const t of target) {
59
+ t.isSaving.value = saving;
60
60
  }
61
+ } else {
62
+ target.isSaving.value = saving;
61
63
  }
62
-
63
- return false;
64
64
  }
65
65
 
66
66
  /**
@@ -69,22 +69,23 @@ export function useActions(actions: ActionOptions[], globalOptions: ActionOption
69
69
  * @param {string} name - can either be a string or an action object
70
70
  * @param {object[]|object} target - an array of targets or a single target object
71
71
  * @param {any} input - The input data to pass to the action handler
72
+ * @param isTriggered - Whether the action was triggered by a trigger function
72
73
  */
73
- async function performAction(name: string | object, target: ActionTarget, input: any = null) {
74
- const action: ActionOptions = typeof name === "string" ? mappedActions.find(a => a.name === name) : name;
74
+ async function performAction(name: string | object, target: ActionTarget, input: any = null, isTriggered = false) {
75
+ const action: ActionOptions | null | undefined = typeof name === "string" ? mappedActions.find(a => a.name === name) : name;
75
76
  if (!action) {
76
77
  throw new Error(`Unknown action: ${name}`);
77
78
  }
78
79
 
79
- if (!action.activeTarget) {
80
- throw new Error(`Action ${action.name} does not have an activeTarget ref. Please use useActions() or manually set the activeTarget ref`);
80
+ // We always want to call the trigger function if it exists, unless it's already been triggered
81
+ // This provides behavior like debounce and custom action resolution
82
+ if (action.trigger && !isTriggered) {
83
+ return action.trigger(target, input);
81
84
  }
82
85
 
83
86
  const vnode = action.vnode && action.vnode(target);
84
87
  let result: any;
85
88
 
86
- action.activeTarget.value = target;
87
-
88
89
  // Run the onStart handler if it exists and quit the operation if it returns false
89
90
  if (action.onStart) {
90
91
  if (!action.onStart(action, target, input)) {
@@ -92,6 +93,8 @@ export function useActions(actions: ActionOptions[], globalOptions: ActionOption
92
93
  }
93
94
  }
94
95
 
96
+ setTargetSavingState(target, true);
97
+
95
98
  // If additional input is required, first render the vnode and wait for the confirm or cancel action
96
99
  if (vnode) {
97
100
  // If the action requires an input, we set the activeActionVnode to the input component.
@@ -119,7 +122,8 @@ export function useActions(actions: ActionOptions[], globalOptions: ActionOption
119
122
  result = await onConfirmAction(action, target, input);
120
123
  }
121
124
 
122
- action.activeTarget.value = null;
125
+ setTargetSavingState(target, false);
126
+
123
127
  return result;
124
128
  }
125
129
 
@@ -135,14 +139,13 @@ export function useActions(actions: ActionOptions[], globalOptions: ActionOption
135
139
 
136
140
  for (const filter of Object.keys(filters)) {
137
141
  const filterValue = filters[filter];
138
- filteredActions = filteredActions.filter(a => a[filter] === filterValue || (Array.isArray(filterValue) && filterValue.includes(a[filter])));
142
+ filteredActions = filteredActions.filter((a: object) => a[filter] === filterValue || (Array.isArray(filterValue) && filterValue.includes(a[filter])));
139
143
  }
140
144
  return filteredActions;
141
145
  }
142
146
 
143
147
  return {
144
148
  actions: mappedActions,
145
- isSavingTarget,
146
149
  filterActions,
147
150
  performAction
148
151
  };
@@ -1,17 +1,12 @@
1
1
  /**
2
- *
3
- * @param array
4
- * @param item
5
- * @param newItem
6
- * @returns {*[]}
2
+ * Replace an item in an array with a new item
7
3
  */
8
- export function replace(array, item, newItem = undefined) {
9
- const index =
10
- typeof item === "function" ? array.findIndex(item) : array.indexOf(item);
11
- if (index === false) {
12
- console.error("Item not found in array", item, array);
13
- throw new Error("Item not found in array");
4
+ export function replace(array: any[], item: any, newItem = undefined, appendIfMissing = false) {
5
+ const index: any = typeof item === "function" ? array.findIndex(item) : array.indexOf(item);
6
+ if (index === false || index === -1) {
7
+ return appendIfMissing ? [...array, newItem] : array;
14
8
  }
9
+
15
10
  const newArray = [...array];
16
11
  newItem !== undefined
17
12
  ? newArray.splice(index, 1, newItem)
@@ -19,17 +14,17 @@ export function replace(array, item, newItem = undefined) {
19
14
  return newArray;
20
15
  }
21
16
 
22
- export function remove(array, item) {
17
+ /**
18
+ * Remove an item from an array
19
+ */
20
+ export function remove(array: any[], item: any) {
23
21
  return replace(array, item);
24
22
  }
25
23
 
26
24
  /**
27
25
  * Remove duplicate items from an array using a callback to compare 2 elements
28
- * @param array
29
- * @param cb
30
- * @returns {*}
31
26
  */
32
- export function uniqueBy(array, cb) {
27
+ export function uniqueBy(array: any[], cb: Function) {
33
28
  return array.filter((a, index, self) => {
34
29
  // Check if the current element 'a' is the first occurrence in the array
35
30
  return index === self.findIndex((b) => cb(a, b));
@@ -1,17 +1,17 @@
1
- import { ref } from "vue";
2
- import { FileUpload, FileUploadOptions } from "./FileUpload";
1
+ import { Ref, ref } from "vue";
2
+ import { FileUpload, FileUploadOptions, UploadedFile } from "./FileUpload";
3
3
 
4
4
  export function useMultiFileUpload(options: FileUploadOptions) {
5
- const uploadedFiles = ref([]);
6
- const onCompleteCb = ref(null);
7
- const onFilesChangeCb = ref(null);
8
- const onFilesSelected = (e) => {
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
9
  uploadedFiles.value = [...uploadedFiles.value, ...e.target.files];
10
10
  new FileUpload(e.target.files, options)
11
- .onProgress(({ file }) => {
11
+ .onProgress(({ file }: { file: UploadedFile }) => {
12
12
  updateFileInList(file);
13
13
  })
14
- .onComplete(({ file, uploadedFile }) => {
14
+ .onComplete(({ file, uploadedFile }: { file: UploadedFile, uploadedFile: UploadedFile }) => {
15
15
  updateFileInList(file, uploadedFile);
16
16
  })
17
17
  .onAllComplete(() => {
@@ -21,7 +21,7 @@ export function useMultiFileUpload(options: FileUploadOptions) {
21
21
  .upload();
22
22
  };
23
23
 
24
- function updateFileInList(file, replace = null) {
24
+ function updateFileInList(file: UploadedFile, replace: UploadedFile | null = null) {
25
25
  const index = uploadedFiles.value.findIndex(f => f.id === file.id);
26
26
  if (index !== -1) {
27
27
  uploadedFiles.value.splice(index, 1, replace || file);
@@ -29,25 +29,25 @@ export function useMultiFileUpload(options: FileUploadOptions) {
29
29
  onFilesChangeCb.value && onFilesChangeCb.value(uploadedFiles.value);
30
30
  }
31
31
 
32
- const onDrop = (e) => {
33
- onFilesSelected({ target: { files: e.dataTransfer.files } });
32
+ const onDrop = (e: InputEvent) => {
33
+ onFilesSelected({ target: { files: e.dataTransfer?.files } });
34
34
  };
35
35
 
36
- const onFilesChange = (cb) => {
36
+ const onFilesChange = (cb: Function) => {
37
37
  onFilesChangeCb.value = cb;
38
38
  };
39
39
 
40
- const onComplete = (cb) => {
40
+ const onComplete = (cb: Function) => {
41
41
  onCompleteCb.value = cb;
42
42
  };
43
43
 
44
- const onClear = () => {
44
+ const clearUploadedFiles = () => {
45
45
  uploadedFiles.value = [];
46
46
  onFilesChangeCb.value && onFilesChangeCb.value(uploadedFiles.value);
47
47
  onCompleteCb.value && onCompleteCb.value();
48
48
  };
49
49
 
50
- const onRemove = (file) => {
50
+ const onRemove = (file: UploadedFile) => {
51
51
  const index = uploadedFiles.value.findIndex(f => f.id === file.id);
52
52
  if (index !== -1) {
53
53
  uploadedFiles.value.splice(index, 1);
@@ -57,7 +57,7 @@ export function useMultiFileUpload(options: FileUploadOptions) {
57
57
  };
58
58
 
59
59
  return {
60
- onClear,
60
+ clearUploadedFiles,
61
61
  onRemove,
62
62
  onComplete,
63
63
  onFilesChange,
@@ -1,19 +1,24 @@
1
- import { computed, ref } from "vue";
2
- import { FileUpload, FileUploadOptions } from "./FileUpload";
1
+ import { computed, Ref, ref } from "vue";
2
+ import { FileUpload, FileUploadOptions, UploadedFile } from "./FileUpload";
3
3
 
4
- export function useSingleFileUpload(options: FileUploadOptions = null) {
5
- const uploadedFile = ref(null);
6
- const onCompleteCb = ref(null);
7
- const onFileChangeCb = ref(null);
4
+ interface FileUploadCallbackParams {
5
+ file: UploadedFile;
6
+ uploadedFile: UploadedFile;
7
+ }
8
+
9
+ export function useSingleFileUpload(options: FileUploadOptions | null = null) {
10
+ const uploadedFile: Ref<UploadedFile | null> = ref(null);
11
+ const onCompleteCb: Ref<Function | null> = ref(null);
12
+ const onFileChangeCb: Ref<Function | null> = ref(null);
8
13
 
9
- const onFileSelected = (e) => {
14
+ const onFileSelected = (e: any) => {
10
15
  uploadedFile.value = null;
11
- new FileUpload(e.target.files[0], options)
12
- .onProgress(({ file }) => {
16
+ new FileUpload(e.target?.files[0], options)
17
+ .onProgress(({ file }: FileUploadCallbackParams) => {
13
18
  uploadedFile.value = file;
14
19
  onFileChangeCb.value && onFileChangeCb.value(uploadedFile.value);
15
20
  })
16
- .onComplete(({ uploadedFile: completedFile }) => {
21
+ .onComplete(({ uploadedFile: completedFile }: FileUploadCallbackParams) => {
17
22
  uploadedFile.value = completedFile;
18
23
  onCompleteCb.value && onCompleteCb.value(uploadedFile.value);
19
24
  onFileChangeCb.value && onFileChangeCb.value(uploadedFile.value);
@@ -21,30 +26,31 @@ export function useSingleFileUpload(options: FileUploadOptions = null) {
21
26
  .upload();
22
27
  };
23
28
 
24
- const onDrop = (e) => {
25
- onFileSelected({ target: { files: e.dataTransfer.files } });
29
+ const onDrop = (e: InputEvent) => {
30
+ onFileSelected({ target: { files: e.dataTransfer?.files } });
26
31
  };
27
32
 
28
33
  const isFileUploaded = computed(() => {
29
34
  return uploadedFile.value && uploadedFile.value.url;
30
35
  });
31
36
 
32
- const onFileChange = (cb) => {
37
+ const onFileChange = (cb: Function) => {
33
38
  onFileChangeCb.value = cb;
34
39
  };
35
40
 
36
- const onComplete = (cb) => {
41
+ const onComplete = (cb: Function) => {
37
42
  onCompleteCb.value = cb;
38
43
  };
39
44
 
40
- const onClear = () => {
45
+ const clearUploadedFile = () => {
41
46
  uploadedFile.value = null;
42
47
  onFileChangeCb.value && onFileChangeCb.value(uploadedFile.value);
48
+ onCompleteCb.value && onCompleteCb.value(uploadedFile.value);
43
49
  };
44
50
 
45
51
  return {
46
52
  isFileUploaded,
47
- onClear,
53
+ clearUploadedFile,
48
54
  onComplete,
49
55
  onFileChange,
50
56
  onDrop,
@@ -1,25 +1,18 @@
1
- import { watch } from "vue";
1
+ import { Ref, watch } from "vue";
2
2
 
3
3
  /**
4
- * Sleep function to be used in conjuction with async await:
4
+ * Sleep function to be used in conjunction with async await:
5
5
  *
6
6
  * eg: await sleep(5000);
7
- *
8
- * @param delay
9
- * @returns {Promise<any>}
10
7
  */
11
- export function sleep(delay) {
8
+ export function sleep(delay: number) {
12
9
  return new Promise((resolve) => setTimeout(resolve, delay));
13
10
  }
14
11
 
15
12
  /**
16
13
  * Wait for a ref to have a value and then resolve the promise
17
- *
18
- * @param ref
19
- * @param value
20
- * @returns {Promise<void>}
21
14
  */
22
- export function waitForRef(ref, value) {
15
+ export function waitForRef(ref: Ref, value: any) {
23
16
  return new Promise<void>((resolve) => {
24
17
  watch(ref, (newValue) => {
25
18
  if (newValue === value) {
@@ -31,39 +24,29 @@ export function waitForRef(ref, value) {
31
24
 
32
25
  /**
33
26
  * Returns a number that is constrained to the given range.
34
- * @param min
35
- * @param max
36
- * @param value
37
- * @returns {number}
38
27
  */
39
- export function minmax(min, max, value) {
28
+ export function minmax(min: number, max: number, value: number) {
40
29
  return Math.max(min, Math.min(max, value));
41
30
  }
42
31
 
43
32
  /**
44
33
  * Convert meters to miles
45
- * @param meters
46
- * @returns {number}
47
34
  */
48
- export function metersToMiles(meters) {
35
+ export function metersToMiles(meters: number) {
49
36
  return meters * 0.000621371;
50
37
  }
51
38
 
52
39
  /**
53
40
  * Convert miles to meters
54
- * @param miles
55
- * @returns {number}
56
41
  */
57
- export function milesToMeters(miles) {
42
+ export function milesToMeters(miles: number) {
58
43
  return miles / 0.000621371;
59
44
  }
60
45
 
61
46
  /**
62
47
  * Parses a string for Lat and Long coords
63
- * @param location
64
- * @returns {null|{lng: number, lat: number}}
65
48
  */
66
- export function parseCoords(location) {
49
+ export function parseCoords(location: string) {
67
50
  const latLong = location.split(",");
68
51
 
69
52
  if (latLong.length === 2) {
@@ -80,3 +63,15 @@ export function parseCoords(location) {
80
63
 
81
64
  return null;
82
65
  }
66
+
67
+ /**
68
+ * Increment a name by adding a number to the end of it or incrementing the number if it already exists
69
+ */
70
+ export function incrementName(name: string) {
71
+ name = (name || "New Item").trim();
72
+ const match = name.match(/(\d+)$/);
73
+ if (match) {
74
+ return name.replace(/\d+$/, (match: string) => "" + (parseInt(match) + 1));
75
+ }
76
+ return `${name} 1`;
77
+ }
@@ -1,3 +1,7 @@
1
+ .q-tab {
2
+ text-transform: capitalize
3
+ }
4
+
1
5
  .q-tab-panels {
2
6
  overflow: visible;
3
7