funuicss
Version:
React and Next.js component UI Library for creating Easy and good looking websites with fewer lines of code. Elevate your web development experience with our cutting-edge React/Next.js component UI Library. Craft stunning websites effortlessly, boasting b
791 lines (790 loc) โข 34 kB
JavaScript
// 'use client';
// import React, { useEffect, useRef, useCallback, useState, useMemo } from 'react';
// import { useQuill } from 'react-quilljs';
// import { MdOutlineEmojiEmotions } from 'react-icons/md';
// import { AllEmojis } from '../../utils/Emojis';
// import Dropdown from '../drop/Dropdown';
// import RowFlex from '../specials/RowFlex';
// import ToolTip from '../tooltip/ToolTip';
// import Circle from '../specials/Circle';
// import Tip from '../tooltip/Tip';
// import Flex from '../flex/Flex';
// type RangeStatic = {
// index: number;
// length: number;
// };
// interface RichTextProps {
// value: string;
// onChange: (content: string) => void;
// showEmojis?: boolean;
// placeholder?: string;
// afterEmoji?: React.ReactNode;
// funcss?: string;
// modules?: any;
// theme?: 'bubble' | 'snow';
// fontFamily?: string;
// maxValue?: number;
// }
// const RichText: React.FC<RichTextProps> = ({
// value,
// onChange,
// showEmojis = false,
// placeholder = 'Write something...',
// afterEmoji,
// funcss = '',
// modules,
// theme = 'bubble',
// fontFamily,
// maxValue,
// }) => {
// const savedRange = useRef<RangeStatic | null>(null);
// const onChangeRef = useRef(onChange);
// const maxValueRef = useRef(maxValue);
// const debounceTimeoutRef = useRef<NodeJS.Timeout | null>(null);
// const isInitialMount = useRef(true);
// const isTypingRef = useRef(false);
// const typingTimeoutRef = useRef<NodeJS.Timeout | null>(null);
// const isUserChangeRef = useRef(false);
// const [isFocused, setIsFocused] = useState(false);
// const lastKnownValueRef = useRef(value);
// const isEditorInitializedRef = useRef(false);
// const isUpdatingFromPropsRef = useRef(false);
// const forceUpdateRef = useRef(0);
// // Store the value prop in a ref to compare with internal state
// const valuePropRef = useRef(value);
// // Update refs when props change
// useEffect(() => {
// onChangeRef.current = onChange;
// maxValueRef.current = maxValue;
// }, [onChange, maxValue]);
// const defaultModules = useMemo(() => ({
// toolbar: [['bold', 'italic', 'underline'], [{ list: 'bullet' }]],
// }), []);
// const { quill, quillRef } = useQuill({
// theme,
// placeholder,
// modules: modules || defaultModules,
// });
// // Clean HTML helper function
// const cleanHTML = useCallback((html: string): string => {
// if (!html) return '';
// return html
// .replace(/<p><br><\/p>/g, '')
// .replace(/\s+/g, ' ')
// .trim();
// }, []);
// // Debounced onChange handler
// const debouncedOnChange = useCallback((content: string) => {
// if (debounceTimeoutRef.current) {
// clearTimeout(debounceTimeoutRef.current);
// }
// debounceTimeoutRef.current = setTimeout(() => {
// onChangeRef.current(content);
// }, 300); // 300ms debounce delay
// }, []);
// // Handle text change with debouncing
// const handleTextChange = useCallback((delta: any, oldDelta: any, source: string) => {
// if (!quill || source !== 'user') return;
// // Skip if we're updating from props
// if (isUpdatingFromPropsRef.current) {
// isUpdatingFromPropsRef.current = false;
// return;
// }
// isUserChangeRef.current = true;
// isTypingRef.current = true;
// // Reset typing flag after 500ms of inactivity
// if (typingTimeoutRef.current) {
// clearTimeout(typingTimeoutRef.current);
// }
// typingTimeoutRef.current = setTimeout(() => {
// isTypingRef.current = false;
// }, 500);
// const plainText = quill.getText().trim();
// const currentHTML = quill.root.innerHTML;
// // --- Enforce maxValue if needed ---
// if (maxValueRef.current && plainText.length > maxValueRef.current) {
// // Store current selection
// const selection = quill.getSelection();
// // Remove the extra content
// quill.deleteText(maxValueRef.current, plainText.length - maxValueRef.current);
// // Restore selection if it was at the end
// if (selection && selection.index > maxValueRef.current) {
// quill.setSelection(maxValueRef.current);
// }
// return; // Don't trigger onChange for truncated text
// }
// // --- Clean the HTML output ---
// const cleanedHTML = cleanHTML(currentHTML);
// lastKnownValueRef.current = cleanedHTML;
// // Update value prop ref
// valuePropRef.current = cleanedHTML;
// debouncedOnChange(cleanedHTML);
// }, [quill, debouncedOnChange, cleanHTML]);
// // Handle selection change
// const handleSelectionChange = useCallback((range: RangeStatic | null) => {
// if (range) savedRange.current = range;
// }, []);
// // Handle focus
// const handleFocus = useCallback(() => {
// setIsFocused(true);
// isUserChangeRef.current = false;
// }, []);
// // Handle blur
// const handleBlur = useCallback(() => {
// setIsFocused(false);
// isTypingRef.current = false;
// isUserChangeRef.current = false;
// }, []);
// // Initialize editor with value
// const initializeEditorWithValue = useCallback(() => {
// if (!quill) return;
// const cleanedValue = cleanHTML(value);
// if (cleanedValue && cleanedValue !== '<p><br></p>' && cleanedValue !== '<p></p>') {
// quill.clipboard.dangerouslyPasteHTML(0, cleanedValue);
// }
// lastKnownValueRef.current = cleanedValue;
// valuePropRef.current = cleanedValue;
// }, [quill, value, cleanHTML]);
// // Update editor content from props
// const updateEditorFromProps = useCallback(() => {
// if (!quill || !isEditorInitializedRef.current) return;
// // Clean the incoming value
// const cleanedValue = cleanHTML(value);
// // Get current editor content
// const currentHTML = quill.root.innerHTML;
// const currentCleaned = cleanHTML(currentHTML);
// // Only update if values are different and not empty paragraphs
// if (cleanedValue !== currentCleaned && cleanedValue !== '<p><br></p>' && cleanedValue !== '<p></p>') {
// // Mark that we're updating from props
// isUpdatingFromPropsRef.current = true;
// // Store selection
// const selection = quill.getSelection();
// // Calculate position to set cursor
// const cursorPosition = Math.min(
// selection?.index || 0,
// cleanedValue.replace(/<[^>]*>/g, '').length
// );
// // Replace content
// quill.setText('');
// if (cleanedValue) {
// quill.clipboard.dangerouslyPasteHTML(0, cleanedValue);
// }
// // Update refs
// lastKnownValueRef.current = cleanedValue;
// valuePropRef.current = cleanedValue;
// // Restore cursor position
// if (quill.hasFocus()) {
// setTimeout(() => {
// quill.setSelection(cursorPosition);
// }, 0);
// }
// }
// }, [quill, value, cleanHTML]);
// // Set up event listeners
// useEffect(() => {
// if (!quill) return;
// const editor = quill.root;
// quill.on('selection-change', handleSelectionChange);
// quill.on('text-change', handleTextChange);
// // Add focus/blur event listeners
// editor.addEventListener('focus', handleFocus);
// editor.addEventListener('blur', handleBlur);
// // Initialize editor with initial value on first mount
// if (isInitialMount.current) {
// initializeEditorWithValue();
// isInitialMount.current = false;
// }
// isEditorInitializedRef.current = true;
// return () => {
// quill.off('selection-change', handleSelectionChange);
// quill.off('text-change', handleTextChange);
// // Remove focus/blur event listeners
// editor.removeEventListener('focus', handleFocus);
// editor.removeEventListener('blur', handleBlur);
// // Clean up timeouts
// if (debounceTimeoutRef.current) {
// clearTimeout(debounceTimeoutRef.current);
// }
// if (typingTimeoutRef.current) {
// clearTimeout(typingTimeoutRef.current);
// }
// };
// }, [quill, handleSelectionChange, handleTextChange, handleFocus, handleBlur, initializeEditorWithValue]);
// // Update editor when value prop changes (for external updates)
// useEffect(() => {
// if (!quill || !isEditorInitializedRef.current) return;
// // Skip if value hasn't changed
// if (value === valuePropRef.current) return;
// // Don't update while user is typing
// if (isTypingRef.current) {
// // If user is typing but value changed significantly (like from template selection),
// // we should still update
// const currentPlain = quill.getText();
// const newPlain = value.replace(/<[^>]*>/g, '');
// // If the plain text is significantly different, update anyway
// if (Math.abs(currentPlain.length - newPlain.length) > 10 ||
// !currentPlain.includes(newPlain.substring(0, 20)) &&
// !newPlain.includes(currentPlain.substring(0, 20))) {
// updateEditorFromProps();
// }
// return;
// }
// // Don't update if editor is focused and user recently made changes
// if (isFocused && isUserChangeRef.current) {
// return;
// }
// updateEditorFromProps();
// }, [quill, value, isFocused, updateEditorFromProps]);
// // Force update when forceUpdateRef changes (for external reset)
// useEffect(() => {
// if (quill && isEditorInitializedRef.current) {
// updateEditorFromProps();
// }
// }, [forceUpdateRef.current, quill, updateEditorFromProps]);
// const insertEmoji = useCallback((emoji: string) => {
// if (quill) {
// // Use current selection if available, otherwise use saved range or end of text
// const selection = quill.getSelection();
// const plainText = quill.getText().trim();
// if (!maxValueRef.current || plainText.length + emoji.length <= maxValueRef.current) {
// let insertIndex;
// if (selection) {
// insertIndex = selection.index;
// } else if (savedRange.current) {
// insertIndex = savedRange.current.index;
// } else {
// insertIndex = quill.getLength();
// }
// // Mark as user change
// isUserChangeRef.current = true;
// quill.insertText(insertIndex, emoji);
// quill.setSelection(insertIndex + emoji.length);
// // Update saved range
// savedRange.current = {
// index: insertIndex + emoji.length,
// length: 0
// };
// // Focus the editor
// quill.focus();
// // Update refs
// const currentHTML = quill.root.innerHTML;
// const cleanedHTML = cleanHTML(currentHTML);
// lastKnownValueRef.current = cleanedHTML;
// valuePropRef.current = cleanedHTML;
// debouncedOnChange(cleanedHTML);
// }
// }
// }, [quill, cleanHTML, debouncedOnChange]);
// const renderEmojiSection = useCallback((title: string, emojis: string[]) => (
// <>
// <div className="mb-2 mt-2 text-sm">{title}</div>
// <RowFlex gap={0.3}>
// {emojis.map((emoji, i) => (
// <span
// key={i}
// className="h6 pointer"
// onClick={() => insertEmoji(emoji)}
// >
// {emoji}
// </span>
// ))}
// </RowFlex>
// </>
// ), [insertEmoji]);
// return (
// <div
// className={`fit round-edge ${funcss}`}
// style={{ position: 'relative', overflow: 'visible' }}
// >
// <div id="editor-container" className="bubble-editor-container p-0">
// <div
// ref={quillRef}
// className={theme === 'bubble' ? 'bubble-editor' : 'snow-editor'}
// style={{
// fontFamily: fontFamily || 'inherit',
// }}
// />
// </div>
// {(showEmojis || maxValue) && (
// <div
// className="p-1"
// style={{ height: 'fit-content', top: `calc(100%)`, width: '100%' }}
// >
// <Flex justify="space-between" gap={1} alignItems="center" width="100%">
// {(showEmojis || afterEmoji) ? (
// <div>
// <Flex width="100%" gap={0.5} alignItems="center">
// {showEmojis && (
// <Dropdown
// closableOnlyOutside
// button={
// <ToolTip>
// <Circle size={2} funcss="bg border">
// <MdOutlineEmojiEmotions />
// </Circle>
// <Tip
// tip="top"
// animation="ScaleUp"
// duration={0.5}
// content="Emojis"
// />
// </ToolTip>
// }
// items={[
// {
// label: (
// <div
// className="w-200 h-200"
// style={{ overflowY: 'auto' }}
// >
// {renderEmojiSection('โค๏ธ Smileys & People', AllEmojis.Smiley)}
// {renderEmojiSection('๐ Gestures & Body Parts', AllEmojis.Gesture)}
// {renderEmojiSection('๐ฅ Symbols & Expressions', AllEmojis.Symbols)}
// {renderEmojiSection('๐ Travel, Objects & Activities', AllEmojis.Travel)}
// {renderEmojiSection('๐จโ๐ฉโ๐งโ๐ฆ People & Professions', AllEmojis.People)}
// {renderEmojiSection('๐ถ Animals & Nature', AllEmojis.Animals)}
// </div>
// ),
// },
// ]}
// />
// )}
// {afterEmoji}
// </Flex>
// </div>
// ) : (
// <div />
// )}
// {maxValue && quill ? (
// <div className="text-xs text-right">
// <span className="text-primary">
// {quill.getText().trim().length}
// </span>
// /{maxValue}
// </div>
// ) : (
// <div />
// )}
// </Flex>
// </div>
// )}
// </div>
// );
// };
// export default RichText;
// 'use client';
// import React, { useEffect, useRef, useCallback, useState } from 'react';
// import { useQuill } from 'react-quilljs';
// import { MdOutlineEmojiEmotions } from 'react-icons/md';
// import { AllEmojis } from '../../utils/Emojis';
// import Dropdown from '../drop/Dropdown';
// import RowFlex from '../specials/RowFlex';
// import ToolTip from '../tooltip/ToolTip';
// import Circle from '../specials/Circle';
// import Tip from '../tooltip/Tip';
// import Flex from '../flex/Flex';
// type RangeStatic = {
// index: number;
// length: number;
// };
// interface RichTextProps {
// value: string;
// onChange: (content: string) => void;
// showEmojis?: boolean;
// placeholder?: string;
// afterEmoji?: React.ReactNode;
// funcss?: string;
// modules?: any;
// theme?: 'bubble' | 'snow';
// fontFamily?: string;
// maxValue?: number;
// }
// const RichText: React.FC<RichTextProps> = ({
// value,
// onChange,
// showEmojis = false,
// placeholder = 'Write something...',
// afterEmoji,
// funcss = '',
// modules,
// theme = 'bubble',
// fontFamily,
// maxValue,
// }) => {
// const savedRange = useRef<RangeStatic | null>(null);
// const onChangeRef = useRef(onChange);
// const maxValueRef = useRef(maxValue);
// const debounceTimeoutRef = useRef<NodeJS.Timeout | null>(null);
// const isInitialMount = useRef(true);
// const isTypingRef = useRef(false);
// const typingTimeoutRef = useRef<NodeJS.Timeout | null>(null);
// const [isFocused, setIsFocused] = useState(false);
// const lastKnownValueRef = useRef(value);
// // Update refs when props change
// useEffect(() => {
// onChangeRef.current = onChange;
// maxValueRef.current = maxValue;
// }, [onChange, maxValue]);
// const defaultModules = {
// toolbar: [['bold', 'italic', 'underline'], [{ list: 'bullet' }]],
// };
// const { quill, quillRef } = useQuill({
// theme,
// placeholder,
// modules: modules || defaultModules,
// });
// // Debounced onChange handler
// const debouncedOnChange = useCallback((content: string) => {
// if (debounceTimeoutRef.current) {
// clearTimeout(debounceTimeoutRef.current);
// }
// debounceTimeoutRef.current = setTimeout(() => {
// onChangeRef.current(content);
// }, 300); // 300ms debounce delay
// }, []);
// // Handle text change with debouncing
// const handleTextChange = useCallback(() => {
// if (!quill) return;
// isTypingRef.current = true;
// // Reset typing flag after 500ms of inactivity
// if (typingTimeoutRef.current) {
// clearTimeout(typingTimeoutRef.current);
// }
// typingTimeoutRef.current = setTimeout(() => {
// isTypingRef.current = false;
// }, 500);
// const plainText = quill.getText().trim();
// // --- Enforce maxValue if needed ---
// if (maxValueRef.current && plainText.length > maxValueRef.current) {
// const truncated = plainText.slice(0, maxValueRef.current);
// quill.setText(truncated);
// quill.setSelection(truncated.length);
// return; // Don't trigger onChange for truncated text
// }
// // --- Clean the HTML output ---
// const cleanedHTML = quill.root.innerHTML
// ?.replace(/<p><br><\/p>/g, '') // remove empty paragraphs
// ?.replace(/\s+/g, ' ') // collapse multiple spaces
// ?.trim(); // remove leading/trailing spaces
// lastKnownValueRef.current = cleanedHTML || '';
// debouncedOnChange(lastKnownValueRef.current);
// }, [quill, debouncedOnChange]);
// // Handle selection change
// const handleSelectionChange = useCallback((range: RangeStatic | null) => {
// if (range) savedRange.current = range;
// }, []);
// // Handle focus
// const handleFocus = useCallback(() => {
// setIsFocused(true);
// }, []);
// // Handle blur
// const handleBlur = useCallback(() => {
// setIsFocused(false);
// isTypingRef.current = false;
// }, []);
// // Set up event listeners
// useEffect(() => {
// if (!quill) return;
// const editor = quill.root;
// quill.on('selection-change', handleSelectionChange);
// quill.on('text-change', handleTextChange);
// // Add focus/blur event listeners
// editor.addEventListener('focus', handleFocus);
// editor.addEventListener('blur', handleBlur);
// return () => {
// quill.off('selection-change', handleSelectionChange);
// quill.off('text-change', handleTextChange);
// // Remove focus/blur event listeners
// editor.removeEventListener('focus', handleFocus);
// editor.removeEventListener('blur', handleBlur);
// // Clean up timeouts
// if (debounceTimeoutRef.current) {
// clearTimeout(debounceTimeoutRef.current);
// }
// if (typingTimeoutRef.current) {
// clearTimeout(typingTimeoutRef.current);
// }
// };
// }, [quill, handleSelectionChange, handleTextChange, handleFocus, handleBlur]);
// // Initialize editor with initial value
// useEffect(() => {
// if (!quill) return;
// // Only set initial value on first mount
// if (isInitialMount.current && value) {
// // clean before setting editor value
// const cleanedValue = value
// ?.replace(/<p><br><\/p>/g, '')
// ?.replace(/\s+/g, ' ')
// ?.trim();
// quill.root.innerHTML = cleanedValue || '';
// lastKnownValueRef.current = cleanedValue || '';
// isInitialMount.current = false;
// }
// }, [quill, value]);
// // Update editor when value prop changes (for external updates)
// useEffect(() => {
// if (!quill || isInitialMount.current) return;
// // Skip if value is the same as current editor content
// if (value === lastKnownValueRef.current) return;
// // Don't update while user is typing or editor is focused
// if (isTypingRef.current || isFocused) {
// return;
// }
// // clean before setting editor value
// const cleanedValue = value
// ?.replace(/<p><br><\/p>/g, '')
// ?.replace(/\s+/g, ' ')
// ?.trim();
// if (quill.root.innerHTML !== cleanedValue) {
// quill.root.innerHTML = cleanedValue || '';
// lastKnownValueRef.current = cleanedValue || '';
// }
// }, [quill, value, isFocused]);
// const insertEmoji = useCallback((emoji: string) => {
// if (quill && savedRange.current) {
// const plainText = quill.getText().trim();
// if (!maxValueRef.current || plainText.length + emoji.length <= maxValueRef.current) {
// const selection = quill.getSelection();
// quill.insertText(savedRange.current.index, emoji);
// quill.setSelection(savedRange.current.index + emoji.length);
// }
// }
// }, [quill]);
// const renderEmojiSection = useCallback((title: string, emojis: string[]) => (
// <>
// <div className="mb-2 mt-2 text-sm">{title}</div>
// <RowFlex gap={0.3}>
// {emojis.map((emoji, i) => (
// <span
// key={i}
// className="h6 pointer"
// onClick={() => insertEmoji(emoji)}
// >
// {emoji}
// </span>
// ))}
// </RowFlex>
// </>
// ), [insertEmoji]);
// return (
// <div
// className={`fit round-edge ${funcss}`}
// style={{ position: 'relative', overflow: 'visible' }}
// >
// <div id="editor-container" className="bubble-editor-container p-0">
// <div
// ref={quillRef}
// className={theme === 'bubble' ? 'bubble-editor' : 'snow-editor'}
// style={{
// fontFamily: fontFamily || 'inherit',
// }}
// />
// </div>
// {(showEmojis || maxValue) && (
// <div
// className="p-1"
// style={{ height: 'fit-content', top: `calc(100%)`, width: '100%' }}
// >
// <Flex justify="space-between" gap={1} alignItems="center" width="100%">
// {(showEmojis || afterEmoji) ? (
// <div>
// <Flex width="100%" gap={0.5} alignItems="center">
// {showEmojis && (
// <Dropdown
// closableOnlyOutside
// button={
// <ToolTip>
// <Circle size={2} funcss="bg border">
// <MdOutlineEmojiEmotions />
// </Circle>
// <Tip
// tip="top"
// animation="ScaleUp"
// duration={0.5}
// content="Emojis"
// />
// </ToolTip>
// }
// items={[
// {
// label: (
// <div
// className="w-200 h-200"
// style={{ overflowY: 'auto' }}
// >
// {renderEmojiSection('โค๏ธ Smileys & People', AllEmojis.Smiley)}
// {renderEmojiSection('๐ Gestures & Body Parts', AllEmojis.Gesture)}
// {renderEmojiSection('๐ฅ Symbols & Expressions', AllEmojis.Symbols)}
// {renderEmojiSection('๐ Travel, Objects & Activities', AllEmojis.Travel)}
// {renderEmojiSection('๐จโ๐ฉโ๐งโ๐ฆ People & Professions', AllEmojis.People)}
// {renderEmojiSection('๐ถ Animals & Nature', AllEmojis.Animals)}
// </div>
// ),
// },
// ]}
// />
// )}
// {afterEmoji}
// </Flex>
// </div>
// ) : (
// <div />
// )}
// {maxValue && quill ? (
// <div className="text-xs text-right">
// <span className="text-primary">
// {quill.getText().trim().length}
// </span>
// /{maxValue}
// </div>
// ) : (
// <div />
// )}
// </Flex>
// </div>
// )}
// </div>
// );
// };
// export default RichText;
'use client';
"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;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
var react_1 = __importStar(require("react"));
var react_quilljs_1 = require("react-quilljs");
var md_1 = require("react-icons/md");
var Emojis_1 = require("../../utils/Emojis");
var Dropdown_1 = __importDefault(require("../drop/Dropdown"));
var RowFlex_1 = __importDefault(require("../specials/RowFlex"));
var ToolTip_1 = __importDefault(require("../tooltip/ToolTip"));
var Circle_1 = __importDefault(require("../specials/Circle"));
var Tip_1 = __importDefault(require("../tooltip/Tip"));
var Flex_1 = __importDefault(require("../flex/Flex"));
var RichText = function (_a) {
var value = _a.value, onChange = _a.onChange, _b = _a.showEmojis, showEmojis = _b === void 0 ? false : _b, _c = _a.placeholder, placeholder = _c === void 0 ? 'Write something...' : _c, afterEmoji = _a.afterEmoji, _d = _a.funcss, funcss = _d === void 0 ? '' : _d, modules = _a.modules, _e = _a.theme, theme = _e === void 0 ? 'bubble' : _e, fontFamily = _a.fontFamily, maxValue = _a.maxValue;
var savedRange = (0, react_1.useRef)(null);
var defaultModules = {
toolbar: [['bold', 'italic', 'underline'], [{ list: 'bullet' }]],
};
var _f = (0, react_quilljs_1.useQuill)({
theme: theme,
placeholder: placeholder,
modules: modules || defaultModules,
}), quill = _f.quill, quillRef = _f.quillRef;
(0, react_1.useEffect)(function () {
if (!quill)
return;
var handleSelectionChange = function (range) {
if (range)
savedRange.current = range;
};
var handleTextChange = function () {
var _a, _b, _c;
if (!quill)
return;
var plainText = quill.getText().trim();
// --- Enforce maxValue if needed ---
if (maxValue && plainText.length > maxValue) {
var truncated = plainText.slice(0, maxValue);
quill.setText(truncated);
quill.setSelection(truncated.length);
}
// --- Clean the HTML output ---
var cleanedHTML = (_c = (_b = (_a = quill.root.innerHTML) === null || _a === void 0 ? void 0 : _a.replace(/<p><br><\/p>/g, '') // remove empty paragraphs
) === null || _b === void 0 ? void 0 : _b.replace(/\s+/g, ' ') // collapse multiple spaces
) === null || _c === void 0 ? void 0 : _c.trim(); // remove leading/trailing spaces
onChange(cleanedHTML || '');
};
quill.on('selection-change', handleSelectionChange);
quill.on('text-change', handleTextChange);
return function () {
quill.off('selection-change', handleSelectionChange);
quill.off('text-change', handleTextChange);
};
}, [quill, onChange, maxValue]);
(0, react_1.useEffect)(function () {
var _a, _b;
if (quill && value !== quill.root.innerHTML) {
// clean before setting editor value
var cleanedValue = (_b = (_a = value === null || value === void 0 ? void 0 : value.replace(/<p><br><\/p>/g, '')) === null || _a === void 0 ? void 0 : _a.replace(/\s+/g, ' ')) === null || _b === void 0 ? void 0 : _b.trim();
quill.root.innerHTML = cleanedValue || '';
}
}, [quill, value]);
var insertEmoji = function (emoji) {
if (quill && savedRange.current) {
var plainText = quill.getText().trim();
if (!maxValue || plainText.length + emoji.length <= maxValue) {
quill.insertText(savedRange.current.index, emoji);
quill.setSelection(savedRange.current.index + emoji.length);
}
}
};
var renderEmojiSection = function (title, emojis) { return (react_1.default.createElement(react_1.default.Fragment, null,
react_1.default.createElement("div", { className: "mb-2 mt-2 text-sm" }, title),
react_1.default.createElement(RowFlex_1.default, { gap: 0.3 }, emojis.map(function (emoji, i) { return (react_1.default.createElement("span", { key: i, className: "h6 pointer", onClick: function () { return insertEmoji(emoji); } }, emoji)); })))); };
return (react_1.default.createElement("div", { className: "fit round-edge ".concat(funcss), style: { position: 'relative', overflow: 'visible' } },
react_1.default.createElement("div", { id: "editor-container", className: "bubble-editor-container p-0" },
react_1.default.createElement("div", { ref: quillRef, className: theme === 'bubble' ? 'bubble-editor' : 'snow-editor', style: {
fontFamily: fontFamily || 'inherit',
} })),
(showEmojis || maxValue) && (react_1.default.createElement("div", { className: "p-1", style: { height: 'fit-content', top: "calc(100%)", width: '100%' } },
react_1.default.createElement(Flex_1.default, { justify: "space-between", gap: 1, alignItems: "center", width: "100%" },
(showEmojis || afterEmoji) ? (react_1.default.createElement("div", null,
react_1.default.createElement(Flex_1.default, { width: "100%", gap: 0.5, alignItems: "center" },
showEmojis && (react_1.default.createElement(Dropdown_1.default, { closableOnlyOutside: true, button: react_1.default.createElement(ToolTip_1.default, null,
react_1.default.createElement(Circle_1.default, { size: 2, funcss: "bg border" },
react_1.default.createElement(md_1.MdOutlineEmojiEmotions, null)),
react_1.default.createElement(Tip_1.default, { tip: "top", animation: "ScaleUp", duration: 0.5, content: "Emojis" })), items: [
{
label: (react_1.default.createElement("div", { className: "w-200 h-200", style: { overflowY: 'auto' } },
renderEmojiSection('โค๏ธ Smileys & People', Emojis_1.AllEmojis.Smiley),
renderEmojiSection('๐ Gestures & Body Parts', Emojis_1.AllEmojis.Gesture),
renderEmojiSection('๐ฅ Symbols & Expressions', Emojis_1.AllEmojis.Symbols),
renderEmojiSection('๐ Travel, Objects & Activities', Emojis_1.AllEmojis.Travel),
renderEmojiSection('๐จโ๐ฉโ๐งโ๐ฆ People & Professions', Emojis_1.AllEmojis.People),
renderEmojiSection('๐ถ Animals & Nature', Emojis_1.AllEmojis.Animals))),
},
] })),
afterEmoji))) : (react_1.default.createElement("div", null)),
maxValue && quill ? (react_1.default.createElement("div", { className: "text-xs text-right" },
react_1.default.createElement("span", { className: "text-primary" }, quill.getText().trim().length),
"/",
maxValue)) : (react_1.default.createElement("div", null)))))));
};
exports.default = RichText;