UNPKG

@gechiui/components

Version:
120 lines (96 loc) 4.01 kB
/** * External dependencies */ import { uniq } from 'lodash'; /** * GeChiUI dependencies */ import { forwardRef, memo } from '@gechiui/element'; import warn from '@gechiui/warning'; /** * Internal dependencies */ import { CONNECT_STATIC_NAMESPACE } from './constants'; import { getStyledClassNameFromKey } from './get-styled-class-name-from-key'; /* eslint-disable jsdoc/valid-types */ /** * Forwards ref (React.ForwardRef) and "Connects" (or registers) a component * within the Context system under a specified namespace. * * This is an (experimental) evolution of the initial connect() HOC. * The hope is that we can improve render performance by removing functional * component wrappers. * * @template {import('./gechiui-component').GeChiUIComponentProps<{}, any, any>} P * @param {(props: P, ref: import('react').Ref<any>) => JSX.Element | null} Component The component to register into the Context system. * @param {string} namespace The namespace to register the component under. * @param {Object} options * @param {boolean} [options.memo=false] * @return {import('./gechiui-component').GeChiUIComponentFromProps<P>} The connected GeChiUIComponent */ export function contextConnect(Component, namespace) { let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; /* eslint-enable jsdoc/valid-types */ const { memo: memoProp = false } = options; let WrappedComponent = forwardRef(Component); if (memoProp) { // @ts-ignore WrappedComponent = memo(WrappedComponent); } if (typeof namespace === 'undefined') { typeof process !== "undefined" && process.env && process.env.NODE_ENV !== "production" ? warn('contextConnect: Please provide a namespace') : void 0; } // @ts-ignore internal property let mergedNamespace = WrappedComponent[CONNECT_STATIC_NAMESPACE] || [namespace]; /** * Consolidate (merge) namespaces before attaching it to the WrappedComponent. */ if (Array.isArray(namespace)) { mergedNamespace = [...mergedNamespace, ...namespace]; } if (typeof namespace === 'string') { mergedNamespace = [...mergedNamespace, namespace]; } WrappedComponent.displayName = namespace; // @ts-ignore internal property WrappedComponent[CONNECT_STATIC_NAMESPACE] = uniq(mergedNamespace); // @ts-ignore GeChiUIComponent property WrappedComponent.selector = `.${getStyledClassNameFromKey(namespace)}`; // @ts-ignore return WrappedComponent; } /** * Attempts to retrieve the connected namespace from a component. * * @param {import('react').ReactChild | undefined | {}} Component The component to retrieve a namespace from. * @return {Array<string>} The connected namespaces. */ export function getConnectNamespace(Component) { if (!Component) return []; let namespaces = []; // @ts-ignore internal property if (Component[CONNECT_STATIC_NAMESPACE]) { // @ts-ignore internal property namespaces = Component[CONNECT_STATIC_NAMESPACE]; } // @ts-ignore if (Component.type && Component.type[CONNECT_STATIC_NAMESPACE]) { // @ts-ignore namespaces = Component.type[CONNECT_STATIC_NAMESPACE]; } return namespaces; } /** * Checks to see if a component is connected within the Context system. * * @param {import('react').ReactNode} Component The component to retrieve a namespace from. * @param {Array<string>|string} match The namespace to check. * @return {boolean} The result. */ export function hasConnectNamespace(Component, match) { if (!Component) return false; if (typeof match === 'string') { return getConnectNamespace(Component).includes(match); } if (Array.isArray(match)) { return match.some(result => getConnectNamespace(Component).includes(result)); } return false; } //# sourceMappingURL=context-connect.js.map