quasar-ui-danx 0.0.37 → 0.0.39
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/package.json +1 -1
- package/src/components/ActionTable/ActionMenu.vue +15 -21
- package/src/components/ActionTable/ActionTable.vue +8 -3
- package/src/components/ActionTable/Layouts/ActionTableLayout.vue +0 -10
- package/src/components/ActionTable/index.ts +1 -1
- package/src/components/ActionTable/{listActions.ts → listControls.ts} +3 -34
- package/src/components/Utility/Tools/ActionInputComponent.vue +21 -0
- package/src/components/Utility/Tools/index.ts +1 -1
- package/src/helpers/actions.ts +169 -0
- package/src/helpers/index.ts +1 -1
- package/src/components/Utility/Tools/ActionPerformerTool.vue +0 -88
- package/src/helpers/performAction.ts +0 -73
package/package.json
CHANGED
@@ -1,49 +1,43 @@
|
|
1
1
|
<template>
|
2
2
|
<PopoverMenu
|
3
3
|
class="px-2 flex action-button"
|
4
|
-
:items="
|
5
|
-
:disabled="
|
6
|
-
:tooltip="
|
7
|
-
:loading="
|
4
|
+
:items="activeActions"
|
5
|
+
:disabled="!hasTarget"
|
6
|
+
:tooltip="!hasTarget ? tooltip : null"
|
7
|
+
:loading="loading"
|
8
8
|
:loading-component="loadingComponent"
|
9
|
-
@action-item="
|
9
|
+
@action-item="$emit('action', $event)"
|
10
10
|
/>
|
11
11
|
</template>
|
12
12
|
<script setup>
|
13
|
-
import { computed
|
14
|
-
import { performAction } from '../../helpers';
|
13
|
+
import { computed } from 'vue';
|
15
14
|
import { PopoverMenu } from '../Utility';
|
16
15
|
|
17
16
|
const emit = defineEmits(['action']);
|
18
17
|
const props = defineProps({
|
19
|
-
|
18
|
+
actions: {
|
20
19
|
type: Array,
|
21
20
|
required: true
|
22
21
|
},
|
23
|
-
|
24
|
-
type: Array,
|
22
|
+
target: {
|
23
|
+
type: [Array, Object],
|
25
24
|
required: true
|
26
25
|
},
|
27
26
|
tooltip: {
|
28
27
|
type: String,
|
29
28
|
default: 'First select records to perform a batch action'
|
30
29
|
},
|
30
|
+
loading: Boolean,
|
31
31
|
loadingComponent: {
|
32
32
|
type: [Function, Object],
|
33
33
|
default: undefined
|
34
34
|
}
|
35
35
|
});
|
36
36
|
|
37
|
-
const
|
38
|
-
if (item.enabled === undefined) return true;
|
39
|
-
return typeof item.enabled === 'function' ? !!item.enabled(props.targets?.[0] ?? null, props.targets) : !!item.enabled;
|
40
|
-
}));
|
37
|
+
const hasTarget = computed(() => !!props.target?.length);
|
41
38
|
|
42
|
-
const
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
await performAction(item, props.targets);
|
47
|
-
isSaving.value = false;
|
48
|
-
}
|
39
|
+
const activeActions = computed(() => props.actions.filter(action => {
|
40
|
+
if (action.enabled === undefined) return true;
|
41
|
+
return typeof action.enabled === 'function' ? !!action.enabled(props.target) : !!action.enabled;
|
42
|
+
}));
|
49
43
|
</script>
|
@@ -75,9 +75,10 @@
|
|
75
75
|
</div>
|
76
76
|
<div v-if="rowProps.col.actions" class="flex-grow flex justify-end pl-2">
|
77
77
|
<ActionMenu
|
78
|
-
:
|
79
|
-
:
|
80
|
-
|
78
|
+
:actions="rowProps.col.actions"
|
79
|
+
:target="rowProps.row"
|
80
|
+
:loading="isSavingItem?.id === rowProps.row.id"
|
81
|
+
@action="(action) => $emit('action', {action, target: rowProps.row})"
|
81
82
|
/>
|
82
83
|
</div>
|
83
84
|
</component>
|
@@ -112,6 +113,10 @@ const props = defineProps({
|
|
112
113
|
type: Object,
|
113
114
|
required: true
|
114
115
|
},
|
116
|
+
isSavingItem: {
|
117
|
+
type: Object,
|
118
|
+
default: null
|
119
|
+
},
|
115
120
|
isLoadingList: Boolean,
|
116
121
|
pagedItems: {
|
117
122
|
type: Object,
|
@@ -6,15 +6,5 @@
|
|
6
6
|
<slot name="filters" />
|
7
7
|
<slot />
|
8
8
|
</div>
|
9
|
-
<ActionPerformerTool
|
10
|
-
v-if="activeAction"
|
11
|
-
:targets="actionTargets"
|
12
|
-
:action="activeAction"
|
13
|
-
@done="clearAction"
|
14
|
-
/>
|
15
9
|
</div>
|
16
10
|
</template>
|
17
|
-
<script setup>
|
18
|
-
import { actionTargets, activeAction, clearAction } from '../../../helpers';
|
19
|
-
import { ActionPerformerTool } from '../../Utility';
|
20
|
-
</script>
|
@@ -1,7 +1,7 @@
|
|
1
1
|
export * from "./Filters";
|
2
2
|
export * from "./Form";
|
3
3
|
export * from "./Layouts";
|
4
|
-
export * from "./
|
4
|
+
export * from "./listControls";
|
5
5
|
export * from "./listHelpers";
|
6
6
|
export * from "./tableColumns";
|
7
7
|
export { default as ActionMenu } from "./ActionMenu.vue";
|
@@ -7,20 +7,17 @@ interface ListActionsOptions {
|
|
7
7
|
summaryRoute?: Function;
|
8
8
|
filterFieldOptionsRoute?: Function;
|
9
9
|
moreRoute?: Function;
|
10
|
-
applyActionRoute?: Function;
|
11
10
|
itemDetailsRoute?: Function;
|
12
11
|
urlPattern?: RegExp;
|
13
12
|
filterDefaults?: Record<string, any>;
|
14
13
|
refreshFilters?: boolean;
|
15
14
|
}
|
16
15
|
|
17
|
-
|
18
|
-
export function useListActions(name: string, {
|
16
|
+
export function useListControls(name: string, {
|
19
17
|
listRoute,
|
20
18
|
summaryRoute = null,
|
21
19
|
filterFieldOptionsRoute = null,
|
22
20
|
moreRoute = null,
|
23
|
-
applyActionRoute = null,
|
24
21
|
itemDetailsRoute = null,
|
25
22
|
refreshFilters = false,
|
26
23
|
urlPattern = null,
|
@@ -225,33 +222,6 @@ export function useListActions(name: string, {
|
|
225
222
|
setItem(PAGE_SETTINGS_KEY, settings);
|
226
223
|
}
|
227
224
|
|
228
|
-
/**
|
229
|
-
* Applies an action to an item.
|
230
|
-
*/
|
231
|
-
const isApplyingActionToItem = ref(null);
|
232
|
-
let actionResultCount = 0;
|
233
|
-
|
234
|
-
async function applyAction(item, input, itemData = {}) {
|
235
|
-
isApplyingActionToItem.value = item;
|
236
|
-
const resultNumber = ++actionResultCount;
|
237
|
-
setItemInPagedList({ ...item, ...input, ...itemData });
|
238
|
-
const result = await applyActionRoute(item, input);
|
239
|
-
if (result.success) {
|
240
|
-
// Only render the most recent campaign changes
|
241
|
-
if (resultNumber !== actionResultCount) return;
|
242
|
-
|
243
|
-
// Update the updated item in the previously loaded list if it exists
|
244
|
-
setItemInPagedList(result.item);
|
245
|
-
|
246
|
-
// Update the active item if it is the same as the updated item
|
247
|
-
if (activeItem.value?.id === result.item.id) {
|
248
|
-
activeItem.value = { ...activeItem.value, ...result.item };
|
249
|
-
}
|
250
|
-
}
|
251
|
-
isApplyingActionToItem.value = null;
|
252
|
-
return result;
|
253
|
-
}
|
254
|
-
|
255
225
|
// The active ad for viewing / editing
|
256
226
|
const activeItem = ref(null);
|
257
227
|
// Controls the active panel (ie: tab) if rendering a panels drawer or similar
|
@@ -355,7 +325,6 @@ export function useListActions(name: string, {
|
|
355
325
|
isLoadingSummary,
|
356
326
|
pager,
|
357
327
|
quasarPagination,
|
358
|
-
isApplyingActionToItem,
|
359
328
|
activeItem,
|
360
329
|
activePanel,
|
361
330
|
|
@@ -366,9 +335,9 @@ export function useListActions(name: string, {
|
|
366
335
|
loadList,
|
367
336
|
loadMore,
|
368
337
|
refreshAll,
|
369
|
-
applyAction,
|
370
338
|
getNextItem,
|
371
339
|
openItemForm,
|
372
|
-
applyFilterFromUrl
|
340
|
+
applyFilterFromUrl,
|
341
|
+
setItemInPagedList
|
373
342
|
};
|
374
343
|
}
|
@@ -0,0 +1,21 @@
|
|
1
|
+
<template>
|
2
|
+
<div>
|
3
|
+
<RenderComponent
|
4
|
+
v-if="activeActionInput"
|
5
|
+
:component="activeActionInput.component"
|
6
|
+
:is-saving="isSaving"
|
7
|
+
@confirm="activeActionInput.confirm"
|
8
|
+
@close="activeActionInput.cancel"
|
9
|
+
/>
|
10
|
+
</div>
|
11
|
+
</template>
|
12
|
+
<script setup>
|
13
|
+
import RenderComponent from 'src/components/Utility/Tools/RenderComponent';
|
14
|
+
import { activeActionInput } from '../../../helpers';
|
15
|
+
|
16
|
+
defineProps({
|
17
|
+
isSaving: Boolean
|
18
|
+
|
19
|
+
});
|
20
|
+
const emit = defineEmits(['result']);
|
21
|
+
</script>
|
@@ -1,2 +1,2 @@
|
|
1
|
-
export { default as
|
1
|
+
export { default as ActionInputComponent } from "./ActionInputComponent.vue";
|
2
2
|
export { default as RenderComponent } from "./RenderComponent.vue";
|
@@ -0,0 +1,169 @@
|
|
1
|
+
import { ref, shallowRef } from "vue";
|
2
|
+
import { FlashMessages } from "./index";
|
3
|
+
|
4
|
+
interface ActionOptions {
|
5
|
+
name: string;
|
6
|
+
label: string;
|
7
|
+
menu?: boolean;
|
8
|
+
batch?: boolean;
|
9
|
+
inputComponent?: (target: object[] | object) => any;
|
10
|
+
enabled?: (target: object) => boolean;
|
11
|
+
onAction?: (action: string | null, target: object, input: any) => any;
|
12
|
+
onBatchAction?: (action: string | null, targets: object[], input: any) => any;
|
13
|
+
onFinish?: (action: string | null, targets: object, input: any) => void;
|
14
|
+
}
|
15
|
+
|
16
|
+
export const activeActionInput = shallowRef(null);
|
17
|
+
|
18
|
+
/**
|
19
|
+
* Hook to perform an action on a set of targets
|
20
|
+
* This helper allows you to perform actions by name on a set of targets using a provided list of actions
|
21
|
+
*
|
22
|
+
* @param actions
|
23
|
+
* @param {ActionOptions} globalOptions
|
24
|
+
*/
|
25
|
+
export function useActions(actions: ActionOptions[], globalOptions: ActionOptions = null) {
|
26
|
+
const activeAction = ref(null);
|
27
|
+
const activeTarget = ref(null);
|
28
|
+
|
29
|
+
/**
|
30
|
+
* Resolves an action by name or object, adds globalOptions and overrides any passes options
|
31
|
+
*
|
32
|
+
* @param name
|
33
|
+
* @param {any} options
|
34
|
+
* @returns {any}
|
35
|
+
*/
|
36
|
+
function resolveAction(name, options = null) {
|
37
|
+
const action = typeof name === "string" ? actions.find(a => a.name === name) : name;
|
38
|
+
if (!action) {
|
39
|
+
throw new Error(`Unknown action: ${name}`);
|
40
|
+
}
|
41
|
+
|
42
|
+
return { ...globalOptions, ...action, ...options };
|
43
|
+
}
|
44
|
+
|
45
|
+
return {
|
46
|
+
actions,
|
47
|
+
activeAction,
|
48
|
+
activeTarget,
|
49
|
+
resolveAction,
|
50
|
+
|
51
|
+
/**
|
52
|
+
* Filter the list of actions based on the provided filters in key-value pairs
|
53
|
+
* You can filter on any ActionOptions property by matching the value exactly or by providing an array of values
|
54
|
+
*
|
55
|
+
* @param filters
|
56
|
+
* @returns {ActionOptions[]}
|
57
|
+
*/
|
58
|
+
filterActions(filters: object) {
|
59
|
+
let filteredActions = [...actions];
|
60
|
+
|
61
|
+
for (const filter of Object.keys(filters)) {
|
62
|
+
const filterValue = filters[filter];
|
63
|
+
filteredActions = filteredActions.filter(a => a[filter] === filterValue || (Array.isArray(filterValue) && filterValue.includes(a[filter])));
|
64
|
+
}
|
65
|
+
return filteredActions;
|
66
|
+
},
|
67
|
+
|
68
|
+
/**
|
69
|
+
* Applies an action to an item.
|
70
|
+
*/
|
71
|
+
async applyAction(item, input, itemData = {}) {
|
72
|
+
isSavingItem.value = item;
|
73
|
+
|
74
|
+
setItemInPagedList({ ...item, ...input, ...itemData });
|
75
|
+
const result = await applyActionRoute(item, input);
|
76
|
+
if (result.success) {
|
77
|
+
// Only render the most recent campaign changes
|
78
|
+
if (resultNumber !== actionResultCount) return;
|
79
|
+
|
80
|
+
// Update the updated item in the previously loaded list if it exists
|
81
|
+
setItemInPagedList(result.item);
|
82
|
+
|
83
|
+
// Update the active item if it is the same as the updated item
|
84
|
+
if (activeItem.value?.id === result.item.id) {
|
85
|
+
activeItem.value = { ...activeItem.value, ...result.item };
|
86
|
+
}
|
87
|
+
}
|
88
|
+
isSavingItem.value = null;
|
89
|
+
return result;
|
90
|
+
},
|
91
|
+
|
92
|
+
/**
|
93
|
+
* Perform an action on a set of targets
|
94
|
+
*
|
95
|
+
* @param {string} name - can either be a string or an action object
|
96
|
+
* @param {object[]|object} target - an array of targets or a single target object
|
97
|
+
* @param {any} input
|
98
|
+
*/
|
99
|
+
async performAction(name: string | object, target: object[] | object, input: any = null) {
|
100
|
+
const action = resolveAction(name);
|
101
|
+
const component = action.inputComponent && action.inputComponent(target);
|
102
|
+
|
103
|
+
if (component) {
|
104
|
+
input = await new Promise((resolve, reject) => {
|
105
|
+
activeActionInput.value = {
|
106
|
+
component,
|
107
|
+
confirm: resolve,
|
108
|
+
cancel: reject
|
109
|
+
};
|
110
|
+
});
|
111
|
+
activeActionInput.value = null;
|
112
|
+
}
|
113
|
+
|
114
|
+
return await onConfirmAction(action, target, input);
|
115
|
+
}
|
116
|
+
};
|
117
|
+
}
|
118
|
+
|
119
|
+
async function onConfirmAction(action, target, input) {
|
120
|
+
if (!action.onAction) {
|
121
|
+
throw new Error("No onAction handler found for the selected action:" + action.name);
|
122
|
+
}
|
123
|
+
|
124
|
+
let result: any;
|
125
|
+
try {
|
126
|
+
if (Array.isArray(target)) {
|
127
|
+
result = await action.onBatchAction(action.name, target, input);
|
128
|
+
} else {
|
129
|
+
result = await action.onAction(action.name, target, input);
|
130
|
+
}
|
131
|
+
} catch (e) {
|
132
|
+
console.error(e);
|
133
|
+
result = { error: `An error occurred while performing the action ${action.label}. Please try again later.` };
|
134
|
+
}
|
135
|
+
|
136
|
+
// If there is no return value or the result marks it as successful, we show a success message
|
137
|
+
if (result === undefined || result?.success) {
|
138
|
+
|
139
|
+
if (result?.success) {
|
140
|
+
FlashMessages.success(`The update was successful`);
|
141
|
+
}
|
142
|
+
|
143
|
+
if (action.onSuccess) {
|
144
|
+
await action.onSuccess(result, target, input);
|
145
|
+
}
|
146
|
+
|
147
|
+
} else {
|
148
|
+
const errors = [];
|
149
|
+
if (result.errors) {
|
150
|
+
errors.push(...result.errors);
|
151
|
+
} else if (result.error) {
|
152
|
+
errors.push(typeof result.error === "string" ? result.error : result.error.message);
|
153
|
+
} else {
|
154
|
+
errors.push("An unknown error occurred. Please try again later.");
|
155
|
+
}
|
156
|
+
|
157
|
+
FlashMessages.combine("error", errors);
|
158
|
+
|
159
|
+
if (action.onError) {
|
160
|
+
await action.onError(result, target, input);
|
161
|
+
}
|
162
|
+
}
|
163
|
+
|
164
|
+
if (action.onFinish) {
|
165
|
+
await action.onFinish(result, target, input);
|
166
|
+
}
|
167
|
+
|
168
|
+
return result;
|
169
|
+
}
|
package/src/helpers/index.ts
CHANGED
@@ -9,7 +9,7 @@ export * from "./FlashMessages";
|
|
9
9
|
export * from "./formats";
|
10
10
|
export * from "./http";
|
11
11
|
export * from "./multiFileUpload";
|
12
|
-
export * from "./
|
12
|
+
export * from "./actions";
|
13
13
|
export * from "./singleFileUpload";
|
14
14
|
export * from "./storage";
|
15
15
|
export * from "./utils";
|
@@ -1,88 +0,0 @@
|
|
1
|
-
<template>
|
2
|
-
<div>
|
3
|
-
<Component
|
4
|
-
v-if="confirmDialog"
|
5
|
-
:is="confirmDialog.is"
|
6
|
-
v-bind="confirmDialog.props"
|
7
|
-
:is-saving="isSaving"
|
8
|
-
@confirm="onConfirmAction"
|
9
|
-
@close="$emit('done')"
|
10
|
-
/>
|
11
|
-
</div>
|
12
|
-
</template>
|
13
|
-
<script setup>
|
14
|
-
import { onMounted, ref, shallowRef } from 'vue';
|
15
|
-
import { FlashMessages } from '../../../helpers';
|
16
|
-
|
17
|
-
const emit = defineEmits(['done']);
|
18
|
-
const props = defineProps({
|
19
|
-
action: {
|
20
|
-
type: Object,
|
21
|
-
required: true
|
22
|
-
},
|
23
|
-
targets: {
|
24
|
-
type: Array,
|
25
|
-
required: true
|
26
|
-
}
|
27
|
-
});
|
28
|
-
|
29
|
-
const confirmDialog = shallowRef(props.action.confirmDialog ? props.action.confirmDialog(props.targets) : null);
|
30
|
-
const isSaving = ref(null);
|
31
|
-
|
32
|
-
onMounted(async () => {
|
33
|
-
// If there is no dialog, we auto-confirm the action
|
34
|
-
if (!confirmDialog.value) {
|
35
|
-
await onConfirmAction();
|
36
|
-
}
|
37
|
-
});
|
38
|
-
|
39
|
-
async function onConfirmAction(input) {
|
40
|
-
if (!props.action.onAction) {
|
41
|
-
throw new Error('No onAction handler found for the selected action:' + props.action.name);
|
42
|
-
}
|
43
|
-
|
44
|
-
isSaving.value = true;
|
45
|
-
let result;
|
46
|
-
try {
|
47
|
-
result = await props.action.onAction(props.targets, input);
|
48
|
-
} catch (e) {
|
49
|
-
console.error(e);
|
50
|
-
result = { error: `An error occurred while performing the action ${props.action.label}. Please try again later.` };
|
51
|
-
}
|
52
|
-
|
53
|
-
isSaving.value = false;
|
54
|
-
|
55
|
-
// If there is no return value or the result marks it as successful, we show a success message
|
56
|
-
if (result === undefined || result?.success) {
|
57
|
-
|
58
|
-
if (result?.success) {
|
59
|
-
FlashMessages.success(`The update was successful`);
|
60
|
-
}
|
61
|
-
|
62
|
-
if (props.action.onSuccess) {
|
63
|
-
await props.action.onSuccess(result, props.targets, input);
|
64
|
-
}
|
65
|
-
|
66
|
-
emit('done');
|
67
|
-
} else {
|
68
|
-
const errors = [];
|
69
|
-
if (result.errors) {
|
70
|
-
errors.push(...result.errors);
|
71
|
-
} else if (result.error) {
|
72
|
-
errors.push(typeof result.error === 'string' ? result.error : result.error.message);
|
73
|
-
} else {
|
74
|
-
errors.push('An unknown error occurred. Please try again later.');
|
75
|
-
}
|
76
|
-
|
77
|
-
FlashMessages.combine('error', errors);
|
78
|
-
|
79
|
-
if (props.action.onError) {
|
80
|
-
await props.action.onError(result, props.targets, input);
|
81
|
-
}
|
82
|
-
}
|
83
|
-
|
84
|
-
if (props.action.onFinish) {
|
85
|
-
await props.action.onFinish(result, props.targets, input);
|
86
|
-
}
|
87
|
-
}
|
88
|
-
</script>
|
@@ -1,73 +0,0 @@
|
|
1
|
-
import { ref } from "vue";
|
2
|
-
import { waitForRef } from "./index";
|
3
|
-
|
4
|
-
export const activeAction = ref(null);
|
5
|
-
export const actionTargets = ref([]);
|
6
|
-
|
7
|
-
/**
|
8
|
-
* Hook to perform an action on a set of targets
|
9
|
-
* This helper allows you to perform actions by name on a set of targets using a provided list of actions
|
10
|
-
*
|
11
|
-
* @param actions
|
12
|
-
* @returns {{performAction(name, targets): Promise<void>}}
|
13
|
-
*/
|
14
|
-
export function usePerformAction(actions: any[]) {
|
15
|
-
|
16
|
-
function filterActions(filters) {
|
17
|
-
let filteredActions = [...actions];
|
18
|
-
|
19
|
-
for (const filter of Object.keys(filters)) {
|
20
|
-
const filterValue = filters[filter];
|
21
|
-
filteredActions = filteredActions.filter(a => a[filter] === filterValue || (Array.isArray(filterValue) && filterValue.includes(a[filter])));
|
22
|
-
}
|
23
|
-
return filteredActions;
|
24
|
-
}
|
25
|
-
|
26
|
-
return {
|
27
|
-
/**
|
28
|
-
* Perform an action on a set of targets
|
29
|
-
*
|
30
|
-
* @param name - can either be a string or an action object
|
31
|
-
* @param targets - an array of targets (or a single target object)
|
32
|
-
* @param options
|
33
|
-
* @returns {Promise<void>}
|
34
|
-
*/
|
35
|
-
async performAction(name, targets, options = {}) {
|
36
|
-
const action = typeof name === "string" ? actions.find(a => a.name === name) : name;
|
37
|
-
if (!action) {
|
38
|
-
throw new Error(`Unknown action: ${name}`);
|
39
|
-
}
|
40
|
-
targets = Array.isArray(targets) ? targets : [targets];
|
41
|
-
|
42
|
-
await performAction({ ...action, ...options }, targets);
|
43
|
-
},
|
44
|
-
|
45
|
-
actions,
|
46
|
-
filterActions,
|
47
|
-
};
|
48
|
-
}
|
49
|
-
|
50
|
-
/**
|
51
|
-
* Perform an action on a set of targets
|
52
|
-
*
|
53
|
-
* NOTE: This function and variables should be used w/ the ActionPerformerTool - make sure to use a Layout that has
|
54
|
-
* rendered this component so the actions will be performed
|
55
|
-
*
|
56
|
-
* @param action
|
57
|
-
* @param targets
|
58
|
-
* @returns {Promise<void>}
|
59
|
-
*/
|
60
|
-
export async function performAction(action: any, targets: any[]): Promise<void> {
|
61
|
-
activeAction.value = action;
|
62
|
-
actionTargets.value = targets;
|
63
|
-
await waitForRef(activeAction, null);
|
64
|
-
}
|
65
|
-
|
66
|
-
/**
|
67
|
-
* Clear the active action and targets - (note: this will tear down any dialogs / rendered components) triggered by the
|
68
|
-
* ActionPerformerTool
|
69
|
-
*/
|
70
|
-
export function clearAction() {
|
71
|
-
activeAction.value = null;
|
72
|
-
actionTargets.value = [];
|
73
|
-
}
|