/** @prettier */
const overlayCssClass: string = "hotkey-overlay";
function getTooltips() {
return $("." + overlayCssClass);
}
function areTooltipsDisplayed() {
return getTooltips().length > 0;
}
function removeTooltipsDOMElements() {
getTooltips().remove();
return;
}
function insertTooltipDOMElement(hotkey, element) {
if( !(element[0] instanceof Element)){
console.error("invalid element provided " + typeof element);
return;
}
// Check if the element is hidden
const computedStyle = window.getComputedStyle(element[0]);
if (computedStyle.display === 'none') {
// Execute certain code if the display property is 'none'
console.warn("Element is hidden. Cannot attach tooltip.");
return;
}
const tooltipId = 'Tooltip_' + hotkey.name.replace(/\s+/g, '').replace(/\+/g, '_');
let tooltipSpan = document.getElementById(tooltipId);
if (!tooltipSpan) {
tooltipSpan = document.createElement('span');
tooltipSpan.id = tooltipId;
tooltipSpan.className = overlayCssClass;
document.body.appendChild(tooltipSpan);
}
const replacements = {
"CONTROL": "CTRL",
"ALTERNATIVE": "ALT",
"ARROWUP": "↑",
"ARROWDOWN": "↓",
"ARROWLEFT": "←",
"ARROWRIGHT": "→",
// Add other replacements as needed
};
let modifierKbds = "";
hotkey.modifiers.forEach(modifier => {
const replacedModifier = replacements[modifier] || modifier;
const kbd = '<kbd>' + replacedModifier + '</kbd> + ';
modifierKbds = modifierKbds + kbd;
});
let kbdKey = replacements[hotkey.key] || hotkey.key;
tooltipSpan.innerHTML = '<span class="htk-name">' + hotkey.name + '</span><span class="htk-key"> '+ modifierKbds + ' <kbd>' + kbdKey + '</kbd></span>';
document.body.appendChild(tooltipSpan);
//positioning start *****************************
const rect = element[0].getBoundingClientRect();
tooltipSpan.style.position = 'absolute';
const padding = 5; // Add some padding from the element
let tooltipRect = tooltipSpan.getBoundingClientRect();
let top, left, hotkeyClass, arrowClass;
// Position tooltip based on the element's parent
if (isDescendant(element[0], '#leftNav')) {
// Position to the right and top of the element
hotkeyClass = 'leftNav-hotkey';
arrowClass = 'element-left-center';
top = window.scrollY + rect.top;
left = window.scrollX + rect.right + padding;
} else if (isDescendant(element[0], '#header')) {
// Position to the bottom of the element
hotkeyClass = 'header-hotkey';
arrowClass = 'element-top-center';
top = window.scrollY + rect.bottom + padding;
left = window.scrollX + rect.left;
} else if (isDescendant(element[0], '#content')) {
// Position to the bottom and align to the left of the element
hotkeyClass = 'content-hotkey';
arrowClass = 'element-top-center';
top = window.scrollY + rect.bottom + padding;
left = window.scrollX + rect.left;
} else if (isDescendant(element[0], '.app-footer')) {
// Position to the bottom left of the element
hotkeyClass = 'footer-hotkey';
arrowClass = 'element-bottom-center';
top = window.scrollY + rect.bottom + padding;
left = window.scrollX + rect.left;
} else {
// Default position to the bottom of the element
hotkeyClass = 'free-hotkey';
arrowClass = '';
top = window.scrollY + rect.bottom + padding;
left = window.scrollX + rect.left;
}
tooltipSpan.classList.add(hotkeyClass);
tooltipSpan.classList.add(arrowClass);
// Adjust position if the tooltip goes beyond the viewport boundaries
if (top + tooltipRect.height > window.innerHeight) {
top = window.scrollY + rect.top - tooltipRect.height - padding; // Move above the element
}
if (left + tooltipRect.width > window.innerWidth) {
left = window.scrollX + rect.right - tooltipRect.width; // Align to the right edge of the element
} else if (left < 0) {
left = window.scrollX + rect.left; // Align to the left edge of the element
}
// Update tooltip dimensions after adjustments
tooltipSpan.style.left = left + 'px';
tooltipSpan.style.top = top + 'px';
tooltipRect = tooltipSpan.getBoundingClientRect();
//Check if tooltip overlaps with other tooltips and adjust position
const existingTooltips = document.querySelectorAll('.' + overlayCssClass);
existingTooltips.forEach(existingTooltip => {
if (existingTooltip !== tooltipSpan) {
const existingRect = existingTooltip.getBoundingClientRect();
if (tooltipRect.top < existingRect.bottom && tooltipRect.bottom > existingRect.top &&
tooltipRect.left < existingRect.right && tooltipRect.right > existingRect.left) {
// Adjust the position to avoid overlap
top = existingRect.bottom + padding;
tooltipSpan.style.top = top + 'px';
tooltipRect = tooltipSpan.getBoundingClientRect(); // Update the tooltip dimensions
}
}
});
//positioning ENDS
return;
}
function isDescendant(child, parentSelector) {
const parent = document.querySelector(parentSelector);
let node = child;
while (node !== null) {
if (node === parent) {
return true;
}
node = node.parentNode;
}
return false;
}
export {
areTooltipsDisplayed,
removeTooltipsDOMElements,
insertTooltipDOMElement
}