diff options
author | Jenny Yu <jennyyu212@outlook.com> | 2022-05-04 00:15:53 -0400 |
---|---|---|
committer | Jenny Yu <jennyyu212@outlook.com> | 2022-05-04 00:15:53 -0400 |
commit | 92142bba66010a8792c00dc372228b92e151c8b3 (patch) | |
tree | 552e3163b36b7d5d05b6ca35dcf8f16d0edf11db /src/client/views/DocComponent.tsx | |
parent | 4381f14cfc058c534d4117a61ece34c6db72da9d (diff) |
fix: removed clear previous button
Diffstat (limited to 'src/client/views/DocComponent.tsx')
-rw-r--r-- | src/client/views/DocComponent.tsx | 396 |
1 files changed, 198 insertions, 198 deletions
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 79aaf2158..103560a2a 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -17,223 +17,223 @@ import { Touchable } from './Touchable'; /// DocComponent returns a generic React base class used by views that don't have 'fieldKey' props (e.g.,CollectionFreeFormDocumentView, DocumentView) export interface DocComponentProps { - Document: Doc; - LayoutTemplate?: () => Opt<Doc>; - LayoutTemplateString?: string; + Document: Doc; + LayoutTemplate?: () => Opt<Doc>; + LayoutTemplateString?: string; } export function DocComponent<P extends DocComponentProps>() { - class Component extends Touchable<P> { - //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then - @computed get Document() { return this.props.Document; } - // This is the "The Document" -- it encapsulates, data, layout, and any templates - @computed get rootDoc() { return Cast(this.props.Document.rootDocument, Doc, null) || this.props.Document; } - // This is the rendering data of a document -- it may be "The Document", or it may be some template document that holds the rendering info - @computed get layoutDoc() { return this.props.LayoutTemplateString ? this.props.Document : Doc.Layout(this.props.Document, this.props.LayoutTemplate?.()); } - // This is the data part of a document -- ie, the data that is constant across all views of the document - @computed get dataDoc() { return this.props.Document[DataSym] as Doc; } - - protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; - } - return Component; + class Component extends Touchable<P> { + //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then + @computed get Document() { return this.props.Document; } + // This is the "The Document" -- it encapsulates, data, layout, and any templates + @computed get rootDoc() { return Cast(this.props.Document.rootDocument, Doc, null) || this.props.Document; } + // This is the rendering data of a document -- it may be "The Document", or it may be some template document that holds the rendering info + @computed get layoutDoc() { return this.props.LayoutTemplateString ? this.props.Document : Doc.Layout(this.props.Document, this.props.LayoutTemplate?.()); } + // This is the data part of a document -- ie, the data that is constant across all views of the document + @computed get dataDoc() { return this.props.Document[DataSym] as Doc; } + + protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; + } + return Component; } /// FieldViewBoxProps - a generic base class for field views that are not annotatable (e.g. InkingStroke, ColorBox) interface ViewBoxBaseProps { - Document: Doc; - DataDoc?: Doc; - ContainingCollectionDoc: Opt<Doc>; - DocumentView?: () => DocumentView; - fieldKey: string; - layerProvider?: (doc: Doc, assign?: boolean) => boolean; - isSelected: (outsideReaction?: boolean) => boolean; - isContentActive: () => boolean | undefined; - renderDepth: number; - rootSelected: (outsideReaction?: boolean) => boolean; + Document: Doc; + DataDoc?: Doc; + ContainingCollectionDoc: Opt<Doc>; + DocumentView?: () => DocumentView; + fieldKey: string; + layerProvider?: (doc: Doc, assign?: boolean) => boolean; + isSelected: (outsideReaction?: boolean) => boolean; + isContentActive: () => boolean | undefined; + renderDepth: number; + rootSelected: (outsideReaction?: boolean) => boolean; } export function ViewBoxBaseComponent<P extends ViewBoxBaseProps>() { - class Component extends Touchable<P> { - //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then - //@computed get Document(): T { return schemaCtor(this.props.Document); } - - // This is the "The Document" -- it encapsulates, data, layout, and any templates - @computed get rootDoc() { return Cast(this.props.Document.rootDocument, Doc, null) || this.props.Document; } - // This is the rendering data of a document -- it may be "The Document", or it may be some template document that holds the rendering info - @computed get layoutDoc() { return Doc.Layout(this.props.Document); } - // This is the data part of a document -- ie, the data that is constant across all views of the document - @computed get dataDoc() { return this.props.DataDoc && (this.props.Document.isTemplateForField || this.props.Document.isTemplateDoc) ? this.props.DataDoc : this.props.Document[DataSym]; } - - // key where data is stored - @computed get fieldKey() { return this.props.fieldKey; } - - lookupField = (field: string) => ScriptCast(this.layoutDoc.lookupField)?.script.run({ self: this.layoutDoc, data: this.rootDoc, field: field, container: this.props.DocumentView?.().props.treeViewDoc ?? this.props.ContainingCollectionDoc }).result; - - isContentActive = (outsideReaction?: boolean) => ( - this.props.isContentActive?.() === false ? false : - (CurrentUserUtils.SelectedTool !== InkTool.None || - (this.props.isContentActive?.() || this.props.Document.forceActive || - this.props.isSelected(outsideReaction) || - this.props.rootSelected(outsideReaction)) ? true : undefined)) - protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; - } - return Component; + class Component extends Touchable<P> { + //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then + //@computed get Document(): T { return schemaCtor(this.props.Document); } + + // This is the "The Document" -- it encapsulates, data, layout, and any templates + @computed get rootDoc() { return Cast(this.props.Document.rootDocument, Doc, null) || this.props.Document; } + // This is the rendering data of a document -- it may be "The Document", or it may be some template document that holds the rendering info + @computed get layoutDoc() { return Doc.Layout(this.props.Document); } + // This is the data part of a document -- ie, the data that is constant across all views of the document + @computed get dataDoc() { return this.props.DataDoc && (this.props.Document.isTemplateForField || this.props.Document.isTemplateDoc) ? this.props.DataDoc : this.props.Document[DataSym]; } + + // key where data is stored + @computed get fieldKey() { return this.props.fieldKey; } + + lookupField = (field: string) => ScriptCast(this.layoutDoc.lookupField)?.script.run({ self: this.layoutDoc, data: this.rootDoc, field: field, container: this.props.DocumentView?.().props.treeViewDoc ?? this.props.ContainingCollectionDoc }).result; + + isContentActive = (outsideReaction?: boolean) => ( + this.props.isContentActive?.() === false ? false : + (CurrentUserUtils.SelectedTool !== InkTool.None || + (this.props.isContentActive?.() || this.props.Document.forceActive || + this.props.isSelected(outsideReaction) || + this.props.rootSelected(outsideReaction)) ? true : undefined)) + protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; + } + return Component; } /// DocAnnotatbleComponent -return a base class for React views of document fields that are annotatable *and* interactive when selected (e.g., pdf, image) export interface ViewBoxAnnotatableProps { - Document: Doc; - DataDoc?: Doc; - fieldKey: string; - filterAddDocument?: (doc: Doc[]) => boolean; // allows a document that renders a Collection view to filter or modify any documents added to the collection (see PresBox for an example) - layerProvider?: (doc: Doc, assign?: boolean) => boolean; - isContentActive: () => boolean | undefined; - select: (isCtrlPressed: boolean) => void; - whenChildContentsActiveChanged: (isActive: boolean) => void; - isSelected: (outsideReaction?: boolean) => boolean; - rootSelected: (outsideReaction?: boolean) => boolean; - renderDepth: number; - isAnnotationOverlay?: boolean; + Document: Doc; + DataDoc?: Doc; + fieldKey: string; + filterAddDocument?: (doc: Doc[]) => boolean; // allows a document that renders a Collection view to filter or modify any documents added to the collection (see PresBox for an example) + layerProvider?: (doc: Doc, assign?: boolean) => boolean; + isContentActive: () => boolean | undefined; + select: (isCtrlPressed: boolean) => void; + whenChildContentsActiveChanged: (isActive: boolean) => void; + isSelected: (outsideReaction?: boolean) => boolean; + rootSelected: (outsideReaction?: boolean) => boolean; + renderDepth: number; + isAnnotationOverlay?: boolean; } export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>() { - class Component extends Touchable<P> { - @observable _annotationKeySuffix = () => "annotations"; - - @observable _isAnyChildContentActive = false; - //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then - @computed get Document() { return this.props.Document; } - // This is the "The Document" -- it encapsulates, data, layout, and any templates - @computed get rootDoc() { return Cast(this.props.Document.rootDocument, Doc, null) || this.props.Document; } - // This is the rendering data of a document -- it may be "The Document", or it may be some template document that holds the rendering info - @computed get layoutDoc() { return Doc.Layout(this.props.Document); } - // This is the data part of a document -- ie, the data that is constant across all views of the document - @computed get dataDoc() { return this.props.DataDoc && (this.props.Document.isTemplateForField || this.props.Document.isTemplateDoc) ? this.props.DataDoc : this.props.Document[DataSym]; } - - // key where data is stored - @computed get fieldKey() { return this.props.fieldKey; } - - isAnyChildContentActive = () => this._isAnyChildContentActive; - - lookupField = (field: string) => ScriptCast((this.layoutDoc as any).lookupField)?.script.run({ self: this.layoutDoc, data: this.rootDoc, field: field }).result; - - styleFromLayoutString = (scale: number) => { - const style: { [key: string]: any } = {}; - const divKeys = ["width", "height", "fontSize", "transform", "left", "background", "left", "right", "top", "bottom", "pointerEvents", "position"]; - const replacer = (match: any, expr: string, offset: any, string: any) => { // bcz: this executes a script to convert a property expression string: { script } into a value - return ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name, scale: "number" })?.script.run({ self: this.rootDoc, this: this.layoutDoc, scale }).result?.toString() ?? ""; - }; - divKeys.map((prop: string) => { - const p = (this.props as any)[prop]; - typeof p === "string" && (style[prop] = p?.replace(/{([^.'][^}']+)}/g, replacer)); - }); - return style; - } - - protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; - - @computed public get annotationKey() { return this.fieldKey + (this._annotationKeySuffix() ? "-" + this._annotationKeySuffix() : ""); } - - @action.bound - removeDocument(doc: Doc | Doc[], annotationKey?: string, leavePushpin?: boolean): boolean { - const effectiveAcl = GetEffectiveAcl(this.dataDoc); - const indocs = doc instanceof Doc ? [doc] : doc; - const docs = indocs.filter(doc => [AclEdit, AclAdmin].includes(effectiveAcl) || GetEffectiveAcl(doc) === AclAdmin); - if (docs.length) { - setTimeout(() => docs.map(doc => { // this allows 'addDocument' to see the annotationOn field in order to create a pushin - Doc.SetInPlace(doc, "isPushpin", undefined, true); - doc.annotationOn === this.props.Document && Doc.SetInPlace(doc, "annotationOn", undefined, true); - })); - const targetDataDoc = this.dataDoc; - const value = DocListCast(targetDataDoc[annotationKey ?? this.annotationKey]); - const toRemove = value.filter(v => docs.includes(v)); - - if (toRemove.length !== 0) { - const recent = Cast(Doc.UserDoc().myRecentlyClosedDocs, Doc) as Doc; - toRemove.forEach(doc => { - leavePushpin && DocUtils.LeavePushpin(doc, annotationKey ?? this.annotationKey); - Doc.RemoveDocFromList(targetDataDoc, annotationKey ?? this.annotationKey, doc); - doc.context = undefined; - if (recent) { - Doc.RemoveDocFromList(recent, "data", doc); - Doc.AddDocToList(recent, "data", doc, undefined, true, true); - } - }); - this.isAnyChildContentActive() && this.props.select(false); - return true; - } + class Component extends Touchable<P> { + @observable _annotationKeySuffix = () => "annotations"; + + @observable _isAnyChildContentActive = false; + //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then + @computed get Document() { return this.props.Document; } + // This is the "The Document" -- it encapsulates, data, layout, and any templates + @computed get rootDoc() { return Cast(this.props.Document.rootDocument, Doc, null) || this.props.Document; } + // This is the rendering data of a document -- it may be "The Document", or it may be some template document that holds the rendering info + @computed get layoutDoc() { return Doc.Layout(this.props.Document); } + // This is the data part of a document -- ie, the data that is constant across all views of the document + @computed get dataDoc() { return this.props.DataDoc && (this.props.Document.isTemplateForField || this.props.Document.isTemplateDoc) ? this.props.DataDoc : this.props.Document[DataSym]; } + + // key where data is stored + @computed get fieldKey() { return this.props.fieldKey; } + + isAnyChildContentActive = () => this._isAnyChildContentActive; + + lookupField = (field: string) => ScriptCast((this.layoutDoc as any).lookupField)?.script.run({ self: this.layoutDoc, data: this.rootDoc, field: field }).result; + + styleFromLayoutString = (scale: number) => { + const style: { [key: string]: any } = {}; + const divKeys = ["width", "height", "fontSize", "transform", "left", "background", "left", "right", "top", "bottom", "pointerEvents", "position"]; + const replacer = (match: any, expr: string, offset: any, string: any) => { // bcz: this executes a script to convert a property expression string: { script } into a value + return ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name, scale: "number" })?.script.run({ self: this.rootDoc, this: this.layoutDoc, scale }).result?.toString() ?? ""; + }; + divKeys.map((prop: string) => { + const p = (this.props as any)[prop]; + typeof p === "string" && (style[prop] = p?.replace(/{([^.'][^}']+)}/g, replacer)); + }); + return style; } - return false; - } - // this is called with the document that was dragged and the collection to move it into. - // if the target collection is the same as this collection, then the move will be allowed. - // otherwise, the document being moved must be able to be removed from its container before - // moving it into the target. - @action.bound - moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[], annotationKey?: string) => boolean, annotationKey?: string): boolean => { - if (Doc.AreProtosEqual(this.props.Document, targetCollection)) { - return true; - } - const first = doc instanceof Doc ? doc : doc[0]; - if (!first?._stayInCollection && addDocument !== returnFalse) { - return UndoManager.RunInTempBatch(() => this.removeDocument(doc, annotationKey, true) && addDocument(doc, annotationKey)); + protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; + + @computed public get annotationKey() { return this.fieldKey + (this._annotationKeySuffix() ? "-" + this._annotationKeySuffix() : ""); } + + @action.bound + removeDocument(doc: Doc | Doc[], annotationKey?: string, leavePushpin?: boolean): boolean { + const effectiveAcl = GetEffectiveAcl(this.dataDoc); + const indocs = doc instanceof Doc ? [doc] : doc; + const docs = indocs.filter(doc => [AclEdit, AclAdmin].includes(effectiveAcl) || GetEffectiveAcl(doc) === AclAdmin); + if (docs.length) { + setTimeout(() => docs.map(doc => { // this allows 'addDocument' to see the annotationOn field in order to create a pushin + Doc.SetInPlace(doc, "isPushpin", undefined, true); + doc.annotationOn === this.props.Document && Doc.SetInPlace(doc, "annotationOn", undefined, true); + })); + const targetDataDoc = this.dataDoc; + const value = DocListCast(targetDataDoc[annotationKey ?? this.annotationKey]); + const toRemove = value.filter(v => docs.includes(v)); + + if (toRemove.length !== 0) { + const recent = Cast(Doc.UserDoc().myRecentlyClosedDocs, Doc) as Doc; + toRemove.forEach(doc => { + leavePushpin && DocUtils.LeavePushpin(doc, annotationKey ?? this.annotationKey); + Doc.RemoveDocFromList(targetDataDoc, annotationKey ?? this.annotationKey, doc); + doc.context = undefined; + if (recent) { + Doc.RemoveDocFromList(recent, "data", doc); + Doc.AddDocToList(recent, "data", doc, undefined, true, true); + } + }); + this.isAnyChildContentActive() && this.props.select(false); + return true; + } + } + + return false; } - return false; - } - @action.bound - addDocument = (doc: Doc | Doc[], annotationKey?: string): boolean => { - const docs = doc instanceof Doc ? [doc] : doc; - if (this.props.filterAddDocument?.(docs) === false || - docs.find(doc => Doc.AreProtosEqual(doc, this.props.Document) && Doc.LayoutField(doc) === Doc.LayoutField(this.props.Document))) { - return false; + // this is called with the document that was dragged and the collection to move it into. + // if the target collection is the same as this collection, then the move will be allowed. + // otherwise, the document being moved must be able to be removed from its container before + // moving it into the target. + @action.bound + moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[], annotationKey?: string) => boolean, annotationKey?: string): boolean => { + if (Doc.AreProtosEqual(this.props.Document, targetCollection)) { + return true; + } + const first = doc instanceof Doc ? doc : doc[0]; + if (!first?._stayInCollection && addDocument !== returnFalse) { + return UndoManager.RunInTempBatch(() => this.removeDocument(doc, annotationKey, true) && addDocument(doc, annotationKey)); + } + return false; } - const targetDataDoc = this.props.Document[DataSym]; - const docList = DocListCast(targetDataDoc[annotationKey ?? this.annotationKey]); - const added = docs.filter(d => !docList.includes(d)); - const effectiveAcl = GetEffectiveAcl(this.dataDoc); - - if (added.length) { - if (effectiveAcl === AclPrivate || effectiveAcl === AclReadonly) { - return false; - } - else { - if (this.props.Document[AclSym] && Object.keys(this.props.Document[AclSym]).length) { - added.forEach(d => { - for (const [key, value] of Object.entries(this.props.Document[AclSym])) { - if (d.author === denormalizeEmail(key.substring(4)) && !d.aliasOf) distributeAcls(key, SharingPermissions.Admin, d); - } - }); - } - - if (effectiveAcl === AclAugment) { - added.map(doc => { - if ([AclAdmin, AclEdit].includes(GetEffectiveAcl(doc))) inheritParentAcls(CurrentUserUtils.ActiveDashboard, doc); - doc.context = this.props.Document; - if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.props.Document; - this.props.layerProvider?.(doc, true); - Doc.AddDocToList(targetDataDoc, annotationKey ?? this.annotationKey, doc); - }); - } - else { - added.filter(doc => [AclAdmin, AclEdit].includes(GetEffectiveAcl(doc))).map(doc => { // only make a pushpin if we have acl's to edit the document - this.props.layerProvider?.(doc, true); - //DocUtils.LeavePushpin(doc); - doc._stayInCollection = undefined; - doc.context = this.props.Document; - if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.props.Document; - - inheritParentAcls(CurrentUserUtils.ActiveDashboard, doc); - }); - const annoDocs = targetDataDoc[annotationKey ?? this.annotationKey] as List<Doc>; - if (annoDocs instanceof List) annoDocs.push(...added); - else targetDataDoc[annotationKey ?? this.annotationKey] = new List<Doc>(added); - targetDataDoc[(annotationKey ?? this.annotationKey) + "-lastModified"] = new DateField(new Date(Date.now())); - } - } + @action.bound + addDocument = (doc: Doc | Doc[], annotationKey?: string): boolean => { + const docs = doc instanceof Doc ? [doc] : doc; + if (this.props.filterAddDocument?.(docs) === false || + docs.find(doc => Doc.AreProtosEqual(doc, this.props.Document) && Doc.LayoutField(doc) === Doc.LayoutField(this.props.Document))) { + return false; + } + const targetDataDoc = this.props.Document[DataSym]; + const docList = DocListCast(targetDataDoc[annotationKey ?? this.annotationKey]); + const added = docs.filter(d => !docList.includes(d)); + const effectiveAcl = GetEffectiveAcl(this.dataDoc); + + if (added.length) { + if (effectiveAcl === AclPrivate || effectiveAcl === AclReadonly) { + return false; + } + else { + if (this.props.Document[AclSym] && Object.keys(this.props.Document[AclSym]).length) { + added.forEach(d => { + for (const [key, value] of Object.entries(this.props.Document[AclSym])) { + if (d.author === denormalizeEmail(key.substring(4)) && !d.aliasOf) distributeAcls(key, SharingPermissions.Admin, d); + } + }); + } + + if (effectiveAcl === AclAugment) { + added.map(doc => { + if ([AclAdmin, AclEdit].includes(GetEffectiveAcl(doc))) inheritParentAcls(CurrentUserUtils.ActiveDashboard, doc); + doc.context = this.props.Document; + if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.props.Document; + this.props.layerProvider?.(doc, true); + Doc.AddDocToList(targetDataDoc, annotationKey ?? this.annotationKey, doc); + }); + } + else { + added.filter(doc => [AclAdmin, AclEdit].includes(GetEffectiveAcl(doc))).map(doc => { // only make a pushpin if we have acl's to edit the document + this.props.layerProvider?.(doc, true); + //DocUtils.LeavePushpin(doc); + doc._stayInCollection = undefined; + doc.context = this.props.Document; + if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.props.Document; + + inheritParentAcls(CurrentUserUtils.ActiveDashboard, doc); + }); + const annoDocs = targetDataDoc[annotationKey ?? this.annotationKey] as List<Doc>; + if (annoDocs instanceof List) annoDocs.push(...added); + else targetDataDoc[annotationKey ?? this.annotationKey] = new List<Doc>(added); + targetDataDoc[(annotationKey ?? this.annotationKey) + "-lastModified"] = new DateField(new Date(Date.now())); + } + } + } + return true; } - return true; - } - whenChildContentsActiveChanged = action((isActive: boolean) => this.props.whenChildContentsActiveChanged(this._isAnyChildContentActive = isActive)); - } - return Component; + whenChildContentsActiveChanged = action((isActive: boolean) => this.props.whenChildContentsActiveChanged(this._isAnyChildContentActive = isActive)); + } + return Component; }
\ No newline at end of file |