Website : rimsha.abasa.com
backdoor
Home
Console
Upload
information
Create File
Create Folder
About
Tools
:
/
var
/
canvas
/
node_modules
/
@instructure
/
ui-drilldown
/
es
/
Drilldown
/
Filename :
index.js
back
Copy
import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties"; const _excluded = ["groupProps"], _excluded2 = ["groupProps"], _excluded3 = ["id", "themeOverride"]; var _dec, _dec2, _dec3, _class, _class2, _IconArrowOpenEndSoli, _IconArrowOpenStartSo, _Options$Separator, _Options$Separator2; /* * The MIT License (MIT) * * Copyright (c) 2015 - present Instructure, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ /** @jsx jsx */ /** @jsxFrag React.Fragment */ import React, { Component } from 'react'; import { warn, error } from '@instructure/console'; import { testable } from '@instructure/ui-testable'; import { contains, containsActiveElement } from '@instructure/ui-dom-utils'; import { callRenderProp, matchComponentTypes, omitProps, safeCloneElement, withDeterministicId } from '@instructure/ui-react-utils'; import { View } from '@instructure/ui-view'; import { Options } from '@instructure/ui-options'; import { Popover } from '@instructure/ui-popover'; import { Selectable } from '@instructure/ui-selectable'; import { IconArrowOpenStartSolid, IconArrowOpenEndSolid, IconCheckSolid } from '@instructure/ui-icons'; import { withStyle, jsx } from '@instructure/emotion'; import { DrilldownSeparator } from './DrilldownSeparator'; import { DrilldownOption } from './DrilldownOption'; import { DrilldownGroup } from './DrilldownGroup'; import { DrilldownPage } from './DrilldownPage'; import generateStyle from './styles'; import generateComponentTheme from './theme'; import { propTypes, allowedProps } from './props'; // Additional data we need to track on the Options, // but shouldn't be settable via props // Contains the Option ComponentElement and the extra data we need track on it, // but don't want to expose as props // Contains the props object of the Page // with the `children` transformed into an array // An object with the mapped Pages with their id as keys /** --- category: components --- **/ let Drilldown = (_dec = withDeterministicId(), _dec2 = withStyle(generateStyle, generateComponentTheme), _dec3 = testable(), _dec(_class = _dec2(_class = _dec3(_class = (_class2 = class Drilldown extends Component { constructor(props) { super(props); this._drilldownRef = null; this._popover = null; this._trigger = null; this._containerElement = null; this._id = void 0; this._triggerId = void 0; this._headerBackId = void 0; this._headerTitleId = void 0; this._headerTitleLabelId = void 0; this._headerActionId = void 0; // Array of visited page ids in "breadcrumbs" fashion this._pageHistory = void 0; // Map of the active options on the page (includes header options too) this._activeOptionsMap = {}; // Map of the selected options in groups (grouped by group id) this.ref = null; this.handleRef = el => { // ref and elementRef have to be set together for the same element const elementRef = this.props.elementRef; this.ref = el; if (typeof elementRef === 'function') { elementRef(el); } }; this.handleDrilldownRef = el => { const drilldownRef = this.props.drilldownRef; this._drilldownRef = el; if (typeof drilldownRef === 'function') { drilldownRef(el); } // setting ref for "non-popover" version, the drilldown itself // (if a trigger is provided, the Popover sets the ref) if (!this.props.trigger) { this.handleRef(el); } }; this.show = event => { if (this._popover) { this._popover.show(event); this.setState({ isShowingPopover: true }); } }; this.hide = event => { if (this._popover) { this._popover.hide(event); this.setState({ isShowingPopover: false }); this.reset(); } }; this.handleOptionHighlight = (_event, { id, direction }) => { const rotateFocus = this.props.rotateFocus; const highlightedOptionId = this.state.highlightedOptionId; // if id exists, use that, or calculate from direction let highlightId = this.getPageChildById(id) ? id : void 0; if (!highlightId) { if (!highlightedOptionId) { // nothing highlighted yet, highlight first option highlightId = this.activeOptionIds[0]; } else if (direction) { // if it has direction, find next id based on it const index = this.activeOptionIds.indexOf(highlightedOptionId); const newIndex = index + direction; highlightId = index > -1 ? this.activeOptionIds[newIndex] : void 0; if (rotateFocus) { const lastOptionsIndex = this.activeOptionIds.length - 1; if (newIndex < 0) { highlightId = this.activeOptionIds[lastOptionsIndex]; } if (newIndex > lastOptionsIndex) { highlightId = this.activeOptionIds[0]; } } } } if (highlightId) { // only highlight if id exists as a valid option this.setState({ highlightedOptionId: highlightId }, () => { this.focusOption(highlightId); }); } }; // Navigates to the page and also returns the new and old pageIds this.goToPage = newPageId => { var _this$pageMap; if (!newPageId) { warn(false, `Cannot go to page because there was no page id provided.`); return void 0; } // TS complains that it cannot be true, but since it is an exposed method, // it is better if we provide a warning for this case too if (typeof newPageId !== 'string') { warn(false, `Cannot go to page because parameter newPageId has to be string (valid page id). Current newPageId is "${typeof newPageId}".`); return void 0; } if (!((_this$pageMap = this.pageMap) !== null && _this$pageMap !== void 0 && _this$pageMap[newPageId])) { warn(false, `Cannot go to page because page with id: "${newPageId}" doesn't exist.`); return void 0; } // the last page id in the history is the current one, // it will become the "prevPage" const prevPageId = this._pageHistory[this._pageHistory.length - 1]; const idxInHistory = this._pageHistory.indexOf(newPageId); if (idxInHistory < 0) { // if it is not in the page history, we have to add it this._pageHistory.push(newPageId); } else { // if it was already in the history, we go back to that page, // and clear the rest from the history this._pageHistory.splice(idxInHistory + 1, this._pageHistory.length - 1); } this.setState({ activePageId: newPageId, highlightedOptionId: void 0 }); return { prevPageId, newPageId }; }; this.goToPreviousPage = () => { if (!this.previousPage) { warn(false, `There is no previous page to go to. The current page history is: [${this._pageHistory.join(', ')}].`); return void 0; } // If there is a previous page, goToPage will succeed and return the data const _ref = this.goToPage(this.previousPage.id), newPageId = _ref.newPageId, prevPageId = _ref.prevPageId; return { newPageId, prevPageId }; }; this.handleBackButtonClick = () => { const _ref2 = this.currentPage, onBackButtonClicked = _ref2.onBackButtonClicked; // we only display the back button when there is a page to go back to const _ref3 = this.goToPreviousPage(), newPageId = _ref3.newPageId, prevPageId = _ref3.prevPageId; if (typeof onBackButtonClicked === 'function') { onBackButtonClicked(newPageId, prevPageId); } }; this.handleOptionSelect = (event, { id }) => { const selectedOption = this.getPageChildById(id); // TODO: this line can be removed when React 16 is no longer supported event.persist(); if (!id || !selectedOption || selectedOption.props.disabled || event.target.getAttribute('disabled') || event.target.getAttribute('aria-disabled')) { event.preventDefault(); event.stopPropagation(); return; } const _this$props = this.props, shouldHideOnSelect = _this$props.shouldHideOnSelect, onSelect = _this$props.onSelect; const groupProps = selectedOption.groupProps, selectedOptionChild = _objectWithoutProperties(selectedOption, _excluded); const _selectedOptionChild$ = selectedOptionChild.props, subPageId = _selectedOptionChild$.subPageId, href = _selectedOptionChild$.href, value = _selectedOptionChild$.value, onOptionClick = _selectedOptionChild$.onOptionClick; if (typeof onOptionClick === 'function') { onOptionClick(event, { optionId: id, drilldown: this, ...this.exposedNavigationProps }); } if (subPageId) { this.goToPage(subPageId); } if (event.type === 'keydown' && href) { var _this$_drilldownRef; const optionEl = (_this$_drilldownRef = this._drilldownRef) === null || _this$_drilldownRef === void 0 ? void 0 : _this$_drilldownRef.querySelector(`#${id}`); const isLink = optionEl.tagName.toLowerCase() === 'a'; // we need this check because in some cases // we ignore href prop in renderOption() if (isLink && optionEl !== null && optionEl !== void 0 && optionEl.href) { optionEl.click(); } } if (groupProps !== null && groupProps !== void 0 && groupProps.selectableType) { this.handleGroupOptionSelected(event, selectedOption); } else { if (typeof onSelect === 'function') { onSelect(event, { value, isSelected: true, selectedOption: selectedOptionChild, drilldown: this }); } } // should prevent closing on page navigation if (shouldHideOnSelect && !subPageId && id !== this._headerBackId) { this.hide(event); } }; // Setting extra logic for keyboard navigation. // Enter, Esc and up/down/home/end keys are handled by Selectable. this.handleKeyDown = event => { const id = event.target.id; const option = this.getPageChildById(id); // On Space... if ([' ', 'space', 'Space'].includes(event.key)) { // we need to preventDefault so the page doesn't scroll on Space event.preventDefault(); event.stopPropagation(); // for options, Space it has to work as Enter (get selected) if (option) { this.handleOptionSelect(event, { id }); } } // On Right arrow... if (event.key === 'ArrowRight') { // if the option has subpage, we open it if (option !== null && option !== void 0 && option.props.subPageId) { this.handleOptionSelect(event, { id }); } } // On Left arrow... if (event.key === 'ArrowLeft') { var _this$_popover; // if it is possible, we go a level up in the history if (this._pageHistory.length > 1) { this.handleBackButtonClick(); } // if on root page and popover is open, we close it if (this.isOnRootPage && (_this$_popover = this._popover) !== null && _this$_popover !== void 0 && _this$_popover.shown) { this._popover.hide(event); } } }; this.handleToggle = (event, shown) => { const onToggle = this.props.onToggle; this.setState({ isShowingPopover: shown }); if (typeof onToggle === 'function') { onToggle(event, { shown, drilldown: this, ...this.exposedNavigationProps }); } }; this.state = { isShowingPopover: props.trigger ? !!props.show : false, activePageId: props.rootPageId, highlightedOptionId: void 0, lastSelectedId: void 0, selectedGroupOptionsMap: this.setDefaultSelected() }; this._pageHistory = [props.rootPageId]; this._id = props.id || props.deterministicId(); this._triggerId = props.deterministicId('Drilldown-Trigger'); this._headerBackId = props.deterministicId('DrilldownHeader-Back'); this._headerTitleId = props.deterministicId('DrilldownHeader-Title'); this._headerTitleLabelId = props.deterministicId('DrilldownHeader-Title-Label'); this._headerActionId = props.deterministicId('DrilldownHeader-Action'); } componentDidMount() { var _this$props$makeStyle, _this$props2; (_this$props$makeStyle = (_this$props2 = this.props).makeStyles) === null || _this$props$makeStyle === void 0 ? void 0 : _this$props$makeStyle.call(_this$props2, this.makeStylesVariables); } componentDidUpdate(_prevProps, prevState) { var _this$props$makeStyle2, _this$props3; (_this$props$makeStyle2 = (_this$props3 = this.props).makeStyles) === null || _this$props$makeStyle2 === void 0 ? void 0 : _this$props$makeStyle2.call(_this$props3, this.makeStylesVariables); if (prevState.activePageId !== this.state.activePageId) { // on page change with mouse navigation, some option can get rendered // under the cursor and get focused, so we need to wait a tick to see // if anything gets focused, otherwise we focus the whole drilldown setTimeout(() => { if (!this.focused()) { this.focus(); } }, 0); } // if the current page was removed if (!this.currentPage) { if (this.previousPage) { this.goToPreviousPage(); } else { this.goToPage(this.props.rootPageId); } } if (this.state.highlightedOptionId && !this.getPageChildById(this.state.highlightedOptionId)) { this.setState({ highlightedOptionId: void 0 }); } } get makeStylesVariables() { return { hasHighlightedOption: !!this.state.highlightedOptionId }; } get activeOptionIds() { return Object.keys(this._activeOptionsMap); } get activeOptions() { return Object.values(this._activeOptionsMap); } get pages() { const children = this.props.children; return React.Children.toArray(children || []); } get pageMap() { const children = this.props.children; if (!children) { return void 0; } const map = {}; this.pages.forEach(page => { const props = page.props; const children = props.children; map[props.id] = { ...props, children: React.Children.toArray(children || []) }; }); return map; } get isOnRootPage() { return this.state.activePageId === this.props.rootPageId; } get currentPage() { return this.getPageById(this.state.activePageId); } get previousPage() { const previousPageId = this._pageHistory[this._pageHistory.length - 2]; return this.getPageById(previousPageId); } // Returns the navigation methods and the page history. // These are used in some callbacks to expose the navigation logic. get exposedNavigationProps() { const goToPage = this.goToPage, goToPreviousPage = this.goToPreviousPage; // we make a copy of the array so the original history // cannot be modified from the outside const pageHistory = [...this._pageHistory]; return { pageHistory, goToPage, goToPreviousPage }; } get currentPageAriaLabel() { var _this$currentPage; // return, if explicitly set if (this.props['aria-labelledby']) { return this.props['aria-labelledby']; } // if it has title, point to the title label content if ((_this$currentPage = this.currentPage) !== null && _this$currentPage !== void 0 && _this$currentPage.renderTitle) { return this._headerTitleLabelId; } // if root page has no title, try the trigger, if exists if (this.isOnRootPage && this.props.trigger) { return this._triggerId; } return void 0; } getChildrenArray(children) { if (!children) { return []; } return Array.isArray(children) ? children : [children]; } getPageById(id) { return this.pageMap && id ? this.pageMap[id] : void 0; } getPageChildById(id) { return id ? this._activeOptionsMap[id] : void 0; } setDefaultSelected() { const selectedGroupOptionsMap = {}; this.pages.forEach(page => { const children = page.props.children; this.getChildrenArray(children).forEach(child => { if (matchComponentTypes(child, [DrilldownGroup])) { var _this$getChildrenArra; const _child$props = child.props, groupId = _child$props.id, selectableType = _child$props.selectableType, _child$props$defaultS = _child$props.defaultSelected, groupDefaultSelected = _child$props$defaultS === void 0 ? [] : _child$props$defaultS, groupChildren = _child$props.children; if (!selectableType) { return; } if (selectableType && !selectedGroupOptionsMap[groupId]) { selectedGroupOptionsMap[groupId] = new Map(); } if (selectableType === 'single' && groupDefaultSelected.length > 1) { error(false, `Radio type selectable groups can have only one item selected! Group with id "${groupId}" cannot select multiple items: [${groupDefaultSelected.join(', ')}]!`); return; } selectedGroupOptionsMap[groupId] = new Map(); (_this$getChildrenArra = this.getChildrenArray(groupChildren)) === null || _this$getChildrenArra === void 0 ? void 0 : _this$getChildrenArra.forEach(groupChild => { if (matchComponentTypes(groupChild, [DrilldownOption])) { const _groupChild$props = groupChild.props, optionId = _groupChild$props.id, optionValue = _groupChild$props.value, optionDefaultSelected = _groupChild$props.defaultSelected; // if explicitly set to false, it should override the group's if (optionDefaultSelected === false) { return; } const isGroupDefaultSelected = typeof optionValue !== 'undefined' && groupDefaultSelected.filter(value => typeof value !== 'undefined').includes(optionValue); if (optionDefaultSelected || isGroupDefaultSelected) { selectedGroupOptionsMap[groupId].set(optionId, optionValue); } } }); } }); }); return selectedGroupOptionsMap; } get selectedGroupOptionIdsArray() { return Object.values(this.state.selectedGroupOptionsMap).map(groupIdMap => Array.from(groupIdMap.keys())).flat(); } get headerChildren() { const currentPage = this.currentPage; const _this$props4 = this.props, styles = _this$props4.styles, deterministicId = _this$props4.deterministicId; const headerChildren = []; if (!currentPage) { return headerChildren; } const children = currentPage.children, renderBackButtonLabel = currentPage.renderBackButtonLabel, renderTitle = currentPage.renderTitle, renderActionLabel = currentPage.renderActionLabel, onHeaderActionClicked = currentPage.onHeaderActionClicked, withoutHeaderSeparator = currentPage.withoutHeaderSeparator; // Back navigation option if (this.previousPage) { const prevPageTitle = callRenderProp(this.previousPage.renderTitle); const backButtonLabel = callRenderProp(renderBackButtonLabel, prevPageTitle); headerChildren.push(jsx(DrilldownOption, { id: this._headerBackId, onOptionClick: this.handleBackButtonClick }, jsx("div", { css: styles === null || styles === void 0 ? void 0 : styles.headerBack, role: "presentation" }, backButtonLabel))); } // Header title if (renderTitle) { const title = callRenderProp(renderTitle); if (title) { headerChildren.push(jsx(DrilldownOption, { id: this._headerTitleId, role: "presentation", "aria-hidden": "true" }, jsx("div", { css: styles === null || styles === void 0 ? void 0 : styles.headerTitle, id: this._headerTitleLabelId }, title))); } } // Action label if (renderActionLabel) { const actionLabel = callRenderProp(renderActionLabel); if (actionLabel) { headerChildren.push(jsx(DrilldownOption, { id: this._headerActionId, themeOverride: { color: styles === null || styles === void 0 ? void 0 : styles.headerActionColor }, onOptionClick: event => { if (typeof onHeaderActionClicked === 'function') { onHeaderActionClicked(event); } } }, actionLabel)); } } // header separator if (headerChildren.length > 0 && children.length > 0 && !withoutHeaderSeparator) { headerChildren.push(jsx(DrilldownSeparator, { id: deterministicId('DrilldownHeader-Separator') })); } return headerChildren; } get shown() { return this.props.trigger ? this.state.isShowingPopover : true; } containsDuplicateChild(children) { let containsDuplicate = false; const childMap = new Map(); for (const child of children) { if (!childMap.has(child.props.id)) { childMap.set(child.props.id, true); } else { warn(false, `Duplicate id: "${child.props.id}"! Make sure all options have unique ids, otherwise they won't be rendered.`); return containsDuplicate = true; } } return containsDuplicate; } reset() { this._activeOptionsMap = {}; this.setState({ highlightedOptionId: void 0 }); } focus() { if (this.shown) { var _this$_drilldownRef2, _this$_drilldownRef3; error(!!((_this$_drilldownRef2 = this._drilldownRef) !== null && _this$_drilldownRef2 !== void 0 && _this$_drilldownRef2.focus), '[Drilldown] Could not focus the drilldown.'); (_this$_drilldownRef3 = this._drilldownRef) === null || _this$_drilldownRef3 === void 0 ? void 0 : _this$_drilldownRef3.focus(); } else { var _this$_trigger; error(!!((_this$_trigger = this._trigger) !== null && _this$_trigger !== void 0 && _this$_trigger.focus), '[Drilldown] Could not focus the trigger.'); this._trigger.focus(); } } focused() { if (this.shown) { return containsActiveElement(this._drilldownRef); } else { return containsActiveElement(this._trigger); } } focusOption(id) { const container = this._containerElement; const optionElement = container === null || container === void 0 ? void 0 : container.querySelector(`[id="${id}"]`); optionElement === null || optionElement === void 0 ? void 0 : optionElement.focus(); } handleGroupOptionSelected(event, selectedOption) { this.setState(oldState => { var _oldState$selectedGro; const _selectedOption$props = selectedOption.props, optionId = _selectedOption$props.id, value = _selectedOption$props.value; const _ref4 = selectedOption.groupProps, groupId = _ref4.id, selectableType = _ref4.selectableType; let newState = new Map(oldState.selectedGroupOptionsMap[groupId]); if (selectableType === 'multiple' && Boolean((_oldState$selectedGro = oldState.selectedGroupOptionsMap[groupId]) === null || _oldState$selectedGro === void 0 ? void 0 : _oldState$selectedGro.has(optionId))) { // toggle off, if already selected newState.delete(optionId); } else { if (selectableType === 'multiple') { // "checkbox" newState.set(optionId, value); } else if (selectableType === 'single') { // "radio" newState = new Map(); newState.set(optionId, value); } } return { ...oldState, selectedGroupOptionsMap: { ...oldState.selectedGroupOptionsMap, [groupId]: newState } }; }, () => { const value = selectedOption.props.value; const _ref5 = selectedOption.groupProps, groupId = _ref5.id, groupOnSelect = _ref5.onSelect; const onSelect = this.props.onSelect; const groupProps = selectedOption.groupProps, option = _objectWithoutProperties(selectedOption, _excluded2); const selectedOptionValuesInGroup = [...this.state.selectedGroupOptionsMap[groupId].values()]; if (typeof groupOnSelect === 'function') { groupOnSelect(event, { value: selectedOptionValuesInGroup, isSelected: selectedOptionValuesInGroup.includes(value), selectedOption: option, drilldown: this }); } if (typeof onSelect === 'function') { onSelect(event, { value: selectedOptionValuesInGroup, isSelected: selectedOptionValuesInGroup.includes(value), selectedOption: option, drilldown: this }); } }); } renderList(getOptionProps, getDisabledOptionProps) { const currentPage = this.currentPage, headerChildren = this.headerChildren; if (!currentPage || this.containsDuplicateChild(currentPage.children)) { return null; } const pageChildren = [...headerChildren, ...currentPage.children]; // for tracking if the last item was a Separator or not let lastItemWasSeparator = false; return pageChildren.map((child, index) => { /** * --- RENDER GROUP --- */ if (matchComponentTypes(child, [DrilldownGroup])) { const isFirstChild = index === 0; const isLastChild = index === pageChildren.length - 1; const afterSeparator = lastItemWasSeparator; const needsFirstSeparator = !child.props.withoutSeparators && !isFirstChild && !afterSeparator; const needsLastSeparator = !child.props.withoutSeparators && !isLastChild; lastItemWasSeparator = needsLastSeparator; return this.renderGroup(child, getOptionProps, getDisabledOptionProps, // for rendering separators appropriately needsFirstSeparator, needsLastSeparator); /** * --- RENDER SEPARATOR --- */ } else if (matchComponentTypes(child, [DrilldownSeparator])) { // if the last item was separator, we don't want to duplicate it if (lastItemWasSeparator) { return null; } lastItemWasSeparator = true; return this.renderSeparator(child); /** * --- RENDER OPTION --- */ } else if (matchComponentTypes(child, [DrilldownOption])) { lastItemWasSeparator = false; return this.renderOption(child, getOptionProps, getDisabledOptionProps); } else { return null; } }); } renderSeparator(separator) { const _separator$props = separator.props, id = _separator$props.id, themeOverride = _separator$props.themeOverride, props = _objectWithoutProperties(_separator$props, _excluded3); return jsx(Options.Separator, Object.assign({}, props, { id: id, key: id, role: "separator" // we pass the themeOverride to Options.Separator , themeOverride: themeOverride })); } renderOption(option, getOptionProps, getDisabledOptionProps, groupProps) { var _this$currentPage2, _optionProps$renderBe, _optionProps$renderBe2, _optionProps$renderAf, _optionProps$renderAf2; const styles = this.props.styles; let isSelected = false; const _option$props = option.props, id = _option$props.id, children = _option$props.children, href = _option$props.href, as = _option$props.as, role = _option$props.role, subPageId = _option$props.subPageId, disabled = _option$props.disabled, renderLabelInfo = _option$props.renderLabelInfo, renderBeforeLabel = _option$props.renderBeforeLabel, renderAfterLabel = _option$props.renderAfterLabel, beforeLabelContentVAlign = _option$props.beforeLabelContentVAlign, afterLabelContentVAlign = _option$props.afterLabelContentVAlign, description = _option$props.description, descriptionRole = _option$props.descriptionRole, elementRef = _option$props.elementRef, themeOverride = _option$props.themeOverride; if (!id) { warn(false, `Drilldown.Option without id won't be rendered. It is needed to internally track the options.`); return null; } // Props passed to the Option.Item let optionProps = { // passthrough props ...omitProps(option.props, [...DrilldownOption.allowedProps, ...Options.Item.allowedProps]), // props from selectable ...getOptionProps({ id, // aria-selected is only valid for these roles, otherwise we need to unset it ...(role && !['gridcell', 'option', 'row', 'tab', 'columnheader', 'rowheader', 'treeitem'].includes(role) && { 'aria-selected': void 0 }) }), // we pass the themeOverride to Options.Item themeOverride, // directly passed props renderBeforeLabel, renderAfterLabel, beforeLabelContentVAlign, afterLabelContentVAlign, description, descriptionRole, as, role, elementRef, variant: 'default', tabIndex: -1 }; // extra data we need track on the option, // but don't want to expose as props const optionData = { groupProps: groupProps }; const isOptionDisabled = id !== this._headerBackId && ( // Back nav is never disabled this.props.disabled || ((_this$currentPage2 = this.currentPage) === null || _this$currentPage2 === void 0 ? void 0 : _this$currentPage2.disabled) || (groupProps === null || groupProps === void 0 ? void 0 : groupProps.disabled) || disabled); // display option as disabled if (isOptionDisabled) { optionProps.variant = 'disabled'; optionProps = { ...optionProps, ...getDisabledOptionProps() }; } // track as valid active option if not the title if (id !== this._headerTitleId) { this._activeOptionsMap[id] = { ...option, ...optionData }; } const customRole = role !== DrilldownOption.defaultProps.role ? role : void 0; // BEFORE/AFTER elements: // we set a few manually on Options.Item, // the rest are passed directly if (subPageId) { optionProps.renderAfterLabel = _IconArrowOpenEndSoli || (_IconArrowOpenEndSoli = jsx(IconArrowOpenEndSolid, null)); optionProps['aria-haspopup'] = true; optionProps.role = customRole || 'button'; warn(!renderAfterLabel, `The prop "renderAfterLabel" is reserved on item with id: "${id}". When it has "subPageId" provided, a navigation arrow will render after the label.`); } if (id === this._headerBackId) { optionProps.renderBeforeLabel = _IconArrowOpenStartSo || (_IconArrowOpenStartSo = jsx(IconArrowOpenStartSolid, null)); } const isOptionControlled = typeof option.props.selected === 'boolean'; if ((groupProps !== null && groupProps !== void 0 && groupProps.selectableType || isOptionControlled) && groupProps) { if (!isOptionControlled) { var _this$state$selectedG; isSelected = Boolean((_this$state$selectedG = this.state.selectedGroupOptionsMap[groupProps.id]) === null || _this$state$selectedG === void 0 ? void 0 : _this$state$selectedG.has(id)); } else { isSelected = Boolean(option.props.selected); } optionProps['aria-checked'] = isSelected; optionProps.renderBeforeLabel = jsx(IconCheckSolid, { style: { opacity: isSelected ? 1 : 0 } }); warn(!renderBeforeLabel, `The prop "renderBeforeLabel" is reserved on item with id: "${id}". When this option is a selectable member of a Drilldown.Group, selection indicator will render before the label.`); // setting aria roles and attributes for selectable group items if (groupProps.selectableType === 'single') { optionProps.role = customRole || 'menuitemradio'; } if (groupProps.selectableType === 'multiple') { optionProps.role = customRole || 'menuitemcheckbox'; } } // display option as highlighted if (id === this.state.highlightedOptionId) { optionProps.variant = 'highlighted'; if (isOptionDisabled) { optionProps.variant = 'highlighted-disabled'; } } if (href) { if (subPageId) { warn(false, `Drilldown.Option with id "${id}" has subPageId, so it will ignore the "href" property.`); } else if (groupProps !== null && groupProps !== void 0 && groupProps.selectableType) { warn(false, `Drilldown.Option with id "${id}" is in a selectable group, so it will ignore the "href" property.`); } else { optionProps.href = href; } } const optionLabel = callRenderProp(children, { id, variant: optionProps.variant, isSelected }); if (!optionLabel) { warn(false, `There are no "children" prop provided for option with id: "${id}", so it won't be rendered.`); return null; } const renderLabelProps = { variant: optionProps.variant, vAlign: afterLabelContentVAlign, as, role: optionProps.role, isSelected }; // we need to bind our own option props the render functions if (typeof optionProps.renderBeforeLabel === 'function' && !((_optionProps$renderBe = optionProps.renderBeforeLabel) !== null && _optionProps$renderBe !== void 0 && (_optionProps$renderBe2 = _optionProps$renderBe.prototype) !== null && _optionProps$renderBe2 !== void 0 && _optionProps$renderBe2.isReactComponent)) { optionProps.renderBeforeLabel = optionProps.renderBeforeLabel.bind(null, renderLabelProps); } if (typeof optionProps.renderAfterLabel === 'function' && !((_optionProps$renderAf = optionProps.renderAfterLabel) !== null && _optionProps$renderAf !== void 0 && (_optionProps$renderAf2 = _optionProps$renderAf.prototype) !== null && _optionProps$renderAf2 !== void 0 && _optionProps$renderAf2.isReactComponent)) { optionProps.renderAfterLabel = optionProps.renderAfterLabel.bind(null, renderLabelProps); } const labelInfo = renderLabelInfo && callRenderProp(renderLabelInfo, renderLabelProps); const vAlignMap = { start: 'flex-start', center: 'center', end: 'flex-end' }; const labelAriaId = `${id}__label`; const infoAriaId = `${id}__info`; const labelledby = option.props['aria-labelledby'] || labelAriaId; const describedby = option.props['aria-describedby'] || (labelInfo ? infoAriaId : void 0); return jsx(Options.Item, Object.assign({}, optionProps, { key: id, "aria-labelledby": labelledby, "aria-describedby": describedby }), jsx("div", { css: styles === null || styles === void 0 ? void 0 : styles.optionContainer, role: "none" }, jsx("span", { css: styles === null || styles === void 0 ? void 0 : styles.optionContent, role: "none", id: labelAriaId }, optionLabel), labelInfo ? jsx("span", { css: styles === null || styles === void 0 ? void 0 : styles.optionLabelInfo, role: "presentation", style: { alignSelf: vAlignMap[afterLabelContentVAlign] } }, jsx("span", { id: infoAriaId }, labelInfo)) : null)); } renderGroup(group, getOptionProps, getDisabledOptionProps, needsFirstSeparator, needsLastSeparator) { const _group$props = group.props, id = _group$props.id, children = _group$props.children, renderGroupTitle = _group$props.renderGroupTitle, themeOverride = _group$props.themeOverride, role = _group$props.role, as = _group$props.as, elementRef = _group$props.elementRef; if (!children) { return null; } const groupChildren = []; // add a separator above if (needsFirstSeparator) { groupChildren.push(_Options$Separator || (_Options$Separator = jsx(Options.Separator, null))); } // create a sublist as a group // (a wrapping list item will be created by Options) groupChildren.push(jsx(Options, { id: id, key: id, role: role, as: as || this.props.as, renderLabel: renderGroupTitle, elementRef: elementRef // we pass the themeOverride to Options , themeOverride: themeOverride }, this.getChildrenArray(children).map(child => { if (matchComponentTypes(child, [DrilldownSeparator])) { return this.renderSeparator(child); } else if (matchComponentTypes(child, [DrilldownOption])) { return this.renderOption(child, getOptionProps, getDisabledOptionProps, group.props); } else { return null; } }))); // add a separator below if (needsLastSeparator) { groupChildren.push(_Options$Separator2 || (_Options$Separator2 = jsx(Options.Separator, null))); } return groupChildren; } renderPage() { const _this$props5 = this.props, styles = _this$props5.styles, overflowY = _this$props5.overflowY, overflowX = _this$props5.overflowX, height = _this$props5.height, width = _this$props5.width, minHeight = _this$props5.minHeight, minWidth = _this$props5.minWidth, maxHeight = _this$props5.maxHeight, maxWidth = _this$props5.maxWidth, role = _this$props5.role, as = _this$props5.as, label = _this$props5.label; if (!this.currentPage) { return null; } return jsx(Selectable, { isShowingOptions: this.shown, highlightedOptionId: this.state.highlightedOptionId, selectedOptionId: this.selectedGroupOptionIdsArray, onRequestShowOptions: this.show, onRequestHideOptions: this.hide, onRequestSelectOption: this.handleOptionSelect, onRequestHighlightOption: this.handleOptionHighlight, onRequestHighlightFirstOption: event => { const firstOptionId = this.activeOptionIds[0]; this.handleOptionHighlight(event, { id: firstOptionId }); }, onRequestHighlightLastOption: event => { const lastOptionId = this.activeOptionIds[this.activeOptionIds.length - 1]; this.handleOptionHighlight(event, { id: lastOptionId }); } }, ({ // TODO: figure out what other Selectable props we need, if we want to add a Select version for drilldown: // getRootProps, - we probably don't need this // getLabelProps, - do we need label? // getDescriptionProps, - might be nice for assistiveText like in Select // getInputProps, - hidden input for a11y? role="combobox" might be needed getTriggerProps, getListProps, getOptionProps, getDisabledOptionProps }) => jsx(View, Object.assign({ as: "div", elementRef: this.handleDrilldownRef, tabIndex: 0, css: styles === null || styles === void 0 ? void 0 : styles.drilldown, position: "relative", borderRadius: "small", width: width, minWidth: maxWidth, maxWidth: maxWidth, role: role, "aria-label": label, "aria-labelledby": this.currentPageAriaLabel }, getTriggerProps({ id: this._id, // We need to override these aria attributes added by Selectable, // since Drilldown is not a combobox and has no popup 'aria-haspopup': false, 'aria-expanded': void 0, onKeyDown: this.handleKeyDown, onBlur: event => { const target = event.currentTarget; const related = event.relatedTarget; const containsRelated = contains(target, related); if (!related || related === this._drilldownRef || related !== target && !containsRelated) { this.setState({ highlightedOptionId: void 0 }); } }, onMouseLeave: () => { this.setState({ highlightedOptionId: void 0 }); } })), jsx(View, { as: "div", overflowY: overflowY, overflowX: overflowX, height: height, width: width, minHeight: minHeight, minWidth: minWidth, maxHeight: maxHeight, maxWidth: maxWidth, css: styles === null || styles === void 0 ? void 0 : styles.container, borderRadius: "small", role: "presentation", elementRef: element => { this._containerElement = element; } }, jsx(Options, Object.assign({}, getListProps(), { role: "presentation", as: as }), this.renderList(getOptionProps, getDisabledOptionProps))))); } render() { // clear temporary option store this._activeOptionsMap = {}; const _this$props6 = this.props, show = _this$props6.show, defaultShow = _this$props6.defaultShow, placement = _this$props6.placement, withArrow = _this$props6.withArrow, shouldContainFocus = _this$props6.shouldContainFocus, shouldReturnFocus = _this$props6.shouldReturnFocus, trigger = _this$props6.trigger, mountNode = _this$props6.mountNode, constrain = _this$props6.constrain, positionTarget = _this$props6.positionTarget, positionContainerDisplay = _this$props6.positionContainerDisplay, popoverRef = _this$props6.popoverRef, disabled = _this$props6.disabled, onDismiss = _this$props6.onDismiss, onFocus = _this$props6.onFocus, onMouseOver = _this$props6.onMouseOver, offsetX = _this$props6.offsetX, offsetY = _this$props6.offsetY; return trigger ? jsx(Popover, { isShowingContent: show, defaultIsShowingContent: defaultShow, shouldCloseOnDocumentClick: true, onHideContent: (event, { documentClick }) => { if (typeof onDismiss === 'function') { onDismiss(event, documentClick); } this.reset(); this.handleToggle(event, false); }, onShowContent: event => this.handleToggle(event, true), mountNode: mountNode, placement: placement, withArrow: withArrow, positionTarget: positionTarget, positionContainerDisplay: positionContainerDisplay, constrain: constrain, shouldContainFocus: shouldContainFocus, shouldReturnFocus: shouldReturnFocus, id: this._id, on: ['click'], onFocus: onFocus, onMouseOver: onMouseOver, offsetX: offsetX, offsetY: offsetY, elementRef: element => { // setting ref for "Popover" version, the popover root // (if there is no trigger, we set it in handleDrilldownRef) this.handleRef(element); }, ref: el => { this._popover = el; if (typeof popoverRef === 'function') { popoverRef(el); } }, renderTrigger: safeCloneElement(trigger, { ref: el => { this._trigger = el; }, 'aria-haspopup': this.props.role, id: this._triggerId, disabled: !!(trigger.props.disabled || disabled), 'aria-disabled': trigger.props.disabled || disabled ? 'true' : void 0 }) }, this.renderPage()) : this.renderPage(); } }, _class2.displayName = "Drilldown", _class2.componentId = 'Drilldown', _class2.propTypes = propTypes, _class2.allowedProps = allowedProps, _class2.defaultProps = { disabled: false, rotateFocus: true, as: 'ul', role: 'menu', overflowX: 'auto', overflowY: 'auto', placement: 'bottom center', defaultShow: false, shouldHideOnSelect: true, shouldContainFocus: false, shouldReturnFocus: true, withArrow: true, offsetX: 0, offsetY: 0 }, _class2.Group = DrilldownGroup, _class2.Option = DrilldownOption, _class2.Page = DrilldownPage, _class2.Separator = DrilldownSeparator, _class2)) || _class) || _class) || _class); export default Drilldown; export { Drilldown };