Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | 2x 5x 5x 5x 5x 2x 2x 2x 2x 5x 5x | import { MASK_OFFSET } from "./constants"; import type { Step } from "../listing/types"; // check if element is part of the DOM and is visible export const isVisibleInDocument = (el: HTMLElement): boolean => document.contains(el) && !(el.offsetWidth === 0 && el.offsetHeight === 0); // find DOM elements for each step, ignore steps with no elements // set default position to "bottom-left" export function prepareSteps(steps: Step[]): Array<{ id: string; position: string; elements: HTMLElement[]; title: string; content: string; }> { return steps .map((step) => { return { ...step, elements: [].slice.apply( document.querySelectorAll(`[data-tour="${step.id}"]`), ), position: step.position || "bottom-left", }; }) .filter((step) => step.elements.length > 0); } // get rectangle of given DOM element // relative to the page, taking scroll into account const getRectFromEl = ( el: HTMLElement, ): { top: number; left: number; width: number; height: number; } => { const clientRect = el.getBoundingClientRect(); const ret = { top: clientRect.top + (window.pageYOffset || document.documentElement.scrollTop), left: clientRect.left + (window.pageXOffset || document.documentElement.scrollLeft), width: clientRect.width, height: clientRect.height, }; return ret; }; // get mask based on rectangle const getMaskFromRect = (rect: { top: number; left: number; width: number; height: number; }): { top: number; bottom: number; left: number; right: number; } => { let top = rect.top - MASK_OFFSET; if (top < 0) { top = 0; } let left = rect.left - MASK_OFFSET; if (left < 0) { left = 0; } const bottom = rect.top + rect.height + MASK_OFFSET; const right = rect.left + rect.width + MASK_OFFSET; return { top, bottom, left, right, }; }; // calculate mask for given element const getMaskFromEl = (el: HTMLElement) => getMaskFromRect(getRectFromEl(el)); // get mask that is an union of all elements' masks // calculates the rectangle that contains each individual element rectangles export const getMaskFromElements = (elements: Array<HTMLElement>) => { const masks = elements .filter(isVisibleInDocument) .map((el) => getMaskFromEl(el)); return masks.reduce( (unionMask, elMask) => { return { top: Math.min(unionMask.top, elMask.top), left: Math.min(unionMask.left, elMask.left), bottom: Math.max(unionMask.bottom, elMask.bottom), right: Math.max(unionMask.right, elMask.right), }; }, { top: Infinity, left: Infinity, right: 0, bottom: 0, }, ); }; |