@momo-kits/native-kits 0.162.30 → 0.162.31

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 (90) hide show
  1. package/CLAUDE.md +19 -0
  2. package/ios/Application/ApplicationEnvironment.swift +14 -6
  3. package/ios/Application/LanguageSource.swift +93 -0
  4. package/ios/Application/Localize.swift +73 -4
  5. package/ios/Application/LocalizeModifier.swift +30 -0
  6. package/ios/Application/Navigation/NavigationContainer.swift +28 -18
  7. package/ios/Application/Navigation/Navigator.swift +121 -53
  8. package/ios/Application/Navigation/NavigatorManager.swift +49 -0
  9. package/ios/Application/Navigation/component/NavigationFooter.swift +90 -0
  10. package/ios/Application/StackScreen.swift +19 -28
  11. package/ios/Badge/Badge.swift +2 -1
  12. package/ios/Badge/BadgeRibbon.swift +6 -2
  13. package/ios/Button/Button.swift +41 -61
  14. package/ios/Chip/Chip.swift +6 -5
  15. package/ios/Icon/Icon.swift +13 -4
  16. package/ios/Input/ErrorView.swift +2 -1
  17. package/ios/Input/Input.swift +9 -2
  18. package/ios/Input/InputPhoneNumber.swift +2 -1
  19. package/ios/Input/InputSearch.swift +3 -2
  20. package/ios/Input/InputTextArea.swift +6 -1
  21. package/ios/InputMoney/InputMoney.swift +5 -2
  22. package/ios/InputOTP/InputOTP.swift +2 -1
  23. package/ios/Layout/GridContext.swift +30 -0
  24. package/ios/Layout/Item.swift +42 -0
  25. package/ios/Layout/Section.swift +134 -0
  26. package/ios/Popup/PopupNotify.swift +2 -1
  27. package/ios/ProgressInfo/ProgressInfo.swift +4 -3
  28. package/ios/Rating/Rating.swift +2 -1
  29. package/ios/Stepper/Stepper.swift +2 -1
  30. package/ios/SuggestAction/SuggestAction.swift +3 -2
  31. package/ios/Tag/Tag.swift +2 -1
  32. package/ios/Template/TrustBanner/TrustBanner.swift +4 -2
  33. package/ios/Title/Title.swift +3 -2
  34. package/ios/Tooltip/Tooltip.swift +2 -1
  35. package/ios/Typography/FontScale.swift +103 -0
  36. package/ios/Typography/FontScaleStore.swift +52 -13
  37. package/ios/Typography/Text.swift +12 -29
  38. package/ios/Typography/Typography.swift +66 -86
  39. package/ios/native-kits.podspec +2 -1
  40. package/ios-demo/MoMoUIKitsDemo.podspec +1 -1
  41. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/AvatarDemo.swift +87 -73
  42. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/BadgeDemo.swift +55 -249
  43. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ButtonDemo.swift +90 -82
  44. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CarouselDemo.swift +74 -104
  45. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CheckboxDemo.swift +47 -50
  46. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ChipDemo.swift +63 -7
  47. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CollapseDemo.swift +90 -81
  48. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DateTimePickerDemo.swift +73 -97
  49. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DemoScreen.swift +420 -0
  50. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DividerDemo.swift +46 -4
  51. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/Home.swift +54 -57
  52. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/IconButtonDemo.swift +80 -45
  53. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/IconDemo.swift +81 -0
  54. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ImageDemo.swift +49 -62
  55. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InformationDemo.swift +66 -116
  56. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputDemo.swift +104 -98
  57. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputDropDownDemo.swift +76 -118
  58. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputMoneyDemo.swift +73 -86
  59. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputOTPDemo.swift +69 -54
  60. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputPhoneNumberDemo.swift +74 -54
  61. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputSearchDemo.swift +73 -20
  62. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputTextAreaDemo.swift +68 -38
  63. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/LoaderDemo.swift +55 -52
  64. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/LocalizeDemo.swift +51 -4
  65. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/NavigationDemo.swift +14 -2
  66. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PaginationDemo.swift +47 -51
  67. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PlaygroundData.swift +76 -0
  68. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupNotifyDemo.swift +67 -93
  69. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupPromotionDemo.swift +34 -43
  70. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ProgressInfoDemo.swift +83 -71
  71. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/RadioDemo.swift +55 -23
  72. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/RatingDemo.swift +58 -46
  73. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SectionDemo.swift +83 -0
  74. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SkeletonDemo.swift +41 -12
  75. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SliderDemo.swift +67 -127
  76. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SnackBarDemo.swift +54 -110
  77. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/StepperDemo.swift +69 -79
  78. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/StepsDemo.swift +78 -58
  79. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SuggestActionDemo.swift +76 -65
  80. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SwipeDemo.swift +82 -127
  81. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SwitchDemo.swift +44 -19
  82. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TabViewDemo.swift +92 -168
  83. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TagDemo.swift +79 -27
  84. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ThemeDemo.swift +0 -1
  85. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TitleDemo.swift +87 -92
  86. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TooltipDemo.swift +94 -163
  87. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TypographyDemo.swift +70 -70
  88. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/UploaderDemo.swift +68 -114
  89. package/ios-demo/Sources/MoMoUIKitsDemo/SwiftUIDemoBrowserView.swift +3 -1
  90. package/package.json +2 -2
