@momo-kits/native-kits 0.162.2-beta.22-debug → 0.162.2-beta.23-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/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +20 -47
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +49 -22
- package/gradle.properties +1 -1
- package/ios/Button/Button.swift +23 -47
- package/package.json +3 -1
package/compose/build.gradle.kts
CHANGED
|
@@ -20,8 +20,6 @@ import androidx.compose.ui.Modifier
|
|
|
20
20
|
import androidx.compose.ui.draw.alpha
|
|
21
21
|
import androidx.compose.ui.draw.clip
|
|
22
22
|
import androidx.compose.ui.graphics.Color
|
|
23
|
-
import androidx.compose.ui.semantics.contentDescription
|
|
24
|
-
import androidx.compose.ui.semantics.semantics
|
|
25
23
|
import androidx.compose.ui.text.TextStyle
|
|
26
24
|
import androidx.compose.ui.text.style.TextOverflow
|
|
27
25
|
import androidx.compose.ui.unit.Dp
|
|
@@ -145,10 +143,9 @@ fun RenderIcon(
|
|
|
145
143
|
icon: String = "",
|
|
146
144
|
forceLoading: Boolean = false,
|
|
147
145
|
textColor: Color? = null,
|
|
148
|
-
iconOnly: Boolean = false,
|
|
149
146
|
) {
|
|
150
147
|
val iconSize = remember(size) { getIconSize(size) }
|
|
151
|
-
val margin = remember(size
|
|
148
|
+
val margin = remember(size) { getIconSpace(size) }
|
|
152
149
|
val color = if (useTintColor) textColor else Color.Unspecified
|
|
153
150
|
|
|
154
151
|
val modifier = Modifier.padding(
|
|
@@ -249,12 +246,6 @@ fun getButtonBackgroundColor(
|
|
|
249
246
|
private fun Color.withLoading(loading: Boolean): Color =
|
|
250
247
|
this.copy(alpha = if (loading) 0.75f else 1f)
|
|
251
248
|
|
|
252
|
-
/**
|
|
253
|
-
* When [iconOnly] is true the label is hidden and the button collapses to a compact,
|
|
254
|
-
* icon-sized box (it ignores [isFull] and always wraps its content). [title] stays
|
|
255
|
-
* required and is exposed as the button's accessibility label. Supply the glyph via
|
|
256
|
-
* [iconLeft] (preferred) or [iconRight]; while [loading] the spinner replaces the icon.
|
|
257
|
-
*/
|
|
258
249
|
@Composable
|
|
259
250
|
fun Button(
|
|
260
251
|
onClick: () -> Unit,
|
|
@@ -266,7 +257,6 @@ fun Button(
|
|
|
266
257
|
loading: Boolean = false,
|
|
267
258
|
useTintColor: Boolean = true,
|
|
268
259
|
isFull: Boolean = true,
|
|
269
|
-
iconOnly: Boolean = false,
|
|
270
260
|
modifier: Modifier = Modifier,
|
|
271
261
|
) {
|
|
272
262
|
val radius = remember(size) { size.value.radius }
|
|
@@ -274,7 +264,6 @@ fun Button(
|
|
|
274
264
|
val loadingOnLeft = remember(iconLeft, iconRight) {
|
|
275
265
|
shouldLoadingOnLeft(iconLeft, iconRight)
|
|
276
266
|
}
|
|
277
|
-
val iconOnlyIcon = remember(iconLeft, iconRight) { iconLeft.ifEmpty { iconRight } }
|
|
278
267
|
|
|
279
268
|
val sizeSpecs = remember(size) { size.value }
|
|
280
269
|
|
|
@@ -313,7 +302,7 @@ fun Button(
|
|
|
313
302
|
val bgColor = getButtonBackgroundColor(loading, type)
|
|
314
303
|
val textColor = getTextColor(loading, type)
|
|
315
304
|
|
|
316
|
-
val rootModifier = if (isFull
|
|
305
|
+
val rootModifier = if (isFull) {
|
|
317
306
|
Modifier
|
|
318
307
|
.fillMaxWidth()
|
|
319
308
|
.wrapContentHeight()
|
|
@@ -322,11 +311,7 @@ fun Button(
|
|
|
322
311
|
}
|
|
323
312
|
|
|
324
313
|
Box(
|
|
325
|
-
modifier = rootModifier
|
|
326
|
-
.then(clickableModifier)
|
|
327
|
-
.conditional(iconOnly) {
|
|
328
|
-
semantics(mergeDescendants = true) { contentDescription = title }
|
|
329
|
-
},
|
|
314
|
+
modifier = rootModifier.then(clickableModifier),
|
|
330
315
|
contentAlignment = Alignment.Center
|
|
331
316
|
) {
|
|
332
317
|
// Visual background (shrinks on press)
|
|
@@ -349,35 +334,23 @@ fun Button(
|
|
|
349
334
|
horizontalArrangement = Arrangement.Center,
|
|
350
335
|
verticalAlignment = Alignment.CenterVertically,
|
|
351
336
|
) {
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
textColor = textColor
|
|
370
|
-
)
|
|
371
|
-
RenderTitle(size, title, textColor = textColor)
|
|
372
|
-
RenderIcon(
|
|
373
|
-
size = size,
|
|
374
|
-
isIconLeft = false,
|
|
375
|
-
useTintColor = useTintColor,
|
|
376
|
-
icon = iconRight,
|
|
377
|
-
forceLoading = loading && !loadingOnLeft,
|
|
378
|
-
textColor = textColor
|
|
379
|
-
)
|
|
380
|
-
}
|
|
337
|
+
RenderIcon(
|
|
338
|
+
size = size,
|
|
339
|
+
isIconLeft = true,
|
|
340
|
+
useTintColor = useTintColor,
|
|
341
|
+
icon = iconLeft,
|
|
342
|
+
forceLoading = loading && loadingOnLeft,
|
|
343
|
+
textColor = textColor
|
|
344
|
+
)
|
|
345
|
+
RenderTitle(size, title, textColor = textColor)
|
|
346
|
+
RenderIcon(
|
|
347
|
+
size = size,
|
|
348
|
+
isIconLeft = false,
|
|
349
|
+
useTintColor = useTintColor,
|
|
350
|
+
icon = iconRight,
|
|
351
|
+
forceLoading = loading && !loadingOnLeft,
|
|
352
|
+
textColor = textColor
|
|
353
|
+
)
|
|
381
354
|
}
|
|
382
355
|
}
|
|
383
356
|
}
|
|
@@ -12,12 +12,14 @@ import androidx.compose.ui.Alignment
|
|
|
12
12
|
import androidx.compose.ui.Modifier
|
|
13
13
|
import androidx.compose.ui.draw.clip
|
|
14
14
|
import androidx.compose.ui.graphics.Color
|
|
15
|
+
import androidx.compose.ui.graphics.Shape
|
|
15
16
|
import androidx.compose.ui.unit.Dp
|
|
16
17
|
import androidx.compose.ui.unit.dp
|
|
17
18
|
import vn.momo.kits.application.IsShowBaseLineDebug
|
|
18
19
|
import vn.momo.kits.const.AppTheme
|
|
19
20
|
import vn.momo.kits.const.Colors
|
|
20
21
|
import vn.momo.kits.const.Radius
|
|
22
|
+
import vn.momo.kits.const.Spacing
|
|
21
23
|
import vn.momo.kits.modifier.conditional
|
|
22
24
|
|
|
23
25
|
data class IconSpecs(
|
|
@@ -46,8 +48,29 @@ enum class IconSize(val value: IconSpecs) {
|
|
|
46
48
|
)
|
|
47
49
|
}
|
|
48
50
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
+
enum class IconButtonShape {
|
|
52
|
+
CIRCLE,
|
|
53
|
+
SQUARE,
|
|
54
|
+
RECTANGLE
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Resolved footprint + corner radius for a (size, shape) pair.
|
|
58
|
+
data class IconButtonGeometry(
|
|
59
|
+
val width: Dp,
|
|
60
|
+
val height: Dp,
|
|
61
|
+
val radius: Dp
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
// CIRCLE keeps the square footprint fully rounded (legacy default); SQUARE/RECTANGLE use the
|
|
65
|
+
// standard button corner (Radius.S) and RECTANGLE widens the footprint so it reads horizontal.
|
|
66
|
+
internal fun resolveIconButtonGeometry(size: IconSize, shape: IconButtonShape): IconButtonGeometry {
|
|
67
|
+
val base = size.value.height
|
|
68
|
+
return when (shape) {
|
|
69
|
+
IconButtonShape.CIRCLE -> IconButtonGeometry(base, base, size.value.radius)
|
|
70
|
+
IconButtonShape.SQUARE -> IconButtonGeometry(base, base, Radius.S)
|
|
71
|
+
IconButtonShape.RECTANGLE -> IconButtonGeometry(base + Spacing.S, base, Radius.S)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
51
74
|
|
|
52
75
|
// Cache for icon sizes to avoid repeated calculations
|
|
53
76
|
private val iconSizeMap = mapOf(
|
|
@@ -56,34 +79,34 @@ private val iconSizeMap = mapOf(
|
|
|
56
79
|
)
|
|
57
80
|
|
|
58
81
|
@Composable
|
|
59
|
-
internal fun getIconStyle(type: ButtonType, color: Color? = null): Modifier {
|
|
82
|
+
internal fun getIconStyle(type: ButtonType, shape: Shape, color: Color? = null): Modifier {
|
|
60
83
|
val theme = AppTheme.current
|
|
61
84
|
|
|
62
|
-
return remember(type, color, theme) {
|
|
85
|
+
return remember(type, shape, color, theme) {
|
|
63
86
|
when (type) {
|
|
64
87
|
ButtonType.DISABLED -> Modifier
|
|
65
|
-
.background(theme.colors.background.disable,
|
|
66
|
-
.border(0.dp, Color.Unspecified,
|
|
88
|
+
.background(theme.colors.background.disable, shape)
|
|
89
|
+
.border(0.dp, Color.Unspecified, shape)
|
|
67
90
|
|
|
68
91
|
ButtonType.PRIMARY -> Modifier
|
|
69
|
-
.background(theme.colors.primary,
|
|
70
|
-
.border(0.dp, Color.Unspecified,
|
|
92
|
+
.background(theme.colors.primary, shape)
|
|
93
|
+
.border(0.dp, Color.Unspecified, shape)
|
|
71
94
|
|
|
72
95
|
ButtonType.SECONDARY -> Modifier
|
|
73
|
-
.background(theme.colors.background.surface,
|
|
74
|
-
.border(1.dp, theme.colors.border.default,
|
|
96
|
+
.background(theme.colors.background.surface, shape)
|
|
97
|
+
.border(1.dp, theme.colors.border.default, shape)
|
|
75
98
|
|
|
76
99
|
ButtonType.OUTLINE -> Modifier
|
|
77
|
-
.background(theme.colors.background.surface,
|
|
78
|
-
.border(1.dp, color ?: theme.colors.primary,
|
|
100
|
+
.background(theme.colors.background.surface, shape)
|
|
101
|
+
.border(1.dp, color ?: theme.colors.primary, shape)
|
|
79
102
|
|
|
80
103
|
ButtonType.TONAL -> Modifier
|
|
81
|
-
.background(theme.colors.background.tonal,
|
|
82
|
-
.border(0.dp, Color.Unspecified,
|
|
104
|
+
.background(theme.colors.background.tonal, shape)
|
|
105
|
+
.border(0.dp, Color.Unspecified, shape)
|
|
83
106
|
|
|
84
107
|
ButtonType.DANGER -> Modifier
|
|
85
|
-
.background(theme.colors.error.primary,
|
|
86
|
-
.border(0.dp, Color.Unspecified,
|
|
108
|
+
.background(theme.colors.error.primary, shape)
|
|
109
|
+
.border(0.dp, Color.Unspecified, shape)
|
|
87
110
|
|
|
88
111
|
ButtonType.TEXT -> Modifier
|
|
89
112
|
}
|
|
@@ -103,6 +126,7 @@ fun IconButton(
|
|
|
103
126
|
onClick: () -> Unit,
|
|
104
127
|
type: ButtonType = ButtonType.PRIMARY,
|
|
105
128
|
size: IconSize = IconSize.SMALL,
|
|
129
|
+
shape: IconButtonShape = IconButtonShape.CIRCLE,
|
|
106
130
|
icon: String = "",
|
|
107
131
|
) {
|
|
108
132
|
// Memoize icon size calculation
|
|
@@ -115,19 +139,22 @@ fun IconButton(
|
|
|
115
139
|
type != ButtonType.DISABLED
|
|
116
140
|
}
|
|
117
141
|
|
|
118
|
-
// Memoize
|
|
119
|
-
val
|
|
120
|
-
size
|
|
142
|
+
// Memoize footprint + corner shape for the requested shape
|
|
143
|
+
val geometry = remember(size, shape) {
|
|
144
|
+
resolveIconButtonGeometry(size, shape)
|
|
145
|
+
}
|
|
146
|
+
val cornerShape = remember(geometry.radius) {
|
|
147
|
+
RoundedCornerShape(geometry.radius)
|
|
121
148
|
}
|
|
122
149
|
|
|
123
150
|
Box(
|
|
124
151
|
modifier = Modifier
|
|
125
|
-
.size(
|
|
126
|
-
.then(getIconStyle(type))
|
|
152
|
+
.size(width = geometry.width, height = geometry.height)
|
|
153
|
+
.then(getIconStyle(type, cornerShape))
|
|
127
154
|
.conditional(IsShowBaseLineDebug) {
|
|
128
155
|
border(1.dp, Colors.blue_03)
|
|
129
156
|
}
|
|
130
|
-
.clip(
|
|
157
|
+
.clip(cornerShape)
|
|
131
158
|
.clickable(enabled = isEnabled, onClick = onClick),
|
|
132
159
|
contentAlignment = Alignment.Center
|
|
133
160
|
) {
|
package/gradle.properties
CHANGED
package/ios/Button/Button.swift
CHANGED
|
@@ -125,12 +125,7 @@ public struct Button: View {
|
|
|
125
125
|
var iconRight: AnyView?
|
|
126
126
|
var isFull: Bool
|
|
127
127
|
var loading: Bool
|
|
128
|
-
var iconOnly: Bool
|
|
129
128
|
|
|
130
|
-
/// When `iconOnly` is true the label is hidden and the button collapses to a compact,
|
|
131
|
-
/// icon-sized box (it ignores `isFull`). `title` stays required and is exposed as the
|
|
132
|
-
/// accessibility label. Supply the glyph via `iconLeft` (preferred) or `iconRight`;
|
|
133
|
-
/// while `loading` the spinner replaces the icon.
|
|
134
129
|
public init(
|
|
135
130
|
title: String = "",
|
|
136
131
|
action: @escaping () -> Void,
|
|
@@ -139,8 +134,7 @@ public struct Button: View {
|
|
|
139
134
|
iconLeft: AnyView? = nil,
|
|
140
135
|
iconRight: AnyView? = nil,
|
|
141
136
|
isFull: Bool = true,
|
|
142
|
-
loading: Bool = false
|
|
143
|
-
iconOnly: Bool = false
|
|
137
|
+
loading: Bool = false
|
|
144
138
|
) {
|
|
145
139
|
self.title = title
|
|
146
140
|
self.action = action
|
|
@@ -150,7 +144,6 @@ public struct Button: View {
|
|
|
150
144
|
self.iconRight = iconRight
|
|
151
145
|
self.isFull = isFull
|
|
152
146
|
self.loading = loading
|
|
153
|
-
self.iconOnly = iconOnly
|
|
154
147
|
}
|
|
155
148
|
|
|
156
149
|
private func shouldLoadingOnLeft(_ left: AnyView?, _ right: AnyView?) -> Bool {
|
|
@@ -173,46 +166,35 @@ public struct Button: View {
|
|
|
173
166
|
let border = type.borderColor
|
|
174
167
|
let borderWidth = type.borderWidth
|
|
175
168
|
|
|
176
|
-
|
|
169
|
+
SwiftUI.Button(action: {
|
|
177
170
|
if !loading, type != .disabled {
|
|
178
171
|
action()
|
|
179
172
|
}
|
|
180
173
|
}) {
|
|
181
174
|
HStack(spacing: size.iconSpacing) {
|
|
182
|
-
if
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
.lineLimit(1)
|
|
203
|
-
.truncationMode(.tail)
|
|
204
|
-
|
|
205
|
-
if loading && !loadingOnLeft {
|
|
206
|
-
LottieView(name: "lottie_circle_loader", loopMode: .loop)
|
|
207
|
-
.frame(width: size.iconSize, height: size.iconSize)
|
|
208
|
-
.colorMultiply(fg)
|
|
209
|
-
|
|
210
|
-
} else if let iconRight = iconRight {
|
|
211
|
-
iconRight.frame(width: size.iconSize, height: size.iconSize)
|
|
212
|
-
}
|
|
175
|
+
if loading && loadingOnLeft {
|
|
176
|
+
LottieView(name: "lottie_circle_loader", loopMode: .loop)
|
|
177
|
+
.frame(width: size.iconSize, height: size.iconSize)
|
|
178
|
+
.colorMultiply(fg)
|
|
179
|
+
|
|
180
|
+
} else if let iconLeft = iconLeft {
|
|
181
|
+
iconLeft.frame(width: size.iconSize, height: size.iconSize)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
MomoText(title, typography: size.typographyStyle, color: fg)
|
|
185
|
+
.lineLimit(1)
|
|
186
|
+
.truncationMode(.tail)
|
|
187
|
+
|
|
188
|
+
if loading && !loadingOnLeft {
|
|
189
|
+
LottieView(name: "lottie_circle_loader", loopMode: .loop)
|
|
190
|
+
.frame(width: size.iconSize, height: size.iconSize)
|
|
191
|
+
.colorMultiply(fg)
|
|
192
|
+
|
|
193
|
+
} else if let iconRight = iconRight {
|
|
194
|
+
iconRight.frame(width: size.iconSize, height: size.iconSize)
|
|
213
195
|
}
|
|
214
196
|
}
|
|
215
|
-
.frame(maxWidth: isFull
|
|
197
|
+
.frame(maxWidth: isFull ? .infinity : nil)
|
|
216
198
|
.padding(.horizontal, size.padding)
|
|
217
199
|
.frame(height: size.height)
|
|
218
200
|
.background(bg)
|
|
@@ -224,11 +206,5 @@ public struct Button: View {
|
|
|
224
206
|
.opacity(loading ? 0.75 : 1)
|
|
225
207
|
}
|
|
226
208
|
.disabled(type == .disabled || loading)
|
|
227
|
-
|
|
228
|
-
if iconOnly {
|
|
229
|
-
button.accessibilityLabel(Text(title))
|
|
230
|
-
} else {
|
|
231
|
-
button
|
|
232
|
-
}
|
|
233
209
|
}
|
|
234
210
|
}
|