UNPKG

tiwi

Version:

React library to create components with Tailwind styles baked in.

99 lines (98 loc) 3.84 kB
import { forwardRef, useMemo, } from "react"; import { twMerge } from "tailwind-merge"; import { allIntrinsicElements } from "./elements.js"; import { tiwiComponentSymbol, } from "./types.js"; // // Variants helpers. // function variantsToArray(variants) { if (!variants) { return []; } if (typeof variants === "string") { return [variants]; } if (Array.isArray(variants)) { return variants.filter(isString); } return Object.keys(variants).filter(k => variants[k] === true); } // // Component factory. // function isTiwiComponent(Element) { return Element && Element[tiwiComponentSymbol] === true; } function isString(value) { return typeof value === "string"; } function buildTiwiBase(createElement) { return (Element) => { return (classNames, ...variantDefinitions) => { const AnyElement = Element; const isTiwi = isTiwiComponent(Element); const component = forwardRef((props, ref) => { const { className, variants, ...otherProps } = props; const flatVariants = variantsToArray(variants); const flatVariantsString = flatVariants.join("::"); const mergedClassName = useMemo(() => { const allClassNames = classNames.reduce((list, current, index) => { const variantDefinition = variantDefinitions[index - 1]; if (variantDefinition) { Object.keys(variantDefinition) .filter(v => flatVariants.includes(v)) .forEach(v => { const variantValue = variantDefinition[v]; if (!variantValue) { return; } list.push(variantValue); }); } list.push(current); return list; }, []); return twMerge([...allClassNames, className]) || undefined; // Refresh on parent variables to make fast-refresh work in RN. // eslint-disable-next-line react-hooks/exhaustive-deps }, [classNames, variantDefinitions, className, flatVariantsString]); if (isTiwi) { return createElement(AnyElement, { ...otherProps, ref: ref, className: mergedClassName, variants: variants, }); } return createElement(AnyElement, { ...otherProps, ref: ref, className: mergedClassName, }); }); component[tiwiComponentSymbol] = true; component.displayName = (() => { if (typeof Element === "string") { return (component.displayName = `tiwi.${Element}`); } const name = Element.displayName || Element.name || ""; return (component.displayName = `tiwi(${name})`); })(); return component; }; }; } // // Add intrinsic components to function. // export function buildTiwi(createElement) { const tiwiBase = buildTiwiBase(createElement); if (typeof navigator === "object" && navigator.product === "ReactNative") { return tiwiBase; } const intrinsicElementsFunctions = allIntrinsicElements.reduce((result, DomElement) => { result[DomElement] = tiwiBase(DomElement); return result; }, {}); return Object.assign(tiwiBase, intrinsicElementsFunctions); }