@momo-kits/native-kits 0.162.30-debug → 0.162.31-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 (93) hide show
  1. package/CLAUDE.md +19 -0
  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/ApplicationEnvironment.swift +14 -6
  6. package/ios/Application/LanguageSource.swift +93 -0
  7. package/ios/Application/Localize.swift +73 -4
  8. package/ios/Application/LocalizeModifier.swift +30 -0
  9. package/ios/Application/Navigation/NavigationContainer.swift +28 -18
  10. package/ios/Application/Navigation/Navigator.swift +121 -53
  11. package/ios/Application/Navigation/NavigatorManager.swift +49 -0
  12. package/ios/Application/Navigation/component/NavigationFooter.swift +90 -0
  13. package/ios/Application/StackScreen.swift +19 -28
  14. package/ios/Badge/Badge.swift +2 -1
  15. package/ios/Badge/BadgeRibbon.swift +6 -2
  16. package/ios/Button/Button.swift +41 -61
  17. package/ios/Chip/Chip.swift +6 -5
  18. package/ios/Icon/Icon.swift +13 -4
  19. package/ios/Input/ErrorView.swift +2 -1
  20. package/ios/Input/Input.swift +9 -2
  21. package/ios/Input/InputPhoneNumber.swift +2 -1
  22. package/ios/Input/InputSearch.swift +3 -2
  23. package/ios/Input/InputTextArea.swift +6 -1
  24. package/ios/InputMoney/InputMoney.swift +5 -2
  25. package/ios/InputOTP/InputOTP.swift +2 -1
  26. package/ios/Layout/GridContext.swift +30 -0
  27. package/ios/Layout/Item.swift +42 -0
  28. package/ios/Layout/Section.swift +134 -0
  29. package/ios/Popup/PopupNotify.swift +2 -1
  30. package/ios/ProgressInfo/ProgressInfo.swift +4 -3
  31. package/ios/Rating/Rating.swift +2 -1
  32. package/ios/Stepper/Stepper.swift +2 -1
  33. package/ios/SuggestAction/SuggestAction.swift +3 -2
  34. package/ios/Tag/Tag.swift +2 -1
  35. package/ios/Template/TrustBanner/TrustBanner.swift +4 -2
  36. package/ios/Title/Title.swift +3 -2
  37. package/ios/Tooltip/Tooltip.swift +2 -1
  38. package/ios/Typography/FontScale.swift +103 -0
  39. package/ios/Typography/FontScaleStore.swift +52 -13
  40. package/ios/Typography/Text.swift +12 -29
  41. package/ios/Typography/Typography.swift +66 -86
  42. package/ios/native-kits.podspec +2 -1
  43. package/ios-demo/MoMoUIKitsDemo.podspec +1 -1
  44. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/AvatarDemo.swift +87 -73
  45. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/BadgeDemo.swift +55 -249
  46. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ButtonDemo.swift +90 -82
  47. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CarouselDemo.swift +74 -104
  48. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CheckboxDemo.swift +47 -50
  49. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ChipDemo.swift +63 -7
  50. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CollapseDemo.swift +90 -81
  51. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DateTimePickerDemo.swift +73 -97
  52. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DemoScreen.swift +420 -0
  53. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DividerDemo.swift +46 -4
  54. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/Home.swift +54 -57
  55. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/IconButtonDemo.swift +80 -45
  56. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/IconDemo.swift +81 -0
  57. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ImageDemo.swift +49 -62
  58. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InformationDemo.swift +66 -116
  59. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputDemo.swift +104 -98
  60. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputDropDownDemo.swift +76 -118
  61. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputMoneyDemo.swift +73 -86
  62. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputOTPDemo.swift +69 -54
  63. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputPhoneNumberDemo.swift +74 -54
  64. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputSearchDemo.swift +73 -20
  65. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputTextAreaDemo.swift +68 -38
  66. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/LoaderDemo.swift +55 -52
  67. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/LocalizeDemo.swift +51 -4
  68. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/NavigationDemo.swift +14 -2
  69. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PaginationDemo.swift +47 -51
  70. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PlaygroundData.swift +76 -0
  71. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupNotifyDemo.swift +67 -93
  72. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupPromotionDemo.swift +34 -43
  73. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ProgressInfoDemo.swift +83 -71
  74. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/RadioDemo.swift +55 -23
  75. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/RatingDemo.swift +58 -46
  76. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SectionDemo.swift +83 -0
  77. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SkeletonDemo.swift +41 -12
  78. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SliderDemo.swift +67 -127
  79. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SnackBarDemo.swift +54 -110
  80. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/StepperDemo.swift +69 -79
  81. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/StepsDemo.swift +78 -58
  82. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SuggestActionDemo.swift +76 -65
  83. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SwipeDemo.swift +82 -127
  84. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SwitchDemo.swift +44 -19
  85. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TabViewDemo.swift +92 -168
  86. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TagDemo.swift +79 -27
  87. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ThemeDemo.swift +0 -1
  88. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TitleDemo.swift +87 -92
  89. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TooltipDemo.swift +94 -163
  90. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TypographyDemo.swift +70 -70
  91. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/UploaderDemo.swift +68 -114
  92. package/ios-demo/Sources/MoMoUIKitsDemo/SwiftUIDemoBrowserView.swift +3 -1
  93. package/package.json +2 -2
