8
8
} from "vue" ;
9
9
import { useSlideContext , useIsSlideActive } from "@slidev/client" ;
10
10
import { AbsolutePosition } from "./use-rough-arrow" ;
11
+ import { SnapTarget } from "./parse-option" ;
11
12
12
13
export type SnapPosition =
13
14
| "top"
@@ -19,43 +20,75 @@ export type SnapPosition =
19
20
| "bottomleft"
20
21
| "bottomright" ;
21
22
22
- export function useElementPosition (
23
- slideContainer : Ref < Element | undefined > ,
24
- rootElement : Ref < SVGSVGElement | undefined > ,
25
- selector : string ,
26
- pos ?: SnapPosition ,
23
+ export function useEndpointResolution (
24
+ slideContainerRef : Ref < Element | undefined > ,
25
+ rootElementRef : Ref < SVGSVGElement | undefined > ,
26
+ endpointRef : Ref < AbsolutePosition | SnapTarget | undefined > ,
27
27
) : Ref < AbsolutePosition | undefined > {
28
28
const { $scale } = useSlideContext ( ) ;
29
29
const isSlideActive = useIsSlideActive ( ) ;
30
30
31
- const elem = computed ( ( ) => {
32
- return slideContainer . value ?. querySelector ( selector ) ?? null ;
31
+ const snappedElementInfo = computed ( ( ) => {
32
+ const endpoint = endpointRef . value ;
33
+ if ( endpoint == null || ! ( "query" in endpoint ) ) {
34
+ return undefined ;
35
+ }
36
+ const element =
37
+ slideContainerRef . value ?. querySelector ( endpoint . query ) ?? null ;
38
+ return {
39
+ element,
40
+ snapPosition : endpoint . snapPosition ,
41
+ } ;
33
42
} ) ;
34
43
35
44
const point = ref < AbsolutePosition | undefined > ( undefined ) ;
36
45
37
- const update = ( ) => {
38
- if ( ! isSlideActive . value || ! rootElement . value || ! elem . value ) {
46
+ // Sync endpointRef -> point in case where endpoint is AbsolutePosition
47
+ watch (
48
+ endpointRef ,
49
+ ( endpoint ) => {
50
+ if ( endpoint == null ) {
51
+ point . value = undefined ;
52
+ return ;
53
+ } else if ( "x" in endpoint ) {
54
+ point . value = { x : endpoint . x , y : endpoint . y } ;
55
+ return ;
56
+ }
57
+ } ,
58
+ { immediate : true } ,
59
+ ) ;
60
+
61
+ // Sync snappedElementInfo -> point in case where endpoint is SnapTarget
62
+ const updateSnappedPosition = ( ) => {
63
+ if ( ! snappedElementInfo . value ) {
64
+ // This case means endpoint is AbsolutePosition
65
+ // so we don't need to update point in this method
66
+ // as it's done in the watch above.
67
+ return ;
68
+ }
69
+
70
+ const { element, snapPosition } = snappedElementInfo . value ;
71
+ if ( ! isSlideActive . value || ! rootElementRef . value || ! element ) {
39
72
point . value = undefined ;
40
73
return ;
41
74
}
42
75
43
- const rect = elem . value . getBoundingClientRect ( ) ;
44
- const rootRect = rootElement . value . getBoundingClientRect ( ) ;
76
+ const rect = element . getBoundingClientRect ( ) ;
77
+ const rootRect = rootElementRef . value . getBoundingClientRect ( ) ;
45
78
46
79
let x = ( rect . left - rootRect . left ) / $scale . value ;
47
80
let y = ( rect . top - rootRect . top ) / $scale . value ;
48
81
const width = rect . width / $scale . value ;
49
82
const height = rect . height / $scale . value ;
50
83
51
- if ( pos ?. includes ( "right" ) ) {
84
+ if ( snapPosition ?. includes ( "right" ) ) {
52
85
x += width ;
53
- } else if ( ! pos ?. includes ( "left" ) ) {
86
+ } else if ( ! snapPosition ?. includes ( "left" ) ) {
54
87
x += width / 2 ;
55
88
}
56
- if ( pos ?. includes ( "bottom" ) ) {
89
+ if ( snapPosition ?. includes ( "bottom" ) ) {
57
90
y += height ;
58
- } else if ( ! pos ?. includes ( "top" ) ) {
91
+ } else if ( ! snapPosition ?. includes ( "top" ) ) {
59
92
y += height / 2 ;
60
93
}
61
94
@@ -70,16 +103,16 @@ export function useElementPosition(
70
103
watch ( isSlideActive , ( ) => {
71
104
setTimeout ( ( ) => {
72
105
// This `setTimeout` is important to ensure `update()` is called after the DOM elements in the slide are updated after `isSlideActive` is changed.
73
- update ( ) ;
106
+ updateSnappedPosition ( ) ;
74
107
} ) ;
75
108
} ) ;
76
109
77
110
watch (
78
- elem ,
111
+ snappedElementInfo ,
79
112
( newVal ) => {
80
- if ( newVal ) {
81
- const observer = new MutationObserver ( update ) ;
82
- observer . observe ( newVal , { attributes : true } ) ;
113
+ if ( newVal ?. element ) {
114
+ const observer = new MutationObserver ( updateSnappedPosition ) ;
115
+ observer . observe ( newVal . element , { attributes : true } ) ;
83
116
84
117
onWatcherCleanup ( ( ) => {
85
118
observer . disconnect ( ) ;
@@ -90,12 +123,12 @@ export function useElementPosition(
90
123
) ;
91
124
92
125
onMounted ( ( ) => {
93
- update ( ) ;
126
+ updateSnappedPosition ( ) ;
94
127
95
128
// Some type of position/size changes can't be observed by MutationObserver.
96
129
// So we need to update the position/size periodically in the polling manner.
97
130
const interval = setInterval ( ( ) => {
98
- update ( ) ;
131
+ updateSnappedPosition ( ) ;
99
132
} , 100 ) ;
100
133
101
134
return ( ) => clearInterval ( interval ) ;
0 commit comments