react-native-enriched-html 1.0.0 → 1.0.1

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.
Files changed (272) hide show
  1. package/android/src/main/java/com/swmansion/enriched/common/EnrichedSpanFlags.kt +39 -0
  2. package/android/src/main/java/com/swmansion/enriched/common/parser/EnrichedParser.java +43 -20
  3. package/android/src/main/java/com/swmansion/enriched/common/parser/EnrichedSpanFactory.kt +1 -0
  4. package/android/src/main/java/com/swmansion/enriched/text/EnrichedTextSpanFactory.kt +1 -0
  5. package/android/src/main/java/com/swmansion/enriched/text/EnrichedTextView.kt +2 -1
  6. package/android/src/main/java/com/swmansion/enriched/textinput/EnrichedTextInputSpannableFactory.kt +2 -1
  7. package/android/src/main/java/com/swmansion/enriched/textinput/EnrichedTextInputView.kt +8 -5
  8. package/android/src/main/java/com/swmansion/enriched/textinput/events/MentionHandler.kt +12 -2
  9. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnMentionEvent.kt +6 -0
  10. package/android/src/main/java/com/swmansion/enriched/textinput/styles/AlignmentStyles.kt +6 -7
  11. package/android/src/main/java/com/swmansion/enriched/textinput/styles/InlineStyles.kt +13 -1
  12. package/android/src/main/java/com/swmansion/enriched/textinput/styles/ListStyles.kt +4 -3
  13. package/android/src/main/java/com/swmansion/enriched/textinput/styles/ParagraphStyles.kt +4 -3
  14. package/android/src/main/java/com/swmansion/enriched/textinput/styles/ParametrizedStyles.kt +38 -11
  15. package/android/src/main/java/com/swmansion/enriched/textinput/utils/EnrichedSelection.kt +2 -0
  16. package/android/src/main/java/com/swmansion/enriched/textinput/utils/EnrichedSpannable.kt +2 -1
  17. package/android/src/main/java/com/swmansion/enriched/textinput/utils/Utils.kt +2 -1
  18. package/cpp/parser/GumboNormalizer.c +61 -3
  19. package/cpp/tests/GumboParserTest.cpp +54 -0
  20. package/ios/EnrichedTextInputView.mm +37 -25
  21. package/ios/extensions/LayoutManagerExtension.mm +73 -62
  22. package/ios/extensions/StringExtension.mm +75 -0
  23. package/ios/htmlParser/HtmlParser.h +4 -1
  24. package/ios/htmlParser/HtmlParser.mm +23 -5
  25. package/ios/inputHtmlParser/InputHtmlParser.mm +6 -3
  26. package/ios/interfaces/StyleHeaders.h +4 -0
  27. package/ios/styles/LinkStyle.mm +20 -34
  28. package/ios/styles/MentionStyle.mm +55 -4
  29. package/ios/textHtmlParser/TextHtmlParser.mm +2 -1
  30. package/ios/utils/ParagraphAttributesUtils.mm +7 -0
  31. package/lib/module/index.js +1 -0
  32. package/lib/module/index.js.map +1 -1
  33. package/lib/module/utils/expandMentionStylesForIndicators.js +2 -3
  34. package/lib/module/utils/expandMentionStylesForIndicators.js.map +1 -1
  35. package/lib/module/utils/normalizeHtmlStyle.js +1 -1
  36. package/lib/module/utils/normalizeHtmlStyle.js.map +1 -1
  37. package/lib/module/web/EnrichedText.css +343 -0
  38. package/lib/module/web/EnrichedText.js +55 -0
  39. package/lib/module/web/EnrichedText.js.map +1 -0
  40. package/lib/module/web/EnrichedTextInput.js +54 -52
  41. package/lib/module/web/EnrichedTextInput.js.map +1 -1
  42. package/lib/module/web/constants/brokenImageGlyph.js +9 -0
  43. package/lib/module/web/constants/brokenImageGlyph.js.map +1 -0
  44. package/lib/module/web/constants/classNames.js +5 -0
  45. package/lib/module/web/constants/classNames.js.map +1 -0
  46. package/lib/module/web/emitLinkDetected.js +12 -0
  47. package/lib/module/web/emitLinkDetected.js.map +1 -0
  48. package/lib/module/web/formats/EnrichedBlockquote.js +1 -1
  49. package/lib/module/web/formats/EnrichedBlockquote.js.map +1 -1
  50. package/lib/module/web/formats/EnrichedCheckboxList.js +2 -2
  51. package/lib/module/web/formats/EnrichedCheckboxList.js.map +1 -1
  52. package/lib/module/web/formats/EnrichedCodeBlock.js +1 -1
  53. package/lib/module/web/formats/EnrichedCodeBlock.js.map +1 -1
  54. package/lib/module/web/formats/EnrichedHeading.js +1 -1
  55. package/lib/module/web/formats/EnrichedHeading.js.map +1 -1
  56. package/lib/module/web/formats/EnrichedImageNodeView.js +1 -1
  57. package/lib/module/web/formats/EnrichedImageNodeView.js.map +1 -1
  58. package/lib/module/web/formats/EnrichedLink.js +43 -3
  59. package/lib/module/web/formats/EnrichedLink.js.map +1 -1
  60. package/lib/module/web/formats/EnrichedOrderedList.js +2 -2
  61. package/lib/module/web/formats/EnrichedOrderedList.js.map +1 -1
  62. package/lib/module/web/formats/EnrichedTextAlign.js +82 -0
  63. package/lib/module/web/formats/EnrichedTextAlign.js.map +1 -0
  64. package/lib/module/web/formats/EnrichedUnorderedList.js +2 -2
  65. package/lib/module/web/formats/EnrichedUnorderedList.js.map +1 -1
  66. package/lib/module/web/formats/applyWrappingListToSelection.js +3 -2
  67. package/lib/module/web/formats/applyWrappingListToSelection.js.map +1 -1
  68. package/lib/module/web/formats/formatRules.js +39 -3
  69. package/lib/module/web/formats/formatRules.js.map +1 -1
  70. package/lib/module/web/formats/listKeyboard.js +4 -1
  71. package/lib/module/web/formats/listKeyboard.js.map +1 -1
  72. package/lib/module/web/normalization/checkboxHtmlNormalizer.js.map +1 -0
  73. package/lib/module/web/normalization/htmlNormalizer.js +514 -0
  74. package/lib/module/web/normalization/htmlNormalizer.js.map +1 -0
  75. package/lib/module/web/normalization/prepareHtmlForWeb.js +78 -0
  76. package/lib/module/web/normalization/prepareHtmlForWeb.js.map +1 -0
  77. package/lib/module/web/{tiptapHtmlNormalizer.js → normalization/tiptapHtmlNormalizer.js} +6 -2
  78. package/lib/module/web/normalization/tiptapHtmlNormalizer.js.map +1 -0
  79. package/lib/module/web/pmPlugins/AutolinkPlugin/autolinkRegex.js +27 -0
  80. package/lib/module/web/pmPlugins/AutolinkPlugin/autolinkRegex.js.map +1 -0
  81. package/lib/module/web/pmPlugins/AutolinkPlugin/index.js +178 -0
  82. package/lib/module/web/pmPlugins/AutolinkPlugin/index.js.map +1 -0
  83. package/lib/module/web/pmPlugins/MentionPlugin/index.js +1 -1
  84. package/lib/module/web/pmPlugins/MentionPlugin/index.js.map +1 -1
  85. package/lib/module/web/pmPlugins/MentionPlugin/makeMentionPluginState.js +23 -4
  86. package/lib/module/web/pmPlugins/MentionPlugin/makeMentionPluginState.js.map +1 -1
  87. package/lib/module/web/pmPlugins/MentionPlugin/setMention.js +17 -18
  88. package/lib/module/web/pmPlugins/MentionPlugin/setMention.js.map +1 -1
  89. package/lib/module/web/pmPlugins/MentionPlugin/useMentionEvents.js +105 -0
  90. package/lib/module/web/pmPlugins/MentionPlugin/useMentionEvents.js.map +1 -0
  91. package/lib/module/web/pmPlugins/ShortcutPlugin.js +11 -0
  92. package/lib/module/web/pmPlugins/ShortcutPlugin.js.map +1 -1
  93. package/lib/module/web/pmPlugins/StrictMarksPlugin.js +2 -0
  94. package/lib/module/web/pmPlugins/StrictMarksPlugin.js.map +1 -1
  95. package/lib/module/web/pmPlugins/StripBoldInStyledHeadingsPlugin.js.map +1 -1
  96. package/lib/module/web/sanitization/htmlSanitizer.js +10 -0
  97. package/lib/module/web/sanitization/htmlSanitizer.js.map +1 -0
  98. package/lib/module/web/styleConversion/buildMentionRulesCSS.js +12 -6
  99. package/lib/module/web/styleConversion/buildMentionRulesCSS.js.map +1 -1
  100. package/lib/module/web/styleConversion/enrichedBaseStyleToCSSProperties.js +159 -0
  101. package/lib/module/web/styleConversion/enrichedBaseStyleToCSSProperties.js.map +1 -0
  102. package/lib/module/web/styleConversion/enrichedInputStyleToCSSProperties.js +3 -155
  103. package/lib/module/web/styleConversion/enrichedInputStyleToCSSProperties.js.map +1 -1
  104. package/lib/module/web/styleConversion/enrichedTextStyleToCSSProperties.js +44 -0
  105. package/lib/module/web/styleConversion/enrichedTextStyleToCSSProperties.js.map +1 -0
  106. package/lib/module/web/styleConversion/{enrichedInputThemingToCSSProperties.js → enrichedThemingToCSSProperties.js} +3 -3
  107. package/lib/module/web/styleConversion/enrichedThemingToCSSProperties.js.map +1 -0
  108. package/lib/module/web/styleConversion/htmlStyleToCSSVariables.js +87 -64
  109. package/lib/module/web/styleConversion/htmlStyleToCSSVariables.js.map +1 -1
  110. package/lib/module/web/styleConversion/inlineImageCSSVariables.js +7 -0
  111. package/lib/module/web/styleConversion/inlineImageCSSVariables.js.map +1 -0
  112. package/lib/module/web/useImageErrorFallback.js +33 -0
  113. package/lib/module/web/useImageErrorFallback.js.map +1 -0
  114. package/lib/module/web/useOnChangeHtml.js +1 -1
  115. package/lib/module/web/useOnChangeHtml.js.map +1 -1
  116. package/lib/module/web/useOnChangeState.js +6 -3
  117. package/lib/module/web/useOnChangeState.js.map +1 -1
  118. package/lib/module/web/useOnLinkDetected.js +32 -45
  119. package/lib/module/web/useOnLinkDetected.js.map +1 -1
  120. package/lib/module/web/usePressInteractions.js +19 -0
  121. package/lib/module/web/usePressInteractions.js.map +1 -0
  122. package/lib/module/web/useStableRef.js +13 -0
  123. package/lib/module/web/useStableRef.js.map +1 -0
  124. package/lib/typescript/src/index.d.ts +1 -0
  125. package/lib/typescript/src/index.d.ts.map +1 -1
  126. package/lib/typescript/src/native/EnrichedText.d.ts +1 -1
  127. package/lib/typescript/src/native/EnrichedText.d.ts.map +1 -1
  128. package/lib/typescript/src/native/EnrichedTextInput.d.ts +1 -1
  129. package/lib/typescript/src/native/EnrichedTextInput.d.ts.map +1 -1
  130. package/lib/typescript/src/types.d.ts +1 -1
  131. package/lib/typescript/src/types.d.ts.map +1 -1
  132. package/lib/typescript/src/utils/expandMentionStylesForIndicators.d.ts +1 -1
  133. package/lib/typescript/src/utils/expandMentionStylesForIndicators.d.ts.map +1 -1
  134. package/lib/typescript/src/utils/normalizeHtmlStyle.d.ts.map +1 -1
  135. package/lib/typescript/src/web/EnrichedText.d.ts +4 -0
  136. package/lib/typescript/src/web/EnrichedText.d.ts.map +1 -0
  137. package/lib/typescript/src/web/EnrichedTextInput.d.ts +2 -2
  138. package/lib/typescript/src/web/EnrichedTextInput.d.ts.map +1 -1
  139. package/lib/typescript/src/web/constants/brokenImageGlyph.d.ts +3 -0
  140. package/lib/typescript/src/web/constants/brokenImageGlyph.d.ts.map +1 -0
  141. package/lib/typescript/src/web/constants/classNames.d.ts +3 -0
  142. package/lib/typescript/src/web/constants/classNames.d.ts.map +1 -0
  143. package/lib/typescript/src/web/emitLinkDetected.d.ts +10 -0
  144. package/lib/typescript/src/web/emitLinkDetected.d.ts.map +1 -0
  145. package/lib/typescript/src/web/formats/EnrichedBlockquote.d.ts.map +1 -1
  146. package/lib/typescript/src/web/formats/EnrichedCheckboxList.d.ts.map +1 -1
  147. package/lib/typescript/src/web/formats/EnrichedCodeBlock.d.ts.map +1 -1
  148. package/lib/typescript/src/web/formats/EnrichedHeading.d.ts.map +1 -1
  149. package/lib/typescript/src/web/formats/EnrichedImageNodeView.d.ts +1 -1
  150. package/lib/typescript/src/web/formats/EnrichedImageNodeView.d.ts.map +1 -1
  151. package/lib/typescript/src/web/formats/EnrichedLink.d.ts +4 -1
  152. package/lib/typescript/src/web/formats/EnrichedLink.d.ts.map +1 -1
  153. package/lib/typescript/src/web/formats/EnrichedOrderedList.d.ts.map +1 -1
  154. package/lib/typescript/src/web/formats/EnrichedTextAlign.d.ts +2 -0
  155. package/lib/typescript/src/web/formats/EnrichedTextAlign.d.ts.map +1 -0
  156. package/lib/typescript/src/web/formats/EnrichedUnorderedList.d.ts.map +1 -1
  157. package/lib/typescript/src/web/formats/applyWrappingListToSelection.d.ts.map +1 -1
  158. package/lib/typescript/src/web/formats/formatRules.d.ts +3 -1
  159. package/lib/typescript/src/web/formats/formatRules.d.ts.map +1 -1
  160. package/lib/typescript/src/web/formats/listKeyboard.d.ts.map +1 -1
  161. package/lib/typescript/src/web/normalization/checkboxHtmlNormalizer.d.ts.map +1 -0
  162. package/lib/typescript/src/web/normalization/htmlNormalizer.d.ts +2 -0
  163. package/lib/typescript/src/web/normalization/htmlNormalizer.d.ts.map +1 -0
  164. package/lib/typescript/src/web/normalization/prepareHtmlForWeb.d.ts +2 -0
  165. package/lib/typescript/src/web/normalization/prepareHtmlForWeb.d.ts.map +1 -0
  166. package/lib/typescript/src/web/normalization/tiptapHtmlNormalizer.d.ts +3 -0
  167. package/lib/typescript/src/web/normalization/tiptapHtmlNormalizer.d.ts.map +1 -0
  168. package/lib/typescript/src/web/pmPlugins/AutolinkPlugin/autolinkRegex.d.ts +7 -0
  169. package/lib/typescript/src/web/pmPlugins/AutolinkPlugin/autolinkRegex.d.ts.map +1 -0
  170. package/lib/typescript/src/web/pmPlugins/AutolinkPlugin/index.d.ts +8 -0
  171. package/lib/typescript/src/web/pmPlugins/AutolinkPlugin/index.d.ts.map +1 -0
  172. package/lib/typescript/src/web/pmPlugins/MentionPlugin/index.d.ts +1 -1
  173. package/lib/typescript/src/web/pmPlugins/MentionPlugin/index.d.ts.map +1 -1
  174. package/lib/typescript/src/web/pmPlugins/MentionPlugin/makeMentionPluginState.d.ts.map +1 -1
  175. package/lib/typescript/src/web/pmPlugins/MentionPlugin/setMention.d.ts.map +1 -1
  176. package/lib/typescript/src/web/pmPlugins/MentionPlugin/useMentionEvents.d.ts +4 -0
  177. package/lib/typescript/src/web/pmPlugins/MentionPlugin/useMentionEvents.d.ts.map +1 -0
  178. package/lib/typescript/src/web/pmPlugins/ShortcutPlugin.d.ts.map +1 -1
  179. package/lib/typescript/src/web/pmPlugins/StrictMarksPlugin.d.ts.map +1 -1
  180. package/lib/typescript/src/web/pmPlugins/StripBoldInStyledHeadingsPlugin.d.ts +6 -1
  181. package/lib/typescript/src/web/pmPlugins/StripBoldInStyledHeadingsPlugin.d.ts.map +1 -1
  182. package/lib/typescript/src/web/sanitization/htmlSanitizer.d.ts +2 -0
  183. package/lib/typescript/src/web/sanitization/htmlSanitizer.d.ts.map +1 -0
  184. package/lib/typescript/src/web/styleConversion/buildMentionRulesCSS.d.ts +2 -2
  185. package/lib/typescript/src/web/styleConversion/buildMentionRulesCSS.d.ts.map +1 -1
  186. package/lib/typescript/src/web/styleConversion/enrichedBaseStyleToCSSProperties.d.ts +8 -0
  187. package/lib/typescript/src/web/styleConversion/enrichedBaseStyleToCSSProperties.d.ts.map +1 -0
  188. package/lib/typescript/src/web/styleConversion/enrichedInputStyleToCSSProperties.d.ts +2 -5
  189. package/lib/typescript/src/web/styleConversion/enrichedInputStyleToCSSProperties.d.ts.map +1 -1
  190. package/lib/typescript/src/web/styleConversion/enrichedTextStyleToCSSProperties.d.ts +4 -0
  191. package/lib/typescript/src/web/styleConversion/enrichedTextStyleToCSSProperties.d.ts.map +1 -0
  192. package/lib/typescript/src/web/styleConversion/{enrichedInputThemingToCSSProperties.d.ts → enrichedThemingToCSSProperties.d.ts} +1 -1
  193. package/lib/typescript/src/web/styleConversion/enrichedThemingToCSSProperties.d.ts.map +1 -0
  194. package/lib/typescript/src/web/styleConversion/htmlStyleToCSSVariables.d.ts +5 -4
  195. package/lib/typescript/src/web/styleConversion/htmlStyleToCSSVariables.d.ts.map +1 -1
  196. package/lib/typescript/src/web/styleConversion/inlineImageCSSVariables.d.ts +3 -0
  197. package/lib/typescript/src/web/styleConversion/inlineImageCSSVariables.d.ts.map +1 -0
  198. package/lib/typescript/src/web/useImageErrorFallback.d.ts +3 -0
  199. package/lib/typescript/src/web/useImageErrorFallback.d.ts.map +1 -0
  200. package/lib/typescript/src/web/useOnChangeState.d.ts.map +1 -1
  201. package/lib/typescript/src/web/useOnLinkDetected.d.ts +2 -2
  202. package/lib/typescript/src/web/useOnLinkDetected.d.ts.map +1 -1
  203. package/lib/typescript/src/web/usePressInteractions.d.ts +2 -0
  204. package/lib/typescript/src/web/usePressInteractions.d.ts.map +1 -0
  205. package/lib/typescript/src/web/useStableRef.d.ts +3 -0
  206. package/lib/typescript/src/web/useStableRef.d.ts.map +1 -0
  207. package/package.json +54 -28
  208. package/src/index.tsx +1 -0
  209. package/src/types.ts +5 -3
  210. package/src/utils/expandMentionStylesForIndicators.ts +3 -3
  211. package/src/utils/normalizeHtmlStyle.ts +2 -1
  212. package/src/web/EnrichedText.css +343 -0
  213. package/src/web/EnrichedText.tsx +82 -0
  214. package/src/web/EnrichedTextInput.tsx +69 -55
  215. package/src/web/constants/brokenImageGlyph.ts +7 -0
  216. package/src/web/constants/classNames.ts +2 -0
  217. package/src/web/emitLinkDetected.ts +29 -0
  218. package/src/web/formats/EnrichedBlockquote.ts +2 -1
  219. package/src/web/formats/EnrichedCheckboxList.ts +5 -2
  220. package/src/web/formats/EnrichedCodeBlock.ts +2 -1
  221. package/src/web/formats/EnrichedHeading.ts +2 -1
  222. package/src/web/formats/EnrichedImageNodeView.tsx +1 -3
  223. package/src/web/formats/EnrichedLink.ts +59 -4
  224. package/src/web/formats/EnrichedOrderedList.ts +5 -2
  225. package/src/web/formats/EnrichedTextAlign.ts +88 -0
  226. package/src/web/formats/EnrichedUnorderedList.ts +5 -2
  227. package/src/web/formats/applyWrappingListToSelection.ts +4 -4
  228. package/src/web/formats/formatRules.ts +48 -4
  229. package/src/web/formats/listKeyboard.ts +6 -1
  230. package/src/web/normalization/htmlNormalizer.ts +656 -0
  231. package/src/web/normalization/prepareHtmlForWeb.ts +93 -0
  232. package/src/web/{tiptapHtmlNormalizer.ts → normalization/tiptapHtmlNormalizer.ts} +12 -2
  233. package/src/web/pmPlugins/AutolinkPlugin/autolinkRegex.ts +43 -0
  234. package/src/web/pmPlugins/AutolinkPlugin/index.ts +274 -0
  235. package/src/web/pmPlugins/MentionPlugin/index.ts +1 -1
  236. package/src/web/pmPlugins/MentionPlugin/makeMentionPluginState.ts +32 -5
  237. package/src/web/pmPlugins/MentionPlugin/setMention.ts +19 -29
  238. package/src/web/pmPlugins/MentionPlugin/useMentionEvents.ts +114 -0
  239. package/src/web/pmPlugins/ShortcutPlugin.ts +7 -0
  240. package/src/web/pmPlugins/StrictMarksPlugin.ts +3 -0
  241. package/src/web/pmPlugins/StripBoldInStyledHeadingsPlugin.ts +41 -36
  242. package/src/web/sanitization/htmlSanitizer.ts +8 -0
  243. package/src/web/styleConversion/buildMentionRulesCSS.ts +30 -11
  244. package/src/web/styleConversion/enrichedBaseStyleToCSSProperties.ts +228 -0
  245. package/src/web/styleConversion/enrichedInputStyleToCSSProperties.ts +6 -217
  246. package/src/web/styleConversion/enrichedTextStyleToCSSProperties.ts +53 -0
  247. package/src/web/styleConversion/{enrichedInputThemingToCSSProperties.ts → enrichedThemingToCSSProperties.ts} +4 -4
  248. package/src/web/styleConversion/htmlStyleToCSSVariables.ts +121 -70
  249. package/src/web/styleConversion/inlineImageCSSVariables.ts +6 -0
  250. package/src/web/useImageErrorFallback.ts +37 -0
  251. package/src/web/useOnChangeHtml.ts +1 -1
  252. package/src/web/useOnChangeState.ts +7 -5
  253. package/src/web/useOnLinkDetected.ts +38 -50
  254. package/src/web/usePressInteractions.tsx +22 -0
  255. package/src/web/useStableRef.ts +12 -0
  256. package/lib/module/web/EnrichedTextInput.css +0 -201
  257. package/lib/module/web/checkboxHtmlNormalizer.js.map +0 -1
  258. package/lib/module/web/pmPlugins/MentionPlugin/subscribeMentionEvents.js +0 -100
  259. package/lib/module/web/pmPlugins/MentionPlugin/subscribeMentionEvents.js.map +0 -1
  260. package/lib/module/web/styleConversion/enrichedInputThemingToCSSProperties.js.map +0 -1
  261. package/lib/module/web/tiptapHtmlNormalizer.js.map +0 -1
  262. package/lib/typescript/src/web/checkboxHtmlNormalizer.d.ts.map +0 -1
  263. package/lib/typescript/src/web/pmPlugins/MentionPlugin/subscribeMentionEvents.d.ts +0 -4
  264. package/lib/typescript/src/web/pmPlugins/MentionPlugin/subscribeMentionEvents.d.ts.map +0 -1
  265. package/lib/typescript/src/web/styleConversion/enrichedInputThemingToCSSProperties.d.ts.map +0 -1
  266. package/lib/typescript/src/web/tiptapHtmlNormalizer.d.ts +0 -3
  267. package/lib/typescript/src/web/tiptapHtmlNormalizer.d.ts.map +0 -1
  268. package/src/web/EnrichedTextInput.css +0 -201
  269. package/src/web/pmPlugins/MentionPlugin/subscribeMentionEvents.ts +0 -105
  270. /package/lib/module/web/{checkboxHtmlNormalizer.js → normalization/checkboxHtmlNormalizer.js} +0 -0
  271. /package/lib/typescript/src/web/{checkboxHtmlNormalizer.d.ts → normalization/checkboxHtmlNormalizer.d.ts} +0 -0
  272. /package/src/web/{checkboxHtmlNormalizer.ts → normalization/checkboxHtmlNormalizer.ts} +0 -0
