Skip to content

feat(useFocusTrap): expose updateContainerElements for dynamic contai… #4849

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Jul 28, 2025
Merged
Prev Previous commit
Next Next commit
feat: call updateContainerElements when target has changes
  • Loading branch information
PeikyLiu committed Jul 18, 2025
commit be349a201fbceef1ea3b9f498d3398cbeb9779d2
10 changes: 6 additions & 4 deletions packages/integrations/useFocusTrap/demo.vue
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
<script setup lang="ts">
import { useFocusTrap } from '@vueuse/integrations'
import { useTemplateRef } from 'vue'
import { shallowRef, useTemplateRef } from 'vue'

const target = useTemplateRef<HTMLElement>('target')
const target2 = useTemplateRef<HTMLElement>('target2')
const { hasFocus, activate, deactivate, updateContainerElements } = useFocusTrap(target)
const targets = shallowRef<HTMLElement | null>(null)

const { hasFocus, activate, deactivate } = useFocusTrap(targets)

function focusTopForm() {
updateContainerElements(target.value!)
targets.value = target.value
}

function focusBottomForm() {
updateContainerElements(target2.value!)
targets.value = target2.value
}
</script>

Expand Down
82 changes: 39 additions & 43 deletions packages/integrations/useFocusTrap/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { notNullish } from '@vueuse/shared'
import { createFocusTrap } from 'focus-trap'
import { computed, shallowRef, toValue, watch } from 'vue'

type ContainerElements = Parameters<FocusTrap['updateContainerElements']>[0]
export interface UseFocusTrapOptions extends Options {
/**
* Immediately activate the trap
Expand Down Expand Up @@ -54,13 +53,6 @@ export interface UseFocusTrapReturn {
* @see https://github.com/focus-trap/focus-trap#trapunpause
*/
unpause: Fn

/**
* Update the container elements
*
* @see https://github.com/focus-trap/focus-trap?tab=readme-ov-file#trapupdatecontainerelements
*/
updateContainerElements: (el: ContainerElements) => FocusTrap | undefined
}

/**
Expand All @@ -69,15 +61,14 @@ export interface UseFocusTrapReturn {
* @see https://vueuse.org/useFocusTrap
*/
export function useFocusTrap(
target: Arrayable<MaybeRefOrGetter<string> | MaybeComputedElementRef>,
target: MaybeRefOrGetter<Arrayable<MaybeRefOrGetter<string> | MaybeComputedElementRef>>,
options: UseFocusTrapOptions = {},
): UseFocusTrapReturn {
let trap: undefined | FocusTrap

const { immediate, ...focusTrapOptions } = options
const hasFocus = shallowRef(false)
const isPaused = shallowRef(false)

let initial = true
const activate = (opts?: ActivateOptions) => trap && trap.activate(opts)
const deactivate = (opts?: DeactivateOptions) => trap && trap.deactivate(opts)

Expand All @@ -94,51 +85,57 @@ export function useFocusTrap(
isPaused.value = false
}
}

const updateContainerElements = (el: ContainerElements) => {
if (trap) {
trap.updateContainerElements(el)
return trap
}
}

const targets = computed(() => {
const _targets = toValue(target)

return toArray(_targets)
.map((el) => {
const _el = toValue(el)
return typeof _el === 'string' ? _el : unrefElement(_el)
})
.filter(notNullish)
})

watch(
targets,
(els) => {
if (!els.length)
return

trap = createFocusTrap(els, {
...focusTrapOptions,
onActivate() {
hasFocus.value = true

// Apply if user provided onActivate option
if (options.onActivate)
options.onActivate()
},
onDeactivate() {
hasFocus.value = false

// Apply if user provided onDeactivate option
if (options.onDeactivate)
options.onDeactivate()
},
})

// Focus if immediate is set to true
if (immediate)
activate()
if (initial) {
trap = createFocusTrap(els, {
...focusTrapOptions,
onActivate() {
hasFocus.value = true

// Apply if user provided onActivate option
if (options.onActivate)
options.onActivate()
},
onDeactivate() {
hasFocus.value = false

// Apply if user provided onDeactivate option
if (options.onDeactivate)
options.onDeactivate()
},
})

// Focus if immediate is set to true
if (immediate) {
activate()
}

initial = false
}
else {
// get the active state of the trap
const isActive = trap?.active
// update the container elements
trap?.updateContainerElements(els)
// if the trap is not active and immediate is set to true, activate the trap
if (!isActive && immediate) {
activate()
}
}
},
{ flush: 'post' },
)
Expand All @@ -153,6 +150,5 @@ export function useFocusTrap(
deactivate,
pause,
unpause,
updateContainerElements,
}
}
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy