Skip to content

Commit 6653f86

Browse files
committed
fix(app): tooltip quirks
1 parent af29d91 commit 6653f86

1 file changed

Lines changed: 83 additions & 4 deletions

File tree

packages/ui/src/components/tooltip.tsx

Lines changed: 83 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Tooltip as KobalteTooltip } from "@kobalte/core/tooltip"
2-
import { createSignal, Match, splitProps, Switch, type JSX } from "solid-js"
2+
import { createEffect, Match, onCleanup, splitProps, Switch, type JSX } from "solid-js"
33
import type { ComponentProps } from "solid-js"
4+
import { createStore } from "solid-js/store"
45

56
export interface TooltipProps extends ComponentProps<typeof KobalteTooltip> {
67
value: JSX.Element
@@ -32,23 +33,101 @@ export function TooltipKeybind(props: TooltipKeybindProps) {
3233
}
3334

3435
export function Tooltip(props: TooltipProps) {
35-
const [open, setOpen] = createSignal(false)
36+
let ref: HTMLDivElement | undefined
37+
const [state, setState] = createStore({
38+
open: false,
39+
block: false,
40+
expand: false,
41+
})
3642
const [local, others] = splitProps(props, [
3743
"children",
3844
"class",
3945
"contentClass",
4046
"contentStyle",
4147
"inactive",
4248
"forceOpen",
49+
"ignoreSafeArea",
4350
"value",
4451
])
4552

53+
const close = () => setState("open", false)
54+
55+
const inside = () => {
56+
const active = document.activeElement
57+
if (!ref || !active) return false
58+
return ref.contains(active)
59+
}
60+
61+
const drop = (expand = state.expand) => {
62+
if (expand) return
63+
if (ref?.matches(":hover")) return
64+
if (inside()) return
65+
setState("block", false)
66+
}
67+
68+
const sync = () => {
69+
const expand = !!ref?.querySelector('[aria-expanded="true"], [data-expanded]')
70+
setState("expand", expand)
71+
if (expand) {
72+
setState("block", true)
73+
close()
74+
return
75+
}
76+
drop(expand)
77+
}
78+
79+
const arm = () => {
80+
setState("block", true)
81+
close()
82+
}
83+
84+
const leave = () => {
85+
if (!inside()) close()
86+
drop()
87+
}
88+
89+
createEffect(() => {
90+
if (!ref) return
91+
sync()
92+
const obs = new MutationObserver(sync)
93+
obs.observe(ref, {
94+
subtree: true,
95+
childList: true,
96+
attributes: true,
97+
attributeFilter: ["aria-expanded", "data-expanded"],
98+
})
99+
onCleanup(() => obs.disconnect())
100+
})
101+
46102
return (
47103
<Switch>
48104
<Match when={local.inactive}>{local.children}</Match>
49105
<Match when={true}>
50-
<KobalteTooltip gutter={4} {...others} closeDelay={0} open={local.forceOpen || open()} onOpenChange={setOpen}>
51-
<KobalteTooltip.Trigger as={"div"} data-component="tooltip-trigger" class={local.class}>
106+
<KobalteTooltip
107+
gutter={4}
108+
{...others}
109+
closeDelay={0}
110+
ignoreSafeArea={local.ignoreSafeArea ?? true}
111+
open={local.forceOpen || state.open}
112+
onOpenChange={(open) => {
113+
if (local.forceOpen) return
114+
if (state.block && open) return
115+
setState("open", open)
116+
}}
117+
>
118+
<KobalteTooltip.Trigger
119+
ref={ref}
120+
as={"div"}
121+
data-component="tooltip-trigger"
122+
class={local.class}
123+
onPointerDownCapture={arm}
124+
onKeyDownCapture={(event: KeyboardEvent) => {
125+
if (event.key !== "Enter" && event.key !== " ") return
126+
arm()
127+
}}
128+
onPointerLeave={leave}
129+
onFocusOut={() => requestAnimationFrame(() => drop())}
130+
>
52131
{local.children}
53132
</KobalteTooltip.Trigger>
54133
<KobalteTooltip.Portal>

0 commit comments

Comments
 (0)