///
///
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 {
}
}