quasar-ui-danx 0.0.28 → 0.0.30

Sign up to get free protection for your applications and to get access to all the features.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "quasar-ui-danx",
3
- "version": "0.0.28",
3
+ "version": "0.0.30",
4
4
  "author": "Dan <dan@flytedesk.com>",
5
5
  "description": "DanX Vue / Quasar component library",
6
6
  "license": "MIT",
@@ -1,15 +1,12 @@
1
1
  <template>
2
- <div>
3
- <PopoverMenu
4
- class="px-4 h-full flex"
5
- :items="items"
6
- @action-item="onAction"
7
- />
8
- </div>
2
+ <PopoverMenu
3
+ class="px-4 h-full flex"
4
+ :items="items"
5
+ @action-item="onAction"
6
+ />
9
7
  </template>
10
8
  <script setup>
11
- import { ref, shallowRef } from 'vue';
12
- import { FlashMessages, performAction } from '../../helpers';
9
+ import { performAction } from '../../helpers';
13
10
  import { PopoverMenu } from '../Utility';
14
11
 
15
12
  const emit = defineEmits(['action']);
@@ -25,59 +22,8 @@ const props = defineProps({
25
22
  });
26
23
 
27
24
 
28
- const activeAction = shallowRef(null);
29
- const confirmDialog = shallowRef(null);
30
- const isSaving = ref(false);
31
-
32
25
  function onAction(item) {
33
26
  emit('action', item);
34
-
35
27
  performAction(item, props.rows);
36
28
  }
37
-
38
- function onCancel() {
39
- activeAction.value = null;
40
- confirmDialog.value = null;
41
- }
42
-
43
- async function onConfirmAction(input) {
44
- if (!activeAction.value.onAction) {
45
- throw new Error('No onAction handler found for the selected action:' + activeAction.value.action);
46
- }
47
-
48
- isSaving.value = true;
49
- const result = await activeAction.value.onAction(input, props.rows);
50
- isSaving.value = false;
51
-
52
- if (!result.success) {
53
- const errors = [];
54
- if (result.errors) {
55
- errors.push(...result.errors);
56
- } else if (result.error) {
57
- errors.push(result.error.message);
58
- } else {
59
- errors.push('An unknown error occurred. Please try again later.');
60
- }
61
-
62
- FlashMessages.combine('error', errors);
63
-
64
- if (activeAction.value.onError) {
65
- await activeAction.value.onError(result, input);
66
- }
67
- }
68
-
69
- FlashMessages.success(`The update was successful`);
70
-
71
- if (activeAction.value.onSuccess) {
72
- await activeAction.value.onSuccess(result, input);
73
- }
74
-
75
- if (activeAction.value.onFinish) {
76
- await activeAction.value.onFinish();
77
- }
78
-
79
- confirmDialog.value = null;
80
- activeAction.value = null;
81
- }
82
-
83
29
  </script>
@@ -6,10 +6,15 @@
6
6
  <slot name="filters" />
7
7
  <slot />
8
8
  </div>
9
- <ActionPerformerTool v-if="activeAction" :targets="actionTargets" :action="activeAction" />
9
+ <ActionPerformerTool
10
+ v-if="activeAction"
11
+ :targets="actionTargets"
12
+ :action="activeAction"
13
+ @done="clearAction"
14
+ />
10
15
  </div>
11
16
  </template>
12
17
  <script setup>
13
- import { actionTargets, activeAction } from '../../../helpers';
18
+ import { actionTargets, activeAction, clearAction } from '../../../helpers';
14
19
  import { ActionPerformerTool } from '../../Utility';
15
20
  </script>
@@ -1,6 +1,6 @@
1
1
  import { computed, ref, watch } from "vue";
2
- import { getItem, setItem } from "../../helpers";
3
- import { getFilterFromUrl, waitForRef } from "./listHelpers";
2
+ import { getItem, setItem, waitForRef } from "../../helpers";
3
+ import { getFilterFromUrl } from "./listHelpers";
4
4
 
