aboutsummaryrefslogtreecommitdiff
path: root/src/fields
diff options
context:
space:
mode:
Diffstat (limited to 'src/fields')
-rw-r--r--src/fields/Doc.ts56
-rw-r--r--src/fields/List.ts34
-rw-r--r--src/fields/ObjectField.ts1
-rw-r--r--src/fields/Proxy.ts17
-rw-r--r--src/fields/Schema.ts2
-rw-r--r--src/fields/Types.ts15
-rw-r--r--src/fields/documentSchemas.ts3
7 files changed, 70 insertions, 58 deletions
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 7714ce46d..4512d5c5b 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -7,7 +7,7 @@ import { DocServer } from '../client/DocServer';
import { CollectionViewType, DocumentType } from '../client/documents/DocumentTypes';
import { scriptingGlobal, ScriptingGlobals } from '../client/util/ScriptingGlobals';
import { afterDocDeserialize, autoObject, Deserializable, SerializationHelper } from '../client/util/SerializationHelper';
-import { undoable } from '../client/util/UndoManager';
+import { undoable, UndoManager } from '../client/util/UndoManager';
import { ClientUtils, incrementTitleCopy } from '../ClientUtils';
import {
AclAdmin, AclAugment, AclEdit, AclPrivate, AclReadonly, Animation, AudioPlay, Brushed, CachedUpdates, DirectLinks,
@@ -38,7 +38,7 @@ export namespace Field {
*/
export function toKeyValueString(doc: Doc, key: string, showComputedValue?: boolean): string {
const isOnDelegate = !Doc.IsDataProto(doc) && Object.keys(doc).includes(key.replace(/^_/, ''));
- const field = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
+ const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
const valFunc = (field: FieldType): string => {
const res =
field instanceof ComputedField && showComputedValue
@@ -55,7 +55,7 @@ export namespace Field {
.trim()
.replace(/^new List\((.*)\)$/, '$1');
};
- return !Field.IsField(field) ? (key.startsWith('_') ? '=' : '') : (isOnDelegate ? '=' : '') + valFunc(field);
+ return !Field.IsField(cfield) ? (key.startsWith('_') ? '=' : '') : (isOnDelegate ? '=' : '') + valFunc(cfield);
}
export function toScriptString(field: FieldType) {
switch (typeof field) {
@@ -98,9 +98,11 @@ export namespace Field {
export function IsField(field: any, includeUndefined: boolean = false): field is FieldType | undefined {
return ['string', 'number', 'boolean'].includes(typeof field) || field instanceof ObjectField || field instanceof RefField || (includeUndefined && field === undefined);
}
+ // eslint-disable-next-line @typescript-eslint/no-shadow
export function Copy(field: any) {
return field instanceof ObjectField ? ObjectField.MakeCopy(field) : field;
}
+ UndoManager.SetFieldPrinter(toJavascriptString);
}
export type FieldType = number | string | boolean | ObjectField | RefField;
export type Opt<T> = T | undefined;
@@ -369,7 +371,7 @@ export class Doc extends RefField {
const sameAuthor = this.author === ClientUtils.CurrentUserEmail();
const fprefix = 'fields.';
Object.keys(set ?? {})
- .filter(key => Object.prototype.hasOwnProperty.call(set, key) && key.startsWith(fprefix))
+ .filter(key => key.startsWith(fprefix))
.forEach(async key => {
const fKey = key.substring(fprefix.length);
const fn = async () => {
@@ -396,7 +398,7 @@ export class Doc extends RefField {
});
const unset = diff.$unset;
Object.keys(unset ?? {})
- .filter(key => Object.prototype.hasOwnProperty.call(unset, key) && key.startsWith(fprefix))
+ .filter(key => key.startsWith(fprefix))
.forEach(async key => {
const fKey = key.substring(7);
const fn = () => {
@@ -511,15 +513,13 @@ export namespace Doc {
*/
export function assign<K extends string>(doc: Doc, fields: Partial<Record<K, Opt<FieldType>>>, skipUndefineds: boolean = false, isInitializing = false) {
isInitializing && (doc[Initializing] = true);
- Object.keys(fields)
- .filter(key => Object.prototype.hasOwnProperty.call(fields, key))
- .forEach(key => {
- const value = (fields as any)[key];
- if (!skipUndefineds || value !== undefined) {
- // Do we want to filter out undefineds?
- doc[key] = value;
- }
- });
+ Object.keys(fields).forEach(key => {
+ const value = (fields as any)[key];
+ if (!skipUndefineds || value !== undefined) {
+ // Do we want to filter out undefineds?
+ doc[key] = value;
+ }
+ });
isInitializing && (doc[Initializing] = false);
return doc;
}
@@ -640,7 +640,7 @@ export namespace Doc {
export function BestEmbedding(doc: Doc) {
const dataDoc = doc[DocData];
const availableEmbeddings = Doc.GetEmbeddings(dataDoc);
- const bestEmbedding = [...(dataDoc !== doc ? [doc] : []), ...availableEmbeddings].find(doc => !doc.embedContainer && doc.author === ClientUtils.CurrentUserEmail());
+ const bestEmbedding = [...(dataDoc !== doc ? [doc] : []), ...availableEmbeddings].find(d => !d.embedContainer && d.author === ClientUtils.CurrentUserEmail());
bestEmbedding && Doc.AddEmbedding(doc, doc);
return bestEmbedding ?? Doc.MakeEmbedding(doc);
}
@@ -673,27 +673,27 @@ export namespace Doc {
};
const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
const field = ProxyField.WithoutProxy(() => doc[key]);
- const copyObjectField = async (field: ObjectField) => {
+ const copyObjectField = async (objField: ObjectField) => {
const list = await Cast(doc[key], listSpec(Doc));
const docs = list && (await DocListCastAsync(list))?.filter(d => d instanceof Doc);
if (docs !== undefined && docs.length) {
const clones = await Promise.all(docs.map(async d => Doc.makeClone(d, cloneMap, linkMap, rtfs, exclusions, pruneDocs, cloneLinks, cloneTemplates)));
assignKey(new List<Doc>(clones));
} else {
- assignKey(ObjectField.MakeCopy(field));
- if (field instanceof RichTextField) {
- if (DocsInTextFieldIds.some(id => field.Data.includes(`"${id}":`))) {
+ assignKey(ObjectField.MakeCopy(objField));
+ if (objField instanceof RichTextField) {
+ if (DocsInTextFieldIds.some(id => objField.Data.includes(`"${id}":`))) {
const docidsearch = new RegExp('(' + DocsInTextFieldIds.map(exp => '(' + exp + ')').join('|') + ')":"([a-z-A-Z0-9_]*)"', 'g');
- const rawdocids = field.Data.match(docidsearch);
+ const rawdocids = objField.Data.match(docidsearch);
const docids = rawdocids?.map((str: string) =>
DocsInTextFieldIds.reduce((output, exp) => output.replace(new RegExp(`${exp}":`, 'g'), ''), str)
.replace(/"/g, '')
.trim()
);
const results = docids && (await DocServer.GetRefFields(docids));
- const docs = results && Array.from(Object.keys(results)).map(key => DocCast(results[key]));
- docs?.map(doc => doc && Doc.makeClone(doc, cloneMap, linkMap, rtfs, exclusions, pruneDocs, cloneLinks, cloneTemplates));
- rtfs.push({ copy, key, field });
+ const rdocs = results && Array.from(Object.keys(results)).map(rkey => DocCast(results[rkey]));
+ rdocs?.map(d => d && Doc.makeClone(d, cloneMap, linkMap, rtfs, exclusions, pruneDocs, cloneLinks, cloneTemplates));
+ rtfs.push({ copy, key, field: objField });
}
}
}
@@ -769,7 +769,7 @@ export namespace Doc {
export async function MakeClone(doc: Doc, cloneLinks = true, cloneTemplates = true, cloneMap: Map<string, Doc> = new Map()) {
const linkMap = new Map<string, Doc>();
const rtfMap: { copy: Doc; key: string; field: RichTextField }[] = [];
- const copy = await Doc.makeClone(doc, cloneMap, linkMap, rtfMap, ['cloneOf'], doc.embedContainer ? [DocCast(doc.embedContainer)] : [], cloneLinks, cloneTemplates);
+ const clone = await Doc.makeClone(doc, cloneMap, linkMap, rtfMap, ['cloneOf'], doc.embedContainer ? [DocCast(doc.embedContainer)] : [], cloneLinks, cloneTemplates);
const repaired = new Set<Doc>();
const linkedDocs = Array.from(linkMap.values());
linkedDocs.forEach(link => Doc.AddLink?.(link, true));
@@ -787,8 +787,8 @@ export namespace Doc {
copy[key] = new RichTextField(field.Data.replace(docidsearch, replacer).replace(re, replacer2), field.Text);
});
const clonedDocs = [...Array.from(cloneMap.values()), ...linkedDocs];
- clonedDocs.forEach(clone => Doc.repairClone(clone, cloneMap, cloneTemplates, repaired));
- return { clone: copy, map: cloneMap, linkMap };
+ clonedDocs.forEach(cloneDoc => Doc.repairClone(cloneDoc, cloneMap, cloneTemplates, repaired));
+ return { clone, map: cloneMap, linkMap };
}
const _pendingMap = new Set<string>();
@@ -1236,9 +1236,9 @@ export namespace Doc {
});
}
/// if doc is defined, then it is unhighlighted, otherwise all highlighted docs are unhighlighted
- export function UnHighlightDoc(doc?: Doc) {
+ export function UnHighlightDoc(docs?: Doc) {
runInAction(() => {
- (doc ? [doc] : Array.from(highlightedDocs)).forEach(doc => {
+ (docs ? [docs] : Array.from(highlightedDocs)).forEach(doc => {
highlightedDocs.delete(doc);
highlightedDocs.delete(doc[DocData]);
doc[Highlight] = doc[DocData][Highlight] = false;
diff --git a/src/fields/List.ts b/src/fields/List.ts
index f6e0473ea..f97f208fe 100644
--- a/src/fields/List.ts
+++ b/src/fields/List.ts
@@ -2,14 +2,12 @@ import { action, computed, makeObservable, observable } from 'mobx';
import { alias, list, serializable } from 'serializr';
import { ScriptingGlobals } from '../client/util/ScriptingGlobals';
import { Deserializable, afterDocDeserialize, autoObject } from '../client/util/SerializationHelper';
-import { Field, FieldType } from './Doc';
+import { Field, FieldType, StrListCast } from './Doc';
import { FieldTuples, Self, SelfProxy } from './DocSymbols';
import { Copy, FieldChanged, Parent, ToJavascriptString, ToScriptString, ToString } from './FieldSymbols';
import { ObjGetRefFields, ObjectField } from './ObjectField';
import { ProxyField } from './Proxy';
import { RefField } from './RefField';
-import { listSpec } from './Schema';
-import { Cast } from './Types';
import { containedFieldChangedHandler, deleteProperty, getter, setter } from './util';
function toObjectField(field: FieldType) {
@@ -223,7 +221,7 @@ class ListImpl<T extends FieldType> extends ObjectField {
},
};
static listGetter(target: any, prop: string | symbol, receiver: any): any {
- if (ListImpl.listHandlers.hasOwnProperty(prop)) {
+ if (Object.prototype.hasOwnProperty.call(ListImpl.listHandlers, prop)) {
return ListImpl.listHandlers[prop];
}
return getter(target, prop, receiver);
@@ -252,6 +250,7 @@ class ListImpl<T extends FieldType> extends ObjectField {
throw new Error("Currently properties can't be defined on documents using Object.defineProperty");
},
});
+ // eslint-disable-next-line no-use-before-define
this[SelfProxy] = list as any as List<FieldType>; // bcz: ugh .. don't know how to convince typesecript that list is a List
if (fields) {
this[SelfProxy].push(...fields);
@@ -287,13 +286,13 @@ class ListImpl<T extends FieldType> extends ObjectField {
private set __fieldTuples(value) {
this[FieldTuples] = value;
- for (const key in value) {
- const item = value[key];
+ Object.keys(value).forEach(key => {
+ const item = value[Number(key)];
if (item instanceof ObjectField) {
item[Parent] = this[Self];
item[FieldChanged] = containedFieldChangedHandler(this[SelfProxy], Number(key), item);
}
- }
+ });
}
[Copy]() {
@@ -306,25 +305,24 @@ class ListImpl<T extends FieldType> extends ObjectField {
@observable
private [FieldTuples]: StoredType<T>[] = [];
private [Self] = this;
+ // eslint-disable-next-line no-use-before-define
private [SelfProxy]: List<FieldType>; // also used in utils.ts even though it won't be found using find all references
- [ToJavascriptString]() {
- return `[${(this as any).map((field: any) => Field.toScriptString(field))}]`;
- }
- [ToScriptString]() {
- return `new List([${(this as any).map((field: any) => Field.toScriptString(field))}])`;
- }
- [ToString]() {
- return `[${(this as any).map((field: any) => Field.toString(field))}]`;
- }
+ [ToScriptString]() { return `new List(${this[ToJavascriptString]})`; } // prettier-ignore
+ [ToJavascriptString]() { return `[${(this as any).map((field: any) => Field.toScriptString(field))}]`; } // prettier-ignore
+ [ToString]() { return `[${(this as any).map((field: any) => Field.toString(field))}]`; } // prettier-ignore
}
+
+// declare List as a type so you can use it in type declarations, e.g., { l: List, ...}
export type List<T extends FieldType> = ListImpl<T> & (T | (T extends RefField ? Promise<T> : never))[];
+// decalre List as a value so you can invoke 'new' on it, e.g., new List<Doc>()
+// eslint-disable-next-line no-redeclare
export const List: { new <T extends FieldType>(fields?: T[]): List<T> } = ListImpl as any;
ScriptingGlobals.add('List', List);
// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function compareLists(l1: any, l2: any) {
- const L1 = Cast(l1, listSpec('string'), []);
- const L2 = Cast(l2, listSpec('string'), []);
+ const L1 = StrListCast(l1);
+ const L2 = StrListCast(l2);
return !L1 && !L2 ? true : L1 && L2 && L1.length === L2.length && L2.reduce((p, v) => p && L1.includes(v), true);
}, 'compare two lists');
diff --git a/src/fields/ObjectField.ts b/src/fields/ObjectField.ts
index 6c70adc1d..231086262 100644
--- a/src/fields/ObjectField.ts
+++ b/src/fields/ObjectField.ts
@@ -5,6 +5,7 @@ import { RefField } from './RefField';
export abstract class ObjectField {
// prettier-ignore
public [FieldChanged]?: (diff?: { op: '$addToSet' | '$remFromSet' | '$set';
+ // eslint-disable-next-line no-use-before-define
items: FieldType[] | undefined;
length: number | undefined;
hint?: any }, serverOp?: any) => void;
diff --git a/src/fields/Proxy.ts b/src/fields/Proxy.ts
index 820d9b6ff..4f8058ce4 100644
--- a/src/fields/Proxy.ts
+++ b/src/fields/Proxy.ts
@@ -21,7 +21,7 @@ export class ProxyField<T extends RefField> extends ObjectField {
constructor(value?: T | string) {
super();
if (typeof value === 'string') {
- //this.cache = DocServer.GetCachedRefField(value) as any;
+ // this.cache = DocServer.GetCachedRefField(value) as any;
this.fieldId = value;
} else if (value) {
this.cache = { field: value, p: undefined };
@@ -29,7 +29,7 @@ export class ProxyField<T extends RefField> extends ObjectField {
}
}
- [ToValue](doc: any) {
+ [ToValue](/* doc: any */) {
return ProxyField.toValue(this);
}
@@ -39,10 +39,10 @@ export class ProxyField<T extends RefField> extends ObjectField {
}
[ToJavascriptString]() {
- return Field.toScriptString(this[ToValue](undefined)?.value);
+ return Field.toScriptString(this[ToValue]()?.value);
}
[ToScriptString]() {
- return Field.toScriptString(this[ToValue](undefined)?.value); // not sure this is quite right since it doesn't recreate a proxy field, but better than 'invalid' ?
+ return Field.toScriptString(this[ToValue]()?.value); // not sure this is quite right since it doesn't recreate a proxy field, but better than 'invalid' ?
}
[ToString]() {
return 'ProxyField';
@@ -59,7 +59,9 @@ export class ProxyField<T extends RefField> extends ObjectField {
return this._cache;
}
private set cache(val: { field: T | undefined; p: FieldWaiting<T> | undefined }) {
- runInAction(() => (this._cache = { ...val }));
+ runInAction(() => {
+ this._cache = { ...val };
+ });
}
private failed = false;
@@ -78,7 +80,7 @@ export class ProxyField<T extends RefField> extends ObjectField {
return this.cache.field ?? this.cache.p;
}
@computed get needsRequesting(): boolean {
- return !this.cache.field && !this.failed && !this._cache.p && !DocServer.GetCachedRefField(this.fieldId) ? true : false;
+ return !!(!this.cache.field && !this.failed && !this._cache.p && !DocServer.GetCachedRefField(this.fieldId));
}
setExternalValuePromise(externalValuePromise: Promise<any>) {
@@ -92,6 +94,7 @@ export class ProxyField<T extends RefField> extends ObjectField {
}
}
+// eslint-disable-next-line no-redeclare
export namespace ProxyField {
let useProxy = true;
export function DisableProxyFields() {
@@ -115,9 +118,11 @@ export namespace ProxyField {
if (useProxy) {
return { value: value.value };
}
+ return undefined;
}
}
+// eslint-disable-next-line no-use-before-define
function prefetchValue(proxy: PrefetchProxy<RefField>) {
return proxy.value as any;
}
diff --git a/src/fields/Schema.ts b/src/fields/Schema.ts
index 364899dc7..ed603e5de 100644
--- a/src/fields/Schema.ts
+++ b/src/fields/Schema.ts
@@ -36,7 +36,7 @@ export function makeInterface<T extends Interface[]>(...schemas: T): InterfaceFu
if (prop in schema) {
const desc = prop === 'proto' ? Doc : (schema as any)[prop]; // bcz: proto doesn't appear in schemas ... maybe it should?
if (typeof desc === 'object' && 'defaultVal' in desc && 'type' in desc) {
- //defaultSpec
+ // defaultSpec
return Cast(field, desc.type, desc.defaultVal);
}
if (typeof desc === 'function' && !ObjectField.isPrototypeOf(desc) && !RefField.isPrototypeOf(desc)) {
diff --git a/src/fields/Types.ts b/src/fields/Types.ts
index 6ed94d341..57a310f6d 100644
--- a/src/fields/Types.ts
+++ b/src/fields/Types.ts
@@ -7,6 +7,7 @@ import { RichTextField } from './RichTextField';
import { ScriptField } from './ScriptField';
import { CsvField, ImageField, WebField } from './URLField';
+// eslint-disable-next-line no-use-before-define
export type ToConstructor<T extends FieldType> = T extends string ? 'string' : T extends number ? 'number' : T extends boolean ? 'boolean' : T extends List<infer U> ? ListSpec<U> : new (...args: any[]) => T;
export type DefaultFieldConstructor<T extends FieldType> = {
@@ -27,6 +28,7 @@ export type ToType<T extends InterfaceValue> = T extends 'string'
: T extends ListSpec<infer U>
? List<U>
: // T extends { new(...args: any[]): infer R } ? (R | Promise<R>) : never;
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
T extends DefaultFieldConstructor<infer _U>
? never
: T extends { new (...args: any[]): List<FieldType> }
@@ -37,6 +39,10 @@ export type ToType<T extends InterfaceValue> = T extends 'string'
? R
: never;
+export interface Interface {
+ [key: string]: InterfaceValue;
+ // [key: string]: ToConstructor<Field> | ListSpec<Field[]>;
+}
export type ToInterface<T extends Interface> = {
[P in Exclude<keyof T, 'proto'>]: T[P] extends DefaultFieldConstructor<infer F> ? Exclude<FieldResult<F>, undefined> : FieldResult<ToType<T[P]>>;
};
@@ -47,10 +53,6 @@ export type Head<T extends any[]> = T extends [any, ...any[]] ? T[0] : never;
export type Tail<T extends any[]> = ((...t: T) => any) extends (_: any, ...tail: infer TT) => any ? TT : [];
export type HasTail<T extends any[]> = T extends [] | [any] ? false : true;
// TODO Allow you to optionally specify default values for schemas, which should then make that field not be partial
-export interface Interface {
- [key: string]: InterfaceValue;
- // [key: string]: ToConstructor<Field> | ListSpec<Field[]>;
-}
export type WithoutRefField<T extends FieldType> = T extends RefField ? never : T;
export type CastCtor = ToConstructor<FieldType> | ListSpec<FieldType>;
@@ -58,13 +60,16 @@ export type CastCtor = ToConstructor<FieldType> | ListSpec<FieldType>;
type WithoutList<T extends FieldType> = T extends List<infer R> ? (R extends RefField ? (R | Promise<R>)[] : R[]) : T;
export function Cast<T extends CastCtor>(field: FieldResult, ctor: T): FieldResult<ToType<T>>;
+// eslint-disable-next-line no-redeclare
export function Cast<T extends CastCtor>(field: FieldResult, ctor: T, defaultVal: WithoutList<WithoutRefField<ToType<T>>> | null): WithoutList<ToType<T>>;
+// eslint-disable-next-line no-redeclare
export function Cast<T extends CastCtor>(field: FieldResult, ctor: T, defaultVal?: ToType<T> | null): FieldResult<ToType<T>> | undefined {
if (field instanceof Promise) {
return defaultVal === undefined ? (field.then(f => Cast(f, ctor) as any) as any) : defaultVal === null ? undefined : defaultVal;
}
if (field !== undefined && !(field instanceof Promise)) {
if (typeof ctor === 'string') {
+ // eslint-disable-next-line valid-typeof
if (typeof field === ctor) {
return field as ToType<T>;
}
@@ -118,7 +123,9 @@ export function ImageCast(field: FieldResult, defaultVal: ImageField | null = nu
}
export function FieldValue<T extends FieldType, U extends WithoutList<T>>(field: FieldResult<T>, defaultValue: U): WithoutList<T>;
+// eslint-disable-next-line no-redeclare
export function FieldValue<T extends FieldType>(field: FieldResult<T>): Opt<T>;
+// eslint-disable-next-line no-redeclare
export function FieldValue<T extends FieldType>(field: FieldResult<T>, defaultValue?: T): Opt<T> {
return field instanceof Promise || field === undefined ? defaultValue : field;
}
diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts
index 1cacfe30c..c73689e1f 100644
--- a/src/fields/documentSchemas.ts
+++ b/src/fields/documentSchemas.ts
@@ -104,7 +104,7 @@ export const documentSchema = createSchema({
export const collectionSchema = createSchema({
childLayoutTemplate: Doc, // layout template to use to render children of a collecion
- childLayoutString: 'string', //layout string to use to render children of a collection
+ childLayoutString: 'string', // layout string to use to render children of a collection
childClickedOpenTemplateView: Doc, // layout template to apply to a child when its clicked on in a collection and opened (requires onChildClick or other script to read this value and apply template)
childDontRegisterViews: 'boolean', // whether views made of this document are registered so that they can be found when drawing links
onChildClick: ScriptField, // script to run for each child when its clicked
@@ -113,4 +113,5 @@ export const collectionSchema = createSchema({
});
export type Document = makeInterface<[typeof documentSchema]>;
+// eslint-disable-next-line no-redeclare
export const Document = makeInterface(documentSchema);