diff options
author | eleanor-park <eleanor_park@brown.edu> | 2024-11-11 01:18:58 -0500 |
---|---|---|
committer | eleanor-park <eleanor_park@brown.edu> | 2024-11-11 01:18:58 -0500 |
commit | 8a01bf324f8313306a578b6e8d5736d8bfcd7dd9 (patch) | |
tree | 3996410b27758bcf292d8ed4f4bf18b64b48fc4a | |
parent | 41a8e1c7f1943145bf7099c70ef3eb6540fe0d26 (diff) | |
parent | 4ab636e338a11e8153d43adddb0e0d3e6bad57ec (diff) |
changes for demo
-rw-r--r-- | src/ClientUtils.ts | 2 | ||||
-rw-r--r-- | src/client/documents/Documents.ts | 2 | ||||
-rw-r--r-- | src/client/util/CurrentUserUtils.ts | 36 | ||||
-rw-r--r-- | src/client/util/bezierFit.ts | 9 | ||||
-rw-r--r-- | src/client/views/DocumentDecorations.tsx | 4 | ||||
-rw-r--r-- | src/client/views/TagsView.scss | 1 | ||||
-rw-r--r-- | src/client/views/collections/CollectionCardDeckView.tsx | 20 | ||||
-rw-r--r-- | src/client/views/collections/CollectionCarousel3DView.tsx | 6 | ||||
-rw-r--r-- | src/client/views/collections/CollectionCarouselView.tsx | 6 | ||||
-rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 3 | ||||
-rw-r--r-- | src/client/views/global/globalScripts.ts | 21 | ||||
-rw-r--r-- | src/client/views/nodes/formattedText/FormattedTextBox.tsx | 12 | ||||
-rw-r--r-- | src/client/views/nodes/imageEditor/ImageEditor.tsx | 30 | ||||
-rw-r--r-- | src/client/views/smartdraw/SmartDrawHandler.scss | 4 | ||||
-rw-r--r-- | src/client/views/smartdraw/SmartDrawHandler.tsx | 10 | ||||
-rw-r--r-- | src/fields/InkField.ts | 8 |
16 files changed, 107 insertions, 67 deletions
diff --git a/src/ClientUtils.ts b/src/ClientUtils.ts index baad9a06c..8f62b9060 100644 --- a/src/ClientUtils.ts +++ b/src/ClientUtils.ts @@ -165,7 +165,7 @@ export namespace ClientUtils { return { scale: 0, translateX: 1, translateY: 1 }; } const rect = ele.getBoundingClientRect(); - const scale = ele.offsetWidth === 0 && rect.width === 0 ? 1 : rect.width / ele.offsetWidth; + const scale = ele.offsetWidth === 0 && rect.width === 0 ? 1 : rect.width / (ele.offsetWidth || 1); const translateX = rect.left; const translateY = rect.top; diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 69c66e0dc..256401eca 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -926,6 +926,8 @@ export namespace Docs { I.stroke_isInkMask = isInkMask; I.text_align = 'center'; I.rotation = 0; + I.width_min = 1; + I.height_min = 1; I.defaultDoubleClick = 'ignore'; I.author_date = new DateField(); I.acl_Guest = Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.View; diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 99faafe38..d62f4ad91 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -4,7 +4,7 @@ import * as rp from 'request-promise'; import { ClientUtils, OmitKeys } from "../../ClientUtils"; import { Doc, DocListCast, DocListCastAsync, FieldType, Opt } from "../../fields/Doc"; import { DocData } from "../../fields/DocSymbols"; -import { InkEraserTool, InkInkTool, InkTool } from "../../fields/InkField"; +import { InkEraserTool, InkInkTool, InkProperty, InkTool } from "../../fields/InkField"; import { List } from "../../fields/List"; import { PrefetchProxy } from "../../fields/Proxy"; import { RichTextField } from "../../fields/RichTextField"; @@ -759,27 +759,27 @@ pie title Minerals in my tap water static inkTools():Button[] { return [ - { title: "Circle", toolTip: "Circle (double tap to lock mode)", btnType: ButtonType.ToggleButton, icon: "circle", toolType: Gestures.Circle, scripts: {onClick:`{ return setActiveTool(this.toolType, false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool(this.toolType, true, _readOnly_);}`} }, - { 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: "Ink", toolTip: "Ink", btnType: ButtonType.MultiToggleButton, toolType: InkTool.Ink, scripts: {onClick:'{ return setActiveTool(this.toolType, true, _readOnly_);}' }, + { title: "Circle", toolTip: "Circle (double tap to lock mode)", btnType: ButtonType.ToggleButton, icon: "circle", toolType: Gestures.Circle, scripts: {onClick:`{ return setActiveTool(this.toolType, false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool(this.toolType, true, _readOnly_);}`} }, + { 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: "Ink", toolTip: "Ink", btnType: ButtonType.MultiToggleButton, toolType: InkTool.Ink, scripts: {onClick:'{ return setActiveTool(this.toolType, true, _readOnly_);}' }, subMenu: [ - { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen-nib", toolType: InkInkTool.Pen, ignoreClick: true, scripts: {onClick:'{ return setActiveTool(this.toolType, true, _readOnly_);}' }}, - { title: "Highlight",toolTip: "Highlight (Ctrl+H)", btnType: ButtonType.ToggleButton, icon: "highlighter", toolType: InkInkTool.Highlight, ignoreClick: true, scripts: {onClick:'{ return setActiveTool(this.toolType, true, _readOnly_);}' }}, - { title: "Write", toolTip: "Write (Ctrl+Shift+P)", btnType: ButtonType.ToggleButton, icon: "pen", toolType: InkInkTool.Write, ignoreClick: true, scripts: {onClick:'{ return setActiveTool(this.toolType, true, _readOnly_);}' }, funcs: {hidden:"IsNoviceMode()" }}, + { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen-nib", toolType: InkInkTool.Pen, ignoreClick: true, scripts: {onClick:'{ return setActiveTool(this.toolType, true, _readOnly_);}' }}, + { title: "Highlight",toolTip: "Highlight (Ctrl+H)", btnType: ButtonType.ToggleButton, icon: "highlighter", toolType: InkInkTool.Highlight, ignoreClick: true, scripts: {onClick:'{ return setActiveTool(this.toolType, true, _readOnly_);}' }}, + { title: "Write", toolTip: "Write (Ctrl+Shift+P)", btnType: ButtonType.ToggleButton, icon: "pen", toolType: InkInkTool.Write, ignoreClick: true, scripts: {onClick:'{ return setActiveTool(this.toolType, true, _readOnly_);}' }, funcs: {hidden:"IsNoviceMode()" }}, ]}, - { title: "Width", toolTip: "Stroke width", btnType: ButtonType.NumberSliderButton, toolType: "strokeWidth", ignoreClick: true, scripts: {script: '{ return setInkProperty(this.toolType, value, _readOnly_);}'}, funcs: {hidden:"!activeInkTool()"}, numBtnMin: 1, linearBtnWidth:40}, - { title: "Color", toolTip: "Stroke color", btnType: ButtonType.ColorButton, icon: "pen", toolType: "strokeColor", ignoreClick: true, scripts: {script: '{ return setInkProperty(this.toolType, value, _readOnly_);}'}, funcs: {hidden:"!activeInkTool()"}}, - { title: "Eraser", toolTip: "Eraser (Ctrl+E)", btnType: ButtonType.MultiToggleButton, toolType: InkTool.Eraser, scripts: {onClick:'{ return setActiveTool(this.toolType, false, _readOnly_);}' }, + { title: "Width", toolTip: "Stroke width", btnType: ButtonType.NumberSliderButton, toolType: InkProperty.StrokeWidth,ignoreClick: true, scripts: {script: '{ return setInkProperty(this.toolType, value, _readOnly_);}'}, funcs: {hidden:"!activeInkTool()"}, numBtnMin: 1, linearBtnWidth:40}, + { title: "Color", toolTip: "Stroke color", btnType: ButtonType.ColorButton, icon: "pen", toolType: InkProperty.StrokeColor,ignoreClick: true, scripts: {script: '{ return setInkProperty(this.toolType, value, _readOnly_);}'}, funcs: {hidden:"!activeInkTool()"}}, + { title: "Eraser", toolTip: "Eraser (Ctrl+E)", btnType: ButtonType.MultiToggleButton, toolType: InkTool.Eraser, scripts: {onClick:'{ return setActiveTool(this.toolType, false, _readOnly_);}' }, subMenu: [ - { title: "Stroke", toolTip: "Eraser complete strokes",btnType: ButtonType.ToggleButton, icon: "eraser", toolType:InkEraserTool.Stroke, ignoreClick: true, scripts: {onClick: '{ return setActiveTool(this.toolType, false, _readOnly_);}'}}, - { title: "Segment", toolTip: "Erase segments up to intersections",btnType: ButtonType.ToggleButton,icon: "xmark",toolType:InkEraserTool.Segment,ignoreClick:true, scripts: {onClick: '{ return setActiveTool(this.toolType, false, _readOnly_);}'}}, - { title: "Area", toolTip: "Erase like a pencil", btnType: ButtonType.ToggleButton, icon: "circle-xmark",toolType:InkEraserTool.Radius, ignoreClick: true, scripts: {onClick: '{ return setActiveTool(this.toolType, false, _readOnly_);}'}}, + { title: "Stroke", toolTip: "Eraser complete strokes",btnType: ButtonType.ToggleButton, icon: "eraser", toolType:InkEraserTool.Stroke, ignoreClick: true, scripts: {onClick: '{ return setActiveTool(this.toolType, false, _readOnly_);}'}}, + { title: "Segment", toolTip: "Erase between intersections",btnType:ButtonType.ToggleButton,icon:"xmark", toolType:InkEraserTool.Segment, ignoreClick: true, scripts: {onClick: '{ return setActiveTool(this.toolType, false, _readOnly_);}'}}, + { title: "Area", toolTip: "Erase like a pencil", btnType: ButtonType.ToggleButton, icon: "circle-xmark",toolType:InkEraserTool.Radius, ignoreClick: true, scripts: {onClick: '{ return setActiveTool(this.toolType, false, _readOnly_);}'}}, ]}, - { title: " Size", toolTip: "Size of area pencil eraser", btnType: ButtonType.NumberSliderButton, toolType: "eraserWidth", ignoreClick: true, scripts: {script: '{ return setInkProperty(this.toolType, value, _readOnly_);}'}, funcs: {hidden:"NotRadiusEraser()"}, numBtnMin: 1, linearBtnWidth:40}, - { title: "Mask", toolTip: "Make Stroke a Stencil Mask", btnType: ButtonType.ToggleButton, icon: "user-circle",toolType: "inkMask", scripts: {onClick:'{ return setInkProperty(this.toolType, value, _readOnly_);}'}, funcs: {hidden:"IsNoviceMode()" } }, - { title: "Labels", toolTip: "Show Labels Inside Shapes", btnType: ButtonType.ToggleButton, icon: "text-width", toolType: "labels", scripts: {onClick:'{ return setInkProperty(this.toolType, value, _readOnly_);}'}}, - { title: "Smart Draw", toolTip: "Draw with GPT", btnType: ButtonType.ToggleButton, icon: "user-pen", toolType: InkTool.SmartDraw, scripts: {onClick:'{ return setActiveTool(this.toolType, false, _readOnly_);}'}, funcs: {hidden: "IsNoviceMode()"}}, + { title: " Size", toolTip: "Size of area pencil eraser", btnType: ButtonType.NumberSliderButton, toolType: InkProperty.EraserWidth,ignoreClick: true, scripts: {script: '{ return setInkProperty(this.toolType, value, _readOnly_);}'}, funcs: {hidden:"NotRadiusEraser()"}, numBtnMin: 1, linearBtnWidth:40}, + { title: "Mask", toolTip: "Make Stroke a Stencil Mask", btnType: ButtonType.ToggleButton, icon: "user-circle", toolType: InkProperty.Mask, scripts: {onClick:'{ return setInkProperty(this.toolType, value, _readOnly_);}'}, funcs: {hidden:"IsNoviceMode()" } }, + { title: "Labels", toolTip: "Show Labels Inside Shapes", btnType: ButtonType.ToggleButton, icon: "text-width", toolType: InkProperty.Labels, scripts: {onClick:'{ return setInkProperty(this.toolType, value, _readOnly_);}'}}, + { title: "Smart Draw", toolTip: "Draw with GPT", btnType: ButtonType.ToggleButton, icon: "user-pen", toolType: InkTool.SmartDraw, scripts: {onClick:'{ return setActiveTool(this.toolType, false, _readOnly_);}'}, funcs: {hidden: "IsNoviceMode()"}}, ]; } diff --git a/src/client/util/bezierFit.ts b/src/client/util/bezierFit.ts index 4aef28e6b..d52460023 100644 --- a/src/client/util/bezierFit.ts +++ b/src/client/util/bezierFit.ts @@ -691,8 +691,9 @@ export function SVGToBezier(name: SVGType, attributes: any): Point[] { } case 'path': { const coordList: Point[] = []; - const startPt = attributes.d.match(/M(-?\d+\.?\d*),(-?\d+\.?\d*)/); - coordList.push({ X: parseInt(startPt[1]), Y: parseInt(startPt[2]) }); + const [startX, startY] = attributes.d.match(/M(-?\d+\.?\d*),(-?\d+\.?\d*)/).slice(1); + const startPt = { X: parseInt(startX), Y: parseInt(startY) }; + coordList.push(startPt); 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) ); @@ -721,8 +722,8 @@ export function SVGToBezier(name: SVGType, attributes: any): Point[] { const hasZ = attributes.d.match(/Z/); if (hasZ) { coordList.push(lastPt); - coordList.push({ X: parseInt(startPt[1]), Y: parseInt(startPt[2]) }); - coordList.push({ X: parseInt(startPt[1]), Y: parseInt(startPt[2]) }); + coordList.push(startPt); + coordList.push(startPt); } else { coordList.pop(); } diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 45dfcb5b8..4dc327d63 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -559,8 +559,8 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora if (setData) Doc.SetNativeHeight(doc[DocData], NumCast(doc._nativeHeight)); } - doc._width = Math.max(1, NumCast(doc._width) * scale.x); - doc._height = Math.max(1, NumCast(doc._height) * scale.y); + doc._width = Math.max(NumCast(doc._width_min, 25), NumCast(doc._width) * scale.x); + doc._height = Math.max(NumCast(doc._height_min, 25), NumCast(doc._height) * scale.y); const { deltaX, deltaY } = this.realignRefPt(doc, refCent, initWidth, initHeight); doc.x = NumCast(doc.x) + deltaX; doc.y = NumCast(doc.y) + deltaY; diff --git a/src/client/views/TagsView.scss b/src/client/views/TagsView.scss index 24f9e86bc..b741e7bfd 100644 --- a/src/client/views/TagsView.scss +++ b/src/client/views/TagsView.scss @@ -59,6 +59,7 @@ margin: auto; align-self: center; width: 90%; + color: black; } .tagsView-buttons { diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx index 0ba6b679c..836a5a2c3 100644 --- a/src/client/views/collections/CollectionCardDeckView.tsx +++ b/src/client/views/collections/CollectionCardDeckView.tsx @@ -1,4 +1,4 @@ -import { IReactionDisposer, ObservableMap, action, computed, makeObservable, observable, reaction } from 'mobx'; +import { IReactionDisposer, ObservableMap, action, computed, makeObservable, observable, reaction, trace } from 'mobx'; import { observer } from 'mobx-react'; import { computedFn } from 'mobx-utils'; import * as React from 'react'; @@ -19,7 +19,7 @@ import { dropActionType } from '../../util/DropActionTypes'; import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from '../../util/Transform'; import { undoable } from '../../util/UndoManager'; -import { PinDocView } from '../PinFuncs'; +import { PinDocView, PinProps } from '../PinFuncs'; import { StyleProp } from '../StyleProp'; import { TagItem } from '../TagsView'; import { DocumentView, DocumentViewProps } from '../nodes/DocumentView'; @@ -113,6 +113,12 @@ export class CollectionCardView extends CollectionSubView() { } } ); + this._disposers.select = reaction( + () => this.childDocs.find(d => this._docRefs.get(d)?.IsSelected), + selected => { + selected && (this.layoutDoc._card_curDoc = selected); + } + ); } componentWillUnmount() { @@ -349,7 +355,7 @@ export class CollectionCardView extends CollectionSubView() { onClickScript={this.curDoc() === doc ? undefined : this._setCurDocScript} dontCenter="y" // Don't center it vertically, because the grid it's in is already doing that and we don't want to do it twice. dragAction={(this.Document.childDragAction ?? this._props.childDragAction) as dropActionType} - showTags={BoolCast(this.layoutDoc.showChildTags)} + showTags={BoolCast(this.layoutDoc.showChildTags) || BoolCast(this.Document._layout_showTags)} whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged} dontHideOnDrag /> @@ -555,7 +561,7 @@ export class CollectionCardView extends CollectionSubView() { const dref = this._docRefs.get(doc); const { translateX, translateY, scale } = ClientUtils.GetScreenTransform(dref?.ContentDiv); - if (!scale) return new Transform(0, 0, 0); + if (!scale) return new Transform(0, 0, 1); return new Transform(-translateX + (dref?.centeringX || 0) * scale, -translateY + (dref?.centeringY || 0) * scale, 1) @@ -608,9 +614,9 @@ export class CollectionCardView extends CollectionSubView() { } return undefined; }); - getAnchor = (addAsAnnotation: boolean) => { + getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => { const anchor = Docs.Create.ConfigDocument({ annotationOn: this.Document, config_card_curDoc: this.curDoc() }); - PinDocView(anchor, { pinData: { collectionType: true, filters: true } }, this.Document); + PinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { collectionType: true, filters: true } }, this.Document); addAsAnnotation && Doc.AddDocToList(this.dataDoc, this.fieldKey + '_annotations', anchor); // when added as an annotation, links to anchors can be found as links to the document even if the anchors are not rendered return anchor; }; @@ -620,6 +626,7 @@ export class CollectionCardView extends CollectionSubView() { * Actually renders all the cards */ @computed get renderCards() { + trace(); // Map sorted documents to their rendered components return this.sortedDocs.map((doc, index) => { const cardsInRow = this.cardsInRowThatIncludesCardIndex(index); @@ -667,6 +674,7 @@ export class CollectionCardView extends CollectionSubView() { curDoc = () => DocCast(this.layoutDoc._card_curDoc); render() { + trace(); const fitContentScale = this.childCards.length === 0 ? 1 : this.fitContentScale; return ( <div diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx index c080ba27e..b7ecf9a2f 100644 --- a/src/client/views/collections/CollectionCarousel3DView.tsx +++ b/src/client/views/collections/CollectionCarousel3DView.tsx @@ -12,7 +12,7 @@ import { DocumentType } from '../../documents/DocumentTypes'; import { Docs } from '../../documents/Documents'; import { DragManager } from '../../util/DragManager'; import { Transform } from '../../util/Transform'; -import { PinDocView } from '../PinFuncs'; +import { PinDocView, PinProps } from '../PinFuncs'; import { StyleProp } from '../StyleProp'; import { DocumentView } from '../nodes/DocumentView'; import { FocusViewOptions } from '../nodes/FocusViewOptions'; @@ -107,9 +107,9 @@ export class CollectionCarousel3DView extends CollectionSubView() { } return undefined; }; - getAnchor = (addAsAnnotation: boolean) => { + getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => { const anchor = Docs.Create.ConfigDocument({ annotationOn: this.Document, config_carousel_index: this.layoutDoc._carousel_index as number }); - PinDocView(anchor, { pinData: { collectionType: true, filters: true } }, this.Document); + PinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { collectionType: true, filters: true } }, this.Document); addAsAnnotation && Doc.AddDocToList(this.dataDoc, this.fieldKey + '_annotations', anchor); // when added as an annotation, links to anchors can be found as links to the document even if the anchors are not rendered return anchor; }; diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx index f714e2a00..87c6e3e5c 100644 --- a/src/client/views/collections/CollectionCarouselView.tsx +++ b/src/client/views/collections/CollectionCarouselView.tsx @@ -8,7 +8,7 @@ import { BoolCast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields import { DocumentType } from '../../documents/DocumentTypes'; import { Docs } from '../../documents/Documents'; import { DragManager } from '../../util/DragManager'; -import { PinDocView } from '../PinFuncs'; +import { PinDocView, PinProps } from '../PinFuncs'; import { StyleProp } from '../StyleProp'; import { DocumentView } from '../nodes/DocumentView'; import { FieldViewProps } from '../nodes/FieldView'; @@ -84,9 +84,9 @@ export class CollectionCarouselView extends CollectionSubView() { return undefined; }; - getAnchor = (addAsAnnotation: boolean) => { + getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => { const anchor = Docs.Create.ConfigDocument({ annotationOn: this.Document, config_carousel_index: this.carouselIndex }); - PinDocView(anchor, { pinData: { collectionType: true, filters: true } }, this.Document); + PinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { collectionType: true, filters: true } }, this.Document); addAsAnnotation && Doc.AddDocToList(this.dataDoc, this.fieldKey + '_annotations', anchor); // when added as an annotation, links to anchors can be found as links to the document even if the anchors are not rendered return anchor; }; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 4f62b6fbb..1de6ec0c5 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1225,7 +1225,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection SmartDrawHandler.Instance.CreateDrawingDoc = this.createDrawingDoc; SmartDrawHandler.Instance.RemoveDrawing = this.removeDrawing; SmartDrawHandler.Instance.AddDrawing = this.addDrawing; - SmartDrawHandler.Instance.displaySmartDrawHandler(x, y); + SmartDrawHandler.Instance.displaySmartDrawHandler(x, y, NumCast(this.layoutDoc[this.scaleFieldKey])); }; _drawing: Doc[] = []; @@ -1320,6 +1320,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection this.Document[this.scaleFieldKey] = Math.abs(safeScale); this.setPan(-localTransform.TranslateX / safeScale, (this._props.originTopLeft ? undefined : NumCast(this.Document.layout_scrollTop) * safeScale) || -localTransform.TranslateY / safeScale, undefined, allowScroll); } + SmartDrawHandler.Instance.hideSmartDrawHandler(); }; @action diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts index 40144c4ce..5a17bc8f6 100644 --- a/src/client/views/global/globalScripts.ts +++ b/src/client/views/global/globalScripts.ts @@ -3,7 +3,7 @@ import { Colors } from 'browndash-components'; import { runInAction } from 'mobx'; import { Doc, DocListCast, Opt, StrListCast } from '../../../fields/Doc'; import { DocData } from '../../../fields/DocSymbols'; -import { InkEraserTool, InkInkTool, InkTool } from '../../../fields/InkField'; +import { InkEraserTool, InkInkTool, InkProperty, InkTool } from '../../../fields/InkField'; import { List } from '../../../fields/List'; import { BoolCast, Cast, NumCast, StrCast } from '../../../fields/Types'; import { WebField } from '../../../fields/URLField'; @@ -430,36 +430,31 @@ ScriptingGlobals.add(function activeEraserTool() { // toggle: Set overlay status of selected document // eslint-disable-next-line prefer-arrow-callback -ScriptingGlobals.add(function setInkProperty(option: 'inkMask' | 'labels' | 'fillColor' | 'strokeWidth' | 'strokeColor' | 'eraserWidth', value: string | number, checkResult?: boolean) { +ScriptingGlobals.add(function setInkProperty(option: InkProperty, value: string | number, checkResult?: boolean) { const selected = DocumentView.SelectedDocs().lastElement(); // prettier-ignore - const map: Map<'inkMask' | 'labels' | 'fillColor' | 'strokeWidth' | 'strokeColor' | 'eraserWidth', { checkResult: () => number|boolean|string|undefined; setInk: (doc: Doc) => void; setMode: () => void }> = new Map([ - ['inkMask', { + const map: Map<InkProperty, { checkResult: () => number|boolean|string|undefined; setInk: (doc: Doc) => void; setMode: () => void }> = new Map([ + [InkProperty.Mask, { checkResult: () => ((selected?._layout_isSvg ? BoolCast(selected[DocData].stroke_isInkMask) : ActiveIsInkMask())), setInk: (doc: Doc) => { doc[DocData].stroke_isInkMask = !doc.stroke_isInkMask; }, setMode: () => SetActiveIsInkMask(value ? true : false) }], - ['labels', { + [InkProperty.Labels, { checkResult: () => ((selected?._layout_isSvg ? BoolCast(selected[DocData].stroke_showLabel) : !ActiveHideTextLabels())), setInk: (doc: Doc) => { doc[DocData].stroke_showLabel = value; }, setMode: () => SetactiveHideTextLabels(value? false : true), }], - ['fillColor', { - checkResult: () => (selected?._layout_isSvg ? StrCast(selected[DocData].fillColor) : ActiveInkFillColor() ?? "transparent"), - setInk: (doc: Doc) => { doc[DocData].fillColor = StrCast(value); }, - setMode: () => SetActiveInkFillColor(StrCast(value)), - }], - [ 'strokeWidth', { + [ InkProperty.StrokeWidth, { checkResult: () => (selected?._layout_isSvg ? NumCast(selected[DocData].stroke_width) : ActiveInkWidth()), setInk: (doc: Doc) => { doc[DocData].stroke_width = NumCast(value); }, setMode: () => SetActiveInkWidth(value.toString()), }], - ['strokeColor', { + [InkProperty.StrokeColor, { checkResult: () => (selected?._layout_isSvg? StrCast(selected[DocData].color) : ActiveInkColor()), setInk: (doc: Doc) => { doc[DocData].color = String(value); }, setMode: () => SetActiveInkColor(StrCast(value)) }], - [ 'eraserWidth', { + [ InkProperty.EraserWidth, { checkResult: () => ActiveEraserWidth() === 0 ? 1 : ActiveEraserWidth(), setInk: (doc: Doc) => { }, setMode: () => SetEraserWidth(+value), diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 905c69bb8..55ad543ca 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -66,6 +66,7 @@ import { RichTextRules } from './RichTextRules'; import { schema } from './schema_rts'; import { Property } from 'csstype'; import { LabelBox } from '../LabelBox'; +import { StickerPalette } from '../../smartdraw/StickerPalette'; // import * as applyDevTools from 'prosemirror-dev-tools'; export interface FormattedTextBoxProps extends FieldViewProps { @@ -989,6 +990,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB }, icon: this.Document._layout_autoHeight ? 'lock' : 'unlock', }); + optionItems.push({ + description: this.Document.savedAsSticker ? 'Sticker Saved!' : 'Save to Stickers', + event: action(undoable(async () => await StickerPalette.addToPalette(this.Document), 'save to palette')), + icon: this.Document.savedAsSticker ? 'clipboard-check' : 'file-arrow-down', + }); !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'eye' }); const help = cm.findByDescription('Help...'); const helpItems = help?.subitems ?? []; @@ -1274,9 +1280,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB ); this._disposers.componentHeights = reaction( // set the document height when one of the component heights changes and layout_autoHeight is on - () => ({ sidebarHeight: this.sidebarHeight, textHeight: this.textHeight, layoutAutoHeight: this.layout_autoHeight, marginsHeight: this.layout_autoHeightMargins, tagsHeight: this.tagsHeight }), - ({ sidebarHeight, textHeight, layoutAutoHeight, marginsHeight, tagsHeight }) => { - const newHeight = this.contentScaling * (tagsHeight + marginsHeight + Math.max(sidebarHeight, textHeight)); + () => ({ sidebarHeight: this.sidebarHeight, textHeight: this.textHeight, layoutAutoHeight: this.layout_autoHeight, marginsHeight: this.layout_autoHeightMargins }), + ({ sidebarHeight, textHeight, layoutAutoHeight, marginsHeight }) => { + const newHeight = this.contentScaling * (marginsHeight + Math.max(sidebarHeight, textHeight)); if ( (!Array.from(FormattedTextBox._globalHighlights).includes('Bold Text') || this._props.isSelected()) && // layoutAutoHeight && diff --git a/src/client/views/nodes/imageEditor/ImageEditor.tsx b/src/client/views/nodes/imageEditor/ImageEditor.tsx index c8fe5adc3..5c7e09645 100644 --- a/src/client/views/nodes/imageEditor/ImageEditor.tsx +++ b/src/client/views/nodes/imageEditor/ImageEditor.tsx @@ -142,6 +142,7 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc ctx.clearRect(0, 0, canvasSize, canvasSize); undoStack.current = []; redoStack.current = []; + cutPts.current.length = 0; ImageUtility.drawImgToCanvas(currImg.current, canvasRef, canvasDims.width, canvasDims.height); }; @@ -366,20 +367,21 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc const canvasOriginalImg = ImageUtility.getCanvasImg(img); if (!canvasOriginalImg) return; // NOTE: cutting two diff shapes can be made possible by having the user press a button to set a new shape! + const currPts = [...cutPts.current]; if (currCutType !== BrushMode.LINE_OUT) handleReset(); // gets rid of the visible brush strokes (mostly needed for line_in) unless it's erasing (which depends on the brush strokes) let minX = img.width; let maxX = 0; let minY = img.height; let maxY = 0; - if (cutPts.current.length) { + if (currPts.length) { ctx.beginPath(); - ctx.moveTo(cutPts.current[0].x, cutPts.current[0].y); - for (let i = 0; i < cutPts.current.length; i++) { - ctx.lineTo(cutPts.current[i].x, cutPts.current[i].y); - minX = Math.min(cutPts.current[i].x, minX); - minY = Math.min(cutPts.current[i].y, minY); - maxX = Math.max(cutPts.current[i].x, maxX); - maxY = Math.max(cutPts.current[i].y, maxY); + ctx.moveTo(currPts[0].x, currPts[0].y); + for (let i = 0; i < currPts.length; i++) { + ctx.lineTo(currPts[i].x, currPts[i].y); + minX = Math.min(currPts[i].x, minX); + minY = Math.min(currPts[i].y, minY); + maxX = Math.max(currPts[i].x, maxX); + maxY = Math.max(currPts[i].y, maxY); } switch (currCutType) { case BrushMode.IN: @@ -453,7 +455,17 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc croppedCanvas.height = maxY - minY; croppedCtx.globalCompositeOperation = 'source-over'; croppedCtx.clearRect(0, 0, croppedCanvas.width, croppedCanvas.height); - croppedCtx.drawImage(image, -minX, -minY); + croppedCtx.drawImage( + image, + minX, + minY, + maxX - minX, + maxY - minY, // Source image crop area + 0, + 0, + maxX - minX, + maxY - minY // Destination area on the canvas + ); const croppedURL = croppedCanvas.toDataURL(); const croppedImage = new Image(); croppedImage.src = croppedURL; diff --git a/src/client/views/smartdraw/SmartDrawHandler.scss b/src/client/views/smartdraw/SmartDrawHandler.scss index 745e00a99..c25273876 100644 --- a/src/client/views/smartdraw/SmartDrawHandler.scss +++ b/src/client/views/smartdraw/SmartDrawHandler.scss @@ -1,11 +1,10 @@ .smart-draw-handler { position: absolute; - width: 265px; + // width: 265px; .smart-draw-main { display: flex; flex-direction: row; - margin-bottom: 5px; .smartdraw-input { color: black; @@ -14,6 +13,7 @@ } .smartdraw-options { + margin-top: 5px; display: flex; flex-direction: row; justify-content: space-around; diff --git a/src/client/views/smartdraw/SmartDrawHandler.tsx b/src/client/views/smartdraw/SmartDrawHandler.tsx index de2c0a4ce..23ab7657f 100644 --- a/src/client/views/smartdraw/SmartDrawHandler.tsx +++ b/src/client/views/smartdraw/SmartDrawHandler.tsx @@ -61,6 +61,7 @@ export class SmartDrawHandler extends ObservableReactComponent<object> { @observable private _display: boolean = false; @observable private _pageX: number = 0; @observable private _pageY: number = 0; + @observable private _scale: number = 0; @observable private _yRelativeToTop: boolean = true; @observable private _isLoading: boolean = false; @observable private _userInput: string = ''; @@ -122,9 +123,10 @@ export class SmartDrawHandler extends ObservableReactComponent<object> { }; @action - displaySmartDrawHandler = (x: number, y: number) => { + displaySmartDrawHandler = (x: number, y: number, scale: number) => { [this._pageX, this._pageY] = [x, y]; this._display = true; + this._scale = scale; }; /** @@ -200,6 +202,10 @@ export class SmartDrawHandler extends ObservableReactComponent<object> { this._showEditBox = false; }); } else { + if (this._userInput == '') { + this._isLoading = false; + return; + } runInAction(() => { this._showOptions = false; }); @@ -292,7 +298,7 @@ export class SmartDrawHandler extends ObservableReactComponent<object> { svgStrokes.forEach(child => { const convertedBezier: InkData = SVGToBezier(child.name as SVGType, child.attributes); strokeData.push([ - convertedBezier.map(point => ({ X: point.X + startPoint.X - this._size / 1.5, Y: point.Y + startPoint.Y - this._size / 2 })), + convertedBezier.map(point => ({ X: startPoint.X + (point.X - startPoint.X) * this._scale, Y: startPoint.Y + (point.Y - startPoint.Y) * this._scale })), (regenerate ? this._lastInput.autoColor : autoColor) ? child.attributes.stroke : '', (regenerate ? this._lastInput.autoColor : autoColor) ? child.attributes.fill : '', ]); diff --git a/src/fields/InkField.ts b/src/fields/InkField.ts index 702301b31..d1dda106a 100644 --- a/src/fields/InkField.ts +++ b/src/fields/InkField.ts @@ -26,6 +26,14 @@ export enum InkEraserTool { Radius = 'Radius', } +export enum InkProperty { + Mask = 'inkMask', + Labels = 'labels', + StrokeWidth = 'strokeWidth', + StrokeColor = 'strokeColor', + EraserWidth = ' eraserWidth', +} + export type Segment = Array<Bezier>; // Defines an ink as an array of points. |