@bm-fe/react-native-ui-components 1.1.3 → 1.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/Components/BuyCrypto/Bitmart Card.imageset/Contents.json +21 -0
  2. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/Components/BuyCrypto/Bitmart Card.imageset/Property 1=Bitmart Card.svg +3 -0
  3. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/Components/BuyCrypto/Contents.json +6 -0
  4. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/Components/BuyCrypto/Credit Debit Card.imageset/Contents.json +21 -0
  5. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/Components/BuyCrypto/Credit Debit Card.imageset/Property 1=Credit Debit Card.svg +3 -0
  6. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/Components/BuyCrypto/Crypto Prepaid Card.imageset/Contents.json +21 -0
  7. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/Components/BuyCrypto/Crypto Prepaid Card.imageset/Property 1=Crypto Prepaid Card.svg +3 -0
  8. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/Components/BuyCrypto/Mobile Recharge.imageset/Contents.json +21 -0
  9. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/Components/BuyCrypto/Mobile Recharge.imageset/Mobile Recharge.svg +3 -0
  10. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/Components/BuyCrypto/P2P Trading.imageset/Contents.json +21 -0
  11. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/Components/BuyCrypto/P2P Trading.imageset/Property 1=P2P Trading.svg +3 -0
  12. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/Components/BuyCrypto/SEPA Deposit.imageset/Contents.json +21 -0
  13. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/Components/BuyCrypto/SEPA Deposit.imageset/SEPA Deposit.svg +3 -0
  14. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/Components/BuyCrypto/Third-Party Payment.imageset/Contents.json +21 -0
  15. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/Components/BuyCrypto/Third-Party Payment.imageset/Property 1=Third-Party Payment.svg +3 -0
  16. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/Components/Contents.json +6 -0
  17. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/Contents.json +6 -0
  18. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/checkmark.imageset/Contents.json +21 -0
  19. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/checkmark.imageset/checkmark.pdf +0 -0
  20. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/close_icon.imageset/Contents.json +22 -0
  21. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/close_icon.imageset/close_icon@2x.png +0 -0
  22. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/close_icon.imageset/close_icon@3x.png +0 -0
  23. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/cross.imageset/Contents.json +21 -0
  24. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/cross.imageset/cross.pdf +0 -0
  25. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/progress.imageset/Contents.json +21 -0
  26. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/progress.imageset/progress.pdf +0 -0
  27. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/progress_circular.imageset/Contents.json +21 -0
  28. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/progress_circular.imageset/progress_circular.pdf +0 -0
  29. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/refresh_footer_dark.imageset/Contents.json +22 -0
  30. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/refresh_footer_dark.imageset/refresh_footer_dark@2x.png +0 -0
  31. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/refresh_footer_dark.imageset/refresh_footer_dark@3x.png +0 -0
  32. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/refresh_footer_light.imageset/Contents.json +22 -0
  33. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/refresh_footer_light.imageset/refresh_footer_light@2x.png +0 -0
  34. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/refresh_footer_light.imageset/refresh_footer_light@3x.png +0 -0
  35. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/search_icon.imageset/Contents.json +21 -0
  36. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/search_icon.imageset/Group 13994.svg +7 -0
  37. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/sheet_dark_chose.imageset/Contents.json +21 -0
  38. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/sheet_dark_chose.imageset/Frame.svg +3 -0
  39. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/sheet_list_arrow.imageset/Contents.json +22 -0
  40. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/sheet_list_arrow.imageset/sheet_list_arrow@2x.png +0 -0
  41. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/sheet_list_arrow.imageset/sheet_list_arrow@3x.png +0 -0
  42. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/sheet_list_cell_checkbox.imageset/Contents.json +21 -0
  43. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/sheet_list_cell_checkbox.imageset/Frame (1).svg +3 -0
  44. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/sheet_list_chose.imageset/Contents.json +21 -0
  45. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/sheet_list_chose.imageset/Frame.png +0 -0
  46. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/slider_bubble.imageset/Contents.json +22 -0
  47. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/slider_bubble.imageset/slider_bubble@2x.png +0 -0
  48. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/slider_bubble.imageset/slider_bubble@3x.png +0 -0
  49. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/spot_second_floor_refresh_arrow.imageset/Contents.json +21 -0
  50. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Assets.xcassets/spot_second_floor_refresh_arrow.imageset/spot_second_floor_refresh_arrow.svg +8 -0
  51. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Font/Alexandria-Medium.ttf +0 -0
  52. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Font/Alexandria-Regular.ttf +0 -0
  53. package/ios/Modules/BMUIComponents/BMUIComponents/Assets/Font/Alexandria-SemiBold.ttf +0 -0
  54. package/ios/Modules/BMUIComponents/BMUIComponents/Classes/BMFont/BMFont.swift +82 -0
  55. package/ios/Modules/BMUIComponents/BMUIComponents/Classes/BMFont/UIFontExtensions.swift +120 -0
  56. package/ios/Modules/BMUIComponents/BMUIComponents/Classes/Components/AlertView/BMComponentAlertController.swift +574 -0
  57. package/ios/Modules/BMUIComponents/BMUIComponents/Classes/Components/Buttons/BMComponentButton+Examples.swift +77 -0
  58. package/ios/Modules/BMUIComponents/BMUIComponents/Classes/Components/Buttons/BMComponentButton.swift +373 -0
  59. package/ios/Modules/BMUIComponents/BMUIComponents/Classes/Components/Buttons/BMComponentButtonConfiguration.swift +181 -0
  60. package/ios/Modules/BMUIComponents/BMUIComponents/Classes/Components/Popup/BMComponentPopupController.swift +312 -0
  61. package/ios/Modules/BMUIComponents/BMUIComponents/Classes/Components/SegmentView/BMComponentSegmentedTitleCell.swift +294 -0
  62. package/ios/Modules/BMUIComponents/BMUIComponents/Classes/Components/SegmentView/BMComponentSegmentedTitleDataSource.swift +49 -0
  63. package/ios/Modules/BMUIComponents/BMUIComponents/Classes/Components/SegmentView/BMComponentSegmentedView.swift +292 -0
  64. package/ios/Modules/BMUIComponents/LICENSE +19 -0
  65. package/ios/Modules/BMUIComponents/README.md +29 -0
  66. package/package.json +3 -1
  67. package/react-native-ui-components.podspec +33 -4
