@@ -5,13 +5,14 @@ import { FAST_SPRING } from "./motion"
55
66export interface ScrollViewProps extends ComponentProps < "div" > {
77 viewportRef ?: ( el : HTMLDivElement ) => void
8+ reverse ?: boolean
89}
910
1011export function ScrollView ( props : ScrollViewProps ) {
1112 const i18n = useI18n ( )
1213 const [ local , events , rest ] = splitProps (
1314 props ,
14- [ "class" , "children" , "viewportRef" , "style" ] ,
15+ [ "class" , "children" , "viewportRef" , "style" , "reverse" ] ,
1516 [
1617 "onScroll" ,
1718 "onWheel" ,
@@ -36,6 +37,8 @@ export function ScrollView(props: ScrollViewProps) {
3637 const [ thumbTop , setThumbTop ] = createSignal ( 0 )
3738 const [ showThumb , setShowThumb ] = createSignal ( false )
3839
40+ const reverse = ( ) => local . reverse === true
41+
3942 const updateThumb = ( ) => {
4043 if ( ! viewportRef ) return
4144 const { scrollTop, scrollHeight, clientHeight } = viewportRef
@@ -57,10 +60,11 @@ export function ScrollView(props: ScrollViewProps) {
5760 const maxScrollTop = scrollHeight - clientHeight
5861 const maxThumbTop = trackHeight - height
5962
60- // With column-reverse: scrollTop=0 is at bottom, negative = scrolled up
61- // Normalize so 0 = at top, maxScrollTop = at bottom
62- const normalizedScrollTop = maxScrollTop + scrollTop
63- const top = maxScrollTop > 0 ? ( normalizedScrollTop / maxScrollTop ) * maxThumbTop : 0
63+ const top = ( ( ) => {
64+ if ( maxScrollTop <= 0 ) return 0
65+ if ( ! reverse ( ) ) return ( scrollTop / maxScrollTop ) * maxThumbTop
66+ return ( ( maxScrollTop + scrollTop ) / maxScrollTop ) * maxThumbTop
67+ } ) ( )
6468
6569 // Ensure thumb stays within bounds
6670 const boundedTop = trackPadding + Math . max ( 0 , Math . min ( top , maxThumbTop ) )
@@ -135,7 +139,8 @@ export function ScrollView(props: ScrollViewProps) {
135139
136140 const limit = ( top : number ) => {
137141 const max = viewportRef . scrollHeight - viewportRef . clientHeight
138- return Math . max ( - max , Math . min ( 0 , top ) )
142+ if ( reverse ( ) ) return Math . max ( - max , Math . min ( 0 , top ) )
143+ return Math . max ( 0 , Math . min ( max , top ) )
139144 }
140145
141146 const glide = ( top : number ) => {
@@ -175,13 +180,11 @@ export function ScrollView(props: ScrollViewProps) {
175180 break
176181 case "Home" :
177182 e . preventDefault ( )
178- // With column-reverse, top of content = -(scrollHeight - clientHeight)
179- glide ( - ( viewportRef . scrollHeight - viewportRef . clientHeight ) )
183+ glide ( reverse ( ) ? - ( viewportRef . scrollHeight - viewportRef . clientHeight ) : 0 )
180184 break
181185 case "End" :
182186 e . preventDefault ( )
183- // With column-reverse, bottom of content = 0
184- glide ( 0 )
187+ glide ( reverse ( ) ? 0 : viewportRef . scrollHeight - viewportRef . clientHeight )
185188 break
186189 case "ArrowUp" :
187190 e . preventDefault ( )
@@ -206,6 +209,7 @@ export function ScrollView(props: ScrollViewProps) {
206209 < div
207210 ref = { viewportRef }
208211 class = "scroll-view__viewport"
212+ data-reverse = { reverse ( ) ? "true" : undefined }
209213 onScroll = { ( e ) => {
210214 updateThumb ( )
211215 if ( typeof events . onScroll === "function" ) events . onScroll ( e as any )
0 commit comments