From 49b3c7cbe01f830a2b1d2c02452901fe360348d3 Mon Sep 17 00:00:00 2001 From: eleanor-park Date: Thu, 11 Jul 2024 11:50:01 -0400 Subject: updates --- src/fields/Doc.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/fields') diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index bc1abd26e..c86342870 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -276,7 +276,7 @@ export class Doc extends RefField { public static get MyPublishedDocs() { return DocListCast(Doc.ActiveDashboard?.myPublishedDocs).concat(DocListCast(DocCast(Doc.UserDoc().myPublishedDocs)?.data)); } // prettier-ignore public static get MyDashboards() { return DocCast(Doc.UserDoc().myDashboards); } // prettier-ignore public static get MyTemplates() { return DocCast(Doc.UserDoc().myTemplates); } // prettier-ignore - public static get MyDrawingAnnos() { return DocCast(Doc.UserDoc().myDrawingAnnos); } // prettier-ignore + public static get MyAnnos() { return DocCast(Doc.UserDoc().myAnnos); } // prettier-ignore public static get MyImports() { return DocCast(Doc.UserDoc().myImports); } // prettier-ignore public static get MyFilesystem() { return DocCast(Doc.UserDoc().myFilesystem); } // prettier-ignore public static get MyTools() { return DocCast(Doc.UserDoc().myTools); } // prettier-ignore @@ -1741,8 +1741,8 @@ ScriptingGlobals.add(function getEmbedding(doc: any) { return Doc.MakeEmbedding(doc); }); // eslint-disable-next-line prefer-arrow-callback -ScriptingGlobals.add(function getCopy(doc: any, copyProto: any) { - return doc.isTemplateDoc ? Doc.MakeDelegateWithProto(doc) : Doc.MakeCopy(doc, copyProto); +ScriptingGlobals.add(async function getCopy(doc: any, copyProto: any) { + return doc.isTemplateDoc ? (doc.isGroup ? (await Doc.MakeClone(doc)).clone : Doc.MakeDelegateWithProto(doc)) : Doc.MakeCopy(doc, copyProto); }); // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function copyField(field: any) { -- cgit v1.2.3-70-g09d2 From a763c4e80cec6aef5219a8c9fcfa2ddccbc10b0a Mon Sep 17 00:00:00 2001 From: eleanor-park Date: Wed, 24 Jul 2024 16:52:50 -0400 Subject: bug and render fixes --- package-lock.json | 26 +-- src/client/apis/gpt/GPT.ts | 2 +- src/client/util/CurrentUserUtils.ts | 26 +-- src/client/util/DropConverter.ts | 22 +++ src/client/util/bezierFit.ts | 13 +- src/client/views/DocumentButtonBar.tsx | 2 +- src/client/views/DocumentDecorations.tsx | 7 +- src/client/views/LightboxView.tsx | 20 +- src/client/views/MainView.tsx | 1 - .../views/collections/CollectionCarousel3DView.tsx | 2 +- .../views/collections/CollectionCarouselView.tsx | 11 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 10 +- .../collections/collectionFreeForm/MarqueeView.tsx | 13 +- src/client/views/smartdraw/AnnotationPalette.tsx | 203 +++++---------------- src/client/views/smartdraw/SmartDrawHandler.tsx | 9 +- src/fields/Doc.ts | 1 + 16 files changed, 145 insertions(+), 223 deletions(-) (limited to 'src/fields') diff --git a/package-lock.json b/package-lock.json index 663409ddb..70f53156c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13531,19 +13531,6 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/browndash-components/node_modules/npm/node_modules/node-gyp/node_modules/readable-stream": { - "version": "3.6.2", - "inBundle": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/browndash-components/node_modules/npm/node_modules/node-gyp/node_modules/signal-exit": { "version": "3.0.7", "inBundle": true, @@ -13989,6 +13976,19 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/browndash-components/node_modules/npm/node_modules/readable-stream": { + "version": "3.6.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/browndash-components/node_modules/npm/node_modules/retry": { "version": "0.12.0", "inBundle": true, diff --git a/src/client/apis/gpt/GPT.ts b/src/client/apis/gpt/GPT.ts index cbe2f0582..a780596fa 100644 --- a/src/client/apis/gpt/GPT.ts +++ b/src/client/apis/gpt/GPT.ts @@ -58,7 +58,7 @@ const callTypeMap: { [type: string]: GPTCallOpts } = { model: 'gpt-4o', maxTokens: 1024, temp: 0.5, - prompt: 'Given an item, a level of complexity from 1-10, and a size in pixels, generate a detailed and colored line drawing representation of it. Make sure every element has the stroke field filled out. More complex drawings will have much more detail and strokes. The drawing should be in SVG format with no additional text or comments. For path coordinates, make sure you format with a comma between numbers, like M100,200 C150,250 etc. The only supported commands are line, ellipse, circle, rect, and path with M, Q, C, and L so only use those.', + prompt: 'Given an item, a level of complexity from 1-10, and a size in pixels, generate a detailed and colored line drawing representation of it. Make sure every element has the stroke field filled out. More complex drawings will have much more detail and strokes. The drawing should be in SVG format with no additional text or comments. For path coordinates, make sure you format with a comma between numbers, like M100,200 C150,250 etc. The only supported commands are line, ellipse, circle, rect, polygon, and path with M, Q, C, and L so only use those.', }, }; diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 98a54d4d0..6b59828eb 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -165,7 +165,7 @@ export class CurrentUserUtils { static setupAnnoPalette(doc: Doc, field="myAnnos") { const reqdOpts:DocumentOptions = { - title: "Saved Annotations", _xMargin: 0, _layout_showTitle: "title", _chromeHidden: true, hidden: false, + title: "Saved Annotations", _xMargin: 0, _layout_showTitle: "title", hidden: false, _dragOnlyWithinContainer: true, layout_hideContextMenu: true, isSystem: true, _forceActive: true, _layout_autoHeight: true, _width: 500, _height: 300, _layout_fitWidth: true, _columnWidth: 35, ignoreClick: true, _lockedPosition: true, }; @@ -174,21 +174,14 @@ export class CurrentUserUtils { return DocUtils.AssignDocField(doc, field, (opts,items) => Docs.Create.MasonryDocument(items??[], opts), reqdOpts, DocListCast(savedAnnos?.data), reqdScripts); } - // static setupNoteTemplates(doc: Doc, field="template_notes") { - // const tempNotes = DocCast(doc[field]); - // const reqdTempOpts:DocumentOptions[] = [ - // { title: "Postit", backgroundColor: "yellow", icon: "sticky-note", _layout_showTitle: "title", layout_borderRounding: "5px"}, - // { title: "Idea", backgroundColor: "pink", icon: "lightbulb" , _layout_showTitle: "title"}, - // { title: "Topic", backgroundColor: "lightblue", icon: "book-open" , _layout_showTitle: "title"}]; - // const reqdNoteList = [...reqdTempOpts.map(opts => { - // const reqdOpts = {...opts, isSystem:true, width:200, layout_autoHeight: true, layout_fitWidth: true}; - // const noteTemp = tempNotes ? DocListCast(tempNotes.data).find(fdoc => fdoc.title === opts.title): undefined; - // return DocUtils.AssignOpts(noteTemp, reqdOpts) ?? MakeTemplate(Docs.Create.TextDocument("",reqdOpts)); - // }), ... DocListCast(tempNotes?.data).filter(note => !reqdTempOpts.find(reqd => reqd.title === note.title))]; - - // const reqdOpts:DocumentOptions = { title: "Note Layouts", _height: 75, isSystem: true }; - // // eslint-disable-next-line no-return-assign - // r + static setupLightboxDrawingPreviews(doc: Doc, field="myLightboxDrawings") { + const reqdOpts:DocumentOptions = { + title: "Preview", _layout_fitWidth: true, childLayoutFitWidth: true, + }; + const reqdScripts = {}; + const drawings = DocCast(doc[field]); + return DocUtils.AssignDocField(doc, field, (opts,items) => Docs.Create.CarouselDocument(items??[], opts), reqdOpts, DocListCast(drawings?.data), reqdScripts); + } // setup templates for different document types when they are iconified from Document Decorations static setupDefaultIconTemplates(doc: Doc, field="template_icons") { @@ -1015,6 +1008,7 @@ pie title Minerals in my tap water this.setupDockedButtons(doc); // the bottom bar of font icons this.setupLeftSidebarMenu(doc); // the left-side column of buttons that open their contents in a flyout panel on the left this.setupAnnoPalette(doc); + this.setupLightboxDrawingPreviews(doc); this.setupDocTemplates(doc); // sets up the template menu of templates // sthis.setupFieldInfos(doc); // sets up the collection of field info descriptions for each possible DocumentOption DocUtils.AssignDocField(doc, "globalScriptDatabase", () => Docs.Prototypes.MainScriptDocument(), {}); diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts index 0314af06b..3e26fefad 100644 --- a/src/client/util/DropConverter.ts +++ b/src/client/util/DropConverter.ts @@ -86,6 +86,28 @@ export function makeUserTemplateButton(doc: Doc) { dbox.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory)'); return dbox; } + +export function makeUserTemplateImage(doc: Doc, image: ImageField) { + const layoutDoc = doc; // doc.layout instanceof Doc && doc.layout.isTemplateForField ? doc.layout : doc; + if (layoutDoc.type !== DocumentType.FONTICON) { + !layoutDoc.isTemplateDoc && makeTemplate(layoutDoc); + } + layoutDoc.isTemplateDoc = true; + const dbox = Docs.Create.ImageDocument(image, { + _nativeWidth: 100, + _nativeHeight: 100, + _width: 100, + _height: 100, + title: StrCast(layoutDoc.title), + isSystem: false, + }); + dbox.title = ComputedField.MakeFunction('this.dragFactory.title'); + dbox.dragFactory = layoutDoc; + dbox.dropPropertiesToRemove = doc.dropPropertiesToRemove instanceof ObjectField ? ObjectField.MakeCopy(doc.dropPropertiesToRemove) : undefined; + dbox.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory)'); + return dbox; +} + export function convertDropDataToButtons(data: DragManager.DocumentDragData) { data?.draggedDocuments.forEach((doc, i) => { let dbox = doc; diff --git a/src/client/util/bezierFit.ts b/src/client/util/bezierFit.ts index 98a6feea0..6a4577506 100644 --- a/src/client/util/bezierFit.ts +++ b/src/client/util/bezierFit.ts @@ -694,7 +694,7 @@ export function SVGToBezier(name: SVGType, attributes: any): Point[] { const matches: RegExpMatchArray[] = Array.from( attributes.d.matchAll(/Q(-?\d+\.?\d*),(-?\d+\.?\d*) (-?\d+\.?\d*),(-?\d+\.?\d*)|C(-?\d+\.?\d*),(-?\d+\.?\d*) (-?\d+\.?\d*),(-?\d+\.?\d*) (-?\d+\.?\d*),(-?\d+\.?\d*)|L(-?\d+\.?\d*),(-?\d+\.?\d*)/g) ); - let lastPt: Point; + let lastPt: Point = { X: 0, Y: 0 }; matches.forEach(match => { if (match[0].startsWith('Q')) { coordList.push({ X: parseInt(match[1]), Y: parseInt(match[2]) }); @@ -726,6 +726,17 @@ export function SVGToBezier(name: SVGType, attributes: any): Point[] { } return coordList; case 'polygon': + const coords: RegExpMatchArray[] = Array.from(attributes.points.matchAll(/(-?\d+\.?\d*),(-?\d+\.?\d*)/g)); + let list: Point[] = []; + coords.forEach(coord => { + list.push({ X: parseInt(coord[1]), Y: parseInt(coord[2]) }); + list.push({ X: parseInt(coord[1]), Y: parseInt(coord[2]) }); + list.push({ X: parseInt(coord[1]), Y: parseInt(coord[2]) }); + list.push({ X: parseInt(coord[1]), Y: parseInt(coord[2]) }); + }); + const firstPts = list.splice(0, 2); + list = list.concat(firstPts); + return list; default: return []; } diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index eb0b00472..6950bec14 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -249,7 +249,7 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => ( saveAnno = action((targetDoc: Doc) => { // targetDoc.savedAsAnno = true; this._annoSaved = true; - AnnotationPalette.Instance.addToPalette(targetDoc); + // AnnotationPalette.addToPalette(targetDoc); }); @computed diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index a229b15db..ae4f5b98b 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -229,6 +229,11 @@ export class DocumentDecorations extends ObservableReactComponent ); } -} \ No newline at end of file +} diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx index 4fcb7ec9c..b0d469674 100644 --- a/src/client/views/LightboxView.tsx +++ b/src/client/views/LightboxView.tsx @@ -12,7 +12,7 @@ import { emptyFunction } from '../../Utils'; import { CreateLinkToActiveAudio, Doc, DocListCast, FieldResult, Opt } from '../../fields/Doc'; import { Id } from '../../fields/FieldSymbols'; import { InkTool } from '../../fields/InkField'; -import { Cast, NumCast, toList } from '../../fields/Types'; +import { Cast, DocCast, NumCast, toList } from '../../fields/Types'; import { SnappingManager } from '../util/SnappingManager'; import { Transform } from '../util/Transform'; import { GestureOverlay } from './GestureOverlay'; @@ -43,13 +43,11 @@ export class LightboxView extends ObservableReactComponent { * @returns true if a DocumentView is descendant of the lightbox view */ public static Contains(view?: DocumentView) { - return true; - } - // return ( - // (view && LightboxView.Instance?._docView && (view.containerViewPath?.() ?? []).concat(view).includes(LightboxView.Instance?._docView)) || - // view?.Document === AnnotationPalette.Instance.FreeformCanvas || - // view?.Document.embedContainer === AnnotationPalette.Instance.DrawingCarousel - // ); } // prettier-ignore + return ( + (view && LightboxView.Instance?._docView && (view.containerViewPath?.() ?? []).concat(view).includes(LightboxView.Instance?._docView)) || + (view && LightboxView.Instance?._annoPaletteView?.Contains(view)) + ); + } // prettier-ignore public static LightboxDoc = () => LightboxView.Instance?._doc; // eslint-disable-next-line no-use-before-define static Instance: LightboxView; @@ -69,6 +67,7 @@ export class LightboxView extends ObservableReactComponent { @observable private _docTarget: Opt = undefined; @observable private _docView: Opt = undefined; @observable private _showPalette: boolean = false; + private _annoPaletteView: AnnotationPalette | null = null; @computed get leftBorder() { return Math.min(this._props.PanelWidth / 4, this._props.maxBorder[0]); } // prettier-ignore @computed get topBorder() { return Math.min(this._props.PanelHeight / 4, this._props.maxBorder[1]); } // prettier-ignore @@ -114,7 +113,6 @@ export class LightboxView extends ObservableReactComponent { this._history = []; Doc.ActiveTool = InkTool.None; SnappingManager.SetExploreMode(false); - AnnotationPalette.Instance.displayPalette(false); this._showPalette = false; } DocumentView.DeselectAll(); @@ -217,8 +215,7 @@ export class LightboxView extends ObservableReactComponent { }; togglePalette = () => { this._showPalette = !this._showPalette; - AnnotationPalette.Instance.displayPalette(this._showPalette); - if (this._showPalette === false) AnnotationPalette.Instance.resetPalette(true); + // if (this._showPalette === false) AnnotationPalette.Instance.resetPalette(true); }; togglePen = () => { Doc.ActiveTool = Doc.ActiveTool === InkTool.Pen ? InkTool.None : InkTool.Pen; @@ -324,6 +321,7 @@ export class LightboxView extends ObservableReactComponent { + {this._showPalette && (this._annoPaletteView = r)} Document={DocCast(Doc.UserDoc().myLightboxDrawings)} />} {this.renderNavBtn(0, undefined, this._props.PanelHeight / 2 - 12.5, 'chevron-left', this._doc && this._history.length, this.previous)} {this.renderNavBtn( this._props.PanelWidth - Math.min(this._props.PanelWidth / 4, this._props.maxBorder[0]), diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index fd1af7547..757fc3fe2 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -1119,7 +1119,6 @@ export class MainView extends ObservableReactComponent<{}> { - ); } diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx index 27c85533f..241087fd2 100644 --- a/src/client/views/collections/CollectionCarousel3DView.tsx +++ b/src/client/views/collections/CollectionCarousel3DView.tsx @@ -78,7 +78,7 @@ export class CollectionCarousel3DView extends CollectionSubView() { // suppressSetHeight={true} NativeWidth={returnZero} NativeHeight={returnZero} - fitWidth={undefined} + fitWidth={this._props.childLayoutFitWidth} onDoubleClickScript={this.onChildDoubleClick} renderDepth={this._props.renderDepth + 1} LayoutTemplate={this._props.childLayoutTemplate} diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx index 2adad68e0..8b083de15 100644 --- a/src/client/views/collections/CollectionCarouselView.tsx +++ b/src/client/views/collections/CollectionCarouselView.tsx @@ -8,7 +8,7 @@ import * as React from 'react'; import { StopEvent, returnFalse, returnOne, returnTrue, returnZero } from '../../../ClientUtils'; import { emptyFunction } from '../../../Utils'; import { Doc, Opt } from '../../../fields/Doc'; -import { DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; +import { Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { DocumentType } from '../../documents/DocumentTypes'; import { DragManager } from '../../util/DragManager'; import { ContextMenu } from '../ContextMenu'; @@ -144,6 +144,7 @@ export class CollectionCarouselView extends CollectionSubView() { revealItems.push({description: 'Quiz Cards', event: () => {this.layoutDoc.filterOp = cardMode.QUIZ;}, icon: 'pencil',}); // prettier-ignore !revealOptions && cm.addItem({ description: 'Filter Flashcards', addDivider: false, noexpand: true, subitems: revealItems, icon: 'layer-group' }); }; + childFitWidth = (doc: Doc) => Cast(this.Document.childLayoutFitWidth, 'boolean', this._props.childLayoutFitWidth?.(doc) ?? Cast(doc.layout_fitWidth, 'boolean', null)); @computed get content() { const index = NumCast(this.layoutDoc._carousel_index); const curDoc = this.carouselItems?.[index]; @@ -154,10 +155,11 @@ export class CollectionCarouselView extends CollectionSubView() {
{!carouselShowsCaptions ? null : ( diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index d0f65866b..f2b94fabc 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1312,7 +1312,7 @@ export class CollectionFreeFormView extends CollectionSubView { this._batch = UndoManager.StartBatch('regenerateDrawing'); if (doc) { - const docData = DocCast(doc[DocData]); + const docData = doc[DocData]; const children = DocListCast(docData.data); + this._props.removeDocument?.(doc); this._props.removeDocument?.(children); - // this._props.removeDocument?.(doc); } else { - this._props.removeDocument?.(this._drawing); - // if (this._drawingContainer) this._props.removeDocument?.(this._drawingContainer); + if (this._drawingContainer) this._props.removeDocument?.(this._drawingContainer); } + this._drawing = []; }; @action diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 5aff3ed6f..8560323c9 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -363,7 +363,7 @@ export class MarqueeView extends ObservableReactComponent, options: DocumentOptions, id?: string) => Doc>, makeGroup: Opt) => { + public static getCollection = action((selected: Doc[], creator: Opt<(documents: Array, options: DocumentOptions, id?: string) => Doc>, makeGroup: Opt, bounds: MarqueeViewBounds) => { const newCollection = creator ? creator(selected, { title: 'nested stack' }) : ((doc: Doc) => { @@ -375,14 +375,13 @@ export class MarqueeView extends ObservableReactComponent Doc.SetContainer(d, newCollection)); - this.hideMarquee(); return newCollection; }); @@ -419,7 +418,7 @@ export class MarqueeView extends ObservableReactComponent { +export class AnnotationPalette extends ObservableReactComponent<{ Document: Doc }> { static Instance: AnnotationPalette; - @observable private _display: boolean = false; @observable private _paletteMode: 'create' | 'view' = 'view'; @observable private _userInput: string = ''; @observable private _isLoading: boolean = false; @observable private _canInteract: boolean = true; @observable private _showRegenerate: boolean = false; - @observable private _freeFormCanvas = Docs.Create.FreeformDocument([], {}); - @observable private _drawingCarousel = Docs.Create.CarouselDocument([], {}); - @observable private _drawings: Doc[] = []; - private _drawing: Doc[] = []; + @observable private _docView: DocumentView | null = null; + @observable private _docCarouselView: DocumentView | null = null; @observable private _opts: DrawingOptions = { text: '', complexity: 5, size: 200, autoColor: true, x: 0, y: 0 }; private _gptRes: string[] = []; @@ -55,33 +53,16 @@ export class AnnotationPalette extends ObservableReactComponent<{}> { return FieldView.LayoutString(AnnotationPalette, fieldKey); } - public get FreeformCanvas() { - return this._freeFormCanvas; - } - - public get DrawingCarousel() { - return this._drawingCarousel; - } - - // componentDidUpdate(prevProps: Readonly<{}>) { - // const docView = DocumentView.getDocumentView(this._freeFormCanvas); - // const componentView = docView?.ComponentView as CollectionFreeFormView; - // if (componentView) { - // componentView.fitContentOnce(); - // } - // this._freeFormCanvas._freeform_fitContentsToBox = false; - // } + Contains = (view: DocumentView) => { + return (this._docView && (view.containerViewPath?.() ?? []).concat(view).includes(this._docView)) || (this._docCarouselView && (view.containerViewPath?.() ?? []).concat(view).includes(this._docCarouselView)); + }; return170 = () => 170; @action handleKeyPress = async (event: React.KeyboardEvent) => { if (event.key === 'Enter') { - // if (this._showRegenerate) { - // this.regenerate(); - // } else { await this.generateDrawing(); - // } } }; @@ -117,42 +98,34 @@ export class AnnotationPalette extends ObservableReactComponent<{}> { this.setDetail(5); this.setColor(true); this.setSize(200); - this._freeFormCanvas = Docs.Create.FreeformDocument([], {}); - this._drawingCarousel = Docs.Create.CarouselDocument([], {}); this._showRegenerate = false; this._canInteract = true; - this._drawing = []; - this._drawings = []; this._opts = { text: '', complexity: 5, size: 200, autoColor: true, x: 0, y: 0 }; this._gptRes = []; }; - addToPalette = async (doc: Doc) => { - if (!doc.savedAsAnno) { - const clone = await Doc.MakeClone(doc); - clone.clone.title = doc.title; - const image = await this.getIcon(doc); - if (image) { - const imageDoc = Docs.Create.ImageDocument(image); - Doc.AddDocToList(Doc.MyAnnos, 'data', imageDoc); - } - doc.savedAsAnno = true; - // const templateBtn = makeUserTemplateButton(clone.clone); - // Doc.AddDocToList(Doc.MyAnnos, 'data', templateBtn); - // this.resetPalette(true); - } + public static addToPalette = async (doc: Doc) => { + // if (!doc.savedAsAnno) { + // const clone = await Doc.MakeClone(doc); + // clone.clone.title = doc.title; + // const image = (await this.getIcon(doc))?.[Copy](); + // if (image) { + // const imageTemplate = makeUserTemplateImage(clone.clone, image); + // Doc.AddDocToList(Doc.MyAnnos, 'data', imageTemplate); + // doc.savedAsAnno = true; + // } + // this.resetPalette(true); + // } }; - @action - displayPalette = (display: boolean) => { - this._display = display; - }; + // @action + // displayPalette = (display: boolean) => { + // this._display = display; + // }; @undoBatch generateDrawing = action(async () => { this._isLoading = true; - this._drawings = []; - this._drawing = []; for (var i = 0; i < 3; i++) { try { SmartDrawHandler.Instance._addFunc = this.createDrawing; @@ -177,8 +150,8 @@ export class AnnotationPalette extends ObservableReactComponent<{}> { createDrawing = (strokeList: [InkData, string, string][], opts: DrawingOptions, gptRes: string) => { this._opts = opts; this._gptRes.push(gptRes); - this._drawing = []; - // const childDocs = DocListCast(this._drawing1[DocData].data); + const drawing: Doc[] = []; + strokeList.forEach((stroke: [InkData, string, string]) => { const bounds = InkField.getBounds(stroke[0]); const inkWidth = ActiveInkWidth(); @@ -199,51 +172,27 @@ export class AnnotationPalette extends ObservableReactComponent<{}> { ActiveDash(), ActiveIsInkMask() ); - this._drawing.push(inkDoc); - // childDocs.push(inkDoc); + drawing.push(inkDoc); }); - const cv = DocumentView.getDocumentView(this._freeFormCanvas)?.ComponentView as CollectionFreeFormView; - const collection = cv._marqueeViewRef.current?.collection(undefined, true, this._drawing); + const collection = MarqueeView.getCollection(drawing, undefined, true, { left: 1, top: 1, width: 1, height: 1 }); if (collection) { - this._drawings.push(collection); - cv.fitContentOnce(); + Doc.AddDocToList(this._props.Document, 'data', collection); } - this._drawingCarousel = Docs.Create.CarouselDocument(this._drawings, { childLayoutFitWidth: true, _layout_fitWidth: true, _freeform_fitContentsToBox: true }); - this._freeFormCanvas = Docs.Create.FreeformDocument(this._drawing, { _freeform_fitContentsToBox: true }); }; saveDrawing = async () => { - // const cv = DocumentView.getDocumentView(this._freeFormCanvas)?.ComponentView as CollectionFreeFormView; - // if (cv) { - // const collection = cv._marqueeViewRef.current?.collection(undefined, true, this._drawing); - const cIndex: number = this._drawingCarousel.carousel_index as number; - const focusedDrawing = this._drawings[cIndex]; - - const docData = focusedDrawing[DocData]; - docData.title = this._opts.text.match(/^(.*?)~~~.*$/)?.[1] || this._opts.text; - docData.drawingInput = this._opts.text; - docData.drawingComplexity = this._opts.complexity; - docData.drawingColored = this._opts.autoColor; - docData.drawingSize = this._opts.size; - docData.drawingData = this._gptRes[cIndex]; - docData.width = this._opts.size; - // const image = await this.getIcon(collection); - await this.addToPalette(focusedDrawing); - - // if (collection) { - // const docData = collection[DocData]; - // docData.title = this._opts.text.match(/^(.*?)~~~.*$/)?.[1] || this._opts.text; - // docData.drawingInput = this._opts.text; - // docData.drawingComplexity = this._opts.complexity; - // docData.drawingColored = this._opts.autoColor; - // docData.drawingSize = this._opts.size; - // docData.drawingData = this._gptRes; - // docData.width = this._opts.size; - // // const image = await this.getIcon(collection); - // await this.addToPalette(collection); - // } - // } + // const cIndex: number = this._drawingCarousel.carousel_index as number; + // const focusedDrawing = this._drawings[cIndex]; + // const docData = focusedDrawing[DocData]; + // docData.title = this._opts.text.match(/^(.*?)~~~.*$/)?.[1] || this._opts.text; + // docData.drawingInput = this._opts.text; + // docData.drawingComplexity = this._opts.complexity; + // docData.drawingColored = this._opts.autoColor; + // docData.drawingSize = this._opts.size; + // docData.drawingData = this._gptRes[cIndex]; + // docData.width = this._opts.size; + // await AnnotationPalette.addToPalette(focusedDrawing); }; async getIcon(group: Doc) { @@ -255,36 +204,13 @@ export class AnnotationPalette extends ObservableReactComponent<{}> { return undefined; } - @computed get drawingCreator() { - return ( - - ); - } - render() { - return !this._display ? null : ( + return (
{this._paletteMode === 'view' && ( <> (this._docView = r)} Document={Doc.MyAnnos} addDocument={undefined} addDocTab={DocumentViewInternal.addDocTabFunc} @@ -303,34 +229,19 @@ export class AnnotationPalette extends ObservableReactComponent<{}> { childFiltersByRanges={returnEmptyFilter} searchFilterDocs={returnEmptyDoclist} /> -
-
- -
(this._docCarouselView = r)} + Document={this._props.Document} addDocument={undefined} addDocTab={DocumentViewInternal.addDocTabFunc} pinToPres={DocumentView.PinDoc} diff --git a/src/client/views/smartdraw/SmartDrawHandler.tsx b/src/client/views/smartdraw/SmartDrawHandler.tsx index 489f6f92b..ca65ea388 100644 --- a/src/client/views/smartdraw/SmartDrawHandler.tsx +++ b/src/client/views/smartdraw/SmartDrawHandler.tsx @@ -99,7 +99,7 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> { @action displayRegenerate = (x: number, y: number, addFunc: (strokeData: [InkData, string, string][], opts: DrawingOptions, gptRes: string, containerDoc?: Doc) => void, deleteFunc: (doc?: Doc) => void) => { - this._selectedDoc = DocumentView.SelectedDocs().lastElement(); + this._selectedDoc = DocumentView.SelectedDocs()?.lastElement(); const docData = this._selectedDoc[DocData]; this._addFunc = addFunc; this._deleteFunc = deleteFunc; @@ -123,7 +123,6 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> { this._size = 350; this._autoColor = true; Doc.ActiveTool = InkTool.None; - this._lastInput = { text: '', complexity: 5, size: 350, autoColor: true, x: 0, y: 0 }; }; @action @@ -154,8 +153,6 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> { } else { this._showOptions = false; await this.drawWithGPT({ X: this._pageX, Y: this._pageY }, this._userInput, this._complexity, this._size, this._autoColor); - this.hideSmartDrawHandler(); - this._showRegenerate = true; } this._isLoading = false; this._canInteract = true; @@ -176,6 +173,8 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> { console.log(res); const strokeData = await this.parseResponse(res, startPt, false, autoColor); this._errorOccurredOnce = false; + this.hideSmartDrawHandler(); + this._showRegenerate = true; return strokeData; } catch (err) { if (this._errorOccurredOnce) { @@ -239,7 +238,7 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> { }); if (regenerate) { if (this._deleteFunc !== unimplementedFunction) this._deleteFunc(this._selectedDoc); - this._addFunc(strokeData, this._lastInput, svg[0], this._selectedDoc); + this._addFunc(strokeData, this._lastInput, svg[0]); } else { this._addFunc(strokeData, this._lastInput, svg[0]); } diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index c86342870..c3e96ab49 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -277,6 +277,7 @@ export class Doc extends RefField { public static get MyDashboards() { return DocCast(Doc.UserDoc().myDashboards); } // prettier-ignore public static get MyTemplates() { return DocCast(Doc.UserDoc().myTemplates); } // prettier-ignore public static get MyAnnos() { return DocCast(Doc.UserDoc().myAnnos); } // prettier-ignore + public static get MyLightboxDrawings() { return DocCast(Doc.UserDoc().myLightboxDrawings); } // prettier-ignore public static get MyImports() { return DocCast(Doc.UserDoc().myImports); } // prettier-ignore public static get MyFilesystem() { return DocCast(Doc.UserDoc().myFilesystem); } // prettier-ignore public static get MyTools() { return DocCast(Doc.UserDoc().myTools); } // prettier-ignore -- cgit v1.2.3-70-g09d2 From 647b66c965f5896d784de0f321d31cc712937e1c Mon Sep 17 00:00:00 2001 From: eleanor-park Date: Fri, 26 Jul 2024 01:23:06 -0400 Subject: Revert "Merge branch 'master' into eleanor-gptdraw" This reverts commit decbefe23a1da35c838222bafe8a2c029c6ea794, reversing changes made to 8ca26551622d36b7856f5c1865498fa9e5d888b5. --- package-lock.json | 92 +++++-- package.json | 3 +- src/ClientUtils.ts | 18 +- src/client/apis/gpt/GPT.ts | 5 +- src/client/util/CurrentUserUtils.ts | 4 +- src/client/util/DocumentManager.ts | 24 +- src/client/util/SettingsManager.tsx | 4 +- src/client/util/bezierFit.ts | 16 ++ src/client/views/GestureOverlay.tsx | 28 +-- src/client/views/LightboxView.scss | 24 +- src/client/views/LightboxView.tsx | 1 + src/client/views/MainView.tsx | 4 + src/client/views/MarqueeAnnotator.tsx | 146 +++++++++++ .../views/collections/CollectionCarousel3DView.tsx | 1 - .../views/collections/CollectionDockingView.tsx | 2 +- src/client/views/collections/CollectionMenu.scss | 2 +- .../collections/CollectionStackedTimeline.tsx | 2 +- .../views/collections/CollectionTreeView.tsx | 4 + src/client/views/collections/TreeView.tsx | 10 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 265 +++++++++++--------- .../collectionFreeForm/ImageLabelHandler.scss | 14 ++ .../collections/collectionFreeForm/MarqueeView.tsx | 2 + .../collectionSchema/CollectionSchemaView.tsx | 1 - src/client/views/global/globalScripts.ts | 4 +- .../views/nodes/CollectionFreeFormDocumentView.tsx | 2 +- .../views/nodes/DataVizBox/components/TableBox.tsx | 1 - src/client/views/nodes/DocumentView.tsx | 43 ++-- src/client/views/nodes/LabelBigText.js | 270 +++++++++++++++++++++ src/client/views/nodes/LabelBox.tsx | 223 ++++++++++------- src/client/views/nodes/PDFBox.scss | 16 +- src/client/views/nodes/PDFBox.tsx | 37 ++- src/client/views/nodes/WebBox.tsx | 1 - .../views/nodes/formattedText/FormattedTextBox.tsx | 12 +- src/client/views/pdf/AnchorMenu.tsx | 14 ++ src/client/views/pdf/Annotation.tsx | 1 - src/client/views/pdf/PDFViewer.tsx | 10 +- src/fields/Doc.ts | 49 +++- src/fields/InkField.ts | 1 + 38 files changed, 1032 insertions(+), 324 deletions(-) create mode 100644 src/client/views/nodes/LabelBigText.js (limited to 'src/fields') diff --git a/package-lock.json b/package-lock.json index be3fac8dc..70f53156c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -49,7 +49,6 @@ "@types/pdf-parse": "^1.1.4", "@types/reveal": "^4.2.0", "@types/supercluster": "^7.1.3", - "@types/textfit": "^2.4.4", "@types/web": "^0.0.151", "@types/webpack-hot-middleware": "^2.25.9", "@typescript-eslint/parser": "^7.8.0", @@ -221,8 +220,8 @@ "stream-browserify": "^3.0.0", "styled-components": "^6.1.1", "supercluster": "^8.0.1", + "svgson": "^5.3.1", "textarea-caret": "^3.1.0", - "textfit": "^2.4.0", "tough-cookie": "^4.1.3", "tslint": "^6.1.3", "tslint-loader": "^3.5.4", @@ -9414,6 +9413,7 @@ "version": "3.5.30", "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.30.tgz", "integrity": "sha512-nbWKkkyb919DOUxjmRVk8vwtDb0/k8FKncmUKFi+NY+QXqWltooxTrswvz4LspQwxvLdvzBN1TImr6cw3aQx2A==", + "dev": true, "dependencies": { "@types/sizzle": "*" } @@ -9817,7 +9817,8 @@ "node_modules/@types/sizzle": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.8.tgz", - "integrity": "sha512-0vWLNK2D5MT9dg0iOo8GlKguPAU02QjmZitPEsXRuJXU/OGIOt9vT9Fc26wtYuavLxtO45v9PGleoL9Z0k1LHg==" + "integrity": "sha512-0vWLNK2D5MT9dg0iOo8GlKguPAU02QjmZitPEsXRuJXU/OGIOt9vT9Fc26wtYuavLxtO45v9PGleoL9Z0k1LHg==", + "dev": true }, "node_modules/@types/sockjs": { "version": "0.3.36", @@ -9853,14 +9854,6 @@ "@types/geojson": "*" } }, - "node_modules/@types/textfit": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/@types/textfit/-/textfit-2.4.4.tgz", - "integrity": "sha512-AYlNcJ5j/WspQfbHIhoF0Wo63F5+REnX/VPFSH5unUUuwRcr6IoXxZki3vYhG4DRVUQe51AsFYyRxml5u+qaAg==", - "dependencies": { - "@types/jquery": "*" - } - }, "node_modules/@types/tough-cookie": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", @@ -16778,6 +16771,29 @@ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" }, + "node_modules/deep-rename-keys": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/deep-rename-keys/-/deep-rename-keys-0.2.1.tgz", + "integrity": "sha512-RHd9ABw4Fvk+gYDWqwOftG849x0bYOySl/RgX0tLI9i27ZIeSO91mLZJEp7oPHOMFqHvpgu21YptmDt0FYD/0A==", + "dependencies": { + "kind-of": "^3.0.2", + "rename-keys": "^1.1.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/deep-rename-keys/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/deepmerge": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", @@ -22596,6 +22612,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, "node_modules/is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -37159,6 +37180,14 @@ "@types/unist": "*" } }, + "node_modules/rename-keys": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/rename-keys/-/rename-keys-1.2.0.tgz", + "integrity": "sha512-U7XpAktpbSgHTRSNRrjKSrjYkZKuhUukfoBlXWXUExCAqhzh1TU3BDRAfJmarcl5voKS+pbKU9MvyLWKZ4UEEg==", + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/renderkid": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", @@ -39250,6 +39279,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/svgson": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/svgson/-/svgson-5.3.1.tgz", + "integrity": "sha512-qdPgvUNWb40gWktBJnbJRelWcPzkLed/ShhnRsjbayXz8OtdPOzbil9jtiZdrYvSDumAz/VNQr6JaNfPx/gvPA==", + "dependencies": { + "deep-rename-keys": "^0.2.1", + "xml-reader": "2.4.3" + } + }, "node_modules/symbol-tree": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", @@ -39529,11 +39567,6 @@ "resolved": "https://registry.npmjs.org/textarea-caret/-/textarea-caret-3.1.0.tgz", "integrity": "sha512-cXAvzO9pP5CGa6NKx0WYHl+8CHKZs8byMkt3PCJBCmq2a34YA9pO1NrQET5pzeqnBjBdToF5No4rrmkDUgQC2Q==" }, - "node_modules/textfit": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/textfit/-/textfit-2.4.0.tgz", - "integrity": "sha512-/x4aoY5+/tJmu+iwpBH1yw75TFp86M6X15SvaaY/Eep7YySQYtqdOifEtfvVyMwzl7SZ+G4RQw00FD9g5R6i1Q==" - }, "node_modules/thingies": { "version": "1.21.0", "resolved": "https://registry.npmjs.org/thingies/-/thingies-1.21.0.tgz", @@ -42119,6 +42152,19 @@ "xtend": "^4.0.0" } }, + "node_modules/xml-lexer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/xml-lexer/-/xml-lexer-0.2.2.tgz", + "integrity": "sha512-G0i98epIwiUEiKmMcavmVdhtymW+pCAohMRgybyIME9ygfVu8QheIi+YoQh3ngiThsT0SQzJT4R0sKDEv8Ou0w==", + "dependencies": { + "eventemitter3": "^2.0.0" + } + }, + "node_modules/xml-lexer/node_modules/eventemitter3": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz", + "integrity": "sha512-jLN68Dx5kyFHaePoXWPsCGW5qdyZQtLYHkxkg02/Mz6g0kYpDx4FyP6XfArhQdlOC4b8Mv+EMxPo/8La7Tzghg==" + }, "node_modules/xml-name-validator": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", @@ -42133,6 +42179,20 @@ "resolved": "https://registry.npmjs.org/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz", "integrity": "sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g==" }, + "node_modules/xml-reader": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/xml-reader/-/xml-reader-2.4.3.tgz", + "integrity": "sha512-xWldrIxjeAMAu6+HSf9t50ot1uL5M+BtOidRCWHXIeewvSeIpscWCsp4Zxjk8kHHhdqFBrfK8U0EJeCcnyQ/gA==", + "dependencies": { + "eventemitter3": "^2.0.0", + "xml-lexer": "^0.2.2" + } + }, + "node_modules/xml-reader/node_modules/eventemitter3": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz", + "integrity": "sha512-jLN68Dx5kyFHaePoXWPsCGW5qdyZQtLYHkxkg02/Mz6g0kYpDx4FyP6XfArhQdlOC4b8Mv+EMxPo/8La7Tzghg==" + }, "node_modules/xml2js": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", diff --git a/package.json b/package.json index b8da6fb56..8929bddf0 100644 --- a/package.json +++ b/package.json @@ -134,7 +134,6 @@ "@types/pdf-parse": "^1.1.4", "@types/reveal": "^4.2.0", "@types/supercluster": "^7.1.3", - "@types/textfit": "^2.4.4", "@types/web": "^0.0.151", "@types/webpack-hot-middleware": "^2.25.9", "@typescript-eslint/parser": "^7.8.0", @@ -306,8 +305,8 @@ "stream-browserify": "^3.0.0", "styled-components": "^6.1.1", "supercluster": "^8.0.1", + "svgson": "^5.3.1", "textarea-caret": "^3.1.0", - "textfit": "^2.4.0", "tough-cookie": "^4.1.3", "tslint": "^6.1.3", "tslint-loader": "^3.5.4", diff --git a/src/ClientUtils.ts b/src/ClientUtils.ts index 630d7edbc..d03ae1486 100644 --- a/src/ClientUtils.ts +++ b/src/ClientUtils.ts @@ -86,7 +86,6 @@ export function returnEmptyDoclist() { return [] as any[]; } -// eslint-disable-next-line @typescript-eslint/no-namespace export namespace ClientUtils { export const CLICK_TIME = 300; export const DRAG_THRESHOLD = 4; @@ -450,29 +449,30 @@ export function smoothScrollHorizontal(duration: number, element: HTMLElement | animateScroll(); } -export function addStyleSheet() { +export function addStyleSheet(styleType: string = 'text/css') { const style = document.createElement('style'); + style.type = styleType; const sheets = document.head.appendChild(style); - return sheets.sheet; + return (sheets as any).sheet; } -export function addStyleSheetRule(sheet: CSSStyleSheet | null, selector: string, css: string | {[key:string]: string}, selectorPrefix = '.') { +export function addStyleSheetRule(sheet: any, selector: any, css: any, selectorPrefix = '.') { const propText = typeof css === 'string' ? css : Object.keys(css) .map(p => p + ':' + (p === 'content' ? "'" + css[p] + "'" : css[p])) .join(';'); - return sheet?.insertRule(selectorPrefix + selector + '{' + propText + '}', sheet.cssRules.length); + return sheet.insertRule(selectorPrefix + selector + '{' + propText + '}', sheet.cssRules.length); } -export function removeStyleSheetRule(sheet: CSSStyleSheet|null, rule: number) { - if (sheet?.rules.length) { +export function removeStyleSheetRule(sheet: any, rule: number) { + if (sheet.rules.length) { sheet.removeRule(rule); return true; } return false; } -export function clearStyleSheetRules(sheet: CSSStyleSheet|null) { - if (sheet?.rules.length) { +export function clearStyleSheetRules(sheet: any) { + if (sheet.rules.length) { numberRange(sheet.rules.length).map(() => sheet.removeRule(0)); return true; } diff --git a/src/client/apis/gpt/GPT.ts b/src/client/apis/gpt/GPT.ts index 231688265..a780596fa 100644 --- a/src/client/apis/gpt/GPT.ts +++ b/src/client/apis/gpt/GPT.ts @@ -12,6 +12,7 @@ enum GPTCallType { DESCRIBE = 'describe', MERMAID = 'mermaid', DATA = 'data', + DRAW = 'draw', } type GPTCallOpts = { @@ -69,10 +70,10 @@ let lastResp = ''; * @param inputText Text to process * @returns AI Output */ -const gptAPICall = async (inputTextIn: string, callType: GPTCallType, prompt?: any) => { +const gptAPICall = async (inputTextIn: string, callType: GPTCallType, prompt?: any, dontCache?: boolean) => { const inputText = [GPTCallType.SUMMARY, GPTCallType.FLASHCARD, GPTCallType.QUIZ].includes(callType) ? inputTextIn + '.' : inputTextIn; const opts: GPTCallOpts = callTypeMap[callType]; - if (lastCall === inputText) return lastResp; + if (lastCall === inputText && dontCache !== true) return lastResp; try { lastCall = inputText; diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 6b4f80e23..8c7d40158 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -752,6 +752,7 @@ pie title Minerals in my tap water static inkTools():Button[] { return [ { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen-nib", toolType: "pen", scripts: {onClick:'{ return setActiveTool(this.toolType, false, _readOnly_);}' }}, + { title: "Highlight", toolTip: "Highlight (Ctrl+H)", btnType: ButtonType.ToggleButton, icon: "highlighter",toolType: "highlighter", scripts: {onClick:'{ return setActiveTool(this.toolType, false, _readOnly_);}' }}, { title: "Write", toolTip: "Write (Ctrl+Shift+P)", btnType: ButtonType.ToggleButton, icon: "pen", toolType: "write", scripts: {onClick:'{ return setActiveTool(this.toolType, false, _readOnly_);}' }, funcs: {hidden:"IsNoviceMode()" }}, { title: "Eraser", toolTip: "Eraser (Ctrl+E)", btnType: ButtonType.MultiToggleButton, scripts: {onClick: '{ return setActiveTool(this.toolType, false, _readOnly_);}'}, funcs: {toolType:"activeEraserTool()"}, subMenu: [ @@ -764,9 +765,10 @@ pie title Minerals in my tap water { title: "Square", toolTip: "Square (double tap to lock mode)", btnType: ButtonType.ToggleButton, icon: "square", toolType: Gestures.Rectangle, scripts: {onClick:`{ return setActiveTool(this.toolType, false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool(this.toolType, true, _readOnly_);}`} }, { title: "Line", toolTip: "Line (double tap to lock mode)", btnType: ButtonType.ToggleButton, icon: "minus", toolType: Gestures.Line, scripts: {onClick:`{ return setActiveTool(this.toolType, false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool(this.toolType, true, _readOnly_);}`} }, { title: "Mask", toolTip: "Mask", btnType: ButtonType.ToggleButton, icon: "user-circle",toolType: "inkMask", scripts: {onClick:'{ return setInkProperty(this.toolType, value, _readOnly_);}'}, funcs: {hidden:"IsNoviceMode()" } }, - { title: "Labels", toolTip: "Lab els", btnType: ButtonType.ToggleButton, icon: "text-width", toolType: "labels", scripts: {onClick:'{ return setInkProperty(this.toolType, value, _readOnly_);}'}, }, + { title: "Labels", toolTip: "Labels", btnType: ButtonType.ToggleButton, icon: "text-width", toolType: "labels", scripts: {onClick:'{ return setInkProperty(this.toolType, value, _readOnly_);}'}, }, { title: "Width", toolTip: "Stroke width", btnType: ButtonType.NumberSliderButton, toolType: "strokeWidth", ignoreClick: true, scripts: {script: '{ return setInkProperty(this.toolType, value, _readOnly_);}'}, numBtnMin: 1}, { title: "Ink", toolTip: "Stroke color", btnType: ButtonType.ColorButton, icon: "pen", toolType: "strokeColor", ignoreClick: true, scripts: {script: '{ return setInkProperty(this.toolType, value, _readOnly_);}'} }, + { title: "Smart Draw", toolTip: "Draw with GPT", btnType: ButtonType.ToggleButton, icon: "user-pen", toolType: "smartdraw", scripts: {onClick:'{ return setActiveTool(this.toolType, false, _readOnly_);}'}, funcs: {hidden: "IsNoviceMode()"}}, ]; } diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 96b8b5657..8ad6ddf47 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -341,22 +341,20 @@ export class DocumentManager { // if there's an options.effect, it will be handled from linkFollowHighlight. We delay the start of // the highlight so that the target document can be somewhat centered so that the effect/highlight will be seen // bcz: should this delay be an options parameter? - setTimeout(() => { - Doc.linkFollowHighlight(viewSpec ? [docView.Document, viewSpec] : docView.Document, undefined, options.effect); - if (options.zoomTextSelections && Doc.IsUnhighlightTimerSet() && contextView && targetDoc.text_html) { - // if the docView is a text anchor, the contextView is the PDF/Web/Text doc - contextView.setTextHtmlOverlay(StrCast(targetDoc.text_html), options.effect); - DocumentManager._overlayViews.add(contextView); - } - Doc.AddUnHighlightWatcher(() => { - docView.Document[Animation] = undefined; - DocumentManager.removeOverlayViews(); - }); - }, (options.zoomTime ?? 0) * 0.5); + setTimeout(() => Doc.linkFollowHighlight(viewSpec ? [docView.Document, viewSpec] : docView.Document, undefined, options.effect), (options.zoomTime ?? 0) * 0.5); if (options.playMedia) docView.ComponentView?.playFrom?.(NumCast(docView.Document._layout_currentTimecode)); if (options.playAudio) DocumentManager.playAudioAnno(docView.Document); if (options.toggleTarget && (!options.didMove || docView.Document.hidden)) docView.Document.hidden = !docView.Document.hidden; - Doc.AddUnHighlightWatcher(() => docView.Document[Animation] = undefined); + + if (options.zoomTextSelections && Doc.IsUnhighlightTimerSet() && contextView && targetDoc.text_html) { + // if the docView is a text anchor, the contextView is the PDF/Web/Text doc + contextView.setTextHtmlOverlay(StrCast(targetDoc.text_html), options.effect); + DocumentManager._overlayViews.add(contextView); + } + Doc.AddUnHighlightWatcher(() => { + docView.Document[Animation] = undefined; + DocumentManager.removeOverlayViews(); + }); } } } diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index 278931cdd..d3c10f9f4 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -29,7 +29,7 @@ export enum ColorScheme { } @observer -export class SettingsManager extends React.Component { +export class SettingsManager extends React.Component<{}> { // eslint-disable-next-line no-use-before-define public static Instance: SettingsManager; static _settingsStyle = addStyleSheet(); @@ -123,7 +123,7 @@ export class SettingsManager extends React.Component { 'change color scheme' ); - constructor(props: object) { + constructor(props: {}) { super(props); makeObservable(this); SettingsManager.Instance = this; diff --git a/src/client/util/bezierFit.ts b/src/client/util/bezierFit.ts index e2055887d..693676bc3 100644 --- a/src/client/util/bezierFit.ts +++ b/src/client/util/bezierFit.ts @@ -2,8 +2,18 @@ /* eslint-disable prefer-destructuring */ /* eslint-disable no-param-reassign */ /* eslint-disable camelcase */ +import e from 'cors'; import { Point } from '../../pen-gestures/ndollar'; +export enum SVGType { + Rect = 'rect', + Path = 'path', + Circle = 'circle', + Ellipse = 'ellipse', + Line = 'line', + Polygon = 'polygon', +} + class SmartRect { minx: number = 0; miny: number = 0; @@ -557,6 +567,12 @@ function FitCubic(d: Point[], first: number, last: number, tHat1: Point, tHat2: const negThatCenter = new Point(-tHatCenter.X, -tHatCenter.Y); FitCubic(d, splitPoint2D, last, negThatCenter, tHat2, error, result); } +/** + * Convert polyline coordinates to a (multi) segment bezier curve + * @param d - polyline coordinates + * @param error - how much error to allow in fitting (measured in pixels) + * @returns + */ export function FitCurve(d: Point[], error: number) { const tHat1 = ComputeLeftTangent(d, 0); // Unit tangent vectors at endpoints const tHat2 = ComputeRightTangent(d, d.length - 1); diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index e961bc031..649208989 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -248,15 +248,15 @@ export class GestureOverlay extends ObservableReactComponent { }; togglePalette = () => { this._showPalette = !this._showPalette; + // if (this._showPalette === false) AnnotationPalette.Instance.resetPalette(true); }; togglePen = () => { Doc.ActiveTool = Doc.ActiveTool === InkTool.Pen ? InkTool.None : InkTool.Pen; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index e6d6b20a4..757fc3fe2 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -320,6 +320,7 @@ export class MainView extends ObservableReactComponent<{}> { fa.faCompass, fa.faSnowflake, fa.faStar, + fa.faSplotch, fa.faMicrophone, fa.faCircleHalfStroke, fa.faKeyboard, @@ -399,6 +400,7 @@ export class MainView extends ObservableReactComponent<{}> { fa.faArrowsLeftRight, fa.faPause, fa.faPen, + fa.faUserPen, fa.faPenNib, fa.faPhone, fa.faPlay, @@ -445,6 +447,7 @@ export class MainView extends ObservableReactComponent<{}> { fa.faEyeDropper, fa.faPaintRoller, fa.faBars, + fa.faBarsStaggered, fa.faBrush, fa.faShapes, fa.faEllipsisH, @@ -1102,6 +1105,7 @@ export class MainView extends ObservableReactComponent<{}> { + diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx index c18ac6738..f06f3efe0 100644 --- a/src/client/views/MarqueeAnnotator.tsx +++ b/src/client/views/MarqueeAnnotator.tsx @@ -28,6 +28,7 @@ export interface MarqueeAnnotatorProps { marqueeContainer: HTMLDivElement; docView: () => DocumentView; savedAnnotations: () => ObservableMap; + savedTapes: () => ObservableMap; selectionText: () => string; annotationLayer: HTMLDivElement; addDocument: (doc: Doc) => boolean; @@ -74,6 +75,7 @@ export class MarqueeAnnotator extends ObservableReactComponent): Opt => { + // const savedTapeMap = savedTapes?.values() && Array.from(savedTapes?.values()).length ? savedTapes : this.props.savedTapes(); + // if (savedTapeMap.size === 0) return undefined; + // const tapes = Array.from(savedTapeMap.values())[0]; + // const doc = this.props.Document; + // const scale = (this.props.annotationLayerScaling?.() || 1) * NumCast(doc._freeform_scale, 1); + // if (tapes.length && (tapes[0] as any).marqueeing) { + // const anno = tapes[0]; + // const containerOffset = this.props.containerOffset?.() || [0, 0]; + // const tape = Docs.Create.FreeformDocument([], { + // onClick: isLinkButton ? FollowLinkScript() : undefined, + // backgroundColor: color, + // annotationOn: this.props.Document, + // title: 'Tape on ' + this.props.Document.title, + // }); + // tape.x = NumCast(doc.freeform_panX_min) + (parseInt(anno.style.left || '0') - containerOffset[0]) / scale; + // tape.y = NumCast(doc.freeform_panY_min) + (parseInt(anno.style.top || '0') - containerOffset[1]) / scale; + // tape._height = parseInt(anno.style.height || '0') / scale; + // tape._width = parseInt(anno.style.width || '0') / scale; + // anno.remove(); + // savedTapeMap.clear(); + // return tape; + // } + + // const textRegionAnno = Docs.Create.ConfigDocument({ + // annotationOn: this.props.Document, + // text: this.props.selectionText() as any, // text want an RTFfield, but strings are acceptable, too. + // text_html: this.props.selectionText() as any, + // backgroundColor: 'transparent', + // presentation_duration: 2100, + // presentation_transition: 500, + // presentation_zoomText: true, + // title: '>' + this.props.Document.title, + // }); + // const textRegionAnnoProto = textRegionAnno[DocData]; + // let minX = Number.MAX_VALUE; + // let maxX = -Number.MAX_VALUE; + // let minY = Number.MAX_VALUE; + // let maxY = -Number.MIN_VALUE; + // const annoRects: string[] = []; + // savedAnnoMap.forEach((value: HTMLDivElement[]) => + // value.forEach(anno => { + // const x = parseInt(anno.style.left ?? '0'); + // const y = parseInt(anno.style.top ?? '0'); + // const height = parseInt(anno.style.height ?? '0'); + // const width = parseInt(anno.style.width ?? '0'); + // annoRects.push(`${x}:${y}:${width}:${height}`); + // anno.remove(); + // minY = Math.min(NumCast(y), minY); + // minX = Math.min(NumCast(x), minX); + // maxY = Math.max(NumCast(y) + NumCast(height), maxY); + // maxX = Math.max(NumCast(x) + NumCast(width), maxX); + // }) + // ); + + // textRegionAnnoProto.y = Math.max(minY, 0); + // textRegionAnnoProto.x = Math.max(minX, 0); + // textRegionAnnoProto.height = Math.max(maxY, 0) - Math.max(minY, 0); + // textRegionAnnoProto.width = Math.max(maxX, 0) - Math.max(minX, 0); + // textRegionAnnoProto.backgroundColor = color; + // // mainAnnoDocProto.text = this._selectionText; + // textRegionAnnoProto.text_inlineAnnotations = new List(annoRects); + // textRegionAnnoProto.opacity = 0; + // textRegionAnnoProto.layout_unrendered = true; + // savedAnnoMap.clear(); + // return textRegionAnno; + // }; + + @undoBatch + makeTapeDocument = (color: string, isLinkButton?: boolean, savedTapes?: ObservableMap): Opt => { + // const savedAnnoMap = savedTapes?.values() && Array.from(savedTapes?.values()).length ? savedTapes : this.props.savedTapes(); + // if (savedAnnoMap.size === 0) return undefined; + // const savedAnnos = Array.from(savedAnnoMap.values())[0]; + const doc = this.props.Document; + const scale = (this.props.annotationLayerScaling?.() || 1) * NumCast(doc._freeform_scale, 1); + const marqueeAnno = Docs.Create.FreeformDocument([], { + onClick: isLinkButton ? FollowLinkScript() : undefined, + backgroundColor: color, + annotationOn: this.props.Document, + title: 'Annotation on ' + this.props.Document.title, + }); + marqueeAnno.x = NumCast(doc.freeform_panX_min) / scale; + marqueeAnno.y = NumCast(doc.freeform_panY_min) / scale; + marqueeAnno._height = parseInt('100') / scale; + marqueeAnno._width = parseInt('100') / scale; + return marqueeAnno; + // } + + // const textRegionAnno = Docs.Create.ConfigDocument({ + // annotationOn: this.props.Document, + // text: this.props.selectionText() as any, // text want an RTFfield, but strings are acceptable, too. + // text_html: this.props.selectionText() as any, + // backgroundColor: 'transparent', + // presentation_duration: 2100, + // presentation_transition: 500, + // presentation_zoomText: true, + // title: '>' + this.props.Document.title, + // }); + // const textRegionAnnoProto = textRegionAnno[DocData]; + // let minX = Number.MAX_VALUE; + // let maxX = -Number.MAX_VALUE; + // let minY = Number.MAX_VALUE; + // let maxY = -Number.MIN_VALUE; + // const annoRects: string[] = []; + // savedAnnoMap.forEach((value: HTMLDivElement[]) => + // value.forEach(anno => { + // const x = parseInt(anno.style.left ?? '0'); + // const y = parseInt(anno.style.top ?? '0'); + // const height = parseInt(anno.style.height ?? '0'); + // const width = parseInt(anno.style.width ?? '0'); + // annoRects.push(`${x}:${y}:${width}:${height}`); + // anno.remove(); + // minY = Math.min(NumCast(y), minY); + // minX = Math.min(NumCast(x), minX); + // maxY = Math.max(NumCast(y) + NumCast(height), maxY); + // maxX = Math.max(NumCast(x) + NumCast(width), maxX); + // }) + // ); + + // textRegionAnnoProto.y = Math.max(minY, 0); + // textRegionAnnoProto.x = Math.max(minX, 0); + // textRegionAnnoProto.height = Math.max(maxY, 0) - Math.max(minY, 0); + // textRegionAnnoProto.width = Math.max(maxX, 0) - Math.max(minX, 0); + // textRegionAnnoProto.backgroundColor = color; + // // mainAnnoDocProto.text = this._selectionText; + // textRegionAnnoProto.text_inlineAnnotations = new List(annoRects); + // textRegionAnnoProto.opacity = 0; + // textRegionAnnoProto.layout_unrendered = true; + // savedAnnoMap.clear(); + // return textRegionAnno; + }; + @action highlight = (color: string, isLinkButton: boolean, savedAnnotations?: ObservableMap, addAsAnnotation?: boolean) => { // creates annotation documents for current highlights @@ -136,6 +272,15 @@ export class MarqueeAnnotator extends ObservableReactComponent, addAsAnnotation?: boolean) => { + // creates annotation documents for current highlights + const effectiveAcl = GetEffectiveAcl(this.props.Document[DocData]); + const tape = [AclAugment, AclSelfEdit, AclEdit, AclAdmin].includes(effectiveAcl) && this.makeTapeDocument(color, isLinkButton, savedTapes); + addAsAnnotation && tape && this.props.addDocument(tape); + return tape as Doc; + }; + public static previewNewAnnotation = action((savedAnnotations: ObservableMap, annotationLayer: HTMLDivElement, div: HTMLDivElement, page: number) => { div.style.backgroundColor = '#ACCEF7'; div.style.opacity = '0.5'; @@ -182,6 +327,7 @@ export class MarqueeAnnotator extends ObservableReactComponent this.props.anchorMenuClick?.()?.(this.highlight(this.props.highlightDragSrcColor ?? 'rgba(173, 216, 230, 0.75)', true, undefined, true)), 'make sidebar annotation'); AnchorMenu.Instance.OnAudio = unimplementedFunction; AnchorMenu.Instance.Highlight = (color: string) => this.highlight(color, false, undefined, true); + AnchorMenu.Instance.Tape = (color: string) => this.tape(color, false, undefined, true); AnchorMenu.Instance.GetAnchor = (savedAnnotations?: ObservableMap /* , addAsAnnotation?: boolean */) => this.highlight('rgba(173, 216, 230, 0.75)', true, savedAnnotations, true); AnchorMenu.Instance.onMakeAnchor = () => AnchorMenu.Instance.GetAnchor(undefined, true); diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx index 4f3ce9d9b..241087fd2 100644 --- a/src/client/views/collections/CollectionCarousel3DView.tsx +++ b/src/client/views/collections/CollectionCarousel3DView.tsx @@ -17,7 +17,6 @@ import { FocusViewOptions } from '../nodes/FocusViewOptions'; import './CollectionCarousel3DView.scss'; import { CollectionSubView } from './CollectionSubView'; -// eslint-disable-next-line @typescript-eslint/no-var-requires const { CAROUSEL3D_CENTER_SCALE, CAROUSEL3D_SIDE_SCALE, CAROUSEL3D_TOP } = require('../global/globalCssVariables.module.scss'); @observer diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 2a36e96bf..73179a266 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -58,7 +58,7 @@ export class CollectionDockingView extends CollectionSubView() { return this._goldenLayout._maximisedItem !== null; } private _goldenLayout: any = null; - static _highlightStyleSheet = addStyleSheet(); + static _highlightStyleSheet: any = addStyleSheet(); constructor(props: any) { super(props); diff --git a/src/client/views/collections/CollectionMenu.scss b/src/client/views/collections/CollectionMenu.scss index 3ec875df4..45d9394ed 100644 --- a/src/client/views/collections/CollectionMenu.scss +++ b/src/client/views/collections/CollectionMenu.scss @@ -6,7 +6,7 @@ align-content: center; justify-content: space-between; background-color: $dark-gray; - height: 35px; + height: 40px; border-bottom: $standard-border; padding: 0 10px; align-items: center; diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx index b03f0cffa..fac885300 100644 --- a/src/client/views/collections/CollectionStackedTimeline.tsx +++ b/src/client/views/collections/CollectionStackedTimeline.tsx @@ -850,7 +850,7 @@ class StackedTimelineAnchor extends ObservableReactComponent>() { public static AddTreeFunc = 'addTreeFolder(this.embedContainer)'; private _treedropDisposer?: DragManager.DragDropDisposer; + private _mainEle?: HTMLDivElement; private _titleRef?: HTMLDivElement | HTMLInputElement | null; private _disposers: { [name: string]: IReactionDisposer } = {}; private _isDisposing = false; // notes that instance is in process of being disposed @@ -82,6 +83,8 @@ export class CollectionTreeView extends CollectionSubView this._mainEle; + // these should stay in synch with counterparts in DocComponent.ts ViewBoxAnnotatableComponent @observable _isAnyChildContentActive = false; whenChildContentsActiveChanged = action((isActive: boolean) => { @@ -131,6 +134,7 @@ export class CollectionTreeView extends CollectionSubView { this._treedropDisposer?.(); + this._mainEle = ele; if (ele) this._treedropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.Document, this.onInternalPreDrop.bind(this)); }; diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index f69aea2a7..b82421e6b 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -469,12 +469,14 @@ export class TreeView extends ObservableReactComponent { return false; } - refTransform = (ref: HTMLElement | undefined | null) => { + refTransform = (ref: HTMLDivElement | undefined | null) => { if (!ref) return this.ScreenToLocalTransform(); - const { translateX, translateY, scale } = ClientUtils.GetScreenTransform(ref); - return new Transform(-translateX, -translateY, 1).scale(1/scale); + const { translateX, translateY } = ClientUtils.GetScreenTransform(ref); + const outerXf = ClientUtils.GetScreenTransform(this.treeView.MainEle()); + const offset = this.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY); + return this.ScreenToLocalTransform().translate(offset[0], offset[1]); }; - docTransform = () => this.refTransform(this._dref?.ContentDiv); + docTransform = () => this.refTransform(this._dref?.ContentRef?.current); getTransform = () => this.refTransform(this._tref.current); embeddedPanelWidth = () => this._props.panelWidth() / (this.treeView._props.NativeDimScaling?.() || 1); embeddedPanelHeight = () => { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 62632e8c2..a298e6ac4 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1,7 +1,7 @@ /* eslint-disable react/jsx-props-no-spreading */ /* eslint-disable jsx-a11y/click-events-have-key-events */ /* eslint-disable jsx-a11y/no-static-element-interactions */ -import { Bezier } from 'bezier-js'; +import { Bezier, Point } from 'bezier-js'; import { Colors } from 'browndash-components'; import { action, computed, IReactionDisposer, makeObservable, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; @@ -27,6 +27,7 @@ import { aggregateBounds, clamp, emptyFunction, intersectRect, Utils } from '../ import { Docs } from '../../../documents/Documents'; import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; import { DocUtils } from '../../../documents/DocUtils'; +import { FitCurve, GenerateControlPoints } from '../../../util/bezierFit'; import { DragManager } from '../../../util/DragManager'; import { dropActionType } from '../../../util/DropActionTypes'; import { CompileScript } from '../../../util/Scripting'; @@ -90,10 +91,8 @@ export class CollectionFreeFormView extends CollectionSubView(); private _batch: UndoManager.Batch | undefined = undefined; + private _brushtimer: any; + private _brushtimer1: any; private _keyTimer: NodeJS.Timeout | undefined; // timer for turning off transition flag when key frame change has completed. Need to clear this if you do a second navigation before first finishes, or else first timer can go off during second naviation. private _presEaseFunc: string = 'ease'; @@ -120,6 +121,7 @@ export class CollectionFreeFormView extends CollectionSubView(); @observable _marqueeViewRef = React.createRef(); @@ -132,7 +134,7 @@ export class CollectionFreeFormView extends CollectionSubView { this._deleteList.lastElement()?._props.removeDocument?.(this._deleteList.map(ink => ink.Document)); @@ -611,60 +616,91 @@ export class CollectionFreeFormView extends CollectionSubView { + erase = (e: PointerEvent, delta: number[]) => { + e.stopImmediatePropagation(); const currPoint = { X: e.clientX, Y: e.clientY }; this._eraserPts.push([currPoint.X, currPoint.Y]); this._eraserPts = this._eraserPts.slice(Math.max(0, this._eraserPts.length - 5)); - // if (this._eraserLock) return false; // leaving this commented out in case the idea is revisited in the future - this.getEraserIntersections({ X: currPoint.X - delta[0], Y: currPoint.Y - delta[1] }, currPoint).forEach(intersect => { - if (!this._deleteList.includes(intersect.inkView)) { - this._deleteList.push(intersect.inkView); - SetActiveInkWidth(StrCast(intersect.inkView.Document.stroke_width?.toString()) || '1'); - SetActiveInkColor(StrCast(intersect.inkView.Document.color?.toString()) || 'black'); - // create a new curve by appending all curves of the current segment together in order to render a single new stroke. - if (Doc.ActiveTool !== InkTool.StrokeEraser) { - // this._eraserLock++; - const segments = this.segmentErase(intersect.inkView, intersect.t); // intersect.t is where the eraser intersected the ink stroke - want to remove the segment that starts at the intersection just before this t value and goes to the one just after it - const newStrokes = segments?.map(segment => { - const points = segment.reduce((data, curve) => [...data, ...curve.points.map(p => intersect.inkView.ComponentView?.ptToScreen?.({ X: p.x, Y: p.y }) ?? { X: 0, Y: 0 })], [] as PointData[]); - const bounds = InkField.getBounds(points); - const B = this.screenToFreeformContentsXf.transformBounds(bounds.left, bounds.top, bounds.width, bounds.height); - const inkWidth = ActiveInkWidth() * this.ScreenToLocalBoxXf().Scale; - return Docs.Create.InkDocument( - points, - { title: 'stroke', + if (Doc.ActiveTool === InkTool.RadiusEraser) { + const strokeMap: Map = this.getRadiusEraserIntersections({ X: currPoint.X - delta[0], Y: currPoint.Y - delta[1] }, currPoint); + strokeMap.forEach((intersects, stroke) => { + if (!this._deleteList.includes(stroke)) { + this._deleteList.push(stroke); + SetActiveInkWidth(StrCast(stroke.Document.stroke_width?.toString()) || '1'); + SetActiveInkColor(StrCast(stroke.Document.color?.toString()) || 'black'); + const segments = this.radiusErase(stroke, intersects.sort()); + segments?.forEach(segment => + this.forceStrokeGesture( + e, + Gestures.Stroke, + segment.reduce((data, curve) => [...data, ...curve.points.map(p => stroke.ComponentView?.ptToScreen?.({ X: p.x, Y: p.y }) ?? { X: 0, Y: 0 })], [] as PointData[]) + ) + ); + } + stroke.layoutDoc.opacity = 0; + stroke.layoutDoc.dontIntersect = true; + }); + } else { + this.getEraserIntersections({ X: currPoint.X - delta[0], Y: currPoint.Y - delta[1] }, currPoint).forEach(intersect => { + if (!this._deleteList.includes(intersect.inkView)) { + this._deleteList.push(intersect.inkView); + SetActiveInkWidth(StrCast(intersect.inkView.Document.stroke_width?.toString()) || '1'); + SetActiveInkColor(StrCast(intersect.inkView.Document.color?.toString()) || 'black'); + // create a new curve by appending all curves of the current segment together in order to render a single new stroke. + if (Doc.ActiveTool !== InkTool.StrokeEraser) { + // this._eraserLock++; + const segments = this.segmentErase(intersect.inkView, intersect.t); // intersect.t is where the eraser intersected the ink stroke - want to remove the segment that starts at the intersection just before this t value and goes to the one just after it + const newStrokes = segments?.map(segment => { + const points = segment.reduce((data, curve) => [...data, ...curve.points.map(p => intersect.inkView.ComponentView?.ptToScreen?.({ X: p.x, Y: p.y }) ?? { X: 0, Y: 0 })], [] as PointData[]); + const bounds = InkField.getBounds(points); + const B = this.screenToFreeformContentsXf.transformBounds(bounds.left, bounds.top, bounds.width, bounds.height); + const inkWidth = ActiveInkWidth() * this.ScreenToLocalBoxXf().Scale; + return Docs.Create.InkDocument( + points, + { title: 'stroke', x: B.x - inkWidth / 2, y: B.y - inkWidth / 2, _width: B.width + inkWidth, _height: B.height + inkWidth, stroke_showLabel: BoolCast(Doc.UserDoc().activeInkHideTextLabels)}, // prettier-ignore - inkWidth, - ActiveInkColor(), - ActiveInkBezierApprox(), - ActiveFillColor(), - ActiveArrowStart(), - ActiveArrowEnd(), - ActiveDash(), - ActiveIsInkMask() - ); - }); - newStrokes && this.addDocument?.(newStrokes); - // setTimeout(() => this._eraserLock--); + inkWidth, + ActiveInkColor(), + ActiveInkBezierApprox(), + ActiveFillColor(), + ActiveArrowStart(), + ActiveArrowEnd(), + ActiveDash(), + ActiveIsInkMask() + ); + }); + newStrokes && this.addDocument?.(newStrokes); + // setTimeout(() => this._eraserLock--); + } } - // Lower ink opacity to give the user a visual indicator of deletion. - intersect.inkView.layoutDoc.opacity = 0; - intersect.inkView.layoutDoc.dontIntersect = true; - } - }); + }); + } return false; }; + /** + * Erases strokes by intersecting them with an invisible "eraser stroke". + * By default this iterates through all intersected ink strokes, determines their segmentation, draws back the non-intersected segments, + * and deletes the original stroke. + */ + @action + onEraserMove = (e: PointerEvent, down: number[], delta: number[]) => { + this.erase(e, delta); + // if (this._eraserLock) return false; // leaving this commented out in case the idea is revisited in the future + return false; + }; + + @action + onEraserClick = (e: PointerEvent, doubleTap?: boolean) => { + e.preventDefault(); + e.stopImmediatePropagation(); + this.erase(e, [0, 0]); + }; + /** * Erases strokes by intersecting them with a circle of variable radius. Essentially creates an InkField for the * eraser circle, then determines its intersections with other ink strokes. Each stroke's DocumentView and its @@ -674,35 +710,35 @@ export class CollectionFreeFormView extends CollectionSubView { - const currPoint = { X: e.clientX, Y: e.clientY }; - this._eraserPts.push([currPoint.X, currPoint.Y]); - this._eraserPts = this._eraserPts.slice(Math.max(0, this._eraserPts.length - 5)); - const strokeMap: Map = this.getRadiusEraserIntersections({ X: currPoint.X - delta[0], Y: currPoint.Y - delta[1] }, currPoint); - - strokeMap.forEach((intersects, stroke) => { - if (!this._deleteList.includes(stroke)) { - this._deleteList.push(stroke); - SetActiveInkWidth(StrCast(stroke.Document.stroke_width?.toString()) || '1'); - SetActiveInkColor(StrCast(stroke.Document.color?.toString()) || 'black'); - const segments = this.radiusErase(stroke, intersects.sort()); - segments?.forEach(segment => - this.forceStrokeGesture( - e, - Gestures.Stroke, - segment.reduce((data, curve) => [...data, ...curve.points.map(p => stroke.ComponentView?.ptToScreen?.({ X: p.x, Y: p.y }) ?? { X: 0, Y: 0 })], [] as PointData[]) - ) - ); - } - stroke.layoutDoc.opacity = 0; - stroke.layoutDoc.dontIntersect = true; - }); - return false; - }; - - forceStrokeGesture = (e: PointerEvent, gesture: Gestures, points: InkData) => { - this.onGesture(e, new GestureUtils.GestureEvent(gesture, points, InkField.getBounds(points))); + // @action + // onRadiusEraserMove = (e: PointerEvent, down: number[], delta: number[]) => { + // const currPoint = { X: e.clientX, Y: e.clientY }; + // this._eraserPts.push([currPoint.X, currPoint.Y]); + // this._eraserPts = this._eraserPts.slice(Math.max(0, this._eraserPts.length - 5)); + // const strokeMap: Map = this.getRadiusEraserIntersections({ X: currPoint.X - delta[0], Y: currPoint.Y - delta[1] }, currPoint); + + // strokeMap.forEach((intersects, stroke) => { + // if (!this._deleteList.includes(stroke)) { + // this._deleteList.push(stroke); + // SetActiveInkWidth(StrCast(stroke.Document.stroke_width?.toString()) || '1'); + // SetActiveInkColor(StrCast(stroke.Document.color?.toString()) || 'black'); + // const segments = this.radiusErase(stroke, intersects.sort()); + // segments?.forEach(segment => + // this.forceStrokeGesture( + // e, + // Gestures.Stroke, + // segment.reduce((data, curve) => [...data, ...curve.points.map(p => stroke.ComponentView?.ptToScreen?.({ X: p.x, Y: p.y }) ?? { X: 0, Y: 0 })], [] as PointData[]) + // ) + // ); + // } + // stroke.layoutDoc.opacity = 0; + // stroke.layoutDoc.dontIntersect = true; + // }); + // return false; + // }; + + forceStrokeGesture = (e: PointerEvent, gesture: Gestures, points: InkData, text?: any) => { + this.onGesture(e, new GestureUtils.GestureEvent(gesture, points, InkField.getBounds(points), text)); }; onPointerMove = (e: PointerEvent) => { @@ -730,7 +766,7 @@ export class CollectionFreeFormView extends CollectionSubView (this.Document.isTemplateDoc || this.Document.isTemplateForField ? false : !this._renderCutoffData.get(doc[Id] + ''))); + renderCutoffProvider = computedFn((doc: Doc) => (this.Document.isTemplateDoc ? false : !this._renderCutoffData.get(doc[Id] + ''))); doEngineLayout( poolData: Map, @@ -1753,7 +1789,7 @@ export class CollectionFreeFormView extends CollectionSubView 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] }, + bounds: (entry[1].opacity === 0 ? { ...entry[1], width: 0, height: 0 } : { ...entry[1] }) as any, inkMask: BoolCast(entry[1].pair.layout.stroke_isInkMask) ? NumCast(entry[1].pair.layout.opacity, 1) : -1, }) ); @@ -1875,32 +1911,31 @@ export class CollectionFreeFormView extends CollectionSubView disposer?.()); } - updateIcon = (usePanelDimensions?: boolean) => { - const contentDiv = this.DocumentView?.().ContentDiv; - contentDiv && - UpdateIcon( - this.layoutDoc[Id] + '-icon' + new Date().getTime(), - contentDiv, - usePanelDimensions ? this._props.PanelWidth() : NumCast(this.layoutDoc._width), - usePanelDimensions ? this._props.PanelHeight() : NumCast(this.layoutDoc._height), - this._props.PanelWidth(), - this._props.PanelHeight(), - 0, - 1, - false, - '', - (iconFile, nativeWidth, nativeHeight) => { - this.dataDoc.icon = new ImageField(iconFile); - this.dataDoc.icon_nativeWidth = nativeWidth; - this.dataDoc.icon_nativeHeight = nativeHeight; - } - ); - }; + updateIcon = (usePanelDimensions?: boolean) => + UpdateIcon( + this.layoutDoc[Id] + '-icon' + new Date().getTime(), + this.DocumentView?.().ContentDiv!, + usePanelDimensions ? this._props.PanelWidth() : NumCast(this.layoutDoc._width), + usePanelDimensions ? this._props.PanelHeight() : NumCast(this.layoutDoc._height), + this._props.PanelWidth(), + this._props.PanelHeight(), + 0, + 1, + false, + '', + (iconFile, nativeWidth, nativeHeight) => { + this.dataDoc.icon = new ImageField(iconFile); + this.dataDoc.icon_nativeWidth = nativeWidth; + this.dataDoc.icon_nativeHeight = nativeHeight; + } + ); @action onCursorMove = (e: React.PointerEvent) => { - this._eraserX = e.clientX; - this._eraserY = e.clientY; + const locPt = this.ScreenToLocalBoxXf().transformPoint(e.clientX, e.clientY); + this._eraserX = locPt[0]; + this._eraserY = locPt[1]; + // Doc.ActiveTool === InkTool.RadiusEraser ? this._childPointerEvents = 'none' : this._childPointerEvents = 'all' // super.setCursorPosition(this.getTransform().transformPoint(e.clientX, e.clientY)); }; @@ -2083,7 +2118,7 @@ export class CollectionFreeFormView extends CollectionSubView { if (!DocumentView.LightboxDoc() || DocumentView.LightboxContains(this.DocumentView?.())) { const layoutUnrendered = this.childDocs.filter(doc => !this._renderCutoffData.get(doc[Id])); - const loadIncrement = this.Document.isTemplateDoc || this.Document.isTemplateForField ? Number.MAX_VALUE : 5; + const loadIncrement = this.Document.isTemplateDoc ? Number.MAX_VALUE : 5; for (let i = 0; i < Math.min(layoutUnrendered.length, loadIncrement); i++) { this._renderCutoffData.set(layoutUnrendered[i][Id] + '', true); } @@ -2218,8 +2253,8 @@ export class CollectionFreeFormView extends CollectionSubView Transform; @@ -425,6 +426,7 @@ export class MarqueeView extends ObservableReactComponent { SetActiveInkColor(StrCast(value)); selected?.type === DocumentType.INK && setActiveTool(GestureOverlay.Instance.InkShape ?? InkTool.Pen, true, false);}, }], [ 'eraserWidth', { - checkResult: () => ActiveEraserWidth(), + checkResult: () => ActiveEraserWidth() === 0 ? 1 : ActiveEraserWidth(), setInk: (doc: Doc) => { }, - setMode: () => { SetEraserWidth(value.toString());}, + setMode: () => { SetEraserWidth(value);}, }] ]); diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index ee67dd305..29a499035 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -320,6 +320,6 @@ export class CollectionFreeFormDocumentView extends DocComponent, root: Doc) { - const dir = ((presEffectDoc?.presentation_effectDirection ?? presEffectDoc?.followLinkAnimDirection) || PresEffectDirection.Center) as PresEffectDirection; - const duration = Cast(presEffectDoc?.presentation_transition, 'number', Cast(presEffectDoc?.followLinkTransitionTime, 'number', null)); + let dir = (presEffectDoc?.presentation_effectDirection ?? presEffectDoc?.followLinkAnimDirection) as PresEffectDirection; + const transitionTime = presEffectDoc?.presentation_transition ? NumCast(presEffectDoc?.presentation_transition) : 500; const effectProps = { left: dir === PresEffectDirection.Left, right: dir === PresEffectDirection.Right, @@ -978,14 +978,26 @@ export class DocumentViewInternal extends DocComponent{renderDoc} case PresEffect.Flip: return {renderDoc} @@ -1067,16 +1079,15 @@ export class DocumentView extends DocComponent() { finished?: (changed: boolean) => void // func called after focusing on target with flag indicating whether anything needed to be done. ) => Promise; public static linkCommonAncestor: (link: Doc) => DocumentView | undefined; - /** - * Pins a Doc to the current presentation trail. (see TabDocView for implementation) - */ + // pin func public static PinDoc: (docIn: Doc | Doc[], pinProps: PinProps) => void; - /** - * The DocumentView below the cursor at the start of a gesture (that receives the pointerDown event). Used by GestureOverlay to determine the doc a gesture should apply to. - */ - public static DownDocView: DocumentView | undefined; + // gesture + public static DownDocView: DocumentView | undefined; // the first DocView that receives a pointerdown event. used by GestureOverlay to determine the doc a gesture should apply to. + // media playing + @observable public static CurrentlyPlaying: DocumentView[] = []; public get displayName() { return 'DocumentView(' + (this.Document?.title??"") + ')'; } // prettier-ignore + public ContentRef = React.createRef(); private _htmlOverlayEffect: Opt; private _disposers: { [name: string]: IReactionDisposer } = {}; private _viewTimer: NodeJS.Timeout | undefined; @@ -1107,7 +1118,6 @@ export class DocumentView extends DocComponent() { @observable private _htmlOverlayText: Opt = undefined; @observable private _isHovering = false; @observable private _selected = false; - @observable public static CurrentlyPlaying: DocumentView[] = []; // audio or video media views that are currently playing @computed private get shouldNotScale() { return (this.layout_fitWidth && !this.nativeWidth) || this.ComponentView?.isUnstyledView?.(); @@ -1454,6 +1464,7 @@ export class DocumentView extends DocComponent() { {!this.Document || !this._props.PanelWidth() ? null : (
tags) inside the text. + +whiteSpace: Sets whitespace handling. Possible values: "nowrap", "pre". Defaults to "nowrap". (Can also be set to enable wrapping but this doesn't work well.) + +Bruno Orlandi - 2017 + +Copyright (C) 2013 Daniel Hoffmann Bernardes, Ícaro Technologies +Copyright (C) 2016 Jet Holt + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +function _calculateInnerDimensions(computedStyle) { + //Calculate the inner width and height + var innerWidth; + var innerHeight; + + var width = parseInt(computedStyle.getPropertyValue("width")); + var height = parseInt(computedStyle.getPropertyValue("height")); + var paddingLeft = parseInt(computedStyle.getPropertyValue("padding-left")); + var paddingRight = parseInt(computedStyle.getPropertyValue("padding-right")); + var paddingTop = parseInt(computedStyle.getPropertyValue("padding-top")); + var paddingBottom = parseInt(computedStyle.getPropertyValue("padding-bottom")); + var borderLeft = parseInt(computedStyle.getPropertyValue("border-left-width")); + var borderRight = parseInt(computedStyle.getPropertyValue("border-right-width")); + var borderTop = parseInt(computedStyle.getPropertyValue("border-top-width")); + var borderBottom = parseInt(computedStyle.getPropertyValue("border-bottom-width")); + + //If box-sizing is border-box, we need to subtract padding and border. + var parentBoxSizing = computedStyle.getPropertyValue("box-sizing"); + if (parentBoxSizing == "border-box") { + innerWidth = width - (paddingLeft + paddingRight + borderLeft + borderRight); + innerHeight = height - (paddingTop + paddingBottom + borderTop + borderBottom); + } else { + innerWidth = width; + innerHeight = height; + } + var obj = {}; + obj["width"] = innerWidth; + obj["height"] = innerHeight; + return obj; +} + +export default function BigText(element, options) { + + if (typeof element === 'string') { + element = document.querySelector(element); + } else if (element.length) { + // Support for array based queries (such as jQuery) + element = element[0]; + } + + var defaultOptions = { + rotateText: null, + fontSizeFactor: 0.8, + maximumFontSize: null, + limitingDimension: "both", + horizontalAlign: "center", + verticalAlign: "center", + textAlign: "center", + whiteSpace: "nowrap", + singleLine: true + }; + + //Merge provided options and default options + options = options || {}; + for (var opt in defaultOptions) + if (defaultOptions.hasOwnProperty(opt) && !options.hasOwnProperty(opt)) + options[opt] = defaultOptions[opt]; + + //Get variables which we will reference frequently + var style = element.style; + var parent = element.parentNode; + var parentStyle = parent.style; + var parentComputedStyle = document.defaultView.getComputedStyle(parent); + + //hides the element to prevent "flashing" + style.visibility = "hidden"; + //Set some properties + style.display = "inline-block"; + style.clear = "both"; + style.float = "left"; + var fontSize = options.maximumFontSize; + if (options.singleLine) { + style.fontSize = (fontSize * options.fontSizeFactor) + "px"; + style.lineHeight = fontSize + "px"; + } else { + for (; fontSize > options.minimumFontSize; fontSize = fontSize - Math.min(fontSize / 2, Math.max(0, fontSize - 48) + 2)) { + style.fontSize = (fontSize * options.fontSizeFactor) + "px"; + style.lineHeight = "1"; + if (element.offsetHeight <= +parentComputedStyle.height.replace("px", "")) { + break; + } + } + } + style.whiteSpace = options.whiteSpace; + style.textAlign = options.textAlign; + style.position = "relative"; + style.padding = 0; + style.margin = 0; + style.left = "50%"; + style.top = "50%"; + var computedStyle = document.defaultView.getComputedStyle(element); + + //Get properties of parent to allow easier referencing later. + var parentPadding = { + top: parseInt(parentComputedStyle.getPropertyValue("padding-top")), + right: parseInt(parentComputedStyle.getPropertyValue("padding-right")), + bottom: parseInt(parentComputedStyle.getPropertyValue("padding-bottom")), + left: parseInt(parentComputedStyle.getPropertyValue("padding-left")), + }; + var parentBorder = { + top: parseInt(parentComputedStyle.getPropertyValue("border-top")), + right: parseInt(parentComputedStyle.getPropertyValue("border-right")), + bottom: parseInt(parentComputedStyle.getPropertyValue("border-bottom")), + left: parseInt(parentComputedStyle.getPropertyValue("border-left")), + }; + + //Calculate the parent inner width and height + var parentInnerDimensions = _calculateInnerDimensions(parentComputedStyle); + var parentInnerWidth = parentInnerDimensions["width"]; + var parentInnerHeight = parentInnerDimensions["height"]; + + var box = { + width: element.offsetWidth, //Note: This is slightly larger than the jQuery version + height: element.offsetHeight, + }; + if (!box.width || !box.height) return element; + + + if (options.rotateText !== null) { + if (typeof options.rotateText !== "number") + throw "bigText error: rotateText value must be a number"; + var rotate = "rotate(" + options.rotateText + "deg)"; + style.webkitTransform = rotate; + style.msTransform = rotate; + style.MozTransform = rotate; + style.OTransform = rotate; + style.transform = rotate; + //calculating bounding box of the rotated element + var sine = Math.abs(Math.sin(options.rotateText * Math.PI / 180)); + var cosine = Math.abs(Math.cos(options.rotateText * Math.PI / 180)); + box.width = element.offsetWidth * cosine + element.offsetHeight * sine; + box.height = element.offsetWidth * sine + element.offsetHeight * cosine; + } + + var parentWidth = (parentInnerWidth - parentPadding.left - parentPadding.right); + var parentHeight = (parentInnerHeight - parentPadding.top - parentPadding.bottom); + var widthFactor = parentWidth / box.width; + var heightFactor = parentHeight / box.height; + var lineHeight; + + if (options.limitingDimension.toLowerCase() === "width") { + lineHeight = Math.floor(widthFactor * fontSize); + } else if (options.limitingDimension.toLowerCase() === "height") { + lineHeight = Math.floor(heightFactor * fontSize); + } else if (widthFactor < heightFactor) + lineHeight = Math.floor(widthFactor * fontSize); + else if (widthFactor >= heightFactor) + lineHeight = Math.floor(heightFactor * fontSize); + + var fontSize = lineHeight * options.fontSizeFactor; + if (fontSize < options.minimumFontSize) { + parentStyle.display = "flex"; + parentStyle.alignItems = "center"; + style.textAlign = "center"; + style.visibility = ""; + style.fontSize = options.minimumFontSize + "px"; + style.lineHeight = ""; + style.overflow = "hidden"; + style.textOverflow = "ellipsis"; + style.top = ""; + style.left = ""; + style.margin = ""; + return element; + } + if (options.maximumFontSize && fontSize > options.maximumFontSize) { + fontSize = options.maximumFontSize; + lineHeight = fontSize / options.fontSizeFactor; + } + + style.fontSize = Math.floor(fontSize) + "px"; + style.lineHeight = Math.ceil(lineHeight) + "px"; + style.marginBottom = "0px"; + style.marginRight = "0px"; + + // if (options.limitingDimension.toLowerCase() === "height") { + // //this option needs the font-size to be set already so computedStyle.getPropertyValue("width") returns the right size + // //this +4 is to compensate the rounding erros that can occur due to the calls to Math.floor in the centering code + // parentStyle.width = (parseInt(computedStyle.getPropertyValue("width")) + 4) + "px"; + // } + + //Calculate the inner width and height + var innerDimensions = _calculateInnerDimensions(computedStyle); + var innerWidth = innerDimensions["width"]; + var innerHeight = innerDimensions["height"]; + + switch (options.verticalAlign.toLowerCase()) { + case "top": + style.top = "0%"; + break; + case "bottom": + style.top = "100%"; + style.marginTop = Math.floor(-innerHeight) + "px"; + break; + default: + style.marginTop = Math.ceil((-innerHeight / 2)) + "px"; + break; + } + + switch (options.horizontalAlign.toLowerCase()) { + case "left": + style.left = "0%"; + break; + case "right": + style.left = "100%"; + style.marginLeft = Math.floor(-innerWidth) + "px"; + break; + default: + style.marginLeft = Math.ceil((-innerWidth / 2)) + "px"; + break; + } + + //shows the element after the work is done + style.visibility = "visible"; + + return element; +} diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx index d33d12603..f80ff5f94 100644 --- a/src/client/views/nodes/LabelBox.tsx +++ b/src/client/views/nodes/LabelBox.tsx @@ -1,17 +1,21 @@ -import { Property } from 'csstype'; -import { action, computed, makeObservable, trace } from 'mobx'; +import { action, computed, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import * as textfit from 'textfit'; -import { Field, FieldType } from '../../../fields/Doc'; -import { BoolCast, NumCast, StrCast } from '../../../fields/Types'; +import { Doc, DocListCast, Field, FieldType } from '../../../fields/Doc'; +import { List } from '../../../fields/List'; +import { listSpec } from '../../../fields/Schema'; +import { BoolCast, Cast, NumCast, StrCast } from '../../../fields/Types'; import { DocumentType } from '../../documents/DocumentTypes'; import { Docs } from '../../documents/Documents'; import { DragManager } from '../../util/DragManager'; +import { undoBatch } from '../../util/UndoManager'; +import { ContextMenu } from '../ContextMenu'; +import { ContextMenuProps } from '../ContextMenuItem'; import { ViewBoxBaseComponent } from '../DocComponent'; import { PinDocView, PinProps } from '../PinFuncs'; import { StyleProp } from '../StyleProp'; import { FieldView, FieldViewProps } from './FieldView'; +import BigText from './LabelBigText'; import './LabelBox.scss'; @observer @@ -19,15 +23,28 @@ export class LabelBox extends ViewBoxBaseComponent() { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(LabelBox, fieldKey); } + public static LayoutStringWithTitle(fieldStr: string, label?: string) { + return !label ? LabelBox.LayoutString(fieldStr) : ``; // e.g., "" + } private dropDisposer?: DragManager.DragDropDisposer; - private _timeout: NodeJS.Timeout | undefined; - _divRef: HTMLDivElement | null = null; + private _timeout: any; constructor(props: FieldViewProps) { super(props); makeObservable(this); } + componentDidMount() { + this._props.setContentViewBox?.(this); + } + componentWillUnMount() { + this._timeout && clearTimeout(this._timeout); + } + + @computed get Title() { + return Field.toString(this.dataDoc[this.fieldKey] as FieldType) || StrCast(this.Document.title); + } + protected createDropTarget = (ele: HTMLDivElement) => { this.dropDisposer?.(); if (ele) { @@ -35,27 +52,44 @@ export class LabelBox extends ViewBoxBaseComponent() { } }; - @computed get Title() { - return Field.toString(this.dataDoc[this.fieldKey] as FieldType) || StrCast(this.Document.title); - } - - @computed get backgroundColor() { - return this._props.styleProvider?.(this.Document, this._props, StyleProp.BackgroundColor) as string; - } - - componentDidMount() { - this._props.setContentViewBox?.(this); - } - componentWillUnMount() { - this._timeout && clearTimeout(this._timeout); + get paramsDoc() { + return Doc.AreProtosEqual(this.layoutDoc, this.dataDoc) ? this.dataDoc : this.layoutDoc; } + specificContextMenu = (): void => { + const funcs: ContextMenuProps[] = []; + !Doc.noviceMode && + funcs.push({ + description: 'Clear Script Params', + event: () => { + const params = Cast(this.paramsDoc['onClick-paramFieldKeys'], listSpec('string'), []); + params?.forEach(p => { + this.paramsDoc[p] = undefined; + }); + }, + icon: 'trash', + }); - specificContextMenu = (): void => {}; + funcs.length && ContextMenu.Instance.addItem({ description: 'OnClick...', noexpand: true, subitems: funcs, icon: 'mouse-pointer' }); + }; - drop = (/* e: Event, de: DragManager.DropEvent */) => { + @undoBatch + drop = (e: Event, de: DragManager.DropEvent) => { + const { docDragData } = de.complete; + const params = Cast(this.paramsDoc['onClick-paramFieldKeys'], listSpec('string'), []); + const missingParams = params?.filter(p => !this.paramsDoc[p]); + if (docDragData && missingParams?.includes((e.target as any).textContent)) { + this.paramsDoc[(e.target as any).textContent] = new List(docDragData.droppedDocuments.map((d, i) => (d.onDragStart ? docDragData.draggedDocuments[i] : d))); + e.stopPropagation(); + return true; + } return false; }; + @observable _mouseOver = false; + @computed get hoverColor() { + return this._mouseOver ? StrCast(this.layoutDoc._hoverBackgroundColor) : this._props.styleProvider?.(this.Document, this._props, StyleProp.BackgroundColor); + } + getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => { if (!pinProps) return this.Document; const anchor = Docs.Create.ConfigDocument({ title: StrCast(this.Document.title), annotationOn: this.Document }); @@ -70,92 +104,101 @@ export class LabelBox extends ViewBoxBaseComponent() { }; fitTextToBox = ( - r: HTMLElement | null | undefined - ): { - minFontSize: number; - maxFontSize: number; - multiLine: boolean; - alignHoriz: boolean; - alignVert: boolean; - detectMultiLine: boolean; - } => { - this._timeout && clearTimeout(this._timeout); - const textfitParams = { - minFontSize: NumCast(this.layoutDoc._label_minFontSize, 1), - maxFontSize: NumCast(this.layoutDoc._label_maxFontSize, 100), - multiLine: BoolCast(this.layoutDoc._singleLine, true) ? false : true, - alignHoriz: true, - alignVert: true, - detectMultiLine: true, + r: any + ): + | NodeJS.Timeout + | { + rotateText: null; + fontSizeFactor: number; + minimumFontSize: number; + maximumFontSize: number; + limitingDimension: string; + horizontalAlign: string; + verticalAlign: string; + textAlign: string; + singleLine: boolean; + whiteSpace: string; + } => { + const singleLine = BoolCast(this.layoutDoc._singleLine, true); + const params = { + rotateText: null, + fontSizeFactor: 1, + minimumFontSize: NumCast(this.layoutDoc._label_minFontSize, 8), + maximumFontSize: NumCast(this.layoutDoc._label_maxFontSize, 1000), + limitingDimension: 'both', + horizontalAlign: 'center', + verticalAlign: 'center', + textAlign: 'center', + singleLine, + whiteSpace: singleLine ? 'nowrap' : 'pre-wrap', }; - if (r) { - if (!r.offsetHeight || !r.offsetWidth) { - console.log("CAN'T FIT TO EMPTY BOX"); - this._timeout && clearTimeout(this._timeout); - this._timeout = setTimeout(() => this.fitTextToBox(r)); - return textfitParams; - } - textfit(r, textfitParams); + this._timeout = undefined; + if (!r) return params; + if (!r.offsetHeight || !r.offsetWidth) { + this._timeout = setTimeout(() => this.fitTextToBox(r)); + return this._timeout; } - return textfitParams; + const parent = r.parentNode; + const parentStyle = parent.style; + parentStyle.display = ''; + parentStyle.alignItems = ''; + r.setAttribute('style', ''); + r.style.width = singleLine ? '' : '100%'; + + r.style.textOverflow = 'ellipsis'; + r.style.overflow = 'hidden'; + BigText(r, params); + return params; }; + // (!missingParams || !missingParams.length ? "" : "(" + missingParams.map(m => m + ":").join(" ") + ")") render() { - trace(); - const boxParams = this.fitTextToBox(undefined); // this causes mobx to trigger re-render when data changes - const label = this.Title.startsWith('#') ? null : this.Title; + const boxParams = this.fitTextToBox(null); // this causes mobx to trigger re-render when data changes + const params = Cast(this.paramsDoc['onClick-paramFieldKeys'], listSpec('string'), []); + const missingParams = params?.filter(p => !this.paramsDoc[p]); + params?.map(p => DocListCast(this.paramsDoc[p])); // bcz: really hacky form of prefetching ... + const label = this.Title; return ( -
+
{ + this._mouseOver = false; + })} + // eslint-disable-next-line jsx-a11y/mouse-events-have-key-events + onMouseOver={action(() => { + this._mouseOver = true; + })} + ref={this.createDropTarget} + onContextMenu={this.specificContextMenu} + style={{ boxShadow: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BoxShadow) }}>
-
{ - e.stopPropagation(); - })} - onKeyUp={action(e => { - e.stopPropagation(); - if (e.key === 'Enter') { - this.dataDoc[this.fieldKey] = this._divRef?.innerText ?? ''; - setTimeout(() => this._props.select(false)); - } - })} - onBlur={() => { - this.dataDoc[this.fieldKey] = this._divRef?.innerText ?? ''; - }} - contentEditable={this._props.onClickScript?.() ? false : true} - ref={r => { - this._divRef = r; - this.fitTextToBox(r); - if (this._props.isSelected() && this._divRef) { - const range = document.createRange(); - range.setStart(this._divRef, this._divRef.childNodes.length); - range.setEnd(this._divRef, this._divRef.childNodes.length); - const sel = window.getSelection(); - sel?.removeAllRanges(); - sel?.addRange(range); - } - }}> - {label} -
+ this.fitTextToBox(r))}> + {label.startsWith('#') ? null : label.replace(/([^a-zA-Z])/g, '$1\u200b')} + +
+
+ {!missingParams?.length + ? null + : missingParams.map(m => ( +
+ {m} +
+ ))}
); diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss index 7bca1230f..6e24b2931 100644 --- a/src/client/views/nodes/PDFBox.scss +++ b/src/client/views/nodes/PDFBox.scss @@ -20,15 +20,27 @@ top: 0; left: 0; + .pdfBox-sidebarBtn-container { + display: flex; + flex-direction: row; + position: absolute; + width: 53px; + height: 33px; + right: 5px; + align-items: center; + justify-content: space-between; + z-index: 1; + } + // glr: This should really be the same component as text and PDFs .pdfBox-sidebarBtn { background: $black; height: 25px; width: 25px; - right: 5px; + // right: 5px; color: $white; display: flex; - position: absolute; + // position: absolute; align-items: center; justify-content: center; border-radius: 3px; diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 8db68ddfe..782df99f6 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -1,6 +1,8 @@ /* eslint-disable jsx-a11y/no-static-element-interactions */ /* eslint-disable jsx-a11y/control-has-associated-label */ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { IconButton } from 'browndash-components'; +import { black } from 'colors'; import { action, computed, IReactionDisposer, makeObservable, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as Pdfjs from 'pdfjs-dist'; @@ -503,17 +505,30 @@ export class PDFBox extends ViewBoxAnnotatableComponent() { } @computed get sidebarHandle() { return ( -
this.sidebarBtnDown(e, true)}> - +
+
+ {/* // onPointerDown={e => this.sidebarBtnDown(e, true)} */} + } onPointerDown={e => this.sidebarBtnDown(e, true)} /> +
+
+ } onPointerDown={e => this.sidebarBtnDown(e, true)} /> +
); } diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index da947face..8835ea5e7 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -45,7 +45,6 @@ import { LinkInfo } from './LinkDocPreview'; import { OpenWhere } from './OpenWhere'; import './WebBox.scss'; -// eslint-disable-next-line @typescript-eslint/no-var-requires const { CreateImage } = require('./WebBoxRenderer'); @observer diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 5b435e44a..9f2a9b8e1 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -87,9 +87,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent(['Audio Tags', 'Text from Others', 'Todo Items', 'Important Items', 'Disagree Items', 'Ignore Items']); - static _highlightStyleSheet = addStyleSheet(); - static _bulletStyleSheet = addStyleSheet(); - static _userStyleSheet = addStyleSheet(); + static _highlightStyleSheet: any = addStyleSheet(); + static _bulletStyleSheet: any = addStyleSheet(); + static _userStyleSheet: any = addStyleSheet(); static _hadSelection: boolean = false; private _selectionHTML: string | undefined; private _sidebarRef = React.createRef(); @@ -384,7 +384,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent ) : ( -
setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => DocumentView.SelectView(this.DocumentView?.(), false), true)}> +
setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => DocumentView.SelectView(this.DocumentView?.()!, false), true)}> { public StartDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction; public StartCropDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction; public Highlight: (color: string) => Opt = (/* color: string */) => undefined; + public Tape: (color: string) => Opt = (/* color: string */) => undefined; public GetAnchor: (savedAnnotations: Opt>, addAsAnnotation: boolean) => Opt = emptyFunction; public Delete: () => void = unimplementedFunction; public PinToPres: () => void = unimplementedFunction; @@ -172,6 +173,12 @@ export class AnchorMenu extends AntimodeMenu { AnchorMenu.Instance.fadeOut(true); }; + @action + tapeClicked = () => { + this.Tape(this.highlightColor); + // AnchorMenu.Instance.fadeOut(true); + }; + @computed get highlighter() { return ( @@ -182,6 +189,13 @@ export class AnchorMenu extends AntimodeMenu { colorPicker={this.highlightColor} color={SettingsManager.userColor} /> + } + onClick={this.tapeClicked} + colorPicker={this.highlightColor} + color={SettingsManager.userColor} + /> ); diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx index 3bd42873c..7dd4047c1 100644 --- a/src/client/views/pdf/Annotation.tsx +++ b/src/client/views/pdf/Annotation.tsx @@ -111,7 +111,6 @@ export class Annotation extends ObservableReactComponent { outline = () => (this.linkHighlighted ? 'solid 1px lightBlue' : undefined); background = () => (this._props.annoDoc[Highlight] ? 'orange' : StrCast(this._props.annoDoc.backgroundColor)); render() { - const forceRenderHack = [this.background(), this.outline(), this.opacity()]; // forces a re-render when these change -- because RegionAnnotation doesn't do this internally.. return (
{StrListCast(this._props.annoDoc.text_inlineAnnotations) diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index fa5e5cedb..fbe3518ec 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -33,7 +33,7 @@ import './PDFViewer.scss'; // pdfjsLib.GlobalWorkerOptions.workerSrc = `/assets/pdf.worker.js`; // The workerSrc property shall be specified. -Pdfjs.GlobalWorkerOptions.workerSrc = 'https://unpkg.com/pdfjs-dist@4.4.168/build/pdf.worker.mjs'; +Pdfjs.GlobalWorkerOptions.workerSrc = 'https://unpkg.com/pdfjs-dist@4.3.136/build/pdf.worker.mjs'; interface IViewerProps extends FieldViewProps { pdfBox: PDFBox; @@ -56,7 +56,7 @@ interface IViewerProps extends FieldViewProps { */ @observer export class PDFViewer extends ObservableReactComponent { - static _annotationStyle = addStyleSheet(); + static _annotationStyle: any = addStyleSheet(); constructor(props: IViewerProps) { super(props); @@ -65,6 +65,7 @@ export class PDFViewer extends ObservableReactComponent { @observable _pageSizes: { width: number; height: number }[] = []; @observable _savedAnnotations = new ObservableMap(); + @observable _savedTapes = new ObservableMap(); @observable _textSelecting = true; @observable _showWaiting = true; @observable Index: number = -1; @@ -524,8 +525,7 @@ export class PDFViewer extends ObservableReactComponent { if (doc instanceof Doc && property === StyleProp.PointerEvents) { if (this.inlineTextAnnotations.includes(doc) || this._props.isContentActive() === false) return 'none'; const isInk = doc.layout_isSvg && !props?.LayoutTemplateString; - if (isInk) return 'visiblePainted'; - //return isInk ? 'visiblePainted' : 'all'; + return isInk ? 'visiblePainted' : 'all'; } return this._props.styleProvider?.(doc, props, property); }; @@ -582,6 +582,7 @@ export class PDFViewer extends ObservableReactComponent { return
; } savedAnnotations = () => this._savedAnnotations; + savedTapes = () => this._savedTapes; addDocumentWrapper = (doc: Doc | Doc[]) => this._props.addDocument!(doc); render() { TraceMobx(); @@ -615,6 +616,7 @@ export class PDFViewer extends ObservableReactComponent { docView={this._props.pdfBox.DocumentView!} finishMarquee={this.finishMarquee} savedAnnotations={this.savedAnnotations} + savedTapes={this.savedTapes} selectionText={this.selectionText} annotationLayer={this._annotationLayer.current} marqueeContainer={this._mainCont.current} diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index e9edb8363..c3e96ab49 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -1,5 +1,3 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/no-namespace */ /* eslint-disable default-param-last */ /* eslint-disable no-use-before-define */ import { action, computed, makeObservable, observable, ObservableMap, ObservableSet, runInAction } from 'mobx'; @@ -176,6 +174,53 @@ export function updateCachedAcls(doc: Doc) { return undefined; } +export function ActiveInkPen(): Doc { return Doc.UserDoc(); } // prettier-ignore +export function ActiveInkColor(): string { return StrCast(ActiveInkPen()?.activeInkColor, 'black'); } // prettier-ignore +export function ActiveFillColor(): string { return StrCast(ActiveInkPen()?.activeFillColor, ''); } // prettier-ignore +export function ActiveIsInkMask(): boolean { return BoolCast(ActiveInkPen()?.activeIsInkMask, false); } // prettier-ignore +export function ActiveInkHideTextLabels(): boolean { return BoolCast(ActiveInkPen().activeInkHideTextLabels, false); } // prettier-ignore +export function ActiveArrowStart(): string { return StrCast(ActiveInkPen()?.activeArrowStart, ''); } // prettier-ignore +export function ActiveArrowEnd(): string { return StrCast(ActiveInkPen()?.activeArrowEnd, ''); } // prettier-ignore +export function ActiveArrowScale(): number { return NumCast(ActiveInkPen()?.activeArrowScale, 1); } // prettier-ignore +export function ActiveDash(): string { return StrCast(ActiveInkPen()?.activeDash, '0'); } // prettier-ignore +export function ActiveInkWidth(): number { return Number(ActiveInkPen()?.activeInkWidth); } // prettier-ignore +export function ActiveInkBezierApprox(): string { return StrCast(ActiveInkPen()?.activeInkBezier); } // prettier-ignore +export function ActiveEraserWidth(): number { return NumCast(ActiveInkPen()?.eraserWidth); } // prettier-ignore + +export function SetActiveInkWidth(width: string): void { + !isNaN(parseInt(width)) && ActiveInkPen() && (ActiveInkPen().activeInkWidth = width); +} +export function SetActiveBezierApprox(bezier: string): void { + ActiveInkPen() && (ActiveInkPen().activeInkBezier = isNaN(parseInt(bezier)) ? '' : bezier); +} +export function SetActiveInkColor(value: string) { + ActiveInkPen() && (ActiveInkPen().activeInkColor = value); +} +export function SetActiveIsInkMask(value: boolean) { + ActiveInkPen() && (ActiveInkPen().activeIsInkMask = value); +} +export function SetActiveInkHideTextLabels(value: boolean) { + ActiveInkPen() && (ActiveInkPen().activeInkHideTextLabels = value); +} +export function SetActiveFillColor(value: string) { + ActiveInkPen() && (ActiveInkPen().activeFillColor = value); +} +export function SetActiveArrowStart(value: string) { + ActiveInkPen() && (ActiveInkPen().activeArrowStart = value); +} +export function SetActiveArrowEnd(value: string) { + ActiveInkPen() && (ActiveInkPen().activeArrowEnd = value); +} +export function SetActiveArrowScale(value: number) { + ActiveInkPen() && (ActiveInkPen().activeArrowScale = value); +} +export function SetActiveDash(dash: string): void { + !isNaN(parseInt(dash)) && ActiveInkPen() && (ActiveInkPen().activeDash = dash); +} +export function SetEraserWidth(width: number): void { + !isNaN(width) && ActiveInkPen() && (ActiveInkPen().eraserWidth = width); +} + @scriptingGlobal @Deserializable('Doc', updateCachedAcls, ['id']) export class Doc extends RefField { diff --git a/src/fields/InkField.ts b/src/fields/InkField.ts index 32abf0076..123d32301 100644 --- a/src/fields/InkField.ts +++ b/src/fields/InkField.ts @@ -17,6 +17,7 @@ export enum InkTool { Stamp = 'stamp', Write = 'write', PresentationPin = 'presentationpin', + SmartDraw = 'smartdraw', } export type Segment = Array; -- cgit v1.2.3-70-g09d2 From 82a619a446c94313f06d86707be28041632c59ea Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 2 Sep 2024 12:19:55 -0400 Subject: removed unused ActiveInk stuff. --- src/fields/Doc.ts | 47 ----------------------------------------------- 1 file changed, 47 deletions(-) (limited to 'src/fields') diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index a3e765659..e7d6a9460 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -181,53 +181,6 @@ export function updateCachedAcls(doc: Doc) { return undefined; } -export function ActiveInkPen(): Doc { return Doc.UserDoc(); } // prettier-ignore -export function ActiveInkColor(): string { return StrCast(ActiveInkPen()?.activeInkColor, 'black'); } // prettier-ignore -export function ActiveFillColor(): string { return StrCast(ActiveInkPen()?.activeFillColor, ''); } // prettier-ignore -export function ActiveIsInkMask(): boolean { return BoolCast(ActiveInkPen()?.activeIsInkMask, false); } // prettier-ignore -export function ActiveInkHideTextLabels(): boolean { return BoolCast(ActiveInkPen().activeInkHideTextLabels, false); } // prettier-ignore -export function ActiveArrowStart(): string { return StrCast(ActiveInkPen()?.activeArrowStart, ''); } // prettier-ignore -export function ActiveArrowEnd(): string { return StrCast(ActiveInkPen()?.activeArrowEnd, ''); } // prettier-ignore -export function ActiveArrowScale(): number { return NumCast(ActiveInkPen()?.activeArrowScale, 1); } // prettier-ignore -export function ActiveDash(): string { return StrCast(ActiveInkPen()?.activeDash, '0'); } // prettier-ignore -export function ActiveInkWidth(): number { return Number(ActiveInkPen()?.activeInkWidth); } // prettier-ignore -export function ActiveInkBezierApprox(): string { return StrCast(ActiveInkPen()?.activeInkBezier); } // prettier-ignore -export function ActiveEraserWidth(): number { return NumCast(ActiveInkPen()?.eraserWidth); } // prettier-ignore - -export function SetActiveInkWidth(width: string): void { - !isNaN(parseInt(width)) && ActiveInkPen() && (ActiveInkPen().activeInkWidth = width); -} -export function SetActiveBezierApprox(bezier: string): void { - ActiveInkPen() && (ActiveInkPen().activeInkBezier = isNaN(parseInt(bezier)) ? '' : bezier); -} -export function SetActiveInkColor(value: string) { - ActiveInkPen() && (ActiveInkPen().activeInkColor = value); -} -export function SetActiveIsInkMask(value: boolean) { - ActiveInkPen() && (ActiveInkPen().activeIsInkMask = value); -} -export function SetActiveInkHideTextLabels(value: boolean) { - ActiveInkPen() && (ActiveInkPen().activeInkHideTextLabels = value); -} -export function SetActiveFillColor(value: string) { - ActiveInkPen() && (ActiveInkPen().activeFillColor = value); -} -export function SetActiveArrowStart(value: string) { - ActiveInkPen() && (ActiveInkPen().activeArrowStart = value); -} -export function SetActiveArrowEnd(value: string) { - ActiveInkPen() && (ActiveInkPen().activeArrowEnd = value); -} -export function SetActiveArrowScale(value: number) { - ActiveInkPen() && (ActiveInkPen().activeArrowScale = value); -} -export function SetActiveDash(dash: string): void { - !isNaN(parseInt(dash)) && ActiveInkPen() && (ActiveInkPen().activeDash = dash); -} -export function SetEraserWidth(width: number): void { - !isNaN(width) && ActiveInkPen() && (ActiveInkPen().eraserWidth = width); -} - @scriptingGlobal @Deserializable('Doc', (obj: unknown) => updateCachedAcls(obj as Doc), ['id']) export class Doc extends RefField { -- cgit v1.2.3-70-g09d2 From 42419cd85b7dc2aec0695f2b29b2f707ae7e36e2 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 27 Sep 2024 14:19:40 -0400 Subject: fixed linting. cleaned up scribble erase code and fixed several issues with determining if cusp intersections amounted to a scribble. also fixed recognition of lines to not recognize scribbles. --- eslint.config.mjs | 2 +- package-lock.json | 14 ++ src/.DS_Store | Bin 14340 -> 10244 bytes src/Utils.ts | 3 +- src/client/views/GestureOverlay.tsx | 276 +++++++-------------- src/client/views/InkingStroke.tsx | 6 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 24 +- src/fields/InkField.ts | 20 ++ 8 files changed, 137 insertions(+), 208 deletions(-) (limited to 'src/fields') diff --git a/eslint.config.mjs b/eslint.config.mjs index aebdc20d0..f7063caa5 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -48,7 +48,7 @@ export default [ 'no-return-assign': 'error', 'no-await-in-loop': 'error', 'no-loop-func': 'error', - '@typescript-eslint/no-cond-assign': 'error', + 'no-cond-assign': 'error', 'no-use-before-define': 'error', '@typescript-eslint/no-explicit-any': 'error', 'no-restricted-globals': ['error', 'event'], diff --git a/package-lock.json b/package-lock.json index fac1cc20b..727b22f34 100644 --- a/package-lock.json +++ b/package-lock.json @@ -130,6 +130,7 @@ "https": "^1.0.0", "https-browserify": "^1.0.0", "i": "^0.3.7", + "iink-ts": "^1.0.5", "image-data-uri": "^2.0.1", "image-size": "^1.0.2", "image-size-stream": "^1.1.0", @@ -20506,6 +20507,14 @@ "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==" }, + "node_modules/iink-ts": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/iink-ts/-/iink-ts-1.0.5.tgz", + "integrity": "sha512-LAWWPvgcsLtouI9ExVijZbPGIWMfJtVCcuznbIDyboaPb+cHYsftcuJdjDU7TQwIpGP4hmcjNQA57XPCw4MK2A==", + "dependencies": { + "json-css": "^1.5.6" + } + }, "node_modules/image-data-uri": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/image-data-uri/-/image-data-uri-2.0.1.tgz", @@ -21989,6 +21998,11 @@ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" }, + "node_modules/json-css": { + "version": "1.5.6", + "resolved": "https://registry.npmjs.org/json-css/-/json-css-1.5.6.tgz", + "integrity": "sha512-B/0T0OxZH9tSb93tXV6VOYtXqrPz/Vgz2QrCT/4NXen8HGElYkYr9V+8IrSVTMj/ftxa8cG1kcu7f3iAMlaFlQ==" + }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", diff --git a/src/.DS_Store b/src/.DS_Store index 19eea5fce..b91453587 100644 Binary files a/src/.DS_Store and b/src/.DS_Store differ diff --git a/src/Utils.ts b/src/Utils.ts index 0590c6930..724725c23 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-namespace */ import * as uuid from 'uuid'; export function clamp(n: number, lower: number, upper: number) { @@ -205,7 +204,6 @@ export function intersectRect(r1: { left: number; top: number; width: number; he } export function stringHash(s?: string) { - // eslint-disable-next-line no-bitwise return !s ? undefined : Math.abs(s.split('').reduce((a, b) => (n => n & n)((a << 5) - a + b.charCodeAt(0)), 0)); } @@ -254,6 +252,7 @@ export namespace JSONUtils { try { results = JSON.parse(source); } catch (e) { + console.log('JSONparse error: ', e); results = source; } return results; diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index 1cd6de55c..e9e12fc3b 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -1,10 +1,9 @@ import * as fitCurve from 'fit-curve'; -import { Bezier } from 'bezier-js'; import { action, computed, makeObservable, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { returnEmptyFilter, returnEmptyString, returnFalse, setupMoveUpEvents } from '../../ClientUtils'; -import { emptyFunction } from '../../Utils'; +import { emptyFunction, intersectRect } from '../../Utils'; import { Doc, Opt, returnEmptyDoclist } from '../../fields/Doc'; import { InkData, InkField, InkTool } from '../../fields/InkField'; import { NumCast } from '../../fields/Types'; @@ -16,6 +15,7 @@ import { Docs } from '../documents/Documents'; import { InteractionUtils } from '../util/InteractionUtils'; import { ScriptingGlobals } from '../util/ScriptingGlobals'; import { Transform } from '../util/Transform'; +import { undoable } from '../util/UndoManager'; import './GestureOverlay.scss'; import { InkingStroke } from './InkingStroke'; import { ObservableReactComponent } from './ObservableReactComponent'; @@ -37,7 +37,6 @@ import { SetActiveInkColor, SetActiveInkWidth, } from './nodes/DocumentView'; -import { docs } from 'googleapis/build/src/apis/docs'; export enum ToolglassTools { InkToText = 'inktotext', IgnoreGesture = 'ignoregesture', @@ -53,7 +52,9 @@ interface GestureOverlayProps { * drew or perform the gesture's action */ export class GestureOverlay extends ObservableReactComponent> { + // eslint-disable-next-line no-use-before-define static Instance: GestureOverlay; + // eslint-disable-next-line no-use-before-define static Instances: GestureOverlay[] = []; @observable public InkShape: Opt = undefined; @@ -79,10 +80,6 @@ export class GestureOverlay extends ObservableReactComponent(); - private _d1: Doc | undefined; - private _inkToTextDoc: Doc | undefined; - private thumbIdentifier?: number; - private pointerIdentifier?: number; constructor(props: GestureOverlayProps) { super(props); @@ -136,119 +133,67 @@ export class GestureOverlay extends ObservableReactComponent view.ComponentView instanceof CollectionFreeFormView); - console.log(inkData.map(ink => ({ x: ink.X, y: ink.Y }))); - let intersectArray: boolean[] = []; - const cuspArray = this.getCusps(inkData); - console.log(cuspArray.length); - for (let i = 0; i < cuspArray.length; i++) { - intersectArray[i] = false; - } - const docsToDelete: Doc[] = []; - let childDocs = (ffView?.ComponentView as CollectionFreeFormView).childDocs.slice(0, -1); - childDocs.filter(doc => doc.type === 'ink').map(doc => DocumentView.getDocumentView(doc, DocumentView.getDocumentView(doc))); - if ((ffView?.ComponentView as CollectionFreeFormView).childDocs) { - for (const doc of childDocs) { - const otherInk = DocumentView.getDocumentView(doc)?.ComponentView as InkingStroke; - const { inkData: otherInkData } = otherInk?.inkScaledData() ?? { inkData: [] }; - const otherScreenPts = otherInkData.map(point => otherInk.ptToScreen(point)); - if (this.isRectangleOverlap(this.getExtremeCoordinates(otherScreenPts), this.getExtremeCoordinates(inkData))) { - const intersects = this.doInksIntersect(inkData, otherScreenPts); - intersects.forEach(intersect => { - let percentage = ''; - if (intersect.includes('/')) { - const leftOfSlash = intersect.split('/')[0]; - percentage = leftOfSlash; - } else { - percentage = intersect; - } - intersectArray[Math.floor((percentage as unknown as number) * cuspArray.length)] = true; - const docsInBoundingBox = this.docsInBoundingBox(doc, childDocs); - childDocs = childDocs.filter(doc => !docsInBoundingBox.includes(doc)); - docsToDelete.push(...docsInBoundingBox); - docsToDelete.push(doc); + isScribble = (ffView: CollectionFreeFormView, cuspArray: { X: number; Y: number }[], scribbleStroke: { X: number; Y: number }[]) => { + const intersectArray = cuspArray.map(() => false); + const scribbleBounds = InkField.getBounds(scribbleStroke); + const docsToDelete = ffView.childDocs + .map(doc => DocumentView.getDocumentView(doc)) + .filter(dv => dv?.ComponentView instanceof InkingStroke) + .map(dv => dv?.ComponentView as InkingStroke) + .filter(otherInk => { + const otherScreenPts = otherInk.inkScaledData?.().inkData.map(otherInk.ptToScreen); + if (intersectRect(InkField.getBounds(otherScreenPts), scribbleBounds)) { + const intersects = this.findInkIntersections(scribbleStroke, otherScreenPts).map(intersect => { + const percentage = intersect.split('/')[0]; + intersectArray[Math.floor(Number(percentage) * cuspArray.length)] = true; }); + return intersects.length > 0; } - } - console.log(intersectArray); - if (intersectArray.length > 3 && this.determineIfScribble(intersectArray)) { - const uniqueArray = Array.from(new Set(docsToDelete)); - console.log(uniqueArray.length); - console.log('is a scribble'); - docsToDelete.forEach(doc => { - ffView?.ComponentView?.removeDocument?.(doc); - }); - this._points = []; - return true; - } - } - return false; - } - /** - * this will return all the docs overlapping with the maindocs bounding box - * @param mainDoc the bounding box of this doc will be used - * @param childDocs the array of all docs in collection - * @returns - */ - docsInBoundingBox(mainDoc: Doc, childDocs: Doc[]): Doc[] { - return childDocs.filter( - doc => - typeof doc.x === 'number' && - typeof doc.y === 'number' && - typeof doc.width === 'number' && - typeof doc.height === 'number' && - typeof mainDoc.x === 'number' && - typeof mainDoc.y === 'number' && - typeof mainDoc.width === 'number' && - typeof mainDoc.height === 'number' && - doc.x < mainDoc.x + mainDoc.width && - doc.x + doc.width > mainDoc.x && - doc.y < mainDoc.y + mainDoc.height && - doc.y + doc.height > mainDoc.y - ); - } + }); + return !this.determineIfScribble(intersectArray) ? undefined : + [ ...docsToDelete.map(stroke => stroke.Document), + // bcz: NOTE: docsInBoundingBox test should be replaced with a docsInConvexHull test + ...this.docsInBoundingBox({ topLeft : ffView.ScreenToContentsXf().transformPoint(scribbleBounds.left, scribbleBounds.top), + bottomRight: ffView.ScreenToContentsXf().transformPoint(scribbleBounds.right,scribbleBounds.bottom)}, + ffView.childDocs.filter(doc => !docsToDelete.map(s => s.Document).includes(doc)) )]; // prettier-ignore + }; /** - * this method determines if what the user drew is a scribble based on certain criteria. - * @param cuspBooleanArray will take in an array of booleans tht represent what sections(seperated by a cusp) in the scribble - * has an object in it. - * @returns + * Returns all docs in array that overlap bounds. Note that the bounds should be given in screen space coordinates. + * @param boundingBox screen space bounding box + * @param childDocs array of docs to test against bounding box + * @returns list of docs that overlap rect */ - determineIfScribble(cuspBooleanArray: boolean[]) { - if (!cuspBooleanArray) { - return false; - } - const quarterArrayLength = Math.ceil((cuspBooleanArray.length - 2) * 0.3); - let hasObjectInFirstAndLast25 = true; - for (let i = 0; i < quarterArrayLength; i++) { - if (cuspBooleanArray[i] == false || cuspBooleanArray[cuspBooleanArray.length - 1 - i] == false) { - hasObjectInFirstAndLast25 = false; - } - } - const trueCount = cuspBooleanArray.filter(value => value).length; - const percentageTrues = trueCount / cuspBooleanArray.length; - return percentageTrues >= 0.5 || hasObjectInFirstAndLast25; - } + docsInBoundingBox = (boundingBox: { topLeft: number[]; bottomRight: number[] }, childDocs: Doc[]): Doc[] => { + const rect = { left: boundingBox.topLeft[0], top: boundingBox.topLeft[1], width: boundingBox.bottomRight[0] - boundingBox.topLeft[0], height: boundingBox.bottomRight[1] - boundingBox.topLeft[1] }; + return childDocs.filter(doc => intersectRect(rect, { left: NumCast(doc.x), top: NumCast(doc.y), width: NumCast(doc._width), height: NumCast(doc._height) })); + }; /** - * determines if two rectangles are overlapping each other - * @param rect1 the rectangle object has has a minX,maxX,minY, and maxY - * @param rect2 + * Determines if what the array of cusp/intersection data corresponds to a scribble. + * true if there are at least 4 cusps and either: + * 1) the initial and final quarters of the array contain objects + * 2) or half of the cusps contain objects + * @param intersectArray array of booleans coresponding to which scribble sections (regions separated by a cusp) contain Docs * @returns */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any - isRectangleOverlap(rect1: any, rect2: any): boolean { - const noOverlap = rect1.maxX < rect2.minX || rect1.minX > rect2.maxX || rect1.maxY < rect2.minY || rect1.minY > rect2.maxY; - - return !noOverlap; - } + determineIfScribble = (intersectArray: boolean[]) => { + const quarterArrayLength = Math.ceil(intersectArray.length / 3.9); // use 3.9 instead of 4 to work better with strokes with only 4 cusps + const { start, end } = intersectArray.reduce((res, val, i) => ({ // test for scribbles at start and end of scribble stroke + start: res.start || (val && i <= quarterArrayLength), + end: res.end || (val && i >= intersectArray.length - quarterArrayLength) + }), { start: false, end: false }); // prettier-ignore + + const percentCuspsWithContent = intersectArray.filter(value => value).length / intersectArray.length; + return intersectArray.length > 3 && (percentCuspsWithContent >= 0.5 || (start && end)); + }; /** * determines if inks intersect * @param line is pointData @@ -260,32 +205,24 @@ export class GestureOverlay extends ObservableReactComponent view.ComponentView instanceof CollectionFreeFormView); - if (ffView && ffView.ComponentView instanceof CollectionFreeFormView) { - for (let i = 0; i < scribble.length - 3; i += 4) { - // iterate over each segment of bezier curve - for (let j = 0; j < inkStroke.length - 3; j += 4) { - const scribbleCurve: Bezier = InkField.Segment(scribble, i); - const strokeCurve: Bezier = InkField.Segment(inkStroke, j); - const points = strokeCurve.points.map(point => ({ - X: point.x, - Y: point.y, - })); - if (ffView && ffView.ComponentView instanceof CollectionFreeFormView && this.isRectangleOverlap(extremeScribble, this.getExtremeCoordinates(points))) { - const result = (ffView.ComponentView as CollectionFreeFormView).bintersects(scribbleCurve, strokeCurve)[0]; - if (result !== undefined) { - intersectArray.push(result.toString()); - } + findInkIntersections = (scribble: InkData, inkStroke: InkData): string[] => { + const intersectArray: string[] = []; + const scribbleBounds = InkField.getBounds(scribble); + for (let i = 0; i < scribble.length - 3; i += 4) { // for each segment of scribble + for (let j = 0; j < inkStroke.length - 3; j += 4) { // for each segment of ink stroke + const scribbleSeg = InkField.Segment(scribble, i); + const strokeSeg = InkField.Segment(inkStroke, j); + const strokeBounds = InkField.getBounds(strokeSeg.points.map(pt => ({ X: pt.x, Y: pt.y }))); + if (intersectRect(scribbleBounds, strokeBounds)) { + const result = InkField.bintersects(scribbleSeg, strokeSeg)[0]; + if (result !== undefined) { + intersectArray.push(result.toString()); } } - } - } + } // prettier-ignore + } // prettier-ignore return intersectArray; - } + }; dryInk = () => { const newPoints = this._points.reduce((p, pts) => { p.push([pts.X, pts.Y]); @@ -309,6 +246,7 @@ export class GestureOverlay extends ObservableReactComponent { + const ffView = DocumentView.DownDocView?.ComponentView instanceof CollectionFreeFormView && DocumentView.DownDocView.ComponentView; DocumentView.DownDocView = undefined; if (this._points.length > 1) { const B = this.svgBounds; @@ -320,31 +258,34 @@ export class GestureOverlay extends ObservableReactComponent { switch (name) { case Gestures.Line: + if (cuspArray.length > 2) return undefined; + // eslint-disable-next-line no-fallthrough case Gestures.Triangle: case Gestures.Rectangle: case Gestures.Circle: this.makeBezierPolygon(Name, true); return this.dispatchGesture(name); case Gestures.RightAngle: - return this.convertToText().length > 0; + return ffView && this.convertToText(ffView).length > 0; default: } })(Score < 0.7 ? Gestures.Stroke : (Name as Gestures)); // if no gesture (or if the gesture was unsuccessful), "dry" the stroke into an ink document + if (!actionPerformed) { - this.dryInk(); - setTimeout(() => { - const ffView = DocumentView.allViews().find(view => view.ComponentView instanceof CollectionFreeFormView); - const scribbleInk = (ffView?.ComponentView as CollectionFreeFormView).childDocs[(ffView?.ComponentView as CollectionFreeFormView).childDocs.length - 1]; - if (this.isScribble((DocumentView.getDocumentView(scribbleInk)?.ComponentView as InkingStroke).inkScaledData().inkData)) { - ffView?.ComponentView?.removeDocument?.(scribbleInk); - } - }, 1); + const scribbledOver = ffView && this.isScribble(ffView, cuspArray, this._points); + if (scribbledOver) { + undoable(() => ffView.removeDocument(scribbledOver), 'scribble erase')(); + } else { + this.dryInk(); + } } } this._points.length = 0; @@ -353,21 +294,19 @@ export class GestureOverlay extends ObservableReactComponent view.ComponentView instanceof CollectionFreeFormView); + convertToText = (ffView: CollectionFreeFormView) => { let minX = 999999999; let maxX = -999999999; let minY = 999999999; let maxY = -999999999; const textDocs: Doc[] = []; - (ffView?.ComponentView as CollectionFreeFormView).childDocs + ffView.childDocs .filter(doc => doc.type === DocumentType.COL) .forEach(doc => { if (typeof doc.width === 'number' && typeof doc.height === 'number' && typeof doc.x === 'number' && typeof doc.y === 'number') { const bounds = DocumentView.getDocumentView(doc)?.getBounds; if (bounds) { - const rect1 = { minX: bounds.left, maxX: bounds.right, minY: bounds.top, maxY: bounds.bottom }; - if (this.isRectangleOverlap(rect1, this.getExtremeCoordinates(this._points))) { + if (intersectRect({ ...bounds, width: bounds.right - bounds.left, height: bounds.bottom - bounds.top }, InkField.getBounds(this._points))) { if (doc.x < minX) { minX = doc.x; } @@ -383,9 +322,9 @@ export class GestureOverlay extends ObservableReactComponent { - if (coord.X < minX) minX = coord.X; - if (coord.X > maxX) maxX = coord.X; - if (coord.Y < minY) minY = coord.Y; - if (coord.Y > maxY) maxY = coord.Y; - }); - return { - minX, - maxX, - minY, - maxY, - }; - } /** * takes in three points and then determines the angle of the points. used to determine if the cusp * is sharp enoug @@ -770,6 +683,7 @@ export class GestureOverlay extends ObservableReactComponent { GestureOverlay.Instance.SavedColor = ActiveInkColor(); @@ -782,6 +696,7 @@ ScriptingGlobals.add(function setPen(width: string, color: string, fill: string, SetActiveDash(dash); }); }); +// eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function resetPen() { runInAction(() => { SetActiveInkColor(GestureOverlay.Instance.SavedColor ?? 'rgb(0, 0, 0)'); @@ -789,6 +704,7 @@ ScriptingGlobals.add(function resetPen() { }); }, 'resets the pen tool'); ScriptingGlobals.add( + // eslint-disable-next-line prefer-arrow-callback function createText(text: string, X: number, Y: number) { GestureOverlay.Instance.dispatchGesture(Gestures.Text, [{ X, Y }], text); }, diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index f04e3eece..177400882 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -49,7 +49,7 @@ import { PinDocView, PinProps } from './PinFuncs'; import { StyleProp } from './StyleProp'; import { ViewBoxInterface } from './ViewBoxInterface'; -// eslint-disable-next-line @typescript-eslint/no-var-requires +// eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-require-imports const { INK_MASK_SIZE } = require('./global/globalCssVariables.module.scss'); // prettier-ignore @observer @@ -107,10 +107,10 @@ export class InkingStroke extends ViewBoxAnnotatableComponent() * analyzes the ink stroke and saves the analysis of the stroke to the 'inkAnalysis' field, * and the recognized words to the 'handwriting' */ - analyzeStrokes=()=> { + analyzeStrokes = () => { const data: InkData = this.inkScaledData().inkData ?? []; CognitiveServices.Inking.Appliers.ConcatenateHandwriting(this.dataDoc, ['inkAnalysis', 'handwriting'], [data]); - } + }; /** * Toggles whether the ink stroke is displayed as an overlay mask or as a regular stroke. diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 3e2c55114..36772e8ec 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -894,7 +894,7 @@ export class CollectionFreeFormView extends CollectionSubView { + InkField.bintersects(intersectCurve, eraserCurve).forEach((val: string | number, k: number) => { // Converting the Bezier.js Split type to a t-value number. const t = +val.toString().split('/')[0]; if (k % 2 === 0) { @@ -1168,26 +1168,6 @@ export class CollectionFreeFormView extends CollectionSubView { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - if ((curve as any)._linear) { - // bezier.js doesn't intersect properly if the curve is actually a line -- so get intersect other curve against this line, then figure out the t coordinates of the intersection on this line - const intersections = otherCurve.lineIntersects({ p1: curve.points[0], p2: curve.points[3] }); - if (intersections.length) { - const intPt = otherCurve.get(intersections[0]); - const intT = curve.project(intPt).t; - return intT ? [intT] : []; - } - } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - if ((otherCurve as any)._linear) { - return curve.lineIntersects({ p1: otherCurve.points[0], p2: otherCurve.points[3] }); - } - return curve.intersects(otherCurve); - }; - /** * Determines all possible intersections of the current curve of the intersected ink stroke with all other curves of all * ink strokes in the current collection. @@ -1219,7 +1199,7 @@ export class CollectionFreeFormView extends CollectionSubView { + InkField.bintersects(curve, otherCurve).forEach((val: string | number, ival: number) => { // Converting the Bezier.js Split type to a t-value number. const t = +val.toString().split('/')[0]; if (ival % 2 === 0 && !tVals.includes(t)) tVals.push(t); // bcz: Hack! don't know why but intersection points are doubled from bezier.js (but not identical). diff --git a/src/fields/InkField.ts b/src/fields/InkField.ts index 32abf0076..ad524f73f 100644 --- a/src/fields/InkField.ts +++ b/src/fields/InkField.ts @@ -102,6 +102,26 @@ export class InkField extends ObjectField { const top = Math.min(...ys); return { right, left, bottom, top, width: right - left, height: bottom - top }; } + + // for some reason bezier.js doesn't handle the case of intersecting a linear curve, so we wrap the intersection + // call in a test for linearity + public static bintersects(curve: Bezier, otherCurve: Bezier) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + if ((curve as any)._linear) { + // bezier.js doesn't intersect properly if the curve is actually a line -- so get intersect other curve against this line, then figure out the t coordinates of the intersection on this line + const intersections = otherCurve.lineIntersects({ p1: curve.points[0], p2: curve.points[3] }); + if (intersections.length) { + const intPt = otherCurve.get(intersections[0]); + const intT = curve.project(intPt).t; + return intT ? [intT] : []; + } + } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + if ((otherCurve as any)._linear) { + return curve.lineIntersects({ p1: otherCurve.points[0], p2: otherCurve.points[3] }); + } + return curve.intersects(otherCurve); + } } ScriptingGlobals.add('InkField', InkField); -- cgit v1.2.3-70-g09d2 From e1d26d7d98962572f020e55c88e6c9e63a7dfa8a Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 1 Oct 2024 11:58:55 -0400 Subject: fixed eraser mode button to show current eraser and to show options. --- package-lock.json | 8 ++++---- package.json | 2 +- src/client/util/CurrentUserUtils.ts | 14 ++++++-------- src/client/views/MainView.tsx | 1 - src/client/views/global/globalScripts.ts | 5 +++-- src/client/views/nodes/FontIconBox/FontIconBox.tsx | 4 ++-- src/client/views/nodes/formattedText/RichTextMenu.tsx | 12 +++++++----- src/fields/InkField.ts | 1 + 8 files changed, 24 insertions(+), 23 deletions(-) (limited to 'src/fields') diff --git a/package-lock.json b/package-lock.json index 7e89f8bb6..c5b251164 100644 --- a/package-lock.json +++ b/package-lock.json @@ -70,7 +70,7 @@ "body-parser": "^1.20.2", "bootstrap": "^5.3.2", "brotli": "^1.3.3", - "browndash-components": "^0.1.49", + "browndash-components": "^0.1.50", "browser-assert": "^1.2.1", "bson": "^6.2.0", "canvas": "^2.11.2", @@ -11491,9 +11491,9 @@ } }, "node_modules/browndash-components": { - "version": "0.1.49", - "resolved": "https://registry.npmjs.org/browndash-components/-/browndash-components-0.1.49.tgz", - "integrity": "sha512-XSW7XLSIml4qhDCHROzVTCzNgd878ndNQU8zC/M+UZqTyFCDLPGV5eo546IZ8QqEFOjUCp7jmzJS6jUcOeQ4HQ==", + "version": "0.1.50", + "resolved": "https://registry.npmjs.org/browndash-components/-/browndash-components-0.1.50.tgz", + "integrity": "sha512-8EgU82os8/Tg3gayh+nYXCQZ8P7GGQWnibz0U2RM0sXTbc3BWTSYiC1Sj1VpL7HPkZ1KMGHzWynCo9QKIOWZBg==", "dependencies": { "@emotion/react": "^11.11.1", "@emotion/styled": "^11.11.0", diff --git a/package.json b/package.json index cb4491497..4ede14770 100644 --- a/package.json +++ b/package.json @@ -149,7 +149,7 @@ "body-parser": "^1.20.2", "bootstrap": "^5.3.2", "brotli": "^1.3.3", - "browndash-components": "^0.1.49", + "browndash-components": "^0.1.50", "browser-assert": "^1.2.1", "bson": "^6.2.0", "canvas": "^2.11.2", diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index a340ea4e3..b316944d1 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -381,9 +381,7 @@ pie title Minerals in my tap water ]; emptyThings.forEach( - thing =>{ DocUtils.AssignDocField(doc, "empty"+thing.key, (opts) => thing.creator(opts), {...standardOps(thing.key), ...thing.opts}, undefined, thing.scripts, thing.funcs); - console.log(thing.key) - }); + thing => DocUtils.AssignDocField(doc, "empty"+thing.key, (opts) => thing.creator(opts), {...standardOps(thing.key), ...thing.opts}, undefined, thing.scripts, thing.funcs)); return [ { toolTip: "Tap or drag to create a note", title: "Note", icon: "sticky-note", dragFactory: doc.emptyNote as Doc, clickFactory: DocCast(doc.emptyNote)}, @@ -740,12 +738,12 @@ pie title Minerals in my tap water static inkTools():Button[] { return [ - { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen-nib", toolType: "pen", scripts: {onClick:'{ return setActiveTool(this.toolType, false, _readOnly_);}' }}, - { title: "Write", toolTip: "Write (Ctrl+Shift+P)", btnType: ButtonType.ToggleButton, icon: "pen", toolType: "write", scripts: {onClick:'{ return setActiveTool(this.toolType, false, _readOnly_);}' }, funcs: {hidden:"IsNoviceMode()" }}, - { title: "Eraser", toolTip: "Eraser (Ctrl+E)", btnType: ButtonType.MultiToggleButton, scripts: {onClick: '{ return setActiveTool(this.toolType, false, _readOnly_);}'}, funcs: {toolType:"activeEraserTool()"}, + { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen-nib", toolType: "pen", scripts: {onClick:'{ return setActiveTool(this.toolType, false, _readOnly_);}' }}, + { title: "Write", toolTip: "Write (Ctrl+Shift+P)", btnType: ButtonType.ToggleButton, icon: "pen", toolType: "write", scripts: {onClick:'{ return setActiveTool(this.toolType, false, _readOnly_);}' }, funcs: {hidden:"IsNoviceMode()" }}, + { title: "Eraser", toolTip: "Eraser (Ctrl+E)", btnType: ButtonType.MultiToggleButton, toolType: InkTool.Eraser, scripts: {onClick:'{ return setActiveTool(this.toolType, false, _readOnly_);}' }, subMenu: [ { title: "Stroke", toolTip: "Stroke Erase", btnType: ButtonType.ToggleButton, icon: "eraser", toolType:InkTool.StrokeEraser, ignoreClick: true, scripts: {onClick: '{ return setActiveTool(this.toolType, false, _readOnly_);}'} }, - { title: "Segment", toolTip: "Segment Erase", btnType: ButtonType.ToggleButton, icon: "xmark",toolType:InkTool.SegmentEraser,ignoreClick: true, scripts: {onClick: '{ return setActiveTool(this.toolType, false, _readOnly_);}'} }, + { title: "Segment", toolTip: "Segment Erase", btnType: ButtonType.ToggleButton, icon: "xmark", toolType:InkTool.SegmentEraser,ignoreClick: true, scripts: {onClick: '{ return setActiveTool(this.toolType, false, _readOnly_);}'} }, { title: "Radius", toolTip: "Radius Erase", btnType: ButtonType.ToggleButton, icon: "circle-xmark",toolType:InkTool.RadiusEraser, ignoreClick: true, scripts: {onClick: '{ return setActiveTool(this.toolType, false, _readOnly_);}'} }, ]}, { title: "Eraser Width", toolTip: "Eraser Width", btnType: ButtonType.NumberSliderButton, toolType: "eraserWidth", ignoreClick: true, scripts: {script: '{ return setInkProperty(this.toolType, value, _readOnly_);}'}, numBtnMin: 1, funcs: {hidden:"NotRadiusEraser()"}}, @@ -801,7 +799,7 @@ pie title Minerals in my tap water { title: "Num", icon:"", toolTip: "Frame # (click to toggle edit mode)",btnType: ButtonType.TextButton, expertMode: true, toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectedDocType(this.toolType, this.expertMode)', buttonText: 'selectedDocs()?.lastElement()?.currentFrame?.toString()'}, width: 20, scripts: { onClick: '{ return curKeyFrame(_readOnly_);}'}}, { title: "Fwd", icon: "chevron-right", toolTip: "Next Animation Frame", btnType: ButtonType.ClickButton, expertMode: true, toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectedDocType(this.toolType, this.expertMode)'}, width: 30, scripts: { onClick: 'nextKeyFrame(_readOnly_)'}}, - { title: "Filter", icon: "=", toolTip: "Filter cards by tags", subMenu: CurrentUserUtils.tagGroupTools(),ignoreClick:true, toolType:DocumentType.COL, funcs: {hidden: '!SelectedDocType(this.toolType, this.expertMode)'}, btnType: ButtonType.MultiToggleButton, width: 30, backgroundColor: doc.userVariantColor}, + { title: "Filter", icon: "=", toolTip: "Filter cards by tags", subMenu: CurrentUserUtils.tagGroupTools(),ignoreClick:true, toolType:DocumentType.COL, funcs: {hidden: '!SelectedDocType(this.toolType, this.expertMode)'}, btnType: ButtonType.MultiToggleButton, width: 30, backgroundColor: doc.userVariantColor as string}, { title: "Text", icon: "Text", toolTip: "Text functions", subMenu: CurrentUserUtils.textTools(), expertMode: false, toolType:DocumentType.RTF, funcs: { linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} }, // Always available { title: "Ink", icon: "Ink", toolTip: "Ink functions", subMenu: CurrentUserUtils.inkTools(), expertMode: false, toolType:DocumentType.INK, funcs: {hidden: `IsExploreMode()`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`}, scripts: { onClick: 'setInkToolDefaults()'} }, // Always available { title: "Doc", icon: "Doc", toolTip: "Freeform Doc tools", subMenu: CurrentUserUtils.freeTools(), expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: `!SelectedDocType(this.toolType, this.expertMode, true)`, linearView_IsOpen: `SelectedDocType(this.toolType, this.expertMode)`} }, // Always available diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 5285e67f6..a956f084d 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -838,7 +838,6 @@ export class MainView extends ObservableReactComponent { }; @computed get mainInnerContent() { - trace(); const leftMenuFlyoutWidth = this._leftMenuFlyoutWidth + this.leftMenuWidth(); const width = this.propertiesWidth() + leftMenuFlyoutWidth; return ( diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts index 7f6c726d6..695f929d8 100644 --- a/src/client/views/global/globalScripts.ts +++ b/src/client/views/global/globalScripts.ts @@ -451,10 +451,11 @@ export function createInkGroup(/* inksToGroup?: Doc[], isSubGroup?: boolean */) CollectionFreeFormView.collectionsWithUnprocessedInk.clear(); } -function setActiveTool(tool: InkTool | Gestures, keepPrim: boolean, checkResult?: boolean) { +function setActiveTool(toolIn: InkTool | Gestures, keepPrim: boolean, checkResult?: boolean) { InkTranscription.Instance?.createInkGroup(); + const tool = toolIn === InkTool.Eraser ? Doc.UserDoc().activeEraserTool : toolIn; if (checkResult) { - return (Doc.ActiveTool === tool && !GestureOverlay.Instance?.InkShape) || GestureOverlay.Instance?.InkShape === tool + return ((Doc.ActiveTool === tool || (Doc.UserDoc().activeEraserTool === tool && (tool === toolIn || Doc.ActiveTool === tool))) && !GestureOverlay.Instance?.InkShape) || GestureOverlay.Instance?.InkShape === tool ? GestureOverlay.Instance?.KeepPrimitiveMode || ![Gestures.Circle, Gestures.Line, Gestures.Rectangle].includes(tool as Gestures) : false; } diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx index aa40b14aa..b7e1350ca 100644 --- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx +++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx @@ -262,7 +262,7 @@ export class FontIconBox extends ViewBoxBaseComponent() { const tooltip: string = StrCast(this.Document.toolTip); const script = ScriptCast(this.Document.onClick)?.script; - const toggleStatus = script?.run({ this: this.Document, self: this.Document, value: undefined, _readOnly_: true }).result as boolean; + const toggleStatus = script?.run({ this: this.Document, value: undefined, _readOnly_: true }).result as boolean; // Colors const color = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color) as string; const background = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor) as string; @@ -276,7 +276,7 @@ export class FontIconBox extends ViewBoxBaseComponent() { background={background === SnappingManager.userBackgroundColor ? undefined : background} multiSelect={true} onPointerDown={e => script && !toggleStatus && setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => script.run({ this: this.Document, value: undefined, _readOnly_: false }))} - isToggle={script ? true : false} + isToggle={false} toggleStatus={toggleStatus} label={this.label} items={items.map(item => ({ diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx index 738f6d699..1560d51a1 100644 --- a/src/client/views/nodes/formattedText/RichTextMenu.tsx +++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx @@ -1,6 +1,6 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@mui/material'; -import { action, computed, IReactionDisposer, makeObservable, observable } from 'mobx'; +import { action, computed, IReactionDisposer, makeObservable, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import { lift, toggleMark, wrapIn } from 'prosemirror-commands'; import { Mark, MarkType } from 'prosemirror-model'; @@ -68,10 +68,12 @@ export class RichTextMenu extends AntimodeMenu { constructor(props: AntimodeMenuProps) { super(props); makeObservable(this); - RichTextMenu._instance.menu = this; - this.updateMenu(undefined, undefined, props, this.layoutDoc); - this._canFade = false; - this.Pinned = true; + runInAction(() => { + RichTextMenu._instance.menu = this; + this.updateMenu(undefined, undefined, props, this.layoutDoc); + this._canFade = false; + this.Pinned = true; + }); } @computed get noAutoLink() { diff --git a/src/fields/InkField.ts b/src/fields/InkField.ts index ad524f73f..dc3e08414 100644 --- a/src/fields/InkField.ts +++ b/src/fields/InkField.ts @@ -14,6 +14,7 @@ export enum InkTool { StrokeEraser = 'strokeeraser', SegmentEraser = 'segmenteraser', RadiusEraser = 'radiuseraser', + Eraser = 'eraser', // not a real tool, but a class of tools Stamp = 'stamp', Write = 'write', PresentationPin = 'presentationpin', -- cgit v1.2.3-70-g09d2 From 8d7bf0588ca63c2d505a494865dcf9921eb1383d Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 1 Oct 2024 15:57:57 -0400 Subject: more lint fixes --- src/client/util/CurrentUserUtils.ts | 2 +- src/client/util/DropConverter.ts | 2 +- src/client/util/Scripting.ts | 5 ----- src/client/views/DocumentDecorations.tsx | 2 +- src/client/views/InkTranscription.tsx | 1 + src/client/views/InkingStroke.tsx | 4 +--- src/client/views/LightboxView.tsx | 2 -- src/client/views/Main.tsx | 1 - src/client/views/MainView.tsx | 19 +++++++------------ .../views/collections/CollectionCarousel3DView.tsx | 2 +- .../views/collections/CollectionCarouselView.tsx | 4 ++-- .../collectionFreeForm/CollectionFreeFormView.tsx | 11 ++++++----- .../collections/collectionFreeForm/MarqueeView.tsx | 3 +-- src/client/views/nodes/FontIconBox/FontIconBox.tsx | 1 - src/client/views/nodes/ImageBox.tsx | 1 - src/client/views/nodes/PDFBox.tsx | 5 +---- .../views/nodes/formattedText/FormattedTextBox.tsx | 8 -------- src/client/views/nodes/formattedText/RichTextMenu.tsx | 1 - src/client/views/pdf/PDFViewer.tsx | 2 -- src/client/views/smartdraw/AnnotationPalette.tsx | 4 +--- src/client/views/smartdraw/SmartDrawHandler.tsx | 3 ++- src/fields/SchemaHeaderField.ts | 1 - 22 files changed, 26 insertions(+), 58 deletions(-) (limited to 'src/fields') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 59760ba32..96d69e7a1 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -2,7 +2,7 @@ import { reaction, runInAction } from "mobx"; import * as rp from 'request-promise'; import { ClientUtils, OmitKeys } from "../../ClientUtils"; -import { Doc, DocListCast, DocListCastAsync, FieldType, Opt, StrListCast } from "../../fields/Doc"; +import { Doc, DocListCast, DocListCastAsync, FieldType, Opt } from "../../fields/Doc"; import { DocData } from "../../fields/DocSymbols"; import { InkTool } from "../../fields/InkField"; import { List } from "../../fields/List"; diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts index 71cdaa58b..b5d29be4c 100644 --- a/src/client/util/DropConverter.ts +++ b/src/client/util/DropConverter.ts @@ -67,7 +67,7 @@ export function MakeTemplate(doc: Doc) { /** * Makes a draggable button or image that will create a template doc Instance */ -export function makeUserTemplateButtonOrImage(doc: Doc, image: string | undefined) { +export function makeUserTemplateButtonOrImage(doc: Doc, image?: string) { const layoutDoc = doc; // doc.layout instanceof Doc && doc.layout.isTemplateForField ? doc.layout : doc; if (layoutDoc.type !== DocumentType.FONTICON) { !layoutDoc.isTemplateDoc && makeTemplate(layoutDoc); diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts index c63d3d7cb..c7b86815a 100644 --- a/src/client/util/Scripting.ts +++ b/src/client/util/Scripting.ts @@ -60,7 +60,6 @@ function Run(script: string | undefined, customParams: string[], diagnostics: ts // let params: any[] = [Docs, ...fieldTypes]; const compiledFunction = (() => { try { - // eslint-disable-next-line no-new-func return new Function(...paramNames, `return ${script}`); } catch (e) { console.log(e); @@ -69,10 +68,8 @@ function Run(script: string | undefined, customParams: string[], diagnostics: ts })(); if (!compiledFunction) return { compiled: false, errors }; const { capturedVariables = {} } = options; - // eslint-disable-next-line default-param-last const run = (args: { [name: string]: unknown } = {}, onError?: (e: string) => void, errorVal?: ts.Diagnostic): ScriptResult => { const argsArray: unknown[] = []; - // eslint-disable-next-line no-restricted-syntax for (const name of customParams) { if (name !== 'this') { argsArray.push(name in args ? args[name] : capturedVariables[name]); @@ -224,7 +221,6 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp if ('this' in params || 'this' in capturedVariables) { paramNames.push('this'); } - // eslint-disable-next-line no-restricted-syntax for (const key in params) { if (key !== 'this') { paramNames.push(key); @@ -234,7 +230,6 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp const val = params[key]; return `${key}: ${val}`; }); - // eslint-disable-next-line no-restricted-syntax for (const key in capturedVariables) { if (key !== 'this') { const val = capturedVariables[key]; diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index e569c7dc8..1c0d51e17 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -2,7 +2,7 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@mui/material'; import { IconButton } from 'browndash-components'; -import { action, computed, makeObservable, observable, runInAction, trace } from 'mobx'; +import { action, computed, makeObservable, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { FaUndo } from 'react-icons/fa'; diff --git a/src/client/views/InkTranscription.tsx b/src/client/views/InkTranscription.tsx index b4f4ebf63..24d53a8c8 100644 --- a/src/client/views/InkTranscription.tsx +++ b/src/client/views/InkTranscription.tsx @@ -18,6 +18,7 @@ import { URLField } from '../../fields/URLField'; * Class component that handles inking in writing mode */ export class InkTranscription extends React.Component { + // eslint-disable-next-line no-use-before-define static Instance: InkTranscription; // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index f44c1720d..270266a94 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -51,7 +51,7 @@ import { InkTranscription } from './InkTranscription'; import { CollectionFreeFormView } from './collections/collectionFreeForm'; import { DocumentView } from './nodes/DocumentView'; -// eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-require-imports +// eslint-disable-next-line @typescript-eslint/no-require-imports const { INK_MASK_SIZE } = require('./global/globalCssVariables.module.scss'); // prettier-ignore @observer @@ -473,7 +473,6 @@ export class InkingStroke extends ViewBoxAnnotatableComponent() // mixBlendMode: this.layoutDoc.tool === InkTool.Highlighter ? 'multiply' : 'unset', cursor: this._props.isSelected() ? 'default' : undefined, }} - // eslint-disable-next-line react/jsx-props-no-spreading {...interactions}> {clickableLine(this.onPointerDown, isInkMask)} {isInkMask ? null : inkLine} @@ -490,7 +489,6 @@ export class InkingStroke extends ViewBoxAnnotatableComponent() // top: (this._props.PanelHeight() - (lineHeightGuess * fsize + 20) * (this._props.NativeDimScaling?.() || 1)) / 2, }}> { ); } // prettier-ignore public static LightboxDoc = () => LightboxView.Instance?._doc; - // eslint-disable-next-line no-use-before-define static Instance: LightboxView; private _path: { doc: Opt; // @@ -341,7 +340,6 @@ export class LightboxView extends ObservableReactComponent { } interface LightboxTourBtnProps { navBtn: (left: Opt, bottom: Opt, top: number, icon: IconProp, display: boolean, click: () => void, color?: string) => JSX.Element; - // eslint-disable-next-line react/no-unused-prop-types future: () => Opt; stepInto: () => void; lightboxDoc: () => Opt; diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 94294e97a..73d2872d1 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -1,4 +1,3 @@ -/* eslint-disable no-new */ // if ((module as any).hot) { // (module as any).hot.accept(); // } diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 8e921ca5e..fa2e94d12 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -3,7 +3,7 @@ import { faBuffer, faHireAHelper } from '@fortawesome/free-brands-svg-icons'; import * as far from '@fortawesome/free-regular-svg-icons'; import * as fa from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, configure, makeObservable, observable, reaction, runInAction, trace } from 'mobx'; +import { action, computed, configure, makeObservable, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import ResizeObserver from 'resize-observer-polyfill'; @@ -77,7 +77,7 @@ import { TopBar } from './topbar/TopBar'; import { SmartDrawHandler } from './smartdraw/SmartDrawHandler'; import { InkTranscription } from './InkTranscription'; -// eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-require-imports +// eslint-disable-next-line @typescript-eslint/no-require-imports const { LEFT_MENU_WIDTH, TOPBAR_HEIGHT } = require('./global/globalCssVariables.module.scss'); // prettier-ignore @observer @@ -977,13 +977,11 @@ export class MainView extends ObservableReactComponent {
{[ - ...SnappingManager.HorizSnapLines.map((l, i) => ( - // eslint-disable-next-line react/no-array-index-key - + ...SnappingManager.HorizSnapLines.map(l => ( + )), - ...SnappingManager.VertSnapLines.map((l, i) => ( - // eslint-disable-next-line react/no-array-index-key - + ...SnappingManager.VertSnapLines.map(l => ( + )), ]} @@ -1071,10 +1069,7 @@ export class MainView extends ObservableReactComponent { docView={DocButtonState.Instance.LinkEditorDocView} /> ) : null} - {LinkInfo.Instance?.LinkInfo ? ( - // eslint-disable-next-line react/jsx-props-no-spreading - - ) : null} + {LinkInfo.Instance?.LinkInfo ? : null} {((page: string) => { // prettier-ignore switch (page) { diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx index cf86a0a4e..c5da8e037 100644 --- a/src/client/views/collections/CollectionCarousel3DView.tsx +++ b/src/client/views/collections/CollectionCarousel3DView.tsx @@ -16,6 +16,7 @@ import './CollectionCarousel3DView.scss'; import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView'; import { Transform } from '../../util/Transform'; +// eslint-disable-next-line @typescript-eslint/no-require-imports const { CAROUSEL3D_CENTER_SCALE, CAROUSEL3D_SIDE_SCALE, CAROUSEL3D_TOP } = require('../global/globalCssVariables.module.scss'); @observer @@ -88,7 +89,6 @@ export class CollectionCarousel3DView extends CollectionSubView() { const currentIndex = NumCast(this.layoutDoc._carousel_index); const displayDoc = (childPair: { layout: Doc; data: Doc }, dxf: () => Transform) => ( this.showSmartDraw(e.pageX, e.pageY), hit !== -1); + setupMoveUpEvents(this, e, this.onPointerMove, emptyFunction, () => this.showSmartDraw(e.pageX, e.pageY), hit !== -1); e.stopPropagation(); + break; case InkTool.None: if (!(this._props.layoutEngine?.() || StrCast(this.layoutDoc._layoutEngine))) { - const hit = this._clusters.handlePointerDown(this.screenToFreeformContentsXf.transformPoint(e.clientX, e.clientY)); - setupMoveUpEvents(this, e, this.onPointerMove, emptyFunction, emptyFunction, hit !== -1, false); + const ahit = this._clusters.handlePointerDown(this.screenToFreeformContentsXf.transformPoint(e.clientX, e.clientY)); + setupMoveUpEvents(this, e, this.onPointerMove, emptyFunction, emptyFunction, ahit !== -1, false); } break; default: @@ -655,13 +656,13 @@ export class CollectionFreeFormView extends CollectionSubView { + onEraserClick = (e: PointerEvent) => { e.preventDefault(); e.stopImmediatePropagation(); this.erase(e, [0, 0]); }; - forceStrokeGesture = (e: PointerEvent, gesture: Gestures, points: InkData, text?: any) => { + forceStrokeGesture = (e: PointerEvent, gesture: Gestures, points: InkData, text?: string) => { this.onGesture(e, new GestureUtils.GestureEvent(gesture, points, InkField.getBounds(points), text)); }; diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 7614b5337..b1f6815b3 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -57,6 +57,7 @@ export class MarqueeView extends ObservableReactComponent tl[0] && truePoint[0] < r1.left && truePoint[1] > r1.top && truePoint[1] < r1.top + r1.height); diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx index b7e1350ca..feaf84b7b 100644 --- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx +++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx @@ -1,4 +1,3 @@ -/* eslint-disable react/jsx-props-no-spreading */ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Button, ColorPicker, Dropdown, DropdownType, IconButton, IListItemProps, MultiToggle, NumberDropdown, NumberDropdownType, Popup, Size, Toggle, ToggleType, Type } from 'browndash-components'; diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index aa4376bb2..ec5e062c8 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -493,7 +493,6 @@ export class ImageBox extends ViewBoxAnnotatableComponent() { }}> () { return ComponentTag === CollectionStackingView ? ( () { ) : (
setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => this._props.select(false), true)}> () { top: 0, }}> -1) { const sel = new TextSelection(pm.state.doc.resolve(ep.from + index + blockOffset + foundAt + 1), pm.state.doc.resolve(ep.from + index + blockOffset + foundAt + find.length + 1)); ret.push(sel); @@ -714,7 +713,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent addStyleSheetRule(FormattedTextBox._userStyleSheet, 'UM-hr-' + (hr - i), { opacity: ((10 - i - 1) / 10).toString() })); } - // eslint-disable-next-line operator-assignment this.layoutDoc[DocCss] = this.layoutDoc[DocCss] + 1; // css changes happen outside of react/mobx. so we need to set a flag that will notify anyone interested in layout changes triggered by css changes (eg., CollectionLinkView) }; @@ -1131,7 +1129,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { - // eslint-disable-next-line no-use-before-define const examinedNode = findAnchorNode(node, editor); if (examinedNode?.node && (examinedNode.node.textContent || examinedNode.node.type === this._editorView?.state.schema.nodes.dashDoc || examinedNode.node.type === this._editorView?.state.schema.nodes.audiotag)) { nodes.push(examinedNode.node); @@ -1285,7 +1282,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { this.prepareForTyping(); if (FormattedTextBox._globalHighlights.has('Bold Text')) { - // eslint-disable-next-line operator-assignment this.layoutDoc[DocCss] = this.layoutDoc[DocCss] + 1; // css change happens outside of mobx/react, so this will notify anyone interested in the layout that it has changed } if (RichTextMenu.Instance?.view === this._editorView && !selected) { @@ -1711,7 +1707,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent kids?.reduce((p, child) => p + toHgt(child), margins) ?? 0; const toNum = (val: string) => Number(val.replace('px', '')); const toHgt = (node: Element): number => { @@ -1880,7 +1874,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => DocumentView.SelectView(this.DocumentView?.(), false), true)}> { - // eslint-disable-next-line react/no-unused-class-component-methods update(view: EditorView & { TextView?: FormattedTextBox }, lastState: EditorState | undefined) { RichTextMenu.Instance?.updateMenu(view, lastState, this.props.editorProps, view.TextView?.layoutDoc); } diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 01242ba48..7a86ee802 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -504,7 +504,6 @@ export class PDFViewer extends ObservableReactComponent { return (
{inlineAnnos.map(anno => ( - // eslint-disable-next-line react/jsx-props-no-spreading ))}
@@ -537,7 +536,6 @@ export class PDFViewer extends ObservableReactComponent { pointerEvents: Doc.ActiveTool !== InkTool.None ? 'all' : undefined, }}> { + // eslint-disable-next-line no-use-before-define static Instance: SmartDrawHandler; private _lastInput: DrawingOptions = { text: '', complexity: 5, size: 350, autoColor: true, x: 0, y: 0 }; @@ -395,7 +396,7 @@ export class SmartDrawHandler extends ObservableReactComponent { defaultChecked={true} value={this._autoColor} size="small" - onChange={action(e => this._canInteract && (this._autoColor = !this._autoColor))} + onChange={action(() => this._canInteract && (this._autoColor = !this._autoColor))} />
diff --git a/src/fields/SchemaHeaderField.ts b/src/fields/SchemaHeaderField.ts index 0a8dd1d9e..5f4d59cf9 100644 --- a/src/fields/SchemaHeaderField.ts +++ b/src/fields/SchemaHeaderField.ts @@ -79,7 +79,6 @@ export class SchemaHeaderField extends ObjectField { @serializable(primitive()) desc: boolean | undefined; // boolean determines sort order, undefined when no sort - // eslint-disable-next-line default-param-last constructor(heading: string = '', color: string = RandomPastel(), type?: ColumnType, width?: number, desc?: boolean, collapsed?: boolean) { super(); -- cgit v1.2.3-70-g09d2