11import React from "react" ;
22import { SnapConfig } from "src" ;
33import { getClosestIndexAndValue } from "../utils" ;
4+ import { useRect } from "./useRect" ;
45
56export interface SnapPoint {
67 value : number ;
@@ -12,17 +13,20 @@ export type Snap = {
1213 getNearestByCoord : ( coord : number ) => SnapPoint ;
1314 getNearestByIndex : ( index : string | number ) => SnapPoint ;
1415 defaultSnap : SnapPoint ;
15- autoSnap : SnapPoint ;
16+ // autoSnap: SnapPoint;
1617 minSnap : SnapPoint ;
1718 minSnapExceptClose : SnapPoint ;
1819 maxSnap : SnapPoint ;
1920 originalMap : Record < string | number , number > ;
2021 processedMap : Record < string | number , number > ;
22+ isScroll : boolean ;
2123} ;
2224
2325export function useSnapPoint < T extends HTMLElement > ( {
24- containerRect ,
26+ containerRef ,
2527 contentRef,
28+ headerRef,
29+ footerRef,
2630 config : {
2731 snaps = [ ] ,
2832 defaultSnap = "auto" ,
@@ -33,49 +37,66 @@ export function useSnapPoint<T extends HTMLElement>({
3337 onMount,
3438 onChange,
3539} : {
36- containerRect : DOMRect | null ;
40+ containerRef : React . RefObject < T > ;
3741 contentRef : React . RefObject < T > ;
42+ headerRef : React . RefObject < T > ;
43+ footerRef : React . RefObject < T > ;
3844 config : SnapConfig ;
3945 onMount ?: ( snapPoints : NonNullable < ReturnType < typeof useSnapPoint > > ) => void ;
4046 onChange ?: ( snapPoints : NonNullable < ReturnType < typeof useSnapPoint > > ) => void ;
4147} ) : null | Snap {
42- const mount = React . useRef ( false ) ;
48+ const mount = React . useRef < null | Record < string , number > > ( null ) ;
49+ const containerRect = useRect ( containerRef ) ;
50+ useRect ( contentRef ) ;
51+ useRect ( headerRef ) ;
52+ useRect ( footerRef ) ;
4353
4454 const snapPoints = React . useMemo ( ( ) => {
4555 if ( ! containerRect || ! contentRef . current ) return null ;
4656
4757 const containerHeight = containerRect . height ;
48- const scrollHeight = contentRef . current . scrollHeight ;
58+ const contentTotalHeight =
59+ contentRef . current . scrollHeight +
60+ ( headerRef ?. current ?. offsetHeight ?? 0 ) +
61+ ( footerRef ?. current ?. offsetHeight ?? 0 ) ;
4962
50- const originalMap : Record < string | number , number > = { } ;
63+ const originalMap : Record < string , number > = { } ;
64+
65+ // create original map
5166
5267 snaps . forEach ( ( value , key ) => {
5368 if ( value <= 1 ) originalMap [ key ] = - value * containerHeight ;
5469 else originalMap [ key ] = - value ;
5570 } ) ;
56- if ( useCloseSnap || useCloseSnap === 0 ) {
57- if ( useCloseSnap === true ) {
58- originalMap [ "close" ] = 0 ;
59- } else {
60- originalMap [ "close" ] = - useCloseSnap ;
61- }
71+ if ( useCloseSnap === true ) {
72+ originalMap [ "close" ] = 0 ;
6273 }
63- if ( useAutoSnap ) {
64- originalMap [ "auto " ] = - scrollHeight ;
74+ if ( typeof useCloseSnap === "number" ) {
75+ originalMap [ "close " ] = - useCloseSnap ;
6576 }
77+ if ( useAutoSnap === true ) {
78+ originalMap [ "auto" ] = - contentTotalHeight ;
79+ }
80+
81+ // pick processed keys that are in range
6682
6783 let processedKeys = Object . keys ( originalMap ) ;
68- if ( autoSnapAsMax ) {
84+ if ( autoSnapAsMax === true ) {
6985 processedKeys = processedKeys . filter ( ( key ) => {
7086 const value = originalMap [ key ] ;
71- return value >= - scrollHeight ;
87+ return value >= - contentTotalHeight ;
7288 } ) ;
7389 }
7490 processedKeys = processedKeys . filter (
7591 ( key ) => originalMap [ key ] >= - containerHeight ,
7692 ) ;
93+ if ( useCloseSnap !== undefined ) {
94+ processedKeys = processedKeys . filter (
95+ ( key ) => key === "close" || originalMap [ key ] < originalMap [ "close" ] ,
96+ ) ;
97+ }
7798
78- const processedMap = processedKeys . reduce < Record < string | number , number > > (
99+ const processedMap = processedKeys . reduce < Record < string , number > > (
79100 ( prev , curr ) => {
80101 prev [ curr ] = originalMap [ curr ] ;
81102 return prev ;
@@ -93,7 +114,6 @@ export function useSnapPoint<T extends HTMLElement>({
93114 index,
94115 } ;
95116 }
96-
97117 return null ;
98118 }
99119
@@ -128,7 +148,7 @@ export function useSnapPoint<T extends HTMLElement>({
128148 }
129149 } ) ( ) ;
130150
131- const autoSnap : SnapPoint = getNearestByIndex ( "auto" ) ;
151+ // const autoSnap: SnapPoint = getNearestByIndex("auto");
132152 const minSnap : SnapPoint = getNearestByCoord ( Math . max ( ...processedValues ) ) ;
133153 const maxSnap : SnapPoint = getNearestByCoord ( Math . min ( ...processedValues ) ) ;
134154 const minSnapExceptClose : SnapPoint = ( ( ) => {
@@ -140,25 +160,37 @@ export function useSnapPoint<T extends HTMLElement>({
140160 return minSnap ;
141161 } ) ( ) ;
142162
163+ const isScroll =
164+ originalMap [ "auto" ] !== undefined
165+ ? maxSnap . value > originalMap [ "auto" ]
166+ : false ;
167+
143168 const result = {
144169 getByIndex,
145170 getNearestByCoord,
146171 getNearestByIndex,
147172 defaultSnap : _defaultSnap ,
148- autoSnap,
173+ // autoSnap,
149174 minSnap,
150175 minSnapExceptClose,
151176 maxSnap,
152177 originalMap,
153178 processedMap,
179+ isScroll,
154180 } ;
155181
156182 if ( ! mount . current ) {
157- mount . current = true ;
158183 if ( onMount ) onMount ( result ) ;
159184 } else {
160- if ( onChange ) onChange ( result ) ;
185+ if (
186+ onChange &&
187+ Object . entries ( processedMap ) . toString ( ) !==
188+ Object . entries ( mount . current ) . toString ( )
189+ ) {
190+ onChange ( result ) ;
191+ }
161192 }
193+ mount . current = processedMap ;
162194
163195 return result ;
164196 } , [
0 commit comments