JSFiddle - React, Tailwind, and code Playground

by Josh Pullen

HTML

<div id="editor" class="editor" tabindex="-1">
  <!--
  <div class="editor-selection">
    <div class="editor-selection-section"></div>
  </div>
  <div class="editor-text">
    <div class="editor-text-line">testing this out</div>
    <div class="editor-text-line">test test</div>
    <div class="editor-text-line">yo yo pineapple</div>
  </div>
  -->
</div>

CSS

.editor {
  background: #ddd;
  position: relative;
  cursor: text;
  overflow: auto;
}
.editor-text {
  font-family: monospace;
  white-space: nowrap;
  -webkit-user-select: none; /* Chrome/Safari */        
  -moz-user-select: none; /* Firefox */
  -ms-user-select: none; /* IE10+ */
  user-select: none;
}
.editor-selection {
  position:absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
}
.editor-selection-cursor {
  position: absolute;
  background: #00f;
  width: 1px;
  height: 15px;
}
.editor-selection-section {
  position: absolute;
  background: rgba(0,0,255,0.2);
}

JavaScript

class Editor {
	constructor(editorElem) {
    this.input = [
    	'testing this test',
      'testing test testing test',
      'really test test testing',
      'testing again more testing'
    ];
  	this.elem = editorElem;
    
   	let selectionElem = document.createElement('div');
    selectionElem.className = 'editor-selection';
    editorElem.append(selectionElem);
    this.selectionElem = selectionElem;
    
    let textElem = document.createElement('div');
    textElem.className = 'editor-text';
    editorElem.append(textElem);
    this.textElem = textElem;
    
    this.renderText();
    
    this.initEventListeners();
  }
  renderText() {
  	// TODO: Restructure to only change things that have been updated (diff-style)
  	this.textElem.innerHTML = '';
    for(let i = 0; i < this.input.length; i++) {
    	let line = document.createElement('div');
      line.innerText = this.input[i];
      this.textElem.append(line);
    }
  }
  initEventListeners() {
  	let _this = this;
  	this.elem.addEventListener('click', function(e) {
    	let x = e.pageX - _this.elem.offsetLeft;
      let y = e.pageY - _this.elem.offsetTop;
    	testSelection = new Selection(Loc.nearest(x, y).restrain(_this));
      testSelection.render(editor);
    });
    this.elem.addEventListener('keydown', function(e) {
    	let keycode = e.keycode || e.which;
      switch(keycode) {
      	case 37: // Left arrow
        	testSelection.loc2.changeCol(-1, _this).restrain(_this);
          if(!e.shiftKey) testSelection.loc1 = testSelection.loc2.clone();
          testSelection.render(_this);
          break;
        case 38: // Up arrow
          testSelection.loc2.changeRow(-1).restrain(_this);
          if(!e.shiftKey) testSelection.loc1 = testSelection.loc2.clone();
          testSelection.render(_this);
          break;
        case 39: // Right arrow
        	testSelection.loc2.changeCol(1, _this).restrain(_this);
          if(!e.shiftKey) testSelection.loc1 = testSelection.loc2.clone();
  ...