diff --git a/src/components/calculator/Calculator.tsx b/src/components/calculator/Calculator.tsx index fba8a8e..d041d32 100644 --- a/src/components/calculator/Calculator.tsx +++ b/src/components/calculator/Calculator.tsx @@ -1,9 +1,9 @@ /** - * Main Calculator component with tabbed interface. - * Combines all gemmological calculators in one place. + * Main Calculator component with grid layout. + * Shows all gemmological calculators in expandable cards. */ -import { useState } from 'react'; +import { useState, type ReactNode } from 'react'; import { SGCalculator } from './SGCalculator'; import { BirefringenceCalc } from './BirefringenceCalc'; import { CriticalAngleCalc } from './CriticalAngleCalc'; @@ -12,9 +12,9 @@ import { LengthConverter } from './LengthConverter'; import { TemperatureConverter } from './TemperatureConverter'; import { CaratEstimator } from './CaratEstimator'; import { RICalculator } from './RICalculator'; -import { cn } from '../ui/cn'; +import { CalculatorCard } from './CalculatorCard'; -type CalculatorTab = +type CalculatorId = | 'sg' | 'ri-lookup' | 'birefringence' @@ -24,24 +24,14 @@ type CalculatorTab = | 'length' | 'temperature'; -interface Tab { - id: CalculatorTab; +interface CalculatorConfig { + id: CalculatorId; label: string; - icon: string; + iconPath: string; description: string; + component: ReactNode; } -const TABS: Tab[] = [ - { id: 'sg', label: 'Specific Gravity', icon: 'scale', description: 'Calculate SG from hydrostatic weighing' }, - { id: 'ri-lookup', label: 'RI Lookup', icon: 'search', description: 'Find gems by refractive index' }, - { id: 'birefringence', label: 'Birefringence', icon: 'layers', description: 'Calculate birefringence from RI values' }, - { id: 'critical-angle', label: 'Critical Angle', icon: 'arrow-right', description: 'Calculate TIR critical angle' }, - { id: 'carat-estimate', label: 'Carat Estimate', icon: 'cube', description: 'Estimate weight from dimensions' }, - { id: 'weight', label: 'Weight', icon: 'weight', description: 'Convert ct/g/mg' }, - { id: 'length', label: 'Length', icon: 'ruler', description: 'Convert mm/inches' }, - { id: 'temperature', label: 'Temperature', icon: 'thermometer', description: 'Convert °C/°F' }, -]; - const ICON_PATHS: Record = { scale: 'M3 6l3 1m0 0l-3 9a5.002 5.002 0 006.001 0M6 7l3 9M6 7l6-2m6 2l3-1m-3 1l-3 9a5.002 5.002 0 006.001 0M18 7l3 9m-3-9l-6-2m0-2v2m0 16V5m0 16H9m3 0h3', search: 'M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z', @@ -53,131 +43,108 @@ const ICON_PATHS: Record = { thermometer: 'M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z', }; +const CALCULATORS: CalculatorConfig[] = [ + { + id: 'sg', + label: 'Specific Gravity', + iconPath: ICON_PATHS.scale, + description: 'Calculate SG from hydrostatic weighing', + component: , + }, + { + id: 'ri-lookup', + label: 'RI Lookup', + iconPath: ICON_PATHS.search, + description: 'Find gems by refractive index', + component: , + }, + { + id: 'birefringence', + label: 'Birefringence', + iconPath: ICON_PATHS.layers, + description: 'Calculate birefringence from RI values', + component: , + }, + { + id: 'critical-angle', + label: 'Critical Angle', + iconPath: ICON_PATHS['arrow-right'], + description: 'Calculate TIR critical angle', + component: , + }, + { + id: 'carat-estimate', + label: 'Carat Estimate', + iconPath: ICON_PATHS.cube, + description: 'Estimate weight from dimensions', + component: , + }, + { + id: 'weight', + label: 'Weight Converter', + iconPath: ICON_PATHS.weight, + description: 'Convert ct/g/mg', + component: , + }, + { + id: 'length', + label: 'Length Converter', + iconPath: ICON_PATHS.ruler, + description: 'Convert mm/inches', + component: , + }, + { + id: 'temperature', + label: 'Temperature Converter', + iconPath: ICON_PATHS.thermometer, + description: 'Convert °C/°F', + component: , + }, +]; + interface CalculatorProps { - defaultTab?: CalculatorTab; + /** Default expanded calculator (null = none expanded) */ + defaultExpanded?: CalculatorId | null; } -export function Calculator({ defaultTab = 'sg' }: CalculatorProps) { - const [activeTab, setActiveTab] = useState(defaultTab); +export function Calculator({ defaultExpanded = 'sg' }: CalculatorProps) { + const [expandedCalc, setExpandedCalc] = useState(defaultExpanded); - const renderCalculator = () => { - switch (activeTab) { - case 'sg': - return ; - case 'ri-lookup': - return ; - case 'birefringence': - return ; - case 'critical-angle': - return ; - case 'carat-estimate': - return ; - case 'weight': - return ; - case 'length': - return ; - case 'temperature': - return ; - default: - return null; - } + const handleToggle = (id: CalculatorId) => { + setExpandedCalc(prev => (prev === id ? null : id)); }; - const activeTabInfo = TABS.find(t => t.id === activeTab); - return ( -
- {/* Mobile: Dropdown selector */} -
- - +
+ {/* Introduction */} +
+

+ Select a calculator below to expand it. These tools help with common gemmological + calculations and conversions. +

- {/* Desktop: Sidebar tabs */} -
-
- -
+ {calc.component} + + ))}
- {/* Calculator content */} -
-
- {/* Header */} -
-

- {activeTabInfo?.label} -

-

- {activeTabInfo?.description} -

-
- - {/* Calculator body */} -
- {renderCalculator()} -
-
-
+ {/* Tip */} +

+ Tip: Click on any calculator header to expand or collapse it. +

); } diff --git a/src/components/calculator/CalculatorCard.tsx b/src/components/calculator/CalculatorCard.tsx new file mode 100644 index 0000000..87d35f6 --- /dev/null +++ b/src/components/calculator/CalculatorCard.tsx @@ -0,0 +1,116 @@ +/** + * Collapsible card wrapper for individual calculators. + * Used in the grid layout to show all calculators at once. + */ + +import type { ReactNode } from 'react'; +import { cn } from '../ui/cn'; + +interface CalculatorCardProps { + /** Unique identifier for the calculator */ + id: string; + /** Display title */ + title: string; + /** Short description of what this calculator does */ + description: string; + /** SVG path data for the icon */ + iconPath: string; + /** Whether the card is currently expanded */ + expanded: boolean; + /** Callback when the card header is clicked */ + onToggle: () => void; + /** The calculator component to render when expanded */ + children: ReactNode; +} + +export function CalculatorCard({ + id, + title, + description, + iconPath, + expanded, + onToggle, + children, +}: CalculatorCardProps) { + return ( +
+ {/* Clickable header */} + + + {/* Expandable content */} +
+
+
+ {children} +
+
+
+
+ ); +} diff --git a/src/components/calculator/index.ts b/src/components/calculator/index.ts index ef1811f..4f6b40a 100644 --- a/src/components/calculator/index.ts +++ b/src/components/calculator/index.ts @@ -22,3 +22,6 @@ export { // Result display export { ResultCard, ResultInline, ResultGroup } from './ResultCard'; + +// Layout +export { CalculatorCard } from './CalculatorCard';