quasar-ui-danx 0.0.31 → 0.0.33

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.31",
3
+ "version": "0.0.33",
4
4
  "author": "Dan <dan@flytedesk.com>",
5
5
  "description": "DanX Vue / Quasar component library",
6
6
  "license": "MIT",
@@ -1,9 +1,12 @@
1
1
  <template>
2
2
  <PopoverMenu
3
- class="px-4 h-full flex"
3
+ class="px-2 flex action-button"
4
4
  :items="activeItems"
5
+ :disabled="targets.length === 0"
5
6
  @action-item="onAction"
6
- />
7
+ >
8
+ <q-tooltip v-if="targets.length === 0">{{ tooltip }}</q-tooltip>
9
+ </PopoverMenu>
7
10
  </template>
8
11
  <script setup>
9
12
  import { computed } from 'vue';
@@ -19,12 +22,16 @@ const props = defineProps({
19
22
  targets: {
20
23
  type: Array,
21
24
  required: true
25
+ },
26
+ tooltip: {
27
+ type: String,
28
+ default: 'First select records to perform a batch action'
22
29
  }
23
30
  });
24
31
 
25
32
  const activeItems = computed(() => props.items.filter(item => {
26
33
  if (item.enabled === undefined) return true;
27
- return typeof item.enabled === 'function' ? !!item.enabled(props.targets) : !!item.enabled;
34
+ return typeof item.enabled === 'function' ? !!item.enabled(props.targets?.[0] ?? null, props.targets) : !!item.enabled;
28
35
  }));
29
36
 
