@awes-io/ui 2.35.0 → 2.39.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.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,55 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [2.39.0](https://github.com/awes-io/client/compare/@awes-io/ui@2.38.0...@awes-io/ui@2.39.0) (2022-01-17)
7
+
8
+
9
+ ### Features
10
+
11
+ * aw-address uses select internally ([d8a71f0](https://github.com/awes-io/client/commit/d8a71f0b42f22853ca69d1b61cfdb3d9d91aed40))
12
+
13
+
14
+
15
+
16
+
17
+ # [2.38.0](https://github.com/awes-io/client/compare/@awes-io/ui@2.37.0...@awes-io/ui@2.38.0) (2021-12-30)
18
+
19
+
20
+ ### Features
21
+
22
+ * default button color replaced to accent ([980a96d](https://github.com/awes-io/client/commit/980a96d5a23c5025a618a53ddf630934440eda91))
23
+
24
+
25
+
26
+
27
+
28
+ # [2.37.0](https://github.com/awes-io/client/compare/@awes-io/ui@2.36.0...@awes-io/ui@2.37.0) (2021-12-28)
29
+
30
+
31
+ ### Bug Fixes
32
+
33
+ * **aw-birthday-picker:** global dayjs with leap year plugin used instead of custom ([047890f](https://github.com/awes-io/client/commit/047890ff1eb26923f455a64dfd12b5a6c5c972c8))
34
+
35
+
36
+ ### Features
37
+
38
+ * scroll buttons collabsing added ([b773080](https://github.com/awes-io/client/commit/b77308027776ca4c2aba94753ec0826819d090c3))
39
+
40
+
41
+
42
+
43
+
44
+ # [2.36.0](https://github.com/awes-io/client/compare/@awes-io/ui@2.35.0...@awes-io/ui@2.36.0) (2021-12-10)
45
+
46
+
47
+ ### Features
48
+
49
+ * logo switching added ([e5e7328](https://github.com/awes-io/client/commit/e5e732812e9acb5c3a6c810d9a7346affa422988))
50
+
51
+
52
+
53
+
54
+
6
55
  # [2.35.0](https://github.com/awes-io/client/compare/@awes-io/ui@2.34.2...@awes-io/ui@2.35.0) (2021-12-04)
7
56
 
8
57
 
@@ -10,6 +10,7 @@
10
10
  @import './badge.css';
11
11
  @import './birthday-picker.css';
12
12
  @import './button.css';
13
+ @import './button-fixed.css';
13
14
  @import './button-nav.css';
14
15
 
15
16
  @import './calendar.css';
@@ -0,0 +1,49 @@
1
+ .aw-button-fixed {
2
+ display: flex;
3
+ align-items: center;
4
+
5
+ padding: 0.75rem 1rem;
6
+ min-width: 3.5rem;
7
+ min-height: 3.5rem;
8
+ /* border-radius: 1rem; */
9
+
10
+ font-size: 0.75rem;
11
+ font-weight: bold;
12
+ letter-spacing: 0.09375rem;
13
+ text-transform: uppercase;
14
+
15
+ background-color: rgba(var(--btn-bg), 1);
16
+ color: rgba(var(--btn-fg), 1);
17
+
18
+ box-shadow: 0px 0.25rem 0.5rem rgba(var(--btn-bg), 0.2);
19
+
20
+ &__text {
21
+ flex-shrink: 1;
22
+ max-width: 1px;
23
+ margin-right: -1px;
24
+
25
+ opacity: 0.1;
26
+ transition-property: max-width, margin, opacity;
27
+ transition-duration: 120ms;
28
+
29
+ &--expanded {
30
+ max-width: 16rem;
31
+ margin-right: 0.75em;
32
+ opacity: 1;
33
+ transition-timing-function: ease-out;
34
+ transition-duration: 240ms;
35
+ }
36
+ }
37
+
38
+ &:hover {
39
+ background-image: linear-gradient(rgba(var(--btn-fg), 0.15), rgba(var(--btn-fg), 0.15));
40
+ }
41
+
42
+ &:active {
43
+ transform: translateY(1px);
44
+ }
45
+
46
+ &:focus-visible {
47
+ outline: theme('focusOutline');
48
+ }
49
+ }
@@ -200,7 +200,7 @@
200
200
  /**
201
201
  * Colors
202
202
  */
203
- @each $color in (info, success, warning, error, surface, overlay) {
203
+ @each $color in (accent, info, success, warning, error, surface, overlay) {
204
204
  .aw-button {
205
205
  /* Circle, Solid */
206
206
  &.theme-circle.color-$(color),
@@ -6,9 +6,19 @@
6
6
  }
7
7
 
8
8
  &__fixed-btns {
9
+ display: flex;
10
+ flex-direction: column;
11
+ align-items: flex-end;
12
+
13
+ max-width: calc(100vw - 2 * theme('spacing.4', 1rem));
14
+
9
15
  position: fixed;
10
16
  right: theme('spacing.4', 1rem);
11
- bottom: calc(env(safe-area-inset-bottom, 0) + var(--page-buttons-bottom, theme('spacing.4', 1rem)));
17
+ bottom: calc(env(safe-area-inset-bottom, 0) + var(--page-buttons-bottom, 0));
12
18
  z-index: 2;
19
+
20
+ & > * {
21
+ margin-bottom: 1rem;
22
+ }
13
23
  }
14
24
  }
@@ -60,3 +60,28 @@ export const getEventTargetAttribute = ($event, attrName, bound = null) => {
60
60
 
61
61
  return null
62
62
  }
63
+
64
+ /**
65
+ * Detect if client supports passive event listeners
66
+ * @return {Boolean} true if supports
67
+ */
68
+ export const supportsPassive = () => {
69
+ let supports = false
70
+
71
+ if (
72
+ window &&
73
+ typeof window.addEventListener === 'function' &&
74
+ typeof Object.defineProperty === 'function'
75
+ ) {
76
+ const options = Object.defineProperty({}, 'passive', {
77
+ // eslint-disable-next-line getter-return
78
+ get() {
79
+ supports = true
80
+ }
81
+ })
82
+
83
+ window.addEventListener('_', null, options)
84
+ }
85
+
86
+ return supports
87
+ }
@@ -31,7 +31,6 @@
31
31
  </template>
32
32
 
33
33
  <script>
34
- import { equals } from 'rambdax'
35
34
  import FieldMixin from '@AwMixins/field'
36
35
  import ErrorMixin from '@AwMixins/error'
37
36
 
@@ -89,16 +88,16 @@ export default {
89
88
  _onChange($event) {
90
89
  let value = this._createValue($event.target.checked)
91
90
 
92
- if (equals(value, this.checked)) {
93
- $event.target.checked = this.isChecked
94
- return
95
- }
96
-
97
91
  if (this.hasError) {
98
92
  this.setError('')
99
93
  }
100
94
 
101
95
  this.$emit('change', value)
96
+
97
+ // sync checkbox state
98
+ this.$nextTick(() => {
99
+ $event.target.checked = this.isChecked
100
+ })
102
101
  },
103
102
 
104
103
  _createValue(isChecked) {
@@ -20,11 +20,21 @@ export default {
20
20
  default: true
21
21
  },
22
22
 
23
+ justify: {
24
+ type: String,
25
+ default: ''
26
+ },
27
+
28
+ align: {
29
+ type: String,
30
+ default: ''
31
+ },
32
+
23
33
  vertical: Boolean
24
34
  },
25
35
 
26
36
  render(h) {
27
- const { tag, gap, wrapChildren, vertical } = this.$props
37
+ const { tag, gap, wrapChildren, vertical, align, justify } = this.$props
28
38
  const children = this.$scopedSlots.default
29
39
  ? this.$scopedSlots.default()
30
40
  : []
@@ -44,7 +54,12 @@ export default {
44
54
  staticClass: 'aw-flow__wrap',
45
55
  class: {
46
56
  'aw-flow__wrap--children': !wrapChildren,
47
- 'aw-flow__wrap--vertical': vertical
57
+ 'aw-flow__wrap--vertical': vertical,
58
+ 'justify-center': justify === 'center',
59
+ 'justify-end': justify === 'end',
60
+ 'items-start': align === 'start',
61
+ 'items-center': align === 'center',
62
+ 'items-end': align === 'end'
48
63
  }
49
64
  },
50
65
  children.reduce((acc, vNode) => {
@@ -26,25 +26,27 @@ export default {
26
26
  render(h, { props: { name, size, viewBox }, data }) {
27
27
  const attrs = data.attrs || {}
28
28
 
29
- return h('SvgIcon', {
30
- props: { name, viewBox },
31
- staticClass: 'aw-icon',
32
- class: [
33
- data.class,
34
- data.staticClass,
35
- {
36
- 'aw-icon--size-text':
37
- !size && isNil(attrs.width) && isNil(attrs.height)
38
- }
39
- ],
40
- style: [data.style, data.staticStyle],
41
- attrs: {
42
- 'aria-hidden': true,
43
- width: size || null,
44
- height: size || null,
45
- ...attrs
46
- }
47
- })
29
+ return name
30
+ ? h('SvgIcon', {
31
+ props: { name, viewBox },
32
+ staticClass: 'aw-icon',
33
+ class: [
34
+ data.class,
35
+ data.staticClass,
36
+ {
37
+ 'aw-icon--size-text':
38
+ !size && isNil(attrs.width) && isNil(attrs.height)
39
+ }
40
+ ],
41
+ style: [data.style, data.staticStyle],
42
+ attrs: {
43
+ 'aria-hidden': true,
44
+ width: size || null,
45
+ height: size || null,
46
+ ...attrs
47
+ }
48
+ })
49
+ : null
48
50
  }
49
51
  }
50
52
  </script>
@@ -28,7 +28,7 @@
28
28
  ]"
29
29
  tabindex="-1"
30
30
  >
31
- <slot name="icon">
31
+ <slot name="icon" v-bind="{ icon, iconSize }">
32
32
  <AwIcon
33
33
  v-if="icon"
34
34
  :name="icon"
@@ -178,6 +178,14 @@
178
178
  </div>
179
179
 
180
180
  <div v-if="_isAjax && _hasNextPage" ref="dropdownEnd"></div>
181
+
182
+ <slot
183
+ name="dropdown-after"
184
+ v-bind="{
185
+ optionsList,
186
+ isLoading
187
+ }"
188
+ ></slot>
181
189
  </slot>
182
190
  </div>
183
191
  </div>
@@ -438,7 +446,7 @@ export default {
438
446
  _showNotFound() {
439
447
  return this._isAjax
440
448
  ? !this.selectOptions.length && !this.isLoading
441
- : !this.optionsList.length
449
+ : this._searchPhraseLength && !this.optionsList.length
442
450
  },
443
451
 
444
452
  isMobile() {
@@ -1,34 +1,25 @@
1
1
  <template>
2
- <div class="relative">
3
- <AwInput v-bind="$attrs" v-model="search" autocomplete="off" />
4
-
5
- <!-- suggestions -->
6
- <AwDropdown
7
- class="w-full z-10"
8
- :show="!!suggestions.length"
9
- close-on-action
10
- >
11
- <AwDropdownButton
12
- icon="location"
13
- v-for="place in suggestions"
14
- :key="place.place_id"
15
- @click="select(place)"
16
- >
17
- {{ place.description }}
18
- </AwDropdownButton>
19
-
2
+ <AwSelectObject
3
+ ref="select"
4
+ v-model="place"
5
+ :options="suggestions"
6
+ track-by="place_id"
7
+ option-label="description"
8
+ @search="search"
9
+ >
10
+ <template #dropdown-after>
20
11
  <!-- branding -->
21
12
  <img
13
+ v-if="suggestions.length"
22
14
  class="block my-2 mx-auto"
23
15
  aria-hidden="true"
24
16
  src="https://maps.gstatic.com/mapfiles/api-3/images/powered-by-google-on-white3.png"
25
17
  alt=""
26
18
  />
27
- </AwDropdown>
28
-
29
- <!-- places service needs a container to init -->
30
- <div ref="place"></div>
31
- </div>
19
+ <!-- places service needs a container to init -->
20
+ <div ref="place"></div>
21
+ </template>
22
+ </AwSelectObject>
32
23
  </template>
33
24
 
34
25
  <script>
@@ -56,30 +47,34 @@ export default {
56
47
  autocomplete: null,
57
48
  placesService: null,
58
49
  sessionToken: null,
50
+ isLoading: false,
59
51
  suggestions: [],
60
- placeDescription: '',
61
52
  place: null
62
53
  }
63
54
  },
64
55
 
65
- computed: {
66
- search: {
67
- get() {
68
- return this.placeDescription
69
- },
70
-
71
- set(text) {
72
- this.placeDescription = text
73
-
74
- if (!this.autocomplete) return
56
+ watch: {
57
+ place(place) {
58
+ if (!place || !place.place_id) {
59
+ this.place = null
60
+ return
61
+ }
75
62
 
76
- if (!text) {
63
+ this.placesService.getDetails(
64
+ {
65
+ placeId: place.place_id,
66
+ sessionToken: this.sessionToken
67
+ },
68
+ (details) => {
69
+ if (this.$refs.select) {
70
+ this.$refs.select.searchPhrase = ''
71
+ }
77
72
  this._resetSearch()
78
- return
73
+ this._formatAddressResponse(details).then((result) => {
74
+ this.$emit('input', result)
75
+ })
79
76
  }
80
-
81
- this._query(text)
82
- }
77
+ )
83
78
  }
84
79
  },
85
80
 
@@ -107,13 +102,22 @@ export default {
107
102
  this.placesService = new window.google.maps.places.PlacesService(
108
103
  this.$refs.place
109
104
  )
105
+ },
106
+
107
+ search(text) {
108
+ if (!this.autocomplete) return
110
109
 
111
- if (this.placeDescription) {
112
- this._query(this.placeDescription)
110
+ if (!text) {
111
+ this._resetSearch()
112
+ return
113
113
  }
114
+
115
+ this._query(text)
114
116
  },
115
117
 
116
118
  _query(input) {
119
+ this.$refs.select.isLoading = true
120
+
117
121
  if (!this.sessionToken) {
118
122
  this.sessionToken = new window.google.maps.places.AutocompleteSessionToken()
119
123
  }
@@ -129,10 +133,14 @@ export default {
129
133
  },
130
134
 
131
135
  _setSuggestions(data) {
136
+ this.$refs.select.isLoading = false
132
137
  this.suggestions = data || []
133
138
  },
134
139
 
135
140
  _resetSearch() {
141
+ if (this.$refs.select) {
142
+ this.$refs.select.isLoading = false
143
+ }
136
144
  this.suggestions = []
137
145
  this.sessionToken = null
138
146
  },
@@ -222,32 +230,8 @@ export default {
222
230
  })
223
231
  },
224
232
 
225
- select(place) {
226
- if (!place || !place.place_id) {
227
- this.place = null
228
- this.placeDescription = ''
229
- return
230
- }
231
-
232
- this.placesService.getDetails(
233
- {
234
- placeId: place.place_id,
235
- sessionToken: this.sessionToken
236
- },
237
- (details) => {
238
- this.place = details
239
- this.placeDescription = details.formatted_address
240
- this._resetSearch()
241
- this._formatAddressResponse(details).then((result) => {
242
- this.$emit('input', result)
243
- })
244
- }
245
- )
246
- },
247
-
248
233
  reset() {
249
234
  this.suggestions = []
250
- this.placeDescription = ''
251
235
  this.place = null
252
236
  }
253
237
  }
@@ -123,7 +123,7 @@ export default {
123
123
  },
124
124
 
125
125
  monthsList() {
126
- const date = this.dayjs()
126
+ const date = this.$dayjs()
127
127
  return [...Array(12).keys()].map((el) => ({
128
128
  value: el,
129
129
  text: date.month(el).format('MMMM')
@@ -131,7 +131,7 @@ export default {
131
131
  },
132
132
 
133
133
  daysList() {
134
- const date = this.dayjs().year(this.year || 2000)
134
+ const date = this.$dayjs().year(this.year || 2000)
135
135
 
136
136
  const maxDays = date.month(this.month || 0).daysInMonth()
137
137
 
@@ -141,10 +141,10 @@ export default {
141
141
  },
142
142
 
143
143
  yearsList() {
144
- const current = this.dayjs().year()
144
+ const current = this.$dayjs().year()
145
145
  return [...Array(101).keys()].map((el) => ({
146
146
  year: current - el,
147
- isLeapYear: this.dayjs()
147
+ isLeapYear: this.$dayjs()
148
148
  .year(current - el)
149
149
  .isLeapYear()
150
150
  }))
@@ -175,7 +175,7 @@ export default {
175
175
 
176
176
  emit() {
177
177
  if (!isNil(this.month) && this.day) {
178
- let d = this.dayjs()
178
+ let d = this.$dayjs()
179
179
  .year(this.year || 2000)
180
180
  .month(this.month)
181
181
  .date(this.day)
@@ -43,7 +43,7 @@ export default {
43
43
  }
44
44
 
45
45
  let age = null
46
- const now = this.dayjs()
46
+ const now = this.$dayjs()
47
47
  const date = this.toDayjs(this.date)
48
48
 
49
49
  const isYearPresent = this.getYearString(this.date).includes(
@@ -9,7 +9,7 @@
9
9
  <slot name="heading" v-bind="{ titleTag, title, breadcrumb }">
10
10
  <AwPageHeadline
11
11
  :style="{
12
- '--page-buttons-bottom': _hideBottomBar ? null : '5rem'
12
+ '--page-buttons-bottom': _hideBottomBar ? null : '4rem'
13
13
  }"
14
14
  :title="_title"
15
15
  :breadcrumb="breadcrumb"
@@ -4,6 +4,7 @@
4
4
  <AwFlow
5
5
  v-if="isExpanded && splitButtons.buttons.length"
6
6
  tag="div"
7
+ justify="end"
7
8
  class="aw-page-menu-buttons__expanded-btns"
8
9
  >
9
10
  <AwButton
@@ -34,28 +35,27 @@
34
35
  </AwContextMenu>
35
36
 
36
37
  <!-- fixed button -->
37
- <AwFlow
38
+ <div
38
39
  v-if="!isExpanded && splitButtons.fixed"
39
40
  class="aw-page-menu-buttons__fixed-btns"
40
- tag="div"
41
- vertical
42
41
  >
43
- <AwButton
42
+ <AwButtonFixed
44
43
  v-for="({ listeners, tooltip, ...attrs },
45
44
  i) in splitButtons.fixed"
46
45
  :key="i"
47
- size="lg"
46
+ :expanded="isInTopPosition"
48
47
  v-tooltip="tooltip"
49
48
  v-bind="attrs"
50
49
  v-on="listeners"
51
50
  />
52
- </AwFlow>
51
+ </div>
53
52
  </div>
54
53
  </template>
55
54
 
56
55
  <script>
57
56
  import { isType, defaultTo } from 'rambdax'
58
57
  import { downloadFile } from '@AwUtils/download'
58
+ import { supportsPassive } from '@AwUtils/events'
59
59
 
60
60
  const toIconButton = (props) => ({
61
61
  ...props,
@@ -66,6 +66,10 @@ const toIconButton = (props) => ({
66
66
  export default {
67
67
  name: 'AwPageMenuButtons',
68
68
 
69
+ components: {
70
+ AwButtonFixed: () => import('@AwPages/_AwButtonFixed.vue')
71
+ },
72
+
69
73
  props: {
70
74
  items: {
71
75
  type: Array,
@@ -75,12 +79,20 @@ export default {
75
79
  breakpoint: {
76
80
  type: String,
77
81
  default: 'lg'
82
+ },
83
+
84
+ /* max window scroll when fixed buttons expanded */
85
+ /* pass -1 to collapse button eventually */
86
+ expandOffset: {
87
+ type: Number,
88
+ default: 150 // px
78
89
  }
79
90
  },
80
91
 
81
92
  data() {
82
93
  return {
83
- isDownloading: {}
94
+ isDownloading: {},
95
+ isInTopPosition: true
84
96
  }
85
97
  },
86
98
 
@@ -121,6 +133,10 @@ export default {
121
133
  }
122
134
  },
123
135
 
136
+ mounted() {
137
+ this._initScrollListener()
138
+ },
139
+
124
140
  methods: {
125
141
  _getVisiblity(show) {
126
142
  return isType('Function', show) ? show(this) : defaultTo(true, show)
@@ -145,6 +161,31 @@ export default {
145
161
  } finally {
146
162
  this.$delete(this.isDownloading, url)
147
163
  }
164
+ },
165
+
166
+ _initScrollListener() {
167
+ if (this.expandOffset < 0) {
168
+ this.isInTopPosition = false
169
+ return
170
+ }
171
+
172
+ const opts = supportsPassive()
173
+ ? {
174
+ passive: true
175
+ }
176
+ : false
177
+
178
+ const detectTopPosition = () => {
179
+ this.isInTopPosition = window.pageYOffset < this.expandOffset
180
+ }
181
+
182
+ detectTopPosition()
183
+
184
+ window.addEventListener('scroll', detectTopPosition, opts)
185
+
186
+ this.$once('hook:beforeDestroy', () => {
187
+ window.removeEventListener('scroll', detectTopPosition, opts)
188
+ })
148
189
  }
149
190
  },
150
191
 
@@ -0,0 +1,45 @@
1
+ <template>
2
+ <Component
3
+ :is="_linkComponent"
4
+ v-bind="_linkAttrs"
5
+ :style="buttonStyle"
6
+ class="aw-button-fixed"
7
+ v-on="$listeners"
8
+ >
9
+ <span
10
+ v-if="text"
11
+ class="aw-button-fixed__text truncate"
12
+ :class="{ 'aw-button-fixed__text--expanded': expanded }"
13
+ >
14
+ <slot>{{ text }}</slot>
15
+ </span>
16
+ <slot name="icon">
17
+ <AwIcon :name="icon" size="24" class="flex-shrink-0" />
18
+ </slot>
19
+ </Component>
20
+ </template>
21
+
22
+ <script>
23
+ import linkMixin from '@AwMixins/link'
24
+ import buttonMixin from '@AwMixins/button'
25
+
26
+ export default {
27
+ name: 'AwButtonFixed',
28
+
29
+ mixins: [linkMixin, buttonMixin],
30
+
31
+ props: {
32
+ icon: {
33
+ type: String,
34
+ required: true
35
+ },
36
+
37
+ text: {
38
+ type: String,
39
+ default: ''
40
+ },
41
+
42
+ expanded: Boolean
43
+ }
44
+ }
45
+ </script>
@@ -27,23 +27,14 @@
27
27
  </template>
28
28
 
29
29
  <script>
30
+ import { mapGetters } from 'vuex'
30
31
  import { pathOr } from 'rambdax'
31
32
 
32
33
  export default {
33
34
  name: 'AwLayoutCenter',
34
35
 
35
36
  computed: {
36
- isDark() {
37
- return this.$store.getters['awesIo/isDarkTheme']
38
- },
39
-
40
- logo() {
41
- return pathOr(
42
- null,
43
- ['$awes', '_config', this.isDark ? 'dark' : 'default', 'logo'],
44
- this
45
- )
46
- },
37
+ ...mapGetters('awesIo', ['isDarkTheme', 'logo']),
47
38
 
48
39
  background() {
49
40
  const { src, ...background } = pathOr(
@@ -51,7 +42,7 @@ export default {
51
42
  [
52
43
  '$awes',
53
44
  '_config',
54
- this.isDark ? 'dark' : 'default',
45
+ this.isDarkTheme ? 'dark' : 'default',
55
46
  'background'
56
47
  ],
57
48
  this
@@ -0,0 +1,18 @@
1
+ <template functional>
2
+ <NLink
3
+ to="/"
4
+ class="aw-layout-logo"
5
+ :class="[data.class, data.staticClass]"
6
+ >
7
+ <img v-if="props.logo" v-bind="props.logo" />
8
+ <span class="sr-only">
9
+ {{ parent.$t('AwLayoutDefault.homepage') }}
10
+ </span>
11
+ </NLink>
12
+ </template>
13
+
14
+ <script>
15
+ export default {
16
+ name: 'AwLayoutLogo'
17
+ }
18
+ </script>
@@ -5,10 +5,12 @@
5
5
  'aw-layout-menu--no-submenu': hideAside || !hasSubmenu
6
6
  }"
7
7
  >
8
- <slot name="logo" v-bind="logo">
9
- <NLink to="/" class="aw-layout-menu__logo">
10
- <img v-if="logo" v-bind="logo" />
11
- </NLink>
8
+ <slot name="logo">
9
+ <Component
10
+ :is="logoComponent.is"
11
+ v-bind="logoComponent.props"
12
+ class="aw-layout-menu__logo"
13
+ />
12
14
  </slot>
13
15
 
14
16
  <div class="aw-layout-menu__menu">
@@ -62,6 +64,7 @@
62
64
  import { mapGetters } from 'vuex'
63
65
  import { pathOr, viewOr, lensProp, omit } from 'rambdax'
64
66
  import AwMenuItemIcon from '@AwLayouts/_AwMenuItemIcon.vue'
67
+ import AwLayoutLogo from '@AwLayouts/_AwLayoutLogo.vue'
65
68
  import AwUserMenu from '@AwLayouts/_AwUserMenu.vue'
66
69
  import AwNav from '@AwLayouts/_AwNav.vue'
67
70
 
@@ -70,6 +73,7 @@ export default {
70
73
 
71
74
  components: {
72
75
  AwMenuItemIcon,
76
+ AwLayoutLogo,
73
77
  AwUserMenu,
74
78
  AwNav
75
79
  },
@@ -85,7 +89,7 @@ export default {
85
89
  },
86
90
 
87
91
  computed: {
88
- ...mapGetters('awesIo', ['user', 'isDarkTheme']),
92
+ ...mapGetters('awesIo', ['user', 'logoComponent']),
89
93
 
90
94
  mainMenu() {
91
95
  return viewOr([], lensProp('mainMenu'), this.layoutProvider)
@@ -115,14 +119,6 @@ export default {
115
119
  this.activeMenu
116
120
  )
117
121
  : ''
118
- },
119
-
120
- logo() {
121
- return pathOr(
122
- null,
123
- ['_config', this.isDarkTheme ? 'dark' : 'default', 'logo'],
124
- this.$awes
125
- )
126
122
  }
127
123
  },
128
124
 
@@ -84,14 +84,21 @@
84
84
  />
85
85
  </template>
86
86
 
87
+ <slot name="after-mobile-menu">
88
+ <Component
89
+ v-if="afterMobileMenuComponent"
90
+ :is="afterMobileMenuComponent.is"
91
+ v-bind="afterMobileMenuComponent.props"
92
+ />
93
+ </slot>
94
+
87
95
  <!-- logo -->
88
96
  <slot name="logo" v-bind="logo">
89
- <NLink to="/" class="aw-mobile-menu__logo">
90
- <img v-if="logo" v-bind="logo" />
91
- <span class="sr-only">
92
- {{ $t('AwMobileMenu.home') }}
93
- </span>
94
- </NLink>
97
+ <Component
98
+ :is="logoComponent.is"
99
+ v-bind="logoComponent.props"
100
+ class="aw-mobile-menu__logo"
101
+ />
95
102
  <span class="aw-mobile-menu__version">
96
103
  {{ $t('AwMobileMenu.version', { version: $config.VERSION }) }}
97
104
  </span>
@@ -128,7 +135,11 @@ export default {
128
135
  },
129
136
 
130
137
  computed: {
131
- ...mapGetters('awesIo', ['user']),
138
+ ...mapGetters('awesIo', [
139
+ 'user',
140
+ 'logoComponent',
141
+ 'afterMobileMenuComponent'
142
+ ]),
132
143
 
133
144
  mainMenu() {
134
145
  return viewOr([], lensProp('mainMenu'), this.layoutProvider).filter(
@@ -16,7 +16,7 @@ export const AwAvatar = {
16
16
  export const AwButton = {
17
17
  routerComponent: 'router-link',
18
18
  size: 'md',
19
- color: 'success',
19
+ color: 'accent',
20
20
  theme: 'solid'
21
21
  }
22
22
 
package/lang/de.js CHANGED
@@ -53,6 +53,7 @@ export const AwCodeSnippet = {
53
53
  }
54
54
 
55
55
  export const AwLayoutDefault = {
56
+ homepage: 'Startseite',
56
57
  offline: 'Keine Internetverbindung',
57
58
  offlineText:
58
59
  'Überprüfen Sie Ihre Verbindung und versuchen Sie es erneut ...',
package/lang/en.js CHANGED
@@ -53,6 +53,7 @@ export const AwCodeSnippet = {
53
53
  }
54
54
 
55
55
  export const AwLayoutDefault = {
56
+ homepage: 'Homepage',
56
57
  offline: 'No Internet Connection',
57
58
  offlineText: 'Check your connection and try again ...',
58
59
  toggleMenu: 'Toggle mobile menu',
package/lang/ru.js CHANGED
@@ -57,6 +57,7 @@ export const AwCodeSnippet = {
57
57
  }
58
58
 
59
59
  export const AwLayoutDefault = {
60
+ homepage: 'Главная страница',
60
61
  offline: 'Нет соединения с Интернетом',
61
62
  offlineText: 'Проверьте Ваше подключение и попробуйте еще раз ...',
62
63
  toggleMenu: 'Мобильное меню',
package/lang/uk.js CHANGED
@@ -53,6 +53,7 @@ export const AwCodeSnippet = {
53
53
  }
54
54
 
55
55
  export const AwLayoutDefault = {
56
+ homepage: 'Головна сторінка',
56
57
  offline: 'Немає доступу до Інтернету',
57
58
  offlineText: 'Перевірте Ваше підключення і спробуйте ще раз ...',
58
59
  toggleMenu: 'Мобільне меню',
@@ -1,10 +1,3 @@
1
- import dayjs from 'dayjs'
2
- import customParseFormat from 'dayjs/plugin/customParseFormat'
3
- import localizedFormat from 'dayjs/plugin/localizedFormat'
4
-
5
- dayjs.extend(customParseFormat)
6
- dayjs.extend(localizedFormat)
7
-
8
1
  export default {
9
2
  props: {
10
3
  /**
@@ -26,20 +19,14 @@ export default {
26
19
  }
27
20
  },
28
21
 
29
- computed: {
30
- dayjs() {
31
- return this.$dayjs || dayjs
32
- }
33
- },
34
-
35
22
  methods: {
36
23
  toDayjs(input) {
37
24
  if (typeof input !== 'string') {
38
- return this.dayjs(input)
25
+ return this.$dayjs(input)
39
26
  }
40
27
 
41
28
  if (this.fullParseFormat) {
42
- const dLong = this.dayjs(input, this.fullParseFormat)
29
+ const dLong = this.$dayjs(input, this.fullParseFormat)
43
30
  if (dLong.isValid()) {
44
31
  return dLong
45
32
  }
@@ -48,13 +35,13 @@ export default {
48
35
  if (this.shortParseFormat) {
49
36
  const _formatWithYear = `${this.shortParseFormat}.YYYY`
50
37
  const _inputWithYear = `${input}.2000` // 2000 is leap year
51
- const dShort = this.dayjs(_inputWithYear, _formatWithYear)
38
+ const dShort = this.$dayjs(_inputWithYear, _formatWithYear)
52
39
  if (dShort.isValid()) {
53
40
  return dShort
54
41
  }
55
42
  }
56
43
 
57
- return this.dayjs(input)
44
+ return this.$dayjs(input)
58
45
  },
59
46
 
60
47
  parseDate(date) {
@@ -0,0 +1,17 @@
1
+ export default {
2
+ props: {
3
+ color: {
4
+ type: String,
5
+ default: 'accent'
6
+ }
7
+ },
8
+
9
+ computed: {
10
+ buttonStyle() {
11
+ return {
12
+ '--btn-bg': `var(--c-${this.color}-rgb)`,
13
+ '--btn-fg': `var(--c-on-${this.color}-rgb)`
14
+ }
15
+ }
16
+ }
17
+ }
@@ -2,7 +2,7 @@ import Vue from 'vue'
2
2
  import dayjs from 'dayjs'
3
3
 
4
4
  /* eslint-disable */
5
- <% ['dayjs/plugin/relativeTime', 'dayjs/plugin/customParseFormat', 'dayjs/plugin/localizedFormat'].concat(Array.isArray(options.dayjs.plugins) ? options.dayjs.plugins : []).filter((val, i, arr) => arr.indexOf(val) === i).forEach((plugin) => {
5
+ <% ['dayjs/plugin/relativeTime', 'dayjs/plugin/customParseFormat', 'dayjs/plugin/localizedFormat', 'dayjs/plugin/isLeapYear'].concat(Array.isArray(options.dayjs.plugins) ? options.dayjs.plugins : []).filter((val, i, arr) => arr.indexOf(val) === i).forEach((plugin) => {
6
6
  const src = plugin.src || plugin
7
7
  const name = plugin.name || src.replace('dayjs/plugin/', '')
8
8
  const options = plugin.options || {}
@@ -6,6 +6,12 @@ export default ({ store, app }) => {
6
6
  */
7
7
  if (!store.hasModule('awesIo')) {
8
8
  store.registerModule('awesIo', storeModule)
9
+
10
+ // Set default logo
11
+ store.commit('awesIo/SET_LOGO', {
12
+ default: JSON.parse('<%= JSON.stringify(options.default.logo) %>'),
13
+ dark: JSON.parse('<%= JSON.stringify(options.dark.logo) %>')
14
+ })
9
15
  }
10
16
 
11
17
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@awes-io/ui",
3
- "version": "2.35.0",
3
+ "version": "2.39.0",
4
4
  "description": "User Interface (UI) components",
5
5
  "keywords": [
6
6
  "ui",
@@ -124,5 +124,5 @@
124
124
  "vue-template-compiler": "^2.6.10",
125
125
  "webfonts-generator": "^0.4.0"
126
126
  },
127
- "gitHead": "034b0ec86005251c5b6984eeb245dfd9a4cebf07"
127
+ "gitHead": "357c3e4b953260cceb0e1514598dc7246aaff63d"
128
128
  }
package/store/awesIo.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import Vue from 'vue'
2
- import { pathOr, pick, isType } from 'rambdax'
2
+ import { pathOr, pick, isType, mergeDeepRight } from 'rambdax'
3
3
 
4
4
  const castArray = (val) => (Array.isArray(val) ? val : [])
5
5
 
@@ -20,12 +20,18 @@ export const state = () => ({
20
20
 
21
21
  profileUrl: null,
22
22
 
23
+ logo: {
24
+ default: { src: '' },
25
+ dark: { src: '' }
26
+ },
27
+
23
28
  menus: {
24
29
  main: [],
25
30
  secondary: [],
26
31
  user: []
27
32
  },
28
33
  mobileMenuOpened: false,
34
+ afterMobileMenu: null,
29
35
 
30
36
  headerNotification: null,
31
37
 
@@ -83,7 +89,26 @@ export const getters = {
83
89
  }
84
90
  },
85
91
 
86
- isDarkTheme: pathOr(false, 'isDarkTheme')
92
+ isDarkTheme: pathOr(false, 'isDarkTheme'),
93
+
94
+ logo(state, getters) {
95
+ return getters.isDarkTheme ? state.logo.dark : state.logo.default
96
+ },
97
+
98
+ logoComponent(state, getters) {
99
+ const logoComponent = pathOr(null, 'logo.component', state)
100
+
101
+ return logoComponent && logoComponent.is
102
+ ? logoComponent
103
+ : {
104
+ is: 'AwLayoutLogo',
105
+ props: { logo: getters.logo }
106
+ }
107
+ },
108
+
109
+ afterMobileMenuComponent(state) {
110
+ return state.afterMobileMenu
111
+ }
87
112
  }
88
113
 
89
114
  export const mutations = {
@@ -127,6 +152,32 @@ export const mutations = {
127
152
 
128
153
  TOGGLE_MOBILE_MENU(state, status = !state.mobileMenuOpened) {
129
154
  state.mobileMenuOpened = Boolean(status)
155
+ },
156
+
157
+ SET_LOGO(state, logo) {
158
+ if (typeof logo === 'string') {
159
+ state.logo = mergeDeepRight(state.logo, {
160
+ default: { src: logo },
161
+ dark: { src: logo }
162
+ })
163
+ } else if (typeof logo === 'object' && logo !== null) {
164
+ const _logo = {}
165
+ _logo.default = pathOr(state.logo.default, 'default', logo)
166
+ _logo.dark = pathOr(
167
+ pathOr(state.logo.dark, 'default', logo),
168
+ 'dark',
169
+ logo
170
+ )
171
+ _logo.component = pathOr(state.logo.component, 'component', logo)
172
+
173
+ state.logo = _logo
174
+ }
175
+ },
176
+
177
+ SET_AFTER_MOBILE_MENU(state, payload) {
178
+ if (payload && payload.component) {
179
+ state.afterMobileMenu = payload.component
180
+ }
130
181
  }
131
182
  }
132
183