Skip to content

Commit a70381f

Browse files
authored
feat(ios): custom preview visible path and disable shadow option (#142)
* feat(ios): custom preview visible path and disable shadow option * feat: update readme
1 parent bf5475b commit a70381f

File tree

6 files changed

+119
-13
lines changed

6 files changed

+119
-13
lines changed

README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,3 +95,21 @@ Optional. When set to `true`, the context menu is triggered with a single tap in
9595
### `disabled`
9696

9797
Optional. Disable menu interaction.
98+
99+
### `borderRadius`
100+
Optional, iOS only. Sets the border radius for all corners of the preview. Can be overridden by individual corner settings.
101+
102+
### `borderTopLeftRadius`
103+
Optional, iOS only. Sets the border radius specifically for the top left corner of the preview.
104+
105+
### `borderTopRightRadius`
106+
Optional, iOS only. Sets the border radius specifically for the top right corner of the preview.
107+
108+
### `borderBottomRightRadius`
109+
Optional, iOS only. Sets the border radius specifically for the bottom right corner of the preview.
110+
111+
### `borderBottomLeftRadius`
112+
Optional, iOS only. Sets the border radius specifically for the bottom left corner of the preview.
113+
114+
### `disableShadow`
115+
Optional, iOS only. When set to `true`, removes the shadow from the preview. Default is `false`.

index.d.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,30 @@ export interface ContextMenuProps extends ViewProps {
9696
* Children prop as per upgrade docs: https://reactjs.org/blog/2022/03/08/react-18-upgrade-guide.html#updates-to-typescript-definitions
9797
*/
9898
children?: React.ReactNode;
99+
/**
100+
* Border radius for all corners
101+
*/
102+
borderRadius?: number;
103+
/**
104+
* Border radius for top left corner
105+
*/
106+
borderTopLeftRadius?: number;
107+
/**
108+
* Border radius for top right corner
109+
*/
110+
borderTopRightRadius?: number;
111+
/**
112+
* Border radius for bottom right corner
113+
*/
114+
borderBottomRightRadius?: number;
115+
/**
116+
* Border radius for bottom left corner
117+
*/
118+
borderBottomLeftRadius?: number;
119+
/**
120+
* Disable shadow in preview
121+
*/
122+
disableShadow?: boolean;
99123
}
100124

101-
export default class ContextMenu extends Component<ContextMenuProps> {}
125+
export default class ContextMenu extends Component<ContextMenuProps> { }

index.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,22 @@ import { requireNativeComponent, View, Platform, StyleSheet, processColor } from
44
const NativeContextMenu = requireNativeComponent("ContextMenu", null);
55

66
const ContextMenu = (props) => {
7+
const defaultProps = {
8+
borderRadius: -1,
9+
borderTopLeftRadius: -1,
10+
borderTopRightRadius: -1,
11+
borderBottomRightRadius: -1,
12+
borderBottomLeftRadius: -1
13+
};
14+
715
const iconColor = props?.iconColor
816
? Platform.OS === 'ios'
917
? processColor(props.iconColor)
1018
: props.iconColor
1119
: undefined;
1220

1321
return (
14-
<NativeContextMenu {...props} iconColor={iconColor}>
22+
<NativeContextMenu {...defaultProps} {...props} iconColor={iconColor}>
1523
{props.children}
1624
{props.preview != null && Platform.OS === 'ios' ? (
1725
<View style={styles.preview} nativeID="ContextMenuPreview">{props.preview}</View>

ios/ContextMenu.m

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ - (UIView *) view {
1919
RCT_CUSTOM_VIEW_PROPERTY(previewBackgroundColor, UIColor, ContextMenuView) {
2020
view.previewBackgroundColor = json != nil ? [RCTConvert UIColor:json] : nil;
2121
}
22+
RCT_EXPORT_VIEW_PROPERTY(borderRadius, CGFloat)
23+
RCT_EXPORT_VIEW_PROPERTY(borderTopLeftRadius, CGFloat)
24+
RCT_EXPORT_VIEW_PROPERTY(borderTopRightRadius, CGFloat)
25+
RCT_EXPORT_VIEW_PROPERTY(borderBottomRightRadius, CGFloat)
26+
RCT_EXPORT_VIEW_PROPERTY(borderBottomLeftRadius, CGFloat)
27+
RCT_EXPORT_VIEW_PROPERTY(disableShadow, BOOL)
2228
RCT_CUSTOM_VIEW_PROPERTY(dropdownMenuMode, BOOL, ContextMenuView) {
2329
if (@available(iOS 14.0, *)) {
2430
view.showsMenuAsPrimaryAction = json != nil ? [RCTConvert BOOL:json] : view.showsMenuAsPrimaryAction;

ios/ContextMenuView.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,11 @@
1919
@property (nullable, nonatomic, copy) NSArray<ContextMenuAction*>* actions;
2020
@property (nullable, nonatomic, copy) UIColor* previewBackgroundColor;
2121
@property (nonatomic, assign) BOOL disabled;
22+
@property (nonatomic, assign) CGFloat borderRadius;
23+
@property (nonatomic, assign) CGFloat borderTopLeftRadius;
24+
@property (nonatomic, assign) CGFloat borderTopRightRadius;
25+
@property (nonatomic, assign) CGFloat borderBottomRightRadius;
26+
@property (nonatomic, assign) CGFloat borderBottomLeftRadius;
27+
@property (nonatomic, assign) BOOL disableShadow;
2228

2329
@end

ios/ContextMenuView.m

Lines changed: 55 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -105,16 +105,65 @@ - (void)contextMenuInteraction:(UIContextMenuInteraction *)interaction willEndFo
105105
}
106106
}
107107

108-
- (UITargetedPreview *)contextMenuInteraction:(UIContextMenuInteraction *)interaction previewForHighlightingMenuWithConfiguration:(UIContextMenuConfiguration *)configuration API_AVAILABLE(ios(13.0)) {
109-
UIPreviewTarget* previewTarget = [[UIPreviewTarget alloc] initWithContainer:self center:self.reactSubviews.firstObject.center];
108+
- (UIPreviewParameters *)getPreviewParams {
110109
UIPreviewParameters* previewParams = [[UIPreviewParameters alloc] init];
111-
110+
112111
if (_previewBackgroundColor != nil) {
113-
previewParams.backgroundColor = _previewBackgroundColor;
112+
previewParams.backgroundColor = _previewBackgroundColor;
113+
}
114+
115+
if (self.borderRadius > -1 ||
116+
self.borderTopLeftRadius > -1 ||
117+
self.borderTopRightRadius > -1 ||
118+
self.borderBottomRightRadius > -1 ||
119+
self.borderBottomLeftRadius > -1) {
120+
121+
CGFloat radius = self.borderRadius > -1 ? self.borderRadius : 0;
122+
CGFloat topLeftRadius = self.borderTopLeftRadius > -1 ? self.borderTopLeftRadius : radius;
123+
CGFloat topRightRadius = self.borderTopRightRadius > -1 ? self.borderTopRightRadius : radius;
124+
CGFloat bottomRightRadius = self.borderBottomRightRadius > -1 ? self.borderBottomRightRadius : radius;
125+
CGFloat bottomLeftRadius = self.borderBottomLeftRadius > -1 ? self.borderBottomLeftRadius : radius;
126+
127+
UIBezierPath *path = [UIBezierPath bezierPath];
128+
CGRect bounds = self.bounds;
129+
130+
[path moveToPoint: CGPointMake(CGRectGetMinX(bounds) + topLeftRadius, CGRectGetMinY(bounds))];
131+
132+
[path addLineToPoint: CGPointMake(CGRectGetMaxX(bounds) - topRightRadius, CGRectGetMinY(bounds))];
133+
[path addQuadCurveToPoint: CGPointMake(CGRectGetMaxX(bounds), CGRectGetMinY(bounds) + topRightRadius)
134+
controlPoint: CGPointMake(CGRectGetMaxX(bounds), CGRectGetMinY(bounds))];
135+
136+
[path addLineToPoint: CGPointMake(CGRectGetMaxX(bounds), CGRectGetMaxY(bounds) - bottomRightRadius)];
137+
[path addQuadCurveToPoint: CGPointMake(CGRectGetMaxX(bounds) - bottomRightRadius, CGRectGetMaxY(bounds))
138+
controlPoint: CGPointMake(CGRectGetMaxX(bounds), CGRectGetMaxY(bounds))];
139+
140+
[path addLineToPoint: CGPointMake(CGRectGetMinX(bounds) + bottomLeftRadius, CGRectGetMaxY(bounds))];
141+
[path addQuadCurveToPoint: CGPointMake(CGRectGetMinX(bounds), CGRectGetMaxY(bounds) - bottomLeftRadius)
142+
controlPoint: CGPointMake(CGRectGetMinX(bounds), CGRectGetMaxY(bounds))];
143+
144+
[path addLineToPoint: CGPointMake(CGRectGetMinX(bounds), CGRectGetMinY(bounds) + topLeftRadius)];
145+
[path addQuadCurveToPoint: CGPointMake(CGRectGetMinX(bounds) + topLeftRadius, CGRectGetMinY(bounds))
146+
controlPoint: CGPointMake(CGRectGetMinX(bounds), CGRectGetMinY(bounds))];
147+
148+
[path closePath];
149+
150+
previewParams.visiblePath = path;
114151
}
152+
153+
154+
if (self.disableShadow) {
155+
previewParams.shadowPath = [UIBezierPath bezierPath];
156+
}
157+
158+
return previewParams;
159+
}
115160

161+
- (UITargetedPreview *)contextMenuInteraction:(UIContextMenuInteraction *)interaction previewForHighlightingMenuWithConfiguration:(UIContextMenuConfiguration *)configuration API_AVAILABLE(ios(13.0)) {
162+
UIPreviewTarget* previewTarget = [[UIPreviewTarget alloc] initWithContainer:self center:self.reactSubviews.firstObject.center];
163+
164+
116165
return [[UITargetedPreview alloc] initWithView:self.reactSubviews.firstObject
117-
parameters:previewParams
166+
parameters:[self getPreviewParams]
118167
target:previewTarget];
119168
}
120169

@@ -127,14 +176,9 @@ - (UITargetedPreview *)contextMenuInteraction:(UIContextMenuInteraction *)intera
127176
}
128177

129178
UIPreviewTarget* previewTarget = [[UIPreviewTarget alloc] initWithContainer:self center:hostView.center];
130-
UIPreviewParameters* previewParams = [[UIPreviewParameters alloc] init];
131-
132-
if (_previewBackgroundColor != nil) {
133-
previewParams.backgroundColor = _previewBackgroundColor;
134-
}
135179

136180
return [[UITargetedPreview alloc] initWithView:hostView
137-
parameters:previewParams
181+
parameters:[self getPreviewParams]
138182
target:previewTarget];
139183
}
140184

0 commit comments

Comments
 (0)