@player-ui/auto-scroll-manager-plugin-react 0.3.1-next.0 → 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.js CHANGED
@@ -3,12 +3,18 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var React = require('react');
6
- var scrollIntoView = require('smooth-scroll-into-view-if-needed');
6
+ var seamlessScrollPolyfill = require('seamless-scroll-polyfill');
7
7
 
8
8
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
9
9
 
10
10
  var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
11
- var scrollIntoView__default = /*#__PURE__*/_interopDefaultLegacy(scrollIntoView);
11
+
12
+ var scrollIntoViewWithOffset = (node, baseElement, offset) => {
13
+ seamlessScrollPolyfill.scrollTo(window, {
14
+ behavior: "smooth",
15
+ top: node.getBoundingClientRect().top - baseElement.getBoundingClientRect().top - offset
16
+ });
17
+ };
12
18
 
13
19
  const AutoScrollManagerContext = React__default["default"].createContext({ register: () => {
14
20
  } });
@@ -18,6 +24,8 @@ const useRegisterAsScrollable = () => {
18
24
  };
19
25
  const AutoScrollProvider = ({
20
26
  getElementToScrollTo,
27
+ getBaseElement,
28
+ offset,
21
29
  children
22
30
  }) => {
23
31
  const [scrollableMap, setScrollableMap] = React.useState(new Map());
@@ -38,10 +46,7 @@ const AutoScrollProvider = ({
38
46
  React.useEffect(() => {
39
47
  const node = document.getElementById(getElementToScrollTo(scrollableMap));
40
48
  if (node) {
41
- scrollIntoView__default["default"](node, {
42
- block: "nearest",
43
- inline: "nearest"
44
- });
49
+ scrollIntoViewWithOffset(node, getBaseElement() || document.body, offset);
45
50
  }
46
51
  });
47
52
  return /* @__PURE__ */ React__default["default"].createElement(AutoScrollManagerContext.Provider, {
@@ -58,16 +63,21 @@ exports.ScrollType = void 0;
58
63
  class AutoScrollManagerPlugin {
59
64
  constructor(config) {
60
65
  this.name = "auto-scroll-manager";
61
- var _a, _b;
66
+ var _a, _b, _c, _d;
62
67
  this.autoScrollOnLoad = (_a = config.autoScrollOnLoad) != null ? _a : false;
63
68
  this.autoFocusOnErrorField = (_b = config.autoFocusOnErrorField) != null ? _b : false;
69
+ this.getBaseElement = (_c = config.getBaseElement) != null ? _c : () => null;
70
+ this.offset = (_d = config.offset) != null ? _d : 0;
64
71
  this.initialRender = false;
65
72
  this.failedNavigation = false;
66
73
  this.alreadyScrolledTo = [];
67
74
  this.scrollFn = this.calculateScroll.bind(this);
68
75
  }
69
76
  getFirstScrollableElement(idList, type) {
70
- const highestElement = { id: "", ypos: 0 };
77
+ const highestElement = {
78
+ id: "",
79
+ ypos: 0
80
+ };
71
81
  const ypos = window.scrollY;
72
82
  idList.forEach((id) => {
73
83
  const element = document.getElementById(id);
@@ -81,9 +91,9 @@ class AutoScrollManagerPlugin {
81
91
  this.alreadyScrolledTo.push(id);
82
92
  }
83
93
  const epos = element == null ? void 0 : element.getBoundingClientRect().top;
84
- if (epos && (epos + ypos > highestElement.ypos || highestElement.ypos === 0)) {
94
+ if (epos !== void 0 && (epos + ypos < highestElement.ypos || highestElement.id === "")) {
85
95
  highestElement.id = id;
86
- highestElement.ypos = ypos - epos;
96
+ highestElement.ypos = ypos + epos;
87
97
  }
88
98
  });
89
99
  return highestElement.id;
@@ -115,10 +125,12 @@ class AutoScrollManagerPlugin {
115
125
  this.initialRender = true;
116
126
  this.failedNavigation = false;
117
127
  this.alreadyScrolledTo = [];
128
+ window.scroll(0, 0);
118
129
  });
119
- flow.hooks.beforeTransition.tap(this.name, (state) => {
120
- this.failedNavigation = true;
121
- return state;
130
+ flow.hooks.skipTransition.intercept({
131
+ call: () => {
132
+ this.failedNavigation = true;
133
+ }
122
134
  });
123
135
  });
124
136
  });
@@ -126,9 +138,11 @@ class AutoScrollManagerPlugin {
126
138
  applyReact(reactPlayer) {
127
139
  reactPlayer.hooks.webComponent.tap(this.name, (Comp) => {
128
140
  return () => {
129
- const { scrollFn } = this;
141
+ const { scrollFn, getBaseElement, offset } = this;
130
142
  return /* @__PURE__ */ React__default["default"].createElement(AutoScrollProvider, {
131
- getElementToScrollTo: scrollFn
143
+ getElementToScrollTo: scrollFn,
144
+ getBaseElement,
145
+ offset
132
146
  }, /* @__PURE__ */ React__default["default"].createElement(Comp, null));
133
147
  };
134
148
  });
package/dist/index.d.ts CHANGED
@@ -5,6 +5,10 @@ import { Player } from '@player-ui/player';
5
5
  interface AutoScrollProviderProps {
6
6
  /** Return the element to scroll to based on the registered types */
7
7
  getElementToScrollTo: (scrollableElements: Map<ScrollType, Set<string>>) => string;
8
+ /** Optional function to get container element, which is used for calculating offset (default: document.body) */
9
+ getBaseElement: () => HTMLElement | undefined | null;
10
+ /** Additional offset to be used (default: 0) */
11
+ offset: number;
8
12
  }
9
13
  interface RegisterData {
10
14
  /** when to scroll to the target */
@@ -20,7 +24,7 @@ declare const AutoScrollManagerContext: React.Context<{
20
24
  /** hook to register as a scroll target */
21
25
  declare const useRegisterAsScrollable: () => ScrollFunction;
22
26
  /** Component to handle scrolling */
23
- declare const AutoScrollProvider: ({ getElementToScrollTo, children, }: PropsWithChildren<AutoScrollProviderProps>) => JSX.Element;
27
+ declare const AutoScrollProvider: ({ getElementToScrollTo, getBaseElement, offset, children, }: PropsWithChildren<AutoScrollProviderProps>) => JSX.Element;
24
28
 
25
29
  declare enum ScrollType {
26
30
  ValidationError = 0,
@@ -32,6 +36,10 @@ interface AutoScrollManagerConfig {
32
36
  autoScrollOnLoad?: boolean;
33
37
  /** Config to auto-focus on an error */
34
38
  autoFocusOnErrorField?: boolean;
39
+ /** Optional function to get container element, which is used for calculating offset (default: document.body) */
40
+ getBaseElement?: () => HTMLElement | undefined | null;
41
+ /** Additional offset to be used (default: 0) */
42
+ offset?: number;
35
43
  }
36
44
  /** A plugin to manage scrolling behavior */
37
45
  declare class AutoScrollManagerPlugin implements ReactPlayerPlugin {
@@ -44,6 +52,10 @@ declare class AutoScrollManagerPlugin implements ReactPlayerPlugin {
44
52
  private initialRender;
45
53
  /** tracks if the navigation failed */
46
54
  private failedNavigation;
55
+ /** function to return the base of the scrollable area */
56
+ private getBaseElement;
57
+ /** static offset */
58
+ private offset;
47
59
  /** map of scroll type to set of ids that are registered under that type */
48
60
  private alreadyScrolledTo;
49
61
  private scrollFn;
package/dist/index.esm.js CHANGED
@@ -1,5 +1,12 @@
1
1
  import React, { useState, useEffect } from 'react';
2
- import scrollIntoView from 'smooth-scroll-into-view-if-needed';
2
+ import { scrollTo } from 'seamless-scroll-polyfill';
3
+
4
+ var scrollIntoViewWithOffset = (node, baseElement, offset) => {
5
+ scrollTo(window, {
6
+ behavior: "smooth",
7
+ top: node.getBoundingClientRect().top - baseElement.getBoundingClientRect().top - offset
8
+ });
9
+ };
3
10
 
4
11
  const AutoScrollManagerContext = React.createContext({ register: () => {
5
12
  } });
@@ -9,6 +16,8 @@ const useRegisterAsScrollable = () => {
9
16
  };
10
17
  const AutoScrollProvider = ({
11
18
  getElementToScrollTo,
19
+ getBaseElement,
20
+ offset,
12
21
  children
13
22
  }) => {
14
23
  const [scrollableMap, setScrollableMap] = useState(new Map());
@@ -29,10 +38,7 @@ const AutoScrollProvider = ({
29
38
  useEffect(() => {
30
39
  const node = document.getElementById(getElementToScrollTo(scrollableMap));
31
40
  if (node) {
32
- scrollIntoView(node, {
33
- block: "nearest",
34
- inline: "nearest"
35
- });
41
+ scrollIntoViewWithOffset(node, getBaseElement() || document.body, offset);
36
42
  }
37
43
  });
38
44
  return /* @__PURE__ */ React.createElement(AutoScrollManagerContext.Provider, {
@@ -49,16 +55,21 @@ var ScrollType;
49
55
  class AutoScrollManagerPlugin {
50
56
  constructor(config) {
51
57
  this.name = "auto-scroll-manager";
52
- var _a, _b;
58
+ var _a, _b, _c, _d;
53
59
  this.autoScrollOnLoad = (_a = config.autoScrollOnLoad) != null ? _a : false;
54
60
  this.autoFocusOnErrorField = (_b = config.autoFocusOnErrorField) != null ? _b : false;
61
+ this.getBaseElement = (_c = config.getBaseElement) != null ? _c : () => null;
62
+ this.offset = (_d = config.offset) != null ? _d : 0;
55
63
  this.initialRender = false;
56
64
  this.failedNavigation = false;
57
65
  this.alreadyScrolledTo = [];
58
66
  this.scrollFn = this.calculateScroll.bind(this);
59
67
  }
60
68
  getFirstScrollableElement(idList, type) {
61
- const highestElement = { id: "", ypos: 0 };
69
+ const highestElement = {
70
+ id: "",
71
+ ypos: 0
72
+ };
62
73
  const ypos = window.scrollY;
63
74
  idList.forEach((id) => {
64
75
  const element = document.getElementById(id);
@@ -72,9 +83,9 @@ class AutoScrollManagerPlugin {
72
83
  this.alreadyScrolledTo.push(id);
73
84
  }
74
85
  const epos = element == null ? void 0 : element.getBoundingClientRect().top;
75
- if (epos && (epos + ypos > highestElement.ypos || highestElement.ypos === 0)) {
86
+ if (epos !== void 0 && (epos + ypos < highestElement.ypos || highestElement.id === "")) {
76
87
  highestElement.id = id;
77
- highestElement.ypos = ypos - epos;
88
+ highestElement.ypos = ypos + epos;
78
89
  }
79
90
  });
80
91
  return highestElement.id;
@@ -106,10 +117,12 @@ class AutoScrollManagerPlugin {
106
117
  this.initialRender = true;
107
118
  this.failedNavigation = false;
108
119
  this.alreadyScrolledTo = [];
120
+ window.scroll(0, 0);
109
121
  });
110
- flow.hooks.beforeTransition.tap(this.name, (state) => {
111
- this.failedNavigation = true;
112
- return state;
122
+ flow.hooks.skipTransition.intercept({
123
+ call: () => {
124
+ this.failedNavigation = true;
125
+ }
113
126
  });
114
127
  });
115
128
  });
@@ -117,9 +130,11 @@ class AutoScrollManagerPlugin {
117
130
  applyReact(reactPlayer) {
118
131
  reactPlayer.hooks.webComponent.tap(this.name, (Comp) => {
119
132
  return () => {
120
- const { scrollFn } = this;
133
+ const { scrollFn, getBaseElement, offset } = this;
121
134
  return /* @__PURE__ */ React.createElement(AutoScrollProvider, {
122
- getElementToScrollTo: scrollFn
135
+ getElementToScrollTo: scrollFn,
136
+ getBaseElement,
137
+ offset
123
138
  }, /* @__PURE__ */ React.createElement(Comp, null));
124
139
  };
125
140
  });
package/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@player-ui/auto-scroll-manager-plugin-react",
3
- "version": "0.3.1-next.0",
3
+ "version": "0.3.1",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org"
7
7
  },
8
8
  "peerDependencies": {
9
- "@player-ui/react": "0.3.1-next.0"
9
+ "@player-ui/react": "0.3.1"
10
10
  },
11
11
  "dependencies": {
12
- "smooth-scroll-into-view-if-needed": "1.1.32",
12
+ "seamless-scroll-polyfill": "2.3.3",
13
13
  "@babel/runtime": "7.15.4"
14
14
  },
15
15
  "main": "dist/index.cjs.js",
@@ -53,6 +53,14 @@
53
53
  {
54
54
  "name": "Kelly Harrop",
55
55
  "url": "https://github.com/kharrop"
56
+ },
57
+ {
58
+ "name": "Alejandro Fimbres",
59
+ "url": "https://github.com/lexfm"
60
+ },
61
+ {
62
+ "name": "Rafael Campos",
63
+ "url": "https://github.com/rafbcampos"
56
64
  }
57
65
  ]
58
66
  }
package/src/hooks.tsx CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { PropsWithChildren } from 'react';
2
2
  import React, { useEffect, useState } from 'react';
3
- import scrollIntoView from 'smooth-scroll-into-view-if-needed';
3
+ import scrollIntoViewWithOffset from './scrollIntoViewWithOffset';
4
4
  import type { ScrollType } from './index';
5
5
 
6
6
  export interface AutoScrollProviderProps {
@@ -8,6 +8,10 @@ export interface AutoScrollProviderProps {
8
8
  getElementToScrollTo: (
9
9
  scrollableElements: Map<ScrollType, Set<string>>
10
10
  ) => string;
11
+ /** Optional function to get container element, which is used for calculating offset (default: document.body) */
12
+ getBaseElement: () => HTMLElement | undefined | null;
13
+ /** Additional offset to be used (default: 0) */
14
+ offset: number;
11
15
  }
12
16
 
13
17
  export interface RegisterData {
@@ -35,6 +39,8 @@ export const useRegisterAsScrollable = (): ScrollFunction => {
35
39
  /** Component to handle scrolling */
36
40
  export const AutoScrollProvider = ({
37
41
  getElementToScrollTo,
42
+ getBaseElement,
43
+ offset,
38
44
  children,
39
45
  }: PropsWithChildren<AutoScrollProviderProps>) => {
40
46
  // Tracker for what elements are registered to be scroll targets
@@ -68,10 +74,7 @@ export const AutoScrollProvider = ({
68
74
  const node = document.getElementById(getElementToScrollTo(scrollableMap));
69
75
 
70
76
  if (node) {
71
- scrollIntoView(node, {
72
- block: 'nearest',
73
- inline: 'nearest',
74
- });
77
+ scrollIntoViewWithOffset(node, getBaseElement() || document.body, offset);
75
78
  }
76
79
  });
77
80
 
package/src/plugin.tsx CHANGED
@@ -14,6 +14,10 @@ export interface AutoScrollManagerConfig {
14
14
  autoScrollOnLoad?: boolean;
15
15
  /** Config to auto-focus on an error */
16
16
  autoFocusOnErrorField?: boolean;
17
+ /** Optional function to get container element, which is used for calculating offset (default: document.body) */
18
+ getBaseElement?: () => HTMLElement | undefined | null;
19
+ /** Additional offset to be used (default: 0) */
20
+ offset?: number;
17
21
  }
18
22
 
19
23
  /** A plugin to manage scrolling behavior */
@@ -32,6 +36,12 @@ export class AutoScrollManagerPlugin implements ReactPlayerPlugin {
32
36
  /** tracks if the navigation failed */
33
37
  private failedNavigation: boolean;
34
38
 
39
+ /** function to return the base of the scrollable area */
40
+ private getBaseElement: () => HTMLElement | undefined | null;
41
+
42
+ /** static offset */
43
+ private offset: number;
44
+
35
45
  /** map of scroll type to set of ids that are registered under that type */
36
46
  private alreadyScrolledTo: Array<string>;
37
47
  private scrollFn: (
@@ -41,6 +51,8 @@ export class AutoScrollManagerPlugin implements ReactPlayerPlugin {
41
51
  constructor(config: AutoScrollManagerConfig) {
42
52
  this.autoScrollOnLoad = config.autoScrollOnLoad ?? false;
43
53
  this.autoFocusOnErrorField = config.autoFocusOnErrorField ?? false;
54
+ this.getBaseElement = config.getBaseElement ?? (() => null);
55
+ this.offset = config.offset ?? 0;
44
56
  this.initialRender = false;
45
57
  this.failedNavigation = false;
46
58
  this.alreadyScrolledTo = [];
@@ -48,7 +60,10 @@ export class AutoScrollManagerPlugin implements ReactPlayerPlugin {
48
60
  }
49
61
 
50
62
  getFirstScrollableElement(idList: Set<string>, type: ScrollType) {
51
- const highestElement = { id: '', ypos: 0 };
63
+ const highestElement = {
64
+ id: '',
65
+ ypos: 0,
66
+ };
52
67
  const ypos = window.scrollY;
53
68
  idList.forEach((id) => {
54
69
  const element = document.getElementById(id);
@@ -72,22 +87,19 @@ export class AutoScrollManagerPlugin implements ReactPlayerPlugin {
72
87
  }
73
88
 
74
89
  const epos = element?.getBoundingClientRect().top;
75
-
76
90
  if (
77
- epos &&
78
- (epos + ypos > highestElement.ypos || highestElement.ypos === 0)
91
+ epos !== undefined &&
92
+ (epos + ypos < highestElement.ypos || highestElement.id === '')
79
93
  ) {
80
94
  highestElement.id = id;
81
- highestElement.ypos = ypos - epos;
95
+ highestElement.ypos = ypos + epos;
82
96
  }
83
97
  });
84
-
85
98
  return highestElement.id;
86
99
  }
87
100
 
88
101
  calculateScroll(scrollableElements: Map<ScrollType, Set<string>>) {
89
102
  let currentScroll = ScrollType.FirstAppearance;
90
-
91
103
  if (this.initialRender) {
92
104
  if (this.autoScrollOnLoad) {
93
105
  currentScroll = ScrollType.ValidationError;
@@ -103,13 +115,11 @@ export class AutoScrollManagerPlugin implements ReactPlayerPlugin {
103
115
  }
104
116
 
105
117
  const elementList = scrollableElements.get(currentScroll);
106
-
107
118
  if (elementList) {
108
119
  const element = this.getFirstScrollableElement(
109
120
  elementList,
110
121
  currentScroll
111
122
  );
112
-
113
123
  return element ?? '';
114
124
  }
115
125
 
@@ -125,13 +135,13 @@ export class AutoScrollManagerPlugin implements ReactPlayerPlugin {
125
135
  this.initialRender = true;
126
136
  this.failedNavigation = false;
127
137
  this.alreadyScrolledTo = [];
138
+ // Reset scroll position for new view
139
+ window.scroll(0, 0);
128
140
  });
129
- flow.hooks.beforeTransition.tap(this.name, (state) => {
130
- // will get reset to false if view successfully transitions
131
- // otherwise stays as true when view get rerendered with errors
132
- this.failedNavigation = true;
133
-
134
- return state;
141
+ flow.hooks.skipTransition.intercept({
142
+ call: () => {
143
+ this.failedNavigation = true;
144
+ },
135
145
  });
136
146
  });
137
147
  });
@@ -140,10 +150,13 @@ export class AutoScrollManagerPlugin implements ReactPlayerPlugin {
140
150
  applyReact(reactPlayer: ReactPlayer) {
141
151
  reactPlayer.hooks.webComponent.tap(this.name, (Comp) => {
142
152
  return () => {
143
- const { scrollFn } = this;
144
-
153
+ const { scrollFn, getBaseElement, offset } = this;
145
154
  return (
146
- <AutoScrollProvider getElementToScrollTo={scrollFn}>
155
+ <AutoScrollProvider
156
+ getElementToScrollTo={scrollFn}
157
+ getBaseElement={getBaseElement}
158
+ offset={offset}
159
+ >
147
160
  <Comp />
148
161
  </AutoScrollProvider>
149
162
  );
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Scroll to the given element
3
+ -* @param node Element to scroll to
4
+ -* @param baseElement Container element used to calculate offset
5
+ -* @param offset Additional offset
6
+ */
7
+
8
+ import { scrollTo } from 'seamless-scroll-polyfill';
9
+
10
+ export default (
11
+ node: HTMLElement,
12
+ baseElement: HTMLElement,
13
+ offset: number
14
+ ) => {
15
+ scrollTo(window, {
16
+ behavior: 'smooth',
17
+ top:
18
+ node.getBoundingClientRect().top -
19
+ baseElement.getBoundingClientRect().top -
20
+ offset,
21
+ });
22
+ };