mirror of
https://github.com/Polymer/polymer.git
synced 2025-02-25 18:55:30 -06:00
195 lines
5.2 KiB
JavaScript
195 lines
5.2 KiB
JavaScript
/**
|
|
* @suppress {checkPrototypalTypes}
|
|
@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
|
|
*/
|
|
import { PropertyAccessors } from './property-accessors.js';
|
|
|
|
import { dedupingMixin } from '../utils/mixin.js';
|
|
|
|
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';
|
|
|
|
const DIR_CHECK = /:dir\((?:ltr|rtl)\)/;
|
|
|
|
const SHIM_SHADOW = Boolean(window['ShadyDOM'] && window['ShadyDOM']['inUse']);
|
|
|
|
/**
|
|
* @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 PropertyAccessors
|
|
*/
|
|
export const DirMixin = dedupingMixin((base) => {
|
|
|
|
if (!SHIM_SHADOW) {
|
|
if (!observer) {
|
|
getRTL();
|
|
observer = new MutationObserver(updateDirection);
|
|
observer.observe(document.documentElement, {attributes: true, attributeFilter: ['dir']});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @constructor
|
|
* @implements {Polymer_PropertyAccessors}
|
|
* @private
|
|
*/
|
|
const elementBase = PropertyAccessors(base);
|
|
|
|
/**
|
|
* @polymer
|
|
* @mixinClass
|
|
* @implements {Polymer_DirMixin}
|
|
*/
|
|
class Dir extends elementBase {
|
|
|
|
/**
|
|
* @param {string} cssText .
|
|
* @param {string} baseURI .
|
|
* @return {string} .
|
|
* @suppress {missingProperties} Interfaces in closure do not inherit statics, but classes do
|
|
*/
|
|
static _processStyleText(cssText, baseURI) {
|
|
cssText = super._processStyleText(cssText, baseURI);
|
|
if (!SHIM_SHADOW && DIR_CHECK.test(cssText)) {
|
|
cssText = this._replaceDirInCssText(cssText);
|
|
this.__activateDir = true;
|
|
}
|
|
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);
|
|
return replacedText;
|
|
}
|
|
|
|
constructor() {
|
|
super();
|
|
/** @type {boolean} */
|
|
this.__autoDirOptOut = false;
|
|
}
|
|
|
|
/**
|
|
* @override
|
|
* @suppress {invalidCasts} Closure doesn't understand that `this` is an
|
|
* HTMLElement
|
|
* @return {void}
|
|
*/
|
|
ready() {
|
|
super.ready();
|
|
this.__autoDirOptOut = /** @type {!HTMLElement} */(this).hasAttribute('dir');
|
|
}
|
|
|
|
/**
|
|
* @override
|
|
* @suppress {missingProperties} If it exists on elementBase, it can be
|
|
* super'd
|
|
* @return {void}
|
|
*/
|
|
connectedCallback() {
|
|
if (elementBase.prototype.connectedCallback) {
|
|
super.connectedCallback();
|
|
}
|
|
if (this.constructor.__activateDir) {
|
|
takeRecords();
|
|
DIR_INSTANCES.push(this);
|
|
setRTL(this);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @override
|
|
* @suppress {missingProperties} If it exists on elementBase, it can be
|
|
* super'd
|
|
* @return {void}
|
|
*/
|
|
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;
|
|
});
|