Skip to content

Commit 4800a1a

Browse files
committed
✨ make it possible to supply the MaskedView component to fade out on a transparent background
1 parent 66c0336 commit 4800a1a

File tree

5 files changed

+143
-101
lines changed

5 files changed

+143
-101
lines changed

src/components/DurationScroll/index.tsx

Lines changed: 132 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ const DurationScroll = forwardRef<DurationScrollRef, DurationScrollProps>(
3434
allowFontScaling = false,
3535
amLabel,
3636
Audio,
37-
bottomPickerGradientOverlayProps,
3837
clickSoundAsset,
3938
disableInfiniteScroll = false,
4039
FlatList = RNFlatList,
@@ -46,6 +45,7 @@ const DurationScroll = forwardRef<DurationScrollRef, DurationScrollProps>(
4645
label,
4746
limit,
4847
LinearGradient,
48+
MaskedView,
4949
maximumValue,
5050
onDurationChange,
5151
padNumbersWithZero = false,
@@ -57,7 +57,6 @@ const DurationScroll = forwardRef<DurationScrollRef, DurationScrollProps>(
5757
repeatNumbersNTimesNotExplicitlySet,
5858
styles,
5959
testID,
60-
topPickerGradientOverlayProps,
6160
} = props;
6261

6362
const numberOfItems = useMemo(() => {
@@ -495,6 +494,127 @@ const DurationScroll = forwardRef<DurationScrollRef, DurationScrollProps>(
495494
latestDuration: latestDuration,
496495
}));
497496

497+
const renderContent = useMemo(() => {
498+
return (
499+
<>
500+
<FlatList
501+
key={flatListRenderKey}
502+
ref={flatListRef}
503+
contentContainerStyle={
504+
styles.durationScrollFlatListContentContainer
505+
}
506+
data={numbersForFlatList}
507+
decelerationRate={0.88}
508+
getItemLayout={getItemLayout}
509+
initialScrollIndex={initialScrollIndex}
510+
keyExtractor={(_, index) => index.toString()}
511+
nestedScrollEnabled
512+
onMomentumScrollEnd={onMomentumScrollEnd}
513+
onScroll={onScroll}
514+
renderItem={renderItem}
515+
scrollEnabled={!isDisabled}
516+
scrollEventThrottle={16}
517+
showsVerticalScrollIndicator={false}
518+
snapToAlignment="start"
519+
// used in place of snapToInterval due to bug on Android
520+
snapToOffsets={[
521+
...Array(numbersForFlatList.length),
522+
].map((_, i) => i * styles.pickerItemContainer.height)}
523+
style={styles.durationScrollFlatList}
524+
testID="duration-scroll-flatlist"
525+
viewabilityConfigCallbackPairs={
526+
viewabilityConfigCallbackPairs
527+
}
528+
windowSize={numberOfItemsToShow}
529+
/>
530+
<View
531+
pointerEvents="none"
532+
style={styles.pickerLabelContainer}>
533+
{typeof label === "string" ? (
534+
<Text
535+
allowFontScaling={allowFontScaling}
536+
style={styles.pickerLabel}>
537+
{label}
538+
</Text>
539+
) : (
540+
label ?? null
541+
)}
542+
</View>
543+
</>
544+
);
545+
}, [
546+
FlatList,
547+
allowFontScaling,
548+
flatListRenderKey,
549+
getItemLayout,
550+
initialScrollIndex,
551+
isDisabled,
552+
label,
553+
numberOfItemsToShow,
554+
numbersForFlatList,
555+
onMomentumScrollEnd,
556+
onScroll,
557+
renderItem,
558+
styles.durationScrollFlatList,
559+
styles.durationScrollFlatListContentContainer,
560+
styles.pickerItemContainer.height,
561+
styles.pickerLabel,
562+
styles.pickerLabelContainer,
563+
viewabilityConfigCallbackPairs,
564+
]);
565+
566+
const renderLinearGradient = useMemo(() => {
567+
if (!LinearGradient) {
568+
return null;
569+
}
570+
571+
let colors: string[];
572+
573+
if (MaskedView) {
574+
// if using masked view, we only care about the opacity
575+
colors = [
576+
"rgba(0,0,0,0)",
577+
"rgba(0,0,0,1)",
578+
"rgba(0,0,0,1)",
579+
"rgba(0,0,0,0)",
580+
];
581+
} else {
582+
const backgroundColor =
583+
styles.pickerContainer.backgroundColor ?? "white";
584+
const transparentBackgroundColor = colorToRgba({
585+
color: backgroundColor,
586+
opacity: 0,
587+
});
588+
colors = [
589+
backgroundColor,
590+
transparentBackgroundColor,
591+
transparentBackgroundColor,
592+
backgroundColor,
593+
];
594+
}
595+
596+
// calculate the gradient height to cover the top item and bottom item
597+
const gradientHeight =
598+
padWithNItems > 0 ? 1 / (padWithNItems * 2 + 1) : 0.3;
599+
600+
return (
601+
<LinearGradient
602+
colors={colors}
603+
locations={[0, gradientHeight, 1 - gradientHeight, 1]}
604+
pointerEvents="none"
605+
style={styles.pickerGradientOverlay}
606+
{...pickerGradientOverlayProps}
607+
/>
608+
);
609+
}, [
610+
LinearGradient,
611+
MaskedView,
612+
padWithNItems,
613+
pickerGradientOverlayProps,
614+
styles.pickerContainer.backgroundColor,
615+
styles.pickerGradientOverlay,
616+
]);
617+
498618
return (
499619
<View
500620
pointerEvents={isDisabled ? "none" : undefined}
@@ -508,90 +628,18 @@ const DurationScroll = forwardRef<DurationScrollRef, DurationScrollProps>(
508628
isDisabled && styles.disabledPickerContainer,
509629
]}
510630
testID={testID}>
511-
<FlatList
512-
key={flatListRenderKey}
513-
ref={flatListRef}
514-
contentContainerStyle={
515-
styles.durationScrollFlatListContentContainer
516-
}
517-
data={numbersForFlatList}
518-
decelerationRate={0.88}
519-
getItemLayout={getItemLayout}
520-
initialScrollIndex={initialScrollIndex}
521-
keyExtractor={(_, index) => index.toString()}
522-
nestedScrollEnabled
523-
onMomentumScrollEnd={onMomentumScrollEnd}
524-
onScroll={onScroll}
525-
renderItem={renderItem}
526-
scrollEnabled={!isDisabled}
527-
scrollEventThrottle={16}
528-
showsVerticalScrollIndicator={false}
529-
snapToAlignment="start"
530-
// used in place of snapToInterval due to bug on Android
531-
snapToOffsets={[...Array(numbersForFlatList.length)].map(
532-
(_, i) => i * styles.pickerItemContainer.height
533-
)}
534-
style={styles.durationScrollFlatList}
535-
testID="duration-scroll-flatlist"
536-
viewabilityConfigCallbackPairs={
537-
viewabilityConfigCallbackPairs
538-
}
539-
windowSize={numberOfItemsToShow}
540-
/>
541-
<View pointerEvents="none" style={styles.pickerLabelContainer}>
542-
{typeof label === "string" ? (
543-
<Text
544-
allowFontScaling={allowFontScaling}
545-
style={styles.pickerLabel}>
546-
{label}
547-
</Text>
548-
) : (
549-
label ?? null
550-
)}
551-
</View>
552-
{LinearGradient ? (
631+
{MaskedView ? (
632+
<MaskedView
633+
maskElement={renderLinearGradient}
634+
style={[styles.maskedView]}>
635+
{renderContent}
636+
</MaskedView>
637+
) : (
553638
<>
554-
<LinearGradient
555-
colors={[
556-
styles.pickerContainer.backgroundColor ??
557-
"white",
558-
colorToRgba({
559-
color:
560-
styles.pickerContainer
561-
.backgroundColor ?? "white",
562-
opacity: 0,
563-
}),
564-
]}
565-
end={{ x: 1, y: 1 }}
566-
pointerEvents="none"
567-
start={{ x: 1, y: 0.3 }}
568-
{...pickerGradientOverlayProps}
569-
{...topPickerGradientOverlayProps}
570-
style={[styles.pickerGradientOverlay, { top: 0 }]}
571-
/>
572-
<LinearGradient
573-
colors={[
574-
colorToRgba({
575-
color:
576-
styles.pickerContainer
577-
.backgroundColor ?? "white",
578-
opacity: 0,
579-
}),
580-
styles.pickerContainer.backgroundColor ??
581-
"white",
582-
]}
583-
end={{ x: 1, y: 0.7 }}
584-
pointerEvents="none"
585-
start={{ x: 1, y: 0 }}
586-
{...pickerGradientOverlayProps}
587-
{...bottomPickerGradientOverlayProps}
588-
style={[
589-
styles.pickerGradientOverlay,
590-
{ bottom: -1 },
591-
]}
592-
/>
639+
{renderContent}
640+
{renderLinearGradient}
593641
</>
594-
) : null}
642+
)}
595643
</View>
596644
);
597645
}

