Skip to content

Commit 358d923

Browse files
committed
feat(portal): add DomPortalHost
1 parent 165ccec commit 358d923

File tree

8 files changed

+1895
-139
lines changed

8 files changed

+1895
-139
lines changed

src/core/platform/browser/browser_adapter.dart

Lines changed: 536 additions & 0 deletions
Large diffs are not rendered by default.

src/core/platform/browser/browser_adapter.ts

Lines changed: 622 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import {StringMapWrapper} from 'angular2/src/facade/collection';
2+
import {isPresent, isFunction, Type} from 'angular2/src/facade/lang';
3+
import {DomAdapter} from 'angular2/src/platform/dom/dom_adapter';
4+
import {XHRImpl} from 'angular2/src/platform/browser/xhr_impl';
5+
6+
7+
/**
8+
* Provides DOM operations in any browser environment.
9+
*/
10+
export abstract class GenericBrowserDomAdapter extends DomAdapter {
11+
private _animationPrefix: string = null;
12+
private _transitionEnd: string = null;
13+
14+
constructor() {
15+
super();
16+
try {
17+
var element = this.createElement('div', this.defaultDoc());
18+
if (isPresent(this.getStyle(element, 'animationName'))) {
19+
this._animationPrefix = '';
20+
} else {
21+
var domPrefixes = ['Webkit', 'Moz', 'O', 'ms'];
22+
for (var i = 0; i < domPrefixes.length; i++) {
23+
if (isPresent(this.getStyle(element, domPrefixes[i] + 'AnimationName'))) {
24+
this._animationPrefix = '-' + domPrefixes[i].toLowerCase() + '-';
25+
break;
26+
}
27+
}
28+
}
29+
var transEndEventNames: {[key: string]: string} = {
30+
WebkitTransition: 'webkitTransitionEnd',
31+
MozTransition: 'transitionend',
32+
OTransition: 'oTransitionEnd otransitionend',
33+
transition: 'transitionend'
34+
};
35+
StringMapWrapper.forEach(transEndEventNames, (value: string, key: string) => {
36+
if (isPresent(this.getStyle(element, key))) {
37+
this._transitionEnd = value;
38+
}
39+
});
40+
} catch (e) {
41+
this._animationPrefix = null;
42+
this._transitionEnd = null;
43+
}
44+
}
45+
46+
getXHR(): Type {
47+
return XHRImpl;
48+
}
49+
50+
getDistributedNodes(el: HTMLElement): Node[] {
51+
return (<any>el).getDistributedNodes();
52+
}
53+
54+
resolveAndSetHref(el: HTMLAnchorElement, baseUrl: string, href: string) {
55+
el.href = href == null ? baseUrl : baseUrl + '/../' + href;
56+
}
57+
58+
supportsDOMEvents(): boolean {
59+
return true;
60+
}
61+
62+
supportsNativeShadowDOM(): boolean {
63+
return isFunction((<any>this.defaultDoc().body).createShadowRoot);
64+
}
65+
66+
getAnimationPrefix(): string {
67+
return isPresent(this._animationPrefix) ? this._animationPrefix : '';
68+
}
69+
70+
getTransitionEnd(): string {
71+
return isPresent(this._transitionEnd) ? this._transitionEnd : '';
72+
}
73+
74+
supportsAnimation(): boolean {
75+
return isPresent(this._animationPrefix) && isPresent(this._transitionEnd);
76+
}
77+
}