@@ -0,0 +1,53 @@
1
+ import type { CSSProperties } from 'react';
2
+ import type { TextStyle } from 'react-native';
3
+ import { enrichedBaseStyleToCSSProperties } from './enrichedBaseStyleToCSSProperties';
4
+ import { toColor } from './toColor';
5
+
6
+ export function enrichedTextStyleToCSSProperties(
7
+ style: TextStyle
8
+ ): CSSProperties {
9
+ const css: CSSProperties = {
10
+ ...enrichedBaseStyleToCSSProperties(style, { scrollEnabled: false }),
11
+
12
+ // Text-only properties
13
+ // textAlign: RN 'auto' has no CSS equivalent
14
+ textAlign: style.textAlign !== 'auto' ? style.textAlign : undefined,
15
+ textTransform: style.textTransform,
16
+ textDecorationLine: style.textDecorationLine,
17
+ textDecorationStyle: style.textDecorationStyle,
18
+ textDecorationColor: toColor(style.textDecorationColor),
19
+ textShadow: resolveTextShadow(style),
20
+ // userSelect: RN 'contain' has no CSS equivalent
21
+ userSelect: style.userSelect !== 'contain' ? style.userSelect : undefined,
22
+ fontVariant: Array.isArray(style.fontVariant)
23
+ ? style.fontVariant.join(' ')
24
+ : undefined,
25
+ // writingDirection: RN 'auto' has no CSS equivalent
26
+ direction:
27
+ style.writingDirection !== 'auto' ? style.writingDirection : undefined,
28
+ // verticalAlign: RN 'auto' has no CSS equivalent
29
+ verticalAlign:
30
+ style.verticalAlign !== 'auto' ? style.verticalAlign : undefined,
31
+ };
32
+
33
+ return Object.fromEntries(
34
+ Object.entries(css).filter(([, v]) => v !== undefined)
35
+ );
36
+ }
37
+
38
+ function resolveTextShadow(style: TextStyle): string | undefined {
39
+ const { textShadowColor, textShadowOffset, textShadowRadius } = style;
40
+ if (
41
+ textShadowColor == null &&
42
+ textShadowOffset == null &&
43
+ textShadowRadius == null
44
+ ) {
45
+ return undefined;
46
+ }
47
+ const x = textShadowOffset?.width ?? 0;
48
+ const y = textShadowOffset?.height ?? 0;
49
+ const blur = textShadowRadius ?? 0;
50
+ const color = toColor(textShadowColor);
51
+ const offset = `${x}px ${y}px ${blur}px`;
52
+ return color ? `${offset} ${color}` : offset;
53
+ }
@@ -4,8 +4,8 @@ import { toColor } from './toColor';
4
4
 
