tss-react
Version:
Type safe CSS-in-JS API heavily inspired by react-jss
131 lines (130 loc) • 5.88 kB
JavaScript
/* eslint-disable @typescript-eslint/no-explicit-any */
import React, { forwardRef, createElement } from "react";
import { createMakeStyles } from "./makeStyles";
import { capitalize } from "./tools/capitalize";
export function createWithStyles(params) {
const { useTheme, cache } = params;
const { makeStyles } = createMakeStyles({ useTheme, cache });
function withStyles(Component, cssObjectByRuleNameOrGetCssObjectByRuleName, params) {
const Component_ = typeof Component === "string"
? (() => {
const tag = Component;
const Out = function ({ children, ...props }) {
return createElement(tag, props, children);
};
Object.defineProperty(Out, "name", {
"value": capitalize(tag)
});
return Out;
})()
: Component;
/**
* Get component name for wrapping
* @see https://reactjs.org/docs/higher-order-components.html#convention-wrap-the-display-name-for-easy-debugging
*/
const name = (() => {
{
const { name: nameOrWrappedName } = params !== null && params !== void 0 ? params : {};
if (nameOrWrappedName !== undefined) {
return typeof nameOrWrappedName !== "object"
? nameOrWrappedName
: Object.keys(nameOrWrappedName)[0];
}
}
let name = undefined;
displayName: {
const displayName = Component_.displayName;
if (typeof displayName !== "string" || displayName === "") {
break displayName;
}
name = displayName;
}
functionName: {
if (name !== undefined) {
break functionName;
}
const functionName = Component_.name;
if (typeof functionName !== "string" || functionName === "") {
break functionName;
}
name = functionName;
}
if (name === undefined) {
return undefined;
}
// Special case for dollar sign
name = name.replace(/\$/g, "usd");
// Replacing open and close parentheses
name = name.replace(/\(/g, "_").replace(/\)/g, "_");
// Catch-all replacement for characters not allowed in CSS class names
name = name.replace(/[^a-zA-Z0-9-_]/g, "_");
return name;
})();
const useStyles = makeStyles({ ...params, name })(typeof cssObjectByRuleNameOrGetCssObjectByRuleName === "function"
? (theme, props, classes) => incorporateMediaQueries(cssObjectByRuleNameOrGetCssObjectByRuleName(theme, props, classes))
: incorporateMediaQueries(cssObjectByRuleNameOrGetCssObjectByRuleName));
function getHasNonRootClasses(classes) {
for (const name in classes) {
if (name === "root") {
continue;
}
return true;
}
return false;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const Out = forwardRef(function (props, ref) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { className, classes: _classes, ...rest } = props;
const { classes, cx } = useStyles(props, { props });
const rootClassName = cx(classes.root, className);
fixedClassesByClasses.set(classes, {
...classes,
"root": rootClassName
});
return (React.createElement(Component_, { ref: ref, className: getHasNonRootClasses(classes)
? className
: rootClassName, ...(typeof Component === "string" ? {} : { classes }), ...rest }));
});
if (name !== undefined) {
Out.displayName = `${capitalize(name)}WithStyles`;
Object.defineProperty(Out, "name", { "value": Out.displayName });
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return Out;
}
withStyles.getClasses = getClasses;
return { withStyles };
}
const fixedClassesByClasses = new WeakMap();
const errorMessageGetClasses = "getClasses should only be used in conjunction with withStyles";
function getClasses(props) {
const classesIn = props.classes;
if (classesIn === undefined) {
throw new Error(errorMessageGetClasses);
}
const classes = fixedClassesByClasses.get(classesIn);
if (classes === undefined) {
throw new Error(errorMessageGetClasses);
}
return classes;
}
function incorporateMediaQueries(cssObjectByRuleNameWithMediaQueries) {
const cssObjectByRuleName = {};
const cssObjectByRuleNameWithMediaQueriesByMediaQuery = {};
Object.keys(cssObjectByRuleNameWithMediaQueries).forEach(ruleNameOrMediaQuery => ((ruleNameOrMediaQuery.startsWith("@media")
? cssObjectByRuleNameWithMediaQueriesByMediaQuery
: cssObjectByRuleName)[ruleNameOrMediaQuery] =
cssObjectByRuleNameWithMediaQueries[ruleNameOrMediaQuery]));
Object.keys(cssObjectByRuleNameWithMediaQueriesByMediaQuery).forEach(mediaQuery => {
const cssObjectByRuleNameBis = cssObjectByRuleNameWithMediaQueriesByMediaQuery[mediaQuery];
Object.keys(cssObjectByRuleNameBis).forEach(ruleName => {
var _a;
return (cssObjectByRuleName[ruleName] = {
...((_a = cssObjectByRuleName[ruleName]) !== null && _a !== void 0 ? _a : {}),
[mediaQuery]: cssObjectByRuleNameBis[ruleName]
});
});
});
return cssObjectByRuleName;
}