Skip to content

Commit

Permalink
feat(pal): working on view abstractions
Browse files Browse the repository at this point in the history
  • Loading branch information
EisenbergEffect committed May 6, 2018
1 parent 24f5cf2 commit db361dc
Show file tree
Hide file tree
Showing 11 changed files with 286 additions and 195 deletions.
204 changes: 119 additions & 85 deletions scripts/app-bundle.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion scripts/app-bundle.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion scripts/vendor-bundle.js
Original file line number Diff line number Diff line change
Expand Up @@ -2145,4 +2145,4 @@ var requirejs, require, define;
}(this, (typeof setTimeout === 'undefined' ? undefined : setTimeout)));

_aureliaConfigureModuleLoader();
function _aureliaConfigureModuleLoader(){requirejs.config({"baseUrl":"src/","paths":{"root":"src","resources":"resources","elements":"resources/elements","attributes":"resources/attributes","valueConverters":"resources/value-converters","bindingBehaviors":"resources/binding-behaviors","app-bundle":"../scripts/app-bundle"},"packages":[],"stubModules":["text"],"shim":{},"bundles":{"app-bundle":["app-config","app","environment","generated-configuration","main","name-tag-config","name-tag","debug/configuration","debug/reporter","debug/task-queue","runtime/aurelia","runtime/decorators","runtime/di","runtime/dom","runtime/interfaces","runtime/platform","runtime/reporter","runtime/task-queue","debug/binding/binding-context","debug/binding/unparser","jit/binding/expression","runtime/configuration/standard","runtime/binding/array-change-records","runtime/binding/array-observation","runtime/binding/ast","runtime/binding/binding-context","runtime/binding/binding-mode","runtime/binding/binding","runtime/binding/call","runtime/binding/checked-observer","runtime/binding/class-observer","runtime/binding/collection-observation","runtime/binding/connect-queue","runtime/binding/connectable-binding","runtime/binding/dirty-checker","runtime/binding/element-observation","runtime/binding/event-manager","runtime/binding/expression","runtime/binding/listener","runtime/binding/map-change-records","runtime/binding/map-observation","runtime/binding/observation","runtime/binding/observer-locator","runtime/binding/property-observation","runtime/binding/ref","runtime/binding/select-value-observer","runtime/binding/set-observation","runtime/binding/signal","runtime/binding/subscriber-collection","runtime/binding/svg-analyzer","runtime/resources/attr-binding-behavior","runtime/resources/binding-mode-behaviors","runtime/resources/compose","runtime/resources/debounce-binding-behavior","runtime/resources/else","runtime/resources/if-core","runtime/resources/if","runtime/resources/replaceable","runtime/resources/sanitize","runtime/resources/self-binding-behavior","runtime/resources/signals","runtime/resources/throttle-binding-behavior","runtime/resources/update-trigger-binding-behavior","runtime/resources/with","runtime/templating/animator","runtime/templating/component","runtime/templating/instructions","runtime/templating/lifecycle","runtime/templating/render-slot","runtime/templating/shadow-dom","runtime/templating/view-engine","runtime/templating/view","svg/binding/svg-analyzer"]}})}
function _aureliaConfigureModuleLoader(){requirejs.config({"baseUrl":"src/","paths":{"root":"src","resources":"resources","elements":"resources/elements","attributes":"resources/attributes","valueConverters":"resources/value-converters","bindingBehaviors":"resources/binding-behaviors","app-bundle":"../scripts/app-bundle"},"packages":[],"stubModules":["text"],"shim":{},"bundles":{"app-bundle":["app-config","app","environment","generated-configuration","main","name-tag-config","name-tag","debug/configuration","debug/reporter","debug/task-queue","runtime/aurelia","runtime/decorators","runtime/di","runtime/dom","runtime/interfaces","runtime/platform","runtime/reporter","runtime/task-queue","debug/binding/binding-context","debug/binding/unparser","jit/binding/expression","runtime/binding/array-change-records","runtime/binding/array-observation","runtime/binding/ast","runtime/binding/binding-context","runtime/binding/binding-mode","runtime/binding/binding","runtime/binding/call","runtime/binding/checked-observer","runtime/binding/class-observer","runtime/binding/collection-observation","runtime/binding/connect-queue","runtime/binding/connectable-binding","runtime/binding/dirty-checker","runtime/binding/element-observation","runtime/binding/event-manager","runtime/binding/expression","runtime/binding/listener","runtime/binding/map-change-records","runtime/binding/map-observation","runtime/binding/observation","runtime/binding/observer-locator","runtime/binding/property-observation","runtime/binding/ref","runtime/binding/select-value-observer","runtime/binding/set-observation","runtime/binding/signal","runtime/binding/subscriber-collection","runtime/binding/svg-analyzer","runtime/configuration/standard","runtime/resources/attr-binding-behavior","runtime/resources/binding-mode-behaviors","runtime/resources/compose","runtime/resources/debounce-binding-behavior","runtime/resources/else","runtime/resources/if-core","runtime/resources/if","runtime/resources/replaceable","runtime/resources/sanitize","runtime/resources/self-binding-behavior","runtime/resources/signals","runtime/resources/throttle-binding-behavior","runtime/resources/update-trigger-binding-behavior","runtime/resources/with","runtime/templating/animator","runtime/templating/component","runtime/templating/instructions","runtime/templating/lifecycle","runtime/templating/render-slot","runtime/templating/shadow-dom","runtime/templating/view-engine","runtime/templating/view","svg/binding/svg-analyzer"]}})}
98 changes: 90 additions & 8 deletions src/runtime/dom.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,31 @@
import { IContainer, IResolver, DI } from "./di";
import { IElementComponent } from "./templating/component";
import { ContentView } from "./templating/view";

