@shopgate/pwa-common 7.30.0-alpha.6 → 7.30.0-alpha.8
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/App.js +48 -6
- package/action-creators/app/index.js +75 -12
- package/action-creators/app/spec.js +96 -1
- package/action-creators/client/index.js +27 -5
- package/action-creators/client/spec.js +44 -1
- package/action-creators/error/index.js +15 -3
- package/action-creators/index.js +9 -1
- package/action-creators/menu/index.js +23 -4
- package/action-creators/menu/spec.js +37 -1
- package/action-creators/modal/index.js +15 -3
- package/action-creators/modal/spec.js +26 -1
- package/action-creators/page/index.js +24 -4
- package/action-creators/page/spec.js +38 -1
- package/action-creators/router/index.js +48 -7
- package/action-creators/url/index.js +24 -4
- package/action-creators/url/spec.js +45 -1
- package/action-creators/user/index.js +90 -13
- package/action-creators/user/spec.js +186 -2
- package/actions/app/handleDeepLink.js +11 -2
- package/actions/app/handleLink.js +62 -6
- package/actions/app/handlePushNotification.js +32 -4
- package/actions/app/handleUniversalLink.js +11 -2
- package/actions/app/registerLinkEvents.js +24 -3
- package/actions/client/fetchClientInformation.js +26 -2
- package/actions/menu/fetchMenu.js +23 -2
- package/actions/modal/closeModal.js +18 -2
- package/actions/modal/promiseMap.js +3 -1
- package/actions/modal/showModal.js +54 -8
- package/actions/page/fetchPageConfig.js +69 -2
- package/actions/page/getPageConfig.js +6 -2
- package/actions/page/index.js +1 -1
- package/actions/router/historyPop.js +12 -2
- package/actions/router/historyPopToRoute.js +27 -2
- package/actions/router/historyPush.js +12 -2
- package/actions/router/historyRedirect.js +21 -2
- package/actions/router/historyReplace.js +20 -3
- package/actions/router/historyReset.js +11 -2
- package/actions/router/historyResetTo.js +12 -2
- package/actions/router/index.js +17 -1
- package/actions/router/routeDidPop.js +11 -2
- package/actions/router/routeDidPush.js +13 -2
- package/actions/router/routeDidReplace.js +11 -2
- package/actions/router/routeDidReset.js +11 -2
- package/actions/router/routeDidUpdate.js +10 -2
- package/actions/router/routeWillPop.js +11 -2
- package/actions/router/routeWillPush.js +13 -2
- package/actions/router/routeWillReplace.js +11 -2
- package/actions/router/routeWillReset.js +11 -2
- package/actions/router/windowOpenOverride.js +10 -2
- package/actions/user/fetchRegisterUrl.js +36 -2
- package/actions/user/fetchUser.js +29 -3
- package/actions/user/getUser.js +6 -2
- package/actions/user/index.js +1 -1
- package/actions/user/login.js +76 -9
- package/actions/user/logout.js +30 -2
- package/collections/AuthRoutes.js +73 -14
- package/collections/Configuration.js +54 -7
- package/collections/EmbeddedMedia.js +84 -11
- package/collections/PersistedReducers.js +41 -6
- package/collections/Redirects.js +103 -17
- package/collections/index.js +5 -1
- package/collections/media-providers/MediaProvider.js +151 -26
- package/collections/media-providers/Vimeo.js +113 -19
- package/collections/media-providers/YouTube.js +74 -14
- package/collections/media-providers/index.js +3 -1
- package/collections/media-providers/style.js +52 -2
- package/components/Backdrop/index.js +95 -6
- package/components/Backdrop/spec.js +23 -1
- package/components/Backdrop/style.js +11 -2
- package/components/Button/index.js +47 -5
- package/components/Button/spec.js +36 -1
- package/components/Button/style.js +6 -1
- package/components/Checkbox/index.js +126 -32
- package/components/Checkbox/spec.js +94 -3
- package/components/Consume/helpers/buildParams.js +13 -2
- package/components/Consume/index.js +14 -2
- package/components/CountdownTimer/index.js +115 -17
- package/components/CountdownTimer/spec.js +126 -12
- package/components/Drawer/index.js +131 -16
- package/components/Drawer/spec.js +76 -1
- package/components/Drawer/style.js +37 -1
- package/components/Dropdown/index.js +65 -6
- package/components/Dropdown/style.js +4 -1
- package/components/Dropdown/transitions.js +34 -1
- package/components/Ellipsis/index.js +16 -2
- package/components/Ellipsis/spec.js +13 -1
- package/components/EmbeddedMedia/index.js +56 -6
- package/components/EmbeddedMedia/spec.js +52 -3
- package/components/ErrorBoundary/connector.js +9 -2
- package/components/ErrorBoundary/index.js +43 -7
- package/components/Grid/components/Item/index.js +40 -4
- package/components/Grid/components/Item/spec.js +23 -1
- package/components/Grid/components/Item/style.js +17 -3
- package/components/Grid/index.js +36 -4
- package/components/Grid/spec.js +23 -1
- package/components/Grid/style.js +11 -2
- package/components/HtmlSanitizer/connector.js +24 -3
- package/components/HtmlSanitizer/index.js +104 -12
- package/components/HtmlSanitizer/spec.js +207 -6
- package/components/I18n/components/FormatDate/index.js +26 -2
- package/components/I18n/components/FormatDate/spec.js +46 -1
- package/components/I18n/components/FormatNumber/index.js +34 -2
- package/components/I18n/components/FormatNumber/spec.js +41 -2
- package/components/I18n/components/FormatPrice/index.js +32 -2
- package/components/I18n/components/FormatPrice/spec.js +46 -1
- package/components/I18n/components/FormatTime/index.js +26 -2
- package/components/I18n/components/FormatTime/spec.js +43 -2
- package/components/I18n/components/I18nProvider/index.js +52 -9
- package/components/I18n/components/I18nProvider/spec.js +39 -1
- package/components/I18n/components/Placeholder/index.js +8 -2
- package/components/I18n/components/Placeholder/spec.js +30 -1
- package/components/I18n/components/Translate/index.js +68 -7
- package/components/I18n/components/Translate/spec.js +30 -1
- package/components/I18n/index.js +16 -1
- package/components/Icon/index.js +25 -2
- package/components/Icon/style.js +6 -1
- package/components/Image/Image.js +176 -19
- package/components/Image/ImageInner.js +48 -2
- package/components/Image/index.js +1 -1
- package/components/Image/style.js +29 -2
- package/components/InfiniteContainer/index.js +381 -49
- package/components/InfiniteContainer/spec.js +199 -10
- package/components/Input/components/DateInput.js +262 -6
- package/components/Input/components/MultiLineInput.js +98 -12
- package/components/Input/components/SimpleInput.js +207 -31
- package/components/Input/index.js +32 -3
- package/components/Input/spec.js +122 -1
- package/components/KeyboardConsumer/index.js +48 -7
- package/components/Link/connector.js +7 -1
- package/components/Link/index.js +96 -11
- package/components/Link/spec.js +56 -1
- package/components/Link/style.js +10 -1
- package/components/List/components/Item/index.js +35 -3
- package/components/List/components/Item/style.js +16 -1
- package/components/List/index.js +20 -2
- package/components/List/spec.js +31 -1
- package/components/Loading/index.js +6 -2
- package/components/Modal/index.js +38 -3
- package/components/Modal/style.js +36 -1
- package/components/ModalContainer/connector.js +17 -3
- package/components/ModalContainer/index.js +36 -3
- package/components/ModalContainer/spec.js +105 -5
- package/components/Picker/components/Button/index.js +34 -2
- package/components/Picker/components/Button/style.js +19 -1
- package/components/Picker/components/List/index.js +33 -2
- package/components/Picker/components/List/style.js +17 -1
- package/components/Picker/components/Modal/index.js +60 -7
- package/components/Picker/components/Modal/style.js +78 -1
- package/components/Picker/index.js +167 -21
- package/components/Picker/spec.js +83 -2
- package/components/Portal/index.js +130 -19
- package/components/ProductCharacteristics/connector.js +33 -4
- package/components/ProductCharacteristics/context.js +2 -1
- package/components/ProductCharacteristics/helpers/index.js +135 -21
- package/components/ProductCharacteristics/index.js +266 -31
- package/components/RangeSlider/components/Handle/index.js +25 -2
- package/components/RangeSlider/components/Handle/style.js +14 -1
- package/components/RangeSlider/helper.js +43 -8
- package/components/RangeSlider/index.js +228 -38
- package/components/RangeSlider/style.js +14 -1
- package/components/Route/RouteNotFound.js +46 -3
- package/components/Route/index.js +78 -10
- package/components/Router/connector.js +9 -2
- package/components/Router/index.js +237 -31
- package/components/ScannerContainer/connector.js +9 -2
- package/components/ScannerContainer/index.js +42 -6
- package/components/Select/components/Item/index.js +20 -4
- package/components/Select/components/Item/style.js +4 -1
- package/components/Select/index.js +149 -28
- package/components/Select/spec.js +86 -2
- package/components/Select/style.js +17 -1
- package/components/SelectBox/components/Item/index.js +47 -5
- package/components/SelectBox/components/Item/style.js +7 -1
- package/components/SelectBox/index.js +173 -17
- package/components/SelectBox/spec.js +59 -3
- package/components/SelectBox/style.js +18 -1
- package/components/Slider/index.js +6 -2
- package/components/SurroundPortals/index.js +26 -2
- package/components/Swiper/components/SwiperItem/index.js +28 -4
- package/components/Swiper/components/SwiperItem/spec.js +17 -1
- package/components/Swiper/components/SwiperItem/styles.js +5 -1
- package/components/Swiper/index.js +210 -18
- package/components/Swiper/styles.js +75 -7
- package/components/Toaster/index.js +10 -2
- package/components/Transition/index.js +89 -13
- package/components/Widgets/components/Widget/index.js +52 -4
- package/components/Widgets/components/Widget/spec.js +68 -3
- package/components/Widgets/components/Widget/style.js +21 -3
- package/components/Widgets/components/WidgetGrid/index.js +52 -7
- package/components/Widgets/components/WidgetGrid/spec.js +46 -2
- package/components/Widgets/components/WidgetGrid/style.js +8 -1
- package/components/Widgets/helpers/shouldShowWidget.js +44 -7
- package/components/Widgets/index.js +127 -15
- package/components/Widgets/spec.js +213 -6
- package/components/index.js +9 -1
- package/constants/ActionTypes.js +97 -19
- package/constants/Configuration.js +12 -2
- package/constants/Device.js +29 -2
- package/constants/DisplayOptions.js +8 -1
- package/constants/MenuIDs.js +2 -1
- package/constants/ModalTypes.js +1 -1
- package/constants/PageIDs.js +1 -1
- package/constants/Pipelines.js +7 -1
- package/constants/Portals.js +136 -3
- package/constants/Registration.js +3 -1
- package/constants/RoutePaths.js +13 -2
- package/constants/Tracking.js +3 -1
- package/constants/client.js +6 -1
- package/constants/ui.js +2 -1
- package/constants/user.js +6 -2
- package/context/index.js +33 -3
- package/helpers/config/index.js +139 -21
- package/helpers/config/mock.js +200 -8
- package/helpers/config/theme.js +50 -4
- package/helpers/data/index.js +204 -29
- package/helpers/data/spec.js +187 -7
- package/helpers/date/index.js +58 -6
- package/helpers/date/spec.js +92 -1
- package/helpers/dom/index.js +48 -11
- package/helpers/environment/index.js +14 -2
- package/helpers/html/decodeHTML.js +7 -1
- package/helpers/html/handleDOM.js +172 -21
- package/helpers/html/parseHTML.js +67 -12
- package/helpers/i18n/getDateFormatter.js +23 -4
- package/helpers/i18n/getNumberFormatter.js +32 -4
- package/helpers/i18n/getPriceFormatter.js +38 -4
- package/helpers/i18n/getTimeFormatter.js +23 -4
- package/helpers/i18n/getTranslator.js +62 -8
- package/helpers/i18n/index.js +5 -1
- package/helpers/i18n/mergeTranslations.js +36 -9
- package/helpers/i18n/messageCache.js +3 -1
- package/helpers/legacy/index.js +47 -9
- package/helpers/modal/withShowModal.js +13 -2
- package/helpers/portals/portalCollection.js +28 -6
- package/helpers/portals/routePortals.js +12 -1
- package/helpers/redux/compareObjects.js +7 -2
- package/helpers/redux/generateResultHash.js +36 -3
- package/helpers/redux/generateSortedHash.js +7 -2
- package/helpers/redux/hasExpired.js +10 -2
- package/helpers/redux/index.js +7 -1
- package/helpers/redux/mutable.js +143 -24
- package/helpers/redux/shouldFetchData.js +46 -10
- package/helpers/redux/shouldFetchFilters.js +17 -4
- package/helpers/router/index.js +49 -5
- package/helpers/style/index.js +43 -4
- package/helpers/style/spec.js +108 -2
- package/helpers/tracking/index.js +52 -9
- package/helpers/validation/index.js +39 -12
- package/helpers/validation/spec.js +10 -1
- package/package.json +3 -3
- package/providers/index.js +4 -1
- package/providers/loading/context.js +2 -1
- package/providers/loading/index.js +137 -22
- package/providers/toast/context.js +2 -1
- package/providers/toast/index.js +105 -11
- package/reducers/client/connectivity.js +22 -2
- package/reducers/client/index.js +7 -1
- package/reducers/client/info.js +27 -2
- package/reducers/index.js +23 -4
- package/reducers/menu/index.js +5 -1
- package/reducers/menu/menusById.js +41 -2
- package/reducers/modal/index.js +14 -2
- package/reducers/page/index.js +68 -5
- package/reducers/router/index.js +48 -2
- package/reducers/url/index.js +42 -3
- package/reducers/user/data.js +27 -2
- package/reducers/user/index.js +7 -1
- package/reducers/user/login.js +65 -2
- package/selectors/client.js +138 -21
- package/selectors/history.js +49 -11
- package/selectors/menu.js +34 -6
- package/selectors/modal.js +15 -4
- package/selectors/page.js +25 -4
- package/selectors/router.js +154 -30
- package/selectors/url.js +25 -4
- package/selectors/user.js +90 -13
- package/store/index.js +60 -6
- package/store/middelwares/logger.js +7 -1
- package/store/middelwares/streams.js +19 -2
- package/streams/app.js +60 -8
- package/streams/client.js +8 -2
- package/streams/error.js +14 -3
- package/streams/index.js +6 -1
- package/streams/interval.js +6 -2
- package/streams/main.js +27 -2
- package/streams/router.js +45 -8
- package/streams/user.js +89 -15
- package/streams/view.js +97 -25
- package/styles/reset/form.js +57 -5
- package/styles/reset/index.js +6 -1
- package/styles/reset/media.js +22 -1
- package/styles/reset/root.js +33 -1
- package/styles/reset/table.js +10 -1
- package/styles/reset/typography.js +26 -1
- package/subscriptions/app.js +148 -17
- package/subscriptions/error.js +292 -13
- package/subscriptions/helpers/buildRegisterUrl.js +25 -6
- package/subscriptions/helpers/clearUpInAppBrowser.js +14 -3
- package/subscriptions/helpers/handleLinks.js +267 -25
- package/subscriptions/helpers/pipeline.js +12 -1
- package/subscriptions/history.js +34 -6
- package/subscriptions/index.js +25 -4
- package/subscriptions/menu.js +22 -5
- package/subscriptions/mock.js +39 -7
- package/subscriptions/router.js +336 -23
- package/subscriptions/user.js +93 -3
|
@@ -1,96 +1,360 @@
|
|
|
1
|
-
import
|
|
1
|
+
import React, { Component } from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import throttle from 'lodash/throttle';
|
|
4
|
+
import isEqual from 'lodash/isEqual';
|
|
5
|
+
import { router } from '@virtuous/conductor';
|
|
6
|
+
import { RouteContext } from "../../context";
|
|
7
|
+
import { ITEMS_PER_LOAD } from "../../constants/DisplayOptions";
|
|
8
|
+
|
|
9
|
+
/**
|
|
2
10
|
* This component receives a data source and will then load
|
|
3
11
|
* more items from it when the user reaches the end of the
|
|
4
12
|
* (parent) scroll container.
|
|
5
|
-
*/
|
|
13
|
+
*/
|
|
14
|
+
class InfiniteContainer extends Component {
|
|
15
|
+
/**
|
|
6
16
|
* The component constructor.
|
|
7
17
|
* @param {Object} props The component props.
|
|
8
18
|
* @param {Object} context The component context.
|
|
9
|
-
*/
|
|
19
|
+
*/
|
|
20
|
+
constructor(props, context) {
|
|
21
|
+
super(props, context);
|
|
22
|
+
this.domElement = null;
|
|
23
|
+
this.domScrollContainer = null;
|
|
24
|
+
/**
|
|
10
25
|
* 10ms was chosen because, on the one hand, it prevents the scroll event from flooding but,
|
|
11
26
|
* on the other hand, it does not hinder users that scroll quickly from reloading next chunk.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
27
|
+
*/
|
|
28
|
+
this.handleLoadingProxy = throttle(() => {
|
|
29
|
+
if (props.enablePromiseBasedLoading) {
|
|
30
|
+
this.handleLoadingPromise();
|
|
31
|
+
} else {
|
|
32
|
+
this.handleLoading();
|
|
33
|
+
}
|
|
34
|
+
}, 10);
|
|
35
|
+
|
|
36
|
+
// A flag to prevent concurrent loading requests.
|
|
37
|
+
this.isLoading = false;
|
|
38
|
+
|
|
39
|
+
// Determine the initial offset of items.
|
|
40
|
+
const {
|
|
41
|
+
items,
|
|
42
|
+
limit,
|
|
43
|
+
initialLimit
|
|
44
|
+
} = props;
|
|
45
|
+
const currentOffset = items.length ? initialLimit : limit;
|
|
46
|
+
const {
|
|
47
|
+
state: {
|
|
48
|
+
offset = 0
|
|
49
|
+
} = {}
|
|
50
|
+
} = context || {};
|
|
51
|
+
this.state = {
|
|
52
|
+
itemCount: items.length,
|
|
53
|
+
offset: [offset, currentOffset],
|
|
54
|
+
// A state flag that will be true as long as we await more items.
|
|
55
|
+
// The loading indicator will be shown accordingly.
|
|
56
|
+
awaitingItems: true
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
17
61
|
* When the component is mounted, it tries to find a proper
|
|
18
62
|
* parent scroll container if available.
|
|
19
63
|
* After that it calls for the initial data to load.
|
|
20
|
-
*/
|
|
21
|
-
|
|
64
|
+
*/
|
|
65
|
+
componentDidMount() {
|
|
66
|
+
const {
|
|
67
|
+
current
|
|
68
|
+
} = this.props.containerRef;
|
|
69
|
+
if (current) {
|
|
70
|
+
this.domScrollContainer = current;
|
|
71
|
+
this.bindEvents();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Initially request items if none received.
|
|
75
|
+
if (!this.props.items.length) {
|
|
76
|
+
const [start] = this.state.offset;
|
|
77
|
+
this.props.loader(start);
|
|
78
|
+
}
|
|
79
|
+
this.verifyAllDone();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
22
83
|
* Checks if the component received new items or already received all items.
|
|
23
84
|
* @param {Object} nextProps The next props.
|
|
24
|
-
*/
|
|
85
|
+
*/
|
|
86
|
+
UNSAFE_componentWillReceiveProps(nextProps) {
|
|
87
|
+
/**
|
|
25
88
|
* Downstream logic to process the props. It's wrapped into a separate function, since it might
|
|
26
89
|
* be executed after the state was updated to avoid race conditions.
|
|
27
|
-
*/
|
|
28
|
-
|
|
90
|
+
*/
|
|
91
|
+
const finalize = () => {
|
|
92
|
+
const {
|
|
93
|
+
current
|
|
94
|
+
} = nextProps.containerRef;
|
|
95
|
+
if (!this.domScrollContainer && current) {
|
|
96
|
+
this.domScrollContainer = current;
|
|
97
|
+
this.bindEvents();
|
|
98
|
+
}
|
|
99
|
+
if (this.receivedTotalItems(nextProps)) {
|
|
100
|
+
// Trigger loading if totalItems are available
|
|
101
|
+
if (nextProps.enablePromiseBasedLoading) {
|
|
102
|
+
this.handleLoadingPromise(true, nextProps);
|
|
103
|
+
} else {
|
|
104
|
+
this.handleLoading(true, nextProps);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
this.verifyAllDone(nextProps);
|
|
108
|
+
};
|
|
109
|
+
if (nextProps.requestHash !== this.props.requestHash) {
|
|
110
|
+
this.resetComponent(() => {
|
|
111
|
+
finalize();
|
|
112
|
+
});
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
if (nextProps.items.length >= this.state.itemCount) {
|
|
116
|
+
this.setState({
|
|
117
|
+
itemCount: nextProps.items.length
|
|
118
|
+
}, finalize());
|
|
119
|
+
} else {
|
|
120
|
+
this.resetComponent(() => {
|
|
121
|
+
finalize();
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
29
127
|
* Let the component only update when props.items or state changes.
|
|
30
128
|
* @param {Object} nextProps The next component props.
|
|
31
129
|
* @param {Object} nextState The next component state.
|
|
32
130
|
* @returns {boolean}
|
|
33
|
-
*/
|
|
131
|
+
*/
|
|
132
|
+
shouldComponentUpdate(nextProps, nextState) {
|
|
133
|
+
return !isEqual(this.props.containerRef, nextProps.containerRef) || !isEqual(this.props.columns, nextProps.columns) || !isEqual(this.props.items, nextProps.items) || !isEqual(this.state, nextState);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
34
137
|
* Reset the loading flag.
|
|
35
|
-
*/
|
|
36
|
-
|
|
37
|
-
// is
|
|
38
|
-
|
|
138
|
+
*/
|
|
139
|
+
componentDidUpdate() {
|
|
140
|
+
// When promise based implementation is active, `isLoading` is reset when response comes in.
|
|
141
|
+
// In the legacy implementation this happens after the fetched items reached the component and
|
|
142
|
+
// is not necessary here anymore.
|
|
143
|
+
if (!this.props.enablePromiseBasedLoading) {
|
|
144
|
+
this.isLoading = false;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
39
149
|
* When the component will unmount it unbinds all previously bound event listeners.
|
|
40
|
-
*/
|
|
150
|
+
*/
|
|
151
|
+
componentWillUnmount() {
|
|
152
|
+
router.update(this.context.id, {
|
|
153
|
+
offset: this.state.offset[0]
|
|
154
|
+
}, false);
|
|
155
|
+
this.unbindEvents();
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
41
159
|
* Adds scroll event listeners to the scroll container if available.
|
|
42
|
-
*/
|
|
160
|
+
*/
|
|
161
|
+
bindEvents() {
|
|
162
|
+
if (this.domScrollContainer) {
|
|
163
|
+
this.domScrollContainer.addEventListener('scroll', this.handleLoadingProxy);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
43
168
|
* Removes scroll event listeners from the scroll container.
|
|
44
169
|
* @param {Node} container A reference to an old scroll container.
|
|
45
|
-
*/
|
|
170
|
+
*/
|
|
171
|
+
unbindEvents(container) {
|
|
172
|
+
if (container) {
|
|
173
|
+
container.removeEventListener('scroll', this.handleLoadingProxy);
|
|
174
|
+
} else if (this.domScrollContainer) {
|
|
175
|
+
this.domScrollContainer.removeEventListener('scroll', this.handleLoadingProxy);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
46
180
|
* Tests if there are more items to be received via items prop.
|
|
47
181
|
* @param {Object} [props] The current or next component props.
|
|
48
182
|
* @returns {boolean}
|
|
49
|
-
*/
|
|
183
|
+
*/
|
|
184
|
+
needsToReceiveItems(props = this.props) {
|
|
185
|
+
return props.totalItems === null || props.items.length < props.totalItems;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
50
189
|
* Tests if the total amount of items has been received via totalItems prop.
|
|
51
190
|
* @param {Object} nextProps The next component props.
|
|
52
191
|
* @returns {boolean}
|
|
53
|
-
*/
|
|
192
|
+
*/
|
|
193
|
+
receivedTotalItems(nextProps) {
|
|
194
|
+
return nextProps.totalItems !== null && nextProps.totalItems !== this.props.totalItems;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
54
198
|
* Tests if all items have been received and are visible based on current offset.
|
|
55
199
|
* @param {Object} [props] The current or next component props.
|
|
56
200
|
* @returns {boolean}
|
|
57
|
-
*/
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
201
|
+
*/
|
|
202
|
+
allItemsAreRendered(props = this.props) {
|
|
203
|
+
const {
|
|
204
|
+
totalItems,
|
|
205
|
+
items
|
|
206
|
+
} = props;
|
|
207
|
+
const [offset, limit] = this.state.offset;
|
|
208
|
+
if (props.enablePromiseBasedLoading) {
|
|
209
|
+
// At promise based loading the offset is increased after the response came in.
|
|
210
|
+
// This method is invoked to evaluate if a new request needs to be dispatched, so we check
|
|
211
|
+
// against the current offset state.
|
|
212
|
+
return totalItems !== null && (offset >= totalItems || offset === 0 && Array.isArray(items) && items.length === totalItems);
|
|
213
|
+
}
|
|
214
|
+
return !this.needsToReceiveItems(props) && offset + limit >= totalItems;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
61
218
|
* Increases the current offset by limit (from props).
|
|
62
219
|
* @returns {Object}
|
|
63
|
-
*/
|
|
220
|
+
*/
|
|
221
|
+
increaseOffset() {
|
|
222
|
+
const [start, length] = this.state.offset;
|
|
223
|
+
let newOffset = start + length;
|
|
224
|
+
|
|
225
|
+
/**
|
|
64
226
|
* When items are cached, the initial limit can be "6".
|
|
65
227
|
* Then, new offset should be limited to the "normal" limit (30).
|
|
66
228
|
* Otherwise, with cached items, this component would skip the initial number of items
|
|
67
229
|
* when the cache is out.
|
|
68
|
-
*/
|
|
69
|
-
|
|
230
|
+
*/
|
|
231
|
+
if (start % this.props.limit) {
|
|
232
|
+
// Example: when 6, bump to 30, not 36.
|
|
233
|
+
newOffset = this.props.limit;
|
|
234
|
+
}
|
|
235
|
+
this.setState({
|
|
236
|
+
offset: [newOffset, this.props.limit]
|
|
237
|
+
});
|
|
238
|
+
return {
|
|
239
|
+
offset: newOffset,
|
|
240
|
+
limit: this.props.limit
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
70
245
|
* Resets the state.
|
|
71
246
|
* @param {Function} callback A callback which is invoked after the state was updated.
|
|
72
247
|
* This is necessary to avoid race conditions with downstream code.
|
|
73
|
-
*/
|
|
248
|
+
*/
|
|
249
|
+
resetComponent(callback) {
|
|
250
|
+
this.setState({
|
|
251
|
+
offset: [0, this.props.limit],
|
|
252
|
+
awaitingItems: true,
|
|
253
|
+
itemCount: 0
|
|
254
|
+
}, () => {
|
|
255
|
+
this.unbindEvents();
|
|
256
|
+
this.bindEvents();
|
|
257
|
+
callback();
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
74
262
|
* Stops the lazy loading processes
|
|
75
|
-
*/
|
|
263
|
+
*/
|
|
264
|
+
stopLazyLoading() {
|
|
265
|
+
this.setState({
|
|
266
|
+
awaitingItems: false
|
|
267
|
+
});
|
|
268
|
+
this.unbindEvents();
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
76
272
|
* Verifies if all items are loaded and shown, then set final state and unbind events.
|
|
77
273
|
* @param {Object} [props] The current or next component props.
|
|
78
274
|
* @returns {boolean} Returns true if the component has reached the final state.
|
|
79
|
-
*/
|
|
275
|
+
*/
|
|
276
|
+
verifyAllDone(props = this.props) {
|
|
277
|
+
if (this.allItemsAreRendered(props)) {
|
|
278
|
+
this.stopLazyLoading();
|
|
279
|
+
return true;
|
|
280
|
+
}
|
|
281
|
+
return false;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
80
285
|
* Tests if the current scroll position is near the bottom
|
|
81
286
|
* of the scroll container.
|
|
82
287
|
* @returns {boolean}
|
|
83
|
-
*/
|
|
288
|
+
*/
|
|
289
|
+
validateScrollPosition() {
|
|
290
|
+
if (!this.domScrollContainer) {
|
|
291
|
+
return true;
|
|
292
|
+
}
|
|
293
|
+
let scrollTop;
|
|
294
|
+
let scrollHeight;
|
|
295
|
+
let clientHeight;
|
|
296
|
+
if (this.domScrollContainer === window) {
|
|
297
|
+
const body = document.querySelector('body');
|
|
298
|
+
scrollTop = window.scrollY;
|
|
299
|
+
({
|
|
300
|
+
scrollHeight,
|
|
301
|
+
clientHeight
|
|
302
|
+
} = body);
|
|
303
|
+
} else {
|
|
304
|
+
({
|
|
305
|
+
scrollTop,
|
|
306
|
+
scrollHeight,
|
|
307
|
+
clientHeight
|
|
308
|
+
} = this.domScrollContainer);
|
|
309
|
+
}
|
|
310
|
+
const {
|
|
311
|
+
preloadMultiplier
|
|
312
|
+
} = this.props;
|
|
313
|
+
const scrollPosition = scrollTop + clientHeight;
|
|
314
|
+
const scrollThreshold = scrollHeight - clientHeight * preloadMultiplier;
|
|
315
|
+
return scrollPosition > scrollThreshold;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
84
319
|
* Handles incrementing of render offset and the request of new items if necessary.
|
|
85
320
|
* @param {boolean} [force] If set to true, proceed independently of scroll validation.
|
|
86
321
|
* @param {Object} [props] The current or next component props.
|
|
87
|
-
*/
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
this.isLoading
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
if(
|
|
322
|
+
*/
|
|
323
|
+
handleLoading(force = false, props = this.props) {
|
|
324
|
+
// Do not load if there is an update in progress.
|
|
325
|
+
if (this.isLoading) {
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
if (this.verifyAllDone()) {
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
const [start, length] = this.state.offset;
|
|
332
|
+
const {
|
|
333
|
+
items,
|
|
334
|
+
totalItems,
|
|
335
|
+
loader
|
|
336
|
+
} = props;
|
|
337
|
+
const renderLength = start + length;
|
|
338
|
+
if (force || this.validateScrollPosition()) {
|
|
339
|
+
// Check if we need to render items that we already received.
|
|
340
|
+
if (renderLength <= items.length) {
|
|
341
|
+
// Render already received items by increasing the offset.
|
|
342
|
+
this.isLoading = true;
|
|
343
|
+
this.increaseOffset();
|
|
344
|
+
} else if (items.length < totalItems) {
|
|
345
|
+
// We already rendered all received items but there are more available.
|
|
346
|
+
// Therefore request new items.
|
|
347
|
+
this.isLoading = true;
|
|
348
|
+
loader(start);
|
|
349
|
+
// If necessary increase render offset for upcoming items.
|
|
350
|
+
if (renderLength < items.length + length) {
|
|
351
|
+
this.increaseOffset();
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
94
358
|
* Handles incrementing of render offset and the request of new items if necessary.
|
|
95
359
|
*
|
|
96
360
|
* Other than the regular handleLoading method this one requires that the loader returns a promise
|
|
@@ -98,13 +362,81 @@ if(renderLength<items.length+length){this.increaseOffset();}}}}/**
|
|
|
98
362
|
* for offset handling.
|
|
99
363
|
* @param {boolean} [force] If set to true, proceed independently of scroll validation.
|
|
100
364
|
* @param {Object} [props] The current or next component props.
|
|
101
|
-
*/
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
this.
|
|
365
|
+
*/
|
|
366
|
+
async handleLoadingPromise(force = false, props = this.props) {
|
|
367
|
+
if (this.isLoading) {
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
if (this.verifyAllDone()) {
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
if (force || this.validateScrollPosition()) {
|
|
374
|
+
// Add isLoading state to prevent requests while the current one is running
|
|
375
|
+
this.isLoading = true;
|
|
376
|
+
const {
|
|
377
|
+
loader
|
|
378
|
+
} = props;
|
|
379
|
+
try {
|
|
380
|
+
const [offset] = this.state.offset;
|
|
381
|
+
// Dispatch the request
|
|
382
|
+
await loader(offset);
|
|
383
|
+
// Increase the offset for the next request
|
|
384
|
+
this.increaseOffset();
|
|
385
|
+
} catch (e) {
|
|
386
|
+
// Stop lazy loading processes on request error
|
|
387
|
+
this.stopLazyLoading();
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// Remove the loading state to enable next request
|
|
391
|
+
this.isLoading = false;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
107
396
|
* Renders the component.
|
|
108
397
|
* @returns {JSX}
|
|
109
|
-
*/
|
|
110
|
-
|
|
398
|
+
*/
|
|
399
|
+
render() {
|
|
400
|
+
const {
|
|
401
|
+
wrapper,
|
|
402
|
+
items,
|
|
403
|
+
iterator,
|
|
404
|
+
loadingIndicator,
|
|
405
|
+
columns
|
|
406
|
+
} = this.props;
|
|
407
|
+
const {
|
|
408
|
+
awaitingItems
|
|
409
|
+
} = this.state;
|
|
410
|
+
const [start, length] = this.state.offset;
|
|
411
|
+
// Only show items in offset range. uses iterator component as item factory
|
|
412
|
+
const children = items.slice(0, start + length).map(item => iterator({
|
|
413
|
+
...item,
|
|
414
|
+
columns
|
|
415
|
+
}));
|
|
416
|
+
const content = typeof wrapper === 'function' ? wrapper({
|
|
417
|
+
children
|
|
418
|
+
}) : (/*#__PURE__*/React.createElement(wrapper, {}, children));
|
|
419
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
420
|
+
ref: elementRef => {
|
|
421
|
+
this.domElement = elementRef;
|
|
422
|
+
},
|
|
423
|
+
className: "common__infinite-container"
|
|
424
|
+
}, /*#__PURE__*/React.createElement("div", null, content), awaitingItems && loadingIndicator);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
InfiniteContainer.contextType = RouteContext;
|
|
428
|
+
InfiniteContainer.defaultProps = {
|
|
429
|
+
columns: 2,
|
|
430
|
+
containerRef: {
|
|
431
|
+
current: null
|
|
432
|
+
},
|
|
433
|
+
initialLimit: 10,
|
|
434
|
+
limit: ITEMS_PER_LOAD,
|
|
435
|
+
loadingIndicator: null,
|
|
436
|
+
preloadMultiplier: 2,
|
|
437
|
+
requestHash: null,
|
|
438
|
+
totalItems: null,
|
|
439
|
+
wrapper: 'div',
|
|
440
|
+
enablePromiseBasedLoading: false
|
|
441
|
+
};
|
|
442
|
+
export default InfiniteContainer;
|