about summary refs log tree commit diff
path: root/src/components/Tooltip/index.web.tsx
blob: fc5808d7ad8f473a1922107290c9c5dd3a5c7b68 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
import {Children, createContext, useContext, useMemo} from 'react'
import {View} from 'react-native'
import {Popover} from 'radix-ui'

import {atoms as a, flatten, select, useTheme} from '#/alf'
import {transparentifyColor} from '#/alf/util/colorGeneration'
import {
  ARROW_SIZE,
  BUBBLE_MAX_WIDTH,
  MIN_EDGE_SPACE,
} from '#/components/Tooltip/const'
import {Text} from '#/components/Typography'

type TooltipContextType = {
  position: 'top' | 'bottom'
  onVisibleChange: (open: boolean) => void
}

const TooltipContext = createContext<TooltipContextType>({
  position: 'bottom',
  onVisibleChange: () => {},
})

export function Outer({
  children,
  position = 'bottom',
  visible,
  onVisibleChange,
}: {
  children: React.ReactNode
  position?: 'top' | 'bottom'
  visible: boolean
  onVisibleChange: (visible: boolean) => void
}) {
  const ctx = useMemo(
    () => ({position, onVisibleChange}),
    [position, onVisibleChange],
  )
  return (
    <Popover.Root open={visible} onOpenChange={onVisibleChange}>
      <TooltipContext.Provider value={ctx}>{children}</TooltipContext.Provider>
    </Popover.Root>
  )
}

export function Target({children}: {children: React.ReactNode}) {
  return (
    <Popover.Trigger asChild>
      <View collapsable={false}>{children}</View>
    </Popover.Trigger>
  )
}

export function Content({
  children,
  label,
}: {
  children: React.ReactNode
  label: string
}) {
  const t = useTheme()
  const {position, onVisibleChange} = useContext(TooltipContext)
  return (
    <Popover.Portal>
      <Popover.Content
        className="radix-popover-content"
        aria-label={label}
        side={position}
        sideOffset={4}
        collisionPadding={MIN_EDGE_SPACE}
        onInteractOutside={() => onVisibleChange(false)}
        style={flatten([
          a.rounded_sm,
          select(t.name, {
            light: t.atoms.bg,
            dark: t.atoms.bg_contrast_100,
            dim: t.atoms.bg_contrast_100,
          }),
          {
            minWidth: 'max-content',
            boxShadow: select(t.name, {
              light: `0 0 24px ${transparentifyColor(t.palette.black, 0.2)}`,
              dark: `0 0 24px ${transparentifyColor(t.palette.black, 0.2)}`,
              dim: `0 0 24px ${transparentifyColor(t.palette.black, 0.2)}`,
            }),
          },
        ])}>
        <Popover.Arrow
          width={ARROW_SIZE}
          height={ARROW_SIZE / 2}
          fill={select(t.name, {
            light: t.atoms.bg.backgroundColor,
            dark: t.atoms.bg_contrast_100.backgroundColor,
            dim: t.atoms.bg_contrast_100.backgroundColor,
          })}
        />
        <View style={[a.px_md, a.py_sm, {maxWidth: BUBBLE_MAX_WIDTH}]}>
          {children}
        </View>
      </Popover.Content>
    </Popover.Portal>
  )
}

export function TextBubble({children}: {children: React.ReactNode}) {
  const c = Children.toArray(children)
  return (
    <Content label={c.join(' ')}>
      <View style={[a.gap_xs]}>
        {c.map((child, i) => (
          <Text key={i} style={[a.text_sm, a.leading_snug]}>
            {child}
          </Text>
        ))}
      </View>
    </Content>
  )
}