import React, { useEffect, useState } from 'react'; import { Netmask } from 'netmask'; export default function Cidr() { const [ip, setIp] = useState([10, 88, 135, 144]); const [cidr, setCidr] = useState(28); const [cols] = useState(["bg-purple-400", "bg-red-400", "bg-green-400", "bg-yellow-400", "bg-slate-300"]); const [isCopied, setIsCopied] = useState(false); const [isShared, setIsShared] = useState(false); const bits = ip.map(octet => Array.from({ length: 8 }, (_, i) => (octet >> (7 - i)) & 1)); const parseOctet = (val: string, max: number) => { const num = Number(val); if (isNaN(num) || num < 0) return 0; if (num > max) return max; return num; } const setIpOctet = (i: number, val: number) => { const newIp = [...ip]; newIp[i] = val; setIp(newIp); } const handleWheel = (event: React.WheelEvent, i: number, max: number) => { event.preventDefault(); const min = 0; const target = event.currentTarget as HTMLInputElement; let value = parseInt(target.value); if (event.deltaY > 0 && value > min) { value -= 1; } if (event.deltaY < 0 && value < max) { value += 1; } if (i == 4) { setCidr(value); } else { setIpOctet(i, value); } } const handleKeyDown = (event: React.KeyboardEvent, i: number, max: number) => { const min = 0; const target = event.currentTarget as HTMLInputElement; let value = parseInt(target.value); if (event.key === "ArrowDown" && value > min) { value -= 1; } else if (event.key === "ArrowUp" && value < max) { value += 1; } else if (event.key === '.') { event.preventDefault(); const parent = (event.target as Node).parentNode; const next = parent?.nextSibling?.firstChild; if (next instanceof HTMLInputElement) { next.select(); next.focus(); } } else if (event.key === "/") { event.preventDefault(); const parent = (event.target as Node).parentNode; const mask = parent?.nextSibling; if (mask instanceof HTMLInputElement) { mask.select(); mask.focus(); } } if (i == 4) { setCidr(value); } else { setIpOctet(i, value); } } const updateCidrString = (val: string) => { const parts = val.split("/"); const ip = parts[0].split(".").map(Number); const cidr = Number(parts[1]); if (ip.length != 4) { return } ip.forEach(octet => { if (Number.isNaN(octet) || octet < 0 || octet > 255) { return } }) setIp(ip); if (Number.isNaN(cidr) || cidr < 0 || cidr > 32) { return } setCidr(cidr); } const handlePaste = (event: React.ClipboardEvent) => { event.preventDefault(); if (event.clipboardData === null) return; const text = event.clipboardData.getData('Text'); updateCidrString(text); } const handleCopy = async () => { await navigator.clipboard.writeText(pretty); setIsCopied(true); setTimeout(() => setIsCopied(false), 2000); }; const handleShare = async () => { const frag = "#" + ip.join(".") + "/" + cidr; window.location.hash = frag; await navigator.clipboard.writeText(window.location.toString()); setIsShared(true); setTimeout(() => setIsShared(false), 2000); }; useEffect(() => { const fragment = window.location.hash.slice(1); if (fragment) { updateCidrString(fragment); } }, []); const pretty = ip.join('.') + '/' + cidr; const netmask = new Netmask(pretty); const details = { "Netmask": netmask.mask, "CIDR Base IP": netmask.base, "Broadcast IP": netmask.broadcast || "None", "Count": netmask.size.toLocaleString(), "First Usable IP": netmask.first, "Last Usable IP": netmask.last } return (
{ip.map((octet, i) => (
setIpOctet(i, parseOctet(e.target.value, 255))} onWheel={(e) => handleWheel(e, i, 255)} onKeyDown={(e) => handleKeyDown(e, i, 255)} onPaste={handlePaste} className={`w-20 h-20 text-3xl text-center rounded-md ${cols[i]}`} maxLength={3} aria-label={`Octet ${i + 1}`} /> {i == 3 ? "/" : "."}
))} setCidr(parseOctet(e.target.value, 32))} onWheel={(e) => handleWheel(e, 4, 32)} onKeyDown={(e) => handleKeyDown(e, 4, 32)} onPaste={handlePaste} className={`w-20 h-20 text-3xl text-center rounded-md ${cols[4]}`} maxLength={2} aria-label={`Network bits`} />
{bits.map((octet, i) => {octet.map((bit, j) => {bit})} )}
{Object.entries(details).map(([label, val]) =>
{val}
{label}
) }
); }