Bluesky app fork with some witchin' additions 馃挮
at twelve 120 lines 2.8 kB view raw
1import {createContext, useContext} from 'react' 2import {View, type ViewStyle} from 'react-native' 3 4import {atoms as a, tokens, useTheme} from '#/alf' 5import {type Props as SVGIconProps} from '#/components/icons/common' 6import {Text} from '#/components/Typography' 7 8const PanelContext = createContext<{active: boolean}>({active: false}) 9 10/** 11 * A nice container for Toggles. See the Threadgate dialog for an example. 12 */ 13export function Panel({ 14 children, 15 active = false, 16 adjacent, 17}: { 18 children: React.ReactNode 19 active?: boolean 20 adjacent?: 'leading' | 'trailing' | 'both' 21}) { 22 const t = useTheme() 23 24 const leading = adjacent === 'leading' || adjacent === 'both' 25 const trailing = adjacent === 'trailing' || adjacent === 'both' 26 const rounding = { 27 borderTopLeftRadius: leading 28 ? tokens.borderRadius.xs 29 : tokens.borderRadius.md, 30 borderTopRightRadius: leading 31 ? tokens.borderRadius.xs 32 : tokens.borderRadius.md, 33 borderBottomLeftRadius: trailing 34 ? tokens.borderRadius.xs 35 : tokens.borderRadius.md, 36 borderBottomRightRadius: trailing 37 ? tokens.borderRadius.xs 38 : tokens.borderRadius.md, 39 } satisfies ViewStyle 40 41 return ( 42 <View 43 style={[ 44 a.w_full, 45 a.flex_row, 46 a.align_center, 47 a.gap_sm, 48 a.px_md, 49 a.py_md, 50 {minHeight: tokens.space._2xl + tokens.space.md * 2}, 51 rounding, 52 active 53 ? {backgroundColor: t.palette.primary_50} 54 : t.atoms.bg_contrast_50, 55 ]}> 56 <PanelContext value={{active}}>{children}</PanelContext> 57 </View> 58 ) 59} 60 61export function PanelText({ 62 children, 63 icon, 64}: { 65 children: React.ReactNode 66 icon?: React.ComponentType<SVGIconProps> 67}) { 68 const t = useTheme() 69 const ctx = useContext(PanelContext) 70 71 const text = ( 72 <Text 73 style={[ 74 a.text_md, 75 a.flex_1, 76 ctx.active 77 ? [a.font_medium, t.atoms.text] 78 : [t.atoms.text_contrast_medium], 79 ]}> 80 {children} 81 </Text> 82 ) 83 84 if (icon) { 85 // eslint-disable-next-line bsky-internal/avoid-unwrapped-text 86 return ( 87 <View style={[a.flex_row, a.align_center, a.gap_xs, a.flex_1]}> 88 <PanelIcon icon={icon} /> 89 {text} 90 </View> 91 ) 92 } 93 94 return text 95} 96 97export function PanelIcon({ 98 icon: Icon, 99}: { 100 icon: React.ComponentType<SVGIconProps> 101}) { 102 const t = useTheme() 103 const ctx = useContext(PanelContext) 104 return ( 105 <Icon 106 style={[ 107 ctx.active ? t.atoms.text : t.atoms.text_contrast_medium, 108 a.flex_shrink_0, 109 ]} 110 size="md" 111 /> 112 ) 113} 114 115/** 116 * A group of panels. TODO: auto-leading/trailing 117 */ 118export function PanelGroup({children}: {children: React.ReactNode}) { 119 return <View style={[a.w_full, a.gap_2xs]}>{children}</View> 120}