Skip to content

Commit 31c0ebc

Browse files
committed
Always do the :dir transform
ShadyCSS version of the :dir transform is not good enough :(
1 parent 7d044b4 commit 31c0ebc

File tree

4 files changed

+226
-114
lines changed

4 files changed

+226
-114
lines changed

lib/mixins/dir-mixin.html

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,30 +15,24 @@
1515
(function() {
1616
'use strict';
1717

18-
const HOST_DIR = /:host\(:dir\(rtl\)\)/g;
19-
const HOST_DIR_REPLACMENT = ':host([dir="rtl"])';
18+
const HOST_DIR = /:host\(:dir\((ltr|rtl)\)\)/g;
19+
const HOST_DIR_REPLACMENT = ':host([dir="$1"])';
2020

21-
const EL_DIR = /([\s\w#\.\[\]\*]*):dir\(rtl\)/g;
22-
const EL_DIR_REPLACMENT = ':host([dir="rtl"]) $1';
23-
24-
const NATIVE_SHADOW = !(window.ShadyDOM && window.ShadyDOM.inUse);
21+
const EL_DIR = /([\s\w#\.\[\]\*]*):dir\((ltr|rtl)\)/g;
22+
const EL_DIR_REPLACMENT = ':host([dir="$2"]) $1';
2523

2624
/**
27-
* @type {!Array<!Dir>}
25+
* @type {!Array<!Polymer_DirMixin>}
2826
*/
2927
const DIR_INSTANCES = [];
3028

31-
if (NATIVE_SHADOW) {
32-
new MutationObserver(() => {
33-
DIR_INSTANCES.forEach(i => setRTL(i));
34-
}).observe(document.documentElement, {attributes: true, attributeFilter: ['dir']});
35-
}
29+
let observed = false;
3630

3731
/**
38-
* @param {!Dir} instance Instance to set RTL status on
32+
* @param {!Polymer_DirMixin} instance Instance to set RTL status on
3933
*/
4034
function setRTL(instance) {
41-
if (NATIVE_SHADOW && !instance.__origRTLStatus) {
35+
if (!instance.__origRTLStatus) {
4236
const el = /** @type {!HTMLElement} */(instance);
4337
if (document.documentElement.getAttribute('dir') === 'rtl') {
4438
el.setAttribute('dir', 'rtl');
@@ -58,6 +52,12 @@
5852
*/
5953
Polymer.DirMixin = Polymer.dedupingMixin((base) => {
6054

55+
if (!observed) {
56+
new MutationObserver(() => {
57+
DIR_INSTANCES.forEach(i => setRTL(i));
58+
}).observe(document.documentElement, {attributes: true, attributeFilter: ['dir']});
59+
}
60+
6161
/**
6262
* @constructor
6363
* @extends {base}
@@ -78,22 +78,20 @@
7878
*/
7979
static _processStyleText(is, template, baseURI) {
8080
let cssText = super._processStyleText(is, template, baseURI);
81-
if (NATIVE_SHADOW) {
82-
cssText = cssText.replace(HOST_DIR, HOST_DIR_REPLACMENT);
83-
cssText = cssText.replace(EL_DIR, EL_DIR_REPLACMENT);
84-
}
81+
cssText = cssText.replace(HOST_DIR, HOST_DIR_REPLACMENT);
82+
cssText = cssText.replace(EL_DIR, EL_DIR_REPLACMENT);
8583
return cssText;
8684
}
8785

88-
/**
89-
* @suppress {invalidCasts} This is an element, but closure needs to be told
90-
*/
9186
constructor() {
9287
super();
9388
/** @type {boolean} */
9489
this.__origRTLStatus = false;
9590
}
9691

92+
/**
93+
* @suppress {invalidCasts} Closure doesn't understand that `this` is an HTMLElement
94+
*/
9795
ready() {
9896
super.ready();
9997
this.__origRTLStatus = /** @type {!HTMLElement} */(this).hasAttribute('dir');
@@ -104,6 +102,7 @@
104102
DIR_INSTANCES.push(this);
105103
setRTL(this);
106104
}
105+
107106
disconnectedCallback() {
108107
super.disconnectedCallback();
109108
const idx = DIR_INSTANCES.indexOf(this);

test/runner.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@
7272
'unit/logging.html',
7373
'unit/mixin-utils.html',
7474
'unit/mixin-behaviors.html',
75-
'unit/render-status.html'
75+
'unit/render-status.html',
76+
'unit/dir.html'
7677
];
7778

7879
// http://eddmann.com/posts/cartesian-product-in-javascript/

test/smoke/dir.html

Lines changed: 0 additions & 92 deletions
This file was deleted.

test/unit/dir.html

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
<!doctype html>
2+
<!--
3+
@license
4+
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
5+
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
6+
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
7+
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
8+
Code distributed by Google as part of the polymer project is also
9+
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
10+
-->
11+
<html dir="rtl">
12+
<head>
13+
<script src="../../../webcomponentsjs/webcomponents-lite.js"></script>
14+
<script src="../../../web-component-tester/browser.js"></script>
15+
<link rel="import" href="../../polymer.html">
16+
<link rel="import" href="../../lib/mixins/dir-mixin.html">
17+
</head>
18+
<body>
19+
<!--
20+
Issues
21+
1. regex will process entire string, including comments and properties, not just selectors...
22+
2. complex selectors. not ok... e.g.
23+
bad: .special > *:dir(rtl)
24+
but ok: .special:dir(rtl) > *
25+
3. multiple selectors per line? ok.
26+
-->
27+
<dom-module id="x-inner-dir">
28+
<template>
29+
<style>
30+
:host {
31+
display: block;
32+
height: 20px;
33+
color: white;
34+
}
35+
input, section {
36+
border-top-width: 0px;
37+
}
38+
39+
section:dir(rtl), input:dir(rtl) {
40+
border: 10px solid rgb(123, 123, 123);
41+
}
42+
43+
.special:dir(rtl) > * {
44+
color: rgb(0, 128, 0);
45+
}
46+
</style>
47+
<div>no rtl styling</div>
48+
<section>rtl styling</section>
49+
<input value="rtl styling">
50+
<div class="special">
51+
<div>at the right.</div>
52+
</div>
53+
</template>
54+
<script>
55+
addEventListener('WebComponentsReady', () => {
56+
class XInnerDir extends Polymer.DirMixin(Polymer.Element) {
57+
static get is() {return 'x-inner-dir';}
58+
}
59+
customElements.define(XInnerDir.is, XInnerDir);
60+
})
61+
</script>
62+
</dom-module>
63+
64+
<dom-module id="x-dir">
65+
<template>
66+
<style>
67+
:host {
68+
display: block;
69+
background-color: rgb(0, 0, 255);
70+
min-height: 100px;
71+
width: 100px;
72+
}
73+
:host(:dir(rtl)) {
74+
background-color: rgb(0, 128, 0);
75+
}
76+
#foo:dir(rtl) {
77+
border: 10px solid rgb(255, 0, 0);
78+
}
79+
.thing:dir(rtl) {
80+
border: 10px solid rgb(0, 0, 255);
81+
}
82+
</style>
83+
<div id="foo"></div>
84+
<div class="thing"></div>
85+
<x-inner-dir></x-inner-dir>
86+
</template>
87+
<script>
88+
addEventListener('WebComponentsReady', () => {
89+
class XDir extends Polymer.DirMixin(Polymer.Element) {
90+
static get is() {return 'x-dir';}
91+
}
92+
customElements.define(XDir.is, XDir);
93+
});
94+
</script>
95+
</dom-module>
96+
97+
<dom-module id="x-dir-legacy">
98+
<template>
99+
<style>
100+
:host {
101+
display: block;
102+
border: 10px solid black;
103+
}
104+
:host(:dir(rtl)) {
105+
border: 2px dotted blue;
106+
}
107+
</style>
108+
</template>
109+
<script>
110+
addEventListener('WebComponentsReady', () => {
111+
class XDirLegacy extends Polymer.DirMixin(Polymer.LegacyElementMixin(HTMLElement)) {
112+
static get is() {return 'x-dir-legacy';}
113+
}
114+
customElements.define(XDirLegacy.is, XDirLegacy);
115+
});
116+
</script>
117+
</dom-module>
118+
119+
<test-fixture id="dir">
120+
<template>
121+
<x-dir></x-dir>
122+
</template>
123+
</test-fixture>
124+
125+
<test-fixture id="preset">
126+
<template>
127+
<x-inner-dir dir="ltr"></x-inner-dir>
128+
</template>
129+
</test-fixture>
130+
131+
<test-fixture id="legacy">
132+
<template>
133+
<x-dir-legacy></x-dir-legacy>
134+
</template>
135+
</test-fixture>
136+
137+
<script>
138+
function assertComputed(node, expected, property = 'border-top-width') {
139+
let actual = getComputedStyle(node).getPropertyValue(property).trim();
140+
assert.equal(expected, actual);
141+
}
142+
143+
suite(':dir', function() {
144+
teardown(function() {
145+
document.documentElement.setAttribute('dir', 'rtl');
146+
});
147+
148+
test(':dir(rtl) functions when html element dir attr set to rtl', function() {
149+
let el = fixture('dir');
150+
assertComputed(el, 'rgb(0, 128, 0)', 'background-color');
151+
assertComputed(el.shadowRoot.querySelector('#foo'), '10px');
152+
assertComputed(el.shadowRoot.querySelector('.thing'), '10px');
153+
let inner = el.shadowRoot.querySelector('x-inner-dir');
154+
assertComputed(inner.shadowRoot.querySelector('.special > div'), 'rgb(0, 128, 0)', 'color');
155+
assertComputed(inner.shadowRoot.querySelector('section'), '10px');
156+
assertComputed(inner.shadowRoot.querySelector('input'), '10px');
157+
});
158+
159+
test(':dir reacts to html attribute changing', function(done) {
160+
let el = fixture('dir');
161+
document.documentElement.setAttribute('dir', 'ltr');
162+
requestAnimationFrame(() => {
163+
assertComputed(el, 'rgb(0, 0, 255)', 'background-color');
164+
assertComputed(el.shadowRoot.querySelector('#foo'), '0px');
165+
assertComputed(el.shadowRoot.querySelector('.thing'), '0px');
166+
let inner = el.shadowRoot.querySelector('x-inner-dir');
167+
assertComputed(inner.shadowRoot.querySelector('.special > div'), 'rgb(255, 255, 255)', 'color');
168+
assertComputed(inner.shadowRoot.querySelector('section'), '0px');
169+
assertComputed(inner.shadowRoot.querySelector('input'), '0px');
170+
done();
171+
});
172+
});
173+
174+
test('elements with dir attribute explicitly set will not change', function() {
175+
let inner = fixture('preset');
176+
assert.equal(document.documentElement.getAttribute('dir'), 'rtl');
177+
assertComputed(inner.shadowRoot.querySelector('.special > div'), 'rgb(255, 255, 255)', 'color');
178+
assertComputed(inner.shadowRoot.querySelector('section'), '0px');
179+
assertComputed(inner.shadowRoot.querySelector('input'), '0px');
180+
});
181+
182+
test('elements will adopt dir status when reconnected', function(done) {
183+
let el = fixture('dir');
184+
assertComputed(el, 'rgb(0, 128, 0)', 'background-color');
185+
let parent = el.parentNode;
186+
parent.removeChild(el);
187+
document.documentElement.setAttribute('dir', 'ltr');
188+
requestAnimationFrame(() => {
189+
parent.appendChild(el);
190+
requestAnimationFrame(() => {
191+
assertComputed(el, 'rgb(0, 0, 255)', 'background-color');
192+
done();
193+
});
194+
});
195+
});
196+
197+
test('legacy elements worth with DirMixin', function() {
198+
let el = fixture('legacy');
199+
assertComputed(el, '2px');
200+
});
201+
});
202+
</script>
203+
</body>
204+
</html>

0 commit comments

Comments
 (0)