From 067a7c093b6de94d116478423b3fa43985ef4fd5 Mon Sep 17 00:00:00 2001 From: binrysearch Date: Thu, 5 Sep 2024 09:51:27 +0100 Subject: [PATCH] pass Step to downstream components instead of State --- src/packages/tooltip/tooltip.ts | 68 +++--------- .../tour/components/ReferenceLayer.ts | 5 +- src/packages/tour/components/TourRoot.ts | 22 +++- src/packages/tour/components/TourTooltip.ts | 101 +++++++----------- src/packages/tour/exitIntro.ts | 6 +- src/packages/tour/tour.ts | 4 + 6 files changed, 73 insertions(+), 133 deletions(-) diff --git a/src/packages/tooltip/tooltip.ts b/src/packages/tooltip/tooltip.ts index 1823c9b76..43b16f7d9 100644 --- a/src/packages/tooltip/tooltip.ts +++ b/src/packages/tooltip/tooltip.ts @@ -1,10 +1,7 @@ -import getOffset, { Offset } from "../../util/getOffset"; +import { Offset } from "../../util/getOffset"; import getWindowSize from "../../util/getWindowSize"; import van, { ChildDom, State } from "../dom/van"; -import { - arrowClassName, - tooltipClassName, -} from "../tour/classNames"; +import { arrowClassName, tooltipClassName } from "../tour/classNames"; import { determineAutoPosition, TooltipPosition } from "./tooltipPosition"; const { div } = van.tags; @@ -300,8 +297,8 @@ const alignTooltip = ( }; export type TooltipProps = { - position: State; - targetOffset: State; + position: TooltipPosition; + targetOffset: Offset; hintMode: boolean; showStepNumbers: boolean; @@ -312,7 +309,7 @@ export type TooltipProps = { export const Tooltip = ( { - position, + position: initialPosition, targetOffset, hintMode = false, showStepNumbers = false, @@ -329,31 +326,28 @@ export const Tooltip = ( const left = van.state("auto"); const marginLeft = van.state("auto"); const marginTop = van.state("auto"); - const opacity = van.state(0); - const display = van.state("none"); // setting a default height for the tooltip instead of 0 to avoid flickering const tooltipHeight = van.state(150); // max width of the tooltip according to its CSS class const tooltipWidth = van.state(300); + const position = van.state(initialPosition); const windowSize = getWindowSize(); const tooltipBottomOverflow = van.derive( - () => targetOffset.val!.top + tooltipHeight.val! > windowSize.height + () => targetOffset.top + tooltipHeight.val! > windowSize.height ); - const isActive = van.state(false); - let isActiveTimeout = 0; // auto-align tooltip based on position precedence and target offset van.derive(() => { if ( + position.val !== undefined && position.val !== "floating" && autoPosition && tooltipWidth.val && - tooltipHeight.val && - position.val !== undefined + tooltipHeight.val ) { position.val = determineAutoPosition( positionPrecedence, - targetOffset.val!, + targetOffset, tooltipWidth.val, tooltipHeight.val, position.val @@ -371,7 +365,7 @@ export const Tooltip = ( ) { alignTooltip( position.val, - targetOffset.val!, + targetOffset, tooltipWidth.val, tooltipHeight.val, top, @@ -387,26 +381,10 @@ export const Tooltip = ( } }); - // show/hide tooltip - van.derive(() => { - if (isActive.val) { - display.val = "block"; - - // wait for the tooltip to be rendered before setting opacity - setTimeout(() => { - opacity.val = 1; - }, 1); - } else { - // hide the tooltip by setting display to none then opacity to 0 to avoid flickering and enable transitions - display.val = "none"; - opacity.val = 0; - } - }); - const tooltip = div( { style: () => - `top: ${top.val}; right: ${right.val}; bottom: ${bottom.val}; left: ${left.val}; margin-left: ${marginLeft.val}; margin-top: ${marginTop.val}; opacity: ${opacity.val}; display: ${display.val};`, + `top: ${top.val}; right: ${right.val}; bottom: ${bottom.val}; left: ${left.val}; margin-left: ${marginLeft.val}; margin-top: ${marginTop.val};`, className: () => `${tooltipClassName} introjs-${position.val}`, role: "dialog", }, @@ -419,27 +397,5 @@ export const Tooltip = ( ] ); - // recalculate tooltip width/height when targetOffset changes - // a targetOffset change means the tooltip width/height needs to be recalculated - van.derive(() => { - if (tooltip && targetOffset.val) { - isActive.val = false; - - // wait for the tooltip to be rendered before calculating the position - setTimeout(() => { - const tooltipOffset = getOffset(tooltip); - tooltipHeight.val = tooltipOffset.height; - tooltipWidth.val = tooltipOffset.width; - }, 1); - - setTimeout(() => { - isActive.val = true; - - // reset the timeout to a higher value to avoid flickering - isActiveTimeout = 350; - }, isActiveTimeout); - } - }); - return tooltip; }; diff --git a/src/packages/tour/components/ReferenceLayer.ts b/src/packages/tour/components/ReferenceLayer.ts index b2260e0c0..74fee132d 100644 --- a/src/packages/tour/components/ReferenceLayer.ts +++ b/src/packages/tour/components/ReferenceLayer.ts @@ -1,19 +1,16 @@ import van from "../../dom/van"; import { tooltipReferenceLayerClassName } from "../classNames"; import { setPositionRelativeToStep } from "../position"; -import { TourStep } from "../steps"; import { TourTooltip, TourTooltipProps } from "./TourTooltip"; const { div } = van.tags; export type ReferenceLayerProps = TourTooltipProps & { - step: TourStep; targetElement: HTMLElement; helperElementPadding: number; }; export const ReferenceLayer = ({ - step, targetElement, helperElementPadding, ...props @@ -28,7 +25,7 @@ export const ReferenceLayer = ({ setPositionRelativeToStep( targetElement, referenceLayer, - step, + props.step, helperElementPadding ); diff --git a/src/packages/tour/components/TourRoot.ts b/src/packages/tour/components/TourRoot.ts index 8a097a1ca..54744fdf7 100644 --- a/src/packages/tour/components/TourRoot.ts +++ b/src/packages/tour/components/TourRoot.ts @@ -6,6 +6,7 @@ import { DisableInteraction } from "./DisableInteraction"; import { OverlayLayer } from "./OverlayLayer"; import { nextStep, previousStep } from "../steps"; import { doneButtonClassName } from "../classNames"; +import { style } from "../../../util/style"; const { div } = van.tags; @@ -26,8 +27,14 @@ export const TourRoot = ({ tour }: TourRootProps) => { helperLayerPadding: tour.getOption("helperElementPadding"), }); + const opacity = van.state(0); + const root = div( - { className: "introjs-tour" }, + { + className: "introjs-tour", + style: () => + style({ transition: "all 250ms ease-out", opacity: `${opacity.val}` }), + }, // helperLayer should not be re-rendered when the state changes for the transition to work helperLayer, () => { @@ -63,7 +70,7 @@ export const TourRoot = ({ tour }: TourRootProps) => { showStepNumbers: tour.getOption("showStepNumbers"), steps: tour.getSteps(), - currentStep, + currentStep: currentStep.val, onBulletClick: (stepNumber: number) => { tour.goToStep(stepNumber); @@ -147,9 +154,18 @@ export const TourRoot = ({ tour }: TourRootProps) => { van.derive(() => { // to clean up the root element when the tour is done if (currentStep.val === undefined) { - root.remove(); + opacity.val = 0; + + setTimeout(() => { + root.remove(); + }, 250); } + }); + setTimeout(() => { + opacity.val = 1; + }, 1); + return root; }; diff --git a/src/packages/tour/components/TourTooltip.ts b/src/packages/tour/components/TourTooltip.ts index 86ab2d404..0abac0db9 100644 --- a/src/packages/tour/components/TourTooltip.ts +++ b/src/packages/tour/components/TourTooltip.ts @@ -47,17 +47,15 @@ const DontShowAgain = ({ }; const Bullets = ({ + step, steps, - currentStep, onBulletClick, }: { + step: TourStep, steps: TourStep[]; - currentStep: State; onBulletClick: (stepNumber: number) => void; }): HTMLElement => { - const step = van.derive(() => steps[currentStep.val!]); - return div({ className: bulletsClassName }, [ ul({ role: "tablist" }, [ ...steps.map(({ step: stepNumber }) => { @@ -69,7 +67,7 @@ const Bullets = ({ a({ role: "tab", className: () => - `${step.val!.step === stepNumber ? activeClassName : ""}`, + `${step.step === stepNumber ? activeClassName : ""}`, onclick: (e: any) => { const stepNumberAttribute = ( e.target as HTMLElement @@ -96,10 +94,10 @@ const ProgressBar = ({ progressBarAdditionalClass }: { steps: TourStep[]; - currentStep: State; + currentStep: number; progressBarAdditionalClass: string; }) => { - const progress = van.derive(() => ((currentStep.val!) / steps.length) * 100); + const progress = van.derive(() => ((currentStep) / steps.length) * 100); return div({ className: progressClassName }, [ div({ @@ -164,7 +162,7 @@ const NextButton = ({ buttonClass }: { steps: TourStep[]; - currentStep: State; + currentStep: number; nextLabel: string; doneLabel: string; @@ -175,18 +173,13 @@ const NextButton = ({ onClick: (e: any) => void; buttonClass: string; }) => { - const isFullButton = van.derive( - () => currentStep.val === 0 && steps.length > 1 && hidePrev - ); - - const isLastStep = van.derive( - () => currentStep.val === steps.length - 1 || steps.length === 1 - ); + const isFullButton = currentStep === 0 && steps.length > 1 && hidePrev; + const isLastStep = currentStep === steps.length - 1 || steps.length === 1; const isDisabled = van.derive(() => { // when the current step is the last one or there is only one step to show return ( - isLastStep.val && + isLastStep && !hideNext && !nextToDone ); @@ -194,7 +187,7 @@ const NextButton = ({ const isDoneButton = van.derive(() => { return ( - isLastStep.val && + isLastStep && !hideNext && nextToDone ); @@ -214,7 +207,7 @@ const NextButton = ({ classNames.push(disabledButtonClassName); } - if (isFullButton.val) { + if (isFullButton) { classNames.push(fullButtonClassName); } @@ -238,35 +231,29 @@ const PrevButton = ({ }: { label: string; steps: TourStep[]; - currentStep: State; + currentStep: number; hidePrev: boolean; hideNext: boolean; onClick: (e: any) => void; buttonClass: string; }) => { - const isDisabled = van.derive(() => { - // when the current step is the first one and there are more steps to show - return currentStep.val === 0 && steps.length > 1 && !hidePrev; - }); - - const isFullButton = van.derive(() => { - // when the current step is the last one or there is only one step to show - return ( - (currentStep.val === steps.length - 1 || steps.length === 1) && hideNext - ); - }); + // when the current step is the first one and there are more steps to show + const disabled = currentStep === 0 && steps.length > 1 && !hidePrev; + // when the current step is the last one or there is only one step to show + const isFullButton = + (currentStep === steps.length - 1 || steps.length === 1) && hideNext; return Button({ label, onClick, - disabled: () => isDisabled.val, + disabled, className: () => { const classNames = [buttonClass, previousButtonClassName]; if (isFullButton) { classNames.push(fullButtonClassName); } - if (isDisabled.val) { + if (disabled) { classNames.push(disabledButtonClassName); } @@ -293,7 +280,7 @@ const Buttons = ({ onPrevClick, }: { steps: TourStep[]; - currentStep: State; + currentStep: number; buttonClass: string; @@ -308,18 +295,13 @@ const Buttons = ({ prevLabel: string; onPrevClick: (e: any) => void; }) => { - const isLastStep = van.derive( - () => currentStep.val === steps.length - 1 || steps.length === 1 - ); - - const isFirstStep = van.derive( - () => currentStep.val === 0 && steps.length > 1 - ); + const isLastStep = currentStep === steps.length - 1 || steps.length === 1 + const isFirstStep = currentStep === 0 && steps.length > 1 return div( { className: tooltipButtonsClassName }, () => - isFirstStep.val && hidePrev + isFirstStep && hidePrev ? null : PrevButton({ label: prevLabel, @@ -331,7 +313,7 @@ const Buttons = ({ buttonClass, }), () => - isLastStep.val && hideNext + isLastStep && hideNext ? null : NextButton({ currentStep, @@ -392,8 +374,9 @@ const scroll = ({ }; export type TourTooltipProps = Omit & { + step: TourStep; steps: TourStep[]; - currentStep: State; + currentStep: number; bullets: boolean; onBulletClick: (stepNumber: number) => void; @@ -426,8 +409,9 @@ export type TourTooltipProps = Omit { - const step = van.derive(() => - currentStep.val !== undefined ? steps[currentStep.val] : null - ); - - return () => { - // there is nothing to be shown if the step is not defined - if (!step.val) { - return null; - } - const children = []; - const title = van.derive(() => step.val!.title); - const text = van.derive(() => step.val!.intro); - const position = van.derive(() => step.val!.position); - const targetOffset = van.derive(() => - getOffset(step.val!.element as HTMLElement) - ); + const title = step.title; + const text = step.intro; + const position = step.position; + const targetOffset = getOffset(step.element as HTMLElement); - children.push(Header({ title: title.val!, skipLabel, onSkipClick })); + children.push(Header({ title, skipLabel, onSkipClick })); children.push(div({ className: tooltipTextClassName }, p(text))); @@ -489,7 +461,7 @@ export const TourTooltip = ({ } if (bullets) { - children.push(Bullets({ steps, currentStep, onBulletClick })); + children.push(Bullets({ step, steps, onBulletClick })); } if (progress) { @@ -499,7 +471,7 @@ export const TourTooltip = ({ } if (stepNumbers) { - children.push(StepNumber({ step: step.val!, steps, stepNumbersOfLabel })); + children.push(StepNumber({ step, steps, stepNumbersOfLabel })); } if (buttons) { @@ -534,12 +506,11 @@ export const TourTooltip = ({ ); scroll({ - step: step.val!, + step, tooltip, scrollToElement: scrollToElement, scrollPadding: scrollPadding, }); return tooltip; - }; }; diff --git a/src/packages/tour/exitIntro.ts b/src/packages/tour/exitIntro.ts index 125f5eb6e..4d1968875 100644 --- a/src/packages/tour/exitIntro.ts +++ b/src/packages/tour/exitIntro.ts @@ -2,11 +2,7 @@ import removeShowElement from "./removeShowElement"; import { removeChild, removeAnimatedChild } from "../../util/removeChild"; import { Tour } from "./tour"; import { - disableInteractionClassName, floatingElementClassName, - helperLayerClassName, - overlayClassName, - tooltipReferenceLayerClassName, } from "./classNames"; import { queryElementByClassName, @@ -79,7 +75,7 @@ export default async function exitIntro( await tour.callback("exit")?.call(tour); // set the step to default - tour.setCurrentStep(-1); + tour.resetCurrentStep(); return true; } diff --git a/src/packages/tour/tour.ts b/src/packages/tour/tour.ts index d12472c30..6f94d22b2 100644 --- a/src/packages/tour/tour.ts +++ b/src/packages/tour/tour.ts @@ -185,6 +185,10 @@ export class Tour implements Package { return this._currentStep.val; } + resetCurrentStep() { + this._currentStep.val = undefined; + } + /** * Set the current step of the tour and the direction of the tour * @param step