1 | import _extends from 'babel-runtime/helpers/extends';
|
2 | import _classCallCheck from 'babel-runtime/helpers/classCallCheck';
|
3 | import _possibleConstructorReturn from 'babel-runtime/helpers/possibleConstructorReturn';
|
4 | import _inherits from 'babel-runtime/helpers/inherits';
|
5 |
|
6 | var _class, _temp;
|
7 |
|
8 | import React from 'react';
|
9 | import PropTypes from 'prop-types';
|
10 | import classnames from 'classnames';
|
11 | import { findDOMNode } from 'react-dom';
|
12 | import { polyfill } from 'react-lifecycles-compat';
|
13 | import ResizeObserver from 'resize-observer-polyfill';
|
14 |
|
15 | import { obj, events, func } from '../util';
|
16 | import ConfigProvider from '../config-provider';
|
17 | import { getScroll, getRect, getNodeHeight } from './util';
|
18 |
|
19 |
|
20 | var Affix = (_temp = _class = function (_React$Component) {
|
21 | _inherits(Affix, _React$Component);
|
22 |
|
23 | Affix._getAffixMode = function _getAffixMode(nextProps) {
|
24 | var affixMode = {
|
25 | top: false,
|
26 | bottom: false,
|
27 | offset: 0
|
28 | };
|
29 | if (!nextProps) {
|
30 | return affixMode;
|
31 | }
|
32 | var offsetTop = nextProps.offsetTop,
|
33 | offsetBottom = nextProps.offsetBottom;
|
34 |
|
35 |
|
36 | if (typeof offsetTop !== 'number' && typeof offsetBottom !== 'number') {
|
37 |
|
38 | affixMode.top = true;
|
39 | } else if (typeof offsetTop === 'number') {
|
40 | affixMode.top = true;
|
41 | affixMode.bottom = false;
|
42 | affixMode.offset = offsetTop;
|
43 | } else if (typeof offsetBottom === 'number') {
|
44 | affixMode.bottom = true;
|
45 | affixMode.top = false;
|
46 | affixMode.offset = offsetBottom;
|
47 | }
|
48 |
|
49 | return affixMode;
|
50 | };
|
51 |
|
52 | function Affix(props, context) {
|
53 | _classCallCheck(this, Affix);
|
54 |
|
55 | var _this = _possibleConstructorReturn(this, _React$Component.call(this, props, context));
|
56 |
|
57 | _this._clearContainerEvent = function () {
|
58 | if (_this.timeout) {
|
59 | clearTimeout(_this.timeout);
|
60 | _this.timeout = null;
|
61 | }
|
62 | var container = _this.props.container;
|
63 |
|
64 | _this._removeEventHandlerForContainer(container);
|
65 | };
|
66 |
|
67 | _this.updatePosition = function () {
|
68 | _this._updateNodePosition();
|
69 | };
|
70 |
|
71 | _this._updateNodePosition = function () {
|
72 | var affixMode = _this.state.affixMode;
|
73 | var _this$props = _this.props,
|
74 | container = _this$props.container,
|
75 | useAbsolute = _this$props.useAbsolute;
|
76 |
|
77 | var affixContainer = container();
|
78 |
|
79 | if (!affixContainer || !_this.affixNode) {
|
80 | return false;
|
81 | }
|
82 | var containerScrollTop = getScroll(affixContainer, true);
|
83 | var affixOffset = _this._getOffset(_this.affixNode, affixContainer);
|
84 | var containerHeight = getNodeHeight(affixContainer);
|
85 | var affixHeight = _this.affixNode.offsetHeight;
|
86 | var containerRect = getRect(affixContainer);
|
87 |
|
88 | var affixChildHeight = _this.affixChildNode.offsetHeight;
|
89 |
|
90 | var affixStyle = {
|
91 | width: affixOffset.width
|
92 | };
|
93 | var containerStyle = {
|
94 | width: affixOffset.width,
|
95 | height: affixChildHeight
|
96 | };
|
97 | var positionStyle = null;
|
98 | if (affixMode.top && containerScrollTop > affixOffset.top - affixMode.offset) {
|
99 |
|
100 | if (useAbsolute) {
|
101 | affixStyle.position = 'absolute';
|
102 | affixStyle.top = containerScrollTop - (affixOffset.top - affixMode.offset);
|
103 | positionStyle = 'relative';
|
104 | } else {
|
105 | affixStyle.position = 'fixed';
|
106 | affixStyle.top = affixMode.offset + containerRect.top;
|
107 | }
|
108 | _this._setAffixStyle(affixStyle, true);
|
109 | _this._setContainerStyle(containerStyle);
|
110 | } else if (affixMode.bottom && containerScrollTop < affixOffset.top + affixHeight + affixMode.offset - containerHeight) {
|
111 |
|
112 | affixStyle.height = affixHeight;
|
113 | if (useAbsolute) {
|
114 | affixStyle.position = 'absolute';
|
115 | affixStyle.top = containerScrollTop - (affixOffset.top + affixHeight + affixMode.offset - containerHeight);
|
116 | positionStyle = 'relative';
|
117 | } else {
|
118 | affixStyle.position = 'fixed';
|
119 | affixStyle.bottom = affixMode.offset;
|
120 | }
|
121 | _this._setAffixStyle(affixStyle, true);
|
122 | _this._setContainerStyle(containerStyle);
|
123 | } else {
|
124 | _this._setAffixStyle(null);
|
125 | _this._setContainerStyle(null);
|
126 | }
|
127 |
|
128 | if (_this.state.positionStyle !== positionStyle) {
|
129 | _this.setState({ positionStyle: positionStyle });
|
130 | }
|
131 | };
|
132 |
|
133 | _this._affixNodeRefHandler = function (ref) {
|
134 | _this.affixNode = ref;
|
135 | };
|
136 |
|
137 | _this._affixChildNodeRefHandler = function (ref) {
|
138 | _this.affixChildNode = ref;
|
139 | };
|
140 |
|
141 | _this.state = {
|
142 | style: null,
|
143 | containerStyle: null,
|
144 | positionStyle: null,
|
145 | affixMode: Affix._getAffixMode(props)
|
146 | };
|
147 | _this.resizeObserver = new ResizeObserver(_this._updateNodePosition);
|
148 | return _this;
|
149 | }
|
150 |
|
151 | Affix.getDerivedStateFromProps = function getDerivedStateFromProps(nextProps, prevState) {
|
152 | if ('offsetTop' in nextProps || 'offsetBottom' in nextProps) {
|
153 | return {
|
154 | affixMode: Affix._getAffixMode(nextProps)
|
155 | };
|
156 | }
|
157 | return null;
|
158 | };
|
159 |
|
160 | Affix.prototype.componentDidMount = function componentDidMount() {
|
161 | var _this2 = this;
|
162 |
|
163 | var container = this.props.container;
|
164 |
|
165 |
|
166 | this.timeout = setTimeout(function () {
|
167 | _this2._updateNodePosition();
|
168 | _this2._setEventHandlerForContainer(container);
|
169 | });
|
170 | };
|
171 |
|
172 | Affix.prototype.componentDidUpdate = function componentDidUpdate(prevProps, prevState, snapshot) {
|
173 | var _this3 = this;
|
174 |
|
175 | if (prevProps.container() !== this.props.container()) {
|
176 | this._clearContainerEvent();
|
177 |
|
178 | this.timeout = setTimeout(function () {
|
179 | _this3._setEventHandlerForContainer(_this3.props.container);
|
180 | });
|
181 | }
|
182 |
|
183 | setTimeout(this._updateNodePosition);
|
184 | };
|
185 |
|
186 | Affix.prototype.componentWillUnmount = function componentWillUnmount() {
|
187 | this._clearContainerEvent();
|
188 | };
|
189 |
|
190 | Affix.prototype._setEventHandlerForContainer = function _setEventHandlerForContainer(getContainer) {
|
191 | var container = getContainer();
|
192 | if (!container) {
|
193 | return;
|
194 | }
|
195 | events.on(container, 'scroll', this._updateNodePosition, false);
|
196 | this.resizeObserver.observe(this.affixNode);
|
197 | };
|
198 |
|
199 | Affix.prototype._removeEventHandlerForContainer = function _removeEventHandlerForContainer(getContainer) {
|
200 | var container = getContainer();
|
201 | if (container) {
|
202 | events.off(container, 'scroll', this._updateNodePosition);
|
203 | this.resizeObserver.disconnect();
|
204 | }
|
205 | };
|
206 |
|
207 | Affix.prototype._setAffixStyle = function _setAffixStyle(affixStyle) {
|
208 | var affixed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
|
209 |
|
210 | if (obj.shallowEqual(affixStyle, this.state.style)) {
|
211 | return;
|
212 | }
|
213 |
|
214 | this.setState({
|
215 | style: affixStyle
|
216 | });
|
217 |
|
218 | var onAffix = this.props.onAffix;
|
219 |
|
220 |
|
221 | if (affixed) {
|
222 | setTimeout(function () {
|
223 | return onAffix(true);
|
224 | });
|
225 | } else if (!affixStyle) {
|
226 | setTimeout(function () {
|
227 | return onAffix(false);
|
228 | });
|
229 | }
|
230 | };
|
231 |
|
232 | Affix.prototype._setContainerStyle = function _setContainerStyle(containerStyle) {
|
233 | if (obj.shallowEqual(containerStyle, this.state.containerStyle)) {
|
234 | return;
|
235 | }
|
236 | this.setState({ containerStyle: containerStyle });
|
237 | };
|
238 |
|
239 | Affix.prototype._getOffset = function _getOffset(affixNode, affixContainer) {
|
240 | var affixRect = affixNode.getBoundingClientRect();
|
241 | var containerRect = getRect(affixContainer);
|
242 | var containerScrollTop = getScroll(affixContainer, true);
|
243 | var containerScrollLeft = getScroll(affixContainer, false);
|
244 |
|
245 | return {
|
246 | top: affixRect.top - containerRect.top + containerScrollTop,
|
247 | left: affixRect.left - containerRect.left + containerScrollLeft,
|
248 | width: affixRect.width,
|
249 | height: affixRect.height
|
250 | };
|
251 | };
|
252 |
|
253 | Affix.prototype.render = function render() {
|
254 | var _classnames;
|
255 |
|
256 | var _state = this.state,
|
257 | affixMode = _state.affixMode,
|
258 | positionStyle = _state.positionStyle;
|
259 | var _props = this.props,
|
260 | prefix = _props.prefix,
|
261 | className = _props.className,
|
262 | style = _props.style,
|
263 | children = _props.children;
|
264 |
|
265 | var state = this.state;
|
266 | var classNames = classnames((_classnames = {}, _classnames[prefix + 'affix'] = state.style, _classnames[prefix + 'affix-top'] = !state.style && affixMode.top, _classnames[prefix + 'affix-bottom'] = !state.style && affixMode.bottom, _classnames[className] = className, _classnames));
|
267 | var wrapperStyle = _extends({}, style, { position: positionStyle });
|
268 |
|
269 | return React.createElement(
|
270 | 'div',
|
271 | { ref: this._affixNodeRefHandler, style: wrapperStyle },
|
272 | state.style && React.createElement('div', { style: state.containerStyle, 'aria-hidden': 'true' }),
|
273 | React.createElement(
|
274 | 'div',
|
275 | { ref: this._affixChildNodeRefHandler, className: classNames, style: state.style },
|
276 | children
|
277 | )
|
278 | );
|
279 | };
|
280 |
|
281 | return Affix;
|
282 | }(React.Component), _class.propTypes = {
|
283 | prefix: PropTypes.string,
|
284 | |
285 |
|
286 |
|
287 |
|
288 | container: PropTypes.func,
|
289 | |
290 |
|
291 |
|
292 | offsetTop: PropTypes.number,
|
293 | |
294 |
|
295 |
|
296 | offsetBottom: PropTypes.number,
|
297 | |
298 |
|
299 |
|
300 |
|
301 | onAffix: PropTypes.func,
|
302 | |
303 |
|
304 |
|
305 |
|
306 | useAbsolute: PropTypes.bool,
|
307 | className: PropTypes.string,
|
308 | style: PropTypes.object,
|
309 | children: PropTypes.any
|
310 | }, _class.defaultProps = {
|
311 | prefix: 'next-',
|
312 | container: function container() {
|
313 | return window;
|
314 | },
|
315 | onAffix: func.noop
|
316 | }, _temp);
|
317 | Affix.displayName = 'Affix';
|
318 |
|
319 |
|
320 | export default ConfigProvider.config(polyfill(Affix)); |
\ | No newline at end of file |