@@ -1,33 +1,15 @@
1
1
  import SwiftUI
2
2
 
3
- public func scaleSize(_ size: CGFloat, _ scaleRate: CGFloat? = nil) -> CGFloat {
4
- if !FontScaleStore.shared.useOSScaleRate {
5
- return size * FontScaleStore.shared.userScaleRate
6
- }
7
- let defaultScreenSize: CGFloat = 375
8
- let maxFontScale: CGFloat = scaleRate ?? 1.5
9
- let maxDeviceScale: CGFloat = 5
10
-
11
- let deviceWidth = UIScreen.main.bounds.width
12
- let deviceScale = deviceWidth / defaultScreenSize
13
-
14
- let defaultFont = UIFont.systemFont(ofSize: UIFont.labelFontSize)
15
- let scaledFont = UIFontMetrics.default.scaledFont(for: defaultFont)
16
- let fontScale = scaledFont.pointSize / defaultFont.pointSize
17
-
18
- var fontSizeDeviceScale = size
19
- var fontSizeOSScale = size
20
-
21
-
22
- if deviceScale > 1 {
23
- fontSizeDeviceScale = min(fontSizeDeviceScale * deviceScale, fontSizeDeviceScale + maxDeviceScale)
24
- }
25
-
26
- if fontScale > 1 {
27
- fontSizeOSScale = min(fontSizeOSScale * fontScale, fontSizeOSScale * maxFontScale)
28
- }
3
+ /// Non-reactive entry point, for call sites that cannot read the environment (UIKit bridges,
4
+ /// free functions). Inside a SwiftUI view prefer `@Environment(\.momoFontScale)` so the view
5
+ /// re-renders when Dynamic Type or the host preference changes.
6
+ public func scaleSize(_ size: CGFloat) -> CGFloat {
7
+ FontScaleStore.shared.snapshot(size)
8
+ }
29
9
 
30
- return max(fontSizeDeviceScale, fontSizeOSScale)
10
+ @available(*, deprecated, message: "Max font scale is fixed at 1.5; this override is no longer applied. Drop the second argument.")
11
+ public func scaleSize(_ size: CGFloat, _ scaleRate: CGFloat?) -> CGFloat {
12
+ scaleSize(size)
31
13
  }
32
14
 
33
15
  public enum TypographyStyle {
@@ -119,6 +101,7 @@ public extension TypographyStyle {
119
101
 
120
102
  public struct MomoText: View {
121
103
  @Environment(\.appTheme) private var theme
104
+ @Environment(\.momoFontScale) private var fontScale
122
105
  private let content: String
123
106
  private let typography: TypographyStyle
124
107
  private let color: Color?
@@ -176,7 +159,7 @@ public struct MomoText: View {
176
159
  let isStrikethrough = strikethrough || typography.isStrikethrough
177
160
 
178
161
  var text = SwiftUI.Text(content)
179
- .font(.system(size: scaleSize(typography.fontSize), weight: typography.fontWeight))
162
+ .font(fontScale.font(size: typography.fontSize, weight: typography.fontWeight))
180
163
  .foregroundColor(textColor)
181
164
  .strikethrough(isStrikethrough, color: textColor)
182
165
  .underline(underline, color: textColor)
@@ -187,7 +170,7 @@ public struct MomoText: View {
187
170
 
188
171
  return text
189
172
  .multilineTextAlignment(textAlign)
190
- .lineSpacing(scaleSize(typography.lineHeight) - scaleSize(typography.fontSize))
173
+ .lineSpacing(fontScale(typography.lineHeight) - fontScale(typography.fontSize))
191
174
  .modifier(LineLimitModifier(range: lineLimitRange))
192
175
  .truncationMode(truncationMode)
193
176
  .fixedSize(horizontal: !softWrap, vertical: false)
@@ -10,98 +10,78 @@ public extension View {
10
10
  }
11
11
 
12
12
  public extension Font {
13
+ /// Non-reactive: resolves against the current `FontScaleStore` snapshot at each access. The
14
+ /// styles below are computed properties for that reason — as stored `static let`s they would
15
+ /// freeze the scale at first access and never pick up a Dynamic Type change.
16
+ /// Inside a view, `@Environment(\.momoFontScale)` + `fontScale.font(size:weight:)` also re-renders.
13
17
  static func appFont(size: CGFloat) -> Font {
14
- if !FontScaleStore.shared.useOSScaleRate {
15
- return Font.system(size: size * FontScaleStore.shared.userScaleRate)
16
- }
17
- let defaultScreenSize: CGFloat = 375
18
- let maxFontScale: CGFloat = 1.5
19
- let maxDeviceScale: CGFloat = 5
20
-
21
- let deviceWidth = UIScreen.main.bounds.width
22
- let deviceScale = deviceWidth / defaultScreenSize
23
-
24
- let defaultFont = UIFont.systemFont(ofSize: UIFont.labelFontSize)
25
- let scaledFont = UIFontMetrics.default.scaledFont(for: defaultFont)
26
- let fontScale = scaledFont.pointSize / defaultFont.pointSize
27
-
28
- var fontSize = size
29
-
30
- if deviceScale > 1 {
31
- fontSize = min(fontSize * deviceScale, fontSize + maxDeviceScale)
32
- }
33
-
34
- if fontScale > 1 {
35
- fontSize = min(fontSize * fontScale, fontSize * maxFontScale)
36
- }
37
-
38
- return Font.system(size: fontSize)
18
+ FontScaleStore.shared.snapshot.font(size: size)
39
19
  }
40
20
 
41
21
  // New supported typography styles
42
- static let headline_default_bold = appFont(size: 24).weight(.bold)
43
- static let header_m_bold = appFont(size: 18).weight(.bold)
44
- static let header_default_bold = appFont(size: 16).weight(.bold)
45
- static let header_default_semibold = appFont(size: 16).weight(.semibold)
46
- static let header_s_semibold = appFont(size: 14).weight(.semibold)
47
- static let header_xs_semibold = appFont(size: 12).weight(.semibold)
48
- static let body_default_regular = appFont(size: 14).weight(.regular)
49
- static let description_default_regular = appFont(size: 12).weight(.regular)
50
- static let description_xs_regular = appFont(size: 10).weight(.regular)
51
- static let label_default_medium = appFont(size: 14).weight(.medium)
52
- static let label_s_medium = appFont(size: 12).weight(.medium)
53
- static let label_xs_medium = appFont(size: 10).weight(.medium)
54
- static let action_default_bold = appFont(size: 16).weight(.bold)
55
- static let action_s_bold = appFont(size: 14).weight(.bold)
56
- static let action_xs_bold = appFont(size: 12).weight(.bold)
57
- static let action_xxs_bold = appFont(size: 10).weight(.bold)
22
+ static var headline_default_bold: Font { appFont(size: 24).weight(.bold) }
23
+ static var header_m_bold: Font { appFont(size: 18).weight(.bold) }
24
+ static var header_default_bold: Font { appFont(size: 16).weight(.bold) }
25
+ static var header_default_semibold: Font { appFont(size: 16).weight(.semibold) }
26
+ static var header_s_semibold: Font { appFont(size: 14).weight(.semibold) }
27
+ static var header_xs_semibold: Font { appFont(size: 12).weight(.semibold) }
28
+ static var body_default_regular: Font { appFont(size: 14).weight(.regular) }
29
+ static var description_default_regular: Font { appFont(size: 12).weight(.regular) }
30
+ static var description_xs_regular: Font { appFont(size: 10).weight(.regular) }
31
+ static var label_default_medium: Font { appFont(size: 14).weight(.medium) }
32
+ static var label_s_medium: Font { appFont(size: 12).weight(.medium) }
33
+ static var label_xs_medium: Font { appFont(size: 10).weight(.medium) }
34
+ static var action_default_bold: Font { appFont(size: 16).weight(.bold) }
35
+ static var action_s_bold: Font { appFont(size: 14).weight(.bold) }
36
+ static var action_xs_bold: Font { appFont(size: 12).weight(.bold) }
37
+ static var action_xxs_bold: Font { appFont(size: 10).weight(.bold) }
58
38
 
59
39
  // Legacy styles
60
- static let headline_default = appFont(size: 28).weight(.semibold)
61
- static let headline_s = appFont(size: 24).weight(.semibold)
62
- static let headline_l = appFont(size: 32).weight(.semibold)
63
- static let headline_xl = appFont(size: 36).weight(.semibold)
64
- static let title_default = appFont(size: 20).weight(.bold)
65
- static let title_xs = appFont(size: 16).weight(.bold)
66
- static let title_s = appFont(size: 18).weight(.bold)
67
- static let header_default = appFont(size: 15).weight(.semibold)
68
- static let header_xs = appFont(size: 12).weight(.semibold)
69
- static let header_s = appFont(size: 14).weight(.semibold)
70
- static let paragraph_default = appFont(size: 15).weight(.regular)
71
- static let paragraph_bold = appFont(size: 15).weight(.bold)
72
- static let description_default = appFont(size: 14).weight(.regular)
73
- static let description_xs = appFont(size: 10).weight(.regular)
74
- static let description_s = appFont(size: 12).weight(.regular)
75
- static let label_default = appFont(size: 14).weight(.medium)
76
- static let label_xxs = appFont(size: 8).weight(.medium)
77
- static let label_xs = appFont(size: 10).weight(.medium)
78
- static let label_s = appFont(size: 12).weight(.medium)
79
- static let action_default = appFont(size: 16).weight(.bold)
80
- static let action_xxs = appFont(size: 10).weight(.bold)
81
- static let action_xs = appFont(size: 12).weight(.bold)
82
- static let action_s = appFont(size: 15).weight(.bold)
40
+ static var headline_default: Font { appFont(size: 28).weight(.semibold) }
41
+ static var headline_s: Font { appFont(size: 24).weight(.semibold) }
42
+ static var headline_l: Font { appFont(size: 32).weight(.semibold) }
43
+ static var headline_xl: Font { appFont(size: 36).weight(.semibold) }
44
+ static var title_default: Font { appFont(size: 20).weight(.bold) }
45
+ static var title_xs: Font { appFont(size: 16).weight(.bold) }
46
+ static var title_s: Font { appFont(size: 18).weight(.bold) }
47
+ static var header_default: Font { appFont(size: 15).weight(.semibold) }
48
+ static var header_xs: Font { appFont(size: 12).weight(.semibold) }
49
+ static var header_s: Font { appFont(size: 14).weight(.semibold) }
50
+ static var paragraph_default: Font { appFont(size: 15).weight(.regular) }
51
+ static var paragraph_bold: Font { appFont(size: 15).weight(.bold) }
52
+ static var description_default: Font { appFont(size: 14).weight(.regular) }
53
+ static var description_xs: Font { appFont(size: 10).weight(.regular) }
54
+ static var description_s: Font { appFont(size: 12).weight(.regular) }
55
+ static var label_default: Font { appFont(size: 14).weight(.medium) }
56
+ static var label_xxs: Font { appFont(size: 8).weight(.medium) }
57
+ static var label_xs: Font { appFont(size: 10).weight(.medium) }
58
+ static var label_s: Font { appFont(size: 12).weight(.medium) }
59
+ static var action_default: Font { appFont(size: 16).weight(.bold) }
60
+ static var action_xxs: Font { appFont(size: 10).weight(.bold) }
61
+ static var action_xs: Font { appFont(size: 12).weight(.bold) }
62
+ static var action_s: Font { appFont(size: 15).weight(.bold) }
83
63
 
84
64
  // deprecated
85
- static let h1 = appFont(size: 36).weight(.semibold) //headline_xl
86
- static let h2 = appFont(size: 32).weight(.semibold)
87
- static let h3 = appFont(size: 28).weight(.semibold)
88
- static let h4 = appFont(size: 24).weight(.semibold)
89
- static let title1 = appFont(size: 20).weight(.bold)
90
- static let title2 = appFont(size: 18).weight(.bold)
91
- static let title3 = appFont(size: 16).weight(.bold)
92
- static let headerText1 = appFont(size: 15).weight(.semibold) //header_default
93
- static let headerText2 = appFont(size: 14).weight(.semibold)
94
- static let headerText3 = appFont(size: 12).weight(.semibold)
95
- static let paragraph = appFont(size: 15).weight(.regular) //paragraph_default
96
- static let description1 = appFont(size: 14).weight(.regular)
97
- static let description2 = appFont(size: 12).weight(.regular)
98
- static let description3 = appFont(size: 10).weight(.regular)
99
- static let label1 = appFont(size: 14).weight(.medium)
100
- static let label2 = appFont(size: 12).weight(.medium)
101
- static let label3 = appFont(size: 10).weight(.medium)
102
- static let label4 = appFont(size: 8).weight(.medium)
103
- static let action1 = appFont(size: 16).weight(.bold)
104
- static let action2 = appFont(size: 15).weight(.bold)
105
- static let action3 = appFont(size: 12).weight(.bold)
106
- static let action4 = appFont(size: 10).weight(.bold)
65
+ static var h1: Font { appFont(size: 36).weight(.semibold) } //headline_xl
66
+ static var h2: Font { appFont(size: 32).weight(.semibold) }
67
+ static var h3: Font { appFont(size: 28).weight(.semibold) }
68
+ static var h4: Font { appFont(size: 24).weight(.semibold) }
69
+ static var title1: Font { appFont(size: 20).weight(.bold) }
70
+ static var title2: Font { appFont(size: 18).weight(.bold) }
71
+ static var title3: Font { appFont(size: 16).weight(.bold) }
72
+ static var headerText1: Font { appFont(size: 15).weight(.semibold) } //header_default
73
+ static var headerText2: Font { appFont(size: 14).weight(.semibold) }
74
+ static var headerText3: Font { appFont(size: 12).weight(.semibold) }
75
+ static var paragraph: Font { appFont(size: 15).weight(.regular) } //paragraph_default
76
+ static var description1: Font { appFont(size: 14).weight(.regular) }
77
+ static var description2: Font { appFont(size: 12).weight(.regular) }
78
+ static var description3: Font { appFont(size: 10).weight(.regular) }
79
+ static var label1: Font { appFont(size: 14).weight(.medium) }
80
+ static var label2: Font { appFont(size: 12).weight(.medium) }
81
+ static var label3: Font { appFont(size: 10).weight(.medium) }
82
+ static var label4: Font { appFont(size: 8).weight(.medium) }
83
+ static var action1: Font { appFont(size: 16).weight(.bold) }
84
+ static var action2: Font { appFont(size: 15).weight(.bold) }
85
+ static var action3: Font { appFont(size: 12).weight(.bold) }
86
+ static var action4: Font { appFont(size: 10).weight(.bold) }
107
87
  }
@@ -1,6 +1,6 @@
1
1
  Pod::Spec.new do |s|
2
2
  s.name = 'MoMoUIKits'
3
- s.version = '0.163.1-beta.16'
3
+ s.version = '0.163.1-sp.10'
4
4
  s.summary = 'MoMoUIKits for iOS'
5
5
  s.homepage = 'https://momo.vn'
6
6
  s.license = { :type => 'MIT' }
@@ -34,6 +34,7 @@ Pod::Spec.new do |s|
34
34
  "InputDropDown/**/*.{swift,m,h}",
35
35
  "InputMoney/**/*.{swift,m,h}",
36
36
  "InputOTP/**/*.{swift,m,h}",
37
+ "Layout/**/*.{swift,m,h}",
37
38
  "Loader/**/*.{swift,m,h}",
38
39
  "Lottie/**/*.{swift,m,h}",
39
40
  "OTPKeyboard/**/*.{swift,m,h}",
@@ -1,6 +1,6 @@
1
1
  Pod::Spec.new do |s|
2
2
  s.name = 'MoMoUIKitsDemo'
3
- s.version = '0.163.1-beta.16'
3
+ s.version = '0.163.1-sp.10'
4
4
  s.summary = 'Demo browser for MoMoUIKits SwiftUI components'
5
5
  s.homepage = 'https://momo.vn'
6
6
  s.license = { :type => 'MIT' }
@@ -2,92 +2,106 @@
2
2
  // AvatarDemo.swift
3
3
  // Example
4
4
  //
5
+ // Preview + Playground demo (DemoScreen pattern).
6
+ //
5
7
 
6
- import SwiftUI
7
8
  import MoMoUIKits
9
+ import SwiftUI
8
10
 
9
11
  struct AvatarDemo: View {
10
12
  var body: some View {
11
- ScrollView {
12
- VStack(alignment: .leading, spacing: 24) {
13
- section("Size") {
14
- HStack(alignment: .center, spacing: 8) {
15
- Avatar(size: .size24)
16
- Avatar(size: .size32)
17
- Avatar(size: .size40)
18
- Avatar(size: .size48)
19
- Avatar(size: .size56)
20
- Avatar(size: .size72)
21
- }
22
- }
23
-
24
- section("Rounded vs Square") {
25
- HStack(spacing: 8) {
26
- Avatar(size: .size40, rounded: true)
27
- Avatar(size: .size40, rounded: false)
28
- }
29
- }
30
-
31
- section("Name (initials)") {
32
- HStack(spacing: 8) {
33
- Avatar(size: .size40, name: "Nguyen Van A")
34
- Avatar(size: .size40, name: "John Doe")
35
- Avatar(size: .size40, name: "MoMo")
36
- }
37
- }
38
-
39
- section("Image") {
40
- HStack(spacing: 8) {
41
- Avatar(size: .size40, image: "https://i.pravatar.cc/150?img=1")
42
- Avatar(size: .size56, image: "https://i.pravatar.cc/150?img=2")
43
- Avatar(size: .size72, image: "https://i.pravatar.cc/150?img=3")
44
- }
45
- }
13
+ if #available(iOS 16.0, *) {
14
+ DemoScreen(preview: { AvatarPreview() }, playground: { AvatarPlayground() })
15
+ } else {
16
+ Text("Requires iOS 16+").padding()
17
+ }
18
+ }
19
+ }
46
20
 
47
- section("showIconMomo") {
48
- HStack(spacing: 8) {
49
- Avatar(size: .size40, showIconMomo: true)
50
- Avatar(size: .size48, name: "MN", showIconMomo: true)
51
- Avatar(size: .size56, image: "https://i.pravatar.cc/150?img=4", showIconMomo: true)
52
- }
53
- }
21
+ private let avatarSizeList: [PlaygroundOption] = [
22
+ .init("size24", AvatarSize.size24),
23
+ .init("size32", AvatarSize.size32),
24
+ .init("size40", AvatarSize.size40),
25
+ .init("size48", AvatarSize.size48),
26
+ .init("size56", AvatarSize.size56),
27
+ .init("size72", AvatarSize.size72),
28
+ ]
54
29
 
55
- section("showIconSupport") {
56
- HStack(spacing: 8) {
57
- Avatar(size: .size40, showIconSupport: true)
58
- Avatar(size: .size48, name: "CS", showIconSupport: true)
59
- Avatar(size: .size56, image: "https://i.pravatar.cc/150?img=5", showIconSupport: true)
60
- }
61
- }
30
+ // MARK: - Preview
62
31
 
63
- section("showIconMomo + showIconSupport") {
64
- HStack(spacing: 8) {
65
- Avatar(size: .size56, showIconMomo: true, showIconSupport: true)
66
- Avatar(size: .size72, name: "NV", showIconMomo: true, showIconSupport: true)
67
- }
68
- }
32
+ private struct AvatarPreview: View {
33
+ var body: some View {
34
+ BasicColumnBox("Size", alignment: .leading) {
35
+ HStack(alignment: .center, spacing: Spacing.S) {
36
+ Avatar(size: .size24)
37
+ Avatar(size: .size32)
38
+ Avatar(size: .size40)
39
+ Avatar(size: .size48)
40
+ Avatar(size: .size56)
41
+ Avatar(size: .size72)
69
42
  }
70
- .padding()
71
43
  }
72
- }
73
-
74
- @ViewBuilder
75
- private func section<Content: View>(
76
- _ title: String,
77
- @ViewBuilder content: () -> Content
78
- ) -> some View {
79
- VStack(alignment: .leading, spacing: 8) {
80
- Text(title)
81
- .font(.headline)
82
- content()
44
+ BasicColumnBox("Rounded vs Square", alignment: .leading) {
45
+ HStack(spacing: Spacing.S) {
46
+ Avatar(size: .size40, rounded: true)
47
+ Avatar(size: .size40, rounded: false)
48
+ }
49
+ }
50
+ BasicColumnBox("Name (initials)", alignment: .leading) {
51
+ HStack(spacing: Spacing.S) {
52
+ Avatar(size: .size40, name: "Nguyen Van A")
53
+ Avatar(size: .size40, name: "John Doe")
54
+ Avatar(size: .size40, name: "MoMo")
55
+ }
56
+ }
57
+ BasicColumnBox("Image", alignment: .leading) {
58
+ HStack(spacing: Spacing.S) {
59
+ Avatar(size: .size40, image: "https://i.pravatar.cc/150?img=1")
60
+ Avatar(size: .size56, image: "https://i.pravatar.cc/150?img=2")
61
+ Avatar(size: .size72, image: "https://i.pravatar.cc/150?img=3")
62
+ }
63
+ }
64
+ BasicColumnBox("Icon badges", alignment: .leading) {
65
+ HStack(spacing: Spacing.S) {
66
+ Avatar(size: .size48, showIconMomo: true)
67
+ Avatar(size: .size48, showIconSupport: true)
68
+ Avatar(size: .size56, showIconMomo: true, showIconSupport: true)
69
+ }
83
70
  }
84
71
  }
85
72
  }
86
73
 
87
- #if DEBUG
88
- struct AvatarDemo_Previews: PreviewProvider {
89
- static var previews: some View {
90
- AvatarDemo()
74
+ // MARK: - Playground
75
+
76
+ @available(iOS 16.0, *)
77
+ private struct AvatarPlayground: View {
78
+ @State private var size: PlaygroundOption? = avatarSizeList.first { $0.key == "size48" }
79
+ @State private var rounded = true
80
+ @State private var name = ""
81
+ @State private var image = ""
82
+ @State private var showIconMomo = false
83
+ @State private var showIconSupport = false
84
+
85
+ var body: some View {
86
+ PlaygroundTab(
87
+ component: {
88
+ Avatar(
89
+ size: (size?.value as? AvatarSize) ?? .size48,
90
+ rounded: rounded,
91
+ name: name.isEmpty ? nil : name,
92
+ image: image.isEmpty ? nil : image,
93
+ showIconMomo: showIconMomo,
94
+ showIconSupport: showIconSupport
95
+ )
96
+ },
97
+ params: [
98
+ .radio("Size", avatarSizeList, $size),
99
+ .checkbox("Rounded", $rounded),
100
+ .string("Name", $name),
101
+ .string("Image", $image),
102
+ .checkbox("ShowIconMomo", $showIconMomo),
103
+ .checkbox("ShowIconSupport", $showIconSupport),
104
+ ]
105
+ )
91
106
  }
92
107
  }
93
- #endif