Loading: full screen loading is now singleton

pull/1503/merge
Leopoldthecoder 2016-11-29 16:04:58 +08:00 committed by cinwell.li
parent 4b9a0125c2
commit 707a0d5a60
4 changed files with 58 additions and 14 deletions

View File

@ -200,6 +200,14 @@ The parameter `options` is the configuration of Loading, and its details can be
let loadingInstance = Loading.service(options); let loadingInstance = Loading.service(options);
loadingInstance.close(); loadingInstance.close();
``` ```
Note that in this case the full screen Loading is singleton. If a new full screen Loading is invoked before an existing one is closed, the existing full screen Loading instance will be returned instead of actually creating another Loading instance:
```javascript
let loadingInstance1 = Loading.service({ fullscreen: true });
let loadingInstance2 = Loading.service({ fullscreen: true });
console.log(loadingInstance1 === loadingInstance2); // true
```
Calling the `close` method on any one of them can close this full screen Loading.
If Element is imported entirely, a globally method `$loading` will be registered to Vue.prototype. You can invoke it like this: `this.$loading(options)`, and it also returns a Loading instance. If Element is imported entirely, a globally method `$loading` will be registered to Vue.prototype. You can invoke it like this: `this.$loading(options)`, and it also returns a Loading instance.
### Options ### Options

View File

@ -204,6 +204,14 @@ Loading.service(options);
let loadingInstance = Loading.service(options); let loadingInstance = Loading.service(options);
loadingInstance.close(); loadingInstance.close();
``` ```
需要注意的是,以服务的方式调用的全屏 Loading 是单例的:若在前一个全屏 Loading 关闭前再次调用全屏 Loading并不会创建一个新的 Loading 实例,而是返回现有全屏 Loading 的实例:
```javascript
let loadingInstance1 = Loading.service({ fullscreen: true });
let loadingInstance2 = Loading.service({ fullscreen: true });
console.log(loadingInstance1 === loadingInstance2); // true
```
此时调用它们中任意一个的 `close` 方法都能关闭这个全屏 Loading。
如果完整引入了 Element那么 Vue.prototype 上会有一个全局方法 `$loading`,它的调用方式为:`this.$loading(options)`,同样会返回一个 Loading 实例。 如果完整引入了 Element那么 Vue.prototype 上会有一个全局方法 `$loading`,它的调用方式为:`this.$loading(options)`,同样会返回一个 Loading 实例。
### Options ### Options

View File

@ -12,16 +12,22 @@ const defaults = {
customClass: '' customClass: ''
}; };
let originalPosition, originalOverflow; let fullscreenLoading;
LoadingConstructor.prototype.originalPosition = '';
LoadingConstructor.prototype.originalOverflow = '';
LoadingConstructor.prototype.close = function() { LoadingConstructor.prototype.close = function() {
if (this.fullscreen && originalOverflow !== 'hidden') { if (this.fullscreen && this.originalOverflow !== 'hidden') {
document.body.style.overflow = originalOverflow; document.body.style.overflow = this.originalOverflow;
} }
if (this.fullscreen || this.body) { if (this.fullscreen || this.body) {
document.body.style.position = originalPosition; document.body.style.position = this.originalPosition;
} else { } else {
this.target.style.position = originalPosition; this.target.style.position = this.originalPosition;
}
if (this.fullscreen) {
fullscreenLoading = undefined;
} }
this.$el && this.$el &&
this.$el.parentNode && this.$el.parentNode &&
@ -29,13 +35,13 @@ LoadingConstructor.prototype.close = function() {
this.$destroy(); this.$destroy();
}; };
const addStyle = (options, parent, element) => { const addStyle = (options, parent, instance) => {
let maskStyle = {}; let maskStyle = {};
if (options.fullscreen) { if (options.fullscreen) {
originalPosition = document.body.style.position; instance.originalPosition = document.body.style.position;
originalOverflow = document.body.style.overflow; instance.originalOverflow = document.body.style.overflow;
} else if (options.body) { } else if (options.body) {
originalPosition = document.body.style.position; instance.originalPosition = document.body.style.position;
['top', 'left'].forEach(property => { ['top', 'left'].forEach(property => {
let scroll = property === 'top' ? 'scrollTop' : 'scrollLeft'; let scroll = property === 'top' ? 'scrollTop' : 'scrollLeft';
maskStyle[property] = options.target.getBoundingClientRect()[property] + maskStyle[property] = options.target.getBoundingClientRect()[property] +
@ -47,10 +53,10 @@ const addStyle = (options, parent, element) => {
maskStyle[property] = options.target.getBoundingClientRect()[property] + 'px'; maskStyle[property] = options.target.getBoundingClientRect()[property] + 'px';
}); });
} else { } else {
originalPosition = parent.style.position; instance.originalPosition = parent.style.position;
} }
Object.keys(maskStyle).forEach(property => { Object.keys(maskStyle).forEach(property => {
element.style[property] = maskStyle[property]; instance.$el.style[property] = maskStyle[property];
}); });
}; };
@ -65,6 +71,9 @@ const Loading = (options = {}) => {
} else { } else {
options.body = true; options.body = true;
} }
if (options.fullscreen && fullscreenLoading) {
return fullscreenLoading;
}
let parent = options.body ? document.body : options.target; let parent = options.body ? document.body : options.target;
let instance = new LoadingConstructor({ let instance = new LoadingConstructor({
@ -72,14 +81,17 @@ const Loading = (options = {}) => {
data: options data: options
}); });
addStyle(options, parent, instance.$el); addStyle(options, parent, instance);
if (originalPosition !== 'absolute') { if (instance.originalPosition !== 'absolute') {
parent.style.position = 'relative'; parent.style.position = 'relative';
} }
if (options.fullscreen && options.lock) { if (options.fullscreen && options.lock) {
parent.style.overflow = 'hidden'; parent.style.overflow = 'hidden';
} }
parent.appendChild(instance.$el); parent.appendChild(instance.$el);
if (options.fullscreen) {
fullscreenLoading = instance;
}
return instance; return instance;
}; };

View File

@ -4,10 +4,11 @@ import LoadingRaw from 'packages/loading';
const Loading = LoadingRaw.service; const Loading = LoadingRaw.service;
describe('Loading', () => { describe('Loading', () => {
let vm, loadingInstance; let vm, loadingInstance, loadingInstance2;
afterEach(() => { afterEach(() => {
destroyVM(vm); destroyVM(vm);
loadingInstance && loadingInstance.close(); loadingInstance && loadingInstance.close();
loadingInstance2 && loadingInstance2.close();
}); });
describe('as a directive', () => { describe('as a directive', () => {
@ -207,6 +208,21 @@ describe('Loading', () => {
expect(mask.classList.contains('is-fullscreen')).to.true; expect(mask.classList.contains('is-fullscreen')).to.true;
}); });
it('fullscreen singleton', done => {
loadingInstance = Loading({ fullScreen: true });
setTimeout(() => {
loadingInstance2 = Loading({ fullScreen: true });
setTimeout(() => {
let masks = document.querySelectorAll('.el-loading-mask');
expect(masks.length).to.equal(1);
loadingInstance2.close();
masks = document.querySelectorAll('.el-loading-mask');
expect(masks.length).to.equal(0);
done();
}, 100);
}, 100);
});
it('lock', () => { it('lock', () => {
loadingInstance = Loading({ lock: true }); loadingInstance = Loading({ lock: true });
expect(document.body.style.overflow).to.equal('hidden'); expect(document.body.style.overflow).to.equal('hidden');