refactor(Compiler): introduce ShimComponent to shim CSS & DOM in emulated mode
Closes #715
This commit is contained in:
committed by
Misko Hevery
parent
5111f9ae37
commit
d0ca07afaa
@@ -9,9 +9,10 @@ import {ElementBindingMarker} from './element_binding_marker';
|
||||
import {ProtoViewBuilder} from './proto_view_builder';
|
||||
import {ProtoElementInjectorBuilder} from './proto_element_injector_builder';
|
||||
import {ElementBinderBuilder} from './element_binder_builder';
|
||||
import {ShadowDomTransformer} from './shadow_dom_transformer';
|
||||
import {ShimShadowCss} from './shim_shadow_css';
|
||||
import {ShimShadowDom} from './shim_shadow_dom';
|
||||
import {DirectiveMetadata} from 'angular2/src/core/compiler/directive_metadata';
|
||||
import {ShadowDomStrategy, NativeShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy';
|
||||
import {ShadowDomStrategy, EmulatedShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy';
|
||||
import {stringify} from 'angular2/src/facade/lang';
|
||||
import {DOM} from 'angular2/src/facade/dom';
|
||||
|
||||
@@ -31,8 +32,8 @@ export function createDefaultSteps(
|
||||
|
||||
var steps = [new ViewSplitter(parser, compilationUnit)];
|
||||
|
||||
if (!(shadowDomStrategy instanceof NativeShadowDomStrategy)) {
|
||||
var step = new ShadowDomTransformer(compiledComponent, shadowDomStrategy, DOM.defaultDoc().head);
|
||||
if (shadowDomStrategy instanceof EmulatedShadowDomStrategy) {
|
||||
var step = new ShimShadowCss(compiledComponent, shadowDomStrategy, DOM.defaultDoc().head);
|
||||
ListWrapper.push(steps, step);
|
||||
}
|
||||
|
||||
@@ -46,5 +47,10 @@ export function createDefaultSteps(
|
||||
new ElementBinderBuilder(parser, compilationUnit)
|
||||
]);
|
||||
|
||||
if (shadowDomStrategy instanceof EmulatedShadowDomStrategy) {
|
||||
var step = new ShimShadowDom(compiledComponent, shadowDomStrategy);
|
||||
ListWrapper.push(steps, step);
|
||||
}
|
||||
|
||||
return steps;
|
||||
}
|
||||
|
||||
+7
-32
@@ -4,24 +4,20 @@ import {CompileControl} from './compile_control';
|
||||
|
||||
import {DirectiveMetadata} from 'angular2/src/core/compiler/directive_metadata';
|
||||
import {ShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy';
|
||||
import {shimCssText} from 'angular2/src/core/compiler/shadow_dom_emulation/shim_css';
|
||||
|
||||
import {DOM, Element} from 'angular2/src/facade/dom';
|
||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||
import {StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
import {isPresent, isBlank, Type} from 'angular2/src/facade/lang';
|
||||
|
||||
var _cssCache = StringMapWrapper.create();
|
||||
|
||||
export class ShadowDomTransformer extends CompileStep {
|
||||
_selector: string;
|
||||
export class ShimShadowCss extends CompileStep {
|
||||
_strategy: ShadowDomStrategy;
|
||||
_styleHost: Element;
|
||||
_lastInsertedStyle: Element;
|
||||
_component: Type;
|
||||
|
||||
constructor(cmpMetadata: DirectiveMetadata, strategy: ShadowDomStrategy, styleHost: Element) {
|
||||
super();
|
||||
this._strategy = strategy;
|
||||
this._selector = cmpMetadata.annotation.selector;
|
||||
this._component = cmpMetadata.type;
|
||||
this._styleHost = styleHost;
|
||||
this._lastInsertedStyle = null;
|
||||
}
|
||||
@@ -33,34 +29,13 @@ export class ShadowDomTransformer extends CompileStep {
|
||||
if (this._strategy.extractStyles()) {
|
||||
DOM.remove(current.element);
|
||||
var css = DOM.getText(current.element);
|
||||
if (this._strategy.shim()) {
|
||||
// The css generated here is unique for the component (because of the shim).
|
||||
// Then we do not need to cache it.
|
||||
css = shimCssText(css, this._selector);
|
||||
this._insertStyle(this._styleHost, css);
|
||||
} else {
|
||||
var seen = isPresent(StringMapWrapper.get(_cssCache, css));
|
||||
if (!seen) {
|
||||
StringMapWrapper.set(_cssCache, css, true);
|
||||
this._insertStyle(this._styleHost, css);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (this._strategy.shim()) {
|
||||
try {
|
||||
DOM.setAttribute(current.element, this._selector, '');
|
||||
} catch(e) {
|
||||
// TODO(vicb): for now only simple selector (tag name) are supported
|
||||
}
|
||||
var shimComponent = this._strategy.getShimComponent(this._component);
|
||||
css = shimComponent.shimCssText(css);
|
||||
this._insertStyle(this._styleHost, css);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clearCache() {
|
||||
_cssCache = StringMapWrapper.create();
|
||||
}
|
||||
|
||||
_insertStyle(el: Element, css: string) {
|
||||
var style = DOM.createStyleElement(css);
|
||||
if (isBlank(this._lastInsertedStyle)) {
|
||||
@@ -0,0 +1,38 @@
|
||||
import {CompileStep} from './compile_step';
|
||||
import {CompileElement} from './compile_element';
|
||||
import {CompileControl} from './compile_control';
|
||||
|
||||
import {isPresent} from 'angular2/src/facade/lang';
|
||||
|
||||
import {DirectiveMetadata} from 'angular2/src/core/compiler/directive_metadata';
|
||||
import {ShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy';
|
||||
|
||||
import {ShimComponent} from 'angular2/src/core/compiler/shadow_dom_emulation/shim_component';
|
||||
|
||||
export class ShimShadowDom extends CompileStep {
|
||||
_strategy: ShadowDomStrategy;
|
||||
_shimComponent: ShimComponent;
|
||||
|
||||
constructor(cmpMetadata: DirectiveMetadata, strategy: ShadowDomStrategy) {
|
||||
super();
|
||||
this._strategy = strategy;
|
||||
this._shimComponent = strategy.getShimComponent(cmpMetadata.type);
|
||||
}
|
||||
|
||||
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
|
||||
if (current.ignoreBindings) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Shim the element as a child of the compiled component
|
||||
this._shimComponent.shimContentElement(current.element);
|
||||
|
||||
// If the current element is also a component, shim it as a host
|
||||
var host = current.componentDirective;
|
||||
if (isPresent(host)) {
|
||||
var shimComponent = this._strategy.getShimComponent(host.type);
|
||||
shimComponent.shimHostElement(current.element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
import {Element, DOM} from 'angular2/src/facade/dom';
|
||||
import {Map, MapWrapper} from 'angular2/src/facade/collection';
|
||||
import {int, isBlank, Type} from 'angular2/src/facade/lang';
|
||||
|
||||
import {ShadowCss} from './shadow_css';
|
||||
|
||||
/**
|
||||
* Used to shim component CSS & DOM
|
||||
*/
|
||||
export class ShimComponent {
|
||||
constructor(component: Type) {
|
||||
}
|
||||
|
||||
shimCssText(cssText: string): string {
|
||||
return null
|
||||
}
|
||||
|
||||
shimContentElement(element: Element) {}
|
||||
|
||||
shimHostElement(element: Element) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Native components does not need to the shim.
|
||||
*
|
||||
* All methods are no-ops.
|
||||
*/
|
||||
export class ShimNativeComponent extends ShimComponent {
|
||||
constructor(component: Type) {
|
||||
super(component);
|
||||
};
|
||||
|
||||
shimCssText(cssText: string): string {
|
||||
return cssText;
|
||||
}
|
||||
|
||||
shimContentElement(element: Element) {
|
||||
}
|
||||
|
||||
shimHostElement(element: Element) {
|
||||
}
|
||||
}
|
||||
|
||||
var _componentCache: Map<Type, int> = MapWrapper.create();
|
||||
var _componentId: int = 0;
|
||||
|
||||
// Reset the component cache - used for tests only
|
||||
export function resetShimComponentCache() {
|
||||
MapWrapper.clear(_componentCache);
|
||||
_componentId = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Emulated components need to be shimmed:
|
||||
* - An attribute needs to be added to the host,
|
||||
* - An attribute needs to be added to all nodes in their content,
|
||||
* - The CSS needs to be scoped.
|
||||
*/
|
||||
export class ShimEmulatedComponent extends ShimComponent {
|
||||
_cmpId: int;
|
||||
|
||||
constructor(component: Type) {
|
||||
super(component);
|
||||
|
||||
// Generates a unique ID for components
|
||||
var componentId = MapWrapper.get(_componentCache, component);
|
||||
if (isBlank(componentId)) {
|
||||
componentId = _componentId++;
|
||||
MapWrapper.set(_componentCache, component, componentId);
|
||||
}
|
||||
this._cmpId = componentId;
|
||||
};
|
||||
|
||||
// Scope the CSS
|
||||
shimCssText(cssText: string): string {
|
||||
var shadowCss = new ShadowCss();
|
||||
return shadowCss.shimCssText(cssText, this._getContentAttribute(), this._getHostAttribute());
|
||||
}
|
||||
|
||||
// Add an attribute on a content element
|
||||
shimContentElement(element: Element) {
|
||||
DOM.setAttribute(element, this._getContentAttribute(), '');
|
||||
}
|
||||
|
||||
// Add an attribute to the host
|
||||
shimHostElement(element: Element) {
|
||||
DOM.setAttribute(element, this._getHostAttribute(), '');
|
||||
}
|
||||
|
||||
// Return the attribute to be added to the component
|
||||
_getHostAttribute() {
|
||||
return `_nghost-${this._cmpId}`;
|
||||
}
|
||||
|
||||
// Returns the attribute to be added on every single nodes in the component
|
||||
_getContentAttribute() {
|
||||
return `_ngcontent-${this._cmpId}`;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
+12
-7
@@ -1,16 +1,21 @@
|
||||
import {Type, isBlank, isPresent} from 'angular2/src/facade/lang';
|
||||
import {DOM, Element} from 'angular2/src/facade/dom';
|
||||
import {List, ListWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
import {View} from './view';
|
||||
|
||||
import {Content} from './shadow_dom_emulation/content_tag';
|
||||
import {LightDom} from './shadow_dom_emulation/light_dom';
|
||||
import {ShimComponent, ShimEmulatedComponent, ShimNativeComponent} from './shadow_dom_emulation/shim_component';
|
||||
|
||||
export class ShadowDomStrategy {
|
||||
attachTemplate(el:Element, view:View){}
|
||||
constructLightDom(lightDomView:View, shadowDomView:View, el:Element){}
|
||||
polyfillDirectives():List<Type>{ return null; }
|
||||
shim(): boolean { return false; }
|
||||
extractStyles(): boolean { return false; }
|
||||
getShimComponent(component: Type): ShimComponent {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export class EmulatedShadowDomStrategy extends ShadowDomStrategy {
|
||||
@@ -31,12 +36,12 @@ export class EmulatedShadowDomStrategy extends ShadowDomStrategy {
|
||||
return [Content];
|
||||
}
|
||||
|
||||
shim(): boolean {
|
||||
extractStyles(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
extractStyles(): boolean {
|
||||
return true;
|
||||
getShimComponent(component: Type): ShimComponent {
|
||||
return new ShimEmulatedComponent(component);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,12 +62,12 @@ export class NativeShadowDomStrategy extends ShadowDomStrategy {
|
||||
return [];
|
||||
}
|
||||
|
||||
shim(): boolean {
|
||||
extractStyles(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
extractStyles(): boolean {
|
||||
return false;
|
||||
getShimComponent(component: Type): ShimComponent {
|
||||
return new ShimNativeComponent(component);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-1
@@ -535,12 +535,15 @@ export class ProtoView {
|
||||
): ProtoView {
|
||||
|
||||
DOM.addClass(insertionElement, NG_BINDING_CLASS);
|
||||
var cmpType = rootComponentAnnotatedType.type;
|
||||
var rootProtoView = new ProtoView(insertionElement, protoChangeDetector, shadowDomStrategy);
|
||||
rootProtoView.instantiateInPlace = true;
|
||||
var binder = rootProtoView.bindElement(
|
||||
new ProtoElementInjector(null, 0, [rootComponentAnnotatedType.type], true));
|
||||
new ProtoElementInjector(null, 0, [cmpType], true));
|
||||
binder.componentDirective = rootComponentAnnotatedType;
|
||||
binder.nestedProtoView = protoView;
|
||||
var shimComponent = shadowDomStrategy.getShimComponent(cmpType);
|
||||
shimComponent.shimHostElement(insertionElement);
|
||||
return rootProtoView;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user