diff --git a/android/src/main/java/com/swmansion/enriched/EnrichedTextInputView.kt b/android/src/main/java/com/swmansion/enriched/EnrichedTextInputView.kt index 086b5d8e9..ccecd7f43 100644 --- a/android/src/main/java/com/swmansion/enriched/EnrichedTextInputView.kt +++ b/android/src/main/java/com/swmansion/enriched/EnrichedTextInputView.kt @@ -409,6 +409,9 @@ class EnrichedTextInputView : AppCompatEditText { EnrichedSpans.H1 -> paragraphStyles?.toggleStyle(EnrichedSpans.H1) EnrichedSpans.H2 -> paragraphStyles?.toggleStyle(EnrichedSpans.H2) EnrichedSpans.H3 -> paragraphStyles?.toggleStyle(EnrichedSpans.H3) + EnrichedSpans.H4 -> paragraphStyles?.toggleStyle(EnrichedSpans.H4) + EnrichedSpans.H5 -> paragraphStyles?.toggleStyle(EnrichedSpans.H5) + EnrichedSpans.H6 -> paragraphStyles?.toggleStyle(EnrichedSpans.H6) EnrichedSpans.CODE_BLOCK -> paragraphStyles?.toggleStyle(EnrichedSpans.CODE_BLOCK) EnrichedSpans.BLOCK_QUOTE -> paragraphStyles?.toggleStyle(EnrichedSpans.BLOCK_QUOTE) EnrichedSpans.ORDERED_LIST -> listStyles?.toggleStyle(EnrichedSpans.ORDERED_LIST) @@ -429,6 +432,9 @@ class EnrichedTextInputView : AppCompatEditText { EnrichedSpans.H1 -> paragraphStyles?.removeStyle(EnrichedSpans.H1, start, end) EnrichedSpans.H2 -> paragraphStyles?.removeStyle(EnrichedSpans.H2, start, end) EnrichedSpans.H3 -> paragraphStyles?.removeStyle(EnrichedSpans.H3, start, end) + EnrichedSpans.H4 -> paragraphStyles?.removeStyle(EnrichedSpans.H4, start, end) + EnrichedSpans.H5 -> paragraphStyles?.removeStyle(EnrichedSpans.H5, start, end) + EnrichedSpans.H6 -> paragraphStyles?.removeStyle(EnrichedSpans.H6, start, end) EnrichedSpans.CODE_BLOCK -> paragraphStyles?.removeStyle(EnrichedSpans.CODE_BLOCK, start, end) EnrichedSpans.BLOCK_QUOTE -> paragraphStyles?.removeStyle(EnrichedSpans.BLOCK_QUOTE, start, end) EnrichedSpans.ORDERED_LIST -> listStyles?.removeStyle(EnrichedSpans.ORDERED_LIST, start, end) @@ -452,6 +458,9 @@ class EnrichedTextInputView : AppCompatEditText { EnrichedSpans.H1 -> paragraphStyles?.getStyleRange() EnrichedSpans.H2 -> paragraphStyles?.getStyleRange() EnrichedSpans.H3 -> paragraphStyles?.getStyleRange() + EnrichedSpans.H4 -> paragraphStyles?.getStyleRange() + EnrichedSpans.H5 -> paragraphStyles?.getStyleRange() + EnrichedSpans.H6 -> paragraphStyles?.getStyleRange() EnrichedSpans.CODE_BLOCK -> paragraphStyles?.getStyleRange() EnrichedSpans.BLOCK_QUOTE -> paragraphStyles?.getStyleRange() EnrichedSpans.ORDERED_LIST -> listStyles?.getStyleRange() diff --git a/android/src/main/java/com/swmansion/enriched/EnrichedTextInputViewManager.kt b/android/src/main/java/com/swmansion/enriched/EnrichedTextInputViewManager.kt index 286a19b7b..91b004ce7 100644 --- a/android/src/main/java/com/swmansion/enriched/EnrichedTextInputViewManager.kt +++ b/android/src/main/java/com/swmansion/enriched/EnrichedTextInputViewManager.kt @@ -235,6 +235,18 @@ class EnrichedTextInputViewManager : SimpleViewManager(), view?.verifyAndToggleStyle(EnrichedSpans.H3) } + override fun toggleH4(view: EnrichedTextInputView?) { + view?.verifyAndToggleStyle(EnrichedSpans.H4) + } + + override fun toggleH5(view: EnrichedTextInputView?) { + view?.verifyAndToggleStyle(EnrichedSpans.H5) + } + + override fun toggleH6(view: EnrichedTextInputView?) { + view?.verifyAndToggleStyle(EnrichedSpans.H6) + } + override fun toggleCodeBlock(view: EnrichedTextInputView?) { view?.verifyAndToggleStyle(EnrichedSpans.CODE_BLOCK) } diff --git a/android/src/main/java/com/swmansion/enriched/spans/EnrichedH4Span.kt b/android/src/main/java/com/swmansion/enriched/spans/EnrichedH4Span.kt new file mode 100644 index 000000000..76f649476 --- /dev/null +++ b/android/src/main/java/com/swmansion/enriched/spans/EnrichedH4Span.kt @@ -0,0 +1,17 @@ +package com.swmansion.enriched.spans + +import android.graphics.Typeface +import android.text.TextPaint +import android.text.style.AbsoluteSizeSpan +import com.swmansion.enriched.spans.interfaces.EnrichedHeadingSpan +import com.swmansion.enriched.styles.HtmlStyle + +class EnrichedH4Span(private val htmlStyle: HtmlStyle) : AbsoluteSizeSpan(htmlStyle.h4FontSize), EnrichedHeadingSpan { + override fun updateDrawState(tp: TextPaint) { + super.updateDrawState(tp) + val bold = htmlStyle.h4Bold + if (bold) { + tp.typeface = Typeface.create(tp.typeface, Typeface.BOLD) + } + } +} diff --git a/android/src/main/java/com/swmansion/enriched/spans/EnrichedH5Span.kt b/android/src/main/java/com/swmansion/enriched/spans/EnrichedH5Span.kt new file mode 100644 index 000000000..437bddf14 --- /dev/null +++ b/android/src/main/java/com/swmansion/enriched/spans/EnrichedH5Span.kt @@ -0,0 +1,17 @@ +package com.swmansion.enriched.spans + +import android.graphics.Typeface +import android.text.TextPaint +import android.text.style.AbsoluteSizeSpan +import com.swmansion.enriched.spans.interfaces.EnrichedHeadingSpan +import com.swmansion.enriched.styles.HtmlStyle + +class EnrichedH5Span(private val htmlStyle: HtmlStyle) : AbsoluteSizeSpan(htmlStyle.h5FontSize), EnrichedHeadingSpan { + override fun updateDrawState(tp: TextPaint) { + super.updateDrawState(tp) + val bold = htmlStyle.h5Bold + if (bold) { + tp.typeface = Typeface.create(tp.typeface, Typeface.BOLD) + } + } +} diff --git a/android/src/main/java/com/swmansion/enriched/spans/EnrichedH6Span.kt b/android/src/main/java/com/swmansion/enriched/spans/EnrichedH6Span.kt new file mode 100644 index 000000000..22c89e7d0 --- /dev/null +++ b/android/src/main/java/com/swmansion/enriched/spans/EnrichedH6Span.kt @@ -0,0 +1,17 @@ +package com.swmansion.enriched.spans + +import android.graphics.Typeface +import android.text.TextPaint +import android.text.style.AbsoluteSizeSpan +import com.swmansion.enriched.spans.interfaces.EnrichedHeadingSpan +import com.swmansion.enriched.styles.HtmlStyle + +class EnrichedH6Span(private val htmlStyle: HtmlStyle) : AbsoluteSizeSpan(htmlStyle.h6FontSize), EnrichedHeadingSpan { + override fun updateDrawState(tp: TextPaint) { + super.updateDrawState(tp) + val bold = htmlStyle.h6Bold + if (bold) { + tp.typeface = Typeface.create(tp.typeface, Typeface.BOLD) + } + } +} diff --git a/android/src/main/java/com/swmansion/enriched/spans/EnrichedSpans.kt b/android/src/main/java/com/swmansion/enriched/spans/EnrichedSpans.kt index ef1b408ee..6b7f71342 100644 --- a/android/src/main/java/com/swmansion/enriched/spans/EnrichedSpans.kt +++ b/android/src/main/java/com/swmansion/enriched/spans/EnrichedSpans.kt @@ -25,6 +25,9 @@ object EnrichedSpans { const val H1 = "h1" const val H2 = "h2" const val H3 = "h3" + const val H4 = "h4" + const val H5 = "h5" + const val H6 = "h6" const val BLOCK_QUOTE = "block_quote" const val CODE_BLOCK = "code_block" @@ -49,6 +52,9 @@ object EnrichedSpans { H1 to ParagraphSpanConfig(EnrichedH1Span::class.java, false), H2 to ParagraphSpanConfig(EnrichedH2Span::class.java, false), H3 to ParagraphSpanConfig(EnrichedH3Span::class.java, false), + H4 to ParagraphSpanConfig(EnrichedH4Span::class.java, false), + H5 to ParagraphSpanConfig(EnrichedH5Span::class.java, false), + H6 to ParagraphSpanConfig(EnrichedH6Span::class.java, false), BLOCK_QUOTE to ParagraphSpanConfig(EnrichedBlockQuoteSpan::class.java, true), CODE_BLOCK to ParagraphSpanConfig(EnrichedCodeBlockSpan::class.java, true), ) @@ -71,6 +77,9 @@ object EnrichedSpans { if (htmlStyle.h1Bold) blockingStyles.add(H1) if (htmlStyle.h2Bold) blockingStyles.add(H2) if (htmlStyle.h3Bold) blockingStyles.add(H3) + if (htmlStyle.h4Bold) blockingStyles.add(H4) + if (htmlStyle.h5Bold) blockingStyles.add(H5) + if (htmlStyle.h6Bold) blockingStyles.add(H6) StylesMergingConfig(blockingStyles = blockingStyles.toTypedArray()) } ITALIC -> StylesMergingConfig( @@ -87,31 +96,46 @@ object EnrichedSpans { blockingStyles = arrayOf(CODE_BLOCK) ) H1 -> { - val conflictingStyles = mutableListOf(H2, H3, ORDERED_LIST, UNORDERED_LIST, BLOCK_QUOTE, CODE_BLOCK) + val conflictingStyles = mutableListOf(H2, H3, H4, H5, H6, ORDERED_LIST, UNORDERED_LIST, BLOCK_QUOTE, CODE_BLOCK) if (htmlStyle.h1Bold) conflictingStyles.add(BOLD) StylesMergingConfig(conflictingStyles = conflictingStyles.toTypedArray()) } H2 -> { - val conflictingStyles = mutableListOf(H1, H3, ORDERED_LIST, UNORDERED_LIST, BLOCK_QUOTE, CODE_BLOCK) + val conflictingStyles = mutableListOf(H1, H3, H4, H5, H6, ORDERED_LIST, UNORDERED_LIST, BLOCK_QUOTE, CODE_BLOCK) if (htmlStyle.h2Bold) conflictingStyles.add(BOLD) StylesMergingConfig(conflictingStyles = conflictingStyles.toTypedArray()) } H3 -> { - val conflictingStyles = mutableListOf(H1, H2, ORDERED_LIST, UNORDERED_LIST, BLOCK_QUOTE, CODE_BLOCK) + val conflictingStyles = mutableListOf(H1, H2, H4, H5, H6, ORDERED_LIST, UNORDERED_LIST, BLOCK_QUOTE, CODE_BLOCK) if (htmlStyle.h3Bold) conflictingStyles.add(BOLD) StylesMergingConfig(conflictingStyles = conflictingStyles.toTypedArray()) } + H4 -> { + val conflictingStyles = mutableListOf(H1, H2, H3, H5, H6, ORDERED_LIST, UNORDERED_LIST, BLOCK_QUOTE, CODE_BLOCK) + if (htmlStyle.h4Bold) conflictingStyles.add(BOLD) + StylesMergingConfig(conflictingStyles = conflictingStyles.toTypedArray()) + } + H5 -> { + val conflictingStyles = mutableListOf(H1, H2, H3, H4, H6, ORDERED_LIST, UNORDERED_LIST, BLOCK_QUOTE, CODE_BLOCK) + if (htmlStyle.h5Bold) conflictingStyles.add(BOLD) + StylesMergingConfig(conflictingStyles = conflictingStyles.toTypedArray()) + } + H6 -> { + val conflictingStyles = mutableListOf(H1, H2, H3, H4, H5, ORDERED_LIST, UNORDERED_LIST, BLOCK_QUOTE, CODE_BLOCK) + if (htmlStyle.h6Bold) conflictingStyles.add(BOLD) + StylesMergingConfig(conflictingStyles = conflictingStyles.toTypedArray()) + } BLOCK_QUOTE -> StylesMergingConfig( - conflictingStyles = arrayOf(H1, H2, H3, CODE_BLOCK, ORDERED_LIST, UNORDERED_LIST) + conflictingStyles = arrayOf(H1, H2, H3, H4, H5, H6, CODE_BLOCK, ORDERED_LIST, UNORDERED_LIST) ) CODE_BLOCK -> StylesMergingConfig( - conflictingStyles = arrayOf(H1, H2, H3, BOLD, ITALIC, UNDERLINE, STRIKETHROUGH, UNORDERED_LIST, ORDERED_LIST, BLOCK_QUOTE, INLINE_CODE) + conflictingStyles = arrayOf(H1, H2, H3, H4, H5, H6, BOLD, ITALIC, UNDERLINE, STRIKETHROUGH, UNORDERED_LIST, ORDERED_LIST, BLOCK_QUOTE, INLINE_CODE) ) UNORDERED_LIST -> StylesMergingConfig( - conflictingStyles = arrayOf(H1, H2, H3, ORDERED_LIST, CODE_BLOCK, BLOCK_QUOTE) + conflictingStyles = arrayOf(H1, H2, H3, H4, H5, H6, ORDERED_LIST, CODE_BLOCK, BLOCK_QUOTE) ) ORDERED_LIST -> StylesMergingConfig( - conflictingStyles = arrayOf(H1, H2, H3, UNORDERED_LIST, CODE_BLOCK, BLOCK_QUOTE), + conflictingStyles = arrayOf(H1, H2, H3, H4, H5, H6, UNORDERED_LIST, CODE_BLOCK, BLOCK_QUOTE), ) LINK -> StylesMergingConfig( blockingStyles = arrayOf(INLINE_CODE, CODE_BLOCK, MENTION) diff --git a/android/src/main/java/com/swmansion/enriched/styles/HtmlStyle.kt b/android/src/main/java/com/swmansion/enriched/styles/HtmlStyle.kt index 8a4b9088f..de258b3f8 100644 --- a/android/src/main/java/com/swmansion/enriched/styles/HtmlStyle.kt +++ b/android/src/main/java/com/swmansion/enriched/styles/HtmlStyle.kt @@ -28,6 +28,15 @@ class HtmlStyle { var h3FontSize: Int = 56 var h3Bold: Boolean = false + var h4FontSize: Int = 48 + var h4Bold: Boolean = false + + var h5FontSize: Int = 40 + var h5Bold: Boolean = false + + var h6FontSize: Int = 32 + var h6Bold: Boolean = false + var blockquoteColor: Int? = null var blockquoteBorderColor: Int = Color.BLACK var blockquoteStripeWidth: Int = 2 @@ -80,6 +89,18 @@ class HtmlStyle { h3FontSize = parseFloat(h3Style, "fontSize").toInt() h3Bold = h3Style?.getBoolean("bold") == true + val h4Style = style.getMap("h4") + h4FontSize = parseFloat(h4Style, "fontSize").toInt() + h4Bold = h4Style?.getBoolean("bold") == true + + val h5Style = style.getMap("h5") + h5FontSize = parseFloat(h5Style, "fontSize").toInt() + h5Bold = h5Style?.getBoolean("bold") == true + + val h6Style = style.getMap("h6") + h6FontSize = parseFloat(h6Style, "fontSize").toInt() + h6Bold = h6Style?.getBoolean("bold") == true + val blockquoteStyle = style.getMap("blockquote") blockquoteColor = parseOptionalColor(blockquoteStyle, "color") blockquoteBorderColor = parseColor(blockquoteStyle, "borderColor") diff --git a/android/src/main/java/com/swmansion/enriched/utils/EnrichedParser.java b/android/src/main/java/com/swmansion/enriched/utils/EnrichedParser.java index 46e08f72b..782cc1d2e 100644 --- a/android/src/main/java/com/swmansion/enriched/utils/EnrichedParser.java +++ b/android/src/main/java/com/swmansion/enriched/utils/EnrichedParser.java @@ -16,6 +16,9 @@ import com.swmansion.enriched.spans.EnrichedH1Span; import com.swmansion.enriched.spans.EnrichedH2Span; import com.swmansion.enriched.spans.EnrichedH3Span; +import com.swmansion.enriched.spans.EnrichedH4Span; +import com.swmansion.enriched.spans.EnrichedH5Span; +import com.swmansion.enriched.spans.EnrichedH6Span; import com.swmansion.enriched.spans.EnrichedImageSpan; import com.swmansion.enriched.spans.EnrichedInlineCodeSpan; import com.swmansion.enriched.spans.EnrichedItalicSpan; @@ -153,6 +156,12 @@ private static String getBlockTag(EnrichedParagraphSpan[] spans) { return "h2"; } else if (span instanceof EnrichedH3Span) { return "h3"; + } else if (span instanceof EnrichedH4Span) { + return "h4"; + } else if (span instanceof EnrichedH5Span) { + return "h5"; + } else if (span instanceof EnrichedH6Span) { + return "h6"; } } @@ -452,6 +461,12 @@ private void handleStartTag(String tag, Attributes attributes) { startHeading(mSpannableStringBuilder, 2); } else if (tag.equalsIgnoreCase("h3")) { startHeading(mSpannableStringBuilder, 3); + } else if (tag.equalsIgnoreCase("h4")) { + startHeading(mSpannableStringBuilder, 4); + } else if (tag.equalsIgnoreCase("h5")) { + startHeading(mSpannableStringBuilder, 5); + } else if (tag.equalsIgnoreCase("h6")) { + startHeading(mSpannableStringBuilder, 6); } else if (tag.equalsIgnoreCase("img")) { startImg(mSpannableStringBuilder, attributes, mImageGetter, mStyle); } else if (tag.equalsIgnoreCase("code")) { @@ -490,6 +505,12 @@ private void handleEndTag(String tag) { endHeading(mSpannableStringBuilder, mStyle, 2); } else if (tag.equalsIgnoreCase("h3")) { endHeading(mSpannableStringBuilder, mStyle, 3); + } else if (tag.equalsIgnoreCase("h4")) { + endHeading(mSpannableStringBuilder, mStyle, 4); + } else if (tag.equalsIgnoreCase("h5")) { + endHeading(mSpannableStringBuilder, mStyle, 5); + } else if (tag.equalsIgnoreCase("h6")) { + endHeading(mSpannableStringBuilder, mStyle, 6); } else if (tag.equalsIgnoreCase("code")) { end(mSpannableStringBuilder, Code.class, new EnrichedInlineCodeSpan(mStyle)); } else if (tag.equalsIgnoreCase("mention")) { @@ -593,6 +614,15 @@ private void startHeading(Editable text, int level) { case 3: start(text, new H3()); break; + case 4: + start(text, new H4()); + break; + case 5: + start(text, new H5()); + break; + case 6: + start(text, new H6()); + break; default: throw new IllegalArgumentException("Unsupported heading level: " + level); } @@ -614,6 +644,18 @@ private static void endHeading(Editable text, HtmlStyle style, int level) { H3 lastH3 = getLast(text, H3.class); setParagraphSpanFromMark(text, lastH3, new EnrichedH3Span(style)); break; + case 4: + H4 lastH4 = getLast(text, H4.class); + setParagraphSpanFromMark(text, lastH4, new EnrichedH4Span(style)); + break; + case 5: + H5 lastH5 = getLast(text, H5.class); + setParagraphSpanFromMark(text, lastH5, new EnrichedH5Span(style)); + break; + case 6: + H6 lastH6 = getLast(text, H6.class); + setParagraphSpanFromMark(text, lastH6, new EnrichedH6Span(style)); + break; default: throw new IllegalArgumentException("Unsupported heading level: " + level); } @@ -807,6 +849,15 @@ private static class H2 { private static class H3 { } + private static class H4 { + } + + private static class H5 { + } + + private static class H6 { + } + private static class Bold { } diff --git a/android/src/main/java/com/swmansion/enriched/utils/EnrichedSpanState.kt b/android/src/main/java/com/swmansion/enriched/utils/EnrichedSpanState.kt index ff733dd4f..66f86729a 100644 --- a/android/src/main/java/com/swmansion/enriched/utils/EnrichedSpanState.kt +++ b/android/src/main/java/com/swmansion/enriched/utils/EnrichedSpanState.kt @@ -27,6 +27,12 @@ class EnrichedSpanState(private val view: EnrichedTextInputView) { private set var h3Start: Int? = null private set + var h4Start: Int? = null + private set + var h5Start: Int? = null + private set + var h6Start: Int? = null + private set var codeBlockStart: Int? = null private set var blockQuoteStart: Int? = null @@ -82,6 +88,21 @@ class EnrichedSpanState(private val view: EnrichedTextInputView) { emitStateChangeEvent() } + fun setH4Start(start: Int?) { + this.h4Start = start + emitStateChangeEvent() + } + + fun setH5Start(start: Int?) { + this.h5Start = start + emitStateChangeEvent() + } + + fun setH6Start(start: Int?) { + this.h6Start = start + emitStateChangeEvent() + } + fun setCodeBlockStart(start: Int?) { this.codeBlockStart = start emitStateChangeEvent() @@ -127,6 +148,9 @@ class EnrichedSpanState(private val view: EnrichedTextInputView) { EnrichedSpans.H1 -> h1Start EnrichedSpans.H2 -> h2Start EnrichedSpans.H3 -> h3Start + EnrichedSpans.H4 -> h4Start + EnrichedSpans.H5 -> h5Start + EnrichedSpans.H6 -> h6Start EnrichedSpans.CODE_BLOCK -> codeBlockStart EnrichedSpans.BLOCK_QUOTE -> blockQuoteStart EnrichedSpans.ORDERED_LIST -> orderedListStart @@ -150,6 +174,9 @@ class EnrichedSpanState(private val view: EnrichedTextInputView) { EnrichedSpans.H1 -> setH1Start(start) EnrichedSpans.H2 -> setH2Start(start) EnrichedSpans.H3 -> setH3Start(start) + EnrichedSpans.H4 -> setH4Start(start) + EnrichedSpans.H5 -> setH5Start(start) + EnrichedSpans.H6 -> setH6Start(start) EnrichedSpans.CODE_BLOCK -> setCodeBlockStart(start) EnrichedSpans.BLOCK_QUOTE -> setBlockQuoteStart(start) EnrichedSpans.ORDERED_LIST -> setOrderedListStart(start) @@ -170,6 +197,9 @@ class EnrichedSpanState(private val view: EnrichedTextInputView) { payload.putBoolean("isH1", h1Start != null) payload.putBoolean("isH2", h2Start != null) payload.putBoolean("isH3", h3Start != null) + payload.putBoolean("isH4", h4Start != null) + payload.putBoolean("isH5", h5Start != null) + payload.putBoolean("isH6", h6Start != null) payload.putBoolean("isCodeBlock", codeBlockStart != null) payload.putBoolean("isBlockQuote", blockQuoteStart != null) payload.putBoolean("isOrderedList", orderedListStart != null) diff --git a/example/src/App.tsx b/example/src/App.tsx index 3c29531fe..dedaa2969 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -47,6 +47,9 @@ const DEFAULT_STYLE: StylesState = { isH1: false, isH2: false, isH3: false, + isH4: false, + isH5: false, + isH6: false, isBlockQuote: false, isCodeBlock: false, isOrderedList: false, @@ -341,14 +344,26 @@ export default function App() { const htmlStyle: HtmlStyle = { h1: { - fontSize: 40, + fontSize: 72, bold: true, }, h2: { - fontSize: 32, + fontSize: 60, bold: true, }, h3: { + fontSize: 50, + bold: true, + }, + h4: { + fontSize: 40, + bold: true, + }, + h5: { + fontSize: 30, + bold: true, + }, + h6: { fontSize: 24, bold: true, }, diff --git a/example/src/components/Toolbar.tsx b/example/src/components/Toolbar.tsx index 28c99b48a..6fad4c527 100644 --- a/example/src/components/Toolbar.tsx +++ b/example/src/components/Toolbar.tsx @@ -39,6 +39,18 @@ const STYLE_ITEMS = [ name: 'heading-3', text: 'H3', }, + { + name: 'heading-4', + text: 'H4', + }, + { + name: 'heading-5', + text: 'H5', + }, + { + name: 'heading-6', + text: 'H6', + }, { name: 'quote', icon: 'quote-right', @@ -114,6 +126,15 @@ export const Toolbar: FC = ({ case 'heading-3': editorRef.current?.toggleH3(); break; + case 'heading-4': + editorRef.current?.toggleH4(); + break; + case 'heading-5': + editorRef.current?.toggleH5(); + break; + case 'heading-6': + editorRef.current?.toggleH6(); + break; case 'code-block': editorRef?.current?.toggleCodeBlock(); break; @@ -156,6 +177,12 @@ export const Toolbar: FC = ({ return stylesState.isH2; case 'heading-3': return stylesState.isH3; + case 'heading-4': + return stylesState.isH4; + case 'heading-5': + return stylesState.isH5; + case 'heading-6': + return stylesState.isH6; case 'code-block': return stylesState.isCodeBlock; case 'quote': diff --git a/ios/EnrichedTextInputView.mm b/ios/EnrichedTextInputView.mm index f145feefa..6022d96c2 100644 --- a/ios/EnrichedTextInputView.mm +++ b/ios/EnrichedTextInputView.mm @@ -91,6 +91,9 @@ - (void)setDefaults { @([H1Style getStyleType]): [[H1Style alloc] initWithInput:self], @([H2Style getStyleType]): [[H2Style alloc] initWithInput:self], @([H3Style getStyleType]): [[H3Style alloc] initWithInput:self], + @([H4Style getStyleType]): [[H4Style alloc] initWithInput:self], + @([H5Style getStyleType]): [[H5Style alloc] initWithInput:self], + @([H6Style getStyleType]): [[H6Style alloc] initWithInput:self], @([UnorderedListStyle getStyleType]): [[UnorderedListStyle alloc] initWithInput:self], @([OrderedListStyle getStyleType]): [[OrderedListStyle alloc] initWithInput:self], @([BlockQuoteStyle getStyleType]): [[BlockQuoteStyle alloc] initWithInput:self], @@ -106,13 +109,16 @@ - (void)setDefaults { @([InlineCodeStyle getStyleType]) : @[@([LinkStyle getStyleType]), @([MentionStyle getStyleType])], @([LinkStyle getStyleType]): @[@([InlineCodeStyle getStyleType]), @([LinkStyle getStyleType]), @([MentionStyle getStyleType])], @([MentionStyle getStyleType]): @[@([InlineCodeStyle getStyleType]), @([LinkStyle getStyleType])], - @([H1Style getStyleType]): @[@([H2Style getStyleType]), @([H3Style getStyleType]), @([UnorderedListStyle getStyleType]), @([OrderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType]), @([CodeBlockStyle getStyleType])], - @([H2Style getStyleType]): @[@([H1Style getStyleType]), @([H3Style getStyleType]), @([UnorderedListStyle getStyleType]), @([OrderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType]), @([CodeBlockStyle getStyleType])], - @([H3Style getStyleType]): @[@([H1Style getStyleType]), @([H2Style getStyleType]), @([UnorderedListStyle getStyleType]), @([OrderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType]), @([CodeBlockStyle getStyleType])], - @([UnorderedListStyle getStyleType]): @[@([H1Style getStyleType]), @([H2Style getStyleType]), @([H3Style getStyleType]), @([OrderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType]), @([CodeBlockStyle getStyleType])], - @([OrderedListStyle getStyleType]): @[@([H1Style getStyleType]), @([H2Style getStyleType]), @([H3Style getStyleType]), @([UnorderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType]), @([CodeBlockStyle getStyleType])], - @([BlockQuoteStyle getStyleType]): @[@([H1Style getStyleType]), @([H2Style getStyleType]), @([H3Style getStyleType]), @([UnorderedListStyle getStyleType]), @([OrderedListStyle getStyleType]), @([CodeBlockStyle getStyleType])], - @([CodeBlockStyle getStyleType]): @[@([H1Style getStyleType]), @([H2Style getStyleType]), @([H3Style getStyleType]), + @([H1Style getStyleType]): @[@([H2Style getStyleType]), @([H3Style getStyleType]), @([H4Style getStyleType]), @([H5Style getStyleType]), @([H6Style getStyleType]), @([UnorderedListStyle getStyleType]), @([OrderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType])], + @([H2Style getStyleType]): @[@([H1Style getStyleType]), @([H3Style getStyleType]), @([H4Style getStyleType]), @([H5Style getStyleType]), @([H6Style getStyleType]), @([UnorderedListStyle getStyleType]), @([OrderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType])], + @([H3Style getStyleType]): @[@([H1Style getStyleType]), @([H2Style getStyleType]), @([H4Style getStyleType]), @([H5Style getStyleType]), @([H6Style getStyleType]), @([UnorderedListStyle getStyleType]), @([OrderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType])], + @([H4Style getStyleType]): @[@([H1Style getStyleType]), @([H2Style getStyleType]), @([H3Style getStyleType]), @([H5Style getStyleType]), @([H6Style getStyleType]), @([UnorderedListStyle getStyleType]), @([OrderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType])], + @([H5Style getStyleType]): @[@([H1Style getStyleType]), @([H2Style getStyleType]), @([H3Style getStyleType]), @([H4Style getStyleType]), @([H6Style getStyleType]), @([UnorderedListStyle getStyleType]), @([OrderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType])], + @([H6Style getStyleType]): @[@([H1Style getStyleType]), @([H2Style getStyleType]), @([H3Style getStyleType]), @([H4Style getStyleType]), @([H5Style getStyleType]), @([UnorderedListStyle getStyleType]), @([OrderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType])], + @([UnorderedListStyle getStyleType]): @[@([H1Style getStyleType]), @([H2Style getStyleType]), @([H3Style getStyleType]), @([H4Style getStyleType]), @([H5Style getStyleType]), @([H6Style getStyleType]), @([OrderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType])], + @([OrderedListStyle getStyleType]): @[@([H1Style getStyleType]), @([H2Style getStyleType]), @([H3Style getStyleType]), @([H4Style getStyleType]), @([H5Style getStyleType]), @([H6Style getStyleType]), @([UnorderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType])], + @([BlockQuoteStyle getStyleType]): @[@([H1Style getStyleType]), @([H2Style getStyleType]), @([H3Style getStyleType]), @([H4Style getStyleType]), @([H5Style getStyleType]), @([H6Style getStyleType]), @([UnorderedListStyle getStyleType]), @([OrderedListStyle getStyleType])], + @([CodeBlockStyle getStyleType]): @[@([H1Style getStyleType]), @([H2Style getStyleType]), @([H3Style getStyleType]), @([H4Style getStyleType]), @([H5Style getStyleType]), @([H6Style getStyleType]), @([BoldStyle getStyleType]), @([ItalicStyle getStyleType]), @([UnderlineStyle getStyleType]), @([StrikethroughStyle getStyleType]), @([UnorderedListStyle getStyleType]), @([OrderedListStyle getStyleType]), @([BlockQuoteStyle getStyleType]), @([InlineCodeStyle getStyleType]), @([MentionStyle getStyleType]), @([LinkStyle getStyleType])], @([ImageStyle getStyleType]) : @[@([LinkStyle getStyleType]), @([MentionStyle getStyleType])] }; @@ -128,6 +134,9 @@ - (void)setDefaults { @([H1Style getStyleType]): @[], @([H2Style getStyleType]): @[], @([H3Style getStyleType]): @[], + @([H4Style getStyleType]): @[], + @([H5Style getStyleType]): @[], + @([H6Style getStyleType]): @[], @([UnorderedListStyle getStyleType]): @[], @([OrderedListStyle getStyleType]): @[], @([BlockQuoteStyle getStyleType]): @[], @@ -252,6 +261,36 @@ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const & stylePropChanged = YES; } + if(newViewProps.htmlStyle.h4.fontSize != oldViewProps.htmlStyle.h4.fontSize) { + [newConfig setH4FontSize:newViewProps.htmlStyle.h4.fontSize]; + stylePropChanged = YES; + } + + if(newViewProps.htmlStyle.h4.bold != oldViewProps.htmlStyle.h4.bold) { + [newConfig setH4Bold:newViewProps.htmlStyle.h4.bold]; + stylePropChanged = YES; + } + + if(newViewProps.htmlStyle.h5.fontSize != oldViewProps.htmlStyle.h5.fontSize) { + [newConfig setH5FontSize:newViewProps.htmlStyle.h5.fontSize]; + stylePropChanged = YES; + } + + if(newViewProps.htmlStyle.h5.bold != oldViewProps.htmlStyle.h5.bold) { + [newConfig setH5Bold:newViewProps.htmlStyle.h5.bold]; + stylePropChanged = YES; + } + + if(newViewProps.htmlStyle.h6.fontSize != oldViewProps.htmlStyle.h6.fontSize) { + [newConfig setH6FontSize:newViewProps.htmlStyle.h6.fontSize]; + stylePropChanged = YES; + } + + if(newViewProps.htmlStyle.h6.bold != oldViewProps.htmlStyle.h6.bold) { + [newConfig setH6Bold:newViewProps.htmlStyle.h6.bold]; + stylePropChanged = YES; + } + if(newViewProps.htmlStyle.blockquote.borderColor != oldViewProps.htmlStyle.blockquote.borderColor) { if(isColorMeaningful(newViewProps.htmlStyle.blockquote.borderColor)) { [newConfig setBlockquoteBorderColor:RCTUIColorFromSharedColor(newViewProps.htmlStyle.blockquote.borderColor)]; @@ -732,6 +771,9 @@ - (void)tryUpdatingActiveStyles { .isH1 = [_activeStyles containsObject: @([H1Style getStyleType])], .isH2 = [_activeStyles containsObject: @([H2Style getStyleType])], .isH3 = [_activeStyles containsObject: @([H3Style getStyleType])], + .isH4 = [_activeStyles containsObject: @([H4Style getStyleType])], + .isH5 = [_activeStyles containsObject: @([H5Style getStyleType])], + .isH6 = [_activeStyles containsObject: @([H6Style getStyleType])], .isUnorderedList = [_activeStyles containsObject: @([UnorderedListStyle getStyleType])], .isOrderedList = [_activeStyles containsObject: @([OrderedListStyle getStyleType])], .isBlockQuote = [_activeStyles containsObject: @([BlockQuoteStyle getStyleType])], @@ -798,6 +840,12 @@ - (void)handleCommand:(const NSString *)commandName args:(const NSArray *)args { [self toggleParagraphStyle:[H2Style getStyleType]]; } else if([commandName isEqualToString:@"toggleH3"]) { [self toggleParagraphStyle:[H3Style getStyleType]]; + } else if([commandName isEqualToString:@"toggleH4"]) { + [self toggleParagraphStyle:[H4Style getStyleType]]; + } else if([commandName isEqualToString:@"toggleH5"]) { + [self toggleParagraphStyle:[H5Style getStyleType]]; + } else if([commandName isEqualToString:@"toggleH6"]) { + [self toggleParagraphStyle:[H6Style getStyleType]]; } else if([commandName isEqualToString:@"toggleUnorderedList"]) { [self toggleParagraphStyle:[UnorderedListStyle getStyleType]]; } else if([commandName isEqualToString:@"toggleOrderedList"]) { @@ -1106,10 +1154,21 @@ - (void)anyTextMayHaveBeenModified { H1Style *h1Style = stylesDict[@([H1Style getStyleType])]; H2Style *h2Style = stylesDict[@([H2Style getStyleType])]; H3Style *h3Style = stylesDict[@([H3Style getStyleType])]; - if(h1Style != nullptr && h2Style != nullptr && h3Style != nullptr) { + H4Style *h4Style = stylesDict[@([H4Style getStyleType])]; + H5Style *h5Style = stylesDict[@([H5Style getStyleType])]; + H6Style *h6Style = stylesDict[@([H6Style getStyleType])]; + + bool hasImproperHeadingStyles = + h1Style != nullptr && h2Style != nullptr && h3Style != nullptr && + h4Style != nullptr && h5Style != nullptr && h6Style != nullptr; + + if(hasImproperHeadingStyles) { [h1Style handleImproperHeadings]; [h2Style handleImproperHeadings]; [h3Style handleImproperHeadings]; + [h4Style handleImproperHeadings]; + [h5Style handleImproperHeadings]; + [h6Style handleImproperHeadings]; } // mentions removal management @@ -1233,6 +1292,9 @@ - (bool)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range r H1Style *h1Style = stylesDict[@([H1Style getStyleType])]; H2Style *h2Style = stylesDict[@([H2Style getStyleType])]; H3Style *h3Style = stylesDict[@([H3Style getStyleType])]; + H4Style *h4Style = stylesDict[@([H4Style getStyleType])]; + H5Style *h5Style = stylesDict[@([H5Style getStyleType])]; + H6Style *h6Style = stylesDict[@([H6Style getStyleType])]; // some of the changes these checks do could interfere with later checks and cause a crash // so here I rely on short circuiting evaluation of the logical expression @@ -1249,6 +1311,9 @@ - (bool)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range r [h1Style handleNewlinesInRange:range replacementText:text] || [h2Style handleNewlinesInRange:range replacementText:text] || [h3Style handleNewlinesInRange:range replacementText:text] || + [h4Style handleNewlinesInRange:range replacementText:text] || + [h5Style handleNewlinesInRange:range replacementText:text] || + [h6Style handleNewlinesInRange:range replacementText:text] || [ZeroWidthSpaceUtils handleBackspaceInRange:range replacementText:text input:self] || [ParagraphAttributesUtils handleBackspaceInRange:range replacementText:text input:self] || // CRITICAL: This callback HAS TO be always evaluated last. diff --git a/ios/config/InputConfig.h b/ios/config/InputConfig.h index e73935413..cff127c18 100644 --- a/ios/config/InputConfig.h +++ b/ios/config/InputConfig.h @@ -29,6 +29,18 @@ - (void)setH3FontSize:(CGFloat)newValue; - (BOOL)h3Bold; - (void)setH3Bold:(BOOL)newValue; +- (CGFloat)h4FontSize; +- (void)setH4FontSize:(CGFloat)newValue; +- (BOOL)h4Bold; +- (void)setH4Bold:(BOOL)newValue; +- (CGFloat)h5FontSize; +- (void)setH5FontSize:(CGFloat)newValue; +- (BOOL)h5Bold; +- (void)setH5Bold:(BOOL)newValue; +- (CGFloat)h6FontSize; +- (void)setH6FontSize:(CGFloat)newValue; +- (BOOL)h6Bold; +- (void)setH6Bold:(BOOL)newValue; - (UIColor *)blockquoteBorderColor; - (void)setBlockquoteBorderColor:(UIColor *)newValue; - (CGFloat)blockquoteBorderWidth; diff --git a/ios/config/InputConfig.mm b/ios/config/InputConfig.mm index 0e5287c74..631b3f9f5 100644 --- a/ios/config/InputConfig.mm +++ b/ios/config/InputConfig.mm @@ -17,6 +17,12 @@ @implementation InputConfig { BOOL _h2Bold; CGFloat _h3FontSize; BOOL _h3Bold; + CGFloat _h4FontSize; + BOOL _h4Bold; + CGFloat _h5FontSize; + BOOL _h5Bold; + CGFloat _h6FontSize; + BOOL _h6Bold; UIColor *_blockquoteBorderColor; CGFloat _blockquoteBorderWidth; CGFloat _blockquoteGapWidth; @@ -66,6 +72,12 @@ - (id)copyWithZone:(NSZone *)zone { copy->_h2Bold = _h2Bold; copy->_h3FontSize = _h3FontSize; copy->_h3Bold = _h3Bold; + copy->_h4FontSize = _h4FontSize; + copy->_h4Bold = _h4Bold; + copy->_h5FontSize = _h5FontSize; + copy->_h5Bold = _h5Bold; + copy->_h6FontSize = _h6FontSize; + copy->_h6Bold = _h6Bold; copy->_blockquoteBorderColor = [_blockquoteBorderColor copy]; copy->_blockquoteBorderWidth = _blockquoteBorderWidth; copy->_blockquoteGapWidth = _blockquoteGapWidth; @@ -218,6 +230,54 @@ - (void)setH3Bold:(BOOL)newValue { _h3Bold = newValue; } +- (CGFloat)h4FontSize { + return _h4FontSize; +} + +- (void)setH4FontSize:(CGFloat)newValue { + _h4FontSize = newValue; +} + +- (BOOL)h4Bold { + return _h4Bold; +} + +- (void)setH4Bold:(BOOL)newValue { + _h4Bold = newValue; +} + +- (CGFloat)h5FontSize { + return _h5FontSize; +} + +- (void)setH5FontSize:(CGFloat)newValue { + _h5FontSize = newValue; +} + +- (BOOL)h5Bold { + return _h5Bold; +} + +- (void)setH5Bold:(BOOL)newValue { + _h5Bold = newValue; +} + +- (CGFloat)h6FontSize { + return _h6FontSize; +} + +- (void)setH6FontSize:(CGFloat)newValue { + _h6FontSize = newValue; +} + +- (BOOL)h6Bold { + return _h6Bold; +} + +- (void)setH6Bold:(BOOL)newValue { + _h6Bold = newValue; +} + - (UIColor *)blockquoteBorderColor { return _blockquoteBorderColor; } diff --git a/ios/inputParser/InputParser.mm b/ios/inputParser/InputParser.mm index 10f250aba..03c666122 100644 --- a/ios/inputParser/InputParser.mm +++ b/ios/inputParser/InputParser.mm @@ -166,6 +166,9 @@ - (NSString *)parseToHtmlFromRange:(NSRange)range { [currentActiveStyles containsObject:@([H1Style getStyleType])] || [currentActiveStyles containsObject:@([H2Style getStyleType])] || [currentActiveStyles containsObject:@([H3Style getStyleType])] || + [currentActiveStyles containsObject:@([H4Style getStyleType])] || + [currentActiveStyles containsObject:@([H5Style getStyleType])] || + [currentActiveStyles containsObject:@([H6Style getStyleType])] || [currentActiveStyles containsObject:@([BlockQuoteStyle getStyleType])] || [currentActiveStyles containsObject:@([CodeBlockStyle getStyleType])] ) { @@ -284,9 +287,12 @@ - (NSString *)parseToHtmlFromRange:(NSRange)range { } else if( [previousActiveStyles containsObject:@([H1Style getStyleType])] || [previousActiveStyles containsObject:@([H2Style getStyleType])] || - [previousActiveStyles containsObject:@([H3Style getStyleType])] + [previousActiveStyles containsObject:@([H3Style getStyleType])] || + [previousActiveStyles containsObject:@([H4Style getStyleType])] || + [previousActiveStyles containsObject:@([H5Style getStyleType])] || + [previousActiveStyles containsObject:@([H6Style getStyleType])] ) { - // do nothing, heading closing tag has already ben appended + // do nothing, heading closing tag has already been appended } else { [result appendString:@"

"]; } @@ -389,6 +395,12 @@ - (NSString *)tagContentForStyle:(NSNumber *)style openingTag:(BOOL)openingTag l return @"h2"; } else if([style isEqualToNumber:@([H3Style getStyleType])]) { return @"h3"; + } else if([style isEqualToNumber:@([H4Style getStyleType])]) { + return @"h4"; + } else if([style isEqualToNumber:@([H5Style getStyleType])]) { + return @"h5"; + } else if([style isEqualToNumber:@([H6Style getStyleType])]) { + return @"h6"; } else if([style isEqualToNumber:@([UnorderedListStyle getStyleType])] || [style isEqualToNumber:@([OrderedListStyle getStyleType])]) { return @"li"; } else if([style isEqualToNumber:@([BlockQuoteStyle getStyleType])] || [style isEqualToNumber:@([CodeBlockStyle getStyleType])]) { @@ -519,6 +531,9 @@ - (NSString * _Nullable)initiallyProcessHtml:(NSString * _Nonnull)html { fixedHtml = [self stringByAddingNewlinesToTag:@"

" inString:fixedHtml leading:YES trailing:NO]; fixedHtml = [self stringByAddingNewlinesToTag:@"

" inString:fixedHtml leading:YES trailing:NO]; fixedHtml = [self stringByAddingNewlinesToTag:@"

" inString:fixedHtml leading:YES trailing:NO]; + fixedHtml = [self stringByAddingNewlinesToTag:@"

" inString:fixedHtml leading:YES trailing:NO]; + fixedHtml = [self stringByAddingNewlinesToTag:@"

" inString:fixedHtml leading:YES trailing:NO]; + fixedHtml = [self stringByAddingNewlinesToTag:@"
" inString:fixedHtml leading:YES trailing:NO]; // line closing tags fixedHtml = [self stringByAddingNewlinesToTag:@"

" inString:fixedHtml leading:NO trailing:YES]; @@ -526,6 +541,9 @@ - (NSString * _Nullable)initiallyProcessHtml:(NSString * _Nonnull)html { fixedHtml = [self stringByAddingNewlinesToTag:@"
" inString:fixedHtml leading:NO trailing:YES]; fixedHtml = [self stringByAddingNewlinesToTag:@"" inString:fixedHtml leading:NO trailing:YES]; fixedHtml = [self stringByAddingNewlinesToTag:@"" inString:fixedHtml leading:NO trailing:YES]; + fixedHtml = [self stringByAddingNewlinesToTag:@"" inString:fixedHtml leading:NO trailing:YES]; + fixedHtml = [self stringByAddingNewlinesToTag:@"" inString:fixedHtml leading:NO trailing:YES]; + fixedHtml = [self stringByAddingNewlinesToTag:@"" inString:fixedHtml leading:NO trailing:YES]; } return fixedHtml; @@ -745,6 +763,12 @@ - (NSArray *)getTextAndStylesFromHtml:(NSString *)fixedHtml { [styleArr addObject:@([H2Style getStyleType])]; } else if([tagName isEqualToString:@"h3"]) { [styleArr addObject:@([H3Style getStyleType])]; + } else if([tagName isEqualToString:@"h4"]) { + [styleArr addObject:@([H4Style getStyleType])]; + } else if([tagName isEqualToString:@"h5"]) { + [styleArr addObject:@([H5Style getStyleType])]; + } else if([tagName isEqualToString:@"h6"]) { + [styleArr addObject:@([H6Style getStyleType])]; } } else if([tagName isEqualToString:@"ul"]) { [styleArr addObject:@([UnorderedListStyle getStyleType])]; diff --git a/ios/styles/BoldStyle.mm b/ios/styles/BoldStyle.mm index 78b80b4a2..28ac55bfd 100644 --- a/ios/styles/BoldStyle.mm +++ b/ios/styles/BoldStyle.mm @@ -79,6 +79,12 @@ - (BOOL)boldHeadingConflictsInRange:(NSRange)range type:(StyleType)type { if(![_input->config h2Bold]) { return NO; } } else if(type == H3) { if(![_input->config h3Bold]) { return NO; } + } else if(type == H4) { + if(![_input->config h4Bold]) { return NO; } + } else if(type == H5) { + if(![_input->config h5Bold]) { return NO; } + } else if(type == H6) { + if(![_input->config h6Bold]) { return NO; } } id headingStyle = _input->stylesDict[@(type)]; @@ -89,7 +95,7 @@ - (BOOL)boldHeadingConflictsInRange:(NSRange)range type:(StyleType)type { - (BOOL)styleCondition:(id _Nullable)value :(NSRange)range { UIFont *font = (UIFont *)value; - return font != nullptr && [font isBold] && ![self boldHeadingConflictsInRange:range type:H1] && ![self boldHeadingConflictsInRange:range type:H2] && ![self boldHeadingConflictsInRange:range type:H3]; + return font != nullptr && [font isBold] && ![self boldHeadingConflictsInRange:range type:H1] && ![self boldHeadingConflictsInRange:range type:H2] && ![self boldHeadingConflictsInRange:range type:H3] && ![self boldHeadingConflictsInRange:range type:H4] && ![self boldHeadingConflictsInRange:range type:H5] && ![self boldHeadingConflictsInRange:range type:H6]; } - (BOOL)detectStyle:(NSRange)range { diff --git a/ios/styles/H4Style.mm b/ios/styles/H4Style.mm new file mode 100644 index 000000000..2c76eb37a --- /dev/null +++ b/ios/styles/H4Style.mm @@ -0,0 +1,10 @@ +#import "StyleHeaders.h" +#import "EnrichedTextInputView.h" + +@implementation H4Style ++ (StyleType)getStyleType { return H4; } +- (CGFloat)getHeadingFontSize { return [((EnrichedTextInputView *)input)->config h4FontSize]; } +- (BOOL)isHeadingBold { + return [((EnrichedTextInputView *)input)->config h4Bold]; +} +@end diff --git a/ios/styles/H5Style.mm b/ios/styles/H5Style.mm new file mode 100644 index 000000000..b47a69120 --- /dev/null +++ b/ios/styles/H5Style.mm @@ -0,0 +1,10 @@ +#import "StyleHeaders.h" +#import "EnrichedTextInputView.h" + +@implementation H5Style ++ (StyleType)getStyleType { return H5; } +- (CGFloat)getHeadingFontSize { return [((EnrichedTextInputView *)input)->config h5FontSize]; } +- (BOOL)isHeadingBold { + return [((EnrichedTextInputView *)input)->config h5Bold]; +} +@end diff --git a/ios/styles/H6Style.mm b/ios/styles/H6Style.mm new file mode 100644 index 000000000..f224e9dca --- /dev/null +++ b/ios/styles/H6Style.mm @@ -0,0 +1,10 @@ +#import "StyleHeaders.h" +#import "EnrichedTextInputView.h" + +@implementation H6Style ++ (StyleType)getStyleType { return H6; } +- (CGFloat)getHeadingFontSize { return [((EnrichedTextInputView *)input)->config h6FontSize]; } +- (BOOL)isHeadingBold { + return [((EnrichedTextInputView *)input)->config h6Bold]; +} +@end diff --git a/ios/styles/HeadingStyleBase.mm b/ios/styles/HeadingStyleBase.mm index 4d3beacab..b1e0699ce 100644 --- a/ios/styles/HeadingStyleBase.mm +++ b/ios/styles/HeadingStyleBase.mm @@ -6,7 +6,7 @@ @implementation HeadingStyleBase -// mock values since H1/2/3Style classes anyway are used +// mock values since H1/2/3/4/5/6Style classes anyway are used + (StyleType)getStyleType { return None; } - (CGFloat)getHeadingFontSize { return 0; } - (BOOL)isHeadingBold { return false; } diff --git a/ios/utils/StyleHeaders.h b/ios/utils/StyleHeaders.h index f980529cc..a6f353a79 100644 --- a/ios/utils/StyleHeaders.h +++ b/ios/utils/StyleHeaders.h @@ -61,6 +61,15 @@ @interface H3Style : HeadingStyleBase @end +@interface H4Style : HeadingStyleBase +@end + +@interface H5Style : HeadingStyleBase +@end + +@interface H6Style : HeadingStyleBase +@end + @interface UnorderedListStyle : NSObject - (BOOL)handleBackspaceInRange:(NSRange)range replacementText:(NSString *)text; - (BOOL)tryHandlingListShorcutInRange:(NSRange)range replacementText:(NSString *)text; diff --git a/ios/utils/StyleTypeEnum.h b/ios/utils/StyleTypeEnum.h index d20419ddb..b19fcb087 100644 --- a/ios/utils/StyleTypeEnum.h +++ b/ios/utils/StyleTypeEnum.h @@ -10,6 +10,9 @@ typedef NS_ENUM(NSInteger, StyleType) { H1, H2, H3, + H4, + H5, + H6, Link, Mention, Image, diff --git a/src/EnrichedTextInput.tsx b/src/EnrichedTextInput.tsx index 07f2d2fe2..0392aac88 100644 --- a/src/EnrichedTextInput.tsx +++ b/src/EnrichedTextInput.tsx @@ -47,6 +47,9 @@ export interface EnrichedTextInputInstance extends NativeMethods { toggleH1: () => void; toggleH2: () => void; toggleH3: () => void; + toggleH4: () => void; + toggleH5: () => void; + toggleH6: () => void; toggleCodeBlock: () => void; toggleBlockQuote: () => void; toggleOrderedList: () => void; @@ -66,19 +69,18 @@ export interface OnChangeMentionEvent { text: string; } +type HeadingStyle = { + fontSize?: number; + bold?: boolean; +}; + export interface HtmlStyle { - h1?: { - fontSize?: number; - bold?: boolean; - }; - h2?: { - fontSize?: number; - bold?: boolean; - }; - h3?: { - fontSize?: number; - bold?: boolean; - }; + h1?: HeadingStyle; + h2?: HeadingStyle; + h3?: HeadingStyle; + h4?: HeadingStyle; + h5?: HeadingStyle; + h6?: HeadingStyle; blockquote?: { borderColor?: ColorValue; borderWidth?: number; @@ -257,6 +259,15 @@ export const EnrichedTextInput = ({ toggleH3: () => { Commands.toggleH3(nullthrows(nativeRef.current)); }, + toggleH4: () => { + Commands.toggleH4(nullthrows(nativeRef.current)); + }, + toggleH5: () => { + Commands.toggleH5(nullthrows(nativeRef.current)); + }, + toggleH6: () => { + Commands.toggleH6(nullthrows(nativeRef.current)); + }, toggleCodeBlock: () => { Commands.toggleCodeBlock(nullthrows(nativeRef.current)); }, diff --git a/src/EnrichedTextInputNativeComponent.ts b/src/EnrichedTextInputNativeComponent.ts index e0b943201..ff0caa6fc 100644 --- a/src/EnrichedTextInputNativeComponent.ts +++ b/src/EnrichedTextInputNativeComponent.ts @@ -25,6 +25,9 @@ export interface OnChangeStateEvent { isH1: boolean; isH2: boolean; isH3: boolean; + isH4: boolean; + isH5: boolean; + isH6: boolean; isCodeBlock: boolean; isBlockQuote: boolean; isOrderedList: boolean; @@ -70,19 +73,18 @@ export interface MentionStyleProperties { textDecorationLine?: 'underline' | 'none'; } +type Heading = { + fontSize?: Float; + bold?: boolean; +}; + export interface HtmlStyleInternal { - h1?: { - fontSize?: Float; - bold?: boolean; - }; - h2?: { - fontSize?: Float; - bold?: boolean; - }; - h3?: { - fontSize?: Float; - bold?: boolean; - }; + h1?: Heading; + h2?: Heading; + h3?: Heading; + h4?: Heading; + h5?: Heading; + h6?: Heading; blockquote?: { borderColor?: ColorValue; borderWidth?: Float; @@ -180,6 +182,9 @@ interface NativeCommands { toggleH1: (viewRef: React.ElementRef) => void; toggleH2: (viewRef: React.ElementRef) => void; toggleH3: (viewRef: React.ElementRef) => void; + toggleH4: (viewRef: React.ElementRef) => void; + toggleH5: (viewRef: React.ElementRef) => void; + toggleH6: (viewRef: React.ElementRef) => void; toggleCodeBlock: (viewRef: React.ElementRef) => void; toggleBlockQuote: (viewRef: React.ElementRef) => void; toggleOrderedList: (viewRef: React.ElementRef) => void; @@ -220,6 +225,9 @@ export const Commands: NativeCommands = codegenNativeCommands({ 'toggleH1', 'toggleH2', 'toggleH3', + 'toggleH4', + 'toggleH5', + 'toggleH6', 'toggleCodeBlock', 'toggleBlockQuote', 'toggleOrderedList', diff --git a/src/normalizeHtmlStyle.ts b/src/normalizeHtmlStyle.ts index 96a7053ea..299cc062f 100644 --- a/src/normalizeHtmlStyle.ts +++ b/src/normalizeHtmlStyle.ts @@ -18,6 +18,18 @@ const defaultStyle: Required = { fontSize: 20, bold: false, }, + h4: { + fontSize: 16, + bold: false, + }, + h5: { + fontSize: 14, + bold: false, + }, + h6: { + fontSize: 12, + bold: false, + }, blockquote: { borderColor: 'darkgray', borderWidth: 4,