@awes-io/ui 2.75.0 → 2.76.0

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.
@@ -0,0 +1,68 @@
1
+ <template>
2
+ <div
3
+ class="aw-progress"
4
+ :style="{ '--progress': progressRatio, '--color': fgColor }"
5
+ ></div>
6
+ </template>
7
+
8
+ <script>
9
+ import anime from 'animejs'
10
+ import { clamp } from 'rambdax'
11
+ import { toColor } from '@AwUtils/styles'
12
+
13
+ export default {
14
+ name: 'AwProgress',
15
+
16
+ props: {
17
+ progress: {
18
+ type: Number,
19
+ default: 0
20
+ },
21
+
22
+ prevValueParam: {
23
+ type: String,
24
+ default: 'prev_progress'
25
+ },
26
+
27
+ color: {
28
+ type: String,
29
+ default: 'accent'
30
+ }
31
+ },
32
+
33
+ data() {
34
+ return {
35
+ value: parseInt(this.$route.query[this.prevValueParam]) || 0
36
+ }
37
+ },
38
+
39
+ computed: {
40
+ progressRatio() {
41
+ return (clamp(0, 100, this.value) / 100).toFixed(4)
42
+ },
43
+
44
+ fgColor() {
45
+ return toColor(this.color)
46
+ }
47
+ },
48
+
49
+ watch: {
50
+ progress: 'animate'
51
+ },
52
+
53
+ mounted() {
54
+ this.animate()
55
+ },
56
+
57
+ methods: {
58
+ animate() {
59
+ anime({
60
+ targets: this,
61
+ value: this.progress,
62
+ duration: 1000,
63
+ easing: 'easeOutCirc'
64
+ })
65
+ }
66
+ }
67
+ }
68
+ </script>
@@ -0,0 +1,104 @@
1
+ <template>
2
+ <AwCard>
3
+ <div
4
+ ref="container"
5
+ class="aw-empty-container"
6
+ :class="{ 'aw-empty-container--visible': isVisible }"
7
+ >
8
+ <slot name="icon">
9
+ <AwIcon
10
+ v-if="icon"
11
+ :name="icon"
12
+ :size="iconSize"
13
+ :style="{ fill: _iconColor }"
14
+ />
15
+ </slot>
16
+
17
+ <AwSubHeadline v-if="title" class="m-0">{{ title }}</AwSubHeadline>
18
+
19
+ <slot name="buttons"></slot>
20
+ </div>
21
+ </AwCard>
22
+ </template>
23
+
24
+ <script>
25
+ import anime from 'animejs'
26
+ import { toColor } from '@AwUtils/styles'
27
+
28
+ export default {
29
+ name: 'AwEmptyContainer',
30
+
31
+ props: {
32
+ icon: {
33
+ type: String,
34
+ default: ''
35
+ },
36
+
37
+ iconSize: {
38
+ type: Number,
39
+ default: 60
40
+ },
41
+
42
+ iconColor: {
43
+ type: String,
44
+ default: 'mono-500'
45
+ },
46
+
47
+ title: {
48
+ type: String,
49
+ default: ''
50
+ }
51
+ },
52
+
53
+ data() {
54
+ return {
55
+ isVisible: true
56
+ }
57
+ },
58
+
59
+ computed: {
60
+ _iconColor() {
61
+ return this.iconColor ? toColor(this.iconColor) : null
62
+ }
63
+ },
64
+
65
+ beforeMount() {
66
+ this.isVisible = false
67
+ },
68
+
69
+ mounted() {
70
+ this.prepare()
71
+
72
+ this.isVisible = true
73
+
74
+ setTimeout(this.animate, 200)
75
+ },
76
+
77
+ methods: {
78
+ prepare() {
79
+ anime.set(this._getElements(), {
80
+ opacity: 0,
81
+ translateY: '0.5rem',
82
+ transition: 'none'
83
+ })
84
+ },
85
+
86
+ animate() {
87
+ anime.set(this._getElements(), { transition: null })
88
+
89
+ anime({
90
+ targets: this._getElements(),
91
+ opacity: 1,
92
+ translateY: 0,
93
+ delay: anime.stagger(120),
94
+ easing: 'easeOutCirc',
95
+ duration: 240
96
+ })
97
+ },
98
+
99
+ _getElements() {
100
+ return this.$refs.container?.children || []
101
+ }
102
+ }
103
+ }
104
+ </script>
@@ -3,6 +3,7 @@
3
3
  v-show="!!croppie"
