Skip to content

Commit f39b0e9

Browse files
committed
fix: text display issue in dark mode
1 parent 678b8a9 commit f39b0e9

File tree

6 files changed

+80
-102
lines changed

6 files changed

+80
-102
lines changed

apps/collabydraw/app/globals.css

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -236,8 +236,6 @@ h6 {
236236

237237
--form-input: #121212;
238238
--form-input-hover: #141414;
239-
--form-input: #fff;
240-
--form-input-hover: #f1f3f5;
241239
--color-border-input: #212121;
242240
--form-color-text: #ced4da;
243241
--color-primary: #a8a5ff;
@@ -739,10 +737,6 @@ button {
739737
z-index: 2;
740738
}
741739

742-
.dark .collabydraw-textEditorContainer textarea {
743-
filter: var(--theme-filter);
744-
}
745-
746740
.canvas-bg-color-item {
747741
filter: var(--theme-filter);
748742
}
@@ -784,4 +778,8 @@ button {
784778
.lg\:prose-xl :where(.lg\:prose-xl>:first-child):not(:where([class~=not-prose], [class~=not-prose] *)) {
785779
margin-top: 0;
786780
}
781+
}
782+
783+
.collabydraw-texty::-webkit-scrollbar {
784+
display: none;
787785
}

apps/collabydraw/canvas-engine/CanvasEngine.ts

Lines changed: 53 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import {
3333
getDashArrayDotted,
3434
RECT_CORNER_RADIUS_FACTOR,
3535
ROUND_RADIUS_FACTOR,
36+
TEXT_ADJUSTED_HEIGHT,
3637
WS_URL,
3738
} from "@/config/constants";
3839
import { MessageQueue } from "./MessageQueue";
@@ -129,6 +130,9 @@ export class CanvasEngine {
129130
private currentTheme: "light" | "dark" | null = null;
130131
private onLiveUpdateFromSelection?: (shape: Shape) => void;
131132

133+
private activeTextarea: HTMLTextAreaElement | null = null;
134+
private activeTextPosition: { x: number; y: number } | null = null;
135+
132136
constructor(
133137
canvas: HTMLCanvasElement,
134138
roomId: string | null,
@@ -227,8 +231,6 @@ export class CanvasEngine {
227231
console.log(`Assigned connection ID: ${this.connectionId}`);
228232
}
229233

230-
// console.log("engine this.connectionId = ", this.connectionId);
231-
// console.log("engine this.userId = ", this.userId);
232234
switch (data.type) {
233235
case WsDataType.USER_JOINED:
234236
if (
@@ -262,36 +264,15 @@ export class CanvasEngine {
262264
break;
263265

264266
case WsDataType.CURSOR_MOVE:
265-
// console.log("=== case WsDataType.CURSOR_MOVE ===");
266-
// console.log(
267-
// `this.userId=${this.userId} & data.userId=${data.userId}`
268-
// );
269-
// console.log(
270-
// `this.userName=${this.userName} & data.userName=${data.userName}`
271-
// );
272-
// console.log(
273-
// `this.connectionId=${this.connectionId} & data.connectionId=${data.connectionId}`
274-
// );
275267
if (data.userId !== this.userId && data.message) {
276-
// const decrypted = await decryptData(
277-
// data.message,
278-
// this.encryptionKey!
279-
// );
280-
// console.log("coords/data.message: ", data.message);
281268
const coords = JSON.parse(data.message);
282-
// const coords:{x:number,y:number} = data.message;
283-
// console.log("coords: ", coords);
284-
// console.log(
285-
// `x: ${coords.x}, y: ${coords.y}, user: ${data.userName}`
286-
// );
287269
const key = `${data.userId}-${data.connectionId}`;
288270
this.remoteCursors.set(key, {
289271
x: coords.x,
290272
y: coords.y,
291273
userId: data.userId,
292274
userName: data.userName ?? data.userId,
293275
});
294-
// console.log("remoteCursors map size:", this.remoteCursors.size);
295276
this.clearCanvas();
296277
}
297278
break;
@@ -357,23 +338,9 @@ export class CanvasEngine {
357338
connectionId: data.connectionId,
358339
shapeId: streamedShape.id,
359340
});
360-
// console.log("STREAM_SHAPE streamKey2 = ", streamKey);
361-
// console.log(
362-
// "STREAM_SHAPE this.remoteStreamingShapes before = ",
363-
// this.remoteStreamingShapes
364-
// );
365341
this.remoteStreamingShapes.set(streamKey, streamedShape);
366-
// console.log(
367-
// "STREAM_SHAPE this.remoteStreamingShapes after = ",
368-
// this.remoteStreamingShapes
369-
// );
370342
const userConnKey = `${data.userId}-${data.connectionId}`;
371343
this.remoteClickIndicators.set(userConnKey, Date.now());
372-
// console.log(
373-
// "this.remoteClickIndicators = ",
374-
// this.remoteClickIndicators
375-
// );
376-
377344
this.clearCanvas();
378345
} catch (err) {
379346
console.error("Error handling streamed shape:", err);
@@ -425,21 +392,12 @@ export class CanvasEngine {
425392
this.encryptionKey!
426393
);
427394
const shape = JSON.parse(decrypted);
428-
// console.log(
429-
// "DRAW this.remoteStreamingShapes before = ",
430-
// this.remoteStreamingShapes
431-
// );
432395
const streamKey = getStreamKey({
433396
userId: data.userId,
434397
connectionId: data.connectionId,
435398
shapeId: shape.id,
436399
});
437-
// console.log("streamKey = ", streamKey);
438400
this.remoteStreamingShapes.delete(streamKey);
439-
// console.log(
440-
// "DRAW this.remoteStreamingShapes after = ",
441-
// this.remoteStreamingShapes
442-
// );
443401
this.updateShapes([shape]);
444402
this.notifyShapeCountChange();
445403
}
@@ -860,6 +818,7 @@ export class CanvasEngine {
860818
shape.x,
861819
shape.y,
862820
shape.width,
821+
shape.height,
863822
shape.text,
864823
shape.strokeFill,
865824
shape.fontStyle,
@@ -870,6 +829,11 @@ export class CanvasEngine {
870829
}
871830
});
872831

832+
if (this.activeTextarea && this.activeTextPosition) {
833+
const { x, y } = this.activeTextPosition;
834+
this.activeTextarea.style.transform = `translate(${x * this.scale + this.panX}px, ${y * this.scale + this.panY}px)`;
835+
}
836+
873837
this.remoteStreamingShapes.forEach((shape) => {
874838
if (shape.type === "rectangle") {
875839
this.drawRect(
@@ -938,6 +902,7 @@ export class CanvasEngine {
938902
shape.x,
939903
shape.y,
940904
shape.width,
905+
shape.height,
941906
shape.text,
942907
shape.strokeFill,
943908
shape.fontStyle,
@@ -960,28 +925,21 @@ export class CanvasEngine {
960925
}
961926

962927
this.remoteCursors.forEach((cursor, userConnKey) => {
963-
// console.log("cursor = ", cursor);
964-
// console.log("userConnKey = ", userConnKey);
965928
const { x, y, userId, userName } = cursor;
966929
const screenX = x * this.scale + this.panX;
967930
const screenY = y * this.scale + this.panY;
968931

969932
const cursorColor: string = getClientColor({ userId, userName });
970-
// const labelStrokeColor = this.currentTheme === "dark" ? "#2f6330" : COLOR_DRAG_CALL;
971933
const boxBackground = cursorColor;
972934
const boxTextColor = COLOR_CHARCOAL_BLACK;
973935
const pointerWidth = 12;
974936
const pointerHeight = 15;
975937

976938
const lastClickTime = this.remoteClickIndicators.get(userConnKey);
977-
if (lastClickTime) {
978-
// console.log("lastClickTime = ", lastClickTime);
979-
}
980939
const showClickCircle =
981940
!!lastClickTime && Date.now() - lastClickTime < 800;
982941

983942
if (showClickCircle) {
984-
// console.log("showClickCircle = ", showClickCircle);
985943
this.ctx.beginPath();
986944
this.ctx.arc(x, y, 14, 0, Math.PI * 2, false);
987945
this.ctx.lineWidth = 3;
@@ -1050,7 +1008,7 @@ export class CanvasEngine {
10501008
this.ctx.strokeStyle = COLOR_WHITE;
10511009
this.ctx.stroke();
10521010

1053-
// Optional highlight stroke for speaker // Option 2 for showing active indicator
1011+
// Highlight stroke for speaker // Option 2 for showing active indicator
10541012
// this.ctx.beginPath();
10551013
// this.ctx.roundRect(boxX - 2, boxY - 2, boxWidth + 4, boxHeight + 4, 8);
10561014
// this.ctx.strokeStyle = labelStrokeColor;
@@ -1079,7 +1037,6 @@ export class CanvasEngine {
10791037

10801038
mouseDownHandler = (e: MouseEvent) => {
10811039
const { x, y } = this.transformPanScale(e.clientX, e.clientY);
1082-
// console.log("x, y: ", x, y);
10831040
if (this.activeTool === "selection") {
10841041
const selectedShape = this.SelectionController.getSelectedShape();
10851042
if (selectedShape) {
@@ -1128,7 +1085,7 @@ export class CanvasEngine {
11281085
strokeStyle: this.strokeStyle,
11291086
fillStyle: this.fillStyle,
11301087
});
1131-
} else if (this.activeTool == "text") {
1088+
} else if (this.activeTool === "text") {
11321089
this.clicked = false;
11331090
this.handleTexty(e);
11341091
} else if (this.activeTool === "eraser") {
@@ -1212,7 +1169,6 @@ export class CanvasEngine {
12121169
const height = y - this.startY;
12131170

12141171
let shape: Shape | null = null;
1215-
// console.log("this.streamingShapeId in ");
12161172
switch (this.activeTool) {
12171173
case "rectangle":
12181174
shape = {
@@ -1640,8 +1596,6 @@ export class CanvasEngine {
16401596
clientY: touch.clientY,
16411597
});
16421598

1643-
// console.log("Touch started at", touch.clientX, touch.clientY);
1644-
16451599
this.mouseDownHandler(simulatedMouse);
16461600
};
16471601

@@ -1674,40 +1628,52 @@ export class CanvasEngine {
16741628
const { x, y } = this.transformPanScale(e.clientX, e.clientY);
16751629

16761630
const textarea = document.createElement("textarea");
1631+
this.activeTextarea = textarea;
1632+
this.activeTextPosition = { x, y };
16771633
Object.assign(textarea.style, {
16781634
position: "absolute",
16791635
display: "inline-block",
16801636
backfaceVisibility: "hidden",
16811637
margin: "0",
16821638
padding: "0",
1683-
border: "0",
1639+
border: `1px dotted ${this.strokeFill}`,
16841640
outline: "0",
16851641
resize: "none",
16861642
background: "transparent",
1687-
overflow: "hidden",
1688-
overflowWrap: "break-word",
1643+
overflowX: "hidden",
1644+
overflowY: "hidden",
1645+
overflowWrap: "normal",
16891646
boxSizing: "content-box",
16901647
wordBreak: "normal",
1691-
whiteSpace: "pre-wrap",
1648+
whiteSpace: "pre",
16921649
transform: `translate(${x * this.scale + this.panX}px, ${y * this.scale + this.panY}px)`,
16931650
verticalAlign: "top",
16941651
opacity: "1",
1695-
filter: "var(--theme-filter)",
1652+
wrap: "off",
1653+
tabIndex: 0,
1654+
dir: "auto",
1655+
scrollbarWidth: "none", // Firefox
1656+
msOverflowStyle: "none", // IE/Edge
16961657
width: "auto",
1697-
minHeight: "2rem",
1658+
minHeight: "auto",
16981659
});
1699-
// console.log(
1700-
// `this.fontSize= ${this.fontSize}, this.fontFamily= ${this.fontFamily}, this.textAlign=${this.textAlign}, this.scale=${this.scale}`
1701-
// );
17021660
const calFont = getFontSize(this.fontSize, this.scale);
17031661
textarea.classList.add("collabydraw-texty");
1704-
textarea.dir = "auto";
1705-
textarea.tabIndex = 0;
1706-
textarea.wrap = "off";
17071662
textarea.style.color = this.strokeFill;
17081663
const fontString = `${calFont}px/1.2 ${this.fontFamily === "normal" ? "Arial" : this.fontFamily === "hand-drawn" ? "Collabyfont, Xiaolai" : "Assistant"}`;
17091664
textarea.style.font = fontString;
1710-
textarea.style.zIndex = "1";
1665+
textarea.style.zIndex = "100";
1666+
1667+
const rawMaxWidth =
1668+
window.innerWidth || document.documentElement.clientWidth;
1669+
const rawMaxHeight =
1670+
window.innerHeight || document.documentElement.clientHeight;
1671+
1672+
const calMaxWidth = rawMaxWidth - x - TEXT_ADJUSTED_HEIGHT;
1673+
const calMaxHeight = rawMaxHeight - y - TEXT_ADJUSTED_HEIGHT;
1674+
1675+
textarea.style.maxWidth = `${calMaxWidth}px`;
1676+
textarea.style.maxHeight = `${calMaxHeight}px`;
17111677

17121678
const collabydrawContainer = document.querySelector(
17131679
".collabydraw-textEditorContainer"
@@ -1731,23 +1697,24 @@ export class CanvasEngine {
17311697
}
17321698

17331699
span = document.createElement("span");
1734-
17351700
Object.assign(span.style, {
17361701
visibility: "hidden",
17371702
position: "absolute",
17381703
whiteSpace: "pre-wrap",
17391704
wordBreak: "break-word",
17401705
font: textarea.style.font,
1741-
width: "auto",
1742-
height: "auto",
1706+
padding: "0",
1707+
margin: "0",
1708+
lineHeight: "1.2",
17431709
});
17441710

17451711
span.textContent = textarea.value || " ";
17461712
document.body.appendChild(span);
17471713

17481714
requestAnimationFrame(() => {
1749-
textarea.style.width = `${Math.max(span!.offsetWidth + 10, 50)}px`;
1750-
textarea.style.height = `${Math.max(span!.offsetHeight, 20)}px`;
1715+
textarea.style.width = `${Math.max(span!.offsetWidth + TEXT_ADJUSTED_HEIGHT, TEXT_ADJUSTED_HEIGHT)}px`;
1716+
textarea.style.height = `${Math.max(span!.offsetHeight + TEXT_ADJUSTED_HEIGHT, TEXT_ADJUSTED_HEIGHT)}px`;
1717+
textarea.style.overflow = "scroll";
17511718
});
17521719
};
17531720

@@ -1762,7 +1729,10 @@ export class CanvasEngine {
17621729
}
17631730
});
17641731

1732+
let saveCalled = false;
17651733
const save = () => {
1734+
if (saveCalled) return;
1735+
saveCalled = true;
17661736
const text = textarea.value.trim();
17671737
if (!text) {
17681738
textarea.remove();
@@ -1774,12 +1744,15 @@ export class CanvasEngine {
17741744
if (!span) {
17751745
throw new Error("Span is null");
17761746
}
1747+
this.activeTextarea = null;
1748+
this.activeTextPosition = null;
17771749
const newShape: Shape = {
17781750
id: uuidv4(),
17791751
type: "text",
17801752
x: x,
17811753
y: y,
1782-
width: span.offsetWidth,
1754+
width: textarea.offsetWidth,
1755+
height: textarea.offsetHeight - TEXT_ADJUSTED_HEIGHT,
17831756
text,
17841757
fontSize: this.fontSize,
17851758
fontFamily: this.fontFamily,
@@ -1831,6 +1804,7 @@ export class CanvasEngine {
18311804

18321805
const handleClickOutside = (e: MouseEvent) => {
18331806
if (!textarea.contains(e.target as Node)) {
1807+
document.removeEventListener("mousedown", handleClickOutside);
18341808
save();
18351809
}
18361810
};
@@ -2363,6 +2337,7 @@ export class CanvasEngine {
23632337
x: number,
23642338
y: number,
23652339
width: number,
2340+
height: number,
23662341
text: string,
23672342
fillStyle: string,
23682343
fontStyle: FontStyle,

0 commit comments

Comments
 (0)