File tree Expand file tree Collapse file tree 3 files changed +85
-0
lines changed Expand file tree Collapse file tree 3 files changed +85
-0
lines changed Original file line number Diff line number Diff line change
1
+ <script lang =" ts" module >
2
+ interface Props {
3
+ class? : string ;
4
+ style? : string ;
5
+ onblur? : () => void ;
6
+ onfocus? : () => void ;
7
+ focused? : boolean ;
8
+ }
9
+
10
+ function isDescendant(parent : HTMLElement , child ? : Node ) {
11
+ let node: Node | null | undefined = child ;
12
+ while (node ) {
13
+ if (node == parent ) {
14
+ return true ;
15
+ }
16
+ node = node .parentNode ;
17
+ }
18
+
19
+ return false ;
20
+ }
21
+ </script >
22
+
23
+ <script lang =" ts" >
24
+ let {
25
+ //
26
+ class : clazz,
27
+ style,
28
+ onblur,
29
+ onfocus,
30
+ focused = $bindable (false )
31
+ }: Props = $props ();
32
+
33
+ let div: HTMLElement ;
34
+
35
+ function focusGained(e : FocusEvent ) {
36
+ if (focused ) return ; // We already HAVE focus.
37
+
38
+ focused = true ;
39
+ onfocus ?.();
40
+ }
41
+
42
+ function focusLost(e : FocusEvent ) {
43
+ if (! focused ) return ; // We've already LOST focus.
44
+
45
+ // @ts-ignore
46
+ const target = e .relatedTarget as Node ;
47
+
48
+ // If the newly focused element is NOT one of ours.
49
+ if (! isDescendant (div , target )) {
50
+ focused = false ;
51
+ onblur ?.();
52
+ }
53
+ }
54
+ </script >
55
+
56
+ <!-- svelte-ignore a11y_click_events_have_key_events -->
57
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
58
+ <div
59
+ {style }
60
+ class ={clazz }
61
+ bind:this ={div }
62
+ onclick ={focusGained }
63
+ onfocusin ={focusGained }
64
+ onfocusout ={focusLost }
65
+ tabindex =" -1"
66
+ >
67
+ <!-- svelte-ignore slot_element_deprecated -->
68
+ <slot />
69
+ </div >
Original file line number Diff line number Diff line change @@ -4,4 +4,5 @@ export { default as Divider } from '$lib/components/Divider.svelte';
4
4
export { default as DynamicList } from '$lib/components/DynamicList.svelte' ;
5
5
export { default as Input } from '$lib/components/Input.svelte' ;
6
6
export { default as InvertedScroller } from '$lib/components/InvertedScroller.svelte' ;
7
+ export { default as FocusTrap } from '$lib/helpers/FocusTrap.svelte' ;
7
8
export { default as LongPressListener } from '$lib/helpers/LongPressListener.svelte' ;
Original file line number Diff line number Diff line change 4
4
Button ,
5
5
Divider ,
6
6
DynamicList ,
7
+ FocusTrap ,
7
8
Input ,
8
9
InvertedScroller ,
9
10
LongPressListener
26
27
let dynamicListBleed = 300 ;
27
28
let dynamicList1: DynamicList ;
28
29
let dynamicList2: DynamicList ;
30
+
31
+ let focusTrapHasFocus: boolean = false ;
29
32
</script >
30
33
31
34
{#snippet itemRenderer (item : number | string )}
@@ -276,6 +279,18 @@ Is at bottom?
276
279
{/ key }
277
280
</div >
278
281
282
+ <h2 >FocusTrap</h2 >
283
+
284
+ Has focus?
285
+ <b >{focusTrapHasFocus ? ' Yes' : ' No' }</b >
286
+
287
+ <FocusTrap bind:focused ={focusTrapHasFocus }>
288
+ <Box sides ={[' top' , ' bottom' , ' left' , ' right' ]}>
289
+ <p >When you click in this box, it gains focus. When you click out, it loses focus.</p >
290
+ <!-- <Input type="text" bind:value={textInputValue} placeholder="Some focusable input" /> -->
291
+ </Box >
292
+ </FocusTrap >
293
+
279
294
<h2 >LongPressListener (for mobile)</h2 >
280
295
281
296
<LongPressListener
You can’t perform that action at this time.
0 commit comments