Bluesky app fork with some witchin' additions 💫
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Convert button to use forwardRef (#4576)

authored by

Eric Bailey and committed by
GitHub
7d8fca56 89d99a87

+320 -301
+320 -301
src/components/Button.tsx
··· 88 88 return React.useContext(Context) 89 89 } 90 90 91 - export function Button({ 92 - children, 93 - variant, 94 - color, 95 - size, 96 - shape = 'default', 97 - label, 98 - disabled = false, 99 - style, 100 - hoverStyle: hoverStyleProp, 101 - ...rest 102 - }: ButtonProps) { 103 - const t = useTheme() 104 - const [state, setState] = React.useState({ 105 - pressed: false, 106 - hovered: false, 107 - focused: false, 108 - }) 109 - 110 - const onPressIn = React.useCallback(() => { 111 - setState(s => ({ 112 - ...s, 113 - pressed: true, 114 - })) 115 - }, [setState]) 116 - const onPressOut = React.useCallback(() => { 117 - setState(s => ({ 118 - ...s, 91 + export const Button = React.forwardRef<View, ButtonProps>( 92 + ( 93 + { 94 + children, 95 + variant, 96 + color, 97 + size, 98 + shape = 'default', 99 + label, 100 + disabled = false, 101 + style, 102 + hoverStyle: hoverStyleProp, 103 + ...rest 104 + }, 105 + ref, 106 + ) => { 107 + const t = useTheme() 108 + const [state, setState] = React.useState({ 119 109 pressed: false, 120 - })) 121 - }, [setState]) 122 - const onHoverIn = React.useCallback(() => { 123 - setState(s => ({ 124 - ...s, 125 - hovered: true, 126 - })) 127 - }, [setState]) 128 - const onHoverOut = React.useCallback(() => { 129 - setState(s => ({ 130 - ...s, 131 110 hovered: false, 132 - })) 133 - }, [setState]) 134 - const onFocus = React.useCallback(() => { 135 - setState(s => ({ 136 - ...s, 137 - focused: true, 138 - })) 139 - }, [setState]) 140 - const onBlur = React.useCallback(() => { 141 - setState(s => ({ 142 - ...s, 143 111 focused: false, 144 - })) 145 - }, [setState]) 112 + }) 146 113 147 - const {baseStyles, hoverStyles} = React.useMemo(() => { 148 - const baseStyles: ViewStyle[] = [] 149 - const hoverStyles: ViewStyle[] = [] 150 - const light = t.name === 'light' 114 + const onPressIn = React.useCallback(() => { 115 + setState(s => ({ 116 + ...s, 117 + pressed: true, 118 + })) 119 + }, [setState]) 120 + const onPressOut = React.useCallback(() => { 121 + setState(s => ({ 122 + ...s, 123 + pressed: false, 124 + })) 125 + }, [setState]) 126 + const onHoverIn = React.useCallback(() => { 127 + setState(s => ({ 128 + ...s, 129 + hovered: true, 130 + })) 131 + }, [setState]) 132 + const onHoverOut = React.useCallback(() => { 133 + setState(s => ({ 134 + ...s, 135 + hovered: false, 136 + })) 137 + }, [setState]) 138 + const onFocus = React.useCallback(() => { 139 + setState(s => ({ 140 + ...s, 141 + focused: true, 142 + })) 143 + }, [setState]) 144 + const onBlur = React.useCallback(() => { 145 + setState(s => ({ 146 + ...s, 147 + focused: false, 148 + })) 149 + }, [setState]) 151 150 152 - if (color === 'primary') { 153 - if (variant === 'solid') { 154 - if (!disabled) { 155 - baseStyles.push({ 156 - backgroundColor: t.palette.primary_500, 157 - }) 158 - hoverStyles.push({ 159 - backgroundColor: t.palette.primary_600, 160 - }) 161 - } else { 162 - baseStyles.push({ 163 - backgroundColor: t.palette.primary_700, 151 + const {baseStyles, hoverStyles} = React.useMemo(() => { 152 + const baseStyles: ViewStyle[] = [] 153 + const hoverStyles: ViewStyle[] = [] 154 + const light = t.name === 'light' 155 + 156 + if (color === 'primary') { 157 + if (variant === 'solid') { 158 + if (!disabled) { 159 + baseStyles.push({ 160 + backgroundColor: t.palette.primary_500, 161 + }) 162 + hoverStyles.push({ 163 + backgroundColor: t.palette.primary_600, 164 + }) 165 + } else { 166 + baseStyles.push({ 167 + backgroundColor: t.palette.primary_700, 168 + }) 169 + } 170 + } else if (variant === 'outline') { 171 + baseStyles.push(a.border, t.atoms.bg, { 172 + borderWidth: 1, 164 173 }) 165 - } 166 - } else if (variant === 'outline') { 167 - baseStyles.push(a.border, t.atoms.bg, { 168 - borderWidth: 1, 169 - }) 170 174 171 - if (!disabled) { 172 - baseStyles.push(a.border, { 173 - borderColor: t.palette.primary_500, 174 - }) 175 - hoverStyles.push(a.border, { 176 - backgroundColor: light 177 - ? t.palette.primary_50 178 - : t.palette.primary_950, 179 - }) 180 - } else { 181 - baseStyles.push(a.border, { 182 - borderColor: light ? t.palette.primary_200 : t.palette.primary_900, 183 - }) 175 + if (!disabled) { 176 + baseStyles.push(a.border, { 177 + borderColor: t.palette.primary_500, 178 + }) 179 + hoverStyles.push(a.border, { 180 + backgroundColor: light 181 + ? t.palette.primary_50 182 + : t.palette.primary_950, 183 + }) 184 + } else { 185 + baseStyles.push(a.border, { 186 + borderColor: light 187 + ? t.palette.primary_200 188 + : t.palette.primary_900, 189 + }) 190 + } 191 + } else if (variant === 'ghost') { 192 + if (!disabled) { 193 + baseStyles.push(t.atoms.bg) 194 + hoverStyles.push({ 195 + backgroundColor: light 196 + ? t.palette.primary_100 197 + : t.palette.primary_900, 198 + }) 199 + } 184 200 } 185 - } else if (variant === 'ghost') { 186 - if (!disabled) { 187 - baseStyles.push(t.atoms.bg) 188 - hoverStyles.push({ 189 - backgroundColor: light 190 - ? t.palette.primary_100 191 - : t.palette.primary_900, 201 + } else if (color === 'secondary') { 202 + if (variant === 'solid') { 203 + if (!disabled) { 204 + baseStyles.push({ 205 + backgroundColor: t.palette.contrast_25, 206 + }) 207 + hoverStyles.push({ 208 + backgroundColor: t.palette.contrast_50, 209 + }) 210 + } else { 211 + baseStyles.push({ 212 + backgroundColor: t.palette.contrast_100, 213 + }) 214 + } 215 + } else if (variant === 'outline') { 216 + baseStyles.push(a.border, t.atoms.bg, { 217 + borderWidth: 1, 192 218 }) 219 + 220 + if (!disabled) { 221 + baseStyles.push(a.border, { 222 + borderColor: t.palette.contrast_300, 223 + }) 224 + hoverStyles.push(t.atoms.bg_contrast_50) 225 + } else { 226 + baseStyles.push(a.border, { 227 + borderColor: t.palette.contrast_200, 228 + }) 229 + } 230 + } else if (variant === 'ghost') { 231 + if (!disabled) { 232 + baseStyles.push(t.atoms.bg) 233 + hoverStyles.push({ 234 + backgroundColor: t.palette.contrast_100, 235 + }) 236 + } 193 237 } 194 - } 195 - } else if (color === 'secondary') { 196 - if (variant === 'solid') { 197 - if (!disabled) { 198 - baseStyles.push({ 199 - backgroundColor: t.palette.contrast_25, 238 + } else if (color === 'negative') { 239 + if (variant === 'solid') { 240 + if (!disabled) { 241 + baseStyles.push({ 242 + backgroundColor: t.palette.negative_500, 243 + }) 244 + hoverStyles.push({ 245 + backgroundColor: t.palette.negative_600, 246 + }) 247 + } else { 248 + baseStyles.push({ 249 + backgroundColor: t.palette.negative_700, 250 + }) 251 + } 252 + } else if (variant === 'outline') { 253 + baseStyles.push(a.border, t.atoms.bg, { 254 + borderWidth: 1, 200 255 }) 201 - hoverStyles.push({ 202 - backgroundColor: t.palette.contrast_50, 203 - }) 204 - } else { 205 - baseStyles.push({ 206 - backgroundColor: t.palette.contrast_100, 207 - }) 208 - } 209 - } else if (variant === 'outline') { 210 - baseStyles.push(a.border, t.atoms.bg, { 211 - borderWidth: 1, 212 - }) 213 256 214 - if (!disabled) { 215 - baseStyles.push(a.border, { 216 - borderColor: t.palette.contrast_300, 217 - }) 218 - hoverStyles.push(t.atoms.bg_contrast_50) 219 - } else { 220 - baseStyles.push(a.border, { 221 - borderColor: t.palette.contrast_200, 222 - }) 223 - } 224 - } else if (variant === 'ghost') { 225 - if (!disabled) { 226 - baseStyles.push(t.atoms.bg) 227 - hoverStyles.push({ 228 - backgroundColor: t.palette.contrast_100, 229 - }) 257 + if (!disabled) { 258 + baseStyles.push(a.border, { 259 + borderColor: t.palette.negative_500, 260 + }) 261 + hoverStyles.push(a.border, { 262 + backgroundColor: light 263 + ? t.palette.negative_50 264 + : t.palette.negative_975, 265 + }) 266 + } else { 267 + baseStyles.push(a.border, { 268 + borderColor: light 269 + ? t.palette.negative_200 270 + : t.palette.negative_900, 271 + }) 272 + } 273 + } else if (variant === 'ghost') { 274 + if (!disabled) { 275 + baseStyles.push(t.atoms.bg) 276 + hoverStyles.push({ 277 + backgroundColor: light 278 + ? t.palette.negative_100 279 + : t.palette.negative_975, 280 + }) 281 + } 230 282 } 231 283 } 232 - } else if (color === 'negative') { 233 - if (variant === 'solid') { 234 - if (!disabled) { 235 - baseStyles.push({ 236 - backgroundColor: t.palette.negative_500, 237 - }) 238 - hoverStyles.push({ 239 - backgroundColor: t.palette.negative_600, 240 - }) 241 - } else { 242 - baseStyles.push({ 243 - backgroundColor: t.palette.negative_700, 244 - }) 245 - } 246 - } else if (variant === 'outline') { 247 - baseStyles.push(a.border, t.atoms.bg, { 248 - borderWidth: 1, 249 - }) 250 284 251 - if (!disabled) { 252 - baseStyles.push(a.border, { 253 - borderColor: t.palette.negative_500, 254 - }) 255 - hoverStyles.push(a.border, { 256 - backgroundColor: light 257 - ? t.palette.negative_50 258 - : t.palette.negative_975, 259 - }) 260 - } else { 261 - baseStyles.push(a.border, { 262 - borderColor: light 263 - ? t.palette.negative_200 264 - : t.palette.negative_900, 265 - }) 285 + if (shape === 'default') { 286 + if (size === 'large') { 287 + baseStyles.push( 288 + {paddingVertical: 15}, 289 + a.px_2xl, 290 + a.rounded_sm, 291 + a.gap_md, 292 + ) 293 + } else if (size === 'medium') { 294 + baseStyles.push( 295 + {paddingVertical: 12}, 296 + a.px_2xl, 297 + a.rounded_sm, 298 + a.gap_md, 299 + ) 300 + } else if (size === 'small') { 301 + baseStyles.push({paddingVertical: 9}, a.px_lg, a.rounded_sm, a.gap_sm) 302 + } else if (size === 'xsmall') { 303 + baseStyles.push({paddingVertical: 6}, a.px_sm, a.rounded_sm, a.gap_sm) 304 + } else if (size === 'tiny') { 305 + baseStyles.push({paddingVertical: 4}, a.px_sm, a.rounded_xs, a.gap_xs) 266 306 } 267 - } else if (variant === 'ghost') { 268 - if (!disabled) { 269 - baseStyles.push(t.atoms.bg) 270 - hoverStyles.push({ 271 - backgroundColor: light 272 - ? t.palette.negative_100 273 - : t.palette.negative_975, 274 - }) 307 + } else if (shape === 'round' || shape === 'square') { 308 + if (size === 'large') { 309 + if (shape === 'round') { 310 + baseStyles.push({height: 54, width: 54}) 311 + } else { 312 + baseStyles.push({height: 50, width: 50}) 313 + } 314 + } else if (size === 'small') { 315 + baseStyles.push({height: 34, width: 34}) 316 + } else if (size === 'xsmall') { 317 + baseStyles.push({height: 28, width: 28}) 318 + } else if (size === 'tiny') { 319 + baseStyles.push({height: 20, width: 20}) 275 320 } 276 - } 277 - } 278 321 279 - if (shape === 'default') { 280 - if (size === 'large') { 281 - baseStyles.push({paddingVertical: 15}, a.px_2xl, a.rounded_sm, a.gap_md) 282 - } else if (size === 'medium') { 283 - baseStyles.push({paddingVertical: 12}, a.px_2xl, a.rounded_sm, a.gap_md) 284 - } else if (size === 'small') { 285 - baseStyles.push({paddingVertical: 9}, a.px_lg, a.rounded_sm, a.gap_sm) 286 - } else if (size === 'xsmall') { 287 - baseStyles.push({paddingVertical: 6}, a.px_sm, a.rounded_sm, a.gap_sm) 288 - } else if (size === 'tiny') { 289 - baseStyles.push({paddingVertical: 4}, a.px_sm, a.rounded_xs, a.gap_xs) 290 - } 291 - } else if (shape === 'round' || shape === 'square') { 292 - if (size === 'large') { 293 322 if (shape === 'round') { 294 - baseStyles.push({height: 54, width: 54}) 295 - } else { 296 - baseStyles.push({height: 50, width: 50}) 323 + baseStyles.push(a.rounded_full) 324 + } else if (shape === 'square') { 325 + if (size === 'tiny') { 326 + baseStyles.push(a.rounded_xs) 327 + } else { 328 + baseStyles.push(a.rounded_sm) 329 + } 297 330 } 298 - } else if (size === 'small') { 299 - baseStyles.push({height: 34, width: 34}) 300 - } else if (size === 'xsmall') { 301 - baseStyles.push({height: 28, width: 28}) 302 - } else if (size === 'tiny') { 303 - baseStyles.push({height: 20, width: 20}) 304 331 } 305 332 306 - if (shape === 'round') { 307 - baseStyles.push(a.rounded_full) 308 - } else if (shape === 'square') { 309 - if (size === 'tiny') { 310 - baseStyles.push(a.rounded_xs) 311 - } else { 312 - baseStyles.push(a.rounded_sm) 313 - } 333 + return { 334 + baseStyles, 335 + hoverStyles, 314 336 } 315 - } 337 + }, [t, variant, color, size, shape, disabled]) 316 338 317 - return { 318 - baseStyles, 319 - hoverStyles, 320 - } 321 - }, [t, variant, color, size, shape, disabled]) 322 - 323 - const {gradientColors, gradientHoverColors, gradientLocations} = 324 - React.useMemo(() => { 325 - const colors: string[] = [] 326 - const hoverColors: string[] = [] 327 - const locations: number[] = [] 328 - const gradient = { 329 - primary: tokens.gradients.sky, 330 - secondary: tokens.gradients.sky, 331 - negative: tokens.gradients.sky, 332 - gradient_sky: tokens.gradients.sky, 333 - gradient_midnight: tokens.gradients.midnight, 334 - gradient_sunrise: tokens.gradients.sunrise, 335 - gradient_sunset: tokens.gradients.sunset, 336 - gradient_nordic: tokens.gradients.nordic, 337 - gradient_bonfire: tokens.gradients.bonfire, 338 - }[color || 'primary'] 339 + const {gradientColors, gradientHoverColors, gradientLocations} = 340 + React.useMemo(() => { 341 + const colors: string[] = [] 342 + const hoverColors: string[] = [] 343 + const locations: number[] = [] 344 + const gradient = { 345 + primary: tokens.gradients.sky, 346 + secondary: tokens.gradients.sky, 347 + negative: tokens.gradients.sky, 348 + gradient_sky: tokens.gradients.sky, 349 + gradient_midnight: tokens.gradients.midnight, 350 + gradient_sunrise: tokens.gradients.sunrise, 351 + gradient_sunset: tokens.gradients.sunset, 352 + gradient_nordic: tokens.gradients.nordic, 353 + gradient_bonfire: tokens.gradients.bonfire, 354 + }[color || 'primary'] 339 355 340 - if (variant === 'gradient') { 341 - colors.push(...gradient.values.map(([_, color]) => color)) 342 - hoverColors.push(...gradient.values.map(_ => gradient.hover_value)) 343 - locations.push(...gradient.values.map(([location, _]) => location)) 344 - } 356 + if (variant === 'gradient') { 357 + colors.push(...gradient.values.map(([_, color]) => color)) 358 + hoverColors.push(...gradient.values.map(_ => gradient.hover_value)) 359 + locations.push(...gradient.values.map(([location, _]) => location)) 360 + } 345 361 346 - return { 347 - gradientColors: colors, 348 - gradientHoverColors: hoverColors, 349 - gradientLocations: locations, 350 - } 351 - }, [variant, color]) 362 + return { 363 + gradientColors: colors, 364 + gradientHoverColors: hoverColors, 365 + gradientLocations: locations, 366 + } 367 + }, [variant, color]) 352 368 353 - const context = React.useMemo<ButtonContext>( 354 - () => ({ 355 - ...state, 356 - variant, 357 - color, 358 - size, 359 - disabled: disabled || false, 360 - }), 361 - [state, variant, color, size, disabled], 362 - ) 369 + const context = React.useMemo<ButtonContext>( 370 + () => ({ 371 + ...state, 372 + variant, 373 + color, 374 + size, 375 + disabled: disabled || false, 376 + }), 377 + [state, variant, color, size, disabled], 378 + ) 363 379 364 - const flattenedBaseStyles = flatten(baseStyles) 380 + const flattenedBaseStyles = flatten(baseStyles) 365 381 366 - return ( 367 - <Pressable 368 - role="button" 369 - accessibilityHint={undefined} // optional 370 - {...rest} 371 - aria-label={label} 372 - aria-pressed={state.pressed} 373 - accessibilityLabel={label} 374 - disabled={disabled || false} 375 - accessibilityState={{ 376 - disabled: disabled || false, 377 - }} 378 - style={[ 379 - a.flex_row, 380 - a.align_center, 381 - a.justify_center, 382 - flattenedBaseStyles, 383 - flatten(style), 384 - ...(state.hovered || state.pressed 385 - ? [hoverStyles, flatten(hoverStyleProp)] 386 - : []), 387 - ]} 388 - onPressIn={onPressIn} 389 - onPressOut={onPressOut} 390 - onHoverIn={onHoverIn} 391 - onHoverOut={onHoverOut} 392 - onFocus={onFocus} 393 - onBlur={onBlur}> 394 - {variant === 'gradient' && ( 395 - <View 396 - style={[ 397 - a.absolute, 398 - a.inset_0, 399 - a.overflow_hidden, 400 - {borderRadius: flattenedBaseStyles.borderRadius}, 401 - ]}> 402 - <LinearGradient 403 - colors={ 404 - state.hovered || state.pressed 405 - ? gradientHoverColors 406 - : gradientColors 407 - } 408 - locations={gradientLocations} 409 - start={{x: 0, y: 0}} 410 - end={{x: 1, y: 1}} 411 - style={[a.absolute, a.inset_0]} 412 - /> 413 - </View> 414 - )} 415 - <Context.Provider value={context}> 416 - {typeof children === 'function' ? children(context) : children} 417 - </Context.Provider> 418 - </Pressable> 419 - ) 420 - } 382 + return ( 383 + <Pressable 384 + role="button" 385 + accessibilityHint={undefined} // optional 386 + {...rest} 387 + ref={ref} 388 + aria-label={label} 389 + aria-pressed={state.pressed} 390 + accessibilityLabel={label} 391 + disabled={disabled || false} 392 + accessibilityState={{ 393 + disabled: disabled || false, 394 + }} 395 + style={[ 396 + a.flex_row, 397 + a.align_center, 398 + a.justify_center, 399 + flattenedBaseStyles, 400 + flatten(style), 401 + ...(state.hovered || state.pressed 402 + ? [hoverStyles, flatten(hoverStyleProp)] 403 + : []), 404 + ]} 405 + onPressIn={onPressIn} 406 + onPressOut={onPressOut} 407 + onHoverIn={onHoverIn} 408 + onHoverOut={onHoverOut} 409 + onFocus={onFocus} 410 + onBlur={onBlur}> 411 + {variant === 'gradient' && ( 412 + <View 413 + style={[ 414 + a.absolute, 415 + a.inset_0, 416 + a.overflow_hidden, 417 + {borderRadius: flattenedBaseStyles.borderRadius}, 418 + ]}> 419 + <LinearGradient 420 + colors={ 421 + state.hovered || state.pressed 422 + ? gradientHoverColors 423 + : gradientColors 424 + } 425 + locations={gradientLocations} 426 + start={{x: 0, y: 0}} 427 + end={{x: 1, y: 1}} 428 + style={[a.absolute, a.inset_0]} 429 + /> 430 + </View> 431 + )} 432 + <Context.Provider value={context}> 433 + {typeof children === 'function' ? children(context) : children} 434 + </Context.Provider> 435 + </Pressable> 436 + ) 437 + }, 438 + ) 439 + Button.displayName = 'Button' 421 440 422 441 export function useSharedButtonTextStyles() { 423 442 const t = useTheme()