@player-ui/auto-scroll-manager-plugin-react 0.16.0--canary.812.32412 → 0.16.0--canary.866.36538
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/cjs/index.cjs +16 -2
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/index.legacy-esm.js +16 -2
- package/dist/index.mjs +16 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +8 -8
- package/src/__tests__/plugin.test.tsx +182 -18
- package/src/hooks.tsx +8 -1
- package/src/plugin.tsx +17 -5
- package/types/hooks.d.ts +3 -1
- package/types/plugin.d.ts +2 -0
package/dist/cjs/index.cjs
CHANGED
|
@@ -61,9 +61,13 @@ var AutoScrollProvider = ({
|
|
|
61
61
|
getElementToScrollTo,
|
|
62
62
|
getBaseElement,
|
|
63
63
|
offset,
|
|
64
|
+
onMount,
|
|
64
65
|
children
|
|
65
66
|
}) => {
|
|
66
67
|
const [scrollableMap, setScrollableMap] = (0, import_react.useState)(/* @__PURE__ */ new Map());
|
|
68
|
+
(0, import_react.useEffect)(() => {
|
|
69
|
+
onMount?.(() => setScrollableMap(/* @__PURE__ */ new Map()));
|
|
70
|
+
}, []);
|
|
67
71
|
const updateScrollableMap = (key, value) => {
|
|
68
72
|
setScrollableMap((prev) => {
|
|
69
73
|
const nm = new Map(prev);
|
|
@@ -82,7 +86,7 @@ var AutoScrollProvider = ({
|
|
|
82
86
|
if (node) {
|
|
83
87
|
scrollIntoViewWithOffset(node, getBaseElement() || document.body, offset);
|
|
84
88
|
}
|
|
85
|
-
});
|
|
89
|
+
}, [scrollableMap]);
|
|
86
90
|
return /* @__PURE__ */ import_react.default.createElement(AutoScrollManagerContext.Provider, { value: { register } }, children);
|
|
87
91
|
};
|
|
88
92
|
|
|
@@ -104,6 +108,8 @@ var AutoScrollManagerPlugin = class {
|
|
|
104
108
|
this.initialRender = false;
|
|
105
109
|
this.failedNavigation = false;
|
|
106
110
|
this.alreadyScrolledTo = [];
|
|
111
|
+
this.clearScrollableMap = () => {
|
|
112
|
+
};
|
|
107
113
|
this.scrollFn = this.calculateScroll.bind(this);
|
|
108
114
|
}
|
|
109
115
|
getFirstScrollableElement(idList, type) {
|
|
@@ -162,6 +168,8 @@ var AutoScrollManagerPlugin = class {
|
|
|
162
168
|
this.initialRender = true;
|
|
163
169
|
this.failedNavigation = false;
|
|
164
170
|
this.alreadyScrolledTo = [];
|
|
171
|
+
this.clearScrollableMap();
|
|
172
|
+
window.dispatchEvent(new WheelEvent("wheel", { bubbles: true }));
|
|
165
173
|
window.scroll(0, 0);
|
|
166
174
|
});
|
|
167
175
|
flow.hooks.skipTransition.intercept({
|
|
@@ -175,13 +183,19 @@ var AutoScrollManagerPlugin = class {
|
|
|
175
183
|
applyReact(reactPlayer) {
|
|
176
184
|
reactPlayer.hooks.webComponent.tap(this.name, (Comp) => {
|
|
177
185
|
const { scrollFn, getBaseElement, offset } = this;
|
|
186
|
+
const setScrollableMapClearer = (clear) => {
|
|
187
|
+
this.clearScrollableMap = clear;
|
|
188
|
+
};
|
|
178
189
|
function AutoScrollManagerComponent() {
|
|
179
190
|
return /* @__PURE__ */ import_react2.default.createElement(
|
|
180
191
|
AutoScrollProvider,
|
|
181
192
|
{
|
|
182
193
|
getElementToScrollTo: scrollFn,
|
|
183
194
|
getBaseElement,
|
|
184
|
-
offset
|
|
195
|
+
offset,
|
|
196
|
+
onMount: (clear) => {
|
|
197
|
+
setScrollableMapClearer(clear);
|
|
198
|
+
}
|
|
185
199
|
},
|
|
186
200
|
/* @__PURE__ */ import_react2.default.createElement(Comp, null)
|
|
187
201
|
);
|
package/dist/cjs/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/plugins/auto-scroll/react/src/index.tsx","../../../../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/plugins/auto-scroll/react/src/hooks.tsx","../../../../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/plugins/auto-scroll/react/src/scrollIntoViewWithOffset.ts","../../../../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/plugins/auto-scroll/react/src/plugin.tsx"],"sourcesContent":["export * from \"./hooks\";\nexport * from \"./plugin\";\n","import type { PropsWithChildren } from \"react\";\nimport React, { useEffect, useState } from \"react\";\nimport { scrollIntoViewWithOffset } from \"./scrollIntoViewWithOffset\";\nimport type { ScrollType } from \"./index\";\n\nexport interface AutoScrollProviderProps {\n /** Return the element to scroll to based on the registered types */\n getElementToScrollTo: (\n scrollableElements: Map<ScrollType, Set<string>>,\n ) => string;\n /** Optional function to get container element, which is used for calculating offset (default: document.body) */\n getBaseElement: () => HTMLElement | undefined | null;\n /** Additional offset to be used (default: 0) */\n offset: number;\n}\n\nexport interface RegisterData {\n /** when to scroll to the target */\n type: ScrollType;\n\n /** the html id to scroll to */\n ref: string;\n}\n\nexport type ScrollFunction = (registerData: RegisterData) => void;\n\nexport const AutoScrollManagerContext = React.createContext<{\n /** function to register a scroll target */\n register: ScrollFunction;\n}>({ register: () => {} });\n\n/** hook to register as a scroll target */\nexport const useRegisterAsScrollable = (): ScrollFunction => {\n const { register } = React.useContext(AutoScrollManagerContext);\n\n return register;\n};\n\n/** Component to handle scrolling */\nexport const AutoScrollProvider = ({\n getElementToScrollTo,\n getBaseElement,\n offset,\n children,\n}: PropsWithChildren<AutoScrollProviderProps>) => {\n // Tracker for what elements are registered to be scroll targets\n // Key is the type (initial, validation, appear)\n // Value is a set of target ids\n const [scrollableMap, setScrollableMap] = useState<\n Map<ScrollType, Set<string>>\n >(new Map());\n\n /** Add a new entry as a scroll target */\n const updateScrollableMap = (key: ScrollType, value: string) => {\n setScrollableMap((prev) => {\n const nm = new Map(prev);\n\n if (!nm.get(key)) {\n nm.set(key, new Set());\n }\n\n nm.get(key)?.add(value);\n\n return nm;\n });\n };\n\n /** register a new scroll target */\n const register: ScrollFunction = (data) => {\n updateScrollableMap(data.type, data.ref);\n };\n\n useEffect(() => {\n const node = document.getElementById(getElementToScrollTo(scrollableMap));\n\n if (node) {\n scrollIntoViewWithOffset(node, getBaseElement() || document.body, offset);\n }\n });\n\n return (\n <AutoScrollManagerContext.Provider value={{ register }}>\n {children}\n </AutoScrollManagerContext.Provider>\n );\n};\n","import { scrollTo } from \"seamless-scroll-polyfill\";\n\n/**\n * Scroll to the given element with an offset\n * @param node Element to scroll to\n * @param baseElement Container element used to calculate offset\n * @param offset Additional offset\n */\nexport function scrollIntoViewWithOffset(\n node: HTMLElement,\n baseElement: HTMLElement,\n offset: number,\n) {\n scrollTo(window, {\n behavior: \"smooth\",\n top:\n node.getBoundingClientRect().top -\n baseElement.getBoundingClientRect().top -\n offset,\n });\n}\n","import type { ReactPlayer, ReactPlayerPlugin } from \"@player-ui/react\";\nimport type { Player } from \"@player-ui/react\";\nimport React from \"react\";\nimport { AutoScrollProvider } from \"./hooks\";\n\nexport enum ScrollType {\n ValidationError,\n FirstAppearance,\n Unknown,\n}\n\nexport interface AutoScrollManagerConfig {\n /** Config to auto-scroll on load */\n autoScrollOnLoad?: boolean;\n /** Config to auto-focus on an error */\n autoFocusOnErrorField?: boolean;\n /** Optional function to get container element, which is used for calculating offset (default: document.body) */\n getBaseElement?: () => HTMLElement | undefined | null;\n /** Additional offset to be used (default: 0) */\n offset?: number;\n}\n\n/** A plugin to manage scrolling behavior */\nexport class AutoScrollManagerPlugin implements ReactPlayerPlugin {\n name = \"auto-scroll-manager\";\n\n /** Toggles if we should auto scroll to to the first failed validation on page load */\n private autoScrollOnLoad: boolean;\n\n /** Toggles if we should auto scroll to the first failed validation on navigation failure */\n private autoFocusOnErrorField: boolean;\n\n /** tracks if its the initial page render */\n private initialRender: boolean;\n\n /** tracks if the navigation failed */\n private failedNavigation: boolean;\n\n /** function to return the base of the scrollable area */\n private getBaseElement: () => HTMLElement | undefined | null;\n\n /** static offset */\n private offset: number;\n\n /** map of scroll type to set of ids that are registered under that type */\n private alreadyScrolledTo: Array<string>;\n private scrollFn: (\n scrollableElements: Map<ScrollType, Set<string>>,\n ) => string;\n\n constructor(config: AutoScrollManagerConfig) {\n this.autoScrollOnLoad = config.autoScrollOnLoad ?? false;\n this.autoFocusOnErrorField = config.autoFocusOnErrorField ?? false;\n this.getBaseElement = config.getBaseElement ?? (() => null);\n this.offset = config.offset ?? 0;\n this.initialRender = false;\n this.failedNavigation = false;\n this.alreadyScrolledTo = [];\n this.scrollFn = this.calculateScroll.bind(this);\n }\n\n getFirstScrollableElement(idList: Set<string>, type: ScrollType) {\n const highestElement = {\n id: \"\",\n ypos: 0,\n };\n const ypos = window.scrollY;\n idList.forEach((id) => {\n const element = document.getElementById(id);\n\n // if we are looking at validation errors, make sure the element is invalid\n if (\n type === ScrollType.ValidationError &&\n element?.getAttribute(\"aria-invalid\") === \"false\"\n ) {\n return;\n }\n\n // if we are just looking at elements that just appeared, make sure we haven't\n // scrolled to them before\n if (type === ScrollType.FirstAppearance) {\n if (this.alreadyScrolledTo.indexOf(id) !== -1) {\n return;\n }\n\n this.alreadyScrolledTo.push(id);\n }\n\n const epos = element?.getBoundingClientRect().top;\n if (\n epos !== undefined &&\n (epos + ypos < highestElement.ypos || highestElement.id === \"\")\n ) {\n highestElement.id = id;\n highestElement.ypos = ypos + epos;\n }\n });\n return highestElement.id;\n }\n\n calculateScroll(scrollableElements: Map<ScrollType, Set<string>>) {\n let currentScroll = ScrollType.FirstAppearance;\n if (this.initialRender) {\n if (this.autoScrollOnLoad) {\n currentScroll = ScrollType.ValidationError;\n }\n\n this.initialRender = false;\n } else if (this.failedNavigation) {\n if (this.autoFocusOnErrorField) {\n currentScroll = ScrollType.ValidationError;\n }\n\n this.failedNavigation = false;\n }\n\n const elementList = scrollableElements.get(currentScroll);\n if (elementList) {\n const element = this.getFirstScrollableElement(\n elementList,\n currentScroll,\n );\n return element ?? \"\";\n }\n\n return \"\";\n }\n\n // Hooks into player flow to determine what scroll targets need to be evaluated at specific lifecycle points\n apply(player: Player) {\n player.hooks.flowController.tap(this.name, (fc) => {\n fc.hooks.flow.tap(this.name, (flow) => {\n flow.hooks.transition.tap(this.name, () => {\n // Reset Everything\n this.initialRender = true;\n this.failedNavigation = false;\n this.alreadyScrolledTo = [];\n // Reset scroll position for new view\n window.scroll(0, 0);\n });\n flow.hooks.skipTransition.intercept({\n call: () => {\n this.failedNavigation = true;\n },\n });\n });\n });\n }\n\n applyReact(reactPlayer: ReactPlayer) {\n reactPlayer.hooks.webComponent.tap(this.name, (Comp) => {\n const { scrollFn, getBaseElement, offset } = this;\n\n function AutoScrollManagerComponent() {\n return (\n <AutoScrollProvider\n getElementToScrollTo={scrollFn}\n getBaseElement={getBaseElement}\n offset={offset}\n >\n <Comp />\n </AutoScrollProvider>\n );\n }\n\n return AutoScrollManagerComponent;\n });\n }\n}\n"],"mappings":"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","names":["React","import_react","ScrollType","React"]}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/plugins/auto-scroll/react/src/index.tsx","../../../../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/plugins/auto-scroll/react/src/hooks.tsx","../../../../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/plugins/auto-scroll/react/src/scrollIntoViewWithOffset.ts","../../../../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/plugins/auto-scroll/react/src/plugin.tsx"],"sourcesContent":["export * from \"./hooks\";\nexport * from \"./plugin\";\n","import type { PropsWithChildren } from \"react\";\nimport React, { useEffect, useState } from \"react\";\nimport { scrollIntoViewWithOffset } from \"./scrollIntoViewWithOffset\";\nimport type { ScrollType } from \"./index\";\n\nexport interface AutoScrollProviderProps {\n /** Return the element to scroll to based on the registered types */\n getElementToScrollTo: (\n scrollableElements: Map<ScrollType, Set<string>>,\n ) => string;\n /** Optional function to get container element, which is used for calculating offset (default: document.body) */\n getBaseElement: () => HTMLElement | undefined | null;\n /** Additional offset to be used (default: 0) */\n offset: number;\n /** Called on mount with a function that clears all registered scroll targets */\n onMount?: (clearScrollableMap: () => void) => void;\n}\n\nexport interface RegisterData {\n /** when to scroll to the target */\n type: ScrollType;\n\n /** the html id to scroll to */\n ref: string;\n}\n\nexport type ScrollFunction = (registerData: RegisterData) => void;\n\nexport const AutoScrollManagerContext = React.createContext<{\n /** function to register a scroll target */\n register: ScrollFunction;\n}>({ register: () => {} });\n\n/** hook to register as a scroll target */\nexport const useRegisterAsScrollable = (): ScrollFunction => {\n const { register } = React.useContext(AutoScrollManagerContext);\n\n return register;\n};\n\n/** Component to handle scrolling */\nexport const AutoScrollProvider = ({\n getElementToScrollTo,\n getBaseElement,\n offset,\n onMount,\n children,\n}: PropsWithChildren<AutoScrollProviderProps>) => {\n // Tracker for what elements are registered to be scroll targets\n // Key is the type (initial, validation, appear)\n // Value is a set of target ids\n const [scrollableMap, setScrollableMap] = useState<\n Map<ScrollType, Set<string>>\n >(new Map());\n\n useEffect(() => {\n onMount?.(() => setScrollableMap(new Map()));\n }, []);\n\n /** Add a new entry as a scroll target */\n const updateScrollableMap = (key: ScrollType, value: string) => {\n setScrollableMap((prev) => {\n const nm = new Map(prev);\n\n if (!nm.get(key)) {\n nm.set(key, new Set());\n }\n\n nm.get(key)?.add(value);\n\n return nm;\n });\n };\n\n /** register a new scroll target */\n const register: ScrollFunction = (data) => {\n updateScrollableMap(data.type, data.ref);\n };\n\n useEffect(() => {\n const node = document.getElementById(getElementToScrollTo(scrollableMap));\n\n if (node) {\n scrollIntoViewWithOffset(node, getBaseElement() || document.body, offset);\n }\n }, [scrollableMap]);\n\n return (\n <AutoScrollManagerContext.Provider value={{ register }}>\n {children}\n </AutoScrollManagerContext.Provider>\n );\n};\n","import { scrollTo } from \"seamless-scroll-polyfill\";\n\n/**\n * Scroll to the given element with an offset\n * @param node Element to scroll to\n * @param baseElement Container element used to calculate offset\n * @param offset Additional offset\n */\nexport function scrollIntoViewWithOffset(\n node: HTMLElement,\n baseElement: HTMLElement,\n offset: number,\n) {\n scrollTo(window, {\n behavior: \"smooth\",\n top:\n node.getBoundingClientRect().top -\n baseElement.getBoundingClientRect().top -\n offset,\n });\n}\n","import type { ReactPlayer, ReactPlayerPlugin } from \"@player-ui/react\";\nimport type { Player } from \"@player-ui/react\";\nimport React from \"react\";\nimport { AutoScrollProvider } from \"./hooks\";\n\nexport enum ScrollType {\n ValidationError,\n FirstAppearance,\n Unknown,\n}\n\nexport interface AutoScrollManagerConfig {\n /** Config to auto-scroll on load */\n autoScrollOnLoad?: boolean;\n /** Config to auto-focus on an error */\n autoFocusOnErrorField?: boolean;\n /** Optional function to get container element, which is used for calculating offset (default: document.body) */\n getBaseElement?: () => HTMLElement | undefined | null;\n /** Additional offset to be used (default: 0) */\n offset?: number;\n}\n\n/** A plugin to manage scrolling behavior */\nexport class AutoScrollManagerPlugin implements ReactPlayerPlugin {\n name = \"auto-scroll-manager\";\n\n /** Toggles if we should auto scroll to to the first failed validation on page load */\n private autoScrollOnLoad: boolean;\n\n /** Toggles if we should auto scroll to the first failed validation on navigation failure */\n private autoFocusOnErrorField: boolean;\n\n /** tracks if its the initial page render */\n private initialRender: boolean;\n\n /** tracks if the navigation failed */\n private failedNavigation: boolean;\n\n /** function to return the base of the scrollable area */\n private getBaseElement: () => HTMLElement | undefined | null;\n\n /** static offset */\n private offset: number;\n\n /** clears scrollableMap in AutoScrollProvider — set when the provider mounts */\n private clearScrollableMap: () => void;\n\n /** map of scroll type to set of ids that are registered under that type */\n private alreadyScrolledTo: Array<string>;\n private scrollFn: (\n scrollableElements: Map<ScrollType, Set<string>>,\n ) => string;\n\n constructor(config: AutoScrollManagerConfig) {\n this.autoScrollOnLoad = config.autoScrollOnLoad ?? false;\n this.autoFocusOnErrorField = config.autoFocusOnErrorField ?? false;\n this.getBaseElement = config.getBaseElement ?? (() => null);\n this.offset = config.offset ?? 0;\n this.initialRender = false;\n this.failedNavigation = false;\n this.alreadyScrolledTo = [];\n this.clearScrollableMap = () => {};\n this.scrollFn = this.calculateScroll.bind(this);\n }\n\n getFirstScrollableElement(idList: Set<string>, type: ScrollType): string {\n const highestElement = {\n id: \"\",\n ypos: 0,\n };\n const ypos = window.scrollY;\n idList.forEach((id) => {\n const element = document.getElementById(id);\n\n // if we are looking at validation errors, make sure the element is invalid\n if (\n type === ScrollType.ValidationError &&\n element?.getAttribute(\"aria-invalid\") === \"false\"\n ) {\n return;\n }\n\n // if we are just looking at elements that just appeared, make sure we haven't\n // scrolled to them before\n if (type === ScrollType.FirstAppearance) {\n if (this.alreadyScrolledTo.indexOf(id) !== -1) {\n return;\n }\n\n this.alreadyScrolledTo.push(id);\n }\n\n const epos = element?.getBoundingClientRect().top;\n if (\n epos !== undefined &&\n (epos + ypos < highestElement.ypos || highestElement.id === \"\")\n ) {\n highestElement.id = id;\n highestElement.ypos = ypos + epos;\n }\n });\n return highestElement.id;\n }\n\n calculateScroll(scrollableElements: Map<ScrollType, Set<string>>): string {\n let currentScroll = ScrollType.FirstAppearance;\n if (this.initialRender) {\n if (this.autoScrollOnLoad) {\n currentScroll = ScrollType.ValidationError;\n }\n\n this.initialRender = false;\n } else if (this.failedNavigation) {\n if (this.autoFocusOnErrorField) {\n currentScroll = ScrollType.ValidationError;\n }\n\n this.failedNavigation = false;\n }\n\n const elementList = scrollableElements.get(currentScroll);\n if (elementList) {\n const element = this.getFirstScrollableElement(\n elementList,\n currentScroll,\n );\n return element ?? \"\";\n }\n\n return \"\";\n }\n\n // Hooks into player flow to determine what scroll targets need to be evaluated at specific lifecycle points\n apply(player: Player): void {\n player.hooks.flowController.tap(this.name, (fc) => {\n fc.hooks.flow.tap(this.name, (flow) => {\n flow.hooks.transition.tap(this.name, () => {\n // Reset Everything\n this.initialRender = true;\n this.failedNavigation = false;\n this.alreadyScrolledTo = [];\n this.clearScrollableMap();\n // Cancel any in-flight polyfill smooth-scroll animation before resetting.\n window.dispatchEvent(new WheelEvent(\"wheel\", { bubbles: true }));\n window.scroll(0, 0);\n });\n flow.hooks.skipTransition.intercept({\n call: () => {\n this.failedNavigation = true;\n },\n });\n });\n });\n }\n\n applyReact(reactPlayer: ReactPlayer): void {\n reactPlayer.hooks.webComponent.tap(this.name, (Comp) => {\n const { scrollFn, getBaseElement, offset } = this;\n const setScrollableMapClearer = (clear: () => void) => {\n this.clearScrollableMap = clear;\n };\n\n function AutoScrollManagerComponent() {\n return (\n <AutoScrollProvider\n getElementToScrollTo={scrollFn}\n getBaseElement={getBaseElement}\n offset={offset}\n onMount={(clear) => {\n setScrollableMapClearer(clear);\n }}\n >\n <Comp />\n </AutoScrollProvider>\n );\n }\n\n return AutoScrollManagerComponent;\n });\n }\n}\n"],"mappings":"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","names":["React","import_react","ScrollType","React"]}
|
package/dist/index.legacy-esm.js
CHANGED
|
@@ -21,9 +21,13 @@ var AutoScrollProvider = ({
|
|
|
21
21
|
getElementToScrollTo,
|
|
22
22
|
getBaseElement,
|
|
23
23
|
offset,
|
|
24
|
+
onMount,
|
|
24
25
|
children
|
|
25
26
|
}) => {
|
|
26
27
|
const [scrollableMap, setScrollableMap] = useState(/* @__PURE__ */ new Map());
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
onMount?.(() => setScrollableMap(/* @__PURE__ */ new Map()));
|
|
30
|
+
}, []);
|
|
27
31
|
const updateScrollableMap = (key, value) => {
|
|
28
32
|
setScrollableMap((prev) => {
|
|
29
33
|
const nm = new Map(prev);
|
|
@@ -42,7 +46,7 @@ var AutoScrollProvider = ({
|
|
|
42
46
|
if (node) {
|
|
43
47
|
scrollIntoViewWithOffset(node, getBaseElement() || document.body, offset);
|
|
44
48
|
}
|
|
45
|
-
});
|
|
49
|
+
}, [scrollableMap]);
|
|
46
50
|
return /* @__PURE__ */ React.createElement(AutoScrollManagerContext.Provider, { value: { register } }, children);
|
|
47
51
|
};
|
|
48
52
|
|
|
@@ -64,6 +68,8 @@ var AutoScrollManagerPlugin = class {
|
|
|
64
68
|
this.initialRender = false;
|
|
65
69
|
this.failedNavigation = false;
|
|
66
70
|
this.alreadyScrolledTo = [];
|
|
71
|
+
this.clearScrollableMap = () => {
|
|
72
|
+
};
|
|
67
73
|
this.scrollFn = this.calculateScroll.bind(this);
|
|
68
74
|
}
|
|
69
75
|
getFirstScrollableElement(idList, type) {
|
|
@@ -122,6 +128,8 @@ var AutoScrollManagerPlugin = class {
|
|
|
122
128
|
this.initialRender = true;
|
|
123
129
|
this.failedNavigation = false;
|
|
124
130
|
this.alreadyScrolledTo = [];
|
|
131
|
+
this.clearScrollableMap();
|
|
132
|
+
window.dispatchEvent(new WheelEvent("wheel", { bubbles: true }));
|
|
125
133
|
window.scroll(0, 0);
|
|
126
134
|
});
|
|
127
135
|
flow.hooks.skipTransition.intercept({
|
|
@@ -135,13 +143,19 @@ var AutoScrollManagerPlugin = class {
|
|
|
135
143
|
applyReact(reactPlayer) {
|
|
136
144
|
reactPlayer.hooks.webComponent.tap(this.name, (Comp) => {
|
|
137
145
|
const { scrollFn, getBaseElement, offset } = this;
|
|
146
|
+
const setScrollableMapClearer = (clear) => {
|
|
147
|
+
this.clearScrollableMap = clear;
|
|
148
|
+
};
|
|
138
149
|
function AutoScrollManagerComponent() {
|
|
139
150
|
return /* @__PURE__ */ React2.createElement(
|
|
140
151
|
AutoScrollProvider,
|
|
141
152
|
{
|
|
142
153
|
getElementToScrollTo: scrollFn,
|
|
143
154
|
getBaseElement,
|
|
144
|
-
offset
|
|
155
|
+
offset,
|
|
156
|
+
onMount: (clear) => {
|
|
157
|
+
setScrollableMapClearer(clear);
|
|
158
|
+
}
|
|
145
159
|
},
|
|
146
160
|
/* @__PURE__ */ React2.createElement(Comp, null)
|
|
147
161
|
);
|
package/dist/index.mjs
CHANGED
|
@@ -21,9 +21,13 @@ var AutoScrollProvider = ({
|
|
|
21
21
|
getElementToScrollTo,
|
|
22
22
|
getBaseElement,
|
|
23
23
|
offset,
|
|
24
|
+
onMount,
|
|
24
25
|
children
|
|
25
26
|
}) => {
|
|
26
27
|
const [scrollableMap, setScrollableMap] = useState(/* @__PURE__ */ new Map());
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
onMount?.(() => setScrollableMap(/* @__PURE__ */ new Map()));
|
|
30
|
+
}, []);
|
|
27
31
|
const updateScrollableMap = (key, value) => {
|
|
28
32
|
setScrollableMap((prev) => {
|
|
29
33
|
const nm = new Map(prev);
|
|
@@ -42,7 +46,7 @@ var AutoScrollProvider = ({
|
|
|
42
46
|
if (node) {
|
|
43
47
|
scrollIntoViewWithOffset(node, getBaseElement() || document.body, offset);
|
|
44
48
|
}
|
|
45
|
-
});
|
|
49
|
+
}, [scrollableMap]);
|
|
46
50
|
return /* @__PURE__ */ React.createElement(AutoScrollManagerContext.Provider, { value: { register } }, children);
|
|
47
51
|
};
|
|
48
52
|
|
|
@@ -64,6 +68,8 @@ var AutoScrollManagerPlugin = class {
|
|
|
64
68
|
this.initialRender = false;
|
|
65
69
|
this.failedNavigation = false;
|
|
66
70
|
this.alreadyScrolledTo = [];
|
|
71
|
+
this.clearScrollableMap = () => {
|
|
72
|
+
};
|
|
67
73
|
this.scrollFn = this.calculateScroll.bind(this);
|
|
68
74
|
}
|
|
69
75
|
getFirstScrollableElement(idList, type) {
|
|
@@ -122,6 +128,8 @@ var AutoScrollManagerPlugin = class {
|
|
|
122
128
|
this.initialRender = true;
|
|
123
129
|
this.failedNavigation = false;
|
|
124
130
|
this.alreadyScrolledTo = [];
|
|
131
|
+
this.clearScrollableMap();
|
|
132
|
+
window.dispatchEvent(new WheelEvent("wheel", { bubbles: true }));
|
|
125
133
|
window.scroll(0, 0);
|
|
126
134
|
});
|
|
127
135
|
flow.hooks.skipTransition.intercept({
|
|
@@ -135,13 +143,19 @@ var AutoScrollManagerPlugin = class {
|
|
|
135
143
|
applyReact(reactPlayer) {
|
|
136
144
|
reactPlayer.hooks.webComponent.tap(this.name, (Comp) => {
|
|
137
145
|
const { scrollFn, getBaseElement, offset } = this;
|
|
146
|
+
const setScrollableMapClearer = (clear) => {
|
|
147
|
+
this.clearScrollableMap = clear;
|
|
148
|
+
};
|
|
138
149
|
function AutoScrollManagerComponent() {
|
|
139
150
|
return /* @__PURE__ */ React2.createElement(
|
|
140
151
|
AutoScrollProvider,
|
|
141
152
|
{
|
|
142
153
|
getElementToScrollTo: scrollFn,
|
|
143
154
|
getBaseElement,
|
|
144
|
-
offset
|
|
155
|
+
offset,
|
|
156
|
+
onMount: (clear) => {
|
|
157
|
+
setScrollableMapClearer(clear);
|
|
158
|
+
}
|
|
145
159
|
},
|
|
146
160
|
/* @__PURE__ */ React2.createElement(Comp, null)
|
|
147
161
|
);
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/plugins/auto-scroll/react/src/hooks.tsx","../../../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/plugins/auto-scroll/react/src/scrollIntoViewWithOffset.ts","../../../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/plugins/auto-scroll/react/src/plugin.tsx"],"sourcesContent":["import type { PropsWithChildren } from \"react\";\nimport React, { useEffect, useState } from \"react\";\nimport { scrollIntoViewWithOffset } from \"./scrollIntoViewWithOffset\";\nimport type { ScrollType } from \"./index\";\n\nexport interface AutoScrollProviderProps {\n /** Return the element to scroll to based on the registered types */\n getElementToScrollTo: (\n scrollableElements: Map<ScrollType, Set<string>>,\n ) => string;\n /** Optional function to get container element, which is used for calculating offset (default: document.body) */\n getBaseElement: () => HTMLElement | undefined | null;\n /** Additional offset to be used (default: 0) */\n offset: number;\n}\n\nexport interface RegisterData {\n /** when to scroll to the target */\n type: ScrollType;\n\n /** the html id to scroll to */\n ref: string;\n}\n\nexport type ScrollFunction = (registerData: RegisterData) => void;\n\nexport const AutoScrollManagerContext = React.createContext<{\n /** function to register a scroll target */\n register: ScrollFunction;\n}>({ register: () => {} });\n\n/** hook to register as a scroll target */\nexport const useRegisterAsScrollable = (): ScrollFunction => {\n const { register } = React.useContext(AutoScrollManagerContext);\n\n return register;\n};\n\n/** Component to handle scrolling */\nexport const AutoScrollProvider = ({\n getElementToScrollTo,\n getBaseElement,\n offset,\n children,\n}: PropsWithChildren<AutoScrollProviderProps>) => {\n // Tracker for what elements are registered to be scroll targets\n // Key is the type (initial, validation, appear)\n // Value is a set of target ids\n const [scrollableMap, setScrollableMap] = useState<\n Map<ScrollType, Set<string>>\n >(new Map());\n\n /** Add a new entry as a scroll target */\n const updateScrollableMap = (key: ScrollType, value: string) => {\n setScrollableMap((prev) => {\n const nm = new Map(prev);\n\n if (!nm.get(key)) {\n nm.set(key, new Set());\n }\n\n nm.get(key)?.add(value);\n\n return nm;\n });\n };\n\n /** register a new scroll target */\n const register: ScrollFunction = (data) => {\n updateScrollableMap(data.type, data.ref);\n };\n\n useEffect(() => {\n const node = document.getElementById(getElementToScrollTo(scrollableMap));\n\n if (node) {\n scrollIntoViewWithOffset(node, getBaseElement() || document.body, offset);\n }\n });\n\n return (\n <AutoScrollManagerContext.Provider value={{ register }}>\n {children}\n </AutoScrollManagerContext.Provider>\n );\n};\n","import { scrollTo } from \"seamless-scroll-polyfill\";\n\n/**\n * Scroll to the given element with an offset\n * @param node Element to scroll to\n * @param baseElement Container element used to calculate offset\n * @param offset Additional offset\n */\nexport function scrollIntoViewWithOffset(\n node: HTMLElement,\n baseElement: HTMLElement,\n offset: number,\n) {\n scrollTo(window, {\n behavior: \"smooth\",\n top:\n node.getBoundingClientRect().top -\n baseElement.getBoundingClientRect().top -\n offset,\n });\n}\n","import type { ReactPlayer, ReactPlayerPlugin } from \"@player-ui/react\";\nimport type { Player } from \"@player-ui/react\";\nimport React from \"react\";\nimport { AutoScrollProvider } from \"./hooks\";\n\nexport enum ScrollType {\n ValidationError,\n FirstAppearance,\n Unknown,\n}\n\nexport interface AutoScrollManagerConfig {\n /** Config to auto-scroll on load */\n autoScrollOnLoad?: boolean;\n /** Config to auto-focus on an error */\n autoFocusOnErrorField?: boolean;\n /** Optional function to get container element, which is used for calculating offset (default: document.body) */\n getBaseElement?: () => HTMLElement | undefined | null;\n /** Additional offset to be used (default: 0) */\n offset?: number;\n}\n\n/** A plugin to manage scrolling behavior */\nexport class AutoScrollManagerPlugin implements ReactPlayerPlugin {\n name = \"auto-scroll-manager\";\n\n /** Toggles if we should auto scroll to to the first failed validation on page load */\n private autoScrollOnLoad: boolean;\n\n /** Toggles if we should auto scroll to the first failed validation on navigation failure */\n private autoFocusOnErrorField: boolean;\n\n /** tracks if its the initial page render */\n private initialRender: boolean;\n\n /** tracks if the navigation failed */\n private failedNavigation: boolean;\n\n /** function to return the base of the scrollable area */\n private getBaseElement: () => HTMLElement | undefined | null;\n\n /** static offset */\n private offset: number;\n\n /** map of scroll type to set of ids that are registered under that type */\n private alreadyScrolledTo: Array<string>;\n private scrollFn: (\n scrollableElements: Map<ScrollType, Set<string>>,\n ) => string;\n\n constructor(config: AutoScrollManagerConfig) {\n this.autoScrollOnLoad = config.autoScrollOnLoad ?? false;\n this.autoFocusOnErrorField = config.autoFocusOnErrorField ?? false;\n this.getBaseElement = config.getBaseElement ?? (() => null);\n this.offset = config.offset ?? 0;\n this.initialRender = false;\n this.failedNavigation = false;\n this.alreadyScrolledTo = [];\n this.scrollFn = this.calculateScroll.bind(this);\n }\n\n getFirstScrollableElement(idList: Set<string>, type: ScrollType) {\n const highestElement = {\n id: \"\",\n ypos: 0,\n };\n const ypos = window.scrollY;\n idList.forEach((id) => {\n const element = document.getElementById(id);\n\n // if we are looking at validation errors, make sure the element is invalid\n if (\n type === ScrollType.ValidationError &&\n element?.getAttribute(\"aria-invalid\") === \"false\"\n ) {\n return;\n }\n\n // if we are just looking at elements that just appeared, make sure we haven't\n // scrolled to them before\n if (type === ScrollType.FirstAppearance) {\n if (this.alreadyScrolledTo.indexOf(id) !== -1) {\n return;\n }\n\n this.alreadyScrolledTo.push(id);\n }\n\n const epos = element?.getBoundingClientRect().top;\n if (\n epos !== undefined &&\n (epos + ypos < highestElement.ypos || highestElement.id === \"\")\n ) {\n highestElement.id = id;\n highestElement.ypos = ypos + epos;\n }\n });\n return highestElement.id;\n }\n\n calculateScroll(scrollableElements: Map<ScrollType, Set<string>>) {\n let currentScroll = ScrollType.FirstAppearance;\n if (this.initialRender) {\n if (this.autoScrollOnLoad) {\n currentScroll = ScrollType.ValidationError;\n }\n\n this.initialRender = false;\n } else if (this.failedNavigation) {\n if (this.autoFocusOnErrorField) {\n currentScroll = ScrollType.ValidationError;\n }\n\n this.failedNavigation = false;\n }\n\n const elementList = scrollableElements.get(currentScroll);\n if (elementList) {\n const element = this.getFirstScrollableElement(\n elementList,\n currentScroll,\n );\n return element ?? \"\";\n }\n\n return \"\";\n }\n\n // Hooks into player flow to determine what scroll targets need to be evaluated at specific lifecycle points\n apply(player: Player) {\n player.hooks.flowController.tap(this.name, (fc) => {\n fc.hooks.flow.tap(this.name, (flow) => {\n flow.hooks.transition.tap(this.name, () => {\n // Reset Everything\n this.initialRender = true;\n this.failedNavigation = false;\n this.alreadyScrolledTo = [];\n // Reset scroll position for new view\n window.scroll(0, 0);\n });\n flow.hooks.skipTransition.intercept({\n call: () => {\n this.failedNavigation = true;\n },\n });\n });\n });\n }\n\n applyReact(reactPlayer: ReactPlayer) {\n reactPlayer.hooks.webComponent.tap(this.name, (Comp) => {\n const { scrollFn, getBaseElement, offset } = this;\n\n function AutoScrollManagerComponent() {\n return (\n <AutoScrollProvider\n getElementToScrollTo={scrollFn}\n getBaseElement={getBaseElement}\n offset={offset}\n >\n <Comp />\n </AutoScrollProvider>\n );\n }\n\n return AutoScrollManagerComponent;\n });\n }\n}\n"],"mappings":"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","names":["React","ScrollType","React"]}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/plugins/auto-scroll/react/src/hooks.tsx","../../../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/plugins/auto-scroll/react/src/scrollIntoViewWithOffset.ts","../../../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/plugins/auto-scroll/react/src/plugin.tsx"],"sourcesContent":["import type { PropsWithChildren } from \"react\";\nimport React, { useEffect, useState } from \"react\";\nimport { scrollIntoViewWithOffset } from \"./scrollIntoViewWithOffset\";\nimport type { ScrollType } from \"./index\";\n\nexport interface AutoScrollProviderProps {\n /** Return the element to scroll to based on the registered types */\n getElementToScrollTo: (\n scrollableElements: Map<ScrollType, Set<string>>,\n ) => string;\n /** Optional function to get container element, which is used for calculating offset (default: document.body) */\n getBaseElement: () => HTMLElement | undefined | null;\n /** Additional offset to be used (default: 0) */\n offset: number;\n /** Called on mount with a function that clears all registered scroll targets */\n onMount?: (clearScrollableMap: () => void) => void;\n}\n\nexport interface RegisterData {\n /** when to scroll to the target */\n type: ScrollType;\n\n /** the html id to scroll to */\n ref: string;\n}\n\nexport type ScrollFunction = (registerData: RegisterData) => void;\n\nexport const AutoScrollManagerContext = React.createContext<{\n /** function to register a scroll target */\n register: ScrollFunction;\n}>({ register: () => {} });\n\n/** hook to register as a scroll target */\nexport const useRegisterAsScrollable = (): ScrollFunction => {\n const { register } = React.useContext(AutoScrollManagerContext);\n\n return register;\n};\n\n/** Component to handle scrolling */\nexport const AutoScrollProvider = ({\n getElementToScrollTo,\n getBaseElement,\n offset,\n onMount,\n children,\n}: PropsWithChildren<AutoScrollProviderProps>) => {\n // Tracker for what elements are registered to be scroll targets\n // Key is the type (initial, validation, appear)\n // Value is a set of target ids\n const [scrollableMap, setScrollableMap] = useState<\n Map<ScrollType, Set<string>>\n >(new Map());\n\n useEffect(() => {\n onMount?.(() => setScrollableMap(new Map()));\n }, []);\n\n /** Add a new entry as a scroll target */\n const updateScrollableMap = (key: ScrollType, value: string) => {\n setScrollableMap((prev) => {\n const nm = new Map(prev);\n\n if (!nm.get(key)) {\n nm.set(key, new Set());\n }\n\n nm.get(key)?.add(value);\n\n return nm;\n });\n };\n\n /** register a new scroll target */\n const register: ScrollFunction = (data) => {\n updateScrollableMap(data.type, data.ref);\n };\n\n useEffect(() => {\n const node = document.getElementById(getElementToScrollTo(scrollableMap));\n\n if (node) {\n scrollIntoViewWithOffset(node, getBaseElement() || document.body, offset);\n }\n }, [scrollableMap]);\n\n return (\n <AutoScrollManagerContext.Provider value={{ register }}>\n {children}\n </AutoScrollManagerContext.Provider>\n );\n};\n","import { scrollTo } from \"seamless-scroll-polyfill\";\n\n/**\n * Scroll to the given element with an offset\n * @param node Element to scroll to\n * @param baseElement Container element used to calculate offset\n * @param offset Additional offset\n */\nexport function scrollIntoViewWithOffset(\n node: HTMLElement,\n baseElement: HTMLElement,\n offset: number,\n) {\n scrollTo(window, {\n behavior: \"smooth\",\n top:\n node.getBoundingClientRect().top -\n baseElement.getBoundingClientRect().top -\n offset,\n });\n}\n","import type { ReactPlayer, ReactPlayerPlugin } from \"@player-ui/react\";\nimport type { Player } from \"@player-ui/react\";\nimport React from \"react\";\nimport { AutoScrollProvider } from \"./hooks\";\n\nexport enum ScrollType {\n ValidationError,\n FirstAppearance,\n Unknown,\n}\n\nexport interface AutoScrollManagerConfig {\n /** Config to auto-scroll on load */\n autoScrollOnLoad?: boolean;\n /** Config to auto-focus on an error */\n autoFocusOnErrorField?: boolean;\n /** Optional function to get container element, which is used for calculating offset (default: document.body) */\n getBaseElement?: () => HTMLElement | undefined | null;\n /** Additional offset to be used (default: 0) */\n offset?: number;\n}\n\n/** A plugin to manage scrolling behavior */\nexport class AutoScrollManagerPlugin implements ReactPlayerPlugin {\n name = \"auto-scroll-manager\";\n\n /** Toggles if we should auto scroll to to the first failed validation on page load */\n private autoScrollOnLoad: boolean;\n\n /** Toggles if we should auto scroll to the first failed validation on navigation failure */\n private autoFocusOnErrorField: boolean;\n\n /** tracks if its the initial page render */\n private initialRender: boolean;\n\n /** tracks if the navigation failed */\n private failedNavigation: boolean;\n\n /** function to return the base of the scrollable area */\n private getBaseElement: () => HTMLElement | undefined | null;\n\n /** static offset */\n private offset: number;\n\n /** clears scrollableMap in AutoScrollProvider — set when the provider mounts */\n private clearScrollableMap: () => void;\n\n /** map of scroll type to set of ids that are registered under that type */\n private alreadyScrolledTo: Array<string>;\n private scrollFn: (\n scrollableElements: Map<ScrollType, Set<string>>,\n ) => string;\n\n constructor(config: AutoScrollManagerConfig) {\n this.autoScrollOnLoad = config.autoScrollOnLoad ?? false;\n this.autoFocusOnErrorField = config.autoFocusOnErrorField ?? false;\n this.getBaseElement = config.getBaseElement ?? (() => null);\n this.offset = config.offset ?? 0;\n this.initialRender = false;\n this.failedNavigation = false;\n this.alreadyScrolledTo = [];\n this.clearScrollableMap = () => {};\n this.scrollFn = this.calculateScroll.bind(this);\n }\n\n getFirstScrollableElement(idList: Set<string>, type: ScrollType): string {\n const highestElement = {\n id: \"\",\n ypos: 0,\n };\n const ypos = window.scrollY;\n idList.forEach((id) => {\n const element = document.getElementById(id);\n\n // if we are looking at validation errors, make sure the element is invalid\n if (\n type === ScrollType.ValidationError &&\n element?.getAttribute(\"aria-invalid\") === \"false\"\n ) {\n return;\n }\n\n // if we are just looking at elements that just appeared, make sure we haven't\n // scrolled to them before\n if (type === ScrollType.FirstAppearance) {\n if (this.alreadyScrolledTo.indexOf(id) !== -1) {\n return;\n }\n\n this.alreadyScrolledTo.push(id);\n }\n\n const epos = element?.getBoundingClientRect().top;\n if (\n epos !== undefined &&\n (epos + ypos < highestElement.ypos || highestElement.id === \"\")\n ) {\n highestElement.id = id;\n highestElement.ypos = ypos + epos;\n }\n });\n return highestElement.id;\n }\n\n calculateScroll(scrollableElements: Map<ScrollType, Set<string>>): string {\n let currentScroll = ScrollType.FirstAppearance;\n if (this.initialRender) {\n if (this.autoScrollOnLoad) {\n currentScroll = ScrollType.ValidationError;\n }\n\n this.initialRender = false;\n } else if (this.failedNavigation) {\n if (this.autoFocusOnErrorField) {\n currentScroll = ScrollType.ValidationError;\n }\n\n this.failedNavigation = false;\n }\n\n const elementList = scrollableElements.get(currentScroll);\n if (elementList) {\n const element = this.getFirstScrollableElement(\n elementList,\n currentScroll,\n );\n return element ?? \"\";\n }\n\n return \"\";\n }\n\n // Hooks into player flow to determine what scroll targets need to be evaluated at specific lifecycle points\n apply(player: Player): void {\n player.hooks.flowController.tap(this.name, (fc) => {\n fc.hooks.flow.tap(this.name, (flow) => {\n flow.hooks.transition.tap(this.name, () => {\n // Reset Everything\n this.initialRender = true;\n this.failedNavigation = false;\n this.alreadyScrolledTo = [];\n this.clearScrollableMap();\n // Cancel any in-flight polyfill smooth-scroll animation before resetting.\n window.dispatchEvent(new WheelEvent(\"wheel\", { bubbles: true }));\n window.scroll(0, 0);\n });\n flow.hooks.skipTransition.intercept({\n call: () => {\n this.failedNavigation = true;\n },\n });\n });\n });\n }\n\n applyReact(reactPlayer: ReactPlayer): void {\n reactPlayer.hooks.webComponent.tap(this.name, (Comp) => {\n const { scrollFn, getBaseElement, offset } = this;\n const setScrollableMapClearer = (clear: () => void) => {\n this.clearScrollableMap = clear;\n };\n\n function AutoScrollManagerComponent() {\n return (\n <AutoScrollProvider\n getElementToScrollTo={scrollFn}\n getBaseElement={getBaseElement}\n offset={offset}\n onMount={(clear) => {\n setScrollableMapClearer(clear);\n }}\n >\n <Comp />\n </AutoScrollProvider>\n );\n }\n\n return AutoScrollManagerComponent;\n });\n }\n}\n"],"mappings":";AACA,OAAO,SAAS,WAAW,gBAAgB;;;ACD3C,SAAS,gBAAgB;AAQlB,SAAS,yBACd,MACA,aACA,QACA;AACA,WAAS,QAAQ;AAAA,IACf,UAAU;AAAA,IACV,KACE,KAAK,sBAAsB,EAAE,MAC7B,YAAY,sBAAsB,EAAE,MACpC;AAAA,EACJ,CAAC;AACH;;;ADQO,IAAM,2BAA2B,MAAM,cAG3C,EAAE,UAAU,MAAM;AAAC,EAAE,CAAC;AAGlB,IAAM,0BAA0B,MAAsB;AAC3D,QAAM,EAAE,SAAS,IAAI,MAAM,WAAW,wBAAwB;AAE9D,SAAO;AACT;AAGO,IAAM,qBAAqB,CAAC;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,MAAkD;AAIhD,QAAM,CAAC,eAAe,gBAAgB,IAAI,SAExC,oBAAI,IAAI,CAAC;AAEX,YAAU,MAAM;AACd,cAAU,MAAM,iBAAiB,oBAAI,IAAI,CAAC,CAAC;AAAA,EAC7C,GAAG,CAAC,CAAC;AAGL,QAAM,sBAAsB,CAAC,KAAiB,UAAkB;AAC9D,qBAAiB,CAAC,SAAS;AACzB,YAAM,KAAK,IAAI,IAAI,IAAI;AAEvB,UAAI,CAAC,GAAG,IAAI,GAAG,GAAG;AAChB,WAAG,IAAI,KAAK,oBAAI,IAAI,CAAC;AAAA,MACvB;AAEA,SAAG,IAAI,GAAG,GAAG,IAAI,KAAK;AAEtB,aAAO;AAAA,IACT,CAAC;AAAA,EACH;AAGA,QAAM,WAA2B,CAAC,SAAS;AACzC,wBAAoB,KAAK,MAAM,KAAK,GAAG;AAAA,EACzC;AAEA,YAAU,MAAM;AACd,UAAM,OAAO,SAAS,eAAe,qBAAqB,aAAa,CAAC;AAExE,QAAI,MAAM;AACR,+BAAyB,MAAM,eAAe,KAAK,SAAS,MAAM,MAAM;AAAA,IAC1E;AAAA,EACF,GAAG,CAAC,aAAa,CAAC;AAElB,SACE,oCAAC,yBAAyB,UAAzB,EAAkC,OAAO,EAAE,SAAS,KAClD,QACH;AAEJ;;;AE1FA,OAAOA,YAAW;AAGX,IAAK,aAAL,kBAAKC,gBAAL;AACL,EAAAA,wBAAA;AACA,EAAAA,wBAAA;AACA,EAAAA,wBAAA;AAHU,SAAAA;AAAA,GAAA;AAkBL,IAAM,0BAAN,MAA2D;AAAA,EA8BhE,YAAY,QAAiC;AA7B7C,gBAAO;AA8BL,SAAK,mBAAmB,OAAO,oBAAoB;AACnD,SAAK,wBAAwB,OAAO,yBAAyB;AAC7D,SAAK,iBAAiB,OAAO,mBAAmB,MAAM;AACtD,SAAK,SAAS,OAAO,UAAU;AAC/B,SAAK,gBAAgB;AACrB,SAAK,mBAAmB;AACxB,SAAK,oBAAoB,CAAC;AAC1B,SAAK,qBAAqB,MAAM;AAAA,IAAC;AACjC,SAAK,WAAW,KAAK,gBAAgB,KAAK,IAAI;AAAA,EAChD;AAAA,EAEA,0BAA0B,QAAqB,MAA0B;AACvE,UAAM,iBAAiB;AAAA,MACrB,IAAI;AAAA,MACJ,MAAM;AAAA,IACR;AACA,UAAM,OAAO,OAAO;AACpB,WAAO,QAAQ,CAAC,OAAO;AACrB,YAAM,UAAU,SAAS,eAAe,EAAE;AAG1C,UACE,SAAS,2BACT,SAAS,aAAa,cAAc,MAAM,SAC1C;AACA;AAAA,MACF;AAIA,UAAI,SAAS,yBAA4B;AACvC,YAAI,KAAK,kBAAkB,QAAQ,EAAE,MAAM,IAAI;AAC7C;AAAA,QACF;AAEA,aAAK,kBAAkB,KAAK,EAAE;AAAA,MAChC;AAEA,YAAM,OAAO,SAAS,sBAAsB,EAAE;AAC9C,UACE,SAAS,WACR,OAAO,OAAO,eAAe,QAAQ,eAAe,OAAO,KAC5D;AACA,uBAAe,KAAK;AACpB,uBAAe,OAAO,OAAO;AAAA,MAC/B;AAAA,IACF,CAAC;AACD,WAAO,eAAe;AAAA,EACxB;AAAA,EAEA,gBAAgB,oBAA0D;AACxE,QAAI,gBAAgB;AACpB,QAAI,KAAK,eAAe;AACtB,UAAI,KAAK,kBAAkB;AACzB,wBAAgB;AAAA,MAClB;AAEA,WAAK,gBAAgB;AAAA,IACvB,WAAW,KAAK,kBAAkB;AAChC,UAAI,KAAK,uBAAuB;AAC9B,wBAAgB;AAAA,MAClB;AAEA,WAAK,mBAAmB;AAAA,IAC1B;AAEA,UAAM,cAAc,mBAAmB,IAAI,aAAa;AACxD,QAAI,aAAa;AACf,YAAM,UAAU,KAAK;AAAA,QACnB;AAAA,QACA;AAAA,MACF;AACA,aAAO,WAAW;AAAA,IACpB;AAEA,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,MAAM,QAAsB;AAC1B,WAAO,MAAM,eAAe,IAAI,KAAK,MAAM,CAAC,OAAO;AACjD,SAAG,MAAM,KAAK,IAAI,KAAK,MAAM,CAAC,SAAS;AACrC,aAAK,MAAM,WAAW,IAAI,KAAK,MAAM,MAAM;AAEzC,eAAK,gBAAgB;AACrB,eAAK,mBAAmB;AACxB,eAAK,oBAAoB,CAAC;AAC1B,eAAK,mBAAmB;AAExB,iBAAO,cAAc,IAAI,WAAW,SAAS,EAAE,SAAS,KAAK,CAAC,CAAC;AAC/D,iBAAO,OAAO,GAAG,CAAC;AAAA,QACpB,CAAC;AACD,aAAK,MAAM,eAAe,UAAU;AAAA,UAClC,MAAM,MAAM;AACV,iBAAK,mBAAmB;AAAA,UAC1B;AAAA,QACF,CAAC;AAAA,MACH,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA,EAEA,WAAW,aAAgC;AACzC,gBAAY,MAAM,aAAa,IAAI,KAAK,MAAM,CAAC,SAAS;AACtD,YAAM,EAAE,UAAU,gBAAgB,OAAO,IAAI;AAC7C,YAAM,0BAA0B,CAAC,UAAsB;AACrD,aAAK,qBAAqB;AAAA,MAC5B;AAEA,eAAS,6BAA6B;AACpC,eACE,gBAAAC,OAAA;AAAA,UAAC;AAAA;AAAA,YACC,sBAAsB;AAAA,YACtB;AAAA,YACA;AAAA,YACA,SAAS,CAAC,UAAU;AAClB,sCAAwB,KAAK;AAAA,YAC/B;AAAA;AAAA,UAEA,gBAAAA,OAAA,cAAC,UAAK;AAAA,QACR;AAAA,MAEJ;AAEA,aAAO;AAAA,IACT,CAAC;AAAA,EACH;AACF;","names":["React","ScrollType","React"]}
|
package/package.json
CHANGED
|
@@ -6,18 +6,18 @@
|
|
|
6
6
|
"types"
|
|
7
7
|
],
|
|
8
8
|
"name": "@player-ui/auto-scroll-manager-plugin-react",
|
|
9
|
-
"version": "0.16.0--canary.
|
|
9
|
+
"version": "0.16.0--canary.866.36538",
|
|
10
10
|
"main": "dist/cjs/index.cjs",
|
|
11
11
|
"devDependencies": {
|
|
12
|
-
"@player-ui/types": "workspace
|
|
13
|
-
"@player-ui/make-flow": "workspace
|
|
14
|
-
"@player-ui/asset-transform-plugin": "workspace
|
|
15
|
-
"@player-ui/common-types-plugin": "workspace
|
|
16
|
-
"@player-ui/reference-assets-plugin-react": "workspace
|
|
17
|
-
"@player-ui/reference-assets-plugin": "workspace
|
|
12
|
+
"@player-ui/types": "workspace:^",
|
|
13
|
+
"@player-ui/make-flow": "workspace:^",
|
|
14
|
+
"@player-ui/asset-transform-plugin": "workspace:^",
|
|
15
|
+
"@player-ui/common-types-plugin": "workspace:^",
|
|
16
|
+
"@player-ui/reference-assets-plugin-react": "workspace:^",
|
|
17
|
+
"@player-ui/reference-assets-plugin": "workspace:^"
|
|
18
18
|
},
|
|
19
19
|
"peerDependencies": {
|
|
20
|
-
"@player-ui/react": "0.16.0--canary.
|
|
20
|
+
"@player-ui/react": "0.16.0--canary.866.36538",
|
|
21
21
|
"react": "^18.2.0",
|
|
22
22
|
"@types/react": "^18.2.39"
|
|
23
23
|
},
|
|
@@ -177,24 +177,24 @@ describe("auto-scroll plugin", () => {
|
|
|
177
177
|
|
|
178
178
|
const action = await findByRole(container, "button");
|
|
179
179
|
act(() => action.click());
|
|
180
|
-
await
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
180
|
+
await waitFor(() =>
|
|
181
|
+
expect(scrollIntoViewWithOffset).toBeCalledWith(
|
|
182
|
+
expect.anything(),
|
|
183
|
+
expect.objectContaining({ id: "view" }),
|
|
184
|
+
40,
|
|
185
|
+
),
|
|
186
186
|
);
|
|
187
187
|
|
|
188
188
|
// Mock the case where the base element can't be found, so document.body is used as a fallback
|
|
189
189
|
getBaseElementMock.mockReturnValue(null);
|
|
190
190
|
|
|
191
191
|
act(() => action.click());
|
|
192
|
-
await
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
192
|
+
await waitFor(() =>
|
|
193
|
+
expect(scrollIntoViewWithOffset).toHaveBeenLastCalledWith(
|
|
194
|
+
expect.anything(),
|
|
195
|
+
document.body,
|
|
196
|
+
40,
|
|
197
|
+
),
|
|
198
198
|
);
|
|
199
199
|
});
|
|
200
200
|
|
|
@@ -233,12 +233,12 @@ describe("auto-scroll plugin", () => {
|
|
|
233
233
|
|
|
234
234
|
const action = await findByRole(container, "button");
|
|
235
235
|
act(() => action.click());
|
|
236
|
-
await
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
236
|
+
await waitFor(() =>
|
|
237
|
+
expect(scrollIntoViewWithOffset).toBeCalledWith(
|
|
238
|
+
expect.anything(),
|
|
239
|
+
document.body,
|
|
240
|
+
0,
|
|
241
|
+
),
|
|
242
242
|
);
|
|
243
243
|
});
|
|
244
244
|
|
|
@@ -280,6 +280,170 @@ describe("auto-scroll plugin", () => {
|
|
|
280
280
|
});
|
|
281
281
|
});
|
|
282
282
|
|
|
283
|
+
const twoViewFlow = {
|
|
284
|
+
id: "two-view-flow",
|
|
285
|
+
views: [
|
|
286
|
+
{
|
|
287
|
+
id: "view-1",
|
|
288
|
+
type: "info",
|
|
289
|
+
primaryInfo: {
|
|
290
|
+
asset: { id: "info-1", type: "input", binding: "person.name" },
|
|
291
|
+
},
|
|
292
|
+
actions: [
|
|
293
|
+
{ asset: { id: "next-action", type: "action", value: "Next" } },
|
|
294
|
+
],
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
id: "view-2",
|
|
298
|
+
type: "info",
|
|
299
|
+
primaryInfo: {
|
|
300
|
+
asset: { id: "info-2", type: "input", binding: "person.age" },
|
|
301
|
+
},
|
|
302
|
+
actions: [],
|
|
303
|
+
},
|
|
304
|
+
],
|
|
305
|
+
schema: {},
|
|
306
|
+
data: { person: { name: "sam", age: "30" } },
|
|
307
|
+
navigation: {
|
|
308
|
+
BEGIN: "FLOW_Start",
|
|
309
|
+
FLOW_Start: {
|
|
310
|
+
startState: "VIEW_1",
|
|
311
|
+
VIEW_1: {
|
|
312
|
+
state_type: "VIEW",
|
|
313
|
+
ref: "view-1",
|
|
314
|
+
transitions: { "*": "VIEW_2" },
|
|
315
|
+
},
|
|
316
|
+
VIEW_2: {
|
|
317
|
+
state_type: "VIEW",
|
|
318
|
+
ref: "view-2",
|
|
319
|
+
transitions: { "*": "END_Done" },
|
|
320
|
+
},
|
|
321
|
+
END_Done: { state_type: "END", outcome: "done" },
|
|
322
|
+
},
|
|
323
|
+
},
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
describe("scroll reset on view transition", () => {
|
|
327
|
+
test("does not scroll to page 1 elements after view transition (stale scrollableMap)", async () => {
|
|
328
|
+
vitest.clearAllMocks();
|
|
329
|
+
|
|
330
|
+
const withFirstAppearance = (Component: ComponentType<any>) => {
|
|
331
|
+
const Scrollable = (props: any) => {
|
|
332
|
+
const register = useRegisterAsScrollable();
|
|
333
|
+
useLayoutEffect(() => {
|
|
334
|
+
register({ type: ScrollType.FirstAppearance, ref: props.id });
|
|
335
|
+
}, []);
|
|
336
|
+
return <Component {...props} />;
|
|
337
|
+
};
|
|
338
|
+
return Scrollable;
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
// only "info-1" (page 1) is findable. if the map isn't cleared on transition,
|
|
342
|
+
// "info-1" remains a scroll target and scrollIntoViewWithOffset gets called.
|
|
343
|
+
vitest.spyOn(document, "getElementById").mockImplementation((id) => {
|
|
344
|
+
if (id === "info-1") {
|
|
345
|
+
return { getBoundingClientRect: () => ({ top: 50 }) } as any;
|
|
346
|
+
}
|
|
347
|
+
return null;
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
const wp = new ReactPlayer({
|
|
351
|
+
plugins: [
|
|
352
|
+
new AssetTransformPlugin([
|
|
353
|
+
[{ type: "action" }, actionTransform],
|
|
354
|
+
[{ type: "input" }, inputTransform],
|
|
355
|
+
[{ type: "info" }, infoTransform],
|
|
356
|
+
]),
|
|
357
|
+
new CommonTypesPlugin(),
|
|
358
|
+
new AutoScrollManagerPlugin({}),
|
|
359
|
+
],
|
|
360
|
+
});
|
|
361
|
+
wp.assetRegistry.set({ type: "info" }, Info);
|
|
362
|
+
wp.assetRegistry.set({ type: "action" }, Action);
|
|
363
|
+
wp.assetRegistry.set({ type: "input" }, withFirstAppearance(Input));
|
|
364
|
+
|
|
365
|
+
wp.start(twoViewFlow as any);
|
|
366
|
+
|
|
367
|
+
const { container } = render(
|
|
368
|
+
<div>
|
|
369
|
+
<React.Suspense fallback="loading...">
|
|
370
|
+
<wp.Component />
|
|
371
|
+
</React.Suspense>
|
|
372
|
+
</div>,
|
|
373
|
+
);
|
|
374
|
+
|
|
375
|
+
// wait for page 1 to settle, then reset scroll call history so we only assert on page 2
|
|
376
|
+
await act(() => waitFor(() => {}));
|
|
377
|
+
(scrollIntoViewWithOffset as ReturnType<typeof vitest.fn>).mockClear();
|
|
378
|
+
|
|
379
|
+
// navigate to page 2
|
|
380
|
+
const action = await findByRole(container, "button");
|
|
381
|
+
act(() => action.click());
|
|
382
|
+
await act(() => waitFor(() => {}));
|
|
383
|
+
|
|
384
|
+
// map was cleared on transition — "info-1" is no longer a scroll target
|
|
385
|
+
expect(scrollIntoViewWithOffset).not.toHaveBeenCalled();
|
|
386
|
+
vitest.restoreAllMocks();
|
|
387
|
+
});
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
describe("scroll reset on view transition cancels in-flight polyfill animation", () => {
|
|
391
|
+
test("dispatches a wheel event and calls scroll(0,0) on transition", async () => {
|
|
392
|
+
vitest.clearAllMocks();
|
|
393
|
+
|
|
394
|
+
const dispatchSpy = vitest
|
|
395
|
+
.spyOn(window, "dispatchEvent")
|
|
396
|
+
.mockImplementation(() => true);
|
|
397
|
+
const scrollSpy = vitest
|
|
398
|
+
.spyOn(window, "scroll")
|
|
399
|
+
.mockImplementation(() => {});
|
|
400
|
+
|
|
401
|
+
vitest.spyOn(document, "getElementById").mockReturnValue(null);
|
|
402
|
+
|
|
403
|
+
const wp = new ReactPlayer({
|
|
404
|
+
plugins: [
|
|
405
|
+
new AssetTransformPlugin([
|
|
406
|
+
[{ type: "action" }, actionTransform],
|
|
407
|
+
[{ type: "input" }, inputTransform],
|
|
408
|
+
[{ type: "info" }, infoTransform],
|
|
409
|
+
]),
|
|
410
|
+
new CommonTypesPlugin(),
|
|
411
|
+
new AutoScrollManagerPlugin({}),
|
|
412
|
+
],
|
|
413
|
+
});
|
|
414
|
+
wp.assetRegistry.set({ type: "info" }, Info);
|
|
415
|
+
wp.assetRegistry.set({ type: "action" }, Action);
|
|
416
|
+
wp.assetRegistry.set({ type: "input" }, Input);
|
|
417
|
+
|
|
418
|
+
wp.start(twoViewFlow as any);
|
|
419
|
+
|
|
420
|
+
const { container } = render(
|
|
421
|
+
<div>
|
|
422
|
+
<React.Suspense fallback="loading...">
|
|
423
|
+
<wp.Component />
|
|
424
|
+
</React.Suspense>
|
|
425
|
+
</div>,
|
|
426
|
+
);
|
|
427
|
+
|
|
428
|
+
await act(() => waitFor(() => {}));
|
|
429
|
+
|
|
430
|
+
// navigate to page 2 — triggers the transition hook
|
|
431
|
+
const action = await findByRole(container, "button");
|
|
432
|
+
act(() => action.click());
|
|
433
|
+
await act(() => waitFor(() => {}));
|
|
434
|
+
|
|
435
|
+
// wheel event must have been dispatched to cancel the polyfill's RAF
|
|
436
|
+
expect(dispatchSpy).toHaveBeenCalledWith(
|
|
437
|
+
expect.objectContaining({ type: "wheel" }),
|
|
438
|
+
);
|
|
439
|
+
|
|
440
|
+
// scroll must be reset to top
|
|
441
|
+
expect(scrollSpy).toHaveBeenCalledWith(0, 0);
|
|
442
|
+
|
|
443
|
+
vitest.restoreAllMocks();
|
|
444
|
+
});
|
|
445
|
+
});
|
|
446
|
+
|
|
283
447
|
describe("getFirstScrollableElement unit tests", () => {
|
|
284
448
|
const defineGetElementId = (bcrValues: any[]) => {
|
|
285
449
|
return (idIn: string): HTMLElement | null => {
|
package/src/hooks.tsx
CHANGED
|
@@ -12,6 +12,8 @@ export interface AutoScrollProviderProps {
|
|
|
12
12
|
getBaseElement: () => HTMLElement | undefined | null;
|
|
13
13
|
/** Additional offset to be used (default: 0) */
|
|
14
14
|
offset: number;
|
|
15
|
+
/** Called on mount with a function that clears all registered scroll targets */
|
|
16
|
+
onMount?: (clearScrollableMap: () => void) => void;
|
|
15
17
|
}
|
|
16
18
|
|
|
17
19
|
export interface RegisterData {
|
|
@@ -41,6 +43,7 @@ export const AutoScrollProvider = ({
|
|
|
41
43
|
getElementToScrollTo,
|
|
42
44
|
getBaseElement,
|
|
43
45
|
offset,
|
|
46
|
+
onMount,
|
|
44
47
|
children,
|
|
45
48
|
}: PropsWithChildren<AutoScrollProviderProps>) => {
|
|
46
49
|
// Tracker for what elements are registered to be scroll targets
|
|
@@ -50,6 +53,10 @@ export const AutoScrollProvider = ({
|
|
|
50
53
|
Map<ScrollType, Set<string>>
|
|
51
54
|
>(new Map());
|
|
52
55
|
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
onMount?.(() => setScrollableMap(new Map()));
|
|
58
|
+
}, []);
|
|
59
|
+
|
|
53
60
|
/** Add a new entry as a scroll target */
|
|
54
61
|
const updateScrollableMap = (key: ScrollType, value: string) => {
|
|
55
62
|
setScrollableMap((prev) => {
|
|
@@ -76,7 +83,7 @@ export const AutoScrollProvider = ({
|
|
|
76
83
|
if (node) {
|
|
77
84
|
scrollIntoViewWithOffset(node, getBaseElement() || document.body, offset);
|
|
78
85
|
}
|
|
79
|
-
});
|
|
86
|
+
}, [scrollableMap]);
|
|
80
87
|
|
|
81
88
|
return (
|
|
82
89
|
<AutoScrollManagerContext.Provider value={{ register }}>
|
package/src/plugin.tsx
CHANGED
|
@@ -42,6 +42,9 @@ export class AutoScrollManagerPlugin implements ReactPlayerPlugin {
|
|
|
42
42
|
/** static offset */
|
|
43
43
|
private offset: number;
|
|
44
44
|
|
|
45
|
+
/** clears scrollableMap in AutoScrollProvider — set when the provider mounts */
|
|
46
|
+
private clearScrollableMap: () => void;
|
|
47
|
+
|
|
45
48
|
/** map of scroll type to set of ids that are registered under that type */
|
|
46
49
|
private alreadyScrolledTo: Array<string>;
|
|
47
50
|
private scrollFn: (
|
|
@@ -56,10 +59,11 @@ export class AutoScrollManagerPlugin implements ReactPlayerPlugin {
|
|
|
56
59
|
this.initialRender = false;
|
|
57
60
|
this.failedNavigation = false;
|
|
58
61
|
this.alreadyScrolledTo = [];
|
|
62
|
+
this.clearScrollableMap = () => {};
|
|
59
63
|
this.scrollFn = this.calculateScroll.bind(this);
|
|
60
64
|
}
|
|
61
65
|
|
|
62
|
-
getFirstScrollableElement(idList: Set<string>, type: ScrollType) {
|
|
66
|
+
getFirstScrollableElement(idList: Set<string>, type: ScrollType): string {
|
|
63
67
|
const highestElement = {
|
|
64
68
|
id: "",
|
|
65
69
|
ypos: 0,
|
|
@@ -98,7 +102,7 @@ export class AutoScrollManagerPlugin implements ReactPlayerPlugin {
|
|
|
98
102
|
return highestElement.id;
|
|
99
103
|
}
|
|
100
104
|
|
|
101
|
-
calculateScroll(scrollableElements: Map<ScrollType, Set<string>>) {
|
|
105
|
+
calculateScroll(scrollableElements: Map<ScrollType, Set<string>>): string {
|
|
102
106
|
let currentScroll = ScrollType.FirstAppearance;
|
|
103
107
|
if (this.initialRender) {
|
|
104
108
|
if (this.autoScrollOnLoad) {
|
|
@@ -127,7 +131,7 @@ export class AutoScrollManagerPlugin implements ReactPlayerPlugin {
|
|
|
127
131
|
}
|
|
128
132
|
|
|
129
133
|
// Hooks into player flow to determine what scroll targets need to be evaluated at specific lifecycle points
|
|
130
|
-
apply(player: Player) {
|
|
134
|
+
apply(player: Player): void {
|
|
131
135
|
player.hooks.flowController.tap(this.name, (fc) => {
|
|
132
136
|
fc.hooks.flow.tap(this.name, (flow) => {
|
|
133
137
|
flow.hooks.transition.tap(this.name, () => {
|
|
@@ -135,7 +139,9 @@ export class AutoScrollManagerPlugin implements ReactPlayerPlugin {
|
|
|
135
139
|
this.initialRender = true;
|
|
136
140
|
this.failedNavigation = false;
|
|
137
141
|
this.alreadyScrolledTo = [];
|
|
138
|
-
|
|
142
|
+
this.clearScrollableMap();
|
|
143
|
+
// Cancel any in-flight polyfill smooth-scroll animation before resetting.
|
|
144
|
+
window.dispatchEvent(new WheelEvent("wheel", { bubbles: true }));
|
|
139
145
|
window.scroll(0, 0);
|
|
140
146
|
});
|
|
141
147
|
flow.hooks.skipTransition.intercept({
|
|
@@ -147,9 +153,12 @@ export class AutoScrollManagerPlugin implements ReactPlayerPlugin {
|
|
|
147
153
|
});
|
|
148
154
|
}
|
|
149
155
|
|
|
150
|
-
applyReact(reactPlayer: ReactPlayer) {
|
|
156
|
+
applyReact(reactPlayer: ReactPlayer): void {
|
|
151
157
|
reactPlayer.hooks.webComponent.tap(this.name, (Comp) => {
|
|
152
158
|
const { scrollFn, getBaseElement, offset } = this;
|
|
159
|
+
const setScrollableMapClearer = (clear: () => void) => {
|
|
160
|
+
this.clearScrollableMap = clear;
|
|
161
|
+
};
|
|
153
162
|
|
|
154
163
|
function AutoScrollManagerComponent() {
|
|
155
164
|
return (
|
|
@@ -157,6 +166,9 @@ export class AutoScrollManagerPlugin implements ReactPlayerPlugin {
|
|
|
157
166
|
getElementToScrollTo={scrollFn}
|
|
158
167
|
getBaseElement={getBaseElement}
|
|
159
168
|
offset={offset}
|
|
169
|
+
onMount={(clear) => {
|
|
170
|
+
setScrollableMapClearer(clear);
|
|
171
|
+
}}
|
|
160
172
|
>
|
|
161
173
|
<Comp />
|
|
162
174
|
</AutoScrollProvider>
|
package/types/hooks.d.ts
CHANGED
|
@@ -8,6 +8,8 @@ export interface AutoScrollProviderProps {
|
|
|
8
8
|
getBaseElement: () => HTMLElement | undefined | null;
|
|
9
9
|
/** Additional offset to be used (default: 0) */
|
|
10
10
|
offset: number;
|
|
11
|
+
/** Called on mount with a function that clears all registered scroll targets */
|
|
12
|
+
onMount?: (clearScrollableMap: () => void) => void;
|
|
11
13
|
}
|
|
12
14
|
export interface RegisterData {
|
|
13
15
|
/** when to scroll to the target */
|
|
@@ -23,5 +25,5 @@ export declare const AutoScrollManagerContext: React.Context<{
|
|
|
23
25
|
/** hook to register as a scroll target */
|
|
24
26
|
export declare const useRegisterAsScrollable: () => ScrollFunction;
|
|
25
27
|
/** Component to handle scrolling */
|
|
26
|
-
export declare const AutoScrollProvider: ({ getElementToScrollTo, getBaseElement, offset, children, }: PropsWithChildren<AutoScrollProviderProps>) => React.JSX.Element;
|
|
28
|
+
export declare const AutoScrollProvider: ({ getElementToScrollTo, getBaseElement, offset, onMount, children, }: PropsWithChildren<AutoScrollProviderProps>) => React.JSX.Element;
|
|
27
29
|
//# sourceMappingURL=hooks.d.ts.map
|
package/types/plugin.d.ts
CHANGED
|
@@ -30,6 +30,8 @@ export declare class AutoScrollManagerPlugin implements ReactPlayerPlugin {
|
|
|
30
30
|
private getBaseElement;
|
|
31
31
|
/** static offset */
|
|
32
32
|
private offset;
|
|
33
|
+
/** clears scrollableMap in AutoScrollProvider — set when the provider mounts */
|
|
34
|
+
private clearScrollableMap;
|
|
33
35
|
/** map of scroll type to set of ids that are registered under that type */
|
|
34
36
|
private alreadyScrolledTo;
|
|
35
37
|
private scrollFn;
|