quasar-ui-danx 0.4.13 → 0.4.15
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/danx.es.js +5394 -5322
- package/dist/danx.es.js.map +1 -1
- package/dist/danx.umd.js +80 -80
- 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 +65 -63
- package/src/components/ActionTable/Form/ActionForm.vue +18 -12
- package/src/components/ActionTable/Form/RenderedForm.vue +39 -19
- package/src/components/ActionTable/Layouts/ActionTableLayout.vue +80 -70
- package/src/components/ActionTable/Toolbars/ActionToolbar.vue +29 -29
- package/src/components/ActionTable/listControls.ts +32 -31
- package/src/components/Navigation/NavigationMenu.vue +83 -40
- package/src/components/PanelsDrawer/PanelsDrawer.vue +5 -3
- package/src/components/Utility/Transitions/AutoHeightTransition.vue +21 -0
- package/src/components/Utility/Transitions/index.ts +1 -0
- package/src/config/index.ts +1 -0
- package/src/helpers/actions.ts +31 -20
- package/src/helpers/formats.ts +8 -5
- package/src/helpers/objectStore.ts +9 -2
- package/src/helpers/request.ts +1 -1
- package/src/helpers/routes.ts +5 -0
- package/src/types/actions.d.ts +1 -4
- package/src/types/config.d.ts +3 -1
- package/src/types/controls.d.ts +5 -7
- package/src/types/forms.d.ts +1 -0
- package/src/types/shared.d.ts +1 -0
@@ -1,5 +1,6 @@
|
|
1
1
|
import { computed, Ref, ref, shallowRef, watch } from "vue";
|
2
|
-
import {
|
2
|
+
import { RouteParams, Router } from "vue-router";
|
3
|
+
import { danxOptions } from "../../config";
|
3
4
|
import { getItem, setItem, storeObject, waitForRef } from "../../helpers";
|
4
5
|
import {
|
5
6
|
ActionController,
|
@@ -7,7 +8,6 @@ import {
|
|
7
8
|
AnyObject,
|
8
9
|
FilterGroup,
|
9
10
|
ListControlsFilter,
|
10
|
-
ListControlsInitializeOptions,
|
11
11
|
ListControlsOptions,
|
12
12
|
ListControlsPagination,
|
13
13
|
PagedItems
|
@@ -16,8 +16,6 @@ import { getFilterFromUrl } from "./listHelpers";
|
|
16
16
|
|
17
17
|
export function useListControls(name: string, options: ListControlsOptions): ActionController {
|
18
18
|
let isInitialized = false;
|
19
|
-
let vueRoute: RouteLocationNormalizedLoaded | null | undefined;
|
20
|
-
let vueRouter: Router | null | undefined;
|
21
19
|
const PAGE_SETTINGS_KEY = `dx-${name}-pager`;
|
22
20
|
const pagedItems = shallowRef<PagedItems | null>(null);
|
23
21
|
const activeFilter = ref<ListControlsFilter>({});
|
@@ -112,11 +110,9 @@ export function useListControls(name: string, options: ListControlsOptions): Act
|
|
112
110
|
|
113
111
|
/**
|
114
112
|
* Loads the filter field options for the current filter.
|
115
|
-
*
|
116
|
-
* @returns {Promise<void>}
|
117
113
|
*/
|
118
114
|
async function loadFieldOptions() {
|
119
|
-
if (!options.routes.fieldOptions
|
115
|
+
if (!options.routes.fieldOptions) return;
|
120
116
|
isLoadingFilters.value = true;
|
121
117
|
try {
|
122
118
|
fieldOptions.value = await options.routes.fieldOptions(activeFilter.value) || {};
|
@@ -184,9 +180,9 @@ export function useListControls(name: string, options: ListControlsOptions): Act
|
|
184
180
|
/**
|
185
181
|
* Sets the pagination settings to the given values.
|
186
182
|
*/
|
187
|
-
function setPagination(updated: ListControlsPagination) {
|
183
|
+
function setPagination(updated: Partial<ListControlsPagination>) {
|
188
184
|
// @ts-expect-error Seems like a bug in the typescript linting?
|
189
|
-
pagination.value = updated;
|
185
|
+
pagination.value = { ...pagination.value, ...updated };
|
190
186
|
}
|
191
187
|
|
192
188
|
/**
|
@@ -250,9 +246,6 @@ export function useListControls(name: string, options: ListControlsOptions): Act
|
|
250
246
|
* Loads the filter and pagination settings from local storage.
|
251
247
|
*/
|
252
248
|
function loadSettings() {
|
253
|
-
// Only load settings when the class is fully initialized
|
254
|
-
if (!isInitialized) return;
|
255
|
-
|
256
249
|
const settings = getItem(PAGE_SETTINGS_KEY);
|
257
250
|
|
258
251
|
// Load the filter settings from local storage
|
@@ -402,26 +395,26 @@ export function useListControls(name: string, options: ListControlsOptions): Act
|
|
402
395
|
}
|
403
396
|
|
404
397
|
// Initialize the list actions and load settings, lists, summaries, filter fields, etc.
|
405
|
-
function initialize(
|
406
|
-
vueRouter =
|
407
|
-
vueRoute = initOptions?.vueRouter?.currentRoute.value;
|
398
|
+
function initialize() {
|
399
|
+
const vueRouter = getVueRouter();
|
408
400
|
isInitialized = true;
|
409
401
|
loadSettings();
|
410
402
|
|
411
403
|
/**
|
412
404
|
* Watch the id params in the route and set the active item to the item with the given id.
|
413
405
|
*/
|
414
|
-
if (options.routes.details
|
415
|
-
const { params, meta } =
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
406
|
+
if (options.routes.details) {
|
407
|
+
const { params, meta, name: controlRouteName } = vueRouter.currentRoute.value;
|
408
|
+
|
409
|
+
if (controlRouteName === name) {
|
410
|
+
vueRouter.afterEach((to) => {
|
411
|
+
if (to.name === controlRouteName) {
|
412
|
+
setPanelFromRoute(to.params, to.meta);
|
413
|
+
}
|
414
|
+
});
|
423
415
|
|
424
|
-
|
416
|
+
setPanelFromRoute(params, meta);
|
417
|
+
}
|
425
418
|
}
|
426
419
|
}
|
427
420
|
|
@@ -429,12 +422,12 @@ export function useListControls(name: string, options: ListControlsOptions): Act
|
|
429
422
|
* Updates the URL bar and route to the given params.
|
430
423
|
*/
|
431
424
|
function updateRouteParams(params: AnyObject) {
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
}
|
425
|
+
const vueRouter = getVueRouter();
|
426
|
+
const { name: routeName } = vueRouter.currentRoute.value;
|
427
|
+
vueRouter.push({
|
428
|
+
name: (Array.isArray(routeName) ? routeName[0] : routeName) || "home",
|
429
|
+
params
|
430
|
+
});
|
438
431
|
}
|
439
432
|
|
440
433
|
function setPanelFromRoute(params: RouteParams, meta: AnyObject) {
|
@@ -445,6 +438,13 @@ export function useListControls(name: string, options: ListControlsOptions): Act
|
|
445
438
|
}
|
446
439
|
}
|
447
440
|
|
441
|
+
function getVueRouter(): Router {
|
442
|
+
if (!danxOptions.value.router) {
|
443
|
+
throw new Error("Vue Router must be configured in danxOptions");
|
444
|
+
}
|
445
|
+
return danxOptions.value.router;
|
446
|
+
}
|
447
|
+
|
448
448
|
return {
|
449
449
|
// State
|
450
450
|
name,
|
@@ -474,6 +474,7 @@ export function useListControls(name: string, options: ListControlsOptions): Act
|
|
474
474
|
loadSummary,
|
475
475
|
loadListAndSummary,
|
476
476
|
loadMore,
|
477
|
+
loadFieldOptions,
|
477
478
|
getActiveItemDetails,
|
478
479
|
refreshAll,
|
479
480
|
exportList,
|
@@ -8,10 +8,12 @@
|
|
8
8
|
:key="'nav-item-' + item.label"
|
9
9
|
class="nav-menu-item-box"
|
10
10
|
>
|
11
|
-
<
|
11
|
+
<a
|
12
12
|
class="nav-menu-item flex flex-nowrap"
|
13
|
+
:href="resolveUrl(item)"
|
13
14
|
:class="item.class || itemClass"
|
14
|
-
|
15
|
+
:target="item.target || '_self'"
|
16
|
+
@click="onClick($event, item)"
|
15
17
|
>
|
16
18
|
<div
|
17
19
|
v-if="item.icon"
|
@@ -36,7 +38,7 @@
|
|
36
38
|
>
|
37
39
|
{{ item.tooltip?.text || item.label }}
|
38
40
|
</QTooltip>
|
39
|
-
</
|
41
|
+
</a>
|
40
42
|
<QSeparator
|
41
43
|
v-if="item.separator"
|
42
44
|
:key="'separator-' + item.label"
|
@@ -47,55 +49,96 @@
|
|
47
49
|
</template>
|
48
50
|
<script setup>
|
49
51
|
import { computed } from "vue";
|
52
|
+
import { danxOptions } from "../../config";
|
50
53
|
|
51
54
|
const props = defineProps({
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
55
|
+
collapsed: Boolean,
|
56
|
+
itemClass: {
|
57
|
+
type: String,
|
58
|
+
default: "hover:bg-gray-200"
|
59
|
+
},
|
60
|
+
activeClass: {
|
61
|
+
type: String,
|
62
|
+
default: "bg-blue-200"
|
63
|
+
},
|
64
|
+
items: {
|
65
|
+
type: Array,
|
66
|
+
required: true
|
67
|
+
}
|
65
68
|
});
|
66
69
|
|
70
|
+
const router = danxOptions.value.router;
|
67
71
|
const allowedItems = computed(() => props.items.filter((item) => !item.hidden));
|
72
|
+
|
73
|
+
function getItemRoute(item) {
|
74
|
+
if (!router) {
|
75
|
+
console.error("Router is not available. Configure in danx options.");
|
76
|
+
return;
|
77
|
+
}
|
78
|
+
|
79
|
+
return typeof item.route === "function" ? item.route() : item.route;
|
80
|
+
}
|
81
|
+
|
82
|
+
function onClick(e, item) {
|
83
|
+
if (!item.url && !e.ctrlKey) {
|
84
|
+
e.preventDefault();
|
85
|
+
}
|
86
|
+
|
87
|
+
if (item.disabled || e.ctrlKey) {
|
88
|
+
return;
|
89
|
+
}
|
90
|
+
|
91
|
+
if (item.onClick) {
|
92
|
+
item.onClick();
|
93
|
+
}
|
94
|
+
|
95
|
+
if (item.route) {
|
96
|
+
router.push(getItemRoute(item));
|
97
|
+
}
|
98
|
+
}
|
99
|
+
|
100
|
+
function resolveUrl(item) {
|
101
|
+
if (item.url) {
|
102
|
+
return item.url;
|
103
|
+
}
|
104
|
+
|
105
|
+
if (item.route) {
|
106
|
+
return router.resolve(getItemRoute(item))?.href || "#";
|
107
|
+
}
|
108
|
+
|
109
|
+
return "#";
|
110
|
+
}
|
68
111
|
</script>
|
69
112
|
|
70
113
|
<style lang="scss">
|
71
114
|
.nav-menu-item {
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
115
|
+
padding: 1em;
|
116
|
+
border-radius: 0.5em;
|
117
|
+
font-weight: bold;
|
118
|
+
font-size: 14px;
|
119
|
+
transition: all 150ms, color 0ms;
|
120
|
+
cursor: pointer;
|
121
|
+
|
122
|
+
&.is-disabled {
|
123
|
+
@apply bg-inherit;
|
124
|
+
}
|
125
|
+
|
126
|
+
.label {
|
127
|
+
@apply transition-all;
|
128
|
+
}
|
129
|
+
|
130
|
+
.nav-icon {
|
131
|
+
@apply w-5 h-5 flex-shrink-0;
|
132
|
+
}
|
90
133
|
}
|
91
134
|
|
92
135
|
.is-collapsed {
|
93
|
-
|
94
|
-
|
95
|
-
|
136
|
+
.nav-link {
|
137
|
+
width: 3.8em;
|
138
|
+
}
|
96
139
|
|
97
|
-
|
98
|
-
|
99
|
-
|
140
|
+
.label {
|
141
|
+
@apply opacity-0;
|
142
|
+
}
|
100
143
|
}
|
101
144
|
</style>
|
@@ -1,6 +1,6 @@
|
|
1
1
|
<template>
|
2
2
|
<ContentDrawer
|
3
|
-
position="
|
3
|
+
:position="position"
|
4
4
|
show
|
5
5
|
overlay
|
6
6
|
content-class="h-full"
|
@@ -82,6 +82,7 @@ export interface Props {
|
|
82
82
|
activeItem: ActionTargetItem;
|
83
83
|
tabsClass?: string | object,
|
84
84
|
panelsClass?: string | object,
|
85
|
+
position?: "standard" | "right" | "left";
|
85
86
|
panels: ActionPanel[]
|
86
87
|
}
|
87
88
|
|
@@ -89,8 +90,9 @@ defineEmits(["update:model-value", "close"]);
|
|
89
90
|
const props = withDefaults(defineProps<Props>(), {
|
90
91
|
title: "",
|
91
92
|
modelValue: null,
|
92
|
-
tabsClass: "w-[13.5rem]",
|
93
|
-
panelsClass: "w-[
|
93
|
+
tabsClass: "w-[13.5rem] flex-shrink-0",
|
94
|
+
panelsClass: "w-[80rem]",
|
95
|
+
position: "right"
|
94
96
|
});
|
95
97
|
|
96
98
|
const activePanel = ref(props.modelValue);
|
@@ -0,0 +1,21 @@
|
|
1
|
+
<template>
|
2
|
+
<Transition name="autoHeight">
|
3
|
+
<slot />
|
4
|
+
</Transition>
|
5
|
+
</template>
|
6
|
+
|
7
|
+
<style lang="scss">
|
8
|
+
.autoHeight-enter-active,
|
9
|
+
.autoHeight-leave-active {
|
10
|
+
transition: height .3s linear;
|
11
|
+
height: calc-size(auto);
|
12
|
+
}
|
13
|
+
|
14
|
+
.autoHeight-enter-to, .autoHeight-leave-from {
|
15
|
+
height: calc-size(auto);
|
16
|
+
}
|
17
|
+
|
18
|
+
.autoHeight-enter-from, .autoHeight-leave-to {
|
19
|
+
height: 0;
|
20
|
+
}
|
21
|
+
</style>
|
@@ -1,3 +1,4 @@
|
|
1
|
+
export { default as AutoHeightTransition } from "./AutoHeightTransition.vue";
|
1
2
|
export { default as ListTransition } from "./ListTransition.vue";
|
2
3
|
export { default as MaxHeightTransition } from "./MaxHeightTransition.vue";
|
3
4
|
export { default as SlideTransition } from "./SlideTransition.vue";
|
package/src/config/index.ts
CHANGED
package/src/helpers/actions.ts
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
import { useDebounceFn } from "@vueuse/core";
|
2
2
|
import { uid } from "quasar";
|
3
3
|
import { isReactive, Ref, shallowRef } from "vue";
|
4
|
-
import type { ActionOptions,
|
4
|
+
import type { ActionOptions, ActionTarget, AnyObject, ResourceAction } from "../types";
|
5
5
|
import { FlashMessages } from "./FlashMessages";
|
6
6
|
import { storeObject } from "./objectStore";
|
7
7
|
|
@@ -11,36 +11,34 @@ export const activeActionVnode: Ref = shallowRef(null);
|
|
11
11
|
* Hook to perform an action on a set of targets
|
12
12
|
* This helper allows you to perform actions by name on a set of targets using a provided list of actions
|
13
13
|
*/
|
14
|
-
export function useActions(actions: ActionOptions[], globalOptions:
|
14
|
+
export function useActions(actions: ActionOptions[], globalOptions: Partial<ActionOptions> | null = null) {
|
15
15
|
const namespace = uid();
|
16
16
|
|
17
17
|
/**
|
18
18
|
* Resolve the action object based on the provided name (or return the object if the name is already an object)
|
19
19
|
*/
|
20
|
-
function getAction(actionName: string
|
21
|
-
let actionOptions: ActionOptions | ResourceAction;
|
22
|
-
|
20
|
+
function getAction(actionName: string, actionOptions?: Partial<ActionOptions>): ResourceAction {
|
23
21
|
/// Resolve the action options or resource action based on the provided input
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
actionOptions
|
22
|
+
const baseOptions = actions.find(a => a.name === actionName) || { name: actionName };
|
23
|
+
|
24
|
+
if (actionOptions) {
|
25
|
+
Object.assign(baseOptions, actionOptions);
|
28
26
|
}
|
29
27
|
|
30
28
|
// If the action is already reactive, return it
|
31
|
-
if (isReactive(
|
29
|
+
if (isReactive(baseOptions) && "__type" in baseOptions) return baseOptions as ResourceAction;
|
32
30
|
|
33
31
|
const resourceAction: ResourceAction = storeObject({
|
34
32
|
...globalOptions,
|
35
|
-
...
|
33
|
+
...baseOptions,
|
36
34
|
trigger: (target, input) => performAction(resourceAction, target, input),
|
37
35
|
isApplying: false,
|
38
36
|
__type: "__Action:" + namespace
|
39
37
|
});
|
40
38
|
|
41
39
|
// Assign Trigger function if it doesn't exist
|
42
|
-
if (
|
43
|
-
resourceAction.trigger = useDebounceFn((target, input) => performAction(resourceAction, target, input),
|
40
|
+
if (baseOptions.debounce) {
|
41
|
+
resourceAction.trigger = useDebounceFn((target, input) => performAction(resourceAction, target, input), baseOptions.debounce);
|
44
42
|
}
|
45
43
|
|
46
44
|
return resourceAction;
|
@@ -63,7 +61,7 @@ export function useActions(actions: ActionOptions[], globalOptions: ActionOption
|
|
63
61
|
}
|
64
62
|
}
|
65
63
|
|
66
|
-
return filteredActions.map((a: ActionOptions) => getAction(a));
|
64
|
+
return filteredActions.map((a: ActionOptions) => getAction(a.name));
|
67
65
|
}
|
68
66
|
|
69
67
|
/**
|
@@ -121,6 +119,11 @@ export function useActions(actions: ActionOptions[], globalOptions: ActionOption
|
|
121
119
|
result = await onConfirmAction(action, target, input);
|
122
120
|
}
|
123
121
|
|
122
|
+
// If the request was aborted (likely due to a newer request), just return immediately without changing state
|
123
|
+
if (result?.abort) {
|
124
|
+
return result;
|
125
|
+
}
|
126
|
+
|
124
127
|
action.isApplying = false;
|
125
128
|
setTargetSavingState(target, false);
|
126
129
|
|
@@ -187,8 +190,16 @@ async function onConfirmAction(action: ActionOptions, target: ActionTarget, inpu
|
|
187
190
|
result = await action.onAction(action.alias || action.name, target, input);
|
188
191
|
}
|
189
192
|
} catch (e) {
|
190
|
-
|
191
|
-
|
193
|
+
if (("" + e).match(/Request was aborted/)) {
|
194
|
+
result = { abort: true };
|
195
|
+
} else {
|
196
|
+
console.error(e);
|
197
|
+
result = { error: `An error occurred while performing the action ${action.label}. Please try again later.` };
|
198
|
+
}
|
199
|
+
}
|
200
|
+
|
201
|
+
if (result?.abort) {
|
202
|
+
return result;
|
192
203
|
}
|
193
204
|
|
194
205
|
// If there is no return value or the result marks it as successful, we show a success message
|
@@ -203,11 +214,11 @@ async function onConfirmAction(action: ActionOptions, target: ActionTarget, inpu
|
|
203
214
|
}
|
204
215
|
|
205
216
|
if (action.onSuccess) {
|
206
|
-
action.onSuccess(result, target, input);
|
217
|
+
await action.onSuccess(result, target, input);
|
207
218
|
}
|
208
219
|
|
209
220
|
if (isBatch && action.onBatchSuccess) {
|
210
|
-
action.onBatchSuccess(result, target, input);
|
221
|
+
await action.onBatchSuccess(result, target, input);
|
211
222
|
}
|
212
223
|
} else {
|
213
224
|
const errors = [];
|
@@ -230,12 +241,12 @@ async function onConfirmAction(action: ActionOptions, target: ActionTarget, inpu
|
|
230
241
|
FlashMessages.combine("error", errors);
|
231
242
|
|
232
243
|
if (action.onError) {
|
233
|
-
action.onError(result, target, input);
|
244
|
+
await action.onError(result, target, input);
|
234
245
|
}
|
235
246
|
}
|
236
247
|
|
237
248
|
if (action.onFinish) {
|
238
|
-
action.onFinish(result, target, input);
|
249
|
+
await action.onFinish(result, target, input);
|
239
250
|
}
|
240
251
|
|
241
252
|
return result;
|
package/src/helpers/formats.ts
CHANGED
@@ -339,21 +339,24 @@ export function fJSON(string: string | object) {
|
|
339
339
|
/**
|
340
340
|
* Convert markdown formatted string into a valid JSON object
|
341
341
|
*/
|
342
|
-
export function parseMarkdownJSON(string: string | object): object | null |
|
342
|
+
export function parseMarkdownJSON(string: string | object): object | null | false {
|
343
|
+
if (!string) return null;
|
343
344
|
if (typeof string === "object") return string as object;
|
344
345
|
|
345
346
|
try {
|
346
347
|
return JSON.parse(parseMarkdownCode(string));
|
347
348
|
} catch (e) {
|
348
|
-
return
|
349
|
+
return false;
|
349
350
|
}
|
350
351
|
}
|
351
352
|
|
352
|
-
export function parseMarkdownYAML(string: string): object | null |
|
353
|
+
export function parseMarkdownYAML(string: string): object | null | false {
|
354
|
+
if (!string) return null;
|
355
|
+
|
353
356
|
try {
|
354
357
|
return parseYAML(parseMarkdownCode(string)) || (string ? undefined : null);
|
355
358
|
} catch (e) {
|
356
|
-
return
|
359
|
+
return false;
|
357
360
|
}
|
358
361
|
}
|
359
362
|
|
@@ -372,7 +375,7 @@ export function fMarkdownCode(type: string, string: string | object): string {
|
|
372
375
|
if (typeof string === "object" || isJSON(string)) {
|
373
376
|
switch (type) {
|
374
377
|
case "yaml":
|
375
|
-
string = stringifyYAML(string);
|
378
|
+
string = stringifyYAML(typeof string === "string" ? JSON.parse(string) : string);
|
376
379
|
break;
|
377
380
|
case "ts":
|
378
381
|
string = "";
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import { uid } from "quasar";
|
1
2
|
import { ShallowReactive, shallowReactive } from "vue";
|
2
3
|
import { TypedObject } from "../types";
|
3
4
|
|
@@ -8,10 +9,13 @@ const store = new Map<string, any>();
|
|
8
9
|
* Returns the stored object that should be used instead of the passed object as the returned object is shared across the system
|
9
10
|
*/
|
10
11
|
export function storeObject<T extends TypedObject>(newObject: T): ShallowReactive<T> {
|
11
|
-
const id = newObject
|
12
|
-
const type = newObject
|
12
|
+
const id = newObject?.id || newObject?.name;
|
13
|
+
const type = newObject?.__type;
|
13
14
|
if (!id || !type) return shallowReactive(newObject);
|
14
15
|
|
16
|
+
if (!newObject.__id) {
|
17
|
+
newObject.__id = uid();
|
18
|
+
}
|
15
19
|
if (!newObject.__timestamp) {
|
16
20
|
newObject.__timestamp = newObject.updated_at || 0;
|
17
21
|
}
|
@@ -34,6 +38,9 @@ export function storeObject<T extends TypedObject>(newObject: T): ShallowReactiv
|
|
34
38
|
for (const index in value) {
|
35
39
|
newObject[key][index] = storeObject(value[index]);
|
36
40
|
}
|
41
|
+
} else if (value?.__type) {
|
42
|
+
// @ts-expect-error newObject[key] is guaranteed to be a TypedObject
|
43
|
+
newObject[key] = storeObject(value);
|
37
44
|
}
|
38
45
|
}
|
39
46
|
|
package/src/helpers/request.ts
CHANGED
@@ -45,7 +45,7 @@ export const request: RequestApi = {
|
|
45
45
|
// If the request was aborted too late, but there was still another request that was made after the current,
|
46
46
|
// then abort the current request with an abort flag
|
47
47
|
if (timestamp < request.abortControllers[abortKey].timestamp) {
|
48
|
-
|
48
|
+
return { abort: true };
|
49
49
|
}
|
50
50
|
|
51
51
|
// Otherwise, the current is the most recent request, so we can delete the abort controller
|
package/src/helpers/routes.ts
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
import { storeObject } from "../helpers";
|
1
2
|
import { ListControlsRoutes } from "../types";
|
2
3
|
import { downloadFile } from "./downloadPdf";
|
3
4
|
import { request } from "./request";
|
@@ -13,6 +14,10 @@ export function useActionRoutes(baseUrl: string): ListControlsRoutes {
|
|
13
14
|
details(target) {
|
14
15
|
return request.get(`${baseUrl}/${target.id}/details`);
|
15
16
|
},
|
17
|
+
async detailsAndStore(target) {
|
18
|
+
const item = await request.get(`${baseUrl}/${target.id}/details`);
|
19
|
+
return storeObject(item);
|
20
|
+
},
|
16
21
|
fieldOptions() {
|
17
22
|
return request.get(`${baseUrl}/field-options`);
|
18
23
|
},
|
package/src/types/actions.d.ts
CHANGED
@@ -13,6 +13,7 @@ export interface ActionPanel {
|
|
13
13
|
|
14
14
|
export interface ActionTargetItem extends TypedObject {
|
15
15
|
isSaving?: boolean;
|
16
|
+
updated_at?: string;
|
16
17
|
}
|
17
18
|
|
18
19
|
export type ActionTarget = ActionTargetItem[] | ActionTargetItem | null;
|
@@ -41,10 +42,6 @@ export interface ActionOptions {
|
|
41
42
|
onFinish?: (result: any, targets: ActionTarget, input: any) => any;
|
42
43
|
}
|
43
44
|
|
44
|
-
export interface ActionOptionsPartial extends ActionOptions {
|
45
|
-
name?: string;
|
46
|
-
}
|
47
|
-
|
48
45
|
export interface ResourceAction extends ActionOptions {
|
49
46
|
isApplying: boolean;
|
50
47
|
trigger: (target?: ActionTarget, input?: any) => Promise<any>;
|
package/src/types/config.d.ts
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
import { QNotifyCreateOptions } from "quasar";
|
2
|
+
import { Router } from "vue-router";
|
2
3
|
import { FileUploadOptions } from "./files";
|
3
4
|
import { RequestOptions } from "./requests";
|
4
5
|
|
5
6
|
export interface DanxOptions {
|
6
7
|
tinyMceApiKey?: string;
|
7
8
|
fileUpload?: FileUploadOptions;
|
8
|
-
request?: RequestOptions
|
9
|
+
request?: RequestOptions;
|
10
|
+
router?: Router;
|
9
11
|
flashMessages?: {
|
10
12
|
default?: QNotifyCreateOptions;
|
11
13
|
success?: QNotifyCreateOptions;
|
package/src/types/controls.d.ts
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
import { ComputedRef, Ref, ShallowRef } from "vue";
|
2
|
-
import { Router } from "vue-router";
|
3
2
|
import { ActionTargetItem } from "./actions";
|
4
3
|
import { AnyObject, LabelValueItem } from "./shared";
|
5
4
|
|
@@ -29,6 +28,8 @@ export interface ListControlsRoutes {
|
|
29
28
|
|
30
29
|
details?(target: ActionTargetItem): Promise<ActionTargetItem>;
|
31
30
|
|
31
|
+
detailsAndStore?(target: ActionTargetItem): Promise<ActionTargetItem>;
|
32
|
+
|
32
33
|
more?(pager: ListControlsPagination): Promise<ActionTargetItem[]>;
|
33
34
|
|
34
35
|
fieldOptions?(filter?: AnyObject): Promise<AnyObject>;
|
@@ -67,10 +68,6 @@ export interface PagedItems {
|
|
67
68
|
} | undefined;
|
68
69
|
}
|
69
70
|
|
70
|
-
export interface ListControlsInitializeOptions {
|
71
|
-
vueRouter?: Router;
|
72
|
-
}
|
73
|
-
|
74
71
|
export interface ActionController {
|
75
72
|
name: string;
|
76
73
|
label: string;
|
@@ -95,15 +92,16 @@ export interface ActionController {
|
|
95
92
|
activePanel: ShallowRef<string | null>;
|
96
93
|
|
97
94
|
// Actions
|
98
|
-
initialize: (
|
95
|
+
initialize: () => void;
|
99
96
|
resetPaging: () => void;
|
100
|
-
setPagination: (updated: ListControlsPagination) => void;
|
97
|
+
setPagination: (updated: Partial<ListControlsPagination>) => void;
|
101
98
|
setSelectedRows: (selection: ActionTargetItem[]) => void;
|
102
99
|
clearSelectedRows: () => void;
|
103
100
|
loadList: (filter?: ListControlsFilter) => Promise<void>;
|
104
101
|
loadSummary: (filter?: ListControlsFilter) => Promise<void>;
|
105
102
|
loadListAndSummary: (filter?: ListControlsFilter) => Promise<void>;
|
106
103
|
loadMore: (index: number, perPage?: number) => Promise<boolean>;
|
104
|
+
loadFieldOptions: () => Promise<void>;
|
107
105
|
getActiveItemDetails: () => Promise<void>;
|
108
106
|
refreshAll: () => Promise<void[]>;
|
109
107
|
exportList: (filter?: ListControlsFilter) => Promise<void>;
|
package/src/types/forms.d.ts
CHANGED