@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
@@ -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
@@ -2,88 +2,100 @@
2
2
  // ProgressInfoDemo.swift
3
3
  // Example
4
4
  //
5
+ // Preview + Playground demo, mirroring Compose's ProgressInfoUsage.kt.
6
+ //
5
7
 
6
- import SwiftUI
7
8
  import MoMoUIKits
9
+ import SwiftUI
8
10
 
9
- // MARK: - ProgressInfoDemo
10
-
11
- struct ProgressInfoDemo: View {
12
- private let sampleSteps = [
13
- ProgressInfoItem(title: "Bước 1", description: "Mô tả bước đầu tiên"),
14
- ProgressInfoItem(title: "Bước 2", description: "Mô tả bước thứ hai"),
15
- ProgressInfoItem(title: "Bước 3", description: "Mô tả bước thứ ba"),
16
- ]
17
-
18
- private let sampleStepsNoDesc = [
19
- ProgressInfoItem(title: "Đăng ký"),
20
- ProgressInfoItem(title: "Xác minh"),
21
- ProgressInfoItem(title: "Hoàn tất"),
22
- ]
23
-
24
- var body: some View {
25
- ScrollView {
26
- VStack(alignment: .leading, spacing: 24) {
27
- section("Vertical (default)") {
28
- ProgressInfo(steps: sampleSteps)
29
- }
30
-
31
- section("Vertical – no description") {
32
- ProgressInfo(steps: sampleStepsNoDesc, showDescription: false)
33
- }
34
-
35
- section("Vertical – useNumber") {
36
- ProgressInfo(steps: sampleSteps, useNumber: true)
37
- }
38
-
39
- section("Vertical – Small size") {
40
- ProgressInfo(steps: sampleSteps, size: .small)
41
- }
11
+ private let piSteps = [
12
+ ProgressInfoItem(title: "Bước 1", description: "Mô tả bước đầu tiên"),
13
+ ProgressInfoItem(title: "Bước 2", description: "Mô tả bước thứ hai"),
14
+ ProgressInfoItem(title: "Bước 3", description: "Mô tả bước thứ ba"),
15
+ ]
42
16
 
43
- section("Horizontal – Center (default)") {
44
- ProgressInfo(steps: sampleSteps, horizontal: true)
45
- }
17
+ private let piStepsNoDesc = [
18
+ ProgressInfoItem(title: "Đăng ký"),
19
+ ProgressInfoItem(title: "Xác minh"),
20
+ ProgressInfoItem(title: "Hoàn tất"),
21
+ ]
46
22
 
47
- section("Horizontal – Left") {
48
- ProgressInfo(steps: sampleSteps, horizontal: true, align: .left)
49
- }
23
+ private let progressInfoSizeList: [PlaygroundOption] = [
24
+ .init("large", ProgressInfoSize.large),
25
+ .init("small", ProgressInfoSize.small),
26
+ ]
50
27
 
51
- section("Horizontal – Right") {
52
- ProgressInfo(steps: sampleSteps, horizontal: true, align: .right)
53
- }
28
+ private let progressInfoAlignList: [PlaygroundOption] = [
29
+ .init("center", ProgressInfoAlign.center),
30
+ .init("left", ProgressInfoAlign.left),
31
+ .init("right", ProgressInfoAlign.right),
32
+ .init("stretch", ProgressInfoAlign.stretch),
33
+ ]
54
34
 
55
- section("Horizontal – Stretch") {
56
- ProgressInfo(steps: sampleSteps, horizontal: true, align: .stretch)
57
- }
35
+ struct ProgressInfoDemo: View {
36
+ var body: some View {
37
+ if #available(iOS 16.0, *) {
38
+ DemoScreen(preview: { ProgressInfoPreview() }, playground: { ProgressInfoPlayground() })
39
+ } else {
40
+ Text("Requires iOS 16+").padding()
41
+ }
42
+ }
43
+ }
58
44
 
59
- section("Horizontal – useNumber") {
60
- ProgressInfo(steps: sampleSteps, horizontal: true, useNumber: true)
61
- }
45
+ // MARK: - Preview
62
46
 
63
- section("Horizontal – Small size") {
64
- ProgressInfo(
65
- steps: sampleStepsNoDesc,
66
- horizontal: true,
67
- size: .small,
68
- showDescription: false
69
- )
70
- }
71
- }
72
- .padding(16)
47
+ private struct ProgressInfoPreview: View {
48
+ var body: some View {
49
+ BasicColumnBox("Vertical (default)", alignment: .leading) {
50
+ ProgressInfo(steps: piSteps)
51
+ }
52
+ BasicColumnBox("Vertical – no description", alignment: .leading) {
53
+ ProgressInfo(steps: piStepsNoDesc, showDescription: false)
54
+ }
55
+ BasicColumnBox("Vertical – useNumber", alignment: .leading) {
56
+ ProgressInfo(steps: piSteps, useNumber: true)
57
+ }
58
+ BasicColumnBox("Horizontal – Center", alignment: .leading) {
59
+ ProgressInfo(steps: piSteps, horizontal: true)
60
+ }
61
+ BasicColumnBox("Horizontal – useNumber", alignment: .leading) {
62
+ ProgressInfo(steps: piSteps, horizontal: true, useNumber: true)
73
63
  }
74
64
  }
65
+ }
75
66
 
76
- @ViewBuilder
77
- private func section<Content: View>(
78
- _ title: String,
79
- @ViewBuilder content: () -> Content
80
- ) -> some View {
81
- VStack(alignment: .leading, spacing: 8) {
82
- Text(title)
83
- .font(.system(size: 14, weight: .semibold))
84
- .foregroundColor(.secondary)
85
- content()
86
- }
87
- .frame(maxWidth: .infinity, alignment: .leading)
67
+ // MARK: - Playground
68
+
69
+ @available(iOS 16.0, *)
70
+ private struct ProgressInfoPlayground: View {
71
+ @State private var size: PlaygroundOption? = progressInfoSizeList.first { $0.key == "large" }
72
+ @State private var align: PlaygroundOption? = progressInfoAlignList.first { $0.key == "center" }
73
+ @State private var horizontal = false
74
+ @State private var useNumber = false
75
+ @State private var showTitle = true
76
+ @State private var showDescription = true
77
+
78
+ var body: some View {
79
+ PlaygroundTab(
80
+ component: {
81
+ ProgressInfo(
82
+ steps: piSteps,
83
+ horizontal: horizontal,
84
+ size: (size?.value as? ProgressInfoSize) ?? .large,
85
+ useNumber: useNumber,
86
+ align: (align?.value as? ProgressInfoAlign) ?? .center,
87
+ showTitle: showTitle,
88
+ showDescription: showDescription
89
+ )
90
+ },
91
+ params: [
92
+ .radio("Size", progressInfoSizeList, $size),
93
+ .radio("Align", progressInfoAlignList, $align),
94
+ .checkbox("Horizontal", $horizontal),
95
+ .checkbox("UseNumber", $useNumber),
96
+ .checkbox("ShowTitle", $showTitle),
97
+ .checkbox("ShowDescription", $showDescription),
98
+ ]
99
+ )
88
100
  }
89
101
  }
@@ -1,32 +1,64 @@
1
- import SwiftUI
1
+ //
2
+ // RadioDemo.swift
3
+ // Example
4
+ //
5
+ // Preview + Playground demo for Radio.
6
+ //
7
+
2
8
  import MoMoUIKits
9
+ import SwiftUI
3
10
 
4
11
  struct RadioDemo: View {
12
+ var body: some View {
13
+ if #available(iOS 16.0, *) {
14
+ DemoScreen(preview: { RadioPreview() }, playground: { RadioPlayground() })
15
+ } else {
16
+ Text("Requires iOS 16+").padding()
17
+ }
18
+ }
19
+ }
20
+
21
+ // MARK: - Preview
22
+
23
+ private struct RadioPreview: View {
5
24
  @State private var group: AnyHashable = 1
6
25
 
7
26
  var body: some View {
8
- VStack(alignment: .leading, spacing: 24) {
9
- // Value
10
- VStack(alignment: .leading, spacing: 12) {
11
- Text("Value").font(.description1)
12
- Radio(value: 1, groupValue: $group)
13
- Radio(value: 2, groupValue: $group)
14
- }
15
-
16
- // Disabled
17
- VStack(alignment: .leading, spacing: 12) {
18
- Text("Disabled").font(.description1)
19
- Radio(value: 1, groupValue: .constant(1), disabled: true)
20
- Radio(value: 2, groupValue: .constant(1), disabled: false)
21
- }
22
-
23
- // Label
24
- VStack(alignment: .leading, spacing: 12) {
25
- Text("Label").font(.description1)
26
- Radio(value: 1, groupValue: .constant(1), label: "Label")
27
- Radio(value: 2, groupValue: .constant(1), label: "Label")
28
- }
27
+ BasicColumnBox("Value", alignment: .leading) {
28
+ Radio(value: 1, groupValue: $group)
29
+ Radio(value: 2, groupValue: $group)
29
30
  }
30
- .padding()
31
+ BasicColumnBox("Disabled", alignment: .leading) {
32
+ Radio(value: 1, groupValue: .constant(1), disabled: true)
33
+ Radio(value: 2, groupValue: .constant(1), disabled: false)
34
+ }
35
+ BasicColumnBox("Label", alignment: .leading) {
36
+ Radio(value: 1, groupValue: .constant(1), label: "Label")
37
+ Radio(value: 2, groupValue: .constant(1), label: "Label")
38
+ }
39
+ }
40
+ }
41
+
42
+ // MARK: - Playground
43
+
44
+ @available(iOS 16.0, *)
45
+ private struct RadioPlayground: View {
46
+ @State private var group: AnyHashable = 1
47
+ @State private var label = "Label"
48
+ @State private var disabled = false
49
+
50
+ var body: some View {
51
+ PlaygroundTab(
52
+ component: {
53
+ VStack(alignment: .leading, spacing: Spacing.S) {
54
+ Radio(value: 1, groupValue: $group, disabled: disabled, label: label)
55
+ Radio(value: 2, groupValue: $group, disabled: disabled, label: label)
56
+ }
57
+ },
58
+ params: [
59
+ .string("Label", $label),
60
+ .checkbox("Disabled", $disabled),
61
+ ]
62
+ )
31
63
  }
32
64
  }
@@ -1,56 +1,68 @@
1
- import SwiftUI
1
+ //
2
+ // RatingDemo.swift
3
+ // Example
4
+ //
5
+ // Preview + Playground demo for Rating.
6
+ //
7
+
2
8
  import MoMoUIKits
9
+ import SwiftUI
3
10
 
4
- struct RatingDemo: View {
5
- @State private var interactiveRating: Float = 3
11
+ private let ratingSizeList: [PlaygroundOption] = [
12
+ .init("small", RatingSize.small),
13
+ .init("medium", RatingSize.medium),
14
+ .init("large", RatingSize.large),
15
+ ]
6
16
 
17
+ struct RatingDemo: View {
7
18
  var body: some View {
8
- ScrollView {
9
- VStack(alignment: .leading, spacing: 24) {
10
- section("Size") {
11
- Rating(numOfStars: 5, rating: 3, size: .small)
12
- Rating(numOfStars: 5, rating: 3, size: .medium)
13
- Rating(numOfStars: 5, rating: 3, size: .large)
14
- }
15
-
16
- section("Half star") {
17
- Rating(numOfStars: 5, rating: 3.5)
18
- }
19
-
20
- section("Full") {
21
- Rating(numOfStars: 5, rating: 5)
22
- }
23
-
24
- section("Empty") {
25
- Rating(numOfStars: 5, rating: 0)
26
- }
27
-
28
- section("Read-only (no callback)") {
29
- Rating(numOfStars: 5, rating: 4)
30
- }
31
-
32
- section("Interactive") {
33
- Rating(
34
- numOfStars: 5,
35
- rating: interactiveRating,
36
- onRatingChange: { newRating in
37
- interactiveRating = Float(newRating)
38
- }
39
- )
40
- }
41
- }
42
- .padding()
19
+ if #available(iOS 16.0, *) {
20
+ DemoScreen(preview: { RatingPreview() }, playground: { RatingPlayground() })
21
+ } else {
22
+ Text("Requires iOS 16+").padding()
43
23
  }
44
24
  }
25
+ }
45
26
 
46
- @ViewBuilder
47
- private func section<Content: View>(
48
- _ title: String,
49
- @ViewBuilder content: () -> Content
50
- ) -> some View {
51
- VStack(alignment: .leading, spacing: 8) {
52
- Text(title)
53
- content()
27
+ // MARK: - Preview
28
+
29
+ private struct RatingPreview: View {
30
+ var body: some View {
31
+ BasicColumnBox("Size", alignment: .leading) {
32
+ Rating(numOfStars: 5, rating: 3, size: .small)
33
+ Rating(numOfStars: 5, rating: 3, size: .medium)
34
+ Rating(numOfStars: 5, rating: 3, size: .large)
54
35
  }
36
+ BasicColumnBox("Values", alignment: .leading) {
37
+ Rating(numOfStars: 5, rating: 3.5)
38
+ Rating(numOfStars: 5, rating: 5)
39
+ Rating(numOfStars: 5, rating: 0)
40
+ }
41
+ }
42
+ }
43
+
44
+ // MARK: - Playground
45
+
46
+ @available(iOS 16.0, *)
47
+ private struct RatingPlayground: View {
48
+ @State private var stars: Double = 5
49
+ @State private var rating: Double = 3
50
+ @State private var size: PlaygroundOption? = ratingSizeList.first { $0.key == "large" }
51
+
52
+ var body: some View {
53
+ PlaygroundTab(
54
+ component: {
55
+ Rating(
56
+ numOfStars: Int(stars),
57
+ rating: Float(rating),
58
+ size: (size?.value as? RatingSize) ?? .large
59
+ )
60
+ },
61
+ params: [
62
+ .slider("NumOfStars", $stars, min: 1, max: 10, step: 1),
63
+ .slider("Rating", $rating, min: 0, max: 10, step: 0.5),
64
+ .radio("Size", ratingSizeList, $size),
65
+ ]
66
+ )
55
67
  }
56
68
  }