diff options
Diffstat (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')
| -rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 229 |
1 files changed, 94 insertions, 135 deletions
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 89aa53c35..3571dab1a 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1,14 +1,17 @@ -import { Bezier } from 'bezier-js'; import { Button, Colors, Type } from '@dash/components'; +import { Slider } from '@mui/material'; +import { Bezier } from 'bezier-js'; import { Property } from 'csstype'; import { action, computed, IReactionDisposer, makeObservable, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import { computedFn } from 'mobx-utils'; import * as React from 'react'; +import { AiOutlineSend } from 'react-icons/ai'; +import ReactLoading from 'react-loading'; import { ClientUtils, DashColor, lightOrDark, OmitKeys, returnFalse, returnZero, setupMoveUpEvents, UpdateIcon } from '../../../../ClientUtils'; import { DateField } from '../../../../fields/DateField'; import { Doc, DocListCast, Field, FieldType, Opt, StrListCast } from '../../../../fields/Doc'; -import { DocData, Height, Width } from '../../../../fields/DocSymbols'; +import { DocData, DocLayout, Height, Width } from '../../../../fields/DocSymbols'; import { Id } from '../../../../fields/FieldSymbols'; import { InkData, InkEraserTool, InkField, InkInkTool, InkTool, Segment } from '../../../../fields/InkField'; import { List } from '../../../../fields/List'; @@ -28,6 +31,7 @@ import { DragManager } from '../../../util/DragManager'; import { dropActionType } from '../../../util/DropActionTypes'; import { CompileScript } from '../../../util/Scripting'; import { ScriptingGlobals } from '../../../util/ScriptingGlobals'; +import { SettingsManager } from '../../../util/SettingsManager'; import { freeformScrollMode, SnappingManager } from '../../../util/SnappingManager'; import { Transform } from '../../../util/Transform'; import { undoable, UndoManager } from '../../../util/UndoManager'; @@ -37,26 +41,26 @@ import { InkingStroke } from '../../InkingStroke'; import { CollectionFreeFormDocumentView } from '../../nodes/CollectionFreeFormDocumentView'; import { SchemaCSVPopUp } from '../../nodes/DataVizBox/SchemaCSVPopUp'; import { + ActiveEraserWidth, ActiveInkArrowEnd, ActiveInkArrowStart, - ActiveInkDash, - ActiveEraserWidth, - ActiveInkFillColor, ActiveInkBezierApprox, ActiveInkColor, + ActiveInkDash, + ActiveInkFillColor, ActiveInkWidth, ActiveIsInkMask, DocumentView, SetActiveInkColor, SetActiveInkWidth, } from '../../nodes/DocumentView'; -import { FieldViewProps } from '../../nodes/FieldView'; import { FocusViewOptions } from '../../nodes/FocusViewOptions'; import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox'; import { OpenWhere } from '../../nodes/OpenWhere'; import { PinDocView, PinProps } from '../../PinFuncs'; -import { StickerPalette } from '../../smartdraw/StickerPalette'; +import { DrawingFillHandler } from '../../smartdraw/DrawingFillHandler'; import { DrawingOptions, SmartDrawHandler } from '../../smartdraw/SmartDrawHandler'; +import { StickerPalette } from '../../smartdraw/StickerPalette'; import { StyleProp } from '../../StyleProp'; import { CollectionSubView, SubCollectionViewProps } from '../CollectionSubView'; import { TreeViewType } from '../CollectionTreeViewType'; @@ -67,11 +71,6 @@ import { CollectionFreeFormPannableContents } from './CollectionFreeFormPannable import { CollectionFreeFormRemoteCursors } from './CollectionFreeFormRemoteCursors'; import './CollectionFreeFormView.scss'; import { MarqueeView } from './MarqueeView'; -import ReactLoading from 'react-loading'; -import { SettingsManager } from '../../../util/SettingsManager'; -import { Slider } from '@mui/material'; -import { AiOutlineSend } from 'react-icons/ai'; -import { DrawingFillHandler } from '../../smartdraw/DrawingFillHandler'; @observer class CollectionFreeFormOverlayView extends React.Component<{ elements: () => ViewDefResult[] }> { @@ -177,13 +176,13 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection return renderableEles; } @computed get fitContentsToBox() { - return (this._props.fitContentsToBox?.() || this.Document._freeform_fitContentsToBox) && !this.isAnnotationOverlay; + return (this._props.fitContentsToBox?.() || this.Document._freeform_fitContentsToBox || this.Document.freeform_isGroup) && !this.isAnnotationOverlay; } @computed get nativeWidth() { - return this._props.NativeWidth?.() || Doc.NativeWidth(this.Document, Cast(this.Document.resolvedDataDoc, Doc, null)); + return this._props.NativeWidth?.() || Doc.NativeWidth(this.Document); } @computed get nativeHeight() { - return this._props.NativeHeight?.() || Doc.NativeHeight(this.Document, Cast(this.Document.resolvedDataDoc, Doc, null)); + return this._props.NativeHeight?.() || Doc.NativeHeight(this.Document); } @computed get centeringShiftX(): number { return this._props.isAnnotationOverlay || this._props.originTopLeft ? 0 : this._props.PanelWidth() / 2 / this.nativeDimScaling; // shift so pan position is at center of window for non-overlay collections @@ -257,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._xMargin, this._props.xMargin ?? 0), + NumCast(this.layoutDoc._yMargin, this._props.yMargin ?? 0) ); const [width, height] = [r - x, b - y]; return { @@ -276,7 +275,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection onChildDoubleClickHandler = () => this._props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick); elementFunc = () => this._layoutElements; viewTransition = () => (this._panZoomTransition ? '' + this._panZoomTransition : undefined); - panZoomTransition = () => (this._panZoomTransition ? `transform ${this._panZoomTransition}ms` : Cast(this.layoutDoc._viewTransition, 'string', Cast(this.Document._viewTransition, 'string', null))); + panZoomTransition = () => (this._panZoomTransition ? `transform ${this._panZoomTransition}ms` : (Cast(this.layoutDoc._viewTransition, 'string', Cast(this.Document._viewTransition, 'string', null) ?? null) ?? '')); fitContentOnce = () => { const { cx, cy, scale } = this.contentBounds(); // prettier-ignore this.layoutDoc._freeform_panX = cx; @@ -285,9 +284,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection }; // freeform_panx, freeform_pany, freeform_scale all attempt to get values first from the layout controller, then from the layout/dataDoc (or template layout doc), and finally from the resolved template data document. // this search order, for example, allows icons of cropped images to find the panx/pany/zoom on the cropped image's data doc instead of the usual layout doc because the zoom/panX/panY define the cropped image - panX = () => this.fitContentBounds?.cx ?? NumCast(this.Document[this.panXFieldKey], NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.freeform_panX, 1)); - panY = () => this.fitContentBounds?.cy ?? NumCast(this.Document[this.panYFieldKey], NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.freeform_panY, 1)); - zoomScaling = () => this.fitContentBounds?.scale ?? NumCast(Doc.Layout(this.Document)[this.scaleFieldKey], 1); // , NumCast(DocCast(this.Document.resolvedDataDoc)?.[this.scaleFieldKey], 1)); + panX = () => this.fitContentBounds?.cx ?? NumCast(this.Document[this.panXFieldKey], NumCast(this.Document.freeform_panX, 1)); + panY = () => this.fitContentBounds?.cy ?? NumCast(this.Document[this.panYFieldKey], NumCast(this.Document.freeform_panY, 1)); + zoomScaling = () => this.fitContentBounds?.scale ?? NumCast(this.Document[this.scaleFieldKey], 1); // , NumCast(DocCast(this.Document.rootDocument)?.[this.scaleFieldKey], 1)); PanZoomCenterXf = () => (this._props.isAnnotationOverlay && this.zoomScaling() === 1 ? `` : `translate(${this.centeringShiftX}px, ${this.centeringShiftY}px) scale(${this.zoomScaling()}) translate(${-this.panX()}px, ${-this.panY()}px)`); ScreenToContentsXf = () => this.screenToFreeformContentsXf.copy(); getActiveDocuments = () => this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map(pair => pair.layout); @@ -343,7 +342,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection */ focusOnPoint = (options: FocusViewOptions) => { const { pointFocus, zoomTime, didMove } = options; - if (!this.Document.isGroup && pointFocus && !didMove) { + if (!this.Document.freeform_isGroup && pointFocus && !didMove) { const dfltScale = this.isAnnotationOverlay ? 1 : 0.25; if (this.layoutDoc[this.scaleFieldKey] !== dfltScale) { this.zoomSmoothlyAboutPt(this.screenToFreeformContentsXf.transformPoint(pointFocus.X, pointFocus.Y), dfltScale, zoomTime); @@ -381,7 +380,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection * @returns */ focus = (anchor: Doc, options: FocusViewOptions) => { - if (anchor.isGroup && !options.docTransform && options.contextPath?.length) { + if (Doc.IsFreeformGroup(anchor) && !options.docTransform && options.contextPath?.length) { // don't focus on group if there's a context path because we're about to focus on a group item // which will override any group focus. (If we allowed the group to focus, it would mark didMove even if there were no net movement) return undefined; @@ -389,14 +388,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection if (options.easeFunc) this.setPresEaseFunc(options.easeFunc); if (this._lightboxDoc) return undefined; if (options.pointFocus) return this.focusOnPoint(options); - const anchorInCollection = DocListCast(this.Document[this.fieldKey ?? Doc.LayoutFieldKey(this.Document)]).includes(anchor); + const anchorInCollection = DocListCast(this.Document[this.fieldKey ?? Doc.LayoutDataKey(this.Document)]).includes(anchor); const anchorInChildViews = this.childLayoutPairs.map(pair => pair.layout).includes(anchor); if (!anchorInCollection && !anchorInChildViews) { return undefined; } const xfToCollection = options?.docTransform ?? Transform.Identity(); const savedState = { panX: NumCast(this.Document[this.panXFieldKey]), panY: NumCast(this.Document[this.panYFieldKey]), scale: options?.willZoomCentered ? this.Document[this.scaleFieldKey] : undefined }; - const cantTransform = this.fitContentsToBox || ((this.Document.isGroup || this.layoutDoc._lockedTransform) && !DocumentView.LightboxDoc()); + const cantTransform = this.fitContentsToBox || ((this.Document.freeform_isGroup || this.layoutDoc._lockedTransform) && !DocumentView.LightboxDoc()); const { panX, panY, scale } = cantTransform || (!options.willPan && !options.willZoomCentered) ? savedState : this.calculatePanIntoView(anchor, xfToCollection, options?.willZoomCentered ? (options?.zoomScale ?? 0.75) : undefined); // focus on the document in the collection @@ -441,7 +440,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection const dropPos = this.Document._currentFrame !== undefined ? [NumCast(dvals.x), NumCast(dvals.y)] : [NumCast(refDoc.x), NumCast(refDoc.y)]; docDragData.droppedDocuments.forEach((d, i) => { - const layoutDoc = Doc.Layout(d); + const layoutDoc = d[DocLayout]; const delta = Utils.rotPt(x - dropPos[0], y - dropPos[1], fromScreenXf.Rotate); if (this.Document._currentFrame !== undefined) { CollectionFreeFormDocumentView.setupKeyframes([d], NumCast(this.Document._currentFrame), false); @@ -497,6 +496,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection }, 'link drop'); onInternalDrop = (e: Event, de: DragManager.DropEvent): boolean => { + 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); @@ -514,7 +514,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection this._downTime = Date.now(); const scrollMode = e.altKey ? (Doc.UserDoc().freeformScrollMode === freeformScrollMode.Pan ? freeformScrollMode.Zoom : freeformScrollMode.Pan) : Doc.UserDoc().freeformScrollMode; if (e.button === 0 && (!(e.ctrlKey && !e.metaKey) || scrollMode !== freeformScrollMode.Pan) && this._props.isContentActive()) { - if (!this.Document.isGroup) { + if (!this.Document.freeform_isGroup) { // group freeforms don't pan when dragged -- instead let the event go through to allow the group itself to drag // prettier-ignore const hit = this._clusters.handlePointerDown(this.screenToFreeformContentsXf.transformPoint(e.clientX, e.clientY)); @@ -561,7 +561,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection const { points } = ge; const B = this.screenToFreeformContentsXf.transformBounds(ge.bounds.left, ge.bounds.top, ge.bounds.width, ge.bounds.height); const inkDoc = this.createInkDoc(points, B); - if (Doc.ActiveInk === InkInkTool.Highlight) inkDoc[DocData].backgroundColor = 'transparent'; + if (Doc.ActiveInk === InkInkTool.Highlight) inkDoc.$backgroundColor = 'transparent'; if (Doc.ActiveInk === InkInkTool.Write) { this.unprocessedDocs.push(inkDoc); CollectionFreeFormView.collectionsWithUnprocessedInk.add(this); @@ -639,7 +639,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection const B = this.screenToFreeformContentsXf.transformBounds(bounds.left, bounds.top, bounds.width, bounds.height); const inkDoc = this.createInkDoc(points, B); ['color', 'fillColor', 'stroke_width', 'stroke_dash', 'stroke_bezier'].forEach(field => { - inkDoc[DocData][field] = stroke.dataDoc[field]; + inkDoc['$' + field] = stroke.dataDoc[field]; }); this.addDocument(inkDoc); }); @@ -1259,16 +1259,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection /** * Adds the created drawing to the freeform canvas and sets the metadata. */ - addDrawing = (doc: Doc, opts: DrawingOptions, gptRes: string, x?: number, y?: number) => { - const docData = doc[DocData]; - docData.title = opts.text; - docData._width = opts.size; - docData.ai_drawing_input = opts.text; - docData.ai_drawing_complexity = opts.complexity; - docData.ai_drawing_colored = opts.autoColor; - docData.ai_drawing_size = opts.size; - docData.ai_drawing_data = gptRes; - docData.ai = 'gpt'; + addDrawing = (doc: Doc, opts: DrawingOptions, x?: number, y?: number) => { + doc.$ai_prompt = opts.text; this._drawingContainer = doc; if (x !== undefined && y !== undefined) { [doc.x, doc.y] = this.screenToFreeformContentsXf.transformPoint(x, y); @@ -1279,7 +1271,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection @action zoom = (pointX: number, pointY: number, deltaY: number): void => { - if (this.Document.isGroup || this.Document[(this._props.viewField ?? '_') + 'freeform_noZoom']) return; + if (this.Document.freeform_isGroup || this.Document[(this._props.viewField ?? '_') + 'freeform_noZoom']) return; let deltaScale = deltaY > 0 ? 1 / 1.05 : 1.05; if (deltaScale < 0) deltaScale = -deltaScale; const [x, y] = this.screenToFreeformContentsXf.transformPoint(pointX, pointY); @@ -1306,11 +1298,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection @action onPointerWheel = (e: React.WheelEvent): void => { - if (this.Document.isGroup || !this.isContentActive()) return; // group style collections neither pan nor zoom + if (this.Document.freeform_isGroup || !this.isContentActive()) return; // group style collections neither pan nor zoom SnappingManager.TriggerUserPanned(); if (this.layoutDoc._Transform || this.Document.treeView_OutlineMode === TreeViewType.outline) return; e.stopPropagation(); - const docHeight = NumCast(this.Document[Doc.LayoutFieldKey(this.Document) + '_nativeHeight'], this.nativeHeight); + const docHeight = NumCast(this.Document[Doc.LayoutDataKey(this.Document) + '_nativeHeight'], this.nativeHeight); const scrollable = this.isAnnotationOverlay && NumCast(this.layoutDoc[this.scaleFieldKey], 1) === 1 && docHeight > this._props.PanelHeight() / this.nativeDimScaling + 1e-4; switch ( !e.ctrlKey && !e.shiftKey && !e.metaKey && !e.altKey ?// @@ -1430,7 +1422,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection @action zoomSmoothlyAboutPt(docpt: number[], scale: number, transitionTime = 500) { - if (this.Document.isGroup) return; + if (this.Document.freeform_isGroup) return; this.setPanZoomTransition(transitionTime); const screenXY = this.screenToFreeformContentsXf.inverse().transformPoint(docpt[0], docpt[1]); this.layoutDoc[this.scaleFieldKey] = scale; @@ -1442,9 +1434,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection } calculatePanIntoView = (doc: Doc, xf: Transform, scale?: number) => { - const layoutdoc = Doc.Layout(doc); const pt = xf.transformPoint(NumCast(doc.x), NumCast(doc.y)); - const pt2 = xf.transformPoint(NumCast(doc.x) + NumCast(layoutdoc._width), NumCast(doc.y) + NumCast(layoutdoc._height)); + const pt2 = xf.transformPoint(NumCast(doc.x) + NumCast(doc._width), NumCast(doc.y) + NumCast(doc._height)); const bounds = { left: pt[0], right: pt2[0], top: pt[1], bot: pt2[1], width: pt2[0] - pt[0], height: pt2[1] - pt[1] }; if (scale !== undefined) { @@ -1490,20 +1481,23 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection * @param below whether to place the new text Doc below or to the right of the one being typed into. * @returns whether the new text doc was created and added successfully */ - createTextDocCopy = undoable((fieldProps: FieldViewProps, below: boolean) => { - const textDoc = DocCast(fieldProps.Document.rootDocument, fieldProps.Document); - const newDoc = Doc.MakeCopy(textDoc, true); - newDoc[DocData][Doc.LayoutFieldKey(newDoc, fieldProps.LayoutTemplateString)] = undefined; // the copy should not copy the text contents of it source, just the render style - newDoc.x = NumCast(textDoc.x) + (below ? 0 : NumCast(textDoc._width) + 10); - newDoc.y = NumCast(textDoc.y) + (below ? NumCast(textDoc._height) + 10 : 0); - DocumentView.SetSelectOnLoad(newDoc); - return this.addDocument?.(newDoc); + createTextDocCopy = undoable((textBox: FormattedTextBox, below: boolean) => { + const textDoc = DocCast(textBox.Document); + if (textDoc) { + const newDoc = Doc.MakeCopy(textDoc, true); + newDoc['$' + Doc.LayoutDataKey(newDoc)] = undefined; // the copy should not copy the text contents of it source, just the render style + newDoc.x = NumCast(textDoc.x) + (below ? 0 : NumCast(textDoc._width) + 10); + newDoc.y = NumCast(textDoc.y) + (below ? NumCast(textDoc._height) + 10 : 0); + DocumentView.SetSelectOnLoad(newDoc); + return this.addDocument?.(newDoc); + } + return false; }, 'copied text note'); - onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => { - if ((e.metaKey || e.ctrlKey || e.altKey || fieldProps.Document._createDocOnCR) && ['Tab', 'Enter'].includes(e.key)) { + onKey = (e: KeyboardEvent, textBox: FormattedTextBox) => { + if ((e.metaKey || e.ctrlKey || e.altKey || textBox.Document._createDocOnCR) && ['Tab', 'Enter'].includes(e.key)) { e.stopPropagation?.(); - return this.createTextDocCopy(fieldProps, !e.altKey && e.key !== 'Tab'); + return this.createTextDocCopy(textBox, !e.altKey && e.key !== 'Tab'); } return undefined; }; @@ -1511,7 +1505,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection removeDocument = (docs: Doc | Doc[], annotationKey?: string | undefined) => { const ret = !!this._props.removeDocument?.(docs, annotationKey); // if this is a group and we have fewer than 2 Docs, then just promote what's left to our parent and get rid of the group. - if (ret && DocListCast(this.dataDoc[annotationKey ?? this.fieldKey]).length < 2 && this.Document.isGroup) { + if (ret && DocListCast(this.dataDoc[annotationKey ?? this.fieldKey]).length < 2 && this.Document.freeform_isGroup) { this.promoteCollection(); } return ret; @@ -1543,7 +1537,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection rootSelected={childData ? this.rootSelected : returnFalse} waitForDoubleClickToClick={this._props.waitForDoubleClickToClick} onClickScript={this.onChildClickHandler} - onKey={this.onKeyDown} + onKey={this.onKey} onDoubleClickScript={this.onChildDoubleClickHandler} bringToFront={this.bringToFront} ScreenToLocalTransform={childLayout.z ? this.ScreenToLocalBoxXf : this.ScreenToContentsXf} @@ -1554,7 +1548,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection searchFilterDocs={this.searchFilterDocs} isDocumentActive={childLayout.pointerEvents === 'none' ? returnFalse : this._props.childDocumentsActive?.() ? this._props.isDocumentActive : this.isContentActive} isContentActive={this.childContentsActive} - focus={this.Document.isGroup ? this.groupFocus : this.isAnnotationOverlay ? this._props.focus : this.focus} + focus={this.Document.freeform_isGroup ? this.groupFocus : this.isAnnotationOverlay ? this._props.focus : this.focus} addDocTab={this.addDocTab} addDocument={this._props.addDocument} removeDocument={this.removeDocument} @@ -1562,6 +1556,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection pinToPres={this._props.pinToPres} whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged} dragAction={(this.Document.childDragAction ?? this._props.childDragAction) as dropActionType} + rejectDrop={this._props.childRejectDrop} showTitle={this._props.childlayout_showTitle} dontRegisterView={this._props.dontRegisterView} pointerEvents={this.childPointerEventsFunc} @@ -1590,7 +1585,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection } case undefined: case OpenWhere.lightbox: - if (this.layoutDoc._isLightbox) { + if (this.dataDoc.$isLightbox) { this._lightboxDoc = docs[0]; return true; } @@ -1602,14 +1597,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection getCalculatedPositions(pair: { layout: Doc; data?: Doc }): PoolData { const random = (min: number, max: number, x: number, y: number) => /* min should not be equal to max */ min + (((Math.abs(x * y) * 9301 + 49297) % 233280) / 233280) * (max - min); const childDoc = pair.layout; - const childDocLayout = Doc.Layout(childDoc); const layoutFrameNumber = Cast(this.Document._currentFrame, 'number'); // frame number that container is at which determines layout frame values - const contentFrameNumber = Cast(childDocLayout._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed - const { z, zIndex } = childDoc; + const contentFrameNumber = Cast(childDoc._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { z, zIndex, stroke_isInkMask } = childDoc; const { backgroundColor, color } = contentFrameNumber === undefined ? { backgroundColor: undefined, color: undefined } : CollectionFreeFormDocumentView.getStringValues(childDoc, contentFrameNumber); const { x, y, autoDim, _width, _height, opacity, _rotation } = layoutFrameNumber === undefined // -1 for width/height means width/height should be PanelWidth/PanelHeight (prevents collectionfreeformdocumentview width/height from getting out of synch with panelWIdth/Height which causes detailView to re-render and lose focus because HTMLtag scaling gets set to a bad intermediate value) - ? { autoDim: 1, _width: Cast(childDoc._width, 'number'), _height: Cast(childDoc._height, 'number'), _rotation: Cast(childDocLayout._rotation, 'number'), x: childDoc.x, y: childDoc.y, opacity: this._props.childOpacity?.() } + ? { autoDim: 1, _width: Cast(childDoc._width, 'number'), _height: Cast(childDoc._height, 'number'), _rotation: Cast(childDoc._rotation, 'number'), x: childDoc.x, y: childDoc.y, opacity: this._props.childOpacity?.() } : CollectionFreeFormDocumentView.getValues(childDoc, layoutFrameNumber); // prettier-ignore const rotation = Cast(_rotation,'number', @@ -1627,8 +1622,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection zIndex: Cast(zIndex, 'number'), width: _width, height: _height, - transition: StrCast(childDocLayout.dataTransition), - showTags: BoolCast(childDocLayout.showTags) || BoolCast(this.Document.showChildTags) || BoolCast(this.Document._layout_showTags), + transition: StrCast(childDoc.dataTransition), + showTags: BoolCast(childDoc.showTags) || BoolCast(this.Document.showChildTags) || BoolCast(this.Document._layout_showTags), pointerEvents: Cast(childDoc.pointerEvents, 'string', null), pair, replica: '', @@ -1721,7 +1716,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection elements.push({ ele: this.getChildDocView(entry[1]), bounds: entry[1].opacity === 0 ? { payload: undefined, type: '', ...entry[1], width: 0, height: 0 } : { payload: undefined, type: '', ...entry[1] }, - inkMask: BoolCast(entry[1].pair.layout.stroke_isInkMask) ? NumCast(entry[1].pair.layout.opacity, 1) : -1, + inkMask: BoolCast(entry[1].pair.layout?.$stroke_isInkMask) ? NumCast(entry[1].pair.layout.opacity, 1) : -1, }) ); @@ -1738,15 +1733,16 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection }); PinDocView( anchor, - { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ? { ...pinProps.pinData, poslayoutview: pinProps.pinData.dataview } : {}), pannable: !this.Document.isGroup, collectionType: true, filters: true } }, + { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ? { ...pinProps.pinData, poslayoutview: pinProps.pinData.dataview } : {}), pannable: !this.Document.freeform_isGroup, collectionType: true, filters: true } }, this.Document ); if (addAsAnnotation) { - if (Cast(this.dataDoc[this._props.fieldKey + '_annotations'], listSpec(Doc), null) !== undefined) { - Cast(this.dataDoc[this._props.fieldKey + '_annotations'], listSpec(Doc), []).push(anchor); + const fieldKey = this._props.isAnnotationOverlay ? this._props.fieldKey : this._props.fieldKey + '_annotations'; + if (Cast(this.dataDoc[fieldKey], listSpec(Doc), null) !== undefined) { + Cast(this.dataDoc[fieldKey], listSpec(Doc), [])?.push(anchor); } else { - this.dataDoc[this._props.fieldKey + '_annotations'] = new List<Doc>([anchor]); + this.dataDoc[fieldKey] = new List<Doc>([anchor]); } } return anchor; @@ -1771,9 +1767,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection this._firstRender = false; this._disposers.groupBounds = reaction( () => { - if (this.Document.isGroup && this.childDocs.length === this.childDocList?.length) { + if (this.Document.freeform_isGroup && this.childDocs.length === this.childDocList?.length) { const clist = this.childDocs.map(cd => ({ x: NumCast(cd.x), y: NumCast(cd.y), width: NumCast(cd._width), height: NumCast(cd._height) })); - return aggregateBounds(clist, NumCast(this.layoutDoc._xPadding), NumCast(this.layoutDoc._yPadding)); + return aggregateBounds(clist, NumCast(this.layoutDoc._xMargin), NumCast(this.layoutDoc._yMargin)); } return undefined; }, @@ -1841,15 +1837,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection Object.values(this._disposers).forEach(disposer => disposer?.()); } - updateIcon = (usePanelDimensions?: boolean) => { + updateIcon = (/*usePanelDimensions?: boolean*/) => { const contentDiv = this._mainCont; return !contentDiv ? new Promise<void>(res => res()) : UpdateIcon( this.layoutDoc[Id] + '_icon_' + new Date().getTime(), contentDiv, - usePanelDimensions || true ? this._props.PanelWidth() : NumCast(this.layoutDoc._width), - usePanelDimensions || true ? this._props.PanelHeight() : NumCast(this.layoutDoc._height), + this._props.PanelWidth(), // usePanelDimensions ? this._props.PanelWidth() : NumCast(this.layoutDoc._width), + this._props.PanelHeight(), // usePanelDimensions ? this._props.PanelHeight() : NumCast(this.layoutDoc._height), this._props.PanelWidth(), this._props.PanelHeight(), 0, @@ -1933,8 +1929,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection const appearance = ContextMenu.Instance.findByDescription('Appearance...'); const appearanceItems = appearance?.subitems ?? []; - !this.Document.isGroup && appearanceItems.push({ description: 'Reset View', event: this.resetView, icon: 'compress-arrows-alt' }); - !this.Document.isGroup && appearanceItems.push({ description: 'Toggle Auto Reset View', event: this.toggleResetView, icon: 'compress-arrows-alt' }); + !this.Document.freeform_isGroup && appearanceItems.push({ description: 'Reset View', event: this.resetView, icon: 'compress-arrows-alt' }); + !this.Document.freeform_isGroup && appearanceItems.push({ description: 'Toggle Auto Reset View', event: this.toggleResetView, icon: 'compress-arrows-alt' }); if (this._props.setContentViewBox === emptyFunction) { !appearance && ContextMenu.Instance.addItem({ description: 'Appearance...', subitems: appearanceItems, icon: 'eye' }); return; @@ -1952,7 +1948,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection !Doc.noviceMode && appearanceItems.push({ description: `update icon`, event: () => this.updateIcon(), icon: 'compress-arrows-alt' }); this._props.renderDepth && appearanceItems.push({ description: 'Ungroup collection', event: this.promoteCollection, icon: 'table' }); - this.Document.isGroup && this.Document.transcription && appearanceItems.push({ description: 'Ink to text', event: this.transcribeStrokes, icon: 'font' }); + this.Document.freeform_isGroup && this.Document.transcription && appearanceItems.push({ description: 'Ink to text', event: this.transcribeStrokes, icon: 'font' }); !Doc.noviceMode ? appearanceItems.push({ description: 'Arrange contents in grid', event: this.layoutDocsInGrid, icon: 'table' }) : null; !Doc.noviceMode ? appearanceItems.push({ description: (this.Document._freeform_useClusters ? 'Hide' : 'Show') + ' Clusters', event: () => this._clusters.updateClusters(!this.Document._freeform_useClusters), icon: 'braille' }) : null; @@ -1975,7 +1971,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection event: action(() => { SmartDrawHandler.Instance.AddDrawing = this.addDrawing; SmartDrawHandler.Instance.RemoveDrawing = this.removeDrawing; - !SmartDrawHandler.Instance.ShowRegenerate ? SmartDrawHandler.Instance.displayRegenerate(this._downX, this._downY - 10) : SmartDrawHandler.Instance.hideRegenerate(); + !SmartDrawHandler.Instance.ShowRegenerate ? SmartDrawHandler.Instance.displayRegenerate(this._downX, this._downY - 10, NumCast(this.layoutDoc[this.scaleFieldKey])) : SmartDrawHandler.Instance.hideRegenerate(); }), icon: 'pen-to-square', }); @@ -1988,7 +1984,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection optionItems.push({ description: 'Use Background Color as Default', event: () => { - Cast(Doc.UserDoc().emptyCollection, Doc, null).backgroundColor = StrCast(this.layoutDoc.backgroundColor); + DocCast(Doc.UserDoc().emptyCollection) && (DocCast(Doc.UserDoc().emptyCollection)!.backgroundColor = StrCast(this.layoutDoc.backgroundColor)); }, icon: 'palette', }); @@ -2011,7 +2007,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection }; transcribeStrokes = undoable(() => { - if (this.Document.isGroup && this.Document.transcription) { + if (this.Document.freeform_isGroup && this.Document.transcription) { const text = StrCast(this.Document.transcription); const lines = text.split('\n'); const height = 30 + 15 * lines.length; @@ -2029,7 +2025,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection dragStarting = (snapToDraggedDoc: boolean = false, showGroupDragTarget: boolean = true, visited = new Set<Doc>()) => { if (visited.has(this.Document)) return; visited.add(this.Document); - showGroupDragTarget && (this.GroupChildDrag = BoolCast(this.Document.isGroup)); + showGroupDragTarget && (this.GroupChildDrag = BoolCast(this.Document.freeform_isGroup)); const activeDocs = this.getActiveDocuments(); const size = this.screenToFreeformContentsXf.transformDirection(this._props.PanelWidth(), this._props.PanelHeight()); const selRect = { left: this.panX() - size[0] / 2, top: this.panY() - size[1] / 2, width: size[0], height: size[1] }; @@ -2037,13 +2033,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection const isDocInView = (doc: Doc, rect: { left: number; top: number; width: number; height: number }) => intersectRect(docDims(doc), rect); const snappableDocs = activeDocs.filter(doc => doc.z === undefined && isDocInView(doc, selRect)); // first see if there are any foreground docs to snap to - activeDocs.filter(doc => doc.isGroup && SnappingManager.IsResizing !== doc[Id] && !DragManager.docsBeingDragged.includes(doc)).forEach(doc => DocumentView.getDocumentView(doc)?.ComponentView?.dragStarting?.(snapToDraggedDoc, false, visited)); + activeDocs + .filter(doc => Doc.IsFreeformGroup(doc) && SnappingManager.IsResizing !== doc[Id] && !DragManager.docsBeingDragged.includes(doc)) + .forEach(doc => DocumentView.getDocumentView(doc)?.ComponentView?.dragStarting?.(snapToDraggedDoc, false, visited)); const horizLines: number[] = []; const vertLines: number[] = []; const invXf = this.screenToFreeformContentsXf.inverse(); snappableDocs - .filter(doc => !doc.isGroup && (snapToDraggedDoc || (SnappingManager.IsResizing !== doc[Id] && !DragManager.docsBeingDragged.includes(doc)))) + .filter(doc => !Doc.IsFreeformGroup(doc) && (snapToDraggedDoc || (SnappingManager.IsResizing !== doc[Id] && !DragManager.docsBeingDragged.includes(doc)))) .forEach(doc => { const { left, top, width, height } = docDims(doc); const topLeftInScreen = invXf.transformPoint(left, top); @@ -2088,7 +2086,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection lightboxPanelHeight = () => Math.max(0, this._props.PanelHeight() - 30); lightboxScreenToLocal = () => this.ScreenToLocalBoxXf().translate(-15, -15); onPassiveWheel = (e: WheelEvent) => { - const docHeight = NumCast(this.Document[Doc.LayoutFieldKey(this.Document) + '_nativeHeight'], this.nativeHeight); + const docHeight = NumCast(this.Document[Doc.LayoutDataKey(this.Document) + '_nativeHeight'], this.nativeHeight); const scrollable = NumCast(this.layoutDoc[this.scaleFieldKey], 1) === 1 && docHeight > this._props.PanelHeight() / this.nativeDimScaling; this._props.isSelected() && !scrollable && e.preventDefault(); }; @@ -2111,12 +2109,12 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection </div> ); } - transitionFunc = () => (this._panZoomTransition ? `transform ${this._panZoomTransition}ms ${this._presEaseFunc}` : Cast(this.layoutDoc._viewTransition, 'string', Cast(this.Document._viewTransition, 'string', null))); + transitionFunc = () => (this._panZoomTransition ? `transform ${this._panZoomTransition}ms ${this._presEaseFunc}` : (Cast(this.layoutDoc._viewTransition, 'string', Cast(this.Document._viewTransition, 'string', null) ?? null) ?? '')); get pannableContents() { this.incrementalRender(); // needs to happen synchronously or freshly typed text documents will flash and miss their first characters return ( <CollectionFreeFormPannableContents - Document={this.Document} + Doc={this.Document} brushedView={this.brushedView} isAnnotationOverlay={this.isAnnotationOverlay} transform={this.PanZoomCenterXf} @@ -2134,7 +2132,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection <MarqueeView {...this._props} ref={this._marqueeViewRef} - ungroup={this.Document.isGroup ? this.promoteCollection : undefined} + Doc={this.Document} + ungroup={this.Document.freeform_isGroup ? this.promoteCollection : undefined} nudge={this.isAnnotationOverlay || this._props.renderDepth > 0 ? undefined : this.nudge} addDocTab={this.addDocTab} slowLoadDocuments={this.slowLoadDocuments} @@ -2150,7 +2149,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection isAnnotationOverlay={this.isAnnotationOverlay}> {this.layoutDoc._freeform_backgroundGrid ? this.backgroundGrid : null} {this.pannableContents} - {this._showAnimTimeline ? <Timeline ref={this._timelineRef} {...this._props} /> : null} + {this._showAnimTimeline ? <Timeline ref={this._timelineRef} {...this._props} Doc={this._props.Document} /> : null} </MarqueeView> ); } @@ -2179,9 +2178,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection placeholder={this._drawingFillInput || StrCast(this.Document.title) || 'Describe image'} type="text" value={this._drawingFillInput} - onChange={action(e => { - this._drawingFillInput = e.target.value; - })} + onChange={action(e => (this._drawingFillInput = e.target.value))} /> <div className="collectionFreeFormView-aiView-strength"> <span className="collectionFreeFormView-aiView-similarity">Similarity</span> @@ -2210,11 +2207,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection onClick={undoable( action(() => { this._drawingFillLoading = true; - DrawingFillHandler.drawingToImage(this.props.Document, this._fireflyRefStrength, this._drawingFillInput || StrCast(this.Document.title))?.then( - action(() => { - this._drawingFillLoading = false; - }) - ); + DrawingFillHandler.drawingToImage(this.props.Document, this._fireflyRefStrength, this._drawingFillInput || StrCast(this.Document.title))?.then(action(() => (this._drawingFillLoading = false))); }), 'create image' )} @@ -2222,37 +2215,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection </div> </div> </div> - <div className="collectionfreeformview-aiView-regenerate-container"> - <span className="collectionfreeformview-aiView-subtitle">Regenerate</span> - <div className="collectionfreeformview-aiView-regenerate"> - <input - className="collectionfreeformview-aiView-input" - aria-label="Edit instructions input" - type="text" - value={this._regenInput} - onChange={action(e => { - this._regenInput = e.target.value; - })} - placeholder="..under development.." - /> - <div className="collectionFreeFormView-aiView-regenBtn"> - <Button - text="Regenerate" - type={Type.SEC} - icon={this._regenLoading ? <ReactLoading type="spin" color={SettingsManager.userVariantColor} width={16} height={20} /> : <AiOutlineSend />} - iconPlacement="right" - // onClick={action(async () => { - // this._regenLoading = true; - // SmartDrawHandler.Instance.CreateDrawingDoc = this.createDrawingDoc; - // SmartDrawHandler.Instance.AddDrawing = this.addDrawing; - // SmartDrawHandler.Instance.RemoveDrawing = this.removeDrawing; - // await SmartDrawHandler.Instance.regenerate([this.Document], undefined, undefined, this._regenInput, true); - // this._regenLoading = false; - // })} - /> - </div> - </div> - </div> </div> ); }; @@ -2265,10 +2227,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection id={this._paintedId} ref={r => { this.createDashEventsTarget(r); - this._oldWheel?.removeEventListener('wheel', this.onPassiveWheel); - this._oldWheel = r; - // prevent wheel events from passivly propagating up through containers - r?.addEventListener('wheel', this.onPassiveWheel, { passive: false }); + this.fixWheelEvents(r, this._props.isContentActive, this.onPassiveWheel); r?.addEventListener('mouseleave', this.onMouseLeave); r?.addEventListener('mouseenter', this.onMouseEnter); }} @@ -2315,7 +2274,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection NativeWidth={returnZero} NativeHeight={returnZero} onClickScript={this.onChildClickHandler} - onKey={this.onKeyDown} + onKey={this.onKey} onDoubleClickScript={this.onChildDoubleClickHandler} childFilters={this.childDocFilters} childFiltersByRanges={this.childDocRangeFilters} @@ -2387,7 +2346,7 @@ ScriptingGlobals.add(function datavizFromSchema() { const keys = Cast(view.layoutDoc.schema_columnKeys, listSpec('string'))?.filter(key => key !== 'text'); if (!keys) return; - const children = DocListCast(view.Document[Doc.LayoutFieldKey(view.Document)]); + const children = DocListCast(view.Document[Doc.LayoutDataKey(view.Document)]); const csvRows = []; csvRows.push(keys.join(',')); for (let i = 0; i < children.length; i++) { |
