├── public ├── css │ └── readme.md ├── js │ └── readme.md └── index.html ├── .gitignore ├── src ├── scss │ ├── _layout.scss │ ├── app.scss │ ├── _user-list.scss │ └── _user-form.scss └── ts │ ├── tsconfig.json │ ├── components │ ├── layout.ts │ ├── user-list.ts │ └── user-form.ts │ ├── main.ts │ ├── models │ └── user.ts │ └── tslint.json ├── .vscode └── settings.json ├── readme.md └── package.json /public/css/readme.md: -------------------------------------------------------------------------------- 1 | Compiled CSS will go here 2 | -------------------------------------------------------------------------------- /public/js/readme.md: -------------------------------------------------------------------------------- 1 | Compiled JS will go here. 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | npm-debug.log 3 | /public/js/app.js 4 | /public/css/app.css 5 | -------------------------------------------------------------------------------- /src/scss/_layout.scss: -------------------------------------------------------------------------------- 1 | .layout { 2 | margin: 10px auto; 3 | max-width: 1000px; 4 | 5 | .menu { 6 | margin: 0 0 30px; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/scss/app.scss: -------------------------------------------------------------------------------- 1 | body, .input, .button { 2 | font: normal 16px Verdana; 3 | margin: 0; 4 | } 5 | 6 | @import 'user-list'; 7 | @import 'user-form'; 8 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "./node_modules/typescript/lib", 3 | "editor.insertSpaces": false, 4 | "files.eol": "\n", 5 | "files.trimTrailingWhitespace": true 6 | } 7 | -------------------------------------------------------------------------------- /src/ts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "lib": ["es2015", "dom"], 6 | "strict": true, 7 | "esModuleInterop": true 8 | }, 9 | "files": ["main.ts"], 10 | "compileOnSave": false, 11 | "buildOnSave": false 12 | } 13 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Mithril Tutorial 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/ts/components/layout.ts: -------------------------------------------------------------------------------- 1 | import m from 'mithril' 2 | 3 | export default { 4 | view (vnode) { 5 | return m("main.layout", [ 6 | m("nav.menu", [ 7 | m("a[href='/list']", {oncreate: m.route.link}, "Users") 8 | ]), 9 | m("section", vnode.children) 10 | ]) 11 | } 12 | } as m.Component 13 | -------------------------------------------------------------------------------- /src/scss/_user-list.scss: -------------------------------------------------------------------------------- 1 | .user-list { 2 | list-style: none; 3 | margin: 0 0 10px; 4 | padding: 0; 5 | 6 | .item { 7 | background: #fafafa; 8 | border: 1px solid #ddd; 9 | color: #333; 10 | display: block; 11 | margin: 0 0 1px; 12 | padding: 8px 15px; 13 | text-decoration: none; 14 | } 15 | 16 | .item:hover { 17 | text-decoration: underline; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/ts/components/user-list.ts: -------------------------------------------------------------------------------- 1 | import m from 'mithril' 2 | import UserModel from '../models/user' 3 | 4 | export default { 5 | oninit: UserModel.loadList, 6 | view() { 7 | return m(".user-list", 8 | UserModel.list.map(user => 9 | m("a.item", 10 | {href: "/edit/" + user.id, oncreate: m.route.link}, 11 | `${user.firstName} ${user.lastName}` 12 | ) 13 | ) 14 | ) 15 | } 16 | } as m.Component 17 | -------------------------------------------------------------------------------- /src/ts/main.ts: -------------------------------------------------------------------------------- 1 | import m from 'mithril' 2 | import UserList from './components/user-list' 3 | import UserForm from './components/user-form' 4 | import Layout from './components/layout' 5 | 6 | m.route(document.body, "/list", { 7 | "/list": { 8 | render() { 9 | return m(Layout, m(UserList)) 10 | } 11 | }, 12 | "/edit/:id": { 13 | render(vnode) { 14 | return m(Layout, m(UserForm, vnode.attrs)) 15 | } 16 | } 17 | }) 18 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # [Mithril 1.1](https://mithril.js.org/) Simple App Tutorial 2 | 3 | Typescript version adapted from: https://mithril.js.org/simple-application.html 4 | 5 | Uses types from DefinitelyTyped on npm (@types/mithril) 6 | 7 | ## Install: 8 | 9 | npm install 10 | 11 | ### Start localhost server, compile JS & CSS, watch for changes: 12 | 13 | npm start 14 | 15 | Then go to http://localhost:3000/ in your browser. 16 | 17 | ### Build minified: 18 | 19 | npm run build 20 | 21 | Outputs to `public/js/app.js` and `public/css/app.js` 22 | -------------------------------------------------------------------------------- /src/scss/_user-form.scss: -------------------------------------------------------------------------------- 1 | .user-form { 2 | .label { 3 | display:block;margin:0 0 5px; 4 | } 5 | 6 | .input { 7 | border: 1px solid #ddd; 8 | border-radius: 3px; 9 | box-sizing: border-box; 10 | display: block; 11 | margin: 0 0 10px; 12 | padding: 10px 15px; 13 | width: 100%; 14 | } 15 | 16 | .button { 17 | background: #eee; 18 | border: 1px solid #ddd; 19 | border-radius: 3px; 20 | color: #333; 21 | display: inline-block; 22 | margin: 0 0 10px; 23 | padding: 10px 15px; 24 | text-decoration: none; 25 | } 26 | 27 | .button:hover { 28 | background: #e8e8e8; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/ts/components/user-form.ts: -------------------------------------------------------------------------------- 1 | import m from 'mithril' 2 | import UserModel from '../models/user' 3 | 4 | export interface Attrs { 5 | id: string 6 | } 7 | 8 | export default { 9 | oninit (vnode) { 10 | UserModel.load(Number(vnode.attrs.id)) 11 | }, 12 | 13 | view() { 14 | return m("form.user-form", 15 | { 16 | onsubmit: (e: Event) => { 17 | e.preventDefault() 18 | UserModel.save() 19 | } 20 | }, 21 | [ 22 | m("label.label", "First name"), 23 | m("input.input[type=text][placeholder=First name]", { 24 | oninput: m.withAttr("value", value => { 25 | UserModel.current.firstName = value 26 | }), 27 | value: UserModel.current.firstName 28 | }), 29 | m("label.label", "Last name"), 30 | m("input.input[placeholder=Last name]", { 31 | oninput: m.withAttr("value", value => { 32 | UserModel.current.lastName = value 33 | }), 34 | value: UserModel.current.lastName 35 | }), 36 | m("button.button[type=submit]", "Save"), 37 | ] 38 | ) 39 | } 40 | } as m.Component 41 | -------------------------------------------------------------------------------- /src/ts/models/user.ts: -------------------------------------------------------------------------------- 1 | import m from 'mithril' 2 | 3 | export interface User { 4 | id: number 5 | firstName: string 6 | lastName: string 7 | } 8 | 9 | const UserModel = { 10 | list: [] as User[], 11 | loadList() { 12 | return m.request<{data: User[]}>({ 13 | method: "GET", 14 | url: "https://rem-rest-api.herokuapp.com/api/users", 15 | withCredentials: true 16 | }) 17 | .then(result => { 18 | UserModel.list = result.data 19 | }) 20 | }, 21 | 22 | current: {} as User, 23 | load (id: number) { 24 | return m.request({ 25 | method: "GET", 26 | url: "https://rem-rest-api.herokuapp.com/api/users/" + id, 27 | withCredentials: true 28 | }) 29 | .then(result => { 30 | UserModel.current = result 31 | }) 32 | }, 33 | 34 | save() { 35 | return m.request({ 36 | method: "PUT", 37 | url: "https://rem-rest-api.herokuapp.com/api/users/" + UserModel.current.id, 38 | data: UserModel.current, 39 | withCredentials: true 40 | }) 41 | } 42 | } 43 | 44 | type UserModel = typeof UserModel 45 | 46 | export default UserModel 47 | -------------------------------------------------------------------------------- /src/ts/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "class-name": true, 4 | "comment-format": [ 5 | false, 6 | "check-space" 7 | ], 8 | "no-duplicate-variable": true, 9 | "no-eval": true, 10 | "no-internal-module": false, 11 | "no-trailing-whitespace": true, 12 | "no-unused-variable": true, 13 | "no-var-keyword": true, 14 | "one-line": [ 15 | true, 16 | "check-open-brace", 17 | "check-whitespace" 18 | ], 19 | "quotemark": [ 20 | false, 21 | "double" 22 | ], 23 | "semicolon": [true, "never"], 24 | "triple-equals": [ 25 | true, 26 | "allow-null-check" 27 | ], 28 | "typedef-whitespace": [ 29 | false, 30 | { 31 | "call-signature": "nospace", 32 | "index-signature": "nospace", 33 | "parameter": "nospace", 34 | "property-declaration": "nospace", 35 | "variable-declaration": "nospace" 36 | } 37 | ], 38 | "variable-name": [ 39 | true, 40 | "ban-keywords" 41 | ], 42 | "whitespace": [ 43 | true, 44 | "check-branch", 45 | "check-decl", 46 | "check-operator", 47 | "check-type" 48 | ] 49 | } 50 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mithril-tutorial-ts", 3 | "version": "1.1.0", 4 | "description": "Mithril Tutorial (Typescript)", 5 | "author": "spacejack", 6 | "license": "MIT", 7 | "scripts": { 8 | "serve": "http-server -p 3000 public", 9 | "compile-ts": "browserify --debug src/ts/main.ts -p [ tsify --project src/ts/tsconfig.json ] -o public/js/app.js", 10 | "watch-ts": "watchify -v --debug src/ts/main.ts -p [ tsify --project src/ts/tsconfig.json ] -o public/js/app.js", 11 | "build-ts": "browserify src/ts/main.ts -p [ tsify --project src/ts/tsconfig.json ] | uglifyjs -cm -o public/js/app.js", 12 | "compile-scss": "node-sass src/scss/app.scss | postcss --use autoprefixer -o public/css/app.css", 13 | "watch-scss": "npm run compile-scss && nodemon -w src/scss -e scss -x \"npm run compile-scss\"", 14 | "build-scss": "node-sass src/scss/app.scss --output-style compressed | postcss --use autoprefixer -o public/css/app.css --no-map", 15 | "compile": "npm-run-all compile-ts compile-scss", 16 | "build": "npm-run-all build-ts build-scss", 17 | "clean": "rm -f public/js/app.js public/css/app.css", 18 | "start": "npm-run-all -p watch-ts watch-scss serve" 19 | }, 20 | "dependencies": { 21 | "mithril": "^1.1.6" 22 | }, 23 | "devDependencies": { 24 | "@types/mithril": "^1.1.12", 25 | "autoprefixer": "^9.1.1", 26 | "browserify": "^16.2.2", 27 | "http-server": "^0.11.1", 28 | "node-sass": "^4.9.3", 29 | "nodemon": "^1.18.3", 30 | "npm-run-all": "^4.1.3", 31 | "postcss-cli": "^6.0.0", 32 | "tsify": "^4.0.0", 33 | "tslint": "^5.11.0", 34 | "typescript": "^3.0.1", 35 | "uglify-js": "^3.4.7", 36 | "watchify": "^3.11.0" 37 | } 38 | } 39 | --------------------------------------------------------------------------------