Rename native borderRadius prop to avoid UIView collision

RN/Expo already owns a borderRadius property on UIView (as a style prop),
so declaring `private var borderRadius` on ExpoView was rejected by Swift
("cannot override with a stored property"). Rename the bridge prop and Swift
property to `previewCornerRadius`; the public Trigger/ImageContextMenu API
keeps `borderRadius` and Root translates it at the bridge layer.

Also tighten `var start` to `let` in the SVG parser.

https://claude.ai/code/session_015REmux3R9uuEMMJUHxTyQT
This commit is contained in:
Claude
2026-04-19 07:28:44 +00:00
committed by Samuel Newman
parent ce686fae89
commit 2dc22c19b0
5 changed files with 18 additions and 15 deletions
@@ -15,8 +15,9 @@ public class ExpoBlueskyContextMenuModule: Module {
view.setMenuItems(value) view.setMenuItems(value)
} }
Prop("borderRadius") { (view: ExpoBlueskyContextMenuView, value: Double) in Prop("previewCornerRadius") {
view.setBorderRadius(value) (view: ExpoBlueskyContextMenuView, value: Double) in
view.setPreviewCornerRadius(value)
} }
} }
} }
@@ -2,21 +2,20 @@ import ExpoModulesCore
import UIKit import UIKit
/// Native view that hosts the children and attaches a /// Native view that hosts the children and attaches a
/// `UIContextMenuInteraction`. Two JS-shipped props drive behaviour: /// `UIContextMenuInteraction`. JS-shipped props drive behaviour:
/// - `preview`: discriminated union describing what to show during peek /// - `preview`: discriminated union describing what to show during peek
/// - `menuItems`: array of menu item specs (see `MenuBuilder`) /// - `menuItems`: array of menu item specs (see `MenuBuilder`)
/// - `borderRadius`: used for the targeted preview's visible path so the lift /// - `previewCornerRadius`: used for the targeted preview's visible path so the
/// animation matches the thumbnail's clipping /// lift animation matches the thumbnail's clipping. (Named distinctly from
/// the RN-owned `borderRadius` style prop on UIView.)
class ExpoBlueskyContextMenuView: ExpoView, UIContextMenuInteractionDelegate { class ExpoBlueskyContextMenuView: ExpoView, UIContextMenuInteractionDelegate {
private var preview: [String: Any]? private var preview: [String: Any]?
private var menuItems: [[String: Any]] = [] private var menuItems: [[String: Any]] = []
private var borderRadius: CGFloat = 0 private var previewCornerRadius: CGFloat = 0
private let onItemPress = EventDispatcher() private let onItemPress = EventDispatcher()
private let onPreviewPress = EventDispatcher() private let onPreviewPress = EventDispatcher()
private var pendingCommitId: String?
required init(appContext: AppContext? = nil) { required init(appContext: AppContext? = nil) {
super.init(appContext: appContext) super.init(appContext: appContext)
let interaction = UIContextMenuInteraction(delegate: self) let interaction = UIContextMenuInteraction(delegate: self)
@@ -25,7 +24,9 @@ class ExpoBlueskyContextMenuView: ExpoView, UIContextMenuInteractionDelegate {
func setPreview(_ value: [String: Any]?) { self.preview = value } func setPreview(_ value: [String: Any]?) { self.preview = value }
func setMenuItems(_ value: [[String: Any]]) { self.menuItems = value } func setMenuItems(_ value: [[String: Any]]) { self.menuItems = value }
func setBorderRadius(_ value: Double) { self.borderRadius = CGFloat(value) } func setPreviewCornerRadius(_ value: Double) {
self.previewCornerRadius = CGFloat(value)
}
// MARK: - UIContextMenuInteractionDelegate // MARK: - UIContextMenuInteractionDelegate
@@ -83,10 +84,10 @@ class ExpoBlueskyContextMenuView: ExpoView, UIContextMenuInteractionDelegate {
private func makeTargetedPreview() -> UITargetedPreview { private func makeTargetedPreview() -> UITargetedPreview {
let parameters = UIPreviewParameters() let parameters = UIPreviewParameters()
parameters.backgroundColor = .clear parameters.backgroundColor = .clear
if borderRadius > 0 { if previewCornerRadius > 0 {
parameters.visiblePath = UIBezierPath( parameters.visiblePath = UIBezierPath(
roundedRect: self.bounds, roundedRect: self.bounds,
cornerRadius: borderRadius cornerRadius: previewCornerRadius
) )
} }
return UITargetedPreview(view: self, parameters: parameters) return UITargetedPreview(view: self, parameters: parameters)
@@ -169,7 +169,7 @@ private struct Tokenizer {
mutating func readNumber() -> CGFloat { mutating func readNumber() -> CGFloat {
skipSeparators() skipSeparators()
var start = index let start = index
var sawDot = false var sawDot = false
var sawE = false var sawE = false
while index < chars.count { while index < chars.count {
@@ -61,7 +61,7 @@ export function Root({children, style}: RootProps) {
<NativeView <NativeView
preview={trigger.props.preview} preview={trigger.props.preview}
menuItems={menuItems} menuItems={menuItems}
borderRadius={trigger.props.borderRadius ?? 0} previewCornerRadius={trigger.props.borderRadius ?? 0}
onItemPress={handleItemPress} onItemPress={handleItemPress}
onPreviewPress={handlePreviewPress} onPreviewPress={handlePreviewPress}
style={[style, trigger.props.style]}> style={[style, trigger.props.style]}>
@@ -48,7 +48,8 @@ export type MenuItemIconSource = IconWithSvgMeta
export type NativeViewProps = { export type NativeViewProps = {
preview?: PreviewContent preview?: PreviewContent
menuItems: MenuItemSpec[] menuItems: MenuItemSpec[]
borderRadius: number /** Named distinctly from `borderRadius`, which RN owns as a style prop. */
previewCornerRadius: number
onItemPress: (e: {nativeEvent: {id: string}}) => void onItemPress: (e: {nativeEvent: {id: string}}) => void
onPreviewPress: (e: {nativeEvent: {}}) => void onPreviewPress: (e: {nativeEvent: {}}) => void
style?: StyleProp<ViewStyle> style?: StyleProp<ViewStyle>