diff --git a/package.json b/package.json index d8752aa..40e4085 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@curiousleaf/utils", "description": "A lightweight set of utilities", - "version": "1.0.26", + "version": "1.0.27", "license": "MIT", "type": "module", "author": { @@ -15,7 +15,7 @@ "scripts": { "build": "bun build ./src/index.ts --outdir ./dist && bun run build:declaration", "build:declaration": "tsc --emitDeclarationOnly", - "lint": "bun biome lint .", + "lint": "bun biome lint --apply .", "format": "bun biome format --write ." }, "dependencies": { diff --git a/src/helpers/helpers.ts b/src/helpers/helpers.ts index 0c71fcf..c53bbe3 100644 --- a/src/helpers/helpers.ts +++ b/src/helpers/helpers.ts @@ -175,11 +175,11 @@ export const setInputValue = ( ) => { const nativeInputValueSetter = Object.getOwnPropertyDescriptor( HTMLInputElement.prototype, - 'value', + "value", )?.set nativeInputValueSetter?.call(input, value) // Trigger a change event if the value was changed - triggerChange && input?.dispatchEvent(new Event('input', { bubbles: true })) + triggerChange && input?.dispatchEvent(new Event("input", { bubbles: true })) } diff --git a/src/numbers/numbers.test.ts b/src/numbers/numbers.test.ts index 8ad350f..651c2d0 100644 --- a/src/numbers/numbers.test.ts +++ b/src/numbers/numbers.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from "bun:test" -import { keepNumberInRange, parseNumericValue } from "./numbers" +import { keepNumberInRange, parseNumericValue, preciseRound } from "./numbers" describe("keepNumberInRange", () => { it("returns the same value if no range is specified", () => { @@ -34,6 +34,32 @@ describe("parseNumericValue", () => { }) it("returns the original string if it cannot be parsed", () => { - expect(parseNumericValue("not a number")).toBe("not a number") + expect(parseNumericValue("not a number")).toBe(undefined) + }) +}) + +describe("preciseRound", () => { + it("rounds to 2 decimal places by default", () => { + expect(preciseRound(2.345)).toEqual(2.35) + }) + + it("rounds to specified decimal places", () => { + expect(preciseRound(2.34567, 3)).toEqual(2.346) + }) + + it("handles rounding up at 0.5 exactly", () => { + expect(preciseRound(2.005, 2)).toEqual(2.01) + }) + + it("handles negative numbers", () => { + expect(preciseRound(-2.345)).toEqual(-2.34) + }) + + it("rounds to 0 decimal places correctly", () => { + expect(preciseRound(2.5, 0)).toEqual(3) + }) + + it("trims the number of decimal places if the value is an integer", () => { + expect(preciseRound(2.0000003, 2)).toEqual(2) }) }) diff --git a/src/numbers/numbers.ts b/src/numbers/numbers.ts index 03795ab..69e4618 100644 --- a/src/numbers/numbers.ts +++ b/src/numbers/numbers.ts @@ -25,11 +25,24 @@ export const keepNumberInRange = (value: number, min?: number, max?: number) => /** * Parse a string into a numeric value. - * @param string - The string to parse. - * @returns The parsed numeric value, or the original string if it cannot be parsed. + * @param value - The value to parse into a numeric value. + * @returns The parsed numeric value, or `undefined` if the value cannot be parsed. */ -export const parseNumericValue = (string: string) => { - const number = Number.parseFloat(string) +export const parseNumericValue = (value?: string | number | null) => { + if (value === undefined || value === null) return undefined + const parsed = Number.parseFloat(value.toString()) - return Number.isNaN(number) ? string : number + return Number.isNaN(parsed) ? undefined : parsed +} + +/** + * Rounds a number to a specified number of decimal places, with an adjustment for floating point precision. + * @param value - The number to round. + * @param decimals - The number of decimal places to round to. Defaults to 2. + * @returns The rounded number. + */ +export const preciseRound = (value: number, decimals = 2) => { + const factor = Math.pow(10, decimals) + + return Math.round((value + Number.EPSILON) * factor) / factor }