Optimize tippy by lazily instantiation
This commit is contained in:
parent
ee09c975f8
commit
749e87f228
14 changed files with 66 additions and 29 deletions
|
|
@ -56,11 +56,12 @@
|
|||
if (popoverIsVisible) {
|
||||
popover?.hide()
|
||||
} else {
|
||||
popover?.show()
|
||||
showPopover()
|
||||
}
|
||||
}
|
||||
|
||||
let popover: Instance | undefined = $state()
|
||||
let showPopover: () => void = $state(() => {})
|
||||
let popoverIsVisible = $state(false)
|
||||
</script>
|
||||
|
||||
|
|
@ -71,6 +72,7 @@
|
|||
{#if !isMobile}
|
||||
<Tippy
|
||||
bind:popover
|
||||
bind:show={showPopover}
|
||||
component={ChatMessageMenu}
|
||||
props={{event, pubkeys, popover, replyTo, edit}}
|
||||
params={{
|
||||
|
|
|
|||
|
|
@ -55,11 +55,12 @@
|
|||
|
||||
let wrapper: Element | undefined = $state()
|
||||
let popover: Instance | undefined = $state()
|
||||
let showPopover: () => void = $state(() => {})
|
||||
let instance: any = $state()
|
||||
|
||||
$effect(() => {
|
||||
if ($term) {
|
||||
popover?.show()
|
||||
showPopover()
|
||||
} else {
|
||||
popover?.hide()
|
||||
}
|
||||
|
|
@ -82,6 +83,7 @@
|
|||
{/if}
|
||||
<Tippy
|
||||
bind:popover
|
||||
bind:show={showPopover}
|
||||
bind:instance
|
||||
component={Suggestions}
|
||||
props={{
|
||||
|
|
|
|||
|
|
@ -28,8 +28,6 @@
|
|||
|
||||
const shouldProtect = $relays.hasNip(url, 70)
|
||||
|
||||
const showPopover = () => popover?.show()
|
||||
|
||||
const hidePopover = () => popover?.hide()
|
||||
|
||||
const onEmoji = async (emoji: NativeEmoji) => {
|
||||
|
|
@ -48,6 +46,7 @@
|
|||
}
|
||||
|
||||
let popover: Instance | undefined = $state()
|
||||
let showPopover: () => void = $state(() => {})
|
||||
</script>
|
||||
|
||||
<div class="items-center join">
|
||||
|
|
@ -62,6 +61,7 @@
|
|||
<Button onclick={showPopover} class="flex join-item button button-neutral button-xs">
|
||||
<Tippy
|
||||
bind:popover
|
||||
bind:show={showPopover}
|
||||
component={EventMenu}
|
||||
props={{url, noun, event, customActions, onClick: hidePopover}}
|
||||
params={{trigger: "manual", interactive: true}}>
|
||||
|
|
|
|||
|
|
@ -15,16 +15,16 @@
|
|||
|
||||
const {pubkey, url, customActions}: Props = $props()
|
||||
|
||||
const showPopover = () => popover?.show()
|
||||
|
||||
const hidePopover = () => popover?.hide()
|
||||
|
||||
let popover: Instance | undefined = $state()
|
||||
let showPopover: () => void = $state(() => {})
|
||||
</script>
|
||||
|
||||
<Button onclick={showPopover} class="button button-circle button-ghost button-sm">
|
||||
<Tippy
|
||||
bind:popover
|
||||
bind:show={showPopover}
|
||||
component={ProfileMenuList}
|
||||
props={{pubkey, url, customActions, onClick: hidePopover}}
|
||||
params={{trigger: "manual", interactive: true, placement: "bottom-end"}}>
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@
|
|||
|
||||
let label: Element | undefined = $state()
|
||||
let popover: Instance | undefined = $state()
|
||||
let showPopover: () => void = $state(() => {})
|
||||
let instance: any = $state()
|
||||
|
||||
$effect(() => {
|
||||
|
|
@ -62,7 +63,7 @@
|
|||
oninput?.($term)
|
||||
|
||||
if ($term) {
|
||||
popover?.show()
|
||||
showPopover()
|
||||
} else {
|
||||
popover?.hide()
|
||||
}
|
||||
|
|
@ -97,6 +98,7 @@
|
|||
</label>
|
||||
<Tippy
|
||||
bind:popover
|
||||
bind:show={showPopover}
|
||||
bind:instance
|
||||
component={Suggestions}
|
||||
props={{
|
||||
|
|
|
|||
|
|
@ -52,8 +52,6 @@
|
|||
|
||||
const uploadFiles = () => editor.then(ed => ed.chain().selectFiles().run())
|
||||
|
||||
const showPopover = () => popover?.show()
|
||||
|
||||
const hidePopover = () => popover?.hide()
|
||||
|
||||
const submit = async () => {
|
||||
|
|
@ -70,6 +68,7 @@
|
|||
}
|
||||
|
||||
let popover: Instance | undefined = $state()
|
||||
let showPopover: () => void = $state(() => {})
|
||||
let content = $state(
|
||||
initialValues?.type === "text" ? initialValues.value : (draftKey?.get()?.content ?? ""),
|
||||
)
|
||||
|
|
@ -126,6 +125,7 @@
|
|||
class="join-item h-10 w-10 min-w-10 button button-neutral">
|
||||
<Tippy
|
||||
bind:popover
|
||||
bind:show={showPopover}
|
||||
component={ComposeMenu}
|
||||
props={{url, h, onClick: hidePopover}}
|
||||
params={{trigger: "manual", interactive: true}}>
|
||||
|
|
|
|||
|
|
@ -9,8 +9,6 @@
|
|||
|
||||
const {url, event} = $props()
|
||||
|
||||
const open = () => popover?.show()
|
||||
|
||||
const onClick = () => popover?.hide()
|
||||
|
||||
const onShow = () => {
|
||||
|
|
@ -32,6 +30,7 @@
|
|||
}
|
||||
|
||||
let popover: Instance | undefined = $state()
|
||||
let open: () => void = $state(() => {})
|
||||
let visible = $state(false)
|
||||
</script>
|
||||
|
||||
|
|
@ -40,6 +39,7 @@
|
|||
<Button onclick={open} class="button button-xs button-neutral join-item">
|
||||
<Tippy
|
||||
bind:popover
|
||||
bind:show={open}
|
||||
component={RoomItemMenu}
|
||||
props={{url, event, onClick}}
|
||||
params={{trigger: "manual", interactive: true, onShow, onHidden}}>
|
||||
|
|
|
|||
|
|
@ -67,11 +67,12 @@
|
|||
|
||||
let label: Element | undefined = $state()
|
||||
let popover: Instance | undefined = $state()
|
||||
let showPopover: () => void = $state(() => {})
|
||||
let instance: any = $state()
|
||||
|
||||
$effect(() => {
|
||||
if ($term.trim()) {
|
||||
popover?.show()
|
||||
showPopover()
|
||||
} else {
|
||||
popover?.hide()
|
||||
}
|
||||
|
|
@ -101,6 +102,7 @@
|
|||
</label>
|
||||
<Tippy
|
||||
bind:popover
|
||||
bind:show={showPopover}
|
||||
bind:instance
|
||||
component={Suggestions}
|
||||
props={{
|
||||
|
|
|
|||
|
|
@ -8,8 +8,6 @@
|
|||
|
||||
const {tippyParams = {}, ...props} = $props()
|
||||
|
||||
const open = () => popover?.show()
|
||||
|
||||
const onClick = (emoji: NativeEmoji) => {
|
||||
props.onEmoji(emoji)
|
||||
popover?.hide()
|
||||
|
|
@ -34,6 +32,7 @@
|
|||
})
|
||||
|
||||
let popover: Instance | undefined = $state()
|
||||
let open: () => void = $state(() => {})
|
||||
let visible = $state(false)
|
||||
</script>
|
||||
|
||||
|
|
@ -42,6 +41,7 @@
|
|||
<Button onclick={open} class={props.class}>
|
||||
<Tippy
|
||||
bind:popover
|
||||
bind:show={open}
|
||||
class="flex"
|
||||
component={EmojiPicker}
|
||||
props={{onClick}}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
if (isMobile) {
|
||||
pushModal(IconPickerModal, {onSelect: onClick}, {nested: true})
|
||||
} else {
|
||||
popover?.show()
|
||||
showPopover()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -42,12 +42,14 @@
|
|||
})
|
||||
|
||||
let popover: Instance | undefined = $state()
|
||||
let showPopover: () => void = $state(() => {})
|
||||
</script>
|
||||
|
||||
<svelte:document onmousemove={onMouseMove} />
|
||||
|
||||
<Tippy
|
||||
bind:popover
|
||||
bind:show={showPopover}
|
||||
component={IconPickerPopover}
|
||||
props={{onSelect: onClick}}
|
||||
params={{trigger: "manual", interactive: true, placement: "top-end"}}>
|
||||
|
|
|
|||
|
|
@ -22,16 +22,16 @@
|
|||
"aria-label": ariaLabel,
|
||||
}: Props = $props()
|
||||
|
||||
const showPopover = () => popover?.show()
|
||||
|
||||
const hidePopover = () => popover?.hide()
|
||||
|
||||
let popover: Instance | undefined = $state()
|
||||
let showPopover: () => void = $state(() => {})
|
||||
</script>
|
||||
|
||||
<Button class={className} aria-label={ariaLabel} onclick={showPopover}>
|
||||
<Tippy
|
||||
bind:popover
|
||||
bind:show={showPopover}
|
||||
{component}
|
||||
props={{...componentProps, onClick: hidePopover}}
|
||||
params={{trigger: "manual", interactive: true, placement: "bottom-end"}}>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
import tippy from "tippy.js"
|
||||
import type {Instance} from "tippy.js"
|
||||
import {onMount, mount, unmount} from "svelte"
|
||||
import {isMobile} from "@lib/html"
|
||||
import {getTippyTarget, isMobile} from "@lib/html"
|
||||
|
||||
let {
|
||||
component,
|
||||
|
|
@ -13,6 +13,7 @@
|
|||
params = {},
|
||||
popover = $bindable(),
|
||||
instance = $bindable(),
|
||||
show = $bindable(),
|
||||
...restProps
|
||||
} = $props()
|
||||
|
||||
|
|
@ -23,17 +24,14 @@
|
|||
// the props it was first mounted with, showing stale data after the source updates.
|
||||
const mountedProps = $state({...props})
|
||||
|
||||
$effect(() => {
|
||||
Object.assign(mountedProps, props)
|
||||
})
|
||||
|
||||
onMount(() => {
|
||||
const target = document.createElement("div")
|
||||
|
||||
popover = tippy(element, {
|
||||
// Building a tippy costs a popper element and a set of listeners, which is wasted on the
|
||||
// hover menus of a chat row nobody ever opens. Only a real trigger needs the instance up
|
||||
// front — tippy is the one listening for it. A manual one can wait for `show`.
|
||||
const create = () => {
|
||||
popover ??= tippy(element, {
|
||||
content: target,
|
||||
animation: "shift-away",
|
||||
appendTo: document.querySelector(".tippy-target")!,
|
||||
appendTo: getTippyTarget(),
|
||||
trigger: isMobile ? "click" : "mouseenter focus",
|
||||
...params,
|
||||
onShow: (tippyInstance: Instance) => {
|
||||
|
|
@ -43,6 +41,22 @@
|
|||
},
|
||||
})
|
||||
|
||||
return popover
|
||||
}
|
||||
|
||||
const target = document.createElement("div")
|
||||
|
||||
show = () => create().show()
|
||||
|
||||
$effect(() => {
|
||||
Object.assign(mountedProps, props)
|
||||
})
|
||||
|
||||
onMount(() => {
|
||||
if (params.trigger !== "manual") {
|
||||
create()
|
||||
}
|
||||
|
||||
return () => {
|
||||
popover?.destroy()
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
|
||||
import tippy from "tippy.js"
|
||||
import {onMount} from "svelte"
|
||||
import {isMobile} from "@lib/html"
|
||||
import {getTippyTarget, isMobile} from "@lib/html"
|
||||
|
||||
let {
|
||||
content,
|
||||
|
|
@ -50,7 +50,7 @@
|
|||
interactive,
|
||||
animation: "shift-away",
|
||||
theme: "tooltip",
|
||||
appendTo: document.querySelector(".tippy-target")!,
|
||||
appendTo: getTippyTarget(),
|
||||
trigger: isMobile ? "click" : "mouseenter focus",
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import {readable} from "svelte/store"
|
||||
import {sleep, randomId} from "@welshman/lib"
|
||||
import type {Maybe} from "@welshman/lib"
|
||||
import {Capacitor} from "@capacitor/core"
|
||||
export {preventDefault, stopPropagation} from "svelte/legacy"
|
||||
|
||||
|
|
@ -152,6 +153,18 @@ export const createScroller = ({
|
|||
|
||||
export const isMobile = "ontouchstart" in document.documentElement
|
||||
|
||||
// The layout's single popover host. Cached, because with a couple of popovers per chat row
|
||||
// this runs thousands of times against a document that is itself thousands of nodes.
|
||||
let tippyTarget: Maybe<Element>
|
||||
|
||||
export const getTippyTarget = () => {
|
||||
if (!tippyTarget?.isConnected) {
|
||||
tippyTarget = document.querySelector(".tippy-target")!
|
||||
}
|
||||
|
||||
return tippyTarget
|
||||
}
|
||||
|
||||
export const downloadText = async (filename: string, text: string) => {
|
||||
// The <a download> blob trick is a no-op in native WebViews (Android in
|
||||
// particular never triggers a download), so on device we write the file and
|
||||
|
|
|
|||
Loading…
Reference in a new issue