@momo-kits/native-kits 0.167.1-duo.3-debug → 0.167.1-duo.4
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 +17 -8
- package/ios/Application/ApplicationEnvironment.swift +6 -0
- package/ios/Application/Components.swift +1 -3
- package/ios/Application/HeaderRight.swift +19 -27
- package/ios/Application/Navigation/BottomTab/BottomTab.swift +9 -30
- package/ios/Application/Navigation/BottomTab/BottomTabBar.swift +2 -5
- package/ios/Application/Navigation/HeaderUser.swift +1 -7
- package/ios/Application/Navigation/NavigationContainer.swift +45 -45
- package/ios/Application/Navigation/Navigator.swift +122 -29
- package/ios/Application/Navigation/NavigatorManager.swift +13 -6
- package/ios/Application/Navigation/Overplay/BottomSheet.swift +4 -41
- package/ios/Application/Navigation/Overplay/ModalScreen.swift +1 -5
- package/ios/Application/Navigation/component/Header.swift +4 -12
- package/ios/Application/Navigation/component/HeaderBackground.swift +2 -3
- package/ios/Application/Navigation/component/NavigationFooter.swift +1 -1
- package/ios/Application/Navigation/component/NavigationHeaderRight.swift +9 -61
- package/ios/Application/StackScreen.swift +18 -135
- package/ios/CalculatorKeyboard/CalculatorKeyboard.swift +59 -5
- package/ios/Colors+Radius+Spacing/Colors.swift +1 -1
- package/ios/Extensions/Color++.swift +20 -9
- package/ios/Extensions/OptionalEnvironmentObject.swift +12 -0
- package/ios/Icon/Icon.swift +14 -22
- package/ios/Icon/Icons.swift +1329 -0
- package/ios/Image/Image.swift +16 -5
- package/ios/Lottie/LottieView.swift +4 -2
- package/ios/Popup/PopupDisplay.swift +68 -84
- package/ios/Popup/PopupNotify.swift +32 -43
- package/ios/Popup/PopupPromotion.swift +6 -18
- package/ios/Resources/dot_loading.json +558 -0
- package/ios/Resources/lottie_circle_loader.json +1 -0
- package/ios/Template/TrustBanner/TrustBanner.swift +69 -31
- package/ios/native-kits.podspec +1 -1
- package/ios-demo/MoMoUIKitsDemo.podspec +1 -1
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/Home.swift +0 -1
- package/package.json +1 -1
- package/scripts/gen-ios-icons.mjs +36 -0
- package/.claude/scheduled_tasks.lock +0 -1
- package/.claude/settings.local.json +0 -185
- package/build.gradle.kts +0 -11
- package/compose/build.gradle.kts +0 -204
- package/compose/build.gradle.kts.backup +0 -204
- package/compose/compose.podspec +0 -47
- package/compose/src/androidMain/kotlin/vn/momo/kits/const/HostFont.android.kt +0 -19
- package/compose/src/androidMain/kotlin/vn/momo/kits/navigation/ScrollToTop.android.kt +0 -6
- package/compose/src/androidMain/kotlin/vn/momo/kits/platform/BackGesture.android.kt +0 -12
- package/compose/src/androidMain/kotlin/vn/momo/kits/platform/OsFontScale.android.kt +0 -7
- package/compose/src/androidMain/kotlin/vn/momo/kits/platform/Platform.android.kt +0 -153
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/AnimationSearchInput.kt +0 -52
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Context.kt +0 -124
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/FloatingButton.kt +0 -183
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Header.kt +0 -207
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderAnimated.kt +0 -39
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderBackground.kt +0 -86
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderDefault.kt +0 -76
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderExtended.kt +0 -76
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderRight.kt +0 -293
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/HeaderTitle.kt +0 -33
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/LiteScreen.kt +0 -831
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Localize.kt +0 -269
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/NavigationContainer.kt +0 -110
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/Screen.kt +0 -378
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/WidgetContainer.kt +0 -56
- package/compose/src/commonMain/kotlin/vn/momo/kits/application/useHeaderSearchAnimation.kt +0 -69
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Avatar.kt +0 -157
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Badge.kt +0 -82
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeDot.kt +0 -32
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/BadgeRibbon.kt +0 -338
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/BaselineView.kt +0 -175
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Button.kt +0 -356
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Carousel.kt +0 -113
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/CheckBox.kt +0 -89
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Chip.kt +0 -128
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Collapse.kt +0 -209
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/CupertinoOverscroll.kt +0 -542
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Divider.kt +0 -49
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Icon.kt +0 -76
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/IconButton.kt +0 -125
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +0 -231
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Information.kt +0 -189
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Input.kt +0 -475
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputDropDown.kt +0 -114
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputMoney.kt +0 -266
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputOTP.kt +0 -240
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputPhoneNumber.kt +0 -223
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputSearch.kt +0 -264
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/InputTextArea.kt +0 -185
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/LazyColumnWithBouncing.kt +0 -343
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Loader.kt +0 -108
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationDot.kt +0 -56
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationNumber.kt +0 -41
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationScroll.kt +0 -84
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PaginationWhiteDot.kt +0 -40
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupNotify.kt +0 -361
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/PopupPromotion.kt +0 -108
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/ProgressInfo.kt +0 -310
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Radio.kt +0 -59
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Rating.kt +0 -87
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/ScaleSizeScope.kt +0 -21
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Skeleton.kt +0 -90
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Slider.kt +0 -323
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Stepper.kt +0 -220
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Steps.kt +0 -473
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/SuggestAction.kt +0 -122
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Swipe.kt +0 -199
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Switch.kt +0 -91
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/TabView.kt +0 -552
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tag.kt +0 -88
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Text.kt +0 -125
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Title.kt +0 -207
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Tooltip.kt +0 -498
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/TrustBanner.kt +0 -176
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Uploader.kt +0 -182
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePicker.kt +0 -193
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerTypes.kt +0 -29
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/DateTimePickerUtils.kt +0 -234
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/datetimepicker/WheelPicker.kt +0 -188
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Colors.kt +0 -325
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/HostFont.kt +0 -16
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitDesignSystem.kt +0 -18
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/KitFontScale.kt +0 -22
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Radius.kt +0 -12
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Spacing.kt +0 -16
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Theme.kt +0 -188
- package/compose/src/commonMain/kotlin/vn/momo/kits/const/Typography.kt +0 -297
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Card.kt +0 -54
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Item.kt +0 -50
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/ScreenLayout.kt +0 -187
- package/compose/src/commonMain/kotlin/vn/momo/kits/layout/Section.kt +0 -41
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/AutomationId.kt +0 -52
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Clickable.kt +0 -66
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Conditional.kt +0 -11
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/DeprecatedModifier.kt +0 -104
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Shadow.kt +0 -48
- package/compose/src/commonMain/kotlin/vn/momo/kits/modifier/Size.kt +0 -51
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/BottomSheet.kt +0 -338
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/HostBackGestureBridge.kt +0 -58
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ModalScreen.kt +0 -137
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigation.kt +0 -106
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/NavigationContainer.kt +0 -205
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/Navigator.kt +0 -393
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/ScrollToTop.kt +0 -8
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackScreen.kt +0 -648
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/StackTransition.kt +0 -148
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/SwipeBackHaptic.kt +0 -35
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTab.kt +0 -194
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/BottomTabBar.kt +0 -232
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/CurvedContainer.kt +0 -86
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/bottomtab/DuoTabBar.kt +0 -281
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/FloatingButton.kt +0 -169
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/Header.kt +0 -408
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderBackground.kt +0 -108
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderJourney.kt +0 -105
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderLocation.kt +0 -74
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderRight.kt +0 -354
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderTitle.kt +0 -37
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/HeaderUser.kt +0 -363
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SideBar.kt +0 -169
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/component/SnackBar.kt +0 -132
- package/compose/src/commonMain/kotlin/vn/momo/kits/navigation/tracking/ScreenTracker.kt +0 -127
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/BackGesture.kt +0 -12
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/ComposeLottieAnimation.kt +0 -91
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/OsFontScale.kt +0 -9
- package/compose/src/commonMain/kotlin/vn/momo/kits/platform/Platform.kt +0 -74
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Icons.kt +0 -1329
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Resources.kt +0 -55
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Tracking.kt +0 -15
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/Utils.kt +0 -118
- package/compose/src/iosMain/kotlin/vn/momo/kits/const/HostFont.ios.kt +0 -40
- package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/HostBackGesture.ios.kt +0 -45
- package/compose/src/iosMain/kotlin/vn/momo/kits/navigation/ScrollToTop.ios.kt +0 -32
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/BackGesture.ios.kt +0 -38
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/OsFontScale.ios.kt +0 -66
- package/compose/src/iosMain/kotlin/vn/momo/kits/platform/Platform.ios.kt +0 -217
- package/gradle/libs.versions.toml +0 -69
- package/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/gradle/wrapper/gradle-wrapper.properties +0 -8
- package/gradle.properties +0 -26
- package/gradlew +0 -252
- package/gradlew.bat +0 -94
- package/ios/Application/Layout/ScreenLayout.swift +0 -196
- package/ios/Application/Navigation/BottomTab/DuoTabBar.swift +0 -275
- package/ios/Application/Navigation/component/DuoScrollGradient.swift +0 -55
- package/ios/Application/Navigation/component/SideBar.swift +0 -155
- package/ios/Popup/PopupLayout.swift +0 -45
- package/ios-demo/README.md +0 -120
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/DuoTabDemo.swift +0 -76
- package/ios-demo/Sources/MoMoUIKitsDemo/SampleDemos/IPhoneDuoDemo.swift +0 -170
- package/local.properties +0 -8
- package/settings.gradle.kts +0 -64
|
@@ -104,9 +104,6 @@ public struct StackScreen<Content: View>: View {
|
|
|
104
104
|
@State private var isBottomTabRoot: Bool = false
|
|
105
105
|
|
|
106
106
|
@Environment(\.applicationEnvironment) private var appEnv
|
|
107
|
-
@Environment(\.screenLayout) private var layout
|
|
108
|
-
@Environment(\.duoTabBarInsets) private var duoTabBarInsets
|
|
109
|
-
@State private var footerHeight: CGFloat = 0
|
|
110
107
|
|
|
111
108
|
// Auto-tracking state (mirrors Compose's `remember { ScreenTrackingState() }` +
|
|
112
109
|
// the once-computed push/back `action`).
|
|
@@ -128,30 +125,6 @@ public struct StackScreen<Content: View>: View {
|
|
|
128
125
|
|
|
129
126
|
private static var headerHeight: CGFloat { 52 }
|
|
130
127
|
|
|
131
|
-
private var toolkit: (data: HeaderRightData?, tintColor: Color?)? { toolkitHeaderRight(headerRight) }
|
|
132
|
-
|
|
133
|
-
private var showsSideBar: Bool {
|
|
134
|
-
layout.sideBar != nil && headerType != .none && !isBottomTabRoot && (goBack != nil || toolkit != nil)
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/// iPhone Duo: content scrolls under a glass header instead of starting below it.
|
|
138
|
-
private var useDuoGlass: Bool {
|
|
139
|
-
layout.sideBar != nil && headerType != .none && animatedHeader == nil
|
|
140
|
-
&& inputSearchProps == nil && !headerTransparent && !fullScreenContent
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/// Height the footer, tab bar or home indicator takes from the bottom of the window.
|
|
144
|
-
private var bottomBarsHeight: CGFloat {
|
|
145
|
-
if footer != nil { return footerHeight + NavigationFooter.safeAreaBottom }
|
|
146
|
-
if duoTabBarInsets.sideBarReserve > 0 { return duoTabBarInsets.sideBarReserve - Spacing.XL }
|
|
147
|
-
return NavigationFooter.navigationBarInset
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/// Keyboard frames are in screen coordinates; UIScreen stays on the outer display on iPhone Duo.
|
|
151
|
-
private var keyboardReferenceHeight: CGFloat {
|
|
152
|
-
layout.isIPhoneDuo ? layout.height : UIScreen.main.bounds.height
|
|
153
|
-
}
|
|
154
|
-
|
|
155
128
|
/// Extra top space reserved inside the content so the animated search bar has
|
|
156
129
|
/// room below the header at scroll 0; it shrinks to 0 as the bar slides up.
|
|
157
130
|
private var animatedSearchInset: CGFloat {
|
|
@@ -190,18 +163,9 @@ public struct StackScreen<Content: View>: View {
|
|
|
190
163
|
hasFooter: footer != nil,
|
|
191
164
|
scrollOffset: scrollOffset
|
|
192
165
|
)
|
|
193
|
-
.padding(.bottom, footer == nil ? duoTabBarInsets.overlay : 0)
|
|
194
166
|
}
|
|
195
167
|
|
|
196
168
|
NavigationFooter(footerComponent: footer, keyboardSize: keyboardSize)
|
|
197
|
-
.padding(.bottom, duoTabBarInsets.overlay)
|
|
198
|
-
.background(
|
|
199
|
-
GeometryReader { proxy in
|
|
200
|
-
Color.clear
|
|
201
|
-
.onAppear { footerHeight = proxy.size.height }
|
|
202
|
-
.onChange(of: proxy.size.height) { footerHeight = $0 }
|
|
203
|
-
}
|
|
204
|
-
)
|
|
205
169
|
}
|
|
206
170
|
|
|
207
171
|
// Animated search bar overlay (mirrors Compose's SearchAnimated layer).
|
|
@@ -212,24 +176,7 @@ public struct StackScreen<Content: View>: View {
|
|
|
212
176
|
hiddenBack: !isBack,
|
|
213
177
|
hasHeaderRight: headerRight != nil,
|
|
214
178
|
headerRightWidth: headerRightWidth,
|
|
215
|
-
headerHeight: Self.headerHeight
|
|
216
|
-
useSideBar: layout.sideBar != nil,
|
|
217
|
-
hasToolkit: toolkit != nil
|
|
218
|
-
)
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
if showsSideBar, let sideBar = layout.sideBar {
|
|
222
|
-
HeaderSideBar(
|
|
223
|
-
sideBar: sideBar,
|
|
224
|
-
goBack: goBack,
|
|
225
|
-
toolkit: toolkit,
|
|
226
|
-
headerColor: getHeaderColor(
|
|
227
|
-
isAnimated: animatedHeader != nil,
|
|
228
|
-
isSurface: animatedHeader?.isSurface ?? true,
|
|
229
|
-
opacity: scrollOpacity,
|
|
230
|
-
tintColor: tintColor
|
|
231
|
-
),
|
|
232
|
-
bottomInset: bottomBarsHeight
|
|
179
|
+
headerHeight: Self.headerHeight
|
|
233
180
|
)
|
|
234
181
|
}
|
|
235
182
|
}
|
|
@@ -244,7 +191,7 @@ public struct StackScreen<Content: View>: View {
|
|
|
244
191
|
guard useAvoidKeyboard,
|
|
245
192
|
let endFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else { return }
|
|
246
193
|
withAnimation(.easeOut(duration: 0.25)) {
|
|
247
|
-
keyboardHeight =
|
|
194
|
+
keyboardHeight = UIScreen.main.bounds.height - endFrame.origin.y
|
|
248
195
|
}
|
|
249
196
|
}
|
|
250
197
|
// Mirrors Compose's `hideKeyboardOnTap()`: a simultaneous gesture so
|
|
@@ -299,25 +246,15 @@ public struct StackScreen<Content: View>: View {
|
|
|
299
246
|
scrollOffset: $scrollOffset,
|
|
300
247
|
scrollToTopCallback: scrollToTopCallback,
|
|
301
248
|
animatedSearchInset: animatedSearchInset,
|
|
302
|
-
bottomInset: duoTabBarInsets.overlay,
|
|
303
249
|
content: { animatedHeroBody(animatedHeader) }
|
|
304
250
|
)
|
|
305
251
|
.edgesIgnoringSafeArea(.all)
|
|
306
252
|
|
|
307
253
|
header
|
|
308
254
|
.background(
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
Color.clear.modifier(DuoBarMaterial(
|
|
313
|
-
shape: Rectangle(),
|
|
314
|
-
visible: scrollOffset > heroGlassStart(animatedHeader)
|
|
315
|
-
))
|
|
316
|
-
} else {
|
|
317
|
-
Colors.black01.opacity(scrollOpacity)
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
.edgesIgnoringSafeArea([.top, .horizontal])
|
|
255
|
+
Colors.black01
|
|
256
|
+
.opacity(scrollOpacity)
|
|
257
|
+
.edgesIgnoringSafeArea(.top)
|
|
321
258
|
)
|
|
322
259
|
.zIndex(100)
|
|
323
260
|
}
|
|
@@ -334,34 +271,9 @@ public struct StackScreen<Content: View>: View {
|
|
|
334
271
|
)
|
|
335
272
|
.zIndex(100)
|
|
336
273
|
|
|
337
|
-
StackScreenContent(scrollable: scrollable, scrollOffset: $scrollOffset, scrollToTopCallback: scrollToTopCallback, animatedSearchInset: animatedSearchInset,
|
|
274
|
+
StackScreenContent(scrollable: scrollable, scrollOffset: $scrollOffset, scrollToTopCallback: scrollToTopCallback, animatedSearchInset: animatedSearchInset, content: content)
|
|
338
275
|
.edgesIgnoringSafeArea(.all)
|
|
339
276
|
}
|
|
340
|
-
} else if useDuoGlass {
|
|
341
|
-
ZStack(alignment: .top) {
|
|
342
|
-
DuoScrollGradient(
|
|
343
|
-
scrollOffset: scrollOffset,
|
|
344
|
-
headerHeight: layout.headerHeight,
|
|
345
|
-
extended: headerType == .extended
|
|
346
|
-
)
|
|
347
|
-
|
|
348
|
-
StackScreenContent(
|
|
349
|
-
scrollable: scrollable,
|
|
350
|
-
scrollOffset: $scrollOffset,
|
|
351
|
-
scrollToTopCallback: scrollToTopCallback,
|
|
352
|
-
topInset: layout.headerHeight - layout.insets.top,
|
|
353
|
-
bottomInset: duoTabBarInsets.overlay,
|
|
354
|
-
content: content
|
|
355
|
-
)
|
|
356
|
-
|
|
357
|
-
header
|
|
358
|
-
.background(
|
|
359
|
-
Color.clear
|
|
360
|
-
.modifier(DuoBarMaterial(shape: Rectangle(), visible: scrollOffset > 0))
|
|
361
|
-
.edgesIgnoringSafeArea([.top, .horizontal])
|
|
362
|
-
)
|
|
363
|
-
.zIndex(100)
|
|
364
|
-
}
|
|
365
277
|
} else {
|
|
366
278
|
header
|
|
367
279
|
.background(
|
|
@@ -371,40 +283,28 @@ public struct StackScreen<Content: View>: View {
|
|
|
371
283
|
headerTransparent: headerTransparent,
|
|
372
284
|
fullScreenContent: fullScreenContent
|
|
373
285
|
)
|
|
374
|
-
.edgesIgnoringSafeArea(
|
|
286
|
+
.edgesIgnoringSafeArea(.top)
|
|
375
287
|
)
|
|
376
288
|
|
|
377
|
-
StackScreenContent(scrollable: scrollable, scrollOffset: $scrollOffset, scrollToTopCallback: scrollToTopCallback, animatedSearchInset: animatedSearchInset,
|
|
289
|
+
StackScreenContent(scrollable: scrollable, scrollOffset: $scrollOffset, scrollToTopCallback: scrollToTopCallback, animatedSearchInset: animatedSearchInset, content: content)
|
|
378
290
|
}
|
|
379
291
|
}
|
|
380
292
|
|
|
381
|
-
/// Edge-to-edge branches lay out outside the safe area; iPhone Duo content still keeps
|
|
382
|
-
/// clear of the side strips, as RN's Screen does.
|
|
383
|
-
private var duoInsetContent: some View {
|
|
384
|
-
content()
|
|
385
|
-
.padding(.leading, layout.insetLeft)
|
|
386
|
-
.padding(.trailing, layout.insetRight)
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
private func heroGlassStart(_ animatedHeader: AnimatedHeader) -> CGFloat {
|
|
390
|
-
max(layout.width / animatedHeader.aspectRatio.value - layout.headerHeight, 0)
|
|
391
|
-
}
|
|
392
|
-
|
|
393
293
|
/// Hero image + body for an animated header. The image fills the width at its
|
|
394
294
|
/// aspect-ratio height; the body is offset below the header band so it overlaps
|
|
395
295
|
/// the lower part of the image (mirrors Compose's image box + padded content).
|
|
396
296
|
@ViewBuilder
|
|
397
297
|
private func animatedHeroBody(_ animatedHeader: AnimatedHeader) -> some View {
|
|
398
|
-
let width =
|
|
298
|
+
let width = UIScreen.main.bounds.width
|
|
399
299
|
let imageHeight = width / animatedHeader.aspectRatio.value
|
|
400
|
-
let contentTopInset =
|
|
300
|
+
let contentTopInset = safeAreaTopInset() + HEADER_HEIGHT + layoutOffset
|
|
401
301
|
|
|
402
302
|
ZStack(alignment: .top) {
|
|
403
303
|
animatedHeader.composable(scrollOffset)
|
|
404
304
|
.frame(width: width, height: imageHeight)
|
|
405
305
|
.clipped()
|
|
406
306
|
|
|
407
|
-
|
|
307
|
+
content()
|
|
408
308
|
.padding(.top, contentTopInset)
|
|
409
309
|
}
|
|
410
310
|
.frame(maxWidth: .infinity, alignment: .top)
|
|
@@ -443,10 +343,6 @@ private struct StackScreenContent<Content: View>: View {
|
|
|
443
343
|
/// Extra top space reserved for the animated search bar (mirrors Compose's
|
|
444
344
|
/// MainContent animated top spacer). Zero for non-animated screens.
|
|
445
345
|
var animatedSearchInset: CGFloat = 0
|
|
446
|
-
/// Room for a header the content scrolls under (iPhone Duo glass header).
|
|
447
|
-
var topInset: CGFloat = 0
|
|
448
|
-
/// Room for a bar that overlays the content (iPhone Duo floating tab bar).
|
|
449
|
-
var bottomInset: CGFloat = 0
|
|
450
346
|
let content: () -> Content
|
|
451
347
|
|
|
452
348
|
private let topAnchorID = "stack_screen_top_anchor"
|
|
@@ -473,17 +369,13 @@ private struct StackScreenContent<Content: View>: View {
|
|
|
473
369
|
}
|
|
474
370
|
})
|
|
475
371
|
|
|
476
|
-
if animatedSearchInset
|
|
477
|
-
Spacer().frame(height: animatedSearchInset
|
|
372
|
+
if animatedSearchInset > 0 {
|
|
373
|
+
Spacer().frame(height: animatedSearchInset)
|
|
478
374
|
}
|
|
479
375
|
|
|
480
376
|
VStack {
|
|
481
377
|
content()
|
|
482
378
|
}
|
|
483
|
-
|
|
484
|
-
if bottomInset > 0 {
|
|
485
|
-
Spacer().frame(height: bottomInset)
|
|
486
|
-
}
|
|
487
379
|
}
|
|
488
380
|
}
|
|
489
381
|
.coordinateSpace(name: scrollSpace)
|
|
@@ -498,8 +390,6 @@ private struct StackScreenContent<Content: View>: View {
|
|
|
498
390
|
}
|
|
499
391
|
} else {
|
|
500
392
|
content()
|
|
501
|
-
.padding(.top, topInset)
|
|
502
|
-
.padding(.bottom, bottomInset)
|
|
503
393
|
}
|
|
504
394
|
}
|
|
505
395
|
}
|
|
@@ -517,23 +407,16 @@ private struct SearchAnimatedHeader: View {
|
|
|
517
407
|
let hasHeaderRight: Bool
|
|
518
408
|
let headerRightWidth: CGFloat
|
|
519
409
|
let headerHeight: CGFloat
|
|
520
|
-
var useSideBar: Bool = false
|
|
521
|
-
var hasToolkit: Bool = false
|
|
522
|
-
|
|
523
|
-
@Environment(\.screenLayout) private var layout
|
|
524
410
|
|
|
525
411
|
var body: some View {
|
|
526
412
|
let scroll = max(scrollOffset, 0)
|
|
527
413
|
|
|
528
|
-
// Insets are measured from the safe-area top
|
|
529
|
-
let
|
|
530
|
-
let
|
|
531
|
-
let maxTop = layout.headerHeight - layout.insets.top
|
|
414
|
+
// Insets are measured from the safe-area top (status bar handled by layout).
|
|
415
|
+
let minTop: CGFloat = 0
|
|
416
|
+
let maxTop = headerHeight
|
|
532
417
|
let minSide = Spacing.M
|
|
533
|
-
let maxStart: CGFloat = hiddenBack
|
|
534
|
-
let maxEnd
|
|
535
|
-
? Spacing.M
|
|
536
|
-
: headerRightWidth + (hasHeaderRight ? Spacing.M * 2 : Spacing.M)
|
|
418
|
+
let maxStart: CGFloat = hiddenBack ? Spacing.M : 48
|
|
419
|
+
let maxEnd = headerRightWidth + (hasHeaderRight ? Spacing.M * 2 : Spacing.M)
|
|
537
420
|
|
|
538
421
|
let top = min(max(maxTop - scroll, minTop), maxTop)
|
|
539
422
|
let start = min(max(minSide + scroll, minSide), maxStart)
|
|
@@ -26,7 +26,10 @@ public struct CalculatorKeyboard: View {
|
|
|
26
26
|
if (value.count > 0 ){
|
|
27
27
|
value.removeLast()
|
|
28
28
|
}
|
|
29
|
-
|
|
29
|
+
// Keys map 1:1 onto `tempValue` characters, so both trims stay aligned.
|
|
30
|
+
if (tempValue.count > 0 ){
|
|
31
|
+
tempValue.removeLast()
|
|
32
|
+
}
|
|
30
33
|
}
|
|
31
34
|
|
|
32
35
|
func onPressClear(key: String){
|
|
@@ -41,12 +44,63 @@ public struct CalculatorKeyboard: View {
|
|
|
41
44
|
if (tempValue.last?.isNumber == false){
|
|
42
45
|
tempValue.removeLast()
|
|
43
46
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
// Overflow and division by zero are treated like a negative result: cleared.
|
|
48
|
+
if let result = Self.evaluate(tempValue), result >= 0 {
|
|
49
|
+
value = String(result)
|
|
50
|
+
} else {
|
|
48
51
|
value = ""
|
|
49
52
|
}
|
|
53
|
+
// The next keys continue from the shown result, not the old expression.
|
|
54
|
+
tempValue = value
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/// Integer `+ - * /` with the usual precedence, as NSExpression did, but nil instead of a
|
|
58
|
+
/// crash when a number or result leaves `Int` range.
|
|
59
|
+
static func evaluate(_ expression: String) -> Int? {
|
|
60
|
+
var numbers: [Int] = []
|
|
61
|
+
var operators: [Character] = []
|
|
62
|
+
var digits = ""
|
|
63
|
+
for char in expression {
|
|
64
|
+
if ("0"..."9").contains(char) {
|
|
65
|
+
digits.append(char)
|
|
66
|
+
continue
|
|
67
|
+
}
|
|
68
|
+
guard let number = Int(digits) else { return nil }
|
|
69
|
+
numbers.append(number)
|
|
70
|
+
operators.append(char)
|
|
71
|
+
digits = ""
|
|
72
|
+
}
|
|
73
|
+
guard let last = Int(digits) else { return nil }
|
|
74
|
+
numbers.append(last)
|
|
75
|
+
|
|
76
|
+
var terms = [numbers[0]]
|
|
77
|
+
var signs: [Character] = []
|
|
78
|
+
for (op, number) in zip(operators, numbers.dropFirst()) {
|
|
79
|
+
switch op {
|
|
80
|
+
case "*":
|
|
81
|
+
let (product, overflow) = terms[terms.count - 1].multipliedReportingOverflow(by: number)
|
|
82
|
+
if overflow { return nil }
|
|
83
|
+
terms[terms.count - 1] = product
|
|
84
|
+
case "/":
|
|
85
|
+
guard number != 0 else { return nil }
|
|
86
|
+
terms[terms.count - 1] /= number
|
|
87
|
+
case "+", "-":
|
|
88
|
+
terms.append(number)
|
|
89
|
+
signs.append(op)
|
|
90
|
+
default:
|
|
91
|
+
return nil
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
var result = terms[0]
|
|
96
|
+
for (sign, term) in zip(signs, terms.dropFirst()) {
|
|
97
|
+
let (next, overflow) = sign == "+"
|
|
98
|
+
? result.addingReportingOverflow(term)
|
|
99
|
+
: result.subtractingReportingOverflow(term)
|
|
100
|
+
if overflow { return nil }
|
|
101
|
+
result = next
|
|
102
|
+
}
|
|
103
|
+
return result
|
|
50
104
|
}
|
|
51
105
|
|
|
52
106
|
func onPressKey(key:String){
|
|
@@ -133,7 +133,7 @@ public enum Colors {
|
|
|
133
133
|
public static let yellow06 = Color(hex: "ffe066")
|
|
134
134
|
public static let yellow05 = Color(hex: "ffd633")
|
|
135
135
|
public static let yellow04 = Color(hex: "ffd119")
|
|
136
|
-
public static let yellow03 = Color(hex: "
|
|
136
|
+
public static let yellow03 = Color(hex: "ffcc00")
|
|
137
137
|
public static let yellow02 = Color(hex: "e6b800")
|
|
138
138
|
public static let yellow01 = Color(hex: "cca300")
|
|
139
139
|
|
|
@@ -43,15 +43,26 @@ public extension Color {
|
|
|
43
43
|
)
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
/// CSS-style `RGB`, `RGBA`, `RRGGBB` or `RRGGBBAA`, `#` optional; anything else is black.
|
|
46
47
|
init(hex: String) {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
48
|
+
var digits = hex.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
49
|
+
if digits.hasPrefix("#") { digits.removeFirst() }
|
|
50
|
+
if digits.count == 3 || digits.count == 4 {
|
|
51
|
+
digits = String(digits.flatMap { [$0, $0] })
|
|
52
|
+
}
|
|
53
|
+
guard digits.count == 6 || digits.count == 8,
|
|
54
|
+
digits.allSatisfy(\.isHexDigit),
|
|
55
|
+
let value = UInt64(digits, radix: 16) else {
|
|
56
|
+
self.init(red: 0, green: 0, blue: 0)
|
|
57
|
+
return
|
|
58
|
+
}
|
|
59
|
+
let rgba = digits.count == 8 ? value : value << 8 | 0xFF
|
|
60
|
+
self.init(
|
|
61
|
+
.sRGB,
|
|
62
|
+
red: Double((rgba >> 24) & 0xFF) / 255,
|
|
63
|
+
green: Double((rgba >> 16) & 0xFF) / 255,
|
|
64
|
+
blue: Double((rgba >> 8) & 0xFF) / 255,
|
|
65
|
+
opacity: Double(rgba & 0xFF) / 255
|
|
66
|
+
)
|
|
56
67
|
}
|
|
57
68
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import SwiftUI
|
|
2
|
+
|
|
3
|
+
/// `@EnvironmentObject` that reads nil instead of trapping when nothing was injected.
|
|
4
|
+
/// `_store` is the backing field of the `@frozen` `EnvironmentObject`, so its layout is ABI-stable.
|
|
5
|
+
@propertyWrapper
|
|
6
|
+
struct OptionalEnvironmentObject<Object: ObservableObject>: DynamicProperty {
|
|
7
|
+
@EnvironmentObject private var object: Object
|
|
8
|
+
|
|
9
|
+
var wrappedValue: Object? {
|
|
10
|
+
Mirror(reflecting: _object).descendant("_store") as? Object
|
|
11
|
+
}
|
|
12
|
+
}
|
package/ios/Icon/Icon.swift
CHANGED
|
@@ -4,27 +4,16 @@ struct IconSource: Codable {
|
|
|
4
4
|
let uri: String
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
// The compiled-in map (Icons.swift) wins; a host `icon.json` in the main bundle only adds keys
|
|
8
|
+
// the kit doesn't know, so hosts that shipped one keep their extra icons.
|
|
9
|
+
let iconSources: [String: String] = {
|
|
10
|
+
guard let url = Bundle.main.url(forResource: "icon", withExtension: "json"),
|
|
11
|
+
let data = try? Data(contentsOf: url),
|
|
12
|
+
let hostIcons = try? JSONDecoder().decode([String: IconSource].self, from: data) else {
|
|
13
|
+
return kitIcons
|
|
11
14
|
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
let data = try Data(contentsOf: url)
|
|
15
|
-
let decoded = try JSONDecoder().decode([String: IconSource].self, from: data)
|
|
16
|
-
return decoded
|
|
17
|
-
} catch {
|
|
18
|
-
print("Error decoding icon.json: \(error)")
|
|
19
|
-
return [:]
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// Load the JSON data into a global variable
|
|
24
|
-
let iconSources: [String: IconSource] = loadIconSources()
|
|
25
|
-
|
|
26
|
-
// Icons that must never be tinted (mirror Compose `noThemeIcons`)
|
|
27
|
-
let noThemeIcons: [String] = ["ic_round_momo_tiny"]
|
|
15
|
+
return kitIcons.merging(hostIcons.mapValues(\.uri)) { kit, _ in kit }
|
|
16
|
+
}()
|
|
28
17
|
|
|
29
18
|
// Compose defaults `color` to the theme text color while `color = null` means "no tint".
|
|
30
19
|
// Swift can't tell an omitted argument from an explicit `nil`, so the default is this
|
|
@@ -53,8 +42,8 @@ public struct Icon: View {
|
|
|
53
42
|
}
|
|
54
43
|
|
|
55
44
|
public var body: some View {
|
|
56
|
-
// Mirror Compose: an https source is used as-is, anything else is a key into icon
|
|
57
|
-
if let uri = source.contains("https") ? source : iconSources[source]
|
|
45
|
+
// Mirror Compose: an https source is used as-is, anything else is a key into the icon map
|
|
46
|
+
if let uri = source.contains("https") ? source : iconSources[source] {
|
|
58
47
|
let requestedColor = color == iconThemeColor ? theme.colors.text.default : color
|
|
59
48
|
// Mirror Compose: certain icons (e.g. branded glyphs) are never tinted
|
|
60
49
|
let resolvedColor = noThemeIcons.contains(source) ? nil : requestedColor
|
|
@@ -63,6 +52,9 @@ public struct Icon: View {
|
|
|
63
52
|
// Mirrors Compose's auto-generated `contentDescription = "img|$iconUrl"`
|
|
64
53
|
// when no explicit label is provided.
|
|
65
54
|
.accessibilityLabel(accessibilityLabel ?? "img|\(uri)")
|
|
55
|
+
} else {
|
|
56
|
+
// Compose keeps the sized box for an unknown key, so layouts don't collapse.
|
|
57
|
+
Color.clear.frame(width: size, height: size)
|
|
66
58
|
}
|
|
67
59
|
}
|
|
68
60
|
}
|