@momo-kits/native-kits 0.163.1-sp.8-debug → 0.163.1-sp.9-debug

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 (55) hide show
  1. package/.claude/settings.local.json +3 -1
  2. package/compose/build.gradle.kts +1 -1
  3. package/compose/compose.podspec +1 -1
  4. package/gradle.properties +1 -1
  5. package/ios/Application/Navigation/Navigator.swift +30 -24
  6. package/ios/Application/StackScreen.swift +1 -7
  7. package/ios/native-kits.podspec +1 -1
  8. package/ios-demo/MoMoUIKitsDemo.podspec +1 -1
  9. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/AvatarDemo.swift +87 -73
  10. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/BadgeDemo.swift +55 -249
  11. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ButtonDemo.swift +89 -82
  12. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CarouselDemo.swift +74 -104
  13. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CheckboxDemo.swift +47 -51
  14. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ChipDemo.swift +63 -7
  15. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CollapseDemo.swift +90 -81
  16. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DateTimePickerDemo.swift +73 -97
  17. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DemoScreen.swift +418 -0
  18. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DividerDemo.swift +46 -4
  19. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/Home.swift +51 -47
  20. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/IconButtonDemo.swift +80 -45
  21. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/IconDemo.swift +67 -90
  22. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ImageDemo.swift +49 -62
  23. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InformationDemo.swift +66 -116
  24. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputDemo.swift +104 -98
  25. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputDropDownDemo.swift +76 -118
  26. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputMoneyDemo.swift +73 -86
  27. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputOTPDemo.swift +69 -54
  28. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputPhoneNumberDemo.swift +74 -54
  29. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputSearchDemo.swift +73 -20
  30. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputTextAreaDemo.swift +68 -38
  31. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/LoaderDemo.swift +55 -52
  32. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PaginationDemo.swift +47 -51
  33. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PlaygroundData.swift +76 -0
  34. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupNotifyDemo.swift +67 -93
  35. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupPromotionDemo.swift +34 -43
  36. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ProgressInfoDemo.swift +83 -71
  37. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/RadioDemo.swift +55 -23
  38. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/RatingDemo.swift +58 -46
  39. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SectionDemo.swift +57 -74
  40. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SkeletonDemo.swift +41 -12
  41. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SliderDemo.swift +67 -127
  42. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SnackBarDemo.swift +54 -110
  43. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/StepperDemo.swift +69 -79
  44. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/StepsDemo.swift +78 -58
  45. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SuggestActionDemo.swift +76 -65
  46. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SwipeDemo.swift +82 -127
  47. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SwitchDemo.swift +44 -19
  48. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TabViewDemo.swift +92 -168
  49. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TagDemo.swift +79 -27
  50. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ThemeDemo.swift +0 -1
  51. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TitleDemo.swift +87 -92
  52. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TooltipDemo.swift +94 -163
  53. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TypographyDemo.swift +70 -70
  54. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/UploaderDemo.swift +68 -114
  55. package/package.json +2 -2
@@ -152,7 +152,9 @@
152
152
  "Bash(cp /Users/sophia/Workspace/momo/momo-native-kits/ios/Application/Navigation/Navigator.swift /Users/sophia/Workspace/momo/momo-app/node_modules/@momo-kits/native-kits/ios/Application/Navigation/Navigator.swift)",
153
153
  "Bash(cp /Users/sophia/Workspace/momo/momo-native-kits/ios/Application/Navigation/NavigatorManager.swift /Users/sophia/Workspace/momo/momo-app/node_modules/@momo-kits/native-kits/ios/Application/Navigation/NavigatorManager.swift)",
154
154
  "Bash(cp /Users/sophia/Workspace/momo/momo-native-kits/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/NavigationDemo.swift /Users/sophia/Workspace/momo/momo-app/node_modules/@momo-kits/native-kits/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/NavigationDemo.swift)",
