quasar-ui-danx 0.0.24 → 0.0.26

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.24",
3
+ "version": "0.0.26",
4
4
  "author": "Dan <dan@flytedesk.com>",
5
5
  "description": "DanX Vue / Quasar component library",
6
6
  "license": "MIT",
@@ -0,0 +1,97 @@
1
+ <template>
2
+ <div>
3
+ <PopoverMenu
4
+ class="px-4 h-full flex"
5
+ :items="items"
6
+ @action-item="onAction"
7
+ />
8
+ <Component
9
+ v-if="confirmDialog"
10
+ :is="confirmDialog.is"
11
+ v-bind="confirmDialog.props"
12
+ :is-saving="isSaving"
13
+ @close="onCancel"
14
+ @confirm="onConfirmAction"
15
+ />
16
+ </div>
17
+ </template>
18
+ <script setup>
19
+ import { ref, shallowRef } from 'vue';
20
+ import { FlashMessages } from '../../helpers';
21
+ import { PopoverMenu } from '../Utility';
22
+
23
+ const emit = defineEmits(['action']);
24
+ const props = defineProps({
25
+ items: {
26
+ type: Array,
27
+ required: true
28
+ },
29
+ rows: {
30
+ type: Array,
31
+ required: true
32
+ }
33
+ });
34
+
35
+
36
+ const activeAction = shallowRef(null);
37
+ const confirmDialog = shallowRef(null);
38
+ const isSaving = ref(false);
39
+
40
+ function onAction(item) {
41
+ emit('action', item);
42
+
43
+ activeAction.value = item;
44
+
45
+ if (item.confirmDialog) {
46
+ confirmDialog.value = typeof item.confirmDialog === 'function' ? item.confirmDialog(props.rows) : item.confirmDialog;
47
+ } else {
48
+ console.log('handle default action');
49
+ }
50
+ }
51
+
52
+ function onCancel() {
53
+ activeAction.value = null;
54
+ confirmDialog.value = null;
55
+ }
56
+
57
+ async function onConfirmAction(input) {
58
+ if (!activeAction.value.onAction) {
59
+ throw new Error('No onAction handler found for the selected action:' + activeAction.value.action);
60
+ }
61
+
62
+ isSaving.value = true;
63
+ const result = await activeAction.value.onAction(input, props.rows);
64
+ isSaving.value = false;
65
+
66
+ if (!result.success) {
67
+ const errors = [];
68
+ if (result.errors) {
69
+ errors.push(...result.errors);
70
+ } else if (result.error) {
71
+ errors.push(result.error.message);
72
+ } else {
73
+ errors.push('An unknown error occurred. Please try again later.');
74
+ }
75
+
76
+ FlashMessages.combine('error', errors);
77
+
78
+ if (activeAction.value.onError) {
79
+ await activeAction.value.onError(result, input);
80
+ }
81
+ }
82
+
83
+ FlashMessages.success(`The update was successful`);
84
+
85
+ if (activeAction.value.onSuccess) {
86
+ await activeAction.value.onSuccess(result, input);
87
+ }
88
+
89
+ if (activeAction.value.onFinish) {
90
+ await activeAction.value.onFinish();
91
+ }
92
+
93
+ confirmDialog.value = null;
94
+ activeAction.value = null;
95
+ }
96
+
97
+ </script>
@@ -12,7 +12,7 @@
12
12
  color="blue-base"
13
13
  @update:selected="$emit('update:selected-rows', $event)"
14
14
  @update:pagination="() => {}"
15
- @request="$emit('update:quasar-pagination', $event.pagination)"
15
+ @request="$emit('update:quasar-pagination', {...$event.pagination, __sort: mapSortBy($event.pagination, columns)})"
16
16
  >
17
17
  <template #no-data>
18
18
  <slot name="empty">
@@ -51,24 +51,30 @@
51
51
  <q-td :key="rowProps.key" :props="rowProps">
52
52
  <component
53
53
  :is="rowProps.col.onClick ? 'a' : 'div'"
54
+ class="flex items-center flex-nowrap"
54
55
  @click="() => rowProps.col.onClick && rowProps.col.onClick(rowProps.row)"
55
56
  >
