Bluesky app fork with some witchin' additions 馃挮
at jean/pds-label 150 lines 3.4 kB view raw
1import {createContext, useContext} from 'react' 2import {type StyleProp, View, type ViewStyle} from 'react-native' 3 4import {atoms as a, useBreakpoints, useTheme} from '#/alf' 5import {Button as BaseButton, type ButtonProps} from '#/components/Button' 6import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfoIcon} from '#/components/icons/CircleInfo' 7import {CircleX_Stroke2_Corner0_Rounded as CircleXIcon} from '#/components/icons/CircleX' 8import {Warning_Stroke2_Corner0_Rounded as WarningIcon} from '#/components/icons/Warning' 9import {Text as BaseText, type TextProps} from '#/components/Typography' 10import {EmojiSad_Stroke2_Corner0_Rounded as EmojiSadIcon} from './icons/Emoji' 11 12type Context = { 13 type: 'info' | 'tip' | 'warning' | 'error' | 'apology' 14} 15 16const Context = createContext<Context>({ 17 type: 'info', 18}) 19Context.displayName = 'AdmonitionContext' 20 21export function Icon() { 22 const t = useTheme() 23 const {type} = useContext(Context) 24 const Icon = { 25 info: CircleInfoIcon, 26 tip: CircleInfoIcon, 27 warning: WarningIcon, 28 error: CircleXIcon, 29 apology: EmojiSadIcon, 30 }[type] 31 const fill = { 32 info: t.atoms.text_contrast_medium.color, 33 tip: t.palette.primary_500, 34 warning: t.palette.yellow, 35 error: t.palette.negative_500, 36 apology: t.atoms.text_contrast_medium.color, 37 }[type] 38 return <Icon fill={fill} size="md" /> 39} 40 41export function Content({ 42 children, 43 style, 44 ...rest 45}: { 46 children: React.ReactNode 47 style?: StyleProp<ViewStyle> 48}) { 49 return ( 50 <View 51 style={[a.gap_sm, a.flex_1, {minHeight: 20}, a.justify_center, style]} 52 {...rest}> 53 {children} 54 </View> 55 ) 56} 57 58export function Text({ 59 children, 60 style, 61 ...rest 62}: Pick<TextProps, 'children' | 'style'>) { 63 return ( 64 <BaseText {...rest} style={[a.text_sm, a.leading_snug, a.pr_md, style]}> 65 {children} 66 </BaseText> 67 ) 68} 69 70export function Button({ 71 children, 72 ...props 73}: Omit<ButtonProps, 'size' | 'variant'>) { 74 return ( 75 <BaseButton size="tiny" {...props}> 76 {children} 77 </BaseButton> 78 ) 79} 80 81export function Row({ 82 children, 83 style, 84}: { 85 children: React.ReactNode 86 style?: StyleProp<ViewStyle> 87}) { 88 return ( 89 <View style={[a.w_full, a.flex_row, a.align_start, a.gap_sm, style]}> 90 {children} 91 </View> 92 ) 93} 94 95export function Outer({ 96 children, 97 type = 'info', 98 style, 99}: { 100 children: React.ReactNode 101 type?: Context['type'] 102 style?: StyleProp<ViewStyle> 103}) { 104 const t = useTheme() 105 const {gtMobile} = useBreakpoints() 106 const borderColor = { 107 info: t.atoms.border_contrast_high.borderColor, 108 tip: t.palette.primary_500, 109 warning: t.palette.yellow, 110 error: t.palette.negative_500, 111 apology: t.atoms.border_contrast_high.borderColor, 112 }[type] 113 return ( 114 <Context.Provider value={{type}}> 115 <View 116 style={[ 117 gtMobile ? a.p_md : a.p_sm, 118 a.p_md, 119 a.rounded_sm, 120 a.border, 121 t.atoms.bg, 122 {borderColor}, 123 style, 124 ]}> 125 {children} 126 </View> 127 </Context.Provider> 128 ) 129} 130 131export function Admonition({ 132 children, 133 type, 134 style, 135}: { 136 children: TextProps['children'] 137 type?: Context['type'] 138 style?: StyleProp<ViewStyle> 139}) { 140 return ( 141 <Outer type={type} style={style}> 142 <Row> 143 <Icon /> 144 <Content> 145 <Text>{children}</Text> 146 </Content> 147 </Row> 148 </Outer> 149 ) 150}