155
- "Bash(xcodebuild -workspace MoMoPlatform.xcworkspace -scheme MoMoPlatform-UAT -sdk iphonesimulator -destination 'generic/platform=iOS Simulator' -derivedDataPath build/DerivedData build)"
155
+ "Bash(xcodebuild -workspace MoMoPlatform.xcworkspace -scheme MoMoPlatform-UAT -sdk iphonesimulator -destination 'generic/platform=iOS Simulator' -derivedDataPath build/DerivedData build)",
156
+ "Bash(cp android/gradle/libs.versions.toml /private/tmp/claude-503/-Users-sophia-Workspace-momo-momo-native-kits/bfb380ee-54cb-4a02-bc5f-d152cce4b727/scratchpad/libs.versions.toml.bak)",
157
+ "Bash(sed -i '' 's/kits = \"0.163.1-sp.8\"/kits = \"0.163.1-sp.7\"/' android/gradle/libs.versions.toml)"
156
158
  ]
157
159
  }
158
160
  }
@@ -40,7 +40,7 @@ kotlin {
40
40
  }
41
41
 
42
42
  cocoapods {
43
- version = "0.163.1-sp.8-debug"
43
+ version = "0.163.1-sp.9-debug"
44
44
  summary = "IOS Shared module"
45
45
  homepage = "https://momo.vn"
46
46
  ios.deploymentTarget = "15.0"
@@ -1,6 +1,6 @@
1
1
  Pod::Spec.new do |spec|
2
2
  spec.name = 'compose'
3
- spec.version = '0.163.1-sp.8-debug'
3
+ spec.version = '0.163.1-sp.9-debug'
4
4
  spec.homepage = 'https://momo.vn'
5
5
  spec.source = { :http=> ''}
6
6
  spec.authors = ''
package/gradle.properties CHANGED
@@ -18,7 +18,7 @@ kotlin.apple.xcodeCompatibility.nowarn=true
18
18
  name="ComposeKits"
19
19
  group=vn.momo.kits
20
20
  artifact.id=kits
21
- version=0.163.1-sp.8
21
+ version=0.163.1-sp.9
22
22
 
23
23
  repo=GitLab
24
24
  url=https://gitlab.mservice.com.vn/api/v4/projects/5400/packages/maven
@@ -273,30 +273,36 @@ public final class Navigator: ObservableObject {
273
273
  }
274
274
  if let preventBack = latest?.options?.headerBackProps?.preventBack {
275
275
  showModal {
276
- PopupDisplay(
277
- isPresented: .constant(true),
278
- title: preventBack.title,
279
- description: preventBack.description,
280
- url: preventBack.image,
281
- buttonDirection: preventBack.buttonDirection,
282
- buttonType: .button,
283
- actionButtonTitle: preventBack.primary?.title ?? "",
284
- closeButtonTitle: preventBack.secondary?.title ?? "",
285
- onPressAction: { [weak self] in
286
- preventBack.primary?.onPress?()
287
- // Close the popup, then leave the screen (matches Compose's pop(2)).
288
- self?.pop(2, callback: callback)
289
- },
290
- onPressCloseButton: { [weak self] in
291
- preventBack.secondary?.onPress?()
292
- self?.hideOverplay()
293
- },
294
- onClose: { [weak self] in
295
- preventBack.onIconClose?()
296
- self?.hideOverplay()
297
- },
298
- errorCode: preventBack.error,
299
- isShowCloseIcon: preventBack.isShowCloseIcon
276
+ PopupNotify(
277
+ props: PopupNotifyProps(
278
+ image: preventBack.image,
279
+ title: preventBack.title,
280
+ description: preventBack.description,
281
+ error: preventBack.error,
282
+ primary: PopupAction(
283
+ title: preventBack.primary?.title ?? "",
284
+ onPress: { [weak self] in
285
+ preventBack.primary?.onPress?()
286
+ // Close the popup, then leave the screen (matches Compose's pop(2)).
287
+ self?.pop(2, callback: callback)
288
+ }
289
+ ),
290
+ secondary: preventBack.secondary.map { secondary in
291
+ PopupAction(
292
+ title: secondary.title,
293
+ onPress: { [weak self] in
294
+ secondary.onPress?()
295
+ self?.hideOverplay()
296
+ }
297
+ )
298
+ },
299
+ buttonDirection: preventBack.buttonDirection,
300
+ onIconClose: { [weak self] in
301
+ preventBack.onIconClose?()
302
+ self?.hideOverplay()
303
+ },
304
+ isShowCloseIcon: preventBack.isShowCloseIcon
305
+ )
300
306
  )
301
307
  }
302
308
  return
@@ -143,13 +143,8 @@ public struct StackScreen<Content: View>: View {
143
143
  // MARK: - Body
144
144
 
145
145
  public var body: some View {
146
- GeometryReader { geometry in
146
+ GeometryReader { _ in
147
147
  let keyboardSize: CGFloat = useAvoidKeyboard ? max(keyboardHeight, 0) : 0
148
- let bottomInset = min(geometry.safeAreaInsets.bottom, 21)
149
- // Skip this screen's own bottom-inset reservation when hosting a footer
150
- // OR a self-contained BottomTab surface (mirrors Compose's
151
- // `if (options.footerComponent != null || isBottomTab) 0.dp else navigationBar`).
152
- let contentBottomInset = (footer == nil && !isBottomTabRoot) ? bottomInset : 0
153
148
 
154
149
  ZStack(alignment: .top) {
155
150
  // Background color
@@ -161,7 +156,6 @@ public struct StackScreen<Content: View>: View {
161
156
  VStack(alignment: horizontalAlignment, spacing: 0) {
162
157
  headerAndContent
163
158
  }
164
- .padding(.bottom, contentBottomInset)
165
159
 
166
160
  FloatingContent(
167
161
  fabProps: fabProps,
@@ -1,6 +1,6 @@
1
1
  Pod::Spec.new do |s|
2
2
  s.name = 'MoMoUIKits'
3
- s.version = '0.163.1-sp.8'
3
+ s.version = '0.163.1-sp.9'
4
4
  s.summary = 'MoMoUIKits for iOS'
5
5
  s.homepage = 'https://momo.vn'
6
6
  s.license = { :type => 'MIT' }
@@ -1,6 +1,6 @@
1
1
  Pod::Spec.new do |s|
2
2
  s.name = 'MoMoUIKitsDemo'
3
- s.version = '0.163.1-sp.8'
3
+ s.version = '0.163.1-sp.9'
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
@@ -2,268 +2,74 @@
2
2
  // BadgeDemo.swift
3
3
  // Example
4
4
  //
5
- // Created by sophia on 12/5/25.
5
+ // Preview + Playground demo for Badge / BadgeDot / BadgeRibbon.
6
6
  //
7
7
 
8
- import Foundation
9
8
  import MoMoUIKits
10
9
  import SwiftUI
11
10
 
12
- // MARK: - BadgeDemo
13
-
14
11
  struct BadgeDemo: View {
15
- // MARK: Internal
12
+ var body: some View {
13
+ if #available(iOS 16.0, *) {
14
+ DemoScreen(preview: { BadgePreview() }, playground: { BadgePlayground() })
15
+ } else {
16
+ Text("Requires iOS 16+").padding()
17
+ }
18
+ }
19
+ }
16
20
 
21
+ // MARK: - Preview
22
+
23
+ private struct BadgePreview: View {
17
24
  var body: some View {
18
- ScrollView {
19
- VStack(alignment: .leading, spacing: 24) {
20
- // MARK: - Basic Badges
21
-
22
- VStack(alignment: .leading, spacing: 12) {
23
- Text("Basic Badges")
24
- .font(.headline)
25
- .padding(.horizontal, 16)
26
-
27
- VStack(spacing: 16) {
28
- HStack(spacing: 16) {
29
- VStack(spacing: 8) {
30
- Badge()
31
- Text("Default")
32
- .font(.caption)
33
- .foregroundColor(.gray)
34
- }
35
-
36
- VStack(spacing: 8) {
37
- Badge(label: "9")
38
- Text("Single Digit")
39
- .font(.caption)
40
- .foregroundColor(.gray)
41
- }
42
-
43
- VStack(spacing: 8) {
44
- Badge(label: "99")
45
- Text("Two Digits")
46
- .font(.caption)
47
- .foregroundColor(.gray)
48
- }
49
-
50
- VStack(spacing: 8) {
51
- Badge(label: "999")
52
- Text("99+ Format")
53
- .font(.caption)
54
- .foregroundColor(.gray)
55
- }
56
- }
57
-
58
- HStack(spacing: 16) {
59
- VStack(spacing: 8) {
60
- Badge(label: "New")
61
- Text("Text Label")
62
- .font(.caption)
63
- .foregroundColor(.gray)
64
- }
65
-
66
- VStack(spacing: 8) {
67
- Badge(label: "Hot", backgroundColor: Colors.pink03)
68
- Text("Custom Color")
69
- .font(.caption)
70
- .foregroundColor(.gray)
71
- }
72
-
73
- VStack(spacing: 8) {
74
- Badge(label: "5", backgroundColor: Colors.green03)
75
- Text("Green Badge")
76
- .font(.caption)
77
- .foregroundColor(.gray)
78
- }
79
- }
80
- }
81
- .padding(.horizontal, 16)
82
- }
83
-
84
- Divider()
85
-
86
- // MARK: - Badge Dots
87
-
88
- VStack(alignment: .leading, spacing: 12) {
89
- Text("Badge Dots")
90
- .font(.headline)
91
- .padding(.horizontal, 16)
92
-
93
- HStack(spacing: 32) {
94
- VStack(spacing: 8) {
95
- BadgeDot(size: .small)
96
- Text("Small")
97
- .font(.caption)
98
- .foregroundColor(.gray)
99
- }
100
-
101
- VStack(spacing: 8) {
102
- BadgeDot(size: .large)
103
- Text("Large")
104
- .font(.caption)
105
- .foregroundColor(.gray)
106
- }
107
- }
108
- .padding(.horizontal, 16)
109
- }
110
-
111
- Divider()
112
-
113
- // MARK: - Badge Ribbons
114
-
115
- VStack(alignment: .leading, spacing: 12) {
116
- Text("Badge Ribbons")
117
- .font(.headline)
118
- .padding(.horizontal, 16)
119
-
120
- VStack(spacing: 20) {
121
- HStack(spacing: 20) {
122
- VStack(spacing: 8) {
123
- BadgeRibbon(position: .topLeft, label: "Top Left", isRound: true)
124
- .frame(width: 150, height: 30)
125
- Text("Top Left")
126
- .font(.caption)
127
- .foregroundColor(.gray)
128
- }
129
-
130
- VStack(spacing: 8) {
131
- BadgeRibbon(position: .topRight, label: "Top Right", isRound: true)
132
- .frame(width: 150, height: 30)
133
- Text("Top Right")
134
- .font(.caption)
135
- .foregroundColor(.gray)
136
- }
137
- }
138
-
139
- HStack(spacing: 20) {
140
- VStack(spacing: 8) {
141
- BadgeRibbon(position: .bottomLeft, label: "Bottom Left", isRound: true)
142
- .frame(width: 150, height: 30)
143
- Text("Bottom Left")
144
- .font(.caption)
145
- .foregroundColor(.gray)
146
- }
147
-
148
- VStack(spacing: 8) {
149
- BadgeRibbon(position: .bottomRight, label: "Bottom Right", isRound: true)
150
- .frame(width: 150, height: 30)
151
- Text("Bottom Right")
152
- .font(.caption)
153
- .foregroundColor(.gray)
154
- }
155
- }
156
- }
157
- .padding(.horizontal, 16)
158
- }
159
-
160
- Divider()
161
-
162
- // MARK: - Badge in Context (Real Usage Examples)
163
-
164
- VStack(alignment: .leading, spacing: 12) {
165
- Text("Real Usage Examples")
166
- .font(.headline)
167
- .padding(.horizontal, 16)
168
-
169
- VStack(spacing: 16) {
170
- // Notification with badge
171
- HStack {
172
- Image(systemName: "bell.fill")
173
- .font(.system(size: 24))
174
- .foregroundColor(Colors.primary)
175
-
176
- Text("Notifications")
177
- .font(.body)
178
-
179
- Spacer()
180
-
181
- Badge(label: "23")
182
- }
183
- .padding()
184
- .background(Color(.systemGray6))
185
- .cornerRadius(12)
186
-
187
- // Message with badge
188
- HStack {
189
- Image(systemName: "message.fill")
190
- .font(.system(size: 24))
191
- .foregroundColor(Colors.primary)
192
-
193
- Text("Messages")
194
- .font(.body)
195
-
196
- Spacer()
197
-
198
- Badge(label: "5")
199
- }
200
- .padding()
201
- .background(Color(.systemGray6))
202
- .cornerRadius(12)
203
-
204
- // Product card with ribbon
205
- ZStack(alignment: .topTrailing) {
206
- VStack(alignment: .leading, spacing: 8) {
207
- Rectangle()
208
- .fill(Color(.systemGray5))
209
- .frame(height: 120)
210
- .cornerRadius(8)
211
-
212
- Text("Product Name")
213
- .font(.headline)
214
-
215
- Text("$99.99")
216
- .font(.subheadline)
217
- .foregroundColor(.gray)
218
- }
219
- .padding()
220
- .background(Color(.systemGray6))
221
- .cornerRadius(12)
222
-
223
- BadgeRibbon(position: .topRight, label: "NEW")
224
- .frame(width: 60, height: 40)
225
- .offset(x: 5, y: 0)
226
- }
227
-
228
- // Profile with dot indicator
229
- HStack {
230
- ZStack(alignment: .topTrailing) {
231
- Circle()
232
- .fill(Color(.systemGray5))
233
- .frame(width: 50, height: 50)
234
-
235
- BadgeDot(size: .small)
236
- .offset(x: 2, y: 2)
237
- }
238
-
239
- VStack(alignment: .leading) {
240
- Text("User Name")
241
- .font(.headline)
242
- Text("Online")
243
- .font(.caption)
244
- .foregroundColor(Colors.green03)
245
- }
246
-
247
- Spacer()
248
- }
249
- .padding()
250
- .background(Color(.systemGray6))
251
- .cornerRadius(12)
252
- }
253
- .padding(.horizontal, 16)
254
- }
25
+ BasicColumnBox("Basic Badges", alignment: .leading) {
26
+ HStack(spacing: Spacing.L) {
27
+ Badge()
28
+ Badge(label: "9")
29
+ Badge(label: "99")
30
+ Badge(label: "999")
31
+ }
32
+ HStack(spacing: Spacing.L) {
33
+ Badge(label: "New")
34
+ Badge(label: "Hot", backgroundColor: Colors.pink03)
35
+ Badge(label: "5", backgroundColor: Colors.green03)
36
+ }
37
+ }
38
+
39
+ BasicColumnBox("Badge Dots", alignment: .leading) {
40
+ HStack(spacing: Spacing.XL) {
41
+ BadgeDot(size: .small)
42
+ BadgeDot(size: .large)
43
+ }
44
+ }
45
+
46
+ BasicColumnBox("Badge Ribbons", alignment: .leading) {
47
+ HStack(spacing: Spacing.L) {
48
+ BadgeRibbon(position: .topLeft, label: "Top Left", isRound: true)
49
+ .frame(width: 150, height: 30)
50
+ BadgeRibbon(position: .topRight, label: "Top Right", isRound: true)
51
+ .frame(width: 150, height: 30)
255
52
  }
256
- .padding(.vertical, 16)
257
53
  }
258
54
  }
259
55
  }
260
56
 
261
- // MARK: - Preview
57
+ // MARK: - Playground
262
58
 
263
- #if DEBUG
264
- struct BadgeDemo_Previews: PreviewProvider {
265
- static var previews: some View {
266
- BadgeDemo()
59
+ @available(iOS 16.0, *)
60
+ private struct BadgePlayground: View {
61
+ @State private var label = "New"
62
+ @State private var color: PlaygroundOption? = playgroundColors.first { $0.key == "pink03" }
63
+
64
+ var body: some View {
65
+ PlaygroundTab(
66
+ component: {
67
+ Badge(label: label, backgroundColor: color?.value as? Color)
68
+ },
69
+ params: [
70
+ .string("Label", $label),
71
+ .color("BackgroundColor", playgroundColors, $color),
72
+ ]
73
+ )
267
74
  }
268
75
  }
269
- #endif