Вы можете получить полный исходный код на Github.

Prequesties

Мы будем использовать стилизованные компоненты реакции для помещения стиля CSS (SASS), встроенного в файлы javascript, который затем будет отображаться как обычный компонент React. Таким образом, мы можем быстро создавать настраиваемые контейнеры с настраиваемым стилем и просто отображать его в реагировании. Поэтому не забудьте сначала установить.

npm install styled-components --save

Кроме того, нам нужно визуализировать значки для панели инструментов, мы можем использовать пакет react-fontawesome, который встраивает все значки fontawesome как SVG.

npm i --save @fortawesome/fontawesome-svg-core
npm i --save @fortawesome/free-solid-svg-icons
npm i --save @fortawesome/react-fontawesome

Предыдущее руководство.

Редактор проекта

Давайте попробуем немного стилизовать редактор, чтобы он больше походил на текстовое поле редактора Rich Text Editor.

Переместите editor.jsx в корневую папку / src, мы также собираемся создать собственный контейнер для редактора черновиков с помощью styled-components.

import styled from "styled-components";

//Root Wrapper of the Editor 
const EditorWrapper = styled.div`
  min-width: 700px;
  display: flex;
  flex-direction: column;
  height: fit-content;
  margin-top: 3em;
`;
//DraftEditor Container 
const EditorContainer = styled.div`
  display: flex;
  min-height: 9em;
  border-radius: 0 0 3px 3px;
  background-color: #fff;
  padding: 5px;
  font-size: 17px;
  font-weight: 300;
  box-shadow: 0px 0px 3px 1px rgba(15, 15, 15, 0.17);
`;

Затем мы можем обернуть редактор контейнером, чтобы применить к нему некоторый собственный стиль, например границу и тень блока.

render() {
    const { editorState } = this.state;
    // Debug your Editor and notice that the EditorState gets update on every
    // character you type
    console.log("EditorSTATE: ", this.state.editorState);
    //Render the Draftjs Editor Component
    /*
    The Editor Takes the current editorState and provides
    you with onChange callback to update the current EditorState being stored on your state.
    */
    return (
        <EditorWrapper>
          {/*Render toolbar here*/}
          <EditorContainer>
              <DraftEditor
              placeholder="Explore Your Way In..."
              editorState={this.state.editorState}
              onChange={this.updateEditorState.bind(this)}
              customStyleFn={customStyleFn}
              />
          </EditorContainer>
        </EditorWrapper>
    );
}

Обязательно запустите наблюдение за сборкой Webpack, чтобы скомпилировать код реакции, затем обновите, вы должны увидеть элегантный контейнер редактора, который теперь больше похож на Rich Tex Editor.

Панель инструментов редактора

Панель инструментов - это то, что содержит различные инструменты для управления текстом под текстовым полем, мы создадим настраиваемую панель инструментов, которая считывает объект конфигурации (массив) и отображает в зависимости от того, что предоставлено таким образом, мы можем сделать редактор более настраиваемым, чтобы позже мы может потребоваться добавить возможность настройки панели инструментов со стороны пользователя.

Создайте панель инструментов / папку в контейнерах / и поместите index.jsx, который является основным файлом входа компонента панели инструментов.

import React from "react";
import styled from "styled-components";

const ToolbarContainer = styled.div`
  display: flex;
  flex-direction: row;
  align-items: center;
  min-height: 48px;
  padding: 5px 7px;
  margin-bottom: 8px;
  border-radius: 2px 2px 0 0;
  box-shadow: 0px 0px 3px 1px rgba(15, 15, 15, 0.17);
`;

export default class Toolbar extends React.Component {
  render() {
    return (
      <ToolbarContainer>
        {/*Render inline styles, block types and custom styles*/}
      </ToolbarContainer>
    );
  }
}

Мы создаем ToolbarContainer с тенью блока и дизайном flexbox. Под контейнером мы будем отображать ToolbarItems для трех разных стилей, которые у нас есть.

  • Встроенные стили: стандартные стили текста, которые применяются встроенно (например, полужирный, курсив).
  • Типы блоков: стили, которые назначаются в новой строке (например, неупорядоченный список, header1).
  • Пользовательские стили: этими стилями можно управлять с помощью пользовательской функции, которая должна определять их поведение.

Создайте файл common.js на панели инструментов / в папке, в которой будут храниться все общие компоненты между различными модулями визуализации.

