0

所以基本上我已经添加了两个自定义功能来为 RichTextBlock 中的文本着色,我想让它们为一部分文本选择一个会自动取消选择另一个颜色按钮,就像 h 标签的情况一样。

我搜索了一下,但没有找到太多,所以我想我可以使用一些帮助,无论是建议、说明甚至是代码。

我的功能是这样的:

@hooks.register('register_rich_text_features')
def register_redtext_feature(features):
    feature_name = 'redtext'
    type_ = 'RED_TEXT'
    tag = 'span'

    control = {
        'type': type_,
        'label': 'Red',
        'style': {'color': '#bd003f'},
    }

    features.register_editor_plugin(
        'draftail', feature_name, draftail_features.InlineStyleFeature(control)
    )

    db_conversion = {
        'from_database_format': {tag: InlineStyleElementHandler(type_)},
        'to_database_format': {
            'style_map': {
                type_: {'element': tag, 'props': {'class': 'text-primary'}}
            }
        },
    }

    features.register_converter_rule(
        'contentstate', feature_name, db_conversion
    )

另一个类似,但颜色不同。

4

1 回答 1

0

这是可能的,但它需要在 Wagtail 中跳很多圈。标签开箱即用h1…h6,因为它们是块级格式 - 编辑器中的每个块只能是一种类型。在这里,您将此RED_TEXT格式创建为内联格式(“内联样式”),它有意支持将多种格式应用于同一文本。


如果您无论如何都想实现这种互斥的实现——您需要编写自定义 JS 代码,以便在尝试添加新样式时自动从文本中删除所需的样式。

这是一个可以做到这一点的函数。它遍历用户选择中的所有字符,并从中删除相关样式:

/**
 * Remove all of the COLOR_ styles from the current selection.
 * This is to ensure only one COLOR_ style is applied per range of text.
 * Replicated from https://github.com/thibaudcolas/draftjs-filters/blob/f997416a0c076eb6e850f13addcdebb5e52898e5/src/lib/filters/styles.js#L7,
 * with additional "is the character in the selection" logic.
 */
export const filterColorStylesFromSelection = (
  content: ContentState,
  selection: SelectionState,
) => {
  const blockMap = content.getBlockMap();
  const startKey = selection.getStartKey();
  const endKey = selection.getEndKey();
  const startOffset = selection.getStartOffset();
  const endOffset = selection.getEndOffset();

  let isAfterStartKey = false;
  let isAfterEndKey = false;

  const blocks = blockMap.map((block) => {
    const isStartBlock = block.getKey() === startKey;
    const isEndBlock = block.getKey() === endKey;
    isAfterStartKey = isAfterStartKey || isStartBlock;
    isAfterEndKey = isAfterEndKey || isEndBlock;
    const isBeforeEndKey = isEndBlock || !isAfterEndKey;
    const isBlockInSelection = isAfterStartKey && isBeforeEndKey;

    // Skip filtering through the block chars if out of selection.
    if (!isBlockInSelection) {
      return block;
    }

    let altered = false;

    const chars = block.getCharacterList().map((char, i) => {
      const isAfterStartOffset = i >= startOffset;
      const isBeforeEndOffset = i < endOffset;
      const isCharInSelection =
        // If the selection is on a single block, the char needs to be in-between start and end offsets.
        (isStartBlock &&
          isEndBlock &&
          isAfterStartOffset &&
          isBeforeEndOffset) ||
        // Start block only: after start offset
        (isStartBlock && !isEndBlock && isAfterStartOffset) ||
        // End block only: before end offset.
        (isEndBlock && !isStartBlock && isBeforeEndOffset) ||
        // Neither start nor end: just "in selection".
        (isBlockInSelection && !isStartBlock && !isEndBlock);

      let newChar = char;

      if (isCharInSelection) {
        char
          .getStyle()
          .filter((type) => type.startsWith("COLOR_"))
          .forEach((type) => {
            altered = true;
            newChar = CharacterMetadata.removeStyle(newChar, type);
          });
      }

      return newChar;
    });

    return altered ? block.set("characterList", chars) : block;
  });

  return content.merge({
    blockMap: blockMap.merge(blocks),
  });
};

这取自 Draftail ColorPicker演示,您可以在Draftail Storybook 的“自定义格式”示例中看到该演示正在运行。


要在 Draftail 中实现这种自定义,您需要使用控件API。不幸的是,目前 Wagtail 的编辑器集成中不支持开箱即用的 API(请参阅wagtail/wagtail#5580),因此目前为了使其正常工作,您还需要在 Wagtail 中自定义 Draftail 的初始化。

于 2022-01-12T21:16:38.413 回答