@momo-kits/native-kits 0.162.30 → 0.162.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/CLAUDE.md +19 -0
  2. package/ios/Application/ApplicationEnvironment.swift +14 -6
  3. package/ios/Application/LanguageSource.swift +93 -0
  4. package/ios/Application/Localize.swift +73 -4
  5. package/ios/Application/LocalizeModifier.swift +30 -0
  6. package/ios/Application/Navigation/NavigationContainer.swift +28 -18
  7. package/ios/Application/Navigation/Navigator.swift +121 -53
  8. package/ios/Application/Navigation/NavigatorManager.swift +49 -0
  9. package/ios/Application/Navigation/component/NavigationFooter.swift +90 -0
  10. package/ios/Application/StackScreen.swift +19 -28
  11. package/ios/Badge/Badge.swift +2 -1
  12. package/ios/Badge/BadgeRibbon.swift +6 -2
  13. package/ios/Button/Button.swift +41 -61
  14. package/ios/Chip/Chip.swift +6 -5
  15. package/ios/Icon/Icon.swift +13 -4
  16. package/ios/Input/ErrorView.swift +2 -1
  17. package/ios/Input/Input.swift +9 -2
  18. package/ios/Input/InputPhoneNumber.swift +2 -1
  19. package/ios/Input/InputSearch.swift +3 -2
  20. package/ios/Input/InputTextArea.swift +6 -1
  21. package/ios/InputMoney/InputMoney.swift +5 -2
  22. package/ios/InputOTP/InputOTP.swift +2 -1
  23. package/ios/Layout/GridContext.swift +30 -0
  24. package/ios/Layout/Item.swift +42 -0
  25. package/ios/Layout/Section.swift +134 -0
  26. package/ios/Popup/PopupNotify.swift +2 -1
  27. package/ios/ProgressInfo/ProgressInfo.swift +4 -3
  28. package/ios/Rating/Rating.swift +2 -1
  29. package/ios/Stepper/Stepper.swift +2 -1
  30. package/ios/SuggestAction/SuggestAction.swift +3 -2
  31. package/ios/Tag/Tag.swift +2 -1
  32. package/ios/Template/TrustBanner/TrustBanner.swift +4 -2
  33. package/ios/Title/Title.swift +3 -2
  34. package/ios/Tooltip/Tooltip.swift +2 -1
  35. package/ios/Typography/FontScale.swift +103 -0
  36. package/ios/Typography/FontScaleStore.swift +52 -13
  37. package/ios/Typography/Text.swift +12 -29
  38. package/ios/Typography/Typography.swift +66 -86
  39. package/ios/native-kits.podspec +2 -1
  40. package/ios-demo/MoMoUIKitsDemo.podspec +1 -1
  41. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/AvatarDemo.swift +87 -73
  42. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/BadgeDemo.swift +55 -249
  43. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ButtonDemo.swift +90 -82
  44. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CarouselDemo.swift +74 -104
  45. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CheckboxDemo.swift +47 -50
  46. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ChipDemo.swift +63 -7
  47. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/CollapseDemo.swift +90 -81
  48. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DateTimePickerDemo.swift +73 -97
  49. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DemoScreen.swift +420 -0
  50. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DividerDemo.swift +46 -4
  51. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/Home.swift +54 -57
  52. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/IconButtonDemo.swift +80 -45
  53. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/IconDemo.swift +81 -0
  54. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ImageDemo.swift +49 -62
  55. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InformationDemo.swift +66 -116
  56. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputDemo.swift +104 -98
  57. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputDropDownDemo.swift +76 -118
  58. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputMoneyDemo.swift +73 -86
  59. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputOTPDemo.swift +69 -54
  60. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputPhoneNumberDemo.swift +74 -54
  61. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputSearchDemo.swift +73 -20
  62. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/InputTextAreaDemo.swift +68 -38
  63. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/LoaderDemo.swift +55 -52
  64. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/LocalizeDemo.swift +51 -4
  65. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/NavigationDemo.swift +14 -2
  66. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PaginationDemo.swift +47 -51
  67. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PlaygroundData.swift +76 -0
  68. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupNotifyDemo.swift +67 -93
  69. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/PopupPromotionDemo.swift +34 -43
  70. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ProgressInfoDemo.swift +83 -71
  71. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/RadioDemo.swift +55 -23
  72. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/RatingDemo.swift +58 -46
  73. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SectionDemo.swift +83 -0
  74. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SkeletonDemo.swift +41 -12
  75. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SliderDemo.swift +67 -127
  76. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SnackBarDemo.swift +54 -110
  77. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/StepperDemo.swift +69 -79
  78. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/StepsDemo.swift +78 -58
  79. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SuggestActionDemo.swift +76 -65
  80. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SwipeDemo.swift +82 -127
  81. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/SwitchDemo.swift +44 -19
  82. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TabViewDemo.swift +92 -168
  83. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TagDemo.swift +79 -27
  84. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ThemeDemo.swift +0 -1
  85. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TitleDemo.swift +87 -92
  86. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TooltipDemo.swift +94 -163
  87. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/TypographyDemo.swift +70 -70
  88. package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/UploaderDemo.swift +68 -114
  89. package/ios-demo/Sources/MoMoUIKitsDemo/SwiftUIDemoBrowserView.swift +3 -1
  90. package/package.json +2 -2
