diff --git a/docs/animation_during_reroute.md b/docs/animation_during_reroute.md
new file mode 100644
index 00000000..723bc241
--- /dev/null
+++ b/docs/animation_during_reroute.md
@@ -0,0 +1,69 @@
+# Animation during reroute
+
+## Demo
+![Animation during reroute](./assets/demoSpinner.gif)
+
+
+## Implementation details
+- Spinner appears only if transition from one route to another took more then 200ms.
+- During transition ILC remove original MSs immediately and place fake cloned nodes to DOM, which in relust don't have
+any JS mouse listeners. so we strongly recommend to use backdrop for spinner.
+In any case, additionally it will cover any your bugs regarding interaction users with site during rerouting.
+
+## How to
+You can use your own spinner by setting `tmplSpinner` property of `ilcConfig` in your main html template.
+Example:
+```html
+
+
+
+
+```
\ No newline at end of file
diff --git a/docs/assets/demoSpinner.gif b/docs/assets/demoSpinner.gif
new file mode 100644
index 00000000..26f9fcd9
Binary files /dev/null and b/docs/assets/demoSpinner.gif differ
diff --git a/docs/ilc_app_interface.md b/docs/ilc_app_interface.md
index 9f5bea2a..0d30b00d 100644
--- a/docs/ilc_app_interface.md
+++ b/docs/ilc_app_interface.md
@@ -59,4 +59,5 @@ See more information about the [lifecycle functions here](https://single-spa.js.
* `domElementGetter(): HTMLElement` - returns ref to `HTMLElement` that should be used as container to render app's content
* `getCurrentPathProps(): {}` - returns _Props_ that were defined for current path
* `getCurrentBasePath(): string` - returns same value as `basePath` param in `routerProps` query parameter
-* `errorHandler(error, errorInfo = {}): void` - app MUST use it to propagate all unhandled errors
\ No newline at end of file
+* `errorHandler(error, errorInfo = {}): void` - app MUST use it to propagate all unhandled errors
+
diff --git a/ilc/client.js b/ilc/client.js
index 406a6c8c..25c51081 100644
--- a/ilc/client.js
+++ b/ilc/client.js
@@ -3,7 +3,7 @@ import * as singleSpa from 'single-spa';
import Router from './common/router/ClientRouter';
import setupErrorHandlers from './client/errorHandler/setupErrorHandlers';
import {fragmentErrorHandlerFactory, crashIlc} from './client/errorHandler/fragmentErrorHandlerFactory';
-import { renderFakeSlot, addContentListener } from './client/pageTransitions';
+import handlePageTransaction from './client/handlePageTransaction';
import initSpaConfig from './client/initSpaConfig';
import setupPerformanceMonitoring from './client/performance';
import selectSlotsToRegister from './client/selectSlotsToRegister';
@@ -75,9 +75,9 @@ function isActiveFactory(appName, slotName) {
let isActive = checkActivity(router.getCurrentRoute());
const wasActive = checkActivity(router.getPrevRoute());
- const willBeRendered = !wasActive && isActive;
- const willBeRemoved = wasActive && !isActive;
- let willBeRerendered = false;
+ let willBe;
+ !wasActive && isActive && (willBe = 'rendered');
+ wasActive && !isActive && (willBe = 'removed');
if (isActive && wasActive && reload === false) {
const oldProps = router.getPrevRouteProps(appName, slotName);
@@ -95,17 +95,12 @@ function isActiveFactory(appName, slotName) {
});
isActive = false;
- willBeRerendered = true;
+ willBe = 'rerendered';
}
}
- if (willBeRendered) {
- addContentListener(slotName);
- } else if (willBeRemoved) {
- renderFakeSlot(slotName);
- } else if (willBeRerendered) {
- renderFakeSlot(slotName);
- addContentListener(slotName);
+ if (window.ilcConfig && window.ilcConfig.tmplSpinner) {
+ willBe && handlePageTransaction(slotName, willBe);
}
reload = false;
diff --git a/ilc/client/pageTransitions.js b/ilc/client/handlePageTransaction.js
similarity index 82%
rename from ilc/client/pageTransitions.js
rename to ilc/client/handlePageTransaction.js
index 358bffc5..c4bfd393 100644
--- a/ilc/client/pageTransitions.js
+++ b/ilc/client/handlePageTransaction.js
@@ -44,7 +44,7 @@ const onAllSlotsLoaded = () => {
window.dispatchEvent(new CustomEvent('ilc:all-slots-loaded'));
};
-export const addContentListener = slotName => {
+const addContentListener = slotName => {
runGlobalSpinner();
if (window.location.hash) {
@@ -67,7 +67,7 @@ export const addContentListener = slotName => {
observer.observe(targetNode, { childList: true });
};
-export const renderFakeSlot = slotName => {
+const renderFakeSlot = slotName => {
const targetNode = getSlotElement(slotName);
const clonedNode = targetNode.cloneNode(true);
clonedNode.removeAttribute('id');
@@ -77,3 +77,20 @@ export const renderFakeSlot = slotName => {
targetNode.style.display = 'none'; // we hide old slot because fake already in the DOM.
hiddenSlots.push(targetNode);
};
+
+/**
+ * @param {string} slotName
+ * @param {string} willBe - possible values: rendered, removed, rerendered
+ */
+export default function (slotName, willBe) {
+ if (!slotName || !willBe) return;
+
+ if (willBe === 'rendered') {
+ addContentListener(slotName);
+ } else if (willBe === 'removed') {
+ renderFakeSlot(slotName);
+ } else if (willBe === 'rerendered') {
+ renderFakeSlot(slotName);
+ addContentListener(slotName);
+ }
+}