From b8afb935f4eafbe64e83512ec5a97bb7b38a6ecb Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Tue, 12 Mar 2024 11:23:01 -0500 Subject: [PATCH] Unwrap Menu.Trigger on web (#3182) --- src/components/Menu/index.web.tsx | 65 +++++++++++++-------- src/components/Menu/types.ts | 28 ++++++++- src/view/com/util/forms/PostDropdownBtn.tsx | 37 ++++-------- src/view/screens/Storybook/Menus.tsx | 2 +- 4 files changed, 78 insertions(+), 54 deletions(-) diff --git a/src/components/Menu/index.web.tsx b/src/components/Menu/index.web.tsx index 054e51b01e..f23c39ced5 100644 --- a/src/components/Menu/index.web.tsx +++ b/src/components/Menu/index.web.tsx @@ -1,3 +1,5 @@ +/* eslint-disable react/prop-types */ + import React from 'react' import {View, Pressable} from 'react-native' import * as DropdownMenu from '@radix-ui/react-dropdown-menu' @@ -14,6 +16,7 @@ import { GroupProps, ItemTextProps, ItemIconProps, + RadixPassThroughTriggerProps, } from '#/components/Menu/types' 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 + }, + ) => 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 { state: hovered, @@ -87,28 +107,27 @@ export function Trigger({children, label, style}: TriggerProps) { return ( - control.open()} - {...web({ - onMouseEnter, - onMouseLeave, - })}> - {children({ - isNative: false, - control, - state: { - hovered, - focused, - pressed: false, - }, - props: {}, - })} - + + {props => + children({ + isNative: false, + control, + state: { + hovered, + focused, + pressed: false, + }, + props: { + ...props, + onFocus: onFocus, + onBlur: onBlur, + onMouseEnter, + onMouseLeave, + accessibilityLabel: label, + }, + }) + } + ) } diff --git a/src/components/Menu/types.ts b/src/components/Menu/types.ts index 2f52e63906..7d04a33441 100644 --- a/src/components/Menu/types.ts +++ b/src/components/Menu/types.ts @@ -1,5 +1,9 @@ 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 * as Dialog from '#/components/Dialog' @@ -9,7 +13,19 @@ export type ContextType = { 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 label: string } @@ -52,7 +68,13 @@ export type TriggerChildProps = */ pressed: false } - props: {} + props: RadixPassThroughTriggerProps & { + onFocus: () => void + onBlur: () => void + onMouseEnter: () => void + onMouseLeave: () => void + accessibilityLabel: string + } } export type ItemProps = React.PropsWithChildren< diff --git a/src/view/com/util/forms/PostDropdownBtn.tsx b/src/view/com/util/forms/PostDropdownBtn.tsx index 6f2ae55b2c..3c1a736f3f 100644 --- a/src/view/com/util/forms/PostDropdownBtn.tsx +++ b/src/view/com/util/forms/PostDropdownBtn.tsx @@ -1,11 +1,5 @@ import React, {memo} from 'react' -import { - StyleProp, - ViewStyle, - Pressable, - View, - PressableProps, -} from 'react-native' +import {StyleProp, ViewStyle, Pressable, PressableProps} from 'react-native' import Clipboard from '@react-native-clipboard/clipboard' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {useNavigation} from '@react-navigation/native' @@ -38,7 +32,7 @@ import {isWeb} from '#/platform/detection' import {richTextToString} from '#/lib/strings/rich-text-helpers' 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 {Clipboard_Stroke2_Corner2_Rounded as ClipboardIcon} from '#/components/icons/Clipboard' import {Filter_Stroke2_Corner0_Rounded as Filter} from '#/components/icons/Filter' @@ -174,29 +168,18 @@ let PostDropdownBtn = ({ {({props, state}) => { - const styles = [ - style, - a.rounded_full, - (state.hovered || state.focused || state.pressed) && [ - web({outline: 0}), - alf.atoms.bg_contrast_25, - ], - ] - return isWeb ? ( - - - - ) : ( + return ( + style={[ + style, + a.rounded_full, + (state.hovered || state.pressed) && [ + alf.atoms.bg_contrast_50, + ], + ]}> - + {({state, props}) => { return (