UNPKG

textarea-pattern-handler

Version:

A TypeScript utility for handling textarea patterns and DOM manipulation

229 lines (228 loc) 9.43 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); const react_1 = __importStar(require("react")); const textareaWithPattern_1 = require("./textareaWithPattern"); require("textarea-pattern-handler/styles.css"); const PreTagTextarea = (0, react_1.forwardRef)(({ value, setValue, node, className = '', onRemoveTag, fixedText = '', defaultValue = '', id = 'preTagBox' }, ref) => { const boxRef = (0, react_1.useRef)(null); const [tagCounter, setTagCounter] = (0, react_1.useState)(0); const [htmlValue, setHtmlValue] = (0, react_1.useState)(''); (0, react_1.useImperativeHandle)(ref, () => ({ addTag: (tagContent) => { const box = boxRef.current; if (!box) return; const selection = (0, textareaWithPattern_1.getCurrentSelection)(); if (!selection) return; const range = selection.getRangeAt(0); const tagId = `tag-${tagCounter}`; setTagCounter((prev) => prev + 1); const tag = (0, textareaWithPattern_1.createTagElement)(tagContent, tagId); if (box.contains(range.startContainer)) { range.insertNode(tag); const space = (0, textareaWithPattern_1.createSpaceNode)(); tag.after(space); const newRange = (0, textareaWithPattern_1.createNewRange)(space); (0, textareaWithPattern_1.setSelectionToRange)(newRange); } else { box.appendChild(tag); box.appendChild((0, textareaWithPattern_1.createSpaceNode)()); } updateValues(box); }, addText: (textContent) => { const box = boxRef.current; if (!box) return; const selection = (0, textareaWithPattern_1.getCurrentSelection)(); if (!selection) return; const range = selection.getRangeAt(0); const textNode = (0, textareaWithPattern_1.createTextNode)(textContent); if (box.contains(range.startContainer)) { range.insertNode(textNode); const space = (0, textareaWithPattern_1.createSpaceNode)(); textNode.after(space); const newRange = (0, textareaWithPattern_1.createNewRange)(space); (0, textareaWithPattern_1.setSelectionToRange)(newRange); } else { box.appendChild(textNode); box.appendChild((0, textareaWithPattern_1.createSpaceNode)()); } updateValues(box); }, })); const handlePaste = (e) => { e.preventDefault(); const text = e.clipboardData.getData('text/plain'); const fragments = text.split('\n'); const selection = (0, textareaWithPattern_1.getCurrentSelection)(); const range = selection === null || selection === void 0 ? void 0 : selection.getRangeAt(0); const box = e.currentTarget; if (!range || !box) return; range.deleteContents(); fragments.forEach((fragment, index) => { const textNode = (0, textareaWithPattern_1.createTextNode)(fragment); range.insertNode(textNode); range.setStartAfter(textNode); range.collapse(true); if (index < fragments.length - 1) { const br = (0, textareaWithPattern_1.createBRNode)(); range.insertNode(br); range.setStartAfter(br); range.collapse(true); } }); (0, textareaWithPattern_1.setSelectionToRange)(range); updateValues(box); }; const handleKeyDown = (e) => { const box = e.currentTarget; const selection = (0, textareaWithPattern_1.getCurrentSelection)(); if (!selection) return; switch (e.key) { case 'Enter': e.preventDefault(); handleEnterKey(box, selection); break; case 'Delete': case 'Backspace': e.preventDefault(); handleDeleteKey(box, selection); break; case 'a': if (e.ctrlKey) { e.preventDefault(); handleSelectAll(box); } break; } }; const handleEnterKey = (box, selection) => { const range = selection.getRangeAt(0); const br = (0, textareaWithPattern_1.createBRNode)(); const emptyText = (0, textareaWithPattern_1.createEmptyTextNode)(); range.insertNode(br); br.after(emptyText); const newRange = (0, textareaWithPattern_1.createNewRange)(emptyText); (0, textareaWithPattern_1.setSelectionToRange)(newRange); updateValues(box); }; const handleDeleteKey = (box, selection) => { const range = selection.getRangeAt(0); const fullRange = document.createRange(); fullRange.selectNodeContents(box); const isFullSelection = range.compareBoundaryPoints(Range.START_TO_START, fullRange) === 0 && range.compareBoundaryPoints(Range.END_TO_END, fullRange) === 0; if (isFullSelection) { clearContent(box); return; } const patterns = box.querySelectorAll('span.tag'); let removedTags = false; patterns.forEach((pattern) => { if (range.intersectsNode(pattern)) { pattern.remove(); removedTags = true; } }); if (removedTags) { updateValues(box); return; } document.execCommand('delete', false); updateValues(box); }; const handleSelectAll = (box) => { const range = document.createRange(); range.selectNodeContents(box); (0, textareaWithPattern_1.setSelectionToRange)(range); }; const clearContent = (box) => { box.innerHTML = ''; const emptyText = (0, textareaWithPattern_1.createTextNode)(''); box.appendChild(emptyText); const range = (0, textareaWithPattern_1.createNewRange)(emptyText); (0, textareaWithPattern_1.setSelectionToRange)(range); updateValues(box); }; const updateValues = (box) => { const cleanText = (0, textareaWithPattern_1.cleanTextContent)(box.innerText); const cleanHtml = (0, textareaWithPattern_1.cleanHtmlContent)(box.innerHTML); (0, textareaWithPattern_1.removeTrailingBR)(box); setValue(cleanText); setHtmlValue(cleanHtml); }; const handleRemoveTag = (e) => { const target = e.target; if (target.id.includes('tag')) { const tagText = target.innerText; const parentElement = target.parentElement; target.remove(); onRemoveTag === null || onRemoveTag === void 0 ? void 0 : onRemoveTag(tagText); updateValues(parentElement); } }; (0, react_1.useEffect)(() => { if (boxRef.current) { const initialContent = htmlValue || defaultValue; if (initialContent) { boxRef.current.innerHTML = initialContent.replace(fixedText, '').replace(/\n/g, '<br>'); } } }, [defaultValue, fixedText]); const handleInput = (e) => { const box = e.currentTarget; updateValues(box); }; const elementProps = { ref: boxRef, contentEditable: true, onPaste: handlePaste, onClick: handleRemoveTag, onKeyDown: handleKeyDown, onInput: handleInput, id, className: `${className} ${(value === null || value === void 0 ? void 0 : value.length) == 0 && 'placeholderOfTextareaWithPattern'} patternBox`, }; return node ? react_1.default.cloneElement(node, elementProps) : react_1.default.createElement("div", Object.assign({}, elementProps)); }); exports.default = PreTagTextarea;