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.
- 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
|
+
}
|