diff options
author | bobzel <zzzman@gmail.com> | 2025-04-22 14:45:05 -0400 |
---|---|---|
committer | bobzel <zzzman@gmail.com> | 2025-04-22 14:45:05 -0400 |
commit | 7671e543dc677c82071d6eaccc33bd164450620d (patch) | |
tree | 83c0d14357cea070f9c54ee41f031aa162597e90 /src | |
parent | 6aa7649ea9195cf3fc53cf70e126095a9045e057 (diff) |
protoype version of scrapboxBox. added rejectDrop prop to prevent drops on child documentviews
Diffstat (limited to 'src')
6 files changed, 64 insertions, 33 deletions
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index e7186c0e3..acf6f928a 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -161,7 +161,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps CollectionViewType.Card, CollectionViewType.Carousel, CollectionViewType.Grid, - ].includes(this.selectedDoc?.type_collection as CollectionViewType); + ].includes(this.selectedDoc?.type_collection as CollectionViewType) || this.selectedDoc.$type === DocumentType.SCRAPBOOK; // prettier-ignore } rtfWidth = () => (!this.selectedLayoutDoc ? 0 : Math.min(NumCast(this.selectedLayoutDoc?._width), this._props.width - 20)); diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 9a0fda3f8..e79d0a76d 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -58,7 +58,7 @@ export interface CollectionViewProps extends React.PropsWithChildren<FieldViewPr childOpacity?: () => number; childContextMenuItems?: () => { script: ScriptField; label: string }[]; childLayoutTemplate?: () => Doc | undefined; // specify a layout Doc template to use for children of the collection - childRejectDrop?: (draggedDoc: Doc[] | undefined, subView?: DocumentView) => boolean; // whether a child document can be dropped on this document + childRejectDrop?: (de: DragManager.DropEvent, subView?: DocumentView) => boolean; // whether a child document can be dropped on this document childHideDecorationTitle?: boolean; childHideResizeHandles?: boolean; childHideDecorations?: boolean; @@ -324,7 +324,7 @@ export function CollectionSubView<X>() { protected onInternalDrop(e: Event, de: DragManager.DropEvent): boolean { const { docDragData } = de.complete; - if (docDragData && !docDragData.draggedDocuments.includes(this.Document) && !this._props.rejectDrop?.(docDragData.draggedDocuments, this.DocumentView?.())) { + if (docDragData && !docDragData.draggedDocuments.includes(this.Document) && !this._props.rejectDrop?.(de, this.DocumentView?.())) { let added; const dropAction = docDragData.dropAction || docDragData.userDropAction; const targetDocments = DocListCast(this.dataDoc[this._props.fieldKey]); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 08126d4fe..c4971c204 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -256,8 +256,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection override contentBounds = () => { const { x, y, r, b } = aggregateBounds( this._layoutElements.filter(e => e.bounds?.width && !e.bounds.z).map(e => e.bounds!), - NumCast(this.layoutDoc._xPadding, this._props.xPadding ?? 0), - NumCast(this.layoutDoc._yPadding, this._props.yPadding ?? 0) + NumCast(this.layoutDoc._xPadding, NumCast(this.layoutDoc._xMargin, this._props.xPadding ?? 0)), + NumCast(this.layoutDoc._yPadding, NumCast(this.layoutDoc._yMargin, this._props.yPadding ?? 0)) ); const [width, height] = [r - x, b - y]; return { @@ -496,7 +496,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection }, 'link drop'); onInternalDrop = (e: Event, de: DragManager.DropEvent): boolean => { - if (this._props.rejectDrop?.(de.complete.docDragData?.draggedDocuments, this._props.DocumentView?.())) return false; + if (this._props.rejectDrop?.(de, this._props.DocumentView?.())) return false; if (de.complete.annoDragData?.dragDocument && super.onInternalDrop(e, de)) return this.internalAnchorAnnoDrop(e, de, de.complete.annoDragData); if (de.complete.linkDragData) return this.internalLinkDrop(e, de, de.complete.linkDragData); if (de.complete.docDragData?.droppedDocuments.length) return this.internalDocDrop(e, de, de.complete.docDragData); diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index b4473d9dc..34e3a5d76 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -68,7 +68,7 @@ export interface FieldViewSharedProps { isGroupActive?: () => string | undefined; // is this document part of a group that is active // eslint-disable-next-line no-use-before-define setContentViewBox?: (view: ViewBoxInterface<FieldViewProps>) => void; // called by rendered field's viewBox so that DocumentView can make direct calls to the viewBox - rejectDrop?: (draggedDoc: Doc[] | undefined, subView?: DocumentView) => boolean; // whether a document drop is rejected + rejectDrop?: (de: DragManager.DropEvent, subView?: DocumentView) => boolean; // whether a document drop is rejected PanelWidth: () => number; PanelHeight: () => number; isDocumentActive?: () => boolean | undefined; // whether a document should handle pointer events diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index dc23a695d..9897a0062 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -586,7 +586,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB return true; } const dragData = de.complete.docDragData; - if (dragData) { + if (dragData && !this._props.rejectDrop?.(de, this.DocumentView?.())) { const layoutProto = DocCast(this.layoutDoc.proto); const dataDoc = layoutProto && Doc.IsDelegateField(layoutProto, this.fieldKey) ? layoutProto : this.dataDoc; const effectiveAcl = GetEffectiveAcl(dataDoc); diff --git a/src/client/views/nodes/scrapbook/ScrapbookBox.tsx b/src/client/views/nodes/scrapbook/ScrapbookBox.tsx index 24946f4d2..6ee9f39ab 100644 --- a/src/client/views/nodes/scrapbook/ScrapbookBox.tsx +++ b/src/client/views/nodes/scrapbook/ScrapbookBox.tsx @@ -1,6 +1,6 @@ import { action, makeObservable, observable } from 'mobx'; import * as React from 'react'; -import { Doc } from '../../../../fields/Doc'; +import { Doc, DocListCast, StrListCast } from '../../../../fields/Doc'; import { List } from '../../../../fields/List'; import { emptyFunction } from '../../../../Utils'; import { Docs } from '../../../documents/Documents'; @@ -10,10 +10,11 @@ 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<FieldViewProps>() { @observable createdDate: string; - private _dropDisposer?: DragManager.DragDropDisposer; constructor(props: FieldViewProps) { super(props); @@ -45,6 +46,28 @@ export class ScrapbookBox extends ViewBoxAnnotatableComponent<FieldViewProps>() 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; + const placeholder = new Doc(); + placeholder.proto = image; + placeholder.original = image; + placeholder.height = 200; + placeholder.x = 0; + placeholder.y = -100; + //placeholder.overrideFields = new List<string>(['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_textType = 'one line'; + const placeholder2 = new Doc(); + placeholder2.proto = summary; + placeholder2.original = summary; + placeholder2.x = 0; + placeholder2.y = 200; + placeholder2._width = 250; + //placeholder2.overrideFields = new List<string>(['x', 'y', '_width']); // shouldn't need to do this for layout fields since the placeholder already overrides its protos + this.dataDoc[this.fieldKey] = new List<Doc>([placeholder, placeholder2]); } } @@ -52,43 +75,49 @@ export class ScrapbookBox extends ViewBoxAnnotatableComponent<FieldViewProps>() this.setTitle(); } - childRejectDrop = (draggedDoc: Doc[] | undefined, subView?: DocumentView) => { - if (draggedDoc?.length === 1 && subView) { - if (subView.Document.type === DocumentType.IMG && draggedDoc[0].$type !== DocumentType.IMG) { - return true; - } - } - return false; + childRejectDrop = (de: DragManager.DropEvent, subView?: DocumentView) => { + return true; // disable dropping documents onto any child of the scrapbook. }; - rejectDrop = (draggedDoc: Doc[] | undefined /* , subView?: DocumentView */) => { - if (draggedDoc?.length === 1 && draggedDoc[0].$type !== DocumentType.IMG) { - return true; - } - return false; - }; - onInternalDrop = (e: Event, de: DragManager.DropEvent) => { - if (de.complete.docDragData?.draggedDocuments[0]?.$type === DocumentType.IMG) { - return true; - } - return false; + 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. }; - protected createDashEventsTarget = (ele: HTMLDivElement | null) => { - this._dropDisposer?.(); - if (ele) { - this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc); + filterAddDocument = (docIn: Doc | Doc[]) => { + const docs = toList(docIn); + if (docs?.length === 1) { + const placeholder = DocListCast(this.dataDoc[this.fieldKey]).find(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 + + if (placeholder) { + // 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(() => { + //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 + placeholder.proto = docs[0]; + }, 'Scrapbook add') + ); + return false; + } } + return false; }; render() { return ( - <div style={{ background: 'beige', width: '100%', height: '100%' }} ref={r => r && this.createDashEventsTarget(r)}> + <div style={{ background: 'beige', width: '100%', height: '100%' }}> <CollectionView {...this._props} // setContentViewBox={emptyFunction} rejectDrop={this.rejectDrop} childRejectDrop={this.childRejectDrop} + filterAddDocument={this.filterAddDocument} /> + {/* <div style={{ border: '1px black', borderStyle: 'dotted', position: 'absolute', top: '50%', width: '100%', textAlign: 'center' }}>Drop an image here</div> */} </div> ); } @@ -102,9 +131,11 @@ Docs.Prototypes.TemplateMap.set(DocumentType.SCRAPBOOK, { _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', }, |