当焦点更改为 React Select 时,React Slate 中的文本选择不再标记
Text selection in React Slate no longer marked when focus changes to React Select
我不知道这是一个常见问题还是我们的错误,但也许有人有想法:
我们正在构建一个带有 React 和 Slate 的 HTML 编辑器。
用户可以 select 文本框然后更改属性。这适用于简单的按钮。但是,当我打开下拉菜单 (react-select) 以更改字体大小时,selected 文本不再被标记。 Slate 保留 selection,因此更改会生效,但这样的用户体验很糟糕。
恕我直言,这应该是一个保持文本标记的石板功能,但也许这是我需要自己应用的东西。
一些片段,不知道有没有帮助:
编辑器组件初始化字体样式插件并负责序列化。
class Editor extends React.Component {
constructor(props) {
super(props);
this.config = {
...mergePluginConfig(PLUGIN_CONFIG, props),
getEditor: () => this.editor,
getValue: () => this.state.value,
};
this.plugins = initializePlugins(this.config);
this.htmlSerializer = new HtmlSerializer({
rules: getSerializationRulesFromPlugins(this.plugins),
});
this.schema = getSchemaFromPlugins(this.plugins);
this.state = {
value: this.htmlSerializer.deserialize(props.value)
};
ref = editor => {
this.editor = editor;
};
render() {
return (
<div>
<Toolbar>
<div className="control">
{renderToolbarElementWithPlugins(this.plugins, 'font-size')}
</div>
<!--- more tools --->
<SlateEditor
autoFocus={true}
spellCheck={true}
placeholder={this.props.placeholder}
ref={this.ref}
value={this.state.value}
onChange={this.onChange}
onKeyDown={this.onKeyDown}
plugins={this.plugins}
schema={this.schema}
/>
onChange = ({ value }) => {
const {startInline, endInline, document, selection, fragment} = value;
// holds the correct information
console.log(fragment.text);
// ...
this.setState({ value });
this.props.onChange(this.htmlSerializer.serialize(value));
};
这是与其他插件一起初始化的字体大小插件,将显示在工具栏中:
export default function initializeFontSizePlugin(options) {
// this takes care of detecting the current value and applying selected change to the value.
// it does not change selection
const plugin = createStyleBasedMarkPlugin(...);
const fontSizeOptions = options.fontSizeOptions || [];
const handleFontSizeChange = ({value}) => {
plugin.reapplyFontSize({value: rendererFontSize(value)});
};
return {
...plugin,
renderToolbarElement() {
const {isMixed, fontSize} = plugin.detectFontSize();
return <Select
isCreatable={true}
name='font-size'
value={isMixed ? undefined : displayFontSize(fontSize)}
onChange={handleFontSizeChange}
options={fontSizeOptions}
/>;
}
};
}
我目前的解决方案是在 select 打开后立即聚焦 slate,然后告诉 select 打开,但这感觉很老套并且有缺点(见下文)
const handleFontSizeChange = ({value}) => {
plugin.reapplyFontSize({value: rendererFontSize(value)});
handleMenuClose();
};
let menuIsOpen = false;
let firstOpen = false;
const handleMenuOpen = (editor) => {
firstOpen = true;
if(!menuIsOpen) {
setTimeout(() => {
if (editor) {
editor.focus();
}
menuIsOpen = true;
}, 1);
}
}
const handleMenuClose = (editor) => {
if(!firstOpen) {
setTimeout(() => {
if(menuIsOpen) {
menuIsOpen = false;
if (editor) {
editor.focus();
}
}
}, 1);
} else {
firstOpen = false;
}
}
<Select
onMenuOpen={handleMenuOpen.bind(this)}
onMenuClose={handleMenuClose.bind(this)}
menuIsOpen={menuIsOpen}
我必须使用超时来跳出反应生命周期,我必须添加一个额外的标志,因为失去对 select 组件的关注也会将其关闭。
就像我说的那样有缺点:
- 在焦点切换期间 selected 文本上有一点闪烁
- select 中的下拉框颜色错误(明显没有聚焦)
- 切换到另一个下拉列表(如对齐)不会关闭另一个,因为它已经没有焦点:
附加信息:
我们必须在 0.47 版本中使用 slate 和 slate-react
,因为我们需要的 slate-html-serializer
不支持更高版本。也许这已经在更高版本中解决了?
所以,我有一个有点工作的版本,但我更喜欢一个解决方案,其中 slate 负责 selection "natively" 而我不必处理焦点。我认为没有 selected 文本 flickering
和关闭颜色应该是可能的。
当您由于打开下拉菜单而将焦点移出编辑器时,Slate 不会保留 selection。现在有了按钮,它就不同了,因为它重新应用了 selection
由于您现在必须手动应用和获取 selection,因此一种方法是在您尝试从 [=25= 打开菜单时将编辑器 selection 存储在状态中].当菜单打开时,使用 Transforms.setSelection
重新应用 selection 并获取可以再次存储在状态中的字体大小,以在下拉列表中显示聚焦值
现在应用更改后,您需要再次应用 selection
您可以遵循 this PR
中使用的概念
const [selection, setSelection] = useState();
const [menuIsOpen, setMenuIsOpen] = useState(false);
const [fontSize, setFontSize] = useState(plugins.detectFontSize());
const handleFontSizeChange = ({value}) => {
plugin.reapplyFontSize({value: rendererFontSize(value)});
handleMenuClose();
};
}
const handleMenuOpen = (editor) => {
setSelection(editor.selection);
setMenuIsOpen(true);
Transforms.setSelection() // pass in the required params here
setFontSize(plugins.detectFontSize());
}
const handleMenuClose = (editor) => {
setMenuIsOpen(false);
Transforms.setSelection() // pass in the required params here based on selection state
}
<Select
onMenuOpen={handleMenuOpen.bind(this)}
onMenuClose={handleMenuClose.bind(this)}
menuIsOpen={menuIsOpen}
value={fontSize}
options={options}
/>
另请参阅 this github issue 关于 focus 和 selection
我不知道这是一个常见问题还是我们的错误,但也许有人有想法: 我们正在构建一个带有 React 和 Slate 的 HTML 编辑器。 用户可以 select 文本框然后更改属性。这适用于简单的按钮。但是,当我打开下拉菜单 (react-select) 以更改字体大小时,selected 文本不再被标记。 Slate 保留 selection,因此更改会生效,但这样的用户体验很糟糕。
恕我直言,这应该是一个保持文本标记的石板功能,但也许这是我需要自己应用的东西。
一些片段,不知道有没有帮助:
编辑器组件初始化字体样式插件并负责序列化。
class Editor extends React.Component {
constructor(props) {
super(props);
this.config = {
...mergePluginConfig(PLUGIN_CONFIG, props),
getEditor: () => this.editor,
getValue: () => this.state.value,
};
this.plugins = initializePlugins(this.config);
this.htmlSerializer = new HtmlSerializer({
rules: getSerializationRulesFromPlugins(this.plugins),
});
this.schema = getSchemaFromPlugins(this.plugins);
this.state = {
value: this.htmlSerializer.deserialize(props.value)
};
ref = editor => {
this.editor = editor;
};
render() {
return (
<div>
<Toolbar>
<div className="control">
{renderToolbarElementWithPlugins(this.plugins, 'font-size')}
</div>
<!--- more tools --->
<SlateEditor
autoFocus={true}
spellCheck={true}
placeholder={this.props.placeholder}
ref={this.ref}
value={this.state.value}
onChange={this.onChange}
onKeyDown={this.onKeyDown}
plugins={this.plugins}
schema={this.schema}
/>
onChange = ({ value }) => {
const {startInline, endInline, document, selection, fragment} = value;
// holds the correct information
console.log(fragment.text);
// ...
this.setState({ value });
this.props.onChange(this.htmlSerializer.serialize(value));
};
这是与其他插件一起初始化的字体大小插件,将显示在工具栏中:
export default function initializeFontSizePlugin(options) {
// this takes care of detecting the current value and applying selected change to the value.
// it does not change selection
const plugin = createStyleBasedMarkPlugin(...);
const fontSizeOptions = options.fontSizeOptions || [];
const handleFontSizeChange = ({value}) => {
plugin.reapplyFontSize({value: rendererFontSize(value)});
};
return {
...plugin,
renderToolbarElement() {
const {isMixed, fontSize} = plugin.detectFontSize();
return <Select
isCreatable={true}
name='font-size'
value={isMixed ? undefined : displayFontSize(fontSize)}
onChange={handleFontSizeChange}
options={fontSizeOptions}
/>;
}
};
}
我目前的解决方案是在 select 打开后立即聚焦 slate,然后告诉 select 打开,但这感觉很老套并且有缺点(见下文)
const handleFontSizeChange = ({value}) => {
plugin.reapplyFontSize({value: rendererFontSize(value)});
handleMenuClose();
};
let menuIsOpen = false;
let firstOpen = false;
const handleMenuOpen = (editor) => {
firstOpen = true;
if(!menuIsOpen) {
setTimeout(() => {
if (editor) {
editor.focus();
}
menuIsOpen = true;
}, 1);
}
}
const handleMenuClose = (editor) => {
if(!firstOpen) {
setTimeout(() => {
if(menuIsOpen) {
menuIsOpen = false;
if (editor) {
editor.focus();
}
}
}, 1);
} else {
firstOpen = false;
}
}
<Select
onMenuOpen={handleMenuOpen.bind(this)}
onMenuClose={handleMenuClose.bind(this)}
menuIsOpen={menuIsOpen}
我必须使用超时来跳出反应生命周期,我必须添加一个额外的标志,因为失去对 select 组件的关注也会将其关闭。 就像我说的那样有缺点: - 在焦点切换期间 selected 文本上有一点闪烁 - select 中的下拉框颜色错误(明显没有聚焦) - 切换到另一个下拉列表(如对齐)不会关闭另一个,因为它已经没有焦点:
附加信息:
我们必须在 0.47 版本中使用 slate 和 slate-react
,因为我们需要的 slate-html-serializer
不支持更高版本。也许这已经在更高版本中解决了?
所以,我有一个有点工作的版本,但我更喜欢一个解决方案,其中 slate 负责 selection "natively" 而我不必处理焦点。我认为没有 selected 文本 flickering
和关闭颜色应该是可能的。
当您由于打开下拉菜单而将焦点移出编辑器时,Slate 不会保留 selection。现在有了按钮,它就不同了,因为它重新应用了 selection
由于您现在必须手动应用和获取 selection,因此一种方法是在您尝试从 [=25= 打开菜单时将编辑器 selection 存储在状态中].当菜单打开时,使用 Transforms.setSelection
重新应用 selection 并获取可以再次存储在状态中的字体大小,以在下拉列表中显示聚焦值
现在应用更改后,您需要再次应用 selection
您可以遵循 this PR
中使用的概念const [selection, setSelection] = useState();
const [menuIsOpen, setMenuIsOpen] = useState(false);
const [fontSize, setFontSize] = useState(plugins.detectFontSize());
const handleFontSizeChange = ({value}) => {
plugin.reapplyFontSize({value: rendererFontSize(value)});
handleMenuClose();
};
}
const handleMenuOpen = (editor) => {
setSelection(editor.selection);
setMenuIsOpen(true);
Transforms.setSelection() // pass in the required params here
setFontSize(plugins.detectFontSize());
}
const handleMenuClose = (editor) => {
setMenuIsOpen(false);
Transforms.setSelection() // pass in the required params here based on selection state
}
<Select
onMenuOpen={handleMenuOpen.bind(this)}
onMenuClose={handleMenuClose.bind(this)}
menuIsOpen={menuIsOpen}
value={fontSize}
options={options}
/>
另请参阅 this github issue 关于 focus 和 selection