Skip to content
This repository was archived by the owner on Sep 20, 2024. It is now read-only.

Commit 3b8ba06

Browse files
committed
fix(focus-lock): fix focus lock component and hook
1 parent 4df1c90 commit 3b8ba06

File tree

24 files changed

+619
-417
lines changed

24 files changed

+619
-417
lines changed

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
"scaffold": "hygen generator",
2020
"release": "yarn changeset publish",
2121
"build": "lerna run build --no-private --stream",
22-
"dev": "NODE_ENV=development vite serve playground --config ./vite.config.ts",
22+
"dev": "NODE_ENV=development vite serve playground --config ./vite.config.ts --open",
2323
"playground:build": "yarn install && yarn build && yarn bootstrap && NODE_ENV=production vite build playground --config ./vite.config.ts",
2424
"test": "cross-env NODE_ENV=test jest --config jest.config.js",
2525
"lint": "eslint '*/**/*.{js,ts,tsx}' --quiet --fix",
@@ -66,6 +66,7 @@
6666
"@vue/eslint-config-typescript": "^5.1.0",
6767
"@vuedx/typecheck": "^0.4.1",
6868
"@vuedx/typescript-plugin-vue": "^0.4.1",
69+
"@vueuse/integrations": "^4.8.1",
6970
"@vueuse/motion": "^1.5.4",
7071
"aria-hidden": "^1.1.2",
7172
"axe-core": "^4.1.2",
@@ -79,6 +80,7 @@
7980
"css-get-unit": "^1.0.1",
8081
"csstype": "^3.0.5",
8182
"dequal": "^2.0.2",
83+
"dom-focus-lock": "^1.0.4",
8284
"esbuild-jest": "^0.4.0",
8385
"eslint": "^7.0.0",
8486
"eslint-config-prettier": "^6.12.0",

packages/c-button/examples/with-button-variants.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<template>
22
<div>
3+
<c-button mr="3" variant="luxury"> Solid </c-button>
34
<c-button mr="3" variant="solid" color-scheme="teal"> Solid </c-button>
45
<c-button mr="3" variant="outline" color-scheme="teal"> Outline </c-button>
56
<c-button mr="3" variant="ghost" color-scheme="teal"> Ghost </c-button>

