@zeix/ui-element
Version:
UIElement - minimal reactive framework based on Web Components
108 lines (97 loc) • 3.22 kB
text/typescript
import type { AttributeParser } from '../component'
/* === Internal Function === */
const parseNumber = (
parseFn: (v: string) => number,
value: string | null,
): 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.7.0
* @param {C} _ - host element
* @param {string} value - maybe string value
* @returns {boolean}
*/
const asBoolean: AttributeParser<HTMLElement, boolean> = (
_: HTMLElement,
value: string | null,
): boolean => value !== 'false' && value != null
/**
* Parse an attribute as as number forced to integer with a fallback
*
* @since 0.11.0
* @param {number} [fallback=0] - fallback value
* @returns {Parser<HTMLElement, number>} - parser function
*/
const asInteger =
(fallback: number = 0): AttributeParser<HTMLElement, number> =>
(_: HTMLElement, value: string | null): number =>
parseNumber(parseInt, value) ?? fallback
/**
* Parse an attribute as as number with a fallback
*
* @since 0.11.0
* @param {number} [fallback=0] - fallback value
* @returns {Parser<number, HTMLElement>} - parser function
*/
const asNumber =
(fallback: number = 0): AttributeParser<HTMLElement, number> =>
(_: HTMLElement, value: string | null): number =>
parseNumber(parseFloat, value) ?? fallback
/**
* Parse an attribute as a string with a fallback
*
* @since 0.11.0
* @param {string} [fallback=''] - fallback value
* @returns {Parser<string, HTMLElement>} - parser function
*/
const asString =
(fallback: string = ''): AttributeParser<HTMLElement, string> =>
(_: HTMLElement, value: string | null): 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 {Parser<string, HTMLElement>} - parser function
*/
const asEnum =
(valid: [string, ...string[]]): AttributeParser<HTMLElement, string> =>
(_: HTMLElement, value: string | null): string =>
value != null && valid.includes(value.toLowerCase()) ? value : valid[0]
/**
* Parse an attribute as a JSON serialized object with a fallback
*
* @since 0.11.0
* @param {T} fallback - fallback value
* @returns {Parser<T, HTMLElement>} - 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<HTMLElement, T> =>
(_: HTMLElement, value: string | null): 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 }