├── .gitignore ├── README.md ├── css └── index.css ├── esm ├── index.js └── todo │ ├── footer.js │ ├── header.js │ ├── index.js │ ├── item.js │ ├── list.js │ ├── main.js │ └── utils.js ├── index.html ├── js ├── new.js └── old.js ├── package.json └── rollup ├── new.js └── old.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | package-lock.json -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [🔥 heresy 🔥](https://github.com/WebReflection/heresy) TodoMVC 2 | 3 | The live demo is @ https://webreflection.github.io/heresy-todo/ 4 | -------------------------------------------------------------------------------- /css/index.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | margin: 0; 4 | padding: 0; 5 | } 6 | 7 | button { 8 | margin: 0; 9 | padding: 0; 10 | border: 0; 11 | background: none; 12 | font-size: 100%; 13 | vertical-align: baseline; 14 | font-family: inherit; 15 | font-weight: inherit; 16 | color: inherit; 17 | -webkit-appearance: none; 18 | appearance: none; 19 | -webkit-font-smoothing: antialiased; 20 | -moz-osx-font-smoothing: grayscale; 21 | } 22 | 23 | body { 24 | font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; 25 | line-height: 1.4em; 26 | background: #f5f5f5; 27 | color: #111111; 28 | min-width: 230px; 29 | max-width: 550px; 30 | margin: 0 auto; 31 | -webkit-font-smoothing: antialiased; 32 | -moz-osx-font-smoothing: grayscale; 33 | font-weight: 300; 34 | } 35 | 36 | :focus { 37 | outline: 0; 38 | } 39 | 40 | .hidden { 41 | display: none; 42 | } 43 | 44 | .todoapp { 45 | background: #fff; 46 | margin: 130px 0 40px 0; 47 | position: relative; 48 | box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 49 | 0 25px 50px 0 rgba(0, 0, 0, 0.1); 50 | } 51 | 52 | .todoapp input::-webkit-input-placeholder { 53 | font-style: italic; 54 | font-weight: 300; 55 | color: rgba(0, 0, 0, 0.4); 56 | } 57 | 58 | .todoapp input::-moz-placeholder { 59 | font-style: italic; 60 | font-weight: 300; 61 | color: rgba(0, 0, 0, 0.4); 62 | } 63 | 64 | .todoapp input::input-placeholder { 65 | font-style: italic; 66 | font-weight: 300; 67 | color: rgba(0, 0, 0, 0.4); 68 | } 69 | 70 | .todoapp h1 { 71 | position: absolute; 72 | top: -140px; 73 | width: 100%; 74 | font-size: 80px; 75 | font-weight: 200; 76 | text-align: center; 77 | color: #b83f45; 78 | -webkit-text-rendering: optimizeLegibility; 79 | -moz-text-rendering: optimizeLegibility; 80 | text-rendering: optimizeLegibility; 81 | } 82 | 83 | .new-todo, 84 | .edit { 85 | position: relative; 86 | margin: 0; 87 | width: 100%; 88 | font-size: 24px; 89 | font-family: inherit; 90 | font-weight: inherit; 91 | line-height: 1.4em; 92 | color: inherit; 93 | padding: 6px; 94 | border: 1px solid #999; 95 | box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); 96 | box-sizing: border-box; 97 | -webkit-font-smoothing: antialiased; 98 | -moz-osx-font-smoothing: grayscale; 99 | } 100 | 101 | .new-todo { 102 | padding: 16px 16px 16px 60px; 103 | border: none; 104 | background: rgba(0, 0, 0, 0.003); 105 | box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03); 106 | } 107 | 108 | .main { 109 | position: relative; 110 | z-index: 2; 111 | border-top: 1px solid #e6e6e6; 112 | } 113 | 114 | .toggle-all { 115 | width: 1px; 116 | height: 1px; 117 | border: none; /* Mobile Safari */ 118 | opacity: 0; 119 | position: absolute; 120 | right: 100%; 121 | bottom: 100%; 122 | } 123 | 124 | .toggle-all + label { 125 | width: 60px; 126 | height: 34px; 127 | font-size: 0; 128 | position: absolute; 129 | top: -52px; 130 | left: -13px; 131 | -webkit-transform: rotate(90deg); 132 | transform: rotate(90deg); 133 | } 134 | 135 | .toggle-all + label:before { 136 | content: '❯'; 137 | font-size: 22px; 138 | color: #e6e6e6; 139 | padding: 10px 27px 10px 27px; 140 | } 141 | 142 | .toggle-all:checked + label:before { 143 | color: #737373; 144 | } 145 | 146 | .todo-list { 147 | margin: 0; 148 | padding: 0; 149 | list-style: none; 150 | } 151 | 152 | .todo-list li { 153 | position: relative; 154 | font-size: 24px; 155 | border-bottom: 1px solid #ededed; 156 | } 157 | 158 | .todo-list li:last-child { 159 | border-bottom: none; 160 | } 161 | 162 | .todo-list li.editing { 163 | border-bottom: none; 164 | padding: 0; 165 | } 166 | 167 | .todo-list li.editing .edit { 168 | display: block; 169 | width: calc(100% - 43px); 170 | padding: 12px 16px; 171 | margin: 0 0 0 43px; 172 | } 173 | 174 | .todo-list li.editing .view { 175 | display: none; 176 | } 177 | 178 | .todo-list li .toggle { 179 | text-align: center; 180 | width: 40px; 181 | /* auto, since non-WebKit browsers doesn't support input styling */ 182 | height: auto; 183 | position: absolute; 184 | top: 0; 185 | bottom: 0; 186 | margin: auto 0; 187 | border: none; /* Mobile Safari */ 188 | -webkit-appearance: none; 189 | appearance: none; 190 | } 191 | 192 | .todo-list li .toggle { 193 | opacity: 0; 194 | } 195 | 196 | .todo-list li .toggle + label { 197 | /* 198 | Firefox requires `#` to be escaped - https://bugzilla.mozilla.org/show_bug.cgi?id=922433 199 | IE and Edge requires *everything* to be escaped to render, so we do that instead of just the `#` - https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/7157459/ 200 | */ 201 | background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23ededed%22%20stroke-width%3D%223%22/%3E%3C/svg%3E'); 202 | background-repeat: no-repeat; 203 | background-position: center left; 204 | } 205 | 206 | .todo-list li .toggle:checked + label { 207 | background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23bddad5%22%20stroke-width%3D%223%22/%3E%3Cpath%20fill%3D%22%235dc2af%22%20d%3D%22M72%2025L42%2071%2027%2056l-4%204%2020%2020%2034-52z%22/%3E%3C/svg%3E'); 208 | } 209 | 210 | .todo-list li label { 211 | word-break: break-all; 212 | padding: 15px 15px 15px 60px; 213 | display: block; 214 | line-height: 1.2; 215 | transition: color 0.4s; 216 | font-weight: 400; 217 | color: #4d4d4d; 218 | } 219 | 220 | .todo-list li.completed label { 221 | color: #cdcdcd; 222 | text-decoration: line-through; 223 | } 224 | 225 | .todo-list li .destroy { 226 | display: none; 227 | position: absolute; 228 | top: 0; 229 | right: 10px; 230 | bottom: 0; 231 | width: 40px; 232 | height: 40px; 233 | margin: auto 0; 234 | font-size: 30px; 235 | color: #cc9a9a; 236 | margin-bottom: 11px; 237 | transition: color 0.2s ease-out; 238 | } 239 | 240 | .todo-list li .destroy:hover { 241 | color: #af5b5e; 242 | } 243 | 244 | .todo-list li .destroy:after { 245 | content: '×'; 246 | } 247 | 248 | .todo-list li:hover .destroy { 249 | display: block; 250 | } 251 | 252 | .todo-list li .edit { 253 | display: none; 254 | } 255 | 256 | .todo-list li.editing:last-child { 257 | margin-bottom: -1px; 258 | } 259 | 260 | .footer { 261 | padding: 10px 15px; 262 | height: 20px; 263 | text-align: center; 264 | font-size: 15px; 265 | border-top: 1px solid #e6e6e6; 266 | } 267 | 268 | .footer:before { 269 | content: ''; 270 | position: absolute; 271 | right: 0; 272 | bottom: 0; 273 | left: 0; 274 | height: 50px; 275 | overflow: hidden; 276 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), 277 | 0 8px 0 -3px #f6f6f6, 278 | 0 9px 1px -3px rgba(0, 0, 0, 0.2), 279 | 0 16px 0 -6px #f6f6f6, 280 | 0 17px 2px -6px rgba(0, 0, 0, 0.2); 281 | } 282 | 283 | .todo-count { 284 | float: left; 285 | text-align: left; 286 | } 287 | 288 | .todo-count strong { 289 | font-weight: 300; 290 | } 291 | 292 | .filters { 293 | margin: 0; 294 | padding: 0; 295 | list-style: none; 296 | position: absolute; 297 | right: 0; 298 | left: 0; 299 | } 300 | 301 | .filters li { 302 | display: inline; 303 | } 304 | 305 | .filters li a { 306 | color: inherit; 307 | margin: 3px; 308 | padding: 3px 7px; 309 | text-decoration: none; 310 | border: 1px solid transparent; 311 | border-radius: 3px; 312 | } 313 | 314 | .filters li a:hover { 315 | border-color: rgba(175, 47, 47, 0.1); 316 | } 317 | 318 | .filters li a.selected { 319 | border-color: rgba(175, 47, 47, 0.2); 320 | } 321 | 322 | .clear-completed, 323 | html .clear-completed:active { 324 | float: right; 325 | position: relative; 326 | line-height: 20px; 327 | text-decoration: none; 328 | cursor: pointer; 329 | } 330 | 331 | .clear-completed:hover { 332 | text-decoration: underline; 333 | } 334 | 335 | .info { 336 | margin: 65px auto 0; 337 | color: #4d4d4d; 338 | font-size: 11px; 339 | text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); 340 | text-align: center; 341 | } 342 | 343 | .info p { 344 | line-height: 1; 345 | } 346 | 347 | .info a { 348 | color: inherit; 349 | text-decoration: none; 350 | font-weight: 400; 351 | } 352 | 353 | .info a:hover { 354 | text-decoration: underline; 355 | } 356 | 357 | /* 358 | Hack to remove background from Mobile Safari. 359 | Can't use it globally since it destroys checkboxes in Firefox 360 | */ 361 | @media screen and (-webkit-min-device-pixel-ratio:0) { 362 | .toggle-all, 363 | .todo-list li .toggle { 364 | background: none; 365 | } 366 | 367 | .todo-list li .toggle { 368 | height: 40px; 369 | } 370 | } 371 | 372 | @media (max-width: 430px) { 373 | .footer { 374 | height: 50px; 375 | } 376 | 377 | .filters { 378 | bottom: 10px; 379 | } 380 | } 381 | -------------------------------------------------------------------------------- /esm/index.js: -------------------------------------------------------------------------------- 1 | import {define} from 'heresy'; 2 | import Todo from './todo/index.js'; 3 | 4 | // define globally the Todo:section 5 | // it will be upgraded/hydrated on the fly 6 | define('Todo', Todo); 7 | -------------------------------------------------------------------------------- /esm/todo/footer.js: -------------------------------------------------------------------------------- 1 | export default { 2 | extends: 'footer', 3 | 4 | mappedAttributes: ['count'], 5 | oncount() { this.render(); }, 6 | 7 | render() { 8 | this.html` 9 | ${this.count} 10 | 21 | 22 | `; 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /esm/todo/header.js: -------------------------------------------------------------------------------- 1 | export default { 2 | extends: 'header', 3 | render() { 4 | this.html` 5 |

todos

6 | 7 | `; 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /esm/todo/index.js: -------------------------------------------------------------------------------- 1 | import {ref} from 'heresy'; 2 | 3 | import {data} from './utils.js'; 4 | 5 | import Header from './header.js'; 6 | import Main from './main.js'; 7 | import Footer from './footer.js'; 8 | 9 | export default { 10 | 11 | // declaration + local components + CSS 12 | extends: 'section', 13 | includes: {Header, Main, Footer}, 14 | style: (self /*, Header, Main, Footer*/) => ` 15 | ${self} ul.completed > li:not(.completed), 16 | ${self} ul.active > li.completed { 17 | display: none; 18 | } 19 | `, 20 | 21 | // lifecycle events 22 | oninit() { 23 | this.data = data([this.id || '', this.is].join(':')); 24 | this.header = ref(); 25 | this.main = ref(); 26 | this.footer = ref(); 27 | }, 28 | 29 | // render view 30 | render() { 31 | const tot = getCount(this.data.items); 32 | this.html` 33 |
34 |
35 |