Skip to content

Commit

Permalink
feat(rn-component): refactor context menu for ios
Browse files Browse the repository at this point in the history
Signed-off-by: Innei <[email protected]>
  • Loading branch information
Innei committed Jan 10, 2025
1 parent 0101139 commit 4bae36a
Show file tree
Hide file tree
Showing 15 changed files with 389 additions and 153 deletions.
2 changes: 2 additions & 0 deletions apps/mobile/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@
"react-native": "0.76.5",
"react-native-context-menu-view": "1.16.0",
"react-native-gesture-handler": "~2.20.2",
"react-native-ios-context-menu": "3.1.0",
"react-native-ios-utilities": "5.1.0",
"react-native-keyboard-controller": "^1.15.0",
"react-native-pager-view": "6.6.1",
"react-native-reanimated": "~3.16.5",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { FC } from "react"

import type { ContextMenuProps } from "./index.ios"
import type { ContextMenuProps } from "./types"

export const ContextMenu: FC<ContextMenuProps> = () => {
throw new Error("ContextMenu not implemented on Android")
Expand Down
92 changes: 91 additions & 1 deletion apps/mobile/src/components/ui/context-menu/index.ios.tsx
Original file line number Diff line number Diff line change
@@ -1 +1,91 @@
export { default as ContextMenu, type ContextMenuProps } from "react-native-context-menu-view"
// export { default as ContextMenu, type ContextMenuProps } from "react-native-context-menu-view"

import type { FC, PropsWithChildren } from "react"
import { useMemo, useState } from "react"
import { View } from "react-native"
import type { MenuAttributes, MenuConfig, MenuElementConfig } from "react-native-ios-context-menu"
import { ContextMenuView } from "react-native-ios-context-menu"

import { useColor } from "@/src/theme/colors"

import type { ContextMenuProps, IContextMenuItemConfig } from "./types"

export const ContextMenu: FC<ContextMenuProps & PropsWithChildren> = ({
config,
onPressMenuItem,
children,
renderPreview,
...props
}) => {
const [actionKeyMap] = useState(() => new Map<string, IContextMenuItemConfig>())
const menuViewConfig = useMemo((): MenuConfig => {
const createMenuItems = (items: typeof config.items): MenuElementConfig[] => {
return items
.filter((item) => !!item)
.map((item) => {
actionKeyMap.set(item.actionKey, item)

const menuAttributes: MenuAttributes[] = []
if (item.destructive) {
menuAttributes.push("destructive")
}
if (item.disabled) {
menuAttributes.push("disabled")
}
if (item.hidden) {
menuAttributes.push("hidden")
}

const menuItem: MenuElementConfig = {
actionTitle: item.title,
actionKey: item.actionKey,
icon: item.systemIcon ? { iconType: "SYSTEM", iconValue: item.systemIcon } : undefined,
menuAttributes,
}

if (item.subMenu) {
const subMenuConfig = menuItem as any as MenuConfig
subMenuConfig.menuTitle = item.subMenu.title || item.title || ""
subMenuConfig.menuSubtitle = item.subMenu.subTitle || ""
subMenuConfig.menuItems = createMenuItems(item.subMenu.items)
}

if (item.checked) {
menuItem.menuState = "on"
}

return menuItem
})
}

return {
menuTitle: config.title || "",
menuSubtitle: config.subTitle || "",
menuItems: createMenuItems(config.items),
}
}, [config, actionKeyMap])

const backgroundColor = useColor("systemBackground")
return (
<View {...props}>
<ContextMenuView
previewConfig={
renderPreview
? {
backgroundColor,
previewType: "CUSTOM",
previewSize: "STRETCH",
}
: undefined
}
renderPreview={renderPreview}
menuConfig={menuViewConfig}
onPressMenuItem={(e) => {
onPressMenuItem(actionKeyMap.get(e.nativeEvent.actionKey)!)
}}
>
{children}
</ContextMenuView>
</View>
)
}
1 change: 0 additions & 1 deletion apps/mobile/src/components/ui/context-menu/index.ts

This file was deleted.

1 change: 1 addition & 0 deletions apps/mobile/src/components/ui/context-menu/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { ContextMenu } from "./index.ios"
37 changes: 37 additions & 0 deletions apps/mobile/src/components/ui/context-menu/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import type { ViewProps } from "react-native"
import type { RenderItem } from "react-native-ios-context-menu"

export interface IContextMenuConfig {
title?: string
subTitle?: string
items: NullableContextMenuItemConfig[]
}

export type NullableContextMenuItemConfig = IContextMenuItemConfig | false | null | undefined
export interface IContextMenuItemConfig {
title: string
// icon?: IconConfig | ImageItemConfig
/**
* @note only available on iOS
*/
systemIcon?: string

subMenu?: IContextMenuConfig

destructive?: boolean
disabled?: boolean
hidden?: boolean
checked?: boolean

actionKey: string
}

export interface ContextMenuProps extends ViewProps {
config: IContextMenuConfig
onPressMenuItem: (item: IContextMenuItemConfig) => void

/**
* @note only available on iOS
*/
renderPreview?: RenderItem
}
Loading

0 comments on commit 4bae36a

Please sign in to comment.