UNPKG

irisrad-ui

Version:

UI elements developered for IRIS R&D Group Inc

203 lines (191 loc) 7.33 kB
/** * IRIS R&D Group Inc. All rights reserved. * * Author: Lucien Chu * Create Date: Apr 07, 2021 * * Description: Defines the left hand side navigation wrapper and its nav links */ /** * IRIS R&D Group Inc. All rights reserved. * * Author: Lucien Chu * Create Date: Apr 07, 2021 * * Description: The nav link wrapper which could display an specified icon on the left * and the label titel on the right. * * It uses Google Font which styles material ui icon and has been imported via css. As a result, simply find the specific * icon via https://fonts.google.com/icons, select an icon and specify necessary inforatmion, you * should see the html implementation like, <span class="material-icons-variant">icon_title</span>, * which this component is using. * * the icon_title specifies the icon to be show * the value after "material-icons" specifies the variants of the icon, ie: "material-icons-round" specifies that the icon * should be styled as rounded. Current support variants are "two-tone", "sharp", "round", "outlined". * * In order to show the icon in this component, specify the iconProps like this * iconProps: {iconTitle: icon_title, variant: value_after_"material-icons-", size: "small" | "medium" | "large" | "xlarge"} * */ import React, { useEffect, useRef } from "react"; import "../../style/styles.css"; import PropTypes from "prop-types"; import { IrisDrawer, DRAWER_MIN_WIDTHS, DRAWER_MAX_WIDTHS, } from "../irisDrawer/IrisDrawer"; import { IrisMenu, IrisMenuItem } from "../irisMenu/IrisMenu"; const SIDE_NAV_BASE_CLASS = "iris-side-nav"; const MATERIAL_UI_ICON_BASIC_CLASS = "material-icons"; // different icons styles predefined by the google font for the material icons const MUI_VARIANTS = ["two-tone", "sharp", "round", "outlined"]; // sizes of the material icons, defiend locally as the css is modified, easier to read and use const MUI_SIZES = ["small", "medium", "large", "xlarge"]; function IrisSideNavLink({ iconProps, label }) { const { variant, size, iconTitle } = iconProps; const iconStyles = [MATERIAL_UI_ICON_BASIC_CLASS]; if (MUI_VARIANTS.indexOf(variant) >= 0) { iconStyles.push(`${MATERIAL_UI_ICON_BASIC_CLASS}-${variant}`); } if (MUI_SIZES.indexOf(size) >= 0) { iconStyles.push(`${MATERIAL_UI_ICON_BASIC_CLASS}__size-${size}`); } return ( <span className="iris-nav-link"> <span className={iconStyles.join(" ")}>{iconTitle}</span>{" "} <span>{label}</span> </span> ); } IrisSideNavLink.propTypes = { label: PropTypes.string.isRequired, // title of the nav link, ie: Home, About Us, etc iconProps: PropTypes.shape({ iconTitle: PropTypes.string, // material icons title, ie: home, local_fire_department, etc size: PropTypes.oneOf(MUI_SIZES), // icon sizes variant: PropTypes.oneOf(MUI_VARIANTS), // "" is default material icon styles, if given, value should be one of "small" | "medium" | "large" | "xlarge" }), onClick: PropTypes.func, }; /** * IRIS R&D Group Inc. All rights reserved. * * Author: Lucien Chu * Create Date: Apr 07, 2021 * * Description: This is a container of navigation links (SideNavLinks), it uses the IrisDrawer * so that the links are expandable. * * Each navigation link is wrapper in side the IrisMenuItem and then all menu items are wrapper * inside the IrisMenu, for styling purpose. * * The IrisMenuItem of a navigation link should be highlighted when the link is clicked. As a result, * before executing the passed in callback function, when a navigtaion link is clicked, previous * highlighted IrisMenuItem should be cleared and a the clicked navigation link's parent (another IrisMenuItem) * should be highlighted. * * LIMITATION: the height of this wrapper's parent MUST be specified. * */ function IrisSideNav({ links, minWidth = 60, maxWidth = 200, className = "", ...props }) { const styles = [ SIDE_NAV_BASE_CLASS, `${SIDE_NAV_BASE_CLASS}--fixed-${minWidth}`, `${SIDE_NAV_BASE_CLASS}--max-width-${maxWidth}`, ]; const sideNaveRef = useRef(null); /** * @summary toggle the active styles of the list elements and executed the passed in callback function * * @description before executing the passed in callback function, * firstly, find the list item which wraps the clicked navigation link should be maked as active later on * secondly, find list item (IrisMenu), remove all "active" class from all of its children * lastly, marked the found list item as "active" * * * @param {ClickEvent} event click event * @param {Function} callBack function to be executed */ const handleClicked = (event, callBack) => { let listNode = event.target; let listItemNode; while (listNode) { if (listNode.classList.contains("iris-list__item")) { // keep the clicked list reference listItemNode = listNode; } if (listNode.classList.contains("iris-list")) { for (let i = 0; i < listNode.childNodes.length; i++) { // use the clicked list referece to determined whehter or not // to toggle the active class of an list item listNode.childNodes[i].classList.toggle( "active", listNode.childNodes[i] === listItemNode ); } break; } listNode = listNode.parentElement; } callBack(); }; useEffect(() => { const current = sideNaveRef?.current; const pathName = window.location.pathname; if (pathName === "/iframe.html") { if (current) { const list = current.querySelector(".iris-list"); list.childNodes[0].classList.toggle("active"); } } }, []); if (typeof className === "string" && className != "") { styles.push(className); } return ( <div className={styles.join(" ")} ref={sideNaveRef} {...props}> <IrisDrawer minWidth={minWidth} maxWidth={maxWidth}> {links && links.length > 0 ? ( <IrisMenu> {links.map(({ label, onClick, iconProps }) => ( <IrisMenuItem key={label} onClick={(event) => handleClicked(event, onClick)} > <IrisSideNavLink label={label} iconProps={iconProps} /> </IrisMenuItem> ))} </IrisMenu> ) : ( <p> Please specify property <em>links</em> </p> )} </IrisDrawer> </div> ); } IrisSideNav.propTypes = { className: PropTypes.string, links: PropTypes.arrayOf( PropTypes.shape({ label: PropTypes.string, iconProps: PropTypes.shape({ iconTitle: PropTypes.string, // material icons title, ie: home, local_fire_department, etc size: PropTypes.oneOf(MUI_SIZES), // icon sizes variant: PropTypes.oneOf(MUI_VARIANTS), // "" is default material icon styles, if given, value should be one of "small" | "medium" | "large" | "xlarge" }), onclick: PropTypes.func, }) ).isRequired, minWidth: PropTypes.oneOf(DRAWER_MIN_WIDTHS), maxWidth: PropTypes.oneOf(DRAWER_MAX_WIDTHS), }; export { IrisSideNav };