@afeefa/vue-app 0.0.48 → 0.0.52
Sign up to get free protection for your applications and to get access to all the features.
- package/.afeefa/package/release/version.txt +1 -1
- package/package.json +1 -1
- package/src/api-resources/ApiActions.js +1 -0
- package/src/components/AContextMenu.vue +2 -2
- package/src/components/AContextMenuItem.vue +9 -2
- package/src/components/ASearchSelect.vue +2 -2
- package/src/components/list/{RouteFilterSource.js → CurrentRouteFilterSource.js} +2 -2
- package/src/components/list/FilterSourceType.js +0 -3
- package/src/components/list/ListViewMixin.js +25 -18
- package/src/components/list/NextRouteFilterSource.js +15 -0
- package/src/components/list/filters/ListFilterSelect.vue +2 -2
- package/src/utils/props-helper.js +21 -0
- package/src-admin/components/App.vue +8 -26
- package/src-admin/components/list/ListView.vue +1 -1
- package/src-admin/components/pages/CreatePage.vue +4 -4
- package/src-admin/components/pages/DetailPage.vue +4 -7
- package/src-admin/components/pages/EditPage.vue +4 -4
- package/src-admin/components/routes/DetailRoute.vue +7 -2
- package/src-admin/components/routes/EditRoute.vue +7 -2
- package/src-admin/components/routes/ListRoute.vue +6 -2
- package/src/components/list/RouteQueryFilterSource.js +0 -18
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.52
|
package/package.json
CHANGED
@@ -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
|
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>
|
@@ -1,6 +1,6 @@
|
|
1
|
-
import {
|
1
|
+
import { ListViewFilterSource } from '@afeefa/api-resources-client'
|
2
2
|
|
3
|
-
export class
|
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
|
}
|
@@ -1,26 +1,31 @@
|
|
1
1
|
import { ListAction } from '@a-vue/api-resources/ApiActions'
|
2
|
+
import { propsWithDefaults } from '@a-vue/utils/props-helper'
|
2
3
|
import { ListViewModel } from '@afeefa/api-resources-client'
|
3
4
|
import { Component, Vue, Watch } from 'vue-property-decorator'
|
4
5
|
|
6
|
+
import { CurrentRouteFilterSource } from './CurrentRouteFilterSource'
|
5
7
|
import { FilterSourceType } from './FilterSourceType'
|
6
|
-
import { RouteFilterSource } from './RouteFilterSource'
|
7
8
|
|
8
9
|
@Component({
|
9
|
-
|
10
|
-
'models', 'meta', // if already loaded
|
10
|
+
...propsWithDefaults([
|
11
|
+
'models', 'meta', // given, if already loaded
|
11
12
|
'listViewConfig',
|
12
|
-
'
|
13
|
-
'
|
14
|
-
|
15
|
-
|
13
|
+
'filterHistoryKey',
|
14
|
+
'loadOnlyIfKeyword',
|
15
|
+
{
|
16
|
+
filterSource: FilterSourceType.QUERY_STRING,
|
17
|
+
events: true,
|
18
|
+
history: true
|
19
|
+
}
|
20
|
+
])
|
16
21
|
})
|
17
22
|
export class ListViewMixin extends Vue {
|
18
23
|
LIST_VIEW = true
|
19
24
|
|
25
|
+
listViewModel = null
|
20
26
|
models_ = []
|
21
27
|
meta_ = {}
|
22
28
|
isLoading = false
|
23
|
-
listViewModel = null
|
24
29
|
|
25
30
|
created () {
|
26
31
|
this.init()
|
@@ -36,10 +41,12 @@ export class ListViewMixin extends Vue {
|
|
36
41
|
this.listViewModel.off('change', this.filtersChanged)
|
37
42
|
}
|
38
43
|
|
39
|
-
const historyKey = this.
|
40
|
-
?
|
41
|
-
:
|
42
|
-
const filterSource = this.filterSource === FilterSourceType.
|
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
|
43
50
|
|
44
51
|
if (this.models) {
|
45
52
|
this.models_ = this.models
|
@@ -47,12 +54,12 @@ export class ListViewMixin extends Vue {
|
|
47
54
|
}
|
48
55
|
|
49
56
|
this.listViewModel = new ListViewModel(this.listViewConfig)
|
50
|
-
.filterSource(filterSource,
|
51
|
-
.historyKey(historyKey,
|
57
|
+
.filterSource(filterSource, !!filterSource)
|
58
|
+
.historyKey(historyKey, this.history)
|
52
59
|
.usedFilters(this.meta_.used_filters || null, this.meta_.count_search || 0)
|
53
60
|
.initFilters({
|
54
|
-
source:
|
55
|
-
history:
|
61
|
+
source: !!filterSource,
|
62
|
+
history: !!historyKey,
|
56
63
|
used: !!this.models
|
57
64
|
})
|
58
65
|
.on('change', this.filtersChanged) // listen to change
|
@@ -68,7 +75,7 @@ export class ListViewMixin extends Vue {
|
|
68
75
|
|
69
76
|
@Watch('$route.query')
|
70
77
|
routeQueryChanged () {
|
71
|
-
if (this.filterSource
|
78
|
+
if (this.filterSource === FilterSourceType.QUERY_STRING) {
|
72
79
|
this.listViewModel.filterSourceChanged()
|
73
80
|
}
|
74
81
|
}
|
@@ -117,7 +124,7 @@ export class ListViewMixin extends Vue {
|
|
117
124
|
|
118
125
|
const {models, meta} = await new ListAction()
|
119
126
|
.setRequest(request)
|
120
|
-
.noEvents(this.
|
127
|
+
.noEvents(!this.events)
|
121
128
|
.load()
|
122
129
|
|
123
130
|
if (!models) { // error happened
|
@@ -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
|
+
}
|
@@ -22,7 +22,7 @@ export default class ListFilterSelect extends Mixins(ListFilterMixin) {
|
|
22
22
|
items = null
|
23
23
|
|
24
24
|
created () {
|
25
|
-
if (this.filter.
|
25
|
+
if (this.filter.hasOptionsRequest()) {
|
26
26
|
this.items = this.loadRequestOptions()
|
27
27
|
} else if (this.filter.hasOptions()) {
|
28
28
|
this.items = this.getOptions()
|
@@ -54,7 +54,7 @@ export default class ListFilterSelect extends Mixins(ListFilterMixin) {
|
|
54
54
|
|
55
55
|
async loadRequestOptions () {
|
56
56
|
const {models} = await new ListAction()
|
57
|
-
.setRequest(this.filter.
|
57
|
+
.setRequest(this.filter.createOptionsRequest())
|
58
58
|
.noEvents()
|
59
59
|
.load()
|
60
60
|
|
@@ -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
|
+
}
|
@@ -13,7 +13,7 @@
|
|
13
13
|
>
|
14
14
|
<router-link
|
15
15
|
:to="{name: rootRouteName}"
|
16
|
-
class="logoContainer d-flex flex-column pa-6"
|
16
|
+
class="logoContainer d-flex flex-column align-center pa-6"
|
17
17
|
>
|
18
18
|
<img
|
19
19
|
v-if="logoUrl"
|
@@ -59,32 +59,14 @@
|
|
59
59
|
</div>
|
60
60
|
</div>
|
61
61
|
|
62
|
-
<
|
63
|
-
<
|
64
|
-
|
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-
|
77
|
-
|
78
|
-
|
79
|
-
|
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>
|
@@ -73,7 +73,7 @@ export default class ListView extends Mixins(ListViewMixin) {
|
|
73
73
|
|
74
74
|
@Watch('isLoading')
|
75
75
|
isLoadingChanged () {
|
76
|
-
if (this.
|
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.
|
60
|
-
console.warn('<create-page> owner must provide a static
|
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.
|
68
|
+
return this.$parent.constructor.createRouteConfig
|
69
69
|
}
|
70
70
|
|
71
71
|
get modelUpateAction () {
|
72
|
-
return this.ModelClass.getAction('
|
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', '
|
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.
|
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.
|
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
|
-
|
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.
|
82
|
-
console.warn('<edit-page> owner must provide a static
|
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.
|
98
|
+
return this.$parent.constructor.editRouteConfig
|
99
99
|
}
|
100
100
|
|
101
101
|
get modelUpateAction () {
|
102
|
-
return this.ModelClass.getAction('
|
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
|
-
|
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
|
-
|
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,7 @@
|
|
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 {
|
13
|
+
import { NextRouteFilterSource } from '@a-vue/components/list/NextRouteFilterSource'
|
14
14
|
import { ListViewModel } from '@afeefa/api-resources-client'
|
15
15
|
|
16
16
|
Component.registerHooks([
|
@@ -36,7 +36,11 @@ function load (route) {
|
|
36
36
|
}
|
37
37
|
|
38
38
|
const request = new ListViewModel(Component.listViewConfig)
|
39
|
-
|
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
|
40
44
|
.historyKey(route.path, false)
|
41
45
|
.initFilters({
|
42
46
|
source: true,
|
@@ -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
|
-
}
|