-
-
Notifications
You must be signed in to change notification settings - Fork 147
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(templating): implement compose element
Added some base-level infrastructure to suppoer the compose element. Changed some type aliases to interfaces. Enabled some more framework services for injection into elements.
- Loading branch information
1 parent
4bef6f8
commit 48f91d3
Showing
11 changed files
with
663 additions
and
240 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
import { compiledElement, inject } from "../decorators"; | ||
import { ViewSlot, SwapOrder } from "../templating/view-slot"; | ||
import { ViewEngine, ITargetedInstruction, IElementInstruction, ITemplateContainer, VisualWithCentralComponent, IVisual } from "../templating/view-engine"; | ||
import { IViewOwner, IViewOwnerType, IView } from "../templating/view"; | ||
import { IContainer } from "../di"; | ||
import { IBindScope } from "../binding/observation"; | ||
import { DOM } from "../pal"; | ||
|
||
const composeSource = { | ||
name: 'au-compose', | ||
template: null, | ||
targetInstructions: null | ||
}; | ||
|
||
const composeProps = ['component', 'swapOrder', 'isComposing']; | ||
|
||
@compiledElement(composeSource) | ||
@inject(IViewOwner, DOM.Element, ViewSlot, ITargetedInstruction) | ||
export class Compose { | ||
//#region Framework-Supplied | ||
private $contentView: IView; | ||
private $bindable: IBindScope[]; | ||
private $isBound: boolean; | ||
//#endregion | ||
|
||
private task: CompositionTask = null; | ||
private visual: VisualWithCentralComponent = null; | ||
private auContent: Element = null; | ||
private baseInstruction: IElementInstruction; | ||
private compositionContainer: ITemplateContainer; | ||
|
||
component: any; | ||
swapOrder: SwapOrder; | ||
isComposing: boolean; | ||
|
||
constructor(private viewOwner: IViewOwner, private element: HTMLElement, private viewSlot: ViewSlot, instruction: ITargetedInstruction) { | ||
this.viewOwner = viewOwner; | ||
this.viewSlot = viewSlot; | ||
|
||
const type = <IViewOwnerType>viewOwner.constructor; | ||
const composeInstruction = <IElementInstruction>instruction; | ||
|
||
this.compositionContainer = type.template.container; | ||
this.baseInstruction = { | ||
type: 'element', | ||
instructions: composeInstruction.instructions.filter(x => !composeProps.includes(x)), | ||
resource: null, | ||
replacements: composeInstruction.replacements | ||
}; | ||
} | ||
|
||
private componentChanged(toBeComposed: any) { | ||
if (this.visual !== null && this.visual.component === toBeComposed) { | ||
return; | ||
} | ||
|
||
if (!toBeComposed) { | ||
this.clear(); | ||
} else { | ||
const previousTask = this.task; | ||
const newTask = this.task = new CompositionTask(this); | ||
|
||
if (previousTask !== null) { | ||
const cancelResult = previousTask.cancel(); | ||
|
||
if (cancelResult instanceof Promise) { | ||
cancelResult.then(() => newTask.start(toBeComposed)); | ||
return; | ||
} | ||
} | ||
|
||
newTask.start(toBeComposed); | ||
} | ||
} | ||
|
||
/** @internal */ compose(toBeComposed: any) { | ||
const instruction = Object.assign({}, { | ||
resource: toBeComposed, | ||
contentElement: this.createContentElement() | ||
}, this.baseInstruction); | ||
|
||
return this.swap( | ||
ViewEngine.visualFromComponent(this.compositionContainer, toBeComposed, instruction) | ||
); | ||
} | ||
|
||
private createContentElement() { | ||
let auContent = this.auContent; | ||
|
||
if (auContent == null) { | ||
this.auContent = auContent = DOM.createElement('au-content'); | ||
|
||
//If the compose element isn't using Shadow DOM | ||
if (this.$contentView !== null) { | ||
let nodes = this.$contentView.childNodes; | ||
|
||
for (let i = 0, ii = nodes.length; i < ii; ++i) { | ||
auContent.appendChild(nodes[i]); | ||
} | ||
} else { //if the compose element is using Shadow DOM | ||
let element = this.element; | ||
|
||
while(element.firstChild) { | ||
auContent.appendChild(element.firstChild); | ||
} | ||
} | ||
} | ||
|
||
return auContent.cloneNode(true); | ||
} | ||
|
||
private swap(newVisual: VisualWithCentralComponent) { | ||
let index = this.$bindable.indexOf(this.visual); | ||
if (index !== -1) { | ||
this.$bindable.splice(index, 1); | ||
} | ||
|
||
this.visual = newVisual; | ||
this.$bindable.push(newVisual); | ||
|
||
if (this.$isBound) { | ||
newVisual.bind(this.viewOwner.$scope); | ||
} | ||
|
||
return this.viewSlot.swap(newVisual, this.swapOrder || 'after'); | ||
} | ||
|
||
private clear() { | ||
this.viewSlot.removeAll(); | ||
} | ||
} | ||
|
||
class CompositionTask { | ||
private isCancelled = false; | ||
private composeResult = null; | ||
|
||
constructor(private compose: Compose) {} | ||
|
||
start(toBeComposed) { | ||
if (this.isCancelled) { | ||
return; | ||
} | ||
|
||
this.compose.isComposing = true; | ||
|
||
if (toBeComposed instanceof Promise) { | ||
toBeComposed.then(x => this.render(x)); | ||
} else { | ||
this.render(toBeComposed); | ||
} | ||
} | ||
|
||
cancel() { | ||
this.compose.isComposing = false; | ||
this.isCancelled = true; | ||
return this.composeResult; | ||
} | ||
|
||
private render(toBeComposed: any) { | ||
if (this.isCancelled) { | ||
return; | ||
} | ||
|
||
this.composeResult = this.compose.compose(toBeComposed); | ||
|
||
if (this.composeResult instanceof Promise) { | ||
this.composeResult = this.composeResult.then(() => this.compose.isComposing = false); | ||
} else { | ||
this.compose.isComposing = false; | ||
this.composeResult = null; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.