[英]How to create code editor like Autocomplete dropdown with Material UI?
我有一个相当具体的用例,我正在考虑如何在我正在开发的应用程序中实现。 该组件是类似编辑器的文本区域,应该填充 Material UI Chip 组件(类似于自动完成文本框中的标签),这些组件会生成某种表达式。 当用户开始在此文本区域内输入时,应该会弹出一个自动完成下拉菜单,向用户显示可能的选项。
我希望将此下拉列表放置在 IDE 中的此文本区域(类似于智能感知)内。
我正在尝试通过结合使用 Autocomplete 和某种自定义 Popper 组件来实现这个组件。 代码看起来像这样(它仍处于某种草稿阶段):
import { createStyles, makeStyles, Theme } from '@material-ui/core/styles';
import TextField from "@material-ui/core/TextField";
import Autocomplete from "@material-ui/lab/Autocomplete";
import Chip from '@material-ui/core/Chip';
import { Popper } from "@material-ui/core";
const targetingOptions = [
{ label: "(", type: "operator" },
{ label: ")", type: "operator" },
{ label: "OR", type: "operator" },
{ label: "AND", type: "operator" },
{ label: "Test Option 1", type: "option" },
{ label: "Test Option 2", type: "option" },
];
const useStyles = makeStyles((theme: Theme) =>
createStyles({
root: {
'& .MuiAutocomplete-inputRoot': {
alignItems: 'start'
}
},
}),
);
export default () => {
const classes = useStyles();
const [value, setValue] = React.useState<string[] | null>([]);
const CustomPopper = function (props) {
return <Popper {...props} style={{ width: 250, position: 'relative' }} />;
};
return (
<div>
<Autocomplete
className={classes.root}
multiple
id="tags-filled"
options={targetingOptions.map((option) => option.label)}
freeSolo
disableClearable
PopperComponent={CustomPopper}
renderTags={(value: string[], getTagProps) =>
value.map((option: string, index: number) => (
<Chip variant="outlined" label={option} {...getTagProps({ index })} />
))
}
renderInput={(params) => (
<TextField {...params} variant="outlined" multiline={true} rows={20} />
)}
/>
</div>
);
};
const useStyles = makeStyles((theme: Theme) =>
createStyles({
root: {
'& .MuiAutocomplete-inputRoot': {
alignItems: 'start'
}
},
}),
);
export default () => {
const classes = useStyles();
const CustomPopper = function (props) {
return <Popper {...props} style={{ width: 250, position: 'relative' }} />;
};
return (
<div>
<Autocomplete
className={classes.root}
multiple
id="tags-filled"
options={targetingOptions.map((option) => option.label)}
freeSolo
disableClearable
PopperComponent={CustomPopper}
renderTags={(value: string[], getTagProps) =>
value.map((option: string, index: number) => (
<Chip variant="outlined" label={option} {...getTagProps({ index })} />
))
}
renderInput={(params) => (
<TextField {...params} variant="outlined" multiline={true} rows={20} />
)}
/>
</div>
);
};
谢谢。
警告:这将深入到 realm 的意见中......我最终选择了 Downshift 进行自定义npm install downshift
这段代码有点脏(在我的开发分支之外),但它有一个自定义的下拉列表,您可以编辑
import React from 'react'
import {render} from 'react-dom'
import Downshift from 'downshift'
import {
MenuItem,
Paper,
TextField,
} from '@material-ui/core'
import {
withStyles
} from '@material-ui/core/styles'
const items = [
'apple',
'pear',
'orange',
'grape',
'banana',
]
class DownshiftWrapper extends React.Component {
constructor(props) {
super(props)
this.state = {
value: props.value || '',
backup: props.value || '',
onChange: v => {console.log('changed', v)}
}
}
_renderMenuItem(args) {
const { key, index, itemProps, current, highlightedIndex, selectedItem, ...rest } = args
const isSelected = key == current
return (
<MenuItem
{...rest}
key = { key }
selected = { isSelected }
component='div'
style={{
fontWeight: isSelected ? 500 : 400,
padding: '2px 16px 2px 16px',
borderBottom: '1px solid rgba(128,128,128,0.5)',
}}
>
{ key }
</MenuItem>
)
}
render() {
const { classes, style } = this.props
const _override = (incoming) => {
console.log('override:', incoming)
this.setState({
...this.state,
value: incoming
})
if(this.props.onChange) {
this.props.onChange(incoming)
} else {
console.log(`Downshift::onChange the onchange handler is missing. New value:${incoming}`)
}
}
return (
<Downshift
ref = { x => this.downshift = x}
onSelect = { (selected) => {
if(selected) {
console.log('::onSelect', selected)
_override(selected)
}
} }
onInputValueChange= { (inputValue, stateAndHelpers) => {
console.log('::onInputValueChange', {
...stateAndHelpers,
_val: inputValue,
})
} }
// onStateChange={( state ) => {
// //return input.onChange(inputValue);
// let value = state.inputValue
// this.state.onChange(state.inputValue)
// console.log('old:state', state)
// console.log('value:', value)
// _override( state.inputValue )
// }}
onChange={ selection => { console.log(selection) }}
itemToString={ item => {
return item || ''
} }
//selectedItem={this.props.input.value}
>
{({
getInputProps,
getItemProps,
getLabelProps,
getMenuProps,
isOpen,
inputValue,
highlightedIndex,
selectedItem,
}) => {
const inputProps = getInputProps()
let value = inputProps.value
//FIXME add filtering options
let filtered = this.props.items || items//.filter(item => !inputValue || item.includes(inputValue))
return (
<div className={classes.container}>
<TextField
{ ...inputProps }
style={
style
}
label={this.props.label}
placeholder={this.props.placeholder}
value = {
this.state.value
}
onFocus = { e => {
this.downshift.openMenu()
e.target.select()
}}
onBlur={ e => {
console.log(inputValue)
e.preventDefault()
this.downshift.closeMenu()
} }
onChange={ e => {
inputProps.onChange(e)//pass to the logic
_override(e.target.value)
}}
onKeyDown= { (e) => {
const key = e.which || e.keyCode
if(key == 27){
e.preventDefault()
e.target.blur()
//reset to default
_override(this.state.backup || '')
} else if (key == 13){
e.preventDefault()
e.target.blur()
_override(e.target.value)
}
}}
/>
{isOpen
? (
<Paper
className={classes.paper}
// style={{
// backgroundColor: 'white',
// }}
square>
{ filtered
.map( (item, index) => {
const _props = {
...getItemProps({ item: item }),
index: index,
key: item,
item: item,
current: this.state.value,
}
return this._renderMenuItem(_props)
} )
}
</Paper>
)
: null}
{/* <div style={{color: 'red'}}>{this.state.value || 'null'}</div> */}
</div>
)
} }
</Downshift>
)
}
}
class Integrated extends React.Component {
}
//Material UI Examples -> https://material-ui.com/demos/autocomplete/
const styles = theme => ({
root: {
flexGrow: 1,
height: 250,
},
container: {
flexGrow: 1,
position: 'relative',
},
paper: {
position: 'absolute',
zIndex: 1,
marginTop: theme.spacing.unit,
left: 0,
right: 0,
},
chip: {
margin: `${theme.spacing.unit / 2}px ${theme.spacing.unit / 4}px`,
},
inputRoot: {
flexWrap: 'wrap',
},
})
export default withStyles(styles)(DownshiftWrapper)
在使用中(EditableSelect 是我项目中的导出名称):
return (
<EditableSelect
//onFocus={e => this.onFocus(e) }
//multiLine={true}
//onKeyDown={ e=> this.keyHandler(e) }
items={ options }
value={ cue.spots[index][field] }
hintText={T.get('spot' + field + 'Hint')}
placeholder={ T.get('spot' + field + 'Hint') }
ref={x => this[id] = x }
style={{width: '90%' }}
onChange={ val => this.updateSpotExplicit(val, index, field) }
/>
)
我不确定您在追求什么,但是当我尝试自定义Autocomplete
功能时,我发现它有问题。 这段代码需要进行大量清理工作,但我可以验证它是否在我们的生产环境中工作。 这是我在 18 个月前发现的最佳解决方案,我们仍在使用它。
"@material-ui/core": "^4.11.2",
"@material-ui/icons": "^4.11.2",
"@material-ui/lab": "^4.0.0-alpha.57",
"@material-ui/styles": "^4.11.2",
"downshift": "^2.0.10",
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.