EditableInput.jsx 수정

This commit is contained in:
현성필
2025-01-25 22:41:32 +09:00
parent e58855e636
commit 387bc00671
2 changed files with 76 additions and 31 deletions
@@ -77,8 +77,7 @@ export const HTTPClientView = ({model, index}) => {
})); }));
}; };
const handlePathChange = (e) => { const handlePathChange = async (path) => {
const path = e.target.value;
if (!path) { if (!path) {
setCurrentModel(prev => ({ setCurrentModel(prev => ({
...prev, ...prev,
@@ -87,12 +86,14 @@ export const HTTPClientView = ({model, index}) => {
})); }));
return; return;
} }
const queryParams = parseQueryParams(path); setCurrentModel(prev => {
setCurrentModel(prev => ({ const queryParams = parseQueryParams(path);
...prev, return {
path, ...prev,
queryParams path,
})); queryParams
};
});
}; };
@@ -106,21 +106,67 @@ const EditableInput = ({
return parts; return parts;
}; };
// useEffect(() => { const getCurrentCursor = () => {
// if (value !== internalValue) { const selection = window.getSelection();
// setInternalValue(value); if (selection.rangeCount === 0) return null;
// }
// }, [value]); const range = selection.getRangeAt(0);
const preCaretRange = range.cloneRange();
preCaretRange.selectNodeContents(contentRef.current);
preCaretRange.setEnd(range.endContainer, range.endOffset);
const content = preCaretRange.toString().trim();
if (content === '\u200B') {
return 0;
}
return content.replace('\u200B', '').length;
};
const restoreCursor = (position) => {
const element = contentRef.current;
const selection = window.getSelection();
const range = document.createRange();
range.setStart(element, 0);
range.collapse(true);
const nodeStack = [element];
let node, foundStart = false, stop = false;
let charIndex = 0;
while (!stop && (node = nodeStack.pop())) {
if (node.nodeType === 3) {
const nextCharIndex = charIndex + node.length;
if (!foundStart && position >= charIndex && position <= nextCharIndex) {
range.setStart(node, position - charIndex);
foundStart = true;
}
if (foundStart && position >= charIndex && position <= nextCharIndex) {
range.setEnd(node, position - charIndex);
stop = true;
}
charIndex = nextCharIndex;
} else {
let i = node.childNodes.length;
while (i--) {
nodeStack.push(node.childNodes[i]);
}
}
}
selection.removeAllRanges();
selection.addRange(range);
};
const handleInput = (e) => { const handleInput = (e) => {
console.log('handleInput', e);
const newValue = e.target.textContent; const newValue = e.target.textContent;
const newSelection = saveSelection(); const cursorPosition = getCurrentCursor();
console.log(newValue);
setInternalValue(newValue); setInternalValue(newValue);
setSelection(newSelection);
onChange(newValue); onChange(newValue);
requestAnimationFrame(() => {
if (contentRef.current) {
restoreCursor(cursorPosition);
}
});
}; };
const handlePaste = (e) => { const handlePaste = (e) => {
@@ -130,22 +176,18 @@ const EditableInput = ({
if (selection.rangeCount > 0) { if (selection.rangeCount > 0) {
const range = selection.getRangeAt(0); const range = selection.getRangeAt(0);
const cursorPosition = getCurrentCursor();
range.deleteContents(); range.deleteContents();
const textNode = document.createTextNode(text);
range.insertNode(textNode);
const processedContent = parseContent(text); const newValue = contentRef.current.textContent;
const fragment = document.createDocumentFragment(); setInternalValue(newValue);
onChange(newValue);
processedContent.forEach(({ type, content }) => { requestAnimationFrame(() => {
const span = document.createElement('span'); restoreCursor(cursorPosition + text.length);
span.textContent = content;
if (type === 'variable') {
span.className = 'bg-blue-100 text-blue-800 rounded px-1';
}
fragment.appendChild(span);
}); });
range.insertNode(fragment);
handleInput({ target: contentRef.current });
} }
}; };
@@ -195,7 +237,9 @@ const EditableInput = ({
data-value={internalValue} data-value={internalValue}
data-name={name} data-name={name}
suppressContentEditableWarning={true} suppressContentEditableWarning={true}
/> >
{internalValue || '\u200B'}
</div>
); );
}; };