271 lines
10 KiB
JavaScript
271 lines
10 KiB
JavaScript
"use strict";
|
|
import { HelperFunctions } from "./HelperFunctions.js";
|
|
import { universalInterfaceActions as UIA } from "../data/Universal-Actions.js";
|
|
export class Popover {
|
|
static defaultElementData = {
|
|
popoverElement: {
|
|
target: null,
|
|
hideEvents: [],
|
|
},
|
|
sourceElement: {
|
|
target: null,
|
|
hideEvents: [],
|
|
},
|
|
parentElement: {
|
|
target: null,
|
|
hideEvents: [],
|
|
},
|
|
};
|
|
static async processPopoverData(
|
|
sourceElement,
|
|
parentElement,
|
|
templateData,
|
|
elementData = Popover.defaultElementData,
|
|
sourceEvent = ""
|
|
) {
|
|
// -- RENDER THE POPOVER
|
|
elementData.parentElement.target = parentElement;
|
|
elementData.sourceElement.target = sourceElement;
|
|
|
|
let elementDataArray = Object.keys(elementData).map((key) => {
|
|
let newData = elementData[key];
|
|
newData.name = key;
|
|
return newData;
|
|
});
|
|
|
|
let popover = await Popover.createAndPositionPopover(
|
|
templateData,
|
|
elementDataArray,
|
|
sourceEvent
|
|
);
|
|
|
|
return popover;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {Event} event - the event (usually hover) that generated the tooltip
|
|
* @param {JQueryObject} $html - the html of the entire app
|
|
* @param {String} dataElementSelector - a string to select the parent element with the relevant data for this tooltip
|
|
*/
|
|
static async generateTooltip(event, $html, dataElementSelector, sourceEvent) {
|
|
if (!$html.jquery) $html = $($html);
|
|
|
|
let sourceElement = event.currentTarget;
|
|
let parentDataElement = sourceElement.closest(dataElementSelector);
|
|
|
|
let templateData = Popover.createTemplateData(parentDataElement, "tooltip", {
|
|
content: sourceElement.dataset.tooltipText,
|
|
});
|
|
let popover = await Popover.processPopoverData(
|
|
sourceElement,
|
|
$html,
|
|
templateData,
|
|
{
|
|
...Popover.defaultElementData,
|
|
},
|
|
sourceEvent
|
|
);
|
|
let eventString = "mouseenter mouseleave";
|
|
let selectorString = `[data-tooltip], .popover[data-popover-id="tooltip"]`;
|
|
$html
|
|
.off(eventString, selectorString)
|
|
.on(eventString, selectorString, async (event) => {
|
|
let { type, currentTarget } = event;
|
|
let isMouseOver = type === "mouseover" || type === "mouseenter";
|
|
!currentTarget.jquery && (currentTarget = $(currentTarget));
|
|
|
|
//if our current target is our source element or the popover itself
|
|
if (currentTarget.is($(sourceElement)) || currentTarget.is($(popover))) {
|
|
if (!popover.hoveredElements) popover.hoveredElements = [];
|
|
let { hoveredElements } = popover;
|
|
let isInArray = Popover.JqObjectInArray(
|
|
hoveredElements,
|
|
currentTarget
|
|
);
|
|
if (isMouseOver) {
|
|
//if we're already tracking it, remove it
|
|
if (!isInArray) {
|
|
hoveredElements.push(currentTarget);
|
|
} else {
|
|
}
|
|
popover.hoveredElements = hoveredElements;
|
|
} else {
|
|
if (isInArray) {
|
|
//if we're already tracking it, remove it
|
|
hoveredElements = hoveredElements.filter(
|
|
(el) => !el.is(currentTarget)
|
|
);
|
|
}
|
|
popover.hoveredElements = hoveredElements;
|
|
if (popover.hoveredElements.length === 0) {
|
|
await Popover.hideAndDeletePopover(popover);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
static JqObjectInArray(objectArray, searchObject) {
|
|
let isIncluded = false;
|
|
objectArray.forEach((obj) => {
|
|
if (obj.is(searchObject)) {
|
|
isIncluded = true;
|
|
}
|
|
});
|
|
return isIncluded;
|
|
}
|
|
|
|
static createTemplateData(parentLI, partialName, context = {}) {
|
|
let template = { frameId: "", id: "", type: "" };
|
|
let dataset = filterObject($(parentLI).data(), template);
|
|
if (!dataset) {
|
|
dataset = {};
|
|
}
|
|
let popoverId = partialName;
|
|
|
|
dataset.popoverId = popoverId; //to keep there from being multiples of the same popover
|
|
return {
|
|
passedPartial: partialName,
|
|
dataset: dataset,
|
|
passedPartialContext: context,
|
|
};
|
|
}
|
|
|
|
static validateInput(inputValue, validationType, onInvalid = "") {
|
|
let valid = false;
|
|
switch (validationType) {
|
|
case "image":
|
|
valid = HelperFunctions.isImage(inputValue);
|
|
break;
|
|
default:
|
|
valid = inputValue !== undefined;
|
|
break;
|
|
}
|
|
return valid;
|
|
}
|
|
|
|
/**
|
|
* Generates a popover (tooltip, etc), and positions it from the source element
|
|
* boundingClientRect data
|
|
* @param {Object} templateData - the data to be passed to the popover
|
|
* @param {Application} parentApp - the parent application rendering the popover
|
|
* @param {HTMLElement} sourceElement - the element that is the "source" of the popover (a button, input, etc.)
|
|
*/
|
|
static async createAndPositionPopover(
|
|
templateData,
|
|
elementDataArray = [],
|
|
sourceEvent = ""
|
|
) {
|
|
let elements = elementDataArray.map((data) => data.target);
|
|
let [popoverElement, sourceElement, parentElement] = elements; //destructure the passed-in elements
|
|
|
|
let boundingRect = sourceElement.getBoundingClientRect();
|
|
|
|
let popoverTemplate = game.JTCS.templates["popover"];
|
|
// popoverElement = parentElement.find(`.popover[data-popover-id="${templateData.dataset.popoverId}"]`);
|
|
let another = parentElement.find(`.popover`);
|
|
popoverElement = parentElement.find(
|
|
`.popover[data-popover-id="${templateData.dataset.popoverId}"]`
|
|
);
|
|
let areTheSame = another.is(popoverElement);
|
|
if (another && !areTheSame) {
|
|
await Popover.hideAndDeletePopover(another);
|
|
}
|
|
|
|
if (popoverElement.length === 0) {
|
|
//if it doesn't already exist, create it
|
|
let renderedHTML = await renderTemplate(popoverTemplate, templateData);
|
|
parentElement.append(renderedHTML);
|
|
popoverElement = parentElement.find(
|
|
`.popover[data-popover-id="${templateData.dataset.popoverId}"`
|
|
);
|
|
UIA.fade(popoverElement);
|
|
}
|
|
|
|
let popoverData = elementDataArray.find((data) => data.name === "popoverElement");
|
|
|
|
popoverData.target = popoverElement;
|
|
|
|
popoverElement.css({ position: "absolute" });
|
|
popoverElement.offset({
|
|
top: boundingRect.top + boundingRect.height,
|
|
left: boundingRect.left,
|
|
});
|
|
popoverElement.focus({ focusVisible: true });
|
|
|
|
//set up a "Click Out" event handler
|
|
let popoverId = templateData.dataset.popoverId;
|
|
// //hideEvents should be list of events to hide the popover on (like blur, change, mouseout, etc)
|
|
// elementDataArray.forEach((data) => {
|
|
// let targetElement = data.target;
|
|
|
|
// data.hideEvents.forEach((eventData) => {
|
|
// let handler;
|
|
// let selector;
|
|
// let eventName;
|
|
// let options;
|
|
|
|
// if (typeof eventData === "string") {
|
|
// //if it's a simple string, just set the handler to immediaetly hide the popover on this event
|
|
// eventName = eventData;
|
|
// handler = async (event) => await Popover.hideAndDeletePopover(popoverElement);
|
|
// selector = "*";
|
|
// } else if (typeof eventData === "object") {
|
|
// //if it's an object, we'll want to do something (like validate input) first before hiding
|
|
// eventName = eventData.eventName;
|
|
|
|
// //pass the popover element and the hide function to the wrapperFunction
|
|
// options = {
|
|
// ...eventData.options,
|
|
// popover: popoverElement,
|
|
// hideFunction: Popover.hideAndDeletePopover,
|
|
// };
|
|
// handler = async (event, options) => {
|
|
// await eventData.wrapperFunction(event, options);
|
|
// };
|
|
// selector = eventData.selector;
|
|
// }
|
|
// $(targetElement)
|
|
// .off(eventName, selector)
|
|
// .on(eventName, selector, async (event) => await handler(event, options));
|
|
// });
|
|
// });
|
|
// let popoverID = popoverElement.data().popoverId;
|
|
$(document)
|
|
.off("click")
|
|
.on("click", async (event) => {
|
|
//make sure the button that originated the click wasn't
|
|
//the same one being handled by this document
|
|
if (Popover.isOutsideClick(event, sourceElement)) {
|
|
await Popover.hideAndDeletePopover(popoverElement);
|
|
}
|
|
});
|
|
|
|
return popoverElement;
|
|
}
|
|
|
|
static isOutsideClick(event, sourceElement) {
|
|
let wasOnPopover = $(event.target).closest(".popover").length > 0;
|
|
let wasOnSourceElement = $(event.target).is($(sourceElement));
|
|
if (wasOnPopover || wasOnSourceElement) {
|
|
//click was on the popover
|
|
return false;
|
|
}
|
|
//if our click is outside of our popover element
|
|
return true;
|
|
}
|
|
|
|
static async hideAndDeletePopover(popoverElement) {
|
|
if (popoverElement.timeout) {
|
|
//if the popover is already counting down to a timeout, cancel it
|
|
clearTimeout(popoverElement.timeout);
|
|
}
|
|
let popoverTimeout = setTimeout(() => {
|
|
UIA.fade(popoverElement, { isFadeOut: true });
|
|
}, 400);
|
|
//save that timeout's id on the popover
|
|
popoverElement.timeout = popoverTimeout;
|
|
}
|
|
}
|