UNPKG

carbon-components

Version:

The Carbon Design System is IBM’s open-source design system for products and experiences.

162 lines (151 loc) 6.42 kB
/** * Copyright IBM Corp. 2016, 2018 * * This source code is licensed under the Apache-2.0 license found in the * LICENSE file in the root directory of this source tree. */ import mixin from '../../globals/js/misc/mixin'; import createComponent from '../../globals/js/mixins/create-component'; import settings from '../../globals/js/settings'; import initComponentBySearch from '../../globals/js/mixins/init-component-by-search'; import handles from '../../globals/js/mixins/handles'; import on from '../../globals/js/misc/on'; import eventMatches from '../../globals/js/misc/event-matches'; const { prefix } = settings; class SideNav extends mixin(createComponent, initComponentBySearch, handles) { /** * The map associating DOM element and copy button UI instance. * @member SideNav.components * @type {WeakMap} */ static components /* #__PURE_CLASS_PROPERTY__ */ = new WeakMap(); /** * Side nav. * @extends CreateComponent * @extends InitComponentBySearch * @extends Handles * @param {HTMLElement} element The element working as a side nav. * @param {object} [options] The component options. * @param {string} [options.selectorSideNavToggle] * The CSS selector to find the toggle button. * @param {string} [options.selectorSideNavSubmenu] The CSS selector to find the trigger buttons for sub nav items. * @param {string} [options.selectorSideNavItem] The CSS selector to find the nav items. * @param {string} [options.selectorSideNavLink] The CSS selector to find the interactive potions in non-nested nav items. * @param {string} [options.selectorSideNavLinkCurrent] * The CSS selector to find the interactive potion in active non-nested nav item. * @param {string} [options.classSideNavExpanded] The CSS class for the expanded state. * @param {string} [options.classSideNavItemActive] * The CSS class for the active/inactive state for nav items. * @param {string} [options.classSideNavLinkCurrent] * The CSS class for the active/inactive state of the interactive potion in non-nested nav items. */ constructor(element, options) { super(element, options); this.manage(on(element, 'click', this._handleClick)); } /** * Enum for toggling side nav visibility * @readonly * @member SideNav.state * @type {object} * @property {string} EXPANDED Opening/visible * @property {string} COLLAPSED Closing/hidden */ static state /* #__PURE_CLASS_PROPERTY__ */ = { EXPANDED: 'expanded', COLLAPSED: 'collapsed', }; /** * @returns {boolean} `true` if the nav is expanded. */ isNavExpanded() { return this.element.classList.contains(this.options.classSideNavExpanded); } /** * Changes the expanded/collapsed state. */ changeState(state) { this.element.classList.toggle( this.options.classSideNavExpanded, state === this.constructor.state.EXPANDED ); } _handleClick = (evt) => { const matchesToggle = eventMatches(evt, this.options.selectorSideNavToggle); const matchesNavSubmenu = eventMatches( evt, this.options.selectorSideNavSubmenu ); const matchesSideNavLink = eventMatches( evt, this.options.selectorSideNavLink ); if (!matchesToggle && !matchesNavSubmenu && !matchesSideNavLink) { return; } if (matchesToggle) { this.changeState( !this.isNavExpanded() ? this.constructor.state.EXPANDED : this.constructor.state.COLLAPSED ); return; } if (matchesNavSubmenu) { const isSubmenuExpanded = matchesNavSubmenu.getAttribute('aria-expanded') === 'true'; matchesNavSubmenu.setAttribute('aria-expanded', `${!isSubmenuExpanded}`); return; } if (matchesSideNavLink) { [ ...this.element.querySelectorAll( this.options.selectorSideNavLinkCurrent ), ].forEach((el) => { el.classList.remove( this.options.classSideNavItemActive, this.options.classSideNavLinkCurrent ); el.removeAttribute('aria-current'); }); matchesSideNavLink.classList.add(this.options.classSideNavLinkCurrent); const closestSideNavItem = matchesSideNavLink.closest( this.options.selectorSideNavItem ); if (closestSideNavItem) { closestSideNavItem.classList.add(this.options.classSideNavItemActive); } } }; /** * The component options. * If `options` is specified in the constructor, {@linkcode SideNav.create .create()}, or {@linkcode SideNav.init .init()}, * properties in this object are overridden for the instance being create and how {@linkcode SideNav.init .init()} works. * @member SideNav.options * @type {object} * @property {string} selectorInit The data attribute to find side navs. * @property {string} [selectorSideNavToggle] The CSS selector to find the toggle button. * @property {string} [selectorSideNavSubmenu] The CSS selector to find the trigger buttons for sub nav items. * @property {string} [selectorSideNavItem] The CSS selector to find the nav items. * @property {string} [selectorSideNavLink] The CSS selector to find the interactive portions in non-nested nav items. * @property {string} [selectorSideNavLinkCurrent] * The CSS selector to find the interactive potion in active non-nested nav item. * @property {string} [classSideNavExpanded] The CSS class for the expanded state. * @property {string} [classSideNavItemActive] The CSS class for the active/inactive state for nav items. * @property {string} [classSideNavLinkCurrent] * The CSS class for the active/inactive state of the interactive portion in non-nested nav items. */ static options /* #__PURE_CLASS_PROPERTY__ */ = { selectorInit: '[data-side-nav]', selectorSideNavToggle: `.${prefix}--side-nav__toggle`, selectorSideNavSubmenu: `.${prefix}--side-nav__submenu`, selectorSideNavItem: `.${prefix}--side-nav__item`, selectorSideNavLink: `.${prefix}--side-nav__link`, selectorSideNavLinkCurrent: `[aria-current="page"],.${prefix}--side-nav__link--current,.${prefix}--side-nav__item--active`, classSideNavExpanded: `${prefix}--side-nav--expanded`, classSideNavItemActive: `${prefix}--side-nav__item--active`, classSideNavLinkCurrent: `${prefix}--side-nav__link--current`, }; } export default SideNav;