5
5
6
6
import { queryOptions , useSuspenseQuery } from "@tanstack/react-query" ;
7
7
import { Navigate , createFileRoute , redirect } from "@tanstack/react-router" ;
8
- import { type Ref , useCallback } from "react" ;
8
+ import { useEffect , useMemo , useRef } from "react" ;
9
9
import { preload } from "react-dom" ;
10
10
import { graphql } from "../gql" ;
11
11
import { graphqlRequest } from "../graphql" ;
@@ -46,9 +46,15 @@ function Plan(): React.ReactElement {
46
46
return < Navigate to = "/" replace /> ;
47
47
}
48
48
49
+ const ref = useRef < HTMLIFrameElement > ( null ) ;
50
+
49
51
// Query the size of the iframe content and set the height
50
52
// This will only work where the iframe is served from the same origin
51
- const calculateHeight = useCallback ( ( iframe : HTMLIFrameElement ) => {
53
+ const calculateHeight = ( ) => {
54
+ const iframe = ref . current ;
55
+ if ( ! iframe ) {
56
+ return ;
57
+ }
52
58
const height =
53
59
iframe . contentWindow ?. document . body . parentElement ?. scrollHeight ;
54
60
@@ -57,37 +63,52 @@ function Plan(): React.ReactElement {
57
63
} else {
58
64
iframe . height = "500px" ;
59
65
}
60
- } , [ ] ) ;
61
-
62
- const ref : Ref < HTMLIFrameElement > = useCallback (
63
- ( iframe : HTMLIFrameElement | null ) => {
64
- if ( ! iframe ) return ;
65
- calculateHeight ( iframe ) ;
66
-
67
- if ( iframe . contentWindow ) {
68
- const iframeDocument = iframe . contentWindow . document ;
66
+ } ;
69
67
70
- const observer = new MutationObserver ( ( _mutationsList ) => {
71
- calculateHeight ( iframe ) ;
72
- } ) ;
68
+ const observer = useMemo (
69
+ ( ) =>
70
+ new MutationObserver ( ( _mutationsList ) => {
71
+ // we calculate the height immediately when the observer is triggered
72
+ calculateHeight ( ) ;
73
+ // then we recalculate the height after a short timeout
74
+ // to ensure that any layout changes have settled
75
+ setTimeout ( ( ) => {
76
+ calculateHeight ( ) ;
77
+ } , 1000 ) ;
78
+ // n.b. we don't worry about the timeout happening after the component is unmounted
79
+ } ) ,
80
+ [ ] ,
81
+ ) ;
73
82
74
- observer . observe ( iframeDocument . body , {
75
- childList : true ,
76
- subtree : true ,
77
- attributes : true ,
78
- } ) ;
83
+ useEffect ( ( ) => {
84
+ const iframe = ref . current ;
85
+ if ( iframe ) {
86
+ attachObserver ( iframe ) ;
87
+ }
88
+ // Cleanup observer when the component unmounts
89
+ return ( ) => observer . disconnect ( ) ;
90
+ } , [ ] ) ;
79
91
80
- return ( ) => observer . disconnect ( ) ;
81
- }
82
- } ,
83
- [ calculateHeight ] ,
84
- ) ;
92
+ const attachObserver = ( iframe : HTMLIFrameElement ) => {
93
+ const iframeBody = iframe . contentWindow ?. document . body ;
94
+ if ( ! iframeBody ) {
95
+ return ;
96
+ }
97
+ // calculate the height immediately
98
+ calculateHeight ( ) ;
99
+ // observe future changes to the body of the iframe
100
+ observer . observe ( iframeBody , {
101
+ childList : true ,
102
+ subtree : true ,
103
+ attributes : true ,
104
+ } ) ;
105
+ } ;
85
106
86
107
return (
87
108
< iframe
88
109
title = "iframe" // no proper title as this is experimental feature
89
110
ref = { ref }
90
- onLoad = { ( e ) => calculateHeight ( e . target as HTMLIFrameElement ) }
111
+ onLoad = { ( e ) => attachObserver ( e . target as HTMLIFrameElement ) }
91
112
src = { planManagementIframeUri }
92
113
scrolling = "no"
93
114
/>
0 commit comments