diff options
Diffstat (limited to 'src/new_fields/List.ts')
-rw-r--r-- | src/new_fields/List.ts | 330 |
1 files changed, 0 insertions, 330 deletions
diff --git a/src/new_fields/List.ts b/src/new_fields/List.ts deleted file mode 100644 index fdabea365..000000000 --- a/src/new_fields/List.ts +++ /dev/null @@ -1,330 +0,0 @@ -import { Deserializable, autoObject, afterDocDeserialize } from "../client/util/SerializationHelper"; -import { Field } from "./Doc"; -import { setter, getter, deleteProperty, updateFunction } from "./util"; -import { serializable, alias, list } from "serializr"; -import { observable, action, runInAction } from "mobx"; -import { ObjectField } from "./ObjectField"; -import { RefField } from "./RefField"; -import { ProxyField } from "./Proxy"; -import { Self, Update, Parent, OnUpdate, SelfProxy, ToScriptString, ToString, Copy, Id } from "./FieldSymbols"; -import { Scripting } from "../client/util/Scripting"; -import { DocServer } from "../client/DocServer"; - -const listHandlers: any = { - /// Mutator methods - copyWithin() { - throw new Error("copyWithin not supported yet"); - }, - fill(value: any, start?: number, end?: number) { - if (value instanceof RefField) { - throw new Error("fill with RefFields not supported yet"); - } - const res = this[Self].__fields.fill(value, start, end); - this[Update](); - return res; - }, - pop(): any { - const field = toRealField(this[Self].__fields.pop()); - this[Update](); - return field; - }, - push: action(function (this: any, ...items: any[]) { - items = items.map(toObjectField); - const list = this[Self]; - const length = list.__fields.length; - for (let i = 0; i < items.length; i++) { - const item = items[i]; - //TODO Error checking to make sure parent doesn't already exist - if (item instanceof ObjectField) { - item[Parent] = list; - item[OnUpdate] = updateFunction(list, i + length, item, this); - } - } - const res = list.__fields.push(...items); - this[Update](); - return res; - }), - reverse() { - const res = this[Self].__fields.reverse(); - this[Update](); - return res; - }, - shift() { - const res = toRealField(this[Self].__fields.shift()); - this[Update](); - return res; - }, - sort(cmpFunc: any) { - this[Self].__realFields(); // coerce retrieving entire array - const res = this[Self].__fields.sort(cmpFunc ? (first: any, second: any) => cmpFunc(toRealField(first), toRealField(second)) : undefined); - this[Update](); - return res; - }, - splice: action(function (this: any, start: number, deleteCount: number, ...items: any[]) { - this[Self].__realFields(); // coerce retrieving entire array - items = items.map(toObjectField); - const list = this[Self]; - for (let i = 0; i < items.length; i++) { - const item = items[i]; - //TODO Error checking to make sure parent doesn't already exist - //TODO Need to change indices of other fields in array - if (item instanceof ObjectField) { - item[Parent] = list; - item[OnUpdate] = updateFunction(list, i + start, item, this); - } - } - const res = list.__fields.splice(start, deleteCount, ...items); - this[Update](); - return res.map(toRealField); - }), - unshift(...items: any[]) { - items = items.map(toObjectField); - const list = this[Self]; - const length = list.__fields.length; - for (let i = 0; i < items.length; i++) { - const item = items[i]; - //TODO Error checking to make sure parent doesn't already exist - //TODO Need to change indices of other fields in array - if (item instanceof ObjectField) { - item[Parent] = list; - item[OnUpdate] = updateFunction(list, i, item, this); - } - } - const res = this[Self].__fields.unshift(...items); - this[Update](); - return res; - - }, - /// Accessor methods - concat: action(function (this: any, ...items: any[]) { - this[Self].__realFields(); - return this[Self].__fields.map(toRealField).concat(...items); - }), - includes(valueToFind: any, fromIndex: number) { - if (valueToFind instanceof RefField) { - return this[Self].__realFields().includes(valueToFind, fromIndex); - } else { - return this[Self].__fields.includes(valueToFind, fromIndex); - } - }, - indexOf(valueToFind: any, fromIndex: number) { - if (valueToFind instanceof RefField) { - return this[Self].__realFields().indexOf(valueToFind, fromIndex); - } else { - return this[Self].__fields.indexOf(valueToFind, fromIndex); - } - }, - join(separator: any) { - this[Self].__realFields(); - return this[Self].__fields.map(toRealField).join(separator); - }, - lastIndexOf(valueToFind: any, fromIndex: number) { - if (valueToFind instanceof RefField) { - return this[Self].__realFields().lastIndexOf(valueToFind, fromIndex); - } else { - return this[Self].__fields.lastIndexOf(valueToFind, fromIndex); - } - }, - slice(begin: number, end: number) { - this[Self].__realFields(); - return this[Self].__fields.slice(begin, end).map(toRealField); - }, - - /// Iteration methods - entries() { - return this[Self].__realFields().entries(); - }, - every(callback: any, thisArg: any) { - return this[Self].__realFields().every(callback, thisArg); - // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway. - // If we don't want to support the array parameter, we should use this version instead - // return this[Self].__fields.every((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg); - }, - filter(callback: any, thisArg: any) { - return this[Self].__realFields().filter(callback, thisArg); - // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway. - // If we don't want to support the array parameter, we should use this version instead - // return this[Self].__fields.filter((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg); - }, - find(callback: any, thisArg: any) { - return this[Self].__realFields().find(callback, thisArg); - // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway. - // If we don't want to support the array parameter, we should use this version instead - // return this[Self].__fields.find((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg); - }, - findIndex(callback: any, thisArg: any) { - return this[Self].__realFields().findIndex(callback, thisArg); - // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway. - // If we don't want to support the array parameter, we should use this version instead - // return this[Self].__fields.findIndex((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg); - }, - forEach(callback: any, thisArg: any) { - return this[Self].__realFields().forEach(callback, thisArg); - // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway. - // If we don't want to support the array parameter, we should use this version instead - // return this[Self].__fields.forEach((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg); - }, - map(callback: any, thisArg: any) { - return this[Self].__realFields().map(callback, thisArg); - // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway. - // If we don't want to support the array parameter, we should use this version instead - // return this[Self].__fields.map((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg); - }, - reduce(callback: any, initialValue: any) { - return this[Self].__realFields().reduce(callback, initialValue); - // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway. - // If we don't want to support the array parameter, we should use this version instead - // return this[Self].__fields.reduce((acc:any, element:any, index:number, array:any) => callback(acc, toRealField(element), index, array), initialValue); - }, - reduceRight(callback: any, initialValue: any) { - return this[Self].__realFields().reduceRight(callback, initialValue); - // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway. - // If we don't want to support the array parameter, we should use this version instead - // return this[Self].__fields.reduceRight((acc:any, element:any, index:number, array:any) => callback(acc, toRealField(element), index, array), initialValue); - }, - some(callback: any, thisArg: any) { - return this[Self].__realFields().some(callback, thisArg); - // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway. - // If we don't want to support the array parameter, we should use this version instead - // return this[Self].__fields.some((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg); - }, - values() { - return this[Self].__realFields().values(); - }, - [Symbol.iterator]() { - return this[Self].__realFields().values(); - } -}; - -function toObjectField(field: Field) { - return field instanceof RefField ? new ProxyField(field) : field; -} - -function toRealField(field: Field) { - return field instanceof ProxyField ? field.value() : field; -} - -function listGetter(target: any, prop: string | number | symbol, receiver: any): any { - if (listHandlers.hasOwnProperty(prop)) { - return listHandlers[prop]; - } - return getter(target, prop, receiver); -} - -interface ListSpliceUpdate<T> { - type: "splice"; - index: number; - added: T[]; - removedCount: number; -} - -interface ListIndexUpdate<T> { - type: "update"; - index: number; - newValue: T; -} - -type ListUpdate<T> = ListSpliceUpdate<T> | ListIndexUpdate<T>; - -type StoredType<T extends Field> = T extends RefField ? ProxyField<T> : T; - -@Deserializable("list") -class ListImpl<T extends Field> extends ObjectField { - constructor(fields?: T[]) { - super(); - const list = new Proxy<this>(this, { - set: setter, - get: listGetter, - ownKeys: target => Object.keys(target.__fields), - getOwnPropertyDescriptor: (target, prop) => { - if (prop in target.__fields) { - return { - configurable: true,//TODO Should configurable be true? - enumerable: true, - }; - } - return Reflect.getOwnPropertyDescriptor(target, prop); - }, - deleteProperty: deleteProperty, - defineProperty: () => { throw new Error("Currently properties can't be defined on documents using Object.defineProperty"); }, - }); - this[SelfProxy] = list; - if (fields) { - (list as any).push(...fields); - } - return list; - } - - [key: number]: T | (T extends RefField ? Promise<T> : never); - - // this requests all ProxyFields at the same time to avoid the overhead - // of separate network requests and separate updates to the React dom. - private __realFields() { - const waiting = this.__fields.filter(f => f instanceof ProxyField && f.promisedValue()); - const promised = waiting.map(f => f instanceof ProxyField ? f.promisedValue() : ""); - // if we find any ProxyFields that don't have a current value, then - // start the server request for all of them - if (promised.length) { - const promise = DocServer.GetRefFields(promised); - // as soon as we get the fields from the server, set all the list values in one - // action to generate one React dom update. - promise.then(fields => runInAction(() => { - waiting.map((w, i) => w instanceof ProxyField && w.setValue(fields[promised[i]])); - })); - // we also have to mark all lists items with this promise so that any calls to them - // will await the batch request. - // This counts on the handler for 'promise' in the call above being invoked before the - // handler for 'promise' in the lines below. - waiting.map((w, i) => { - w instanceof ProxyField && w.setPromise(promise.then(fields => fields[promised[i]])); - }); - } - return this.__fields.map(toRealField); - } - - @serializable(alias("fields", list(autoObject(), { afterDeserialize: afterDocDeserialize }))) - private get __fields() { - return this.___fields; - } - - private set __fields(value) { - this.___fields = value; - for (const key in value) { - const field = value[key]; - if (!(field instanceof ObjectField)) continue; - (field as ObjectField)[Parent] = this[Self]; - (field as ObjectField)[OnUpdate] = updateFunction(this[Self], key, field, this[SelfProxy]); - } - } - - [Copy]() { - const copiedData = this[Self].__fields.map(f => f instanceof ObjectField ? f[Copy]() : f); - const deepCopy = new ListImpl<T>(copiedData as any); - return deepCopy; - } - - // @serializable(alias("fields", list(autoObject()))) - @observable - private ___fields: StoredType<T>[] = []; - - private [Update] = (diff: any) => { - // console.log(diff); - const update = this[OnUpdate]; - // update && update(diff); - update?.(); - } - - private [Self] = this; - private [SelfProxy]: any; - - [ToScriptString]() { - return `new List([${(this as any).map((field: any) => Field.toScriptString(field))}])`; - } - [ToString]() { - return "List"; - } -} -export type List<T extends Field> = ListImpl<T> & (T | (T extends RefField ? Promise<T> : never))[]; -export const List: { new <T extends Field>(fields?: T[]): List<T> } = ListImpl as any; - -Scripting.addGlobal("List", List);
\ No newline at end of file |