Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(useFocusTrap): new function (#433)
* feat(useFocusTrap): new function * fix issue with focus trap not working when navigated to from other page * Update packages/integrations/useFocusTrap/index.md Co-authored-by: Anthony Fu <anthonyfu117@hotmail.com> * Update packages/integrations/useFocusTrap/index.ts Co-authored-by: Anthony Fu <anthonyfu117@hotmail.com> * fix: update demo. Allow ref change for useFocusTrap * fix: remove unnecessary stop * fix: reflect foucs trap state on button Co-authored-by: Anthony Fu <anthonyfu117@hotmail.com>
- Loading branch information
Showing
9 changed files
with
302 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
export * from './useAxios' | ||
export * from './useCookies' | ||
export * from './useFocusTrap' | ||
export * from './useJwt' | ||
export * from './useNProgress' | ||
export * from './useQRCode' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
<script setup lang="ts"> | ||
import { ref } from 'vue-demi' | ||
import { useFocusTrap } from '.' | ||
const target = ref() | ||
const { hasFocus, activate, deactivate } = useFocusTrap(target) | ||
</script> | ||
|
||
<template> | ||
<div class="flex flex-col items-center"> | ||
<button @click="activate()"> | ||
{{ hasFocus ? 'Focus is trapped within form' : 'Trap focus within form' }} | ||
</button> | ||
<input | ||
type="text" | ||
:placeholder="hasFocus ? 'You can\'t foucs me' : 'You can focus me'" | ||
/> | ||
|
||
<div | ||
ref="target" | ||
class="shadow-lg bg-gray-600 bg-opacity-10 dark:(bg-gray-400 bg-opacity-10) rounded max-w-96 mx-auto p-8" | ||
> | ||
<div class="flex flex-row items-center text-6xl text-center justify-center"> | ||
<twemoji:face-with-monocle v-if="hasFocus" /> | ||
<twemoji:sleeping-face v-else /> | ||
</div> | ||
<input type="text" class="!w-12" placeholder="Email"> | ||
<input type="text" placeholder="Nickname"> | ||
<input placeholder="Password" type="text"> | ||
<div class="flex flex-row justify-center"> | ||
<button @click="deactivate()"> | ||
Free Focus | ||
</button> | ||
</div> | ||
</div> | ||
</div> | ||
</template> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
--- | ||
category: '@Integrations' | ||
--- | ||
|
||
# useFocusTrap | ||
|
||
Reactive wrapper for [`focus-trap`](https://github.com/focus-trap/focus-trap) | ||
|
||
For more info on what options can be passed see [`createOptions`](https://github.com/focus-trap/focus-trap#createfocustrapelement-createoptions) in the `focus-trap` docs. | ||
|
||
## Usage | ||
|
||
**Basic Usage** | ||
|
||
```html | ||
<script setup> | ||
import { ref } from 'vue' | ||
import { useFocusTrap } from '@vueuse/integrations' | ||
const target = ref() | ||
const { hasFocus, activate, deactivate } = useFocusTrap(target) | ||
</script> | ||
|
||
<template> | ||
<div> | ||
<button @click="activate()">Activate</button> | ||
<div ref="target"> | ||
<span>Has Focus: {{ hasFocus }}</span> | ||
<input type="text" /> | ||
<button @click="deactivate()">Deactivate</button> | ||
</div> | ||
</div> | ||
</template> | ||
``` | ||
|
||
**Automatically Focus** | ||
|
||
```html | ||
<script setup> | ||
import { ref } from 'vue' | ||
import { useFocusTrap } from '@vueuse/integrations' | ||
const target = ref() | ||
const { hasFocus, activate, deactivate } = useFocusTrap(target, { immediate: true }) | ||
</script> | ||
|
||
<template> | ||
<div> | ||
<div ref="target">...</div> | ||
</div> | ||
</template> | ||
``` | ||
|
||
<!--FOOTER_STARTS--> | ||
## Type Declarations | ||
|
||
```typescript | ||
export interface UseFocusTrapOptions extends Options { | ||
/** | ||
* Immediately activate the trap | ||
*/ | ||
immediate?: boolean | ||
} | ||
export interface UseFocusTrapReturn { | ||
/** | ||
* Indicates if the focus trap is currently active | ||
*/ | ||
hasFocus: Ref<boolean> | ||
/** | ||
* Indicates if the focus trap is currently paused | ||
*/ | ||
isPaused: Ref<boolean> | ||
/** | ||
* Activate the focus trap | ||
* | ||
* @link https://github.com/focus-trap/focus-trap#trapactivateactivateoptions | ||
* @param opts Activate focus trap options | ||
*/ | ||
activate: (opts?: ActivateOptions) => void | ||
/** | ||
* Deactivate the focus trap | ||
* | ||
* @link https://github.com/focus-trap/focus-trap#trapdeactivatedeactivateoptions | ||
* @param opts Deactivate focus trap options | ||
*/ | ||
deactivate: (opts?: DeactivateOptions) => void | ||
/** | ||
* Pause the focus trap | ||
* | ||
* @link https://github.com/focus-trap/focus-trap#trappause | ||
*/ | ||
pause: Fn | ||
/** | ||
* Unpauses the focus trap | ||
* | ||
* @link https://github.com/focus-trap/focus-trap#trapunpause | ||
*/ | ||
unpause: Fn | ||
} | ||
/** | ||
* Reactive focus-trap | ||
* | ||
* @link https://vueuse.org/integrations/useFocusTrap/ | ||
* @param target The target element to trap focus within | ||
* @param options Focus trap options | ||
* @param autoFocus Focus trap automatically when mounted | ||
*/ | ||
export declare function useFocusTrap( | ||
target: MaybeElementRef, | ||
options?: UseFocusTrapOptions | ||
): UseFocusTrapReturn | ||
``` | ||
|
||
## Source | ||
|
||
[Source](https://github.com/vueuse/vueuse/blob/main/packages/integrations/useFocusTrap/index.ts) • [Demo](https://github.com/vueuse/vueuse/blob/main/packages/integrations/useFocusTrap/demo.vue) • [Docs](https://github.com/vueuse/vueuse/blob/main/packages/integrations/useFocusTrap/index.md) | ||
|
||
|
||
<!--FOOTER_ENDS--> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
import { MaybeElementRef, unrefElement, tryOnUnmounted, Fn } from '@vueuse/core' | ||
import { watch, ref, Ref } from 'vue-demi' | ||
import { ActivateOptions, createFocusTrap, DeactivateOptions, FocusTrap, Options } from 'focus-trap' | ||
|
||
export interface UseFocusTrapOptions extends Options { | ||
/** | ||
* Immediately activate the trap | ||
*/ | ||
immediate?: boolean | ||
} | ||
|
||
export interface UseFocusTrapReturn { | ||
/** | ||
* Indicates if the focus trap is currently active | ||
*/ | ||
hasFocus: Ref<boolean> | ||
|
||
/** | ||
* Indicates if the focus trap is currently paused | ||
*/ | ||
isPaused: Ref<boolean> | ||
|
||
/** | ||
* Activate the focus trap | ||
* | ||
* @link https://github.com/focus-trap/focus-trap#trapactivateactivateoptions | ||
* @param opts Activate focus trap options | ||
*/ | ||
activate: (opts?: ActivateOptions) => void | ||
|
||
/** | ||
* Deactivate the focus trap | ||
* | ||
* @link https://github.com/focus-trap/focus-trap#trapdeactivatedeactivateoptions | ||
* @param opts Deactivate focus trap options | ||
*/ | ||
deactivate: (opts?: DeactivateOptions) => void | ||
|
||
/** | ||
* Pause the focus trap | ||
* | ||
* @link https://github.com/focus-trap/focus-trap#trappause | ||
*/ | ||
pause: Fn | ||
|
||
/** | ||
* Unpauses the focus trap | ||
* | ||
* @link https://github.com/focus-trap/focus-trap#trapunpause | ||
*/ | ||
unpause: Fn | ||
} | ||
|
||
/** | ||
* Reactive focus-trap | ||
* | ||
* @link https://vueuse.org/useFocusTrap | ||
* @param target The target element to trap focus within | ||
* @param options Focus trap options | ||
* @param autoFocus Focus trap automatically when mounted | ||
*/ | ||
export function useFocusTrap(target: MaybeElementRef, options: UseFocusTrapOptions = {}): UseFocusTrapReturn { | ||
let trap: undefined | FocusTrap | ||
|
||
const { immediate, ...focusTrapOptions } = options | ||
const hasFocus = ref(false) | ||
const isPaused = ref(false) | ||
|
||
const activate = (opts?: ActivateOptions) => trap && trap.activate(opts) | ||
const deactivate = (opts?: DeactivateOptions) => trap && trap.deactivate(opts) | ||
|
||
const pause = () => { | ||
if (trap) { | ||
trap.pause() | ||
isPaused.value = true | ||
} | ||
} | ||
|
||
const unpause = () => { | ||
if (trap) { | ||
trap.unpause() | ||
isPaused.value = false | ||
} | ||
} | ||
|
||
watch( | ||
() => unrefElement(target), | ||
(el: HTMLElement) => { | ||
trap = createFocusTrap(el, { | ||
...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() | ||
}, { flush: 'post' }) | ||
|
||
// Cleanup on unmount | ||
tryOnUnmounted(() => deactivate()) | ||
|
||
return { | ||
hasFocus, | ||
isPaused, | ||
activate, | ||
deactivate, | ||
pause, | ||
unpause, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters