Commit de86b737 authored by Leopoldthecoder's avatar Leopoldthecoder

Merge remote-tracking branch 'eleme/dev' into es-doc

# Conflicts:
#	examples/app.vue
parents 496ad686 f24aeeca
......@@ -6,7 +6,7 @@
"expect": true,
"sinon": true
},
"plugins": ['vue'],
"plugins": ['vue','json'],
"extends": 'elemefe',
"parserOptions": {
"ecmaFeatures": {
......
## Changelog
### 2.0.4
*2017-11-10*
- Improved accessibility for Cascader, Dropdown, Message, Notification, Popover, Tooltip and Tree
- Fixed Container resize when the width of viewport decreases, #8042
- Fixed Tree's `updateKeyChildren` incorrectly deleting child nodes, #8100
- Fixed bordered CheckboxButton's height when nested in a Form, #8100
- Fixed Menu's parsing error for custom colors, #8153 (by @zhouyixiang)
### 2.0.3
*2017-11-03*
......
## 更新日志
### 2.0.4
*2017-11-10*
- 提升 Cascader、Dropdown、Message、Notification、Popover、Tooltip、Tree 的可访问性
- 修复当视口变窄时 Container 无法同步更新其宽度的问题,#8042
- 修复 Tree 的 `updateKeyChildren` 在删除子节点时的行为错误,#8100
- 修复带有边框的 CheckboxButton 在 Form 中高度错误的问题,#8100
- 修复 Menu 在解析自定义颜色时的错误,#8153(by @zhouyixiang)
### 2.0.3
*2017-11-03*
......
......@@ -191,7 +191,6 @@
const lang = location.hash.replace('#', '').split('/')[1] || 'zh-CN';
const localize = lang => {
console.log(lang);
switch (lang) {
case 'zh-CN':
use(zhLocale);
......@@ -232,6 +231,7 @@
const preferGithub = localStorage.getItem('PREFER_GITHUB');
if (href.indexOf('element-cn') > -1 || preferGithub) return;
setTimeout(() => {
if (this.lang !== 'zh-CN') return;
this.$confirm('建议大陆用户访问部署在国内的站点,是否跳转?', '提示')
.then(() => {
location.href = location.href.replace('element.', 'element-cn.');
......@@ -249,12 +249,12 @@
this.suggestJump();
}
setTimeout(() => {
const notified = localStorage.getItem('RELEASE_NOTIFIED');
if (!notified) {
const notified = localStorage.getItem('ES_NOTIFIED');
if (!notified && this.lang !== 'zh-CN') {
const h = this.$createElement;
const title = this.lang === 'zh-CN'
? '2.0 正式发布'
: '2.0 available now';
? '帮助我们完成西班牙语文档'
: 'Help us with Spanish docs';
const messages = this.lang === 'zh-CN'
? ['点击', '这里', '查看详情']
: ['Click ', 'here', ' to learn more'];
......@@ -266,13 +266,13 @@
h('a', {
attrs: {
target: '_blank',
href: `https://github.com/ElemeFE/element/issues/${ this.lang === 'zh-CN' ? '7755' : '7756' }`
href: 'https://github.com/ElemeFE/element/issues/8074'
}
}, messages[1]),
messages[2]
]),
onClose() {
localStorage.setItem('RELEASE_NOTIFIED', 1);
localStorage.setItem('ES_NOTIFIED', 1);
}
});
}
......
......@@ -101,6 +101,7 @@
value8: '',
value9: [],
value10: [],
value11: [],
loading: false,
states: ["Alabama", "Alaska", "Arizona", "Arkansas", "California", "Colorado", "Connecticut", "Delaware", "Florida", "Georgia", "Hawaii", "Idaho", "Illinois", "Indiana", "Iowa", "Kansas", "Kentucky", "Louisiana", "Maine", "Maryland", "Massachusetts", "Michigan", "Minnesota", "Mississippi", "Missouri", "Montana", "Nebraska", "Nevada", "New Hampshire", "New Jersey", "New Mexico", "New York", "North Carolina", "North Dakota", "Ohio", "Oklahoma", "Oregon", "Pennsylvania", "Rhode Island", "South Carolina", "South Dakota", "Tennessee", "Texas", "Utah", "Vermont", "Virginia", "Washington", "West Virginia", "Wisconsin", "Wyoming"]
};
......@@ -320,7 +321,7 @@ You can clear Select using a clear icon.
Multiple select uses tags to display selected options.
:::demo Set `multiple` attribute for `el-select` to enable multiple mode. In this case, the value of `v-model` will be an array of selected options.
:::demo Set `multiple` attribute for `el-select` to enable multiple mode. In this case, the value of `v-model` will be an array of selected options. By default the selected options will be displayed as Tags. You can collapse them to a text by using `collapse-tags` attribute.
```html
<template>
<el-select v-model="value5" multiple placeholder="Select">
......@@ -331,6 +332,20 @@ Multiple select uses tags to display selected options.
:value="item.value">
</el-option>
</el-select>
<el-select
v-model="value11"
multiple
collapse-tags
style="margin-left: 20px;"
placeholder="Select">
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</template>
<script>
......@@ -650,6 +665,7 @@ If the binding value of Select is an object, make sure to assign `value-key` as
| value-key | unique identity key name for value, required when value is an object | string | — | value |
| size | size of Input | string | large/small/mini | — |
| clearable | whether single select can be cleared | boolean | — | false |
| collapse-tags | whether to collapse tags to a text when multiple selecting | boolean | — | false |
| multiple-limit | maximum number of options user can select when `multiple` is `true`. No limit when set to 0 | number | — | 0 |
| name | the name attribute of select input | string | — | — |
| placeholder | placeholder | string | — | Select |
......
......@@ -101,6 +101,7 @@
value8: '',
value9: '',
value10: [],
value11: [],
loading: false,
states: ["Alabama", "Alaska", "Arizona", "Arkansas", "California", "Colorado", "Connecticut", "Delaware", "Florida", "Georgia", "Hawaii", "Idaho", "Illinois", "Indiana", "Iowa", "Kansas", "Kentucky", "Louisiana", "Maine", "Maryland", "Massachusetts", "Michigan", "Minnesota", "Mississippi", "Missouri", "Montana", "Nebraska", "Nevada", "New Hampshire", "New Jersey", "New Mexico", "New York", "North Carolina", "North Dakota", "Ohio", "Oklahoma", "Oregon", "Pennsylvania", "Rhode Island", "South Carolina", "South Dakota", "Tennessee", "Texas", "Utah", "Vermont", "Virginia", "Washington", "West Virginia", "Wisconsin", "Wyoming"]
};
......@@ -319,7 +320,7 @@
适用性较广的基础多选,用 Tag 展示已选项
:::demo 为`el-select`设置`multiple`属性即可启用多选,此时`v-model`的值为当前选中值所组成的数组
:::demo 为`el-select`设置`multiple`属性即可启用多选,此时`v-model`的值为当前选中值所组成的数组。默认情况下选中值会以 Tag 的形式展现,你也可以设置`collapse-tags`属性将它们合并为一段文字。
```html
<template>
<el-select v-model="value5" multiple placeholder="请选择">
......@@ -330,6 +331,20 @@
:value="item.value">
</el-option>
</el-select>
<el-select
v-model="value11"
multiple
collapse-tags
style="margin-left: 20px;"
placeholder="请选择">
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</template>
<script>
......@@ -645,6 +660,7 @@
| value-key | 作为 value 唯一标识的键名,绑定值为对象类型时必填 | string | — | value |
| size | 输入框尺寸 | string | large/small/mini | — |
| clearable | 单选时是否可以清空选项 | boolean | — | false |
| collapse-tags | 多选时是否将选中值按文字的形式展示 | boolean | — | false |
| multiple-limit | 多选时用户最多可以选择的项目数,为 0 则不限制 | number | — | 0 |
| name | select input 的 name 属性 | string | — | — |
| placeholder | 占位符 | string | — | 请选择 |
......
......@@ -10,7 +10,7 @@
},
{
"name": "Element Angular",
"href": "https://eleme.github.io/element-angular/"
"href": "https://element-angular.faas.ele.me/"
},
{
"name": "开发指南",
......@@ -260,7 +260,7 @@
},
{
"name": "Element Angular",
"href": "https://eleme.github.io/element-angular/"
"href": "https://element-angular.faas.ele.me/"
},
{
"name": "Development",
......
{"1.0.9":"1.0","1.1.6":"1.1","1.2.9":"1.2","1.3.7":"1.3","1.4.9":"1.4","2.0.3":"2.0"}
\ No newline at end of file
{"1.0.9":"1.0","1.1.6":"1.1","1.2.9":"1.2","1.3.7":"1.3","1.4.9":"1.4","2.0.4":"2.0"}
\ No newline at end of file
{
"name": "element-ui",
"version": "2.0.3",
"version": "2.0.4",
"description": "A Component Library for Vue.js.",
"main": "lib/element-ui.common.js",
"files": [
......@@ -82,6 +82,7 @@
"css-loader": "^0.28.7",
"es6-promise": "^4.0.5",
"eslint": "^3.10.2",
"eslint-plugin-json": "^1.2.0",
"extract-text-webpack-plugin": "^3.0.1",
"file-loader": "^1.1.5",
"file-save": "^0.2.0",
......
......@@ -10,9 +10,12 @@
]"
@click="handleClick"
@mouseenter="inputHover = true"
@focus="inputHover = true"
@mouseleave="inputHover = false"
@blur="inputHover = false"
ref="reference"
v-clickoutside="handleClickoutside"
@keydown="handleKeydown"
>
<el-input
ref="input"
......@@ -63,6 +66,7 @@ import emitter from 'element-ui/src/mixins/emitter';
import Locale from 'element-ui/src/mixins/locale';
import { t } from 'element-ui/src/locale';
import debounce from 'throttle-debounce/debounce';
import { generateId } from 'element-ui/src/utils/util';
const popperMixin = {
props: {
......@@ -149,6 +153,10 @@ export default {
beforeFilter: {
type: Function,
default: () => (() => {})
},
hoverThreshold: {
type: Number,
default: 500
}
},
......@@ -191,11 +199,15 @@ export default {
},
cascaderSize() {
return this.size || this._elFormItemSize || (this.$ELEMENT || {}).size;
},
id() {
return generateId();
}
},
watch: {
menuVisible(value) {
this.$refs.input.$refs.input.setAttribute('aria-expanded', value);
value ? this.showMenu() : this.hideMenu();
},
value(value) {
......@@ -204,6 +216,10 @@ export default {
currentValue(value) {
this.dispatch('ElFormItem', 'el.form.change', [value]);
},
currentLabels(value) {
const inputLabel = this.showAllLevels ? value.join('/') : value[value.length - 1] ;
this.$refs.input.$refs.input.setAttribute('value', inputLabel);
},
options: {
deep: true,
handler(value) {
......@@ -224,10 +240,13 @@ export default {
this.menu.expandTrigger = this.expandTrigger;
this.menu.changeOnSelect = this.changeOnSelect;
this.menu.popperClass = this.popperClass;
this.menu.hoverThreshold = this.hoverThreshold;
this.popperElm = this.menu.$el;
this.menu.$refs.menus[0].setAttribute('id', `cascader-menu-${this.id}`);
this.menu.$on('pick', this.handlePick);
this.menu.$on('activeItemChange', this.handleActiveItemChange);
this.menu.$on('menuLeave', this.doDestroy);
this.menu.$on('closeInside', this.handleClickoutside);
},
showMenu() {
if (!this.menu) {
......@@ -245,6 +264,7 @@ export default {
hideMenu() {
this.inputValue = '';
this.menu.visible = false;
this.$refs.input.focus();
},
handleActiveItemChange(value) {
this.$nextTick(_ => {
......@@ -252,6 +272,23 @@ export default {
});
this.$emit('active-item-change', value);
},
handleKeydown(e) {
const keyCode = e.keyCode;
if (keyCode === 13) {
this.handleClick();
} else if (keyCode === 40) { // down
this.menuVisible = true; // 打开
setTimeout(() => {
const firstMenu = this.popperElm.querySelectorAll('.el-cascader-menu')[0];
firstMenu.querySelectorAll("[tabindex='-1']")[0].focus();
});
e.stopPropagation();
e.preventDefault();
} else if (keyCode === 27 || keyCode === 9) { // esc tab
this.inputValue = '';
if (this.menu) this.menu.visible = false;
}
},
handlePick(value, close = true) {
this.currentValue = value;
this.$emit('input', value);
......
<script>
import { isDef } from 'element-ui/src/utils/shared';
import scrollIntoView from 'element-ui/src/utils/scroll-into-view';
import { generateId } from 'element-ui/src/utils/util';
const copyArray = (arr, props) => {
if (!arr || !Array.isArray(arr) || !props) return arr;
......@@ -39,7 +40,8 @@
value: [],
expandTrigger: 'click',
changeOnSelect: false,
popperClass: ''
popperClass: '',
hoverTimer: 0
};
},
......@@ -94,6 +96,9 @@
formatOptions(optionsCopy);
return loadActiveOptions(optionsCopy);
}
},
id() {
return generateId();
}
},
......@@ -135,11 +140,41 @@
activeOptions,
visible,
expandTrigger,
popperClass
popperClass,
hoverThreshold
} = this;
let itemId = null;
let itemIndex = 0;
let hoverMenuRefs = {};
const hoverMenuHandler = e => {
const offsetX = e.offsetX;
const width = hoverMenuRefs.activeMenu.offsetWidth;
const height = hoverMenuRefs.activeMenu.offsetHeight;
if (e.target === hoverMenuRefs.activeItem) {
clearTimeout(this.hoverTimer);
const {activeItem} = hoverMenuRefs;
const offsetY_top = activeItem.offsetTop;
const offsetY_Bottom = offsetY_top + activeItem.offsetHeight;
hoverMenuRefs.hoverZone.innerHTML = `
<path style="pointer-events: auto;" fill="transparent" d="M${offsetX} ${offsetY_top} L${width} 0 V${offsetY_top} Z" />
<path style="pointer-events: auto;" fill="transparent" d="M${offsetX} ${offsetY_Bottom} L${width} ${height} V${offsetY_Bottom} Z" />
`;
} else {
if (!this.hoverTimer) {
this.hoverTimer = setTimeout(() => {
hoverMenuRefs.hoverZone.innerHTML = '';
}, hoverThreshold);
}
}
};
const menus = this._l(activeOptions, (menu, menuIndex) => {
let isFlat = false;
const menuId = `menu-${this.id}-${ menuIndex}`;
const ownsId = `menu-${this.id}-${ menuIndex + 1 }`;
const items = this._l(menu, item => {
const events = {
on: {}
......@@ -148,12 +183,52 @@
if (item.__IS__FLAT__OPTIONS) isFlat = true;
if (!item.disabled) {
// keydown up/down/left/right/enter
events.on.keydown = (ev) => {
const keyCode = ev.keyCode;
if (![37, 38, 39, 40, 13, 9, 27].indexOf(keyCode) > -1) {
return;
}
const currentEle = ev.target;
const parentEle = this.$refs.menus[menuIndex];
const menuItemList = parentEle.querySelectorAll("[tabindex='-1']");
const currentIndex = Array.prototype.indexOf.call(menuItemList, currentEle); // 当前索引
let nextIndex, nextMenu;
if ([38, 40].indexOf(keyCode) > -1) {
if (keyCode === 38) { // up键
nextIndex = currentIndex !== 0 ? (currentIndex - 1) : currentIndex;
} else if (keyCode === 40) { // down
nextIndex = currentIndex !== (menuItemList.length - 1) ? currentIndex + 1 : currentIndex;
}
menuItemList[nextIndex].focus();
} else if (keyCode === 37) { // left键
if (menuIndex !== 0) {
const previousMenu = this.$refs.menus[menuIndex - 1];
previousMenu.querySelector('[aria-expanded=true]').focus();
}
} else if (keyCode === 39) { // right
if (item.children) {
// 有子menu 选择子menu的第一个menuitem
nextMenu = this.$refs.menus[menuIndex + 1];
nextMenu.querySelectorAll("[tabindex='-1']")[0].focus();
}
} else if (keyCode === 13) {
if (!item.children) {
const id = currentEle.getAttribute('id');
parentEle.setAttribute('aria-activedescendant', id);
this.select(item, menuIndex);
this.$nextTick(() => this.scrollMenu(this.$refs.menus[menuIndex]));
}
} else if (keyCode === 9 || keyCode === 27) { // esc tab
this.$emit('closeInside');
}
};
if (item.children) {
let triggerEvent = {
click: 'click',
hover: 'mouseenter'
}[expandTrigger];
events.on[triggerEvent] = () => {
events.on[triggerEvent] = events.on['focus'] = () => { // focus 选中
this.activeItem(item, menuIndex);
this.$nextTick(() => {
// adjust self and next level
......@@ -168,7 +243,10 @@
};
}
}
if (!item.disabled && !item.children) { // no children set id
itemId = `${menuId}-${itemIndex}`;
itemIndex++;
}
return (
<li
class={{
......@@ -177,7 +255,14 @@
'is-active': item.value === activeValue[menuIndex],
'is-disabled': item.disabled
}}
ref={item.value === activeValue[menuIndex] ? 'activeItem' : null}
{...events}
tabindex= { item.disabled ? null : -1 }
role="menuitem"
aria-haspopup={ !!item.children }
aria-expanded={ item.value === activeValue[menuIndex] }
id = { itemId }
aria-owns = { !item.children ? null : ownsId }
>
{item.label}
</li>
......@@ -188,19 +273,68 @@
menuStyle.minWidth = this.inputWidth + 'px';
}
const isHoveredMenu = expandTrigger === 'hover' && activeValue.length - 1 === menuIndex;
const hoverMenuEvent = {
on: {
}
};
if (isHoveredMenu) {
hoverMenuEvent.on.mousemove = hoverMenuHandler;
menuStyle.position = 'relative';
}
return (
<ul
class={{
'el-cascader-menu': true,
'el-cascader-menu--flexible': isFlat
}}
{...hoverMenuEvent}
style={menuStyle}
refInFor
ref="menus">
ref="menus"
role="menu"
id = { menuId }
>
{items}
{
isHoveredMenu
? (<svg
ref="hoverZone"
style={{
position: 'absolute',
top: 0,
height: '100%',
width: '100%',
left: 0,
pointerEvents: 'none'
}}
></svg>) : null
}
</ul>
);
});
if (expandTrigger === 'hover') {
this.$nextTick(() => {
const activeItem = this.$refs.activeItem;
if (activeItem) {
const activeMenu = activeItem.parentElement;
const hoverZone = this.$refs.hoverZone;
hoverMenuRefs = {
activeMenu,
activeItem,
hoverZone
};
} else {
hoverMenuRefs = {};
}
});
}
return (
<transition name="el-zoom-in-top" on-before-enter={this.handleMenuEnter} on-after-leave={this.handleMenuLeave}>
<div
......
......@@ -6,6 +6,8 @@
'el-dropdown-menu__item--divided': divided
}"
@click="handleClick"
:aria-disabled="disabled"
:tabindex="disabled ? null : -1"
>
<slot></slot>
</li>
......
......@@ -4,6 +4,7 @@
import Migrating from 'element-ui/src/mixins/migrating';
import ElButton from 'element-ui/packages/button';
import ElButtonGroup from 'element-ui/packages/button-group';
import { generateId } from 'element-ui/src/utils/util';
export default {
name: 'ElDropdown',
......@@ -61,25 +62,43 @@
return {
timeout: null,
visible: false,
triggerElm: null
triggerElm: null,
menuItems: null,
menuItemsArray: null,
dropdownElm: null,
focusing: false
};
},
computed: {
dropdownSize() {
return this.size || (this.$ELEMENT || {}).size;
},
listId() {
return `dropdown-menu-${generateId()}`;
}
},
mounted() {
this.$on('menu-item-click', this.handleMenuItemClick);
this.initEvent();
this.initAria();
},
watch: {
visible(val) {
this.broadcast('ElDropdownMenu', 'visible', val);
this.$emit('visible-change', val);
},
focusing(val) {
const selfDefine = this.$el.querySelector('.el-dropdown-selfdefine');
if (selfDefine) { // 自定义
if (val) {
selfDefine.className += ' focusing';
} else {
selfDefine.className = selfDefine.className.replace('focusing', '');
}
}
}
},
......@@ -100,6 +119,8 @@
},
hide() {
if (this.triggerElm.disabled) return;
this.removeTabindex();
this.resetTabindex(this.triggerElm);
clearTimeout(this.timeout);
this.timeout = setTimeout(() => {
this.visible = false;
......@@ -109,18 +130,98 @@
if (this.triggerElm.disabled) return;
this.visible = !this.visible;
},
handleTriggerKeyDown(ev) {
const keyCode = ev.keyCode;
if ([38, 40].indexOf(keyCode) > -1) { // up/down
this.removeTabindex();
this.resetTabindex(this.menuItems[0]);
this.menuItems[0].focus();
ev.preventDefault();
ev.stopPropagation();
} else if (keyCode === 13) { // space enter选中
this.handleClick();
} else if ([9, 27].indexOf(keyCode) > -1) { // tab || esc
this.hide();
}
return;
},
handleItemKeyDown(ev) {
const keyCode = ev.keyCode;
const target = ev.target;
const currentIndex = this.menuItemsArray.indexOf(target);
const max = this.menuItemsArray.length - 1;
let nextIndex;
if ([38, 40].indexOf(keyCode) > -1) { // up/down
if (keyCode === 38) { // up
nextIndex = currentIndex !== 0 ? currentIndex - 1 : 0;
} else { // down
nextIndex = currentIndex < max ? currentIndex + 1 : max;
}
this.removeTabindex();
this.resetTabindex(this.menuItems[nextIndex]);
this.menuItems[nextIndex].focus();
ev.preventDefault();
ev.stopPropagation();
} else if (keyCode === 13) { // enter选中
this.triggerElm.focus();
target.click();
if (!this.hideOnClick) { // click关闭
this.visible = false;
}
} else if ([9, 27].indexOf(keyCode) > -1) { // tab // esc
this.hide();
this.triggerElm.focus();
}
return;
},
resetTabindex(ele) { // 下次tab时组件聚焦元素
this.removeTabindex();
ele.setAttribute('tabindex', '0'); // 下次期望的聚焦元素
},
removeTabindex() {
this.triggerElm.setAttribute('tabindex', '-1');
this.menuItemsArray.forEach((item) => {
item.setAttribute('tabindex', '-1');
});
},
initAria() {
this.dropdownElm.setAttribute('id', this.listId);
this.triggerElm.setAttribute('aria-haspopup', 'list');
this.triggerElm.setAttribute('aria-controls', this.listId);
this.menuItems = this.dropdownElm.querySelectorAll("[tabindex='-1']");
this.menuItemsArray = Array.prototype.slice.call(this.menuItems);
if (!this.splitButton) { // 自定义
this.triggerElm.setAttribute('role', 'button');
this.triggerElm.setAttribute('tabindex', '0');
this.triggerElm.className += ' el-dropdown-selfdefine'; // 控制
}
},
initEvent() {
let { trigger, show, hide, handleClick, splitButton } = this;
let { trigger, show, hide, handleClick, splitButton, handleTriggerKeyDown, handleItemKeyDown } = this;
this.triggerElm = splitButton
? this.$refs.trigger.$el
: this.$slots.default[0].elm;
let dropdownElm = this.dropdownElm = this.$slots.dropdown[0].elm;
this.triggerElm.addEventListener('keydown', handleTriggerKeyDown); // triggerElm keydown
dropdownElm.addEventListener('keydown', handleItemKeyDown, true); // item keydown
// 控制自定义元素的样式
if (!splitButton) {
this.triggerElm.addEventListener('focus', () => {
this.focusing = true;
});
this.triggerElm.addEventListener('blur', () => {
this.focusing = false;
});
this.triggerElm.addEventListener('click', () => {
this.focusing = false;
});
}
if (trigger === 'hover') {
this.triggerElm.addEventListener('mouseenter', show);
this.triggerElm.addEventListener('mouseleave', hide);
let dropdownElm = this.$slots.dropdown[0].elm;
dropdownElm.addEventListener('mouseenter', show);
dropdownElm.addEventListener('mouseleave', hide);
} else if (trigger === 'click') {
......
......@@ -157,14 +157,14 @@
},
getColorChannels(color) {
color = color.replace('#', '');
if (/^[1-9a-fA-F]{3}$/.test(color)) {
if (/^[0-9a-fA-F]{3}$/.test(color)) {
color = color.split('');
for (let i = 2; i >= 0; i--) {
color.splice(i, 0, color[i]);
}
color = color.join('');
}
if (/^[1-9a-fA-F]{6}$/.test(color)) {
if (/^[0-9a-fA-F]{6}$/.test(color)) {
return {
red: parseInt(color.slice(0, 2), 16),
green: parseInt(color.slice(2, 4), 16),
......
......@@ -173,11 +173,13 @@
},
handleTitleMouseenter() {
if (this.mode === 'horizontal' && !this.rootMenu.backgroundColor) return;
this.$refs['submenu-title'].style.backgroundColor = this.rootMenu.hoverBackground;
const title = this.$refs['submenu-title'];
title && (title.style.backgroundColor = this.rootMenu.hoverBackground);
},
handleTitleMouseleave() {
if (this.mode === 'horizontal' && !this.rootMenu.backgroundColor) return;
this.$refs['submenu-title'].style.backgroundColor = this.rootMenu.backgroundColor || '';
const title = this.$refs['submenu-title'];
title && (title.style.backgroundColor = this.rootMenu.backgroundColor || '');
}
},
created() {
......
......@@ -9,15 +9,15 @@
v-show="visible"
@mouseenter="clearTimer"
@mouseleave="startTimer"
role="alertdialog"
role="alert"
>
<i :class="iconClass" v-if="iconClass"></i>
<i :class="typeClass" v-else></i>
<slot>
<p v-if="!dangerouslyUseHTMLString" class="el-message__content" tabindex="0">{{ message }}</p>
<p v-else v-html="message" class="el-message__content" tabindex="0"></p>
<p v-if="!dangerouslyUseHTMLString" class="el-message__content">{{ message }}</p>
<p v-else v-html="message" class="el-message__content"></p>
</slot>
<i v-if="showClose" class="el-message__closeBtn el-icon-close" @click="close" tabindex="0" role="button" aria-label="close" @keydown.enter.stop="close"></i>
<i v-if="showClose" class="el-message__closeBtn el-icon-close" @click="close"></i>
</div>
</transition>
</template>
......@@ -44,9 +44,7 @@
closed: false,
timer: null,
dangerouslyUseHTMLString: false,
center: false,
initFocus: null,
originFocus: null
center: false
};
},
......@@ -87,18 +85,6 @@
if (typeof this.onClose === 'function') {
this.onClose(this);
}
if (!this.originFocus || !this.originFocus.getBoundingClientRect) return;
// restore keyboard focus
const { top, left, bottom, right } = this.originFocus.getBoundingClientRect();
const viewportHeight = window.innerHeight || document.documentElement.clientHeight;
const viewportWidth = window.innerWidth || document.documentElement.clientWidth;
if (top >= 0 &&
left >= 0 &&
bottom <= viewportHeight &&
right <= viewportWidth) {
this.originFocus.focus();
}
},
clearTimer() {
......@@ -115,24 +101,15 @@
}
},
keydown(e) {
if (e.keyCode === 46 || e.keyCode === 8) {
this.clearTimer(); // detele 取消倒计时
} else if (e.keyCode === 27) { // esc关闭消息
if (e.keyCode === 27) { // esc关闭消息
if (!this.closed) {
this.close();
}
} else {
this.startTimer(); // 恢复倒计时
}
}
},
mounted() {
this.startTimer();
this.originFocus = document.activeElement;
this.initFocus = this.showClose ? this.$el.querySelector('.el-icon-close') : this.$el.querySelector('.el-message__content');
setTimeout(() => {
this.initFocus && this.initFocus.focus();
});
document.addEventListener('keydown', this.keydown);
},
beforeDestroy() {
......
......@@ -6,7 +6,9 @@
:style="positionStyle"
@mouseenter="clearTimer()"
@mouseleave="startTimer()"
@click="click">
@click="click"
role="alert"
>
<i
class="el-notification__icon"
:class="[ typeClass, iconClass ]"
......@@ -119,9 +121,19 @@
}
}, this.duration);
}
},
keydown(e) {
if (e.keyCode === 46 || e.keyCode === 8) {
this.clearTimer(); // detele 取消倒计时
} else if (e.keyCode === 27) { // esc关闭消息
if (!this.closed) {
this.close();
}
} else {
this.startTimer(); // 恢复倒计时
}
}
},
mounted() {
if (this.duration > 0) {
this.timer = setTimeout(() => {
......@@ -130,6 +142,11 @@
}
}, this.duration);
}
document.addEventListener('keydown', this.keydown);
},
beforeDestroy() {
document.removeEventListener('keydown', this.keydown);
}
};
</script>
......@@ -6,7 +6,11 @@
:class="[popperClass, content && 'el-popover--plain']"
ref="popper"
v-show="!disabled && showPopper"
:style="{ width: width + 'px' }">
:style="{ width: width + 'px' }"
role="tooltip"
:id="tooltipId"
:aria-hidden="(disabled || !showPopper) ? 'true' : 'false'"
>
<div class="el-popover__title" v-if="title" v-text="title"></div>
<slot>{{ content }}</slot>
</div>
......@@ -14,10 +18,11 @@
<slot name="reference"></slot>
</span>
</template>
<script>
import Popper from 'element-ui/src/utils/vue-popper';
import { on, off } from 'element-ui/src/utils/dom';
import { addClass, removeClass } from 'element-ui/src/utils/dom';
import { generateId } from 'element-ui/src/utils/util';
export default {
name: 'ElPopover',
......@@ -49,25 +54,35 @@ export default {
}
},
computed: {
tooltipId() {
return `el-popover-${generateId()}`;
}
},
watch: {
showPopper(newVal, oldVal) {
newVal ? this.$emit('show') : this.$emit('hide');
},
'$refs.reference': {
deep: true,
handler(val) {
console.log(val);
}
showPopper(val) {
val ? this.$emit('show') : this.$emit('hide');
}
},
mounted() {
let reference = this.reference || this.$refs.reference;
let reference = this.referenceElm = this.reference || this.$refs.reference;
const popper = this.popper || this.$refs.popper;
if (!reference && this.$slots.reference && this.$slots.reference[0]) {
reference = this.referenceElm = this.$slots.reference[0].elm;
}
// 可访问性
if (reference) {
addClass(reference, 'el-popover__reference');
reference.setAttribute('aria-describedby', this.tooltipId);
reference.setAttribute('tabindex', 0); // tab序列
this.trigger !== 'click' && on(reference, 'focus', this.handleFocus);
this.trigger !== 'click' && on(reference, 'blur', this.handleBlur);
on(reference, 'keydown', this.handleKeydown);
on(reference, 'click', this.handleClick);
}
if (this.trigger === 'click') {
on(reference, 'click', this.doToggle);
on(document, 'click', this.handleDocumentClick);
......@@ -114,6 +129,17 @@ export default {
doClose() {
this.showPopper = false;
},
handleFocus() {
addClass(this.referenceElm, 'focusing');
this.showPopper = true;
},
handleClick() {
removeClass(this.referenceElm, 'focusing');
},
handleBlur() {
removeClass(this.referenceElm, 'focusing');
this.showPopper = false;
},
handleMouseEnter() {
clearTimeout(this._timer);
if (this.openDelay) {
......@@ -124,6 +150,11 @@ export default {
this.showPopper = true;
}
},
handleKeydown(ev) {
if (ev.keyCode === 27) { // esc
this.doClose();
}
},
handleMouseLeave() {
clearTimeout(this._timer);
this._timer = setTimeout(() => {
......
......@@ -53,9 +53,10 @@
},
mounted() {
// 当radioGroup没有默认选项时,第一个可以选中Tab导航
let radios = this.$el.querySelectorAll('[type=radio]');
if (![].some.call(radios, radio => radio.checked)) {
this.$el.querySelectorAll('[role=radio]')[0].tabIndex = 0;
const radios = this.$el.querySelectorAll('[type=radio]');
const firstLabel = this.$el.querySelectorAll('[role=radio]')[0];
if (![].some.call(radios, radio => radio.checked) && firstLabel) {
firstLabel.tabIndex = 0;
}
},
methods: {
......
<template>
<ul class="el-select-group__wrap">
<li class="el-select-group__title" v-show="visible">{{ label }}</li>
<ul class="el-select-group__wrap" v-show="visible">
<li class="el-select-group__title">{{ label }}</li>
<li>
<ul class="el-select-group">
<slot></slot>
......
......@@ -9,7 +9,12 @@
@click.stop="toggleMenu"
ref="tags"
:style="{ 'max-width': inputWidth - 32 + 'px' }">
<transition-group @after-leave="resetInputHeight">
<span
class="el-select__multiple-text"
v-if="collapseTags">
{{ multipleText }}
</span>
<transition-group @after-leave="resetInputHeight" v-if="!collapseTags">
<el-tag
v-for="item in selected"
:key="getValueKey(item)"
......@@ -29,6 +34,7 @@
:class="[selectSize ? `is-${ selectSize }` : '']"
:disabled="disabled"
@focus="handleFocus"
@click.stop
@keyup="managePlaceholder"
@keydown="resetInputState"
@keydown.down.prevent="navigateOptions('next')"
......@@ -183,6 +189,14 @@
selectSize() {
return this.size || this._elFormItemSize || (this.$ELEMENT || {}).size;
},
multipleText() {
const selected = this.selected;
if (!selected || !selected.length) return '';
const length = selected.length;
const countText = length > 1 ? `(+${ selected.length - 1 })` : '';
return `${ selected[0].currentLabel } ${ countText }`;
}
},
......@@ -231,7 +245,8 @@
valueKey: {
type: String,
default: 'value'
}
},
collapseTags: Boolean
},
data() {
......@@ -534,6 +549,7 @@
},
resetInputHeight() {
if (this.collapseTags) return;
this.$nextTick(() => {
if (!this.$refs.reference) return;
let inputChildNodes = this.$refs.reference.$el.childNodes;
......
......@@ -119,12 +119,10 @@ export default {
{
column.sortable
? <span class="caret-wrapper" on-click={ ($event) => this.handleSortClick($event, column) }>
<span class="sort-caret ascending" on-click={ ($event) => this.handleSortClick($event, column, 'ascending') }>
<i class="el-icon-sort-up"></i>
</span>
<span class="sort-caret descending" on-click={ ($event) => this.handleSortClick($event, column, 'descending') }>
<i class="el-icon-sort-down"></i>
</span>
<i class="sort-caret ascending el-icon-caret-top" on-click={ ($event) => this.handleSortClick($event, column, 'ascending') }>
</i>
<i class="sort-caret descending el-icon-caret-bottom" on-click={ ($event) => this.handleSortClick($event, column, 'descending') }>
</i>
</span>
: ''
}
......
{
"name": "element-theme-chalk",
"version": "2.0.3",
"version": "2.0.4",
"description": "Element component chalk theme.",
"main": "lib/index.css",
"style": "lib/index.css",
......@@ -28,7 +28,8 @@
"devDependencies": {
"gulp": "^3.9.1",
"gulp-cssmin": "^0.1.7",
"gulp-sass": "^3.1.0"
"gulp-sass": "^3.1.0",
"gulp-autoprefixer": "^4.0.0"
},
"dependencies": {}
}
......@@ -128,7 +128,7 @@
line-height: 1.5;
box-sizing: border-box;
cursor: pointer;
outline: none;
@include m(extensible) {
&:after {
font-family: 'element-icons';
......@@ -154,7 +154,7 @@
color: $--select-option-selected;
}
&:hover {
&:hover, &:focus:not(:active) {
background-color: $--select-option-hover-background;
}
......
......@@ -17,6 +17,9 @@
padding: $--checkbox-bordered-padding;
border-radius: $--border-radius-base;
border: $--border-base;
box-sizing: border-box;
line-height: normal;
height: $--checkbox-bordered-height;
&.is-checked {
border-color: $--color-primary;
......@@ -34,6 +37,7 @@
&.el-checkbox--medium {
padding: $--checkbox-bordered-medium-padding;
border-radius: $--button-medium-border-radius;
height: $--checkbox-bordered-medium-height;
.el-checkbox__label {
line-height: 17px;
......@@ -49,6 +53,7 @@
&.el-checkbox--small {
padding: $--checkbox-bordered-small-padding;
border-radius: $--button-small-border-radius;
height: $--checkbox-bordered-small-height;
.el-checkbox__label {
line-height: 15px;
......@@ -69,6 +74,7 @@
&.el-checkbox--mini {
padding: $--checkbox-bordered-mini-padding;
border-radius: $--button-mini-border-radius;
height: $--checkbox-bordered-mini-height;
.el-checkbox__label {
line-height: 12px;
......
......@@ -137,16 +137,20 @@ $--checkbox-checked-icon-color: $--fill-base !default;
$--checkbox-input-border-color-hover: $--color-primary !default;
$--checkbox-bordered-height: 40px !default;
$--checkbox-bordered-padding: 9px 20px 9px 10px !default;
$--checkbox-bordered-medium-padding: 7px 20px 7px 10px !default;
$--checkbox-bordered-small-padding: 3px 15px 7px 10px !default;
$--checkbox-bordered-mini-padding: 1px 15px 5px 10px !default;
$--checkbox-bordered-small-padding: 5px 15px 5px 10px !default;
$--checkbox-bordered-mini-padding: 3px 15px 3px 10px !default;
$--checkbox-bordered-medium-input-height: 14px !default;
$--checkbox-bordered-medium-input-width: 14px !default;
$--checkbox-bordered-medium-height: 36px !default;
$--checkbox-bordered-small-input-height: 12px !default;
$--checkbox-bordered-small-input-width: 12px !default;
$--checkbox-bordered-small-height: 32px !default;
$--checkbox-bordered-mini-input-height: 12px !default;
$--checkbox-bordered-mini-input-width: 12px !default;
$--checkbox-bordered-mini-height: 28px !default;
$--checkbox-button-font-size: $--font-size-base !default;
$--checkbox-button-checked-fill: $--color-primary !default;
......@@ -183,16 +187,20 @@ $--radio-checked-icon-color: $--color-primary !default;
$--radio-input-border-color-hover: $--color-primary !default;
$--radio-bordered-padding: 10px 20px 10px 10px !default;
$--radio-bordered-medium-padding: 8px 20px 8px 10px !default;
$--radio-bordered-small-padding: 6px 15px 6px 10px !default;
$--radio-bordered-mini-padding: 4px 15px 4px 10px !default;
$--radio-bordered-height: 40px !default;
$--radio-bordered-padding: 12px 20px 0 10px !default;
$--radio-bordered-medium-padding: 10px 20px 0 10px !default;
$--radio-bordered-small-padding: 8px 15px 0 10px !default;
$--radio-bordered-mini-padding: 6px 15px 0 10px !default;
$--radio-bordered-medium-input-height: 14px !default;
$--radio-bordered-medium-input-width: 14px !default;
$--radio-bordered-medium-height: 36px !default;
$--radio-bordered-small-input-height: 12px !default;
$--radio-bordered-small-input-width: 12px !default;
$--radio-bordered-small-height: 32px !default;
$--radio-bordered-mini-input-height: 12px !default;
$--radio-bordered-mini-input-width: 12px !default;
$--radio-bordered-mini-height: 28px !default;
$--radio-button-font-size: $--font-size-base !default;
$--radio-button-checked-fill: $--color-primary !default;
......
......@@ -50,6 +50,12 @@
font-size: 12px;
margin: 0 3px;
}
.el-dropdown-selfdefine { // 自定义
&:focus:active, &:focus:not(.focusing) {
outline-width: 0;
}
}
}
@include b(dropdown-menu) {
......@@ -72,8 +78,8 @@
font-size: $--font-size-base;
color: $--color-text-regular;
cursor: pointer;
&:not(.is-disabled):hover {
outline: none;
&:not(.is-disabled):hover, &:focus {
background-color: $--dropdown-menuItem-hover-fill;
color: $--dropdown-menuItem-hover-color;
}
......
......@@ -27,4 +27,10 @@
line-height: 1;
margin-bottom: 12px;
}
@include e(reference) {
&:focus:not(.focusing), &:focus:hover {
outline-width: 0;
}
}
}
......@@ -19,6 +19,8 @@
padding: $--radio-bordered-padding;
border-radius: $--border-radius-base;
border: $--border-base;
box-sizing: border-box;
height: $--radio-bordered-height;
&.is-checked {
border-color: $--color-primary;
......@@ -38,6 +40,7 @@
&.is-bordered {
padding: $--radio-bordered-medium-padding;
border-radius: $--button-medium-border-radius;
height: $--radio-bordered-medium-height;
.el-radio__label {
font-size: $--button-medium-font-size;
}
......@@ -51,6 +54,7 @@
&.is-bordered {
padding: $--radio-bordered-small-padding;
border-radius: $--button-small-border-radius;
height: $--radio-bordered-small-height;
.el-radio__label {
font-size: $--button-small-font-size;
}
......@@ -64,6 +68,7 @@
&.is-bordered {
padding: $--radio-bordered-mini-padding;
border-radius: $--button-mini-border-radius;
height: $--radio-bordered-mini-height;
.el-radio__label {
font-size: $--button-mini-font-size;
}
......
@import "mixins/mixins";
@import "mixins/utils";
@import "common/var";
@import "select-dropdown";
@import "input";
......@@ -88,6 +89,14 @@
}
}
@include e(multiple-text) {
margin-left: 15px;
color: $--input-color;
font-size: $--font-size-base;
display: block;
@include utils-ellipsis;
}
@include e(close) {
cursor: pointer;
position: absolute;
......
......@@ -426,20 +426,29 @@
}
.caret-wrapper {
position: relative;
display: inline-flex;
align-items: center;
height: 13px;
width: 24px;
position: absolute;
display: inline-block;
height: 100%;
vertical-align: middle;
cursor: pointer;
overflow: initial;
}
.sort-caret {
color: $--icon-color-base;
color: $--color-text-placeholder;
width: 14px;
overflow: hidden;
font-size: 13px;
font-size: 15px;
position: absolute;
left: 4px;
&.ascending {
top: 1px;
}
&.descending {
bottom: 1px;
}
}
.ascending .sort-caret.ascending {
......
......@@ -2,6 +2,9 @@
@import "common/var";
@include b(tooltip) {
&:focus:not(.focusing), &:focus:hover {
outline-width: 0;
}
@include e(popper) {
position: absolute;
border-radius: 4px;
......
......@@ -25,7 +25,12 @@
@include b(tree-node) {
white-space: nowrap;
outline: none;
&:focus { /* focus */
> .el-tree-node__content {
background-color: $--tree-node-hover-color;
}
}
@include e(content) {
display: flex;
align-items: center;
......
import Popper from 'element-ui/src/utils/vue-popper';
import debounce from 'throttle-debounce/debounce';
import { getFirstComponentChild } from 'element-ui/src/utils/vdom';
import { generateId } from 'element-ui/src/utils/util';
import Vue from 'vue';
export default {
......@@ -48,10 +49,15 @@ export default {
data() {
return {
timeoutPending: null
timeoutPending: null,
focusing: false
};
},
computed: {
tooltipId() {
return `el-tooltip-${generateId()}`;
}
},
beforeCreate() {
if (this.$isServer) return;
......@@ -75,6 +81,9 @@ export default {
onMouseleave={ () => { this.setExpectedState(false); this.debounceClose(); } }
onMouseenter= { () => { this.setExpectedState(true); } }
ref="popper"
role="tooltip"
id={this.tooltipId}
aria-hidden={ (this.disabled || !this.showPopper) ? 'true' : 'false' }
v-show={!this.disabled && this.showPopper}
class={
['el-tooltip__popper', 'is-' + this.effect, this.popperClass]
......@@ -87,24 +96,38 @@ export default {
if (!this.$slots.default || !this.$slots.default.length) return this.$slots.default;
const vnode = getFirstComponentChild(this.$slots.default);
if (!vnode) return vnode;
const data = vnode.data = vnode.data || {};
const on = vnode.data.on = vnode.data.on || {};
const nativeOn = vnode.data.nativeOn = vnode.data.nativeOn || {};
data.staticClass = this.concatClass(data.staticClass, 'el-tooltip');
on.mouseenter = this.addEventHandle(on.mouseenter, this.show);
on.mouseleave = this.addEventHandle(on.mouseleave, this.hide);
nativeOn.mouseenter = this.addEventHandle(nativeOn.mouseenter, this.show);
nativeOn.mouseleave = this.addEventHandle(nativeOn.mouseleave, this.hide);
nativeOn.mouseenter = on.mouseenter = this.addEventHandle(on.mouseenter, this.show);
nativeOn.mouseleave = on.mouseleave = this.addEventHandle(on.mouseleave, this.hide);
nativeOn.focus = on.focus = this.addEventHandle(on.focus, this.handleFocus);
nativeOn.blur = on.blur = this.addEventHandle(on.blur, this.handleBlur);
nativeOn.click = on.click = this.addEventHandle(on.click, () => { this.focusing = false; });
return vnode;
},
mounted() {
this.referenceElm = this.$el;
if (this.$el.nodeType === 1) {
this.$el.setAttribute('aria-describedby', this.tooltipId);
this.$el.setAttribute('tabindex', 0);
}
},
watch: {
focusing(val) {
if (val) {
this.referenceElm.className += ' focusing';
} else {
this.referenceElm.className = this.referenceElm.className.replace('focusing', '');
}
}
},
methods: {
show() {
this.setExpectedState(true);
......@@ -115,7 +138,14 @@ export default {
this.setExpectedState(false);
this.debounceClose();
},
handleFocus() {
this.focusing = true;
this.show();
},
handleBlur() {
this.focusing = false;
this.hide();
},
addEventHandle(old, fn) {
if (!old) {
return fn;
......
......@@ -198,9 +198,10 @@ export default class TreeStore {
const node = this.nodesMap[key];
if (!node) return;
const childNodes = node.childNodes;
childNodes.forEach(child => {
for (let i = childNodes.length - 1; i >= 0; i--) {
const child = childNodes[i];
this.remove(child.data);
});
}
for (let i = 0, j = data.length; i < j; i++) {
const child = data[i];
this.append(child, node.data);
......
<template>
<div class="el-tree-node"
<div
class="el-tree-node"
@click.stop="handleClick"
v-show="node.visible"
:class="{
'is-expanded': expanded,
'is-current': tree.store.currentNode === node,
'is-hidden': !node.visible
}">
'is-hidden': !node.visible,
'is-focusable': !node.disabled,
'is-checked': !node.disabled && node.checked
}"
role="treeitem"
tabindex="-1"
:aria-expanded="expanded"
:aria-disabled="node.disabled"
:aria-checked="node.checked"
>
<div class="el-tree-node__content"
:style="{ 'padding-left': (node.level - 1) * tree.indent + 'px' }">
<span
......@@ -20,7 +29,8 @@
:indeterminate="node.indeterminate"
:disabled="!!node.disabled"
@click.native.stop
@change="handleCheckChange">
@change="handleCheckChange"
>
</el-checkbox>
<span
v-if="node.loading"
......@@ -32,7 +42,10 @@
<div
class="el-tree-node__children"
v-if="childNodeRendered"
v-show="expanded">
v-show="expanded"
role="group"
:aria-expanded="expanded"
>
<el-tree-node
:render-content="renderContent"
v-for="child in node.childNodes"
......
<template>
<div class="el-tree" :class="{ 'el-tree--highlight-current': highlightCurrent }">
<div
class="el-tree"
:class="{ 'el-tree--highlight-current': highlightCurrent }"
role="tree"
>
<el-tree-node
v-for="child in root.childNodes"
:node="child"
......@@ -33,7 +37,9 @@
return {
store: null,
root: null,
currentNode: null
currentNode: null,
treeItems: null,
checkboxItems: []
};
},
......@@ -101,6 +107,9 @@
get() {
return this.data;
}
},
treeItemArray() {
return Array.prototype.slice.call(this.treeItems);
}
},
......@@ -115,6 +124,11 @@
},
data(newVal) {
this.store.setData(newVal);
},
checkboxItems(val) {
Array.prototype.forEach.call(val, (checkbox) => {
checkbox.setAttribute('tabindex', -1);
});
}
},
......@@ -171,6 +185,42 @@
updateKeyChildren(key, data) {
if (!this.nodeKey) throw new Error('[Tree] nodeKey is required in updateKeyChild');
this.store.updateChildren(key, data);
},
initTabindex() {
this.treeItems = this.$el.querySelectorAll('.is-focusable[role=treeitem]');
this.checkboxItems = this.$el.querySelectorAll('input[type=checkbox]');
const checkedItem = this.$el.querySelectorAll('.is-checked[role=treeitem]');
if (checkedItem.length) {
checkedItem[0].setAttribute('tabindex', 0);
return;
}
this.treeItems[0].setAttribute('tabindex', 0);
},
handelKeydown(ev) {
const currentItem = ev.target;
const keyCode = ev.keyCode;
this.treeItems = this.$el.querySelectorAll('.is-focusable[role=treeitem]');
const currentIndex = this.treeItemArray.indexOf(currentItem);
let nextIndex;
if ([38, 40].indexOf(keyCode) > -1) { // up、down
if (keyCode === 38) { // up
nextIndex = currentIndex !== 0 ? currentIndex - 1 : 0;
} else {
nextIndex = (currentIndex < this.treeItemArray.length - 1) ? currentIndex + 1 : 0;
}
this.treeItemArray[nextIndex].focus(); // 选中
}
const hasInput = currentItem.querySelector('[type="checkbox"]');
if ([37, 39].indexOf(keyCode) > -1) { // left、right 展开
currentItem.click(); // 选中
}
if ([13, 32].indexOf(keyCode) > -1) { // space enter选中checkbox
if (hasInput) {
hasInput.click();
}
ev.stopPropagation();
ev.preventDefault();
}
}
},
......@@ -194,6 +244,14 @@
});
this.root = this.store.root;
},
mounted() {
this.initTabindex();
this.$el.addEventListener('keydown', this.handelKeydown);
},
updated() {
this.treeItems = this.$el.querySelectorAll('[role=treeitem]');
this.checkboxItems = this.$el.querySelectorAll('input[type=checkbox]');
}
};
</script>
function getError(action, option, xhr) {
let msg;
if (xhr.response) {
msg = `${xhr.status} ${xhr.response.error || xhr.response}`;
msg = `${xhr.response.error || xhr.response}`;
} else if (xhr.responseText) {
msg = `${xhr.status} ${xhr.responseText}`;
msg = `${xhr.responseText}`;
} else {
msg = `fail to post ${action} ${xhr.status}`;
}
......
......@@ -173,7 +173,7 @@ if (typeof window !== 'undefined' && window.Vue) {
};
module.exports = {
version: '2.0.3',
version: '2.0.4',
locale: locale.use,
i18n: locale.i18n,
install,
......
......@@ -65,5 +65,6 @@ export default {
break;
}
}
delete el[ctx];
}
};
......@@ -41,7 +41,7 @@ describe('ajax', () => {
});
it('40x code should be error', done => {
option.onError = e => {
expect(e.toString()).to.contain('404 Not found');
expect(e.toString()).to.contain('Not found');
done();
};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment