- children: ReactNode
-}
-
-/**
- * Sentinel: does not render. `Root` reads props + children off this element
- * and hosts `children` inside the native context-menu view.
- */
-function TriggerImpl(_: TriggerProps): null {
- return null
-}
-
-export const Trigger = tag(TriggerImpl, 'trigger')
diff --git a/modules/expo-bluesky-context-menu/src/registry.ts b/modules/expo-bluesky-context-menu/src/registry.ts
deleted file mode 100644
index a3a6b2c269..0000000000
--- a/modules/expo-bluesky-context-menu/src/registry.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- * Marker keys and type tags shared between `Root`, `Trigger`, `Menu`, and
- * `MenuItem*`. `Root` walks its children looking for these tags so the
- * composition API doesn't rely on string component names or display names.
- */
-export const CONTEXT_MENU_KIND = '__ExpoBlueskyContextMenuKind__'
-
-export type ContextMenuKind =
- | 'trigger'
- | 'menu'
- | 'item'
- | 'item-icon'
- | 'item-text'
-
-export type TaggedComponent = React.FunctionComponent
& {
- [CONTEXT_MENU_KIND]: ContextMenuKind
-}
-
-export function tag
(
- component: React.FunctionComponent
,
- kind: ContextMenuKind,
-): TaggedComponent
{
- ;(component as TaggedComponent
)[CONTEXT_MENU_KIND] = kind
- return component as TaggedComponent
-}
-
-export function kindOf(type: unknown): ContextMenuKind | undefined {
- if (type && typeof type === 'function') {
- return (type as TaggedComponent)[CONTEXT_MENU_KIND]
- }
- return undefined
-}
diff --git a/modules/expo-bluesky-context-menu/src/types.ts b/modules/expo-bluesky-context-menu/src/types.ts
deleted file mode 100644
index d3eb10027c..0000000000
--- a/modules/expo-bluesky-context-menu/src/types.ts
+++ /dev/null
@@ -1,61 +0,0 @@
-import {type ReactNode} from 'react'
-import {type StyleProp, type ViewStyle} from 'react-native'
-
-import {type IconWithSvgMeta} from '#/components/icons/TEMPLATE'
-
-/**
- * Content to show during the peek preview. Discriminated by `type`; the native
- * side dispatches on it to build the right `UIViewController`.
- *
- * Only `image` is implemented on iOS today. `video` and `externalCard` are the
- * planned follow-ups; leaving them in the type keeps the JS call-sites honest.
- */
-export type PreviewContent =
- | {
- type: 'image'
- uri: string
- /** Thumb URL. When present, the native side paints it in as an instant
- * placeholder (reading from the shared SDWebImage cache) while the
- * fullsize loads — avoids the black flash on first peek. */
- thumbUri?: string
- /** Aspect ratio as width / height. */
- aspectRatio: number
- }
- | {
- type: 'video'
- uri: string
- poster?: string
- aspectRatio: number
- }
- | {
- type: 'externalCard'
- thumbUri?: string
- title: string
- description?: string
- url: string
- }
-
-export type MenuItemSpec = {
- id: string
- label: string
- destructive?: boolean
- disabled?: boolean
- icon?: {
- paths: string[]
- viewBox: string
- strokeWidth: number
- }
-}
-
-export type MenuItemIconSource = IconWithSvgMeta
-
-export type NativeViewProps = {
- preview?: PreviewContent
- menuItems: MenuItemSpec[]
- /** Named distinctly from `borderRadius`, which RN owns as a style prop. */
- previewCornerRadius: number
- onItemPress: (e: {nativeEvent: {id: string}}) => void
- onPreviewPress: (e: {nativeEvent: {}}) => void
- style?: StyleProp
- children?: ReactNode
-}
diff --git a/package.json b/package.json
index 1787e76315..39ee8ebb28 100644
--- a/package.json
+++ b/package.json
@@ -101,6 +101,7 @@
"@bsky.app/expo-image-crop-tool": "^0.5.1",
"@bsky.app/expo-scroll-edge-effect": "^0.1.4",
"@bsky.app/expo-translate-text": "^0.2.9",
+ "@bsky.app/peek-menu": "^0.2.0",
"@bsky.app/react-native-mmkv": "2.12.5",
"@bsky.app/sift": "^0.3.4",
"@bsky.app/tapper": "^0.5.3",
diff --git a/src/components/PeekMenu.tsx b/src/components/PeekMenu.tsx
new file mode 100644
index 0000000000..53732ec3e8
--- /dev/null
+++ b/src/components/PeekMenu.tsx
@@ -0,0 +1 @@
+export * from '@bsky.app/peek-menu'
diff --git a/src/components/PeekMenu/index.ios.tsx b/src/components/PeekMenu/index.ios.tsx
deleted file mode 100644
index 3e2f025542..0000000000
--- a/src/components/PeekMenu/index.ios.tsx
+++ /dev/null
@@ -1,12 +0,0 @@
-export type {
- MenuItemSpec,
- PreviewContent,
-} from '../../../modules/expo-bluesky-context-menu'
-export {
- Menu,
- MenuItem,
- MenuItemIcon,
- MenuItemText,
- Root,
- Trigger,
-} from '../../../modules/expo-bluesky-context-menu'
diff --git a/src/components/PeekMenu/index.tsx b/src/components/PeekMenu/index.tsx
deleted file mode 100644
index 695347edc2..0000000000
--- a/src/components/PeekMenu/index.tsx
+++ /dev/null
@@ -1,54 +0,0 @@
-import {type ReactNode} from 'react'
-import {type StyleProp, View, type ViewStyle} from 'react-native'
-
-import {type IconWithSvgMeta} from '#/components/icons/TEMPLATE'
-import {type PreviewContent} from '../../../modules/expo-bluesky-context-menu'
-
-export type {
- MenuItemSpec,
- PreviewContent,
-} from '../../../modules/expo-bluesky-context-menu'
-
-export function Root({
- children,
- style,
-}: {
- children: ReactNode
- style?: StyleProp
-}) {
- return {children}
-}
-
-export function Trigger({
- children,
-}: {
- preview?: PreviewContent
- onPreviewPress?: () => void
- borderRadius?: number
- style?: StyleProp
- children: ReactNode
-}) {
- return <>{children}>
-}
-
-export function Menu(_: {children: ReactNode}): null {
- return null
-}
-
-export function MenuItem(_: {
- id: string
- destructive?: boolean
- disabled?: boolean
- onSelect: () => void
- children: ReactNode
-}): null {
- return null
-}
-
-export function MenuItemIcon(_: {icon: IconWithSvgMeta}): null {
- return null
-}
-
-export function MenuItemText(_: {children: string}): null {
- return null
-}