33 EditorSelection ,
44 Module ,
55 Sandbox ,
6- UserSelection ,
76} from '@codesandbox/common/lib/types' ;
87import { indexToLineAndColumn } from 'app/overmind/utils/common' ;
98import { actions , dispatch } from 'codesandbox-api' ;
@@ -198,9 +197,36 @@ export class ModelsHandler {
198197 this . isApplyingOperation = false ;
199198 }
200199
200+ public clearUserSelections ( userId : string ) {
201+ const decorations = Object . keys ( this . userSelectionDecorations ) . filter ( d =>
202+ d . startsWith ( userId )
203+ ) ;
204+ Object . keys ( this . moduleModels ) . forEach ( async key => {
205+ const moduleModel = this . moduleModels [ key ] ;
206+
207+ if ( ! moduleModel . model ) {
208+ return ;
209+ }
210+
211+ const model = await moduleModel . model ;
212+
213+ decorations . forEach ( decorationId => {
214+ if ( decorationId . startsWith ( userId + model . id ) ) {
215+ this . userSelectionDecorations [ decorationId ] = model . deltaDecorations (
216+ this . userSelectionDecorations [ decorationId ] || [ ] ,
217+ [ ]
218+ ) ;
219+ }
220+ } ) ;
221+ } ) ;
222+ }
223+
224+ nameTagTimeouts : { [ name : string ] : number } = { } ;
225+
201226 public async updateUserSelections (
202227 module ,
203- userSelections : Array < UserSelection | EditorSelection >
228+ userSelections : EditorSelection [ ] ,
229+ showNameTag = true
204230 ) {
205231 const moduleModel = this . getModuleModel ( module ) ;
206232
@@ -212,19 +238,15 @@ export class ModelsHandler {
212238
213239 const model = await moduleModel . model ;
214240 const lines = model . getLinesContent ( ) || [ ] ;
215- const activeEditor = this . editorApi . getActiveCodeEditor ( ) ;
216241
217- userSelections . forEach ( ( data : EditorSelection & UserSelection ) => {
242+ userSelections . forEach ( ( data : EditorSelection ) => {
218243 const { userId } = data ;
219244
220- const decorationId = module . shortid + userId ;
245+ const decorationId = userId + model . id + module . shortid ;
221246 if ( data . selection === null ) {
222- this . userSelectionDecorations [
223- decorationId
224- ] = activeEditor . deltaDecorations (
247+ this . userSelectionDecorations [ decorationId ] = model . deltaDecorations (
225248 this . userSelectionDecorations [ decorationId ] || [ ] ,
226- [ ] ,
227- data . userId
249+ [ ]
228250 ) ;
229251
230252 return ;
@@ -233,82 +255,75 @@ export class ModelsHandler {
233255 const decorations = [ ] ;
234256 const { selection, color, name } = data ;
235257
236- if ( selection ) {
237- const addCursor = ( position , className ) => {
238- const cursorPos = indexToLineAndColumn ( lines , position ) ;
239-
240- decorations . push ( {
241- range : new this . monaco . Range (
242- cursorPos . lineNumber ,
243- cursorPos . column ,
244- cursorPos . lineNumber ,
245- cursorPos . column
246- ) ,
247- options : {
248- beforeContentClassName : this . userClassesGenerated [ className ] ,
249- zIndex : 2 ,
250- } ,
251- } ) ;
258+ const getCursorDecoration = ( position , className ) => {
259+ const cursorPos = indexToLineAndColumn ( lines , position ) ;
260+
261+ return {
262+ range : new this . monaco . Range (
263+ cursorPos . lineNumber ,
264+ cursorPos . column ,
265+ cursorPos . lineNumber ,
266+ cursorPos . column
267+ ) ,
268+ options : {
269+ className : `${ this . userClassesGenerated [ className ] } ` ,
270+ } ,
252271 } ;
272+ } ;
253273
254- const addSelection = ( start , end , className ) => {
255- const from = indexToLineAndColumn ( lines , start ) ;
256- const to = indexToLineAndColumn ( lines , end ) ;
274+ const getSelectionDecoration = ( start , end , className ) => {
275+ const from = indexToLineAndColumn ( lines , start ) ;
276+ const to = indexToLineAndColumn ( lines , end ) ;
257277
258- decorations . push ( {
259- range : new this . monaco . Range (
260- from . lineNumber ,
261- from . column ,
262- to . lineNumber ,
263- to . column
264- ) ,
265- options : {
266- className : this . userClassesGenerated [ className ] ,
267- } ,
268- } ) ;
278+ return {
279+ range : new this . monaco . Range (
280+ from . lineNumber ,
281+ from . column ,
282+ to . lineNumber ,
283+ to . column
284+ ) ,
285+ options : {
286+ className : this . userClassesGenerated [ className ] ,
287+ } ,
269288 } ;
289+ } ;
290+ const prefix = color . join ( '-' ) + userId ;
291+ const cursorClassName = prefix + '-cursor' ;
292+ const nameTagClassName = prefix + '-nametag' ;
293+ const secondaryCursorClassName = prefix + '-secondary-cursor' ;
294+ const selectionClassName = prefix + '-selection' ;
295+ const secondarySelectionClassName = prefix + '-secondary-selection' ;
270296
271- const prefix = color . join ( '-' ) + userId ;
272- const cursorClassName = prefix + '-cursor' ;
273- const secondaryCursorClassName = prefix + '-secondary-cursor' ;
274- const selectionClassName = prefix + '-selection' ;
275- const secondarySelectionClassName = prefix + '-secondary-selection' ;
276-
297+ if ( selection ) {
298+ const nameStyles = {
299+ content : name ,
300+ position : 'absolute' ,
301+ bottom : '100%' ,
302+ backgroundColor : `rgb(${ color [ 0 ] } , ${ color [ 1 ] } , ${ color [ 2 ] } )` ,
303+ zIndex : 200 ,
304+ color :
305+ color [ 0 ] + color [ 1 ] + color [ 2 ] > 500
306+ ? 'rgba(0, 0, 0, 0.8)'
307+ : 'white' ,
308+ padding : '0 4px' ,
309+ borderRadius : 2 ,
310+ borderBottomLeftRadius : 0 ,
311+ fontSize : '.75rem' ,
312+ fontWeight : 600 ,
313+ userSelect : 'none' ,
314+ pointerEvents : 'none' ,
315+ width : 'max-content' ,
316+ fontFamily : 'dm, Menlo, monospace' ,
317+ } ;
277318 if ( ! this . userClassesGenerated [ cursorClassName ] ) {
278- const nameStyles = {
279- content : name ,
280- position : 'absolute' ,
281- bottom : '100%' ,
282- backgroundColor : `rgb(${ color [ 0 ] } , ${ color [ 1 ] } , ${ color [ 2 ] } )` ,
283- zIndex : 200 ,
284- color :
285- color [ 0 ] + color [ 1 ] + color [ 2 ] > 500
286- ? 'rgba(0, 0, 0, 0.8)'
287- : 'white' ,
288- padding : '0 4px' ,
289- borderRadius : 2 ,
290- borderBottomLeftRadius : 0 ,
291- fontSize : '.75rem' ,
292- fontWeight : 600 ,
293- userSelect : 'none' ,
294- pointerEvents : 'none' ,
295- width : 'max-content' ,
296- } ;
297-
298319 this . userClassesGenerated [ cursorClassName ] = `${ css ( {
320+ display : 'inherit' ,
299321 position : 'absolute' ,
300322 backgroundColor : `rgba(${ color [ 0 ] } , ${ color [ 1 ] } , ${ color [ 2 ] } , 0.8)` ,
301323 width : '2px !important' ,
302324 height : '100%' ,
303325 cursor : 'text' ,
304326 zIndex : 200 ,
305- ':before' : {
306- animation : `${ fadeOut } 0.3s` ,
307- animationDelay : '1s' ,
308- animationFillMode : 'forwards' ,
309- opacity : 1 ,
310- ...nameStyles ,
311- } ,
312327 ':hover' : {
313328 ':before' : {
314329 animation : `${ fadeIn } 0.3s` ,
@@ -320,6 +335,18 @@ export class ModelsHandler {
320335 } ) } `;
321336 }
322337
338+ if ( ! this . userClassesGenerated [ nameTagClassName ] ) {
339+ this . userClassesGenerated [ nameTagClassName ] = `${ css ( {
340+ ':before' : {
341+ animation : `${ fadeOut } 0.3s` ,
342+ animationDelay : '1s' ,
343+ animationFillMode : 'forwards' ,
344+ opacity : 1 ,
345+ ...nameStyles ,
346+ } ,
347+ } ) } `;
348+ }
349+
323350 if ( ! this . userClassesGenerated [ secondaryCursorClassName ] ) {
324351 this . userClassesGenerated [ secondaryCursorClassName ] = `${ css ( {
325352 backgroundColor : `rgba(${ color [ 0 ] } , ${ color [ 1 ] } , ${ color [ 2 ] } , 0.6)` ,
@@ -343,48 +370,65 @@ export class ModelsHandler {
343370 } ) } `;
344371 }
345372
346- // These types are not working, have to figure this out
347- // @ts -ignore
348- addCursor ( selection . primary . cursorPosition , cursorClassName ) ;
349- // @ts -ignore
373+ decorations . push (
374+ getCursorDecoration ( selection . primary . cursorPosition , cursorClassName )
375+ ) ;
376+
350377 if ( selection . primary . selection . length ) {
351- addSelection (
352- // @ts -ignore
353- selection . primary . selection [ 0 ] ,
354- // @ts -ignore
355- selection . primary . selection [ 1 ] ,
356- selectionClassName
378+ decorations . push (
379+ getSelectionDecoration (
380+ // @ts -ignore
381+ selection . primary . selection [ 0 ] ,
382+ // @ts -ignore
383+ selection . primary . selection [ 1 ] ,
384+ selectionClassName
385+ )
357386 ) ;
358387 }
359388
360- // @ts -ignore
361389 if ( selection . secondary . length ) {
362- // @ts -ignore
363390 selection . secondary . forEach ( s => {
364- addCursor ( s . cursorPosition , secondaryCursorClassName ) ;
391+ decorations . push (
392+ getCursorDecoration ( s . cursorPosition , secondaryCursorClassName )
393+ ) ;
365394
366395 if ( s . selection . length ) {
367- addSelection (
368- s . selection [ 0 ] ,
369- s . selection [ 1 ] ,
370- secondarySelectionClassName
396+ decorations . push (
397+ getSelectionDecoration (
398+ s . selection [ 0 ] ,
399+ s . selection [ 1 ] ,
400+ secondarySelectionClassName
401+ )
371402 ) ;
372403 }
373404 } ) ;
374405 }
375406 }
376407
377- // Allow new model to attach in case it's attaching
378- // Should ideally verify this, this is hacky
379- requestAnimationFrame ( ( ) => {
380- this . userSelectionDecorations [
381- decorationId
382- ] = activeEditor . deltaDecorations (
383- this . userSelectionDecorations [ decorationId ] || [ ] ,
384- decorations ,
385- userId
408+ this . userSelectionDecorations [ decorationId ] = model . deltaDecorations (
409+ this . userSelectionDecorations [ decorationId ] || [ ] ,
410+ decorations
411+ ) ;
412+
413+ if ( this . nameTagTimeouts [ decorationId ] ) {
414+ clearTimeout ( this . nameTagTimeouts [ decorationId ] ) ;
415+ }
416+ if ( showNameTag ) {
417+ const decoration = model . deltaDecorations (
418+ [ ] ,
419+ [
420+ getCursorDecoration (
421+ selection . primary . cursorPosition ,
422+ nameTagClassName
423+ ) ,
424+ ]
386425 ) ;
387- } ) ;
426+ this . userSelectionDecorations [ decorationId ] . push ( decoration ) ;
427+ this . nameTagTimeouts [ decorationId ] = window . setTimeout ( ( ) => {
428+ // And now hide the nametag after 1.5s
429+ model . deltaDecorations ( [ decoration ] , [ ] ) ;
430+ } , 1500 ) ;
431+ }
388432 } ) ;
389433 }
390434
0 commit comments