Skip to content

Commit 0ab4ec6

Browse files
committed
Suport image drop with json format
1 parent 4767e13 commit 0ab4ec6

25 files changed

+3878
-3320
lines changed

RichEditorDemo/RichEditorDemo/ContentView.swift

Lines changed: 190 additions & 165 deletions
Original file line numberDiff line numberDiff line change
@@ -9,183 +9,208 @@ import RichEditorSwiftUI
99
import SwiftUI
1010

1111
struct ContentView: View {
12-
@Environment(\.colorScheme) var colorScheme
13-
14-
@ObservedObject var state: RichEditorState
15-
@State private var isInspectorPresented = false
16-
@State private var fileName: String = ""
17-
@State private var exportFormat: RichTextDataFormat? = nil
18-
@State private var otherExportFormat: RichTextExportOption? = nil
19-
@State private var exportService: StandardRichTextExportService = .init()
20-
21-
init(state: RichEditorState? = nil) {
22-
if let state {
23-
self.state = state
24-
} else {
25-
if let richText = readJSONFromFile(
26-
fileName: "Sample_json",
27-
type: RichText.self)
28-
{
29-
self.state = .init(richText: richText)
30-
} else {
31-
self.state = .init(input: "Hello World!")
32-
}
33-
}
34-
}
12+
@Environment(\.colorScheme) var colorScheme
13+
14+
@ObservedObject var state: RichEditorState
15+
@State private var isInspectorPresented = false
16+
@State private var fileName: String = ""
17+
@State private var exportFormat: RichTextDataFormat? = nil
18+
@State private var otherExportFormat: RichTextExportOption? = nil
19+
@State private var exportService: StandardRichTextExportService = .init()
20+
21+
init(state: RichEditorState? = nil) {
22+
if let state {
23+
self.state = state
24+
} else {
25+
if let richText = readJSONFromFile(
26+
fileName: "Sample_json",
27+
type: RichText.self)
28+
{
29+
self.state = .init(richText: richText, handleImageDropAction: handleImages(_:_:))
30+
} else {
31+
self.state = .init(
32+
input: "Hello World!",
33+
handleImageDropAction: handleImages(_:_:))
34+
}
3535

36-
var body: some View {
37-
NavigationStack {
38-
VStack {
39-
#if os(macOS)
40-
RichTextFormat.Toolbar(context: state)
41-
#endif
42-
43-
RichTextEditor(
44-
context: _state,
45-
viewConfiguration: { _ in
46-
47-
}
48-
)
49-
.background(
50-
colorScheme == .dark ? .gray.opacity(0.3) : Color.white
51-
)
52-
.cornerRadius(10)
53-
54-
#if os(iOS)
55-
RichTextKeyboardToolbar(
56-
context: state,
57-
leadingButtons: { $0 },
58-
trailingButtons: { $0 },
59-
formatSheet: { $0 }
60-
)
61-
#endif
62-
}
63-
#if os(iOS) || os(macOS)
64-
.inspector(isPresented: $isInspectorPresented) {
65-
RichTextFormat.Sidebar(context: state)
66-
#if os(macOS)
67-
.inspectorColumnWidth(
68-
min: 200, ideal: 200, max: 315)
69-
#endif
70-
}
71-
#endif
72-
.padding(10)
73-
#if os(iOS) || os(macOS)
74-
.toolbar {
75-
ToolbarItemGroup(placement: .automatic) {
76-
toolBarGroup
77-
}
78-
}
79-
#endif
80-
.background(colorScheme == .dark ? .black : .gray.opacity(0.07))
81-
.navigationTitle("Rich Editor")
82-
.alert("Enter file name", isPresented: getBindingAlert()) {
83-
TextField("Enter file name", text: $fileName)
84-
Button("OK", action: submit)
85-
} message: {
86-
Text("Please enter file name")
87-
}
88-
.focusedValue(\.richEditorState, state)
89-
.toolbarRole(.automatic)
90-
#if os(iOS) || os(macOS) || os(visionOS)
91-
.richTextFormatSheetConfig(.init(colorPickers: colorPickers))
92-
.richTextFormatSidebarConfig(
93-
.init(
94-
colorPickers: colorPickers,
95-
fontPicker: isMac
96-
)
97-
)
98-
.richTextFormatToolbarConfig(.init(colorPickers: []))
99-
#endif
100-
}
10136
}
37+
}
10238

103-
#if os(iOS) || os(macOS)
104-
var toolBarGroup: some View {
105-
return Group {
106-
RichTextExportMenu.init(
107-
formatAction: { format in
108-
exportFormat = format
109-
},
110-
otherOptionAction: { format in
111-
otherExportFormat = format
112-
}
113-
)
114-
#if !os(macOS)
115-
.frame(width: 25, alignment: .center)
116-
#endif
117-
Button(
118-
action: {
119-
print("Exported JSON == \(state.outputAsString())")
120-
},
121-
label: {
122-
Image(systemName: "printer.inverse")
123-
}
124-
)
125-
#if !os(macOS)
126-
.frame(width: 25, alignment: .center)
127-
#endif
128-
Toggle(isOn: $isInspectorPresented) {
129-
Image.richTextFormatBrush
130-
.resizable()
131-
.aspectRatio(1, contentMode: .fit)
132-
}
133-
#if !os(macOS)
134-
.frame(width: 25, alignment: .center)
135-
#endif
136-
}
137-
}
138-
#endif
139-
140-
func getBindingAlert() -> Binding<Bool> {
141-
.init(
142-
get: { exportFormat != nil || otherExportFormat != nil },
143-
set: { newValue in
144-
exportFormat = nil
145-
otherExportFormat = nil
146-
})
147-
}
148-
149-
func submit() {
150-
guard !fileName.isEmpty else { return }
151-
var path: URL?
39+
var body: some View {
40+
NavigationStack {
41+
VStack {
42+
#if os(macOS)
43+
RichTextFormat.Toolbar(context: state)
44+
#endif
15245

153-
if let exportFormat {
154-
path = try? exportService.generateExportFile(
155-
withName: fileName, content: state.attributedString,
156-
format: exportFormat)
46+
RichTextEditor(
47+
context: _state,
48+
viewConfiguration: { _ in
49+
50+
}
51+
)
52+
.background(
53+
colorScheme == .dark ? .gray.opacity(0.3) : Color.white
54+
)
55+
.cornerRadius(10)
56+
57+
#if os(iOS)
58+
RichTextKeyboardToolbar(
59+
context: state,
60+
leadingButtons: { $0 },
61+
trailingButtons: { $0 },
62+
formatSheet: { $0 }
63+
)
64+
#endif
65+
}
66+
#if os(iOS) || os(macOS)
67+
.inspector(isPresented: $isInspectorPresented) {
68+
RichTextFormat.Sidebar(context: state)
69+
#if os(macOS)
70+
.inspectorColumnWidth(
71+
min: 200, ideal: 200, max: 315)
72+
#endif
15773
}
158-
if let otherExportFormat {
159-
switch otherExportFormat {
160-
case .pdf:
161-
path = try? exportService.generatePdfExportFile(
162-
withName: fileName, content: state.attributedString)
163-
case .json:
164-
path = try? exportService.generateJsonExportFile(
165-
withName: fileName, content: state.richText)
166-
}
74+
#endif
75+
.padding(10)
76+
#if os(iOS) || os(macOS)
77+
.toolbar {
78+
ToolbarItemGroup(placement: .automatic) {
79+
toolBarGroup
80+
}
16781
}
168-
if let path {
169-
print("Exported at path == \(path)")
82+
#endif
83+
.background(colorScheme == .dark ? .black : .gray.opacity(0.07))
84+
.navigationTitle("Rich Editor")
85+
.alert("Enter file name", isPresented: getBindingAlert()) {
86+
TextField("Enter file name", text: $fileName)
87+
Button("OK", action: submit)
88+
} message: {
89+
Text("Please enter file name")
90+
}
91+
.focusedValue(\.richEditorState, state)
92+
.toolbarRole(.automatic)
93+
#if os(iOS) || os(macOS) || os(visionOS)
94+
.richTextFormatSheetConfig(.init(colorPickers: colorPickers))
95+
.richTextFormatSidebarConfig(
96+
.init(
97+
colorPickers: colorPickers,
98+
fontPicker: isMac
99+
)
100+
)
101+
.richTextFormatToolbarConfig(.init(colorPickers: []))
102+
#endif
103+
}
104+
}
105+
106+
#if os(iOS) || os(macOS)
107+
var toolBarGroup: some View {
108+
return Group {
109+
RichTextExportMenu.init(
110+
formatAction: { format in
111+
exportFormat = format
112+
},
113+
otherOptionAction: { format in
114+
otherExportFormat = format
115+
}
116+
)
117+
#if !os(macOS)
118+
.frame(width: 25, alignment: .center)
119+
#endif
120+
Button(
121+
action: {
122+
print("Exported JSON == \(state.outputAsString())")
123+
},
124+
label: {
125+
Image(systemName: "printer.inverse")
126+
}
127+
)
128+
#if !os(macOS)
129+
.frame(width: 25, alignment: .center)
130+
#endif
131+
Toggle(isOn: $isInspectorPresented) {
132+
Image.richTextFormatBrush
133+
.resizable()
134+
.aspectRatio(1, contentMode: .fit)
170135
}
136+
#if !os(macOS)
137+
.frame(width: 25, alignment: .center)
138+
#endif
139+
}
171140
}
141+
#endif
142+
143+
func getBindingAlert() -> Binding<Bool> {
144+
.init(
145+
get: { exportFormat != nil || otherExportFormat != nil },
146+
set: { newValue in
147+
exportFormat = nil
148+
otherExportFormat = nil
149+
})
150+
}
151+
152+
func submit() {
153+
guard !fileName.isEmpty else { return }
154+
var path: URL?
155+
156+
if let exportFormat {
157+
path = try? exportService.generateExportFile(
158+
withName: fileName, content: state.attributedString,
159+
format: exportFormat)
160+
}
161+
if let otherExportFormat {
162+
switch otherExportFormat {
163+
case .pdf:
164+
path = try? exportService.generatePdfExportFile(
165+
withName: fileName, content: state.attributedString)
166+
case .json:
167+
path = try? exportService.generateJsonExportFile(
168+
withName: fileName, content: state.richText)
169+
}
170+
}
171+
if let path {
172+
print("Exported at path == \(path)")
173+
}
174+
}
172175
}
173176

174177
extension ContentView {
175178

176-
var isMac: Bool {
177-
#if os(macOS)
178-
true
179-
#else
180-
false
181-
#endif
182-
}
179+
var isMac: Bool {
180+
#if os(macOS)
181+
true
182+
#else
183+
false
184+
#endif
185+
}
183186

184-
var colorPickers: [RichTextColor] {
185-
[.foreground, .background]
186-
}
187+
var colorPickers: [RichTextColor] {
188+
[.foreground, .background]
189+
}
187190

188-
var formatToolbarEdge: VerticalEdge {
189-
isMac ? .top : .bottom
190-
}
191+
var formatToolbarEdge: VerticalEdge {
192+
isMac ? .top : .bottom
193+
}
194+
}
195+
196+
private func handleImages(
197+
_ action: ImageAttachmentAction,
198+
_ onCompletion: ((ImageAttachmentCompleteAction) -> Void)?
199+
) {
200+
switch action {
201+
case .save(let images):
202+
images.forEach({
203+
$0.updateUrl(with: "https://example.com/image/\($0.id).jpg")
204+
})
205+
onCompletion?(.saved(images))
206+
case .delete(_):
207+
onCompletion?(.deleted)
208+
return
209+
case .getImage(_):
210+
onCompletion?(.getImage(nil))
211+
return
212+
case .getImages(_):
213+
onCompletion?(.getImages([]))
214+
return
215+
}
191216
}

0 commit comments

Comments
 (0)