UNPKG

@patreon/studio

Version:

Patreon Studio Design System

89 lines 4.09 kB
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