budibase/packages/builder/src/components/portal/onboarding/TourPopover.svelte

168 lines
4.2 KiB
Svelte

<script>
import { Popover, Layout, Heading, Body, Button, Link } from "@budibase/bbui"
import { TOURS, getCurrentStepIdx } from "./tours.js"
import { goto, layout, isActive } from "@roxi/routify"
import { builderStore } from "stores/builder"
let popoverAnchor
let popover
let tourSteps = null
let tourStep
let tourStepIdx
let lastStep
let skipping = false
$: tourNodes = { ...$builderStore.tourNodes }
$: tourKey = $builderStore.tourKey
$: tourStepKey = $builderStore.tourStepKey
$: tour = TOURS[tourKey]
$: tourOnSkip = tour?.onSkip
const updateTourStep = (targetStepKey, tourKey) => {
if (!tourKey) {
tourSteps = null
tourStepIdx = null
lastStep = null
tourStep = null
popoverAnchor = null
popover = null
skipping = false
return
}
if (!tourSteps?.length) {
tourSteps = [...tour.steps]
}
tourStepIdx = getCurrentStepIdx(tourSteps, targetStepKey)
lastStep = tourStepIdx + 1 == tourSteps.length
tourStep = { ...tourSteps[tourStepIdx] }
tourStep.onLoad()
}
$: updateTourStep(tourStepKey, tourKey)
const showPopover = (tourStep, tourNodes, popover) => {
if (!tourStep) {
return
}
popoverAnchor = tourNodes[tourStep.id]
popover?.show()
}
$: showPopover(tourStep, tourNodes, popover)
const navigateStep = step => {
if (step.route) {
const activeNav = $layout.children.find(c => $isActive(c.path))
if (activeNav) {
builderStore.setPreviousTopNavPath(
activeNav.path,
window.location.pathname
)
$goto($builderStore.previousTopNavPath[step.route] || step.route)
}
}
}
const nextStep = async () => {
if (!lastStep === true) {
let target = tourSteps[tourStepIdx + 1]
if (target) {
builderStore.update(state => ({
...state,
tourStepKey: target.id,
}))
navigateStep(target)
} else {
console.warn("Could not retrieve step")
}
} else {
if (typeof tourStep.onComplete === "function") {
tourStep.onComplete()
}
popover.hide()
if (tour.endRoute) {
$goto(tour.endRoute)
}
}
}
</script>
{#if tourKey}
{#key tourStepKey}
<Popover
align={tourStep?.align}
bind:this={popover}
anchor={popoverAnchor}
maxWidth={300}
dismissible={false}
offset={12}
handlePostionUpdate={tourStep?.positionHandler}
customZindex={3}
>
<div class="tour-content">
<Layout noPadding gap="M">
<div class="tour-header">
<Heading size="XS">{tourStep?.title || "-"}</Heading>
{#if tourSteps?.length > 1}
<div>{`${tourStepIdx + 1}/${tourSteps?.length}`}</div>
{/if}
</div>
<Body size="S">
<span class="tour-body">
{#if tourStep.layout}
<svelte:component this={tourStep.layout} />
{:else}
{tourStep?.body || ""}
{/if}
</span>
</Body>
<div class="tour-footer">
<div class="tour-navigation">
{#if typeof tourOnSkip === "function" && !lastStep}
<Link
secondary
quiet
on:click={() => {
skipping = true
tourOnSkip()
if (tour.endRoute) {
$goto(tour.endRoute)
}
}}
disabled={skipping}
>
Skip
</Link>
{/if}
<Button cta on:click={nextStep} disabled={skipping}>
<div>{lastStep ? "Finish" : "Next"}</div>
</Button>
</div>
</div>
</Layout>
</div>
</Popover>
{/key}
{/if}
<style>
.tour-content {
padding: var(--spacing-xl);
}
.tour-navigation {
grid-gap: var(--spacing-xl);
display: flex;
justify-content: end;
align-items: center;
}
.tour-body :global(.feature-list) {
margin-bottom: 0px;
padding-left: var(--spacing-xl);
}
.tour-header {
display: flex;
align-items: center;
justify-content: space-between;
}
</style>