1
1
import { makeStyles , mergeClasses , Portal , tokens } from '@fluentui/react-components' ;
2
- import { isFunction , isNotEmptyArray , isNotEmptyString } from '@kwiz/common' ;
3
- import React from 'react' ;
2
+ import { getScrollParent , isFunction , isNotEmptyArray , isNotEmptyString , isNullOrUndefined } from '@kwiz/common' ;
3
+ import React , { useEffect , useState } from 'react' ;
4
+ import { useElementSize , useRefWithState } from '../helpers' ;
4
5
import { useKWIZFluentContext } from '../helpers/context-internal' ;
5
6
import { KnownClassNames , mixins , useCommonStyles } from '../styles/styles' ;
6
7
@@ -17,6 +18,14 @@ const useStyles = makeStyles({
17
18
float : "right" ,
18
19
marginLeft : tokens . spacingHorizontalXXL
19
20
} ,
21
+ sticky : {
22
+ ...mixins . box ,
23
+ position : "sticky" ,
24
+ top : 0 ,
25
+ overflow : "auto" ,
26
+ height : "fit-content" ,
27
+ maxHeight : "fit-content"
28
+ } ,
20
29
selfCentered : {
21
30
alignSelf : "center"
22
31
}
@@ -32,6 +41,8 @@ export interface ISectionProps {
32
41
title ?: string ;
33
42
left ?: boolean ;
34
43
right ?: boolean ;
44
+ /** set height to match scroll parent's height, and stick to top */
45
+ sticky ?: boolean ;
35
46
/** true - will add css position fixed. portal will also wrap it in a portal. */
36
47
fullscreen ?: boolean | "portal" ;
37
48
centerSelf ?: boolean ;
@@ -55,11 +66,59 @@ export const Section = React.forwardRef<HTMLDivElement, React.PropsWithChildren<
55
66
css . push ( cssNames . right ) ;
56
67
css . push ( KnownClassNames . right ) ;
57
68
}
69
+ else if ( props . sticky ) {
70
+ css . push ( cssNames . sticky ) ;
71
+ }
72
+
73
+ /** need scrollparent if we are sticky */
74
+ const [ scrollParent , setScrollParent ] = useState < HTMLElement > ( null ) ;
75
+ const divRef = useRefWithState < HTMLDivElement > ( ) ;
76
+
77
+ //wait for my content to finish loading, it might change scrollparent
78
+ const mySize = useElementSize ( divRef . ref . current ) ;
79
+
80
+ useEffect ( ( ) => {
81
+ //setting the forwardRef
82
+ if ( ! isNullOrUndefined ( ref ) ) {
83
+ if ( isFunction ( ref ) ) ref ( divRef . ref . current ) ;
84
+ else ( ref as React . MutableRefObject < HTMLDivElement > ) . current = divRef . ref . current ;
85
+ }
86
+ } , [ divRef . value ] ) ;
87
+
88
+ useEffect ( ( ) => {
89
+ if ( props . sticky ) {
90
+ let scrollParent = getScrollParent ( divRef . ref . current ? divRef . ref . current . parentElement : null ) ;
91
+ setScrollParent ( scrollParent ) ;
92
+ }
93
+ } , [ divRef . value , mySize . height ] ) ;
94
+
95
+ const parentSize = useElementSize ( scrollParent ) ;
96
+ useEffect ( ( ) => {
97
+ if ( props . sticky && divRef . ref . current ) {
98
+ let maxHeight = "fit-content" ;
99
+ if ( scrollParent ) {
100
+ let height = parseFloat ( getComputedStyle ( scrollParent ) . height ) ;
101
+ let myStyle = getComputedStyle ( divRef . ref . current ) ;
102
+
103
+ let pTop = parseFloat ( myStyle . paddingTop ) ;
104
+ let pBottom = parseFloat ( myStyle . paddingBottom ) ;
105
+ let mTop = parseFloat ( myStyle . marginTop ) ;
106
+ let mBottom = parseFloat ( myStyle . marginBottom ) ;
107
+ if ( pTop > 0 ) height -= pTop ;
108
+ if ( pBottom > 0 ) height -= pBottom ;
109
+ if ( mTop > 0 ) height -= mTop ;
110
+ if ( mBottom > 0 ) height -= mBottom ;
111
+
112
+ maxHeight = `${ height } px` ;
113
+ }
114
+ divRef . ref . current . style . maxHeight = maxHeight ;
115
+ }
116
+ } , [ props . sticky , parentSize . height , divRef . value ] ) ;
58
117
59
118
//a css class might have space and multiuple classes in it
60
119
if ( isNotEmptyArray ( props . css ) ) props . css . filter ( c => isNotEmptyString ( c ) ) . forEach ( c => css . push ( ...c . split ( " " ) ) ) ;
61
120
if ( props . fullscreen ) css . push ( commonStyles . fullscreen ) ;
62
- const control = < div ref = { ref } { ...( props . rootProps || { } ) } title = { props . title } style = { props . style }
121
+ const control = < div ref = { divRef . set } { ...( props . rootProps || { } ) } title = { props . title } style = { props . style }
63
122
className = { mergeClasses ( ...css ) }
64
123
onClick = { props . onClick } >
65
124
{ props . children }
0 commit comments