@glowhop/core-tour 1.3.1 → 1.5.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/config/index.js CHANGED
@@ -12,17 +12,6 @@ function cloneStepProps(props) {
12
12
  ...props.popover,
13
13
  animation: props.popover.animation && { ...props.popover.animation },
14
14
  arrow: props.popover.arrow && { ...props.popover.arrow },
15
- keyboardShortcuts: props.popover.keyboardShortcuts && {
16
- previous: props.popover.keyboardShortcuts.previous && [
17
- ...props.popover.keyboardShortcuts.previous
18
- ],
19
- advance: props.popover.keyboardShortcuts.advance && [
20
- ...props.popover.keyboardShortcuts.advance
21
- ],
22
- cancel: props.popover.keyboardShortcuts.cancel && [
23
- ...props.popover.keyboardShortcuts.cancel
24
- ]
25
- },
26
15
  placementTryOrder: props.popover.placementTryOrder && [...props.popover.placementTryOrder]
27
16
  },
28
17
  indicator: props.indicator && {
@@ -31,9 +20,29 @@ function cloneStepProps(props) {
31
20
  placementTryOrder: props.indicator.placementTryOrder && [
32
21
  ...props.indicator.placementTryOrder
33
22
  ]
34
- }
23
+ },
24
+ behavior: props.behavior && {
25
+ ...props.behavior,
26
+ missingTarget: props.behavior.missingTarget && { ...props.behavior.missingTarget },
27
+ scroll: props.behavior.scroll && { ...props.behavior.scroll }
28
+ },
29
+ controls: cloneControls(props.controls),
30
+ classNames: cloneClassNames(props.classNames)
35
31
  };
36
32
  }
33
+ function cloneControls(controls) {
34
+ return controls && Object.freeze({
35
+ previous: cloneControl(controls.previous),
36
+ advance: cloneControl(controls.advance),
37
+ cancel: cloneControl(controls.cancel)
38
+ });
39
+ }
40
+ function cloneControl(control) {
41
+ return control && Object.freeze({ state: control.state, keys: control.keys && Object.freeze([...control.keys]) });
42
+ }
43
+ function cloneClassNames(classNames) {
44
+ return classNames && Object.freeze({ ...classNames });
45
+ }
37
46
  function freezeStepProps(props) {
38
47
  const cloned = cloneStepProps(props);
39
48
  if (cloned.data)
@@ -46,14 +55,6 @@ function freezeStepProps(props) {
46
55
  Object.freeze(cloned.popover.animation);
47
56
  if (cloned.popover?.arrow)
48
57
  Object.freeze(cloned.popover.arrow);
49
- if (cloned.popover?.keyboardShortcuts?.previous)
50
- Object.freeze(cloned.popover.keyboardShortcuts.previous);
51
- if (cloned.popover?.keyboardShortcuts?.advance)
52
- Object.freeze(cloned.popover.keyboardShortcuts.advance);
53
- if (cloned.popover?.keyboardShortcuts?.cancel)
54
- Object.freeze(cloned.popover.keyboardShortcuts.cancel);
55
- if (cloned.popover?.keyboardShortcuts)
56
- Object.freeze(cloned.popover.keyboardShortcuts);
57
58
  if (cloned.popover?.placementTryOrder)
58
59
  Object.freeze(cloned.popover.placementTryOrder);
59
60
  if (cloned.popover)
@@ -64,6 +65,12 @@ function freezeStepProps(props) {
64
65
  Object.freeze(cloned.indicator.placementTryOrder);
65
66
  if (cloned.indicator)
66
67
  Object.freeze(cloned.indicator);
68
+ if (cloned.behavior?.missingTarget)
69
+ Object.freeze(cloned.behavior.missingTarget);
70
+ if (cloned.behavior?.scroll)
71
+ Object.freeze(cloned.behavior.scroll);
72
+ if (cloned.behavior)
73
+ Object.freeze(cloned.behavior);
67
74
  return Object.freeze(cloned);
68
75
  }
69
76
  // packages/core/src/definition/workflow-definition.ts
@@ -84,11 +91,6 @@ function freezePopover(options) {
84
91
  ...options,
85
92
  animation: freezeAnimation(options.animation),
86
93
  arrow: options.arrow && freezeRecord({ ...options.arrow }),
87
- keyboardShortcuts: options.keyboardShortcuts && freezeRecord({
88
- previous: options.keyboardShortcuts.previous && freezeRecord([...options.keyboardShortcuts.previous]),
89
- advance: options.keyboardShortcuts.advance && freezeRecord([...options.keyboardShortcuts.advance]),
90
- cancel: options.keyboardShortcuts.cancel && freezeRecord([...options.keyboardShortcuts.cancel])
91
- }),
92
94
  placementTryOrder: options.placementTryOrder && freezeRecord([...options.placementTryOrder])
93
95
  });
94
96
  }