@@ -0,0 +1,373 @@
1
+ //
2
+ // BMComponentButton.swift
3
+ // BMComponents
4
+ //
5
+ // Created by james on 2025/12/31.
6
+ //
7
+
8
+ import UIKit
9
+ import BMTheme
10
+ import BMCore
11
+ import SnapKit
12
+
13
+ /// 组件化 Button - 简化设计,确保文字正常显示
14
+ public class BMComponentButton: UIControl {
15
+
16
+ /// 按钮配置
17
+ public var configuration: BMComponentButtonConfiguration {
18
+ didSet {
19
+ applyConfiguration()
20
+ }
21
+ }
22
+
23
+ /// 按钮点击状态
24
+ private var currentState: BMComponentButtonState = .normal {
25
+ didSet {
26
+ updateStateAppearance()
27
+ }
28
+ }
29
+
30
+ /// 是否正在加载
31
+ public var isLoading: Bool = false {
32
+ didSet {
33
+ updateState()
34
+ }
35
+ }
36
+
37
+ /// 重写 isEnabled 属性以同步状态
38
+ public override var isEnabled: Bool {
39
+ didSet {
40
+ updateState()
41
+ }
42
+ }
43
+
44
+ /// 标题文本
45
+ public var title: String? {
46
+ didSet {
47
+ titleLabel.text = title
48
+ updateLayout()
49
+ }
50
+ }
51
+
52
+ /// 图标
53
+ public var icon: UIImage? {
54
+ didSet {
55
+ iconImageView.image = icon
56
+ updateLayout()
57
+ }
58
+ }
59
+
60
+ // MARK: - Private Properties
61
+
62
+ private let titleLabel = UILabel()
63
+ private let iconImageView = UIImageView()
64
+ private let loadingIndicator = UIActivityIndicatorView(style: .medium)
65
+
66
+ private var heightConstraint: Constraint?
67
+ private var iconLeadingConstraint: Constraint?
68
+ private var iconTrailingConstraint: Constraint?
69
+ private var titleLeadingConstraint: Constraint?
70
+ private var titleTrailingConstraint: Constraint?
71
+
72
+ // MARK: - Initialization
73
+
74
+ /// 通过配置初始化
75
+ public init(title: String? = nil, icon: UIImage? = nil,configuration: BMComponentButtonConfiguration = .primary()) {
76
+ self.title = title
77
+ self.icon = icon
78
+ self.configuration = configuration
79
+ super.init(frame: .zero)
80
+ setupUI()
81
+ applyConfiguration()
82
+ updateLayout()
83
+ }
84
+
85
+
86
+ required init?(coder: NSCoder) {
87
+ fatalError("init(coder:) has not been implemented")
88
+ }
89
+
90
+ // MARK: - Setup
91
+
92
+ private func setupUI() {
93
+ // 添加子视图
94
+ addSubview(titleLabel)
95
+ addSubview(iconImageView)
96
+ addSubview(loadingIndicator)
97
+ // 配置标题标签
98
+ titleLabel.text = title
99
+ titleLabel.textAlignment = .center
100
+ titleLabel.numberOfLines = 1
101
+ titleLabel.adjustsFontSizeToFitWidth = false
102
+ titleLabel.lineBreakMode = .byTruncatingTail
103
+
104
+ // 配置图标
105
+ iconImageView.image = icon
106
+ iconImageView.contentMode = .scaleAspectFit
107
+ iconImageView.tintColor = .white
108
+
109
+ // 配置加载指示器
110
+ loadingIndicator.hidesWhenStopped = true
111
+ loadingIndicator.color = .white // 默认白色,会在 applyConfiguration 中更新
112
+
113
+ // 设置高度约束
114
+ snp.makeConstraints { make in
115
+ heightConstraint = make.height.equalTo(configuration.size.height).constraint
116
+ }
117
+ // 初始化状态
118
+ updateState()
119
+ }
120
+
121
+ // MARK: - Configuration
122
+ private func applyConfiguration() {
123
+ // 更新高度
124
+ heightConstraint?.update(offset: configuration.size.height)
125
+ // 更新字体
126
+ if let font = configuration.currentFont {
127
+ titleLabel.font = font
128
+ } else {
129
+ titleLabel.font = configuration.size.sizeFont
130
+ }
131
+ // 更新圆角
132
+ if let cornerRadius = configuration.cornerRadius {
133
+ layer.cornerRadius = cornerRadius
134
+ } else {
135
+ layer.cornerRadius = configuration.size.height / 2
136
+ }
137
+ layer.masksToBounds = true
138
+ // 更新颜色
139
+ updateColors()
140
+ }
141
+
142
+ /// 根据状态更新外观
143
+ private func updateStateAppearance() {
144
+ // 先更新背景色(所有状态都使用相同的背景色)
145
+ updateColors()
146
+ switch currentState {
147
+ case .normal:
148
+ // 正常状态 - 正常效果
149
+ alpha = 1.0
150
+ case .pressed:
151
+ // 点击状态 - 正常背景加上透明度
152
+ alpha = 0.7 // 点击时降低透明度
153
+ case .disable:
154
+ // 禁用状态 - 不可以点击效果
155
+ alpha = configuration.disableAlpha
156
+ case .loading:
157
+ // 加载状态 - 显示加载动画,隐藏文字和图标
158
+ alpha = 1.0
159
+ titleLabel.isHidden = true
160
+ iconImageView.isHidden = true
161
+ loadingIndicator.isHidden = false
162
+ loadingIndicator.startAnimating()
163
+ return // 加载状态需要特殊处理,直接返回
164
+ }
165
+
166
+ // 非加载状态时,根据是否有文字/图标来决定显示
167
+ if currentState != .loading {
168
+ let hasTitle = title != nil && !title!.isEmpty
169
+ let hasIcon = icon != nil
170
+ titleLabel.isHidden = !hasTitle
171
+ iconImageView.isHidden = !hasIcon
172
+ loadingIndicator.stopAnimating()
173
+ loadingIndicator.isHidden = true
174
+ }
175
+ }
176
+
177
+ private func updateColors() {
178
+ if currentState == .disable {
179
+ // 背景色
180
+ cexTheme.backgroundColor = .bgLineColor
181
+ // 文字颜色
182
+ titleLabel.cexTheme.textColor = .thirdColor
183
+ // 图标颜色
184
+ iconImageView.cexTheme.tintColor = .thirdColor
185
+ // 加载指示器颜色(使用前景色)
186
+ // loadingIndicator.color = configuration.type.foregroundColor.currentUIColor()
187
+ } else {
188
+ // 背景色
189
+ cexTheme.backgroundColor = configuration.type.backgroundColor
190
+ // 文字颜色
191
+ titleLabel.cexTheme.textColor = configuration.type.foregroundColor
192
+ // 图标颜色
193
+ iconImageView.cexTheme.tintColor = configuration.type.foregroundColor
194
+ // 加载指示器颜色(使用前景色)
195
+ // loadingIndicator.color = configuration.type.foregroundColor.currentUIColor()
196
+ }
197
+ }
198
+
199
+ /// 更新按钮状态
200
+ private func updateState() {
201
+ if isLoading {
202
+ currentState = .loading
203
+ } else if !isEnabled {
204
+ currentState = .disable
205
+ } else {
206
+ // 如果当前不是 pressed 状态,则恢复为 normal
207
+ if currentState != .pressed {
208
+ currentState = .normal
209
+ }
210
+ }
211
+ }
212
+
213
+ // MARK: - Layout
214
+ private func updateLayout() {
215
+ let hasTitle = title != nil && !title!.isEmpty
216
+ let hasIcon = icon != nil
217
+ // 只有在非加载状态下才更新文字和图标的显示状态
218
+ // 加载状态下的显示由 updateStateAppearance() 控制
219
+ if currentState != .loading {
220
+ titleLabel.isHidden = !hasTitle
221
+ iconImageView.isHidden = !hasIcon
222
+ }
223
+ // 移除旧约束
224
+ iconLeadingConstraint?.deactivate()
225
+ iconTrailingConstraint?.deactivate()
226
+ titleLeadingConstraint?.deactivate()
227
+ titleTrailingConstraint?.deactivate()
228
+ iconLeadingConstraint = nil
229
+ iconTrailingConstraint = nil
230
+ titleLeadingConstraint = nil
231
+ titleTrailingConstraint = nil
232
+
233
+ if hasIcon && hasTitle {
234
+ // 有图标和文字
235
+ let iconSize = configuration.size.iconSize
236
+ let spacing = configuration.size.iconSpacing
237
+ let padding = configuration.size.horizontalPadding
238
+ if configuration.iconPosition == .leading {
239
+ // 图标在左侧
240
+ iconImageView.snp.remakeConstraints { make in
241
+ iconLeadingConstraint = make.leading.equalToSuperview().offset(padding).constraint
242
+ make.centerY.equalToSuperview()
243
+ make.size.equalTo(iconSize)
244
+ }
245
+ titleLabel.snp.remakeConstraints { make in
246
+ titleLeadingConstraint = make.leading.equalTo(iconImageView.snp.trailing).offset(spacing).constraint
247
+ titleTrailingConstraint = make.trailing.equalToSuperview().offset(-padding).constraint
248
+ make.centerY.equalToSuperview()
249
+ }
250
+ } else {
251
+ // 图标在右侧
252
+ titleLabel.snp.remakeConstraints { make in
253
+ titleLeadingConstraint = make.leading.equalToSuperview().offset(padding).constraint
254
+ make.centerY.equalToSuperview()
255
+ }
256
+ iconImageView.snp.remakeConstraints { make in
257
+ iconTrailingConstraint = make.trailing.equalToSuperview().offset(-padding).constraint
258
+ make.leading.equalTo(titleLabel.snp.trailing).offset(spacing)
259
+ make.centerY.equalToSuperview()
260
+ make.size.equalTo(iconSize)
261
+ }
262
+ }
263
+ } else if hasIcon {
264
+ // 只有图标
265
+ let iconSize = configuration.size.iconSize
266
+ iconImageView.snp.remakeConstraints { make in
267
+ make.center.equalToSuperview()
268
+ make.size.equalTo(iconSize)
269
+ }
270
+ } else if hasTitle {
271
+ // 只有文字
272
+ let padding = configuration.size.horizontalPadding
273
+ titleLabel.snp.remakeConstraints { make in
274
+ titleLeadingConstraint = make.leading.equalToSuperview().offset(padding).constraint
275
+ titleTrailingConstraint = make.trailing.equalToSuperview().offset(-padding).constraint
276
+ make.centerY.equalToSuperview()
277
+ make.height.lessThanOrEqualToSuperview()
278
+ }
279
+ } else {
280
+ // 既没有文字也没有图标,设置最小宽度
281
+ snp.remakeConstraints { make in
282
+ make.width.greaterThanOrEqualTo(configuration.size.minWidth)
283
+ make.height.equalTo(configuration.size.height)
284
+ }
285
+ }
286
+ // 加载指示器
287
+ loadingIndicator.snp.remakeConstraints { make in
288
+ make.center.equalToSuperview()
289
+ }
290
+ }
291
+
292
+ /// 仅文字时用 frame 强制 titleLabel 占满宽度并居中,避免外部通过 frame 设置按钮大小时约束未正确更新导致文字不居中
293
+ public override func layoutSubviews() {
294
+ super.layoutSubviews()
295
+ let hasTitle = title != nil && !title!.isEmpty
296
+ let hasIcon = icon != nil
297
+ if hasTitle && !hasIcon && !isLoading && bounds.width > 0 && bounds.height > 0 {
298
+ let padding = configuration.size.horizontalPadding
299
+ titleLabel.textAlignment = .center
300
+ titleLabel.frame = CGRect(x: padding, y: 0, width: bounds.width - padding * 2, height: bounds.height)
301
+ }
302
+ }
303
+
304
+
305
+ // MARK: - Touch Events
306
+ @objc private func handleTouchDown() {
307
+ // 只有在启用且非加载状态下才响应点击
308
+ if isEnabled && !isLoading {
309
+ currentState = .pressed
310
+ }
311
+ }
312
+
313
+ @objc private func handleTouchUp() {
314
+ // 触摸结束时恢复状态
315
+ if isEnabled && !isLoading {
316
+ currentState = .normal
317
+ }
318
+ }
319
+
320
+ public override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
321
+ super.touchesBegan(touches, with: event)
322
+ handleTouchDown()
323
+ }
324
+
325
+ public override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
326
+ super.touchesMoved(touches, with: event)
327
+ // 如果触摸移出按钮范围,恢复正常状态
328
+ if let touch = touches.first {
329
+ let location = touch.location(in: self)
330
+ if !bounds.contains(location) {
331
+ handleTouchUp()
332
+ } else if isEnabled && !isLoading && currentState != .pressed {
333
+ handleTouchDown()
334
+ }
335
+ }
336
+ }
337
+
338
+ public override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
339
+ super.touchesEnded(touches, with: event)
340
+ handleTouchUp()
341
+ }
342
+
343
+ public override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
344
+ super.touchesCancelled(touches, with: event)
345
+ handleTouchUp()
346
+ }
347
+
348
+
349
+ // MARK: - Intrinsic Content Size
350
+
351
+ public override var intrinsicContentSize: CGSize {
352
+ let hasTitle = title != nil && !title!.isEmpty
353
+ let hasIcon = icon != nil
354
+ var width: CGFloat = 0
355
+ let height = configuration.size.height
356
+ let padding = configuration.size.horizontalPadding * 2
357
+ if hasTitle {
358
+ titleLabel.sizeToFit()
359
+ width += titleLabel.frame.width
360
+ }
361
+ if hasIcon {
362
+ width += configuration.size.iconSize
363
+ if hasTitle {
364
+ width += configuration.size.iconSpacing
365
+ }
366
+ }
367
+ width += padding
368
+ // 最小宽度
369
+ width = max(width, configuration.size.minWidth)
370
+ return CGSize(width: width, height: height)
371
+ }
372
+
373
+ }
@@ -0,0 +1,181 @@
1
+ //
2
+ // BMComponentButtonConfiguration.swift
3
+ // BMComponents
4
+ //
5
+ // Created by james on 2025/12/31.
6
+ //
7
+
8
+ import UIKit
9
+ import BMTheme
10
+
11
+ // MARK: - Button Style Type 类型
12
+ /// 按钮变体(颜色主题)
13
+ public enum BMComponentButtonType {
14
+ /// 主色按钮(实心)
15
+ case primary
16
+ /// 次要按钮(边框)
17
+ case secondary
18
+ /// 成功按钮(绿色)
19
+ case green
20
+ /// 危险按钮(红色)
21
+ case red
22
+ /// 禁用按钮(灰色)
23
+ case white
24
+
25
+ /// 控件的背景色
26
+ var backgroundColor: BMCexThemeColor {
27
+ switch self {
28
+ case .primary: return BMCexThemeColor.brandColor
29
+ case .secondary: return BMCexThemeColor.brandBgColor
30
+ case .green: return BMCexThemeColor.buyColor
31
+ case .red: return BMCexThemeColor.sellColor
32
+ case .white: return BMCexThemeColor.bgLine2Color
33
+ }
34
+ }
35
+
36
+ /// (文字和图标)颜色
37
+ var foregroundColor: BMCexThemeColor {
38
+ switch self {
39
+ case .primary: return BMCexThemeColor.btnTextColor
40
+ case .secondary: return BMCexThemeColor.brandColor
41
+ case .green: return BMCexThemeColor.btnTextColor
42
+ case .red: return BMCexThemeColor.btnTextColor
43
+ case .white: return BMCexThemeColor.primaryColor
44
+ }
45
+ }
46
+
47
+ }
48
+
49
+ // MARK: - Button Size
50
+ public enum BMComponentButtonSize {
51
+
52
+ case XLarge
53
+ case Large
54
+ case Medium
55
+ case Small
56
+ case XSmall
57
+ case XXSmall
58
+
59
+ var minWidth: CGFloat {
60
+ switch self {
61
+ case .XLarge: return 64
62
+ case .Large: return 48
63
+ case .Medium: return 40
64
+ case .Small: return 32
65
+ case .XSmall: return 24
66
+ case .XXSmall: return 20
67
+ }
68
+ }
69
+
70
+ var height: CGFloat {
71
+ switch self {
72
+ case .XLarge: return 56
73
+ case .Large: return 48
74
+ case .Medium: return 40
75
+ case .Small: return 36
76
+ case .XSmall: return 32
77
+ case .XXSmall: return 26
78
+ }
79
+ }
80
+
81
+ var horizontalPadding: CGFloat{
82
+ switch self {
83
+ case .XLarge: return 32
84
+ case .Large: return 24
85
+ case .Medium: return 20
86
+ case .Small: return 16
87
+ case .XSmall: return 12
88
+ case .XXSmall: return 10
89
+ }
90
+ }
91
+
92
+ /// 图标尺寸
93
+ var iconSize: CGFloat {
94
+ switch self {
95
+ case .XLarge: return 20
96
+ case .Large: return 20
97
+ case .Medium: return 16
98
+ case .Small: return 16
99
+ case .XSmall: return 14
100
+ case .XXSmall: return 14
101
+ }
102
+ }
103
+
104
+ /// 图标与文字间距
105
+ var iconSpacing: CGFloat {
106
+ switch self {
107
+ case .XLarge: return 8
108
+ case .Large: return 8
109
+ case .Medium: return 6
110
+ case .Small: return 6
111
+ case .XSmall: return 6
112
+ case .XXSmall: return 4
113
+ }
114
+ }
115
+
116
+ var sizeFont: UIFont {
117
+ switch self {
118
+ case .XLarge: return .H7Font
119
+ case .Large: return .S1Font
120
+ case .Medium: return .S4Font
121
+ case .Small: return .S4Font
122
+ case .XSmall: return .S4Font
123
+ case .XXSmall: return .S5Font
124
+ }
125
+ }
126
+
127
+ }
128
+
129
+ // MARK: - State
130
+ /// 按钮状态
131
+ public enum BMComponentButtonState {
132
+ case normal
133
+ case pressed
134
+ case disable
135
+ case loading
136
+ }
137
+
138
+ // MARK: - Icon Position
139
+ /// 图标位置
140
+ public enum BMComponentButtonIconPosition {
141
+ case leading // 左侧
142
+ case trailing // 右侧
143
+ case none // 无图标
144
+ }
145
+
146
+ // MARK: - Button Configuration
147
+ /// 组件化按钮配置
148
+ public struct BMComponentButtonConfiguration {
149
+
150
+ /// 按钮样式变体(颜色主题)
151
+ public let type: BMComponentButtonType
152
+
153
+ /// 按钮尺寸
154
+ public let size: BMComponentButtonSize
155
+
156
+ /// 图标位置
157
+ public let iconPosition: BMComponentButtonIconPosition
158
+
159
+ /// 点击按钮的透明度(0.0 - 1.0)
160
+ public var disableAlpha: CGFloat = 0.8
161
+
162
+ /// 圆角半径
163
+ public var cornerRadius: CGFloat?
164
+
165
+ /// 字体
166
+ public var currentFont: UIFont?
167
+
168
+ // MARK: - Initialization
169
+ public init(type: BMComponentButtonType = .primary,size: BMComponentButtonSize = .Medium,iconPosition: BMComponentButtonIconPosition = .leading,alpha: CGFloat = 1.0,cornerRadius: CGFloat? = nil) {
170
+ self.type = type
171
+ self.size = size
172
+ self.iconPosition = iconPosition
173
+ self.disableAlpha = alpha
174
+ self.cornerRadius = cornerRadius
175
+ }
176
+
177
+
178
+ }
179
+
180
+
181
+