Integrations — Hub Diagram
blockmarketingRadial integration diagram: a central platform node connected by drawn SVG spokes to eight orbiting service nodes, with a legend splitting sources from destinations.
Installation
$pnpm dlx uipkge-ng@latest add integrations-hub-diagram$npx uipkge-ng@latest add integrations-hub-diagram$yarn dlx uipkge-ng@latest add integrations-hub-diagram$bunx uipkge-ng@latest add integrations-hub-diagramManifest:
https://uipkge.dev/r/angular/integrations-hub-diagram.jsonInstalls to:src/app/components/blocks/integrations-hub-diagram/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.
HubNodeinterface HubNode {
mark: string
name: string
direction: string
}npm dependencies
Files installed (2)
src/app/components/blocks/integrations-hub-diagram/integrations-hub-diagram.component.ts6.2 kB
// Radial integration diagram: a central platform node connected by drawn SVG // spokes to eight orbiting service nodes, with a legend splitting sources from // destinations. Port of the React block 1:1. import { ChangeDetectionStrategy, Component, Input } from '@angular/core' import { ArrowRight, Boxes, LucideAngularModule } 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' interface HubNode { mark: string name: string direction: string } interface PlacedNode extends HubNode { x: number y: number } const nodes: HubNode[] = [ { mark: 'SF', name: 'Snowflake', direction: 'source' }, { mark: 'BQ', name: 'BigQuery', direction: 'source' }, { mark: 'PG', name: 'Postgres', direction: 'source' }, { mark: 'DT', name: 'dbt', direction: 'source' }, { mark: 'SL', name: 'Slack', direction: 'destination' }, { mark: 'PD', name: 'PagerDuty', direction: 'destination' }, { mark: 'LK', name: 'Looker', direction: 'destination' }, { mark: 'HX', name: 'Hex', direction: 'destination' }, ] // Positions are computed on a circle in a 100x100 user-space viewBox, so any // number of nodes distributes evenly without touching the markup. const RADIUS = 38 const placed: PlacedNode[] = nodes.map((node, index) => { const angle = (index / nodes.length) * Math.PI * 2 - Math.PI / 2 return { ...node, x: 50 + RADIUS * Math.cos(angle), y: 50 + RADIUS * Math.sin(angle) } }) @Component({ changeDetection: ChangeDetectionStrategy.Eager, selector: 'ui-integrations-hub-diagram, [ui-integrations-hub-diagram]', 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="integrations-hub-diagram" [class]="rootClass"> <div class="mx-auto max-w-6xl px-6 py-20 lg:py-28"> <div class="grid items-center gap-12 lg:grid-cols-[1fr_1.1fr] lg:gap-16"> <div> <span ui-badge variant="secondary">Architecture</span> <h2 class="mt-4 text-3xl font-semibold tracking-tight text-balance sm:text-4xl"> One hub between your warehouse and everything downstream </h2> <p class="text-muted-foreground mt-3 leading-relaxed"> Sources stay read-only and in place. Destinations receive certified metrics rather than raw extracts, so nothing downstream can quietly redefine a number. </p> <dl class="mt-8 flex flex-wrap gap-x-8 gap-y-3 text-sm"> <div class="flex items-center gap-2"> <span class="bg-primary size-2 rounded-full" aria-hidden="true"></span> <dt class="font-medium">Sources</dt> <dd class="text-muted-foreground">read-only</dd> </div> <div class="flex items-center gap-2"> <span class="bg-muted-foreground/50 size-2 rounded-full" aria-hidden="true"></span> <dt class="font-medium">Destinations</dt> <dd class="text-muted-foreground">certified metrics only</dd> </div> </dl> <button ui-button class="mt-8"> See the reference architecture <lucide-icon [img]="ArrowRight" class="ml-2 size-4" aria-hidden="true" /> </button> </div> <div ui-card> <div ui-card-content class="p-6"> <div class="relative aspect-square"> <!-- Spokes are drawn in the same 100-unit space the nodes are placed in, so line ends always meet the node centres. --> <svg class="absolute inset-0 size-full" viewBox="0 0 100 100" aria-hidden="true"> @for (node of placed; track node.name) { <line x1="50" y1="50" [attr.x2]="node.x" [attr.y2]="node.y" class="stroke-border" stroke-width="0.4" [attr.stroke-dasharray]="node.direction === 'destination' ? '2 2' : null" /> } <circle cx="50" cy="50" [attr.r]="radius" class="stroke-border/60" fill="none" stroke-width="0.3" /> </svg> <div class="border-border bg-card absolute top-1/2 left-1/2 flex size-20 -translate-x-1/2 -translate-y-1/2 flex-col items-center justify-center gap-1 rounded-xl border shadow-sm" > <lucide-icon [img]="Boxes" class="text-primary size-5" aria-hidden="true" /> <span class="text-xs font-semibold tracking-tight">Metric layer</span> </div> @for (node of placed; track node.name) { <div class="absolute flex -translate-x-1/2 -translate-y-1/2 flex-col items-center gap-1" [style.left]="node.x + '%'" [style.top]="node.y + '%'" > <span [class]="nodeTileClass(node)" aria-hidden="true"> {{ node.mark }} </span> <span class="text-muted-foreground text-xs whitespace-nowrap">{{ node.name }}</span> </div> } </div> </div> </div> </div> </div> </section> `, }) export class UiIntegrationsHubDiagramComponent { protected readonly ArrowRight = ArrowRight protected readonly Boxes = Boxes protected readonly placed = placed protected readonly radius = RADIUS @Input('class') className?: string get rootClass(): string { return cn('bg-background', this.className) } nodeTileClass(node: HubNode): string { return cn( 'border-border bg-background flex size-10 items-center justify-center rounded-lg border font-mono text-xs font-semibold', node.direction === 'source' ? 'text-foreground' : 'text-muted-foreground', ) } }src/app/components/blocks/integrations-hub-diagram/index.ts0.1 kB
export { UiIntegrationsHubDiagramComponent } from './integrations-hub-diagram.component'
Raw manifest:https://uipkge.dev/r/angular/integrations-hub-diagram.json