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 style?: StyleProp<ViewStyle>
25}
26
27export type ItemProps = Omit<MenuItemProps, 'onPress' | 'children'> & {
28 // remove default styles (i.e. for emoji reactions)
29 unstyled?: boolean
30 onPress: (evt?: GestureResponderEvent) => void
31 children?: React.ReactNode | ((hovered: boolean) => React.ReactNode)
32 // absolute position of the parent element. if undefined, assumed to
33 // be in the context menu. use this if using AuxiliaryView
34 position?: Measurement
35}
36
37export type Measurement = {
38 x: number
39 y: number
40 width: number
41 height: number
42}
43
44export type ContextType = {
45 isOpen: boolean
46 measurement: Measurement | null
47 /* Spring animation between 0 and 1 */
48 animationSV: SharedValue<number>
49 /* Translation in Y axis to ensure everything's onscreen */
50 translationSV: SharedValue<number>
51 mode: 'full' | 'auxiliary-only'
52 open: (evt: Measurement, mode: 'full' | 'auxiliary-only') => void
53 returnLocationSV: SharedValue<{x: number; y: number} | null>
54 close: () => void
55 registerHoverable: (
56 id: string,
57 rect: Measurement,
58 onTouchUp: () => void,
59 ) => void
60 hoverablesSV: SharedValue<Record<string, {id: string; rect: Measurement}>>
61 hoveredMenuItem: string | null
62 setHoveredMenuItem: React.Dispatch<React.SetStateAction<string | null>>
63 onTouchUpMenuItem: (id: string) => void
64}
65
66export type MenuContextType = {
67 align: 'left' | 'right'
68 xOffset: number
69}
70
71export type ItemContextType = {
72 disabled: boolean
73}
74
75export type TriggerProps = {
76 children(props: TriggerChildProps): React.ReactNode
77 label: string
78 /**
79 * When activated, this is the accessibility label for the entire thing that has been triggered.
80 * For example, if the trigger is a message bubble, use the message content.
81 *
82 * @platform ios, android
83 */
84 contentLabel: string
85 hint?: string
86 role?: AccessibilityRole
87 style?: StyleProp<ViewStyle>
88 /**
89 * Callback for single taps. Composed with the double-tap and
90 * press-and-hold gestures via `Gesture.Exclusive`, so a double tap
91 * does not also fire this handler.
92 *
93 * @platform ios, android
94 */
95 onTap?: () => void
96}
97export type TriggerChildProps =
98 | {
99 IS_NATIVE: true
100 control: {
101 isOpen: boolean
102 open: (mode: 'full' | 'auxiliary-only') => void
103 }
104 state: {
105 hovered: false
106 focused: false
107 pressed: false
108 }
109 /**
110 * We don't necessarily know what these will be spread on to, so we
111 * should add props one-by-one.
112 *
113 * On web, these properties are applied to a parent `Pressable`, so this
114 * object is empty.
115 */
116 props: {
117 ref: null
118 onPress: null
119 onFocus: null
120 onBlur: null
121 onPressIn: null
122 onPressOut: null
123 accessibilityHint: null
124 accessibilityLabel: string
125 accessibilityRole: null
126 }
127 }
128 | {
129 IS_NATIVE: false
130 control: Dialog.DialogOuterProps['control']
131 state: {
132 hovered: false
133 focused: false
134 pressed: false
135 }
136 props: RadixPassThroughTriggerProps & {
137 onPress: () => void
138 onFocus: () => void
139 onBlur: () => void
140 onMouseEnter: () => void
141 onMouseLeave: () => void
142 accessibilityHint?: string
143 accessibilityLabel: string
144 accessibilityRole: AccessibilityRole
145 }
146 }