Skip to content

ScrollBar - TS migration #1078

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Dec 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 91 additions & 0 deletions generatedTypes/components/scrollBar/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import React, { Component } from 'react';
import { Animated, FlatListProps, ImageSourcePropType, NativeSyntheticEvent, NativeScrollEvent, LayoutChangeEvent } from 'react-native';
import { ForwardRefInjectedProps } from '../../commons/new';
interface Props extends FlatListProps<any>, ForwardRefInjectedProps {
/**
* Whether to use a FlatList. NOTE: you must pass 'data' and 'renderItem' props as well
*/
useList?: boolean;
/**
* The element to use as a container, instead of a View
*/
containerView?: JSX.Element;
/**
* The props to pass the container
*/
containerProps?: object;
/**
* The component's height
*/
height?: number;
/**
* The gradient's height, defaults to the component's height
*/
gradientHeight?: number;
/**
* The gradient's width
*/
gradientWidth?: number;
/**
* The gradient's margins for the edge
*/
gradientMargins?: number;
/**
* The gradient's tint color
*/
gradientColor?: string;
/**
* The gradient's image, instead of the default image.
* NOTE: pass an image for the right-hand side and it will be flipped to match the left-hand side
*/
gradientImage?: ImageSourcePropType;
/**
* The index to currently focus on
*/
focusIndex?: number;
}
export declare type ScrollBarProps = Props;
export declare type State = {
gradientOpacity: Animated.Value;
gradientOpacityLeft: Animated.Value;
};
/**
* @description: Scrollable container with animated gradient overlay for horizontal scroll
* @extends: ScrollView / FlatList
*/
declare class ScrollBar extends Component<Props, State> {
static displayName: string;
static defaultProps: {
gradientWidth: number;
gradientMargins: number;
gradientColor: string;
focusIndex: number;
};
static Item: typeof Item;
constructor(props: Props);
private scrollbar;
private itemsLayouts;
private contentOffset;
private scrollContentWidth;
private containerWidth;
componentDidUpdate(prevProps: Props): void;
focusIndex: (index?: number) => void;
animateGradientOpacity: (offsetX: number, contentWidth: number, containerWidth: number) => void;
onScroll: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
onContentSizeChange: (contentWidth: number, contentHeight: number) => void;
onLayout: (event: LayoutChangeEvent) => void;
onItemLayout: ({ layout, index }: any) => void;
renderScrollable(): JSX.Element;
renderGradient(left: boolean): JSX.Element;
render(): JSX.Element;
}
declare const Item: {
({ children, index, onLayout }: any): JSX.Element;
displayName: string;
};
declare const _default: React.ComponentClass<Props & {
useCustomTheme?: boolean | undefined; /**
* Whether to use a FlatList. NOTE: you must pass 'data' and 'renderItem' props as well
*/
}, any> & typeof ScrollBar;
export default _default;
2 changes: 1 addition & 1 deletion generatedTypes/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export {default as RadioButton, RadioButtonPropTypes, RadioButtonProps} from './
export {default as RadioGroup, RadioGroupPropTypes, RadioGroupProps} from './components/radioButton/RadioGroup';
export {default as Switch, SwitchProps} from './components/switch';
export {default as TabBar, TabBarProps} from './components/TabBar';
export {default as ScrollBar, ScrollBarProps} from './components/ScrollBar';
export {default as Fader, FaderProps, FaderPosition} from './components/fader';
export {default as ExpandableSection, ExpandableSectionProps} from './components/expandableSection';
export {default as Modal, ModalProps, ModalTopBarProps} from './components/modal';
Expand Down Expand Up @@ -113,7 +114,6 @@ export const KeyboardAwareListView;
export const KeyboardAwareScrollView;
export const SafeAreaInsetsManager;
export const SafeAreaSpacerView;
export const ScrollBar;
export const SelectableComponent;
export const TabController;
export const TextField;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,69 +1,83 @@
import _ from 'lodash';
import PropTypes from 'prop-types';
import React, {useCallback} from 'react';
import {Animated, ScrollView, FlatList} from 'react-native';
import React, {Component, useCallback} from 'react';
import {
Animated,
ScrollView,
FlatList,
FlatListProps,
ImageSourcePropType,
NativeSyntheticEvent,
NativeScrollEvent,
LayoutChangeEvent
} from 'react-native';
import {Constants} from '../../helpers';
import {Colors} from '../../style';
import {BaseComponent, forwardRef} from '../../commons';
import {asBaseComponent, forwardRef, ForwardRefInjectedProps} from '../../commons/new';
import View from '../view';
import Image from '../image';

