UNPKG

@grafana/ui

Version:
1 lines 15.5 kB
{"version":3,"file":"Input.mjs","sources":["../../../../src/components/Input/Input.tsx"],"sourcesContent":["import { css, cx } from '@emotion/css';\nimport { forwardRef, HTMLProps, ReactNode, useContext } from 'react';\nimport { useMeasure } from 'react-use';\n\nimport { GrafanaTheme2 } from '@grafana/data';\n\nimport { useTheme2 } from '../../themes/ThemeContext';\nimport { stylesFactory } from '../../themes/stylesFactory';\nimport { getFocusStyle, sharedInputStyle } from '../Forms/commonStyles';\nimport { Spinner } from '../Spinner/Spinner';\n\nimport { AutoSizeInputContext } from './AutoSizeInputContext';\n\nexport interface Props extends Omit<HTMLProps<HTMLInputElement>, 'prefix' | 'size'> {\n /** Sets the width to a multiple of 8px. Should only be used with inline forms. Setting width of the container is preferred in other cases.*/\n width?: number;\n /** Show an invalid state around the input */\n invalid?: boolean;\n /** Show an icon as a prefix in the input */\n prefix?: ReactNode;\n /** Show an icon as a suffix in the input */\n suffix?: ReactNode;\n /** Show a loading indicator as a suffix in the input */\n loading?: boolean;\n /** Add a component as an addon before the input */\n addonBefore?: ReactNode;\n /** Add a component as an addon after the input */\n addonAfter?: ReactNode;\n}\n\ninterface StyleDeps {\n theme: GrafanaTheme2;\n invalid?: boolean;\n width?: number;\n}\n\nexport const Input = forwardRef<HTMLInputElement, Props>((props, ref) => {\n const {\n className,\n addonAfter,\n addonBefore,\n prefix,\n suffix: suffixProp,\n invalid,\n loading,\n width = 0,\n ...restProps\n } = props;\n /**\n * Prefix & suffix are positioned absolutely within inputWrapper. We use client rects below to apply correct padding to the input\n * when prefix/suffix is larger than default (28px = 16px(icon) + 12px(left/right paddings)).\n * Thanks to that prefix/suffix do not overflow the input element itself.\n */\n const [prefixRef, prefixRect] = useMeasure<HTMLDivElement>();\n const [suffixRef, suffixRect] = useMeasure<HTMLDivElement>();\n\n // Yes, this is gross - When Input is being wrapped by AutoSizeInput, add the suffix/prefix width to the overall width\n // so the text content is not clipped. The intention is to make all the input's text appear without overflow/clipping,\n // which isn't normally how width is used in this component.\n // This behaviour is not controlled via a prop so we can limit API surface, and remove this as a 'breaking change' later\n // if a better solution is found.\n const isInAutoSizeInput = useContext(AutoSizeInputContext);\n const accessoriesWidth = (prefixRect.width || 0) + (suffixRect.width || 0);\n const autoSizeWidth = isInAutoSizeInput && width ? width + accessoriesWidth / 8 : undefined;\n\n const theme = useTheme2();\n\n // Don't pass the width prop, as this causes an unnecessary amount of Emotion calls when auto sizing\n const styles = getInputStyles({ theme, invalid: !!invalid, width: autoSizeWidth ? undefined : width });\n\n const suffix = suffixProp || (loading && <Spinner inline={true} />);\n\n return (\n <div\n className={cx(styles.wrapper, className)}\n // If the component is in an AutoSizeInput, set the width here to prevent emotion doing stuff\n // on every keypress\n style={autoSizeWidth ? { width: theme.spacing(autoSizeWidth) } : undefined}\n data-testid=\"input-wrapper\"\n >\n {!!addonBefore && <div className={styles.addon}>{addonBefore}</div>}\n <div className={styles.inputWrapper}>\n {prefix && (\n <div className={styles.prefix} ref={prefixRef}>\n {prefix}\n </div>\n )}\n\n <input\n ref={ref}\n className={styles.input}\n {...restProps}\n style={{\n paddingLeft: prefix ? prefixRect.width + 12 : undefined,\n paddingRight: suffix || loading ? suffixRect.width + 12 : undefined,\n }}\n />\n\n {suffix && (\n <div className={styles.suffix} ref={suffixRef}>\n {suffix}\n </div>\n )}\n </div>\n {!!addonAfter && <div className={styles.addon}>{addonAfter}</div>}\n </div>\n );\n});\n\nInput.displayName = 'Input';\n\nexport const getInputStyles = stylesFactory(({ theme, invalid = false, width }: StyleDeps) => {\n const prefixSuffixStaticWidth = '28px';\n const prefixSuffix = css({\n position: 'absolute',\n top: 0,\n zIndex: 1,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n flexGrow: 0,\n flexShrink: 0,\n fontSize: theme.typography.size.md,\n height: '100%',\n /* Min width specified for prefix/suffix classes used outside React component*/\n minWidth: prefixSuffixStaticWidth,\n color: theme.colors.text.secondary,\n });\n\n return {\n // Wraps inputWrapper and addons\n wrapper: cx(\n css({\n label: 'input-wrapper',\n display: 'flex',\n width: width ? theme.spacing(width) : '100%',\n height: theme.spacing(theme.components.height.md),\n borderRadius: theme.shape.radius.default,\n '&:hover': {\n '> .prefix, .suffix, .input': {\n borderColor: invalid ? theme.colors.error.border : theme.colors.primary.border,\n },\n\n // only show number buttons on hover\n \"input[type='number']\": {\n appearance: 'textfield',\n },\n\n \"input[type='number']::-webkit-inner-spin-button, input[type='number']::-webkit-outer-spin-button\": {\n // Need type assertion here due to the use of !important\n // see https://github.com/frenic/csstype/issues/114#issuecomment-697201978\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n WebkitAppearance: 'inner-spin-button !important' as 'inner-spin-button',\n opacity: 1,\n },\n },\n })\n ),\n // Wraps input and prefix/suffix\n inputWrapper: css({\n label: 'input-inputWrapper',\n position: 'relative',\n flexGrow: 1,\n /* we want input to be above addons, especially for focused state */\n zIndex: 1,\n\n /* when input rendered with addon before only*/\n '&:not(:first-child):last-child': {\n '> input': {\n borderLeft: 'none',\n borderTopLeftRadius: 'unset',\n borderBottomLeftRadius: 'unset',\n },\n },\n\n /* when input rendered with addon after only*/\n '&:first-child:not(:last-child)': {\n '> input': {\n borderRight: 'none',\n borderTopRightRadius: 'unset',\n borderBottomRightRadius: 'unset',\n },\n },\n\n /* when rendered with addon before and after */\n '&:not(:first-child):not(:last-child)': {\n '> input': {\n borderRight: 'none',\n borderTopRightRadius: 'unset',\n borderBottomRightRadius: 'unset',\n borderTopLeftRadius: 'unset',\n borderBottomLeftRadius: 'unset',\n },\n },\n\n input: {\n /* paddings specified for classes used outside React component */\n '&:not(:first-child)': {\n paddingLeft: prefixSuffixStaticWidth,\n },\n '&:not(:last-child)': {\n paddingRight: prefixSuffixStaticWidth,\n },\n '&[readonly]': {\n cursor: 'default',\n },\n },\n }),\n\n input: cx(\n getFocusStyle(theme),\n sharedInputStyle(theme, invalid),\n css({\n label: 'input-input',\n position: 'relative',\n zIndex: 0,\n flexGrow: 1,\n borderRadius: theme.shape.radius.default,\n height: '100%',\n width: '100%',\n })\n ),\n inputDisabled: css({\n backgroundColor: theme.colors.action.disabledBackground,\n color: theme.colors.action.disabledText,\n border: `1px solid ${theme.colors.action.disabledBackground}`,\n '&:focus': {\n boxShadow: 'none',\n },\n }),\n addon: css({\n label: 'input-addon',\n display: 'flex',\n justifyContent: 'center',\n alignItems: 'center',\n flexGrow: 0,\n flexShrink: 0,\n position: 'relative',\n\n '&:first-child': {\n borderTopRightRadius: 'unset',\n borderBottomRightRadius: 'unset',\n '> :last-child': {\n borderTopRightRadius: 'unset',\n borderBottomRightRadius: 'unset',\n },\n },\n\n '&:last-child': {\n borderTopLeftRadius: 'unset',\n borderBottomLeftRadius: 'unset',\n '> :first-child': {\n borderTopLeftRadius: 'unset',\n borderBottomLeftRadius: 'unset',\n },\n },\n '> *:focus': {\n /* we want anything that has focus and is an addon to be above input */\n zIndex: 2,\n },\n }),\n prefix: cx(\n prefixSuffix,\n css({\n label: 'input-prefix',\n paddingLeft: theme.spacing(1),\n paddingRight: theme.spacing(0.5),\n borderRight: 'none',\n borderTopRightRadius: 'unset',\n borderBottomRightRadius: 'unset',\n })\n ),\n suffix: cx(\n prefixSuffix,\n css({\n label: 'input-suffix',\n paddingLeft: theme.spacing(1),\n paddingRight: theme.spacing(1),\n borderLeft: 'none',\n borderTopLeftRadius: 'unset',\n borderBottomLeftRadius: 'unset',\n right: 0,\n })\n ),\n loadingIndicator: css({\n '& + *': {\n marginLeft: theme.spacing(0.5),\n },\n }),\n };\n});\n"],"names":[],"mappings":"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"}