quasar-ui-danx 0.2.32 → 0.3.1
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.
- package/dist/danx.es.js +6582 -6372
- package/dist/danx.es.js.map +1 -1
- package/dist/danx.umd.js +5 -5
- package/dist/danx.umd.js.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/components/ActionTable/ActionTableColumn.vue +5 -2
- package/src/components/ActionTable/ActionTableHeaderColumn.vue +2 -1
- package/src/components/ActionTable/Filters/FilterListToggle.vue +1 -2
- package/src/components/ActionTable/Form/Fields/BooleanField.vue +9 -2
- package/src/components/ActionTable/Form/Fields/MultiFileField.vue +2 -2
- package/src/components/ActionTable/Form/Fields/SelectField.vue +1 -1
- package/src/components/ActionTable/Form/Fields/SingleFileField.vue +2 -2
- package/src/components/ActionTable/Form/RenderedForm.vue +148 -10
- package/src/components/ActionTable/listControls.ts +68 -38
- package/src/components/ActionTable/tableColumns.ts +3 -3
- package/src/components/PanelsDrawer/PanelsDrawerPanels.vue +1 -1
- package/src/components/Utility/Tools/RenderVnode.vue +18 -2
- package/src/config/index.ts +2 -7
- package/src/helpers/FileUpload.ts +18 -20
- package/src/helpers/actions.ts +34 -31
- package/src/helpers/array.ts +11 -16
- package/src/helpers/multiFileUpload.ts +16 -16
- package/src/helpers/singleFileUpload.ts +22 -16
- package/src/helpers/utils.ts +20 -25
- package/src/styles/quasar-reset.scss +4 -0
@@ -5,8 +5,8 @@ import { FlashMessages } from "./FlashMessages";
|
|
5
5
|
|
6
6
|
export type FileUploadOptions = {
|
7
7
|
directory?: string,
|
8
|
-
presignedUploadUrl?:
|
9
|
-
uploadCompletedUrl?:
|
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
|
-
|
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
|
}
|
package/src/helpers/actions.ts
CHANGED
@@ -1,17 +1,23 @@
|
|
1
|
-
import {
|
1
|
+
import { useDebounceFn } from "@vueuse/core";
|
2
|
+
import { Ref, shallowRef, VNode } from "vue";
|
2
3
|
import { FlashMessages } from "./FlashMessages";
|
3
4
|
|
4
|
-
type
|
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:
|
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 (
|
40
|
-
mappedAction.trigger = (target, input) => performAction(mappedAction, target, input);
|
41
|
-
|
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
|
-
*
|
54
|
+
* Set the reactive saving state of a target
|
48
55
|
*/
|
49
|
-
function
|
50
|
-
if (
|
51
|
-
|
52
|
-
|
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
|
80
|
-
|
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
|
-
|
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
|
};
|
package/src/helpers/array.ts
CHANGED
@@ -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
|
-
|
11
|
-
|
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
|
-
|
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
|
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
|
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
|
-
|
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
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
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
|
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
|
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
|
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
|
-
|
53
|
+
clearUploadedFile,
|
48
54
|
onComplete,
|
49
55
|
onFileChange,
|
50
56
|
onDrop,
|
package/src/helpers/utils.ts
CHANGED
@@ -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
|
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
|
+
}
|