@react-md/form
Version:
This package is for creating all the different form input types.
146 lines • 11 kB
JavaScript
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