UNPKG

@ffsm/factory

Version:

A powerful component factory for React that simplifies creation of reusable components with built-in support for composition patterns, conditional rendering, prop management, and TypeScript integration.

90 lines (89 loc) 3.35 kB
var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; import { jsx as _jsx } from "react/jsx-runtime"; import { forwardRef, } from 'react'; import { extractProps, filterProps } from './utils'; /** * Creates a reusable factory component with customizable rendering. * Factory components support dynamic props, type-safe forwarding, * and customizable rendering through template functions. * * @template AdditionalProps - Additional props type that the factory component accepts * @template Element - The base element type, defaults to 'div' * @param {string} displayName - Display name for the component in React DevTools * @param {FactoryInitialProps<Element, AdditionalProps>} [init] - Initial props or props factory function * @param {FactoryOptions<ElementType, AdditionalProps>} [options={}] - Additional options for the factory * @returns {ForwardRefExoticComponent<PropsWithoutRef<FactoryProps<Element, AdditionalProps>> & RefAttributes<Factory<Element>>>} * A forward ref component with the specified features * * @example * // Basic usage - create a Button component * const Button = factory('Button', { * className: 'btn' * }); * * @example * // With dynamic initialization * const Card = factory('Card', (props) => ({ * className: clsx('card', props.variant && `card-${props.variant}`) * })); * * @example * // With custom template for form field layout * const FormInput = factory( * 'FormInput', * { * as: 'input', * className: 'form-control' * }, * { * template: (Component, props, initProps) => ( * <div className="form-group"> * {props.label && <label>{props.label}</label>} * <Component {...props} /> * {props.error && <div className="error">{props.error}</div>} * </div> * ) * } * ); * * @example * // Filtering props with options * const Link = factory( * 'Link', * { as: 'a' }, * { * excludeProps: ['isExternal'], * template: (Component, props, initProps) => ( * <Component * {...props} * target={initProps.isExternal ? '_blank' : undefined} * rel={initProps.isExternal ? 'noopener noreferrer' : undefined} * /> * ) * } * ); */ export function factory(displayName, init, options) { if (options === void 0) { options = {}; } var template = options.template || (function (Component, props) { return _jsx(Component, __assign({}, props)); }); var FactoryElement = forwardRef(function FactoryElement(props, ref) { var solvedInit = typeof init === 'function' ? init(props) : init; var _a = extractProps(solvedInit, props), Component = _a.Component, children = _a.children, merged = _a.merged, initProps = _a.initProps; var filtered = filterProps(merged, options); return template(Component, __assign(__assign({}, filtered), { ref: ref, children: children }), initProps); }); FactoryElement.displayName = displayName; return FactoryElement; }