UNPKG

fabric

Version:

Object model for HTML5 canvas, and SVG-to-canvas parser. Backed by jsdom and node-canvas.

1 lines 10.3 kB
{"version":3,"file":"ITextClickBehavior.min.mjs","names":[],"sources":["../../../../src/shapes/IText/ITextClickBehavior.ts"],"sourcesContent":["import type {\n ObjectPointerEvents,\n TPointerEvent,\n TPointerEventInfo,\n} from '../../EventTypeDefs';\nimport { Point } from '../../Point';\nimport { invertTransform } from '../../util/misc/matrix';\nimport { DraggableTextDelegate } from './DraggableTextDelegate';\nimport type { ITextEvents } from './ITextBehavior';\nimport { ITextKeyBehavior } from './ITextKeyBehavior';\nimport type { TOptions } from '../../typedefs';\nimport type { TextProps, SerializedTextProps } from '../Text/Text';\nimport type { IText } from './IText';\n/**\n * `LEFT_CLICK === 0`\n */\nconst notALeftClick = (e: Event) => !!(e as MouseEvent).button;\n\nexport abstract class ITextClickBehavior<\n Props extends TOptions<TextProps> = Partial<TextProps>,\n SProps extends SerializedTextProps = SerializedTextProps,\n EventSpec extends ITextEvents = ITextEvents,\n> extends ITextKeyBehavior<Props, SProps, EventSpec> {\n protected draggableTextDelegate: DraggableTextDelegate;\n\n initBehavior() {\n // Initializes event handlers related to cursor or selection\n this.on('mousedown', this._mouseDownHandler);\n this.on('mouseup', this.mouseUpHandler);\n this.on('mousedblclick', this.doubleClickHandler);\n this.on('mousetripleclick', this.tripleClickHandler);\n\n this.draggableTextDelegate = new DraggableTextDelegate(\n this as unknown as IText,\n );\n\n super.initBehavior();\n }\n\n /**\n * If this method returns true a mouse move operation over a text selection\n * will not prevent the native mouse event allowing the browser to start a drag operation.\n * shouldStartDragging can be read 'do not prevent default for mouse move event'\n * To prevent drag and drop between objects both shouldStartDragging and onDragStart should return false\n * @returns\n */\n shouldStartDragging() {\n return this.draggableTextDelegate.isActive();\n }\n\n /**\n * @public override this method to control whether instance should/shouldn't become a drag source,\n * @see also {@link DraggableTextDelegate#isActive}\n * To prevent drag and drop between objects both shouldStartDragging and onDragStart should return false\n * @returns {boolean} should handle event\n */\n onDragStart(e: DragEvent) {\n return this.draggableTextDelegate.onDragStart(e);\n }\n\n /**\n * @public override this method to control whether instance should/shouldn't become a drop target\n */\n canDrop(e: DragEvent) {\n return this.draggableTextDelegate.canDrop(e);\n }\n\n /**\n * Default handler for double click, select a word\n */\n doubleClickHandler(options: TPointerEventInfo) {\n if (!this.isEditing) {\n return;\n }\n this.selectWord(this.getSelectionStartFromPointer(options.e));\n this.renderCursorOrSelection();\n }\n\n /**\n * Default handler for triple click, select a line\n */\n tripleClickHandler(options: TPointerEventInfo) {\n if (!this.isEditing) {\n return;\n }\n this.selectLine(this.getSelectionStartFromPointer(options.e));\n this.renderCursorOrSelection();\n }\n\n /**\n * Default event handler for the basic functionalities needed on _mouseDown\n * can be overridden to do something different.\n * Scope of this implementation is: find the click position, set selectionStart\n * find selectionEnd, initialize the drawing of either cursor or selection area\n * initializing a mousedDown on a text area will cancel fabricjs knowledge of\n * current compositionMode. It will be set to false.\n */\n _mouseDownHandler({ e, alreadySelected }: ObjectPointerEvents['mousedown']) {\n if (\n !this.canvas ||\n !this.editable ||\n notALeftClick(e) ||\n this.getActiveControl()\n ) {\n return;\n }\n\n if (this.draggableTextDelegate.start(e)) {\n return;\n }\n\n this.canvas.textEditingManager.register(this);\n\n if (alreadySelected) {\n this.inCompositionMode = false;\n this.setCursorByClick(e);\n }\n\n if (this.isEditing) {\n this.__selectionStartOnMouseDown = this.selectionStart;\n if (this.selectionStart === this.selectionEnd) {\n this.abortCursorAnimation();\n }\n this.renderCursorOrSelection();\n }\n this.selected ||= alreadySelected || this.isEditing;\n }\n\n /**\n * standard handler for mouse up, overridable\n * @private\n */\n mouseUpHandler({ e, transform }: ObjectPointerEvents['mouseup']) {\n const didDrag = this.draggableTextDelegate.end(e);\n\n if (this.canvas) {\n this.canvas.textEditingManager.unregister(this);\n\n const activeObject = this.canvas._activeObject;\n if (activeObject && activeObject !== this) {\n // avoid running this logic when there is an active object\n // this because is possible with shift click and fast clicks,\n // to rapidly deselect and reselect this object and trigger an enterEdit\n return;\n }\n }\n\n if (\n !this.editable ||\n (this.group && !this.group.interactive) ||\n (transform && transform.actionPerformed) ||\n notALeftClick(e) ||\n didDrag\n ) {\n return;\n }\n\n if (this.selected && !this.getActiveControl()) {\n this.enterEditing(e);\n if (this.selectionStart === this.selectionEnd) {\n this.initDelayedCursor(true);\n } else {\n this.renderCursorOrSelection();\n }\n }\n }\n\n /**\n * Changes cursor location in a text depending on passed pointer (x/y) object\n * @param {TPointerEvent} e Event object\n */\n setCursorByClick(e: TPointerEvent) {\n const newSelection = this.getSelectionStartFromPointer(e),\n start = this.selectionStart,\n end = this.selectionEnd;\n if (e.shiftKey) {\n this.setSelectionStartEndWithShift(start, end, newSelection);\n } else {\n this.selectionStart = newSelection;\n this.selectionEnd = newSelection;\n }\n if (this.isEditing) {\n this._fireSelectionChanged();\n this._updateTextarea();\n }\n }\n\n /**\n * Returns index of a character corresponding to where an object was clicked\n * @param {TPointerEvent} e Event object\n * @return {Number} Index of a character\n */\n getSelectionStartFromPointer(e: TPointerEvent): number {\n const mouseOffset = this.canvas!.getScenePoint(e)\n .transform(invertTransform(this.calcTransformMatrix()))\n .add(new Point(-this._getLeftOffset(), -this._getTopOffset()));\n let height = 0,\n charIndex = 0,\n lineIndex = 0;\n\n for (let i = 0; i < this._textLines.length; i++) {\n if (height <= mouseOffset.y) {\n height += this.getHeightOfLine(i);\n lineIndex = i;\n if (i > 0) {\n charIndex +=\n this._textLines[i - 1].length + this.missingNewlineOffset(i - 1);\n }\n } else {\n break;\n }\n }\n const lineLeftOffset = Math.abs(this._getLineLeftOffset(lineIndex));\n let width = lineLeftOffset;\n const charLength = this._textLines[lineIndex].length;\n const chars = this.__charBounds[lineIndex];\n for (let j = 0; j < charLength; j++) {\n // i removed something about flipX here, check.\n const charWidth = chars[j].kernedWidth;\n const widthAfter = width + charWidth;\n if (mouseOffset.x <= widthAfter) {\n // if the pointer is closer to the end of the char we increment charIndex\n // in order to position the cursor after the char\n if (\n Math.abs(mouseOffset.x - widthAfter) <=\n Math.abs(mouseOffset.x - width)\n ) {\n charIndex++;\n }\n break;\n }\n width = widthAfter;\n charIndex++;\n }\n\n return Math.min(\n // if object is horizontally flipped, mirror cursor location from the end\n this.flipX ? charLength - charIndex : charIndex,\n this._text.length,\n );\n }\n}\n"],"mappings":"oWAgBA,MAAM,EAAiB,GAAA,CAAA,CAAgB,EAAiB,OAExD,IAAsB,EAAtB,cAIU,CAAA,CAAA,YAAA,GAAA,EAAA,CAAA,MAAA,GAAA,EAAA,CAAA,EAAA,KACE,wBAAA,IAAA,GAAA,CAEV,cAAA,CAEE,KAAK,GAAG,YAAa,KAAK,kBAAA,CAC1B,KAAK,GAAG,UAAW,KAAK,eAAA,CACxB,KAAK,GAAG,gBAAiB,KAAK,mBAAA,CAC9B,KAAK,GAAG,mBAAoB,KAAK,mBAAA,CAEjC,KAAK,sBAAwB,IAAI,EAC/B,KAAA,CAGF,MAAM,cAAA,CAUR,qBAAA,CACE,OAAO,KAAK,sBAAsB,UAAA,CASpC,YAAY,EAAA,CACV,OAAO,KAAK,sBAAsB,YAAY,EAAA,CAMhD,QAAQ,EAAA,CACN,OAAO,KAAK,sBAAsB,QAAQ,EAAA,CAM5C,mBAAmB,EAAA,CACZ,KAAK,YAGV,KAAK,WAAW,KAAK,6BAA6B,EAAQ,EAAA,CAAA,CAC1D,KAAK,yBAAA,EAMP,mBAAmB,EAAA,CACZ,KAAK,YAGV,KAAK,WAAW,KAAK,6BAA6B,EAAQ,EAAA,CAAA,CAC1D,KAAK,yBAAA,EAWP,kBAAA,CAAoB,EAAA,gBAAG,GAAA,CAElB,KAAK,QACL,KAAK,UAAA,CACN,EAAc,EAAA,EAAA,CACd,KAAK,kBAAA,GAKH,KAAK,sBAAsB,MAAM,EAAA,GAIrC,KAAK,OAAO,mBAAmB,SAAS,KAAA,CAEpC,IACF,KAAK,kBAAA,CAAoB,EACzB,KAAK,iBAAiB,EAAA,EAGpB,KAAK,YACP,KAAK,4BAA8B,KAAK,eACpC,KAAK,iBAAmB,KAAK,cAC/B,KAAK,sBAAA,CAEP,KAAK,yBAAA,EAEP,KAAK,WAAA,KAAA,SAAa,GAAmB,KAAK,aAO5C,eAAA,CAAiB,EAAA,UAAG,GAAA,CAClB,IAAM,EAAU,KAAK,sBAAsB,IAAI,EAAA,CAE/C,GAAI,KAAK,OAAQ,CACf,KAAK,OAAO,mBAAmB,WAAW,KAAA,CAE1C,IAAM,EAAe,KAAK,OAAO,cACjC,GAAI,GAAgB,IAAiB,KAInC,OAAA,CAKD,KAAK,UACL,KAAK,OAAA,CAAU,KAAK,MAAM,aAC1B,GAAa,EAAU,iBACxB,EAAc,EAAA,EACd,GAKE,KAAK,UAAA,CAAa,KAAK,kBAAA,GACzB,KAAK,aAAa,EAAA,CACd,KAAK,iBAAmB,KAAK,aAC/B,KAAK,kBAAA,CAAkB,EAAA,CAEvB,KAAK,yBAAA,EASX,iBAAiB,EAAA,CACf,IAAM,EAAe,KAAK,6BAA6B,EAAA,CACrD,EAAQ,KAAK,eACb,EAAM,KAAK,aACT,EAAE,SACJ,KAAK,8BAA8B,EAAO,EAAK,EAAA,EAE/C,KAAK,eAAiB,EACtB,KAAK,aAAe,GAElB,KAAK,YACP,KAAK,uBAAA,CACL,KAAK,iBAAA,EAST,6BAA6B,EAAA,CAC3B,IAAM,EAAc,KAAK,OAAQ,cAAc,EAAA,CAC5C,UAAU,EAAgB,KAAK,qBAAA,CAAA,CAAA,CAC/B,IAAI,IAAI,EAAA,CAAO,KAAK,gBAAA,CAAA,CAAmB,KAAK,eAAA,CAAA,CAAA,CAC3C,EAAS,EACX,EAAY,EACZ,EAAY,EAEd,IAAK,IAAI,EAAI,EAAG,EAAI,KAAK,WAAW,QAC9B,GAAU,EAAY,EADgB,IAExC,GAAU,KAAK,gBAAgB,EAAA,CAC/B,EAAY,EACR,EAAI,IACN,GACE,KAAK,WAAW,EAAI,GAAG,OAAS,KAAK,qBAAqB,EAAI,EAAA,EAOtE,IAAI,EADmB,KAAK,IAAI,KAAK,mBAAmB,EAAA,CAAA,CAElD,EAAa,KAAK,WAAW,GAAW,OACxC,EAAQ,KAAK,aAAa,GAChC,IAAK,IAAI,EAAI,EAAG,EAAI,EAAY,IAAK,CAEnC,IACM,EAAa,EADD,EAAM,GAAG,YAE3B,GAAI,EAAY,GAAK,EAAY,CAI7B,KAAK,IAAI,EAAY,EAAI,EAAA,EACzB,KAAK,IAAI,EAAY,EAAI,EAAA,EAEzB,IAEF,MAEF,EAAQ,EACR,IAGF,OAAO,KAAK,IAEV,KAAK,MAAQ,EAAa,EAAY,EACtC,KAAK,MAAM,OAAA,GAAA,OAAA,KAAA"}