154 lines
4.6 KiB
JavaScript
154 lines
4.6 KiB
JavaScript
import { Component } from 'react';
|
|
import TextField from '@mui/material/TextField';
|
|
|
|
function normalizeSx(s) {
|
|
if (!s || typeof s !== 'object' || Array.isArray(s)) return s;
|
|
const bg = s.bgcolor ?? s.backgroundColor;
|
|
if (bg && bg !== 'transparent') {
|
|
const { bgcolor: _bg, backgroundColor: _bgc, ...rest } = s;
|
|
return {
|
|
...rest,
|
|
bgcolor: 'transparent',
|
|
'& .MuiInputBase-root': {
|
|
bgcolor: bg,
|
|
...(typeof rest['& .MuiInputBase-root'] === 'object' ? rest['& .MuiInputBase-root'] : {}),
|
|
},
|
|
};
|
|
}
|
|
return s;
|
|
}
|
|
|
|
function resolveSx(s) {
|
|
if (!s) return undefined;
|
|
if (Array.isArray(s)) return s.map(resolveSx);
|
|
return normalizeSx(s);
|
|
}
|
|
|
|
/**
|
|
* TextInput:
|
|
* Reusable Class Component wrapping MUI TextField with the harmonized
|
|
* style of EANs (small outlined, shrink label by default, transparent root)
|
|
* and complete suppression of browser autocomplete/autofill popups.
|
|
*/
|
|
export default class TextInput extends Component {
|
|
state = {
|
|
// Starting as readOnly prevents Chrome/Safari/Firefox from attaching autofill popup listeners
|
|
readOnly: true,
|
|
};
|
|
|
|
handleFocus = (e) => {
|
|
if (this.state.readOnly && !this.props.readOnly && !this.props.slotProps?.htmlInput?.readOnly) {
|
|
this.setState({ readOnly: false });
|
|
}
|
|
if (this.props.onFocus) {
|
|
this.props.onFocus(e);
|
|
}
|
|
};
|
|
|
|
handleMouseDown = (e) => {
|
|
if (this.state.readOnly && !this.props.readOnly && !this.props.slotProps?.htmlInput?.readOnly) {
|
|
this.setState({ readOnly: false });
|
|
}
|
|
if (this.props.onMouseDown) {
|
|
this.props.onMouseDown(e);
|
|
}
|
|
};
|
|
|
|
render() {
|
|
const {
|
|
slotProps,
|
|
inputProps,
|
|
InputProps,
|
|
InputLabelProps,
|
|
inputRef,
|
|
label,
|
|
size = 'small',
|
|
onFocus,
|
|
onMouseDown,
|
|
readOnly: propReadOnly,
|
|
sx,
|
|
...rest
|
|
} = this.props;
|
|
|
|
const isSelect = Boolean(rest.select);
|
|
const isAutocomplete = Boolean(inputProps?.['aria-autocomplete'] || rest['aria-autocomplete']);
|
|
const effectiveReadOnly = (isSelect || isAutocomplete)
|
|
? (propReadOnly ?? slotProps?.htmlInput?.readOnly ?? inputProps?.readOnly ?? false)
|
|
: (propReadOnly ?? slotProps?.htmlInput?.readOnly ?? inputProps?.readOnly ?? this.state.readOnly);
|
|
|
|
const { inputProps: nestedFromInputProps, ...cleanInputProps } = InputProps || {};
|
|
const { inputProps: nestedFromSlotInput, ...cleanSlotInput } = slotProps?.input || {};
|
|
|
|
const rawResolvedInputProps = (InputProps || slotProps?.input) ? {
|
|
...cleanSlotInput,
|
|
...cleanInputProps,
|
|
startAdornment: (slotProps?.input?.startAdornment && InputProps?.startAdornment)
|
|
? <>{slotProps.input.startAdornment}{InputProps.startAdornment}</>
|
|
: (InputProps?.startAdornment ?? slotProps?.input?.startAdornment),
|
|
endAdornment: (slotProps?.input?.endAdornment && InputProps?.endAdornment)
|
|
? <>{InputProps.endAdornment}{slotProps.input.endAdornment}</>
|
|
: (InputProps?.endAdornment ?? slotProps?.input?.endAdornment),
|
|
} : undefined;
|
|
|
|
const htmlInput = isSelect
|
|
? {
|
|
readOnly: effectiveReadOnly,
|
|
...slotProps?.htmlInput,
|
|
...nestedFromSlotInput,
|
|
...nestedFromInputProps,
|
|
...inputProps,
|
|
}
|
|
: {
|
|
autoComplete: 'one-time-code',
|
|
autoCorrect: 'off',
|
|
autoCapitalize: 'off',
|
|
spellCheck: false,
|
|
'aria-autocomplete': 'none',
|
|
'data-lpignore': 'true',
|
|
'data-1p-ignore': 'true',
|
|
'data-bwignore': 'true',
|
|
'data-form-type': 'other',
|
|
readOnly: effectiveReadOnly,
|
|
...slotProps?.htmlInput,
|
|
...nestedFromSlotInput,
|
|
...nestedFromInputProps,
|
|
...inputProps,
|
|
};
|
|
|
|
const resolvedInputProps = rawResolvedInputProps;
|
|
const { input: _unusedInput, ...restSlotProps } = (slotProps || {});
|
|
|
|
const hasLabelProps = Boolean(label || InputLabelProps || slotProps?.inputLabel);
|
|
const mergedSlotProps = {
|
|
...restSlotProps,
|
|
...(resolvedInputProps ? { input: resolvedInputProps } : {}),
|
|
...(hasLabelProps
|
|
? {
|
|
inputLabel: {
|
|
...(label ? { shrink: true } : {}),
|
|
...InputLabelProps,
|
|
...slotProps?.inputLabel,
|
|
},
|
|
}
|
|
: {}),
|
|
htmlInput,
|
|
};
|
|
|
|
return (
|
|
<TextField
|
|
size={size}
|
|
label={label}
|
|
inputRef={inputRef}
|
|
autoComplete="one-time-code"
|
|
onFocus={this.handleFocus}
|
|
onMouseDown={this.handleMouseDown}
|
|
slotProps={mergedSlotProps}
|
|
sx={resolveSx(sx)}
|
|
{...rest}
|
|
/>
|
|
);
|
|
}
|
|
}
|
|
|
|
export { TextInput };
|