Classを使用してHyperappを書いてみた

`state`、`actions`、`view` を Class で書いておけば再利用も楽になりそうと思ってやってみた。多分いい感じ。

Hyperapp用のクラスをHyperclassって呼びたい。

```html:hyperclass.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
  </head>
  <body>
    <script type="module">

      import { h, app as mount} from 'https://unpkg.com/hyperapp?module'

      class Hyperclass {
        constructor() {
          this.state = {}
          this.actions = {}
          this.view = (state, actions) => h("h1", {}, "Hyperclass")
        }
        appendTo(container) {
          return mount(this.state, this.actions, this.view, container)
        }
        assign(namespace, component) {
          this[namespace] = { view: component.view }
          this.state[namespace] = component.state
          this.actions[namespace] = component.actions
        }
      }

      class Counter {
        constructor(classname='counter') {
          this.state = {
            count: 0,
          }
          this.actions = {
            up  : (by) => (state, actions) => ({count: state.count+by}),
            down: (by) => (state, actions) => ({count: state.count-by}),
          }
          this.view = (state, actions) => h("div", {class:classname}, [
            h("h1", {}, state.count),
            h("button", {onclick:()=>actions.up(1)}, '+1'),
            h("button", {onclick:()=>actions.down(1)}, '-1'),
          ])
        }
      }

      class TwinCounter extends Hyperclass {
        constructor(cls='twinCounter') {
          super()
          this.assign('c1', new Counter())
          this.assign('c2', new Counter())
          Object.assign(this.actions, {
            twin: _ => (s,a) => {
              a.c1.up(1)
              a.c2.down(1)
            },
          })
          const tree = c => h("div",{class:cls},c)
          const button = t => (s,a) => h("button",{onclick:a.twin},t)
          const view1 = _ => (s,a) => this.c1.view(s.c1,a.c1)
          const view2 = _ => (s,a) => this.c2.view(s.c2,a.c2)
          this.view = (s,a) => tree([
            button("twin action"),
            view1(),
            view2(),
          ])
        }
      }

      const component = new TwinCounter()

      const widget = component.appendTo(document.body)

      for(let msec=0; msec<10000; msec++) setTimeout(widget.twin, msec)

    </script>
  </body>
</html>
```

追記：次期Hyperappでこんな風に書けるといいな。自分でもチャレンジしてみたい。

```js:hyperclass-example.js
import Hyperclass from './hyperclass.js'

class Counter extends Hyperclass {
  constructor(classname='counter') {
    this.classname = classname
    this.count = 0
  }
  up(by) {
    //this.count += by
    return self => ({count: self.count + by})
  }
  down(by) {
    //this.count -= by
    return self => ({count: self.count - by})
  }
  view(self) {
    return h("div", {class:self.classname}, [
      h("h1", {}, self.count),
      h("button", {onclick:()=>self.up(1)}, '+1'),
      h("button", {onclick:()=>self.down(1)}, '-1'),
    ])
  }
}

class TwinCounter extends Hyperclass {
  constructor(classname='twincounter') {
    this.classname = classname
    this.counter1 = new Counter()
    this.counter2 = new Counter()
  }
  twin() {
    return self => {
      self.counter1.up(1)
      self.counter2.down(1)
    }
  }
  view(self) {
    return h("div", {class:self.classname}, [
      h("button", {onclick:()=>self.twin()}, "twin action"),
      self.counter1.view(self.counter1),
      self.counter1.view(self.counter2),
    ])
  }
}

new TwinCounter().appendTo(document.body)
```