@grafana/ui
Version:
Grafana Components Library
262 lines (257 loc) • 9.14 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var jsxRuntime = require('react/jsx-runtime');
var css = require('@emotion/css');
var React = require('react');
var reactUse = require('react-use');
var ThemeContext = require('../../themes/ThemeContext.cjs');
var stylesFactory = require('../../themes/stylesFactory.cjs');
var FieldContext = require('../Forms/FieldContext.cjs');
var commonStyles = require('../Forms/commonStyles.cjs');
var Spinner = require('../Spinner/Spinner.cjs');
var AutoSizeInputContext = require('./AutoSizeInputContext.cjs');
"use strict";
const Input = React.forwardRef((props, ref) => {
const {
className,
addonAfter,
addonBefore,
prefix,
suffix: suffixProp,
invalid: invalidProp,
loading: loadingProp,
width = 0,
id: idProp,
disabled: disabledProp,
"aria-describedby": ariaDescribedByProp,
"aria-labelledby": ariaLabelledByProp,
...restProps
} = props;
const fieldContext = FieldContext.useFieldContext();
const invalid = invalidProp != null ? invalidProp : fieldContext.invalid;
const loading = loadingProp != null ? loadingProp : fieldContext.loading;
const id = idProp != null ? idProp : fieldContext.id;
const disabled = disabledProp != null ? disabledProp : fieldContext.disabled;
const ariaDescribedBy = ariaDescribedByProp != null ? ariaDescribedByProp : fieldContext["aria-describedby"];
const ariaLabelledBy = ariaLabelledByProp != null ? ariaLabelledByProp : fieldContext["aria-labelledby"];
const [prefixRef, prefixRect] = reactUse.useMeasure();
const [suffixRef, suffixRect] = reactUse.useMeasure();
const isInAutoSizeInput = React.useContext(AutoSizeInputContext.AutoSizeInputContext);
const accessoriesWidth = (prefixRect.width || 0) + (suffixRect.width || 0);
const autoSizeWidth = isInAutoSizeInput && width ? width + accessoriesWidth / 8 : void 0;
const theme = ThemeContext.useTheme2();
const styles = getInputStyles({ theme, invalid: !!invalid, width: autoSizeWidth ? void 0 : width });
const suffix = suffixProp || loading && /* @__PURE__ */ jsxRuntime.jsx(Spinner.Spinner, { inline: true });
return /* @__PURE__ */ jsxRuntime.jsxs(
"div",
{
className: css.cx(styles.wrapper, className),
style: autoSizeWidth ? { width: theme.spacing(autoSizeWidth) } : void 0,
"data-testid": "input-wrapper",
children: [
!!addonBefore && /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles.addon, children: addonBefore }),
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles.inputWrapper, children: [
prefix && /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles.prefix, ref: prefixRef, children: prefix }),
/* @__PURE__ */ jsxRuntime.jsx(
"input",
{
ref,
className: styles.input,
"aria-invalid": !!invalid,
id,
disabled,
"aria-describedby": ariaDescribedBy,
"aria-labelledby": ariaLabelledBy,
...restProps,
onWheel: restProps.type === "number" ? (e) => {
var _a;
e.currentTarget.blur();
(_a = restProps.onWheel) == null ? void 0 : _a.call(restProps, e);
} : restProps.onWheel,
style: {
paddingLeft: prefix ? prefixRect.width + 12 : void 0,
paddingRight: suffix || loading ? suffixRect.width + 12 : void 0
}
}
),
suffix && /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles.suffix, ref: suffixRef, children: suffix })
] }),
!!addonAfter && /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles.addon, children: addonAfter })
]
}
);
});
Input.displayName = "Input";
const getInputStyles = stylesFactory.stylesFactory(({ theme, invalid = false, width }) => {
const prefixSuffixStaticWidth = "28px";
const prefixSuffix = css.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: css.cx(
css.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.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: css.cx(
commonStyles.getFocusStyle(theme),
commonStyles.sharedInputStyle(theme, invalid),
css.css({
label: "input-input",
position: "relative",
zIndex: 0,
flexGrow: 1,
borderRadius: theme.shape.radius.default,
height: "100%",
width: "100%"
})
),
inputDisabled: css.css({
backgroundColor: theme.colors.action.disabledBackground,
color: theme.colors.action.disabledText,
border: `1px solid ${theme.colors.action.disabledBackground}`,
"&:focus": {
boxShadow: "none"
}
}),
addon: css.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: css.cx(
prefixSuffix,
css.css({
label: "input-prefix",
paddingLeft: theme.spacing(1),
paddingRight: theme.spacing(0.5),
borderRight: "none",
borderTopRightRadius: "unset",
borderBottomRightRadius: "unset"
})
),
suffix: css.cx(
prefixSuffix,
css.css({
label: "input-suffix",
paddingLeft: theme.spacing(1),
paddingRight: theme.spacing(1),
borderLeft: "none",
borderTopLeftRadius: "unset",
borderBottomLeftRadius: "unset",
right: 0
})
),
loadingIndicator: css.css({
"& + *": {
marginLeft: theme.spacing(0.5)
}
})
};
});
exports.Input = Input;
exports.getInputStyles = getInputStyles;
//# sourceMappingURL=Input.cjs.map