This commit is contained in:
seb
2026-10-04 11:25:10 +02:00
parent dd8566eabc
commit 874d6c010f
16 changed files with 345 additions and 346 deletions

View File

@@ -0,0 +1,139 @@
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 effectiveReadOnly = isSelect
? (propReadOnly ?? slotProps?.htmlInput?.readOnly ?? inputProps?.readOnly)
: (propReadOnly ?? slotProps?.htmlInput?.readOnly ?? inputProps?.readOnly ?? this.state.readOnly);
const htmlInput = isSelect
? {
readOnly: effectiveReadOnly,
...slotProps?.htmlInput,
...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,
...inputProps,
};
const resolvedInputProps = (InputProps || slotProps?.input) ? {
...slotProps?.input,
...InputProps,
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 { input: _unusedInput, ...restSlotProps } = (slotProps || {});
const mergedSlotProps = {
...restSlotProps,
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}
InputProps={resolvedInputProps}
slotProps={mergedSlotProps}
sx={resolveSx(sx)}
{...rest}
/>
);
}
}
export { TextInput };