@grafana/ui
Version:
Grafana Components Library
235 lines (232 loc) • 7.61 kB
JavaScript
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