如何在切换编辑模式时保持MUI Autocomplete的样式一致?
问题:编辑模式下Autocomplete组件内TextField样式异常
进入editMode(编辑模式)时,Autocomplete组件内的TextField样式会发生变化,选择下拉选项时也会出现同样问题。尝试修改Autocomplete内的TextField以及getOptionsLabel,但效果更差。期望进入编辑模式后,TextField的样式与非编辑模式完全一致,但目前无法找到样式差异的原因——两者信息相同,但布局位置不同。
- 非编辑模式目标样式:左侧缩略图+右侧上下排列的文本(上方为
libraryRef,下方为composition、manufactureDetailName和weight的组合文本),整体居中对齐 - 编辑模式异常样式:Autocomplete内的TextField文本布局混乱,位置偏移,与非编辑模式排版不一致
相关代码
// Format the value for the Autocomplete component const formatValue = (option: Material): ReactElement => ( <Stack px={3} py={2} width={1}> <Typography variant="bodySmall.default"> {option.libraryRef} </Typography> <Typography variant="captions.default" color={alpha(palette.primary.main, 0.7)}> {option.composition} {'manufactureDetail' in option && option.manufactureDetail?.manufactureDetailName ? ` • ${option.manufactureDetail.manufactureDetailName}` : ''} {' | '} {option.weight} </Typography> </Stack> ); const handleChange = ( event: React.SyntheticEvent, newValue: Material | { id: string; libraryRef: string; composition: string; weight: string } | null, ): void => { if (newValue && 'materialSubcategory' in newValue) { setSelectedMaterial(newValue); } }; return ( <Stack direction="row" height={1} flexGrow={1} alignItems="center" boxSizing="border-box" > {isEditMode ? ( <Autocomplete groupBy={(option) => { if ('materialSubcategory' in option && option.materialSubcategory) { return option.materialSubcategory.materialSubcategoryName; } return ''; }} value={selectedMaterial ?? value} options={[{ id: 'on-the-fly', libraryRef: 'Material On The Fly', composition: '', weight: '' }, ...materials]} getOptionLabel={(option) => `${option.libraryRef} | ${option.composition}${'manufactureDetail' in option && option.manufactureDetail?.manufactureDetailName ? ` • ${option.manufactureDetail.manufactureDetailName}` : ''} | ${option.weight}`} renderInput={(params: AutocompleteRenderInputParams): ReactElement => ( // This is what we see when the dropdown is closed <Stack direction="row"> <TextField {...params} placeholder={!value ? '' : 'Select Material'} inputProps={{ ...params.inputProps, readOnly: false, }} InputProps={{ ...params.InputProps, disableUnderline: true, startAdornment: ( <ImagePlaceholder sx={{ width: THUMBNAIL_SIZE, minWidth: THUMBNAIL_SIZE, height: THUMBNAIL_SIZE, marginRight: 3, }} /> ), }} sx={{ [`& .${autocompleteClasses.inputRoot}`]: { ['& input']: { ...theme.typography['bodySmall.default'], }, }, [`& .${autocompleteClasses.popupIndicator}`]: { right: 12, display: 'flex', alignItems: 'center', width: '16px !important', height: '16px !important', ['&:hover']: { backgroundColor: 'transparent', }, }, }} /> </Stack> )} renderOption={(props, option) => ( <> {/*this is the materials grouped by MSC name*/} <MaterialOption option={option} props={props} /> {/*this is the material on the fly option with the plus icon*/} {'id' in option && option.id === 'on-the-fly' && ( <MaterialPickerGroup option={option} props={props} setShowMaterialOnTheFlyDialog={setShowMaterialOnTheFlyDialog} /> )} </> )} ) : ( <Stack direction="row"> <ImagePlaceholder sx={{ width: THUMBNAIL_SIZE, minWidth: THUMBNAIL_SIZE, height: THUMBNAIL_SIZE }} /> {formatValue(value)} </Stack> )}
解决方案
1. 复用非编辑模式的文本渲染逻辑
核心问题是编辑模式用getOptionLabel生成的纯文本,无法还原非编辑模式formatValue的多层文本结构和样式。修改renderInput,直接复用formatValue来渲染输入框内容:
renderInput={(params: AutocompleteRenderInputParams): ReactElement => { const currentValue = params.value as Material; return ( <Stack direction="row" height={1} alignItems="center" flexGrow={1}> <ImagePlaceholder sx={{ width: THUMBNAIL_SIZE, minWidth: THUMBNAIL_SIZE, height: THUMBNAIL_SIZE, marginRight: 3, }} /> {/* 复用formatValue渲染内容,无值时显示占位符 */} {currentValue ? formatValue(currentValue) : ( <Typography variant="bodySmall.default" color="text.secondary"> Select Material </Typography> )} {/* 保留下拉箭头 */} {params.InputProps.endAdornment} </Stack> ); }}
2. 隐藏原生输入框干扰
由于已经自定义了显示内容,不需要TextField的原生输入框,可将其隐藏:
renderInput={(params) => ( <TextField {...params} inputProps={{ ...params.inputProps, style: { display: 'none' }, // 隐藏原生输入框 }} InputProps={{ ...params.InputProps, disableUnderline: true, startAdornment: null, // 移除默认startAdornment,自定义外层Stack处理 children: null, }} sx={{ width: '100%', [`& .${autocompleteClasses.popupIndicator}`]: { right: 12, display: 'flex', alignItems: 'center', width: '16px !important', height: '16px !important', '&:hover': { backgroundColor: 'transparent', }, }, }} /> )}
3. 统一外层容器样式
确保编辑模式下的Autocomplete外层容器和非编辑模式保持完全一致的布局属性:
{isEditMode ? ( <Stack direction="row" height={1} flexGrow={1} alignItems="center" boxSizing="border-box"> <Autocomplete // ...其他属性 /> </Stack> ) : ( // 非编辑模式原有代码 )}
4. 对齐value类型
确保selectedMaterial和value的类型一致,避免渲染时的异常:
value={selectedMaterial ?? (value as Material)}
关键原因总结
- 非编辑模式用自定义
Stack+Typography组合实现排版,编辑模式依赖TextField原生输入框的文本渲染,两者样式规则(行高、间距、对齐)完全不同 getOptionLabel生成的纯文本无法还原formatValue的多层文本结构,导致布局偏移
内容的提问来源于stack exchange,提问作者lucia garcia
相关产品推荐
相关产品推荐

