| 1 | import { useState } from "react"; |
| 2 | import { StyleSheet, View } from "react-native"; |
| 3 | import Svg, { Path } from "react-native-svg"; |
| 4 | |
| 5 | import type { FrostedCutoutProps } from "./FrostedCutout.types"; |
| 6 | |
| 7 | /** Fills its parent with a theme-matched tint that leaves a rounded hole clear. iOS blurs instead. */ |
| 8 | export function FrostedCutout(props: FrostedCutoutProps) { |
| 9 | const [width, setWidth] = useState(0); |
| 10 | const [height, setHeight] = useState(0); |
| 11 | const x = (width - props.cutoutWidth) / 2; |
| 12 | const y = props.cutoutTop; |
| 13 | const w = props.cutoutWidth; |
| 14 | const h = props.cutoutHeight; |
| 15 | const r = Math.min(props.cutoutRadius, w / 2, h / 2); |
| 16 | const hole = `M${x + r},${y} H${x + w - r} A${r},${r} 0 0 1 ${x + w},${y + r} V${y + h - r} A${r},${r} 0 0 1 ${x + w - r},${y + h} H${x + r} A${r},${r} 0 0 1 ${x},${y + h - r} V${y + r} A${r},${r} 0 0 1 ${x + r},${y} Z`; |
| 17 | |
| 18 | return ( |
| 19 | <View |
| 20 | pointerEvents="none" |
| 21 | style={StyleSheet.absoluteFill} |
| 22 | onLayout={(event) => { |
| 23 | setWidth(event.nativeEvent.layout.width); |
| 24 | setHeight(event.nativeEvent.layout.height); |
| 25 | }} |
| 26 | > |
| 27 | {width > 0 ? ( |
| 28 | <Svg height={height} width={width}> |
| 29 | <Path |
| 30 | d={`M0,0 H${width} V${height} H0 Z ${hole}`} |
| 31 | fill={props.appearance === "light" ? "#ffffff" : "#000000"} |
| 32 | fillOpacity={0.55} |
| 33 | fillRule="evenodd" |
| 34 | /> |
| 35 | </Svg> |
| 36 | ) : null} |
| 37 | </View> |
| 38 | ); |
| 39 | } |