import style from "./style.module.scss"; import {Preview} from "@/components/preview"; import cn from "classnames"; import {useState, useRef, MutableRef, useEffect} from "preact/hooks"; import {JSX} from "preact"; import { User, Type, Bold, Italic, Underline, ChevronRight, EyeOff, Smile, Image, } from "preact-feather"; import {smileysMap} from "@/utils/smileys"; import {Button} from "@/components/button"; import {Input} from "@/components/input"; import {Alert} from "@/components/alert"; const iconSize = 14; // const initialText = "> zefgze https://image.noelshack.com/fichiers/2018/13/4/1522325846-jesusopti.png\n" + // "> zgfzreg :-)))\n" + // "> > ergerg\n" + // "zezefzef\n" + // "loool AHI"; export function Wysiwyg(props: { showTitle: boolean, onSubmit: (user: string, title: string, text: string) => void, disabled?: boolean, initialText?: string, text?: string, setText?: (text: string) => void, }) { const [errors, setErrors] = useState([]); const [title, setTitle] = useState(""); const [user, setUser] = useState(""); const [text, setText] = props.text !== undefined && props.setText !== undefined ? [props.text, props.setText] : useState(props.initialText || ""); const [accordion, setAccordion] = useState<"smileys"|"stickers"|false>(false); // Create a ref for the textarea const textareaRef = useRef(null); const selectionRange = useRef<[start: number, end: number]|null>(null); // // Example function to demonstrate interaction with the ref // const focusTextarea = () => { // if (textareaRef.current) { // textareaRef.current.focus(); // } // }; useEffect(() => { if (textareaRef.current && selectionRange.current) { const [start, end] = selectionRange.current; textareaRef.current.setSelectionRange(start, end); selectionRange.current = null; } }, [text]) const bold = createAction(textareaRef, (start, end) => { const tag = "'''"; setText(insertAt(insertAt(text, tag, start), tag, end + tag.length)); selectionRange.current = [start + tag.length, end + tag.length]; }); const italic = createAction(textareaRef, (start, end) => { const tag = "''"; setText(insertAt(insertAt(text, tag, start), tag, end + tag.length)); selectionRange.current = [start + tag.length, end + tag.length]; }); const underline = createAction(textareaRef, (start, end) => { const startTag = ""; const endTag = ""; setText(insertAt(insertAt(text, startTag, start), endTag, end + startTag.length)); selectionRange.current = [start + startTag.length, end + startTag.length]; }); const quote = createAction(textareaRef, (start, end) => { const lines = text.split("\n"); const newLines: string[] = []; let charCount = 0; let quoteEnd = 0; let addedChars = 0; for (const line of lines) { charCount += line.length + 1; // Add "\n" if (charCount > start && (charCount - line.length) <= end + 1) { newLines.push(`> ${line}`); addedChars += 2; quoteEnd = charCount; } else { newLines.push(line); } } setText(newLines.join("\n")); selectionRange.current = [quoteEnd + addedChars - 1, quoteEnd + addedChars - 1]; }); const spoil = createAction(textareaRef, (start, end) => { const startTag = ""; const endTag = ""; setText(insertAt(insertAt(text, startTag, start), endTag, end + startTag.length)); selectionRange.current = [start + startTag.length, end + startTag.length]; }); return ( setUser(v as string)} placeholder="Pseudo" /> { props.showTitle ? ( setTitle(v as string)} placeholder="Titre du sujet" /> ) : null } { setAccordion(accordion ? false : "smileys") }} > setText((e.target as HTMLTextAreaElement).value)} ref={textareaRef} // Attach ref to textarea /> { const errors: string[] = []; if(user.length < 2) { errors.push("Le pseudo doit se composer au minimum de 3 caractères"); } if(user.match(/[^a-zA-Z0-9_-]/)) { errors.push("Le pseudo doit uniquement contenir les caractères suivant: a-z, A-Z, 0-9, _ et -"); } if(props.showTitle && title.length < 2) { errors.push("Le titre du sujet doit se composer au minimum de 3 caractères"); } if(text.length < 2) { errors.push("Le text du sujet doit se composer au minimum de 3 caractères"); } setErrors(errors); if(errors.length > 0) { return; } props.onSubmit(user, title, text); setUser(""); setTitle(""); setText(""); }} disabled={props.disabled} > Poster ); } function SmileyList(props: { text: string, setText: (text: string) => void, textareaRef: MutableRef, selectionRange: MutableRef<[start: number, end: number]|null>, }) { // const addSmiley = createAction(props.textareaRef, (start, end) => { // const startTag = ""; // const endTag = ""; // // props.setText(insertAt(insertAt(props.text, startTag, start), endTag, end + startTag.length)); // props.selectionRange.current = [start + startTag.length, end + startTag.length]; // }); const addSmiley: (smiley: string) => JSX.MouseEventHandler = (smiley) => (e) => { e.preventDefault(); // Do not lose the focus on textarea if (props.textareaRef.current) { props.setText(insertAt(props.text, smiley, props.textareaRef.current.selectionStart)); props.selectionRange.current = [props.textareaRef.current.selectionStart + smiley.length, props.textareaRef.current.selectionStart + smiley.length]; props.textareaRef.current.focus(); } } return ( {smileysMap.map((smiley) => ( ))} ) } function createAction( ref: MutableRef, callback: (start: number, end: number) => void ): JSX.MouseEventHandler { return (e) => { e.preventDefault(); // Do not lose the focus on textarea if (ref.current) { callback(ref.current.selectionStart, ref.current.selectionEnd); ref.current.focus(); } } } function insertAt(originalString: string, substring: string, position: number): string { return originalString.slice(0, position) + substring + originalString.slice(position); }