quasar-ui-danx 0.2.32 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/danx.es.js +6582 -6372
- package/dist/danx.es.js.map +1 -1
- package/dist/danx.umd.js +5 -5
- package/dist/danx.umd.js.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/components/ActionTable/ActionTableColumn.vue +5 -2
- package/src/components/ActionTable/ActionTableHeaderColumn.vue +2 -1
- package/src/components/ActionTable/Filters/FilterListToggle.vue +1 -2
- package/src/components/ActionTable/Form/Fields/BooleanField.vue +9 -2
- package/src/components/ActionTable/Form/Fields/MultiFileField.vue +2 -2
- package/src/components/ActionTable/Form/Fields/SelectField.vue +1 -1
- package/src/components/ActionTable/Form/Fields/SingleFileField.vue +2 -2
- package/src/components/ActionTable/Form/RenderedForm.vue +148 -10
- package/src/components/ActionTable/listControls.ts +68 -38
- package/src/components/ActionTable/tableColumns.ts +3 -3
- package/src/components/PanelsDrawer/PanelsDrawerPanels.vue +1 -1
- package/src/components/Utility/Tools/RenderVnode.vue +18 -2
- package/src/config/index.ts +2 -7
- package/src/helpers/FileUpload.ts +18 -20
- package/src/helpers/actions.ts +34 -31
- package/src/helpers/array.ts +11 -16
- package/src/helpers/multiFileUpload.ts +16 -16
- package/src/helpers/singleFileUpload.ts +22 -16
- package/src/helpers/utils.ts +20 -25
- package/src/styles/quasar-reset.scss +4 -0
@@ -46,7 +46,8 @@ const columnStyle = computed(() => {
|
|
46
46
|
const width = props.settings?.width || column.value.width;
|
47
47
|
return {
|
48
48
|
width: width ? `${width}px` : undefined,
|
49
|
-
minWidth: column.value.minWidth ? `${column.value.minWidth}px` : undefined
|
49
|
+
minWidth: column.value.minWidth ? `${column.value.minWidth}px` : undefined,
|
50
|
+
...(column.value.headerStyle || {})
|
50
51
|
};
|
51
52
|
});
|
52
53
|
|
@@ -2,7 +2,7 @@
|
|
2
2
|
<div class="flex items-center transition-all" :class="{'w-72': showFilters, 'w-[6.5rem]': !showFilters}">
|
3
3
|
<div class="flex-grow">
|
4
4
|
<QBtn
|
5
|
-
class="
|
5
|
+
class="btn-blue-highlight border-blue-700"
|
6
6
|
:class="{'highlighted': showFilters}"
|
7
7
|
@click="$emit('update:show-filters', !showFilters)"
|
8
8
|
>
|
@@ -38,7 +38,6 @@ const props = defineProps({
|
|
38
38
|
const activeCount = computed(() => Object.keys(props.filter).filter(key => props.filter[key] !== undefined).length);
|
39
39
|
</script>
|
40
40
|
<style lang="scss" scoped>
|
41
|
-
|
42
41
|
.btn-blue-highlight {
|
43
42
|
@apply rounded-lg border border-solid;
|
44
43
|
|
@@ -2,12 +2,13 @@
|
|
2
2
|
<QToggle
|
3
3
|
:data-testid="'boolean-field-' + field.id"
|
4
4
|
:model-value="modelValue"
|
5
|
+
:disable="disable || readonly"
|
5
6
|
:toggle-indeterminate="toggleIndeterminate"
|
6
7
|
:indeterminate-value="undefined"
|
7
8
|
@update:model-value="$emit('update:model-value', $event)"
|
8
9
|
>
|
9
10
|
<FieldLabel
|
10
|
-
:field="field"
|
11
|
+
:field="{...field, label}"
|
11
12
|
:show-name="showName"
|
12
13
|
:class="labelClass"
|
13
14
|
/>
|
@@ -19,6 +20,10 @@ import FieldLabel from "./FieldLabel";
|
|
19
20
|
|
20
21
|
defineEmits(["update:model-value"]);
|
21
22
|
defineProps({
|
23
|
+
label: {
|
24
|
+
type: String,
|
25
|
+
default: null
|
26
|
+
},
|
22
27
|
modelValue: {
|
23
28
|
type: [Boolean],
|
24
29
|
default: undefined
|
@@ -32,6 +37,8 @@ defineProps({
|
|
32
37
|
default: "text-sm"
|
33
38
|
},
|
34
39
|
showName: Boolean,
|
35
|
-
toggleIndeterminate: Boolean
|
40
|
+
toggleIndeterminate: Boolean,
|
41
|
+
disable: Boolean,
|
42
|
+
readonly: Boolean
|
36
43
|
});
|
37
44
|
</script>
|
@@ -21,7 +21,7 @@
|
|
21
21
|
<a
|
22
22
|
v-if="uploadedFiles.length > 0"
|
23
23
|
class="ml-3 text-red-900"
|
24
|
-
@click="
|
24
|
+
@click="clearUploadedFiles"
|
25
25
|
>Clear</a>
|
26
26
|
<input
|
27
27
|
ref="file"
|
@@ -81,7 +81,7 @@ const props = defineProps({
|
|
81
81
|
readonly: Boolean
|
82
82
|
});
|
83
83
|
|
84
|
-
const { onComplete, onDrop, onFilesSelected, uploadedFiles,
|
84
|
+
const { onComplete, onDrop, onFilesSelected, uploadedFiles, clearUploadedFiles, onRemove } = useMultiFileUpload();
|
85
85
|
onMounted(() => {
|
86
86
|
if (props.modelValue) {
|
87
87
|
uploadedFiles.value = props.modelValue;
|
@@ -234,7 +234,7 @@ function resolveSelectionLabel(option) {
|
|
234
234
|
* @returns {string|*|string}
|
235
235
|
*/
|
236
236
|
function resolveValue(option) {
|
237
|
-
if (typeof option === "string") {
|
237
|
+
if (!option || typeof option === "string") {
|
238
238
|
return option;
|
239
239
|
}
|
240
240
|
let value = option.value;
|
@@ -21,7 +21,7 @@
|
|
21
21
|
<a
|
22
22
|
v-if="uploadedFile"
|
23
23
|
class="ml-3 text-red-900"
|
24
|
-
@click="
|
24
|
+
@click="clearUploadedFile"
|
25
25
|
>Clear</a>
|
26
26
|
<input
|
27
27
|
ref="file"
|
@@ -67,7 +67,7 @@ const props = defineProps({
|
|
67
67
|
disable: Boolean,
|
68
68
|
readonly: Boolean
|
69
69
|
});
|
70
|
-
const { onComplete, onDrop, onFileSelected, uploadedFile,
|
70
|
+
const { onComplete, onDrop, onFileSelected, uploadedFile, clearUploadedFile } = useSingleFileUpload();
|
71
71
|
onComplete(() => emit("update:model-value", uploadedFile.value));
|
72
72
|
|
73
73
|
onMounted(() => {
|
@@ -1,5 +1,46 @@
|
|
1
1
|
<template>
|
2
2
|
<div class="rendered-form">
|
3
|
+
<div v-if="form.variations > 1" class="mb-4">
|
4
|
+
<QTabs v-model="currentVariation" class="text-xs">
|
5
|
+
<QTab
|
6
|
+
v-for="(name, index) in variationNames"
|
7
|
+
:key="name"
|
8
|
+
:name="name"
|
9
|
+
class="p-0"
|
10
|
+
>
|
11
|
+
<div class="flex flex-nowrap items-center text-sm">
|
12
|
+
<div>{{ name || "(Default)" }}</div>
|
13
|
+
<template v-if="!disable && !readonly">
|
14
|
+
<a
|
15
|
+
@click="() => (variationToEdit = name) && (newVariationName = name)"
|
16
|
+
class="ml-1 p-1 hover:opacity-100 opacity-20 hover:bg-blue-200 rounded"
|
17
|
+
>
|
18
|
+
<EditIcon class="w-3 text-blue-900" />
|
19
|
+
</a>
|
20
|
+
<a
|
21
|
+
v-if="index > 0"
|
22
|
+
@click="variationToDelete = name"
|
23
|
+
class="ml-1 p-1 hover:opacity-100 opacity-20 hover:bg-red-200 rounded"
|
24
|
+
>
|
25
|
+
<RemoveIcon class="w-3 text-red-900" />
|
26
|
+
</a>
|
27
|
+
</template>
|
28
|
+
</div>
|
29
|
+
</QTab>
|
30
|
+
<QTab
|
31
|
+
v-if="canAddVariation"
|
32
|
+
name="add"
|
33
|
+
key="add-new-variation"
|
34
|
+
@click="onAddVariation"
|
35
|
+
class="bg-blue-600 rounded-t-lg !text-white"
|
36
|
+
>
|
37
|
+
<template v-if="saving">
|
38
|
+
<QSpinnerBall class="w-4" />
|
39
|
+
</template>
|
40
|
+
<template v-else>+ Add Variation</template>
|
41
|
+
</QTab>
|
42
|
+
</QTabs>
|
43
|
+
</div>
|
3
44
|
<div
|
4
45
|
v-for="(field, index) in mappedFields"
|
5
46
|
:key="field.id"
|
@@ -7,7 +48,8 @@
|
|
7
48
|
>
|
8
49
|
<Component
|
9
50
|
:is="field.component"
|
10
|
-
|
51
|
+
:key="field.name + '-' + currentVariation"
|
52
|
+
:model-value="getFieldValue(field.name)"
|
11
53
|
:field="field"
|
12
54
|
:label="field.label || undefined"
|
13
55
|
:no-label="noLabel"
|
@@ -17,10 +59,36 @@
|
|
17
59
|
@update:model-value="onInput(field.name, $event)"
|
18
60
|
/>
|
19
61
|
</div>
|
62
|
+
<ConfirmDialog
|
63
|
+
v-if="variationToEdit !== false"
|
64
|
+
title="Change variation name"
|
65
|
+
@confirm="onChangeVariationName"
|
66
|
+
@close="variationToEdit = false"
|
67
|
+
>
|
68
|
+
<TextField
|
69
|
+
v-model="newVariationName"
|
70
|
+
label="Enter name"
|
71
|
+
placeholder="Variation Name"
|
72
|
+
input-class="bg-white"
|
73
|
+
/>
|
74
|
+
</ConfirmDialog>
|
75
|
+
<ConfirmDialog
|
76
|
+
v-if="variationToDelete"
|
77
|
+
:title="`Remove variation ${variationToDelete}?`"
|
78
|
+
content="You cannot undo this action. If there was any analytics collected for this variation, it will still be attributed to the ad."
|
79
|
+
confirm-class="bg-red-900 text-white"
|
80
|
+
content-class="w-96"
|
81
|
+
@confirm="onRemoveVariation(variationToDelete)"
|
82
|
+
@close="variationToDelete = ''"
|
83
|
+
/>
|
20
84
|
</div>
|
21
85
|
</template>
|
22
86
|
<script setup>
|
23
|
-
import {
|
87
|
+
import { PencilIcon as EditIcon } from "@heroicons/vue/solid";
|
88
|
+
import { computed, ref } from "vue";
|
89
|
+
import { FlashMessages, incrementName, replace } from "../../../helpers";
|
90
|
+
import { TrashIcon as RemoveIcon } from "../../../svg";
|
91
|
+
import { ConfirmDialog } from "../../Utility";
|
24
92
|
import {
|
25
93
|
BooleanField,
|
26
94
|
DateField,
|
@@ -36,17 +104,18 @@ import {
|
|
36
104
|
const emit = defineEmits(["update:values"]);
|
37
105
|
const props = defineProps({
|
38
106
|
values: {
|
39
|
-
type:
|
107
|
+
type: Array,
|
40
108
|
default: null
|
41
109
|
},
|
42
|
-
|
43
|
-
type:
|
110
|
+
form: {
|
111
|
+
type: Object,
|
44
112
|
required: true
|
45
113
|
},
|
46
114
|
noLabel: Boolean,
|
47
115
|
showName: Boolean,
|
48
116
|
disable: Boolean,
|
49
|
-
readonly: Boolean
|
117
|
+
readonly: Boolean,
|
118
|
+
saving: Boolean
|
50
119
|
});
|
51
120
|
|
52
121
|
const FORM_FIELD_MAP = {
|
@@ -61,16 +130,85 @@ const FORM_FIELD_MAP = {
|
|
61
130
|
WYSIWYG: WysiwygField
|
62
131
|
};
|
63
132
|
|
64
|
-
const mappedFields = props.fields.map((field) => ({
|
133
|
+
const mappedFields = props.form.fields.map((field) => ({
|
65
134
|
placeholder: `Enter ${field.label}`,
|
66
135
|
...field,
|
67
136
|
component: FORM_FIELD_MAP[field.type],
|
68
137
|
default: field.type === "BOOLEAN" ? false : ""
|
69
138
|
}));
|
70
139
|
|
71
|
-
const
|
140
|
+
const variationNames = computed(() => {
|
141
|
+
return [...new Set(props.values.map(v => v.variation))].sort();
|
142
|
+
});
|
143
|
+
|
144
|
+
const currentVariation = ref(variationNames.value[0] || "");
|
145
|
+
const newVariationName = ref("");
|
146
|
+
const variationToEdit = ref(false);
|
147
|
+
const variationToDelete = ref("");
|
148
|
+
const canAddVariation = computed(() => variationNames.value.length < props.form.variations && !props.readonly && !props.disable);
|
149
|
+
|
150
|
+
function getFieldResponse(name) {
|
151
|
+
if (!props.values) return undefined;
|
152
|
+
return props.values.find((v) => v.variation === currentVariation.value && v.name === name);
|
153
|
+
}
|
154
|
+
function getFieldValue(name) {
|
155
|
+
return getFieldResponse(name)?.value;
|
156
|
+
}
|
157
|
+
function onInput(name, value) {
|
158
|
+
const fieldResponse = getFieldResponse(name);
|
159
|
+
const newFieldResponse = {
|
160
|
+
name,
|
161
|
+
variation: currentVariation.value || "",
|
162
|
+
value
|
163
|
+
};
|
164
|
+
const newValues = replace(props.values, fieldResponse, newFieldResponse, true);
|
165
|
+
emit("update:values", newValues);
|
166
|
+
}
|
167
|
+
|
168
|
+
function onAddVariation() {
|
169
|
+
if (props.saving) return;
|
170
|
+
|
171
|
+
const previousName = variationNames.value[variationNames.value.length - 1];
|
172
|
+
const newName = incrementName(!previousName ? "Variation 1" : previousName);
|
173
|
+
|
174
|
+
const newVariation = props.form.fields.map((field) => ({
|
175
|
+
variation: newName,
|
176
|
+
name: field.name,
|
177
|
+
value: field.type === "BOOLEAN" ? false : null
|
178
|
+
}));
|
179
|
+
const newValues = [...props.values, ...newVariation];
|
180
|
+
emit("update:values", newValues);
|
181
|
+
currentVariation.value = newName;
|
182
|
+
}
|
183
|
+
|
184
|
+
function onChangeVariationName() {
|
185
|
+
if (!newVariationName.value) return;
|
186
|
+
if (variationNames.value.includes(newVariationName.value)) {
|
187
|
+
FlashMessages.error("Variation name already exists");
|
188
|
+
return;
|
189
|
+
}
|
190
|
+
const newValues = props.values.map((v) => {
|
191
|
+
if (v.variation === variationToEdit.value) {
|
192
|
+
return { ...v, variation: newVariationName.value };
|
193
|
+
}
|
194
|
+
return v;
|
195
|
+
});
|
196
|
+
emit("update:values", newValues);
|
197
|
+
|
198
|
+
currentVariation.value = newVariationName.value;
|
199
|
+
variationToEdit.value = false;
|
200
|
+
newVariationName.value = "";
|
201
|
+
}
|
202
|
+
|
203
|
+
function onRemoveVariation(name) {
|
204
|
+
if (!name) return;
|
205
|
+
|
206
|
+
const newValues = props.values.filter((v) => v.variation !== name);
|
207
|
+
emit("update:values", newValues);
|
72
208
|
|
73
|
-
|
74
|
-
|
209
|
+
if (currentVariation.value === name) {
|
210
|
+
currentVariation.value = variationNames.value[0];
|
211
|
+
}
|
212
|
+
variationToDelete.value = "";
|
75
213
|
}
|
76
214
|
</script>
|
@@ -1,8 +1,8 @@
|
|
1
|
-
import { computed, ref, watch } from "vue";
|
2
|
-
import { getItem, setItem, waitForRef } from "../../helpers";
|
1
|
+
import { computed, Ref, ref, ShallowRef, shallowRef, watch } from "vue";
|
2
|
+
import { ActionTargetItem, getItem, setItem, waitForRef } from "../../helpers";
|
3
3
|
import { getFilterFromUrl } from "./listHelpers";
|
4
4
|
|
5
|
-
interface ListActionsOptions {
|
5
|
+
export interface ListActionsOptions {
|
6
6
|
listRoute: Function;
|
7
7
|
summaryRoute?: Function | null;
|
8
8
|
filterFieldOptionsRoute?: Function | null;
|
@@ -13,6 +13,14 @@ interface ListActionsOptions {
|
|
13
13
|
refreshFilters?: boolean;
|
14
14
|
}
|
15
15
|
|
16
|
+
export interface PagedItems {
|
17
|
+
data: any[] | undefined;
|
18
|
+
meta: {
|
19
|
+
total: number;
|
20
|
+
last_page?: number;
|
21
|
+
} | undefined;
|
22
|
+
}
|
23
|
+
|
16
24
|
export function useListControls(name: string, {
|
17
25
|
listRoute,
|
18
26
|
summaryRoute = null,
|
@@ -25,14 +33,19 @@ export function useListControls(name: string, {
|
|
25
33
|
}: ListActionsOptions) {
|
26
34
|
let isInitialized = false;
|
27
35
|
const PAGE_SETTINGS_KEY = `${name}-pagination-settings`;
|
28
|
-
const pagedItems =
|
29
|
-
const filter = ref({});
|
36
|
+
const pagedItems: Ref<PagedItems | null> = shallowRef(null);
|
37
|
+
const filter: Ref<object | any> = ref({});
|
30
38
|
const globalFilter = ref({});
|
31
39
|
const showFilters = ref(false);
|
32
|
-
const selectedRows =
|
40
|
+
const selectedRows = shallowRef([]);
|
33
41
|
const isLoadingList = ref(false);
|
34
42
|
const isLoadingSummary = ref(false);
|
35
|
-
const summary =
|
43
|
+
const summary = shallowRef(null);
|
44
|
+
|
45
|
+
// The active ad for viewing / editing
|
46
|
+
const activeItem: ShallowRef<ActionTargetItem | null> = shallowRef(null);
|
47
|
+
// Controls the active panel (ie: tab) if rendering a panels drawer or similar
|
48
|
+
const activePanel = shallowRef(null);
|
36
49
|
|
37
50
|
// Filter fields are the field values available for the currently applied filter on Creative Groups
|
38
51
|
// (ie: all states available under the current filter)
|
@@ -86,7 +99,7 @@ export function useListControls(name: string, {
|
|
86
99
|
isLoadingSummary.value = true;
|
87
100
|
const summaryFilter = { id: null, ...filter.value, ...globalFilter.value };
|
88
101
|
if (selectedRows.value.length) {
|
89
|
-
summaryFilter.id = selectedRows.value.map((row) => row.id);
|
102
|
+
summaryFilter.id = selectedRows.value.map((row: { id: string }) => row.id);
|
90
103
|
}
|
91
104
|
summary.value = await summaryRoute(summaryFilter);
|
92
105
|
isLoadingSummary.value = false;
|
@@ -108,7 +121,7 @@ export function useListControls(name: string, {
|
|
108
121
|
* Watches for a filter URL parameter and applies the filter if it is set.
|
109
122
|
*/
|
110
123
|
function applyFilterFromUrl(url: string, filterFields = null) {
|
111
|
-
if (url.match(urlPattern)) {
|
124
|
+
if (urlPattern && url.match(urlPattern)) {
|
112
125
|
// A flat list of valid filterable field names
|
113
126
|
const validFilterKeys = filterFields?.value?.map(group => group.fields.map(field => field.name)).flat();
|
114
127
|
|
@@ -125,15 +138,32 @@ export function useListControls(name: string, {
|
|
125
138
|
|
126
139
|
// Set the reactive pager to map from the Laravel pagination to Quasar pagination
|
127
140
|
// and automatically update the list of ads
|
128
|
-
function setPagedItems(items) {
|
141
|
+
function setPagedItems(items: any[] | PagedItems) {
|
142
|
+
let data, meta;
|
143
|
+
|
129
144
|
if (Array.isArray(items)) {
|
130
|
-
|
145
|
+
data = items;
|
146
|
+
meta = { total: items.length };
|
147
|
+
|
131
148
|
} else {
|
132
|
-
|
133
|
-
|
134
|
-
quasarPagination.value.rowsNumber = items.meta.total;
|
135
|
-
}
|
149
|
+
data = items.data;
|
150
|
+
meta = items.meta;
|
136
151
|
}
|
152
|
+
|
153
|
+
// Update the Quasar pagination rows number if it is different from the total
|
154
|
+
if (meta && meta.total !== quasarPagination.value.rowsNumber) {
|
155
|
+
quasarPagination.value.rowsNumber = meta.total;
|
156
|
+
}
|
157
|
+
|
158
|
+
// Add a reactive isSaving property to each item (for performance reasons in checking saving state)
|
159
|
+
data = data.map((item: any) => {
|
160
|
+
// We want to keep the isSaving state if it is already set, as optimizations prevent reloading the
|
161
|
+
// components, and therefore reactivity is not responding to the new isSaving state
|
162
|
+
const oldItem = pagedItems.value?.data?.find(i => i.id === item.id);
|
163
|
+
return { ...item, isSaving: oldItem?.isSaving || ref(false) };
|
164
|
+
});
|
165
|
+
|
166
|
+
pagedItems.value = { data, meta };
|
137
167
|
}
|
138
168
|
|
139
169
|
/**
|
@@ -148,9 +178,12 @@ export function useListControls(name: string, {
|
|
148
178
|
*
|
149
179
|
* @param updatedItem
|
150
180
|
*/
|
151
|
-
function
|
181
|
+
function setItemInList(updatedItem: any) {
|
152
182
|
const data = pagedItems.value?.data?.map(item => (item.id === updatedItem.id && (item.updated_at === null || item.updated_at <= updatedItem.updated_at)) ? updatedItem : item);
|
153
|
-
|
183
|
+
setPagedItems({
|
184
|
+
data,
|
185
|
+
meta: { total: pagedItems.value.meta.total }
|
186
|
+
});
|
154
187
|
|
155
188
|
// Update the active item as well if it is set
|
156
189
|
if (activeItem.value?.id === updatedItem.id) {
|
@@ -160,10 +193,10 @@ export function useListControls(name: string, {
|
|
160
193
|
|
161
194
|
/**
|
162
195
|
* Loads more items into the list.
|
163
|
-
* @param index
|
164
|
-
* @param perPage
|
165
196
|
*/
|
166
|
-
async function loadMore(index, perPage = undefined) {
|
197
|
+
async function loadMore(index: number, perPage = undefined) {
|
198
|
+
if (!moreRoute) return;
|
199
|
+
|
167
200
|
const newItems = await moreRoute({
|
168
201
|
page: index + 1,
|
169
202
|
perPage,
|
@@ -171,7 +204,10 @@ export function useListControls(name: string, {
|
|
171
204
|
});
|
172
205
|
|
173
206
|
if (newItems && newItems.length > 0) {
|
174
|
-
|
207
|
+
setPagedItems({
|
208
|
+
data: [...pagedItems.value.data, ...newItems],
|
209
|
+
meta: { total: pagedItems.value.meta.total }
|
210
|
+
});
|
175
211
|
return true;
|
176
212
|
}
|
177
213
|
|
@@ -180,7 +216,6 @@ export function useListControls(name: string, {
|
|
180
216
|
|
181
217
|
/**
|
182
218
|
* Refreshes the list, summary, and filter field options.
|
183
|
-
* @returns {Promise<Awaited<void>[]>}
|
184
219
|
*/
|
185
220
|
async function refreshAll() {
|
186
221
|
return Promise.all([loadList(), loadSummary(), loadFilterFieldOptions(), getActiveItemDetails()]);
|
@@ -188,10 +223,8 @@ export function useListControls(name: string, {
|
|
188
223
|
|
189
224
|
/**
|
190
225
|
* Updates the settings in local storage
|
191
|
-
* @param key
|
192
|
-
* @param value
|
193
226
|
*/
|
194
|
-
function updateSettings(key, value) {
|
227
|
+
function updateSettings(key: string, value: any) {
|
195
228
|
const settings = getItem(PAGE_SETTINGS_KEY) || {};
|
196
229
|
settings[key] = value;
|
197
230
|
setItem(PAGE_SETTINGS_KEY, settings);
|
@@ -242,11 +275,6 @@ export function useListControls(name: string, {
|
|
242
275
|
setItem(PAGE_SETTINGS_KEY, settings);
|
243
276
|
}
|
244
277
|
|
245
|
-
// The active ad for viewing / editing
|
246
|
-
const activeItem = ref(null);
|
247
|
-
// Controls the active panel (ie: tab) if rendering a panels drawer or similar
|
248
|
-
const activePanel = ref(null);
|
249
|
-
|
250
278
|
/**
|
251
279
|
* Gets the additional details for the currently active item.
|
252
280
|
* (ie: data that is not normally loaded in the list because it is not needed for the list view)
|
@@ -261,7 +289,8 @@ export function useListControls(name: string, {
|
|
261
289
|
// NOTE: race conditions might allow the finished loading item to be different to the currently
|
262
290
|
// requested item
|
263
291
|
if (result?.id === activeItem.value?.id) {
|
264
|
-
|
292
|
+
const loadedItem = pagedItems.value?.data.find((i: { id: string }) => i.id === result.id);
|
293
|
+
activeItem.value = { ...result, isSaving: loadedItem.isSaving || ref(false) };
|
265
294
|
}
|
266
295
|
}
|
267
296
|
|
@@ -289,12 +318,13 @@ export function useListControls(name: string, {
|
|
289
318
|
/**
|
290
319
|
* Gets the next item in the list at the given offset (ie: 1 or -1) from the current position in the list of the
|
291
320
|
* selected item. If the next item is on a previous or next page, it will load the page first then select the item
|
292
|
-
* @param offset
|
293
|
-
* @returns {Promise<void>}
|
294
321
|
*/
|
295
|
-
async function getNextItem(offset) {
|
296
|
-
|
297
|
-
|
322
|
+
async function getNextItem(offset: number) {
|
323
|
+
if (!pagedItems.value) return;
|
324
|
+
|
325
|
+
const index = pagedItems.value.data.findIndex((i: { id: string }) => i.id === activeItem.value?.id);
|
326
|
+
if (index === undefined || index === null) return;
|
327
|
+
|
298
328
|
let nextIndex = index + offset;
|
299
329
|
|
300
330
|
// Load the previous page if the offset is before index 0
|
@@ -321,7 +351,7 @@ export function useListControls(name: string, {
|
|
321
351
|
}
|
322
352
|
}
|
323
353
|
|
324
|
-
activeItem.value = pagedItems.value
|
354
|
+
activeItem.value = pagedItems.value?.data[nextIndex];
|
325
355
|
}
|
326
356
|
|
327
357
|
// Initialize the list actions and load settings, lists, summaries, filter fields, etc.
|
@@ -358,6 +388,6 @@ export function useListControls(name: string, {
|
|
358
388
|
getNextItem,
|
359
389
|
activatePanel,
|
360
390
|
applyFilterFromUrl,
|
361
|
-
|
391
|
+
setItemInList
|
362
392
|
};
|
363
393
|
}
|
@@ -9,9 +9,9 @@ export interface TableColumn {
|
|
9
9
|
field: string,
|
10
10
|
format?: Function,
|
11
11
|
innerClass?: string | object,
|
12
|
-
style?: string,
|
13
|
-
headerStyle?: string,
|
14
|
-
|
12
|
+
style?: string | object,
|
13
|
+
headerStyle?: string | object,
|
14
|
+
isSavingRow?: boolean | Function,
|
15
15
|
label: string,
|
16
16
|
maxWidth?: number,
|
17
17
|
minWidth?: number,
|
@@ -1,5 +1,21 @@
|
|
1
1
|
<script>
|
2
|
-
|
3
|
-
|
2
|
+
import { isRef, isVNode } from "vue";
|
3
|
+
|
4
|
+
const RenderVnode = (props) => {
|
5
|
+
if (isVNode(props.vnode)) {
|
6
|
+
return props.vnode;
|
7
|
+
}
|
8
|
+
|
9
|
+
if (isRef(props.vnode)) {
|
10
|
+
return props.vnode.value;
|
11
|
+
}
|
12
|
+
|
13
|
+
if (typeof props.vnode === "function") {
|
14
|
+
return props.vnode();
|
15
|
+
}
|
16
|
+
|
17
|
+
return null;
|
18
|
+
};
|
19
|
+
RenderVnode.props = { vnode: { type: [Function, Object], required: true } };
|
4
20
|
export default RenderVnode;
|
5
21
|
</script>
|
package/src/config/index.ts
CHANGED
@@ -1,13 +1,8 @@
|
|
1
1
|
import { QNotifyCreateOptions } from "quasar";
|
2
|
-
|
3
|
-
export interface DanxFileUploadOptions {
|
4
|
-
directory?: string;
|
5
|
-
presignedUploadUrl?: Function | null;
|
6
|
-
uploadCompletedUrl?: Function | null;
|
7
|
-
}
|
2
|
+
import { FileUploadOptions } from "../helpers";
|
8
3
|
|
9
4
|
export interface DanxOptions {
|
10
|
-
fileUpload:
|
5
|
+
fileUpload: FileUploadOptions;
|
11
6
|
flashMessages: {
|
12
7
|
default: QNotifyCreateOptions;
|
13
8
|
success: QNotifyCreateOptions;
|