1
1
import {
2
2
computed ,
3
3
getCurrentInstance ,
4
- nextTick ,
5
4
onBeforeMount ,
6
5
onBeforeUnmount ,
7
- onMounted ,
8
6
ref ,
9
7
Ref ,
10
- SetupContext ,
11
8
ToRefs ,
12
9
toRefs ,
13
10
VNodeProps ,
@@ -16,7 +13,7 @@ import {
16
13
} from 'vue'
17
14
import { useIds } from '@chakra-ui/vue-composables'
18
15
import { FocusLockProps , useFocusLock } from '@chakra-ui/c-focus-lock'
19
- import { MaybeElementRef , useRef } from '@chakra-ui/vue-utils'
16
+ import { MaybeElementRef , useRef , getSelector } from '@chakra-ui/vue-utils'
20
17
import { hideOthers , Undo } from 'aria-hidden'
21
18
import { FocusTarget } from 'focus-trap'
22
19
import { focus , FocusableElement } from '@chakra-ui/utils'
@@ -128,7 +125,7 @@ export function useModal(options: UseModalOptions) {
128
125
*/
129
126
const shouldHide = computed ( ( ) => isOpen . value && useInert ?. value )
130
127
useAriaHidden ( dialogRefEl , shouldHide )
131
- const { lastFocused , lastFocusedSelector } = useReturnFocus ( isOpen )
128
+ const { lastFocusedSelector } = useReturnFocusSelector ( isOpen )
132
129
133
130
const hasHeader = ref ( false )
134
131
const hasBody = ref ( false )
@@ -142,23 +139,42 @@ export function useModal(options: UseModalOptions) {
142
139
delayInitialFocus : true ,
143
140
initialFocus : initialFocusRef ?. value as FocusTarget ,
144
141
onDeactivate ( ) {
145
- console . log ( 'lastFocused' , lastFocused . value )
142
+ /**
143
+ * There appears to be a bug in which
144
+ * the DOM refreshes and elements are modified
145
+ * in a way that displaces the DOM.
146
+ *
147
+ * At the time of writing this composable, I am
148
+ * unable to ascertain where it came from. However,
149
+ * this acts a failsafe to allow the `useFocusLock()`
150
+ * hook to always track the last focused element
151
+ * before it was activated.
152
+ */
146
153
setTimeout ( ( ) => {
147
- console . log ( 'Getting last focused' , lastFocusedSelector . value )
148
154
const lastfocusedNode = document . querySelector (
149
155
lastFocusedSelector . value as string
150
156
)
151
157
152
- focus ( lastfocusedNode as HTMLElement )
158
+ if ( finalFocusElement . value ) {
159
+ focus ( finalFocusElement . value )
160
+ } else {
161
+ focus ( lastfocusedNode as HTMLElement )
162
+ }
153
163
} , 100 )
154
- // if (finalFocusElement.value) {
155
- // focus(finalFocusElement.value)
156
- // }
157
164
} ,
158
165
immediate : true ,
159
166
} )
167
+
160
168
const { scrollLockRef } = useBodyScrollLock ( isOpen )
161
169
170
+ /**
171
+ * This watcher is being used to track
172
+ * the element refs for the dialog container
173
+ * element.
174
+ *
175
+ * When the ref is bound, we activate
176
+ * the focus lock and body scroll lock refs.
177
+ */
162
178
watch ( dialogRefEl , ( newVal ) => {
163
179
if ( newVal ) {
164
180
lock ( newVal )
@@ -264,31 +280,31 @@ export function useAriaHidden(
264
280
) {
265
281
let undo : Undo | null = null
266
282
267
- watchEffect (
268
- ( onInvalidate ) => {
269
- if ( ! node . value ) return
283
+ watchEffect ( ( onInvalidate ) => {
284
+ if ( ! node . value ) return
270
285
271
- if ( shouldHide . value && node . value ) {
272
- undo = hideOthers ( node . value )
273
- }
286
+ console . log ( {
287
+ hasNode : ! ! node . value ,
288
+ shouldHide : shouldHide . value ,
289
+ } )
274
290
275
- onInvalidate ( ( ) => {
276
- undo ?.( )
277
- } )
278
- } ,
279
- {
280
- flush : 'post' ,
291
+ if ( shouldHide . value && node . value ) {
292
+ undo = hideOthers ( node . value )
281
293
}
282
- )
294
+
295
+ onInvalidate ( ( ) => {
296
+ undo ?.( )
297
+ } )
298
+ } )
283
299
}
284
300
285
- /** Tracks last opened element before Modal is opened */
286
- export function useReturnFocus ( isOpen : Ref < boolean > ) {
301
+ /** Tracks last focused element selector before Modal/dialog is opened */
302
+ export function useReturnFocusSelector ( shouldTrack : Ref < boolean > ) {
287
303
const lastFocused = ref < EventTarget | null > ( null )
288
304
const lastFocusedSelector = ref < string | undefined > ( )
289
305
290
306
const trackFocus = ( event : Event ) => {
291
- if ( ! isOpen . value ) {
307
+ if ( ! shouldTrack . value ) {
292
308
lastFocusedSelector . value = getSelector ( event . target as HTMLElement )
293
309
}
294
310
}
@@ -308,55 +324,3 @@ export function useReturnFocus(isOpen: Ref<boolean>) {
308
324
lastFocusedSelector,
309
325
}
310
326
}
311
-
312
- function getSelector ( node : HTMLElement ) {
313
- var id = node . getAttribute ( 'id' )
314
-
315
- if ( id ) {
316
- return '#' + id
317
- }
318
-
319
- var path = ''
320
-
321
- while ( node ) {
322
- var name = node . localName
323
- var parent = node . parentNode
324
-
325
- if ( ! parent ) {
326
- path = name + ' > ' + path
327
- continue
328
- }
329
-
330
- if ( node . getAttribute ( 'id' ) ) {
331
- path = '#' + node . getAttribute ( 'id' ) + ' > ' + path
332
- break
333
- }
334
-
335
- var sameTagSiblings = [ ]
336
- var children = parent . childNodes
337
- children = Array . prototype . slice . call ( children )
338
-
339
- children . forEach ( function ( child ) {
340
- if ( child . localName == name ) {
341
- sameTagSiblings . push ( child )
342
- }
343
- } )
344
-
345
- // if there are more than one children of that type use nth-of-type
346
-
347
- if ( sameTagSiblings . length > 1 ) {
348
- var index = sameTagSiblings . indexOf ( node )
349
- name += ':nth-of-type(' + ( index + 1 ) + ')'
350
- }
351
-
352
- if ( path ) {
353
- path = name + ' > ' + path
354
- } else {
355
- path = name
356
- }
357
-
358
- node = parent
359
- }
360
-
361
- return path
362
- }
0 commit comments