@afeefa/vue-app 0.0.63 → 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.
Files changed (51) 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/AModal.vue +21 -3
  8. package/src/components/ARichTextArea.vue +95 -85
  9. package/src/components/ARow.vue +0 -7
  10. package/src/components/ATableRow.vue +4 -0
  11. package/src/components/flying-context/FlyingContextEvent.js +5 -0
  12. package/src/components/form/EditForm.vue +13 -3
  13. package/src/components/form/EditModal.vue +52 -35
  14. package/src/components/form/fields/FormFieldRichTextArea.vue +5 -3
  15. package/src/components/list/ListViewMixin.js +25 -2
  16. package/src/components/mixins/ClickOutsideMixin.js +5 -1
  17. package/src/components/search-select/SearchSelectList.vue +0 -1
  18. package/src/components/vue/Component.js +9 -2
  19. package/src/events.js +2 -0
  20. package/src-admin/bootstrap.js +1 -0
  21. package/src-admin/components/App.vue +58 -59
  22. package/src-admin/components/FlyingContext.vue +77 -0
  23. package/src-admin/components/FlyingContextContainer.vue +85 -0
  24. package/src-admin/components/Sidebar.vue +66 -0
  25. package/src-admin/components/SidebarItem.vue +59 -0
  26. package/src-admin/components/StickyFooter.vue +42 -0
  27. package/src-admin/components/StickyFooterContainer.vue +66 -0
  28. package/src-admin/components/StickyHeader.vue +73 -0
  29. package/src-admin/components/app/AppBarButtons.vue +0 -7
  30. package/src-admin/components/app/AppBarTitle.vue +55 -11
  31. package/src-admin/components/app/AppBarTitleContainer.vue +2 -3
  32. package/src-admin/components/controls/SearchSelectFormField.vue +1 -0
  33. package/src-admin/components/detail/DetailProperty.vue +20 -16
  34. package/src-admin/components/form/EditFormButtons.vue +25 -6
  35. package/src-admin/components/form/RemoveButton.vue +17 -8
  36. package/src-admin/components/index.js +6 -7
  37. package/src-admin/components/list/ListView.vue +22 -9
  38. package/src-admin/components/pages/EditPage.vue +17 -8
  39. package/src-admin/components/routes/DataRouteMixin.js +24 -15
  40. package/src-admin/config/routing.js +0 -11
  41. package/src-admin/config/vuetify.js +22 -2
  42. package/src-admin/directives/index.js +26 -0
  43. package/src-admin/styles.scss +21 -4
  44. package/src-admin/components/pages/CreatePage.vue +0 -114
  45. package/src-admin/components/pages/DetailPage.vue +0 -143
  46. package/src-admin/components/pages/EditPageMixin.js +0 -96
  47. package/src-admin/components/pages/ListPage.vue +0 -55
  48. package/src-admin/components/routes/CreateRoute.vue +0 -15
  49. package/src-admin/components/routes/DetailRoute.vue +0 -85
  50. package/src-admin/components/routes/EditRoute.vue +0 -78
  51. package/src-admin/components/routes/ListRoute.vue +0 -110
@@ -1,6 +1,7 @@
1
1
  <template>
2
2
  <a-modal
3
3
  :title="title"
4
+ :beforeClose="beforeClose"
4
5
  :show.sync="show_"
5
6
  v-bind="$attrs"
6
7
  >
@@ -10,16 +11,18 @@
10
11
 
11
12
  <edit-form
12
13
  v-if="show_"
14
+ ref="form"
13
15
  :model="model"
16
+ :createModelToEdit="createModelToEdit"
14
17
  >
15
- <template #fields>
18
+ <template #form="{modelToEdit, changed, valid}">
16
19
  <slot
17
- name="fields"
18
- :model="model"
20
+ name="form"
21
+ :modelToEdit="modelToEdit"
22
+ :changed="changed"
23
+ :valid="valid"
19
24
  />
20
- </template>
21
25
 
22
- <template #default="{changed, valid}">
23
26
  <a-row
24
27
  class="mt-8 mb-1 pb-1 gap-4"
25
28
  right
@@ -31,25 +34,14 @@
31
34
  Schließen
32
35
  </v-btn>
33
36
 
34
- <a-row gap="2">
35
- <v-btn
36
- small
37
- :disabled="!changed || !valid"
38
- color="green white--text"
39
- @click="save"
40
- >
41
- Speichern
42
- </v-btn>
43
-
44
- <v-icon
45
- v-if="changed"
46
- small
47
- text
48
- @click="reset"
49
- >
50
- {{ undoIcon }}
51
- </v-icon>
52
- </a-row>
37
+ <edit-form-buttons
38
+ :changed="changed"
39
+ :valid="valid"
40
+ small
41
+ :has="{reset: !!modelToEdit.id}"
42
+ @save="$emit('save', modelToEdit, ignoreChangesOnClose)"
43
+ @reset="$refs.form.reset()"
44
+ />
53
45
  </a-row>
54
46
  </template>
55
47
  </edit-form>
@@ -59,21 +51,26 @@
59
51
 
60
52
  <script>
61
53
  import { Component, Vue, Watch } from '@a-vue'
