UNPKG

@zag-js/tour

Version:

Core logic for the tour widget implemented as a state machine

321 lines (319 loc) 11 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/tour.connect.ts var tour_connect_exports = {}; __export(tour_connect_exports, { connect: () => connect }); module.exports = __toCommonJS(tour_connect_exports); var import_core = require("@zag-js/core"); var import_dom_query = require("@zag-js/dom-query"); var import_popper = require("@zag-js/popper"); var import_utils = require("@zag-js/utils"); var import_tour = require("./tour.anatomy.js"); var dom = __toESM(require("./tour.dom.js")); var import_clip_path = require("./utils/clip-path.js"); var import_step = require("./utils/step.js"); function connect(service, normalize) { const { state, context, computed, send, prop, scope } = service; const open = state.hasTag("open"); const steps = Array.from(context.get("steps")); const stepIndex = computed("stepIndex"); const step = computed("step"); const hasTarget = typeof step?.target?.() !== "undefined"; const hasNextStep = computed("hasNextStep"); const hasPrevStep = computed("hasPrevStep"); const firstStep = computed("isFirstStep"); const lastStep = computed("isLastStep"); const placement = context.get("currentPlacement"); const placementSide = (0, import_step.isTooltipPlacement)(placement) ? (0, import_popper.getPlacementSide)(placement) : void 0; const targetRect = context.get("targetRect"); const popperStyles = (0, import_popper.getPlacementStyles)({ strategy: "absolute", placement: (0, import_step.isTooltipPlacement)(placement) ? placement : void 0 }); const clipPath = (0, import_clip_path.getClipPath)({ enabled: (0, import_step.isTooltipStep)(step), rect: targetRect, rootSize: context.get("boundarySize"), radius: prop("spotlightRadius") }); const actionMap = { next() { send({ type: "STEP.NEXT", src: "actionTrigger" }); }, prev() { send({ type: "STEP.PREV", src: "actionTrigger" }); }, dismiss() { send({ type: "DISMISS", src: "actionTrigger" }); }, skip() { send({ type: "SKIP", src: "actionTrigger" }); }, goto(id) { send({ type: "STEP.SET", value: id, src: "actionTrigger" }); } }; return { open, totalSteps: steps.length, stepIndex, step, hasNextStep, hasPrevStep, firstStep, lastStep, addStep(step2) { const next = steps.concat(step2); send({ type: "STEPS.SET", value: next, src: "addStep" }); }, removeStep(id) { const next = steps.filter((step2) => step2.id !== id); send({ type: "STEPS.SET", value: next, src: "removeStep" }); }, updateStep(id, stepOverrides) { const next = steps.map((step2) => step2.id === id ? (0, import_core.mergeProps)(step2, stepOverrides) : step2); send({ type: "STEPS.SET", value: next, src: "updateStep" }); }, setSteps(steps2) { send({ type: "STEPS.SET", value: steps2, src: "setSteps" }); }, setStep(id) { send({ type: "STEP.SET", value: id }); }, start(id) { send({ type: "START", value: id }); }, isValidStep(id) { return steps.some((step2) => step2.id === id); }, isCurrentStep(id) { return Boolean(step?.id === id); }, next() { send({ type: "STEP.NEXT" }); }, prev() { send({ type: "STEP.PREV" }); }, getProgressPercent() { const index = (0, import_step.getEffectiveStepIndex)(steps, step?.id); const total = (0, import_step.getEffectiveSteps)(steps).length; return (index + 1) / total * 100; }, getProgressText() { const index = (0, import_step.getEffectiveStepIndex)(steps, step?.id); const total = (0, import_step.getEffectiveSteps)(steps).length; const details = { current: index, total }; return prop("translations").progressText?.(details) ?? ""; }, getBackdropProps() { return normalize.element({ ...import_tour.parts.backdrop.attrs, id: dom.getBackdropId(scope), dir: prop("dir"), hidden: !open, "data-state": open ? "open" : "closed", "data-type": step?.type, style: { "--tour-layer": 0, clipPath: (0, import_step.isTooltipStep)(step) ? `path("${clipPath}")` : void 0, position: (0, import_step.isDialogStep)(step) ? "fixed" : "absolute", inset: "0", willChange: (0, import_step.isTooltipStep)(step) ? "clip-path" : void 0 } }); }, getSpotlightProps() { return normalize.element({ ...import_tour.parts.spotlight.attrs, hidden: !open || !step?.target?.(), style: { "--tour-layer": 1, position: "absolute", width: (0, import_utils.toPx)(targetRect.width), height: (0, import_utils.toPx)(targetRect.height), left: (0, import_utils.toPx)(targetRect.x), top: (0, import_utils.toPx)(targetRect.y), borderRadius: (0, import_utils.toPx)(prop("spotlightRadius")), pointerEvents: "none" } }); }, getProgressTextProps() { return normalize.element({ ...import_tour.parts.progressText.attrs }); }, getPositionerProps() { return normalize.element({ ...import_tour.parts.positioner.attrs, dir: prop("dir"), id: dom.getPositionerId(scope), "data-type": step?.type, "data-placement": placement, "data-side": placementSide, style: { "--tour-layer": 2, ...step?.type === "tooltip" && popperStyles.floating } }); }, getArrowProps() { return normalize.element({ id: dom.getArrowId(scope), ...import_tour.parts.arrow.attrs, dir: prop("dir"), hidden: step?.type !== "tooltip", style: step?.type === "tooltip" ? popperStyles.arrow : void 0, opacity: hasTarget ? void 0 : 0 }); }, getArrowTipProps() { return normalize.element({ ...import_tour.parts.arrowTip.attrs, dir: prop("dir"), style: popperStyles.arrowTip }); }, getContentProps() { return normalize.element({ ...import_tour.parts.content.attrs, id: dom.getContentId(scope), dir: prop("dir"), role: "alertdialog", "aria-modal": "true", "aria-live": "polite", "aria-atomic": "true", hidden: !open, "data-state": open ? "open" : "closed", "data-type": step?.type, "data-placement": placement, "data-side": placementSide, "data-step": step?.id, "aria-labelledby": dom.getTitleId(scope), "aria-describedby": dom.getDescriptionId(scope), tabIndex: -1, onKeyDown(event) { if (event.defaultPrevented) return; if (!prop("keyboardNavigation")) return; const isRtl = prop("dir") === "rtl"; switch (event.key) { case "ArrowRight": if (!hasNextStep) return; send({ type: isRtl ? "STEP.PREV" : "STEP.NEXT", src: "keydown" }); break; case "ArrowLeft": if (!hasPrevStep) return; send({ type: isRtl ? "STEP.NEXT" : "STEP.PREV", src: "keydown" }); break; default: break; } } }); }, getTitleProps() { return normalize.element({ ...import_tour.parts.title.attrs, id: dom.getTitleId(scope), "data-placement": hasTarget ? placement : "center", "data-side": hasTarget ? placementSide : void 0 }); }, getDescriptionProps() { return normalize.element({ ...import_tour.parts.description.attrs, id: dom.getDescriptionId(scope), "data-placement": hasTarget ? placement : "center", "data-side": hasTarget ? placementSide : void 0 }); }, getCloseTriggerProps() { return normalize.element({ ...import_tour.parts.closeTrigger.attrs, "data-type": step?.type, "aria-label": prop("translations").close, onClick: actionMap.dismiss }); }, getActionTriggerProps(props) { const { action, attrs } = props.action; let actionProps = {}; switch (action) { case "next": actionProps = { "data-type": "next", disabled: !hasNextStep, "data-disabled": (0, import_dom_query.dataAttr)(!hasNextStep), "aria-label": prop("translations").nextStep, onClick: actionMap.next }; break; case "prev": actionProps = { "data-type": "prev", disabled: !hasPrevStep, "data-disabled": (0, import_dom_query.dataAttr)(!hasPrevStep), "aria-label": prop("translations").prevStep, onClick: actionMap.prev }; break; case "dismiss": actionProps = { "data-type": "close", "aria-label": prop("translations").close, onClick: actionMap.dismiss }; break; default: actionProps = { "data-type": "custom", onClick() { if (typeof action === "function") { action(actionMap); } } }; break; } return normalize.button({ ...import_tour.parts.actionTrigger.attrs, type: "button", ...attrs, ...actionProps }); } }; } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { connect });