reactjsrangeselectiontext-decorations

how to replace 'selected text' => 'decorated text' in react / textarea? i can't get exact location(index) of 'selected text'


i'm trying to build some text editor with react

i'm not native in english, so sorry about below question


what i want to do is like this below

(it's 'notion' page gif)

i'm stuck at first one.

i can get whole text and selected text

but i can't get 'exact location' of selectedText in wholeText


here's my code (react, styled-component)

import { useState, useEffect } from 'react';
import styled from 'styled-components';
import autosize from 'autosize';

export default function TextBox({ id, index, content, handleContentInput }) {
  const [currentContent, setCurrentContent] = useState(content);
  const [areaHeight, setAreaHeight] = useState(25);

  useEffect(() => {
    autosize(document.querySelector(`.TextBoxWrap_${id}`));
  }, [currentContent]);

  return (
    <TextBoxContainer
      className={`TextBoxContainer_${id}`}
      areaHeight={areaHeight}
      onClick={() => {
        document.querySelector(`.TextBoxContainer_${id}`).focus();
      }}
    >
      <TextBoxWrap
        className={`TextBoxWrap_${id}`}
        areaHeight={areaHeight}
        onChange={(event) => {
          setCurrentContent(event.target.value);
        }}
        onBlur={() => handleContentInput(id, index, currentContent)}
        onKeyUp={() => {
          let newHeight = document.querySelector(`.TextBoxWrap_${id}`)
            .clientHeight;
          setAreaHeight(newHeight);
        }}
        onMouseUp={() => {
          let whole = window.getSelection();
          if (!whole) return;

          let range = whole.getRangeAt(0);

          console.log({ whole, range });

          let wholeText = whole.anchorNode.childNodes[0].defaultValue;

          let selectedText = whole.toString();

          console.log({ wholeText, selectedText });

          let start = range.startOffset; // Start position
          let end = range.endOffset; // End position

          console.log({ start, end });
        }}
        value={currentContent}
      ></TextBoxWrap>
    </TextBoxContainer>
  );
}

const TextBoxContainer = styled.div`
  max-width: 890px;
  width: 100%;
  height: ${(props) => `${props.areaHeight}px`};

  border: 1px solid black;
  margin: 3px;
  padding: 2px;
`;

const TextBoxWrap = styled.textarea`
  /* border: 1px solid black; */
  box-sizing: 'border-box';
  border: none;
  outline: none;

  width: 100%;
  min-height: 20px;
  height: 20px;

  /* padding: 2x; */
  box-shadow: none;
  display: block;
  overflow: hidden; // Removes scrollbar
  resize: none;
  font-size: 18px;
  /* line-height: 1.5em; */
  /* font-family: Georgia, 'Malgun Gothic', serif; */
`;

inside of onMouseUp, it works like this

wholeText.indexOf(selectedText) was my option, but indexOf() gives me just 'first' index matches,

so, even if i want "aaaaaa'a'aa" but indexOf will give me '0' (=> "'a'aaaaaaaaaa")


what i was thinking,

which means, i need 'selected one's location


how can i do this? plz give me advice

thank you!


posts i read before post this question


Solution

  • i might have some solution for my question, so post this answer myself


    in this question(How to get the start and end points of selection in text area?), 'Tim Down' answered with getInputSelection function.

    getInputSelection needs el as parameter, and i thought this is some kind of 'html element'

    so, from this question(get id of focused element using javascript ), i got a hint : document.activeElement

    and, i tried getInputSelection(document.activeElement) and got an object like { start: (number), end: (number) }