quasar-ui-danx 0.4.26 → 0.4.28
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 +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