Files
polymer/lib/mixins/dir-mixin.html
Steven Orvell 819652eb9f Allow style elements to be separate in the element template.
Previously Polymer collapsed any included shared styles into 1 style element. This prevents the browser from recognizing shared style elements as similar and optimizing them. With this change, we no longer collapse styles. Note, included styles are still currently collapsed by ShadyCSS but the polyfill will likely not do this in the future.
2017-10-19 12:56:30 -07:00

174 lines
5.3 KiB
HTML

<!--
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<link rel="import" href="property-accessors.html">
<script>
(function() {
'use strict';
const HOST_DIR = /:host\(:dir\((ltr|rtl)\)\)/g;
const HOST_DIR_REPLACMENT = ':host([dir="$1"])';
const EL_DIR = /([\s\w#\.\[\]\*]*):dir\((ltr|rtl)\)/g;
const EL_DIR_REPLACMENT = ':host([dir="$2"]) $1';
/**
* @type {!Array<!Polymer_DirMixin>}
*/
const DIR_INSTANCES = [];
/** @type {MutationObserver} */
let observer = null;
let DOCUMENT_DIR = '';
function getRTL() {
DOCUMENT_DIR = document.documentElement.getAttribute('dir');
}
/**
* @param {!Polymer_DirMixin} instance Instance to set RTL status on
*/
function setRTL(instance) {
if (!instance.__autoDirOptOut) {
const el = /** @type {!HTMLElement} */(instance);
el.setAttribute('dir', DOCUMENT_DIR);
}
}
function updateDirection() {
getRTL();
DOCUMENT_DIR = document.documentElement.getAttribute('dir');
for (let i = 0; i < DIR_INSTANCES.length; i++) {
setRTL(DIR_INSTANCES[i]);
}
}
function takeRecords() {
if (observer && observer.takeRecords().length) {
updateDirection();
}
}
/**
* Element class mixin that allows elements to use the `:dir` CSS Selector to have
* text direction specific styling.
*
* With this mixin, any stylesheet provided in the template will transform `:dir` into
* `:host([dir])` and sync direction with the page via the element's `dir` attribute.
*
* Elements can opt out of the global page text direction by setting the `dir` attribute
* directly in `ready()` or in HTML.
*
* Caveats:
* - Applications must set `<html dir="ltr">` or `<html dir="rtl">` to sync direction
* - Automatic left-to-right or right-to-left styling is sync'd with the `<html>` element only.
* - Changing `dir` at runtime is supported.
* - Opting out of the global direction styling is permanent
*
* @mixinFunction
* @polymer
* @appliesMixin Polymer.PropertyAccessors
* @memberof Polymer
*/
Polymer.DirMixin = Polymer.dedupingMixin((base) => {
if (!observer) {
getRTL();
observer = new MutationObserver(updateDirection);
observer.observe(document.documentElement, {attributes: true, attributeFilter: ['dir']});
}
/**
* @constructor
* @extends {base}
* @implements {Polymer_PropertyAccessors}
*/
const elementBase = Polymer.PropertyAccessors(base);
/**
* @polymer
* @mixinClass
* @implements {Polymer_DirMixin}
*/
class Dir extends elementBase {
/**
* @override
* @suppress {missingProperties} Interfaces in closure do not inherit statics, but classes do
*/
static _processStyleText(cssText, baseURI) {
cssText = super._processStyleText(cssText, baseURI);
cssText = this._replaceDirInCssText(cssText);
return cssText;
}
/**
* Replace `:dir` in the given CSS text
*
* @param {string} text CSS text to replace DIR
* @return {string} Modified CSS
*/
static _replaceDirInCssText(text) {
let replacedText = text;
replacedText = replacedText.replace(HOST_DIR, HOST_DIR_REPLACMENT);
replacedText = replacedText.replace(EL_DIR, EL_DIR_REPLACMENT);
if (text !== replacedText) {
this.__activateDir = true;
}
return replacedText;
}
constructor() {
super();
/** @type {boolean} */
this.__autoDirOptOut = false;
}
/**
* @suppress {invalidCasts} Closure doesn't understand that `this` is an HTMLElement
*/
ready() {
super.ready();
this.__autoDirOptOut = /** @type {!HTMLElement} */(this).hasAttribute('dir');
}
/** @suppress {missingProperties} If it exists on elementBase, it can be super'd */
connectedCallback() {
if (elementBase.prototype.connectedCallback) {
super.connectedCallback();
}
if (this.constructor.__activateDir) {
takeRecords();
DIR_INSTANCES.push(this);
setRTL(this);
}
}
/** @suppress {missingProperties} If it exists on elementBase, it can be super'd */
disconnectedCallback() {
if (elementBase.prototype.disconnectedCallback) {
super.disconnectedCallback();
}
if (this.constructor.__activateDir) {
const idx = DIR_INSTANCES.indexOf(this);
if (idx > -1) {
DIR_INSTANCES.splice(idx, 1);
}
}
}
}
Dir.__activateDir = false;
return Dir;
});
})();
</script>