import styled from "styled-components";
//Toolbar Item styled components 
export const ToolbarItem = styled.div`
  width: 28px;
  height: 27px;
  display: flex;
  justify-content: center;
  align-items: center;
  margin-right: 5px;
  box-shadow: 0px 1px 11px 1px rgba(15, 15, 15, 0.2);
  background-color: #34495e;
  color: #fff;
  font-size: 16px;
  font-family: Oxygen, sans-serif;
  transition: all 250ms ease-in-out;
  cursor: pointer;

  ${props =>
    props.isActive &&
    `    transform: translateY(1px);
    color: #34495e;
    background-color: transparent;
    box-shadow: none;
    border: 1px solid #34495e;`}

  &:hover {
    transform: translateY(1px);
    color: #34495e;
    background-color: transparent;
    box-shadow: none;
    border: 1px solid #34495e;
  }
`;
//Basic Container 
export const Container = styled.div`
  display: flex;
  margin-right: 7px;
`;

Также создайте файл constants.jsx, который будет содержать объекты конфигурации для панели инструментов.

/* common.jsx */
import React from "react";
//We Import icon components
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
//Different types of fontawesome icons 
import {
  faBold,
  faUnderline,
  faItalic,
  faAnchor
} from "@fortawesome/free-solid-svg-icons";

/*Array of object each object has a label to identify the current style, standard Draftjs style to be applied and an optional icon to use when rendering the current style under the toolbar */
const inlineStyles = [
  {
    label: "bold",
    style: "BOLD",
    icon: <FontAwesomeIcon icon={faBold} />
  },
  {
    label: "italic",
    style: "ITALIC",
    icon: <FontAwesomeIcon icon={faItalic} />
  },
  {
    label: "Underline",
    style: "UNDERLINE",
    icon: <FontAwesomeIcon icon={faUnderline} />
  }
];

export { inlineStyles };

Таким образом, у нас может быть декларативный API, поэтому всякий раз, когда нам нужно отредактировать элемент панели инструментов, добавляя, редактируя или удаляя, мы можем легко изменить это в массиве inlineStyles.

Мы также используем стандартные бесплатные SVG-значки Fontawesome (например, faBold) для необязательной визуализации значка под панелью инструментов.

Давайте создадим настраиваемое средство визуализации для встроенных стилей, которое будет заботиться о отображении элемента панели инструментов для каждого объекта в массиве встроенных стилей и обрабатывать события щелчка и отображать активное состояние текущего примененного элемента к тексту.

/* inlineStyles.jsx */
import React from "react";
import { inlineStyles } from "./constants";
import styled from "styled-components";
import { ToolbarItem, Container } from "./common";
//Rich utils is a utility library for manipulating text (like inlineStyle, blockTypes...)
import { RichUtils } from "draft-js";

export function RenderInlineStyles(props) {
  const { editorState, updateEditorState } = props;
  //apply stlye using RichUtils 
  const applyStyle = (e, style) => {
    e.preventDefault();
    //Rich utils returns a new editorState with applied style
    //Make sure to update the main editor state 
    updateEditorState(RichUtils.toggleInlineStyle(editorState, style));
  };
  /*Check if current style is active under text selection (the position of the cursor under text)*/
  const isActive = style => {
    //currentStyle is a map of currently applied style to selected text
    const currentStyle = editorState.getCurrentInlineStyle();
    //check if current style is among the style map.
    return currentStyle.has(style);
  };
 
  /*  
    Loop through the array where we render each ToolbarItem passing it the active state and the react key prop for optimizing the rendering process and we handle the click event to apply style.
  */
  return (
    <Container>
      {inlineStyles.map((item, idx) => {
        return (
          <ToolbarItem
            isActive={isActive(item.style)}
            key={`${item.label}-${idx}`}
            onClick={e => applyStyle(e, item.style)}
          >
            {item.icon || item.label}
          </ToolbarItem>
        );
      })}
    </Container>
  );
}

Таким образом, мы просто перебираем встроенный массив стилей и визуализируем для каждого объекта новый ToolbarItem, который принимает свойство isActive, чтобы определить, активен ли стиль текущего элемента под выделенным текстом (по курсору).

Опора Key используется для уникальной идентификации каждого элемента, отображаемого в массиве, таким образом, react знает, как оптимизировать элементы, не сталкиваясь с проблемами производительности.

событие щелчка используется для применения стиля, передавая ему желаемый стиль, который мы хотим применить, который мы получаем из объекта встроенных стилей, затем мы используем Rich Utils для применения стандартного встроенного стиля к текущему выделенному тексту.

Наконец, нам нужно использовать встроенное средство визуализации стилей под панелью инструментов, где каждый тип стиля имеет собственное настраиваемое средство визуализации, чтобы знать, как правильно обрабатывать каждый объект конфигурации.

render() {
    return (
        <ToolbarContainer>
        <RenderInlineStyles
            editorState={this.props.editorState}
            updateEditorState={this.props.updateEditorState}
        />
        </ToolbarContainer>
    );
}

Не забудьте передать editorState и метод updateEditorState, чтобы иметь возможность управлять основным состоянием Draft Editor.