62
- import { mdiRotateLeft} from '@mdi/js'
54
+ import { DialogEvent } from '@a-vue/events'
63
55
 
64
56
  @Component({
65
- props: ['model', 'title', 'show']
57
+ props: ['model', 'createModelToEdit', 'title', 'show']
66
58
  })
67
59
  export default class EditModal extends Vue {
68
60
  show_ = false
61
+ ignoreChangesOnClose_ = false
69
62
 
70
- undoIcon = mdiRotateLeft
63
+ created () {
64
+ if (this.show) { // open on create with v-show
65
+ this.open()
66
+ }
67
+ }
71
68
 
72
69
  /**
73
70
  * visiblility changes from outside
74
71
  * this will trigger the show_ watcher,
75
72
  * forward the change to the modal and
76
- * later emit a open/close event
73
+ * later emit an open/close event
77
74
  */
78
75
  @Watch('show')
79
76
  showChanged () {
@@ -90,7 +87,6 @@ export default class EditModal extends Vue {
90
87
  @Watch('show_')
91
88
  show_Changed () {
92
89
  if (this.show_) {
93
- this.reset()
94
90
  this.$emit('open')
95
91
  } else {
96
92
  this.$emit('close')
@@ -101,16 +97,37 @@ export default class EditModal extends Vue {
101
97
  this.show_ = true
102
98
  }
103
99
 
104
- close () {
105
- this.show_ = false
100
+ async beforeClose () {
101
+ // run only if show_ is true to prevent double checks with a-modal
102
+ if (this.show_ && this.$refs.form.changed && !this.ignoreChangesOnClose_) {
103
+ const result = await this.$events.dispatch(new DialogEvent(DialogEvent.SHOW, {
104
+ title: 'Änderungen verwerfen?',
105
+ message: 'Im Formular sind nicht gespeicherte Änderungen. Sollen diese verworfen werden?',
106
+ yesButton: 'Verwerfen'
107
+ }))
108
+ if (result !== DialogEvent.RESULT_YES) {
109
+ return false
110
+ }
111
+ }
112
+ return true
106
113
  }
107
114
 
108
- reset () {
109
- this.$emit('reset')
115
+ async close () {
116
+ const result = await this.beforeClose()
117
+ if (!result) {
118
+ return
119
+ }
120
+
121
+ this.show_ = false
110
122
  }
111
123
 
112
- save () {
113
- this.$emit('save')
124
+ /**
125
+ * hook to allow to leave a just created (saved) model
126
+ */
127
+ ignoreChangesOnClose () {
128
+ // this.$refs.form.forceUnchanged()
129
+ console.info('TODO switch form to forceUnchanged')
130
+ this.ignoreChangesOnClose_ = true
114
131
  }
115
132
  }
116
133
  </script>
@@ -1,7 +1,9 @@
1
1
  <template>
2
- <a-rich-text-area
3
- v-model="model[name]"
4
- />
2
+ <a-rich-text-area v-model="model[name]">
3
+ <template #buttons>
4
+ <slot name="buttons" />
5
+ </template>
6
+ </a-rich-text-area>
5
7
  </template>
6
8
 
7
9
  <script>
@@ -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,
@@ -64,9 +65,16 @@ export class ListViewMixin extends Vue {
64
65
  .on('change', this.filtersChanged) // listen to change
65
66
 
66
67
  this._filtersInitialized()
68
+ this.$emit('update:filters', this.filters)
69
+ this.$emit('update:listViewModel', this.listViewModel)
67
70
 
68
71
  if (this.models) {
69
72
  this.$emit('update:count', this.meta_.count_search)
73
+
74
+ this.$emit('onLoad', {
75
+ models: this.models_,
76
+ meta: this.meta_
77
+ })
70
78
  } else {
71
79
  this.load()
72
80
  }
@@ -113,6 +121,16 @@ export class ListViewMixin extends Vue {
113
121
  }
114
122
 
115
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
+
116
134
  if (this._loadOnlyIfKeyword && !this.filters.q.value) {
117
135
  this.models_ = []
118
136
  this.meta_ = {}
@@ -132,7 +150,7 @@ export class ListViewMixin extends Vue {
132
150
 
133
151
  if (!models) { // error happened
134
152
  this.isLoading = false
135
- this.$emit('update:isLoading', this.isLoading)
153
+ this.$emit('update:isLoading', false)
136
154
  return
137
155
  }
138
156
 
@@ -144,8 +162,13 @@ export class ListViewMixin extends Vue {
144
162
  }
145
163
 
146
164
  this.isLoading = false
147
- this.$emit('update:isLoading', this.isLoading)
165
+ this.$emit('update:isLoading', false)
148
166
 
149
167
  this.$emit('update:count', this.meta_.count_search)
168
+
169
+ this.$emit('onLoad', {
170
+ models,
171
+ meta
172
+ })
150
173
  }
151
174
  }
@@ -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
  }
@@ -67,7 +67,6 @@ export default class SearchSelectList extends Mixins(ListViewMixin) {
67
67
  if (this.q) {
68
68
  this.filters.q.value = this.q
69
69
  }
70
- this.$emit('update:filters', this.filters)
71
70
  }
72
71
  }
73
72
  </script>
@@ -32,9 +32,16 @@ function propsWithDefaults (props) {
32
32
  // property: { some object }, should be a normal vue props object
33
33
  } else if (value && typeof value === 'object' && value.constructor === Object) {
34
34
  normalizedProps[subProp] = value
35
- // property: true, null, ...
35
+ // property: true, false, null, ...
36
36
  } else {
37
- normalizedProps[subProp] = { default: value }
37
+ if (typeof value === 'boolean') {
38
+ normalizedProps[subProp] = {
39
+ type: Boolean,
40
+ default: value
41
+ }
42
+ } else {
43
+ normalizedProps[subProp] = { default: value }
44
+ }
38
45
  }
39
46
  })
40
47
  } else {
package/src/events.js CHANGED
@@ -1,4 +1,6 @@
1
+ export { BaseEvent } from './plugins/event-bus/BaseEvent'
1
2
  export { LoadingEvent } from './components/loading-indicator/LoadingEvent'
2
3
  export { SaveEvent } from './components/save-indicator/SaveEvent'
3
4
  export { AlertEvent } from './components/alert/AlertEvent'
4
5
  export { DialogEvent } from './components/dialog/DialogEvent'
6
+ export { FlyingContextEvent } from './components/flying-context/FlyingContextEvent'
@@ -1,5 +1,6 @@
1
1
  import './config/event-bus'
2
2
  import './config/components'
3
+ import './directives'
3
4
 
4
5
  import { translationPlugin } from '@a-admin/plugins/translation/TranslationPlugin'
5
6
  import { apiResourcesPlugin } from '@a-vue/plugins/api-resources/ApiResourcesPlugin'
@@ -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,66 +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
- <v-app-bar
95
- app
96
- flat
97
- dense
98
- color="#FAFAFA"
99
- >
100
- <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
+ >
101
90
  <v-app-bar-nav-icon
102
- class="sidebarToggleButton mr-2 ml-n1"
91
+ class="sidebarToggleButton mr-2 ml-4"
103
92
  @click="toggleDrawer"
104
93
  />
105
- <a-breadcrumbs class="mt-2" />
106
- </div>
107
-
108
- <a-loading-indicator
109
- fixed
110
- top
111
- left
112
- class="loadingIndicator"
113
- :isLoading="isLoading"
114
- :color="loaderColor"
115
- />
116
- </v-app-bar>
117
-
118
- <v-main>
94
+
95
+ <a-breadcrumbs />
96
+ </a-row>
97
+
119
98
  <v-container
120
99
  fluid
121
- class="pa-4"
100
+ class="pa-8 pt-4"
122
101
  >
123
- <div class="d-flex align-center">
124
- <app-bar-title-container class="flex-grow-1 mb-4" />
125
- <app-bar-buttons class="mr-2 mb-4" />
126
- </div>
102
+ <sticky-header />
127
103
 
128
104
  <router-view :class="{isLoading}" />
129
105
  </v-container>
106
+
107
+ <sticky-footer-container />
130
108
  </v-main>
131
109
 
132
110
  <a-dialog id="app" />
133
111
 
134
112
  <a-save-indicator />
113
+
114
+ <sidebar />
115
+
116
+ <flying-context-container />
135
117
  </div>
136
118
  </template>
137
119
 
@@ -142,17 +124,24 @@ import { appConfig } from '@a-admin/config/AppConfig'
142
124
  import { sleep } from '@a-vue/utils/timeout'
143
125
  import AppBarButtons from './app/AppBarButtons'
144
126
  import AppBarTitleContainer from './app/AppBarTitleContainer'
127
+ import FlyingContextContainer from './FlyingContextContainer'
128
+ import StickyFooterContainer from './StickyFooterContainer'
129
+ import Sidebar from './Sidebar'
130
+ import StickyHeader from './StickyHeader'
145
131
  import '../styles.scss'
146
132
 
147
133
  @Component({
148
134
  components: {
149
135
  AppBarButtons,
150
- AppBarTitleContainer
136
+ AppBarTitleContainer,
137
+ FlyingContextContainer,
138
+ StickyFooterContainer,
139
+ Sidebar,
140
+ StickyHeader
151
141
  }
152
142
  })
153
143
  export default class App extends Vue {
154
144
  drawer = true
155
- mainDrawer = false
156
145
  isLoading = false
157
146
  account = null
158
147
 
@@ -199,16 +188,6 @@ export default class App extends Vue {
199
188
  this.drawer = !this.drawer
200
189
  }
201
190
 
202
- @Watch('drawer')
203
- async drawerChanged () {
204
- if (this.drawer) {
205
- this.mainDrawer = false
206
- } else {
207
- await sleep(0.1)
208
- this.mainDrawer = true
209
- }
210
- }
211
-
212
191
  get hasAuthService () {
213
192
  return !!appConfig.authService
214
193
  }
@@ -244,4 +223,24 @@ export default class App extends Vue {
244
223
  height: 36px !important;
245
224
  margin-top: 1px;
246
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
+ }
247
246
  </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,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>