quasar-ui-danx 0.4.26 → 0.4.28
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/danx.es.js +24536 -24082
- package/dist/danx.es.js.map +1 -1
- package/dist/danx.umd.js +109 -109
- 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 +29 -7
- package/src/components/ActionTable/Filters/FilterableField.vue +14 -2
- package/src/components/ActionTable/Form/ActionForm.vue +17 -12
- package/src/components/ActionTable/Form/Fields/DateField.vue +24 -20
- package/src/components/ActionTable/Form/Fields/DateRangeField.vue +57 -53
- package/src/components/ActionTable/Form/Fields/EditOnClickTextField.vue +9 -2
- package/src/components/ActionTable/Form/Fields/EditableDiv.vue +12 -12
- package/src/components/ActionTable/Form/Fields/FieldLabel.vue +1 -1
- package/src/components/ActionTable/Form/Fields/SelectField.vue +27 -6
- package/src/components/ActionTable/Form/Fields/SelectOrCreateField.vue +56 -0
- package/src/components/ActionTable/Form/Fields/index.ts +1 -0
- package/src/components/ActionTable/Layouts/ActionTableLayout.vue +20 -23
- package/src/components/ActionTable/Toolbars/ActionToolbar.vue +44 -36
- package/src/components/ActionTable/listControls.ts +3 -3
- package/src/components/DragAndDrop/ListItemDraggable.vue +38 -28
- package/src/components/DragAndDrop/dragAndDrop.ts +220 -220
- package/src/components/DragAndDrop/listDragAndDrop.ts +256 -227
- package/src/components/PanelsDrawer/PanelsDrawer.vue +7 -7
- package/src/components/PanelsDrawer/PanelsDrawerTabs.vue +3 -3
- package/src/components/Utility/Buttons/ShowHideButton.vue +86 -0
- package/src/components/Utility/Buttons/index.ts +1 -0
- package/src/components/Utility/Dialogs/ActionFormDialog.vue +30 -0
- package/src/components/Utility/Dialogs/CreateNewWithNameDialog.vue +26 -0
- package/src/components/Utility/Dialogs/RenderedFormDialog.vue +50 -0
- package/src/components/Utility/Dialogs/index.ts +3 -0
- package/src/helpers/actions.ts +84 -20
- package/src/helpers/formats.ts +23 -21
- package/src/helpers/objectStore.ts +24 -12
- package/src/types/actions.d.ts +12 -6
- package/src/types/controls.d.ts +23 -6
- package/types/vue-shims.d.ts +3 -2
@@ -0,0 +1,30 @@
|
|
1
|
+
<template>
|
2
|
+
<ConfirmDialog
|
3
|
+
:title="title"
|
4
|
+
:confirm-text="confirmText || title"
|
5
|
+
@confirm="$emit('confirm', {name})"
|
6
|
+
@close="$emit('close')"
|
7
|
+
>
|
8
|
+
<ActionForm
|
9
|
+
:form="form"
|
10
|
+
:action="action"
|
11
|
+
:target="target"
|
12
|
+
/>
|
13
|
+
</ConfirmDialog>
|
14
|
+
</template>
|
15
|
+
<script setup lang="ts">
|
16
|
+
import { ref } from "vue";
|
17
|
+
import { ActionTargetItem, Form, ResourceAction } from "../../../types";
|
18
|
+
import { ActionForm } from "../../ActionTable";
|
19
|
+
import ConfirmDialog from "./ConfirmDialog";
|
20
|
+
|
21
|
+
defineEmits(["confirm", "close"]);
|
22
|
+
defineProps<{
|
23
|
+
title: string;
|
24
|
+
confirmText?: string;
|
25
|
+
form: Form;
|
26
|
+
action: ResourceAction;
|
27
|
+
target: ActionTargetItem;
|
28
|
+
}>();
|
29
|
+
const name = ref("");
|
30
|
+
</script>
|
@@ -0,0 +1,26 @@
|
|
1
|
+
<template>
|
2
|
+
<ConfirmDialog
|
3
|
+
:title="title"
|
4
|
+
:confirm-text="confirmText || title"
|
5
|
+
@confirm="$emit('confirm', {name})"
|
6
|
+
@close="$emit('close')"
|
7
|
+
>
|
8
|
+
<TextField
|
9
|
+
v-model="name"
|
10
|
+
label="Name"
|
11
|
+
/>
|
12
|
+
<slot />
|
13
|
+
</ConfirmDialog>
|
14
|
+
</template>
|
15
|
+
<script setup lang="ts">
|
16
|
+
import { ref } from "vue";
|
17
|
+
import { TextField } from "../../ActionTable";
|
18
|
+
import ConfirmDialog from "./ConfirmDialog";
|
19
|
+
|
20
|
+
defineEmits(["confirm", "close"]);
|
21
|
+
defineProps<{
|
22
|
+
title: string;
|
23
|
+
confirmText?: string;
|
24
|
+
}>();
|
25
|
+
const name = ref("");
|
26
|
+
</script>
|
@@ -0,0 +1,50 @@
|
|
1
|
+
<template>
|
2
|
+
<ConfirmDialog
|
3
|
+
:title="title"
|
4
|
+
:confirm-text="confirmText || title"
|
5
|
+
:content-class="contentClass"
|
6
|
+
@confirm="$emit('confirm', input)"
|
7
|
+
@close="$emit('close')"
|
8
|
+
>
|
9
|
+
<RenderedForm
|
10
|
+
v-bind="renderedFormProps"
|
11
|
+
v-model:values="input"
|
12
|
+
empty-value=""
|
13
|
+
>
|
14
|
+
<slot />
|
15
|
+
</RenderedForm>
|
16
|
+
</ConfirmDialog>
|
17
|
+
</template>
|
18
|
+
<script setup lang="ts">
|
19
|
+
import { AnyObject, Form } from "../../../types";
|
20
|
+
import { RenderedForm } from "../../ActionTable/Form";
|
21
|
+
import ConfirmDialog from "./ConfirmDialog";
|
22
|
+
|
23
|
+
defineEmits(["confirm", "close"]);
|
24
|
+
const props = defineProps<{
|
25
|
+
title: string;
|
26
|
+
confirmText?: string;
|
27
|
+
form: Form;
|
28
|
+
noLabel?: boolean;
|
29
|
+
showName?: boolean;
|
30
|
+
disable?: boolean;
|
31
|
+
readonly?: boolean;
|
32
|
+
clearable?: boolean;
|
33
|
+
fieldClass?: string;
|
34
|
+
savingClass?: string;
|
35
|
+
hideSavedAt?: boolean;
|
36
|
+
contentClass?: string;
|
37
|
+
}>();
|
38
|
+
const input = defineModel<AnyObject>();
|
39
|
+
|
40
|
+
const renderedFormProps = {
|
41
|
+
form: props.form,
|
42
|
+
noLabel: props.noLabel,
|
43
|
+
showName: props.showName,
|
44
|
+
disable: props.disable,
|
45
|
+
readonly: props.readonly,
|
46
|
+
clearable: props.clearable,
|
47
|
+
fieldClass: props.fieldClass,
|
48
|
+
savingClass: props.savingClass
|
49
|
+
};
|
50
|
+
</script>
|
@@ -1,6 +1,9 @@
|
|
1
|
+
export { default as ActionFormDialog } from "./ActionFormDialog.vue";
|
1
2
|
export { default as ConfirmActionDialog } from "./ConfirmActionDialog.vue";
|
2
3
|
export { default as ConfirmDialog } from "./ConfirmDialog.vue";
|
4
|
+
export { default as CreateNewWithNameDialog } from "./CreateNewWithNameDialog.vue";
|
3
5
|
export { default as FullScreenCarouselDialog } from "./FullscreenCarouselDialog.vue";
|
4
6
|
export { default as FullScreenDialog } from "./FullScreenDialog.vue";
|
5
7
|
export { default as InfoDialog } from "./InfoDialog.vue";
|
6
8
|
export { default as InputDialog } from "./InputDialog.vue";
|
9
|
+
export { default as RenderedFormDialog } from "./RenderedFormDialog.vue";
|
package/src/helpers/actions.ts
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
import { useDebounceFn } from "@vueuse/core";
|
2
|
+
import { FaSolidCopy as CopyIcon, FaSolidPencil as EditIcon, FaSolidTrash as DeleteIcon } from "danx-icon";
|
2
3
|
import { uid } from "quasar";
|
3
|
-
import { isReactive, Ref, shallowRef } from "vue";
|
4
|
-
import
|
4
|
+
import { h, isReactive, Ref, shallowRef } from "vue";
|
5
|
+
import { ConfirmActionDialog, CreateNewWithNameDialog } from "../components";
|
6
|
+
import type { ActionGlobalOptions, ActionOptions, ActionTarget, ListController, ResourceAction } from "../types";
|
5
7
|
import { FlashMessages } from "./FlashMessages";
|
6
8
|
import { storeObject } from "./objectStore";
|
7
9
|
|
@@ -11,7 +13,7 @@ export const activeActionVnode: Ref = shallowRef(null);
|
|
11
13
|
* Hook to perform an action on a set of targets
|
12
14
|
* This helper allows you to perform actions by name on a set of targets using a provided list of actions
|
13
15
|
*/
|
14
|
-
export function useActions(actions: ActionOptions[], globalOptions:
|
16
|
+
export function useActions(actions: ActionOptions[], globalOptions: ActionGlobalOptions | null = null) {
|
15
17
|
const namespace = uid();
|
16
18
|
|
17
19
|
/**
|
@@ -33,6 +35,14 @@ export function useActions(actions: ActionOptions[], globalOptions: Partial<Acti
|
|
33
35
|
return storeObject(extendedAction);
|
34
36
|
}
|
35
37
|
|
38
|
+
/**
|
39
|
+
* Updates an action replacing the old options with the new options
|
40
|
+
*/
|
41
|
+
function modifyAction(actionName: string, actionOptions: Partial<ActionOptions>): ResourceAction {
|
42
|
+
const action = getAction(actionName);
|
43
|
+
return storeObject({ ...action, ...actionOptions });
|
44
|
+
}
|
45
|
+
|
36
46
|
/**
|
37
47
|
* Resolve the action object based on the provided name (or return the object if the name is already an object)
|
38
48
|
*/
|
@@ -48,6 +58,9 @@ export function useActions(actions: ActionOptions[], globalOptions: Partial<Acti
|
|
48
58
|
if (isReactive(baseOptions) && "__type" in baseOptions) return baseOptions as ResourceAction;
|
49
59
|
|
50
60
|
const resourceAction: ResourceAction = storeObject({
|
61
|
+
onAction: globalOptions?.routes?.applyAction,
|
62
|
+
onBatchAction: globalOptions?.routes?.batchAction,
|
63
|
+
onBatchSuccess: globalOptions?.controls?.clearSelectedRows,
|
51
64
|
...globalOptions,
|
52
65
|
...baseOptions,
|
53
66
|
trigger: (target, input) => performAction(resourceAction, target, input),
|
@@ -60,27 +73,22 @@ export function useActions(actions: ActionOptions[], globalOptions: Partial<Acti
|
|
60
73
|
resourceAction.trigger = useDebounceFn((target, input) => performAction(resourceAction, target, input), baseOptions.debounce);
|
61
74
|
}
|
62
75
|
|
76
|
+
// Splice the resourceAction in place of the action in the actions list
|
77
|
+
actions.splice(actions.findIndex(a => a.name === actionName), 1, resourceAction);
|
78
|
+
|
63
79
|
return resourceAction;
|
64
80
|
}
|
65
81
|
|
66
82
|
/**
|
67
|
-
*
|
68
|
-
*
|
69
|
-
*
|
70
|
-
* @param filters
|
71
|
-
* @returns {ActionOptions[]}
|
83
|
+
* Returns a filtered list of actions. Useful for building ordered menus.
|
84
|
+
* NOTE: If an action doesn't already exist, it will be created.
|
72
85
|
*/
|
73
|
-
function getActions(
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
for (const filterKey of Object.keys(filters)) {
|
78
|
-
const filterValue = filters[filterKey];
|
79
|
-
filteredActions = filteredActions.filter((a: AnyObject) => a[filterKey] === filterValue || (Array.isArray(filterValue) && filterValue.includes(a[filterKey])));
|
80
|
-
}
|
86
|
+
function getActions(names: string[]): ResourceAction[] {
|
87
|
+
const filteredActions = [];
|
88
|
+
for (const name of names) {
|
89
|
+
filteredActions.push(getAction(name));
|
81
90
|
}
|
82
|
-
|
83
|
-
return filteredActions.map((a: ActionOptions) => getAction(a.name));
|
91
|
+
return filteredActions;
|
84
92
|
}
|
85
93
|
|
86
94
|
/**
|
@@ -90,11 +98,15 @@ export function useActions(actions: ActionOptions[], globalOptions: Partial<Acti
|
|
90
98
|
* @param {object[]|object} target - an array of targets or a single target object
|
91
99
|
* @param {any} input - The input data to pass to the action handler
|
92
100
|
*/
|
93
|
-
async function performAction(action: ResourceAction, target: ActionTarget = null, input: any = null) {
|
101
|
+
async function performAction(action: ResourceAction | string, target: ActionTarget = null, input: any = null): Promise<any | void> {
|
102
|
+
if (typeof action === "string") {
|
103
|
+
action = getAction(action);
|
104
|
+
}
|
105
|
+
|
94
106
|
// Resolve the original action, if the current action is an alias
|
95
107
|
const aliasedAction = action.alias ? getAction(action.alias) : null;
|
96
108
|
|
97
|
-
const vnode = action.vnode && action.vnode(target);
|
109
|
+
const vnode = action.vnode && action.vnode(target, input);
|
98
110
|
let result: any;
|
99
111
|
|
100
112
|
// Run the onStart handler if it exists and quit the operation if it returns false
|
@@ -160,6 +172,8 @@ export function useActions(actions: ActionOptions[], globalOptions: Partial<Acti
|
|
160
172
|
return {
|
161
173
|
getAction,
|
162
174
|
getActions,
|
175
|
+
action: performAction,
|
176
|
+
modifyAction,
|
163
177
|
extendAction
|
164
178
|
};
|
165
179
|
}
|
@@ -271,3 +285,53 @@ async function onConfirmAction(action: ActionOptions, target: ActionTarget, inpu
|
|
271
285
|
|
272
286
|
return result;
|
273
287
|
}
|
288
|
+
|
289
|
+
export function withDefaultActions(label: string, listController?: ListController): ActionOptions[] {
|
290
|
+
return [
|
291
|
+
{
|
292
|
+
name: "create",
|
293
|
+
label: "Create " + label,
|
294
|
+
vnode: () => h(CreateNewWithNameDialog, { title: "Create " + label }),
|
295
|
+
onFinish: listController && ((result) => {
|
296
|
+
listController.activatePanel(result.item, "edit");
|
297
|
+
listController.loadListAndSummary();
|
298
|
+
})
|
299
|
+
},
|
300
|
+
{
|
301
|
+
name: "update",
|
302
|
+
optimistic: true
|
303
|
+
},
|
304
|
+
{
|
305
|
+
name: "update-debounced",
|
306
|
+
alias: "update",
|
307
|
+
debounce: 1000,
|
308
|
+
optimistic: true
|
309
|
+
},
|
310
|
+
{
|
311
|
+
name: "copy",
|
312
|
+
label: "Copy",
|
313
|
+
icon: CopyIcon,
|
314
|
+
onSuccess: listController?.loadListAndSummary
|
315
|
+
},
|
316
|
+
{
|
317
|
+
name: "edit",
|
318
|
+
label: "Edit",
|
319
|
+
icon: EditIcon,
|
320
|
+
onAction: (action, target) => listController?.activatePanel(target, "edit")
|
321
|
+
},
|
322
|
+
{
|
323
|
+
name: "delete",
|
324
|
+
label: "Delete",
|
325
|
+
class: "text-red-500",
|
326
|
+
iconClass: "text-red-500",
|
327
|
+
icon: DeleteIcon,
|
328
|
+
onFinish: listController?.loadListAndSummary,
|
329
|
+
vnode: (target: ActionTarget) => h(ConfirmActionDialog, {
|
330
|
+
action: "Delete",
|
331
|
+
label,
|
332
|
+
target,
|
333
|
+
confirmClass: "bg-red-900"
|
334
|
+
})
|
335
|
+
}
|
336
|
+
];
|
337
|
+
}
|
package/src/helpers/formats.ts
CHANGED
@@ -32,32 +32,35 @@ export function remoteDateTime(dateTimeString: string) {
|
|
32
32
|
/**
|
33
33
|
* Parses a date string into a Luxon DateTime object
|
34
34
|
*/
|
35
|
-
export function parseDateTime(dateTime: string | DateTime | null): DateTime {
|
35
|
+
export function parseDateTime(dateTime: string | DateTime | null): DateTime<boolean> | null {
|
36
36
|
if (typeof dateTime === "string") {
|
37
|
-
dateTime
|
38
|
-
return DateTime.fromSQL(dateTime);
|
37
|
+
return parseSqlDateTime(dateTime) || parseQDate(dateTime) || parseQDateTime(dateTime);
|
39
38
|
}
|
40
39
|
return dateTime || DateTime.fromSQL("0000-00-00 00:00:00");
|
41
40
|
}
|
42
41
|
|
42
|
+
/**
|
43
|
+
* Parses a SQL formatted date string into a Luxon DateTime object
|
44
|
+
*/
|
45
|
+
export function parseSqlDateTime(dateTime: string) {
|
46
|
+
const parsed = DateTime.fromSQL(dateTime.replace("T", " ").replace(/\//g, "-"));
|
47
|
+
return parsed.isValid ? parsed : null;
|
48
|
+
}
|
49
|
+
|
43
50
|
/**
|
44
51
|
* Parses a Quasar formatted date string into a Luxon DateTime object
|
45
|
-
* @param date
|
46
|
-
* @param format
|
47
|
-
* @returns {DateTime}
|
48
52
|
*/
|
49
|
-
export function parseQDate(date: string, format = "yyyy/MM/dd") {
|
50
|
-
|
53
|
+
export function parseQDate(date: string, format = "yyyy/MM/dd"): DateTime<boolean> | null {
|
54
|
+
const parsed = DateTime.fromFormat(date, format);
|
55
|
+
return parsed.isValid ? parsed : null;
|
51
56
|
}
|
52
57
|
|
53
58
|
/**
|
54
59
|
* Parses a Quasar formatted date/time string into a Luxon DateTime object
|
55
|
-
* @param date
|
56
|
-
* @param format
|
57
|
-
* @returns {DateTime}
|
58
60
|
*/
|
59
|
-
export function parseQDateTime(date: string, format = "yyyy/MM/dd HH:mm:ss") {
|
60
|
-
|
61
|
+
export function parseQDateTime(date: string, format = "yyyy/MM/dd HH:mm:ss"): DateTime<boolean> | null {
|
62
|
+
const parsed = DateTime.fromFormat(date, format);
|
63
|
+
return parsed.isValid ? parsed : null;
|
61
64
|
}
|
62
65
|
|
63
66
|
/**
|
@@ -91,8 +94,8 @@ export function fDateTime(
|
|
91
94
|
dateTime: string | DateTime | null = null,
|
92
95
|
{ format = "M/d/yy h:mma", empty = "- -" }: fDateOptions = {}
|
93
96
|
) {
|
94
|
-
const formatted = parseDateTime(dateTime)
|
95
|
-
return
|
97
|
+
const formatted = parseDateTime(dateTime)?.toFormat(format).toLowerCase();
|
98
|
+
return formatted || empty;
|
96
99
|
}
|
97
100
|
|
98
101
|
/**
|
@@ -111,9 +114,9 @@ export function dbDateTime(dateTime: string | DateTime | null = null) {
|
|
111
114
|
* @param format
|
112
115
|
* @returns {string}
|
113
116
|
*/
|
114
|
-
export function fDate(dateTime: string, { empty = "--", format = "M/d/yy" }: fDateOptions = {}) {
|
115
|
-
const formatted = parseDateTime(dateTime)
|
116
|
-
return
|
117
|
+
export function fDate(dateTime: string | DateTime | null, { empty = "--", format = "M/d/yy" }: fDateOptions = {}) {
|
118
|
+
const formatted = parseDateTime(dateTime)?.toFormat(format || "M/d/yy");
|
119
|
+
return formatted || empty;
|
117
120
|
}
|
118
121
|
|
119
122
|
/**
|
@@ -130,8 +133,8 @@ export function fSecondsToTime(second: number) {
|
|
130
133
|
|
131
134
|
export function fElapsedTime(start: string, end?: string) {
|
132
135
|
const endDateTime = end ? parseDateTime(end) : DateTime.now();
|
133
|
-
const diff = endDateTime
|
134
|
-
if (!diff
|
136
|
+
const diff = endDateTime?.diff(parseDateTime(start) || DateTime.now(), ["hours", "minutes", "seconds"]);
|
137
|
+
if (!diff?.isValid) {
|
135
138
|
return "-";
|
136
139
|
}
|
137
140
|
const hours = Math.floor(diff.hours);
|
@@ -384,7 +387,6 @@ export function parseMarkdownCode(string: string): string {
|
|
384
387
|
* ie: a valid JSON string with a ```json prefix and ``` postfix
|
385
388
|
*/
|
386
389
|
export function fMarkdownCode(type: string, string: string | object): string {
|
387
|
-
console.log("formatting", type, string);
|
388
390
|
if (typeof string === "object" || isJSON(string)) {
|
389
391
|
switch (type) {
|
390
392
|
case "yaml":
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import { uid } from "quasar";
|
2
2
|
import { ShallowReactive, shallowReactive } from "vue";
|
3
|
-
import { TypedObject } from "../types";
|
3
|
+
import { AnyObject, TypedObject } from "../types";
|
4
4
|
import { FlashMessages } from "./FlashMessages";
|
5
5
|
|
6
6
|
const store = new Map<string, any>();
|
@@ -18,7 +18,7 @@ export function storeObjects<T extends TypedObject>(newObjects: T[]) {
|
|
18
18
|
* Store an object in the object store via type + id
|
19
19
|
* Returns the stored object that should be used instead of the passed object as the returned object is shared across the system
|
20
20
|
*/
|
21
|
-
export function storeObject<T extends TypedObject>(newObject: T): ShallowReactive<T> {
|
21
|
+
export function storeObject<T extends TypedObject>(newObject: T, recentlyStoredObjects: AnyObject = {}): ShallowReactive<T> {
|
22
22
|
const id = newObject?.id || newObject?.name;
|
23
23
|
const type = newObject?.__type;
|
24
24
|
if (!id || !type) return shallowReactive(newObject);
|
@@ -32,38 +32,50 @@ export function storeObject<T extends TypedObject>(newObject: T): ShallowReactiv
|
|
32
32
|
|
33
33
|
const objectKey = `${type}:${id}`;
|
34
34
|
|
35
|
+
// If the object was recently stored, return the recently stored object to avoid infinite recursion
|
36
|
+
if (recentlyStoredObjects[objectKey]) {
|
37
|
+
return recentlyStoredObjects[objectKey];
|
38
|
+
}
|
39
|
+
|
35
40
|
// Retrieve the existing object if it already exists in the store
|
36
41
|
const oldObject = store.get(objectKey);
|
37
42
|
|
38
43
|
// If an old object exists, and it is newer than the new object, do not store the new object, just return the old
|
44
|
+
// NOTE: If the timestamp is the same, its possible the intention is to update the existing object, so DO NOT return old object in this case
|
39
45
|
// @ts-expect-error __timestamp is guaranteed to be set in this case on both old and new
|
40
|
-
if (oldObject && newObject.__timestamp
|
46
|
+
if (oldObject && newObject.__timestamp < oldObject.__timestamp) {
|
47
|
+
recentlyStoredObjects[objectKey] = oldObject;
|
41
48
|
return oldObject;
|
42
49
|
}
|
43
50
|
|
51
|
+
// Reference to the reactive version of the object so we can update all the child relationships and return the reactive object
|
52
|
+
const reactiveObject = oldObject || shallowReactive(newObject);
|
53
|
+
|
54
|
+
// Make sure to store the object in the recently stored objects to avoid infinite recursion
|
55
|
+
recentlyStoredObjects[objectKey] = reactiveObject;
|
56
|
+
|
44
57
|
// Recursively store all the children of the object as well
|
45
58
|
for (const key of Object.keys(newObject)) {
|
46
59
|
const value = newObject[key];
|
47
60
|
if (Array.isArray(value) && value.length > 0) {
|
48
61
|
for (const index in value) {
|
49
62
|
if (value[index] && typeof value[index] === "object") {
|
50
|
-
newObject[key][index] = storeObject(value[index]);
|
63
|
+
newObject[key][index] = storeObject(value[index], recentlyStoredObjects);
|
51
64
|
}
|
52
65
|
}
|
53
66
|
} else if (value?.__type) {
|
54
|
-
// @ts-expect-error
|
55
|
-
newObject[key] = storeObject(value);
|
67
|
+
// @ts-expect-error __type is guaranteed to be set in this case
|
68
|
+
newObject[key] = storeObject(value as TypedObject, recentlyStoredObjects);
|
56
69
|
}
|
57
70
|
}
|
58
71
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
72
|
+
Object.assign(reactiveObject, newObject);
|
73
|
+
|
74
|
+
if (!oldObject) {
|
75
|
+
// Store the reactive object in the store if there was not already one existing
|
76
|
+
store.set(objectKey, reactiveObject);
|
63
77
|
}
|
64
78
|
|
65
|
-
const reactiveObject = shallowReactive(newObject);
|
66
|
-
store.set(objectKey, reactiveObject);
|
67
79
|
return reactiveObject;
|
68
80
|
}
|
69
81
|
|
package/src/types/actions.d.ts
CHANGED
@@ -1,14 +1,15 @@
|
|
1
|
+
import { ListController, ListControlsRoutes } from "src/types/controls";
|
1
2
|
import { VNode } from "vue";
|
2
|
-
import { TypedObject } from "./shared";
|
3
|
+
import { AnyObject, TypedObject } from "./shared";
|
3
4
|
|
4
5
|
export interface ActionPanel {
|
5
6
|
name: string | number;
|
6
7
|
label: string;
|
7
8
|
category?: string;
|
8
9
|
class?: string | object;
|
9
|
-
enabled?: boolean | ((
|
10
|
-
tabVnode?: (
|
11
|
-
vnode: (
|
10
|
+
enabled?: boolean | ((target: ActionTargetItem) => boolean);
|
11
|
+
tabVnode?: (target: ActionTargetItem | null | undefined, activePanel: string | number) => VNode | any;
|
12
|
+
vnode: (target: ActionTargetItem | null | undefined) => VNode | any;
|
12
13
|
}
|
13
14
|
|
14
15
|
export interface ActionTargetItem extends TypedObject {
|
@@ -33,8 +34,8 @@ export interface ActionOptions {
|
|
33
34
|
vnode?: ((target: ActionTarget) => VNode) | any;
|
34
35
|
enabled?: (target: object) => boolean;
|
35
36
|
batchEnabled?: (targets: object[]) => boolean;
|
36
|
-
onAction?: (action: string |
|
37
|
-
onBatchAction?: (action: string |
|
37
|
+
onAction?: (action: string | ResourceAction | ActionOptions, target: ActionTargetItem | null, input?: AnyObject | any) => Promise<AnyObject | any> | void;
|
38
|
+
onBatchAction?: (action: string | ResourceAction | ActionOptions, targets: ActionTargetItem[], input: any) => Promise<AnyObject | any> | void;
|
38
39
|
onStart?: (action: ActionOptions | null, targets: ActionTarget, input: any) => boolean;
|
39
40
|
onSuccess?: (result: any, targets: ActionTarget, input: any) => any;
|
40
41
|
onBatchSuccess?: (result: any, targets: ActionTargetItem[], input: any) => any;
|
@@ -42,6 +43,11 @@ export interface ActionOptions {
|
|
42
43
|
onFinish?: (result: any, targets: ActionTarget, input: any) => any;
|
43
44
|
}
|
44
45
|
|
46
|
+
export interface ActionGlobalOptions extends Partial<ActionOptions> {
|
47
|
+
routes?: ListControlsRoutes;
|
48
|
+
controls?: ListController;
|
49
|
+
}
|
50
|
+
|
45
51
|
export interface ResourceAction extends ActionOptions {
|
46
52
|
isApplying: boolean;
|
47
53
|
trigger: (target?: ActionTarget, input?: any) => Promise<any>;
|
package/src/types/controls.d.ts
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
+
import { FormField } from "src/types/forms";
|
2
|
+
import { TableColumn } from "src/types/tables";
|
1
3
|
import { ComputedRef, Ref, ShallowRef } from "vue";
|
2
|
-
import { ActionTargetItem } from "./actions";
|
4
|
+
import { ActionOptions, ActionPanel, ActionTargetItem, ResourceAction } from "./actions";
|
3
5
|
import { AnyObject, LabelValueItem } from "./shared";
|
4
6
|
|
5
7
|
export interface ListControlsFilter {
|
@@ -20,7 +22,6 @@ export interface FilterGroup {
|
|
20
22
|
fields: FilterableField[];
|
21
23
|
}
|
22
24
|
|
23
|
-
|
24
25
|
export interface ListControlsRoutes {
|
25
26
|
list(pager?: ListControlsPagination): Promise<ActionTargetItem[]>;
|
26
27
|
|
@@ -36,9 +37,9 @@ export interface ListControlsRoutes {
|
|
36
37
|
|
37
38
|
fieldOptions?(filter?: AnyObject): Promise<AnyObject>;
|
38
39
|
|
39
|
-
applyAction?(action: string, target: ActionTargetItem | null, data?: object): Promise<AnyObject>;
|
40
|
+
applyAction?(action: string | ResourceAction | ActionOptions, target: ActionTargetItem | null, data?: object): Promise<AnyObject>;
|
40
41
|
|
41
|
-
batchAction?(action: string, targets: ActionTargetItem[], data: object): Promise<AnyObject>;
|
42
|
+
batchAction?(action: string | ResourceAction | ActionOptions, targets: ActionTargetItem[], data: object): Promise<AnyObject>;
|
42
43
|
|
43
44
|
export?(filter?: ListControlsFilter, name?: string): Promise<void>;
|
44
45
|
}
|
@@ -70,7 +71,7 @@ export interface PagedItems {
|
|
70
71
|
} | undefined;
|
71
72
|
}
|
72
73
|
|
73
|
-
export interface
|
74
|
+
export interface ListController {
|
74
75
|
name: string;
|
75
76
|
label: string;
|
76
77
|
pagedItems: Ref<PagedItems | null>;
|
@@ -93,7 +94,7 @@ export interface ActionController {
|
|
93
94
|
activeItem: ShallowRef<ActionTargetItem | null>;
|
94
95
|
activePanel: ShallowRef<string | null>;
|
95
96
|
|
96
|
-
//
|
97
|
+
// List Controls
|
97
98
|
initialize: () => void;
|
98
99
|
resetPaging: () => void;
|
99
100
|
setPagination: (updated: Partial<ListControlsPagination>) => void;
|
@@ -114,3 +115,19 @@ export interface ActionController {
|
|
114
115
|
applyFilterFromUrl: (url: string, filters?: Ref<FilterGroup[]> | null) => void;
|
115
116
|
getFieldOptions: (field: string) => any[];
|
116
117
|
}
|
118
|
+
|
119
|
+
export interface ActionController {
|
120
|
+
// Actions
|
121
|
+
action?: (actionName: string, target?: ActionTargetItem | null, input?: any) => Promise<any | void>;
|
122
|
+
getAction?: (actionName: string, actionOptions?: Partial<ActionOptions>) => ResourceAction;
|
123
|
+
getActions?: (names?: string[]) => ResourceAction[];
|
124
|
+
extendAction?: (actionName: string, extendedId: string | number, actionOptions: Partial<ActionOptions>) => ResourceAction;
|
125
|
+
modifyAction?: (actionName: string, actionOptions: Partial<ActionOptions>) => ResourceAction;
|
126
|
+
batchActions?: ResourceAction[];
|
127
|
+
menuActions?: ResourceAction[];
|
128
|
+
columns?: TableColumn[];
|
129
|
+
filters?: ComputedRef<FilterGroup[]>;
|
130
|
+
fields?: FormField[];
|
131
|
+
panels?: ActionPanel[];
|
132
|
+
routes?: ListControlsRoutes;
|
133
|
+
}
|
package/types/vue-shims.d.ts
CHANGED