-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Expand file tree
/
Copy pathViewExtension.swift
More file actions
140 lines (122 loc) · 5.15 KB
/
ViewExtension.swift
File metadata and controls
140 lines (122 loc) · 5.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
import SwiftUI
import ComposableArchitecture
let ITEM_SELECTED_COLOR = Color("ItemSelectedColor")
struct HoverBackgroundModifier: ViewModifier {
var isHovered: Bool
func body(content: Content) -> some View {
content
.background(isHovered ? ITEM_SELECTED_COLOR : Color.clear)
}
}
struct HoverRadiusBackgroundModifier: ViewModifier {
var isHovered: Bool
var hoverColor: Color?
var cornerRadius: CGFloat = 0
var showBorder: Bool = false
var borderColor: Color = .white.opacity(0.07)
var borderWidth: CGFloat = 1
func body(content: Content) -> some View {
content
.background(
RoundedRectangle(cornerRadius: cornerRadius)
.fill(isHovered ? hoverColor ?? ITEM_SELECTED_COLOR : Color.clear)
)
.clipShape(
RoundedRectangle(cornerRadius: cornerRadius, style: .continuous)
)
.overlay(
(isHovered && showBorder) ?
RoundedRectangle(cornerRadius: cornerRadius, style: .continuous)
.strokeBorder(borderColor, lineWidth: borderWidth) :
nil
)
}
}
struct HoverForegroundModifier: ViewModifier {
var isHovered: Bool
var defaultColor: Color
func body(content: Content) -> some View {
content.foregroundColor(isHovered ? Color.white : defaultColor)
}
}
extension View {
public func hoverBackground(isHovered: Bool) -> some View {
self.modifier(HoverBackgroundModifier(isHovered: isHovered))
}
public func hoverRadiusBackground(isHovered: Bool, cornerRadius: CGFloat) -> some View {
self.modifier(HoverRadiusBackgroundModifier(isHovered: isHovered, cornerRadius: cornerRadius))
}
public func hoverRadiusBackground(isHovered: Bool, hoverColor: Color?, cornerRadius: CGFloat) -> some View {
self.modifier(HoverRadiusBackgroundModifier(isHovered: isHovered, hoverColor: hoverColor, cornerRadius: cornerRadius))
}
public func hoverRadiusBackground(isHovered: Bool, hoverColor: Color?, cornerRadius: CGFloat, showBorder: Bool, borderColor: Color = .white.opacity(0.07)) -> some View {
self.modifier(
HoverRadiusBackgroundModifier(
isHovered: isHovered,
hoverColor: hoverColor,
cornerRadius: cornerRadius,
showBorder: true,
borderColor: borderColor
)
)
}
public func hoverForeground(isHovered: Bool, defaultColor: Color) -> some View {
self.modifier(HoverForegroundModifier(isHovered: isHovered, defaultColor: defaultColor))
}
public func hoverPrimaryForeground(isHovered: Bool) -> some View {
self.hoverForeground(isHovered: isHovered, defaultColor: .primary)
}
public func hoverSecondaryForeground(isHovered: Bool) -> some View {
self.hoverForeground(isHovered: isHovered, defaultColor: .secondary)
}
// MARK: - Editor Mode
/// Dims the view when in edit mode and provides tap/keyboard exit functionality
/// - Parameters:
/// - chat: The chat store
/// - messageId: Optional message ID to determine if this specific message should be dimmed
/// - isDimmed: Whether this view should be dimmed (defaults to true when editing affects this view)
/// - allowTapToExit: Whether tapping on this view should exit edit mode (defaults to true)
func dimWithExitEditMode(
_ chat: StoreOf<Chat>,
applyTo messageId: String? = nil,
isDimmed: Bool? = nil,
allowTapToExit: Bool = true
) -> some View {
let editUserMessageEffectedMessageIds = chat.editUserMessageEffectedMessages.map { $0.id }
let shouldDim = isDimmed ?? {
guard chat.editorMode.isEditingUserMessage else { return false }
guard let messageId else { return true }
return editUserMessageEffectedMessageIds.contains(messageId)
}()
let isInEditMode = chat.editorMode.isEditingUserMessage
let shouldAllowTapExit = allowTapToExit && isInEditMode
return self
.opacity(shouldDim && isInEditMode ? 0.5 : 1)
.overlay(
Group {
if shouldAllowTapExit {
Color.clear
.contentShape(Rectangle()) // Ensure the entire area is tappable
.onTapGesture {
if shouldAllowTapExit {
chat.send(.setEditorMode(.input))
}
}
}
}
)
.background(
// Global escape key handler - only add once per view hierarchy
Group {
if isInEditMode {
Button("") {
chat.send(.setEditorMode(.input))
}
.keyboardShortcut(.escape, modifiers: [])
.opacity(0)
.accessibilityHidden(true)
}
}
)
}
}