mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
1import {Children, createContext, useContext, useMemo} from 'react'
2import {View} from 'react-native'
3import {Popover} from 'radix-ui'
4
5import {atoms as a, flatten, select, useTheme} from '#/alf'
6import {transparentifyColor} from '#/alf/util/colorGeneration'
7import {
8 ARROW_SIZE,
9 BUBBLE_MAX_WIDTH,
10 MIN_EDGE_SPACE,
11} from '#/components/Tooltip/const'
12import {Text} from '#/components/Typography'
13
14type TooltipContextType = {
15 position: 'top' | 'bottom'
16 onVisibleChange: (open: boolean) => void
17}
18
19const TooltipContext = createContext<TooltipContextType>({
20 position: 'bottom',
21 onVisibleChange: () => {},
22})
23TooltipContext.displayName = 'TooltipContext'
24
25export function Outer({
26 children,
27 position = 'bottom',
28 visible,
29 onVisibleChange,
30}: {
31 children: React.ReactNode
32 position?: 'top' | 'bottom'
33 visible: boolean
34 onVisibleChange: (visible: boolean) => void
35}) {
36 const ctx = useMemo(
37 () => ({position, onVisibleChange}),
38 [position, onVisibleChange],
39 )
40 return (
41 <Popover.Root open={visible} onOpenChange={onVisibleChange}>
42 <TooltipContext.Provider value={ctx}>{children}</TooltipContext.Provider>
43 </Popover.Root>
44 )
45}
46
47export function Target({children}: {children: React.ReactNode}) {
48 return (
49 <Popover.Trigger asChild>
50 <View collapsable={false}>{children}</View>
51 </Popover.Trigger>
52 )
53}
54
55export function Content({
56 children,
57 label,
58}: {
59 children: React.ReactNode
60 label: string
61}) {
62 const t = useTheme()
63 const {position, onVisibleChange} = useContext(TooltipContext)
64 return (
65 <Popover.Portal>
66 <Popover.Content
67 className="radix-popover-content"
68 aria-label={label}
69 side={position}
70 sideOffset={4}
71 collisionPadding={MIN_EDGE_SPACE}
72 onInteractOutside={() => onVisibleChange(false)}
73 style={flatten([
74 a.rounded_sm,
75 select(t.name, {
76 light: t.atoms.bg,
77 dark: t.atoms.bg_contrast_100,
78 dim: t.atoms.bg_contrast_100,
79 }),
80 {
81 minWidth: 'max-content',
82 boxShadow: select(t.name, {
83 light: `0 0 24px ${transparentifyColor(t.palette.black, 0.2)}`,
84 dark: `0 0 24px ${transparentifyColor(t.palette.black, 0.2)}`,
85 dim: `0 0 24px ${transparentifyColor(t.palette.black, 0.2)}`,
86 }),
87 },
88 ])}>
89 <Popover.Arrow
90 width={ARROW_SIZE}
91 height={ARROW_SIZE / 2}
92 fill={select(t.name, {
93 light: t.atoms.bg.backgroundColor,
94 dark: t.atoms.bg_contrast_100.backgroundColor,
95 dim: t.atoms.bg_contrast_100.backgroundColor,
96 })}
97 />
98 <View style={[a.px_md, a.py_sm, {maxWidth: BUBBLE_MAX_WIDTH}]}>
99 {children}
100 </View>
101 </Popover.Content>
102 </Popover.Portal>
103 )
104}
105
106export function TextBubble({children}: {children: React.ReactNode}) {
107 const c = Children.toArray(children)
108 return (
109 <Content label={c.join(' ')}>
110 <View style={[a.gap_xs]}>
111 {c.map((child, i) => (
112 <Text key={i} style={[a.text_sm, a.leading_snug]}>
113 {child}
114 </Text>
115 ))}
116 </View>
117 </Content>
118 )
119}