quasar-ui-danx 0.0.23 → 0.0.25
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 +1 -1
- package/src/components/ActionTable/ActionMenu.vue +97 -0
- package/src/components/ActionTable/ActionTable.vue +25 -12
- package/src/components/ActionTable/BatchActionMenu.vue +1 -1
- package/src/components/ActionTable/Form/Fields/MultiFileField.vue +3 -3
- package/src/components/ActionTable/Form/Fields/SingleFileField.vue +2 -2
- package/src/components/ActionTable/index.ts +1 -0
- package/src/components/ActionTable/listActions.ts +8 -12
- package/src/components/DragAndDrop/ListItemDraggable.vue +1 -1
- package/src/components/Utility/Files/FilePreview.vue +3 -3
- package/src/components/Utility/Popovers/PopoverMenu.vue +10 -5
package/package.json
CHANGED
@@ -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
|
-
<
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
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
|
-
</
|
67
|
-
<
|
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
|
-
</
|
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 {
|
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({
|
@@ -23,7 +23,7 @@
|
|
23
23
|
</template>
|
24
24
|
<script setup>
|
25
25
|
import { computed, ref } from 'vue';
|
26
|
-
import PopoverMenu from '
|
26
|
+
import { PopoverMenu } from '../Utility';
|
27
27
|
|
28
28
|
const emit = defineEmits(['action']);
|
29
29
|
const props = defineProps({
|
@@ -33,7 +33,7 @@
|
|
33
33
|
</div>
|
34
34
|
|
35
35
|
<div class="max-w-[50em] flex items-stretch justify-start">
|
36
|
-
<
|
36
|
+
<FilePreview
|
37
37
|
v-for="file in uploadedFiles"
|
38
38
|
:key="'file-upload-' + file.id"
|
39
39
|
class="w-32 m-2 cursor-pointer bg-neutral-plus-5"
|
@@ -44,7 +44,7 @@
|
|
44
44
|
:removable="!readonly && !disable"
|
45
45
|
@remove="onRemove(file)"
|
46
46
|
/>
|
47
|
-
<
|
47
|
+
<FilePreview
|
48
48
|
v-if="!disable && !readonly"
|
49
49
|
class="w-32 m-2 cursor-pointer border border-dashed border-blue-base"
|
50
50
|
disabled
|
@@ -63,7 +63,7 @@
|
|
63
63
|
<script setup>
|
64
64
|
import { onMounted } from 'vue';
|
65
65
|
import { useMultiFileUpload } from '../../../../helpers';
|
66
|
-
import {
|
66
|
+
import { FilePreview } from '../../../Utility';
|
67
67
|
import FieldLabel from './FieldLabel';
|
68
68
|
|
69
69
|
const emit = defineEmits(['update:model-value']);
|
@@ -31,7 +31,7 @@
|
|
31
31
|
/>
|
32
32
|
</div>
|
33
33
|
|
34
|
-
<
|
34
|
+
<FilePreview
|
35
35
|
v-if="!readonly || uploadedFile"
|
36
36
|
class="w-32 cursor-pointer mt-2"
|
37
37
|
:class="{'border border-dashed border-blue-base': !uploadedFile, 'mx-auto': !readonly}"
|
@@ -50,7 +50,7 @@
|
|
50
50
|
<script setup>
|
51
51
|
import { onMounted } from 'vue';
|
52
52
|
import { useSingleFileUpload } from '../../../../helpers';
|
53
|
-
import {
|
53
|
+
import { FilePreview } from '../../../Utility';
|
54
54
|
import FieldLabel from './FieldLabel';
|
55
55
|
|
56
56
|
const emit = defineEmits(['update:model-value']);
|
@@ -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,
|
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:
|
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
|
-
|
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
|
@@ -16,8 +16,8 @@
|
|
16
16
|
</div>
|
17
17
|
</template>
|
18
18
|
<script setup>
|
19
|
-
import SvgImg from 'src/components/Utility/Files/SvgImg';
|
20
19
|
import { DragHandleDotsIcon as DragHandleIcon } from '../../svg';
|
20
|
+
import { SvgImg } from '../Utility';
|
21
21
|
import { ListDragAndDrop } from './listDragAndDrop';
|
22
22
|
|
23
23
|
const emit = defineEmits(['position', 'update:list-items']);
|
@@ -99,10 +99,10 @@
|
|
99
99
|
|
100
100
|
<script setup>
|
101
101
|
import { DocumentTextIcon as TextFileIcon, DownloadIcon, PlayIcon } from '@heroicons/vue/outline';
|
102
|
-
import { FullScreenCarouselDialog } from 'src/components/Utility/index';
|
103
|
-
import { download } from 'src/helpers';
|
104
|
-
import { ImageIcon, PdfIcon, TrashIcon as RemoveIcon } from 'src/svg';
|
105
102
|
import { computed, ref } from 'vue';
|
103
|
+
import { download } from '../../../helpers';
|
104
|
+
import { ImageIcon, PdfIcon, TrashIcon as RemoveIcon } from '../../../svg';
|
105
|
+
import { FullScreenCarouselDialog } from '../Dialogs';
|
106
106
|
|
107
107
|
const emit = defineEmits(['remove']);
|
108
108
|
const props = defineProps({
|
@@ -37,19 +37,19 @@
|
|
37
37
|
:key="item.action"
|
38
38
|
clickable
|
39
39
|
:class="item.class"
|
40
|
-
@click="
|
40
|
+
@click="onAction(item)"
|
41
41
|
>
|
42
42
|
{{ item.label }}
|
43
|
-
|
43
|
+
</q-item>
|
44
44
|
</template>
|
45
|
-
|
46
|
-
|
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>
|