src/core/platform/dom/dom_adapter.ts

Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
import {isBlank, Type} from 'angular2/src/facade/lang';
2+
3+
export var DOM: DomAdapter = null;
4+
5+
export function setRootDomAdapter(adapter: DomAdapter) {
6+
if (isBlank(DOM)) {
7+
DOM = adapter;
8+
}
9+
}
10+
11+
/* tslint:disable:requireParameterType */
12+
/**
13+
* Provides DOM operations in an environment-agnostic way.
14+
*/
15+
export abstract class DomAdapter {
16+
abstract hasProperty(element: any, name: string): boolean;
17+
18+
abstract setProperty(el: Element, name: string, value: any): void;
19+
20+
abstract getProperty(el: Element, name: string): any;
21+
22+
abstract invoke(el: Element, methodName: string, args: any[]): any;
23+
24+
abstract logError(error: any): void;
25+
26+
abstract log(error: any): void;
27+
28+
abstract logGroup(error: any): void;
29+
30+
abstract logGroupEnd(): void;
31+
32+
/** @deprecated */
33+
abstract getXHR(): Type;
34+
35+
/**
36+
* Maps attribute names to their corresponding property names for cases
37+
* where attribute name doesn't match property name.
38+
*/
39+
get attrToPropMap(): {[key: string]: string} {
40+
return this._attrToPropMap;
41+
};
42+
43+
set attrToPropMap(value: {[key: string]: string}) {
44+
this._attrToPropMap = value;
45+
};
46+
47+
/** @internal */
48+
_attrToPropMap: {[key: string]: string};
49+
50+
abstract parse(templateHtml: string): any;
51+
52+
abstract query(selector: string): any;
53+
54+
abstract querySelector(el: any, selector: string): HTMLElement;
55+
56+
abstract querySelectorAll(el: any, selector: string): any[];
57+
58+
abstract on(el: any, evt: any, listener: any): void;
59+
60+
abstract onAndCancel(el: any, evt: any, listener: any): Function;
61+
62+
abstract dispatchEvent(el: any, evt: any): void;
63+
64+
abstract createMouseEvent(eventType: any): any;
65+
66+
abstract createEvent(eventType: string): any;
67+
68+
abstract preventDefault(evt: any): void;
69+
70+
abstract isPrevented(evt: any): boolean;
71+
72+
abstract getInnerHTML(el: any): string;
73+
74+
abstract getOuterHTML(el: any): string;
75+
76+
abstract nodeName(node: any): string;
77+
78+
abstract nodeValue(node: any): string;
79+
80+
abstract type(node: any): string;
81+
82+
abstract content(node: any): any;
83+
84+
abstract firstChild(el: any): Node;
85+
86+
abstract nextSibling(el: any): Node;
87+
88+
abstract parentElement(el: any): Node;
89+
90+
abstract childNodes(el: any): Node[];
91+
92+
abstract childNodesAsList(el: any): Node[];
93+
94+
abstract clearNodes(el: any): void;
95+
96+
abstract appendChild(el: any, node: any): void;
97+
98+
abstract removeChild(el: any, node: any): void;
99+
100+
abstract replaceChild(el: any, newNode: any, oldNode: any): void;
101+
102+
abstract remove(el: any): Node;
103+
104+
abstract insertBefore(el: any, node: any): void;
105+
106+
abstract insertAllBefore(el: any, nodes: any): void;
107+
108+
abstract insertAfter(el: any, node: any): void;
109+
110+
abstract setInnerHTML(el: any, value: any): void;
111+
112+
abstract getText(el: any): string;
113+
114+
abstract setText(el: any, value: string): void;
115+
116+
abstract getValue(el: any): string;
117+
118+
abstract setValue(el: any, value: string): void;
119+
120+
abstract getChecked(el: any): boolean;
121+
122+
abstract setChecked(el: any, value: boolean): void;
123+
124+
abstract createComment(text: string): any;
125+
126+
abstract createTemplate(html: any): HTMLElement;
127+
128+
abstract createElement(tagName: any, doc?: any): HTMLElement;
129+
130+
abstract createElementNS(ns: string, tagName: string, doc?: any): Element;
131+
132+
abstract createTextNode(text: string, doc?: any): Text;
133+
134+
abstract createScriptTag(attrName: string, attrValue: string, doc?: any): HTMLElement;
135+
136+
abstract createStyleElement(css: string, doc?: any): HTMLStyleElement;
137+
138+
abstract createShadowRoot(el: any): any;
139+
140+
abstract getShadowRoot(el: any): any;
141+
142+
abstract getHost(el: any): any;
143+
144+
abstract getDistributedNodes(el: any): Node[];
145+
146+
abstract clone /*<T extends Node>*/(node: Node /*T*/): Node /*T*/;
147+
148+
abstract getElementsByClassName(element: any, name: string): HTMLElement[];
149+
150+
abstract getElementsByTagName(element: any, name: string): HTMLElement[];
151+
152+
abstract classList(element: any): any[];
153+
154+
abstract addClass(element: any, className: string): void;
155+
156+
abstract removeClass(element: any, className: string): void;
157+
158+
abstract hasClass(element: any, className: string): boolean;
159+
160+
abstract setStyle(element: any, styleName: string, styleValue: string): void;
161+
162+
abstract removeStyle(element: any, styleName: string): void;
163+
164+
abstract getStyle(element: any, styleName: string): string;
165+
166+
abstract hasStyle(element: any, styleName: string, styleValue?: string): boolean;
167+
168+
abstract tagName(element: any): string;
169+
170+
abstract attributeMap(element: any): Map<string, string>;
171+
172+
abstract hasAttribute(element: any, attribute: string): boolean;
173+
174+
abstract hasAttributeNS(element: any, ns: string, attribute: string): boolean;
175+
176+
abstract getAttribute(element: any, attribute: string): string;
177+
178+
abstract getAttributeNS(element: any, ns: string, attribute: string): string;
179+
180+
abstract setAttribute(element: any, name: string, value: string): void;
181+
182+
abstract setAttributeNS(element: any, ns: string, name: string, value: string): void;
183+
184+
abstract removeAttribute(element: any, attribute: string): void;
185+
186+
abstract removeAttributeNS(element: any, ns: string, attribute: string): void;
187+
188+
abstract templateAwareRoot(el: any): any;
189+
190+
abstract createHtmlDocument(): HTMLDocument;
191+
192+
abstract defaultDoc(): HTMLDocument;
193+
194+
abstract getBoundingClientRect(el: any): any;
195+
196+
abstract getTitle(): string;
197+
198+
abstract setTitle(newTitle: string): void;
199+
200+
abstract elementMatches(n: any, selector: string): boolean;
201+
202+
abstract isTemplateElement(el: any): boolean;
203+
204+
abstract isTextNode(node: any): boolean;
205+
206+
abstract isCommentNode(node: any): boolean;
207+
208+
abstract isElementNode(node: any): boolean;
209+
210+
abstract hasShadowRoot(node: any): boolean;
211+
212+
abstract isShadowRoot(node: any): boolean;
213+
214+
abstract importIntoDoc /*<T extends Node>*/(node: Node /*T*/): Node /*T*/;
215+
216+
abstract adoptNode /*<T extends Node>*/(node: Node /*T*/): Node /*T*/;
217+
218+
abstract getHref(element: any): string;
219+
220+
abstract getEventKey(event: any): string;
221+
222+
abstract resolveAndSetHref(element: any, baseUrl: string, href: string): any;
223+
224+
abstract supportsDOMEvents(): boolean;
225+
226+
abstract supportsNativeShadowDOM(): boolean;
227+
228+
abstract getGlobalEventTarget(target: string): any;
229+
230+
abstract getHistory(): History;
231+
232+
abstract getLocation(): Location;
233+
234+
abstract getBaseHref(): string;
235+
236+
abstract resetBaseElement(): void;
237+
238+
abstract getUserAgent(): string;
239+
240+
abstract setData(element: any, name: string, value: string): void;
241+
242+
abstract getComputedStyle(element: any): any;
243+
244+
abstract getData(element: any, name: string): string;
245+
246+
abstract setGlobalVar(name: string, value: any): void;
247+
248+
abstract requestAnimationFrame(callback: any): number;
249+
250+
abstract cancelAnimationFrame(id: any): void;
251+
252+
abstract performanceNow(): number;
253+
254+
abstract getAnimationPrefix(): string;
255+
256+
abstract getTransitionEnd(): string;
257+
258+
abstract supportsAnimation(): boolean;
259+
}

