mirror of
https://github.com/moodle/moodle.git
synced 2025-01-18 05:58:34 +01:00
MDL-83005 tool_usertours: Correct user tour highlighting bugs
This way of highlighting user tour content removes the need to juggle z-index. It also resolves the problem of cloning the target element into a separate container and breaking styling.
This commit is contained in:
parent
a8b2acd35c
commit
7809800e0c
2
admin/tool/usertours/amd/build/tour.min.js
vendored
2
admin/tool/usertours/amd/build/tour.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -47,6 +47,7 @@ import PendingPromise from 'core/pending';
|
||||
* @type {number}
|
||||
*/
|
||||
const MINSPACING = 10;
|
||||
const BUFFER = 10;
|
||||
|
||||
/**
|
||||
* A user tour.
|
||||
@ -811,15 +812,6 @@ const Tour = class {
|
||||
|
||||
targetNode.data('flexitour', 'target');
|
||||
|
||||
let zIndex = this.calculateZIndex(targetNode);
|
||||
if (zIndex) {
|
||||
stepConfig.zIndex = zIndex + 1;
|
||||
}
|
||||
|
||||
if (stepConfig.zIndex) {
|
||||
currentStepNode.css('zIndex', stepConfig.zIndex + 1);
|
||||
}
|
||||
|
||||
// Add the backdrop.
|
||||
this.positionBackdrop(stepConfig);
|
||||
|
||||
@ -1209,9 +1201,8 @@ const Tour = class {
|
||||
this.currentStepConfig = null;
|
||||
}
|
||||
|
||||
// Remove the backdrop features.
|
||||
$('[data-flexitour="step-background"]').remove();
|
||||
$('[data-flexitour="step-backdrop"]').removeAttr('data-flexitour');
|
||||
// Remove the highlight attribute when the hide occurs.
|
||||
$('[data-flexitour="highlight"]').removeAttr('data-flexitour');
|
||||
|
||||
const backdrop = $('[data-flexitour="backdrop"]');
|
||||
if (backdrop.length) {
|
||||
@ -1381,6 +1372,12 @@ const Tour = class {
|
||||
break;
|
||||
}
|
||||
|
||||
let offset = '0';
|
||||
if (stepConfig.backdrop) {
|
||||
// Offset the arrow so that it points to the cut-out in the backdrop.
|
||||
offset = `-${BUFFER}, ${BUFFER}`;
|
||||
}
|
||||
|
||||
let target = this.getStepTarget(stepConfig);
|
||||
var config = {
|
||||
placement: stepConfig.placement + '-start',
|
||||
@ -1392,6 +1389,9 @@ const Tour = class {
|
||||
arrow: {
|
||||
element: '[data-role="arrow"]',
|
||||
},
|
||||
offset: {
|
||||
offset: offset
|
||||
}
|
||||
},
|
||||
onCreate: function(data) {
|
||||
recalculateArrowPosition(data);
|
||||
@ -1404,6 +1404,8 @@ const Tour = class {
|
||||
thisT.possitionNeedToBeRecalculated = false;
|
||||
recalculateStepPosition(data);
|
||||
}
|
||||
// Reset backdrop position when things update.
|
||||
thisT.recalculateBackdropPosition(stepConfig);
|
||||
},
|
||||
};
|
||||
|
||||
@ -1537,7 +1539,7 @@ const Tour = class {
|
||||
thisT.currentStepPopper.update();
|
||||
};
|
||||
|
||||
let background = $('[data-flexitour="step-background"]');
|
||||
let background = $('[data-flexitour="highlight"]');
|
||||
if (background.length) {
|
||||
target = background;
|
||||
}
|
||||
@ -1556,23 +1558,37 @@ const Tour = class {
|
||||
* @return {String} The placement after recalculate
|
||||
*/
|
||||
recalculatePlacement(stepConfig) {
|
||||
const buffer = 10;
|
||||
const arrowWidth = 16;
|
||||
let target = this.getStepTarget(stepConfig);
|
||||
let widthContent = this.currentStepNode.width() + arrowWidth;
|
||||
let targetOffsetLeft = target.offset().left - buffer;
|
||||
let targetOffsetRight = target.offset().left + target.width() + buffer;
|
||||
let targetOffsetLeft = target.offset().left - BUFFER;
|
||||
let targetOffsetRight = target.offset().left + target.width() + BUFFER;
|
||||
let placement = stepConfig.placement;
|
||||
|
||||
if (['left', 'right'].indexOf(placement) !== -1) {
|
||||
if ((targetOffsetLeft < (widthContent + buffer)) &&
|
||||
((targetOffsetRight + widthContent + buffer) > document.documentElement.clientWidth)) {
|
||||
if ((targetOffsetLeft < (widthContent + BUFFER)) &&
|
||||
((targetOffsetRight + widthContent + BUFFER) > document.documentElement.clientWidth)) {
|
||||
placement = 'top';
|
||||
}
|
||||
}
|
||||
return placement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recaculate where the backdrop and its cut-out should be.
|
||||
*
|
||||
* This is needed when highlighted elements are off the page.
|
||||
* This can be called on update to recalculate it all.
|
||||
*
|
||||
* @method recalculateBackdropPosition
|
||||
* @param {Object} stepConfig The step configuration of the step
|
||||
*/
|
||||
recalculateBackdropPosition(stepConfig) {
|
||||
if (stepConfig.backdrop) {
|
||||
this.positionBackdrop(stepConfig);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the backdrop.
|
||||
*
|
||||
@ -1584,194 +1600,125 @@ const Tour = class {
|
||||
positionBackdrop(stepConfig) {
|
||||
if (stepConfig.backdrop) {
|
||||
this.currentStepConfig.hasBackdrop = true;
|
||||
let backdrop = $('<div data-flexitour="backdrop"></div>');
|
||||
|
||||
if (stepConfig.zIndex) {
|
||||
if (stepConfig.attachPoint === 'append') {
|
||||
stepConfig.attachTo.append(backdrop);
|
||||
} else {
|
||||
backdrop.insertAfter(stepConfig.attachTo);
|
||||
}
|
||||
} else {
|
||||
// Position our backdrop above everything else.
|
||||
let backdrop = $('div[data-flexitour="backdrop"]');
|
||||
if (!backdrop.length) {
|
||||
backdrop = $('<div data-flexitour="backdrop"></div>');
|
||||
$('body').append(backdrop);
|
||||
}
|
||||
|
||||
if (this.isStepActuallyVisible(stepConfig)) {
|
||||
// The step has a visible target.
|
||||
// Punch a hole through the backdrop.
|
||||
let background = $('[data-flexitour="step-background"]');
|
||||
if (!background.length) {
|
||||
background = $('<div data-flexitour="step-background"></div>');
|
||||
}
|
||||
|
||||
let targetNode = this.getStepTarget(stepConfig);
|
||||
targetNode.attr('data-flexitour', 'highlight');
|
||||
|
||||
let buffer = 10;
|
||||
let distanceFromTop = targetNode[0].getBoundingClientRect().top;
|
||||
let relativeTop = targetNode.offset().top - distanceFromTop;
|
||||
|
||||
let colorNode = targetNode;
|
||||
if (buffer) {
|
||||
colorNode = $('body');
|
||||
}
|
||||
/*
|
||||
Draw a clip-path that makes the backdrop a window.
|
||||
The clip-path is drawn with x/y coordinates in the following sequence.
|
||||
|
||||
let drawertop = 0;
|
||||
1--------------------------------------------------2
|
||||
11 |
|
||||
| |
|
||||
| 8-----------------------------7 |
|
||||
| | | |
|
||||
| | | |
|
||||
| | | |
|
||||
10-------9 | |
|
||||
5--------------------------------------6 |
|
||||
| |
|
||||
| |
|
||||
4--------------------------------------------------3
|
||||
*/
|
||||
|
||||
// These values will help us draw the backdrop.
|
||||
const viewportHeight = $(window).height();
|
||||
const viewportWidth = $(window).width();
|
||||
const elementWidth = targetNode.outerWidth() + (BUFFER * 2);
|
||||
let elementHeight = targetNode.outerHeight() + (BUFFER * 2);
|
||||
const elementLeft = targetNode.offset().left - BUFFER;
|
||||
let elementTop = targetNode.offset().top - BUFFER - relativeTop;
|
||||
|
||||
// Check the amount of navbar overlap the highlight element has.
|
||||
// We will adjust the backdrop shape to compensate for the fixed navbar.
|
||||
let navbarOverlap = 0;
|
||||
if (targetNode.parents('[data-usertour="scroller"]').length) {
|
||||
// Determine the navbar height.
|
||||
const scrollerElement = targetNode.parents('[data-usertour="scroller"]');
|
||||
const navigationBuffer = scrollerElement.offset().top;
|
||||
if (scrollerElement.scrollTop() >= navigationBuffer) {
|
||||
drawertop = scrollerElement.scrollTop() - navigationBuffer;
|
||||
background.css({
|
||||
position: 'fixed'
|
||||
});
|
||||
}
|
||||
const navbarHeight = scrollerElement.offset().top;
|
||||
navbarOverlap = Math.max(Math.ceil(navbarHeight - elementTop), 0);
|
||||
elementTop = elementTop + navbarOverlap;
|
||||
elementHeight = elementHeight - navbarOverlap;
|
||||
}
|
||||
|
||||
background.css({
|
||||
width: targetNode.outerWidth() + buffer + buffer,
|
||||
height: targetNode.outerHeight() + buffer + buffer,
|
||||
left: targetNode.offset().left - buffer,
|
||||
top: targetNode.offset().top + drawertop - buffer,
|
||||
backgroundColor: this.calculateInherittedBackgroundColor(colorNode),
|
||||
});
|
||||
|
||||
if (targetNode.offset().left < buffer) {
|
||||
background.css({
|
||||
width: targetNode.outerWidth() + targetNode.offset().left + buffer,
|
||||
left: targetNode.offset().left,
|
||||
});
|
||||
}
|
||||
|
||||
if ((targetNode.offset().top + drawertop) < buffer) {
|
||||
background.css({
|
||||
height: targetNode.outerHeight() + targetNode.offset().top + buffer,
|
||||
top: targetNode.offset().top,
|
||||
});
|
||||
}
|
||||
|
||||
let targetRadius = targetNode.css('borderRadius');
|
||||
if (targetRadius && targetRadius !== $('body').css('borderRadius')) {
|
||||
background.css('borderRadius', targetRadius);
|
||||
}
|
||||
|
||||
let targetPosition = this.calculatePosition(targetNode);
|
||||
if (targetPosition === 'absolute') {
|
||||
background.css('position', 'fixed');
|
||||
}
|
||||
|
||||
let fader = background.clone();
|
||||
fader.css({
|
||||
backgroundColor: backdrop.css('backgroundColor'),
|
||||
opacity: backdrop.css('opacity'),
|
||||
});
|
||||
fader.attr('data-flexitour', 'step-background-fader');
|
||||
|
||||
if (!stepConfig.zIndex) {
|
||||
let targetClone = targetNode.clone();
|
||||
background.append(targetClone.first());
|
||||
$('body').append(fader);
|
||||
$('body').append(background);
|
||||
} else {
|
||||
if (stepConfig.attachPoint === 'append') {
|
||||
stepConfig.attachTo.append(background);
|
||||
// Check if the step container is in the 'top' position.
|
||||
// We will re-anchor the step container to the shifted backdrop edge as opposed to the actual element.
|
||||
if (this.currentStepNode && this.currentStepNode.length) {
|
||||
const xPlacement = this.currentStepNode[0].getAttribute('x-placement');
|
||||
if (xPlacement === 'top-start') {
|
||||
this.currentStepNode[0].style.top = `${navbarOverlap}px`;
|
||||
} else {
|
||||
fader.insertAfter(stepConfig.attachTo);
|
||||
background.insertAfter(stepConfig.attachTo);
|
||||
this.currentStepNode[0].style.top = '0px';
|
||||
}
|
||||
}
|
||||
|
||||
// Add the backdrop data to the actual target.
|
||||
// This is the part which actually does the work.
|
||||
targetNode.attr('data-flexitour', 'step-backdrop');
|
||||
let backdropPath = document.querySelector('div[data-flexitour="backdrop"]');
|
||||
const radius = 10;
|
||||
|
||||
if (stepConfig.zIndex) {
|
||||
backdrop.css('zIndex', stepConfig.zIndex);
|
||||
background.css('zIndex', stepConfig.zIndex + 1);
|
||||
targetNode.css('zIndex', stepConfig.zIndex + 2);
|
||||
}
|
||||
const bottomRight = {
|
||||
'x1': elementLeft + elementWidth - radius,
|
||||
'y1': elementTop + elementHeight,
|
||||
'x2': elementLeft + elementWidth,
|
||||
'y2': elementTop + elementHeight - radius,
|
||||
};
|
||||
|
||||
fader.fadeOut('2000', function() {
|
||||
$(this).remove();
|
||||
});
|
||||
const topRight = {
|
||||
'x1': elementLeft + elementWidth,
|
||||
'y1': elementTop + radius,
|
||||
'x2': elementLeft + elementWidth - radius,
|
||||
'y2': elementTop,
|
||||
};
|
||||
|
||||
const topLeft = {
|
||||
'x1': elementLeft + radius,
|
||||
'y1': elementTop,
|
||||
'x2': elementLeft,
|
||||
'y2': elementTop + radius,
|
||||
};
|
||||
|
||||
const bottomLeft = {
|
||||
'x1': elementLeft,
|
||||
'y1': elementTop + elementHeight - radius,
|
||||
'x2': elementLeft + radius,
|
||||
'y2': elementTop + elementHeight,
|
||||
};
|
||||
|
||||
// L = line.
|
||||
// C = Bezier curve.
|
||||
// Z = Close path.
|
||||
backdropPath.style.clipPath = `path('M 0 0 \
|
||||
L ${viewportWidth} 0 \
|
||||
L ${viewportWidth} ${viewportHeight} \
|
||||
L 0 ${viewportHeight} \
|
||||
L 0 ${elementTop + elementHeight} \
|
||||
L ${bottomRight.x1} ${bottomRight.y1} \
|
||||
C ${bottomRight.x1} ${bottomRight.y1} ${bottomRight.x2} ${bottomRight.y1} ${bottomRight.x2} ${bottomRight.y2} \
|
||||
L ${topRight.x1} ${topRight.y1} \
|
||||
C ${topRight.x1} ${topRight.y1} ${topRight.x1} ${topRight.y2} ${topRight.x2} ${topRight.y2} \
|
||||
L ${topLeft.x1} ${topLeft.y1} \
|
||||
C ${topLeft.x1} ${topLeft.y1} ${topLeft.x2} ${topLeft.y1} ${topLeft.x2} ${topLeft.y2} \
|
||||
L ${bottomLeft.x1} ${bottomLeft.y1} \
|
||||
C ${bottomLeft.x1} ${bottomLeft.y1} ${bottomLeft.x1} ${bottomLeft.y2} ${bottomLeft.x2} ${bottomLeft.y2} \
|
||||
L 0 ${elementTop + elementHeight} \
|
||||
Z'
|
||||
)`;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the inheritted z-index.
|
||||
*
|
||||
* @method calculateZIndex
|
||||
* @param {jQuery} elem The element to calculate z-index for
|
||||
* @return {Number} Calculated z-index
|
||||
*/
|
||||
calculateZIndex(elem) {
|
||||
elem = $(elem);
|
||||
if (this.requireDefaultTourZindex(elem)) {
|
||||
return 0;
|
||||
}
|
||||
while (elem.length && elem[0] !== document) {
|
||||
// Ignore z-index if position is set to a value where z-index is ignored by the browser
|
||||
// This makes behavior of this function consistent across browsers
|
||||
// WebKit always returns auto if the element is positioned.
|
||||
let position = elem.css("position");
|
||||
if (position === "absolute" || position === "fixed") {
|
||||
// IE returns 0 when zIndex is not specified
|
||||
// other browsers return a string
|
||||
// we ignore the case of nested elements with an explicit value of 0
|
||||
// <div style="z-index: -10;"><div style="z-index: 0;"></div></div>
|
||||
let value = parseInt(elem.css("zIndex"), 10);
|
||||
if (!isNaN(value) && value !== 0) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
elem = elem.parent();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the element require the default tour z-index.
|
||||
*
|
||||
* Some page elements have fixed z-index. However, their weight is not enough to cover
|
||||
* other page elements like the top navbar or a sticky footer so they use the default
|
||||
* tour z-index instead.
|
||||
*
|
||||
* @param {jQuery} elem the page element to highlight
|
||||
* @return {Boolean} true if the element requires the default tour z-index instead of the calculated one
|
||||
*/
|
||||
requireDefaultTourZindex(elem) {
|
||||
if (elem.parents('[data-region="fixed-drawer"]').length !== 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the inheritted background colour.
|
||||
*
|
||||
* @method calculateInherittedBackgroundColor
|
||||
* @param {jQuery} elem The element to calculate colour for
|
||||
* @return {String} Calculated background colour
|
||||
*/
|
||||
calculateInherittedBackgroundColor(elem) {
|
||||
// Use a fake node to compare each element against.
|
||||
let fakeNode = $('<div>').hide();
|
||||
$('body').append(fakeNode);
|
||||
let fakeElemColor = fakeNode.css('backgroundColor');
|
||||
fakeNode.remove();
|
||||
|
||||
elem = $(elem);
|
||||
while (elem.length && elem[0] !== document) {
|
||||
let color = elem.css('backgroundColor');
|
||||
if (color !== fakeElemColor) {
|
||||
return color;
|
||||
}
|
||||
elem = elem.parent();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the inheritted position.
|
||||
*
|
||||
|
@ -33,19 +33,6 @@ span[data-flexitour="container"].orphan div[data-role="arrow"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
body div[data-flexitour="step-background-fader"],
|
||||
body div[data-flexitour="step-background"] {
|
||||
position: absolute;
|
||||
background: inherit;
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
div[data-flexitour="step-background-fader"],
|
||||
[data-flexitour="step-backdrop"] > td,
|
||||
[data-flexitour="step-backdrop"] {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
span[data-flexitour="container"].orphan div[data-role="arrow"] {
|
||||
display: none;
|
||||
}
|
||||
|
@ -1,11 +1,9 @@
|
||||
/**
|
||||
* Tour step must sit above all other UI components.
|
||||
* The backdrop is the lowest point in the tour.
|
||||
* Everything else is in the container, and the target background should be at the same z-index.
|
||||
* ----- moodle
|
||||
* ---- step backdrop
|
||||
* --- step container
|
||||
* --- step target background
|
||||
*/
|
||||
$flexitour-base-zindex: 1040;
|
||||
|
||||
@ -18,24 +16,11 @@ div[data-flexitour="backdrop"] {
|
||||
z-index: #{$flexitour-base-zindex};
|
||||
}
|
||||
|
||||
// The step-background is used to highlight the region targetted in the step.
|
||||
div[data-flexitour="step-background-fader"],
|
||||
div[data-flexitour="step-background"] {
|
||||
@include border-radius($border-radius-lg);
|
||||
|
||||
// The step container, and the target background should be at the same z-index.
|
||||
padding: 10px;
|
||||
span[data-flexitour="container"] {
|
||||
// The step container, needs to be higher than the backdrop.
|
||||
z-index: ($flexitour-base-zindex + 1);
|
||||
}
|
||||
|
||||
span[data-flexitour="container"],
|
||||
div[data-flexitour="step-background-fader"],
|
||||
[data-flexitour="step-backdrop"] > td,
|
||||
[data-flexitour="step-backdrop"] {
|
||||
// The step container, and the target background should be at the same z-index.
|
||||
z-index: ($flexitour-base-zindex + 2);
|
||||
}
|
||||
|
||||
span[data-flexitour="container"] {
|
||||
.modal-dialog {
|
||||
/**
|
||||
|
@ -37094,11 +37094,9 @@ body {
|
||||
/**
|
||||
* Tour step must sit above all other UI components.
|
||||
* The backdrop is the lowest point in the tour.
|
||||
* Everything else is in the container, and the target background should be at the same z-index.
|
||||
* ----- moodle
|
||||
* ---- step backdrop
|
||||
* --- step container
|
||||
* --- step target background
|
||||
*/
|
||||
div[data-flexitour=backdrop] {
|
||||
background-color: #000;
|
||||
@ -37106,20 +37104,10 @@ div[data-flexitour=backdrop] {
|
||||
z-index: 1040;
|
||||
}
|
||||
|
||||
div[data-flexitour=step-background-fader],
|
||||
div[data-flexitour=step-background] {
|
||||
border-radius: 0.6rem;
|
||||
padding: 10px;
|
||||
span[data-flexitour=container] {
|
||||
z-index: 1041;
|
||||
}
|
||||
|
||||
span[data-flexitour=container],
|
||||
div[data-flexitour=step-background-fader],
|
||||
[data-flexitour=step-backdrop] > td,
|
||||
[data-flexitour=step-backdrop] {
|
||||
z-index: 1042;
|
||||
}
|
||||
|
||||
span[data-flexitour=container] .modal-dialog {
|
||||
/**
|
||||
* Remove all margins to:
|
||||
|
@ -37028,11 +37028,9 @@ body {
|
||||
/**
|
||||
* Tour step must sit above all other UI components.
|
||||
* The backdrop is the lowest point in the tour.
|
||||
* Everything else is in the container, and the target background should be at the same z-index.
|
||||
* ----- moodle
|
||||
* ---- step backdrop
|
||||
* --- step container
|
||||
* --- step target background
|
||||
*/
|
||||
div[data-flexitour=backdrop] {
|
||||
background-color: #000;
|
||||
@ -37040,20 +37038,10 @@ div[data-flexitour=backdrop] {
|
||||
z-index: 1040;
|
||||
}
|
||||
|
||||
div[data-flexitour=step-background-fader],
|
||||
div[data-flexitour=step-background] {
|
||||
border-radius: 0.3rem;
|
||||
padding: 10px;
|
||||
span[data-flexitour=container] {
|
||||
z-index: 1041;
|
||||
}
|
||||
|
||||
span[data-flexitour=container],
|
||||
div[data-flexitour=step-background-fader],
|
||||
[data-flexitour=step-backdrop] > td,
|
||||
[data-flexitour=step-backdrop] {
|
||||
z-index: 1042;
|
||||
}
|
||||
|
||||
span[data-flexitour=container] .modal-dialog {
|
||||
/**
|
||||
* Remove all margins to:
|
||||
|
Loading…
x
Reference in New Issue
Block a user