Refactor modal size to be a modifer parameter (#877)
Way more flexible.
This commit is contained in:
parent
b08243949c
commit
9ca103e949
|
@ -74,7 +74,9 @@ public struct AppCoordinator: View, AppCoordinatorConforming {
|
|||
}
|
||||
.themeModal(
|
||||
item: $modalRoute,
|
||||
isRoot: true,
|
||||
size: modalRoute?.size ?? .large,
|
||||
isFixedWidth: modalRoute?.isFixedWidth ?? false,
|
||||
isFixedHeight: modalRoute?.isFixedHeight ?? false,
|
||||
isInteractive: modalRoute?.isInteractive ?? true,
|
||||
content: modalDestination
|
||||
)
|
||||
|
@ -105,6 +107,36 @@ extension AppCoordinator {
|
|||
}
|
||||
}
|
||||
|
||||
var size: ThemeModalSize {
|
||||
switch self {
|
||||
case .migrateProfiles:
|
||||
return .custom(width: 700, height: 400)
|
||||
|
||||
default:
|
||||
return .large
|
||||
}
|
||||
}
|
||||
|
||||
var isFixedWidth: Bool {
|
||||
switch self {
|
||||
case .migrateProfiles:
|
||||
return true
|
||||
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
var isFixedHeight: Bool {
|
||||
switch self {
|
||||
case .migrateProfiles:
|
||||
return true
|
||||
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
var isInteractive: Bool {
|
||||
switch self {
|
||||
case .editProfile:
|
||||
|
|
|
@ -56,7 +56,10 @@ private extension VPNProviderServerView {
|
|||
} label: {
|
||||
ThemeImage(.filters)
|
||||
}
|
||||
.themePopover(isPresented: $isPresented) {
|
||||
.themePopover(
|
||||
isPresented: $isPresented,
|
||||
size: .custom(width: 400, height: 400)
|
||||
) {
|
||||
filtersContent
|
||||
.modifier(FiltersViewModifier(isPresented: $isPresented))
|
||||
}
|
||||
|
|
|
@ -32,7 +32,6 @@ extension Theme {
|
|||
public convenience init() {
|
||||
self.init(dummy: ())
|
||||
animationCategories = [.diagnostics, .modules, .profiles, .providers]
|
||||
popoverSize = .init(width: 400.0, height: 400.0)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -41,10 +40,12 @@ extension Theme {
|
|||
extension View {
|
||||
public func themePopover<Content>(
|
||||
isPresented: Binding<Bool>,
|
||||
size: ThemeModalSize = .medium,
|
||||
content: @escaping () -> Content
|
||||
) -> some View where Content: View {
|
||||
modifier(ThemeBooleanPopoverModifier(
|
||||
isPresented: isPresented,
|
||||
size: size,
|
||||
popover: content
|
||||
))
|
||||
}
|
||||
|
@ -70,15 +71,18 @@ struct ThemeBooleanPopoverModifier<Popover>: ViewModifier, SizeClassProviding wh
|
|||
@Binding
|
||||
var isPresented: Bool
|
||||
|
||||
let size: ThemeModalSize
|
||||
|
||||
@ViewBuilder
|
||||
let popover: Popover
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
let modalSize = theme.modalSize(size)
|
||||
if isBigDevice {
|
||||
content
|
||||
.popover(isPresented: $isPresented) {
|
||||
popover
|
||||
.frame(minWidth: theme.popoverSize?.width, minHeight: theme.popoverSize?.height)
|
||||
.frame(minWidth: modalSize.width, minHeight: modalSize.height)
|
||||
.themeLockScreen()
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -30,8 +30,6 @@ import SwiftUI
|
|||
extension Theme {
|
||||
public convenience init() {
|
||||
self.init(dummy: Void())
|
||||
rootModalSize = CGSize(width: 750, height: 500)
|
||||
secondaryModalSize = CGSize(width: 500.0, height: 200.0)
|
||||
animationCategories = [.diagnostics, .profiles, .providers]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,12 +32,6 @@ public final class Theme: ObservableObject {
|
|||
|
||||
public internal(set) var animationCategories: Set<ThemeAnimationCategory> = Set(ThemeAnimationCategory.allCases)
|
||||
|
||||
public internal(set) var rootModalSize: CGSize?
|
||||
|
||||
public internal(set) var secondaryModalSize: CGSize?
|
||||
|
||||
public internal(set) var popoverSize: CGSize?
|
||||
|
||||
public internal(set) var relevantWeight: Font.Weight = .semibold
|
||||
|
||||
public internal(set) var titleColor: Color = .primary
|
||||
|
@ -70,6 +64,10 @@ public final class Theme: ObservableObject {
|
|||
|
||||
public internal(set) var logoImage = "Logo"
|
||||
|
||||
public internal(set) var modalSize: (ThemeModalSize) -> CGSize = {
|
||||
$0.defaultSize
|
||||
}
|
||||
|
||||
public internal(set) var systemImageName: (ImageName) -> String = Theme.ImageName.defaultSystemName
|
||||
|
||||
public internal(set) var menuImageName: (MenuImageName) -> String = Theme.MenuImageName.defaultImageName
|
||||
|
|
|
@ -32,16 +32,30 @@ import SwiftUI
|
|||
|
||||
// MARK: Shortcuts
|
||||
|
||||
public enum ThemeModalSize {
|
||||
case small
|
||||
|
||||
case medium
|
||||
|
||||
case large
|
||||
|
||||
case custom(width: CGFloat, height: CGFloat)
|
||||
}
|
||||
|
||||
extension View {
|
||||
public func themeModal<Content>(
|
||||
isPresented: Binding<Bool>,
|
||||
isRoot: Bool = false,
|
||||
size: ThemeModalSize = .medium,
|
||||
isFixedWidth: Bool = false,
|
||||
isFixedHeight: Bool = false,
|
||||
isInteractive: Bool = true,
|
||||
content: @escaping () -> Content
|
||||
) -> some View where Content: View {
|
||||
modifier(ThemeBooleanModalModifier(
|
||||
isPresented: isPresented,
|
||||
isRoot: isRoot,
|
||||
size: size,
|
||||
isFixedWidth: isFixedWidth,
|
||||
isFixedHeight: isFixedHeight,
|
||||
isInteractive: isInteractive,
|
||||
modal: content
|
||||
))
|
||||
|
@ -49,13 +63,17 @@ extension View {
|
|||
|
||||
public func themeModal<Content, T>(
|
||||
item: Binding<T?>,
|
||||
isRoot: Bool = false,
|
||||
size: ThemeModalSize = .medium,
|
||||
isFixedWidth: Bool = false,
|
||||
isFixedHeight: Bool = false,
|
||||
isInteractive: Bool = true,
|
||||
content: @escaping (T) -> Content
|
||||
) -> some View where Content: View, T: Identifiable {
|
||||
modifier(ThemeItemModalModifier(
|
||||
item: item,
|
||||
isRoot: isRoot,
|
||||
size: size,
|
||||
isFixedWidth: isFixedWidth,
|
||||
isFixedHeight: isFixedHeight,
|
||||
isInteractive: isInteractive,
|
||||
modal: content
|
||||
))
|
||||
|
@ -200,6 +218,24 @@ extension View {
|
|||
|
||||
// MARK: - Presentation modifiers
|
||||
|
||||
extension ThemeModalSize {
|
||||
var defaultSize: CGSize {
|
||||
switch self {
|
||||
case .small:
|
||||
return CGSize(width: 300, height: 300)
|
||||
|
||||
case .medium:
|
||||
return CGSize(width: 550, height: 350)
|
||||
|
||||
case .large:
|
||||
return CGSize(width: 800, height: 500)
|
||||
|
||||
case .custom(let width, let height):
|
||||
return CGSize(width: width, height: height)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ThemeBooleanModalModifier<Modal>: ViewModifier where Modal: View {
|
||||
|
||||
@EnvironmentObject
|
||||
|
@ -208,25 +244,34 @@ struct ThemeBooleanModalModifier<Modal>: ViewModifier where Modal: View {
|
|||
@Binding
|
||||
var isPresented: Bool
|
||||
|
||||
let isRoot: Bool
|
||||
let size: ThemeModalSize
|
||||
|
||||
let isFixedWidth: Bool
|
||||
|
||||
let isFixedHeight: Bool
|
||||
|
||||
let isInteractive: Bool
|
||||
|
||||
let modal: () -> Modal
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
let modalSize = theme.modalSize(size)
|
||||
_ = modalSize
|
||||
return content
|
||||
.sheet(isPresented: $isPresented) {
|
||||
modal()
|
||||
.frame(minWidth: modalSize?.width, minHeight: modalSize?.height)
|
||||
#if os(macOS)
|
||||
.frame(
|
||||
minWidth: modalSize.width,
|
||||
maxWidth: isFixedWidth ? modalSize.width : nil,
|
||||
minHeight: modalSize.height,
|
||||
maxHeight: isFixedHeight ? modalSize.height : nil
|
||||
)
|
||||
#endif
|
||||
.interactiveDismissDisabled(!isInteractive)
|
||||
.themeLockScreen()
|
||||
}
|
||||
}
|
||||
|
||||
private var modalSize: CGSize? {
|
||||
isRoot ? theme.rootModalSize : theme.secondaryModalSize
|
||||
}
|
||||
}
|
||||
|
||||
struct ThemeItemModalModifier<Modal, T>: ViewModifier where Modal: View, T: Identifiable {
|
||||
|
@ -237,25 +282,34 @@ struct ThemeItemModalModifier<Modal, T>: ViewModifier where Modal: View, T: Iden
|
|||
@Binding
|
||||
var item: T?
|
||||
|
||||
let isRoot: Bool
|
||||
let size: ThemeModalSize
|
||||
|
||||
let isFixedWidth: Bool
|
||||
|
||||
let isFixedHeight: Bool
|
||||
|
||||
let isInteractive: Bool
|
||||
|
||||
let modal: (T) -> Modal
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
let modalSize = theme.modalSize(size)
|
||||
_ = modalSize
|
||||
return content
|
||||
.sheet(item: $item) {
|
||||
modal($0)
|
||||
.frame(minWidth: modalSize?.width, minHeight: modalSize?.height)
|
||||
#if os(macOS)
|
||||
.frame(
|
||||
minWidth: modalSize.width,
|
||||
maxWidth: isFixedWidth ? modalSize.width : nil,
|
||||
minHeight: modalSize.height,
|
||||
maxHeight: isFixedHeight ? modalSize.height : nil
|
||||
)
|
||||
#endif
|
||||
.interactiveDismissDisabled(!isInteractive)
|
||||
.themeLockScreen()
|
||||
}
|
||||
}
|
||||
|
||||
private var modalSize: CGSize? {
|
||||
isRoot ? theme.rootModalSize : theme.secondaryModalSize
|
||||
}
|
||||
}
|
||||
|
||||
struct ThemeConfirmationModifier: ViewModifier {
|
||||
|
|
Loading…
Reference in New Issue