Skip to content

Commit febc03f

Browse files
Merge pull request #186 from Shaily-62/unit-converter
Implement unit converter with Length, Temperature, and Weight categories
2 parents 90a3eb8 + 45a90b2 commit febc03f

File tree

3 files changed

+547
-45
lines changed

3 files changed

+547
-45
lines changed

projects/unit-converter/index.html

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,50 @@
1-
<!doctype html>
1+
<!DOCTYPE html>
22
<html lang="en">
3+
34
<head>
4-
<meta charset="utf-8" />
5-
<meta name="viewport" content="width=device-width, initial-scale=1" />
6-
<title>Unit Converter | Vanilla Verse</title>
7-
<link rel="stylesheet" href="styles.css" />
5+
<meta charset="UTF-8">
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
7+
<title>Unit Converter</title>
8+
<link rel="stylesheet" href="styles.css">
89
</head>
10+
911
<body>
10-
<header><h1>Unit Converter</h1></header>
11-
<main id="app">
12-
<!-- TODO: Select category (Length, Temperature, Weight) -->
13-
<!-- TODO: Inputs: from-value, from-unit, to-unit; show converted result live -->
14-
<!-- TODO: Add helpful hints and rounding options -->
15-
</main>
16-
<script defer src="main.js"></script>
12+
<main class="container">
13+
<button class="theme-toggle" id="themeToggle" aria-label="Toggle theme">
14+
<span id="themeIcon">🌙</span>
15+
</button>
16+
17+
<h1>Unit Converter</h1>
18+
19+
<div class="category-selector">
20+
<label for="category">Category</label>
21+
<select id="category">
22+
<option value="length">Length</option>
23+
<option value="temperature">Temperature</option>
24+
<option value="weight">Weight</option>
25+
</select>
26+
</div>
27+
28+
<div class="converter-box">
29+
<div class="input-group">
30+
<input type="number" id="input1" placeholder="0" step="any">
31+
<select class="unit-select" id="unit1"></select>
32+
</div>
33+
</div>
34+
35+
<button class="swap-btn" id="swapBtn">
36+
<span class="swap-icon"></span>
37+
Swap Units
38+
</button>
39+
40+
<div class="converter-box">
41+
<div class="input-group">
42+
<input type="number" id="input2" placeholder="0" step="any">
43+
<select class="unit-select" id="unit2"></select>
44+
</div>
45+
</div>
46+
</main>
47+
<script src="main.js"></script>
1748
</body>
18-
</html>
49+
50+
</html>

projects/unit-converter/main.js

