Coming Soon
blockmarketingInteractive launch and maintenance workbench with high-impact countdown, VIP early access waitlist queue allocator, live launch roadmap progress tracker, and feature sneak-peek previews.
Installation
$pnpm dlx uipkge-ng@latest add coming-soon$npx uipkge-ng@latest add coming-soon$yarn dlx uipkge-ng@latest add coming-soon$bunx uipkge-ng@latest add coming-soonManifest:
https://uipkge.dev/r/angular/coming-soon.jsonInstalls to:src/app/components/blocks/coming-soon/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.
ComingSoonMilestoneinterface ComingSoonMilestone {
id: string
title: string
category: string
status: 'completed' | 'in_progress' | 'scheduled'
completionPercent: number
date: string
}FeatureTeaserinterface FeatureTeaser {
id: string
badge: string
title: string
description: string
icon: LucideIconData
statLabel: string
statValue: string
codeSnippet: string
}Files installed (2)
src/app/components/blocks/coming-soon/coming-soon.component.ts25.2 kB
// Interactive launch and maintenance workbench with high-impact countdown, VIP early // access waitlist queue allocator, live launch roadmap progress tracker, and feature // sneak-peek previews. Port of the React block 1:1. import { ChangeDetectionStrategy, Component, computed, Input, OnDestroy, OnInit, signal } from '@angular/core' import { Activity, ArrowRight, ArrowUpRight, Bot, Check, CheckCircle2, Copy, LucideAngularModule, Palette, Rocket, ShieldCheck, Terminal, Zap, type LucideIconData, } 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, UiCardDescriptionComponent, UiCardHeaderComponent, UiCardTitleComponent, } from '@/ui/card/card.component' import { UiInputComponent } from '@/ui/input/input.component' import { UiProgressComponent } from '@/ui/progress/progress.component' import { UiSeparatorComponent } from '@/ui/separator/separator.component' function pad(value: number): string { return String(value).padStart(2, '0') } export interface ComingSoonMilestone { id: string title: string category: string status: 'completed' | 'in_progress' | 'scheduled' completionPercent: number date: string } const initialMilestones: ComingSoonMilestone[] = [ { id: 'm1', title: 'Dual-Framework Compiler & Monorepo Architecture', category: 'Core Infra', status: 'completed', completionPercent: 100, date: 'Aug 15, 2026', }, { id: 'm2', title: 'Tailwind CSS v4 OKLCH Dynamic Theming Engine', category: 'Design System', status: 'completed', completionPercent: 100, date: 'Aug 18, 2026', }, { id: 'm3', title: 'Zero-Lockin Code Distribution Registry & CLI', category: 'DX & Tooling', status: 'completed', completionPercent: 100, date: 'Aug 21, 2026', }, { id: 'm4', title: 'Multi-Region Edge Registry Cache & Global SLA Testing', category: 'Infrastructure', status: 'in_progress', completionPercent: 88, date: 'Target: 3 Days', }, { id: 'm5', title: 'Public V2.0 Global General Availability Rollout', category: 'Release', status: 'scheduled', completionPercent: 0, date: 'Target: 7 Days', }, ] export interface FeatureTeaser { id: string badge: string title: string description: string icon: LucideIconData statLabel: string statValue: string codeSnippet: string } const teasers: FeatureTeaser[] = [ { id: 'agentic', badge: 'Flagship AI', title: 'Autonomous Multi-Agent Orchestrator', description: 'Decompose complex workflow DAGs into parallel subagent execution with real-time vector memory.', icon: Bot, statLabel: 'DAG Execution Speed', statValue: '120ms P99', codeSnippet: 'npx shadcn-vue@latest add @uipkge/ai-agent-orchestrator', }, { id: 'design', badge: 'Design Engineering', title: 'Tailwind v4 OKLCH Fluid Theme Tokens', description: 'Perceptually uniform color spaces with automatic high-contrast dark mode switching & zero CSS bloat.', icon: Palette, statLabel: 'Bundle Size Overhead', statValue: '0.00 kB', codeSnippet: 'npx shadcn-vue@latest add @uipkge/theme-customize', }, { id: 'edge', badge: 'Ultra Low Latency', title: 'Cloudflare Workers Multi-Region Cache', description: 'Global component manifest resolution with smart geo-routing and edge-cached JSON trees.', icon: Zap, statLabel: 'Global Median Latency', statValue: '8.4ms', codeSnippet: 'curl -s https://uipkge.dev/r/vue/init.json', }, ] const roleOptions = [ { id: 'frontend', label: 'Frontend / UI' }, { id: 'architect', label: 'Tech Lead / Arch' }, { id: 'founder', label: 'CTO / Founder' }, ] as const export type WaitlistRole = (typeof roleOptions)[number]['id'] @Component({ changeDetection: ChangeDetectionStrategy.Eager, selector: 'ui-coming-soon, [ui-coming-soon]', 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, UiCardDescriptionComponent, UiCardHeaderComponent, UiCardTitleComponent, UiInputComponent, UiProgressComponent, UiSeparatorComponent, ], template: ` <section data-slot="coming-soon" [class]="rootClass"> <!-- Top Telemetry & Status Bar --> <div class="border-border/70 mx-auto flex w-full max-w-6xl flex-col items-center justify-between gap-4 border-b pb-12 sm:flex-row" > <div class="flex items-center gap-3"> <div class="bg-primary text-primary-foreground flex size-9 items-center justify-center rounded-xl font-bold shadow-xs" > <lucide-icon [img]="Rocket" class="size-5" /> </div> <div> <div class="flex items-center gap-2"> <span class="text-foreground text-sm font-semibold tracking-tight">UIPKGE Engine</span> <span ui-badge variant="outline" class="border-primary/30 text-primary px-1.5 py-0.5 font-mono text-xs"> {{ productVersion }} </span> </div> <p class="text-muted-foreground text-xs">Dual-framework registry & visual workbench suite</p> </div> </div> <!-- Global SLA Readiness Pill --> <div class="bg-muted/40 border-border flex items-center gap-3 rounded-full border px-3.5 py-1.5"> <div class="bg-success flex size-2 animate-pulse rounded-full"></div> <span class="text-muted-foreground font-mono text-xs"> Staging Infrastructure: <strong class="text-foreground font-semibold">99.99% Operational</strong> </span> <div ui-separator orientation="vertical" class="h-3.5"></div> <a [href]="statusUrl" target="_blank" rel="noreferrer" class="text-primary inline-flex items-center gap-1 font-mono text-xs hover:underline" > Live Status <lucide-icon [img]="ArrowUpRight" class="size-3" /> </a> </div> </div> <!-- Main Hero & Countdown Body --> <div class="mx-auto my-auto w-full max-w-6xl space-y-16 py-10"> <!-- Headline & Subtitle --> <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]="Rocket" class="text-primary size-3.5" /> V2.0 General Availability Launch Matrix </span> <h1 class="text-foreground text-4xl leading-[1.1] font-bold tracking-tight sm:text-5xl lg:text-6xl"> Production-grade design engineering workbenches. </h1> <p class="text-muted-foreground mx-auto max-w-2xl text-base leading-relaxed sm:text-lg"> We are deploying 281+ production-grade primitives and composed workbenches with zero package dependencies. Own your source code with complete architectural freedom. </p> </div> <!-- High-Impact Digital Countdown Grid --> <div class="mx-auto grid max-w-2xl grid-cols-2 gap-4 sm:grid-cols-4"> @for (unit of units(); track unit.label) { <div ui-card class="border-border bg-card/70 group hover:border-primary/40 relative overflow-hidden p-5 text-center shadow-xs backdrop-blur-xs transition-colors" > <div class="text-foreground font-mono text-4xl font-bold tracking-tight tabular-nums sm:text-5xl"> {{ unit.value }} </div> <div class="text-muted-foreground mt-2 font-mono text-xs font-medium tracking-widest uppercase"> {{ unit.label }} </div> <div class="bg-primary/20 group-hover:bg-primary absolute right-0 bottom-0 left-0 h-0.5 transition-colors" ></div> </div> } </div> <!-- Early Access & Queue Tracker Section --> <div class="mx-auto w-full max-w-xl"> <div ui-card class="border-border bg-card overflow-hidden shadow-sm"> <div ui-card-header class="pb-4"> <div class="flex items-center justify-between"> <h3 ui-card-title class="flex items-center gap-2 text-base font-semibold"> <lucide-icon [img]="ShieldCheck" class="text-primary size-4" /> Early Access Allocation </h3> <span ui-badge variant="outline" class="border-success/20 bg-success/10 text-success font-mono text-xs"> Batch #2 Opening Soon </span> </div> <p ui-card-description class="text-xs"> Request priority invite access. Get instant sandbox access to all 281+ blocks before general public launch. </p> </div> <div ui-card-content> @if (!submitted()) { <form class="space-y-4" (submit)="onWaitlistSubmit($event)"> <!-- Role Selector Tabs --> <div class="space-y-1.5"> <label class="text-foreground text-xs font-medium">Your Primary Engineering Focus</label> <div class="grid grid-cols-3 gap-2"> @for (r of roles; track r.id) { <button type="button" [class]="roleClass(r.id)" (click)="role.set(r.id)" > {{ r.label }} </button> } </div> </div> <!-- Email Input & Submit --> <div class="flex flex-col gap-2 sm:flex-row"> <div class="relative flex-1"> <ui-input [value]="email()" (valueChange)="email.set($event)" type="email" placeholder="[email protected]" required class="h-10 font-mono text-xs" /> </div> <button ui-button type="submit" class="h-10 gap-1.5 px-5 text-xs font-semibold"> <span>Reserve Spot</span> <lucide-icon [img]="ArrowRight" class="size-3.5" /> </button> </div> <p class="text-muted-foreground text-center text-xs"> No spam. You will only receive your single cryptographic invite token. </p> </form> } @else { <div class="bg-muted/40 border-border space-y-4 rounded-xl border p-4 text-center"> <div class="bg-success/10 text-success mx-auto flex size-10 items-center justify-center rounded-full"> <lucide-icon [img]="Check" class="size-5" /> </div> <div class="space-y-1"> <p class="text-foreground text-sm font-semibold">You are #{{ queuePosition() }} in queue!</p> <p class="text-muted-foreground text-xs"> Invitation tokens for Batch #2 will be delivered to <strong class="text-foreground font-mono">{{ email() }}</strong >. </p> </div> <div class="border-border flex flex-col items-center justify-between gap-3 border-t pt-2 sm:flex-row"> <span class="text-muted-foreground font-mono text-xs">Move up 5 spots per teammate invite:</span> <button ui-button variant="outline" size="sm" class="h-8 gap-1.5 font-mono text-xs" (click)="copyReferral()" > @if (copiedReferral()) { <lucide-icon [img]="Check" class="text-success size-3" /> } @else { <lucide-icon [img]="Copy" class="size-3" /> } <span>{{ copiedReferral() ? 'Copied Link' : 'Copy Invite Link' }}</span> </button> </div> </div> } </div> </div> </div> <!-- Interactive Feature Sneak-Peek Carousel & Terminal Preview --> <div class="space-y-6"> <div class="flex flex-col justify-between gap-4 sm:flex-row sm:items-center"> <div> <h2 class="text-foreground flex items-center gap-2 text-lg font-semibold tracking-tight"> <lucide-icon [img]="Zap" class="text-primary size-4" /> Incoming Flagship Capabilities </h2> <p class="text-muted-foreground text-xs">Sneak peek preview of the upcoming architecture release</p> </div> <!-- Teaser Navigation Tabs --> <div class="bg-muted/40 border-border flex items-center gap-1.5 rounded-lg border p-1"> @for (t of teasersList; track t.id; let idx = $index) { <button type="button" [class]="teaserTabClass(idx)" (click)="activeTeaserIndex.set(idx)" > {{ t.badge }} </button> } </div> </div> <!-- Active Teaser Card --> <div ui-card class="border-border bg-card overflow-hidden"> <div class="divide-border grid grid-cols-1 divide-y lg:grid-cols-12 lg:divide-x lg:divide-y-0"> <div class="space-y-4 p-6 sm:p-8 lg:col-span-7"> <div class="flex items-center gap-2.5"> <div class="bg-primary/10 text-primary rounded-lg p-2"> <lucide-icon [img]="activeTeaser().icon" class="size-5" /> </div> <span ui-badge variant="secondary" class="font-mono text-xs"> {{ activeTeaser().badge }} </span> </div> <h3 class="text-foreground text-xl font-bold tracking-tight">{{ activeTeaser().title }}</h3> <p class="text-muted-foreground text-xs leading-relaxed sm:text-sm"> {{ activeTeaser().description }} </p> <div class="flex items-center gap-6 pt-2"> <div> <p class="text-muted-foreground font-mono text-xs">{{ activeTeaser().statLabel }}</p> <p class="text-foreground mt-0.5 font-mono text-xl font-bold"> {{ activeTeaser().statValue }} </p> </div> <div ui-separator orientation="vertical" class="h-8"></div> <div> <p class="text-muted-foreground font-mono text-xs">Framework Support</p> <p class="text-success mt-1 text-xs font-semibold">Vue 3.5 & React 19 Parity</p> </div> </div> </div> <!-- Terminal Snippet Box --> <div class="bg-muted/20 flex flex-col justify-between space-y-4 p-6 lg:col-span-5"> <div class="space-y-2"> <div class="flex items-center justify-between"> <span class="text-muted-foreground flex items-center gap-1.5 font-mono text-xs font-medium"> <lucide-icon [img]="Terminal" class="size-3.5" /> Direct CLI Command </span> <button ui-button variant="ghost" size="sm" class="h-7 gap-1.5 px-2 font-mono text-xs" (click)="copyTeaserSnippet(activeTeaser().codeSnippet)" > @if (copiedCli()) { <lucide-icon [img]="Check" class="text-success size-3" /> } @else { <lucide-icon [img]="Copy" class="size-3" /> } <span>{{ copiedCli() ? 'Copied' : 'Copy' }}</span> </button> </div> <div class="bg-background border-border text-foreground selection:bg-primary/20 overflow-x-auto rounded-lg border p-3 font-mono text-xs" > <code>{{ activeTeaser().codeSnippet }}</code> </div> </div> <div class="border-border/80 bg-background/50 text-muted-foreground flex items-center gap-2 rounded-lg border p-3 text-xs" > <lucide-icon [img]="CheckCircle2" class="text-success size-3.5 shrink-0" /> <span>Transitive dependencies, OKLCH styles & TS types included.</span> </div> </div> </div> </div> </div> <!-- Launch Readiness Milestones Tracker --> <div class="border-border/70 space-y-6 border-t pt-4"> <div class="flex flex-col justify-between gap-4 sm:flex-row sm:items-center"> <div> <h2 class="text-foreground flex items-center gap-2 text-lg font-semibold tracking-tight"> <lucide-icon [img]="Activity" class="text-primary size-4" /> V2.0 Launch Milestones & Engineering Progress </h2> <p class="text-muted-foreground text-xs">Transparent real-time build and release status</p> </div> <div class="flex items-center gap-3"> <span class="text-foreground font-mono text-xs font-semibold" >Overall Readiness: {{ overallReadiness() }}%</span > <div ui-progress [value]="overallReadiness()" class="h-2 w-28"></div> </div> </div> <div class="grid grid-cols-1 gap-3 md:grid-cols-2 lg:grid-cols-3"> @for (m of milestones; track m.id) { <div class="border-border bg-card hover:border-primary/30 flex flex-col justify-between space-y-3 rounded-xl border p-4 shadow-xs transition-colors" > <div class="flex items-start justify-between gap-2"> <span ui-badge variant="outline" [class]="milestoneBadgeClass(m.status)" > {{ milestoneLabel(m.status) }} </span> <span class="text-muted-foreground font-mono text-xs">{{ m.date }}</span> </div> <div> <p class="text-foreground text-xs leading-snug font-semibold">{{ m.title }}</p> <p class="text-muted-foreground mt-1 font-mono text-xs">{{ m.category }}</p> </div> <div class="space-y-1 pt-1"> <div class="text-muted-foreground flex justify-between font-mono text-xs"> <span>Progress</span> <span>{{ m.completionPercent }}%</span> </div> <div ui-progress [value]="m.completionPercent" class="h-1.5"></div> </div> </div> } </div> </div> </div> <!-- Footer --> <footer class="border-border text-muted-foreground mx-auto flex w-full max-w-6xl flex-col items-center justify-between gap-4 border-t pt-8 text-xs sm:flex-row" > <div class="flex items-center gap-2"> <span>© {{ currentYear }} UIPKGE. Open source under MIT License.</span> </div> <div class="flex items-center gap-4"> <a href="https://github.com/uday-a/uipkge-registry" target="_blank" rel="noreferrer" class="hover:text-foreground transition-colors" > GitHub </a> <div ui-separator orientation="vertical" class="h-3"></div> <a href="https://uipkge.dev" target="_blank" rel="noreferrer" class="hover:text-foreground transition-colors"> Registry Docs </a> <div ui-separator orientation="vertical" class="h-3"></div> <a [href]="statusUrl" target="_blank" rel="noreferrer" class="hover:text-foreground transition-colors"> Status Page </a> </div> </footer> </section> `, }) export class UiComingSoonComponent implements OnInit, OnDestroy { protected readonly Activity = Activity protected readonly ArrowRight = ArrowRight protected readonly ArrowUpRight = ArrowUpRight protected readonly Check = Check protected readonly CheckCircle2 = CheckCircle2 protected readonly Copy = Copy protected readonly Rocket = Rocket protected readonly ShieldCheck = ShieldCheck protected readonly Terminal = Terminal protected readonly Zap = Zap protected readonly milestones = initialMilestones protected readonly teasersList = teasers protected readonly roles = roleOptions @Input() targetIso = '' @Input() statusUrl = 'https://status.uipkge.dev' @Input() productVersion = 'v2.0 Beta' @Input('class') className?: string readonly days = signal('00') readonly hours = signal('00') readonly minutes = signal('00') readonly seconds = signal('00') readonly activeTeaserIndex = signal(0) // Waitlist form state readonly email = signal('') readonly role = signal<WaitlistRole>('frontend') readonly submitted = signal(false) readonly queuePosition = signal<number | null>(null) readonly copiedReferral = signal(false) readonly copiedCli = signal(false) private deadline = 0 private timer: ReturnType<typeof setInterval> | undefined private copyTimers: ReturnType<typeof setTimeout>[] = [] readonly units = computed(() => [ { label: 'Days', value: this.days() }, { label: 'Hours', value: this.hours() }, { label: 'Minutes', value: this.minutes() }, { label: 'Seconds', value: this.seconds() }, ]) readonly overallReadiness = computed(() => Math.round(initialMilestones.reduce((acc, m) => acc + m.completionPercent, 0) / initialMilestones.length), ) readonly activeTeaser = computed(() => teasers[this.activeTeaserIndex()] ?? teasers[0]) get currentYear(): number { return new Date().getFullYear() } get rootClass(): string { return cn( 'bg-background relative flex min-h-screen flex-col justify-between overflow-hidden px-4 py-16 sm:px-6 lg:px-8', this.className, ) } ngOnInit(): void { this.deadline = this.targetIso ? new Date(this.targetIso).getTime() : Date.now() + 7 * 24 * 60 * 60 * 1000 this.tick() this.timer = setInterval(() => this.tick(), 1000) } ngOnDestroy(): void { clearInterval(this.timer) this.copyTimers.forEach((t) => clearTimeout(t)) } tick(): void { const diff = Math.max(0, this.deadline - Date.now()) this.days.set(pad(Math.floor(diff / 86_400_000))) this.hours.set(pad(Math.floor((diff % 86_400_000) / 3_600_000))) this.minutes.set(pad(Math.floor((diff % 3_600_000) / 60_000))) this.seconds.set(pad(Math.floor((diff % 60_000) / 1000))) } onWaitlistSubmit(event: Event): void { event.preventDefault() this.handleWaitlistSubmit() } handleWaitlistSubmit(): void { if (!this.email()) return const randomPos = Math.floor(Math.random() * 80) + 120 this.queuePosition.set(randomPos) this.submitted.set(true) } copyReferral(): void { void navigator.clipboard?.writeText(`https://uipkge.dev/invite?ref=queue_${this.queuePosition()}`) this.copiedReferral.set(true) this.copyTimers.push(setTimeout(() => this.copiedReferral.set(false), 2000)) } copyTeaserSnippet(snippet: string): void { void navigator.clipboard?.writeText(snippet) this.copiedCli.set(true) this.copyTimers.push(setTimeout(() => this.copiedCli.set(false), 2000)) } roleClass(id: WaitlistRole): string { return cn( 'rounded-lg border px-2.5 py-1.5 text-center text-xs font-medium transition-colors', this.role() === id ? 'border-primary bg-primary/10 text-primary font-semibold shadow-xs' : 'border-border bg-background text-muted-foreground hover:text-foreground', ) } teaserTabClass(idx: number): string { return cn( 'rounded-md px-3 py-1 text-xs font-medium transition-colors', this.activeTeaserIndex() === idx ? 'bg-background text-foreground font-semibold shadow-xs' : 'text-muted-foreground hover:text-foreground', ) } milestoneBadgeClass(status: ComingSoonMilestone['status']): string { return cn( 'px-2 py-0.5 font-mono text-xs', status === 'completed' && 'border-success/20 bg-success/10 text-success', status === 'in_progress' && 'border-warning/20 bg-warning/10 text-warning', status === 'scheduled' && 'bg-muted text-muted-foreground border-border', ) } milestoneLabel(status: ComingSoonMilestone['status']): string { if (status === 'completed') return 'Completed' if (status === 'in_progress') return 'In Progress' return 'Scheduled' } }src/app/components/blocks/coming-soon/index.ts0.1 kB
export { UiComingSoonComponent, type ComingSoonMilestone, type FeatureTeaser, type WaitlistRole, } from './coming-soon.component'
Raw manifest:https://uipkge.dev/r/angular/coming-soon.json