Fixed #4841 - TreeSelect: Accessibility Improvements
parent
e0c634a1f8
commit
d05054a5fc
|
@ -43,8 +43,19 @@
|
||||||
</slot>
|
</slot>
|
||||||
</div>
|
</div>
|
||||||
<Portal :appendTo="appendTo">
|
<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') }">
|
<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>
|
<slot name="header" :value="modelValue" :options="options"></slot>
|
||||||
<div :class="cx('wrapper')" :style="{ 'max-height': scrollHeight }" v-bind="ptm('wrapper')">
|
<div :class="cx('wrapper')" :style="{ 'max-height': scrollHeight }" v-bind="ptm('wrapper')">
|
||||||
<TSTree
|
<TSTree
|
||||||
|
@ -78,6 +89,17 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<slot name="footer" :value="modelValue" :options="options"></slot>
|
<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>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
</Portal>
|
</Portal>
|
||||||
|
@ -161,11 +183,15 @@ export default {
|
||||||
this.$emit('blur', event);
|
this.$emit('blur', event);
|
||||||
},
|
},
|
||||||
onClick(event) {
|
onClick(event) {
|
||||||
|
if (this.disabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.disabled && (!this.overlay || !this.overlay.contains(event.target))) {
|
if (!this.disabled && (!this.overlay || !this.overlay.contains(event.target))) {
|
||||||
if (this.overlayVisible) this.hide();
|
if (this.overlayVisible) this.hide();
|
||||||
else this.show();
|
else this.show();
|
||||||
|
|
||||||
this.$refs.focusInput.focus();
|
DomHandler.focus(this.$refs.focusInput);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onSelectionChange(keys) {
|
onSelectionChange(keys) {
|
||||||
|
@ -186,6 +212,16 @@ export default {
|
||||||
onNodeToggle(keys) {
|
onNodeToggle(keys) {
|
||||||
this.expandedKeys = 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) {
|
onKeyDown(event) {
|
||||||
switch (event.code) {
|
switch (event.code) {
|
||||||
case 'ArrowDown':
|
case 'ArrowDown':
|
||||||
|
@ -199,7 +235,10 @@ export default {
|
||||||
|
|
||||||
case 'Escape':
|
case 'Escape':
|
||||||
this.onEscapeKey(event);
|
this.onEscapeKey(event);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'Tab':
|
||||||
|
this.onTabKey(event);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -235,11 +274,30 @@ export default {
|
||||||
event.preventDefault();
|
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) {
|
onOverlayEnter(el) {
|
||||||
ZIndexUtils.set('overlay', el, this.$primevue.config.zIndex.overlay);
|
ZIndexUtils.set('overlay', el, this.$primevue.config.zIndex.overlay);
|
||||||
|
|
||||||
DomHandler.addStyles(el, { position: 'absolute', top: '0', left: '0' });
|
DomHandler.addStyles(el, { position: 'absolute', top: '0', left: '0' });
|
||||||
this.alignOverlay();
|
this.alignOverlay();
|
||||||
|
this.focus();
|
||||||
|
},
|
||||||
|
onOverlayAfterEnter() {
|
||||||
this.bindOutsideClickListener();
|
this.bindOutsideClickListener();
|
||||||
this.bindScrollListener();
|
this.bindScrollListener();
|
||||||
this.bindResizeListener();
|
this.bindResizeListener();
|
||||||
|
@ -256,6 +314,13 @@ export default {
|
||||||
onOverlayAfterLeave(el) {
|
onOverlayAfterLeave(el) {
|
||||||
ZIndexUtils.clear(el);
|
ZIndexUtils.clear(el);
|
||||||
},
|
},
|
||||||
|
focus() {
|
||||||
|
let focusableElements = DomHandler.getFocusableElements(this.overlay);
|
||||||
|
|
||||||
|
if (focusableElements && focusableElements.length > 0) {
|
||||||
|
focusableElements[0].focus();
|
||||||
|
}
|
||||||
|
},
|
||||||
alignOverlay() {
|
alignOverlay() {
|
||||||
if (this.appendTo === 'self') {
|
if (this.appendTo === 'self') {
|
||||||
DomHandler.relativePosition(this.overlay, this.$el);
|
DomHandler.relativePosition(this.overlay, this.$el);
|
||||||
|
|
Loading…
Reference in New Issue