packages/c-button/src/button-group.ts

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -64,29 +64,28 @@ const CButtonGroup = defineComponent({
6464
variant: props.variant,
6565
isDisabled: props.isDisabled,
6666
}))
67-
return () => {
68-
const styles = computed(() => {
69-
let groupStyles: SystemStyleObject = {
70-
display: 'inline-flex',
71-
}
67+
const styles = computed(() => {
68+
let groupStyles: SystemStyleObject = {
69+
display: 'inline-flex',
70+
}
7271

73-
if (props.isAttached) {
74-
groupStyles = {
75-
...groupStyles,
76-
'> *:first-of-type:not(:last-of-type)': { borderRightRadius: 0 },
77-
'> *:not(:first-of-type):not(:last-of-type)': { borderRadius: 0 },
78-
'> *:not(:first-of-type):last-of-type': { borderLeftRadius: 0 },
79-
}
80-
} else {
81-
groupStyles = {
82-
...groupStyles,
83-
'& > *:not(style) ~ *:not(style)': { marginLeft: props.spacing },
84-
}
72+
if (props.isAttached) {
73+
groupStyles = {
74+
...groupStyles,
75+
'> *:first-of-type:not(:last-of-type)': { borderRightRadius: 0 },
76+
'> *:not(:first-of-type):not(:last-of-type)': { borderRadius: 0 },
77+
'> *:not(:first-of-type):last-of-type': { borderLeftRadius: 0 },
8578
}
79+
} else {
80+
groupStyles = {
81+
...groupStyles,
82+
'& > *:not(style) ~ *:not(style)': { marginLeft: props.spacing },
83+
}
84+
}
8685

87-
return groupStyles
88-
})
89-
86+
return groupStyles
87+
})
88+
return () => {
9089
return h(
9190
chakra('div', { label: 'button__group' }),
9291
{

packages/c-button/src/button.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,13 @@ const CButtonSpinner = defineComponent({
2121
>,
2222
},
2323
setup(props, { attrs }) {
24+
const spinnerStyles = computed<SystemStyleObject>(() => ({
25+
display: 'flex',
26+
alignItems: 'center',
27+
position: props.label ? 'relative' : 'absolute',
28+
marginEnd: props.label ? props.spacing : 0,
29+
}))
2430
return () => {
25-
const spinnerStyles = computed<SystemStyleObject>(() => ({
26-
display: 'flex',
27-
alignItems: 'center',
28-
position: props.label ? 'relative' : 'absolute',
29-
marginEnd: props.label ? props.spacing : 0,
30-
}))
3131
return h(
3232
chakra('div', {
3333
label: 'button__spinner',

packages/c-button/src/icon-button.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@ const CIconButton = defineComponent({
2222
name: 'CIconButton',
2323
props: IconButtonProps,
2424
setup(props, { attrs }) {
25+
if (!props.ariaLabel) {
26+
console.error(
27+
`chakra-ui: The \`aria-label\` prop is required for the <c-icon-button />`
28+
)
29+
}
2530
return () => {
26-
if (!props.ariaLabel) {
27-
console.error(
28-
`chakra-ui: The \`aria-label\` prop is required for the <c-icon-button />`
29-
)
30-
}
3131
return h(
3232
CButton,
3333
{

packages/c-close-button/src/c-close-button.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ const CCloseIcon = defineComponent({
2020
name: 'close',
2121
...attrs,
2222
},
23-
[
23+
() => [
2424
h('path', {
2525
fill: 'currentColor',
2626
d:

packages/c-focus-lock/examples/with-focus-lock-component.vue

Lines changed: 64 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,105 @@
11
<template>
22
<chakra.div>
3-
<c-focus-lock
4-
p="4"
5-
border="4px dashed"
6-
rounded="lg"
7-
border-color="gray.400"
8-
d="inline-block"
9-
:is-disabled="isDisabled"
10-
@activate="handleActivate"
11-
@deactivate="handleDeactivate"
12-
>
13-
<chakra.div>
3+
<chakra.pre font-weight="bold">
4+
Focus trap enabled: {{ isActive }}
5+
</chakra.pre>
6+
<c-portal to="#new-target">
7+
<c-focus-lock
8+
p="4"
9+
border="4px dashed"
10+
rounded="lg"
11+
border-color="gray.400"
12+
d="inline-block"
13+
@activate="handleActivate"
14+
@deactivate="handleDeactivate"
15+
:allow-outside-click="false"
16+
:initial-focus-ref="() => initialFocusRef"
17+
:final-focus-ref="() => finalFocusRef"
18+
#default="{ hasFocus, deactivate }"
19+
pos="relative"
20+
v-if="isActive"
21+
>
22+
<c-close-button
23+
position="absolute"
24+
top="10px"
25+
right="10px"
26+
@click="deactivate"
27+
></c-close-button>
28+
<chakra.pre> Focus trap enabled: {{ hasFocus }} </chakra.pre>
1429
<chakra.p mb="2">Inside focus trap</chakra.p>
15-
<c-button @click="isDisabled = false" color-scheme="teal">
16-
Enable focus lock
17-
</c-button>
18-
<c-button ref="initialFocus" color-scheme="yellow" mx="2"
19-
>Button 2</c-button
30+
<c-button color-scheme="teal"> Login </c-button>
31+
<c-button :ref="initialFocus" color-scheme="yellow" mx="2"
32+
>Initial focus!</c-button
2033
>
21-
<c-button color-scheme="blue">Button 3</c-button>
22-
</chakra.div>
23-
</c-focus-lock>
34+
<c-button left-icon="user" color-scheme="red">Delete account</c-button>
35+
</c-focus-lock>
36+
</c-portal>
37+
2438
<chakra.div
2539
p="4"
2640
border="4px dashed"
2741
rounded="lg"
2842
border-color="gray.400"
2943
mt="4"
3044
d="inline-block"
45+
position="absolute"
46+
bottom="100px"
47+
right="100px"
3148
>
3249
<chakra.p mb="2">Outside focus trap</chakra.p>
33-
<c-button @click="isDisabled = true"> Deactivate </c-button>
34-
<c-button @click="isDisabled = false" ml="3" color-scheme="blue">
35-
Enable
50+
<c-button @click="isActive = true" color-scheme="blue">
51+
Mount focus trap
52+
</c-button>
53+
<c-button :ref="finalFocus" @click="isActive = false" ml="3">
54+
Final focus element
3655
</c-button>
3756
</chakra.div>
38-
<chakra.pre> enabled: {{ !isDisabled }} </chakra.pre>
3957
</chakra.div>
4058
</template>
4159

4260
<script lang="ts">
43-
import { CFocusLock } from '@chakra-ui/c-focus-lock'
61+
import { useRef } from '@chakra-ui/vue-utils'
4462
import { defineComponent, ref } from 'vue'
63+
import { CFocusLock } from '../src/c-focus-lock'
64+
65+
if (!document.getElementById('new-target')) {
66+
const target = document.createElement('div')
67+
target.style.display = 'inline-block'
68+
target.style.position = 'absolute'
69+
target.style.top = '50px'
70+
target.style.left = '250px'
71+
72+
target.id = 'new-target'
73+
document.body.appendChild(target)
74+
}
4575
4676
export default defineComponent({
4777
components: {
4878
CFocusLock,
4979
},
5080
setup() {
51-
const isDisabled = ref(false)
81+
const isActive = ref(false)
82+
const [finalFocus, finalFocusRef] = useRef()
83+
const [initialFocus, initialFocusRef] = useRef()
5284
5385
const handleActivate = () => {
5486
console.log('focuslock activated')
5587
}
5688
5789
const handleDeactivate = () => {
5890
console.log('focuslock deactivated')
91+
isActive.value = false
92+
setTimeout(() => {})
5993
}
6094
6195
return {
62-
isDisabled,
96+
isActive,
6397
handleActivate,
6498
handleDeactivate,
99+
finalFocus,
100+
finalFocusRef,
101+
initialFocus,
102+
initialFocusRef,
65103
}
66104
},
67105
})
Lines changed: 68 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,91 @@
11
<template>
2+
<!--
3+
I noticed that when using the focus lock in a template with a component at the root,
4+
the application breaks, which i
5+
-->
26
<chakra.div>
3-
<chakra.div
4-
:ref="lock"
5-
p="4"
6-
border="4px dashed"
7-
rounded="lg"
8-
border-color="gray.400"
9-
d="inline-block"
10-
>
11-
<chakra.p mb="2">Inside focus trap</chakra.p>
12-
<c-button @click="options.enabled = true" color-scheme="teal">
13-
Enable focus lock
14-
</c-button>
15-
<c-button :ref="initialFocus" color-scheme="yellow" mx="2"
16-
>Button 2</c-button
7+
<c-portal to="#new-target">
8+
<chakra.div
9+
:ref="lock"
10+
p="4"
11+
border="4px dashed"
12+
rounded="lg"
13+
border-color="gray.400"
14+
d="inline-block"
15+
position="relative"
16+
v-if="isLocked"
1717
>
18-
<c-button color-scheme="blue">Button 3</c-button>
19-
</chakra.div>
18+
<c-close-button
19+
position="absolute"
20+
top="10px"
21+
right="10px"
22+
@click="deactivate"
23+
></c-close-button>
24+
<chakra.p mb="2">Inside focus trap</chakra.p>
25+
<c-button color-scheme="teal"> Login </c-button>
26+
<c-button color-scheme="yellow" :ref="initialFocus" mx="2"
27+
>Initial focus!</c-button
28+
>
29+
<c-button left-icon="user" color-scheme="red">Delete account</c-button>
30+
</chakra.div>
31+
</c-portal>
2032
<chakra.div
2133
p="4"
2234
border="4px dashed"
2335
rounded="lg"
2436
border-color="gray.400"
2537
mt="4"
2638
d="inline-block"
39+
position="absolute"
40+
bottom="100px"
41+
right="100px"
2742
>
2843
<chakra.p mb="2">Outside focus trap</chakra.p>
29-
<c-button @click="options.enabled = false"> Deactivate </c-button>
30-
<c-button @click="options.enabled = true" ml="3" color-scheme="blue">
31-
Enable
32-
</c-button>
44+
<c-button @click="deactivate"> Deactivate </c-button>
45+
<c-button @click="activate" ml="3" color-scheme="blue">Enable</c-button>
3346
</chakra.div>
3447
<chakra.pre font-weight="bold">
35-
Focus lock enabled: {{ options.enabled }}
48+
Focus lock enabled: {{ hasFocus }}
3649
</chakra.pre>
3750
</chakra.div>
3851
</template>
3952

40-
<script lang="ts">
41-
import { useFocusLock, FocusLockOptions } from '@chakra-ui/c-focus-lock'
42-
import { defineComponent, reactive } from 'vue'
53+
<script setup lang="ts">
54+
import { useFocusLock } from '../src/use-focus-lock'
55+
import { defineComponent, nextTick, onBeforeMount, ref } from 'vue'
56+
57+
const isLocked = ref(false)
4358
44-
export default defineComponent({
45-
setup() {
46-
const options: FocusLockOptions = reactive({
47-
enabled: true,
48-
escapeDeactivates: false,
49-
onActivate: () => {
50-
console.log('focus lock ENABLED')
51-
},
52-
onDeactivate: () => {
53-
console.log('focus lock DEACTIVATED')
54-
},
55-
})
59+
if (!document.getElementById('new-target')) {
60+
const target = document.createElement('div')
61+
target.style.display = 'inline-block'
62+
target.style.position = 'absolute'
63+
target.style.top = '50px'
64+
target.style.left = '250px'
5665
57-
const { lock, initialFocus } = useFocusLock(options)
66+
target.id = 'new-target'
67+
document.body.appendChild(target)
68+
}
5869
59-
return {
60-
lock,
61-
options,
62-
initialFocus,
63-
}
64-
},
70+
const {
71+
hasFocus,
72+
lock,
73+
activate: lockactivate,
74+
deactivate: lockdeactivate,
75+
initialFocus,
76+
} = useFocusLock({
77+
escapeDeactivates: false,
78+
delayInitialFocus: true,
6579
})
80+
81+
const activate = async () => {
82+
isLocked.value = true
83+
// setTimeout(lockactivate)
84+
lockactivate()
85+
}
86+
87+
const deactivate = () => {
88+
lockdeactivate()
89+
isLocked.value = false
90+
}
6691
</script>

0 commit comments

Comments
 (0)