@@ -1,58 +1,93 @@
1
+ //
2
+ // IconButtonDemo.swift
3
+ // Example
4
+ //
5
+ // Preview + Playground demo (DemoScreen pattern).
6
+ //
7
+
1
8
  import MoMoUIKits
2
9
  import SwiftUI
3
10
 
4
- // MARK: - IconButtonDemo
11
+ private let iconButtonSizeList: [PlaygroundOption] = [
12
+ .init("large", IconSize.large),
13
+ .init("medium", IconSize.medium),
14
+ .init("small", IconSize.small),
15
+ ]
16
+
17
+ private let iconShapeList: [PlaygroundOption] = [
18
+ .init("circle", IconShape.circle),
19
+ .init("square", IconShape.square),
20
+ ]
5
21
 
6
22
  struct IconButtonDemo: View {
7
23
  var body: some View {
8
- ScrollView {
9
- VStack(alignment: .leading, spacing: 24) {
10
- Group {
11
- Text("Source")
12
- .font(.headline)
13
- HStack(spacing: 12) {
14
- IconButton(icon: url, onClick: onPress, type: .primary)
15
- IconButton(icon: url2, onClick: onPress, type: .primary)
16
- IconButton(icon: url3, onClick: onPress, type: .primary)
17
- IconButton(icon: url4, onClick: onPress, type: .primary)
18
- }
19
- }
20
-
21
- Group {
22
- Text("Type")
23
- .font(.headline)
24
- HStack(spacing: 12) {
25
- IconButton(icon: url, onClick: onPress, type: .primary)
26
- IconButton(icon: url, onClick: onPress, type: .secondary)
27
- IconButton(icon: url, onClick: onPress, type: .tonal)
28
- IconButton(icon: url, onClick: onPress, type: .outline)
29
- IconButton(icon: url, onClick: onPress, type: .disabled)
30
- IconButton(icon: url, onClick: onPress, type: .danger)
31
- }
32
- }
33
-
34
- Group {
35
- Text("Size")
36
- .font(.headline)
37
- HStack(spacing: 12) {
38
- IconButton(icon: url, onClick: onPress, type: .primary, size: .large)
39
- IconButton(icon: url, onClick: onPress, type: .primary, size: .small)
40
- }
41
- }
42
- }
43
- .padding(16)
24
+ if #available(iOS 16.0, *) {
25
+ DemoScreen(preview: { IconButtonPreview() }, playground: { IconButtonPlayground() })
26
+ } else {
27
+ Text("Requires iOS 16+").padding()
44
28
  }
45
- .padding(.top)
46
29
  }
30
+ }
31
+
32
+ // MARK: - Preview
47
33
 
