UNPKG

@zeix/ui-element

Version:

UIElement - a HTML-first library for reactive Web Components

122 lines (109 loc) 3.65 kB
import type { AttributeParser } from '../component' /* === Internal Function === */ const parseNumber = ( parseFn: (v: string) => number, value: string | null | undefined, ): number | undefined => { if (value == null) return const parsed = parseFn(value) return Number.isFinite(parsed) ? parsed : undefined } /* === Exported Functions === */ /** * Parse a boolean attribute as an actual boolean value * * @since 0.13.1 * @returns {AttributeParser<boolean>} */ const asBoolean = (): AttributeParser<boolean> => (_: HTMLElement, value: string | null | undefined): boolean => value != null && value !== 'false' /** * Parse an attribute as as number forced to integer with a fallback * * Supports hexadecimal and scientific notation * * @since 0.11.0 * @param {number} [fallback=0] - fallback value * @returns {AttributeParser<number>} parser function */ const asInteger = (fallback: number = 0): AttributeParser<number> => (_: HTMLElement, value: string | null | undefined): number => { if (value == null) return fallback // Handle hexadecimal notation const trimmed = value.trim() if (trimmed.toLowerCase().startsWith('0x')) return parseNumber(v => parseInt(v, 16), trimmed) ?? fallback // Handle other formats (including scientific notation) const parsed = parseNumber(parseFloat, value) return parsed != null ? Math.trunc(parsed) : fallback } /** * Parse an attribute as as number with a fallback * * @since 0.11.0 * @param {number} [fallback=0] - fallback value * @returns {AttributeParser<number>} parser function */ const asNumber = (fallback: number = 0): AttributeParser<number> => (_: HTMLElement, value: string | null | undefined): number => parseNumber(parseFloat, value) ?? fallback /** * Parse an attribute as a string with a fallback * * @since 0.11.0 * @param {string} [fallback=''] - fallback value * @returns {AttributeParser<string>} parser function */ const asString = (fallback: string = ''): AttributeParser<string> => (_: HTMLElement, value: string | null | undefined): string => value ?? fallback /** * Parse an attribute as a multi-state value (for examnple: true, false, mixed), defaulting to the first valid option * * @since 0.9.0 * @param {string[]} valid - array of valid values * @returns {AttributeParser<string>} parser function */ const asEnum = (valid: [string, ...string[]]): AttributeParser<string> => (_: HTMLElement, value: string | null | undefined): string => { if (value == null) return valid[0] const lowerValue = value.toLowerCase() const matchingValid = valid.find(v => v.toLowerCase() === lowerValue) return matchingValid ? value : valid[0] } /** * Parse an attribute as a JSON serialized object with a fallback * * @since 0.11.0 * @param {T} fallback - fallback value * @returns {AttributeParser<T>} parser function * @throws {ReferenceError} if the value and fallback are both null or undefined * @throws {SyntaxError} if the value is not a valid JSON object */ const asJSON = <T extends {}>(fallback: T): AttributeParser<T> => (_: HTMLElement, value: string | null | undefined): T => { if ((value ?? fallback) == null) throw new ReferenceError( 'Value and fallback are both null or undefined', ) if (value == null) return fallback if (value === '') throw new SyntaxError('Empty string is not valid JSON') let result: T | undefined try { result = JSON.parse(value) } catch (error) { throw new SyntaxError(`Failed to parse JSON: ${String(error)}`, { cause: error, }) } return result ?? fallback } export { asBoolean, asInteger, asNumber, asString, asEnum, asJSON }