1- import { Component , Context } from "react" ;
2- import { findDOMNode } from "react-dom" ;
1+ import { useCallback , useEffect , useRef } from "react" ;
32import Box from "ui-box" ;
43import h from "@macrostrat/hyper" ;
5-
6- import { ColumnContext , ColumnCtx , ColumnDivision } from "../context" ;
4+ import { useColumn } from "../context" ;
75
86interface ColumnScrollerProps {
97 scrollToHeight : number ;
@@ -19,101 +17,74 @@ interface ScrollToOpts {
1917 alignment ?: "center" | "top" | "bottom" ;
2018}
2119
22- const splitProps = function ( keys , props ) {
23- const obj = { } ;
24- const rest = { } ;
25- for ( let k in props ) {
26- const v = props [ k ] ;
27- if ( keys . includes ( k ) ) {
28- obj [ k ] = v ;
29- } else {
30- rest [ k ] = v ;
31- }
32- }
33- return [ obj , rest ] ;
34- } ;
35-
36- export class ColumnScroller extends Component < ColumnScrollerProps > {
37- constructor ( props ) {
38- super ( props ) ;
39- this . scrollTo = this . scrollTo . bind ( this ) ;
40- }
41-
42- private static defaultProps : Partial < ColumnScrollerProps > = {
43- animated : true ,
44- alignment : "center" ,
45- onScrolled ( height ) {
46- return console . log ( `Scrolled to ${ height } m` ) ;
47- } ,
48- scrollContainer ( ) {
49- return document . querySelector ( ".panel-container" ) ;
50- } ,
51- } ;
20+ export function ColumnScroller ( props : ColumnScrollerProps ) {
21+ const {
22+ onScrolled = defaultOnScrolled ,
23+ scrollContainer = defaultGetScrollContainer ,
24+ scrollToHeight,
25+ paddingTop,
26+ animated,
27+ alignment,
28+ ...rest
29+ } = props ;
5230
53- static contextType : Context < ColumnCtx < ColumnDivision > > = ColumnContext ;
31+ const ref = useRef ( null ) ;
32+ const ctx = useColumn ( ) ;
33+ const columnScale = ctx ?. scale ;
5434
55- declare context : ColumnCtx < ColumnDivision > ;
35+ const scrollTo = useCallback (
36+ ( height : number , opts : ScrollToOpts ) => {
37+ let node = ref . current ;
38+ if ( node == null || columnScale == null ) return ;
39+ let { animated, alignment } = opts ;
40+ if ( animated == null ) {
41+ animated = false ;
42+ }
43+ const pixelOffset = columnScale ( height ) ;
44+ const { top } = node . getBoundingClientRect ( ) ;
5645
57- render ( ) {
58- const keys = [
59- "scrollToHeight" ,
60- "alignment" ,
61- "animated" ,
62- "onScrolled" ,
63- "paddingTop" ,
64- "scrollContainer" ,
65- ] ;
66- const [ props , rest ] = splitProps ( keys , this . props ) ;
67- const { pixelHeight } = this . context ;
68- return h ( Box , {
69- height : pixelHeight ,
70- position : "absolute" ,
71- ...rest ,
72- } ) ;
73- }
74-
75- scrollTo ( height , opts : ScrollToOpts = { } ) {
76- let node = findDOMNode ( this ) as HTMLElement ;
77- let { animated, alignment, ...rest } = opts ;
78- if ( animated == null ) {
79- animated = false ;
80- }
81- const { paddingTop } = this . props ;
82- const { scale } = this . context ;
83- const pixelOffset = scale ( height ) ;
84- const { top } = node . getBoundingClientRect ( ) ;
85-
86- node = this . props . scrollContainer ( ) ;
87- let pos = pixelOffset + top + paddingTop ;
88- const screenHeight = window . innerHeight ;
89-
90- if ( this . props . alignment === "center" ) {
91- pos -= screenHeight / 2 ;
92- } else if ( this . props . alignment === "bottom" ) {
93- pos -= screenHeight ;
94- }
46+ node = scrollContainer ( ) ;
47+ let pos = pixelOffset + top + paddingTop ;
48+ const screenHeight = window . innerHeight ;
9549
96- return ( node . scrollTop = pos ) ;
97- }
50+ if ( alignment === "center" ) {
51+ pos -= screenHeight / 2 ;
52+ } else if ( alignment === "bottom" ) {
53+ pos -= screenHeight ;
54+ }
55+ if ( animated && "scrollBehavior" in document . documentElement . style ) {
56+ node . scrollTo ( { top : pos , behavior : "smooth" } ) ;
57+ } else {
58+ node . scrollTop = pos ;
59+ }
60+ } ,
61+ [ onScrolled , scrollContainer , ref . current , columnScale , paddingTop ] ,
62+ ) ;
9863
99- componentDidMount ( ) {
100- const { scrollToHeight, alignment } = this . props ;
64+ useEffect ( ( ) => {
65+ const { scrollToHeight, alignment } = props ;
10166 if ( scrollToHeight == null ) {
10267 return ;
10368 }
104- this . scrollTo ( scrollToHeight , { alignment, animated : false } ) ;
105- return this . props . onScrolled ( scrollToHeight ) ;
106- }
69+ // Actually perform the scroll
70+ scrollTo ( scrollToHeight , { alignment, animated } ) ;
71+ return onScrolled ( scrollToHeight ) ;
72+ } , [ scrollTo , scrollToHeight ] ) ;
10773
108- componentDidUpdate ( prevProps ) {
109- const { scrollToHeight, animated, alignment } = this . props ;
110- if ( scrollToHeight == null ) {
111- return ;
112- }
113- if ( prevProps . scrollToHeight === scrollToHeight ) {
114- return ;
115- }
116- this . scrollTo ( scrollToHeight , { alignment, animated } ) ;
117- return this . props . onScrolled ( scrollToHeight ) ;
118- }
74+ const { pixelHeight } = this . context ;
75+ return h ( Box , {
76+ height : pixelHeight ,
77+ position : "absolute" ,
78+ ref,
79+ ...rest ,
80+ } ) ;
81+ }
82+
83+ function defaultOnScrolled ( height : number ) {
84+ console . log ( `Scrolled to ${ height } m` ) ;
85+ }
86+
87+ function defaultGetScrollContainer ( ) {
88+ // Todo: generalize this
89+ return document . querySelector ( ".panel-container" ) ;
11990}
0 commit comments