@@ -103,17 +105,11 @@ function freezeStep(draft) {
103
105
  return freezeRecord({
104
106
  id: draft.id,
105
107
  target: draft.target,
106
- resetPropsOnEnter: draft.resetPropsOnEnter,
107
108
  props: freezeStepProps(draft.props),
108
- behavior: draft.behavior && freezeRecord({
109
- ...draft.behavior,
110
- scroll: draft.behavior.scroll && freezeRecord({ ...draft.behavior.scroll })
111
- }),
112
109
  actions: freezeRecord(draft.actions.map((action) => action)),
113
- eventHandlers: freezeRecord(draft.eventHandlers.map((handler) => freezeRecord({ ...handler }))),
114
- advanceAction: draft.advanceAction,
115
- previousAction: draft.previousAction,
116
- cancelAction: draft.cancelAction
110
+ targetEvents: freezeRecord(draft.targetEvents.map((handler) => freezeRecord({ ...handler }))),
111
+ beforeEnter: draft.beforeEnter,
112
+ beforeLeave: draft.beforeLeave
117
113
  });
118
114
  }
119
115
  function freezeOptions(options) {
@@ -122,8 +118,11 @@ function freezeOptions(options) {
122
118
  overlay: freezeOverlay(options.overlay),
123
119
  popover: freezePopover(options.popover),
124
120
  indicator: freezeIndicator(options.indicator),
121
+ classNames: cloneClassNames(options.classNames),
122
+ controls: cloneControls(options.controls),
125
123
  behavior: options.behavior && freezeRecord({
126
124
  ...options.behavior,
125
+ missingTarget: options.behavior.missingTarget && freezeRecord({ ...options.behavior.missingTarget }),
127
126
  scroll: options.behavior.scroll && freezeRecord({ ...options.behavior.scroll })
128
127
  })
129
128
  });
@@ -133,7 +132,7 @@ function cloneWorkflowStepDraft(definition) {
133
132
  ...definition,
134
133
  props: cloneStepProps(definition.props),
135
134
  actions: definition.actions.map((action) => action),
136
- eventHandlers: [...definition.eventHandlers]
135
+ targetEvents: [...definition.targetEvents]
137
136
  };
138
137
  }
