UNPKG

@equinor/fusion-react-utils

Version:
66 lines 3 kB
import { createElement, forwardRef, useMemo } from 'react'; import { extractElementProps, useElementEvents, useElementProps, useForwardRef } from './hooks'; const translateReactAttribute = (k) => { switch (k) { /** * React does *not* handle `className` for custom elements * so coerce it to `class` so it's handled correctly. */ case 'className': return 'class'; } return k.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); }; /** @see @link [Data Types](https://www.programiz.com/javascript/data-types) */ const SUPPORTED_REACT_PROP_TYPES = ['string', 'number', 'boolean', 'bigint']; /** * Wraps a custom element as a React Component * * @param elementClass HTMLElement - Custom element to wrap * @param tag string - tag name which the element uses in DOM */ export const createComponent = (elementClass, tag, options = {}) => { const { events = {}, functions = new Set(), displayName = elementClass.name } = options; /** extract all properties and combine with exposed functions */ const elementPropsNames = extractElementProps(elementClass); for (const fn of functions.values()) { elementPropsNames.add(fn); } /** element native props which should be handled programmatically */ const nativePropsName = new Set([...elementPropsNames, ...Object.keys(events)]); /** create reference component */ const component = forwardRef((props, __ref) => { const ref = useForwardRef(__ref); /** bind native properties and function */ useElementProps(ref, props, elementPropsNames); /** bind custom events */ useElementEvents(ref, props, events); /** properties which React should handle */ const reactProps = useMemo(() => { const entries = Object.entries(props || {}); const reactEntries = entries.filter(([k]) => !nativePropsName.has(k)); const nativeEntries = ref.current ? [] : entries /** filter out native property entries */ .filter(([k]) => nativePropsName.has(k)) /** filter supported properties */ .filter(([_, v]) => SUPPORTED_REACT_PROP_TYPES.includes(typeof v)) /** filter out empty properties */ .filter(([_, v]) => !!v); return ( /** combine properties */ [...reactEntries, ...nativeEntries] /** translate property name to reactName */ .map(([k, v]) => [translateReactAttribute(k), v]) /** build property object */ .reduce((c, [k, v]) => Object.assign(c, { [k]: v }), { ref })); }, [ref, props]); return createElement(tag, reactProps); }); /** component display name */ component.displayName = displayName; return component; }; export default createComponent; //# sourceMappingURL=create-element.js.map