/* eslint-disable @typescript-eslint/no-unnecessary-condition */ import React from "react"; import { TextInput } from "./TextInput"; export function IPInput(p: { label: string; editable: boolean; value?: string; onValueChange?: (newVal: string | undefined) => void; version: 4 | 6; }): React.ReactElement { const { onValueChange, ...props } = p; return ( { onValueChange?.(p.version === 4 ? sanitizeIpV4(v) : sanitizeIpV6(v)); }} {...props} /> ); } export function IPInputWithMask(p: { label: string; editable: boolean; ipAndMask?: string; ip?: string; mask?: number; onValueChange?: (ip?: string, mask?: number, ipAndMask?: string) => void; version: 4 | 6; }): React.ReactElement { const showSlash = React.useRef(!!p.mask); const currValue = p.ipAndMask ?? `${p.ip ?? ""}${p.mask || showSlash.current ? "/" : ""}${p.mask ?? ""}`; const { onValueChange, ...props } = p; return ( { showSlash.current = false; if (!v) { onValueChange?.(undefined, undefined, undefined); return; } const split = v.split("/"); const ip = p.version === 4 ? sanitizeIpV4(split[0]) : sanitizeIpV6(split[0]); let mask = undefined; if (split.length > 1) { showSlash.current = true; mask = sanitizeMask(p.version, split[1]); } onValueChange?.( ip, mask, mask || showSlash.current ? `${ip}/${mask ?? ""}` : ip ); }} value={currValue} {...props} /> ); } function sanitizeIpV4(s: string | undefined): string | undefined { if (s === "" || s === undefined) return s; const split = s.split("."); if (split.length > 4) split.splice(4); let needAnotherIteration = false; const res = split .map((v) => { if (v === "") return ""; const num = Number(v); if (isNaN(num) || num < 0) return "0"; if (num > 255) { needAnotherIteration = true; return v.slice(0, 2) + "." + v.slice(2); } return num.toString(); }) .join("."); return needAnotherIteration ? sanitizeIpV4(res) : res; } function sanitizeIpV6(s: string | undefined): string | undefined { if (s === "" || s === undefined) return s; const split = s.split(":"); if (split.length > 8) split.splice(8); let needAnotherIteration = false; let res = split .map((e) => { if (e === "") return e; const num = parseInt(e, 16); if (isNaN(num)) return "0"; const s = num.toString(16); if (num > 0xffff) { needAnotherIteration = true; return s.slice(0, 4) + ":" + s.slice(4); } return s; }) .join(":"); const firstIndex = res.indexOf("::"); let nextIndex = res.lastIndexOf("::"); while (nextIndex !== firstIndex) { res = res.slice(0, nextIndex) + res.slice(nextIndex + 1); nextIndex = res.lastIndexOf("::"); } return needAnotherIteration ? sanitizeIpV6(res) : res; } function sanitizeMask(version: 4 | 6, mask?: string): number | undefined { if (!mask) return undefined; const value = Math.floor(Number(mask)); if (version === 4) { return value < 0 || value > 32 ? 32 : value; } else { return value < 0 || value > 128 ? 128 : value; } }