2023-10-22 13:06:04 +00:00
|
|
|
import tippy, {followCursor} from 'tippy.js';
|
2024-07-07 15:32:30 +00:00
|
|
|
import {isDocumentFragmentOrElementNode} from '../utils/dom.ts';
|
|
|
|
import {formatDatetime} from '../utils/time.ts';
|
2024-11-21 13:57:42 +00:00
|
|
|
import type {Content, Instance, Placement, Props} from 'tippy.js';
|
2022-07-18 22:33:34 +00:00
|
|
|
|
2024-08-10 09:46:48 +00:00
|
|
|
type TippyOpts = {
|
|
|
|
role?: string,
|
|
|
|
theme?: 'default' | 'tooltip' | 'menu' | 'box-with-header' | 'bare',
|
|
|
|
} & Partial<Props>;
|
|
|
|
|
|
|
|
const visibleInstances = new Set<Instance>();
|
2024-04-03 09:15:06 +00:00
|
|
|
const arrowSvg = `<svg width="16" height="7"><path d="m0 7 8-7 8 7Z" class="tippy-svg-arrow-outer"/><path d="m0 8 8-7 8 7Z" class="tippy-svg-arrow-inner"/></svg>`;
|
2023-05-11 05:40:54 +00:00
|
|
|
|
2024-11-04 10:14:36 +00:00
|
|
|
export function createTippy(target: Element, opts: TippyOpts = {}): Instance {
|
2023-06-19 07:46:50 +00:00
|
|
|
// the callback functions should be destructured from opts,
|
|
|
|
// because we should use our own wrapper functions to handle them, do not let the user override them
|
2024-04-03 09:15:06 +00:00
|
|
|
const {onHide, onShow, onDestroy, role, theme, arrow, ...other} = opts;
|
2024-03-15 02:05:31 +00:00
|
|
|
|
2024-08-10 09:46:48 +00:00
|
|
|
const instance: Instance = tippy(target, {
|
2022-07-18 22:33:34 +00:00
|
|
|
appendTo: document.body,
|
|
|
|
animation: false,
|
2022-08-23 20:17:42 +00:00
|
|
|
allowHTML: false,
|
2022-12-25 17:17:48 +00:00
|
|
|
hideOnClick: false,
|
2023-04-10 23:01:20 +00:00
|
|
|
interactiveBorder: 20,
|
2022-09-09 21:03:18 +00:00
|
|
|
ignoreAttributes: true,
|
2022-08-09 12:37:34 +00:00
|
|
|
maxWidth: 500, // increase over default 350px
|
2024-08-10 09:46:48 +00:00
|
|
|
onHide: (instance: Instance) => {
|
2023-05-11 05:40:54 +00:00
|
|
|
visibleInstances.delete(instance);
|
2023-06-19 07:46:50 +00:00
|
|
|
return onHide?.(instance);
|
2023-05-11 05:40:54 +00:00
|
|
|
},
|
2024-08-10 09:46:48 +00:00
|
|
|
onDestroy: (instance: Instance) => {
|
2023-05-11 05:40:54 +00:00
|
|
|
visibleInstances.delete(instance);
|
2023-06-19 07:46:50 +00:00
|
|
|
return onDestroy?.(instance);
|
2023-05-11 05:40:54 +00:00
|
|
|
},
|
2024-08-10 09:46:48 +00:00
|
|
|
onShow: (instance: Instance) => {
|
2023-05-12 16:58:55 +00:00
|
|
|
// hide other tooltip instances so only one tooltip shows at a time
|
2023-05-11 05:40:54 +00:00
|
|
|
for (const visibleInstance of visibleInstances) {
|
2023-05-19 20:12:30 +00:00
|
|
|
if (visibleInstance.props.role === 'tooltip') {
|
2023-05-12 16:58:55 +00:00
|
|
|
visibleInstance.hide();
|
|
|
|
}
|
2023-05-11 05:40:54 +00:00
|
|
|
}
|
|
|
|
visibleInstances.add(instance);
|
2023-06-19 07:46:50 +00:00
|
|
|
return onShow?.(instance);
|
2023-05-11 05:40:54 +00:00
|
|
|
},
|
2024-04-03 09:15:06 +00:00
|
|
|
arrow: arrow || (theme === 'bare' ? false : arrowSvg),
|
2024-04-30 14:52:46 +00:00
|
|
|
// HTML role attribute, ideally the default role would be "popover" but it does not exist
|
|
|
|
role: role || 'menu',
|
|
|
|
// CSS theme, either "default", "tooltip", "menu", "box-with-header" or "bare"
|
|
|
|
theme: theme || role || 'default',
|
2023-10-22 13:06:04 +00:00
|
|
|
plugins: [followCursor],
|
2023-06-19 07:46:50 +00:00
|
|
|
...other,
|
2024-08-10 09:46:48 +00:00
|
|
|
} satisfies Partial<Props>);
|
2022-08-09 12:37:34 +00:00
|
|
|
|
2024-03-15 02:05:31 +00:00
|
|
|
if (role === 'menu') {
|
|
|
|
target.setAttribute('aria-haspopup', 'true');
|
2022-08-09 12:37:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return instance;
|
|
|
|
}
|
|
|
|
|
Use a general approach to show tooltip, fix temporary tooltip bug (#23574)
## TLDR
* Improve performance: lazy creating the tippy instances.
* Transparently support all "tooltip" elements, no need to call
`initTooltip` again and again.
* Fix a temporary tooltip re-entrance bug, which causes showing temp
content forever.
* Upgrade vue3-calendar-heatmap to 2.0.2 with lazy tippy init
(initHeatmap time decreases from 100ms to 50ms)
## Details
### The performance
Creating a lot of tippy tooltip instances is expensive. This PR doesn't
create all tippy tooltip instances, instead, it only adds "mouseover"
event listener to necessary elements, and then switches to the tippy
tooltip
### The general approach for all tooltips
Before, dynamically generated tooltips need to be called with
`initTooltip`.
After, use MutationObserver to:
* Attach the event listeners to newly created tooltip elements, work for
Vue (easier than before)
* Catch changed attributes and update the tooltip content (better than
before)
It does help a lot, eg:
https://github.com/go-gitea/gitea/blob/1a4efa0ee9a49d48549be7479a46be133b9bc260/web_src/js/components/PullRequestMergeForm.vue#L33-L36
### Temporary tooltip re-entrance bug
To reproduce, on try.gitea.io, click the "copy clone url" quickly, then
the tooltip will be "Copied!" forever.
After this PR, with the help of `attachTippyTooltip`, the tooltip
content could be reset to the default correctly.
### Other changes
* `data-tooltip-content` is preferred from now on, the old
`data-content` may cause conflicts with other modules.
* `data-placement` was only used for tooltip, so it's renamed to
`data-tooltip-placement`, and removed from `createTippy`.
2023-03-23 09:56:15 +00:00
|
|
|
/**
|
|
|
|
* Attach a tooltip tippy to the given target element.
|
|
|
|
* If the target element already has a tooltip tippy attached, the tooltip will be updated with the new content.
|
|
|
|
* If the target element has no content, then no tooltip will be attached, and it returns null.
|
|
|
|
*
|
|
|
|
* Note: "tooltip" doesn't equal to "tippy". "tooltip" means a auto-popup content, it just uses tippy as the implementation.
|
|
|
|
*/
|
2024-11-21 13:57:42 +00:00
|
|
|
function attachTooltip(target: Element, content: Content = null): Instance {
|
2023-04-10 23:01:20 +00:00
|
|
|
switchTitleToTooltip(target);
|
|
|
|
|
2023-03-24 10:35:38 +00:00
|
|
|
content = content ?? target.getAttribute('data-tooltip-content');
|
2022-08-09 12:37:34 +00:00
|
|
|
if (!content) return null;
|
Use a general approach to show tooltip, fix temporary tooltip bug (#23574)
## TLDR
* Improve performance: lazy creating the tippy instances.
* Transparently support all "tooltip" elements, no need to call
`initTooltip` again and again.
* Fix a temporary tooltip re-entrance bug, which causes showing temp
content forever.
* Upgrade vue3-calendar-heatmap to 2.0.2 with lazy tippy init
(initHeatmap time decreases from 100ms to 50ms)
## Details
### The performance
Creating a lot of tippy tooltip instances is expensive. This PR doesn't
create all tippy tooltip instances, instead, it only adds "mouseover"
event listener to necessary elements, and then switches to the tippy
tooltip
### The general approach for all tooltips
Before, dynamically generated tooltips need to be called with
`initTooltip`.
After, use MutationObserver to:
* Attach the event listeners to newly created tooltip elements, work for
Vue (easier than before)
* Catch changed attributes and update the tooltip content (better than
before)
It does help a lot, eg:
https://github.com/go-gitea/gitea/blob/1a4efa0ee9a49d48549be7479a46be133b9bc260/web_src/js/components/PullRequestMergeForm.vue#L33-L36
### Temporary tooltip re-entrance bug
To reproduce, on try.gitea.io, click the "copy clone url" quickly, then
the tooltip will be "Copied!" forever.
After this PR, with the help of `attachTippyTooltip`, the tooltip
content could be reset to the default correctly.
### Other changes
* `data-tooltip-content` is preferred from now on, the old
`data-content` may cause conflicts with other modules.
* `data-placement` was only used for tooltip, so it's renamed to
`data-tooltip-placement`, and removed from `createTippy`.
2023-03-23 09:56:15 +00:00
|
|
|
|
2023-05-28 01:34:18 +00:00
|
|
|
// when element has a clipboard target, we update the tooltip after copy
|
|
|
|
// in which case it is undesirable to automatically hide it on click as
|
|
|
|
// it would momentarily flash the tooltip out and in.
|
|
|
|
const hasClipboardTarget = target.hasAttribute('data-clipboard-target');
|
|
|
|
const hideOnClick = !hasClipboardTarget;
|
|
|
|
|
2024-11-21 13:57:42 +00:00
|
|
|
const props: TippyOpts = {
|
2022-08-09 12:37:34 +00:00
|
|
|
content,
|
|
|
|
delay: 100,
|
|
|
|
role: 'tooltip',
|
2023-06-09 09:10:51 +00:00
|
|
|
theme: 'tooltip',
|
2023-05-28 01:34:18 +00:00
|
|
|
hideOnClick,
|
2024-11-21 13:57:42 +00:00
|
|
|
placement: target.getAttribute('data-tooltip-placement') as Placement || 'top-start',
|
|
|
|
followCursor: target.getAttribute('data-tooltip-follow-cursor') as Props['followCursor'] || false,
|
2023-03-24 09:43:52 +00:00
|
|
|
...(target.getAttribute('data-tooltip-interactive') === 'true' ? {interactive: true, aria: {content: 'describedby', expanded: false}} : {}),
|
2024-11-21 13:57:42 +00:00
|
|
|
};
|
2022-08-09 12:37:34 +00:00
|
|
|
|
Use a general approach to show tooltip, fix temporary tooltip bug (#23574)
## TLDR
* Improve performance: lazy creating the tippy instances.
* Transparently support all "tooltip" elements, no need to call
`initTooltip` again and again.
* Fix a temporary tooltip re-entrance bug, which causes showing temp
content forever.
* Upgrade vue3-calendar-heatmap to 2.0.2 with lazy tippy init
(initHeatmap time decreases from 100ms to 50ms)
## Details
### The performance
Creating a lot of tippy tooltip instances is expensive. This PR doesn't
create all tippy tooltip instances, instead, it only adds "mouseover"
event listener to necessary elements, and then switches to the tippy
tooltip
### The general approach for all tooltips
Before, dynamically generated tooltips need to be called with
`initTooltip`.
After, use MutationObserver to:
* Attach the event listeners to newly created tooltip elements, work for
Vue (easier than before)
* Catch changed attributes and update the tooltip content (better than
before)
It does help a lot, eg:
https://github.com/go-gitea/gitea/blob/1a4efa0ee9a49d48549be7479a46be133b9bc260/web_src/js/components/PullRequestMergeForm.vue#L33-L36
### Temporary tooltip re-entrance bug
To reproduce, on try.gitea.io, click the "copy clone url" quickly, then
the tooltip will be "Copied!" forever.
After this PR, with the help of `attachTippyTooltip`, the tooltip
content could be reset to the default correctly.
### Other changes
* `data-tooltip-content` is preferred from now on, the old
`data-content` may cause conflicts with other modules.
* `data-placement` was only used for tooltip, so it's renamed to
`data-tooltip-placement`, and removed from `createTippy`.
2023-03-23 09:56:15 +00:00
|
|
|
if (!target._tippy) {
|
|
|
|
createTippy(target, props);
|
2022-08-09 12:37:34 +00:00
|
|
|
} else {
|
Use a general approach to show tooltip, fix temporary tooltip bug (#23574)
## TLDR
* Improve performance: lazy creating the tippy instances.
* Transparently support all "tooltip" elements, no need to call
`initTooltip` again and again.
* Fix a temporary tooltip re-entrance bug, which causes showing temp
content forever.
* Upgrade vue3-calendar-heatmap to 2.0.2 with lazy tippy init
(initHeatmap time decreases from 100ms to 50ms)
## Details
### The performance
Creating a lot of tippy tooltip instances is expensive. This PR doesn't
create all tippy tooltip instances, instead, it only adds "mouseover"
event listener to necessary elements, and then switches to the tippy
tooltip
### The general approach for all tooltips
Before, dynamically generated tooltips need to be called with
`initTooltip`.
After, use MutationObserver to:
* Attach the event listeners to newly created tooltip elements, work for
Vue (easier than before)
* Catch changed attributes and update the tooltip content (better than
before)
It does help a lot, eg:
https://github.com/go-gitea/gitea/blob/1a4efa0ee9a49d48549be7479a46be133b9bc260/web_src/js/components/PullRequestMergeForm.vue#L33-L36
### Temporary tooltip re-entrance bug
To reproduce, on try.gitea.io, click the "copy clone url" quickly, then
the tooltip will be "Copied!" forever.
After this PR, with the help of `attachTippyTooltip`, the tooltip
content could be reset to the default correctly.
### Other changes
* `data-tooltip-content` is preferred from now on, the old
`data-content` may cause conflicts with other modules.
* `data-placement` was only used for tooltip, so it's renamed to
`data-tooltip-placement`, and removed from `createTippy`.
2023-03-23 09:56:15 +00:00
|
|
|
target._tippy.setProps(props);
|
|
|
|
}
|
|
|
|
return target._tippy;
|
|
|
|
}
|
|
|
|
|
2024-11-21 13:57:42 +00:00
|
|
|
function switchTitleToTooltip(target: Element): void {
|
2024-03-08 07:57:52 +00:00
|
|
|
let title = target.getAttribute('title');
|
2023-04-10 23:01:20 +00:00
|
|
|
if (title) {
|
2024-03-08 07:57:52 +00:00
|
|
|
// apply custom formatting to relative-time's tooltips
|
|
|
|
if (target.tagName.toLowerCase() === 'relative-time') {
|
|
|
|
const datetime = target.getAttribute('datetime');
|
|
|
|
if (datetime) {
|
|
|
|
title = formatDatetime(new Date(datetime));
|
|
|
|
}
|
|
|
|
}
|
2023-04-10 23:01:20 +00:00
|
|
|
target.setAttribute('data-tooltip-content', title);
|
|
|
|
target.setAttribute('aria-label', title);
|
|
|
|
// keep the attribute, in case there are some other "[title]" selectors
|
|
|
|
// and to prevent infinite loop with <relative-time> which will re-add
|
|
|
|
// title if it is absent
|
|
|
|
target.setAttribute('title', '');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Use a general approach to show tooltip, fix temporary tooltip bug (#23574)
## TLDR
* Improve performance: lazy creating the tippy instances.
* Transparently support all "tooltip" elements, no need to call
`initTooltip` again and again.
* Fix a temporary tooltip re-entrance bug, which causes showing temp
content forever.
* Upgrade vue3-calendar-heatmap to 2.0.2 with lazy tippy init
(initHeatmap time decreases from 100ms to 50ms)
## Details
### The performance
Creating a lot of tippy tooltip instances is expensive. This PR doesn't
create all tippy tooltip instances, instead, it only adds "mouseover"
event listener to necessary elements, and then switches to the tippy
tooltip
### The general approach for all tooltips
Before, dynamically generated tooltips need to be called with
`initTooltip`.
After, use MutationObserver to:
* Attach the event listeners to newly created tooltip elements, work for
Vue (easier than before)
* Catch changed attributes and update the tooltip content (better than
before)
It does help a lot, eg:
https://github.com/go-gitea/gitea/blob/1a4efa0ee9a49d48549be7479a46be133b9bc260/web_src/js/components/PullRequestMergeForm.vue#L33-L36
### Temporary tooltip re-entrance bug
To reproduce, on try.gitea.io, click the "copy clone url" quickly, then
the tooltip will be "Copied!" forever.
After this PR, with the help of `attachTippyTooltip`, the tooltip
content could be reset to the default correctly.
### Other changes
* `data-tooltip-content` is preferred from now on, the old
`data-content` may cause conflicts with other modules.
* `data-placement` was only used for tooltip, so it's renamed to
`data-tooltip-placement`, and removed from `createTippy`.
2023-03-23 09:56:15 +00:00
|
|
|
/**
|
|
|
|
* Creating tooltip tippy instance is expensive, so we only create it when the user hovers over the element
|
|
|
|
* According to https://www.w3.org/TR/DOM-Level-3-Events/#events-mouseevent-event-order , mouseover event is fired before mouseenter event
|
2023-12-14 23:26:36 +00:00
|
|
|
* Some browsers like PaleMoon don't support "addEventListener('mouseenter', capture)"
|
Use a general approach to show tooltip, fix temporary tooltip bug (#23574)
## TLDR
* Improve performance: lazy creating the tippy instances.
* Transparently support all "tooltip" elements, no need to call
`initTooltip` again and again.
* Fix a temporary tooltip re-entrance bug, which causes showing temp
content forever.
* Upgrade vue3-calendar-heatmap to 2.0.2 with lazy tippy init
(initHeatmap time decreases from 100ms to 50ms)
## Details
### The performance
Creating a lot of tippy tooltip instances is expensive. This PR doesn't
create all tippy tooltip instances, instead, it only adds "mouseover"
event listener to necessary elements, and then switches to the tippy
tooltip
### The general approach for all tooltips
Before, dynamically generated tooltips need to be called with
`initTooltip`.
After, use MutationObserver to:
* Attach the event listeners to newly created tooltip elements, work for
Vue (easier than before)
* Catch changed attributes and update the tooltip content (better than
before)
It does help a lot, eg:
https://github.com/go-gitea/gitea/blob/1a4efa0ee9a49d48549be7479a46be133b9bc260/web_src/js/components/PullRequestMergeForm.vue#L33-L36
### Temporary tooltip re-entrance bug
To reproduce, on try.gitea.io, click the "copy clone url" quickly, then
the tooltip will be "Copied!" forever.
After this PR, with the help of `attachTippyTooltip`, the tooltip
content could be reset to the default correctly.
### Other changes
* `data-tooltip-content` is preferred from now on, the old
`data-content` may cause conflicts with other modules.
* `data-placement` was only used for tooltip, so it's renamed to
`data-tooltip-placement`, and removed from `createTippy`.
2023-03-23 09:56:15 +00:00
|
|
|
* The tippy by default uses "mouseenter" event to show, so we use "mouseover" event to switch to tippy
|
|
|
|
*/
|
2024-12-21 18:59:25 +00:00
|
|
|
function lazyTooltipOnMouseHover(e: Event): void {
|
Use a general approach to show tooltip, fix temporary tooltip bug (#23574)
## TLDR
* Improve performance: lazy creating the tippy instances.
* Transparently support all "tooltip" elements, no need to call
`initTooltip` again and again.
* Fix a temporary tooltip re-entrance bug, which causes showing temp
content forever.
* Upgrade vue3-calendar-heatmap to 2.0.2 with lazy tippy init
(initHeatmap time decreases from 100ms to 50ms)
## Details
### The performance
Creating a lot of tippy tooltip instances is expensive. This PR doesn't
create all tippy tooltip instances, instead, it only adds "mouseover"
event listener to necessary elements, and then switches to the tippy
tooltip
### The general approach for all tooltips
Before, dynamically generated tooltips need to be called with
`initTooltip`.
After, use MutationObserver to:
* Attach the event listeners to newly created tooltip elements, work for
Vue (easier than before)
* Catch changed attributes and update the tooltip content (better than
before)
It does help a lot, eg:
https://github.com/go-gitea/gitea/blob/1a4efa0ee9a49d48549be7479a46be133b9bc260/web_src/js/components/PullRequestMergeForm.vue#L33-L36
### Temporary tooltip re-entrance bug
To reproduce, on try.gitea.io, click the "copy clone url" quickly, then
the tooltip will be "Copied!" forever.
After this PR, with the help of `attachTippyTooltip`, the tooltip
content could be reset to the default correctly.
### Other changes
* `data-tooltip-content` is preferred from now on, the old
`data-content` may cause conflicts with other modules.
* `data-placement` was only used for tooltip, so it's renamed to
`data-tooltip-placement`, and removed from `createTippy`.
2023-03-23 09:56:15 +00:00
|
|
|
e.target.removeEventListener('mouseover', lazyTooltipOnMouseHover, true);
|
|
|
|
attachTooltip(this);
|
|
|
|
}
|
|
|
|
|
2023-04-10 23:01:20 +00:00
|
|
|
// Activate the tooltip for current element.
|
|
|
|
// If the element has no aria-label, use the tooltip content as aria-label.
|
2024-12-21 18:59:25 +00:00
|
|
|
function attachLazyTooltip(el: HTMLElement): void {
|
2023-04-10 23:01:20 +00:00
|
|
|
el.addEventListener('mouseover', lazyTooltipOnMouseHover, {capture: true});
|
Use a general approach to show tooltip, fix temporary tooltip bug (#23574)
## TLDR
* Improve performance: lazy creating the tippy instances.
* Transparently support all "tooltip" elements, no need to call
`initTooltip` again and again.
* Fix a temporary tooltip re-entrance bug, which causes showing temp
content forever.
* Upgrade vue3-calendar-heatmap to 2.0.2 with lazy tippy init
(initHeatmap time decreases from 100ms to 50ms)
## Details
### The performance
Creating a lot of tippy tooltip instances is expensive. This PR doesn't
create all tippy tooltip instances, instead, it only adds "mouseover"
event listener to necessary elements, and then switches to the tippy
tooltip
### The general approach for all tooltips
Before, dynamically generated tooltips need to be called with
`initTooltip`.
After, use MutationObserver to:
* Attach the event listeners to newly created tooltip elements, work for
Vue (easier than before)
* Catch changed attributes and update the tooltip content (better than
before)
It does help a lot, eg:
https://github.com/go-gitea/gitea/blob/1a4efa0ee9a49d48549be7479a46be133b9bc260/web_src/js/components/PullRequestMergeForm.vue#L33-L36
### Temporary tooltip re-entrance bug
To reproduce, on try.gitea.io, click the "copy clone url" quickly, then
the tooltip will be "Copied!" forever.
After this PR, with the help of `attachTippyTooltip`, the tooltip
content could be reset to the default correctly.
### Other changes
* `data-tooltip-content` is preferred from now on, the old
`data-content` may cause conflicts with other modules.
* `data-placement` was only used for tooltip, so it's renamed to
`data-tooltip-placement`, and removed from `createTippy`.
2023-03-23 09:56:15 +00:00
|
|
|
|
2023-04-10 23:01:20 +00:00
|
|
|
// meanwhile, if the element has no aria-label, use the tooltip content as aria-label
|
|
|
|
if (!el.hasAttribute('aria-label')) {
|
|
|
|
const content = el.getAttribute('data-tooltip-content');
|
|
|
|
if (content) {
|
|
|
|
el.setAttribute('aria-label', content);
|
Use a general approach to show tooltip, fix temporary tooltip bug (#23574)
## TLDR
* Improve performance: lazy creating the tippy instances.
* Transparently support all "tooltip" elements, no need to call
`initTooltip` again and again.
* Fix a temporary tooltip re-entrance bug, which causes showing temp
content forever.
* Upgrade vue3-calendar-heatmap to 2.0.2 with lazy tippy init
(initHeatmap time decreases from 100ms to 50ms)
## Details
### The performance
Creating a lot of tippy tooltip instances is expensive. This PR doesn't
create all tippy tooltip instances, instead, it only adds "mouseover"
event listener to necessary elements, and then switches to the tippy
tooltip
### The general approach for all tooltips
Before, dynamically generated tooltips need to be called with
`initTooltip`.
After, use MutationObserver to:
* Attach the event listeners to newly created tooltip elements, work for
Vue (easier than before)
* Catch changed attributes and update the tooltip content (better than
before)
It does help a lot, eg:
https://github.com/go-gitea/gitea/blob/1a4efa0ee9a49d48549be7479a46be133b9bc260/web_src/js/components/PullRequestMergeForm.vue#L33-L36
### Temporary tooltip re-entrance bug
To reproduce, on try.gitea.io, click the "copy clone url" quickly, then
the tooltip will be "Copied!" forever.
After this PR, with the help of `attachTippyTooltip`, the tooltip
content could be reset to the default correctly.
### Other changes
* `data-tooltip-content` is preferred from now on, the old
`data-content` may cause conflicts with other modules.
* `data-placement` was only used for tooltip, so it's renamed to
`data-tooltip-placement`, and removed from `createTippy`.
2023-03-23 09:56:15 +00:00
|
|
|
}
|
2022-08-09 12:37:34 +00:00
|
|
|
}
|
Use a general approach to show tooltip, fix temporary tooltip bug (#23574)
## TLDR
* Improve performance: lazy creating the tippy instances.
* Transparently support all "tooltip" elements, no need to call
`initTooltip` again and again.
* Fix a temporary tooltip re-entrance bug, which causes showing temp
content forever.
* Upgrade vue3-calendar-heatmap to 2.0.2 with lazy tippy init
(initHeatmap time decreases from 100ms to 50ms)
## Details
### The performance
Creating a lot of tippy tooltip instances is expensive. This PR doesn't
create all tippy tooltip instances, instead, it only adds "mouseover"
event listener to necessary elements, and then switches to the tippy
tooltip
### The general approach for all tooltips
Before, dynamically generated tooltips need to be called with
`initTooltip`.
After, use MutationObserver to:
* Attach the event listeners to newly created tooltip elements, work for
Vue (easier than before)
* Catch changed attributes and update the tooltip content (better than
before)
It does help a lot, eg:
https://github.com/go-gitea/gitea/blob/1a4efa0ee9a49d48549be7479a46be133b9bc260/web_src/js/components/PullRequestMergeForm.vue#L33-L36
### Temporary tooltip re-entrance bug
To reproduce, on try.gitea.io, click the "copy clone url" quickly, then
the tooltip will be "Copied!" forever.
After this PR, with the help of `attachTippyTooltip`, the tooltip
content could be reset to the default correctly.
### Other changes
* `data-tooltip-content` is preferred from now on, the old
`data-content` may cause conflicts with other modules.
* `data-placement` was only used for tooltip, so it's renamed to
`data-tooltip-placement`, and removed from `createTippy`.
2023-03-23 09:56:15 +00:00
|
|
|
}
|
2022-08-09 12:37:34 +00:00
|
|
|
|
2023-04-10 23:01:20 +00:00
|
|
|
// Activate the tooltip for all children elements.
|
2024-12-21 18:59:25 +00:00
|
|
|
function attachChildrenLazyTooltip(target: HTMLElement): void {
|
|
|
|
for (const el of target.querySelectorAll<HTMLElement>('[data-tooltip-content]')) {
|
2023-04-10 23:01:20 +00:00
|
|
|
attachLazyTooltip(el);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-21 13:57:42 +00:00
|
|
|
export function initGlobalTooltips(): void {
|
2023-04-10 23:01:20 +00:00
|
|
|
// use MutationObserver to detect new "data-tooltip-content" elements added to the DOM, or attributes changed
|
2024-08-10 09:46:48 +00:00
|
|
|
const observerConnect = (observer: MutationObserver) => observer.observe(document, {
|
2023-04-10 23:01:20 +00:00
|
|
|
subtree: true,
|
|
|
|
childList: true,
|
2024-03-22 14:06:53 +00:00
|
|
|
attributeFilter: ['data-tooltip-content', 'title'],
|
2023-04-10 23:01:20 +00:00
|
|
|
});
|
|
|
|
const observer = new MutationObserver((mutationList, observer) => {
|
|
|
|
const pending = observer.takeRecords();
|
|
|
|
observer.disconnect();
|
|
|
|
for (const mutation of [...mutationList, ...pending]) {
|
Use a general approach to show tooltip, fix temporary tooltip bug (#23574)
## TLDR
* Improve performance: lazy creating the tippy instances.
* Transparently support all "tooltip" elements, no need to call
`initTooltip` again and again.
* Fix a temporary tooltip re-entrance bug, which causes showing temp
content forever.
* Upgrade vue3-calendar-heatmap to 2.0.2 with lazy tippy init
(initHeatmap time decreases from 100ms to 50ms)
## Details
### The performance
Creating a lot of tippy tooltip instances is expensive. This PR doesn't
create all tippy tooltip instances, instead, it only adds "mouseover"
event listener to necessary elements, and then switches to the tippy
tooltip
### The general approach for all tooltips
Before, dynamically generated tooltips need to be called with
`initTooltip`.
After, use MutationObserver to:
* Attach the event listeners to newly created tooltip elements, work for
Vue (easier than before)
* Catch changed attributes and update the tooltip content (better than
before)
It does help a lot, eg:
https://github.com/go-gitea/gitea/blob/1a4efa0ee9a49d48549be7479a46be133b9bc260/web_src/js/components/PullRequestMergeForm.vue#L33-L36
### Temporary tooltip re-entrance bug
To reproduce, on try.gitea.io, click the "copy clone url" quickly, then
the tooltip will be "Copied!" forever.
After this PR, with the help of `attachTippyTooltip`, the tooltip
content could be reset to the default correctly.
### Other changes
* `data-tooltip-content` is preferred from now on, the old
`data-content` may cause conflicts with other modules.
* `data-placement` was only used for tooltip, so it's renamed to
`data-tooltip-placement`, and removed from `createTippy`.
2023-03-23 09:56:15 +00:00
|
|
|
if (mutation.type === 'childList') {
|
|
|
|
// mainly for Vue components and AJAX rendered elements
|
2024-12-21 18:59:25 +00:00
|
|
|
for (const el of mutation.addedNodes as NodeListOf<HTMLElement>) {
|
2024-02-08 02:42:18 +00:00
|
|
|
if (!isDocumentFragmentOrElementNode(el)) continue;
|
|
|
|
attachChildrenLazyTooltip(el);
|
|
|
|
if (el.hasAttribute('data-tooltip-content')) {
|
|
|
|
attachLazyTooltip(el);
|
Use a general approach to show tooltip, fix temporary tooltip bug (#23574)
## TLDR
* Improve performance: lazy creating the tippy instances.
* Transparently support all "tooltip" elements, no need to call
`initTooltip` again and again.
* Fix a temporary tooltip re-entrance bug, which causes showing temp
content forever.
* Upgrade vue3-calendar-heatmap to 2.0.2 with lazy tippy init
(initHeatmap time decreases from 100ms to 50ms)
## Details
### The performance
Creating a lot of tippy tooltip instances is expensive. This PR doesn't
create all tippy tooltip instances, instead, it only adds "mouseover"
event listener to necessary elements, and then switches to the tippy
tooltip
### The general approach for all tooltips
Before, dynamically generated tooltips need to be called with
`initTooltip`.
After, use MutationObserver to:
* Attach the event listeners to newly created tooltip elements, work for
Vue (easier than before)
* Catch changed attributes and update the tooltip content (better than
before)
It does help a lot, eg:
https://github.com/go-gitea/gitea/blob/1a4efa0ee9a49d48549be7479a46be133b9bc260/web_src/js/components/PullRequestMergeForm.vue#L33-L36
### Temporary tooltip re-entrance bug
To reproduce, on try.gitea.io, click the "copy clone url" quickly, then
the tooltip will be "Copied!" forever.
After this PR, with the help of `attachTippyTooltip`, the tooltip
content could be reset to the default correctly.
### Other changes
* `data-tooltip-content` is preferred from now on, the old
`data-content` may cause conflicts with other modules.
* `data-placement` was only used for tooltip, so it's renamed to
`data-tooltip-placement`, and removed from `createTippy`.
2023-03-23 09:56:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (mutation.type === 'attributes') {
|
2024-08-10 09:46:48 +00:00
|
|
|
attachTooltip(mutation.target as Element);
|
Use a general approach to show tooltip, fix temporary tooltip bug (#23574)
## TLDR
* Improve performance: lazy creating the tippy instances.
* Transparently support all "tooltip" elements, no need to call
`initTooltip` again and again.
* Fix a temporary tooltip re-entrance bug, which causes showing temp
content forever.
* Upgrade vue3-calendar-heatmap to 2.0.2 with lazy tippy init
(initHeatmap time decreases from 100ms to 50ms)
## Details
### The performance
Creating a lot of tippy tooltip instances is expensive. This PR doesn't
create all tippy tooltip instances, instead, it only adds "mouseover"
event listener to necessary elements, and then switches to the tippy
tooltip
### The general approach for all tooltips
Before, dynamically generated tooltips need to be called with
`initTooltip`.
After, use MutationObserver to:
* Attach the event listeners to newly created tooltip elements, work for
Vue (easier than before)
* Catch changed attributes and update the tooltip content (better than
before)
It does help a lot, eg:
https://github.com/go-gitea/gitea/blob/1a4efa0ee9a49d48549be7479a46be133b9bc260/web_src/js/components/PullRequestMergeForm.vue#L33-L36
### Temporary tooltip re-entrance bug
To reproduce, on try.gitea.io, click the "copy clone url" quickly, then
the tooltip will be "Copied!" forever.
After this PR, with the help of `attachTippyTooltip`, the tooltip
content could be reset to the default correctly.
### Other changes
* `data-tooltip-content` is preferred from now on, the old
`data-content` may cause conflicts with other modules.
* `data-placement` was only used for tooltip, so it's renamed to
`data-tooltip-placement`, and removed from `createTippy`.
2023-03-23 09:56:15 +00:00
|
|
|
}
|
|
|
|
}
|
2023-04-10 23:01:20 +00:00
|
|
|
observerConnect(observer);
|
Use a general approach to show tooltip, fix temporary tooltip bug (#23574)
## TLDR
* Improve performance: lazy creating the tippy instances.
* Transparently support all "tooltip" elements, no need to call
`initTooltip` again and again.
* Fix a temporary tooltip re-entrance bug, which causes showing temp
content forever.
* Upgrade vue3-calendar-heatmap to 2.0.2 with lazy tippy init
(initHeatmap time decreases from 100ms to 50ms)
## Details
### The performance
Creating a lot of tippy tooltip instances is expensive. This PR doesn't
create all tippy tooltip instances, instead, it only adds "mouseover"
event listener to necessary elements, and then switches to the tippy
tooltip
### The general approach for all tooltips
Before, dynamically generated tooltips need to be called with
`initTooltip`.
After, use MutationObserver to:
* Attach the event listeners to newly created tooltip elements, work for
Vue (easier than before)
* Catch changed attributes and update the tooltip content (better than
before)
It does help a lot, eg:
https://github.com/go-gitea/gitea/blob/1a4efa0ee9a49d48549be7479a46be133b9bc260/web_src/js/components/PullRequestMergeForm.vue#L33-L36
### Temporary tooltip re-entrance bug
To reproduce, on try.gitea.io, click the "copy clone url" quickly, then
the tooltip will be "Copied!" forever.
After this PR, with the help of `attachTippyTooltip`, the tooltip
content could be reset to the default correctly.
### Other changes
* `data-tooltip-content` is preferred from now on, the old
`data-content` may cause conflicts with other modules.
* `data-placement` was only used for tooltip, so it's renamed to
`data-tooltip-placement`, and removed from `createTippy`.
2023-03-23 09:56:15 +00:00
|
|
|
});
|
2023-04-10 23:01:20 +00:00
|
|
|
observerConnect(observer);
|
Use a general approach to show tooltip, fix temporary tooltip bug (#23574)
## TLDR
* Improve performance: lazy creating the tippy instances.
* Transparently support all "tooltip" elements, no need to call
`initTooltip` again and again.
* Fix a temporary tooltip re-entrance bug, which causes showing temp
content forever.
* Upgrade vue3-calendar-heatmap to 2.0.2 with lazy tippy init
(initHeatmap time decreases from 100ms to 50ms)
## Details
### The performance
Creating a lot of tippy tooltip instances is expensive. This PR doesn't
create all tippy tooltip instances, instead, it only adds "mouseover"
event listener to necessary elements, and then switches to the tippy
tooltip
### The general approach for all tooltips
Before, dynamically generated tooltips need to be called with
`initTooltip`.
After, use MutationObserver to:
* Attach the event listeners to newly created tooltip elements, work for
Vue (easier than before)
* Catch changed attributes and update the tooltip content (better than
before)
It does help a lot, eg:
https://github.com/go-gitea/gitea/blob/1a4efa0ee9a49d48549be7479a46be133b9bc260/web_src/js/components/PullRequestMergeForm.vue#L33-L36
### Temporary tooltip re-entrance bug
To reproduce, on try.gitea.io, click the "copy clone url" quickly, then
the tooltip will be "Copied!" forever.
After this PR, with the help of `attachTippyTooltip`, the tooltip
content could be reset to the default correctly.
### Other changes
* `data-tooltip-content` is preferred from now on, the old
`data-content` may cause conflicts with other modules.
* `data-placement` was only used for tooltip, so it's renamed to
`data-tooltip-placement`, and removed from `createTippy`.
2023-03-23 09:56:15 +00:00
|
|
|
|
|
|
|
attachChildrenLazyTooltip(document.documentElement);
|
|
|
|
}
|
|
|
|
|
2024-11-21 13:57:42 +00:00
|
|
|
export function showTemporaryTooltip(target: Element, content: Content): void {
|
2024-11-08 06:04:24 +00:00
|
|
|
// if the target is inside a dropdown, the menu will be hidden soon
|
|
|
|
// so display the tooltip on the dropdown instead
|
|
|
|
target = target.closest('.ui.dropdown') || target;
|
Use a general approach to show tooltip, fix temporary tooltip bug (#23574)
## TLDR
* Improve performance: lazy creating the tippy instances.
* Transparently support all "tooltip" elements, no need to call
`initTooltip` again and again.
* Fix a temporary tooltip re-entrance bug, which causes showing temp
content forever.
* Upgrade vue3-calendar-heatmap to 2.0.2 with lazy tippy init
(initHeatmap time decreases from 100ms to 50ms)
## Details
### The performance
Creating a lot of tippy tooltip instances is expensive. This PR doesn't
create all tippy tooltip instances, instead, it only adds "mouseover"
event listener to necessary elements, and then switches to the tippy
tooltip
### The general approach for all tooltips
Before, dynamically generated tooltips need to be called with
`initTooltip`.
After, use MutationObserver to:
* Attach the event listeners to newly created tooltip elements, work for
Vue (easier than before)
* Catch changed attributes and update the tooltip content (better than
before)
It does help a lot, eg:
https://github.com/go-gitea/gitea/blob/1a4efa0ee9a49d48549be7479a46be133b9bc260/web_src/js/components/PullRequestMergeForm.vue#L33-L36
### Temporary tooltip re-entrance bug
To reproduce, on try.gitea.io, click the "copy clone url" quickly, then
the tooltip will be "Copied!" forever.
After this PR, with the help of `attachTippyTooltip`, the tooltip
content could be reset to the default correctly.
### Other changes
* `data-tooltip-content` is preferred from now on, the old
`data-content` may cause conflicts with other modules.
* `data-placement` was only used for tooltip, so it's renamed to
`data-tooltip-placement`, and removed from `createTippy`.
2023-03-23 09:56:15 +00:00
|
|
|
const tippy = target._tippy ?? attachTooltip(target, content);
|
2022-08-09 12:37:34 +00:00
|
|
|
tippy.setContent(content);
|
2022-12-25 17:17:48 +00:00
|
|
|
if (!tippy.state.isShown) tippy.show();
|
2022-08-09 12:37:34 +00:00
|
|
|
tippy.setProps({
|
|
|
|
onHidden: (tippy) => {
|
Use a general approach to show tooltip, fix temporary tooltip bug (#23574)
## TLDR
* Improve performance: lazy creating the tippy instances.
* Transparently support all "tooltip" elements, no need to call
`initTooltip` again and again.
* Fix a temporary tooltip re-entrance bug, which causes showing temp
content forever.
* Upgrade vue3-calendar-heatmap to 2.0.2 with lazy tippy init
(initHeatmap time decreases from 100ms to 50ms)
## Details
### The performance
Creating a lot of tippy tooltip instances is expensive. This PR doesn't
create all tippy tooltip instances, instead, it only adds "mouseover"
event listener to necessary elements, and then switches to the tippy
tooltip
### The general approach for all tooltips
Before, dynamically generated tooltips need to be called with
`initTooltip`.
After, use MutationObserver to:
* Attach the event listeners to newly created tooltip elements, work for
Vue (easier than before)
* Catch changed attributes and update the tooltip content (better than
before)
It does help a lot, eg:
https://github.com/go-gitea/gitea/blob/1a4efa0ee9a49d48549be7479a46be133b9bc260/web_src/js/components/PullRequestMergeForm.vue#L33-L36
### Temporary tooltip re-entrance bug
To reproduce, on try.gitea.io, click the "copy clone url" quickly, then
the tooltip will be "Copied!" forever.
After this PR, with the help of `attachTippyTooltip`, the tooltip
content could be reset to the default correctly.
### Other changes
* `data-tooltip-content` is preferred from now on, the old
`data-content` may cause conflicts with other modules.
* `data-placement` was only used for tooltip, so it's renamed to
`data-tooltip-placement`, and removed from `createTippy`.
2023-03-23 09:56:15 +00:00
|
|
|
// reset the default tooltip content, if no default, then this temporary tooltip could be destroyed
|
|
|
|
if (!attachTooltip(target)) {
|
2022-08-09 12:37:34 +00:00
|
|
|
tippy.destroy();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
});
|
2022-07-18 22:33:34 +00:00
|
|
|
}
|