quasar-ui-danx 0.2.31 → 0.3.0
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 +6560 -6313
- 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/ActionTable.vue +9 -45
- package/src/components/ActionTable/ActionTableColumn.vue +5 -2
- package/src/components/ActionTable/ActionTableHeaderColumn.vue +83 -0
- package/src/components/ActionTable/Filters/FilterListToggle.vue +1 -2
- package/src/components/ActionTable/Form/Fields/BooleanField.vue +9 -2
- package/src/components/ActionTable/Form/RenderedForm.vue +146 -10
- package/src/components/ActionTable/index.ts +1 -0
- 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 +16 -2
- package/src/helpers/actions.ts +34 -31
- package/src/helpers/array.ts +11 -16
- package/src/helpers/utils.ts +20 -25
- package/src/styles/quasar-reset.scss +4 -0
@@ -1,5 +1,19 @@
|
|
1
1
|
<script>
|
2
|
-
const RenderVnode = (props) =>
|
3
|
-
|
2
|
+
const RenderVnode = (props) => {
|
3
|
+
if (props.vnode.__v_isVNode) {
|
4
|
+
return props.vnode;
|
5
|
+
}
|
6
|
+
|
7
|
+
if (props.vnode.__v_isRef) {
|
8
|
+
return props.vnode.value;
|
9
|
+
}
|
10
|
+
|
11
|
+
if (typeof props.vnode === "function") {
|
12
|
+
return props.vnode();
|
13
|
+
}
|
14
|
+
|
15
|
+
return null;
|
16
|
+
};
|
17
|
+
RenderVnode.props = { vnode: { type: [Function, Object], required: true } };
|
4
18
|
export default RenderVnode;
|
5
19
|
</script>
|
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));
|
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
|
+
}
|