svelte
Version:
Cybernetically enhanced web apps
220 lines (186 loc) • 5.13 kB
JavaScript
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);
}