|
1 |
| -import React from 'react' |
2 |
| -import { findDOMNode } from 'react-dom' |
3 |
| -import parsePath from 'history/lib/parsePath' |
4 |
| - |
5 |
| -const setManualScroll = () => { |
6 |
| - if (typeof window !== 'undefined' && 'scrollRestoration' in window.history) { |
7 |
| - history.scrollRestoration = 'manual' |
8 |
| - } |
9 |
| -} |
10 |
| - |
11 |
| -const createKey = () => ( |
12 |
| - Math.random().toString(36).substr(2, 6) |
13 |
| -) |
14 |
| - |
15 |
| -const addScrollKey = (locationOrString) => { |
16 |
| - const location = typeof locationOrString === 'string' ? |
17 |
| - parsePath(locationOrString) : locationOrString |
18 |
| - if (!location.state) |
19 |
| - location.state = {} |
20 |
| - location.state.__scrollKey = createKey() |
21 |
| - return location |
22 |
| -} |
23 |
| - |
24 |
| -const RestoreWindowScroll = React.createClass({ |
25 |
| - |
26 |
| - propTypes: { |
27 |
| - restoreScrollPosition: React.PropTypes.func.isRequired, |
28 |
| - location: React.PropTypes.object.isRequired |
29 |
| - }, |
30 |
| - |
31 |
| - componentDidUpdate(prevProps) { |
32 |
| - const { location } = this.props |
33 |
| - if (prevProps.location !== this.props.location) { |
34 |
| - this.props.restoreScrollPosition('window', location) |
35 |
| - } |
36 |
| - }, |
37 |
| - |
38 |
| - render() { |
39 |
| - return this.props.children |
40 |
| - } |
41 |
| -}) |
42 |
| - |
43 |
| -const RestoreScroll = React.createClass({ |
44 |
| - |
45 |
| - contextTypes: { |
46 |
| - router: React.PropTypes.object.isRequired |
47 |
| - }, |
48 |
| - |
49 |
| - propTypes: { |
50 |
| - scrollKey: React.PropTypes.string.isRequired |
51 |
| - }, |
52 |
| - |
53 |
| - componentDidMount() { |
54 |
| - const { registerScroller } = this.context.router.restoreScroll |
55 |
| - const { scrollKey } = this.props |
56 |
| - registerScroller(scrollKey, findDOMNode(this)) |
57 |
| - this.restoreScrollPosition() |
58 |
| - }, |
59 |
| - |
60 |
| - componentWillUnmount() { |
61 |
| - const { unregisterScroller } = this.context.router.restoreScroll |
62 |
| - const { scrollKey } = this.props |
63 |
| - unregisterScroller(scrollKey) |
64 |
| - }, |
65 |
| - |
66 |
| - restoreScrollPosition() { |
67 |
| - const { restoreScrollPosition } = this.context.router.restoreScroll |
68 |
| - restoreScrollPosition(this.props.scrollKey) |
69 |
| - }, |
70 |
| - |
71 |
| - render() { |
72 |
| - return React.Children.only(this.props.children) |
73 |
| - } |
74 |
| - |
75 |
| -}) |
76 |
| - |
77 |
| -const useHistoryRestoreScroll = (createHistory) => ( |
78 |
| - (options={}) => { |
79 |
| - setManualScroll() |
80 |
| - |
81 |
| - const initialScrollKey = createKey() |
82 |
| - let currentScrollKey = null |
83 |
| - |
84 |
| - const history = createHistory(options) |
85 |
| - |
86 |
| - //// |
87 |
| - // `positionsByLocation` looks like this |
88 |
| - // |
89 |
| - // ``` |
90 |
| - // { |
91 |
| - // [location.key]: { |
92 |
| - // window: { scrollX, scrollY }, |
93 |
| - // [scrollKey]: { scrollTop, scrollLeft } |
94 |
| - // }, |
95 |
| - // [location.key]: etc... |
96 |
| - // } |
97 |
| - // ``` |
98 |
| - const positionsByLocation = {} |
99 |
| - const scrollers = {} |
100 |
| - |
101 |
| - const push = (locationWithoutKey) => { |
102 |
| - const location = addScrollKey(locationWithoutKey) |
103 |
| - history.push(location) |
104 |
| - } |
105 |
| - |
106 |
| - const replace = (locationWithoutKey) => { |
107 |
| - const location = addScrollKey(locationWithoutKey) |
108 |
| - history.replace(location) |
109 |
| - } |
110 |
| - |
111 |
| - const registerScroller = (scrollKey, node) => { |
112 |
| - scrollers[scrollKey] = node |
113 |
| - } |
114 |
| - |
115 |
| - const unregisterScroller = (scrollKey) => { |
116 |
| - delete scrollers[scrollKey] |
117 |
| - } |
118 |
| - |
119 |
| - const getScrollerPosition = (componentScrollKey) => { |
120 |
| - const locationPositions = positionsByLocation[currentScrollKey] |
121 |
| - return locationPositions ? locationPositions[componentScrollKey] || null : null |
122 |
| - } |
123 |
| - |
124 |
| - const saveScrollerPositions = () => { |
125 |
| - if (!positionsByLocation[currentScrollKey]) |
126 |
| - positionsByLocation[currentScrollKey] = {} |
127 |
| - const { scrollY, scrollX } = window |
128 |
| - savePosition('window', { scrollX, scrollY }) |
129 |
| - for (const scrollKey in scrollers) { |
130 |
| - const scrollerNode = scrollers[scrollKey] |
131 |
| - const { scrollTop, scrollLeft } = scrollerNode |
132 |
| - savePosition(scrollKey, { scrollTop, scrollLeft }) |
133 |
| - } |
134 |
| - } |
135 |
| - |
136 |
| - const savePosition = (scrollKey, position) => { |
137 |
| - positionsByLocation[currentScrollKey][scrollKey] = position |
138 |
| - } |
139 |
| - |
140 |
| - const unlisten = history.listen((location) => { |
141 |
| - saveScrollerPositions() |
142 |
| - currentScrollKey = ( |
143 |
| - location.state && location.state.__scrollKey |
144 |
| - ) || initialScrollKey |
145 |
| - }) |
146 |
| - |
147 |
| - const listen = (...args) => { |
148 |
| - const internalUnlisten = history.listen(...args) |
149 |
| - return () => unlisten() && internalUnlisten() |
150 |
| - } |
151 |
| - |
152 |
| - const restoreWindow = (location) => { |
153 |
| - if (location.action === 'PUSH' || location.action === 'REPLACE') { |
154 |
| - window.scrollTo(0, 0) |
155 |
| - } else { |
156 |
| - const position = getScrollerPosition('window') |
157 |
| - if (position) { |
158 |
| - const { scrollX, scrollY } = position |
159 |
| - window.scrollTo(scrollX, scrollY) |
160 |
| - } |
161 |
| - } |
162 |
| - } |
163 |
| - |
164 |
| - const restoreNode = (scrollKey) => { |
165 |
| - const position = getScrollerPosition(scrollKey) |
166 |
| - if (position) { |
167 |
| - const node = scrollers[scrollKey] |
168 |
| - const { scrollTop, scrollLeft } = position |
169 |
| - node.scrollTop = scrollTop |
170 |
| - node.scrollLeft = scrollLeft |
171 |
| - } |
172 |
| - } |
173 |
| - |
174 |
| - const restoreScrollPosition = (key, location) => { |
175 |
| - if (key === 'window') { |
176 |
| - restoreWindow(location) |
177 |
| - } else { |
178 |
| - restoreNode(key) |
179 |
| - } |
180 |
| - } |
181 |
| - |
182 |
| - return { |
183 |
| - ...history, |
184 |
| - listen, |
185 |
| - push, |
186 |
| - replace, |
187 |
| - restoreScroll: { |
188 |
| - registerScroller, |
189 |
| - unregisterScroller, |
190 |
| - getScrollerPosition, |
191 |
| - restoreScrollPosition |
192 |
| - } |
193 |
| - } |
194 |
| - } |
195 |
| -) |
196 |
| - |
197 |
| -const useRouterRestoreScroll = () => ({ |
198 |
| - renderRouterContext: (child, props) => ( |
199 |
| - <RestoreWindowScroll |
200 |
| - restoreScrollPosition={props.router.restoreScroll.restoreScrollPosition} |
201 |
| - location={props.location} |
202 |
| - children={child} |
203 |
| - /> |
204 |
| - ) |
205 |
| -}) |
206 |
| - |
207 |
| -export { |
208 |
| - useHistoryRestoreScroll, |
209 |
| - useRouterRestoreScroll, |
210 |
| - RestoreScroll |
211 |
| -} |
| 1 | +export RestoreScroll from './RestoreScroll' |
| 2 | +export useHistoryRestoreScroll from './useHistoryRestoreScroll' |
| 3 | +export useRouterRestoreScroll from './useRouterRestoreScroll' |
212 | 4 |
|
0 commit comments