48
- func onPress() {
49
- print("IconButton")
34
+ private struct IconButtonPreview: View {
35
+ private let icon = "travel_plane"
36
+
37
+ var body: some View {
38
+ BasicColumnBox("Source", alignment: .leading) {
39
+ HStack(spacing: Spacing.M) {
40
+ IconButton(icon: "travel_plane", onClick: {})
41
+ IconButton(icon: "shopping_coupon", onClick: {})
42
+ IconButton(icon: "shopping_store", onClick: {})
43
+ IconButton(icon: "time_alarm_check", onClick: {})
44
+ }
45
+ }
46
+ BasicColumnBox("Type", alignment: .leading) {
47
+ HStack(spacing: Spacing.M) {
48
+ IconButton(icon: icon, onClick: {}, type: .primary)
49
+ IconButton(icon: icon, onClick: {}, type: .secondary)
50
+ IconButton(icon: icon, onClick: {}, type: .tonal)
51
+ IconButton(icon: icon, onClick: {}, type: .outline)
52
+ IconButton(icon: icon, onClick: {}, type: .disabled)
53
+ IconButton(icon: icon, onClick: {}, type: .danger)
54
+ }
55
+ }
56
+ BasicColumnBox("Size", alignment: .leading) {
57
+ HStack(spacing: Spacing.M) {
58
+ IconButton(icon: icon, onClick: {}, type: .primary, size: .large)
59
+ IconButton(icon: icon, onClick: {}, type: .primary, size: .small)
60
+ }
61
+ }
50
62
  }
63
+ }
64
+
65
+ // MARK: - Playground
51
66
 
52
- // MARK: Private
67
+ @available(iOS 16.0, *)
68
+ private struct IconButtonPlayground: View {
69
+ @State private var icon: PlaygroundOption? = playgroundIcons.first { $0.key == "basic_home" }
70
+ @State private var type: PlaygroundOption? = buttonTypeList.first { $0.key == "primary" }
71
+ @State private var size: PlaygroundOption? = iconButtonSizeList.first { $0.key == "small" }
72
+ @State private var shape: PlaygroundOption? = iconShapeList.first { $0.key == "circle" }
53
73
 
54
- private let url = "travel_plane"
55
- private let url2 = "shopping_coupon"
56
- private let url3 = "shopping_store"
57
- private let url4 = "time_alarm_check"
74
+ var body: some View {
75
+ PlaygroundTab(
76
+ component: {
77
+ IconButton(
78
+ icon: (icon?.value as? String) ?? "basic_home",
79
+ onClick: {},
80
+ type: (type?.value as? ButtonType) ?? .primary,
81
+ size: (size?.value as? IconSize) ?? .small,
82
+ shape: (shape?.value as? IconShape) ?? .circle
83
+ )
84
+ },
85
+ params: [
86
+ .option("Icon", playgroundIcons, $icon),
87
+ .radio("Type", buttonTypeList, $type),
88
+ .radio("Size", iconButtonSizeList, $size),
89
+ .radio("Shape", iconShapeList, $shape),
90
+ ]
91
+ )
92
+ }
58
93
  }
@@ -0,0 +1,81 @@
1
+ //
2
+ // IconDemo.swift
3
+ // Example
4
+ //
5
+ // Preview + Playground demo, mirroring Compose's IconUsage.kt.
6
+ //
7
+
8
+ import MoMoUIKits
9
+ import SwiftUI
10
+
11
+ struct IconDemo: View {
12
+ var body: some View {
13
+ if #available(iOS 16.0, *) {
14
+ DemoScreen(preview: { IconPreview() }, playground: { IconPlayground() })
15
+ } else {
16
+ Text("Requires iOS 16+").padding()
17
+ }
18
+ }
19
+ }
20
+
21
+ // MARK: - Preview
22
+
23
+ private struct IconPreview: View {
24
+ var body: some View {
25
+ BasicColumnBox("Source", alignment: .leading) {
26
+ HStack(spacing: Spacing.M) {
27
+ Icon(source: "basic_home", size: 24)
28
+ Icon(source: "basic_flag", size: 24)
29
+ Icon(source: "basic_filter", size: 24)
30
+ Icon(source: "basic_delete", size: 24)
31
+ }
32
+ }
33
+ BasicColumnBox("Size", alignment: .leading) {
34
+ HStack(alignment: .center, spacing: Spacing.M) {
35
+ Icon(source: "basic_home", size: 16)
36
+ Icon(source: "basic_flag", size: 20)
37
+ Icon(source: "basic_filter", size: 24)
38
+ Icon(source: "basic_delete", size: 28)
39
+ }
40
+ }
41
+ BasicColumnBox("Color", alignment: .leading) {
42
+ HStack(spacing: Spacing.M) {
43
+ Icon(source: "basic_home", size: 24, color: Colors.red03)
44
+ Icon(source: "basic_flag", size: 24, color: Colors.green04)
45
+ Icon(source: "basic_filter", size: 24, color: Colors.pink06)
46
+ Icon(source: "basic_delete", size: 24, color: Colors.yellow02)
47
+ }
48
+ }
49
+ BasicColumnBox("No theme icon", alignment: .leading) {
50
+ HStack(spacing: Spacing.M) {
51
+ Icon(source: "ic_round_momo_tiny", size: 24)
52
+ }
53
+ }
54
+ }
55
+ }
56
+
57
+ // MARK: - Playground
58
+
59
+ @available(iOS 16.0, *)
60
+ private struct IconPlayground: View {
61
+ @State private var source: PlaygroundOption? = playgroundIcons.first { $0.key == "basic_home" }
62
+ @State private var color: PlaygroundOption? = playgroundColors.first { $0.key == "black17" }
63
+ @State private var size = "24"
64
+
65
+ var body: some View {
66
+ PlaygroundTab(
67
+ component: {
68
+ Icon(
69
+ source: (source?.value as? String) ?? "basic_home",
70
+ size: CGFloat(Int(size) ?? 24),
71
+ color: color?.value as? Color
72
+ )
73
+ },
74
+ params: [
75
+ .option("Source", playgroundIcons, $source),
76
+ .color("Color", playgroundColors, $color),
77
+ .number("Size (dp)", $size),
78
+ ]
79
+ )
80
+ }
81
+ }
@@ -2,77 +2,64 @@
2
2
  // ImageDemo.swift
