react-native#GestureResponderEvent TypeScript Examples
The following examples show how to use
react-native#GestureResponderEvent.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: icon.tsx From THUInfo with MIT License | 6 votes |
HomeIcon = ({
title,
onPress,
children,
}: {
title: keyof typeof zh;
onPress: (event: GestureResponderEvent) => void;
children: ReactElement;
}) => {
const themeName = useColorScheme();
const theme = themes(themeName);
return (
<TouchableOpacity
style={{
marginTop: 12,
alignItems: "center",
flexGrow: 0,
flexShrink: 0,
flexBasis: "20%",
}}
onPress={onPress}>
{children}
<Text
style={{color: theme.colors.text, marginTop: 8}}
ellipsizeMode="tail"
numberOfLines={1}>
{getStr(title)}
</Text>
</TouchableOpacity>
);
}
Example #2
Source File: useLinkProps.d.ts From nlw2-proffy with MIT License | 6 votes |
/**
* Hook to get props for an anchor tag so it can work with in page navigation.
*
* @param props.to Absolute path to screen (e.g. `/feeds/hot`).
* @param props.action Optional action to use for in-page navigation. By default, the path is parsed to an action based on linking config.
*/
export default function useLinkProps({ to, action }: Props): {
href: string;
accessibilityRole: "link";
onPress: (e: React.MouseEvent<HTMLAnchorElement, MouseEvent> | GestureResponderEvent) => void;
};
Example #3
Source File: CardButton.tsx From react-native-ios-context-menu with MIT License | 6 votes |
/**
* ```
* ┌─────────────────────────────┐
* │ Title │
* │ Subtitle │
* └─────────────────────────────┘
* ```
*/
export function CardButton(props: {
title: string;
subtitle: string;
onPress: (event: GestureResponderEvent) => void;
}){
return(
<TouchableOpacity
style={styles.cardButtonContainer}
onPress={props.onPress}
>
<React.Fragment>
<Text style={styles.cardButtonTitleText}>
{props.title}
</Text>
<Text style={styles.cardButtonSubtitleText}>
{props.subtitle}
</Text>
</React.Fragment>
</TouchableOpacity>
);
}
Example #4
Source File: feedback.tsx From THUInfo with MIT License | 6 votes |
BottomButton = ({
text,
onPress,
disabled,
}: {
text: keyof typeof zh;
onPress: (event: GestureResponderEvent) => void;
disabled: boolean;
}) => {
const dark = useColorScheme() === "dark";
return (
<TouchableOpacity
style={{
backgroundColor: dark
? disabled
? "#FFF4"
: "#ccc"
: disabled
? "#0000"
: "#0002",
flex: 1,
margin: 4,
borderRadius: 4,
}}
disabled={disabled}
onPress={(e) => !disabled && onPress(e)}>
<Text style={{textAlign: "center", padding: 10}}>{getStr(text)}</Text>
</TouchableOpacity>
);
}
Example #5
Source File: items.tsx From THUInfo with MIT License | 6 votes |
SettingsMiddleText = ({
text,
onPress,
}: {
text: string;
onPress: (event: GestureResponderEvent) => void;
}) => {
const themeName = useColorScheme();
const {colors} = themes(themeName);
const content = (
<View
style={{
padding: 8,
alignItems: "center",
}}>
<Text style={{color: colors.text}}>{text}</Text>
</View>
);
return Platform.OS === "ios" ? (
<TouchableHighlight underlayColor="#0002" onPress={onPress}>
{content}
</TouchableHighlight>
) : (
<TouchableNativeFeedback
background={TouchableNativeFeedback.Ripple("#0002", false)}
onPress={onPress}>
{content}
</TouchableNativeFeedback>
);
}
Example #6
Source File: gitlab.tsx From THUInfo with MIT License | 6 votes |
BranchItem = ({
name,
onPress,
}: {
name: string;
onPress: (event: GestureResponderEvent) => void;
}) => {
const themeName = useColorScheme();
const {colors} = themes(themeName);
const content = (
<View
style={{
padding: 8,
paddingRight: 16,
flexDirection: "row",
justifyContent: "space-between",
}}>
<Text style={{fontSize: 17, marginHorizontal: 10, color: colors.text}}>
{name}
</Text>
</View>
);
return Platform.OS === "ios" ? (
<TouchableHighlight underlayColor="#0002" onPress={onPress}>
{content}
</TouchableHighlight>
) : (
<TouchableNativeFeedback
background={TouchableNativeFeedback.Ripple("#0002", false)}
onPress={onPress}>
{content}
</TouchableNativeFeedback>
);
}
Example #7
Source File: Link.tsx From nlw2-proffy with MIT License | 6 votes |
/**
* Component to render link to another screen using a path.
* Uses an anchor tag on the web.
*
* @param props.to Absolute path to screen (e.g. `/feeds/hot`).
* @param props.action Optional action to use for in-page navigation. By default, the path is parsed to an action based on linking config.
* @param props.children Child elements to render the content.
*/
export default function Link({ to, action, ...rest }: Props) {
const props = useLinkProps({ to, action });
const onPress = (
e: React.MouseEvent<HTMLAnchorElement, MouseEvent> | GestureResponderEvent
) => {
if ('onPress' in rest) {
rest.onPress?.(e);
}
props.onPress(e);
};
return React.createElement(Text, {
...props,
...rest,
...Platform.select({
web: { onClick: onPress } as any,
default: { onPress },
}),
});
}
Example #8
Source File: gitlab.tsx From THUInfo with MIT License | 5 votes |
ProjectItem = ({
project,
onPress,
}: {
project: Project;
onPress: (event: GestureResponderEvent) => void;
}) => {
const themeName = useColorScheme();
const {colors} = themes(themeName);
const content = (
<View
style={{
padding: 8,
flexDirection: "row",
justifyContent: "space-between",
}}>
<View
style={{flexDirection: "column", flex: 3, alignItems: "flex-start"}}>
<Text style={{fontSize: 13, marginHorizontal: 10, color: "grey"}}>
{project.path_with_namespace}
</Text>
<Text style={{fontSize: 17, marginHorizontal: 10, color: colors.text}}>
{project.name}
</Text>
</View>
<View style={{flexDirection: "column", flex: 1, alignItems: "flex-end"}}>
<Text style={{fontSize: 14, marginHorizontal: 6, color: colors.text}}>
{getStr("gitlabLastUpdate")}
</Text>
<Text style={{fontSize: 14, marginHorizontal: 6, color: colors.text}}>
<TimeAgo time={project.last_activity_at} />
</Text>
</View>
</View>
);
return Platform.OS === "ios" ? (
<TouchableHighlight underlayColor="#0002" onPress={onPress}>
{content}
</TouchableHighlight>
) : (
<TouchableNativeFeedback
background={TouchableNativeFeedback.Ripple("#0002", false)}
onPress={onPress}>
{content}
</TouchableNativeFeedback>
);
}
Example #9
Source File: gitlab.tsx From THUInfo with MIT License | 5 votes |
FileItem = ({
file,
onPress,
onLongPress,
}: {
file: File;
onPress: (event: GestureResponderEvent) => void;
onLongPress?: (event: GestureResponderEvent) => void;
}) => {
const themeName = useColorScheme();
const {colors} = themes(themeName);
const content = (
<View
style={{
padding: 8,
flexDirection: "row",
justifyContent: "flex-start",
alignItems: "center",
}}>
<Feather name={file.type === "tree" ? "folder" : "code"} size={20} />
<Text style={{fontSize: 17, marginHorizontal: 10, color: colors.text}}>
{file.name}
</Text>
</View>
);
return Platform.OS === "ios" ? (
<TouchableHighlight
underlayColor="#0002"
onPress={onPress}
onLongPress={onLongPress}>
{content}
</TouchableHighlight>
) : (
<TouchableNativeFeedback
background={TouchableNativeFeedback.Ripple("#0002", false)}
onPress={onPress}
onLongPress={onLongPress}>
{content}
</TouchableNativeFeedback>
);
}
Example #10
Source File: Radio.tsx From react-native-design-kit with MIT License | 5 votes |
export default function Radio({
containerStyle,
radioIds,
radioComponent,
defaultId,
onSelect,
onPress,
...props
}: RadioProps) {
const [selected, setSelected] = useState(defaultId);
const handlePressRadioItem = useCallback(
(id: string, event: GestureResponderEvent) => {
onPress && onPress(event);
onSelect(id);
setSelected(id);
},
[onPress, onSelect],
);
const handleRenderRadioItem = useCallback(
(id: string) => {
const isSelected = selected === id;
const component = radioComponent && radioComponent({id, isSelected});
const title =
typeof component === 'string'
? component
: component === undefined
? id
: undefined;
return (
<RadioItem
{...props}
testID="radio-item"
key={id}
title={title}
isSelected={isSelected}
onPress={event => handlePressRadioItem(id, event)}>
{component !== undefined &&
typeof component !== 'string' &&
component}
</RadioItem>
);
},
[props, selected, radioComponent, handlePressRadioItem],
);
const handleRenderListRadioItem = useMemo(
() => radioIds.map(value => handleRenderRadioItem(value)),
[radioIds, handleRenderRadioItem],
);
return <View style={containerStyle}>{handleRenderListRadioItem}</View>;
}
Example #11
Source File: items.tsx From THUInfo with MIT License | 5 votes |
SettingsItem = ({
text,
onPress,
icon,
badge,
normalText,
}: {
text: string;
onPress: (event: GestureResponderEvent) => void;
icon: ReactElement | undefined;
badge?: string;
normalText?: boolean;
}) => {
const themeName = useColorScheme();
const {colors} = themes(themeName);
const content = (
<View
style={{
padding: 8,
paddingRight: 16,
flexDirection: "row",
justifyContent: "space-between",
}}>
<View style={{flexDirection: "row", alignItems: "center"}}>
{setIconWidth(icon, colors)}
<Text
style={
normalText
? {color: colors.text}
: {fontSize: 17, marginHorizontal: 10, color: colors.text}
}
numberOfLines={1}>
{text}
{badge && <Text style={{color: "red", fontSize: 12}}>[{badge}]</Text>}
</Text>
</View>
<Icon name="angle-right" size={24} color="lightgrey" />
</View>
);
return Platform.OS === "ios" ? (
<TouchableHighlight underlayColor="#0002" onPress={onPress}>
{content}
</TouchableHighlight>
) : (
<TouchableNativeFeedback
background={TouchableNativeFeedback.Ripple("#0002", false)}
onPress={onPress}>
{content}
</TouchableNativeFeedback>
);
}
Example #12
Source File: items.tsx From THUInfo with MIT License | 5 votes |
SettingsDoubleText = ({
textLeft,
textRight,
onPress,
icon,
}: {
textLeft: string;
textRight: string;
onPress: ((event: GestureResponderEvent) => void) | undefined;
icon: ReactElement | undefined;
}) => {
const themeName = useColorScheme();
const {colors} = themes(themeName);
const content = (
<View
style={{
padding: 8,
paddingRight: 16,
flexDirection: "row",
justifyContent: "space-between",
}}>
<View style={{flexDirection: "row", alignItems: "center"}}>
{setIconWidth(icon, colors)}
<Text style={{fontSize: 17, marginHorizontal: 10, color: colors.text}}>
{textLeft}
</Text>
</View>
<Text style={{fontSize: 17, marginHorizontal: 10, color: colors.text}}>
{textRight}
</Text>
</View>
);
return onPress === undefined ? (
content
) : Platform.OS === "ios" ? (
<TouchableHighlight underlayColor="#0002" onPress={onPress}>
{content}
</TouchableHighlight>
) : (
<TouchableNativeFeedback
background={TouchableNativeFeedback.Ripple("#0002", false)}
onPress={onPress}>
{content}
</TouchableNativeFeedback>
);
}
Example #13
Source File: Modal.tsx From react-native-design-kit with MIT License | 5 votes |
export default function Modal({
containerStyle,
hasBackdrop = true,
backdropContainerStyle,
onPressBackdrop,
visible = false,
transparent,
children,
...props
}: ModalProps) {
const [toggle, setToggle] = useState(visible);
const handlePressBackdrop = useCallback(
(event: GestureResponderEvent) => {
onPressBackdrop && onPressBackdrop(event);
setToggle(!toggle);
},
[toggle, onPressBackdrop],
);
const handleRenderBackdrop = useMemo(
() =>
hasBackdrop && (
<Touchable
testID="backdrop"
touchableType="normal"
onPress={handlePressBackdrop}>
<View
style={StyleSheet.flatten([
!transparent &&
StyleSheet.flatten([
styles.backdropContainer,
backdropContainerStyle,
]),
styles.fixedBackdropContainer,
])}
/>
</Touchable>
),
[hasBackdrop, transparent, backdropContainerStyle, handlePressBackdrop],
);
useDidUpdate(() => {
setToggle(visible);
}, [visible]);
return (
<ModalRN {...props} transparent visible={toggle}>
<View style={StyleSheet.flatten([styles.container, containerStyle])}>
{handleRenderBackdrop}
{children}
</View>
</ModalRN>
);
}
Example #14
Source File: items.tsx From THUInfo with MIT License | 5 votes |
SettingsLargeButton = ({
text,
onPress,
disabled,
redText,
}: {
text: string;
onPress: (event: GestureResponderEvent) => void;
disabled: boolean;
redText: boolean;
}) => {
const themeName = useColorScheme();
const {colors} = themes(themeName);
return (
<TouchableOpacity
style={{
backgroundColor:
themeName === "dark"
? disabled
? "#FFF4"
: "#ccc4"
: disabled
? "#0000"
: "#0002",
marginHorizontal: 20,
borderRadius: 4,
justifyContent: "center",
}}
disabled={disabled}
onPress={(e) => !disabled && onPress(e)}>
<Text
style={{
textAlign: "center",
padding: 12,
fontSize: 20,
color: redText ? "red" : colors.text,
}}>
{text}
</Text>
</TouchableOpacity>
);
}
Example #15
Source File: invoice.tsx From THUInfo with MIT License | 5 votes |
InvoiceItem = ({
invoice,
onPress,
}: {
invoice: Invoice;
onPress: (event: GestureResponderEvent) => void;
}) => {
const themeName = useColorScheme();
const {colors} = themes(themeName);
const content = (
<View style={{padding: 8}}>
<View
style={{
flexDirection: "row",
justifyContent: "space-between",
}}>
<View
style={{flexDirection: "column", flex: 3, alignItems: "flex-start"}}>
<Text style={{fontSize: 13, marginHorizontal: 10, color: "grey"}}>
{invoice.inv_no}
</Text>
<Text
style={{fontSize: 17, marginHorizontal: 10, color: colors.text}}>
{invoice.financial_item_name}
</Text>
<Text style={{fontSize: 14, marginHorizontal: 10, color: "grey"}}>
{invoice.financial_dept_name}/{invoice.payment_item_type_name}
</Text>
</View>
<View
style={{flexDirection: "column", flex: 1, alignItems: "flex-end"}}>
<Text style={{fontSize: 18, marginHorizontal: 6, color: colors.text}}>
{invoice.inv_amount}
</Text>
<Text style={{fontSize: 12, marginHorizontal: 6, color: "grey"}}>
{invoice.inv_date}
</Text>
</View>
</View>
<Text style={{fontSize: 14, marginHorizontal: 10, color: "grey"}}>
{invoice.inv_note}
</Text>
</View>
);
return Platform.OS === "ios" ? (
<TouchableHighlight underlayColor="#0002" onPress={onPress}>
{content}
</TouchableHighlight>
) : (
<TouchableNativeFeedback
onPress={onPress}
background={TouchableNativeFeedback.Ripple("#0002", false)}>
{content}
</TouchableNativeFeedback>
);
}
Example #16
Source File: reservesLibWelcome.tsx From THUInfo with MIT License | 5 votes |
BookItem = ({
book,
onPress,
}: {
book: SearchResultItem;
onPress: (event: GestureResponderEvent) => void;
}) => {
const themeName = useColorScheme();
const {colors} = themes(themeName);
const content = (
<View
style={{
padding: 8,
flexDirection: "row",
justifyContent: "space-between",
}}>
<View
style={{flexDirection: "column", flex: 3, alignItems: "flex-start"}}>
<Text style={{fontSize: 13, marginHorizontal: 10, color: "grey"}}>
{book.author}
</Text>
<Text style={{fontSize: 17, marginHorizontal: 10, color: colors.text}}>
{book.title}
</Text>
</View>
<View style={{flexDirection: "column", flex: 1, alignItems: "flex-end"}}>
<Text style={{fontSize: 14, marginHorizontal: 6, color: colors.text}}>
{book.publisher}
</Text>
</View>
</View>
);
return Platform.OS === "ios" ? (
<TouchableHighlight underlayColor="#0002" onPress={onPress}>
{content}
</TouchableHighlight>
) : (
<TouchableNativeFeedback
background={TouchableNativeFeedback.Ripple("#0002", false)}
onPress={onPress}>
{content}
</TouchableNativeFeedback>
);
}
Example #17
Source File: useLinkProps.tsx From nlw2-proffy with MIT License | 5 votes |
/**
* Hook to get props for an anchor tag so it can work with in page navigation.
*
* @param props.to Absolute path to screen (e.g. `/feeds/hot`).
* @param props.action Optional action to use for in-page navigation. By default, the path is parsed to an action based on linking config.
*/
export default function useLinkProps({ to, action }: Props) {
const navigation = React.useContext(NavigationHelpersContext);
const linkTo = useLinkTo();
const onPress = (
e: React.MouseEvent<HTMLAnchorElement, MouseEvent> | GestureResponderEvent
) => {
let shouldHandle = false;
if (Platform.OS !== 'web' || !e) {
shouldHandle = e ? !e.defaultPrevented : true;
} else if (
!e.defaultPrevented && // onPress prevented default
// @ts-expect-error: these properties exist on web, but not in React Native
!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey) && // ignore clicks with modifier keys
// @ts-expect-error: these properties exist on web, but not in React Native
(e.button == null || e.button === 0) && // ignore everything but left clicks
// @ts-expect-error: these properties exist on web, but not in React Native
[undefined, null, '', 'self'].includes(e.currentTarget?.target) // let browser handle "target=_blank" etc.
) {
e.preventDefault();
shouldHandle = true;
}
if (shouldHandle) {
if (action) {
if (navigation) {
navigation.dispatch(action);
} else {
throw new Error("Couldn't find a navigation object.");
}
} else {
if (typeof to !== 'string') {
throw new Error(
`To 'to' option is invalid (found '${String(
to
)}'. It must be a valid string for navigation.`
);
}
linkTo(to);
}
}
};
return {
href: to,
accessibilityRole: 'link' as const,
onPress,
};
}
Example #18
Source File: ScalingButton.tsx From nyxo-app with GNU General Public License v3.0 | 5 votes |
ScalingButton: FC<Props> = ({
analyticsEvent,
children,
onPress,
disabled
}) => {
const scaleIn = useRef(new Animated.Value(0)).current
const pressIn = () => {
scaleIn.setValue(0)
Animated.timing(scaleIn, {
toValue: 1,
duration: 150,
useNativeDriver: true
}).start()
}
const pressOut = () => {
scaleIn.setValue(1)
Animated.timing(scaleIn, {
toValue: 0,
duration: 150,
useNativeDriver: true
}).start()
}
const handlePress = (event: GestureResponderEvent) => {
Analytics.trackEvent('Button pressed', {
buttonType: analyticsEvent
})
ReactNativeHapticFeedback.trigger('impactMedium', {
enableVibrateFallback: true
})
onPress(event)
}
const transform = (animated: Animated.Value) => {
const interpolation = animated.interpolate({
inputRange: [0, 1],
outputRange: [1, 0.95]
})
return {
transform: [{ scale: interpolation }]
}
}
return (
<TouchableWithoutFeedback
onPress={handlePress}
disabled={disabled}
onPressIn={pressIn}
onPressOut={pressOut}>
<Button style={transform(scaleIn)}>{children}</Button>
</TouchableWithoutFeedback>
)
}
Example #19
Source File: AnalogClock.tsx From react-native-paper-dates with MIT License | 4 votes |
function AnalogClock({
hours,
minutes,
focused,
is24Hour,
onChange,
}: {
hours: number
minutes: number
focused: PossibleClockTypes
is24Hour: boolean
onChange: (hoursMinutesAndFocused: {
hours: number
minutes: number
focused?: undefined | PossibleClockTypes
}) => any
}) {
const theme = useTheme()
const { mode } = React.useContext(DisplayModeContext)
// used to make pointer shorter if hours are selected and above 12
const shortPointer = (hours === 0 || hours > 12) && is24Hour
const clockRef = React.useRef<View | null>(null)
// Hooks are nice, sometimes... :-)..
// We need the latest values, since the onPointerMove uses a closure to the function
const hoursRef = useLatest(hours)
const onChangeRef = useLatest(onChange)
const minutesRef = useLatest(minutes)
const focusedRef = useLatest(focused)
const is24HourRef = useLatest(is24Hour)
const modeRef = useLatest(mode)
const onPointerMove = React.useCallback(
(e: GestureResponderEvent, final: boolean) => {
let x = e.nativeEvent.locationX
let y = e.nativeEvent.locationY
let angle = getAngle(x, y, circleSize)
if (focusedRef.current === clockTypes.hours) {
let hours24 = is24HourRef.current
let previousHourType = getHourType(hoursRef.current)
let pickedHours = getHours(angle, previousHourType)
let hours12AndPm = !hours24 && modeRef.current === 'AM'
let hourTypeFromOffset = getHourTypeFromOffset(x, y, circleSize)
let hours24AndPM = hours24 && hourTypeFromOffset === hourTypes.pm
// Avoiding the "24h"
// Should be 12h for 12 hours and PM mode
if (hours12AndPm || hours24AndPM) {
pickedHours += 12
}
if (modeRef.current === 'AM' && pickedHours === 12) {
pickedHours = 0
}
if (
(!hours24 && modeRef.current === 'AM' && pickedHours === 12) ||
pickedHours === 24
) {
pickedHours = 0
}
if (hoursRef.current !== pickedHours || final) {
onChangeRef.current({
hours: pickedHours,
minutes: minutesRef.current,
focused: final ? clockTypes.minutes : undefined,
})
}
} else if (focusedRef.current === clockTypes.minutes) {
let pickedMinutes = getMinutes(angle)
if (minutesRef.current !== pickedMinutes) {
onChangeRef.current({
hours: hoursRef.current,
minutes: pickedMinutes,
})
}
}
},
[focusedRef, is24HourRef, hoursRef, onChangeRef, minutesRef, modeRef]
)
const panResponder = React.useRef(
PanResponder.create({
onPanResponderGrant: (e) => onPointerMove(e, false),
onPanResponderMove: (e) => onPointerMove(e, false),
onPanResponderRelease: (e) => onPointerMove(e, true),
onStartShouldSetPanResponder: returnTrue,
onStartShouldSetPanResponderCapture: () => false,
onMoveShouldSetPanResponder: returnTrue,
onMoveShouldSetPanResponderCapture: returnTrue,
onPanResponderTerminationRequest: returnTrue,
onShouldBlockNativeResponder: returnTrue,
})
).current
const dynamicSize = focused === clockTypes.hours && shortPointer ? 33 : 0
const pointerNumber = focused === clockTypes.hours ? hours : minutes
const degreesPerNumber = focused === clockTypes.hours ? 30 : 6
return (
<View
ref={clockRef}
{...panResponder.panHandlers}
style={[
styles.clock,
{
backgroundColor: theme.dark
? Color(theme.colors.surface).lighten(1.2).hex()
: Color(theme.colors.surface).darken(0.1).hex(),
},
]}
// @ts-ignore -> https://github.com/necolas/react-native-web/issues/506
cursor={'pointer'}
>
<View
style={[
styles.line,
{
backgroundColor: theme.colors.primary,
transform: [
{ rotate: -90 + pointerNumber * degreesPerNumber + 'deg' },
{
translateX: circleSize / 4 - 4 - dynamicSize / 2,
},
],
width: circleSize / 2 - 4 - dynamicSize,
},
]}
pointerEvents="none"
>
<View
style={[styles.endPoint, { backgroundColor: theme.colors.primary }]}
/>
</View>
<View
style={[StyleSheet.absoluteFill, styles.center]}
pointerEvents="none"
>
<View
style={[
styles.middlePoint,
{
backgroundColor: theme.colors.primary,
},
]}
/>
</View>
<AnimatedClockSwitcher
focused={focused}
hours={<AnalogClockHours is24Hour={is24Hour} hours={hours} />}
minutes={<AnalogClockMinutes minutes={minutes} />}
/>
</View>
)
}
Example #20
Source File: Ripple.tsx From mobile with Apache License 2.0 | 4 votes |
Ripple = ({
children,
disabled,
rippleColor = 'rgb(0, 0, 0)',
rippleCentered = false,
rippleOpacity = 0.3,
rippleExpandDuration = 500,
rippleFadeDuration = 200,
rippleContainerBorderRadius = 0,
rippleSize = 0,
...touchableWithoutFeedbackProps
}: RippleProps) => {
const [width, setWidth] = useState(0);
const [height, setHeight] = useState(0);
const uuid = useRef(0);
const [ripples, setRipples] = useState<AnimatedRipple[]>([]);
const [fadings, setFadings] = useState<number[]>([]);
const startFade = useCallback(
(ripple: AnimatedRipple, duration: number) => {
if (fadings.indexOf(ripple.id) >= 0) {
return;
}
setFadings([...fadings, ripple.id]);
Animated.timing(ripple.fadeAnimatedValue, {
toValue: 1,
easing: Easing.out(Easing.ease),
duration,
useNativeDriver: true,
}).start(() => {
setRipples(ripples.filter(item => item !== ripple));
});
},
[fadings, ripples],
);
const startExpand = useCallback(
(event: GestureResponderEvent) => {
if (!width || !height) {
return;
}
const timestamp = Date.now();
if (ripples.length > 0 && timestamp < ripples[ripples.length - 1].timestamp + DEBOUNCE) {
return;
}
const w2 = 0.5 * width;
const h2 = 0.5 * height;
const {locationX, locationY} = rippleCentered ? {locationX: w2, locationY: h2} : event.nativeEvent;
const offsetX = Math.abs(w2 - locationX);
const offsetY = Math.abs(h2 - locationY);
const radius = rippleSize > 0 ? 0.5 * rippleSize : Math.sqrt((w2 + offsetX) ** 2 + (h2 + offsetY) ** 2);
const id = uuid.current;
uuid.current += 1;
const ripple: AnimatedRipple = {
id,
locationX,
locationY,
radius,
timestamp,
expandAnimatedValue: new Animated.Value(0),
fadeAnimatedValue: new Animated.Value(0),
};
Animated.timing(ripple.expandAnimatedValue, {
toValue: 1,
easing: Easing.out(Easing.ease),
duration: rippleExpandDuration,
useNativeDriver: true,
}).start();
setRipples(ripples.concat(ripple));
},
[height, rippleCentered, rippleExpandDuration, rippleSize, ripples, width],
);
const onLayout = useCallback(
(event: LayoutChangeEvent) => {
const {
nativeEvent: {
layout: {height, width},
},
} = event;
setWidth(width);
setHeight(height);
touchableWithoutFeedbackProps.onLayout?.(event);
},
[touchableWithoutFeedbackProps.onLayout],
);
const onPressIn = useCallback(
(event: GestureResponderEvent) => {
startExpand(event);
touchableWithoutFeedbackProps.onPressIn?.(event);
},
[startExpand, touchableWithoutFeedbackProps.onPressIn],
);
const onPressOut = useCallback(
(event: GestureResponderEvent) => {
ripples.forEach(ripple => startFade(ripple, rippleFadeDuration + rippleExpandDuration / 2));
touchableWithoutFeedbackProps.onPressOut?.(event);
},
[rippleExpandDuration, rippleFadeDuration, ripples, startFade, touchableWithoutFeedbackProps.onPressOut],
);
const onPress = useCallback(
(event: GestureResponderEvent) => {
requestAnimationFrame(() => {
touchableWithoutFeedbackProps.onPress?.(event);
});
},
[touchableWithoutFeedbackProps.onPress],
);
const renderRipple = useCallback(
({locationX, locationY, radius, id, expandAnimatedValue, fadeAnimatedValue}: AnimatedRipple) => {
const rippleStyle = {
top: locationY - RADIUS,
[I18nManager.isRTL ? 'right' : 'left']: locationX - RADIUS,
backgroundColor: rippleColor,
transform: [
{
scale: expandAnimatedValue.interpolate({
inputRange: [0, 1],
outputRange: [0.5 / RADIUS, radius / RADIUS],
}),
},
],
opacity: fadeAnimatedValue.interpolate({
inputRange: [0, 1],
outputRange: [rippleOpacity, 0],
}),
};
return <Animated.View style={[styles.ripple, rippleStyle]} key={id} />;
},
[rippleColor, rippleOpacity],
);
const style = useMemo(
() => [
styles.container,
{
borderRadius: rippleContainerBorderRadius,
},
],
[rippleContainerBorderRadius],
);
return (
<TouchableWithoutFeedback
{...touchableWithoutFeedbackProps}
disabled={disabled}
onPressIn={onPressIn}
onPressOut={onPressOut}
onPress={onPress}
onLayout={onLayout}
>
<Animated.View pointerEvents="box-only">
<View style={style}>{ripples.map(renderRipple)}</View>
{children}
</Animated.View>
</TouchableWithoutFeedback>
);
}
Example #21
Source File: Slider.tsx From react-native-design-kit with MIT License | 4 votes |
export default function Slider({
containerStyle,
minValue = 0,
minTrackContainerStyle,
maxValue = 100,
maxTrackContainerStyle,
initialValue,
button,
buttonValue,
startButton,
startButtonContainerStyle,
endButton,
endButtonContainerStyle,
thumb,
thumbContainerStyle,
trackContainerStyle,
hitSlop = {top: 10, bottom: 10},
indicator,
indicatorStyle,
indicatorComponent,
indicatorSubStyle,
indicatorSubComponent,
indicatorContainerStyle,
numberOfSection = 10,
numberOfSubSection = 2,
onChangeValue,
}: SliderProps) {
const [progress, setProgress] = useState(
(initialValue !== undefined && getProgress(initialValue)) || 0.25,
);
const [startProgress, setStartProgress] = useState(progress);
const [thumbLayout, setThumbLayout] = useState<LayoutRectangle>();
const [pageX, setPageX] = useState<number>();
const [width, setWidth] = useState<number>();
const value = useMemo(() => progress * (maxValue - minValue) + minValue, [
progress,
maxValue,
minValue,
]);
function setValue(val: number) {
setProgress(getProgress(val));
}
function getProgress(val: number) {
return (
(Math.max(minValue, Math.min(maxValue, val)) - minValue) /
(maxValue - minValue)
);
}
const handleRenderIndicator = useMemo(() => {
const components: ReactElement[] = [];
for (let index = 0; index <= numberOfSection; index++) {
components.push(
<View key={`{indicatorSection: ${index}}`}>
{indicatorComponent || (
<View
style={StyleSheet.flatten([styles.indicator, indicatorStyle])}
/>
)}
</View>,
);
if (index < numberOfSection) {
for (let indexSub = 0; indexSub < numberOfSubSection - 1; indexSub++) {
components.push(
<View key={`{indicator: ${index}, sub: ${indexSub}}`}>
{indicatorSubComponent || (
<View
style={StyleSheet.flatten([
styles.indicator,
styles.indicatorSub,
indicatorSubStyle,
])}
/>
)}
</View>,
);
}
}
}
return components;
}, [
numberOfSection,
numberOfSubSection,
indicatorComponent,
indicatorSubComponent,
indicatorStyle,
indicatorSubStyle,
]);
const handlePressButtonLeft = useCallback(
() =>
setValue(
value -
(buttonValue !== undefined
? Math.max(0, buttonValue)
: (maxValue - minValue) * 0.15),
),
[value, buttonValue, maxValue, minValue],
);
const handlePressButtonRight = useCallback(
() =>
setValue(
value +
(buttonValue !== undefined
? Math.max(0, buttonValue)
: (maxValue - minValue) * 0.15),
),
[value, buttonValue, maxValue, minValue],
);
const handleResponderStart = useCallback(
(event: GestureResponderEvent) => {
if (width !== undefined) {
setPageX(event.nativeEvent.pageX);
setStartProgress(event.nativeEvent.locationX / width);
}
},
[width, progress],
);
const handleResponderMove = useCallback(
(event: GestureResponderEvent) => {
if (pageX !== undefined && width !== undefined) {
setProgress(
Math.max(
0,
Math.min(
1,
startProgress + (event.nativeEvent.pageX - pageX) / width,
),
),
);
}
},
[pageX, width],
);
const handleRenderButtonLeft = useMemo(
() =>
button && (
<Touchable
testID="button-start"
style={StyleSheet.flatten([
styles.startButtonContainer,
startButtonContainerStyle,
])}
onPress={handlePressButtonLeft}>
{startButton || <Icon style={styles.buttonIcon} name="caret-left" />}
</Touchable>
),
[button, startButtonContainerStyle, startButton, handlePressButtonLeft],
);
const handleRenderButtonRight = useMemo(
() =>
button && (
<Touchable
testID="button-end"
style={StyleSheet.flatten([
styles.endButtonContainer,
endButtonContainerStyle,
])}
onPress={handlePressButtonRight}>
{endButton || <Icon style={styles.buttonIcon} name="caret-right" />}
</Touchable>
),
[button, endButtonContainerStyle, endButton, handlePressButtonRight],
);
const handleRenderTopIndicator = useMemo(
() =>
indicator && (
<View
style={StyleSheet.flatten([
indicatorContainerStyle,
styles.sectionIndicator,
styles.sectionIndicatorTop,
])}>
{handleRenderIndicator}
</View>
),
[indicator, indicatorContainerStyle, handleRenderIndicator],
);
const handleRenderBottomIndicator = useMemo(
() =>
indicator && (
<View
style={StyleSheet.flatten([
indicatorContainerStyle,
styles.sectionIndicator,
styles.sectionIndicatorBottom,
])}>
{handleRenderIndicator}
</View>
),
[indicator, indicatorContainerStyle, handleRenderIndicator],
);
const handleRenderThumb = useMemo(
() =>
width ? (
<View
testID="thumb-container"
onLayout={event => setThumbLayout(event.nativeEvent.layout)}
style={StyleSheet.flatten([
styles.thumbContainer,
thumbContainerStyle,
styles.sectionThumb,
{
left:
progress * width - (thumbLayout ? thumbLayout.width / 2 : 0),
opacity: thumbLayout ? 1 : 0,
},
])}>
{thumb || <View style={styles.thumb} />}
</View>
) : null,
[thumb, thumbContainerStyle, width, progress, width, thumbLayout],
);
const handleRenderSlider = useMemo(
() => (
<View
testID="track-container"
style={styles.sectionTrackContainer}
pointerEvents="box-only"
onStartShouldSetResponder={() => true}
onResponderStart={handleResponderStart}
onResponderMove={handleResponderMove}
hitSlop={hitSlop}
onLayout={event => setWidth(event.nativeEvent.layout.width)}>
{handleRenderTopIndicator}
<View style={styles.sectionTrack}>
<View
style={StyleSheet.flatten([
styles.trackContainer,
trackContainerStyle,
styles.trackContainerMin,
minTrackContainerStyle,
{width: `${progress * 100}%`},
])}
/>
<View
style={StyleSheet.flatten([
styles.trackContainer,
trackContainerStyle,
styles.trackContainerMax,
maxTrackContainerStyle,
{width: `${(1 - progress) * 100}%`},
])}
/>
{handleRenderThumb}
</View>
{handleRenderBottomIndicator}
</View>
),
[
trackContainerStyle,
trackContainerStyle,
minTrackContainerStyle,
maxTrackContainerStyle,
progress,
handleRenderThumb,
handleRenderTopIndicator,
handleRenderBottomIndicator,
handleResponderStart,
handleResponderMove,
],
);
useDidUpdate(() => {
onChangeValue && onChangeValue(value, progress);
}, [value]);
return (
<View style={StyleSheet.flatten([styles.container, containerStyle])}>
{handleRenderButtonLeft}
{handleRenderSlider}
{handleRenderButtonRight}
</View>
);
}
Example #22
Source File: Chip.tsx From react-native-design-kit with MIT License | 4 votes |
export default function Chip({
actionType = 'chip',
containerStyle,
chips,
chipContainerStyle,
chipComponent,
chipTitleStyle,
selectedChipContainerStyle,
selectedChipTitleStyle,
horizontal,
horizontalScrollIndicator = false,
horizontalScrollEnabled = true,
horizontalScrollButton = true,
horizontalScrollLeftButton,
horizontalScrollLeftButtonContainerStyle,
horizontalScrollRightButton,
horizontalScrollRightButtonContainerStyle,
selectedId,
leftIcon,
leftIconAction,
rightIcon,
rightIconAction,
onSelect,
onPress,
...props
}: ChipProps) {
const singleValue = useMemo(() => actionType === 'radio', [actionType]);
const [chipIds, setChipIds] = useState(chips);
const [layout, setLayout] = useState<LayoutRectangle>();
const [size, setSize] = useState<NativeScrollPoint>();
const [offset, setOffset] = useState<NativeScrollPoint>({x: 0, y: 0});
const [selected, setSelected] = useState<string[]>(
filterSelectList(chipIds, selectedId || [], singleValue),
);
const refScroll = useRef<FlatList<string>>();
const initialize = useRef(false);
const difSize = useMemo(
() => (layout && size ? size.x - layout.width : undefined),
[layout],
);
const allowScrollLeft = useMemo(() => offset !== undefined && offset.x > 0, [
offset,
]);
const allowScrollRight = useMemo(
() => difSize !== undefined && offset.x < difSize,
[difSize, offset],
);
const isSelected = useCallback((id: string) => selected.indexOf(id) >= 0, [
selected,
]);
const removeChipId = useCallback(
(id: string) => setChipIds(chipIds.filter(chipId => chipId !== id)),
[chipIds],
);
const getIconAction = useCallback(
(id: string, iconActionFunction?: ChipIconAction) => {
if (iconActionFunction) {
const action = iconActionFunction(id, isSelected(id));
if (action === 'delete') {
return () => removeChipId(id);
} else if (action === 'check') {
return undefined;
}
return action;
}
return undefined;
},
[isSelected],
);
const handleRefList = useCallback((instance: FlatList<string>) => {
if (instance) {
refScroll.current = instance;
}
}, []);
const handlePressChipItem = useCallback(
(id: string, event: GestureResponderEvent) => {
onPress && onPress(event);
if (actionType !== 'chip') {
if (actionType === 'checkbox') {
const selection = [...selected];
if (isSelected(id)) {
selection.splice(selection.indexOf(id), 1);
} else {
selection.push(id);
}
setSelected(selection);
onSelect(id, selection);
} else {
const selection = [id];
setSelected([id]);
onSelect(id, selection);
}
} else {
onSelect(id, selected);
}
},
[actionType, selected, isSelected, onPress, onSelect],
);
const handlePressScrollLeftButton = useCallback(
() =>
refScroll.current &&
refScroll.current.scrollToOffset({
offset: Math.max(0, offset.x - 125),
animated: true,
}),
[refScroll.current, offset],
);
const handlePressScrollRightButton = useCallback(
() =>
refScroll.current &&
difSize &&
refScroll.current.scrollToOffset({
offset: Math.min(difSize, offset.x + 125),
animated: true,
}),
[refScroll.current, difSize, offset],
);
const handleRenderIcon = useCallback(
(
id: string,
iconFunction?: ChipIcon,
iconActionFunction?: ChipIconAction,
) => {
if (iconFunction) {
return iconFunction({id, isSelected: isSelected(id)});
}
if (iconActionFunction) {
const action = iconActionFunction(id, isSelected(id));
if (action === 'delete') {
return (
<Icon
testID="icon-delete"
style={styles.icon}
name="times-circle"
/>
);
} else if (action === 'check' && isSelected(id)) {
return (
<Icon
style={StyleSheet.flatten([styles.icon, styles.iconCheck])}
name="check"
/>
);
}
}
return undefined;
},
[isSelected],
);
const handleRenderChipItem = useCallback(
(id: string) => {
const component =
chipComponent && chipComponent({id, isSelected: isSelected(id)});
const title =
typeof component === 'string'
? component
: component === undefined
? id
: undefined;
return (
<ChipItem
{...props}
key={id}
containerStyle={StyleSheet.flatten([
typeof chipContainerStyle === 'function'
? chipContainerStyle(id)
: chipContainerStyle,
isSelected(id)
? StyleSheet.flatten([
styles.selectedChipContainer,
typeof selectedChipContainerStyle === 'function'
? selectedChipContainerStyle(id)
: selectedChipContainerStyle,
])
: {},
])}
title={title}
titleStyle={StyleSheet.flatten([
typeof chipTitleStyle === 'function'
? chipTitleStyle(id)
: chipTitleStyle,
isSelected(id)
? typeof selectedChipTitleStyle === 'function'
? selectedChipTitleStyle(id)
: selectedChipTitleStyle
: {},
])}
leftIcon={handleRenderIcon(id, leftIcon, leftIconAction)}
leftIconAction={getIconAction(id, leftIconAction)}
rightIcon={handleRenderIcon(id, rightIcon, rightIconAction)}
rightIconAction={getIconAction(id, rightIconAction)}
onPress={event => handlePressChipItem(id, event)}>
{component !== undefined &&
typeof component !== 'string' &&
component}
</ChipItem>
);
},
[
props,
chipTitleStyle,
chipContainerStyle,
selectedChipTitleStyle,
selectedChipContainerStyle,
leftIcon,
leftIconAction,
rightIcon,
rightIconAction,
chipComponent,
isSelected,
handleRenderIcon,
handlePressChipItem,
],
);
const handleRenderScrollLeftButton = useMemo(
() =>
horizontalScrollButton && (
<Touchable
testID="button-left"
disabled={!allowScrollLeft}
style={StyleSheet.flatten([
styles.scrollContainer,
styles.scrollLeftIconContainer,
horizontalScrollLeftButtonContainerStyle,
!allowScrollLeft ? styles.scrollContainerDisabled : {},
])}
onPress={handlePressScrollLeftButton}>
{horizontalScrollLeftButton || <Icon name="chevron-left" />}
</Touchable>
),
[
allowScrollLeft,
horizontalScrollButton,
horizontalScrollLeftButton,
horizontalScrollLeftButtonContainerStyle,
handlePressScrollLeftButton,
],
);
const handleRenderScrollRightButton = useMemo(() => {
return (
horizontalScrollButton && (
<Touchable
testID="button-right"
disabled={!allowScrollRight}
style={StyleSheet.flatten([
styles.scrollContainer,
styles.scrollRightIconContainer,
horizontalScrollRightButtonContainerStyle,
!allowScrollRight ? styles.scrollContainerDisabled : {},
])}
onPress={handlePressScrollRightButton}>
{horizontalScrollRightButton || <Icon name="chevron-right" />}
</Touchable>
)
);
}, [
allowScrollRight,
horizontalScrollButton,
horizontalScrollRightButton,
horizontalScrollRightButtonContainerStyle,
handlePressScrollRightButton,
]);
const handleRenderListChipItem = useMemo(
() => chipIds.map(id => handleRenderChipItem(id)),
[chipIds, handleRenderChipItem],
);
useEffect(() => {
setChipIds(chips);
}, [chips]);
useEffect(() => {
if (initialize.current) {
setSelected(filterSelectList(chipIds, selectedId || [], singleValue));
} else {
initialize.current = true;
}
}, [singleValue, chipIds, selectedId]);
return horizontal ? (
<View style={StyleSheet.flatten([containerStyle, styles.containerNoWrap])}>
{handleRenderScrollLeftButton}
<FlatList
horizontal
testID="list"
ref={handleRefList}
onLayout={event => setLayout(event.nativeEvent.layout)}
data={chipIds}
scrollEnabled={horizontalScrollEnabled}
onContentSizeChange={(w, h) => setSize({x: w, y: h})}
onScroll={event => setOffset(event.nativeEvent.contentOffset)}
contentContainerStyle={styles.sectionWrap}
showsHorizontalScrollIndicator={horizontalScrollIndicator}
keyExtractor={item => item}
renderItem={({item}) => handleRenderChipItem(item)}
/>
{handleRenderScrollRightButton}
</View>
) : (
<View style={StyleSheet.flatten([containerStyle, styles.containerWrap])}>
{handleRenderListChipItem}
</View>
);
}
Example #23
Source File: Checkbox.tsx From react-native-design-kit with MIT License | 4 votes |
export default function Checkbox({
containerStyle,
checkboxIds,
checkboxComponent,
checkboxIndeterminateContainerStyle,
defaultIds,
onSelect,
onPress,
...props
}: CheckboxProps) {
const [selected, setSelected] = useState<string[]>(
defaultIds !== undefined ? filterId(defaultIds) : [],
);
function checkId(
id: string,
checkboxIdenfitifer: CheckboxIdentifier[],
): boolean {
for (const value of checkboxIdenfitifer) {
if (typeof value === 'string') {
if (value === id) {
return true;
}
} else {
return checkId(id, value.checkboxIds);
}
}
return false;
}
function filterId(id: string | string[]) {
const selection: string[] = [];
if (Array.isArray(id)) {
for (const check of id) {
if (checkId(check, checkboxIds)) {
selection.push(check);
}
}
} else if (checkId(id, checkboxIds)) {
selection.push(id);
}
return selection;
}
const isSelected = useCallback((id: string) => selected.indexOf(id) >= 0, [
selected,
]);
const checkIndeterminateStatus = useCallback(
(
checkboxIdenfitifer: CheckboxIdentifier[],
checked: boolean,
hasEmpty?: boolean,
): CheckboxCategoryStatus => {
for (const indeterminate of checkboxIdenfitifer) {
if (typeof indeterminate === 'string') {
if (!hasEmpty && !isSelected(indeterminate)) {
hasEmpty = true;
} else if (!checked && isSelected(indeterminate)) {
checked = true;
}
} else {
return checkIndeterminateStatus(
indeterminate.checkboxIds,
checked,
hasEmpty,
);
}
}
return checked
? hasEmpty
? 'indeterminate'
: 'selected'
: 'not-selected';
},
[isSelected],
);
const filterSelection = useCallback(
(
base: string[],
checkboxIdentifier: CheckboxIdentifier[],
toggle: boolean,
): string[] => {
const selection = [...base];
for (const identifier of checkboxIdentifier) {
if (typeof identifier === 'string') {
const select = selection.indexOf(identifier) >= 0;
if (select && !toggle) {
selection.splice(selection.indexOf(identifier), 1);
}
if (!select && toggle) {
selection.push(identifier);
}
} else {
return filterSelection(selection, identifier.checkboxIds, toggle);
}
}
return selection;
},
[],
);
const handlePressCheckboxNested = useCallback(
(
status: CheckboxCategoryStatus,
identifier: CheckboxIdentifier[],
event: GestureResponderEvent,
) => {
onPress && onPress(event);
const selection = filterSelection(
selected,
identifier,
status === 'not-selected' || status === 'indeterminate',
);
setSelected(selection);
},
[selected, filterSelection, onPress],
);
const handleRenderCheckboxNested = useCallback(
(key: string, title: string, identifier: CheckboxIdentifier[]) => {
const status = checkIndeterminateStatus(identifier, false);
return (
<CheckboxNested
{...props}
key={key}
title={title}
checkboxIds={identifier}
status={status}
onPress={event =>
handlePressCheckboxNested(status, identifier, event)
}
/>
);
},
[props, checkIndeterminateStatus, handlePressCheckboxNested],
);
const handlePressCheckboxItem = useCallback(
(id: string, event: GestureResponderEvent) => {
onPress && onPress(event);
const selection = [...selected];
if (isSelected(id)) {
selection.splice(selection.indexOf(id), 1);
onSelect(id, false, selection);
} else {
selection.push(id);
onSelect(id, true, selection);
}
setSelected(selection);
},
[selected, isSelected, onPress, onSelect],
);
const handleRenderCheckboxItem = useCallback(
(id: string) => {
const isIdSelected = isSelected(id);
const component =
checkboxComponent && checkboxComponent({id, isSelected: isIdSelected});
const title =
typeof component === 'string'
? component
: component === undefined
? id
: undefined;
return (
<CheckboxItem
{...props}
key={id}
title={title}
isSelected={isIdSelected}
onPress={event => handlePressCheckboxItem(id, event)}>
{component && typeof component !== 'string' && component}
</CheckboxItem>
);
},
[props, checkboxComponent, isSelected, handlePressCheckboxItem],
);
const handleRenderListCheckboxItem = useCallback(
(checkboxIdenfitifer: CheckboxIdentifier[], category?: string) => {
const list: ReactNode[] = [];
for (const value of checkboxIdenfitifer) {
if (typeof value === 'string') {
const item = handleRenderCheckboxItem(value);
list.push(item);
} else {
const title = value.title;
const identifier = value.checkboxIds;
const key = category !== undefined ? `${category}:${title}` : title;
const checkboxNested = handleRenderCheckboxNested(
key,
title,
identifier,
);
list.push(checkboxNested);
list.push(handleRenderListCheckboxItem(value.checkboxIds, key));
}
}
return category !== undefined ? (
<View
key={`category: ${category}`}
style={StyleSheet.flatten([
styles.checkboxIndeterminateContainer,
checkboxIndeterminateContainerStyle,
])}>
{list}
</View>
) : (
list
);
},
[
checkboxIndeterminateContainerStyle,
handleRenderCheckboxItem,
handleRenderCheckboxNested,
],
);
return (
<View style={containerStyle}>
{handleRenderListCheckboxItem(checkboxIds)}
</View>
);
}
Example #24
Source File: ButtonGroup.tsx From react-native-design-kit with MIT License | 4 votes |
export default function ButtonGroup({
type = 'solid',
actionType = 'button',
buttonIds,
buttonComponent,
buttonContainerStyle,
buttonTitleStyle,
containerStyle,
containerBorderRadius,
standbyButtonRaised,
selectedButtonRaised,
selectedButtonContainerStyle,
selectedButtonTitleStyle,
selectedId,
onPress,
onSelect,
...props
}: ButtonGroupProps) {
const singleValue = useMemo(() => actionType === 'radio', [actionType]);
const [selected, setSelected] = useState<string[]>(
filterSelectList(buttonIds, selectedId || [], singleValue),
);
const isSelected = useCallback((id: string) => selected.indexOf(id) >= 0, [
selected,
]);
const handlePressButtonItem = useCallback(
(id: string, event: GestureResponderEvent) => {
onPress && onPress(event);
if (actionType !== 'button') {
if (actionType === 'checkbox') {
const selection = [...selected];
if (isSelected(id)) {
selection.splice(selection.indexOf(id), 1);
} else {
selection.push(id);
}
setSelected(selection);
onSelect(id, selection);
} else {
const selection = [id];
setSelected(selection);
onSelect(id, selection);
}
} else {
onSelect(id, selected);
}
},
[actionType, selected, isSelected, onSelect, onPress],
);
const handleRenderButtonItem = useCallback(
(id: string, index: number) => {
const component =
buttonComponent && buttonComponent({id, isSelected: isSelected(id)});
const title =
typeof component === 'string'
? component
: component === undefined
? id
: undefined;
return (
<Button
{...props}
key={id}
type={type}
raised={isSelected(id) ? selectedButtonRaised : standbyButtonRaised}
containerStyle={StyleSheet.flatten([
styles.buttonContainer,
buttonContainerStyle,
isSelected(id)
? StyleSheet.flatten([
styles.selectedButtonContainer,
selectedButtonContainerStyle,
])
: {},
index === 0
? {
borderTopLeftRadius: containerBorderRadius,
borderBottomLeftRadius: containerBorderRadius,
}
: {},
index === buttonIds.length - 1
? {
borderTopRightRadius: containerBorderRadius,
borderBottomRightRadius: containerBorderRadius,
}
: {},
])}
title={title}
titleStyle={StyleSheet.flatten([
buttonTitleStyle,
isSelected(id) &&
StyleSheet.flatten([
type !== 'solid' && styles.selectedTitle,
selectedButtonTitleStyle,
]),
])}
onPress={event => handlePressButtonItem(id, event)}>
{component && typeof component !== 'string' && component}
</Button>
);
},
[
props,
type,
buttonIds,
buttonTitleStyle,
selectedButtonTitleStyle,
selectedButtonRaised,
standbyButtonRaised,
buttonContainerStyle,
selectedButtonContainerStyle,
containerBorderRadius,
isSelected,
buttonComponent,
handlePressButtonItem,
],
);
const handleRenderListButton = useMemo(() => {
const list: ReactElement[] = [];
for (let index = 0; index < buttonIds.length; index++) {
const id = buttonIds[index];
const button = handleRenderButtonItem(id, index);
list.push(button);
}
return list;
}, [buttonIds, handleRenderButtonItem]);
useDidUpdate(() => {
setSelected(filterSelectList(buttonIds, selectedId || [], singleValue));
}, [singleValue, buttonIds, selectedId]);
return (
<View style={StyleSheet.flatten([styles.container, containerStyle])}>
{handleRenderListButton}
</View>
);
}