Hero Globe Interactive Nodes
blockheroGlobal edge latency simulation hero with interactive PoP node inspection and real-time telemetry.
Installation
$pnpm dlx uipkge-ng@latest add hero-globe-interactive-nodes$npx uipkge-ng@latest add hero-globe-interactive-nodes$yarn dlx uipkge-ng@latest add hero-globe-interactive-nodes$bunx uipkge-ng@latest add hero-globe-interactive-nodesManifest:
https://uipkge.dev/r/angular/hero-globe-interactive-nodes.jsonInstalls to:src/app/components/blocks/hero-globe-interactive-nodes/Variants
Loading interactive previews…
Loading interactive previews…
Schema
Type aliases exported from this item's source. Use these to shape the data you pass in.
EdgeNodeinterface EdgeNode {
id: string
name: string
region: string
country: string
city: string
latencyMs: number
p99LatencyMs: number
cacheHitRatio: number
qps: number
status: 'optimal' | 'operational' | 'degraded'
coordinates: { x: number; y: number }
}npm dependencies
Files installed (2)
src/app/components/blocks/hero-globe-interactive-nodes/hero-globe-interactive-nodes.component.ts15.1 kB
// Global edge latency simulation hero with interactive PoP node inspection and // real-time telemetry. Port of the React block 1:1. import { ChangeDetectionStrategy, Component, Input, computed, signal } from '@angular/core' import { Activity, ArrowRight, Check, Copy, Globe, LucideAngularModule, Radio, Server, ShieldCheck, Terminal, Zap, } from 'lucide-angular' import { cn } from '@/lib/utils' import { UiBadgeComponent } from '@/ui/badge/badge.component' import { UiButtonComponent } from '@/ui/button/button.component' import { UiCardComponent, UiCardContentComponent } from '@/ui/card/card.component' export interface EdgeNode { id: string name: string region: string country: string city: string latencyMs: number p99LatencyMs: number cacheHitRatio: number qps: number status: 'optimal' | 'operational' | 'degraded' coordinates: { x: number; y: number } } const DEFAULT_NODES: EdgeNode[] = [ { id: 'node-iad', name: 'iad-1', region: 'us-east-1', country: 'United States', city: 'Washington, D.C.', latencyMs: 8, p99LatencyMs: 14, cacheHitRatio: 99.4, qps: 18450, status: 'optimal', coordinates: { x: 28, y: 38 }, }, { id: 'node-sfo', name: 'sfo-1', region: 'us-west-1', country: 'United States', city: 'San Francisco', latencyMs: 12, p99LatencyMs: 22, cacheHitRatio: 99.1, qps: 14200, status: 'optimal', coordinates: { x: 18, y: 40 }, }, { id: 'node-fra', name: 'fra-1', region: 'eu-central-1', country: 'Germany', city: 'Frankfurt', latencyMs: 15, p99LatencyMs: 27, cacheHitRatio: 98.9, qps: 21300, status: 'optimal', coordinates: { x: 52, y: 32 }, }, { id: 'node-sin', name: 'sin-1', region: 'ap-southeast-1', country: 'Singapore', city: 'Singapore', latencyMs: 24, p99LatencyMs: 41, cacheHitRatio: 98.6, qps: 16800, status: 'optimal', coordinates: { x: 76, y: 58 }, }, { id: 'node-nrt', name: 'nrt-1', region: 'ap-northeast-1', country: 'Japan', city: 'Tokyo', latencyMs: 19, p99LatencyMs: 34, cacheHitRatio: 99.2, qps: 19400, status: 'optimal', coordinates: { x: 84, y: 42 }, }, { id: 'node-syd', name: 'syd-1', region: 'ap-southeast-2', country: 'Australia', city: 'Sydney', latencyMs: 32, p99LatencyMs: 52, cacheHitRatio: 98.2, qps: 9800, status: 'optimal', coordinates: { x: 88, y: 78 }, }, { id: 'node-gru', name: 'gru-1', region: 'sa-east-1', country: 'Brazil', city: 'São Paulo', latencyMs: 44, p99LatencyMs: 68, cacheHitRatio: 97.9, qps: 7600, status: 'optimal', coordinates: { x: 36, y: 74 }, }, { id: 'node-lhr', name: 'lhr-1', region: 'eu-west-2', country: 'United Kingdom', city: 'London', latencyMs: 11, p99LatencyMs: 20, cacheHitRatio: 99.5, qps: 24600, status: 'optimal', coordinates: { x: 48, y: 30 }, }, ] @Component({ changeDetection: ChangeDetectionStrategy.Eager, selector: 'ui-hero-globe-interactive-nodes, [ui-hero-globe-interactive-nodes]', standalone: true, // React renders the root <section> itself: the host stays out of layout and `class` goes to the root. host: { '[attr.class]': '"contents"' }, imports: [LucideAngularModule, UiBadgeComponent, UiButtonComponent, UiCardComponent, UiCardContentComponent], template: ` <section data-slot="hero-globe-interactive-nodes" [class]="rootClass"> <div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8"> <div class="grid grid-cols-1 gap-12 lg:grid-cols-12 lg:items-center"> <!-- Left: Copy & Actions (6 Cols) --> <div class="space-y-6 lg:col-span-6"> <div class="border-primary/20 bg-primary/10 text-primary inline-flex items-center gap-2 rounded-full border px-3 py-1 text-xs font-medium shadow-2xs" > <lucide-icon [img]="Radio" class="text-primary size-3.5 animate-pulse" /> <span>Global Edge CDN Mesh Active • 310+ PoPs</span> </div> <h1 class="text-foreground text-3xl font-bold tracking-tight sm:text-4xl lg:text-5xl"> {{ title }} </h1> <p class="text-muted-foreground text-base sm:text-lg"> {{ description }} </p> <!-- CLI Command Sandbox --> <div class="flex flex-col gap-3 sm:flex-row sm:items-center"> <div class="border-border bg-muted/60 text-foreground flex items-center justify-between rounded-lg border px-3 py-2 font-mono text-xs shadow-2xs sm:w-80" > <div class="flex items-center gap-2 truncate"> <lucide-icon [img]="Terminal" class="text-muted-foreground size-3.5 shrink-0" /> <span class="truncate">{{ cliCommand }}</span> </div> <button ui-button variant="ghost" size="icon" type="button" class="text-muted-foreground hover:text-foreground size-7 shrink-0" aria-label="Copy CLI command" (click)="copyCommand()" > @if (copied()) { <lucide-icon [img]="Check" class="text-primary size-3.5" /> } @else { <lucide-icon [img]="Copy" class="size-3.5" /> } </button> </div> <button ui-button size="default" type="button" class="gap-1.5 shadow-xs"> <span>Explore Registry</span> <lucide-icon [img]="ArrowRight" class="size-4" /> </button> </div> <!-- Quick Telemetry Strip --> <div class="border-border grid grid-cols-3 gap-4 border-t pt-6"> <div class="space-y-1"> <div class="text-muted-foreground text-xs font-medium">Global Avg Ping</div> <div class="text-foreground text-xl font-bold tracking-tight">{{ avgGlobalLatency() }}ms</div> <div class="text-success text-xs font-medium">−42% vs origin</div> </div> <div class="space-y-1"> <div class="text-muted-foreground text-xs font-medium">Network Throughput</div> <div class="text-foreground text-xl font-bold tracking-tight">{{ totalGlobalQps() }}</div> <div class="text-muted-foreground text-xs">req/sec live</div> </div> <div class="space-y-1"> <div class="text-muted-foreground text-xs font-medium">Edge Availability</div> <div class="text-foreground text-xl font-bold tracking-tight">99.95%</div> <div class="text-success text-xs font-medium">Enterprise SLA</div> </div> </div> </div> <!-- Right: Interactive SVG Node Map & Telemetry Workbench (6 Cols) --> <div class="space-y-4 lg:col-span-6"> <div ui-card class="border-border bg-card shadow-sm"> <div ui-card-content class="space-y-4 p-4 sm:p-6"> <!-- Top bar: active node switch --> <div class="border-border flex items-center justify-between border-b pb-3"> <div class="flex items-center gap-2"> <lucide-icon [img]="Globe" class="text-primary size-4" /> <span class="text-foreground text-xs font-semibold">Active Edge Inspection</span> </div> <span ui-badge variant="outline" class="border-border text-muted-foreground font-mono text-xs"> {{ selectedNode().city }} ({{ selectedNode().name }}) </span> </div> <!-- Interactive Node Grid / Map Canvas --> <div class="border-border/80 bg-muted/30 relative h-60 w-full overflow-hidden rounded-lg border p-3"> <!-- SVG Connections Network Layer --> <svg class="stroke-border/70 absolute inset-0 size-full" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" > @for (n of activeNodes().slice(1); track n.id) { <line [attr.x1]="hubNode().coordinates.x + '%'" [attr.y1]="hubNode().coordinates.y + '%'" [attr.x2]="n.coordinates.x + '%'" [attr.y2]="n.coordinates.y + '%'" stroke-dasharray="3 3" stroke-width="1" /> } </svg> <!-- Interactive Region Nodes --> @for (node of activeNodes(); track node.id) { <button type="button" [style.left]="node.coordinates.x + '%'" [style.top]="node.coordinates.y + '%'" [class]="nodeButtonClass(node.id)" [attr.aria-label]="'Inspect ' + node.name + ' in ' + node.city" (click)="selectNode(node.id)" > <span class="font-mono text-xs leading-none font-bold"> {{ selectedNodeId() === node.id ? node.name.slice(0, 3).toUpperCase() : '' }} </span> @if (selectedNodeId() !== node.id) { <span class="bg-success size-2 rounded-full"></span> } </button> } </div> <!-- Edge Node Telemetry Card --> <div class="border-border bg-background grid grid-cols-2 gap-2.5 rounded-lg border p-3 sm:grid-cols-4"> <div class="space-y-0.5"> <div class="text-muted-foreground flex items-center gap-1 text-xs"> <lucide-icon [img]="Zap" class="text-primary size-3" /> <span>Ping (p50)</span> </div> <div class="text-foreground font-mono text-sm font-bold">{{ selectedNode().latencyMs }}ms</div> </div> <div class="space-y-0.5"> <div class="text-muted-foreground flex items-center gap-1 text-xs"> <lucide-icon [img]="Activity" class="text-success size-3" /> <span>Ping (p99)</span> </div> <div class="text-foreground font-mono text-sm font-bold">{{ selectedNode().p99LatencyMs }}ms</div> </div> <div class="space-y-0.5"> <div class="text-muted-foreground flex items-center gap-1 text-xs"> <lucide-icon [img]="Server" class="text-info size-3" /> <span>Cache Hit</span> </div> <div class="text-foreground font-mono text-sm font-bold">{{ selectedNode().cacheHitRatio }}%</div> </div> <div class="space-y-0.5"> <div class="text-muted-foreground flex items-center gap-1 text-xs"> <lucide-icon [img]="ShieldCheck" class="text-primary size-3" /> <span>Throughput</span> </div> <div class="text-foreground font-mono text-sm font-bold"> {{ selectedNode().qps.toLocaleString() }} qps </div> </div> </div> <!-- Quick Node Pill Selector --> <div class="flex flex-wrap gap-1.5 pt-1"> @for (node of activeNodes(); track node.id) { <button type="button" [class]="nodePillClass(node.id)" (click)="selectNode(node.id)"> {{ node.name }} • {{ node.city }} </button> } </div> </div> </div> </div> </div> </div> </section> `, }) export class UiHeroGlobeInteractiveNodesComponent { protected readonly Activity = Activity protected readonly ArrowRight = ArrowRight protected readonly Check = Check protected readonly Copy = Copy protected readonly Globe = Globe protected readonly Radio = Radio protected readonly Server = Server protected readonly ShieldCheck = ShieldCheck protected readonly Terminal = Terminal protected readonly Zap = Zap @Input() title = 'Sub-millisecond UI edge delivery across 310+ PoPs globally.' @Input() description = 'Compile unbundled Vue 3.5 and React 19 components with instant edge caching. Zero cold starts, deterministic tree-shaking, and hardware-accelerated telemetry.' @Input() cliCommand = 'npx shadcn add @uipkge/init' @Input() nodes?: EdgeNode[] @Input('class') className?: string readonly selectedNodeId = signal('node-iad') readonly copied = signal(false) private copyTimer: ReturnType<typeof setTimeout> | undefined readonly activeNodes = computed(() => this.nodes ?? DEFAULT_NODES) readonly hubNode = computed<EdgeNode>(() => this.activeNodes()[0] ?? DEFAULT_NODES[0]!) readonly selectedNode = computed<EdgeNode>(() => { const all = this.activeNodes() return all.find((n) => n.id === this.selectedNodeId()) ?? all[0] ?? DEFAULT_NODES[0]! }) readonly avgGlobalLatency = computed(() => { const all = this.activeNodes() if (!all.length) return 0 return Math.round(all.reduce((acc, curr) => acc + curr.latencyMs, 0) / all.length) }) readonly totalGlobalQps = computed(() => this.activeNodes() .reduce((acc, curr) => acc + curr.qps, 0) .toLocaleString(), ) get rootClass(): string { return cn('bg-background relative overflow-hidden py-16 sm:py-24 lg:py-28', this.className) } nodeButtonClass(id: string): string { return cn( 'focus-visible:ring-ring absolute flex -translate-x-1/2 -translate-y-1/2 items-center justify-center rounded-full transition-colors duration-150 focus-visible:ring-2 focus-visible:outline-none', this.selectedNodeId() === id ? 'bg-primary text-primary-foreground ring-primary/20 z-10 size-7 scale-110 shadow-sm ring-4' : 'bg-card hover:bg-accent border-border text-foreground size-5 border hover:scale-105', ) } nodePillClass(id: string): string { return cn( 'rounded-md border px-2 py-1 font-mono text-xs font-medium transition-colors focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none', this.selectedNodeId() === id ? 'border-primary bg-primary/10 text-primary' : 'border-border bg-card text-muted-foreground hover:text-foreground hover:bg-accent/50', ) } selectNode(id: string): void { this.selectedNodeId.set(id) } async copyCommand(): Promise<void> { try { await navigator.clipboard.writeText(this.cliCommand) this.copied.set(true) clearTimeout(this.copyTimer) this.copyTimer = setTimeout(() => this.copied.set(false), 2000) } catch { // Clipboard unavailable (e.g. insecure context): leave the Copy icon. } } }src/app/components/blocks/hero-globe-interactive-nodes/index.ts0.1 kB
export { UiHeroGlobeInteractiveNodesComponent, type EdgeNode } from './hero-globe-interactive-nodes.component'
Raw manifest:https://uipkge.dev/r/angular/hero-globe-interactive-nodes.json