UNPKG

svelte

Version:

Cybernetically enhanced web apps

220 lines (186 loc) 5.13 kB
import { escape_html } from '../../escaping.js'; import { clsx as _clsx } from 'clsx'; /** * `<div translate={false}>` should be rendered as `<div translate="no">` and _not_ * `<div translate="false">`, which is equivalent to `<div translate="yes">`. There * may be other odd cases that need to be added to this list in future * @type {Record<string, Map<any, string>>} */ const replacements = { translate: new Map([ [true, 'yes'], [false, 'no'] ]) }; /** * @template V * @param {string} name * @param {V} value * @param {boolean} [is_boolean] * @returns {string} */ export function attr(name, value, is_boolean = false) { if (value == null || (!value && is_boolean)) return ''; const normalized = (name in replacements && replacements[name].get(value)) || value; const assignment = is_boolean ? '' : `="${escape_html(normalized, true)}"`; return ` ${name}${assignment}`; } /** * Small wrapper around clsx to preserve Svelte's (weird) handling of falsy values. * TODO Svelte 6 revisit this, and likely turn all falsy values into the empty string (what clsx also does) * @param {any} value */ export function clsx(value) { if (typeof value === 'object') { return _clsx(value); } else { return value ?? ''; } } const whitespace = [...' \t\n\r\f\u00a0\u000b\ufeff']; /** * @param {any} value * @param {string | null} [hash] * @param {Record<string, boolean>} [directives] * @returns {string | null} */ export function to_class(value, hash, directives) { var classname = value == null ? '' : '' + value; if (hash) { classname = classname ? classname + ' ' + hash : hash; } if (directives) { for (var key in directives) { if (directives[key]) { classname = classname ? classname + ' ' + key : key; } else if (classname.length) { var len = key.length; var a = 0; while ((a = classname.indexOf(key, a)) >= 0) { var b = a + len; if ( (a === 0 || whitespace.includes(classname[a - 1])) && (b === classname.length || whitespace.includes(classname[b])) ) { classname = (a === 0 ? '' : classname.substring(0, a)) + classname.substring(b + 1); } else { a = b; } } } } } return classname === '' ? null : classname; } /** * * @param {Record<string,any>} styles * @param {boolean} important */ function append_styles(styles, important = false) { var separator = important ? ' !important;' : ';'; var css = ''; for (var key in styles) { var value = styles[key]; if (value != null && value !== '') { css += ' ' + key + ': ' + value + separator; } } return css; } /** * @param {string} name * @returns {string} */ function to_css_name(name) { if (name[0] !== '-' || name[1] !== '-') { return name.toLowerCase(); } return name; } /** * @param {any} value * @param {Record<string, any> | [Record<string, any>, Record<string, any>]} [styles] * @returns {string | null} */ export function to_style(value, styles) { if (styles) { var new_style = ''; /** @type {Record<string,any> | undefined} */ var normal_styles; /** @type {Record<string,any> | undefined} */ var important_styles; if (Array.isArray(styles)) { normal_styles = styles[0]; important_styles = styles[1]; } else { normal_styles = styles; } if (value) { value = String(value) .replaceAll(/\s*\/\*.*?\*\/\s*/g, '') .trim(); /** @type {boolean | '"' | "'"} */ var in_str = false; var in_apo = 0; var in_comment = false; var reserved_names = []; if (normal_styles) { reserved_names.push(...Object.keys(normal_styles).map(to_css_name)); } if (important_styles) { reserved_names.push(...Object.keys(important_styles).map(to_css_name)); } var start_index = 0; var name_index = -1; const len = value.length; for (var i = 0; i < len; i++) { var c = value[i]; if (in_comment) { if (c === '/' && value[i - 1] === '*') { in_comment = false; } } else if (in_str) { if (in_str === c) { in_str = false; } } else if (c === '/' && value[i + 1] === '*') { in_comment = true; } else if (c === '"' || c === "'") { in_str = c; } else if (c === '(') { in_apo++; } else if (c === ')') { in_apo--; } if (!in_comment && in_str === false && in_apo === 0) { if (c === ':' && name_index === -1) { name_index = i; } else if (c === ';' || i === len - 1) { if (name_index !== -1) { var name = to_css_name(value.substring(start_index, name_index).trim()); if (!reserved_names.includes(name)) { if (c !== ';') { i++; } var property = value.substring(start_index, i).trim(); new_style += ' ' + property + ';'; } } start_index = i + 1; name_index = -1; } } } } if (normal_styles) { new_style += append_styles(normal_styles); } if (important_styles) { new_style += append_styles(important_styles, true); } new_style = new_style.trim(); return new_style === '' ? null : new_style; } return value == null ? null : String(value); }