-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #4 from stoplightio/SL-151/scroller
SL-151/scroller
- Loading branch information
Showing
17 changed files
with
610 additions
and
129 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
import * as React from 'react'; | ||
// @ts-ignore | ||
import { positionValues, Scrollbars } from 'react-custom-scrollbars'; | ||
|
||
import { Box, IBoxProps } from './Box'; | ||
import { useScrollToHash } from './hooks/useScrollToHash'; | ||
import { getScrollTransform, getThumbDimension, horizontalTrackStyle, verticalTrackStyle } from './utils/scroll'; | ||
|
||
export interface IScrollBoxRef { | ||
scrollTop: (top?: number) => void; | ||
scrollLeft: (left?: number) => void; | ||
|
||
scrollToTop: () => void; | ||
scrollToBottom: () => void; | ||
scrollToLeft: () => void; | ||
scrollToRight: () => void; | ||
|
||
getScrollLeft: () => number; | ||
getScrollTop: () => number; | ||
getScrollWidth: () => number; | ||
getScrollHeight: () => number; | ||
|
||
getClientWidth: () => number; | ||
getClientHeight: () => number; | ||
|
||
getThumbVerticalHeight: () => number; | ||
getThumbHorizontalWidth: () => number; | ||
|
||
getValues: () => any; | ||
} | ||
|
||
export interface IScrollBox extends IBoxProps { | ||
innerRef?: React.RefObject<IScrollBoxRef>; | ||
|
||
autoHeight?: boolean; | ||
autoHideTimeout?: number; | ||
|
||
// can scroll to an anchor/id | ||
scrollTo?: string; | ||
|
||
// if use are using this as a list scroller children should be an array | ||
children?: any; | ||
|
||
onUpdate?: (values: positionValues) => void; | ||
} | ||
|
||
export const ScrollBox = (props: IScrollBox) => { | ||
// pull out scrollTo so they are not in scrollbarProps (don't want them spred onto <Scrollbars /> component) | ||
const { scrollTo, children, onUpdate, autoHeight = true, autoHideTimeout = 500, innerRef, ...scrollbarProps } = props; | ||
|
||
const [isScrolling, setisScrolling] = React.useState(false); | ||
|
||
useScrollToHash(scrollTo); | ||
|
||
const scrollbars = innerRef || React.useRef<IScrollBoxRef>(null); | ||
const current = scrollbars.current as IScrollBoxRef; | ||
const values = (current && current.getValues()) || {}; | ||
const { clientHeight, clientWidth, scrollHeight, scrollLeft, scrollTop, scrollWidth } = values; | ||
|
||
const thumbHorizontal = getThumbDimension({ scroll: scrollWidth, client: clientWidth }) || 0; | ||
const thumbVertical = getThumbDimension({ scroll: scrollHeight, client: clientHeight }) || 0; | ||
|
||
return ( | ||
<Scrollbars | ||
{...scrollbarProps} | ||
ref={innerRef || scrollbars} | ||
autoHideTimeout={autoHideTimeout} | ||
autoHeight={autoHeight} | ||
onUpdate={onUpdate} | ||
onScroll={(e: any) => { | ||
if (isScrolling) { | ||
// @ts-ignore | ||
clearTimeout(isScrolling); | ||
} | ||
|
||
setisScrolling( | ||
// @ts-ignore | ||
setTimeout(() => { | ||
setisScrolling(false); | ||
}, autoHideTimeout) | ||
); | ||
}} | ||
renderView={({ style }: any) => { | ||
// overide to offset the native scroll bars | ||
return ( | ||
<div | ||
style={{ | ||
...style, | ||
marginRight: '-15px', | ||
marginBottom: '-15px', | ||
}} | ||
/> | ||
); | ||
}} | ||
// Custom component overrides | ||
renderTrackHorizontal={({ style }: any) => { | ||
return <div style={horizontalTrackStyle()} />; | ||
}} | ||
renderThumbHorizontal={({ style }: any) => { | ||
return ( | ||
<Box | ||
radius="full" | ||
cursor="grab" | ||
bg="scrollbar.bg" | ||
opacity={isScrolling ? 1 : 0} | ||
height="6px" | ||
width={thumbHorizontal} | ||
css={{ | ||
transition: 'opacity .1s', | ||
transform: `translateX(${getScrollTransform(clientWidth, scrollWidth, scrollLeft, thumbHorizontal)}px)`, | ||
}} | ||
/> | ||
); | ||
}} | ||
renderTrackVertical={() => { | ||
return <div style={verticalTrackStyle()} />; | ||
}} | ||
renderThumbVertical={({ style }: any) => { | ||
return ( | ||
<Box | ||
radius="full" | ||
cursor="grab" | ||
bg="scrollbar.bg" | ||
opacity={isScrolling ? 1 : 0} | ||
height={thumbVertical} | ||
width="6px" | ||
css={{ | ||
transition: 'opacity .1s', | ||
transform: `translateY(${getScrollTransform(clientHeight, scrollHeight, scrollTop, thumbVertical)}px)`, | ||
}} | ||
/> | ||
); | ||
}} | ||
> | ||
{children} | ||
</Scrollbars> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
import * as React from 'react'; | ||
import { AutoSizer, Index, List, ListRowProps, ListRowRenderer } from 'react-virtualized'; | ||
|
||
import { Box } from './Box'; | ||
import { getScrollTransform, getThumbDimension, verticalTrackStyle } from './utils/scroll'; | ||
|
||
import { styled } from './utils'; | ||
|
||
export interface IScrollListItemProps { | ||
key: string; | ||
index: number; | ||
value: any; | ||
style: React.CSSProperties; | ||
} | ||
|
||
export interface IOnScroll { | ||
clientHeight?: number; | ||
scrollHeight?: number; | ||
scrollTop?: number; | ||
} | ||
|
||
export interface IScrollListProps { | ||
// Either a fixed row height (number) or a function that returns the height of a row given its index. | ||
rowHeight: number | ((params: Index) => number); | ||
|
||
// Responsible for rendering a row | ||
rowRenderer: ({ key, index, value }: IScrollListItemProps) => JSX.Element; | ||
noRowsRenderer?: () => JSX.Element; | ||
onScroll?: (e: IOnScroll) => void; | ||
list: any[]; | ||
|
||
// Controls the alignment scrolled-to-rows. | ||
scrollToAlignment?: 'auto' | 'start' | 'end' | 'center'; | ||
|
||
// Row index to ensure visible (by forcefully scrolling if necessary) | ||
scrollToIndex?: number; | ||
|
||
// Forced vertical scroll offset; can be used to synchronize scrolling between components | ||
offset?: number; | ||
} | ||
|
||
const ListView = (props: IScrollListProps & { className: string }) => { | ||
const { | ||
className, | ||
list, | ||
rowHeight, | ||
scrollToIndex, | ||
scrollToAlignment, | ||
offset, | ||
rowRenderer, | ||
noRowsRenderer, | ||
onScroll, | ||
} = props; | ||
|
||
const [{ clientHeight, scrollHeight, scrollTop }, setScrollEvent] = React.useState<IOnScroll>({}); | ||
const thumbSize = getThumbDimension({ scroll: scrollHeight, client: clientHeight }); | ||
|
||
const [isScrolling, setisScrolling] = React.useState(false); | ||
|
||
const renderRow = ({ key, index, style }: ListRowProps) => rowRenderer({ key, index, value: list[index], style }); | ||
|
||
return ( | ||
<AutoSizer> | ||
{({ height, width }) => ( | ||
<Box height={height} width={width} overflow="hidden" className={'box'}> | ||
<List | ||
className={className} | ||
rowHeight={rowHeight} | ||
rowCount={list.length} | ||
rowRenderer={renderRow as ListRowRenderer} | ||
noRowsRenderer={noRowsRenderer} | ||
onScroll={(e: IOnScroll) => { | ||
setScrollEvent(e); | ||
|
||
if (isScrolling) { | ||
// @ts-ignore | ||
clearTimeout(isScrolling); | ||
} | ||
|
||
setisScrolling( | ||
// @ts-ignore | ||
setTimeout(() => { | ||
setisScrolling(false); | ||
}, 1000) | ||
); | ||
|
||
if (onScroll) { | ||
onScroll(e); | ||
} | ||
}} | ||
scrollToAlignment={scrollToAlignment} | ||
scrollToIndex={scrollToIndex} | ||
scrollTop={offset} | ||
height={height + 15} // add 15 to offset the native scrollbars | ||
width={width + 15} // add 15 to offset the native scrollbars | ||
/> | ||
|
||
{/** scrollbar */} | ||
<div style={verticalTrackStyle()}> | ||
<Box | ||
className={'scroll1'} | ||
height={`${thumbSize}px`} | ||
width="6px" | ||
cursor="grab" | ||
radius="full" | ||
opacity={isScrolling ? 1 : 0} | ||
bg="scrollbar.bg" | ||
css={{ | ||
transform: `translateY(${getScrollTransform(clientHeight, scrollHeight, scrollTop, thumbSize)}px)`, | ||
transition: 'opacity .1s', | ||
}} | ||
/> | ||
</div> | ||
</Box> | ||
)} | ||
</AutoSizer> | ||
); | ||
}; | ||
|
||
export const ScrollList = styled<IScrollListProps>(ListView as any)``; |
Oops, something went wrong.