kopia lustrzana https://github.com/shoelace-style/shoelace
account for elements with tabbable controls
rodzic
dd27db5196
commit
8d579c18cc
|
@ -300,9 +300,9 @@ export default class SlDialog extends ShoelaceElement {
|
|||
`
|
||||
: ''}
|
||||
${
|
||||
'' /* The tabindex="-1" is here because the body is technically scrollable if overflowing. However, if there's no focusable elements inside, you won't actually be able to scroll it via keyboard. */
|
||||
'' /* The tabindex="-1" is here because the body is technically scrollable if overflowing. However, if there's no focusable elements inside, you won't actually be able to scroll it via keyboard. Previously this was just a <slot>, but tabindex="-1" on the slot causes children to not be focusable. https://github.com/shoelace-style/shoelace/issues/1753#issuecomment-1836803277 */
|
||||
}
|
||||
<slot part="body" class="dialog__body" tabindex="-1"></slot>
|
||||
<div part="body" class="dialog__body" tabindex="-1"><slot></slot></div>
|
||||
|
||||
<footer part="footer" class="dialog__footer">
|
||||
<slot name="footer"></slot>
|
||||
|
|
|
@ -71,14 +71,26 @@ export default class Modal {
|
|||
if (event.key !== 'Tab' || this.isExternalActivated) return;
|
||||
if (!this.isActive()) return;
|
||||
|
||||
const elementsWithTabbableControls = [
|
||||
"audio",
|
||||
"video",
|
||||
"iframe"
|
||||
]
|
||||
|
||||
const possiblyHasTabbableChildren = (element: HTMLElement) => {
|
||||
return (
|
||||
elementsWithTabbableControls.includes(element.tagName.toLowerCase())
|
||||
|| element.hasAttribute("controls")
|
||||
// Should we add a data-attribute for people to set just in case they have an element where we don't know if it has possibly tabbable elements?
|
||||
)
|
||||
}
|
||||
|
||||
if (event.shiftKey) {
|
||||
this.tabDirection = 'backward';
|
||||
} else {
|
||||
this.tabDirection = 'forward';
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
const tabbableElements = getTabbableElements(this.element);
|
||||
|
||||
// Because sometimes focus can actually be taken over from outside sources,
|
||||
|
@ -89,6 +101,14 @@ export default class Modal {
|
|||
|
||||
if (currentFocusIndex === -1) {
|
||||
this.currentFocus = tabbableElements[0];
|
||||
|
||||
// We don't call event.preventDefault() here because it messes with tabbing to the <iframe> controls.
|
||||
// We just wait until the current focus is no longer an element with possible hidden controls.
|
||||
if (possiblyHasTabbableChildren(this.currentFocus)) {
|
||||
return
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
this.currentFocus?.focus({ preventScroll: true });
|
||||
return;
|
||||
}
|
||||
|
@ -104,6 +124,14 @@ export default class Modal {
|
|||
}
|
||||
|
||||
this.currentFocus = tabbableElements[currentFocusIndex];
|
||||
|
||||
// We don't call event.preventDefault() here because it messes with tabbing to the <iframe> controls.
|
||||
// We just wait until the current focus is no longer an element with possible hidden controls.
|
||||
if (possiblyHasTabbableChildren(this.currentFocus)) {
|
||||
return
|
||||
}
|
||||
|
||||
event.preventDefault()
|
||||
this.currentFocus?.focus({ preventScroll: true });
|
||||
|
||||
setTimeout(() => this.checkFocus());
|
||||
|
|
|
@ -58,7 +58,7 @@ function isTabbable(el: HTMLElement) {
|
|||
}
|
||||
|
||||
// At this point, the following elements are considered tabbable
|
||||
return ['button', 'input', 'select', 'textarea', 'a', 'audio', 'video', 'summary'].includes(tag);
|
||||
return ['button', 'input', 'select', 'textarea', 'a', 'audio', 'video', 'summary', 'iframe'].includes(tag);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Ładowanie…
Reference in New Issue