Skip to content

Commit

Permalink
Auto-localize typed boxes
Browse files Browse the repository at this point in the history
  • Loading branch information
iamgio committed Oct 14, 2024
1 parent a68351e commit 63213cd
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 2 deletions.
25 changes: 23 additions & 2 deletions stdlib/src/main/kotlin/eu/iamgio/quarkdown/stdlib/Layout.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package eu.iamgio.quarkdown.stdlib

import eu.iamgio.quarkdown.ast.InlineContent
import eu.iamgio.quarkdown.ast.InlineMarkdownContent
import eu.iamgio.quarkdown.ast.MarkdownContent
import eu.iamgio.quarkdown.ast.base.block.Table
import eu.iamgio.quarkdown.ast.dsl.buildInline
import eu.iamgio.quarkdown.ast.quarkdown.block.Aligned
import eu.iamgio.quarkdown.ast.quarkdown.block.Box
import eu.iamgio.quarkdown.ast.quarkdown.block.Clipped
Expand Down Expand Up @@ -187,7 +189,9 @@ fun clip(

/**
* Inserts content in a box.
* @param title box title. If unset, the box is untitled
* @param title box title. If unset:
* - If the locale ([docLanguage]) is set and supported, the title is localized according to the box [type]
* - Otherwise, the box is untitled
* @param type box type. If unset, it defaults to a callout box
* @param padding padding around the box. If unset, the box uses the default padding
* @param backgroundColor background color. If unset, the box uses the default color
Expand All @@ -196,13 +200,30 @@ fun clip(
* @return the new box node
*/
fun box(
@Injected context: Context,
title: InlineMarkdownContent? = null,
type: Box.Type = Box.Type.CALLOUT,
padding: Size? = null,
@Name("background") backgroundColor: Color? = null,
@Name("foreground") foregroundColor: Color? = null,
body: MarkdownContent,
) = Box(title?.children, type, padding, backgroundColor, foregroundColor, body.children).wrappedAsValue()
): NodeValue {
// Localizes the title according to the box type,
// if the title is not manually set.
fun localizedTitle(): InlineContent? =
Stdlib.localizeOrNull(type.name, context)?.let {
buildInline { text(it) }
}

return Box(
title?.children ?: localizedTitle(),
type,
padding,
backgroundColor,
foregroundColor,
body.children,
).wrappedAsValue()
}

/**
* Inserts content in a collapsible block, whose content can be hidden or shown by interacting with it.
Expand Down
18 changes: 18 additions & 0 deletions stdlib/src/main/kotlin/eu/iamgio/quarkdown/stdlib/Stdlib.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package eu.iamgio.quarkdown.stdlib

import eu.iamgio.quarkdown.context.Context
import eu.iamgio.quarkdown.context.localization.localizeOrNull
import eu.iamgio.quarkdown.function.library.Library
import eu.iamgio.quarkdown.function.library.LibraryExporter
import eu.iamgio.quarkdown.function.library.loader.MultiFunctionLibraryLoader
Expand All @@ -16,6 +18,11 @@ typealias Module = Set<KFunction<OutputValue<*>>>
* Exporter of Quarkdown's standard library.
*/
object Stdlib : LibraryExporter {
/**
* The name of the localization table used by this library.
*/
private const val LOCALIZATION_TABLE = "std"

override val library: Library
get() =
MultiFunctionLibraryLoader(name = "stdlib").load(
Expand Down Expand Up @@ -43,4 +50,15 @@ object Stdlib : LibraryExporter {
},
),
)

/**
* Localizes a key from the stdlib table ([LOCALIZATION_TABLE]).
* @param key localization key
* @param context context to localize for
* @return the localized string if the [key] exists in the `std` table, `null` otherwise
*/
fun localizeOrNull(
key: String,
context: Context,
): String? = context.localizeOrNull(LOCALIZATION_TABLE, key.lowercase())
}
75 changes: 75 additions & 0 deletions test/src/test/kotlin/eu/iamgio/quarkdown/test/FullPipelineTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -846,6 +846,81 @@ class FullPipelineTest {
}
}

@Test
fun boxes() {
execute(".box {Hello} \n\tHello, **world**!") {
assertEquals(
"<div class=\"box callout\"><header><h4>Hello</h4></header>" +
"<div class=\"box-content\"><p>Hello, <strong>world</strong>!</p></div></div>",
it,
)
}

execute(".box {Hello} type:{tip}\n\tHello, world!") {
assertEquals(
"<div class=\"box tip\"><header><h4>Hello</h4></header>" +
"<div class=\"box-content\"><p>Hello, world!</p></div></div>",
it,
)
}

execute(".box {Hello, *world*} type:{warning}\n\tHello, world!") {
assertEquals(
"<div class=\"box warning\"><header><h4>Hello, <em>world</em></h4></header>" +
"<div class=\"box-content\"><p>Hello, world!</p></div></div>",
it,
)
}

execute(".box type:{error}\n\tHello, world!") {
assertEquals(
"<div class=\"box error\">" +
"<div class=\"box-content\"><p>Hello, world!</p></div></div>",
it,
)
}

// Localized title.
execute(
"""
.doclang {english}
.box type:{error}
Hello, world!
.box type:{tip}
Hello, world!
.box
Hello, world!
""".trimIndent(),
) {
assertEquals(
"<div class=\"box error\"><header><h4>Error</h4></header>" +
"<div class=\"box-content\"><p>Hello, world!</p></div></div>" +
"<div class=\"box tip\"><header><h4>Tip</h4></header>" +
"<div class=\"box-content\"><p>Hello, world!</p></div></div>" +
"<div class=\"box callout\">" +
"<div class=\"box-content\"><p>Hello, world!</p></div></div>",
it,
)
}

// Unsupported localization.
execute(
"""
.doclang {japanese}
.box type:{warning}
Hello, world!
""".trimIndent(),
) {
assertEquals(
"<div class=\"box warning\">" +
"<div class=\"box-content\"><p>Hello, world!</p></div></div>",
it,
)
}
}

@Test
fun fibonacci() {
// Iterative Fibonacci sequence calculation.
Expand Down

0 comments on commit 63213cd

Please sign in to comment.