quasar-ui-danx 0.3.24 → 0.3.26
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/dist/danx.es.js +89 -84
- package/dist/danx.es.js.map +1 -1
- package/dist/danx.umd.js +1 -1
- package/dist/danx.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/components/ActionTable/Filters/FilterableField.vue +29 -27
- package/src/components/ActionTable/Form/RenderedForm.vue +108 -106
- package/src/components/ActionTable/tableColumns.ts +65 -64
package/package.json
CHANGED
@@ -5,6 +5,7 @@
|
|
5
5
|
v-if="field.options?.length > 0 || loading"
|
6
6
|
:model-value="modelValue"
|
7
7
|
:options="field.options"
|
8
|
+
:clearable="field.clearable === undefined ? true : field.clearable"
|
8
9
|
multiple
|
9
10
|
:loading="loading"
|
10
11
|
:chip-limit="1"
|
@@ -30,6 +31,7 @@
|
|
30
31
|
v-else-if="field.type === 'single-select'"
|
31
32
|
:model-value="modelValue"
|
32
33
|
:options="field.options"
|
34
|
+
:clearable="field.clearable === undefined ? true : field.clearable"
|
33
35
|
:placeholder="field.placeholder"
|
34
36
|
:loading="loading"
|
35
37
|
:label="field.label"
|
@@ -108,41 +110,41 @@
|
|
108
110
|
</template>
|
109
111
|
<script setup>
|
110
112
|
import {
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
113
|
+
BooleanField,
|
114
|
+
DateField,
|
115
|
+
DateRangeField,
|
116
|
+
MultiKeywordField,
|
117
|
+
NumberRangeField,
|
118
|
+
SelectField,
|
119
|
+
SelectWithChildrenField
|
118
120
|
} from "../Form/Fields";
|
119
121
|
|
120
122
|
const emit = defineEmits(["update:model-value"]);
|
121
123
|
const props = defineProps({
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
124
|
+
field: {
|
125
|
+
type: Object,
|
126
|
+
required: true
|
127
|
+
},
|
128
|
+
modelValue: {
|
129
|
+
type: [String, Array, Number, Object, Boolean],
|
130
|
+
default: undefined
|
131
|
+
},
|
132
|
+
loading: Boolean
|
131
133
|
});
|
132
134
|
|
133
135
|
function onUpdate(val) {
|
134
|
-
|
136
|
+
let newVal = val || undefined;
|
135
137
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
138
|
+
switch (props.field.type) {
|
139
|
+
case "multi-select":
|
140
|
+
newVal = (val && val.length > 0) ? val : undefined;
|
141
|
+
break;
|
142
|
+
case "single-select":
|
143
|
+
case "boolean":
|
144
|
+
newVal = val === null ? undefined : val;
|
145
|
+
break;
|
146
|
+
}
|
145
147
|
|
146
|
-
|
148
|
+
emit("update:model-value", newVal);
|
147
149
|
}
|
148
150
|
</script>
|
@@ -75,6 +75,7 @@
|
|
75
75
|
:label="field.label || undefined"
|
76
76
|
:no-label="noLabel"
|
77
77
|
:show-name="showName"
|
78
|
+
:clearable="field.clearable || clearable"
|
78
79
|
:disable="disable"
|
79
80
|
:readonly="readonly"
|
80
81
|
@update:model-value="onInput(field.name, $event)"
|
@@ -111,65 +112,66 @@ import { FlashMessages, incrementName, replace } from "../../../helpers";
|
|
111
112
|
import { TrashIcon as RemoveIcon } from "../../../svg";
|
112
113
|
import { ConfirmDialog } from "../../Utility";
|
113
114
|
import {
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
115
|
+
BooleanField,
|
116
|
+
DateField,
|
117
|
+
DateRangeField,
|
118
|
+
IntegerField,
|
119
|
+
MultiFileField,
|
120
|
+
NumberField,
|
121
|
+
SingleFileField,
|
122
|
+
TextField,
|
123
|
+
WysiwygField
|
123
124
|
} from "./Fields";
|
124
125
|
|
125
126
|
const emit = defineEmits(["update:values"]);
|
126
127
|
const props = defineProps({
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
128
|
+
values: {
|
129
|
+
type: Array,
|
130
|
+
default: null
|
131
|
+
},
|
132
|
+
form: {
|
133
|
+
type: Object,
|
134
|
+
required: true
|
135
|
+
},
|
136
|
+
noLabel: Boolean,
|
137
|
+
showName: Boolean,
|
138
|
+
disable: Boolean,
|
139
|
+
clearable: Boolean,
|
140
|
+
readonly: Boolean,
|
141
|
+
saving: Boolean,
|
142
|
+
emptyValue: {
|
143
|
+
type: [String, Number, Boolean],
|
144
|
+
default: undefined
|
145
|
+
},
|
146
|
+
canModifyVariations: Boolean
|
145
147
|
});
|
146
148
|
|
147
149
|
const FORM_FIELD_MAP = {
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
150
|
+
BOOLEAN: BooleanField,
|
151
|
+
DATE: DateField,
|
152
|
+
DATE_RANGE: DateRangeField,
|
153
|
+
INTEGER: IntegerField,
|
154
|
+
NUMBER: NumberField,
|
155
|
+
TEXT: TextField,
|
156
|
+
SINGLE_FILE: SingleFileField,
|
157
|
+
MULTI_FILE: MultiFileField,
|
158
|
+
WYSIWYG: WysiwygField
|
157
159
|
};
|
158
160
|
|
159
161
|
const mappedFields = props.form.fields.map((field) => ({
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
162
|
+
placeholder: `Enter ${field.label}`,
|
163
|
+
...field,
|
164
|
+
component: FORM_FIELD_MAP[field.type],
|
165
|
+
default: field.type === "BOOLEAN" ? false : ""
|
164
166
|
}));
|
165
167
|
|
166
168
|
const variationNames = computed(() => {
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
169
|
+
const names = [...new Set(props.values.map(v => v.variation))].sort();
|
170
|
+
// Always guarantee that we show the default variation
|
171
|
+
if (names.length === 0) {
|
172
|
+
names.push("");
|
173
|
+
}
|
174
|
+
return names;
|
173
175
|
});
|
174
176
|
|
175
177
|
const currentVariation = ref(variationNames.value[0] || "");
|
@@ -179,88 +181,88 @@ const variationToDelete = ref("");
|
|
179
181
|
const canAddVariation = computed(() => props.canModifyVariations && !props.readonly && !props.disable && variationNames.value.length < props.form.variations);
|
180
182
|
|
181
183
|
function getFieldResponse(name, variation) {
|
182
|
-
|
183
|
-
|
184
|
+
if (!props.values) return undefined;
|
185
|
+
return props.values.find((v) => v.variation === (variation !== undefined ? variation : currentVariation.value) && v.name === name);
|
184
186
|
}
|
185
187
|
function getFieldValue(name) {
|
186
|
-
|
188
|
+
return getFieldResponse(name)?.value;
|
187
189
|
}
|
188
190
|
function onInput(name, value) {
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
191
|
+
const fieldResponse = getFieldResponse(name);
|
192
|
+
const newFieldResponse = {
|
193
|
+
name,
|
194
|
+
variation: currentVariation.value || "",
|
195
|
+
value: value === undefined ? props.emptyValue : value
|
196
|
+
};
|
197
|
+
const newValues = replace(props.values, fieldResponse, newFieldResponse, true);
|
198
|
+
emit("update:values", newValues);
|
197
199
|
}
|
198
200
|
|
199
201
|
function createVariation(variation) {
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
202
|
+
return props.form.fields.map((field) => ({
|
203
|
+
variation,
|
204
|
+
name: field.name,
|
205
|
+
value: field.type === "BOOLEAN" ? false : null
|
206
|
+
}));
|
205
207
|
}
|
206
208
|
|
207
209
|
function onAddVariation() {
|
208
|
-
|
209
|
-
|
210
|
+
if (props.saving) return;
|
211
|
+
let newValues = [...props.values];
|
210
212
|
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
213
|
+
if (newValues.length === 0) {
|
214
|
+
newValues = createVariation("");
|
215
|
+
}
|
216
|
+
const previousName = variationNames.value[variationNames.value.length - 1];
|
217
|
+
const newName = incrementName(!previousName ? "1" : previousName);
|
218
|
+
const newVariation = createVariation(newName);
|
219
|
+
emit("update:values", [...newValues, ...newVariation]);
|
220
|
+
currentVariation.value = newName;
|
219
221
|
}
|
220
222
|
|
221
223
|
function onChangeVariationName() {
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
224
|
+
if (!newVariationName.value) return;
|
225
|
+
if (variationNames.value.includes(newVariationName.value)) {
|
226
|
+
FlashMessages.error("Variation name already exists");
|
227
|
+
return;
|
228
|
+
}
|
229
|
+
const newValues = props.values.map((v) => {
|
230
|
+
if (v.variation === variationToEdit.value) {
|
231
|
+
return { ...v, variation: newVariationName.value };
|
232
|
+
}
|
233
|
+
return v;
|
234
|
+
});
|
235
|
+
emit("update:values", newValues);
|
234
236
|
|
235
|
-
|
236
|
-
|
237
|
-
|
237
|
+
currentVariation.value = newVariationName.value;
|
238
|
+
variationToEdit.value = false;
|
239
|
+
newVariationName.value = "";
|
238
240
|
}
|
239
241
|
|
240
242
|
function onRemoveVariation(name) {
|
241
|
-
|
243
|
+
if (!name) return;
|
242
244
|
|
243
|
-
|
244
|
-
|
245
|
+
const newValues = props.values.filter((v) => v.variation !== name);
|
246
|
+
emit("update:values", newValues);
|
245
247
|
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
248
|
+
if (currentVariation.value === name) {
|
249
|
+
currentVariation.value = variationNames.value[0];
|
250
|
+
}
|
251
|
+
variationToDelete.value = "";
|
250
252
|
}
|
251
253
|
|
252
254
|
function isVariationFormComplete(variation) {
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
255
|
+
const requiredGroups = {};
|
256
|
+
return props.form.fields.filter(r => r.required || r.required_group).every((field) => {
|
257
|
+
const fieldResponse = getFieldResponse(field.name, variation);
|
258
|
+
const hasValue = !!fieldResponse && fieldResponse.value !== null;
|
259
|
+
if (field.required_group) {
|
260
|
+
// This required group has already been satisfied
|
261
|
+
if (requiredGroups[field.required_group]) return true;
|
262
|
+
return requiredGroups[field.required_group] = hasValue;
|
263
|
+
} else {
|
264
|
+
return hasValue;
|
265
|
+
}
|
266
|
+
});
|
265
267
|
}
|
266
268
|
</script>
|
@@ -2,81 +2,82 @@ import { computed, ref, watch } from "vue";
|
|
2
2
|
import { getItem, setItem } from "../../helpers";
|
3
3
|
|
4
4
|
export interface TableColumn {
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
5
|
+
actionMenu?: object,
|
6
|
+
align?: string,
|
7
|
+
category?: string,
|
8
|
+
class?: string | object,
|
9
|
+
field: string,
|
10
|
+
format?: Function,
|
11
|
+
innerClass?: string | object,
|
12
|
+
style?: string | object,
|
13
|
+
headerStyle?: string | object,
|
14
|
+
isSavingRow?: boolean | Function,
|
15
|
+
label: string,
|
16
|
+
maxWidth?: number,
|
17
|
+
minWidth?: number,
|
18
|
+
name: string,
|
19
|
+
onClick?: Function,
|
20
|
+
required?: boolean,
|
21
|
+
resizeable?: boolean,
|
22
|
+
sortable?: boolean,
|
23
|
+
sortBy?: string,
|
24
|
+
sortByExpression?: string,
|
25
|
+
titleColumns?: Function,
|
26
|
+
vnode?: Function,
|
27
27
|
}
|
28
28
|
|
29
29
|
export function useTableColumns(name: string, columns: TableColumn[]) {
|
30
|
-
|
31
|
-
|
32
|
-
|
30
|
+
const COLUMN_ORDER_KEY = `${name}-column-order`;
|
31
|
+
const VISIBLE_COLUMNS_KEY = `${name}-visible-columns`;
|
32
|
+
const TITLE_COLUMNS_KEY = `${name}-title-columns`;
|
33
33
|
|
34
|
-
|
35
|
-
|
34
|
+
// The list that defines the order the columns should appear in
|
35
|
+
const columnOrder = ref(getItem(COLUMN_ORDER_KEY) || []);
|
36
36
|
|
37
|
-
|
38
|
-
|
37
|
+
// Manages visible columns on the table
|
38
|
+
const hiddenColumnNames = ref(getItem(VISIBLE_COLUMNS_KEY, []));
|
39
39
|
|
40
|
-
|
41
|
-
|
40
|
+
// Title columns will have their name appear on the first column of the table as part of the records' title
|
41
|
+
const titleColumnNames = ref(getItem(TITLE_COLUMNS_KEY, []));
|
42
42
|
|
43
|
-
|
44
|
-
|
43
|
+
// Columns that should be locked to the left side of the table
|
44
|
+
const lockedColumns = computed(() => orderedColumns.value.slice(0, 1));
|
45
45
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
46
|
+
// The resolved list of columns in the order they should appear in
|
47
|
+
const orderedColumns = computed(() => [...columns].sort((a, b) => {
|
48
|
+
const aIndex = columnOrder.value.indexOf(a.name);
|
49
|
+
const bIndex = columnOrder.value.indexOf(b.name);
|
50
|
+
return aIndex === -1 ? 1 : bIndex === -1 ? -1 : aIndex - bIndex;
|
51
|
+
}));
|
52
52
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
53
|
+
// The ordered list of columns. The ordering of this list is editable and will be stored in localStorage
|
54
|
+
const sortableColumns = computed({
|
55
|
+
get() {
|
56
|
+
return orderedColumns.value.slice(1);
|
57
|
+
},
|
58
|
+
set(newColumns) {
|
59
|
+
columnOrder.value = [...lockedColumns.value.map(c => c.name), ...newColumns.map(c => c.name)];
|
60
|
+
setItem(COLUMN_ORDER_KEY, columnOrder.value);
|
61
|
+
}
|
62
|
+
});
|
63
63
|
|
64
|
-
|
65
|
-
|
64
|
+
// The list of columns that are visible. To edit the visible columns, edit the hiddenColumnNames list
|
65
|
+
const visibleColumns = computed(() => orderedColumns.value.filter(c => !hiddenColumnNames.value.includes(c.name)));
|
66
66
|
|
67
|
-
|
68
|
-
|
67
|
+
// The list of columns that should be included in the title of a row
|
68
|
+
const orderedTitleColumns = computed(() => orderedColumns.value.filter(c => titleColumnNames.value.includes(c.name)));
|
69
69
|
|
70
|
-
|
71
|
-
|
72
|
-
|
70
|
+
// Save changes to the list of hidden columns in localStorage
|
71
|
+
watch(() => hiddenColumnNames.value, () => setItem(VISIBLE_COLUMNS_KEY, hiddenColumnNames.value));
|
72
|
+
watch(() => titleColumnNames.value, () => setItem(TITLE_COLUMNS_KEY, titleColumnNames.value));
|
73
73
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
74
|
+
return {
|
75
|
+
sortableColumns,
|
76
|
+
lockedColumns,
|
77
|
+
visibleColumns,
|
78
|
+
hiddenColumnNames,
|
79
|
+
columnOrder,
|
80
|
+
titleColumnNames,
|
81
|
+
orderedTitleColumns
|
82
|
+
};
|
82
83
|
}
|