quasar-ui-danx 0.4.12 → 0.4.14
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 +10975 -6424
- package/dist/danx.es.js.map +1 -1
- package/dist/danx.umd.js +137 -7
- package/dist/danx.umd.js.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +3 -2
- package/src/components/ActionTable/ActionTable.vue +65 -63
- package/src/components/ActionTable/Form/ActionForm.vue +18 -12
- package/src/components/ActionTable/Form/RenderedForm.vue +86 -60
- 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 +20 -8
- package/src/components/PanelsDrawer/PanelsDrawerPanels.vue +3 -1
- package/src/components/PanelsDrawer/PanelsDrawerTabs.vue +17 -4
- package/src/components/Utility/Tools/RenderVnode.vue +5 -1
- 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/date.ts +2 -2
- package/src/helpers/formats.ts +55 -5
- package/src/helpers/objectStore.ts +7 -0
- package/src/helpers/request.ts +1 -1
- package/src/helpers/routes.ts +5 -0
- package/src/helpers/utils.ts +3 -3
- package/src/types/actions.d.ts +4 -7
- package/src/types/config.d.ts +3 -1
- package/src/types/controls.d.ts +5 -7
- package/src/types/forms.d.ts +20 -1
- 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,7 +1,7 @@
|
|
1
1
|
<template>
|
2
2
|
<ContentDrawer
|
3
|
-
position="
|
4
|
-
|
3
|
+
:position="position"
|
4
|
+
show
|
5
5
|
overlay
|
6
6
|
content-class="h-full"
|
7
7
|
class="dx-panels-drawer"
|
@@ -13,7 +13,13 @@
|
|
13
13
|
<div class="dx-panels-drawer-header flex items-center px-6 py-4">
|
14
14
|
<div class="flex-grow">
|
15
15
|
<slot name="header">
|
16
|
-
<h2
|
16
|
+
<h2 v-if="title">
|
17
|
+
{{ title }}
|
18
|
+
</h2>
|
19
|
+
<div v-if="!activeItem">
|
20
|
+
Loading
|
21
|
+
<QSpinnerHourglass />
|
22
|
+
</div>
|
17
23
|
</slot>
|
18
24
|
</div>
|
19
25
|
<div
|
@@ -32,10 +38,14 @@
|
|
32
38
|
</div>
|
33
39
|
</div>
|
34
40
|
<div class="dx-panels-drawer-body flex-grow overflow-hidden h-full">
|
35
|
-
<div
|
41
|
+
<div
|
42
|
+
v-if="activeItem.__timestamp > 0"
|
43
|
+
class="flex items-stretch flex-nowrap h-full"
|
44
|
+
>
|
36
45
|
<PanelsDrawerTabs
|
37
46
|
:key="'pd-tabs:' + activeItem.id"
|
38
47
|
v-model="activePanel"
|
48
|
+
:active-item="activeItem"
|
39
49
|
:class="tabsClass"
|
40
50
|
:panels="panels"
|
41
51
|
@update:model-value="$emit('update:model-value', $event)"
|
@@ -44,6 +54,7 @@
|
|
44
54
|
:key="'pd-panels:' + activeItem.id"
|
45
55
|
:panels="panels"
|
46
56
|
:active-panel="activePanel"
|
57
|
+
:active-item="activeItem"
|
47
58
|
:class="activePanelOptions?.class || panelsClass"
|
48
59
|
/>
|
49
60
|
<div
|
@@ -68,9 +79,10 @@ import PanelsDrawerTabs from "./PanelsDrawerTabs";
|
|
68
79
|
export interface Props {
|
69
80
|
title?: string,
|
70
81
|
modelValue?: string | number,
|
71
|
-
activeItem
|
82
|
+
activeItem: ActionTargetItem;
|
72
83
|
tabsClass?: string | object,
|
73
84
|
panelsClass?: string | object,
|
85
|
+
position?: "standard" | "right" | "left";
|
74
86
|
panels: ActionPanel[]
|
75
87
|
}
|
76
88
|
|
@@ -78,9 +90,9 @@ defineEmits(["update:model-value", "close"]);
|
|
78
90
|
const props = withDefaults(defineProps<Props>(), {
|
79
91
|
title: "",
|
80
92
|
modelValue: null,
|
81
|
-
|
82
|
-
|
83
|
-
|
93
|
+
tabsClass: "w-[13.5rem] flex-shrink-0",
|
94
|
+
panelsClass: "w-[80rem]",
|
95
|
+
position: "right"
|
84
96
|
});
|
85
97
|
|
86
98
|
const activePanel = ref(props.modelValue);
|
@@ -11,17 +11,19 @@
|
|
11
11
|
<RenderVnode
|
12
12
|
v-if="panel.vnode"
|
13
13
|
:vnode="panel.vnode"
|
14
|
+
:props="activeItem"
|
14
15
|
/>
|
15
16
|
</QTabPanel>
|
16
17
|
</QTabPanels>
|
17
18
|
</template>
|
18
19
|
|
19
20
|
<script setup lang="ts">
|
20
|
-
import { ActionPanel } from "../../types";
|
21
|
+
import { ActionPanel, ActionTargetItem } from "../../types";
|
21
22
|
import { RenderVnode } from "../Utility";
|
22
23
|
|
23
24
|
defineProps<{
|
24
25
|
activePanel?: string | number,
|
26
|
+
activeItem: ActionTargetItem,
|
25
27
|
panels: ActionPanel[]
|
26
28
|
}>();
|
27
29
|
</script>
|
@@ -9,11 +9,11 @@
|
|
9
9
|
@update:model-value="$emit('update:model-value', $event)"
|
10
10
|
>
|
11
11
|
<template v-for="panel in panels">
|
12
|
-
<template v-if="panel
|
12
|
+
<template v-if="isEnabled(panel)">
|
13
13
|
<RenderVnode
|
14
14
|
v-if="panel.tabVnode"
|
15
15
|
:key="panel.name"
|
16
|
-
:vnode="panel.tabVnode(modelValue)"
|
16
|
+
:vnode="panel.tabVnode(activeItem, modelValue)"
|
17
17
|
:is-active="modelValue === panel.name"
|
18
18
|
:name="panel.name"
|
19
19
|
:label="panel.label"
|
@@ -30,19 +30,32 @@
|
|
30
30
|
</template>
|
31
31
|
<script setup lang="ts">
|
32
32
|
import { QTab } from "quasar";
|
33
|
-
import { ActionPanel } from "../../types";
|
33
|
+
import { ActionPanel, ActionTargetItem } from "../../types";
|
34
34
|
import { RenderVnode } from "../Utility";
|
35
35
|
|
36
36
|
defineEmits(["update:model-value"]);
|
37
37
|
|
38
38
|
interface Props {
|
39
39
|
modelValue?: string | number;
|
40
|
+
activeItem: ActionTargetItem;
|
40
41
|
panels: ActionPanel[];
|
41
42
|
}
|
42
43
|
|
43
|
-
withDefaults(defineProps<Props>(), {
|
44
|
+
const props = withDefaults(defineProps<Props>(), {
|
44
45
|
modelValue: "general"
|
45
46
|
});
|
47
|
+
|
48
|
+
function isEnabled(panel) {
|
49
|
+
if (panel.enabled === undefined) return true;
|
50
|
+
|
51
|
+
if (!panel.enabled) return false;
|
52
|
+
|
53
|
+
if (typeof panel.enabled === "function") {
|
54
|
+
return panel.enabled(props.activeItem);
|
55
|
+
}
|
56
|
+
|
57
|
+
return true;
|
58
|
+
}
|
46
59
|
</script>
|
47
60
|
|
48
61
|
<style lang="scss" module="cls">
|
@@ -11,7 +11,7 @@ const RenderVnode = (props) => {
|
|
11
11
|
}
|
12
12
|
|
13
13
|
if (typeof props.vnode === "function") {
|
14
|
-
return props.vnode(props.props);
|
14
|
+
return props.vnode(props.props, props.params);
|
15
15
|
}
|
16
16
|
|
17
17
|
return null;
|
@@ -24,6 +24,10 @@ RenderVnode.props = {
|
|
24
24
|
props: {
|
25
25
|
type: Object,
|
26
26
|
default: () => ({})
|
27
|
+
},
|
28
|
+
params: {
|
29
|
+
type: Object,
|
30
|
+
default: null
|
27
31
|
}
|
28
32
|
};
|
29
33
|
export default RenderVnode;
|
@@ -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/date.ts
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
import { parseDateTime } from "./formats";
|
2
2
|
|
3
|
-
export function diffInDays(date1, date2) {
|
4
|
-
|
3
|
+
export function diffInDays(date1: string, date2: string) {
|
4
|
+
return parseDateTime(date2).diff(parseDateTime(date1), ["days"]).days;
|
5
5
|
}
|