modules_RouterNavBar_index.js

import { React, ReactRouter, PropTypes, classnames } from '@gravityforms/libraries';
import { uniqueId } from '@gravityforms/utils';
import Button from '../../elements/Button';
import Icon from '../../elements/Icon';
import Text from '../../elements/Text';

const { forwardRef } = React;
const { Link } = ReactRouter;

/**
 * @module RouterNavBar
 * @description The main nav used in various admin apps, using react router.
 *
 * @since 3.3.2
 *
 * @param {object}              props                  Component props.
 * @param {JSX.Element}         props.children         React element children.
 * @param {object}              props.customAttributes Custom attributes for the component.
 * @param {string|Array|object} props.customClasses    Custom classes for the component.
 * @param {Array}               props.items            The nav items.
 * @param {string}              props.type             The type of the nav bar, one of `icon-button` or `icon-text-tag`.
 * @param {object|null}         ref                    Ref to the component.
 *
 * @return {JSX.Element} The RouterNavBar component.
 *
 * @example
 * import RouterNavBar from '@gravityforms/components/react/admin/modules/RouterNavBar';
 *
 * const items = [
 *   {
 *     item: { active: true, key: 'send-a-test' },
 *     link: { to: '/send-a-test' },
 *     icon: { icon: 'beaker', iconPrefix: 'gravitysmtp-admin-icon' },
 *     text: { content: 'Send a test' },
 *   },
 *   {
 *     item: { active: true, key: 'scheduled-events' },
 *     link: { to: '/scheduled-events' },
 *     icon: { icon: 'clock', iconPrefix: 'gravitysmtp-admin-icon' },
 *     text: { content: 'Scheduled events' },
 *   },
 * ];
 *
 * return <RouterNavBar items={ items } />;
 *
 */
