Skip to content

Commit

Permalink
feat(UseFocusTrap): component (vitest-dev#820)
Browse files Browse the repository at this point in the history
Co-authored-by: Anthony Fu <anthonyfu117@hotmail.com>
  • Loading branch information
harmyderoman and antfu committed Oct 16, 2021
1 parent 223bfce commit bdea8ea
Show file tree
Hide file tree
Showing 6 changed files with 126 additions and 13 deletions.
2 changes: 1 addition & 1 deletion packages/core/types.ts
Expand Up @@ -7,7 +7,7 @@ export interface Position {

export interface RenderableComponent {
/**
* The elment that the component should be rendered as
* The element that the component should be rendered as
*
* @default 'div'
*/
Expand Down
4 changes: 4 additions & 0 deletions packages/integrations/package.json
Expand Up @@ -36,6 +36,10 @@
"import": "./useFocusTrap.mjs",
"require": "./useFocusTrap.cjs"
},
"./useFocusTrap/component": {
"import": "./useFocusTrap/component.mjs",
"require": "./useFocusTrap/component.cjs"
},
"./useFuse": {
"import": "./useFuse.mjs",
"require": "./useFuse.cjs"
Expand Down
32 changes: 32 additions & 0 deletions packages/integrations/useFocusTrap/component.ts
@@ -0,0 +1,32 @@
import { h, ref, defineComponent, watch, onUnmounted } from 'vue-demi'
import { createFocusTrap } from 'focus-trap'
import type { FocusTrap } from 'focus-trap'
import { unrefElement } from '@vueuse/core'
import { RenderableComponent } from '@vueuse/core/types'

export const UseFocusTrap = defineComponent<RenderableComponent>({
name: 'UseFocusTrap',
props: ['as'] as unknown as undefined,
setup(props, { slots }) {
let trap: undefined | FocusTrap
const target = ref()

const activate = () => trap && trap.activate()
const deactivate = () => trap && trap.deactivate()
watch(
() => unrefElement(target),
(el) => {
if (!el)
return
trap = createFocusTrap(el, {})
activate()
}, { flush: 'post' })

// Cleanup on unmount
onUnmounted(() => deactivate())
return () => {
if (slots.default)
return h(props.as || 'div', { ref: target }, slots.default())
}
},
})
20 changes: 20 additions & 0 deletions packages/integrations/useFocusTrap/index.md
Expand Up @@ -50,3 +50,23 @@ const { hasFocus, activate, deactivate } = useFocusTrap(target, { immediate: tru
</div>
</template>
```

## Using Component

This function can't properly activate focus on elements with conditional rendering. In this case, you can use the `UseFocusTrap` component. Focus Trap will be activated automatically on mounting this component and deactivated on unmount.

```html
<script setup>
import { ref } from 'vue'
import { UseFocusTrap } from '@vueuse/integrations/useFocusTrap/component'
const show = ref(false)
</script>

<template>
<UseFocusTrap v-if="show">
<div class="modal">...</div>
</UseFocusTrap>
</template>

```
68 changes: 57 additions & 11 deletions scripts/rollup.config.ts
Expand Up @@ -5,6 +5,7 @@ import dts from 'rollup-plugin-dts'
import type { OutputOptions, Plugin, RollupOptions } from 'rollup'
import fg from 'fast-glob'
import { packages } from '../meta/packages'
import { functions } from '../meta/function-indexes'

const VUE_DEMI_IIFE = fs.readFileSync(require.resolve('vue-demi/lib/index.iife.js'), 'utf-8')
const configs: RollupOptions[] = []
Expand All @@ -16,6 +17,19 @@ const injectVueDemi: Plugin = {
},
}

const buildPlugins = [
esbuild(),
]

const dtsPlugin = [
dts(),
]

const externals = [
'vue-demi',
'@vueuse/shared',
]

const esbuildMinifer = (options: ESBuildOptions) => {
const { renderChunk } = esbuild(options)

Expand All @@ -40,7 +54,11 @@ for (const { globals, name, external, submodules, iife } of packages) {
functionNames.push(...fg.sync('*/index.ts', { cwd: resolve(`packages/${name}`) }).map(i => i.split('/')[0]))

for (const fn of functionNames) {
const input = fn === 'index' ? `packages/${name}/index.ts` : `packages/${name}/${fn}/index.ts`
const input = fn === 'index'
? `packages/${name}/index.ts`
: `packages/${name}/${fn}/index.ts`

const info = functions.find(i => i.name === fn)

const output: OutputOptions[] = [
{
Expand Down Expand Up @@ -84,12 +102,9 @@ for (const { globals, name, external, submodules, iife } of packages) {
configs.push({
input,
output,
plugins: [
esbuild(),
],
plugins: buildPlugins,
external: [
'vue-demi',
'@vueuse/shared',
...externals,
...(external || []),
],
})
Expand All @@ -100,15 +115,46 @@ for (const { globals, name, external, submodules, iife } of packages) {
file: `packages/${name}/dist/${fn}.d.ts`,
format: 'es',
},
plugins: [
dts(),
],
plugins: dtsPlugin,
external: [
'vue-demi',
'@vueuse/shared',
...externals,
...(external || []),
],
})

if (info?.component) {
configs.push({
input: `packages/${name}/${fn}/component.ts`,
output: [
{
file: `packages/${name}/dist/${fn}/component.cjs`,
format: 'cjs',
},
{
file: `packages/${name}/dist/${fn}/component.mjs`,
format: 'es',
},
],
plugins: buildPlugins,
external: [
...externals,
...(external || []),
],
})

configs.push({
input: `packages/${name}/${fn}/component.ts`,
output: {
file: `packages/${name}/dist/${fn}/component.d.ts`,
format: 'es',
},
plugins: dtsPlugin,
external: [
...externals,
...(external || []),
],
})
}
}
}

Expand Down
13 changes: 12 additions & 1 deletion scripts/utils.ts
Expand Up @@ -183,7 +183,12 @@ export async function updateImport({ packages, functions }: PackageIndexes) {
imports = functions
.sort((a, b) => a.name.localeCompare(b.name))
.flatMap((fn) => {
const arr = []
const arr: string[] = []

// don't include integration components
if (fn.package === 'integrations')
return arr

if (fn.component)
arr.push(`export * from '../${fn.package}/${fn.name}/component'`)
if (fn.directive)
Expand Down Expand Up @@ -371,6 +376,12 @@ export async function updatePackageJSON(indexes: PackageIndexes) {
import: `./${i.name}.mjs`,
require: `./${i.name}.cjs`,
}
if (i.component) {
packageJSON.exports[`./${i.name}/component`] = {
import: `./${i.name}/component.mjs`,
require: `./${i.name}/component.cjs`,
}
}
})
}

Expand Down

0 comments on commit bdea8ea

Please sign in to comment.