src/core/portal/dom-portal-host.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import {DynamicComponentLoader, AppViewManager, ComponentRef} from 'angular2/core';
2+
import {BasePortalHost, ComponentPortal, TemplatePortal} from './portal';
3+
import {MdComponentPortalAttachedToDomWithoutOriginException} from './portal-exceptions';
4+
5+
6+
/**
7+
* A PortalHost for attaching portals to an arbitrary DOM element outside of the Angular
8+
* application context.
9+
*
10+
* This is the only part of the portal core that directly touches the DOM.
11+
*/
12+
export class DomPortalHost extends BasePortalHost {
13+
constructor(
14+
private _hostDomElement: Element,
15+
private _componentLoader: DynamicComponentLoader,
16+
private _viewManager: AppViewManager) {
17+
super();
18+
}
19+
20+
/** Attach the given ComponentPortal to DOM element using the DynamicComponentLoader. */
21+
attachComponentPortal(portal: ComponentPortal): Promise<ComponentRef> {
22+
if (portal.origin == null) {
23+
throw new MdComponentPortalAttachedToDomWithoutOriginException();
24+
}
25+
26+
return this._componentLoader.loadNextToLocation(portal.component, portal.origin).then(ref => {
27+
this._hostDomElement.appendChild(ref.hostView.rootNodes[0]);
28+
this.setDisposeFn(() => ref.dispose());
29+
return ref;
30+
});
31+
}
32+
33+
attachTemplatePortal(portal: TemplatePortal): Promise<Map<string, any>> {
34+
let viewContainer = this._viewManager.getViewContainer(portal.templateRef.elementRef);
35+
let viewRef = viewContainer.createEmbeddedView(portal.templateRef);
36+
// TODO(jelbourn): locals don't currently work with DomPortalHost; investigate whether there
37+
// is a bug in Angular.
38+
portal.locals.forEach((v, k) => viewRef.setLocal(k, v));
39+
40+
viewRef.rootNodes.forEach(rootNode => {
41+
this._hostDomElement.appendChild(rootNode);
42+
});
43+
44+
this.setDisposeFn((() => {
45+
let index = viewContainer.indexOf(viewRef);
46+
if (index != -1) {
47+
viewContainer.remove(index);
48+
}
49+
}));
50+
51+
// TODO(jelbourn): Return locals from view.
52+
return Promise.resolve(new Map<string, any>());
53+
}
54+
}

0 commit comments

Comments
 (0)