aboutsummaryrefslogtreecommitdiff
path: root/src/fields/Doc.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/fields/Doc.ts')
-rw-r--r--src/fields/Doc.ts74
1 files changed, 49 insertions, 25 deletions
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index ba94f0504..25e70d950 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -25,6 +25,7 @@ import { ComputedField, ScriptField } from './ScriptField';
import { BoolCast, Cast, DocCast, FieldValue, ImageCastWithSuffix, NumCast, RTFCast, StrCast, ToConstructor, toList } from './Types';
import { containedFieldChangedHandler, deleteProperty, GetEffectiveAcl, getField, getter, makeEditable, makeReadOnly, setter, SharingPermissions } from './util';
import { gptImageLabel } from '../client/apis/gpt/GPT';
+import { DateField } from './DateField';
export let ObjGetRefField: (id: string, force?: boolean) => Promise<Doc | undefined>;
export let ObjGetRefFields: (ids: string[]) => Promise<Map<string, Doc | undefined>>;
@@ -535,23 +536,35 @@ export namespace Doc {
export function GetT<T extends FieldType>(doc: Doc, key: string, ctor: ToConstructor<T>, ignoreProto: boolean = false): FieldResult<T> {
return Cast(Get(doc, key, ignoreProto), ctor) as FieldResult<T>;
}
- export function isTemplateDoc(doc: Doc) {
- return GetT(doc, 'isTemplateDoc', 'boolean', true);
- }
- export function isTemplateForField(doc: Doc) {
- return GetT(doc, 'isTemplateForField', 'string', true);
- }
- export function IsDataProto(doc: Doc) {
- return GetT(doc, 'isDataDoc', 'boolean', true);
- }
- export function IsBaseProto(doc: Doc) {
- return GetT(doc, 'isBaseProto', 'boolean', true);
- }
- export function IsSystem(doc: Doc) {
- return GetT(doc, 'isSystem', 'boolean', true);
- }
- export function IsDelegateField(doc: Doc, fieldKey: string) {
- return doc && Get(doc, fieldKey, true) !== undefined;
+ /**
+ * Tests whether the Doc is flagged as being a template.
+ * Templates can be set as a layout for another target Doc. When rendered by the target Doc, the template
+ * creates an instance of itself that holds rendering data specific to the target
+ * @param doc
+ */
+ export function IsTemplateDoc(doc: Doc) { return GetT(doc, 'isTemplateDoc', 'boolean', true); } // prettier-ignore
+ /**
+ * Tests whether the Doc is flagged as being a template for rendering a specific field of a Doc
+ * When flagged as field template and rendered, the template will redirect its componentView to write to the
+ * specified template field. In general, a compound Doc template will contain multiple field templates, one for each of the
+ * data fields rendered by the compound template.
+ * @param doc
+ * @returns
+ */
+ export function IsTemplateForField(doc: Doc) { return GetT(doc, 'isTemplateForField', 'string', true); } // prettier-ignore
+ export function IsDataProto(doc: Doc) { return GetT(doc, 'isDataDoc', 'boolean', true); } // prettier-ignore
+ export function IsBaseProto(doc: Doc) { return GetT(doc, 'isBaseProto', 'boolean', true); } // prettier-ignore
+ export function IsSystem(doc: Doc) { return GetT(doc, 'isSystem', 'boolean', true); } // prettier-ignore
+ export function IsDelegateField(doc: Doc, fieldKey: string) { return doc && Get(doc, fieldKey, true) !== undefined; } // prettier-ignore
+ /**
+ * Tests whether a doc is a freeform collection that renders as a group.
+ * The group variant of a collection automatically resizes so that none of its contents
+ * are ever hidden.
+ * @param doc doc to test for being a freeform group
+ * @returns boolean
+ */
+ export function IsFreeformGroup(doc: Doc) {
+ return doc.freeform_isGroup && doc.type_collection === CollectionViewType.Freeform;
}
//
// this will write the value to the key on either the data doc or the embedding doc. The choice
@@ -733,7 +746,7 @@ export namespace Doc {
const FindDocsInRTF = new RegExp(/(audioId|textId|anchorId|docId)"\s*:\s*"(.*?)"/g);
export function makeClone(doc: Doc, cloneMap: Map<string, Doc>, linkMap: Map<string, Doc>, rtfs: { copy: Doc; key: string; field: RichTextField }[], exclusions: string[], pruneDocs: Doc[], cloneLinks: boolean, cloneTemplates: boolean): Doc {
- if (Doc.IsBaseProto(doc) || ((Doc.isTemplateDoc(doc) || Doc.isTemplateForField(doc)) && !cloneTemplates)) {
+ if (Doc.IsBaseProto(doc) || ((Doc.IsTemplateDoc(doc) || Doc.IsTemplateForField(doc)) && !cloneTemplates)) {
return doc;
}
if (cloneMap.get(doc[Id])) return cloneMap.get(doc[Id])!;
@@ -807,7 +820,7 @@ export namespace Doc {
const docAtKey = DocCast(clone[key]);
if (docAtKey && !Doc.IsSystem(docAtKey)) {
if (!Array.from(cloneMap.values()).includes(docAtKey)) {
- clone[key] = !cloneTemplates && (Doc.isTemplateDoc(docAtKey) || Doc.isTemplateForField(docAtKey)) ? docAtKey : cloneMap.get(docAtKey[Id]);
+ clone[key] = !cloneTemplates && (Doc.IsTemplateDoc(docAtKey) || Doc.IsTemplateForField(docAtKey)) ? docAtKey : cloneMap.get(docAtKey[Id]);
} else {
repairClone(docAtKey, cloneMap, cloneTemplates, visited);
}
@@ -858,7 +871,7 @@ export namespace Doc {
*/
export function expandTemplateLayout(templateLayoutDoc: Doc, targetDoc?: Doc, layoutFieldKey?: string) {
// nothing to do if the layout isn't a template or we don't have a target that's different than the template
- if (!targetDoc || templateLayoutDoc === targetDoc || (!Doc.isTemplateForField(templateLayoutDoc) && !Doc.isTemplateDoc(templateLayoutDoc))) {
+ if (!targetDoc || templateLayoutDoc === targetDoc || (!Doc.IsTemplateForField(templateLayoutDoc) && !Doc.IsTemplateDoc(templateLayoutDoc))) {
return templateLayoutDoc;
}
@@ -921,7 +934,7 @@ export namespace Doc {
console.log('Warning: GetLayoutDataDocPair childDoc not defined');
return { layout: childDoc, data: childDoc };
}
- const data = Doc.AreProtosEqual(containerDataDoc, containerDoc) || (!Doc.isTemplateDoc(childDoc) && !Doc.isTemplateForField(childDoc)) ? undefined : containerDataDoc;
+ const data = Doc.AreProtosEqual(containerDataDoc, containerDoc) || (!Doc.IsTemplateDoc(childDoc) && !Doc.IsTemplateForField(childDoc)) ? undefined : containerDataDoc;
const templateRoot = DocCast(containerDoc?.rootDocument);
return { layout: Doc.expandTemplateLayout(childDoc, templateRoot, layoutFieldKey), data };
}
@@ -1109,6 +1122,10 @@ export namespace Doc {
Cast(templateFieldValue, listSpec(Doc), [])?.map(d => d instanceof Doc && MakeMetadataFieldTemplate(d, templateDoc));
Doc.GetProto(templateField)[metadataFieldKey] = ObjectField.MakeCopy(templateFieldValue);
}
+ if (templateField.type === DocumentType.IMG) {
+ // bcz: should be a better way .. but, if the image is a template, then we can't expect to know the aspect ratio. When the image is replaced by data and rendered, we want to recomputed the native dimensions.
+ templateField[DocData].layout_resetNativeDim = true;
+ }
// get the layout string that the template uses to specify its layout
const templateFieldLayoutString = StrCast(Doc.LayoutField(templateField[DocLayout]));
@@ -1184,12 +1201,14 @@ export namespace Doc {
return Doc.NativeWidth(doc, dataDoc, useDim) / (Doc.NativeHeight(doc, dataDoc, useDim) || 1);
}
export function NativeWidth(doc?: Doc, dataDoc?: Doc, useWidth?: boolean) {
- return !doc ? 0 : NumCast(doc._nativeWidth, NumCast((dataDoc || doc)[Doc.LayoutDataKey(doc) + '_nativeWidth'], useWidth ? NumCast(doc._width) : 0));
+ // if this is a field template, then don't use the doc's nativeWidth/height
+ return !doc ? 0 : NumCast(doc._nativeWidth, doc[DocLayout].isTemplateDoc ? 0 : NumCast((dataDoc || doc)[Doc.LayoutDataKey(doc) + '_nativeWidth'], useWidth ? NumCast(doc._width) : 0));
}
export function NativeHeight(doc?: Doc, dataDoc?: Doc, useHeight?: boolean) {
if (!doc) return 0;
const nheight = (Doc.NativeWidth(doc, dataDoc, useHeight) / NumCast(doc._width)) * NumCast(doc._height); // divide before multiply to avoid floating point errrorin case nativewidth = width
- const dheight = NumCast((dataDoc || doc)[Doc.LayoutDataKey(doc) + '_nativeHeight'], useHeight ? NumCast(doc._height) : 0);
+ const dheight = doc[DocLayout].isTemplateDoc ? 0 : NumCast((dataDoc || doc)[Doc.LayoutDataKey(doc) + '_nativeHeight'], useHeight ? NumCast(doc._height) : 0);
+ // if this is a field template, then don't use the doc's nativeWidth/height
return NumCast(doc._nativeHeight, nheight || dheight);
}
@@ -1460,7 +1479,6 @@ export namespace Doc {
layoutDoc._nativeWidth = undefined;
layoutDoc._nativeHeight = undefined;
} else {
- layoutDoc._layout_autoHeight = false;
if (!Doc.NativeWidth(layoutDoc)) {
layoutDoc._nativeWidth = NumCast(layoutDoc._width, panelWidth);
layoutDoc._nativeHeight = NumCast(layoutDoc._height, panelHeight);
@@ -1504,7 +1522,13 @@ export namespace Doc {
case DocumentType.RTF: return RTFCast(tdoc[Doc.LayoutDataKey(tdoc)])?.Text ?? StrCast(tdoc[Doc.LayoutDataKey(tdoc)]);
default: return StrCast(tdoc.title).startsWith("Untitled") ? "" : StrCast(tdoc.title);
}}); // prettier-ignore
- return docText(doc).then(text => (doc['$' + Doc.LayoutDataKey(doc) + '_description'] = text));
+ return docText(doc).then(
+ action(text => {
+ // set the time when the date changes. This also allows a live textbox view to react to the update, otherwise, it wouldn't take effect until the next time the view is rerendered.
+ doc['$' + Doc.LayoutDataKey(doc) + '_description_modificationDate'] = new DateField();
+ return (doc['$' + Doc.LayoutDataKey(doc) + '_description'] = text);
+ })
+ );
}
// prettier-ignore