From afda43dc0281e3671d6df3218749d5050d47f188 Mon Sep 17 00:00:00 2001 From: David East Date: Mon, 9 Mar 2015 16:37:28 -0700 Subject: [PATCH] feat(examples): Add TodoMVC sample application. --- modules/examples/src/todo/css/base.css | 379 ++++++++++++++++++ modules/examples/src/todo/css/bg.png | Bin 0 -> 2126 bytes modules/examples/src/todo/css/main.css | 19 + modules/examples/src/todo/index.html | 13 + modules/examples/src/todo/index.js | 77 ++++ .../examples/src/todo/services/TodoStore.js | 72 ++++ modules/examples/src/todo/todo.html | 71 ++++ 7 files changed, 631 insertions(+) create mode 100644 modules/examples/src/todo/css/base.css create mode 100644 modules/examples/src/todo/css/bg.png create mode 100644 modules/examples/src/todo/css/main.css create mode 100644 modules/examples/src/todo/index.html create mode 100644 modules/examples/src/todo/index.js create mode 100644 modules/examples/src/todo/services/TodoStore.js create mode 100644 modules/examples/src/todo/todo.html diff --git a/modules/examples/src/todo/css/base.css b/modules/examples/src/todo/css/base.css new file mode 100644 index 0000000000..502f06cf18 --- /dev/null +++ b/modules/examples/src/todo/css/base.css @@ -0,0 +1,379 @@ +@charset "utf-8"; + +button { + margin: 0; + padding: 0; + border: 0; + background: none; + font-size: 100%; + vertical-align: baseline; + font-family: inherit; + font-weight: inherit; + color: inherit; + -webkit-appearance: none; + -ms-appearance: none; + appearance: none; + -webkit-font-smoothing: antialiased; + -moz-font-smoothing: antialiased; + -ms-font-smoothing: antialiased; + font-smoothing: antialiased; +} + +button, +input[type="checkbox"] { + outline: none; +} + +.hidden { + display: none; +} + +.visible { + display: block !important; +} + +#todoapp { + background: #fff; + margin: 130px 0 40px 0; + position: relative; + box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), + 0 25px 50px 0 rgba(0, 0, 0, 0.1); +} + +#todoapp input::-webkit-input-placeholder { + font-style: italic; + font-weight: 300; + color: #e6e6e6; +} + +#todoapp input::-moz-placeholder { + font-style: italic; + font-weight: 300; + color: #e6e6e6; +} + +#todoapp input::input-placeholder { + font-style: italic; + font-weight: 300; + color: #e6e6e6; +} + +#todoapp h1 { + position: absolute; + top: -155px; + width: 100%; + font-size: 100px; + font-weight: 100; + text-align: center; + color: rgba(175, 47, 47, 0.15); + -webkit-text-rendering: optimizeLegibility; + -moz-text-rendering: optimizeLegibility; + -ms-text-rendering: optimizeLegibility; + text-rendering: optimizeLegibility; +} + +#new-todo, +.edit { + position: relative; + margin: 0; + width: 100%; + font-size: 24px; + font-family: inherit; + font-weight: inherit; + line-height: 1.4em; + border: 0; + outline: none; + color: inherit; + padding: 6px; + border: 1px solid #999; + box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); + -ms-box-sizing: border-box; + box-sizing: border-box; + -webkit-font-smoothing: antialiased; + -moz-font-smoothing: antialiased; + -ms-font-smoothing: antialiased; + font-smoothing: antialiased; +} + +#new-todo { + padding: 16px 16px 16px 60px; + border: none; + background: rgba(0, 0, 0, 0.003); + box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03); +} + +#main { + position: relative; + z-index: 2; + border-top: 1px solid #e6e6e6; +} + +label[for='toggle-all'] { + display: none; +} + +#toggle-all { + position: absolute; + top: -55px; + left: -12px; + width: 60px; + height: 34px; + text-align: center; + border: none; /* Mobile Safari */ +} + +#toggle-all:before { + content: '❯'; + font-size: 22px; + color: #e6e6e6; + padding: 10px 27px 10px 27px; +} + +#toggle-all:checked:before { + color: #737373; +} + +#todo-list { + margin: 0; + padding: 0; + list-style: none; +} + +#todo-list li { + position: relative; + font-size: 24px; + border-bottom: 1px solid #ededed; +} + +#todo-list li:last-child { + border-bottom: none; +} + +#todo-list li.editing { + border-bottom: none; + padding: 0; +} + +#todo-list li.editing .edit { + display: block; + width: 506px; + padding: 13px 17px 12px 17px; + margin: 0 0 0 43px; +} + +#todo-list li.editing .view { + display: none; +} + +#todo-list li .toggle { + text-align: center; + width: 40px; + /* auto, since non-WebKit browsers doesn't support input styling */ + height: auto; + position: absolute; + top: 0; + bottom: 0; + margin: auto 0; + border: none; /* Mobile Safari */ + -webkit-appearance: none; + -ms-appearance: none; + appearance: none; +} + +#todo-list li .toggle:after { + content: url('data:image/svg+xml;utf8,'); +} + +#todo-list li .toggle:checked:after { + content: url('data:image/svg+xml;utf8,'); +} + +#todo-list li label { + white-space: pre; + word-break: break-word; + padding: 15px 60px 15px 15px; + margin-left: 45px; + display: block; + line-height: 1.2; + transition: color 0.4s; +} + +#todo-list li.completed label { + color: #d9d9d9; + text-decoration: line-through; +} + +#todo-list li .destroy { + display: none; + position: absolute; + top: 0; + right: 10px; + bottom: 0; + width: 40px; + height: 40px; + margin: auto 0; + font-size: 30px; + color: #cc9a9a; + margin-bottom: 11px; + transition: color 0.2s ease-out; +} + +#todo-list li .destroy:hover { + color: #af5b5e; +} + +#todo-list li .destroy:after { + content: '×'; +} + +#todo-list li:hover .destroy { + display: block; +} + +#todo-list li .edit { + display: none; +} + +#todo-list li.editing:last-child { + margin-bottom: -1px; +} + +#footer { + color: #777; + padding: 10px 15px; + height: 20px; + text-align: center; + border-top: 1px solid #e6e6e6; +} + +#footer:before { + content: ''; + position: absolute; + right: 0; + bottom: 0; + left: 0; + height: 50px; + overflow: hidden; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), + 0 8px 0 -3px #f6f6f6, + 0 9px 1px -3px rgba(0, 0, 0, 0.2), + 0 16px 0 -6px #f6f6f6, + 0 17px 2px -6px rgba(0, 0, 0, 0.2); +} + +#todo-count { + float: left; + text-align: left; +} + +#todo-count strong { + font-weight: 300; +} + +#filters { + margin: 0; + padding: 0; + list-style: none; + position: absolute; + right: 0; + left: 0; +} + +#filters li { + display: inline; +} + +#filters li a { + color: inherit; + margin: 3px; + padding: 3px 7px; + text-decoration: none; + border: 1px solid transparent; + border-radius: 3px; +} + +#filters li a.selected, +#filters li a:hover { + border-color: rgba(175, 47, 47, 0.1); +} + +#filters li a.selected { + border-color: rgba(175, 47, 47, 0.2); +} + +#clear-completed, +html #clear-completed:active { + float: right; + position: relative; + line-height: 20px; + text-decoration: none; + cursor: pointer; + visibility: hidden; + position: relative; +} + +#clear-completed::after { + visibility: visible; + content: 'Clear completed'; + position: absolute; + right: 0; + white-space: nowrap; +} + +#clear-completed:hover::after { + text-decoration: underline; +} + +#info { + margin: 65px auto 0; + color: #bfbfbf; + font-size: 10px; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); + text-align: center; +} + +#info p { + line-height: 1; +} + +#info a { + color: inherit; + text-decoration: none; + font-weight: 400; +} + +#info a:hover { + text-decoration: underline; +} + +/* +Hack to remove background from Mobile Safari. +Can't use it globally since it destroys checkboxes in Firefox +*/ +@media screen and (-webkit-min-device-pixel-ratio:0) { + #toggle-all, + #todo-list li .toggle { + background: none; + } + + #todo-list li .toggle { + height: 40px; + } + + #toggle-all { + -webkit-transform: rotate(90deg); + transform: rotate(90deg); + -webkit-appearance: none; + appearance: none; + } +} + +@media (max-width: 430px) { + #footer { + height: 50px; + } + + #filters { + bottom: 10px; + } +} diff --git a/modules/examples/src/todo/css/bg.png b/modules/examples/src/todo/css/bg.png new file mode 100644 index 0000000000000000000000000000000000000000..b2a7600825ee11f21849ed475499c7d1ecbcc870 GIT binary patch literal 2126 zcmV-U2(kBxP)+9y`=HK7nt=~3t000O5Nklm=04hVa_lXl_bm=+M;=eI<%$rO2ta`suh*Sr#POw*?adq!O!CV z*0&b)rkCCEV)1nIuiLBntUH-MJI;&qYgz|d@Nhnn_abi2g`pu4NAMVid3hS%?quz~ zjlJf(x8cj{VS zUVEP1msg9`^OFUhSO5rtXHhRlyU2i>6$BT=glG`{JlctGHrwRIm)6Buu65jLQn^H8 zvl9lZqUBA+%qvM5XC+yY@mfA(YB~XP=e|6<{%$^eU8nrK?v2ccb@Jom5CG>rKAL7J zplHEIavVp|0p1&m!G`Ti?<7iZ;}@G7#Z)}Km%2!FHnQ0*&?PLR)G!HAaHgU#c|$dz zpj#0HmeadIe>`#)=Jjkb{B=FKnU3pc-&Y@j_j6(h4Y~+Uq!^J=LHunx1xi4QXK-Y{&MoT8ky6Vb9c|WhnOwF~c=3zOy8agktliS6# z`#8F`9H)D=bmk9B5MnW&_r#)f$c+;$LSr-@^An8dhc~Iquuv>jOK7pw7LJ;&X0i1C zGMsHdP1Os@ny$$j!>XAii%7bp5k>`pyNA!~epb)()p9zR4Yl6z=U}{CIdh1z(FpAo zQIW!;0zpCyC4*7YLkZCO@cul0R_&zghsA8Ek)z%jNpKa92{@NH+SstX%@}xB*Jk!l}PZ1cClIns~}5^!RncUk*rmA z%SIVgt58EQxLJ+OiFqKkBuwquyZLUp|gr zPUbUbFbBrPd1xO`^C*r-i3p9*F#(OBh!4Wy@aC&*!|O|GXcjDA&YhF{;cD7*o(GS^ z@pQSE7)x_81=Qyje6*LQ#TXu-7(o;S8u3tpDA0_ddj%hmCspeXdhYR}-i9A=C`EoChUYuH~^x!9+|&(Pgb*>Ck<=9j|)*@xyfT zpP#+Kt<}39b|3Wl4fxT(+G?aH>gG^d@MEaUOJRfy55TtFI7^Y)VuMU=7Pp0y55jS~ z+TJ)igMyrqkX;wU8j68iIDtqJYhS_D3_Oem-@g6me_RfiQ}uT_K-A-9qG%}gk3E9c z!8`KgsMNXm)beloPfMql*|&$M>1q`i!cY)RFYnjNYu*gSv@8XebV7%}xYL>6)GJPJ zJpA7K31lcJuIFKSw-{x4FX0K2Az)~Xg<`sOu$4|^-(^XJX4YzoiNRvL zyuY7~26w-P^Zw%6F}shBwV2$02c8RsSUy6dM92diCAxiX3IUqsq5ig6>U_!Vy*q5$ zjm}16tJr+QZ&T?HkkpeIwVX-r1EI=@%Zlt;6g*mj&E!A))%gXJ=x6u#l zcKEP>bx4rqV*k`BBrZ$Mqjt{TsHcxUH>;)iAK}B(Kila&VD%b?6m&^0WK4^&EZNAI z8AMYs_%$D%|M+@+cQqL-hi4yG>h*jwdii!W1{}p>ZhwFYt_4Su1kjY9<-5`!$VNOI1y(EDSH4WpCijE_>al!Nt=^eVER#uJ1=b z;BWF2IgyF5LexV+yec$Kin;Ai@myo;G>zJ&jrdW#ecXhIZph5OYqw_PEfcF56Z5Zu_ZZE#q3Mc+N&1O^7hoh9QR7`+L$cBP5pqG=fqA0uh zUBukaxTFmH)<|Xbvp2c=HSbNkpXw73a5lv7V$jb92#yZ141@$X?F}Jt8gIU7Wm6m9 z?e_;;zsnm6`LZeP>T=$)Kr>Z?kr*UmFqR7zx0C6^bmcsc@1AGtw_rNH>-Xm$d*|Q< zn&1Ln0u7=l&ILs>%CkJp`DiG9F18x4Ne+lg<#i?e7jL%x;4ZnRkN^Mx07*qoM6N<$ Ef(>0N!2kdN literal 0 HcmV?d00001 diff --git a/modules/examples/src/todo/css/main.css b/modules/examples/src/todo/css/main.css new file mode 100644 index 0000000000..6177782da2 --- /dev/null +++ b/modules/examples/src/todo/css/main.css @@ -0,0 +1,19 @@ +html, +body { + margin: 0; + padding: 0; +} + +body { + font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; + line-height: 1.4em; + background: #eaeaea; + color: #4d4d4d; + width: 550px; + margin: 0 auto; + -webkit-font-smoothing: antialiased; + -moz-font-smoothing: antialiased; + -ms-font-smoothing: antialiased; + -o-font-smoothing: antialiased; + font-smoothing: antialiased; +} diff --git a/modules/examples/src/todo/index.html b/modules/examples/src/todo/index.html new file mode 100644 index 0000000000..a7e935cb7b --- /dev/null +++ b/modules/examples/src/todo/index.html @@ -0,0 +1,13 @@ + + + Todo Angular 2 + + + + Loading... + + + + $SCRIPTS$ + + diff --git a/modules/examples/src/todo/index.js b/modules/examples/src/todo/index.js new file mode 100644 index 0000000000..c90e5e967d --- /dev/null +++ b/modules/examples/src/todo/index.js @@ -0,0 +1,77 @@ +import {bootstrap, Component, Template, Foreach} from 'angular2/angular2'; +import {Store, Todo, TodoFactory} from './services/TodoStore'; + +@Component({ + selector: 'todo-app', + services: [ + Store, + TodoFactory + ] +}) +@Template({ + url: 'todo.html', + directives: [Foreach] +}) +class TodoApp { + todoStore: Store; + todoEdit: Todo; + factory: TodoFactory; + + constructor(store: Store, factory: TodoFactory) { + this.todoStore = store; + this.todoEdit = null; + this.factory = factory; + } + + enterTodo($event, inputElement) { + if($event.which === 13) { // ENTER_KEY + this.addTodo(inputElement.value); + inputElement.value = ''; + } + } + + editTodo(todo: Todo) { + this.todoEdit = todo; + } + + doneEditing($event, todo: Todo) { + var which = $event.which; + var target = $event.target; + if(which === 13) { + todo.title = target.value; + this.todoStore.save(todo); + this.todoEdit = null; + } else if (which === 27) { + this.todoEdit = null; + target.value = todo.title; + } + } + + addTodo(newTitle: string) { + this.todoStore.add(this.factory.create(newTitle, false)); + } + + completeMe(todo: Todo) { + todo.completed = !todo.completed; + } + + deleteMe(todo: Todo) { + this.todoStore.remove(todo); + } + + toggleAll($event) { + var isComplete = $event.target.checked; + this.todoStore.list.forEach((todo) => { + todo.completed = isComplete; + this.todoStore.save(todo); + }); + } + + clearCompleted() { + this.todoStore.removeBy((todo) => todo.completed); + } +} + +export function main() { + bootstrap(TodoApp); +} diff --git a/modules/examples/src/todo/services/TodoStore.js b/modules/examples/src/todo/services/TodoStore.js new file mode 100644 index 0000000000..751c7656dd --- /dev/null +++ b/modules/examples/src/todo/services/TodoStore.js @@ -0,0 +1,72 @@ +import {ListWrapper} from 'angular2/src/facade/collection'; + +// base model for RecordStore +export class KeyModel { + _key: number; + constructor(key: number) { + this._key = key; + } +} + +export class Todo extends KeyModel { + title: string; + completed: boolean; + + constructor(key: number, theTitle: string, isCompleted: boolean) { + super(key); + this.title = theTitle; + this.completed = isCompleted; + } +} + +export class TodoFactory { + _uid: number; + + constructor() { + this._uid = 0; + } + + nextUid() { + this._uid = this._uid + 1; + return this._uid; + } + + create(title: string, isCompleted: boolean) { + return new Todo(this.nextUid(), title, isCompleted); + } +} + +// Store manages any generic item that inherits from KeyModel +export class Store { + list: List; + + constructor() { + this.list = []; + } + + add(record: KeyModel) { + this.list.push(record); + } + + remove(record: KeyModel) { + this.spliceOut(record); + } + + removeBy(callback: Function) { + var records = ListWrapper.filter(this.list, callback); + ListWrapper.removeAll(this.list, records); + } + + spliceOut(record: KeyModel) { + var i = this.indexFor(record); + if( i > -1 ) { + return this.list.splice(i, 1)[0]; + } + return null; + } + + indexFor(record: KeyModel) { + return this.list.indexOf(record); + } + +} diff --git a/modules/examples/src/todo/todo.html b/modules/examples/src/todo/todo.html new file mode 100644 index 0000000000..51db5c016a --- /dev/null +++ b/modules/examples/src/todo/todo.html @@ -0,0 +1,71 @@ + + +
+ + + +
+ + + +
    + +
  • + +
    + + + + + + +
    + +
    + + + +
    + +
  • +
+ +
+ + + +
+ +