How to add color in some specific words in a text React JS?

Viewed 117

I am developing a text area that contains some specific words(Eg. they can start/end with "%": %QWERTY%). I want these words to have a different color(E.g: Green) and the text area is still editable. I have tried some solutions but they do not work as I expected, that is why I would like to ask the community how to implement a text input area.

My code is here https://codesandbox.io/s/festive-cache-sshmjd?file=/src/TagsInput.js

In the 1st and 2nd inputs, I classified words and used map() to display them in tags, however, they became separated inputs, not combine into a single one

In the 3rd input, I implemented a for the special word, but this is a fixed string, and I do not know how to implement it like a state variable to detect any changes in the input area.

Could somebody review my code and help?

1 Answers

This works, it colors the target word once the input is no longer focused.

It works by saving the text into state onBlur, separating the string whenever it sees the target word "red".

// src/components/TagsInput.js
import React, { useState } from "react";

function TagsInput() {
  const [tags, setTags] = useState([]);
  const todoList = [
    { title: "item 1", isDone: false },
    { title: "item 2", isDone: true },
  ];

  function handleKeyDown(e) {
    if (e.key !== "Enter") return;
    const value = e.target.value;
    if (!value.trim()) return;
    setTags([...tags, value]);
    e.target.value = "";
    console.log("tags: " + tags);
  }

  function removeTag(index) {
    setTags(tags.filter((el, i) => i !== index));
  }
  const onCodeChange = (event) => {
    console.log("onCodeChange: " + event.currentTarget.textContent);
  };

  function TodoItem({ title, isDone }) {
    return (
      <span
        contentEditable="true"
        style={{ color: isDone ? "green" : "red" }}
        value={title}
        onInput={onCodeChange}
      >
        {title}
      </span>
    );
  }

  function Todo(props) {
    const todoList = props.todoList.map((el) => (
      <TodoItem key={el.id} title={el.title} isDone={el.isDone} />
    ));
    return <p className="tags-input-container">{todoList}</p>;
  }
  function ColorfulText({ children }) {
    return <span style={{ color: "green" }}>{children}</span>;
  }

  const [text, setText] = useState(["Your test sentence"]);

  const targetWord = "red";
  // used to split the text at target word but keep the target word
  // in the returned array
  const regex = new RegExp(`(${targetWord})`);

  return (
    <React.Fragment>
      <div className="tags-input-container">
        {tags.map((tag, index) => (
          <div className="tag-item" key={index}>
            <span className="text">{tag}</span>
            <span className="close" onClick={() => removeTag(index)}>
              &times;
            </span>
          </div>
        ))}
        <input
          onKeyDown={handleKeyDown}
          type="text"
          className="tags-input"
          placeholder="Type something and Press Enter"
        />
      </div>
      <div>
        <code contentEditable="true" suppressContentEditableWarning={true}>
          {tags.map((tag) => {
            <span style={{ color: "orange" }}>{tag}</span>;
          })}
        </code>
      </div>
      <Todo todoList={todoList} />

      <div>
        <p
          contentEditable="true"
          style={{ fontSize: "2rem" }}
          onBlur={(e) => {
            setText([...e.target.textContent.split(regex)]);
          }}
        >
          {text.map((words) => {
            if (words === targetWord) {
              return <ColorfulText>{words}</ColorfulText>;
            } else {
              return words;
            }
          })}
        </p>
      </div>
    </React.Fragment>
  );
}

export default TagsInput;

Related