4
4
  class="aw-cropper"
5
5
  :style="{ width: totalWidth + 'px' }"
6
+ :class="{ 'aw-cropper--square': square }"
6
7
  >
7
8
  <!-- image wrapped because of Croppie -->
8
9
  <div class="aw-cropper__croppie">
@@ -83,7 +84,9 @@ export default {
83
84
  quality: {
84
85
  type: [String, Number],
85
86
  default: 1
86
- }
87
+ },
88
+
89
+ square: Boolean
87
90
  },
88
91
 
89
92
  data() {
@@ -16,6 +16,8 @@
16
16
  :name="name || title"
17
17
  :type="name || title ? 'initials' : 'no-img'"
18
18
  :loading="loading"
19
+ :square="squareAvatar"
20
+ :size="avatarSize"
19
21
  class="aw-island-avatar__icon-userpic"
20
22
  />
21
23
  <button
@@ -210,7 +212,14 @@ export default {
210
212
  default: true
211
213
  },
212
214
 
213
- truncateDescription: Boolean
215
+ truncateDescription: Boolean,
216
+
217
+ squareAvatar: Boolean,
218
+
219
+ avatarSize: {
220
+ type: [Number, String],
221
+ default: null
222
+ }
214
223
  },
215
224
 
216
225
  data() {
@@ -93,7 +93,7 @@ export default {
93
93
  getColor(str) {
94
94
  const index = memoizedHasher(str, this.colors.length)
95
95
 
96
- return this.colors[index]
96
+ return this.colors[index].onColor
97
97
  },
98
98
 
99
99
  _emitOnClick($event) {
@@ -112,30 +112,21 @@
112
112
 
113
113
  <script>
114
114
  import { pathOr } from 'rambdax'
115
+ import pageMixin from '@AwMixins/page'
115
116
  import AwBottomBar from '@AwOrganisms/AwBottomBar.vue'
116
117
  import AwPageHeadline from '@AwPages/_AwPageHeadline.vue'
117
118
 
118
119
  export default {
119
120
  name: 'AwPage',
120
121
 
122
+ mixins: [pageMixin],
123
+
121
124
  components: {
122
125
  AwBottomBar,
123
126
  AwPageHeadline
124
127
  },
125
128
 
126
129
  props: {
127
- // Main headline on the page
128
- title: {
129
- type: String,
130
- default: ''
131
- },
132
-
133
- // Meta title (`title` prop if not provided)
134
- metaTitle: {
135
- type: String,
136
- default: ''
137
- },
138
-
139
130
  titleTag: {
140
131
  type: String,
141
132
  default: null
@@ -147,15 +138,6 @@ export default {
147
138
  default: () => []
148
139
  },
149
140
 
150
- // Breadcrumb object with title and href to return back
151
- breadcrumb: {
152
- type: Object,
153
- default: null,
154
- validator(params) {
155
- return params ? typeof params.href !== 'undefined' : true
156
- }
157
- },
158
-
159
141
  // A prop to toggle page fullscreen mode
160
142
  fullscreen: Boolean,
161
143
 
@@ -185,18 +167,6 @@ export default {
185
167
  }
186
168
  },
187
169
 
188
- head() {
189
- let title = this.metaTitle || this.title
190
-
191
- // if (this._breadcrumb) {
192
- // title = this._breadcrumb.text + ' / ' + title
193
- // }
194
-
195
- return {
196
- title
197
- }
198
- },
199
-
200
170
  data() {
201
171
  return {
202
172
  isFullscreen: false,
@@ -205,14 +175,6 @@ export default {
205
175
  },
206
176
 
207
177
  computed: {
208
- _title() {
209
- return this.title
210
- },
211
-
212
- _hasBreadcrumb() {
213
- return this.breadcrumb ? !!this.breadcrumb.href : false
214
- },
215
-
216
178
  _hideBottomBar() {
217
179
  return this.hideBottomBar === null
218
180
  ? this._hasBreadcrumb
@@ -0,0 +1,219 @@
1
+ <template>
2
+ <div class="aw-page-single">
3
+ <AwPageHeader
4
+ ref="header"
5
+ :progress="headerProgress"
6
+ :primary="$screen.lg || isHeaderStuck"
7
+ :title="_title"
8
+ :hide-title="!$screen.lg && !isHeaderStuck"
9
+ :hide-menu="hideMenu"
10
+ class="aw-page-single__header"
11
+ :class="{ 'aw-page-single__header--is-stuck': isHeaderStuck }"
12
+ >
13
+ <template #breadcrumbs>
14
+ <slot name="breadcrumb">
15
+ <AwButton
16
+ theme="ghost"
17
+ color="default"
18
+ :href="backUrl"
19
+ :aria-label="breadcrumb?.title ?? $t('Back')"
20
+ v-on="backUrl ? null : { click: () => $router.back() }"
21
+ >
22
+ <template #icon>
23
+ <AwIconSystemMono name="arrow" />
24
+ </template>
25
+ </AwButton>
26
+ </slot>
27
+
28
+ <slot name="after-breadcrumb" />
29
+ </template>
30
+
31
+ <template #default>
32
+ <slot name="buttons"></slot>
33
+
34
+ <AwButton
35
+ v-if="actionButton && $screen.lg"
36
+ v-bind="actionButton"
37
+ size="md"
38
+ @click="$emit('action')"
39
+ />
40
+ </template>
41
+ </AwPageHeader>
42
+
43
+ <div
44
+ class="aw-page-single__container"
45
+ :class="{
46
+ 'aw-page-single__container--preview': isPreviewContainer,
47
+ container: containerType === 'default',
48
+ 'container-fluid': containerType === 'full'
49
+ }"
50
+ >
51
+ <div class="aw-page-single__mobile-title" aria-hidden="true">
52
+ {{ _title }}
53
+ </div>
54
+
55
+ <AwDescription
56
+ v-if="!$screen.lg && description"
57
+ class="aw-page-single__mobile-description"
58
+ tag="div"
59
+ >
60
+ {{ description }}
61
+ </AwDescription>
62
+
63
+ <div
64
+ ref="topScrollMark"
65
+ class="aw-page-single__top-scroll-mark"
66
+ aria-hidden="true"
67
+ ></div>
68
+
69
+ <div
70
+ class="aw-page-single__content"
71
+ :class="{
72
+ 'aw-page-single__content--column': isPreviewContainer
73
+ }"
74
+ >
75
+ <slot />
76
+ </div>
77
+
78
+ <!-- 'aw-page-single__preview--header-sticky': isHeaderStuck -->
79
+ <div
80
+ v-if="hasPreview"
81
+ class="aw-page-single__preview"
82
+ :class="{
83
+ 'aw-page-single__preview--reverse': reverse,
84
+ 'aw-page-single__preview--column': isPreviewContainer
85
+ }"
86
+ >
87
+ <slot name="preview" />
88
+ </div>
89
+
90
+ <div
91
+ v-if="actionButton && !$screen.lg"
92
+ class="aw-page-single__mobile-action-button"
93
+ :class="{
94
+ 'aw-page-single__mobile-action-button--is-stuck': isActionButtonStuck
95
+ }"
96
+ >
97
+ <AwButton
98
+ v-bind="actionButton"
99
+ size="lg"
100
+ @click="$emit('action')"
101
+ />
102
+ </div>
103
+
104
+ <div
105
+ ref="bottomScrollMark"
106
+ class="aw-page-single__bottom-scroll-mark"
107
+ aria-hidden="true"
108
+ ></div>
109
+ </div>
110
+ </div>
111
+ </template>
112
+
113
+ <script>
114
+ import { isType } from 'rambdax'
115
+ import pageMixin from '@AwMixins/page'
116
+
117
+ const CONTAINER_TYPES = ['default', 'full']
118
+
119
+ export default {
120
+ name: 'AwPageSingle',
121
+
122
+ components: {
123
+ AwPageHeader: () => import('@AwPages/_AwPageHeader.vue')
124
+ },
125
+
126
+ mixins: [pageMixin],
127
+
128
+ props: {
129
+ headerProgress: {
130
+ type: Number,
131
+ default: null
132
+ },
133
+
134
+ action: {
135
+ type: Object,
136
+ default: null
137
+ },
138
+
139
+ container: {
140
+ type: String,
141
+ default: null
142
+ },
143
+
144
+ description: {
145
+ type: String,
146
+ default: ''
147
+ },
148
+
149
+ reverse: Boolean,
150
+
151
+ hideMenu: Boolean
152
+ },
153
+
154
+ data() {
155
+ return {
156
+ isHeaderStuck: false,
157
+ isActionButtonStuck: false
158
+ }
159
+ },
160
+
161
+ computed: {
162
+ containerType() {
163
+ return CONTAINER_TYPES.includes(this.container)
164
+ ? this.container
165
+ : null
166
+ },
167
+
168
+ isPreviewContainer() {
169
+ return this.containerType === null
170
+ },
171
+
172
+ hasPreview() {
173
+ return this.$scopedSlots.preview || this.$slots.preview
174
+ },
175
+
176
+ actionButton() {
177
+ if (!isType('Object', this.action)) {
178
+ return null
179
+ }
180
+
181
+ return this.action
182
+ }
183
+ },
184
+
185
+ mounted() {
186
+ this._initScrollWatcher()
187
+ },
188
+
189
+ methods: {
190
+ _initScrollWatcher() {
191
+ if (this.$options.$observer) return
192
+
193
+ const { topScrollMark, bottomScrollMark } = this.$refs
194
+
195
+ this.$options.$observer = new IntersectionObserver((entries) => {
196
+ for (let i = 0; i < entries.length; i++) {
197
+ const entry = entries[i]
198
+
199
+ if (entry.target === topScrollMark) {
200
+ this.isHeaderStuck = !entry.isIntersecting
201
+ }
202
+
203
+ if (entry.target === bottomScrollMark) {
204
+ this.isActionButtonStuck = !entry.isIntersecting
205
+ }
206
+ }
207
+ })
208
+
209
+ this.$options.$observer.observe(topScrollMark)
210
+ this.$options.$observer.observe(bottomScrollMark)
211
+
212
+ this.$once('hook:beforeDestroy', () => {
213
+ this.$options.$observer.disconnect()
214
+ this.$options.$observer = null
215
+ })
216
+ }
217
+ }
218
+ }
219
+ </script>
@@ -0,0 +1,64 @@
1
+ <template>
2
+ <div class="aw-page-header" :class="{ 'aw-page-header--primary': primary }">
3
+ <div class="aw-page-header__breadcrumbs">
4
+ <slot name="breadcrumbs"> </slot>
5
+ </div>
6
+
7
+ <h1
8
+ v-if="title"
9
+ class="aw-page-header__title"
10
+ :class="{ 'sr-only': hideTitle }"
11
+ >
12
+ {{ title }}
13
+ </h1>
14
+
15
+ <div class="aw-page-header__buttons">
16
+ <slot>
17
+ <!-- <EwSupportChat /> -->
18
+ </slot>
19
+
20
+ <AwUserMenu v-if="!hideMenu" outline vertical caret />
21
+ </div>
22
+
23
+ <slot name="progress">
24
+ <AwProgress v-if="showProgress" :progress="progress" />
25
+ </slot>
26
+ </div>
27
+ </template>
28
+
29
+ <script>
30
+ import { isType } from 'rambdax'
31
+
32
+ export default {
33
+ name: 'AwPageHeader',
34
+
35
+ components: {
36
+ AwProgress: () => import('@AwAtoms/AwProgress.vue'),
37
+ AwUserMenu: () => import('@AwLayouts/_AwUserMenu.vue')
38
+ },
39
+
40
+ props: {
41
+ progress: {
42
+ type: Number,
43
+ default: null
44
+ },
45
+
46
+ title: {
47
+ type: String,
48
+ default: ''
49
+ },
50
+
51
+ hideTitle: Boolean,
52
+
53
+ hideMenu: Boolean,
54
+
55
+ primary: Boolean
56
+ },
57
+
58
+ computed: {
59
+ showProgress() {
60
+ return isType('Number', this.progress)
61
+ }
62
+ }
63
+ }
64
+ </script>
@@ -9,7 +9,13 @@
9
9
  v-on="$listeners"
10
10
  >
11
11
  <slot v-bind="$props">
12
- <span class="aw-icon-menu-item__icon-block" tabindex="-1">
12
+ <span
13
+ class="aw-icon-menu-item__icon-block"
14
+ tabindex="-1"
15
+ v-tooltip:right="
16
+ tooltip ? { content: text, offset: [0, -6] } : null
17
+ "
18
+ >
13
19
  <AwIcon
14
20
  :name="(active && iconActive) || icon"
15
21
  :size="size"
@@ -72,7 +78,7 @@ export default {
72
78
 
73
79
  size: {
74
80
  type: [String, Number],
75
- default: 24
81
+ default: 18
76
82
  },
77
83
 
78
84
  active: Boolean,
@@ -10,12 +10,24 @@
10
10
  <slot name="user">
11
11
  <button
12
12
  class="aw-user-menu__user focus-outline"
13
+ :class="{ 'aw-user-menu__user--outline': outline }"
13
14
  aria-haspopup="true"
14
15
  :aria-label="$t('Open menu')"
15
16
  @focus="openDropdown"
16
17
  @mouseenter="openDropdown"
17
18
  >
18
- <AwAvatar v-bind="user" size="40" tabindex="-1" />
19
+ <AwAvatar
20
+ v-bind="user"
21
+ :size="outline ? 24 : 40"
22
+ tabindex="-1"
23
+ />
24
+
25
+ <AwIconSystemMono
26
+ v-if="caret"
27
+ name="angle"
28
+ class="transform transition duration-100"
29
+ :class="{ 'rotate-90': isOpened, 'rotate-270': !isOpened }"
30
+ />
19
31
  </button>
20
32
  </slot>
21
33
 
@@ -23,8 +35,9 @@
23
35
  <AwDropdown
24
36
  ref="dropdown"
25
37
  class="aw-user-menu__menu"
38
+ :show.sync="isOpened"
26
39
  :options="{
27
- placement: 'right-end',
40
+ placement: vertical ? 'bottom-end' : 'right-end',
28
41
  modifiers: [{ name: 'offset', options: { offset: [4, 8] } }]
29
42
  }"
30
43
  >
@@ -111,12 +124,26 @@ export default {
111
124
  AwNavItem
112
125
  },
113
126
 
127
+ props: {
128
+ vertical: Boolean,
129
+
130
+ outline: Boolean,
131
+
132
+ caret: Boolean
133
+ },
134
+
114
135
  inject: {
115
136
  layoutProvider: {
116
137
  default: null
117
138
  }
118
139
  },
119
140
 
141
+ data() {
142
+ return {
143
+ isOpened: false
144
+ }
145
+ },
146
+
120
147
  computed: {
121
148
  ...mapGetters('awesIo', ['user']),
122
149