Skip to content

Commit

Permalink
Support @Keyframes declared after they are used.
Browse files Browse the repository at this point in the history
  • Loading branch information
Chris Joel committed Jan 5, 2016
1 parent cd43e3b commit 3adeade
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 51 deletions.
2 changes: 1 addition & 1 deletion src/lib/custom-style.html
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@
e.textContent;
}
if (e.textContent) {
styleUtil.forEachStyleRule(styleUtil.rulesForStyle(e), function(rule) {
styleUtil.forEachRule(styleUtil.rulesForStyle(e), function(rule) {
styleTransformer.documentRule(rule);
});
// Allow all custom-styles defined in this turn to register
Expand Down
6 changes: 3 additions & 3 deletions src/lib/style-extends.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@
var styleUtil = Polymer.StyleUtil;

return {

hasExtends: function(cssText) {
return Boolean(cssText.match(this.rx.EXTEND));
},

transform: function(style) {
var rules = styleUtil.rulesForStyle(style);
var self = this;
styleUtil.forEachStyleRule(rules, function(rule) {
styleUtil.forEachRule(rules, function(rule) {
var map = self._mapRule(rule);
if (rule.parent) {
var m;
Expand Down Expand Up @@ -72,7 +72,7 @@
// TODO: this misses `%foo, .bar` as an unetended selector but
// this seems rare and could possibly be unsupported.
source.selector = source.selector.replace(this.rx.STRIP, '');
source.selector = (source.selector && source.selector + ',\n') +
source.selector = (source.selector && source.selector + ',\n') +
target.selector;
if (source.extends) {
source.extends.forEach(function(e) {
Expand Down
47 changes: 28 additions & 19 deletions src/lib/style-properties.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,15 @@
// decorates styles with rule info and returns an array of used style
// property names
decorateStyles: function(styles) {
var self = this, props = {};
styleUtil.forRulesInStyles(styles, function(rule) {
var self = this, props = {}, keyframes = [];
styleUtil.forRulesInStyles(styles, function onStyleRule(rule) {
self.decorateRule(rule);
self.collectPropertiesInCssText(rule.propertyInfo.cssText, props);
}, function onKeyframesRule(rule) {
keyframes.push(rule);
});
// Cache all found keyframes rules for later reference:
styles._keyframes = keyframes;
// return this list of property names *consumes* in these styles.
var names = [];
for (var i in props) {
Expand Down Expand Up @@ -263,35 +267,40 @@
hostSelector;
var hostRx = new RegExp(this.rx.HOST_PREFIX + rxHostSelector +
this.rx.HOST_SUFFIX);
var keyframesRules = element._styles._keyframes;
var keyframeTransforms = {};

if (!nativeShadow) {
// For non-ShadowDOM, we transform all known keyframes rules in
// advance for the current scope. This allows us to catch keyframes
// rules that appear anywhere in the stylesheet:
for (var i = 0, keyframesRule = keyframesRules[i];
i < keyframesRules.length;
keyframesRule = keyframesRules[++i]) {
this._scopeKeyframes(keyframesRule, scopeSelector);
keyframeTransforms[keyframesRule.keyframesName] = function(rule, cssText) {
return cssText.replace(rule.keyframesNameRx, rule.transformedKeyframesName);
}.bind(this, keyframesRule);
}
}

return styleTransformer.elementStyles(element, function(rule) {
self.applyProperties(rule, properties);
self.applyKeyframeTransforms(rule, keyframeTransforms);
if (!nativeShadow) {
// If the rule is a keyframe selector, we put a transformation in
// the keyframeTransforms map so that it can be applied to other
// rules in the future. We use an anonymous function for the
// transformer so that we can avoid creating a new RegExp for every
// call to `applyKeyframeTransforms`.
if (Polymer.StyleUtil.isKeyframesSelector(rule)) {
var keyframesNameRx = new RegExp(rule.parent.keyframesName, 'g');
self._scopeKeyframes(rule.parent, scopeSelector);
keyframeTransforms[rule.parent.keyframesName] = function(cssText) {
return cssText.replace(
keyframesNameRx, rule.parent.transformedKeyframesName);
};
} else if (rule.cssText) {
self._scopeSelector(rule, hostRx, hostSelector,
element._scopeCssViaAttr, scopeSelector);
}

if (!nativeShadow &&
!Polymer.StyleUtil.isKeyframesSelector(rule) &&
rule.cssText) {
self._scopeSelector(rule, hostRx, hostSelector,
element._scopeCssViaAttr, scopeSelector);
}
});
},

// Transforms `@keyframes` names to be unique for the current host.
// Example: @keyframes foo-anim -> @keyframes foo-anim-x-foo-0
_scopeKeyframes: function(rule, scopeId) {
rule.keyframesNameRx = new RegExp(rule.keyframesName, 'g');
rule.transformedKeyframesName = rule.keyframesName + '-' + scopeId;
rule.transformedSelector = rule.transformedSelector || rule.selector;
rule.selector = rule.transformedSelector.replace(
Expand Down
18 changes: 12 additions & 6 deletions src/lib/style-util.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,18 @@
rules = this.parser.parse(rules);
}
if (callback) {
this.forEachStyleRule(rules, callback);
this.forEachRule(rules, callback);
}
return this.parser.stringify(rules, preserveProperties);
},

forRulesInStyles: function(styles, callback) {
forRulesInStyles: function(styles, styleRuleCallback, keyframesRuleCallback) {
if (styles) {
for (var i=0, l=styles.length, s; (i<l) && (s=styles[i]); i++) {
this.forEachStyleRule(this.rulesForStyle(s), callback);
this.forEachRule(
this.rulesForStyle(s),
styleRuleCallback,
keyframesRuleCallback);
}
}
},
Expand All @@ -52,20 +55,23 @@
rule.parent.type === Polymer.StyleUtil.ruleTypes.KEYFRAMES_RULE;
},

forEachStyleRule: function(node, callback) {
forEachRule: function(node, styleRuleCallback, keyframesRuleCallback) {
if (!node) {
return;
}
var skipRules = false;
if (node.type === this.ruleTypes.STYLE_RULE) {
callback(node);
styleRuleCallback(node);
} else if (keyframesRuleCallback &&
node.type === this.ruleTypes.KEYFRAMES_RULE) {
keyframesRuleCallback(node);
} else if (node.type === this.ruleTypes.MIXIN_RULE) {
skipRules = true;
}
var r$ = node.rules;
if (r$ && !skipRules) {
for (var i=0, l=r$.length, r; (i<l) && (r=r$[i]); i++) {
this.forEachStyleRule(r, callback);
this.forEachRule(r, styleRuleCallback, keyframesRuleCallback);
}
}
},
Expand Down
15 changes: 11 additions & 4 deletions test/smoke/keyframes.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@
--color: blue;
--anim-color: red;
}

.alternative {
--color: green;
--anim-color: blue;
}
</style>

<dom-module id="test-keyframes">
Expand All @@ -42,12 +47,14 @@
Polymer({
is: 'test-keyframes'
});

</script>
</dom-module>

<test-keyframes></test-keyframes>

<p>Text should be the color blue. Background should animate from the color red to the color yellow, and then become transparent.</p>
<test-keyframes>red</test-keyframes>

</body>
<p>Text should be the color green. Background should animate from the color blue to the color yellow, and then become transparent.</p>
<test-keyframes class="alternative">blue</test-keyframes>

</body>
36 changes: 18 additions & 18 deletions test/unit/styling-cross-scope-var.html
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,24 @@
<dom-module id="x-keyframes">
<template>
<style>
:host {
display: block;
position: relative;
border: 10px solid blue;
left: 0px;
/* Prefix required by Safari <= 8 */
-webkit-animation-duration: 0.3s;
animation-duration: 0.3s;
-webkit-animation-fill-mode: forwards;
animation-fill-mode: forwards;
}

:host([animated]) {
/* Prefix required by Safari <= 8 */
-webkit-animation-name: x-keyframes-animation;
animation-name: x-keyframes-animation;
}

/* Prefix required by Safari <= 8 */
@-webkit-keyframes x-keyframes-animation {
0% {
Expand All @@ -300,24 +318,6 @@
left: var(--b);
}
}

:host {
display: block;
position: relative;
border: 10px solid blue;
left: 0px;
/* Prefix required by Safari <= 8 */
-webkit-animation-duration: 0.3s;
animation-duration: 0.3s;
-webkit-animation-fill-mode: forwards;
animation-fill-mode: forwards;
}

:host([animated]) {
/* Prefix required by Safari <= 8 */
-webkit-animation-name: x-keyframes-animation;
animation-name: x-keyframes-animation;
}
</style>
x-keyframes
</template>
Expand Down

0 comments on commit 3adeade

Please sign in to comment.