Skip to content

Commit 8593ebd

Browse files
committed
Visualize network overview and details for CIDR
1 parent c680b13 commit 8593ebd

File tree

5 files changed

+234
-3
lines changed

5 files changed

+234
-3
lines changed

src/components/CIDR.svelte

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,17 @@ Visualize CIDR
6565
<IPv4Chart
6666
{min}
6767
{max}
68-
{address}
68+
{length}
69+
{prefix}
70+
bind:address
71+
{network}
72+
{mask}
6973
{broadcast}
7074
{hosts}
7175
{renderedHosts}
7276
{renderedAddress}
7377
{renderedMin}
7478
{renderedMax}
7579
{renderedBroadcast}
80+
{renderedNetwork}
7681
/>

src/components/blend/IPv4Network.svelte

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ to augment IPv4 with Network and Netmask visualization
66
import type { BitsIntegerUpdateEvent } from '../parts/Bits.svelte';
77
import Bits from '../parts/Bits.svelte';
88
import { getBaseLog } from '@lib/math';
9+
import { newAddressFrom } from '@lib/ipv4';
910
export let length = 32;
1011
export let address = 0xffffffff;
1112
export let prefix = 0xffffffff;
@@ -15,7 +16,7 @@ to augment IPv4 with Network and Netmask visualization
1516
export let renderedMask = 'placeholder';
1617
1718
function onUpdateForNetwork({ detail: { integer } }: CustomEvent<BitsIntegerUpdateEvent>) {
18-
address = ((address & ~mask) >>> 0) + integer;
19+
address = newAddressFrom(address, mask, integer);
1920
}
2021
2122
const minExponent = (n: number, mask: number, prefix: number) => {

src/components/parts/IPv4Chart.svelte

Lines changed: 188 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,19 @@
11
<!-- @component
22
Visualize various IPv4 related info
33
-->
4+
<script lang="ts" context="module">
5+
const totalNetworkSize = Math.pow(2, 32);
6+
</script>
7+
48
<script lang="ts">
9+
import { newAddressFrom } from '@lib/ipv4';
10+
import PartialTransition from './PartialTransition.svelte';
11+
12+
export let length = 32;
13+
export let prefix = 0;
514
export let address = 0;
15+
export let network = 0;
16+
export let mask = 0;
617
export let min = 0;
718
export let max = 0;
819
export let broadcast = 0;
@@ -12,11 +23,187 @@ Visualize various IPv4 related info
1223
export let renderedMax = 'placeholder';
1324
export let renderedBroadcast = 'placeholder';
1425
export let renderedHosts = 'placeholder';
26+
export let renderedNetwork = 'placeholder';
27+
$: renderedNetworkWithouPrefix = renderedNetwork.split('/')[0];
28+
29+
$: addressCount = hosts + 2;
30+
$: networkStart = min - 1;
31+
32+
/* for overview */
33+
const pointOfTooSmall = 7;
34+
$: levels = length - 1 - pointOfTooSmall;
35+
$: level = prefix - pointOfTooSmall;
36+
$: opacity = prefix < pointOfTooSmall + 1 ? 1 : ((levels - level) / levels) * 0.5 + 0.5;
37+
$: opacityInPercent = Math.round(opacity * 100);
38+
39+
$: startInPercent = Math.ceil((networkStart / totalNetworkSize) * 10000) / 100;
40+
$: sizeInPercent = (addressCount / totalNetworkSize) * 100;
41+
$: sizeInPercentWithMin = Math.floor(Math.max(1, sizeInPercent) * 100) / 100;
42+
/* end of overview */
43+
44+
/* for detail */
45+
$: opacityForDetail = ((length - prefix) / length) * 0.75 + 0.25;
46+
$: opacityForDetailInPercent = Math.round(opacityForDetail * 100);
47+
48+
$: beforeIP = Math.ceil(((address - networkStart) / addressCount) * 10000) / 100;
49+
$: relativeIPSize = (1 / addressCount) * 100;
50+
$: relativeIPSizeWithMin = Math.floor(Math.max(1, relativeIPSize) * 100) / 100;
51+
/* end of detail */
1552
1653
$: hostsValid = hosts > 0;
54+
55+
$: networkUnit = Math.pow(2, length - prefix);
56+
57+
$: isAddressNetwork = network === address;
58+
$: isAddressBroadcast = broadcast === address;
59+
$: isAddressHost = !isAddressNetwork && !isAddressBroadcast;
1760
</script>
1861

19-
<div class="flex">
62+
<div class="flex mt-5">
63+
<div class="grow" />
64+
<div class="w-full sm:w-[94%]">
65+
<!-- overview -->
66+
<div>
67+
<div class="flex text-center text-sm text-gray items-end">
68+
<div class="flex-1 text-left text-xs">0.0.0.0</div>
69+
<div class="flex-1 text-left">64.0.0.0</div>
70+
<div class="flex-1">128.0.0.0</div>
71+
<div class="flex-1 text-right">192.0.0.0</div>
72+
<div class="flex-1 text-right text-xs">255.255.255.255</div>
73+
</div>
74+
<table class="h-3 w-full">
75+
<tr>
76+
{#each [1, 2, 3, 4] as idx}
77+
<td data-position={idx} class="border-x-1 border-gray-300" />
78+
{/each}
79+
</tr>
80+
</table>
81+
</div>
82+
<div class="h-2 w-full flex bg-gray-100">
83+
<div class="h-full" style:width={`${startInPercent}%`} />
84+
<div
85+
class="h-full bg-cyan rounded"
86+
style:width={`${sizeInPercentWithMin}%`}
87+
style:--un-bg-opacity={`${opacityInPercent}%`}
88+
/>
89+
<div class="h-full grow" />
90+
</div>
91+
<!-- detail -->
92+
<div class="w-full flex text-cyan items-center mt-2">
93+
<div class="min-w-6" style:width={`${startInPercent}%`}>
94+
<button
95+
class="i-bi-arrow-bar-left"
96+
on:click={() => {
97+
address = newAddressFrom(address, mask, network - networkUnit);
98+
}}
99+
/>
100+
</div>
101+
102+
<div class="flex items-center" class:flex-row-reverse={startInPercent > 70}>
103+
<div class="flex items-center">
104+
<div class="i-bi-arrow-down ml-1" />
105+
<div class="i-bi-zoom-in mr-1" />
106+
<div class="text-sm">
107+
<PartialTransition notation={renderedNetwork} />
108+
</div>
109+
</div>
110+
<div class="mx-2 text-xs flex">
111+
(<PartialTransition notation={addressCount.toLocaleString()} />)
112+
</div>
113+
</div>
114+
<div class="grow flex ml-2">
115+
<div class="grow" />
116+
<button
117+
class="i-bi-arrow-bar-right"
118+
on:click={() => {
119+
address = newAddressFrom(address, mask, network + networkUnit);
120+
}}
121+
/>
122+
</div>
123+
</div>
124+
<div
125+
class="mt-2 h-2 w-full rounded bg-cyan flex"
126+
style:--un-bg-opacity={`${opacityForDetailInPercent}%`}
127+
>
128+
<div class="h-full" style:width={`${beforeIP}%`} />
129+
<div class="h-full bg-lime-800 rounded" style:width={`${relativeIPSizeWithMin}%`} />
130+
<div class="h-full grow" />
131+
</div>
132+
<div>
133+
<table class="min-h-3 w-full">
134+
<tr>
135+
{#each [1] as idx}
136+
<!-- safe list class="!hidden" -->
137+
<td
138+
data-position={idx}
139+
class="px-1 text-[0.6rem]/1 text-gray border-x-1 border-gray-300"
140+
>
141+
<div class="flex">
142+
<div
143+
class="flex-1"
144+
class:!hidden={!hostsValid}
145+
class:font-bold={isAddressNetwork}
146+
class:text-black={isAddressNetwork}
147+
>
148+
Network
149+
</div>
150+
<div class="grow" />
151+
<!-- safe list class="text-gray-700" -->
152+
<div class:text-gray-700={isAddressHost}>
153+
<span class:hidden={!hostsValid}> {renderedMin} - {renderedMax} =</span>
154+
{prefix < 31 ? renderedHosts : addressCount.toLocaleString()} hosts
155+
</div>
156+
<div class="grow" />
157+
<div
158+
class="flex-1 text-right"
159+
class:!hidden={!hostsValid}
160+
class:font-bold={isAddressBroadcast}
161+
class:text-black={isAddressBroadcast}
162+
>
163+
Broadcast
164+
</div>
165+
</div>
166+
</td>
167+
{/each}
168+
</tr>
169+
</table>
170+
<div class="flex text-center">
171+
<div class="flex-1 text-left text-xs text-gray">
172+
<PartialTransition notation={renderedNetworkWithouPrefix} />
173+
</div>
174+
<div class="flex-1 text-center flex text-lime-800 items-center">
175+
<div class="grow" />
176+
<button
177+
class="i-bi-arrow-bar-left mx-1"
178+
on:click={() => {
179+
address = address - 1;
180+
}}
181+
/>
182+
<div class="w-32 flex text-sm">
183+
<div class="grow" />
184+
<PartialTransition notation={renderedAddress} />
185+
<div class="grow" />
186+
</div>
187+
<button
188+
class="i-bi-arrow-bar-right mx-1"
189+
on:click={() => {
190+
address = address + 1;
191+
}}
192+
/>
193+
<div class="grow" />
194+
</div>
195+
<div class="flex-1 text-right text-xs text-gray flex">
196+
<div class="grow" />
197+
<PartialTransition notation={renderedBroadcast} />
198+
</div>
199+
</div>
200+
</div>
201+
</div>
202+
<div class="grow" />
203+
</div>
204+
205+
<!-- TODO: remove this -->
206+
<div class="flex !hidden">
20207
<div class="grow" />
21208
<ul class="mx-3 my-1 min-w-64">
22209
<li class="list-disc">
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<!-- @component
2+
To apply partial fade transition currently for IPv4 Addresses
3+
-->
4+
<script lang="ts">
5+
import type { TransitionConfig } from 'svelte/transition';
6+
import { fade } from 'svelte/transition';
7+
8+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
9+
export let transition: (...args: any[]) => TransitionConfig = fade;
10+
export let mono = true;
11+
12+
export let notation = '0.0.0.0/0';
13+
$: splits = notation.split('/');
14+
$: address = splits[0];
15+
$: chunks = address.split('.');
16+
$: prefix = splits[1];
17+
$: texts = prefix ? [...chunks, prefix] : chunks;
18+
</script>
19+
20+
<!-- safe list class="font-mono" -->
21+
<div class="flex" class:font-mono={mono}>
22+
{#each texts as text, idx}
23+
<div class="first:hidden">{idx < 4 ? '.' : '/'}</div>
24+
<div class="relative">
25+
<div class="opacity-0">
26+
{text}
27+
</div>
28+
{#key text}
29+
<div class="absolute left-0 top-0" transition:transition>
30+
{text}
31+
</div>
32+
{/key}
33+
</div>
34+
{/each}
35+
</div>

src/lib/ipv4.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,6 @@ export const calcIP = (address: number, prefix: number): CalcIPResult => {
9696
};
9797
return result;
9898
};
99+
100+
export const newAddressFrom = (address: number, mask: number, network: number) =>
101+
((address & ~mask) >>> 0) + ((network & mask) >>> 0);

0 commit comments

Comments
 (0)