UNPKG

@sap-ux/ui-components

Version:
253 lines 11.3 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.UIDropdown = exports.UIDropdownMenuItemType = void 0; const react_1 = __importDefault(require("react")); const react_2 = require("@fluentui/react"); Object.defineProperty(exports, "UIDropdownMenuItemType", { enumerable: true, get: function () { return react_2.DropdownMenuItemType; } }); const UIIcon_1 = require("../UIIcon"); const ValidationMessage_1 = require("../../helper/ValidationMessage"); const UILabel_1 = require("../UILabel"); const utils_1 = require("./utils"); const UICallout_1 = require("../UICallout"); const types_1 = require("../types"); require("./UIDropdown.scss"); const ERROR_BORDER_COLOR = 'var(--vscode-inputValidation-errorBorder)'; /** * UIDropdown component * based on https://developer.microsoft.com/en-us/fluentui#/controls/web/dropdown * * @exports * @class UIDropdown * @extends {React.Component<UIDropdownProps, UIDropdownState>} */ class UIDropdown extends react_1.default.Component { /** * Initializes component properties. * * @param {UIDropdownProps} props */ constructor(props) { super(props); this.dropdownDomRef = react_1.default.createRef(); this.menuDomRef = react_1.default.createRef(); this.calloutCollisionTransform = new UICallout_1.CalloutCollisionTransform(this.dropdownDomRef, this.menuDomRef); this.onRenderCaretDown = () => { return react_1.default.createElement(UIIcon_1.UIIcon, { iconName: "ArrowDown", style: { height: '100%' } }); }; this.onRenderTitle = (items) => { if (this.props.multiSelect && this.props.onHandleRenderTitle) { return this.props.onHandleRenderTitle(items); } else { const { multiSelectDelimiter = ', ' } = this.props; if (items) { const title = items.map((i) => i.text).join(multiSelectDelimiter); return react_1.default.createElement(react_1.default.Fragment, null, title); } else { return react_1.default.createElement(react_1.default.Fragment, null, this.props.title); } } }; this.onClick = ( /* event: React.FormEvent<HTMLDivElement>, option?: IDropdownOption, index?: number */) => { this.setState((prevState) => ({ isOpen: !prevState.isOpen }), () => { if (this.props.multiSelect && this.props.onHandleOpen) { if (this.state.isOpen) { this.setState((prevState) => ({ isOpen: !prevState.isOpen })); this.props.onHandleOpen(); } } }); }; this.onChange = (event, option, index) => { if (this.props.multiSelect && this.props.onHandleChange) { if (option && index) { this.props.onHandleChange(option, index); } } }; /** * Method stops event propagation. * * @param {React.MouseEvent<HTMLDivElement>} event Mouse event. */ this.stopEventPropagation = (event) => { event.stopPropagation(); }; /** * Method used as workaround to separate focus and hover. * Default behaviour of fluent ui is that focus follows hover, but we need separe them. * * @param {IDropdownOption} [props] Dropdown props. * @param {(props?: IDropdownOption) => JSX.Element | null} [defaultRender] Default option renderer. * @returns {JSX.Element | null} Returns dropdown option element. */ this._onRenderOption = (props, defaultRender) => { return (react_1.default.createElement(react_1.default.Fragment, null, defaultRender?.(props), props?.itemType !== react_2.DropdownMenuItemType.Header && (react_1.default.createElement("div", { onMouseEnter: this.stopEventPropagation.bind(this), onMouseLeave: this.stopEventPropagation.bind(this), onMouseMove: this.stopEventPropagation.bind(this), className: "ts-dropdown-item-blocker" })))); }; /** * Render dropdown menu option. * * @param {IDropdownOption} [props] Dropdown props. * @param {(props?: IDropdownOption) => JSX.Element | null} [defaultRender] Default option renderer. * @returns {JSX.Element | null} Returns dropdown option element. */ this.onRenderOption = (props, defaultRender) => { if (this.props.onRenderOption) { return this.props.onRenderOption(props, this._onRenderOption.bind(this, props, defaultRender)); } return this._onRenderOption(props, defaultRender); }; /** * Method called on combobox item render. * We should pass query to it and avoid rendering if it is hidden. * * @param {IComboBoxOption} props Combobox item props. * @param {Function} defaultRender Combobox item default renderer. * @returns {JSX.Element | null} Element to render. */ this._onRenderItem = (props, defaultRender) => { if (defaultRender && props) { if (props.title === undefined) { // Apply title by default if property not provided // In older fluent-ui versions it was applied by default, but behavior changed in version '8.66.2' props.title = props.text; } return defaultRender(props); } return null; }; /** * Render dropdown menu item. * * @param {IComboBoxOption} props Combobox item props. * @param {Function} defaultRender Combobox item default renderer. * @returns {JSX.Element | null} Element to render. */ this.onRenderItem = (props, defaultRender) => { if (this.props.onRenderItem) { return this.props.onRenderItem(props, this._onRenderItem.bind(this, props, defaultRender)); } return this._onRenderItem(props, defaultRender); }; this.state = { options: [], isOpen: false }; } /** * Method returns styles for callout to support property 'useDropdownAsMenuMinWidth'. * States: * 1. Min width of callout is equals to width of droipdown input box; * 2. Max width equals to windows size minus 10px; * 3. Width is auto - it allows to make callout wider if menu option size exceeds size of dropdown input(min-width). * * @param {ICalloutContentStyleProps} calloutStyleProps Current callout styles. * @returns {Partial<ICalloutContentStyles>} Styles for callout. */ getCalloutStylesForUseAsMinWidth(calloutStyleProps) { return { root: { minWidth: calloutStyleProps.calloutWidth, width: 'auto', maxWidth: 'calc(100% - 10px)' } }; } /** * Method returns class names string depending on props and component state. * * @param {InputValidationMessageInfo} messageInfo Error/warning message if applied * @returns {string} Class names of root dropdown element. */ getClassNames(messageInfo) { const { className, readOnly, disabled } = this.props; const errorSuffix = messageInfo.message ? ValidationMessage_1.MESSAGE_TYPES_CLASSNAME_MAP.get(messageInfo.type) : undefined; let classNames = `ts-SelectBox${messageInfo.message ? ' ts-SelectBox--' + errorSuffix : ''}`; // Readonly if (readOnly && !disabled) { classNames += ' ts-SelectBox--readonly'; } // Disabled if (disabled) { classNames += ' ts-SelectBox--disabled'; } // Custom external classes if (className) { classNames += ` ${className}`; } // Empty value if ((0, utils_1.isDropdownEmpty)(this.props)) { classNames += ' ts-SelectBox--empty'; } return classNames; } /** * Method returns additional component properties for accessibility. * * @returns {AccessibilityProps} Additional properties. */ getAccessibilityProps() { const { readOnly, disabled } = this.props; const additionalProps = {}; if (readOnly && !disabled) { // Make dropdown focusable additionalProps.tabIndex = 0; additionalProps['data-is-focusable'] = true; // Adjust aria attributes for readonly additionalProps['aria-disabled'] = undefined; additionalProps['aria-readonly'] = true; } else if (disabled) { additionalProps.tabIndex = 0; additionalProps['data-is-focusable'] = true; } return additionalProps; } /** * @returns {JSX.Element} */ render() { const messageInfo = (0, ValidationMessage_1.getMessageInfo)(this.props); const additionalProps = this.getAccessibilityProps(); const dropdownStyles = () => ({ ...{ label: { ...UILabel_1.labelGlobalStyle, ...(this.props.disabled && { opacity: '0.4' }), ...(this.props.required && { selectors: { '::after': { content: types_1.REQUIRED_LABEL_INDICATOR, color: ERROR_BORDER_COLOR, paddingRight: 12 } } }) }, errorMessage: [messageInfo.style] } }); return (react_1.default.createElement(react_2.Dropdown, { ref: this.dropdownDomRef, onRenderCaretDown: this.onRenderCaretDown, onClick: this.onClick, onChange: this.onChange, onRenderTitle: this.onRenderTitle, // Use default responsiveMode as xxxLarge, which does not enter mobile mode. responsiveMode: react_2.ResponsiveMode.xxxLarge, disabled: this.props.readOnly, ...additionalProps, ...this.props, calloutProps: { calloutMaxHeight: 200, styles: this.props.useDropdownAsMenuMinWidth ? this.getCalloutStylesForUseAsMinWidth : undefined, className: 'ts-Callout ts-Callout-Dropdown', popupProps: { ref: this.menuDomRef }, ...this.props.calloutProps, ...(0, utils_1.getCalloutCollisionTransformationPropsForDropdown)(this, this.calloutCollisionTransform) }, onRenderOption: this.onRenderOption.bind(this), onRenderItem: this.onRenderItem.bind(this), styles: dropdownStyles, className: this.getClassNames(messageInfo), errorMessage: messageInfo.message })); } } exports.UIDropdown = UIDropdown; //# sourceMappingURL=UIDropdown.js.map