Skip to content
This repository was archived by the owner on May 29, 2019. It is now read-only.

Commit 3d01c59

Browse files
committed
feat(modal): support multiple open classes
- Support multiple modal open classes when multiple modals are open Closes #4226 Fixes #4184
1 parent 550fe20 commit 3d01c59

File tree

3 files changed

+171
-4
lines changed

3 files changed

+171
-4
lines changed

Diff for: src/modal/modal.js

+67-4
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,61 @@ angular.module('ui.bootstrap.modal', [])
5454
};
5555
})
5656

57+
/**
58+
* A helper, internal data structure that stores all references attached to key
59+
*/
60+
.factory('$$multiMap', function() {
61+
return {
62+
createNew: function() {
63+
var map = {};
64+
65+
return {
66+
entries: function() {
67+
return Object.keys(map).map(function(key) {
68+
return {
69+
key: key,
70+
value: map[key]
71+
};
72+
});
73+
},
74+
get: function(key) {
75+
return map[key];
76+
},
77+
hasKey: function(key) {
78+
return !!map[key];
79+
},
80+
keys: function() {
81+
return Object.keys(map);
82+
},
83+
put: function(key, value) {
84+
if (!map[key]) {
85+
map[key] = [];
86+
}
87+
88+
map[key].push(value);
89+
},
90+
remove: function(key, value) {
91+
var values = map[key];
92+
93+
if (!values) {
94+
return;
95+
}
96+
97+
var idx = values.indexOf(value);
98+
99+
if (idx !== -1) {
100+
values.splice(idx, 1);
101+
}
102+
103+
if (!values.length) {
104+
delete map[key];
105+
}
106+
}
107+
};
108+
}
109+
};
110+
})
111+
57112
/**
58113
* A helper directive for the $modal service. It creates a backdrop element.
59114
*/
@@ -220,10 +275,12 @@ angular.module('ui.bootstrap.modal', [])
220275
'$animate', '$timeout', '$document', '$compile', '$rootScope',
221276
'$q',
222277
'$injector',
278+
'$$multiMap',
223279
'$$stackedMap',
224280
function($animate , $timeout , $document , $compile , $rootScope ,
225281
$q,
226282
$injector,
283+
$$multiMap,
227284
$$stackedMap) {
228285
var $animateCss = null;
229286

@@ -235,6 +292,7 @@ angular.module('ui.bootstrap.modal', [])
235292

236293
var backdropDomEl, backdropScope;
237294
var openedWindows = $$stackedMap.createNew();
295+
var openedClasses = $$multiMap.createNew();
238296
var $modalStack = {
239297
NOW_CLOSING_EVENT: 'modal.stack.now-closing'
240298
};
@@ -264,15 +322,16 @@ angular.module('ui.bootstrap.modal', [])
264322
});
265323

266324
function removeModalWindow(modalInstance, elementToReceiveFocus) {
267-
268325
var body = $document.find('body').eq(0);
269326
var modalWindow = openedWindows.get(modalInstance).value;
270327

271328
//clean up the stack
272329
openedWindows.remove(modalInstance);
273330

274331
removeAfterAnimate(modalWindow.modalDomEl, modalWindow.modalScope, function() {
275-
body.toggleClass(modalWindow.openedClass || OPENED_MODAL_CLASS, openedWindows.length() > 0);
332+
var modalBodyClass = modalWindow.openedClass || OPENED_MODAL_CLASS;
333+
openedClasses.remove(modalBodyClass, modalInstance);
334+
body.toggleClass(modalBodyClass, openedClasses.hasKey(modalBodyClass));
276335
});
277336
checkRemoveBackdrop();
278337

@@ -377,7 +436,8 @@ angular.module('ui.bootstrap.modal', [])
377436
});
378437

379438
$modalStack.open = function(modalInstance, modal) {
380-
var modalOpener = $document[0].activeElement;
439+
var modalOpener = $document[0].activeElement,
440+
modalBodyClass = modal.openedClass || OPENED_MODAL_CLASS;
381441

382442
openedWindows.add(modalInstance, {
383443
deferred: modal.deferred,
@@ -388,6 +448,8 @@ angular.module('ui.bootstrap.modal', [])
388448
openedClass: modal.openedClass
389449
});
390450

451+
openedClasses.put(modalBodyClass, modalInstance);
452+
391453
var body = $document.find('body').eq(0),
392454
currBackdropIndex = backdropIndex();
393455

@@ -419,7 +481,8 @@ angular.module('ui.bootstrap.modal', [])
419481
openedWindows.top().value.modalDomEl = modalDomEl;
420482
openedWindows.top().value.modalOpener = modalOpener;
421483
body.append(modalDomEl);
422-
body.addClass(modal.openedClass || OPENED_MODAL_CLASS);
484+
body.addClass(modalBodyClass);
485+
423486
$modalStack.clearFocusListCache();
424487
};
425488

