sn-controls-aurelia
Version:
Aurelia controls for sensenet ECM
260 lines (217 loc) • 8.42 kB
text/typescript
/**
* @module NavigationControls
*
*/ /** */
import { bindable, computedFrom, autoinject } from 'aurelia-framework';
import { Content, Repository } from 'sn-client-js';
import { Subscription } from 'rxjs/Subscription';
import { Observable } from 'rxjs/Observable';
/**
* A tree with bindable Root content and Selection.
* Usage example:
* ``` html
* <tree content.bind="RootContent" selection.two-way="SelectionContent"></tree>
* ```
*/
export class Tree {
Subscriptions: Subscription[] = [];
get Repository(): Repository.BaseRepository {
return this.Content.GetRepository();
};
public NestingLevel: number = 0;
/**
* Property that represents if the tree is expanded or not
*/
IsExpanded: boolean = false;
/**
* Property that represents if the tree is currently loading it's child content
*/
IsLoading: boolean = false;
/**
* Property that represents of the tree has children or not
*/
HasChildren: boolean = true;
/**
* The root level Content instance of the tree
*/
Content: Content;
/**
* List of the child content (will be reloaded on each expand)
*/
Children: Content[] = [];
/**
* An optional Content that represents the current Selection for the Tree. Used for auto-expand and Selection binding
*/
Selection: Content;
/**
* indicates if the current level Content is selected in the Tree. This getter is also used for auto-expand
*/
get IsSelected() {
if (this.Selection && this.Selection.Path && this.Content && this.Content.Path) {
if (!this.IsExpanded && this.Selection.Path.indexOf(this.Content.Path + '/') === 0 && this.Selection.Path !== this.Content.Path) {
this.Expand();
}
return this.Selection.Path === this.Content.Path;
}
return false;
}
get hasValidContent(): boolean {
return this.Content && this.Content.SavedFields && Object.keys(this.Content.SavedFields).length > 0;
}
/**
* Triggers an Expand on the current level (also used to load Children content)
*/
Expand: () => Promise<void> = () => {
return new Promise<void>((resolve, reject) => {
this.IsExpanded = true;
this.IsLoading = true;
this.Content.Children({
metadata: 'no',
expand: ['Workspace'],
select: ['Path', 'Id', 'Name', 'DisplayName', 'Icon', 'IsFolder'],
orderby: ['DisplayName', 'Name'],
filter: 'IsFolder'
}).subscribe(children => {
setTimeout(() => {
this.Children = children;
this.ReorderChildren();
resolve();
}, 200)
this.IsLoading = false;
}, (err) => {
this.IsLoading = false;
reject(err);
});
});
}
/**
* Triggers a Collapse operation (hide children)
*/
Collapse() {
this.IsExpanded = false;
}
/**
* Toggles between Expand / Collapse
*/
Toggle() {
!this.IsLoading && (!this.IsExpanded ? this.Expand() : this.Collapse());
}
/**
* Sets the Selection to the current level Content
*/
Select() {
this.Selection = this.Content;
}
ReorderChildren() {
this.Children = this.Children.sort((a, b) => {
let x: string = a['DisplayName'] || a['Name'] || '';
let y: string = b['DisplayName'] || b['Name'] || '';
return ((x < y) ? -1 : ((x > y) ? 1 : 0));
});
this.HasChildren = this.Children.length > 0;
}
ContentChanged() {
this.clearSubscriptions();
if (!this.hasValidContent) {
return;
}
this.Subscriptions.push(this.Repository.Events.OnContentCreated.subscribe(created => this.handleContentCreated(created)));
this.Subscriptions.push(this.Repository.Events.OnContentDeleted.subscribe(deleted => this.handleContentDeleted(deleted)));
this.Subscriptions.push(this.Repository.Events.OnContentModified.subscribe(mod => this.handleContentModified(mod)));
this.Subscriptions.push(this.Repository.Events.OnContentMoved.subscribe(move => this.handleContentMoved(move)));
}
handleContentCreated(created: Repository.EventModels.Created) {
if (this.IsExpanded && !this.IsLoading && created.Content.IsChildOf(this.Content)) {
this.Children.push(created.Content)
this.ReorderChildren();
}
}
handleContentDeleted(deleted: Repository.EventModels.Deleted) {
const child = this.Children.find(c => c.Id === deleted.ContentData.Id);
if (this.IsExpanded && !this.IsLoading && child) {
this.Children = this.Children.filter(c => c !== child);
}
}
handleContentModified(modified: Repository.EventModels.Modified) {
if (this.IsExpanded && !this.IsLoading && this.Children.find(c => c != null && c.Id === modified.Content.Id)) {
this.ReorderChildren();
}
}
handleContentMoved(moved: Repository.EventModels.ContentMoved): Observable<Content> {
const child = this.Children.find(c => c.Id === moved.Content.Id);
if (this.IsExpanded && child) {
this.Children = this.Children.filter(c => c !== child);
return Observable.of(child);
}
if (moved.To === this.Content.Path) {
if (this.IsExpanded) {
this.IsLoading = true;
const request = this.Repository.Load(moved.Content.Id || moved.Content.Path || '', { select: 'all' });
request.subscribe(c => {
if (c.IsFolder){
this.Children.push(c);
}
this.IsLoading = false;
this.ReorderChildren();
}, err => {
this.IsLoading = false;
})
return request;
}
}
return Observable.of(moved.Content);
}
clearSubscriptions() {
this.Subscriptions.forEach(subscription => {
subscription.unsubscribe();
});
this.Subscriptions = [];
}
detached() {
this.clearSubscriptions();
}
public OnDropContent: (params: {event: DragEvent, content: Content}) => void = (params: {content: Content}) => {
if (this.Content.Path && this.Content.Path !== params.content.ParentPath && this.Content.Path !== params.content.Path && !params.content.IsAncestorOf(this.Content)) {
params.content.MoveTo(this.Content.Path);
}
}
public OnDropContentList: (params: {event: DragEvent, contentList: Content[]}) => void = (params) => {
if (this.Content.Path){
if (params.event.ctrlKey){
this.Repository.CopyBatch(params.contentList, this.Content.Path);
} else {
this.Repository.MoveBatch(params.contentList, this.Content.Path);
}
}
};
public OnDropFiles: (params: {event: DragEvent, files: FileList}) => void = (params) => {
};
async dropContent(event: DragEvent, stringifiedContent: string, stringifiedContentList?: string[], files?: FileList) {
if (stringifiedContent){
const droppedContent = this.Repository.ParseContent(stringifiedContent);
this.OnDropContent({event, content: droppedContent});
}
if (stringifiedContentList){
const contentList = stringifiedContentList.map(c => this.Repository.ParseContent(c))
this.OnDropContentList({event, contentList});
}
if (files){
this.OnDropFiles({event, files});
}
}
}