@awes-io/ui 2.36.0 → 2.37.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,22 @@
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.37.0](https://github.com/awes-io/client/compare/@awes-io/ui@2.36.0...@awes-io/ui@2.37.0) (2021-12-28)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * **aw-birthday-picker:** global dayjs with leap year plugin used instead of custom ([047890f](https://github.com/awes-io/client/commit/047890ff1eb26923f455a64dfd12b5a6c5c972c8))
12
+
13
+
14
+ ### Features
15
+
16
+ * scroll buttons collabsing added ([b773080](https://github.com/awes-io/client/commit/b77308027776ca4c2aba94753ec0826819d090c3))
17
+
18
+
19
+
20
+
21
+
6
22
  # [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)
7
23
 
8
24
 
@@ -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
+ }
@@ -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-start': 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"
@@ -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>
@@ -5,6 +5,9 @@
5
5
  :class="[data.class, data.staticClass]"
6
6
  >
7
7
  <img v-if="props.logo" v-bind="props.logo" />
8
+ <span class="sr-only">
9
+ {{ $t('AwLayoutDefault.homepage') }}
10
+ </span>
8
11
  </NLink>
9
12
  </template>
10
13
 
@@ -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(
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 || {}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@awes-io/ui",
3
- "version": "2.36.0",
3
+ "version": "2.37.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": "3ac5dc679b50b56efa3b57c967cd37b2c2eba154"
127
+ "gitHead": "c1bfbdde456b9c8019674ec89b2a1e1a82987c96"
128
128
  }
package/store/awesIo.js CHANGED
@@ -31,6 +31,7 @@ export const state = () => ({
31
31
  user: []
32
32
  },
33
33
  mobileMenuOpened: false,
34
+ afterMobileMenu: null,
34
35
 
35
36
  headerNotification: null,
36
37
 
@@ -103,6 +104,10 @@ export const getters = {
103
104
  is: 'AwLayoutLogo',
104
105
  props: { logo: getters.logo }
105
106
  }
107
+ },
108
+
109
+ afterMobileMenuComponent(state) {
110
+ return state.afterMobileMenu
106
111
  }
107
112
  }
108
113
 
@@ -167,6 +172,12 @@ export const mutations = {
167
172
 
168
173
  state.logo = _logo
169
174
  }
175
+ },
176
+
177
+ SET_AFTER_MOBILE_MENU(state, payload) {
178
+ if (payload && payload.component) {
179
+ state.afterMobileMenu = payload.component
180
+ }
170
181
  }
171
182
  }
172
183