├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── dist
├── bundle.js
└── bundle.js.map
├── index.html
├── package.json
├── src
├── Controller.ts
├── Store.ts
├── Todo.ts
├── View.ts
├── app.ts
├── helpers.ts
└── templates.ts
└── tsconfig.json
/.gitignore:
--------------------------------------------------------------------------------
1 | **/.DS_Store
2 | node_modules
3 | npm-debug.log
4 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "node"
4 | - "6"
5 | - "5"
6 | - "4"
7 |
8 | install:
9 | - npm install
10 | script:
11 | - npm run compile
12 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 HyunSeob
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # typescript-TodoMVC
2 |
3 | [](https://travis-ci.org/HyunSeob/typescript-TodoMVC) 
4 |
5 | This repository is TodoMVC implemented with TypeScript. Not using other frameworks.
6 |
7 | [Demo](https://typescript-todomvc.herokuapp.com/)
8 |
9 | ## Dependencies
10 |
11 | - [TypeScript](http://www.typescriptlang.org/) (v1.8)
12 | - [SystemJS](https://github.com/systemjs/systemjs)
13 | - [http-server](https://github.com/indexzero/http-server) (server only)
14 |
15 |
16 | ## Disclaimer
17 |
18 | This project is influenced by the [TodoMVC - Vanilla ES6](https://github.com/tastejs/todomvc/tree/gh-pages/examples/vanilla-es6) project.
19 |
--------------------------------------------------------------------------------
/dist/bundle.js:
--------------------------------------------------------------------------------
1 | System.register("helpers", [], function(exports_1, context_1) {
2 | "use strict";
3 | var __moduleName = context_1 && context_1.id;
4 | function token() {
5 | return Math.floor((Math.random() + 1) * 0x10000)
6 | .toString(16)
7 | .substring(1);
8 | }
9 | function guid() {
10 | return [
11 | token() + token(),
12 | token(),
13 | token(),
14 | token(),
15 | token() + token()
16 | ].join('-');
17 | }
18 | exports_1("guid", guid);
19 | return {
20 | setters:[],
21 | execute: function() {
22 | }
23 | }
24 | });
25 | System.register("Todo", ["helpers"], function(exports_2, context_2) {
26 | "use strict";
27 | var __moduleName = context_2 && context_2.id;
28 | var helpers_1;
29 | var Todo;
30 | return {
31 | setters:[
32 | function (helpers_1_1) {
33 | helpers_1 = helpers_1_1;
34 | }],
35 | execute: function() {
36 | Todo = (function () {
37 | function Todo(_todo) {
38 | if (typeof _todo === 'string') {
39 | this._id = helpers_1.guid();
40 | this._content = _todo;
41 | this._done = false;
42 | }
43 | else {
44 | this._id = _todo._id;
45 | this._content = _todo._content;
46 | this._done = _todo._done;
47 | }
48 | }
49 | Object.defineProperty(Todo.prototype, "id", {
50 | get: function () { return this._id; },
51 | enumerable: true,
52 | configurable: true
53 | });
54 | Object.defineProperty(Todo.prototype, "content", {
55 | get: function () { return this._content; },
56 | set: function (_content) { this._content = _content; },
57 | enumerable: true,
58 | configurable: true
59 | });
60 | Object.defineProperty(Todo.prototype, "done", {
61 | get: function () { return this._done; },
62 | enumerable: true,
63 | configurable: true
64 | });
65 | Todo.prototype.equal = function (_todo) { return this._id === _todo.id; };
66 | Todo.prototype.toggle = function () { return this._done = !this._done; };
67 | return Todo;
68 | }());
69 | exports_2("default", Todo);
70 | }
71 | }
72 | });
73 | System.register("Store", ["Todo"], function(exports_3, context_3) {
74 | "use strict";
75 | var __moduleName = context_3 && context_3.id;
76 | var Todo_1;
77 | var Store;
78 | return {
79 | setters:[
80 | function (Todo_1_1) {
81 | Todo_1 = Todo_1_1;
82 | }],
83 | execute: function() {
84 | Store = (function () {
85 | function Store() {
86 | this._localStorage = window.localStorage;
87 | this._subscribers = [];
88 | }
89 | Store.prototype._getStorage = function () {
90 | return this._todos = this._todos || ((JSON.parse(this._localStorage.getItem('TodoList'))) || []).map(function (item) { return new Todo_1.default(item); });
91 | };
92 | Store.prototype._setStorage = function (todos) {
93 | this._localStorage.setItem('TodoList', JSON.stringify(todos));
94 | this._publish();
95 | };
96 | Store.prototype.initialize = function () {
97 | this._getStorage();
98 | this._publish();
99 | return this;
100 | };
101 | Store.prototype.insert = function (todo) {
102 | this._setStorage(this._todos = this._getStorage().concat([todo]));
103 | };
104 | Store.prototype.find = function (query) {
105 | return this._getStorage().filter(function (todo) {
106 | for (var key in query) {
107 | if (todo[key] !== query[key]) {
108 | return false;
109 | }
110 | }
111 | return true;
112 | });
113 | };
114 | Store.prototype.update = function (patch) {
115 | this._setStorage(this._getStorage().map(function (target) {
116 | if (target.equal(patch)) {
117 | target = patch;
118 | }
119 | return target;
120 | }));
121 | };
122 | Store.prototype.remove = function (query) {
123 | this._setStorage(this._todos = this._getStorage()
124 | .filter(function (todo) {
125 | for (var key in query) {
126 | if (todo[key] !== query[key]) {
127 | return true;
128 | }
129 | }
130 | return false;
131 | }));
132 | };
133 | Store.prototype.count = function (query) {
134 | return this._getStorage().filter(function (todo) {
135 | for (var key in query) {
136 | if (todo[key] !== query[key]) {
137 | return false;
138 | }
139 | }
140 | return true;
141 | }).length;
142 | };
143 | Store.prototype.subscribe = function (subscriber) {
144 | this._subscribers.push(subscriber);
145 | };
146 | Store.prototype._publish = function () {
147 | var _this = this;
148 | this._subscribers.forEach(function (s) { return s(_this._getStorage()); });
149 | };
150 | return Store;
151 | }());
152 | exports_3("default", Store);
153 | }
154 | }
155 | });
156 | System.register("templates", [], function(exports_4, context_4) {
157 | "use strict";
158 | var __moduleName = context_4 && context_4.id;
159 | function todoTpl(todo) {
160 | return [
161 | ("
"),
162 | (""),
163 | (""),
164 | "",
165 | ""
166 | ].join('');
167 | }
168 | exports_4("todoTpl", todoTpl);
169 | function todoListTpl(todos) {
170 | return todos.map(function (t) { return todoTpl(t); }).join('');
171 | }
172 | exports_4("todoListTpl", todoListTpl);
173 | function statTpl(leftTodos) {
174 | return leftTodos + " item" + (leftTodos === 1 ? '' : 's') + " left.";
175 | }
176 | exports_4("statTpl", statTpl);
177 | return {
178 | setters:[],
179 | execute: function() {
180 | }
181 | }
182 | });
183 | System.register("View", ["templates"], function(exports_5, context_5) {
184 | "use strict";
185 | var __moduleName = context_5 && context_5.id;
186 | var templates_1;
187 | var ENTER_KEY, ESCAPE_KEY, View;
188 | return {
189 | setters:[
190 | function (templates_1_1) {
191 | templates_1 = templates_1_1;
192 | }],
193 | execute: function() {
194 | ENTER_KEY = 13;
195 | ESCAPE_KEY = 27;
196 | View = (function () {
197 | function View() {
198 | this.$todoList = document.querySelector('#todo-list');
199 | this.$todoInput = document.querySelector('#todo-input');
200 | this.$todoStat = document.querySelector('#todo-stat');
201 | }
202 | View.prototype.onSubmit = function (handler) {
203 | this.$todoInput.addEventListener('keypress', function (event) {
204 | if (event.keyCode === ENTER_KEY) {
205 | var target = event.target;
206 | handler(target.value);
207 | target.value = '';
208 | }
209 | });
210 | };
211 | View.prototype.onClickCheckbox = function (handler) {
212 | this.$todoList.addEventListener('click', function (event) {
213 | var target = event.target;
214 | if (target.tagName === 'INPUT' && target.type === 'checkbox') {
215 | handler(target.parentElement.dataset['id']);
216 | }
217 | });
218 | };
219 | View.prototype.onClickButton = function (handler) {
220 | this.$todoList.addEventListener('click', function (event) {
221 | var target = event.target;
222 | if (target.tagName === 'BUTTON') {
223 | handler(target.parentElement.dataset['id']);
224 | }
225 | });
226 | };
227 | View.prototype.render = function (todos, lefts) {
228 | this.$todoList.innerHTML = templates_1.todoListTpl(todos);
229 | this.$todoStat.innerHTML = templates_1.statTpl(lefts);
230 | };
231 | return View;
232 | }());
233 | exports_5("default", View);
234 | }
235 | }
236 | });
237 | System.register("Controller", ["Todo", "Store", "View"], function(exports_6, context_6) {
238 | "use strict";
239 | var __moduleName = context_6 && context_6.id;
240 | var Todo_2, Store_1, View_1;
241 | var Controller;
242 | return {
243 | setters:[
244 | function (Todo_2_1) {
245 | Todo_2 = Todo_2_1;
246 | },
247 | function (Store_1_1) {
248 | Store_1 = Store_1_1;
249 | },
250 | function (View_1_1) {
251 | View_1 = View_1_1;
252 | }],
253 | execute: function() {
254 | Controller = (function () {
255 | function Controller() {
256 | var _this = this;
257 | this._view = new View_1.default();
258 | this._store = new Store_1.default();
259 | this._view.onSubmit(function (value) {
260 | var todo = new Todo_2.default(value);
261 | _this._store.insert(todo);
262 | });
263 | this._view.onClickCheckbox(function (targetId) {
264 | var targetTodo = _this._store.find({ id: targetId })[0];
265 | targetTodo.toggle();
266 | _this._store.update(targetTodo);
267 | });
268 | this._view.onClickButton(function (targetId) {
269 | _this._store.remove({ id: targetId });
270 | });
271 | this._store.subscribe(function (todos) {
272 | _this._view.render(todos, _this._store.count({ done: false }));
273 | });
274 | this._store.initialize();
275 | }
276 | return Controller;
277 | }());
278 | exports_6("default", Controller);
279 | }
280 | }
281 | });
282 | System.register("app", ["Controller"], function(exports_7, context_7) {
283 | "use strict";
284 | var __moduleName = context_7 && context_7.id;
285 | var Controller_1;
286 | var controller;
287 | return {
288 | setters:[
289 | function (Controller_1_1) {
290 | Controller_1 = Controller_1_1;
291 | }],
292 | execute: function() {
293 | controller = new Controller_1.default();
294 | }
295 | }
296 | });
297 | //# sourceMappingURL=bundle.js.map
--------------------------------------------------------------------------------
/dist/bundle.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"bundle.js","sourceRoot":"","sources":["../src/helpers.ts","../src/Todo.ts","../src/Store.ts","../src/templates.ts","../src/View.ts","../src/Controller.ts","../src/app.ts"],"names":[],"mappings":"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"}
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | TypeScript TodoMVC
6 |
7 |
8 |
9 |
13 |
14 | -
15 | Todo is not found.
16 |
17 |
18 |
21 |
22 |
23 |
24 |
25 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "typescript-todo",
3 | "version": "0.1.0",
4 | "description": "A Todo application implemented by typescript.",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "./node_modules/.bin/http-server .",
8 | "compile": "rm dist/bundle.js.map; rm dist/bundle.js; ./node_modules/.bin/tsc --project ./tsconfig.json",
9 | "watch": "rm dist/bundle.js.map; rm dist/bundle.js; ./node_modules/.bin/tsc --watch --project ./tsconfig.json"
10 | },
11 | "keywords": [
12 | "Typescript",
13 | "Todo",
14 | "TodoApp"
15 | ],
16 | "author": "HyunSeob (http://hyunseob.github.io)",
17 | "license": "MIT",
18 | "devDependencies": {
19 | "typescript": "^1.8.10"
20 | },
21 | "dependencies": {
22 | "http-server": "^0.9.0",
23 | "systemjs": "^0.19.38"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/Controller.ts:
--------------------------------------------------------------------------------
1 | import Todo from './Todo';
2 | import Store from './Store';
3 | import View from './View';
4 |
5 | export default class Controller {
6 |
7 | private _view : View;
8 | private _store: Store;
9 |
10 | constructor() {
11 | this._view = new View();
12 | this._store = new Store();
13 |
14 | this._view.onSubmit((value: string) => {
15 | const todo: Todo = new Todo(value);
16 | this._store.insert(todo);
17 | });
18 |
19 | this._view.onClickCheckbox((targetId: string) => {
20 | const targetTodo: Todo = this._store.find({ id: targetId })[0];
21 | targetTodo.toggle();
22 | this._store.update(targetTodo);
23 | });
24 |
25 | this._view.onClickButton((targetId: string) => {
26 | this._store.remove({ id: targetId });
27 | });
28 |
29 | this._store.subscribe((todos: Array) => {
30 | this._view.render(todos, this._store.count({ done: false }));
31 | });
32 |
33 | this._store.initialize();
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/Store.ts:
--------------------------------------------------------------------------------
1 | import Todo from './Todo';
2 |
3 | export default class Store {
4 |
5 | private _localStorage: Storage;
6 | private _todos : Array;
7 | private _subscribers : Array;
8 |
9 | constructor() {
10 | this._localStorage = window.localStorage;
11 | this._subscribers = [];
12 | }
13 |
14 | private _getStorage(): Array {
15 | return this._todos = this._todos || ((JSON.parse(
16 | this._localStorage.getItem('TodoList')
17 | )) || []).map((item: Object) => new Todo(item));
18 | }
19 |
20 | private _setStorage(todos: Array): void {
21 | this._localStorage.setItem(
22 | 'TodoList',
23 | JSON.stringify(todos)
24 | );
25 | this._publish();
26 | }
27 |
28 | initialize(): Store {
29 | this._getStorage();
30 | this._publish();
31 | return this;
32 | }
33 |
34 | insert(todo: Todo): void {
35 | this._setStorage(
36 | this._todos = this._getStorage().concat([ todo ])
37 | );
38 | }
39 |
40 | find(query: any): Array {
41 | return this._getStorage().filter((todo: Todo) => {
42 | for (let key in query) {
43 | if (todo[key] !== query[key]) {
44 | return false;
45 | }
46 | }
47 | return true;
48 | });
49 | }
50 |
51 | update(patch: Todo): void {
52 | this._setStorage(
53 | this._getStorage().map((target: Todo) => {
54 | if (target.equal(patch)) {
55 | target = patch;
56 | }
57 | return target;
58 | })
59 | );
60 | }
61 |
62 | remove(query: any): void {
63 | this._setStorage(
64 | this._todos = this._getStorage()
65 | .filter((todo: Todo) => {
66 | for (let key in query) {
67 | if (todo[key] !== query[key]) {
68 | return true;
69 | }
70 | }
71 | return false;
72 | })
73 | );
74 | }
75 |
76 | count(query: any): Number {
77 | return this._getStorage().filter((todo: Todo) => {
78 | for (let key in query) {
79 | if (todo[key] !== query[key]) {
80 | return false;
81 | }
82 | }
83 | return true;
84 | }).length;
85 | }
86 |
87 | subscribe(subscriber: Function): void {
88 | this._subscribers.push(subscriber);
89 | }
90 |
91 | private _publish(): void {
92 | this._subscribers.forEach(s => s(this._getStorage()));
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/src/Todo.ts:
--------------------------------------------------------------------------------
1 | import { guid } from './helpers';
2 |
3 | export default class Todo {
4 |
5 | [index: string]: any; /** Indexible */
6 |
7 | private _id : string;
8 | private _content: string;
9 | private _done : boolean;
10 |
11 | constructor(_todo: string | any) {
12 | if (typeof _todo === 'string') {
13 | this._id = guid();
14 | this._content = _todo;
15 | this._done = false;
16 | } else {
17 | this._id = _todo._id;
18 | this._content = _todo._content;
19 | this._done = _todo._done;
20 | }
21 | }
22 |
23 | get id (): string { return this._id; }
24 | get content(): string { return this._content; }
25 | get done (): boolean { return this._done; }
26 |
27 | set content(_content: string) { this._content = _content; }
28 |
29 | equal(_todo: Todo): boolean { return this._id === _todo.id; }
30 |
31 | toggle(): boolean { return this._done = !this._done; }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/src/View.ts:
--------------------------------------------------------------------------------
1 | import Todo from './Todo';
2 | import { todoListTpl, statTpl } from './templates';
3 |
4 | const ENTER_KEY = 13;
5 | const ESCAPE_KEY = 27;
6 |
7 | export default class View {
8 |
9 | private $todoList : Element;
10 | private $todoInput : Element;
11 | private $todoStat : Element;
12 | private _submitHandler: Function;
13 |
14 | constructor() {
15 | this.$todoList = document.querySelector('#todo-list');
16 | this.$todoInput = document.querySelector('#todo-input');
17 | this.$todoStat = document.querySelector('#todo-stat');
18 | }
19 |
20 | onSubmit(handler: Function): void {
21 | this.$todoInput.addEventListener('keypress', (event: KeyboardEvent) => {
22 | if (event.keyCode === ENTER_KEY) {
23 | const target: HTMLInputElement = event.target;
24 | handler(target.value);
25 | target.value = '';
26 | }
27 | });
28 | }
29 |
30 | onClickCheckbox(handler: Function): void {
31 | this.$todoList.addEventListener('click', (event: Event) => {
32 | const target: HTMLInputElement = event.target;
33 | if (target.tagName === 'INPUT' && target.type === 'checkbox') {
34 | handler(target.parentElement.dataset['id']);
35 | }
36 | });
37 | }
38 |
39 | onClickButton(handler: Function): void {
40 | this.$todoList.addEventListener('click', (event: Event) => {
41 | const target: HTMLElement = event.target;
42 | if (target.tagName === 'BUTTON') {
43 | handler(target.parentElement.dataset['id']);
44 | }
45 | });
46 | }
47 |
48 | render(todos: Array, lefts: Number): void {
49 | this.$todoList.innerHTML = todoListTpl(todos);
50 | this.$todoStat.innerHTML = statTpl(lefts);
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/app.ts:
--------------------------------------------------------------------------------
1 | import Controller from './Controller';
2 |
3 | const controller: Controller = new Controller();
4 |
--------------------------------------------------------------------------------
/src/helpers.ts:
--------------------------------------------------------------------------------
1 | function token(): string {
2 | return Math.floor((Math.random() + 1) * 0x10000)
3 | .toString(16)
4 | .substring(1);
5 | }
6 |
7 | export function guid(): string {
8 | /**
9 | * Generate a GUID.
10 | *
11 | * @example guid();
12 | */
13 | return [
14 | token() + token(),
15 | token(),
16 | token(),
17 | token(),
18 | token() + token()
19 | ].join('-');
20 | }
21 |
--------------------------------------------------------------------------------
/src/templates.ts:
--------------------------------------------------------------------------------
1 | import Todo from './Todo';
2 |
3 | export function todoTpl(todo: Todo): string {
4 | return [
5 | ``,
6 | ``,
7 | ``,
8 | ``,
9 | ``
10 | ].join('');
11 | }
12 |
13 | export function todoListTpl(todos: Array): string {
14 | return todos.map((t: Todo) => todoTpl(t)).join('');
15 | }
16 |
17 | export function statTpl(leftTodos: Number): string {
18 | return `${leftTodos} item${leftTodos === 1 ? '' : 's'} left.`;
19 | }
20 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "system",
4 | "noImplicitAny": true,
5 | "removeComments": true,
6 | "preserveConstEnums": true,
7 | "sourceMap": true,
8 | "target": "ES5",
9 | "outFile": "./dist/bundle.js",
10 | "pretty": true
11 | },
12 | "exclude": [
13 | "node_modules"
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------