@furystack/shades
Version:
A lightweight UI framework for FuryStack with JSX support
90 lines • 3.06 kB
JavaScript
/**
* @example
* camelToKebab('backgroundColor') // 'background-color'
*/
export const camelToKebab = (str) => {
return str.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
};
export const isSelectorKey = (key) => {
return key.startsWith('&');
};
/**
* Serializes the non-selector entries of a {@link CSSProperties} object as
* a CSS declaration body. Selector keys (`&...`) are skipped.
*/
export const propertiesToCSSString = (properties) => {
const declarations = [];
for (const key in properties) {
if (Object.prototype.hasOwnProperty.call(properties, key) && !isSelectorKey(key)) {
const value = properties[key];
if (value !== undefined && value !== null && value !== '' && typeof value === 'string') {
declarations.push(`${camelToKebab(key)}: ${value}`);
}
}
}
return declarations.join('; ');
};
/** Builds `selector { decls; }` from the non-selector entries. Empty when no decls. */
export const generateCSSRule = (selector, properties) => {
const cssString = propertiesToCSSString(properties);
if (!cssString) {
return '';
}
return `${selector} { ${cssString}; }`;
};
/**
* Renders a {@link CSSObject} as CSS rules. Base properties become a single
* rule against `selector`; each `&...` selector key produces an additional
* rule with `&` substituted for `selector`.
*
* @example
* ```typescript
* generateCSS('my-component', {
* color: 'red',
* '&:hover': { color: 'blue' },
* '& .inner': { fontWeight: 'bold' }
* })
* // my-component { color: red; }
* // my-component:hover { color: blue; }
* // my-component .inner { font-weight: bold; }
* ```
*/
export const generateCSS = (selector, cssObject) => {
const rules = [];
// Extract base properties (non-selector keys)
const baseProperties = {};
const selectorRules = [];
for (const key in cssObject) {
if (Object.prototype.hasOwnProperty.call(cssObject, key)) {
if (isSelectorKey(key)) {
const properties = cssObject[key];
if (properties && typeof properties === 'object') {
selectorRules.push({ selectorKey: key, properties: properties });
}
}
else {
const value = cssObject[key];
if (typeof value !== 'object') {
;
baseProperties[key] = value;
}
}
}
}
// Generate base rule
const baseRule = generateCSSRule(selector, baseProperties);
if (baseRule) {
rules.push(baseRule);
}
// Generate selector rules
for (const { selectorKey, properties } of selectorRules) {
// Replace '&' with the base selector
const fullSelector = selectorKey.replace(/&/g, selector);
const rule = generateCSSRule(fullSelector, properties);
if (rule) {
rules.push(rule);
}
}
return rules.join('\n');
};
//# sourceMappingURL=css-generator.js.map