forked from
jollywhoppers.com/witchsky.app
Bluesky app fork with some witchin' additions 馃挮
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}