const GRADIENT_WIDTH = 76;
const defaultImage = () => require('./assets/gradientOverlay.png');

/**
* @description: Scrollable container with animated gradient overlay for horizontal scroll
* @extends: ScrollView / FlatList
*/

class ScrollBar extends BaseComponent {
static displayName = 'ScrollBar';

static propTypes = {
...ScrollView.propTypes,
...FlatList.propTypes,
/**
export interface ScrollBarProps extends FlatListProps<any> {
/**
* Whether to use a FlatList. NOTE: you must pass 'data' and 'renderItem' props as well
*/
useList: PropTypes.bool,
useList?: boolean,
/**
* The element to use as a container, instead of a View
*/
containerView: PropTypes.oneOfType([PropTypes.element, PropTypes.elementType]),
containerView?: JSX.Element,
/**
* The props to pass the container
*/
containerProps: PropTypes.object,
containerProps?: object,
/**
* The component's height
*/
height: PropTypes.number,
height?: number,
/**
* The gradient's height, defaults to the component's height
*/
gradientHeight: PropTypes.number,
gradientHeight?: number,
/**
* The gradient's width
*/
gradientWidth: PropTypes.number,
gradientWidth?: number,
/**
* The gradient's margins for the edge
*/
gradientMargins: PropTypes.number,
gradientMargins?: number,
/**
* The gradient's tint color
*/
gradientColor: PropTypes.string,
gradientColor?: string,
/**
* The gradient's image, instead of the default image.
* NOTE: pass an image for the right-hand side and it will be flipped to match the left-hand side
*/
gradientImage: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
gradientImage?: ImageSourcePropType,
/**
* The index to currently focus on
*/
focusIndex: PropTypes.number
};
focusIndex?: number
};
type Props = ScrollBarProps & ForwardRefInjectedProps;

export type State = {
gradientOpacity: Animated.Value,
gradientOpacityLeft: Animated.Value
};


const GRADIENT_WIDTH = 76;
const defaultImage = () => require('./assets/gradientOverlay.png');

/**
* @description: Scrollable container with animated gradient overlay for horizontal scroll
* @extends: ScrollView / FlatList
*/