@@ -1,34 +1,86 @@
1
- import SwiftUI
1
+ //
2
+ // TagDemo.swift
3
+ // Example
4
+ //
5
+ // Preview + Playground demo, mirroring Compose's TagUsage.kt.
6
+ //
7
+
2
8
  import MoMoUIKits
9
+ import SwiftUI
10
+
11
+ private let tagColorList: [PlaygroundOption] = [
12
+ .init("default", Tag.TagColor.default),
13
+ .init("orange", Tag.TagColor.orange),
14
+ .init("green", Tag.TagColor.green),
15
+ .init("red", Tag.TagColor.red),
16
+ .init("blue", Tag.TagColor.blue),
17
+ .init("grey", Tag.TagColor.grey),
18
+ ]
19
+
20
+ private let tagSizeList: [PlaygroundOption] = [
21
+ .init("large", Tag.TagSize.large),
22
+ .init("medium", Tag.TagSize.medium),
23
+ ]
3
24
 
4
25
  struct TagDemo: View {
5
26
  var body: some View {
6
- ScrollView {
7
- VStack(alignment: .leading, spacing: 24) {
8
- VStack(alignment: .leading, spacing: 8) {
9
- Text("Color").font(.headline)
10
- Tag()
11
- Tag(color: .red)
12
- Tag(color: .blue)
13
- Tag(color: .green)
14
- Tag(color: .orange)
15
- Tag(color: .grey)
16
- }
17
-
18
- VStack(alignment: .leading, spacing: 8) {
19
- Text("Size").font(.headline)
20
- Tag(size: .large)
21
- Tag(size: .medium)
22
- }
23
-
24
- VStack(alignment: .leading, spacing: 8) {
25
- Text("With Icon").font(.headline)
26
- Tag(icon: "ic_momo", color: .grey)
27
- Tag(icon: "ic_momo", color: .red, size: .medium)
28
- Tag(icon: "ic_momo", color: .blue, size: .medium)
29
- }
30
- }
31
- .padding()
27
+ if #available(iOS 16.0, *) {
28
+ DemoScreen(preview: { TagPreview() }, playground: { TagPlayground() })
29
+ } else {
30
+ Text("Requires iOS 16+").padding()
32
31
  }
33
32
  }
34
33
  }
34
+
35
+ // MARK: - Preview
36
+
37
+ private struct TagPreview: View {
38
+ var body: some View {
39
+ BasicColumnBox("Color", alignment: .leading) {
40
+ Tag()
41
+ Tag(color: .red)
42
+ Tag(color: .blue)
43
+ Tag(color: .green)
44
+ Tag(color: .orange)
45
+ Tag(color: .grey)
46
+ }
47
+ BasicColumnBox("Size", alignment: .leading) {
48
+ Tag(size: .large)
49
+ Tag(size: .medium)
50
+ }
51
+ BasicColumnBox("With Icon", alignment: .leading) {
52
+ Tag(icon: "ic_momo", color: .grey)
53
+ Tag(icon: "ic_momo", color: .red, size: .medium)
54
+ Tag(icon: "ic_momo", color: .blue, size: .medium)
55
+ }
56
+ }
57
+ }
58
+
59
+ // MARK: - Playground
60
+
61
+ @available(iOS 16.0, *)
62
+ private struct TagPlayground: View {
63
+ @State private var label = "Label"
64
+ @State private var color: PlaygroundOption? = tagColorList.first { $0.key == "default" }
65
+ @State private var size: PlaygroundOption? = tagSizeList.first { $0.key == "large" }
66
+ @State private var icon: PlaygroundOption?
67
+
68
+ var body: some View {
69
+ PlaygroundTab(
70
+ component: {
71
+ Tag(
72
+ label: label,
73
+ icon: icon?.value as? String,
74
+ color: (color?.value as? Tag.TagColor) ?? .default,
75
+ size: (size?.value as? Tag.TagSize) ?? .large
76
+ )
77
+ },
78
+ params: [
79
+ .string("Label", $label),
80
+ .radio("Color", tagColorList, $color),
81
+ .radio("Size", tagSizeList, $size),
82
+ .option("Icon", playgroundIcons, $icon),
83
+ ]
84
+ )
85
+ }
86
+ }
@@ -90,7 +90,6 @@ struct ThemeDemo: View {
90
90
  Spacer(minLength: 32)
91
91
  }
92
92
  }
93
- .navigationTitle("Theme")
94
93
  .background(isDarkMode ? Colors.black10.ignoresSafeArea() : theme.colors.background.default.ignoresSafeArea())
95
94
  .environment(\.appTheme, isDarkMode ? darkTheme : defaultTheme)
96
95
  }
@@ -2,108 +2,103 @@
2
2
  // TitleDemo.swift
3
3
  // Example
4
4
  //
5
- import SwiftUI
5
+ // Preview + Playground demo (DemoScreen pattern).
6
+ //
7
+
6
8
  import MoMoUIKits
9
+ import SwiftUI
7
10
 
