diff --git a/capacities.js b/capacities.js
new file mode 100644
index 0000000..01b9f3c
--- /dev/null
+++ b/capacities.js
@@ -0,0 +1,6 @@
+"use strict";
+
+exports.__esModule = true;
+exports.IntersectionObserver = void 0;
+var IntersectionObserver = typeof window !== 'undefined' && window.IntersectionObserver;
+exports.IntersectionObserver = IntersectionObserver;
\ No newline at end of file
diff --git a/createLoadableVisibilityComponent.js b/createLoadableVisibilityComponent.js
new file mode 100644
index 0000000..8d878c5
--- /dev/null
+++ b/createLoadableVisibilityComponent.js
@@ -0,0 +1,132 @@
+"use strict";
+
+exports.__esModule = true;
+exports["default"] = void 0;
+
+var _react = _interopRequireWildcard(require("react"));
+
+var _capacities = require("./capacities");
+
+function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function _getRequireWildcardCache() { return cache; }; return cache; }
+
+function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
+
+function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
+
+var intersectionObserver;
+var trackedElements = new Map();
+
+if (_capacities.IntersectionObserver) {
+ intersectionObserver = new window.IntersectionObserver(function (entries, observer) {
+ entries.forEach(function (entry) {
+ var visibilityHandler = trackedElements.get(entry.target);
+
+ if (visibilityHandler && (entry.isIntersecting || entry.intersectionRatio > 0)) {
+ visibilityHandler();
+ }
+ });
+ });
+}
+
+function createLoadableVisibilityComponent(args, _ref) {
+ var Loadable = _ref.Loadable,
+ preloadFunc = _ref.preloadFunc,
+ loadFunc = _ref.loadFunc,
+ LoadingComponent = _ref.LoadingComponent;
+ var preloaded = false,
+ loaded = false;
+ var visibilityHandlers = [];
+ var LoadableComponent = Loadable.apply(void 0, args);
+
+ function LoadableVisibilityComponent(props) {
+ var visibilityElementRef = (0, _react.useRef)();
+
+ var _useState = (0, _react.useState)(preloaded),
+ isVisible = _useState[0],
+ setVisible = _useState[1];
+
+ function visibilityHandler() {
+ if (visibilityElementRef.current) {
+ intersectionObserver.unobserve(visibilityElementRef.current);
+ trackedElements["delete"](visibilityElementRef.current);
+ }
+
+ setVisible(true);
+ }
+
+ (0, _react.useEffect)(function () {
+ var element = visibilityElementRef.current;
+
+ if (!isVisible && element) {
+ visibilityHandlers.push(visibilityHandler);
+ trackedElements.set(element, visibilityHandler);
+ intersectionObserver.observe(element);
+ return function () {
+ var handlerIndex = visibilityHandlers.indexOf(visibilityHandler);
+
+ if (handlerIndex >= 0) {
+ visibilityHandlers.splice(handlerIndex, 1);
+ }
+
+ intersectionObserver.unobserve(element);
+ trackedElements["delete"](element);
+ };
+ }
+ }, [isVisible, visibilityElementRef.current]);
+
+ if (isVisible) {
+ return _react["default"].createElement(LoadableComponent, props);
+ }
+
+ if (LoadingComponent || props.fallback) {
+ return _react["default"].createElement("div", _extends({
+ style: {
+ display: "inline-block",
+ minHeight: "1px",
+ minWidth: "1px"
+ }
+ }, props, {
+ ref: visibilityElementRef
+ }), LoadingComponent ? _react["default"].createElement(LoadingComponent, _extends({
+ isLoading: true
+ }, props)) : props.fallback);
+ }
+
+ return _react["default"].createElement("div", _extends({
+ style: {
+ display: "inline-block",
+ minHeight: "1px",
+ minWidth: "1px"
+ }
+ }, props, {
+ ref: visibilityElementRef
+ }));
+ }
+
+ LoadableVisibilityComponent[preloadFunc] = function () {
+ if (!preloaded) {
+ preloaded = true;
+ visibilityHandlers.forEach(function (handler) {
+ return handler();
+ });
+ }
+
+ return LoadableComponent[preloadFunc]();
+ };
+
+ LoadableVisibilityComponent[loadFunc] = function () {
+ if (!loaded) {
+ loaded = true;
+ visibilityHandlers.forEach(function (handler) {
+ return handler();
+ });
+ }
+
+ return LoadableComponent[loadFunc]();
+ };
+
+ return LoadableVisibilityComponent;
+}
+
+var _default = createLoadableVisibilityComponent;
+exports["default"] = _default;
\ No newline at end of file
diff --git a/index.js b/index.js
new file mode 100644
index 0000000..c9c4d31
--- /dev/null
+++ b/index.js
@@ -0,0 +1,3 @@
+"use strict";
+
+module.exports = require("./loadable-components");
\ No newline at end of file
diff --git a/loadable-components.js b/loadable-components.js
new file mode 100644
index 0000000..a6fa7c6
--- /dev/null
+++ b/loadable-components.js
@@ -0,0 +1,36 @@
+"use strict";
+
+var _react = _interopRequireWildcard(require("react"));
+
+var _component = _interopRequireDefault(require("@loadable/component"));
+
+var _createLoadableVisibilityComponent = _interopRequireDefault(require("./createLoadableVisibilityComponent"));
+
+var _capacities = require("./capacities");
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
+
+function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function _getRequireWildcardCache() { return cache; }; return cache; }
+
+function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
+
+function loadableVisiblity(load, opts) {
+ if (opts === void 0) {
+ opts = {};
+ }
+
+ if (_capacities.IntersectionObserver) {
+ return (0, _createLoadableVisibilityComponent["default"])([load, opts], {
+ Loadable: _component["default"],
+ preloadFunc: "preload",
+ loadFunc: "load",
+ LoadingComponent: opts.fallback ? function () {
+ return opts.fallback;
+ } : null
+ });
+ } else {
+ return (0, _component["default"])(load, opts);
+ }
+}
+
+module.exports = loadableVisiblity;
\ No newline at end of file
diff --git a/react-loadable.js b/react-loadable.js
new file mode 100644
index 0000000..c0d727f
--- /dev/null
+++ b/react-loadable.js
@@ -0,0 +1,46 @@
+"use strict";
+
+var _react = _interopRequireWildcard(require("react"));
+
+var _reactLoadable = _interopRequireDefault(require("react-loadable"));
+
+var _createLoadableVisibilityComponent = _interopRequireDefault(require("./createLoadableVisibilityComponent"));
+
+var _capacities = require("./capacities");
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
+
+function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function _getRequireWildcardCache() { return cache; }; return cache; }
+
+function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
+
+function LoadableVisibility(opts) {
+ if (_capacities.IntersectionObserver) {
+ return (0, _createLoadableVisibilityComponent["default"])([opts], {
+ Loadable: _reactLoadable["default"],
+ preloadFunc: 'preload',
+
+ /* Preload helps in synchronously loading a component and returns a promise */
+ loadFunc: 'preload',
+ LoadingComponent: opts.loading
+ });
+ } else {
+ return (0, _reactLoadable["default"])(opts);
+ }
+}
+
+function LoadableVisibilityMap(opts) {
+ if (_capacities.IntersectionObserver) {
+ return (0, _createLoadableVisibilityComponent["default"])([opts], {
+ Loadable: _reactLoadable["default"].Map,
+ preloadFunc: 'preload',
+ loadFunc: 'preload',
+ LoadingComponent: opts.loading
+ });
+ } else {
+ return _reactLoadable["default"].Map(opts);
+ }
+}
+
+LoadableVisibility.Map = LoadableVisibilityMap;
+module.exports = LoadableVisibility;
\ No newline at end of file
diff --git a/src/__mocks__/@loadable/component.js b/src/__mocks__/@loadable/component.js
index c937c2f..0cc7188 100644
--- a/src/__mocks__/@loadable/component.js
+++ b/src/__mocks__/@loadable/component.js
@@ -8,6 +8,7 @@ const loadableObject = props => {
};
loadableObject.preload = jest.fn();
+loadableObject.load = jest.fn();
function loadable(opts) {
return loadableObject;
diff --git a/src/__tests__/loadable-components/intersection-observer.test.js b/src/__tests__/loadable-components/intersection-observer.test.js
index 8d065d4..3ef8502 100644
--- a/src/__tests__/loadable-components/intersection-observer.test.js
+++ b/src/__tests__/loadable-components/intersection-observer.test.js
@@ -126,6 +126,34 @@ describe("Loadable", () => {
expect(queryByTestId("loaded-component")).toBeTruthy();
});
+ test("load calls loadable load", () => {
+ // Mock @loadable/component to get a stable `load` function
+ jest.doMock("@loadable/component");
+
+ const loadable = require("@loadable/component");
+ const loadableVisiblity = require("../../loadable-components"); // Require our tested module with the above mock applied
+
+ loadableVisiblity(loader).load();
+
+ expect(loadable().load).toHaveBeenCalled();
+ });
+
+ test("load will cause the loadable component to be displayed", async () => {
+ const Loader = loadableVisiblity(loader);
+ let returnedValue;
+
+ const { queryByTestId } = render();
+ expect(queryByTestId("loaded-component")).toBeNull();
+
+ act(() => {
+ returnedValue = Loader.load()
+ expect(returnedValue).toBeInstanceOf(Promise);
+ });
+ returnedValue.then(() => {
+ expect(queryByTestId("loaded-component")).toBeTruthy();
+ })
+ });
+
test("it displays the loadable component when it becomes visible", async () => {
const Loader = loadableVisiblity(loader);
diff --git a/src/__tests__/react-loadable/intersection-observer.test.js b/src/__tests__/react-loadable/intersection-observer.test.js
index f14675b..4b48160 100644
--- a/src/__tests__/react-loadable/intersection-observer.test.js
+++ b/src/__tests__/react-loadable/intersection-observer.test.js
@@ -99,18 +99,19 @@ describe("Loadable", () => {
test("preload will cause the loadable component to be displayed", async () => {
const Loader = LoadableVisibility(opts);
+ let returnedValue;
const { queryByTestId } = render();
expect(queryByTestId("loaded-component")).toBeNull();
act(() => {
- Loader.preload();
+ returnedValue = Loader.preload()
+ expect(returnedValue).toBeInstanceOf(Promise);
});
-
- await waitForElement(() => queryByTestId("loaded-component"));
-
- expect(queryByTestId("loaded-component")).toBeTruthy();
+ returnedValue.then(() => {
+ expect(queryByTestId("loaded-component")).toBeTruthy();
+ })
});
test("it displays the loadable component when it becomes visible", async () => {
@@ -210,4 +211,4 @@ describe("Loadable.Map", () => {
expect(Loadable.Map().preload).toHaveBeenCalled();
});
-});
+})
diff --git a/src/createLoadableVisibilityComponent.js b/src/createLoadableVisibilityComponent.js
index c1e8fb7..192cb88 100644
--- a/src/createLoadableVisibilityComponent.js
+++ b/src/createLoadableVisibilityComponent.js
@@ -23,9 +23,9 @@ if (IntersectionObserver) {
function createLoadableVisibilityComponent(
args,
- { Loadable, preloadFunc, LoadingComponent }
+ { Loadable, preloadFunc, loadFunc, LoadingComponent }
) {
- let preloaded = false;
+ let preloaded = false, loaded = false;
const visibilityHandlers = [];
const LoadableComponent = Loadable(...args);
@@ -108,6 +108,14 @@ function createLoadableVisibilityComponent(
return LoadableComponent[preloadFunc]();
};
+ LoadableVisibilityComponent[loadFunc] = () => {
+ if (!loaded) {
+ loaded = true;
+ visibilityHandlers.forEach(handler => handler());
+ }
+ return LoadableComponent[loadFunc]();
+ };
+
return LoadableVisibilityComponent;
}
diff --git a/src/loadable-components.js b/src/loadable-components.js
index a28d8d2..406acb1 100644
--- a/src/loadable-components.js
+++ b/src/loadable-components.js
@@ -8,6 +8,7 @@ function loadableVisiblity(load, opts = {}) {
return createLoadableVisibilityComponent([load, opts], {
Loadable: loadable,
preloadFunc: "preload",
+ loadFunc: "load",
LoadingComponent: opts.fallback ? () => opts.fallback : null
});
} else {
diff --git a/src/react-loadable.js b/src/react-loadable.js
index 4f9a392..77ac76a 100644
--- a/src/react-loadable.js
+++ b/src/react-loadable.js
@@ -8,6 +8,8 @@ function LoadableVisibility (opts) {
return createLoadableVisibilityComponent([opts], {
Loadable,
preloadFunc: 'preload',
+ /* Preload helps in synchronously loading a component and returns a promise */
+ loadFunc: 'preload',
LoadingComponent: opts.loading,
})
} else {
@@ -20,6 +22,7 @@ function LoadableVisibilityMap (opts) {
return createLoadableVisibilityComponent([opts], {
Loadable: Loadable.Map,
preloadFunc: 'preload',
+ loadFunc: 'preload',
LoadingComponent: opts.loading,
})
} else {
diff --git a/yarn.lock b/yarn.lock
index 68e9938..beb72ab 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -699,13 +699,20 @@
"@babel/plugin-transform-react-jsx-self" "^7.7.4"
"@babel/plugin-transform-react-jsx-source" "^7.7.4"
-"@babel/runtime@^7.6.2", "@babel/runtime@^7.7.4", "@babel/runtime@^7.7.6":
+"@babel/runtime@^7.6.2", "@babel/runtime@^7.7.6":
version "7.7.7"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.7.7.tgz#194769ca8d6d7790ec23605af9ee3e42a0aa79cf"
integrity sha512-uCnC2JEVAu8AKB5do1WRIsvrdJ0flYx/A/9f/6chdacnEZ7LmavjdsDXr5ksYBegxtuTPR5Va9/+13QF/kFkCA==
dependencies:
regenerator-runtime "^0.13.2"
+"@babel/runtime@^7.7.7":
+ version "7.14.0"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.0.tgz#46794bc20b612c5f75e62dd071e24dfd95f1cbe6"
+ integrity sha512-JELkvo/DlpNdJ7dlyw/eY7E0suy5i5GQH+Vlxaq1nsNJ+H7f4Vtv3jMeCEgRhZZQFXTjldYfQgv2qmM6M1v5wA==
+ dependencies:
+ regenerator-runtime "^0.13.4"
+
"@babel/template@^7.4.0", "@babel/template@^7.7.4":
version "7.7.4"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.7.4.tgz#428a7d9eecffe27deac0a98e23bf8e3675d2a77b"
@@ -895,13 +902,14 @@
"@types/istanbul-reports" "^1.1.1"
"@types/yargs" "^13.0.0"
-"@loadable/component@^5.10.1":
- version "5.11.0"
- resolved "https://registry.yarnpkg.com/@loadable/component/-/component-5.11.0.tgz#e495834279663d20aabf0e4ca25e5c44bef308eb"
- integrity sha512-RzXAH419hnC6vF/ZAEv+k1JgbE/4G7veZo3RvwQDRfp2BFrwbJp9qasjccuk6RrQR/nWWgufaW/CGlL6NSBNGw==
+"@loadable/component@^5.12.0":
+ version "5.15.0"
+ resolved "https://registry.yarnpkg.com/@loadable/component/-/component-5.15.0.tgz#48b9524237be553f48b158f8c9152593f3f3fded"
+ integrity sha512-g63rQzypPOZi0BeGsK4ST2MYhsFR+i7bhL8k/McUoWDNMDuTTdUlQ2GACKxqh5sI/dNC/6nVoPrycMnSylnAgQ==
dependencies:
- "@babel/runtime" "^7.7.4"
+ "@babel/runtime" "^7.7.7"
hoist-non-react-statics "^3.3.1"
+ react-is "^16.12.0"
"@sheerun/mutationobserver-shim@^0.3.2":
version "0.3.2"
@@ -3378,6 +3386,11 @@ react-dom@16.12.0:
prop-types "^15.6.2"
scheduler "^0.18.0"
+react-is@^16.12.0:
+ version "16.13.1"
+ resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
+ integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
+
react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4:
version "16.12.0"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.12.0.tgz#2cc0fe0fba742d97fd527c42a13bec4eeb06241c"
@@ -3462,6 +3475,11 @@ regenerator-runtime@^0.13.2:
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz#7cf6a77d8f5c6f60eb73c5fc1955b2ceb01e6bf5"
integrity sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==
+regenerator-runtime@^0.13.4:
+ version "0.13.7"
+ resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55"
+ integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==
+
regenerator-transform@^0.14.0:
version "0.14.1"
resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.1.tgz#3b2fce4e1ab7732c08f665dfdb314749c7ddd2fb"
@@ -4276,3 +4294,8 @@ yargs@^13.3.0:
which-module "^2.0.0"
y18n "^4.0.0"
yargs-parser "^13.1.1"
+
+yarn@^1.22.10:
+ version "1.22.10"
+ resolved "https://registry.yarnpkg.com/yarn/-/yarn-1.22.10.tgz#c99daa06257c80f8fa2c3f1490724e394c26b18c"
+ integrity sha512-IanQGI9RRPAN87VGTF7zs2uxkSyQSrSPsju0COgbsKQOOXr5LtcVPeyXWgwVa0ywG3d8dg6kSYKGBuYK021qeA==