139
138
  function assertUniqueStepIds(name, drafts) {
@@ -246,21 +245,21 @@ class WorkflowBuilder {
246
245
  this.currentStep = new WorkflowStepBuilder(this, {
247
246
  id: options.id,
248
247
  target: options.target,
249
- resetPropsOnEnter: options.resetPropsOnEnter,
250
248
  props: {
251
249
  title: options.title,
252
250
  content: options.content,
253
251
  data: cloneStepProps(options).data,
254
252
  overlay: options.overlay,
255
253
  popover: options.popover,
256
- indicator: options.indicator
254
+ indicator: options.indicator,
255
+ behavior: options.behavior,
256
+ controls: options.controls,
257
+ classNames: options.classNames
257
258
  },
258
- behavior: options.behavior,
259
259
  actions: [],
260
- eventHandlers: [],
261
- advanceAction: null,
262
- previousAction: null,
263
- cancelAction: null
260
+ targetEvents: [],
261
+ beforeEnter: null,
262
+ beforeLeave: null
264
263
  });
265
264
  return this.currentStep;
266
265
  }
@@ -364,19 +363,14 @@ class WorkflowStepBuilder {
364
363
  this.draft.actions.push(callback);
365
364
  return this;
366
365
  }
367
- beforeAdvance(callback) {
366
+ beforeEnter(callback) {
368
367
  this.assertActive();
369
- this.draft.advanceAction = callback;
368
+ this.draft.beforeEnter = callback;
370
369
  return this;
371
370
  }
372
- beforePrevious(callback) {
371
+ beforeLeave(callback) {
373
372
  this.assertActive();
374
- this.draft.previousAction = callback;
375
- return this;
376
- }
377
- beforeCancel(callback) {
378
- this.assertActive();
379
- this.draft.cancelAction = callback;
373
+ this.draft.beforeLeave = callback;
380
374
  return this;
381
375
  }
382
376
  onTargetEvent(eventOrEvents, callback) {
@@ -387,7 +381,7 @@ class WorkflowStepBuilder {
387
381
  for (const event of events) {
388
382
  if (event.length === 0)
389
383
  throw new TypeError("event name must not be empty");
390
- this.draft.eventHandlers.push({
384
+ this.draft.targetEvents.push({
391
385
  event,
392
386
  callback
393
387
  });
@@ -419,14 +413,15 @@ ${summary}`);
419
413
 
420
414
  // packages/core/src/config/validate.ts
421
415
  var TOP_LEVEL_KEYS = [
416
+ "version",
422
417
  "name",
423
- "cancellable",
424
- "allowScroll",
425
418
  "overlay",
426
419
  "popover",
427
420
  "indicator",
428
421
  "animated",
429
422
  "behavior",
423
+ "controls",
424
+ "classNames",
430
425
  "onStart",
431
426
  "onCancel",
432
427
  "onFinish",
@@ -435,58 +430,58 @@ var TOP_LEVEL_KEYS = [
435
430
  var STEP_KEYS = [
436
431
  "id",
437
432
  "target",
438
- "resetPropsOnEnter",
439
433
  "overlay",
440
434
  "popover",
441
435
  "indicator",
442
436
  "behavior",
437
+ "controls",
438
+ "classNames",
443
439
  "title",
444
440
  "content",
445
441
  "data",
446
442
  "actions",
447
- "eventHandlers",
448
- "advanceAction",
449
- "previousAction",
450
- "cancelAction"
443
+ "targetEvents",
444
+ "beforeEnter",
445
+ "beforeLeave"
451
446
  ];
452
447
  var EVENT_HANDLER_KEYS = ["event", "action"];
453
448
  var ANIMATION_KEYS = ["duration", "easing"];
454
449
  var OVERLAY_KEYS = ["animated", "animation", "color", "opacity", "padding", "radius"];
455
- var INDICATOR_KEYS = ["animated", "animation", "disabled", "gap", "placementTryOrder"];
456
- var POPOVER_KEYS = [
457
- "animated",
458
- "animation",
459
- "placementTryOrder",
460
- "arrow",
461
- "hideFooter",
462
- "disablePreviousButton",
463
- "hidePreviousButton",
464
- "disableAdvanceButton",
465
- "hideAdvanceButton",
466
- "gap",
467
- "keyboardShortcuts"
450
+ var INDICATOR_KEYS = ["animated", "animation", "hidden", "gap", "placementTryOrder"];
451
+ var POPOVER_KEYS = ["animated", "animation", "placementTryOrder", "arrow", "gap"];
452
+ var CONTROL_KEYS = ["advance", "previous", "cancel"];
453
+ var CONTROL_FIELD_KEYS = ["state", "keys"];
454
+ var CLASS_NAME_KEYS = [
455
+ "overlay",
456
+ "popover",
457
+ "pointer",
458
+ "header",
459
+ "content",
460
+ "footer",
461
+ ...CONTROL_KEYS
468
462
  ];
469
463
  var POPOVER_ARROW_KEYS = [
470
- "disabled",
464
+ "hidden",
471
465
  "color",
472
466
  "size",
473
467
  "borderWidth",
474
468
  "borderRadius",
475
469
  "edgePadding",
476
470
  "styleNonce",
477
- "disableAutoStyles"
471
+ "autoStyles"
478
472
  ];
479
- var KEYBOARD_SHORTCUT_KEYS = ["previous", "advance", "cancel"];
473
+ var MISSING_TARGET_KEYS = ["strategy", "timeout"];
480
474
  var BEHAVIOR_KEYS = [
481
475
  "allowInteraction",
482
- "disableAutoFocus",
483
- "disableAutoScroll",
484
- "missingTargetStrategy",
476
+ "allowScroll",
477
+ "autoFocus",
478
+ "autoScroll",
479
+ "missingTarget",
485
480
  "scroll",
486
- "targetTimeout",
487
481
  "overlayClick"
488
482
  ];
489
- var SCROLL_KEYS = ["behavior", "block", "inline"];
483
+ var SCROLL_KEYS = ["behavior", "block", "inline", "returnDelay"];
484
+ var CONFIG_VERSION = "1.1";
490
485
  var BUILTIN_ACTION_KEYS = {
491
486
  wait: ["type", "ms"],
492
487
  waitUntilElement: ["type", "selector", "interval", "timeout"],
@@ -513,16 +508,19 @@ function validateWorkflowConfigShape(value, issues, validateContent) {
513
508
  return;
514
509
  }
515
510
  assertNoUnknownKeys(value, TOP_LEVEL_KEYS, "", issues);
511
+ if (value.version !== CONFIG_VERSION) {
512
+ issues.push({ path: "version", message: `version must be "${CONFIG_VERSION}"` });
513
+ }
516
514
  if (typeof value.name !== "string" || value.name.length === 0) {
517
515
  issues.push({ path: "name", message: "name must be a non-empty string" });
518
516
  }
519
- validateOptionalBoolean("cancellable", value.cancellable, issues);
520
- validateOptionalBoolean("allowScroll", value.allowScroll, issues);
521
517
  validateOptionalBoolean("animated", value.animated, issues);
522
518
  validateOverlayShape("overlay", value.overlay, issues);
523
519
  validatePopoverShape("popover", value.popover, issues);
524
520
  validateIndicatorShape("indicator", value.indicator, issues);
525
521
  validateBehaviorShape("behavior", value.behavior, issues);
522
+ validateControlsShape("controls", value.controls, issues);
523
+ validateClassNamesShape("classNames", value.classNames, issues);
526
524
  validateLifecycleActionRefShape("onStart", value.onStart, issues);
527
525
  validateLifecycleActionRefShape("onCancel", value.onCancel, issues);
528
526
  validateLifecycleActionRefShape("onFinish", value.onFinish, issues);
@@ -564,17 +562,18 @@ function validateStepConfigShape(value, path, issues, validateContent) {
564
562
  message: "target must be a non-empty CSS selector string"
565
563
  });
566
564
  }
567
- const titleError = validateContent(value.title, `${path}.title`);
565
+ const titleError = value.title === undefined ? null : validateContent(value.title, `${path}.title`);
568
566
  if (titleError)
569
567
  issues.push({ path: `${path}.title`, message: titleError });
570
568
  const contentError = validateContent(value.content, `${path}.content`);
571
569
  if (contentError)
572
570
  issues.push({ path: `${path}.content`, message: contentError });
573
- validateOptionalBoolean(`${path}.resetPropsOnEnter`, value.resetPropsOnEnter, issues);
574
571
  validateOverlayShape(`${path}.overlay`, value.overlay, issues);
575
572
  validatePopoverShape(`${path}.popover`, value.popover, issues);
576
573
  validateIndicatorShape(`${path}.indicator`, value.indicator, issues);
577
574
  validateBehaviorShape(`${path}.behavior`, value.behavior, issues);
575
+ validateControlsShape(`${path}.controls`, value.controls, issues);
576
+ validateClassNamesShape(`${path}.classNames`, value.classNames, issues);
578
577
  validateDataShape(`${path}.data`, value.data, issues);
579
578
  if (value.actions !== undefined) {
580
579
  if (!Array.isArray(value.actions)) {
@@ -585,22 +584,21 @@ function validateStepConfigShape(value, path, issues, validateContent) {
585
584
  }
586
585
  }
587
586
  }
588
- if (value.eventHandlers !== undefined) {
589
- if (!Array.isArray(value.eventHandlers)) {
590
- issues.push({ path: `${path}.eventHandlers`, message: "eventHandlers must be an array" });
587
+ if (value.targetEvents !== undefined) {
588
+ if (!Array.isArray(value.targetEvents)) {
589
+ issues.push({ path: `${path}.targetEvents`, message: "targetEvents must be an array" });
591
590
  } else {
592
- for (const [index, handler] of value.eventHandlers.entries()) {
593
- validateEventHandlerConfigShape(handler, `${path}.eventHandlers[${index}]`, issues);
591
+ for (const [index, handler] of value.targetEvents.entries()) {
592
+ validateTargetEventConfigShape(handler, `${path}.targetEvents[${index}]`, issues);
594
593
  }
595
594
  }
596
595
  }
597
- validateTransitionActionRefShape(`${path}.advanceAction`, value.advanceAction, issues);
598
- validateTransitionActionRefShape(`${path}.previousAction`, value.previousAction, issues);
599
- validateTransitionActionRefShape(`${path}.cancelAction`, value.cancelAction, issues);
596
+ validateHookActionRefShape(`${path}.beforeEnter`, value.beforeEnter, issues);
597
+ validateHookActionRefShape(`${path}.beforeLeave`, value.beforeLeave, issues);
600
598
  }
601
- function validateEventHandlerConfigShape(value, path, issues) {
599
+ function validateTargetEventConfigShape(value, path, issues) {
602
600
  if (!isPlainObject(value)) {
603
- issues.push({ path, message: "Event handler must be a plain object" });
601
+ issues.push({ path, message: "Target event must be a plain object" });
604
602
  return;
605
603
  }
606
604
  assertNoUnknownKeys(value, EVENT_HANDLER_KEYS, path, issues);
@@ -626,7 +624,7 @@ function validateStepActionRefShape(value, path, issues) {
626
624
  message: 'must be a function, or a built-in action object with a known "type"'
627
625
  });
628
626
  }
629
- function validateTransitionActionRefShape(path, value, issues) {
627
+ function validateHookActionRefShape(path, value, issues) {
630
628
  if (value === undefined)
631
629
  return;
632
630
  if (typeof value === "function")
@@ -637,7 +635,7 @@ function validateTransitionActionRefShape(path, value, issues) {
637
635
  });
638
636
  }
639
637
  function validateLifecycleActionRefShape(path, value, issues) {
640
- validateTransitionActionRefShape(path, value, issues);
638
+ validateHookActionRefShape(path, value, issues);
641
639
  }
642
640
  function validateBuiltinActionShape(value, path, issues) {
643
641
  const type = value.type;
@@ -746,7 +744,7 @@ function validateIndicatorShape(path, value, issues) {
746
744
  }
747
745
  assertNoUnknownKeys(value, INDICATOR_KEYS, path, issues);
748
746
  validateBaseOptionsShape(path, value, issues);
749
- validateOptionalBoolean(`${path}.disabled`, value.disabled, issues);
747
+ validateOptionalBoolean(`${path}.hidden`, value.hidden, issues);
750
748
  validateOptionalFiniteNonNegative(`${path}.gap`, value.gap, issues);
751
749
  validateOptionalStringArray(`${path}.placementTryOrder`, value.placementTryOrder, issues, [
752
750
  "top",
@@ -763,26 +761,14 @@ function validatePopoverArrowShape(path, value, issues) {
763
761
  return;
764
762
  }
765
763
  assertNoUnknownKeys(value, POPOVER_ARROW_KEYS, path, issues);
766
- validateOptionalBoolean(`${path}.disabled`, value.disabled, issues);
764
+ validateOptionalBoolean(`${path}.hidden`, value.hidden, issues);
767
765
  validateOptionalString(`${path}.color`, value.color, issues);
768
766
  validateOptionalFiniteNonNegative(`${path}.size`, value.size, issues);
769
767
  validateOptionalFiniteNonNegative(`${path}.borderWidth`, value.borderWidth, issues);
770
768
  validateOptionalFiniteNonNegative(`${path}.borderRadius`, value.borderRadius, issues);
771
769
  validateOptionalFiniteNonNegative(`${path}.edgePadding`, value.edgePadding, issues);
772
770
  validateOptionalString(`${path}.styleNonce`, value.styleNonce, issues);
773
- validateOptionalBoolean(`${path}.disableAutoStyles`, value.disableAutoStyles, issues);
774
- }
775
- function validateKeyboardShortcutsShape(path, value, issues) {
776
- if (value === undefined)
777
- return;
778
- if (!isPlainObject(value)) {
779
- issues.push({ path, message: "must be an object" });
780
- return;
781
- }
782
- assertNoUnknownKeys(value, KEYBOARD_SHORTCUT_KEYS, path, issues);
783
- for (const key of KEYBOARD_SHORTCUT_KEYS) {
784
- validateOptionalStringArray(`${path}.${key}`, value[key], issues);
785
- }
771
+ validateOptionalBoolean(`${path}.autoStyles`, value.autoStyles, issues);
786
772
  }
787
773
  function validatePopoverShape(path, value, issues) {
788
774
  if (value === undefined)
@@ -800,13 +786,55 @@ function validatePopoverShape(path, value, issues) {
800
786
  "right"
801
787
  ]);
802
788
  validatePopoverArrowShape(`${path}.arrow`, value.arrow, issues);
803
- validateOptionalBoolean(`${path}.hideFooter`, value.hideFooter, issues);
804
- validateOptionalBoolean(`${path}.disablePreviousButton`, value.disablePreviousButton, issues);
805
- validateOptionalBoolean(`${path}.hidePreviousButton`, value.hidePreviousButton, issues);
806
- validateOptionalBoolean(`${path}.disableAdvanceButton`, value.disableAdvanceButton, issues);
807
- validateOptionalBoolean(`${path}.hideAdvanceButton`, value.hideAdvanceButton, issues);
808
789
  validateOptionalFiniteNonNegative(`${path}.gap`, value.gap, issues);
809
- validateKeyboardShortcutsShape(`${path}.keyboardShortcuts`, value.keyboardShortcuts, issues);
790
+ }
791
+ function validateClassNamesShape(path, value, issues) {
792
+ if (value === undefined)
793
+ return;
794
+ if (!isPlainObject(value)) {
795
+ issues.push({ path, message: "must be an object" });
796
+ return;
797
+ }
798
+ assertNoUnknownKeys(value, CLASS_NAME_KEYS, path, issues);
799
+ for (const key of CLASS_NAME_KEYS) {
800
+ if (typeof value[key] !== "string") {
801
+ validateOptionalStringArray(`${path}.${key}`, value[key], issues);
802
+ }
803
+ }
804
+ }
805
+ function validateControlsShape(path, value, issues) {
806
+ if (value === undefined)
807
+ return;
808
+ if (!isPlainObject(value)) {
809
+ issues.push({ path, message: "must be an object" });
810
+ return;
811
+ }
812
+ assertNoUnknownKeys(value, CONTROL_KEYS, path, issues);
813
+ for (const key of CONTROL_KEYS) {
814
+ validateControlShape(`${path}.${key}`, value[key], issues);
815
+ }
816
+ }
817
+ function validateControlShape(path, value, issues) {
818
+ if (value === undefined)
819
+ return;
820
+ if (!isPlainObject(value)) {
821
+ issues.push({ path, message: "must be an object" });
822
+ return;
823
+ }
824
+ assertNoUnknownKeys(value, CONTROL_FIELD_KEYS, path, issues);
825
+ validateOptionalEnum(`${path}.state`, value.state, ["enabled", "disabled"], issues);
826
+ validateOptionalStringArray(`${path}.keys`, value.keys, issues);
827
+ }
828
+ function validateMissingTargetShape(path, value, issues) {
829
+ if (value === undefined)
830
+ return;
831
+ if (!isPlainObject(value)) {
832
+ issues.push({ path, message: "must be an object" });
833
+ return;
834
+ }
835
+ assertNoUnknownKeys(value, MISSING_TARGET_KEYS, path, issues);
836
+ validateOptionalEnum(`${path}.strategy`, value.strategy, ["wait", "skip", "error", "detached"], issues);
837
+ validateOptionalFiniteNonNegative(`${path}.timeout`, value.timeout, issues);
810
838
  }
811
839
  function validateScrollShape(path, value, issues) {
812
840
  if (value === undefined)
@@ -819,6 +847,8 @@ function validateScrollShape(path, value, issues) {
819
847
  validateOptionalEnum(`${path}.behavior`, value.behavior, ["auto", "smooth"], issues);
820
848
  validateOptionalEnum(`${path}.block`, value.block, ["start", "center", "end", "nearest"], issues);
821
849
  validateOptionalEnum(`${path}.inline`, value.inline, ["start", "center", "end", "nearest"], issues);
850
+ if (value.returnDelay !== false)
851
+ validateOptionalFiniteNonNegative(`${path}.returnDelay`, value.returnDelay, issues);
822
852
  }
823
853
  function validateBehaviorShape(path, value, issues) {
824
854
  if (value === undefined)
@@ -829,11 +859,11 @@ function validateBehaviorShape(path, value, issues) {
829
859
  }
830
860
  assertNoUnknownKeys(value, BEHAVIOR_KEYS, path, issues);
831
861
  validateOptionalBoolean(`${path}.allowInteraction`, value.allowInteraction, issues);
832
- validateOptionalBoolean(`${path}.disableAutoFocus`, value.disableAutoFocus, issues);
833
- validateOptionalBoolean(`${path}.disableAutoScroll`, value.disableAutoScroll, issues);
834
- validateOptionalEnum(`${path}.missingTargetStrategy`, value.missingTargetStrategy, ["wait", "skip", "error"], issues);
862
+ validateOptionalBoolean(`${path}.allowScroll`, value.allowScroll, issues);
863
+ validateOptionalBoolean(`${path}.autoFocus`, value.autoFocus, issues);
864
+ validateOptionalBoolean(`${path}.autoScroll`, value.autoScroll, issues);
865
+ validateMissingTargetShape(`${path}.missingTarget`, value.missingTarget, issues);
835
866
  validateScrollShape(`${path}.scroll`, value.scroll, issues);
836
- validateOptionalFiniteNonNegative(`${path}.targetTimeout`, value.targetTimeout, issues);
837
867
  validateOptionalEnum(`${path}.overlayClick`, value.overlayClick, ["none", "advance", "cancel"], issues);
838
868
  }
839
869
  function validateDataShape(path, value, issues) {
@@ -865,13 +895,13 @@ function assertNoUnknownKeys(value, allowedKeys, path, issues) {
865
895
  function createWorkflowFromConfig(config, options = {}) {
866
896
  const validated = validateWorkflowConfig(config, options);
867
897
  const builder = new WorkflowBuilder(validated.name, {
868
- cancellable: validated.cancellable,
869
- allowScroll: validated.allowScroll,
870
898
  overlay: validated.overlay,
871
899
  popover: validated.popover,
872
900
  indicator: validated.indicator,
873
901
  animated: validated.animated,
874
902
  behavior: validated.behavior,
903
+ controls: validated.controls,
904
+ classNames: validated.classNames,
875
905
  onStart: validated.onStart,
876
906
  onCancel: validated.onCancel,
877
907
  onFinish: validated.onFinish
@@ -886,11 +916,12 @@ function applyStepConfig(builder, stepConfig, path) {
886
916
  const step = builder.step({
887
917
  id: stepConfig.id,
888
918
  target: stepConfig.target,
889
- resetPropsOnEnter: stepConfig.resetPropsOnEnter,
890
919
  overlay: stepConfig.overlay,
891
920
  popover: stepConfig.popover,
892
921
  indicator: stepConfig.indicator,
893
922
  behavior: stepConfig.behavior,
923
+ controls: stepConfig.controls,
924
+ classNames: stepConfig.classNames,
894
925
  title: stepConfig.title,
895
926
  content: stepConfig.content,
896
927
  data: stepConfig.data
@@ -898,15 +929,13 @@ function applyStepConfig(builder, stepConfig, path) {
898
929
  for (const [index, actionRef] of (stepConfig.actions ?? []).entries()) {
899
930
  applyStepActionRef(step, actionRef, `${path}.actions[${index}]`);
900
931
  }
901
- for (const [index, handler] of (stepConfig.eventHandlers ?? []).entries()) {
902
- applyEventHandler(step, handler, `${path}.eventHandlers[${index}]`);
932
+ for (const [index, handler] of (stepConfig.targetEvents ?? []).entries()) {
933
+ applyTargetEvent(step, handler, `${path}.targetEvents[${index}]`);
903
934
  }
904
- if (stepConfig.advanceAction)
905
- step.beforeAdvance(stepConfig.advanceAction);
906
- if (stepConfig.previousAction)
907
- step.beforePrevious(stepConfig.previousAction);
908
- if (stepConfig.cancelAction)
909
- step.beforeCancel(stepConfig.cancelAction);
935
+ if (stepConfig.beforeEnter)
936
+ step.beforeEnter(stepConfig.beforeEnter);
937
+ if (stepConfig.beforeLeave)
938
+ step.beforeLeave(stepConfig.beforeLeave);
910
939
  }
911
940
  function applyStepActionRef(step, ref, path) {
912
941
  if (typeof ref === "function") {
@@ -936,8 +965,8 @@ function applyBuiltinAction(step, builtin, path) {
936
965
  throw new Error(`Unknown built-in action type at ${path}`);
937
966
  }
938
967
  }
939
- function applyEventHandler(step, handler, path) {
940
- const action = resolveEventHandlerAction(handler.action, `${path}.action`);
968
+ function applyTargetEvent(step, handler, path) {
969
+ const action = resolveTargetEventAction(handler.action, `${path}.action`);
941
970
  const events = typeof handler.event === "string" ? [handler.event] : handler.event;
942
971
  for (const event of events) {
943
972
  step.onTargetEvent(event, async (_event, context) => {
@@ -945,7 +974,7 @@ function applyEventHandler(step, handler, path) {
945
974
  });
946
975
  }
947
976
  }
948
- function resolveEventHandlerAction(ref, path) {
977
+ function resolveTargetEventAction(ref, path) {
949
978
  if (typeof ref === "function")
950
979
  return ref;
951
980
  const scratch = new WorkflowBuilder("__config_event_handler_scratch__");
package/config/types.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { WorkflowDefinition } from "../definition";
2
- import type { IndicatorOptions, LifecycleHookContext, OverlayOptions, PopoverOptions, PrimitiveValue, StepAction, StepBehavior, StepTransitionAction } from "../types";
2
+ import type { IndicatorOptions, LifecycleHookContext, OverlayOptions, PopoverOptions, PrimitiveValue, StepAction, StepBehavior, StepHookAction, TourClassNames, TourControls } from "../types";
3
3
  /**
4
4
  * A tour-level lifecycle hook callback (`onStart`, `onCancel`, `onFinish`), generic over the
5
5
  * config's content type `T` (defaults to `string`, the untrusted-JSON case).
@@ -42,23 +42,23 @@ export type BuiltinAction = {
42
42
  * builder verb.
43
43
  * - anything else is a validation error.
44
44
  *
45
- * Used for `actions[]` and `eventHandlers[].action`, which both run against a full `StepContext`
45
+ * Used for `actions[]` and `targetEvents[].action`, which both run against a full `StepContext`
46
46
  * (`target`, `signal`, navigation methods) — the same context `BuiltinAction`'s verbs assume, so
47
47
  * builtins are valid here.
48
48
  */
49
49
  export type StepActionRef<T = string> = BuiltinAction | StepAction<T>;
50
50
  /**
51
- * A reference to a transition hook (`advanceAction`/`previousAction`/`cancelAction`).
51
+ * A reference to a step hook (`beforeEnter`/`beforeLeave`).
52
52
  *
53
- * `BeforeActionStepContext` has a `target` but no `signal`/navigation methods, which the
54
- * `BuiltinAction` verbs (`wait`, `waitUntilElement`, `clickTarget`, `focusTarget`) all need — none
55
- * of them can run in this slot. With no registry to fall back on, a transition hook is therefore
56
- * only expressible as a same-runtime JS function, never as plain JSON. This is a plain function
57
- * type (not a union) precisely because there is nothing else valid to put here.
53
+ * Step hooks run while a transition is in progress. The `BuiltinAction` verbs describe a step's
54
+ * own action sequence (`wait` would stall the transition, `clickTarget` would act on a step that
55
+ * is not shown yet or is being left), so they are not accepted here. With no registry to fall back
56
+ * on, a step hook is only expressible as a same-runtime JS function, never as plain JSON. This is a
57
+ * plain function type (not a union) precisely because there is nothing else valid to put here.
58
58
  */
59
- export type TransitionActionRef<T = string> = StepTransitionAction<T>;
59
+ export type StepHookActionRef<T = string> = StepHookAction<T>;
60
60
  /** JSON config form of a single `onTargetEvent` registration. */
61
- export interface EventHandlerConfig<T = string> {
61
+ export interface TargetEventConfig<T = string> {
62
62
  /** Event name, or multiple event names sharing the same action. */
63
63
  readonly event: string | readonly string[];
64
64
  readonly action: StepActionRef<T>;
@@ -75,19 +75,23 @@ export interface StepConfig<T = string> {
75
75
  readonly id: string;
76
76
  /** CSS selector for the step's target. Functions and `HTMLElement` are not supported in config form. */
77
77
  readonly target: string;
78
- readonly resetPropsOnEnter?: boolean;
79
78
  readonly overlay?: OverlayOptions;
80
79
  readonly popover?: PopoverOptions;
81
80
  readonly indicator?: IndicatorOptions;
82
81
  readonly behavior?: StepBehavior;
83
- readonly title: T;
82
+ /** Navigation controls for this step, overriding the workflow ones field by field. */
83
+ readonly controls?: TourControls;
84
+ /** Classes added to the tour components on this step, overriding the workflow ones per component. */
85
+ readonly classNames?: TourClassNames;
86
+ readonly title?: T;
84
87
  readonly content: T;
85
88
  readonly data?: Record<string, PrimitiveValue>;
86
89
  readonly actions?: readonly StepActionRef<T>[];
87
- readonly eventHandlers?: readonly EventHandlerConfig<T>[];
88
- readonly advanceAction?: TransitionActionRef<T>;
89
- readonly previousAction?: TransitionActionRef<T>;
90
- readonly cancelAction?: TransitionActionRef<T>;
90
+ readonly targetEvents?: readonly TargetEventConfig<T>[];
91
+ /** Runs after the target is resolved and before the step is shown. Mirrors `beforeEnter`. */
92
+ readonly beforeEnter?: StepHookActionRef<T>;
93
+ /** Runs before navigating away from the step, never on cancel. Mirrors `beforeLeave`. */
94
+ readonly beforeLeave?: StepHookActionRef<T>;
91
95
  }
92
96
  /**
93
97
  * A complete, JSON-serializable tour definition.
@@ -96,14 +100,18 @@ export interface StepConfig<T = string> {
96
100
  * and same-runtime configs carrying framework content (`ReactNode`, `VNode`, `JSX.Element`, ...).
97
101
  */
98
102
  export interface WorkflowConfig<T = string> {
103
+ /** Version of the config format, required. It does not follow the package version. */
104
+ readonly version: "1.1";
99
105
  readonly name: string;
100
- readonly cancellable?: boolean;
101
- readonly allowScroll?: boolean;
102
106
  readonly overlay?: OverlayOptions;
103
107
  readonly popover?: PopoverOptions;
104
108
  readonly indicator?: IndicatorOptions;
105
109
  readonly animated?: boolean;
106
110
  readonly behavior?: StepBehavior;
111
+ /** Navigation controls for every step. */
112
+ readonly controls?: TourControls;
113
+ /** Classes added to the tour components on every step. */
114
+ readonly classNames?: TourClassNames;
107
115
  readonly onStart?: LifecycleActionRef<T>;
108
116
  readonly onCancel?: LifecycleActionRef<T>;
109
117
  readonly onFinish?: LifecycleActionRef<T>;
@@ -111,7 +119,7 @@ export interface WorkflowConfig<T = string> {
111
119
  }
112
120
  /** A single validation failure, with a path pointing at the offending config field. */
113
121
  export interface ConfigValidationIssue {
114
- /** Path into the config, e.g. `steps[2].eventHandlers[0].action`. */
122
+ /** Path into the config, e.g. `steps[2].targetEvents[0].action`. */
115
123
  readonly path: string;
116
124
  readonly message: string;
117
125
  }