Created
December 15, 2024 23:31
-
-
Save BrunoWinck/40478fa66e56948c04cc0d7697e6d1d9 to your computer and use it in GitHub Desktop.
Web component for units conversion
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| class KnvUnit extends HTMLElement { | |
| constructor() { | |
| super(); | |
| this.attachShadow({ mode: 'open' }); | |
| } | |
| connectedCallback() { | |
| this.render(); | |
| } | |
| // Parse and decode the inner text to extract the value and unit | |
| parseContent() { | |
| const content = this.textContent.trim(); | |
| const match = content.match(/^(\d+(?:\.\d+)?)\s*([a-zA-Z°]+)$/); | |
| if (!match) { | |
| return null; // Invalid format | |
| } | |
| const [, value, unit] = match; | |
| return { value: parseFloat(value), unit: unit.toLowerCase() }; | |
| } | |
| render() { | |
| const parsed = this.parseContent(); | |
| if (!parsed) { | |
| this.shadowRoot.innerHTML = ` | |
| <style> | |
| span { | |
| font-family: Arial, sans-serif; | |
| font-size: 1rem; | |
| color: red; | |
| } | |
| </style> | |
| <span>Invalid input format</span> | |
| `; | |
| return; | |
| } | |
| const { value, unit } = parsed; | |
| const conversion = this.convertToImperial(value, unit); | |
| // don't issue warning, just ignore | |
| const imperialValue = conversion ? `${this.toFraction(conversion.value)} ${conversion.unit}` : ''; | |
| this.shadowRoot.innerHTML = ` | |
| <style> | |
| span { | |
| font-family: Arial, sans-serif; | |
| font-size: 1rem; | |
| color: #333; | |
| } | |
| </style> | |
| <span>${value} ${unit} (${imperialValue})</span> | |
| `; | |
| } | |
| convertToImperial(value, unit) { | |
| switch (unit) { | |
| case 'kg': // Kilograms to Pounds | |
| return { value: (value * 2.20462), unit: 'lbs' }; | |
| case 'm': // Meters to Feet | |
| return { value: (value * 3.28084), unit: 'ft' }; | |
| case 'cm': // Centimeters to Inches | |
| return { value: (value * 0.393701), unit: 'in' }; | |
| case '°c': // Celsius to Fahrenheit | |
| return { value: ((value * 9) / 5 + 32), unit: '°F' }; | |
| case 'l': // Liters to Gallons | |
| return { value: (value * 0.264172), unit: 'gal' }; | |
| case 'km': // Kilometers to Miles | |
| return { value: (value * 0.621371), unit: 'mi' }; | |
| case 'g': // Grams to Ounces | |
| return { value: (value * 0.035274), unit: 'oz' }; | |
| default: | |
| return null; // Unsupported unit | |
| } | |
| } | |
| toFraction(n0 ) | |
| { | |
| // adapted from https://stackoverflow.com/questions/55495386/how-to-convert-decimals-to-the-nearest-fraction | |
| // set as args with default values | |
| const acceptableDenominators = [1, 2, 4, 8 ]; | |
| const maxDistanceToNumerator1 = 0.05; | |
| const negative = (n0 < 0); | |
| const n1 = (negative)?-n0:n0; | |
| // it's a proportional error we tolerate | |
| const maxDistanceToNumerator = n1 * maxDistanceToNumerator1; | |
| const wholePart = Math.floor(n1); | |
| const n = n1 - wholePart; | |
| const denominator = acceptableDenominators.find(d => | |
| Math.abs(d * n - Math.round(d * n)) <= maxDistanceToNumerator | |
| ); | |
| // No good solution keep decimals | |
| if (typeof denominator === 'undefined') { | |
| return n0.toFixed(2); | |
| } | |
| const numerator = Math.round(denominator * n); | |
| if (denominator === 1) { | |
| // I don't understand this case, go to next integer value? | |
| return "" + (wholePart + numerator) * (negative ? -1 : 1); | |
| } | |
| return (negative ? "-" : "") + | |
| (wholePart ? wholePart + " " : "") + | |
| numerator + "/" + denominator; | |
| } | |
| } | |
| // Register the custom element | |
| customElements.define('knv-unit', KnvUnit); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment