{"version":3,"file":"XTextbox.min.mjs","sources":["../../../../src/shapes/canvasx/XTextbox.ts"],"sourcesContent":["import { TClassProperties, TOriginX, TOriginY } from '../../typedefs';\nimport { IText } from '../IText/IText';\nimport { classRegistry } from '../../ClassRegistry';\nimport { createTextboxDefaultControls } from '../../controls/X_commonControls';\nimport { XTextbase } from './XTextbase';\nimport { isTransformCentered, getLocalPoint } from '../../controls/util';\nimport { EntityKeys } from './type/widget.entity.textbox';\nimport { WidgetType } from './type/widget.type';\nimport { Point } from '../../Point';\nimport { WidgetTextInterface } from './type/widget.entity.text';\n\n// Default values for the textbox\nexport const textboxDefaultValues: Partial<TClassProperties<XTextbase>> = {\n    minWidth: 20,\n    dynamicMinWidth: 2,\n    splitByGrapheme: false,\n    cornerColor: 'white',\n    cornerSize: 10,\n    cornerStyle: 'circle',\n    transparentCorners: false,\n    cornerStrokeColor: 'gray',\n    connectors: [],\n};\n\n// Connector class definition\nclass Connector {\n    connectorId: string;\n    connectorType: string;\n    point: Point;\n}\n\nexport const XTextboxProps: Partial<TClassProperties<XTextbase>> = {};\n\n/**\n * Textbox class, based on IText, allows the user to resize the text rectangle\n * and wraps lines automatically. Textboxes have their Y scaling locked, the\n * user can only change width. Height is adjusted automatically based on the\n * wrapping of lines.\n */\nexport class XTextbox extends XTextbase implements WidgetTextInterface {\n    // Property declarations\n    declare minWidth: number;\n    declare tempTop: number;\n    declare hasNoText: boolean;\n    static objType: WidgetType = 'XTextbox';\n    static type: WidgetType = 'XTextbox';\n\n    declare dynamicMinWidth: number;\n    declare oneLine: boolean;\n    declare fromCopy: boolean;\n    declare originX: TOriginX;\n    declare originY: TOriginY;\n    declare connectors: Connector[];\n\n    /**\n     * Use this boolean property in order to split strings that have no white space concept.\n     * This is a cheap way to help with Chinese/Japanese.\n     * @type Boolean\n     * @since 2.6.0\n     */\n    declare splitByGrapheme: boolean;\n\n    static textLayoutProperties = [...IText.textLayoutProperties, 'width'];\n\n    static ownDefaults: Record<string, any> = textboxDefaultValues;\n\n    /**\n     * Override the getDefaults method to set default origin to center\n     */\n    static getDefaults() {\n        return {\n            ...super.getDefaults(),\n            controls: createTextboxDefaultControls(),\n            originX: 'center', // Default originX\n            originY: 'center', // Default originY\n            ...XTextbox.ownDefaults,\n        };\n    }\n\n\n    /**\n     * Constructor to initialize the textbox with default origin\n     * @param text - The initial text content\n     * @param options - Configuration options\n     */\n    constructor(text: string, options: any) {\n        super(text, options);\n\n        // Initialize dimensions\n        this.initDimensions();\n\n        // Remove height from options to manage it dynamically\n        delete options.height;\n        Object.assign(this, options);\n        this.objType = 'XTextbox';\n\n        this.dirty = true;\n\n        // Lock scaling flip and rotation to maintain aspect\n        this.lockScalingFlip = true;\n        this.lockRotation = true;\n        // Setup custom resize controls\n        this.setupCustomResizeControls();\n        // Bind event listeners for editing\n        this.on('editing:entered', this.onEditingEntered.bind(this));\n        this.on('editing:exited', this.onEditingExited.bind(this));\n    }\n    /**\n     * Set up custom resize controls that use our implementation\n     */\n    setupCustomResizeControls() {\n        // Override the action handler for 'mr' (middle right) control\n        if (this.controls && this.controls.mr) {\n            this.controls.mr.actionHandler = this.handleWidthChange.bind(this);\n        }\n\n        // Override the action handler for 'ml' (middle left) control\n        if (this.controls && this.controls.ml) {\n            this.controls.ml.actionHandler = this.handleWidthChange.bind(this);\n        }\n\n\n    }\n\n    // reserve for external interface\n    handleWidthChange2(eventData: any, transform: any, x: number, y: number) {\n\n    }\n    /**\n     * Event handler for entering edit mode\n     */\n    private onEditingEntered() {\n        const canvas = this.canvas;\n        if (!canvas) return;\n\n        // Calculate the current top-left corner in canvas coordinates\n        const topLeft = this.getBoundingRect();\n\n        // Store the current center position\n        const center = this.getCenterPoint();\n\n        // Change origin to 'left' and 'top'\n        this.originX = 'left';\n        this.originY = 'top';\n\n        // Update the position to keep the top-left corner fixed\n        this.set({\n            left: topLeft.left,\n            top: topLeft.top,\n        });\n\n        this.setCoords();\n    }\n\n    /**\n     * Event handler for exiting edit mode\n     */\n    private onEditingExited() {\n        const canvas = this.canvas;\n        if (!canvas) return;\n\n        // Calculate the current top-left corner in canvas coordinates\n        const topLeft = this.getBoundingRect();\n\n        // Change origin back to 'center'\n        this.originX = 'center';\n        this.originY = 'center';\n\n        // Calculate the new center based on top-left to keep position fixed\n        this.set({\n            left: topLeft.left + this.width / 2,\n            top: topLeft.top + this.height / 2,\n        });\n\n        this.setCoords();\n    }\n\n    /**\n     * Retrieves the object's properties\n     * @returns A record of the object's properties\n     */\n    getObject() {\n        const entityKeys: string[] = EntityKeys;\n        const result: Record<string, any> = {};\n\n        entityKeys.forEach((key) => {\n            if (key in this) {\n                result[key] = (this as any)[key];\n            }\n        });\n\n        return result;\n    }\n\n    /**\n     * Override the initDimensions method to ensure height increases downward\n     */\n    initDimensions() {\n        if (!this.initialized) {\n            return;\n        }\n        if (this.isEditing) {\n            this.initDelayedCursor();\n        }\n        this._clearCache();\n        // Clear dynamicMinWidth as it will be different after we re-wrap line\n        this.dynamicMinWidth = 0;\n\n        // Check if text contains Chinese characters\n        if (/[\\u3400-\\u9FBF]/.test(this.text)) {\n            this.splitByGrapheme = true;\n        }\n\n        // Wrap lines\n        this._styleMap = this._generateStyleMap(this._splitText());\n        // If after wrapping, the width is smaller than dynamicMinWidth, change the width and re-wrap\n        if (this.dynamicMinWidth > this.width) {\n            this.set('width', this.dynamicMinWidth);\n        }\n        if (this.textAlign.indexOf('justify') !== -1) {\n            // Once text is measured we need to make space fatter to make justified text.\n            this.enlargeSpaces();\n        }\n        // Calculate height based on wrapped text\n        const newHeight = this.calcTextHeight();\n        if (newHeight !== this.height) {\n            this.set('height', newHeight);\n            this.setCoords(); // Update coordinates after height change\n        }\n    }\n\n\n    /**\n     * Handler for width changes that maintains top-left position\n     */\n    handleWidthChange(eventData: any, transform: any, x: number, y: number) {\n        // Store original top-left position\n        const oldBoundingRect = this.getBoundingRect();\n\n        // Calculate new width based on mouse position\n        const localPoint = getLocalPoint(\n            transform,\n            transform.originX,\n            transform.originY,\n            x,\n            y\n        );\n\n        const strokePadding =\n            this.strokeWidth / (this.strokeUniform ? this.scaleX : 1);\n        const multiplier = isTransformCentered(transform) ? 2 : 1;\n        const oldWidth = this.width;\n\n        // Calculate and set new width\n        const newWidth =\n            Math.abs((localPoint.x * multiplier) / this.scaleX) - strokePadding;\n\n        // Record original left/top before width change\n        const originalLeft = this.left;\n        const originalTop = this.top;\n\n        // Set new width and update dimensions\n        this.set('width', Math.max(newWidth, this.getMinWidth()));\n\n        // Recalculate text dimensions and height\n        this.initDimensions();\n        this.set('dirty', true);\n\n        // After dimensions update, get new bounding rectangle\n        const newBoundingRect = this.getBoundingRect();\n\n        // Calculate position adjustment to keep top-left fixed\n        const dx = newBoundingRect.left - oldBoundingRect.left;\n        const dy = newBoundingRect.top - oldBoundingRect.top;\n\n        // Apply position adjustment\n        this.set({\n            left: originalLeft - dx,\n            top: originalTop - dy\n        });\n\n        // Update coordinates\n        this.setCoords();\n        this.handleWidthChange2(eventData, transform, x, y);\n\n        return oldWidth !== this.width;\n    }\n}\n\n// Register the XTextbox class with the class registry\nclassRegistry.setClass(XTextbox);\n// classRegistry.getSVGClass(Textbox); // Uncomment if SVG class registration is needed\n"],"names":["textboxDefaultValues","minWidth","dynamicMinWidth","splitByGrapheme","cornerColor","cornerSize","cornerStyle","transparentCorners","cornerStrokeColor","connectors","XTextbox","XTextbase","getDefaults","_objectSpread","super","controls","createTextboxDefaultControls","originX","originY","ownDefaults","constructor","text","options","this","initDimensions","height","Object","assign","objType","dirty","lockScalingFlip","lockRotation","setupCustomResizeControls","on","onEditingEntered","bind","onEditingExited","mr","actionHandler","handleWidthChange","ml","handleWidthChange2","eventData","transform","x","y","canvas","topLeft","getBoundingRect","getCenterPoint","set","left","top","setCoords","width","getObject","result","EntityKeys","forEach","key","initialized","isEditing","initDelayedCursor","_clearCache","test","_styleMap","_generateStyleMap","_splitText","textAlign","indexOf","enlargeSpaces","newHeight","calcTextHeight","oldBoundingRect","localPoint","getLocalPoint","strokePadding","strokeWidth","strokeUniform","scaleX","multiplier","isTransformCentered","oldWidth","newWidth","Math","abs","originalLeft","originalTop","max","getMinWidth","newBoundingRect","dx","dy","_defineProperty","IText","textLayoutProperties","classRegistry","setClass"],"mappings":"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"}