/**
* @module templating
* @description Functions that are primarily used for templating (vue, static sites, etc)
*/
/**
* Helper function to process various Vue like class binding types
* - Used for modifiers
* - Handles same structure as Vue class bindings
* @param {Object|Array|String}
* @return {Set} Set of unique classnames
*/
export function normalizeClasses(inputClasses) {
const classes = new Set();
if (!inputClasses) {
return classes; // Return empty set if null/undefined/empty
}
if (typeof inputClasses === "string") {
// Split string by space to handle multiple classes like "class1 class2"
inputClasses.split(" ").forEach(cls => {
if (cls) classes.add(cls);
});
} else if (Array.isArray(inputClasses)) {
inputClasses.forEach(item => {
// Recursively normalize items within the array (supports [{ "active": true }, "static"])
normalizeClasses(item).forEach(cls => classes.add(cls));
});
} else if (typeof inputClasses === "object") {
// Handle object syntax { "class-name": condition }
for (const key in inputClasses) {
if (Object.prototype.hasOwnProperty.call(inputClasses, key) && inputClasses[key]) {
if (key) classes.add(key); // Add key if its value is truthy and key is not empty
}
}
}
return classes;
}
/**
* Creates a class string from various sources (arrays, objects, strings).
* A convenience wrapper for `normalizeClasses` that returns a string.
* @param {Object|Array|String} classes The classes to process.
* @returns {String} A space-separated class string.
* @example
* // normalizeClassString(['button', isPrimary && 'is-primary', 'large'])
* // -> "button is-primary large" or "button large"
*
* // normalizeClassString({ button: true, 'is-active': isActive })
* // -> "button is-active" or "button"
*/
export function normalizeClassString(classes) {
const classSet = normalizeClasses(classes);
return [...classSet].join(" ");
}
/**
* Conditionally executes a callback, ideal for logic within template literals.
* If the condition is truthy, the callback is executed and its result is returned.
* Otherwise, the fallback value is returned.
*
* @param {*} cond The condition to evaluate.
* @param {Function} callback Function to execute if `cond` is truthy. It receives `cond` as its argument.
* @param {*} [fallback=""] Value to return if `cond` is falsy. Defaults to an empty string.
* @returns {*} The result of `callback(cond)` if `cond` is truthy, otherwise the `fallback` value.
* @example
* const user = { name: "Joe" };
* const guest = null;
*
* // Example with a truthy condition:
* const welcomeUser = `<div>${when(user, u => `Welcome, ${u.name}`)}...`;
* // welcomeUser is "<div>Welcome, Joe..."
*
* // Example with a falsy condition and a custom fallback:
* const welcomeGuest = `<div>${when(guest, g => `Welcome, ${g.name}`, "Welcome, Guest!")}</div>`;
* // welcomeGuest is "<div>Welcome, Guest!</div>"
*/
export function when(cond, callback, fallback = "") {
return cond ? callback(cond) : fallback;
}
/**
* Returns a value if it is truthy, otherwise returns a fallback.
* A simpler version of `when()` for template literals where you only need to output a value as-is.
* @param {*} value The value to check.
* @param {*} [fallback=""] The value to return if `value` is falsy. Defaults to an empty string.
* @returns {*} The `value` if it's truthy, otherwise the `fallback`.
* @example
* // Optional class name
* const className = `item ${optional(activeClass)}`;
*
* // Providing a default for an optional name
* const displayName = `Welcome, ${optional(user.name, "Guest")}!`;
*/
export function optional(value, fallback = "") {
return value ? value : fallback;
}