Tuesday, December 31, 2024

Multiple className values in React, pt. 2

When dealing with an important React project at work, I was manipulating multiple CSS Modules class names with a function I wrote 2 and a half years ago, accepting an object for conditionals.

The early hours of the morning of this last day of the year brought me an idea for a shorter, simpler syntax that works better for imported CSS Modules names:

/**
 * Generates the `className` attribute with the given class names.
 *
 * @example
 * cn( 'abc' );
 * cn( 'abc', 'def' );
 * cn( 'abc', ['def', true] );
 */
export function cn(...items: (string | null | undefined | [string, boolean])[]): string {
	let fin = '';
	for (let i = 0; i < items.length; ++i) {
		const item = items[i];
		if (item === null || item === undefined) {
			continue;
		} else if (typeof item === 'string') {
			fin += item + ' ';
		} else if (Array.isArray(item) && item.length === 2) {
			if (typeof item[0] === 'string') {
				if (item[1]) fin += item[0] + ' ';
			} else {
				throw new Error(`cn() item #${i} is an invalid tuple: ${JSON.stringify(item)}`);
			}
		} else {
			throw new Error(`cn() item #${i} is invalid: ${JSON.stringify(item)}`);
		}
	}
	return fin.length ? fin.substring(0, fin.length - 1) : '';
}

And this new function covers all my current needs.

No comments: