Skip to content

Commit 9bb9c52

Browse files
committed
feat(menu): add disabled prop to MenuView component
1 parent 5cb6f18 commit 9bb9c52

File tree

7 files changed

+200
-39
lines changed

7 files changed

+200
-39
lines changed

README.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ A native menu component for React Native that provides platform-specific context
2525
- ✅ Custom trigger components - pass any React Native component as a child
2626
- ✅ Customizable colors for menu items
2727
- ✅ Checkmark support with custom colors
28+
- ✅ SF Symbols support on iOS (iosSymbol property)
2829
- ✅ Scrollable menus for long lists
2930
- ✅ Event handling for menu item selection
3031
- ✅ TypeScript support
@@ -172,6 +173,64 @@ const [selectedSort, setSelectedSort] = useState('date');
172173
</MenuView>
173174
```
174175

176+
### Disabled Menu
177+
178+
```tsx
179+
const [isDisabled, setIsDisabled] = useState(false);
180+
181+
<MenuView
182+
disabled={isDisabled}
183+
menuItems={[
184+
{ identifier: 'enable', title: 'Enable Menu' },
185+
{ identifier: 'disable', title: 'Disable Menu' },
186+
]}
187+
onMenuSelect={({ nativeEvent }) => {
188+
setIsDisabled(nativeEvent.identifier === 'disable');
189+
}}
190+
>
191+
<View style={[styles.menuButton, isDisabled && styles.disabledButton]}>
192+
<Text style={[styles.menuButtonText, isDisabled && styles.disabledText]}>
193+
{isDisabled ? '🔒 Menu Disabled' : '🔓 Menu Enabled'}
194+
</Text>
195+
</View>
196+
</MenuView>
197+
198+
// Add these styles
199+
const styles = StyleSheet.create({
200+
// ... other styles
201+
disabledButton: {
202+
opacity: 0.6,
203+
},
204+
disabledText: {
205+
color: '#999',
206+
},
207+
});
208+
```
209+
210+
### SF Symbols (iOS only)
211+
212+
```tsx
213+
import { MenuView } from 'react-native-menus';
214+
215+
<MenuView
216+
menuItems={[
217+
{ identifier: 'profile', title: 'View Profile', iosSymbol: 'person.circle' },
218+
{ identifier: 'settings', title: 'Settings', iosSymbol: 'gear' },
219+
{ identifier: 'logout', title: 'Logout', iosSymbol: 'arrow.right.square' },
220+
]}
221+
onMenuSelect={handleMenuSelect}
222+
>
223+
<View style={styles.menuButton}>
224+
<Text>👤 Account Menu with SF Symbols</Text>
225+
</View>
226+
</MenuView>
227+
```
228+
229+
For a list of available SF Symbols, refer to Apple's [SF Symbols app](https://developer.apple.com/sf-symbols/) or use common names like:
230+
- `"arrow.up"`, `"arrow.down"`, `"arrow.left"`, `"arrow.right"`
231+
- `"gear"`, `"heart"`, `"trash"`, `"checkmark"`, `"xmark"`
232+
- Add `.fill` suffix for filled variants: `"heart.fill"`, `"gear.fill"`
233+
175234
## API Reference
176235

177236
### Props
@@ -185,6 +244,7 @@ const [selectedSort, setSelectedSort] = useState('date');
185244
| `checkedColor` | `string` | No | `#007AFF` | Color for checked/selected menu items (Android only) |
186245
| `uncheckedColor` | `string` | No | `#8E8E93` | Color for unchecked/unselected menu items (Android only) |
187246
| `color` | `string` | No | - | Reserved for future use |
247+
| `disabled` | `boolean` | No | `false` | Disables the menu interaction when set to `true` |
188248
| `style` | `ViewStyle` | No | - | Style applied to the container view |
189249

190250
### Types
@@ -195,6 +255,7 @@ const [selectedSort, setSelectedSort] = useState('date');
195255
interface MenuItem {
196256
identifier: string; // Unique identifier for the menu item
197257
title: string; // Display text for the menu item
258+
iosSymbol?: string; // iOS-only: SF Symbol name to show beside the title (e.g., "gear", "heart.fill")
198259
}
199260
```
200261

@@ -254,6 +315,7 @@ The MenuView component accepts any React Native component as a child, which beco
254315
| Feature | iOS | Android |
255316
|---------|-----|---------|
256317
| Menu Style | Native UIMenu popover | Modal dialog at bottom |
318+
| SF Symbols | ✅ Full support via `iosSymbol` property | ❌ Not supported |
257319
| Checkmark Color | System default (not customizable) | Fully customizable |
258320
| Unchecked Color | System default | Fully customizable |
259321
| Animation | Native iOS animation | Slide up animation |

android/src/main/java/com/menu/MenuView.kt

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ class MenuView(context: Context) : FrameLayout(context) {
2828
private var checkedColor: String = "#007AFF" // Default iOS blue
2929
private var uncheckedColor: String = "#8E8E93" // Default iOS gray
3030
private var textColor: String? = null
31+
private var disabled: Boolean = false
3132

3233
init {
3334
setupMenuTrigger()
@@ -46,7 +47,10 @@ class MenuView(context: Context) : FrameLayout(context) {
4647
}
4748

4849
override fun onTouchEvent(event: android.view.MotionEvent?): Boolean {
49-
// Handle touch events - show menu on tap
50+
// Handle touch events - show menu on tap only if not disabled
51+
if (disabled) {
52+
return false
53+
}
5054
if (event?.action == android.view.MotionEvent.ACTION_UP) {
5155
performClick()
5256
return true
@@ -91,6 +95,16 @@ class MenuView(context: Context) : FrameLayout(context) {
9195
this.selectedItemIdentifier = selectedIdentifier
9296
}
9397

98+
fun setDisabled(disabled: Boolean) {
99+
this.disabled = disabled
100+
updateDisabledState()
101+
}
102+
103+
private fun updateDisabledState() {
104+
isClickable = !disabled
105+
isFocusable = !disabled
106+
}
107+
94108
fun setMenuItems(menuItems: ReadableArray?) {
95109
val items = mutableListOf<Map<String, String>>()
96110

android/src/main/java/com/menu/MenuViewManager.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@ class MenuViewManager : ViewGroupManager<MenuView>() {
4040
view.setSelectedIdentifier(selectedIdentifier)
4141
}
4242

43+
@ReactProp(name = "disabled")
44+
fun setDisabled(view: MenuView, disabled: Boolean) {
45+
view.setDisabled(disabled)
46+
}
47+
4348
override fun getExportedCustomBubblingEventTypeConstants(): Map<String, Any> {
4449
return mapOf(
4550
"onMenuSelect" to mapOf(

example/src/App.tsx

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { MenuView, asSFSymbol } from 'react-native-menus';
1212
const App = () => {
1313
const [selectedTheme, setSelectedTheme] = useState('dark');
1414
const [selectedSort, setSelectedSort] = useState('date');
15+
const [isDisabled, setIsDisabled] = useState(false);
1516

1617
const handleMenuSelect = (event: {
1718
nativeEvent: { identifier: string; title: string };
@@ -336,6 +337,51 @@ const App = () => {
336337
</View>
337338
</MenuView>
338339
</View>
340+
341+
{/* Example 7: Disabled Menu */}
342+
<View style={styles.menuContainer}>
343+
<Text style={styles.subtitle}>7. Disabled Menu</Text>
344+
<Text onPress={() => setIsDisabled(false)} style={styles.hint}>
345+
Current:{' '}
346+
{isDisabled ? 'Disabled, (Click me to re-enable)' : 'Enabled'}
347+
</Text>
348+
<MenuView
349+
disabled={isDisabled}
350+
menuItems={[
351+
{
352+
identifier: 'enable',
353+
title: 'Enable Menu',
354+
iosSymbol: asSFSymbol('checkmark.circle'),
355+
},
356+
{
357+
identifier: 'disable',
358+
title: 'Disable Menu',
359+
iosSymbol: asSFSymbol('xmark.circle'),
360+
},
361+
]}
362+
onMenuSelect={(event) => {
363+
const { identifier } = event.nativeEvent;
364+
setIsDisabled(identifier === 'disable');
365+
Alert.alert(
366+
'Menu Selection',
367+
`Menu ${identifier === 'disable' ? 'disabled' : 'enabled'}`
368+
);
369+
}}
370+
>
371+
<View
372+
style={[styles.menuButton, isDisabled && styles.disabledButton]}
373+
>
374+
<Text
375+
style={[
376+
styles.menuButtonText,
377+
isDisabled && styles.disabledText,
378+
]}
379+
>
380+
{isDisabled ? '🔒 Disabled Menu' : '🔓 Enabled Menu'}
381+
</Text>
382+
</View>
383+
</MenuView>
384+
</View>
339385
</View>
340386
</ScrollView>
341387
</SafeAreaView>
@@ -389,6 +435,13 @@ const styles = StyleSheet.create({
389435
fontSize: 16,
390436
fontWeight: '500',
391437
},
438+
disabledButton: {
439+
backgroundColor: '#f3f4f6',
440+
opacity: 0.6,
441+
},
442+
disabledText: {
443+
color: '#9ca3af',
444+
},
392445
});
393446

394447
export default App;

0 commit comments

Comments
 (0)