@fluid-topics/ft-wc-utils 1.1.100 → 1.1.101

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.
@@ -0,0 +1,22 @@
1
+ import { Optional } from "./generic-types";
2
+ type FluidTopicsScrollHelperInterface = {
3
+ lock<T extends Element>(manager: Object, scrollable: Optional<T>): T | undefined;
4
+ release(manager: Object, scrollable: Optional<Element>): void;
5
+ findFirstScrollableParent(element: HTMLElement): HTMLElement;
6
+ getAbsoluteScrollOffset(scrollable: HTMLElement, target: HTMLElement): number;
7
+ scrollIntoViewIfPossible(element: HTMLElement, options?: FtScrollIntoViewOptions): void;
8
+ };
9
+ declare global {
10
+ interface Window {
11
+ FluidTopicsScrollHelper: FluidTopicsScrollHelperInterface;
12
+ }
13
+ }
14
+ export interface FtScrollIntoViewOptions {
15
+ behavior?: ScrollIntoViewOptions["behavior"] | "instant";
16
+ /**
17
+ * position can be a number from 0 (=== "start") to 1 (=== "end").
18
+ */
19
+ position?: ScrollIntoViewOptions["block"] | number;
20
+ }
21
+ export declare const scrollHelper: FluidTopicsScrollHelperInterface;
22
+ export {};
@@ -0,0 +1,96 @@
1
+ var _a;
2
+ import { minmax } from "./helpers";
3
+ const locks = new Map();
4
+ class FluidTopicsScrollHelper {
5
+ lock(manager, scrollable) {
6
+ if (scrollable) {
7
+ const existingManager = locks.get(scrollable);
8
+ if (existingManager == null || existingManager === manager) {
9
+ locks.set(scrollable, manager);
10
+ return scrollable;
11
+ }
12
+ }
13
+ return undefined;
14
+ }
15
+ release(manager, scrollable) {
16
+ if (scrollable && locks.get(scrollable) === manager) {
17
+ locks.delete(scrollable);
18
+ }
19
+ }
20
+ findFirstScrollableParent(element) {
21
+ let scrollableParent;
22
+ const findScrollableParent = (e) => {
23
+ var _a;
24
+ e.stopPropagation();
25
+ let scrollable, maybeScrollable;
26
+ for (let target of e.composedPath()) {
27
+ const element = target;
28
+ const mayScroll = this.elementCanScroll(element);
29
+ const definitelyScrolls = mayScroll && element.clientHeight && element.clientHeight < element.scrollHeight;
30
+ if (definitelyScrolls) {
31
+ scrollable = element;
32
+ break;
33
+ }
34
+ else if (mayScroll && maybeScrollable == null) {
35
+ maybeScrollable = element;
36
+ }
37
+ }
38
+ scrollableParent = (_a = scrollable !== null && scrollable !== void 0 ? scrollable : maybeScrollable) !== null && _a !== void 0 ? _a : document.body;
39
+ };
40
+ element.addEventListener("find-scrollable-parent", findScrollableParent);
41
+ element.dispatchEvent(new Event("find-scrollable-parent", { composed: true }));
42
+ element.removeEventListener("find-scrollable-parent", findScrollableParent);
43
+ return scrollableParent;
44
+ }
45
+ elementCanScroll(element) {
46
+ try {
47
+ return ["auto", "scroll"].includes(getComputedStyle(element).overflowY);
48
+ }
49
+ catch (e) {
50
+ return false;
51
+ }
52
+ }
53
+ getAbsoluteScrollOffset(scrollable, target) {
54
+ let offset = 0;
55
+ while (target && target.offsetParent !== scrollable.offsetParent) {
56
+ offset += target.offsetTop;
57
+ target = target.offsetParent;
58
+ }
59
+ return offset + target.offsetTop - scrollable.offsetTop;
60
+ }
61
+ computeTopOffsetRatio(position, nearestValue) {
62
+ if (typeof position === "number") {
63
+ return position;
64
+ }
65
+ switch (position) {
66
+ case "end":
67
+ return 1;
68
+ case "center":
69
+ return 0.5;
70
+ case "nearest":
71
+ return nearestValue;
72
+ default:
73
+ return 0;
74
+ }
75
+ }
76
+ scrollIntoViewIfPossible(element, options) {
77
+ const scrollableParent = this.findFirstScrollableParent(element);
78
+ if (element && this.lock(this, scrollableParent)) {
79
+ const targetOffset = this.getAbsoluteScrollOffset(scrollableParent, element);
80
+ const minScrollTop = targetOffset - scrollableParent.clientHeight + element.clientHeight;
81
+ const maxScrollTop = targetOffset;
82
+ const nearestValue = minmax(0, (maxScrollTop - scrollableParent.scrollTop) / (maxScrollTop - minScrollTop), 1);
83
+ const top = this.computeTopOffsetRatio(options === null || options === void 0 ? void 0 : options.position, nearestValue) * (minScrollTop - maxScrollTop) + maxScrollTop;
84
+ scrollableParent.scrollTo({
85
+ behavior: options === null || options === void 0 ? void 0 : options.behavior,
86
+ top: top
87
+ });
88
+ }
89
+ else {
90
+ console.warn(`Could not scroll element into view because the scrollable parent is already locked`, element, scrollableParent);
91
+ }
92
+ this.release(this, scrollableParent);
93
+ }
94
+ }
95
+ window.FluidTopicsScrollHelper = (_a = window.FluidTopicsScrollHelper) !== null && _a !== void 0 ? _a : new FluidTopicsScrollHelper();
96
+ export const scrollHelper = window.FluidTopicsScrollHelper;