src/components/DurationScroll/types.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ export interface DurationScrollProps {
2121
FlatList?: CustomFlatList;
2222
Haptics?: any;
2323
LinearGradient?: any;
24+
MaskedView?: any;
2425
aggressivelyGetLatestDuration: boolean;
2526
allowFontScaling?: boolean;
2627
amLabel?: string;
27-
bottomPickerGradientOverlayProps?: Partial<LinearGradientProps>;
2828
clickSoundAsset?: SoundAssetType;
2929
disableInfiniteScroll?: boolean;
3030
initialValue?: number;
@@ -44,7 +44,6 @@ export interface DurationScrollProps {
4444
repeatNumbersNTimesNotExplicitlySet: boolean;
4545
styles: ReturnType<typeof generateStyles>;
4646
testID?: string;
47-
topPickerGradientOverlayProps?: Partial<LinearGradientProps>;
4847
}
4948

5049
export interface DurationScrollRef {

src/components/TimerPicker/index.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -82,12 +82,9 @@ const TimerPicker = forwardRef<TimerPickerRef, TimerPickerProps>(
8282
);
8383

8484
const styles = useMemo(
85-
() =>
86-
generateStyles(customStyles, {
87-
padWithNItems: safePadWithNItems,
88-
}),
85+
() => generateStyles(customStyles),
8986

90-
[safePadWithNItems, customStyles]
87+
[customStyles]
9188
);
9289

9390
const [selectedHours, setSelectedHours] = useState(

src/components/TimerPicker/styles.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,10 @@ export interface CustomTimerPickerStyles {
2323
const DARK_MODE_BACKGROUND_COLOR = "#232323";
2424
const DARK_MODE_TEXT_COLOR = "#E9E9E9";
2525
const LIGHT_MODE_BACKGROUND_COLOR = "#F1F1F1";
26-
const LIGHT_MODE_TEXT_COLOR = "#1B1B1B";
26+
const LIGHT_MODE_TEXT_COLOR = "#1B1B1uB";
2727

2828
export const generateStyles = (
2929
customStyles: CustomTimerPickerStyles | undefined,
30-
options: { padWithNItems: number }
3130
) =>
3231
StyleSheet.create({
3332
pickerContainer: {
@@ -113,14 +112,13 @@ export const generateStyles = (
113112
opacity: 0.2,
114113
...customStyles?.disabledPickerItem,
115114
},
115+
maskedView: {
116+
flex: 1,
117+
},
116118
pickerGradientOverlay: {
117119
position: "absolute",
118-
left: 0,
119-
right: 0,
120-
height:
121-
options.padWithNItems === 0
122-
? "30%"
123-
: (customStyles?.pickerItemContainer?.height ?? 50) * 0.8,
120+
width: "100%",
121+
height: "100%",
124122
...customStyles?.pickerGradientOverlay,
125123
},
126124
durationScrollFlatList: {

src/components/TimerPicker/types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,11 @@ export interface TimerPickerProps {
3636
Haptics?: any;
3737
// eslint-disable-next-line @typescript-eslint/no-explicit-any
3838
LinearGradient?: any;
39+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
40+
MaskedView?: any;
3941
aggressivelyGetLatestDuration?: boolean;
4042
allowFontScaling?: boolean;
4143
amLabel?: string;
42-
bottomPickerGradientOverlayProps?: Partial<LinearGradientProps>;
4344
clickSoundAsset?: SoundAssetType;
4445
disableInfiniteScroll?: boolean;
4546
hideHours?: boolean;
@@ -82,6 +83,5 @@ export interface TimerPickerProps {
8283
secondLimit?: LimitType;
8384
secondsPickerIsDisabled?: boolean;
8485
styles?: CustomTimerPickerStyles;
85-
topPickerGradientOverlayProps?: Partial<LinearGradientProps>;
8686
use12HourPicker?: boolean;
8787
}

0 commit comments

Comments
 (0)