Skip to content

Commit

Permalink
Feat/with scroll enabler (#807)
Browse files Browse the repository at this point in the history
* Add asScrollEnabled

* Rename asScrollEnabled to withScrollEnabler

* Fix type for FlatList \ ScrollView

* Move screen to components

* Simplify ref (not working)

* Move ref to screen

* Remove ref from screen since it creates a warning

* Move to scrollEnablerProps

* Fix dependencies

* Remove extra renders

* Move to useCallback

* Fix dependencies
  • Loading branch information
M-i-k-e-l committed Jun 24, 2020
1 parent f3d2ecc commit d83cc6c
Show file tree
Hide file tree
Showing 12 changed files with 431 additions and 0 deletions.
5 changes: 5 additions & 0 deletions demo/src/screens/MenuStructure.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,11 @@ export const navigationData = {
{title: 'StateScreen', tags: 'empty state screen', screen: 'unicorn.screens.EmptyStateScreen'},
{title: 'TabController', tags: 'tabbar controller native', screen: 'unicorn.components.TabControllerScreen'},
{title: 'TabBar', tags: 'tab bar', screen: 'unicorn.components.TabBarScreen'},
{
title: 'withScrollEnabler',
tags: 'scroll enabled withScrollEnabler',
screen: 'unicorn.components.WithScrollEnablerScreen'
},
{title: 'Wizard', tags: 'wizard', screen: 'unicorn.components.WizardScreen'}
]
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import _ from 'lodash';
import React, {memo, useCallback} from 'react';
import {
FlatList,
FlatListProps,
StyleSheet,
LayoutChangeEvent
} from 'react-native';
import {
Colors,
Text,
View,
withScrollEnabler,
WithScrollEnablerProps
} from 'react-native-ui-lib';

export type AutoLockScrollViewProps = FlatListProps<number> & {
numberOfItems: number;
};

const AutoLockFlatList = (props: AutoLockScrollViewProps) => {
const numberOfItems = props.numberOfItems;

const WithScrollEnabler = withScrollEnabler(
useCallback(
(props: WithScrollEnablerProps) => {
const getData = useCallback(
(numberOfItems: number) => {
return [...Array(numberOfItems).keys()];
},
[numberOfItems]
);

const keyExtractor = useCallback((item: number) => {
return item.toString();
}, []);

const renderItem = useCallback(({index}: {index: number}) => {
return (
<View key={index} style={styles.item}>
<Text>{index + 1}</Text>
</View>
);
}, []);

const onContentSizeChange = useCallback(
(contentWidth: number, contentHeight: number) => {
_.invoke(props, 'onContentSizeChange', contentWidth, contentHeight);
_.invoke(
props,
'scrollEnablerProps.onContentSizeChange',
contentWidth,
contentHeight
);
},
[
props.onContentSizeChange,
props.scrollEnablerProps.onContentSizeChange
]
);

const onLayout = useCallback(
(nativeEvent: LayoutChangeEvent) => {
_.invoke(props, 'onLayout', nativeEvent);
_.invoke(props, 'scrollEnablerProps.onLayout', nativeEvent);
},
[props.onLayout, props.scrollEnablerProps.onLayout]
);

return (
<FlatList
{...props}
style={styles.flatList}
contentContainerStyle={styles.flatListContainer}
showsHorizontalScrollIndicator={false}
showsVerticalScrollIndicator={false}
data={getData(numberOfItems)}
renderItem={renderItem}
keyExtractor={keyExtractor}
onContentSizeChange={onContentSizeChange}
onLayout={onLayout}
scrollEnabled={props.scrollEnablerProps.scrollEnabled}
/>
);
},
[numberOfItems]
)
);

return <WithScrollEnabler {...props} />;
};

export default memo(AutoLockFlatList);

const styles = StyleSheet.create({
flatList: {
height: 240
},
flatListContainer: {
alignItems: 'center'
},
item: {
width: 100,
height: 100,
margin: 9,
backgroundColor: Colors.grey40,
alignItems: 'center',
justifyContent: 'center'
}
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import _ from 'lodash';
import React, {memo, useCallback} from 'react';
import {
ScrollView,
ScrollViewProps,
StyleSheet,
LayoutChangeEvent
} from 'react-native';
import {
Colors,
Text,
View,
withScrollEnabler,
WithScrollEnablerProps
} from 'react-native-ui-lib';

export type AutoLockScrollViewProps = ScrollViewProps & {
numberOfItems: number;
};

const AutoLockScrollView = (props: AutoLockScrollViewProps) => {
const numberOfItems = props.numberOfItems;

const WithScrollEnabler = withScrollEnabler(
useCallback(
(props: WithScrollEnablerProps) => {
const renderItem = useCallback((index: number) => {
return (
<View key={index} style={styles.item}>
<Text>{index + 1}</Text>
</View>
);
}, []);

const onContentSizeChange = useCallback(
(contentWidth: number, contentHeight: number) => {
_.invoke(props, 'onContentSizeChange', contentWidth, contentHeight);
_.invoke(
props,
'scrollEnablerProps.onContentSizeChange',
contentWidth,
contentHeight
);
},
[
props.onContentSizeChange,
props.scrollEnablerProps.onContentSizeChange
]
);

const onLayout = useCallback(
(nativeEvent: LayoutChangeEvent) => {
_.invoke(props, 'onLayout', nativeEvent);
_.invoke(props, 'scrollEnablerProps.onLayout', nativeEvent);
},
[props.onLayout, props.scrollEnablerProps.onLayout]
);

return (
<ScrollView
{...props}
style={styles.scrollView}
contentContainerStyle={styles.scrollViewContainer}
showsHorizontalScrollIndicator={false}
showsVerticalScrollIndicator={false}
onContentSizeChange={onContentSizeChange}
onLayout={onLayout}
scrollEnabled={props.scrollEnablerProps.scrollEnabled}
>
{_.times(numberOfItems, renderItem)}
</ScrollView>
);
},
[numberOfItems]
)
);

return <WithScrollEnabler {...props} />;
};

export default memo(AutoLockScrollView);

const styles = StyleSheet.create({
scrollView: {
height: 240
},
scrollViewContainer: {
alignItems: 'center'
},
item: {
width: 100,
height: 100,
margin: 9,
backgroundColor: Colors.grey40,
alignItems: 'center',
justifyContent: 'center'
}
});
116 changes: 116 additions & 0 deletions demo/src/screens/componentScreens/WithScrollEnablerScreen/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import React, {Component} from 'react';
import {LayoutChangeEvent} from 'react-native';
import {Text, View} from 'react-native-ui-lib';
import {
renderHeader,
renderBooleanOption,
renderSliderOption
} from '../../ExampleScreenPresenter';
import AutoLockScrollView from './AutoLockScrollView';
import AutoLockFlatList from './AutoLockFlatList';

class WithScrollEnablerScreen extends Component {
state = {
isListView: false,
isHorizontal: false,
numberOfItems: 3,
contentWidth: undefined,
contentHeight: undefined,
layoutWidth: undefined,
layoutHeight: undefined
};

onContentSizeChange = (contentWidth: number, contentHeight: number) => {
const {
contentWidth: currentContentWidth,
contentHeight: currentContentHeight
} = this.state;
if (
currentContentWidth !== contentWidth ||
currentContentHeight !== contentHeight
) {
this.setState({contentWidth, contentHeight});
}
};

onLayout = ({
nativeEvent: {
layout: {width, height}
}
}: LayoutChangeEvent) => {
const {layoutWidth, layoutHeight} = this.state;
if (width !== layoutWidth || height !== layoutHeight) {
this.setState({layoutWidth: width, layoutHeight: height});
}
};

renderList = () => {
const {isListView, isHorizontal, numberOfItems} = this.state;
const Container = isListView ? AutoLockScrollView : AutoLockFlatList;

return (
// @ts-ignore
<Container
key={`${isHorizontal}`}
horizontal={isHorizontal}
numberOfItems={numberOfItems}
onContentSizeChange={this.onContentSizeChange}
onLayout={this.onLayout}
/>
);
};

renderData = () => {
const {contentWidth, contentHeight, layoutWidth, layoutHeight} = this.state;
const contentText = `Content {width, height}: ${contentWidth}, ${contentHeight}`;
const layoutText = `Layout {width, height}: ${layoutWidth}, ${layoutHeight}`;
return (
<>
<Text text70>{contentText}</Text>
<Text text70>{layoutText}</Text>
</>
);
};

renderOptions = () => {
const {isListView, isHorizontal} = this.state;
const orientationText = isHorizontal ? 'Horizontal' : 'Vertical';
const listTypeText = isListView ? 'ListView' : 'FlatList';
return (
<>
<View row>
<View flex marginR-10>
{renderBooleanOption.call(this, orientationText, 'isHorizontal')}
</View>
<View flex marginL-10>
{renderBooleanOption.call(this, listTypeText, 'isListView')}
</View>
</View>
{renderSliderOption.call(
this,
'Number of items shown',
'numberOfItems',
{
min: 1,
max: 5,
step: 1,
initial: 3
}
)}
</>
);
};

render() {
return (
<View margin-10>
{renderHeader('withScrollEnabler', {'marginB-10': true})}
{this.renderOptions()}
{this.renderData()}
{this.renderList()}
</View>
);
}
}

export default WithScrollEnablerScreen;
1 change: 1 addition & 0 deletions demo/src/screens/componentScreens/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,5 +57,6 @@ export function registerScreens(registrar) {
registrar('unicorn.screens.EmptyStateScreen', () => require('./EmptyStateScreen').default);
registrar('unicorn.screens.LoadingScreen', () => require('./LoadingScreen').default);
registrar('unicorn.screens.ModalScreen', () => require('./ModalScreen').default);
registrar('unicorn.components.WithScrollEnablerScreen', () => require('./WithScrollEnablerScreen').default);
}

1 change: 1 addition & 0 deletions generatedTypes/commons/new.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export { default as UIComponent } from './UIComponent';
export { default as asBaseComponent, BaseComponentInjectedProps } from './asBaseComponent';
export { default as forwardRef, ForwardRefInjectedProps } from './forwardRef';
export { default as withScrollEnabler } from './withScrollEnabler';
export { ContainerModifiers, MarginModifiers, TypographyModifiers, ColorsModifiers, BackgroundColorModifier } from './modifiers';
14 changes: 14 additions & 0 deletions generatedTypes/commons/withScrollEnabler.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from 'react';
import { FlatListProps, ScrollViewProps, LayoutChangeEvent } from 'react-native';
export declare type ScrollEnablerProps = {
onContentSizeChange: (contentWidth: number, contentHeight: number) => void;
onLayout: (event: LayoutChangeEvent) => void;
scrollEnabled: boolean;
};
declare type SupportedViews = FlatListProps<any> | ScrollViewProps;
export declare type WithScrollEnablerProps = SupportedViews & {
scrollEnablerProps: ScrollEnablerProps;
ref?: any;
};
declare function withScrollEnabler<PROPS extends SupportedViews>(WrappedComponent: React.ComponentType<WithScrollEnablerProps>): React.ComponentType<PROPS>;
export default withScrollEnabler;
1 change: 1 addition & 0 deletions generatedTypes/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* Please use this file for declaring all the exports, so they could be picked up by typescript's complier
*/
export * from './style';
export {withScrollEnabler} from './commons/new';
export {default as Card, CardPropTypes, CardSectionProps} from './components/card';
export {default as View, ViewPropTypes} from './components/view';
export {default as Text} from './components/text';
Expand Down
3 changes: 3 additions & 0 deletions src/commons/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,8 @@ module.exports = {
},
get forwardRef() {
return require('./forwardRef').default;
},
get withScrollEnabler() {
return require('./withScrollEnabler').default;
}
};
1 change: 1 addition & 0 deletions src/commons/new.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
export {default as UIComponent} from './UIComponent';
export {default as asBaseComponent, BaseComponentInjectedProps} from './asBaseComponent';
export {default as forwardRef, ForwardRefInjectedProps} from './forwardRef';
export {default as withScrollEnabler} from './withScrollEnabler';
export {
ContainerModifiers,
MarginModifiers,
Expand Down

0 comments on commit d83cc6c

Please sign in to comment.