@momo-kits/native-kits 0.157.1-skill.1-debug → 0.157.1-skill.3-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.
- package/.claude/momo-native-kits-skill/SKILL.md +51 -0
- package/.claude/momo-native-kits-skill/evals/evals.json +95 -0
- package/.claude/momo-native-kits-skill/references/badge.md +52 -0
- package/.claude/momo-native-kits-skill/references/button.md +182 -0
- package/.claude/momo-native-kits-skill/references/card.md +48 -0
- package/.claude/momo-native-kits-skill/references/checkbox.md +51 -0
- package/.claude/momo-native-kits-skill/references/chip.md +48 -0
- package/.claude/momo-native-kits-skill/references/constants.md +260 -0
- package/.claude/momo-native-kits-skill/references/divider.md +26 -0
- package/.claude/momo-native-kits-skill/references/input.md +408 -0
- package/.claude/momo-native-kits-skill/references/navigation.md +475 -0
- package/.claude/momo-native-kits-skill/references/popup.md +97 -0
- package/.claude/momo-native-kits-skill/references/radio.md +60 -0
- package/.claude/momo-native-kits-skill/references/skeleton.md +40 -0
- package/.claude/momo-native-kits-skill/references/snackbar.md +54 -0
- package/.claude/momo-native-kits-skill/references/switch.md +62 -0
- package/.claude/momo-native-kits-skill/references/text.md +195 -0
- package/.claude/momo-native-kits-skill/references/tooltip.md +34 -0
- package/.claude/momo-native-kits-skill/references/trustbanner.md +61 -0
- package/.claude/momo-native-kits-skill/workspace/iteration-1/benchmark.json +20 -0
- package/.claude/momo-native-kits-skill/workspace/iteration-1/benchmark.md +13 -0
- package/.claude/momo-native-kits-skill/workspace/iteration-1/eval-0-button/eval_metadata.json +6 -0
- package/.claude/momo-native-kits-skill/workspace/iteration-1/eval-0-button/with_skill/outputs/ButtonExample.kt +55 -0
- package/.claude/momo-native-kits-skill/workspace/iteration-1/eval-0-button/without_skill/outputs/ButtonExample.kt +45 -0
- package/.claude/momo-native-kits-skill/workspace/iteration-1/eval-1-input/eval_metadata.json +6 -0
- package/.claude/momo-native-kits-skill/workspace/iteration-1/eval-1-input/with_skill/outputs/InputPhoneExample.kt +40 -0
- package/.claude/momo-native-kits-skill/workspace/iteration-1/eval-1-input/without_skill/outputs/InputPhoneExample.kt +42 -0
- package/.claude/momo-native-kits-skill/workspace/iteration-1/eval-2-bottomtab/eval_metadata.json +6 -0
- package/.claude/momo-native-kits-skill/workspace/iteration-1/eval-2-bottomtab/with_skill/outputs/BottomTabExample.kt +236 -0
- package/.claude/momo-native-kits-skill/workspace/iteration-1/eval-2-bottomtab/without_skill/outputs/BottomTabExample.kt +152 -0
- package/.claude/momo-native-kits-skill/workspace/iteration-1/eval-3-checkbox/eval_metadata.json +6 -0
- package/.claude/momo-native-kits-skill/workspace/iteration-1/eval-3-checkbox/with_skill/outputs/CheckBoxExample.kt +49 -0
- package/.claude/momo-native-kits-skill/workspace/iteration-1/eval-3-checkbox/without_skill/outputs/CheckBoxExample.kt +123 -0
- package/.claude/momo-native-kits-skill/workspace/iteration-1/eval-4-datetimepicker/eval_metadata.json +6 -0
- package/.claude/momo-native-kits-skill/workspace/iteration-1/eval-4-datetimepicker/with_skill/outputs/DateTimePickerExample.kt +318 -0
- package/.claude/momo-native-kits-skill/workspace/iteration-1/eval-4-datetimepicker/without_skill/outputs/DateTimePickerExample.kt +330 -0
- package/.claude/momo-native-kits-skill/workspace/iteration-2/eval-card/with_skill/outputs/CardExample.kt +124 -0
- package/.claude/momo-native-kits-skill/workspace/iteration-2/eval-card/without_skill/outputs/CardExample.kt +71 -0
- package/.claude/momo-native-kits-skill/workspace/iteration-2/eval-loginform/with_skill/outputs/LoginFormExample.kt +134 -0
- package/.claude/momo-native-kits-skill/workspace/iteration-2/eval-loginform/without_skill/outputs/LoginFormExample.kt +199 -0
- package/.claude/momo-native-kits-skill/workspace/iteration-2/eval-navcontainer/with_skill/outputs/NavigationContainerExample.kt +224 -0
- package/.claude/momo-native-kits-skill/workspace/iteration-2/eval-navcontainer/without_skill/outputs/NavigationContainerExample.kt +225 -0
- package/.claude/momo-native-kits-skill/workspace/iteration-2/eval-popup/with_skill/outputs/PopupExample.kt +79 -0
- package/.claude/momo-native-kits-skill/workspace/iteration-2/eval-popup/without_skill/outputs/PopupExample.kt +169 -0
- package/.claude/momo-native-kits-skill/workspace/iteration-2/eval-setoptions/eval_metadata.json +6 -0
- package/.claude/momo-native-kits-skill/workspace/iteration-2/eval-setoptions/with_skill/outputs/SetOptionsExample.kt +255 -0
- package/.claude/momo-native-kits-skill/workspace/iteration-2/eval-setoptions/without_skill/outputs/SetOptionsExample.kt +212 -0
- package/.claude/momo-native-kits-skill/workspace/iteration-2/eval-skeleton/with_skill/outputs/SkeletonExample.kt +199 -0
- package/.claude/momo-native-kits-skill/workspace/iteration-2/eval-skeleton/without_skill/outputs/SkeletonExample.kt +229 -0
- package/.claude/momo-native-kits-skill/workspace/iteration-3/benchmark.json +20 -0
- package/.claude/momo-native-kits-skill/workspace/iteration-3/benchmark.md +13 -0
- package/.claude/momo-native-kits-skill/workspace/iteration-3/eval-button/eval_metadata.json +22 -0
- package/.claude/momo-native-kits-skill/workspace/iteration-3/eval-button/with_skill/outputs/PrimaryButtonExample.kt +38 -0
- package/.claude/momo-native-kits-skill/workspace/iteration-3/eval-button/with_skill/timing.json +5 -0
- package/.claude/momo-native-kits-skill/workspace/iteration-3/eval-button/without_skill/outputs/PrimaryButtonExample.kt +83 -0
- package/.claude/momo-native-kits-skill/workspace/iteration-3/eval-button/without_skill/timing.json +5 -0
- package/.claude/momo-native-kits-skill/workspace/iteration-3/eval-navcontainer/eval_metadata.json +22 -0
- package/.claude/momo-native-kits-skill/workspace/iteration-3/eval-navcontainer/with_skill/outputs/NavigationContainerExample.kt +547 -0
- package/.claude/momo-native-kits-skill/workspace/iteration-3/eval-navcontainer/with_skill/timing.json +5 -0
- package/.claude/momo-native-kits-skill/workspace/iteration-3/eval-navcontainer/without_skill/outputs/MoMoNavigationContainer.kt +519 -0
- package/.claude/momo-native-kits-skill/workspace/iteration-3/eval-navcontainer/without_skill/timing.json +5 -0
- package/.claude/momo-native-kits-skill/workspace/iteration-3/eval-setoptions/eval_metadata.json +27 -0
- package/.claude/momo-native-kits-skill/workspace/iteration-3/eval-setoptions/with_skill/outputs/SetOptionsExample.kt +429 -0
- package/.claude/momo-native-kits-skill/workspace/iteration-3/eval-setoptions/with_skill/timing.json +5 -0
- package/.claude/momo-native-kits-skill/workspace/iteration-3/eval-setoptions/without_skill/outputs/SetOptionsExample.kt +353 -0
- package/.claude/momo-native-kits-skill/workspace/iteration-3/eval-setoptions/without_skill/timing.json +5 -0
- package/.claude/settings.local.json +35 -7
- package/compose/build.gradle.kts +1 -1
- package/gradle.properties +1 -1
- package/ios/Input/Input.swift +112 -50
- package/local.properties +1 -1
- package/package.json +1 -1
- package/.claude/momo-native-kits/SKILL.md +0 -87
- package/.claude/momo-native-kits/references/Badge.md +0 -39
- package/.claude/momo-native-kits/references/BadgeDot.md +0 -37
- package/.claude/momo-native-kits/references/BottomSheet.md +0 -51
- package/.claude/momo-native-kits/references/BottomTab.md +0 -65
- package/.claude/momo-native-kits/references/Button.md +0 -197
- package/.claude/momo-native-kits/references/CheckBox.md +0 -51
- package/.claude/momo-native-kits/references/Chip.md +0 -47
- package/.claude/momo-native-kits/references/Divider.md +0 -29
- package/.claude/momo-native-kits/references/HeaderTitle.md +0 -45
- package/.claude/momo-native-kits/references/HeaderType.md +0 -47
- package/.claude/momo-native-kits/references/Icon.md +0 -32
- package/.claude/momo-native-kits/references/Image.md +0 -38
- package/.claude/momo-native-kits/references/Information.md +0 -36
- package/.claude/momo-native-kits/references/Input.md +0 -334
- package/.claude/momo-native-kits/references/InputDropDown.md +0 -47
- package/.claude/momo-native-kits/references/InputMoney.md +0 -241
- package/.claude/momo-native-kits/references/InputOTP.md +0 -52
- package/.claude/momo-native-kits/references/InputPhoneNumber.md +0 -175
- package/.claude/momo-native-kits/references/InputSearch.md +0 -57
- package/.claude/momo-native-kits/references/InputTextArea.md +0 -46
- package/.claude/momo-native-kits/references/NavigationContainer.md +0 -51
- package/.claude/momo-native-kits/references/Navigator.md +0 -287
- package/.claude/momo-native-kits/references/PaginationDot.md +0 -28
- package/.claude/momo-native-kits/references/PaginationNumber.md +0 -28
- package/.claude/momo-native-kits/references/PopupNotify.md +0 -47
- package/.claude/momo-native-kits/references/Radio.md +0 -44
- package/.claude/momo-native-kits/references/Skeleton.md +0 -32
- package/.claude/momo-native-kits/references/Switch.md +0 -36
- package/.claude/momo-native-kits/references/Tag.md +0 -40
- package/.claude/momo-native-kits/references/Text.md +0 -37
- package/.claude/momo-native-kits/references/Title.md +0 -43
- package/.claude/momo-native-kits/references/Tooltip.md +0 -30
- package/building-skill-for-claude.md +0 -1190
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
# Navigation System
|
|
2
|
+
|
|
3
|
+
## Key Points (IMPORTANT)
|
|
4
|
+
|
|
5
|
+
- **ONE NavigationContainer per Fragment** - All screens in one container
|
|
6
|
+
- **setOptions in LaunchedEffect(Unit)** - NOT in composable root
|
|
7
|
+
- **LocalNavigation.current** - For setOptions (header, scroll, footer)
|
|
8
|
+
- **LocalNavigator.current** - For push/pop/present/showModal/showBottomSheet
|
|
9
|
+
|
|
10
|
+
## Overview
|
|
11
|
+
|
|
12
|
+
MoMo Native Kits provides a comprehensive navigation system built on top of Jetpack Navigation Compose.
|
|
13
|
+
|
|
14
|
+
**Package:** `vn.momo.kits.navigation`
|
|
15
|
+
|
|
16
|
+
## Architecture
|
|
17
|
+
|
|
18
|
+
### Single NavigationContainer Rule
|
|
19
|
+
|
|
20
|
+
**IMPORTANT:** There should only be **ONE** `NavigationContainer` per Fragment/Activity. All screens are managed within this single container.
|
|
21
|
+
|
|
22
|
+
```kotlin
|
|
23
|
+
// ✅ CORRECT - Single NavigationContainer
|
|
24
|
+
@Composable
|
|
25
|
+
fun MyApp() {
|
|
26
|
+
NavigationContainer(
|
|
27
|
+
initialScreen = {
|
|
28
|
+
HomeScreen()
|
|
29
|
+
},
|
|
30
|
+
options = NavigationOptions(
|
|
31
|
+
headerTitle = HeaderTitle.Default("My App")
|
|
32
|
+
)
|
|
33
|
+
) {
|
|
34
|
+
// All screens pushed here
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Navigation Context
|
|
40
|
+
|
|
41
|
+
Within any screen, access navigation through:
|
|
42
|
+
|
|
43
|
+
```kotlin
|
|
44
|
+
val navigator = LocalNavigator.current // For screen navigation (push, pop, etc.)
|
|
45
|
+
val navigation = LocalNavigation.current // For setting screen options
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## NavigationContainer
|
|
51
|
+
|
|
52
|
+
**File:** `vn/momo/kits/navigation/NavigationContainer.kt`
|
|
53
|
+
|
|
54
|
+
### Parameters
|
|
55
|
+
|
|
56
|
+
```kotlin
|
|
57
|
+
@Composable
|
|
58
|
+
fun NavigationContainer(
|
|
59
|
+
initialScreen: @Composable () -> Unit, // First screen to show
|
|
60
|
+
options: NavigationOptions = NavigationOptions(), // Global options
|
|
61
|
+
modifier: Modifier = Modifier,
|
|
62
|
+
applicationContext: MiniAppContext? = null
|
|
63
|
+
)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### NavigationOptions
|
|
67
|
+
|
|
68
|
+
```kotlin
|
|
69
|
+
data class NavigationOptions(
|
|
70
|
+
val headerTitle: HeaderTitle? = null,
|
|
71
|
+
val headerType: HeaderType = HeaderType.DEFAULT,
|
|
72
|
+
val headerRight: @Composable () -> Unit = {},
|
|
73
|
+
val hiddenBack: Boolean = false,
|
|
74
|
+
val footerComponent: @Composable () -> Unit = {},
|
|
75
|
+
val floatingButtonProps: FloatingButtonProps? = null,
|
|
76
|
+
val scrollData: ScrollData? = null,
|
|
77
|
+
val keyboardOptions: KeyboardOptions? = null,
|
|
78
|
+
val statusBarStyle: StatusBarStyle = StatusBarStyle.LIGHT_CONTENT,
|
|
79
|
+
val statusBarColor: Color? = null
|
|
80
|
+
)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Full Example
|
|
84
|
+
|
|
85
|
+
```kotlin
|
|
86
|
+
import vn.momo.kits.navigation.NavigationContainer
|
|
87
|
+
import vn.momo.kits.navigation.NavigationOptions
|
|
88
|
+
import vn.momo.kits.navigation.component.HeaderRight
|
|
89
|
+
import vn.momo.kits.navigation.component.HeaderTitle
|
|
90
|
+
import vn.momo.kits.navigation.component.HeaderType
|
|
91
|
+
|
|
92
|
+
@Composable
|
|
93
|
+
fun MyApp() {
|
|
94
|
+
val inputSearch = remember { mutableStateOf("") }
|
|
95
|
+
|
|
96
|
+
Box(Modifier.fillMaxSize()) {
|
|
97
|
+
NavigationContainer(
|
|
98
|
+
initialScreen = {
|
|
99
|
+
HomeScreen()
|
|
100
|
+
},
|
|
101
|
+
options = NavigationOptions(
|
|
102
|
+
headerTitle = HeaderTitle.Default("MoMo App"),
|
|
103
|
+
headerType = HeaderType.Extended(
|
|
104
|
+
useAnimated = true,
|
|
105
|
+
inputSearchProps = InputSearchProps(
|
|
106
|
+
text = inputSearch,
|
|
107
|
+
placeholder = "Search...",
|
|
108
|
+
onChangeText = { inputSearch.value = it }
|
|
109
|
+
)
|
|
110
|
+
),
|
|
111
|
+
headerRight = {
|
|
112
|
+
HeaderRight.Toolkit(useShortcut = true)
|
|
113
|
+
},
|
|
114
|
+
hiddenBack = false,
|
|
115
|
+
footerComponent = {
|
|
116
|
+
Button(title = "Footer Button", onClick = {})
|
|
117
|
+
},
|
|
118
|
+
floatingButtonProps = FloatingButtonProps(
|
|
119
|
+
icon = "ic_warning",
|
|
120
|
+
iconColor = Color.White,
|
|
121
|
+
label = "Help",
|
|
122
|
+
onClick = { /* Handle click */ },
|
|
123
|
+
size = FABSize.DEFAULT,
|
|
124
|
+
position = FABPosition.END
|
|
125
|
+
)
|
|
126
|
+
)
|
|
127
|
+
)
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## setOptions - Dynamic Screen Configuration
|
|
135
|
+
|
|
136
|
+
`setOptions` is used to dynamically change screen options **after** the screen has been pushed. This is called inside `LaunchedEffect(Unit)`.
|
|
137
|
+
|
|
138
|
+
### When to Use setOptions
|
|
139
|
+
|
|
140
|
+
1. **On screen initialization** - Set header type, scroll behavior
|
|
141
|
+
2. **In response to user actions** - Change header title, show/hide components
|
|
142
|
+
3. **To configure scroll behavior** - Connect scroll state to header animation
|
|
143
|
+
|
|
144
|
+
### Basic Usage
|
|
145
|
+
|
|
146
|
+
```kotlin
|
|
147
|
+
@Composable
|
|
148
|
+
fun MyScreen() {
|
|
149
|
+
val navigation = LocalNavigation.current
|
|
150
|
+
|
|
151
|
+
LaunchedEffect(Unit) {
|
|
152
|
+
navigation.setOptions(
|
|
153
|
+
headerTitle = HeaderTitle.Default("My Screen"),
|
|
154
|
+
headerType = HeaderType.DEFAULT
|
|
155
|
+
)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Screen content
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Change Header Title Dynamically
|
|
163
|
+
|
|
164
|
+
```kotlin
|
|
165
|
+
@Composable
|
|
166
|
+
fun DynamicTitleScreen() {
|
|
167
|
+
val navigation = LocalNavigation.current
|
|
168
|
+
val title = remember { mutableStateOf("") }
|
|
169
|
+
|
|
170
|
+
Column(modifier = Modifier.padding(16.dp)) {
|
|
171
|
+
Input(
|
|
172
|
+
text = title,
|
|
173
|
+
onChangeText = { title.value = it },
|
|
174
|
+
floatingValue = "Enter title"
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
Button(
|
|
178
|
+
title = "Change Title",
|
|
179
|
+
onClick = {
|
|
180
|
+
navigation.setOptions(
|
|
181
|
+
headerTitle = HeaderTitle.Default(title.value)
|
|
182
|
+
)
|
|
183
|
+
}
|
|
184
|
+
)
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Configure Scroll Behavior
|
|
190
|
+
|
|
191
|
+
```kotlin
|
|
192
|
+
@Composable
|
|
193
|
+
fun ScrollableScreen() {
|
|
194
|
+
val navigation = LocalNavigation.current
|
|
195
|
+
val scrollState = rememberScrollState()
|
|
196
|
+
|
|
197
|
+
LaunchedEffect(Unit) {
|
|
198
|
+
navigation.setOptions(
|
|
199
|
+
scrollData = ScrollData(
|
|
200
|
+
scrollState = scrollState
|
|
201
|
+
)
|
|
202
|
+
)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
LazyColumn(state = scrollState) {
|
|
206
|
+
// Content
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Footer Component
|
|
212
|
+
|
|
213
|
+
```kotlin
|
|
214
|
+
@Composable
|
|
215
|
+
fun ScreenWithFooter() {
|
|
216
|
+
val navigation = LocalNavigation.current
|
|
217
|
+
|
|
218
|
+
LaunchedEffect(Unit) {
|
|
219
|
+
navigation.setOptions(
|
|
220
|
+
footerComponent = {
|
|
221
|
+
Button(
|
|
222
|
+
title = "Submit",
|
|
223
|
+
onClick = { /* Submit */ }
|
|
224
|
+
)
|
|
225
|
+
}
|
|
226
|
+
)
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Screen content
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Prevent Back with Confirmation
|
|
234
|
+
|
|
235
|
+
```kotlin
|
|
236
|
+
@Composable
|
|
237
|
+
fun PreventBackScreen() {
|
|
238
|
+
val navigation = LocalNavigation.current
|
|
239
|
+
val navigator = LocalNavigator.current
|
|
240
|
+
|
|
241
|
+
LaunchedEffect(Unit) {
|
|
242
|
+
navigation.setOptions(
|
|
243
|
+
headerBackProps = HeaderBackProps(
|
|
244
|
+
preventBack = PopupNotifyProps(
|
|
245
|
+
onIconClose = { navigator.pop() },
|
|
246
|
+
primary = PopupAction(
|
|
247
|
+
title = "Go back",
|
|
248
|
+
onPress = { navigator.pop() }
|
|
249
|
+
),
|
|
250
|
+
secondary = PopupAction(
|
|
251
|
+
title = "Stay",
|
|
252
|
+
onPress = { /* Do nothing */ }
|
|
253
|
+
)
|
|
254
|
+
)
|
|
255
|
+
)
|
|
256
|
+
)
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
## Navigator - Screen Navigation
|
|
264
|
+
|
|
265
|
+
Use `LocalNavigator.current` for navigation actions.
|
|
266
|
+
|
|
267
|
+
### Navigation Actions
|
|
268
|
+
|
|
269
|
+
```kotlin
|
|
270
|
+
val navigator = LocalNavigator.current
|
|
271
|
+
|
|
272
|
+
// Push a new screen
|
|
273
|
+
navigator.push({ ScreenContent() })
|
|
274
|
+
|
|
275
|
+
// Push with options
|
|
276
|
+
navigator.push(
|
|
277
|
+
content = { ScreenContent() },
|
|
278
|
+
options = NavigationOptions(
|
|
279
|
+
headerTitle = HeaderTitle.Default("New Screen")
|
|
280
|
+
)
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
// Pop current screen
|
|
284
|
+
navigator.pop()
|
|
285
|
+
|
|
286
|
+
// Pop with callback
|
|
287
|
+
navigator.pop(callBack = { /* Called after pop */ })
|
|
288
|
+
|
|
289
|
+
// Present (modal)
|
|
290
|
+
navigator.present({ ModalContent() })
|
|
291
|
+
|
|
292
|
+
// Replace current screen
|
|
293
|
+
navigator.replace({ NewScreen() })
|
|
294
|
+
|
|
295
|
+
// Reset to new screen (clear stack)
|
|
296
|
+
navigator.reset({ NewStartScreen() })
|
|
297
|
+
|
|
298
|
+
// Show modal overlay
|
|
299
|
+
navigator.showModal(
|
|
300
|
+
content = { /* Modal content */ },
|
|
301
|
+
barrierDismissible = true,
|
|
302
|
+
onDismiss = { /* Handle dismiss */ }
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
// Show bottom sheet
|
|
306
|
+
navigator.showBottomSheet(
|
|
307
|
+
content = { /* Sheet content */ },
|
|
308
|
+
barrierDismissible = true,
|
|
309
|
+
onDismiss = { /* Handle dismiss */ }
|
|
310
|
+
)
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
---
|
|
314
|
+
|
|
315
|
+
## Header Types
|
|
316
|
+
|
|
317
|
+
### HeaderType.None
|
|
318
|
+
```kotlin
|
|
319
|
+
headerType = HeaderType.None // No header
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
### HeaderType.Default
|
|
323
|
+
```kotlin
|
|
324
|
+
headerType = HeaderType.Default
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
### HeaderType.Extended
|
|
328
|
+
```kotlin
|
|
329
|
+
headerType = HeaderType.Extended(
|
|
330
|
+
useAnimated = true,
|
|
331
|
+
inputSearchProps = InputSearchProps(
|
|
332
|
+
text = searchState,
|
|
333
|
+
placeholder = "Search...",
|
|
334
|
+
onChangeText = { /* Handle */ }
|
|
335
|
+
)
|
|
336
|
+
)
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
### HeaderType.Animated
|
|
340
|
+
```kotlin
|
|
341
|
+
headerType = HeaderType.Animated(
|
|
342
|
+
isSurface = true,
|
|
343
|
+
aspectRatio = AnimatedHeaderRatio.RATIO_16_9,
|
|
344
|
+
composable = {
|
|
345
|
+
Image(
|
|
346
|
+
source = "https://example.com/header.jpg",
|
|
347
|
+
modifier = Modifier.fillMaxSize()
|
|
348
|
+
)
|
|
349
|
+
}
|
|
350
|
+
)
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
---
|
|
354
|
+
|
|
355
|
+
## Bottom Tab Navigation
|
|
356
|
+
|
|
357
|
+
**File:** `vn.momo/kits/navigation/bottomtab/BottomTab.kt`
|
|
358
|
+
|
|
359
|
+
### Parameters
|
|
360
|
+
|
|
361
|
+
```kotlin
|
|
362
|
+
@Composable
|
|
363
|
+
fun BottomTab(
|
|
364
|
+
items: List<BottomTabItem>, // List of tabs
|
|
365
|
+
floatingButton: BottomTabFloatingButton? = null // Optional FAB
|
|
366
|
+
)
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
### BottomTabItem
|
|
370
|
+
|
|
371
|
+
```kotlin
|
|
372
|
+
data class BottomTabItem(
|
|
373
|
+
val name: String, // Tab identifier
|
|
374
|
+
val label: String, // Tab label
|
|
375
|
+
val icon: String, // Icon name
|
|
376
|
+
val showDot: Boolean = false, // Show notification dot
|
|
377
|
+
val badgeLabel: String? = null, // Badge text
|
|
378
|
+
val screen: @Composable () -> Unit, // Screen content
|
|
379
|
+
val options: NavigationOptions? = null, // Tab-specific options
|
|
380
|
+
val initialParams: Any? = null // Initial params
|
|
381
|
+
)
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
### BottomTabFloatingButton
|
|
385
|
+
|
|
386
|
+
```kotlin
|
|
387
|
+
data class BottomTabFloatingButton(
|
|
388
|
+
val icon: String, // FAB icon
|
|
389
|
+
val label: String, // FAB label
|
|
390
|
+
val onPress: () -> Unit // Click handler
|
|
391
|
+
)
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
### Example
|
|
395
|
+
|
|
396
|
+
```kotlin
|
|
397
|
+
@Composable
|
|
398
|
+
fun MainScreen() {
|
|
399
|
+
val navigator = LocalNavigator.current
|
|
400
|
+
|
|
401
|
+
BottomTab(
|
|
402
|
+
items = listOf(
|
|
403
|
+
BottomTabItem(
|
|
404
|
+
name = "home",
|
|
405
|
+
label = "Trang chủ",
|
|
406
|
+
icon = "ic_home",
|
|
407
|
+
screen = { HomeScreen() },
|
|
408
|
+
options = NavigationOptions(
|
|
409
|
+
headerTitle = HeaderTitle.Default("Trang chủ")
|
|
410
|
+
)
|
|
411
|
+
),
|
|
412
|
+
BottomTabItem(
|
|
413
|
+
name = "wallet",
|
|
414
|
+
label = "Ví của tôi",
|
|
415
|
+
icon = "ic_wallet",
|
|
416
|
+
screen = { WalletScreen() },
|
|
417
|
+
options = NavigationOptions(
|
|
418
|
+
headerTitle = HeaderTitle.Default("Ví của tôi")
|
|
419
|
+
),
|
|
420
|
+
badgeLabel = "3"
|
|
421
|
+
),
|
|
422
|
+
BottomTabItem(
|
|
423
|
+
name = "history",
|
|
424
|
+
label = "Lịch sử",
|
|
425
|
+
icon = "ic_history",
|
|
426
|
+
screen = { HistoryScreen() }
|
|
427
|
+
),
|
|
428
|
+
BottomTabItem(
|
|
429
|
+
name = "profile",
|
|
430
|
+
label = "Tài khoản",
|
|
431
|
+
icon = "ic_profile",
|
|
432
|
+
screen = { ProfileScreen() }
|
|
433
|
+
)
|
|
434
|
+
),
|
|
435
|
+
floatingButton = BottomTabFloatingButton(
|
|
436
|
+
icon = "ic_qr",
|
|
437
|
+
label = "Quét QR",
|
|
438
|
+
onPress = { navigator.push({ QRScannerScreen() }) }
|
|
439
|
+
)
|
|
440
|
+
)
|
|
441
|
+
}
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
### Changing Tab Header Dynamically
|
|
445
|
+
|
|
446
|
+
```kotlin
|
|
447
|
+
BottomTabItem(
|
|
448
|
+
name = "tab2",
|
|
449
|
+
label = "Tab 2",
|
|
450
|
+
icon = "ic_tab",
|
|
451
|
+
screen = {
|
|
452
|
+
Box(Modifier.fillMaxSize().background(Color.Red)) {
|
|
453
|
+
val navigation = LocalNavigation.current
|
|
454
|
+
Button(onClick = {
|
|
455
|
+
navigation.setOptions(
|
|
456
|
+
headerTitle = HeaderTitle.Default("Changed Title")
|
|
457
|
+
)
|
|
458
|
+
}, title = "Change Title")
|
|
459
|
+
}
|
|
460
|
+
},
|
|
461
|
+
options = NavigationOptions(headerTitle = HeaderTitle.Default("Tab 2"))
|
|
462
|
+
)
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
---
|
|
466
|
+
|
|
467
|
+
## Best Practices
|
|
468
|
+
|
|
469
|
+
1. **One NavigationContainer per Fragment** - Don't nest containers
|
|
470
|
+
2. **Use setOptions in LaunchedEffect** - Set options when screen loads
|
|
471
|
+
3. **Use NavigationOptions in push()** - Set options when pushing screens
|
|
472
|
+
4. **Connect scroll state early** - Set scrollData in LaunchedEffect
|
|
473
|
+
5. **Use meaningful screen names** - For debugging and analytics
|
|
474
|
+
6. **Handle back press properly** - Use preventBack for confirmation dialogs
|
|
475
|
+
7. **BottomTab wraps entire app** - It's the root navigation
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# PopupNotify
|
|
2
|
+
|
|
3
|
+
**File:** `vn.momo/kits/components/PopupNotify.kt`
|
|
4
|
+
|
|
5
|
+
## Parameters
|
|
6
|
+
|
|
7
|
+
```kotlin
|
|
8
|
+
@Composable
|
|
9
|
+
fun PopupNotify(
|
|
10
|
+
props: PopupNotifyProps
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
data class PopupNotifyProps(
|
|
14
|
+
val title: String,
|
|
15
|
+
val message: String? = null,
|
|
16
|
+
val type: PopupNotifyType = PopupNotifyType.INFO,
|
|
17
|
+
val primary: PopupAction? = null,
|
|
18
|
+
val secondary: PopupAction? = null,
|
|
19
|
+
val onIconClose: (() -> Unit)? = null,
|
|
20
|
+
val isVisible: Boolean = true
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
data class PopupAction(
|
|
24
|
+
val title: String,
|
|
25
|
+
val onPress: () -> Unit
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
enum class PopupNotifyType {
|
|
29
|
+
SUCCESS, ERROR, WARNING, INFO
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Basic
|
|
34
|
+
|
|
35
|
+
```kotlin
|
|
36
|
+
PopupNotify(
|
|
37
|
+
props = PopupNotifyProps(
|
|
38
|
+
title = "Thành công",
|
|
39
|
+
message = "Giao dịch đã được xử lý",
|
|
40
|
+
type = PopupNotifyType.SUCCESS,
|
|
41
|
+
onIconClose = { showPopup = false }
|
|
42
|
+
)
|
|
43
|
+
)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## With Actions
|
|
47
|
+
|
|
48
|
+
```kotlin
|
|
49
|
+
PopupNotify(
|
|
50
|
+
props = PopupNotifyProps(
|
|
51
|
+
title = "Xác nhận",
|
|
52
|
+
message = "Bạn có chắc muốn xóa?",
|
|
53
|
+
type = PopupNotifyType.WARNING,
|
|
54
|
+
primary = PopupAction(title = "Xóa", onPress = { /* delete */ }),
|
|
55
|
+
secondary = PopupAction(title = "Hủy", onPress = { /* cancel */ }),
|
|
56
|
+
onIconClose = { showPopup = false }
|
|
57
|
+
)
|
|
58
|
+
)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
# PopupPromotion
|
|
64
|
+
|
|
65
|
+
**File:** `vn/momo/kits/components/PopupPromotion.kt`
|
|
66
|
+
|
|
67
|
+
## Parameters
|
|
68
|
+
|
|
69
|
+
```kotlin
|
|
70
|
+
@Composable
|
|
71
|
+
fun PopupPromotion(
|
|
72
|
+
isVisible: Boolean,
|
|
73
|
+
imageUrl: String,
|
|
74
|
+
title: String = "",
|
|
75
|
+
description: String = "",
|
|
76
|
+
primaryButtonText: String = "",
|
|
77
|
+
secondaryButtonText: String = "",
|
|
78
|
+
onPrimaryClick: () -> Unit = {},
|
|
79
|
+
onSecondaryClick: () -> Unit = {},
|
|
80
|
+
onDismiss: () -> Unit
|
|
81
|
+
)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Usage
|
|
85
|
+
|
|
86
|
+
```kotlin
|
|
87
|
+
PopupPromotion(
|
|
88
|
+
isVisible = showPromotion,
|
|
89
|
+
imageUrl = "https://example.com/promotion.jpg",
|
|
90
|
+
title = "Giảm 50%",
|
|
91
|
+
description = "Ưu đãi đặc biệt",
|
|
92
|
+
primaryButtonText = "Nhận ngay",
|
|
93
|
+
secondaryButtonText = "Để sau",
|
|
94
|
+
onPrimaryClick = { /* claim */ },
|
|
95
|
+
onDismiss = { showPromotion = false }
|
|
96
|
+
)
|
|
97
|
+
```
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Radio
|
|
2
|
+
|
|
3
|
+
**File:** `vn.momo/kits/components/Radio.kt`
|
|
4
|
+
|
|
5
|
+
## Parameters
|
|
6
|
+
|
|
7
|
+
```kotlin
|
|
8
|
+
@Composable
|
|
9
|
+
fun Radio(
|
|
10
|
+
value: T, // Required: This radio's value
|
|
11
|
+
onChange: (T) -> Unit, // Required: Selection callback
|
|
12
|
+
groupValue: T, // Required: Currently selected value
|
|
13
|
+
modifier: Modifier = Modifier,
|
|
14
|
+
label: String = "", // Label text
|
|
15
|
+
enabled: Boolean = true
|
|
16
|
+
)
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
```kotlin
|
|
22
|
+
var selectedOption by remember { mutableStateOf("option1") }
|
|
23
|
+
|
|
24
|
+
Column {
|
|
25
|
+
Radio(
|
|
26
|
+
value = "option1",
|
|
27
|
+
onChange = { selectedOption = it },
|
|
28
|
+
groupValue = selectedOption,
|
|
29
|
+
label = "Option 1"
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
Radio(
|
|
33
|
+
value = "option2",
|
|
34
|
+
onChange = { selectedOption = it },
|
|
35
|
+
groupValue = selectedOption,
|
|
36
|
+
label = "Option 2"
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## With Enum
|
|
42
|
+
|
|
43
|
+
```kotlin
|
|
44
|
+
enum class Gender { MALE, FEMALE, OTHER }
|
|
45
|
+
|
|
46
|
+
var gender by remember { mutableStateOf(Gender.MALE) }
|
|
47
|
+
|
|
48
|
+
Gender.entries.forEach { g ->
|
|
49
|
+
Radio(
|
|
50
|
+
value = g,
|
|
51
|
+
onChange = { gender = it },
|
|
52
|
+
groupValue = gender,
|
|
53
|
+
label = when (g) {
|
|
54
|
+
Gender.MALE -> "Nam"
|
|
55
|
+
Gender.FEMALE -> "Nữ"
|
|
56
|
+
Gender.OTHER -> "Khác"
|
|
57
|
+
}
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
```
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Skeleton
|
|
2
|
+
|
|
3
|
+
**File:** `vn.momo/kits/components/Skeleton.kt`
|
|
4
|
+
|
|
5
|
+
## Parameters
|
|
6
|
+
|
|
7
|
+
```kotlin
|
|
8
|
+
@Composable
|
|
9
|
+
fun Skeleton(
|
|
10
|
+
modifier: Modifier = Modifier,
|
|
11
|
+
width: Dp? = null, // Specific width or null for wrap
|
|
12
|
+
height: Dp = 20.dp, // Height
|
|
13
|
+
cornerRadius: Dp = Radius.S, // Corner radius
|
|
14
|
+
animate: Boolean = true // Enable animation
|
|
15
|
+
)
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
```kotlin
|
|
21
|
+
@Composable
|
|
22
|
+
fun LoadingCard() {
|
|
23
|
+
Card(modifier = Modifier.fillMaxWidth()) {
|
|
24
|
+
Column(modifier = Modifier.padding(16.dp)) {
|
|
25
|
+
Row {
|
|
26
|
+
Skeleton(width = 60.dp, height = 60.dp, cornerRadius = 8.dp)
|
|
27
|
+
Column(modifier = Modifier.padding(start = 12.dp)) {
|
|
28
|
+
Skeleton(width = 120.dp)
|
|
29
|
+
Spacer(modifier = Modifier.height(8.dp))
|
|
30
|
+
Skeleton(width = 80.dp)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
Spacer(modifier = Modifier.height(12.dp))
|
|
34
|
+
Skeleton()
|
|
35
|
+
Spacer(modifier = Modifier.height(4.dp))
|
|
36
|
+
Skeleton(width = 200.dp)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
```
|