Features — Segmented Compare
blockmarketingSegmented control switching one feature section between two operating modes, with the capability list, constraints, and a recommendation line changing per mode.
Installation
$pnpm dlx uipkge-ng@latest add feature-segmented-compare$npx uipkge-ng@latest add feature-segmented-compare$yarn dlx uipkge-ng@latest add feature-segmented-compare$bunx uipkge-ng@latest add feature-segmented-compareManifest:
https://uipkge.dev/r/angular/feature-segmented-compare.jsonInstalls to:src/app/components/blocks/feature-segmented-compare/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.
ExecutionModeinterface ExecutionMode {
id: string
label: string
summary: string
capabilities: string[]
constraints: string[]
recommendation: string
}npm dependencies
Files installed (2)
src/app/components/blocks/feature-segmented-compare/feature-segmented-compare.component.ts6.2 kB
// Segmented execution-mode comparison: one toggle switches the whole section between // two operating modes, swapping the summary, capability list, constraints, and the // recommendation line. Port of the React block 1:1. import { ChangeDetectionStrategy, Component, Input, computed, signal } from '@angular/core' import { Check, LucideAngularModule, TriangleAlert } 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' import { UiSeparatorComponent } from '@/ui/separator/separator.component' import { UiToggleGroupComponent, UiToggleGroupItemComponent } from '@/ui/toggle-group/toggle-group.component' export interface ExecutionMode { id: string label: string summary: string capabilities: string[] constraints: string[] recommendation: string } // Each mode states its constraints as plainly as its capabilities. A comparison // that only lists upsides makes the reader do the work of distrusting it. const modes: ExecutionMode[] = [ { id: 'live', label: 'Live query', summary: 'Every read hits the warehouse at request time.', capabilities: [ 'Always current, to the second', 'No storage footprint on our side', 'Row-level scope applied per query', ], constraints: ['Latency follows your warehouse', 'Each read is billable compute'], recommendation: 'Use for exploratory analysis and anything finance signs off on.', }, { id: 'materialised', label: 'Materialised', summary: 'Aggregates are computed on a schedule you set.', capabilities: [ 'Sub-100ms reads at any concurrency', 'Warehouse cost bounded by the schedule', 'Freshness stamp shown with every figure', ], constraints: ['Stale between runs, by design', 'Backfills reprocess the window'], recommendation: 'Use for dashboards opened dozens of times an hour.', }, ] @Component({ changeDetection: ChangeDetectionStrategy.Eager, selector: 'ui-feature-segmented-compare, [ui-feature-segmented-compare]', 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, UiSeparatorComponent, UiToggleGroupComponent, UiToggleGroupItemComponent, ], template: ` <section data-slot="feature-segmented-compare" [class]="rootClass"> <div class="mx-auto max-w-4xl px-6 py-20 lg:py-28"> <div class="flex flex-wrap items-end justify-between gap-6"> <div class="max-w-xl"> <span ui-badge variant="secondary">Execution modes</span> <h2 class="mt-4 text-3xl font-semibold tracking-tight sm:text-4xl">Pick per metric, not per platform</h2> </div> <div ui-toggle-group type="single" variant="outline" size="sm" aria-label="Choose an execution mode" [value]="active()" (valueChange)="onChange($event)" > @for (mode of allModes; track mode.id) { <button ui-toggle-group-item [value]="mode.id">{{ mode.label }}</button> } </div> </div> <!-- min-h keeps the card from resizing between modes with different list lengths, which would otherwise shift the page on every toggle. --> <div ui-card class="mt-8"> <div ui-card-content class="min-h-[22rem] p-6"> <p class="text-lg leading-snug font-medium text-balance">{{ current().summary }}</p> <ui-separator class="my-6" /> <div class="grid gap-8 sm:grid-cols-2"> <div> <p class="text-muted-foreground font-mono text-xs tracking-[0.14em] uppercase">What you get</p> <ul class="mt-3 space-y-2.5"> @for (capability of current().capabilities; track capability) { <li class="flex items-start gap-2.5 text-sm"> <lucide-icon [img]="Check" class="text-success mt-0.5 size-4 shrink-0" aria-hidden="true" /> <span>{{ capability }}</span> </li> } </ul> </div> <div> <p class="text-muted-foreground font-mono text-xs tracking-[0.14em] uppercase">What it costs you</p> <ul class="mt-3 space-y-2.5"> @for (constraint of current().constraints; track constraint) { <li class="flex items-start gap-2.5 text-sm"> <lucide-icon [img]="TriangleAlert" class="text-muted-foreground mt-0.5 size-4 shrink-0" aria-hidden="true" /> <span class="text-muted-foreground">{{ constraint }}</span> </li> } </ul> </div> </div> <ui-separator class="my-6" /> <p class="text-sm"> <span class="font-medium">Recommendation. </span> <span class="text-muted-foreground">{{ current().recommendation }}</span> </p> </div> </div> <button ui-button variant="ghost" class="mt-6">Read how the planner chooses</button> </div> </section> `, }) export class UiFeatureSegmentedCompareComponent { protected readonly Check = Check protected readonly TriangleAlert = TriangleAlert protected readonly allModes = modes @Input() set initialMode(v: string) { if (modes.some((m) => m.id === v)) this.active.set(v) } @Input('class') className?: string readonly active = signal(modes[0]!.id) readonly current = computed(() => modes.find((mode) => mode.id === this.active()) ?? modes[0]!) get rootClass(): string { return cn('bg-background', this.className) } onChange(value: unknown): void { if (typeof value === 'string' && value) this.active.set(value) } }src/app/components/blocks/feature-segmented-compare/index.ts0.1 kB
export { UiFeatureSegmentedCompareComponent, type ExecutionMode } from './feature-segmented-compare.component'
Raw manifest:https://uipkge.dev/r/angular/feature-segmented-compare.json