30
37
  function onAction(item) {
@@ -70,12 +70,13 @@
70
70
  {{ rowProps.value }}
71
71
  </slot>
72
72
  </div>
73
- <ActionMenu
74
- v-if="rowProps.col.actions" class="ml-2"
75
- :items="rowProps.col.actions"
76
- :targets="[rowProps.row]"
77
- @action="(action) => $emit('action', {action: action, row: rowProps.row})"
78
- />
73
+ <div v-if="rowProps.col.actions" class="flex-grow flex justify-end pl-2">
74
+ <ActionMenu
75
+ :items="rowProps.col.actions"
76
+ :targets="[rowProps.row]"
77
+ @action="(action) => $emit('action', {action: action, row: rowProps.row})"
78
+ />
79
+ </div>
79
80
  </component>
80
81
  </q-td>
81
82
  </template>
@@ -6,7 +6,6 @@ export * from "./listHelpers";
6
6
  export * from "./tableColumns";
7
7
  export { default as ActionMenu } from "./ActionMenu.vue";
8
8
  export { default as ActionTable } from "./ActionTable.vue";
9
- export { default as BatchActionMenu } from "./BatchActionMenu.vue";
10
9
  export { default as EmptyTableState } from "./EmptyTableState.vue";
11
10
  export { default as RenderComponent } from "./RenderComponent.vue";
12
11
  export { default as TableSummaryRow } from "./TableSummaryRow.vue";
@@ -2,18 +2,30 @@ import { computed, ref, watch } from "vue";
2
2
  import { getItem, setItem, waitForRef } from "../../helpers";
3
3
  import { getFilterFromUrl } from "./listHelpers";
4
4
 
5
- export function useListActions(name, {
5
+ interface ListActionsOptions {
6
+ listRoute: Function;
7
+ summaryRoute?: Function;
8
+ filterFieldOptionsRoute?: Function;
9
+ moreRoute?: Function;
10
+ applyActionRoute?: Function;
11
+ itemDetailsRoute?: Function;
12
+ urlPattern?: RegExp;
13
+ filterDefaults?: Record<string, any>;
14
+ refreshFilters?: boolean;
15
+ }
16
+
17
+
18
+ export function useListActions(name: string, {
6
19
  listRoute,
7
- filterFieldOptionsRoute,
8
20
  summaryRoute = null,
21
+ filterFieldOptionsRoute = null,
9
22
  moreRoute = null,
10
23
  applyActionRoute = null,
11
- applyBatchActionRoute = null,
12
24
  itemDetailsRoute = null,
13
25
  refreshFilters = false,
14
26
  urlPattern = null,
15
27
  filterDefaults = {}
16
- }) {
28
+ }: ListActionsOptions) {
17
29
  let isInitialized = false;
18
30
  const PAGE_SETTINGS_KEY = `${name}-pagination-settings`;
19
31
  const pagedItems = ref(null);
@@ -60,23 +72,22 @@ export function useListActions(name, {
60
72
  }
61
73
 
62
74
  async function loadList() {
63
- if (isInitialized) {
64
- isLoadingList.value = true;
65
- setPagedItems(await listRoute(pager.value));
66
- isLoadingList.value = false;
67
- }
75
+ if (!isInitialized) return;
76
+ isLoadingList.value = true;
77
+ setPagedItems(await listRoute(pager.value));
78
+ isLoadingList.value = false;
68
79
  }
69
80
 
70
81
  async function loadSummary() {
71
- if (summaryRoute && isInitialized) {
72
- isLoadingSummary.value = true;
73
- const summaryFilter = { id: null, ...filter.value, ...globalFilter.value };
74
- if (selectedRows.value.length) {
75
- summaryFilter.id = selectedRows.value.map((row) => row.id);
76
- }
77
- summary.value = await summaryRoute(summaryFilter);
78
- isLoadingSummary.value = false;
82
+ if (!summaryRoute || !isInitialized) return;
83
+
84
+ isLoadingSummary.value = true;
85
+ const summaryFilter = { id: null, ...filter.value, ...globalFilter.value };
86
+ if (selectedRows.value.length) {
87
+ summaryFilter.id = selectedRows.value.map((row) => row.id);
79
88
  }
89
+ summary.value = await summaryRoute(summaryFilter);
90
+ isLoadingSummary.value = false;
80
91
  }
81
92
 
82
93
  // Filter fields are the field values available for the currently applied filter on Creative Groups
@@ -89,6 +100,7 @@ export function useListActions(name, {
89
100
  });
90
101
 
91
102
  async function loadFilterFieldOptions() {
103
+ if (!filterFieldOptionsRoute || !isInitialized) return;
92
104
  isLoadingFilters.value = true;
93
105
  filterFieldOptions.value = await filterFieldOptionsRoute(filter.value);
94
106
  isLoadingFilters.value = false;
@@ -240,21 +252,6 @@ export function useListActions(name, {
240
252
  return result;
241
253
  }
242
254
 
243
- /**
244
- * Applies an action to all selected items.
245
- */
246
- const isApplyingBatchAction = ref(false);
247
-
248
- async function applyBatchAction(input) {
249
- isApplyingBatchAction.value = true;
250
- const batchFilter = { id: selectedRows.value.map(r => r.id) };
251
- const result = await applyBatchActionRoute(batchFilter, input);
252
- isApplyingBatchAction.value = false;
253
- await refreshAll();
254
-
255
- return result;
256
- }
257
-
258
255
  // The active ad for viewing / editing in the Ad Form
259
256
  const activeItem = ref(null);
260
257
  // Controls the tab on the Ad Form
@@ -267,7 +264,7 @@ export function useListActions(name, {
267
264
  */
268
265
  async function getActiveItemDetails() {
269
266
  if (!activeItem.value) return;
270
-
267
+
271
268
  const result = await itemDetailsRoute(activeItem.value);
272
269
 
273
270
  // Only set the ad details if we are the response for the currently loaded item
@@ -337,11 +334,11 @@ export function useListActions(name, {
337
334
  activeItem.value = pagedItems.value.data[nextIndex];
338
335
  }
339
336
 
340
- // Async load the settings for this Action List
341
- setTimeout(() => {
337
+ // Initialize the list actions and load settings, lists, summaries, filter fields, etc.
338
+ function initialize() {
342
339
  isInitialized = true;
343
340
  loadSettings();
344
- }, 1);
341
+ }
345
342
 
346
343
  return {
347
344
  // State
@@ -359,18 +356,17 @@ export function useListActions(name, {
359
356
  pager,
360
357
  quasarPagination,
361
358
  isApplyingActionToItem,
362
- isApplyingBatchAction,
363
359
  activeItem,
364
360
  formTab,
365
361
 
366
362
  // Actions
363
+ initialize,
367
364
  loadSummary,
368
365
  resetPaging,
369
366
  loadList,
370
367
  loadMore,
371
368
  refreshAll,
372
369
  applyAction,
373
- applyBatchAction,
374
370
  getNextItem,
375
371
  openItemForm,
376
372
  applyFilterFromUrl
@@ -3,6 +3,7 @@
3
3
  class="p-3 actionable"
4
4
  :class="{'opacity-50 cursor-not-allowed': disabled}"
5
5
  >
6
+ <slot />
6
7
  <Transition
7
8
  mode="out-in"
8
9
  :duration="150"
@@ -45,7 +45,15 @@ async function onConfirmAction(input) {
45
45
  const result = await props.action.onAction(props.targets, input);
46
46
  isSaving.value = false;
47
47
 
48
- if (!result.success) {
48
+ if (result.success) {
49
+ FlashMessages.success(`The update was successful`);
50
+
51
+ if (props.action.onSuccess) {
52
+ await props.action.onSuccess(result, props.targets, input);
53
+ }
54
+
55
+ emit('done');
56
+ } else {
49
57
  const errors = [];
50
58
  if (result.errors) {
51
59
  errors.push(...result.errors);
@@ -62,16 +70,8 @@ async function onConfirmAction(input) {
62
70
  }
63
71
  }
64
72
 
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
73
  if (props.action.onFinish) {
72
74
  await props.action.onFinish(result, props.targets, input);
73
75
  }
74
-
75
- emit('done');
76
76
  }
77
77
  </script>
@@ -1,26 +1,26 @@
1
1
  export const request = {
2
- async get(url, options = {}) {
3
- return fetch(url, {
4
- method: "get",
5
- headers: {
6
- Accept: "application/json",
7
- "Content-Type": "application/json"
8
- },
9
- ...options
10
- });
11
- },
2
+ async get(url, options = {}) {
3
+ return fetch(url, {
4
+ method: "get",
5
+ headers: {
6
+ Accept: "application/json",
7
+ "Content-Type": "application/json"
8
+ },
9
+ ...options
10
+ }).then((r) => r.json());
11
+ },
12
12
 
13
- async post(url, data = {}, options = {}) {
14
- return fetch(url, {
15
- method: "post",
16
- body: JSON.stringify(data),
17
- headers: {
18
- Accept: "application/json",
19
- "Content-Type": "application/json"
20
- },
21
- ...options
22
- }).then((r) => r.json());
23
- }
13
+ async post(url, data = {}, options = {}) {
14
+ return fetch(url, {
15
+ method: "post",
16
+ body: JSON.stringify(data),
17
+ headers: {
18
+ Accept: "application/json",
19
+ "Content-Type": "application/json"
20
+ },
21
+ ...options
22
+ }).then((r) => r.json());
23
+ }
24
24
  };
25
25
 
26
26
  /**
@@ -36,19 +36,19 @@ export const request = {
36
36
  * @returns {Promise<void>}
37
37
  */
38
38
  export async function fetchResourceListWithSelected(fetchFn, list, id, filter) {
39
- // First make sure we have the selected record, so we can always add it to the list
40
- let selectedResource;
41
- if (id) {
42
- selectedResource = list.value.find((c) => c.id === id) || (await fetchFn({ id }))[0];
43
- }
39
+ // First make sure we have the selected record, so we can always add it to the list
40
+ let selectedResource;
41
+ if (id) {
42
+ selectedResource = list.value.find((c) => c.id === id) || (await fetchFn({ id }))[0];
43
+ }
44
44
 
45
- // Get the filtered campaign list
46
- list.value = await fetchFn(filter);
45
+ // Get the filtered campaign list
46
+ list.value = await fetchFn(filter);
47
47
 
48
- // If our selected campaign is not in the filtered list, add it
49
- if (selectedResource && !list.value.find((c) => c.id === id)) {
50
- list.value.push(selectedResource);
51
- }
48
+ // If our selected campaign is not in the filtered list, add it
49
+ if (selectedResource && !list.value.find((c) => c.id === id)) {
50
+ list.value.push(selectedResource);
51
+ }
52
52
  }
53
53
 
54
54
  /**
@@ -57,6 +57,6 @@ export async function fetchResourceListWithSelected(fetchFn, list, id, filter) {
57
57
  * @param url
58
58
  */
59
59
  export function getUrlParam(key, url = undefined) {
60
- const params = new URLSearchParams(url?.replace(/.*\?/, "") || window.location.search);
61
- return params.get(key);
60
+ const params = new URLSearchParams(url?.replace(/.*\?/, "") || window.location.search);
61
+ return params.get(key);
62
62
  }
@@ -12,6 +12,17 @@ export const actionTargets = ref([]);
12
12
  * @returns {{performAction(name, targets): Promise<void>}}
13
13
  */
14
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
+
15
26
  return {
16
27
  /**
17
28
  * Perform an action on a set of targets
@@ -31,7 +42,8 @@ export function usePerformAction(actions: any[]) {
31
42
  await performAction({ ...action, ...options }, targets);
32
43
  },
33
44
 
34
- batchActions: actions.filter(a => a.batch)
45
+ actions,
46
+ filterActions,
35
47
  };
36
48
  }
37
49
 
@@ -0,0 +1,10 @@
1
+ .action-button {
2
+ cursor: pointer;
3
+ border-radius: 0.5em;
4
+ transition: all 0.5s;
5
+ outline: 1px solid transparent;
6
+
7
+ &:hover {
8
+ @apply bg-blue-light outline outline-blue-base;
9
+ }
10
+ }
@@ -1,60 +0,0 @@
1
- <template>
2
- <div>
3
- <PopoverMenu
4
- class="bg-neutral-plus-6 px-4 h-full flex"
5
- :items="items"
6
- :disabled="selectedRows.length === 0"
7
- @action="onAction"
8
- />
9
- <q-tooltip v-if="selectedRows.length === 0">
10
- Batch actions require a selection
11
- </q-tooltip>
12
- <slot>
13
- <Component
14
- :is="activeComponent.is"
15
- v-if="activeComponent"
16
- v-bind="activeComponent.props"
17
- :is-saving="isSaving"
18
- @close="activeAction = false"
19
- @confirm="onConfirmAction"
20
- />
21
- </slot>
22
- </div>
23
- </template>
24
- <script setup>
25
- import { computed, ref } from 'vue';
26
- import { PopoverMenu } from '../Utility';
27
-
28
- const emit = defineEmits(['action']);
29
- const props = defineProps({
30
- items: {
31
- type: Array,
32
- required: true
33
- },
34
- selectedRows: {
35
- type: Array,
36
- required: true
37
- },
38
- applyBatchAction: {
39
- type: Function,
40
- required: true
41
- },
42
- isSaving: Boolean
43
- });
44
-
45
-
46
- const activeAction = ref(null);
47
- const activeComponent = computed(() => (props.items.find(i => i.action === activeAction.value)?.component || (() => null))(props.selectedRows));
48
-
49
- function onAction(action) {
50
- activeAction.value = action;
51
- emit('action', action);
52
- }
53
- async function onConfirmAction(input) {
54
- const result = await props.applyBatchAction(input || activeComponent.value.input());
55
-
56
- if (result?.success) {
57
- activeAction.value = null;
58
- }
59
- }
60
- </script>