mirror of https://github.com/ElemeFE/element
add accessibility for input & rate & collapse & progress & upload (#7196)
parent
5ce0e22823
commit
d66473f005
|
@ -163,7 +163,7 @@ Use attribute `size` to set additional sizes with `medium`, `small` or `mini`.
|
|||
|debounce| debounce delay when typing, in millisecond | number | — | 300 |
|
||||
|controls-position | position of the control buttons | string | right | - |
|
||||
|name | same as `name` in native input | string | — | — |
|
||||
|
||||
|label | label text | string | — | — |
|
||||
### Events
|
||||
|
||||
| Event Name | Description | Parameters |
|
||||
|
|
|
@ -630,7 +630,7 @@ Search data from server-side.
|
|||
|autofocus | same as `autofocus` in native input | boolean | — | false |
|
||||
|form | same as `form` in native input | string | — | — |
|
||||
| on-icon-click | hook function when clicking on the input icon | function | — | — |
|
||||
|
||||
| label | label text | string | — | — |
|
||||
### Input slot
|
||||
|
||||
| Name | Description |
|
||||
|
@ -663,7 +663,7 @@ Attribute | Description | Type | Options | Default
|
|||
| on-icon-click | hook function when clicking on the input icon | function | — | — |
|
||||
| name | same as `name` in native input | string | — | — |
|
||||
| select-when-unmatched | whether to emit a `select` event on enter when there is no autocomplete match | boolean | — | false |
|
||||
|
||||
| label | label text | string | — | — |
|
||||
### props
|
||||
| Attribute | Description | Type | Accepted Values | Default |
|
||||
| --------- | ----------------- | ------ | ------ | ------ |
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
:::demo 要使用它,只需要在`el-input-number`元素中使用`v-model`绑定变量即可,变量的初始值即为默认值。
|
||||
```html
|
||||
<template>
|
||||
<el-input-number v-model="num1" @change="handleChange" :min="1" :max="10"></el-input-number>
|
||||
<el-input-number v-model="num1" @change="handleChange" :min="1" :max="10" label="描述文字"></el-input-number>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
|
@ -162,7 +162,7 @@
|
|||
| debounce | 输入时的去抖延迟,毫秒 | number | — | 300 |
|
||||
| controls-position | 控制按钮位置 | string | right | - |
|
||||
| name | 原生属性 | string | — | — |
|
||||
|
||||
| label | 输入框关联的label文字 | string | — | — |
|
||||
### Events
|
||||
| 事件名称 | 说明 | 回调参数 |
|
||||
|---------|--------|---------|
|
||||
|
|
|
@ -785,7 +785,7 @@ export default {
|
|||
| resize | 控制是否能被用户缩放 | string | none, both, horizontal, vertical | — |
|
||||
| autofocus | 原生属性,自动获取焦点 | boolean | true, false | false |
|
||||
| form | 原生属性 | string | — | — |
|
||||
|
||||
| label | 输入框关联的label文字 | string | — | — |
|
||||
### Input slot
|
||||
| name | 说明 |
|
||||
|------|--------|
|
||||
|
@ -821,7 +821,7 @@ export default {
|
|||
| icon | 输入框尾部图标 | string | — | — |
|
||||
| name | 原生属性 | string | — | — |
|
||||
| select-when-unmatched | 在输入没有任何匹配建议的情况下,按下回车是否触发 `select` 事件 | boolean | — | false |
|
||||
|
||||
| label | 输入框关联的label文字 | string | — | — |
|
||||
### props
|
||||
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|
||||
| -------- | ----------------- | ------ | ------ | ------ |
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
class="el-autocomplete-suggestion el-popper"
|
||||
:class="{ 'is-loading': parent.loading }"
|
||||
:style="{ width: dropdownWidth }"
|
||||
role="region"
|
||||
>
|
||||
<el-scrollbar
|
||||
tag="ul"
|
||||
|
@ -44,7 +45,8 @@
|
|||
gpuAcceleration: false
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
id: String
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
@ -62,6 +64,9 @@
|
|||
mounted() {
|
||||
this.$parent.popperElm = this.popperElm = this.$el;
|
||||
this.referenceElm = this.$parent.$refs.input.$refs.input;
|
||||
this.referenceList = this.$el.querySelector('.el-autocomplete-suggestion__list');
|
||||
this.referenceList.setAttribute('role', 'listbox');
|
||||
this.referenceList.setAttribute('id', this.id);
|
||||
},
|
||||
|
||||
created() {
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
<template>
|
||||
<div class="el-autocomplete" v-clickoutside="close">
|
||||
<div
|
||||
class="el-autocomplete"
|
||||
v-clickoutside="close"
|
||||
aria-haspopup="listbox"
|
||||
role="combobox"
|
||||
:aria-expanded="suggestionVisible"
|
||||
:aria-owns="id"
|
||||
>
|
||||
<el-input
|
||||
ref="input"
|
||||
v-bind="$props"
|
||||
|
@ -13,6 +20,7 @@
|
|||
@keydown.down.native.prevent="highlight(highlightedIndex + 1)"
|
||||
@keydown.enter.native="handleKeyEnter"
|
||||
@keydown.native.tab="close"
|
||||
:label="label"
|
||||
>
|
||||
<template slot="prepend" v-if="$slots.prepend">
|
||||
<slot name="prepend"></slot>
|
||||
|
@ -24,12 +32,17 @@
|
|||
<el-autocomplete-suggestions
|
||||
visible-arrow
|
||||
:class="[popperClass ? popperClass : '']"
|
||||
ref="suggestions">
|
||||
ref="suggestions"
|
||||
:id="id">
|
||||
<li
|
||||
v-for="(item, index) in suggestions"
|
||||
:key="index"
|
||||
:class="{'highlighted': highlightedIndex === index}"
|
||||
@click="select(item)">
|
||||
@click="select(item)"
|
||||
:id="`${id}-item-${index}`"
|
||||
role="option"
|
||||
:aria-selected="highlightedIndex === index"
|
||||
>
|
||||
<slot :item="item">
|
||||
{{ item[props.label] }}
|
||||
</slot>
|
||||
|
@ -42,6 +55,7 @@
|
|||
import Clickoutside from 'element-ui/src/utils/clickoutside';
|
||||
import ElAutocompleteSuggestions from './autocomplete-suggestions.vue';
|
||||
import Emitter from 'element-ui/src/mixins/emitter';
|
||||
import { generateId } from 'element-ui/src/utils/util';
|
||||
|
||||
export default {
|
||||
name: 'ElAutocomplete',
|
||||
|
@ -85,7 +99,8 @@
|
|||
selectWhenUnmatched: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
label: String
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -101,6 +116,9 @@
|
|||
const suggestions = this.suggestions;
|
||||
let isValidData = Array.isArray(suggestions) && suggestions.length > 0;
|
||||
return (isValidData || this.loading) && this.activated;
|
||||
},
|
||||
id() {
|
||||
return `el-autocomplete-${generateId()}`;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
@ -191,14 +209,19 @@
|
|||
if (offsetTop < scrollTop) {
|
||||
suggestion.scrollTop -= highlightItem.scrollHeight;
|
||||
}
|
||||
|
||||
this.highlightedIndex = index;
|
||||
this.$el.querySelector('.el-input__inner').setAttribute('aria-activedescendant', `${this.id}-item-${this.highlightedIndex}`);
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$on('item-click', item => {
|
||||
this.select(item);
|
||||
});
|
||||
let $input = this.$el.querySelector('.el-input__inner');
|
||||
$input.setAttribute('role', 'textbox');
|
||||
$input.setAttribute('aria-autocomplete', 'list');
|
||||
$input.setAttribute('aria-controls', 'id');
|
||||
$input.setAttribute('aria-activedescendant', `${this.id}-item-${this.highlightedIndex}`);
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.$refs.suggestions.$destroy();
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
<template>
|
||||
<button
|
||||
v-bind="$props"
|
||||
class="el-button"
|
||||
@click="handleClick"
|
||||
:type="nativeType"
|
||||
|
|
|
@ -1,11 +1,35 @@
|
|||
<template>
|
||||
<div class="el-collapse-item" :class="{'is-active': isActive}">
|
||||
<div class="el-collapse-item__header" @click="handleHeaderClick">
|
||||
<i class="el-collapse-item__arrow el-icon-arrow-right"></i>
|
||||
<slot name="title">{{title}}</slot>
|
||||
<div
|
||||
role="tab"
|
||||
:aria-expanded="isActive"
|
||||
:aria-controls="`el-collapse-content-${id}`"
|
||||
:aria-describedby ="`el-collapse-content-${id}`"
|
||||
>
|
||||
<div
|
||||
class="el-collapse-item__header"
|
||||
@click="handleHeaderClick"
|
||||
role="button"
|
||||
:id="`el-collapse-head-${id}`"
|
||||
tabindex="0"
|
||||
@keyup.space.enter.stop="handleEnterClick"
|
||||
:class="{'focusing': focusing}"
|
||||
@focus="focusing = true"
|
||||
@blur="focusing = false"
|
||||
>
|
||||
<i class="el-collapse-item__arrow el-icon-arrow-right"></i>
|
||||
<slot name="title">{{title}}</slot>
|
||||
</div>
|
||||
</div>
|
||||
<el-collapse-transition>
|
||||
<div class="el-collapse-item__wrap" v-show="isActive">
|
||||
<div
|
||||
class="el-collapse-item__wrap"
|
||||
v-show="isActive"
|
||||
role="tabpanel"
|
||||
:aria-hidden="!isActive"
|
||||
:aria-labelledby="`el-collapse-head-${id}`"
|
||||
:id="`el-collapse-content-${id}`"
|
||||
>
|
||||
<div class="el-collapse-item__content">
|
||||
<slot></slot>
|
||||
</div>
|
||||
|
@ -16,6 +40,7 @@
|
|||
<script>
|
||||
import ElCollapseTransition from 'element-ui/src/transitions/collapse-transition';
|
||||
import Emitter from 'element-ui/src/mixins/emitter';
|
||||
import { generateId } from 'element-ui/src/utils/util';
|
||||
|
||||
export default {
|
||||
name: 'ElCollapseItem',
|
||||
|
@ -32,7 +57,8 @@
|
|||
height: 'auto',
|
||||
display: 'block'
|
||||
},
|
||||
contentHeight: 0
|
||||
contentHeight: 0,
|
||||
focusing: false
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -49,6 +75,9 @@
|
|||
computed: {
|
||||
isActive() {
|
||||
return this.$parent.activeNames.indexOf(this.name) > -1;
|
||||
},
|
||||
id() {
|
||||
return generateId();
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -60,6 +89,10 @@
|
|||
methods: {
|
||||
handleHeaderClick() {
|
||||
this.dispatch('ElCollapse', 'item-click', this);
|
||||
this.focusing = false;
|
||||
},
|
||||
handleEnterClick() {
|
||||
this.dispatch('ElCollapse', 'item-click', this);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="el-collapse">
|
||||
<div class="el-collapse" role="tablist" aria-multiselectable="true">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
class="el-input-number__decrease"
|
||||
:class="{'is-disabled': minDisabled}"
|
||||
v-repeat-click="decrease"
|
||||
@keydown.enter="decrease"
|
||||
role="button"
|
||||
>
|
||||
<i :class="`el-icon-${controlsAtRight ? 'arrow-down' : 'minus'}`"></i>
|
||||
</span>
|
||||
|
@ -20,6 +22,8 @@
|
|||
class="el-input-number__increase"
|
||||
:class="{'is-disabled': maxDisabled}"
|
||||
v-repeat-click="increase"
|
||||
@keydown.enter="increase"
|
||||
role="button"
|
||||
>
|
||||
<i :class="`el-icon-${controlsAtRight ? 'arrow-up' : 'plus'}`"></i>
|
||||
</span>
|
||||
|
@ -36,6 +40,7 @@
|
|||
:min="min"
|
||||
:name="name"
|
||||
ref="input"
|
||||
:label="label"
|
||||
>
|
||||
<template slot="prepend" v-if="$slots.prepend">
|
||||
<slot name="prepend"></slot>
|
||||
|
@ -111,7 +116,8 @@
|
|||
type: Number,
|
||||
default: 300
|
||||
},
|
||||
name: String
|
||||
name: String,
|
||||
label: String
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -223,6 +229,18 @@
|
|||
this.debounceHandleInput = debounce(this.debounce, value => {
|
||||
this.handleInput(value);
|
||||
});
|
||||
},
|
||||
mounted() {
|
||||
let innerInput = this.$refs.input.$refs.input;
|
||||
innerInput.setAttribute('role', 'spinbutton');
|
||||
innerInput.setAttribute('aria-valuemax', this.max);
|
||||
innerInput.setAttribute('aria-valuemin', this.min);
|
||||
innerInput.setAttribute('aria-valuenow', this.currentValue);
|
||||
innerInput.setAttribute('aria-disabled', this.disabled);
|
||||
},
|
||||
updated() {
|
||||
let innerInput = this.$refs.input.$refs.input;
|
||||
innerInput.setAttribute('aria-valuenow', this.currentValue);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -13,17 +13,9 @@
|
|||
]">
|
||||
<template v-if="type !== 'textarea'">
|
||||
<!-- 前置元素 -->
|
||||
<div class="el-input-group__prepend" v-if="$slots.prepend">
|
||||
<div class="el-input-group__prepend" v-if="$slots.prepend" tabindex="0">
|
||||
<slot name="prepend"></slot>
|
||||
</div>
|
||||
<!-- 前置内容 -->
|
||||
<span class="el-input__prefix" v-if="$slots.prefix || prefixIcon">
|
||||
<slot name="prefix"></slot>
|
||||
<i class="el-input__icon"
|
||||
v-if="prefixIcon"
|
||||
:class="prefixIcon">
|
||||
</i>
|
||||
</span>
|
||||
<input
|
||||
v-if="type !== 'textarea'"
|
||||
class="el-input__inner"
|
||||
|
@ -34,7 +26,16 @@
|
|||
@input="handleInput"
|
||||
@focus="handleFocus"
|
||||
@blur="handleBlur"
|
||||
:aria-label="label"
|
||||
>
|
||||
<!-- 前置内容 -->
|
||||
<span class="el-input__prefix" v-if="$slots.prefix || prefixIcon">
|
||||
<slot name="prefix"></slot>
|
||||
<i class="el-input__icon"
|
||||
v-if="prefixIcon"
|
||||
:class="prefixIcon">
|
||||
</i>
|
||||
</span>
|
||||
<!-- 后置内容 -->
|
||||
<span class="el-input__suffix" v-if="$slots.suffix || suffixIcon || validateState">
|
||||
<span class="el-input__suffix-inner">
|
||||
|
@ -63,7 +64,9 @@
|
|||
v-bind="$props"
|
||||
:style="textareaStyle"
|
||||
@focus="handleFocus"
|
||||
@blur="handleBlur">
|
||||
@blur="handleBlur"
|
||||
:aria-label="label"
|
||||
>
|
||||
</textarea>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -127,7 +130,8 @@
|
|||
},
|
||||
onIconClick: Function,
|
||||
suffixIcon: String,
|
||||
prefixIcon: String
|
||||
prefixIcon: String,
|
||||
label: String
|
||||
},
|
||||
|
||||
computed: {
|
||||
|
|
|
@ -9,6 +9,10 @@
|
|||
'el-progress--text-inside': textInside,
|
||||
}
|
||||
]"
|
||||
role="progressbar"
|
||||
:aria-valuenow="percentage"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100"
|
||||
>
|
||||
<div class="el-progress-bar" v-if="type === 'line'">
|
||||
<div class="el-progress-bar__outer" :style="{height: strokeWidth + 'px'}">
|
||||
|
|
|
@ -1,12 +1,24 @@
|
|||
<template>
|
||||
<div class="el-rate">
|
||||
<div class="el-rate"
|
||||
@keydown="handelKey"
|
||||
role="slider"
|
||||
:aria-valuenow="currentValue"
|
||||
:aria-valuetext="text"
|
||||
aria-valuemin="0"
|
||||
:aria-valuemin="max"
|
||||
tabindex="0"
|
||||
@focus="focusing = true"
|
||||
@blur="focusing = false"
|
||||
:class="{'focusing': focusing}"
|
||||
>
|
||||
<span
|
||||
v-for="item in max"
|
||||
class="el-rate__item"
|
||||
@mousemove="setCurrentValue(item, $event)"
|
||||
@mouseleave="resetCurrentValue"
|
||||
@click="selectValue(item)"
|
||||
:style="{ cursor: disabled ? 'auto' : 'pointer' }">
|
||||
:style="{ cursor: disabled ? 'auto' : 'pointer' }"
|
||||
>
|
||||
<i
|
||||
:class="[classes[item - 1], { 'hover': hoverIndex === item }]"
|
||||
class="el-rate__icon"
|
||||
|
@ -34,7 +46,8 @@
|
|||
classMap: {},
|
||||
pointerAtLeftHalf: true,
|
||||
currentValue: this.value,
|
||||
hoverIndex: -1
|
||||
hoverIndex: -1,
|
||||
focusing: false
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -237,6 +250,34 @@
|
|||
this.$emit('input', value);
|
||||
this.$emit('change', value);
|
||||
}
|
||||
this.focusing = false;
|
||||
},
|
||||
|
||||
handelKey(e) {
|
||||
let currentValue = this.currentValue;
|
||||
const keyCode = e.keyCode;
|
||||
if (keyCode === 38 || keyCode === 39) { // left / down
|
||||
if (this.allowHalf) {
|
||||
currentValue += 0.5;
|
||||
} else {
|
||||
currentValue += 1;
|
||||
}
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
} else if (keyCode === 37 || keyCode === 40) {
|
||||
if (this.allowHalf) {
|
||||
currentValue -= 0.5;
|
||||
} else {
|
||||
currentValue -= 1;
|
||||
}
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}
|
||||
currentValue = currentValue < 0 ? 0 : currentValue;
|
||||
currentValue = currentValue > this.max ? this.max : currentValue;
|
||||
|
||||
this.$emit('input', currentValue);
|
||||
this.$emit('change', currentValue);
|
||||
},
|
||||
|
||||
setCurrentValue(value, event) {
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
<template>
|
||||
<label class="el-switch" :class="{ 'is-disabled': disabled, 'is-checked': checked }">
|
||||
<div
|
||||
class="el-switch"
|
||||
:class="{ 'is-disabled': disabled, 'is-checked': checked }"
|
||||
role="switch"
|
||||
:aria-checked="checked"
|
||||
:aria-disabled="disabled"
|
||||
@click="switchValue"
|
||||
>
|
||||
<input
|
||||
class="el-switch__input"
|
||||
type="checkbox"
|
||||
|
@ -8,12 +15,14 @@
|
|||
:name="name"
|
||||
:true-value="onValue"
|
||||
:false-value="offValue"
|
||||
:disabled="disabled">
|
||||
:disabled="disabled"
|
||||
@keydown.enter="switchValue"
|
||||
>
|
||||
<span
|
||||
:class="['el-switch__label', 'el-switch__label--left', !checked ? 'is-active' : '']"
|
||||
v-if="offIconClass || offText">
|
||||
<i :class="[offIconClass]" v-if="offIconClass"></i>
|
||||
<span v-if="!offIconClass && offText">{{ offText }}</span>
|
||||
<span v-if="!offIconClass && offText" :aria-hidden="checked">{{ offText }}</span>
|
||||
</span>
|
||||
<span class="el-switch__core" ref="core" :style="{ 'width': coreWidth + 'px' }">
|
||||
<span class="el-switch__button" :style="{ transform }"></span>
|
||||
|
@ -22,11 +31,10 @@
|
|||
:class="['el-switch__label', 'el-switch__label--right', checked ? 'is-active' : '']"
|
||||
v-if="onIconClass || onText">
|
||||
<i :class="[onIconClass]" v-if="onIconClass"></i>
|
||||
<span v-if="!onIconClass && onText">{{ onText }}</span>
|
||||
<span v-if="!onIconClass && onText" :aria-hidden="!checked">{{ onText }}</span>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ElSwitch',
|
||||
|
@ -114,6 +122,9 @@
|
|||
let newColor = this.checked ? this.onColor : this.offColor;
|
||||
this.$refs.core.style.borderColor = newColor;
|
||||
this.$refs.core.style.backgroundColor = newColor;
|
||||
},
|
||||
switchValue() {
|
||||
this.$refs.input.click();
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
|
|
@ -16,6 +16,9 @@
|
|||
font-size: $--collapse-header-size;
|
||||
font-weight: 500;
|
||||
transition: border-bottom-color .3s;
|
||||
&:focus:not(.focusing), &:active {
|
||||
outline-width: 0;
|
||||
}
|
||||
|
||||
@include e(arrow) {
|
||||
margin-right: 8px;
|
||||
|
@ -42,9 +45,8 @@
|
|||
}
|
||||
|
||||
@include when(active) {
|
||||
> .el-collapse-item__header {
|
||||
.el-collapse-item__header {
|
||||
border-bottom-color: transparent;
|
||||
|
||||
.el-collapse-item__arrow {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
|
|
@ -5,6 +5,10 @@
|
|||
height: $--rate-height;
|
||||
line-height: 1;
|
||||
|
||||
&:focus:not(.focusing), &:active {
|
||||
outline-width: 0;
|
||||
}
|
||||
|
||||
@include e(item) {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
|
|
|
@ -43,7 +43,13 @@
|
|||
}
|
||||
|
||||
@include e(input) {
|
||||
display: none;
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
opacity: 0;
|
||||
&:focus ~ .el-switch__core {
|
||||
outline: 1px solid #f00;
|
||||
}
|
||||
}
|
||||
|
||||
@include e(core) {
|
||||
|
|
|
@ -156,6 +156,17 @@
|
|||
}
|
||||
}
|
||||
|
||||
& .el-icon-close-tip {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: 0;
|
||||
cursor: pointer;
|
||||
opacity: 1;
|
||||
color: $--color-primary;
|
||||
transform: translate(15%,0) scale(.7);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: $--background-color-base;
|
||||
|
||||
|
@ -173,12 +184,25 @@
|
|||
display: block;
|
||||
}
|
||||
|
||||
.el-upload-list__item-name:hover {
|
||||
.el-upload-list__item-name:hover, .el-upload-list__item-name:focus {
|
||||
color: $--link-hover-color;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
&:focus {
|
||||
.el-icon-close-tip {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
&:focus:not(.focusing), &:active {
|
||||
outline-width: 0;
|
||||
.el-icon-close-tip {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover, &:focus { /*键盘焦点时 显示提示文字 focus*/
|
||||
.el-upload-list__item-status-label {
|
||||
display: none;
|
||||
}
|
||||
|
@ -255,7 +279,6 @@
|
|||
.el-icon-close {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.el-upload-list__item-status-label {
|
||||
display: none;
|
||||
|
@ -378,7 +401,6 @@
|
|||
.el-upload-list__item-name {
|
||||
line-height: 70px;
|
||||
margin-top: 0;
|
||||
|
||||
i {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -10,8 +10,13 @@
|
|||
>
|
||||
<li
|
||||
v-for="(file, index) in files"
|
||||
:class="['el-upload-list__item', 'is-' + file.status]"
|
||||
:class="['el-upload-list__item', 'is-' + file.status, focusing ? 'focusing' : '']"
|
||||
:key="index"
|
||||
tabindex="0"
|
||||
@keydown.delete="$emit('remove', file)"
|
||||
@focus="focusing = true"
|
||||
@blur="focusing = false"
|
||||
@click="focusing = false"
|
||||
>
|
||||
<img
|
||||
class="el-upload-list__item-thumbnail"
|
||||
|
@ -29,13 +34,14 @@
|
|||
}"></i>
|
||||
</label>
|
||||
<i class="el-icon-close" v-if="!disabled" @click="$emit('remove', file)"></i>
|
||||
<i class="el-icon-close-tip" v-if="!disabled">按delete键可删除</i> <!--因为close按钮只在li:focus的时候 display, li blur后就不存在了,所以键盘导航时永远无法 focus到 close按钮上-->
|
||||
<el-progress
|
||||
v-if="file.status === 'uploading'"
|
||||
:type="listType === 'picture-card' ? 'circle' : 'line'"
|
||||
:stroke-width="listType === 'picture-card' ? 6 : 2"
|
||||
:percentage="parsePercentage(file.percentage)">
|
||||
</el-progress>
|
||||
<span class="el-upload-list__item-actions" v-if="listType === 'picture-card'">
|
||||
<span class="el-upload-list__item-actions" v-if="listType === 'picture-card'">
|
||||
<span
|
||||
class="el-upload-list__item-preview"
|
||||
v-if="handlePreview && listType === 'picture-card'"
|
||||
|
@ -61,6 +67,11 @@
|
|||
export default {
|
||||
mixins: [Locale],
|
||||
|
||||
data() {
|
||||
return {
|
||||
focusing: false
|
||||
};
|
||||
},
|
||||
components: { ElProgress },
|
||||
|
||||
props: {
|
||||
|
|
|
@ -145,6 +145,11 @@ export default {
|
|||
this.$refs.input.value = null;
|
||||
this.$refs.input.click();
|
||||
}
|
||||
},
|
||||
handleKeydown(e) {
|
||||
if (e.keyCode === 13 || e.keyCode === 32) {
|
||||
this.handleClick();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -158,19 +163,21 @@ export default {
|
|||
accept,
|
||||
listType,
|
||||
uploadFiles,
|
||||
disabled
|
||||
disabled,
|
||||
handleKeydown
|
||||
} = this;
|
||||
const data = {
|
||||
class: {
|
||||
'el-upload': true
|
||||
},
|
||||
on: {
|
||||
click: handleClick
|
||||
click: handleClick,
|
||||
keydown: handleKeydown
|
||||
}
|
||||
};
|
||||
data.class[`el-upload--${listType}`] = true;
|
||||
return (
|
||||
<div {...data}>
|
||||
<div {...data} tabindex="0" >
|
||||
{
|
||||
drag
|
||||
? <upload-dragger disabled={disabled} on-file={uploadFiles}>{this.$slots.default}</upload-dragger>
|
||||
|
|
|
@ -37,3 +37,8 @@ export const getValueByPath = function(object, prop) {
|
|||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
export const generateId = function() {
|
||||
return Math.floor(Math.random() * 10000);
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue