@pie-lib/editable-html-tip-tap 2.1.0-next.1 → 2.1.0-next.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/lib/components/EditableHtml.js +1 -1
- package/lib/components/EditableHtml.js.map +1 -1
- package/lib/utils/helper.js +58 -2
- package/lib/utils/helper.js.map +1 -1
- package/package.json +7 -7
- package/src/__tests__/EditableHtml.test.jsx +122 -0
- package/src/__tests__/div-to-paragraph-conversion.test.jsx +125 -0
- package/src/components/EditableHtml.jsx +1 -1
- package/src/utils/__tests__/helper.test.js +126 -0
- package/src/utils/helper.js +54 -2
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,16 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# [2.1.0-next.3](https://github.com/pie-framework/pie-lib/compare/@pie-lib/editable-html-tip-tap@2.1.0-next.2...@pie-lib/editable-html-tip-tap@2.1.0-next.3) (2026-05-07)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @pie-lib/editable-html-tip-tap
|
|
9
|
+
|
|
10
|
+
# [2.1.0-next.2](https://github.com/pie-framework/pie-lib/compare/@pie-lib/editable-html-tip-tap@2.1.0-next.1...@pie-lib/editable-html-tip-tap@2.1.0-next.2) (2026-05-06)
|
|
11
|
+
|
|
12
|
+
### Bug Fixes
|
|
13
|
+
|
|
14
|
+
- made sure multiple divs are treated as line breaks [PIE-439] ([2b507e0](https://github.com/pie-framework/pie-lib/commit/2b507e0d1176a33afebb90e69bf7693c17bd4caf))
|
|
15
|
+
|
|
6
16
|
# [2.1.0-next.1](https://github.com/pie-framework/pie-lib/compare/@pie-lib/editable-html-tip-tap@2.1.0-next.0...@pie-lib/editable-html-tip-tap@2.1.0-next.1) (2026-05-06)
|
|
7
17
|
|
|
8
18
|
### Features
|
|
@@ -236,7 +236,7 @@ var EditableHtml = exports.EditableHtml = function EditableHtml(props) {
|
|
|
236
236
|
onUpdate: function onUpdate(_ref4) {
|
|
237
237
|
var editor = _ref4.editor,
|
|
238
238
|
transaction = _ref4.transaction;
|
|
239
|
-
if (transaction.isDone) {
|
|
239
|
+
if (transaction.isDone || props.markup !== editor.getHTML()) {
|
|
240
240
|
var _props$onChange;
|
|
241
241
|
(_props$onChange = props.onChange) === null || _props$onChange === void 0 || _props$onChange.call(props, editor.getHTML());
|
|
242
242
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EditableHtml.js","names":["_react","_interopRequireWildcard","require","_debounce","_interopRequireDefault","_react2","_styles","_starterKit","_extensionTextStyle","_extensionCharacterCount","_extensionSuperscript","_extensionSubscript","_extensionTextAlign","_extensionImage","_extensionPlaceholder","_helper","_extendedTable","_extendedTableCell","_divNode","_ensureEmptyRootDiv","_ensureListItemContentIsDiv","_extensionTableRow","_responseArea","_math","_image","_media","_css","_extendedListItem","_headingParagraph","_TiptapContainer","_size","_extensions","_excluded","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","_typeof","has","get","set","_t","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","ownKeys","keys","getOwnPropertySymbols","filter","enumerable","push","apply","_objectSpread","arguments","length","forEach","_defineProperty2","getOwnPropertyDescriptors","defineProperties","defaultToolbarOpts","position","alignment","alwaysVisible","showDone","doneOn","defaultResponseAreaProps","options","respAreaToolbar","onHandleAreaChange","DEFAULT_ACTIVE_PLUGINS","cssVariables","EditableHtml","exports","props","_props$pluginProps","_ref","pluginProps","showParagraphs","separateParagraphs","_useState","useState","_useState2","_slicedToArray2","pendingImages","setPendingImages","_useState3","_useState4","scheduled","setScheduled","toolbarOpts","removePendingImage","useCallback","imagePos","prev","next","img","pos","toolbarOptsToUse","activePluginsToUse","useMemo","_ref3","_props$responseAreaPr","_ref2","customPlugins","otherPluginProps","_objectWithoutProperties2","filteredActivePlugins","activePlugins","pluginName","nameToUse","PLUGINS_MAP","pluginInfo","disabled","buildExtensions","math","textAlign","html","extraCSSRules","image","imageSupport","toolbar","table","responseArea","type","responseAreaProps","languageCharacters","languageCharactersProps","keyPadCharacterRef","setKeypadInteraction","media","extensions","TextAlign","configure","types","alignments","TextStyleKit","CharacterCount","limit","charactersLimit","StarterKit","trailingNode","node","notAfter","ExtendedListItem","DivNode","HeadingParagraph","EnsureEmptyRootIsDiv","EnsureListItemContentIsDiv","Placeholder","placeholder","showOnlyWhenEditable","showOnlyCurrent","includeChildren","ExtendedTable","TableRow","ExtendedTableHeader","ExtendedTableCell","ResponseAreaExtension","ExplicitConstructedResponseNode","DragInTheBlankNode","InlineDropdownNode","MathTemplatedNode","MathNode","SubScript","SuperScript","Image","ImageUploadNode","imageHandling","disableImageAlignmentButtons","onDone","editor","_props$onDone","getHTML","onDelete","src","attrs","insertImageRequested","imageInfo","getHandler","_imageInfo","addedImage","onFinish","result","_cb","cb","onChange","callback","handler","focusHandler","debounce","detach","window","removeEventListener","_insertingImage","addEventListener","add","concat","_toConsumableArray2","maxImageWidth","maxImageHeight","Media","uploadSoundSupport","CSSMark","useEditor","immediatelyRender","editorProps","handleKeyDown","view","event","onKeyDown","editable","content","normalizeInitialMarkup","markup","onUpdate","_ref4","transaction","isDone","_props$onChange","onBlur","_ref5","otherToolbarOpened","_toolbarOpened","isActive","_props$onChange2","_props$onDone2","useEffect","editorRef","setEditable","nextMarkup","commands","setContent","entries","_ref6","_ref7","key","value","document","documentElement","style","setProperty","editorState","useEditorState","selector","ctx","_ctx$editor","isFocused","sizeStyle","minWidth","width","maxWidth","minHeight","height","maxHeight","valueToSize","createElement","_extends2","StyledEditorContent","showParagraph","separateParagraph","styled","EditorContent","shouldForwardProp","prop","includes","_ref8","display","outline","flex","padding","margin","left","top","color","pointerEvents","whiteSpace","fontSize","marginBottom","_default"],"sources":["../../src/components/EditableHtml.jsx"],"sourcesContent":["import React, { useCallback, useEffect, useMemo, useState } from 'react';\nimport debounce from 'lodash-es/debounce';\nimport { EditorContent, useEditor, useEditorState } from '@tiptap/react';\nimport { styled } from '@mui/material/styles';\nimport StarterKit from '@tiptap/starter-kit';\nimport { TextStyleKit } from '@tiptap/extension-text-style';\nimport { CharacterCount } from '@tiptap/extension-character-count';\nimport SuperScript from '@tiptap/extension-superscript';\nimport SubScript from '@tiptap/extension-subscript';\nimport TextAlign from '@tiptap/extension-text-align';\nimport Image from '@tiptap/extension-image';\nimport Placeholder from '@tiptap/extension-placeholder';\nimport { normalizeInitialMarkup } from '../utils/helper';\n\nimport ExtendedTable from '../extensions/extended-table';\nimport { ExtendedTableCell, ExtendedTableHeader } from '../extensions/extended-table-cell';\nimport { DivNode } from '../extensions/div-node';\nimport { EnsureEmptyRootIsDiv } from '../extensions/ensure-empty-root-div';\nimport { EnsureListItemContentIsDiv } from '../extensions/ensure-list-item-content-is-div';\nimport { TableRow } from '@tiptap/extension-table-row';\nimport {\n DragInTheBlankNode,\n ExplicitConstructedResponseNode,\n InlineDropdownNode,\n MathTemplatedNode,\n ResponseAreaExtension,\n} from '../extensions/responseArea';\nimport { MathNode } from '../extensions/math';\nimport { ImageUploadNode } from '../extensions/image';\nimport { Media } from '../extensions/media';\nimport { CSSMark } from '../extensions/css';\nimport { ExtendedListItem } from '../extensions/extended-list-item';\nimport { HeadingParagraph } from '../extensions/heading-paragraph';\n\nimport EditorContainer from './TiptapContainer';\nimport { valueToSize } from '../utils/size';\nimport { buildExtensions, PLUGINS_MAP } from '../extensions';\n\nconst defaultToolbarOpts = {\n position: 'bottom',\n alignment: 'left',\n alwaysVisible: false,\n showDone: true,\n doneOn: 'blur',\n};\n\nconst defaultResponseAreaProps = {\n options: {},\n respAreaToolbar: () => {},\n onHandleAreaChange: () => {},\n};\n\nconst DEFAULT_ACTIVE_PLUGINS = [\n 'bold',\n 'italic',\n 'underline',\n 'strikethrough',\n 'code',\n 'bulleted-list',\n 'numbered-list',\n 'image',\n 'math',\n 'languageCharacters',\n 'text-align',\n 'table',\n 'video',\n 'audio',\n 'responseArea',\n 'superscript',\n 'subscript',\n 'css',\n 'h3',\n 'undo',\n 'redo',\n];\n\nconst cssVariables = {\n '--white': '#fff',\n '--black': '#2e2b29',\n '--black-contrast': '#110f0e',\n '--gray-1': 'rgba(61, 37, 20, .05)',\n '--gray-2': 'rgba(61, 37, 20, .08)',\n '--gray-3': 'rgba(61, 37, 20, .12)',\n '--gray-4': 'rgba(53, 38, 28, .3)',\n '--gray-5': 'rgba(28, 25, 23, .6)',\n '--green': '#22c55e',\n '--purple': '#6a00f5',\n '--purple-contrast': '#5800cc',\n '--purple-light': 'rgba(88, 5, 255, .05)',\n '--yellow-contrast': '#facc15',\n '--yellow': 'rgba(250, 204, 21, .4)',\n '--yellow-light': '#fffae5',\n '--red': '#ff5c33',\n '--red-light': '#ffebe5',\n '--shadow': `0px 12px 33px 0px rgba(0, 0, 0, .06),\n 0px 3.618px 9.949px 0px rgba(0, 0, 0, .04)`,\n};\n\nexport const EditableHtml = (props) => {\n const { showParagraphs, separateParagraphs } = props.pluginProps || {};\n const [pendingImages, setPendingImages] = useState([]);\n const [scheduled, setScheduled] = useState(false);\n const { toolbarOpts } = props;\n\n const removePendingImage = useCallback(\n (imagePos) => {\n setPendingImages((prev) => {\n const next = prev.filter((img) => img.pos !== imagePos);\n if (next.length === 0) {\n setScheduled(false);\n }\n return next;\n });\n },\n [setPendingImages],\n );\n\n const toolbarOptsToUse = {\n ...defaultToolbarOpts,\n ...toolbarOpts,\n };\n\n const activePluginsToUse = useMemo(() => {\n let { customPlugins, ...otherPluginProps } = props.pluginProps || {};\n\n customPlugins = customPlugins || [];\n\n const filteredActivePlugins = (props.activePlugins || DEFAULT_ACTIVE_PLUGINS)?.filter((pluginName) => {\n const nameToUse = PLUGINS_MAP[pluginName] || pluginName;\n const pluginInfo = otherPluginProps[nameToUse] || {};\n\n return !pluginInfo || !pluginInfo.disabled;\n });\n\n return buildExtensions(filteredActivePlugins, customPlugins, {\n math: {},\n textAlign: props.textAlign,\n html: {},\n extraCSSRules: props.extraCSSRules || {},\n image: {\n ...props.imageSupport,\n },\n toolbar: {},\n table: {},\n responseArea: {\n type: props.responseAreaProps?.type,\n },\n languageCharacters: props.languageCharactersProps,\n keyPadCharacterRef: {},\n setKeypadInteraction: {},\n media: {},\n });\n }, [props]);\n\n const extensions = [\n TextAlign.configure({\n types: ['heading', 'paragraph', 'div', 'headingParagraph', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'td', 'th'],\n alignments: ['left', 'right', 'center', 'justify'],\n }),\n TextStyleKit,\n CharacterCount.configure({\n limit: props.charactersLimit || 1000000,\n }),\n StarterKit.configure({\n trailingNode: {\n node: 'paragraph',\n notAfter: ['paragraph', 'div'],\n },\n }),\n ExtendedListItem,\n DivNode,\n HeadingParagraph,\n EnsureEmptyRootIsDiv,\n EnsureListItemContentIsDiv,\n Placeholder.configure({\n placeholder: props.placeholder,\n // show placeholder even when editor is focused\n showOnlyWhenEditable: true,\n showOnlyCurrent: false, // show on all empty nodes, not only the current one\n includeChildren: true,\n }),\n ExtendedTable,\n TableRow,\n ExtendedTableHeader,\n ExtendedTableCell,\n ResponseAreaExtension.configure(props.responseAreaProps),\n ExplicitConstructedResponseNode.configure(props.responseAreaProps),\n DragInTheBlankNode.configure(props.responseAreaProps),\n InlineDropdownNode.configure(props.responseAreaProps),\n MathTemplatedNode.configure(props.responseAreaProps),\n MathNode.configure({\n toolbarOpts: toolbarOptsToUse,\n math: props.pluginProps?.math || {},\n }),\n SubScript,\n SuperScript,\n Image,\n ImageUploadNode.configure({\n toolbarOpts: toolbarOptsToUse,\n imageHandling: {\n disableImageAlignmentButtons: props.disableImageAlignmentButtons,\n onDone: (editor) => props.onDone?.(editor.getHTML()),\n onDelete:\n props.imageSupport &&\n props.imageSupport.delete &&\n ((node) => {\n const { src } = node.attrs;\n\n props.imageSupport.delete(src, (e) => {\n removePendingImage(node.pos);\n });\n }),\n insertImageRequested:\n props.imageSupport &&\n ((editor, imageInfo, getHandler) => {\n const [addedImage, pos] = imageInfo;\n\n const onFinish = (result) => {\n let cb;\n\n if (scheduled && result) {\n // finish editing only on success\n cb = props.onChange;\n }\n\n removePendingImage(pos);\n cb?.(editor.getHTML());\n };\n\n const callback = () => {\n /**\n * The handler is the object through which the outer context\n * communicates file upload events like: fileChosen, cancel, progress\n */\n const handler = getHandler(onFinish);\n\n // If the user closes the file picker without choosing a file, the window regains\n // focus while _insertingImage is still true — drop the stale pending entry.\n const focusHandler = debounce(() => {\n const detach = () => window.removeEventListener('focus', focusHandler);\n\n if (!editor._insertingImage) {\n detach();\n return;\n }\n\n removePendingImage(pos);\n editor._insertingImage = false;\n detach();\n }, 500);\n\n window.addEventListener('focus', focusHandler);\n\n props.imageSupport.add(handler);\n };\n\n editor._insertingImage = true;\n setPendingImages((prev) => [...prev, addedImage]);\n callback();\n }),\n maxImageWidth: props.maxImageWidth,\n maxImageHeight: props.maxImageHeight,\n },\n limit: 3,\n }),\n Media.configure({\n uploadSoundSupport: props.uploadSoundSupport,\n }),\n CSSMark.configure({\n extraCSSRules: props.extraCSSRules,\n }),\n ];\n\n const editor = useEditor(\n {\n extensions,\n immediatelyRender: false,\n editorProps: {\n handleKeyDown(view, event) {\n if (props.onKeyDown) {\n return props.onKeyDown(event);\n }\n\n // Return false to let default behavior continue\n return false;\n },\n },\n editable: !props.disabled,\n content: normalizeInitialMarkup(props.markup),\n onUpdate: ({ editor, transaction }) => {\n if (transaction.isDone) {\n props.onChange?.(editor.getHTML());\n }\n },\n onBlur: debounce(({ editor }) => {\n const otherToolbarOpened =\n editor._insertingImage ||\n editor._toolbarOpened ||\n editor.isActive('inline_dropdown') ||\n editor.isActive('explicit_constructed_response');\n\n if (otherToolbarOpened) {\n return;\n }\n\n if (props.markup !== editor.getHTML()) {\n props.onChange?.(editor.getHTML());\n }\n\n if (toolbarOptsToUse.doneOn === 'blur') {\n props.onDone?.(editor.getHTML());\n }\n }, 200),\n },\n [props.charactersLimit],\n );\n\n useEffect(() => {\n if (props.editorRef) {\n props.editorRef(editor);\n }\n }, [props.editorRef, editor]);\n\n useEffect(() => {\n editor?.setEditable(!props.disabled);\n }, [props.disabled, editor]);\n\n useEffect(() => {\n if (!editor) {\n return;\n }\n const nextMarkup = normalizeInitialMarkup(props.markup);\n\n if (nextMarkup !== editor.getHTML()) {\n editor.commands.setContent(nextMarkup, false);\n }\n }, [props.markup, editor]);\n\n useEffect(() => {\n Object.entries(cssVariables).forEach(([key, value]) => {\n document.documentElement.style.setProperty(key, value);\n });\n }, []);\n\n const editorState = useEditorState({\n editor,\n selector: (ctx) => ({\n isFocused: ctx.editor?.isFocused,\n }),\n });\n\n const sizeStyle = useMemo(() => {\n const { minWidth, width, maxWidth, minHeight, height, maxHeight } = props;\n\n return {\n width: valueToSize(width),\n minWidth: valueToSize(minWidth),\n maxWidth: valueToSize(maxWidth),\n height: valueToSize(height),\n minHeight: valueToSize(minHeight),\n maxHeight: valueToSize(maxHeight),\n };\n }, [props]);\n\n return (\n <EditorContainer\n {...{\n ...props,\n activePlugins: activePluginsToUse,\n toolbarOpts: toolbarOptsToUse,\n }}\n editorState={editorState}\n editor={editor}\n >\n {editor && (\n <StyledEditorContent\n style={{\n minHeight: sizeStyle.minHeight,\n height: sizeStyle.height,\n maxHeight: sizeStyle.maxHeight,\n }}\n showParagraph={showParagraphs && !showParagraphs.disabled}\n separateParagraph={separateParagraphs && !separateParagraphs.disabled}\n editor={editor}\n />\n )}\n </EditorContainer>\n );\n};\n\nconst StyledEditorContent = styled(EditorContent, {\n shouldForwardProp: (prop) => !['showParagraph', 'separateParagraph'].includes(prop),\n})(({ showParagraph, separateParagraph }) => ({\n display: 'flex',\n outline: 'none !important',\n '& .ProseMirror': {\n flex: 1,\n padding: '5px',\n maxHeight: '500px',\n outline: 'none !important',\n position: 'initial',\n\n // reset default margins for all block paragraphs/divs in the editor\n '& > p, & > div': {\n margin: '0',\n },\n\n // Out of flow so the caret stays at the start of the block; in-flow ::before pushes the caret after the hint text.\n '& p.is-editor-empty, & div.is-editor-empty': {\n position: 'relative',\n },\n '& p.is-editor-empty::before, & div.is-editor-empty::before': {\n content: 'attr(data-placeholder)',\n position: 'absolute',\n left: 0,\n top: 0,\n color: '#9CA3AF',\n pointerEvents: 'none',\n whiteSpace: 'pre-wrap',\n },\n\n ...(showParagraph && {\n '& > p:has(+ p)::after, & > div:has(+ div)::after': {\n display: 'block',\n content: '\"¶\"',\n fontSize: '1em',\n color: '#146EB3',\n },\n }),\n ...(separateParagraph && {\n '& > div:has(+ div)': {\n marginBottom: '1em',\n },\n }),\n },\n}));\n\nexport default EditableHtml;\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"EditableHtml.js","names":["_react","_interopRequireWildcard","require","_debounce","_interopRequireDefault","_react2","_styles","_starterKit","_extensionTextStyle","_extensionCharacterCount","_extensionSuperscript","_extensionSubscript","_extensionTextAlign","_extensionImage","_extensionPlaceholder","_helper","_extendedTable","_extendedTableCell","_divNode","_ensureEmptyRootDiv","_ensureListItemContentIsDiv","_extensionTableRow","_responseArea","_math","_image","_media","_css","_extendedListItem","_headingParagraph","_TiptapContainer","_size","_extensions","_excluded","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","_typeof","has","get","set","_t","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","ownKeys","keys","getOwnPropertySymbols","filter","enumerable","push","apply","_objectSpread","arguments","length","forEach","_defineProperty2","getOwnPropertyDescriptors","defineProperties","defaultToolbarOpts","position","alignment","alwaysVisible","showDone","doneOn","defaultResponseAreaProps","options","respAreaToolbar","onHandleAreaChange","DEFAULT_ACTIVE_PLUGINS","cssVariables","EditableHtml","exports","props","_props$pluginProps","_ref","pluginProps","showParagraphs","separateParagraphs","_useState","useState","_useState2","_slicedToArray2","pendingImages","setPendingImages","_useState3","_useState4","scheduled","setScheduled","toolbarOpts","removePendingImage","useCallback","imagePos","prev","next","img","pos","toolbarOptsToUse","activePluginsToUse","useMemo","_ref3","_props$responseAreaPr","_ref2","customPlugins","otherPluginProps","_objectWithoutProperties2","filteredActivePlugins","activePlugins","pluginName","nameToUse","PLUGINS_MAP","pluginInfo","disabled","buildExtensions","math","textAlign","html","extraCSSRules","image","imageSupport","toolbar","table","responseArea","type","responseAreaProps","languageCharacters","languageCharactersProps","keyPadCharacterRef","setKeypadInteraction","media","extensions","TextAlign","configure","types","alignments","TextStyleKit","CharacterCount","limit","charactersLimit","StarterKit","trailingNode","node","notAfter","ExtendedListItem","DivNode","HeadingParagraph","EnsureEmptyRootIsDiv","EnsureListItemContentIsDiv","Placeholder","placeholder","showOnlyWhenEditable","showOnlyCurrent","includeChildren","ExtendedTable","TableRow","ExtendedTableHeader","ExtendedTableCell","ResponseAreaExtension","ExplicitConstructedResponseNode","DragInTheBlankNode","InlineDropdownNode","MathTemplatedNode","MathNode","SubScript","SuperScript","Image","ImageUploadNode","imageHandling","disableImageAlignmentButtons","onDone","editor","_props$onDone","getHTML","onDelete","src","attrs","insertImageRequested","imageInfo","getHandler","_imageInfo","addedImage","onFinish","result","_cb","cb","onChange","callback","handler","focusHandler","debounce","detach","window","removeEventListener","_insertingImage","addEventListener","add","concat","_toConsumableArray2","maxImageWidth","maxImageHeight","Media","uploadSoundSupport","CSSMark","useEditor","immediatelyRender","editorProps","handleKeyDown","view","event","onKeyDown","editable","content","normalizeInitialMarkup","markup","onUpdate","_ref4","transaction","isDone","_props$onChange","onBlur","_ref5","otherToolbarOpened","_toolbarOpened","isActive","_props$onChange2","_props$onDone2","useEffect","editorRef","setEditable","nextMarkup","commands","setContent","entries","_ref6","_ref7","key","value","document","documentElement","style","setProperty","editorState","useEditorState","selector","ctx","_ctx$editor","isFocused","sizeStyle","minWidth","width","maxWidth","minHeight","height","maxHeight","valueToSize","createElement","_extends2","StyledEditorContent","showParagraph","separateParagraph","styled","EditorContent","shouldForwardProp","prop","includes","_ref8","display","outline","flex","padding","margin","left","top","color","pointerEvents","whiteSpace","fontSize","marginBottom","_default"],"sources":["../../src/components/EditableHtml.jsx"],"sourcesContent":["import React, { useCallback, useEffect, useMemo, useState } from 'react';\nimport debounce from 'lodash-es/debounce';\nimport { EditorContent, useEditor, useEditorState } from '@tiptap/react';\nimport { styled } from '@mui/material/styles';\nimport StarterKit from '@tiptap/starter-kit';\nimport { TextStyleKit } from '@tiptap/extension-text-style';\nimport { CharacterCount } from '@tiptap/extension-character-count';\nimport SuperScript from '@tiptap/extension-superscript';\nimport SubScript from '@tiptap/extension-subscript';\nimport TextAlign from '@tiptap/extension-text-align';\nimport Image from '@tiptap/extension-image';\nimport Placeholder from '@tiptap/extension-placeholder';\nimport { normalizeInitialMarkup } from '../utils/helper';\n\nimport ExtendedTable from '../extensions/extended-table';\nimport { ExtendedTableCell, ExtendedTableHeader } from '../extensions/extended-table-cell';\nimport { DivNode } from '../extensions/div-node';\nimport { EnsureEmptyRootIsDiv } from '../extensions/ensure-empty-root-div';\nimport { EnsureListItemContentIsDiv } from '../extensions/ensure-list-item-content-is-div';\nimport { TableRow } from '@tiptap/extension-table-row';\nimport {\n DragInTheBlankNode,\n ExplicitConstructedResponseNode,\n InlineDropdownNode,\n MathTemplatedNode,\n ResponseAreaExtension,\n} from '../extensions/responseArea';\nimport { MathNode } from '../extensions/math';\nimport { ImageUploadNode } from '../extensions/image';\nimport { Media } from '../extensions/media';\nimport { CSSMark } from '../extensions/css';\nimport { ExtendedListItem } from '../extensions/extended-list-item';\nimport { HeadingParagraph } from '../extensions/heading-paragraph';\n\nimport EditorContainer from './TiptapContainer';\nimport { valueToSize } from '../utils/size';\nimport { buildExtensions, PLUGINS_MAP } from '../extensions';\n\nconst defaultToolbarOpts = {\n position: 'bottom',\n alignment: 'left',\n alwaysVisible: false,\n showDone: true,\n doneOn: 'blur',\n};\n\nconst defaultResponseAreaProps = {\n options: {},\n respAreaToolbar: () => {},\n onHandleAreaChange: () => {},\n};\n\nconst DEFAULT_ACTIVE_PLUGINS = [\n 'bold',\n 'italic',\n 'underline',\n 'strikethrough',\n 'code',\n 'bulleted-list',\n 'numbered-list',\n 'image',\n 'math',\n 'languageCharacters',\n 'text-align',\n 'table',\n 'video',\n 'audio',\n 'responseArea',\n 'superscript',\n 'subscript',\n 'css',\n 'h3',\n 'undo',\n 'redo',\n];\n\nconst cssVariables = {\n '--white': '#fff',\n '--black': '#2e2b29',\n '--black-contrast': '#110f0e',\n '--gray-1': 'rgba(61, 37, 20, .05)',\n '--gray-2': 'rgba(61, 37, 20, .08)',\n '--gray-3': 'rgba(61, 37, 20, .12)',\n '--gray-4': 'rgba(53, 38, 28, .3)',\n '--gray-5': 'rgba(28, 25, 23, .6)',\n '--green': '#22c55e',\n '--purple': '#6a00f5',\n '--purple-contrast': '#5800cc',\n '--purple-light': 'rgba(88, 5, 255, .05)',\n '--yellow-contrast': '#facc15',\n '--yellow': 'rgba(250, 204, 21, .4)',\n '--yellow-light': '#fffae5',\n '--red': '#ff5c33',\n '--red-light': '#ffebe5',\n '--shadow': `0px 12px 33px 0px rgba(0, 0, 0, .06),\n 0px 3.618px 9.949px 0px rgba(0, 0, 0, .04)`,\n};\n\nexport const EditableHtml = (props) => {\n const { showParagraphs, separateParagraphs } = props.pluginProps || {};\n const [pendingImages, setPendingImages] = useState([]);\n const [scheduled, setScheduled] = useState(false);\n const { toolbarOpts } = props;\n\n const removePendingImage = useCallback(\n (imagePos) => {\n setPendingImages((prev) => {\n const next = prev.filter((img) => img.pos !== imagePos);\n if (next.length === 0) {\n setScheduled(false);\n }\n return next;\n });\n },\n [setPendingImages],\n );\n\n const toolbarOptsToUse = {\n ...defaultToolbarOpts,\n ...toolbarOpts,\n };\n\n const activePluginsToUse = useMemo(() => {\n let { customPlugins, ...otherPluginProps } = props.pluginProps || {};\n\n customPlugins = customPlugins || [];\n\n const filteredActivePlugins = (props.activePlugins || DEFAULT_ACTIVE_PLUGINS)?.filter((pluginName) => {\n const nameToUse = PLUGINS_MAP[pluginName] || pluginName;\n const pluginInfo = otherPluginProps[nameToUse] || {};\n\n return !pluginInfo || !pluginInfo.disabled;\n });\n\n return buildExtensions(filteredActivePlugins, customPlugins, {\n math: {},\n textAlign: props.textAlign,\n html: {},\n extraCSSRules: props.extraCSSRules || {},\n image: {\n ...props.imageSupport,\n },\n toolbar: {},\n table: {},\n responseArea: {\n type: props.responseAreaProps?.type,\n },\n languageCharacters: props.languageCharactersProps,\n keyPadCharacterRef: {},\n setKeypadInteraction: {},\n media: {},\n });\n }, [props]);\n\n const extensions = [\n TextAlign.configure({\n types: ['heading', 'paragraph', 'div', 'headingParagraph', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'td', 'th'],\n alignments: ['left', 'right', 'center', 'justify'],\n }),\n TextStyleKit,\n CharacterCount.configure({\n limit: props.charactersLimit || 1000000,\n }),\n StarterKit.configure({\n trailingNode: {\n node: 'paragraph',\n notAfter: ['paragraph', 'div'],\n },\n }),\n ExtendedListItem,\n DivNode,\n HeadingParagraph,\n EnsureEmptyRootIsDiv,\n EnsureListItemContentIsDiv,\n Placeholder.configure({\n placeholder: props.placeholder,\n // show placeholder even when editor is focused\n showOnlyWhenEditable: true,\n showOnlyCurrent: false, // show on all empty nodes, not only the current one\n includeChildren: true,\n }),\n ExtendedTable,\n TableRow,\n ExtendedTableHeader,\n ExtendedTableCell,\n ResponseAreaExtension.configure(props.responseAreaProps),\n ExplicitConstructedResponseNode.configure(props.responseAreaProps),\n DragInTheBlankNode.configure(props.responseAreaProps),\n InlineDropdownNode.configure(props.responseAreaProps),\n MathTemplatedNode.configure(props.responseAreaProps),\n MathNode.configure({\n toolbarOpts: toolbarOptsToUse,\n math: props.pluginProps?.math || {},\n }),\n SubScript,\n SuperScript,\n Image,\n ImageUploadNode.configure({\n toolbarOpts: toolbarOptsToUse,\n imageHandling: {\n disableImageAlignmentButtons: props.disableImageAlignmentButtons,\n onDone: (editor) => props.onDone?.(editor.getHTML()),\n onDelete:\n props.imageSupport &&\n props.imageSupport.delete &&\n ((node) => {\n const { src } = node.attrs;\n\n props.imageSupport.delete(src, (e) => {\n removePendingImage(node.pos);\n });\n }),\n insertImageRequested:\n props.imageSupport &&\n ((editor, imageInfo, getHandler) => {\n const [addedImage, pos] = imageInfo;\n\n const onFinish = (result) => {\n let cb;\n\n if (scheduled && result) {\n // finish editing only on success\n cb = props.onChange;\n }\n\n removePendingImage(pos);\n cb?.(editor.getHTML());\n };\n\n const callback = () => {\n /**\n * The handler is the object through which the outer context\n * communicates file upload events like: fileChosen, cancel, progress\n */\n const handler = getHandler(onFinish);\n\n // If the user closes the file picker without choosing a file, the window regains\n // focus while _insertingImage is still true — drop the stale pending entry.\n const focusHandler = debounce(() => {\n const detach = () => window.removeEventListener('focus', focusHandler);\n\n if (!editor._insertingImage) {\n detach();\n return;\n }\n\n removePendingImage(pos);\n editor._insertingImage = false;\n detach();\n }, 500);\n\n window.addEventListener('focus', focusHandler);\n\n props.imageSupport.add(handler);\n };\n\n editor._insertingImage = true;\n setPendingImages((prev) => [...prev, addedImage]);\n callback();\n }),\n maxImageWidth: props.maxImageWidth,\n maxImageHeight: props.maxImageHeight,\n },\n limit: 3,\n }),\n Media.configure({\n uploadSoundSupport: props.uploadSoundSupport,\n }),\n CSSMark.configure({\n extraCSSRules: props.extraCSSRules,\n }),\n ];\n\n const editor = useEditor(\n {\n extensions,\n immediatelyRender: false,\n editorProps: {\n handleKeyDown(view, event) {\n if (props.onKeyDown) {\n return props.onKeyDown(event);\n }\n\n // Return false to let default behavior continue\n return false;\n },\n },\n editable: !props.disabled,\n content: normalizeInitialMarkup(props.markup),\n onUpdate: ({ editor, transaction }) => {\n if (transaction.isDone || props.markup !== editor.getHTML()) {\n props.onChange?.(editor.getHTML());\n }\n },\n onBlur: debounce(({ editor }) => {\n const otherToolbarOpened =\n editor._insertingImage ||\n editor._toolbarOpened ||\n editor.isActive('inline_dropdown') ||\n editor.isActive('explicit_constructed_response');\n\n if (otherToolbarOpened) {\n return;\n }\n\n if (props.markup !== editor.getHTML()) {\n props.onChange?.(editor.getHTML());\n }\n\n if (toolbarOptsToUse.doneOn === 'blur') {\n props.onDone?.(editor.getHTML());\n }\n }, 200),\n },\n [props.charactersLimit],\n );\n\n useEffect(() => {\n if (props.editorRef) {\n props.editorRef(editor);\n }\n }, [props.editorRef, editor]);\n\n useEffect(() => {\n editor?.setEditable(!props.disabled);\n }, [props.disabled, editor]);\n\n useEffect(() => {\n if (!editor) {\n return;\n }\n const nextMarkup = normalizeInitialMarkup(props.markup);\n\n if (nextMarkup !== editor.getHTML()) {\n editor.commands.setContent(nextMarkup, false);\n }\n }, [props.markup, editor]);\n\n useEffect(() => {\n Object.entries(cssVariables).forEach(([key, value]) => {\n document.documentElement.style.setProperty(key, value);\n });\n }, []);\n\n const editorState = useEditorState({\n editor,\n selector: (ctx) => ({\n isFocused: ctx.editor?.isFocused,\n }),\n });\n\n const sizeStyle = useMemo(() => {\n const { minWidth, width, maxWidth, minHeight, height, maxHeight } = props;\n\n return {\n width: valueToSize(width),\n minWidth: valueToSize(minWidth),\n maxWidth: valueToSize(maxWidth),\n height: valueToSize(height),\n minHeight: valueToSize(minHeight),\n maxHeight: valueToSize(maxHeight),\n };\n }, [props]);\n\n return (\n <EditorContainer\n {...{\n ...props,\n activePlugins: activePluginsToUse,\n toolbarOpts: toolbarOptsToUse,\n }}\n editorState={editorState}\n editor={editor}\n >\n {editor && (\n <StyledEditorContent\n style={{\n minHeight: sizeStyle.minHeight,\n height: sizeStyle.height,\n maxHeight: sizeStyle.maxHeight,\n }}\n showParagraph={showParagraphs && !showParagraphs.disabled}\n separateParagraph={separateParagraphs && !separateParagraphs.disabled}\n editor={editor}\n />\n )}\n </EditorContainer>\n );\n};\n\nconst StyledEditorContent = styled(EditorContent, {\n shouldForwardProp: (prop) => !['showParagraph', 'separateParagraph'].includes(prop),\n})(({ showParagraph, separateParagraph }) => ({\n display: 'flex',\n outline: 'none !important',\n '& .ProseMirror': {\n flex: 1,\n padding: '5px',\n maxHeight: '500px',\n outline: 'none !important',\n position: 'initial',\n\n // reset default margins for all block paragraphs/divs in the editor\n '& > p, & > div': {\n margin: '0',\n },\n\n // Out of flow so the caret stays at the start of the block; in-flow ::before pushes the caret after the hint text.\n '& p.is-editor-empty, & div.is-editor-empty': {\n position: 'relative',\n },\n '& p.is-editor-empty::before, & div.is-editor-empty::before': {\n content: 'attr(data-placeholder)',\n position: 'absolute',\n left: 0,\n top: 0,\n color: '#9CA3AF',\n pointerEvents: 'none',\n whiteSpace: 'pre-wrap',\n },\n\n ...(showParagraph && {\n '& > p:has(+ p)::after, & > div:has(+ div)::after': {\n display: 'block',\n content: '\"¶\"',\n fontSize: '1em',\n color: '#146EB3',\n },\n }),\n ...(separateParagraph && {\n '& > div:has(+ div)': {\n marginBottom: '1em',\n },\n }),\n },\n}));\n\nexport default EditableHtml;\n"],"mappings":";;;;;;;;;;;;;AAAA,IAAAA,MAAA,GAAAC,uBAAA,CAAAC,OAAA;AACA,IAAAC,SAAA,GAAAC,sBAAA,CAAAF,OAAA;AACA,IAAAG,OAAA,GAAAH,OAAA;AACA,IAAAI,OAAA,GAAAJ,OAAA;AACA,IAAAK,WAAA,GAAAH,sBAAA,CAAAF,OAAA;AACA,IAAAM,mBAAA,GAAAN,OAAA;AACA,IAAAO,wBAAA,GAAAP,OAAA;AACA,IAAAQ,qBAAA,GAAAN,sBAAA,CAAAF,OAAA;AACA,IAAAS,mBAAA,GAAAP,sBAAA,CAAAF,OAAA;AACA,IAAAU,mBAAA,GAAAR,sBAAA,CAAAF,OAAA;AACA,IAAAW,eAAA,GAAAT,sBAAA,CAAAF,OAAA;AACA,IAAAY,qBAAA,GAAAV,sBAAA,CAAAF,OAAA;AACA,IAAAa,OAAA,GAAAb,OAAA;AAEA,IAAAc,cAAA,GAAAZ,sBAAA,CAAAF,OAAA;AACA,IAAAe,kBAAA,GAAAf,OAAA;AACA,IAAAgB,QAAA,GAAAhB,OAAA;AACA,IAAAiB,mBAAA,GAAAjB,OAAA;AACA,IAAAkB,2BAAA,GAAAlB,OAAA;AACA,IAAAmB,kBAAA,GAAAnB,OAAA;AACA,IAAAoB,aAAA,GAAApB,OAAA;AAOA,IAAAqB,KAAA,GAAArB,OAAA;AACA,IAAAsB,MAAA,GAAAtB,OAAA;AACA,IAAAuB,MAAA,GAAAvB,OAAA;AACA,IAAAwB,IAAA,GAAAxB,OAAA;AACA,IAAAyB,iBAAA,GAAAzB,OAAA;AACA,IAAA0B,iBAAA,GAAA1B,OAAA;AAEA,IAAA2B,gBAAA,GAAAzB,sBAAA,CAAAF,OAAA;AACA,IAAA4B,KAAA,GAAA5B,OAAA;AACA,IAAA6B,WAAA,GAAA7B,OAAA;AAA6D,IAAA8B,SAAA;AAAA,SAAA/B,wBAAAgC,CAAA,EAAAC,CAAA,6BAAAC,OAAA,MAAAC,CAAA,OAAAD,OAAA,IAAAE,CAAA,OAAAF,OAAA,YAAAlC,uBAAA,YAAAA,wBAAAgC,CAAA,EAAAC,CAAA,SAAAA,CAAA,IAAAD,CAAA,IAAAA,CAAA,CAAAK,UAAA,SAAAL,CAAA,MAAAM,CAAA,EAAAC,CAAA,EAAAC,CAAA,KAAAC,SAAA,mBAAAT,CAAA,iBAAAA,CAAA,gBAAAU,OAAA,CAAAV,CAAA,0BAAAA,CAAA,SAAAQ,CAAA,MAAAF,CAAA,GAAAL,CAAA,GAAAG,CAAA,GAAAD,CAAA,QAAAG,CAAA,CAAAK,GAAA,CAAAX,CAAA,UAAAM,CAAA,CAAAM,GAAA,CAAAZ,CAAA,GAAAM,CAAA,CAAAO,GAAA,CAAAb,CAAA,EAAAQ,CAAA,cAAAM,EAAA,IAAAd,CAAA,gBAAAc,EAAA,OAAAC,cAAA,CAAAC,IAAA,CAAAhB,CAAA,EAAAc,EAAA,OAAAP,CAAA,IAAAD,CAAA,GAAAW,MAAA,CAAAC,cAAA,KAAAD,MAAA,CAAAE,wBAAA,CAAAnB,CAAA,EAAAc,EAAA,OAAAP,CAAA,CAAAK,GAAA,IAAAL,CAAA,CAAAM,GAAA,IAAAP,CAAA,CAAAE,CAAA,EAAAM,EAAA,EAAAP,CAAA,IAAAC,CAAA,CAAAM,EAAA,IAAAd,CAAA,CAAAc,EAAA,WAAAN,CAAA,KAAAR,CAAA,EAAAC,CAAA;AAAA,SAAAmB,QAAApB,CAAA,EAAAG,CAAA,QAAAF,CAAA,GAAAgB,MAAA,CAAAI,IAAA,CAAArB,CAAA,OAAAiB,MAAA,CAAAK,qBAAA,QAAAhB,CAAA,GAAAW,MAAA,CAAAK,qBAAA,CAAAtB,CAAA,GAAAG,CAAA,KAAAG,CAAA,GAAAA,CAAA,CAAAiB,MAAA,WAAApB,CAAA,WAAAc,MAAA,CAAAE,wBAAA,CAAAnB,CAAA,EAAAG,CAAA,EAAAqB,UAAA,OAAAvB,CAAA,CAAAwB,IAAA,CAAAC,KAAA,CAAAzB,CAAA,EAAAK,CAAA,YAAAL,CAAA;AAAA,SAAA0B,cAAA3B,CAAA,aAAAG,CAAA,MAAAA,CAAA,GAAAyB,SAAA,CAAAC,MAAA,EAAA1B,CAAA,UAAAF,CAAA,WAAA2B,SAAA,CAAAzB,CAAA,IAAAyB,SAAA,CAAAzB,CAAA,QAAAA,CAAA,OAAAiB,OAAA,CAAAH,MAAA,CAAAhB,CAAA,OAAA6B,OAAA,WAAA3B,CAAA,QAAA4B,gBAAA,aAAA/B,CAAA,EAAAG,CAAA,EAAAF,CAAA,CAAAE,CAAA,SAAAc,MAAA,CAAAe,yBAAA,GAAAf,MAAA,CAAAgB,gBAAA,CAAAjC,CAAA,EAAAiB,MAAA,CAAAe,yBAAA,CAAA/B,CAAA,KAAAmB,OAAA,CAAAH,MAAA,CAAAhB,CAAA,GAAA6B,OAAA,WAAA3B,CAAA,IAAAc,MAAA,CAAAC,cAAA,CAAAlB,CAAA,EAAAG,CAAA,EAAAc,MAAA,CAAAE,wBAAA,CAAAlB,CAAA,EAAAE,CAAA,iBAAAH,CAAA;AAE7D,IAAMkC,kBAAkB,GAAG;EACzBC,QAAQ,EAAE,QAAQ;EAClBC,SAAS,EAAE,MAAM;EACjBC,aAAa,EAAE,KAAK;EACpBC,QAAQ,EAAE,IAAI;EACdC,MAAM,EAAE;AACV,CAAC;AAED,IAAMC,wBAAwB,GAAG;EAC/BC,OAAO,EAAE,CAAC,CAAC;EACXC,eAAe,EAAE,SAAjBA,eAAeA,CAAA,EAAQ,CAAC,CAAC;EACzBC,kBAAkB,EAAE,SAApBA,kBAAkBA,CAAA,EAAQ,CAAC;AAC7B,CAAC;AAED,IAAMC,sBAAsB,GAAG,CAC7B,MAAM,EACN,QAAQ,EACR,WAAW,EACX,eAAe,EACf,MAAM,EACN,eAAe,EACf,eAAe,EACf,OAAO,EACP,MAAM,EACN,oBAAoB,EACpB,YAAY,EACZ,OAAO,EACP,OAAO,EACP,OAAO,EACP,cAAc,EACd,aAAa,EACb,WAAW,EACX,KAAK,EACL,IAAI,EACJ,MAAM,EACN,MAAM,CACP;AAED,IAAMC,YAAY,GAAG;EACnB,SAAS,EAAE,MAAM;EACjB,SAAS,EAAE,SAAS;EACpB,kBAAkB,EAAE,SAAS;EAC7B,UAAU,EAAE,uBAAuB;EACnC,UAAU,EAAE,uBAAuB;EACnC,UAAU,EAAE,uBAAuB;EACnC,UAAU,EAAE,sBAAsB;EAClC,UAAU,EAAE,sBAAsB;EAClC,SAAS,EAAE,SAAS;EACpB,UAAU,EAAE,SAAS;EACrB,mBAAmB,EAAE,SAAS;EAC9B,gBAAgB,EAAE,uBAAuB;EACzC,mBAAmB,EAAE,SAAS;EAC9B,UAAU,EAAE,wBAAwB;EACpC,gBAAgB,EAAE,SAAS;EAC3B,OAAO,EAAE,SAAS;EAClB,aAAa,EAAE,SAAS;EACxB,UAAU;AAEZ,CAAC;AAEM,IAAMC,YAAY,GAAAC,OAAA,CAAAD,YAAA,GAAG,SAAfA,YAAYA,CAAIE,KAAK,EAAK;EAAA,IAAAC,kBAAA;EACrC,IAAAC,IAAA,GAA+CF,KAAK,CAACG,WAAW,IAAI,CAAC,CAAC;IAA9DC,cAAc,GAAAF,IAAA,CAAdE,cAAc;IAAEC,kBAAkB,GAAAH,IAAA,CAAlBG,kBAAkB;EAC1C,IAAAC,SAAA,GAA0C,IAAAC,eAAQ,EAAC,EAAE,CAAC;IAAAC,UAAA,OAAAC,eAAA,aAAAH,SAAA;IAA/CI,aAAa,GAAAF,UAAA;IAAEG,gBAAgB,GAAAH,UAAA;EACtC,IAAAI,UAAA,GAAkC,IAAAL,eAAQ,EAAC,KAAK,CAAC;IAAAM,UAAA,OAAAJ,eAAA,aAAAG,UAAA;IAA1CE,SAAS,GAAAD,UAAA;IAAEE,YAAY,GAAAF,UAAA;EAC9B,IAAQG,WAAW,GAAKhB,KAAK,CAArBgB,WAAW;EAEnB,IAAMC,kBAAkB,GAAG,IAAAC,kBAAW,EACpC,UAACC,QAAQ,EAAK;IACZR,gBAAgB,CAAC,UAACS,IAAI,EAAK;MACzB,IAAMC,IAAI,GAAGD,IAAI,CAAC7C,MAAM,CAAC,UAAC+C,GAAG;QAAA,OAAKA,GAAG,CAACC,GAAG,KAAKJ,QAAQ;MAAA,EAAC;MACvD,IAAIE,IAAI,CAACxC,MAAM,KAAK,CAAC,EAAE;QACrBkC,YAAY,CAAC,KAAK,CAAC;MACrB;MACA,OAAOM,IAAI;IACb,CAAC,CAAC;EACJ,CAAC,EACD,CAACV,gBAAgB,CACnB,CAAC;EAED,IAAMa,gBAAgB,GAAA7C,aAAA,CAAAA,aAAA,KACjBO,kBAAkB,GAClB8B,WAAW,CACf;EAED,IAAMS,kBAAkB,GAAG,IAAAC,cAAO,EAAC,YAAM;IAAA,IAAAC,KAAA,EAAAC,qBAAA;IACvC,IAAAC,KAAA,GAA6C7B,KAAK,CAACG,WAAW,IAAI,CAAC,CAAC;MAA9D2B,aAAa,GAAAD,KAAA,CAAbC,aAAa;MAAKC,gBAAgB,OAAAC,yBAAA,aAAAH,KAAA,EAAA9E,SAAA;IAExC+E,aAAa,GAAGA,aAAa,IAAI,EAAE;IAEnC,IAAMG,qBAAqB,IAAAN,KAAA,GAAI3B,KAAK,CAACkC,aAAa,IAAItC,sBAAsB,cAAA+B,KAAA,uBAA9CA,KAAA,CAAiDpD,MAAM,CAAC,UAAC4D,UAAU,EAAK;MACpG,IAAMC,SAAS,GAAGC,uBAAW,CAACF,UAAU,CAAC,IAAIA,UAAU;MACvD,IAAMG,UAAU,GAAGP,gBAAgB,CAACK,SAAS,CAAC,IAAI,CAAC,CAAC;MAEpD,OAAO,CAACE,UAAU,IAAI,CAACA,UAAU,CAACC,QAAQ;IAC5C,CAAC,CAAC;IAEF,OAAO,IAAAC,2BAAe,EAACP,qBAAqB,EAAEH,aAAa,EAAE;MAC3DW,IAAI,EAAE,CAAC,CAAC;MACRC,SAAS,EAAE1C,KAAK,CAAC0C,SAAS;MAC1BC,IAAI,EAAE,CAAC,CAAC;MACRC,aAAa,EAAE5C,KAAK,CAAC4C,aAAa,IAAI,CAAC,CAAC;MACxCC,KAAK,EAAAlE,aAAA,KACAqB,KAAK,CAAC8C,YAAY,CACtB;MACDC,OAAO,EAAE,CAAC,CAAC;MACXC,KAAK,EAAE,CAAC,CAAC;MACTC,YAAY,EAAE;QACZC,IAAI,GAAAtB,qBAAA,GAAE5B,KAAK,CAACmD,iBAAiB,cAAAvB,qBAAA,uBAAvBA,qBAAA,CAAyBsB;MACjC,CAAC;MACDE,kBAAkB,EAAEpD,KAAK,CAACqD,uBAAuB;MACjDC,kBAAkB,EAAE,CAAC,CAAC;MACtBC,oBAAoB,EAAE,CAAC,CAAC;MACxBC,KAAK,EAAE,CAAC;IACV,CAAC,CAAC;EACJ,CAAC,EAAE,CAACxD,KAAK,CAAC,CAAC;EAEX,IAAMyD,UAAU,GAAG,CACjBC,8BAAS,CAACC,SAAS,CAAC;IAClBC,KAAK,EAAE,CAAC,SAAS,EAAE,WAAW,EAAE,KAAK,EAAE,kBAAkB,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;IAC1GC,UAAU,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS;EACnD,CAAC,CAAC,EACFC,gCAAY,EACZC,uCAAc,CAACJ,SAAS,CAAC;IACvBK,KAAK,EAAEhE,KAAK,CAACiE,eAAe,IAAI;EAClC,CAAC,CAAC,EACFC,sBAAU,CAACP,SAAS,CAAC;IACnBQ,YAAY,EAAE;MACZC,IAAI,EAAE,WAAW;MACjBC,QAAQ,EAAE,CAAC,WAAW,EAAE,KAAK;IAC/B;EACF,CAAC,CAAC,EACFC,kCAAgB,EAChBC,gBAAO,EACPC,kCAAgB,EAChBC,wCAAoB,EACpBC,sDAA0B,EAC1BC,gCAAW,CAAChB,SAAS,CAAC;IACpBiB,WAAW,EAAE5E,KAAK,CAAC4E,WAAW;IAC9B;IACAC,oBAAoB,EAAE,IAAI;IAC1BC,eAAe,EAAE,KAAK;IAAE;IACxBC,eAAe,EAAE;EACnB,CAAC,CAAC,EACFC,yBAAa,EACbC,2BAAQ,EACRC,sCAAmB,EACnBC,oCAAiB,EACjBC,mCAAqB,CAACzB,SAAS,CAAC3D,KAAK,CAACmD,iBAAiB,CAAC,EACxDkC,6CAA+B,CAAC1B,SAAS,CAAC3D,KAAK,CAACmD,iBAAiB,CAAC,EAClEmC,gCAAkB,CAAC3B,SAAS,CAAC3D,KAAK,CAACmD,iBAAiB,CAAC,EACrDoC,gCAAkB,CAAC5B,SAAS,CAAC3D,KAAK,CAACmD,iBAAiB,CAAC,EACrDqC,+BAAiB,CAAC7B,SAAS,CAAC3D,KAAK,CAACmD,iBAAiB,CAAC,EACpDsC,cAAQ,CAAC9B,SAAS,CAAC;IACjB3C,WAAW,EAAEQ,gBAAgB;IAC7BiB,IAAI,EAAE,EAAAxC,kBAAA,GAAAD,KAAK,CAACG,WAAW,cAAAF,kBAAA,uBAAjBA,kBAAA,CAAmBwC,IAAI,KAAI,CAAC;EACpC,CAAC,CAAC,EACFiD,8BAAS,EACTC,gCAAW,EACXC,0BAAK,EACLC,sBAAe,CAAClC,SAAS,CAAC;IACxB3C,WAAW,EAAEQ,gBAAgB;IAC7BsE,aAAa,EAAE;MACbC,4BAA4B,EAAE/F,KAAK,CAAC+F,4BAA4B;MAChEC,MAAM,EAAE,SAARA,MAAMA,CAAGC,MAAM;QAAA,IAAAC,aAAA;QAAA,QAAAA,aAAA,GAAKlG,KAAK,CAACgG,MAAM,cAAAE,aAAA,uBAAZA,aAAA,CAAAlI,IAAA,CAAAgC,KAAK,EAAUiG,MAAM,CAACE,OAAO,CAAC,CAAC,CAAC;MAAA;MACpDC,QAAQ,EACNpG,KAAK,CAAC8C,YAAY,IAClB9C,KAAK,CAAC8C,YAAY,UAAO,IACxB,UAACsB,IAAI,EAAK;QACT,IAAQiC,GAAG,GAAKjC,IAAI,CAACkC,KAAK,CAAlBD,GAAG;QAEXrG,KAAK,CAAC8C,YAAY,UAAO,CAACuD,GAAG,EAAE,UAACrJ,CAAC,EAAK;UACpCiE,kBAAkB,CAACmD,IAAI,CAAC7C,GAAG,CAAC;QAC9B,CAAC,CAAC;MACJ,CAAE;MACJgF,oBAAoB,EAClBvG,KAAK,CAAC8C,YAAY,IACjB,UAACmD,MAAM,EAAEO,SAAS,EAAEC,UAAU,EAAK;QAClC,IAAAC,UAAA,OAAAjG,eAAA,aAA0B+F,SAAS;UAA5BG,UAAU,GAAAD,UAAA;UAAEnF,GAAG,GAAAmF,UAAA;QAEtB,IAAME,QAAQ,GAAG,SAAXA,QAAQA,CAAIC,MAAM,EAAK;UAAA,IAAAC,GAAA;UAC3B,IAAIC,EAAE;UAEN,IAAIjG,SAAS,IAAI+F,MAAM,EAAE;YACvB;YACAE,EAAE,GAAG/G,KAAK,CAACgH,QAAQ;UACrB;UAEA/F,kBAAkB,CAACM,GAAG,CAAC;UACvB,CAAAuF,GAAA,GAAAC,EAAE,cAAAD,GAAA,eAAFA,GAAA,CAAKb,MAAM,CAACE,OAAO,CAAC,CAAC,CAAC;QACxB,CAAC;QAED,IAAMc,QAAQ,GAAG,SAAXA,QAAQA,CAAA,EAAS;UACrB;AACd;AACA;AACA;UACc,IAAMC,OAAO,GAAGT,UAAU,CAACG,QAAQ,CAAC;;UAEpC;UACA;UACA,IAAMO,YAAY,GAAG,IAAAC,oBAAQ,EAAC,YAAM;YAClC,IAAMC,MAAM,GAAG,SAATA,MAAMA,CAAA;cAAA,OAASC,MAAM,CAACC,mBAAmB,CAAC,OAAO,EAAEJ,YAAY,CAAC;YAAA;YAEtE,IAAI,CAAClB,MAAM,CAACuB,eAAe,EAAE;cAC3BH,MAAM,CAAC,CAAC;cACR;YACF;YAEApG,kBAAkB,CAACM,GAAG,CAAC;YACvB0E,MAAM,CAACuB,eAAe,GAAG,KAAK;YAC9BH,MAAM,CAAC,CAAC;UACV,CAAC,EAAE,GAAG,CAAC;UAEPC,MAAM,CAACG,gBAAgB,CAAC,OAAO,EAAEN,YAAY,CAAC;UAE9CnH,KAAK,CAAC8C,YAAY,CAAC4E,GAAG,CAACR,OAAO,CAAC;QACjC,CAAC;QAEDjB,MAAM,CAACuB,eAAe,GAAG,IAAI;QAC7B7G,gBAAgB,CAAC,UAACS,IAAI;UAAA,UAAAuG,MAAA,KAAAC,mBAAA,aAASxG,IAAI,IAAEuF,UAAU;QAAA,CAAC,CAAC;QACjDM,QAAQ,CAAC,CAAC;MACZ,CAAE;MACJY,aAAa,EAAE7H,KAAK,CAAC6H,aAAa;MAClCC,cAAc,EAAE9H,KAAK,CAAC8H;IACxB,CAAC;IACD9D,KAAK,EAAE;EACT,CAAC,CAAC,EACF+D,YAAK,CAACpE,SAAS,CAAC;IACdqE,kBAAkB,EAAEhI,KAAK,CAACgI;EAC5B,CAAC,CAAC,EACFC,YAAO,CAACtE,SAAS,CAAC;IAChBf,aAAa,EAAE5C,KAAK,CAAC4C;EACvB,CAAC,CAAC,CACH;EAED,IAAMqD,MAAM,GAAG,IAAAiC,iBAAS,EACtB;IACEzE,UAAU,EAAVA,UAAU;IACV0E,iBAAiB,EAAE,KAAK;IACxBC,WAAW,EAAE;MACXC,aAAa,WAAbA,aAAaA,CAACC,IAAI,EAAEC,KAAK,EAAE;QACzB,IAAIvI,KAAK,CAACwI,SAAS,EAAE;UACnB,OAAOxI,KAAK,CAACwI,SAAS,CAACD,KAAK,CAAC;QAC/B;;QAEA;QACA,OAAO,KAAK;MACd;IACF,CAAC;IACDE,QAAQ,EAAE,CAACzI,KAAK,CAACuC,QAAQ;IACzBmG,OAAO,EAAE,IAAAC,8BAAsB,EAAC3I,KAAK,CAAC4I,MAAM,CAAC;IAC7CC,QAAQ,EAAE,SAAVA,QAAQA,CAAAC,KAAA,EAA+B;MAAA,IAA1B7C,MAAM,GAAA6C,KAAA,CAAN7C,MAAM;QAAE8C,WAAW,GAAAD,KAAA,CAAXC,WAAW;MAC9B,IAAIA,WAAW,CAACC,MAAM,IAAIhJ,KAAK,CAAC4I,MAAM,KAAK3C,MAAM,CAACE,OAAO,CAAC,CAAC,EAAE;QAAA,IAAA8C,eAAA;QAC3D,CAAAA,eAAA,GAAAjJ,KAAK,CAACgH,QAAQ,cAAAiC,eAAA,eAAdA,eAAA,CAAAjL,IAAA,CAAAgC,KAAK,EAAYiG,MAAM,CAACE,OAAO,CAAC,CAAC,CAAC;MACpC;IACF,CAAC;IACD+C,MAAM,EAAE,IAAA9B,oBAAQ,EAAC,UAAA+B,KAAA,EAAgB;MAAA,IAAblD,MAAM,GAAAkD,KAAA,CAANlD,MAAM;MACxB,IAAMmD,kBAAkB,GACtBnD,MAAM,CAACuB,eAAe,IACtBvB,MAAM,CAACoD,cAAc,IACrBpD,MAAM,CAACqD,QAAQ,CAAC,iBAAiB,CAAC,IAClCrD,MAAM,CAACqD,QAAQ,CAAC,+BAA+B,CAAC;MAElD,IAAIF,kBAAkB,EAAE;QACtB;MACF;MAEA,IAAIpJ,KAAK,CAAC4I,MAAM,KAAK3C,MAAM,CAACE,OAAO,CAAC,CAAC,EAAE;QAAA,IAAAoD,gBAAA;QACrC,CAAAA,gBAAA,GAAAvJ,KAAK,CAACgH,QAAQ,cAAAuC,gBAAA,eAAdA,gBAAA,CAAAvL,IAAA,CAAAgC,KAAK,EAAYiG,MAAM,CAACE,OAAO,CAAC,CAAC,CAAC;MACpC;MAEA,IAAI3E,gBAAgB,CAACjC,MAAM,KAAK,MAAM,EAAE;QAAA,IAAAiK,cAAA;QACtC,CAAAA,cAAA,GAAAxJ,KAAK,CAACgG,MAAM,cAAAwD,cAAA,eAAZA,cAAA,CAAAxL,IAAA,CAAAgC,KAAK,EAAUiG,MAAM,CAACE,OAAO,CAAC,CAAC,CAAC;MAClC;IACF,CAAC,EAAE,GAAG;EACR,CAAC,EACD,CAACnG,KAAK,CAACiE,eAAe,CACxB,CAAC;EAED,IAAAwF,gBAAS,EAAC,YAAM;IACd,IAAIzJ,KAAK,CAAC0J,SAAS,EAAE;MACnB1J,KAAK,CAAC0J,SAAS,CAACzD,MAAM,CAAC;IACzB;EACF,CAAC,EAAE,CAACjG,KAAK,CAAC0J,SAAS,EAAEzD,MAAM,CAAC,CAAC;EAE7B,IAAAwD,gBAAS,EAAC,YAAM;IACdxD,MAAM,aAANA,MAAM,eAANA,MAAM,CAAE0D,WAAW,CAAC,CAAC3J,KAAK,CAACuC,QAAQ,CAAC;EACtC,CAAC,EAAE,CAACvC,KAAK,CAACuC,QAAQ,EAAE0D,MAAM,CAAC,CAAC;EAE5B,IAAAwD,gBAAS,EAAC,YAAM;IACd,IAAI,CAACxD,MAAM,EAAE;MACX;IACF;IACA,IAAM2D,UAAU,GAAG,IAAAjB,8BAAsB,EAAC3I,KAAK,CAAC4I,MAAM,CAAC;IAEvD,IAAIgB,UAAU,KAAK3D,MAAM,CAACE,OAAO,CAAC,CAAC,EAAE;MACnCF,MAAM,CAAC4D,QAAQ,CAACC,UAAU,CAACF,UAAU,EAAE,KAAK,CAAC;IAC/C;EACF,CAAC,EAAE,CAAC5J,KAAK,CAAC4I,MAAM,EAAE3C,MAAM,CAAC,CAAC;EAE1B,IAAAwD,gBAAS,EAAC,YAAM;IACdxL,MAAM,CAAC8L,OAAO,CAAClK,YAAY,CAAC,CAACf,OAAO,CAAC,UAAAkL,KAAA,EAAkB;MAAA,IAAAC,KAAA,OAAAxJ,eAAA,aAAAuJ,KAAA;QAAhBE,GAAG,GAAAD,KAAA;QAAEE,KAAK,GAAAF,KAAA;MAC/CG,QAAQ,CAACC,eAAe,CAACC,KAAK,CAACC,WAAW,CAACL,GAAG,EAAEC,KAAK,CAAC;IACxD,CAAC,CAAC;EACJ,CAAC,EAAE,EAAE,CAAC;EAEN,IAAMK,WAAW,GAAG,IAAAC,sBAAc,EAAC;IACjCxE,MAAM,EAANA,MAAM;IACNyE,QAAQ,EAAE,SAAVA,QAAQA,CAAGC,GAAG;MAAA,IAAAC,WAAA;MAAA,OAAM;QAClBC,SAAS,GAAAD,WAAA,GAAED,GAAG,CAAC1E,MAAM,cAAA2E,WAAA,uBAAVA,WAAA,CAAYC;MACzB,CAAC;IAAA;EACH,CAAC,CAAC;EAEF,IAAMC,SAAS,GAAG,IAAApJ,cAAO,EAAC,YAAM;IAC9B,IAAQqJ,QAAQ,GAAoD/K,KAAK,CAAjE+K,QAAQ;MAAEC,KAAK,GAA6ChL,KAAK,CAAvDgL,KAAK;MAAEC,QAAQ,GAAmCjL,KAAK,CAAhDiL,QAAQ;MAAEC,SAAS,GAAwBlL,KAAK,CAAtCkL,SAAS;MAAEC,MAAM,GAAgBnL,KAAK,CAA3BmL,MAAM;MAAEC,SAAS,GAAKpL,KAAK,CAAnBoL,SAAS;IAE/D,OAAO;MACLJ,KAAK,EAAE,IAAAK,iBAAW,EAACL,KAAK,CAAC;MACzBD,QAAQ,EAAE,IAAAM,iBAAW,EAACN,QAAQ,CAAC;MAC/BE,QAAQ,EAAE,IAAAI,iBAAW,EAACJ,QAAQ,CAAC;MAC/BE,MAAM,EAAE,IAAAE,iBAAW,EAACF,MAAM,CAAC;MAC3BD,SAAS,EAAE,IAAAG,iBAAW,EAACH,SAAS,CAAC;MACjCE,SAAS,EAAE,IAAAC,iBAAW,EAACD,SAAS;IAClC,CAAC;EACH,CAAC,EAAE,CAACpL,KAAK,CAAC,CAAC;EAEX,oBACEjF,MAAA,YAAAuQ,aAAA,CAAC1O,gBAAA,WAAe,MAAA2O,SAAA,iBAAA5M,aAAA,CAAAA,aAAA,KAETqB,KAAK;IACRkC,aAAa,EAAET,kBAAkB;IACjCT,WAAW,EAAEQ;EAAgB;IAE/BgJ,WAAW,EAAEA,WAAY;IACzBvE,MAAM,EAAEA;EAAO,IAEdA,MAAM,iBACLlL,MAAA,YAAAuQ,aAAA,CAACE,mBAAmB;IAClBlB,KAAK,EAAE;MACLY,SAAS,EAAEJ,SAAS,CAACI,SAAS;MAC9BC,MAAM,EAAEL,SAAS,CAACK,MAAM;MACxBC,SAAS,EAAEN,SAAS,CAACM;IACvB,CAAE;IACFK,aAAa,EAAErL,cAAc,IAAI,CAACA,cAAc,CAACmC,QAAS;IAC1DmJ,iBAAiB,EAAErL,kBAAkB,IAAI,CAACA,kBAAkB,CAACkC,QAAS;IACtE0D,MAAM,EAAEA;EAAO,CAChB,CAEY,CAAC;AAEtB,CAAC;AAED,IAAMuF,mBAAmB,GAAG,IAAAG,cAAM,EAACC,qBAAa,EAAE;EAChDC,iBAAiB,EAAE,SAAnBA,iBAAiBA,CAAGC,IAAI;IAAA,OAAK,CAAC,CAAC,eAAe,EAAE,mBAAmB,CAAC,CAACC,QAAQ,CAACD,IAAI,CAAC;EAAA;AACrF,CAAC,CAAC,CAAC,UAAAE,KAAA;EAAA,IAAGP,aAAa,GAAAO,KAAA,CAAbP,aAAa;IAAEC,iBAAiB,GAAAM,KAAA,CAAjBN,iBAAiB;EAAA,OAAQ;IAC5CO,OAAO,EAAE,MAAM;IACfC,OAAO,EAAE,iBAAiB;IAC1B,gBAAgB,EAAAvN,aAAA,CAAAA,aAAA;MACdwN,IAAI,EAAE,CAAC;MACPC,OAAO,EAAE,KAAK;MACdhB,SAAS,EAAE,OAAO;MAClBc,OAAO,EAAE,iBAAiB;MAC1B/M,QAAQ,EAAE,SAAS;MAEnB;MACA,gBAAgB,EAAE;QAChBkN,MAAM,EAAE;MACV,CAAC;MAED;MACA,4CAA4C,EAAE;QAC5ClN,QAAQ,EAAE;MACZ,CAAC;MACD,4DAA4D,EAAE;QAC5DuJ,OAAO,EAAE,wBAAwB;QACjCvJ,QAAQ,EAAE,UAAU;QACpBmN,IAAI,EAAE,CAAC;QACPC,GAAG,EAAE,CAAC;QACNC,KAAK,EAAE,SAAS;QAChBC,aAAa,EAAE,MAAM;QACrBC,UAAU,EAAE;MACd;IAAC,GAEGjB,aAAa,IAAI;MACnB,kDAAkD,EAAE;QAClDQ,OAAO,EAAE,OAAO;QAChBvD,OAAO,EAAE,KAAK;QACdiE,QAAQ,EAAE,KAAK;QACfH,KAAK,EAAE;MACT;IACF,CAAC,GACGd,iBAAiB,IAAI;MACvB,oBAAoB,EAAE;QACpBkB,YAAY,EAAE;MAChB;IACF,CAAC;EAEL,CAAC;AAAA,CAAC,CAAC;AAAC,IAAAC,QAAA,GAAA9M,OAAA,cAEWD,YAAY","ignoreList":[]}
|
package/lib/utils/helper.js
CHANGED
|
@@ -7,11 +7,67 @@ exports.normalizeInitialMarkup = void 0;
|
|
|
7
7
|
var escapeHtml = function escapeHtml(str) {
|
|
8
8
|
return String(str).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''');
|
|
9
9
|
};
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Converts consecutive div elements into a single paragraph with line breaks.
|
|
13
|
+
* Example: "<div>A</div><div>B</div>" becomes "<p>A<br>B</p>"
|
|
14
|
+
*/
|
|
15
|
+
var convertConsecutiveDivsToParagraph = function convertConsecutiveDivsToParagraph(html) {
|
|
16
|
+
// Create a temporary element to parse the HTML
|
|
17
|
+
var temp = document.createElement('div');
|
|
18
|
+
temp.innerHTML = html;
|
|
19
|
+
|
|
20
|
+
// Get all top-level children
|
|
21
|
+
var children = Array.from(temp.children);
|
|
22
|
+
|
|
23
|
+
// Only convert if there are 2 or more divs
|
|
24
|
+
if (children.length < 2) {
|
|
25
|
+
return html;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Check if all children are divs with simple content (text or inline elements)
|
|
29
|
+
var allDivs = children.every(function (child) {
|
|
30
|
+
return child.tagName === 'DIV';
|
|
31
|
+
});
|
|
32
|
+
if (!allDivs) {
|
|
33
|
+
return html;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Check if divs have no attributes (only convert plain divs)
|
|
37
|
+
var hasNoAttributes = children.every(function (div) {
|
|
38
|
+
return div.attributes.length === 0;
|
|
39
|
+
});
|
|
40
|
+
if (!hasNoAttributes) {
|
|
41
|
+
return html;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Check if divs contain only simple content (no nested block elements)
|
|
45
|
+
var hasOnlySimpleContent = children.every(function (div) {
|
|
46
|
+
return Array.from(div.children).every(function (child) {
|
|
47
|
+
var tag = child.tagName;
|
|
48
|
+
// Allow inline elements and br tags
|
|
49
|
+
return ['SPAN', 'B', 'I', 'EM', 'STRONG', 'U', 'SUB', 'SUP', 'A', 'CODE', 'BR'].includes(tag);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
if (!hasOnlySimpleContent) {
|
|
53
|
+
return html;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Convert to paragraph with br tags
|
|
57
|
+
var contents = children.map(function (div) {
|
|
58
|
+
return div.innerHTML;
|
|
59
|
+
});
|
|
60
|
+
return "<p>".concat(contents.join('<br>'), "</p>");
|
|
61
|
+
};
|
|
10
62
|
var normalizeInitialMarkup = exports.normalizeInitialMarkup = function normalizeInitialMarkup(markup) {
|
|
11
63
|
var trimmed = String(markup !== null && markup !== void 0 ? markup : '').trim();
|
|
12
64
|
if (!trimmed) return '<div></div>';
|
|
13
65
|
var looksLikeHtml = /<[^>]+>/.test(trimmed);
|
|
14
|
-
if (looksLikeHtml)
|
|
15
|
-
|
|
66
|
+
if (!looksLikeHtml) {
|
|
67
|
+
return "<div>".concat(escapeHtml(trimmed), "</div>");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Apply the div-to-paragraph transformation
|
|
71
|
+
return convertConsecutiveDivsToParagraph(trimmed);
|
|
16
72
|
};
|
|
17
73
|
//# sourceMappingURL=helper.js.map
|
package/lib/utils/helper.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helper.js","names":["escapeHtml","str","String","replace","normalizeInitialMarkup","exports","markup","trimmed","trim","looksLikeHtml","test"
|
|
1
|
+
{"version":3,"file":"helper.js","names":["escapeHtml","str","String","replace","convertConsecutiveDivsToParagraph","html","temp","document","createElement","innerHTML","children","Array","from","length","allDivs","every","child","tagName","hasNoAttributes","div","attributes","hasOnlySimpleContent","tag","includes","contents","map","concat","join","normalizeInitialMarkup","exports","markup","trimmed","trim","looksLikeHtml","test"],"sources":["../../src/utils/helper.js"],"sourcesContent":["const escapeHtml = (str) =>\n String(str)\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n\n/**\n * Converts consecutive div elements into a single paragraph with line breaks.\n * Example: \"<div>A</div><div>B</div>\" becomes \"<p>A<br>B</p>\"\n */\nconst convertConsecutiveDivsToParagraph = (html) => {\n // Create a temporary element to parse the HTML\n const temp = document.createElement('div');\n temp.innerHTML = html;\n\n // Get all top-level children\n const children = Array.from(temp.children);\n\n // Only convert if there are 2 or more divs\n if (children.length < 2) {\n return html;\n }\n\n // Check if all children are divs with simple content (text or inline elements)\n const allDivs = children.every((child) => child.tagName === 'DIV');\n\n if (!allDivs) {\n return html;\n }\n\n // Check if divs have no attributes (only convert plain divs)\n const hasNoAttributes = children.every((div) => div.attributes.length === 0);\n\n if (!hasNoAttributes) {\n return html;\n }\n\n // Check if divs contain only simple content (no nested block elements)\n const hasOnlySimpleContent = children.every((div) => {\n return Array.from(div.children).every((child) => {\n const tag = child.tagName;\n // Allow inline elements and br tags\n return ['SPAN', 'B', 'I', 'EM', 'STRONG', 'U', 'SUB', 'SUP', 'A', 'CODE', 'BR'].includes(tag);\n });\n });\n\n if (!hasOnlySimpleContent) {\n return html;\n }\n\n // Convert to paragraph with br tags\n const contents = children.map((div) => div.innerHTML);\n return `<p>${contents.join('<br>')}</p>`;\n};\n\nexport const normalizeInitialMarkup = (markup) => {\n const trimmed = String(markup ?? '').trim();\n if (!trimmed) return '<div></div>';\n\n const looksLikeHtml = /<[^>]+>/.test(trimmed);\n if (!looksLikeHtml) {\n return `<div>${escapeHtml(trimmed)}</div>`;\n }\n\n // Apply the div-to-paragraph transformation\n return convertConsecutiveDivsToParagraph(trimmed);\n};\n"],"mappings":";;;;;;AAAA,IAAMA,UAAU,GAAG,SAAbA,UAAUA,CAAIC,GAAG;EAAA,OACrBC,MAAM,CAACD,GAAG,CAAC,CACRE,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CACtBA,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CACrBA,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CACrBA,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CACvBA,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;AAAA;;AAE3B;AACA;AACA;AACA;AACA,IAAMC,iCAAiC,GAAG,SAApCA,iCAAiCA,CAAIC,IAAI,EAAK;EAClD;EACA,IAAMC,IAAI,GAAGC,QAAQ,CAACC,aAAa,CAAC,KAAK,CAAC;EAC1CF,IAAI,CAACG,SAAS,GAAGJ,IAAI;;EAErB;EACA,IAAMK,QAAQ,GAAGC,KAAK,CAACC,IAAI,CAACN,IAAI,CAACI,QAAQ,CAAC;;EAE1C;EACA,IAAIA,QAAQ,CAACG,MAAM,GAAG,CAAC,EAAE;IACvB,OAAOR,IAAI;EACb;;EAEA;EACA,IAAMS,OAAO,GAAGJ,QAAQ,CAACK,KAAK,CAAC,UAACC,KAAK;IAAA,OAAKA,KAAK,CAACC,OAAO,KAAK,KAAK;EAAA,EAAC;EAElE,IAAI,CAACH,OAAO,EAAE;IACZ,OAAOT,IAAI;EACb;;EAEA;EACA,IAAMa,eAAe,GAAGR,QAAQ,CAACK,KAAK,CAAC,UAACI,GAAG;IAAA,OAAKA,GAAG,CAACC,UAAU,CAACP,MAAM,KAAK,CAAC;EAAA,EAAC;EAE5E,IAAI,CAACK,eAAe,EAAE;IACpB,OAAOb,IAAI;EACb;;EAEA;EACA,IAAMgB,oBAAoB,GAAGX,QAAQ,CAACK,KAAK,CAAC,UAACI,GAAG,EAAK;IACnD,OAAOR,KAAK,CAACC,IAAI,CAACO,GAAG,CAACT,QAAQ,CAAC,CAACK,KAAK,CAAC,UAACC,KAAK,EAAK;MAC/C,IAAMM,GAAG,GAAGN,KAAK,CAACC,OAAO;MACzB;MACA,OAAO,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,CAACM,QAAQ,CAACD,GAAG,CAAC;IAC/F,CAAC,CAAC;EACJ,CAAC,CAAC;EAEF,IAAI,CAACD,oBAAoB,EAAE;IACzB,OAAOhB,IAAI;EACb;;EAEA;EACA,IAAMmB,QAAQ,GAAGd,QAAQ,CAACe,GAAG,CAAC,UAACN,GAAG;IAAA,OAAKA,GAAG,CAACV,SAAS;EAAA,EAAC;EACrD,aAAAiB,MAAA,CAAaF,QAAQ,CAACG,IAAI,CAAC,MAAM,CAAC;AACpC,CAAC;AAEM,IAAMC,sBAAsB,GAAAC,OAAA,CAAAD,sBAAA,GAAG,SAAzBA,sBAAsBA,CAAIE,MAAM,EAAK;EAChD,IAAMC,OAAO,GAAG7B,MAAM,CAAC4B,MAAM,aAANA,MAAM,cAANA,MAAM,GAAI,EAAE,CAAC,CAACE,IAAI,CAAC,CAAC;EAC3C,IAAI,CAACD,OAAO,EAAE,OAAO,aAAa;EAElC,IAAME,aAAa,GAAG,SAAS,CAACC,IAAI,CAACH,OAAO,CAAC;EAC7C,IAAI,CAACE,aAAa,EAAE;IAClB,eAAAP,MAAA,CAAe1B,UAAU,CAAC+B,OAAO,CAAC;EACpC;;EAEA;EACA,OAAO3B,iCAAiC,CAAC2B,OAAO,CAAC;AACnD,CAAC","ignoreList":[]}
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "2.1.0-next.
|
|
6
|
+
"version": "2.1.0-next.3",
|
|
7
7
|
"description": "",
|
|
8
8
|
"license": "ISC",
|
|
9
9
|
"main": "lib/index.js",
|
|
@@ -16,11 +16,11 @@
|
|
|
16
16
|
"@dnd-kit/utilities": "3.2.2",
|
|
17
17
|
"@mui/icons-material": "^7.3.4",
|
|
18
18
|
"@mui/material": "^7.3.4",
|
|
19
|
-
"@pie-lib/drag": "^4.0.2-next.
|
|
20
|
-
"@pie-lib/math-input": "^8.1.0-next.
|
|
21
|
-
"@pie-lib/math-rendering": "^5.0.2-next.
|
|
22
|
-
"@pie-lib/math-toolbar": "^3.0.2-next.
|
|
23
|
-
"@pie-lib/render-ui": "^6.1.0-next.
|
|
19
|
+
"@pie-lib/drag": "^4.0.2-next.2",
|
|
20
|
+
"@pie-lib/math-input": "^8.1.0-next.1",
|
|
21
|
+
"@pie-lib/math-rendering": "^5.0.2-next.1",
|
|
22
|
+
"@pie-lib/math-toolbar": "^3.0.2-next.2",
|
|
23
|
+
"@pie-lib/render-ui": "^6.1.0-next.2",
|
|
24
24
|
"@tiptap/core": "3.0.9",
|
|
25
25
|
"@tiptap/extension-character-count": "3.0.9",
|
|
26
26
|
"@tiptap/extension-color": "3.0.9",
|
|
@@ -59,6 +59,6 @@
|
|
|
59
59
|
"peerDependencies": {
|
|
60
60
|
"react": "^18.2.0"
|
|
61
61
|
},
|
|
62
|
-
"gitHead": "
|
|
62
|
+
"gitHead": "abac0350cad300ce7a38fd54141b281b48a1461a",
|
|
63
63
|
"scripts": {}
|
|
64
64
|
}
|
|
@@ -349,4 +349,126 @@ describe('EditableHtml', () => {
|
|
|
349
349
|
|
|
350
350
|
jest.useRealTimers();
|
|
351
351
|
});
|
|
352
|
+
|
|
353
|
+
describe('onUpdate callback', () => {
|
|
354
|
+
it('calls onChange when transaction.isDone is true', async () => {
|
|
355
|
+
const onChange = jest.fn();
|
|
356
|
+
const markup = '<p>Initial content</p>';
|
|
357
|
+
|
|
358
|
+
render(<EditableHtml {...defaultProps} markup={markup} onChange={onChange} />);
|
|
359
|
+
|
|
360
|
+
await waitFor(() => {
|
|
361
|
+
expect(useEditor).toHaveBeenCalled();
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
const editorConfig = useEditor.mock.calls[useEditor.mock.calls.length - 1][0];
|
|
365
|
+
const mockEditor = {
|
|
366
|
+
getHTML: jest.fn(() => '<p>Updated content</p>'),
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
const mockTransaction = {
|
|
370
|
+
isDone: true,
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
editorConfig.onUpdate({ editor: mockEditor, transaction: mockTransaction });
|
|
374
|
+
|
|
375
|
+
expect(onChange).toHaveBeenCalledWith('<p>Updated content</p>');
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
it('calls onChange when markup differs from editor HTML', async () => {
|
|
379
|
+
const onChange = jest.fn();
|
|
380
|
+
const markup = '<p>Initial content</p>';
|
|
381
|
+
|
|
382
|
+
render(<EditableHtml {...defaultProps} markup={markup} onChange={onChange} />);
|
|
383
|
+
|
|
384
|
+
await waitFor(() => {
|
|
385
|
+
expect(useEditor).toHaveBeenCalled();
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
const editorConfig = useEditor.mock.calls[useEditor.mock.calls.length - 1][0];
|
|
389
|
+
const mockEditor = {
|
|
390
|
+
getHTML: jest.fn(() => '<p>Different content</p>'),
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
const mockTransaction = {
|
|
394
|
+
isDone: false,
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
editorConfig.onUpdate({ editor: mockEditor, transaction: mockTransaction });
|
|
398
|
+
|
|
399
|
+
expect(onChange).toHaveBeenCalledWith('<p>Different content</p>');
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
it('does not call onChange when transaction.isDone is false and markup matches editor HTML', async () => {
|
|
403
|
+
const onChange = jest.fn();
|
|
404
|
+
const markup = '<p>Same content</p>';
|
|
405
|
+
|
|
406
|
+
render(<EditableHtml {...defaultProps} markup={markup} onChange={onChange} />);
|
|
407
|
+
|
|
408
|
+
await waitFor(() => {
|
|
409
|
+
expect(useEditor).toHaveBeenCalled();
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
const editorConfig = useEditor.mock.calls[useEditor.mock.calls.length - 1][0];
|
|
413
|
+
const mockEditor = {
|
|
414
|
+
getHTML: jest.fn(() => '<p>Same content</p>'),
|
|
415
|
+
};
|
|
416
|
+
|
|
417
|
+
const mockTransaction = {
|
|
418
|
+
isDone: false,
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
editorConfig.onUpdate({ editor: mockEditor, transaction: mockTransaction });
|
|
422
|
+
|
|
423
|
+
expect(onChange).not.toHaveBeenCalled();
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
it('calls onChange when transaction.isDone is true even if markup matches', async () => {
|
|
427
|
+
const onChange = jest.fn();
|
|
428
|
+
const markup = '<p>Same content</p>';
|
|
429
|
+
|
|
430
|
+
render(<EditableHtml {...defaultProps} markup={markup} onChange={onChange} />);
|
|
431
|
+
|
|
432
|
+
await waitFor(() => {
|
|
433
|
+
expect(useEditor).toHaveBeenCalled();
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
const editorConfig = useEditor.mock.calls[useEditor.mock.calls.length - 1][0];
|
|
437
|
+
const mockEditor = {
|
|
438
|
+
getHTML: jest.fn(() => '<p>Same content</p>'),
|
|
439
|
+
};
|
|
440
|
+
|
|
441
|
+
const mockTransaction = {
|
|
442
|
+
isDone: true,
|
|
443
|
+
};
|
|
444
|
+
|
|
445
|
+
editorConfig.onUpdate({ editor: mockEditor, transaction: mockTransaction });
|
|
446
|
+
|
|
447
|
+
expect(onChange).toHaveBeenCalledWith('<p>Same content</p>');
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
it('does not call onChange when onChange is not provided', async () => {
|
|
451
|
+
const markup = '<p>Content</p>';
|
|
452
|
+
|
|
453
|
+
render(<EditableHtml {...defaultProps} markup={markup} onChange={undefined} />);
|
|
454
|
+
|
|
455
|
+
await waitFor(() => {
|
|
456
|
+
expect(useEditor).toHaveBeenCalled();
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
const editorConfig = useEditor.mock.calls[useEditor.mock.calls.length - 1][0];
|
|
460
|
+
const mockEditor = {
|
|
461
|
+
getHTML: jest.fn(() => '<p>Updated content</p>'),
|
|
462
|
+
};
|
|
463
|
+
|
|
464
|
+
const mockTransaction = {
|
|
465
|
+
isDone: true,
|
|
466
|
+
};
|
|
467
|
+
|
|
468
|
+
// Should not throw error when onChange is undefined
|
|
469
|
+
expect(() => {
|
|
470
|
+
editorConfig.onUpdate({ editor: mockEditor, transaction: mockTransaction });
|
|
471
|
+
}).not.toThrow();
|
|
472
|
+
});
|
|
473
|
+
});
|
|
352
474
|
});
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, waitFor } from '@testing-library/react';
|
|
3
|
+
import { EditableHtml } from '../components/EditableHtml';
|
|
4
|
+
|
|
5
|
+
describe('Div to Paragraph Conversion', () => {
|
|
6
|
+
it('converts consecutive divs to paragraph with br tags', async () => {
|
|
7
|
+
const markup = '<div>A</div><div>B</div>';
|
|
8
|
+
const { container } = render(<EditableHtml markup={markup} onChange={() => {}} pluginProps={{}} />);
|
|
9
|
+
|
|
10
|
+
// Wait for the editor to initialize
|
|
11
|
+
await waitFor(() => {
|
|
12
|
+
const prosemirror = container.querySelector('.ProseMirror');
|
|
13
|
+
expect(prosemirror).toBeInTheDocument();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
// Check that the content was converted to a paragraph
|
|
17
|
+
const paragraph = container.querySelector('.ProseMirror p');
|
|
18
|
+
expect(paragraph).toBeInTheDocument();
|
|
19
|
+
|
|
20
|
+
// Check that br tag is present
|
|
21
|
+
const br = container.querySelector('.ProseMirror p br');
|
|
22
|
+
expect(br).toBeInTheDocument();
|
|
23
|
+
|
|
24
|
+
// Verify the text content
|
|
25
|
+
expect(paragraph.textContent).toBe('AB');
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('converts three consecutive divs correctly', async () => {
|
|
29
|
+
const markup = '<div>First</div><div>Second</div><div>Third</div>';
|
|
30
|
+
const { container } = render(<EditableHtml markup={markup} onChange={() => {}} pluginProps={{}} />);
|
|
31
|
+
|
|
32
|
+
await waitFor(() => {
|
|
33
|
+
const prosemirror = container.querySelector('.ProseMirror');
|
|
34
|
+
expect(prosemirror).toBeInTheDocument();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const paragraph = container.querySelector('.ProseMirror p');
|
|
38
|
+
expect(paragraph).toBeInTheDocument();
|
|
39
|
+
|
|
40
|
+
// Should have 2 br tags (between 3 items)
|
|
41
|
+
const brTags = container.querySelectorAll('.ProseMirror p br');
|
|
42
|
+
expect(brTags.length).toBe(2);
|
|
43
|
+
|
|
44
|
+
expect(paragraph.textContent).toBe('FirstSecondThird');
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('does not convert single div', async () => {
|
|
48
|
+
const markup = '<div>Single</div>';
|
|
49
|
+
const { container } = render(<EditableHtml markup={markup} onChange={() => {}} pluginProps={{}} />);
|
|
50
|
+
|
|
51
|
+
await waitFor(() => {
|
|
52
|
+
const prosemirror = container.querySelector('.ProseMirror');
|
|
53
|
+
expect(prosemirror).toBeInTheDocument();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// Should remain as a div
|
|
57
|
+
const div = container.querySelector('.ProseMirror div');
|
|
58
|
+
expect(div).toBeInTheDocument();
|
|
59
|
+
expect(div.textContent).toBe('Single');
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('does not convert divs with attributes', async () => {
|
|
63
|
+
const markup = '<div class="test">A</div><div>B</div>';
|
|
64
|
+
const { container } = render(<EditableHtml markup={markup} onChange={() => {}} pluginProps={{}} />);
|
|
65
|
+
|
|
66
|
+
await waitFor(() => {
|
|
67
|
+
const prosemirror = container.querySelector('.ProseMirror');
|
|
68
|
+
expect(prosemirror).toBeInTheDocument();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// Should remain as divs since one has an attribute
|
|
72
|
+
const divs = container.querySelectorAll('.ProseMirror div');
|
|
73
|
+
expect(divs.length).toBeGreaterThanOrEqual(2);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('handles divs with inline formatting', async () => {
|
|
77
|
+
const markup = '<div><strong>Bold</strong></div><div><em>Italic</em></div>';
|
|
78
|
+
const { container } = render(<EditableHtml markup={markup} onChange={() => {}} pluginProps={{}} />);
|
|
79
|
+
|
|
80
|
+
await waitFor(() => {
|
|
81
|
+
const prosemirror = container.querySelector('.ProseMirror');
|
|
82
|
+
expect(prosemirror).toBeInTheDocument();
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Should be converted to paragraph
|
|
86
|
+
const paragraph = container.querySelector('.ProseMirror p');
|
|
87
|
+
expect(paragraph).toBeInTheDocument();
|
|
88
|
+
|
|
89
|
+
// Check that formatting is preserved
|
|
90
|
+
const strong = container.querySelector('.ProseMirror p strong');
|
|
91
|
+
const em = container.querySelector('.ProseMirror p em');
|
|
92
|
+
expect(strong).toBeInTheDocument();
|
|
93
|
+
expect(em).toBeInTheDocument();
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('does not convert mixed element types', async () => {
|
|
97
|
+
const markup = '<div>A</div><p>B</p>';
|
|
98
|
+
const { container } = render(<EditableHtml markup={markup} onChange={() => {}} pluginProps={{}} />);
|
|
99
|
+
|
|
100
|
+
await waitFor(() => {
|
|
101
|
+
const prosemirror = container.querySelector('.ProseMirror');
|
|
102
|
+
expect(prosemirror).toBeInTheDocument();
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// Should have both div and paragraph
|
|
106
|
+
const div = container.querySelector('.ProseMirror div');
|
|
107
|
+
const p = container.querySelector('.ProseMirror p');
|
|
108
|
+
expect(div).toBeInTheDocument();
|
|
109
|
+
expect(p).toBeInTheDocument();
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('does not convert divs with nested block elements', async () => {
|
|
113
|
+
const markup = '<div><div>Nested</div></div><div>B</div>';
|
|
114
|
+
const { container } = render(<EditableHtml markup={markup} onChange={() => {}} pluginProps={{}} />);
|
|
115
|
+
|
|
116
|
+
await waitFor(() => {
|
|
117
|
+
const prosemirror = container.querySelector('.ProseMirror');
|
|
118
|
+
expect(prosemirror).toBeInTheDocument();
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
// Should remain as divs
|
|
122
|
+
const divs = container.querySelectorAll('.ProseMirror > div');
|
|
123
|
+
expect(divs.length).toBeGreaterThanOrEqual(1);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
@@ -288,7 +288,7 @@ export const EditableHtml = (props) => {
|
|
|
288
288
|
editable: !props.disabled,
|
|
289
289
|
content: normalizeInitialMarkup(props.markup),
|
|
290
290
|
onUpdate: ({ editor, transaction }) => {
|
|
291
|
-
if (transaction.isDone) {
|
|
291
|
+
if (transaction.isDone || props.markup !== editor.getHTML()) {
|
|
292
292
|
props.onChange?.(editor.getHTML());
|
|
293
293
|
}
|
|
294
294
|
},
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { normalizeInitialMarkup } from '../helper';
|
|
2
|
+
|
|
3
|
+
describe('normalizeInitialMarkup', () => {
|
|
4
|
+
describe('basic normalization', () => {
|
|
5
|
+
it('returns empty div for empty string', () => {
|
|
6
|
+
expect(normalizeInitialMarkup('')).toBe('<div></div>');
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it('returns empty div for null', () => {
|
|
10
|
+
expect(normalizeInitialMarkup(null)).toBe('<div></div>');
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('returns empty div for undefined', () => {
|
|
14
|
+
expect(normalizeInitialMarkup(undefined)).toBe('<div></div>');
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('wraps plain text in div', () => {
|
|
18
|
+
expect(normalizeInitialMarkup('Hello')).toBe('<div>Hello</div>');
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('returns HTML tags as-is when detected as HTML', () => {
|
|
22
|
+
// Since '<script>' matches the HTML pattern, it's returned as-is
|
|
23
|
+
// To be escaped, it would need to not match the HTML pattern
|
|
24
|
+
expect(normalizeInitialMarkup('<script>')).toBe('<script>');
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('escapes HTML entities in plain text', () => {
|
|
28
|
+
// Plain text without angle brackets gets escaped
|
|
29
|
+
expect(normalizeInitialMarkup('Hello & World')).toBe('<div>Hello & World</div>');
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('returns single div as-is', () => {
|
|
33
|
+
const html = '<div>Hello</div>';
|
|
34
|
+
expect(normalizeInitialMarkup(html)).toBe(html);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('returns paragraph as-is', () => {
|
|
38
|
+
const html = '<p>Hello</p>';
|
|
39
|
+
expect(normalizeInitialMarkup(html)).toBe(html);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe('consecutive divs to paragraph conversion', () => {
|
|
44
|
+
it('converts two consecutive divs to paragraph with br', () => {
|
|
45
|
+
const input = '<div>A</div><div>B</div>';
|
|
46
|
+
const expected = '<p>A<br>B</p>';
|
|
47
|
+
expect(normalizeInitialMarkup(input)).toBe(expected);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('converts three consecutive divs to paragraph with br tags', () => {
|
|
51
|
+
const input = '<div>A</div><div>B</div><div>C</div>';
|
|
52
|
+
const expected = '<p>A<br>B<br>C</p>';
|
|
53
|
+
expect(normalizeInitialMarkup(input)).toBe(expected);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('handles divs with whitespace', () => {
|
|
57
|
+
const input = '<div> A </div><div> B </div>';
|
|
58
|
+
const expected = '<p> A <br> B </p>';
|
|
59
|
+
expect(normalizeInitialMarkup(input)).toBe(expected);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('handles divs with inline elements', () => {
|
|
63
|
+
const input = '<div><strong>A</strong></div><div><em>B</em></div>';
|
|
64
|
+
const expected = '<p><strong>A</strong><br><em>B</em></p>';
|
|
65
|
+
expect(normalizeInitialMarkup(input)).toBe(expected);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('handles empty divs', () => {
|
|
69
|
+
const input = '<div></div><div>B</div>';
|
|
70
|
+
const expected = '<p><br>B</p>';
|
|
71
|
+
expect(normalizeInitialMarkup(input)).toBe(expected);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('preserves existing br tags within divs', () => {
|
|
75
|
+
const input = '<div>A<br>A2</div><div>B</div>';
|
|
76
|
+
const expected = '<p>A<br>A2<br>B</p>';
|
|
77
|
+
expect(normalizeInitialMarkup(input)).toBe(expected);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe('cases that should NOT convert', () => {
|
|
82
|
+
it('does not convert single div', () => {
|
|
83
|
+
const input = '<div>Hello</div>';
|
|
84
|
+
expect(normalizeInitialMarkup(input)).toBe(input);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('does not convert divs with nested block elements', () => {
|
|
88
|
+
const input = '<div><div>Nested</div></div><div>B</div>';
|
|
89
|
+
expect(normalizeInitialMarkup(input)).toBe(input);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('does not convert divs containing tables', () => {
|
|
93
|
+
const input = '<div><table><tr><td>A</td></tr></table></div><div>B</div>';
|
|
94
|
+
expect(normalizeInitialMarkup(input)).toBe(input);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('does not convert divs containing lists', () => {
|
|
98
|
+
const input = '<div><ul><li>Item</li></ul></div><div>B</div>';
|
|
99
|
+
expect(normalizeInitialMarkup(input)).toBe(input);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('does not convert mixed element types', () => {
|
|
103
|
+
const input = '<div>A</div><p>B</p>';
|
|
104
|
+
expect(normalizeInitialMarkup(input)).toBe(input);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('does not convert paragraphs', () => {
|
|
108
|
+
const input = '<p>A</p><p>B</p>';
|
|
109
|
+
expect(normalizeInitialMarkup(input)).toBe(input);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
describe('edge cases', () => {
|
|
114
|
+
it('handles divs with attributes', () => {
|
|
115
|
+
const input = '<div class="test">A</div><div>B</div>';
|
|
116
|
+
// Should not convert since we only want simple divs
|
|
117
|
+
expect(normalizeInitialMarkup(input)).toBe(input);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('handles complex inline formatting', () => {
|
|
121
|
+
const input = '<div><strong><em>A</em></strong></div><div><u>B</u></div>';
|
|
122
|
+
const expected = '<p><strong><em>A</em></strong><br><u>B</u></p>';
|
|
123
|
+
expect(normalizeInitialMarkup(input)).toBe(expected);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
});
|
package/src/utils/helper.js
CHANGED
|
@@ -6,12 +6,64 @@ const escapeHtml = (str) =>
|
|
|
6
6
|
.replace(/"/g, '"')
|
|
7
7
|
.replace(/'/g, ''');
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Converts consecutive div elements into a single paragraph with line breaks.
|
|
11
|
+
* Example: "<div>A</div><div>B</div>" becomes "<p>A<br>B</p>"
|
|
12
|
+
*/
|
|
13
|
+
const convertConsecutiveDivsToParagraph = (html) => {
|
|
14
|
+
// Create a temporary element to parse the HTML
|
|
15
|
+
const temp = document.createElement('div');
|
|
16
|
+
temp.innerHTML = html;
|
|
17
|
+
|
|
18
|
+
// Get all top-level children
|
|
19
|
+
const children = Array.from(temp.children);
|
|
20
|
+
|
|
21
|
+
// Only convert if there are 2 or more divs
|
|
22
|
+
if (children.length < 2) {
|
|
23
|
+
return html;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Check if all children are divs with simple content (text or inline elements)
|
|
27
|
+
const allDivs = children.every((child) => child.tagName === 'DIV');
|
|
28
|
+
|
|
29
|
+
if (!allDivs) {
|
|
30
|
+
return html;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Check if divs have no attributes (only convert plain divs)
|
|
34
|
+
const hasNoAttributes = children.every((div) => div.attributes.length === 0);
|
|
35
|
+
|
|
36
|
+
if (!hasNoAttributes) {
|
|
37
|
+
return html;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Check if divs contain only simple content (no nested block elements)
|
|
41
|
+
const hasOnlySimpleContent = children.every((div) => {
|
|
42
|
+
return Array.from(div.children).every((child) => {
|
|
43
|
+
const tag = child.tagName;
|
|
44
|
+
// Allow inline elements and br tags
|
|
45
|
+
return ['SPAN', 'B', 'I', 'EM', 'STRONG', 'U', 'SUB', 'SUP', 'A', 'CODE', 'BR'].includes(tag);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
if (!hasOnlySimpleContent) {
|
|
50
|
+
return html;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Convert to paragraph with br tags
|
|
54
|
+
const contents = children.map((div) => div.innerHTML);
|
|
55
|
+
return `<p>${contents.join('<br>')}</p>`;
|
|
56
|
+
};
|
|
57
|
+
|
|
9
58
|
export const normalizeInitialMarkup = (markup) => {
|
|
10
59
|
const trimmed = String(markup ?? '').trim();
|
|
11
60
|
if (!trimmed) return '<div></div>';
|
|
12
61
|
|
|
13
62
|
const looksLikeHtml = /<[^>]+>/.test(trimmed);
|
|
14
|
-
if (looksLikeHtml)
|
|
63
|
+
if (!looksLikeHtml) {
|
|
64
|
+
return `<div>${escapeHtml(trimmed)}</div>`;
|
|
65
|
+
}
|
|
15
66
|
|
|
16
|
-
|
|
67
|
+
// Apply the div-to-paragraph transformation
|
|
68
|
+
return convertConsecutiveDivsToParagraph(trimmed);
|
|
17
69
|
};
|