Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allows for local step asynchronous events #1266

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions example/programmatic/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,21 @@ <h4>Section Six</h4>
{
element: '#step4',
intro: "Another step.",
position: 'bottom'
position: 'bottom',
onchange () {
alert('changed to step 4')
}
},
{
element: '#step5',
intro: 'Get it, use it.'
intro: 'Get it, use it.',
onbeforechange (next) {
// assign dynamically loaded element
this.element = document.querySelector('#step5')
this.position = 'bottom'

// call next when done
next()
}
}
]
});
Expand Down
104 changes: 42 additions & 62 deletions src/core/introForElement.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ import createElement from "../util/createElement";
* @param {String} group
* @returns {Boolean} Success or not?
*/
export default function introForElement(targetElm, group) {
const allIntroSteps = targetElm.querySelectorAll("*[data-intro]");
export default function introForElement (targetElm, group) {
let introItems = [];

if (this._options.steps) {
Expand Down Expand Up @@ -68,6 +67,9 @@ export default function introForElement(targetElm, group) {
}
});
} else {
const allIntroSteps = targetElm.querySelectorAll("*[data-intro]");
const introWithStepCount = allIntroSteps.filter(elem => elem.getAttribute('data-step') !== null).length;

//use steps from data-* annotations
const elmsLength = allIntroSteps.length;
let disableInteraction;
Expand All @@ -77,6 +79,9 @@ export default function introForElement(targetElm, group) {
return false;
}

const noStepIntroItems = [];
let nextStep = introWithStepCount;

forEach(allIntroSteps, (currentElement) => {
// PR #80
// start intro for groups of elements
Expand All @@ -89,22 +94,21 @@ export default function introForElement(targetElm, group) {
return;
}

const step = parseInt(currentElement.getAttribute("data-step"), 10);

if (currentElement.hasAttribute("data-disable-interaction")) {
disableInteraction = !!currentElement.getAttribute(
"data-disable-interaction"
);
} else {
disableInteraction = this._options.disableInteraction;
}
// add intro items without data-step
if (currentElement.getAttribute("data-step") === null) {
if (currentElement.hasAttribute("data-disable-interaction")) {
disableInteraction = !!currentElement.getAttribute(
"data-disable-interaction"
);
} else {
disableInteraction = this._options.disableInteraction;
}

if (step > 0) {
introItems[step - 1] = {
noStepIntroItems.push({
element: currentElement,
title: currentElement.getAttribute("data-title") || "",
intro: currentElement.getAttribute("data-intro"),
step: parseInt(currentElement.getAttribute("data-step"), 10),
step: nextStep++,
tooltipClass: currentElement.getAttribute("data-tooltipclass"),
highlightClass: currentElement.getAttribute("data-highlightclass"),
position:
Expand All @@ -114,29 +118,9 @@ export default function introForElement(targetElm, group) {
currentElement.getAttribute("data-scrollto") ||
this._options.scrollTo,
disableInteraction,
};
}
});

//next add intro items without data-step
//todo: we need a cleanup here, two loops are redundant
let nextStep = 0;

forEach(allIntroSteps, (currentElement) => {
// PR #80
// start intro for groups of elements
if (group && currentElement.getAttribute("data-intro-group") !== group) {
return;
}

if (currentElement.getAttribute("data-step") === null) {
while (true) {
if (typeof introItems[nextStep] === "undefined") {
break;
} else {
nextStep++;
}
}
});
} else {
const step = parseInt(currentElement.getAttribute("data-step"), 10);

if (currentElement.hasAttribute("data-disable-interaction")) {
disableInteraction = !!currentElement.getAttribute(
Expand All @@ -146,35 +130,31 @@ export default function introForElement(targetElm, group) {
disableInteraction = this._options.disableInteraction;
}

introItems[nextStep] = {
element: currentElement,
title: currentElement.getAttribute("data-title") || "",
intro: currentElement.getAttribute("data-intro"),
step: nextStep + 1,
tooltipClass: currentElement.getAttribute("data-tooltipclass"),
highlightClass: currentElement.getAttribute("data-highlightclass"),
position:
currentElement.getAttribute("data-position") ||
this._options.tooltipPosition,
scrollTo:
currentElement.getAttribute("data-scrollto") ||
this._options.scrollTo,
disableInteraction,
};
if (step > 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a scary function to change mainly because we don't have any tests for it.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, the point is to use a single loop, instead of two. As such, existing tests already cover this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if we have tests for this? there are two ways of adding the steps, using the data-* attributes and a JSON object. I don't think we have any tests for the data-* attr approach.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just started refactoring this function and adding some tests. I will ping you once it's merged.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WIP #1295

introItems[step - 1] = {
element: currentElement,
title: currentElement.getAttribute("data-title") || "",
intro: currentElement.getAttribute("data-intro"),
step,
tooltipClass: currentElement.getAttribute("data-tooltipclass"),
highlightClass: currentElement.getAttribute("data-highlightclass"),
position:
currentElement.getAttribute("data-position") ||
this._options.tooltipPosition,
scrollTo:
currentElement.getAttribute("data-scrollto") ||
this._options.scrollTo,
disableInteraction,
};
}
}
});
}

//removing undefined/null elements
const tempIntroItems = [];
for (let z = 0; z < introItems.length; z++) {
if (introItems[z]) {
// copy non-falsy values to the end of the array
tempIntroItems.push(introItems[z]);
}
}
//removing undefined/null elements
introItems = introItems.filter(item => item);

introItems = tempIntroItems;
introItems = [...introItems, ...noStepIntroItems];
}

//Ok, sort all items with given steps
introItems.sort((a, b) => a.step - b.step);
Expand Down
57 changes: 49 additions & 8 deletions src/core/showElement.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import appendChild from "../util/appendChild";
* @method _getProgress
* @returns current progress percentage
*/
function _getProgress() {
function _getProgress () {
// Steps are 0 indexed
const currentStep = parseInt(this._currentStep + 1, 10);
return (currentStep / this._introItems.length) * 100;
Expand All @@ -32,7 +32,7 @@ function _getProgress() {
* @api private
* @method _disableInteraction
*/
function _disableInteraction() {
function _disableInteraction () {
let disableInteractionLayer = document.querySelector(
".introjs-disableInteraction"
);
Expand All @@ -55,8 +55,10 @@ function _disableInteraction() {
* @method _showElement
* @param {Object} targetElement
*/
export default function _showElement(targetElement) {
if (typeof this._introChangeCallback !== "undefined") {
export default function _showElement (targetElement) {
if (typeof targetElement.onchange === 'function') {
targetElement.onchange();
} else if (typeof this._introChangeCallback !== "undefined") {
this._introChangeCallback.call(this, targetElement.element);
}

Expand All @@ -69,7 +71,6 @@ export default function _showElement(targetElement) {
let nextTooltipButton;
let prevTooltipButton;
let skipTooltipButton;
let scrollParent;

//check for a current step highlight class
if (typeof targetElement.highlightClass === "string") {
Expand All @@ -94,6 +95,22 @@ export default function _showElement(targetElement) {
const oldtooltipContainer = oldReferenceLayer.querySelector(
".introjs-tooltip"
);
const oldTooltipHeaderLayer = oldReferenceLayer.querySelector(
".introjs-tooltip-header"
);

// remove previous step class
oldReferenceLayer.classList.remove(`step-${targetElement.step - 1}`);
// remove next step class in case going back
oldReferenceLayer.classList.remove(`step-${targetElement.step + 1}`);
// add current step class
oldReferenceLayer.classList.add(`step-${targetElement.step}`);

if (targetElement.title) {
oldTooltipHeaderLayer.classList.remove('no-title');
} else {
oldTooltipHeaderLayer.classList.add('no-title');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How this no-title class will be used? I couldn't find any CSS changes in this PR.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it's for themes to use if they need it.

For instance, in my use case, I needed to hide the header element if there's no title.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, got it. I personally think we should keep the codebase simple and avoid adding any classes that is not used right now. I understand that you need that no-title class in your application though. Can you use your initial input (e.g. your steps list) to see if a specific step as title or not?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, thought of that but that's JS. It's better to use CSS if possible instead of JS.

About avoiding adding classes, it's not unusual for libraries to add css classes that may help developers customize the outlook, even if the library doesn't use the classes.

}

skipTooltipButton = oldReferenceLayer.querySelector(".introjs-skipbutton");
prevTooltipButton = oldReferenceLayer.querySelector(".introjs-prevbutton");
Expand Down Expand Up @@ -191,7 +208,7 @@ export default function _showElement(targetElement) {
className: highlightClass,
});
const referenceLayer = createElement("div", {
className: "introjs-tooltipReferenceLayer",
className: `introjs-tooltipReferenceLayer step-${targetElement.step}`,
});
const arrowLayer = createElement("div", {
className: "introjs-arrow",
Expand Down Expand Up @@ -232,6 +249,10 @@ export default function _showElement(targetElement) {
tooltipTextLayer.innerHTML = targetElement.intro;
tooltipTitleLayer.innerHTML = targetElement.title;

if (!targetElement.title) {
tooltipHeaderLayer.classList.add('no-title');
}

if (this._options.showBullets === false) {
bulletsLayer.style.display = "none";
}
Expand Down Expand Up @@ -314,6 +335,14 @@ export default function _showElement(targetElement) {
nextTooltipButton = createElement("a");

nextTooltipButton.onclick = () => {
const refLayer = oldReferenceLayer || document.querySelector(
".introjs-tooltipReferenceLayer"
);

if (refLayer && refLayer.classList.contains('waiting')) {
return;
}

if (self._introItems.length - 1 !== self._currentStep) {
nextStep.call(self);
} else if (/introjs-donebutton/gi.test(nextTooltipButton.className)) {
Expand All @@ -332,6 +361,14 @@ export default function _showElement(targetElement) {
prevTooltipButton = createElement("a");

prevTooltipButton.onclick = () => {
const refLayer = oldReferenceLayer || document.querySelector(
".introjs-tooltipReferenceLayer"
);

if (refLayer && refLayer.classList.contains('waiting')) {
return;
}

if (self._currentStep !== 0) {
previousStep.call(self);
}
Expand All @@ -356,7 +393,9 @@ export default function _showElement(targetElement) {
self._introCompleteCallback.call(self);
}

if (typeof self._introSkipCallback === "function") {
if (typeof targetElement.onskip === 'function') {
targetElement.onskip.call(self);
} else if (typeof self._introSkipCallback === "function") {
self._introSkipCallback.call(self);
}

Expand Down Expand Up @@ -503,7 +542,9 @@ export default function _showElement(targetElement) {

setShowElement(targetElement);

if (typeof this._introAfterChangeCallback !== "undefined") {
if (typeof targetElement.onafterchange === 'function') {
targetElement.onafterchange();
} else if (typeof this._introAfterChangeCallback !== "undefined") {
this._introAfterChangeCallback.call(this, targetElement.element);
}
}
Loading