JSFiddle - React, Tailwind, and code Playground

by levchenko_d

HTML

<label for="travel">Time Travel</label>
<input id="travel" type="range" min="0" max="1" step="1" list="ticks" style="position: absolute; opacity: 0;">
<datalist id="ticks">
  <option value="0" label="0"/>
  <option value="1" label="0"/>
</datalist>
<div id="travel-preview"></div>
<br>
<button id="back">&lt;</button>
<button id="log">log</button>
<button id="bulk">bulk</button>
<button id="forward">&gt;</button>

<canvas id="can" width="400" height="400" style="position:absolute;top:130px;left:10px;border:2px solid;"></canvas>

CSS

#travel-preview {
  padding: 5px;
    white-space: nowrap;
    text-overflow: ellipsis;
    overflow-x: scroll;
}

#travel-preview > *:not(.active) {
  opacity: 0.5;
  box-shadow: 0 0 0 1px #f2f3f9;
}

#travel-preview > *.active {
  box-shadow: 0 0 0 2px #00b0ef;
}

#travel-preview > *:hover {
  opacity: 1;
}

JavaScript

var Store = function(options){
    var t=this;
  
  t.storage = {};
  t.handlers = {};
  t.storageHistory = [];
  t.version = 0;
  t.callbacks = {
  	onchange: options.onchange
  };
  
  t.defaults = {
    storageItem: {
      key: undefined,
      value: undefined,
      handler: undefined
    }
  };
  
  return t;
};
Store.prototype.handleStartData = function(optionsList){
  var t=this,
  		list = optionsList || [],
  		isInit = true;
      
		list.map(function(options, index){
	  var isInitDone = index === list.length - 1;
  	t.set(options, isInit, isInitDone);
  });
};
Store.prototype._clone = function(obj){
  return JSON.parse(JSON.stringify(obj));
};

Store.prototype.keyExist = function(options){
  var t=this;
  return t.storage[options.key];
};
Store.prototype.prettyStorage = function(){
  var t=this,
  		pretty = {};
      
 	for(var key in t.storage){
    if (t.storage.hasOwnProperty(key)) {
      pretty[key] = t.storage[key].value;
    }
  }
  
  return t._clone(pretty);
};
Store.prototype.updateHistory = function(options, isInit, isInitDone){
  var t=this;
  
  if(!isInit){
    t.storageHistory = t.storageHistory.slice(0, t.version+1);
	  t.version += 1;
  }
  
  if(isInitDone){
  	t.storageHistory = [];
    t.version = 0;
  }
  t.storageHistory.push(t._clone(t.storage));

  return t;
};
Store.prototype.fillKey = function(options){
  var t=this;
  
  ['key', 'value'].map(function(slug){
	  t.storage[options.key][slug] = options[slug];    
  });
  
  if(options.handler && typeof options.handler === 'function'){
	  t.handlers[options.key] = options.handler;
  }
  
  return t;
};
Store.prototype.handleKey = function(options){
  var t=this;
  
  if(!t.keyExist(options)){
    t.storage[options.key] = t._clone(t.defaults.storageItem);
  }
  
  t.fillKey(options);
  return t;
};
Store.prototype.set = function(options, isInit, isInitDone){
  var t=this;
  
  t.handleKey(options);
  
  t.updateHistory(options, isInit, isInitDone);
 	t.onchange(options);
  
 ...