Skip to content

Commit 377f5ba

Browse files
committed
New modifiers to disable button and hit testing when loading
1 parent fe51669 commit 377f5ba

8 files changed

+129
-12
lines changed

README.md

+21-4
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,9 @@ ThrowableButton {
4747
}
4848
```
4949

50-
By default, when the button closure throws, the button will Shake on error.
51-
For now, only the shake behavior is built-in:
50+
When the button closure throws, the button will shake by default
51+
52+
For now, only this shake behavior is built-in:
5253

5354
<table>
5455
<tr>
@@ -59,7 +60,7 @@ For now, only the shake behavior is built-in:
5960
</tr>
6061
</table>
6162

62-
You can disable it by passing `.none` to throwableButtonStyle:
63+
You can disable still it by passing `.none` to throwableButtonStyle:
6364

6465
```swift
6566
ThrowableButton {
@@ -107,7 +108,6 @@ ThrowableButton {
107108
### Asynchronous
108109

109110
Use it as any SwiftUI button, but the closure will support both try and await.
110-
When the process is in progress, the button hit test will be disabled, and the process will only triggers once.
111111

112112
```swift
113113
AsyncButton {
@@ -117,6 +117,23 @@ AsyncButton {
117117
}
118118
```
119119

120+
When the process is in progress, another button press will not result in a new Task being issued. But the button is still enabled and hittable.
121+
You can disable the button on loading using `disabledWhenLoading` modifier.
122+
```swift
123+
AsyncButton {
124+
...
125+
}
126+
.disabledWhenLoading()
127+
```
128+
129+
You can also disable hitTesting when loading with `allowsHitTestingWhenLoading` modifier.
130+
```swift
131+
AsyncButton {
132+
...
133+
}
134+
.allowsHitTestingWhenLoading(false)
135+
```
136+
120137
While the progress is loading, the button will animate, defaulting by replacing the label of the button with a `ProgressIndicator`.
121138
All sort of styles are built-in:
122139

Sources/ButtonKit/AsyncStyle/AsyncStyle+Leading.swift

+6-1
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,15 @@ import SwiftUI
3030
public struct LeadingAsyncButtonStyle: AsyncButtonStyle {
3131
let disable: Bool
3232

33-
public init(disableOnLoading: Bool = true) {
33+
@available(*, deprecated, message: "Initializing with disableOnLoading is deprecated and will be removed in 0.2.0; Use `.disabledWhenLoading` modifier instead")
34+
public init(disableOnLoading: Bool) {
3435
self.disable = disableOnLoading
3536
}
3637

38+
public init() {
39+
disable = false
40+
}
41+
3742
public func makeLabel(configuration: LabelConfiguration) -> some View {
3843
HStack(spacing: 8) {
3944
if configuration.isLoading {

Sources/ButtonKit/AsyncStyle/AsyncStyle+Overlay.swift

+6-1
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,15 @@ import SwiftUI
3030
public struct OverlayAsyncButtonStyle: AsyncButtonStyle {
3131
let disable: Bool
3232

33-
public init(disableOnLoading: Bool = true) {
33+
@available(*, deprecated, message: "Initializing with disableOnLoading is deprecated and will be removed in 0.2.0; Use `.disabledWhenLoading` modifier instead")
34+
public init(disableOnLoading: Bool) {
3435
self.disable = disableOnLoading
3536
}
3637

38+
public init() {
39+
disable = false
40+
}
41+
3742
public func makeLabel(configuration: LabelConfiguration) -> some View {
3843
configuration.label
3944
.opacity(configuration.isLoading ? 0 : 1)

Sources/ButtonKit/AsyncStyle/AsyncStyle+Pulse.swift

+6-1
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,15 @@ import SwiftUI
3030
public struct PulseAsyncButtonStyle: AsyncButtonStyle {
3131
let disable: Bool
3232

33-
public init(disableOnLoading: Bool = false) {
33+
@available(*, deprecated, message: "Initializing with disableOnLoading is deprecated and will be removed in 0.2.0; Use `.disabledWhenLoading` modifier instead")
34+
public init(disableOnLoading: Bool) {
3435
self.disable = disableOnLoading
3536
}
3637

38+
public init() {
39+
disable = false
40+
}
41+
3742
public func makeButton(configuration: ButtonConfiguration) -> some View {
3843
configuration.button
3944
.compositingGroup()

Sources/ButtonKit/AsyncStyle/AsyncStyle+Trailing.swift

+6-1
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,15 @@ import SwiftUI
3030
public struct TrailingAsyncButtonStyle: AsyncButtonStyle {
3131
let disable: Bool
3232

33-
public init(disableOnLoading: Bool = true) {
33+
@available(*, deprecated, message: "Initializing with disableOnLoading is deprecated and will be removed in 0.2.0; Use `.disabledWhenLoading` modifier instead")
34+
public init(disableOnLoading: Bool) {
3435
self.disable = disableOnLoading
3536
}
3637

38+
public init() {
39+
disable = false
40+
}
41+
3742
public func makeLabel(configuration: LabelConfiguration) -> some View {
3843
HStack(spacing: 8) {
3944
configuration.label

Sources/ButtonKit/Button+Async.swift

+7-2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ import SwiftUI
3030
public struct AsyncButton<S: View>: View {
3131
@Environment(\.asyncButtonStyle)
3232
private var asyncButtonStyle
33+
@Environment(\.allowsHitTestingWhenLoading)
34+
private var allowsHitTestingWhenLoading
35+
@Environment(\.disabledWhenLoading)
36+
private var disabledWhenLoading
3337
@Environment(\.throwableButtonStyle)
3438
private var throwableButtonStyle
3539

@@ -77,7 +81,8 @@ public struct AsyncButton<S: View>: View {
7781
)
7882
return asyncButtonStyle
7983
.makeButton(configuration: asyncConfiguration)
80-
.allowsHitTesting(task == nil)
84+
.allowsHitTesting(allowsHitTestingWhenLoading || task == nil)
85+
.disabled(disabledWhenLoading && task != nil)
8186
}
8287

8388
public init(role: ButtonRole? = nil, action: @escaping () async throws -> Void, @ViewBuilder label: @escaping () -> S) {
@@ -120,6 +125,6 @@ extension AsyncButton where S == Text {
120125
}
121126
.buttonStyle(.borderedProminent)
122127
.buttonBorderShape(.roundedRectangle)
123-
.asyncButtonStyle(.pulse)
128+
.asyncButtonStyle(.overlay)
124129
.throwableButtonStyle(.shake)
125130
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
//
2+
// Button+AsyncDisabled.swift
3+
// ButtonKit
4+
//
5+
// MIT License
6+
//
7+
// Copyright (c) 2024 Thomas Durand
8+
//
9+
// Permission is hereby granted, free of charge, to any person obtaining a copy
10+
// of this software and associated documentation files (the "Software"), to deal
11+
// in the Software without restriction, including without limitation the rights
12+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13+
// copies of the Software, and to permit persons to whom the Software is
14+
// furnished to do so, subject to the following conditions:
15+
//
16+
// The above copyright notice and this permission notice shall be included in all
17+
// copies or substantial portions of the Software.
18+
//
19+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25+
// SOFTWARE.
26+
//
27+
28+
import Foundation
29+
30+
import SwiftUI
31+
32+
// MARK: Public protocol
33+
34+
extension View {
35+
public func allowsHitTestingWhenLoading(_ enabled: Bool) -> some View {
36+
environment(\.allowsHitTestingWhenLoading, enabled)
37+
}
38+
39+
public func disabledWhenLoading(_ disabled: Bool = true) -> some View {
40+
environment(\.disabledWhenLoading, disabled)
41+
}
42+
}
43+
44+
// MARK: SwiftUI Environment
45+
46+
struct AllowsHitTestingWhenLoadingKey: EnvironmentKey {
47+
static let defaultValue: Bool = false
48+
}
49+
50+
struct DisabledWhenLoadingKey: EnvironmentKey {
51+
static let defaultValue: Bool = false
52+
}
53+
54+
extension EnvironmentValues {
55+
var allowsHitTestingWhenLoading: Bool {
56+
get {
57+
return self[AllowsHitTestingWhenLoadingKey.self]
58+
}
59+
set {
60+
self[AllowsHitTestingWhenLoadingKey.self] = newValue
61+
}
62+
}
63+
64+
var disabledWhenLoading: Bool {
65+
get {
66+
return self[DisabledWhenLoadingKey.self]
67+
}
68+
set {
69+
self[DisabledWhenLoadingKey.self] = newValue
70+
}
71+
}
72+
}

Sources/ButtonKit/Subview/HierarchicalProgressView.swift

+5-2
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727

2828
import SwiftUI
2929

30-
// What I got
3130
public struct HierarchicalProgressView: View {
3231
public var body: some View {
3332
ProgressView()
@@ -45,5 +44,9 @@ public struct HierarchicalProgressView: View {
4544

4645
#Preview {
4746
HierarchicalProgressView()
48-
.foregroundStyle(.red)
47+
.foregroundStyle(.linearGradient(
48+
colors: [.blue, .red],
49+
startPoint: .topLeading,
50+
endPoint: .bottomTrailing)
51+
)
4952
}

0 commit comments

Comments
 (0)