import {app} from 'https://unpkg.com/hyperapp'
import html from 'https://unpkg.com/hyperlit'

const counter = (() => {
    
    const init = x => x
          
    const increment = (x, max) => Math.min(max, x + 1)

    const decrement = (x, min) => Math.max(min, x - 1)
    
    const wire = ({
        getter,
        setter,
        onIncrement,
        onDecrement,
        min=-Infinity,
        max=Infinity,
    }) => {

        const _increment = state => setter(
            state,
            increment(getter(state), max)
        )

        const _decrement = state => setter(
            state,
            decrement(getter(state), min)
        )
        
        const _value = state => getter(state)

        const Increment = state => {
            let old = _value(state)
            state = _increment(state)
            if (_value(state) === old) return state
            else return onIncrement(state)
        }

        const Decrement = state => {
            let old = _value(state)
            state = _decrement(state)
            if(_value(state) === old) return state
            else return onDecrement(state)
        }

        return {
            increment: _increment,
            decrement: _decrement,
            value: _value,
            model: state => ({
                value: _value(state),
                Increment,
                Decrement,
            })
        }
    }

   const view = model => html`
       <span class="counter">
           <button onclick=${model.Decrement}>-</button>
           ${model.value}
           <button onclick=${model.Increment}>+</button>
       </span>`

    return {init, wire, view}
})()



const init = () => ({
    points: 8,
    strength: counter.init(1),
    dexterity: counter.init(1),
    intelligence: counter.init(1),
    charisma: counter.init(1),
})

const spendPoint = (state, orElse) =>
    !state.points
    ? orElse(state)
    : {
        ...state,
        points: state.points - 1,
    }

const returnPoint = state => ({
    ...state,
    points: state.points + 1
})

const strength = counter.wire({
    getter: state => state.strength,
    setter: (state, strength) => ({...state, strength}),
    onIncrement: state => spendPoint(state, strength.decrement),
    onDecrement: returnPoint,
    min: 1,
    max: 5,
})

const dexterity = counter.wire({
    getter: state => state.dexterity,
    setter: (state, dexterity) => ({...state, dexterity}),
    onIncrement: state => spendPoint(state, dexterity.decrement),
    onDecrement: returnPoint,
    min: 1,
    max: 5,
})

const intelligence = counter.wire({
    getter: state => state.intelligence,
    setter: (state, intelligence) => ({...state, intelligence}),
    onIncrement: state => spendPoint(state, intelligence.decrement),
    onDecrement: returnPoint,
    min: 1,
    max: 5,
})

const charisma = counter.wire({
    getter: state => state.charisma,
    setter: (state, charisma) => ({...state, charisma}),
    onIncrement: state => spendPoint(state, charisma.decrement),
    onDecrement: returnPoint,
    min: 1,
    max: 5,
})


app({
    init,
    view: state => html`
        <body>
            <p> 
                Assign points to attributes
                (<b>${state.points}</b> remaining):
            </p>
            <table border="1">
                <tr>
                    <th>STR</th>
                    <th>DEX</th>
                    <th>INT</th>
                    <th>CHA</th>
                </tr>
                <tr>
                    <td>
                        <${counter.view} ${strength.model(state)} />
                    </td>
                    <td>
                        <${counter.view} ${dexterity.model(state)} />
                    </td>
                    <td>
                        <${counter.view} ${intelligence.model(state)} />
                    </td>
                    <td>
                        <${counter.view} ${charisma.model(state)} />
                    </td>
                </tr>
            </table>
        </body>`,
    node: document.body
})

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.