@@ -6,10 +6,8 @@ This supports virtualization, window splitting, etc., without loss of state.
6
6
7
7
import { useCallback , useEffect , useRef } from "react" ;
8
8
import $ from "jquery" ;
9
-
10
- // This is just an initial default height; the actual height of the should
11
- // resize to the content.
12
- const HEIGHT = "50vh" ;
9
+ import { useFrameContext } from "@cocalc/frontend/frame-editors/frame-tree/frame-context" ;
10
+ import { useIFrameContext } from "@cocalc/frontend/jupyter/cell-list" ;
13
11
14
12
interface Props {
15
13
globalKey : string ;
@@ -21,69 +19,140 @@ const immortals: { [globalKey: string]: any } = {};
21
19
22
20
const Z_INDEX = 1 ;
23
21
22
+ // make it really standout:
23
+ // const PADDING = 5;
24
+ // const STYLE = {
25
+ // border: "1px solid #ccc",
26
+ // borderRadius: "5px",
27
+ // padding: `${PADDING}px`,
28
+ // background: "#eee",
29
+ // } as const;
30
+
31
+ // make it blend in
32
+ const PADDING = 0 ;
33
+ const STYLE = { } as const ;
34
+
24
35
export default function ImmortalDomNode ( {
25
36
globalKey,
26
37
html,
27
38
zIndex = Z_INDEX , // todo: support changing?
28
39
} : Props ) {
29
40
const divRef = useRef < any > ( null ) ;
30
- const eltRef = useRef < any > ( null ) ;
31
41
const intervalRef = useRef < any > ( null ) ;
42
+ const { isVisible } = useFrameContext ( ) ;
43
+ const iframeContext = useIFrameContext ( ) ;
32
44
33
45
const position = useCallback ( ( ) => {
34
- // make it so eltRef.current is exactly positioned on top of divRef.current using CSS
35
- if ( eltRef . current == null || divRef . current == null ) {
46
+ // make it so elt is exactly positioned on top of divRef.current using CSS
47
+ if ( divRef . current == null ) {
36
48
return ;
37
49
}
38
- const eltRect = eltRef . current . getBoundingClientRect ( ) ;
50
+ const elt = getElt ( ) [ 0 ] ;
51
+ const eltRect = elt . getBoundingClientRect ( ) ;
39
52
const divRect = divRef . current . getBoundingClientRect ( ) ;
53
+
54
+ // position our immortal html element
40
55
let deltaTop = divRect . top - eltRect . top ;
41
56
if ( deltaTop ) {
42
- if ( eltRef . current . style . top ) {
43
- deltaTop += parseFloat ( eltRef . current . style . top . slice ( 0 , - 2 ) ) ;
57
+ if ( elt . style . top ) {
58
+ deltaTop += parseFloat ( elt . style . top . slice ( 0 , - 2 ) ) ;
44
59
}
45
- eltRef . current . style . top = `${ deltaTop } px` ;
60
+ elt . style . top = `${ deltaTop + PADDING } px` ;
46
61
}
47
62
let deltaLeft = divRect . left - eltRect . left ;
48
63
if ( deltaLeft ) {
49
- if ( eltRef . current . style . left ) {
50
- deltaLeft += parseFloat ( eltRef . current . style . left . slice ( 0 , - 2 ) ) ;
64
+ if ( elt . style . left ) {
65
+ deltaLeft += parseFloat ( elt . style . left . slice ( 0 , - 2 ) ) ;
51
66
}
52
- eltRef . current . style . left = `${ deltaLeft } px` ;
67
+ elt . style . left = `${ deltaLeft + PADDING } px` ;
53
68
}
54
- } , [ ] ) ;
55
69
56
- useEffect ( ( ) => {
57
- if ( divRef . current == null ) {
58
- return ;
70
+ // set the size of the actual react div that is in place
71
+ divRef . current . style . height = `${
72
+ eltRect . bottom - eltRect . top + 2 * PADDING
73
+ } px`;
74
+ divRef . current . style . width = `${
75
+ eltRect . right - eltRect . left + 2 * PADDING
76
+ } px`;
77
+
78
+ // clip our immortal html so it isn't visible outside the parent
79
+ const parent = $ ( iframeContext . cellListDivRef ?. current ) [ 0 ] ;
80
+ if ( parent != null ) {
81
+ const parentRect = parent . getBoundingClientRect ( ) ;
82
+ console . log ( { parentRect, eltRect } ) ;
83
+ // Calculate the overlap area
84
+ const top = Math . max ( 0 , parentRect . top - eltRect . top ) ;
85
+ const right = Math . min ( eltRect . width , parentRect . right - eltRect . left ) ;
86
+ const bottom = Math . min ( eltRect . height , parentRect . bottom - eltRect . top ) ;
87
+ const left = Math . max ( 0 , parentRect . left - eltRect . left ) ;
88
+
89
+ // Apply clip-path to elt to make it visible only inside of parentRect:
90
+ elt . style . clipPath = `polygon(${ left } px ${ top } px, ${ right } px ${ top } px, ${ right } px ${ bottom } px, ${ left } px ${ bottom } px)` ;
59
91
}
60
- let elt ;
92
+ } , [ ] ) ;
93
+
94
+ const getElt = ( ) => {
61
95
if ( immortals [ globalKey ] == null ) {
62
- elt = immortals [ globalKey ] = $ (
63
- `<div id="${ globalKey } " style="border:0;overflow:hidden;width:100%;height: ${ HEIGHT } ; position:absolute;left:130px ;z-index:${ zIndex } "/>${ html } </div>` ,
64
- ) ;
96
+ const elt = ( immortals [ globalKey ] = $ (
97
+ `<div id="${ globalKey } " style="border:0;position:absolute;z-index:${ zIndex } "/>${ html } </div>` ,
98
+ ) ) ;
65
99
$ ( "body" ) . append ( elt ) ;
100
+ return elt ;
66
101
} else {
67
- elt = immortals [ globalKey ] ;
68
- elt . show ( ) ;
102
+ return immortals [ globalKey ] ;
69
103
}
70
- eltRef . current = elt [ 0 ] ;
71
- intervalRef . current = setInterval ( position , 1000 ) ;
104
+ } ;
105
+
106
+ const show = ( ) => {
107
+ if ( divRef . current == null ) {
108
+ return ;
109
+ }
110
+ const elt = getElt ( ) ;
111
+ elt . show ( ) ;
72
112
position ( ) ;
113
+ } ;
114
+
115
+ const hide = ( ) => {
116
+ // unmounting so hide
117
+ const elt = getElt ( ) ;
118
+ elt . hide ( ) ;
119
+ } ;
120
+
121
+ useEffect ( ( ) => {
122
+ if ( isVisible ) {
123
+ show ( ) ;
124
+ return hide ;
125
+ }
126
+ } , [ isVisible ] ) ;
127
+
128
+ useEffect ( ( ) => {
129
+ intervalRef . current = setInterval ( position , 1000 ) ;
130
+
131
+ if ( iframeContext . iframeOnScrolls != null ) {
132
+ let count = 0 ;
133
+ iframeContext . iframeOnScrolls [ globalKey ] = async ( ) => {
134
+ // We run position a lot whenever there is a scroll
135
+ // in order to make it so the iframe doesn't appear
136
+ // to just get "dragged along" nearly as much, as
137
+ // onScroll is throttled.
138
+ count = Math . min ( 100 , count + 100 ) ;
139
+ while ( count > 0 ) {
140
+ position ( ) ;
141
+ await new Promise ( requestAnimationFrame ) ;
142
+ count -= 1 ;
143
+ }
144
+ // throw in an update when we're done.
145
+ position ( ) ;
146
+ } ;
147
+ }
73
148
74
149
return ( ) => {
75
- // unmounting so hide
76
- elt . hide ( ) ;
150
+ delete iframeContext . iframeOnScrolls ?. [ globalKey ] ;
77
151
if ( intervalRef . current ) {
78
152
clearInterval ( intervalRef . current ) ;
79
153
}
80
154
} ;
81
155
} , [ ] ) ;
82
156
83
- return (
84
- < div
85
- ref = { divRef }
86
- style = { { border : "1px solid black" , height : HEIGHT } }
87
- > </ div >
88
- ) ;
157
+ return < div ref = { divRef } style = { STYLE } > </ div > ;
89
158
}
0 commit comments