JSFiddle - React, Tailwind, and code Playground

by MKGaru

HTML

<script src="https://rawgit.com/SteveSanderson/knockout-es5/master/dist/knockout-es5.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/sugar/1.4.1/sugar-full.min.js"></script>
<ol>
<li><input type="time" data-bind="value:time1">
<li><input type="time" data-bind="value:time2" step="600">
<li><time-picker params="time2"/>
<li><time-picker params="time3,'17:30'"/>
</ol>

CSS

html,
body{
  height: 100%;
}

time-picker{
  display:inline-block;
  background:white;
  border:solid 1px #ccc;
  padding:0.1em;
  padding-right:1.2em;
  position:relative;
}
time-picker input{
	border:none; 
  outline:none;
  background:transparent;
  width:1.5em;
  user-select:none;
  -webkit-user-select:none;
}
time-picker input::selection {
  background:transparent;
}
/*time-picker input:focus,*/
time-picker input.focus{
	background:#ACCEF7;
}
time-picker .spin-buttons{
	display:inline-flex;
  flex-direction:column;
  visibility: hidden;
  transform: scale(0.5);
  transform-origin: top left;
  position: absolute;
  top: 0.1em;
  right: -0.5em;
}
.time-picker.focus .spin-buttons,
time-picker:hover .spin-buttons,
time-picker:focus .spin-buttons{
  visibility: visible;
}
time-picker .spin-buttons button{
	border:none;
  outline:none;
}
time-picker .spin-buttons button:hover{
  background:lightgray;
}
time-picker .spin-buttons button:active{
  background:gray;
}

TypeScript

class VM{
	public time1 = '';
	public time2 = '';
	public time3 = '';
  	constructor(){
  		ko.track(this);
  	}
}

class TimePicker{
	public default='08:30';
	public hour='- -';
	public minute='- -';
	public hourInput = null;
	public minuteInput = null;
	public target = null;
	constructor(params,info){
    var picker = this;
    ko.track(picker);
	var keys = Object.keys(params);
    var name = keys[0];
    var prop = ko.getObservable(ko.contextFor(info.element),name);
	if(keys.length>=2){
		if(Number.isInteger(+keys[1][0])){
			picker.default = keys[1];
		}
		else{
			picker.default = ko.getObservable(ko.contextFor(info.element),keys[1]);
		}
	}
	
    if(!prop) prop = ko.getObservable(ko.dataFor(info.element),name);
    
    prop.subscribe(()=>{
	  var value = prop.peek();
	  if(!value) return;
      var date = Date.create(value);
      if(date.isValid()){
        picker.hour = date.getHours().pad(2);
        picker.minute = date.getMinutes().pad(2);
		ko.getObservable(picker,'hour').valueHasMutated();
		ko.getObservable(picker,'minute').valueHasMutated();
      }
    });
    prop.valueHasMutated();
    
	function updateInput(){
	  if(picker.hour === '' || picker.hour=='- -' || picker.minute===''||picker.minute=='- -'){
		prop(picker.default);
		return;
	  }
	  let h = Number(picker.hour);
      if(isNaN(h)) picker.hour = '- -';
      else picker.hour = h.pad(2);
      if(h>23) picker.hour = Math.floor(h/24).pad(2);
	  
	  let m = Number(picker.minute);
      if(isNaN(m)) picker.minute = '- -';
      else picker.minute = m.pad(2);
      if(m>59) picker.minute = Math.floor(m/60).pad(2);
	  
	  let value = [picker.hour,picker.minute].join(':');
	  if(value != prop.peek())
      	prop(value);
	}
    ko.getObservable(picker,'hour').subscribe(()=>{
      return updateInput();
    });
    ko.getObservable(picker,'minute').subscribe(()=>{
      return updateInput();
    });
    setTimeout(()=>{
	  //info.element.addEventListener('keydown',(e)=>{
	 ...