{"version":3,"file":"useRichTextEditor.hook.cjs","sources":["../../../../../src/components/forms/rich-text-editor/hooks/useRichTextEditor.hook.ts"],"sourcesContent":["import { useEditor, type Editor } from '@tiptap/react'\r\nimport Color from '@tiptap/extension-color'\r\nimport Image from '@tiptap/extension-image'\r\nimport Link from '@tiptap/extension-link'\r\nimport TextAlign from '@tiptap/extension-text-align'\r\nimport { FontSize, TextStyle } from '@tiptap/extension-text-style'\r\nimport StarterKit from '@tiptap/starter-kit'\r\nimport { useEffect, useReducer, useRef } from 'react'\r\n\r\nimport { EMPTY_RICH_TEXT_DOCUMENT } from '@components/forms/rich-text-editor/constants/richTextEmptyDocument.const.ts'\r\nimport type { RichTextValue } from '@components/forms/rich-text-editor/types/RichTextEditor.types.ts'\r\nimport { handleRichTextImagePaste } from '@components/forms/rich-text-editor/utils/richTextImagePaste.utils.ts'\r\nimport { normalizeRichTextUrl } from '@components/forms/rich-text-editor/utils/richText.utils.ts'\r\n\r\ntype UseRichTextEditorOptions = {\r\n    value?: RichTextValue\r\n    defaultValue?: RichTextValue\r\n    onValueChange?: (value: RichTextValue) => void\r\n    disabled: boolean\r\n    readOnly: boolean\r\n    required: boolean\r\n    label?: string\r\n    placeholder?: string\r\n    error?: string\r\n    helperText?: string\r\n    labelId: string\r\n    helperId: string\r\n    ariaLabel?: string\r\n    ariaLabelledBy?: string\r\n}\r\n\r\nexport const useRichTextEditor = ({\r\n    value,\r\n    defaultValue,\r\n    onValueChange,\r\n    disabled,\r\n    readOnly,\r\n    required,\r\n    label,\r\n    placeholder,\r\n    error,\r\n    helperText,\r\n    labelId,\r\n    helperId,\r\n    ariaLabel,\r\n    ariaLabelledBy,\r\n}: UseRichTextEditorOptions): Editor | null => {\r\n    const onValueChangeRef = useRef(onValueChange)\r\n    const controlledValueRef = useRef(value)\r\n    const [, refresh] = useReducer((count: number) => count + 1, 0)\r\n    const isDisabled = disabled || readOnly\r\n\r\n    controlledValueRef.current = value\r\n\r\n    useEffect(() => {\r\n        onValueChangeRef.current = onValueChange\r\n    }, [onValueChange])\r\n\r\n    const editor = useEditor({\r\n        extensions: [\r\n            StarterKit.configure({\r\n                link: false,\r\n            }),\r\n            TextStyle,\r\n            FontSize,\r\n            Color,\r\n            Image.extend({\r\n                addAttributes() {\r\n                    return {\r\n                        ...this.parent?.(),\r\n                        width: {\r\n                            default: null,\r\n                            parseHTML: (element) => element.getAttribute('data-width'),\r\n                            renderHTML: (attributes) => attributes.width\r\n                                ? { 'data-width': attributes.width, style: `width: ${attributes.width}` }\r\n                                : {},\r\n                        },\r\n                    }\r\n                },\r\n            }).configure({\r\n                allowBase64: true,\r\n                inline: true,\r\n            }),\r\n            Link.configure({\r\n                autolink: true,\r\n                linkOnPaste: true,\r\n                openOnClick: false,\r\n                enableClickSelection: true,\r\n                HTMLAttributes: {\r\n                    rel: 'noopener noreferrer',\r\n                    target: '_blank',\r\n                },\r\n                isAllowedUri: (url) => Boolean(normalizeRichTextUrl(url)),\r\n                shouldAutoLink: (url) => Boolean(normalizeRichTextUrl(url)),\r\n            }),\r\n            TextAlign.configure({\r\n                types: ['heading', 'paragraph'],\r\n            }),\r\n        ],\r\n        content: value ?? defaultValue ?? EMPTY_RICH_TEXT_DOCUMENT,\r\n        editable: !isDisabled,\r\n        immediatelyRender: false,\r\n        editorProps: {\r\n            attributes: {\r\n                ...(error || helperText ? { 'aria-describedby': helperId } : {}),\r\n                ...(error ? { 'aria-invalid': 'true' } : {}),\r\n                ...(ariaLabel || label ? { 'aria-label': ariaLabel ?? label } : {}),\r\n                ...(ariaLabelledBy || label ? { 'aria-labelledby': ariaLabelledBy ?? labelId } : {}),\r\n                'aria-multiline': 'true',\r\n                ...(required ? { 'aria-required': 'true' } : {}),\r\n                'data-placeholder': placeholder ?? '',\r\n                role: 'textbox',\r\n            },\r\n            handleDOMEvents: {\r\n                mousedown: (view) => {\r\n                    view.dom.setAttribute('data-mouse-focus', '')\r\n                    return false\r\n                },\r\n                blur: (view) => {\r\n                    view.dom.removeAttribute('data-mouse-focus')\r\n                    return false\r\n                },\r\n            },\r\n            handlePaste: handleRichTextImagePaste,\r\n        },\r\n        onUpdate: ({ editor: updatedEditor }) => {\r\n            const nextValue = updatedEditor.getJSON() as RichTextValue\r\n            const emittedValue = JSON.stringify(nextValue)\r\n\r\n            onValueChangeRef.current?.(nextValue)\r\n            refresh()\r\n\r\n            setTimeout(() => {\r\n                const controlledValue = controlledValueRef.current\r\n\r\n                if (\r\n                    controlledValue === undefined\r\n                    || JSON.stringify(controlledValue) === emittedValue\r\n                    || JSON.stringify(updatedEditor.getJSON()) !== emittedValue\r\n                ) {\r\n                    return\r\n                }\r\n\r\n                updatedEditor.commands.setContent(controlledValue, { emitUpdate: false })\r\n                refresh()\r\n            }, 0)\r\n        },\r\n        onTransaction: refresh,\r\n    })\r\n\r\n    useEffect(() => {\r\n        if (!editor) {\r\n            return\r\n        }\r\n\r\n        editor.setEditable(!isDisabled)\r\n    }, [editor, isDisabled])\r\n\r\n    useEffect(() => {\r\n        if (!editor || value === undefined) {\r\n            return\r\n        }\r\n\r\n        const nextValue = JSON.stringify(value)\r\n        const currentValue = JSON.stringify(editor.getJSON())\r\n\r\n        if (nextValue === currentValue) {\r\n            return\r\n        }\r\n\r\n        const { from, to } = editor.state.selection\r\n        editor.commands.setContent(value, { emitUpdate: false })\r\n        const documentSize = editor.state.doc.content.size\r\n\r\n        editor.commands.setTextSelection({\r\n            from: Math.min(from, documentSize),\r\n            to: Math.min(to, documentSize),\r\n        })\r\n        refresh()\r\n    }, [editor, value])\r\n\r\n    return editor\r\n}\r\n"],"names":["useRichTextEditor","value","defaultValue","onValueChange","disabled","readOnly","required","label","placeholder","error","helperText","labelId","helperId","ariaLabel","ariaLabelledBy","onValueChangeRef","useRef","controlledValueRef","refresh","useReducer","count","isDisabled","useEffect","editor","useEditor","StarterKit","TextStyle","FontSize","Color","Image","element","attributes","Link","url","normalizeRichTextUrl","TextAlign","EMPTY_RICH_TEXT_DOCUMENT","view","handleRichTextImagePaste","updatedEditor","nextValue","emittedValue","controlledValue","currentValue","from","to","documentSize"],"mappings":"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"}