'use strict'; import { createPropsBuilder } from '../../style'; import type { UnknownRecord } from '../../types'; import { hasValueProcessor, isConfigPropertyAlias, isDefined, kebabizeCamelCase, maybeAddSuffix, } from '../../utils'; import { hasNameAlias, isRuleBuilder } from '../utils'; import { PROPERTIES_CONFIG } from './config'; import type { PropsBuilderConfig, RuleBuilder } from './types'; type WebPropsBuilderConfig

= PropsBuilderConfig

; type WebPropsBuilderOptions = { // Appends ' !important' to every emitted declaration (e.g. pseudo-selector // rules that must override the element's inline styles). important?: boolean; // Emits `: initial` for props whose input value is `undefined` includeUnprocessed?: boolean; }; export type WebPropsBuilder

= { build(props: Partial

, options?: WebPropsBuilderOptions): string | null; }; export function createWebPropsBuilder( config: WebPropsBuilderConfig ): WebPropsBuilder { const usedRuleBuilders = new Set>(); // Maps a prop key to the CSS property it should be emitted under (e.g. a // Polygon's `points` is emitted as `d`). const nameAliases = new Map(); const propsBuilder = createPropsBuilder({ config, processConfigValue(configValue, propertyKey) { // Handle true - include unchanged if (configValue === true) { return true; } // Handle false - exclude property if (configValue === false) { return; } // Handle suffix (e.g., 'px') if (typeof configValue === 'string') { return (value) => maybeAddSuffix(value, configValue); } // Handle property alias if (isConfigPropertyAlias(configValue)) { return config[configValue.as]; } // Handle rule builders - store reference and return marker if (isRuleBuilder(configValue)) { // Return a processor that feeds values to the rule builder and returns undefined // so the property doesn't appear in the regular processed props (only the result // of the rule builder will appear in the final style) return (value) => { usedRuleBuilders.add(configValue); configValue.add(propertyKey, value as TProps[keyof TProps]); return; }; } // Handle name alias (emit under a different CSS property), optionally // combined with a value processor. const isNameAlias = hasNameAlias(configValue); if (isNameAlias) { nameAliases.set(propertyKey as string, configValue.name); } // Handle value processor if (hasValueProcessor(configValue)) { return configValue.process; } if (isNameAlias) { return (value) => String(value); } }, }); return { build( props: Partial, options?: WebPropsBuilderOptions ): string | null { usedRuleBuilders.clear(); // Build props - rule builders are fed during processing const processedProps = propsBuilder.build(props, { includeUnprocessed: options?.includeUnprocessed, }); // Build only used rule builders and merge their results for (const builder of usedRuleBuilders) { Object.assign(processedProps, builder.build()); } // Convert to CSS string const importance = options?.important ? ' !important' : ''; const cssString = Object.entries(processedProps) .reduce((acc, [key, value]) => { const name = nameAliases.get(key) ?? key; if (isDefined(value)) { acc.push( `${kebabizeCamelCase(name)}: ${value as string}${importance}` ); } else if ( options?.includeUnprocessed && props[key as keyof TProps] === undefined ) { acc.push(`${kebabizeCamelCase(name)}: initial${importance}`); } return acc; }, []) .join('; '); return cssString || null; // Return null if cssString is empty }, }; } export const webPropsBuilder = createWebPropsBuilder(PROPERTIES_CONFIG);