All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.com.mikepenz.markdown.compose.components.MarkdownComponents.kt Maven / Gradle / Ivy

package com.mikepenz.markdown.compose.components

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.ui.Modifier
import com.mikepenz.markdown.compose.LocalReferenceLinkHandler
import com.mikepenz.markdown.compose.elements.*
import com.mikepenz.markdown.model.MarkdownTypography
import org.intellij.markdown.IElementType
import org.intellij.markdown.MarkdownElementTypes
import org.intellij.markdown.MarkdownTokenTypes
import org.intellij.markdown.ast.ASTNode
import org.intellij.markdown.ast.findChildOfType
import org.intellij.markdown.ast.getTextInNode

typealias MarkdownComponent = @Composable ColumnScope.(MarkdownComponentModel) -> Unit

typealias CustomMarkdownComponent = @Composable ColumnScope.(IElementType, MarkdownComponentModel) -> Unit

/**
 * Model holding data relevant for a component
 */
data class MarkdownComponentModel(
    val content: String,
    val node: ASTNode,
    val typography: MarkdownTypography,
)

private fun MarkdownComponentModel.getTextInNode() = node.getTextInNode(content)

@Composable
fun markdownComponents(
    text: MarkdownComponent = CurrentComponentsBridge.text,
    eol: MarkdownComponent = CurrentComponentsBridge.eol,
    codeFence: MarkdownComponent = CurrentComponentsBridge.codeFence,
    codeBlock: MarkdownComponent = CurrentComponentsBridge.codeBlock,
    heading1: MarkdownComponent = CurrentComponentsBridge.heading1,
    heading2: MarkdownComponent = CurrentComponentsBridge.heading2,
    heading3: MarkdownComponent = CurrentComponentsBridge.heading3,
    heading4: MarkdownComponent = CurrentComponentsBridge.heading4,
    heading5: MarkdownComponent = CurrentComponentsBridge.heading5,
    heading6: MarkdownComponent = CurrentComponentsBridge.heading6,
    setextHeading1: MarkdownComponent = CurrentComponentsBridge.setextHeading1,
    setextHeading2: MarkdownComponent = CurrentComponentsBridge.setextHeading2,
    blockQuote: MarkdownComponent = CurrentComponentsBridge.blockQuote,
    paragraph: MarkdownComponent = CurrentComponentsBridge.paragraph,
    orderedList: MarkdownComponent = CurrentComponentsBridge.orderedList,
    unorderedList: MarkdownComponent = CurrentComponentsBridge.unorderedList,
    image: MarkdownComponent = CurrentComponentsBridge.image,
    linkDefinition: MarkdownComponent = CurrentComponentsBridge.linkDefinition,
    horizontalRule: MarkdownComponent = CurrentComponentsBridge.horizontalRule,
    custom: CustomMarkdownComponent? = CurrentComponentsBridge.custom,
): MarkdownComponents = DefaultMarkdownComponents(
    text = text,
    eol = eol,
    codeFence = codeFence,
    codeBlock = codeBlock,
    heading1 = heading1,
    heading2 = heading2,
    heading3 = heading3,
    heading4 = heading4,
    heading5 = heading5,
    heading6 = heading6,
    setextHeading1 = setextHeading1,
    setextHeading2 = setextHeading2,
    blockQuote = blockQuote,
    paragraph = paragraph,
    orderedList = orderedList,
    unorderedList = unorderedList,
    image = image,
    linkDefinition = linkDefinition,
    horizontalRule = horizontalRule,
    custom = custom,
)

/**
 * Interface defining all supported components.
 */
@Stable
interface MarkdownComponents {
    val text: MarkdownComponent
    val eol: MarkdownComponent
    val codeFence: MarkdownComponent
    val codeBlock: MarkdownComponent
    val heading1: MarkdownComponent
    val heading2: MarkdownComponent
    val heading3: MarkdownComponent
    val heading4: MarkdownComponent
    val heading5: MarkdownComponent
    val heading6: MarkdownComponent
    val setextHeading1: MarkdownComponent
    val setextHeading2: MarkdownComponent
    val blockQuote: MarkdownComponent
    val paragraph: MarkdownComponent
    val orderedList: MarkdownComponent
    val unorderedList: MarkdownComponent
    val image: MarkdownComponent
    val linkDefinition: MarkdownComponent
    val horizontalRule: MarkdownComponent
    val custom: CustomMarkdownComponent?
}

