UNPKG

@grafana/ui

Version:
235 lines (232 loc) • 7.61 kB
import { jsx, jsxs } from 'react/jsx-runtime'; import { cx, css } from '@emotion/css'; import { forwardRef, useContext } from 'react'; import { useMeasure } from 'react-use'; import { useTheme2 } from '../../themes/ThemeContext.mjs'; import { stylesFactory } from '../../themes/stylesFactory.mjs'; import { getFocusStyle, sharedInputStyle } from '../Forms/commonStyles.mjs'; import { Spinner } from '../Spinner/Spinner.mjs'; import { AutoSizeInputContext } from './AutoSizeInputContext.mjs'; "use strict"; const Input = forwardRef((props, ref) => { const { className, addonAfter, addonBefore, prefix, suffix: suffixProp, invalid, loading, width = 0, ...restProps } = props; const [prefixRef, prefixRect] = useMeasure(); const [suffixRef, suffixRect] = useMeasure(); const isInAutoSizeInput = useContext(AutoSizeInputContext); const accessoriesWidth = (prefixRect.width || 0) + (suffixRect.width || 0); const autoSizeWidth = isInAutoSizeInput && width ? width + accessoriesWidth / 8 : void 0; const theme = useTheme2(); const styles = getInputStyles({ theme, invalid: !!invalid, width: autoSizeWidth ? void 0 : width }); const suffix = suffixProp || loading && /* @__PURE__ */ jsx(Spinner, { inline: true }); return /* @__PURE__ */ jsxs( "div", { className: cx(styles.wrapper, className), style: autoSizeWidth ? { width: theme.spacing(autoSizeWidth) } : void 0, "data-testid": "input-wrapper", children: [ !!addonBefore && /* @__PURE__ */ jsx("div", { className: styles.addon, children: addonBefore }), /* @__PURE__ */ jsxs("div", { className: styles.inputWrapper, children: [ prefix && /* @__PURE__ */ jsx("div", { className: styles.prefix, ref: prefixRef, children: prefix }), /* @__PURE__ */ jsx( "input", { ref, className: styles.input, ...restProps, style: { paddingLeft: prefix ? prefixRect.width + 12 : void 0, paddingRight: suffix || loading ? suffixRect.width + 12 : void 0 } } ), suffix && /* @__PURE__ */ jsx("div", { className: styles.suffix, ref: suffixRef, children: suffix }) ] }), !!addonAfter && /* @__PURE__ */ jsx("div", { className: styles.addon, children: addonAfter }) ] } ); }); Input.displayName = "Input"; const getInputStyles = stylesFactory(({ theme, invalid = false, width }) => { const prefixSuffixStaticWidth = "28px"; const prefixSuffix = css({ position: "absolute", top: 0, zIndex: 1, display: "flex", alignItems: "center", justifyContent: "center", flexGrow: 0, flexShrink: 0, fontSize: theme.typography.size.md, height: "100%", /* Min width specified for prefix/suffix classes used outside React component*/ minWidth: prefixSuffixStaticWidth, color: theme.colors.text.secondary }); return { // Wraps inputWrapper and addons wrapper: cx( css({ label: "input-wrapper", display: "flex", width: width ? theme.spacing(width) : "100%", height: theme.spacing(theme.components.height.md), borderRadius: theme.shape.radius.default, "&:hover": { "> .prefix, .suffix, .input": { borderColor: invalid ? theme.colors.error.border : theme.colors.primary.border }, // only show number buttons on hover "input[type='number']": { appearance: "textfield" }, "input[type='number']::-webkit-inner-spin-button, input[type='number']::-webkit-outer-spin-button": { // Need type assertion here due to the use of !important // see https://github.com/frenic/csstype/issues/114#issuecomment-697201978 // eslint-disable-next-line @typescript-eslint/consistent-type-assertions WebkitAppearance: "inner-spin-button !important", opacity: 1 } } }) ), // Wraps input and prefix/suffix inputWrapper: css({ label: "input-inputWrapper", position: "relative", flexGrow: 1, /* we want input to be above addons, especially for focused state */ zIndex: 1, /* when input rendered with addon before only*/ "&:not(:first-child):last-child": { "> input": { borderLeft: "none", borderTopLeftRadius: "unset", borderBottomLeftRadius: "unset" } }, /* when input rendered with addon after only*/ "&:first-child:not(:last-child)": { "> input": { borderRight: "none", borderTopRightRadius: "unset", borderBottomRightRadius: "unset" } }, /* when rendered with addon before and after */ "&:not(:first-child):not(:last-child)": { "> input": { borderRight: "none", borderTopRightRadius: "unset", borderBottomRightRadius: "unset", borderTopLeftRadius: "unset", borderBottomLeftRadius: "unset" } }, input: { /* paddings specified for classes used outside React component */ "&:not(:first-child)": { paddingLeft: prefixSuffixStaticWidth }, "&:not(:last-child)": { paddingRight: prefixSuffixStaticWidth }, "&[readonly]": { cursor: "default" } } }), input: cx( getFocusStyle(theme), sharedInputStyle(theme, invalid), css({ label: "input-input", position: "relative", zIndex: 0, flexGrow: 1, borderRadius: theme.shape.radius.default, height: "100%", width: "100%" }) ), inputDisabled: css({ backgroundColor: theme.colors.action.disabledBackground, color: theme.colors.action.disabledText, border: `1px solid ${theme.colors.action.disabledBackground}`, "&:focus": { boxShadow: "none" } }), addon: css({ label: "input-addon", display: "flex", justifyContent: "center", alignItems: "center", flexGrow: 0, flexShrink: 0, position: "relative", "&:first-child": { borderTopRightRadius: "unset", borderBottomRightRadius: "unset", "> :last-child": { borderTopRightRadius: "unset", borderBottomRightRadius: "unset" } }, "&:last-child": { borderTopLeftRadius: "unset", borderBottomLeftRadius: "unset", "> :first-child": { borderTopLeftRadius: "unset", borderBottomLeftRadius: "unset" } }, "> *:focus": { /* we want anything that has focus and is an addon to be above input */ zIndex: 2 } }), prefix: cx( prefixSuffix, css({ label: "input-prefix", paddingLeft: theme.spacing(1), paddingRight: theme.spacing(0.5), borderRight: "none", borderTopRightRadius: "unset", borderBottomRightRadius: "unset" }) ), suffix: cx( prefixSuffix, css({ label: "input-suffix", paddingLeft: theme.spacing(1), paddingRight: theme.spacing(1), borderLeft: "none", borderTopLeftRadius: "unset", borderBottomLeftRadius: "unset", right: 0 }) ), loadingIndicator: css({ "& + *": { marginLeft: theme.spacing(0.5) } }) }; }); export { Input, getInputStyles }; //# sourceMappingURL=Input.mjs.map