@afeefa/vue-app 0.0.139 → 0.0.141
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/.afeefa/package/release/version.txt +1 -1
- package/package.json +1 -1
- package/src/components/AContextMenu.vue +2 -1
- package/src/components/ATextField.vue +5 -1
- package/src/components/form/FormFieldMixin.js +1 -1
- package/src/components/form/fields/FormFieldSearchSelect.vue +1 -1
- package/src/components/list/ListViewMixin.js +8 -11
- package/src/components/list/filters/ListFilterSearch.vue +77 -12
- package/src/components/list/filters/ListFilterSelect.vue +8 -39
- package/src-admin/components/App.vue +5 -5
- package/src-admin/config/vuetify.js +3 -1
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.141
|
package/package.json
CHANGED
@@ -95,7 +95,7 @@ export default class FormFieldSearchSelect extends Mixins(FormFieldMixin) {
|
|
95
95
|
|
96
96
|
get listAction () {
|
97
97
|
const request = this.field
|
98
|
-
.
|
98
|
+
.createOptionsRequest()
|
99
99
|
.addParams(this.optionRequestParams || {})
|
100
100
|
return ListAction.fromRequest(request)
|
101
101
|
}
|
@@ -2,6 +2,7 @@ import { Component, Vue, Watch } from '@a-vue'
|
|
2
2
|
import { ListAction } from '@a-vue/api-resources/ApiActions'
|
3
3
|
import { sleep } from '@a-vue/utils/timeout'
|
4
4
|
import { ListViewModel } from '@afeefa/api-resources-client'
|
5
|
+
import axios from 'axios'
|
5
6
|
|
6
7
|
import { CurrentRouteFilterSource } from './CurrentRouteFilterSource'
|
7
8
|
import { FilterSourceType } from './FilterSourceType'
|
@@ -11,7 +12,6 @@ import { FilterSourceType } from './FilterSourceType'
|
|
11
12
|
'models', 'meta', // given, if already loaded
|
12
13
|
'listAction',
|
13
14
|
'filterHistoryKey',
|
14
|
-
'checkBeforeLoad',
|
15
15
|
{
|
16
16
|
filterSource: FilterSourceType.QUERY_STRING,
|
17
17
|
loadOnlyIfKeyword: false,
|
@@ -27,6 +27,7 @@ export class ListViewMixin extends Vue {
|
|
27
27
|
models_ = []
|
28
28
|
meta_ = {}
|
29
29
|
isLoading = false
|
30
|
+
cancelSource = null
|
30
31
|
|
31
32
|
created () {
|
32
33
|
this.init()
|
@@ -123,16 +124,6 @@ export class ListViewMixin extends Vue {
|
|
123
124
|
}
|
124
125
|
|
125
126
|
async load () {
|
126
|
-
if (this.checkBeforeLoad) {
|
127
|
-
const canLoad = await this.checkBeforeLoad()
|
128
|
-
if (!canLoad) {
|
129
|
-
if (this.meta_.used_filters) {
|
130
|
-
this.listViewModel.initFromUsedFilters(this.meta_.used_filters, this.meta_.count_search)
|
131
|
-
}
|
132
|
-
return
|
133
|
-
}
|
134
|
-
}
|
135
|
-
|
136
127
|
if (this.loadOnlyIfKeyword && !this.filters.q.value) {
|
137
128
|
this.models_ = []
|
138
129
|
this.meta_ = {}
|
@@ -143,10 +134,16 @@ export class ListViewMixin extends Vue {
|
|
143
134
|
this.isLoading = true
|
144
135
|
this.$emit('update:isLoading', this.isLoading)
|
145
136
|
|
137
|
+
if (this.cancelSource) {
|
138
|
+
this.cancelSource.cancel()
|
139
|
+
}
|
140
|
+
|
141
|
+
this.cancelSource = axios.CancelToken.source()
|
146
142
|
const request = this.listViewModel.getApiRequest()
|
147
143
|
|
148
144
|
const {models, meta} = await ListAction
|
149
145
|
.fromRequest(request)
|
146
|
+
.cancelSource(this.cancelSource)
|
150
147
|
.dispatchGlobalLoadingEvents(this.events)
|
151
148
|
.load()
|
152
149
|
|
@@ -1,16 +1,47 @@
|
|
1
1
|
<template>
|
2
|
-
<
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
2
|
+
<div>
|
3
|
+
<a-text-field
|
4
|
+
ref="input"
|
5
|
+
v-model="filter.value"
|
6
|
+
:maxWidth="$attrs.maxWidth || maxWidth_"
|
7
|
+
:label="label_"
|
8
|
+
:debounce="500"
|
9
|
+
v-bind="$attrs"
|
10
|
+
clearable
|
11
|
+
hide-details
|
12
|
+
@keyup.esc="clearValue"
|
13
|
+
v-on="$listeners"
|
14
|
+
>
|
15
|
+
<template
|
16
|
+
v-if="hasQFieldOptions()"
|
17
|
+
#append
|
18
|
+
>
|
19
|
+
<a-context-menu>
|
20
|
+
<template #activator>
|
21
|
+
<!-- click.prevent brings hand cursor -->
|
22
|
+
<!-- mousedown.stop will prevent the input focused -->
|
23
|
+
<a-icon
|
24
|
+
@click.prevent
|
25
|
+
@mousedown.stop
|
26
|
+
>
|
27
|
+
{{ appendIcon }}
|
28
|
+
</a-icon>
|
29
|
+
</template>
|
30
|
+
|
31
|
+
<a-context-menu-item
|
32
|
+
v-for="option in qFieldOptions"
|
33
|
+
:key="option.title"
|
34
|
+
@click="filters.qfield.value = option.value"
|
35
|
+
>
|
36
|
+
<a-icon :class="{selected: filters.qfield.value === option.value}">
|
37
|
+
$checkIcon
|
38
|
+
</a-icon>
|
39
|
+
{{ option.title }}
|
40
|
+
</a-context-menu-item>
|
41
|
+
</a-context-menu>
|
42
|
+
</template>
|
43
|
+
</a-text-field>
|
44
|
+
</div>
|
14
45
|
</template>
|
15
46
|
|
16
47
|
|
@@ -33,5 +64,39 @@ export default class ListFilterSearch extends Mixins(ListFilterMixin) {
|
|
33
64
|
setFocus (force) {
|
34
65
|
this.$refs.input.setFocus(force)
|
35
66
|
}
|
67
|
+
|
68
|
+
get appendIcon () {
|
69
|
+
if (this.hasQFieldOptions()) {
|
70
|
+
return '$filterIcon'
|
71
|
+
}
|
72
|
+
}
|
73
|
+
|
74
|
+
hasQFieldOptions () {
|
75
|
+
return this.filters.qfield && this.filters.qfield.hasOptions()
|
76
|
+
}
|
77
|
+
|
78
|
+
get qFieldOptions () {
|
79
|
+
return this.filters.qfield.options
|
80
|
+
}
|
81
|
+
|
82
|
+
get label_ () {
|
83
|
+
const label = this.label || 'Suche'
|
84
|
+
|
85
|
+
if (this.hasQFieldOptions() && !this.filters.qfield.hasDefaultValueSet()) {
|
86
|
+
const selected = this.qFieldOptions.filter(o => o.value === this.filters.qfield.value)[0]
|
87
|
+
if (selected) {
|
88
|
+
return `${label} (${selected.title})`
|
89
|
+
}
|
90
|
+
}
|
91
|
+
|
92
|
+
return label
|
93
|
+
}
|
36
94
|
}
|
37
95
|
</script>
|
96
|
+
|
97
|
+
|
98
|
+
<style lang="scss" scoped>
|
99
|
+
.contextMenuItem > *:not(.selected) {
|
100
|
+
opacity: 0;
|
101
|
+
}
|
102
|
+
</style>
|
@@ -5,7 +5,7 @@
|
|
5
5
|
:items="_items"
|
6
6
|
itemText="itemTitle"
|
7
7
|
itemValue="itemValue"
|
8
|
-
:clearable="filter.
|
8
|
+
:clearable="filter.hasDefaultValue() && !filter.hasDefaultValueSet()"
|
9
9
|
:defaultValue="filter.defaultValue"
|
10
10
|
hide-details
|
11
11
|
v-bind="$attrs"
|
@@ -36,44 +36,13 @@ export default class ListFilterSelect extends Mixins(ListFilterMixin) {
|
|
36
36
|
return this.$attrs.items || this.items || []
|
37
37
|
}
|
38
38
|
|
39
|
-
get showNullOption () {
|
40
|
-
// either null is a selectable option, than it should be shown in the list
|
41
|
-
// or the default is null, so the list should offer a 'null' option for the unselected state
|
42
|
-
return this.filter.nullIsOption || this.filter.defaultValue === null
|
43
|
-
}
|
44
|
-
|
45
|
-
createOptions () {
|
46
|
-
const options = []
|
47
|
-
|
48
|
-
if (this.filter.allIsOption) {
|
49
|
-
options.push({
|
50
|
-
itemTitle: 'Alle',
|
51
|
-
itemValue: 'all'
|
52
|
-
})
|
53
|
-
} else if (this.showNullOption) {
|
54
|
-
options.push({
|
55
|
-
itemTitle: 'Alle',
|
56
|
-
itemValue: null
|
57
|
-
})
|
58
|
-
}
|
59
|
-
|
60
|
-
if (this.filter.noneIsOption) {
|
61
|
-
options.push({
|
62
|
-
itemTitle: 'Keine',
|
63
|
-
itemValue: 'none'
|
64
|
-
})
|
65
|
-
}
|
66
|
-
|
67
|
-
return options
|
68
|
-
}
|
69
|
-
|
70
39
|
async loadRequestOptions () {
|
71
40
|
const {models} = await ListAction
|
72
41
|
.fromRequest(this.filter.createOptionsRequest())
|
73
42
|
.load()
|
74
43
|
|
75
44
|
return [
|
76
|
-
...this.
|
45
|
+
...this.getOptions(),
|
77
46
|
...models.map(model => {
|
78
47
|
let itemValue
|
79
48
|
if (this.itemValue) {
|
@@ -93,13 +62,13 @@ export default class ListFilterSelect extends Mixins(ListFilterMixin) {
|
|
93
62
|
|
94
63
|
getOptions () {
|
95
64
|
return [
|
96
|
-
...this.createOptions(),
|
97
65
|
...this.filter.options
|
98
|
-
.
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
66
|
+
.map(o => {
|
67
|
+
return {
|
68
|
+
itemTitle: o.title,
|
69
|
+
itemValue: o.value
|
70
|
+
}
|
71
|
+
})
|
103
72
|
]
|
104
73
|
}
|
105
74
|
}
|
@@ -94,7 +94,7 @@
|
|
94
94
|
top
|
95
95
|
left
|
96
96
|
class="loadingIndicator"
|
97
|
-
:isLoading="
|
97
|
+
:isLoading="!!numLoadingRequests"
|
98
98
|
:color="loaderColor"
|
99
99
|
/>
|
100
100
|
|
@@ -119,7 +119,7 @@
|
|
119
119
|
>
|
120
120
|
<sticky-header />
|
121
121
|
|
122
|
-
<router-view :class="{isLoading}" />
|
122
|
+
<router-view :class="{isLoading: !!numLoadingRequests}" />
|
123
123
|
</v-container>
|
124
124
|
|
125
125
|
<sticky-footer-container />
|
@@ -164,7 +164,7 @@ export default class App extends Vue {
|
|
164
164
|
|
165
165
|
drawer = true
|
166
166
|
closeMenuIcon = mdiBackburger
|
167
|
-
|
167
|
+
numLoadingRequests = 0
|
168
168
|
|
169
169
|
created () {
|
170
170
|
this.$events.on(LoadingEvent.START_LOADING, this.startLoading)
|
@@ -200,11 +200,11 @@ export default class App extends Vue {
|
|
200
200
|
}
|
201
201
|
|
202
202
|
startLoading () {
|
203
|
-
this.
|
203
|
+
this.numLoadingRequests++
|
204
204
|
}
|
205
205
|
|
206
206
|
stopLoading () {
|
207
|
-
this.
|
207
|
+
this.numLoadingRequests--
|
208
208
|
}
|
209
209
|
|
210
210
|
toggleDrawer () {
|
@@ -12,6 +12,7 @@ import {
|
|
12
12
|
mdiDelete,
|
13
13
|
mdiDotsHorizontal,
|
14
14
|
mdiDotsVertical,
|
15
|
+
mdiFilter,
|
15
16
|
mdiLock,
|
16
17
|
mdiLogoutVariant,
|
17
18
|
mdiMagnify,
|
@@ -58,7 +59,8 @@ export default new Vuetify({
|
|
58
59
|
printerIcon: mdiPrinter,
|
59
60
|
euroSymbol: mdiCurrencyEur,
|
60
61
|
paletteIcon: mdiPalette,
|
61
|
-
addIcon: mdiPlusCircle
|
62
|
+
addIcon: mdiPlusCircle,
|
63
|
+
filterIcon: mdiFilter
|
62
64
|
}
|
63
65
|
},
|
64
66
|
breakpoint: {
|