Skip to content

Commit

Permalink
Merge pull request #522 from MohamedRejeb/1.x
Browse files Browse the repository at this point in the history
Press `Enter` on an empty list item: Exit the list
  • Loading branch information
MohamedRejeb authored Feb 17, 2025
2 parents 2c4fda0 + cfbc493 commit 727b436
Show file tree
Hide file tree
Showing 7 changed files with 184 additions and 2 deletions.
2 changes: 2 additions & 0 deletions richeditor-compose/api/android/richeditor-compose.api
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ public final class com/mohamedrejeb/richeditor/model/RichTextConfig {
public final fun getCodeSpanBackgroundColor-0d7_KjU ()J
public final fun getCodeSpanColor-0d7_KjU ()J
public final fun getCodeSpanStrokeColor-0d7_KjU ()J
public final fun getExitListOnEmptyItem ()Z
public final fun getLinkColor-0d7_KjU ()J
public final fun getLinkTextDecoration ()Landroidx/compose/ui/text/style/TextDecoration;
public final fun getListIndent ()I
Expand All @@ -113,6 +114,7 @@ public final class com/mohamedrejeb/richeditor/model/RichTextConfig {
public final fun setCodeSpanBackgroundColor-8_81llA (J)V
public final fun setCodeSpanColor-8_81llA (J)V
public final fun setCodeSpanStrokeColor-8_81llA (J)V
public final fun setExitListOnEmptyItem (Z)V
public final fun setLinkColor-8_81llA (J)V
public final fun setLinkTextDecoration (Landroidx/compose/ui/text/style/TextDecoration;)V
public final fun setListIndent (I)V
Expand Down
2 changes: 2 additions & 0 deletions richeditor-compose/api/desktop/richeditor-compose.api
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ public final class com/mohamedrejeb/richeditor/model/RichTextConfig {
public final fun getCodeSpanBackgroundColor-0d7_KjU ()J
public final fun getCodeSpanColor-0d7_KjU ()J
public final fun getCodeSpanStrokeColor-0d7_KjU ()J
public final fun getExitListOnEmptyItem ()Z
public final fun getLinkColor-0d7_KjU ()J
public final fun getLinkTextDecoration ()Landroidx/compose/ui/text/style/TextDecoration;
public final fun getListIndent ()I
Expand All @@ -113,6 +114,7 @@ public final class com/mohamedrejeb/richeditor/model/RichTextConfig {
public final fun setCodeSpanBackgroundColor-8_81llA (J)V
public final fun setCodeSpanColor-8_81llA (J)V
public final fun setCodeSpanStrokeColor-8_81llA (J)V
public final fun setExitListOnEmptyItem (Z)V
public final fun setLinkColor-8_81llA (J)V
public final fun setLinkTextDecoration (Landroidx/compose/ui/text/style/TextDecoration;)V
public final fun setListIndent (I)V
Expand Down
3 changes: 3 additions & 0 deletions richeditor-compose/api/richeditor-compose.klib.api
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,9 @@ final class com.mohamedrejeb.richeditor.model/RichTextConfig { // com.mohamedrej
final var codeSpanStrokeColor // com.mohamedrejeb.richeditor.model/RichTextConfig.codeSpanStrokeColor|{}codeSpanStrokeColor[0]
final fun <get-codeSpanStrokeColor>(): androidx.compose.ui.graphics/Color // com.mohamedrejeb.richeditor.model/RichTextConfig.codeSpanStrokeColor.<get-codeSpanStrokeColor>|<get-codeSpanStrokeColor>(){}[0]
final fun <set-codeSpanStrokeColor>(androidx.compose.ui.graphics/Color) // com.mohamedrejeb.richeditor.model/RichTextConfig.codeSpanStrokeColor.<set-codeSpanStrokeColor>|<set-codeSpanStrokeColor>(androidx.compose.ui.graphics.Color){}[0]
final var exitListOnEmptyItem // com.mohamedrejeb.richeditor.model/RichTextConfig.exitListOnEmptyItem|{}exitListOnEmptyItem[0]
final fun <get-exitListOnEmptyItem>(): kotlin/Boolean // com.mohamedrejeb.richeditor.model/RichTextConfig.exitListOnEmptyItem.<get-exitListOnEmptyItem>|<get-exitListOnEmptyItem>(){}[0]
final fun <set-exitListOnEmptyItem>(kotlin/Boolean) // com.mohamedrejeb.richeditor.model/RichTextConfig.exitListOnEmptyItem.<set-exitListOnEmptyItem>|<set-exitListOnEmptyItem>(kotlin.Boolean){}[0]
final var linkColor // com.mohamedrejeb.richeditor.model/RichTextConfig.linkColor|{}linkColor[0]
final fun <get-linkColor>(): androidx.compose.ui.graphics/Color // com.mohamedrejeb.richeditor.model/RichTextConfig.linkColor.<get-linkColor>|<get-linkColor>(){}[0]
final fun <set-linkColor>(androidx.compose.ui.graphics/Color) // com.mohamedrejeb.richeditor.model/RichTextConfig.linkColor.<set-linkColor>|<set-linkColor>(androidx.compose.ui.graphics.Color){}[0]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,15 @@ public class RichTextConfig internal constructor(
* Default is `true`.
*/
public var preserveStyleOnEmptyLine: Boolean = true

/**
* Whether to exit the list when pressing Enter on an empty list item.
* When true, pressing Enter on an empty list item will convert it to a normal paragraph.
* When false, pressing Enter on an empty list item will create a new list item.
*
* Default is `true`.
*/
public var exitListOnEmptyItem: Boolean = true
}

internal const val DefaultListIndent = 38
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2252,8 +2252,27 @@ public class RichTextState internal constructor(
val isSelectionAtNewRichSpan =
newParagraphFirstRichSpan?.textRange?.min == tempTextFieldValue.selection.min - 1

// Check if the cursor is at the new paragraph
// Check if the cursor is at the new paragraph and if it's an empty list item
if (
config.exitListOnEmptyItem &&
isSelectionAtNewRichSpan &&
richSpan.paragraph.isEmpty() &&
richSpan.paragraph.type is ConfigurableListLevel
) {
// Exit list by removing list formatting
tempTextFieldValue = updateParagraphType(
paragraph = richSpan.paragraph,
newType = DefaultParagraph(),
textFieldValue = tempTextFieldValue,
)
newParagraphFirstRichSpan.spanStyle = SpanStyle()
newParagraphFirstRichSpan.richSpanStyle = RichSpanStyle.Default

// Ignore adding the new paragraph
index--
continue
} else
if (
(!config.preserveStyleOnEmptyLine || richSpan.paragraph.isEmpty()) &&
isSelectionAtNewRichSpan
) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
package com.mohamedrejeb.richeditor.model

import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.input.TextFieldValue
import com.mohamedrejeb.richeditor.annotation.ExperimentalRichTextApi
import com.mohamedrejeb.richeditor.paragraph.RichParagraph
import com.mohamedrejeb.richeditor.paragraph.type.DefaultParagraph
import com.mohamedrejeb.richeditor.paragraph.type.OrderedList
import org.intellij.markdown.lexer.Compat.assert
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertIs

@OptIn(ExperimentalRichTextApi::class)
class RichTextStateOrderedListTest {

@Test
Expand Down Expand Up @@ -66,4 +71,89 @@ class RichTextStateOrderedListTest {
assertEquals(2, lastParagraphType.level)
}

}
@Test
fun testAddListItemWithEnter() {
val richTextState = RichTextState(
listOf(
RichParagraph(
type = OrderedList(
number = 1,
)
).also {
it.children.add(
RichSpan(
text = "test",
paragraph = it,
)
)
}
)
)

// Simulate pressing Enter on non-empty list item
richTextState.selection = TextRange(richTextState.annotatedString.length)
richTextState.addTextAfterSelection("\n")

// Verify that list formatting is removed
assertEquals(2, richTextState.richParagraphList.size)
assertIs<OrderedList>(richTextState.richParagraphList[0].type)
assertIs<OrderedList>(richTextState.richParagraphList[1].type)
}

@Test
fun testExitEmptyListItem() {
// Test with exitListOnEmptyItem = true (default)
val richTextState = RichTextState(
listOf(
RichParagraph(
type = OrderedList(
number = 1,
)
).also {
it.children.add(
RichSpan(
text = "",
paragraph = it,
)
)
}
)
)

// Simulate pressing Enter on empty list item
richTextState.selection = TextRange(richTextState.annotatedString.length)
richTextState.addTextAfterSelection("\n")

// Verify that list formatting is removed
assertEquals(1, richTextState.richParagraphList.size)
assertIs<DefaultParagraph>(richTextState.richParagraphList[0].type)

// Test with exitListOnEmptyItem = false
val richTextState2 = RichTextState(
listOf(
RichParagraph(
type = OrderedList(
number = 1,
)
).also {
it.children.add(
RichSpan(
text = "",
paragraph = it,
)
)
}
)
)
richTextState2.config.exitListOnEmptyItem = false

// Simulate pressing Enter on empty list item
richTextState2.selection = TextRange(richTextState2.annotatedString.length)
richTextState2.addTextAfterSelection("\n")

// Verify that list formatting is preserved and a new list item is created
assertEquals(2, richTextState2.richParagraphList.size)
assertIs<OrderedList>(richTextState2.richParagraphList[0].type)
assertIs<OrderedList>(richTextState2.richParagraphList[1].type)
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
package com.mohamedrejeb.richeditor.model

import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.input.TextFieldValue
import com.mohamedrejeb.richeditor.annotation.ExperimentalRichTextApi
import com.mohamedrejeb.richeditor.paragraph.RichParagraph
import com.mohamedrejeb.richeditor.paragraph.type.DefaultParagraph
import com.mohamedrejeb.richeditor.paragraph.type.UnorderedList
import com.mohamedrejeb.richeditor.paragraph.type.UnorderedListStyleType
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertIs

@OptIn(ExperimentalRichTextApi::class)
class RichTextStateUnorderedListTest {
Expand Down Expand Up @@ -170,4 +174,57 @@ class RichTextStateUnorderedListTest {
// Should fallback to bullet point when the prefix list is empty
assertEquals("", paragraph.type.startRichSpan.text)
}

@Test
fun testExitEmptyListItem() {
// Test with exitListOnEmptyItem = true (default)
val richTextState = RichTextState(
initialRichParagraphList = listOf(
RichParagraph(
type = UnorderedList(),
).also {
it.children.add(
RichSpan(
text = "",
paragraph = it,
),
)
}
)
)

// Simulate pressing Enter on empty list item
richTextState.selection = TextRange(richTextState.annotatedString.length)
richTextState.addTextAfterSelection("\n")

// Verify that list formatting is removed
assertEquals(1, richTextState.richParagraphList.size)
assertIs<DefaultParagraph>(richTextState.richParagraphList[0].type)

// Test with exitListOnEmptyItem = false
val richTextState2 = RichTextState(
initialRichParagraphList = listOf(
RichParagraph(
type = UnorderedList(),
).also {
it.children.add(
RichSpan(
text = "",
paragraph = it,
),
)
}
)
)
richTextState2.config.exitListOnEmptyItem = false

// Simulate pressing Enter on empty list item
richTextState2.selection = TextRange(richTextState2.annotatedString.length)
richTextState2.addTextAfterSelection("\n")

// Verify that list formatting is preserved
assertEquals(2, richTextState2.richParagraphList.size)
assertIs<UnorderedList>(richTextState2.richParagraphList[0].type)
assertIs<UnorderedList>(richTextState2.richParagraphList[1].type)
}
}

0 comments on commit 727b436

Please sign in to comment.