JSFiddle - React, Tailwind, and code Playground

by khamer

HTML

<div id="cidr">
  <input type="text" v-model="shorthand" placeholder="xxx.xxx.xxx.xxx">
  <div class="bits">
      <span class="byte" v-for="byte, j in bytes">
          <span class="bit" v-for="bit, i in byte" v-text="bit" :class="{inMask: (i + j*8) < mask}">
          </span>
      </span>
  </div>  
  <ul class="explanation">
      <octet-mask-explanation v-if="mask < 8" :mask="mask" :bits="octet1" adj="first"></octet-mask-explanation>
      <li v-if="mask >= 8">
          Since the subnet mask is 8 or more, we know the first 8 bits must be exactly the same. In this case, that's
          <binary-number :bits="octet1"></binary-number>.
      </li>
      <octet-mask-explanation v-if="mask > 8 && mask < 16" :mask="mask - 8" :bits="octet2" adj="second"></octet-mask-explanation>
      <li v-if="mask >= 16">
          Since the subnet mask is 16 or more, we know the second 8 bits must be exactly the same. In this case, that's
          <binary-number :bits="octet2"></binary-number>.
      </li>
      <octet-mask-explanation v-if="mask > 16 && mask < 24" :mask="mask - 16" :bits="octet3" adj="third"></octet-mask-explanation>
      <li v-if="mask >= 24">
          Since the subnet mask is 24 or more, we know the third 8 bits must be exactly the same. In this case, that's
          <binary-number :bits="octet3"></binary-number>.
      </li>
      <octet-mask-explanation v-if="mask > 24 && mask < 32" :mask="mask - 24" :bits="octet4" adj="fourth"></octet-mask-explanation>
      <li v-if="mask == 32">
          With a 32 bit mask, all 32 bits must be the same, and we know that the only matching IP is <strong v-text="ip"></strong>.
      </li>
  </ul>
</div>

<script type="text/tmpl" id="octet-mask">
	<li>
		Only the first {{ niceBits }} of the {{ adj }} octet are masked, which means the {{ adj }} octet can go
        from <binary-number :bits="min"></binary-number> to <binary-number :bits="max"></binary-number>.
    </li>
</script>

SCSS

html, body {
    background-color: #fff;
}

.bits {
    padding: 1em;
}

.byte {
    border: 1px solid #ddd;
    padding: .25em;
    font-family: monospace;
    font-size: 2em;
}

.inMask {
    color: green;
}

.number {
    background-color: #f4f4f4;
    padding-left: .25em;
    padding-right: .25em;
}

.binary {
    font-family: monospace;
}

JavaScript

(function() {

	let toBits = function(n) {
    	let digits = (n >>> 0).toString(2);
        return ("00000000".substr(digits.length) + digits);
    };
    
    Vue.component('binary-number', {
    	template: `<span class="number"><span class="binary" v-text="bits" /> (<span v-text="decimal" />)</span>`,
        props: ['bits'],
        computed: {
        	decimal() {
           		return parseInt(this.bits, 2);
			},
        },
    });
    
    Vue.component('octet-mask-explanation', {
    	template: '#octet-mask',
        props: ['mask', 'bits', 'adj'],
        computed: {
        	niceBits() {
            	return this.mask + " " + (this.bits.length == 1 ? "bit" : "bits");
            },
            min() {
            	var minbits = this.bits.slice(0, this.mask);
                return minbits + "00000000".slice(minbits.length);
            },
            max() {
            	var maxbits = this.bits.slice(0, this.mask);
                return maxbits + "11111111".slice(maxbits.length);
            },
        }
    });
    
    new Vue({
        el: "#cidr",
        data: {
            shorthand: "192.168.10.1/22",
        },
        computed: {
        	ip() {
            	return this.shorthand.replace(/\/.*$/, "");
            },
            mask() {
            	return this.shorthand.replace(/^.*\//, "");
            },
        	bytes() {
            	return this.ip.split(".").map(toBits);
            },
            
            octet1() {
            	return this.bytes[0];
            },
            octet2() {
            	return this.bytes[1];
            },
            octet3() {
            	return this.bytes[2];
            },
            octet4() {
            	return this.bytes[3];
            },
        },
    });
})();