@momo-kits/native-kits 0.163.1-beta.16-debug → 0.163.1-beta.17-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/compose/build.gradle.kts +1 -1
- package/compose/compose.podspec +1 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +4 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +66 -19
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +15 -3
- package/gradle.properties +1 -1
- package/ios/Application/Components.swift +14 -14
- package/ios/Application/HeaderRight.swift +79 -45
- package/ios/Application/Navigation/component/Header.swift +1 -1
- package/ios/Button/Button.swift +20 -14
- package/ios/native-kits.podspec +1 -1
- package/ios-demo/MoMoUIKitsDemo.podspec +1 -1
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ButtonDemo.swift +66 -211
- package/package.json +1 -1
- package/.claude/settings.local.json +0 -85
- package/ios/Application/Navigation/component/HeaderRightWidthKey.swift +0 -11
- package/ios/Application/Navigation/component/NavigationHeaderRight.swift +0 -196
- package/local.properties +0 -8
package/compose/build.gradle.kts
CHANGED
package/compose/compose.podspec
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
package vn.momo.kits.layout
|
|
2
2
|
|
|
3
|
-
import androidx.compose.foundation.layout.
|
|
3
|
+
import androidx.compose.foundation.layout.Column
|
|
4
4
|
import androidx.compose.foundation.layout.height
|
|
5
5
|
import androidx.compose.foundation.layout.width
|
|
6
6
|
import androidx.compose.runtime.Composable
|
|
@@ -32,5 +32,7 @@ internal fun Item(
|
|
|
32
32
|
m = m.width(grid.sizeForSpan(widthSpan))
|
|
33
33
|
if (heightSpan > 0) m = m.height(grid.sizeForSpan(heightSpan))
|
|
34
34
|
}
|
|
35
|
-
|
|
35
|
+
// A Column (not a Box) so multiple children flow VERTICALLY instead of stacking
|
|
36
|
+
// on top of each other — matches the React Item's flex-column content.
|
|
37
|
+
Column(modifier = m) { content() }
|
|
36
38
|
}
|
|
@@ -28,6 +28,7 @@ import androidx.compose.runtime.remember
|
|
|
28
28
|
import androidx.compose.runtime.rememberCoroutineScope
|
|
29
29
|
import androidx.compose.ui.Alignment
|
|
30
30
|
import androidx.compose.ui.Modifier
|
|
31
|
+
import androidx.compose.ui.graphics.Brush
|
|
31
32
|
import androidx.compose.ui.graphics.Color
|
|
32
33
|
import androidx.compose.ui.input.pointer.pointerInput
|
|
33
34
|
import androidx.compose.ui.platform.LocalDensity
|
|
@@ -57,6 +58,9 @@ internal fun BottomSheet(
|
|
|
57
58
|
header: BottomHeader,
|
|
58
59
|
isSurface: Boolean = false,
|
|
59
60
|
barrierDismissible: Boolean = true,
|
|
61
|
+
draggable: Boolean = true,
|
|
62
|
+
useBottomInset: Boolean = true,
|
|
63
|
+
footerComponent: (@Composable () -> Unit)? = null,
|
|
60
64
|
onDismiss: (() -> Unit)?
|
|
61
65
|
) {
|
|
62
66
|
val screenHeightDp = getScreenHeight()
|
|
@@ -161,30 +165,35 @@ internal fun BottomSheet(
|
|
|
161
165
|
.noFeedbackClickable { },
|
|
162
166
|
contentAlignment = Alignment.BottomCenter
|
|
163
167
|
) {
|
|
164
|
-
|
|
168
|
+
val bottomInset = if (useBottomInset) {
|
|
169
|
+
(AppNavigationBar.current - keyboardSizeState().value).coerceAtLeast(0.dp)
|
|
170
|
+
} else 0.dp
|
|
171
|
+
Column(Modifier.padding(bottom = bottomInset)) {
|
|
165
172
|
Box(
|
|
166
173
|
modifier = Modifier
|
|
167
174
|
.height(72.dp)
|
|
168
175
|
.fillMaxWidth()
|
|
169
|
-
.
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
change
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
176
|
+
.conditional(draggable) {
|
|
177
|
+
pointerInput(Unit) {
|
|
178
|
+
detectDragGestures(
|
|
179
|
+
onDrag = { change, dragAmount ->
|
|
180
|
+
change.consume()
|
|
181
|
+
coroutineScope.launch {
|
|
182
|
+
val newOffset = (sheetOffset.value + dragAmount.y).coerceAtLeast(0f)
|
|
183
|
+
sheetOffset.snapTo(newOffset)
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
onDragEnd = {
|
|
187
|
+
coroutineScope.launch {
|
|
188
|
+
if (sheetOffset.value > sheetCloseOffset) {
|
|
189
|
+
closeEvent()
|
|
190
|
+
} else {
|
|
191
|
+
sheetOffset.animateTo(0f)
|
|
192
|
+
}
|
|
184
193
|
}
|
|
185
194
|
}
|
|
186
|
-
|
|
187
|
-
|
|
195
|
+
)
|
|
196
|
+
}
|
|
188
197
|
}
|
|
189
198
|
) {
|
|
190
199
|
Column(
|
|
@@ -235,12 +244,50 @@ internal fun BottomSheet(
|
|
|
235
244
|
}
|
|
236
245
|
}
|
|
237
246
|
Divider()
|
|
238
|
-
|
|
247
|
+
if (footerComponent != null) {
|
|
248
|
+
Box(Modifier.weight(1f, fill = false)) {
|
|
249
|
+
content()
|
|
250
|
+
}
|
|
251
|
+
BottomSheetFooter(footerComponent)
|
|
252
|
+
} else {
|
|
253
|
+
content()
|
|
254
|
+
}
|
|
239
255
|
}
|
|
240
256
|
}
|
|
241
257
|
}
|
|
242
258
|
}
|
|
243
259
|
|
|
260
|
+
@Composable
|
|
261
|
+
private fun BottomSheetFooter(footerComponent: @Composable () -> Unit) {
|
|
262
|
+
val shadowBrush = remember {
|
|
263
|
+
Brush.verticalGradient(
|
|
264
|
+
colors = listOf(Color.Transparent, Color.Black.copy(alpha = 0.05f))
|
|
265
|
+
)
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
Box {
|
|
269
|
+
Box(
|
|
270
|
+
Modifier
|
|
271
|
+
.fillMaxWidth()
|
|
272
|
+
.background(AppTheme.current.colors.background.surface)
|
|
273
|
+
.conditional(IsShowBaseLineDebug) {
|
|
274
|
+
border(1.dp, Colors.blue_03)
|
|
275
|
+
}
|
|
276
|
+
.padding(vertical = Spacing.S)
|
|
277
|
+
) {
|
|
278
|
+
footerComponent()
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
Box(
|
|
282
|
+
modifier = Modifier
|
|
283
|
+
.fillMaxWidth()
|
|
284
|
+
.height(6.dp)
|
|
285
|
+
.offset(x = 0.dp, y = (-6).dp)
|
|
286
|
+
.background(shadowBrush)
|
|
287
|
+
)
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
244
291
|
sealed class BottomHeader {
|
|
245
292
|
data class Title(
|
|
246
293
|
val data: String = "Bottom Sheet Title",
|
|
@@ -139,10 +139,13 @@ class Navigator(
|
|
|
139
139
|
isSurface: Boolean = false,
|
|
140
140
|
barrierDismissible: Boolean = true,
|
|
141
141
|
onDismiss: (() -> Unit)? = null,
|
|
142
|
-
bottomSheetHeader: BottomHeader? = null
|
|
142
|
+
bottomSheetHeader: BottomHeader? = null,
|
|
143
|
+
draggable: Boolean = true,
|
|
144
|
+
useBottomInset: Boolean = true,
|
|
145
|
+
footerComponent: (@Composable () -> Unit)? = null
|
|
143
146
|
){
|
|
144
147
|
val id = currentScreen()?.id ?: -1
|
|
145
|
-
OverplayComponentRegistry.registerOverplay(id, content, OverplayComponentType.BOTTOM_SHEET, isSurface, barrierDismissible, onDismiss, bottomSheetHeader)
|
|
148
|
+
OverplayComponentRegistry.registerOverplay(id, content, OverplayComponentType.BOTTOM_SHEET, isSurface, barrierDismissible, onDismiss, bottomSheetHeader, draggable, useBottomInset, footerComponent)
|
|
146
149
|
}
|
|
147
150
|
|
|
148
151
|
fun showSnackBar(snackBar: SnackBar, onDismiss: (() -> Unit)? = null) {
|
|
@@ -257,6 +260,9 @@ sealed class OverplayComponentParams {
|
|
|
257
260
|
val onDismiss: (() -> Unit)? = null,
|
|
258
261
|
val barrierDismissible: Boolean = true,
|
|
259
262
|
val bottomSheetHeader: BottomHeader? = null,
|
|
263
|
+
val draggable: Boolean = true,
|
|
264
|
+
val useBottomInset: Boolean = true,
|
|
265
|
+
val footerComponent: (@Composable () -> Unit)? = null,
|
|
260
266
|
) : OverplayComponentParams()
|
|
261
267
|
|
|
262
268
|
class SnackBar : OverplayComponentParams()
|
|
@@ -288,10 +294,13 @@ object OverplayComponentRegistry {
|
|
|
288
294
|
barrierDismissible: Boolean = true,
|
|
289
295
|
onDismiss: (() -> Unit)?,
|
|
290
296
|
bottomSheetHeader: BottomHeader? = null,
|
|
297
|
+
draggable: Boolean = true,
|
|
298
|
+
useBottomInset: Boolean = true,
|
|
299
|
+
footerComponent: (@Composable () -> Unit)? = null,
|
|
291
300
|
){
|
|
292
301
|
val params = when(type){
|
|
293
302
|
OverplayComponentType.MODAL -> OverplayComponentParams.Modal(onDismiss, barrierDismissible)
|
|
294
|
-
OverplayComponentType.BOTTOM_SHEET -> OverplayComponentParams.BottomSheet(isSurface, onDismiss, barrierDismissible, bottomSheetHeader)
|
|
303
|
+
OverplayComponentType.BOTTOM_SHEET -> OverplayComponentParams.BottomSheet(isSurface, onDismiss, barrierDismissible, bottomSheetHeader, draggable, useBottomInset, footerComponent)
|
|
295
304
|
OverplayComponentType.SNACK_BAR -> OverplayComponentParams.SnackBar()
|
|
296
305
|
}
|
|
297
306
|
|
|
@@ -333,6 +342,9 @@ object OverplayComponentRegistry {
|
|
|
333
342
|
header = params.bottomSheetHeader ?: Title(),
|
|
334
343
|
isSurface = params.isSurface,
|
|
335
344
|
barrierDismissible = params.barrierDismissible,
|
|
345
|
+
draggable = params.draggable,
|
|
346
|
+
useBottomInset = params.useBottomInset,
|
|
347
|
+
footerComponent = params.footerComponent,
|
|
336
348
|
onDismiss = params.onDismiss
|
|
337
349
|
)
|
|
338
350
|
}
|
package/gradle.properties
CHANGED
|
@@ -100,20 +100,6 @@ public struct HeaderBackground: View {
|
|
|
100
100
|
.frame(height: height)
|
|
101
101
|
.shadow(color: Colors.black20.opacity(0.2), radius: 10, x: 0, y: -2)
|
|
102
102
|
.background(backgroundColor)
|
|
103
|
-
.overlay(
|
|
104
|
-
headerTransparent ? AnyView(EmptyView()) :
|
|
105
|
-
AnyView(
|
|
106
|
-
Rectangle()
|
|
107
|
-
.fill(LinearGradient(
|
|
108
|
-
gradient: Gradient(colors: [
|
|
109
|
-
Color(red: 1, green: 0.8, blue: 0.87),
|
|
110
|
-
Color(red: 1, green: 0.8, blue: 0.87).opacity(0.5)
|
|
111
|
-
]),
|
|
112
|
-
startPoint: .top,
|
|
113
|
-
endPoint: .bottom))
|
|
114
|
-
.opacity(opacity)
|
|
115
|
-
.frame(height: height))
|
|
116
|
-
)
|
|
117
103
|
if !headerTransparent {
|
|
118
104
|
Rectangle()
|
|
119
105
|
.fill(Color.black.opacity(0.1))
|
|
@@ -146,6 +132,17 @@ public struct HeaderBackground: View {
|
|
|
146
132
|
}
|
|
147
133
|
}
|
|
148
134
|
|
|
135
|
+
// MARK: - Header right width preference
|
|
136
|
+
|
|
137
|
+
/// Reports the measured width of the header-right area so the animated search
|
|
138
|
+
/// header can compute its trailing inset (mirrors Compose's headerRightWidthPx).
|
|
139
|
+
public struct HeaderRightWidthKey: PreferenceKey {
|
|
140
|
+
public static var defaultValue: CGFloat = 0
|
|
141
|
+
public static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
|
|
142
|
+
value = max(value, nextValue())
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
149
146
|
// MARK: - Bottom-tab root preference
|
|
150
147
|
|
|
151
148
|
/// Set by `BottomTab` on appear so its enclosing `StackScreen` can detect that
|
|
@@ -235,6 +232,9 @@ public struct Header: View {
|
|
|
235
232
|
|
|
236
233
|
if let headerRight = headerRight {
|
|
237
234
|
AnyView(headerRight())
|
|
235
|
+
.background(GeometryReader { geo in
|
|
236
|
+
Color.clear.preference(key: HeaderRightWidthKey.self, value: geo.size.width)
|
|
237
|
+
})
|
|
238
238
|
}
|
|
239
239
|
}
|
|
240
240
|
.padding(.horizontal, 12)
|
|
@@ -101,69 +101,98 @@ struct ToolkitHeaderRight: View {
|
|
|
101
101
|
var tintColor: Color?
|
|
102
102
|
@State private var isFavorite: Bool = false
|
|
103
103
|
@State private var isLoading: Bool = false
|
|
104
|
-
@
|
|
104
|
+
@Environment(\.applicationEnvironment) private var environment
|
|
105
|
+
|
|
106
|
+
private func apiValue(_ value: Any?) -> Any {
|
|
107
|
+
value ?? NSNull()
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
private var contextMap: [String: Any] {
|
|
111
|
+
let context = environment.applicationContext
|
|
112
|
+
return [
|
|
113
|
+
"appId": apiValue(context?.appId),
|
|
114
|
+
"code": apiValue(context?.appCode),
|
|
115
|
+
"name": apiValue(context?.appName),
|
|
116
|
+
"icon": apiValue(context?.appIcon),
|
|
117
|
+
"description": apiValue(context?.description),
|
|
118
|
+
"support": apiValue(context?.support),
|
|
119
|
+
"toolkitConfig": apiValue(context?.toolkitConfig),
|
|
120
|
+
"providerId": apiValue(context?.providerId),
|
|
121
|
+
"permissions": apiValue(context?.permissions)
|
|
122
|
+
]
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
private var shouldShowShortcut: Bool {
|
|
126
|
+
headerRight?.useShortcut == true && !(headerRight?.useMore == true && environment.applicationContext == nil)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
private func parseResponse(_ response: String) -> Any? {
|
|
130
|
+
guard let jsonData = response.data(using: .utf8),
|
|
131
|
+
let json = try? JSONSerialization.jsonObject(with: jsonData) as? [String: Any] else {
|
|
132
|
+
return nil
|
|
133
|
+
}
|
|
134
|
+
return json["response"]
|
|
135
|
+
}
|
|
105
136
|
|
|
106
137
|
// MARK: - Actions
|
|
138
|
+
private func loadFavoriteState() {
|
|
139
|
+
environment.composeApi?.request(
|
|
140
|
+
funcName: "isFavoriteApp",
|
|
141
|
+
params: ["code": apiValue(environment.applicationContext?.appCode)]
|
|
142
|
+
) { response in
|
|
143
|
+
if let isFavorite = parseResponse(response) as? Bool {
|
|
144
|
+
self.isFavorite = isFavorite
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
107
149
|
private func onPressShortcut() {
|
|
108
150
|
environment.composeApi?.request(
|
|
109
151
|
funcName: "onToolAction",
|
|
110
|
-
params: [
|
|
111
|
-
|
|
112
|
-
|
|
152
|
+
params: [
|
|
153
|
+
"item": ["key": "onFavorite"],
|
|
154
|
+
"context": contextMap
|
|
155
|
+
]
|
|
156
|
+
) { response in
|
|
157
|
+
if let result = parseResponse(response) as? [String: Any],
|
|
158
|
+
result["success"] as? Bool == true {
|
|
159
|
+
isFavorite.toggle()
|
|
160
|
+
}
|
|
113
161
|
}
|
|
114
162
|
}
|
|
115
163
|
|
|
116
164
|
private func onPressHelpCenter() {
|
|
117
|
-
let context = environment.applicationContext
|
|
118
|
-
let paramMap: [String: Any] = [
|
|
119
|
-
"appId": context?.appId,
|
|
120
|
-
"code": context?.appCode,
|
|
121
|
-
"name": context?.appName,
|
|
122
|
-
"icon": context?.appIcon,
|
|
123
|
-
"description": context?.description
|
|
124
|
-
]
|
|
125
165
|
environment.composeApi?.request(
|
|
126
166
|
funcName: "showHelpCenter",
|
|
127
|
-
params:
|
|
167
|
+
params: [
|
|
168
|
+
"appId": apiValue(environment.applicationContext?.appId),
|
|
169
|
+
"code": apiValue(environment.applicationContext?.appCode),
|
|
170
|
+
"name": apiValue(environment.applicationContext?.appName),
|
|
171
|
+
"icon": apiValue(environment.applicationContext?.appIcon),
|
|
172
|
+
"description": apiValue(environment.applicationContext?.description)
|
|
173
|
+
]
|
|
128
174
|
) { _ in }
|
|
129
175
|
}
|
|
130
176
|
|
|
131
177
|
private func onPressClose() {
|
|
132
178
|
environment.composeApi?.request(
|
|
133
179
|
funcName: "dismissAll",
|
|
134
|
-
params:
|
|
180
|
+
params: nil
|
|
135
181
|
) { _ in }
|
|
136
182
|
}
|
|
137
183
|
|
|
138
184
|
private func onPressMore() {
|
|
139
|
-
let context = environment.applicationContext
|
|
140
185
|
let params: [String: Any] = [
|
|
141
|
-
"useSystemTools": headerRight?.useSystemTools,
|
|
186
|
+
"useSystemTools": headerRight?.useSystemTools ?? true,
|
|
142
187
|
"tools": headerRight?.tools.map { $0.toMap() } ?? [],
|
|
143
|
-
"context":
|
|
144
|
-
"appId": context?.appId,
|
|
145
|
-
"code": context?.appCode,
|
|
146
|
-
"name": context?.appName,
|
|
147
|
-
"icon": context?.appIcon,
|
|
148
|
-
"description": context?.description,
|
|
149
|
-
"support": context?.support,
|
|
150
|
-
"toolkitConfig": context?.toolkitConfig,
|
|
151
|
-
"providerId": context?.providerId,
|
|
152
|
-
"permissions": context?.permissions
|
|
153
|
-
]
|
|
188
|
+
"context": contextMap
|
|
154
189
|
]
|
|
155
190
|
environment.composeApi?.request(
|
|
156
191
|
funcName: "showTools",
|
|
157
192
|
params: params
|
|
158
193
|
) { response in
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
let json = try JSONSerialization.jsonObject(with: jsonData) as? [String: Any],
|
|
162
|
-
let toolResponse = json["response"] as? String {
|
|
163
|
-
headerRight?.toolCallback?(toolResponse)
|
|
164
|
-
}
|
|
165
|
-
} catch {
|
|
166
|
-
print("Error parsing response:", error)
|
|
194
|
+
if let toolResponse = parseResponse(response) as? String {
|
|
195
|
+
headerRight?.toolCallback?(toolResponse)
|
|
167
196
|
}
|
|
168
197
|
}
|
|
169
198
|
}
|
|
@@ -195,7 +224,7 @@ struct ToolkitHeaderRight: View {
|
|
|
195
224
|
} ?? false
|
|
196
225
|
|
|
197
226
|
HStack(alignment: .center, spacing: 0) {
|
|
198
|
-
if
|
|
227
|
+
if shouldShowShortcut {
|
|
199
228
|
NavigationButton(
|
|
200
229
|
disabled: isLoading,
|
|
201
230
|
icon: navButtonConfig.icon,
|
|
@@ -206,15 +235,17 @@ struct ToolkitHeaderRight: View {
|
|
|
206
235
|
}
|
|
207
236
|
|
|
208
237
|
HStack(alignment: .center, spacing: 0) {
|
|
209
|
-
|
|
210
|
-
.
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
238
|
+
if environment.applicationContext != nil {
|
|
239
|
+
Icon(source: "help_center", size: 20, color: tintColor ?? Colors.black17)
|
|
240
|
+
.padding(4)
|
|
241
|
+
.onTapGesture {
|
|
242
|
+
onPressHelpCenter()
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
Rectangle()
|
|
246
|
+
.fill(tintColor ?? Colors.black20)
|
|
247
|
+
.frame(width: 0.5, height: 12)
|
|
248
|
+
}
|
|
218
249
|
|
|
219
250
|
Icon(source: "16_basic_home", size: 20, color: tintColor ?? Colors.black17)
|
|
220
251
|
.padding(4)
|
|
@@ -231,6 +262,9 @@ struct ToolkitHeaderRight: View {
|
|
|
231
262
|
)
|
|
232
263
|
.padding(.leading, Spacing.S)
|
|
233
264
|
}
|
|
265
|
+
.onAppear {
|
|
266
|
+
loadFavoriteState()
|
|
267
|
+
}
|
|
234
268
|
}
|
|
235
269
|
}
|
|
236
270
|
|
|
@@ -22,7 +22,7 @@ public struct NavigationHeader: View {
|
|
|
22
22
|
/// Optional rich title (mirrors Compose's `HeaderTitle`); `.user` renders an
|
|
23
23
|
/// avatar group, `.default`/nil renders plain `title`.
|
|
24
24
|
var headerTitle: HeaderTitle?
|
|
25
|
-
var headerRight: (() -> any View)? = {
|
|
25
|
+
var headerRight: (() -> any View)? = { HeaderRight() }
|
|
26
26
|
var goBack: (() -> Void)?
|
|
27
27
|
var opacity: CGFloat = 1
|
|
28
28
|
var animatedHeader: AnimatedHeader?
|
package/ios/Button/Button.swift
CHANGED
|
@@ -281,19 +281,25 @@ private struct ButtonPressFeedbackStyle: ButtonStyle {
|
|
|
281
281
|
func makeBody(configuration: Configuration) -> some View {
|
|
282
282
|
let pressed = configuration.isPressed && isEnabled
|
|
283
283
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
284
|
+
ZStack {
|
|
285
|
+
RoundedRectangle(cornerRadius: radius)
|
|
286
|
+
.fill(background)
|
|
287
|
+
.overlay(
|
|
288
|
+
RoundedRectangle(cornerRadius: radius)
|
|
289
|
+
.strokeBorder(border ?? .clear, lineWidth: border != nil ? borderWidth : 0)
|
|
290
|
+
)
|
|
291
|
+
.padding(.horizontal, pressed ? 2 : 0)
|
|
292
|
+
|
|
293
|
+
configuration.label
|
|
294
|
+
.opacity(pressed ? 0.5 : 1)
|
|
295
|
+
}
|
|
296
|
+
// Pin the whole button to its intended height explicitly: the bare
|
|
297
|
+
// RoundedRectangle background has no intrinsic size of its own, so left
|
|
298
|
+
// unconstrained in a ZStack inside a non-height-constraining container
|
|
299
|
+
// (e.g. a footer VStack) it can report an oversized ideal height and
|
|
300
|
+
// stretch the button — and its container — far taller than intended.
|
|
301
|
+
.frame(height: height)
|
|
302
|
+
.clipShape(RoundedRectangle(cornerRadius: radius))
|
|
303
|
+
.animation(.easeInOut(duration: 0.1), value: pressed)
|
|
298
304
|
}
|
|
299
305
|
}
|
package/ios/native-kits.podspec
CHANGED
|
@@ -1,245 +1,100 @@
|
|
|
1
1
|
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
2
|
+
// Foundation.swift
|
|
3
|
+
// Example
|
|
4
4
|
//
|
|
5
|
-
//
|
|
6
|
-
// Preview tab mirrors `PreviewButton`, Playground tab mirrors `PlaygroundButton`.
|
|
5
|
+
// Created by Wem on 13/12/2022.
|
|
7
6
|
//
|
|
8
7
|
|
|
9
8
|
import Foundation
|
|
9
|
+
import Lottie
|
|
10
10
|
import MoMoUIKits
|
|
11
11
|
import SwiftUI
|
|
12
12
|
|
|
13
|
-
// Mirror the option lists in Compose `DemoScreen.kt`.
|
|
14
|
-
private let buttonTypeList: [(key: String, value: ButtonType)] = [
|
|
15
|
-
("primary", .primary),
|
|
16
|
-
("secondary", .secondary),
|
|
17
|
-
("tonal", .tonal),
|
|
18
|
-
("outline", .outline),
|
|
19
|
-
("danger", .danger),
|
|
20
|
-
("text", .text),
|
|
21
|
-
("disabled", .disabled),
|
|
22
|
-
]
|
|
23
|
-
|
|
24
|
-
private let sizeList: [(key: String, value: ButtonSize)] = [
|
|
25
|
-
("large", .large),
|
|
26
|
-
("medium", .medium),
|
|
27
|
-
("small", .small),
|
|
28
|
-
]
|
|
29
|
-
|
|
30
|
-
// Subset of the Compose `Icons` map — names resolved via `Icon(source:)` / icon.json.
|
|
31
|
-
private let iconOptionList: [String] = [
|
|
32
|
-
"none",
|
|
33
|
-
"arrow_arrow-back",
|
|
34
|
-
"arrow_arrow-next",
|
|
35
|
-
"Phonebook",
|
|
36
|
-
]
|
|
37
|
-
|
|
38
13
|
// MARK: - ButtonDemo
|
|
39
14
|
|
|
40
15
|
struct ButtonDemo: View {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
var body: some View {
|
|
44
|
-
VStack(spacing: 0) {
|
|
45
|
-
// Stand-in for Compose `DemoScreen`'s Preview/Playground bottom tabs.
|
|
46
|
-
Picker("", selection: $tab) {
|
|
47
|
-
Text("Preview").tag(0)
|
|
48
|
-
Text("Playground").tag(1)
|
|
49
|
-
}
|
|
50
|
-
.pickerStyle(.segmented)
|
|
51
|
-
.padding(.horizontal, 12)
|
|
52
|
-
.padding(.top, 8)
|
|
53
|
-
|
|
54
|
-
if tab == 0 {
|
|
55
|
-
PreviewButton()
|
|
56
|
-
} else {
|
|
57
|
-
PlaygroundButton()
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// MARK: - PreviewButton
|
|
64
|
-
|
|
65
|
-
private struct PreviewButton: View {
|
|
66
|
-
private let url = "https://static-00.iconduck.com/assets.00/external-link-icon-512x512-h2zzryfc.png"
|
|
16
|
+
// MARK: Internal
|
|
67
17
|
|
|
68
18
|
var body: some View {
|
|
69
19
|
ScrollView {
|
|
70
|
-
VStack
|
|
71
|
-
|
|
72
|
-
Button(title: "Open", action: {})
|
|
73
|
-
}
|
|
20
|
+
VStack {
|
|
21
|
+
Toggle("Disabled", isOn: $disabled)
|
|
74
22
|
|
|
75
|
-
|
|
76
|
-
Button(title: "
|
|
77
|
-
|
|
78
|
-
Button(title: "
|
|
79
|
-
Button(title: "TONAL", action: {}, type: .tonal)
|
|
80
|
-
Button(title: "OUTLINE", action: {}, type: .outline)
|
|
81
|
-
Button(title: "TEXT", action: {}, type: .text)
|
|
82
|
-
Button(title: "DISABLED", action: {}, type: .disabled)
|
|
23
|
+
Group {
|
|
24
|
+
Button(title: "Button", action: onPress, type: disabled ? .disabled : .primary, size: .large,
|
|
25
|
+
iconRight: AnyView(Image(systemName: "checkmark.circle")))
|
|
26
|
+
Button(title: "Button", action: onPress, size: .small)
|
|
83
27
|
}
|
|
84
28
|
|
|
85
|
-
|
|
86
|
-
Button(title: "
|
|
87
|
-
|
|
88
|
-
Button(title: "
|
|
29
|
+
Group {
|
|
30
|
+
Button(title: "Button", action: onPress, type: .danger,
|
|
31
|
+
iconLeft: AnyView(Image(systemName: "checkmark.circle")))
|
|
32
|
+
Button(title: "Button", action: onPress, type: .danger, size: .medium)
|
|
33
|
+
Button(title: "Button", action: onPress, type: .danger, size: .small)
|
|
89
34
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
Button(title: "
|
|
93
|
-
Button(title: "
|
|
94
|
-
Button(title: "
|
|
95
|
-
Button(title: "TONAL", action: {}, type: .tonal, loading: true)
|
|
96
|
-
Button(title: "OUTLINE", action: {}, type: .outline, loading: true)
|
|
97
|
-
Button(title: "TEXT", action: {}, type: .text, loading: true)
|
|
98
|
-
Button(title: "DISABLED", action: {}, type: .disabled, loading: true)
|
|
35
|
+
|
|
36
|
+
Group {
|
|
37
|
+
Button(title: "Button", action: onPress, type: .danger, iconLeft: AnyView(Image(systemName: "checkmark.circle")), isFull: false)
|
|
38
|
+
Button(title: "Button", action: onPress, type: .danger, size: .medium, isFull: false)
|
|
39
|
+
Button(title: "Button", action: onPress, type: .danger, size: .small, isFull: false)
|
|
99
40
|
}
|
|
100
41
|
|
|
101
|
-
ButtonSectionBox(title: "Icon Right") {
|
|
102
|
-
Button(title: "Icon", action: {}, iconRight: AnyView(ImageView(url, loading: false)))
|
|
103
|
-
}
|
|
104
42
|
|
|
105
|
-
|
|
106
|
-
Button(title: "
|
|
43
|
+
Group {
|
|
44
|
+
Button(title: "Button", action: onPress, type: .tonal, size: .large)
|
|
45
|
+
Button(title: "Button", action: onPress, type: .tonal, size: .medium)
|
|
46
|
+
Button(title: "Button", action: onPress, type: .tonal, size: .small)
|
|
107
47
|
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// MARK: - PlaygroundButton
|
|
114
48
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
@State private var iconLeft: AnyHashable = "none"
|
|
121
|
-
@State private var full = true
|
|
122
|
-
@State private var loading = false
|
|
123
|
-
@State private var useTintColor = true
|
|
124
|
-
|
|
125
|
-
var body: some View {
|
|
126
|
-
VStack(spacing: 0) {
|
|
127
|
-
ButtonSectionBox(title: "Component", alignment: .leading) {
|
|
128
|
-
Button(
|
|
129
|
-
title: title,
|
|
130
|
-
action: {},
|
|
131
|
-
type: buttonTypeList.first { $0.key == type }?.value ?? .primary,
|
|
132
|
-
size: sizeList.first { $0.key == size }?.value ?? .large,
|
|
133
|
-
iconLeft: iconView(iconLeft),
|
|
134
|
-
iconRight: iconView(iconRight),
|
|
135
|
-
isFull: full,
|
|
136
|
-
loading: loading,
|
|
137
|
-
useTintColor: useTintColor
|
|
138
|
-
)
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
ScrollView {
|
|
142
|
-
ButtonSectionBox(title: "Props", alignment: .leading) {
|
|
143
|
-
PropTitle("Children")
|
|
144
|
-
Input(text: $title)
|
|
145
|
-
PropGap()
|
|
146
|
-
|
|
147
|
-
PropTitle("Type")
|
|
148
|
-
RadioGroup(options: buttonTypeList.map(\.key), selection: $type)
|
|
149
|
-
PropGap()
|
|
150
|
-
|
|
151
|
-
PropTitle("Size")
|
|
152
|
-
RadioGroup(options: sizeList.map(\.key), selection: $size)
|
|
153
|
-
PropGap()
|
|
154
|
-
|
|
155
|
-
PropTitle("Full")
|
|
156
|
-
Checkbox($full, title: "\(full)")
|
|
157
|
-
PropGap()
|
|
158
|
-
|
|
159
|
-
PropTitle("Iconright")
|
|
160
|
-
RadioGroup(options: iconOptionList, selection: $iconRight)
|
|
161
|
-
PropGap()
|
|
162
|
-
|
|
163
|
-
PropTitle("Iconleft")
|
|
164
|
-
RadioGroup(options: iconOptionList, selection: $iconLeft)
|
|
165
|
-
PropGap()
|
|
166
|
-
|
|
167
|
-
PropTitle("Loading")
|
|
168
|
-
Checkbox($loading, title: "\(loading)")
|
|
169
|
-
PropGap()
|
|
170
|
-
|
|
171
|
-
PropTitle("Usetinecolor")
|
|
172
|
-
Checkbox($useTintColor, title: "\(useTintColor)")
|
|
49
|
+
Group {
|
|
50
|
+
Button(title: "Tinted icon", action: onPress, type: .primary,
|
|
51
|
+
iconLeft: AnyView(Image(systemName: "checkmark.circle")), useTintColor: true)
|
|
52
|
+
Button(title: "Original icon", action: onPress, type: .primary,
|
|
53
|
+
iconLeft: AnyView(Image(systemName: "checkmark.circle")), useTintColor: false)
|
|
173
54
|
}
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
private func iconView(_ selection: AnyHashable) -> AnyView? {
|
|
179
|
-
guard let name = selection as? String, name != "none" else { return nil }
|
|
180
|
-
return AnyView(Icon(source: name))
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// MARK: - Playground helpers
|
|
185
55
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
var body: some View {
|
|
192
|
-
MomoText(title, typography: .headerDefaultBold)
|
|
193
|
-
}
|
|
194
|
-
}
|
|
56
|
+
Group {
|
|
57
|
+
Button(title: "Button", action: onPress, type: .secondary, size: .large)
|
|
58
|
+
Button(title: "Button", action: onPress, type: .secondary, size: .medium)
|
|
59
|
+
Button(title: "Button", action: onPress, type: .secondary, size: .small)
|
|
60
|
+
}
|
|
195
61
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
}
|
|
62
|
+
Group {
|
|
63
|
+
Button(title: "Button", action: onPress, type: .text, size: .large)
|
|
64
|
+
Button(title: "Button", action: onPress, type: .text, size: .medium)
|
|
65
|
+
Button(title: "Button", action: onPress, type: .text, size: .small)
|
|
66
|
+
}
|
|
201
67
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
68
|
+
Group {
|
|
69
|
+
Button(title: "Button", action: onPress, type: .outline, size: .large)
|
|
70
|
+
Button(title: "Button", action: onPress, type: .outline, size: .medium)
|
|
71
|
+
Button(title: "Button", action: onPress, type: .outline, size: .small)
|
|
72
|
+
}
|
|
205
73
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
74
|
+
Group {
|
|
75
|
+
Button(title: "Button", action: onPress, type: .disabled, size: .large)
|
|
76
|
+
Button(title: "Button", action: onPress, type: .disabled, size: .medium)
|
|
77
|
+
Button(title: "Button", action: onPress, type: .disabled, size: .small)
|
|
78
|
+
}
|
|
79
|
+
Group {
|
|
80
|
+
Button(title: "Button", action: onPress, type: .primary, size: .medium, loading: true)
|
|
81
|
+
Button(title: "Button", action: onPress, type: .danger, size: .medium, iconLeft: AnyView(Image(systemName: "checkmark.circle")), loading: true)
|
|
82
|
+
Button(title: "Button", action: onPress, type: .disabled, size: .medium, iconRight: AnyView(Image(systemName: "checkmark.circle")), loading: true)
|
|
83
|
+
Button(title: "Button", action: onPress, type: .outline, size: .medium, iconLeft: AnyView(Image(systemName: "checkmark.circle")), iconRight: AnyView(Image(systemName: "checkmark.circle")), loading: true)
|
|
84
|
+
Button(title: "Button", action: onPress, type: .secondary, size: .medium, loading: true)
|
|
85
|
+
Button(title: "Button", action: onPress, type: .text, size: .medium, loading: true)
|
|
86
|
+
Button(title: "Button", action: onPress, type: .tonal, size: .medium, loading: true)
|
|
87
|
+
}
|
|
88
|
+
}.padding(16)
|
|
89
|
+
}.padding(.top)
|
|
210
90
|
}
|
|
211
|
-
}
|
|
212
91
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
/// Titled rounded-card section container, mirroring Compose's `BasicColumnBox`.
|
|
216
|
-
private struct ButtonSectionBox<Content: View>: View {
|
|
217
|
-
@Environment(\.appTheme) private var theme
|
|
218
|
-
let title: String
|
|
219
|
-
var alignment: HorizontalAlignment = .center
|
|
220
|
-
@ViewBuilder let content: () -> Content
|
|
221
|
-
|
|
222
|
-
var body: some View {
|
|
223
|
-
VStack(alignment: .leading, spacing: 8) {
|
|
224
|
-
if !title.isEmpty {
|
|
225
|
-
MomoText(title, typography: .headerMBold)
|
|
226
|
-
.padding(.top, 8)
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
VStack(alignment: alignment, spacing: 8) {
|
|
230
|
-
content()
|
|
231
|
-
}
|
|
232
|
-
.frame(maxWidth: .infinity, alignment: alignment == .leading ? .leading : .center)
|
|
233
|
-
.padding(12)
|
|
234
|
-
.background(theme.colors.background.surface)
|
|
235
|
-
.clipShape(RoundedRectangle(cornerRadius: Radius.M))
|
|
236
|
-
}
|
|
237
|
-
.padding(12)
|
|
92
|
+
func onPress() {
|
|
93
|
+
print("Button")
|
|
238
94
|
}
|
|
239
|
-
}
|
|
240
95
|
|
|
241
|
-
// MARK:
|
|
96
|
+
// MARK: Private
|
|
242
97
|
|
|
243
|
-
|
|
244
|
-
|
|
98
|
+
@State private var value: String = ""
|
|
99
|
+
@State private var disabled: Bool = false
|
|
245
100
|
}
|
package/package.json
CHANGED
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"permissions": {
|
|
3
|
-
"allow": [
|
|
4
|
-
"Bash(curl -s -o /dev/null -w \"%{http_code}\" \"https://repo.maven.apache.org/maven2/org/jetbrains/skiko/skiko-android/0.144.6/skiko-android-0.144.6.pom\")",
|
|
5
|
-
"Bash(curl -s \"https://repo.maven.apache.org/maven2/org/jetbrains/skiko/skiko-android/maven-metadata.xml\")",
|
|
6
|
-
"Bash(curl -s \"https://repo.maven.apache.org/maven2/org/jetbrains/skiko/skiko/maven-metadata.xml\")",
|
|
7
|
-
"Bash(curl -s \"https://repo.maven.apache.org/maven2/org/jetbrains/skiko/skiko/0.144.6/skiko-0.144.6.module\")",
|
|
8
|
-
"Bash(curl -s -o /dev/null -w '%{http_code}' https://repo.maven.apache.org/maven2/org/jetbrains/skiko/skiko-android/0.144.6/skiko-android-0.144.6.__TRACKED_VAR__)",
|
|
9
|
-
"Bash(curl -s \"https://repo.maven.apache.org/maven2/org/jetbrains/skiko/skiko-android/0.144.6/\")",
|
|
10
|
-
"Bash(curl -s \"https://repo.maven.apache.org/maven2/org/jetbrains/skiko/skiko-android/\")",
|
|
11
|
-
"Bash(curl -s -o /dev/null -w \"%{http_code}\\\\n\" \"https://repo.maven.apache.org/maven2/org/jetbrains/skiko/skiko-android/0.148.1/skiko-android-0.148.1.pom\")",
|
|
12
|
-
"Bash(curl -s -o /dev/null -w \"%{http_code}\\\\n\" \"https://repo.maven.apache.org/maven2/org/jetbrains/skiko/skiko-android/0.144.5/skiko-android-0.144.5.pom\")",
|
|
13
|
-
"Bash(curl -s -o /dev/null -w \"%{http_code}\\\\n\" \"https://dl.google.com/dl/android/maven2/org/jetbrains/skiko/skiko-android/0.144.6/skiko-android-0.144.6.pom\")",
|
|
14
|
-
"Bash(curl -s \"https://search.maven.org/solrsearch/select?q=a:skiko-android&rows=5&wt=json\")",
|
|
15
|
-
"Bash(curl -sL -o /dev/null -w \"%{http_code} %{url_effective}\\\\n\" \"https://repo1.maven.org/maven2/org/jetbrains/skiko/skiko-android/0.144.6/skiko-android-0.144.6.pom\")",
|
|
16
|
-
"Bash(curl *)",
|
|
17
|
-
"Bash(./gradlew :sample:androidApp:dependencies --configuration debugRuntimeClasspath)",
|
|
18
|
-
"Bash(./gradlew :sample:shared:compileDebugKotlinAndroid)",
|
|
19
|
-
"Bash(./gradlew :sample:androidApp:compileDebugKotlin)",
|
|
20
|
-
"Bash(ls -d *.xcodeproj)",
|
|
21
|
-
"Bash(grep -c ChipDemo.swift __TRACKED_VAR__/project.pbxproj)",
|
|
22
|
-
"Bash(grep -c PBXFileSystemSynchronizedRootGroup __TRACKED_VAR__/project.pbxproj)",
|
|
23
|
-
"Bash(grep -c InformationDemo.swift __TRACKED_VAR__/project.pbxproj)",
|
|
24
|
-
"Bash(plutil -lint project.pbxproj)",
|
|
25
|
-
"Bash(xcode-select -p)",
|
|
26
|
-
"Bash(/usr/bin/xcrun xcodebuild *)",
|
|
27
|
-
"Bash(echo \"exit: $?\")",
|
|
28
|
-
"Bash(/Applications/Xcode26.app/Contents/Developer/usr/bin/xcodebuild -version)",
|
|
29
|
-
"Bash(./gradlew :compose:tasks --all)",
|
|
30
|
-
"Bash(grep -n \"vanniktech\\\\|mavenPublishing\\\\|^publishing\\\\|version = gitlabVersion\\\\|namespace =\\\\|compileSdk =\\\\|minSdk =\\\\|GitLabPackages\\\\|name = \\\\\"\\\\${gitlabRepo}Packages\\\\\"\")",
|
|
31
|
-
"Bash(git checkout *)",
|
|
32
|
-
"Bash(./gradlew :compose:publishAllPublicationsToGitLabPackagesRepository --dry-run)",
|
|
33
|
-
"Bash(git fetch *)",
|
|
34
|
-
"Bash(git pull *)",
|
|
35
|
-
"Bash(git push *)",
|
|
36
|
-
"Bash(git branch *)",
|
|
37
|
-
"Bash(xcodebuild -workspace iosApp.xcworkspace -list)",
|
|
38
|
-
"Bash(xcrun simctl *)",
|
|
39
|
-
"Bash(xcodebuild -workspace iosApp.xcworkspace -scheme iosApp -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 14 Pro' -configuration Debug build CODE_SIGNING_ALLOWED=NO)",
|
|
40
|
-
"Bash(pod install *)",
|
|
41
|
-
"Bash(xcodebuild -workspace iosApp.xcworkspace -scheme iosApp -sdk iphonesimulator -destination 'id=1E6F9A79-5E64-4590-BEA2-828D75D99393' -configuration Debug build CODE_SIGNING_ALLOWED=NO)",
|
|
42
|
-
"Bash(xcrun swiftc *)",
|
|
43
|
-
"Bash(echo \"=== exit: $? \\(1=none found\\) ===\")",
|
|
44
|
-
"Bash(cat Radius.swift)",
|
|
45
|
-
"Bash(cat Spacing.swift)",
|
|
46
|
-
"Bash(echo \"exit=$?\")",
|
|
47
|
-
"Bash(ruby *)",
|
|
48
|
-
"Bash(xcodebuild -list -workspace iosApp.xcworkspace)",
|
|
49
|
-
"Bash(xcodebuild -workspace iosApp.xcworkspace -scheme MoMoUIKits -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 14 Pro' build)",
|
|
50
|
-
"Bash(xcodebuild -workspace iosApp.xcworkspace -scheme MoMoUIKits -sdk iphonesimulator -destination 'platform=iOS Simulator,id=E6A899C0-098E-473A-95B8-A2BBD2D42A92' build)",
|
|
51
|
-
"Bash(xcodebuild -workspace iosApp.xcworkspace -scheme iosApp -sdk iphonesimulator -destination 'platform=iOS Simulator,id=E6A899C0-098E-473A-95B8-A2BBD2D42A92' build)",
|
|
52
|
-
"Bash(xcodebuild build *)",
|
|
53
|
-
"Bash(git stash *)",
|
|
54
|
-
"Bash(plutil -lint sample/iosApp/iosApp.xcodeproj/project.pbxproj)",
|
|
55
|
-
"Bash(git add *)",
|
|
56
|
-
"Bash(git commit -m 'feat: DS-646 [ci build] *)",
|
|
57
|
-
"Bash(git commit -q -m 'fix: DS-656 [ci build] *)",
|
|
58
|
-
"Bash(xargs git log --oneline -1)",
|
|
59
|
-
"Bash(git rev-list *)",
|
|
60
|
-
"Bash(git status *)",
|
|
61
|
-
"Bash(git rebase *)",
|
|
62
|
-
"Bash(git merge *)",
|
|
63
|
-
"Bash(git reset *)",
|
|
64
|
-
"Bash(awk '/^ *)",
|
|
65
|
-
"Bash(git ls-tree *)",
|
|
66
|
-
"Bash(awk *)",
|
|
67
|
-
"Read(//Users/sophia/Workspace/momo/momo-app/**)",
|
|
68
|
-
"Bash(git log *)",
|
|
69
|
-
"Bash(xcodebuild -version)",
|
|
70
|
-
"Bash(xcodebuild -workspace iosApp.xcworkspace -scheme iosApp -sdk iphonesimulator -destination 'generic/platform=iOS Simulator' -configuration Debug build CODE_SIGNING_ALLOWED=NO)",
|
|
71
|
-
"Bash(xcodebuild -workspace iosApp.xcworkspace -scheme MoMoUIKits -sdk iphonesimulator -destination 'generic/platform=iOS Simulator' -configuration Debug build CODE_SIGNING_ALLOWED=NO)",
|
|
72
|
-
"Bash(xcodebuild -workspace iosApp.xcworkspace -scheme MoMoUIKits -sdk iphonesimulator -destination 'generic/platform=iOS Simulator' -configuration Debug clean build CODE_SIGNING_ALLOWED=NO)",
|
|
73
|
-
"Bash(git commit *)",
|
|
74
|
-
"Bash(npm view *)",
|
|
75
|
-
"Bash(echo \"EXIT=$? -> not resolvable\")",
|
|
76
|
-
"Bash(yarn install *)",
|
|
77
|
-
"Bash(RCT_NEW_ARCH_ENABLED=1 pod install)",
|
|
78
|
-
"Bash(xcodebuild -workspace MoMoPlatform.xcworkspace -list)",
|
|
79
|
-
"Bash(git --no-pager diff --stat ios/Application/Localize.swift ios/Application/Navigation/NavigationContainer.swift ios/Popup/PopupDisplay.swift ios/Popup/PopupNotify.swift ios/Template/TrustBanner/TrustBanner.swift)",
|
|
80
|
-
"Bash(git rm *)",
|
|
81
|
-
"Bash(echo \"EXIT=$?\")",
|
|
82
|
-
"Bash(git -C /Users/sophia/Workspace/momo/momo-native-kits diff sample/iosApp/iosApp/Demo/ButtonDemo.swift)"
|
|
83
|
-
]
|
|
84
|
-
}
|
|
85
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import SwiftUI
|
|
2
|
-
|
|
3
|
-
/// Reports the measured width of the header-right area so the animated search
|
|
4
|
-
/// header can compute its trailing inset (mirrors Compose's headerRightWidthPx).
|
|
5
|
-
/// Written by the header views (`NavigationHeader`) and read by `StackScreen`.
|
|
6
|
-
public struct HeaderRightWidthKey: PreferenceKey {
|
|
7
|
-
public static var defaultValue: CGFloat = 0
|
|
8
|
-
public static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
|
|
9
|
-
value = max(value, nextValue())
|
|
10
|
-
}
|
|
11
|
-
}
|
|
@@ -1,196 +0,0 @@
|
|
|
1
|
-
import SwiftUI
|
|
2
|
-
import Foundation
|
|
3
|
-
|
|
4
|
-
/// Keyed (`@Environment(\.applicationEnvironment)`) variant of `HeaderRight`, for hosts
|
|
5
|
-
/// wired through `NavigationContainer`/`KitContainer` (the injection style introduced by
|
|
6
|
-
/// MR 227 / DS-637). Reuses the shared data types (`HeaderRightData`, `Tool`, `ToolGroup`)
|
|
7
|
-
/// and `NavigationButton` from `HeaderRight.swift`. The legacy `HeaderRight` there stays on
|
|
8
|
-
/// `@EnvironmentObject` for hosts that render `Screen` directly with
|
|
9
|
-
/// `.environmentObject(ApplicationEnvironment(...))`. Opt in per screen with
|
|
10
|
-
/// `headerRight: { NavigationHeaderRight() }`.
|
|
11
|
-
public struct NavigationHeaderRight: View {
|
|
12
|
-
public init(headerRight: HeaderRightData? = nil, tintColor: Color? = nil) {
|
|
13
|
-
self.headerRight = headerRight
|
|
14
|
-
self.tintColor = tintColor
|
|
15
|
-
}
|
|
16
|
-
var headerRight: HeaderRightData?
|
|
17
|
-
var tintColor: Color? = nil
|
|
18
|
-
|
|
19
|
-
public var body: some View {
|
|
20
|
-
HStack(alignment: .center) {
|
|
21
|
-
ToolkitNavigationHeaderRight(headerRight: headerRight, tintColor: tintColor)
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
struct ToolkitNavigationHeaderRight: View {
|
|
27
|
-
var headerRight: HeaderRightData?
|
|
28
|
-
var tintColor: Color?
|
|
29
|
-
@State private var isFavorite: Bool = false
|
|
30
|
-
@State private var isLoading: Bool = false
|
|
31
|
-
@Environment(\.applicationEnvironment) private var environment
|
|
32
|
-
|
|
33
|
-
private func apiValue(_ value: Any?) -> Any {
|
|
34
|
-
value ?? NSNull()
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
private var contextMap: [String: Any] {
|
|
38
|
-
let context = environment.applicationContext
|
|
39
|
-
return [
|
|
40
|
-
"appId": apiValue(context?.appId),
|
|
41
|
-
"code": apiValue(context?.appCode),
|
|
42
|
-
"name": apiValue(context?.appName),
|
|
43
|
-
"icon": apiValue(context?.appIcon),
|
|
44
|
-
"description": apiValue(context?.description),
|
|
45
|
-
"support": apiValue(context?.support),
|
|
46
|
-
"toolkitConfig": apiValue(context?.toolkitConfig),
|
|
47
|
-
"providerId": apiValue(context?.providerId),
|
|
48
|
-
"permissions": apiValue(context?.permissions)
|
|
49
|
-
]
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
private var shouldShowShortcut: Bool {
|
|
53
|
-
headerRight?.useShortcut == true && !(headerRight?.useMore == true && environment.applicationContext == nil)
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
private func parseResponse(_ response: String) -> Any? {
|
|
57
|
-
guard let jsonData = response.data(using: .utf8),
|
|
58
|
-
let json = try? JSONSerialization.jsonObject(with: jsonData) as? [String: Any] else {
|
|
59
|
-
return nil
|
|
60
|
-
}
|
|
61
|
-
return json["response"]
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// MARK: - Actions
|
|
65
|
-
private func loadFavoriteState() {
|
|
66
|
-
environment.composeApi?.request(
|
|
67
|
-
funcName: "isFavoriteApp",
|
|
68
|
-
params: ["code": apiValue(environment.applicationContext?.appCode)]
|
|
69
|
-
) { response in
|
|
70
|
-
if let isFavorite = parseResponse(response) as? Bool {
|
|
71
|
-
self.isFavorite = isFavorite
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
private func onPressShortcut() {
|
|
77
|
-
environment.composeApi?.request(
|
|
78
|
-
funcName: "onToolAction",
|
|
79
|
-
params: [
|
|
80
|
-
"item": ["key": "onFavorite"],
|
|
81
|
-
"context": contextMap
|
|
82
|
-
]
|
|
83
|
-
) { response in
|
|
84
|
-
if let result = parseResponse(response) as? [String: Any],
|
|
85
|
-
result["success"] as? Bool == true {
|
|
86
|
-
isFavorite.toggle()
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
private func onPressHelpCenter() {
|
|
92
|
-
environment.composeApi?.request(
|
|
93
|
-
funcName: "showHelpCenter",
|
|
94
|
-
params: [
|
|
95
|
-
"appId": apiValue(environment.applicationContext?.appId),
|
|
96
|
-
"code": apiValue(environment.applicationContext?.appCode),
|
|
97
|
-
"name": apiValue(environment.applicationContext?.appName),
|
|
98
|
-
"icon": apiValue(environment.applicationContext?.appIcon),
|
|
99
|
-
"description": apiValue(environment.applicationContext?.description)
|
|
100
|
-
]
|
|
101
|
-
) { _ in }
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
private func onPressClose() {
|
|
105
|
-
environment.composeApi?.request(
|
|
106
|
-
funcName: "dismissAll",
|
|
107
|
-
params: nil
|
|
108
|
-
) { _ in }
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
private func onPressMore() {
|
|
112
|
-
let params: [String: Any] = [
|
|
113
|
-
"useSystemTools": headerRight?.useSystemTools ?? true,
|
|
114
|
-
"tools": headerRight?.tools.map { $0.toMap() } ?? [],
|
|
115
|
-
"context": contextMap
|
|
116
|
-
]
|
|
117
|
-
environment.composeApi?.request(
|
|
118
|
-
funcName: "showTools",
|
|
119
|
-
params: params
|
|
120
|
-
) { response in
|
|
121
|
-
if let toolResponse = parseResponse(response) as? String {
|
|
122
|
-
headerRight?.toolCallback?(toolResponse)
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
private func getNavigationButtonConfig() -> NavigationButtonConfig {
|
|
128
|
-
let totalTools = headerRight?.tools.reduce(0) { $0 + $1.items.count } ?? 0
|
|
129
|
-
var icon = isFavorite ? "pin_star_checked" : "pin_star"
|
|
130
|
-
var onClickHandler: () -> Void = onPressShortcut
|
|
131
|
-
|
|
132
|
-
if totalTools > 1 || headerRight?.useMore == true {
|
|
133
|
-
icon = "navigation_more_icon"
|
|
134
|
-
onClickHandler = onPressMore
|
|
135
|
-
} else if totalTools == 1, let singleTool = headerRight?.tools.first?.items.first {
|
|
136
|
-
icon = singleTool.icon
|
|
137
|
-
onClickHandler = {
|
|
138
|
-
headerRight?.toolCallback?(singleTool.key)
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
return NavigationButtonConfig(icon: icon, onPress: onClickHandler)
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
var body: some View {
|
|
146
|
-
let backgroundButtonColor: Color = tintColor == Colors.black01 ? Colors.black20.opacity(0.6) : Colors.black01.opacity(0.6)
|
|
147
|
-
let borderColor: Color = tintColor == Colors.black01 ? Colors.black01.opacity(0.2) : Colors.black20.opacity(0.2)
|
|
148
|
-
let navButtonConfig = getNavigationButtonConfig()
|
|
149
|
-
let showBadge = headerRight?.tools.contains { group in
|
|
150
|
-
group.items.contains { $0.showBadge }
|
|
151
|
-
} ?? false
|
|
152
|
-
|
|
153
|
-
HStack(alignment: .center, spacing: 0) {
|
|
154
|
-
if shouldShowShortcut {
|
|
155
|
-
NavigationButton(
|
|
156
|
-
disabled: isLoading,
|
|
157
|
-
icon: navButtonConfig.icon,
|
|
158
|
-
showBadge: showBadge,
|
|
159
|
-
onClick: navButtonConfig.onPress,
|
|
160
|
-
tintColor: tintColor
|
|
161
|
-
)
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
HStack(alignment: .center, spacing: 0) {
|
|
165
|
-
if environment.applicationContext != nil {
|
|
166
|
-
Icon(source: "help_center", size: 20, color: tintColor ?? Colors.black17)
|
|
167
|
-
.padding(4)
|
|
168
|
-
.onTapGesture {
|
|
169
|
-
onPressHelpCenter()
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
Rectangle()
|
|
173
|
-
.fill(tintColor ?? Colors.black20)
|
|
174
|
-
.frame(width: 0.5, height: 12)
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
Icon(source: "16_basic_home", size: 20, color: tintColor ?? Colors.black17)
|
|
178
|
-
.padding(4)
|
|
179
|
-
.onTapGesture {
|
|
180
|
-
onPressClose()
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
.frame(width: 65, height: 28)
|
|
184
|
-
.background(backgroundButtonColor)
|
|
185
|
-
.cornerRadius(14)
|
|
186
|
-
.overlay(
|
|
187
|
-
RoundedRectangle(cornerRadius: 14)
|
|
188
|
-
.stroke(borderColor, lineWidth: 0.2)
|
|
189
|
-
)
|
|
190
|
-
.padding(.leading, Spacing.S)
|
|
191
|
-
}
|
|
192
|
-
.onAppear {
|
|
193
|
-
loadFavoriteState()
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
}
|
package/local.properties
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
## This file must *NOT* be checked into Version Control Systems,
|
|
2
|
-
# as it contains information specific to your local configuration.
|
|
3
|
-
#
|
|
4
|
-
# Location of the SDK. This is only used by Gradle.
|
|
5
|
-
# For customization when using a Version Control System, please read the
|
|
6
|
-
# header note.
|
|
7
|
-
#Tue Jun 02 17:05:32 ICT 2026
|
|
8
|
-
sdk.dir=/Users/sophia/Library/Android/sdk
|