UNPKG

@kadconsulting/dry

Version:
122 lines 6.29 kB
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; import { useState, useEffect, useRef, forwardRef, useCallback, useMemo, } from 'react'; import { v4 as uuid } from 'uuid'; import useCopyToClipboard from '../../hooks/useCopyToClipboard'; import './Textarea.scss'; // import * as Utils from "./Textarea.utils.js"; import { Label as DryLabel } from '../TextInput/constituents/Label'; import classnames from 'classnames'; const Textarea = forwardRef(({ id, className, passProps, label, error, labelColor, ContainerProps, onFocus, onBlur, readOnly, disabled, lineNumbers, defaultText, tabHandling, richTextFormatting, asyncValidation, width, inputTextColor, inputBackgroundColor, required, LabelProps, labelRef, HintText, ...props }, ref) => { const [textAreaId] = useState(() => id || `textarea-${uuid()}`); const [textAreaValue, setTextAreaValue] = useState(defaultText || ''); const [lineCount, setLineCount] = useState(1); const [asyncError, setAsyncError] = useState(null); const textAreaRef = useRef(null); const { copyToClipboard } = useCopyToClipboard(); const genStyle = () => { const baseStyle = inputTextColor ? { color: inputTextColor, } : {}; if (inputBackgroundColor) { baseStyle.backgroundColor = inputBackgroundColor; } return baseStyle; }; useEffect(() => { if (textAreaRef.current && lineNumbers) { const lineArray = textAreaValue.split('\n'); setLineCount(lineArray.length); } }, [textAreaValue, lineNumbers]); const validateAsync = useCallback(async (value) => { if (asyncValidation) { const result = await asyncValidation(value); setAsyncError(result); } }, [asyncValidation]); const handleEvent = (event) => { const value = event.target.value; setTextAreaValue(value); validateAsync(value); }; const handleFocus = () => { if (onFocus) { onFocus(); } }; const handleBlur = () => { if (onBlur) { onBlur(); } if (!textAreaValue && defaultText) { setTextAreaValue(defaultText); } }; const applyFormatting = (startTag, endTag) => { const start = textAreaRef.current?.selectionStart; const end = textAreaRef.current?.selectionEnd; const selectedText = textAreaValue.substring(start || 0, end); const beforeText = textAreaValue.substring(0, start); const afterText = textAreaValue.substring(end || 0); const formattedText = `${startTag}${selectedText}${endTag}`; const newValue = `${beforeText}${formattedText}${afterText}`; setTextAreaValue(newValue); textAreaRef.current.selectionStart = (start || 0) + formattedText.length; textAreaRef.current.selectionEnd = (start || 0) + formattedText.length; }; const handleKeyDown = (e) => { if (tabHandling && e.key === 'Tab') { e.preventDefault(); const start = textAreaRef.current?.selectionStart; const end = textAreaRef.current?.selectionEnd; const value = textAreaValue; const newValue = `${value.substring(0, start)}\t${value.substring(end || 0)}`; setTextAreaValue(newValue); } if (richTextFormatting) { if (e.ctrlKey && e.key === 'b') { e.preventDefault(); applyFormatting('**', '**'); } if (e.ctrlKey && e.key === 'i') { e.preventDefault(); applyFormatting('_', '_'); } } }; const handleCut = (e) => { if (richTextFormatting) { const selectedText = window.getSelection()?.toString(); e.clipboardData.setData('text/plain', `Cut: ${selectedText}`); copyToClipboard(`Cut: ${selectedText}`); } }; const handleCopy = (e) => { if (richTextFormatting) { const selectedText = window.getSelection()?.toString(); e.clipboardData.setData('text/plain', `Copy: ${selectedText}`); copyToClipboard(`Copy: ${selectedText}`); } }; const handlePaste = async (e) => { if (richTextFormatting) { e.preventDefault(); const pastedData = e.clipboardData.getData('text/plain'); const modifiedData = `Pasted: ${pastedData}`; const start = textAreaRef.current?.selectionStart; const end = textAreaRef.current?.selectionEnd; const value = textAreaValue; const newValue = `${value.substring(0, start)}${modifiedData}${value.substring(end || 0)}`; setTextAreaValue(newValue); if (asyncValidation) { await validateAsync(newValue); } } }; const Label = useMemo(() => (_jsx(DryLabel, { required: required, labelColor: labelColor, htmlFor: textAreaId, ref: labelRef, className: classnames('dry-textarea__label', LabelProps?.className), ...LabelProps, children: label })), [label, LabelProps, textAreaId, labelRef, required, labelColor]); return (_jsxs("div", { ref: ref, style: { width: typeof width === 'number' ? `${width}px` : width }, className: 'dry-textarea', ...ContainerProps, children: [Label, lineNumbers && (_jsx("div", { className: 'line-numbers', children: [...Array(lineCount)].map((_, i) => (_jsx("div", { className: 'line-number', children: i + 1 }, i))) })), _jsx("textarea", { className: `dry-textarea__input ${className}`, id: textAreaId, value: textAreaValue, onChange: handleEvent, onFocus: handleFocus, onBlur: handleBlur, onKeyDown: handleKeyDown, onCut: handleCut, onCopy: handleCopy, onPaste: handlePaste, ref: textAreaRef, readOnly: readOnly, disabled: disabled, ...props, style: genStyle() }), HintText && _jsx("div", { className: 'dry-textarea__hint', children: HintText }), (error || asyncError) && (_jsx("p", { className: 'dry-textarea__error', children: error || asyncError })), inputTextColor && (_jsx("style", { children: ` .dry-textarea__input::placeholder {color: ${inputTextColor}; }` }))] })); }); export default Textarea; //# sourceMappingURL=Textarea.js.map