3
3
  // Example
4
4
  //
5
- // Created by dung.pham2 on 2/12/24.
5
+ // Preview + Playground demo for ImageView.
6
6
  //
7
7
 
8
- import SwiftUI
9
8
  import MoMoUIKits
9
+ import SwiftUI
10
10
 
11
11
  struct ImageDemo: View {
12
- // MARK: - Properties
13
- @State private var time = 1000
14
-
15
- // MARK: - Body
16
12
  var body: some View {
17
- NavigationView {
18
- ScrollView {
19
- VStack(spacing: 16) { // Equivalent to Spacing.M
20
- HStack {
21
- ImageView("https://source.unsplash.com/random/\(time)")
22
- .frame(width: 100, height: 100)
23
-
24
- Spacer()
25
-
26
- ImageView("https://abc.com")
27
- .frame(width: 100, height: 100)
28
- }
29
-
30
- ImageView("https://source.unsplash.com/random/\(time)")
31
- .frame(maxWidth: .infinity)
32
- .frame(height: 100)
33
- .aspectRatio(contentMode: .fill)
34
- .clipped()
35
-
36
- ImageView("https://images.unsplash.com/photo-1509395062183-67c5ad6faff9")
37
- .frame(maxWidth: .infinity)
38
- .frame(height: 100)
39
- .aspectRatio(contentMode: .fill)
40
- .clipped()
41
-
42
- ImageView("https://source.unsplash.com/random/\(time)")
43
- .frame(maxWidth: .infinity)
44
- .frame(height: 100)
45
- .aspectRatio(contentMode: .fill)
46
- .clipped()
47
-
48
- ImageView("https://source.unsplash.com/random/\(time)")
49
- .frame(maxWidth: .infinity)
50
- .frame(height: 100)
51
- .aspectRatio(contentMode: .fit)
52
-
53
- ImageView("https://source.unsplash.com/random/\(time)")
54
- .frame(maxWidth: .infinity)
55
- .frame(height: 100)
56
- .aspectRatio(contentMode: .fit)
57
-
58
- ImageView("https://source.unsplash.com/random/\(time)")
59
- .frame(maxWidth: .infinity)
60
- .frame(height: 100)
61
- .aspectRatio(contentMode: .fit)
62
- }
63
- .padding()
64
- }
65
- }
66
- .onAppear {
67
- startTimer()
13
+ if #available(iOS 16.0, *) {
14
+ DemoScreen(preview: { ImagePreview() }, playground: { ImagePlayground() })
15
+ } else {
16
+ Text("Requires iOS 16+").padding()
68
17
  }
69
18
  }
70
-
71
- // MARK: - Methods
72
- private func startTimer() {
73
- Timer.scheduledTimer(withTimeInterval: 5, repeats: true) { _ in
74
- time = Int.random(in: 1...1000)
19
+ }
20
+
21
+ // MARK: - Preview
22
+
23
+ private struct ImagePreview: View {
24
+ private let sample = "https://images.unsplash.com/photo-1509395062183-67c5ad6faff9"
25
+
26
+ var body: some View {
27
+ BasicColumnBox("Fixed size", alignment: .leading) {
28
+ ImageView(sample).frame(width: 100, height: 100)
29
+ }
30
+ BasicColumnBox("Fill width", alignment: .leading) {
31
+ ImageView(sample)
32
+ .frame(maxWidth: .infinity)
33
+ .frame(height: 120)
34
+ .aspectRatio(contentMode: .fill)
35
+ .clipped()
36
+ }
37
+ BasicColumnBox("Invalid URL (fallback)", alignment: .leading) {
38
+ ImageView("https://abc.com").frame(width: 100, height: 100)
75
39
  }
76
40
  }
77
41
  }
78
42
 
43
+ // MARK: - Playground
44
+
45
+ @available(iOS 16.0, *)
46
+ private struct ImagePlayground: View {
47
+ @State private var url = "https://images.unsplash.com/photo-1509395062183-67c5ad6faff9"
48
+ @State private var height: Double = 120
49
+
50
+ var body: some View {
51
+ PlaygroundTab(
52
+ component: {
53
+ ImageView(url)
54
+ .frame(maxWidth: .infinity)
55
+ .frame(height: CGFloat(height))
56
+ .aspectRatio(contentMode: .fill)
57
+ .clipped()
58
+ },
59
+ params: [
60
+ .string("URL", $url),
61
+ .slider("Height", $height, min: 60, max: 240, step: 10),
62
+ ]
63
+ )
64
+ }
65
+ }
@@ -2,137 +2,87 @@
2
2
  // InformationDemo.swift
3
3
  // Example
4
4
  //
5
- // Created by product2 on 6/18/26.
5
+ // Preview + Playground demo (DemoScreen pattern).
6
6
  //
7
7
 
8
- import Foundation
9
8
  import MoMoUIKits
10
9
  import SwiftUI
11
10
 
12
- // MARK: - InformationDemo
11
+ private let informationStateList: [PlaygroundOption] = [
12
+ .init("default", InformationState.default),
13
+ .init("warning", InformationState.warning),
14
+ .init("error", InformationState.error),
15
+ ]
13
16
 
14
17
  struct InformationDemo: View {
15
- @State private var showDismissible = true
16
-
17
18
  var body: some View {
18
- ScrollView {
19
- VStack(alignment: .leading, spacing: 24) {
20
- // MARK: - States
21
-
22
- section("States") {
23
- Information(
24
- title: "Default",
25
- description: "This is a default information message.",
26
- showIconClose: true,
27
- onPressClose: {}
28
- )
29
- Information(
30
- title: "Warning",
31
- description: "This is a warning information message.",
32
- state: .warning
33
- )
34
- Information(
35
- title: "Error",
36
- description: "This is an error information message.",
37
- state: .error
38
- )
39
- }
40
-
41
- // MARK: - Without title
42
-
43
- section("Description only") {
44
- Information(
45
- description: "Information without a title, only a description."
46
- )
47
- Information(
48
- description: "Warning without a title.",
49
- state: .warning
50
- )
51
- }
52
-
53
- // MARK: - With action
54
-
55
- section("With action") {
56
- Information(
57
- title: "Update available",
58
- description: "A new version is ready to install.",
59
- onPressAction: { print("Action pressed") },
60
- action: "Update now"
61
- )
62
- Information(
63
- title: "Payment failed",
64
- description: "We couldn't process your payment.",
65
- state: .error,
66
- onPressAction: { print("Retry pressed") },
67
- action: "Retry"
68
- )
69
- }
70
-
71
- // MARK: - With close button
72
-
73
- section("Dismissible") {
74
- if showDismissible {
75
- Information(
76
- title: "Dismissible",
77
- description: "Tap the close icon to dismiss this banner.",
78
- onPressClose: { showDismissible = false }
79
- )
80
- } else {
81
- Information(
82
- description: "Banner dismissed. Toggle to show again.",
83
- state: .warning,
84
- onPressAction: { showDismissible = true }, showIcon: true,
85
- action: "Show again"
86
-
87
- )
88
- }
89
- }
90
-
91
- // MARK: - With image
92
-
93
- section("With image") {
94
- Information(
95
- title: "Custom image",
96
- description: "Uses a remote image instead of the state icon.",
97
- image: "https://images.unsplash.com/photo-1509395062183-67c5ad6faff9",
98
- onPressClose: { print("Close pressed") }
99
- )
100
- }
101
-
102
- // MARK: - Without icon
103
-
104
- section("Without icon") {
105
- Information(
106
- title: "No icon",
107
- description: "showIcon is set to false.",
108
- showIcon: false
109
- )
110
- }
111
- }
112
- .padding(.vertical, 16)
19
+ if #available(iOS 16.0, *) {
20
+ DemoScreen(preview: { InformationPreview() }, playground: { InformationPlayground() })
21
+ } else {
22
+ Text("Requires iOS 16+").padding()
113
23
  }
114
24
  }
25
+ }
115
26
 
116
- // MARK: - Helpers
27
+ // MARK: - Preview
117
28
 
118
- @ViewBuilder
119
- private func section<Content: View>(
120
- _ title: String,
121
- @ViewBuilder content: () -> Content
122
- ) -> some View {
123
- VStack(alignment: .leading, spacing: 12) {
124
- Text(title)
125
- .font(.headline)
126
- content()
29
+ private struct InformationPreview: View {
30
+ var body: some View {
31
+ BasicColumnBox("States", alignment: .leading) {
32
+ Information(title: "Default", description: "This is a default information message.",
33
+ showIconClose: true, onPressClose: {})
34
+ Information(title: "Warning", description: "This is a warning information message.", state: .warning)
35
+ Information(title: "Error", description: "This is an error information message.", state: .error)
36
+ }
37
+ BasicColumnBox("Description only", alignment: .leading) {
38
+ Information(description: "Information without a title, only a description.")
39
+ Information(description: "Warning without a title.", state: .warning)
40
+ }
41
+ BasicColumnBox("With action", alignment: .leading) {
42
+ Information(title: "Update available", description: "A new version is ready to install.",
43
+ onPressAction: {}, action: "Update now")
44
+ Information(title: "Payment failed", description: "We couldn't process your payment.",
45
+ state: .error, onPressAction: {}, action: "Retry")
46
+ }
47
+ BasicColumnBox("Without icon", alignment: .leading) {
48
+ Information(title: "No icon", description: "showIcon is set to false.", showIcon: false)
127
49
  }
128
- .padding(.horizontal, 16)
129
50
  }
130
51
  }
131
52
 
132
- #if DEBUG
133
- struct InformationDemo_Previews: PreviewProvider {
134
- static var previews: some View {
135
- InformationDemo()
53
+ // MARK: - Playground
54
+
55
+ @available(iOS 16.0, *)
56
+ private struct InformationPlayground: View {
57
+ @State private var title = "Title"
58
+ @State private var description = "Information description."
59
+ @State private var state: PlaygroundOption? = informationStateList.first { $0.key == "default" }
60
+ @State private var showIcon = true
61
+ @State private var showIconClose = false
62
+ @State private var action = ""
63
+
64
+ var body: some View {
65
+ PlaygroundTab(
66
+ component: {
67
+ Information(
68
+ title: title.isEmpty ? nil : title,
69
+ description: description,
70
+ state: (state?.value as? InformationState) ?? .default,
71
+ onPressAction: {},
72
+ showIcon: showIcon,
73
+ action: action.isEmpty ? nil : action,
74
+ showIconClose: showIconClose,
75
+ onPressClose: {}
76
+ )
77
+ },
78
+ params: [
79
+ .string("Title", $title),
80
+ .string("Description", $description),
81
+ .radio("State", informationStateList, $state),
82
+ .checkbox("ShowIcon", $showIcon),
83
+ .checkbox("ShowIconClose", $showIconClose),
84
+ .string("Action", $action),
85
+ ]
86
+ )
136
87
  }
137
88
  }
138
- #endif