@afeefa/vue-app 0.0.47 → 0.0.51

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
- }