JSFiddle - React, Tailwind, and code Playground

by Lloyd Atkinson

HTML

<div id="app" v-cloak>
<div class="option-timing-component">
        <div>
            
        </div>
        <table>
            <thead>
                <tr>

                    <!-- <th
                        v-for="(timeStep, index) in timeStepVariationCalculation(timeStepStart, timeStepIncrement, timeStepCount)"
                        v-bind:key="timeStep">
                        {{ timeStep }}</th> -->
                        <th><input type="number" v-model.number="timeStepStart" /></th>
                        <th v-for="(step, index) in timeSteps" v-if="index > 0">
                            {{ timeStepStart + (index * timeStepIncrement  )}}
                        </th>
                </tr>
            </thead>
            <tbody>
                <tr>
                    <td
                        v-for="(item, index) in timeSteps"
                        v-bind:key="index">
                        <div
                            v-bind:class="{ selected: item.selected }"
                            v-on:click="toggleSelectedItem(index)">
                            <!-- Don't store state in DOM. Vue does not and will not support hidden fields. -->
                            <!-- This is good. However we are using MVC so need to get this mixture of state management over to the controller. This would all be better with a SPA and/or API behind it, so no hidden field hacks required. -->
                            <input type="text" :value="item.selected">
                        </div>
                    </td>
                </tr>
            </tbody>
        </table>
        <div>
            <p>{{ selectedTimeSteps.length }} selected timeSteps:</p>
            <ul>
                <li v-for="(item, index) in timeSteps" v-bind:key="index">{{ timeSteps[index].selected }}</li>
            </ul>
        </div>
        <button v-on:click="reactiveTest()">Change timestep start</button>
    </div>
</div>

SCSS

.option-timing-component {
    [v-cloak] { display:none; }

    $width: 60px;
    
    * {
        box-sizing: border-box;  
    }
    
    .selected {
        background-color: #5cb85c;
    }
    
    td div {
        width: $width;
        height: 30px;
        background-color: #ddd;
        border: 1px solid transparent;
        border-color: #767676;
    }
    
    input[type="number"] {
        width: $width;
    }

    // See comment about doing state management properly; we shouldn't need to do this.
    input[type="text"] {
        display: none !important; // Make sure to prevent something accidentally overriding.
    }
}

Babel + JSX

// @ts-check

let temporaryViewModel = {
    timeStepStart: 2000,
    timeStepIncrement: 5,
    optionTimings: [
        false,
        true,
        false,
        false,
        true,
        false,
        true,
        true,
        true,
        false,
        true
    ]
};

new Vue({
    el: '#app',
    data () {
        return {
            timeSteps: [],
            timeStepStart: 0,
            timeStepIncrement: 0,
            timeStepCount: 0
        }
    },
    computed: {
        selectedTimeSteps () {
            return this.timeSteps.filter(timestep => timestep.selected);
        }
    },
    methods: {
        toggleSelectedItem (timestepIndex) {
            this.timeSteps[timestepIndex].selected = !this.timeSteps[timestepIndex].selected;
        },
        timeStepVariationCalculation (initialTimeStep, variation, numberOfTimeSteps) {
            let timeSteps = [];
            for (let i = 0; i < numberOfTimeSteps; i++) {
                timeSteps.push(initialTimeStep + i * variation);
            }
            return timeSteps;
        },
        reactiveTest () {
            this.timeStepStart = 2000;
        }
    },
    created () {      
        this.timeStepStart = temporaryViewModel.timeStepStart;
        this.timeStepIncrement = temporaryViewModel.timeStepIncrement;
        this.timeStepCount = temporaryViewModel.optionTimings.length;
        
        var _this = this; // Workaround for not being able to use arrow functions in legacy browsers.
        this.timeSteps = temporaryViewModel.optionTimings.map((x, index, array) => {
            return {
                selected: x
                // value: this.timeStepVariationCalculation(this.timeStepStart, this.timeStepIncrement, array.length)[index]
            }
        });
        
        this.timestepYearStart = temporaryViewModel.timestepYearStart;
        this.timestepIncrementValue = temporaryViewModel.timestepIncrementValue;
    }
});