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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "quasar-ui-danx",
3
- "version": "0.0.37",
3
+ "version": "0.0.39",
4
4
  "author": "Dan <dan@flytedesk.com>",
5
5
  "description": "DanX Vue / Quasar component library",
6
6
  "license": "MIT",
@@ -1,49 +1,43 @@
1
1
  <template>
2
2
  <PopoverMenu
3
3
  class="px-2 flex action-button"
4
- :items="activeItems"
5
- :disabled="targets.length === 0"
6
- :tooltip="targets.length === 0 ? tooltip : null"
7
- :loading="isSaving"
4
+ :items="activeActions"
5
+ :disabled="!hasTarget"
6
+ :tooltip="!hasTarget ? tooltip : null"
7
+ :loading="loading"
8
8
  :loading-component="loadingComponent"
9
- @action-item="onAction"
9
+ @action-item="$emit('action', $event)"
10
10
  />
11
11
  </template>
12
12
  <script setup>
13
- import { computed, ref } from 'vue';
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
- items: {
18
+ actions: {
20
19
  type: Array,
21
20
  required: true
22
21
  },
23
- targets: {
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 activeItems = computed(() => props.items.filter(item => {
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 isSaving = ref(false);
43
- async function onAction(item) {
44
- emit('action', item);
45
- isSaving.value = true;
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
- :items="rowProps.col.actions"
79
- :targets="[rowProps.row]"
80
- @action="(action) => $emit('action', {action: action, row: rowProps.row})"
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 "./listActions";
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 ActionPerformerTool } from "./ActionPerformerTool.vue";
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
+ }
@@ -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 "./performAction";
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
- }