56
- <template v-if="rowProps.col.component">
57
- <RenderComponent
58
- :row-props="rowProps"
59
- @action="$emit('action', $event)"
60
- />
61
- </template>
62
- <template v-else-if="rowProps.col.fieldList">
57
+ <RenderComponent
58
+ v-if="rowProps.col.component"
59
+ :row-props="rowProps"
60
+ @action="$emit('action', $event)"
61
+ />
62
+ <div v-else-if="rowProps.col.fieldList">
63
63
  <div v-for="field in rowProps.col.fieldList" :key="field">
64
64
  {{ rowProps.row[field] }}
65
65
  </div>
66
- </template>
67
- <template v-else>
66
+ </div>
67
+ <div v-else>
68
68
  <slot v-bind="{name: rowProps.col.name, row: rowProps.row, value: rowProps.value}">
69
69
  {{ rowProps.value }}
70
70
  </slot>
71
- </template>
71
+ </div>
72
+ <ActionMenu
73
+ v-if="rowProps.col.actions" class="ml-2"
74
+ :items="rowProps.col.actions"
75
+ :rows="[rowProps.row]"
76
+ @action="(action) => $emit('action', {action: action, row: rowProps.row})"
77
+ />
72
78
  </component>
73
79
  </q-td>
74
80
  </template>
@@ -79,7 +85,14 @@
79
85
  import { ref } from 'vue';
80
86
  import { DragHandleIcon as RowResizeIcon } from '../../svg';
81
87
  import { HandleDraggable } from '../DragAndDrop';
82
- import { EmptyTableState, registerStickyScrolling, RenderComponent, TableSummaryRow } from './index';
88
+ import {
89
+ ActionMenu,
90
+ EmptyTableState,
91
+ mapSortBy,
92
+ registerStickyScrolling,
93
+ RenderComponent,
94
+ TableSummaryRow
95
+ } from './index';
83
96
 
84
97
  defineEmits(['action', 'filter', 'update:quasar-pagination', 'update:selected-rows']);
