Skip to content

Feat/SectionsWheelPicker new component #1260

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 11 commits into from
May 4, 2021
3 changes: 3 additions & 0 deletions demo/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ module.exports = {
get RadioButtonScreen() {
return require('./screens/componentScreens/RadioButtonScreen').default;
},
get SectionsWheelPickerScreen() {
return require('./screens/componentScreens/SectionsWheelPickerScreen').default;
},
get SegmentedControlScreen() {
return require('./screens/componentScreens/SegmentedControlScreen').default;
},
Expand Down
1 change: 1 addition & 0 deletions demo/src/screens/MenuStructure.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export const navigationData = {
{title: 'Picker', tags: 'picker form', screen: 'unicorn.components.PickerScreen'},
{title: 'DateTimePicker', tags: 'date time picker form', screen: 'unicorn.components.DateTimePickerScreen'},
{title: 'RadioButton', tags: 'radio button group controls', screen: 'unicorn.components.RadioButtonScreen'},
{title: 'SectionsWheelPicker', tags: 'sections wheel picker form', screen: 'unicorn.components.SectionsWheelPickerScreen'},
{title: 'SegmentedControl', tags: 'segmented control switch toggle', screen: 'unicorn.components.SegmentedControlScreen'},
{title: 'Stepper', tags: 'stepper form', screen: 'unicorn.components.StepperScreen'},
{title: 'Slider', tags: 'slider', screen: 'unicorn.components.SliderScreen'},
Expand Down
95 changes: 95 additions & 0 deletions demo/src/screens/componentScreens/SectionsWheelPickerScreen.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import _ from 'lodash';
import React, {useState, useCallback} from 'react';
import {Alert} from 'react-native';
import {Text, View, SectionsWheelPicker, SegmentedControl, Button, Incubator} from 'react-native-ui-lib';

const SectionsWheelPickerScreen = () => {
const [showMinutes, setShowMinutes] = useState(false);

const [selectedDays, setSelectedDays] = useState(0);
const [selectedHours, setSelectedHours] = useState(0);
const [selectedMinutes, setSelectedMinutes] = useState(0);

const days = _.times(10, i => i);
const hours = _.times(24, i => i);
const minutes = _.times(60, i => i);

const getItems = useCallback(values => {
return _.map(values, item => ({label: '' + item, value: item}));
}, []);

const onDaysChange = (item: number | string) => {
setSelectedDays(item as number);
};

const onHoursChange = (item: number | string) => {
setSelectedHours(item as number);
};

const onMinutesChange = (item: number | string) => {
setSelectedMinutes(item as number);
};

const onSavePress = () => {
const days = selectedDays === 1 ? 'day' : 'days';
const hours = selectedHours === 1 ? 'hour' : 'hours';
const minutes = selectedMinutes === 1 ? 'minute' : 'minutes';

showMinutes
? Alert.alert('Your chosen duration is:\n' +
selectedDays +
' ' +
days +
', ' +
selectedHours +
' ' +
hours +
' and ' +
selectedMinutes +
' ' +
minutes)
: Alert.alert('Your chosen duration is:\n' + selectedDays + ' ' + days + ' and ' + selectedHours + ' ' + hours);
};

const onResetPress = () => {
setSelectedDays(0);
setSelectedHours(0);
setSelectedMinutes(0);
};

const sections: Incubator.WheelPickerProps[] = [
{items: getItems(days), onChange: onDaysChange, selectedValue: selectedDays, label: 'Days'},
{items: getItems(hours), onChange: onHoursChange, selectedValue: selectedHours, label: 'Hrs'},
{
items: getItems(minutes),
onChange: onMinutesChange,
selectedValue: selectedMinutes,
label: 'Mins'
}
];

const sectionsToPresent = showMinutes ? sections : _.slice(sections, 0, 2);

const onChangeIndex = (index: number) => {
return index === 0 ? setShowMinutes(false) : setShowMinutes(true);
};

return (
<View>
<Text text40 marginL-10 marginT-20>
Sections Wheel Picker
</Text>
<View centerH marginT-40>
<SegmentedControl segments={[{label: '2 sections'}, {label: '3 sections'}]} onChangeIndex={onChangeIndex}/>
<Text text50 marginV-20>
Pick a duration
</Text>
</View>
<SectionsWheelPicker sections={sectionsToPresent}/>
<Button marginH-150 marginT-300 label={'Save'} onPress={onSavePress}/>
<Button marginH-150 marginT-15 label={'Reset'} onPress={onResetPress}/>
</View>
);
};

export default SectionsWheelPickerScreen;
1 change: 1 addition & 0 deletions demo/src/screens/componentScreens/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export function registerScreens(registrar) {
registrar('unicorn.animations.ProgressBarScreen', () => require('../componentScreens/ProgressBarScreen').default);
registrar('unicorn.components.RadioButtonScreen', () => require('./RadioButtonScreen').default);
registrar('unicorn.components.ScrollBarScreen', () => require('./ScrollBarScreen').default);
registrar('unicorn.components.SectionsWheelPickerScreen', () => require('./SectionsWheelPickerScreen').default);
registrar('unicorn.components.SegmentedControlScreen', () => require('./SegmentedControlScreen').default);
registrar('unicorn.components.SharedTransitionScreen', () => require('./SharedTransitionScreen').default);
registrar('unicorn.components.StepperScreen', () => require('./StepperScreen').default);
Expand Down
2 changes: 1 addition & 1 deletion demo/src/screens/incubatorScreens/WheelPickerScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ export default () => {
<Incubator.WheelPicker
onChange={onDaysChange}
selectedValue={selectedDays}
label={selectedDays === 1 ? 'Day' : 'Days'}
label={'Days'}
items={getDays()}
/>
</Dialog>
Expand Down
36 changes: 36 additions & 0 deletions generatedTypes/components/sectionsWheelPicker/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React from 'react';
import { TextStyle } from 'react-native';
import { WheelPickerProps } from '../../incubator';
export declare type SectionsWheelPickerProps = {
/**
* Array of sections.
*/
sections?: WheelPickerProps[];
/**
* Describe the height of each item in the WheelPicker
* default value: 44
*/
itemHeight?: number;
/**
* Describe the number of rows visible
* default value: 5
*/
numberOfVisibleRows?: number;
/**
* Text color for the focused row
*/
activeTextColor?: string;
/**
* Text color for other, non-focused rows
*/
inactiveTextColor?: string;
/**
* Row text style
*/
textStyle?: TextStyle;
testID?: string;
};
declare const _default: React.ComponentClass<SectionsWheelPickerProps & {
useCustomTheme?: boolean | undefined;
}, any>;
export default _default;
4 changes: 3 additions & 1 deletion generatedTypes/incubator/WheelPicker/Item.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ interface InternalProps extends ItemProps {
inactiveColor?: string;
style?: TextStyle;
onSelect: (index: number) => void;
testID?: string;
centerH?: boolean;
}
declare const _default: ({ index, label, itemHeight, onSelect, offset, activeColor, inactiveColor, style }: InternalProps) => JSX.Element;
declare const _default: ({ index, label, itemHeight, onSelect, offset, activeColor, inactiveColor, style, testID, centerH }: InternalProps) => JSX.Element;
export default _default;
3 changes: 2 additions & 1 deletion generatedTypes/incubator/WheelPicker/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export interface WheelPickerProps {
* WheelPicker initial value, can be ItemProps.value, number as index
*/
selectedValue: ItemProps | number | string;
testID?: string;
}
declare const WheelPicker: React.MemoExoticComponent<({ items: propItems, itemHeight, numberOfVisibleRows, activeTextColor, inactiveTextColor, textStyle, label, labelStyle, labelProps, onChange, style, children, selectedValue }: WheelPickerProps) => JSX.Element>;
declare const WheelPicker: React.MemoExoticComponent<({ items: propItems, itemHeight, numberOfVisibleRows, activeTextColor, inactiveTextColor, textStyle, label, labelStyle, labelProps, onChange, style, children, selectedValue, testID }: WheelPickerProps) => JSX.Element>;
export default WheelPicker;
2 changes: 1 addition & 1 deletion generatedTypes/incubator/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export { default as TabController } from './TabController';
export { default as TextField } from './TextField';
export { default as TouchableOpacity, TouchableOpacityProps } from './TouchableOpacity';
export { default as WheelPicker } from './WheelPicker';
export { default as WheelPicker, WheelPickerProps } from './WheelPicker';
1 change: 1 addition & 0 deletions generatedTypes/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export {default as Image, ImageProps} from './components/image';
export {default as Overlay, OverlayTypes} from './components/overlay';
export {default as RadioButton, RadioButtonPropTypes, RadioButtonProps} from './components/radioButton/RadioButton';
export {default as RadioGroup, RadioGroupPropTypes, RadioGroupProps} from './components/radioButton/RadioGroup';
export {default as SectionsWheelPicker, SectionsWheelPickerProps} from './components/sectionsWheelPicker';
export {default as HapticService, HapticType} from './services/HapticService';
export {default as SegmentedControl, SegmentedControlProps, SegmentedControlItemProps} from './components/segmentedControl';
export {default as Switch, SwitchProps} from './components/switch';
Expand Down
72 changes: 72 additions & 0 deletions src/components/sectionsWheelPicker/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import _ from 'lodash';
import React from 'react';
import {TextStyle} from 'react-native';
import {asBaseComponent} from '../../commons/new';
import View from '../view';
import {WheelPicker, WheelPickerProps} from '../../incubator';

export type SectionsWheelPickerProps = {
/**
* Array of sections.
*/
sections?: WheelPickerProps[];
/**
* Describe the height of each item in the WheelPicker
* default value: 44
*/
itemHeight?: number;
/**
* Describe the number of rows visible
* default value: 5
*/
numberOfVisibleRows?: number;
/**
* Text color for the focused row
*/
activeTextColor?: string;
/**
* Text color for other, non-focused rows
*/
inactiveTextColor?: string;
/**
* Row text style
*/
textStyle?: TextStyle;
testID?: string;
};

/**
* @description: SectionsWheelPicker component for presenting set of wheelPickers
* @example: https://github.com/wix/react-native-ui-lib/blob/master/demo/src/screens/componentScreens/SectionsWheelPickerScreen.tsx
*/

const SectionsWheelPicker = (props: SectionsWheelPickerProps) => {
const {sections, itemHeight, numberOfVisibleRows, activeTextColor, inactiveTextColor, textStyle, testID} = props;

const wheelPickerProps = {
itemHeight,
numberOfVisibleRows,
activeTextColor,
inactiveTextColor,
textStyle
};

const renderSections = () =>
_.map(sections, (section, index) => {
return (
<View height={1} key={index} testID={testID}>
<WheelPicker testID={`${testID}.${index}`} {...wheelPickerProps} {...section}/>
</View>
);
});

return (
<View flex row centerH>
{renderSections()}
</View>
);
};

SectionsWheelPicker.displayName = 'SectionsWheelPicker';

export default asBaseComponent<SectionsWheelPickerProps>(SectionsWheelPicker);
42 changes: 25 additions & 17 deletions src/incubator/WheelPicker/Item.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import React, {useCallback, useMemo} from 'react';
import {TextStyle, StyleSheet} from 'react-native';
import Animated, {interpolateColors} from 'react-native-reanimated';
import Text from '../../components/text';
import TouchableOpacity from '../../components/touchableOpacity';
import {TextStyle} from 'react-native';
import {Colors} from '../../../src/style';
import {Colors, Spacings} from '../../../src/style';

const AnimatedTouchableOpacity = Animated.createAnimatedComponent(
TouchableOpacity
);
const AnimatedTouchableOpacity = Animated.createAnimatedComponent(TouchableOpacity);
const AnimatedText = Animated.createAnimatedComponent(Text);

export interface ItemProps {
Expand All @@ -23,6 +21,8 @@ interface InternalProps extends ItemProps {
inactiveColor?: string;
style?: TextStyle;
onSelect: (index: number) => void;
testID?: string;
centerH?: boolean;
}

export default ({
Expand All @@ -33,34 +33,36 @@ export default ({
offset,
activeColor = Colors.primary,
inactiveColor = Colors.grey20,
style
style,
testID,
centerH = true
}: InternalProps) => {

const selectItem = useCallback(() => onSelect(index), [index]);
const itemOffset = index * itemHeight;

const color = useMemo(() => {
return interpolateColors(offset, {
inputRange: [
itemOffset - itemHeight,
itemOffset,
itemOffset + itemHeight
],
inputRange: [itemOffset - itemHeight, itemOffset, itemOffset + itemHeight],
outputColorRange: [inactiveColor, activeColor, inactiveColor]
});
}, [itemHeight]);


const containerStyle = useMemo(() => {
return [{height: itemHeight}, styles.container];
}, [itemHeight]);

return (
<AnimatedTouchableOpacity
activeOpacity={1}
style={{
height: itemHeight
}}
style={containerStyle}
key={index}
center
centerV
centerH={centerH}
right={!centerH}
onPress={selectItem}
// @ts-ignore reanimated2
index={index}
testID={testID}
>
{/* @ts-ignore reanimated2*/}
<AnimatedText text60R style={{color, ...style}}>
Expand All @@ -69,3 +71,9 @@ export default ({
</AnimatedTouchableOpacity>
);
};

const styles = StyleSheet.create({
container: {
minWidth: Spacings.s10
}
});
Loading