diff options
Diffstat (limited to 'src/new_fields/Doc.ts')
-rw-r--r-- | src/new_fields/Doc.ts | 108 |
1 files changed, 77 insertions, 31 deletions
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 2ad6ae5f0..d3d7d584e 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -1,6 +1,6 @@ import { observable, action } from "mobx"; -import { serializable, primitive, map, alias, list } from "serializr"; -import { autoObject, SerializationHelper, Deserializable } from "../client/util/SerializationHelper"; +import { serializable, primitive, map, alias, list, PropSchema, custom } from "serializr"; +import { autoObject, SerializationHelper, Deserializable, afterDocDeserialize } from "../client/util/SerializationHelper"; import { DocServer } from "../client/DocServer"; import { setter, getter, getField, updateFunction, deleteProperty, makeEditable, makeReadOnly } from "./util"; import { Cast, ToConstructor, PromiseValue, FieldValue, NumCast, BoolCast, StrCast } from "./Types"; @@ -68,8 +68,15 @@ export function DocListCast(field: FieldResult): Doc[] { export const WidthSym = Symbol("Width"); export const HeightSym = Symbol("Height"); +function fetchProto(doc: Doc) { + const proto = doc.proto; + if (proto instanceof Promise) { + return proto; + } +} + @scriptingGlobal -@Deserializable("doc").withFields(["id"]) +@Deserializable("doc", fetchProto).withFields(["id"]) export class Doc extends RefField { constructor(id?: FieldId, forceSave?: boolean) { super(id); @@ -102,7 +109,7 @@ export class Doc extends RefField { proto: Opt<Doc>; [key: string]: FieldResult; - @serializable(alias("fields", map(autoObject()))) + @serializable(alias("fields", map(autoObject(), { afterDeserialize: afterDocDeserialize }))) private get __fields() { return this.___fields; } @@ -134,14 +141,14 @@ export class Doc extends RefField { return "invalid"; } - public [HandleUpdate](diff: any) { + public async [HandleUpdate](diff: any) { const set = diff.$set; if (set) { for (const key in set) { if (!key.startsWith("fields.")) { continue; } - const value = SerializationHelper.Deserialize(set[key]); + const value = await SerializationHelper.Deserialize(set[key]); const fKey = key.substring(7); this[fKey] = value; } @@ -244,7 +251,7 @@ export namespace Doc { let r = (doc === other); let r2 = (doc.proto === other); let r3 = (other.proto === doc); - let r4 = (doc.proto === other.proto); + let r4 = (doc.proto === other.proto && other.proto !== undefined); return r || r2 || r3 || r4; } @@ -298,7 +305,7 @@ export namespace Doc { x: Math.min(sptX, bounds.x), y: Math.min(sptY, bounds.y), r: Math.max(bptX, bounds.r), b: Math.max(bptY, bounds.b) }; - }, { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: Number.MIN_VALUE, b: Number.MIN_VALUE }); + }, { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: -Number.MAX_VALUE, b: -Number.MAX_VALUE }); return bounds; } @@ -314,20 +321,22 @@ export namespace Doc { } export function UpdateDocumentExtensionForField(doc: Doc, fieldKey: string) { - let extensionDoc = doc[fieldKey + "_ext"]; - if (extensionDoc === undefined) { + let docExtensionForField = doc[fieldKey + "_ext"] as Doc; + if (docExtensionForField === undefined) { setTimeout(() => { - let docExtensionForField = new Doc(doc[Id] + fieldKey, true); - docExtensionForField.title = doc.title + ":" + fieldKey + ".ext"; - docExtensionForField.extendsDoc = doc; + docExtensionForField = new Doc(doc[Id] + fieldKey, true); + docExtensionForField.title = fieldKey + ".ext"; + docExtensionForField.extendsDoc = doc; // this is used by search to map field matches on the extension doc back to the document it extends. + docExtensionForField.type = DocumentType.EXTENSION; let proto: Doc | undefined = doc; while (proto && !Doc.IsPrototype(proto)) { proto = proto.proto; } (proto ? proto : doc)[fieldKey + "_ext"] = docExtensionForField; }, 0); - } else if (extensionDoc instanceof Doc && extensionDoc.extendsDoc === undefined) { - setTimeout(() => (extensionDoc as Doc).extendsDoc = doc, 0); + } else if (doc instanceof Doc) { // backward compatibility -- add fields for docs that don't have them already + docExtensionForField.extendsDoc === undefined && setTimeout(() => docExtensionForField.extendsDoc = doc, 0); + docExtensionForField.type === undefined && setTimeout(() => docExtensionForField.type = DocumentType.EXTENSION, 0); } } export function MakeAlias(doc: Doc) { @@ -337,11 +346,25 @@ export namespace Doc { return Doc.MakeDelegate(doc); // bcz? } + // + // Determines whether the combination of the layoutDoc and dataDoc represents + // a template relationship. If so, the layoutDoc will be expanded into a new + // document that inherits the properties of the original layout while allowing + // for individual layout properties to be overridden in the expanded layout. + // + export function WillExpandTemplateLayout(layoutDoc: Doc, dataDoc?: Doc) { + return BoolCast(layoutDoc.isTemplate) && dataDoc && layoutDoc !== dataDoc && !(layoutDoc.layout instanceof Doc); + } + + // + // Returns an expanded template layout for a target data document. + // First it checks if an expanded layout already exists -- if so it will be stored on the dataDoc + // using the template layout doc's id as the field key. + // If it doesn't find the expanded layout, then it makes a delegate of the template layout and + // saves it on the data doc indexed by the template layout's id + // export function expandTemplateLayout(templateLayoutDoc: Doc, dataDoc?: Doc) { - let resolvedDataDoc = (templateLayoutDoc !== dataDoc) ? dataDoc : undefined; - if (!dataDoc || !(templateLayoutDoc && !(Cast(templateLayoutDoc.layout, Doc) instanceof Doc) && resolvedDataDoc && resolvedDataDoc !== templateLayoutDoc)) { - return templateLayoutDoc; - } + if (!WillExpandTemplateLayout(templateLayoutDoc, dataDoc) || !dataDoc) return templateLayoutDoc; // if we have a data doc that doesn't match the layout, then we're rendering a template. // ... which means we change the layout to be an expanded view of the template layout. // This allows the view override the template's properties and be referenceable as its own document. @@ -350,19 +373,18 @@ export namespace Doc { if (expandedTemplateLayout instanceof Doc) { return expandedTemplateLayout; } - if (expandedTemplateLayout === undefined && BoolCast(templateLayoutDoc.isTemplate)) { - setTimeout(() => { - let expandedDoc = Doc.MakeDelegate(templateLayoutDoc); - expandedDoc.title = templateLayoutDoc.title + "[" + StrCast(dataDoc.title).match(/\.\.\.[0-9]*/) + "]"; - expandedDoc.isExpandedTemplate = templateLayoutDoc; - dataDoc[templateLayoutDoc[Id]] = expandedDoc; - }, 0); + let expandedLayoutFieldKey = "Layout[" + templateLayoutDoc[Id] + "]"; + expandedTemplateLayout = dataDoc[expandedLayoutFieldKey]; + if (expandedTemplateLayout instanceof Doc) { + return expandedTemplateLayout; + } + if (expandedTemplateLayout === undefined) { + setTimeout(() => + dataDoc[expandedLayoutFieldKey] = Doc.MakeDelegate(templateLayoutDoc, undefined, templateLayoutDoc.title + ".layout"), 0); } return templateLayoutDoc; // use the templateLayout when it's not a template or the expandedTemplate is pending. } - let _pendingExpansions: Map<string, boolean> = new Map(); - export function MakeCopy(doc: Doc, copyProto: boolean = false): Doc { const copy = new Doc; Object.keys(doc).forEach(key => { @@ -387,17 +409,32 @@ export namespace Doc { return copy; } - export function MakeDelegate(doc: Doc, id?: string): Doc; - export function MakeDelegate(doc: Opt<Doc>, id?: string): Opt<Doc>; - export function MakeDelegate(doc: Opt<Doc>, id?: string): Opt<Doc> { + export function MakeDelegate(doc: Doc, id?: string, title?: string): Doc; + export function MakeDelegate(doc: Opt<Doc>, id?: string, title?: string): Opt<Doc>; + export function MakeDelegate(doc: Opt<Doc>, id?: string, title?: string): Opt<Doc> { if (doc) { const delegate = new Doc(id, true); delegate.proto = doc; + title && (delegate.title = title); return delegate; } return undefined; } + let _applyCount: number = 0; + export function ApplyTemplate(templateDoc: Doc) { + if (!templateDoc) return undefined; + let otherdoc = new Doc(); + otherdoc.width = templateDoc[WidthSym](); + otherdoc.height = templateDoc[HeightSym](); + otherdoc.title = templateDoc.title + "(..." + _applyCount++ + ")"; + otherdoc.layout = Doc.MakeDelegate(templateDoc); + otherdoc.miniLayout = StrCast(templateDoc.miniLayout); + otherdoc.detailedLayout = otherdoc.layout; + otherdoc.type = DocumentType.TEMPLATE; + return otherdoc; + } + export function MakeTemplate(fieldTemplate: Doc, metaKey: string, proto: Doc) { // move data doc fields to layout doc as needed (nativeWidth/nativeHeight, data, ??) let backgroundLayout = StrCast(fieldTemplate.backgroundLayout); @@ -417,6 +454,7 @@ export namespace Doc { layoutDelegate.layout = layout; fieldTemplate.title = metaKey; + fieldTemplate.templateField = metaKey; fieldTemplate.layout = layoutDelegate !== fieldTemplate ? layoutDelegate : layout; fieldTemplate.backgroundLayout = backgroundLayout; fieldTemplate.nativeWidth = nw; @@ -432,4 +470,12 @@ export namespace Doc { d.layout !== miniLayout ? miniLayout && (d.layout = d.miniLayout) : detailLayout && (d.layout = detailLayout); if (d.layout === detailLayout) Doc.GetProto(d).nativeWidth = Doc.GetProto(d).nativeHeight = undefined; } + export async function UseDetailLayout(d: Doc) { + let miniLayout = await PromiseValue(d.miniLayout); + let detailLayout = await PromiseValue(d.detailedLayout); + if (miniLayout && d.layout === miniLayout && detailLayout) { + d.layout = detailLayout; + d.nativeWidth = d.nativeHeight = undefined; + } + } }
\ No newline at end of file |