Skip to content

Commit

Permalink
Tooltips and a bit of rollmods in codepen
Browse files Browse the repository at this point in the history
  • Loading branch information
Eunomiac committed Feb 21, 2024
1 parent 8cf1053 commit 0d0388c
Show file tree
Hide file tree
Showing 10 changed files with 251 additions and 86 deletions.
7 changes: 2 additions & 5 deletions css/style.min.css
Original file line number Diff line number Diff line change
Expand Up @@ -5127,7 +5127,6 @@ template {
text-align: center;
position: absolute;
pointer-events: none;
transform-origin: 50% 100%;
}
:root body.vtt.game.system-eunos-blades #blades-overlay .tooltip > h1 {
text-align: left;
Expand Down Expand Up @@ -8474,7 +8473,6 @@ template {
text-align: center;
position: absolute;
pointer-events: none;
transform-origin: 50% 100%;
}
:root body.vtt.game.system-eunos-blades #clocks-overlay .tooltip > h1 {
text-align: left;
Expand Down Expand Up @@ -17219,7 +17217,6 @@ template {
text-align: center;
position: absolute;
pointer-events: none;
transform-origin: 50% 100%;
}
:root body.vtt.game.system-eunos-blades #interface #chat .tooltip > h1,
:root body.vtt.game.system-eunos-blades #controls #chat .tooltip > h1,
Expand Down Expand Up @@ -21431,7 +21428,6 @@ template {
text-align: center;
position: absolute;
pointer-events: none;
transform-origin: 50% 100%;
}
:root body.vtt.game.system-eunos-blades .app.window-app .tooltip > h1 {
text-align: left;
Expand Down Expand Up @@ -23986,7 +23982,6 @@ template {
text-align: center;
position: absolute;
pointer-events: none;
transform-origin: 50% 100%;
}
:root body.vtt.game.system-eunos-blades .app.window-app.sheet .window-content form .tooltip > h1 {
text-align: left;
Expand Down Expand Up @@ -28988,6 +28983,8 @@ template {
font-style: normal;
margin-left: 0;
position: unset;
left: unset;
right: unset;
height: 20px;
}
:root body.vtt.game.system-eunos-blades .app.window-app.sheet.roll-collab .window-content form .sheet-root .roll-sheet-block.rolling-block .roll-sheet-sub-block.roll-header-block .roll-sheet-select.roll-sheet-select,
Expand Down
1 change: 0 additions & 1 deletion css/tinymce/content.min.css
Original file line number Diff line number Diff line change
Expand Up @@ -4144,7 +4144,6 @@ html .tooltip, :root .tooltip {
text-align: center;
position: absolute;
pointer-events: none;
transform-origin: 50% 100%;
}
html .tooltip > h1, :root .tooltip > h1 {
text-align: left;
Expand Down
38 changes: 38 additions & 0 deletions module/classes/BladesDirector.js
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,42 @@ class BladesDirector {
_tooltipObserver;
_tooltipElems = new Map();
_displayedTooltipID;
/**
* Adjusts the tooltip's position to ensure it remains within its parent container using jQuery methods.
* @param tooltip - The tooltip element, which can be either an HTMLElement or a JQuery<HTMLElement>.
*/
adjustTooltipPosition(tooltip$) {
// Validate tooltip position style
if (tooltip$.css("position") !== "absolute") {
throw new Error("Tooltip position must be 'absolute'.");
}
// Calculate bounds and directly apply necessary shifts to the tooltip element
const tooltipRect = tooltip$[0].getBoundingClientRect();
const containerRect = this.tooltipSection$[0].getBoundingClientRect();
// Initial position of the tooltip
const currentTop = tooltip$.position().top;
const currentLeft = tooltip$.position().left;
// Check for right overflow and adjust left position if necessary
if (tooltipRect.right > containerRect.right) {
const xShift = containerRect.right - tooltipRect.right;
tooltip$.css("left", `${currentLeft + xShift}px`);
}
// Check for left overflow and adjust left position if necessary
else if (tooltipRect.left < containerRect.left) {
const xShift = containerRect.left - tooltipRect.left;
tooltip$.css("left", `${currentLeft + xShift}px`);
}
// Check for bottom overflow and adjust top position if necessary
if (tooltipRect.bottom > containerRect.bottom) {
const yShift = containerRect.bottom - tooltipRect.bottom;
tooltip$.css("top", `${currentTop + yShift}px`);
}
// Check for top overflow and adjust top position if necessary
else if (tooltipRect.top < containerRect.top) {
const yShift = containerRect.top - tooltipRect.top;
tooltip$.css("top", `${currentTop + yShift}px`);
}
}
displayTooltip(tooltip) {
if (!tooltip.id) {
throw new Error("Tooltip must have an ID to be cloned to the overlay.");
Expand All @@ -547,6 +583,8 @@ class BladesDirector {
if (!this._tooltipElems.has(tooltip.id)) {
// Create cloned tooltip and attach it to the tooltip overlay.
const ttClone$ = $(U.changeContainer(tooltip, game.eunoblades.Director.tooltipSection$[0], true));
// Adjust the tooltip's position so it does not overflow the tooltip container
this.adjustTooltipPosition(ttClone$);
// Generate the reveal timeline and attach it to the cloned tooltip element.
const revealTimeline = U.gsap.effects.blurRevealTooltip(ttClone$[0], {
onReverseComplete() {
Expand Down
40 changes: 19 additions & 21 deletions module/core/gsap.js
Original file line number Diff line number Diff line change
Expand Up @@ -681,31 +681,24 @@ export const gsapEffects = {
},
extendTimeline: true
},
blurRemoveTooltip: {
effect: (tooltip, config) => {
const tl = U.gsap.timeline({})
.blurRemove(tooltip, { ignoreMargin: true, blur: 15, stagger: config.stagger });
return tl;
},
defaults: {
stagger: 0
},
extendTimeline: true
},
blurRevealTooltip: {
effect: (tooltip, config) => {
effect: (target, config) => {
if (!target) {
throw new Error(`blurRevealTooltip effect: tooltip element is ${target === null ? "null" : typeof target}`);
}
const tooltip$ = $(target);
return U.gsap.timeline({
paused: true,
onReverseComplete: config.onReverseComplete
// onInterrupt() { this.reverse(); }
}).fromTo(tooltip, {
})
.fromTo(tooltip$, {
filter: `blur(${config.blurStrength}px)`,
autoAlpha: 0,
xPercent: 50,
yPercent: -200,
scale: config.scale
}, {
filter: "none",
filter: "blur(0px)",
autoAlpha: 1,
xPercent: -50,
yPercent: -100,
Expand Down Expand Up @@ -809,17 +802,22 @@ export function ApplyTooltipAnimations(html) {
if (!tooltipElem) {
return;
}
const tooltip$ = $(tooltipElem);
// Find the tooltip's parent container. If its position isn't relative or absolute, set it to relative.
const tooltipContainer = $(tooltipElem).parent()[0];
if ($(tooltipContainer).css("position") !== "relative"
&& $(tooltipContainer).css("position") !== "absolute") {
$(tooltipContainer).css("position", "relative");
const tooltipContainer$ = tooltip$.parent();
if (tooltipContainer$.css("position") !== "relative"
&& tooltipContainer$.css("position") !== "absolute") {
tooltipContainer$.css("position", "relative");
}
// Set the tooltip itself to absolute positioning
$(tooltipElem).css("position", "absolute");
tooltip$.css("position", "absolute");
// Assign a unique ID to the tooltip element
const tooltipID = `tooltip-${randomID()}`;
$(tooltipElem).attr("id", tooltipID);
tooltip$.attr("id", tooltipID);
// For .tooltip-wide tooltips, adjust the aspect ratio accordingly
if (tooltip$.hasClass("tooltip-wide")) {
U.adjustTextContainerAspectRatio(tooltipElem, 6);
}
$(el).on({
mouseenter: function () {
game.eunoblades.Director.displayTooltip(tooltipElem);
Expand Down
54 changes: 46 additions & 8 deletions module/core/utilities.js
Original file line number Diff line number Diff line change
Expand Up @@ -1412,38 +1412,74 @@ const changeContainer = (elem, container, isCloning = false) => {
gsap.set(elem, relPos);
return elem;
};
/**
* Adjusts the aspect ratio of a text container to match a target ratio by modifying its font size and line height.
* This function recursively adjusts the font size and line height until the container's aspect ratio or maximum dimensions are met.
*
* @param {HTMLElement|JQuery<HTMLElement>} textContainer - The text container element or jQuery object to adjust.
* @param {number} targetRatio - The target aspect ratio (width / height) to achieve.
* @param {number} [maxHeight] - Optional maximum height for the text container.
* @param {number} [maxWidth] - Optional maximum width for the text container.
* @param {number} [minFontSize=8] - Optional minimum font size to prevent the text from becoming too small.
* @returns {void}
*/
const adjustTextContainerAspectRatio = (textContainer, targetRatio, maxHeight, maxWidth, minFontSize = 8) => {
// Ensure textContainer is an HTMLElement
textContainer = $(textContainer)[0];
// If no maxWidth is provided, initialize textContainer's width to maximum possible
if (!maxWidth) {
textContainer.style.setProperty("width", "max-content", "important");
}
else {
textContainer.style.setProperty("width", `${maxWidth}px`, "important");
}
/**
* Recursively adjusts the font size and line height of the text container.
* This function is called if the current adjustments do not meet the target aspect ratio or maximum dimensions.
*
* @returns {boolean} - Returns false if the new font size is below the minimum font size, indicating no further adjustments should be made.
*/
function recurAdjustment() {
// Ensure textContainer is an HTMLElement for each recursive call
textContainer = $(textContainer)[0];
// Calculate new font size and line height as 80% of their current values
const newFontSize = parseFloat(style.fontSize) * 0.8;
const newLineHeight = parseFloat(style.lineHeight) * 0.8;
// Stop recursion if the new font size is below the minimum
if (newFontSize < minFontSize) {
return false;
}
// Apply the new font size and line height
textContainer.style.fontSize = `${newFontSize}px`;
textContainer.style.lineHeight = `${newLineHeight}px`;
return adjustTextContainerAspectRatio(textContainer, targetRatio, lineCount ?? maxHeight, maxWidth);
// Recursively call adjustTextContainerAspectRatio with updated parameters
return adjustTextContainerAspectRatio(textContainer, targetRatio, lineCount ?? maxHeight, maxWidth, minFontSize);
}
// Get computed styles of the text container
const style = window.getComputedStyle(textContainer);
const lineHeight = parseFloat(style.lineHeight);
// If maxHeight is provided AND it is an integer that is less than lineHeight,
// assume maxHeight is referring to the number of lines, and calculate pixel
// height accordingly:
// Initialize lineCount as undefined
let lineCount = undefined;
// If maxHeight is provided and is an integer less than lineHeight, calculate lineCount
if (isInt(maxHeight) && maxHeight < lineHeight) {
lineCount = maxHeight;
}
// Get the initial width of the text container
const initialWidth = parseFloat(style.width);
// Initialize bestWidth with the initial width
let bestWidth = initialWidth;
// Flag to indicate if the maximum line count has been reached
let isAtMaxLineCount = false;
// Loop to find the best width that matches the target aspect ratio
for (let lines = 1;; lines++) {
const expectedHeight = lineHeight * lines;
const expectedWidth = initialWidth / lines;
const expectedRatio = expectedWidth / expectedHeight;
// Break the loop if the expected ratio is less than the target ratio
if (expectedRatio < targetRatio) {
break;
}
// Handle cases where lineCount is defined
if (isInt(lineCount)) {
if (lines > lineCount) {
if (recurAdjustment()) {
Expand All @@ -1453,26 +1489,28 @@ const adjustTextContainerAspectRatio = (textContainer, targetRatio, maxHeight, m
}
}
else if (maxHeight && expectedHeight > maxHeight) {
// Handle cases where maxHeight is exceeded
if (recurAdjustment()) {
return;
}
break;
}
// Update bestWidth with the expected width
bestWidth = expectedWidth;
// Check if the current line count matches the maximum line count
if (isInt(lineCount) && lines === lineCount) {
isAtMaxLineCount = true;
break;
}
}
// If a maximum width is provided but we've exceeded that,
// reduce the font size and line height by 80% and recursively return this function to try again
// If the best width exceeds maxWidth, attempt to adjust font size and line height
if (!isAtMaxLineCount && maxWidth && bestWidth > maxWidth) {
if (recurAdjustment()) {
return;
}
}
// Apply the best width
textContainer.style.width = `${bestWidth}px`;
// Apply the best width to the text container
textContainer.style.setProperty("width", `${bestWidth}px`, "important");
};
const getSvgCode = (svgDotKey, svgPathKeys) => {
const svgData = getProperty(SVGDATA, svgDotKey);
Expand Down
8 changes: 7 additions & 1 deletion scss/core/_globals.scss
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,12 @@
}

.tooltip {
// filter: none !important;
// opacity: 1 !important;
// visibility: visible !important;
// pointer-events: all !important;


visibility: hidden;
display: flex;
flex-direction: column;
Expand Down Expand Up @@ -255,7 +261,7 @@

pointer-events: none;

transform-origin: 50% 100%;
// transform-origin: 50% 100%;

// bottom: 50px;

Expand Down
2 changes: 2 additions & 0 deletions scss/sheets/_roll-collab-sheet.scss
Original file line number Diff line number Diff line change
Expand Up @@ -1571,6 +1571,8 @@
font-style: normal;
margin-left: 0;
position: unset;
left: unset;
right: unset;
height: 20px;

&.roll-sheet-select {
Expand Down
47 changes: 47 additions & 0 deletions ts/classes/BladesDirector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,50 @@ class BladesDirector {
_tooltipElems: Map<string, JQuery<HTMLElement>> = new Map<string, JQuery<HTMLElement>>();
_displayedTooltipID?: string;

/**
* Adjusts the tooltip's position to ensure it remains within its parent container using jQuery methods.
* @param tooltip - The tooltip element, which can be either an HTMLElement or a JQuery<HTMLElement>.
*/
adjustTooltipPosition(
tooltip$: JQuery<HTMLElement>
) {

// Validate tooltip position style
if (tooltip$.css("position") !== "absolute") {
throw new Error("Tooltip position must be 'absolute'.");
}

// Calculate bounds and directly apply necessary shifts to the tooltip element
const tooltipRect = tooltip$[0].getBoundingClientRect();
const containerRect = this.tooltipSection$[0].getBoundingClientRect();

// Initial position of the tooltip
const currentTop = tooltip$.position().top;
const currentLeft = tooltip$.position().left;

// Check for right overflow and adjust left position if necessary
if (tooltipRect.right > containerRect.right) {
const xShift = containerRect.right - tooltipRect.right;
tooltip$.css("left", `${currentLeft + xShift}px`);
}
// Check for left overflow and adjust left position if necessary
else if (tooltipRect.left < containerRect.left) {
const xShift = containerRect.left - tooltipRect.left;
tooltip$.css("left", `${currentLeft + xShift}px`);
}

// Check for bottom overflow and adjust top position if necessary
if (tooltipRect.bottom > containerRect.bottom) {
const yShift = containerRect.bottom - tooltipRect.bottom;
tooltip$.css("top", `${currentTop + yShift}px`);
}
// Check for top overflow and adjust top position if necessary
else if (tooltipRect.top < containerRect.top) {
const yShift = containerRect.top - tooltipRect.top;
tooltip$.css("top", `${currentTop + yShift}px`);
}
}

displayTooltip(tooltip: HTMLElement) {
if (!tooltip.id) {
throw new Error("Tooltip must have an ID to be cloned to the overlay.");
Expand All @@ -677,6 +721,9 @@ class BladesDirector {
game.eunoblades.Director.tooltipSection$[0],
true
));
// Adjust the tooltip's position so it does not overflow the tooltip container
this.adjustTooltipPosition(ttClone$);

// Generate the reveal timeline and attach it to the cloned tooltip element.
const revealTimeline = U.gsap.effects.blurRevealTooltip(
ttClone$[0],
Expand Down
Loading

0 comments on commit 0d0388c

Please sign in to comment.