Skip to content

Commit 1d7fd4b

Browse files
author
Chris Joel
committed
Support @Keyframes declared after they are used.
1 parent 93eca70 commit 1d7fd4b

File tree

6 files changed

+73
-51
lines changed

6 files changed

+73
-51
lines changed

src/lib/custom-style.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@
139139
e.textContent;
140140
}
141141
if (e.textContent) {
142-
styleUtil.forEachStyleRule(styleUtil.rulesForStyle(e), function(rule) {
142+
styleUtil.forEachRule(styleUtil.rulesForStyle(e), function(rule) {
143143
styleTransformer.documentRule(rule);
144144
});
145145
// Allow all custom-styles defined in this turn to register

src/lib/style-extends.html

+3-3
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,15 @@
1616
var styleUtil = Polymer.StyleUtil;
1717

1818
return {
19-
19+
2020
hasExtends: function(cssText) {
2121
return Boolean(cssText.match(this.rx.EXTEND));
2222
},
2323

2424
transform: function(style) {
2525
var rules = styleUtil.rulesForStyle(style);
2626
var self = this;
27-
styleUtil.forEachStyleRule(rules, function(rule) {
27+
styleUtil.forEachRule(rules, function(rule) {
2828
var map = self._mapRule(rule);
2929
if (rule.parent) {
3030
var m;
@@ -72,7 +72,7 @@
7272
// TODO: this misses `%foo, .bar` as an unetended selector but
7373
// this seems rare and could possibly be unsupported.
7474
source.selector = source.selector.replace(this.rx.STRIP, '');
75-
source.selector = (source.selector && source.selector + ',\n') +
75+
source.selector = (source.selector && source.selector + ',\n') +
7676
target.selector;
7777
if (source.extends) {
7878
source.extends.forEach(function(e) {

src/lib/style-properties.html

+28-19
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,15 @@
2525
// decorates styles with rule info and returns an array of used style
2626
// property names
2727
decorateStyles: function(styles) {
28-
var self = this, props = {};
29-
styleUtil.forRulesInStyles(styles, function(rule) {
28+
var self = this, props = {}, keyframes = [];
29+
styleUtil.forRulesInStyles(styles, function onStyleRule(rule) {
3030
self.decorateRule(rule);
3131
self.collectPropertiesInCssText(rule.propertyInfo.cssText, props);
32+
}, function onKeyframesRule(rule) {
33+
keyframes.push(rule);
3234
});
35+
// Cache all found keyframes rules for later reference:
36+
styles._keyframes = keyframes;
3337
// return this list of property names *consumes* in these styles.
3438
var names = [];
3539
for (var i in props) {
@@ -263,35 +267,40 @@
263267
hostSelector;
264268
var hostRx = new RegExp(this.rx.HOST_PREFIX + rxHostSelector +
265269
this.rx.HOST_SUFFIX);
270+
var keyframesRules = element._styles._keyframes;
266271
var keyframeTransforms = {};
267272

273+
if (!nativeShadow) {
274+
// For non-ShadowDOM, we transform all known keyframes rules in
275+
// advance for the current scope. This allows us to catch keyframes
276+
// rules that appear anywhere in the stylesheet:
277+
for (var i = 0, keyframesRule = keyframesRules[i];
278+
i < keyframesRules.length;
279+
keyframesRule = keyframesRules[++i]) {
280+
this._scopeKeyframes(keyframesRule, scopeSelector);
281+
keyframeTransforms[keyframesRule.keyframesName] = function(rule, cssText) {
282+
return cssText.replace(rule.keyframesNameRx, rule.transformedKeyframesName);
283+
}.bind(this, keyframesRule);
284+
}
285+
}
286+
268287
return styleTransformer.elementStyles(element, function(rule) {
269288
self.applyProperties(rule, properties);
270289
self.applyKeyframeTransforms(rule, keyframeTransforms);
271-
if (!nativeShadow) {
272-
// If the rule is a keyframe selector, we put a transformation in
273-
// the keyframeTransforms map so that it can be applied to other
274-
// rules in the future. We use an anonymous function for the
275-
// transformer so that we can avoid creating a new RegExp for every
276-
// call to `applyKeyframeTransforms`.
277-
if (Polymer.StyleUtil.isKeyframesSelector(rule)) {
278-
var keyframesNameRx = new RegExp(rule.parent.keyframesName, 'g');
279-
self._scopeKeyframes(rule.parent, scopeSelector);
280-
keyframeTransforms[rule.parent.keyframesName] = function(cssText) {
281-
return cssText.replace(
282-
keyframesNameRx, rule.parent.transformedKeyframesName);
283-
};
284-
} else if (rule.cssText) {
285-
self._scopeSelector(rule, hostRx, hostSelector,
286-
element._scopeCssViaAttr, scopeSelector);
287-
}
290+
291+
if (!nativeShadow &&
292+
!Polymer.StyleUtil.isKeyframesSelector(rule) &&
293+
rule.cssText) {
294+
self._scopeSelector(rule, hostRx, hostSelector,
295+
element._scopeCssViaAttr, scopeSelector);
288296
}
289297
});
290298
},
291299

292300
// Transforms `@keyframes` names to be unique for the current host.
293301
// Example: @keyframes foo-anim -> @keyframes foo-anim-x-foo-0
294302
_scopeKeyframes: function(rule, scopeId) {
303+
rule.keyframesNameRx = new RegExp(rule.keyframesName, 'g');
295304
rule.transformedKeyframesName = rule.keyframesName + '-' + scopeId;
296305
rule.transformedSelector = rule.transformedSelector || rule.selector;
297306
rule.selector = rule.transformedSelector.replace(

src/lib/style-util.html

+12-6
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,18 @@
2424
rules = this.parser.parse(rules);
2525
}
2626
if (callback) {
27-
this.forEachStyleRule(rules, callback);
27+
this.forEachRule(rules, callback);
2828
}
2929
return this.parser.stringify(rules, preserveProperties);
3030
},
3131

32-
forRulesInStyles: function(styles, callback) {
32+
forRulesInStyles: function(styles, styleRuleCallback, keyframesRuleCallback) {
3333
if (styles) {
3434
for (var i=0, l=styles.length, s; (i<l) && (s=styles[i]); i++) {
35-
this.forEachStyleRule(this.rulesForStyle(s), callback);
35+
this.forEachRule(
36+
this.rulesForStyle(s),
37+
styleRuleCallback,
38+
keyframesRuleCallback);
3639
}
3740
}
3841
},
@@ -52,20 +55,23 @@
5255
rule.parent.type === Polymer.StyleUtil.ruleTypes.KEYFRAMES_RULE;
5356
},
5457

55-
forEachStyleRule: function(node, callback) {
58+
forEachRule: function(node, styleRuleCallback, keyframesRuleCallback) {
5659
if (!node) {
5760
return;
5861
}
5962
var skipRules = false;
6063
if (node.type === this.ruleTypes.STYLE_RULE) {
61-
callback(node);
64+
styleRuleCallback(node);
65+
} else if (keyframesRuleCallback &&
66+
node.type === this.ruleTypes.KEYFRAMES_RULE) {
67+
keyframesRuleCallback(node);
6268
} else if (node.type === this.ruleTypes.MIXIN_RULE) {
6369
skipRules = true;
6470
}
6571
var r$ = node.rules;
6672
if (r$ && !skipRules) {
6773
for (var i=0, l=r$.length, r; (i<l) && (r=r$[i]); i++) {
68-
this.forEachStyleRule(r, callback);
74+
this.forEachRule(r, styleRuleCallback, keyframesRuleCallback);
6975
}
7076
}
7177
},

test/smoke/keyframes.html

+11-4
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@
1717
--color: blue;
1818
--anim-color: red;
1919
}
20+
21+
.alternative {
22+
--color: green;
23+
--anim-color: blue;
24+
}
2025
</style>
2126

2227
<dom-module id="test-keyframes">
@@ -42,12 +47,14 @@
4247
Polymer({
4348
is: 'test-keyframes'
4449
});
45-
50+
4651
</script>
4752
</dom-module>
4853

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

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

60+
</body>

test/unit/styling-cross-scope-var.html

+18-18
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,24 @@
281281
<dom-module id="x-keyframes">
282282
<template>
283283
<style>
284+
:host {
285+
display: block;
286+
position: relative;
287+
border: 10px solid blue;
288+
left: 0px;
289+
/* Prefix required by Safari <= 8 */
290+
-webkit-animation-duration: 0.3s;
291+
animation-duration: 0.3s;
292+
-webkit-animation-fill-mode: forwards;
293+
animation-fill-mode: forwards;
294+
}
295+
296+
:host([animated]) {
297+
/* Prefix required by Safari <= 8 */
298+
-webkit-animation-name: x-keyframes-animation;
299+
animation-name: x-keyframes-animation;
300+
}
301+
284302
/* Prefix required by Safari <= 8 */
285303
@-webkit-keyframes x-keyframes-animation {
286304
0% {
@@ -300,24 +318,6 @@
300318
left: var(--b);
301319
}
302320
}
303-
304-
:host {
305-
display: block;
306-
position: relative;
307-
border: 10px solid blue;
308-
left: 0px;
309-
/* Prefix required by Safari <= 8 */
310-
-webkit-animation-duration: 0.3s;
311-
animation-duration: 0.3s;
312-
-webkit-animation-fill-mode: forwards;
313-
animation-fill-mode: forwards;
314-
}
315-
316-
:host([animated]) {
317-
/* Prefix required by Safari <= 8 */
318-
-webkit-animation-name: x-keyframes-animation;
319-
animation-name: x-keyframes-animation;
320-
}
321321
</style>
322322
x-keyframes
323323
</template>

0 commit comments

Comments
 (0)