Technical Specs Sheet
blockecommerceComprehensive engineering technical specifications sheet with Metric/Imperial SI toggles, parameter category filters, search, and downloadable PDF trigger.
Installation
$pnpm dlx uipkge-ng@latest add technical-specs-sheet$npx uipkge-ng@latest add technical-specs-sheet$yarn dlx uipkge-ng@latest add technical-specs-sheet$bunx uipkge-ng@latest add technical-specs-sheetManifest:
https://uipkge.dev/r/angular/technical-specs-sheet.jsonInstalls to:src/app/components/blocks/technical-specs-sheet/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.
SpecIteminterface SpecItem {
id: string
category: SpecCategory
parameter: string
metricValue: string
imperialValue: string
tolerance?: string
testCondition?: string
highlight?: boolean
}CategoryOptioninterface CategoryOption {
id: 'all' | SpecCategory
label: string
}Files installed (2)
src/app/components/blocks/technical-specs-sheet/technical-specs-sheet.component.ts14.3 kB
// Laboratory-grade engineering specification sheet with Metric/Imperial SI // toggles, parameter category filters, search, and downloadable PDF trigger. // Port of the React block 1:1. import { ChangeDetectionStrategy, Component, Input, computed, signal } from '@angular/core' import { CheckCircle2, FileDown, Info, LucideAngularModule, Search, ShieldCheck } 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 { UiTableBodyComponent, UiTableCellComponent, UiTableComponent, UiTableHeadComponent, UiTableHeaderComponent, UiTableRowComponent, } from '@/ui/table/table.component' export type SpecCategory = 'acoustic' | 'electrical' | 'physical' | 'connectivity' | 'compliance' export interface SpecItem { id: string category: SpecCategory parameter: string metricValue: string imperialValue: string tolerance?: string testCondition?: string highlight?: boolean } const defaultSpecs: SpecItem[] = [ // Acoustic { id: 's1', category: 'acoustic', parameter: 'Transducer Type', metricValue: '50mm Planar-Magnetic with Neodymium N52 Array', imperialValue: '1.97 in Planar-Magnetic with Neodymium N52 Array', highlight: true, }, { id: 's2', category: 'acoustic', parameter: 'Frequency Response', metricValue: '5 Hz – 52,000 Hz', imperialValue: '5 Hz – 52,000 Hz', tolerance: '± 0.5 dB (20 Hz - 20 kHz)', highlight: true, }, { id: 's3', category: 'acoustic', parameter: 'Total Harmonic Distortion (THD)', metricValue: '< 0.04% @ 1 kHz, 100 dB SPL', imperialValue: '< 0.04% @ 1 kHz, 100 dB SPL', testCondition: 'IEC 60268-7 Standard', }, { id: 's4', category: 'acoustic', parameter: 'Sound Pressure Level (SPL)', metricValue: '106 dB / 1 mW @ 1 kHz', imperialValue: '106 dB / 1 mW @ 1 kHz', testCondition: 'Free-field calibrated', }, { id: 's5', category: 'acoustic', parameter: 'Acoustic Enclosure', metricValue: 'Open-Back CNC Milled Aircraft-Grade Aluminum Grille', imperialValue: 'Open-Back CNC Milled Aircraft-Grade Aluminum Grille', }, // Electrical { id: 's6', category: 'electrical', parameter: 'Nominal Impedance', metricValue: '32 Ω pure resistive load', imperialValue: '32 Ω pure resistive load', tolerance: '± 5% @ 1 kHz', highlight: true, }, { id: 's7', category: 'electrical', parameter: 'Maximum Input Power Handling', metricValue: '3,000 mW (3.0 W Continuous)', imperialValue: '3,000 mW (3.0 W Continuous)', testCondition: 'Thermal peak 5,000 mW (100ms)', }, { id: 's8', category: 'electrical', parameter: 'Recommended Amplifier Output', metricValue: '250 mW – 2,000 mW @ 32 Ω', imperialValue: '250 mW – 2,000 mW @ 32 Ω', }, // Physical { id: 's9', category: 'physical', parameter: 'Weight (excluding cable)', metricValue: '385 grams', imperialValue: '13.58 oz (0.85 lbs)', tolerance: '± 5g', highlight: true, }, { id: 's10', category: 'physical', parameter: 'Ear Cushion Dimensions (Inner ID)', metricValue: '68 mm (H) × 52 mm (W) × 26 mm (D)', imperialValue: '2.68 in (H) × 2.05 in (W) × 1.02 in (D)', }, { id: 's11', category: 'physical', parameter: 'Headband Clamping Force', metricValue: '4.2 Newtons', imperialValue: '0.94 lbf (Pound-force)', tolerance: '± 0.3 N', }, { id: 's12', category: 'physical', parameter: 'Earpad Material Composition', metricValue: 'Perforated Lambskin + Cooling Gel Memory Foam Core', imperialValue: 'Perforated Lambskin + Cooling Gel Memory Foam Core', }, // Connectivity { id: 's13', category: 'connectivity', parameter: 'Headphone Terminals', metricValue: 'Dual 3.5mm Gold-Plated TRRS Balanced Sockets', imperialValue: 'Dual 1/8 in Gold-Plated TRRS Balanced Sockets', }, { id: 's14', category: 'connectivity', parameter: 'Included Studio Cable', metricValue: '2.5 meter 8-Core Monocrystalline Silver-Plated Copper (OCC)', imperialValue: '8.20 feet 8-Core Monocrystalline Silver-Plated Copper (OCC)', highlight: true, }, { id: 's15', category: 'connectivity', parameter: 'Source Termination', metricValue: '4.4mm Pentaconn Balanced + 6.35mm Gold-Plated Adapter', imperialValue: '4.4mm Pentaconn Balanced + 1/4 in Gold-Plated Adapter', }, // Compliance { id: 's16', category: 'compliance', parameter: 'Certifications & Directives', metricValue: 'Hi-Res Audio Certified, CE Mark, FCC Class B, RoHS 3, WEEE', imperialValue: 'Hi-Res Audio Certified, CE Mark, FCC Class B, RoHS 3, WEEE', highlight: true, }, { id: 's17', category: 'compliance', parameter: 'Operating Temperature & Humidity', metricValue: '-10°C to +45°C (10% to 90% RH non-condensing)', imperialValue: '14°F to 113°F (10% to 90% RH non-condensing)', }, ] interface CategoryOption { id: 'all' | SpecCategory label: string } const categories: CategoryOption[] = [ { id: 'all', label: 'All Specifications' }, { id: 'acoustic', label: 'Acoustic Architecture' }, { id: 'electrical', label: 'Electrical & Power' }, { id: 'physical', label: 'Dimensions & Weight' }, { id: 'connectivity', label: 'Cables & Interconnects' }, { id: 'compliance', label: 'Standards & Compliance' }, ] @Component({ changeDetection: ChangeDetectionStrategy.Eager, selector: 'ui-technical-specs-sheet, [ui-technical-specs-sheet]', standalone: true, // React renders the root Card 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, UiTableBodyComponent, UiTableCellComponent, UiTableComponent, UiTableHeadComponent, UiTableHeaderComponent, UiTableRowComponent, ], template: ` <div ui-card data-slot="technical-specs-sheet" [class]="rootClass"> <div ui-card-header class="border-border bg-muted/20 border-b pb-4"> <div class="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between"> <div> <div class="flex items-center gap-2"> <span class="text-muted-foreground font-mono text-xs font-semibold tracking-wider uppercase"> {{ modelNumber }} · {{ revision }} </span> <span ui-badge variant="outline" class="text-success font-mono text-xs"> Laboratory Certified </span> </div> <h3 ui-card-title class="text-foreground text-lg font-semibold tracking-tight sm:text-xl"> Engineering & Technical Specifications Sheet </h3> <p ui-card-description class="text-xs"> Comprehensive acoustic measurements, electrical parameters, material tolerances, and compliance standards. </p> </div> <div class="flex flex-wrap items-center gap-2.5"> <!-- Metric / Imperial Toggle --> <div class="border-border bg-background flex items-center rounded-lg border p-0.5 text-xs shadow-2xs"> <button type="button" [class]="unitToggleClass(isMetric())" (click)="isMetric.set(true)"> Metric (SI) </button> <button type="button" [class]="unitToggleClass(!isMetric())" (click)="isMetric.set(false)"> Imperial (US) </button> </div> <button ui-button variant="outline" size="sm" class="gap-1.5 text-xs shadow-2xs" (click)="downloadPdf()"> <lucide-icon [img]="FileDown" class="size-3.5" /> <span>Download Spec PDF</span> </button> </div> </div> <!-- Controls: Category Filter Tabs & Search Bar --> <div class="mt-4 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between"> <div class="flex flex-wrap items-center gap-1.5"> @for (cat of categoryOptions; track cat.id) { <button type="button" [class]="categoryClass(cat.id)" (click)="activeCategory.set(cat.id)"> {{ cat.label }} </button> } </div> <div class="relative w-full sm:w-64"> <lucide-icon [img]="Search" class="text-muted-foreground absolute top-1/2 left-2.5 size-3.5 -translate-y-1/2" /> <ui-input [value]="searchQuery()" (valueChange)="searchQuery.set($event)" placeholder="Search parameters..." class="h-8 pl-8 text-xs" /> </div> </div> </div> <div ui-card-content class="p-0"> <table ui-table> <thead ui-table-header> <tr ui-table-row class="bg-muted/40"> <th ui-table-head class="w-[280px]">Parameter / Measurement</th> <th ui-table-head>Specification ({{ isMetric() ? 'Metric' : 'Imperial' }})</th> <th ui-table-head class="w-[180px]">Tolerance / Range</th> <th ui-table-head class="w-[220px]">Test Standard / Method</th> </tr> </thead> <tbody ui-table-body> @for (spec of filteredSpecs(); track spec.id) { <tr ui-table-row [class]="spec.highlight ? 'bg-primary/5' : ''"> <td ui-table-cell> <div class="flex items-center gap-2"> <span class="text-foreground text-xs font-medium">{{ spec.parameter }}</span> @if (spec.highlight) { <span ui-badge variant="secondary" class="h-4.5 px-1.5 font-mono text-xs font-semibold uppercase"> Key Spec </span> } </div> </td> <td ui-table-cell> <span class="text-foreground font-mono text-xs font-semibold"> {{ isMetric() ? spec.metricValue : spec.imperialValue }} </span> </td> <td ui-table-cell> <span class="text-muted-foreground font-mono text-xs">{{ spec.tolerance || '—' }}</span> </td> <td ui-table-cell> <span class="text-muted-foreground text-xs">{{ spec.testCondition || 'Factory Standard Q/C' }}</span> </td> </tr> } @if (filteredSpecs().length === 0) { <tr ui-table-row> <td ui-table-cell [attr.colspan]="4" class="text-muted-foreground h-28 text-center text-xs"> No matching specifications found for "{{ searchQuery() }}". </td> </tr> } </tbody> </table> <!-- Bottom Compliance Certification Footer Badges --> <div class="border-border bg-muted/20 flex flex-wrap items-center justify-between gap-3 border-t p-4 text-xs"> <div class="text-muted-foreground flex flex-wrap items-center gap-4"> <div class="flex items-center gap-1.5"> <lucide-icon [img]="ShieldCheck" class="text-primary size-4" /> <span class="text-foreground font-medium">Calibrated Precision</span> </div> <div class="flex items-center gap-1.5"> <lucide-icon [img]="CheckCircle2" class="text-success size-4" /> <span>3-Year Studio Warranty Included</span> </div> <div class="flex items-center gap-1.5"> <lucide-icon [img]="Info" class="text-info size-4" /> <span>Serial matched drivers (± 0.3 dB stereo tracking)</span> </div> </div> <span class="text-muted-foreground font-mono text-xs"> Document ID: SPEC-APX-{{ modelNumber }}-2026 </span> </div> </div> </div> `, }) export class UiTechnicalSpecsSheetComponent { protected readonly CheckCircle2 = CheckCircle2 protected readonly FileDown = FileDown protected readonly Info = Info protected readonly Search = Search protected readonly ShieldCheck = ShieldCheck protected readonly categoryOptions = categories @Input() productTitle = 'Apex Pro Reference Planar-Magnetic Studio Monitor Headphones' @Input() modelNumber = 'APX-950-PRO' @Input() revision = 'Rev 2.4 (2026)' @Input() set specs(v: SpecItem[] | undefined) { this.specsSignal.set(v ?? defaultSpecs) } @Input('class') className?: string readonly specsSignal = signal<SpecItem[]>(defaultSpecs) readonly isMetric = signal(true) readonly activeCategory = signal<'all' | SpecCategory>('all') readonly searchQuery = signal('') readonly filteredSpecs = computed(() => { const cat = this.activeCategory() const q = this.searchQuery().trim().toLowerCase() return this.specsSignal().filter((item) => { const matchesCat = cat === 'all' || item.category === cat const matchesSearch = q === '' || item.parameter.toLowerCase().includes(q) || item.metricValue.toLowerCase().includes(q) || item.imperialValue.toLowerCase().includes(q) return matchesCat && matchesSearch }) }) get rootClass(): string { return cn('border-border w-full shadow-xs', this.className) } unitToggleClass(active: boolean): string { return cn( 'rounded-md px-2.5 py-1 font-medium transition-colors', active ? 'bg-primary text-primary-foreground font-semibold shadow-2xs' : 'text-muted-foreground hover:text-foreground', ) } categoryClass(id: 'all' | SpecCategory): string { return cn( 'rounded-md px-2.5 py-1 text-xs font-medium transition-colors', this.activeCategory() === id ? 'bg-foreground text-background shadow-2xs' : 'bg-muted/60 text-muted-foreground hover:bg-muted hover:text-foreground border border-transparent', ) } downloadPdf(): void { alert(`Downloading official engineering data sheet for ${this.modelNumber} (${this.revision})...`) } }src/app/components/blocks/technical-specs-sheet/index.ts0.1 kB
export { UiTechnicalSpecsSheetComponent, type SpecCategory, type SpecItem } from './technical-specs-sheet.component'
Raw manifest:https://uipkge.dev/r/angular/technical-specs-sheet.json