|
@@ -10,78 +10,6 @@ export default function (Alpine) {
|
|
|
currentFocused = document.activeElement
|
|
|
})
|
|
|
|
|
|
- Alpine.directive('teleport-focus', (el) => {
|
|
|
- let lastTarget = document.activeElement
|
|
|
-
|
|
|
- document.addEventListener('focusin', e => {
|
|
|
- // Let's check if we just crossed over the <template> portal tag.
|
|
|
- if (
|
|
|
- (el.compareDocumentPosition(e.target) & Node.DOCUMENT_POSITION_FOLLOWING
|
|
|
- && el.compareDocumentPosition(lastTarget) & Node.DOCUMENT_POSITION_PRECEDING)
|
|
|
- || (el.compareDocumentPosition(e.target) & Node.DOCUMENT_POSITION_PRECEDING
|
|
|
- && el.compareDocumentPosition(lastTarget) & Node.DOCUMENT_POSITION_FOLLOWING)
|
|
|
- ) {
|
|
|
- let els = tabbable(el._x_teleport, { includeContainer: true, displayCheck: 'none' })
|
|
|
- // If there is a focusable, focus it, otherwise, bail and let it be past the <template>.
|
|
|
- if (els[0]) {
|
|
|
- queueMicrotask(() => {
|
|
|
- el._x_teleport._x_return_focus_to = e.target
|
|
|
- els[0].focus()
|
|
|
-
|
|
|
- el._x_teleport.addEventListener('keydown', function portalListener(e) {
|
|
|
- if (e.key.toLowerCase() !== 'tab') return
|
|
|
- // We are tabbing away from something focusable.
|
|
|
- if (! document.activeElement.isSameNode(e.target)) return
|
|
|
-
|
|
|
- let els = focusable(el._x_teleport, { includeContainer: true })
|
|
|
- let last = els.slice(-1)[0]
|
|
|
-
|
|
|
- if (last && last.isSameNode(e.target)) {
|
|
|
- el._x_teleport._x_return_focus_to.focus()
|
|
|
- e.preventDefault()
|
|
|
- e.stopPropagation()
|
|
|
- el._x_teleport.removeEventListener('keydown', portalListener)
|
|
|
- }
|
|
|
- })
|
|
|
- })
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- lastTarget = e.target
|
|
|
- })
|
|
|
-
|
|
|
- // document.addEventListener('focusin', e => {
|
|
|
- // // If focusing is happening outside the teleported content.
|
|
|
- // if (! el._x_teleport.contains(e.target)) {
|
|
|
- // // AND the last focused el was inside teleport
|
|
|
- // if (el._x_teleport.contains(lastTarget)) {
|
|
|
- // lastOutsideTarget.focus()
|
|
|
- // } else {
|
|
|
- // // OTHERWISE, we know we are still outside the portal.
|
|
|
- // if (
|
|
|
- // el.compareDocumentPosition(e.target) & Node.DOCUMENT_POSITION_FOLLOWING
|
|
|
- // && el.compareDocumentPosition(lastOutsideTarget) & Node.DOCUMENT_POSITION_PRECEDING
|
|
|
- // ) {
|
|
|
- // // If we did, intercept this focus, and instead focus the first focusable inside the portal.
|
|
|
-
|
|
|
- // let els = tabbable(el._x_teleport, { includeContainer: true, displayCheck: 'none' })
|
|
|
- // // If there is a focusable, focus it, otherwise, bail and let it be past the <template>.
|
|
|
- // if (els[0]) {
|
|
|
- // doFocus = () => els[0].focus()
|
|
|
- // }
|
|
|
- // }
|
|
|
- // }
|
|
|
-
|
|
|
- // lastOutsideTarget = e.target
|
|
|
- // }
|
|
|
-
|
|
|
- // lastTarget = e.target
|
|
|
-
|
|
|
- // doFocus()
|
|
|
- // doFocus = () => {}
|
|
|
- // })
|
|
|
- })
|
|
|
-
|
|
|
Alpine.magic('focus', el => {
|
|
|
let within = el
|
|
|
|
|
@@ -91,6 +19,7 @@ export default function (Alpine) {
|
|
|
within(el) { within = el; return this },
|
|
|
withoutScrolling() { this.__noscroll = true; return this },
|
|
|
withWrapAround() { this.__wrapAround = true; return this },
|
|
|
+ wrap() { return this.withWrapAround() },
|
|
|
focusable(el) {
|
|
|
return isFocusable(el)
|
|
|
},
|
|
@@ -114,9 +43,7 @@ export default function (Alpine) {
|
|
|
},
|
|
|
getFirst() { return this.all()[0] },
|
|
|
getLast() { return this.all(f).slice(-1)[0] },
|
|
|
- first() { this.focus(this.getFirst()) },
|
|
|
- last() { this.focus(this.getLast()) },
|
|
|
- next() {
|
|
|
+ getNext() {
|
|
|
let list = this.all()
|
|
|
let current = document.activeElement
|
|
|
|
|
@@ -125,13 +52,12 @@ export default function (Alpine) {
|
|
|
|
|
|
// This is the last element in the list and we want to wrap around.
|
|
|
if (this.__wrapAround && list.indexOf(current) === list.length - 1) {
|
|
|
- return this.focus(list[0])
|
|
|
+ return list[0]
|
|
|
}
|
|
|
|
|
|
- this.focus(list[list.indexOf(current) + 1])
|
|
|
+ return list[list.indexOf(current) + 1]
|
|
|
},
|
|
|
- prev() { return this.previous() },
|
|
|
- previous() {
|
|
|
+ getPrevious() {
|
|
|
let list = this.all()
|
|
|
let current = document.activeElement
|
|
|
|
|
@@ -140,12 +66,17 @@ export default function (Alpine) {
|
|
|
|
|
|
// This is the first element in the list and we want to wrap around.
|
|
|
if (this.__wrapAround && list.indexOf(current) === 0) {
|
|
|
- return this.focus(list.slice(-1)[0])
|
|
|
+ return list.slice(-1)[0]
|
|
|
}
|
|
|
|
|
|
- this.focus(list[list.indexOf(current) - 1])
|
|
|
+ return list[list.indexOf(current) - 1]
|
|
|
},
|
|
|
- focus(el, wrapEl) {
|
|
|
+ first() { this.focus(this.getFirst()) },
|
|
|
+ last() { this.focus(this.getLast()) },
|
|
|
+ next() { this.focus(this.getNext()) },
|
|
|
+ previous() { this.focus(this.getPrevious()) },
|
|
|
+ prev() { return this.previous() },
|
|
|
+ focus(el) {
|
|
|
if (! el) return
|
|
|
|
|
|
setTimeout(() => {
|