@afeefa/vue-app 0.0.173 → 0.0.175

Sign up to get free protection for your applications and to get access to all the features.
Files changed (29) hide show
  1. package/.afeefa/package/release/version.txt +1 -1
  2. package/package.json +1 -1
  3. package/src/components/AInfo.vue +15 -1
  4. package/src/components/ALoadingIndicator.vue +4 -0
  5. package/src/components/ATable.vue +6 -0
  6. package/src/components/ATableRow.vue +5 -0
  7. package/src/plugins/route-config/RouteConfigPlugin.js +17 -0
  8. package/src/utils/format-time.js +17 -0
  9. package/src-admin/components/App.vue +48 -186
  10. package/src-admin/components/CollapsibleSection.vue +64 -0
  11. package/src-admin/components/DetailOrInfo.vue +33 -0
  12. package/src-admin/components/FlyingContextContainer.vue +21 -1
  13. package/src-admin/components/HSeparator.vue +90 -0
  14. package/src-admin/components/InformationBar.vue +176 -0
  15. package/src-admin/components/NavigationBar.vue +187 -0
  16. package/src-admin/components/StickyHeader.vue +40 -8
  17. package/src-admin/components/app/AppBarTitle.vue +6 -11
  18. package/src-admin/components/detail/DetailProperty.vue +12 -17
  19. package/src-admin/components/index.js +14 -5
  20. package/src-admin/components/list/ListView.vue +1 -1
  21. package/src-admin/components/sidebar/InformationBarItem.vue +209 -0
  22. package/src-admin/components/sidebar/SidebarEvent.js +5 -0
  23. package/src-admin/components/sidebar/SidebarService.js +107 -0
  24. package/src-admin/components/transitions/CollapseTransition.vue +61 -0
  25. package/src-admin/config/vuetify.js +7 -1
  26. package/src-admin/events.js +1 -0
  27. package/src-admin/styles.scss +10 -2
  28. package/src-admin/components/Sidebar.vue +0 -66
  29. package/src-admin/components/SidebarItem.vue +0 -59
