import { action, makeObservable, observable } from 'mobx'; import * as React from 'react'; import { Doc, DocListCast, StrListCast } from '../../../../fields/Doc'; import { List } from '../../../../fields/List'; import { emptyFunction } from '../../../../Utils'; import { Docs } from '../../../documents/Documents'; import { DocumentType } from '../../../documents/DocumentTypes'; import { CollectionView } from '../../collections/CollectionView'; import { ViewBoxAnnotatableComponent } from '../../DocComponent'; import { DocumentView } from '../DocumentView'; import { FieldView, FieldViewProps } from '../FieldView'; import { DragManager } from '../../../util/DragManager'; import { RTFCast, StrCast, toList } from '../../../../fields/Types'; import { undoable } from '../../../util/UndoManager'; // Scrapbook view: a container that lays out its child items in a grid/template export class ScrapbookBox extends ViewBoxAnnotatableComponent() { @observable createdDate: string; constructor(props: FieldViewProps) { super(props); makeObservable(this); this.createdDate = this.getFormattedDate(); // ensure we always have a List in dataDoc['items'] if (!this.dataDoc[this.fieldKey]) { this.dataDoc[this.fieldKey] = new List(); } this.createdDate = this.getFormattedDate(); this.setTitle(); } public static LayoutString(fieldStr: string) { return FieldView.LayoutString(ScrapbookBox, fieldStr); } getFormattedDate(): string { return new Date().toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric', }); } @action setTitle() { const title = `Scrapbook - ${this.createdDate}`; if (this.dataDoc.title !== title) { this.dataDoc.title = title; const image = Docs.Create.TextDocument('image'); image.accepts_docType = DocumentType.IMG; image.accepts_tagType = 'LANDSCAPE' //should i be writing fields on this doc? clarify diff between this and proto, original const placeholder = new Doc(); placeholder.proto = image; placeholder.original = image; placeholder._width = 250; placeholder._height = 200; placeholder.x = 0; placeholder.y = -100; //placeholder.overrideFields = new List(['x', 'y']); // shouldn't need to do this for layout fields since the placeholder already overrides its protos const summary = Docs.Create.TextDocument('summary'); summary.accepts_docType = DocumentType.RTF; summary.accepts_tagType = 'caption'; //summary.$tags_chat = new List(['lengthy description']); //we need to go back and set this const placeholder2 = new Doc(); placeholder2.proto = summary; placeholder2.original = summary; placeholder2.x = 0; placeholder2.y = 200; placeholder2._width = 250; //placeholder2.overrideFields = new List(['x', 'y', '_width']); // shouldn't need to do this for layout fields since the placeholder already overrides its protos const sidebar = Docs.Create.TextDocument('sidebar'); sidebar.accepts_docType = DocumentType.RTF; sidebar.accepts_tagType = 'lengthy description'; //accepts_textType = 'lengthy description' const placeholder3 = new Doc(); placeholder3.proto = sidebar; placeholder3.original = sidebar; placeholder3.x = 280; placeholder3.y = -50; placeholder3._width = 50; placeholder3._height = 200; const internalImg = Docs.Create.TextDocument('image internal'); internalImg.accepts_docType = DocumentType.IMG; internalImg.accepts_tagType = 'PERSON' //should i be writing fields on this doc? clarify diff between this and proto, original const placeholder5 = new Doc(); placeholder5.proto = image; placeholder5.original = image; placeholder5._width = 50; placeholder5._height = 100; placeholder5.x = 0; placeholder5.y = -100; const collection = Docs.Create.StackingDocument([placeholder5], { _width: 300, _height: 300, title: "internal coll" }); //collection.accepts_docType = DocumentType.COL; don't mark this field const placeholder4 = new Doc(); placeholder4.proto = collection; placeholder4.original = collection; placeholder4.x = -200; placeholder4.y = -100; placeholder4._width = 100; placeholder4._height = 200; /*note-to-self would doing: const collection = Docs.Create.ScrapbookDocument([placeholder, placeholder2, placeholder3]); create issues with references to the same object?*/ /*note-to-self Should we consider that there are more collections than just COL type collections? when spreading*/ /*note-to-self difference between passing a new List versus just the raw array? */ this.dataDoc[this.fieldKey] = new List([placeholder, placeholder2, placeholder3, placeholder4]); } } componentDidMount() { this.setTitle(); } childRejectDrop = (de: DragManager.DropEvent, subView?: DocumentView) => { return true; // disable dropping documents onto any child of the scrapbook. }; rejectDrop = (de: DragManager.DropEvent, subView?: DocumentView) => { // Test to see if the dropped doc is dropped on an acceptable location (anywerhe? on a specific box). // const draggedDocs = de.complete.docDragData?.draggedDocuments; return false; // allow all Docs to be dropped onto scrapbook -- let filterAddDocument make the final decision. }; filterAddDocument = (docIn: Doc | Doc[]) => { const docs = toList(docIn); //The docs being added to the scrapbook // 1) Grab all template slots: const slots = DocListCast(this.dataDoc[this.fieldKey]); // 2) recursive unwrap: const unwrap = (items: Doc[]): Doc[] => items.flatMap(d => d.$type === DocumentType.COL ? unwrap(DocListCast(d[Doc.LayoutDataKey(d)])) : [d] ); // 3) produce a flat list of every doc, unwrapping any number of nested COLs const allDocs: Doc[] = unwrap(slots); if (docs?.length === 1) { const placeholder = allDocs.filter(d => (d.accepts_docType === docs[0].$type || // match fields based on type, or by analyzing content .. simple example of matching text in placeholder to dropped doc's type RTFCast(d[Doc.LayoutDataKey(d)])?.Text.includes(StrCast(docs[0].$type))) ); // prettier-ignore //DocListCast(this.Document.items).map(doc => DocListCast(doc[Doc.LayoutDataKey(doc)]) if (placeholder) { /**Look at the autotags and see what matches*RTFCast(d[Doc.LayoutDataKey(d)])?.Text*/ // ugh. we have to tell the underlying view not to add the Doc so that we can add it where we want it. // However, returning 'false' triggers an undo. so this settimeout is needed to make the assignment happen after the undo. setTimeout( undoable(() => { const slotTagsList: Set[] = placeholder.map(doc => new Set(StrListCast(doc.$tags_chat)) ); // turn docs[0].$tags_chat into a Set const targetTags = new Set(StrListCast(docs[0].$tags_chat)); //StrListCast(placeholder.overrideFields).map(field => (docs[0][field] = placeholder[field])); // // shouldn't need to do this for layout fields since the placeholder already overrides its protos // find the first placeholder that shares *any* tag const match = placeholder.find(ph => ph.accepts_tagType != null && // make sure it actually has one targetTags.has(StrCast(ph.accepts_tagType)) // test membership in the Set //StrListCast(ph.$tags_chat).some(tag => targetTags.has(tag)) ); if (match) { match.proto = docs[0]; } /*const chosenPlaceholder = placeholder.find(d => pl = new Set(StrListCast(d.$tags_chat) d.$tags_chat && d.$tags_chat[0].equals(docs[0].$tags_chat)); //why [0] if (chosenPlaceholder){ chosenPlaceholder.proto = docs[0];}*/ //excess if statement?? }, 'Scrapbook add') ); return false; } } return false; }; render() { return (
{/*
Drop an image here
*/}
); } } // Register scrapbook Docs.Prototypes.TemplateMap.set(DocumentType.SCRAPBOOK, { layout: { view: ScrapbookBox, dataField: 'items' }, options: { acl: '', _height: 200, _xMargin: 10, _yMargin: 10, _layout_fitWidth: false, _layout_autoHeight: true, _layout_reflowVertical: true, _layout_reflowHorizontal: true, _freeform_fitContentsToBox: true, defaultDoubleClick: 'ignore', systemIcon: 'BsImages', }, });