/// /// namespace Leaf { export class List extends Widget { [index: number]: TListItem ListItemConstructor: WidgetConstructor constructor(el: HTMLElement) { super(el, "List") let arrayFnsNames = ["map", "forEach", "slice", "every", "some", "filter"] for (let name of arrayFnsNames) { this[name] = (...args) => { let arr = this.toArray() return arr[name].apply(arr, args) } } Object.defineProperty(this, "length", { get: () => { return this._length }, set: (v: number) => { let toRemove: TListItem[] = [] if (v > this._length) { throw new Error("can't asign length larger than the orig") } if (typeof v != "number") { throw new TypeError() } for (let i = v; i < this._length; i++) { toRemove.push(this[i]) delete this[i] } this._length = v for (let item of toRemove) { this.detach(item) } } }) this._length = 0 } public fill(datas: any[], Cons: WidgetConstructor) { this.length = 0 for (let item of datas) { let widget = new Cons(item) for (let name of widget._VMFields) { if (typeof item[name] !== "undefined") { widget.VM[name] = item[name] } } widget.ListItemData = item this.push(widget as TListItem) } } public events: Leaf.EventEmitter<{ "child/add": TListItem "child/change": TListItem "child/remove": TListItem }> private _length: number = 0 public length: number = 0 public forEach: (callbackFn: (item: TListItem, index?: number, array?: TListItem[]) => void) => void public some: (callbackFn: (item: TListItem, index?: number, array?: TListItem[]) => boolean) => boolean public every: (callbackFn: (item: TListItem, index?: number, array?: TListItem[]) => boolean) => boolean public map: (callbackFn: (item: TListItem, index?: number, array?: TListItem[]) => any) => any[] public filter: (callbackFn: (item: TListItem, index?: number, array?: TListItem[]) => boolean) => TListItem[] public slice: (start?: number, end?: number) => TListItem[] public empty() { let toRemove = [] for (let i = 0; i < this._length; i++) { toRemove.push(this[i]) delete this[i] } for (let item of toRemove) { this.detach(item) } this._length = 0 } public indexOf(item: TListItem): number { for (let i = 0; i < this._length; i++) { if (this[i] === item) { return i } } return -1 } private check(item: TListItem) { if (!(item instanceof Widget)) { throw new Error("List expect item to be Widget") } for (let i = 0; i < this._length; i++) { if (this[i] === item) { throw new Error("Duplicate ListItem") } } return true } public push(...items: TListItem[]) { for (let item of items) { this.check(item) this[this._length] = item if (this._length != 0) { item.after(this[this._length - 1]) } else { item.appendTo(this.node) } this._length += 1 this.attach(item) } } public pop(): TListItem { if (this._length === 0) return null this._length -= 1 let item = this[this._length] delete this[this._length] return item } public unshift(item: TListItem): number { this.check(item) if (this._length === 0) { item.prependTo(this.node) this[0] = item this._length = 1 this.attach(item) return } for (let index = this._length; index > 0; index -= 1) { this[index] = this[index - 1] } this[0] = item this._length += 1 item.before(this[1]) this.attach(item) return this._length } public shift(): TListItem { let result = this[0] for (let index = 0; index < this._length; index++) { this[index] = this[index + 1] } this.length -= 1 this.detach(result) return result } public splice(index: number, count?: number, ...toAdd: TListItem[]) { let result: TListItem[] = [] let toRemoves: TListItem[] = [] if (typeof count != "number" || index + count > this._length) { count === this._length - index } for (let i = 0; i < count; i++) { let item = this[index + i] toRemoves.push(item) result.push(item) } let toAddFinal = toAdd let frag = document.createDocumentFragment() toAddFinal.forEach((item) => { this.check(item) frag.appendChild(item.node) }) if (index < this._length && this._length > 0) { this.node.insertBefore(frag, this[index].node) } else { this.node.appendChild(frag) } // make a hole let increase = toAddFinal.length - count if (increase < 0) { for (let origin = index + count; origin < this._length; origin++) { this[origin + increase] = this[origin] } } else if (increase > 0) { for (let origin = this._length - 1; origin > index + count - 1; origin--) { this[origin + increase] = this[origin] } } // fill the hole for (let offset = 0; offset < toAddFinal.length; offset++) { let item = toAddFinal[offset] this[index + offset] = item } this._length += increase for (let item of toRemoves) { this.detach(item) } for (let item of toAddFinal) { this.attach(item) } return result } public removeItem(item: TListItem) { let index = this.indexOf(item) if (index < 0) return this.splice(index, 1) return item } private attach(item: TListItem) { item["parentList"] = this this.events.emit("child/add", item) this.events.emit("child/change") } private detach(item: TListItem) { item["parentList"] = null let node = item.node if (node && node.parentElement === this.node) { this.node.removeChild(node) } this.events.emit("child/remove", item) this.events.emit("child/change") } public toArray(): TListItem[] { let result: TListItem[] = [] for (let i = 0; i < this._length; i++) { result.push(this[i]) } return result } } export interface ListItem extends Widget { } }