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

Commit

Permalink
feat(modal): support multiple open classes
Browse files Browse the repository at this point in the history
- Support multiple modal open classes when multiple modals are open

Closes #4226
Fixes #4184
  • Loading branch information
wesleycho committed Aug 18, 2015
1 parent 550fe20 commit 3d01c59
Show file tree
Hide file tree
Showing 3 changed files with 171 additions and 4 deletions.
71 changes: 67 additions & 4 deletions src/modal/modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,61 @@ angular.module('ui.bootstrap.modal', [])
};
})

/**
* A helper, internal data structure that stores all references attached to key
*/
.factory('$$multiMap', function() {
return {
createNew: function() {
var map = {};

return {
entries: function() {
return Object.keys(map).map(function(key) {
return {
key: key,
value: map[key]
};
});
},
get: function(key) {
return map[key];
},
hasKey: function(key) {
return !!map[key];
},
keys: function() {
return Object.keys(map);
},
put: function(key, value) {
if (!map[key]) {
map[key] = [];
}

map[key].push(value);
},
remove: function(key, value) {
var values = map[key];

if (!values) {
return;
}

var idx = values.indexOf(value);

if (idx !== -1) {
values.splice(idx, 1);
}

if (!values.length) {
delete map[key];
}
}
};
}
};
})

/**
* A helper directive for the $modal service. It creates a backdrop element.
*/
Expand Down Expand Up @@ -220,10 +275,12 @@ angular.module('ui.bootstrap.modal', [])
'$animate', '$timeout', '$document', '$compile', '$rootScope',
'$q',
'$injector',
'$$multiMap',
'$$stackedMap',
function($animate , $timeout , $document , $compile , $rootScope ,
$q,
$injector,
$$multiMap,
$$stackedMap) {
var $animateCss = null;

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

var backdropDomEl, backdropScope;
var openedWindows = $$stackedMap.createNew();
var openedClasses = $$multiMap.createNew();
var $modalStack = {
NOW_CLOSING_EVENT: 'modal.stack.now-closing'
};
Expand Down Expand Up @@ -264,15 +322,16 @@ angular.module('ui.bootstrap.modal', [])
});

function removeModalWindow(modalInstance, elementToReceiveFocus) {

var body = $document.find('body').eq(0);
var modalWindow = openedWindows.get(modalInstance).value;

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

removeAfterAnimate(modalWindow.modalDomEl, modalWindow.modalScope, function() {
body.toggleClass(modalWindow.openedClass || OPENED_MODAL_CLASS, openedWindows.length() > 0);
var modalBodyClass = modalWindow.openedClass || OPENED_MODAL_CLASS;
openedClasses.remove(modalBodyClass, modalInstance);
body.toggleClass(modalBodyClass, openedClasses.hasKey(modalBodyClass));
});
checkRemoveBackdrop();

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

$modalStack.open = function(modalInstance, modal) {
var modalOpener = $document[0].activeElement;
var modalOpener = $document[0].activeElement,
modalBodyClass = modal.openedClass || OPENED_MODAL_CLASS;

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

openedClasses.put(modalBodyClass, modalInstance);

var body = $document.find('body').eq(0),
currBackdropIndex = backdropIndex();

Expand Down Expand Up @@ -419,7 +481,8 @@ angular.module('ui.bootstrap.modal', [])
openedWindows.top().value.modalDomEl = modalDomEl;
openedWindows.top().value.modalOpener = modalOpener;
body.append(modalDomEl);
body.addClass(modal.openedClass || OPENED_MODAL_CLASS);
body.addClass(modalBodyClass);

$modalStack.clearFocusListCache();
};

Expand Down
46 changes: 46 additions & 0 deletions src/modal/test/modal.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -798,6 +798,52 @@ describe('$modal', function () {

expect(body).not.toHaveClass('foo');
});

it('should add multiple custom classes to the body element and remove appropriately', function() {
var modal1 = open({
template: '<div>dummy modal</div>',
openedClass: 'foo'
});

expect(body).toHaveClass('foo');
expect(body).not.toHaveClass('modal-open');

var modal2 = open({
template: '<div>dummy modal</div>',
openedClass: 'bar'
});

expect(body).toHaveClass('foo');
expect(body).toHaveClass('bar');
expect(body).not.toHaveClass('modal-open');

var modal3 = open({
template: '<div>dummy modal</div>',
openedClass: 'foo'
});

expect(body).toHaveClass('foo');
expect(body).toHaveClass('bar');
expect(body).not.toHaveClass('modal-open');

close(modal1);

expect(body).toHaveClass('foo');
expect(body).toHaveClass('bar');
expect(body).not.toHaveClass('modal-open');

close(modal2);

expect(body).toHaveClass('foo');
expect(body).not.toHaveClass('bar');
expect(body).not.toHaveClass('modal-open');

close(modal3);

expect(body).not.toHaveClass('foo');
expect(body).not.toHaveClass('bar');
expect(body).not.toHaveClass('modal-open');
});
});
});

Expand Down
58 changes: 58 additions & 0 deletions src/modal/test/multiMap.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
describe('multi map', function() {
var multiMap;

beforeEach(module('ui.bootstrap.modal'));
beforeEach(inject(function($$multiMap) {
multiMap = $$multiMap.createNew();
}));

it('should add and remove objects by key', function() {
multiMap.put('foo', 'bar');

expect(multiMap.get('foo')).toEqual(['bar']);

multiMap.put('foo', 'baz');

expect(multiMap.get('foo')).toEqual(['bar', 'baz']);

multiMap.remove('foo', 'bar');

expect(multiMap.get('foo')).toEqual(['baz']);

multiMap.remove('foo', 'baz');

expect(multiMap.hasKey('foo')).toBe(false);
});

it('should support getting the keys', function() {
multiMap.put('foo', 'bar');
multiMap.put('baz', 'boo');

expect(multiMap.keys()).toEqual(['foo', 'baz']);
});

it('should return all entries', function() {
multiMap.put('foo', 'bar');
multiMap.put('foo', 'bar2');
multiMap.put('baz', 'boo');

expect(multiMap.entries()).toEqual([
{
key: 'foo',
value: ['bar', 'bar2']
},
{
key: 'baz',
value: ['boo']
}
]);
});

it('should preserve semantic of an empty key', function() {
expect(multiMap.get('key')).toBeUndefined();
});

it('should respect removal of non-existing elements', function() {
expect(multiMap.remove('foo', 'bar')).toBeUndefined();
});
});

0 comments on commit 3d01c59

Please sign in to comment.