@@ -23,9 +23,9 @@ function update(action, model, data) {
23
23
var new_model = JSON . parse ( JSON . stringify ( model ) ) // "clone" the model
24
24
switch ( action ) { // and an action (String) runs a switch
25
25
case 'ADD' :
26
- // can you see an "issue" with this way of generating the todo id? Bug...?
27
- var id = ( typeof model . todos !== 'undefined' && model . todos . length > 0 ) ?
28
- ( model . todos . length + 1 ) : 1 ;
26
+ var last = ( typeof model . todos !== 'undefined' && model . todos . length > 0 )
27
+ ? model . todos [ model . todos . length - 1 ] : null ;
28
+ var id = last ? last . id + 1 : 1 ;
29
29
var input = document . getElementById ( 'new-todo' ) ;
30
30
new_model . todos = ( new_model . todos && new_model . todos . length > 0 )
31
31
? new_model . todos : [ ] ;
@@ -54,17 +54,75 @@ function update(action, model, data) {
54
54
} ) ;
55
55
break ;
56
56
case 'DELETE' :
57
- new_model . todos = new_model . todos . filter ( function ( item ) {
58
- console . log ( item . id , data ) ;
57
+ console . log ( 'DELETE' , data ) ;
58
+ new_model . todos = new_model . todos . filter ( function ( item ) {
59
59
return item . id !== data ;
60
60
} ) ;
61
61
break ;
62
+ case 'EDIT' :
63
+ // this code is inspired by: https://stackoverflow.com/a/16033129/1148249
64
+ // simplified as we are not altering the DOM!
65
+ console . log ( 'EDIT' , data , model . editing , new_model . click_time , Date . now ( ) ) ;
66
+
67
+ // if we are ALREADY editing the todo item, no need to do anything
68
+ if ( new_model . editing && new_model . editing === data ) {
69
+ console . log ( 'already editing ...' , data ) ;
70
+ }
71
+ // on second click, the new_model.clicked will have been previously set
72
+ else if ( new_model . clicked && new_model . clicked === data &&
73
+ Date . now ( ) - 300 < new_model . click_time ) { // DOUBLE CLICK is faster than 300ms
74
+ new_model . editing = data ;
75
+ console . log ( 'DOUBLE CLICK' , data ) ;
76
+ }
77
+ else { // first click
78
+ new_model . clicked = data ;
79
+ new_model . click_time = Date . now ( )
80
+ new_model . editing = false ;
81
+ console . log ( 'FIRST CLICK' , data ) ;
82
+ }
83
+ break ;
84
+ case 'SAVE' :
85
+ var edit = document . querySelectorAll ( '.edit' ) [ 0 ] ;
86
+ var value = edit . value ;
87
+ var id = parseInt ( edit . id , 10 ) ;
88
+ // End Editing
89
+ new_model . clicked = false ;
90
+ new_model . editing = false ;
91
+
92
+ if ( ! value || value . length < 1 ) { // delete item if title is blank:
93
+ return update ( 'DELETE' , new_model , id ) ;
94
+ }
95
+ // update the value of the item.title that has been edited:
96
+ new_model . todos = new_model . todos . map ( function ( item ) {
97
+ if ( item . id === id && value && value . length > 0 ) {
98
+ item . title = value . trim ( ) ;
99
+ }
100
+ return item ; // return all todo items.
101
+ } ) ;
102
+ break ;
62
103
default : // if action unrecognised or undefined,
63
104
return model ; // return model unmodified
64
105
} // see: https://softwareengineering.stackexchange.com/a/201786/211301
65
106
return new_model ;
66
107
}
67
108
109
+ function show_item ( item , model , signal ) {
110
+ return [
111
+ label ( [ typeof signal === 'function' ? signal ( 'EDIT' , item . id ) : '' ] ,
112
+ [ text ( item . title ) ] ) ,
113
+ button ( [ "class=destroy" ,
114
+ typeof signal === 'function' ? signal ( 'DELETE' , item . id ) : '' ] )
115
+ ]
116
+ }
117
+
118
+ function edit_item ( item , model , signal ) {
119
+ return [
120
+ input ( [ "class=edit" , "id=" + item . id ,
121
+ "value=" + item . title , "autofocus" ] , [ ] ) ,
122
+ ]
123
+ }
124
+
125
+
68
126
/**
69
127
* `render_item` creates an DOM "tree" with a single Todo List Item
70
128
* using the "elmish" DOM functions (`li`, `div`, `input`, `label` and `button`)
@@ -86,21 +144,25 @@ function render_item (item, model, signal) {
86
144
li ( [
87
145
"data-id=" + item . id ,
88
146
"id=" + item . id ,
89
- item . done ? "class=completed" : ""
147
+ item . done ? "class=completed" : "" ,
148
+ model && model . editing && model . editing === item . id ? "class=editing" : ""
90
149
] , [
91
150
div ( [ "class=view" ] , [
92
151
input ( [
93
152
item . done ? "checked=true" : "" ,
94
153
"class=toggle" ,
95
154
"type=checkbox" ,
96
155
typeof signal === 'function' ? signal ( 'TOGGLE' , item . id ) : ''
97
- ] ,
98
- [ ] ) , // <input> does not have any nested elements
99
- label ( [ ] , [ text ( item . title ) ] ) ,
100
- button ( [ "class=destroy" ,
101
- typeof signal === 'function' ? signal ( 'DELETE' , item . id ) : '' ] )
102
- ] ) // </div>
103
- ] ) // </li>
156
+ ] , [ ] ) , // <input> does not have any nested elements
157
+ ] . concat ( model && model . editing !== item . id ?
158
+ show_item ( item , model , signal ) : [ ]
159
+ )
160
+ ) , // </div>
161
+ ] . concat (
162
+ model && model . editing && model . editing === item . id ?
163
+ edit_item ( item , model , signal ) : [ ]
164
+ ) // concat editing input if in "editing mode"
165
+ ) // </li>
104
166
)
105
167
}
106
168
@@ -236,12 +298,23 @@ function subscriptions (signal) {
236
298
document . addEventListener ( 'keyup' , function handler ( e ) {
237
299
var new_todo = document . getElementById ( 'new-todo' ) ;
238
300
console . log ( 'e.keyCode:' , e . keyCode , '| key:' , e . key ) ;
239
- if ( e . keyCode === ENTER_KEY && new_todo . value . length > 0 ) {
240
- signal ( 'ADD' ) ( ) ; // invoke singal inner callback
241
- new_todo . value = '' ; // reset <input> so we can add another todo
242
- document . getElementById ( 'new-todo' ) . focus ( ) ;
301
+ var editing = document . getElementsByClassName ( 'editing' ) ;
302
+ if ( e . keyCode === ENTER_KEY ) {
303
+ if ( editing && editing . length > 0 ) {
304
+ console . log ( 'editing:' , editing ) ;
305
+ signal ( 'SAVE' ) ( ) ; // invoke singal inner callback
306
+ }
307
+ if ( new_todo . value . length > 0 ) {
308
+ signal ( 'ADD' ) ( ) ; // invoke singal inner callback
309
+ new_todo . value = '' ; // reset <input> so we can add another todo
310
+ document . getElementById ( 'new-todo' ) . focus ( ) ;
311
+ }
243
312
}
244
313
} ) ;
314
+
315
+ // document.addEventListener('click', function click_anywhere (e) {
316
+ //
317
+ // });
245
318
}
246
319
247
320
/* module.exports is needed to run the functions using Node.js for testing! */
0 commit comments