Unwrap Menu.Trigger on web (#3182)

zio/stable
Eric Bailey 2024-03-12 11:23:01 -05:00 committed by GitHub
parent 17d921fd9d
commit b8afb935f4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 78 additions and 54 deletions

View File

@ -1,3 +1,5 @@
/* eslint-disable react/prop-types */
import React from 'react' import React from 'react'
import {View, Pressable} from 'react-native' import {View, Pressable} from 'react-native'
import * as DropdownMenu from '@radix-ui/react-dropdown-menu' import * as DropdownMenu from '@radix-ui/react-dropdown-menu'
@ -14,6 +16,7 @@ import {
GroupProps, GroupProps,
ItemTextProps, ItemTextProps,
ItemIconProps, ItemIconProps,
RadixPassThroughTriggerProps,
} from '#/components/Menu/types' } from '#/components/Menu/types'
import {Context} from '#/components/Menu/context' import {Context} from '#/components/Menu/context'
@ -76,7 +79,24 @@ export function Root({
) )
} }
export function Trigger({children, label, style}: TriggerProps) { const RadixTriggerPassThrough = React.forwardRef(
(
props: {
children: (
props: RadixPassThroughTriggerProps & {
ref: React.Ref<any>
},
) => React.ReactNode
},
ref,
) => {
// @ts-expect-error Radix provides no types of this stuff
return props.children({...props, ref})
},
)
RadixTriggerPassThrough.displayName = 'RadixTriggerPassThrough'
export function Trigger({children, label}: TriggerProps) {
const {control} = React.useContext(Context) const {control} = React.useContext(Context)
const { const {
state: hovered, state: hovered,
@ -87,28 +107,27 @@ export function Trigger({children, label, style}: TriggerProps) {
return ( return (
<DropdownMenu.Trigger asChild> <DropdownMenu.Trigger asChild>
<Pressable <RadixTriggerPassThrough>
accessibilityHint="" {props =>
accessibilityLabel={label} children({
onFocus={onFocus} isNative: false,
onBlur={onBlur} control,
style={flatten([style, focused && web({outline: 0})])} state: {
onPointerDown={() => control.open()} hovered,
{...web({ focused,
onMouseEnter, pressed: false,
onMouseLeave, },
})}> props: {
{children({ ...props,
isNative: false, onFocus: onFocus,
control, onBlur: onBlur,
state: { onMouseEnter,
hovered, onMouseLeave,
focused, accessibilityLabel: label,
pressed: false, },
}, })
props: {}, }
})} </RadixTriggerPassThrough>
</Pressable>
</DropdownMenu.Trigger> </DropdownMenu.Trigger>
) )
} }

View File

@ -1,5 +1,9 @@
import React from 'react' import React from 'react'
import {GestureResponderEvent, PressableProps} from 'react-native' import {
GestureResponderEvent,
PressableProps,
AccessibilityProps,
} from 'react-native'
import {Props as SVGIconProps} from '#/components/icons/common' import {Props as SVGIconProps} from '#/components/icons/common'
import * as Dialog from '#/components/Dialog' import * as Dialog from '#/components/Dialog'
@ -9,7 +13,19 @@ export type ContextType = {
control: Dialog.DialogOuterProps['control'] control: Dialog.DialogOuterProps['control']
} }
export type TriggerProps = ViewStyleProp & { export type RadixPassThroughTriggerProps = {
id: string
type: 'button'
disabled: boolean
['data-disabled']: boolean
['data-state']: string
['aria-controls']?: string
['aria-haspopup']?: boolean
['aria-expanded']?: AccessibilityProps['aria-expanded']
onKeyDown: (e: React.KeyboardEvent) => void
onPointerDown: PressableProps['onPointerDown']
}
export type TriggerProps = {
children(props: TriggerChildProps): React.ReactNode children(props: TriggerChildProps): React.ReactNode
label: string label: string
} }
@ -52,7 +68,13 @@ export type TriggerChildProps =
*/ */
pressed: false pressed: false
} }
props: {} props: RadixPassThroughTriggerProps & {
onFocus: () => void
onBlur: () => void
onMouseEnter: () => void
onMouseLeave: () => void
accessibilityLabel: string
}
} }
export type ItemProps = React.PropsWithChildren< export type ItemProps = React.PropsWithChildren<

View File

@ -1,11 +1,5 @@
import React, {memo} from 'react' import React, {memo} from 'react'
import { import {StyleProp, ViewStyle, Pressable, PressableProps} from 'react-native'
StyleProp,
ViewStyle,
Pressable,
View,
PressableProps,
} from 'react-native'
import Clipboard from '@react-native-clipboard/clipboard' import Clipboard from '@react-native-clipboard/clipboard'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {useNavigation} from '@react-navigation/native' import {useNavigation} from '@react-navigation/native'
@ -38,7 +32,7 @@ import {isWeb} from '#/platform/detection'
import {richTextToString} from '#/lib/strings/rich-text-helpers' import {richTextToString} from '#/lib/strings/rich-text-helpers'
import {useGlobalDialogsControlContext} from '#/components/dialogs/Context' import {useGlobalDialogsControlContext} from '#/components/dialogs/Context'
import {atoms as a, useTheme as useAlf, web} from '#/alf' import {atoms as a, useTheme as useAlf} from '#/alf'
import * as Menu from '#/components/Menu' import * as Menu from '#/components/Menu'
import {Clipboard_Stroke2_Corner2_Rounded as ClipboardIcon} from '#/components/icons/Clipboard' import {Clipboard_Stroke2_Corner2_Rounded as ClipboardIcon} from '#/components/icons/Clipboard'
import {Filter_Stroke2_Corner0_Rounded as Filter} from '#/components/icons/Filter' import {Filter_Stroke2_Corner0_Rounded as Filter} from '#/components/icons/Filter'
@ -174,29 +168,18 @@ let PostDropdownBtn = ({
<Menu.Root> <Menu.Root>
<Menu.Trigger label={_(msg`Open post options menu`)}> <Menu.Trigger label={_(msg`Open post options menu`)}>
{({props, state}) => { {({props, state}) => {
const styles = [ return (
style,
a.rounded_full,
(state.hovered || state.focused || state.pressed) && [
web({outline: 0}),
alf.atoms.bg_contrast_25,
],
]
return isWeb ? (
<View {...props} testID={testID} style={styles}>
<FontAwesomeIcon
icon="ellipsis"
size={20}
color={defaultCtrlColor}
style={{pointerEvents: 'none'}}
/>
</View>
) : (
<Pressable <Pressable
{...props} {...props}
hitSlop={hitSlop} hitSlop={hitSlop}
testID={testID} testID={testID}
style={styles}> style={[
style,
a.rounded_full,
(state.hovered || state.pressed) && [
alf.atoms.bg_contrast_50,
],
]}>
<FontAwesomeIcon <FontAwesomeIcon
icon="ellipsis" icon="ellipsis"
size={20} size={20}

View File

@ -16,7 +16,7 @@ export function Menus() {
<View style={[a.gap_md]}> <View style={[a.gap_md]}>
<View style={[a.flex_row, a.align_start]}> <View style={[a.flex_row, a.align_start]}>
<Menu.Root control={menuControl}> <Menu.Root control={menuControl}>
<Menu.Trigger label="Open basic menu" style={[a.flex_1]}> <Menu.Trigger label="Open basic menu">
{({state, props}) => { {({state, props}) => {
return ( return (
<Text <Text