JSFiddle - React, Tailwind, and code Playground

by stefansarya

HTML

<link rel="stylesheet" href="https://unpkg.com/todomvc-app-css@2.2.0/index.css">
<script src="https://cdn.jsdelivr.net/npm/scarletsframe@0.32.11/dist/scarletsframe.min.js"></script>
<!-- Click "Run" before trying -->

<sf-m name="todos">
  <section class="todoapp">
    <header class="header">
      <h1>todos</h1>
      <input sf-bind="textInput"
        @keyup="inputKeyUp"
        class="new-todo"
        placeholder="What needs to be done?"
        autofocus
      >
    </header>
    
    <section class="main">
      <input class="toggle-all" type="checkbox">
      <label @click="toggleAll" for="toggle-all">
        Mark all as complete
      </label>
      <ul class="todo-list">
        <li
          sf-each="x in list"
          class="{{ x.completed ? 'completed' : '' }}"
          style="display: {{
              (x.completed && switch === 'active' && 'none')
              || (!x.completed && switch === 'completed' && 'none')
              || 'block'
          }}"
        >
          <div class="view">
            <input @click="itemToggle(x)" sf-bind="x.completed" class="toggle" type="checkbox">
            <label @click="clicking(x)">{{ x.value }}</label>
            <button @pointerup="remove(x)" class="destroy"></button>
          </div>
        </li>
      </ul>
    </section>
    
    <footer class="footer">
      <span class="todo-count">
        <strong>{{ itemsLeft }}</strong> items left
      </span>
      <ul class="filters">
        <li>
          <a @click="switch = 'all'"
            class="{{ switch === 'all' ? 'selected' : '' }}">All</a>
        </li>
        <li>
          <a @click="switch = 'active'"
            class="{{ switch === 'active' ? 'selected' : '' }}">Active</a>
        </li>
        <li>
          <a @click="switch = 'completed'"
            class="{{ switch === 'completed' ? 'selected' : '' }}">Completed</a>
        </li>
      </ul>
      <button @click="clearCompleted"
        class="clear-completed"
        style="display:...

CSS

a, input[type="checkbox"]{
  cursor: pointer;
}

JavaScript

sf.model('todos', function(My){
	// === Local Data ===
  let modifyItem = null;
  let modifyMode = null;
  
  // === Reactivity ===
  My.list = [/* {completed, value} */];
  My.itemsLeft = 0;
  
  My.switch = 'all';
  My.textInput = '';
  
  // === Local Storage ===
  My.list = JSON.parse(localStorage.todos || '[]');
  My.itemsLeft = My.list.length;
  function saveList(){
    localStorage.todos = JSON.stringify(My.list);
    My.itemsLeft = My.list.filter((val)=> !val.completed).length;
  }
  
  // === Functions ===
  My.inputKeyUp = function(e){
    if(e.key === "Enter"){
    	if(!modifyMode){
        My.list.push({
          value: My.textInput,
          completed: false
        });
      }
      else modifyItem.value = My.textInput;
    
    	modifyItem = modifyMode = false;
      My.textInput = '';
      saveList();
    }
  }

  My.itemToggle = function(item){
    item.completed = !item.completed;
    saveList();
  }
  
  let toggle = false;
  My.toggleAll = function(){
    toggle = !toggle;

    My.list.forEach((x)=> {x.completed = toggle});
    saveList();
  }
  
  My.clearCompleted = function(item){
    My.list = My.list.filter((x)=> !x.completed);
    saveList();
  }
  
  My.remove = function(item){
    My.list.splice(My.list.indexOf(item), 1);
    saveList();
  }
  
  My.clicking = function(item){
    if(modifyItem === item){
    	My.textInput = item.value;
      modifyMode = true;
      My.$el('input.new-todo').focus();
    	return;
    }
    
    modifyItem = item;
    modifyMode = false;
  }
});