export const INode = DI.createInterface('INode');
export interface INode {
readonly parentNode: INode | null;

export interface INodeLike {
readonly firstChild: INode | null;
readonly lastChild: INode | null;

readonly childNodes: ArrayLike<INode>;
}

export interface INode extends INodeLike {
readonly parentNode: INode | null;
readonly nextSibling: INode | null;
readonly previousSibling: INode | null;
}

export interface IView extends INodeLike {
childNodes: INode[];

findTargets(): ArrayLike<INode>;
appendChild(child: INode): void;
insertBefore(refNode: INode): void;
appendTo(parent: INode): void;
remove(): void;
}

export interface IChildObserver {
childNodes: ArrayLike<INode>;
disconnect(): void;
Expand All @@ -25,10 +38,10 @@ let platformSupportsShadowDOM = function() {
};

export const DOM = {
createFactoryFromMarkup(markup: string): () => INode {
createFactoryFromMarkup(markup: string): () => IView {
let template = document.createElement('template');
template.innerHTML = markup;
return () => <any>template.content.cloneNode(true);
return () => new TemplateView(template);
},

createElement(name: string): INode {
Expand All @@ -41,7 +54,26 @@ export const DOM = {

createChildObserver(parent: INode, callback: () => void, options?: any): IChildObserver {
if (DOM.isUsingSlotEmulation(parent)) {
throw new Error('Not Implemented');
let component = DOM.getComponentForNode(parent);
let contentView = <ContentView>component.$contentView;
let observer = {
get childNodes() {
return contentView.childNodes;
},
disconnect() {
callback = null;
}
};

// TODO: materialize content

contentView.notifyChildrenChanged = function() {
if (callback !== null) {
callback();
}
};

return observer;
} else {
let observer = new MutationObserver(callback);
(<any>observer).childNodes = parent.childNodes;
Expand All @@ -63,8 +95,8 @@ export const DOM = {
return (<Node>node).cloneNode(deep);
},

findCompileTargets(node: INode): ArrayLike<INode> {
return (<Element>node).querySelectorAll('.au');
getComponentForNode(node: INode): IElementComponent | null {
return (<any>node).$component || null; //Set during component $hydrate
},

isUsingSlotEmulation(node: INode): boolean {
Expand Down Expand Up @@ -186,3 +218,53 @@ function getAttribute(name) {
function setAttribute(name, value) {
this.$proxyTarget.setAttribute(name, value);
}

class TemplateView implements IView {
private fragment: DocumentFragment;

firstChild: INode;
lastChild: INode;
childNodes: INode[];

constructor(template: HTMLTemplateElement) {
let container = this.fragment = <DocumentFragment>template.content.cloneNode(true);
this.firstChild = container.firstChild;
this.lastChild = container.lastChild;
this.childNodes = Array.from(container.childNodes);
}

appendChild(node: INode) {
DOM.appendChild(this.fragment, node);
}

findTargets(): ArrayLike<INode> {
return this.fragment.querySelectorAll('.au');
}

insertBefore(refNode: INode): void {
DOM.insertBefore(this.fragment, refNode);
}

appendTo(parent: INode): void {
DOM.appendChild(parent, this.fragment);
}

remove(): void {
let fragment = this.fragment;
let current = this.firstChild;
let end = this.lastChild;
let append = DOM.appendChild;
let next;

while (current) {
next = current.nextSibling;
append(fragment, current);

if (current === end) {
break;
}

current = next;
}
}
}
10 changes: 5 additions & 5 deletions src/runtime/platform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const global = (function() {

export const PLATFORM = {
global: global,
emptyArray: Object.freeze([]),
emptyArray: <Array<any>>Object.freeze([]),
emptyObject: Object.freeze({}),

now(): number {
Expand All @@ -26,7 +26,7 @@ export const PLATFORM = {
return requestAnimationFrame(callback);
},

createTaskFlushRequester(callback: () => void) {
createTaskFlushRequester(onFlush: () => void) {
return function requestFlush() {
// We dispatch a timeout with a specified delay of 0 for engines that
// can reliably accommodate that request. This will usually be snapped
Expand All @@ -44,14 +44,14 @@ export const PLATFORM = {
// flush.
clearTimeout(timeoutHandle);
clearInterval(intervalHandle);
callback();
onFlush();
}
};
},

createMicroTaskFlushRequestor(callback: () => void): () => void {
createMicroTaskFlushRequestor(onFlush: () => void): () => void {
let toggle = 1;
const observer = new MutationObserver(callback);
const observer = new MutationObserver(onFlush);
const node = document.createTextNode('');

observer.observe(node, { characterData: true });
Expand Down
4 changes: 2 additions & 2 deletions src/runtime/resources/compose.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import { customElement, inject } from "../decorators";
import { IRenderSlot, SwapOrder } from "../templating/render-slot";
import { ViewEngine, ITemplateContainer, VisualWithCentralComponent, IVisual } from "../templating/view-engine";
import { ITargetedInstruction, IHydrateElementInstruction, TargetedInstructionType } from "../templating/instructions";
import { IViewOwner, IViewOwnerType, IView } from "../templating/view";
import { IViewOwner, IViewOwnerType } from "../templating/view";
import { IContainer } from "../di";
import { IBindScope } from "../binding/observation";
import { INode, DOM } from "../dom";
import { INode, DOM, IView } from "../dom";

const composeSource = {
name: 'au-compose',
Expand Down
10 changes: 7 additions & 3 deletions src/runtime/templating/component.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ViewEngine, ITemplate, } from "./view-engine";
import { IView, View, IViewOwner } from "./view";
import { View, IViewOwner } from "./view";
import { TaskQueue } from "../task-queue";
import { Observer } from "../binding/property-observation";
import { IEmulatedShadowSlot, ShadowDOMEmulation } from "./shadow-dom";
Expand All @@ -12,9 +12,11 @@ import { IScope, BindingContext } from "../binding/binding-context";
import { IRenderSlot } from "./render-slot";
import { IBindSelf, IAttach, AttachContext, DetachContext } from "./lifecycle";
import { ICompiledViewSource, IBindableInstruction } from "./instructions";
import { INode, DOM } from "../dom";
import { INode, DOM, IView } from "../dom";

export interface IElementComponent extends IBindSelf, IAttach, IViewOwner {
$view: IView;
$contentView: IView;
$slots: Record<string, IEmulatedShadowSlot>;
$usingSlotEmulation: boolean;

Expand Down Expand Up @@ -240,6 +242,8 @@ export const Component = {
this.$usingSlotEmulation = DOM.isUsingSlotEmulation(this.$host);
this.$contentView = View.fromCompiledContent(this.$host, contentOverride);
this.$view = this.$createView(this.$host, replacements);

(<any>this.$host).$component = this;

if (this.$behavior.hasCreated) {
(<any>this).created();
Expand Down Expand Up @@ -300,7 +304,7 @@ export const Component = {
this.$slot.$attach(context);
}

//Native ShadowDOM would be distributed as soon as we append the view above.
//Native ShadowDOM would be distributed as soon as we append the view below.
//So, we emulate the distribution of nodes at the same time.
if (this.$contentView !== View.none && this.$slots) {
ShadowDOMEmulation.distributeView(this.$contentView, this.$slots);
Expand Down
42 changes: 39 additions & 3 deletions src/runtime/templating/render-slot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import { IBindScope } from '../binding/observation';
import { Reporter } from '../reporter';
import { IAttach, AttachContext, DetachContext } from './lifecycle';
import { DI } from '../di';
import { INode } from '../dom';
import { INode, IView } from '../dom';
import { ContentView } from './view';

function appendVisualToContainer(visual: IVisual, owner: RenderSlotImplementation) {
visual.$view.appendTo(owner.anchor);
Expand All @@ -17,6 +18,17 @@ function addVisualToList(visual: IVisual, owner: RenderSlotImplementation) {
}

function projectAddVisualToList(visual: IVisual, owner: RenderSlotImplementation) {
let contentView = owner.source;

if (contentView.notifyChildrenChanged) {
let contentNodes = contentView.childNodes;
let contentIndex = contentNodes.indexOf(owner.anchor) + 1;
let projectedNodes = Array.from(visual.$view.childNodes);

contentNodes.splice(contentIndex, 0, ...projectedNodes);
contentView.notifyChildrenChanged();
}

visual.$view.remove = () => ShadowDOMEmulation.undistributeView(visual.$view, owner.slots, owner);
ShadowDOMEmulation.distributeView(visual.$view, owner.slots, owner);
}
Expand All @@ -26,6 +38,17 @@ function insertVisualAtIndex(visual: IVisual, owner: RenderSlotImplementation, i
}

function projectInsertVisualAtIndex(visual: IVisual, owner: RenderSlotImplementation, index: number) {
let contentView = owner.source;

if (contentView.notifyChildrenChanged) {
let contentNodes = contentView.childNodes;
let contentIndex = contentNodes.indexOf(owner.children[index].$view.firstChild);
let projectedNodes = Array.from(visual.$view.childNodes);

contentNodes.splice(index, 0, ...projectedNodes);
contentView.notifyChildrenChanged();
}

visual.$view.remove = () => ShadowDOMEmulation.undistributeView(visual.$view, owner.slots, owner);
ShadowDOMEmulation.distributeView(visual.$view, owner.slots, owner, index);
}
Expand All @@ -35,6 +58,17 @@ function removeView(visual: IVisual, owner: RenderSlotImplementation) {
}

function projectRemoveView(visual: IVisual, owner: RenderSlotImplementation) {
let contentView = owner.source;

if (contentView.notifyChildrenChanged) {
let contentNodes = contentView.childNodes;
let startIndex = contentNodes.indexOf(visual.$view.firstChild);
let endIndex = contentNodes.indexOf(visual.$view.lastChild);

contentNodes.splice(startIndex, (endIndex - startIndex) + 1);
contentView.notifyChildrenChanged();
}

ShadowDOMEmulation.undistributeView(visual.$view, owner.slots, owner);
}

Expand Down Expand Up @@ -108,7 +142,7 @@ export interface IRenderSlot extends IAttach {
*/
removeMany(visualsToRemove: IVisual[], returnToCache?: boolean, skipAnimation?: boolean): void | IVisual[] | Promise<IVisual[]>;

/** @internal */ projectTo(slots: Record<string, IEmulatedShadowSlot>): void;
/** @internal */ projectTo(slots: Record<string, IEmulatedShadowSlot>, source: IView): void;
}

export const RenderSlot = {
Expand All @@ -125,6 +159,7 @@ class RenderSlotImplementation implements IRenderSlot {

/** @internal */ public children: IVisual[] = [];
/** @internal */ public slots: Record<string, IEmulatedShadowSlot> = null;
/** @internal */ public source: ContentView = null;

constructor(public anchor: INode, anchorIsContainer: boolean) {
(<any>anchor).$slot = this; // Usage: Shadow DOM Emulation
Expand Down Expand Up @@ -312,11 +347,12 @@ class RenderSlotImplementation implements IRenderSlot {
}
}

projectTo(slots: Record<string, IEmulatedShadowSlot>): void {
projectTo(slots: Record<string, IEmulatedShadowSlot>, source: IView): void {
this.slots = slots;
this.addVisualCore = projectAddVisualToList;
this.insertVisualCore = projectInsertVisualAtIndex;
this.removeViewCore = projectRemoveView;
this.source = <ContentView>source;

if (this.$isAttached) {
const children = this.children;
Expand Down
12 changes: 6 additions & 6 deletions src/runtime/templating/shadow-dom.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { PLATFORM } from '../platform';;
import { IView, IViewOwner } from './view';
import { IViewOwner } from './view';
import { IRenderSlot } from './render-slot';
import { IVisual, IVisualFactory } from './view-engine';
import { IBindScope } from '../binding/observation';
import { IScope } from '../binding/binding-context';
import { IAttach, AttachContext, DetachContext } from './lifecycle';
import { DOM, INode } from '../dom';
import { DOM, INode, IView } from '../dom';

type ProjectionSource = IRenderSlot | IEmulatedShadowSlot;

Expand All @@ -31,7 +31,7 @@ export interface IEmulatedShadowSlot extends IBindScope, IAttach {
/** @internal */ projectFrom(view: IView, projectionSource: ProjectionSource);
/** @internal */ addNode(view: IView, node: SlotNode, projectionSource: ProjectionSource, index: number, destination?: string);
/** @internal */ renderFallback(view: IView, nodes: SlotNode[], projectionSource: ProjectionSource, index: number);
/** @internal */ projectTo?(slots: Record<string, IEmulatedShadowSlot>);
/** @internal */ projectTo?(slots: Record<string, IEmulatedShadowSlot>, source: IView);
}

const noNodes = <SlotNode[]>PLATFORM.emptyArray;
Expand Down Expand Up @@ -310,7 +310,7 @@ class ShadowSlot extends ShadowSlotBase implements IEmulatedShadowSlot {
return this.anchor;
}

projectTo(slots: Record<string, IEmulatedShadowSlot>) {
projectTo(slots: Record<string, IEmulatedShadowSlot>, source: IView) {
this.destinationSlots = slots;
}

Expand All @@ -337,7 +337,7 @@ function distributeNodes(view: IView, nodes: SlotNode[], slots: Record<string, I
const currentNode = nodes[i];

if (currentNode.$isContentProjectionSource) {
currentNode.$slot.projectTo(slots);
currentNode.$slot.projectTo(slots, view);

for (const slotName in slots) {
slots[slotName].projectFrom(view, currentNode.$slot);
Expand Down Expand Up @@ -393,7 +393,7 @@ function getSlotName(node: INode): string {

export const ShadowDOMEmulation = {
createSlot(target: INode, owner: IViewOwner, name?: string, destination?: string, fallbackFactory?: IVisualFactory) {
const anchor = <any>DOM.createAnchor();
const anchor = <SlotNode>DOM.createAnchor();

DOM.replaceNode(anchor, target);

Expand Down
Loading

0 comments on commit db361dc

Please sign in to comment.