@destyler/tour 0.0.7-beta.7 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs DELETED
@@ -1,910 +0,0 @@
1
- import { createAnatomy } from '@zag-js/anatomy';
2
- import { mergeProps, guards, createMachine, ref } from '@zag-js/core';
3
- import { createScope, dataAttr, MAX_Z_INDEX, getWindow, isHTMLElement, contains, getDocument } from '@zag-js/dom-query';
4
- import { getPlacementStyles, getPlacement } from '@zag-js/popper';
5
- import { trackDismissableBranch } from '@zag-js/dismissable';
6
- import { trapFocus } from '@zag-js/focus-trap';
7
- import { trackInteractOutside } from '@zag-js/interact-outside';
8
- import { compact, prevIndex, nextIndex, isString, isEqual, createSplitProps } from '@zag-js/utils';
9
- import { createProps } from '@zag-js/types';
10
-
11
- const anatomy = createAnatomy("tour").parts(
12
- "content",
13
- "actionTrigger",
14
- "closeTrigger",
15
- "progressText",
16
- "title",
17
- "description",
18
- "positioner",
19
- "arrow",
20
- "arrowTip",
21
- "backdrop",
22
- "spotlight"
23
- );
24
- const parts = anatomy.build();
25
-
26
- const dom = createScope({
27
- getPositionerId: (ctx) => ctx.ids?.positioner ?? `tour-positioner-${ctx.id}`,
28
- getContentId: (ctx) => ctx.ids?.content ?? `tour-content-${ctx.id}`,
29
- getTitleId: (ctx) => ctx.ids?.title ?? `tour-title-${ctx.id}`,
30
- getDescriptionId: (ctx) => ctx.ids?.description ?? `tour-desc-${ctx.id}`,
31
- getArrowId: (ctx) => ctx.ids?.arrow ?? `tour-arrow-${ctx.id}`,
32
- getBackdropId: (ctx) => ctx.ids?.backdrop ?? `tour-backdrop-${ctx.id}`,
33
- getContentEl: (ctx) => dom.getById(ctx, dom.getContentId(ctx)),
34
- getPositionerEl: (ctx) => dom.getById(ctx, dom.getPositionerId(ctx)),
35
- getBackdropEl: (ctx) => dom.getById(ctx, dom.getBackdropId(ctx))
36
- });
37
-
38
- function getClipPath(options) {
39
- const {
40
- radius = 0,
41
- rootSize: { width: w, height: h },
42
- rect: { width, height, x, y },
43
- enabled = true
44
- } = options;
45
- if (!enabled)
46
- return "";
47
- const {
48
- topLeft = 0,
49
- topRight = 0,
50
- bottomRight = 0,
51
- bottomLeft = 0
52
- } = typeof radius === "number" ? { topLeft: radius, topRight: radius, bottomRight: radius, bottomLeft: radius } : radius;
53
- return `M${w},${h} H0 V0 H${w} V${h} Z M${x + topLeft},${y} a${topLeft},${topLeft},0,0,0-${topLeft},${topLeft} V${height + y - bottomLeft} a${bottomLeft},${bottomLeft},0,0,0,${bottomLeft},${bottomLeft} H${width + x - bottomRight} a${bottomRight},${bottomRight},0,0,0,${bottomRight}-${bottomRight} V${y + topRight} a${topRight},${topRight},0,0,0-${topRight}-${topRight} Z`;
54
- }
55
-
56
- function isTooltipStep(step) {
57
- return step?.type === "tooltip";
58
- }
59
- function isTooltipPlacement(placement) {
60
- return placement != null && placement !== "center";
61
- }
62
- function normalizeStep(step) {
63
- if (step.type === "floating") {
64
- return { backdrop: false, arrow: false, placement: "bottom-end", ...step };
65
- }
66
- if (step.target == null || step.type === "dialog") {
67
- return { type: "dialog", placement: "center", backdrop: true, ...step };
68
- }
69
- if (!step.type || step.type === "tooltip") {
70
- return { type: "tooltip", arrow: true, backdrop: true, ...step };
71
- }
72
- return step;
73
- }
74
- function findStep(steps, id) {
75
- const res = id != null ? steps.find((step) => step.id === id) : null;
76
- return res ? normalizeStep(res) : null;
77
- }
78
- function findStepIndex(steps, id) {
79
- return id != null ? steps.findIndex((step) => step.id === id) : -1;
80
- }
81
-
82
- function connect(state, send, normalize) {
83
- const open = state.hasTag("open");
84
- const steps = Array.from(state.context.steps);
85
- const stepIndex = state.context.stepIndex;
86
- const step = state.context.step;
87
- const hasTarget = typeof step?.target?.() !== "undefined";
88
- const hasNextStep = state.context.hasNextStep;
89
- const hasPrevStep = state.context.hasPrevStep;
90
- const firstStep = state.context.isFirstStep;
91
- const lastStep = state.context.isLastStep;
92
- const placement = state.context.currentPlacement;
93
- const targetRect = state.context.targetRect;
94
- const popperStyles = getPlacementStyles({
95
- strategy: "absolute",
96
- placement: isTooltipPlacement(placement) ? placement : void 0
97
- });
98
- const clipPath = getClipPath({
99
- enabled: isTooltipStep(step),
100
- rect: targetRect,
101
- rootSize: state.context.boundarySize,
102
- radius: state.context.spotlightRadius
103
- });
104
- const actionMap = {
105
- next() {
106
- send({ type: "STEP.NEXT", src: "actionTrigger" });
107
- },
108
- prev() {
109
- send({ type: "STEP.PREV", src: "actionTrigger" });
110
- },
111
- dismiss() {
112
- send({ type: "DISMISS", src: "actionTrigger" });
113
- },
114
- goto(id) {
115
- send({ type: "STEP.SET", value: id, src: "actionTrigger" });
116
- }
117
- };
118
- return {
119
- open,
120
- totalSteps: steps.length,
121
- stepIndex,
122
- step,
123
- hasNextStep,
124
- hasPrevStep,
125
- firstStep,
126
- lastStep,
127
- addStep(step2) {
128
- const next = steps.concat(step2);
129
- send({ type: "STEPS.SET", value: next, src: "addStep" });
130
- },
131
- removeStep(id) {
132
- const next = steps.filter((step2) => step2.id !== id);
133
- send({ type: "STEPS.SET", value: next, src: "removeStep" });
134
- },
135
- updateStep(id, stepOverrides) {
136
- const next = steps.map((step2) => step2.id === id ? mergeProps(step2, stepOverrides) : step2);
137
- send({ type: "STEPS.SET", value: next, src: "updateStep" });
138
- },
139
- setSteps(steps2) {
140
- send({ type: "STEPS.SET", value: steps2, src: "setSteps" });
141
- },
142
- setStep(id) {
143
- send({ type: "STEP.SET", value: id });
144
- },
145
- start(id) {
146
- send({ type: "START", id });
147
- },
148
- isValidStep(id) {
149
- return steps.some((step2) => step2.id === id);
150
- },
151
- isCurrentStep(id) {
152
- return Boolean(step?.id === id);
153
- },
154
- next() {
155
- send({ type: "STEP.NEXT" });
156
- },
157
- prev() {
158
- send({ type: "STEP.PREV" });
159
- },
160
- getProgressPercent() {
161
- return stepIndex / steps.length * 100;
162
- },
163
- getProgressText() {
164
- const effectiveSteps = steps.filter((step2) => step2.type !== "wait");
165
- const index = findStepIndex(effectiveSteps, step?.id);
166
- const details = { current: index, total: effectiveSteps.length };
167
- return state.context.translations.progressText?.(details) ?? "";
168
- },
169
- getBackdropProps() {
170
- return normalize.element({
171
- ...parts.backdrop.attrs,
172
- "id": dom.getBackdropId(state.context),
173
- "dir": state.context.dir,
174
- "hidden": !open,
175
- "data-state": open ? "open" : "closed",
176
- "style": {
177
- clipPath: isTooltipStep(step) ? `path("${clipPath}")` : void 0,
178
- position: "absolute",
179
- inset: "0",
180
- willChange: "clip-path"
181
- }
182
- });
183
- },
184
- getSpotlightProps() {
185
- return normalize.element({
186
- ...parts.spotlight.attrs,
187
- hidden: !open || !step?.target?.(),
188
- style: {
189
- position: "absolute",
190
- width: `${targetRect.width}px`,
191
- height: `${targetRect.height}px`,
192
- left: `${targetRect.x}px`,
193
- top: `${targetRect.y}px`,
194
- borderRadius: `${state.context.spotlightRadius}px`,
195
- pointerEvents: "none"
196
- }
197
- });
198
- },
199
- getProgressTextProps() {
200
- return normalize.element({
201
- ...parts.progressText.attrs
202
- });
203
- },
204
- getPositionerProps() {
205
- return normalize.element({
206
- ...parts.positioner.attrs,
207
- "dir": state.context.dir,
208
- "id": dom.getPositionerId(state.context),
209
- "data-type": step?.type,
210
- "data-placement": state.context.currentPlacement,
211
- "style": step?.type === "tooltip" ? popperStyles.floating : { zIndex: MAX_Z_INDEX }
212
- });
213
- },
214
- getArrowProps() {
215
- return normalize.element({
216
- id: dom.getArrowId(state.context),
217
- ...parts.arrow.attrs,
218
- dir: state.context.dir,
219
- hidden: step?.type !== "tooltip",
220
- style: step?.type === "tooltip" ? popperStyles.arrow : void 0,
221
- opacity: hasTarget ? void 0 : 0
222
- });
223
- },
224
- getArrowTipProps() {
225
- return normalize.element({
226
- ...parts.arrowTip.attrs,
227
- dir: state.context.dir,
228
- style: popperStyles.arrowTip
229
- });
230
- },
231
- getContentProps() {
232
- return normalize.element({
233
- ...parts.content.attrs,
234
- "id": dom.getContentId(state.context),
235
- "dir": state.context.dir,
236
- "role": "alertdialog",
237
- "aria-modal": "true",
238
- "aria-live": "polite",
239
- "aria-atomic": "true",
240
- "hidden": !open,
241
- "data-state": open ? "open" : "closed",
242
- "data-type": step?.type,
243
- "data-placement": state.context.currentPlacement,
244
- "data-step": step?.id,
245
- "aria-labelledby": dom.getTitleId(state.context),
246
- "aria-describedby": dom.getDescriptionId(state.context),
247
- "tabIndex": -1,
248
- "style": { zIndex: MAX_Z_INDEX },
249
- onKeyDown(event) {
250
- if (event.defaultPrevented)
251
- return;
252
- if (!state.context.keyboardNavigation)
253
- return;
254
- const isRtl = state.context.dir === "rtl";
255
- switch (event.key) {
256
- case "ArrowRight":
257
- if (!hasNextStep)
258
- return;
259
- send({ type: isRtl ? "STEP.PREV" : "STEP.NEXT", src: "keydown" });
260
- break;
261
- case "ArrowLeft":
262
- if (!hasPrevStep)
263
- return;
264
- send({ type: isRtl ? "STEP.NEXT" : "STEP.PREV", src: "keydown" });
265
- break;
266
- }
267
- }
268
- });
269
- },
270
- getTitleProps() {
271
- return normalize.element({
272
- ...parts.title.attrs,
273
- "id": dom.getTitleId(state.context),
274
- "data-placement": hasTarget ? state.context.currentPlacement : "center"
275
- });
276
- },
277
- getDescriptionProps() {
278
- return normalize.element({
279
- ...parts.description.attrs,
280
- "id": dom.getDescriptionId(state.context),
281
- "data-placement": hasTarget ? state.context.currentPlacement : "center"
282
- });
283
- },
284
- getCloseTriggerProps() {
285
- return normalize.element({
286
- ...parts.closeTrigger.attrs,
287
- "data-type": step?.type,
288
- "aria-label": state.context.translations.close,
289
- "onClick": actionMap.dismiss
290
- });
291
- },
292
- getActionTriggerProps(props) {
293
- const { action, attrs } = props.action;
294
- let actionProps = {};
295
- switch (action) {
296
- case "next":
297
- actionProps = {
298
- "data-type": "next",
299
- "disabled": !hasNextStep,
300
- "data-disabled": dataAttr(!hasNextStep),
301
- "aria-label": state.context.translations.nextStep,
302
- "onClick": actionMap.next
303
- };
304
- break;
305
- case "prev":
306
- actionProps = {
307
- "data-type": "prev",
308
- "disabled": !hasPrevStep,
309
- "data-disabled": dataAttr(!hasPrevStep),
310
- "aria-label": state.context.translations.prevStep,
311
- "onClick": actionMap.prev
312
- };
313
- break;
314
- case "dismiss":
315
- actionProps = {
316
- "data-type": "close",
317
- "aria-label": state.context.translations.close,
318
- "onClick": actionMap.dismiss
319
- };
320
- break;
321
- default:
322
- actionProps = {
323
- "data-type": "custom",
324
- onClick() {
325
- if (typeof action === "function") {
326
- action(actionMap);
327
- }
328
- }
329
- };
330
- break;
331
- }
332
- return normalize.button({
333
- ...parts.actionTrigger.attrs,
334
- type: "button",
335
- ...attrs,
336
- ...actionProps
337
- });
338
- }
339
- };
340
- }
341
-
342
- function getFrameElement(win) {
343
- return win.parent && Object.getPrototypeOf(win.parent) ? win.frameElement : null;
344
- }
345
- function normalizeEventPoint(event) {
346
- let clientX = event.clientX;
347
- let clientY = event.clientY;
348
- let win = event.view || window;
349
- let frame = getFrameElement(win);
350
- while (frame) {
351
- const iframeRect = frame.getBoundingClientRect();
352
- const css = getComputedStyle(frame);
353
- const left = iframeRect.left + (frame.clientLeft + Number.parseFloat(css.paddingLeft));
354
- const top = iframeRect.top + (frame.clientTop + Number.parseFloat(css.paddingTop));
355
- clientX += left;
356
- clientY += top;
357
- win = getWindow(frame);
358
- frame = getFrameElement(win);
359
- }
360
- return { clientX, clientY };
361
- }
362
- function isEventInRect(rect, event) {
363
- const { clientX, clientY } = normalizeEventPoint(event);
364
- return rect.y <= clientY && clientY <= rect.y + rect.height && rect.x <= clientX && clientX <= rect.x + rect.width;
365
- }
366
- function offset(r, i) {
367
- const dx = i.x || 0;
368
- const dy = i.y || 0;
369
- return {
370
- x: r.x - dx,
371
- y: r.y - dy,
372
- width: r.width + dx + dx,
373
- height: r.height + dy + dy
374
- };
375
- }
376
-
377
- const { and } = guards;
378
- const invoke = {
379
- stepChange(ctx) {
380
- const effectiveLength = ctx.steps.filter((step) => step.type !== "wait").length;
381
- const progress = (ctx.stepIndex + 1) / effectiveLength;
382
- ctx.onStepChange?.({
383
- complete: ctx.isLastStep,
384
- stepId: ctx.stepId,
385
- totalSteps: ctx.steps.length,
386
- stepIndex: ctx.stepIndex,
387
- progress
388
- });
389
- ctx._effectCleanup?.();
390
- ctx._effectCleanup = void 0;
391
- },
392
- statusChange(ctx, status) {
393
- ctx.onStatusChange?.({
394
- status,
395
- stepId: ctx.stepId,
396
- stepIndex: ctx.stepIndex
397
- });
398
- }
399
- };
400
- const set = {
401
- step(ctx, idx) {
402
- const step = ctx.steps[idx];
403
- if (!step) {
404
- ctx.stepId = null;
405
- invoke.stepChange(ctx);
406
- return;
407
- }
408
- if (isEqual(ctx.stepId, step.id))
409
- return;
410
- const update = (data) => {
411
- ctx.steps[idx] = { ...step, ...data };
412
- };
413
- const next = () => {
414
- const idx2 = nextIndex(ctx.steps, ctx.stepIndex);
415
- ctx.stepId = ctx.steps[idx2].id;
416
- invoke.stepChange(ctx);
417
- };
418
- const goto = (id) => {
419
- const idx2 = ctx.steps.findIndex((s) => s.id === id);
420
- ctx.stepId = ctx.steps[idx2].id;
421
- invoke.stepChange(ctx);
422
- };
423
- const dismiss = () => {
424
- ctx.stepId = null;
425
- invoke.stepChange(ctx);
426
- invoke.statusChange(ctx, "dismissed");
427
- };
428
- const show = () => {
429
- ctx.stepId = step.id;
430
- invoke.stepChange(ctx);
431
- };
432
- if (!step.effect) {
433
- show();
434
- return;
435
- }
436
- ctx._effectCleanup = step.effect({
437
- show,
438
- next,
439
- update,
440
- target: step.target,
441
- dismiss,
442
- goto
443
- });
444
- }
445
- };
446
- function machine(userContext) {
447
- const ctx = compact(userContext);
448
- return createMachine(
449
- {
450
- id: "tour",
451
- initial: "tour.inactive",
452
- context: {
453
- stepId: null,
454
- steps: [],
455
- preventInteraction: false,
456
- closeOnInteractOutside: true,
457
- closeOnEscape: true,
458
- keyboardNavigation: true,
459
- spotlightOffset: { x: 10, y: 10 },
460
- spotlightRadius: 4,
461
- translations: {
462
- nextStep: "next step",
463
- prevStep: "previous step",
464
- close: "close tour",
465
- progressText: ({ current, total }) => `${current + 1} of ${total}`,
466
- skip: "skip tour",
467
- ...ctx.translations
468
- },
469
- ...ctx,
470
- resolvedTarget: ref({ value: null }),
471
- targetRect: ref({ width: 0, height: 0, x: 0, y: 0 }),
472
- boundarySize: ref({ width: 0, height: 0 })
473
- },
474
- computed: {
475
- stepIndex: (ctx2) => findStepIndex(ctx2.steps, ctx2.stepId),
476
- step: (ctx2) => findStep(ctx2.steps, ctx2.stepId),
477
- hasNextStep: (ctx2) => ctx2.stepIndex < ctx2.steps.length - 1,
478
- hasPrevStep: (ctx2) => ctx2.stepIndex > 0,
479
- isFirstStep: (ctx2) => ctx2.stepIndex === 0,
480
- isLastStep: (ctx2) => ctx2.stepIndex === ctx2.steps.length - 1
481
- },
482
- created: ["validateSteps"],
483
- watch: {
484
- stepId: ["setResolvedTarget", "raiseStepChange", "syncTargetAttrs"]
485
- },
486
- activities: ["trackBoundarySize"],
487
- exit: ["clearStep", "cleanupFns"],
488
- on: {
489
- "STEPS.SET": {
490
- actions: ["setSteps"]
491
- },
492
- "STEP.SET": {
493
- actions: ["setStep"]
494
- },
495
- "STEP.NEXT": {
496
- actions: ["setNextStep"]
497
- },
498
- "STEP.PREV": {
499
- actions: ["setPrevStep"]
500
- },
501
- "STEP.CHANGED": [
502
- {
503
- guard: and("isValidStep", "hasResolvedTarget"),
504
- target: "target.scrolling"
505
- },
506
- {
507
- guard: and("isValidStep", "hasTarget"),
508
- target: "target.resolving"
509
- },
510
- {
511
- guard: and("isValidStep", "isWaitingStep"),
512
- target: "step.waiting"
513
- },
514
- {
515
- guard: "isValidStep",
516
- target: "tour.active"
517
- }
518
- ],
519
- "DISMISS": [
520
- {
521
- guard: "isLastStep",
522
- target: "tour.inactive",
523
- actions: ["invokeOnDismiss", "invokeOnComplete", "clearStep"]
524
- },
525
- {
526
- target: "tour.inactive",
527
- actions: ["invokeOnDismiss", "clearStep"]
528
- }
529
- ]
530
- },
531
- states: {
532
- "tour.inactive": {
533
- tags: ["closed"],
534
- on: {
535
- START: {
536
- actions: ["setInitialStep", "invokeOnStart"]
537
- }
538
- }
539
- },
540
- "target.resolving": {
541
- tags: ["closed"],
542
- activities: ["waitForTarget"],
543
- after: {
544
- MISSING_TARGET_TIMEOUT: {
545
- target: "tour.inactive",
546
- actions: ["invokeOnNotFound", "clearStep"]
547
- }
548
- },
549
- on: {
550
- "TARGET.RESOLVED": {
551
- target: "target.scrolling",
552
- actions: ["setResolvedTarget"]
553
- }
554
- }
555
- },
556
- "target.scrolling": {
557
- tags: ["open"],
558
- entry: ["scrollToTarget"],
559
- activities: [
560
- "trapFocus",
561
- "trackPlacement",
562
- "trackDismissableBranch",
563
- "trackInteractOutside",
564
- "trackEscapeKeydown"
565
- ],
566
- after: {
567
- 100: "tour.active"
568
- }
569
- },
570
- "step.waiting": {
571
- tags: ["closed"]
572
- },
573
- "tour.active": {
574
- tags: ["open"],
575
- activities: [
576
- "trapFocus",
577
- "trackPlacement",
578
- "trackDismissableBranch",
579
- "trackInteractOutside",
580
- "trackEscapeKeydown"
581
- ]
582
- }
583
- }
584
- },
585
- {
586
- delays: {
587
- MISSING_TARGET_TIMEOUT: 3e3
588
- },
589
- guards: {
590
- isLastStep: (ctx2) => ctx2.isLastStep,
591
- isValidStep: (ctx2) => ctx2.stepId != null,
592
- hasTarget: (ctx2) => ctx2.step?.target != null,
593
- hasResolvedTarget: (ctx2) => ctx2.resolvedTarget.value != null,
594
- isWaitingStep: (ctx2) => ctx2.step?.type === "wait"
595
- },
596
- actions: {
597
- scrollToTarget(ctx2, _evt) {
598
- const node = ctx2.resolvedTarget.value;
599
- node?.scrollIntoView({ behavior: "instant", block: "center", inline: "center" });
600
- },
601
- setStep(ctx2, evt) {
602
- set.step(ctx2, evt.value);
603
- },
604
- clearStep(ctx2) {
605
- ctx2.targetRect = ref({ width: 0, height: 0, x: 0, y: 0 });
606
- set.step(ctx2, -1);
607
- },
608
- setInitialStep(ctx2, evt) {
609
- if (ctx2.steps.length === 0)
610
- return;
611
- if (isString(evt.id)) {
612
- const idx = findStepIndex(ctx2.steps, evt.id);
613
- set.step(ctx2, idx);
614
- return;
615
- }
616
- set.step(ctx2, 0);
617
- },
618
- setNextStep(ctx2) {
619
- const idx = nextIndex(ctx2.steps, ctx2.stepIndex);
620
- set.step(ctx2, idx);
621
- },
622
- setPrevStep(ctx2) {
623
- const idx = prevIndex(ctx2.steps, ctx2.stepIndex);
624
- set.step(ctx2, idx);
625
- },
626
- invokeOnStart(ctx2) {
627
- invoke.statusChange(ctx2, "started");
628
- },
629
- invokeOnDismiss(ctx2) {
630
- invoke.statusChange(ctx2, "dismissed");
631
- },
632
- invokeOnComplete(ctx2) {
633
- invoke.statusChange(ctx2, "completed");
634
- },
635
- invokeOnSkip(ctx2) {
636
- invoke.statusChange(ctx2, "skipped");
637
- },
638
- invokeOnNotFound(ctx2) {
639
- invoke.statusChange(ctx2, "not-found");
640
- },
641
- raiseStepChange(_ctx, _evt, { send }) {
642
- send({ type: "STEP.CHANGED" });
643
- },
644
- setResolvedTarget(ctx2, evt) {
645
- const node = evt.node ?? ctx2.step?.target?.();
646
- ctx2.resolvedTarget.value = node ?? null;
647
- },
648
- syncTargetAttrs(ctx2) {
649
- ctx2._targetCleanup?.();
650
- ctx2._targetCleanup = void 0;
651
- const targetEl = ctx2.resolvedTarget.value;
652
- if (!targetEl)
653
- return;
654
- if (ctx2.preventInteraction)
655
- targetEl.inert = true;
656
- targetEl.setAttribute("data-tour-highlighted", "");
657
- ctx2._targetCleanup = () => {
658
- if (ctx2.preventInteraction)
659
- targetEl.inert = false;
660
- targetEl.removeAttribute("data-tour-highlighted");
661
- };
662
- },
663
- cleanupFns(ctx2) {
664
- ctx2._targetCleanup?.();
665
- ctx2._targetCleanup = void 0;
666
- ctx2._effectCleanup?.();
667
- ctx2._effectCleanup = void 0;
668
- },
669
- validateSteps(ctx2) {
670
- const ids = /* @__PURE__ */ new Set();
671
- ctx2.steps.forEach((step) => {
672
- if (ids.has(step.id)) {
673
- throw new Error(`[destyler/tour] Duplicate step id: ${step.id}`);
674
- }
675
- if (step.target == null && step.type == null) {
676
- throw new Error(`[destyler/tour] Step ${step.id} has no target or type. At least one of those is required.`);
677
- }
678
- ids.add(step.id);
679
- });
680
- }
681
- },
682
- activities: {
683
- waitForTarget(ctx2, _evt, { send }) {
684
- const targetEl = ctx2.step?.target;
685
- const win = dom.getWin(ctx2);
686
- const rootNode = dom.getRootNode(ctx2);
687
- const observer = new win.MutationObserver(() => {
688
- const node = targetEl?.();
689
- if (node) {
690
- send({ type: "TARGET.RESOLVED", node });
691
- observer.disconnect();
692
- }
693
- });
694
- observer.observe(rootNode, {
695
- childList: true,
696
- subtree: true,
697
- characterData: true
698
- });
699
- return () => {
700
- observer.disconnect();
701
- };
702
- },
703
- trackBoundarySize(ctx2) {
704
- const win = dom.getWin(ctx2);
705
- const doc = dom.getDoc(ctx2);
706
- const onResize = () => {
707
- const width = visualViewport?.width ?? win.innerWidth;
708
- const height = doc.documentElement.scrollHeight;
709
- ctx2.boundarySize = { width, height };
710
- };
711
- onResize();
712
- const viewport = win.visualViewport ?? win;
713
- viewport.addEventListener("resize", onResize);
714
- return () => viewport.removeEventListener("resize", onResize);
715
- },
716
- trackEscapeKeydown(ctx2, _evt, { send }) {
717
- if (!ctx2.closeOnEscape)
718
- return;
719
- const doc = dom.getDoc(ctx2);
720
- const onKeyDown = (event) => {
721
- if (event.key === "Escape") {
722
- event.preventDefault();
723
- event.stopPropagation();
724
- send({ type: "DISMISS", src: "esc" });
725
- }
726
- };
727
- doc.addEventListener("keydown", onKeyDown, true);
728
- return () => {
729
- doc.removeEventListener("keydown", onKeyDown, true);
730
- };
731
- },
732
- trackInteractOutside(ctx2, _evt, { send }) {
733
- if (ctx2.step == null)
734
- return;
735
- const contentEl = () => dom.getContentEl(ctx2);
736
- return trackInteractOutside(contentEl, {
737
- defer: true,
738
- exclude(target) {
739
- return contains(ctx2.step?.target?.(), target);
740
- },
741
- onFocusOutside(event) {
742
- ctx2.onFocusOutside?.(event);
743
- if (!ctx2.closeOnInteractOutside) {
744
- event.preventDefault();
745
- }
746
- },
747
- onPointerDownOutside(event) {
748
- ctx2.onPointerDownOutside?.(event);
749
- const isWithin = isEventInRect(ctx2.targetRect, event.detail.originalEvent);
750
- if (isWithin) {
751
- event.preventDefault();
752
- return;
753
- }
754
- if (!ctx2.closeOnInteractOutside) {
755
- event.preventDefault();
756
- }
757
- },
758
- onInteractOutside(event) {
759
- ctx2.onInteractOutside?.(event);
760
- if (event.defaultPrevented)
761
- return;
762
- send({ type: "DISMISS", src: "interact-outside" });
763
- }
764
- });
765
- },
766
- trackDismissableBranch(ctx2) {
767
- if (ctx2.step == null)
768
- return;
769
- const contentEl = () => dom.getContentEl(ctx2);
770
- return trackDismissableBranch(contentEl, { defer: !contentEl() });
771
- },
772
- trapFocus(ctx2) {
773
- const contentEl = () => dom.getContentEl(ctx2);
774
- return trapFocus(contentEl, {
775
- escapeDeactivates: false,
776
- allowOutsideClick: true,
777
- preventScroll: true,
778
- returnFocusOnDeactivate: false
779
- });
780
- },
781
- trackPlacement(ctx2) {
782
- if (ctx2.step == null)
783
- return;
784
- ctx2.currentPlacement = ctx2.step.placement ?? "bottom";
785
- if (!isTooltipStep(ctx2.step))
786
- return;
787
- const positionerEl = () => dom.getPositionerEl(ctx2);
788
- return getPlacement(ctx2.resolvedTarget.value, positionerEl, {
789
- defer: true,
790
- placement: ctx2.step.placement ?? "bottom",
791
- strategy: "absolute",
792
- gutter: 10,
793
- offset: ctx2.step.offset,
794
- getAnchorRect(el) {
795
- if (!isHTMLElement(el))
796
- return null;
797
- const rect = el.getBoundingClientRect();
798
- return offset(rect, ctx2.spotlightOffset);
799
- },
800
- onComplete(data) {
801
- const { rects } = data.middlewareData;
802
- ctx2.currentPlacement = data.placement;
803
- ctx2.targetRect = rects.reference;
804
- }
805
- });
806
- }
807
- }
808
- }
809
- );
810
- }
811
-
812
- const props = createProps()([
813
- "closeOnEscape",
814
- "closeOnInteractOutside",
815
- "dir",
816
- "getRootNode",
817
- "id",
818
- "ids",
819
- "keyboardNavigation",
820
- "onFocusOutside",
821
- "onInteractOutside",
822
- "onPointerDownOutside",
823
- "onStatusChange",
824
- "onStepChange",
825
- "preventInteraction",
826
- "spotlightOffset",
827
- "spotlightRadius",
828
- "stepId",
829
- "steps",
830
- "translations"
831
- ]);
832
- const splitProps = createSplitProps(props);
833
-
834
- function waitForPromise(promise, controller, timeout) {
835
- const { signal } = controller;
836
- const wrappedPromise = new Promise((resolve, reject) => {
837
- const timeoutId = setTimeout(() => {
838
- reject(new Error(`Timeout of ${timeout}ms exceeded`));
839
- }, timeout);
840
- signal.addEventListener("abort", () => {
841
- clearTimeout(timeoutId);
842
- reject(new Error("Promise aborted"));
843
- });
844
- promise.then((result) => {
845
- if (!signal.aborted) {
846
- clearTimeout(timeoutId);
847
- resolve(result);
848
- }
849
- }).catch((error) => {
850
- if (!signal.aborted) {
851
- clearTimeout(timeoutId);
852
- reject(error);
853
- }
854
- });
855
- });
856
- const abort = () => controller.abort();
857
- return [wrappedPromise, abort];
858
- }
859
- function waitForElement(target, options) {
860
- const { timeout, rootNode } = options;
861
- const win = getWindow(rootNode);
862
- const doc = getDocument(rootNode);
863
- const controller = new win.AbortController();
864
- return waitForPromise(
865
- new Promise((resolve) => {
866
- const el = target();
867
- if (el) {
868
- resolve(el);
869
- return;
870
- }
871
- const observer = new win.MutationObserver(() => {
872
- const el2 = target();
873
- if (el2) {
874
- observer.disconnect();
875
- resolve(el2);
876
- }
877
- });
878
- observer.observe(doc.body, {
879
- childList: true,
880
- subtree: true
881
- });
882
- }),
883
- controller,
884
- timeout
885
- );
886
- }
887
- function waitForElementValue(target, value, options) {
888
- const { timeout, rootNode } = options;
889
- const win = getWindow(rootNode);
890
- const controller = new win.AbortController();
891
- return waitForPromise(
892
- new Promise((resolve) => {
893
- const el = target();
894
- if (!el)
895
- return;
896
- const checkValue = () => {
897
- if (el.value === value) {
898
- resolve();
899
- el.removeEventListener("input", checkValue);
900
- }
901
- };
902
- checkValue();
903
- el.addEventListener("input", checkValue, { signal: controller.signal });
904
- }),
905
- controller,
906
- timeout
907
- );
908
- }
909
-
910
- export { anatomy, connect, machine, props, splitProps, waitForElement, waitForElementValue };