class ScrollBar extends Component<Props, State> {
static displayName = 'ScrollBar';

static defaultProps = {
gradientWidth: GRADIENT_WIDTH,
Expand All @@ -72,27 +86,31 @@ class ScrollBar extends BaseComponent {
focusIndex: 0
};

constructor(props) {
static Item: typeof Item;

constructor(props: Props) {
super(props);

this.state = {
gradientOpacity: new Animated.Value(0),
gradientOpacityLeft: new Animated.Value(0)
};

this.scrollContentWidth = undefined;
this.itemsLayouts = {};
this.contentOffset = 0;
}

componentDidUpdate(prevProps, prevState) {
private scrollbar: any = undefined;
private itemsLayouts: any = {};
private contentOffset: number = 0;
private scrollContentWidth: number = 0;
private containerWidth: number = 0;

componentDidUpdate(prevProps: Props) {
const {focusIndex} = this.props;
if (focusIndex && prevProps.focusIndex !== focusIndex) {
if (prevProps.focusIndex !== focusIndex) {
this.focusIndex(focusIndex);
}
}

focusIndex = index => {
focusIndex = (index: number = 0) => {
const focusedItemLayout = this.itemsLayouts[index];
if (focusedItemLayout) {
const {x, width} = focusedItemLayout;
Expand All @@ -105,7 +123,7 @@ class ScrollBar extends BaseComponent {
}
};

animateGradientOpacity = (offsetX, contentWidth, containerWidth) => {
animateGradientOpacity = (offsetX: number, contentWidth: number, containerWidth: number) => {
const overflow = contentWidth - containerWidth;
const newValue = offsetX > 0 && offsetX >= overflow - 1 ? 0 : 1;
const newValueLeft = offsetX > 0 ? 1 : 0;
Expand All @@ -124,7 +142,7 @@ class ScrollBar extends BaseComponent {
]).start();
};

onScroll = event => {
onScroll = (event: NativeSyntheticEvent<NativeScrollEvent>) => {
const {layoutMeasurement, contentOffset, contentSize} = event.nativeEvent;
this.contentOffset = contentOffset.x;
const offsetX = contentOffset.x;
Expand All @@ -136,7 +154,7 @@ class ScrollBar extends BaseComponent {
_.invoke(this.props, 'onScroll', event);
};

onContentSizeChange = (contentWidth, contentHeight) => {
onContentSizeChange = (contentWidth: number, contentHeight: number) => {
if (this.scrollContentWidth !== contentWidth) {
this.scrollContentWidth = contentWidth;

Expand All @@ -149,34 +167,37 @@ class ScrollBar extends BaseComponent {
}
};

onLayout = ({nativeEvent}) => {
this.containerWidth = nativeEvent.layout.width;
onLayout = (event: LayoutChangeEvent) => {
this.containerWidth = event.nativeEvent.layout.width;

// 1 - for race condition, in case onContentSizeChange() is called before
// 0 - for containerWidth change, when onContentSizeChange() is called first
this.setState({gradientOpacity: new Animated.Value(this.scrollContentWidth > this.containerWidth ? 1 : 0)});
};

onItemLayout = ({layout, index}) => {
onItemLayout = ({layout, index}: any) => {
this.itemsLayouts[index] = layout;
if (_.keys(this.itemsLayouts).length === this.props.children.length) {
this.focusIndex(this.props.focusIndex);

const {children, focusIndex} = this.props;
if (children && _.keys(this.itemsLayouts).length === _.keys(children).length) {
this.focusIndex(focusIndex);
}
};

renderScrollable() {
const {useList, forwardedRef, children} = this.props;
const Component = useList ? FlatList : ScrollView;
const Component: any = useList ? FlatList : ScrollView;

return (
<Component
scrollEventThrottle={100}
{...this.getThemeProps()}
ref={r => {
{...this.props}
ref={(r: any) => {
this.scrollbar = r;
if (_.isFunction(forwardedRef)) {
forwardedRef(r);
} else if (_.isObject(forwardedRef)) {
//@ts-ignore
forwardedRef.current = r;
}
}}
Expand All @@ -197,9 +218,9 @@ class ScrollBar extends BaseComponent {
);
}

renderGradient(left) {
renderGradient(left: boolean) {
const {gradientOpacity, gradientOpacityLeft} = this.state;
const {gradientWidth, gradientHeight, gradientMargins, height, gradientColor, gradientImage} = this.getThemeProps();
const {gradientWidth, gradientHeight, gradientMargins, height, gradientColor, gradientImage} = this.props;
const imageTransform = Constants.isRTL ? (left ? undefined : [{scaleX: -1}]) : left ? [{scaleX: -1}] : undefined;
const heightToUse = gradientHeight || height || '100%';
return (
Expand Down Expand Up @@ -229,23 +250,23 @@ class ScrollBar extends BaseComponent {
}

render() {
const {containerView, containerProps} = this.getThemeProps();
const Container = containerView || View;
const {containerView, containerProps} = this.props;
const Container: any = containerView || View;

return (
<Container row {...containerProps} onLayout={this.onLayout}>
{this.renderScrollable()}
{this.renderGradient()}
{this.renderGradient(false)}
{this.renderGradient(true)}
</Container>
);
}
}

const Item = ({children, index, onLayout}) => {
const Item = ({children, index, onLayout}: any) => {
const onItemLayout = useCallback(({nativeEvent: {layout}}) => {
onLayout({layout, index});
});
}, [children]);

return (
<View flexG onLayout={onItemLayout}>
Expand All @@ -256,4 +277,5 @@ const Item = ({children, index, onLayout}) => {

Item.displayName = 'IGNORE';
ScrollBar.Item = Item;
export default forwardRef(ScrollBar);
export default asBaseComponent<ScrollBarProps, typeof ScrollBar>(forwardRef(ScrollBar));

1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export {default as RadioButton, RadioButtonPropTypes, RadioButtonProps} from './
export {default as RadioGroup, RadioGroupPropTypes, RadioGroupProps} from './components/radioButton/RadioGroup';
export {default as Switch, SwitchProps} from './components/switch';
export {default as TabBar, TabBarProps} from './components/TabBar';
export {default as ScrollBar, ScrollBarProps} from './components/ScrollBar';
export {default as Fader, FaderProps, FaderPosition} from './components/fader';
export {default as ExpandableSection, ExpandableSectionProps} from './components/expandableSection';
export {default as Modal, ModalProps, ModalTopBarProps} from './components/modal';
Expand Down