@afeefa/vue-app 0.0.64 → 0.0.67

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.
Files changed (36) hide show
  1. package/.afeefa/package/release/version.txt +1 -1
  2. package/package.json +1 -1
  3. package/src/components/ABreadcrumbs.vue +75 -18
  4. package/src/components/ADatePicker.vue +1 -1
  5. package/src/components/AIcon.vue +3 -6
  6. package/src/components/AIconButton.vue +1 -2
  7. package/src/components/ARichTextArea.vue +95 -85
  8. package/src/components/ARow.vue +0 -7
  9. package/src/components/form/EditForm.vue +9 -0
  10. package/src/components/form/EditModal.vue +2 -0
  11. package/src/components/form/FormFieldMixin.js +13 -4
  12. package/src/components/form/fields/FormFieldRichTextArea.vue +5 -3
  13. package/src/components/list/ListViewMixin.js +25 -2
  14. package/src/components/mixins/ClickOutsideMixin.js +5 -1
  15. package/src/components/search-select/SearchSelectList.vue +0 -1
  16. package/src/components/vue/Component.js +9 -2
  17. package/src/events.js +1 -0
  18. package/src-admin/components/App.vue +56 -61
  19. package/src-admin/components/Sidebar.vue +66 -0
  20. package/src-admin/components/SidebarItem.vue +59 -0
  21. package/src-admin/components/StickyFooter.vue +42 -0
  22. package/src-admin/components/StickyFooterContainer.vue +66 -0
  23. package/src-admin/components/StickyHeader.vue +73 -0
  24. package/src-admin/components/app/AppBarButtons.vue +0 -7
  25. package/src-admin/components/app/AppBarTitle.vue +55 -11
  26. package/src-admin/components/app/AppBarTitleContainer.vue +2 -3
  27. package/src-admin/components/controls/SearchSelectFormField.vue +1 -0
  28. package/src-admin/components/detail/DetailProperty.vue +20 -16
  29. package/src-admin/components/form/EditFormButtons.vue +21 -4
  30. package/src-admin/components/form/RemoveButton.vue +17 -8
  31. package/src-admin/components/index.js +4 -0
  32. package/src-admin/components/list/ListView.vue +5 -6
  33. package/src-admin/components/pages/EditPage.vue +11 -7
  34. package/src-admin/components/routes/DataRouteMixin.js +1 -1
  35. package/src-admin/config/vuetify.js +22 -2
  36. package/src-admin/styles.scss +21 -4
@@ -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
@@ -72,68 +73,47 @@
72
73
  </v-container>
73
74
  </v-navigation-drawer>
74
75
 
75
- <v-navigation-drawer
76
- v-if="false"
77
- app
78
- clipped
79
- right
80
- >
81
- <v-list>
82
- <v-list-item
83
- v-for="n in 5"
84
- :key="n"
85
- link
86
- >
87
- <v-list-item-content>
88
- <v-list-item-title>Item {{ n }}</v-list-item-title>
89
- </v-list-item-content>
90
- </v-list-item>
91
- </v-list>
92
- </v-navigation-drawer>
93
-
94
- <flying-context-container />
95
-
96
- <v-app-bar
97
- app
98
- flat
99
- dense
100
- color="#FAFAFA"
101
- >
102
- <div class="d-flex align-start mt-n2">
76
+ <a-loading-indicator
77
+ fixed
78
+ top
79
+ left
80
+ class="loadingIndicator"
81
+ :isLoading="isLoading"
82
+ :color="loaderColor"
83
+ />
84
+
85
+ <v-main id="v-main">
86
+ <a-row
87
+ start
88
+ class="topbar"
89
+ >
103
90
  <v-app-bar-nav-icon
104
- class="sidebarToggleButton mr-2 ml-n1"
91
+ class="sidebarToggleButton mr-2 ml-4"
105
92
  @click="toggleDrawer"
106
93
  />
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>
119
-
120
- <v-main>
94
+
95
+ <a-breadcrumbs />
96
+ </a-row>
97
+
121
98
  <v-container
122
99
  fluid
123
- class="pa-4"
100
+ class="pa-8 pt-4"
124
101
  >
125
- <div class="d-flex align-center">
126
- <app-bar-title-container class="flex-grow-1 mb-4" />
127
- <app-bar-buttons class="mr-2 mb-4" />
128
- </div>
102
+ <sticky-header />
129
103
 