Diff for: src/modal/test/modal.spec.js

+46
Original file line numberDiff line numberDiff line change
@@ -798,6 +798,52 @@ describe('$modal', function () {
798798

799799
expect(body).not.toHaveClass('foo');
800800
});
801+
802+
it('should add multiple custom classes to the body element and remove appropriately', function() {
803+
var modal1 = open({
804+
template: '<div>dummy modal</div>',
805+
openedClass: 'foo'
806+
});
807+
808+
expect(body).toHaveClass('foo');
809+
expect(body).not.toHaveClass('modal-open');
810+
811+
var modal2 = open({
812+
template: '<div>dummy modal</div>',
813+
openedClass: 'bar'
814+
});
815+
816+
expect(body).toHaveClass('foo');
817+
expect(body).toHaveClass('bar');
818+
expect(body).not.toHaveClass('modal-open');
819+
820+
var modal3 = open({
821+
template: '<div>dummy modal</div>',
822+
openedClass: 'foo'
823+
});
824+
825+
expect(body).toHaveClass('foo');
826+
expect(body).toHaveClass('bar');
827+
expect(body).not.toHaveClass('modal-open');
828+
829+
close(modal1);
830+
831+
expect(body).toHaveClass('foo');
832+
expect(body).toHaveClass('bar');
833+
expect(body).not.toHaveClass('modal-open');
834+
835+
close(modal2);
836+
837+
expect(body).toHaveClass('foo');
838+
expect(body).not.toHaveClass('bar');
839+
expect(body).not.toHaveClass('modal-open');
840+
841+
close(modal3);
842+
843+
expect(body).not.toHaveClass('foo');
844+
expect(body).not.toHaveClass('bar');
845+
expect(body).not.toHaveClass('modal-open');
846+
});
801847
});
802848
});
803849

Diff for: src/modal/test/multiMap.spec.js

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
describe('multi map', function() {
2+
var multiMap;
3+
4+
beforeEach(module('ui.bootstrap.modal'));
5+
beforeEach(inject(function($$multiMap) {
6+
multiMap = $$multiMap.createNew();
7+
}));
8+
9+
it('should add and remove objects by key', function() {
10+
multiMap.put('foo', 'bar');
11+
12+
expect(multiMap.get('foo')).toEqual(['bar']);
13+
14+
multiMap.put('foo', 'baz');
15+
16+
expect(multiMap.get('foo')).toEqual(['bar', 'baz']);
17+
18+
multiMap.remove('foo', 'bar');
19+
20+
expect(multiMap.get('foo')).toEqual(['baz']);
21+
22+
multiMap.remove('foo', 'baz');
23+
24+
expect(multiMap.hasKey('foo')).toBe(false);
25+
});
26+
27+
it('should support getting the keys', function() {
28+
multiMap.put('foo', 'bar');
29+
multiMap.put('baz', 'boo');
30+
31+
expect(multiMap.keys()).toEqual(['foo', 'baz']);
32+
});
33+
34+
it('should return all entries', function() {
35+
multiMap.put('foo', 'bar');
36+
multiMap.put('foo', 'bar2');
37+
multiMap.put('baz', 'boo');
38+
39+
expect(multiMap.entries()).toEqual([
40+
{
41+
key: 'foo',
42+
value: ['bar', 'bar2']
43+
},
44+
{
45+
key: 'baz',
46+
value: ['boo']
47+
}
48+
]);
49+
});
50+
51+
it('should preserve semantic of an empty key', function() {
52+
expect(multiMap.get('key')).toBeUndefined();
53+
});
54+
55+
it('should respect removal of non-existing elements', function() {
56+
expect(multiMap.remove('foo', 'bar')).toBeUndefined();
57+
});
58+
});

0 commit comments

Comments
 (0)