Creating and updating recipes works in the new edit view
This commit is contained in:
Binary file not shown.
@@ -46,6 +46,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
":" : {
|
":" : {
|
||||||
|
"extractionState" : "stale",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"de" : {
|
"de" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@@ -273,6 +274,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"00" : {
|
"00" : {
|
||||||
|
"extractionState" : "stale",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"de" : {
|
"de" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@@ -3069,6 +3071,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Total" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Total duration:" : {
|
"Total duration:" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
|
|||||||
@@ -10,64 +10,42 @@ import SwiftUI
|
|||||||
|
|
||||||
|
|
||||||
class DurationComponents: ObservableObject {
|
class DurationComponents: ObservableObject {
|
||||||
@Published var secondComponent: String = "00" {
|
@Published var secondComponent: Int = 0 {
|
||||||
didSet {
|
didSet {
|
||||||
if secondComponent.count > 2 {
|
if secondComponent > 59 {
|
||||||
secondComponent = oldValue
|
secondComponent = 59
|
||||||
} else if secondComponent.count == 1 {
|
} else if secondComponent < 0 {
|
||||||
secondComponent = "0\(secondComponent)"
|
secondComponent = 0
|
||||||
} else if secondComponent.count == 0 {
|
|
||||||
secondComponent = "00"
|
|
||||||
}
|
}
|
||||||
let filtered = secondComponent.filter { $0.isNumber }
|
}
|
||||||
if secondComponent != filtered {
|
}
|
||||||
secondComponent = filtered
|
@Published var minuteComponent: Int = 0 {
|
||||||
|
didSet {
|
||||||
|
if minuteComponent > 59 {
|
||||||
|
minuteComponent = 59
|
||||||
|
} else if minuteComponent < 0 {
|
||||||
|
minuteComponent = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Published var minuteComponent: String = "00" {
|
@Published var hourComponent: Int = 0 {
|
||||||
didSet {
|
didSet {
|
||||||
if minuteComponent.count > 2 {
|
if hourComponent < 0 {
|
||||||
minuteComponent = oldValue
|
hourComponent = 0
|
||||||
} else if minuteComponent.count == 1 {
|
|
||||||
minuteComponent = "0\(minuteComponent)"
|
|
||||||
} else if minuteComponent.count == 0 {
|
|
||||||
minuteComponent = "00"
|
|
||||||
}
|
|
||||||
let filtered = minuteComponent.filter { $0.isNumber }
|
|
||||||
if minuteComponent != filtered {
|
|
||||||
minuteComponent = filtered
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Published var hourComponent: String = "00" {
|
|
||||||
didSet {
|
|
||||||
if hourComponent.count > 2 {
|
|
||||||
hourComponent = oldValue
|
|
||||||
} else if hourComponent.count == 1 {
|
|
||||||
hourComponent = "0\(hourComponent)"
|
|
||||||
} else if hourComponent.count == 0 {
|
|
||||||
hourComponent = "00"
|
|
||||||
}
|
|
||||||
let filtered = hourComponent.filter { $0.isNumber }
|
|
||||||
if hourComponent != filtered {
|
|
||||||
hourComponent = filtered
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var displayString: String {
|
var displayString: String {
|
||||||
let intHour = Int(hourComponent) ?? 0
|
if hourComponent != 0 && minuteComponent != 0 {
|
||||||
let intMinute = Int(minuteComponent) ?? 0
|
return "\(hourComponent) h \(minuteComponent) min"
|
||||||
|
} else if hourComponent == 0 && minuteComponent != 0 {
|
||||||
if intHour != 0 && intMinute != 0 {
|
return "\(minuteComponent) min"
|
||||||
return "\(intHour) h \(intMinute) min"
|
} else if hourComponent != 0 && minuteComponent == 0 {
|
||||||
} else if intHour == 0 && intMinute != 0 {
|
return "\(hourComponent) h"
|
||||||
return "\(intMinute) min"
|
|
||||||
} else if intHour != 0 && intMinute == 0 {
|
|
||||||
return "\(intHour) h"
|
|
||||||
} else {
|
} else {
|
||||||
return "-"
|
return "-"
|
||||||
}
|
}
|
||||||
@@ -78,10 +56,10 @@ class DurationComponents: ObservableObject {
|
|||||||
let hourRegex = /([0-9]{1,2})H/
|
let hourRegex = /([0-9]{1,2})H/
|
||||||
let minuteRegex = /([0-9]{1,2})M/
|
let minuteRegex = /([0-9]{1,2})M/
|
||||||
if let match = PTRepresentation.firstMatch(of: hourRegex) {
|
if let match = PTRepresentation.firstMatch(of: hourRegex) {
|
||||||
duration.hourComponent = String(match.1)
|
duration.hourComponent = Int(match.1) ?? 0
|
||||||
}
|
}
|
||||||
if let match = PTRepresentation.firstMatch(of: minuteRegex) {
|
if let match = PTRepresentation.firstMatch(of: minuteRegex) {
|
||||||
duration.minuteComponent = String(match.1)
|
duration.minuteComponent = Int(match.1) ?? 0
|
||||||
}
|
}
|
||||||
return duration
|
return duration
|
||||||
}
|
}
|
||||||
@@ -90,41 +68,47 @@ class DurationComponents: ObservableObject {
|
|||||||
let hourRegex = /([0-9]{1,2})H/
|
let hourRegex = /([0-9]{1,2})H/
|
||||||
let minuteRegex = /([0-9]{1,2})M/
|
let minuteRegex = /([0-9]{1,2})M/
|
||||||
if let match = PTRepresentation.firstMatch(of: hourRegex) {
|
if let match = PTRepresentation.firstMatch(of: hourRegex) {
|
||||||
self.hourComponent = String(match.1)
|
self.hourComponent = Int(match.1) ?? 0
|
||||||
}
|
}
|
||||||
if let match = PTRepresentation.firstMatch(of: minuteRegex) {
|
if let match = PTRepresentation.firstMatch(of: minuteRegex) {
|
||||||
self.minuteComponent = String(match.1)
|
self.minuteComponent = Int(match.1) ?? 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func stringFormatComponents() -> (String, String, String) {
|
||||||
|
let sec = secondComponent < 10 ? "0\(secondComponent)" : "\(secondComponent)"
|
||||||
|
let min = minuteComponent < 10 ? "0\(minuteComponent)" : "\(minuteComponent)"
|
||||||
|
let hr = hourComponent < 10 ? "0\(hourComponent)" : "\(hourComponent)"
|
||||||
|
return (hr, min, sec)
|
||||||
|
}
|
||||||
|
|
||||||
func toPTString() -> String {
|
func toPTString() -> String {
|
||||||
return "PT\(hourComponent)H\(minuteComponent)M00S"
|
let (hr, min, sec) = stringFormatComponents()
|
||||||
|
return "PT\(hr)H\(min)M\(sec)S"
|
||||||
}
|
}
|
||||||
|
|
||||||
func toTimerText() -> String {
|
func toTimerText() -> String {
|
||||||
var timeString = ""
|
var timeString = ""
|
||||||
if hourComponent != "00" {
|
let (hr, min, sec) = stringFormatComponents()
|
||||||
timeString.append("\(hourComponent):")
|
if hourComponent != 0 {
|
||||||
|
timeString.append("\(hr):")
|
||||||
}
|
}
|
||||||
timeString.append("\(minuteComponent):")
|
timeString.append("\(min):")
|
||||||
timeString.append("\(secondComponent)")
|
timeString.append(sec)
|
||||||
return timeString
|
return timeString
|
||||||
}
|
}
|
||||||
|
|
||||||
func toSeconds() -> Double {
|
func toSeconds() -> Double {
|
||||||
guard let hours = Double(hourComponent) else { return 0 }
|
return Double(hourComponent) * 3600 + Double(minuteComponent) * 60 + Double(secondComponent)
|
||||||
guard let minutes = Double(minuteComponent) else { return 0 }
|
|
||||||
guard let seconds = Double(secondComponent) else { return 0 }
|
|
||||||
return hours * 3600 + minutes * 60 + seconds
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func fromSeconds(_ totalSeconds: Int) {
|
func fromSeconds(_ totalSeconds: Int) {
|
||||||
let hours = totalSeconds / 3600
|
let hours = totalSeconds / 3600
|
||||||
let minutes = (totalSeconds % 3600) / 60
|
let minutes = (totalSeconds % 3600) / 60
|
||||||
let seconds = totalSeconds % 60
|
let seconds = totalSeconds % 60
|
||||||
self.hourComponent = String(hours)
|
self.hourComponent = Int(hours)
|
||||||
self.minuteComponent = String(minutes)
|
self.minuteComponent = Int(minutes)
|
||||||
self.secondComponent = String(seconds)
|
self.secondComponent = Int(seconds)
|
||||||
}
|
}
|
||||||
|
|
||||||
static func ptToText(_ ptString: String) -> String? {
|
static func ptToText(_ ptString: String) -> String? {
|
||||||
|
|||||||
@@ -267,17 +267,7 @@ fileprivate struct DurationPicker: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
HStack {
|
HStack {
|
||||||
Text(title)
|
Text(title)
|
||||||
Spacer()
|
|
||||||
TextField("00", text: $duration.hourComponent)
|
|
||||||
.keyboardType(.decimalPad)
|
|
||||||
.textFieldStyle(.roundedBorder)
|
|
||||||
.multilineTextAlignment(.trailing)
|
|
||||||
.frame(maxWidth: 40)
|
|
||||||
Text(":")
|
|
||||||
TextField("00", text: $duration.minuteComponent)
|
|
||||||
.keyboardType(.decimalPad)
|
|
||||||
.textFieldStyle(.roundedBorder)
|
|
||||||
.frame(maxWidth: 40)
|
|
||||||
}
|
}
|
||||||
.frame(maxHeight: 40)
|
.frame(maxHeight: 40)
|
||||||
.clipped()
|
.clipped()
|
||||||
|
|||||||
@@ -102,20 +102,34 @@ struct RecipeView: View {
|
|||||||
.navigationTitle(viewModel.showTitle ? viewModel.recipe.name : "")
|
.navigationTitle(viewModel.showTitle ? viewModel.recipe.name : "")
|
||||||
.toolbar {
|
.toolbar {
|
||||||
if viewModel.editMode {
|
if viewModel.editMode {
|
||||||
|
// Cancel Button
|
||||||
ToolbarItem(placement: .topBarLeading) {
|
ToolbarItem(placement: .topBarLeading) {
|
||||||
Button("Cancel") {
|
Button("Cancel") {
|
||||||
viewModel.editMode = false
|
viewModel.editMode = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Upload Button
|
||||||
ToolbarItem(placement: .topBarTrailing) {
|
ToolbarItem(placement: .topBarTrailing) {
|
||||||
Button {
|
Button {
|
||||||
// TODO: POST edited recipe
|
// TODO: POST edited recipe
|
||||||
|
Task {
|
||||||
if viewModel.newRecipe {
|
if viewModel.newRecipe {
|
||||||
|
if let res = await uploadNewRecipe() {
|
||||||
|
viewModel.alertType = res
|
||||||
|
viewModel.presentAlert = true
|
||||||
|
} else {
|
||||||
presentationMode.wrappedValue.dismiss()
|
presentationMode.wrappedValue.dismiss()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if let res = await uploadEditedRecipe() {
|
||||||
|
viewModel.alertType = res
|
||||||
|
viewModel.presentAlert = true
|
||||||
} else {
|
} else {
|
||||||
viewModel.editMode = false
|
viewModel.editMode = false
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} label: {
|
} label: {
|
||||||
if viewModel.newRecipe {
|
if viewModel.newRecipe {
|
||||||
Text("Upload Recipe")
|
Text("Upload Recipe")
|
||||||
@@ -124,6 +138,8 @@ struct RecipeView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delete Button
|
||||||
if !viewModel.newRecipe {
|
if !viewModel.newRecipe {
|
||||||
ToolbarItem(placement: .topBarTrailing) {
|
ToolbarItem(placement: .topBarTrailing) {
|
||||||
Menu {
|
Menu {
|
||||||
|
|||||||
@@ -12,21 +12,43 @@ import SwiftUI
|
|||||||
|
|
||||||
struct RecipeDurationSection: View {
|
struct RecipeDurationSection: View {
|
||||||
@ObservedObject var viewModel: RecipeView.ViewModel
|
@ObservedObject var viewModel: RecipeView.ViewModel
|
||||||
|
@State var presentPopover: Bool = false
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
if viewModel.editMode {
|
if !viewModel.editMode {
|
||||||
LazyVGrid(columns: [GridItem(.adaptive(minimum: 200, maximum: .infinity), alignment: .leading)]) {
|
|
||||||
EditableDurationView(time: viewModel.observableRecipeDetail.prepTime, title: LocalizedStringKey("Preparation"))
|
|
||||||
EditableDurationView(time: viewModel.observableRecipeDetail.cookTime, title: LocalizedStringKey("Cooking"))
|
|
||||||
EditableDurationView(time: viewModel.observableRecipeDetail.totalTime, title: LocalizedStringKey("Total time"))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
LazyVGrid(columns: [GridItem(.adaptive(minimum: 200, maximum: .infinity), alignment: .leading)]) {
|
LazyVGrid(columns: [GridItem(.adaptive(minimum: 200, maximum: .infinity), alignment: .leading)]) {
|
||||||
DurationView(time: viewModel.observableRecipeDetail.prepTime, title: LocalizedStringKey("Preparation"))
|
DurationView(time: viewModel.observableRecipeDetail.prepTime, title: LocalizedStringKey("Preparation"))
|
||||||
DurationView(time: viewModel.observableRecipeDetail.cookTime, title: LocalizedStringKey("Cooking"))
|
DurationView(time: viewModel.observableRecipeDetail.cookTime, title: LocalizedStringKey("Cooking"))
|
||||||
DurationView(time: viewModel.observableRecipeDetail.totalTime, title: LocalizedStringKey("Total time"))
|
DurationView(time: viewModel.observableRecipeDetail.totalTime, title: LocalizedStringKey("Total time"))
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
LazyVGrid(columns: [GridItem(.adaptive(minimum: 200, maximum: .infinity), alignment: .leading)]) {
|
||||||
|
Button {
|
||||||
|
presentPopover.toggle()
|
||||||
|
} label: {
|
||||||
|
DurationView(time: viewModel.observableRecipeDetail.prepTime, title: LocalizedStringKey("Preparation"))
|
||||||
}
|
}
|
||||||
|
Button {
|
||||||
|
presentPopover.toggle()
|
||||||
|
} label: {
|
||||||
|
DurationView(time: viewModel.observableRecipeDetail.cookTime, title: LocalizedStringKey("Cooking"))
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
presentPopover.toggle()
|
||||||
|
} label: {
|
||||||
|
DurationView(time: viewModel.observableRecipeDetail.totalTime, title: LocalizedStringKey("Total time"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.popover(isPresented: $presentPopover) {
|
||||||
|
EditableDurationView(
|
||||||
|
prepTime: viewModel.observableRecipeDetail.prepTime,
|
||||||
|
cookTime: viewModel.observableRecipeDetail.cookTime,
|
||||||
|
totalTime: viewModel.observableRecipeDetail.totalTime
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,48 +74,30 @@ fileprivate struct DurationView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fileprivate struct EditableDurationView: View {
|
fileprivate struct EditableDurationView: View {
|
||||||
@ObservedObject var time: DurationComponents
|
@ObservedObject var prepTime: DurationComponents
|
||||||
@State var title: LocalizedStringKey
|
@ObservedObject var cookTime: DurationComponents
|
||||||
@State var presentPopoverView: Bool = false
|
@ObservedObject var totalTime: DurationComponents
|
||||||
@State var hour: Int = 0
|
|
||||||
@State var minute: Int = 0
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
ScrollView {
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
HStack {
|
HStack {
|
||||||
SecondaryLabel(text: title)
|
SecondaryLabel(text: "Preparation")
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
Button {
|
TimePickerView(selectedHour: $prepTime.hourComponent, selectedMinute: $prepTime.minuteComponent)
|
||||||
presentPopoverView.toggle()
|
SecondaryLabel(text: "Cooking")
|
||||||
} label: {
|
TimePickerView(selectedHour: $cookTime.hourComponent, selectedMinute: $cookTime.minuteComponent)
|
||||||
HStack {
|
SecondaryLabel(text: "Total")
|
||||||
Image(systemName: "clock")
|
TimePickerView(selectedHour: $totalTime.hourComponent, selectedMinute: $totalTime.minuteComponent)
|
||||||
.foregroundStyle(.secondary)
|
|
||||||
Text(time.displayString)
|
|
||||||
.lineLimit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.padding()
|
.padding()
|
||||||
.popover(isPresented: $presentPopoverView) {
|
|
||||||
TimePickerPopoverView(selectedHour: $hour, selectedMinute: $minute)
|
|
||||||
}
|
|
||||||
.onChange(of: presentPopoverView) { presentPopover in
|
|
||||||
if !presentPopover {
|
|
||||||
time.hourComponent = String(hour)
|
|
||||||
time.minuteComponent = String(minute)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.onAppear {
|
|
||||||
minute = Int(time.minuteComponent) ?? 0
|
|
||||||
hour = Int(time.hourComponent) ?? 0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fileprivate struct TimePickerPopoverView: View {
|
fileprivate struct TimePickerView: View {
|
||||||
@Binding var selectedHour: Int
|
@Binding var selectedHour: Int
|
||||||
@Binding var selectedMinute: Int
|
@Binding var selectedMinute: Int
|
||||||
|
|
||||||
|
|||||||
@@ -14,14 +14,19 @@ struct RecipeMetadataSection: View {
|
|||||||
@EnvironmentObject var appState: AppState
|
@EnvironmentObject var appState: AppState
|
||||||
@ObservedObject var viewModel: RecipeView.ViewModel
|
@ObservedObject var viewModel: RecipeView.ViewModel
|
||||||
|
|
||||||
@State var categories: [String] = []
|
|
||||||
@State var keywords: [RecipeKeyword] = []
|
@State var keywords: [RecipeKeyword] = []
|
||||||
|
var categories: [String] {
|
||||||
|
appState.categories.map({ category in category.name })
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@State var presentKeywordSheet: Bool = false
|
@State var presentKeywordSheet: Bool = false
|
||||||
@State var presentServingsPopover: Bool = false
|
@State var presentServingsPopover: Bool = false
|
||||||
@State var presentCategoryPopover: Bool = false
|
@State var presentCategoryPopover: Bool = false
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
|
// Category
|
||||||
//CategoryPickerView(items: $categories, input: $viewModel.observableRecipeDetail.recipeCategory, titleKey: "Category")
|
//CategoryPickerView(items: $categories, input: $viewModel.observableRecipeDetail.recipeCategory, titleKey: "Category")
|
||||||
SecondaryLabel(text: "Category")
|
SecondaryLabel(text: "Category")
|
||||||
HStack {
|
HStack {
|
||||||
@@ -29,13 +34,16 @@ struct RecipeMetadataSection: View {
|
|||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
.textFieldStyle(.roundedBorder)
|
.textFieldStyle(.roundedBorder)
|
||||||
|
|
||||||
Button {
|
Picker("Choose", selection: $viewModel.observableRecipeDetail.recipeCategory) {
|
||||||
presentCategoryPopover.toggle()
|
Text("").tag("")
|
||||||
} label: {
|
ForEach(categories, id: \.self) { item in
|
||||||
Text("Choose")
|
Text(item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.pickerStyle(.menu)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keywords
|
||||||
SecondaryLabel(text: "Keywords")
|
SecondaryLabel(text: "Keywords")
|
||||||
|
|
||||||
if !viewModel.observableRecipeDetail.keywords.isEmpty {
|
if !viewModel.observableRecipeDetail.keywords.isEmpty {
|
||||||
@@ -54,7 +62,7 @@ struct RecipeMetadataSection: View {
|
|||||||
Image(systemName: "chevron.right")
|
Image(systemName: "chevron.right")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Servings / Yield
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
SecondaryLabel(text: "Servings")
|
SecondaryLabel(text: "Servings")
|
||||||
Button {
|
Button {
|
||||||
@@ -63,22 +71,16 @@ struct RecipeMetadataSection: View {
|
|||||||
Text("\(viewModel.observableRecipeDetail.recipeYield) serving(s)")
|
Text("\(viewModel.observableRecipeDetail.recipeYield) serving(s)")
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
}
|
}
|
||||||
|
.popover(isPresented: $presentServingsPopover) {
|
||||||
|
PickerPopoverView(value: $viewModel.observableRecipeDetail.recipeYield, items: 0..<99, titleKey: "Servings")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding()
|
.padding()
|
||||||
.background(Rectangle().foregroundStyle(Color.white.opacity(0.1)))
|
.background(Rectangle().foregroundStyle(Color.white.opacity(0.1)))
|
||||||
.task {
|
|
||||||
categories = appState.categories.map({ category in category.name })
|
|
||||||
}
|
|
||||||
.sheet(isPresented: $presentKeywordSheet) {
|
.sheet(isPresented: $presentKeywordSheet) {
|
||||||
KeywordPickerView(title: "Keywords", searchSuggestions: appState.allKeywords, selection: $viewModel.observableRecipeDetail.keywords)
|
KeywordPickerView(title: "Keywords", searchSuggestions: appState.allKeywords, selection: $viewModel.observableRecipeDetail.keywords)
|
||||||
}
|
}
|
||||||
.popover(isPresented: $presentServingsPopover) {
|
|
||||||
PickerPopoverView(value: $viewModel.observableRecipeDetail.recipeYield, items: 0..<99, titleKey: "Servings")
|
|
||||||
}
|
|
||||||
.popover(isPresented: $presentCategoryPopover) {
|
|
||||||
PickerPopoverView(value: $viewModel.observableRecipeDetail.recipeCategory, items: categories, titleKey: "Category")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,7 +97,7 @@ fileprivate struct PickerPopoverView<Item: Hashable & CustomStringConvertible, C
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.pickerStyle(WheelPickerStyle())
|
.pickerStyle(WheelPickerStyle())
|
||||||
.frame(width: 100, height: 150)
|
.frame(width: 150, height: 150)
|
||||||
.clipped()
|
.clipped()
|
||||||
}
|
}
|
||||||
.padding()
|
.padding()
|
||||||
|
|||||||
Reference in New Issue
Block a user