wave-ui 4.2.0 → 4.2.2
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/dist/types/types/components/WBadge.d.ts +7 -0
- package/dist/types/types/components/WCard.d.ts +7 -0
- package/dist/types/types/components/WDrawer.d.ts +7 -0
- package/dist/types/types/components/WIcon.d.ts +7 -0
- package/dist/types/types/components/WProgress.d.ts +7 -0
- package/dist/types/types/components/WRating.d.ts +7 -0
- package/dist/types/types/components/WSpinner.d.ts +6 -0
- package/dist/types/types/components/WToolbar.d.ts +7 -0
- package/dist/wave-ui.cjs.js +2 -2
- package/dist/wave-ui.esm.js +479 -393
- package/dist/wave-ui.umd.js +2 -2
- package/package.json +1 -1
- package/src/wave-ui/components/w-alert.vue +7 -1
- package/src/wave-ui/components/w-badge.vue +6 -4
- package/src/wave-ui/components/w-breadcrumbs.vue +2 -2
- package/src/wave-ui/components/w-card.vue +3 -0
- package/src/wave-ui/components/w-checkbox.vue +1 -0
- package/src/wave-ui/components/w-dialog.vue +5 -0
- package/src/wave-ui/components/w-drawer.vue +7 -0
- package/src/wave-ui/components/w-icon.vue +4 -2
- package/src/wave-ui/components/w-input.vue +1 -0
- package/src/wave-ui/components/w-menu.vue +2 -2
- package/src/wave-ui/components/w-progress.vue +10 -1
- package/src/wave-ui/components/w-radio.vue +1 -0
- package/src/wave-ui/components/w-rating.vue +22 -19
- package/src/wave-ui/components/w-select.vue +11 -2
- package/src/wave-ui/components/w-spinner.vue +2 -1
- package/src/wave-ui/components/w-tag.vue +3 -3
- package/src/wave-ui/components/w-textarea.vue +1 -0
- package/src/wave-ui/components/w-toolbar.vue +2 -1
- package/src/wave-ui/components/w-tooltip.vue +3 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wave-ui",
|
|
3
|
-
"version": "4.2.
|
|
3
|
+
"version": "4.2.2",
|
|
4
4
|
"description": "A UI framework for Vue.js 3 (and 2) with only the bright side. :sunny:",
|
|
5
5
|
"author": "Antoni Andre <antoniandre.web@gmail.com>",
|
|
6
6
|
"homepage": "https://antoniandre.github.io/wave-ui",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template lang="pug">
|
|
2
|
-
.w-alert(v-if="show" :class="classes")
|
|
2
|
+
.w-alert(v-if="show" :role="ariaRole" aria-atomic="true" :class="classes")
|
|
3
3
|
//- Add a wrapper around the content when needed.
|
|
4
4
|
template(v-if="type || icon || dismiss")
|
|
5
5
|
w-icon.w-alert__icon(v-if="type || icon") {{ type ? typeIcon : icon }}
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
w-button.w-alert__dismiss(
|
|
9
9
|
v-if="dismiss"
|
|
10
10
|
@click="$emit('update:modelValue', show = false);$emit('input', false);$emit('close', false)"
|
|
11
|
+
aria-label="Dismiss"
|
|
11
12
|
icon="wi-cross"
|
|
12
13
|
color="inherit"
|
|
13
14
|
sm
|
|
@@ -80,6 +81,11 @@ export default {
|
|
|
80
81
|
)
|
|
81
82
|
},
|
|
82
83
|
|
|
84
|
+
// error/warning demand immediate attention; success/info/plain are polite.
|
|
85
|
+
ariaRole () {
|
|
86
|
+
return (this.error || this.warning) ? 'alert' : 'status'
|
|
87
|
+
},
|
|
88
|
+
|
|
83
89
|
presetSize () {
|
|
84
90
|
return (
|
|
85
91
|
(this.xs && 'xs') ||
|
|
@@ -6,10 +6,11 @@
|
|
|
6
6
|
v-if="modelValue"
|
|
7
7
|
:class="classes"
|
|
8
8
|
:style="styles"
|
|
9
|
-
aria-
|
|
10
|
-
aria-
|
|
11
|
-
aria-
|
|
12
|
-
|
|
9
|
+
:aria-hidden="dot ? 'true' : undefined"
|
|
10
|
+
:aria-atomic="dot ? undefined : 'true'"
|
|
11
|
+
:aria-label="dot ? undefined : (ariaLabel || (modelValue === true ? 'Badge' : String(modelValue)))"
|
|
12
|
+
:aria-live="dot ? undefined : 'polite'"
|
|
13
|
+
:role="dot ? undefined : 'status'")
|
|
13
14
|
slot(v-if="!dot" name="badge") {{ modelValue === true ? '' : (modelValue || '') }}
|
|
14
15
|
</template>
|
|
15
16
|
|
|
@@ -19,6 +20,7 @@ export default {
|
|
|
19
20
|
|
|
20
21
|
props: {
|
|
21
22
|
modelValue: { default: true },
|
|
23
|
+
ariaLabel: { type: String }, // Override the default aria-label (defaults to the badge value).
|
|
22
24
|
xs: { type: Boolean },
|
|
23
25
|
sm: { type: Boolean },
|
|
24
26
|
md: { type: Boolean },
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template lang="pug">
|
|
2
|
-
.w-breadcrumbs(:class="classes")
|
|
2
|
+
nav.w-breadcrumbs(:class="classes" aria-label="Breadcrumb")
|
|
3
3
|
template(v-for="(item, i) in items")
|
|
4
4
|
//- Separator.
|
|
5
5
|
span.w-breadcrumbs__separator(
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
:item="item"
|
|
42
42
|
:index="i + 1"
|
|
43
43
|
:is-last="i === items.length - 1")
|
|
44
|
-
span(v-else :key="`${i}f`" v-html="item[itemLabelKey]")
|
|
44
|
+
span(v-else :key="`${i}f`" aria-current="page" v-html="item[itemLabelKey]")
|
|
45
45
|
</template>
|
|
46
46
|
|
|
47
47
|
<script>
|
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
.w-card(:class="classes")
|
|
3
3
|
.w-card__title(
|
|
4
4
|
v-if="$slots.title"
|
|
5
|
+
:id="titleId || undefined"
|
|
5
6
|
:class="{ 'w-card__title--has-toolbar': $slots.title && titleHasToolbar, ...titleClasses }")
|
|
6
7
|
slot(name="title")
|
|
7
8
|
.w-card__title(
|
|
8
9
|
v-else-if="title"
|
|
9
10
|
v-html="title"
|
|
11
|
+
:id="titleId || undefined"
|
|
10
12
|
:class="{ 'w-card__title--has-toolbar': $slots.title && titleHasToolbar, ...titleClasses }")
|
|
11
13
|
w-image.w-card__image(v-if="image" :src="image" v-bind="imgProps")
|
|
12
14
|
slot(name="image-content")
|
|
@@ -35,6 +37,7 @@ export default {
|
|
|
35
37
|
imageProps: { type: Object },
|
|
36
38
|
titleClass: { type: [String, Object, Array] },
|
|
37
39
|
contentClass: { type: [String, Object, Array] },
|
|
40
|
+
titleId: { type: String },
|
|
38
41
|
dark: { type: Boolean },
|
|
39
42
|
light: { type: Boolean }
|
|
40
43
|
},
|
|
@@ -20,6 +20,7 @@ component(
|
|
|
20
20
|
@change="onInput() /* Edge doesn't emit an `input` event on checkbox/radio/select change */"
|
|
21
21
|
@keypress.enter="onInput"
|
|
22
22
|
:aria-checked="isChecked || 'false'"
|
|
23
|
+
:aria-invalid="valid === false ? 'true' : undefined"
|
|
23
24
|
role="checkbox")
|
|
24
25
|
template(v-if="hasLabel && labelOnLeft")
|
|
25
26
|
label.w-checkbox__label.w-form-el-shakable.pr2(
|
|
@@ -20,6 +20,10 @@ w-overlay.w-dialog(
|
|
|
20
20
|
:title-class="titleClass"
|
|
21
21
|
:content-class="contentClass"
|
|
22
22
|
:title="title || undefined"
|
|
23
|
+
:title-id="(title || $slots.title) ? titleId : undefined"
|
|
24
|
+
role="dialog"
|
|
25
|
+
aria-modal="true"
|
|
26
|
+
:aria-labelledby="(title || $slots.title) ? titleId : undefined"
|
|
23
27
|
:style="contentStyles")
|
|
24
28
|
template(#title v-if="$slots.title")
|
|
25
29
|
slot(name="title")
|
|
@@ -67,6 +71,7 @@ export default {
|
|
|
67
71
|
|
|
68
72
|
data () {
|
|
69
73
|
return {
|
|
74
|
+
titleId: `w-dialog-title-${Math.random().toString(36).slice(2, 9)}`,
|
|
70
75
|
showWrapper: this.modelValue,
|
|
71
76
|
showContent: this.modelValue
|
|
72
77
|
}
|
|
@@ -21,6 +21,9 @@
|
|
|
21
21
|
v-if="showDrawer"
|
|
22
22
|
ref="drawer"
|
|
23
23
|
:is="tag || 'aside'"
|
|
24
|
+
role="dialog"
|
|
25
|
+
:aria-modal="!noOverlay ? 'true' : undefined"
|
|
26
|
+
:aria-label="ariaLabel || undefined"
|
|
24
27
|
:class="drawerClasses"
|
|
25
28
|
:style="styles"
|
|
26
29
|
:tabindex="noOverlay ? 0 : null"
|
|
@@ -46,6 +49,9 @@
|
|
|
46
49
|
v-if="showDrawer"
|
|
47
50
|
ref="drawer"
|
|
48
51
|
:is="tag || 'aside'"
|
|
52
|
+
role="dialog"
|
|
53
|
+
:aria-modal="!noOverlay ? 'true' : undefined"
|
|
54
|
+
:aria-label="ariaLabel || undefined"
|
|
49
55
|
:class="drawerClasses"
|
|
50
56
|
:style="styles"
|
|
51
57
|
:tabindex="noOverlay ? 0 : null"
|
|
@@ -85,6 +91,7 @@ export default {
|
|
|
85
91
|
overlayColor: { type: String },
|
|
86
92
|
overlayOpacity: { type: [Number, String, Boolean] },
|
|
87
93
|
drawerClass: { type: String },
|
|
94
|
+
ariaLabel: { type: String }, // Accessible label for the drawer dialog (recommended).
|
|
88
95
|
tag: { type: String, default: 'aside' },
|
|
89
96
|
dark: { type: Boolean },
|
|
90
97
|
light: { type: Boolean }
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
component.w-icon(
|
|
3
3
|
:is="tag || 'i'"
|
|
4
4
|
:class="classes"
|
|
5
|
-
role="
|
|
6
|
-
aria-
|
|
5
|
+
:role="ariaLabel ? 'img' : undefined"
|
|
6
|
+
:aria-label="ariaLabel || undefined"
|
|
7
|
+
:aria-hidden="ariaLabel ? undefined : 'true'"
|
|
7
8
|
:style="readIcon() /* Always reacting to slot change when called from template. */ && styles")
|
|
8
9
|
template(v-if="hasLigature") {{ icon }}
|
|
9
10
|
</template>
|
|
@@ -14,6 +15,7 @@ export default {
|
|
|
14
15
|
|
|
15
16
|
props: {
|
|
16
17
|
tag: { type: String, default: 'i' },
|
|
18
|
+
ariaLabel: { type: String }, // When set, exposes the icon as role="img" with this accessible label.
|
|
17
19
|
color: { type: String },
|
|
18
20
|
bgColor: { type: String },
|
|
19
21
|
xs: { type: Boolean },
|
|
@@ -46,6 +46,7 @@ component(
|
|
|
46
46
|
:maxlength="maxlength || null"
|
|
47
47
|
:readonly="isReadonly || null"
|
|
48
48
|
:aria-readonly="isReadonly ? 'true' : 'false'"
|
|
49
|
+
:aria-invalid="valid === false ? 'true' : undefined"
|
|
49
50
|
:disabled="isDisabled || null"
|
|
50
51
|
:required="required || null"
|
|
51
52
|
:tabindex="tabindex || null"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template lang="pug">
|
|
2
|
-
slot(name="activator")
|
|
2
|
+
slot(name="activator" :is-open="detachableVisible")
|
|
3
3
|
slot(v-if="!$slots.activator")
|
|
4
4
|
teleport(v-if="detachableDomReady" :to="teleportTarget" :disabled="!teleportTarget")
|
|
5
5
|
transition(:name="transitionName" appear @after-enter="onDetachableAfterEnter" @after-leave="onAfterLeave")
|
|
@@ -52,7 +52,7 @@ import { focusElement } from '../utils/focus'
|
|
|
52
52
|
|
|
53
53
|
export default {
|
|
54
54
|
name: 'w-menu',
|
|
55
|
-
expose: ['focus'],
|
|
55
|
+
expose: ['focus', 'computeDetachableCoords'],
|
|
56
56
|
mixins: [DetachableMixin],
|
|
57
57
|
inheritAttrs: false, // The attrs are only bound to the button-partial, not the root.
|
|
58
58
|
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
<template lang="pug">
|
|
2
|
-
.w-progress(
|
|
2
|
+
.w-progress(
|
|
3
|
+
role="progressbar"
|
|
4
|
+
:aria-valuenow="progressValue > -1 ? progressValue : undefined"
|
|
5
|
+
:aria-valuemin="progressValue > -1 ? 0 : undefined"
|
|
6
|
+
:aria-valuemax="progressValue > -1 ? 100 : undefined"
|
|
7
|
+
:aria-valuetext="progressValue === -1 ? 'Loading' : undefined"
|
|
8
|
+
:aria-label="ariaLabel || undefined"
|
|
9
|
+
:class="classes"
|
|
10
|
+
:style="styles")
|
|
3
11
|
//- Linear progress.
|
|
4
12
|
.w-progress__progress(
|
|
5
13
|
v-if="!circle"
|
|
@@ -43,6 +51,7 @@ export default {
|
|
|
43
51
|
|
|
44
52
|
props: {
|
|
45
53
|
modelValue: { type: [Number, String, Boolean], default: -1 },
|
|
54
|
+
ariaLabel: { type: String }, // Accessible name for the progress bar (e.g. 'Upload progress').
|
|
46
55
|
label: { type: Boolean },
|
|
47
56
|
roundCap: { type: Boolean },
|
|
48
57
|
color: { type: String, default: 'primary' },
|
|
@@ -18,6 +18,7 @@ component(
|
|
|
18
18
|
@focus="$emit('focus', $event)"
|
|
19
19
|
@change="onInput($event) /* Edge doesn't emit an `input` event on checkbox/radio/select change */"
|
|
20
20
|
:aria-checked="inputValue || 'false'"
|
|
21
|
+
:aria-invalid="valid === false ? 'true' : undefined"
|
|
21
22
|
role="radio")
|
|
22
23
|
template(v-if="hasLabel && labelOnLeft")
|
|
23
24
|
label.w-radio__label.w-form-el-shakable.pr2(
|
|
@@ -7,25 +7,27 @@ component(
|
|
|
7
7
|
@reset="$emit('update:modelValue', rating = null);$emit('input', null)"
|
|
8
8
|
:class="classes")
|
|
9
9
|
input(:id="inputId" :name="inputName" type="hidden" :value="rating")
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
10
|
+
div(role="group" :aria-label="ariaLabel || 'Rating'")
|
|
11
|
+
template(v-for="i in max" :key="i")
|
|
12
|
+
slot(v-if="$slots.item" name="item" :index="i + 1")
|
|
13
|
+
button.w-rating__button(
|
|
14
|
+
:disabled="isDisabled || isReadonly"
|
|
15
|
+
@mouseenter="hover = i"
|
|
16
|
+
@mouseleave="hover = 0"
|
|
17
|
+
@click="onButtonClick(i)"
|
|
18
|
+
@focus="onFocus"
|
|
19
|
+
@blur="onBlur"
|
|
20
|
+
@keydown="onKeydown"
|
|
21
|
+
:aria-label="`${i} of ${max}`"
|
|
22
|
+
:aria-pressed="(rating >= i).toString()"
|
|
23
|
+
:class="buttonClasses(i)"
|
|
24
|
+
type="button"
|
|
25
|
+
:tabindex="i === 1 ? 0 : -1")
|
|
26
|
+
i.w-icon(
|
|
27
|
+
v-if="i - 1 === ~~rating && rating - ~~rating"
|
|
28
|
+
:class="`${icon} ${color}`"
|
|
29
|
+
aria-hidden="true"
|
|
30
|
+
:style="halfStarStyle")
|
|
29
31
|
</template>
|
|
30
32
|
|
|
31
33
|
<script>
|
|
@@ -44,6 +46,7 @@ export default {
|
|
|
44
46
|
|
|
45
47
|
props: {
|
|
46
48
|
modelValue: {},
|
|
49
|
+
ariaLabel: { type: String }, // Accessible group label (default: 'Rating').
|
|
47
50
|
max: { type: [Number, String], default: 5 },
|
|
48
51
|
color: { type: String, default: 'primary' },
|
|
49
52
|
bgColor: { type: String },
|
|
@@ -31,7 +31,7 @@ component(
|
|
|
31
31
|
aria-haspopup="listbox"
|
|
32
32
|
:aria-expanded="showMenu ? 'true' : 'false'"
|
|
33
33
|
:aria-owns="selectListId"
|
|
34
|
-
:aria-activedescendant="
|
|
34
|
+
:aria-activedescendant="activeDescendantId"
|
|
35
35
|
:class="inputWrapClasses")
|
|
36
36
|
slot(name="icon-left")
|
|
37
37
|
w-icon.w-select__icon.w-select__icon--inner-left(
|
|
@@ -208,7 +208,7 @@ export default {
|
|
|
208
208
|
class: { 'w-select__selection--placeholder': !this.$slots.selection && !this.selectionString && this.placeholder },
|
|
209
209
|
disabled: this.isDisabled || null,
|
|
210
210
|
readonly: true,
|
|
211
|
-
|
|
211
|
+
'aria-readonly': 'true',
|
|
212
212
|
tabindex: this.tabindex ?? null,
|
|
213
213
|
contenteditable: this.isDisabled || this.isReadonly ? 'false' : 'true'
|
|
214
214
|
}
|
|
@@ -218,6 +218,15 @@ export default {
|
|
|
218
218
|
item => item[this.itemValueKey] !== undefined ? item[this.itemLabelKey] : (item[this.itemLabelKey] ?? item)
|
|
219
219
|
).join(', ')
|
|
220
220
|
},
|
|
221
|
+
|
|
222
|
+
// Points to the focused/selected list item when the menu is open.
|
|
223
|
+
activeDescendantId () {
|
|
224
|
+
if (!this.showMenu || !this.selectListId) return undefined
|
|
225
|
+
const first = this.inputValue[0]
|
|
226
|
+
if (!first) return `${this.selectListId}_item-1`
|
|
227
|
+
const idx = this.selectItems.findIndex(item => item.value === first[this.itemValueKey] || item.value === first._value)
|
|
228
|
+
return `${this.selectListId}_item-${idx >= 0 ? idx + 1 : 1}`
|
|
229
|
+
},
|
|
221
230
|
selectionHtml () {
|
|
222
231
|
if (!this.inputValue.length) return this.placeholder || ''
|
|
223
232
|
if (this.$slots.selection) return ''
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template lang="pug">
|
|
2
|
-
.w-spinner(v-if="modelValue || modelValue === undefined" :class="classes" :style="styles")
|
|
2
|
+
.w-spinner(v-if="modelValue || modelValue === undefined" role="status" :aria-label="ariaLabel || 'Loading'" :class="classes" :style="styles")
|
|
3
3
|
span(v-if="isThreeDots")
|
|
4
4
|
</template>
|
|
5
5
|
|
|
@@ -8,6 +8,7 @@ export default {
|
|
|
8
8
|
name: 'w-spinner',
|
|
9
9
|
props: {
|
|
10
10
|
modelValue: {},
|
|
11
|
+
ariaLabel: { type: String }, // Accessible label announced by screen readers (default: 'Loading').
|
|
11
12
|
color: { type: String, default: 'primary' },
|
|
12
13
|
xs: { type: Boolean },
|
|
13
14
|
sm: { type: Boolean },
|
|
@@ -9,11 +9,11 @@ span.w-tag(
|
|
|
9
9
|
:tabindex="modelValue !== -1 && 0"
|
|
10
10
|
:style="styles")
|
|
11
11
|
slot
|
|
12
|
-
|
|
12
|
+
button(
|
|
13
13
|
v-if="closable && modelValue"
|
|
14
14
|
@click.stop="$emit('update:modelValue', false);$emit('input', false)"
|
|
15
|
-
|
|
16
|
-
aria-
|
|
15
|
+
type="button"
|
|
16
|
+
aria-label="Remove"
|
|
17
17
|
class="w-icon w-tag__closable wi-cross")
|
|
18
18
|
</template>
|
|
19
19
|
|
|
@@ -38,6 +38,7 @@ component(
|
|
|
38
38
|
:cols="cols || null"
|
|
39
39
|
:readonly="isReadonly || null"
|
|
40
40
|
:aria-readonly="isReadonly ? 'true' : 'false'"
|
|
41
|
+
:aria-invalid="valid === false ? 'true' : undefined"
|
|
41
42
|
:disabled="isDisabled || null"
|
|
42
43
|
:required="required || null"
|
|
43
44
|
:tabindex="tabindex || null")
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template lang="pug">
|
|
2
|
-
.w-toolbar(:class="classes" :style="styles")
|
|
2
|
+
.w-toolbar(role="toolbar" :aria-label="ariaLabel || undefined" :class="classes" :style="styles")
|
|
3
3
|
slot
|
|
4
4
|
</template>
|
|
5
5
|
|
|
@@ -8,6 +8,7 @@ export default {
|
|
|
8
8
|
name: 'w-toolbar',
|
|
9
9
|
|
|
10
10
|
props: {
|
|
11
|
+
ariaLabel: { type: String }, // Accessible label for the toolbar (recommended when multiple toolbars are present).
|
|
11
12
|
color: { type: String },
|
|
12
13
|
bgColor: { type: String },
|
|
13
14
|
absolute: { type: Boolean },
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
<template lang="pug">
|
|
2
|
-
slot(name="activator")
|
|
2
|
+
slot(name="activator" :tooltip-id="tooltipInstanceId")
|
|
3
3
|
slot(v-if="!$slots.activator")
|
|
4
4
|
teleport(v-if="detachableDomReady" :to="teleportTarget" :disabled="!teleportTarget")
|
|
5
5
|
transition(:name="transitionName" appear @after-enter="onDetachableAfterEnter" @after-leave="onAfterLeave")
|
|
6
6
|
.w-tooltip(
|
|
7
7
|
v-if="detachableVisible"
|
|
8
8
|
ref="detachable"
|
|
9
|
+
:id="tooltipInstanceId"
|
|
10
|
+
role="tooltip"
|
|
9
11
|
:key="tooltipInstanceId"
|
|
10
12
|
:class="classes"
|
|
11
13
|
:style="styles")
|