@afeefa/vue-app 0.0.65 → 0.0.66
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/package.json +1 -1
- package/src/components/ABreadcrumbs.vue +75 -19
- package/src/components/ADatePicker.vue +1 -1
- package/src/components/AIcon.vue +3 -6
- package/src/components/AIconButton.vue +1 -2
- package/src/components/ARow.vue +0 -7
- package/src/components/list/ListViewMixin.js +11 -0
- package/src/components/mixins/ClickOutsideMixin.js +5 -1
- package/src-admin/components/App.vue +35 -85
- package/src-admin/components/Sidebar.vue +66 -0
- package/src-admin/components/SidebarItem.vue +59 -0
- package/src-admin/components/StickyHeader.vue +73 -0
- package/src-admin/components/app/AppBarButtons.vue +0 -7
- package/src-admin/components/app/AppBarTitle.vue +55 -11
- package/src-admin/components/app/AppBarTitleContainer.vue +2 -3
- package/src-admin/components/controls/SearchSelectFormField.vue +1 -0
- package/src-admin/components/detail/DetailProperty.vue +20 -16
- package/src-admin/components/form/EditFormButtons.vue +18 -4
- package/src-admin/components/form/RemoveButton.vue +17 -8
- package/src-admin/components/index.js +2 -0
- package/src-admin/components/pages/EditPage.vue +2 -2
- package/src-admin/config/vuetify.js +16 -2
- package/src-admin/styles.scss +20 -0
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.66
|
package/package.json
CHANGED
@@ -1,19 +1,38 @@
|
|
1
1
|
<template>
|
2
|
-
<div class="d-flex
|
3
|
-
<div
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
<v-icon>$chevronRightIcon</v-icon>
|
9
|
-
|
10
|
-
<router-link
|
11
|
-
:to="breadcrumb.to"
|
12
|
-
:exact="true"
|
2
|
+
<div class="a-breadcrumbs d-flex align-start gap-2 mr-4">
|
3
|
+
<div :class="['breadcrumbs d-flex align-center', {'flex-wrap': wrapBreadcrumbs_}]">
|
4
|
+
<div
|
5
|
+
v-for="(breadcrumb, index) in breadcrumbs"
|
6
|
+
:key="index"
|
7
|
+
class="item mr-2 d-flex align-center"
|
13
8
|
>
|
14
|
-
|
15
|
-
|
9
|
+
<v-icon v-if="index > 0">
|
10
|
+
$chevronRightIcon
|
11
|
+
</v-icon>
|
12
|
+
|
13
|
+
<router-link
|
14
|
+
:to="breadcrumb.to"
|
15
|
+
:exact="true"
|
16
|
+
>
|
17
|
+
{{ breadcrumb.title }}
|
18
|
+
</router-link>
|
19
|
+
</div>
|
16
20
|
</div>
|
21
|
+
|
22
|
+
<v-avatar
|
23
|
+
v-if="expandVisible"
|
24
|
+
class="expand"
|
25
|
+
color="#EEE"
|
26
|
+
size="1.3rem"
|
27
|
+
@click="wrapBreadcrumbs"
|
28
|
+
>
|
29
|
+
<a-icon>$caret{{ wrapBreadcrumbs_ ? 'Up' : 'Down' }}Icon</a-icon>
|
30
|
+
</v-avatar>
|
31
|
+
|
32
|
+
<div
|
33
|
+
v-else
|
34
|
+
class="expandDummy"
|
35
|
+
/>
|
17
36
|
</div>
|
18
37
|
</template>
|
19
38
|
|
@@ -28,6 +47,8 @@ export default class ABreadcrumbs extends Vue {
|
|
28
47
|
breadcrumbs = []
|
29
48
|
titleCache = {}
|
30
49
|
lastRoute = null
|
50
|
+
expandVisible = false
|
51
|
+
wrapBreadcrumbs_ = false
|
31
52
|
|
32
53
|
created () {
|
33
54
|
this.$events.on(SaveEvent.STOP_SAVING, this.afterSave)
|
@@ -101,21 +122,46 @@ export default class ABreadcrumbs extends Vue {
|
|
101
122
|
}
|
102
123
|
|
103
124
|
this.breadcrumbs = breadcrumbs
|
125
|
+
this.wrapBreadcrumbs_ = false
|
126
|
+
|
127
|
+
this.scrollBreadcrumbs()
|
128
|
+
}
|
129
|
+
|
130
|
+
scrollBreadcrumbs () {
|
131
|
+
this.$nextTick(() => {
|
132
|
+
const objDiv = this.$el.querySelector('.breadcrumbs')
|
133
|
+
if (objDiv.scrollWidth > objDiv.offsetWidth) {
|
134
|
+
objDiv.scrollLeft = objDiv.scrollWidth
|
135
|
+
this.expandVisible = true
|
136
|
+
} else {
|
137
|
+
objDiv.scrollLeft = 0
|
138
|
+
this.expandVisible = false
|
139
|
+
}
|
140
|
+
})
|
104
141
|
}
|
105
142
|
|
106
|
-
|
107
|
-
|
108
|
-
if (
|
109
|
-
|
143
|
+
wrapBreadcrumbs () {
|
144
|
+
this.wrapBreadcrumbs_ = !this.wrapBreadcrumbs_
|
145
|
+
if (this.wrapBreadcrumbs_) {
|
146
|
+
const objDiv = this.$el.querySelector('.breadcrumbs')
|
147
|
+
objDiv.scrollLeft = 0
|
148
|
+
} else {
|
149
|
+
this.scrollBreadcrumbs()
|
110
150
|
}
|
111
|
-
return title
|
112
|
-
// return title.toUpperCase()
|
113
151
|
}
|
114
152
|
}
|
115
153
|
</script>
|
116
154
|
|
117
155
|
|
118
156
|
<style lang="scss" scoped>
|
157
|
+
.a-breadcrumbs {
|
158
|
+
overflow: hidden;
|
159
|
+
}
|
160
|
+
|
161
|
+
.breadcrumbs {
|
162
|
+
overflow: hidden;
|
163
|
+
}
|
164
|
+
|
119
165
|
.item {
|
120
166
|
white-space: nowrap;
|
121
167
|
|
@@ -131,4 +177,14 @@ export default class ABreadcrumbs extends Vue {
|
|
131
177
|
}
|
132
178
|
}
|
133
179
|
}
|
180
|
+
|
181
|
+
.expand {
|
182
|
+
cursor: pointer;
|
183
|
+
margin-top: 1px;
|
184
|
+
}
|
185
|
+
|
186
|
+
.expandDummy {
|
187
|
+
width: 1.5rem;
|
188
|
+
height: 1.5rem;
|
189
|
+
}
|
134
190
|
</style>
|
package/src/components/AIcon.vue
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
<template>
|
2
2
|
<v-icon
|
3
|
-
:class="{
|
3
|
+
:class="{button}"
|
4
4
|
v-bind="$attrs"
|
5
5
|
v-on="$listeners"
|
6
6
|
>
|
@@ -13,17 +13,14 @@
|
|
13
13
|
import { Component, Vue } from '@a-vue'
|
14
14
|
|
15
15
|
@Component({
|
16
|
-
props: [
|
16
|
+
props: [{button: false}]
|
17
17
|
})
|
18
18
|
export default class AIcon extends Vue {
|
19
|
-
get isButton () {
|
20
|
-
return this.button !== undefined
|
21
|
-
}
|
22
19
|
}
|
23
20
|
</script>
|
24
21
|
|
25
22
|
<style lang="scss" scoped>
|
26
|
-
.v-icon:not(.
|
23
|
+
.v-icon:not(.button)::after {
|
27
24
|
background: none;
|
28
25
|
}
|
29
26
|
</style>
|
package/src/components/ARow.vue
CHANGED
@@ -58,13 +58,6 @@ export default class ARow extends Vue {
|
|
58
58
|
<style scoped lang="scss">
|
59
59
|
.a-row {
|
60
60
|
display: flex;
|
61
|
-
overflow: hidden;
|
62
|
-
@media (max-width: 900px), (orientation : portrait) {
|
63
|
-
flex-wrap: wrap;
|
64
|
-
& > * {
|
65
|
-
flex: 0 0 auto;
|
66
|
-
}
|
67
|
-
}
|
68
61
|
|
69
62
|
&.full {
|
70
63
|
width: 100%;
|
@@ -11,6 +11,7 @@ import { FilterSourceType } from './FilterSourceType'
|
|
11
11
|
'listAction',
|
12
12
|
'filterHistoryKey',
|
13
13
|
'loadOnlyIfKeyword',
|
14
|
+
'checkBeforeLoad',
|
14
15
|
{
|
15
16
|
filterSource: FilterSourceType.QUERY_STRING,
|
16
17
|
events: true,
|
@@ -120,6 +121,16 @@ export class ListViewMixin extends Vue {
|
|
120
121
|
}
|
121
122
|
|
122
123
|
async load () {
|
124
|
+
if (this.checkBeforeLoad) {
|
125
|
+
const canLoad = await this.checkBeforeLoad()
|
126
|
+
if (!canLoad) {
|
127
|
+
if (this.meta_.used_filters) {
|
128
|
+
this.listViewModel.initFromUsedFilters(this.meta_.used_filters, this.meta_.count_search)
|
129
|
+
}
|
130
|
+
return
|
131
|
+
}
|
132
|
+
}
|
133
|
+
|
123
134
|
if (this._loadOnlyIfKeyword && !this.filters.q.value) {
|
124
135
|
this.models_ = []
|
125
136
|
this.meta_ = {}
|
@@ -25,10 +25,14 @@ export class ClickOutsideMixin extends Vue {
|
|
25
25
|
// popup clicked
|
26
26
|
const thisIndex = getZIndex(this.$el)
|
27
27
|
const targetIndex = getZIndex(e.target)
|
28
|
-
if (targetIndex > thisIndex) {
|
28
|
+
if (targetIndex > 10 && targetIndex > thisIndex) { // sidebar === 6
|
29
29
|
return
|
30
30
|
}
|
31
31
|
|
32
|
+
this.com_onClickOutside()
|
32
33
|
this.$emit('click:outside')
|
33
34
|
}
|
35
|
+
|
36
|
+
com_onClickOutside () {
|
37
|
+
}
|
34
38
|
}
|
@@ -73,78 +73,46 @@
|
|
73
73
|
</v-container>
|
74
74
|
</v-navigation-drawer>
|
75
75
|
|
76
|
-
<
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
v-for="n in 5"
|
85
|
-
:key="n"
|
86
|
-
link
|
87
|
-
>
|
88
|
-
<v-list-item-content>
|
89
|
-
<v-list-item-title>Item {{ n }}</v-list-item-title>
|
90
|
-
</v-list-item-content>
|
91
|
-
</v-list-item>
|
92
|
-
</v-list>
|
93
|
-
</v-navigation-drawer>
|
94
|
-
|
95
|
-
<v-app-bar
|
96
|
-
v-if="false"
|
97
|
-
app
|
98
|
-
flat
|
99
|
-
dense
|
100
|
-
color="#FAFAFA"
|
101
|
-
>
|
102
|
-
<div class="d-flex align-start mt-n2">
|
103
|
-
<v-app-bar-nav-icon
|
104
|
-
class="sidebarToggleButton mr-2 ml-n1"
|
105
|
-
@click="toggleDrawer"
|
106
|
-
/>
|
107
|
-
<a-breadcrumbs class="mt-2" />
|
108
|
-
</div>
|
109
|
-
|
110
|
-
<a-loading-indicator
|
111
|
-
fixed
|
112
|
-
top
|
113
|
-
left
|
114
|
-
class="loadingIndicator"
|
115
|
-
:isLoading="isLoading"
|
116
|
-
:color="loaderColor"
|
117
|
-
/>
|
118
|
-
</v-app-bar>
|
76
|
+
<a-loading-indicator
|
77
|
+
fixed
|
78
|
+
top
|
79
|
+
left
|
80
|
+
class="loadingIndicator"
|
81
|
+
:isLoading="isLoading"
|
82
|
+
:color="loaderColor"
|
83
|
+
/>
|
119
84
|
|
120
85
|
<v-main id="v-main">
|
121
|
-
<a-row
|
86
|
+
<a-row
|
87
|
+
start
|
88
|
+
class="topbar"
|
89
|
+
>
|
122
90
|
<v-app-bar-nav-icon
|
123
|
-
class="sidebarToggleButton mr-2 ml-
|
91
|
+
class="sidebarToggleButton mr-2 ml-4"
|
124
92
|
@click="toggleDrawer"
|
125
93
|
/>
|
94
|
+
|
126
95
|
<a-breadcrumbs />
|
127
96
|
</a-row>
|
128
97
|
|
129
98
|
<v-container
|
130
99
|
fluid
|
131
|
-
class="pa-4"
|
100
|
+
class="pa-8 pt-4"
|
132
101
|
>
|
133
|
-
<
|
134
|
-
<app-bar-title-container class="flex-grow-1" />
|
135
|
-
<app-bar-buttons class="mr-2" />
|
136
|
-
</div>
|
102
|
+
<sticky-header />
|
137
103
|
|
138
104
|
<router-view :class="{isLoading}" />
|
139
105
|
</v-container>
|
140
106
|
|
141
|
-
<sticky-footer-container
|
107
|
+
<sticky-footer-container />
|
142
108
|
</v-main>
|
143
109
|
|
144
110
|
<a-dialog id="app" />
|
145
111
|
|
146
112
|
<a-save-indicator />
|
147
113
|
|
114
|
+
<sidebar />
|
115
|
+
|
148
116
|
<flying-context-container />
|
149
117
|
</div>
|
150
118
|
</template>
|
@@ -158,6 +126,8 @@ import AppBarButtons from './app/AppBarButtons'
|
|
158
126
|
import AppBarTitleContainer from './app/AppBarTitleContainer'
|
159
127
|
import FlyingContextContainer from './FlyingContextContainer'
|
160
128
|
import StickyFooterContainer from './StickyFooterContainer'
|
129
|
+
import Sidebar from './Sidebar'
|
130
|
+
import StickyHeader from './StickyHeader'
|
161
131
|
import '../styles.scss'
|
162
132
|
|
163
133
|
@Component({
|
@@ -165,12 +135,13 @@ import '../styles.scss'
|
|
165
135
|
AppBarButtons,
|
166
136
|
AppBarTitleContainer,
|
167
137
|
FlyingContextContainer,
|
168
|
-
StickyFooterContainer
|
138
|
+
StickyFooterContainer,
|
139
|
+
Sidebar,
|
140
|
+
StickyHeader
|
169
141
|
}
|
170
142
|
})
|
171
143
|
export default class App extends Vue {
|
172
144
|
drawer = true
|
173
|
-
mainDrawer = false
|
174
145
|
isLoading = false
|
175
146
|
account = null
|
176
147
|
|
@@ -185,17 +156,6 @@ export default class App extends Vue {
|
|
185
156
|
this.$emit('appLoaded')
|
186
157
|
}
|
187
158
|
|
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
|
-
|
199
159
|
get SidebarMenu () {
|
200
160
|
return appConfig.components.SidebarMenu
|
201
161
|
}
|
@@ -228,16 +188,6 @@ export default class App extends Vue {
|
|
228
188
|
this.drawer = !this.drawer
|
229
189
|
}
|
230
190
|
|
231
|
-
@Watch('drawer')
|
232
|
-
async drawerChanged () {
|
233
|
-
if (this.drawer) {
|
234
|
-
this.mainDrawer = false
|
235
|
-
} else {
|
236
|
-
await sleep(0.1)
|
237
|
-
this.mainDrawer = true
|
238
|
-
}
|
239
|
-
}
|
240
|
-
|
241
191
|
get hasAuthService () {
|
242
192
|
return !!appConfig.authService
|
243
193
|
}
|
@@ -281,16 +231,16 @@ export default class App extends Vue {
|
|
281
231
|
top: 0;
|
282
232
|
padding: .2rem 1rem;
|
283
233
|
}
|
284
|
-
|
285
|
-
|
286
|
-
top:
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
234
|
+
|
235
|
+
.a-breadcrumbs {
|
236
|
+
margin-top: 7px;
|
237
|
+
}
|
238
|
+
|
239
|
+
.menubar {
|
240
|
+
// background: #666666 !important;
|
241
|
+
}
|
242
|
+
|
243
|
+
#sidebar {
|
244
|
+
// background: #F4F4F4 !important;
|
295
245
|
}
|
296
246
|
</style>
|
@@ -0,0 +1,66 @@
|
|
1
|
+
<template>
|
2
|
+
<v-navigation-drawer
|
3
|
+
id="sidebar"
|
4
|
+
v-model="visible"
|
5
|
+
app
|
6
|
+
right
|
7
|
+
disable-resize-watcher
|
8
|
+
width="220"
|
9
|
+
>
|
10
|
+
<div id="sidebar__children">
|
11
|
+
<div class="top" />
|
12
|
+
<div class="bottom" />
|
13
|
+
</div>
|
14
|
+
</v-navigation-drawer>
|
15
|
+
</template>
|
16
|
+
|
17
|
+
<script>
|
18
|
+
import { Component, Vue } from '@a-vue'
|
19
|
+
|
20
|
+
@Component({
|
21
|
+
props: []
|
22
|
+
})
|
23
|
+
export default class Sidebar extends Vue {
|
24
|
+
visible = false
|
25
|
+
|
26
|
+
mounted () {
|
27
|
+
this.mutationWatcher = new MutationObserver(this.domChanged)
|
28
|
+
this.mutationWatcher.observe(this.$el.querySelector('#sidebar__children > .top'), { childList: true })
|
29
|
+
this.mutationWatcher.observe(this.$el.querySelector('#sidebar__children > .bottom'), { childList: true })
|
30
|
+
|
31
|
+
this.domChanged()
|
32
|
+
}
|
33
|
+
|
34
|
+
domChanged () {
|
35
|
+
this.visible = this.hasSidebarItems()
|
36
|
+
}
|
37
|
+
|
38
|
+
getChildrenContainer () {
|
39
|
+
return this.$el.querySelector('#sidebar__children')
|
40
|
+
}
|
41
|
+
|
42
|
+
hasSidebarItems () {
|
43
|
+
return !!(this.$el.querySelector('#sidebar__children .top').children.length +
|
44
|
+
this.$el.querySelector('#sidebar__children .bottom').children.length)
|
45
|
+
}
|
46
|
+
}
|
47
|
+
</script>
|
48
|
+
|
49
|
+
|
50
|
+
<style lang="scss" scoped>
|
51
|
+
#sidebar {
|
52
|
+
&__children {
|
53
|
+
width: 100%;
|
54
|
+
}
|
55
|
+
}
|
56
|
+
|
57
|
+
#sidebar__children {
|
58
|
+
height: 100%;
|
59
|
+
padding: 2rem;
|
60
|
+
// padding-left: 4rem;
|
61
|
+
|
62
|
+
display: flex;
|
63
|
+
flex-direction: column;
|
64
|
+
justify-content: space-between;
|
65
|
+
}
|
66
|
+
</style>
|
@@ -0,0 +1,59 @@
|
|
1
|
+
<template>
|
2
|
+
<div class="sidebarItem">
|
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
|
+
props: [
|
15
|
+
{
|
16
|
+
top: true,
|
17
|
+
bottom: false
|
18
|
+
}
|
19
|
+
]
|
20
|
+
})
|
21
|
+
export default class SidebarItem extends Vue {
|
22
|
+
contextId = randomCssClass(10)
|
23
|
+
|
24
|
+
mounted () {
|
25
|
+
const container = this.getSidebarContainer()
|
26
|
+
console.log(container)
|
27
|
+
container.appendChild(this.getContent())
|
28
|
+
}
|
29
|
+
|
30
|
+
destroyed () {
|
31
|
+
const container = this.getSidebarContainer()
|
32
|
+
const el = this.getContent()
|
33
|
+
if (container.contains(el)) {
|
34
|
+
container.removeChild(el)
|
35
|
+
}
|
36
|
+
}
|
37
|
+
|
38
|
+
getContent () {
|
39
|
+
return document.querySelector('.' + this.contextId)
|
40
|
+
}
|
41
|
+
|
42
|
+
getSidebarContainer () {
|
43
|
+
console.log('toporbottom', this.$props)
|
44
|
+
return document.querySelector('#sidebar__children > .' + this.position)
|
45
|
+
}
|
46
|
+
|
47
|
+
get position () {
|
48
|
+
if (this.bottom) {
|
49
|
+
return 'bottom'
|
50
|
+
} else {
|
51
|
+
return 'top'
|
52
|
+
}
|
53
|
+
}
|
54
|
+
}
|
55
|
+
</script>
|
56
|
+
|
57
|
+
|
58
|
+
<style lang="scss" scoped>
|
59
|
+
</style>
|
@@ -0,0 +1,73 @@
|
|
1
|
+
<template>
|
2
|
+
<div
|
3
|
+
id="stickyHeader"
|
4
|
+
:class="['d-flex align-center gap-8', {visible}]"
|
5
|
+
>
|
6
|
+
<app-bar-title-container class="appBarTitle flex-grow-1" />
|
7
|
+
<app-bar-buttons class="appBarButtons mr-2" />
|
8
|
+
</div>
|
9
|
+
</template>
|
10
|
+
|
11
|
+
<script>
|
12
|
+
import { Component, Vue } from '@a-vue'
|
13
|
+
import AppBarButtons from './app/AppBarButtons'
|
14
|
+
import AppBarTitleContainer from './app/AppBarTitleContainer'
|
15
|
+
|
16
|
+
@Component({
|
17
|
+
components: {
|
18
|
+
AppBarButtons,
|
19
|
+
AppBarTitleContainer
|
20
|
+
}
|
21
|
+
})
|
22
|
+
export default class StickyHeader extends Vue {
|
23
|
+
visible = false
|
24
|
+
|
25
|
+
mounted () {
|
26
|
+
// watch mutation
|
27
|
+
this.mutationWatcher = new MutationObserver(this.domChanged)
|
28
|
+
this.mutationWatcher.observe(this.$el.querySelector('.appBarTitle'), { childList: true })
|
29
|
+
this.mutationWatcher.observe(this.$el.querySelector('.appBarButtons'), { childList: true })
|
30
|
+
|
31
|
+
// watch intersection
|
32
|
+
const el = document.querySelector('#stickyHeader')
|
33
|
+
const observer = new IntersectionObserver(
|
34
|
+
([e]) => {
|
35
|
+
e.target.classList.toggle('is-pinned', e.intersectionRatio < 1)
|
36
|
+
},
|
37
|
+
{ threshold: [1] }
|
38
|
+
)
|
39
|
+
observer.observe(el)
|
40
|
+
|
41
|
+
this.domChanged()
|
42
|
+
}
|
43
|
+
|
44
|
+
domChanged () {
|
45
|
+
this.visible = this.hasItems()
|
46
|
+
}
|
47
|
+
|
48
|
+
hasItems () {
|
49
|
+
return !!(this.$el.querySelector('.appBarTitle').children.length +
|
50
|
+
this.$el.querySelector('.appBarButtons').children.length)
|
51
|
+
}
|
52
|
+
}
|
53
|
+
</script>
|
54
|
+
|
55
|
+
|
56
|
+
<style lang="scss" scoped>
|
57
|
+
#stickyHeader {
|
58
|
+
position: sticky;
|
59
|
+
top: -1px;
|
60
|
+
margin: -1rem -2rem 2rem;
|
61
|
+
padding: 1rem 2rem;
|
62
|
+
|
63
|
+
&:not(.visible) {
|
64
|
+
display: none !important;
|
65
|
+
}
|
66
|
+
|
67
|
+
&.is-pinned {
|
68
|
+
background: white;
|
69
|
+
z-index: 2;
|
70
|
+
box-shadow: 0 4px 7px -4px #00000033;
|
71
|
+
}
|
72
|
+
}
|
73
|
+
</style>
|
@@ -1,13 +1,36 @@
|
|
1
1
|
<template>
|
2
|
-
<div class="d-flex align-center">
|
3
|
-
<v-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
2
|
+
<div class="d-flex align-center gap-4">
|
3
|
+
<v-btn
|
4
|
+
v-if="back"
|
5
|
+
fab
|
6
|
+
x-small
|
7
|
+
color="#F4F4F4"
|
8
|
+
title="Zurück"
|
9
|
+
class="mr-n2"
|
10
|
+
@click="$router.push(back)"
|
11
|
+
>
|
12
|
+
<v-icon>
|
13
|
+
$arrowLeftIcon
|
14
|
+
</v-icon>
|
15
|
+
</v-btn>
|
16
|
+
|
17
|
+
<v-avatar
|
18
|
+
color="#F4F4F4"
|
19
|
+
size="3rem"
|
20
|
+
>
|
21
|
+
<v-icon
|
22
|
+
:color="icon.color"
|
23
|
+
size="2.2rem"
|
24
|
+
v-text="icon.icon"
|
25
|
+
/>
|
26
|
+
</v-avatar>
|
27
|
+
|
28
|
+
<div class="titleContainer">
|
29
|
+
<h3 v-if="subtitle">
|
30
|
+
{{ subtitle }}
|
31
|
+
</h3>
|
32
|
+
<h2>{{ title }}</h2>
|
33
|
+
</div>
|
11
34
|
</div>
|
12
35
|
</template>
|
13
36
|
|
@@ -15,7 +38,7 @@
|
|
15
38
|
import { Component, Vue } from '@a-vue'
|
16
39
|
|
17
40
|
@Component({
|
18
|
-
props: ['icon', 'title']
|
41
|
+
props: ['back', 'icon', 'title', 'subtitle']
|
19
42
|
})
|
20
43
|
export default class appBarTitle extends Vue {
|
21
44
|
mounted () {
|
@@ -41,8 +64,29 @@ export default class appBarTitle extends Vue {
|
|
41
64
|
|
42
65
|
|
43
66
|
<style lang="scss" scoped>
|
67
|
+
.titleContainer {
|
68
|
+
overflow: hidden;
|
69
|
+
margin-top: -.2rem;
|
70
|
+
}
|
71
|
+
|
72
|
+
h3 {
|
73
|
+
font-size: .9rem;
|
74
|
+
font-weight: normal;
|
75
|
+
margin-bottom: .1rem;
|
76
|
+
line-height: 1rem;
|
77
|
+
color: #999999;
|
78
|
+
|
79
|
+
white-space: nowrap;
|
80
|
+
overflow: hidden;
|
81
|
+
text-overflow: ellipsis;
|
82
|
+
}
|
83
|
+
|
44
84
|
h2 {
|
45
|
-
|
85
|
+
font-size: 1.5rem;
|
46
86
|
line-height: 1.5rem;
|
87
|
+
|
88
|
+
white-space: nowrap;
|
89
|
+
overflow: hidden;
|
90
|
+
text-overflow: ellipsis;
|
47
91
|
}
|
48
92
|
</style>
|
@@ -11,9 +11,8 @@ export default class AppBarTitleContainer extends Vue {
|
|
11
11
|
}
|
12
12
|
</script>
|
13
13
|
|
14
|
-
|
15
14
|
<style lang="scss" scoped>
|
16
|
-
#appBarTitleContainer
|
17
|
-
|
15
|
+
#appBarTitleContainer {
|
16
|
+
min-width: 0; // https://stackoverflow.com/questions/38657688/text-overflow-ellipsis-not-working-in-a-nested-flex-container
|
18
17
|
}
|
19
18
|
</style>
|
@@ -1,17 +1,23 @@
|
|
1
1
|
<template>
|
2
2
|
<div class="detailProperty">
|
3
3
|
<div class="header">
|
4
|
-
<v-
|
4
|
+
<v-avatar
|
5
5
|
v-if="_icon"
|
6
|
-
|
7
|
-
size="
|
6
|
+
color="#EEEEEE"
|
7
|
+
size="2.5rem"
|
8
8
|
>
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
<v-icon
|
10
|
+
:color="_icon.color"
|
11
|
+
size="1.5rem"
|
12
|
+
>
|
13
|
+
{{ _icon.icon }}
|
14
|
+
</v-icon>
|
15
|
+
</v-avatar>
|
16
|
+
|
17
|
+
<label :class="['label', {'label--withIcon': !!_icon}]">{{ label }}</label>
|
12
18
|
</div>
|
13
19
|
|
14
|
-
<div :class="['content', {'content--withIcon': _icon
|
20
|
+
<div :class="['content', {'content--withIcon': !!_icon}]">
|
15
21
|
<a-row
|
16
22
|
vertical
|
17
23
|
gap="6"
|
@@ -52,29 +58,27 @@ export default class DetailProperty extends Vue {
|
|
52
58
|
flex-wrap: nowrap;
|
53
59
|
align-items: center;
|
54
60
|
height: 40px;
|
55
|
-
|
61
|
+
|
62
|
+
.v-avatar {
|
56
63
|
flex: 0 0 40px;
|
57
64
|
margin-right: 15px;
|
58
65
|
}
|
66
|
+
|
59
67
|
.label {
|
60
68
|
display: block;
|
61
69
|
text-transform: uppercase;
|
62
70
|
letter-spacing: 2px;
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
padding-left: 0;
|
67
|
-
}
|
71
|
+
padding-left: 55px;
|
72
|
+
&--withIcon {
|
73
|
+
padding-left: 0;
|
68
74
|
}
|
69
75
|
}
|
70
76
|
}
|
71
77
|
.content {
|
78
|
+
padding-left: 55px;
|
72
79
|
&--withIcon {
|
73
80
|
padding-left: 55px;
|
74
81
|
}
|
75
|
-
@media (max-width: 900px), (orientation : portrait) {
|
76
|
-
padding-left: 55px;
|
77
|
-
}
|
78
82
|
}
|
79
83
|
}
|
80
84
|
</style>
|
@@ -1,20 +1,27 @@
|
|
1
1
|
<template>
|
2
2
|
<a-row
|
3
|
-
gap="
|
3
|
+
gap="1"
|
4
4
|
v-bind="$attrs"
|
5
5
|
>
|
6
6
|
<v-btn
|
7
|
-
|
7
|
+
fab
|
8
|
+
small
|
8
9
|
:disabled="($has.reset && !changed) || !valid"
|
9
10
|
color="green white--text"
|
11
|
+
title="Speichern"
|
10
12
|
@click="$emit('save')"
|
11
13
|
>
|
12
|
-
|
14
|
+
<v-icon>
|
15
|
+
$checkIcon
|
16
|
+
</v-icon>
|
13
17
|
</v-btn>
|
14
18
|
|
15
19
|
<v-icon
|
16
20
|
v-if="$has.reset && changed"
|
17
21
|
:small="small"
|
22
|
+
:class="{disabled: !changed}"
|
23
|
+
:disabled="!changed"
|
24
|
+
color="#999999"
|
18
25
|
text
|
19
26
|
title="Formular zurücksetzen"
|
20
27
|
@click="$emit('reset')"
|
@@ -32,7 +39,7 @@ import { mdiRotateLeft} from '@mdi/js'
|
|
32
39
|
props: [
|
33
40
|
'changed',
|
34
41
|
'valid',
|
35
|
-
|
42
|
+
{small: false}
|
36
43
|
]
|
37
44
|
})
|
38
45
|
export default class EditFormButtons extends Vue {
|
@@ -41,3 +48,10 @@ export default class EditFormButtons extends Vue {
|
|
41
48
|
undoIcon = mdiRotateLeft
|
42
49
|
}
|
43
50
|
</script>
|
51
|
+
|
52
|
+
|
53
|
+
<style lang="scss" scoped>
|
54
|
+
.v-icon--disabled {
|
55
|
+
opacity: .3;
|
56
|
+
}
|
57
|
+
</style>
|
@@ -1,13 +1,22 @@
|
|
1
1
|
<template>
|
2
2
|
<div>
|
3
|
-
<
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
3
|
+
<v-hover v-slot="{ hover }">
|
4
|
+
<v-btn
|
5
|
+
:class="'removeButton-' + dialogId"
|
6
|
+
fab
|
7
|
+
small
|
8
|
+
:color="(hover ? 'red' : 'grey lighten-3')"
|
9
|
+
title="Löschen"
|
10
|
+
@click="remove"
|
11
|
+
>
|
12
|
+
<v-icon
|
13
|
+
:color="hover ? 'white' : '#999999'"
|
14
|
+
size="1.4rem"
|
15
|
+
>
|
16
|
+
$trashCanIcon
|
17
|
+
</v-icon>
|
18
|
+
</v-btn>
|
19
|
+
</v-hover>
|
11
20
|
|
12
21
|
<a-dialog
|
13
22
|
:id="dialogId"
|
@@ -19,6 +19,7 @@ import ListView from './list/ListView'
|
|
19
19
|
import ModelCount from './model/ModelCount'
|
20
20
|
import ModelIcon from './model/ModelIcon'
|
21
21
|
import EditPage from './pages/EditPage'
|
22
|
+
import SidebarItem from './SidebarItem.vue'
|
22
23
|
import Start from './Start.vue'
|
23
24
|
import StickyFooter from './StickyFooter.vue'
|
24
25
|
|
@@ -48,3 +49,4 @@ Vue.component('AppBarTitle', AppBarTitle)
|
|
48
49
|
Vue.component('Start', Start)
|
49
50
|
Vue.component('FlyingContext', FlyingContext)
|
50
51
|
Vue.component('StickyFooter', StickyFooter)
|
52
|
+
Vue.component('SidebarItem', SidebarItem)
|
@@ -12,7 +12,7 @@
|
|
12
12
|
:valid="valid"
|
13
13
|
/>
|
14
14
|
|
15
|
-
<
|
15
|
+
<app-bar-button>
|
16
16
|
<edit-form-buttons
|
17
17
|
:changed="changed"
|
18
18
|
:valid="valid"
|
@@ -20,7 +20,7 @@
|
|
20
20
|
@save="$emit('save', modelToEdit, ignoreChangesOnRouteChange)"
|
21
21
|
@reset="$refs.form.reset()"
|
22
22
|
/>
|
23
|
-
</
|
23
|
+
</app-bar-button>
|
24
24
|
</template>
|
25
25
|
</edit-form>
|
26
26
|
</template>
|
@@ -1,5 +1,6 @@
|
|
1
1
|
import {
|
2
2
|
mdiAlarmLightOutline,
|
3
|
+
mdiArrowLeft,
|
3
4
|
mdiCalendar,
|
4
5
|
mdiCheck,
|
5
6
|
mdiCheckBold,
|
@@ -12,9 +13,15 @@ import {
|
|
12
13
|
mdiLock,
|
13
14
|
mdiLogoutVariant,
|
14
15
|
mdiMagnify,
|
16
|
+
mdiMenuDown,
|
17
|
+
mdiMenuUp,
|
15
18
|
mdiPencil,
|
16
19
|
mdiPlus,
|
17
|
-
mdiThumbUpOutline
|
20
|
+
mdiThumbUpOutline,
|
21
|
+
mdiAccountGroup,
|
22
|
+
mdiShopping,
|
23
|
+
mdiMessage,
|
24
|
+
mdiPencilBoxMultiple
|
18
25
|
} from '@mdi/js'
|
19
26
|
import Vue from 'vue'
|
20
27
|
import Vuetify from 'vuetify/lib'
|
@@ -40,7 +47,14 @@ export default new Vuetify({
|
|
40
47
|
searchIcon: mdiMagnify,
|
41
48
|
lockIcon: mdiLock,
|
42
49
|
checkIcon: mdiCheck,
|
43
|
-
checkBoldIcon: mdiCheckBold
|
50
|
+
checkBoldIcon: mdiCheckBold,
|
51
|
+
arrowLeftIcon: mdiArrowLeft,
|
52
|
+
caretDownIcon: mdiMenuDown,
|
53
|
+
caretUpIcon: mdiMenuUp,
|
54
|
+
householdMembers: mdiAccountGroup,
|
55
|
+
shop: mdiShopping,
|
56
|
+
annotation: mdiMessage,
|
57
|
+
duplicates: mdiPencilBoxMultiple
|
44
58
|
}
|
45
59
|
},
|
46
60
|
breakpoint: {
|
package/src-admin/styles.scss
CHANGED
@@ -32,6 +32,26 @@
|
|
32
32
|
background-color: #E9E9E9;
|
33
33
|
}
|
34
34
|
|
35
|
+
.theme--light.v-btn.v-btn--disabled,
|
36
|
+
.theme--light.v-btn.v-btn--disabled .v-icon,
|
37
|
+
.theme--light.v-btn.v-btn--disabled .v-btn__loading {
|
38
|
+
color: white !important;
|
39
|
+
}
|
40
|
+
|
41
|
+
.theme--light.v-btn.v-btn--disabled.v-btn--has-bg {
|
42
|
+
background-color: #EEEEEE !important;
|
43
|
+
}
|
44
|
+
|
45
|
+
.v-btn {
|
46
|
+
box-shadow: none !important;
|
47
|
+
}
|
48
|
+
|
49
|
+
// @for $i from 1 through 10 {
|
50
|
+
// .gap-{$i} {
|
51
|
+
// gap: 0.25rem * $i;
|
52
|
+
// }
|
53
|
+
// }
|
54
|
+
|
35
55
|
.gap-0 {
|
36
56
|
gap: 0;
|
37
57
|
}
|