85
98
  defineProps({
@@ -9,7 +9,7 @@
9
9
  >
10
10
  <FilterGroupList
11
11
  :filter="filter"
12
- :filter-groups="filterGroups"
12
+ :filter-fields="filterFields"
13
13
  @update:filter="$emit('update:filter', $event)"
14
14
  />
15
15
  </CollapsableSidebar>
@@ -29,7 +29,7 @@ defineProps({
29
29
  type: Object,
30
30
  default: null
31
31
  },
32
- filterGroups: {
32
+ filterFields: {
33
33
  type: Array,
34
34
  default: () => []
35
35
  }
@@ -2,7 +2,7 @@
2
2
  <q-list>
3
3
  <div class="px-4 py-2 max-w-full">
4
4
  <template
5
- v-for="(group, index) in filterGroups"
5
+ v-for="(group, index) in filterFields"
6
6
  :key="'group-' + group.name"
7
7
  >
8
8
  <template v-if="group.flat">
@@ -34,12 +34,12 @@
34
34
  </FilterGroupItem>
35
35
 
36
36
  <q-separator
37
- v-if="index < (filterGroups.length - 1)"
37
+ v-if="index < (filterFields.length - 1)"
38
38
  class="my-2"
39
39
  />
40
40
  </template>
41
41
  </div>
42
- </q-list>
42
+ </q-list>
43
43
  </template>
44
44
  <script setup>
45
45
  import { computed } from 'vue';
@@ -48,7 +48,7 @@ import FilterGroupItem from './FilterGroupItem';
48
48
 
49
49
  const emit = defineEmits(['update:filter']);
50
50
  const props = defineProps({
51
- filterGroups: {
51
+ filterFields: {
52
52
  type: Array,
53
53
  required: true
54
54
  },
@@ -61,7 +61,7 @@ const props = defineProps({
61
61
 
62
62
  const activeCountByGroup = computed(() => {
63
63
  const activeCountByGroup = {};
64
- for (const group of props.filterGroups) {
64
+ for (const group of props.filterFields) {
65
65
  activeCountByGroup[group.name] = group.fields.filter(field => props.filter[field.name] !== undefined).length;
66
66
  }
67
67
  return activeCountByGroup;
@@ -3,6 +3,7 @@ export * from "./Form";
3
3
  export * from "./listActions";
4
4
  export * from "./listHelpers";
5
5
  export * from "./tableColumns";
6
+ export { default as ActionMenu } from "./ActionMenu.vue";
6
7
  export { default as ActionTable } from "./ActionTable.vue";
7
8
  export { default as BatchActionMenu } from "./BatchActionMenu.vue";
8
9
  export { default as EmptyTableState } from "./EmptyTableState.vue";
@@ -1,6 +1,6 @@
1
1
  import { computed, ref, watch } from "vue";
2
2
  import { getItem, setItem } from "../../helpers";
3
- import { getFilterFromUrl, mapSortBy, waitForRef } from "./listHelpers";
3
+ import { getFilterFromUrl, waitForRef } from "./listHelpers";
4
4
 
5
5
  export function useListActions(name, {
6
6
  listRoute,
@@ -10,7 +10,6 @@ export function useListActions(name, {
10
10
  applyActionRoute = null,
11
11
  applyBatchActionRoute = null,
12
12
  itemDetailsRoute = null,
13
- columns = null,
14
13
  filterGroups = null,
15
14
  refreshFilters = false,
16
15
  urlPattern = null,
@@ -30,6 +29,7 @@ export function useListActions(name, {
30
29
  const filterActiveCount = computed(() => Object.keys(filter.value).filter(key => filter.value[key] !== undefined).length);
31
30
 
32
31
  const PAGING_DEFAULT = {
32
+ __sort: null,
33
33
  sortBy: null,
34
34
  descending: false,
35
35
  page: 1,
@@ -42,7 +42,7 @@ export function useListActions(name, {
42
42
  perPage: quasarPagination.value.rowsPerPage,
43
43
  page: quasarPagination.value.page,
44
44
  filter: { ...filter.value, ...globalFilter.value },
45
- sort: columns ? mapSortBy(quasarPagination.value, columns) : undefined
45
+ sort: quasarPagination.value.__sort || undefined
46
46
  }));
47
47
 
48
48
  // When any part of the filter changes, get the new list of creatives
@@ -95,15 +95,15 @@ export function useListActions(name, {
95
95
  isLoadingFilters.value = false;
96
96
  }
97
97
 
98
- // A flat list of valid filterable field names
99
- const validFilterKeys = computed(() => filterGroups?.value?.map(group => group.fields.map(field => field.name)).flat());
100
-
101
98
  /**
102
99
  * Watches for a filter URL parameter and applies the filter if it is set.
103
100
  */
104
- function applyFilterFromUrl(url) {
101
+ function applyFilterFromUrl(url, filterGroups = null) {
105
102
  if (url.match(urlPattern)) {
106
- const urlFilter = getFilterFromUrl(url, validFilterKeys.value);
103
+ // A flat list of valid filterable field names
104
+ const validFilterKeys = filterGroups?.value?.map(group => group.fields.map(field => field.name)).flat();
105
+
106
+ const urlFilter = getFilterFromUrl(url, validFilterKeys);
107
107
 
108
108
  if (Object.keys(urlFilter).length > 0) {
109
109
  filter.value = urlFilter;
@@ -187,9 +187,6 @@ export function useListActions(name, {
187
187
  filter.value = { ...filterDefaults, ...filter.value };
188
188
  }
189
189
 
190
- // Load the URL filters if they are set
191
- applyFilterFromUrl(window.location.href);
192
-
193
190
  setTimeout(() => {
194
191
  if (!isLoadingList.value) {
195
192
  loadList();
@@ -355,7 +352,6 @@ export function useListActions(name, {
355
352
  isApplyingBatchAction,
356
353
  activeItem,
357
354
  formTab,
358
- columns,
359
355
  filterGroups,
360
356
 
361
357
  // Actions
@@ -37,19 +37,19 @@
37
37
  :key="item.action"
38
38
  clickable
39
39
  :class="item.class"
40
- @click="$emit('action', item.action)"
40
+ @click="onAction(item)"
41
41
  >
42
42
  {{ item.label }}
43
- </q-item>
43
+ </q-item>
44
44
  </template>
45
- </q-list>
46
- </q-menu>
45
+ </q-list>
46
+ </q-menu>
47
47
  </a>
48
48
  </template>
49
49
  <script setup>
50
50
  import { DotsVerticalIcon as MenuIcon } from '@heroicons/vue/outline';
51
51
 
52
- defineEmits(['action']);
52
+ const emit = defineEmits(['action', 'action-item']);
53
53
  defineProps({
54
54
  items: {
55
55
  type: Array,
@@ -61,4 +61,9 @@ defineProps({
61
61
  disabled: Boolean,
62
62
  loading: Boolean
63
63
  });
64
+
65
+ function onAction(item) {
66
+ emit('action', item.action);
67
+ emit('action-item', item);
68
+ }
64
69
  </script>