diff --git "a/\353\260\261\354\234\244\354\204\234/TodoList/.gitignore" "b/\353\260\261\354\234\244\354\204\234/TodoList/.gitignore" new file mode 100644 index 0000000..b2d59d1 --- /dev/null +++ "b/\353\260\261\354\234\244\354\204\234/TodoList/.gitignore" @@ -0,0 +1,2 @@ +/node_modules +/dist \ No newline at end of file diff --git "a/\353\260\261\354\234\244\354\204\234/TodoList/index.html" "b/\353\260\261\354\234\244\354\204\234/TodoList/index.html" new file mode 100644 index 0000000..d4ca147 --- /dev/null +++ "b/\353\260\261\354\234\244\354\204\234/TodoList/index.html" @@ -0,0 +1,14 @@ + + + + + + Todo List + + + + +
+ + + diff --git "a/\353\260\261\354\234\244\354\204\234/TodoList/package-lock.json" "b/\353\260\261\354\234\244\354\204\234/TodoList/package-lock.json" new file mode 100644 index 0000000..05cc57f --- /dev/null +++ "b/\353\260\261\354\234\244\354\204\234/TodoList/package-lock.json" @@ -0,0 +1,455 @@ +{ + "name": "todo-list", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "todo-list", + "version": "1.0.0", + "license": "ISC", + "devDependencies": { + "tslint": "^6.1.3", + "typescript": "^5.3.3" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.23.4", + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha512-wxXCdllwGhI2kCC0MnvTGYTMvnVZTvqgypkiTI8Pa5tcz2i6VqsqwYGgqwXji+4RgCzms6EajE4IxiUH6HH8nQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/tslint": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.3.tgz", + "integrity": "sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==", + "deprecated": "TSLint has been deprecated in favor of ESLint. Please see https://github.com/palantir/tslint/issues/4534 for more information.", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "builtin-modules": "^1.1.1", + "chalk": "^2.3.0", + "commander": "^2.12.1", + "diff": "^4.0.1", + "glob": "^7.1.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.3", + "resolve": "^1.3.2", + "semver": "^5.3.0", + "tslib": "^1.13.0", + "tsutils": "^2.29.0" + }, + "bin": { + "tslint": "bin/tslint" + }, + "engines": { + "node": ">=4.8.0" + }, + "peerDependencies": { + "typescript": ">=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >=3.0.0-dev || >= 3.1.0-dev || >= 3.2.0-dev || >= 4.0.0-dev" + } + }, + "node_modules/tsutils": { + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", + "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "peerDependencies": { + "typescript": ">=2.1.0 || >=2.1.0-dev || >=2.2.0-dev || >=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >= 3.0.0-dev || >= 3.1.0-dev" + } + }, + "node_modules/typescript": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + } + } +} diff --git "a/\353\260\261\354\234\244\354\204\234/TodoList/package.json" "b/\353\260\261\354\234\244\354\204\234/TodoList/package.json" new file mode 100644 index 0000000..54b8863 --- /dev/null +++ "b/\353\260\261\354\234\244\354\204\234/TodoList/package.json" @@ -0,0 +1,12 @@ +{ + "name": "todo-list", + "version": "1.0.0", + "main": "main.js", + "scripts": { + "start": "tsc && npx serve" + }, + "devDependencies": { + "tslint": "^6.1.3", + "typescript": "^5.3.3" + } +} diff --git "a/\353\260\261\354\234\244\354\204\234/TodoList/src/App.ts" "b/\353\260\261\354\234\244\354\204\234/TodoList/src/App.ts" new file mode 100644 index 0000000..fdeaf67 --- /dev/null +++ "b/\353\260\261\354\234\244\354\204\234/TodoList/src/App.ts" @@ -0,0 +1,48 @@ +import Header from "./components/Header.js"; +import TodoForm from "./components/TodoForm.js"; +import TodoList from "./components/TodoList.js"; +import TodoCount from "./components/TodoCount.js"; +import { setItem, getItem } from "./utils/storage.js"; +import { TodoCount as TodoCnt, TodoList as TodoLi } from "./types/todo.js"; +export default class App { + private readonly todoList: TodoList; + private readonly todoCount: TodoCount; + private readonly initialState: TodoLi; + private readonly initialCount: TodoCnt; + + constructor( + private readonly $target: HTMLElement, + ) { + this.initialState = getItem("todo", []); + this.initialCount = getItem("count", { total: 0, done: 0 }); + + new Header($target, "Todo List"); + + new TodoForm( + $target, + (text: string) => { + const nextState = [ + ...this.todoList.state, + { text, isCompleted: false } + ]; + this.todoList.setState(nextState); + }, + ); + + this.todoList = new TodoList( + $target, + this.initialState, + (state: TodoLi) => this.#updateCount(state) + ); + + this.todoCount = new TodoCount($target, this.initialCount); + } + + // 카운트 업데이트 + #updateCount(todoList: TodoLi) { + const done = todoList.filter((todo) => todo.isCompleted).length; + const nextState = { total: todoList.length, done }; + this.todoCount.setState(nextState); + setItem("count", JSON.stringify(nextState)); + }; +} diff --git "a/\353\260\261\354\234\244\354\204\234/TodoList/src/components/Header.ts" "b/\353\260\261\354\234\244\354\204\234/TodoList/src/components/Header.ts" new file mode 100644 index 0000000..39f2429 --- /dev/null +++ "b/\353\260\261\354\234\244\354\204\234/TodoList/src/components/Header.ts" @@ -0,0 +1,15 @@ +export default class Header { + private readonly $header = document.createElement("h1"); + + constructor( + private readonly $target: HTMLElement, + private readonly text: string + ) { + $target.appendChild(this.$header); + this.render(); + }; + + private render() { + this.$header.textContent = this.text; + } +}; diff --git "a/\353\260\261\354\234\244\354\204\234/TodoList/src/components/TodoCount.ts" "b/\353\260\261\354\234\244\354\204\234/TodoList/src/components/TodoCount.ts" new file mode 100644 index 0000000..bcb2bda --- /dev/null +++ "b/\353\260\261\354\234\244\354\204\234/TodoList/src/components/TodoCount.ts" @@ -0,0 +1,27 @@ +import { TodoCount as TodoCnt } from "../types/todo.js"; + +export default class TodoCount { + private readonly $container = document.createElement("div"); + private state: TodoCnt; + + constructor( + private readonly $target: HTMLElement, + private readonly initialCount: TodoCnt + ) { + $target.appendChild(this.$container); + + if (initialCount.total) this.state = initialCount; + else this.state = { total: 0, done: 0 }; + + this.render(); + } + + setState(nextState: TodoCnt) { + this.state = nextState; + this.render(); + }; + + private render() { + this.$container.textContent = `완료 ${this.state.done}개 / 총 ${this.state.total}개`; + }; +} diff --git "a/\353\260\261\354\234\244\354\204\234/TodoList/src/components/TodoForm.ts" "b/\353\260\261\354\234\244\354\204\234/TodoList/src/components/TodoForm.ts" new file mode 100644 index 0000000..3cd141f --- /dev/null +++ "b/\353\260\261\354\234\244\354\204\234/TodoList/src/components/TodoForm.ts" @@ -0,0 +1,31 @@ + +export default class TodoForm { + private readonly $form = document.createElement("form"); + + constructor( + private readonly $target: HTMLElement, + private readonly onSubmit: (text: string) => void + ) { + $target.appendChild(this.$form); + this.render(); + this.setEvent(); + } + + private render() { + this.$form.innerHTML = ` + + + `; + }; + + private setEvent() { + this.$form.addEventListener("submit", (e) => { + e.preventDefault(); + const $todo = this.$form.querySelector("input[name=todo]"); + if (!$todo) return; + const text = $todo.value; + $todo.value = ""; + this.onSubmit(text); + }); + } +} diff --git "a/\353\260\261\354\234\244\354\204\234/TodoList/src/components/TodoList.ts" "b/\353\260\261\354\234\244\354\204\234/TodoList/src/components/TodoList.ts" new file mode 100644 index 0000000..89d6624 --- /dev/null +++ "b/\353\260\261\354\234\244\354\204\234/TodoList/src/components/TodoList.ts" @@ -0,0 +1,70 @@ +import { setItem } from "../utils/storage.js"; +import { TodoList as TodoLi } from "../types/todo.js"; + +export default class TodoList { + state: TodoLi; + private readonly $todoList = document.createElement("div"); + + constructor( + private readonly $target: HTMLElement, + private readonly initialState: TodoLi, + private readonly updateCount: (state: TodoLi) => void + ) { + $target.appendChild(this.$todoList); + this.state = initialState; + this.render(); + this.setEvent(); + } + + setState(nextState: TodoLi) { + this.state = nextState; + setItem("todo", JSON.stringify(nextState)); + this.updateCount(nextState); + this.render(); + }; + + private render() { + this.$todoList.innerHTML = ` + + `; + }; + + private setEvent() { + this.$todoList.addEventListener("click", (e) => { + const target = e.target as HTMLLIElement; + const $li = target.closest("li"); + + if (!$li) return; + + const newState = [...this.state]; + if (typeof $li.dataset.index !== "string") return; + const index = +$li.dataset.index; + + if (target.className === "deleteBtn") { + newState.splice(index, 1); + this.setState(newState); + } else if (target.className.includes("todoList")) { + const isCompleted = target.className.includes("completed"); + if (isCompleted) target.classList.remove("completed"); + else target.classList.add("completed"); + newState[index] = { + ...newState[index], + isCompleted: !isCompleted, + }; + this.setState(newState); + } + }); + } +} diff --git "a/\353\260\261\354\234\244\354\204\234/TodoList/src/main.ts" "b/\353\260\261\354\234\244\354\204\234/TodoList/src/main.ts" new file mode 100644 index 0000000..579bc6b --- /dev/null +++ "b/\353\260\261\354\234\244\354\204\234/TodoList/src/main.ts" @@ -0,0 +1,3 @@ +import App from "./App.js"; +const $app = document.querySelector("#app"); +$app && new App($app); diff --git "a/\353\260\261\354\234\244\354\204\234/TodoList/src/types/todo.ts" "b/\353\260\261\354\234\244\354\204\234/TodoList/src/types/todo.ts" new file mode 100644 index 0000000..0f9893c --- /dev/null +++ "b/\353\260\261\354\234\244\354\204\234/TodoList/src/types/todo.ts" @@ -0,0 +1,13 @@ +export type StorageKey = "todo" | "count"; + +export type TodoList = TodoItem[]; + +interface TodoItem { + text: string; + isCompleted: boolean; +} + +export interface TodoCount { + total: number; + done: number; +} diff --git "a/\353\260\261\354\234\244\354\204\234/TodoList/src/utils/storage.ts" "b/\353\260\261\354\234\244\354\204\234/TodoList/src/utils/storage.ts" new file mode 100644 index 0000000..371c6b9 --- /dev/null +++ "b/\353\260\261\354\234\244\354\204\234/TodoList/src/utils/storage.ts" @@ -0,0 +1,23 @@ +import { StorageKey } from "../types/todo"; + +const storage = window.localStorage; + +export const setItem = (key: string, value: string) => { + try { + storage.setItem(key, value); + } + catch (error: unknown) { + if (error instanceof Error) console.log(error); + } +}; + +export const getItem = (key: StorageKey, defaultValue: T): T => { + try { + const data = storage.getItem(key); + if (data) return JSON.parse(data); + return defaultValue; + } catch (error) { + if (error instanceof Error) console.log(error); + return defaultValue; + } +}; diff --git "a/\353\260\261\354\234\244\354\204\234/TodoList/style.css" "b/\353\260\261\354\234\244\354\204\234/TodoList/style.css" new file mode 100644 index 0000000..855a621 --- /dev/null +++ "b/\353\260\261\354\234\244\354\204\234/TodoList/style.css" @@ -0,0 +1,35 @@ +ul { + list-style: none; + padding: 0; +} + +li { + cursor: pointer; + margin: 10px; +} + +input { + padding: 4px 8px; + border-radius: 4px; + border: 1px solid #2e2f35; + outline: none; +} + +button { + cursor: pointer; + border-radius: 4px; + border: 1px solid #2e2f35; + margin-left: 5px; + padding-top: 3px; + background-color: transparent; + color: #2e2f35; +} + +button:hover { + background-color: #2e2f35; + color: #fff; +} + +.completed { + text-decoration: line-through; +} \ No newline at end of file diff --git "a/\353\260\261\354\234\244\354\204\234/TodoList/tsconfig.json" "b/\353\260\261\354\234\244\354\204\234/TodoList/tsconfig.json" new file mode 100644 index 0000000..6303ad8 --- /dev/null +++ "b/\353\260\261\354\234\244\354\204\234/TodoList/tsconfig.json" @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "target": "ES2015", + "module": "ES2015", + "strict": true, + "outDir": "dist", + "sourceMap": true + } +} diff --git "a/\353\260\261\354\234\244\354\204\234/TodoList/tslint.json" "b/\353\260\261\354\234\244\354\204\234/TodoList/tslint.json" new file mode 100644 index 0000000..32fa6e5 --- /dev/null +++ "b/\353\260\261\354\234\244\354\204\234/TodoList/tslint.json" @@ -0,0 +1,9 @@ +{ + "defaultSeverity": "error", + "extends": [ + "tslint:recommended" + ], + "jsRules": {}, + "rules": {}, + "rulesDirectory": [] +} \ No newline at end of file