Javascript 如何使用MaterialUI创建代码编辑器,如自动完成下拉列表?
我有一个非常具体的用例,我正在考虑如何在我正在开发的应用程序中实现。 该组件是类似于编辑器的文本区域,应该填充材质UI芯片组件(类似于自动完成文本框中的标记),这些组件生成某种表达式。当用户开始在此文本区域内键入时,将弹出一个自动完成下拉列表,向用户显示可能的选项 我希望将此下拉列表放置在IDE中的此文本区域内(类似于intellisense)。 我正试图通过使用Autocomplete和某种自定义Popper组件的组合来实现这个组件。 代码如下所示(仍处于某种起草阶段):Javascript 如何使用MaterialUI创建代码编辑器,如自动完成下拉列表?,javascript,reactjs,material-ui,Javascript,Reactjs,Material Ui,我有一个非常具体的用例,我正在考虑如何在我正在开发的应用程序中实现。 该组件是类似于编辑器的文本区域,应该填充材质UI芯片组件(类似于自动完成文本框中的标记),这些组件生成某种表达式。当用户开始在此文本区域内键入时,将弹出一个自动完成下拉列表,向用户显示可能的选项 我希望将此下拉列表放置在IDE中的此文本区域内(类似于intellisense)。 我正试图通过使用Autocomplete和某种自定义Popper组件的组合来实现这个组件。 代码如下所示(仍处于某种起草阶段): 从'@materia
从'@material ui/core/styles'导入{createStyles,makeStyles,Theme};
从“@material ui/core/TextField”导入TextField;
从“@material ui/lab/Autocomplete”导入自动完成;
从“@material ui/core/Chip”导入芯片;
从“@material ui/core”导入{Popper}”;
常量目标选项=[
{label:(”,类型:“operator”},
{label:”,键入:“operator”},
{label:“OR”,键入:“operator”},
{label:“AND”,键入:“operator”},
{标签:“测试选项1”,类型:“选项”},
{标签:“测试选项2”,类型:“选项”},
];
const useStyles=makeStyles((主题:主题)=>
创建样式({
根目录:{
“&.muiautomlete inputRoot”:{
对齐项目:“开始”
}
},
}),
);
导出默认值()=>{
const classes=useStyles();
const[value,setValue]=React.useState([]);
const CustomPopper=函数(道具){
返回;
};
返回(
选项(标签)}
自由女神
不可清除
PopperComponent={CustomPopper}
renderTags={(值:string[],getTagProps)=>
value.map((选项:字符串,索引:编号)=>(
))
}
renderInput={(参数)=>(
)}
/>
);
};
const useStyles=makeStyles((主题:主题)=>
创建样式({
根目录:{
“&.muiautomlete inputRoot”:{
对齐项目:“开始”
}
},
}),
);
导出默认值()=>{
const classes=useStyles();
const CustomPopper=函数(道具){
返回;
};
返回(
选项(标签)}
自由女神
不可清除
PopperComponent={CustomPopper}
renderTags={(值:string[],getTagProps)=>
value.map((选项:字符串,索引:编号)=>(
))
}
renderInput={(参数)=>(
)}
/>
);
};
谢谢。警告:这将深入到意见的领域。。。我最终选择了降档进行定制
npm安装降档
这段代码有点脏(出自我的开发分支),但它有一个定制的下拉列表,您可以编辑它
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)
谢谢你的麻烦,我想我也会放弃自动完成并手动创建组件。基本上,这是一个非传统的商业需求,不是我自己发明的东西,我只是想澄清我对此的疑虑,看看过去是否有人有类似的问题。我还需要支持Textfield内芯片的布局调整,我认为Material UI组件不可能做到这一点。
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)
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) }
/>
)
"@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",