@Denys557

Как вложить span в textarea?

Получается у меня есть что то типо редактора кода, и я хочу сделать подсветку синтаксиса, но я не совсем понимаю как сделать так, что бы я мог вложить span в textarea, потому что если просто его туда всунуть, то будет ошибка

import './Codespace.scss'
import TextareaAutosize from 'react-textarea-autosize';
import { useEffect, useRef, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { RootState } from '../../app/store';
import { editOriginalText, editText } from './selectedFileSlice';
import { editOpenedFileText, saveChanges } from '../OpenedFiles/openedFilesSlice';
const fs = require('fs')

function Codespace() {
    const selectedFile = useSelector((state: RootState) => state.selectedFile);
    const dispatch = useDispatch();
    const [lines, setLines] = useState(['1']);
    const textareaRef = useRef(null);

    const onChange = (e) => {
        dispatch(editText(e.target.value));
        dispatch(editOpenedFileText({ text: e.target.value, path: selectedFile.path }))
        const newLines = [];
        for (let i = 1; i <= e.target.value.split('\n').length; i++) {
            newLines.push(i);
        }
        setLines(newLines);
    }

    function highlightSyntax(text) {
        const keywords = /\b(const|let|var|function|if|else|for|while|return|print)\b/g;
    
        return text
            .replace(keywords, <span className="keyword">$&</span>);
    }

    useEffect(() => {
        const newLines = [];
        for (let i = 1; i <= selectedFile.originalText.split('\n').length; i++) {
            newLines.push(i);
        }
        setLines(newLines);
    }, [selectedFile.originalText])

    useEffect(() => {
        const textarea = textareaRef.current;
        const tabulation = (e) => {
            if (e.keyCode === 9) {
                e.preventDefault();
                const start = textarea.selectionStart;
                const end = textarea.selectionEnd;
                const value = textarea.value;

                textarea.value = value.substring(0, start) + "\t" + value.substring(end);
                textarea.selectionStart = textarea.selectionEnd = start + 1;

                dispatch(editText(textarea.value));
                dispatch(editOpenedFileText({ text: textarea.value, path: selectedFile.path }));
            }
        };

        textarea?.addEventListener('keydown', tabulation);

        return () => {
            textarea?.removeEventListener('keydown', tabulation);
        };
    }, [dispatch, selectedFile.path]);

    useEffect(() => {
        const saveFile = (e) => {
            if(e.keyCode === 83 && e.ctrlKey) {
                e.preventDefault();
                if(selectedFile.originalText !== selectedFile.text) {
                    fs.writeFile(selectedFile.path, selectedFile.text, (err: string) => {
                        if (err) {
                            console.error(err);
                        } else {
                            console.log('updated');
                            dispatch(editOriginalText(selectedFile.text));
                            dispatch(saveChanges({ text: selectedFile.text, path: selectedFile.path }))
                        }
                    });
                }
            }
        }

        document.addEventListener('keydown', saveFile);

        return () => {
            document.removeEventListener('keydown', saveFile);
        }
    }, [dispatch, selectedFile.path]);

    return(
        <div className='codespace'>
            {
                selectedFile.path ?
                    <>
                        <div className="line-numbers">
                            {lines.map(line => (
                                <span key={line}>{line}</span>
                            ))}
                        </div>
                        <TextareaAutosize value={selectedFile.text} onChange={(e) => onChange(e)} className='textarea' ref={textareaRef} />
                    </>
                :
                    <h1>No file selected</h1>
            }
        </div>
    )
}

export default Codespace
  • Вопрос задан
  • 192 просмотра
Решения вопроса 1
Aetae
@Aetae Куратор тега JavaScript
Тлен
Никак.
Textarea - зона текста, там может быть только текст.
Если хотите большего - есть только три варианта, два - геморройные, с кучей коссбраузерных несовместимостей и особенностей, которые обязательно заставят вас рвать волосы на заднице:
1. Отказаться от textarea и использовать contentEditable div.
2. Подкладывать под(или поверх с pointer-events:none) div, который копирует содержимое textarea 1:1 со всеми стилями и отступами и раскрашивает его как надо.

...и третий, рекомендуемый лично мной:
3. Использовать одну из долгоживущих готовых библиотек, в которых всё давно отладили и предоставили удобные интерфейсы.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы