Pricing
blockpricingPricing collection with eleven interchangeable variants: seat matrix, interactive calculator, enterprise SLA card, feature add-on builder, feature tier slider, grouped matrix, per-plan cards, sticky-header matrix, single plan, tier comparison matrix, and usage calculator slider.
Installation
$pnpm dlx uipkge-ng@latest add pricing$npx uipkge-ng@latest add pricing$yarn dlx uipkge-ng@latest add pricing$bunx uipkge-ng@latest add pricingManifest:
https://uipkge.dev/r/angular/pricing.jsonInstalls to:src/app/components/blocks/pricing/Variants
Loading interactive previews…
Loading interactive previews…
Files installed (3)
src/app/components/blocks/pricing/pricing.component.ts4.1 kB
// Pricing collection shell: the flagship seat matrix (pricing-01) plus the ten // sibling variant blocks, composed into the one install unit that mirrors the // 11-file Vue `pricing` registry item. Use `variant` to render a single variant; // the default stacks the full collection like the React demo page. import { ChangeDetectionStrategy, Component, Input } from '@angular/core' import { UiPricing01Component } from '../pricing-01/pricing-01.component' import { UiPricingCalculatorComponent } from '../pricing-calculator/pricing-calculator.component' import { UiPricingEnterpriseSlaCardComponent } from '../pricing-enterprise-sla-card/pricing-enterprise-sla-card.component' import { UiPricingFeatureAddonBuilderComponent } from '../pricing-feature-addon-builder/pricing-feature-addon-builder.component' import { UiPricingFeatureTierSliderComponent } from '../pricing-feature-tier-slider/pricing-feature-tier-slider.component' import { UiPricingMatrixGroupedComponent } from '../pricing-matrix-grouped/pricing-matrix-grouped.component' import { UiPricingMatrixPlanCardsComponent } from '../pricing-matrix-plan-cards/pricing-matrix-plan-cards.component' import { UiPricingMatrixStickyHeaderComponent } from '../pricing-matrix-sticky-header/pricing-matrix-sticky-header.component' import { UiPricingSinglePlanComponent } from '../pricing-single-plan/pricing-single-plan.component' import { UiPricingTierComparisonMatrixComponent } from '../pricing-tier-comparison-matrix/pricing-tier-comparison-matrix.component' import { UiPricingUsageCalculatorSliderComponent } from './pricing-usage-calculator-slider.component' export type PricingVariant = | 'all' | 'pricing-01' | 'calculator' | 'enterprise-sla' | 'addon-builder' | 'tier-slider' | 'matrix-grouped' | 'matrix-plan-cards' | 'matrix-sticky-header' | 'single-plan' | 'tier-comparison' | 'usage-calculator' @Component({ changeDetection: ChangeDetectionStrategy.Eager, selector: 'ui-pricing, [ui-pricing]', standalone: true, host: { '[attr.class]': '"contents"' }, imports: [ UiPricing01Component, UiPricingCalculatorComponent, UiPricingEnterpriseSlaCardComponent, UiPricingFeatureAddonBuilderComponent, UiPricingFeatureTierSliderComponent, UiPricingMatrixGroupedComponent, UiPricingMatrixPlanCardsComponent, UiPricingMatrixStickyHeaderComponent, UiPricingSinglePlanComponent, UiPricingTierComparisonMatrixComponent, UiPricingUsageCalculatorSliderComponent, ], template: ` <div data-slot="pricing" class="w-full space-y-16"> @switch (variant) { @case ('pricing-01') { <ui-pricing-01 /> } @case ('calculator') { <ui-pricing-calculator /> } @case ('enterprise-sla') { <ui-pricing-enterprise-sla-card /> } @case ('addon-builder') { <ui-pricing-feature-addon-builder /> } @case ('tier-slider') { <ui-pricing-feature-tier-slider /> } @case ('matrix-grouped') { <ui-pricing-matrix-grouped /> } @case ('matrix-plan-cards') { <ui-pricing-matrix-plan-cards /> } @case ('matrix-sticky-header') { <ui-pricing-matrix-sticky-header /> } @case ('single-plan') { <ui-pricing-single-plan /> } @case ('tier-comparison') { <ui-pricing-tier-comparison-matrix /> } @case ('usage-calculator') { <ui-pricing-usage-calculator-slider /> } @default { <ui-pricing-01 /> <ui-pricing-calculator /> <ui-pricing-enterprise-sla-card /> <ui-pricing-feature-addon-builder /> <ui-pricing-feature-tier-slider /> <ui-pricing-matrix-grouped /> <ui-pricing-matrix-plan-cards /> <ui-pricing-matrix-sticky-header /> <ui-pricing-single-plan /> <ui-pricing-tier-comparison-matrix /> <ui-pricing-usage-calculator-slider /> } } </div> `, }) export class UiPricingComponent { /** Single variant to render, or 'all' for the full stacked collection. */ @Input() variant: PricingVariant = 'all' }src/app/components/blocks/pricing/pricing-usage-calculator-slider.component.ts10.1 kB
// Infrastructure ROI calculator: MAU / QPS / edge-replica sliders against a // traditional monolith cost model. Port of the Vue PricingUsageCalculatorSlider 1:1. import { ChangeDetectionStrategy, Component, Input, computed, signal } from '@angular/core' import { ArrowRight, Calculator, Globe, LucideAngularModule, Server, Users } from 'lucide-angular' import { cn } from '@/lib/utils' import { UiBadgeComponent } from '@/ui/badge/badge.component' import { UiButtonComponent } from '@/ui/button/button.component' import { UiCardComponent } from '@/ui/card/card.component' export function formatCompactNumber(num: number): string { if (num >= 1000000) return (num / 1000000).toFixed(1) + 'M' if (num >= 1000) return (num / 1000).toFixed(0) + 'k' return num.toString() } @Component({ changeDetection: ChangeDetectionStrategy.Eager, selector: 'ui-pricing-usage-calculator-slider, [ui-pricing-usage-calculator-slider]', standalone: true, host: { '[attr.class]': '"contents"' }, imports: [LucideAngularModule, UiBadgeComponent, UiButtonComponent, UiCardComponent], template: ` <section data-slot="pricing-usage-calculator-slider" [class]="rootClass"> <div class="mx-auto max-w-6xl space-y-12"> <div class="mx-auto max-w-3xl space-y-4 text-center"> <span ui-badge variant="secondary" class="gap-1.5 px-3 py-1 font-mono text-xs shadow-xs"> <lucide-icon [img]="Calculator" class="text-primary size-3.5" /> Interactive Infrastructure ROI Calculator </span> <h2 class="text-foreground text-3xl font-bold tracking-tight sm:text-4xl"> Calculate your annual savings with unbundled architecture. </h2> <p class="text-muted-foreground text-base"> See how eliminating proprietary seat licenses and runtime SaaS wrappers cuts your front-end TCO. </p> <div class="flex flex-wrap items-center justify-center gap-2 pt-2"> <button ui-button size="sm" variant="outline" class="font-mono text-xs" (click)="applyPreset('seed')"> Seed Startup (25k MAU) </button> <button ui-button size="sm" variant="outline" class="font-mono text-xs" (click)="applyPreset('growth')"> Growth Scale (350k MAU) </button> <button ui-button size="sm" variant="outline" class="font-mono text-xs" (click)="applyPreset('scale')"> Hypergrowth (2.5M MAU) </button> </div> </div> <div class="grid grid-cols-1 items-stretch gap-8 lg:grid-cols-12"> <div ui-card class="border-border bg-card/95 flex flex-col justify-between space-y-6 rounded-2xl p-6 text-left shadow-xl sm:p-8 lg:col-span-7" > <div class="space-y-6"> <div class="border-border flex items-center justify-between border-b pb-4"> <h3 class="text-foreground font-mono text-sm font-bold">Traffic & Telemetry Inputs</h3> <span class="text-muted-foreground font-mono text-xs">Dynamic Projection</span> </div> <div class="space-y-2"> <div class="flex items-center justify-between font-mono text-xs"> <span class="text-muted-foreground flex items-center gap-1.5"> <lucide-icon [img]="Users" class="text-primary size-3.5" /> Monthly Active Users (MAU) </span> <span class="text-foreground text-sm font-bold">{{ formatNumber(mau()) }} users</span> </div> <input type="range" min="10000" max="5000000" step="25000" [value]="mau()" (input)="mau.set($any($event.target).valueAsNumber)" class="accent-primary bg-border h-2 w-full cursor-pointer rounded-lg" aria-label="Monthly active users" /> </div> <div class="space-y-2"> <div class="flex items-center justify-between font-mono text-xs"> <span class="text-muted-foreground flex items-center gap-1.5"> <lucide-icon [img]="Server" class="text-primary size-3.5" /> Peak Query Throughput </span> <span class="text-foreground text-sm font-bold">{{ formatNumber(qps()) }} QPS</span> </div> <input type="range" min="100" max="20000" step="100" [value]="qps()" (input)="qps.set($any($event.target).valueAsNumber)" class="accent-primary bg-border h-2 w-full cursor-pointer rounded-lg" aria-label="Peak query throughput" /> </div> <div class="space-y-2"> <div class="flex items-center justify-between font-mono text-xs"> <span class="text-muted-foreground flex items-center gap-1.5"> <lucide-icon [img]="Globe" class="text-primary size-3.5" /> Global Edge POP Replicas </span> <span class="text-foreground text-sm font-bold">{{ edgeReplicas() }} Edge Regions</span> </div> <input type="range" min="1" max="12" step="1" [value]="edgeReplicas()" (input)="edgeReplicas.set($any($event.target).valueAsNumber)" class="accent-primary bg-border h-2 w-full cursor-pointer rounded-lg" aria-label="Global edge replicas" /> </div> </div> <div class="border-border text-muted-foreground flex items-center justify-between border-t pt-4 font-mono text-xs" > <span>Formula: Direct AST + Raw Static Hosting</span> <span class="text-success font-semibold">✓ Zero Seat Surcharges</span> </div> </div> <div ui-card class="border-border bg-card/95 flex flex-col justify-between space-y-6 rounded-2xl p-6 text-left shadow-sm sm:p-8 lg:col-span-5" > <div class="space-y-6"> <span ui-badge variant="outline" class="border-success/20 bg-success/10 text-success font-mono text-xs"> Projected Annual Net Savings </span> <div class="space-y-1"> <div class="text-success font-mono text-4xl font-bold tracking-tight sm:text-5xl"> \${{ annualSavings().toLocaleString() }} </div> <p class="text-muted-foreground font-mono text-xs"> Saved every year (\${{ monthlySavings().toLocaleString() }}/month) </p> </div> <div class="space-y-3 pt-2"> <div class="space-y-1"> <div class="flex items-center justify-between font-mono text-xs"> <span class="text-destructive font-medium">Traditional Monolith Stack:</span> <span class="text-foreground font-bold">\${{ traditionalCost().toLocaleString() }}/mo</span> </div> <div class="bg-destructive/20 h-2 overflow-hidden rounded-full"> <div class="bg-destructive h-full w-full"></div> </div> </div> <div class="space-y-1"> <div class="flex items-center justify-between font-mono text-xs"> <span class="text-success font-semibold">UIPKGE Unbundled Registry:</span> <span class="text-foreground font-bold">\${{ uipkgeCost().toLocaleString() }}/mo</span> </div> <div class="bg-success/20 h-2 overflow-hidden rounded-full"> <div class="bg-success h-full transition-[width] duration-300" [style.width]="uipkgeBarWidth() + '%'" ></div> </div> </div> </div> </div> <button ui-button class="mt-4 h-10 w-full gap-1.5 font-mono text-xs shadow-md"> <span>Lock In Pro Savings</span> <lucide-icon [img]="ArrowRight" class="size-3.5" /> </button> </div> </div> </div> </section> `, }) export class UiPricingUsageCalculatorSliderComponent { protected readonly ArrowRight = ArrowRight protected readonly Calculator = Calculator protected readonly Globe = Globe protected readonly Server = Server protected readonly Users = Users @Input('class') className?: string readonly mau = signal(100000) readonly qps = signal(500) readonly edgeReplicas = signal(3) readonly traditionalCost = computed(() => { const baseSeatFee = 350 const perUserFee = (this.mau() / 1000) * 1.8 const qpsSurcharge = this.qps() * 0.25 return Math.round(baseSeatFee + perUserFee + qpsSurcharge) }) readonly uipkgeCost = computed(() => { const rawBandwidth = (this.mau() / 100000) * 4 const flatProLicense = 24 return Math.round(flatProLicense + rawBandwidth) }) readonly monthlySavings = computed(() => Math.max(0, this.traditionalCost() - this.uipkgeCost())) readonly annualSavings = computed(() => this.monthlySavings() * 12) readonly uipkgeBarWidth = computed(() => Math.max(5, (this.uipkgeCost() / Math.max(1, this.traditionalCost())) * 100)) get rootClass(): string { return cn('bg-background relative overflow-hidden px-4 py-16 sm:px-6 sm:py-24 lg:px-8', this.className) } formatNumber(num: number): string { return formatCompactNumber(num) } applyPreset(preset: 'seed' | 'growth' | 'scale'): void { if (preset === 'seed') { this.mau.set(25000) this.qps.set(150) this.edgeReplicas.set(2) } else if (preset === 'growth') { this.mau.set(350000) this.qps.set(2500) this.edgeReplicas.set(5) } else { this.mau.set(2500000) this.qps.set(12000) this.edgeReplicas.set(10) } } }src/app/components/blocks/pricing/index.ts0.2 kB
export { UiPricingComponent, type PricingVariant } from './pricing.component' export { UiPricingUsageCalculatorSliderComponent, formatCompactNumber, } from './pricing-usage-calculator-slider.component'
Raw manifest:https://uipkge.dev/r/angular/pricing.json