@afeefa/vue-app 0.0.62 → 0.0.65
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/README.md +3 -1
- package/package.json +1 -1
- package/src/components/ABreadcrumbs.vue +3 -2
- package/src/components/AModal.vue +21 -3
- package/src/components/ARichTextArea.vue +95 -85
- package/src/components/ATableRow.vue +4 -0
- package/src/components/flying-context/FlyingContextEvent.js +5 -0
- package/src/components/form/EditForm.vue +13 -3
- package/src/components/form/EditModal.vue +52 -35
- package/src/components/form/NestedEditForm.vue +4 -0
- package/src/components/form/fields/FormFieldRichTextArea.vue +5 -3
- package/src/components/list/ListViewMixin.js +14 -2
- package/src/components/search-select/SearchSelectList.vue +0 -1
- package/src/components/vue/Component.js +9 -2
- package/src/events.js +2 -0
- package/src-admin/bootstrap.js +1 -0
- package/src-admin/components/App.vue +55 -6
- package/src-admin/components/FlyingContext.vue +77 -0
- package/src-admin/components/FlyingContextContainer.vue +85 -0
- package/src-admin/components/StickyFooter.vue +42 -0
- package/src-admin/components/StickyFooterContainer.vue +66 -0
- package/src-admin/components/form/EditFormButtons.vue +8 -3
- package/src-admin/components/index.js +4 -7
- package/src-admin/components/list/ListView.vue +22 -9
- package/src-admin/components/pages/EditPage.vue +23 -9
- package/src-admin/components/routes/DataRouteMixin.js +24 -15
- package/src-admin/config/routing.js +0 -11
- package/src-admin/config/vuetify.js +7 -1
- package/src-admin/directives/index.js +26 -0
- package/src-admin/styles.scss +1 -4
- package/src-admin/components/pages/CreatePage.vue +0 -114
- package/src-admin/components/pages/DetailPage.vue +0 -143
- package/src-admin/components/pages/EditPageMixin.js +0 -96
- package/src-admin/components/pages/ListPage.vue +0 -55
- package/src-admin/components/routes/CreateRoute.vue +0 -15
- package/src-admin/components/routes/DetailRoute.vue +0 -85
- package/src-admin/components/routes/EditRoute.vue +0 -78
- package/src-admin/components/routes/ListRoute.vue +0 -110
@@ -3,8 +3,9 @@
|
|
3
3
|
<v-navigation-drawer
|
4
4
|
v-model="drawer"
|
5
5
|
app
|
6
|
-
fixeds
|
7
6
|
left
|
7
|
+
width="280"
|
8
|
+
class="menubar"
|
8
9
|
>
|
9
10
|
<v-container
|
10
11
|
flex-column
|
@@ -92,6 +93,7 @@
|
|
92
93
|
</v-navigation-drawer>
|
93
94
|
|
94
95
|
<v-app-bar
|
96
|
+
v-if="false"
|
95
97
|
app
|
96
98
|
flat
|
97
99
|
dense
|
@@ -115,23 +117,35 @@
|
|
115
117
|
/>
|
116
118
|
</v-app-bar>
|
117
119
|
|
118
|
-
<v-main>
|
120
|
+
<v-main id="v-main">
|
121
|
+
<a-row class="topbar">
|
122
|
+
<v-app-bar-nav-icon
|
123
|
+
class="sidebarToggleButton mr-2 ml-n1"
|
124
|
+
@click="toggleDrawer"
|
125
|
+
/>
|
126
|
+
<a-breadcrumbs />
|
127
|
+
</a-row>
|
128
|
+
|
119
129
|
<v-container
|
120
130
|
fluid
|
121
131
|
class="pa-4"
|
122
132
|
>
|
123
|
-
<div class="d-flex align-center">
|
124
|
-
<app-bar-title-container class="flex-grow-1
|
125
|
-
<app-bar-buttons class="mr-2
|
133
|
+
<div class="sticky-app-bar d-flex align-center">
|
134
|
+
<app-bar-title-container class="flex-grow-1" />
|
135
|
+
<app-bar-buttons class="mr-2" />
|
126
136
|
</div>
|
127
137
|
|
128
138
|
<router-view :class="{isLoading}" />
|
129
139
|
</v-container>
|
140
|
+
|
141
|
+
<sticky-footer-container v-if="true" />
|
130
142
|
</v-main>
|
131
143
|
|
132
144
|
<a-dialog id="app" />
|
133
145
|
|
134
146
|
<a-save-indicator />
|
147
|
+
|
148
|
+
<flying-context-container />
|
135
149
|
</div>
|
136
150
|
</template>
|
137
151
|
|
@@ -142,12 +156,16 @@ import { appConfig } from '@a-admin/config/AppConfig'
|
|
142
156
|
import { sleep } from '@a-vue/utils/timeout'
|
143
157
|
import AppBarButtons from './app/AppBarButtons'
|
144
158
|
import AppBarTitleContainer from './app/AppBarTitleContainer'
|
159
|
+
import FlyingContextContainer from './FlyingContextContainer'
|
160
|
+
import StickyFooterContainer from './StickyFooterContainer'
|
145
161
|
import '../styles.scss'
|
146
162
|
|
147
163
|
@Component({
|
148
164
|
components: {
|
149
165
|
AppBarButtons,
|
150
|
-
AppBarTitleContainer
|
166
|
+
AppBarTitleContainer,
|
167
|
+
FlyingContextContainer,
|
168
|
+
StickyFooterContainer
|
151
169
|
}
|
152
170
|
})
|
153
171
|
export default class App extends Vue {
|
@@ -167,6 +185,17 @@ export default class App extends Vue {
|
|
167
185
|
this.$emit('appLoaded')
|
168
186
|
}
|
169
187
|
|
188
|
+
mounted () {
|
189
|
+
const el = document.querySelector('.sticky-app-bar')
|
190
|
+
const observer = new IntersectionObserver(
|
191
|
+
([e]) => {
|
192
|
+
e.target.classList.toggle('is-pinned', e.intersectionRatio < 1)
|
193
|
+
},
|
194
|
+
{ threshold: [1] }
|
195
|
+
)
|
196
|
+
observer.observe(el)
|
197
|
+
}
|
198
|
+
|
170
199
|
get SidebarMenu () {
|
171
200
|
return appConfig.components.SidebarMenu
|
172
201
|
}
|
@@ -244,4 +273,24 @@ export default class App extends Vue {
|
|
244
273
|
height: 36px !important;
|
245
274
|
margin-top: 1px;
|
246
275
|
}
|
276
|
+
|
277
|
+
.topbar {
|
278
|
+
position: relative;
|
279
|
+
width: 100%;
|
280
|
+
left: 0;
|
281
|
+
top: 0;
|
282
|
+
padding: .2rem 1rem;
|
283
|
+
}
|
284
|
+
.sticky-app-bar {
|
285
|
+
position: sticky;
|
286
|
+
top: -1px;
|
287
|
+
margin: -1rem -1rem 0;
|
288
|
+
padding: 1rem;
|
289
|
+
|
290
|
+
&.is-pinned {
|
291
|
+
background: white;
|
292
|
+
z-index: 2;
|
293
|
+
box-shadow: 0 4px 7px -4px #00000033;
|
294
|
+
}
|
295
|
+
}
|
247
296
|
</style>
|
@@ -0,0 +1,77 @@
|
|
1
|
+
<template>
|
2
|
+
<div class="flyingContext">
|
3
|
+
<div :class="contextId">
|
4
|
+
<slot v-if="isVisible" />
|
5
|
+
</div>
|
6
|
+
</div>
|
7
|
+
</template>
|
8
|
+
|
9
|
+
<script>
|
10
|
+
import { Component, Watch, Vue } from '@a-vue'
|
11
|
+
import { FlyingContextEvent } from '@a-vue/events'
|
12
|
+
import { randomCssClass } from '@a-vue/utils/random'
|
13
|
+
|
14
|
+
@Component({
|
15
|
+
props: [{show: false}]
|
16
|
+
})
|
17
|
+
export default class FlyingContext extends Vue {
|
18
|
+
isVisible = false
|
19
|
+
contextId = randomCssClass(10)
|
20
|
+
|
21
|
+
created () {
|
22
|
+
this.$events.on(FlyingContextEvent.HIDE_ALL, this.onHide)
|
23
|
+
}
|
24
|
+
|
25
|
+
mounted () {
|
26
|
+
if (this.show) {
|
27
|
+
this.showChanged()
|
28
|
+
}
|
29
|
+
}
|
30
|
+
|
31
|
+
@Watch('show')
|
32
|
+
showChanged () {
|
33
|
+
if (this.isVisible === this.show) {
|
34
|
+
return
|
35
|
+
}
|
36
|
+
|
37
|
+
this.isVisible = this.show
|
38
|
+
|
39
|
+
if (this.isVisible) {
|
40
|
+
const container = this.getSidebarContainer()
|
41
|
+
container.appendChild(this.getContent())
|
42
|
+
} else {
|
43
|
+
this.$el.appendChild(this.getContent())
|
44
|
+
}
|
45
|
+
}
|
46
|
+
|
47
|
+
destroyed () {
|
48
|
+
const container = this.getSidebarContainer()
|
49
|
+
const el = this.getContent()
|
50
|
+
if (container.contains(el)) {
|
51
|
+
container.removeChild(el)
|
52
|
+
}
|
53
|
+
|
54
|
+
this.$events.off(FlyingContextEvent.HIDE_ALL, this.onHide)
|
55
|
+
}
|
56
|
+
|
57
|
+
onHide () {
|
58
|
+
if (this.isVisible) {
|
59
|
+
this.$el.appendChild(this.getContent())
|
60
|
+
this.isVisible = false
|
61
|
+
this.$emit('hide')
|
62
|
+
}
|
63
|
+
}
|
64
|
+
|
65
|
+
getContent () {
|
66
|
+
return document.querySelector('.' + this.contextId)
|
67
|
+
}
|
68
|
+
|
69
|
+
getSidebarContainer () {
|
70
|
+
return document.getElementById('flyingContextContainer__children')
|
71
|
+
}
|
72
|
+
}
|
73
|
+
</script>
|
74
|
+
|
75
|
+
|
76
|
+
<style lang="scss" scoped>
|
77
|
+
</style>
|
@@ -0,0 +1,85 @@
|
|
1
|
+
<template>
|
2
|
+
<div
|
3
|
+
id="flyingContextContainer"
|
4
|
+
:class="{visible}"
|
5
|
+
>
|
6
|
+
<a
|
7
|
+
href=""
|
8
|
+
@click.prevent="hide"
|
9
|
+
>close</a>
|
10
|
+
|
11
|
+
<br>
|
12
|
+
<br>
|
13
|
+
|
14
|
+
<div id="flyingContextContainer__children" />
|
15
|
+
</div>
|
16
|
+
</template>
|
17
|
+
|
18
|
+
<script>
|
19
|
+
import { Component, Vue } from '@a-vue'
|
20
|
+
import { FlyingContextEvent } from '@a-vue/events'
|
21
|
+
|
22
|
+
@Component({
|
23
|
+
props: []
|
24
|
+
})
|
25
|
+
export default class FlyingContextContainer extends Vue {
|
26
|
+
visible = false
|
27
|
+
|
28
|
+
mounted () {
|
29
|
+
this.mutationWatcher = new MutationObserver(this.domChanged)
|
30
|
+
this.mutationWatcher.observe(this.getChildrenContainer(), { childList: true })
|
31
|
+
|
32
|
+
window.addEventListener('mousedown', this.onClickOutside)
|
33
|
+
}
|
34
|
+
|
35
|
+
domChanged () {
|
36
|
+
const container = this.getChildrenContainer()
|
37
|
+
this.visible = !!container.children.length
|
38
|
+
}
|
39
|
+
|
40
|
+
hide () {
|
41
|
+
if (this.visible) {
|
42
|
+
this.$events.dispatch(new FlyingContextEvent(FlyingContextEvent.HIDE_ALL))
|
43
|
+
}
|
44
|
+
}
|
45
|
+
|
46
|
+
getChildrenContainer () {
|
47
|
+
return this.$el.querySelector('#flyingContextContainer__children')
|
48
|
+
}
|
49
|
+
|
50
|
+
onClickOutside (e) {
|
51
|
+
// check if trigger is clicked
|
52
|
+
let parent = e.target
|
53
|
+
while (parent) {
|
54
|
+
if (parent.classList.contains('flyingContextTrigger')) {
|
55
|
+
return
|
56
|
+
}
|
57
|
+
parent = parent.parentElement
|
58
|
+
}
|
59
|
+
|
60
|
+
// check if flying context ist clicked
|
61
|
+
if (!this.$el.contains(e.target)) {
|
62
|
+
this.hide()
|
63
|
+
}
|
64
|
+
}
|
65
|
+
}
|
66
|
+
</script>
|
67
|
+
|
68
|
+
|
69
|
+
<style lang="scss" scoped>
|
70
|
+
#flyingContextContainer {
|
71
|
+
position: fixed;
|
72
|
+
z-index: 200;
|
73
|
+
right: 0;
|
74
|
+
width: 60vw;
|
75
|
+
height: 80vh;
|
76
|
+
top: 10vh;
|
77
|
+
background: rgba($color: white, $alpha: .6);
|
78
|
+
border: 1px solid black;
|
79
|
+
transition: right .2s;
|
80
|
+
|
81
|
+
&:not(.visible) {
|
82
|
+
right: -80vw;
|
83
|
+
}
|
84
|
+
}
|
85
|
+
</style>
|
@@ -0,0 +1,42 @@
|
|
1
|
+
<template>
|
2
|
+
<div class="stickyFooter">
|
3
|
+
<div :class="contextId">
|
4
|
+
<slot />
|
5
|
+
</div>
|
6
|
+
</div>
|
7
|
+
</template>
|
8
|
+
|
9
|
+
<script>
|
10
|
+
import { Component, Vue } from '@a-vue'
|
11
|
+
import { randomCssClass } from '@a-vue/utils/random'
|
12
|
+
|
13
|
+
@Component
|
14
|
+
export default class StickyFooter extends Vue {
|
15
|
+
contextId = randomCssClass(10)
|
16
|
+
|
17
|
+
mounted () {
|
18
|
+
const container = this.getFooterContainer()
|
19
|
+
container.appendChild(this.getContent())
|
20
|
+
}
|
21
|
+
|
22
|
+
destroyed () {
|
23
|
+
const container = this.getFooterContainer()
|
24
|
+
const el = this.getContent()
|
25
|
+
if (container.contains(el)) {
|
26
|
+
container.removeChild(el)
|
27
|
+
}
|
28
|
+
}
|
29
|
+
|
30
|
+
getContent () {
|
31
|
+
return document.querySelector('.' + this.contextId)
|
32
|
+
}
|
33
|
+
|
34
|
+
getFooterContainer () {
|
35
|
+
return document.getElementById('stickyFooterContainer__children')
|
36
|
+
}
|
37
|
+
}
|
38
|
+
</script>
|
39
|
+
|
40
|
+
|
41
|
+
<style lang="scss" scoped>
|
42
|
+
</style>
|
@@ -0,0 +1,66 @@
|
|
1
|
+
<template>
|
2
|
+
<v-footer
|
3
|
+
id="stickyFooterContainer"
|
4
|
+
fixed
|
5
|
+
app
|
6
|
+
inset
|
7
|
+
:class="{visible}"
|
8
|
+
>
|
9
|
+
<div id="stickyFooterContainer__children" />
|
10
|
+
</v-footer>
|
11
|
+
</template>
|
12
|
+
|
13
|
+
<script>
|
14
|
+
import { Component, Vue } from '@a-vue'
|
15
|
+
|
16
|
+
@Component({
|
17
|
+
props: []
|
18
|
+
})
|
19
|
+
export default class StickyFooterContainer extends Vue {
|
20
|
+
visible = false
|
21
|
+
|
22
|
+
mounted () {
|
23
|
+
this.mutationWatcher = new MutationObserver(this.domChanged)
|
24
|
+
this.mutationWatcher.observe(this.getChildrenContainer(), { childList: true })
|
25
|
+
}
|
26
|
+
|
27
|
+
domChanged () {
|
28
|
+
const container = this.getChildrenContainer()
|
29
|
+
this.visible = !!container.children.length
|
30
|
+
|
31
|
+
this.$nextTick(() => {
|
32
|
+
const mainStyle = document.getElementById('v-main').style
|
33
|
+
if (this.visible) {
|
34
|
+
mainStyle.paddingBottom = this.$el.offsetHeight + 'px'
|
35
|
+
} else {
|
36
|
+
mainStyle.paddingBottom = '0px'
|
37
|
+
}
|
38
|
+
})
|
39
|
+
}
|
40
|
+
|
41
|
+
getChildrenContainer () {
|
42
|
+
return this.$el.querySelector('#stickyFooterContainer__children')
|
43
|
+
}
|
44
|
+
}
|
45
|
+
</script>
|
46
|
+
|
47
|
+
|
48
|
+
<style lang="scss" scoped>
|
49
|
+
#stickyFooterContainer {
|
50
|
+
background: white;
|
51
|
+
padding: 0;
|
52
|
+
|
53
|
+
&:not(.visible) {
|
54
|
+
display: none;
|
55
|
+
}
|
56
|
+
|
57
|
+
&__children {
|
58
|
+
width: 100%;
|
59
|
+
}
|
60
|
+
}
|
61
|
+
|
62
|
+
#stickyFooterContainer__children {
|
63
|
+
background: #F5F5F5;
|
64
|
+
padding: 1rem;
|
65
|
+
}
|
66
|
+
</style>
|
@@ -1,8 +1,11 @@
|
|
1
1
|
<template>
|
2
|
-
<a-row
|
2
|
+
<a-row
|
3
|
+
gap="2"
|
4
|
+
v-bind="$attrs"
|
5
|
+
>
|
3
6
|
<v-btn
|
4
7
|
:small="small"
|
5
|
-
:disabled="!changed || !valid"
|
8
|
+
:disabled="($has.reset && !changed) || !valid"
|
6
9
|
color="green white--text"
|
7
10
|
@click="$emit('save')"
|
8
11
|
>
|
@@ -10,7 +13,7 @@
|
|
10
13
|
</v-btn>
|
11
14
|
|
12
15
|
<v-icon
|
13
|
-
v-if="changed"
|
16
|
+
v-if="$has.reset && changed"
|
14
17
|
:small="small"
|
15
18
|
text
|
16
19
|
title="Formular zurücksetzen"
|
@@ -33,6 +36,8 @@ import { mdiRotateLeft} from '@mdi/js'
|
|
33
36
|
]
|
34
37
|
})
|
35
38
|
export default class EditFormButtons extends Vue {
|
39
|
+
$hasOptions = ['reset']
|
40
|
+
|
36
41
|
undoIcon = mdiRotateLeft
|
37
42
|
}
|
38
43
|
</script>
|
@@ -7,6 +7,7 @@ import DetailContent from './detail/DetailContent'
|
|
7
7
|
import DetailMeta from './detail/DetailMeta'
|
8
8
|
import DetailProperty from './detail/DetailProperty'
|
9
9
|
import DetailTitle from './detail/DetailTitle'
|
10
|
+
import FlyingContext from './FlyingContext.vue'
|
10
11
|
import EditFormButtons from './form/EditFormButtons'
|
11
12
|
import RemoveButton from './form/RemoveButton'
|
12
13
|
import ListCard from './list/ListCard'
|
@@ -17,11 +18,9 @@ import ListTitle from './list/ListTitle'
|
|
17
18
|
import ListView from './list/ListView'
|
18
19
|
import ModelCount from './model/ModelCount'
|
19
20
|
import ModelIcon from './model/ModelIcon'
|
20
|
-
import CreatePage from './pages/CreatePage'
|
21
|
-
import DetailPage from './pages/DetailPage'
|
22
21
|
import EditPage from './pages/EditPage'
|
23
|
-
import ListPage from './pages/ListPage'
|
24
22
|
import Start from './Start.vue'
|
23
|
+
import StickyFooter from './StickyFooter.vue'
|
25
24
|
|
26
25
|
Vue.component('ListCard', ListCard)
|
27
26
|
Vue.component('ListColumnHeader', ListColumnHeader)
|
@@ -29,9 +28,6 @@ Vue.component('ListContent', ListContent)
|
|
29
28
|
Vue.component('ListMeta', ListMeta)
|
30
29
|
Vue.component('ListTitle', ListTitle)
|
31
30
|
Vue.component('ListView', ListView)
|
32
|
-
Vue.component('ListPage', ListPage)
|
33
|
-
|
34
|
-
Vue.component('CreatePage', CreatePage)
|
35
31
|
|
36
32
|
Vue.component('EditPage', EditPage)
|
37
33
|
Vue.component('EditFormButtons', EditFormButtons)
|
@@ -43,7 +39,6 @@ Vue.component('ModelIcon', ModelIcon)
|
|
43
39
|
Vue.component('DetailContent', DetailContent)
|
44
40
|
Vue.component('DetailMeta', DetailMeta)
|
45
41
|
Vue.component('DetailTitle', DetailTitle)
|
46
|
-
Vue.component('DetailPage', DetailPage)
|
47
42
|
Vue.component('DetailProperty', DetailProperty)
|
48
43
|
Vue.component('DetailColumn', DetailColumn)
|
49
44
|
|
@@ -51,3 +46,5 @@ Vue.component('AppBarButton', AppBarButton)
|
|
51
46
|
Vue.component('AppBarTitle', AppBarTitle)
|
52
47
|
|
53
48
|
Vue.component('Start', Start)
|
49
|
+
Vue.component('FlyingContext', FlyingContext)
|
50
|
+
Vue.component('StickyFooter', StickyFooter)
|
@@ -8,12 +8,16 @@
|
|
8
8
|
/>
|
9
9
|
</div>
|
10
10
|
|
11
|
+
<slot
|
12
|
+
v-if="$scopedSlots.models"
|
13
|
+
name="before-models"
|
14
|
+
/>
|
15
|
+
|
11
16
|
<template v-if="models_.length">
|
12
17
|
<template v-if="$scopedSlots.models">
|
13
18
|
<slot
|
14
19
|
name="models"
|
15
20
|
:models="models_"
|
16
|
-
:setFilter="setFilter"
|
17
21
|
/>
|
18
22
|
</template>
|
19
23
|
|
@@ -28,7 +32,11 @@
|
|
28
32
|
<a-table-row
|
29
33
|
v-for="model in models_"
|
30
34
|
:key="model.id"
|
31
|
-
|
35
|
+
v-flying-context-trigger="hasFlyingContext"
|
36
|
+
:class="{selectable: hasFlyingContext}"
|
37
|
+
v-bind="getRowAttributes(model)"
|
38
|
+
v-on="getRowListeners(model)"
|
39
|
+
@click="$emit('flyingContext', model)"
|
32
40
|
>
|
33
41
|
<v-icon
|
34
42
|
v-if="$has.icon"
|
@@ -40,7 +48,6 @@
|
|
40
48
|
<slot
|
41
49
|
name="model-table"
|
42
50
|
:model="model"
|
43
|
-
:setFilter="setFilter"
|
44
51
|
/>
|
45
52
|
</a-table-row>
|
46
53
|
</a-table>
|
@@ -50,7 +57,6 @@
|
|
50
57
|
<div
|
51
58
|
v-for="model in models_"
|
52
59
|
:key="model.id"
|
53
|
-
:class="getModelClass(model)"
|
54
60
|
>
|
55
61
|
<slot
|
56
62
|
name="model"
|
@@ -76,7 +82,7 @@ import { ListViewMixin } from '@a-vue/components/list/ListViewMixin'
|
|
76
82
|
import { LoadingEvent } from '@a-vue/events'
|
77
83
|
|
78
84
|
@Component({
|
79
|
-
props: ['
|
85
|
+
props: ['rowAttributes', 'rowListeners']
|
80
86
|
})
|
81
87
|
export default class ListView extends Mixins(ListViewMixin) {
|
82
88
|
$hasOptions = ['icon']
|
@@ -93,12 +99,19 @@ export default class ListView extends Mixins(ListViewMixin) {
|
|
93
99
|
this.$emit('update:isLoading', this.isLoading)
|
94
100
|
}
|
95
101
|
|
96
|
-
|
97
|
-
|
102
|
+
getRowAttributes (model) {
|
103
|
+
if (typeof this.rowAttributes === 'function') {
|
104
|
+
return this.rowAttributes(model)
|
105
|
+
}
|
106
|
+
return this.rowAttributes
|
107
|
+
}
|
108
|
+
|
109
|
+
getRowListeners (model) {
|
110
|
+
return (this.rowListeners && this.rowListeners(model)) || {}
|
98
111
|
}
|
99
112
|
|
100
|
-
|
101
|
-
this.
|
113
|
+
get hasFlyingContext () {
|
114
|
+
return !!this.$listeners.flyingContext
|
102
115
|
}
|
103
116
|
}
|
104
117
|
</script>
|
@@ -4,19 +4,23 @@
|
|
4
4
|
:model="model"
|
5
5
|
:createModelToEdit="createModelToEdit"
|
6
6
|
>
|
7
|
-
<template #
|
7
|
+
<template #form="{modelToEdit, changed, valid}">
|
8
8
|
<slot
|
9
|
-
|
9
|
+
name="form"
|
10
|
+
:modelToEdit="modelToEdit"
|
10
11
|
:changed="changed"
|
11
12
|
:valid="valid"
|
12
13
|
/>
|
13
14
|
|
14
|
-
<
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
15
|
+
<sticky-footer>
|
16
|
+
<edit-form-buttons
|
17
|
+
:changed="changed"
|
18
|
+
:valid="valid"
|
19
|
+
:has="{reset: !!modelToEdit.id}"
|
20
|
+
@save="$emit('save', modelToEdit, ignoreChangesOnRouteChange)"
|
21
|
+
@reset="$refs.form.reset()"
|
22
|
+
/>
|
23
|
+
</sticky-footer>
|
20
24
|
</template>
|
21
25
|
</edit-form>
|
22
26
|
</template>
|
@@ -30,10 +34,11 @@ import { DialogEvent } from '@a-vue/events'
|
|
30
34
|
})
|
31
35
|
export default class EditPage extends Vue {
|
32
36
|
unregisterRouterHook = null
|
37
|
+
ignoreChangesOnRouteChange_ = false
|
33
38
|
|
34
39
|
created () {
|
35
40
|
this.unregisterRouterHook = this.$router.beforeEach(async (to, from, next) => {
|
36
|
-
if (this.$refs.form.changed) {
|
41
|
+
if (this.$refs.form.changed && !this.ignoreChangesOnRouteChange_) {
|
37
42
|
const result = await this.$events.dispatch(new DialogEvent(DialogEvent.SHOW, {
|
38
43
|
title: 'Änderungen verwerfen?',
|
39
44
|
message: 'Im Formular sind nicht gespeicherte Änderungen. Sollen diese verworfen werden?',
|
@@ -51,5 +56,14 @@ export default class EditPage extends Vue {
|
|
51
56
|
destroyed () {
|
52
57
|
this.unregisterRouterHook()
|
53
58
|
}
|
59
|
+
|
60
|
+
/**
|
61
|
+
* hook to allow to leave a just created (saved) model
|
62
|
+
*/
|
63
|
+
ignoreChangesOnRouteChange () {
|
64
|
+
// this.$refs.form.forceUnchanged()
|
65
|
+
console.info('TODO switch form to forceUnchanged')
|
66
|
+
this.ignoreChangesOnRouteChange_ = true
|
67
|
+
}
|
54
68
|
}
|
55
69
|
</script>
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { Component, Vue
|
1
|
+
import { Component, Vue } from '@a-vue'
|
2
2
|
|
3
3
|
Component.registerHooks([
|
4
4
|
'beforeRouteEnter',
|
@@ -7,6 +7,7 @@ Component.registerHooks([
|
|
7
7
|
|
8
8
|
let onLoadCallback = () => {}
|
9
9
|
let routerHookCalled = false
|
10
|
+
let lastResult = null // cache last result because of hmr reload
|
10
11
|
|
11
12
|
function onLoad (callback) {
|
12
13
|
onLoadCallback = callback
|
@@ -52,24 +53,32 @@ export class DataRouteMixin extends Vue {
|
|
52
53
|
|
53
54
|
/**
|
54
55
|
* triggered both, if route name or route params change
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
}
|
56
|
+
@Watch('$route.params')
|
57
|
+
async routeParamsChanged () {
|
58
|
+
if (routerHookCalled) {
|
59
|
+
return
|
60
|
+
}
|
61
61
|
|
62
|
-
|
63
|
-
|
62
|
+
if (!this.drm_isLoaded) {
|
63
|
+
const result = await load(this.$route)
|
64
64
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
65
|
+
if (result !== false) {
|
66
|
+
this.drm_onLoad(result)
|
67
|
+
this.drm_isLoaded = true
|
68
|
+
}
|
69
|
+
}
|
70
|
+
}
|
71
|
+
*/
|
71
72
|
|
72
73
|
drm_onLoad (result) {
|
73
74
|
onLoadCallback(this, result)
|
75
|
+
lastResult = result
|
76
|
+
}
|
77
|
+
|
78
|
+
created () {
|
79
|
+
// hmr reload creates vm but not triggers route enter
|
80
|
+
if (!routerHookCalled) {
|
81
|
+
onLoadCallback(this, lastResult)
|
82
|
+
}
|
74
83
|
}
|
75
84
|
}
|
@@ -1,7 +1,3 @@
|
|
1
|
-
import CreateRoute from '@a-admin/components/routes/CreateRoute'
|
2
|
-
import DetailRoute from '@a-admin/components/routes/DetailRoute'
|
3
|
-
import EditRoute from '@a-admin/components/routes/EditRoute'
|
4
|
-
import ListRoute from '@a-admin/components/routes/ListRoute'
|
5
1
|
import { routeConfigPlugin } from '@a-vue/plugins/route-config/RouteConfigPlugin'
|
6
2
|
|
7
3
|
export default routeConfigPlugin
|
@@ -9,13 +5,6 @@ export default routeConfigPlugin
|
|
9
5
|
linkActiveClass: 'active'
|
10
6
|
})
|
11
7
|
|
12
|
-
.defaultComponents({
|
13
|
-
list: ListRoute,
|
14
|
-
new: CreateRoute,
|
15
|
-
detail: DetailRoute,
|
16
|
-
edit: EditRoute
|
17
|
-
})
|
18
|
-
|
19
8
|
.defaultBreadcrumbTitles({
|
20
9
|
edit: 'Bearbeiten',
|
21
10
|
new: 'Neu'
|