diff --git a/example/html-tooltip/index.html b/example/html-tooltip/index.html index cccc146c8..d892a8b1c 100644 --- a/example/html-tooltip/index.html +++ b/example/html-tooltip/index.html @@ -70,10 +70,14 @@

Section Six

diff --git a/src/packages/hint/components/HintTooltip.ts b/src/packages/hint/components/HintTooltip.ts index 28db9c7d4..ae1c46882 100644 --- a/src/packages/hint/components/HintTooltip.ts +++ b/src/packages/hint/components/HintTooltip.ts @@ -19,7 +19,7 @@ export type HintTooltipProps = Omit< renderAsHtml?: boolean; }; -export const HintTooltip = ({ +export const HintTooltip = async ({ hintItem, closeButtonEnabled, closeButtonOnClick, @@ -28,7 +28,7 @@ export const HintTooltip = ({ className, renderAsHtml, ...props -}: HintTooltipProps) => { +}: HintTooltipProps): Promise => { const text = hintItem.hint; return Tooltip( diff --git a/src/packages/hint/components/HintsRoot.ts b/src/packages/hint/components/HintsRoot.ts index b015dbbfe..52caa7de4 100644 --- a/src/packages/hint/components/HintsRoot.ts +++ b/src/packages/hint/components/HintsRoot.ts @@ -64,7 +64,11 @@ export const HintsRoot = ({ hint }: HintsRootProps) => { if (!hintItem) return; - const referenceLayer = ReferenceLayer({ + // Create placeholder for async tooltip + const referencePlaceholder = div(); + + // Async load the reference layer + ReferenceLayer({ activeHintSignal, hintItem, @@ -87,8 +91,14 @@ export const HintsRoot = ({ hint }: HintsRootProps) => { closeButtonOnClick: (hintItem: HintItem) => hideHint(hint, hintItem), className: hint.getOption("tooltipClass"), text: hintItem.hint, + }).then((referenceLayer) => { + if (referenceLayer) { + referencePlaceholder.replaceWith(referenceLayer); + } }); + const referenceLayer = referencePlaceholder; + dom.add(root, referenceLayer); }); diff --git a/src/packages/hint/components/ReferenceLayer.ts b/src/packages/hint/components/ReferenceLayer.ts index fc1a3b074..bb40b59c1 100644 --- a/src/packages/hint/components/ReferenceLayer.ts +++ b/src/packages/hint/components/ReferenceLayer.ts @@ -15,40 +15,39 @@ export type ReferenceLayerProps = HintTooltipProps & { helperElementPadding: number; }; -export const ReferenceLayer = ({ +export const ReferenceLayer = async ({ activeHintSignal, targetElement, helperElementPadding, ...props -}: ReferenceLayerProps) => { +}: ReferenceLayerProps): Promise => { const initialActiveHintSignal = activeHintSignal.val; - return () => { - // remove the reference layer if the active hint signal is set to undefined - // e.g. when the user clicks outside the hint - if (activeHintSignal.val == undefined) return null; - - // remove the reference layer if the active hint signal changes - // and the initial active hint signal is not same as the current active hint signal (e.g. when the user clicks on another hint) - if (initialActiveHintSignal !== activeHintSignal.val) return null; - - const referenceLayer = div( - { - [dataStepAttribute]: activeHintSignal.val, - className: `${tooltipReferenceLayerClassName} ${hintReferenceClassName}`, - }, - HintTooltip(props) - ); - - setTimeout(() => { - setPositionRelativeTo( - targetElement, - referenceLayer, - props.hintItem.hintTooltipElement as HTMLElement, - helperElementPadding - ); - }, 1); - - return referenceLayer; - }; + if ( + activeHintSignal.val == undefined || + initialActiveHintSignal !== activeHintSignal.val + ) { + return null; + } + + const tooltip = await HintTooltip(props); + + const referenceLayer = div( + { + [dataStepAttribute]: activeHintSignal.val, + className: `${tooltipReferenceLayerClassName} ${hintReferenceClassName}`, + }, + tooltip + ); + + //setTimeout(() => { + setPositionRelativeTo( + targetElement, + referenceLayer, + props.hintItem.hintTooltipElement as HTMLElement, + helperElementPadding + ); + //}, 1); + + return referenceLayer; }; diff --git a/src/packages/tooltip/tooltip.test.ts b/src/packages/tooltip/tooltip.test.ts index ad246b5f9..4a54b8f34 100644 --- a/src/packages/tooltip/tooltip.test.ts +++ b/src/packages/tooltip/tooltip.test.ts @@ -47,7 +47,7 @@ describe("Tour Tooltip", () => { for (let i = 0; i < mockTour.getSteps().length; i++) { const step = mockTour.getStep(i); - const tooltip = Tooltip({ + const tooltip = await Tooltip({ className: "custom-tooltip", element: step.element as HTMLElement, position: step.position, diff --git a/src/packages/tooltip/tooltip.ts b/src/packages/tooltip/tooltip.ts index f81f6dbb4..e50ac6248 100644 --- a/src/packages/tooltip/tooltip.ts +++ b/src/packages/tooltip/tooltip.ts @@ -320,7 +320,7 @@ export type TooltipProps = { text: string; }; -export const Tooltip = ( +export const Tooltip = async ( { position: initialPosition, element, @@ -338,7 +338,7 @@ export const Tooltip = ( onClick, }: TooltipProps, children?: ChildDom[] -) => { +): Promise => { const top = dom.state("auto"); const right = dom.state("auto"); const bottom = dom.state("auto"); @@ -437,17 +437,18 @@ export const Tooltip = ( [children], ] ); - - // apply the transition effect - setTimeout(() => { - opacity.val = 1; - }, transitionDuration); - - setTimeout(() => { - // set the correct height and width of the tooltip after it has been rendered - tooltipHeight.val = tooltip.offsetHeight; - tooltipWidth.val = tooltip.offsetWidth; - }, 1); + // after tooltip creation (just before return) + queueMicrotask(() => { + requestAnimationFrame(() => { + tooltipHeight.val = tooltip.offsetHeight; + tooltipWidth.val = tooltip.offsetWidth; + + // Fade in + requestAnimationFrame(() => { + opacity.val = 1; + }); + }); + }); return tooltip; }; diff --git a/src/packages/tour/components/DisableInteraction.ts b/src/packages/tour/components/DisableInteraction.ts index efa5356ed..a23d00310 100644 --- a/src/packages/tour/components/DisableInteraction.ts +++ b/src/packages/tour/components/DisableInteraction.ts @@ -13,38 +13,35 @@ export type HelperLayerProps = { helperElementPadding: number; }; -export const DisableInteraction = ({ +export const DisableInteraction = async ({ currentStep, steps, refreshes, targetElement, helperElementPadding, -}: HelperLayerProps) => { +}: HelperLayerProps): Promise => { + // derive current step const step = dom.derive(() => currentStep.val !== undefined ? steps[currentStep.val] : null ); - return () => { - if (!step.val) { - return null; - } - - const disableInteraction = div({ - className: disableInteractionClassName, - }); - - dom.derive(() => { - // set the position of the reference layer if the refreshes signal changes - if (!step.val || refreshes.val == undefined) return; - - setPositionRelativeToStep( - targetElement, - disableInteraction, - step.val, - helperElementPadding - ); - }); - - return disableInteraction; - }; + if (!step.val) return null; + + const disableInteraction = div({ + className: disableInteractionClassName, + }); + + dom.derive(() => { + // set the position of the reference layer if the refreshes signal changes + if (!step.val || refreshes.val == undefined) return; + + setPositionRelativeToStep( + targetElement, + disableInteraction, + step.val, + helperElementPadding + ); + }); + + return disableInteraction; }; diff --git a/src/packages/tour/components/HelperLayer.ts b/src/packages/tour/components/HelperLayer.ts index 4a1f3eb1d..2f360fef9 100644 --- a/src/packages/tour/components/HelperLayer.ts +++ b/src/packages/tour/components/HelperLayer.ts @@ -38,7 +38,7 @@ export type HelperLayerProps = { helperLayerPadding: number; }; -export const HelperLayer = ({ +export const HelperLayer = async ({ currentStep, steps, refreshes, @@ -46,7 +46,7 @@ export const HelperLayer = ({ tourHighlightClass, overlayOpacity, helperLayerPadding, -}: HelperLayerProps) => { +}: HelperLayerProps): Promise => { const step = dom.derive(() => currentStep.val !== undefined ? steps[currentStep.val] : null ); diff --git a/src/packages/tour/components/OverlayLayer.ts b/src/packages/tour/components/OverlayLayer.ts index 624de8062..8d28bed4e 100644 --- a/src/packages/tour/components/OverlayLayer.ts +++ b/src/packages/tour/components/OverlayLayer.ts @@ -10,10 +10,10 @@ export type OverlayLayerProps = { onExitTour: () => Promise; }; -export const OverlayLayer = ({ +export const OverlayLayer = async ({ exitOnOverlayClick, onExitTour, -}: OverlayLayerProps) => { +}: OverlayLayerProps): Promise => { const overlayLayer = div({ className: overlayClassName, style: style({ diff --git a/src/packages/tour/components/ReferenceLayer.ts b/src/packages/tour/components/ReferenceLayer.ts index 3aae54189..de5cc2e61 100644 --- a/src/packages/tour/components/ReferenceLayer.ts +++ b/src/packages/tour/components/ReferenceLayer.ts @@ -10,16 +10,18 @@ export type ReferenceLayerProps = TourTooltipProps & { helperElementPadding: number; }; -export const ReferenceLayer = ({ +export const ReferenceLayer = async ({ targetElement, helperElementPadding, ...props -}: ReferenceLayerProps) => { +}: ReferenceLayerProps): Promise => { + // Wait for tooltip to be ready (async) + const tooltip = await TourTooltip(props); + if (!tooltip) return null; + const referenceLayer = div( - { - className: tooltipReferenceLayerClassName, - }, - TourTooltip(props) + { className: tooltipReferenceLayerClassName }, + tooltip ); dom.derive(() => { diff --git a/src/packages/tour/components/TourRoot.test.ts b/src/packages/tour/components/TourRoot.test.ts index 8c403877a..ebf1523f5 100644 --- a/src/packages/tour/components/TourRoot.test.ts +++ b/src/packages/tour/components/TourRoot.test.ts @@ -6,6 +6,12 @@ jest.mock("./OverlayLayer", () => ({ jest.mock("./DisableInteraction", () => ({ DisableInteraction: jest.fn(() => "DisableInteraction"), })); +jest.mock("./HelperLayer", () => ({ + HelperLayer: jest.fn(() => "HelperLayer"), +})); +jest.mock("./ReferenceLayer", () => ({ + ReferenceLayer: jest.fn(() => "ReferenceLayer"), +})); jest.mock("../steps", () => ({ nextStep: jest.fn(), previousStep: jest.fn() })); jest.useFakeTimers(); @@ -17,8 +23,15 @@ describe("TourRoot", () => { tour = { getCurrentStepSignal: jest.fn(() => ({ val: 0 })), getRefreshesSignal: jest.fn(() => ({ val: 0 })), - getSteps: jest.fn(() => [{ disableInteraction: false }]), - getTargetElement: jest.fn(() => "targetElement"), + getStepReadySignal: jest.fn(() => ({ val: true })), + getSteps: jest.fn(() => [ + { + disableInteraction: false, + element: document.createElement("div"), + intro: "test", + }, + ]), + getTargetElement: jest.fn(() => document.createElement("div")), getOption: jest.fn((option: keyof typeof Option) => { const options = { highlightClass: "highlight", diff --git a/src/packages/tour/components/TourRoot.ts b/src/packages/tour/components/TourRoot.ts index 505f0b950..6cbaff1c9 100644 --- a/src/packages/tour/components/TourRoot.ts +++ b/src/packages/tour/components/TourRoot.ts @@ -14,15 +14,19 @@ export type TourRootProps = { tour: Tour; }; -export const TourRoot = ({ tour }: TourRootProps) => { +export const TourRoot = async ({ + tour, +}: TourRootProps): Promise => { const currentStepSignal = tour.getCurrentStepSignal(); const refreshesSignal = tour.getRefreshesSignal(); + //const stepReadySignal = tour.getStepReadySignal(); const steps = tour.getSteps(); - const helperLayer = HelperLayer({ + const helperLayer = await HelperLayer({ currentStep: currentStepSignal, steps, refreshes: refreshesSignal, + //stepReady: stepReadySignal, targetElement: tour.getTargetElement(), tourHighlightClass: tour.getOption("highlightClass"), overlayOpacity: tour.getOption("overlayOpacity"), @@ -30,142 +34,135 @@ export const TourRoot = ({ tour }: TourRootProps) => { }); const opacity = dom.state(0); - // render the tooltip immediately when the tour starts - // but we reset the transition duration to 300ms when the tooltip is rendered for the first time let tooltipTransitionDuration = 0; - const root = div( - { - className: "introjs-tour", - style: () => style({ opacity: `${opacity.val}` }), - }, - // helperLayer should not be re-rendered when the state changes for the transition to work - helperLayer, - () => { - // do not remove this check, it is necessary for this state-binding to work - // and render the entire section every time the state changes - if (currentStepSignal.val === undefined) { - return null; - } - - const step = dom.derive(() => - currentStepSignal.val !== undefined - ? steps[currentStepSignal.val] - : null - ); - - if (!step.val) { - return null; - } - - const exitOnOverlayClick = tour.getOption("exitOnOverlayClick") === true; - const overlayLayer = OverlayLayer({ - exitOnOverlayClick, - onExitTour: async () => { - return tour.exit(); - }, - }); - - const referenceLayer = ReferenceLayer({ - step: step.val, - targetElement: tour.getTargetElement(), - refreshes: refreshesSignal, - helperElementPadding: tour.getOption("helperElementPadding"), - - transitionDuration: tooltipTransitionDuration, - - positionPrecedence: tour.getOption("positionPrecedence"), - autoPosition: tour.getOption("autoPosition"), - showStepNumbers: tour.getOption("showStepNumbers"), - - steps: tour.getSteps(), - currentStep: currentStepSignal.val, - - onBulletClick: (stepNumber: number) => { - tour.goToStep(stepNumber); - }, - - bullets: tour.getOption("showBullets"), - - buttons: tour.getOption("showButtons"), - nextLabel: tour.getOption("nextLabel"), - onNextClick: async (e: any) => { - if (!tour.isLastStep()) { - await nextStep(tour); - } else if ( - new RegExp(doneButtonClassName, "gi").test( - (e.target as HTMLElement).className - ) - ) { - await tour - .callback("complete") - ?.call(tour, tour.getCurrentStep(), "done"); - - await tour.exit(); - } - }, - prevLabel: tour.getOption("prevLabel"), - onPrevClick: async () => { - const currentStep = tour.getCurrentStep(); - if (currentStep !== undefined && currentStep > 0) { - await previousStep(tour); - } - }, - skipLabel: tour.getOption("skipLabel"), - onSkipClick: async () => { - if (tour.isLastStep()) { - await tour - .callback("complete") - ?.call(tour, tour.getCurrentStep(), "skip"); - } - - await tour.callback("skip")?.call(tour, tour.getCurrentStep()); + const overlayLayer = await OverlayLayer({ + exitOnOverlayClick: tour.getOption("exitOnOverlayClick") === true, + onExitTour: async () => tour.exit(), + }); + + const referenceContainer = div(); + const disableInteractionContainer = div(); + + dom.derive(() => { + const currentIndex = currentStepSignal.val; + if (currentIndex === undefined) return; + const step = steps[currentIndex]; + if (!step) return; + + queueMicrotask(() => updateLayers(step, currentIndex)); + }); + async function updateLayers( + step: (typeof steps)[number], + currentIndex: number + ) { + // --- ReferenceLayer --- + const referenceLayer = await ReferenceLayer({ + step, + targetElement: tour.getTargetElement(), + refreshes: refreshesSignal, + helperElementPadding: tour.getOption("helperElementPadding"), + positionPrecedence: tour.getOption("positionPrecedence"), + autoPosition: tour.getOption("autoPosition"), + showStepNumbers: tour.getOption("showStepNumbers"), + steps, + currentStep: currentIndex, + onBulletClick: (stepNumber: number) => tour.goToStep(stepNumber), + bullets: tour.getOption("showBullets"), + buttons: tour.getOption("showButtons"), + nextLabel: tour.getOption("nextLabel"), + onNextClick: async (e: any) => { + if (!tour.isLastStep()) { + await nextStep(tour); + } else if ( + new RegExp(doneButtonClassName, "gi").test( + (e.target as HTMLElement).className + ) + ) { + await tour + .callback("complete") + ?.call(tour, tour.getCurrentStep(), "done"); await tour.exit(); - }, - buttonClass: tour.getOption("buttonClass"), - nextToDone: tour.getOption("nextToDone"), - doneLabel: tour.getOption("doneLabel"), - hideNext: tour.getOption("hideNext"), - hidePrev: tour.getOption("hidePrev"), - className: step.val.tooltipClass || tour.getOption("tooltipClass"), - progress: tour.getOption("showProgress"), - progressBarAdditionalClass: tour.getOption( - "progressBarAdditionalClass" - ), - - stepNumbers: tour.getOption("showStepNumbers"), - stepNumbersOfLabel: tour.getOption("stepNumbersOfLabel"), - - scrollToElement: tour.getOption("scrollToElement"), - scrollPadding: tour.getOption("scrollPadding"), - - dontShowAgain: tour.getOption("dontShowAgain"), - onDontShowAgainChange: (checked: boolean) => { - tour.setDontShowAgain(checked); - }, - dontShowAgainLabel: tour.getOption("dontShowAgainLabel"), - renderAsHtml: tour.getOption("tooltipRenderAsHtml"), - text: step.val.title || step.val.intro, - }); - - const disableInteraction = step.val.disableInteraction - ? DisableInteraction({ + } + }, + prevLabel: tour.getOption("prevLabel"), + onPrevClick: async () => { + const currentStep = tour.getCurrentStep(); + if (currentStep !== undefined && currentStep > 0) { + await previousStep(tour); + } + }, + skipLabel: tour.getOption("skipLabel"), + onSkipClick: async () => { + if (tour.isLastStep()) { + await tour + .callback("complete") + ?.call(tour, tour.getCurrentStep(), "skip"); + } + + await tour.callback("skip")?.call(tour, tour.getCurrentStep()); + + await tour.exit(); + }, + buttonClass: tour.getOption("buttonClass"), + nextToDone: tour.getOption("nextToDone"), + doneLabel: tour.getOption("doneLabel"), + hideNext: tour.getOption("hideNext"), + hidePrev: tour.getOption("hidePrev"), + className: step.tooltipClass || tour.getOption("tooltipClass"), + progress: tour.getOption("showProgress"), + progressBarAdditionalClass: tour.getOption("progressBarAdditionalClass"), + stepNumbers: tour.getOption("showStepNumbers"), + stepNumbersOfLabel: tour.getOption("stepNumbersOfLabel"), + scrollToElement: tour.getOption("scrollToElement"), + scrollPadding: tour.getOption("scrollPadding"), + dontShowAgain: tour.getOption("dontShowAgain"), + onDontShowAgainChange: (checked) => tour.setDontShowAgain(checked), + dontShowAgainLabel: tour.getOption("dontShowAgainLabel"), + renderAsHtml: tour.getOption("tooltipRenderAsHtml"), + text: step.title || step.intro, + transitionDuration: tooltipTransitionDuration, + }); + + const safeReference = referenceLayer ?? div(); + await Promise.resolve(); + + requestAnimationFrame(() => { + referenceContainer.replaceChildren(safeReference); + disableInteractionContainer.replaceChildren(safeDisableInteraction); + }); + // --- DisableInteraction --- + const disableInteraction = + step.disableInteraction === true + ? await DisableInteraction({ currentStep: currentStepSignal, - steps: tour.getSteps(), + steps, refreshes: refreshesSignal, targetElement: tour.getTargetElement(), helperElementPadding: tour.getOption("helperElementPadding"), }) - : null; + : div(); - // wait for the helper layer to be rendered before showing the tooltip - // this is to prevent the tooltip from flickering when the helper layer is transitioning - // the 300ms delay is coming from the helper layer transition duration - tooltipTransitionDuration = 300; + const safeDisableInteraction = disableInteraction ?? div(); - return div(overlayLayer, referenceLayer, disableInteraction); - } + await Promise.resolve(); + + requestAnimationFrame(() => { + disableInteractionContainer.replaceChildren(safeDisableInteraction); + }); + } + + // Root container + const root = div( + { + className: "introjs-tour", + style: () => style({ opacity: `${opacity.val}` }), + }, + helperLayer, + overlayLayer, + referenceContainer, + disableInteractionContainer ); dom.derive(() => { @@ -179,10 +176,9 @@ export const TourRoot = ({ tour }: TourRootProps) => { } }); - setTimeout(() => { - // fade in the root element + queueMicrotask(() => { opacity.val = 1; - }, 1); + }); return root; }; diff --git a/src/packages/tour/components/TourTooltip.test.ts b/src/packages/tour/components/TourTooltip.test.ts index 56d66929f..002633544 100644 --- a/src/packages/tour/components/TourTooltip.test.ts +++ b/src/packages/tour/components/TourTooltip.test.ts @@ -1,8 +1,8 @@ import { Header } from "./TourTooltip"; describe("Header", () => { - it("renders plain text title when renderAsHtml is false", () => { - const el = Header({ + it("renders plain text title when renderAsHtml is false", async () => { + const el = await Header({ title: "Bold Text", skipLabel: "Skip", renderAsHtml: false, @@ -15,8 +15,8 @@ describe("Header", () => { expect(h1.querySelector("strong")).toBeNull(); }); - it("renders HTML title when renderAsHtml is true", () => { - const el = Header({ + it("renders HTML title when renderAsHtml is true", async () => { + const el = await Header({ title: "Bold Text", skipLabel: "Skip", renderAsHtml: true, diff --git a/src/packages/tour/components/TourTooltip.ts b/src/packages/tour/components/TourTooltip.ts index cb925a8c1..87081f404 100644 --- a/src/packages/tour/components/TourTooltip.ts +++ b/src/packages/tour/components/TourTooltip.ts @@ -270,7 +270,7 @@ const PrevButton = ({ }); }; -const Buttons = ({ +const Buttons = async ({ steps, currentStep, @@ -330,7 +330,7 @@ const Buttons = ({ ); }; -export const Header = ({ +export const Header = async ({ title, skipLabel, renderAsHtml, @@ -427,7 +427,7 @@ export type TourTooltipProps = Omit< onDontShowAgainChange: (checked: boolean) => void; }; -export const TourTooltip = ({ +export const TourTooltip = async ({ step, currentStep, steps, @@ -463,13 +463,13 @@ export const TourTooltip = ({ dontShowAgainLabel, renderAsHtml, ...props -}: TourTooltipProps) => { +}: TourTooltipProps): Promise => { const children = []; const title = step.title; const text = step.intro; const position = step.position; - children.push(Header({ title, skipLabel, renderAsHtml, onSkipClick })); + children.push(await Header({ title, skipLabel, renderAsHtml, onSkipClick })); children.push( TooltipContent({ @@ -499,7 +499,7 @@ export const TourTooltip = ({ if (buttons) { children.push( - Buttons({ + await Buttons({ steps, currentStep, @@ -518,7 +518,7 @@ export const TourTooltip = ({ ); } - const tooltip = Tooltip( + const tooltip = await Tooltip( { ...props, element: step.element as HTMLElement, diff --git a/src/packages/tour/position.ts b/src/packages/tour/position.ts index 43795bc28..e5a391d4f 100644 --- a/src/packages/tour/position.ts +++ b/src/packages/tour/position.ts @@ -11,12 +11,12 @@ export const setPositionRelativeToStep = ( step: TourStep, padding: number ) => { - setTimeout(() => { - setPositionRelativeTo( - relativeElement, - element, - step.element as HTMLElement, - step.position === "floating" ? 0 : padding - ); - }, 0); + //setTimeout(() => { + setPositionRelativeTo( + relativeElement, + element, + step.element as HTMLElement, + step.position === "floating" ? 0 : padding + ); + //}, 0); }; diff --git a/src/packages/tour/steps.ts b/src/packages/tour/steps.ts index 161ef4b74..7a5883ebd 100644 --- a/src/packages/tour/steps.ts +++ b/src/packages/tour/steps.ts @@ -46,6 +46,8 @@ export async function nextStep(tour: Tour) { const nextStep = tour.getStep(currentStep); let continueStep: boolean | undefined = true; + // Set step ready signal to false before onBeforeChange + // tour.getStepReadySignal().val = false; continueStep = await tour .callback("beforeChange") ?.call( @@ -69,6 +71,9 @@ export async function nextStep(tour: Tour) { return false; } + // Set step ready signal to true after onBeforeChange callback + //tour.getStepReadySignal().val = true; + await showElement(tour, nextStep); return true; @@ -97,6 +102,9 @@ export async function previousStep(tour: Tour) { const nextStep = tour.getStep(currentStep); let continueStep: boolean | undefined = true; + // Set step ready signal to false before onBeforeChange + //tour.getStepReadySignal().val = false; + continueStep = await tour .callback("beforeChange") ?.call( @@ -112,6 +120,9 @@ export async function previousStep(tour: Tour) { return false; } + // Set step ready signal to true after onBeforeChange callback + //tour.getStepReadySignal().val = true; + await showElement(tour, nextStep); return true; diff --git a/src/packages/tour/tour.ts b/src/packages/tour/tour.ts index 1563eb2cd..492874bca 100644 --- a/src/packages/tour/tour.ts +++ b/src/packages/tour/tour.ts @@ -30,6 +30,7 @@ export class Tour implements Package { private _steps: TourStep[] = []; private _currentStepSignal = dom.state(undefined); private _refreshesSignal = dom.state(0); + private _stepReadySignal = dom.state(false); private _root: Element | undefined; private _direction: "forward" | "backward"; private readonly _targetElement: HTMLElement; @@ -181,6 +182,14 @@ export class Tour implements Package { return this._refreshesSignal; } + /** + * Returns the underlying state of the step ready signal + * This is an internal method and should not be used outside of the package. + */ + getStepReadySignal() { + return this._stepReadySignal; + } + /** * Get the current step of the tour */ @@ -422,9 +431,9 @@ export class Tour implements Package { /** * Create the root element for the tour */ - private createRoot() { + private async createRoot() { if (!this._root) { - this._root = TourRoot({ tour: this }); + this._root = await TourRoot({ tour: this }); dom.add(this.getTargetElement(), this._root); } } @@ -432,11 +441,11 @@ export class Tour implements Package { /** * Deletes the root element and recreates it */ - private recreateRoot() { + private async recreateRoot() { if (this._root) { this._root.remove(); this._root = undefined; - this.createRoot(); + await this.createRoot(); } } @@ -445,7 +454,7 @@ export class Tour implements Package { */ async start() { if (await start(this)) { - this.createRoot(); + await this.createRoot(); this.enableKeyboardNavigation(); this.enableRefreshOnResize(); }