@expo/ui 55.0.0 → 55.0.2
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/CHANGELOG.md +33 -0
- package/CONTRIBUTING.md +30 -0
- package/android/build.gradle +2 -2
- package/android/src/main/java/expo/modules/ui/DatePickerView.kt +90 -7
- package/android/src/main/java/expo/modules/ui/HorizontalFloatingToolbarView.kt +21 -8
- package/android/src/main/java/expo/modules/ui/ModifierRegistry.kt +105 -39
- package/android/src/main/java/expo/modules/ui/RNHostView.kt +204 -50
- package/android/src/main/java/expo/modules/ui/SwitchView.kt +70 -4
- package/android/src/main/java/expo/modules/ui/button/IconButton.kt +7 -1
- package/android/src/main/java/expo/modules/ui/convertibles/AnimatableFloat.kt +26 -0
- package/android/src/main/java/expo/modules/ui/convertibles/AnimationSpecParams.kt +93 -0
- package/android/src/main/java/expo/modules/ui/convertibles/GraphicsLayerParams.kt +24 -0
- package/build/jetpack-compose/Box/index.d.ts +14 -0
- package/build/jetpack-compose/Box/index.d.ts.map +1 -0
- package/build/jetpack-compose/Column/index.d.ts +22 -0
- package/build/jetpack-compose/Column/index.d.ts.map +1 -0
- package/build/jetpack-compose/DatePicker/index.d.ts +99 -0
- package/build/jetpack-compose/DatePicker/index.d.ts.map +1 -1
- package/build/jetpack-compose/FlowRow/index.d.ts +14 -0
- package/build/jetpack-compose/FlowRow/index.d.ts.map +1 -0
- package/build/jetpack-compose/LazyColumn/index.d.ts +0 -3
- package/build/jetpack-compose/LazyColumn/index.d.ts.map +1 -1
- package/build/jetpack-compose/Row/index.d.ts +22 -0
- package/build/jetpack-compose/Row/index.d.ts.map +1 -0
- package/build/jetpack-compose/Spacer/index.d.ts +2 -2
- package/build/jetpack-compose/Switch/index.d.ts +18 -0
- package/build/jetpack-compose/Switch/index.d.ts.map +1 -1
- package/build/jetpack-compose/index.d.ts +4 -1
- package/build/jetpack-compose/index.d.ts.map +1 -1
- package/build/jetpack-compose/layout-types.d.ts +26 -0
- package/build/jetpack-compose/layout-types.d.ts.map +1 -0
- package/build/jetpack-compose/layout.d.ts +5 -36
- package/build/jetpack-compose/layout.d.ts.map +1 -1
- package/build/jetpack-compose/modifiers/animation.d.ts +44 -0
- package/build/jetpack-compose/modifiers/animation.d.ts.map +1 -0
- package/build/jetpack-compose/modifiers/index.d.ts +50 -3
- package/build/jetpack-compose/modifiers/index.d.ts.map +1 -1
- package/build/swift-ui/AccessoryWidgetBackground/index.d.ts +4 -0
- package/build/swift-ui/AccessoryWidgetBackground/index.d.ts.map +1 -0
- package/build/swift-ui/ConfirmationDialog/index.d.ts.map +1 -1
- package/build/swift-ui/ContextMenu/index.d.ts.map +1 -1
- package/build/swift-ui/ControlGroup/index.d.ts +29 -0
- package/build/swift-ui/ControlGroup/index.d.ts.map +1 -0
- package/build/swift-ui/Gauge/index.d.ts.map +1 -1
- package/build/swift-ui/Image/index.d.ts +7 -1
- package/build/swift-ui/Image/index.d.ts.map +1 -1
- package/build/swift-ui/Label/index.d.ts.map +1 -1
- package/build/swift-ui/LabeledContent/index.d.ts.map +1 -1
- package/build/swift-ui/Menu/index.d.ts.map +1 -1
- package/build/swift-ui/Picker/index.d.ts.map +1 -1
- package/build/swift-ui/Popover/index.d.ts.map +1 -1
- package/build/swift-ui/Section/index.d.ts.map +1 -1
- package/build/swift-ui/Slider/index.d.ts.map +1 -1
- package/build/swift-ui/SlotView.d.ts +5 -0
- package/build/swift-ui/SlotView.d.ts.map +1 -0
- package/build/swift-ui/index.d.ts +2 -0
- package/build/swift-ui/index.d.ts.map +1 -1
- package/build/swift-ui/modifiers/index.d.ts +38 -1
- package/build/swift-ui/modifiers/index.d.ts.map +1 -1
- package/expo-module.config.json +1 -1
- package/ios/AccessoryWidgetBackgroundView.swift +27 -0
- package/ios/ConfirmationDialog/ConfirmationDialog.swift +3 -9
- package/ios/ConfirmationDialog/ConfirmationDialogProps.swift +0 -5
- package/ios/ContextMenu/ContextMenu.swift +27 -22
- package/ios/ContextMenu/ContextMenuRecords.swift +0 -6
- package/ios/ControlGroupView.swift +33 -0
- package/ios/ExpoUIModule.swift +10 -32
- package/ios/GaugeView.swift +4 -26
- package/ios/HostView.swift +1 -2
- package/ios/ImageView.swift +22 -11
- package/ios/Label.swift +2 -17
- package/ios/LabeledContentView.swift +3 -27
- package/ios/Menu/MenuRecords.swift +0 -2
- package/ios/Menu/MenuView.swift +2 -5
- package/ios/Modifiers/ResizableModifier.swift +24 -0
- package/ios/Modifiers/Rotation3DEffectModifier.swift +20 -0
- package/ios/Modifiers/View+ModifierArray.swift +29 -0
- package/ios/Modifiers/ViewModifierRegistry.swift +39 -3
- package/ios/Modifiers/WidgetModifiers.swift +46 -0
- package/ios/Picker/PickerView.swift +2 -6
- package/ios/Popover/PopoverProps.swift +0 -4
- package/ios/Popover/PopoverView.swift +2 -6
- package/ios/RNHostView.swift +91 -10
- package/ios/SectionView.swift +6 -12
- package/ios/SecureFieldView.swift +0 -1
- package/ios/ShareLink/ShareLinkView.swift +1 -1
- package/ios/SliderView.swift +10 -27
- package/ios/SlotView.swift +38 -0
- package/ios/TextFieldView.swift +0 -1
- package/ios/UIBaseView.swift +34 -3
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/{55.0.0/expo.modules.ui-55.0.0-sources.jar → 55.0.2/expo.modules.ui-55.0.2-sources.jar} +0 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/55.0.2/expo.modules.ui-55.0.2-sources.jar.md5 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/55.0.2/expo.modules.ui-55.0.2-sources.jar.sha1 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/55.0.2/expo.modules.ui-55.0.2-sources.jar.sha256 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/55.0.2/expo.modules.ui-55.0.2-sources.jar.sha512 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/55.0.2/expo.modules.ui-55.0.2.aar +0 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/55.0.2/expo.modules.ui-55.0.2.aar.md5 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/55.0.2/expo.modules.ui-55.0.2.aar.sha1 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/55.0.2/expo.modules.ui-55.0.2.aar.sha256 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/55.0.2/expo.modules.ui-55.0.2.aar.sha512 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/{55.0.0/expo.modules.ui-55.0.0.module → 55.0.2/expo.modules.ui-55.0.2.module} +22 -22
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/55.0.2/expo.modules.ui-55.0.2.module.md5 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/55.0.2/expo.modules.ui-55.0.2.module.sha1 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/55.0.2/expo.modules.ui-55.0.2.module.sha256 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/55.0.2/expo.modules.ui-55.0.2.module.sha512 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/{55.0.0/expo.modules.ui-55.0.0.pom → 55.0.2/expo.modules.ui-55.0.2.pom} +1 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/55.0.2/expo.modules.ui-55.0.2.pom.md5 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/55.0.2/expo.modules.ui-55.0.2.pom.sha1 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/55.0.2/expo.modules.ui-55.0.2.pom.sha256 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/55.0.2/expo.modules.ui-55.0.2.pom.sha512 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml +4 -4
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.md5 +1 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.sha1 +1 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.sha256 +1 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.sha512 +1 -1
- package/package.json +2 -2
- package/src/jetpack-compose/Box/index.tsx +26 -0
- package/src/jetpack-compose/Column/index.tsx +39 -0
- package/src/jetpack-compose/DatePicker/index.tsx +106 -2
- package/src/jetpack-compose/FlowRow/index.tsx +29 -0
- package/src/jetpack-compose/LazyColumn/index.tsx +0 -3
- package/src/jetpack-compose/Row/index.tsx +36 -0
- package/src/jetpack-compose/Spacer/index.tsx +2 -2
- package/src/jetpack-compose/Switch/index.tsx +18 -0
- package/src/jetpack-compose/index.ts +4 -1
- package/src/jetpack-compose/layout-types.ts +48 -0
- package/src/jetpack-compose/layout.tsx +5 -106
- package/src/jetpack-compose/modifiers/animation.ts +37 -0
- package/src/jetpack-compose/modifiers/index.ts +60 -4
- package/src/swift-ui/AccessoryWidgetBackground/index.tsx +12 -0
- package/src/swift-ui/ConfirmationDialog/index.tsx +4 -12
- package/src/swift-ui/ContextMenu/index.tsx +6 -20
- package/src/swift-ui/ControlGroup/index.tsx +59 -0
- package/src/swift-ui/Gauge/index.tsx +5 -20
- package/src/swift-ui/Image/index.tsx +7 -1
- package/src/swift-ui/Label/index.tsx +2 -5
- package/src/swift-ui/LabeledContent/index.tsx +3 -12
- package/src/swift-ui/Menu/index.tsx +2 -6
- package/src/swift-ui/Picker/index.tsx +4 -11
- package/src/swift-ui/Popover/index.tsx +3 -12
- package/src/swift-ui/Section/index.tsx +4 -9
- package/src/swift-ui/Slider/index.tsx +4 -12
- package/src/swift-ui/SlotView.tsx +8 -0
- package/src/swift-ui/index.tsx +2 -0
- package/src/swift-ui/modifiers/index.ts +54 -1
- package/ios/ConfirmationDialog/ConfirmationDialogComponents.swift +0 -26
- package/ios/ContextMenu/ContextMenuComponents.swift +0 -37
- package/ios/Menu/MenuComponents.swift +0 -12
- package/ios/Picker/PickerComponents.swift +0 -24
- package/ios/Popover/PopoverComponents.swift +0 -18
- package/ios/SectionComponents.swift +0 -34
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/55.0.0/expo.modules.ui-55.0.0-sources.jar.md5 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/55.0.0/expo.modules.ui-55.0.0-sources.jar.sha1 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/55.0.0/expo.modules.ui-55.0.0-sources.jar.sha256 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/55.0.0/expo.modules.ui-55.0.0-sources.jar.sha512 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/55.0.0/expo.modules.ui-55.0.0.aar +0 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/55.0.0/expo.modules.ui-55.0.0.aar.md5 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/55.0.0/expo.modules.ui-55.0.0.aar.sha1 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/55.0.0/expo.modules.ui-55.0.0.aar.sha256 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/55.0.0/expo.modules.ui-55.0.0.aar.sha512 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/55.0.0/expo.modules.ui-55.0.0.module.md5 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/55.0.0/expo.modules.ui-55.0.0.module.sha1 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/55.0.0/expo.modules.ui-55.0.0.module.sha256 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/55.0.0/expo.modules.ui-55.0.0.module.sha512 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/55.0.0/expo.modules.ui-55.0.0.pom.md5 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/55.0.0/expo.modules.ui-55.0.0.pom.sha1 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/55.0.0/expo.modules.ui-55.0.0.pom.sha256 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/55.0.0/expo.modules.ui-55.0.0.pom.sha512 +0 -1
|
@@ -7,24 +7,6 @@ final class LabeledContentProps: UIBaseViewProps {
|
|
|
7
7
|
@Field var label: String?
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
internal final class LabeledContentLabelProps: ExpoSwiftUI.ViewProps {}
|
|
11
|
-
internal struct LabeledContentLabel: ExpoSwiftUI.View {
|
|
12
|
-
@ObservedObject var props: LabeledContentLabelProps
|
|
13
|
-
|
|
14
|
-
var body: some View {
|
|
15
|
-
Children()
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
internal final class LabeledContentContentProps: ExpoSwiftUI.ViewProps {}
|
|
20
|
-
internal struct LabeledContentContent: ExpoSwiftUI.View {
|
|
21
|
-
@ObservedObject var props: LabeledContentContentProps
|
|
22
|
-
|
|
23
|
-
var body: some View {
|
|
24
|
-
Children()
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
10
|
internal struct LabeledContentView: ExpoSwiftUI.View {
|
|
29
11
|
@ObservedObject var props: LabeledContentProps
|
|
30
12
|
|
|
@@ -45,25 +27,19 @@ internal struct LabeledContentView: ExpoSwiftUI.View {
|
|
|
45
27
|
}
|
|
46
28
|
|
|
47
29
|
private var hasCustomLabel: Bool {
|
|
48
|
-
props.children
|
|
49
|
-
.compactMap({ $0.childView as? LabeledContentLabel })
|
|
50
|
-
.first != nil
|
|
30
|
+
props.children?.slot("label") != nil
|
|
51
31
|
}
|
|
52
32
|
|
|
53
33
|
@ViewBuilder
|
|
54
34
|
private var contentChildren: some View {
|
|
55
|
-
if let content = props.children
|
|
56
|
-
.compactMap({ $0.childView as? LabeledContentContent })
|
|
57
|
-
.first {
|
|
35
|
+
if let content = props.children?.slot("content") {
|
|
58
36
|
content
|
|
59
37
|
}
|
|
60
38
|
}
|
|
61
39
|
|
|
62
40
|
@ViewBuilder
|
|
63
41
|
private var customLabelContent: some View {
|
|
64
|
-
if let labelContent = props.children
|
|
65
|
-
.compactMap({ $0.childView as? LabeledContentLabel })
|
|
66
|
-
.first {
|
|
42
|
+
if let labelContent = props.children?.slot("label") {
|
|
67
43
|
labelContent
|
|
68
44
|
}
|
|
69
45
|
}
|
package/ios/Menu/MenuView.swift
CHANGED
|
@@ -9,8 +9,7 @@ internal struct MenuView: ExpoSwiftUI.View {
|
|
|
9
9
|
// If label is a component, it is passed as a child, so we need to exclude it in order to display the menu content
|
|
10
10
|
@ViewBuilder
|
|
11
11
|
func ChildrenWithoutLabel() -> some View {
|
|
12
|
-
|
|
13
|
-
ForEach(props.children?.filter { $0.id != labelView?.id } ?? [], id: \.id) { child in
|
|
12
|
+
ForEach(props.children?.withoutSlot("label") ?? [], id: \.id) { child in
|
|
14
13
|
let view: any View = child.childView
|
|
15
14
|
AnyView(view)
|
|
16
15
|
}
|
|
@@ -18,9 +17,7 @@ internal struct MenuView: ExpoSwiftUI.View {
|
|
|
18
17
|
|
|
19
18
|
var body: some View {
|
|
20
19
|
if #available(iOS 14.0, tvOS 17.0, *) {
|
|
21
|
-
let labelContent = props.children
|
|
22
|
-
.compactMap { $0.childView as? MenuLabel }
|
|
23
|
-
.first
|
|
20
|
+
let labelContent = props.children?.slot("label")
|
|
24
21
|
|
|
25
22
|
if props.hasPrimaryAction {
|
|
26
23
|
// With primaryAction, tap triggers callback and long-press shows menu
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Copyright 2025-present 650 Industries. All rights reserved.
|
|
2
|
+
|
|
3
|
+
import ExpoModulesCore
|
|
4
|
+
import SwiftUI
|
|
5
|
+
|
|
6
|
+
internal enum ResizingModeOptions: String, Enumerable {
|
|
7
|
+
case stretch
|
|
8
|
+
case tile
|
|
9
|
+
|
|
10
|
+
var toResizingMode: Image.ResizingMode {
|
|
11
|
+
switch self {
|
|
12
|
+
case .stretch: return .stretch
|
|
13
|
+
case .tile: return .tile
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
internal struct ResizableModifier: Record {
|
|
19
|
+
@Field var top: CGFloat = 0
|
|
20
|
+
@Field var leading: CGFloat = 0
|
|
21
|
+
@Field var bottom: CGFloat = 0
|
|
22
|
+
@Field var trailing: CGFloat = 0
|
|
23
|
+
@Field var resizingMode: ResizingModeOptions = .stretch
|
|
24
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Copyright 2015-present 650 Industries. All rights reserved.
|
|
2
|
+
|
|
3
|
+
import ExpoModulesCore
|
|
4
|
+
import SwiftUI
|
|
5
|
+
|
|
6
|
+
internal struct Rotation3DEffectModifier: ViewModifier, Record {
|
|
7
|
+
@Field var angle: Double = 0
|
|
8
|
+
@Field var axisX: CGFloat = 0
|
|
9
|
+
@Field var axisY: CGFloat = 0
|
|
10
|
+
@Field var axisZ: CGFloat = 0
|
|
11
|
+
@Field var perspective: CGFloat = 1
|
|
12
|
+
|
|
13
|
+
func body(content: Content) -> some View {
|
|
14
|
+
content.rotation3DEffect(
|
|
15
|
+
.degrees(angle),
|
|
16
|
+
axis: (x: axisX, y: axisY, z: axisZ),
|
|
17
|
+
perspective: perspective
|
|
18
|
+
)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -50,3 +50,32 @@ internal extension Text {
|
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
|
+
|
|
54
|
+
internal extension Image {
|
|
55
|
+
@ViewBuilder
|
|
56
|
+
func applyImageModifiers(_ modifiers: ModifierArray?, appContext: AppContext?) -> some View {
|
|
57
|
+
if let modifiers, let appContext {
|
|
58
|
+
let image = modifiers.reduce(self) { currentImage, modifierConfig in
|
|
59
|
+
guard let type = modifierConfig["$type"] as? String else {
|
|
60
|
+
return currentImage
|
|
61
|
+
}
|
|
62
|
+
return ViewModifierRegistry.shared.applyImageModifier(
|
|
63
|
+
type,
|
|
64
|
+
to: currentImage,
|
|
65
|
+
appContext: appContext,
|
|
66
|
+
params: modifierConfig
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if #available(iOS 18.0, *),
|
|
71
|
+
let modifierConfig = modifiers.first(where: { $0["$type"] as? String == "widgetAccentedRenderingMode" }),
|
|
72
|
+
let modifier = try? WidgetAccentedRenderingModeModifier(from: modifierConfig, appContext: appContext) {
|
|
73
|
+
modifier.apply(to: image)
|
|
74
|
+
} else {
|
|
75
|
+
image
|
|
76
|
+
}
|
|
77
|
+
} else {
|
|
78
|
+
self
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -158,6 +158,12 @@ internal struct ForegroundColorModifier: ViewModifier, Record {
|
|
|
158
158
|
}
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
+
internal struct LuminanceToAlphaModifier: ViewModifier, Record {
|
|
162
|
+
func body(content: Content) -> some View {
|
|
163
|
+
content.luminanceToAlpha()
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
161
167
|
internal struct BoldModifier: ViewModifier, Record {
|
|
162
168
|
func body(content: Content) -> some View {
|
|
163
169
|
if #available(iOS 16.0, tvOS 16.0, *) {
|
|
@@ -774,7 +780,6 @@ internal struct ListRowBackground: ViewModifier, Record {
|
|
|
774
780
|
}
|
|
775
781
|
}
|
|
776
782
|
|
|
777
|
-
|
|
778
783
|
internal enum VerticalEdgeOptions: String, Enumerable {
|
|
779
784
|
case all
|
|
780
785
|
case top
|
|
@@ -1100,7 +1105,7 @@ internal enum AxisOptions: String, Enumerable {
|
|
|
1100
1105
|
case horizontal
|
|
1101
1106
|
case vertical
|
|
1102
1107
|
case both
|
|
1103
|
-
|
|
1108
|
+
|
|
1104
1109
|
func toAxis() -> Axis.Set {
|
|
1105
1110
|
switch self {
|
|
1106
1111
|
case .vertical:
|
|
@@ -1301,7 +1306,7 @@ public class ViewModifierRegistry {
|
|
|
1301
1306
|
}
|
|
1302
1307
|
|
|
1303
1308
|
/**
|
|
1304
|
-
|
|
1309
|
+
* Applies Text returning modifiers. Useful for Text concatenation in TextView.
|
|
1305
1310
|
*/
|
|
1306
1311
|
func applyTextModifier(
|
|
1307
1312
|
_ type: String,
|
|
@@ -1347,6 +1352,29 @@ public class ViewModifierRegistry {
|
|
|
1347
1352
|
}
|
|
1348
1353
|
}
|
|
1349
1354
|
|
|
1355
|
+
/**
|
|
1356
|
+
* Applies Image returning modifiers.
|
|
1357
|
+
*/
|
|
1358
|
+
func applyImageModifier(
|
|
1359
|
+
_ type: String,
|
|
1360
|
+
to image: Image,
|
|
1361
|
+
appContext: AppContext,
|
|
1362
|
+
params: [String: Any]
|
|
1363
|
+
) -> Image {
|
|
1364
|
+
switch type {
|
|
1365
|
+
case "resizable":
|
|
1366
|
+
guard let modifier = try? ResizableModifier(from: params, appContext: appContext)
|
|
1367
|
+
else { return image.resizable() }
|
|
1368
|
+
return image.resizable(capInsets: EdgeInsets(top: modifier.top, leading: modifier.leading, bottom: modifier.bottom, trailing: modifier.trailing), resizingMode: modifier.resizingMode.toResizingMode)
|
|
1369
|
+
default:
|
|
1370
|
+
#if DEBUG
|
|
1371
|
+
return image
|
|
1372
|
+
#else
|
|
1373
|
+
return image
|
|
1374
|
+
#endif
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1350
1378
|
/**
|
|
1351
1379
|
* Checks if a modifier type is registered.
|
|
1352
1380
|
*/
|
|
@@ -1494,6 +1522,10 @@ extension ViewModifierRegistry {
|
|
|
1494
1522
|
return try RotationEffectModifier(from: params, appContext: appContext)
|
|
1495
1523
|
}
|
|
1496
1524
|
|
|
1525
|
+
register("rotation3DEffect") { params, appContext, _ in
|
|
1526
|
+
return try Rotation3DEffectModifier(from: params, appContext: appContext)
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1497
1529
|
register("offset") { params, appContext, _ in
|
|
1498
1530
|
return try OffsetModifier(from: params, appContext: appContext)
|
|
1499
1531
|
}
|
|
@@ -1506,6 +1538,10 @@ extension ViewModifierRegistry {
|
|
|
1506
1538
|
return try ForegroundStyleModifier(from: params, appContext: appContext)
|
|
1507
1539
|
}
|
|
1508
1540
|
|
|
1541
|
+
register("luminanceToAlpha") { params, appContext, _ in
|
|
1542
|
+
return try LuminanceToAlphaModifier(from: params, appContext: appContext)
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1509
1545
|
register("bold") { params, appContext, _ in
|
|
1510
1546
|
return try BoldModifier(from: params, appContext: appContext)
|
|
1511
1547
|
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// Copyright 2015-present 650 Industries. All rights reserved.
|
|
2
|
+
|
|
3
|
+
import ExpoModulesCore
|
|
4
|
+
import SwiftUI
|
|
5
|
+
#if !os(tvOS)
|
|
6
|
+
import WidgetKit
|
|
7
|
+
#endif
|
|
8
|
+
|
|
9
|
+
internal enum WidgetAccentedRenderingModeOptions: String, Enumerable {
|
|
10
|
+
case accented
|
|
11
|
+
case desaturated
|
|
12
|
+
case accentedDesaturated
|
|
13
|
+
case fullColor
|
|
14
|
+
|
|
15
|
+
#if !os(tvOS)
|
|
16
|
+
@available(iOS 18.0, *)
|
|
17
|
+
var toWidgetAccentedRenderingMode: WidgetAccentedRenderingMode {
|
|
18
|
+
switch self {
|
|
19
|
+
case .accented: return .accented
|
|
20
|
+
case .accentedDesaturated: return .accentedDesaturated
|
|
21
|
+
case .desaturated: return .desaturated
|
|
22
|
+
case .fullColor: return .fullColor
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
#endif
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* This is a unique modifier that exists only on Image, but returns some View, and for this reason it cannot be a ViewModifier.
|
|
30
|
+
*/
|
|
31
|
+
internal struct WidgetAccentedRenderingModeModifier: Record {
|
|
32
|
+
@Field var renderingMode: WidgetAccentedRenderingModeOptions?
|
|
33
|
+
|
|
34
|
+
@ViewBuilder
|
|
35
|
+
func apply(to image: Image) -> some View {
|
|
36
|
+
#if !os(tvOS)
|
|
37
|
+
if #available(iOS 18.0, *), renderingMode != nil {
|
|
38
|
+
image.widgetAccentedRenderingMode(renderingMode?.toWidgetAccentedRenderingMode)
|
|
39
|
+
} else {
|
|
40
|
+
image
|
|
41
|
+
}
|
|
42
|
+
#else
|
|
43
|
+
image
|
|
44
|
+
#endif
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -24,13 +24,9 @@ internal struct PickerView: ExpoSwiftUI.View {
|
|
|
24
24
|
|
|
25
25
|
@ViewBuilder
|
|
26
26
|
private func makePicker() -> some View {
|
|
27
|
-
let content =
|
|
28
|
-
.compactMap { $0.childView as? PickerContentView }
|
|
29
|
-
.first)
|
|
27
|
+
let content = props.children?.slot("content")
|
|
30
28
|
|
|
31
|
-
let labelContent = props.children
|
|
32
|
-
.compactMap { $0.childView as? PickerLabelView }
|
|
33
|
-
.first
|
|
29
|
+
let labelContent = props.children?.slot("label")
|
|
34
30
|
|
|
35
31
|
if let systemImage = props.systemImage, let label = props.label {
|
|
36
32
|
Picker(label, systemImage: systemImage, selection: $selection) { content }
|
|
@@ -8,10 +8,6 @@ internal class PopoverViewProps: UIBaseViewProps {
|
|
|
8
8
|
@Field var arrowEdge: PopoverArrowEdgeOption?
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
internal final class PopoverViewContentPorps: ExpoSwiftUI.ViewProps {}
|
|
12
|
-
|
|
13
|
-
internal final class PopoverViewPopContentPorps: ExpoSwiftUI.ViewProps {}
|
|
14
|
-
|
|
15
11
|
internal enum PopoverAttachmentAnchorOption: String, Enumerable {
|
|
16
12
|
case top
|
|
17
13
|
case center
|
|
@@ -45,18 +45,14 @@ internal struct PopoverView: ExpoSwiftUI.View {
|
|
|
45
45
|
|
|
46
46
|
@ViewBuilder
|
|
47
47
|
private var triggerContent: some View {
|
|
48
|
-
if let content = props.children
|
|
49
|
-
.compactMap({ $0.childView as? PopoverViewContent })
|
|
50
|
-
.first {
|
|
48
|
+
if let content = props.children?.slot("trigger") {
|
|
51
49
|
content
|
|
52
50
|
}
|
|
53
51
|
}
|
|
54
52
|
|
|
55
53
|
@ViewBuilder
|
|
56
54
|
private var popoverContent: some View {
|
|
57
|
-
if let content = props.children
|
|
58
|
-
.compactMap({ $0.childView as? PopoverViewPopContent })
|
|
59
|
-
.first {
|
|
55
|
+
if let content = props.children?.slot("popover") {
|
|
60
56
|
content
|
|
61
57
|
}
|
|
62
58
|
}
|
package/ios/RNHostView.swift
CHANGED
|
@@ -1,21 +1,102 @@
|
|
|
1
1
|
// Copyright 2015-present 650 Industries. All rights reserved.
|
|
2
2
|
|
|
3
|
+
import SwiftUI
|
|
3
4
|
import ExpoModulesCore
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
internal final class RNHostViewProps: ExpoSwiftUI.ViewProps {
|
|
7
|
+
@Field var matchContents: Bool = false
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
struct RNHostView: ExpoSwiftUI.View {
|
|
11
|
+
|
|
12
|
+
@ObservedObject var props: RNHostViewProps
|
|
13
|
+
|
|
14
|
+
var body: some View {
|
|
15
|
+
if props.matchContents, let childUIView = firstChildUIView {
|
|
16
|
+
ApplySizeFromYogaNode(childUIView: childUIView) {
|
|
17
|
+
Children()
|
|
18
|
+
}
|
|
19
|
+
.onAppear {
|
|
20
|
+
ExpoUITouchHandlerHelper.createAndAttachTouchHandler(for: childUIView)
|
|
21
|
+
}
|
|
22
|
+
} else {
|
|
23
|
+
Children()
|
|
24
|
+
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
|
25
|
+
.modifier(ReportSizeToYogaNodeModifier(shadowNodeProxy: props.shadowNodeProxy))
|
|
26
|
+
.onAppear {
|
|
27
|
+
if let view = firstChildUIView {
|
|
28
|
+
ExpoUITouchHandlerHelper.createAndAttachTouchHandler(for: view)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
private var firstChildUIView: UIView? {
|
|
35
|
+
props.children?.first?.uiView
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Sets SwiftUI view size from Yoga node size
|
|
40
|
+
// Listens to Yoga node size changes and updates the SwiftUI view size
|
|
41
|
+
private struct ApplySizeFromYogaNode<Content: SwiftUI.View>: SwiftUI.View {
|
|
42
|
+
@StateObject private var observer: Observer
|
|
43
|
+
let content: Content
|
|
44
|
+
|
|
45
|
+
init(childUIView: UIView, @ViewBuilder content: () -> Content) {
|
|
46
|
+
_observer = StateObject(wrappedValue: Observer(view: childUIView))
|
|
47
|
+
self.content = content()
|
|
9
48
|
}
|
|
10
49
|
|
|
11
|
-
|
|
50
|
+
var body: some SwiftUI.View {
|
|
51
|
+
content
|
|
52
|
+
.frame(width: observer.size.width, height: observer.size.height)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@MainActor
|
|
56
|
+
fileprivate class Observer: ObservableObject {
|
|
57
|
+
@Published var size: CGSize
|
|
58
|
+
private var kvoToken: NSKeyValueObservation?
|
|
59
|
+
|
|
60
|
+
init(view: UIView) {
|
|
61
|
+
self.size = view.bounds.size
|
|
62
|
+
kvoToken = view.observe(\.bounds) { [weak self] view, _ in
|
|
63
|
+
MainActor.assumeIsolated {
|
|
64
|
+
self?.size = view.bounds.size
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
deinit {
|
|
70
|
+
kvoToken?.invalidate()
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Sets Yoga node size from SwiftUI view size
|
|
76
|
+
// Listens to SwiftUI view size changes and updates the Yoga node size
|
|
77
|
+
private struct ReportSizeToYogaNodeModifier: ViewModifier {
|
|
78
|
+
let shadowNodeProxy: ExpoSwiftUI.ShadowNodeProxy
|
|
79
|
+
|
|
80
|
+
private func handleSizeChange(_ size: CGSize) {
|
|
81
|
+
shadowNodeProxy.setViewSize?(size)
|
|
82
|
+
}
|
|
12
83
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
84
|
+
func body(content: Content) -> some View {
|
|
85
|
+
if #available(iOS 16.0, tvOS 16.0, macOS 13.0, *) {
|
|
86
|
+
content.onGeometryChange(for: CGSize.self, of: { proxy in proxy.size }) { size in
|
|
87
|
+
handleSizeChange(size)
|
|
88
|
+
}
|
|
17
89
|
} else {
|
|
18
|
-
|
|
90
|
+
content.overlay {
|
|
91
|
+
GeometryReader { geometry in
|
|
92
|
+
Color.clear
|
|
93
|
+
.hidden()
|
|
94
|
+
.onAppear {
|
|
95
|
+
handleSizeChange(geometry.size)
|
|
96
|
+
}
|
|
97
|
+
.onChange(of: geometry.size) { handleSizeChange($0) }
|
|
98
|
+
}
|
|
99
|
+
}
|
|
19
100
|
}
|
|
20
101
|
}
|
|
21
102
|
}
|
package/ios/SectionView.swift
CHANGED
|
@@ -95,21 +95,15 @@ internal struct SectionView: ExpoSwiftUI.View {
|
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
private var contentChildren:
|
|
99
|
-
props.children
|
|
100
|
-
.compactMap({ $0.childView as? SectionContent })
|
|
101
|
-
.first
|
|
98
|
+
private var contentChildren: SlotView? {
|
|
99
|
+
props.children?.slot("content")
|
|
102
100
|
}
|
|
103
101
|
|
|
104
|
-
private var headerView:
|
|
105
|
-
props.children
|
|
106
|
-
.compactMap({ $0.childView as? SectionHeader })
|
|
107
|
-
.first
|
|
102
|
+
private var headerView: SlotView? {
|
|
103
|
+
props.children?.slot("header")
|
|
108
104
|
}
|
|
109
105
|
|
|
110
|
-
private var footerView:
|
|
111
|
-
props.children
|
|
112
|
-
.compactMap({ $0.childView as? SectionFooter })
|
|
113
|
-
.first
|
|
106
|
+
private var footerView: SlotView? {
|
|
107
|
+
props.children?.slot("footer")
|
|
114
108
|
}
|
|
115
109
|
}
|
package/ios/SliderView.swift
CHANGED
|
@@ -6,6 +6,7 @@ import ExpoModulesCore
|
|
|
6
6
|
struct SliderView: ExpoSwiftUI.View {
|
|
7
7
|
@ObservedObject var props: SliderProps
|
|
8
8
|
@State var value: Float = 0.0
|
|
9
|
+
@State var isEditing: Bool = false
|
|
9
10
|
|
|
10
11
|
init(props: SliderProps) {
|
|
11
12
|
self.props = props
|
|
@@ -23,8 +24,8 @@ struct SliderView: ExpoSwiftUI.View {
|
|
|
23
24
|
}
|
|
24
25
|
}
|
|
25
26
|
.onReceive(props.value.publisher, perform: { newValue in
|
|
26
|
-
|
|
27
|
-
value =
|
|
27
|
+
guard !isEditing else { return }
|
|
28
|
+
value = newValue
|
|
28
29
|
})
|
|
29
30
|
#else
|
|
30
31
|
Text("Slider is not supported on tvOS")
|
|
@@ -34,14 +35,9 @@ struct SliderView: ExpoSwiftUI.View {
|
|
|
34
35
|
#if !os(tvOS)
|
|
35
36
|
@ViewBuilder
|
|
36
37
|
private var sliderContent: some View {
|
|
37
|
-
let label = props.children?.
|
|
38
|
-
|
|
39
|
-
let
|
|
40
|
-
.compactMap({ $0.childView as? SliderLabelView })
|
|
41
|
-
.first(where: { $0.props.kind == .minimum })
|
|
42
|
-
let maximumValueLabel = props.children?
|
|
43
|
-
.compactMap({ $0.childView as? SliderLabelView })
|
|
44
|
-
.first(where: { $0.props.kind == .maximum })
|
|
38
|
+
let label = props.children?.slot("label")
|
|
39
|
+
let minimumValueLabel = props.children?.slot("minimum")
|
|
40
|
+
let maximumValueLabel = props.children?.slot("maximum")
|
|
45
41
|
|
|
46
42
|
if let min = props.min, let max = props.max, let step = props.step {
|
|
47
43
|
Slider(
|
|
@@ -52,6 +48,7 @@ struct SliderView: ExpoSwiftUI.View {
|
|
|
52
48
|
minimumValueLabel: { minimumValueLabel },
|
|
53
49
|
maximumValueLabel: { maximumValueLabel }
|
|
54
50
|
) { isEditing in
|
|
51
|
+
self.isEditing = isEditing
|
|
55
52
|
props.onEditingChanged(["isEditing": isEditing])
|
|
56
53
|
}
|
|
57
54
|
} else if let min = props.min, let max = props.max {
|
|
@@ -62,6 +59,7 @@ struct SliderView: ExpoSwiftUI.View {
|
|
|
62
59
|
minimumValueLabel: { minimumValueLabel },
|
|
63
60
|
maximumValueLabel: { maximumValueLabel }
|
|
64
61
|
) { isEditing in
|
|
62
|
+
self.isEditing = isEditing
|
|
65
63
|
props.onEditingChanged(["isEditing": isEditing])
|
|
66
64
|
}
|
|
67
65
|
} else if let step = props.step {
|
|
@@ -73,6 +71,7 @@ struct SliderView: ExpoSwiftUI.View {
|
|
|
73
71
|
minimumValueLabel: { minimumValueLabel },
|
|
74
72
|
maximumValueLabel: { maximumValueLabel }
|
|
75
73
|
) { isEditing in
|
|
74
|
+
self.isEditing = isEditing
|
|
76
75
|
props.onEditingChanged(["isEditing": isEditing])
|
|
77
76
|
}
|
|
78
77
|
} else {
|
|
@@ -82,6 +81,7 @@ struct SliderView: ExpoSwiftUI.View {
|
|
|
82
81
|
minimumValueLabel: { minimumValueLabel },
|
|
83
82
|
maximumValueLabel: { maximumValueLabel }
|
|
84
83
|
) { isEditing in
|
|
84
|
+
self.isEditing = isEditing
|
|
85
85
|
props.onEditingChanged(["isEditing": isEditing])
|
|
86
86
|
}
|
|
87
87
|
}
|
|
@@ -98,20 +98,3 @@ final class SliderProps: UIBaseViewProps {
|
|
|
98
98
|
var onEditingChanged = EventDispatcher()
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
internal enum SliderLabelKind: String, Enumerable {
|
|
102
|
-
case label
|
|
103
|
-
case minimum
|
|
104
|
-
case maximum
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
internal final class SliderLabelProps: ExpoSwiftUI.ViewProps {
|
|
108
|
-
@Field var kind: SliderLabelKind = .minimum
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
internal struct SliderLabelView: ExpoSwiftUI.View {
|
|
112
|
-
@ObservedObject var props: SliderLabelProps
|
|
113
|
-
|
|
114
|
-
var body: some View {
|
|
115
|
-
Children()
|
|
116
|
-
}
|
|
117
|
-
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Copyright 2025-present 650 Industries. All rights reserved.
|
|
2
|
+
|
|
3
|
+
import SwiftUI
|
|
4
|
+
import ExpoModulesCore
|
|
5
|
+
|
|
6
|
+
internal final class SlotViewProps: ExpoSwiftUI.ViewProps {
|
|
7
|
+
@Field var name: String = ""
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
internal struct SlotView: ExpoSwiftUI.View {
|
|
11
|
+
@ObservedObject var props: SlotViewProps
|
|
12
|
+
|
|
13
|
+
init(props: SlotViewProps) {
|
|
14
|
+
self.props = props
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
var body: some View {
|
|
18
|
+
Children()
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
extension [any ExpoSwiftUI.AnyChild] {
|
|
23
|
+
func slot(_ name: String) -> SlotView? {
|
|
24
|
+
compactMap { $0.childView as? SlotView }
|
|
25
|
+
.first { $0.props.name == name }
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
func withoutSlot(_ name: String) -> [any ExpoSwiftUI.AnyChild] {
|
|
29
|
+
filter {
|
|
30
|
+
guard let slot = $0.childView as? SlotView else { return true }
|
|
31
|
+
return slot.props.name != name
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
func withoutSlots() -> [any ExpoSwiftUI.AnyChild] {
|
|
36
|
+
filter { !($0.childView is SlotView) }
|
|
37
|
+
}
|
|
38
|
+
}
|
package/ios/TextFieldView.swift
CHANGED
|
@@ -161,7 +161,6 @@ struct TextFieldView: ExpoSwiftUI.View, ExpoSwiftUI.FocusableView {
|
|
|
161
161
|
)
|
|
162
162
|
}
|
|
163
163
|
return text.lineLimit((props.multiline && allowMultiLine()) ? props.numberOfLines : 1)
|
|
164
|
-
.modifier(UIBaseViewModifier(props: props))
|
|
165
164
|
.fixedSize(horizontal: false, vertical: true)
|
|
166
165
|
.keyboardType(getKeyboardType(props.keyboardType))
|
|
167
166
|
.autocorrectionDisabled(!props.autocorrection)
|