@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
@@ -24,27 +24,72 @@ private let AppTranslations = LocalizationObject(
24
24
  )
25
25
 
26
26
  struct LocalizeDemo: View {
27
- @EnvironmentObject private var localize: Localize
27
+ // AI-GENERATED START: observe the app language and drive it through the real platform API
28
+ @Environment(\.localize) private var localize
29
+ @Environment(\.applicationEnvironment) private var applicationEnvironment
30
+ // AI-GENERATED END: observe the app language and drive it through the real platform API
28
31
 
29
32
  private var isEnglish: Bool { localize.currentLanguage == Localize.EN }
30
33
 
34
+ // AI-GENERATED START: observe the app language and drive it through the real platform API
35
+ private var storedLanguage: String {
36
+ UserDefaults.standard.string(forKey: UserDefaultsLanguageSource.defaultKey) ?? "nil"
37
+ }
38
+
39
+ private func setLanguage(_ language: String) {
40
+ // Platform API -> KMP LanguageApi.SetLanguage -> LocalizationProvider.set, which also
41
+ // mirrors AppleLanguages. KVO then pushes the change back into Localize. The real wire.
42
+ if let composeApi = applicationEnvironment.composeApi {
43
+ _ = composeApi.request(funcName: "setLanguage", params: language)
44
+ } else {
45
+ localize.changeLanguage(language) // #Preview / sample app: no host bridge
46
+ }
47
+ }
48
+ // AI-GENERATED END: observe the app language and drive it through the real platform API
49
+
31
50
  var body: some View {
32
51
  ScrollView {
33
52
  VStack(alignment: .leading, spacing: 0) {
34
53
 
35
54
  LocalizeSectionBox(title: "Đổi ngôn ngữ (toàn app)") {
36
55
  MomoText(
37
- "localize.changeLanguage(...) đổi ngôn ngữ cho cả NavigationContainer mọi màn hình đang đọc translate() sẽ cập nhật theo.",
56
+ "Switch gọi platform API setLanguageđổi ngôn ngữ THẬT của app. Localize observe lại "
57
+ + "qua UserDefaults nên mọi màn hình đang đọc translate() đều cập nhật.",
38
58
  typography: .descriptionDefaultRegular
39
59
  )
40
60
  Switch(
41
61
  .constant(isEnglish),
42
- onChange: { _ in localize.changeLanguage(isEnglish ? Localize.VI : Localize.EN) },
62
+ onChange: { _ in setLanguage(isEnglish ? Localize.VI : Localize.EN) },
43
63
  title: "English"
44
64
  )
45
65
  MomoText("currentLanguage = \(localize.currentLanguage)", typography: .labelDefaultMedium)
46
66
  }
47
67
 
68
+ // AI-GENERATED START: prove the observation loop, not kit-local state, drives updates
69
+ LocalizeSectionBox(title: "Nguồn thật: UserDefaults") {
70
+ MomoText(
71
+ "Ghi thẳng vào UserDefaults, bỏ qua mọi API của kit — màn hình vẫn đổi, chứng minh "
72
+ + "Localize đang observe ngôn ngữ app.",
73
+ typography: .descriptionDefaultRegular
74
+ )
75
+ TranslationRow(key: UserDefaultsLanguageSource.defaultKey, value: storedLanguage)
76
+ Button(
77
+ title: "Ghi thẳng UserDefaults = \"en\"",
78
+ action: {
79
+ // Quoted on purpose: proves normalize() strips the JSON quoting
80
+ // RN and KMP persist.
81
+ UserDefaults.standard.set(
82
+ "\"en\"",
83
+ forKey: UserDefaultsLanguageSource.defaultKey
84
+ )
85
+ },
86
+ type: .outline,
87
+ size: .medium,
88
+ isFull: false
89
+ )
90
+ }
91
+ // AI-GENERATED END: prove the observation loop, not kit-local state, drives updates
92
+
48
93
  LocalizeSectionBox(title: "translate(key) — từ điển mặc định của kit") {
49
94
  TranslationRow(key: "confirm", value: localize.translate("confirm"))
50
95
  TranslationRow(key: "cancel", value: localize.translate("cancel"))
@@ -179,8 +224,10 @@ private struct TranslationRow: View {
179
224
  #Preview {
180
225
  if #available(iOS 16.0, *) {
181
226
  NavigationStack {
227
+ // AI-GENERATED START: preview needs the observing provider or the toggle will not update
182
228
  LocalizeDemo()
183
- .environmentObject(Localize())
229
+ .momoLocalize(Localize())
230
+ // AI-GENERATED END: preview needs the observing provider or the toggle will not update
184
231
  }
185
232
  } else {
186
233
  // Fallback on earlier versions
@@ -101,9 +101,21 @@ private struct NavigationDemoContent: View {
101
101
 
102
102
  Divider().padding(.vertical, 4)
103
103
 
104
+ row("Custom Back Handler") {
105
+ // The screen owns back: the header button and the edge swipe both
106
+ // run this instead of popping (mirrors Compose's onBackHandler).
107
+ navigator.push(
108
+ { DemoLeafScreen(title: "Back is intercepted — swipe from the edge", color: Colors.blue09) },
109
+ options: NavigationOptions(
110
+ title: "Custom Back",
111
+ onBackHandler: { print("back intercepted") }
112
+ )
113
+ )
114
+ }
115
+
104
116
  row("Prevent Back") {
105
- // Tapping the header back button shows a confirm popup before
106
- // leaving (mirrors Compose's headerBackProps.preventBack).
117
+ // The header back button and the edge swipe both show a confirm popup
118
+ // before leaving (mirrors Compose's headerBackProps.preventBack).
107
119
  navigator.push(
108
120
  { DemoLeafScreen(title: "Tap the header back button", color: Colors.pink09) },
109
121
  options: NavigationOptions(
@@ -2,71 +2,67 @@
2
2
  // PaginationDemo.swift
3
3
  // Example
4
4
  //
5
- // Combined demo for the four pagination variants.
6
- // Mirrors shared PaginationUsage.kt: a configurable Data Length plus
7
- // Prev/Next controls drive PaginationScroll, PaginationNumber,
8
- // PaginationDot and PaginationWhiteDot.
5
+ // Preview + Playground demo for the pagination variants.
9
6
  //
10
7
 
11
8
  import MoMoUIKits
12
9
  import SwiftUI
13
10
 
14
11
  struct PaginationDemo: View {
15
- @State private var dataLengthText: String = "3"
16
- @State private var activeIndex: Int = 0
17
-
18
- private var dataLength: Int {
19
- let n = Int(dataLengthText) ?? 1
20
- return max(n, 1)
12
+ var body: some View {
13
+ if #available(iOS 16.0, *) {
14
+ DemoScreen(preview: { PaginationPreview() }, playground: { PaginationPlayground() })
15
+ } else {
16
+ Text("Requires iOS 16+").padding()
17
+ }
21
18
  }
19
+ }
20
+
21
+ // MARK: - Preview
22
22
 
23
+ private struct PaginationPreview: View {
23
24
  var body: some View {
24
- ScrollView {
25
- VStack(alignment: .center, spacing: Spacing.L) {
26
- PaginationScroll {
27
- ForEach(0..<10, id: \.self) { i in
28
- MomoText("\(i)", typography: .bodyDefaultRegular)
29
- .frame(width: 120, height: 60)
30
- .background(Colors.blue08)
31
- .overlay(
32
- Rectangle().stroke(Colors.black06, lineWidth: 1)
33
- )
34
- }
25
+ BasicColumnBox("PaginationScroll", alignment: .leading) {
26
+ PaginationScroll {
27
+ ForEach(0..<10, id: \.self) { i in
28
+ MomoText("\(i)", typography: .bodyDefaultRegular)
29
+ .frame(width: 120, height: 60)
30
+ .background(Colors.blue08)
31
+ .overlay(Rectangle().stroke(Colors.black06, lineWidth: 1))
35
32
  }
33
+ }
34
+ }
35
+ BasicColumnBox("Number / Dot", alignment: .leading) {
36
+ PaginationNumber(activeIndex: 1, dataLength: 3)
37
+ PaginationDot(activeIndex: 1, dataLength: 3)
38
+ }
39
+ BasicColumnBox("WhiteDot", alignment: .leading, background: Colors.black17) {
40
+ PaginationWhiteDot(activeIndex: 1, dataLength: 3)
41
+ }
42
+ }
43
+ }
36
44
 
37
- PaginationNumber(activeIndex: activeIndex, dataLength: dataLength)
38
-
39
- PaginationDot(activeIndex: activeIndex, dataLength: dataLength)
40
-
41
- PaginationWhiteDot(activeIndex: activeIndex, dataLength: dataLength)
42
- .padding(Spacing.S)
43
- .background(Colors.black17) // dark backdrop so white dots are visible
45
+ // MARK: - Playground
44
46
 
45
- MomoText("Data Length", typography: .labelSMedium, color: Colors.textSecondary)
46
- TextField("Data Length", text: $dataLengthText)
47
- .keyboardType(.numberPad)
48
- .textFieldStyle(.roundedBorder)
49
- .onChange(of: dataLengthText) { _ in activeIndex = 0 }
50
- .frame(maxWidth: 200)
47
+ @available(iOS 16.0, *)
48
+ private struct PaginationPlayground: View {
49
+ @State private var dataLength = "5"
50
+ @State private var activeIndex: Double = 0
51
51
 
52
- HStack {
53
- Button("Prev") {
54
- if activeIndex != 0 { activeIndex -= 1 }
55
- }
56
- .frame(maxWidth: .infinity)
52
+ private var length: Int { max(Int(dataLength) ?? 1, 1) }
57
53
 
58
- Button("Next") {
59
- if activeIndex != dataLength - 1 { activeIndex += 1 }
60
- }
61
- .frame(maxWidth: .infinity)
54
+ var body: some View {
55
+ PlaygroundTab(
56
+ component: {
57
+ VStack(alignment: .leading, spacing: Spacing.L) {
58
+ PaginationNumber(activeIndex: Int(activeIndex), dataLength: length)
59
+ PaginationDot(activeIndex: Int(activeIndex), dataLength: length)
62
60
  }
63
- }
64
- .padding(Spacing.L)
65
- }
66
- .background(Colors.background)
61
+ },
62
+ params: [
63
+ .number("DataLength", $dataLength),
64
+ .slider("ActiveIndex", $activeIndex, min: 0, max: 9, step: 1),
65
+ ]
66
+ )
67
67
  }
68
68
  }
69
-
70
- #Preview {
71
- PaginationDemo()
72
- }
@@ -0,0 +1,76 @@
1
+ //
2
+ // PlaygroundData.swift
3
+ // Example
4
+ //
5
+ // Swift equivalents of the option maps in Compose's DemoScreen.kt (buttonTypeList,
6
+ // sizeList, Icons, Colors.colorList, ...). Each list feeds an OPTION / RADIO / COLOR
7
+ // PlaygroundParams. Extend as more demos are converted.
8
+ //
9
+
10
+ import MoMoUIKits
11
+ import SwiftUI
12
+
13
+ // MARK: - Enum option maps
14
+
15
+ let buttonTypeList: [PlaygroundOption] = [
16
+ .init("primary", ButtonType.primary),
17
+ .init("secondary", ButtonType.secondary),
18
+ .init("tonal", ButtonType.tonal),
19
+ .init("outline", ButtonType.outline),
20
+ .init("danger", ButtonType.danger),
21
+ .init("text", ButtonType.text),
22
+ .init("disabled", ButtonType.disabled),
23
+ ]
24
+
25
+ let buttonSizeList: [PlaygroundOption] = [
26
+ .init("large", ButtonSize.large),
27
+ .init("medium", ButtonSize.medium),
28
+ .init("small", ButtonSize.small),
29
+ ]
30
+
31
+ let inputSizeList: [PlaygroundOption] = [
32
+ .init("large", InputSize.large),
33
+ .init("small", InputSize.small),
34
+ ]
35
+
36
+ let inputFontWeightList: [PlaygroundOption] = [
37
+ .init("regular", InputFontWeight.regular),
38
+ .init("bold", InputFontWeight.bold),
39
+ ]
40
+
41
+ let keyboardTypeList: [PlaygroundOption] = [
42
+ .init("default", UIKeyboardType.default),
43
+ .init("asciiCapable", UIKeyboardType.asciiCapable),
44
+ .init("numberPad", UIKeyboardType.numberPad),
45
+ .init("phonePad", UIKeyboardType.phonePad),
46
+ .init("URL", UIKeyboardType.URL),
47
+ .init("emailAddress", UIKeyboardType.emailAddress),
48
+ .init("decimalPad", UIKeyboardType.decimalPad),
49
+ ]
50
+
51
+ // MARK: - Icons (curated from icon.json; Compose `Icons`)
52
+
53
+ let playgroundIcons: [PlaygroundOption] = [
54
+ "basic_home", "basic_flag", "basic_filter", "basic_delete", "basic_copy",
55
+ "basic_account", "basic_chart", "basic_setting", "basic_sun", "basic_person",
56
+ "basic_person_add", "basic_person_group", "basic_options", "basic_sorting",
57
+ "arrow_chevron_down", "arrow_chevron_up", "arrow_chevron_left", "arrow_chevron_right",
58
+ "arrow_arrow_forward", "arrow_redo", "arrow_undo", "arrow_refresh_ccw",
59
+ "security_eye_open", "notifications_check_circle", "help_center", "pin_star",
60
+ "navigation_more_icon",
61
+ ].map { PlaygroundOption($0, $0) }
62
+
63
+ // MARK: - Colors (Compose `Colors.colorList`)
64
+
65
+ let playgroundColors: [PlaygroundOption] = [
66
+ .init("primary", Colors.primary),
67
+ .init("black17", Colors.black17),
68
+ .init("black13", Colors.black13),
69
+ .init("black10", Colors.black10),
70
+ .init("black05", Colors.black05),
71
+ .init("black01", Colors.black01),
72
+ .init("pink06", Colors.pink06),
73
+ .init("pink03", Colors.pink03),
74
+ .init("blue09", Colors.blue09),
75
+ .init("blue05", Colors.blue05),
76
+ ]
@@ -4,118 +4,92 @@ import SwiftUI
4
4
  struct PopupNotifyDemo: View {
5
5
  var body: some View {
6
6
  if #available(iOS 16.0, *) {
7
- PopupNotifyDemoContent()
7
+ DemoScreen(preview: { PopupNotifyPreview() }, playground: { PopupNotifyPlayground() })
8
8
  } else {
9
- MomoText("PopupNotify demo requires iOS 16+")
10
- .padding()
9
+ Text("Requires iOS 16+").padding()
11
10
  }
12
11
  }
13
12
  }
14
13
 
14
+ // MARK: - Preview
15
+
15
16
  @available(iOS 16.0, *)
16
- private struct PopupNotifyDemoContent: View {
17
+ private struct PopupNotifyPreview: View {
17
18
  @EnvironmentObject private var navigator: Navigator
18
19
 
20
+ var body: some View {
21
+ BasicColumnBox("PopupNotify", alignment: .leading) {
22
+ MoMoUIKits.Button(title: "Show popup", action: { show() }, size: .large)
23
+ }
24
+ }
25
+
26
+ private func show() {
27
+ navigator.showModal {
28
+ PopupNotify(props: PopupNotifyProps(
29
+ image: "https://i.imgur.com/s70S9s9.png",
30
+ title: "Title",
31
+ description: "Check lại một nhịp auto chuyển sang dạng CTA.",
32
+ error: "9004",
33
+ primary: PopupAction(title: "Primary", onPress: { navigator.hideOverplay() }),
34
+ secondary: PopupAction(title: "Secondary", onPress: { navigator.hideOverplay() }),
35
+ onIconClose: { navigator.hideOverplay() },
36
+ isShowCloseIcon: true
37
+ ))
38
+ }
39
+ }
40
+ }
41
+
42
+ // MARK: - Playground
43
+
44
+ private let popupDirectionList: [PlaygroundOption] = [
45
+ .init("auto", PopupActionDirection.auto),
46
+ .init("row", PopupActionDirection.row),
47
+ .init("column", PopupActionDirection.column),
48
+ ]
49
+
50
+ @available(iOS 16.0, *)
51
+ private struct PopupNotifyPlayground: View {
52
+ @EnvironmentObject private var navigator: Navigator
19
53
  @State private var image = "https://i.imgur.com/s70S9s9.png"
20
54
  @State private var title = "Title"
21
55
  @State private var description = "Check lại một nhịp auto chuyển sang dạng CTA."
22
56
  @State private var error = "9004"
23
- @State private var buttonDirection: PopupActionDirection = .auto
24
57
  @State private var primaryTitle = "Primary"
25
58
  @State private var secondaryTitle = "Secondary"
26
- @State private var showCloseButton = false
59
+ @State private var showClose = false
60
+ @State private var direction: PlaygroundOption? = popupDirectionList.first { $0.key == "auto" }
27
61
 
28
62
  var body: some View {
29
- ScrollView {
30
- VStack(alignment: .leading, spacing: Spacing.M) {
31
- HStack(alignment: .center, spacing: Spacing.S) {
32
- Input(
33
- text: $image,
34
- placeholder: "Image URL",
35
- floatingValue: "Image URL"
36
- )
37
- Checkbox(
38
- Binding(
39
- get: { image.isEmpty },
40
- set: { checked in
41
- image = checked ? "" : "https://i.imgur.com/s70S9s9.png"
42
- }
43
- ),
44
- title: "undefined"
45
- )
46
- }
47
-
48
- Input(text: $title, placeholder: "Title", floatingValue: "Title")
49
- Input(text: $description, placeholder: "Description", floatingValue: "Description")
50
-
51
- HStack(alignment: .center, spacing: Spacing.S) {
52
- Input(text: $error, placeholder: "Error", floatingValue: "Error")
53
- Checkbox(
54
- Binding(
55
- get: { error.isEmpty },
56
- set: { checked in error = checked ? "" : "9004" }
57
- ),
58
- title: "undefined"
59
- )
60
- }
61
-
62
- VStack(alignment: .leading, spacing: Spacing.XS) {
63
- MomoText("Button Direction", typography: .descriptionDefaultRegular, color: Colors.black12)
64
- Picker("Button Direction", selection: $buttonDirection) {
65
- Text("Auto").tag(PopupActionDirection.auto)
66
- Text("Row").tag(PopupActionDirection.row)
67
- Text("Column").tag(PopupActionDirection.column)
68
- }
69
- .pickerStyle(.segmented)
70
- }
71
-
72
- Input(
73
- text: $primaryTitle,
74
- placeholder: "Primary Button Title",
75
- floatingValue: "Primary Button Title"
76
- )
77
- Input(
78
- text: $secondaryTitle,
79
- placeholder: "Secondary Button Title",
80
- floatingValue: "Secondary Button Title"
81
- )
82
-
83
- Checkbox($showCloseButton, title: "Show Close Button")
84
-
85
- MoMoUIKits.Button(
86
- title: "Show popup",
87
- action: showPopup,
88
- size: .large
89
- )
90
- .padding(.top, Spacing.XS)
91
- }
92
- .padding(Spacing.M * 2)
93
- }
63
+ PlaygroundTab(
64
+ component: {
65
+ MoMoUIKits.Button(title: "Show popup", action: { show() }, size: .large)
66
+ },
67
+ params: [
68
+ .string("Image", $image),
69
+ .string("Title", $title),
70
+ .string("Description", $description),
71
+ .string("Error", $error),
72
+ .string("PrimaryTitle", $primaryTitle),
73
+ .string("SecondaryTitle", $secondaryTitle),
74
+ .radio("ButtonDirection", popupDirectionList, $direction),
75
+ .checkbox("ShowCloseButton", $showClose),
76
+ ]
77
+ )
94
78
  }
95
79
 
96
- private func showPopup() {
80
+ private func show() {
97
81
  navigator.showModal {
98
- PopupNotify(
99
- props: PopupNotifyProps(
100
- image: image,
101
- title: title,
102
- description: description,
103
- error: error,
104
- primary: PopupAction(title: primaryTitle, onPress: { navigator.hideOverplay() }),
105
- secondary: PopupAction(title: secondaryTitle, onPress: { navigator.hideOverplay() }),
106
- buttonDirection: buttonDirection,
107
- onIconClose: { navigator.hideOverplay() },
108
- isShowCloseIcon: showCloseButton
109
- )
110
- )
82
+ PopupNotify(props: PopupNotifyProps(
83
+ image: image,
84
+ title: title,
85
+ description: description,
86
+ error: error,
87
+ primary: PopupAction(title: primaryTitle, onPress: { navigator.hideOverplay() }),
88
+ secondary: PopupAction(title: secondaryTitle, onPress: { navigator.hideOverplay() }),
89
+ buttonDirection: (direction?.value as? PopupActionDirection) ?? .auto,
90
+ onIconClose: { navigator.hideOverplay() },
91
+ isShowCloseIcon: showClose
92
+ ))
111
93
  }
112
94
  }
113
95
  }
114
-
115
- #if DEBUG
116
- struct PopupNotifyDemo_Previews: PreviewProvider {
117
- static var previews: some View {
118
- PopupNotifyDemo()
119
- }
120
- }
121
- #endif
@@ -2,75 +2,66 @@
2
2
  // PopupPromotionDemo.swift
3
3
  // Example
4
4
  //
5
- // Usage demo for PopupPromotion component.
6
- // Mirrors Compose's PromotionPopupUsage: an image-URL form whose action
7
- // button presents the popup through `navigator.showModal`.
5
+ // Preview + Playground demo. PopupPromotion is presented via `navigator.showModal`.
8
6
  //
9
7
 
10
8
  import MoMoUIKits
11
9
  import SwiftUI
12
10
 
13
- // MARK: - PopupPromotionDemo
14
-
15
11
  struct PopupPromotionDemo: View {
16
12
  var body: some View {
17
13
  if #available(iOS 16.0, *) {
18
- PopupPromotionDemoContent()
14
+ DemoScreen(preview: { PopupPromotionPreview() }, playground: { PopupPromotionPlayground() })
19
15
  } else {
20
- Text("PopupPromotion demo requires iOS 16+")
21
- .padding()
16
+ Text("Requires iOS 16+").padding()
22
17
  }
23
18
  }
24
19
  }
25
20
 
26
- // MARK: - PopupPromotionDemoContent
21
+ // MARK: - Preview
27
22
 
28
23
  @available(iOS 16.0, *)
29
- private struct PopupPromotionDemoContent: View {
24
+ private struct PopupPromotionPreview: View {
30
25
  @EnvironmentObject private var navigator: Navigator
31
26
 
32
- @State private var imageUrl = "https://static.momocdn.net/app/app/appx/ABC.png?size=M"
33
-
34
27
  var body: some View {
35
- ScrollView {
36
- VStack(alignment: .leading, spacing: 12) {
37
- Input(
38
- text: $imageUrl,
39
- placeholder: "Image URL",
40
- floatingValue: "Image URL"
41
- )
42
-
43
- Button(
44
- title: "Show Popup",
45
- action: showPopup,
46
- type: .primary,
47
- size: .large
48
- )
49
- .padding(.top, 8)
50
- }
51
- .padding(Spacing.M * 2)
28
+ BasicColumnBox("PopupPromotion", alignment: .leading) {
29
+ MoMoUIKits.Button(
30
+ title: "Show Popup",
31
+ action: { show("https://static.momocdn.net/app/app/appx/ABC.png?size=M") },
32
+ size: .large
33
+ )
52
34
  }
53
35
  }
54
36
 
55
- // MARK: - Helpers
56
-
57
- private func showPopup() {
37
+ private func show(_ url: String) {
58
38
  navigator.showModal {
59
- PopupPromotion(
60
- imageUrl: imageUrl,
61
- onIconClose: { navigator.hideOverplay() },
62
- onAction: { navigator.hideOverplay() }
63
- )
39
+ PopupPromotion(imageUrl: url, onIconClose: { navigator.hideOverplay() }, onAction: { navigator.hideOverplay() })
64
40
  }
65
41
  }
66
42
  }
67
43
 
68
- // MARK: - Preview
44
+ // MARK: - Playground
69
45
 
70
- #if DEBUG
71
- struct PopupPromotionDemo_Previews: PreviewProvider {
72
- static var previews: some View {
73
- PopupPromotionDemo()
46
+ @available(iOS 16.0, *)
47
+ private struct PopupPromotionPlayground: View {
48
+ @EnvironmentObject private var navigator: Navigator
49
+ @State private var imageUrl = "https://static.momocdn.net/app/app/appx/ABC.png?size=M"
50
+
51
+ var body: some View {
52
+ PlaygroundTab(
53
+ component: {
54
+ MoMoUIKits.Button(title: "Show Popup", action: { show() }, size: .large)
55
+ },
56
+ params: [
57
+ .string("ImageURL", $imageUrl),
58
+ ]
59
+ )
60
+ }
61
+
62
+ private func show() {
63
+ navigator.showModal {
64
+ PopupPromotion(imageUrl: imageUrl, onIconClose: { navigator.hideOverplay() }, onAction: { navigator.hideOverplay() })
65
+ }
74
66
  }
75
67
  }
76
- #endif