react-simple-pull-to-refresh 1.3.1 → 1.3.2
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/README.md +2 -0
- package/build/components/pull-to-refresh.d.ts +19 -0
- package/build/components/pulling-content.d.ts +3 -0
- package/build/components/refreshing-content.d.ts +4 -0
- package/build/index.cjs.js +294 -0
- package/build/index.d.ts +2 -0
- package/build/index.esm.js +289 -0
- package/build/isScrollable.d.ts +5 -0
- package/build/types.d.ts +4 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -83,6 +83,8 @@ return (
|
|
|
83
83
|
|
|
84
84
|
## Changelog
|
|
85
85
|
|
|
86
|
+
1.3.2: Fix build issue encountered with 1.3.1
|
|
87
|
+
|
|
86
88
|
1.3.1: Fix issue preventing fixed elements to work properly - (From: [@ManuDoni](https://github.com/ManuDoni))
|
|
87
89
|
|
|
88
90
|
1.3.0: Add a _resistance_ prop, that allows to adjust the pull down difficulty - (From: [@joshuahiggins](https://github.com/joshuahiggins))
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import '../styles/main.scss';
|
|
3
|
+
interface PullToRefreshProps {
|
|
4
|
+
isPullable?: boolean;
|
|
5
|
+
canFetchMore?: boolean;
|
|
6
|
+
onRefresh: () => Promise<any>;
|
|
7
|
+
onFetchMore?: () => Promise<any>;
|
|
8
|
+
refreshingContent?: JSX.Element | string;
|
|
9
|
+
pullingContent?: JSX.Element | string;
|
|
10
|
+
children: JSX.Element;
|
|
11
|
+
pullDownThreshold?: number;
|
|
12
|
+
fetchMoreThreshold?: number;
|
|
13
|
+
maxPullDownDistance?: number;
|
|
14
|
+
resistance?: number;
|
|
15
|
+
backgroundColor?: string;
|
|
16
|
+
className?: string;
|
|
17
|
+
}
|
|
18
|
+
declare const PullToRefresh: React.FC<PullToRefreshProps>;
|
|
19
|
+
export default PullToRefresh;
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
|
|
4
|
+
|
|
5
|
+
var React = require('react');
|
|
6
|
+
var React__default = _interopDefault(React);
|
|
7
|
+
|
|
8
|
+
var DIRECTION;
|
|
9
|
+
(function (DIRECTION) {
|
|
10
|
+
DIRECTION[DIRECTION["UP"] = -1] = "UP";
|
|
11
|
+
DIRECTION[DIRECTION["DOWN"] = 1] = "DOWN";
|
|
12
|
+
})(DIRECTION || (DIRECTION = {}));
|
|
13
|
+
|
|
14
|
+
function isOverflowScrollable(element) {
|
|
15
|
+
var overflowType = getComputedStyle(element).overflowY;
|
|
16
|
+
if (element === document.scrollingElement && overflowType === 'visible') {
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
if (overflowType !== 'scroll' && overflowType !== 'auto') {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
function isScrollable(element, direction) {
|
|
25
|
+
if (!isOverflowScrollable(element)) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
if (direction === DIRECTION.DOWN) {
|
|
29
|
+
var bottomScroll = element.scrollTop + element.clientHeight;
|
|
30
|
+
return bottomScroll < element.scrollHeight;
|
|
31
|
+
}
|
|
32
|
+
if (direction === DIRECTION.UP) {
|
|
33
|
+
return element.scrollTop > 0;
|
|
34
|
+
}
|
|
35
|
+
throw new Error('unsupported direction');
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Returns whether a given element or any of its ancestors (up to rootElement) is scrollable in a given direction.
|
|
39
|
+
*/
|
|
40
|
+
function isTreeScrollable(element, direction) {
|
|
41
|
+
if (isScrollable(element, direction)) {
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
if (element.parentElement == null) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
return isTreeScrollable(element.parentElement, direction);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function styleInject(css, ref) {
|
|
51
|
+
if ( ref === void 0 ) ref = {};
|
|
52
|
+
var insertAt = ref.insertAt;
|
|
53
|
+
|
|
54
|
+
if (!css || typeof document === 'undefined') { return; }
|
|
55
|
+
|
|
56
|
+
var head = document.head || document.getElementsByTagName('head')[0];
|
|
57
|
+
var style = document.createElement('style');
|
|
58
|
+
style.type = 'text/css';
|
|
59
|
+
|
|
60
|
+
if (insertAt === 'top') {
|
|
61
|
+
if (head.firstChild) {
|
|
62
|
+
head.insertBefore(style, head.firstChild);
|
|
63
|
+
} else {
|
|
64
|
+
head.appendChild(style);
|
|
65
|
+
}
|
|
66
|
+
} else {
|
|
67
|
+
head.appendChild(style);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (style.styleSheet) {
|
|
71
|
+
style.styleSheet.cssText = css;
|
|
72
|
+
} else {
|
|
73
|
+
style.appendChild(document.createTextNode(css));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
var css = ".lds-ellipsis {\n display: inline-block;\n position: relative;\n width: 64px;\n height: 64px; }\n\n.lds-ellipsis div {\n position: absolute;\n top: 27px;\n width: 11px;\n height: 11px;\n border-radius: 50%;\n background: #363636;\n animation-timing-function: cubic-bezier(0, 1, 1, 0); }\n\n.lds-ellipsis div:nth-child(1) {\n left: 6px;\n animation: lds-ellipsis1 0.6s infinite; }\n\n.lds-ellipsis div:nth-child(2) {\n left: 6px;\n animation: lds-ellipsis2 0.6s infinite; }\n\n.lds-ellipsis div:nth-child(3) {\n left: 26px;\n animation: lds-ellipsis2 0.6s infinite; }\n\n.lds-ellipsis div:nth-child(4) {\n left: 45px;\n animation: lds-ellipsis3 0.6s infinite; }\n\n@keyframes lds-ellipsis1 {\n 0% {\n transform: scale(0); }\n 100% {\n transform: scale(1); } }\n\n@keyframes lds-ellipsis3 {\n 0% {\n transform: scale(1); }\n 100% {\n transform: scale(0); } }\n\n@keyframes lds-ellipsis2 {\n 0% {\n transform: translate(0, 0); }\n 100% {\n transform: translate(19px, 0); } }\n";
|
|
78
|
+
styleInject(css);
|
|
79
|
+
|
|
80
|
+
// Source: https://loading.io/css/
|
|
81
|
+
var RefreshingContent = function () {
|
|
82
|
+
return (React__default.createElement("div", { className: "lds-ellipsis" },
|
|
83
|
+
React__default.createElement("div", null),
|
|
84
|
+
React__default.createElement("div", null),
|
|
85
|
+
React__default.createElement("div", null),
|
|
86
|
+
React__default.createElement("div", null)));
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
var PullingContent = function () {
|
|
90
|
+
return (React__default.createElement("div", null,
|
|
91
|
+
React__default.createElement("p", null, "\u21A7\u00A0\u00A0pull to refresh\u00A0\u00A0\u21A7")));
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
var css$1 = ".ptr,\n.ptr__children {\n height: 100%;\n width: 100%;\n overflow: hidden;\n -webkit-overflow-scrolling: touch;\n position: relative; }\n\n.ptr.ptr--fetch-more-treshold-breached .ptr__fetch-more {\n display: block; }\n\n.ptr__fetch-more {\n display: none; }\n\n/**\n * Pull down transition \n */\n.ptr__children,\n.ptr__pull-down {\n transition: transform 0.2s cubic-bezier(0, 0, 0.31, 1); }\n\n.ptr__pull-down {\n position: absolute;\n overflow: hidden;\n left: 0;\n right: 0;\n top: 0;\n visibility: hidden; }\n .ptr__pull-down > div {\n display: none; }\n\n.ptr--dragging {\n /**\n * Hide PullMore content is treshold breached\n */\n /**\n * Otherwize, display content\n */ }\n .ptr--dragging.ptr--pull-down-treshold-breached .ptr__pull-down--pull-more {\n display: none; }\n .ptr--dragging .ptr__pull-down--pull-more {\n display: block; }\n\n.ptr--pull-down-treshold-breached {\n /**\n * Force opacity to 1 is pull down trashold breached\n */\n /**\n * And display loader\n */ }\n .ptr--pull-down-treshold-breached .ptr__pull-down {\n opacity: 1 !important; }\n .ptr--pull-down-treshold-breached .ptr__pull-down--loading {\n display: block; }\n\n.ptr__loader {\n margin: 0 auto;\n text-align: center; }\n";
|
|
95
|
+
styleInject(css$1);
|
|
96
|
+
|
|
97
|
+
var PullToRefresh = function (_a) {
|
|
98
|
+
var _b = _a.isPullable, isPullable = _b === void 0 ? true : _b, _c = _a.canFetchMore, canFetchMore = _c === void 0 ? false : _c, onRefresh = _a.onRefresh, onFetchMore = _a.onFetchMore, _d = _a.refreshingContent, refreshingContent = _d === void 0 ? React__default.createElement(RefreshingContent, null) : _d, _e = _a.pullingContent, pullingContent = _e === void 0 ? React__default.createElement(PullingContent, null) : _e, children = _a.children, _f = _a.pullDownThreshold, pullDownThreshold = _f === void 0 ? 67 : _f, _g = _a.fetchMoreThreshold, fetchMoreThreshold = _g === void 0 ? 100 : _g, _h = _a.maxPullDownDistance, maxPullDownDistance = _h === void 0 ? 95 : _h, // max distance to scroll to trigger refresh
|
|
99
|
+
_j = _a.resistance, // max distance to scroll to trigger refresh
|
|
100
|
+
resistance = _j === void 0 ? 1 : _j, backgroundColor = _a.backgroundColor, _k = _a.className, className = _k === void 0 ? '' : _k;
|
|
101
|
+
var containerRef = React.useRef(null);
|
|
102
|
+
var childrenRef = React.useRef(null);
|
|
103
|
+
var pullDownRef = React.useRef(null);
|
|
104
|
+
var fetchMoreRef = React.useRef(null);
|
|
105
|
+
var pullToRefreshThresholdBreached = false;
|
|
106
|
+
var fetchMoreTresholdBreached = false; // if true, fetchMore loader is displayed
|
|
107
|
+
var isDragging = false;
|
|
108
|
+
var startY = 0;
|
|
109
|
+
var currentY = 0;
|
|
110
|
+
React.useEffect(function () {
|
|
111
|
+
if (!isPullable || !childrenRef || !childrenRef.current)
|
|
112
|
+
return;
|
|
113
|
+
var childrenEl = childrenRef.current;
|
|
114
|
+
childrenEl.addEventListener('touchstart', onTouchStart, { passive: true });
|
|
115
|
+
childrenEl.addEventListener('mousedown', onTouchStart);
|
|
116
|
+
childrenEl.addEventListener('touchmove', onTouchMove, { passive: false });
|
|
117
|
+
childrenEl.addEventListener('mousemove', onTouchMove);
|
|
118
|
+
window.addEventListener('scroll', onScroll);
|
|
119
|
+
childrenEl.addEventListener('touchend', onEnd);
|
|
120
|
+
childrenEl.addEventListener('mouseup', onEnd);
|
|
121
|
+
document.body.addEventListener('mouseleave', onEnd);
|
|
122
|
+
return function () {
|
|
123
|
+
childrenEl.removeEventListener('touchstart', onTouchStart);
|
|
124
|
+
childrenEl.removeEventListener('mousedown', onTouchStart);
|
|
125
|
+
childrenEl.removeEventListener('touchmove', onTouchMove);
|
|
126
|
+
childrenEl.removeEventListener('mousemove', onTouchMove);
|
|
127
|
+
window.removeEventListener('scroll', onScroll);
|
|
128
|
+
childrenEl.removeEventListener('touchend', onEnd);
|
|
129
|
+
childrenEl.removeEventListener('mouseup', onEnd);
|
|
130
|
+
document.body.removeEventListener('mouseleave', onEnd);
|
|
131
|
+
};
|
|
132
|
+
}, [
|
|
133
|
+
children,
|
|
134
|
+
isPullable,
|
|
135
|
+
onRefresh,
|
|
136
|
+
pullDownThreshold,
|
|
137
|
+
maxPullDownDistance,
|
|
138
|
+
canFetchMore,
|
|
139
|
+
fetchMoreThreshold,
|
|
140
|
+
]);
|
|
141
|
+
/**
|
|
142
|
+
* Check onMount / canFetchMore becomes true
|
|
143
|
+
* if fetchMoreThreshold is already breached
|
|
144
|
+
*/
|
|
145
|
+
React.useEffect(function () {
|
|
146
|
+
var _a;
|
|
147
|
+
/**
|
|
148
|
+
* Check if it is already in fetching more state
|
|
149
|
+
*/
|
|
150
|
+
if (!((_a = containerRef) === null || _a === void 0 ? void 0 : _a.current))
|
|
151
|
+
return;
|
|
152
|
+
var isAlreadyFetchingMore = containerRef.current.classList.contains('ptr--fetch-more-treshold-breached');
|
|
153
|
+
if (isAlreadyFetchingMore)
|
|
154
|
+
return;
|
|
155
|
+
/**
|
|
156
|
+
* Proceed
|
|
157
|
+
*/
|
|
158
|
+
if (canFetchMore && getScrollToBottomValue() < fetchMoreThreshold && onFetchMore) {
|
|
159
|
+
containerRef.current.classList.add('ptr--fetch-more-treshold-breached');
|
|
160
|
+
fetchMoreTresholdBreached = true;
|
|
161
|
+
onFetchMore().then(initContainer).catch(initContainer);
|
|
162
|
+
}
|
|
163
|
+
}, [canFetchMore, children]);
|
|
164
|
+
/**
|
|
165
|
+
* Returns distance to bottom of the container
|
|
166
|
+
*/
|
|
167
|
+
var getScrollToBottomValue = function () {
|
|
168
|
+
if (!childrenRef || !childrenRef.current)
|
|
169
|
+
return -1;
|
|
170
|
+
var scrollTop = window.scrollY; // is the pixels hidden in top due to the scroll. With no scroll its value is 0.
|
|
171
|
+
var scrollHeight = childrenRef.current.scrollHeight; // is the pixels of the whole container
|
|
172
|
+
return scrollHeight - scrollTop - window.innerHeight;
|
|
173
|
+
};
|
|
174
|
+
var initContainer = function () {
|
|
175
|
+
requestAnimationFrame(function () {
|
|
176
|
+
/**
|
|
177
|
+
* Reset Styles
|
|
178
|
+
*/
|
|
179
|
+
if (childrenRef.current) {
|
|
180
|
+
childrenRef.current.style.overflowX = 'hidden';
|
|
181
|
+
childrenRef.current.style.overflowY = 'auto';
|
|
182
|
+
childrenRef.current.style.transform = "unset";
|
|
183
|
+
}
|
|
184
|
+
if (pullDownRef.current) {
|
|
185
|
+
pullDownRef.current.style.opacity = '0';
|
|
186
|
+
}
|
|
187
|
+
if (containerRef.current) {
|
|
188
|
+
containerRef.current.classList.remove('ptr--pull-down-treshold-breached');
|
|
189
|
+
containerRef.current.classList.remove('ptr--dragging');
|
|
190
|
+
containerRef.current.classList.remove('ptr--fetch-more-treshold-breached');
|
|
191
|
+
}
|
|
192
|
+
if (pullToRefreshThresholdBreached)
|
|
193
|
+
pullToRefreshThresholdBreached = false;
|
|
194
|
+
if (fetchMoreTresholdBreached)
|
|
195
|
+
fetchMoreTresholdBreached = false;
|
|
196
|
+
});
|
|
197
|
+
};
|
|
198
|
+
var onTouchStart = function (e) {
|
|
199
|
+
isDragging = false;
|
|
200
|
+
if (e instanceof MouseEvent) {
|
|
201
|
+
startY = e.pageY;
|
|
202
|
+
}
|
|
203
|
+
if (window.TouchEvent && e instanceof TouchEvent) {
|
|
204
|
+
startY = e.touches[0].pageY;
|
|
205
|
+
}
|
|
206
|
+
currentY = startY;
|
|
207
|
+
// Check if element can be scrolled
|
|
208
|
+
if (e.type === 'touchstart' && isTreeScrollable(e.target, DIRECTION.UP)) {
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
// Top non visible so cancel
|
|
212
|
+
if (childrenRef.current.getBoundingClientRect().top < 0) {
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
isDragging = true;
|
|
216
|
+
};
|
|
217
|
+
var onTouchMove = function (e) {
|
|
218
|
+
if (!isDragging) {
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
if (window.TouchEvent && e instanceof TouchEvent) {
|
|
222
|
+
currentY = e.touches[0].pageY;
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
currentY = e.pageY;
|
|
226
|
+
}
|
|
227
|
+
containerRef.current.classList.add('ptr--dragging');
|
|
228
|
+
if (currentY < startY) {
|
|
229
|
+
isDragging = false;
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
if (e.cancelable) {
|
|
233
|
+
e.preventDefault();
|
|
234
|
+
}
|
|
235
|
+
var yDistanceMoved = Math.min((currentY - startY) / resistance, maxPullDownDistance);
|
|
236
|
+
// Limit to trigger refresh has been breached
|
|
237
|
+
if (yDistanceMoved >= pullDownThreshold) {
|
|
238
|
+
isDragging = true;
|
|
239
|
+
pullToRefreshThresholdBreached = true;
|
|
240
|
+
containerRef.current.classList.remove('ptr--dragging');
|
|
241
|
+
containerRef.current.classList.add('ptr--pull-down-treshold-breached');
|
|
242
|
+
}
|
|
243
|
+
// maxPullDownDistance breached, stop the animation
|
|
244
|
+
if (yDistanceMoved >= maxPullDownDistance) {
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
pullDownRef.current.style.opacity = ((yDistanceMoved) / 65).toString();
|
|
248
|
+
childrenRef.current.style.overflow = 'visible';
|
|
249
|
+
childrenRef.current.style.transform = "translate(0px, " + yDistanceMoved + "px)";
|
|
250
|
+
pullDownRef.current.style.visibility = 'visible';
|
|
251
|
+
};
|
|
252
|
+
var onScroll = function (e) {
|
|
253
|
+
/**
|
|
254
|
+
* Check if component has already called onFetchMore
|
|
255
|
+
*/
|
|
256
|
+
if (fetchMoreTresholdBreached)
|
|
257
|
+
return;
|
|
258
|
+
/**
|
|
259
|
+
* Check if user breached fetchMoreThreshold
|
|
260
|
+
*/
|
|
261
|
+
if (canFetchMore && getScrollToBottomValue() < fetchMoreThreshold && onFetchMore) {
|
|
262
|
+
fetchMoreTresholdBreached = true;
|
|
263
|
+
containerRef.current.classList.add('ptr--fetch-more-treshold-breached');
|
|
264
|
+
onFetchMore().then(initContainer).catch(initContainer);
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
var onEnd = function () {
|
|
268
|
+
isDragging = false;
|
|
269
|
+
startY = 0;
|
|
270
|
+
currentY = 0;
|
|
271
|
+
// Container has not been dragged enough, put it back to it's initial state
|
|
272
|
+
if (!pullToRefreshThresholdBreached) {
|
|
273
|
+
if (pullDownRef.current)
|
|
274
|
+
pullDownRef.current.style.visibility = 'hidden';
|
|
275
|
+
initContainer();
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
if (childrenRef.current) {
|
|
279
|
+
childrenRef.current.style.overflow = 'visible';
|
|
280
|
+
childrenRef.current.style.transform = "translate(0px, " + pullDownThreshold + "px)";
|
|
281
|
+
}
|
|
282
|
+
onRefresh().then(initContainer).catch(initContainer);
|
|
283
|
+
};
|
|
284
|
+
return (React__default.createElement("div", { className: "ptr " + className, style: { backgroundColor: backgroundColor }, ref: containerRef },
|
|
285
|
+
React__default.createElement("div", { className: "ptr__pull-down", ref: pullDownRef },
|
|
286
|
+
React__default.createElement("div", { className: "ptr__loader ptr__pull-down--loading" }, refreshingContent),
|
|
287
|
+
React__default.createElement("div", { className: "ptr__pull-down--pull-more" }, pullingContent)),
|
|
288
|
+
React__default.createElement("div", { className: "ptr__children", ref: childrenRef },
|
|
289
|
+
children,
|
|
290
|
+
React__default.createElement("div", { className: "ptr__fetch-more", ref: fetchMoreRef },
|
|
291
|
+
React__default.createElement("div", { className: "ptr__loader ptr__fetch-more--loading" }, refreshingContent)))));
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
module.exports = PullToRefresh;
|
package/build/index.d.ts
ADDED
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import React, { useRef, useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
var DIRECTION;
|
|
4
|
+
(function (DIRECTION) {
|
|
5
|
+
DIRECTION[DIRECTION["UP"] = -1] = "UP";
|
|
6
|
+
DIRECTION[DIRECTION["DOWN"] = 1] = "DOWN";
|
|
7
|
+
})(DIRECTION || (DIRECTION = {}));
|
|
8
|
+
|
|
9
|
+
function isOverflowScrollable(element) {
|
|
10
|
+
var overflowType = getComputedStyle(element).overflowY;
|
|
11
|
+
if (element === document.scrollingElement && overflowType === 'visible') {
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
if (overflowType !== 'scroll' && overflowType !== 'auto') {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
function isScrollable(element, direction) {
|
|
20
|
+
if (!isOverflowScrollable(element)) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
if (direction === DIRECTION.DOWN) {
|
|
24
|
+
var bottomScroll = element.scrollTop + element.clientHeight;
|
|
25
|
+
return bottomScroll < element.scrollHeight;
|
|
26
|
+
}
|
|
27
|
+
if (direction === DIRECTION.UP) {
|
|
28
|
+
return element.scrollTop > 0;
|
|
29
|
+
}
|
|
30
|
+
throw new Error('unsupported direction');
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Returns whether a given element or any of its ancestors (up to rootElement) is scrollable in a given direction.
|
|
34
|
+
*/
|
|
35
|
+
function isTreeScrollable(element, direction) {
|
|
36
|
+
if (isScrollable(element, direction)) {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
if (element.parentElement == null) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
return isTreeScrollable(element.parentElement, direction);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function styleInject(css, ref) {
|
|
46
|
+
if ( ref === void 0 ) ref = {};
|
|
47
|
+
var insertAt = ref.insertAt;
|
|
48
|
+
|
|
49
|
+
if (!css || typeof document === 'undefined') { return; }
|
|
50
|
+
|
|
51
|
+
var head = document.head || document.getElementsByTagName('head')[0];
|
|
52
|
+
var style = document.createElement('style');
|
|
53
|
+
style.type = 'text/css';
|
|
54
|
+
|
|
55
|
+
if (insertAt === 'top') {
|
|
56
|
+
if (head.firstChild) {
|
|
57
|
+
head.insertBefore(style, head.firstChild);
|
|
58
|
+
} else {
|
|
59
|
+
head.appendChild(style);
|
|
60
|
+
}
|
|
61
|
+
} else {
|
|
62
|
+
head.appendChild(style);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (style.styleSheet) {
|
|
66
|
+
style.styleSheet.cssText = css;
|
|
67
|
+
} else {
|
|
68
|
+
style.appendChild(document.createTextNode(css));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
var css = ".lds-ellipsis {\n display: inline-block;\n position: relative;\n width: 64px;\n height: 64px; }\n\n.lds-ellipsis div {\n position: absolute;\n top: 27px;\n width: 11px;\n height: 11px;\n border-radius: 50%;\n background: #363636;\n animation-timing-function: cubic-bezier(0, 1, 1, 0); }\n\n.lds-ellipsis div:nth-child(1) {\n left: 6px;\n animation: lds-ellipsis1 0.6s infinite; }\n\n.lds-ellipsis div:nth-child(2) {\n left: 6px;\n animation: lds-ellipsis2 0.6s infinite; }\n\n.lds-ellipsis div:nth-child(3) {\n left: 26px;\n animation: lds-ellipsis2 0.6s infinite; }\n\n.lds-ellipsis div:nth-child(4) {\n left: 45px;\n animation: lds-ellipsis3 0.6s infinite; }\n\n@keyframes lds-ellipsis1 {\n 0% {\n transform: scale(0); }\n 100% {\n transform: scale(1); } }\n\n@keyframes lds-ellipsis3 {\n 0% {\n transform: scale(1); }\n 100% {\n transform: scale(0); } }\n\n@keyframes lds-ellipsis2 {\n 0% {\n transform: translate(0, 0); }\n 100% {\n transform: translate(19px, 0); } }\n";
|
|
73
|
+
styleInject(css);
|
|
74
|
+
|
|
75
|
+
// Source: https://loading.io/css/
|
|
76
|
+
var RefreshingContent = function () {
|
|
77
|
+
return (React.createElement("div", { className: "lds-ellipsis" },
|
|
78
|
+
React.createElement("div", null),
|
|
79
|
+
React.createElement("div", null),
|
|
80
|
+
React.createElement("div", null),
|
|
81
|
+
React.createElement("div", null)));
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
var PullingContent = function () {
|
|
85
|
+
return (React.createElement("div", null,
|
|
86
|
+
React.createElement("p", null, "\u21A7\u00A0\u00A0pull to refresh\u00A0\u00A0\u21A7")));
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
var css$1 = ".ptr,\n.ptr__children {\n height: 100%;\n width: 100%;\n overflow: hidden;\n -webkit-overflow-scrolling: touch;\n position: relative; }\n\n.ptr.ptr--fetch-more-treshold-breached .ptr__fetch-more {\n display: block; }\n\n.ptr__fetch-more {\n display: none; }\n\n/**\n * Pull down transition \n */\n.ptr__children,\n.ptr__pull-down {\n transition: transform 0.2s cubic-bezier(0, 0, 0.31, 1); }\n\n.ptr__pull-down {\n position: absolute;\n overflow: hidden;\n left: 0;\n right: 0;\n top: 0;\n visibility: hidden; }\n .ptr__pull-down > div {\n display: none; }\n\n.ptr--dragging {\n /**\n * Hide PullMore content is treshold breached\n */\n /**\n * Otherwize, display content\n */ }\n .ptr--dragging.ptr--pull-down-treshold-breached .ptr__pull-down--pull-more {\n display: none; }\n .ptr--dragging .ptr__pull-down--pull-more {\n display: block; }\n\n.ptr--pull-down-treshold-breached {\n /**\n * Force opacity to 1 is pull down trashold breached\n */\n /**\n * And display loader\n */ }\n .ptr--pull-down-treshold-breached .ptr__pull-down {\n opacity: 1 !important; }\n .ptr--pull-down-treshold-breached .ptr__pull-down--loading {\n display: block; }\n\n.ptr__loader {\n margin: 0 auto;\n text-align: center; }\n";
|
|
90
|
+
styleInject(css$1);
|
|
91
|
+
|
|
92
|
+
var PullToRefresh = function (_a) {
|
|
93
|
+
var _b = _a.isPullable, isPullable = _b === void 0 ? true : _b, _c = _a.canFetchMore, canFetchMore = _c === void 0 ? false : _c, onRefresh = _a.onRefresh, onFetchMore = _a.onFetchMore, _d = _a.refreshingContent, refreshingContent = _d === void 0 ? React.createElement(RefreshingContent, null) : _d, _e = _a.pullingContent, pullingContent = _e === void 0 ? React.createElement(PullingContent, null) : _e, children = _a.children, _f = _a.pullDownThreshold, pullDownThreshold = _f === void 0 ? 67 : _f, _g = _a.fetchMoreThreshold, fetchMoreThreshold = _g === void 0 ? 100 : _g, _h = _a.maxPullDownDistance, maxPullDownDistance = _h === void 0 ? 95 : _h, // max distance to scroll to trigger refresh
|
|
94
|
+
_j = _a.resistance, // max distance to scroll to trigger refresh
|
|
95
|
+
resistance = _j === void 0 ? 1 : _j, backgroundColor = _a.backgroundColor, _k = _a.className, className = _k === void 0 ? '' : _k;
|
|
96
|
+
var containerRef = useRef(null);
|
|
97
|
+
var childrenRef = useRef(null);
|
|
98
|
+
var pullDownRef = useRef(null);
|
|
99
|
+
var fetchMoreRef = useRef(null);
|
|
100
|
+
var pullToRefreshThresholdBreached = false;
|
|
101
|
+
var fetchMoreTresholdBreached = false; // if true, fetchMore loader is displayed
|
|
102
|
+
var isDragging = false;
|
|
103
|
+
var startY = 0;
|
|
104
|
+
var currentY = 0;
|
|
105
|
+
useEffect(function () {
|
|
106
|
+
if (!isPullable || !childrenRef || !childrenRef.current)
|
|
107
|
+
return;
|
|
108
|
+
var childrenEl = childrenRef.current;
|
|
109
|
+
childrenEl.addEventListener('touchstart', onTouchStart, { passive: true });
|
|
110
|
+
childrenEl.addEventListener('mousedown', onTouchStart);
|
|
111
|
+
childrenEl.addEventListener('touchmove', onTouchMove, { passive: false });
|
|
112
|
+
childrenEl.addEventListener('mousemove', onTouchMove);
|
|
113
|
+
window.addEventListener('scroll', onScroll);
|
|
114
|
+
childrenEl.addEventListener('touchend', onEnd);
|
|
115
|
+
childrenEl.addEventListener('mouseup', onEnd);
|
|
116
|
+
document.body.addEventListener('mouseleave', onEnd);
|
|
117
|
+
return function () {
|
|
118
|
+
childrenEl.removeEventListener('touchstart', onTouchStart);
|
|
119
|
+
childrenEl.removeEventListener('mousedown', onTouchStart);
|
|
120
|
+
childrenEl.removeEventListener('touchmove', onTouchMove);
|
|
121
|
+
childrenEl.removeEventListener('mousemove', onTouchMove);
|
|
122
|
+
window.removeEventListener('scroll', onScroll);
|
|
123
|
+
childrenEl.removeEventListener('touchend', onEnd);
|
|
124
|
+
childrenEl.removeEventListener('mouseup', onEnd);
|
|
125
|
+
document.body.removeEventListener('mouseleave', onEnd);
|
|
126
|
+
};
|
|
127
|
+
}, [
|
|
128
|
+
children,
|
|
129
|
+
isPullable,
|
|
130
|
+
onRefresh,
|
|
131
|
+
pullDownThreshold,
|
|
132
|
+
maxPullDownDistance,
|
|
133
|
+
canFetchMore,
|
|
134
|
+
fetchMoreThreshold,
|
|
135
|
+
]);
|
|
136
|
+
/**
|
|
137
|
+
* Check onMount / canFetchMore becomes true
|
|
138
|
+
* if fetchMoreThreshold is already breached
|
|
139
|
+
*/
|
|
140
|
+
useEffect(function () {
|
|
141
|
+
var _a;
|
|
142
|
+
/**
|
|
143
|
+
* Check if it is already in fetching more state
|
|
144
|
+
*/
|
|
145
|
+
if (!((_a = containerRef) === null || _a === void 0 ? void 0 : _a.current))
|
|
146
|
+
return;
|
|
147
|
+
var isAlreadyFetchingMore = containerRef.current.classList.contains('ptr--fetch-more-treshold-breached');
|
|
148
|
+
if (isAlreadyFetchingMore)
|
|
149
|
+
return;
|
|
150
|
+
/**
|
|
151
|
+
* Proceed
|
|
152
|
+
*/
|
|
153
|
+
if (canFetchMore && getScrollToBottomValue() < fetchMoreThreshold && onFetchMore) {
|
|
154
|
+
containerRef.current.classList.add('ptr--fetch-more-treshold-breached');
|
|
155
|
+
fetchMoreTresholdBreached = true;
|
|
156
|
+
onFetchMore().then(initContainer).catch(initContainer);
|
|
157
|
+
}
|
|
158
|
+
}, [canFetchMore, children]);
|
|
159
|
+
/**
|
|
160
|
+
* Returns distance to bottom of the container
|
|
161
|
+
*/
|
|
162
|
+
var getScrollToBottomValue = function () {
|
|
163
|
+
if (!childrenRef || !childrenRef.current)
|
|
164
|
+
return -1;
|
|
165
|
+
var scrollTop = window.scrollY; // is the pixels hidden in top due to the scroll. With no scroll its value is 0.
|
|
166
|
+
var scrollHeight = childrenRef.current.scrollHeight; // is the pixels of the whole container
|
|
167
|
+
return scrollHeight - scrollTop - window.innerHeight;
|
|
168
|
+
};
|
|
169
|
+
var initContainer = function () {
|
|
170
|
+
requestAnimationFrame(function () {
|
|
171
|
+
/**
|
|
172
|
+
* Reset Styles
|
|
173
|
+
*/
|
|
174
|
+
if (childrenRef.current) {
|
|
175
|
+
childrenRef.current.style.overflowX = 'hidden';
|
|
176
|
+
childrenRef.current.style.overflowY = 'auto';
|
|
177
|
+
childrenRef.current.style.transform = "unset";
|
|
178
|
+
}
|
|
179
|
+
if (pullDownRef.current) {
|
|
180
|
+
pullDownRef.current.style.opacity = '0';
|
|
181
|
+
}
|
|
182
|
+
if (containerRef.current) {
|
|
183
|
+
containerRef.current.classList.remove('ptr--pull-down-treshold-breached');
|
|
184
|
+
containerRef.current.classList.remove('ptr--dragging');
|
|
185
|
+
containerRef.current.classList.remove('ptr--fetch-more-treshold-breached');
|
|
186
|
+
}
|
|
187
|
+
if (pullToRefreshThresholdBreached)
|
|
188
|
+
pullToRefreshThresholdBreached = false;
|
|
189
|
+
if (fetchMoreTresholdBreached)
|
|
190
|
+
fetchMoreTresholdBreached = false;
|
|
191
|
+
});
|
|
192
|
+
};
|
|
193
|
+
var onTouchStart = function (e) {
|
|
194
|
+
isDragging = false;
|
|
195
|
+
if (e instanceof MouseEvent) {
|
|
196
|
+
startY = e.pageY;
|
|
197
|
+
}
|
|
198
|
+
if (window.TouchEvent && e instanceof TouchEvent) {
|
|
199
|
+
startY = e.touches[0].pageY;
|
|
200
|
+
}
|
|
201
|
+
currentY = startY;
|
|
202
|
+
// Check if element can be scrolled
|
|
203
|
+
if (e.type === 'touchstart' && isTreeScrollable(e.target, DIRECTION.UP)) {
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
// Top non visible so cancel
|
|
207
|
+
if (childrenRef.current.getBoundingClientRect().top < 0) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
isDragging = true;
|
|
211
|
+
};
|
|
212
|
+
var onTouchMove = function (e) {
|
|
213
|
+
if (!isDragging) {
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
if (window.TouchEvent && e instanceof TouchEvent) {
|
|
217
|
+
currentY = e.touches[0].pageY;
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
currentY = e.pageY;
|
|
221
|
+
}
|
|
222
|
+
containerRef.current.classList.add('ptr--dragging');
|
|
223
|
+
if (currentY < startY) {
|
|
224
|
+
isDragging = false;
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
if (e.cancelable) {
|
|
228
|
+
e.preventDefault();
|
|
229
|
+
}
|
|
230
|
+
var yDistanceMoved = Math.min((currentY - startY) / resistance, maxPullDownDistance);
|
|
231
|
+
// Limit to trigger refresh has been breached
|
|
232
|
+
if (yDistanceMoved >= pullDownThreshold) {
|
|
233
|
+
isDragging = true;
|
|
234
|
+
pullToRefreshThresholdBreached = true;
|
|
235
|
+
containerRef.current.classList.remove('ptr--dragging');
|
|
236
|
+
containerRef.current.classList.add('ptr--pull-down-treshold-breached');
|
|
237
|
+
}
|
|
238
|
+
// maxPullDownDistance breached, stop the animation
|
|
239
|
+
if (yDistanceMoved >= maxPullDownDistance) {
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
pullDownRef.current.style.opacity = ((yDistanceMoved) / 65).toString();
|
|
243
|
+
childrenRef.current.style.overflow = 'visible';
|
|
244
|
+
childrenRef.current.style.transform = "translate(0px, " + yDistanceMoved + "px)";
|
|
245
|
+
pullDownRef.current.style.visibility = 'visible';
|
|
246
|
+
};
|
|
247
|
+
var onScroll = function (e) {
|
|
248
|
+
/**
|
|
249
|
+
* Check if component has already called onFetchMore
|
|
250
|
+
*/
|
|
251
|
+
if (fetchMoreTresholdBreached)
|
|
252
|
+
return;
|
|
253
|
+
/**
|
|
254
|
+
* Check if user breached fetchMoreThreshold
|
|
255
|
+
*/
|
|
256
|
+
if (canFetchMore && getScrollToBottomValue() < fetchMoreThreshold && onFetchMore) {
|
|
257
|
+
fetchMoreTresholdBreached = true;
|
|
258
|
+
containerRef.current.classList.add('ptr--fetch-more-treshold-breached');
|
|
259
|
+
onFetchMore().then(initContainer).catch(initContainer);
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
var onEnd = function () {
|
|
263
|
+
isDragging = false;
|
|
264
|
+
startY = 0;
|
|
265
|
+
currentY = 0;
|
|
266
|
+
// Container has not been dragged enough, put it back to it's initial state
|
|
267
|
+
if (!pullToRefreshThresholdBreached) {
|
|
268
|
+
if (pullDownRef.current)
|
|
269
|
+
pullDownRef.current.style.visibility = 'hidden';
|
|
270
|
+
initContainer();
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
if (childrenRef.current) {
|
|
274
|
+
childrenRef.current.style.overflow = 'visible';
|
|
275
|
+
childrenRef.current.style.transform = "translate(0px, " + pullDownThreshold + "px)";
|
|
276
|
+
}
|
|
277
|
+
onRefresh().then(initContainer).catch(initContainer);
|
|
278
|
+
};
|
|
279
|
+
return (React.createElement("div", { className: "ptr " + className, style: { backgroundColor: backgroundColor }, ref: containerRef },
|
|
280
|
+
React.createElement("div", { className: "ptr__pull-down", ref: pullDownRef },
|
|
281
|
+
React.createElement("div", { className: "ptr__loader ptr__pull-down--loading" }, refreshingContent),
|
|
282
|
+
React.createElement("div", { className: "ptr__pull-down--pull-more" }, pullingContent)),
|
|
283
|
+
React.createElement("div", { className: "ptr__children", ref: childrenRef },
|
|
284
|
+
children,
|
|
285
|
+
React.createElement("div", { className: "ptr__fetch-more", ref: fetchMoreRef },
|
|
286
|
+
React.createElement("div", { className: "ptr__loader ptr__fetch-more--loading" }, refreshingContent)))));
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
export default PullToRefresh;
|
package/build/types.d.ts
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-simple-pull-to-refresh",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.2",
|
|
4
4
|
"description": "A Simple Pull To Refresh Component for React Application",
|
|
5
5
|
"main": "build/index.cjs.js",
|
|
6
6
|
"module": "build/index.esm.js",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"@types/react": "^16.9.9",
|
|
32
32
|
"@types/react-dom": "^16.9.2",
|
|
33
33
|
"ncp": "^2.0.0",
|
|
34
|
-
"node-sass": "^
|
|
34
|
+
"node-sass": "^7.0.1",
|
|
35
35
|
"npm-run-all": "^4.1.5",
|
|
36
36
|
"rollup": "^1.25.0",
|
|
37
37
|
"rollup-plugin-delete": "^1.1.0",
|