@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.
@@ -1 +1 @@
1
- 0.0.139
1
+ 0.0.141
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@afeefa/vue-app",
3
- "version": "0.0.139",
3
+ "version": "0.0.141",
4
4
  "description": "",
5
5
  "author": "Afeefa Kollektiv <kollektiv@afeefa.de>",
6
6
  "license": "MIT",
@@ -1,8 +1,9 @@
1
1
  <template>
2
2
  <div class="contextEditor">
3
+ <!-- click.stop prevents possible surrounding v-input to focus -->
3
4
  <div
4
5
  class="activator"
5
- @click="open"
6
+ @click.stop="open"
6
7
  >
7
8
  <slot name="activator">
8
9
  <a-icon class="contextButton">
@@ -11,7 +11,11 @@
11
11
  @click:clear="clear"
12
12
  @blur="sanitizeInternalValue"
13
13
  v-on="listeners"
14
- />
14
+ >
15
+ <template #append>
16
+ <slot name="append" />
17
+ </template>
18
+ </v-text-field>
15
19
  </template>
16
20
 
17
21
 
@@ -54,7 +54,7 @@ export class FormFieldMixin extends Vue {
54
54
 
55
55
  if (field.hasOptionsRequest()) {
56
56
  const request = field
57
- .getOptionsRequest()
57
+ .createOptionsRequest()
58
58
  .addParams(this.optionRequestParams || {})
59
59
  .addFilters(filters)
60
60
 
@@ -95,7 +95,7 @@ export default class FormFieldSearchSelect extends Mixins(FormFieldMixin) {
95
95
 
96
96
  get listAction () {
97
97
  const request = this.field
98
- .getOptionsRequest()
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
- <a-text-field
3
- ref="input"
4
- v-model="filter.value"
5
- :maxWidth="$attrs.maxWidth || maxWidth_"
6
- :label="label || 'Suche'"
7
- :debounce="500"
8
- v-bind="$attrs"
9
- clearable
10
- hide-details
11
- @keyup.esc="clearValue"
12
- v-on="$listeners"
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.value !== filter.defaultValue"
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.createOptions(),
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
- .filter(o => o !== null) // null is already set in options (if any)
99
- .map(o => ({
100
- itemTitle: o ? 'Ja' : 'Nein',
101
- itemValue: o
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="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
- isLoading = false
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.isLoading = true
203
+ this.numLoadingRequests++
204
204
  }
205
205
 
206
206
  stopLoading () {
207
- this.isLoading = false
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: {