Fixed #4841 - TreeSelect: Accessibility Improvements
parent
e0c634a1f8
commit
d05054a5fc
|
@ -43,8 +43,19 @@
|
|||
</slot>
|
||||
</div>
|
||||
<Portal :appendTo="appendTo">
|
||||
<transition name="p-connected-overlay" @enter="onOverlayEnter" @leave="onOverlayLeave" @after-leave="onOverlayAfterLeave" v-bind="ptm('transition')">
|
||||
<transition name="p-connected-overlay" @enter="onOverlayEnter" @after-enter="onOverlayAfterEnter" @leave="onOverlayLeave" @after-leave="onOverlayAfterLeave" v-bind="ptm('transition')">
|
||||
<div v-if="overlayVisible" :ref="overlayRef" @click="onOverlayClick" :class="[cx('panel'), panelClass]" @keydown="onOverlayKeydown" v-bind="{ ...panelProps, ...ptm('panel') }">
|
||||
<span
|
||||
ref="firstHiddenFocusableElementOnOverlay"
|
||||
role="presentation"
|
||||
aria-hidden="true"
|
||||
class="p-hidden-accessible p-hidden-focusable"
|
||||
:tabindex="0"
|
||||
@focus="onFirstHiddenFocus"
|
||||
v-bind="ptm('hiddenFirstFocusableEl')"
|
||||
:data-p-hidden-accessible="true"
|
||||
:data-p-hidden-focusable="true"
|
||||
></span>
|
||||
<slot name="header" :value="modelValue" :options="options"></slot>
|
||||
<div :class="cx('wrapper')" :style="{ 'max-height': scrollHeight }" v-bind="ptm('wrapper')">
|
||||
<TSTree
|
||||
|
@ -78,6 +89,17 @@
|
|||
</div>
|
||||
</div>
|
||||
<slot name="footer" :value="modelValue" :options="options"></slot>
|
||||
<span
|
||||
ref="lastHiddenFocusableElementOnOverlay"
|
||||
role="presentation"
|
||||
aria-hidden="true"
|
||||
class="p-hidden-accessible p-hidden-focusable"
|
||||
:tabindex="0"
|
||||
@focus="onLastHiddenFocus"
|
||||
v-bind="ptm('hiddenLastFocusableEl')"
|
||||
:data-p-hidden-accessible="true"
|
||||
:data-p-hidden-focusable="true"
|
||||
></span>
|
||||
</div>
|
||||
</transition>
|
||||
</Portal>
|
||||
|
@ -161,11 +183,15 @@ export default {
|
|||
this.$emit('blur', event);
|
||||
},
|
||||
onClick(event) {
|
||||
if (this.disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.disabled && (!this.overlay || !this.overlay.contains(event.target))) {
|
||||
if (this.overlayVisible) this.hide();
|
||||
else this.show();
|
||||
|
||||
this.$refs.focusInput.focus();
|
||||
DomHandler.focus(this.$refs.focusInput);
|
||||
}
|
||||
},
|
||||
onSelectionChange(keys) {
|
||||
|
@ -186,6 +212,16 @@ export default {
|
|||
onNodeToggle(keys) {
|
||||
this.expandedKeys = keys;
|
||||
},
|
||||
onFirstHiddenFocus(event) {
|
||||
const focusableEl = event.relatedTarget === this.$refs.focusInput ? DomHandler.getFirstFocusableElement(this.overlay, ':not([data-p-hidden-focusable="true"])') : this.$refs.focusInput;
|
||||
|
||||
DomHandler.focus(focusableEl);
|
||||
},
|
||||
onLastHiddenFocus(event) {
|
||||
const focusableEl = event.relatedTarget === this.$refs.focusInput ? DomHandler.getLastFocusableElement(this.overlay, ':not([data-p-hidden-focusable="true"])') : this.$refs.focusInput;
|
||||
|
||||
DomHandler.focus(focusableEl);
|
||||
},
|
||||
onKeyDown(event) {
|
||||
switch (event.code) {
|
||||
case 'ArrowDown':
|
||||
|
@ -199,7 +235,10 @@ export default {
|
|||
|
||||
case 'Escape':
|
||||
this.onEscapeKey(event);
|
||||
break;
|
||||
|
||||
case 'Tab':
|
||||
this.onTabKey(event);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -235,11 +274,30 @@ export default {
|
|||
event.preventDefault();
|
||||
}
|
||||
},
|
||||
onTabKey(event, pressedInInputText = false) {
|
||||
if (!pressedInInputText) {
|
||||
if (this.overlayVisible && this.hasFocusableElements()) {
|
||||
DomHandler.focus(this.$refs.firstHiddenFocusableElementOnOverlay);
|
||||
|
||||
event.preventDefault();
|
||||
} else {
|
||||
if (this.focusedOptionIndex !== -1) {
|
||||
this.onOptionSelect(event, this.visibleOptions[this.focusedOptionIndex]);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
hasFocusableElements() {
|
||||
return DomHandler.getFocusableElements(this.overlay, ':not([data-p-hidden-focusable="true"])').length > 0;
|
||||
},
|
||||
onOverlayEnter(el) {
|
||||
ZIndexUtils.set('overlay', el, this.$primevue.config.zIndex.overlay);
|
||||
|
||||
DomHandler.addStyles(el, { position: 'absolute', top: '0', left: '0' });
|
||||
this.alignOverlay();
|
||||
this.focus();
|
||||
},
|
||||
onOverlayAfterEnter() {
|
||||
this.bindOutsideClickListener();
|
||||
this.bindScrollListener();
|
||||
this.bindResizeListener();
|
||||
|
@ -256,6 +314,13 @@ export default {
|
|||
onOverlayAfterLeave(el) {
|
||||
ZIndexUtils.clear(el);
|
||||
},
|
||||
focus() {
|
||||
let focusableElements = DomHandler.getFocusableElements(this.overlay);
|
||||
|
||||
if (focusableElements && focusableElements.length > 0) {
|
||||
focusableElements[0].focus();
|
||||
}
|
||||
},
|
||||
alignOverlay() {
|
||||
if (this.appendTo === 'self') {
|
||||
DomHandler.relativePosition(this.overlay, this.$el);
|
||||
|
|
Loading…
Reference in New Issue