Skip to content

Commit

Permalink
Add Markdown content to block configuration (#220)
Browse files Browse the repository at this point in the history
* Add methods to access child content and render plain text

* Add the Markdown content to the block configuration

* Fix building with Swift 5.8

* Deprecate block-style methods with a label closure

* Fix deprecation warnings

* Update documentation
  • Loading branch information
gonzalezreal authored Apr 11, 2023
1 parent b12b137 commit c51f1a1
Show file tree
Hide file tree
Showing 37 changed files with 640 additions and 222 deletions.
4 changes: 2 additions & 2 deletions Examples/Demo/Demo/HeadingsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ struct HeadingsView: View {
Section("Customization Example") {
Markdown("# One Big Header")
}
.markdownBlockStyle(\.heading1) { label in
label
.markdownBlockStyle(\.heading1) { configuration in
configuration.label
.markdownMargin(top: .em(1), bottom: .em(1))
.markdownTextStyle {
FontFamily(.custom("Trebuchet MS"))
Expand Down
4 changes: 2 additions & 2 deletions Examples/Demo/Demo/ImagesView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ struct ImagesView: View {
Section("Customization Example") {
Markdown(self.content)
}
.markdownBlockStyle(\.image) { label in
label
.markdownBlockStyle(\.image) { configuration in
configuration.label
.clipShape(RoundedRectangle(cornerRadius: 8))
.shadow(radius: 8, y: 8)
.markdownMargin(top: .em(1.6), bottom: .em(1.6))
Expand Down
4 changes: 2 additions & 2 deletions Examples/Demo/Demo/QuotesView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ struct QuotesView: View {
Section("Customization Example") {
Markdown(self.content)
}
.markdownBlockStyle(\.blockquote) { label in
label
.markdownBlockStyle(\.blockquote) { configuration in
configuration.label
.padding()
.markdownTextStyle {
FontCapsVariant(.lowercaseSmallCaps)
Expand Down
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -226,13 +226,14 @@ extension Theme {
ForegroundColor(.purple)
}
// More text styles...
.paragraph { label in
label
.paragraph { configuration in
configuration.label
.relativeLineSpacing(.em(0.25))
.markdownMargin(top: 0, bottom: 16)
}
.listItem { label in
label.markdownMargin(top: .em(0.25))
.listItem { configuration in
configuration.label
.markdownMargin(top: .em(0.25))
}
// More block styles...
}
Expand Down
20 changes: 20 additions & 0 deletions Sources/MarkdownUI/DSL/Blocks/MarkdownContent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,27 @@ public protocol MarkdownContentProtocol {
/// }
/// ```
public struct MarkdownContent: Equatable, MarkdownContentProtocol {
/// Returns a Markdown content value with the sum of the contents of all the container blocks
/// present in this content.
///
/// You can use this property to access the contents of a blockquote or a list. Returns `nil` if
/// there are no container blocks.
public var childContent: MarkdownContent? {
let children = self.blocks.map(\.children).flatMap { $0 }
return children.isEmpty ? nil : .init(blocks: children)
}

public var _markdownContent: MarkdownContent { self }
let blocks: [BlockNode]

init(blocks: [BlockNode] = []) {
self.blocks = blocks
}

init(block: BlockNode) {
self.init(blocks: [block])
}

init(_ components: [MarkdownContentProtocol]) {
self.init(blocks: components.map(\._markdownContent).flatMap(\.blocks))
}
Expand All @@ -88,4 +102,10 @@ public struct MarkdownContent: Equatable, MarkdownContentProtocol {
let result = self.blocks.renderMarkdown()
return result.hasSuffix("\n") ? String(result.dropLast()) : result
}

/// Renders this Markdown content value as plain text.
public func renderPlainText() -> String {
let result = self.blocks.renderPlainText()
return result.hasSuffix("\n") ? String(result.dropLast()) : result
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -183,13 +183,14 @@ extension Theme {
ForegroundColor(.purple)
}
// More text styles...
.paragraph { label in
label
.paragraph { configuration in
configuration.label
.relativeLineSpacing(.em(0.25))
.markdownMargin(top: 0, bottom: 16)
}
.listItem { label in
label.markdownMargin(top: .em(0.25))
.listItem { configuration in
configuration.label
.markdownMargin(top: .em(0.25))
}
// More block styles...
}
Expand Down
15 changes: 15 additions & 0 deletions Sources/MarkdownUI/Parser/BlockNode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,21 @@ enum BlockNode: Hashable {
}

extension BlockNode {
var children: [BlockNode] {
switch self {
case .blockquote(let children):
return children
case .bulletedList(_, let items):
return items.map(\.children).flatMap { $0 }
case .numberedList(_, _, let items):
return items.map(\.children).flatMap { $0 }
case .taskList(_, let items):
return items.map(\.children).flatMap { $0 }
default:
return []
}
}

var isParagraph: Bool {
guard case .paragraph = self else { return false }
return true
Expand Down
6 changes: 6 additions & 0 deletions Sources/MarkdownUI/Parser/MarkdownParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ extension Array where Element == BlockNode {
String(cString: cmark_render_commonmark(document, CMARK_OPT_DEFAULT, 0))
} ?? ""
}

func renderPlainText() -> String {
UnsafeNode.makeDocument(self) { document in
String(cString: cmark_render_plaintext(document, CMARK_OPT_DEFAULT, 0))
} ?? ""
}
}

extension BlockNode {
Expand Down
25 changes: 8 additions & 17 deletions Sources/MarkdownUI/Theme/BlockStyle/BlockConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,14 @@ public struct BlockConfiguration {
public let body: AnyView
}

/// The Markdown block content.
/// The Markdown block view.
public let label: Label
}

extension BlockStyle where Configuration == BlockConfiguration {
/// Creates a block style that customizes a block by applying the given body.
/// - Parameter body: A view builder that returns the customized block.
public init<Body: View>(
@ViewBuilder body: @escaping (_ label: BlockConfiguration.Label) -> Body
) {
self.init { configuration in
body(configuration.label)
}
}

/// Creates a block style that returns the block content without applying any customization.
public init() {
self.init { $0 }
}
/// The content of the Markdown block.
///
/// This property provides access to different representations of the block content.
/// For example, you can use ``MarkdownContent/renderMarkdown()``
/// to get the Markdown formatted text or ``MarkdownContent/renderPlainText()``
/// to get the plain text of the block content.
public let content: MarkdownContent
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ public struct TableCellConfiguration {
/// The table cell column index.
public let column: Int

/// The table cell content.
/// The table cell view.
public let label: Label

/// The table cell content.
public let content: MarkdownContent
}
37 changes: 19 additions & 18 deletions Sources/MarkdownUI/Theme/Theme+Basic.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,61 +19,61 @@ extension Theme {
FontFamilyVariant(.monospaced)
FontSize(.em(0.94))
}
.heading1 { label in
label
.heading1 { configuration in
configuration.label
.markdownMargin(top: .rem(1.5), bottom: .rem(1))
.markdownTextStyle {
FontWeight(.semibold)
FontSize(.em(2))
}
}
.heading2 { label in
label
.heading2 { configuration in
configuration.label
.markdownMargin(top: .rem(1.5), bottom: .rem(1))
.markdownTextStyle {
FontWeight(.semibold)
FontSize(.em(1.5))
}
}
.heading3 { label in
label
.heading3 { configuration in
configuration.label
.markdownMargin(top: .rem(1.5), bottom: .rem(1))
.markdownTextStyle {
FontWeight(.semibold)
FontSize(.em(1.17))
}
}
.heading4 { label in
label
.heading4 { configuration in
configuration.label
.markdownMargin(top: .rem(1.5), bottom: .rem(1))
.markdownTextStyle {
FontWeight(.semibold)
FontSize(.em(1))
}
}
.heading5 { label in
label
.heading5 { configuration in
configuration.label
.markdownMargin(top: .rem(1.5), bottom: .rem(1))
.markdownTextStyle {
FontWeight(.semibold)
FontSize(.em(0.83))
}
}
.heading6 { label in
label
.heading6 { configuration in
configuration.label
.markdownMargin(top: .rem(1.5), bottom: .rem(1))
.markdownTextStyle {
FontWeight(.semibold)
FontSize(.em(0.67))
}
}
.paragraph { label in
label
.paragraph { configuration in
configuration.label
.relativeLineSpacing(.em(0.15))
.markdownMargin(top: .zero, bottom: .em(1))
}
.blockquote { label in
label
.blockquote { configuration in
configuration.label
.markdownTextStyle {
FontStyle(.italic)
}
Expand All @@ -92,8 +92,9 @@ extension Theme {
}
.markdownMargin(top: .zero, bottom: .em(1))
}
.table { label in
label.markdownMargin(top: .zero, bottom: .em(1))
.table { configuration in
configuration.label
.markdownMargin(top: .zero, bottom: .em(1))
}
.tableCell { configuration in
configuration.label
Expand Down
45 changes: 23 additions & 22 deletions Sources/MarkdownUI/Theme/Theme+DocC.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,65 +21,65 @@ extension Theme {
.link {
ForegroundColor(.link)
}
.heading1 { label in
label
.heading1 { configuration in
configuration.label
.markdownMargin(top: .em(0.8), bottom: .zero)
.markdownTextStyle {
FontWeight(.semibold)
FontSize(.em(2))
}
}
.heading2 { label in
label
.heading2 { configuration in
configuration.label
.relativeLineSpacing(.em(0.0625))
.markdownMargin(top: .em(1.6), bottom: .zero)
.markdownTextStyle {
FontWeight(.semibold)
FontSize(.em(1.88235))
}
}
.heading3 { label in
label
.heading3 { configuration in
configuration.label
.relativeLineSpacing(.em(0.07143))
.markdownMargin(top: .em(1.6), bottom: .zero)
.markdownTextStyle {
FontWeight(.semibold)
FontSize(.em(1.64706))
}
}
.heading4 { label in
label
.heading4 { configuration in
configuration.label
.relativeLineSpacing(.em(0.083335))
.markdownMargin(top: .em(1.6), bottom: .zero)
.markdownTextStyle {
FontWeight(.semibold)
FontSize(.em(1.41176))
}
}
.heading5 { label in
label
.heading5 { configuration in
configuration.label
.relativeLineSpacing(.em(0.09091))
.markdownMargin(top: .em(1.6), bottom: .zero)
.markdownTextStyle {
FontWeight(.semibold)
FontSize(.em(1.29412))
}
}
.heading6 { label in
label
.heading6 { configuration in
configuration.label
.relativeLineSpacing(.em(0.235295))
.markdownMargin(top: .em(1.6), bottom: .zero)
.markdownTextStyle {
FontWeight(.semibold)
}
}
.paragraph { label in
label
.paragraph { configuration in
configuration.label
.relativeLineSpacing(.em(0.235295))
.markdownMargin(top: .em(0.8), bottom: .zero)
}
.blockquote { label in
label
.blockquote { configuration in
configuration.label
.relativePadding(length: .rem(0.94118))
.frame(maxWidth: .infinity, alignment: .leading)
.background {
Expand Down Expand Up @@ -107,21 +107,22 @@ extension Theme {
.clipShape(.container)
.markdownMargin(top: .em(0.8), bottom: .zero)
}
.image { label in
label
.image { configuration in
configuration.label
.frame(maxWidth: .infinity)
.markdownMargin(top: .em(1.6), bottom: .em(1.6))
}
.listItem { label in
label.markdownMargin(top: .em(0.8))
.listItem { configuration in
configuration.label
.markdownMargin(top: .em(0.8))
}
.taskListMarker { _ in
// DocC renders task lists as bullet lists
ListBullet.disc
.relativeFrame(minWidth: .em(1.5), alignment: .trailing)
}
.table { label in
label
.table { configuration in
configuration.label
.markdownTableBorderStyle(.init(.horizontalBorders, color: .grid))
.markdownMargin(top: .em(1.6), bottom: .zero)
}
Expand Down
Loading

0 comments on commit c51f1a1

Please sign in to comment.