Skip to content
This repository was archived by the owner on Sep 5, 2024. It is now read-only.

Commit 3034237

Browse files
crisbetojelbourn
authored andcommitted
feat(panel): add contentElement option (#9829)
Hooks up the `contentElement` option from `$mdCompile` to `$mdPanel`, allowing for a pre-compiled element to be used as the panel's content. Relates to #9675. Fixes #9757.
1 parent 6ebe04d commit 3034237

File tree

2 files changed

+217
-62
lines changed

2 files changed

+217
-62
lines changed

src/components/panel/panel.js

Lines changed: 120 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ angular
9494
* [$sce service](https://docs.angularjs.org/api/ng/service/$sce).
9595
* - `templateUrl` - `{string=}`: The URL that will be used as the content of
9696
* the panel.
97+
* - `contentElement` - `{(string|!angular.JQLite|!Element)=}`: Pre-compiled
98+
* element to be used as the panel's content.
9799
* - `controller` - `{(function|string)=}`: The controller to associate with
98100
* the panel. The controller can inject a reference to the returned
99101
* panelRef, which allows the panel to be closed, hidden, and shown. Any
@@ -1034,6 +1036,24 @@ MdPanelService.prototype._wrapTemplate = function(origTemplate) {
10341036
};
10351037

10361038

1039+
/**
1040+
* Wraps a content element in a md-panel-outer wrapper and
1041+
* positions it off-screen. Allows for proper control over positoning
1042+
* and animations.
1043+
* @param {!angular.JQLite} contentElement Element to be wrapped.
1044+
* @return {!angular.JQLite} Wrapper element.
1045+
* @private
1046+
*/
1047+
MdPanelService.prototype._wrapContentElement = function(contentElement) {
1048+
var wrapper = angular.element('<div class="md-panel-outer-wrapper">');
1049+
1050+
contentElement.addClass('md-panel').css('left', '-9999px');
1051+
wrapper.append(contentElement);
1052+
1053+
return wrapper;
1054+
};
1055+
1056+
10371057
/*****************************************************************************
10381058
* MdPanelRef *
10391059
*****************************************************************************/
@@ -1123,6 +1143,23 @@ function MdPanelRef(config, $injector) {
11231143
* @private {!Object}
11241144
*/
11251145
this._interceptors = Object.create(null);
1146+
1147+
/**
1148+
* Cleanup function, provided by `$mdCompiler` and assigned after the element
1149+
* has been compiled. When `contentElement` is used, the function is used to
1150+
* restore the element to it's proper place in the DOM.
1151+
* @private {!Function}
1152+
*/
1153+
this._compilerCleanup = null;
1154+
1155+
/**
1156+
* Cache for saving and restoring element inline styles, CSS classes etc.
1157+
* @type {{styles: string, classes: string}}
1158+
*/
1159+
this._restoreCache = {
1160+
styles: '',
1161+
classes: ''
1162+
};
11261163
}
11271164

11281165

@@ -1241,6 +1278,14 @@ MdPanelRef.prototype.detach = function() {
12411278
self._bottomFocusTrap.parentNode.removeChild(self._bottomFocusTrap);
12421279
}
12431280

1281+
if (self._restoreCache.classes) {
1282+
self.panelEl[0].className = self._restoreCache.classes;
1283+
}
1284+
1285+
// Either restore the saved styles or clear the ones set by mdPanel.
1286+
self.panelEl[0].style.cssText = self._restoreCache.styles || '';
1287+
1288+
self._compilerCleanup();
12441289
self.panelContainer.remove();
12451290
self.isAttached = false;
12461291
return self._$q.when(self);
@@ -1473,6 +1518,51 @@ MdPanelRef.prototype.toggleClass = function(toggleClass, onElement) {
14731518
};
14741519

14751520

1521+
/**
1522+
* Compiles the panel, according to the passed in config and appends it to
1523+
* the DOM. Helps normalize differences in the compilation process between
1524+
* using a string template and a content element.
1525+
* @returns {!angular.$q.Promise<!MdPanelRef>} Promise that is resolved when
1526+
* the element has been compiled and added to the DOM.
1527+
* @private
1528+
*/
1529+
MdPanelRef.prototype._compile = function() {
1530+
var self = this;
1531+
1532+
// Compile the element via $mdCompiler. Note that when using a
1533+
// contentElement, the element isn't actually being compiled, rather the
1534+
// compiler saves it's place in the DOM and provides a way of restoring it.
1535+
return self._$mdCompiler.compile(self.config).then(function(compileData) {
1536+
var config = self.config;
1537+
1538+
if (config.contentElement) {
1539+
var panelEl = compileData.element;
1540+
1541+
// Since mdPanel modifies the inline styles and CSS classes, we need
1542+
// to save them in order to be able to restore on close.
1543+
self._restoreCache.styles = panelEl[0].style.cssText;
1544+
self._restoreCache.classes = panelEl[0].className;
1545+
1546+
self.panelContainer = self._$mdPanel._wrapContentElement(panelEl);
1547+
self.panelEl = panelEl;
1548+
} else {
1549+
self.panelContainer = compileData.link(config['scope']);
1550+
self.panelEl = angular.element(
1551+
self.panelContainer[0].querySelector('.md-panel')
1552+
);
1553+
}
1554+
1555+
// Save a reference to the cleanup function from the compiler.
1556+
self._compilerCleanup = compileData.cleanup;
1557+
1558+
// Attach the panel to the proper place in the DOM.
1559+
getElement(self.config['attachTo']).append(self.panelContainer);
1560+
1561+
return self;
1562+
});
1563+
};
1564+
1565+
14761566
/**
14771567
* Creates a panel and adds it to the dom.
14781568
* @returns {!angular.$q.Promise} A promise that is resolved when the panel is
@@ -1488,44 +1578,41 @@ MdPanelRef.prototype._createPanel = function() {
14881578
}
14891579

14901580
self.config.locals.mdPanelRef = self;
1491-
self._$mdCompiler.compile(self.config)
1492-
.then(function(compileData) {
1493-
self.panelContainer = compileData.link(self.config['scope']);
1494-
getElement(self.config['attachTo']).append(self.panelContainer);
1495-
1496-
if (self.config['disableParentScroll']) {
1497-
self._restoreScroll = self._$mdUtil.disableScrollAround(
1498-
null,
1499-
self.panelContainer,
1500-
{ disableScrollMask: true }
1501-
);
1502-
}
15031581

1504-
self.panelEl = angular.element(
1505-
self.panelContainer[0].querySelector('.md-panel'));
1582+
self._compile().then(function() {
1583+
if (self.config['disableParentScroll']) {
1584+
self._restoreScroll = self._$mdUtil.disableScrollAround(
1585+
null,
1586+
self.panelContainer,
1587+
{ disableScrollMask: true }
1588+
);
1589+
}
15061590

1507-
// Add a custom CSS class to the panel element.
1508-
if (self.config['panelClass']) {
1509-
self.panelEl.addClass(self.config['panelClass']);
1510-
}
1591+
// Add a custom CSS class to the panel element.
1592+
if (self.config['panelClass']) {
1593+
self.panelEl.addClass(self.config['panelClass']);
1594+
}
15111595

1512-
// Handle click and touch events for the panel container.
1513-
if (self.config['propagateContainerEvents']) {
1514-
self.panelContainer.css('pointer-events', 'none');
1515-
}
1596+
// Handle click and touch events for the panel container.
1597+
if (self.config['propagateContainerEvents']) {
1598+
self.panelContainer.css('pointer-events', 'none');
1599+
}
15161600

1517-
// Panel may be outside the $rootElement, tell ngAnimate to animate
1518-
// regardless.
1519-
if (self._$animate.pin) {
1520-
self._$animate.pin(self.panelContainer,
1521-
getElement(self.config['attachTo']));
1522-
}
1601+
// Panel may be outside the $rootElement, tell ngAnimate to animate
1602+
// regardless.
1603+
if (self._$animate.pin) {
1604+
self._$animate.pin(
1605+
self.panelContainer,
1606+
getElement(self.config['attachTo'])
1607+
);
1608+
}
1609+
1610+
self._configureTrapFocus();
1611+
self._addStyles().then(function() {
1612+
resolve(self);
1613+
}, reject);
1614+
}, reject);
15231615

1524-
self._configureTrapFocus();
1525-
self._addStyles().then(function() {
1526-
resolve(self);
1527-
}, reject);
1528-
}, reject);
15291616
});
15301617
};
15311618

0 commit comments

Comments
 (0)