130
104
  <router-view :class="{isLoading}" />
131
105
  </v-container>
106
+
107
+ <sticky-footer-container />
132
108
  </v-main>
133
109
 
134
110
  <a-dialog id="app" />
135
111
 
136
112
  <a-save-indicator />
113
+
114
+ <sidebar />
115
+
116
+ <flying-context-container />
137
117
  </div>
138
118
  </template>
139
119
 
@@ -145,18 +125,23 @@ import { sleep } from '@a-vue/utils/timeout'
145
125
  import AppBarButtons from './app/AppBarButtons'
146
126
  import AppBarTitleContainer from './app/AppBarTitleContainer'
147
127
  import FlyingContextContainer from './FlyingContextContainer'
128
+ import StickyFooterContainer from './StickyFooterContainer'
129
+ import Sidebar from './Sidebar'
130
+ import StickyHeader from './StickyHeader'
148
131
  import '../styles.scss'
149
132
 
150
133
  @Component({
151
134
  components: {
152
135
  AppBarButtons,
153
136
  AppBarTitleContainer,
154
- FlyingContextContainer
137
+ FlyingContextContainer,
138
+ StickyFooterContainer,
139
+ Sidebar,
140
+ StickyHeader
155
141
  }
156
142
  })
157
143
  export default class App extends Vue {
158
144
  drawer = true
159
- mainDrawer = false
160
145
  isLoading = false
161
146
  account = null
162
147
 
@@ -203,16 +188,6 @@ export default class App extends Vue {
203
188
  this.drawer = !this.drawer
204
189
  }
205
190
 
206
- @Watch('drawer')
207
- async drawerChanged () {
208
- if (this.drawer) {
209
- this.mainDrawer = false
210
- } else {
211
- await sleep(0.1)
212
- this.mainDrawer = true
213
- }
214
- }
215
-
216
191
  get hasAuthService () {
217
192
  return !!appConfig.authService
218
193
  }
@@ -248,4 +223,24 @@ export default class App extends Vue {
248
223
  height: 36px !important;
249
224
  margin-top: 1px;
250
225
  }
226
+
227
+ .topbar {
228
+ position: relative;
229
+ width: 100%;
230
+ left: 0;
231
+ top: 0;
232
+ padding: .2rem 1rem;
233
+ }
234
+
235
+ .a-breadcrumbs {
236
+ margin-top: 7px;
237
+ }
238
+
239
+ .menubar {
240
+ // background: #666666 !important;
241
+ }
242
+
243
+ #sidebar {
244
+ // background: #F4F4F4 !important;
245
+ }
251
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,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>
@@ -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>
@@ -12,10 +12,3 @@ import { Component, Vue } from '@a-vue'
12
12
  export default class AppBarButtons extends Vue {
13
13
  }
14
14
  </script>
15
-
16
-
17
- <style lang="scss" scoped>
18
- #appBarButtons:empty {
19
- display: none !important;
20
- }
21
- </style>
@@ -1,13 +1,36 @@
1
1
  <template>
2
- <div class="d-flex align-center">
3
- <v-icon
4
- class="mr-2"
5
- :color="icon.color"
6
- size="1.8rem"
7
- v-text="icon.icon"
8
- />
9
-
10
- <h2>{{ title }}</h2>
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
- // white-space: nowrap;
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:empty {
17
- display: none;
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>
@@ -68,6 +68,7 @@
68
68
  >
69
69
  <template #activator>
70
70
  <a-icon-button
71
+ small
71
72
  icon="$plusIcon"
72
73
  :text="selectableConfig.addButtonTitle || 'Hinzufügen'"
73
74
  class="mt-4"
@@ -1,17 +1,23 @@
1
1
  <template>
2
2
  <div class="detailProperty">
3
3
  <div class="header">
4
- <v-icon
4
+ <v-avatar
5
5
  v-if="_icon"
6
- :color="_icon.color"
7
- size="2rem"
6
+ color="#EEEEEE"
7
+ size="2.5rem"
8
8
  >
9
- {{ _icon.icon }}
10
- </v-icon>
11
- <label :class="['label', {'label--withIcon': _icon != null}]">{{ label }}</label>
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 != null}]">
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
- .v-icon {
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
- @media (max-width: 900px), (orientation : portrait) {
64
- padding-left: 55px;
65
- &--withIcon {
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>