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.
- package/android/src/main/java/com/swmansion/enriched/common/EnrichedSpanFlags.kt +39 -0
- package/android/src/main/java/com/swmansion/enriched/common/parser/EnrichedParser.java +43 -20
- package/android/src/main/java/com/swmansion/enriched/common/parser/EnrichedSpanFactory.kt +1 -0
- package/android/src/main/java/com/swmansion/enriched/text/EnrichedTextSpanFactory.kt +1 -0
- package/android/src/main/java/com/swmansion/enriched/text/EnrichedTextView.kt +2 -1
- package/android/src/main/java/com/swmansion/enriched/textinput/EnrichedTextInputSpannableFactory.kt +2 -1
- package/android/src/main/java/com/swmansion/enriched/textinput/EnrichedTextInputView.kt +8 -5
- package/android/src/main/java/com/swmansion/enriched/textinput/events/MentionHandler.kt +12 -2
- package/android/src/main/java/com/swmansion/enriched/textinput/events/OnMentionEvent.kt +6 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/styles/AlignmentStyles.kt +6 -7
- package/android/src/main/java/com/swmansion/enriched/textinput/styles/InlineStyles.kt +13 -1
- package/android/src/main/java/com/swmansion/enriched/textinput/styles/ListStyles.kt +4 -3
- package/android/src/main/java/com/swmansion/enriched/textinput/styles/ParagraphStyles.kt +4 -3
- package/android/src/main/java/com/swmansion/enriched/textinput/styles/ParametrizedStyles.kt +38 -11
- package/android/src/main/java/com/swmansion/enriched/textinput/utils/EnrichedSelection.kt +2 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/utils/EnrichedSpannable.kt +2 -1
- package/android/src/main/java/com/swmansion/enriched/textinput/utils/Utils.kt +2 -1
- package/cpp/parser/GumboNormalizer.c +61 -3
- package/cpp/tests/GumboParserTest.cpp +54 -0
- package/ios/EnrichedTextInputView.mm +37 -25
- package/ios/extensions/LayoutManagerExtension.mm +73 -62
- package/ios/extensions/StringExtension.mm +75 -0
- package/ios/htmlParser/HtmlParser.h +4 -1
- package/ios/htmlParser/HtmlParser.mm +23 -5
- package/ios/inputHtmlParser/InputHtmlParser.mm +6 -3
- package/ios/interfaces/StyleHeaders.h +4 -0
- package/ios/styles/LinkStyle.mm +20 -34
- package/ios/styles/MentionStyle.mm +55 -4
- package/ios/textHtmlParser/TextHtmlParser.mm +2 -1
- package/ios/utils/ParagraphAttributesUtils.mm +7 -0
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/utils/expandMentionStylesForIndicators.js +2 -3
- package/lib/module/utils/expandMentionStylesForIndicators.js.map +1 -1
- package/lib/module/utils/normalizeHtmlStyle.js +1 -1
- package/lib/module/utils/normalizeHtmlStyle.js.map +1 -1
- package/lib/module/web/EnrichedText.css +343 -0
- package/lib/module/web/EnrichedText.js +55 -0
- package/lib/module/web/EnrichedText.js.map +1 -0
- package/lib/module/web/EnrichedTextInput.js +54 -52
- package/lib/module/web/EnrichedTextInput.js.map +1 -1
- package/lib/module/web/constants/brokenImageGlyph.js +9 -0
- package/lib/module/web/constants/brokenImageGlyph.js.map +1 -0
- package/lib/module/web/constants/classNames.js +5 -0
- package/lib/module/web/constants/classNames.js.map +1 -0
- package/lib/module/web/emitLinkDetected.js +12 -0
- package/lib/module/web/emitLinkDetected.js.map +1 -0
- package/lib/module/web/formats/EnrichedBlockquote.js +1 -1
- package/lib/module/web/formats/EnrichedBlockquote.js.map +1 -1
- package/lib/module/web/formats/EnrichedCheckboxList.js +2 -2
- package/lib/module/web/formats/EnrichedCheckboxList.js.map +1 -1
- package/lib/module/web/formats/EnrichedCodeBlock.js +1 -1
- package/lib/module/web/formats/EnrichedCodeBlock.js.map +1 -1
- package/lib/module/web/formats/EnrichedHeading.js +1 -1
- package/lib/module/web/formats/EnrichedHeading.js.map +1 -1
- package/lib/module/web/formats/EnrichedImageNodeView.js +1 -1
- package/lib/module/web/formats/EnrichedImageNodeView.js.map +1 -1
- package/lib/module/web/formats/EnrichedLink.js +43 -3
- package/lib/module/web/formats/EnrichedLink.js.map +1 -1
- package/lib/module/web/formats/EnrichedOrderedList.js +2 -2
- package/lib/module/web/formats/EnrichedOrderedList.js.map +1 -1
- package/lib/module/web/formats/EnrichedTextAlign.js +82 -0
- package/lib/module/web/formats/EnrichedTextAlign.js.map +1 -0
- package/lib/module/web/formats/EnrichedUnorderedList.js +2 -2
- package/lib/module/web/formats/EnrichedUnorderedList.js.map +1 -1
- package/lib/module/web/formats/applyWrappingListToSelection.js +3 -2
- package/lib/module/web/formats/applyWrappingListToSelection.js.map +1 -1
- package/lib/module/web/formats/formatRules.js +39 -3
- package/lib/module/web/formats/formatRules.js.map +1 -1
- package/lib/module/web/formats/listKeyboard.js +4 -1
- package/lib/module/web/formats/listKeyboard.js.map +1 -1
- package/lib/module/web/normalization/checkboxHtmlNormalizer.js.map +1 -0
- package/lib/module/web/normalization/htmlNormalizer.js +514 -0
- package/lib/module/web/normalization/htmlNormalizer.js.map +1 -0
- package/lib/module/web/normalization/prepareHtmlForWeb.js +78 -0
- package/lib/module/web/normalization/prepareHtmlForWeb.js.map +1 -0
- package/lib/module/web/{tiptapHtmlNormalizer.js → normalization/tiptapHtmlNormalizer.js} +6 -2
- package/lib/module/web/normalization/tiptapHtmlNormalizer.js.map +1 -0
- package/lib/module/web/pmPlugins/AutolinkPlugin/autolinkRegex.js +27 -0
- package/lib/module/web/pmPlugins/AutolinkPlugin/autolinkRegex.js.map +1 -0
- package/lib/module/web/pmPlugins/AutolinkPlugin/index.js +178 -0
- package/lib/module/web/pmPlugins/AutolinkPlugin/index.js.map +1 -0
- package/lib/module/web/pmPlugins/MentionPlugin/index.js +1 -1
- package/lib/module/web/pmPlugins/MentionPlugin/index.js.map +1 -1
- package/lib/module/web/pmPlugins/MentionPlugin/makeMentionPluginState.js +23 -4
- package/lib/module/web/pmPlugins/MentionPlugin/makeMentionPluginState.js.map +1 -1
- package/lib/module/web/pmPlugins/MentionPlugin/setMention.js +17 -18
- package/lib/module/web/pmPlugins/MentionPlugin/setMention.js.map +1 -1
- package/lib/module/web/pmPlugins/MentionPlugin/useMentionEvents.js +105 -0
- package/lib/module/web/pmPlugins/MentionPlugin/useMentionEvents.js.map +1 -0
- package/lib/module/web/pmPlugins/ShortcutPlugin.js +11 -0
- package/lib/module/web/pmPlugins/ShortcutPlugin.js.map +1 -1
- package/lib/module/web/pmPlugins/StrictMarksPlugin.js +2 -0
- package/lib/module/web/pmPlugins/StrictMarksPlugin.js.map +1 -1
- package/lib/module/web/pmPlugins/StripBoldInStyledHeadingsPlugin.js.map +1 -1
- package/lib/module/web/sanitization/htmlSanitizer.js +10 -0
- package/lib/module/web/sanitization/htmlSanitizer.js.map +1 -0
- package/lib/module/web/styleConversion/buildMentionRulesCSS.js +12 -6
- package/lib/module/web/styleConversion/buildMentionRulesCSS.js.map +1 -1
- package/lib/module/web/styleConversion/enrichedBaseStyleToCSSProperties.js +159 -0
- package/lib/module/web/styleConversion/enrichedBaseStyleToCSSProperties.js.map +1 -0
- package/lib/module/web/styleConversion/enrichedInputStyleToCSSProperties.js +3 -155
- package/lib/module/web/styleConversion/enrichedInputStyleToCSSProperties.js.map +1 -1
- package/lib/module/web/styleConversion/enrichedTextStyleToCSSProperties.js +44 -0
- package/lib/module/web/styleConversion/enrichedTextStyleToCSSProperties.js.map +1 -0
- package/lib/module/web/styleConversion/{enrichedInputThemingToCSSProperties.js → enrichedThemingToCSSProperties.js} +3 -3
- package/lib/module/web/styleConversion/enrichedThemingToCSSProperties.js.map +1 -0
- package/lib/module/web/styleConversion/htmlStyleToCSSVariables.js +87 -64
- package/lib/module/web/styleConversion/htmlStyleToCSSVariables.js.map +1 -1
- package/lib/module/web/styleConversion/inlineImageCSSVariables.js +7 -0
- package/lib/module/web/styleConversion/inlineImageCSSVariables.js.map +1 -0
- package/lib/module/web/useImageErrorFallback.js +33 -0
- package/lib/module/web/useImageErrorFallback.js.map +1 -0
- package/lib/module/web/useOnChangeHtml.js +1 -1
- package/lib/module/web/useOnChangeHtml.js.map +1 -1
- package/lib/module/web/useOnChangeState.js +6 -3
- package/lib/module/web/useOnChangeState.js.map +1 -1
- package/lib/module/web/useOnLinkDetected.js +32 -45
- package/lib/module/web/useOnLinkDetected.js.map +1 -1
- package/lib/module/web/usePressInteractions.js +19 -0
- package/lib/module/web/usePressInteractions.js.map +1 -0
- package/lib/module/web/useStableRef.js +13 -0
- package/lib/module/web/useStableRef.js.map +1 -0
- package/lib/typescript/src/index.d.ts +1 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/native/EnrichedText.d.ts +1 -1
- package/lib/typescript/src/native/EnrichedText.d.ts.map +1 -1
- package/lib/typescript/src/native/EnrichedTextInput.d.ts +1 -1
- package/lib/typescript/src/native/EnrichedTextInput.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +1 -1
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/utils/expandMentionStylesForIndicators.d.ts +1 -1
- package/lib/typescript/src/utils/expandMentionStylesForIndicators.d.ts.map +1 -1
- package/lib/typescript/src/utils/normalizeHtmlStyle.d.ts.map +1 -1
- package/lib/typescript/src/web/EnrichedText.d.ts +4 -0
- package/lib/typescript/src/web/EnrichedText.d.ts.map +1 -0
- package/lib/typescript/src/web/EnrichedTextInput.d.ts +2 -2
- package/lib/typescript/src/web/EnrichedTextInput.d.ts.map +1 -1
- package/lib/typescript/src/web/constants/brokenImageGlyph.d.ts +3 -0
- package/lib/typescript/src/web/constants/brokenImageGlyph.d.ts.map +1 -0
- package/lib/typescript/src/web/constants/classNames.d.ts +3 -0
- package/lib/typescript/src/web/constants/classNames.d.ts.map +1 -0
- package/lib/typescript/src/web/emitLinkDetected.d.ts +10 -0
- package/lib/typescript/src/web/emitLinkDetected.d.ts.map +1 -0
- package/lib/typescript/src/web/formats/EnrichedBlockquote.d.ts.map +1 -1
- package/lib/typescript/src/web/formats/EnrichedCheckboxList.d.ts.map +1 -1
- package/lib/typescript/src/web/formats/EnrichedCodeBlock.d.ts.map +1 -1
- package/lib/typescript/src/web/formats/EnrichedHeading.d.ts.map +1 -1
- package/lib/typescript/src/web/formats/EnrichedImageNodeView.d.ts +1 -1
- package/lib/typescript/src/web/formats/EnrichedImageNodeView.d.ts.map +1 -1
- package/lib/typescript/src/web/formats/EnrichedLink.d.ts +4 -1
- package/lib/typescript/src/web/formats/EnrichedLink.d.ts.map +1 -1
- package/lib/typescript/src/web/formats/EnrichedOrderedList.d.ts.map +1 -1
- package/lib/typescript/src/web/formats/EnrichedTextAlign.d.ts +2 -0
- package/lib/typescript/src/web/formats/EnrichedTextAlign.d.ts.map +1 -0
- package/lib/typescript/src/web/formats/EnrichedUnorderedList.d.ts.map +1 -1
- package/lib/typescript/src/web/formats/applyWrappingListToSelection.d.ts.map +1 -1
- package/lib/typescript/src/web/formats/formatRules.d.ts +3 -1
- package/lib/typescript/src/web/formats/formatRules.d.ts.map +1 -1
- package/lib/typescript/src/web/formats/listKeyboard.d.ts.map +1 -1
- package/lib/typescript/src/web/normalization/checkboxHtmlNormalizer.d.ts.map +1 -0
- package/lib/typescript/src/web/normalization/htmlNormalizer.d.ts +2 -0
- package/lib/typescript/src/web/normalization/htmlNormalizer.d.ts.map +1 -0
- package/lib/typescript/src/web/normalization/prepareHtmlForWeb.d.ts +2 -0
- package/lib/typescript/src/web/normalization/prepareHtmlForWeb.d.ts.map +1 -0
- package/lib/typescript/src/web/normalization/tiptapHtmlNormalizer.d.ts +3 -0
- package/lib/typescript/src/web/normalization/tiptapHtmlNormalizer.d.ts.map +1 -0
- package/lib/typescript/src/web/pmPlugins/AutolinkPlugin/autolinkRegex.d.ts +7 -0
- package/lib/typescript/src/web/pmPlugins/AutolinkPlugin/autolinkRegex.d.ts.map +1 -0
- package/lib/typescript/src/web/pmPlugins/AutolinkPlugin/index.d.ts +8 -0
- package/lib/typescript/src/web/pmPlugins/AutolinkPlugin/index.d.ts.map +1 -0
- package/lib/typescript/src/web/pmPlugins/MentionPlugin/index.d.ts +1 -1
- package/lib/typescript/src/web/pmPlugins/MentionPlugin/index.d.ts.map +1 -1
- package/lib/typescript/src/web/pmPlugins/MentionPlugin/makeMentionPluginState.d.ts.map +1 -1
- package/lib/typescript/src/web/pmPlugins/MentionPlugin/setMention.d.ts.map +1 -1
- package/lib/typescript/src/web/pmPlugins/MentionPlugin/useMentionEvents.d.ts +4 -0
- package/lib/typescript/src/web/pmPlugins/MentionPlugin/useMentionEvents.d.ts.map +1 -0
- package/lib/typescript/src/web/pmPlugins/ShortcutPlugin.d.ts.map +1 -1
- package/lib/typescript/src/web/pmPlugins/StrictMarksPlugin.d.ts.map +1 -1
- package/lib/typescript/src/web/pmPlugins/StripBoldInStyledHeadingsPlugin.d.ts +6 -1
- package/lib/typescript/src/web/pmPlugins/StripBoldInStyledHeadingsPlugin.d.ts.map +1 -1
- package/lib/typescript/src/web/sanitization/htmlSanitizer.d.ts +2 -0
- package/lib/typescript/src/web/sanitization/htmlSanitizer.d.ts.map +1 -0
- package/lib/typescript/src/web/styleConversion/buildMentionRulesCSS.d.ts +2 -2
- package/lib/typescript/src/web/styleConversion/buildMentionRulesCSS.d.ts.map +1 -1
- package/lib/typescript/src/web/styleConversion/enrichedBaseStyleToCSSProperties.d.ts +8 -0
- package/lib/typescript/src/web/styleConversion/enrichedBaseStyleToCSSProperties.d.ts.map +1 -0
- package/lib/typescript/src/web/styleConversion/enrichedInputStyleToCSSProperties.d.ts +2 -5
- package/lib/typescript/src/web/styleConversion/enrichedInputStyleToCSSProperties.d.ts.map +1 -1
- package/lib/typescript/src/web/styleConversion/enrichedTextStyleToCSSProperties.d.ts +4 -0
- package/lib/typescript/src/web/styleConversion/enrichedTextStyleToCSSProperties.d.ts.map +1 -0
- package/lib/typescript/src/web/styleConversion/{enrichedInputThemingToCSSProperties.d.ts → enrichedThemingToCSSProperties.d.ts} +1 -1
- package/lib/typescript/src/web/styleConversion/enrichedThemingToCSSProperties.d.ts.map +1 -0
- package/lib/typescript/src/web/styleConversion/htmlStyleToCSSVariables.d.ts +5 -4
- package/lib/typescript/src/web/styleConversion/htmlStyleToCSSVariables.d.ts.map +1 -1
- package/lib/typescript/src/web/styleConversion/inlineImageCSSVariables.d.ts +3 -0
- package/lib/typescript/src/web/styleConversion/inlineImageCSSVariables.d.ts.map +1 -0
- package/lib/typescript/src/web/useImageErrorFallback.d.ts +3 -0
- package/lib/typescript/src/web/useImageErrorFallback.d.ts.map +1 -0
- package/lib/typescript/src/web/useOnChangeState.d.ts.map +1 -1
- package/lib/typescript/src/web/useOnLinkDetected.d.ts +2 -2
- package/lib/typescript/src/web/useOnLinkDetected.d.ts.map +1 -1
- package/lib/typescript/src/web/usePressInteractions.d.ts +2 -0
- package/lib/typescript/src/web/usePressInteractions.d.ts.map +1 -0
- package/lib/typescript/src/web/useStableRef.d.ts +3 -0
- package/lib/typescript/src/web/useStableRef.d.ts.map +1 -0
- package/package.json +54 -28
- package/src/index.tsx +1 -0
- package/src/types.ts +5 -3
- package/src/utils/expandMentionStylesForIndicators.ts +3 -3
- package/src/utils/normalizeHtmlStyle.ts +2 -1
- package/src/web/EnrichedText.css +343 -0
- package/src/web/EnrichedText.tsx +82 -0
- package/src/web/EnrichedTextInput.tsx +69 -55
- package/src/web/constants/brokenImageGlyph.ts +7 -0
- package/src/web/constants/classNames.ts +2 -0
- package/src/web/emitLinkDetected.ts +29 -0
- package/src/web/formats/EnrichedBlockquote.ts +2 -1
- package/src/web/formats/EnrichedCheckboxList.ts +5 -2
- package/src/web/formats/EnrichedCodeBlock.ts +2 -1
- package/src/web/formats/EnrichedHeading.ts +2 -1
- package/src/web/formats/EnrichedImageNodeView.tsx +1 -3
- package/src/web/formats/EnrichedLink.ts +59 -4
- package/src/web/formats/EnrichedOrderedList.ts +5 -2
- package/src/web/formats/EnrichedTextAlign.ts +88 -0
- package/src/web/formats/EnrichedUnorderedList.ts +5 -2
- package/src/web/formats/applyWrappingListToSelection.ts +4 -4
- package/src/web/formats/formatRules.ts +48 -4
- package/src/web/formats/listKeyboard.ts +6 -1
- package/src/web/normalization/htmlNormalizer.ts +656 -0
- package/src/web/normalization/prepareHtmlForWeb.ts +93 -0
- package/src/web/{tiptapHtmlNormalizer.ts → normalization/tiptapHtmlNormalizer.ts} +12 -2
- package/src/web/pmPlugins/AutolinkPlugin/autolinkRegex.ts +43 -0
- package/src/web/pmPlugins/AutolinkPlugin/index.ts +274 -0
- package/src/web/pmPlugins/MentionPlugin/index.ts +1 -1
- package/src/web/pmPlugins/MentionPlugin/makeMentionPluginState.ts +32 -5
- package/src/web/pmPlugins/MentionPlugin/setMention.ts +19 -29
- package/src/web/pmPlugins/MentionPlugin/useMentionEvents.ts +114 -0
- package/src/web/pmPlugins/ShortcutPlugin.ts +7 -0
- package/src/web/pmPlugins/StrictMarksPlugin.ts +3 -0
- package/src/web/pmPlugins/StripBoldInStyledHeadingsPlugin.ts +41 -36
- package/src/web/sanitization/htmlSanitizer.ts +8 -0
- package/src/web/styleConversion/buildMentionRulesCSS.ts +30 -11
- package/src/web/styleConversion/enrichedBaseStyleToCSSProperties.ts +228 -0
- package/src/web/styleConversion/enrichedInputStyleToCSSProperties.ts +6 -217
- package/src/web/styleConversion/enrichedTextStyleToCSSProperties.ts +53 -0
- package/src/web/styleConversion/{enrichedInputThemingToCSSProperties.ts → enrichedThemingToCSSProperties.ts} +4 -4
- package/src/web/styleConversion/htmlStyleToCSSVariables.ts +121 -70
- package/src/web/styleConversion/inlineImageCSSVariables.ts +6 -0
- package/src/web/useImageErrorFallback.ts +37 -0
- package/src/web/useOnChangeHtml.ts +1 -1
- package/src/web/useOnChangeState.ts +7 -5
- package/src/web/useOnLinkDetected.ts +38 -50
- package/src/web/usePressInteractions.tsx +22 -0
- package/src/web/useStableRef.ts +12 -0
- package/lib/module/web/EnrichedTextInput.css +0 -201
- package/lib/module/web/checkboxHtmlNormalizer.js.map +0 -1
- package/lib/module/web/pmPlugins/MentionPlugin/subscribeMentionEvents.js +0 -100
- package/lib/module/web/pmPlugins/MentionPlugin/subscribeMentionEvents.js.map +0 -1
- package/lib/module/web/styleConversion/enrichedInputThemingToCSSProperties.js.map +0 -1
- package/lib/module/web/tiptapHtmlNormalizer.js.map +0 -1
- package/lib/typescript/src/web/checkboxHtmlNormalizer.d.ts.map +0 -1
- package/lib/typescript/src/web/pmPlugins/MentionPlugin/subscribeMentionEvents.d.ts +0 -4
- package/lib/typescript/src/web/pmPlugins/MentionPlugin/subscribeMentionEvents.d.ts.map +0 -1
- package/lib/typescript/src/web/styleConversion/enrichedInputThemingToCSSProperties.d.ts.map +0 -1
- package/lib/typescript/src/web/tiptapHtmlNormalizer.d.ts +0 -3
- package/lib/typescript/src/web/tiptapHtmlNormalizer.d.ts.map +0 -1
- package/src/web/EnrichedTextInput.css +0 -201
- package/src/web/pmPlugins/MentionPlugin/subscribeMentionEvents.ts +0 -105
- /package/lib/module/web/{checkboxHtmlNormalizer.js → normalization/checkboxHtmlNormalizer.js} +0 -0
- /package/lib/typescript/src/web/{checkboxHtmlNormalizer.d.ts → normalization/checkboxHtmlNormalizer.d.ts} +0 -0
- /package/src/web/{checkboxHtmlNormalizer.ts → normalization/checkboxHtmlNormalizer.ts} +0 -0
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
|
+
useCallback,
|
|
2
3
|
useEffect,
|
|
3
4
|
useImperativeHandle,
|
|
4
5
|
useMemo,
|
|
5
6
|
useRef,
|
|
6
7
|
type CSSProperties,
|
|
7
8
|
} from 'react';
|
|
8
|
-
import './
|
|
9
|
+
import './EnrichedText.css';
|
|
9
10
|
import type { Node } from '@tiptap/pm/model';
|
|
10
11
|
import type {
|
|
11
12
|
EnrichedTextInputInstance,
|
|
@@ -32,13 +33,14 @@ import { useOnChangeHtml } from './useOnChangeHtml';
|
|
|
32
33
|
import { useOnChangeText } from './useOnChangeText';
|
|
33
34
|
import { useOnChangeState } from './useOnChangeState';
|
|
34
35
|
import { useOnLinkDetected } from './useOnLinkDetected';
|
|
36
|
+
import type { LinkEmitterState } from './emitLinkDetected';
|
|
35
37
|
import {
|
|
36
38
|
prepareHtmlForTiptap,
|
|
37
39
|
normalizeHtmlFromTiptap,
|
|
38
|
-
} from './tiptapHtmlNormalizer';
|
|
40
|
+
} from './normalization/tiptapHtmlNormalizer';
|
|
39
41
|
import { ENRICHED_TEXT_INPUT_DEFAULT_PROPS } from '../utils/EnrichedTextInputDefaultProps';
|
|
40
42
|
import { enrichedInputStyleToCSSProperties } from './styleConversion/enrichedInputStyleToCSSProperties';
|
|
41
|
-
import { enrichedInputThemingToCSSProperties } from './styleConversion/
|
|
43
|
+
import { enrichedInputThemingToCSSProperties } from './styleConversion/enrichedThemingToCSSProperties';
|
|
42
44
|
import { buildMentionRulesCSS } from './styleConversion/buildMentionRulesCSS';
|
|
43
45
|
import {
|
|
44
46
|
htmlStyleToCSSVariables,
|
|
@@ -60,6 +62,7 @@ import { EnrichedUnorderedList } from './formats/EnrichedUnorderedList';
|
|
|
60
62
|
import { EnrichedOrderedList } from './formats/EnrichedOrderedList';
|
|
61
63
|
import { EnrichedCheckboxItem } from './formats/EnrichedCheckboxItem';
|
|
62
64
|
import { EnrichedCheckboxList } from './formats/EnrichedCheckboxList';
|
|
65
|
+
import { EnrichedTextAlign } from './formats/EnrichedTextAlign';
|
|
63
66
|
import { StripBoldInStyledHeadingsPlugin } from './pmPlugins/StripBoldInStyledHeadingsPlugin';
|
|
64
67
|
import { StrictMarksPlugin } from './pmPlugins/StrictMarksPlugin';
|
|
65
68
|
import { MergeAdjacentSameKindBlocksPlugin } from './pmPlugins/MergeAdjacentSameKindBlocksPlugin';
|
|
@@ -69,11 +72,15 @@ import {
|
|
|
69
72
|
MentionPlugin,
|
|
70
73
|
setMention,
|
|
71
74
|
startMention,
|
|
72
|
-
|
|
75
|
+
useMentionEvents,
|
|
73
76
|
} from './pmPlugins/MentionPlugin';
|
|
74
77
|
import { StripMarksOnImagePlugin } from './pmPlugins/StripMarksOnImagePlugin';
|
|
75
78
|
import { ShortcutPlugin } from './pmPlugins/ShortcutPlugin';
|
|
76
79
|
import { returnKeyTypeToEnterKeyHint } from './returnKeyTypeToEnterKeyHint';
|
|
80
|
+
import { ENRICHED_TEXT_INPUT_CLASSNAME } from './constants/classNames';
|
|
81
|
+
import { AutolinkPlugin } from './pmPlugins/AutolinkPlugin';
|
|
82
|
+
import { useStableRef } from './useStableRef';
|
|
83
|
+
|
|
77
84
|
function runFocused(
|
|
78
85
|
editor: Editor,
|
|
79
86
|
apply: (chain: ChainedCommands) => ChainedCommands
|
|
@@ -110,60 +117,39 @@ export const EnrichedTextInput = ({
|
|
|
110
117
|
onStartMention,
|
|
111
118
|
onChangeMention,
|
|
112
119
|
onEndMention,
|
|
120
|
+
linkRegex,
|
|
113
121
|
htmlStyle,
|
|
122
|
+
useHtmlNormalizer,
|
|
114
123
|
}: EnrichedTextInputProps) => {
|
|
115
124
|
const tiptapContent =
|
|
116
|
-
defaultValue != null
|
|
125
|
+
defaultValue != null
|
|
126
|
+
? prepareHtmlForTiptap(defaultValue, useHtmlNormalizer)
|
|
127
|
+
: defaultValue;
|
|
117
128
|
|
|
118
129
|
const resolvedHtmlStyle = useMemo(
|
|
119
130
|
() => mergeWithDefaultHtmlStyle(htmlStyle),
|
|
120
131
|
[htmlStyle]
|
|
121
132
|
);
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
useEffect(() => {
|
|
125
|
-
htmlStyleRef.current = resolvedHtmlStyle;
|
|
126
|
-
}, [resolvedHtmlStyle]);
|
|
127
|
-
|
|
128
|
-
const onPasteImagesRef = useRef(onPasteImages);
|
|
129
|
-
useEffect(() => {
|
|
130
|
-
onPasteImagesRef.current = onPasteImages;
|
|
131
|
-
}, [onPasteImages]);
|
|
132
|
-
|
|
133
|
-
const mentionIndicatorsRef = useRef(mentionIndicators);
|
|
134
|
-
useEffect(() => {
|
|
135
|
-
mentionIndicatorsRef.current = mentionIndicators;
|
|
136
|
-
}, [mentionIndicators]);
|
|
137
|
-
|
|
138
|
-
const mentionCallbacksRef = useRef({
|
|
139
|
-
onStartMention,
|
|
140
|
-
onChangeMention,
|
|
141
|
-
onEndMention,
|
|
142
|
-
onMentionDetected,
|
|
143
|
-
});
|
|
144
|
-
useEffect(() => {
|
|
145
|
-
mentionCallbacksRef.current = {
|
|
133
|
+
const mentionCallbacks = useMemo(
|
|
134
|
+
() => ({
|
|
146
135
|
onStartMention,
|
|
147
136
|
onChangeMention,
|
|
148
137
|
onEndMention,
|
|
149
138
|
onMentionDetected,
|
|
150
|
-
}
|
|
151
|
-
|
|
139
|
+
}),
|
|
140
|
+
[onStartMention, onChangeMention, onEndMention, onMentionDetected]
|
|
141
|
+
);
|
|
152
142
|
|
|
153
|
-
const
|
|
154
|
-
const
|
|
155
|
-
const
|
|
156
|
-
const
|
|
143
|
+
const htmlStyleRef = useStableRef(resolvedHtmlStyle);
|
|
144
|
+
const onPasteImagesRef = useStableRef(onPasteImages);
|
|
145
|
+
const mentionIndicatorsRef = useStableRef(mentionIndicators);
|
|
146
|
+
const submitBehaviorRef = useStableRef(submitBehavior);
|
|
147
|
+
const onSubmitEditingRef = useStableRef(onSubmitEditing);
|
|
148
|
+
const onKeyPressRef = useStableRef(onKeyPress);
|
|
149
|
+
const useHtmlNormalizerRef = useStableRef(useHtmlNormalizer);
|
|
150
|
+
const mentionCallbacksRef = useStableRef(mentionCallbacks);
|
|
157
151
|
|
|
158
|
-
|
|
159
|
-
submitBehaviorRef.current = submitBehavior;
|
|
160
|
-
}, [submitBehavior]);
|
|
161
|
-
useEffect(() => {
|
|
162
|
-
onSubmitEditingRef.current = onSubmitEditing;
|
|
163
|
-
}, [onSubmitEditing]);
|
|
164
|
-
useEffect(() => {
|
|
165
|
-
onKeyPressRef.current = onKeyPress;
|
|
166
|
-
}, [onKeyPress]);
|
|
152
|
+
const editorInstanceRef = useRef<Editor | null>(null);
|
|
167
153
|
|
|
168
154
|
const handleKeyDown = (doc: Node, event: KeyboardEvent): boolean => {
|
|
169
155
|
onKeyPressRef.current?.(adaptWebToNativeEvent(event, { key: event.key }));
|
|
@@ -185,6 +171,16 @@ export const EnrichedTextInput = ({
|
|
|
185
171
|
return false;
|
|
186
172
|
};
|
|
187
173
|
|
|
174
|
+
const linkEmitterRef = useRef<LinkEmitterState>({
|
|
175
|
+
linkRegex,
|
|
176
|
+
onLinkDetected,
|
|
177
|
+
lastEmitted: null,
|
|
178
|
+
});
|
|
179
|
+
useEffect(() => {
|
|
180
|
+
linkEmitterRef.current.linkRegex = linkRegex;
|
|
181
|
+
linkEmitterRef.current.onLinkDetected = onLinkDetected;
|
|
182
|
+
}, [linkRegex, onLinkDetected]);
|
|
183
|
+
|
|
188
184
|
const extensions = useMemo(
|
|
189
185
|
() => [
|
|
190
186
|
Document,
|
|
@@ -196,7 +192,9 @@ export const EnrichedTextInput = ({
|
|
|
196
192
|
EnrichedUnderline,
|
|
197
193
|
EnrichedStrike,
|
|
198
194
|
EnrichedCode,
|
|
199
|
-
EnrichedLink
|
|
195
|
+
EnrichedLink.configure({
|
|
196
|
+
getLinkRegex: () => linkEmitterRef.current.linkRegex,
|
|
197
|
+
}),
|
|
200
198
|
EnrichedImage,
|
|
201
199
|
EnrichedMention,
|
|
202
200
|
EnrichedHeading,
|
|
@@ -207,6 +205,7 @@ export const EnrichedTextInput = ({
|
|
|
207
205
|
EnrichedUnorderedList,
|
|
208
206
|
EnrichedOrderedList,
|
|
209
207
|
EnrichedCheckboxList,
|
|
208
|
+
EnrichedTextAlign,
|
|
210
209
|
StripMarksInCodeBlockPlugin,
|
|
211
210
|
StripMarksOnImagePlugin,
|
|
212
211
|
StripBoldInStyledHeadingsPlugin.configure({
|
|
@@ -220,12 +219,15 @@ export const EnrichedTextInput = ({
|
|
|
220
219
|
ShortcutPlugin.configure({
|
|
221
220
|
getHtmlStyle: () => htmlStyleRef.current,
|
|
222
221
|
}),
|
|
222
|
+
AutolinkPlugin.configure({
|
|
223
|
+
getLinkEmitter: () => linkEmitterRef.current,
|
|
224
|
+
}),
|
|
223
225
|
Placeholder.configure({
|
|
224
226
|
placeholder,
|
|
225
227
|
showOnlyWhenEditable: true,
|
|
226
228
|
}),
|
|
227
229
|
],
|
|
228
|
-
[placeholder]
|
|
230
|
+
[placeholder, htmlStyleRef, mentionIndicatorsRef]
|
|
229
231
|
);
|
|
230
232
|
|
|
231
233
|
const editor = useEditor(
|
|
@@ -264,6 +266,9 @@ export const EnrichedTextInput = ({
|
|
|
264
266
|
autoCapitalize,
|
|
265
267
|
enterkeyhint: returnKeyTypeToEnterKeyHint(returnKeyType),
|
|
266
268
|
},
|
|
269
|
+
transformPastedHTML: (html) => {
|
|
270
|
+
return prepareHtmlForTiptap(html, useHtmlNormalizerRef.current);
|
|
271
|
+
},
|
|
267
272
|
},
|
|
268
273
|
},
|
|
269
274
|
[tiptapContent, extensions]
|
|
@@ -291,15 +296,16 @@ export const EnrichedTextInput = ({
|
|
|
291
296
|
editor?.commands.normalizeBoldInStyledHeadings();
|
|
292
297
|
}, [editor, resolvedHtmlStyle]);
|
|
293
298
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
299
|
+
const getMentionCallbacks = useCallback(
|
|
300
|
+
() => mentionCallbacksRef.current,
|
|
301
|
+
[mentionCallbacksRef]
|
|
302
|
+
);
|
|
298
303
|
|
|
304
|
+
useMentionEvents(editor, getMentionCallbacks);
|
|
299
305
|
useOnChangeHtml(editor, onChangeHtml);
|
|
300
306
|
useOnChangeText(editor, onChangeText);
|
|
301
307
|
useOnChangeState(editor, resolvedHtmlStyle, onChangeState);
|
|
302
|
-
useOnLinkDetected(editor,
|
|
308
|
+
useOnLinkDetected(editor, linkEmitterRef);
|
|
303
309
|
|
|
304
310
|
useImperativeHandle(
|
|
305
311
|
ref,
|
|
@@ -307,7 +313,9 @@ export const EnrichedTextInput = ({
|
|
|
307
313
|
focus: () => editor.commands.focus(),
|
|
308
314
|
blur: () => editor.commands.blur(),
|
|
309
315
|
setValue: (value: string) =>
|
|
310
|
-
editor.commands.setContent(
|
|
316
|
+
editor.commands.setContent(
|
|
317
|
+
prepareHtmlForTiptap(value, useHtmlNormalizerRef.current)
|
|
318
|
+
),
|
|
311
319
|
setSelection: (start, end) => {
|
|
312
320
|
const doc = editor.state.doc;
|
|
313
321
|
runFocused(editor, (c) =>
|
|
@@ -354,9 +362,15 @@ export const EnrichedTextInput = ({
|
|
|
354
362
|
measureInWindow: () => {},
|
|
355
363
|
measureLayout: () => {},
|
|
356
364
|
setNativeProps: () => {},
|
|
357
|
-
setTextAlignment: () => {
|
|
365
|
+
setTextAlignment: (alignment) => {
|
|
366
|
+
if (alignment === 'auto') {
|
|
367
|
+
runFocused(editor, (c) => c.unsetTextAlign());
|
|
368
|
+
} else {
|
|
369
|
+
runFocused(editor, (c) => c.setTextAlign(alignment));
|
|
370
|
+
}
|
|
371
|
+
},
|
|
358
372
|
}),
|
|
359
|
-
[editor]
|
|
373
|
+
[editor, mentionIndicatorsRef, useHtmlNormalizerRef]
|
|
360
374
|
);
|
|
361
375
|
|
|
362
376
|
const editorStyle: CSSProperties = useMemo(
|
|
@@ -394,7 +408,7 @@ export const EnrichedTextInput = ({
|
|
|
394
408
|
{mentionRulesCSS ? <style>{mentionRulesCSS}</style> : null}
|
|
395
409
|
<EditorContent
|
|
396
410
|
editor={editor}
|
|
397
|
-
className=
|
|
411
|
+
className={ENRICHED_TEXT_INPUT_CLASSNAME}
|
|
398
412
|
style={finalStyle}
|
|
399
413
|
data-placeholder={placeholder}
|
|
400
414
|
/>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// SVG path of the broken-image glyph, shared between the editor's
|
|
2
|
+
// image node view (rendered as an inline <svg>).
|
|
3
|
+
export const BROKEN_IMAGE_PATH_D =
|
|
4
|
+
'M200,840Q167,840 143.5,816.5Q120,793 120,760L120,200Q120,167 143.5,143.5Q167,120 200,120L760,120Q793,120 816.5,143.5Q840,167 840,200L840,760Q840,793 816.5,816.5Q793,840 760,840L200,840ZM240,503L400,343L560,503L720,343L760,383L760,200Q760,200 760,200Q760,200 760,200L200,200Q200,200 200,200Q200,200 200,200L200,463L240,503ZM200,760L760,760Q760,760 760,760Q760,760 760,760L760,496L720,456L560,616L400,456L240,616L200,576L200,760Q200,760 200,760Q200,760 200,760ZM200,760L200,760Q200,760 200,760Q200,760 200,760L200,496L200,576L200,463L200,383L200,200Q200,200 200,200Q200,200 200,200L200,200Q200,200 200,200Q200,200 200,200L200,463L200,463L200,576L200,576L200,760Q200,760 200,760Q200,760 200,760Z';
|
|
5
|
+
|
|
6
|
+
// Glyph as a `mask`-ready data URI, used in EnrichedText.css.
|
|
7
|
+
export const BROKEN_IMAGE_GLYPH_MASK = `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 960 960'%3E%3Cpath d='${BROKEN_IMAGE_PATH_D}'/%3E%3C/svg%3E")`;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { RefObject } from 'react';
|
|
2
|
+
import type { OnLinkDetected } from '../types';
|
|
3
|
+
|
|
4
|
+
export interface LinkEmitterState {
|
|
5
|
+
linkRegex?: RegExp | null;
|
|
6
|
+
onLinkDetected?: (e: OnLinkDetected) => void;
|
|
7
|
+
lastEmitted: OnLinkDetected | null;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export type LinkEmitterRef = RefObject<LinkEmitterState>;
|
|
11
|
+
|
|
12
|
+
function isSamePayload(a: OnLinkDetected, b: OnLinkDetected): boolean {
|
|
13
|
+
return (
|
|
14
|
+
a.text === b.text &&
|
|
15
|
+
a.url === b.url &&
|
|
16
|
+
a.start === b.start &&
|
|
17
|
+
a.end === b.end
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function emitLinkDetected(
|
|
22
|
+
state: LinkEmitterState,
|
|
23
|
+
next: OnLinkDetected
|
|
24
|
+
): void {
|
|
25
|
+
const prev = state.lastEmitted;
|
|
26
|
+
if (prev && isSamePayload(prev, next)) return;
|
|
27
|
+
state.lastEmitted = next;
|
|
28
|
+
state.onLinkDetected?.(next);
|
|
29
|
+
}
|
|
@@ -2,6 +2,7 @@ import { type CommandProps } from '@tiptap/core';
|
|
|
2
2
|
import { TaskList } from '@tiptap/extension-list';
|
|
3
3
|
|
|
4
4
|
import { applyWrappingListToSelection } from './applyWrappingListToSelection';
|
|
5
|
+
import { withPreservedAlignment } from './formatRules';
|
|
5
6
|
|
|
6
7
|
declare module '@tiptap/core' {
|
|
7
8
|
interface Commands<ReturnType> {
|
|
@@ -24,9 +25,11 @@ export const EnrichedCheckboxList = TaskList.extend({
|
|
|
24
25
|
addCommands() {
|
|
25
26
|
return {
|
|
26
27
|
toggleCheckboxList: (checked: boolean) => {
|
|
27
|
-
return ({ editor,
|
|
28
|
+
return ({ editor, chain }: CommandProps): boolean => {
|
|
28
29
|
if (editor.isActive('checkboxList')) {
|
|
29
|
-
return
|
|
30
|
+
return withPreservedAlignment(editor, chain(), (c) =>
|
|
31
|
+
c.clearNodes().setParagraph()
|
|
32
|
+
);
|
|
30
33
|
}
|
|
31
34
|
|
|
32
35
|
return applyWrappingListToSelection(
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import { NodeViewWrapper, type ReactNodeViewProps } from '@tiptap/react';
|
|
2
2
|
import type { CSSProperties } from 'react';
|
|
3
3
|
import { useState } from 'react';
|
|
4
|
+
import { BROKEN_IMAGE_PATH_D } from '../constants/brokenImageGlyph';
|
|
4
5
|
|
|
5
6
|
const IMAGE_FALLBACK_SIZE = 80;
|
|
6
7
|
|
|
7
|
-
const BROKEN_IMAGE_PATH_D =
|
|
8
|
-
'M200,840Q167,840 143.5,816.5Q120,793 120,760L120,200Q120,167 143.5,143.5Q167,120 200,120L760,120Q793,120 816.5,143.5Q840,167 840,200L840,760Q840,793 816.5,816.5Q793,840 760,840L200,840ZM240,503L400,343L560,503L720,343L760,383L760,200Q760,200 760,200Q760,200 760,200L200,200Q200,200 200,200Q200,200 200,200L200,463L240,503ZM200,760L760,760Q760,760 760,760Q760,760 760,760L760,496L720,456L560,616L400,456L240,616L200,576L200,760Q200,760 200,760Q200,760 200,760ZM200,760L200,760Q200,760 200,760Q200,760 200,760L200,496L200,576L200,463L200,383L200,200Q200,200 200,200Q200,200 200,200L200,200Q200,200 200,200Q200,200 200,200L200,463L200,463L200,576L200,576L200,760Q200,760 200,760Q200,760 200,760Z';
|
|
9
|
-
|
|
10
8
|
function BrokenImageGlyph() {
|
|
11
9
|
return (
|
|
12
10
|
<svg
|
|
@@ -1,15 +1,63 @@
|
|
|
1
|
-
import Link from '@tiptap/extension-link';
|
|
2
|
-
import type
|
|
1
|
+
import Link, { type LinkOptions } from '@tiptap/extension-link';
|
|
2
|
+
import { mergeAttributes, type CommandProps } from '@tiptap/core';
|
|
3
3
|
import type { Editor } from '@tiptap/react';
|
|
4
4
|
|
|
5
5
|
import { nativePosToTiptapPos } from '../positionMapping';
|
|
6
6
|
import { isLinkBlocked } from './formatRules';
|
|
7
|
+
import { findAutolinkRangesInWord } from '../pmPlugins/AutolinkPlugin/autolinkRegex';
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
function isFullLinkMatch(text: string, linkRegex: RegExp | undefined): boolean {
|
|
10
|
+
const ranges = findAutolinkRangesInWord(text, linkRegex);
|
|
11
|
+
return ranges.some((r) => r.start === 0 && r.endExclusive === text.length);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const EnrichedLink = Link.extend<
|
|
15
|
+
LinkOptions & { getLinkRegex: () => RegExp | null | undefined }
|
|
16
|
+
>({
|
|
9
17
|
excludes: 'link code',
|
|
10
18
|
|
|
19
|
+
parseHTML() {
|
|
20
|
+
return [
|
|
21
|
+
{
|
|
22
|
+
tag: 'a[href]',
|
|
23
|
+
getAttrs: (node: string | HTMLElement) => {
|
|
24
|
+
if (typeof node === 'string') return null;
|
|
25
|
+
const href = node.getAttribute('href');
|
|
26
|
+
if (!href || /^javascript:/i.test(href)) return false;
|
|
27
|
+
return null;
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
];
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
addAttributes() {
|
|
34
|
+
return {
|
|
35
|
+
...this.parent?.(),
|
|
36
|
+
auto: {
|
|
37
|
+
default: false,
|
|
38
|
+
parseHTML: (el) => {
|
|
39
|
+
const href = el.getAttribute('href');
|
|
40
|
+
const textContent = el.textContent;
|
|
41
|
+
if (!href || href !== textContent) return false;
|
|
42
|
+
|
|
43
|
+
const linkRegex = this.options.getLinkRegex();
|
|
44
|
+
if (linkRegex === null) return false;
|
|
45
|
+
|
|
46
|
+
return isFullLinkMatch(href, linkRegex);
|
|
47
|
+
},
|
|
48
|
+
renderHTML: () => {
|
|
49
|
+
return {};
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
},
|
|
54
|
+
|
|
11
55
|
renderHTML({ HTMLAttributes }) {
|
|
12
|
-
return [
|
|
56
|
+
return [
|
|
57
|
+
'a',
|
|
58
|
+
mergeAttributes(this.options.HTMLAttributes, HTMLAttributes),
|
|
59
|
+
0,
|
|
60
|
+
];
|
|
13
61
|
},
|
|
14
62
|
|
|
15
63
|
addOptions() {
|
|
@@ -24,6 +72,9 @@ export const EnrichedLink = Link.extend({
|
|
|
24
72
|
target: null,
|
|
25
73
|
rel: null,
|
|
26
74
|
},
|
|
75
|
+
getLinkRegex: () => {
|
|
76
|
+
throw new Error('EnrichedLink.configure({ getLinkRegex }) is required');
|
|
77
|
+
},
|
|
27
78
|
};
|
|
28
79
|
},
|
|
29
80
|
|
|
@@ -55,6 +106,10 @@ export const EnrichedLink = Link.extend({
|
|
|
55
106
|
},
|
|
56
107
|
};
|
|
57
108
|
},
|
|
109
|
+
|
|
110
|
+
addPasteRules() {
|
|
111
|
+
return [];
|
|
112
|
+
},
|
|
58
113
|
});
|
|
59
114
|
|
|
60
115
|
export function removeLink(editor: Editor, start: number, end: number) {
|
|
@@ -2,6 +2,7 @@ import { wrappingInputRule } from '@tiptap/core';
|
|
|
2
2
|
import { OrderedList } from '@tiptap/extension-list';
|
|
3
3
|
|
|
4
4
|
import { applyWrappingListToSelection } from './applyWrappingListToSelection';
|
|
5
|
+
import { withPreservedAlignment } from './formatRules';
|
|
5
6
|
|
|
6
7
|
const ORDERED_LIST_INPUT_REGEX = /^1\.\s$/;
|
|
7
8
|
|
|
@@ -23,9 +24,11 @@ export const EnrichedOrderedList = OrderedList.extend({
|
|
|
23
24
|
return {
|
|
24
25
|
toggleOrderedList:
|
|
25
26
|
() =>
|
|
26
|
-
({ editor,
|
|
27
|
+
({ editor, chain }) => {
|
|
27
28
|
if (editor.isActive('orderedList')) {
|
|
28
|
-
return
|
|
29
|
+
return withPreservedAlignment(editor, chain(), (c) =>
|
|
30
|
+
c.clearNodes().setParagraph()
|
|
31
|
+
);
|
|
29
32
|
}
|
|
30
33
|
|
|
31
34
|
return applyWrappingListToSelection(
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { isTextSelection } from '@tiptap/core';
|
|
2
|
+
import { TextAlign } from '@tiptap/extension-text-align';
|
|
3
|
+
|
|
4
|
+
export const EnrichedTextAlign = TextAlign.extend({
|
|
5
|
+
addCommands() {
|
|
6
|
+
return {
|
|
7
|
+
...this.parent?.(),
|
|
8
|
+
setTextAlign: (alignment) => (props) => {
|
|
9
|
+
const { state, dispatch, tr } = props;
|
|
10
|
+
|
|
11
|
+
if (!this.options.alignments.includes(alignment)) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const { $from } = state.selection;
|
|
16
|
+
let listNode: typeof $from.parent | null = null;
|
|
17
|
+
let listPos = -1;
|
|
18
|
+
|
|
19
|
+
// Walk up the tree to see if the cursor is inside a list wrapper
|
|
20
|
+
for (let depth = $from.depth; depth > 0; depth--) {
|
|
21
|
+
const node = $from.node(depth);
|
|
22
|
+
if (
|
|
23
|
+
['orderedList', 'unorderedList', 'checkboxList'].includes(
|
|
24
|
+
node.type.name
|
|
25
|
+
)
|
|
26
|
+
) {
|
|
27
|
+
listNode = node;
|
|
28
|
+
listPos = $from.before(depth);
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// If in a list, manually apply the alignment to the parent wrapper
|
|
34
|
+
if (listNode) {
|
|
35
|
+
if (dispatch) {
|
|
36
|
+
tr.setNodeMarkup(listPos, undefined, {
|
|
37
|
+
...listNode.attrs,
|
|
38
|
+
textAlign: alignment,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// If not in a list, fire the original Tiptap command
|
|
45
|
+
return this.parent?.().setTextAlign?.(alignment)(props) ?? false;
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
addKeyboardShortcuts() {
|
|
51
|
+
return {
|
|
52
|
+
...this.parent?.(),
|
|
53
|
+
Backspace: () => {
|
|
54
|
+
const { selection } = this.editor.state;
|
|
55
|
+
|
|
56
|
+
if (!selection.empty || !isTextSelection(selection)) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const { $cursor } = selection;
|
|
61
|
+
|
|
62
|
+
if (!$cursor || !this.editor.isEmpty) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const currentAlignment = $cursor.parent.attrs.textAlign;
|
|
67
|
+
const hasAlignment = currentAlignment && currentAlignment !== 'auto';
|
|
68
|
+
|
|
69
|
+
// If the input is empty and has an alignment, clear the alignment
|
|
70
|
+
if (hasAlignment) {
|
|
71
|
+
return this.editor.commands.unsetTextAlign();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return false;
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
},
|
|
78
|
+
}).configure({
|
|
79
|
+
types: [
|
|
80
|
+
'paragraph',
|
|
81
|
+
'heading',
|
|
82
|
+
'orderedList',
|
|
83
|
+
'unorderedList',
|
|
84
|
+
'checkboxList',
|
|
85
|
+
],
|
|
86
|
+
defaultAlignment: null,
|
|
87
|
+
alignments: ['left', 'center', 'right', 'justify'],
|
|
88
|
+
});
|
|
@@ -2,6 +2,7 @@ import { wrappingInputRule, type CommandProps } from '@tiptap/core';
|
|
|
2
2
|
import { BulletList } from '@tiptap/extension-list';
|
|
3
3
|
|
|
4
4
|
import { applyWrappingListToSelection } from './applyWrappingListToSelection';
|
|
5
|
+
import { withPreservedAlignment } from './formatRules';
|
|
5
6
|
|
|
6
7
|
declare module '@tiptap/core' {
|
|
7
8
|
interface Commands<ReturnType> {
|
|
@@ -33,9 +34,11 @@ export const EnrichedUnorderedList = BulletList.extend({
|
|
|
33
34
|
return {
|
|
34
35
|
toggleUnorderedList:
|
|
35
36
|
() =>
|
|
36
|
-
({ editor,
|
|
37
|
+
({ editor, chain }: CommandProps) => {
|
|
37
38
|
if (editor.isActive('unorderedList')) {
|
|
38
|
-
return
|
|
39
|
+
return withPreservedAlignment(editor, chain(), (c) =>
|
|
40
|
+
c.clearNodes().setParagraph()
|
|
41
|
+
);
|
|
39
42
|
}
|
|
40
43
|
|
|
41
44
|
return applyWrappingListToSelection(
|
|
@@ -4,6 +4,7 @@ import { Fragment } from '@tiptap/pm/model';
|
|
|
4
4
|
import { TextSelection } from '@tiptap/pm/state';
|
|
5
5
|
|
|
6
6
|
import { nativePosToTiptapPos, tiptapPosToNativePos } from '../positionMapping';
|
|
7
|
+
import { withPreservedAlignment } from './formatRules';
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Clears block styling with `clearNodes`, then wraps the selection’s blocks in a flat
|
|
@@ -31,9 +32,8 @@ export function applyWrappingListToSelection(
|
|
|
31
32
|
const nativeAnchor = tiptapPosToNativePos(docBefore, selBefore.anchor);
|
|
32
33
|
const nativeHead = tiptapPosToNativePos(docBefore, selBefore.head);
|
|
33
34
|
|
|
34
|
-
return chain()
|
|
35
|
-
.clearNodes()
|
|
36
|
-
.command(({ tr, state }) => {
|
|
35
|
+
return withPreservedAlignment(editor, chain(), (c) =>
|
|
36
|
+
c.clearNodes().command(({ tr, state }) => {
|
|
37
37
|
const listType = state.schema.nodes[listTypeName];
|
|
38
38
|
const itemType = state.schema.nodes[itemTypeName];
|
|
39
39
|
if (!listType || !itemType) {
|
|
@@ -72,5 +72,5 @@ export function applyWrappingListToSelection(
|
|
|
72
72
|
);
|
|
73
73
|
return true;
|
|
74
74
|
})
|
|
75
|
-
|
|
75
|
+
);
|
|
76
76
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { Editor } from '@tiptap/core';
|
|
2
2
|
import type { HtmlStyle } from '../../types';
|
|
3
3
|
import { HEADING_LEVELS, HEADING_TAGS } from './EnrichedHeading';
|
|
4
|
-
|
|
5
4
|
type ChainedCommands = ReturnType<Editor['chain']>;
|
|
6
5
|
|
|
7
6
|
export function isAnyParagraphFormatActive(editor: Editor): boolean {
|
|
@@ -77,8 +76,53 @@ export function toggleParagraphFormat(
|
|
|
77
76
|
isActive: () => boolean,
|
|
78
77
|
deactivate: () => boolean,
|
|
79
78
|
activate: (c: ChainedCommands) => ChainedCommands,
|
|
80
|
-
chain: () => ChainedCommands
|
|
79
|
+
chain: () => ChainedCommands,
|
|
80
|
+
editor: Editor
|
|
81
81
|
): boolean {
|
|
82
|
-
if (isActive())
|
|
83
|
-
|
|
82
|
+
if (isActive()) {
|
|
83
|
+
return withPreservedAlignment(editor, chain(), (c) => {
|
|
84
|
+
deactivate();
|
|
85
|
+
return c;
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return withPreservedAlignment(editor, chain(), (c) =>
|
|
90
|
+
activate(c.clearNodes())
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function getCurrentAlignment(editor: Editor): string | null {
|
|
95
|
+
const { doc, selection } = editor.state;
|
|
96
|
+
let { $from } = selection;
|
|
97
|
+
|
|
98
|
+
// If the user presses Cmd+A, the selection anchors to the document root (depth 0).
|
|
99
|
+
// We resolve position '1' to step exactly inside the first paragraph node
|
|
100
|
+
// so the loop can correctly read its alignment.
|
|
101
|
+
if ($from.depth === 0 && doc.content.size > 0) {
|
|
102
|
+
$from = doc.resolve(1);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
for (let depth = $from.depth; depth >= 0; depth--) {
|
|
106
|
+
const node = $from.node(depth);
|
|
107
|
+
if (node.attrs.textAlign) {
|
|
108
|
+
return node.attrs.textAlign;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function withPreservedAlignment(
|
|
115
|
+
editor: Editor,
|
|
116
|
+
chain: ChainedCommands,
|
|
117
|
+
mutateChain: (c: ChainedCommands) => ChainedCommands
|
|
118
|
+
): boolean {
|
|
119
|
+
const currentAlignment = getCurrentAlignment(editor);
|
|
120
|
+
|
|
121
|
+
const c = mutateChain(chain);
|
|
122
|
+
|
|
123
|
+
if (currentAlignment && currentAlignment !== 'auto') {
|
|
124
|
+
c.setTextAlign(currentAlignment);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return c.run();
|
|
84
128
|
}
|