<html>

<head>
  <title>
    Vue 3 Todo App
  </title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>

<body>
  <div class="container" id="todoapp">
    <div class="row">
      <div class="section">
        <h4>Add Todos</h4>
      </div>
      <div class="divider"></div>
      <div class="section">
        <form class="col s12">
          <div class="row">
            <div class="input-field col s4">
              <i class="material-icons prefix">title</i>
              <textarea v-model="todo.title" id="todo_title" class="materialize-textarea"></textarea>
              <label for="todo_title">Title</label>
            </div>
            <div class="input-field col s4">
              <i class="material-icons prefix">description</i>
              <textarea v-model="todo.description" id="todo_description" class="materialize-textarea"></textarea>
              <label for="todo_description">Description</label>
            </div>
            <div class="s4">
              <a class="btn-floating btn-large waves-effect waves-light red tooltipped" data-position="bottom" data-tooltip="Add Todo!" @click="addTodo()"><i class="material-icons">add</i></a>
            </div>
          </div>
        </form>
      </div>
    </div>
    <div class="row">
      <div class="divider"></div>
      <div class="section">
        <h5>Your Todos</h5>
      </div>
      <div class="divider"></div>
      <div class="section">
        <table class="striped centered responsive-table">
          <thead>
            <th>

            </th>
            <th>
              Title
            </th>
            <th>
              Description
            </th>
            <th>

            </th>
          </thead>
          <tbody>
            <tr v-for="(todo,id) in todos">
              <td>
                {{id}}
              </td>
              <td>
                {{todo.title}}
              </td>
              <td>
                {{todo.description}}
              </td>
              <td>
                <a v-show="!todo.done" class="btn-floating btn-large waves-effect waves-light light-green accent-4 tooltipped" data-position="bottom" data-tooltip="I'm done with this one!" @click="markAsDone(todo.id)"><i class="material-icons">check</i></a>

                <i v-if="todo.done" class="todo-done-tick material-icons tooltipped" data-position="bottom" data-tooltip="You're done with this one!">check</i>
              </td>
            </tr>
            <tr v-show="todoCount == 0">
              <td colspan="4">You haven't added any Todos yet :(</td>
            </tr>
          </tbody>
        </table>
      </div>

    </div>
  </div>
  </div>
  </div>
</body>

</html>
.todo-done-tick {
  font-size: 50px;
  color: #33691e;
  cursor: pointer;
}
const TodoApp = {
  data() {
    return {
      todoCount: 0,
      todo: {
        id: 0,
        title: "",
        description: "",
        done: false
      },
      todos: {}
    };
  },
  mounted() {
    const vm = this;
    vm.initialize();
  },
  methods: {
    initialize() {
      const vm = this;
      vm.addTooltips(vm.findTooltippedElementsFromDOM());
    },
    addTooltips(tooltippedElements) {
      const vm = this;
      M.Tooltip.init(tooltippedElements, {});
    },
    findTooltippedElementsFromDOM() {
      const vm = this;
      return document.querySelectorAll(".tooltipped");
    },
    addTodo() {
      const vm = this;

      if (!vm.todo.title || vm.todo.title.trim() === "") {
        M.toast({ html: "Need a title for this Todo!", classes: "rounded" });
        return;
      }

      if (!vm.todo.description || vm.todo.description.trim() === "") {
        M.toast({
          html: "A small description would be nice!",
          classes: "rounded"
        });
        return;
      }

      vm.todo.id = ++vm.todoCount;
      vm.todos[vm.todo.id] = vm.todo;

      vm.todo = { title: "", description: "" };
      vm.addTooltipsToDynamicElements();
    },
    markAsDone(id) {
      const vm = this;
      vm.todos[id].done = true;
      vm.addTooltipsToDynamicElements();
    },
    addTooltipsToDynamicElements() {
      const vm = this;

      setTimeout(function () {
        vm.addTooltips(vm.findTooltippedElementsFromDOM());
      }, 500);
    }
  }
};

Vue.createApp(TodoApp).mount("#todoapp");

External CSS

  1. https://fonts.googleapis.com/icon?family=Material+Icons
  2. https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css

External JavaScript

  1. https://unpkg.com/vue@next
  2. https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js