@momo-kits/native-kits 0.167.1-duo.1-debug → 0.167.1-duo.10-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.md +32 -0
- package/compose/build.gradle.kts +15 -1
- package/compose/build.gradle.kts.backup +14 -0
- package/compose/compose.podspec +1 -1
- package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +2 -0
- package/compose/src/androidMain/kotlin/vn/momo/kits/platform/ReservedRegions.android.kt +6 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +20 -21
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +2 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Carousel.kt +4 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +1 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Divider.kt +1 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +1 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +1 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +9 -6
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +11 -12
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +4 -9
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +2 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +10 -9
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +6 -6
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +53 -26
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +12 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/ProgressInfo.kt +7 -9
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +1 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Slider.kt +13 -6
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Swipe.kt +13 -5
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +3 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/TabView.kt +14 -6
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +19 -21
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +2 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tooltip.kt +5 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt +3 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +7 -15
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +13 -11
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +21 -9
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/ScreenLayout.kt +232 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +11 -9
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +43 -4
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +8 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +4 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +143 -31
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +100 -66
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +22 -16
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/DuoBarCompression.kt +98 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/DuoTabBar.kt +312 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +66 -18
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +23 -14
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderJourney.kt +3 -3
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderLocation.kt +1 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +81 -24
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +1 -2
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SideBar.kt +185 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +2 -1
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +3 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ReservedRegions.kt +13 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +4 -1
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +4 -0
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/ReservedRegions.ios.kt +86 -0
- package/gradle.properties +1 -1
- package/ios/Application/ApplicationEnvironment.swift +17 -0
- package/ios/Application/ComposeApi.swift +2 -1
- package/ios/Application/{Components.swift → LegacyHeader.swift} +7 -65
- package/ios/Application/Screen.swift +3 -2
- package/ios/Avatar/Avatar.swift +4 -3
- package/ios/Badge/Badge.swift +6 -4
- package/ios/Badge/BadgeDot.swift +1 -1
- package/ios/Badge/BadgeRibbon.swift +0 -1
- package/ios/Button/Button.swift +15 -12
- package/ios/CalculatorKeyboard/CalculatorKeyboard.swift +64 -18
- package/ios/Carousel/Carousel.swift +29 -12
- package/ios/Checkbox/Checkbox.swift +7 -3
- package/ios/Chip/Chip.swift +5 -4
- package/ios/Collapse/Collapse.swift +70 -19
- package/ios/Colors+Radius+Spacing/Colors.swift +10 -1
- package/ios/DateTimePicker/DateTimePicker.swift +54 -20
- package/ios/DateTimePicker/WheelPicker.swift +103 -105
- package/ios/Divider/Divider.swift +37 -4
- package/ios/Extensions/Color++.swift +38 -9
- package/ios/Extensions/OptionalEnvironmentObject.swift +12 -0
- package/ios/Extensions/SkeletonUICompat.swift +11 -0
- package/ios/Icon/Icon.swift +21 -23
- package/ios/Icon/Icons.swift +1329 -0
- package/ios/IconButton/IconButton.swift +15 -7
- package/ios/Image/Image.swift +7 -23
- package/ios/Information/Information.swift +27 -9
- package/ios/Input/ErrorView.swift +2 -2
- package/ios/Input/Input.swift +160 -147
- package/ios/Input/InputPhoneNumber.swift +12 -9
- package/ios/Input/InputSearch.swift +102 -75
- package/ios/Input/InputTextArea.swift +128 -145
- package/ios/InputDropDown/InputDropDown.swift +10 -7
- package/ios/InputMoney/InputMoney.swift +33 -11
- package/ios/InputOTP/InputOTP.swift +22 -12
- package/ios/Layout/Card.swift +50 -0
- package/ios/Layout/GridContext.swift +123 -3
- package/ios/Layout/Item.swift +5 -4
- package/ios/Layout/ScreenLayout.swift +312 -0
- package/ios/Layout/Section.swift +4 -100
- package/ios/Loader/Loader.swift +5 -9
- package/ios/Lottie/LottieView.swift +14 -2
- package/ios/{Application/Navigation/Overplay → Navigation}/BottomSheet.swift +97 -8
- package/ios/Navigation/BottomTab/BottomTab.swift +187 -0
- package/ios/{Application/Navigation → Navigation}/BottomTab/BottomTabBar.swift +15 -7
- package/ios/Navigation/BottomTab/DuoBarCompression.swift +105 -0
- package/ios/Navigation/BottomTab/DuoTabBar.swift +304 -0
- package/ios/{Application → Navigation/Component}/FloatingButton.swift +14 -3
- package/ios/Navigation/Component/Header.swift +493 -0
- package/ios/Navigation/Component/HeaderBackground.swift +134 -0
- package/ios/Navigation/Component/HeaderJourney.swift +73 -0
- package/ios/Navigation/Component/HeaderLocation.swift +48 -0
- package/ios/Navigation/Component/HeaderRight.swift +386 -0
- package/ios/Navigation/Component/HeaderTitle.swift +36 -0
- package/ios/{Application/Navigation → Navigation/Component}/HeaderUser.swift +21 -24
- package/ios/Navigation/Component/SideBar.swift +176 -0
- package/ios/{Application/Navigation/Overplay → Navigation/Component}/SnackBar.swift +5 -5
- package/ios/Navigation/HostBackGestureBridge.swift +57 -0
- package/ios/{Application/Navigation/Overplay → Navigation}/ModalScreen.swift +5 -1
- package/ios/Navigation/Navigation.swift +190 -0
- package/ios/{Application/Navigation → Navigation}/NavigationContainer.swift +92 -81
- package/ios/{Application/Navigation → Navigation}/Navigator.swift +185 -36
- package/ios/Navigation/StackScreen.swift +782 -0
- package/ios/{Application/Navigation → Navigation}/Tracking/ScreenTracker.swift +12 -5
- package/ios/OTPKeyboard/OTPKeyboard.swift +8 -11
- package/ios/Pagination/PaginationScroll.swift +2 -2
- package/ios/Popup/PopupDisplay.swift +72 -136
- package/ios/Popup/PopupInput.swift +10 -13
- package/ios/Popup/PopupLayout.swift +140 -0
- package/ios/Popup/PopupNotify.swift +56 -73
- package/ios/Popup/PopupPromotion.swift +31 -47
- package/ios/PopupView/FullscreenPopup.swift +13 -19
- package/ios/PopupView/PopupView.swift +1 -1
- package/ios/PopupView/Utils++.swift +0 -8
- package/ios/ProgressInfo/ProgressInfo.swift +4 -2
- package/ios/Radio/Radio.swift +2 -1
- package/ios/Rating/Rating.swift +9 -1
- package/ios/Resources/dot_loading.json +558 -0
- package/ios/Resources/lottie_circle_loader.json +1 -0
- package/ios/ScrollIndicator/ScrollIndicator.swift +11 -10
- package/ios/Skeleton/Skeleton.swift +22 -27
- package/ios/Slider/Slider.swift +16 -18
- package/ios/Stepper/Stepper.swift +6 -1
- package/ios/Steps/Steps.swift +8 -6
- package/ios/SuggestAction/SuggestAction.swift +5 -0
- package/ios/Swipeable/SwipeCell.swift +10 -6
- package/ios/Switch/Switch.swift +22 -20
- package/ios/TabView/TabView.swift +102 -21
- package/ios/Tag/Tag.swift +15 -4
- package/ios/Template/Logo/Logo.swift +2 -1
- package/ios/Template/TrustBanner/TrustBanner.swift +106 -54
- package/ios/Title/Title.swift +6 -17
- package/ios/Tooltip/Tooltip.swift +29 -14
- package/ios/Typography/Text.swift +61 -19
- package/ios/Uploader/Uploader.swift +3 -1
- package/ios/native-kits.podspec +2 -1
- package/ios-demo/MoMoUIKitsDemo.podspec +1 -1
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/ContentView.swift +1 -1
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DuoTabDemo.swift +80 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/Home.swift +1 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/IPhoneDuoDemo.swift +173 -0
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/NavigationDemo.swift +77 -1
- package/package.json +1 -1
- package/scripts/gen-ios-icons.mjs +36 -0
- package/.claude/settings.local.json +0 -185
- package/ios/Application/HeaderRight.swift +0 -306
- package/ios/Application/Navigation/BottomTab/BottomTab.swift +0 -104
- package/ios/Application/Navigation/HeaderBackProps.swift +0 -66
- package/ios/Application/Navigation/HeaderTitle.swift +0 -57
- package/ios/Application/Navigation/NavigationOptions.swift +0 -109
- package/ios/Application/Navigation/NavigatorManager.swift +0 -22
- package/ios/Application/Navigation/component/Header.swift +0 -178
- package/ios/Application/Navigation/component/HeaderBackground.swift +0 -83
- package/ios/Application/Navigation/component/HeaderColor.swift +0 -65
- package/ios/Application/Navigation/component/HeaderRightWidthKey.swift +0 -11
- package/ios/Application/Navigation/component/NavigationFooter.swift +0 -90
- package/ios/Application/Navigation/component/NavigationHeaderRight.swift +0 -196
- package/ios/Application/StackScreen.swift +0 -492
- package/local.properties +0 -8
- /package/ios/{Application/Navigation → Navigation}/BottomTab/CurvedContainer.swift +0 -0
package/CLAUDE.md
CHANGED
|
@@ -35,6 +35,13 @@ No unit-test suite is configured. Validate changes by compiling the relevant tar
|
|
|
35
35
|
./gradlew :sample:androidApp:installDebug # run demo on device/emulator
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
+
The SwiftUI kit (`ios/`, pod `MoMoUIKits`) compiles on its own through the sample's Pods project (`pod install` in `sample/iosApp` first when files are added):
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
xcodebuild -project sample/iosApp/Pods/Pods.xcodeproj -scheme MoMoUIKits -sdk iphonesimulator \
|
|
42
|
+
-destination 'generic/platform=iOS Simulator' build
|
|
43
|
+
```
|
|
44
|
+
|
|
38
45
|
Publishing is automated (`publish.sh` + GitLab CI on `[ci build]` / `main`). Do not publish manually unless asked: `sh publish.sh beta` / `sh publish.sh release` (Maven→GitLab + NPM).
|
|
39
46
|
|
|
40
47
|
## Architecture essentials
|
|
@@ -47,6 +54,31 @@ Publishing is automated (`publish.sh` + GitLab CI on `[ci build]` / `main`). Do
|
|
|
47
54
|
- Navigation uses a runtime `DynamicScreenRegistry`; overlays (modal/bottom-sheet/snackbar) go through `Navigator` + `OverplayComponentRegistry`.
|
|
48
55
|
- **iOS swipe-back:** the host owns the only edge-pan recogniser and replays it through `HostBackGestureBridge` (`navigation/HostBackGestureBridge.kt`; the kit framework is never exported, so the host reaches it through its own Koin-provided facade): `checkCanPop` / `canPopInner` / `pop` route the back into the kit stack, `started` / `progressed` / `completed` / `cancelled` seek the NavHost transition, `shouldUseFloatingArrowBack` is set by bottom tabs. `NavigationContainer` attaches itself automatically (`AttachHostBackGesture`, iOS only, detached by identity on dispose) — hosts need no wrapper and no `setNavigator`. `maxApi` is a required parameter of `NavigationContainer` — every container, nested or root, is handed its host bridge explicitly.
|
|
49
56
|
|
|
57
|
+
## SwiftUI kit (`ios/`)
|
|
58
|
+
|
|
59
|
+
- **Layout mirrors Compose:** `ios/Navigation/` follows `navigation/` file for file (`NavigationContainer`, `Navigator`, `Navigation` (options + per-screen `Navigation`), `StackScreen` (incl. `NavigationFooter`), `BottomSheet`, `ModalScreen`, `HostBackGestureBridge`, `BottomTab/`, `Component/`, `Tracking/`); `ios/Layout/ScreenLayout.swift` mirrors `layout/`. `ios/Application/` keeps only context (`ApplicationEnvironment`), `Localize*`, `MaxApi` and the deprecated `Screen` / `LegacyHeader` (Compose `application/`). Change a navigation piece on both sides together.
|
|
60
|
+
- **File basenames must be unique across the pod** — Swift fails with "filename used twice" even in different folders (hence `LegacyHeader.swift` next to `Navigation/Component/Header.swift`). New folders under `ios/` must be added to `source_files` in `ios/native-kits.podspec`.
|
|
61
|
+
- Screens update their own chrome at runtime through `@EnvironmentObject var navigation: Navigation` → `setOptions { $0.title = … }` (Compose `LocalNavigation`); bottom tabs keep one `Navigation` per tab. The host back bridge is `HostBackGestureBridge.shared` (`NavigatorManager` is a deprecated alias); a bottom tab off its first tab routes back to the first tab (`Navigator.tabBackHandler`, keyed by the hosting screen).
|
|
62
|
+
- The pod's minimum is **iOS 15**, but the navigation shell (`NavigationContainer`, `Navigator`, `HostBackGestureBridge`, `BottomTab`, overplay views) is **iOS 16+** (`@available(iOS 16.0, *)`) because it is built on `NavigationStack`. Keep components outside that shell iOS 15-compatible.
|
|
63
|
+
- `present` opens one `fullScreenCover` that hosts its own `NavigationStack` (`Navigator.presentedPath`): pushes and pops act on the presented flow while it is up, and each overplay (modal / bottom sheet / snackbar) renders in the context it was shown over (`OverplayItem.presentedId`).
|
|
64
|
+
- Icons are compiled in: `ios/Icon/Icons.swift` is generated from Compose `utils/Icons.kt` by `node scripts/gen-ios-icons.mjs` — don't hand-edit it. A host `icon.json` in the main bundle only adds keys the kit lacks.
|
|
65
|
+
- Bundled assets (colors, Lottie JSON) live in `ios/Resources` and ship in the `MoMoUIKitsResources` bundle; load them through `MoMoUIKitsResources.bundle`, never `Bundle.main`.
|
|
66
|
+
|
|
67
|
+
## Screen layout / iPhone Duo (port of RN `useScreenLayout`, momo-kits !664)
|
|
68
|
+
|
|
69
|
+
- `layout/ScreenLayout.kt`: `getScreenLayout()` returns window size, raw top/bottom + side insets, `contentWidth`, header geometry, the iPhone Duo `sideBar` and `duoTopInset`. `NavigationContainer` computes it once and provides `LocalScreenLayout`; reactive to fold/unfold and rotation (window size + `WindowInsets`). Metrics (`DuoMetrics`, 72pt iPhone header floor, 20pt status-bar threshold) mirror RN `Layout/utils.ts` — change both together.
|
|
70
|
+
- iPhone-only: everything gates on `isPhoneIdiom()` (`platform/`), never on "iOS", so iPad windows keep the regular layout. Two detectors, as in RN: `isIPhoneDuo` (window aspect < 1.6 or asymmetric side insets) drives layout; `isIPhoneDuoDevice` (short side of screen or window > 440, RN `device.ts`) drives popups, modal and bottom sheet.
|
|
71
|
+
- Header height is `headerHeight` (not `AppStatusBar + HEADER_HEIGHT`); the bar row is `headerBarHeight` and starts `headerStatusBarHeight` below the window top. When the status bar is off the top edge (iPhone landscape, iPhone Duo) `headerStatusBarHeight` is 0 so the title stays centered — except on the Duo outer display in portrait, where the header is 76pt and the row starts at 14pt so the title centers on the camera punchout.
|
|
72
|
+
- iPhone Duo with a side strip ≥ 60pt: back + toolkit leave the bar for `HeaderSideBar` (`navigation/component/SideBar.kt`, 48pt glass controls); custom header-right stays in the bar. `sideBar.top` is 170pt in portrait, 120pt on the inner display in landscape (height ≥ 600), 24pt on the outer display in landscape, or below the header when the status bar is on top; UIKit's `statusBarFrame` is useless on Duo. Unlike RN the column is clamped above footer (24pt gap) / rail (12pt gap).
|
|
73
|
+
- Reserved regions (iOS 27.1 `reservedRegionsOfKind:`, reached through the ObjC runtime so older SDKs still build): a probe `UIView` (`platform/ReservedRegions.ios.kt` on the Compose root view; `ReservedRegionsProbe` inside SwiftUI `momoScreenLayout()`) re-reads them in `layoutSubviews`, which UIKit re-runs when they change. They push `sideBar.top` below the status cluster as it grows for calls / Live Activities, and `sideBar.bottom` (≥ 24pt) above a camera at the bottom end of the strip.
|
|
74
|
+
- Tab bar on Duo is `bottomtab/DuoTabBar.kt`: a rail in the side strip (`sideBar.bottom` above the window bottom, QR below it) or, without a side bar, a floating bar spanning 6/8 grid columns. Both overlay the content; `duoFloatingTabBarInset` pads tab content, footer and FAB.
|
|
75
|
+
- Bar compression (`bottomtab/DuoBarCompression.kt`, HIG "Vertical controls"): when toolbar + rail don't fit in the strip, one is compressed — the toolkit into a `navigation_menu` button, the rail into one button showing the current tab — and tapping it swaps them; `BottomTab(compressionBehavior)` picks which starts compressed. If neither fits beside the other compressed, both rest compressed and a tap opens one for a moment. Side bar columns report their full height so the decision follows each screen's toolkit.
|
|
76
|
+
- Glass header: on Duo with a side bar, Default/Extended headers without search and with a `ScrollState` let content scroll under the bar (`usesDuoGlassHeader`); the bar fades in a translucent surface + hairline over 16pt (Compose has no backdrop blur — RN's fallback look), the gradient leaves with the content.
|
|
77
|
+
- Quick return header: glass headers and Animated headers (with a `ScrollState`) on the Duo side bar layout slide out with the content and back in on a reverse scroll; a half-shown header settles in or out once the scroll stops. The side bar is drawn apart from the header, so it stays put.
|
|
78
|
+
- Overlays on Duo: popups cap at 440pt wide and the room between `duoTopInset` and the bottom inset (body scrolls, actions stay); modal centers inside that room; bottom sheet uses the window height, centers at width ≥ 600 and pads by side insets in landscape.
|
|
79
|
+
- Font scale deliberately uses the short side (`min(w, h)`), unlike RN which reads the window width — landscape must not inflate every font.
|
|
80
|
+
- `StackScreen` pads content/footer/search/FAB by the side insets and provides an inset-consumed layout to its content; a bottom-tab root stays full width (tab bar + each tab's StackScreen handle insets). Grid columns use `contentWidth`.
|
|
81
|
+
|
|
50
82
|
## i18n / Localize (port of RN `Localize`)
|
|
51
83
|
|
|
52
84
|
- `application/Localize.kt`: everything i18n. `LocalizationObject` is a data class with required `vi` / `en` maps (`LocalizationObject(vi = mapOf(…), en = mapOf(…))`). `Localize` constructors take a `LocalizationObject` or a `(vi, en)` map pair; the constructor **merges** the kit defaults with the host data (host wins on key clash). Methods: `translate(key)` (missing/empty → returns the key), `translateData(map)` / `translateData(vi, en)`, `addTranslations` (existing keys win), `changeLanguage(lang)`. Default language `"vi"`, `"en"` only on exact match; language is snapshot state so a switch recomposes consumers.
|
package/compose/build.gradle.kts
CHANGED
|
@@ -40,7 +40,7 @@ kotlin {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
cocoapods {
|
|
43
|
-
version = "0.167.1-duo.
|
|
43
|
+
version = "0.167.1-duo.10-debug"
|
|
44
44
|
summary = "IOS Shared module"
|
|
45
45
|
homepage = "https://momo.vn"
|
|
46
46
|
ios.deploymentTarget = "15.0"
|
|
@@ -188,3 +188,17 @@ compose {
|
|
|
188
188
|
// }
|
|
189
189
|
// }
|
|
190
190
|
// }
|
|
191
|
+
|
|
192
|
+
// Xcode 27 rejects pods below iOS 15 (lottie-ios 4.4.3 declares 13.0), and the synthetic Podfile has no post_install hook.
|
|
193
|
+
val syntheticPodsProject = layout.buildDirectory.file("cocoapods/synthetic/ios/Pods/Pods.xcodeproj/project.pbxproj")
|
|
194
|
+
tasks.matching { it.name.startsWith("podInstallSynthetic") }.configureEach {
|
|
195
|
+
doLast {
|
|
196
|
+
val pbxproj = syntheticPodsProject.get().asFile
|
|
197
|
+
if (!pbxproj.exists()) return@doLast
|
|
198
|
+
val target = Regex("IPHONEOS_DEPLOYMENT_TARGET = ([0-9.]+);")
|
|
199
|
+
pbxproj.writeText(pbxproj.readText().replace(target) { match ->
|
|
200
|
+
val major = match.groupValues[1].substringBefore('.').toIntOrNull() ?: 0
|
|
201
|
+
if (major < 15) "IPHONEOS_DEPLOYMENT_TARGET = 15.0;" else match.value
|
|
202
|
+
})
|
|
203
|
+
}
|
|
204
|
+
}
|
|
@@ -188,3 +188,17 @@ mavenPublishing {
|
|
|
188
188
|
}
|
|
189
189
|
}
|
|
190
190
|
}
|
|
191
|
+
|
|
192
|
+
// Xcode 27 rejects pods below iOS 15 (lottie-ios 4.4.3 declares 13.0), and the synthetic Podfile has no post_install hook.
|
|
193
|
+
val syntheticPodsProject = layout.buildDirectory.file("cocoapods/synthetic/ios/Pods/Pods.xcodeproj/project.pbxproj")
|
|
194
|
+
tasks.matching { it.name.startsWith("podInstallSynthetic") }.configureEach {
|
|
195
|
+
doLast {
|
|
196
|
+
val pbxproj = syntheticPodsProject.get().asFile
|
|
197
|
+
if (!pbxproj.exists()) return@doLast
|
|
198
|
+
val target = Regex("IPHONEOS_DEPLOYMENT_TARGET = ([0-9.]+);")
|
|
199
|
+
pbxproj.writeText(pbxproj.readText().replace(target) { match ->
|
|
200
|
+
val major = match.groupValues[1].substringBefore('.').toIntOrNull() ?: 0
|
|
201
|
+
if (major < 15) "IPHONEOS_DEPLOYMENT_TARGET = 15.0;" else match.value
|
|
202
|
+
})
|
|
203
|
+
}
|
|
204
|
+
}
|
package/compose/compose.podspec
CHANGED
|
@@ -36,6 +36,8 @@ import androidx.activity.compose.BackHandler as AndroidBackHandler
|
|
|
36
36
|
|
|
37
37
|
actual fun getPlatformName(): String = "Android"
|
|
38
38
|
|
|
39
|
+
actual fun isPhoneIdiom(): Boolean = false
|
|
40
|
+
|
|
39
41
|
@Composable
|
|
40
42
|
actual fun getScreenDimensions(): ScreenDimension {
|
|
41
43
|
val config = LocalConfiguration.current
|
|
@@ -17,31 +17,30 @@ import vn.momo.kits.application.IsShowBaseLineDebug
|
|
|
17
17
|
import vn.momo.kits.const.*
|
|
18
18
|
import vn.momo.kits.modifier.conditional
|
|
19
19
|
|
|
20
|
+
private val badgePrimaryColors = listOf(
|
|
21
|
+
Color(0xFFF0F0F0),
|
|
22
|
+
Color(0xFFEB2F96),
|
|
23
|
+
Color(0xFF962AF0),
|
|
24
|
+
Color(0xFF4E4BFF),
|
|
25
|
+
Color(0xFF007AFF),
|
|
26
|
+
Color(0xFF13C2C2),
|
|
27
|
+
Color(0xFF34C759),
|
|
28
|
+
Color(0xFFA0D911),
|
|
29
|
+
Color(0xFFFFCC00),
|
|
30
|
+
Color(0xFFFA8C16),
|
|
31
|
+
Color(0xFFFA541C),
|
|
32
|
+
Color(0xFFF5222D)
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
private val badgeNumberRegex = "^\\d+$".toRegex()
|
|
20
36
|
|
|
21
37
|
@Composable
|
|
22
38
|
fun Badge(label: String = "Label", backgroundColor: Color? = null, modifier: Modifier = Modifier) {
|
|
23
|
-
|
|
24
|
-
Color(0xFFF0F0F0),
|
|
25
|
-
Color(0xFFEB2F96),
|
|
26
|
-
Color(0xFF962AF0),
|
|
27
|
-
Color(0xFF4E4BFF),
|
|
28
|
-
Color(0xFF007AFF),
|
|
29
|
-
Color(0xFF13C2C2),
|
|
30
|
-
Color(0xFF34C759),
|
|
31
|
-
Color(0xFFA0D911),
|
|
32
|
-
Color(0xFFFFCC00),
|
|
33
|
-
Color(0xFFFA8C16),
|
|
34
|
-
Color(0xFFFA541C),
|
|
35
|
-
Color(0xFFF5222D)
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
fun isNumber(label: String): Boolean {
|
|
39
|
-
val numberRegex = "^\\d+$".toRegex()
|
|
40
|
-
return numberRegex.matches(label)
|
|
41
|
-
}
|
|
39
|
+
fun isNumber(label: String): Boolean = badgeNumberRegex.matches(label)
|
|
42
40
|
|
|
43
41
|
fun formatTitle(label: String): String {
|
|
44
|
-
|
|
42
|
+
// Digits too long for Int are well over 99; toInt() would throw.
|
|
43
|
+
if (isNumber(label) && (label.toIntOrNull() ?: Int.MAX_VALUE) > 99) {
|
|
45
44
|
return "99+"
|
|
46
45
|
}
|
|
47
46
|
return label
|
|
@@ -54,7 +53,7 @@ fun Badge(label: String = "Label", backgroundColor: Color? = null, modifier: Mod
|
|
|
54
53
|
AppTheme.current.colors.warning.primary
|
|
55
54
|
}
|
|
56
55
|
|
|
57
|
-
if (backgroundColor != null &&
|
|
56
|
+
if (backgroundColor != null && badgePrimaryColors.contains(backgroundColor)) {
|
|
58
57
|
badgeColor = backgroundColor
|
|
59
58
|
}
|
|
60
59
|
val scaleSize = scaleSize(16f)
|
|
@@ -146,7 +146,8 @@ fun RenderIcon(
|
|
|
146
146
|
) {
|
|
147
147
|
val iconSize = remember(size) { getIconSize(size) }
|
|
148
148
|
val margin = remember(size) { getIconSpace(size) }
|
|
149
|
-
|
|
149
|
+
// null = untinted; Color.Unspecified would tint with a transparent color and hide the icon.
|
|
150
|
+
val color = if (useTintColor) textColor else null
|
|
150
151
|
|
|
151
152
|
val modifier = Modifier.padding(
|
|
152
153
|
start = if (isIconLeft) 0.dp else margin,
|
|
@@ -43,6 +43,8 @@ fun Carousel(
|
|
|
43
43
|
val pagerState = rememberPagerState(initialPage = startPage) { virtualPageCount }
|
|
44
44
|
|
|
45
45
|
val realIndex: (Int) -> Int = { page -> page % itemCount }
|
|
46
|
+
val currentOnSnapToItem by rememberUpdatedState(onSnapToItem)
|
|
47
|
+
val currentOnScrollIndexChanged by rememberUpdatedState(onScrollIndexChanged)
|
|
46
48
|
|
|
47
49
|
var isUserScrolling by remember { mutableStateOf(false) }
|
|
48
50
|
|
|
@@ -70,8 +72,8 @@ fun Carousel(
|
|
|
70
72
|
LaunchedEffect(pagerState) {
|
|
71
73
|
snapshotFlow { pagerState.currentPage }.collect { page ->
|
|
72
74
|
val real = realIndex(page)
|
|
73
|
-
|
|
74
|
-
|
|
75
|
+
currentOnSnapToItem(real)
|
|
76
|
+
currentOnScrollIndexChanged(real)
|
|
75
77
|
}
|
|
76
78
|
}
|
|
77
79
|
|
|
@@ -77,7 +77,7 @@ fun Chip(
|
|
|
77
77
|
horizontalArrangement = Arrangement.spacedBy(iconSpacing.dp)
|
|
78
78
|
) {
|
|
79
79
|
if (iconLeft != null) {
|
|
80
|
-
Icon(source = iconLeft, size =
|
|
80
|
+
Icon(source = iconLeft, size = iconSize.dp, color = leftTint)
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
if (!label.isNullOrEmpty()) {
|
|
@@ -18,7 +18,7 @@ import vn.momo.kits.const.Spacing
|
|
|
18
18
|
enum class DividerType { DEFAULT, DASH }
|
|
19
19
|
|
|
20
20
|
@Composable
|
|
21
|
-
fun Divider(useMargin: Boolean =
|
|
21
|
+
fun Divider(useMargin: Boolean = false, type: DividerType = DividerType.DEFAULT) {
|
|
22
22
|
val color = AppTheme.current.colors.border.default
|
|
23
23
|
val margin = if (useMargin) Spacing.XS else 0.dp
|
|
24
24
|
if (type == DividerType.DASH) {
|
|
@@ -90,7 +90,7 @@ fun Information(
|
|
|
90
90
|
width = 0.5.dp,
|
|
91
91
|
shape = RoundedCornerShape(Radius.S),
|
|
92
92
|
color = borderColor
|
|
93
|
-
).background(color = backgroundColor)
|
|
93
|
+
).background(color = backgroundColor, shape = RoundedCornerShape(Radius.S))
|
|
94
94
|
.conditional(IsShowBaseLineDebug) {
|
|
95
95
|
border(1.dp, Colors.blue_03)
|
|
96
96
|
}
|
|
@@ -16,6 +16,7 @@ import androidx.compose.ui.Alignment
|
|
|
16
16
|
import androidx.compose.ui.Modifier
|
|
17
17
|
import androidx.compose.ui.focus.onFocusChanged
|
|
18
18
|
import androidx.compose.ui.graphics.Color
|
|
19
|
+
import androidx.compose.ui.graphics.SolidColor
|
|
19
20
|
import androidx.compose.ui.text.AnnotatedString
|
|
20
21
|
import androidx.compose.ui.text.SpanStyle
|
|
21
22
|
import androidx.compose.ui.text.TextStyle
|
|
@@ -214,7 +215,7 @@ fun Input(
|
|
|
214
215
|
|
|
215
216
|
// Memoized color calculations
|
|
216
217
|
val theme = AppTheme.current
|
|
217
|
-
val colors = remember(disabled, theme) {
|
|
218
|
+
val colors = remember(disabled, theme, iconColor) {
|
|
218
219
|
if (disabled) {
|
|
219
220
|
val disabledColor = theme.colors.text.disable
|
|
220
221
|
Triple(disabledColor, disabledColor, disabledColor)
|
|
@@ -241,8 +242,9 @@ fun Input(
|
|
|
241
242
|
val lineHeight = 24.sp
|
|
242
243
|
val scaleFontSize = scaleSize(fontSize)
|
|
243
244
|
val scaleLineHeight = scaleSize(lineHeight)
|
|
245
|
+
val scaledHeight = scaleSize(size.values.height)
|
|
244
246
|
|
|
245
|
-
val textStyle = remember(textColor, fontWeight) {
|
|
247
|
+
val textStyle = remember(textColor, fontWeight, scaleFontSize, scaleLineHeight) {
|
|
246
248
|
TextStyle(
|
|
247
249
|
color = textColor,
|
|
248
250
|
fontSize = scaleFontSize,
|
|
@@ -268,7 +270,7 @@ fun Input(
|
|
|
268
270
|
|
|
269
271
|
val passwordFontFamily = getFontFamily(AppTheme.current.font, FontWeight.Normal)
|
|
270
272
|
val passwordFontSize = scaleSize(24.sp)
|
|
271
|
-
val visualTransformation = remember(secureTextEntry, inputState.passHidden, passwordFontFamily) {
|
|
273
|
+
val visualTransformation = remember(secureTextEntry, inputState.passHidden, passwordFontFamily, passwordFontSize) {
|
|
272
274
|
if (secureTextEntry && !inputState.passHidden)
|
|
273
275
|
PasswordVisualTransformation(fontFamily = passwordFontFamily, fontSize = passwordFontSize)
|
|
274
276
|
else
|
|
@@ -287,8 +289,9 @@ fun Input(
|
|
|
287
289
|
textStyle = textStyle,
|
|
288
290
|
visualTransformation = visualTransformation,
|
|
289
291
|
keyboardOptions = keyboardOptionsConfig,
|
|
292
|
+
cursorBrush = SolidColor(theme.colors.primary),
|
|
290
293
|
modifier = inputModifier
|
|
291
|
-
.height(
|
|
294
|
+
.height(scaledHeight)
|
|
292
295
|
.onFocusChanged { focusState ->
|
|
293
296
|
val wasFocused = inputState.isFocused
|
|
294
297
|
inputState = inputState.copy(
|
|
@@ -313,7 +316,7 @@ fun Input(
|
|
|
313
316
|
floatingIcon = floatingIcon,
|
|
314
317
|
titleColor = floatingTitleColor,
|
|
315
318
|
iconColor = floatingIconTintColor,
|
|
316
|
-
offsetY = -
|
|
319
|
+
offsetY = -scaledHeight / 2,
|
|
317
320
|
required = required,
|
|
318
321
|
)
|
|
319
322
|
|
|
@@ -359,7 +362,7 @@ fun Input(
|
|
|
359
362
|
}
|
|
360
363
|
|
|
361
364
|
// Clear button
|
|
362
|
-
if (inputState.isFocused && text.value.isNotEmpty()) {
|
|
365
|
+
if (!readOnly && inputState.isFocused && text.value.isNotEmpty()) {
|
|
363
366
|
Row(Modifier.padding(horizontal = Spacing.XS)) {
|
|
364
367
|
Icon(
|
|
365
368
|
source = "24_navigation_close_circle_full",
|
|
@@ -27,6 +27,7 @@ import androidx.compose.ui.Alignment
|
|
|
27
27
|
import androidx.compose.ui.Modifier
|
|
28
28
|
import androidx.compose.ui.focus.onFocusChanged
|
|
29
29
|
import androidx.compose.ui.graphics.Color
|
|
30
|
+
import androidx.compose.ui.graphics.SolidColor
|
|
30
31
|
import androidx.compose.ui.semantics.contentDescription
|
|
31
32
|
import androidx.compose.ui.semantics.semantics
|
|
32
33
|
import androidx.compose.ui.semantics.testTag
|
|
@@ -43,7 +44,9 @@ import vn.momo.kits.application.IsShowBaseLineDebug
|
|
|
43
44
|
import vn.momo.kits.const.Colors
|
|
44
45
|
import vn.momo.kits.const.Typography
|
|
45
46
|
import vn.momo.kits.const.getFontFamily
|
|
47
|
+
import vn.momo.kits.const.scaleSize
|
|
46
48
|
import vn.momo.kits.modifier.conditional
|
|
49
|
+
import vn.momo.kits.modifier.setAutomationId
|
|
47
50
|
|
|
48
51
|
private const val MONEY_CURRENCY = "đ"
|
|
49
52
|
|
|
@@ -134,7 +137,6 @@ fun InputMoney(
|
|
|
134
137
|
val current = if (digitsOf(field.text) == digits) field else moneyDisplay(digits).let {
|
|
135
138
|
TextFieldValue(it, TextRange(caretFromDigitIndex(it, digits.length)))
|
|
136
139
|
}
|
|
137
|
-
var isBlurred = false
|
|
138
140
|
val (textColor, placeholderColor, iconTintColor, floatingTitleColor, floatingIconTintColor) =
|
|
139
141
|
getInputColors(disabled, iconColor, floatingValueColor, floatingIconColor)
|
|
140
142
|
|
|
@@ -153,19 +155,16 @@ fun InputMoney(
|
|
|
153
155
|
value = current,
|
|
154
156
|
textStyle = TextStyle(
|
|
155
157
|
color = textColor,
|
|
156
|
-
fontSize = 20.sp,
|
|
157
|
-
lineHeight = 24.sp,
|
|
158
|
+
fontSize = scaleSize(20.sp),
|
|
159
|
+
lineHeight = scaleSize(24.sp),
|
|
158
160
|
fontWeight = fontWeight.value,
|
|
159
161
|
fontFamily = getFontFamily(AppTheme.current.font, fontWeight.value)
|
|
160
162
|
),
|
|
161
163
|
keyboardOptions = KeyboardOptions.Default.copy(keyboardType = keyboardType),
|
|
164
|
+
cursorBrush = SolidColor(AppTheme.current.colors.primary),
|
|
162
165
|
modifier = Modifier.height(size.values.height).onFocusChanged {
|
|
166
|
+
if (it.isFocused) onFocus() else if (isFocused) onBlur()
|
|
163
167
|
isFocused = it.isFocused
|
|
164
|
-
if (it.isFocused) {
|
|
165
|
-
onFocus()
|
|
166
|
-
}
|
|
167
|
-
if (!it.isFocused && isBlurred) onBlur()
|
|
168
|
-
if (it.isFocused && !isBlurred) isBlurred = true
|
|
169
168
|
},
|
|
170
169
|
onValueChange = { value ->
|
|
171
170
|
if (value.text == current.text) {
|
|
@@ -223,7 +222,7 @@ fun InputMoney(
|
|
|
223
222
|
color = leadingIconColor
|
|
224
223
|
)
|
|
225
224
|
}
|
|
226
|
-
Box(Modifier.weight(9f)
|
|
225
|
+
Box(Modifier.weight(9f), contentAlignment = Alignment.CenterStart) {
|
|
227
226
|
if (text.value.isEmpty()) {
|
|
228
227
|
Text(
|
|
229
228
|
text = placeholder,
|
|
@@ -234,9 +233,9 @@ fun InputMoney(
|
|
|
234
233
|
),
|
|
235
234
|
color = placeholderColor
|
|
236
235
|
)
|
|
237
|
-
} else {
|
|
238
|
-
innerTextField()
|
|
239
236
|
}
|
|
237
|
+
// Composed even when empty so the caret shows over the placeholder.
|
|
238
|
+
innerTextField()
|
|
240
239
|
}
|
|
241
240
|
if (isFocused && text.value.isNotEmpty()) {
|
|
242
241
|
Row {
|
|
@@ -252,7 +251,7 @@ fun InputMoney(
|
|
|
252
251
|
},
|
|
253
252
|
interactionSource = remember { MutableInteractionSource() },
|
|
254
253
|
indication = null
|
|
255
|
-
)
|
|
254
|
+
).setAutomationId("ic_clear")
|
|
256
255
|
)
|
|
257
256
|
}
|
|
258
257
|
}
|
|
@@ -101,7 +101,6 @@ fun InputOTP(
|
|
|
101
101
|
) {
|
|
102
102
|
val maxLength = length ?: 10
|
|
103
103
|
var isFocused by remember { mutableStateOf(false) }
|
|
104
|
-
var isBlurred = false
|
|
105
104
|
|
|
106
105
|
val handleChangeText: (String) -> Unit = { text ->
|
|
107
106
|
if (text.length <= maxLength &&
|
|
@@ -127,13 +126,9 @@ fun InputOTP(
|
|
|
127
126
|
onValueChange = handleChangeText,
|
|
128
127
|
singleLine = true,
|
|
129
128
|
keyboardOptions = KeyboardOptions.Default.copy(keyboardType = if (dataType == "number") KeyboardType.Number else KeyboardType.Ascii),
|
|
130
|
-
modifier =
|
|
129
|
+
modifier = Modifier.height(56.dp).onFocusChanged {
|
|
130
|
+
if (it.isFocused) onFocus() else if (isFocused) onBlur()
|
|
131
131
|
isFocused = it.isFocused
|
|
132
|
-
if (it.isFocused) {
|
|
133
|
-
onFocus()
|
|
134
|
-
}
|
|
135
|
-
if (!it.isFocused && isBlurred) onBlur()
|
|
136
|
-
if (it.isFocused && !isBlurred) isBlurred = true
|
|
137
132
|
},
|
|
138
133
|
decorationBox = { innerTextField ->
|
|
139
134
|
Box {
|
|
@@ -207,11 +202,11 @@ fun InputOTP(
|
|
|
207
202
|
style = Typography.headerDefaultBold,
|
|
208
203
|
text = value[i].toString()
|
|
209
204
|
)
|
|
210
|
-
if (i != value.length - 1 || (isFocused && value.length !=
|
|
205
|
+
if (i != value.length - 1 || (isFocused && value.length != maxLength)) {
|
|
211
206
|
Spacer(Modifier.width(Spacing.L))
|
|
212
207
|
}
|
|
213
208
|
}
|
|
214
|
-
if (isFocused && value.length !=
|
|
209
|
+
if (isFocused && value.length != maxLength) OTPCaret()
|
|
215
210
|
}
|
|
216
211
|
}
|
|
217
212
|
}
|
|
@@ -89,7 +89,7 @@ fun InputPhoneNumber(
|
|
|
89
89
|
|
|
90
90
|
// Memoized color calculations
|
|
91
91
|
val theme = AppTheme.current
|
|
92
|
-
val colors = remember(
|
|
92
|
+
val colors = remember(theme, iconColor) {
|
|
93
93
|
Triple(
|
|
94
94
|
theme.colors.text.default, // textColor
|
|
95
95
|
theme.colors.text.hint, // placeholderColor
|
|
@@ -173,7 +173,7 @@ fun InputPhoneNumber(
|
|
|
173
173
|
Box(
|
|
174
174
|
modifier = Modifier.width(1.dp)
|
|
175
175
|
.height(size.values.dividerHeight)
|
|
176
|
-
.background(
|
|
176
|
+
.background(theme.colors.border.default)
|
|
177
177
|
)
|
|
178
178
|
}
|
|
179
179
|
|
|
@@ -29,6 +29,7 @@ import androidx.compose.ui.Alignment
|
|
|
29
29
|
import androidx.compose.ui.Modifier
|
|
30
30
|
import androidx.compose.ui.focus.onFocusChanged
|
|
31
31
|
import androidx.compose.ui.graphics.Color
|
|
32
|
+
import androidx.compose.ui.graphics.SolidColor
|
|
32
33
|
import androidx.compose.ui.graphics.takeOrElse
|
|
33
34
|
import androidx.compose.ui.text.TextStyle
|
|
34
35
|
import androidx.compose.ui.text.input.KeyboardType
|
|
@@ -99,7 +100,8 @@ data class InputSearchProps(
|
|
|
99
100
|
val loading: Boolean = false,
|
|
100
101
|
val fontWeight: InputFontWeight = InputFontWeight.REGULAR,
|
|
101
102
|
val keyboardType: KeyboardType = KeyboardType.Text,
|
|
102
|
-
|
|
103
|
+
// Unspecified resolves to the theme surface (white in the light theme).
|
|
104
|
+
val backgroundColor: Color = Color.Unspecified,
|
|
103
105
|
val clearCondition: (() -> Boolean)? = null,
|
|
104
106
|
val keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
|
|
105
107
|
val keyboardActions: KeyboardActions = KeyboardActions.Default,
|
|
@@ -118,7 +120,6 @@ fun InputSearch(
|
|
|
118
120
|
),
|
|
119
121
|
) {
|
|
120
122
|
var isFocused by remember { mutableStateOf(false) }
|
|
121
|
-
var isBlurred = false
|
|
122
123
|
val disabledColor = AppTheme.current.colors.text.disable
|
|
123
124
|
var textColor = AppTheme.current.colors.text.default
|
|
124
125
|
var placeholderColor = AppTheme.current.colors.text.hint
|
|
@@ -134,7 +135,7 @@ fun InputSearch(
|
|
|
134
135
|
|
|
135
136
|
val fontFamily = getFontFamily(AppTheme.current.font, inputSearchProps.fontWeight.value)
|
|
136
137
|
|
|
137
|
-
val textStyle = remember(textColor, inputSearchProps.fontWeight.value, fontFamily) {
|
|
138
|
+
val textStyle = remember(textColor, inputSearchProps.fontWeight.value, fontFamily, scaleFontSize) {
|
|
138
139
|
TextStyle(
|
|
139
140
|
color = textColor,
|
|
140
141
|
fontSize = scaleFontSize,
|
|
@@ -167,13 +168,13 @@ fun InputSearch(
|
|
|
167
168
|
textStyle = textStyle,
|
|
168
169
|
keyboardOptions = inputSearchProps.keyboardOptions.copy(keyboardType = inputSearchProps.keyboardType),
|
|
169
170
|
keyboardActions = inputSearchProps.keyboardActions,
|
|
171
|
+
cursorBrush = SolidColor(AppTheme.current.colors.primary),
|
|
172
|
+
// Transitions only: onFocusChanged also runs once on attach with isFocused = false.
|
|
170
173
|
modifier = inputSearchProps.modifier.weight(1f).fillMaxHeight().onFocusChanged {
|
|
174
|
+
val wasFocused = isFocused
|
|
171
175
|
isFocused = it.isFocused
|
|
172
|
-
if (it.isFocused)
|
|
173
|
-
|
|
174
|
-
}
|
|
175
|
-
if (!it.isFocused && isBlurred) inputSearchProps.onBlur()
|
|
176
|
-
if (it.isFocused && !isBlurred) isBlurred = true
|
|
176
|
+
if (it.isFocused && !wasFocused) inputSearchProps.onFocus()
|
|
177
|
+
if (!it.isFocused && wasFocused) inputSearchProps.onBlur()
|
|
177
178
|
},
|
|
178
179
|
onValueChange = inputSearchProps.onChangeText,
|
|
179
180
|
decorationBox = { innerTextField ->
|
|
@@ -186,7 +187,7 @@ fun InputSearch(
|
|
|
186
187
|
)
|
|
187
188
|
).fillMaxHeight()
|
|
188
189
|
.background(
|
|
189
|
-
inputSearchProps.backgroundColor,
|
|
190
|
+
inputSearchProps.backgroundColor.takeOrElse { AppTheme.current.colors.background.surface },
|
|
190
191
|
RoundedCornerShape(Radius.XL)
|
|
191
192
|
),
|
|
192
193
|
horizontalArrangement = Arrangement.Start,
|
|
@@ -13,6 +13,7 @@ import androidx.compose.ui.Alignment
|
|
|
13
13
|
import androidx.compose.ui.Modifier
|
|
14
14
|
import androidx.compose.ui.focus.onFocusChanged
|
|
15
15
|
import androidx.compose.ui.graphics.Color
|
|
16
|
+
import androidx.compose.ui.graphics.SolidColor
|
|
16
17
|
import androidx.compose.ui.text.TextStyle
|
|
17
18
|
import androidx.compose.ui.text.input.KeyboardType
|
|
18
19
|
import androidx.compose.ui.unit.Dp
|
|
@@ -56,7 +57,6 @@ fun InputTextArea(
|
|
|
56
57
|
keyboardType: KeyboardType = KeyboardType.Text,
|
|
57
58
|
) {
|
|
58
59
|
var isFocused by remember { mutableStateOf(false) }
|
|
59
|
-
var isBlurred = false
|
|
60
60
|
val (textColor, placeholderColor, iconTintColor, floatingTitleColor, floatingIconTintColor) =
|
|
61
61
|
getInputColors(disabled, iconColor, floatingValueColor, floatingIconColor)
|
|
62
62
|
|
|
@@ -81,13 +81,13 @@ fun InputTextArea(
|
|
|
81
81
|
fontFamily = getFontFamily(AppTheme.current.font, fontWeight.value)
|
|
82
82
|
),
|
|
83
83
|
keyboardOptions = KeyboardOptions.Default.copy(keyboardType = keyboardType),
|
|
84
|
+
cursorBrush = SolidColor(AppTheme.current.colors.primary),
|
|
85
|
+
// Transitions only: onFocusChanged also runs once on attach with isFocused = false.
|
|
84
86
|
modifier = Modifier.height(scaleHeight).onFocusChanged {
|
|
87
|
+
val wasFocused = isFocused
|
|
85
88
|
isFocused = it.isFocused
|
|
86
|
-
if (it.isFocused)
|
|
87
|
-
|
|
88
|
-
}
|
|
89
|
-
if (!it.isFocused && isBlurred) onBlur()
|
|
90
|
-
if (it.isFocused && !isBlurred) isBlurred = true
|
|
89
|
+
if (it.isFocused && !wasFocused) onFocus()
|
|
90
|
+
if (!it.isFocused && wasFocused) onBlur()
|
|
91
91
|
},
|
|
92
92
|
onValueChange = {
|
|
93
93
|
if (it.length <= maxLength) {
|