8
- struct TitleDemo: View {
9
- var body: some View {
10
- ScrollView {
11
- VStack(alignment: .leading, spacing: 24) {
12
- section("Section Type") {
13
- Title(
14
- title: "Section Small",
15
- type: .section,
16
- size: .small
17
- )
18
- Title(
19
- title: "Section Medium",
20
- type: .section,
21
- size: .medium,
22
- icon: "basic_home",
23
- description: "This is a medium section with icon."
24
- )
25
- Title(
26
- title: "Section Large",
27
- type: .section,
28
- size: .large,
29
- showTrailingAction: true,
30
- badgeLabel: "NEW",
31
- onPressTrailingAction: { }
32
- )
33
- }
11
+ private let titleTypeList: [PlaygroundOption] = [
12
+ .init("card", TitleType.card),
13
+ .init("section", TitleType.section),
14
+ ]
15
+
16
+ private let titleSizeList: [PlaygroundOption] = [
17
+ .init("small", TitleSize.small),
18
+ .init("medium", TitleSize.medium),
19
+ .init("large", TitleSize.large),
20
+ ]
34
21
 
35
- section("Card Type") {
36
- Title(
37
- title: "Card Small Text Only",
38
- type: .card,
39
- size: .small,
40
- textOnly: true
41
- )
42
- Title(
43
- title: "Card Medium with Right Action",
44
- type: .card,
45
- size: .medium,
46
- icon: "basic_home",
47
- showRightAction: true,
48
- buttonTitle: "Action",
49
- onPressRightAction: { }
50
- )
51
- Title(
52
- title: "Card Large Full",
53
- type: .card,
54
- size: .large,
55
- icon: "basic_home",
56
- showRightAction: true,
57
- badgeLabel: "99+",
58
- onPressRightAction: { },
59
- description: "A large card title with description, badge and right arrow."
60
- )
61
- }
22
+ private let iconAlignList: [PlaygroundOption] = [
23
+ .init("top", IconAlign.top),
24
+ .init("center", IconAlign.center),
25
+ .init("bottom", IconAlign.bottom),
26
+ ]
62
27
 
63
- section("Icon Alignment") {
64
- Title(
65
- title: "Icon Top",
66
- type: .section,
67
- icon: "basic_home",
68
- iconAlign: .top,
69
- description: "Icon aligned to top"
70
- )
71
- Title(
72
- title: "Icon Center",
73
- type: .section,
74
- icon: "basic_home",
75
- iconAlign: .center,
76
- description: "Icon aligned to center"
77
- )
78
- Title(
79
- title: "Icon Bottom",
80
- type: .section,
81
- icon: "basic_home",
82
- iconAlign: .bottom,
83
- description: "Icon aligned to bottom"
84
- )
85
- }
86
- }
87
- .padding(16)
28
+ struct TitleDemo: View {
29
+ var body: some View {
30
+ if #available(iOS 16.0, *) {
31
+ DemoScreen(preview: { TitlePreview() }, playground: { TitlePlayground() })
32
+ } else {
33
+ Text("Requires iOS 16+").padding()
88
34
  }
89
35
  }
36
+ }
37
+
38
+ // MARK: - Preview
90
39
 
91
- @ViewBuilder
92
- private func section<Content: View>(
93
- _ title: String,
94
- @ViewBuilder content: () -> Content
95
- ) -> some View {
96
- VStack(alignment: .leading, spacing: 8) {
97
- MomoText(title, typography: .headerSSemibold, color: Colors.textSecondary)
98
- content()
40
+ private struct TitlePreview: View {
41
+ var body: some View {
42
+ BasicColumnBox("Section Type", alignment: .leading) {
43
+ Title(title: "Section Small", type: .section, size: .small)
44
+ Title(title: "Section Medium", type: .section, size: .medium, icon: "basic_home",
45
+ description: "This is a medium section with icon.")
46
+ Title(title: "Section Large", type: .section, size: .large,
47
+ showTrailingAction: true, badgeLabel: "NEW", onPressTrailingAction: {})
48
+ }
49
+ BasicColumnBox("Card Type", alignment: .leading) {
50
+ Title(title: "Card Small Text Only", type: .card, size: .small, textOnly: true)
51
+ Title(title: "Card Medium with Right Action", type: .card, size: .medium, icon: "basic_home",
52
+ showRightAction: true, buttonTitle: "Action", onPressRightAction: {})
53
+ }
54
+ BasicColumnBox("Icon Alignment", alignment: .leading) {
55
+ Title(title: "Icon Top", type: .section, icon: "basic_home", iconAlign: .top, description: "Aligned top")
56
+ Title(title: "Icon Center", type: .section, icon: "basic_home", iconAlign: .center, description: "Aligned center")
99
57
  }
100
58
  }
101
59
  }
102
60
 
103
- struct TitleDemo_Previews: PreviewProvider {
104
- static var previews: some View {
105
- NavigationView {
106
- TitleDemo()
107
- }
61
+ // MARK: - Playground
62
+
63
+ @available(iOS 16.0, *)
64
+ private struct TitlePlayground: View {
65
+ @State private var title = "Title"
66
+ @State private var type: PlaygroundOption? = titleTypeList.first { $0.key == "section" }
67
+ @State private var size: PlaygroundOption? = titleSizeList.first { $0.key == "medium" }
68
+ @State private var icon: PlaygroundOption?
69
+ @State private var iconAlign: PlaygroundOption? = iconAlignList.first { $0.key == "top" }
70
+ @State private var description = ""
71
+ @State private var badgeLabel = ""
72
+ @State private var showRightAction = false
73
+ @State private var textOnly = false
74
+
75
+ var body: some View {
76
+ PlaygroundTab(
77
+ component: {
78
+ Title(
79
+ title: title,
80
+ type: (type?.value as? TitleType) ?? .section,
81
+ size: (size?.value as? TitleSize) ?? .medium,
82
+ icon: (icon?.value as? String),
83
+ iconAlign: (iconAlign?.value as? IconAlign) ?? .top,
84
+ showRightAction: showRightAction,
85
+ badgeLabel: badgeLabel.isEmpty ? nil : badgeLabel,
86
+ onPressRightAction: {},
87
+ textOnly: textOnly,
88
+ description: description.isEmpty ? nil : description
89
+ )
90
+ },
91
+ params: [
92
+ .string("Title", $title),
93
+ .radio("Type", titleTypeList, $type),
94
+ .radio("Size", titleSizeList, $size),
95
+ .option("Icon", playgroundIcons, $icon),
96
+ .radio("IconAlign", iconAlignList, $iconAlign),
97
+ .string("Description", $description),
98
+ .string("BadgeLabel", $badgeLabel),
99
+ .checkbox("ShowRightAction", $showRightAction),
100
+ .checkbox("TextOnly", $textOnly),
101
+ ]
102
+ )
108
103
  }
109
104
  }
@@ -2,186 +2,117 @@
2
2
  // TooltipDemo.swift
3
3
  // Example
4
4
  //
5
+ // Preview + Playground demo (mirrors the Compose DemoScreen pattern).
6
+ //
5
7
 
6
- import Foundation
7
8
  import MoMoUIKits
8
9
  import SwiftUI
9
10
 
10
- // MARK: - TooltipDemo
11
-
12
11
  struct TooltipDemo: View {
13
- // Placement
14
- @StateObject private var topState = TooltipState()
15
- @StateObject private var bottomState = TooltipState()
16
- @StateObject private var leftState = TooltipState()
17
- @StateObject private var rightState = TooltipState()
18
-
19
- // Alignment
20
- @StateObject private var startState = TooltipState()
21
- @StateObject private var centerState = TooltipState()
22
- @StateObject private var endState = TooltipState()
12
+ var body: some View {
13
+ if #available(iOS 16.0, *) {
14
+ DemoScreen(preview: { TooltipPreview() }, playground: { TooltipPlayground() })
15
+ } else {
16
+ Text("Requires iOS 16+").padding()
17
+ }
18
+ }
19
+ }
23
20
 
24
- // With Buttons
25
- @StateObject private var singleBtnState = TooltipState()
26
- @StateObject private var twoBtnState = TooltipState()
21
+ // MARK: - Preview
27
22
 
28
- // Description Only
23
+ private struct TooltipPreview: View {
24
+ @StateObject private var topState = TooltipState()
25
+ @StateObject private var bottomState = TooltipState()
26
+ @StateObject private var btnState = TooltipState()
29
27
  @StateObject private var descState = TooltipState()
30
28
 
31
29
  var body: some View {
32
- ScrollView {
33
- VStack(alignment: .leading, spacing: 48) {
34
- // MARK: - Placement
35
-
36
- section("Placement") {
37
- Tooltip(
38
- state: topState,
39
- title: "Top",
40
- description: "Tooltip on top of the anchor.",
41
- placement: .top,
42
- onPressClose: { topState.hide() }
43
- ) {
44
- Button(title: "Top", action: { topState.toggle() }, size: .small, isFull: false)
45
- }
46
-
47
- Tooltip(
48
- state: bottomState,
49
- title: "Bottom",
50
- description: "Tooltip below the anchor.",
51
- placement: .bottom,
52
- onPressClose: { bottomState.hide() }
53
- ) {
54
- Button(title: "Bottom", action: { bottomState.toggle() }, size: .small, isFull: false)
55
- }
56
-
57
- HStack {
58
- Tooltip(
59
- state: rightState,
60
- title: "Right",
61
- description: "Tooltip on the right.",
62
- placement: .right,
63
- onPressClose: { rightState.hide() }
64
- ) {
65
- Button(title: "Right", action: { rightState.toggle() }, size: .small, isFull: false)
66
- }
67
- Spacer().frame(width: 12)
68
- Tooltip(
69
- state: leftState,
70
- title: "Left",
71
- description: "Tooltip on the left.",
72
- placement: .left,
73
- onPressClose: { leftState.hide() }
74
- ) {
75
- Button(title: "Left", action: { leftState.toggle() }, size: .small, isFull: false)
76
- }
77
- }
78
- }
79
-
80
- // MARK: - Alignment
81
-
82
- section("Alignment") {
83
- Tooltip(
84
- state: startState,
85
- title: "Start",
86
- description: "Aligned to start.",
87
- placement: .bottom,
88
- align: .start,
89
- onPressClose: { startState.hide() }
90
- ) {
91
- Button(title: "Start", action: { startState.toggle() }, size: .small, isFull: false)
92
- }
93
-
94
- Tooltip(
95
- state: centerState,
96
- title: "Center",
97
- description: "Aligned to center.",
98
- placement: .bottom,
99
- align: .center,
100
- onPressClose: { centerState.hide() }
101
- ) {
102
- Button(title: "Center", action: { centerState.toggle() }, size: .small, isFull: false)
103
- }
104
-
105
- Tooltip(
106
- state: endState,
107
- title: "End",
108
- description: "Aligned to end.",
109
- placement: .bottom,
110
- align: .end,
111
- onPressClose: { endState.hide() }
112
- ) {
113
- Button(title: "End", action: { endState.toggle() }, size: .small, isFull: false)
114
- }
115
- }
116
-
117
- // MARK: - With Buttons
118
-
119
- section("With Buttons") {
120
- Tooltip(
121
- state: singleBtnState,
122
- title: "Single Button",
123
- description: "Tooltip with one action button.",
124
- buttons: [
125
- TooltipButton(title: "Got it", onPress: { singleBtnState.hide() })
126
- ],
127
- placement: .bottom,
128
- onPressClose: { singleBtnState.hide() }
129
- ) {
130
- Button(title: "1 Button", action: { singleBtnState.toggle() }, size: .small, isFull: false)
131
- }
132
-
133
- Tooltip(
134
- state: twoBtnState,
135
- title: "Two Buttons",
136
- description: "Tooltip with primary and secondary actions.",
137
- buttons: [
138
- TooltipButton(title: "Confirm", onPress: { twoBtnState.hide() }),
139
- TooltipButton(title: "Cancel", onPress: { twoBtnState.hide() })
140
- ],
141
- placement: .bottom,
142
- onPressClose: { twoBtnState.hide() }
143
- ) {
144
- Button(title: "2 Buttons", action: { twoBtnState.toggle() }, size: .small, isFull: false)
145
- }
146
- }
147
-
148
- // MARK: - Description Only
149
-
150
- section("Description Only") {
151
- Tooltip(
152
- state: descState,
153
- description: "This tooltip has no title, just a description.",
154
- placement: .top,
155
- onPressClose: { descState.hide() }
156
- ) {
157
- Button(title: "No Title", action: { descState.toggle() }, size: .small, isFull: false)
158
- }
159
- }
30
+ BasicColumnBox("Placement", alignment: .leading) {
31
+ Tooltip(state: topState, title: "Top", description: "Tooltip on top of the anchor.",
32
+ placement: .top, onPressClose: { topState.hide() }) {
33
+ MoMoUIKits.Button(title: "Top", action: { topState.toggle() }, size: .small, isFull: false)
34
+ }
35
+ Tooltip(state: bottomState, title: "Bottom", description: "Tooltip below the anchor.",
36
+ placement: .bottom, onPressClose: { bottomState.hide() }) {
37
+ MoMoUIKits.Button(title: "Bottom", action: { bottomState.toggle() }, size: .small, isFull: false)
38
+ }
39
+ }
40
+ BasicColumnBox("With Buttons", alignment: .leading) {
41
+ Tooltip(state: btnState, title: "Two Buttons",
42
+ description: "Tooltip with primary and secondary actions.",
43
+ buttons: [
44
+ TooltipButton(title: "Confirm", onPress: { btnState.hide() }),
45
+ TooltipButton(title: "Cancel", onPress: { btnState.hide() }),
46
+ ],
47
+ placement: .bottom, onPressClose: { btnState.hide() }) {
48
+ MoMoUIKits.Button(title: "2 Buttons", action: { btnState.toggle() }, size: .small, isFull: false)
49
+ }
50
+ }
51
+ BasicColumnBox("Description Only", alignment: .leading) {
52
+ Tooltip(state: descState, description: "This tooltip has no title, just a description.",
53
+ placement: .top, onPressClose: { descState.hide() }) {
54
+ MoMoUIKits.Button(title: "No Title", action: { descState.toggle() }, size: .small, isFull: false)
160
55
  }
161
- .padding(.vertical, 48)
162
- .padding(.horizontal, 16)
163
56
  }
164
57
  }
58
+ }
165
59
 
166
- // MARK: - Helpers
60
+ // MARK: - Playground
61
+
62
+ // String-keyed so the demo doesn't depend on the exact enum type name;
63
+ // the literal cases below are resolved against Tooltip's own parameter types.
64
+ private let tooltipPlacementList: [PlaygroundOption] = [
65
+ .init("top", "top"), .init("bottom", "bottom"), .init("left", "left"), .init("right", "right"),
66
+ ]
67
+ private let tooltipAlignList: [PlaygroundOption] = [
68
+ .init("start", "start"), .init("center", "center"), .init("end", "end"),
69
+ ]
70
+
71
+ @available(iOS 16.0, *)
72
+ private struct TooltipPlayground: View {
73
+ @StateObject private var state = TooltipState()
74
+ @State private var title = "Tooltip"
75
+ @State private var desc = "This is a tooltip description."
76
+ @State private var placement: PlaygroundOption? = tooltipPlacementList.first { $0.key == "bottom" }
77
+ @State private var align: PlaygroundOption? = tooltipAlignList.first { $0.key == "center" }
167
78
 
168
- @ViewBuilder
169
- private func section<Content: View>(
170
- _ title: String,
171
- @ViewBuilder content: () -> Content
172
- ) -> some View {
173
- VStack(alignment: .leading, spacing: 24) {
174
- Text(title)
175
- .font(.headline)
176
- content()
79
+ var body: some View {
80
+ PlaygroundTab(
81
+ component: {
82
+ Tooltip(
83
+ state: state,
84
+ title: title,
85
+ description: desc,
86
+ placement: placementCase,
87
+ align: alignCase,
88
+ onPressClose: { state.hide() }
89
+ ) {
90
+ MoMoUIKits.Button(title: "Show tooltip", action: { state.toggle() }, size: .small, isFull: false)
91
+ }
92
+ },
93
+ params: [
94
+ .string("Title", $title),
95
+ .string("Description", $desc),
96
+ .radio("Placement", tooltipPlacementList, $placement),
97
+ .radio("Align", tooltipAlignList, $align),
98
+ ]
99
+ )
100
+ }
101
+
102
+ private var placementCase: TooltipPlacement {
103
+ switch placement?.key {
104
+ case "top": return .top
105
+ case "left": return .left
106
+ case "right": return .right
107
+ default: return .bottom
177
108
  }
178
109
  }
179
- }
180
110
 
181
- #if DEBUG
182
- struct TooltipDemo_Previews: PreviewProvider {
183
- static var previews: some View {
184
- TooltipDemo()
111
+ private var alignCase: TooltipAlign {
112
+ switch align?.key {
113
+ case "start": return .start
114
+ case "end": return .end
115
+ default: return .center
116
+ }
185
117
  }
186
118
  }
187
- #endif