-
Notifications
You must be signed in to change notification settings - Fork 3.4k
feat(themes): register theme on the fly #9413
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -538,20 +538,53 @@ function ThemingProvider($mdColorPalette, $$mdMetaProvider) { | |
* @param {el=} element to apply theming to | ||
*/ | ||
/* @ngInject */ | ||
function ThemingService($rootScope, $log) { | ||
function ThemingService($rootScope, $mdUtil, $q, $log) { | ||
// Allow us to be invoked via a linking function signature. | ||
var applyTheme = function (scope, el) { | ||
if (el === undefined) { el = scope; scope = undefined; } | ||
if (scope === undefined) { scope = $rootScope; } | ||
applyTheme.inherit(el, el); | ||
}; | ||
|
||
applyTheme.THEMES = angular.extend({}, THEMES); | ||
applyTheme.PALETTES = angular.extend({}, PALETTES); | ||
Object.defineProperty(applyTheme, 'THEMES', { | ||
get: function () { | ||
return angular.extend({}, THEMES); | ||
} | ||
}); | ||
Object.defineProperty(applyTheme, 'PALETTES', { | ||
get: function () { | ||
return angular.extend({}, PALETTES); | ||
} | ||
}); | ||
applyTheme.inherit = inheritTheme; | ||
applyTheme.registered = registered; | ||
applyTheme.defaultTheme = function() { return defaultTheme; }; | ||
applyTheme.generateTheme = function(name) { generateTheme(THEMES[name], name, themeConfig.nonce); }; | ||
applyTheme.defineTheme = function(name, options) { | ||
options = options || {}; | ||
|
||
var theme = registerTheme(name); | ||
|
||
if (options.primary) { | ||
theme.primaryPalette(options.primary); | ||
} | ||
if (options.accent) { | ||
theme.accentPalette(options.accent); | ||
} | ||
if (options.warn) { | ||
theme.warnPalette(options.warn); | ||
} | ||
if (options.background) { | ||
theme.backgroundPalette(options.background); | ||
} | ||
if (options.dark){ | ||
theme.dark(); | ||
} | ||
|
||
this.generateTheme(name); | ||
|
||
return $q.resolve(name); | ||
}; | ||
applyTheme.setBrowserColor = enableBrowserColor; | ||
|
||
return applyTheme; | ||
|
@@ -568,22 +601,31 @@ function ThemingProvider($mdColorPalette, $$mdMetaProvider) { | |
* Get theme name for the element, then update with Theme CSS class | ||
*/ | ||
function inheritTheme (el, parent) { | ||
var ctrl = parent.controller('mdTheme'); | ||
var attrThemeValue = el.attr('md-theme-watch'); | ||
var watchTheme = (alwaysWatchTheme || angular.isDefined(attrThemeValue)) && attrThemeValue != 'false'; | ||
var ctrl = parent.controller('mdTheme') || el.data('$mdThemeController'); | ||
|
||
updateThemeClass(lookupThemeName()); | ||
|
||
if ((alwaysWatchTheme && !registerChangeCallback()) || (!alwaysWatchTheme && watchTheme)) { | ||
el.on('$destroy', $rootScope.$watch(lookupThemeName, updateThemeClass) ); | ||
if (ctrl) { | ||
var watchTheme = | ||
alwaysWatchTheme || ctrl.$shouldWatch || $mdUtil.parseAttributeBoolean(el.attr('md-theme-watch')); | ||
|
||
var unwatch = ctrl.registerChanges(function (name) { | ||
updateThemeClass(name); | ||
|
||
if (!watchTheme) { | ||
unwatch(); | ||
} | ||
else { | ||
el.on('$destroy', unwatch); | ||
} | ||
}); | ||
} | ||
|
||
/** | ||
* Find the theme name from the parent controller or element data | ||
*/ | ||
function lookupThemeName() { | ||
// As a few components (dialog) add their controllers later, we should also watch for a controller init. | ||
ctrl = parent.controller('mdTheme') || el.data('$mdThemeController'); | ||
return ctrl && ctrl.$mdTheme || (defaultTheme == 'default' ? '' : defaultTheme); | ||
} | ||
|
||
|
@@ -606,24 +648,12 @@ function ThemingProvider($mdColorPalette, $$mdMetaProvider) { | |
el.data('$mdThemeController', ctrl); | ||
} | ||
} | ||
|
||
/** | ||
* Register change callback with parent mdTheme controller | ||
*/ | ||
function registerChangeCallback() { | ||
var parentController = parent.controller('mdTheme'); | ||
if (!parentController) return false; | ||
el.on('$destroy', parentController.registerChanges( function() { | ||
updateThemeClass(lookupThemeName()); | ||
})); | ||
return true; | ||
} | ||
} | ||
|
||
} | ||
} | ||
|
||
function ThemingDirective($mdTheming, $interpolate, $log) { | ||
function ThemingDirective($mdTheming, $interpolate, $parse, $mdUtil, $q, $log) { | ||
return { | ||
priority: 100, | ||
link: { | ||
|
@@ -649,16 +679,39 @@ function ThemingDirective($mdTheming, $interpolate, $log) { | |
if (!$mdTheming.registered(theme)) { | ||
$log.warn('attempted to use unregistered theme \'' + theme + '\''); | ||
} | ||
|
||
|
||
ctrl.$mdTheme = theme; | ||
|
||
registeredCallbacks.forEach(function (cb) { | ||
cb(); | ||
// Iterating backwards to support unregistering during iteration | ||
// http://stackoverflow.com/a/9882349/890293 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't that comment now invalid? since we are now using a simple There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's still explaining why are we iterating backwards There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The URL does in my case only shows how to loop backwards. But does not explain why we do this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's not a big deal. But where do we explicitly use a
Let's keep it as it is. |
||
registeredCallbacks.reverse().forEach(function (cb) { | ||
cb(theme); | ||
}); | ||
} | ||
}, | ||
$shouldWatch: $mdUtil.parseAttributeBoolean(el.attr('md-theme-watch')) | ||
}; | ||
|
||
el.data('$mdThemeController', ctrl); | ||
ctrl.$setTheme($interpolate(attrs.mdTheme)(scope)); | ||
attrs.$observe('mdTheme', ctrl.$setTheme); | ||
|
||
var getThemeInterpolation = function () { return $interpolate(attrs.mdTheme)(scope); }; | ||
|
||
var setParsedTheme = function (interpolation) { | ||
var theme = $parse(interpolation)(scope) || interpolation; | ||
|
||
if (typeof theme === 'string') { | ||
return ctrl.$setTheme(theme); | ||
} | ||
|
||
$q.when( (typeof theme === 'function') ? theme() : theme ) | ||
.then(function(name){ | ||
ctrl.$setTheme(name) | ||
}); | ||
}; | ||
|
||
setParsedTheme(getThemeInterpolation()); | ||
|
||
scope.$watch(getThemeInterpolation, setParsedTheme); | ||
} | ||
} | ||
}; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why this complexity... unless you want generate the THEMES object on demand (each time the getter is accessed).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, generating it once doesn't support themes that are registered on the fly
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reading this code, the following assertion happen, which seems like the wrong logic:
And any app that changes THEMES (say by calling
generateTheme
) would not change references to THEMES that already exist.