1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

タスクが自動で消える!タイマー付きToDoリストを作ってみた

Posted at

はじめに

"タイマー付きのToDoリスト"を作ってみました。

"従来のToDoリスト"では一度設定したタスクは消去しない限り残るのに対し、"タイマー付きのToDoリスト"では、タイムオーバーになったタスクは自動的に消えることが大きな特徴です。

基本仕様

  • タスクそれぞれにタイマーを設定する(デフォルトは1h)
  • タイムオーバーになると実施の有無にかかわらずタスクが自動的に消える
  • ファイル保存、一括削除ができる(タスク保存したい場合はファイル保存を行う)
  • タスクごとにチェック、削除ができる
  • local storageにデータを保存している

想定される使用場面

  • 難しいタスクを、細分化したタスクにして処理する
  • 時々刻々発生する課題を新しいタスクとして記録する
  • 1日の業務手順を、タイマー設定により計画・実行する

スクリプト

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Todo List</title>
    <script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>

    <style>
        input[type="number"] {
            width: 60px;
            margin-left: 10px;
        }
        .timer {
            font-weight: bold;
            color: red;
        }
    </style>
</head>
<body>
    <div id="app">
        <h1>Todo List</h1>
        <textarea v-model="newTodos" placeholder="Enter multiple todos, each on a new line"></textarea>
        <button @click="addTodos">Add Todos</button>
        <button @click="saveToFile">Save to File</button>
        <button @click="resetTodos">Reset</button>
        <input type="number" v-model.number="defaultTime" placeholder="時間(時間単位)" min="0" />
        <ul>
            <li v-for="(todo, index) in todos" :key="index">
                <input type="checkbox" v-model="todo.done" />
                <span :style="{ textDecoration: todo.done ? 'line-through' : 'none' }">{{ todo.text }}</span>&nbsp;
                <span class="timer" v-if="todo.remainingTime >= 0">{{ formatRemainingTime(todo.remainingTime) }}</span>
                <button @click="removeTodo(index)">Remove</button>
            </li>
        </ul>
    </div>
    <script>
        const app = Vue.createApp({
            data() {
                return {
                    newTodos: '',
                    todos: [],
                    defaultTime: 1 // 一定時間を保持する変数
                };
            },
            mounted() {
                const savedTodos = localStorage.getItem('todos');
                if (savedTodos) {
                    this.todos = JSON.parse(savedTodos);
                    this.todos.forEach(todo => {
                        todo.remainingTime = this.calculateRemainingTime(todo.deleteAt);
                    });
                }
                this.startGlobalTimer(); 
            },
            watch: {
                todos: {
                    deep: true,
                    handler(newTodos) {
                        localStorage.setItem('todos', JSON.stringify(newTodos));
                    }
                }
            },
            methods: {
                addTodos() {
                    const todosArray = this.newTodos.trim().split('\n').filter(todo => todo.trim() !== '');
                    todosArray.forEach(todoText => {
                        const deleteAt = Date.now() + this.defaultTime * 3600000;
                        this.todos.push({ 
                            text: todoText, 
                            done: false, 
                            deleteAt: deleteAt,
                            remainingTime: this.calculateRemainingTime(deleteAt)
                        });
                    });
                    this.newTodos = '';
                },
                removeTodo(index) {
                    this.todos.splice(index, 1);
                },
                saveToFile() {
                    const todosText = this.todos.map(todo => `${todo.text}${todo.done ? ' (done)' : ''}`).join('\n');
                    const blob = new Blob([todosText], { type: 'text/plain' });
                    const url = URL.createObjectURL(blob);
                    const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
                    const filename = `todos-${timestamp}.txt`;
                    const a = document.createElement('a');
                    a.href = url;
                    a.download = filename;
                    document.body.appendChild(a);
                    a.click();
                    document.body.removeChild(a);
                    URL.revokeObjectURL(url);
                },
                resetTodos() {
                    this.todos = [];
                },
                startGlobalTimer() {
                    setInterval(() => {
                        const now = Date.now();
                        this.todos = this.todos.filter(todo => {
                            todo.remainingTime = this.calculateRemainingTime(todo.deleteAt);
                            return todo.remainingTime > 0;
                        });
                    }, 1000);
                },
                calculateRemainingTime(deleteAt) {
                    const remainingTime = deleteAt - Date.now();
                    return remainingTime > 0 ? remainingTime : 0;
                },
                formatRemainingTime(remainingTime) {
                    const hours = Math.floor(remainingTime / 3600000);
                    const minutes = Math.floor((remainingTime % 3600000) / 60000);
                    const seconds = Math.floor((remainingTime % 60000) / 1000);
                    return `${hours}h ${minutes}m ${seconds}s `;
                }
            }
        });
        app.mount('#app');
    </script>
</body>
</html>

技術的特徴

以下がアプリの主な技術的特徴です。

  1. Vue.jsを使用したアプリケーション構成

    • Vueのdatamethodsを活用して、タスクデータの管理やユーザー操作の処理を実装
    • Vueのmountedライフサイクルメソッドを利用し、ページの読み込み時にローカルストレージからタスクを読み込む処理が行われる
  2. LocalStorageへの自動保存

    • watchプロパティを使用し、todosの変更を監視してリアルタイムでlocalStorageに保存する
  3. タイマー機能

    • defaultTime変数でタスクのデフォルトのタイムアウト時間(1時間)を設定
    • addTodosメソッドでタスク追加時に「削除予定時刻」を設定し、remainingTimeを計算することで、残り時間が表示される
    • startGlobalTimerメソッドで1秒ごとに全タスクをチェックし、remainingTimeが0以下になったタスクはリストから自動削除される
  4. タスクの残り時間を動的に表示

    • calculateRemainingTimeメソッドで各タスクの残り時間をミリ秒単位で計算し、formatRemainingTimeメソッドで「時:分:秒」にフォーマットする

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?