UNPKG

@react-md/form

Version:

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

146 lines 11 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; }; var __read = (this && this.__read) || function (o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; }; import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime"; import { forwardRef, useCallback, useMemo, useRef } from "react"; import cn from "classnames"; import { useIcon } from "@react-md/icon"; import { useFixedPositioning } from "@react-md/transition"; import { BELOW_CENTER_ANCHOR, bem, DEFAULT_GET_ITEM_VALUE, tryToSubmitRelatedForm, useCloseOnOutsideClick, useEnsuredRef, useToggle, } from "@react-md/utils"; import { useFormTheme } from "../FormThemeProvider"; import { FloatingLabel } from "../label/FloatingLabel"; import { TextFieldContainer } from "../text-field/TextFieldContainer"; import { useFocusState } from "../useFocusState"; import { Listbox } from "./Listbox"; import { defaultIsOptionDisabled, getDisplayLabel as DEFAULT_GET_DISPLAY_LABEL, getOptionId as DEFAULT_GET_OPTION_ID, getOptionLabel as DEFAULT_GET_OPTION_LABEL, } from "./utils"; var block = bem("rmd-select"); /** * This component is an accessible version of the `<select>` element that allows * for some more custom styles by using the `@react-md/list` package to render * the list of options. * * The `Select` component **must be controlled** with a `value` and `onChange` * handler. * * Note: Since this is not a native `<select>` component, the current value will * be rendered in an `<input type="hidden" />` element so that the value can be * sent along in forms. It is highly recommended to always provide a `name` prop * so this value is sent. */ export var Select = forwardRef(function Select(_a, forwardedRef) { var onBlur = _a.onBlur, onFocus = _a.onFocus, onKeyDown = _a.onKeyDown, onClick = _a.onClick, className = _a.className, label = _a.label, labelStyle = _a.labelStyle, labelClassName = _a.labelClassName, displayLabelStyle = _a.displayLabelStyle, displayLabelClassName = _a.displayLabelClassName, propListboxStyle = _a.listboxStyle, listboxClassName = _a.listboxClassName, _b = _a.anchor, anchor = _b === void 0 ? BELOW_CENTER_ANCHOR : _b, propTheme = _a.theme, _c = _a.dense, dense = _c === void 0 ? false : _c, _d = _a.inline, inline = _d === void 0 ? false : _d, _e = _a.error, error = _e === void 0 ? false : _e, _f = _a.stretch, stretch = _f === void 0 ? false : _f, _g = _a.disabled, disabled = _g === void 0 ? false : _g, _h = _a.isLeftAddon, isLeftAddon = _h === void 0 ? true : _h, _j = _a.isRightAddon, isRightAddon = _j === void 0 ? true : _j, propUnderlineDirection = _a.underlineDirection, _k = _a.listboxWidth, listboxWidth = _k === void 0 ? "equal" : _k, positionOptions = _a.positionOptions, _l = _a.portal, portal = _l === void 0 ? true : _l, portalInto = _a.portalInto, portalIntoId = _a.portalIntoId, name = _a.name, options = _a.options, _m = _a.labelKey, labelKey = _m === void 0 ? "label" : _m, _o = _a.valueKey, valueKey = _o === void 0 ? "value" : _o, _p = _a.getOptionId, getOptionId = _p === void 0 ? DEFAULT_GET_OPTION_ID : _p, _q = _a.getOptionLabel, getOptionLabel = _q === void 0 ? DEFAULT_GET_OPTION_LABEL : _q, _r = _a.getOptionValue, getOptionValue = _r === void 0 ? DEFAULT_GET_ITEM_VALUE : _r, _s = _a.getDisplayLabel, getDisplayLabel = _s === void 0 ? DEFAULT_GET_DISPLAY_LABEL : _s, _t = _a.isOptionDisabled, isOptionDisabled = _t === void 0 ? defaultIsOptionDisabled : _t, _u = _a.disableLeftAddon, disableLeftAddon = _u === void 0 ? false : _u, _v = _a.disableMovementChange, disableMovementChange = _v === void 0 ? false : _v, _w = _a.closeOnResize, closeOnResize = _w === void 0 ? false : _w, _x = _a.closeOnScroll, closeOnScroll = _x === void 0 ? false : _x, readOnly = _a.readOnly, placeholder = _a.placeholder, value = _a.value, onChange = _a.onChange, propRightChildren = _a.rightChildren, props = __rest(_a, ["onBlur", "onFocus", "onKeyDown", "onClick", "className", "label", "labelStyle", "labelClassName", "displayLabelStyle", "displayLabelClassName", "listboxStyle", "listboxClassName", "anchor", "theme", "dense", "inline", "error", "stretch", "disabled", "isLeftAddon", "isRightAddon", "underlineDirection", "listboxWidth", "positionOptions", "portal", "portalInto", "portalIntoId", "name", "options", "labelKey", "valueKey", "getOptionId", "getOptionLabel", "getOptionValue", "getDisplayLabel", "isOptionDisabled", "disableLeftAddon", "disableMovementChange", "closeOnResize", "closeOnScroll", "readOnly", "placeholder", "value", "onChange", "rightChildren"]); var id = props.id; var rightChildren = useIcon("dropdown", propRightChildren); var _y = useFormTheme({ theme: propTheme, underlineDirection: propUnderlineDirection, }), theme = _y.theme, underlineDirection = _y.underlineDirection; var valued = typeof value === "number" || !!value; var displayValue = useMemo(function () { var currentOption = options.find(function (option) { return getOptionValue(option, valueKey) === value; }) || null; return getDisplayLabel(currentOption, labelKey, !disableLeftAddon); }, [ options, getDisplayLabel, labelKey, disableLeftAddon, getOptionValue, valueKey, value, ]); var _z = __read(useToggle(false), 3), visible = _z[0], show = _z[1], hide = _z[2]; var _0 = __read(useFocusState({ onBlur: onBlur, onFocus: onFocus }), 3), focused = _0[0], handleFocus = _0[1], handleBlur = _0[2]; var handleKeyDown = useCallback(function (event) { if (onKeyDown) { onKeyDown(event); } if (tryToSubmitRelatedForm(event)) { return; } switch (event.key) { case " ": case "ArrowUp": case "ArrowDown": // prevent page scroll event.preventDefault(); show(); break; // no default } }, [onKeyDown, show]); var _1 = __read(useEnsuredRef(forwardedRef), 2), ref = _1[0], refHandler = _1[1]; useCloseOnOutsideClick({ enabled: visible, element: ref.current, onOutsideClick: hide, }); var nodeRef = useRef(null); var _2 = useFixedPositioning(__assign(__assign({ style: propListboxStyle, fixedTo: ref, nodeRef: nodeRef, anchor: anchor, onScroll: closeOnScroll ? hide : undefined, onResize: closeOnResize ? hide : undefined, transformOrigin: true, width: listboxWidth }, positionOptions), { onEntering: function () { var _a; // can't do onEnter since the positioning styles haven't been applied to the // dom node at this time. this means the list is the last element in the DOM // when portalled, which causes the page to scroll to the end. Moving it to // onEntering will ensure the styles have been applied and won't cause page // scrolling (_a = nodeRef.current) === null || _a === void 0 ? void 0 : _a.focus(); } })), listboxRef = _2.ref, listboxStyle = _2.style, transitionOptions = _2.callbacks; var handleClick = useCallback(function (event) { if (onClick) { onClick(event); } show(); }, [onClick, show]); var handleKeyboardClose = useCallback(function () { hide(); if (ref.current) { ref.current.focus(); } }, [hide, ref]); var labelId = "".concat(id, "-label"); var valueId = "".concat(id, "-value"); var listboxId = "".concat(id, "-listbox"); var displayValueId = "".concat(id, "-display-value"); return (_jsxs(_Fragment, { children: [_jsxs(TextFieldContainer, __assign({}, props, { "aria-haspopup": "listbox", "aria-disabled": disabled || undefined, ref: refHandler, role: "button", dense: dense, tabIndex: disabled ? undefined : 0, label: !!label, onFocus: handleFocus, onBlur: handleBlur, onKeyDown: disabled ? undefined : handleKeyDown, onClick: disabled ? undefined : handleClick, theme: theme, error: error, active: focused || visible, inline: inline, stretch: stretch, disabled: disabled, underlineDirection: underlineDirection, isLeftAddon: isLeftAddon, isRightAddon: isRightAddon, rightChildren: rightChildren, className: cn(block({ disabled: disabled }), className) }, { children: [_jsx(FloatingLabel, __assign({ id: labelId, style: labelStyle, className: cn(block("label"), labelClassName), htmlFor: id, error: error, active: valued && (focused || visible), valued: valued, floating: focused || valued || visible, dense: dense, disabled: disabled, component: "span" }, { children: label })), _jsx("span", __assign({ id: displayValueId, style: displayLabelStyle, className: cn(block("value", { disabled: disabled, readonly: readOnly, placeholder: !valued && placeholder, "placeholder-active": !valued && placeholder && (focused || visible), }), displayLabelClassName) }, { children: displayValue || (!valued && placeholder) })), _jsx("input", { id: valueId, type: "hidden", name: name, value: value })] })), _jsx(Listbox, __assign({ id: listboxId, ref: listboxRef }, transitionOptions, { "aria-labelledby": id, style: listboxStyle, className: listboxClassName, name: name, readOnly: readOnly, portal: portal, portalInto: portalInto, portalIntoId: portalIntoId, value: value, onChange: onChange, visible: visible, temporary: true, onRequestClose: handleKeyboardClose, options: options, labelKey: labelKey, valueKey: valueKey, getOptionId: getOptionId, getOptionLabel: getOptionLabel, getOptionValue: getOptionValue, isOptionDisabled: isOptionDisabled, disableMovementChange: disableMovementChange }))] })); }); //# sourceMappingURL=Select.js.map