/* * SPDX-License-Identifier: Apache-2.0 * * The OpenSearch Contributors require contributions made to * this file be licensed under the Apache-2.0 license or a * compatible open source license. * * Modifications Copyright OpenSearch Contributors. See * GitHub history for details. */ /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright * ownership. Elasticsearch B.V. licenses this file to you under * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ import React, { cloneElement, ReactNode, ReactElement, MouseEventHandler, useState, useEffect, } from 'react'; import classNames from 'classnames'; import { CommonProps } from '../common'; import { OuiIcon } from '../icon'; import { getSecureRelForTarget } from '../../services'; import { validateHref } from '../../services/security/href_validator'; import { OuiInnerText } from '../inner_text'; /** * The props that are exposed to, or altered for, the consumer * for use in the object of items in `OuiSideNav` * can be found in the `side_nave_types.ts` file. */ export type _OuiSideNavItemButtonProps = CommonProps & { /** * Is an optional string to be passed as the navigation item's `href` prop, * and by default it will force rendering of the item as an `` */ href?: string; target?: string; rel?: string; /** * Callback function to be passed as the navigation item's `onClick` prop, * and by default it will force rendering of the item as a ` ); } return (
{children}
); }; export function OuiSideNavItem< T extends _OuiSideNavItemButtonProps & _OuiSideNavItemProps & { renderItem?: (props: any) => JSX.Element } >({ isOpen, isSelected, isParent, icon, onClick, href: _href, rel, target, items, children, renderItem: RenderItem = DefaultRenderItem, depth = 0, className, truncate = true, emphasize, buttonClassName, childrenOnly, ...rest }: OuiSideNavItemProps) { const isHrefValid = !_href || validateHref(_href); const href = isHrefValid ? _href : ''; const isClickable = onClick || href; // Forcing accordion style item if not linked, but has children const [itemIsOpen, setItemIsOpen] = useState(isOpen); useEffect(() => { setItemIsOpen(isOpen); }, [isOpen]); const toggleItemOpen = () => { setItemIsOpen((isOpen) => !isOpen); }; let childItems; if (items && itemIsOpen) { childItems =
{items}
; } let buttonIcon; if (icon) { buttonIcon = cloneElement(icon, { className: classNames('ouiSideNavItemButton__icon', icon.props.className), }); } const classes = classNames( 'ouiSideNavItem', { 'ouiSideNavItem--root': depth === 0, 'ouiSideNavItem--rootIcon': depth === 0 && icon, 'ouiSideNavItem--trunk': depth === 1, 'ouiSideNavItem--branch': depth > 1, 'ouiSideNavItem--hasChildItems': !!childItems, 'ouiSideNavItem--emphasized': emphasize, }, className ); const buttonClasses = classNames( 'ouiSideNavItemButton', { 'ouiSideNavItemButton--isClickable': isClickable, 'ouiSideNavItemButton-isOpen': depth > 0 && itemIsOpen && !isSelected, 'ouiSideNavItemButton-isSelected': isSelected, }, buttonClassName ); let caret; if (depth > 0 && childrenOnly) { caret = ; } const buttonContent = ( {buttonIcon} {(ref, innerText) => ( {children} )} {caret} ); const renderItemProps: _OuiSideNavItemButtonProps = { href, rel, target, onClick: childrenOnly ? toggleItemOpen : onClick, className: buttonClasses, children: buttonContent, }; return (
{childItems}
); }