mirror of
https://github.com/anyproto/anytype-kotlin.git
synced 2025-06-08 05:47:05 +09:00
Editor | Feature | Table of Contents, indents + dnd (#2236)
* update table of contents items depth + test * set untitled if header name is empty * fix clicks on toc Co-authored-by: konstantiniiv <ki@anytype.io>
This commit is contained in:
parent
06d865a9dc
commit
6e5d0dc3d7
4 changed files with 199 additions and 32 deletions
|
@ -33,10 +33,6 @@ class TableOfContents(
|
|||
fallback = { e -> itemView.onTouchEvent(e) }
|
||||
)
|
||||
|
||||
init {
|
||||
itemView.setOnTouchListener { v, e -> editorTouchProcessor.process(v, e) }
|
||||
}
|
||||
|
||||
fun bind(item: BlockView.TableOfContents, clicked: (ListenerType) -> Unit) {
|
||||
cleanup()
|
||||
selected.isSelected = item.isSelected
|
||||
|
@ -45,6 +41,7 @@ class TableOfContents(
|
|||
id = generateViewId()
|
||||
setPadding(getPadding(header.depth), 0, 0, 0)
|
||||
setName(header.name)
|
||||
setOnTouchListener { v, e -> editorTouchProcessor.process(v, e) }
|
||||
setOnClickListener {
|
||||
clicked.invoke(
|
||||
ListenerType.TableOfContentsItem(
|
||||
|
@ -53,10 +50,6 @@ class TableOfContents(
|
|||
)
|
||||
)
|
||||
}
|
||||
setOnLongClickListener {
|
||||
clicked.invoke(ListenerType.LongClick(item.id))
|
||||
true
|
||||
}
|
||||
}
|
||||
container.addView(textview)
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import android.graphics.Paint
|
|||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.FrameLayout
|
||||
import com.anytypeio.anytype.core_ui.R
|
||||
import com.anytypeio.anytype.core_ui.databinding.TvTableOfContentsBinding
|
||||
|
||||
class TableOfContentsItemWidget @JvmOverloads constructor(
|
||||
|
@ -23,6 +24,6 @@ class TableOfContentsItemWidget @JvmOverloads constructor(
|
|||
}
|
||||
|
||||
fun setName(name: String) {
|
||||
textView.text = name
|
||||
textView.text = name.ifBlank { resources.getString(R.string.untitled) }
|
||||
}
|
||||
}
|
|
@ -543,7 +543,6 @@ class DefaultBlockViewRenderer @Inject constructor(
|
|||
result.add(
|
||||
toc(
|
||||
block = block,
|
||||
indent = indent,
|
||||
mode = mode,
|
||||
selection = selection,
|
||||
blocks = children
|
||||
|
@ -1455,17 +1454,30 @@ class DefaultBlockViewRenderer @Inject constructor(
|
|||
private fun toc(
|
||||
block: Block,
|
||||
blocks: List<Block>,
|
||||
indent: Int,
|
||||
mode: EditorMode,
|
||||
selection: Set<Id>
|
||||
): BlockView.TableOfContents {
|
||||
val headers = blocks.filter { (it.content as? Content.Text)?.isHeader() ?: false }
|
||||
var depth = 0
|
||||
var isH1Present = false
|
||||
var isH2Present = false
|
||||
val items = mutableListOf<BlockView.TableOfContentsItem>()
|
||||
headers.forEachIndexed { index, b ->
|
||||
val content = b.content<Content.Text>()
|
||||
if (content.style == Content.Text.Style.H1) {
|
||||
depth = 0
|
||||
var depth = 0
|
||||
when (content.style) {
|
||||
Content.Text.Style.H1 -> {
|
||||
isH1Present = true
|
||||
isH2Present = false
|
||||
}
|
||||
Content.Text.Style.H2 -> {
|
||||
isH2Present = true
|
||||
if (isH1Present) depth += 1
|
||||
}
|
||||
Content.Text.Style.H3 -> {
|
||||
if (isH1Present) depth += 1
|
||||
if (isH2Present) depth += 1
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
val item = BlockView.TableOfContentsItem(
|
||||
id = b.id,
|
||||
|
@ -1473,24 +1485,6 @@ class DefaultBlockViewRenderer @Inject constructor(
|
|||
depth = depth
|
||||
)
|
||||
items.add(item)
|
||||
val next = if (index + 1 < headers.size) {
|
||||
headers[index + 1]
|
||||
} else {
|
||||
null
|
||||
}
|
||||
if (next != null) {
|
||||
val contentNext = next.content<Content.Text>()
|
||||
if (content.style == Content.Text.Style.H1 && (
|
||||
contentNext.style == Content.Text.Style.H2
|
||||
|| contentNext.style == Content.Text.Style.H3
|
||||
)
|
||||
) {
|
||||
depth += 1
|
||||
}
|
||||
if (content.style == Content.Text.Style.H2 && contentNext.style == Content.Text.Style.H3) {
|
||||
depth += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val toc = BlockView.TableOfContents(
|
||||
|
|
|
@ -597,4 +597,183 @@ class EditorTableOfContentsBlockTest : EditorPresentationTestSetup() {
|
|||
|
||||
assertEquals(expected, result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should have proper depth for headers`() {
|
||||
val title = MockTypicalDocumentFactory.title
|
||||
val header = MockTypicalDocumentFactory.header
|
||||
|
||||
val blockHeaderOne1 = Block(
|
||||
id = MockDataFactory.randomUuid(),
|
||||
fields = Block.Fields(emptyMap()),
|
||||
content = Block.Content.Text(
|
||||
text = MockDataFactory.randomString(),
|
||||
marks = emptyList(),
|
||||
style = Block.Content.Text.Style.H1
|
||||
),
|
||||
children = emptyList()
|
||||
)
|
||||
|
||||
val blockHeaderTwo1 = Block(
|
||||
id = MockDataFactory.randomUuid(),
|
||||
fields = Block.Fields(emptyMap()),
|
||||
content = Block.Content.Text(
|
||||
text = "",
|
||||
marks = emptyList(),
|
||||
style = Block.Content.Text.Style.H2
|
||||
),
|
||||
children = emptyList()
|
||||
)
|
||||
|
||||
val blockHeaderTwo2 = Block(
|
||||
id = MockDataFactory.randomUuid(),
|
||||
fields = Block.Fields(emptyMap()),
|
||||
content = Block.Content.Text(
|
||||
text = "",
|
||||
marks = emptyList(),
|
||||
style = Block.Content.Text.Style.H2
|
||||
),
|
||||
children = emptyList()
|
||||
)
|
||||
|
||||
val blockHeaderThree = Block(
|
||||
id = MockDataFactory.randomUuid(),
|
||||
fields = Block.Fields(emptyMap()),
|
||||
content = Block.Content.Text(
|
||||
text = "",
|
||||
marks = emptyList(),
|
||||
style = Block.Content.Text.Style.H3
|
||||
),
|
||||
children = emptyList()
|
||||
)
|
||||
|
||||
val blockHeaderThree2 = Block(
|
||||
id = MockDataFactory.randomUuid(),
|
||||
fields = Block.Fields(emptyMap()),
|
||||
content = Block.Content.Text(
|
||||
text = "",
|
||||
marks = emptyList(),
|
||||
style = Block.Content.Text.Style.H3
|
||||
),
|
||||
children = emptyList()
|
||||
)
|
||||
|
||||
val blockToC = Block(
|
||||
id = MockDataFactory.randomUuid(),
|
||||
fields = Block.Fields(emptyMap()),
|
||||
content = Block.Content.TableOfContents,
|
||||
children = emptyList()
|
||||
)
|
||||
|
||||
val page = Block(
|
||||
id = root,
|
||||
fields = Block.Fields(emptyMap()),
|
||||
content = Block.Content.Smart(SmartBlockType.PAGE),
|
||||
children = listOf(
|
||||
header.id,
|
||||
blockToC.id,
|
||||
blockHeaderOne1.id,
|
||||
blockHeaderTwo1.id,
|
||||
blockHeaderThree.id,
|
||||
blockHeaderTwo2.id,
|
||||
blockHeaderThree2.id
|
||||
)
|
||||
)
|
||||
|
||||
val doc = listOf(
|
||||
page, header, title,
|
||||
blockToC,
|
||||
blockHeaderOne1,
|
||||
blockHeaderTwo1,
|
||||
blockHeaderThree,
|
||||
blockHeaderTwo2,
|
||||
blockHeaderThree2
|
||||
)
|
||||
|
||||
stubInterceptEvents()
|
||||
stubInterceptThreadStatus()
|
||||
stubGetObjectTypes(objectTypes = listOf())
|
||||
stubOpenDocument(
|
||||
document = doc,
|
||||
details = Block.Details(),
|
||||
relations = listOf()
|
||||
)
|
||||
stubUpdateText()
|
||||
|
||||
val vm = buildViewModel()
|
||||
|
||||
//TESTING
|
||||
vm.apply { onStart(root) }
|
||||
|
||||
val expected = ViewState.Success(
|
||||
blocks = listOf(
|
||||
BlockView.Title.Basic(
|
||||
id = title.id,
|
||||
isFocused = false,
|
||||
text = title.content<TXT>().text,
|
||||
mode = BlockView.Mode.EDIT
|
||||
),
|
||||
BlockView.TableOfContents(
|
||||
id = blockToC.id,
|
||||
backgroundColor = blockToC.backgroundColor,
|
||||
isSelected = false,
|
||||
items = listOf(
|
||||
BlockView.TableOfContentsItem(
|
||||
id = blockHeaderOne1.id,
|
||||
name = blockHeaderOne1.content.asText().text,
|
||||
depth = 0
|
||||
),
|
||||
BlockView.TableOfContentsItem(
|
||||
id = blockHeaderTwo1.id,
|
||||
name = blockHeaderTwo1.content.asText().text,
|
||||
depth = 1
|
||||
),
|
||||
BlockView.TableOfContentsItem(
|
||||
id = blockHeaderThree.id,
|
||||
name = blockHeaderThree.content.asText().text,
|
||||
depth = 2
|
||||
),
|
||||
BlockView.TableOfContentsItem(
|
||||
id = blockHeaderTwo2.id,
|
||||
name = blockHeaderTwo2.content.asText().text,
|
||||
depth = 1
|
||||
),
|
||||
BlockView.TableOfContentsItem(
|
||||
id = blockHeaderThree2.id,
|
||||
name = blockHeaderThree2.content.asText().text,
|
||||
depth = 2
|
||||
)
|
||||
)
|
||||
),
|
||||
BlockView.Text.Header.One(
|
||||
id = blockHeaderOne1.id,
|
||||
indent = 0,
|
||||
text = blockHeaderOne1.content.asText().text
|
||||
),
|
||||
BlockView.Text.Header.Two(
|
||||
id = blockHeaderTwo1.id,
|
||||
indent = 0,
|
||||
text = blockHeaderTwo1.content.asText().text
|
||||
),
|
||||
BlockView.Text.Header.Three(
|
||||
id = blockHeaderThree.id,
|
||||
indent = 0,
|
||||
text = blockHeaderThree.content.asText().text
|
||||
),
|
||||
BlockView.Text.Header.Two(
|
||||
id = blockHeaderTwo2.id,
|
||||
indent = 0,
|
||||
text = blockHeaderTwo2.content.asText().text
|
||||
),
|
||||
BlockView.Text.Header.Three(
|
||||
id = blockHeaderThree2.id,
|
||||
indent = 0,
|
||||
text = blockHeaderThree2.content.asText().text
|
||||
)
|
||||
)
|
||||
)
|
||||
val result = vm.state.test().value()
|
||||
|
||||
assertEquals(expected, result)
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue