fabric
Version:
Object model for HTML5 canvas, and SVG-to-canvas parser. Backed by jsdom and node-canvas.
1 lines • 19.8 kB
Source Map (JSON)
{"version":3,"file":"DraggableTextDelegate.mjs","names":[],"sources":["../../../../src/shapes/IText/DraggableTextDelegate.ts"],"sourcesContent":["import type {\n DragEventData,\n DropEventData,\n TPointerEvent,\n} from '../../EventTypeDefs';\nimport { Point } from '../../Point';\nimport type { IText } from './IText';\nimport { setStyle } from '../../util/internals/dom_style';\nimport { cloneStyles } from '../../util/internals/cloneStyles';\nimport type { TextStyleDeclaration } from '../Text/StyledText';\nimport { getDocumentFromElement } from '../../util/dom_misc';\nimport { CHANGED, NONE } from '../../constants';\n\n/**\n * #### Dragging IText/Textbox Lifecycle\n * - {@link start} is called from `mousedown` {@link IText#_mouseDownHandler} and determines if dragging should start by testing {@link isPointerOverSelection}\n * - if true `mousedown` {@link IText#_mouseDownHandler} is blocked to keep selection\n * - if the pointer moves, canvas fires numerous mousemove {@link Canvas#_onMouseMove} that we make sure **aren't** prevented ({@link IText#shouldStartDragging}) in order for the window to start a drag session\n * - once/if the session starts canvas calls {@link onDragStart} on the active object to determine if dragging should occur\n * - canvas fires relevant drag events that are handled by the handlers defined in this scope\n * - {@link end} is called from `mouseup` {@link IText#mouseUpHandler}, blocking IText default click behavior\n * - in case the drag session didn't occur, {@link end} handles a click, since logic to do so was blocked during `mousedown`\n */\nexport class DraggableTextDelegate {\n readonly target: IText;\n private __mouseDownInPlace = false;\n private __dragStartFired = false;\n private __isDraggingOver = false;\n private __dragStartSelection?: {\n selectionStart: number;\n selectionEnd: number;\n };\n private __dragImageDisposer?: VoidFunction;\n private _dispose?: () => void;\n\n constructor(target: IText) {\n this.target = target;\n const disposers = [\n this.target.on('dragenter', this.dragEnterHandler.bind(this)),\n this.target.on('dragover', this.dragOverHandler.bind(this)),\n this.target.on('dragleave', this.dragLeaveHandler.bind(this)),\n this.target.on('dragend', this.dragEndHandler.bind(this)),\n this.target.on('drop', this.dropHandler.bind(this)),\n ];\n this._dispose = () => {\n disposers.forEach((d) => d());\n this._dispose = undefined;\n };\n }\n\n isPointerOverSelection(e: TPointerEvent) {\n const target = this.target;\n const newSelection = target.getSelectionStartFromPointer(e);\n return (\n target.isEditing &&\n newSelection >= target.selectionStart &&\n newSelection <= target.selectionEnd &&\n target.selectionStart < target.selectionEnd\n );\n }\n\n /**\n * @public override this method to disable dragging and default to mousedown logic\n */\n start(e: TPointerEvent) {\n return (this.__mouseDownInPlace = this.isPointerOverSelection(e));\n }\n\n /**\n * @public override this method to disable dragging without discarding selection\n */\n isActive() {\n return this.__mouseDownInPlace;\n }\n\n /**\n * Ends interaction and sets cursor in case of a click\n * @returns true if was active\n */\n end(e: TPointerEvent) {\n const active = this.isActive();\n if (active && !this.__dragStartFired) {\n // mousedown has been blocked since `active` is true => cursor has not been set.\n // `__dragStartFired` is false => dragging didn't occur, pointer didn't move and is over selection.\n // meaning this is actually a click, `active` is a false positive.\n this.target.setCursorByClick(e);\n this.target.initDelayedCursor(true);\n }\n this.__mouseDownInPlace = false;\n this.__dragStartFired = false;\n this.__isDraggingOver = false;\n return active;\n }\n\n getDragStartSelection() {\n return this.__dragStartSelection;\n }\n\n /**\n * Override to customize the drag image\n * https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer/setDragImage\n */\n setDragImage(\n e: DragEvent,\n {\n selectionStart,\n selectionEnd,\n }: {\n selectionStart: number;\n selectionEnd: number;\n },\n ) {\n const target = this.target;\n const canvas = target.canvas!;\n const flipFactor = new Point(target.flipX ? -1 : 1, target.flipY ? -1 : 1);\n const boundaries = target._getCursorBoundaries(selectionStart);\n const selectionPosition = new Point(\n boundaries.left + boundaries.leftOffset,\n boundaries.top + boundaries.topOffset,\n ).multiply(flipFactor);\n const pos = selectionPosition.transform(target.calcTransformMatrix());\n const pointer = canvas.getScenePoint(e);\n const diff = pointer.subtract(pos);\n const retinaScaling = target.getCanvasRetinaScaling();\n const bbox = target.getBoundingRect();\n const correction = pos.subtract(new Point(bbox.left, bbox.top));\n const vpt = canvas.viewportTransform;\n const offset = correction.add(diff).transform(vpt, true);\n // prepare instance for drag image snapshot by making all non selected text invisible\n const bgc = target.backgroundColor;\n const styles = cloneStyles(target.styles);\n target.backgroundColor = '';\n const styleOverride = {\n stroke: 'transparent',\n fill: 'transparent',\n textBackgroundColor: 'transparent',\n };\n target.setSelectionStyles(styleOverride, 0, selectionStart);\n target.setSelectionStyles(styleOverride, selectionEnd, target.text.length);\n target.dirty = true;\n const dragImage = target.toCanvasElement({\n enableRetinaScaling: canvas.enableRetinaScaling,\n viewportTransform: true,\n });\n // restore values\n target.backgroundColor = bgc;\n target.styles = styles;\n target.dirty = true;\n // position drag image offscreen\n setStyle(dragImage, {\n position: 'fixed',\n left: `${-dragImage.width}px`,\n border: NONE,\n width: `${dragImage.width / retinaScaling}px`,\n height: `${dragImage.height / retinaScaling}px`,\n });\n this.__dragImageDisposer && this.__dragImageDisposer();\n this.__dragImageDisposer = () => {\n dragImage.remove();\n };\n getDocumentFromElement(\n (e.target || this.target.hiddenTextarea)! as HTMLElement,\n ).body.appendChild(dragImage);\n e.dataTransfer?.setDragImage(dragImage, offset.x, offset.y);\n }\n\n /**\n * @returns {boolean} determines whether {@link target} should/shouldn't become a drag source\n */\n onDragStart(e: DragEvent): boolean {\n this.__dragStartFired = true;\n const target = this.target;\n const active = this.isActive();\n if (active && e.dataTransfer) {\n const selection = (this.__dragStartSelection = {\n selectionStart: target.selectionStart,\n selectionEnd: target.selectionEnd,\n });\n const value = target._text\n .slice(selection.selectionStart, selection.selectionEnd)\n .join('');\n const data = { text: target.text, value, ...selection };\n e.dataTransfer.setData('text/plain', value);\n e.dataTransfer.setData(\n 'application/fabric',\n JSON.stringify({\n value: value,\n styles: target.getSelectionStyles(\n selection.selectionStart,\n selection.selectionEnd,\n true,\n ),\n }),\n );\n e.dataTransfer.effectAllowed = 'copyMove';\n this.setDragImage(e, data);\n }\n target.abortCursorAnimation();\n return active;\n }\n\n /**\n * use {@link targetCanDrop} to respect overriding\n * @returns {boolean} determines whether {@link target} should/shouldn't become a drop target\n */\n canDrop(e: DragEvent): boolean {\n if (\n this.target.editable &&\n !this.target.getActiveControl() &&\n !e.defaultPrevented\n ) {\n if (this.isActive() && this.__dragStartSelection) {\n // drag source trying to drop over itself\n // allow dropping only outside of drag start selection\n const index = this.target.getSelectionStartFromPointer(e);\n const dragStartSelection = this.__dragStartSelection;\n return (\n index < dragStartSelection.selectionStart ||\n index > dragStartSelection.selectionEnd\n );\n }\n return true;\n }\n return false;\n }\n\n /**\n * in order to respect overriding {@link IText#canDrop} we call that instead of calling {@link canDrop} directly\n */\n protected targetCanDrop(e: DragEvent) {\n return this.target.canDrop(e);\n }\n\n dragEnterHandler({ e }: DragEventData) {\n const canDrop = this.targetCanDrop(e);\n if (!this.__isDraggingOver && canDrop) {\n this.__isDraggingOver = true;\n }\n }\n\n dragOverHandler(ev: DragEventData) {\n const { e } = ev;\n const canDrop = this.targetCanDrop(e);\n if (!this.__isDraggingOver && canDrop) {\n this.__isDraggingOver = true;\n } else if (this.__isDraggingOver && !canDrop) {\n // drop state has changed\n this.__isDraggingOver = false;\n }\n if (this.__isDraggingOver) {\n // can be dropped, inform browser\n e.preventDefault();\n // inform event subscribers\n ev.canDrop = true;\n ev.dropTarget = this.target;\n }\n }\n\n dragLeaveHandler() {\n if (this.__isDraggingOver || this.isActive()) {\n this.__isDraggingOver = false;\n }\n }\n\n /**\n * Override the `text/plain | application/fabric` types of {@link DragEvent#dataTransfer}\n * in order to change the drop value or to customize styling respectively, by listening to the `drop:before` event\n * https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API/Drag_operations#performing_a_drop\n */\n dropHandler(ev: DropEventData) {\n const { e } = ev;\n const didDrop = e.defaultPrevented;\n this.__isDraggingOver = false;\n // inform browser that the drop has been accepted\n e.preventDefault();\n let insert = e.dataTransfer?.getData('text/plain');\n if (insert && !didDrop) {\n const target = this.target;\n const canvas = target.canvas!;\n let insertAt = target.getSelectionStartFromPointer(e);\n const { styles } = (\n e.dataTransfer!.types.includes('application/fabric')\n ? JSON.parse(e.dataTransfer!.getData('application/fabric'))\n : {}\n ) as { styles: TextStyleDeclaration[] };\n const trailing = insert[Math.max(0, insert.length - 1)];\n const selectionStartOffset = 0;\n // drag and drop in same instance\n if (this.__dragStartSelection) {\n const selectionStart = this.__dragStartSelection.selectionStart;\n const selectionEnd = this.__dragStartSelection.selectionEnd;\n if (insertAt > selectionStart && insertAt <= selectionEnd) {\n insertAt = selectionStart;\n } else if (insertAt > selectionEnd) {\n insertAt -= selectionEnd - selectionStart;\n }\n target.removeChars(selectionStart, selectionEnd);\n // prevent `dragend` from handling event\n delete this.__dragStartSelection;\n }\n // remove redundant line break\n if (\n target._reNewline.test(trailing) &&\n (target._reNewline.test(target._text[insertAt]) ||\n insertAt === target._text.length)\n ) {\n insert = insert.trimEnd();\n }\n // inform subscribers\n ev.didDrop = true;\n ev.dropTarget = target;\n // finalize\n target.insertChars(insert, styles, insertAt);\n // can this part be moved in an outside event? andrea to check.\n canvas.setActiveObject(target);\n target.enterEditing(e);\n target.selectionStart = Math.min(\n insertAt + selectionStartOffset,\n target._text.length,\n );\n target.selectionEnd = Math.min(\n target.selectionStart + insert.length,\n target._text.length,\n );\n target.hiddenTextarea!.value = target.text;\n target._updateTextarea();\n target.hiddenTextarea!.focus();\n target.fire(CHANGED, {\n index: insertAt + selectionStartOffset,\n action: 'drop',\n });\n canvas.fire('text:changed', { target });\n canvas.contextTopDirty = true;\n canvas.requestRenderAll();\n }\n }\n\n /**\n * fired only on the drag source after drop (if occurred)\n * handle changes to the drag source in case of a drop on another object or a cancellation\n * https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API/Drag_operations#finishing_a_drag\n */\n dragEndHandler({ e }: DragEventData) {\n if (this.isActive() && this.__dragStartFired) {\n // once the drop event finishes we check if we need to change the drag source\n // if the drag source received the drop we bail out since the drop handler has already handled logic\n if (this.__dragStartSelection) {\n const target = this.target;\n const canvas = this.target.canvas!;\n const { selectionStart, selectionEnd } = this.__dragStartSelection;\n const dropEffect = e.dataTransfer?.dropEffect || NONE;\n if (dropEffect === NONE) {\n // pointer is back over selection\n target.selectionStart = selectionStart;\n target.selectionEnd = selectionEnd;\n target._updateTextarea();\n target.hiddenTextarea!.focus();\n } else {\n target.clearContextTop();\n if (dropEffect === 'move') {\n target.removeChars(selectionStart, selectionEnd);\n target.selectionStart = target.selectionEnd = selectionStart;\n target.hiddenTextarea &&\n (target.hiddenTextarea.value = target.text);\n target._updateTextarea();\n target.fire(CHANGED, {\n index: selectionStart,\n action: 'dragend',\n });\n canvas.fire('text:changed', { target });\n canvas.requestRenderAll();\n }\n target.exitEditing();\n }\n }\n }\n\n this.__dragImageDisposer && this.__dragImageDisposer();\n delete this.__dragImageDisposer;\n delete this.__dragStartSelection;\n this.__isDraggingOver = false;\n }\n\n dispose() {\n this._dispose && this._dispose();\n }\n}\n"],"mappings":"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"}