forked from
jollywhoppers.com/witchsky.app
Bluesky app fork with some witchin' additions 馃挮
1import {
2 type AccessibilityRole,
3 type GestureResponderEvent,
4 type StyleProp,
5 type ViewStyle,
6} from 'react-native'
7import {type SharedValue} from 'react-native-reanimated'
8
9import type * as Dialog from '#/components/Dialog'
10import {
11 type ItemProps as MenuItemProps,
12 type RadixPassThroughTriggerProps,
13} from '#/components/Menu/types'
14
15export type {
16 GroupProps,
17 ItemIconProps,
18 ItemTextProps,
19} from '#/components/Menu/types'
20
21export type AuxiliaryViewProps = {
22 children?: React.ReactNode
23 align?: 'left' | 'right'
24}
25
26export type ItemProps = Omit<MenuItemProps, 'onPress' | 'children'> & {
27 // remove default styles (i.e. for emoji reactions)
28 unstyled?: boolean
29 onPress: (evt?: GestureResponderEvent) => void
30 children?: React.ReactNode | ((hovered: boolean) => React.ReactNode)
31 // absolute position of the parent element. if undefined, assumed to
32 // be in the context menu. use this if using AuxiliaryView
33 position?: Measurement
34}
35
36export type Measurement = {
37 x: number
38 y: number
39 width: number
40 height: number
41}
42
43export type ContextType = {
44 isOpen: boolean
45 measurement: Measurement | null
46 /* Spring animation between 0 and 1 */
47 animationSV: SharedValue<number>
48 /* Translation in Y axis to ensure everything's onscreen */
49 translationSV: SharedValue<number>
50 mode: 'full' | 'auxiliary-only'
51 open: (evt: Measurement, mode: 'full' | 'auxiliary-only') => void
52 returnLocationSV: SharedValue<{x: number; y: number} | null>
53 close: () => void
54 registerHoverable: (
55 id: string,
56 rect: Measurement,
57 onTouchUp: () => void,
58 ) => void
59 hoverablesSV: SharedValue<Record<string, {id: string; rect: Measurement}>>
60 hoveredMenuItem: string | null
61 setHoveredMenuItem: React.Dispatch<React.SetStateAction<string | null>>
62 onTouchUpMenuItem: (id: string) => void
63}
64
65export type MenuContextType = {
66 align: 'left' | 'right'
67}
68
69export type ItemContextType = {
70 disabled: boolean
71}
72
73export type TriggerProps = {
74 children(props: TriggerChildProps): React.ReactNode
75 label: string
76 /**
77 * When activated, this is the accessibility label for the entire thing that has been triggered.
78 * For example, if the trigger is a message bubble, use the message content.
79 *
80 * @platform ios, android
81 */
82 contentLabel: string
83 hint?: string
84 role?: AccessibilityRole
85 style?: StyleProp<ViewStyle>
86}
87export type TriggerChildProps =
88 | {
89 IS_NATIVE: true
90 control: {
91 isOpen: boolean
92 open: (mode: 'full' | 'auxiliary-only') => void
93 }
94 state: {
95 hovered: false
96 focused: false
97 pressed: false
98 }
99 /**
100 * We don't necessarily know what these will be spread on to, so we
101 * should add props one-by-one.
102 *
103 * On web, these properties are applied to a parent `Pressable`, so this
104 * object is empty.
105 */
106 props: {
107 ref: null
108 onPress: null
109 onFocus: null
110 onBlur: null
111 onPressIn: null
112 onPressOut: null
113 accessibilityHint: null
114 accessibilityLabel: string
115 accessibilityRole: null
116 }
117 }
118 | {
119 IS_NATIVE: false
120 control: Dialog.DialogOuterProps['control']
121 state: {
122 hovered: false
123 focused: false
124 pressed: false
125 }
126 props: RadixPassThroughTriggerProps & {
127 onPress: () => void
128 onFocus: () => void
129 onBlur: () => void
130 onMouseEnter: () => void
131 onMouseLeave: () => void
132 accessibilityHint?: string
133 accessibilityLabel: string
134 accessibilityRole: AccessibilityRole
135 }
136 }