5
5
  type EnrichedInputThemingStyle = Partial<
6
6
  Pick<CSSProperties, 'caretColor'> & {
7
- '--eti-placeholder-text-color': string;
8
- '--eti-selection-color': string;
7
+ '--et-placeholder-text-color': string;
8
+ '--et-selection-color': string;
9
9
  }
10
10
  >;
11
11
 
@@ -25,10 +25,10 @@ export function enrichedInputThemingToCSSProperties({
25
25
  if (caret) extra.caretColor = caret;
26
26
 
27
27
  const placeholderCss = toColor(placeholderTextColor);
28
- if (placeholderCss) extra['--eti-placeholder-text-color'] = placeholderCss;
28
+ if (placeholderCss) extra['--et-placeholder-text-color'] = placeholderCss;
29
29
 
30
30
  const selectionCss = toColor(selectionColor);
31
- if (selectionCss) extra['--eti-selection-color'] = selectionCss;
31
+ if (selectionCss) extra['--et-selection-color'] = selectionCss;
32
32
 
33
33
  return extra;
34
34
  }
@@ -1,7 +1,15 @@
1
1
  import type { CSSProperties } from 'react';
2
2
  import type { ColorValue } from 'react-native';
3
- import type { HtmlStyle, MentionStyleProperties } from '../../types';
4
- import { DEFAULT_HTML_STYLE } from '../../utils/defaultHtmlStyle';
3
+ import type {
4
+ EnrichedTextHtmlStyle,
5
+ EnrichedTextMentionStyleProperties,
6
+ HtmlStyle,
7
+ MentionStyleProperties,
8
+ } from '../../types';
9
+ import {
10
+ DEFAULT_ENRICHED_TEXT_STYLE,
11
+ DEFAULT_HTML_STYLE,
12
+ } from '../../utils/defaultHtmlStyle';
5
13
  import { expandMentionStylesForIndicators } from '../../utils/expandMentionStylesForIndicators';
6
14
  import { HEADING_TAGS } from '../formats/EnrichedHeading';
7
15
  import {
@@ -12,17 +20,21 @@ import { toColor } from './toColor';
12
20
  import { isMentionStyleRecord } from '../../utils/isMentionStyleRecord';
13
21
 
14
22
  export function mergeWithDefaultHtmlStyle(
15
- htmlStyle?: HtmlStyle
23
+ htmlStyle?: HtmlStyle,
24
+ htmlStyleToMergeWith: HtmlStyle = DEFAULT_HTML_STYLE
16
25
  ): Required<HtmlStyle> {
17
26
  const style = htmlStyle ?? {};
18
27
 
19
- const mentionMap = expandMentionStylesForIndicatorsIncludeDefault(style);
28
+ const mentionMap = expandMentionStylesForIndicatorsIncludeDefault(
29
+ style,
30
+ htmlStyleToMergeWith
31
+ );
20
32
  const converted: HtmlStyle = {
21
33
  ...style,
22
34
  mention: mentionMap,
23
35
  };
24
36
 
25
- const merged: Record<string, unknown> = { ...DEFAULT_HTML_STYLE };
37
+ const merged: Record<string, unknown> = { ...htmlStyleToMergeWith };
26
38
 
27
39
  for (const key in converted) {
28
40
  if (key === 'mention') {
@@ -30,7 +42,7 @@ export function mergeWithDefaultHtmlStyle(
30
42
  continue;
31
43
  }
32
44
  merged[key] = {
33
- ...DEFAULT_HTML_STYLE[key as keyof HtmlStyle],
45
+ ...htmlStyleToMergeWith[key as keyof HtmlStyle],
34
46
  ...(converted[key as keyof HtmlStyle] as object),
35
47
  };
36
48
  }
@@ -38,39 +50,76 @@ export function mergeWithDefaultHtmlStyle(
38
50
  return merged as Required<HtmlStyle>;
39
51
  }
40
52
 
41
- const ETI_CSS_VARS = {
42
- codeColor: '--eti-code-color',
43
- codeBgColor: '--eti-code-bg-color',
44
- blockquoteBorderColor: '--eti-blockquote-border-color',
45
- blockquoteBorderWidth: '--eti-blockquote-border-width',
46
- blockquoteGapWidth: '--eti-blockquote-gap-width',
47
- blockquoteColor: '--eti-blockquote-color',
48
- codeblockBgColor: '--eti-codeblock-bg-color',
49
- codeblockColor: '--eti-codeblock-color',
50
- codeblockBorderRadius: '--eti-codeblock-border-radius',
51
- linkColor: '--eti-link-color',
52
- linkTextDecorationLine: '--eti-link-text-decoration-line',
53
- ulBulletColor: '--eti-ul-bullet-color',
54
- ulBulletSize: '--eti-ul-bullet-size',
55
- ulMarginLeft: '--eti-ul-margin-left',
56
- ulGapWidth: '--eti-ul-gap-width',
57
- olMarginLeft: '--eti-ol-margin-left',
58
- olGapWidth: '--eti-ol-gap-width',
59
- olMarkerColor: '--eti-ol-marker-color',
60
- olMarkerFontWeight: '--eti-ol-marker-font-weight',
61
- checkboxBoxSize: '--eti-checkbox-box-size',
62
- checkboxGapWidth: '--eti-checkbox-gap-width',
63
- checkboxMarginLeft: '--eti-checkbox-margin-left',
64
- checkboxBoxColor: '--eti-checkbox-box-color',
53
+ export function mergeWithDefaultEnrichedTextHtmlStyle(
54
+ htmlStyle?: EnrichedTextHtmlStyle
55
+ ): Required<EnrichedTextHtmlStyle> {
56
+ const merged = mergeWithDefaultHtmlStyle(
57
+ htmlStyle as HtmlStyle,
58
+ DEFAULT_ENRICHED_TEXT_STYLE
59
+ );
60
+
61
+ const a = {
62
+ ...DEFAULT_ENRICHED_TEXT_STYLE.a,
63
+ ...htmlStyle?.a,
64
+ };
65
+
66
+ const mentionDefaults = DEFAULT_ENRICHED_TEXT_STYLE.mention;
67
+ const passedMentionMap = htmlStyle?.mention;
68
+ const mergedMentionMap = merged.mention as Record<
69
+ string,
70
+ EnrichedTextMentionStyleProperties
71
+ >;
72
+ const mention: Record<string, EnrichedTextMentionStyleProperties> = {};
73
+ for (const indicator in mergedMentionMap) {
74
+ mention[indicator] = {
75
+ ...mentionDefaults,
76
+ ...(isMentionStyleRecord(passedMentionMap)
77
+ ? (passedMentionMap[indicator] ??
78
+ passedMentionMap[MENTION_STYLE_DEFAULT_KEY])
79
+ : passedMentionMap),
80
+ };
81
+ }
82
+
83
+ return {
84
+ ...merged,
85
+ a,
86
+ mention,
87
+ } as Required<EnrichedTextHtmlStyle>;
88
+ }
89
+
90
+ const ET_CSS_VARS = {
91
+ codeColor: '--et-code-color',
92
+ codeBgColor: '--et-code-bg-color',
93
+ blockquoteBorderColor: '--et-blockquote-border-color',
94
+ blockquoteBorderWidth: '--et-blockquote-border-width',
95
+ blockquoteGapWidth: '--et-blockquote-gap-width',
96
+ blockquoteColor: '--et-blockquote-color',
97
+ codeblockBgColor: '--et-codeblock-bg-color',
98
+ codeblockColor: '--et-codeblock-color',
99
+ codeblockBorderRadius: '--et-codeblock-border-radius',
100
+ linkColor: '--et-link-color',
101
+ linkTextDecorationLine: '--et-link-text-decoration-line',
102
+ ulBulletColor: '--et-ul-bullet-color',
103
+ ulBulletSize: '--et-ul-bullet-size',
104
+ ulMarginLeft: '--et-ul-margin-left',
105
+ ulGapWidth: '--et-ul-gap-width',
106
+ olMarginLeft: '--et-ol-margin-left',
107
+ olGapWidth: '--et-ol-gap-width',
108
+ olMarkerColor: '--et-ol-marker-color',
109
+ olMarkerFontWeight: '--et-ol-marker-font-weight',
110
+ checkboxBoxSize: '--et-checkbox-box-size',
111
+ checkboxGapWidth: '--et-checkbox-gap-width',
112
+ checkboxMarginLeft: '--et-checkbox-margin-left',
113
+ checkboxBoxColor: '--et-checkbox-box-color',
65
114
  } as const;
66
115
 
67
- export const ETI_MENTION_CSS_VARS = {
116
+ export const ET_MENTION_CSS_VARS = {
68
117
  color: (indicator: string) =>
69
- `--eti-mention-${indicatorToMentionCssKey(indicator)}-color`,
118
+ `--et-mention-${indicatorToMentionCssKey(indicator)}-color`,
70
119
  backgroundColor: (indicator: string) =>
71
- `--eti-mention-${indicatorToMentionCssKey(indicator)}-background-color`,
120
+ `--et-mention-${indicatorToMentionCssKey(indicator)}-background-color`,
72
121
  textDecorationLine: (indicator: string) =>
73
- `--eti-mention-${indicatorToMentionCssKey(indicator)}-text-decoration-line`,
122
+ `--et-mention-${indicatorToMentionCssKey(indicator)}-text-decoration-line`,
74
123
  } as const;
75
124
 
76
125
  function setColorVar(
@@ -94,8 +143,8 @@ function applyCodeVars(
94
143
  vars: Record<string, string>,
95
144
  code?: HtmlStyle['code']
96
145
  ): void {
97
- setColorVar(vars, ETI_CSS_VARS.codeColor, code?.color);
98
- setColorVar(vars, ETI_CSS_VARS.codeBgColor, code?.backgroundColor);
146
+ setColorVar(vars, ET_CSS_VARS.codeColor, code?.color);
147
+ setColorVar(vars, ET_CSS_VARS.codeBgColor, code?.backgroundColor);
99
148
  }
100
149
 
101
150
  function applyHeadingVars(
@@ -105,9 +154,9 @@ function applyHeadingVars(
105
154
  for (const level of HEADING_TAGS) {
106
155
  const h = htmlStyle?.[level];
107
156
  if (h?.fontSize != null)
108
- vars[`--eti-${level}-font-size`] = `${h.fontSize}px`;
157
+ vars[`--et-${level}-font-size`] = `${h.fontSize}px`;
109
158
  if (h?.bold != null)
110
- vars[`--eti-${level}-font-weight`] = h.bold ? 'bold' : 'normal';
159
+ vars[`--et-${level}-font-weight`] = h.bold ? 'bold' : 'normal';
111
160
  }
112
161
  }
113
162
 
@@ -115,28 +164,28 @@ function applyBlockquoteVars(
115
164
  vars: Record<string, string>,
116
165
  bq?: HtmlStyle['blockquote']
117
166
  ): void {
118
- setColorVar(vars, ETI_CSS_VARS.blockquoteBorderColor, bq?.borderColor);
119
- setPxVar(vars, ETI_CSS_VARS.blockquoteBorderWidth, bq?.borderWidth);
120
- setPxVar(vars, ETI_CSS_VARS.blockquoteGapWidth, bq?.gapWidth);
121
- setColorVar(vars, ETI_CSS_VARS.blockquoteColor, bq?.color);
167
+ setColorVar(vars, ET_CSS_VARS.blockquoteBorderColor, bq?.borderColor);
168
+ setPxVar(vars, ET_CSS_VARS.blockquoteBorderWidth, bq?.borderWidth);
169
+ setPxVar(vars, ET_CSS_VARS.blockquoteGapWidth, bq?.gapWidth);
170
+ setColorVar(vars, ET_CSS_VARS.blockquoteColor, bq?.color);
122
171
  }
123
172
 
124
173
  function applyCodeblockVars(
125
174
  vars: Record<string, string>,
126
175
  cb?: HtmlStyle['codeblock']
127
176
  ): void {
128
- setColorVar(vars, ETI_CSS_VARS.codeblockBgColor, cb?.backgroundColor);
129
- setColorVar(vars, ETI_CSS_VARS.codeblockColor, cb?.color);
130
- setPxVar(vars, ETI_CSS_VARS.codeblockBorderRadius, cb?.borderRadius);
177
+ setColorVar(vars, ET_CSS_VARS.codeblockBgColor, cb?.backgroundColor);
178
+ setColorVar(vars, ET_CSS_VARS.codeblockColor, cb?.color);
179
+ setPxVar(vars, ET_CSS_VARS.codeblockBorderRadius, cb?.borderRadius);
131
180
  }
132
181
 
133
182
  function applyLinkVars(
134
183
  vars: Record<string, string>,
135
184
  anchor?: HtmlStyle['a']
136
185
  ): void {
137
- setColorVar(vars, ETI_CSS_VARS.linkColor, anchor?.color);
186
+ setColorVar(vars, ET_CSS_VARS.linkColor, anchor?.color);
138
187
  if (anchor?.textDecorationLine != null) {
139
- vars[ETI_CSS_VARS.linkTextDecorationLine] = anchor.textDecorationLine;
188
+ vars[ET_CSS_VARS.linkTextDecorationLine] = anchor.textDecorationLine;
140
189
  }
141
190
  }
142
191
 
@@ -144,21 +193,21 @@ function applyUnorderedListVars(
144
193
  vars: Record<string, string>,
145
194
  ul?: HtmlStyle['ul']
146
195
  ): void {
147
- setColorVar(vars, ETI_CSS_VARS.ulBulletColor, ul?.bulletColor);
148
- setPxVar(vars, ETI_CSS_VARS.ulBulletSize, ul?.bulletSize);
149
- setPxVar(vars, ETI_CSS_VARS.ulMarginLeft, ul?.marginLeft);
150
- setPxVar(vars, ETI_CSS_VARS.ulGapWidth, ul?.gapWidth);
196
+ setColorVar(vars, ET_CSS_VARS.ulBulletColor, ul?.bulletColor);
197
+ setPxVar(vars, ET_CSS_VARS.ulBulletSize, ul?.bulletSize);
198
+ setPxVar(vars, ET_CSS_VARS.ulMarginLeft, ul?.marginLeft);
199
+ setPxVar(vars, ET_CSS_VARS.ulGapWidth, ul?.gapWidth);
151
200
  }
152
201
 
153
202
  function applyOrderedListVars(
154
203
  vars: Record<string, string>,
155
204
  ol?: HtmlStyle['ol']
156
205
  ): void {
157
- setPxVar(vars, ETI_CSS_VARS.olMarginLeft, ol?.marginLeft);
158
- setPxVar(vars, ETI_CSS_VARS.olGapWidth, ol?.gapWidth);
159
- setColorVar(vars, ETI_CSS_VARS.olMarkerColor, ol?.markerColor);
206
+ setPxVar(vars, ET_CSS_VARS.olMarginLeft, ol?.marginLeft);
207
+ setPxVar(vars, ET_CSS_VARS.olGapWidth, ol?.gapWidth);
208
+ setColorVar(vars, ET_CSS_VARS.olMarkerColor, ol?.markerColor);
160
209
  if (ol?.markerFontWeight != null) {
161
- vars[ETI_CSS_VARS.olMarkerFontWeight] = String(ol.markerFontWeight);
210
+ vars[ET_CSS_VARS.olMarkerFontWeight] = String(ol.markerFontWeight);
162
211
  }
163
212
  }
164
213
 
@@ -166,35 +215,36 @@ function applyCheckboxListVars(
166
215
  vars: Record<string, string>,
167
216
  ulCheckbox?: HtmlStyle['ulCheckbox']
168
217
  ): void {
169
- setPxVar(vars, ETI_CSS_VARS.checkboxBoxSize, ulCheckbox?.boxSize);
170
- setPxVar(vars, ETI_CSS_VARS.checkboxGapWidth, ulCheckbox?.gapWidth);
171
- setPxVar(vars, ETI_CSS_VARS.checkboxMarginLeft, ulCheckbox?.marginLeft);
172
- setColorVar(vars, ETI_CSS_VARS.checkboxBoxColor, ulCheckbox?.boxColor);
218
+ setPxVar(vars, ET_CSS_VARS.checkboxBoxSize, ulCheckbox?.boxSize);
219
+ setPxVar(vars, ET_CSS_VARS.checkboxGapWidth, ulCheckbox?.gapWidth);
220
+ setPxVar(vars, ET_CSS_VARS.checkboxMarginLeft, ulCheckbox?.marginLeft);
221
+ setColorVar(vars, ET_CSS_VARS.checkboxBoxColor, ulCheckbox?.boxColor);
173
222
  }
174
223
 
175
224
  function applyMentionVars(
176
225
  vars: Record<string, string>,
177
226
  mention: Record<string, MentionStyleProperties>
178
227
  ): void {
228
+ if (!mention) return;
229
+
179
230
  for (const [indicator, mentionStyle] of Object.entries(mention)) {
231
+ setColorVar(vars, ET_MENTION_CSS_VARS.color(indicator), mentionStyle.color);
180
232
  setColorVar(
181
233
  vars,
182
- ETI_MENTION_CSS_VARS.color(indicator),
183
- mentionStyle.color
184
- );
185
- setColorVar(
186
- vars,
187
- ETI_MENTION_CSS_VARS.backgroundColor(indicator),
234
+ ET_MENTION_CSS_VARS.backgroundColor(indicator),
188
235
  mentionStyle.backgroundColor
189
236
  );
190
237
  if (mentionStyle.textDecorationLine != null) {
191
- vars[ETI_MENTION_CSS_VARS.textDecorationLine(indicator)] =
238
+ vars[ET_MENTION_CSS_VARS.textDecorationLine(indicator)] =
192
239
  mentionStyle.textDecorationLine;
193
240
  }
194
241
  }
195
242
  }
196
243
 
197
- function expandMentionStylesForIndicatorsIncludeDefault(htmlStyle?: HtmlStyle) {
244
+ function expandMentionStylesForIndicatorsIncludeDefault(
245
+ htmlStyle: HtmlStyle,
246
+ htmlStyleToMergeWith: HtmlStyle
247
+ ) {
198
248
  const mentionIndicators = isMentionStyleRecord(htmlStyle?.mention)
199
249
  ? Object.keys(htmlStyle?.mention)
200
250
  : [];
@@ -204,11 +254,12 @@ function expandMentionStylesForIndicatorsIncludeDefault(htmlStyle?: HtmlStyle) {
204
254
 
205
255
  return expandMentionStylesForIndicators(
206
256
  htmlStyle?.mention,
207
- mentionIndicators
257
+ mentionIndicators,
258
+ htmlStyleToMergeWith
208
259
  );
209
260
  }
210
261
 
211
- export function htmlStyleToCSSVariables(htmlStyle?: HtmlStyle): CSSProperties {
262
+ export function htmlStyleToCSSVariables(htmlStyle: HtmlStyle): CSSProperties {
212
263
  const vars: Record<string, string> = {};
213
264
  applyCodeVars(vars, htmlStyle?.code);
214
265
  applyHeadingVars(vars, htmlStyle);
@@ -220,7 +271,7 @@ export function htmlStyleToCSSVariables(htmlStyle?: HtmlStyle): CSSProperties {
220
271
  applyCheckboxListVars(vars, htmlStyle?.ulCheckbox);
221
272
  applyMentionVars(
222
273
  vars,
223
- expandMentionStylesForIndicatorsIncludeDefault(htmlStyle)
274
+ htmlStyle.mention as Record<string, MentionStyleProperties>
224
275
  );
225
276
  return vars as CSSProperties;
226
277
  }
@@ -0,0 +1,6 @@
1
+ import type { CSSProperties } from 'react';
2
+ import { BROKEN_IMAGE_GLYPH_MASK } from '../constants/brokenImageGlyph';
3
+
4
+ export const INLINE_IMAGE_CSS_VARIABLES = {
5
+ '--et-broken-image-glyph': BROKEN_IMAGE_GLYPH_MASK,
6
+ } as CSSProperties;
@@ -0,0 +1,37 @@
1
+ import { useEffect, type RefObject } from 'react';
2
+
3
+ /*
4
+ * Flag images that fail to load so CSS can swap in a broken-image placeholder.
5
+ */
6
+ export const useImageErrorFallback = (
7
+ containerRef: RefObject<HTMLElement | null>
8
+ ) => {
9
+ // listen for errors
10
+ useEffect(() => {
11
+ const container = containerRef.current;
12
+ if (!container) return;
13
+
14
+ const handleImageError = (e: Event) => {
15
+ const target = e.target as HTMLElement;
16
+ if (target && target.tagName && target.tagName.toLowerCase() === 'img') {
17
+ target.classList.add('error');
18
+ }
19
+ };
20
+
21
+ container.addEventListener('error', handleImageError, true);
22
+
23
+ // handle <img> elements that emitted an error event before we could set up a listener
24
+ const images =
25
+ container.querySelectorAll<HTMLImageElement>('img:not(.error)');
26
+
27
+ images.forEach((img) => {
28
+ if (img.complete && img.naturalHeight === 0) {
29
+ img.classList.add('error');
30
+ }
31
+ });
32
+
33
+ return () => {
34
+ container.removeEventListener('error', handleImageError, true);
35
+ };
36
+ }, [containerRef]);
37
+ };
@@ -2,7 +2,7 @@ import { type Editor } from '@tiptap/react';
2
2
  import type { OnChangeHtmlEvent } from '../types';
3
3
  import type { NativeSyntheticEvent } from 'react-native';
4
4
  import { useOnEditorChange } from './useOnEditorChange';
5
- import { normalizeHtmlFromTiptap } from './tiptapHtmlNormalizer';
5
+ import { normalizeHtmlFromTiptap } from './normalization/tiptapHtmlNormalizer';
6
6
 
7
7
  export const useOnChangeHtml = (
8
8
  editor: Editor,
@@ -4,6 +4,7 @@ import type { OnChangeStateEvent } from '../types';
4
4
  import type { NativeSyntheticEvent } from 'react-native';
5
5
  import { adaptWebToNativeEvent } from './adaptWebToNativeEvent';
6
6
  import {
7
+ getCurrentAlignment,
7
8
  isAnyParagraphFormatActive,
8
9
  isFormatBlocked,
9
10
  } from './formats/formatRules';
@@ -97,21 +98,22 @@ function buildState(
97
98
  isConflicting: editor.isActive('link'),
98
99
  isBlocking: isFormatBlocked('image', editor, htmlStyle),
99
100
  },
100
- alignment: 'left',
101
+ alignment: getCurrentAlignment(editor) ?? 'auto',
101
102
  };
102
103
  }
103
104
 
104
105
  function hashState(state: OnChangeStateEvent): string {
105
106
  return Object.values(state)
106
- .map((formatState) =>
107
- String(
107
+ .map((formatState) => {
108
+ if (typeof formatState === 'string') return formatState;
109
+ return String(
108
110
  getFormatHash(
109
111
  formatState.isActive,
110
112
  formatState.isConflicting,
111
113
  formatState.isBlocking
112
114
  )
113
- )
114
- )
115
+ );
116
+ })
115
117
  .join('');
116
118
  }
117
119
 
@@ -1,82 +1,70 @@
1
- import { useEffect, useRef } from 'react';
1
+ import { useEffect } from 'react';
2
2
  import { type Editor } from '@tiptap/react';
3
3
  import { getMarkRange, getMarksBetween } from '@tiptap/core';
4
- import type { OnLinkDetected } from '../types';
4
+ import type { EditorState } from '@tiptap/pm/state';
5
+ import type { MarkType } from '@tiptap/pm/model';
6
+ import { emitLinkDetected, type LinkEmitterRef } from './emitLinkDetected';
5
7
  import { tiptapPosToNativePos } from './positionMapping';
6
8
 
9
+ function findLinkRangeAt(
10
+ state: EditorState,
11
+ linkType: MarkType
12
+ ): { from: number; to: number } | null {
13
+ const $pos = state.selection.$from;
14
+ const direct = getMarkRange($pos, linkType);
15
+ if (direct) return direct;
16
+ if ($pos.pos > 0) {
17
+ const $before = state.doc.resolve($pos.pos - 1);
18
+ const before = getMarkRange($before, linkType);
19
+ if (before && before.to === $pos.pos) return before;
20
+ }
21
+ return null;
22
+ }
23
+
7
24
  export const useOnLinkDetected = (
8
25
  editor: Editor | null,
9
- onLinkDetected?: (e: OnLinkDetected) => void
26
+ ref: LinkEmitterRef
10
27
  ) => {
11
- const lastEmittedRef = useRef<OnLinkDetected | null>(null);
12
- const wasInLinkRef = useRef(false);
13
-
14
28
  useEffect(() => {
15
- if (!editor || !onLinkDetected) return;
29
+ if (!editor) return;
16
30
 
17
31
  const handleUpdate = () => {
18
32
  const { state } = editor;
19
33
  const linkType = state.schema.marks.link;
20
34
  if (!linkType) return;
21
35
 
22
- const $pos = state.selection.$from;
23
- const range = getMarkRange($pos, linkType);
36
+ const range = findLinkRangeAt(state, linkType);
24
37
 
25
38
  if (!range) {
26
- if (wasInLinkRef.current) {
27
- wasInLinkRef.current = false;
28
- onLinkDetected({
29
- text: '',
30
- url: '',
31
- start: 0,
32
- end: 0,
33
- });
34
- }
35
- lastEmittedRef.current = null;
39
+ const last = ref.current.lastEmitted;
40
+ if (!last || last.url === '') return;
41
+ emitLinkDetected(ref.current, {
42
+ text: '',
43
+ url: '',
44
+ start: 0,
45
+ end: 0,
46
+ });
36
47
  return;
37
48
  }
38
49
 
39
50
  const linkMark = getMarksBetween(range.from, range.to, state.doc).find(
40
51
  (entry) => entry.mark.type === linkType
41
52
  )?.mark;
53
+ if (!linkMark) return;
42
54
 
43
- if (!linkMark) {
44
- wasInLinkRef.current = false;
45
- lastEmittedRef.current = null;
46
- return;
47
- }
48
-
49
- wasInLinkRef.current = true;
50
-
51
- const { from, to } = range;
52
- const url = (linkMark.attrs.href as string | undefined) ?? '';
53
- const text = state.doc.textBetween(from, to, '\n');
54
- const start = tiptapPosToNativePos(state.doc, from);
55
- const end = tiptapPosToNativePos(state.doc, to);
56
-
57
- const next: OnLinkDetected = { text, url, start, end };
58
- const prev = lastEmittedRef.current;
59
-
60
- if (
61
- prev !== null &&
62
- prev.text === next.text &&
63
- prev.url === next.url &&
64
- prev.start === next.start &&
65
- prev.end === next.end
66
- ) {
67
- return;
68
- }
69
-
70
- lastEmittedRef.current = next;
71
- onLinkDetected(next);
55
+ emitLinkDetected(ref.current, {
56
+ text: state.doc.textBetween(range.from, range.to, '\n'),
57
+ url: (linkMark.attrs.href as string | undefined) ?? '',
58
+ start: tiptapPosToNativePos(state.doc, range.from),
59
+ end: tiptapPosToNativePos(state.doc, range.to),
60
+ });
72
61
  };
73
62
 
74
63
  handleUpdate();
75
64
  editor.on('transaction', handleUpdate);
76
65
 
77
66
  return () => {
78
- wasInLinkRef.current = false;
79
67
  editor.off('transaction', handleUpdate);
80
68
  };
81
- }, [editor, onLinkDetected]);
69
+ }, [editor, ref]);
82
70
  };
@@ -0,0 +1,22 @@
1
+ import { useEffect } from 'react';
2
+
3
+ export function usePressInteractions(
4
+ containerRef: React.RefObject<HTMLDivElement | null>
5
+ ) {
6
+ useEffect(() => {
7
+ const container = containerRef.current;
8
+ if (!container) return;
9
+
10
+ const handleClick = (e: MouseEvent) => {
11
+ const target = e.target as HTMLElement;
12
+
13
+ const anchor = target.closest('a');
14
+ if (anchor && container.contains(anchor)) {
15
+ e.preventDefault();
16
+ }
17
+ };
18
+
19
+ container.addEventListener('click', handleClick);
20
+ return () => container.removeEventListener('click', handleClick);
21
+ }, [containerRef]);
22
+ }
@@ -0,0 +1,12 @@
1
+ import { useEffect, useRef, type RefObject } from 'react';
2
+
3
+ //TODO: When upgrading to React 19.2 migrate to useEffectEvent instead
4
+ export const useStableRef = <T>(value: T): RefObject<T> => {
5
+ const ref = useRef<T>(value);
6
+
7
+ useEffect(() => {
8
+ ref.current = value;
9
+ }, [value]);
10
+
11
+ return ref;
12
+ };