javascript - 当焦点更改为 React Select 时,React Slate 中的文本选择不再标记

标签 javascript reactjs slate

我不知道这是一个常见问题还是我们的错误,但也许有人有一个想法: 我们正在使用 React 和 slate 构建一个 HTML 编辑器。 用户可以选择一个文本框,然后更改属性。这对于简单的按钮来说效果很好。但是,当我打开下拉菜单( react 选择)以更改字体大小等时,所选文本不再被标记。 Slate 会保留选择,以便更改生效,但这样的用户体验很糟糕。

恕我直言,这应该是一个石板功能,以保持文本标记,但也许这是我需要自己应用的东西。

一些片段,不知道是否有帮助:

编辑器组件初始化字体样式插件并负责序列化。

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}

我必须使用超时来超出 react 生命周期,并且我必须添加一个额外的标志,因为失去对选择组件的关注也会将其关闭。 就像我说的那样,它有缺点: - 焦点切换期间所选文本有一点闪烁 - select 中的下拉框颜色错误(明显没有聚焦) - 切换到另一个下拉菜单(例如对齐)不会关闭另一个下拉菜单,因为它已经没有焦点:

附加信息: 我们必须在 0.47 版本中使用 slate 和 slate-react ,因为我们需要的 slate-html-serializer 不支持更高版本。也许这个问题已经在更高版本中解决了?

所以,我有一个有点工作的版本,但我更喜欢一个解决方案,其中 slate 可以“本地”处理选择,而无需我处理焦点。我认为如果没有选定的文本闪烁和不正常的颜色,应该是可能的。

最佳答案

当您因打开下拉菜单而将焦点移出编辑器时,Slate 不会保留选择。现在有了按钮,情况有所不同,因为它重新应用选择

由于您现在必须手动应用并获取选择,因此一种方法是在您尝试从选择中打开菜单时将编辑器选择存储在状态中。当菜单打开时,使用 Transforms.setSelection 重新应用选择并获取可以再次存储在状态中的字体大小,以在下拉列表中显示焦点值

现在,应用更改后,您需要再次应用选择

您可以遵循 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关于重点和选择

关于javascript - 当焦点更改为 React Select 时,React Slate 中的文本选择不再标记,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61749733/

相关文章:

ReactJS:.map(数组)中的事件未触发

reactjs - Formik + Yup : How to immediately validate form before submit?

javascript - 在 Slate.js editor.apply(operation) 中,未正确应用 "split_node"操作

javascript - 如何使用 javascript 获取域名后面的属性?

javascript - 如何访问和处理嵌套对象,数组或JSON?

javascript - React json 获取值

css - 将 Bootstrap 4 添加到 Slate V1 - 在 Chrome 开发者工具中获取两个索引

python - 使用 slate 时出现 unicodeDecodeError

javascript - 如何在Vue中将多个文件转换为filebase64?

javascript - 你如何检测 CSS 动画何时以 JavaScript 开始和结束?