Lines changed: 288 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,289 @@
1+
2+
// ============================================
3+
// CONFIGURATION & DATA
4+
// ============================================
5+
6+
const conversionData = {
7+
length: {
8+
units: ['m', 'km', 'mi', 'ft'],
9+
toBase: {
10+
m: 1,
11+
km: 1000,
12+
mi: 1609.344,
13+
ft: 0.3048
14+
}
15+
},
16+
temperature: {
17+
units: ['C', 'F', 'K'],
18+
convert: (value, from, to) => {
19+
if (from === to) return value;
20+
21+
// Convert to Celsius first
22+
let celsius;
23+
if (from === 'C') celsius = value;
24+
else if (from === 'F') celsius = (value - 32) * 5 / 9;
25+
else if (from === 'K') celsius = value - 273.15;
26+
27+
// Convert from Celsius to target
28+
if (to === 'C') return celsius;
29+
if (to === 'F') return celsius * 9 / 5 + 32;
30+
if (to === 'K') return celsius + 273.15;
31+
}
32+
},
33+
weight: {
34+
units: ['g', 'kg', 'lb', 'oz'],
35+
toBase: {
36+
g: 1,
37+
kg: 1000,
38+
lb: 453.59237,
39+
oz: 28.349523125
40+
}
41+
}
42+
};
43+
44+
// ============================================
45+
// STATE MANAGEMENT
46+
// ============================================
47+
48+
const state = {
49+
category: 'length',
50+
unit1: 'm',
51+
unit2: 'km',
52+
value1: '',
53+
value2: '',
54+
theme: 'light',
55+
isUpdating: false
56+
};
57+
58+
// ============================================
59+
// DOM REFERENCES
60+
// ============================================
61+
62+
const elements = {
63+
category: document.getElementById('category'),
64+
input1: document.getElementById('input1'),
65+
input2: document.getElementById('input2'),
66+
unit1: document.getElementById('unit1'),
67+
unit2: document.getElementById('unit2'),
68+
swapBtn: document.getElementById('swapBtn'),
69+
themeToggle: document.getElementById('themeToggle'),
70+
themeIcon: document.getElementById('themeIcon'),
71+
html: document.documentElement
72+
};
73+
74+
// ============================================
75+
// UTILITY FUNCTIONS
76+
// ============================================
77+
78+
/**
79+
* Format number with sensible rounding (max 6 decimals, no trailing zeros)
80+
*/
81+
function formatNumber(num) {
82+
if (num === null || num === undefined || num === '' || isNaN(num) || !isFinite(num)) {
83+
return '';
84+
}
85+
return parseFloat(parseFloat(num).toFixed(6)).toString();
86+
}
87+
88+
/**
89+
* Convert value between units
90+
*/
91+
function convert(value, fromUnit, toUnit, category) {
92+
if (value === '' || value === null || value === undefined || isNaN(value)) {
93+
return '';
94+
}
95+
96+
const numValue = parseFloat(value);
97+
const data = conversionData[category];
98+
99+
if (category === 'temperature') {
100+
return data.convert(numValue, fromUnit, toUnit);
101+
}
102+
103+
// For length and weight: convert through base unit
104+
const baseValue = numValue * data.toBase[fromUnit];
105+
return baseValue / data.toBase[toUnit];
106+
}
107+
108+
/**
109+
* Update state and persist
110+
*/
111+
function updateState(updates) {
112+
Object.assign(state, updates);
113+
}
114+
115+
// ============================================
116+
// THEME MANAGEMENT
117+
// ============================================
118+
119+
function setTheme(theme) {
120+
updateState({ theme });
121+
122+
if (theme === 'dark') {
123+
elements.html.setAttribute('data-theme', 'dark');
124+
elements.themeIcon.textContent = '☀️';
125+
} else {
126+
elements.html.removeAttribute('data-theme');
127+
elements.themeIcon.textContent = '🌙';
128+
}
129+
}
130+
131+
function toggleTheme() {
132+
setTheme(state.theme === 'light' ? 'dark' : 'light');
133+
}
134+
135+
// ============================================
136+
// UNIT MANAGEMENT
137+
// ============================================
138+
1139
/**
2-
* TODO: Unit converter logic
3-
* Categories:
4-
* - Length: m, km, cm, mm, mi, yd, ft, in
5-
* - Temperature: C, F, K
6-
* - Weight: g, kg, lb, oz
7-
* Implementation:
8-
* - Define conversion maps/functions
9-
* - Convert on input/change events; show precise and rounded values
10-
* Optional: Persist last category/units to localStorage
11-
*/
12-
13-
document.addEventListener('DOMContentLoaded', () => {
14-
console.log('Unit converter ready');
15-
// TODO: Build conversion functions and wire to form
16-
});
140+
* Populate unit dropdowns based on selected category
141+
*/
142+
function populateUnitOptions(category) {
143+
const units = conversionData[category].units;
144+
145+
// Clear existing options
146+
elements.unit1.innerHTML = '';
147+
elements.unit2.innerHTML = '';
148+
149+
// Add new options
150+
units.forEach(unit => {
151+
elements.unit1.add(new Option(unit, unit));
152+
elements.unit2.add(new Option(unit, unit));
153+
});
154+
155+
// Set default selections
156+
elements.unit1.value = units[0];
157+
elements.unit2.value = units[Math.min(1, units.length - 1)];
158+
159+
updateState({
160+
unit1: elements.unit1.value,
161+
unit2: elements.unit2.value
162+
});
163+
}
164+
165+
/**
166+
* Update unit options when category changes
167+
*/
168+
function handleCategoryChange() {
169+
const category = elements.category.value;
170+
updateState({ category, value1: '', value2: '' });
171+
172+
populateUnitOptions(category);
173+
174+
// Clear inputs
175+
elements.input1.value = '';
176+
elements.input2.value = '';
177+
}
178+
179+
// ============================================
180+
// CONVERSION LOGIC
181+
// ============================================
182+
183+
/**
184+
* Perform conversion from input1 to input2
185+
*/
186+
function convertFromInput1() {
187+
if (state.isUpdating) return;
188+
189+
state.isUpdating = true;
190+
191+
const value = elements.input1.value;
192+
const result = convert(value, state.unit1, state.unit2, state.category);
193+
194+
elements.input2.value = formatNumber(result);
195+
updateState({ value1: value, value2: elements.input2.value });
196+
197+
state.isUpdating = false;
198+
}
199+
200+
/**
201+
* Perform conversion from input2 to input1
202+
*/
203+
function convertFromInput2() {
204+
if (state.isUpdating) return;
205+
206+
state.isUpdating = true;
207+
208+
const value = elements.input2.value;
209+
const result = convert(value, state.unit2, state.unit1, state.category);
210+
211+
elements.input1.value = formatNumber(result);
212+
updateState({ value1: elements.input1.value, value2: value });
213+
214+
state.isUpdating = false;
215+
}
216+
217+
/**
218+
* Handle unit selection changes
219+
*/
220+
function handleUnitChange(changedUnit) {
221+
// Update state
222+
if (changedUnit === 'unit1') {
223+
updateState({ unit1: elements.unit1.value });
224+
} else {
225+
updateState({ unit2: elements.unit2.value });
226+
}
227+
228+
// Recalculate based on which input has a value
229+
if (elements.input1.value !== '') {
230+
convertFromInput1();
231+
} else if (elements.input2.value !== '') {
232+
convertFromInput2();
233+
}
234+
}
235+
236+
/**
237+
* Swap units and values
238+
*/
239+
function handleSwap() {
240+
// Swap units
241+
const tempUnit = state.unit1;
242+
updateState({ unit1: state.unit2, unit2: tempUnit });
243+
elements.unit1.value = state.unit1;
244+
elements.unit2.value = state.unit2;
245+
246+
// Swap values
247+
const tempValue = elements.input1.value;
248+
elements.input1.value = elements.input2.value;
249+
elements.input2.value = tempValue;
250+
251+
updateState({
252+
value1: elements.input1.value,
253+
value2: elements.input2.value
254+
});
255+
}
256+
257+
// ============================================
258+
// INITIALIZATION
259+
// ============================================
260+
261+
/**
262+
* Set up all event listeners
263+
*/
264+
function initEventListeners() {
265+
elements.category.addEventListener('change', handleCategoryChange);
266+
elements.input1.addEventListener('input', convertFromInput1);
267+
elements.input2.addEventListener('input', convertFromInput2);
268+
elements.unit1.addEventListener('change', () => handleUnitChange('unit1'));
269+
elements.unit2.addEventListener('change', () => handleUnitChange('unit2'));
270+
elements.swapBtn.addEventListener('click', handleSwap);
271+
elements.themeToggle.addEventListener('click', toggleTheme);
272+
}
273+
274+
/**
275+
* Initialize the application
276+
*/
277+
function init() {
278+
// Set up initial category
279+
populateUnitOptions(state.category);
280+
281+
// Set up event listeners
282+
initEventListeners();
283+
284+
// Set initial theme
285+
setTheme(state.theme);
286+
}
287+
288+
// Start the application
289+
init();

0 commit comments

Comments
 (0)