@afeefa/vue-app 0.0.47 → 0.0.51

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.47
1
+ 0.0.51
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@afeefa/vue-app",
3
- "version": "0.0.47",
3
+ "version": "0.0.51",
4
4
  "description": "",
5
5
  "author": "Afeefa Kollektiv <kollektiv@afeefa.de>",
6
6
  "license": "MIT",
@@ -197,6 +197,7 @@ export class RemoveAction {
197
197
  .params({
198
198
  id: this.id
199
199
  })
200
+ .data(null)
200
201
  .send()
201
202
 
202
203
  if (this.afterRemoveHook) {
@@ -6,7 +6,7 @@
6
6
  >
7
7
  <slot name="activator">
8
8
  <a-icon class="contextButton">
9
- $dotsHorizontalIcon
9
+ {{ triggerIcon || '$dotsHorizontalIcon' }}
10
10
  </a-icon>
11
11
  </slot>
12
12
  </div>
@@ -31,7 +31,7 @@ import { Positions, PositionConfig } from '../services/PositionService'
31
31
  import { randomCssClass } from '../utils/random'
32
32
 
33
33
  @Component({
34
- props: ['contentHeight', 'repositionWatchKey']
34
+ props: ['contentHeight', 'repositionWatchKey', 'triggerIcon']
35
35
  })
36
36
  export default class AContextMenu extends Mixins(UsesPositionServiceMixin) {
37
37
  CONTEXT_MENU = true
@@ -10,7 +10,9 @@
10
10
  <script>
11
11
  import { Component, Vue } from 'vue-property-decorator'
12
12
 
13
- @Component
13
+ @Component({
14
+ props: ['to']
15
+ })
14
16
  export default class AContextMenuItem extends Vue {
15
17
  get contextMenu () {
16
18
  let parent = this.$parent
@@ -25,7 +27,12 @@ export default class AContextMenuItem extends Vue {
25
27
 
26
28
  click () {
27
29
  this.contextMenu.close()
28
- this.$emit('click')
30
+ if (this.to) {
31
+ this.$router.push(this.to)
32
+ .catch(() => null) // prevent duplicated navigation
33
+ } else {
34
+ this.$emit('click')
35
+ }
29
36
  }
30
37
  }
31
38
  </script>
@@ -64,7 +64,7 @@ export default class ADialog extends Mixins(UsesPositionServiceMixin, ComponentW
64
64
  }
65
65
 
66
66
  mounted () {
67
- // monkey patch onFousin to allow non vuetify-popups to receive focus
67
+ // monkey patch onFocusin to allow non vuetify-popups to receive focus
68
68
  const dialog = this.$refs.dialog
69
69
  const onFocusin = dialog.onFocusin
70
70
  dialog.onFocusin = e => {
@@ -127,7 +127,6 @@ export default class ADialog extends Mixins(UsesPositionServiceMixin, ComponentW
127
127
 
128
128
  createTransientAnchor () {
129
129
  let transientAnchorEl = document.querySelector('.' + this.transientAnchorClass)
130
- console.log('anchor', this.anchorPosition)
131
130
  if (!transientAnchorEl) {
132
131
  transientAnchorEl = document.createElement('div')
133
132
  transientAnchorEl.classList.add(this.transientAnchorClass)
@@ -46,11 +46,10 @@
46
46
  v-if="isOpen"
47
47
  v-bind="listConfig"
48
48
  :q="q"
49
- :noEvents="true"
50
- :noHistory="true"
49
+ :events="false"
50
+ :history="false"
51
51
  :filterSource="filterSource"
52
52
  :loadOnlyIfKeyword="_loadOnlyIfKeyword"
53
- :filters.sync="filters"
54
53
  :count.sync="count"
55
54
  :isLoading.sync="isLoading"
56
55
  >
@@ -76,7 +75,7 @@
76
75
  <div class="lastColumn" />
77
76
  </template>
78
77
 
79
- <template #not-found>
78
+ <template #not-found="{ filters }">
80
79
  <slot
81
80
  name="not-found"
82
81
  :filters="filters"
@@ -23,14 +23,22 @@ export default class ASelect extends Mixins(ComponentWidthMixin) {
23
23
  items_ = []
24
24
 
25
25
  mounted () {
26
- // monkey patch v-select setting 'undefined' on clearable
26
+ // monkey patch v-select to set default value on clear
27
+ const clearableCallback = this.select.clearableCallback
28
+ this.select.clearableCallback = () => {
29
+ this.select.isClear = true
30
+ clearableCallback()
31
+ this.select.isClear = false
32
+ }
33
+
27
34
  const setValue = this.select.setValue
28
35
  this.select.setValue = value => {
29
- if (!value && value !== false) { // if undefined alike
36
+ if (this.select.isClear) {
30
37
  value = this.defaultValue || null
31
38
  }
32
39
  setValue(value)
33
40
  }
41
+
34
42
  this.init()
35
43
  }
36
44
 
@@ -1,6 +1,6 @@
1
- import { BaseFilterSource } from '@afeefa/api-resources-client'
1
+ import { ListViewFilterSource } from '@afeefa/api-resources-client'
2
2
 
3
- export class RouteFilterSource extends BaseFilterSource {
3
+ export class CurrentRouteFilterSource extends ListViewFilterSource {
4
4
  router = null
5
5
 
6
6
  constructor (router) {
@@ -2,9 +2,6 @@ export class FilterSourceType {
2
2
  // filters are synchronized with url's query string
3
3
  static QUERY_STRING = 'query_string'
4
4
 
5
- // filters are initialized from given route params (no synchronisation allowed)
6
- static ROUTE_PARAMS = 'route_params'
7
-
8
5
  // filters are synchronized with plain history object
9
6
  static OBJECT = 'object'
10
7
  }
@@ -28,12 +28,9 @@ export class ListFilterMixin extends Vue {
28
28
 
29
29
  get filter () {
30
30
  const filter = this.filters[this._name]
31
-
32
-
33
31
  if (!filter) {
34
32
  console.warn(`Filter "${this._name}" not found`)
35
33
  }
36
-
37
34
  return filter
38
35
  }
39
36
 
@@ -1,24 +1,30 @@
1
1
  import { ListAction } from '@a-vue/api-resources/ApiActions'
2
+ import { propsWithDefaults } from '@a-vue/utils/props-helper'
3
+ import { ListViewModel } from '@afeefa/api-resources-client'
2
4
  import { Component, Vue, Watch } from 'vue-property-decorator'
3
5
 
6
+ import { CurrentRouteFilterSource } from './CurrentRouteFilterSource'
4
7
  import { FilterSourceType } from './FilterSourceType'
5
- import { RouteFilterSource } from './RouteFilterSource'
6
8
 
7
9
  @Component({
8
- props: [
9
- 'models', 'meta', // if already loaded
10
- 'listViewRequest',
11
- 'noEvents', 'noHistory',
12
- 'filterHistoryKey', 'filterSource',
13
- 'loadOnlyIfKeyword'
14
- ]
10
+ ...propsWithDefaults([
11
+ 'models', 'meta', // given, if already loaded
12
+ 'listViewConfig',
13
+ 'filterHistoryKey',
14
+ 'loadOnlyIfKeyword',
15
+ {
16
+ filterSource: FilterSourceType.QUERY_STRING,
17
+ events: true,
18
+ history: true
19
+ }
20
+ ])
15
21
  })
16
22
  export class ListViewMixin extends Vue {
17
23
  LIST_VIEW = true
18
24
 
25
+ listViewModel = null
19
26
  models_ = []
20
27
  meta_ = {}
21
- requestFilters = null
22
28
  isLoading = false
23
29
 
24
30
  created () {
@@ -26,34 +32,41 @@ export class ListViewMixin extends Vue {
26
32
  }
27
33
 
28
34
  destroyed () {
29
- this.requestFilters.off('change', this.filtersChanged)
35
+ this.listViewModel.off('change', this.filtersChanged)
30
36
  }
31
37
 
32
38
  init () {
39
+ if (this.listViewModel) {
40
+ // this can happen only on HMR-reload
41
+ this.listViewModel.off('change', this.filtersChanged)
42
+ }
43
+
44
+ const historyKey = this.history
45
+ ? [this.$route.path, this.filterHistoryKey].filter(i => i).join('.')
46
+ : undefined
47
+ const filterSource = this.filterSource === FilterSourceType.QUERY_STRING
48
+ ? new CurrentRouteFilterSource(this.$router)
49
+ : undefined
50
+
33
51
  if (this.models) {
34
52
  this.models_ = this.models
35
53
  this.meta_ = this.meta
36
54
  }
37
55
 
38
- if (this.requestFilters) {
39
- // this can happen only on HMR-reload
40
- this.requestFilters.off('change', this.filtersChanged)
41
- }
42
-
43
- const historyKey = this.noHistory
44
- ? undefined
45
- : [this.$route.path, this.filterHistoryKey].filter(i => i).join('.')
46
- const filterSource = this.filterSource === FilterSourceType.OBJECT ? undefined : new RouteFilterSource(this.$router)
47
- const action = this.listViewRequest.getAction()
48
- this.requestFilters = action.createRequestFilters(historyKey, filterSource)
49
-
50
- this.requestFilters.on('change', this.filtersChanged)
56
+ this.listViewModel = new ListViewModel(this.listViewConfig)
57
+ .filterSource(filterSource, !!filterSource)
58
+ .historyKey(historyKey, this.history)
59
+ .usedFilters(this.meta_.used_filters || null, this.meta_.count_search || 0)
60
+ .initFilters({
61
+ source: !!filterSource,
62
+ history: !!historyKey,
63
+ used: !!this.models
64
+ })
65
+ .on('change', this.filtersChanged) // listen to change
51
66
 
52
- this.$emit('update:filters', this.filters)
53
67
  this._filtersInitialized()
54
68
 
55
69
  if (this.models) {
56
- this.requestFilters.initFromUsed(this.meta_.used_filters, this.meta_.count_search)
57
70
  this.$emit('update:count', this.meta_.count_search)
58
71
  } else {
59
72
  this.load()
@@ -62,8 +75,8 @@ export class ListViewMixin extends Vue {
62
75
 
63
76
  @Watch('$route.query')
64
77
  routeQueryChanged () {
65
- if (this.filterSource !== FilterSourceType.QUERY_STRING) {
66
- this.requestFilters.filterSourceChanged()
78
+ if (this.filterSource === FilterSourceType.QUERY_STRING) {
79
+ this.listViewModel.filterSourceChanged()
67
80
  }
68
81
  }
69
82
 
@@ -81,11 +94,11 @@ export class ListViewMixin extends Vue {
81
94
  }
82
95
 
83
96
  resetFilters () {
84
- this.requestFilters.reset()
97
+ this.listViewModel.resetFilters()
85
98
  }
86
99
 
87
100
  get filters () {
88
- return this.requestFilters.getFilters()
101
+ return this.listViewModel.getFilters().getEntries()
89
102
  }
90
103
 
91
104
  get count () {
@@ -107,17 +120,16 @@ export class ListViewMixin extends Vue {
107
120
  this.isLoading = true
108
121
  this.$emit('update:isLoading', this.isLoading)
109
122
 
110
- const request = this.listViewRequest
111
- .filters(this.requestFilters.serialize())
112
- .toApiRequest()
123
+ const request = this.listViewModel.getApiRequest()
113
124
 
114
125
  const {models, meta} = await new ListAction()
115
126
  .setRequest(request)
116
- .noEvents(this.noEvents)
127
+ .noEvents(!this.events)
117
128
  .load()
118
129
 
119
- if (!models) { // error, reset filters
120
- this.resetFilters()
130
+ if (!models) { // error happened
131
+ this.isLoading = false
132
+ this.$emit('update:isLoading', this.isLoading)
121
133
  return
122
134
  }
123
135
 
@@ -125,7 +137,7 @@ export class ListViewMixin extends Vue {
125
137
  this.meta_ = meta
126
138
 
127
139
  if (this.meta_.used_filters) {
128
- this.requestFilters.initFromUsed(this.meta_.used_filters, this.meta_.count_search)
140
+ this.listViewModel.initFromUsedFilters(this.meta_.used_filters, this.meta_.count_search)
129
141
  }
130
142
 
131
143
  this.isLoading = false
@@ -0,0 +1,15 @@
1
+ import { ListViewFilterSource } from '@afeefa/api-resources-client'
2
+
3
+ export class NextRouteFilterSource extends ListViewFilterSource {
4
+ route = null
5
+
6
+ constructor (route) {
7
+ super()
8
+
9
+ this.route = route
10
+ }
11
+
12
+ getQuery () {
13
+ return this.route.query
14
+ }
15
+ }
@@ -5,7 +5,8 @@
5
5
  :items="_items"
6
6
  itemText="itemTitle"
7
7
  itemValue="itemValue"
8
- :clearable="filter.value !== null"
8
+ :clearable="filter.value !== filter.defaultValue"
9
+ :defaultValue="filter.defaultValue"
9
10
  v-bind="$attrs"
10
11
  />
11
12
  </template>
@@ -16,16 +17,14 @@ import { Component, Mixins } from 'vue-property-decorator'
16
17
  import { ListFilterMixin } from '../ListFilterMixin'
17
18
  import { ListAction } from '@a-vue/api-resources/ApiActions'
18
19
 
19
- @Component({
20
- props: ['totalVisible']
21
- })
20
+ @Component
22
21
  export default class ListFilterSelect extends Mixins(ListFilterMixin) {
23
22
  items = null
24
23
 
25
24
  created () {
26
- if (this.hasOptionsRequest()) {
25
+ if (this.filter.hasOptionsRequest()) {
27
26
  this.items = this.loadRequestOptions()
28
- } else if (this.hasOptions()) {
27
+ } else if (this.filter.hasOptions()) {
29
28
  this.items = this.getOptions()
30
29
  }
31
30
  }
@@ -34,43 +33,49 @@ export default class ListFilterSelect extends Mixins(ListFilterMixin) {
34
33
  return this.$attrs.items || this.items || []
35
34
  }
36
35
 
37
- hasOptions () {
38
- return this.filter.options
36
+ get showNullOption () {
37
+ // either null is a selectable option, than it should be shown in the list
38
+ // or the default is null, so the list should offer a 'null' option for the unselected state
39
+ return this.filter.nullIsOption || this.filter.defaultValue === null
39
40
  }
40
41
 
41
- hasOptionsRequest () {
42
- return this.filter.request
42
+ createOptions () {
43
+ const options = []
44
+
45
+ if (this.showNullOption) {
46
+ options.push({
47
+ itemTitle: 'Alle',
48
+ itemValue: null
49
+ })
50
+ }
51
+
52
+ return options
43
53
  }
44
54
 
45
55
  async loadRequestOptions () {
46
56
  const {models} = await new ListAction()
47
- .setRequest(this.filter.request)
57
+ .setRequest(this.filter.createOptionsRequest())
48
58
  .noEvents()
49
59
  .load()
50
60
 
51
61
  return [
52
- {
53
- itemTitle: 'Alle',
54
- itemValue: null
55
- },
62
+ ...this.createOptions(),
56
63
  ...models.map(model => ({
57
64
  itemTitle: model.name,
58
65
  itemValue: model.id
59
66
  }))
60
-
61
67
  ]
62
68
  }
63
69
 
64
70
  getOptions () {
65
71
  return [
66
- {
67
- itemTitle: 'Alle',
68
- itemValue: null
69
- },
70
- ...this.filter.options.map(o => ({
71
- itemTitle: o ? 'Ja' : 'Nein',
72
- itemValue: o
73
- }))
72
+ ...this.createOptions(),
73
+ ...this.filter.options
74
+ .filter(o => o !== null) // null is already set in options (if any)
75
+ .map(o => ({
76
+ itemTitle: o ? 'Ja' : 'Nein',
77
+ itemValue: o
78
+ }))
74
79
  ]
75
80
  }
76
81
  }
@@ -26,7 +26,10 @@
26
26
  v-else-if="showNotFound"
27
27
  class="notFound"
28
28
  >
29
- <slot name="not-found">
29
+ <slot
30
+ name="not-found"
31
+ :filters="filters"
32
+ >
30
33
  Nichts gefunden.
31
34
  </slot>
32
35
  </div>
@@ -0,0 +1,21 @@
1
+ export function propsWithDefaults (props) {
2
+ const normalizedProps = {}
3
+
4
+ for (const prop of props) {
5
+ if (typeof prop === 'object') {
6
+ Object.keys(prop).forEach(subProp => {
7
+ if (typeof prop[subProp] === 'object') {
8
+ normalizedProps[subProp] = prop[subProp]
9
+ } else {
10
+ normalizedProps[subProp] = { default: prop[subProp] }
11
+ }
12
+ })
13
+ } else {
14
+ normalizedProps[prop] = null
15
+ }
16
+ }
17
+
18
+ return {
19
+ props: normalizedProps
20
+ }
21
+ }
@@ -59,32 +59,14 @@
59
59
  </div>
60
60
  </div>
61
61
 
62
- <v-menu top>
63
- <template #activator="{ on, attrs }">
64
- <v-icon
65
- class="contextButton"
66
- v-bind="attrs"
67
- v-on="on"
68
- >
69
- $dotsVerticalIcon
70
- </v-icon>
71
- </template>
72
-
73
- <v-list
74
- class="pa-0"
62
+ <a-context-menu triggerIcon="$dotsVerticalIcon">
63
+ <a-context-menu-item
64
+ :to="{name: 'settings', params: {accountId: account.id}}"
75
65
  >
76
- <v-list-item :to="{name: 'accounts.edit', params: {accountId: account.id}}">
77
- <v-list-item-icon class="ma-0 mr-2 align-self-center">
78
- <v-icon class="ml-n1 mr-1">
79
- $pencilIcon
80
- </v-icon>
81
- </v-list-item-icon>
82
- <v-list-item-title>
83
- Einstellungen
84
- </v-list-item-title>
85
- </v-list-item>
86
- </v-list>
87
- </v-menu>
66
+ <v-icon>$pencilIcon</v-icon>
67
+ Einstellungen
68
+ </a-context-menu-item>
69
+ </a-context-menu>
88
70
  </div>
89
71
  </v-container>
90
72
  </v-container>
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <v-app>
2
+ <v-app class="splash">
3
3
  <v-container
4
4
  fill-height
5
5
  fluid
@@ -49,6 +49,13 @@ export default class Splash extends Vue {
49
49
 
50
50
 
51
51
  <style lang="scss" scoped>
52
+ .splash {
53
+ position: absolute;
54
+ top: 0;
55
+ left: 0;
56
+ width: 100vw;
57
+ }
58
+
52
59
  .logo {
53
60
  margin-bottom: 2rem;
54
61
  }
@@ -73,7 +73,7 @@ export default class ListView extends Mixins(ListViewMixin) {
73
73
 
74
74
  @Watch('isLoading')
75
75
  isLoadingChanged () {
76
- if (this.noEvents === undefined || !this.noEvents) {
76
+ if (this.events) {
77
77
  if (this.isLoading) {
78
78
  this.$events.dispatch(new LoadingEvent(LoadingEvent.START_LOADING))
79
79
  } else {
@@ -56,8 +56,8 @@ import { apiResources } from '@afeefa/api-resources-client'
56
56
  })
57
57
  export default class CreatePage extends Mixins(EditPageMixin) {
58
58
  created () {
59
- if (!this.$parent.constructor.getCreateConfig) {
60
- console.warn('<create-page> owner must provide a static getCreateConfig method.')
59
+ if (!this.$parent.constructor.createRouteConfig) {
60
+ console.warn('<create-page> owner must provide a static createRouteConfig method.')
61
61
  }
62
62
 
63
63
  this.reset()
@@ -65,11 +65,11 @@ export default class CreatePage extends Mixins(EditPageMixin) {
65
65
  }
66
66
 
67
67
  get editConfig () {
68
- return this.$parent.constructor.getCreateConfig(this.$route)
68
+ return this.$parent.constructor.createRouteConfig
69
69
  }
70
70
 
71
71
  get modelUpateAction () {
72
- return this.ModelClass.getAction('create')
72
+ return this.editConfig.createAction || this.ModelClass.getAction('save')
73
73
  }
74
74
 
75
75
  get _icon () {
@@ -55,7 +55,7 @@ import { Component, Vue, Watch } from 'vue-property-decorator'
55
55
  import { RemoveAction } from '@a-vue/api-resources/ApiActions'
56
56
 
57
57
  @Component({
58
- props: ['model', 'title', 'icon', 'removeAction', 'protectRemove', 'listLink']
58
+ props: ['model', 'title', 'icon', 'protectRemove', 'listLink']
59
59
  })
60
60
  export default class DetailPage extends Vue {
61
61
  $hasOptions = ['edit', 'remove', 'list']
@@ -64,7 +64,7 @@ export default class DetailPage extends Vue {
64
64
  removeConfirmed = null
65
65
 
66
66
  created () {
67
- if (!this.$parent.constructor.getDetailConfig) {
67
+ if (!this.$parent.constructor.detailRouteConfig) {
68
68
  console.warn('<detail-page> owner must provide a static getDetailConfig method.')
69
69
  }
70
70
  this.$emit('model', this.model)
@@ -80,7 +80,7 @@ export default class DetailPage extends Vue {
80
80
  }
81
81
 
82
82
  get detailConfig () {
83
- return this.$parent.constructor.getDetailConfig(this.$route)
83
+ return this.$parent.constructor.detailRouteConfig
84
84
  }
85
85
 
86
86
  get document () {
@@ -110,10 +110,7 @@ export default class DetailPage extends Vue {
110
110
  }
111
111
 
112
112
  get _deleteAction () {
113
- if (this.removeAction) {
114
- return this.removeAction
115
- }
116
- return this.ModelClass.getAction('delete')
113
+ return this.detailConfig.removeAction || this.ModelClass.getAction('save')
117
114
  }
118
115
 
119
116
  async remove () {
@@ -78,8 +78,8 @@ export default class EditPage extends Mixins(EditPageMixin) {
78
78
  model_ = null
79
79
 
80
80
  created () {
81
- if (!this.$parent.constructor.getEditConfig) {
82
- console.warn('<edit-page> owner must provide a static getEditConfig method.')
81
+ if (!this.$parent.constructor.editRouteConfig) {
82
+ console.warn('<edit-page> owner must provide a static editRouteConfig method.')
83
83
  }
84
84
 
85
85
  this.model_ = this.model
@@ -95,11 +95,11 @@ export default class EditPage extends Mixins(EditPageMixin) {
95
95
  }
96
96
 
97
97
  get editConfig () {
98
- return this.$parent.constructor.getEditConfig(this.$route)
98
+ return this.$parent.constructor.editRouteConfig
99
99
  }
100
100
 
101
101
  get modelUpateAction () {
102
- return this.ModelClass.getAction('update')
102
+ return this.editConfig.updateAction || this.ModelClass.getAction('save')
103
103
  }
104
104
 
105
105
  get _getAction () {
@@ -18,13 +18,18 @@ Component.registerHooks([
18
18
  function load (route) {
19
19
  const routeDefinition = route.meta.routeDefinition
20
20
  const Component = routeDefinition.config.detail
21
- const detailConfig = Component.getDetailConfig(route)
21
+
22
+ if (!Component.detailRouteConfig) {
23
+ console.warn('A detail route component must implement a static detailRouteConfig property.')
24
+ }
25
+
26
+ const detailConfig = Component.detailRouteConfig
22
27
  const action = detailConfig.action || detailConfig.ModelClass.getAction('get')
23
28
 
24
29
  return new GetAction()
25
30
  .setAction(action)
26
31
  .setFields(detailConfig.fields)
27
- .setId(route.params[routeDefinition.idKey])
32
+ .setId(detailConfig.id || route.params[routeDefinition.idKey])
28
33
  .load()
29
34
  }
30
35
 
@@ -19,13 +19,18 @@ Component.registerHooks([
19
19
  function load (route) {
20
20
  const routeDefinition = route.meta.routeDefinition
21
21
  const Component = routeDefinition.config.edit
22
- const editConfig = Component.getEditConfig(route)
22
+
23
+ if (!Component.editRouteConfig) {
24
+ console.warn('An edit route component must implement a static editRouteConfig property.')
25
+ }
26
+
27
+ const editConfig = Component.editRouteConfig
23
28
  const action = editConfig.getAction || editConfig.ModelClass.getAction('get')
24
29
 
25
30
  return new GetAction()
26
31
  .setAction(action)
27
32
  .setFields(editConfig.fields)
28
- .setId(route.params[routeDefinition.idKey])
33
+ .setId(editConfig.id || route.params[routeDefinition.idKey])
29
34
  .load()
30
35
  }
31
36
 
@@ -10,7 +10,8 @@
10
10
  <script>
11
11
  import { Component, Vue, Watch } from 'vue-property-decorator'
12
12
  import { ListAction } from '@a-vue/api-resources/ApiActions'
13
- import { RouteQueryFilterSource } from '@a-vue/components/list/RouteQueryFilterSource'
13
+ import { NextRouteFilterSource } from '@a-vue/components/list/NextRouteFilterSource'
14
+ import { ListViewModel } from '@afeefa/api-resources-client'
14
15
 
15
16
  Component.registerHooks([
16
17
  'beforeRouteEnter',
@@ -30,14 +31,22 @@ function load (route) {
30
31
  const routeDefinition = route.meta.routeDefinition
31
32
  const Component = routeDefinition.config.list
32
33
 
33
- if (!Component.listViewRequest) {
34
- console.warn('A list route component must implement a static listViewRequest property.')
34
+ if (!Component.listViewConfig) {
35
+ console.warn('A list route component must implement a static listViewConfig property.')
35
36
  }
36
37
 
37
- const request = Component.listViewRequest.clone()
38
- .initFromFilterSource(new RouteQueryFilterSource(route))
39
- .recreateFromHistory(route.path)
40
- .toApiRequest()
38
+ const request = new ListViewModel(Component.listViewConfig)
39
+ // read from next route query string, but do not push
40
+ // list component will be init with used_filters
41
+ .filterSource(new NextRouteFilterSource(route), false)
42
+ // read from history, but do not push
43
+ // list component will be init with used_filters
44
+ .historyKey(route.path, false)
45
+ .initFilters({
46
+ source: true,
47
+ history: true
48
+ })
49
+ .getApiRequest()
41
50
 
42
51
  return new ListAction()
43
52
  .setRequest(request)
@@ -21,14 +21,16 @@
21
21
  }
22
22
 
23
23
  .v-application {
24
- margin-bottom: 2rem;
25
-
26
24
  .text-strike {
27
25
  color: inherit;
28
26
  text-decoration: line-through;
29
27
  }
30
28
  }
31
29
 
30
+ .v-main {
31
+ margin-bottom: 2rem;
32
+ }
33
+
32
34
  .theme--light.v-btn.v-btn--has-bg {
33
35
  background-color: #E9E9E9;
34
36
  }
@@ -1,124 +0,0 @@
1
- import { RequestFilters, apiResources } from '@afeefa/api-resources-client'
2
-
3
- export class ListViewRequest {
4
- _api = {}
5
- _resource = {}
6
- _action = {}
7
-
8
- _params = {}
9
- _filters = {}
10
- _fields = {}
11
-
12
- _filterSource = null
13
- _historyKey = null
14
-
15
- action ({api, resource, action}) {
16
- this._api = api
17
- this._resource = resource
18
- this._action = action
19
- return this
20
- }
21
-
22
- getAction () {
23
- return apiResources.getAction({
24
- api: this._api,
25
- resource: this._resource,
26
- action: this._action
27
- })
28
- }
29
-
30
- params (params) {
31
- this._params = params
32
- return this
33
- }
34
-
35
- getParams () {
36
- return this._params
37
- }
38
-
39
- filters (filters) {
40
- this._filters = filters
41
- return this
42
- }
43
-
44
- getFilters () {
45
- return this._filters
46
- }
47
-
48
- fields (fields) {
49
- this._fields = fields
50
- return this
51
- }
52
-
53
- getFields () {
54
- return this._fields
55
- }
56
-
57
- initFromFilterSource (filterSource) {
58
- this._filterSource = filterSource
59
- return this
60
- }
61
-
62
- recreateFromHistory (historyKey) {
63
- this._historyKey = historyKey
64
- return this
65
- }
66
-
67
- clone () {
68
- const request = new ListViewRequest()
69
- request._api = this._api
70
- request._resource = this._resource
71
- request._action = this._action
72
- request._params = this._params
73
- request._filters = this._filters
74
- request._fields = this._fields
75
- return request
76
- }
77
-
78
- toApiRequest () {
79
- const action = this.getAction({
80
- api: this._api,
81
- resource: this._resource,
82
- action: this._action
83
- })
84
-
85
- const request = action.createRequest()
86
- .params(this._params)
87
- .fields(this._fields)
88
-
89
- // filters set on the request will be skipped if filterSource or historyKey are given
90
- // and produce results
91
- let filtersToUse = this._filters
92
- let useHistory = true
93
-
94
- // create and init request filters based on the current filter source state
95
- if (this._filterSource) {
96
- const requestFilters = action.createRequestFilters(null, this._filterSource)
97
- const requestFiltersToUse = requestFilters.serialize()
98
- // do not use history if any filter given by the given filter source
99
- if (Object.keys(requestFiltersToUse).length) {
100
- useHistory = false
101
- }
102
-
103
- filtersToUse = {
104
- ...filtersToUse,
105
- ...requestFiltersToUse
106
- }
107
- }
108
-
109
- // if any filters is set by current filter source
110
- // skip history and give that filter precedence
111
- // otherwise: set stored filters
112
- if (useHistory && this._historyKey) {
113
- // check any already stored filters from a previous request
114
- const storedFilters = RequestFilters.fromHistory(this._historyKey)
115
- if (storedFilters) {
116
- filtersToUse = storedFilters.serialize()
117
- }
118
- }
119
-
120
- request.filters(filtersToUse)
121
-
122
- return request
123
- }
124
- }
@@ -1,18 +0,0 @@
1
- import { BaseFilterSource } from '@afeefa/api-resources-client'
2
-
3
- export class RouteQueryFilterSource extends BaseFilterSource {
4
- route = null
5
-
6
- constructor (route) {
7
- super()
8
-
9
- this.route = route
10
- }
11
-
12
- getQuery () {
13
- return this.route.query
14
- }
15
-
16
- push (_query) {
17
- }
18
- }