From aa9eabda2a67008e7dcff4404407ef566e7dd522 Mon Sep 17 00:00:00 2001 From: Artur Bien Date: Thu, 31 Dec 2020 14:44:21 +0100 Subject: [PATCH] feat(scrollview): add support for horizontal scrollview --- example/src/examples/ScrollViewExample.tsx | 45 +++++++++++++ example/src/examples/index.tsx | 6 ++ src/ScrollView/ScrollView.tsx | 75 +++++++++++++--------- 3 files changed, 96 insertions(+), 30 deletions(-) create mode 100644 example/src/examples/ScrollViewExample.tsx diff --git a/example/src/examples/ScrollViewExample.tsx b/example/src/examples/ScrollViewExample.tsx new file mode 100644 index 0000000..706fb54 --- /dev/null +++ b/example/src/examples/ScrollViewExample.tsx @@ -0,0 +1,45 @@ +import React from 'react'; +import { StyleSheet, View } from 'react-native'; +import { Text, Panel, Cutout, ScrollView } from 'react95-native'; + +const lorem = `Lorem Ipsum is simply dummy text of the printing and typesetting +industry. Lorem Ipsum has been the industry standard dummy text +ever since the 1500s, when an unknown printer took a galley of +type and scrambled it to make a type specimen book. It has +survived not only five centuries, but also the leap into +electronic typesetting, remaining essentially unchanged. It was +popularised in the 1960s with the release of Letraset sheets +containing Lorem Ipsum passages, and more recently with desktop +publishing software like Aldus PageMaker including versions of +Lorem Ipsum.`; + +const NumberInputExample = () => { + return ( + + + + {lorem} + + + + + + {lorem} + + + + + ); +}; + +const styles = StyleSheet.create({ + container: { + flex: 1, + padding: 16, + }, + fieldset: { + padding: 20, + }, +}); + +export default NumberInputExample; diff --git a/example/src/examples/index.tsx b/example/src/examples/index.tsx index 23b7cdf..5abee65 100644 --- a/example/src/examples/index.tsx +++ b/example/src/examples/index.tsx @@ -13,6 +13,7 @@ import PanelExample from './PanelExample'; import ProgressExample from './ProgressExample'; import RadioExample from './RadioExample'; import ScrollPanelExample from './ScrollPanelExample'; +import ScrollViewExample from './ScrollViewExample'; import SelectBoxExample from './SelectBoxExample'; import SelectExample from './SelectExample'; import SnackbarExample from './SnackbarExample'; @@ -68,6 +69,11 @@ export default [ title: 'Fieldset', }, { name: 'PanelExample', component: PanelExample, title: 'Panel' }, + { + name: 'ScrollViewExample', + component: ScrollViewExample, + title: 'ScrollView', + }, { name: 'ScrollPanelExample', component: ScrollPanelExample, diff --git a/src/ScrollView/ScrollView.tsx b/src/ScrollView/ScrollView.tsx index b618d5c..c878c11 100644 --- a/src/ScrollView/ScrollView.tsx +++ b/src/ScrollView/ScrollView.tsx @@ -20,6 +20,7 @@ import { Panel, Button } from '..'; type ScrollViewProps = React.ComponentProps & { alwaysShowScrollbars?: boolean; children: React.ReactNode; + horizontal?: boolean; scrollViewProps?: React.ComponentProps; style?: StyleProp; }; @@ -44,6 +45,7 @@ const Icon = ( const ScrollView = ({ alwaysShowScrollbars = false, children, + horizontal = false, scrollViewProps = {}, style, ...rest @@ -53,29 +55,31 @@ const ScrollView = ({ const scrollViewRef = useRef(null); const [contentOffset, setContentOffset] = useState({ x: 0, y: 0 }); const [contentSize, setContentSize] = useState(0); - const [scrollViewHeight, setScrollViewHeight] = useState(0); + const [scrollViewSize, setScrollViewSize] = useState(0); - const scrollElementHeightPercent = 100 * (scrollViewHeight / contentSize); + const visiblePercentage = 100 * (scrollViewSize / contentSize); - const scrollPerc = - (contentOffset.y / (contentSize - scrollViewHeight)) * - (100 - scrollElementHeightPercent); + const scrollAxis = horizontal ? 'x' : 'y'; + const scrollDimension = horizontal ? 'width' : 'height'; + const scrolledPercentage = (contentOffset[scrollAxis] / contentSize) * 100; const thumbPosition = Math.max( 0, Math.min( - 100 - scrollElementHeightPercent, - parseFloat((scrollPerc || 0).toFixed(3)), + 100 - visiblePercentage, + parseFloat(scrolledPercentage.toFixed(3)), ), ); const moveScroll = (direction: -1 | 1) => { if (scrollViewRef.current) { - scrollViewRef.current.scrollTo({ y: contentOffset.y + 24 * direction }); + scrollViewRef.current.scrollTo({ + [scrollAxis]: contentOffset[scrollAxis] + 24 * direction, + }); } }; - const contentFullyVisible = contentSize <= scrollViewHeight; + const contentFullyVisible = contentSize <= scrollViewSize; const handleScroll = (e: NativeSyntheticEvent) => { scrollViewProps.onScroll?.(e); @@ -84,32 +88,49 @@ const ScrollView = ({ const handleContentSizeChange = (width: number, height: number) => { scrollViewProps.onContentSizeChange?.(width, height); - setContentSize(height); + setContentSize(horizontal ? width : height); }; const handleLayout = (e: LayoutChangeEvent) => { scrollViewProps.onLayout?.(e); - setScrollViewHeight(e.nativeEvent.layout.height); + setScrollViewSize(e.nativeEvent.layout[scrollDimension]); }; return ( - + {children} {(!contentFullyVisible || alwaysShowScrollbars) && ( {Icon} - + {!contentFullyVisible && ( + // SCROLLBAR THUMB @@ -163,7 +187,7 @@ const ScrollView = ({ style={{ justifyContent: 'center', alignItems: 'center', - // flex: 1, + transform: [{ rotate: horizontal ? '-90deg' : '0deg' }], }} > {Icon} @@ -178,24 +202,18 @@ const ScrollView = ({ const styles = StyleSheet.create({ wrapper: { display: 'flex', - flexDirection: 'row', - height: 'auto', position: 'relative', }, content: { flexGrow: 1, - flex: 1, - }, - scrollbarTrack: { - height: '100%', + flexShrink: 1, }, scrollbarButton: { height: scrollbarSize, width: scrollbarSize, padding: 0, }, - scrollbar: { - width: scrollbarSize, + scrollbarTrack: { overflow: 'hidden', flex: 1, }, @@ -206,9 +224,6 @@ const styles = StyleSheet.create({ bottom: 0, left: 0, }, - scrollbarBar: { - width: '100%', - }, }); export default ScrollView;