Skip to content

Commit 8573289

Browse files
authored
Support font color customization (#55)
1 parent c98b319 commit 8573289

25 files changed

+1934
-196
lines changed
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
//
2+
// RichTextAlignment+Picker.swift
3+
// RichEditorSwiftUI
4+
//
5+
// Created by Divyesh Vekariya on 18/11/24.
6+
//
7+
8+
import SwiftUI
9+
10+
public extension RichTextAlignment {
11+
12+
/// This picker can be used to pick a text alignment.
13+
///
14+
/// This view returns a plain SwiftUI `Picker` view that
15+
/// can be styled and configured with a `PickerStyle`.
16+
struct Picker: View {
17+
18+
/// Create a rich text alignment picker.
19+
///
20+
/// - Parameters:
21+
/// - selection: The binding to update with the picker.
22+
/// - values: The pickable alignments, by default `.allCases`.
23+
public init(
24+
selection: Binding<RichTextAlignment>,
25+
values: [RichTextAlignment] = RichTextAlignment.allCases
26+
) {
27+
self._selection = selection
28+
self.values = values
29+
}
30+
31+
let values: [RichTextAlignment]
32+
33+
@Binding
34+
private var selection: RichTextAlignment
35+
36+
public var body: some View {
37+
SwiftUI.Picker(RTEL10n.textAlignment.text, selection: $selection) {
38+
ForEach(values) { value in
39+
value.label
40+
.labelStyle(.iconOnly)
41+
}
42+
}
43+
}
44+
}
45+
}

Sources/RichEditorSwiftUI/Alignment/RichTextAlignment.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import SwiftUI
1111
This enum defines supported rich text alignments, like left,
1212
right, center, and justified.
1313
*/
14-
public enum RichTextAlignment: String, CaseIterable, Codable, Equatable, Identifiable {
14+
public enum RichTextAlignment: String, CaseIterable, Codable, Equatable, Identifiable, RichTextLabelValue {
1515

1616
/**
1717
Initialize a rich text alignment with a native alignment.
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
//
2+
// RichTextColor+Picker.swift
3+
// RichEditorSwiftUI
4+
//
5+
// Created by Divyesh Vekariya on 18/11/24.
6+
//
7+
8+
import SwiftUI
9+
10+
public extension RichTextColor {
11+
12+
/**
13+
This picker can be used to select a rich text color.
14+
15+
This picker renders an icon next to the color picker as
16+
well as an optional list of quick colors.
17+
18+
The quick color list is empty by default. You can add a
19+
custom set of colors, for instance `.quickPickerColors`.
20+
*/
21+
struct Picker: View {
22+
23+
/**
24+
Create a rich text color picker that binds to a color.
25+
26+
- Parameters:
27+
- type: The type of color to pick.
28+
- icon: The icon to show, if any, by default the `type` icon.
29+
- value: The value to bind to.
30+
- quickColors: Colors to show in the trailing list, by default no colors.
31+
*/
32+
public init(
33+
type: RichTextColor,
34+
icon: Image? = nil,
35+
value: Binding<Color>,
36+
quickColors: [Color] = []
37+
) {
38+
self.type = type
39+
self.icon = icon ?? type.icon
40+
self._value = value
41+
self.quickColors = quickColors
42+
}
43+
44+
private let type: RichTextColor
45+
private let icon: Image?
46+
private let quickColors: [Color]
47+
48+
@Binding
49+
private var value: Color
50+
51+
private let spacing = 10.0
52+
53+
@Environment(\.colorScheme)
54+
private var colorScheme
55+
56+
public var body: some View {
57+
HStack(spacing: 0) {
58+
iconView
59+
picker
60+
if hasColors {
61+
HStack(spacing: spacing) {
62+
quickPickerDivider
63+
quickPickerButton(for: nil)
64+
quickPickerDivider
65+
}
66+
quickPicker
67+
}
68+
}
69+
.labelsHidden()
70+
}
71+
}
72+
}
73+
74+
private extension RichTextColor.Picker {
75+
76+
var hasColors: Bool {
77+
!quickColors.isEmpty
78+
}
79+
}
80+
81+
public extension Color {
82+
83+
/// Get a curated list of quick color picker colors.
84+
static var quickPickerColors: [Self] {
85+
[
86+
.black, .gray, .white,
87+
.red, .pink, .orange, .yellow,
88+
.indigo, .purple, .blue, .cyan, .teal, .mint,
89+
.green, .brown
90+
]
91+
}
92+
}
93+
94+
public extension Collection where Element == Color {
95+
96+
/// Get a curated list of quick color picker colors.
97+
static var quickPickerColors: [Element] {
98+
Element.quickPickerColors
99+
}
100+
}
101+
102+
private extension RichTextColor.Picker {
103+
104+
@ViewBuilder
105+
var iconView: some View {
106+
if let icon {
107+
icon.frame(minWidth: 30)
108+
}
109+
}
110+
111+
@ViewBuilder
112+
var picker: some View {
113+
#if iOS || macOS || os(visionOS)
114+
ColorPicker("", selection: $value)
115+
.fixedSize()
116+
.padding(.horizontal, spacing)
117+
#endif
118+
}
119+
120+
var quickPicker: some View {
121+
ScrollView(.horizontal, showsIndicators: false) {
122+
HStack(spacing: spacing) {
123+
ForEach(Array(quickColors.enumerated()), id: \.offset) {
124+
quickPickerButton(for: $0.element)
125+
}
126+
}
127+
.padding(.horizontal, spacing)
128+
.padding(.vertical, 2)
129+
}.frame(maxWidth: .infinity)
130+
}
131+
132+
func quickPickerButton(for color: Color?) -> some View {
133+
Button {
134+
value = type.adjust(color, for: colorScheme)
135+
} label: {
136+
if let color {
137+
color
138+
} else {
139+
Image.richTextColorReset
140+
}
141+
}
142+
.buttonStyle(ColorButtonStyle())
143+
}
144+
145+
var quickPickerDivider: some View {
146+
Divider()
147+
.padding(0)
148+
.frame(maxHeight: 30)
149+
}
150+
}
151+
152+
private struct ColorButtonStyle: ButtonStyle {
153+
154+
func makeBody(configuration: Configuration) -> some View {
155+
configuration.label
156+
.frame(width: 20, height: 20)
157+
.clipShape(Circle())
158+
.shadow(radius: 1, x: 0, y: 1)
159+
}
160+
}
161+
162+
#Preview {
163+
164+
struct Preview: View {
165+
@State
166+
private var foregroundColor = Color.black
167+
168+
@State
169+
private var backgroundColor = Color.white
170+
171+
var body: some View {
172+
VStack(alignment: .leading, spacing: 10) {
173+
Text("Preview")
174+
.foregroundStyle(foregroundColor)
175+
.padding()
176+
.background(backgroundColor)
177+
.frame(maxWidth: .infinity)
178+
.border(Color.black)
179+
.background(Color.red)
180+
.padding()
181+
182+
RichTextColor.Picker(
183+
type: .foreground,
184+
value: $foregroundColor,
185+
quickColors: [.white, .black, .red, .green, .blue]
186+
)
187+
.padding(.leading)
188+
189+
RichTextColor.Picker(
190+
type: .background,
191+
value: $backgroundColor,
192+
quickColors: [.white, .black, .red, .green, .blue]
193+
)
194+
.padding(.leading)
195+
}
196+
}
197+
}
198+
199+
return Preview()
200+
}

Sources/RichEditorSwiftUI/Data/Models/RichAttributes.swift

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
// Created by Divyesh Vekariya on 04/04/24.
66
//
77

8-
import Foundation
8+
import SwiftUI
99

1010
// MARK: - RichAttributes
1111
public struct RichAttributes: Codable {
@@ -224,6 +224,18 @@ extension RichAttributes {
224224
if let list = list {
225225
styles.insert(list.getTextSpanStyle())
226226
}
227+
if let size = size {
228+
styles.insert(.size(size))
229+
}
230+
if let font = font {
231+
styles.insert(.font(font))
232+
}
233+
if let color = color {
234+
styles.insert(.color(Color(hex: color)))
235+
}
236+
if let background = background {
237+
styles.insert(.background(Color(hex: background)))
238+
}
227239
return styles
228240
}
229241
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
//
2+
// RichTextFont+ListPicker.swift
3+
// RichEditorSwiftUI
4+
//
5+
// Created by Divyesh Vekariya on 18/11/24.
6+
//
7+
8+
import SwiftUI
9+
10+
public extension RichTextFont {
11+
12+
/**
13+
This view uses a `List` to list a set of fonts of which
14+
one can be selected.
15+
16+
Unlike ``RichTextFont/Picker`` this picker presents all
17+
pickers with proper previews on all platforms. You must
18+
therefore add it ina way that gives it space.
19+
20+
You can configure this picker by applying a config view
21+
modifier to your view hierarchy:
22+
23+
```swift
24+
VStack {
25+
RichTextFont.ListPicker(...)
26+
...
27+
}
28+
.richTextFontPickerConfig(...)
29+
```
30+
*/
31+
struct ListPicker: View {
32+
33+
/**
34+
Create a font list picker.
35+
36+
- Parameters:
37+
- selection: The selected font name.
38+
*/
39+
public init(
40+
selection: Binding<FontName>
41+
) {
42+
self._selection = selection
43+
}
44+
45+
public typealias Config = RichTextFont.PickerConfig
46+
public typealias Font = Config.Font
47+
public typealias FontName = Config.FontName
48+
49+
@Binding
50+
private var selection: FontName
51+
52+
@Environment(\.richTextFontPickerConfig)
53+
private var config
54+
55+
public var body: some View {
56+
let font = Binding(
57+
get: { Font(fontName: selection) },
58+
set: { selection = $0.fontName }
59+
)
60+
61+
RichEditorSwiftUI.ListPicker(
62+
items: config.fontsToList(for: selection),
63+
selection: font,
64+
dismissAfterPick: config.dismissAfterPick
65+
) { font, isSelected in
66+
RichTextFont.PickerItem(
67+
font: font,
68+
fontSize: config.fontSize,
69+
isSelected: isSelected
70+
)
71+
}
72+
}
73+
}
74+
}

0 commit comments

Comments
 (0)