@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/CHANGELOG.md +193 -0
- package/README.md +3 -3
- package/builder/index.d.ts +15 -16
- package/config/index.d.ts +1 -1
- package/config/index.js +164 -135
- package/config/types.d.ts +27 -19
- package/definition/step-props.d.ts +9 -1
- package/definition/types.d.ts +10 -10
- package/definition/workflow-definition.d.ts +4 -7
- package/dom/tour-view-driver.d.ts +134 -3
- package/elements/base.d.ts +24 -2
- package/elements/popover.d.ts +23 -6
- package/index.d.ts +1 -1
- package/index.js +896 -503
- package/package.json +6 -4
- package/runtime/active-step.d.ts +18 -55
- package/runtime/tour-controller.d.ts +48 -12
- package/state/focus-guard.d.ts +15 -0
- package/types/index.d.ts +187 -86
- package/utils/options.d.ts +13 -1
- package/utils/utils.d.ts +16 -8
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
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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
|
-
|
|
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
|
-
|
|
261
|
-
|
|
262
|
-
|
|
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
|
-
|
|
366
|
+
beforeEnter(callback) {
|
|
368
367
|
this.assertActive();
|
|
369
|
-
this.draft.
|
|
368
|
+
this.draft.beforeEnter = callback;
|
|
370
369
|
return this;
|
|
371
370
|
}
|
|
372
|
-
|
|
371
|
+
beforeLeave(callback) {
|
|
373
372
|
this.assertActive();
|
|
374
|
-
this.draft.
|
|
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.
|
|
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
|
-
"
|
|
448
|
-
"
|
|
449
|
-
"
|
|
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", "
|
|
456
|
-
var POPOVER_KEYS = [
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
"
|
|
461
|
-
"
|
|
462
|
-
"
|
|
463
|
-
"
|
|
464
|
-
"
|
|
465
|
-
"
|
|
466
|
-
|
|
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
|
-
"
|
|
464
|
+
"hidden",
|
|
471
465
|
"color",
|
|
472
466
|
"size",
|
|
473
467
|
"borderWidth",
|
|
474
468
|
"borderRadius",
|
|
475
469
|
"edgePadding",
|
|
476
470
|
"styleNonce",
|
|
477
|
-
"
|
|
471
|
+
"autoStyles"
|
|
478
472
|
];
|
|
479
|
-
var
|
|
473
|
+
var MISSING_TARGET_KEYS = ["strategy", "timeout"];
|
|
480
474
|
var BEHAVIOR_KEYS = [
|
|
481
475
|
"allowInteraction",
|
|
482
|
-
"
|
|
483
|
-
"
|
|
484
|
-
"
|
|
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.
|
|
589
|
-
if (!Array.isArray(value.
|
|
590
|
-
issues.push({ path: `${path}.
|
|
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.
|
|
593
|
-
|
|
591
|
+
for (const [index, handler] of value.targetEvents.entries()) {
|
|
592
|
+
validateTargetEventConfigShape(handler, `${path}.targetEvents[${index}]`, issues);
|
|
594
593
|
}
|
|
595
594
|
}
|
|
596
595
|
}
|
|
597
|
-
|
|
598
|
-
|
|
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
|
|
599
|
+
function validateTargetEventConfigShape(value, path, issues) {
|
|
602
600
|
if (!isPlainObject(value)) {
|
|
603
|
-
issues.push({ path, message: "
|
|
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
|
|
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
|
-
|
|
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}.
|
|
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}.
|
|
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}.
|
|
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
|
-
|
|
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}.
|
|
833
|
-
validateOptionalBoolean(`${path}.
|
|
834
|
-
|
|
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.
|
|
902
|
-
|
|
932
|
+
for (const [index, handler] of (stepConfig.targetEvents ?? []).entries()) {
|
|
933
|
+
applyTargetEvent(step, handler, `${path}.targetEvents[${index}]`);
|
|
903
934
|
}
|
|
904
|
-
if (stepConfig.
|
|
905
|
-
step.
|
|
906
|
-
if (stepConfig.
|
|
907
|
-
step.
|
|
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
|
|
940
|
-
const 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
|
|
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,
|
|
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 `
|
|
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
|
|
51
|
+
* A reference to a step hook (`beforeEnter`/`beforeLeave`).
|
|
52
52
|
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
* only expressible as a same-runtime JS function, never as plain JSON. This is a
|
|
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
|
|
59
|
+
export type StepHookActionRef<T = string> = StepHookAction<T>;
|
|
60
60
|
/** JSON config form of a single `onTargetEvent` registration. */
|
|
61
|
-
export interface
|
|
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
|
-
|
|
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
|
|
88
|
-
|
|
89
|
-
readonly
|
|
90
|
-
|
|
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].
|
|
122
|
+
/** Path into the config, e.g. `steps[2].targetEvents[0].action`. */
|
|
115
123
|
readonly path: string;
|
|
116
124
|
readonly message: string;
|
|
117
125
|
}
|