From 7735795a5660ed03a711e29cd0240736c9444fe1 Mon Sep 17 00:00:00 2001 From: sharkiecodes Date: Mon, 21 Apr 2025 19:58:47 -0400 Subject: scrapbooks --- src/client/documents/DocUtils.ts | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src/client/documents/DocUtils.ts') diff --git a/src/client/documents/DocUtils.ts b/src/client/documents/DocUtils.ts index 7b36c3a66..db0770b12 100644 --- a/src/client/documents/DocUtils.ts +++ b/src/client/documents/DocUtils.ts @@ -308,6 +308,13 @@ export namespace DocUtils { */ export async function DocumentFromType(type: string, path: string, options: DocumentOptions, overwriteDoc?: Doc): Promise> { let ctor: ((path: string, options: DocumentOptions, overwriteDoc?: Doc) => Doc | Promise) | undefined; + + if (type.indexOf('scrapbook') !== -1) { + ctor = Docs.Create.ScrapbookDocument; + if (!options._width) options._width = 400; + if (!options._title) options._title = 'New Scrapbook'; + } + if (type.indexOf('image') !== -1) { ctor = Docs.Create.ImageDocument; if (!options._width) options._width = 300; -- cgit v1.2.3-70-g09d2 From e8f1d494d36a5e1f1ee33d59faa4be2559cd752e Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 22 Apr 2025 10:54:38 -0400 Subject: adding drop handling code for scrapbookBox --- src/client/documents/DocUtils.ts | 60 +++++++-------- src/client/views/DocumentDecorations.tsx | 2 +- src/client/views/InkingStroke.tsx | 2 +- src/client/views/Main.tsx | 4 +- .../views/collections/CollectionStackingView.tsx | 5 +- src/client/views/collections/CollectionSubView.tsx | 3 +- src/client/views/collections/CollectionView.tsx | 20 ++--- .../collectionFreeForm/CollectionFreeFormView.tsx | 2 + .../collectionGrid/CollectionGridView.tsx | 1 + .../collectionLinear/CollectionLinearView.tsx | 1 + .../CollectionMulticolumnView.tsx | 1 + .../CollectionMultirowView.tsx | 1 + src/client/views/nodes/FieldView.tsx | 2 +- src/client/views/nodes/FontIconBox/FontIconBox.tsx | 4 +- src/client/views/nodes/ImageBox.tsx | 5 +- src/client/views/nodes/scrapbook/ScrapbookBox.tsx | 87 ++++++++++++---------- 16 files changed, 104 insertions(+), 96 deletions(-) (limited to 'src/client/documents/DocUtils.ts') diff --git a/src/client/documents/DocUtils.ts b/src/client/documents/DocUtils.ts index db0770b12..df14dce5a 100644 --- a/src/client/documents/DocUtils.ts +++ b/src/client/documents/DocUtils.ts @@ -308,13 +308,7 @@ export namespace DocUtils { */ export async function DocumentFromType(type: string, path: string, options: DocumentOptions, overwriteDoc?: Doc): Promise> { let ctor: ((path: string, options: DocumentOptions, overwriteDoc?: Doc) => Doc | Promise) | undefined; - - if (type.indexOf('scrapbook') !== -1) { - ctor = Docs.Create.ScrapbookDocument; - if (!options._width) options._width = 400; - if (!options._title) options._title = 'New Scrapbook'; - } - + if (type.indexOf('image') !== -1) { ctor = Docs.Create.ImageDocument; if (!options._width) options._width = 300; @@ -718,33 +712,31 @@ export namespace DocUtils { nativeWidth: 40, nativeHeight: 40, }) - : (defaultTextTemplate?.type === DocumentType.JOURNAL ? Docs.Create.DailyJournalDocument:Docs.Create.TextDocument)( - '', - { - annotationOn, - backgroundColor, - x, - y, - title, - ...(defaultTextTemplate - ? {} // if the new doc will inherit from a template, don't set any layout fields since that would block the inheritance - : { - _width: width || BoolCast(Doc.UserDoc().fitBox) ? Number(StrCast(Doc.UserDoc().fontSize).replace('px', '')) * 1.5 * 6 : 200, - _height: BoolCast(Doc.UserDoc().fitBox) ? Number(StrCast(Doc.UserDoc().fontSize).replace('px', '')) * 1.5 : 35, - _layout_autoHeight: true, - backgroundColor: StrCast(Doc.UserDoc().textBackgroundColor), - borderColor: Doc.UserDoc().borderColor as string, - borderWidth: Doc.UserDoc().borderWidth as number, - text_centered: BoolCast(Doc.UserDoc().textCentered), - text_fitBox: BoolCast(Doc.UserDoc().fitBox), - text_align: StrCast(Doc.UserDoc().textAlign), - text_fontColor: StrCast(Doc.UserDoc().fontColor), - text_fontFamily: StrCast(Doc.UserDoc().fontFamily), - text_fontWeight: StrCast(Doc.UserDoc().fontWeight), - text_fontStyle: StrCast(Doc.UserDoc().fontStyle), - text_fontDecoration: StrCast(Doc.UserDoc().fontDecoration), - }), - }); + : (defaultTextTemplate?.type === DocumentType.JOURNAL ? Docs.Create.DailyJournalDocument : Docs.Create.TextDocument)('', { + annotationOn, + backgroundColor, + x, + y, + title, + ...(defaultTextTemplate + ? {} // if the new doc will inherit from a template, don't set any layout fields since that would block the inheritance + : { + _width: width || BoolCast(Doc.UserDoc().fitBox) ? Number(StrCast(Doc.UserDoc().fontSize).replace('px', '')) * 1.5 * 6 : 200, + _height: BoolCast(Doc.UserDoc().fitBox) ? Number(StrCast(Doc.UserDoc().fontSize).replace('px', '')) * 1.5 : 35, + _layout_autoHeight: true, + backgroundColor: StrCast(Doc.UserDoc().textBackgroundColor), + borderColor: Doc.UserDoc().borderColor as string, + borderWidth: Doc.UserDoc().borderWidth as number, + text_centered: BoolCast(Doc.UserDoc().textCentered), + text_fitBox: BoolCast(Doc.UserDoc().fitBox), + text_align: StrCast(Doc.UserDoc().textAlign), + text_fontColor: StrCast(Doc.UserDoc().fontColor), + text_fontFamily: StrCast(Doc.UserDoc().fontFamily), + text_fontWeight: StrCast(Doc.UserDoc().fontWeight), + text_fontStyle: StrCast(Doc.UserDoc().fontStyle), + text_fontDecoration: StrCast(Doc.UserDoc().fontDecoration), + }), + }); if (defaultTextTemplate) { tbox.layout_fieldKey = 'layout_' + StrCast(defaultTextTemplate.title); diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 7424aaf2c..3f11a4713 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -240,7 +240,7 @@ export class DocumentDecorations extends ObservableReactComponent() * factor for converting between ink and screen space. */ inkScaledData = () => { - const inkData = Cast(this.dataDoc[this.fieldKey], InkField, Cast(this.layoutDoc[this.fieldKey], InkField, null))?.inkData ?? []; + const inkData = Cast(this.dataDoc[this.fieldKey], InkField, Cast(this.layoutDoc[this.fieldKey], InkField, null) ?? null)?.inkData ?? []; const inkStrokeWidth = NumCast(this.layoutDoc.stroke_width, 1); const inkTop = Math.min(...inkData.map(p => p.Y)) - inkStrokeWidth / 2; const inkBottom = Math.max(...inkData.map(p => p.Y)) + inkStrokeWidth / 2; diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 7992ed412..b884eb8c8 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -53,7 +53,6 @@ import { WebBox } from './nodes/WebBox'; import { CalendarBox } from './nodes/calendarBox/CalendarBox'; import { ChatBox } from './nodes/chatbot/chatboxcomponents/ChatBox'; import { DailyJournal } from './nodes/formattedText/DailyJournal'; -import { ScrapbookVersionTwo } from './nodes/scrapbook/ScrapbookVersionTwo'; import { DashDocCommentView } from './nodes/formattedText/DashDocCommentView'; import { DashDocView } from './nodes/formattedText/DashDocView'; import { DashFieldView } from './nodes/formattedText/DashFieldView'; @@ -66,6 +65,7 @@ import { PresBox, PresSlideBox } from './nodes/trails'; import { FaceRecognitionHandler } from './search/FaceRecognitionHandler'; import { SearchBox } from './search/SearchBox'; import { StickerPalette } from './smartdraw/StickerPalette'; +import { ScrapbookBox } from './nodes/scrapbook/ScrapbookBox'; dotenv.config(); @@ -121,7 +121,6 @@ FieldLoader.ServerLoadStatus = { requested: 0, retrieved: 0, message: 'cache' }; FormattedTextBox, DailyJournal, // AARAV ImageBox, - ScrapbookVersionTwo, FontIconBox, LabelBox, EquationBox, @@ -136,6 +135,7 @@ FieldLoader.ServerLoadStatus = { requested: 0, retrieved: 0, message: 'cache' }; VideoBox, AudioBox, RecordingBox, + ScrapbookBox, PresBox, PresSlideBox, SearchBox, diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 9155227dd..f11e646cc 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -343,7 +343,7 @@ export class CollectionStackingView extends CollectionSubView Cast(this.Document.childLayoutFitWidth, 'boolean', this._props.childLayoutFitWidth?.(doc) ?? Cast(doc.layout_fitWidth, 'boolean', null) ?? null); // this is what renders the document that you see on the screen // called in Children: this actually adds a document to our children list - getDisplayDoc(doc: Doc, trans: () => string, count: number) { + getDisplayDoc = (doc: Doc, trans: () => string, count: number) => { const dataDoc = doc.isTemplateDoc || doc.isTemplateForField ? this._props.TemplateDataDocument : undefined; this._docXfs.push({ stackedDocTransform: this.getDocTransform(doc), width: this.getDocWidth(doc), height: this.getDocHeight(doc) }); return count > this._renderCount ? null : ( @@ -384,6 +384,7 @@ export class CollectionStackingView extends CollectionSubView ); - } + }; getDocTransform = computedFn((doc: Doc) => () => { // these must be referenced for document decorations to update when the text box container is scrolled diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index bc7d6f897..9a0fda3f8 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -58,6 +58,7 @@ export interface CollectionViewProps extends React.PropsWithChildren 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 childHideDecorationTitle?: boolean; childHideResizeHandles?: boolean; childHideDecorations?: boolean; @@ -323,7 +324,7 @@ export function CollectionSubView() { protected onInternalDrop(e: Event, de: DragManager.DropEvent): boolean { const { docDragData } = de.complete; - if (docDragData && !docDragData.draggedDocuments.includes(this.Document)) { + if (docDragData && !docDragData.draggedDocuments.includes(this.Document) && !this._props.rejectDrop?.(docDragData.draggedDocuments, 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/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index c9af92a1b..eb9caf29d 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -183,15 +183,17 @@ export class CollectionView extends ViewBoxAnnotatableComponent - onClicks.push({ - description: `Set child ${childClick.title}`, - icon: 'edit', - event: () => { - this.dataDoc[StrCast(childClick.targetScriptKey)] = ObjectField.MakeCopy(ScriptCast(childClick.data)); - }, - }) - ); + DocListCast(Cast(Doc.UserDoc()['clickFuncs-child'], Doc, null)?.data) + .filter(childClick => ScriptCast(childClick.data)) + .forEach(childClick => + onClicks.push({ + description: `Set child ${childClick.title}`, + icon: 'edit', + event: () => { + this.dataDoc[StrCast(childClick.targetScriptKey)] = ObjectField.MakeCopy(ScriptCast(childClick.data)!); + }, + }) + ); !Doc.IsSystem(this.Document) && !existingOnClick && cm.addItem({ description: 'OnClick...', noexpand: true, subitems: onClicks, icon: 'mouse-pointer' }); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 842293358..08126d4fe 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -496,6 +496,7 @@ export class CollectionFreeFormView extends CollectionSubView { + if (this._props.rejectDrop?.(de.complete.docDragData?.draggedDocuments, 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); @@ -1562,6 +1563,7 @@ export class CollectionFreeFormView extends CollectionSubView string | undefined; // is this document part of a group that is active // eslint-disable-next-line no-use-before-define setContentViewBox?: (view: ViewBoxInterface) => 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 PanelWidth: () => number; PanelHeight: () => number; isDocumentActive?: () => boolean | undefined; // whether a document should handle pointer events diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx index 3190757e2..5941e1669 100644 --- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx +++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx @@ -117,10 +117,10 @@ export class FontIconBox extends ViewBoxBaseComponent() { default: type = 'slider'; break; } // prettier-ignore - const numScript = (value?: number) => ScriptCast(this.Document.script).script.run({ this: this.Document, value, _readOnly_: value === undefined }); + const numScript = (value?: number) => ScriptCast(this.Document.script)?.script.run({ this: this.Document, value, _readOnly_: value === undefined }); const color = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color) as string; // Script for checking the outcome of the toggle - const checkResult = Number(Number(numScript().result ?? 0).toPrecision(NumCast(this.dataDoc.numPrecision, 3))); + const checkResult = Number(Number(numScript()?.result ?? 0).toPrecision(NumCast(this.dataDoc.numPrecision, 3))); return ( () { drop = undoable( action((e: Event, de: DragManager.DropEvent) => { - if (de.complete.docDragData) { + if (de.complete.docDragData && this._props.rejectDrop?.(de.complete.docDragData?.draggedDocuments, this.DocumentView?.())) { let added: boolean | undefined; const hitDropTarget = (ele: HTMLElement, dropTarget: HTMLDivElement | null): boolean => { if (!ele) return false; @@ -938,6 +938,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent() { ScreenToLocalTransform={this.screenToLocalTransform} select={emptyFunction} focus={this.focus} + rejectDrop={this._props.rejectDrop} getScrollHeight={this.getScrollHeight} NativeDimScaling={returnOne} isAnyChildContentActive={returnFalse} diff --git a/src/client/views/nodes/scrapbook/ScrapbookBox.tsx b/src/client/views/nodes/scrapbook/ScrapbookBox.tsx index 56cfcda70..b02976067 100644 --- a/src/client/views/nodes/scrapbook/ScrapbookBox.tsx +++ b/src/client/views/nodes/scrapbook/ScrapbookBox.tsx @@ -1,24 +1,25 @@ -import { makeObservable } from 'mobx'; +import { action, makeObservable, observable } from 'mobx'; import * as React from 'react'; -import { ViewBoxAnnotatableComponent } from '../../DocComponent'; -import { FieldView, FieldViewProps } from '../FieldView'; +import { Doc } from '../../../../fields/Doc'; +import { List } from '../../../../fields/List'; +import { emptyFunction } from '../../../../Utils'; import { Docs } from '../../../documents/Documents'; import { DocumentType } from '../../../documents/DocumentTypes'; -import { action, observable } from 'mobx'; -import { DocListCast } from '../../../../fields/Doc'; -import { Doc } from '../../../../fields/Doc'; +import { CollectionView } from '../../collections/CollectionView'; +import { ViewBoxAnnotatableComponent } from '../../DocComponent'; import { DocumentView } from '../DocumentView'; -import { FormattedTextBox } from '../formattedText/FormattedTextBox'; -import { List } from '../../../../fields/List'; +import { FieldView, FieldViewProps } from '../FieldView'; +import { DragManager } from '../../../util/DragManager'; // Scrapbook view: a container that lays out its child items in a grid/template export class ScrapbookBox extends ViewBoxAnnotatableComponent() { @observable createdDate: string; + private _dropDisposer?: DragManager.DragDropDisposer; 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(); @@ -31,12 +32,6 @@ export class ScrapbookBox extends ViewBoxAnnotatableComponent() return FieldView.LayoutString(ScrapbookBox, fieldStr); } - - - - - - getFormattedDate(): string { return new Date().toLocaleDateString(undefined, { year: 'numeric', @@ -55,40 +50,50 @@ export class ScrapbookBox extends ViewBoxAnnotatableComponent() componentDidMount() { this.setTitle(); - if (!this.dataDoc[this.fieldKey]) { - this.dataDoc[this.fieldKey] = new List(); - } } + 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; + }; + 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; + }; + + protected createDashEventsTarget = (ele: HTMLDivElement | null) => { + this._dropDisposer?.(); + if (ele) { + this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc); + } + }; + render() { - // cast into an array even if empty - const items: Doc[] = DocListCast(this.dataDoc[this.fieldKey]); - return ( -
- +
r && this.createDashEventsTarget(r)}> +
- - //
- // {items.length === 0 - // ?
Drop docs here
- // : items.map((childDoc, idx) => ( - // - // )) - // } - //
); - } + } } - // Register scrapbook Docs.Prototypes.TemplateMap.set(DocumentType.SCRAPBOOK, { layout: { view: ScrapbookBox, dataField: 'items' }, -- cgit v1.2.3-70-g09d2