trap-focus-svelte
Advanced tools
Comparing version 1.0.2 to 1.1.0-rc1
@@ -1,1 +0,1 @@ | ||
let t=[];function e(t,e,n){return t.addEventListener(e,n),()=>t.removeEventListener(e,n)}function n(n,o=1){let c=0;const u=e(document,"keydown",(t=>c=t.shiftKey&&"Tab"==t.key));function r(){const t=[...n.querySelectorAll("*")].filter((t=>t.tabIndex>=0));return[t.at(0)??n,t.at(-1)??n]}let s;function f(){t.push(n),s=document.activeElement,r().at(0).focus()}function i(){t=t.filter((t=>t!=n)),s.focus()}o&&f();const a=e=>t.at(-1)?.contains(e),d=e(n,"focusout",(t=>{if(a(n)){const[e,n]=r();t.target==e&&c?n.focus():t.target!=n||c||e.focus()}})),l=e(document,"focusin",(t=>{if(a(n)&&!a(t.target)){const[t,e]=r();(c?e:t).focus()}}));return{update(t){t?f():i()},destroy(){u(),l(),d(),i()}}}export{n as trapFocus}; | ||
let t=[];function e(e,n=1){function o(){const t=[...e.querySelectorAll("*")].filter((t=>t.tabIndex>=0));return[t.at(0)??e,t.at(-1)??e]}let c;function u(){t.push(e),c=document.activeElement,o().at(0).focus()}function r(){t.pop(),c.focus()}n&&u();const s=e=>t.at(-1)?.contains(e),a=((f=document).addEventListener("focusin",i=t=>{if(!s(e)||s(t.target))return;const[n,c]=o(),u=t.relatedTarget;u&&s(u)&&u!==c?u!==n&&u!==e?u.focus():c.focus():n.focus()}),()=>f.removeEventListener("focusin",i));var f,i;return{update(t){t?u():r()},destroy(){a(),r()}}}export{e as trapFocus}; |
73
index.ts
@@ -11,13 +11,16 @@ let stack: HTMLElement[] = [] | ||
function trapFocus(wrap: HTMLElement, active = true) { | ||
// true if tabbing backwards with shift + tab | ||
let shiftTab = false | ||
/** time when tab was last pressed */ | ||
// let tabTime: number | ||
// /** true if shift key was pressed while tab was pressed */ | ||
// let shiftKey: boolean | ||
// set shiftTab to true if shift + tab is pressed | ||
const shiftTabListener = listen( | ||
document, | ||
'keydown', | ||
(e: KeyboardEvent) => (shiftTab = e.shiftKey && e.key == 'Tab') | ||
) | ||
// /** sets tabTime and shiftKey */ | ||
// const shiftTabListener = listen(document, 'keydown', (e: KeyboardEvent) => { | ||
// if (e.code == 'Tab') { | ||
// shiftKey = e.shiftKey | ||
// tabTime = e.timeStamp | ||
// } | ||
// }) | ||
// return the first and last focusable children | ||
/** return the first and last focusable children */ | ||
function getFirstAndLastFocusable() { | ||
@@ -41,3 +44,3 @@ const els = [...wrap.querySelectorAll('*')].filter( | ||
function removeFromStack() { | ||
stack = stack.filter((el) => el != wrap) | ||
stack.pop() | ||
lastActiveElement.focus() | ||
@@ -54,21 +57,35 @@ } | ||
/** loop focus if leaving first of last focusable element in wrap */ | ||
const focusOutListener = listen(wrap, 'focusout', (e: FocusEvent) => { | ||
if (inCurrentTrap(wrap)) { | ||
const [firstFocusableEl, lastFocusableEl] = getFirstAndLastFocusable() | ||
if (e.target == firstFocusableEl && shiftTab) { | ||
lastFocusableEl.focus() | ||
} else if (e.target == lastFocusableEl && !shiftTab) { | ||
firstFocusableEl.focus() | ||
} | ||
/** moves focus back to wrap if something outside the wrap is focused */ | ||
const focusOutListener = listen(document, 'focusin', (e: FocusEvent) => { | ||
// return if ths trap is not active | ||
// return if focus is inside the trap | ||
if (!inCurrentTrap(wrap) || inCurrentTrap(e.target as HTMLElement)) { | ||
return | ||
} | ||
}) | ||
const [firstFocusable, lastFocusable] = getFirstAndLastFocusable() | ||
const previousFocusable = e.relatedTarget as HTMLElement | ||
// const isTab = e.timeStamp - tabTime < 50 | ||
/** moves focus back to wrap if something outside the wrap is focused */ | ||
const focusListener = listen(document, 'focusin', (e: FocusEvent) => { | ||
if (inCurrentTrap(wrap) && !inCurrentTrap(e.target as HTMLElement)) { | ||
const [first, last] = getFirstAndLastFocusable() | ||
let focusEl = shiftTab ? last : first | ||
focusEl.focus() | ||
// if no previousFocusable, focus first focusable | ||
// if previousFocusable is not in the trap, focus first focusable | ||
// if last element, focus first focusable | ||
if ( | ||
!previousFocusable || | ||
!inCurrentTrap(previousFocusable) || | ||
previousFocusable === lastFocusable | ||
) { | ||
// console.log('last element and tab within time (but not shift tab) focus first element') | ||
firstFocusable.focus() | ||
return | ||
} | ||
// if first element and shift tab within time, focus last element | ||
if (previousFocusable === firstFocusable || previousFocusable === wrap) { | ||
// console.log('first element and shift tab within time, focus last element') | ||
lastFocusable.focus() | ||
return | ||
} | ||
// fall back to focus on previousFocusable (we made sure it was in current trap above) | ||
// console.log('fall back to focus on relatedTarget') | ||
previousFocusable.focus() | ||
}) | ||
@@ -87,5 +104,5 @@ | ||
destroy() { | ||
shiftTabListener() | ||
focusListener() | ||
// shiftTabListener() | ||
focusOutListener() | ||
// focusOutListener() | ||
removeFromStack() | ||
@@ -92,0 +109,0 @@ }, |
{ | ||
"name": "trap-focus-svelte", | ||
"version": "1.0.2", | ||
"version": "1.1.0-rc1", | ||
"license": "MIT", | ||
@@ -5,0 +5,0 @@ "description": "Small 0.4kB focus trap that supports stacking, toggling, and dynamic content. Compatible with any framework.", |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
12524
182
1