@docentjs/dom 0.2.1 → 0.3.1
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.cjs +62 -29
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.js +62 -29
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -114,7 +114,12 @@ export declare class DomRenderer implements Renderer {
|
|
|
114
114
|
private isSheet;
|
|
115
115
|
/** In sheet mode, scroll once so the target is not hidden behind the sheet. */
|
|
116
116
|
private keepClearOfSheet;
|
|
117
|
-
/**
|
|
117
|
+
/**
|
|
118
|
+
* Run once a smooth scroll has settled, or right away for instant scrolls.
|
|
119
|
+
* Settled means the target stopped moving for two frames' worth of samples,
|
|
120
|
+
* not a fixed delay: smooth scrolls take longer on slow or busy devices.
|
|
121
|
+
* `scrollend` finishes early where supported; a cap keeps it bounded.
|
|
122
|
+
*/
|
|
118
123
|
private afterScroll;
|
|
119
124
|
private template;
|
|
120
125
|
private buildDefault;
|
|
@@ -145,8 +150,11 @@ interface CreateTourOptions extends Omit<ControllerOptions, 'tour' | 'renderer'>
|
|
|
145
150
|
*/
|
|
146
151
|
export declare class DomTourController extends TourController {
|
|
147
152
|
private readonly cleanups;
|
|
153
|
+
private readonly followRoutes;
|
|
148
154
|
constructor(tour: Tour, options?: CreateTourOptions);
|
|
155
|
+
start(at?: number | string): Promise<void>;
|
|
149
156
|
destroy(): Promise<void>;
|
|
157
|
+
private listenToRoutes;
|
|
150
158
|
}
|
|
151
159
|
/** Create a browser-ready tour. Call `.start()` or `.resume()` on the result. */
|
|
152
160
|
export declare function createTour(tour: Tour, options?: CreateTourOptions): DomTourController;
|
package/dist/index.d.ts
CHANGED
|
@@ -114,7 +114,12 @@ export declare class DomRenderer implements Renderer {
|
|
|
114
114
|
private isSheet;
|
|
115
115
|
/** In sheet mode, scroll once so the target is not hidden behind the sheet. */
|
|
116
116
|
private keepClearOfSheet;
|
|
117
|
-
/**
|
|
117
|
+
/**
|
|
118
|
+
* Run once a smooth scroll has settled, or right away for instant scrolls.
|
|
119
|
+
* Settled means the target stopped moving for two frames' worth of samples,
|
|
120
|
+
* not a fixed delay: smooth scrolls take longer on slow or busy devices.
|
|
121
|
+
* `scrollend` finishes early where supported; a cap keeps it bounded.
|
|
122
|
+
*/
|
|
118
123
|
private afterScroll;
|
|
119
124
|
private template;
|
|
120
125
|
private buildDefault;
|
|
@@ -145,8 +150,11 @@ interface CreateTourOptions extends Omit<ControllerOptions, 'tour' | 'renderer'>
|
|
|
145
150
|
*/
|
|
146
151
|
export declare class DomTourController extends TourController {
|
|
147
152
|
private readonly cleanups;
|
|
153
|
+
private readonly followRoutes;
|
|
148
154
|
constructor(tour: Tour, options?: CreateTourOptions);
|
|
155
|
+
start(at?: number | string): Promise<void>;
|
|
149
156
|
destroy(): Promise<void>;
|
|
157
|
+
private listenToRoutes;
|
|
150
158
|
}
|
|
151
159
|
/** Create a browser-ready tour. Call `.start()` or `.resume()` on the result. */
|
|
152
160
|
export declare function createTour(tour: Tour, options?: CreateTourOptions): DomTourController;
|
package/dist/index.js
CHANGED
|
@@ -480,7 +480,9 @@ const STYLES = `
|
|
|
480
480
|
--docent-width: 320px;
|
|
481
481
|
--docent-overlay: #000;
|
|
482
482
|
--docent-overlay-opacity: 0.55;
|
|
483
|
-
--docent-duration:
|
|
483
|
+
--docent-duration: 220ms;
|
|
484
|
+
/* Fast start, gentle stop: movement begins the moment you click. */
|
|
485
|
+
--docent-easing: cubic-bezier(0.2, 0.8, 0.2, 1);
|
|
484
486
|
position: fixed;
|
|
485
487
|
inset: 0;
|
|
486
488
|
z-index: var(--docent-z, 2147483000);
|
|
@@ -504,7 +506,7 @@ const STYLES = `
|
|
|
504
506
|
background: var(--docent-overlay);
|
|
505
507
|
opacity: var(--docent-overlay-opacity);
|
|
506
508
|
pointer-events: auto;
|
|
507
|
-
transition: clip-path var(--docent-duration)
|
|
509
|
+
transition: clip-path var(--docent-duration) var(--docent-easing);
|
|
508
510
|
}
|
|
509
511
|
.blocker {
|
|
510
512
|
position: absolute;
|
|
@@ -524,9 +526,12 @@ const STYLES = `
|
|
|
524
526
|
padding: 16px;
|
|
525
527
|
pointer-events: auto;
|
|
526
528
|
outline: none;
|
|
527
|
-
transition: transform var(--docent-duration)
|
|
529
|
+
transition: transform var(--docent-duration) var(--docent-easing), opacity var(--docent-duration) var(--docent-easing);
|
|
528
530
|
}
|
|
529
531
|
.popover[data-entering] { opacity: 0; transition: none; }
|
|
532
|
+
/* Between steps the popover slides; only its content cross-fades, briefly. */
|
|
533
|
+
.popover[data-moving] > * { animation: docent-swap 160ms ease-out; }
|
|
534
|
+
@keyframes docent-swap { from { opacity: 0; } to { opacity: 1; } }
|
|
530
535
|
.popover.headless {
|
|
531
536
|
width: auto;
|
|
532
537
|
max-width: none;
|
|
@@ -598,6 +603,7 @@ const STYLES = `
|
|
|
598
603
|
.button:focus-visible, .close:focus-visible { outline: 2px solid var(--docent-accent); outline-offset: 2px; }
|
|
599
604
|
@media (prefers-reduced-motion: reduce) {
|
|
600
605
|
.overlay, .popover { transition: none; }
|
|
606
|
+
.popover[data-moving] > * { animation: none; }
|
|
601
607
|
}
|
|
602
608
|
`;
|
|
603
609
|
//#endregion
|
|
@@ -754,6 +760,7 @@ var DomRenderer = class {
|
|
|
754
760
|
show(ctx) {
|
|
755
761
|
const firstStep = !this.host;
|
|
756
762
|
const host = this.mount();
|
|
763
|
+
const from = this.popover?.style.transform || null;
|
|
757
764
|
this.teardownStep();
|
|
758
765
|
this.ctx = ctx;
|
|
759
766
|
this.target = ctx.step.target === void 0 ? null : resolveTarget(ctx.step.target, this.doc);
|
|
@@ -761,12 +768,15 @@ var DomRenderer = class {
|
|
|
761
768
|
applyTheme(host, mergeThemes(this.options.theme, template?.theme, ctx.tour.options?.theme));
|
|
762
769
|
this.setTemplateCss(template?.css);
|
|
763
770
|
const initialFocus = this.options.headless ? this.buildHeadless(ctx, host, this.options.headless) : this.buildDefault(ctx, host, template);
|
|
764
|
-
this.popover
|
|
771
|
+
if (this.popover && from) {
|
|
772
|
+
this.popover.style.transform = from;
|
|
773
|
+
this.popover.setAttribute("data-moving", "");
|
|
774
|
+
} else this.popover?.setAttribute("data-entering", "");
|
|
765
775
|
if (this.target) {
|
|
766
776
|
const smooth = this.scrollIntoView(this.target, ctx.step);
|
|
767
777
|
if (this.options.avoidOcclusion !== false) {
|
|
768
778
|
const target = this.target;
|
|
769
|
-
this.afterScroll(smooth, () => {
|
|
779
|
+
this.afterScroll(smooth, target, () => {
|
|
770
780
|
if (this.target === target && uncover(target, host, this.viewport())) this.update();
|
|
771
781
|
});
|
|
772
782
|
}
|
|
@@ -909,28 +919,41 @@ var DomRenderer = class {
|
|
|
909
919
|
behavior: "auto"
|
|
910
920
|
});
|
|
911
921
|
}
|
|
912
|
-
/**
|
|
913
|
-
|
|
922
|
+
/**
|
|
923
|
+
* Run once a smooth scroll has settled, or right away for instant scrolls.
|
|
924
|
+
* Settled means the target stopped moving for two frames' worth of samples,
|
|
925
|
+
* not a fixed delay: smooth scrolls take longer on slow or busy devices.
|
|
926
|
+
* `scrollend` finishes early where supported; a cap keeps it bounded.
|
|
927
|
+
*/
|
|
928
|
+
afterScroll(smooth, target, fn) {
|
|
914
929
|
const win = this.doc.defaultView;
|
|
915
930
|
if (!smooth || !win) {
|
|
916
931
|
fn();
|
|
917
932
|
return;
|
|
918
933
|
}
|
|
919
934
|
let done = false;
|
|
920
|
-
|
|
921
|
-
|
|
935
|
+
let lastTop = NaN;
|
|
936
|
+
let stableSamples = 0;
|
|
937
|
+
const stop = () => {
|
|
922
938
|
done = true;
|
|
923
939
|
win.removeEventListener("scrollend", finish);
|
|
924
|
-
|
|
940
|
+
clearInterval(poll);
|
|
941
|
+
clearTimeout(cap);
|
|
942
|
+
};
|
|
943
|
+
const finish = () => {
|
|
944
|
+
if (done) return;
|
|
945
|
+
stop();
|
|
925
946
|
fn();
|
|
926
947
|
};
|
|
927
|
-
const
|
|
948
|
+
const poll = setInterval(() => {
|
|
949
|
+
const top = target.getBoundingClientRect().top;
|
|
950
|
+
stableSamples = Math.abs(top - lastTop) < .5 ? stableSamples + 1 : 0;
|
|
951
|
+
lastTop = top;
|
|
952
|
+
if (stableSamples >= 2) finish();
|
|
953
|
+
}, 80);
|
|
954
|
+
const cap = setTimeout(finish, 3e3);
|
|
928
955
|
win.addEventListener("scrollend", finish, { once: true });
|
|
929
|
-
this.cleanups.push(
|
|
930
|
-
done = true;
|
|
931
|
-
win.removeEventListener("scrollend", finish);
|
|
932
|
-
clearTimeout(timer);
|
|
933
|
-
});
|
|
956
|
+
this.cleanups.push(stop);
|
|
934
957
|
}
|
|
935
958
|
template(ctx) {
|
|
936
959
|
const name = ctx.tour.options?.template ?? this.options.template;
|
|
@@ -1095,6 +1118,9 @@ var DomRenderer = class {
|
|
|
1095
1118
|
}
|
|
1096
1119
|
onKeydown(e, ctx) {
|
|
1097
1120
|
const options = ctx.tour.options ?? {};
|
|
1121
|
+
const path = typeof e.composedPath === "function" ? e.composedPath() : [];
|
|
1122
|
+
const origin = path[0] ?? e.target;
|
|
1123
|
+
if (path.some((n) => n instanceof Element && n.hasAttribute("data-docent-ignore-keys"))) return;
|
|
1098
1124
|
if (e.key === "Escape" && options.allowClose !== false) {
|
|
1099
1125
|
e.preventDefault();
|
|
1100
1126
|
ctx.actions.skip();
|
|
@@ -1105,7 +1131,7 @@ var DomRenderer = class {
|
|
|
1105
1131
|
return;
|
|
1106
1132
|
}
|
|
1107
1133
|
if (options.keyboard === false) return;
|
|
1108
|
-
if (
|
|
1134
|
+
if (origin instanceof HTMLElement && (/^(INPUT|TEXTAREA|SELECT)$/.test(origin.tagName) || origin.isContentEditable)) return;
|
|
1109
1135
|
if (e.key === "ArrowRight" && ctx.step.buttons?.next !== false) {
|
|
1110
1136
|
e.preventDefault();
|
|
1111
1137
|
ctx.actions.next();
|
|
@@ -1200,6 +1226,7 @@ function createLocalStorage(storage) {
|
|
|
1200
1226
|
*/
|
|
1201
1227
|
var DomTourController = class extends TourController {
|
|
1202
1228
|
cleanups = [];
|
|
1229
|
+
followRoutes;
|
|
1203
1230
|
constructor(tour, options = {}) {
|
|
1204
1231
|
const { renderer: rendererOptions, followRoutes, ...rest } = options;
|
|
1205
1232
|
const renderer = new DomRenderer(rendererOptions);
|
|
@@ -1209,24 +1236,30 @@ var DomTourController = class extends TourController {
|
|
|
1209
1236
|
renderer,
|
|
1210
1237
|
storage: rest.storage ?? createLocalStorage()
|
|
1211
1238
|
});
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
}
|
|
1218
|
-
const nav = window.navigation;
|
|
1219
|
-
if (nav) {
|
|
1220
|
-
nav.addEventListener("navigatesuccess", onChange);
|
|
1221
|
-
this.cleanups.push(() => nav.removeEventListener("navigatesuccess", onChange));
|
|
1222
|
-
}
|
|
1223
|
-
}
|
|
1239
|
+
this.followRoutes = followRoutes !== false;
|
|
1240
|
+
}
|
|
1241
|
+
async start(at) {
|
|
1242
|
+
this.listenToRoutes();
|
|
1243
|
+
return super.start(at);
|
|
1224
1244
|
}
|
|
1225
1245
|
async destroy() {
|
|
1226
1246
|
for (const c of this.cleanups) c();
|
|
1227
1247
|
this.cleanups.length = 0;
|
|
1228
1248
|
await super.destroy();
|
|
1229
1249
|
}
|
|
1250
|
+
listenToRoutes() {
|
|
1251
|
+
if (!this.followRoutes || this.cleanups.length > 0 || typeof window === "undefined") return;
|
|
1252
|
+
const onChange = () => void this.routeChanged();
|
|
1253
|
+
for (const type of ["popstate", "hashchange"]) {
|
|
1254
|
+
window.addEventListener(type, onChange);
|
|
1255
|
+
this.cleanups.push(() => window.removeEventListener(type, onChange));
|
|
1256
|
+
}
|
|
1257
|
+
const nav = window.navigation;
|
|
1258
|
+
if (nav) {
|
|
1259
|
+
nav.addEventListener("navigatesuccess", onChange);
|
|
1260
|
+
this.cleanups.push(() => nav.removeEventListener("navigatesuccess", onChange));
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1230
1263
|
};
|
|
1231
1264
|
/** Create a browser-ready tour. Call `.start()` or `.resume()` on the result. */
|
|
1232
1265
|
function createTour(tour, options) {
|