Skip to content

Instantly share code, notes, and snippets.

@thepuskar
Created December 11, 2024 16:56
Show Gist options
  • Save thepuskar/6c4541bcdf0c9b17eabe7045c9a554d3 to your computer and use it in GitHub Desktop.
Save thepuskar/6c4541bcdf0c9b17eabe7045c9a554d3 to your computer and use it in GitHub Desktop.
The `TextAreaButtonInteraction` is a sophisticated React TypeScript component designed to provide an enhanced text editing experience with intelligent button-based text insertion. It allows users to seamlessly add predefined text to a textarea while maintaining precise cursor positioning and automatic spacing.
import React, { useState, useRef, ChangeEvent, MouseEvent } from 'react';
/**
* Configuration for buttons to be displayed
* @interface ButtonConfig
*/
interface ButtonConfig {
/** Text to be displayed and inserted */
text: string;
/** Tailwind CSS background color class */
color: string;
}
/**
* TextAreaButtonInteraction Component
* Provides an interactive textarea with buttons that insert text
* while maintaining intelligent spacing and cursor position
*
* @component
* @returns {React.ReactElement} Rendered React component
*/
const TextAreaButtonInteraction: React.FC = () => {
/**
* State to manage textarea content
* @type {[string, React.Dispatch<React.SetStateAction<string>>]}
*/
const [text, setText] = useState<string>('');
/**
* Ref to access textarea DOM element directly
* @type {React.RefObject<HTMLTextAreaElement>}
*/
const textareaRef = useRef<HTMLTextAreaElement>(null);
/**
* Ref to track and preserve cursor position
* @type {React.MutableRefObject<number>}
*/
const cursorPositionRef = useRef<number>(0);
/**
* Handles text change in the textarea
* Updates text state and current cursor position
*
* @param {ChangeEvent<HTMLTextAreaElement>} e - Textarea change event
* @returns {void}
*/
const handleTextChange = (e: ChangeEvent<HTMLTextAreaElement>): void => {
setText(e.target.value);
cursorPositionRef.current = e.target.selectionStart;
};
/**
* Tracks cursor position when clicking in the textarea
*
* @param {MouseEvent<HTMLTextAreaElement>} e - Textarea click event
* @returns {void}
*/
const handleCursorClick = (e: MouseEvent<HTMLTextAreaElement>): void => {
if (e.target instanceof HTMLTextAreaElement) {
cursorPositionRef.current = e.target.selectionStart;
}
};
/**
* Inserts button text into textarea with intelligent spacing
*
* @param {string} buttonText - Text to be inserted
* @returns {void}
*/
const handleButtonClick = (buttonText: string): void => {
const textarea = textareaRef.current;
if (textarea) {
// Preserve the exact cursor position
const cursorPosition = cursorPositionRef.current;
// Check characters before and after the cursor
const charBefore = text.charAt(cursorPosition - 1) || '';
const charAfter = text.charAt(cursorPosition) || '';
// Determine space requirements
let finalButtonText = buttonText;
if (charBefore !== '' && charBefore !== ' ') {
// Add space at the start if previous character exists and is not a space
finalButtonText = ' ' + finalButtonText;
}
if (charAfter !== '' && charAfter !== ' ') {
// Add space at the end if next character exists and is not a space
finalButtonText = finalButtonText + ' ';
}
// Create new text by inserting button text at cursor position
const newText =
text.slice(0, cursorPosition) +
finalButtonText +
text.slice(cursorPosition);
// Update text state
setText(newText);
// Set cursor back to its original position
requestAnimationFrame(() => {
const newCursorPosition = cursorPosition + finalButtonText.length;
textarea.setSelectionRange(newCursorPosition, newCursorPosition);
});
}
};
/**
* Button configurations for dynamic rendering
* @type {ButtonConfig[]}
*/
const buttons: ButtonConfig[] = [
{ text: 'Hello', color: 'bg-blue-500' },
{ text: 'World', color: 'bg-green-500' },
{ text: 'React', color: 'bg-purple-500' },
{ text: 'Coding', color: 'bg-red-500' }
];
/**
* Renders the TextAreaButtonInteraction component
* @returns {React.ReactElement}
*/
return (
<div className="p-4 max-w-md mx-auto">
<textarea
ref={textareaRef}
value={text}
onChange={handleTextChange}
onClick={handleCursorClick}
className="w-full h-40 border p-2 mb-4"
placeholder="Click buttons to insert text..."
/>
<div className="flex flex-wrap gap-2">
{buttons.map((button, index) => (
<button
key={index}
onClick={() => handleButtonClick(button.text)}
className={`${button.color} text-white px-3 py-1 rounded`}
>
{button.text}
</button>
))}
</div>
</div>
);
};
export default TextAreaButtonInteraction;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment