JSFiddle - React, Tailwind, and code Playground

by PhilQ

HTML

<script src="//code.jquery.com/jquery-3.3.1.slim.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.0/umd/popper.min.js"></script>
<script src="//stackpath.bootstrapcdn.com/bootstrap/4.1.0/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
<link rel="stylesheet" href="//stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<br />
<div id="app" class="container">
  <div class="row">
    <div class="col">
      <div class="form-control" ref="input"
				spellcheck="false"
				:contenteditable="editable"
				:class="{'has-tags':selected_tags.length}"
				@mousedown="inputMouseDown"
				@keydown.shift.tab.prevent="inputKeyShiftTab"
				@keydown.left="inputKeyLeft($event)"
				@keydown.up.prevent="inputKeyUp"
				@keydown.down.prevent="inputKeyDown"
			>
				<template v-for="(tag,i) in selected_tags">
      		<a href="#"
						class="tag btn btn-sm btn-light"
						@focus="tagFocus(i)"
						@blur="tagBlur(i,$event)"
						@click.prevent.stop="tagClick(i)"
						@mousedown.prevent.stop="tagMouseDown(i)"
						@keydown.tab.stop="tagKeyTab(i,$event)"
					>{{tag.name}}</a>
				</template>&nbsp;{{input_value}}
      </div>
    </div>
  </div>  
</div>

CSS

.form-control {
	min-height: calc(2.19rem + 2px);
	height: auto;
	cursor: text;
	padding-left: 0;
	padding-right: 0;
}
.form-control.focus {
    color: #495057;
    background-color: #fff;
    border-color: #80bdff;
    outline: 0;
    box-shadow: 0 0 0 0.2rem rgba(0,123,255,.25);
    box-shadow: 0 0 0 0.2rem rgba(255,123,0,.25);
}
.has-tag {
	padding-left: 0.5rem;
	/* padding-right: 0.5rem; */
}
.tag {
	margin-left: 0.5rem;
	margin-right: 0.5rem;
}
.tag + .tag {
	margin-left: 0;
}
.tag:focus {
	background: #3490dc;
	color: #fff;
}

JavaScript

var app = new Vue({
  el: '#app',
  data: {
		editable: true,
    selected_tags: [
			{name: 'Tag 1', id: 1},
			{name: 'Tag 2', id: 2},
			{name: 'Tag 3', id: 3},
		],
		input_value: 'Text en zo',
  },
	computed: {
	},
	methods: {
		tagFocus(idx) {
			console.log('tag-focus', idx);
		},
		tagBlur(idx,evt) {
			console.log('tag-blur', idx);
			var to_el = evt.relatedTarget || evt.toElement;
			if ( ! to_el || to_el && !$(to_el).hasClass('tag') ) {
				this.editable = true;
			}
		},
		tagMouseDown(idx,evt) {
			console.log('tag-mousedown', idx);
			this.editable = false;
		},
		tagClick(idx,evt) {
			console.log('tag-click', idx);
			this.$refs.input.childNodes[idx].focus();
		},
		tagKeyTab(idx,evt) {
			if (!evt.shiftKey && idx == this.selected_tags.length-1) {
				evt.preventDefault();
				this.editable = true;
				Vue.nextTick(() => {
					this.$refs.input.focus();
					this.placeCaret(this.$refs.input, true, false);
				});
			}
		},
		
		inputMouseDown() {
			console.log('input-mousedown');
			this.editable = true;
			Vue.nextTick(() => { this.$refs.input.focus(); });
		},
		inputClick() {
			//this.$refs.input.focus();
		},
		inputKeyShiftTab() {
			this.editable = false;
			Vue.nextTick(() => { this.$refs.input.childNodes[this.$refs.input.childNodes.length-2].focus(); });
		},
		inputKeyLeft(evt) {
			var selectedRange = window.getSelection().getRangeAt(0);
			console.log('range:', selectedRange.startOffset, selectedRange.endOffset);
			if (1 == selectedRange.startOffset) {
				evt.preventDefault();
				this.editable = false;
				Vue.nextTick(() => { this.$refs.input.childNodes[this.$refs.input.childNodes.length-2].focus(); });
			}
		},
		inputKeyUp() {}, // reserve for autocomplete
		inputKeyDown() {}, // reserve for autocomplete
		
		
		placeCaret(el, collapse, atStart) { 
			// feature test to see which methods to use for given browser
			if (
				typeof window.getSelection != "undefined"
				&& typeof document.createRange != "undefined"
			) {
				//...