Skip to content

Commit 6a92498

Browse files
authored
Added header style selection too (#60)
1 parent 24576b6 commit 6a92498

File tree

12 files changed

+182
-11
lines changed

12 files changed

+182
-11
lines changed

Sources/RichEditorSwiftUI/Data/Models/HeaderType.swift

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@
66
//
77

88
import Foundation
9+
import SwiftUI
910

10-
public enum HeaderType: Int, CaseIterable, Codable {
11+
public enum HeaderType: Int, CaseIterable, Codable, Equatable, Identifiable, RichTextLabelValue {
1112
case `default` = 0
1213
case h1 = 1
1314
case h2 = 2
@@ -16,22 +17,22 @@ public enum HeaderType: Int, CaseIterable, Codable {
1617
case h5 = 5
1718
case h6 = 6
1819

19-
var title: String {
20+
var titleLabel: String {
2021
switch self {
2122
case .default:
22-
return "Normal Text"
23+
return "Body"
2324
case .h1:
24-
return "Header 1"
25+
return "H1"
2526
case .h2:
26-
return "Header 2"
27+
return "H2"
2728
case .h3:
28-
return "Header 3"
29+
return "H3"
2930
case .h4:
30-
return "Header 4"
31+
return "H4"
3132
case .h5:
32-
return "Header 5"
33+
return "H5"
3334
case .h6:
34-
return "Header 6"
35+
return "H6"
3536
}
3637
}
3738

@@ -47,3 +48,44 @@ public enum HeaderType: Int, CaseIterable, Codable {
4748
}
4849
}
4950
}
51+
52+
53+
public extension Collection where Element == HeaderType {
54+
55+
static var all: [Element] { HeaderType.allCases }
56+
}
57+
58+
public extension HeaderType {
59+
60+
/// The unique header ID.
61+
var id: String { "\(rawValue)" }
62+
63+
/// The standard icon to use for the header.
64+
var icon: Image {
65+
switch self {
66+
case .default: .richTextHeaderDefault
67+
case .h1: .richTextHeader1
68+
case .h2: .richTextHeader2
69+
case .h3: .richTextHeader3
70+
case .h4: .richTextHeader4
71+
case .h5: .richTextHeader5
72+
case .h6: .richTextHeader6
73+
}
74+
}
75+
76+
/// standard title to use for the headers.
77+
var title: String { titleKey.text }
78+
79+
/// The standard title key to use for the header.
80+
var titleKey: RTEL10n {
81+
switch self {
82+
case .default: .headerDefault
83+
case .h1: .header1
84+
case .h2: .header2
85+
case .h3: .header3
86+
case .h4: .header4
87+
case .h5: .header5
88+
case .h6: .header6
89+
}
90+
}
91+
}

Sources/RichEditorSwiftUI/Format/RichTextFormat+Toolbar.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,10 @@ private extension RichTextFormat.Toolbar {
114114
var controlsContent: some View {
115115
HStack {
116116
#if macOS
117+
headerPicker(value: $context.headerType)
118+
.onChangeBackPort(of: context.headerType) { newValue in
119+
context.updateStyle(style: newValue.getTextSpanStyle())
120+
}
117121
fontPicker(value: $context.fontName)
118122
.onChangeBackPort(of: context.fontName) { newValue in
119123
context.updateStyle(style: .font(newValue))
@@ -129,6 +133,12 @@ private extension RichTextFormat.Toolbar {
129133
}
130134
}
131135
HStack {
136+
#if !macOS
137+
headerPicker(value: $context.headerType)
138+
.onChangeBackPort(of: context.headerType) { newValue in
139+
context.updateStyle(style: newValue.getTextSpanStyle())
140+
}
141+
#endif
132142
alignmentPicker(value: $context.textAlignment)
133143
// superscriptButtons(for: context, greedy: false)
134144
// indentButtons(for: context, greedy: false)

Sources/RichEditorSwiftUI/Format/RichTextFormat+ToolbarConfig.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ public extension RichTextFormat {
1414
struct ToolbarConfig {
1515

1616
public init(
17+
headers: [HeaderType] = .all,
1718
alignments: [RichTextAlignment] = .all,
1819
colorPickers: [RichTextColor] = [.foreground],
1920
colorPickersDisclosed: [RichTextColor] = [.background],
@@ -24,6 +25,7 @@ public extension RichTextFormat {
2425
styles: [RichTextStyle] = [.bold, .italic, .underline, .strikethrough],
2526
superscriptButtons: Bool = true
2627
) {
28+
self.headers = headers
2729
self.alignments = alignments
2830
self.colorPickers = colorPickers
2931
self.colorPickersDisclosed = colorPickersDisclosed
@@ -39,6 +41,7 @@ public extension RichTextFormat {
3941
#endif
4042
}
4143

44+
public var headers: [HeaderType]
4245
public var alignments: [RichTextAlignment]
4346
public var colorPickers: [RichTextColor]
4447
public var colorPickersDisclosed: [RichTextColor]

Sources/RichEditorSwiftUI/Format/RichTextFormatToolbarBase.swift

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,19 @@ extension RichTextFormatToolbarBase {
4040
}
4141
}
4242

43+
@ViewBuilder
44+
func headerPicker(
45+
value: Binding<HeaderType>
46+
) -> some View {
47+
if !config.headers.isEmpty {
48+
RichTextHeader.Picker(
49+
selection: value,
50+
values: config.headers
51+
)
52+
.pickerStyle(.segmented)
53+
}
54+
}
55+
4356
@ViewBuilder
4457
func colorPickers(
4558
for context: RichEditorState
@@ -88,7 +101,6 @@ extension RichTextFormatToolbarBase {
88101
.labelStyle(.iconOnly)
89102
.frame(minWidth: 30)
90103
}
91-
.padding(.horizontal)
92104
}
93105
}
94106

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
//
2+
// RichTextHeader+Picker.swift
3+
// RichEditorSwiftUI
4+
//
5+
// Created by Divyesh Vekariya on 25/11/24.
6+
//
7+
8+
import SwiftUI
9+
10+
public extension RichTextHeader {
11+
12+
/**
13+
This picker can be used to pick a Header type.
14+
15+
The view returns a plain SwiftUI `Picker` view that can
16+
be styled and configured with plain SwiftUI.
17+
18+
You can configure this picker by applying a config view
19+
modifier to your view hierarchy:
20+
21+
```swift
22+
VStack {
23+
RichTextHeader.HeaderTypePicker(...)
24+
...
25+
}
26+
```
27+
*/
28+
struct Picker: View {
29+
30+
/**
31+
Create a font size picker.
32+
33+
- Parameters:
34+
- selection: The selected font size.
35+
*/
36+
public init(
37+
selection: Binding<HeaderType>,
38+
values: [HeaderType]
39+
) {
40+
self._selection = selection
41+
self.values = values
42+
}
43+
44+
@Binding
45+
private var selection: HeaderType
46+
47+
private let values: [HeaderType]
48+
49+
public var body: some View {
50+
SwiftUI.Picker("", selection: $selection) {
51+
ForEach(values,
52+
id: \.self) {
53+
text(for: $0)
54+
.tag($0)
55+
}
56+
}
57+
.pickerStyle(.automatic)
58+
}
59+
}
60+
}
61+
62+
private extension RichTextHeader.Picker {
63+
64+
func text(
65+
for headerType: HeaderType
66+
) -> some View {
67+
Text(headerType.titleLabel)
68+
.fixedSize(horizontal: true, vertical: false)
69+
}
70+
}
71+
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
//
2+
// RichTextHeader.swift
3+
// RichEditorSwiftUI
4+
//
5+
// Created by Divyesh Vekariya on 25/11/24.
6+
//
7+
8+
import SwiftUI
9+
10+
/// This is a namespace for header-related types and views.
11+
public struct RichTextHeader {}

Sources/RichEditorSwiftUI/Images/Image+RichText.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,14 @@ public extension Image {
3131
static let richTextColorUnderline = symbol("underline")
3232
static let richTextColorUndefined = symbol("questionmark.app")
3333

34+
static let richTextHeaderDefault = symbol("textformat")
35+
static let richTextHeader1 = symbol("textformat")
36+
static let richTextHeader2 = symbol("textformat")
37+
static let richTextHeader3 = symbol("textformat")
38+
static let richTextHeader4 = symbol("textformat")
39+
static let richTextHeader5 = symbol("textformat")
40+
static let richTextHeader6 = symbol("textformat")
41+
3442
static let richTextDocument = symbol("doc.text")
3543
static let richTextDocuments = symbol("doc.on.doc")
3644

Sources/RichEditorSwiftUI/Keyboard/RichTextKeyboardToolbar.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ public struct RichTextKeyboardToolbar<LeadingButtons: View, TrailingButtons: Vie
114114
HStack(spacing: style.itemSpacing) {
115115
leadingViews
116116
Spacer()
117+
.frame(minWidth: 0, maxWidth: .infinity)
117118
trailingViews
118119
}
119120
.padding(10)

Sources/RichEditorSwiftUI/Localization/RTEL10n.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,14 @@ public enum RTEL10n: String, CaseIterable, Identifiable {
9090
textAlignmentCentered,
9191
textAlignmentJustified,
9292

93+
headerDefault,
94+
header1,
95+
header2,
96+
header3,
97+
header4,
98+
header5,
99+
header6,
100+
93101
ignoreIt
94102
}
95103

Sources/RichEditorSwiftUI/UI/Context/RichEditorState.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ public class RichEditorState: ObservableObject {
5353
@Published
5454
public var isEditingText = false
5555

56+
@Published
57+
public var headerType: HeaderType = .default
58+
5659
/// The current text alignment, if any.
5760
@Published
5861
public var textAlignment: RichTextAlignment = .left

0 commit comments

Comments
 (0)