lemmy-ui/src/shared/tippy.ts

71 lines
2 KiB
TypeScript
Raw Normal View History

Use mixins and decorators for scroll restoration and tippy cleanup (#2415) * Enable @babel/plugin-proposal-decorators Dependency already exists * Use tippy.js delegate addon, cleanup tippy instances from a mixin. The delegate addon creates tippy instances from mouse and touch events with a matching `event.target`. This is initially significantly cheaper than creating all instances at once. The addon keeps all created tippy instances alive until it is destroyed itself. `tippyMixin` destroys the addon instance after every render, as long as all instances are hidden. This drops some tippy instances that may have to be recreated later (e.g when the mouse moves over the trigger again), but is otherwise fairly cheap (creates one tippy instance). * Restore scroll positions when resource loading settles. The history module generates a random string (`location.key`) for every browser history entry. The names for saved positions include this key. The position is saved before a route component unmounts or before the `location.key` changes. The `scrollMixin` tires to restore the scroll position after every change of `location.key`. It only does so after the first render for which the route components `loadingSettled()` returns true. Things like `scrollToComments` should only scroll when `history.action` is not "POP". * Drop individual scrollTo calls * Scroll to comments without reloading post --------- Co-authored-by: SleeplessOne1917 <28871516+SleeplessOne1917@users.noreply.github.com>
2024-04-11 17:18:07 +00:00
import { RefObject } from "inferno";
import {
DelegateInstance as TippyDelegateInstance,
Props as TippyProps,
Instance as TippyInstance,
delegate as tippyDelegate,
} from "tippy.js";
Use mixins and decorators for scroll restoration and tippy cleanup (#2415) * Enable @babel/plugin-proposal-decorators Dependency already exists * Use tippy.js delegate addon, cleanup tippy instances from a mixin. The delegate addon creates tippy instances from mouse and touch events with a matching `event.target`. This is initially significantly cheaper than creating all instances at once. The addon keeps all created tippy instances alive until it is destroyed itself. `tippyMixin` destroys the addon instance after every render, as long as all instances are hidden. This drops some tippy instances that may have to be recreated later (e.g when the mouse moves over the trigger again), but is otherwise fairly cheap (creates one tippy instance). * Restore scroll positions when resource loading settles. The history module generates a random string (`location.key`) for every browser history entry. The names for saved positions include this key. The position is saved before a route component unmounts or before the `location.key` changes. The `scrollMixin` tires to restore the scroll position after every change of `location.key`. It only does so after the first render for which the route components `loadingSettled()` returns true. Things like `scrollToComments` should only scroll when `history.action` is not "POP". * Drop individual scrollTo calls * Scroll to comments without reloading post --------- Co-authored-by: SleeplessOne1917 <28871516+SleeplessOne1917@users.noreply.github.com>
2024-04-11 17:18:07 +00:00
let instance: TippyDelegateInstance<TippyProps> | undefined;
const tippySelector = "[data-tippy-content]";
const shownInstances: Set<TippyInstance<TippyProps>> = new Set();
let instanceCounter = 0;
Use mixins and decorators for scroll restoration and tippy cleanup (#2415) * Enable @babel/plugin-proposal-decorators Dependency already exists * Use tippy.js delegate addon, cleanup tippy instances from a mixin. The delegate addon creates tippy instances from mouse and touch events with a matching `event.target`. This is initially significantly cheaper than creating all instances at once. The addon keeps all created tippy instances alive until it is destroyed itself. `tippyMixin` destroys the addon instance after every render, as long as all instances are hidden. This drops some tippy instances that may have to be recreated later (e.g when the mouse moves over the trigger again), but is otherwise fairly cheap (creates one tippy instance). * Restore scroll positions when resource loading settles. The history module generates a random string (`location.key`) for every browser history entry. The names for saved positions include this key. The position is saved before a route component unmounts or before the `location.key` changes. The `scrollMixin` tires to restore the scroll position after every change of `location.key`. It only does so after the first render for which the route components `loadingSettled()` returns true. Things like `scrollToComments` should only scroll when `history.action` is not "POP". * Drop individual scrollTo calls * Scroll to comments without reloading post --------- Co-authored-by: SleeplessOne1917 <28871516+SleeplessOne1917@users.noreply.github.com>
2024-04-11 17:18:07 +00:00
const tippyDelegateOptions: Partial<TippyProps> & { target: string } = {
delay: [500, 0],
// Display on "long press"
touch: ["hold", 500],
target: tippySelector,
onShow(i: TippyInstance<TippyProps>) {
shownInstances.add(i);
},
onHidden(i: TippyInstance<TippyProps>) {
shownInstances.delete(i);
},
onCreate() {
instanceCounter++;
},
onDestroy(i: TippyInstance<TippyProps>) {
// Tippy doesn't remove its onDocumentPress listener when destroyed.
// Instead the listener removes itself after calling hide for hideOnClick.
const origHide = i.hide;
// This silences the first warning when hiding a destroyed tippy instance.
// hide() is otherwise a noop for destroyed instances.
i.hide = () => {
i.hide = origHide;
};
},
Use mixins and decorators for scroll restoration and tippy cleanup (#2415) * Enable @babel/plugin-proposal-decorators Dependency already exists * Use tippy.js delegate addon, cleanup tippy instances from a mixin. The delegate addon creates tippy instances from mouse and touch events with a matching `event.target`. This is initially significantly cheaper than creating all instances at once. The addon keeps all created tippy instances alive until it is destroyed itself. `tippyMixin` destroys the addon instance after every render, as long as all instances are hidden. This drops some tippy instances that may have to be recreated later (e.g when the mouse moves over the trigger again), but is otherwise fairly cheap (creates one tippy instance). * Restore scroll positions when resource loading settles. The history module generates a random string (`location.key`) for every browser history entry. The names for saved positions include this key. The position is saved before a route component unmounts or before the `location.key` changes. The `scrollMixin` tires to restore the scroll position after every change of `location.key`. It only does so after the first render for which the route components `loadingSettled()` returns true. Things like `scrollToComments` should only scroll when `history.action` is not "POP". * Drop individual scrollTo calls * Scroll to comments without reloading post --------- Co-authored-by: SleeplessOne1917 <28871516+SleeplessOne1917@users.noreply.github.com>
2024-04-11 17:18:07 +00:00
};
export function setupTippy(root: RefObject<Element>) {
if (!instance && root.current) {
instance = tippyDelegate(root.current, tippyDelegateOptions);
}
}
Use mixins and decorators for scroll restoration and tippy cleanup (#2415) * Enable @babel/plugin-proposal-decorators Dependency already exists * Use tippy.js delegate addon, cleanup tippy instances from a mixin. The delegate addon creates tippy instances from mouse and touch events with a matching `event.target`. This is initially significantly cheaper than creating all instances at once. The addon keeps all created tippy instances alive until it is destroyed itself. `tippyMixin` destroys the addon instance after every render, as long as all instances are hidden. This drops some tippy instances that may have to be recreated later (e.g when the mouse moves over the trigger again), but is otherwise fairly cheap (creates one tippy instance). * Restore scroll positions when resource loading settles. The history module generates a random string (`location.key`) for every browser history entry. The names for saved positions include this key. The position is saved before a route component unmounts or before the `location.key` changes. The `scrollMixin` tires to restore the scroll position after every change of `location.key`. It only does so after the first render for which the route components `loadingSettled()` returns true. Things like `scrollToComments` should only scroll when `history.action` is not "POP". * Drop individual scrollTo calls * Scroll to comments without reloading post --------- Co-authored-by: SleeplessOne1917 <28871516+SleeplessOne1917@users.noreply.github.com>
2024-04-11 17:18:07 +00:00
export function cleanupTippy() {
// Hide tooltips for elements that are no longer connected to the document.
shownInstances.forEach(i => {
if (!i.reference.isConnected) {
console.assert(!i.state.isDestroyed, "hide called on destroyed tippy");
i.hide();
Use mixins and decorators for scroll restoration and tippy cleanup (#2415) * Enable @babel/plugin-proposal-decorators Dependency already exists * Use tippy.js delegate addon, cleanup tippy instances from a mixin. The delegate addon creates tippy instances from mouse and touch events with a matching `event.target`. This is initially significantly cheaper than creating all instances at once. The addon keeps all created tippy instances alive until it is destroyed itself. `tippyMixin` destroys the addon instance after every render, as long as all instances are hidden. This drops some tippy instances that may have to be recreated later (e.g when the mouse moves over the trigger again), but is otherwise fairly cheap (creates one tippy instance). * Restore scroll positions when resource loading settles. The history module generates a random string (`location.key`) for every browser history entry. The names for saved positions include this key. The position is saved before a route component unmounts or before the `location.key` changes. The `scrollMixin` tires to restore the scroll position after every change of `location.key`. It only does so after the first render for which the route components `loadingSettled()` returns true. Things like `scrollToComments` should only scroll when `history.action` is not "POP". * Drop individual scrollTo calls * Scroll to comments without reloading post --------- Co-authored-by: SleeplessOne1917 <28871516+SleeplessOne1917@users.noreply.github.com>
2024-04-11 17:18:07 +00:00
}
});
if (shownInstances.size || instanceCounter < 10) {
// Avoid randomly closing tooltips.
return;
}
instanceCounter = 0;
const current = instance?.reference ?? null;
// delegate from tippy.js creates tippy instances when needed, but only
// destroys them when the delegate instance is destroyed.
destroyTippy();
setupTippy({ current });
Use mixins and decorators for scroll restoration and tippy cleanup (#2415) * Enable @babel/plugin-proposal-decorators Dependency already exists * Use tippy.js delegate addon, cleanup tippy instances from a mixin. The delegate addon creates tippy instances from mouse and touch events with a matching `event.target`. This is initially significantly cheaper than creating all instances at once. The addon keeps all created tippy instances alive until it is destroyed itself. `tippyMixin` destroys the addon instance after every render, as long as all instances are hidden. This drops some tippy instances that may have to be recreated later (e.g when the mouse moves over the trigger again), but is otherwise fairly cheap (creates one tippy instance). * Restore scroll positions when resource loading settles. The history module generates a random string (`location.key`) for every browser history entry. The names for saved positions include this key. The position is saved before a route component unmounts or before the `location.key` changes. The `scrollMixin` tires to restore the scroll position after every change of `location.key`. It only does so after the first render for which the route components `loadingSettled()` returns true. Things like `scrollToComments` should only scroll when `history.action` is not "POP". * Drop individual scrollTo calls * Scroll to comments without reloading post --------- Co-authored-by: SleeplessOne1917 <28871516+SleeplessOne1917@users.noreply.github.com>
2024-04-11 17:18:07 +00:00
}
export function destroyTippy() {
instance?.destroy();
instance = undefined;
}