Files
units-ui/lib/units.ts
Sebastian Krüger 1943974908 fix: use direct hex colors for visual comparison bars
Fixed the colored bar segments not showing:

🎨 Direct Color Implementation:
- Added getCategoryColorHex() function to return actual hex values
- Changed from CSS variables to direct backgroundColor
- No more var(--color-*) that wasn't resolving
- Direct hex colors like #3B82F6 for length, #10B981 for mass, etc.

 Visual Improvements:
- Taller bars (h-8, 32px) for better visibility
- Drop shadow on percentage labels for readability
- White text on bars >30% filled
- Foreground color text on smaller bars
- pointer-events-none on overlay to prevent interaction issues

🔧 Updated Components:
- MainConverter: Import and use getCategoryColorHex()
- VisualComparison: Accept hex color string directly
- lib/units: Added getCategoryColorHex() with all 23 colors

The bars will now definitely show with vibrant colors!

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-08 10:31:35 +01:00

252 lines
5.5 KiB
TypeScript

/**
* Unit conversion service wrapper for convert-units library
* Provides type-safe conversion utilities and metadata
*/
import convert from 'convert-units';
export type Measure =
| 'angle'
| 'apparentPower'
| 'area'
| 'current'
| 'digital'
| 'each'
| 'energy'
| 'frequency'
| 'illuminance'
| 'length'
| 'mass'
| 'pace'
| 'partsPer'
| 'power'
| 'pressure'
| 'reactiveEnergy'
| 'reactivePower'
| 'speed'
| 'temperature'
| 'time'
| 'voltage'
| 'volume'
| 'volumeFlowRate';
export interface UnitInfo {
abbr: string;
measure: Measure;
system: 'metric' | 'imperial' | 'bits' | 'bytes' | string;
singular: string;
plural: string;
}
export interface ConversionResult {
value: number;
unit: string;
unitInfo: UnitInfo;
}
/**
* Get all available measures/categories
*/
export function getAllMeasures(): Measure[] {
return convert().measures() as Measure[];
}
/**
* Get all units for a specific measure
*/
export function getUnitsForMeasure(measure: Measure): string[] {
return convert().possibilities(measure);
}
/**
* Get detailed information about a unit
*/
export function getUnitInfo(unit: string): UnitInfo | null {
try {
const description = convert().describe(unit);
return description as UnitInfo;
} catch {
return null;
}
}
/**
* Convert a value from one unit to another
*/
export function convertUnit(
value: number,
fromUnit: string,
toUnit: string
): number {
try {
return convert(value).from(fromUnit).to(toUnit);
} catch (error) {
console.error('Conversion error:', error);
return value;
}
}
/**
* Convert a value to all compatible units in the same measure
*/
export function convertToAll(
value: number,
fromUnit: string
): ConversionResult[] {
try {
const unitInfo = getUnitInfo(fromUnit);
if (!unitInfo) return [];
const compatibleUnits = getUnitsForMeasure(unitInfo.measure);
return compatibleUnits.map(toUnit => {
const convertedValue = convertUnit(value, fromUnit, toUnit);
const toUnitInfo = getUnitInfo(toUnit);
return {
value: convertedValue,
unit: toUnit,
unitInfo: toUnitInfo!,
};
});
} catch (error) {
console.error('Conversion error:', error);
return [];
}
}
/**
* Get category color for a measure (Tailwind class name)
*/
export function getCategoryColor(measure: Measure): string {
const colorMap: Record<Measure, string> = {
angle: 'category-angle',
apparentPower: 'category-apparent-power',
area: 'category-area',
current: 'category-current',
digital: 'category-digital',
each: 'category-each',
energy: 'category-energy',
frequency: 'category-frequency',
illuminance: 'category-illuminance',
length: 'category-length',
mass: 'category-mass',
pace: 'category-pace',
partsPer: 'category-parts-per',
power: 'category-power',
pressure: 'category-pressure',
reactiveEnergy: 'category-reactive-energy',
reactivePower: 'category-reactive-power',
speed: 'category-speed',
temperature: 'category-temperature',
time: 'category-time',
voltage: 'category-voltage',
volume: 'category-volume',
volumeFlowRate: 'category-volume-flow-rate',
};
return colorMap[measure];
}
/**
* Get category color hex value for a measure
*/
export function getCategoryColorHex(measure: Measure): string {
const colorMap: Record<Measure, string> = {
angle: '#0EA5E9',
apparentPower: '#8B5CF6',
area: '#F59E0B',
current: '#F59E0B',
digital: '#06B6D4',
each: '#64748B',
energy: '#EAB308',
frequency: '#A855F7',
illuminance: '#84CC16',
length: '#3B82F6',
mass: '#10B981',
pace: '#14B8A6',
partsPer: '#EC4899',
power: '#F43F5E',
pressure: '#6366F1',
reactiveEnergy: '#D946EF',
reactivePower: '#E879F9',
speed: '#10B981',
temperature: '#EF4444',
time: '#7C3AED',
voltage: '#FB923C',
volume: '#8B5CF6',
volumeFlowRate: '#22D3EE',
};
return colorMap[measure];
}
/**
* Format measure name for display
*/
export function formatMeasureName(measure: Measure): string {
const nameMap: Record<Measure, string> = {
angle: 'Angle',
apparentPower: 'Apparent Power',
area: 'Area',
current: 'Current',
digital: 'Digital Storage',
each: 'Each',
energy: 'Energy',
frequency: 'Frequency',
illuminance: 'Illuminance',
length: 'Length',
mass: 'Mass',
pace: 'Pace',
partsPer: 'Parts Per',
power: 'Power',
pressure: 'Pressure',
reactiveEnergy: 'Reactive Energy',
reactivePower: 'Reactive Power',
speed: 'Speed',
temperature: 'Temperature',
time: 'Time',
voltage: 'Voltage',
volume: 'Volume',
volumeFlowRate: 'Volume Flow Rate',
};
return nameMap[measure];
}
/**
* Search units by query string (fuzzy search)
*/
export function searchUnits(query: string): UnitInfo[] {
if (!query) return [];
const allMeasures = getAllMeasures();
const results: UnitInfo[] = [];
const lowerQuery = query.toLowerCase();
for (const measure of allMeasures) {
const units = getUnitsForMeasure(measure);
for (const unit of units) {
const info = getUnitInfo(unit);
if (!info) continue;
const searchableText = [
info.abbr,
info.singular,
info.plural,
measure,
formatMeasureName(measure),
]
.join(' ')
.toLowerCase();
if (searchableText.includes(lowerQuery)) {
results.push(info);
}
}
}
return results;
}