Skip to content

Commit 1ade05c

Browse files
authored
fix: collaboration cursor chrome should not wrap mid-line (#1423)
Here be dragons! The collaboration cursor caret needs to: - exist in the dom as a non-zero width element, so that when hovering over it, the label can display - yet, effectively not take up space in the dom, so that it doesn't cause wrapping or otherwise effect layout of the page To achieve this, it took quite a bit of fiddling to figure out how to do this. The caret is a span which has a before and after pseudo element. The before element is what actually takes up space in the dom, and is colored via a border. The color is actually set by reading the current color from the `.collaboration-cursor__caret` element. Allowing for dynamic coloring from JS. There are a number of browser specific quirks with these hacks: - Firefox differs from Chrome & Safari in that it will split a word that is wrapping if not displayed as inline-block (whereas the others need display: inline) - Safari differs from Chrome & Firefox in that it needs the pseudo element to be position: relative to display a pseudo-element element with a negative margin The word-joiner char (\u2060) is used to make sure the caret doesn't wrap around the text. Therefore if modifying this code, please test in all major browsers to ensure that the caret is rendered correctly in all browsers.
1 parent af24b5d commit 1ade05c

File tree

2 files changed

+48
-16
lines changed

2 files changed

+48
-16
lines changed

packages/core/src/editor/editor.css

+47-13
Original file line numberDiff line numberDiff line change
@@ -77,23 +77,56 @@ Tippy popups that are appended to document.body directly
7777
opacity: 0.001;
7878
}
7979

80+
/**
81+
Here be dragons!
82+
83+
The collaboration cursor caret needs to:
84+
- exist in the dom as a non-zero width element, so that when hovering over it, the label can display
85+
- yet, effectively not take up space in the dom, so that it doesn't cause wrapping or otherwise effect layout of the page
86+
87+
To achieve this, it took quite a bit of fiddling to figure out how to do this.
88+
89+
The caret is a span which has a before and after pseudo element.
90+
The before element is what actually takes up space in the dom, and is colored via a border.
91+
The color is actually set by reading the current color from the `.collaboration-cursor__caret` element. Allowing for dynamic coloring from JS.
92+
93+
There are a number of browser specific quirks with these hacks:
94+
- Firefox differs from Chrome & Safari in that it will split a word that is wrapping if not displayed as inline-block (whereas the others need display: inline)
95+
- Safari differs from Chrome & Firefox in that it needs the pseudo element to be position: relative to display a pseudo-element element with a negative margin
96+
97+
The word-joiner char (\u2060) is used to make sure the caret doesn't wrap around the text.
98+
99+
Therefore if modifying this code, please test in all major browsers to ensure that the caret is rendered correctly in all browsers.
100+
**/
101+
80102
/* Give a remote user a caret */
81103
.collaboration-cursor__caret {
82-
outline: 1px solid #0d0d0d;
83104
position: relative;
84105
word-break: normal;
85106
white-space: nowrap !important;
86107
}
87108

88-
/* Allow the caret to be hovered over */
89-
.collaboration-cursor__caret::after {
90-
content: "";
91-
position: absolute;
92-
top: 0;
93-
right: 0;
94-
left: 0;
95-
bottom: 0;
96-
width: 2px;
109+
/* Allow the caret to be colored & hovered over */
110+
.collaboration-cursor__caret::before {
111+
/* Use currentColor to grab the color from the caret in set by JS */
112+
border-left: 2px solid currentColor;
113+
/* Make the cursor not actually take up the 2px of space within the element */
114+
margin-left: -2px;
115+
/* Fixes Safari's rendering of negative margin elements */
116+
position: relative;
117+
}
118+
119+
/* Firefox will split a word that is wrapping if not displayed as inline-block */
120+
@-moz-document url-prefix() {
121+
.collaboration-cursor__caret::before {
122+
display: inline-block;
123+
}
124+
}
125+
126+
/* Add a word-joiner (\u2060) char to each side of the caret */
127+
.collaboration-cursor__caret::after,
128+
.collaboration-cursor__caret::before {
129+
content: "⁠";
97130
}
98131

99132
/* Render the username above the caret */
@@ -103,10 +136,11 @@ Tippy popups that are appended to document.body directly
103136
font-style: normal;
104137
font-weight: 600;
105138
line-height: normal;
106-
left: 0;
139+
left: -2px;
107140
overflow: hidden;
108141
position: absolute;
109142
white-space: nowrap;
143+
user-select: none;
110144

111145
color: transparent;
112146
max-height: 5px;
@@ -121,8 +155,8 @@ Tippy popups that are appended to document.body directly
121155
max-height: 1.1rem;
122156
max-width: 20rem;
123157
padding: 0.1rem 0.3rem;
124-
top: -18px;
125-
left: -1px;
158+
top: -17px;
159+
left: -2px;
126160
border-radius: 3px 3px 3px 0;
127161

128162
transition: all 0.2s;

packages/core/src/extensions/Collaboration/createCollaborationExtensions.ts

+1-3
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ export const createCollaborationExtensions = (collaboration: {
6666
const cursorElement = document.createElement("span");
6767

6868
cursorElement.classList.add("collaboration-cursor__caret");
69-
cursorElement.setAttribute("style", `outline-color: ${user.color}`);
69+
cursorElement.setAttribute("style", `color: ${user.color}`);
7070
if (collaboration?.showCursorLabels === "always") {
7171
cursorElement.setAttribute("data-active", "");
7272
}
@@ -77,9 +77,7 @@ export const createCollaborationExtensions = (collaboration: {
7777
labelElement.setAttribute("style", `background-color: ${user.color}`);
7878
labelElement.insertBefore(document.createTextNode(user.name), null);
7979

80-
cursorElement.insertBefore(document.createTextNode("\u2060"), null); // Non-breaking space
8180
cursorElement.insertBefore(labelElement, null);
82-
cursorElement.insertBefore(document.createTextNode("\u2060"), null); // Non-breaking space
8381

8482
return cursorElement;
8583
};

0 commit comments

Comments
 (0)