@@ -0,0 +1,209 @@
1
+ <template>
2
+ <div class="informationBarItem">
3
+ <div
4
+ :class="['item', infoItemId, {expanded, rail}]"
5
+ :style="{width}"
6
+ >
7
+ <a-row justify-space-between>
8
+ <div
9
+ class="header"
10
+ @click="derail"
11
+ >
12
+ <v-icon
13
+ v-if="_icon"
14
+ :color="_icon.color"
15
+ size="2rem"
16
+ >
17
+ {{ _icon.icon }}
18
+ </v-icon>
19
+
20
+ <div
21
+ v-else
22
+ class="iconPlaceholder"
23
+ />
24
+
25
+ <template v-if="!rail">
26
+ <label class="label">{{ label }}</label>
27
+
28
+ <v-icon class="contextButton mt-n1">
29
+ {{ expanded ? '$caretDownIcon' : '$caretRightIcon' }}
30
+ </v-icon>
31
+ </template>
32
+ </div>
33
+
34
+ <div v-if="expanded">
35
+ <slot name="actionButton" />
36
+ </div>
37
+ </a-row>
38
+
39
+ <collapse-transition>
40
+ <div
41
+ v-if="!rail && expanded"
42
+ class="content"
43
+ >
44
+ <a-row
45
+ vertical
46
+ gap="6"
47
+ >
48
+ <slot />
49
+ </a-row>
50
+ </div>
51
+ </collapse-transition>
52
+ </div>
53
+ </div>
54
+ </template>
55
+
56
+ <script>
57
+ import { Component, Vue } from '@a-vue'
58
+ import { randomCssClass } from '@a-vue/utils/random'
59
+ import { sidebarService } from './SidebarService'
60
+ import { SidebarEvent } from '@a-admin/events'
61
+
62
+ @Component({
63
+ props: [
64
+ 'icon',
65
+ 'iconModelType',
66
+ 'label',
67
+ {
68
+ top: true,
69
+ bottom: false,
70
+ width: 'auto',
71
+ open: false
72
+ }
73
+ ]
74
+ })
75
+ export default class InformationBarItem extends Vue {
76
+ infoItemId = randomCssClass(10)
77
+ rail = sidebarService.informationRailed
78
+ expanded = !this.rail && this.open
79
+ isCreated = false
80
+
81
+ created () {
82
+ this.$events.on(SidebarEvent.STATUS, this.railChanged)
83
+ }
84
+
85
+ mounted () {
86
+ const container = this.getSidebarContainer()
87
+ container.appendChild(this.getContent())
88
+ }
89
+
90
+ destroyed () {
91
+ const container = this.getSidebarContainer()
92
+ const el = this.getContent()
93
+ if (container.contains(el)) {
94
+ container.removeChild(el)
95
+ }
96
+
97
+ this.$events.off(SidebarEvent.STATUS, this.railChanged)
98
+ }
99
+
100
+ get _icon () {
101
+ if (this.icon) {
102
+ return this.icon
103
+ }
104
+
105
+ if (this.iconModelType) {
106
+ const ModelClass = this.$apiResources.getModelClass(this.iconModelType)
107
+ return ModelClass.icon
108
+ }
109
+ }
110
+
111
+ railChanged ({payload: {informationRailed}}) {
112
+ if (this.rail === informationRailed) {
113
+ return
114
+ }
115
+
116
+ this.rail = informationRailed
117
+
118
+ if (!this.rail) { // derailed
119
+ if (!this.isCreated) { // open only the first time not after subsequent derailing
120
+ this.expanded = this.open
121
+ }
122
+ }
123
+
124
+ this.isCreated = true
125
+ }
126
+
127
+ derail () {
128
+ const railed = this.rail
129
+
130
+ sidebarService.setRailInformation(false)
131
+
132
+ if (railed) {
133
+ this.expanded = true
134
+ } else {
135
+ this.expanded = !this.expanded
136
+ }
137
+ }
138
+
139
+ getContent () {
140
+ return document.querySelector('.' + this.infoItemId)
141
+ }
142
+
143
+ getSidebarContainer () {
144
+ return document.querySelector('#information-bar__children > .' + this.position)
145
+ }
146
+
147
+ get position () {
148
+ if (this.bottom) {
149
+ return 'bottom'
150
+ } else {
151
+ return 'top'
152
+ }
153
+ }
154
+ }
155
+ </script>
156
+
157
+
158
+ <style lang="scss" scoped>
159
+ .item {
160
+ min-width: 100%;
161
+ margin-bottom: 1rem;
162
+
163
+ &:last-child {
164
+ margin-bottom: 0;
165
+ }
166
+
167
+ &.rail {
168
+ margin-bottom: .5rem;
169
+ }
170
+ }
171
+
172
+ .header {
173
+ display: flex;
174
+ flex-wrap: nowrap;
175
+ align-items: center;
176
+ font-size: .8rem;
177
+ cursor: pointer;
178
+
179
+ > .v-icon {
180
+ margin-right: .75rem;
181
+ }
182
+
183
+ .label {
184
+ display: block;
185
+ text-transform: uppercase;
186
+ letter-spacing: 3px;
187
+ color: #666666;
188
+ cursor: pointer;
189
+ }
190
+
191
+ &:hover {
192
+ label {
193
+ color: #333333;
194
+ }
195
+
196
+ .v-icon.contextButton {
197
+ color: #333333 !important;
198
+ }
199
+ }
200
+ }
201
+
202
+ .content {
203
+ border-top: 2px solid #EEEEEE;
204
+ font-size: .9rem;
205
+
206
+ margin: .25rem -1.5rem;
207
+ padding: 1rem 1.5rem;
208
+ }
209
+ </style>
@@ -0,0 +1,5 @@
1
+ import { BaseEvent } from '@a-vue/plugins/event-bus/BaseEvent'
2
+
3
+ export class SidebarEvent extends BaseEvent {
4
+ static STATUS = 'SidebarEvent:status'
5
+ }
@@ -0,0 +1,107 @@
1
+ import { eventBus } from '@a-vue/plugins/event-bus/EventBus'
2
+
3
+ import { SidebarEvent } from './SidebarEvent'
4
+
5
+ export class SidebarState {
6
+ navigation = false
7
+ informationDerailed = false
8
+ informationRailed = false
9
+ mobile = false
10
+
11
+ constructor (service) {
12
+ this.navigation = service.navigation
13
+ this.information = service.information
14
+ this.informationRailed = service.informationRailed
15
+ this.mobile = service.mobile
16
+ this.hasFloatingOverlay = service.hasFloatingOverlay
17
+ }
18
+ }
19
+
20
+ class SidebarService {
21
+ navigation = true
22
+ information = false
23
+ informationRailed = false
24
+ mobile = false
25
+
26
+ constructor () {
27
+ window.addEventListener('resize', () => this.checkIsMobile())
28
+
29
+ this.checkIsMobile()
30
+ }
31
+
32
+ checkIsMobile () {
33
+ const old = this.mobile
34
+
35
+ const width = window.innerWidth
36
+ this.mobile = width < 1200
37
+
38
+ if (old !== this.mobile) {
39
+ if (!this.mobile) { // expand if destop
40
+ this.navigation = true
41
+ }
42
+
43
+ if (this.information) { // rail if mobile, derail if desktop
44
+ this.informationRailed = this.mobile
45
+ }
46
+
47
+ eventBus.dispatch(new SidebarEvent(SidebarEvent.STATUS, new SidebarState(this)))
48
+ }
49
+ }
50
+
51
+ setNavigation (navigation) {
52
+ if (this.navigation === navigation) {
53
+ return
54
+ }
55
+
56
+ this.navigation = navigation
57
+
58
+ eventBus.dispatch(new SidebarEvent(SidebarEvent.STATUS, new SidebarState(this)))
59
+ }
60
+
61
+ setInformation (information) {
62
+ if (this.information === information) {
63
+ return
64
+ }
65
+
66
+ this.information = information
67
+
68
+ if (information) { // rail if mobile, derail if desktop
69
+ this.informationRailed = this.mobile
70
+ }
71
+
72
+ eventBus.dispatch(new SidebarEvent(SidebarEvent.STATUS, new SidebarState(this)))
73
+ }
74
+
75
+ setRailInformation (rail) {
76
+ if (this.informationRailed === rail) {
77
+ return
78
+ }
79
+
80
+ this.informationRailed = rail
81
+
82
+ eventBus.dispatch(new SidebarEvent(SidebarEvent.STATUS, new SidebarState(this)))
83
+ }
84
+
85
+ closeAllFloating () {
86
+ this.setNavigation(false)
87
+ this.setRailInformation(true)
88
+ }
89
+
90
+ get hasFloatingOverlay () {
91
+ if (!this.mobile) {
92
+ return false
93
+ }
94
+
95
+ if (this.navigation) {
96
+ return true
97
+ }
98
+
99
+ if (this.information) {
100
+ return !this.informationRailed
101
+ }
102
+
103
+ return false
104
+ }
105
+ }
106
+
107
+ export const sidebarService = new SidebarService()
@@ -0,0 +1,61 @@
1
+ <template>
2
+ <transition
3
+ name="collapse-transition"
4
+ @enter="enter"
5
+ @after-enter="afterEnter"
6
+ @leave="leave"
7
+ @after-leave="afterLeave"
8
+ >
9
+ <slot />
10
+ </transition>
11
+ </template>
12
+
13
+ <script>
14
+ // concepts by https://github.com/ivanvermeyen/vue-collapse-transition
15
+ import { Component, Vue } from '@a-vue'
16
+
17
+ @Component({
18
+ props: [{ duration: 150, min: 0 }]
19
+ })
20
+ export default class CollapseTransition extends Vue {
21
+ enter (el, done) {
22
+ const h = el.offsetHeight
23
+
24
+ el.style.height = this.min + 'px'
25
+ el.style.overflow = 'hidden'
26
+
27
+ getComputedStyle(el).height // force repaint
28
+
29
+ el.style.transition = 'height ' + this.duration + 'ms'
30
+ el.style.height = h + 'px'
31
+
32
+ setTimeout(done, this.duration)
33
+ }
34
+
35
+ afterEnter (el) {
36
+ el.style.transition = ''
37
+ el.style.overflow = ''
38
+ el.style.height = ''
39
+ }
40
+
41
+ leave (el, done) {
42
+ const h = el.offsetHeight
43
+
44
+ el.style.height = h + 'px'
45
+ el.style.overflow = 'hidden'
46
+
47
+ getComputedStyle(el).height // force repaint
48
+
49
+ el.style.transition = 'height ' + this.duration + 'ms'
50
+ el.style.height = this.min + 'px'
51
+
52
+ setTimeout(done, this.duration)
53
+ }
54
+
55
+ afterLeave (el) {
56
+ el.style.transition = ''
57
+ el.style.overflow = ''
58
+ el.style.height = ''
59
+ }
60
+ }
61
+ </script>
@@ -2,6 +2,7 @@ import {
2
2
  mdiAlarmLightOutline,
3
3
  mdiAlert,
4
4
  mdiArrowLeft,
5
+ mdiArrowRight,
5
6
  mdiCalendar,
6
7
  mdiCheck,
7
8
  mdiCheckBold,
@@ -13,11 +14,13 @@ import {
13
14
  mdiDotsHorizontal,
14
15
  mdiDotsVertical,
15
16
  mdiFilter,
17
+ mdiInformationOutline,
16
18
  mdiLock,
17
19
  mdiLockOpenVariant,
18
20
  mdiLogoutVariant,
19
21
  mdiMagnify,
20
22
  mdiMenuDown,
23
+ mdiMenuRight,
21
24
  mdiMenuUp,
22
25
  mdiPalette,
23
26
  mdiPencil,
@@ -56,13 +59,16 @@ export default new Vuetify({
56
59
  checkIcon: mdiCheck,
57
60
  checkBoldIcon: mdiCheckBold,
58
61
  arrowLeftIcon: mdiArrowLeft,
62
+ arrowRightIcon: mdiArrowRight,
59
63
  caretDownIcon: mdiMenuDown,
60
64
  caretUpIcon: mdiMenuUp,
65
+ caretRightIcon: mdiMenuRight,
61
66
  printerIcon: mdiPrinter,
62
67
  euroSymbol: mdiCurrencyEur,
63
68
  paletteIcon: mdiPalette,
64
69
  addIcon: mdiPlusCircle,
65
- filterIcon: mdiFilter
70
+ filterIcon: mdiFilter,
71
+ infoIcon: mdiInformationOutline
66
72
  }
67
73
  },
68
74
  breakpoint: {
@@ -0,0 +1 @@
1
+ export { SidebarEvent } from './components/sidebar/SidebarEvent'
@@ -1,5 +1,13 @@
1
1
  html {
2
- overflow-y: auto; // ress.css • v2.0.4 sets overflow-y: scroll
2
+ // overflow-y: auto; // ress.css • v2.0.4 sets overflow-y: scroll
3
+ overflow: hidden;
4
+ }
5
+
6
+ #v-main {
7
+ overflow-y: auto;
8
+ overflow-x: auto;
9
+ height: 100vh;
10
+ flex: 1 1 auto;
3
11
  }
4
12
 
5
13
  .container {
@@ -17,7 +25,7 @@ html {
17
25
  }
18
26
 
19
27
  .contextButton {
20
- color: #AAAAAA !important;
28
+ color: #999999 !important;
21
29
  cursor: pointer;
22
30
 
23
31
  &:hover {
@@ -1,66 +0,0 @@
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>
@@ -1,59 +0,0 @@
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>