-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
ebb021d
commit 8c892f2
Showing
12 changed files
with
537 additions
and
0 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
// swift-tools-version:5.5 | ||
// The swift-tools-version declares the minimum version of Swift required to build this package. | ||
|
||
import PackageDescription | ||
|
||
let package = Package( | ||
name: "StyledMarkdown", | ||
platforms: [ | ||
.iOS(.v15), .macOS(.v12), .tvOS(.v15), .watchOS(.v8) | ||
], | ||
products: [ | ||
.library( | ||
name: "StyledMarkdown", | ||
targets: ["StyledMarkdown"] | ||
), | ||
], | ||
targets: [ | ||
.target( | ||
name: "StyledMarkdown", | ||
path: "Sources" | ||
), | ||
.testTarget( | ||
name: "StyledMarkdownTests", | ||
dependencies: ["StyledMarkdown"] | ||
), | ||
] | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
<p align="center"> | ||
<img src="Docs/logo.png" width="300" max-width="80%" alt="glide"/> | ||
</p> | ||
|
||
<p align="center"> | ||
iOS 15.0 / macOS 12.0 / tvOS 15.0 / watchOS 8.0 | ||
</p> | ||
|
||
StyledMarkdown is a mini library that lets you define custom styles in code and use them in your localized markdown strings. | ||
|
||
``` | ||
let normalStyle = Style { style in | ||
style.font = .subheadline | ||
style.foregroundColor = .red | ||
} | ||
let boldStyle = Style { style in | ||
style.font = Font.italic(.system(size: 20))() | ||
style.foregroundColor = .blue | ||
} | ||
let myStyleGroup = StyleGroup( | ||
base: normalStyle, | ||
[ | ||
"bold": boldStyle | ||
] | ||
) | ||
Text( | ||
"Hey ^[buddy](style: 'bold')", | ||
styleGroup: myStyleGroup | ||
) | ||
``` | ||
|
||
A custom `Text` initializer with custom `AttributedStringKey`s are used to achieve this outcome. | ||
|
||
***The idea of StyleGroup and named Styles comes directly from [`SwiftRichString` library by `Daniele Margutti` on GitHub](https://github.com/malcommac/SwiftRichString). Some of the code from there is also used in this package.*** | ||
|
||
### Limitation | ||
|
||
Currently there is a bug with `.init(markdown:including:)` initialiser of `AttributedString`. This initializer ignores custom `AttributedStringKey`s used in this library while creating the final string. Due to this, right now it is not possible to style any markdown string, but only localized keys. Radar reported, a new initialiser will be added once that is fixed. | ||
|
||
## Examples | ||
<p align="center"> | ||
<img src="Docs/examples.png" width="400" max-width="80%" alt="glide devices"/> | ||
</p> | ||
|
||
## Supported modifiers | ||
|
||
#### font(*SwiftUI.Font*) | ||
#### foregroundColor(*Color*) | ||
#### strikethrough(*Color*) | ||
#### strikethroughStyle(*NSUnderlineStyle*) | ||
#### underline(*Color*) | ||
#### underlineStyle(*NSUnderlineStyle*) | ||
#### kerning(*CGFloat*) | ||
#### tracking(*CGFloat*) | ||
#### baselineOffset(*CGFloat*) | ||
|
||
### 🔗 Links | ||
|
||
You can add links inside your strings using the custom `link` `AttributedStringKey`: | ||
`^[styled link](link: {url: 'http://www.example.com', style: 'linkStyle'})` | ||
|
||
### 🎆 Images (not supported) | ||
|
||
It is currently not supported to include `Image` elements within `AttributedString`. |
42 changes: 42 additions & 0 deletions
42
Sources/StyledMarkdown/Style/AttributeScopes.StyledMarkdownAttributes.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
// | ||
// AttributeScopes.StyledMarkdownAttributes.swift | ||
// StyledMarkdown | ||
// | ||
// Created by cocoatoucher on 2022-01-04. | ||
// | ||
|
||
import Foundation | ||
|
||
extension AttributeScopes { | ||
struct StyledMarkdownAttributes: AttributeScope { | ||
let styleName: StyleNameAttribute | ||
let linkWithStyleName: LinkWithStyleNameAttribute | ||
let swiftUI: SwiftUIAttributes | ||
} | ||
|
||
var styledMarkdown: StyledMarkdownAttributes.Type { StyledMarkdownAttributes.self } | ||
} | ||
|
||
extension AttributeDynamicLookup { | ||
subscript<T: AttributedStringKey>( | ||
dynamicMember keyPath: KeyPath<AttributeScopes.StyledMarkdownAttributes, | ||
T> | ||
) -> T { | ||
self[T.self] | ||
} | ||
} | ||
|
||
enum StyleNameAttribute: CodableAttributedStringKey, MarkdownDecodableAttributedStringKey { | ||
typealias Value = String | ||
|
||
static var name = "style" | ||
} | ||
|
||
enum LinkWithStyleNameAttribute: CodableAttributedStringKey, MarkdownDecodableAttributedStringKey { | ||
struct Value: Codable, Hashable { | ||
let url: URL | ||
let style: String | ||
} | ||
|
||
static var name = "link" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
// | ||
// Style.swift | ||
// StyledMarkdown | ||
// | ||
// Created by cocoatoucher on 2022-01-04. | ||
// | ||
|
||
import SwiftUI | ||
|
||
public class Style: StyleProtocol { | ||
|
||
public var font: SwiftUI.Font? | ||
|
||
public var foregroundColor: Color? | ||
|
||
public var strikethroughColor: Color? | ||
|
||
public var strikethroughStyle: NSUnderlineStyle? | ||
|
||
public var underlineColor: Color? | ||
|
||
public var underlineStyle: NSUnderlineStyle? | ||
|
||
public var kerning: CGFloat? | ||
|
||
public var tracking: CGFloat? | ||
|
||
public var baselineOffset: CGFloat? | ||
|
||
public init(_ handler: ((Style) -> Void)? = nil) { | ||
handler?(self) | ||
} | ||
|
||
public var modifiers: [StyleModifier] { | ||
var result: [StyleModifier] = [] | ||
if let font = font { | ||
result.append(.font(font)) | ||
} | ||
if let foregroundColor = foregroundColor { | ||
result.append(.foregroundColor(foregroundColor)) | ||
} | ||
if let strikethroughColor = strikethroughColor { | ||
result.append(.strikethroughColor(strikethroughColor)) | ||
} | ||
if let strikethroughStyle = strikethroughStyle { | ||
result.append(.strikethroughStyle(strikethroughStyle)) | ||
} | ||
if let underlineColor = underlineColor { | ||
result.append(.underline(underlineColor)) | ||
} | ||
if let underlineStyle = underlineStyle { | ||
result.append(.underlineStyle(underlineStyle)) | ||
} | ||
if let kerning = kerning { | ||
result.append(.kerning(kerning)) | ||
} | ||
if let tracking = tracking { | ||
result.append(.tracking(tracking)) | ||
} | ||
if let baselineOffset = baselineOffset { | ||
result.append(.baselineOffset(baselineOffset)) | ||
} | ||
return result | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
// | ||
// StyleGroup.swift | ||
// StyledMarkdown | ||
// | ||
// Created by cocoatoucher on 2022-01-04. | ||
// | ||
|
||
import Foundation | ||
|
||
public class StyleGroup: StyleProtocol { | ||
|
||
public var modifiers: [StyleModifier] = [] | ||
|
||
public private(set) var styles: [String: StyleProtocol] | ||
|
||
public var baseStyle: StyleProtocol? | ||
|
||
public init( | ||
base: StyleProtocol? = nil, | ||
_ styles: [String: StyleProtocol] = [:] | ||
) { | ||
self.styles = styles | ||
self.baseStyle = base | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
// | ||
// StyleModifier.swift | ||
// StyledMarkdown | ||
// | ||
// Created by cocoatoucher on 2022-01-04. | ||
// | ||
|
||
import SwiftUI | ||
|
||
public enum StyleModifier { | ||
case font(SwiftUI.Font) | ||
case foregroundColor(Color) | ||
case strikethroughColor(Color) | ||
case strikethroughStyle(NSUnderlineStyle) | ||
case underline(Color) | ||
case underlineStyle(NSUnderlineStyle) | ||
case kerning(CGFloat) | ||
case tracking(CGFloat) | ||
case baselineOffset(CGFloat) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
// | ||
// StyleProtocol.swift | ||
// StyledMarkdown | ||
// | ||
// Created by cocoatoucher on 2022-01-04. | ||
// | ||
|
||
import Foundation | ||
|
||
public protocol StyleProtocol { | ||
var modifiers: [StyleModifier] { get } | ||
|
||
func add(to source: inout AttributedSubstring) | ||
} | ||
|
||
public extension StyleProtocol { | ||
|
||
func add(to source: inout AttributedSubstring) { | ||
for modifier in self.modifiers { | ||
switch modifier { | ||
case .font(let font): | ||
source.font = font | ||
case .foregroundColor(let color): | ||
source.foregroundColor = color | ||
case .strikethroughColor(let color): | ||
source.strikethroughColor = .init(color) | ||
case .strikethroughStyle(let style): | ||
source.strikethroughStyle = style | ||
case .underline(let color): | ||
source.underlineColor = .init(color) | ||
case .underlineStyle(let style): | ||
source.underlineStyle = style | ||
case .kerning(let kerning): | ||
source.kern = kerning | ||
case .tracking(let tracking): | ||
source.tracking = tracking | ||
case .baselineOffset(let baselineOffset): | ||
source.baselineOffset = baselineOffset | ||
} | ||
} | ||
} | ||
|
||
} |
Oops, something went wrong.