quasar-ui-danx 0.4.1 → 0.4.2
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 +5246 -5265
- 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/ActionTable.vue +4 -3
- package/src/components/ActionTable/Filters/CollapsableFiltersSidebar.vue +10 -9
- package/src/components/ActionTable/Form/Fields/FileUploadButton.vue +34 -33
- package/src/components/ActionTable/Form/Fields/NumberField.vue +60 -59
- package/src/components/ActionTable/Form/Fields/SelectField.vue +135 -135
- package/src/components/ActionTable/Form/RenderedForm.vue +1 -5
- package/src/components/ActionTable/Layouts/ActionTableLayout.vue +8 -3
- package/src/components/ActionTable/Toolbars/ActionToolbar.vue +7 -7
- package/src/components/ActionTable/tableColumns.ts +66 -66
- package/src/components/Utility/Dialogs/ConfirmDialog.vue +69 -115
- package/src/components/Utility/Dialogs/DialogLayout.vue +95 -0
- package/src/components/Utility/Dialogs/InfoDialog.vue +40 -80
- package/src/components/Utility/Tools/RenderVnode.vue +21 -12
- package/src/helpers/actions.ts +6 -6
- package/src/styles/quasar-reset.scss +79 -68
- package/src/styles/themes/danx/dialogs.scss +43 -0
- package/src/styles/themes/danx/forms.scss +18 -0
- package/src/styles/themes/danx/index.scss +4 -0
@@ -16,7 +16,7 @@
|
|
16
16
|
option-value="value"
|
17
17
|
placeholder=""
|
18
18
|
:input-class="{'is-hidden': !isShowing, [inputClass]: true}"
|
19
|
-
class="max-w-full"
|
19
|
+
class="max-w-full dx-select-field"
|
20
20
|
@filter="onFilter"
|
21
21
|
@clear="onClear"
|
22
22
|
@popup-show="onShow"
|
@@ -71,40 +71,40 @@ import { computed, isRef, nextTick, ref } from "vue";
|
|
71
71
|
|
72
72
|
const emit = defineEmits(["update:model-value", "search", "update"]);
|
73
73
|
const props = defineProps({
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
74
|
+
...QSelect.props,
|
75
|
+
modelValue: {
|
76
|
+
type: [Array, String, Number, Object],
|
77
|
+
default: undefined
|
78
|
+
},
|
79
|
+
placeholder: {
|
80
|
+
type: String,
|
81
|
+
default: ""
|
82
|
+
},
|
83
|
+
selectionLabel: {
|
84
|
+
type: String,
|
85
|
+
default: null
|
86
|
+
},
|
87
|
+
chipLimit: {
|
88
|
+
type: Number,
|
89
|
+
default: 3
|
90
|
+
},
|
91
|
+
inputClass: {
|
92
|
+
type: String,
|
93
|
+
default: ""
|
94
|
+
},
|
95
|
+
selectionClass: {
|
96
|
+
type: String,
|
97
|
+
default: ""
|
98
|
+
},
|
99
|
+
options: {
|
100
|
+
type: Array,
|
101
|
+
default: () => []
|
102
|
+
},
|
103
|
+
filterable: Boolean,
|
104
|
+
filterFn: {
|
105
|
+
type: Function,
|
106
|
+
default: null
|
107
|
+
}
|
108
108
|
});
|
109
109
|
|
110
110
|
const selectField = ref(null);
|
@@ -119,27 +119,27 @@ const isShowing = ref(false);
|
|
119
119
|
* @type {ComputedRef<{selectionLabel: string, label: string, value: string|*}[]>}
|
120
120
|
*/
|
121
121
|
const computedOptions = computed(() => {
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
122
|
+
let options = props.options;
|
123
|
+
if (props.placeholder && !props.multiple && !props.filterable) {
|
124
|
+
options = [{ label: props.placeholder, value: null }, ...props.options];
|
125
|
+
}
|
126
|
+
options = options.map((o) => {
|
127
|
+
let opt = isRef(o) ? o.value : o;
|
128
|
+
return {
|
129
|
+
label: resolveLabel(opt),
|
130
|
+
value: resolveValue(opt),
|
131
|
+
selectionLabel: resolveSelectionLabel(opt)
|
132
|
+
};
|
133
|
+
});
|
134
|
+
return options;
|
135
135
|
});
|
136
136
|
|
137
137
|
const filteredOptions = computed(() => {
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
138
|
+
if (filter.value && !props.filterFn) {
|
139
|
+
return computedOptions.value.filter(o => o.label.toLocaleLowerCase().indexOf(filter.value.toLowerCase()) > -1);
|
140
|
+
} else {
|
141
|
+
return computedOptions.value;
|
142
|
+
}
|
143
143
|
});
|
144
144
|
|
145
145
|
/**
|
@@ -147,14 +147,14 @@ const filteredOptions = computed(() => {
|
|
147
147
|
* @type {ComputedRef<unknown>}
|
148
148
|
*/
|
149
149
|
const selectedValue = computed(() => {
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
150
|
+
if (props.multiple) {
|
151
|
+
const arrVal = Array.isArray(props.modelValue) ? props.modelValue : [];
|
152
|
+
return arrVal.map((v) => {
|
153
|
+
return v === null ? "__null__" : v;
|
154
|
+
}) || [];
|
155
|
+
} else {
|
156
|
+
return props.modelValue === null ? "__null__" : props.modelValue;
|
157
|
+
}
|
158
158
|
});
|
159
159
|
|
160
160
|
/**
|
@@ -162,13 +162,13 @@ const selectedValue = computed(() => {
|
|
162
162
|
* @type {ComputedRef<*>}
|
163
163
|
*/
|
164
164
|
const selectedOptions = computed(() => {
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
165
|
+
let values = selectedValue.value;
|
166
|
+
if (!props.multiple) {
|
167
|
+
values = (values || values === 0) ? [values] : [];
|
168
|
+
}
|
169
|
+
return computedOptions.value.filter((o) => {
|
170
|
+
return values.includes(o.value) || values.map(v => typeof v === "object" && v.id).includes(o.value?.id);
|
171
|
+
});
|
172
172
|
});
|
173
173
|
|
174
174
|
/**
|
@@ -177,12 +177,12 @@ const selectedOptions = computed(() => {
|
|
177
177
|
* @type {ComputedRef<unknown>}
|
178
178
|
*/
|
179
179
|
const selectedLabel = computed(() => {
|
180
|
-
|
180
|
+
if (props.filterable && isShowing.value) return "";
|
181
181
|
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
182
|
+
if (!selectedOptions.value || selectedOptions.value.length === 0) {
|
183
|
+
return props.placeholder || "(Select Option)";
|
184
|
+
}
|
185
|
+
return selectedOptions.value[0].selectionLabel;
|
186
186
|
});
|
187
187
|
|
188
188
|
/**
|
@@ -190,7 +190,7 @@ const selectedLabel = computed(() => {
|
|
190
190
|
* @type {ComputedRef<*>}
|
191
191
|
*/
|
192
192
|
const chipOptions = computed(() => {
|
193
|
-
|
193
|
+
return selectedOptions.value.slice(0, props.chipLimit);
|
194
194
|
});
|
195
195
|
|
196
196
|
/**
|
@@ -199,16 +199,16 @@ const chipOptions = computed(() => {
|
|
199
199
|
* @returns {*|string}
|
200
200
|
*/
|
201
201
|
function resolveLabel(option) {
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
202
|
+
if (typeof option === "string") {
|
203
|
+
return option;
|
204
|
+
}
|
205
|
+
if (typeof props.optionLabel === "string") {
|
206
|
+
return option[props.optionLabel];
|
207
|
+
}
|
208
|
+
if (typeof props.optionLabel === "function") {
|
209
|
+
return props.optionLabel(option);
|
210
|
+
}
|
211
|
+
return option?.label;
|
212
212
|
}
|
213
213
|
|
214
214
|
/**
|
@@ -218,16 +218,16 @@ function resolveLabel(option) {
|
|
218
218
|
* @returns {*|{default: null, type: String | StringConstructor}|string}
|
219
219
|
*/
|
220
220
|
function resolveSelectionLabel(option) {
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
221
|
+
if (typeof option === "string") {
|
222
|
+
return option;
|
223
|
+
}
|
224
|
+
if (typeof props.selectionLabel === "string") {
|
225
|
+
return option[props.selectionLabel];
|
226
|
+
}
|
227
|
+
if (typeof props.selectionLabel === "function") {
|
228
|
+
return props.selectionLabel(option);
|
229
|
+
}
|
230
|
+
return option?.selectionLabel || option?.label;
|
231
231
|
}
|
232
232
|
|
233
233
|
/**
|
@@ -236,17 +236,17 @@ function resolveSelectionLabel(option) {
|
|
236
236
|
* @returns {string|*|string}
|
237
237
|
*/
|
238
238
|
function resolveValue(option) {
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
239
|
+
if (!option || typeof option === "string") {
|
240
|
+
return option;
|
241
|
+
}
|
242
|
+
let value = option.value;
|
243
|
+
if (typeof props.optionValue === "string") {
|
244
|
+
value = option[props.optionValue];
|
245
|
+
} else if (typeof props.optionValue === "function") {
|
246
|
+
value = props.optionValue(option);
|
247
|
+
}
|
248
|
+
// Note the __null__ special case here. See the onUpdate function for more details
|
249
|
+
return value === null ? "__null__" : value;
|
250
250
|
}
|
251
251
|
|
252
252
|
/**
|
@@ -256,14 +256,14 @@ function resolveValue(option) {
|
|
256
256
|
* @param value
|
257
257
|
*/
|
258
258
|
function onUpdate(value) {
|
259
|
-
|
260
|
-
|
261
|
-
|
259
|
+
if (Array.isArray(value)) {
|
260
|
+
value = value.map((v) => v === "__null__" ? null : v);
|
261
|
+
}
|
262
262
|
|
263
|
-
|
263
|
+
value = value === "__null__" ? null : value;
|
264
264
|
|
265
|
-
|
266
|
-
|
265
|
+
emit("update", value);
|
266
|
+
emit("update:model-value", value);
|
267
267
|
}
|
268
268
|
|
269
269
|
/** XXX: This tells us when we should apply the filter. QSelect likes to trigger a new filter everytime you open the dropdown
|
@@ -277,19 +277,19 @@ const shouldFilter = ref(false);
|
|
277
277
|
* @param update
|
278
278
|
*/
|
279
279
|
async function onFilter(val, update) {
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
280
|
+
if (!props.filterFn) {
|
281
|
+
filter.value = val;
|
282
|
+
await nextTick(update);
|
283
|
+
} else {
|
284
|
+
update();
|
285
|
+
if (shouldFilter.value === false) return;
|
286
|
+
if (val !== null && val !== filter.value) {
|
287
|
+
filter.value = val;
|
288
|
+
if (props.filterFn) {
|
289
|
+
await props.filterFn(val);
|
290
|
+
}
|
291
|
+
}
|
292
|
+
}
|
293
293
|
}
|
294
294
|
|
295
295
|
/**
|
@@ -297,29 +297,29 @@ async function onFilter(val, update) {
|
|
297
297
|
* See the onUpdate function for more details
|
298
298
|
*/
|
299
299
|
function onClear() {
|
300
|
-
|
301
|
-
|
300
|
+
emit("update:model-value", undefined);
|
301
|
+
emit("update", undefined);
|
302
302
|
}
|
303
303
|
|
304
304
|
/**
|
305
305
|
* Handle behavior when showing the dropdown
|
306
306
|
*/
|
307
307
|
function onShow() {
|
308
|
-
|
308
|
+
isShowing.value = true;
|
309
309
|
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
310
|
+
// XXX: See description on shouldFilter declaration. Only allow filtering after dropdown is ALREADY opened
|
311
|
+
shouldFilter.value = false;
|
312
|
+
nextTick(() => {
|
313
|
+
shouldFilter.value = true;
|
314
|
+
selectField.value.focus();
|
315
|
+
});
|
316
316
|
}
|
317
317
|
|
318
318
|
/**
|
319
319
|
* Handle behavior when hiding the dropdown
|
320
320
|
*/
|
321
321
|
function onHide() {
|
322
|
-
|
323
|
-
|
322
|
+
isShowing.value = false;
|
323
|
+
shouldFilter.value = false;
|
324
324
|
}
|
325
325
|
</script>
|
@@ -70,11 +70,7 @@
|
|
70
70
|
<RenderVnode
|
71
71
|
v-if="field.vnode"
|
72
72
|
:vnode="field.vnode"
|
73
|
-
:
|
74
|
-
:disable="disable"
|
75
|
-
:show-name="showName"
|
76
|
-
:no-label="noLabel"
|
77
|
-
:model-value="getFieldValue(field.name)"
|
73
|
+
:props="{field, modelValue: getFieldValue(field.name), readonly, disable, showName, noLabel}"
|
78
74
|
@update:model-value="onInput(field.name, $event)"
|
79
75
|
/>
|
80
76
|
<Component
|
@@ -8,7 +8,11 @@
|
|
8
8
|
:action-target="controller.selectedRows.value"
|
9
9
|
:exporter="controller.exportList"
|
10
10
|
@refresh="controller.refreshAll"
|
11
|
-
|
11
|
+
>
|
12
|
+
<template #default>
|
13
|
+
<slot name="action-toolbar" />
|
14
|
+
</template>
|
15
|
+
</ActionToolbar>
|
12
16
|
</slot>
|
13
17
|
<div class="flex flex-nowrap flex-grow overflow-hidden w-full">
|
14
18
|
<slot name="filter-fields">
|
@@ -18,6 +22,7 @@
|
|
18
22
|
:show-filters="showFilters"
|
19
23
|
:filters="filters"
|
20
24
|
:active-filter="activeFilter"
|
25
|
+
class="dx-action-table-filters"
|
21
26
|
@update:active-filter="controller.setActiveFilter"
|
22
27
|
/>
|
23
28
|
</slot>
|
@@ -65,7 +70,7 @@ import { PanelsDrawer } from "../../PanelsDrawer";
|
|
65
70
|
import { PreviousNextControls } from "../../Utility";
|
66
71
|
import ActionTable from "../ActionTable";
|
67
72
|
import { CollapsableFiltersSidebar } from "../Filters";
|
68
|
-
import { ActionController, ActionPanel,
|
73
|
+
import { ActionController, ActionPanel, FilterGroup } from "../listControls";
|
69
74
|
import { TableColumn } from "../tableColumns";
|
70
75
|
import { ActionToolbar } from "../Toolbars";
|
71
76
|
|
@@ -74,7 +79,7 @@ const props = defineProps<{
|
|
74
79
|
showFilters: boolean,
|
75
80
|
controller: ActionController,
|
76
81
|
columns: TableColumn[],
|
77
|
-
filters?:
|
82
|
+
filters?: FilterGroup[],
|
78
83
|
panels?: ActionPanel[],
|
79
84
|
actions?: ActionOptions[],
|
80
85
|
exporter?: () => Promise<void>,
|
@@ -1,5 +1,5 @@
|
|
1
1
|
<template>
|
2
|
-
<div class="flex items-center">
|
2
|
+
<div class="dx-action-toolbar flex items-center">
|
3
3
|
<div class="flex-grow px-6">
|
4
4
|
<slot name="title">
|
5
5
|
<h2 v-if="title">
|
@@ -36,11 +36,11 @@ import ActionMenu from "../ActionMenu";
|
|
36
36
|
|
37
37
|
defineEmits(["refresh"]);
|
38
38
|
defineProps<{
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
39
|
+
title?: string,
|
40
|
+
actions: ActionOptions[],
|
41
|
+
actionTarget?: ActionTargetItem[],
|
42
|
+
refreshButton?: boolean,
|
43
|
+
loading?: boolean,
|
44
|
+
exporter?: () => void
|
45
45
|
}>();
|
46
46
|
</script>
|
@@ -1,82 +1,82 @@
|
|
1
|
-
import { computed, ref, watch } from "vue";
|
2
|
-
import { getItem, setItem } from "../../helpers";
|
1
|
+
import { computed, ref, VNode, watch } from "vue";
|
2
|
+
import { ActionOptions, 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?: ActionOptions[],
|
6
|
+
align?: string,
|
7
|
+
category?: string,
|
8
|
+
class?: string | object,
|
9
|
+
field: string,
|
10
|
+
format?: (value: any, options: any) => any,
|
11
|
+
innerClass?: string | object,
|
12
|
+
style?: string | object,
|
13
|
+
headerStyle?: string | object,
|
14
|
+
isSavingRow?: boolean | (() => boolean),
|
15
|
+
label: string,
|
16
|
+
maxWidth?: number,
|
17
|
+
minWidth?: number,
|
18
|
+
name: string,
|
19
|
+
onClick?: (target: any) => void,
|
20
|
+
required?: boolean,
|
21
|
+
resizeable?: boolean,
|
22
|
+
sortable?: boolean,
|
23
|
+
sortBy?: string,
|
24
|
+
sortByExpression?: string,
|
25
|
+
titleColumns?: () => string[],
|
26
|
+
vnode?: () => VNode,
|
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
|
+
titleColumnNames,
|
80
|
+
orderedTitleColumns
|
81
|
+
};
|
82
82
|
}
|