private class DefaultMarkdownComponents(
    override val text: MarkdownComponent,
    override val eol: MarkdownComponent,
    override val codeFence: MarkdownComponent,
    override val codeBlock: MarkdownComponent,
    override val heading1: MarkdownComponent,
    override val heading2: MarkdownComponent,
    override val heading3: MarkdownComponent,
    override val heading4: MarkdownComponent,
    override val heading5: MarkdownComponent,
    override val heading6: MarkdownComponent,
    override val setextHeading1: MarkdownComponent,
    override val setextHeading2: MarkdownComponent,
    override val blockQuote: MarkdownComponent,
    override val paragraph: MarkdownComponent,
    override val orderedList: MarkdownComponent,
    override val unorderedList: MarkdownComponent,
    override val image: MarkdownComponent,
    override val linkDefinition: MarkdownComponent,
    override val horizontalRule: MarkdownComponent,
    override val custom: CustomMarkdownComponent?,
) : MarkdownComponents

/**
 * Adapts the universal signature @Composable (MarkdownComponentModel) -> Unit to the existing components.
 */
object CurrentComponentsBridge {
    val text: MarkdownComponent = {
        MarkdownText(it.getTextInNode().toString())
    }
    val eol: MarkdownComponent = { }
    val codeFence: MarkdownComponent = {
        MarkdownCodeFence(it.content, it.node)
    }
    val codeBlock: MarkdownComponent = {
        MarkdownCodeBlock(it.content, it.node)
    }
    val heading1: MarkdownComponent = {
        MarkdownHeader(it.content, it.node, it.typography.h1)
    }
    val heading2: MarkdownComponent = {
        MarkdownHeader(it.content, it.node, it.typography.h2)
    }
    val heading3: MarkdownComponent = {
        MarkdownHeader(it.content, it.node, it.typography.h3)
    }
    val heading4: MarkdownComponent = {
        MarkdownHeader(it.content, it.node, it.typography.h4)
    }
    val heading5: MarkdownComponent = {
        MarkdownHeader(it.content, it.node, it.typography.h5)
    }
    val heading6: MarkdownComponent = {
        MarkdownHeader(it.content, it.node, it.typography.h6)
    }
    val setextHeading1: MarkdownComponent = {
        MarkdownHeader(it.content, it.node, it.typography.h1, MarkdownTokenTypes.SETEXT_CONTENT)
    }
    val setextHeading2: MarkdownComponent = {
        MarkdownHeader(it.content, it.node, it.typography.h2, MarkdownTokenTypes.SETEXT_CONTENT)
    }
    val blockQuote: MarkdownComponent = {
        MarkdownBlockQuote(it.content, it.node)
    }
    val paragraph: MarkdownComponent = {
        MarkdownParagraph(it.content, it.node, style = it.typography.paragraph)
    }
    val orderedList: MarkdownComponent = {
        Column(modifier = Modifier) {
            MarkdownOrderedList(it.content, it.node, style = it.typography.ordered)
        }
    }
    val unorderedList: MarkdownComponent = {
        Column(modifier = Modifier) {
            MarkdownBulletList(it.content, it.node, style = it.typography.bullet)
        }
    }
    val image: MarkdownComponent = {
        MarkdownImage(it.content, it.node)
    }
    val linkDefinition: MarkdownComponent = {
        val linkLabel =
            it.node.findChildOfType(MarkdownElementTypes.LINK_LABEL)?.getTextInNode(it.content)
                ?.toString()
        if (linkLabel != null) {
            val destination = it.node.findChildOfType(MarkdownElementTypes.LINK_DESTINATION)
                ?.getTextInNode(it.content)?.toString()
            LocalReferenceLinkHandler.current.store(linkLabel, destination)
        }
    }
    val horizontalRule: MarkdownComponent = {
        MarkdownDivider(Modifier.fillMaxWidth())
    }
    val custom: CustomMarkdownComponent? = null
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy