Add supporting themes required for Lotusdocs

This commit is contained in:
Abner Coimbre
2026-01-11 16:48:19 -08:00
parent 8a4d04db58
commit f8d40c4e41
1289 changed files with 234948 additions and 0 deletions

View File

@@ -0,0 +1,5 @@
import trap from '../src/index.js'
document.addEventListener('alpine:init', () => {
window.Alpine.plugin(trap)
})

View File

@@ -0,0 +1,5 @@
import focus from '../src/index.js'
export default focus
export { focus }

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,20 @@
{
"name": "@alpinejs/focus",
"version": "3.13.8",
"description": "Manage focus within a page",
"homepage": "https://alpinejs.dev/plugins/focus",
"repository": {
"type": "git",
"url": "https://github.com/alpinejs/alpine.git",
"directory": "packages/focus"
},
"author": "Caleb Porzio",
"license": "MIT",
"main": "dist/module.cjs.js",
"module": "dist/module.esm.js",
"unpkg": "dist/cdn.min.js",
"dependencies": {
"focus-trap": "^6.9.4",
"tabbable": "^5.3.3"
}
}

View File

@@ -0,0 +1,210 @@
import { createFocusTrap } from 'focus-trap'
import { focusable, isFocusable } from 'tabbable'
export default function (Alpine) {
let lastFocused
let currentFocused
window.addEventListener('focusin', () => {
lastFocused = currentFocused
currentFocused = document.activeElement
})
Alpine.magic('focus', el => {
let within = el
return {
__noscroll: false,
__wrapAround: false,
within(el) { within = el; return this },
withoutScrolling() { this.__noscroll = true; return this },
noscroll() { this.__noscroll = true; return this },
withWrapAround() { this.__wrapAround = true; return this },
wrap() { return this.withWrapAround() },
focusable(el) {
return isFocusable(el)
},
previouslyFocused() {
return lastFocused
},
lastFocused() {
return lastFocused
},
focused() {
return currentFocused
},
focusables() {
if (Array.isArray(within)) return within
return focusable(within, { displayCheck: 'none' })
},
all() { return this.focusables() },
isFirst(el) {
let els = this.all()
return els[0] && els[0].isSameNode(el)
},
isLast(el) {
let els = this.all()
return els.length && els.slice(-1)[0].isSameNode(el)
},
getFirst() { return this.all()[0] },
getLast() { return this.all().slice(-1)[0] },
getNext() {
let list = this.all()
let current = document.activeElement
// Can't find currently focusable element in list.
if (list.indexOf(current) === -1) return
// This is the last element in the list and we want to wrap around.
if (this.__wrapAround && list.indexOf(current) === list.length - 1) {
return list[0]
}
return list[list.indexOf(current) + 1]
},
getPrevious() {
let list = this.all()
let current = document.activeElement
// Can't find currently focusable element in list.
if (list.indexOf(current) === -1) return
// This is the first element in the list and we want to wrap around.
if (this.__wrapAround && list.indexOf(current) === 0) {
return list.slice(-1)[0]
}
return list[list.indexOf(current) - 1]
},
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(() => {
if (! el.hasAttribute('tabindex')) el.setAttribute('tabindex', '0')
el.focus({ preventScroll: this.__noscroll })
})
}
}
})
Alpine.directive('trap', Alpine.skipDuringClone(
(el, { expression, modifiers }, { effect, evaluateLater, cleanup }) => {
let evaluator = evaluateLater(expression)
let oldValue = false
let options = {
escapeDeactivates: false,
allowOutsideClick: true,
fallbackFocus: () => el,
}
if (modifiers.includes('noautofocus')) {
options.initialFocus = false
} else {
let autofocusEl = el.querySelector('[autofocus]')
if (autofocusEl) options.initialFocus = autofocusEl
}
let trap = createFocusTrap(el, options)
let undoInert = () => {}
let undoDisableScrolling = () => {}
const releaseFocus = () => {
undoInert()
undoInert = () => {}
undoDisableScrolling()
undoDisableScrolling = () => {}
trap.deactivate({
returnFocus: !modifiers.includes('noreturn')
})
}
effect(() => evaluator(value => {
if (oldValue === value) return
// Start trapping.
if (value && ! oldValue) {
if (modifiers.includes('noscroll')) undoDisableScrolling = disableScrolling()
if (modifiers.includes('inert')) undoInert = setInert(el)
// Activate the trap after a generous tick. (Needed to play nice with transitions...)
setTimeout(() => {
trap.activate()
}, 15)
}
// Stop trapping.
if (! value && oldValue) {
releaseFocus()
}
oldValue = !! value
}))
cleanup(releaseFocus)
},
// When cloning, we only want to add aria-hidden attributes to the
// DOM and not try to actually trap, as trapping can mess with the
// live DOM and isn't just isolated to the cloned DOM.
(el, { expression, modifiers }, { evaluate }) => {
if (modifiers.includes('inert') && evaluate(expression)) setInert(el)
},
))
}
function setInert(el) {
let undos = []
crawlSiblingsUp(el, (sibling) => {
let cache = sibling.hasAttribute('aria-hidden')
sibling.setAttribute('aria-hidden', 'true')
undos.push(() => cache || sibling.removeAttribute('aria-hidden'))
})
return () => {
while(undos.length) undos.pop()()
}
}
function crawlSiblingsUp(el, callback) {
if (el.isSameNode(document.body) || ! el.parentNode) return
Array.from(el.parentNode.children).forEach(sibling => {
if (sibling.isSameNode(el)) {
crawlSiblingsUp(el.parentNode, callback)
} else {
callback(sibling)
}
})
}
function disableScrolling() {
let overflow = document.documentElement.style.overflow
let paddingRight = document.documentElement.style.paddingRight
let scrollbarWidth = window.innerWidth - document.documentElement.clientWidth
document.documentElement.style.overflow = 'hidden'
document.documentElement.style.paddingRight = `${scrollbarWidth}px`
return () => {
document.documentElement.style.overflow = overflow
document.documentElement.style.paddingRight = paddingRight
}
}