quasar-ui-danx 0.0.31 → 0.0.33

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.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>