5
5
  export function useListActions(name, {
6
6
  listRoute,
@@ -165,7 +165,7 @@ export function useListActions(name, {
165
165
  * @returns {Promise<Awaited<void>[]>}
166
166
  */
167
167
  async function refreshAll() {
168
- return Promise.all([loadList(), loadSummary(), loadFilterFieldOptions()]);
168
+ return Promise.all([loadList(), loadSummary(), loadFilterFieldOptions(), getActiveItemDetails()]);
169
169
  }
170
170
 
171
171
  /**
@@ -260,19 +260,30 @@ export function useListActions(name, {
260
260
  // Controls the tab on the Ad Form
261
261
  const formTab = ref("general");
262
262
 
263
+ /**
264
+ * Gets the additional details for the currently active item.
265
+ * (ie: data that is not normally loaded in the list because it is not needed for the list view)
266
+ * @returns {Promise<void>}
267
+ */
268
+ async function getActiveItemDetails() {
269
+ if (!activeItem.value) return;
270
+
271
+ const result = await itemDetailsRoute(activeItem.value);
272
+
273
+ // Only set the ad details if we are the response for the currently loaded item
274
+ // NOTE: race conditions might allow the finished loading item to be different to the currently
275
+ // requested item
276
+ if (result?.id === activeItem.value?.id) {
277
+ activeItem.value = result;
278
+ }
279
+ }
280
+
263
281
  // Whenever the active item changes, fill the additional item details
264
282
  // (ie: tasks, verifications, creatives, etc.)
265
283
  if (itemDetailsRoute) {
266
284
  watch(() => activeItem.value, async (newItem, oldItem) => {
267
285
  if (newItem && oldItem?.id !== newItem.id) {
268
- const result = await itemDetailsRoute(newItem);
269
-
270
- // Only set the ad details if we are the response for the currently loaded item
271
- // NOTE: race conditions might allow the finished loading item to be different to the currently
272
- // requested item
273
- if (result?.id === activeItem.value?.id) {
274
- activeItem.value = result;
275
- }
286
+ await getActiveItemDetails();
276
287
  }
277
288
  });
278
289
  }
@@ -1,4 +1,4 @@
1
- import { onMounted, watch } from "vue";
1
+ import { onMounted } from "vue";
2
2
  import { getUrlParam } from "../../helpers";
3
3
 
4
4
  export function registerStickyScrolling(tableRef) {
@@ -37,29 +37,12 @@ export function mapSortBy(pagination, columns) {
37
37
  ];
38
38
  }
39
39
 
40
- /**
41
- * Wait for a ref to have a value and then resolve the promise
42
- *
43
- * @param ref
44
- * @param value
45
- * @returns {Promise<void>}
46
- */
47
- export function waitForRef(ref, value) {
48
- return new Promise<void>((resolve) => {
49
- watch(ref, (newValue) => {
50
- if (newValue === value) {
51
- resolve();
52
- }
53
- });
54
- });
55
- }
56
-
57
40
  /**
58
41
  * Returns the filter from the URL if it is set
59
42
  * @param url
60
43
  * @param allowedKeys
61
44
  */
62
- export function getFilterFromUrl(url, allowedKeys = null) {
45
+ export function getFilterFromUrl(url: string, allowedKeys = null) {
63
46
  const filter = {};
64
47
  const urlFilter = getUrlParam("filter", url);
65
48
  if (urlFilter) {
@@ -34,7 +34,7 @@
34
34
  </a>
35
35
  <q-item
36
36
  v-else
37
- :key="item.action"
37
+ :key="item.name || item.action"
38
38
  clickable
39
39
  :class="item.class"
40
40
  @click="onAction(item)"
@@ -55,7 +55,7 @@ defineProps({
55
55
  type: Array,
56
56
  required: true,
57
57
  validator(items) {
58
- return items.every((item) => item.label && (item.url || item.action));
58
+ return items.every((item) => item.label && (item.url || item.action || item.name));
59
59
  }
60
60
  },
61
61
  disabled: Boolean,
@@ -63,7 +63,7 @@ defineProps({
63
63
  });
64
64
 
65
65
  function onAction(item) {
66
- emit('action', item.action);
66
+ emit('action', item.name || item.action);
67
67
  emit('action-item', item);
68
68
  }
69
69
  </script>
@@ -1,11 +1,20 @@
1
1
  <template>
2
2
  <div>
3
- <Component v-if="confirmDialog" :is="confirmDialog" :is-saving="isSaving" @confirm="onConfirmAction" />
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
+ />
4
11
  </div>
5
12
  </template>
6
13
  <script setup>
7
14
  import { onMounted, ref, shallowRef } from 'vue';
15
+ import { FlashMessages } from '../../../helpers';
8
16
 
17
+ const emit = defineEmits(['done']);
9
18
  const props = defineProps({
10
19
  action: {
11
20
  type: Object,
@@ -21,22 +30,48 @@ const confirmDialog = shallowRef(props.action.confirmDialog ? props.action.confi
21
30
  const isSaving = ref(null);
22
31
 
23
32
  onMounted(async () => {
24
- console.log('mounting action', props.action, props.targets);
25
33
  // If there is no dialog, we auto-confirm the action
26
34
  if (!confirmDialog.value) {
27
- onConfirmAction();
35
+ await onConfirmAction();
28
36
  }
29
37
  });
30
38
 
31
- function onConfirmAction(input) {
32
- console.log('action confirmed', input);
39
+ async function onConfirmAction(input) {
33
40
  if (!props.action.onAction) {
34
41
  throw new Error('No onAction handler found for the selected action:' + props.action.name);
35
42
  }
36
43
 
37
44
  isSaving.value = true;
38
- props.action.onAction().then(() => {
39
- isSaving.value = false;
40
- });
45
+ const result = await props.action.onAction(props.targets, input);
46
+ isSaving.value = false;
47
+
48
+ if (!result.success) {
49
+ const errors = [];
50
+ if (result.errors) {
51
+ errors.push(...result.errors);
52
+ } else if (result.error) {
53
+ errors.push(result.error.message);
54
+ } else {
55
+ errors.push('An unknown error occurred. Please try again later.');
56
+ }
57
+
58
+ FlashMessages.combine('error', errors);
59
+
60
+ if (props.action.onError) {
61
+ await props.action.onError(result, props.targets, input);
62
+ }
63
+ }
64
+
65
+ FlashMessages.success(`The update was successful`);
66
+
67
+ if (props.action.onSuccess) {
68
+ await props.action.onSuccess(result, props.targets, input);
69
+ }
70
+
71
+ if (props.action.onFinish) {
72
+ await props.action.onFinish(result, props.targets, input);
73
+ }
74
+
75
+ emit('done');
41
76
  }
42
77
  </script>
@@ -1,10 +1,59 @@
1
1
  import { ref } from "vue";
2
+ import { waitForRef } from "./index";
2
3
 
3
4
  export const activeAction = ref(null);
4
5
  export const actionTargets = ref([]);
5
6
 
6
- export async function performAction(action, targets) {
7
- console.log("performing action", action, targets);
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
+ return {
16
+ /**
17
+ * Perform an action on a set of targets
18
+ *
19
+ * @param name - can either be a string or an action object
20
+ * @param targets - an array of targets (or a single target object)
21
+ * @param options
22
+ * @returns {Promise<void>}
23
+ */
24
+ async performAction(name, targets, options = {}) {
25
+ const action = typeof name === "string" ? actions.find(a => a.name === name) : name;
26
+ if (!action) {
27
+ throw new Error(`Unknown action: ${name}`);
28
+ }
29
+ targets = Array.isArray(targets) ? targets : [targets];
30
+
31
+ await performAction({ ...action, ...options }, targets);
32
+ }
33
+ };
34
+ }
35
+
36
+ /**
37
+ * Perform an action on a set of targets
38
+ *
39
+ * NOTE: This function and variables should be used w/ the ActionPerformerTool - make sure to use a Layout that has
40
+ * rendered this component so the actions will be performed
41
+ *
42
+ * @param action
43
+ * @param targets
44
+ * @returns {Promise<void>}
45
+ */
46
+ export async function performAction(action: any, targets: any[]): Promise<void> {
8
47
  activeAction.value = action;
9
48
  actionTargets.value = targets;
49
+ await waitForRef(activeAction, null);
50
+ }
51
+
52
+ /**
53
+ * Clear the active action and targets - (note: this will tear down any dialogs / rendered components) triggered by the
54
+ * ActionPerformerTool
55
+ */
56
+ export function clearAction() {
57
+ activeAction.value = null;
58
+ actionTargets.value = [];
10
59
  }
@@ -1,3 +1,5 @@
1
+ import { watch } from "vue";
2
+
1
3
  /**
2
4
  * Sleep function to be used in conjuction with async await:
3
5
  *
@@ -7,7 +9,24 @@
7
9
  * @returns {Promise<any>}
8
10
  */
9
11
  export function sleep(delay) {
10
- return new Promise((resolve) => setTimeout(resolve, delay));
12
+ return new Promise((resolve) => setTimeout(resolve, delay));
13
+ }
14
+
15
+ /**
16
+ * Wait for a ref to have a value and then resolve the promise
17
+ *
18
+ * @param ref
19
+ * @param value
20
+ * @returns {Promise<void>}
21
+ */
22
+ export function waitForRef(ref, value) {
23
+ return new Promise<void>((resolve) => {
24
+ watch(ref, (newValue) => {
25
+ if (newValue === value) {
26
+ resolve();
27
+ }
28
+ });
29
+ });
11
30
  }
12
31
 
13
32
  /**
@@ -18,7 +37,7 @@ export function sleep(delay) {
18
37
  * @returns {number}
19
38
  */
20
39
  export function minmax(min, max, value) {
21
- return Math.max(min, Math.min(max, value));
40
+ return Math.max(min, Math.min(max, value));
22
41
  }
23
42
 
24
43
  /**
@@ -27,7 +46,7 @@ export function minmax(min, max, value) {
27
46
  * @returns {number}
28
47
  */
29
48
  export function metersToMiles(meters) {
30
- return meters * 0.000621371;
49
+ return meters * 0.000621371;
31
50
  }
32
51
 
33
52
  /**
@@ -36,7 +55,7 @@ export function metersToMiles(meters) {
36
55
  * @returns {number}
37
56
  */
38
57
  export function milesToMeters(miles) {
39
- return miles / 0.000621371;
58
+ return miles / 0.000621371;
40
59
  }
41
60
 
42
61
  /**
@@ -45,19 +64,19 @@ export function milesToMeters(miles) {
45
64
  * @returns {null|{lng: number, lat: number}}
46
65
  */
47
66
  export function parseCoords(location) {
48
- const latLong = location.split(",");
67
+ const latLong = location.split(",");
49
68
 
50
- if (latLong.length === 2) {
51
- const lat = parseFloat(latLong[0].trim());
52
- const lng = parseFloat(latLong[1].trim());
69
+ if (latLong.length === 2) {
70
+ const lat = parseFloat(latLong[0].trim());
71
+ const lng = parseFloat(latLong[1].trim());
53
72
 
54
- if (!isNaN(lat) && !isNaN(lng)) {
55
- return {
56
- lat,
57
- lng,
58
- };
73
+ if (!isNaN(lat) && !isNaN(lng)) {
74
+ return {
75
+ lat,
76
+ lng,
77
+ };
78
+ }
59
79
  }
60
- }
61
80
 
62
- return null;
81
+ return null;
63
82
  }