mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
at samuel/exp-cli 235 lines 5.5 kB view raw
1import React from 'react' 2import {type GestureResponderEvent, View} from 'react-native' 3import {msg} from '@lingui/macro' 4import {useLingui} from '@lingui/react' 5 6import { 7 atoms as a, 8 useBreakpoints, 9 useTheme, 10 type ViewStyleProp, 11 web, 12} from '#/alf' 13import {Button, type ButtonColor, ButtonText} from '#/components/Button' 14import * as Dialog from '#/components/Dialog' 15import {Text} from '#/components/Typography' 16import {type BottomSheetViewProps} from '../../modules/bottom-sheet' 17 18export { 19 type DialogControlProps as PromptControlProps, 20 useDialogControl as usePromptControl, 21} from '#/components/Dialog' 22 23const Context = React.createContext<{ 24 titleId: string 25 descriptionId: string 26}>({ 27 titleId: '', 28 descriptionId: '', 29}) 30 31export function Outer({ 32 children, 33 control, 34 testID, 35 nativeOptions, 36}: React.PropsWithChildren<{ 37 control: Dialog.DialogControlProps 38 testID?: string 39 nativeOptions?: Omit<BottomSheetViewProps, 'children'> 40}>) { 41 const titleId = React.useId() 42 const descriptionId = React.useId() 43 44 const context = React.useMemo( 45 () => ({titleId, descriptionId}), 46 [titleId, descriptionId], 47 ) 48 49 return ( 50 <Dialog.Outer 51 control={control} 52 testID={testID} 53 webOptions={{alignCenter: true}} 54 nativeOptions={{preventExpansion: true, ...nativeOptions}}> 55 <Dialog.Handle /> 56 <Context.Provider value={context}> 57 <Dialog.ScrollableInner 58 accessibilityLabelledBy={titleId} 59 accessibilityDescribedBy={descriptionId} 60 style={web({maxWidth: 400})}> 61 {children} 62 </Dialog.ScrollableInner> 63 </Context.Provider> 64 </Dialog.Outer> 65 ) 66} 67 68export function TitleText({ 69 children, 70 style, 71}: React.PropsWithChildren<ViewStyleProp>) { 72 const {titleId} = React.useContext(Context) 73 return ( 74 <Text 75 nativeID={titleId} 76 style={[ 77 a.flex_1, 78 a.text_2xl, 79 a.font_bold, 80 a.pb_sm, 81 a.leading_snug, 82 style, 83 ]}> 84 {children} 85 </Text> 86 ) 87} 88 89export function DescriptionText({ 90 children, 91 selectable, 92}: React.PropsWithChildren<{selectable?: boolean}>) { 93 const t = useTheme() 94 const {descriptionId} = React.useContext(Context) 95 return ( 96 <Text 97 nativeID={descriptionId} 98 selectable={selectable} 99 style={[a.text_md, a.leading_snug, t.atoms.text_contrast_high, a.pb_lg]}> 100 {children} 101 </Text> 102 ) 103} 104 105export function Actions({children}: React.PropsWithChildren<{}>) { 106 const {gtMobile} = useBreakpoints() 107 108 return ( 109 <View 110 style={[ 111 a.w_full, 112 a.gap_md, 113 a.justify_end, 114 gtMobile 115 ? [a.flex_row, a.flex_row_reverse, a.justify_start] 116 : [a.flex_col], 117 ]}> 118 {children} 119 </View> 120 ) 121} 122 123export function Cancel({ 124 cta, 125}: { 126 /** 127 * Optional i18n string. If undefined, it will default to "Cancel". 128 */ 129 cta?: string 130}) { 131 const {_} = useLingui() 132 const {gtMobile} = useBreakpoints() 133 const {close} = Dialog.useDialogContext() 134 const onPress = React.useCallback(() => { 135 close() 136 }, [close]) 137 138 return ( 139 <Button 140 variant="solid" 141 color="secondary" 142 size={gtMobile ? 'small' : 'large'} 143 label={cta || _(msg`Cancel`)} 144 onPress={onPress}> 145 <ButtonText>{cta || _(msg`Cancel`)}</ButtonText> 146 </Button> 147 ) 148} 149 150export function Action({ 151 onPress, 152 color = 'primary', 153 cta, 154 testID, 155}: { 156 /** 157 * Callback to run when the action is pressed. The method is called _after_ 158 * the dialog closes. 159 * 160 * Note: The dialog will close automatically when the action is pressed, you 161 * should NOT close the dialog as a side effect of this method. 162 */ 163 onPress: (e: GestureResponderEvent) => void 164 color?: ButtonColor 165 /** 166 * Optional i18n string. If undefined, it will default to "Confirm". 167 */ 168 cta?: string 169 testID?: string 170}) { 171 const {_} = useLingui() 172 const {gtMobile} = useBreakpoints() 173 const {close} = Dialog.useDialogContext() 174 const handleOnPress = React.useCallback( 175 (e: GestureResponderEvent) => { 176 close(() => onPress?.(e)) 177 }, 178 [close, onPress], 179 ) 180 181 return ( 182 <Button 183 variant="solid" 184 color={color} 185 size={gtMobile ? 'small' : 'large'} 186 label={cta || _(msg`Confirm`)} 187 onPress={handleOnPress} 188 testID={testID}> 189 <ButtonText>{cta || _(msg`Confirm`)}</ButtonText> 190 </Button> 191 ) 192} 193 194export function Basic({ 195 control, 196 title, 197 description, 198 cancelButtonCta, 199 confirmButtonCta, 200 onConfirm, 201 confirmButtonColor, 202 showCancel = true, 203}: React.PropsWithChildren<{ 204 control: Dialog.DialogOuterProps['control'] 205 title: string 206 description?: string 207 cancelButtonCta?: string 208 confirmButtonCta?: string 209 /** 210 * Callback to run when the Confirm button is pressed. The method is called 211 * _after_ the dialog closes. 212 * 213 * Note: The dialog will close automatically when the action is pressed, you 214 * should NOT close the dialog as a side effect of this method. 215 */ 216 onConfirm: (e: GestureResponderEvent) => void 217 confirmButtonColor?: ButtonColor 218 showCancel?: boolean 219}>) { 220 return ( 221 <Outer control={control} testID="confirmModal"> 222 <TitleText>{title}</TitleText> 223 {description && <DescriptionText>{description}</DescriptionText>} 224 <Actions> 225 <Action 226 cta={confirmButtonCta} 227 onPress={onConfirm} 228 color={confirmButtonColor} 229 testID="confirmBtn" 230 /> 231 {showCancel && <Cancel cta={cancelButtonCta} />} 232 </Actions> 233 </Outer> 234 ) 235}