├── 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 |
--------------------------------------------------------------------------------