const RouterNavBar = forwardRef( ( {
	children = null,
	customAttributes = {},
	customClasses = [],
	items = [],
	type = 'icon-button',
}, ref ) => {
	const navAttributes = {
		className: classnames( [
			'gform-router-nav-bar',
			`gform-router-nav-bar--${ type }`,
		], customClasses ),
		...customAttributes,
	};
	const isIconButton = type === 'icon-button';

	const getIconButtonItems = () => {
		return items.map( ( item ) => {
			const {
				item: {
					customClasses: itemCustomClasses,
					key: itemKey,
					active: itemActive,
					disabled: itemDisabled,
					...itemProps
				},
				icon: { customClasses: iconCustomClasses, ...iconProps },
				text: { customClasses: textCustomClasses, ...textProps },
			} = item;
			const textId = uniqueId( 'gform-router-nav-bar__item-text-' );

			const itemAttrs = {
				className: classnames( {
					'gform-router-nav-bar__item': true,
					'gform-router-nav-bar__item--active': itemActive,
					'gform-router-nav-bar__item--disabled': itemDisabled,
				}, itemCustomClasses || [] ),
				...itemProps,
			};

			const iconAttrs = {
				customClasses: classnames( [ 'gform-router-nav-bar__item-icon' ], iconCustomClasses || [] ),
				...iconProps,
			};

			const textAttrs = {
				customAttributes: {
					id: textId,
				},
				customClasses: classnames( [ 'gform-router-nav-bar__item-text' ], textCustomClasses || [] ),
				size: 'text-xs',
				tagName: 'span',
				weight: 'medium',
				...textProps,
			};

			if ( item.link ) {
				const { customClasses: linkCustomClasses, ...linkProps } = item.link;

				const linkAttrs = {
					'aria-labelledby': textId,
					className: classnames( [
						'gform-router-nav-bar__item-link',
						'gform-button',
						'gform-button--icon-white',
						'gform-button--size-height-m',
					], linkCustomClasses || [] ),
					...linkProps,
				};

				return (
					<li { ...itemAttrs } key={ itemKey }>
						<Link { ...linkAttrs }>
							<Icon { ...iconAttrs } />
						</Link>
						<Text { ...textAttrs } />
					</li>
				);
			} else if ( item.button ) {
				const { customClasses: buttonCustomClasses, ...buttonProps } = item.button;

				const buttonAttrs = {
					'aria-labelledby': textId,
					customClasses: classnames( [
						'gform-router-nav-bar__item-button',
					], buttonCustomClasses || [] ),
					...buttonProps,
				};

				return (
					<li { ...itemAttrs } key={ itemKey }>
						<Button { ...buttonAttrs }>
							<Icon { ...iconAttrs } />
						</Button>
						<Text { ...textAttrs } />
					</li>
				);
			}

			return null;
		} );
	};

	const getIconTextTagItems = () => {
		return items.map( ( item ) => {
			const {
				item: {
					customClasses: itemCustomClasses,
					key: itemKey,
					active: itemActive,
					disabled: itemDisabled,
					...itemProps
				},
				text: { customClasses: textCustomClasses, ...textProps },
			} = item;

			const itemAttrs = {
				className: classnames( {
					'gform-router-nav-bar__item': true,
					'gform-router-nav-bar__item--active': itemActive,
					'gform-router-nav-bar__item--disabled': itemDisabled,
				}, itemCustomClasses || [] ),
				...itemProps,
			};

			const hasIcon = !! item?.icon;
			let iconAttrs = {};
			if ( hasIcon ) {
				const { customClasses: iconCustomClasses, ...iconProps } = item.icon;
				iconAttrs = {
					customClasses: classnames( [ 'gform-router-nav-bar__item-icon' ], iconCustomClasses || [] ),
					...iconProps,
				};
			}

			const textAttrs = {
				customClasses: classnames( [ 'gform-router-nav-bar__item-text' ], textCustomClasses || [] ),
				size: 'text-sm',
				tagName: 'span',
				weight: 'medium',
				...textProps,
			};

			if ( item.link ) {
				const { customClasses: linkCustomClasses, ...linkProps } = item.link;

				const linkAttrs = {
					className: classnames( [ 'gform-router-nav-bar__item-link' ], linkCustomClasses || [] ),
					...linkProps,
				};

				return (
					<li { ...itemAttrs } key={ itemKey }>
						<Link { ...linkAttrs }>
							{ hasIcon && <Icon { ...iconAttrs } /> }
							<Text { ...textAttrs } />
						</Link>
					</li>
				);
			} else if ( item.button ) {
				const { customClasses: buttonCustomClasses, ...buttonProps } = item.button;

				const buttonAttrs = {
					customClasses: classnames( [ 'gform-router-nav-bar__item-button' ], buttonCustomClasses || [] ),
					...buttonProps,
				};

				return (
					<li { ...itemAttrs } key={ itemKey }>
						<Button { ...buttonAttrs }>
							{ hasIcon && <Icon { ...iconAttrs } /> }
							<Text { ...textAttrs } />
						</Button>
					</li>
				);
			}

			return null;
		} );
	};

	const getItems = () => {
		if ( items.length === 0 ) {
			return null;
		}

		return (
			<ul className="gform-router-nav-bar__list">
				{ isIconButton ? getIconButtonItems() : getIconTextTagItems() }
			</ul>
		);
	};

	return (
		<nav { ...navAttributes } ref={ ref }>
			{ getItems() }
			{ children }
		</nav>
	);
} );

RouterNavBar.propTypes = {
	children: PropTypes.oneOfType( [
		PropTypes.arrayOf( PropTypes.node ),
		PropTypes.node,
	] ),
	customAttributes: PropTypes.object,
	customClasses: PropTypes.oneOfType( [
		PropTypes.string,
		PropTypes.array,
		PropTypes.object,
	] ),
	items: PropTypes.arrayOf( PropTypes.shape( {
		item: PropTypes.object,
		link: PropTypes.object,
		icon: PropTypes.object,
		text: PropTypes.object,
	} ) ),
	type: PropTypes.oneOf( [ 'icon-button', 'icon-text-tag' ] ),
};

RouterNavBar.displayName = 'RouterNavBar';

export default RouterNavBar;