UNPKG

react-lightning-design-system

Version:

Salesforce Lightning Design System components built with React

552 lines (528 loc) 22.5 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); var _typeof3 = require("@babel/runtime/helpers/typeof"); Object.defineProperty(exports, "__esModule", { value: true }); exports.PicklistItem = exports.Picklist = void 0; var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray")); var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray")); var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties")); var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof")); var _react = _interopRequireWildcard(require("react")); var _classnames = _interopRequireDefault(require("classnames")); var _FormElement = require("./FormElement"); var _Icon = require("./Icon"); var _AutoAlign = require("./AutoAlign"); var _util = require("./util"); var _ComponentSettings = require("./ComponentSettings"); var _hooks = require("./hooks"); var _common = require("./common"); var _excluded = ["id", "className", "value", "defaultValue", "opened", "defaultOpened", "multiSelect", "selectedText", "optionsSelectedText", "menuSize", "menuStyle", "disabled", "label", "required", "error", "cols", "tooltip", "tooltipIcon", "elementRef", "buttonRef", "dropdownRef", "onSelect", "onComplete", "onValueChange", "onBlur", "onKeyDown", "children"]; function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); } function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof3(e) && "function" != typeof e) return { "default": e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n["default"] = e, t && t.set(e, n), n; } /** * Recursively collect option values from PicklistItem components */ function collectOptionValues(children) { return _react["default"].Children.map(children, function (child) { if (! /*#__PURE__*/_react["default"].isValidElement(child)) { return []; } var props = child.props; var isPropsObject = (0, _typeof2["default"])(props) === 'object' && props !== null; if (!isPropsObject) { return []; } // Recursively check children for nested PicklistItems if (child.type !== PicklistItem) { return !('children' in props) ? [] : collectOptionValues(props.children); } // Check if this is specifically a PicklistItem component if (!('value' in props) || typeof props.value !== 'string' && typeof props.value !== 'number') { return []; } // Skip disabled items if ('disabled' in props && props.disabled === true) { return []; } return [props.value]; }).flat(); } /** * Recursively find selected item label from PicklistItem components */ function findSelectedItemLabel(children, selectedValue) { return _react["default"].Children.map(children, function (child) { if (! /*#__PURE__*/_react["default"].isValidElement(child)) { return null; } var props = child.props; var isPropsObject = (0, _typeof2["default"])(props) === 'object' && props !== null; if (!isPropsObject) { return null; } // Recursively check children for nested PicklistItems if (child.type !== PicklistItem) { return !('children' in props) ? null : findSelectedItemLabel(props.children, selectedValue); } // Check if this is specifically a PicklistItem component if (!('value' in props) || props.value !== selectedValue) { return null; } // Skip disabled items if ('disabled' in props && props.disabled === true) { return null; } // Safely access label and children properties with proper type checking var label = 'label' in props ? props.label : undefined; var itemChildren = 'children' in props ? props.children : undefined; // Simple type check for React.ReactNode values var labelValue = typeof label === 'string' || typeof label === 'number' || /*#__PURE__*/_react["default"].isValidElement(label) ? label : undefined; var childrenValue = typeof itemChildren === 'string' || typeof itemChildren === 'number' || /*#__PURE__*/_react["default"].isValidElement(itemChildren) || Array.isArray(itemChildren) ? itemChildren : undefined; return labelValue || childrenValue; }); } /** * */ /** * */ var PicklistContext = /*#__PURE__*/(0, _react.createContext)({ values: [], onSelect: function onSelect() { // noop }, optionIdPrefix: '' }); /** * */ /** * */ var Picklist = exports.Picklist = (0, _common.createFC)(function (props) { var id_ = props.id, className = props.className, value_ = props.value, defaultValue = props.defaultValue, opened_ = props.opened, defaultOpened = props.defaultOpened, multiSelect = props.multiSelect, _props$selectedText = props.selectedText, selectedText = _props$selectedText === void 0 ? '' : _props$selectedText, _props$optionsSelecte = props.optionsSelectedText, optionsSelectedText = _props$optionsSelecte === void 0 ? '' : _props$optionsSelecte, menuSize = props.menuSize, menuStyle = props.menuStyle, disabled = props.disabled, label = props.label, required = props.required, error = props.error, cols = props.cols, tooltip = props.tooltip, tooltipIcon = props.tooltipIcon, elementRef_ = props.elementRef, buttonRef_ = props.buttonRef, dropdownRef_ = props.dropdownRef, onSelect = props.onSelect, onComplete = props.onComplete, onValueChange = props.onValueChange, onBlur_ = props.onBlur, onKeyDown_ = props.onKeyDown, children = props.children, rprops = (0, _objectWithoutProperties2["default"])(props, _excluded); var fallbackId = (0, _react.useId)(); var id = id_ !== null && id_ !== void 0 ? id_ : fallbackId; var listboxId = "".concat(id, "-listbox"); var optionIdPrefix = "".concat((0, _react.useId)(), "-option"); var values_ = typeof value_ === 'undefined' ? undefined : value_ == null ? [] : Array.isArray(value_) ? value_ : [value_]; var defaultValues = typeof defaultValue === 'undefined' ? undefined : defaultValue == null ? [] : Array.isArray(defaultValue) ? defaultValue : [defaultValue]; var _useControlledValue = (0, _hooks.useControlledValue)(values_, defaultValues !== null && defaultValues !== void 0 ? defaultValues : []), _useControlledValue2 = (0, _slicedToArray2["default"])(_useControlledValue, 2), values = _useControlledValue2[0], setValues = _useControlledValue2[1]; var _useControlledValue3 = (0, _hooks.useControlledValue)(opened_, defaultOpened !== null && defaultOpened !== void 0 ? defaultOpened : false), _useControlledValue4 = (0, _slicedToArray2["default"])(_useControlledValue3, 2), opened = _useControlledValue4[0], setOpened = _useControlledValue4[1]; var _useState = (0, _react.useState)(), _useState2 = (0, _slicedToArray2["default"])(_useState, 2), focusedValue = _useState2[0], setFocusedValue = _useState2[1]; var _useContext = (0, _react.useContext)(_ComponentSettings.ComponentSettingsContext), getActiveElement = _useContext.getActiveElement; // Memoized option values - recursively collected from PicklistItem components (excluding disabled items) var optionValues = (0, _react.useMemo)(function () { return collectOptionValues(children); }, [children]); // Get next option value for keyboard navigation var getNextValue = (0, _react.useCallback)(function (currentValue) { if (optionValues.length === 0) return undefined; if (!currentValue) return optionValues[0]; var currentIndex = optionValues.indexOf(currentValue); return optionValues[Math.min(currentIndex + 1, optionValues.length - 1)]; // not wrap around }, [optionValues]); // Get previous option value for keyboard navigation var getPrevValue = (0, _react.useCallback)(function (currentValue) { if (optionValues.length === 0) return undefined; if (!currentValue) return optionValues[optionValues.length - 1]; var currentIndex = optionValues.indexOf(currentValue); return optionValues[Math.max(currentIndex - 1, 0)]; // not wrap around }, [optionValues]); // Scroll focused element into view var scrollFocusedElementIntoView = (0, _hooks.useEventCallback)(function (nextFocusedValue) { if (!nextFocusedValue || !dropdownElRef.current) { return; } var dropdownContainer = dropdownElRef.current; var targetElement = dropdownContainer.querySelector("#".concat(CSS.escape("".concat(optionIdPrefix, "-").concat(nextFocusedValue)))); if (!(targetElement instanceof HTMLElement)) { return; } targetElement.focus(); }); // Set initial focus when dropdown opens (0, _react.useEffect)(function () { if (opened && !focusedValue) { // Focus on first selected value or first option var initialFocus = values.length > 0 ? values[0] : optionValues[0]; setFocusedValue(initialFocus); scrollFocusedElementIntoView(initialFocus); } else if (!opened) { // Reset focus when dropdown closes setFocusedValue(undefined); } }, [opened, values, optionValues, focusedValue, scrollFocusedElementIntoView]); var elRef = (0, _react.useRef)(null); var elementRef = (0, _hooks.useMergeRefs)([elRef, elementRef_]); var comboboxElRef = (0, _react.useRef)(null); var buttonRef = (0, _hooks.useMergeRefs)([comboboxElRef, buttonRef_]); var dropdownElRef = (0, _react.useRef)(null); var dropdownRef = (0, _hooks.useMergeRefs)([dropdownElRef, dropdownRef_]); var setPicklistValues = (0, _hooks.useEventCallback)(function (newValues) { var prevValues = values; setValues(newValues); if (onValueChange && prevValues !== newValues) { if (multiSelect) { onValueChange(newValues, prevValues); } else { onValueChange(newValues.length > 0 ? newValues[0] : null, prevValues.length > 0 ? prevValues[0] : null); } } }); var updateItemValue = (0, _hooks.useEventCallback)(function (itemValue) { if (multiSelect) { var newValues = (0, _toConsumableArray2["default"])(values); // toggle value if (newValues.indexOf(itemValue) === -1) { // add value to array newValues.push(itemValue); } else { // remove from array newValues.splice(newValues.indexOf(itemValue), 1); } setPicklistValues(newValues); } else { // set only one value setPicklistValues([itemValue]); setOpened(false); setTimeout(function () { var _comboboxElRef$curren; (_comboboxElRef$curren = comboboxElRef.current) === null || _comboboxElRef$curren === void 0 || _comboboxElRef$curren.focus(); onComplete === null || onComplete === void 0 || onComplete(); }, 10); } }); var isFocusedInComponent = (0, _hooks.useEventCallback)(function () { var targetEl = getActiveElement(); return (0, _util.isElInChildren)(elRef.current, targetEl) || (0, _util.isElInChildren)(dropdownElRef.current, targetEl); }); var onClick = (0, _hooks.useEventCallback)(function () { if (!disabled) { setOpened(function (opened) { return !opened; }); } }); var onPicklistItemSelect = (0, _hooks.useEventCallback)(function (value) { updateItemValue(value); onSelect === null || onSelect === void 0 || onSelect(value); }); var onBlur = (0, _hooks.useEventCallback)(function () { setTimeout(function () { if (!isFocusedInComponent()) { setOpened(false); onBlur_ === null || onBlur_ === void 0 || onBlur_(); onComplete === null || onComplete === void 0 || onComplete(); } }, 10); }); var onKeyDown = (0, _hooks.useEventCallback)(function (e) { if (e.keyCode === 40) { // down e.preventDefault(); e.stopPropagation(); if (!opened) { setOpened(true); } else { // Navigate to next option var nextValue = getNextValue(focusedValue); setFocusedValue(nextValue); scrollFocusedElementIntoView(nextValue); } } else if (e.keyCode === 38) { // up e.preventDefault(); e.stopPropagation(); if (!opened) { setOpened(true); } else { // Navigate to previous option var _prevValue = getPrevValue(focusedValue); setFocusedValue(_prevValue); scrollFocusedElementIntoView(_prevValue); } } else if (e.keyCode === 9) { // Tab or Shift+Tab if (opened) { e.preventDefault(); e.stopPropagation(); var currentIndex = focusedValue ? optionValues.indexOf(focusedValue) : -1; if (e.shiftKey) { // Shift+Tab - Navigate to previous option or close if at first if (currentIndex <= 0) { // At first option or no focus, close the picklist setOpened(false); onComplete === null || onComplete === void 0 || onComplete(); } else { var _prevValue2 = getPrevValue(focusedValue); setFocusedValue(_prevValue2); scrollFocusedElementIntoView(_prevValue2); } } else { // Tab - Navigate to next option or close if at last if (currentIndex >= optionValues.length - 1) { // At last option, close the picklist setOpened(false); onComplete === null || onComplete === void 0 || onComplete(); } else { var _nextValue = getNextValue(focusedValue); setFocusedValue(_nextValue); scrollFocusedElementIntoView(_nextValue); } } } } else if (e.keyCode === 27) { // ESC e.preventDefault(); e.stopPropagation(); setOpened(false); onComplete === null || onComplete === void 0 || onComplete(); } else if (e.keyCode === 13 || e.keyCode === 32) { // Enter or Space e.preventDefault(); e.stopPropagation(); if (opened && focusedValue != null) { // Select focused option onPicklistItemSelect(focusedValue); } else { setOpened(function (opened) { return !opened; }); } } onKeyDown_ === null || onKeyDown_ === void 0 || onKeyDown_(e); }); // Memoized selected item label - displays count for multiple selections, label for single selection, or placeholder text var selectedItemLabel = (0, _react.useMemo)(function () { // many items selected if (values.length > 1) { return optionsSelectedText; } // one item if (values.length === 1) { var selectedValue = values[0]; var selected = findSelectedItemLabel(children, selectedValue); return selected || selectedValue; } // zero items return selectedText; }, [values, optionsSelectedText, selectedText, children]); var hasValue = values.length > 0; var portalClassNames = (0, _classnames["default"])(className, 'react-slds-picklist', 'slds-combobox_container'); var containerClassNames = (0, _classnames["default"])(portalClassNames, 'slds-size_small'); var comboboxClassNames = (0, _classnames["default"])('slds-combobox', 'slds-dropdown-trigger', 'slds-dropdown-trigger_click', { 'slds-is-open': opened }); var inputClassNames = (0, _classnames["default"])('slds-input_faux', 'slds-combobox__input', { 'slds-has-focus': opened && !disabled, 'slds-combobox__input-value': hasValue, 'slds-is-disabled': disabled }); var createDropdownClassNames = (0, _react.useCallback)(function (alignment) { var _alignment = (0, _slicedToArray2["default"])(alignment, 2), vertAlign = _alignment[0], align = _alignment[1]; return (0, _classnames["default"])('slds-dropdown', vertAlign ? "slds-dropdown_".concat(vertAlign) : undefined, align ? "slds-dropdown_".concat(align) : undefined, menuSize ? "slds-dropdown_".concat(menuSize) : 'slds-dropdown_fluid'); }, [menuSize]); var formElemProps = { id: id, label: label, required: required, error: error, cols: cols, tooltip: tooltip, tooltipIcon: tooltipIcon, elementRef: elementRef }; var contextValue = { values: values, multiSelect: multiSelect, onSelect: onPicklistItemSelect, focusedValue: focusedValue, optionIdPrefix: optionIdPrefix }; return /*#__PURE__*/_react["default"].createElement(_FormElement.FormElement, formElemProps, /*#__PURE__*/_react["default"].createElement("div", { className: containerClassNames }, /*#__PURE__*/_react["default"].createElement("div", { className: comboboxClassNames, ref: elementRef }, /*#__PURE__*/_react["default"].createElement("div", { className: "slds-combobox__form-element slds-input-has-icon slds-input-has-icon_right", role: "none" }, /*#__PURE__*/_react["default"].createElement("button", (0, _extends2["default"])({ type: "button", ref: buttonRef, role: "combobox", tabIndex: disabled ? -1 : 0, className: inputClassNames, "aria-controls": listboxId, "aria-expanded": opened, "aria-haspopup": "listbox", "aria-disabled": disabled, "aria-activedescendant": focusedValue ? "".concat(optionIdPrefix, "-").concat(focusedValue) : undefined, onClick: onClick, onKeyDown: onKeyDown, onBlur: onBlur, disabled: disabled }, rprops), /*#__PURE__*/_react["default"].createElement("span", { className: "slds-truncate" }, selectedItemLabel)), /*#__PURE__*/_react["default"].createElement(_Icon.Icon, { containerClassName: "slds-input__icon slds-input__icon_right", category: "utility", icon: "down", size: "x-small", textColor: "default" })), opened && /*#__PURE__*/_react["default"].createElement(_AutoAlign.AutoAlign, { triggerSelector: ".react-slds-picklist", alignmentStyle: "menu", portalClassName: portalClassNames, size: menuSize }, function (_ref) { var alignment = _ref.alignment, autoAlignContentRef = _ref.autoAlignContentRef; return /*#__PURE__*/_react["default"].createElement("div", { id: listboxId, className: createDropdownClassNames(alignment), role: "listbox", "aria-label": "Options", tabIndex: 0, "aria-busy": false, ref: (0, _hooks.useMergeRefs)([dropdownRef, autoAlignContentRef]), style: menuStyle }, /*#__PURE__*/_react["default"].createElement("ul", { className: "slds-listbox slds-listbox_vertical", role: "presentation", onKeyDown: onKeyDown, onBlur: onBlur }, /*#__PURE__*/_react["default"].createElement(PicklistContext.Provider, { value: contextValue }, children))); })))); }, { isFormElement: true }); /** * */ /** * */ var PicklistItem = exports.PicklistItem = function PicklistItem(_ref2) { var label = _ref2.label, selected_ = _ref2.selected, value = _ref2.value, disabled = _ref2.disabled, icon = _ref2.icon, iconRight = _ref2.iconRight, divider = _ref2.divider, onClick_ = _ref2.onClick, children = _ref2.children; var _useContext2 = (0, _react.useContext)(PicklistContext), values = _useContext2.values, multiSelect = _useContext2.multiSelect, onSelect = _useContext2.onSelect, focusedValue = _useContext2.focusedValue, optionIdPrefix = _useContext2.optionIdPrefix; var selected = selected_ !== null && selected_ !== void 0 ? selected_ : value != null ? values.indexOf(value) >= 0 : false; var isFocused = value != null && focusedValue === value; var onClick = (0, _hooks.useEventCallback)(function (e) { if (disabled) { return; } if (value != null) { onSelect(value); } onClick_ === null || onClick_ === void 0 || onClick_(e); }); var itemClassNames = (0, _classnames["default"])('slds-media', 'slds-listbox__option', 'slds-listbox__option_plain', 'slds-media_small', { 'slds-is-selected': selected, 'slds-has-focus': isFocused }); var mainListItem = /*#__PURE__*/_react["default"].createElement("li", { role: "presentation", className: "slds-listbox__item" }, /*#__PURE__*/_react["default"].createElement("div", { id: value ? "".concat(optionIdPrefix, "-").concat(value) : undefined, className: itemClassNames, role: "option", "aria-selected": selected, "aria-checked": multiSelect ? selected : undefined, "aria-disabled": disabled, tabIndex: disabled ? undefined : 0, onClick: onClick }, /*#__PURE__*/_react["default"].createElement("span", { className: "slds-media__figure slds-listbox__option-icon" }, icon ? /*#__PURE__*/_react["default"].createElement(_Icon.Icon, { category: "utility", icon: icon, size: "x-small" }) : selected ? /*#__PURE__*/_react["default"].createElement(_Icon.Icon, { category: "utility", icon: "check", size: "x-small", textColor: "currentColor" }) : null), /*#__PURE__*/_react["default"].createElement("span", { className: "slds-media__body" }, /*#__PURE__*/_react["default"].createElement("span", { className: "slds-truncate", title: String(label || children) }, label || children)), iconRight && /*#__PURE__*/_react["default"].createElement("span", { className: "slds-media__figure slds-media__figure_reverse" }, /*#__PURE__*/_react["default"].createElement(_Icon.Icon, { category: "utility", icon: iconRight, size: "x-small" })))); return /*#__PURE__*/_react["default"].createElement(_react["default"].Fragment, null, divider === 'top' && /*#__PURE__*/_react["default"].createElement("li", { className: "slds-has-divider_".concat(divider, "-space"), role: "separator" }), mainListItem, divider === 'bottom' && /*#__PURE__*/_react["default"].createElement("li", { className: "slds-has-divider_".concat(divider, "-space"), role: "separator" })); }; //# sourceMappingURL=Picklist.js.map