Skip to content

Commit

Permalink
Merge pull request #1295 from usablica/intro-refresh-cached-element
Browse files Browse the repository at this point in the history
fix: refactoring the introForElements function + adding tests
  • Loading branch information
afshinm authored Jun 13, 2021
2 parents e55efdb + 9d328a9 commit 64639dd
Show file tree
Hide file tree
Showing 7 changed files with 414 additions and 173 deletions.
175 changes: 175 additions & 0 deletions src/core/fetchIntroSteps.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
import forEach from "../util/forEach";
import cloneObject from "../util/cloneObject";
import createElement from "../util/createElement";

/**
* Finds all Intro steps from the data-* attributes and the options.steps array
*
* @api private
* @param targetElm
* @returns {[]}
*/
export default function fetchIntroSteps(targetElm) {
const allIntroSteps = targetElm.querySelectorAll("*[data-intro]");
let introItems = [];

if (this._options.steps) {
//use steps passed programmatically
forEach(this._options.steps, (step) => {
const currentItem = cloneObject(step);

//set the step
currentItem.step = introItems.length + 1;

currentItem.title = currentItem.title || "";

//use querySelector function only when developer used CSS selector
if (typeof currentItem.element === "string") {
//grab the element with given selector from the page
currentItem.element = document.querySelector(currentItem.element);
}

//intro without element
if (
typeof currentItem.element === "undefined" ||
currentItem.element === null
) {
let floatingElementQuery = document.querySelector(
".introjsFloatingElement"
);

if (floatingElementQuery === null) {
floatingElementQuery = createElement("div", {
className: "introjsFloatingElement",
});

document.body.appendChild(floatingElementQuery);
}

currentItem.element = floatingElementQuery;
currentItem.position = "floating";
}

currentItem.position = currentItem.position || this._options.tooltipPosition;
currentItem.scrollTo = currentItem.scrollTo || this._options.scrollTo;

if (typeof currentItem.disableInteraction === "undefined") {
currentItem.disableInteraction = this._options.disableInteraction;
}

if (currentItem.element !== null) {
introItems.push(currentItem);
}
});
} else {
//use steps from data-* annotations
const elmsLength = allIntroSteps.length;
let disableInteraction;

//if there's no element to intro
if (elmsLength < 1) {
return [];
}

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

// skip hidden elements
if (currentElement.style.display === "none") {
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;
}

if (step > 0) {
introItems[step - 1] = {
element: currentElement,
title: currentElement.getAttribute("data-title") || "",
intro: currentElement.getAttribute("data-intro"),
step: parseInt(currentElement.getAttribute("data-step"), 10),
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,
};
}
});

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

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

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

if (currentElement.hasAttribute("data-disable-interaction")) {
disableInteraction = !!currentElement.getAttribute(
"data-disable-interaction"
);
} else {
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,
};
}
});
}

//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]);
}
}

introItems = tempIntroItems;

//Ok, sort all items with given steps
introItems.sort((a, b) => a.step - b.step);

return introItems;
}
175 changes: 8 additions & 167 deletions src/core/introForElement.js
Original file line number Diff line number Diff line change
@@ -1,186 +1,27 @@
import addOverlayLayer from "./addOverlayLayer";
import cloneObject from "../util/cloneObject";
import forEach from "../util/forEach";
import DOMEvent from "./DOMEvent";
import { nextStep } from "./steps";
import onKeyDown from "./onKeyDown";
import onResize from "./onResize";
import createElement from "../util/createElement";
import fetchIntroSteps from "./fetchIntroSteps";

/**
* Initiate a new introduction/guide from an element in the page
*
* @api private
* @method _introForElement
* @method introForElement
* @param {Object} targetElm
* @param {String} group
* @returns {Boolean} Success or not?
*/
export default function introForElement(targetElm, group) {
const allIntroSteps = targetElm.querySelectorAll("*[data-intro]");
let introItems = [];

if (this._options.steps) {
//use steps passed programmatically
forEach(this._options.steps, (step) => {
const currentItem = cloneObject(step);

//set the step
currentItem.step = introItems.length + 1;

currentItem.title = currentItem.title || "";

//use querySelector function only when developer used CSS selector
if (typeof currentItem.element === "string") {
//grab the element with given selector from the page
currentItem.element = document.querySelector(currentItem.element);
}

//intro without element
if (
typeof currentItem.element === "undefined" ||
currentItem.element === null
) {
let floatingElementQuery = document.querySelector(
".introjsFloatingElement"
);

if (floatingElementQuery === null) {
floatingElementQuery = createElement("div", {
className: "introjsFloatingElement",
});

document.body.appendChild(floatingElementQuery);
}

currentItem.element = floatingElementQuery;
currentItem.position = "floating";
}

currentItem.scrollTo = currentItem.scrollTo || this._options.scrollTo;

if (typeof currentItem.disableInteraction === "undefined") {
currentItem.disableInteraction = this._options.disableInteraction;
}

if (currentItem.element !== null) {
introItems.push(currentItem);
}
});
} else {
//use steps from data-* annotations
const elmsLength = allIntroSteps.length;
let disableInteraction;

//if there's no element to intro
if (elmsLength < 1) {
return false;
}

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

// skip hidden elements
if (currentElement.style.display === "none") {
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;
}

if (step > 0) {
introItems[step - 1] = {
element: currentElement,
title: currentElement.getAttribute("data-title") || "",
intro: currentElement.getAttribute("data-intro"),
step: parseInt(currentElement.getAttribute("data-step"), 10),
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,
};
}
});

//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++;
}
}

if (currentElement.hasAttribute("data-disable-interaction")) {
disableInteraction = !!currentElement.getAttribute(
"data-disable-interaction"
);
} else {
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,
};
}
});
}
export default function introForElement(targetElm ) {
//set it to the introJs object
const steps = fetchIntroSteps.call(this, targetElm);

//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]);
}
if (steps.length === 0) {
return false;
}

introItems = tempIntroItems;

//Ok, sort all items with given steps
introItems.sort((a, b) => a.step - b.step);

//set it to the introJs object
this._introItems = introItems;
this._introItems = steps;

//add overlay layer to the page
if (addOverlayLayer.call(this, targetElm)) {
Expand Down
8 changes: 7 additions & 1 deletion src/core/refresh.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { reAlignHints } from "./hint";
import setHelperLayerPosition from "./setHelperLayerPosition";
import placeTooltip from "./placeTooltip";
import fetchIntroSteps from "./fetchIntroSteps";

/**
* Update placement of the intro objects on the screen
* @api private
* @param {boolean} refreshSteps to refresh the intro steps as well
*/
export default function refresh() {
export default function refresh(refreshSteps) {
// re-align intros
setHelperLayerPosition.call(
this,
Expand All @@ -21,6 +23,10 @@ export default function refresh() {
document.querySelector(".introjs-disableInteraction")
);

if (refreshSteps) {
this._introItems = fetchIntroSteps.call(this, this._targetElement);
}

// re-align tooltip
if (this._currentStep !== undefined && this._currentStep !== null) {
const oldArrowLayer = document.querySelector(".introjs-arrow");
Expand Down
Loading

0 comments on commit 64639dd

Please sign in to comment.