UNPKG

@react-md/form

Version:

This package is for creating all the different form input types.

198 lines 10.1 kB
var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; import { jsx as _jsx } from "react/jsx-runtime"; import { forwardRef, useCallback, useEffect, useRef } from "react"; import cn from "classnames"; import { List } from "@react-md/list"; import { ConditionalPortal } from "@react-md/portal"; import { useScaleTransition } from "@react-md/transition"; import { bem, DEFAULT_GET_ITEM_VALUE, MovementPresets, omit, scrollIntoView, useActiveDescendantMovement, } from "@react-md/utils"; import { Option } from "./Option"; import { defaultIsOptionDisabled, getOptionId as DEFAULT_GET_OPTION_ID, getOptionLabel as DEFAULT_GET_OPTION_LABEL, isListboxOptionProps, } from "./utils"; var block = bem("rmd-listbox"); var warned; /** * This component is used to render the list part of a `<select>` element with * built-in accessibility and the ability to add custom styles. This should * probably not be used much outside of `react-md` itself and the `Select` * component, but I'm planning on adding support for an inline listbox at some * point. */ export var Listbox = forwardRef(function Listbox(_a, nodeRef) { var propClassName = _a.className, _b = _a.visible, visible = _b === void 0 ? true : _b, _c = _a.temporary, temporary = _c === void 0 ? false : _c, _d = _a.labelKey, labelKey = _d === void 0 ? "label" : _d, _e = _a.valueKey, valueKey = _e === void 0 ? "value" : _e, _f = _a.getOptionId, getOptionId = _f === void 0 ? DEFAULT_GET_OPTION_ID : _f, _g = _a.getOptionLabel, getOptionLabel = _g === void 0 ? DEFAULT_GET_OPTION_LABEL : _g, _h = _a.getOptionValue, getOptionValue = _h === void 0 ? DEFAULT_GET_ITEM_VALUE : _h, _j = _a.isOptionDisabled, isOptionDisabled = _j === void 0 ? defaultIsOptionDisabled : _j, _k = _a.disableMovementChange, disableMovementChange = _k === void 0 ? false : _k, onFocus = _a.onFocus, propOnKeyDown = _a.onKeyDown, name = _a.name, options = _a.options, value = _a.value, onChange = _a.onChange, propTabIndex = _a.tabIndex, portal = _a.portal, portalInto = _a.portalInto, portalIntoId = _a.portalIntoId, onRequestClose = _a.onRequestClose, timeout = _a.timeout, readOnly = _a.readOnly, classNames = _a.classNames, onEnter = _a.onEnter, onEntering = _a.onEntering, onEntered = _a.onEntered, onExit = _a.onExit, onExiting = _a.onExiting, onExited = _a.onExited, props = __rest(_a, ["className", "visible", "temporary", "labelKey", "valueKey", "getOptionId", "getOptionLabel", "getOptionValue", "isOptionDisabled", "disableMovementChange", "onFocus", "onKeyDown", "name", "options", "value", "onChange", "tabIndex", "portal", "portalInto", "portalIntoId", "onRequestClose", "timeout", "readOnly", "classNames", "onEnter", "onEntering", "onEntered", "onExit", "onExiting", "onExited"]); var id = props.id; var tabIndex = propTabIndex; if (temporary) { tabIndex = -1; } else if (typeof propTabIndex === "undefined") { tabIndex = 0; } /** * Gets the current index of the option that has the same value as the * provided prop value. */ var getIndex = useCallback(function () { return options.findIndex(function (option) { return value === getOptionValue(option, valueKey); }); }, [getOptionValue, options, value, valueKey]); /** * Conditionally calls the onChange callback with the new value and option if * the value has changed. This will be called when: * - the user presses the enter or space key while "focusing" an option * - the user keyboard navigates to a new option while the * `disableMovementChange` prop is `false` * - the user clicks the option with a mouse or touch */ var handleChange = useCallback(function (index) { if (readOnly) { return; } var option = options[index]; if (!option || isOptionDisabled(option)) { return; } var optionValue = getOptionValue(option, valueKey); if (value !== optionValue) { onChange(optionValue, options[index], { id: id, name: name, value: value, valueKey: valueKey, options: options, }); } }, [ getOptionValue, id, isOptionDisabled, name, onChange, options, readOnly, value, valueKey, ]); var handleKeyboardClick = useCallback(function (focusedIndex) { handleChange(focusedIndex); if (temporary && onRequestClose) { onRequestClose(); } }, [handleChange, onRequestClose, temporary]); var _l = useActiveDescendantMovement(__assign(__assign({}, MovementPresets.VERTICAL_LISTBOX), { defaultFocusedIndex: getIndex, items: options, baseId: id, valueKey: labelKey, getId: getOptionId, getItemValue: function (option, key) { if (!isListboxOptionProps(option)) { return "".concat(option); } var search = option[key]; if (typeof search === "number" || typeof search === "string") { return "".concat(search); } if (process.env.NODE_ENV !== "production") { if (!warned) { warned = new Set(); } if (!warned.has(id)) { /* eslint-disable no-console */ console.warn("A listbox with an id of \"".concat(id, "\" has an option that does not have a searchable label string. ") + "Users will be unable to use the typeahead feature in the Listbox component until this is fixed. " + "To fix this warning, you can use the `labelKey` prop on the `Listbox`/`Select` component to point " + "to a string on the following option:", option); warned.add(id); } } return ""; }, onChange: function (data) { if (disableMovementChange) { return; } handleChange(data.index); }, onEnter: handleKeyboardClick, onSpace: handleKeyboardClick, onKeyDown: function (event) { if (propOnKeyDown) { propOnKeyDown(event); } switch (event.key) { case "Tab": case "Escape": if (event.key === "Escape") { event.stopPropagation(); } if (temporary && onRequestClose) { onRequestClose(); } break; // no default } } })), activeId = _l.activeId, itemRefs = _l.itemRefs, onKeyDown = _l.onKeyDown, focusedIndex = _l.focusedIndex, setFocusedIndex = _l.setFocusedIndex; var prevVisible = useRef(visible); useEffect(function () { if (prevVisible.current === visible) { return; } prevVisible.current = visible; // whenever it gains visibility, try to set the focused index to the // current active value if (visible) { setFocusedIndex(getIndex()); } }, [getIndex, setFocusedIndex, visible]); var handleFocus = useCallback(function (event) { if (onFocus) { onFocus(event); } var item = itemRefs[focusedIndex] && itemRefs[focusedIndex].current; if (item) { scrollIntoView(event.currentTarget, item); } }, [focusedIndex, itemRefs, onFocus]); var _m = useScaleTransition({ nodeRef: nodeRef, timeout: timeout, className: cn(block({ temporary: temporary }), propClassName), classNames: classNames, transitionIn: visible, onEnter: onEnter, onEntering: onEntering, onEntered: onEntered, onExit: onExit, onExiting: onExiting, onExited: onExited, }), elementProps = _m.elementProps, rendered = _m.rendered; return (_jsx(ConditionalPortal, __assign({ portal: portal, portalInto: portalInto, portalIntoId: portalIntoId }, { children: rendered && (_jsx(List, __assign({}, props, elementProps, { "aria-activedescendant": activeId, role: "listbox", tabIndex: tabIndex, onFocus: handleFocus, onKeyDown: onKeyDown }, { children: options.map(function (option, i) { var optionId = getOptionId(id, i); var optionValue = getOptionValue(option, valueKey); var optionLabel = getOptionLabel(option, labelKey); var optionProps; if (isListboxOptionProps(option)) { optionProps = omit(option, [labelKey, valueKey]); } var disabled = isOptionDisabled(option); var onClick; if (!readOnly && !disabled) { onClick = function () { handleChange(i); setFocusedIndex(i); }; } return (_jsx(Option, __assign({ id: optionId, disabled: disabled }, optionProps, { ref: itemRefs[i], focused: optionId === activeId, selected: value === optionValue, onClick: onClick }, { children: optionLabel }), optionValue)); }) }))) }))); }); //# sourceMappingURL=Listbox.js.map