Skip to content
Open
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
180 changes: 117 additions & 63 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ interface SegmentedControlProps {
* Badge Text Styles
*/
badgeTextStyle?: TextStyle;
/**
* The segment component
*/
SegmentComponent?: React.ComponentType<SegmentProps>;
}

const defaultShadowStyle = {
Expand All @@ -95,49 +99,55 @@ const DEFAULT_SPRING_CONFIG = {
restDisplacementThreshold: 0.001,
};

const SegmentedControl: React.FC<SegmentedControlProps> = ({
segments,
currentIndex,
onChange,
badgeValues = [],
isRTL = false,
containerMargin = 0,
export interface SegmentProps {
/**
* The string value of the segment
*/
segment: string | null;
/**
* The selection state of this segment
*/
selected: boolean;
/**
* The selection state of this segment
*/
badgeValue: number | null;
/**
* Active Segment Text Style
*/
activeTextStyle?: TextStyle;
/**
* InActive Segment Text Style
*/
inactiveTextStyle?: TextStyle;
/**
* The moving Tile Container Styles
*/
tileStyle?: ViewStyle;
/**
* Active Badge Styles
*/
activeBadgeStyle?: ViewStyle;
/**
* Inactive Badge Styles
*/
inactiveBadgeStyle?: ViewStyle;
/**
* Badge Text Styles
*/
badgeTextStyle?: TextStyle;
}

const DefaultSegment: React.FC<SegmentProps> = ({
segment,
selected,
badgeValue,
activeTextStyle,
inactiveTextStyle,
segmentedControlWrapper,
pressableWrapper,
tileStyle,
activeBadgeStyle,
inactiveBadgeStyle,
badgeTextStyle,
}: SegmentedControlProps) => {
const width = widthPercentageToDP('100%') - containerMargin * 2;
const translateValue = width / segments.length;
const tabTranslateValue = useSharedValue(0);

// useCallBack with an empty array as input, which will call inner lambda only once and memoize the reference for future calls
const memoizedTabPressCallback = React.useCallback(
(index) => {
onChange(index);
},
[onChange]
);
useEffect(() => {
// If phone is set to RTL, make sure the animation does the correct transition.
const transitionMultiplier = isRTL ? -1 : 1;
tabTranslateValue.value = withSpring(
currentIndex * (translateValue * transitionMultiplier),
DEFAULT_SPRING_CONFIG
);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [currentIndex]);

const tabTranslateAnimatedStyles = useAnimatedStyle(() => {
return {
transform: [{ translateX: tabTranslateValue.value }],
};
});

}) => {
const finalisedActiveTextStyle: TextStyle = {
fontSize: 15,
fontWeight: '600',
Expand Down Expand Up @@ -177,6 +187,69 @@ const SegmentedControl: React.FC<SegmentedControlProps> = ({
...badgeTextStyle,
};

return (
<View style={styles.textWrapper}>
<Text
style={[
selected ? finalisedActiveTextStyle : finalisedInActiveTextStyle,
]}
>
{segment}
</Text>
{badgeValue && (
<View
style={[
styles.defaultBadgeContainerStyle,
selected ? finalisedActiveBadgeStyle : finalisedInActiveBadgeStyle,
]}
>
<Text style={finalisedBadgeTextStyle}>{badgeValue}</Text>
</View>
)}
</View>
);
};

const SegmentedControl: React.FC<SegmentedControlProps> = ({
segments,
currentIndex,
onChange,
isRTL = false,
containerMargin = 0,
segmentedControlWrapper,
pressableWrapper,
tileStyle,
SegmentComponent = DefaultSegment,
badgeValues = [],
...rest
}: SegmentedControlProps) => {
const width = widthPercentageToDP('100%') - containerMargin * 2;
const translateValue = width / segments.length;
const tabTranslateValue = useSharedValue(0);

// useCallBack with an empty array as input, which will call inner lambda only once and memoize the reference for future calls
const memoizedTabPressCallback = React.useCallback(
(index: number) => {
onChange(index);
},
[onChange]
);
useEffect(() => {
// If phone is set to RTL, make sure the animation does the correct transition.
const transitionMultiplier = isRTL ? -1 : 1;
tabTranslateValue.value = withSpring(
currentIndex * (translateValue * transitionMultiplier),
DEFAULT_SPRING_CONFIG
);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [currentIndex]);

const tabTranslateAnimatedStyles = useAnimatedStyle(() => {
return {
transform: [{ translateX: tabTranslateValue.value }],
};
});

return (
<Animated.View
style={[styles.defaultSegmentedControlWrapper, segmentedControlWrapper]}
Expand All @@ -200,31 +273,12 @@ const SegmentedControl: React.FC<SegmentedControlProps> = ({
key={index}
style={[styles.touchableContainer, pressableWrapper]}
>
<View style={styles.textWrapper}>
<Text
style={[
currentIndex === index
? finalisedActiveTextStyle
: finalisedInActiveTextStyle,
]}
>
{segment}
</Text>
{badgeValues[index] && (
<View
style={[
styles.defaultBadgeContainerStyle,
currentIndex === index
? finalisedActiveBadgeStyle
: finalisedInActiveBadgeStyle,
]}
>
<Text style={finalisedBadgeTextStyle}>
{badgeValues[index]}
</Text>
</View>
)}
</View>
<SegmentComponent
selected={index === currentIndex}
badgeValue={badgeValues[index]}
segment={segment}
{...rest}
/>
</Pressable>
);
})}
Expand Down