11import {
22 FillStyle ,
3+ FONT_SIZE_MAP ,
34 FontFamily ,
45 FontSize ,
56 FontStyle ,
@@ -495,6 +496,7 @@ export class CanvasEngine {
495496 this . drawText (
496497 shape . x ,
497498 shape . y ,
499+ shape . width ,
498500 shape . text ,
499501 shape . strokeFill ,
500502 shape . fontStyle ,
@@ -972,7 +974,6 @@ export class CanvasEngine {
972974 Object . assign ( textarea . style , {
973975 position : "absolute" ,
974976 display : "inline-block" ,
975- minHeight : "1em" ,
976977 backfaceVisibility : "hidden" ,
977978 margin : "0" ,
978979 padding : "0" ,
@@ -984,11 +985,13 @@ export class CanvasEngine {
984985 overflowWrap : "break-word" ,
985986 boxSizing : "content-box" ,
986987 wordBreak : "normal" ,
987- whiteSpace : "pre" ,
988+ whiteSpace : "pre-wrap " ,
988989 transform : `translate(${ x * this . scale + this . panX } px, ${ y * this . scale + this . panY } px)` ,
989990 verticalAlign : "top" ,
990991 opacity : "1" ,
991992 filter : "var(--theme-filter)" ,
993+ width : "auto" ,
994+ minHeight : "2rem" ,
992995 } ) ;
993996 // console.log(
994997 // `this.fontSize= ${this.fontSize}, this.fontFamily= ${this.fontFamily}, this.textAlign=${this.textAlign}, this.scale=${this.scale}`
@@ -1001,7 +1004,7 @@ export class CanvasEngine {
10011004 textarea . style . color = this . strokeFill ;
10021005 const fontString = `${ calFont } px/1.2 ${ this . fontFamily === "normal" ? "Arial" : this . fontFamily === "hand-drawn" ? "Excalifont, Xiaolai" : "Assistant" } ` ;
10031006 textarea . style . font = fontString ;
1004- textarea . style . zIndex = "1000 " ;
1007+ textarea . style . zIndex = "1 " ;
10051008
10061009 const collabydrawContainer = document . querySelector (
10071010 ".collabydraw-textEditorContainer"
@@ -1015,29 +1018,64 @@ export class CanvasEngine {
10151018 return ;
10161019 }
10171020
1018- // Track if there are pending changes
10191021 let hasUnsavedChanges = false ;
1020- let idleTimer : number | null = null ;
10211022
1022- // Functions to handle saving
1023+ let span : HTMLSpanElement | null = null ;
1024+
1025+ const resizeTextarea = ( ) => {
1026+ if ( span ) {
1027+ document . body . removeChild ( span ) ;
1028+ }
1029+
1030+ span = document . createElement ( "span" ) ;
1031+
1032+ Object . assign ( span . style , {
1033+ visibility : "hidden" ,
1034+ position : "absolute" ,
1035+ whiteSpace : "pre-wrap" ,
1036+ wordBreak : "break-word" ,
1037+ font : textarea . style . font ,
1038+ width : "auto" ,
1039+ height : "auto" ,
1040+ } ) ;
1041+
1042+ span . textContent = textarea . value || " " ;
1043+ document . body . appendChild ( span ) ;
1044+
1045+ requestAnimationFrame ( ( ) => {
1046+ textarea . style . width = `${ Math . max ( span ! . offsetWidth + 10 , 50 ) } px` ;
1047+ textarea . style . height = `${ Math . max ( span ! . offsetHeight , 20 ) } px` ;
1048+ } ) ;
1049+
1050+ console . log ( "span.offsetWidth= " , span . offsetWidth ) ;
1051+ console . log ( "textarea.style.width= " , textarea . style . width ) ;
1052+ } ;
1053+
1054+ textarea . addEventListener ( "input" , ( ) => {
1055+ hasUnsavedChanges = true ;
1056+ resizeTextarea ( ) ;
1057+ } ) ;
1058+ textarea . addEventListener ( "keydown" , ( e ) => {
1059+ if ( e . key === "Enter" ) {
1060+ hasUnsavedChanges = true ;
1061+ resizeTextarea ( ) ;
1062+ }
1063+ } ) ;
1064+
10231065 const save = ( ) => {
10241066 const text = textarea . value . trim ( ) ;
10251067 if ( ! text ) {
10261068 textarea . remove ( ) ;
1069+ document . body . removeChild ( span ! ) ;
10271070 return ;
10281071 }
10291072
1030- const rect = textarea . getBoundingClientRect ( ) ;
1031- const width = rect . width / this . scale ;
1032- const height = rect . height / this . scale ;
1033-
10341073 const newShape : Shape = {
10351074 id : uuidv4 ( ) ,
10361075 type : "text" ,
10371076 x : x ,
10381077 y : y ,
1039- width,
1040- height,
1078+ width : span ! . offsetWidth ,
10411079 text,
10421080 fontSize : this . fontSize ,
10431081 fontFamily : this . fontFamily ,
@@ -1066,53 +1104,34 @@ export class CanvasEngine {
10661104
10671105 if ( collabydrawContainer ?. contains ( textarea ) ) {
10681106 collabydrawContainer . removeChild ( textarea ) ;
1107+ document . body . removeChild ( span ! ) ;
10691108 }
10701109
1071- this . clearCanvas ( ) ; // Make sure text appears on canvas immediately
1110+ this . clearCanvas ( ) ;
10721111 hasUnsavedChanges = false ;
10731112 } ;
10741113
1075- // Auto-save after user stops typing for 1.5 seconds
1076- const resetIdleTimer = ( ) => {
1077- if ( idleTimer ) {
1078- clearTimeout ( idleTimer ) ;
1079- }
1080-
1081- if ( hasUnsavedChanges ) {
1082- idleTimer = window . setTimeout ( ( ) => {
1083- save ( ) ;
1084- } , 1500 ) ;
1085- }
1086- } ;
1087-
1088- // Track input changes
10891114 textarea . addEventListener ( "input" , ( ) => {
10901115 hasUnsavedChanges = true ;
1091- resetIdleTimer ( ) ;
10921116 } ) ;
10931117
1094- // Save when user presses Enter+Ctrl/Cmd
10951118 textarea . addEventListener ( "keydown" , ( e ) => {
10961119 if ( e . key === "Enter" && ( e . ctrlKey || e . metaKey ) ) {
10971120 e . preventDefault ( ) ;
10981121 save ( ) ;
10991122 }
1100- resetIdleTimer ( ) ;
11011123 } ) ;
11021124
1103- // Save when user clicks outside
11041125 const handleClickOutside = ( e : MouseEvent ) => {
11051126 if ( ! textarea . contains ( e . target as Node ) ) {
11061127 save ( ) ;
11071128 }
11081129 } ;
11091130
1110- // Delay adding the click listener to prevent immediate trigger
11111131 setTimeout ( ( ) => {
11121132 document . addEventListener ( "mousedown" , handleClickOutside ) ;
11131133 } , 100 ) ;
11141134
1115- // Clean up event listener when textarea loses focus
11161135 textarea . addEventListener ( "blur" , ( ) => {
11171136 document . removeEventListener ( "mousedown" , handleClickOutside ) ;
11181137 if ( hasUnsavedChanges ) {
@@ -1197,6 +1216,22 @@ export class CanvasEngine {
11971216 ( point ) => Math . hypot ( point . x - x , point . y - y ) <= tolerance
11981217 ) ;
11991218 }
1219+
1220+ case "text" : {
1221+ const startX = shape . x ;
1222+ const endX = shape . x + shape . width ;
1223+ const startY = shape . y ;
1224+ const textHeight = FONT_SIZE_MAP [ shape . fontSize ] ;
1225+ const endY = shape . y + textHeight ;
1226+
1227+ return (
1228+ x >= startX - tolerance &&
1229+ x <= endX + tolerance &&
1230+ y >= startY - tolerance &&
1231+ y <= endY + tolerance
1232+ ) ;
1233+ }
1234+
12001235 default :
12011236 return false ;
12021237 }
@@ -1617,14 +1652,14 @@ export class CanvasEngine {
16171652 drawText (
16181653 x : number ,
16191654 y : number ,
1655+ width : number ,
16201656 text : string ,
16211657 fillStyle : string ,
16221658 fontStyle : FontStyle ,
16231659 fontFamily : FontFamily ,
16241660 fontSize : FontSize ,
16251661 textAlign : TextAlign
16261662 ) {
1627- const width = 200 ;
16281663 const calFontSize = getFontSize ( fontSize , this . scale ) ;
16291664 const lineHeight = getLineHeight ( calFontSize ) ;
16301665
0 commit comments