config

mail@pastecode.io avatar
unknown
typescript
2 years ago
4.4 kB
2
Indexable
Never
import random from "lodash/random";
import { Vue3Instance } from "vue/types/v3-component-public-instance";

import { createDraftSteps } from "./scenarios/base";
import { experimentsSteps } from "./scenarios/experiments";
import { tasksSteps } from "./scenarios/tasks";

export enum Tutorials {
  "CreateDraft" = "CreateDraft",
  "Experiments" = "Eperiments",
  "Tasks" = "Tasks",
}

// @ts-ignore
export const startTutorial = async (tutorial: Tutorials, context: Vue3Instance) => {
  let steps;

  context.activeTour = tutorial;

  context.tour = context?.$shepherd({
    useModalOverlay: true,
    keyboardNavigation: false,
    exitOnEsc: true,
    stepsContainer: document.querySelector(".tutorial__wrapper"),
    modalContainer: document.querySelector(".tutorial__wrapper"),
    confirmCancel: true,
    confirmCancelMessage: "Are you sure you want to stop the tutorial?",
    defaultStepOptions: {
      classes: "shadow-md bg-purple-dark custom-step",
      cancelIcon: {
        enabled: true,
      },
      popperOptions: {
        modifiers: [{ name: "offset", options: { offset: [0, 50] } }],
      },
    },
  });

  context.buttonStart = {
    text: "Start tutorial",
    classes: "v-btn v-btn--is-elevated v-btn--has-bg theme--light v-size--default primary",
    action: await context.tour.next,
  };

  context.buttonNext = {
    text: "Next",
    classes: "v-btn v-btn--is-elevated v-btn--has-bg theme--light v-size--default primary",
    action: context.tour.next,
  };

  context.buttonFinish = {
    text: "Finish",
    classes: "v-btn v-btn--is-elevated v-btn--has-bg theme--light v-size--default primary",
    action: context.tour.complete,
  };

  context.buttonNextTutorial = {
    text: "Next tutorial section",
    classes: "v-btn v-btn--is-elevated v-btn--has-bg theme--light v-size--default primary",
    action: () => {
      context.tour.complete();

      switch (context.activeTour) {
        case "CreateDraft":
          startTutorial(Tutorials.Experiments, context);
          break;
        case "Experiments":
          startTutorial(Tutorials.Tasks, context);
          break;
        default:
          break;
      }
    },
  };

  switch (tutorial) {
    case Tutorials.CreateDraft:
      steps = await createDraftSteps(context);
      break;
    case Tutorials.Experiments:
      steps = await experimentsSteps(context);
      context.tour.randomSeed = random();
      break;
    case Tutorials.Tasks:
      steps = await tasksSteps(context);
      break;

    default:
      break;
  }

  type Step = {
    when?: { show?: () => void; cancel?: () => void; complete?: () => void };
  };

  context.tour.on("start", () => {
    context.taskCreatorPersistent = true;
  });

  ["cancel", "complete"].forEach((event) => {
    context.tour.on(event, () => {
      context.tour = undefined;
      context.activeTour = undefined;
      context.taskCreatorPersistent = false;
    });
  });

  steps?.forEach((step: Step) => {
    if (!step.when) {
      step.when = {};
    }
    const showFunc = step.when.show;
    step.when.show = () => {
      addProgressToHeader(context.tour);
      if (showFunc) {
        showFunc();
      }
    };
  });
  context.tour.addSteps(steps);

  context.tour.start();
};

// @ts-ignore
function addProgressToHeader(tour: Shepherd.Tour) {
  const currentStepElement = tour.getCurrentStep().el;
  const header = currentStepElement.querySelector(".shepherd-header");
  const progress = document.createElement("span");
  progress.style.setProperty("margin-right", "315px");
  progress.innerText = `${tour.steps.indexOf(tour.getCurrentStep()) + 1}/${tour.steps.length}`;
  header.insertBefore(progress, currentStepElement.querySelector(".shepherd-cancel-icon"));
}

export function findDrawflowNodeByName(regex: string | RegExp): Element | undefined {
  return Array.from(document.querySelectorAll("div.drawflow-node").values()).find(
    (e: Element) => e.textContent?.match(regex) ?? false
  );
}

export function waitForElement(
  selector: string,
  callback: (el: Element) => void,
  condition?: (el: Element) => boolean,
  intervalMs = 500
) {
  const interval = setInterval(() => {
    try {
      const element = document.querySelector(selector);
      if (element && (!condition || condition(element))) {
        callback(element);
        clearInterval(interval);
        return;
      }
    } catch {
      clearInterval(interval);
    }
  }, intervalMs);
}