@patreon/studio
Version:
Patreon Studio Design System
89 lines • 4.09 kB
JavaScript
import { breakpointNames, breakpointSuffixes } from '../breakpoint-definitions';
import { IS_EXPORT } from '../env';
import { reduceResponsive } from '../opaque-responsive';
/**
* Helper function to generate a responsive class name lookup function
* that takes a responsive value and returns a class name for the
* responsive value.
*
* @warning This should be used outside of the React render function
* to validate failures before they occur in the render lifecycle.
*/
export function createResponsiveClassNameLookup(
/** CSS modules styles object */
styles,
/** Object describing the class names for each option value */
options) {
// if we're in export mode, we don't need to do any of this
// because we don't generate any CSS classes
if (IS_EXPORT) {
return () => '';
}
// build a map of class names for each option value so that we can
// verify that the styles object has a value for each breakpoint.
// we need to do this because the typescript compiler doesn't know
// about the classes generated by the `@responsive` at-rule. since
// we're already doing this check, we can also optimize the class
// name lookup function by only generating the class names once
const classNameMap = {};
// iterate over the options object and build a map of class names for each option value
for (const [optionName, optionValue] of Object.entries(options)) {
// initialize the map of class names for the option value
classNameMap[optionName] = {};
for (const bp of breakpointNames) {
// when the breakpoint is xs, we just use the option value as the class name
const responsiveClassName = bp === 'xs' ? optionValue : `${optionValue}${breakpointSuffixes[bp]}`;
// if the styles object does not contain the responsive class name, throw an error
if (!styles[responsiveClassName]) {
throw new Error(`Invalid responsive class name for \`${optionValue}\`, it's likely not wrapped inside of \`@responsive\` at-rule.`);
}
// add the responsive class name to the map of class names for the option value
classNameMap[optionName][bp] = styles[responsiveClassName];
}
}
// return the lookup function using the validated class name map
return (responsiveValue, bp) => {
if (!classNameMap[responsiveValue]) {
throw new Error(`While retrieving a responsive className, the responsive class name "${responsiveValue}" is not defined in the CSS module.`);
}
return classNameMap[responsiveValue][bp];
};
}
/**
* Generates a CSS class name for a responsive value using a lookup function
* that takes a responsive value and breakpoint then returns a class name
* for the responsive value.
*/
export function classNameForResponsiveValue(
/** responsive value to generate the class name */
responsiveValue,
/** lookup function to use to generate the class name per breakpoint */
lookupFunction) {
return reduceResponsive(responsiveValue, (acc, value, bp) => {
acc.push(lookupFunction(value, bp));
return acc;
}, []).join(' ');
}
/**
* Wraps a classNameForResponsiveValue function in a try/catch block that
* catches errors and logs them to the incrementLogger. If an error is caught,
* it returns an empty string.
*
* @warning this is not general purpose and should only be used to track down
* deprecated properties that are no longer supported.
*/
export function safeResponsiveClassNameFor({ classNameFn, props, incrementLogger, incrementName, }) {
try {
return classNameFn(props);
}
catch (e) {
// when we get an error, we want to log it and return an empty string so that
// we don't crash the application, and want to log the error so that we can investigate it
if (e instanceof Error) {
const value = e.message.match(/"(.*?)"/)?.[1] ?? 'unknown';
incrementLogger(incrementName, { value });
}
return '';
}
}
//# sourceMappingURL=index.js.map