react-native-platform-components 1.3.0 → 1.4.0
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/README.md +24 -30
- package/android/src/main/java/com/platformcomponents/PCButtonGroupView.kt +498 -0
- package/android/src/main/java/com/platformcomponents/PCButtonGroupViewManager.kt +191 -0
- package/android/src/main/java/com/platformcomponents/PCButtonSupport.kt +127 -0
- package/android/src/main/java/com/platformcomponents/PCButtonView.kt +345 -0
- package/android/src/main/java/com/platformcomponents/PCButtonViewManager.kt +126 -0
- package/android/src/main/java/com/platformcomponents/PCExpressive.kt +107 -0
- package/android/src/main/java/com/platformcomponents/PCFloatingToolbarView.kt +115 -0
- package/android/src/main/java/com/platformcomponents/PCFloatingToolbarViewManager.kt +65 -0
- package/android/src/main/java/com/platformcomponents/PCSegmentedControlView.kt +11 -3
- package/android/src/main/java/com/platformcomponents/PCSegmentedControlViewManager.kt +6 -1
- package/android/src/main/java/com/platformcomponents/PCTextFieldView.kt +1018 -0
- package/android/src/main/java/com/platformcomponents/PCTextFieldViewManager.kt +248 -0
- package/android/src/main/java/com/platformcomponents/PlatformComponentsPackage.kt +4 -0
- package/android/src/main/res/values/attrs.xml +13 -0
- package/android/src/main/res/values/styles.xml +183 -0
- package/ios/PCButton.h +10 -0
- package/ios/PCButton.mm +196 -0
- package/ios/PCButtonGroup.h +10 -0
- package/ios/PCButtonGroup.mm +256 -0
- package/ios/PCButtonGroupView.swift +254 -0
- package/ios/PCButtonSupport.swift +141 -0
- package/ios/PCButtonView.swift +155 -0
- package/ios/PCFloatingToolbar.h +10 -0
- package/ios/PCFloatingToolbar.mm +76 -0
- package/ios/PCFloatingToolbarView.swift +95 -0
- package/ios/PCTextField.h +10 -0
- package/ios/PCTextField.mm +391 -0
- package/ios/PCTextFieldView.swift +1012 -0
- package/lib/commonjs/Button.js +77 -0
- package/lib/commonjs/Button.js.map +1 -0
- package/lib/commonjs/ButtonGroup.js +99 -0
- package/lib/commonjs/ButtonGroup.js.map +1 -0
- package/lib/commonjs/ButtonGroupNativeComponent.ts +121 -0
- package/lib/commonjs/ButtonNativeComponent.ts +76 -0
- package/lib/commonjs/FloatingToolbar.js +69 -0
- package/lib/commonjs/FloatingToolbar.js.map +1 -0
- package/lib/commonjs/FloatingToolbarNativeComponent.ts +31 -0
- package/lib/commonjs/SegmentedControl.js +11 -85
- package/lib/commonjs/SegmentedControl.js.map +1 -1
- package/lib/commonjs/SegmentedControlNativeComponent.ts +3 -0
- package/lib/commonjs/TextField.js +195 -0
- package/lib/commonjs/TextField.js.map +1 -0
- package/lib/commonjs/TextFieldNativeComponent.ts +218 -0
- package/lib/commonjs/focusRegistry.js +78 -0
- package/lib/commonjs/focusRegistry.js.map +1 -0
- package/lib/commonjs/icons.js +92 -0
- package/lib/commonjs/icons.js.map +1 -0
- package/lib/commonjs/index.js +66 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/index.web.js +32 -2
- package/lib/commonjs/index.web.js.map +1 -1
- package/lib/commonjs/labelStyle.js +27 -0
- package/lib/commonjs/labelStyle.js.map +1 -0
- package/lib/commonjs/reactNativeInternals.d.js +2 -0
- package/lib/commonjs/reactNativeInternals.d.js.map +1 -0
- package/lib/module/Button.js +70 -0
- package/lib/module/Button.js.map +1 -0
- package/lib/module/ButtonGroup.js +92 -0
- package/lib/module/ButtonGroup.js.map +1 -0
- package/lib/module/ButtonGroupNativeComponent.ts +121 -0
- package/lib/module/ButtonNativeComponent.ts +76 -0
- package/lib/module/FloatingToolbar.js +62 -0
- package/lib/module/FloatingToolbar.js.map +1 -0
- package/lib/module/FloatingToolbarNativeComponent.ts +31 -0
- package/lib/module/SegmentedControl.js +12 -87
- package/lib/module/SegmentedControl.js.map +1 -1
- package/lib/module/SegmentedControlNativeComponent.ts +3 -0
- package/lib/module/TextField.js +189 -0
- package/lib/module/TextField.js.map +1 -0
- package/lib/module/TextFieldNativeComponent.ts +218 -0
- package/lib/module/focusRegistry.js +74 -0
- package/lib/module/focusRegistry.js.map +1 -0
- package/lib/module/icons.js +87 -0
- package/lib/module/icons.js.map +1 -0
- package/lib/module/index.js +6 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.web.js +28 -0
- package/lib/module/index.web.js.map +1 -1
- package/lib/module/labelStyle.js +23 -0
- package/lib/module/labelStyle.js.map +1 -0
- package/lib/module/reactNativeInternals.d.js +2 -0
- package/lib/module/reactNativeInternals.d.js.map +1 -0
- package/lib/typescript/commonjs/docs/sidebars.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/Button.d.ts +76 -0
- package/lib/typescript/commonjs/src/Button.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/ButtonGroup.d.ts +102 -0
- package/lib/typescript/commonjs/src/ButtonGroup.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/ButtonGroupNativeComponent.d.ts +93 -0
- package/lib/typescript/commonjs/src/ButtonGroupNativeComponent.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/ButtonNativeComponent.d.ts +57 -0
- package/lib/typescript/commonjs/src/ButtonNativeComponent.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/FloatingToolbar.d.ts +45 -0
- package/lib/typescript/commonjs/src/FloatingToolbar.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/FloatingToolbarNativeComponent.d.ts +24 -0
- package/lib/typescript/commonjs/src/FloatingToolbarNativeComponent.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/SegmentedControl.d.ts +17 -46
- package/lib/typescript/commonjs/src/SegmentedControl.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/SegmentedControlNativeComponent.d.ts +2 -0
- package/lib/typescript/commonjs/src/SegmentedControlNativeComponent.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/TextField.d.ts +174 -0
- package/lib/typescript/commonjs/src/TextField.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/TextFieldNativeComponent.d.ts +162 -0
- package/lib/typescript/commonjs/src/TextFieldNativeComponent.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/focusRegistry.d.ts +13 -0
- package/lib/typescript/commonjs/src/focusRegistry.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/icons.d.ts +52 -0
- package/lib/typescript/commonjs/src/icons.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/index.d.ts +6 -0
- package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/index.web.d.ts +121 -0
- package/lib/typescript/commonjs/src/index.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/labelStyle.d.ts +20 -0
- package/lib/typescript/commonjs/src/labelStyle.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/sharedTypes.d.ts +6 -1
- package/lib/typescript/commonjs/src/sharedTypes.d.ts.map +1 -1
- package/lib/typescript/module/docs/sidebars.d.ts.map +1 -1
- package/lib/typescript/module/src/Button.d.ts +76 -0
- package/lib/typescript/module/src/Button.d.ts.map +1 -0
- package/lib/typescript/module/src/ButtonGroup.d.ts +102 -0
- package/lib/typescript/module/src/ButtonGroup.d.ts.map +1 -0
- package/lib/typescript/module/src/ButtonGroupNativeComponent.d.ts +93 -0
- package/lib/typescript/module/src/ButtonGroupNativeComponent.d.ts.map +1 -0
- package/lib/typescript/module/src/ButtonNativeComponent.d.ts +57 -0
- package/lib/typescript/module/src/ButtonNativeComponent.d.ts.map +1 -0
- package/lib/typescript/module/src/FloatingToolbar.d.ts +45 -0
- package/lib/typescript/module/src/FloatingToolbar.d.ts.map +1 -0
- package/lib/typescript/module/src/FloatingToolbarNativeComponent.d.ts +24 -0
- package/lib/typescript/module/src/FloatingToolbarNativeComponent.d.ts.map +1 -0
- package/lib/typescript/module/src/SegmentedControl.d.ts +17 -46
- package/lib/typescript/module/src/SegmentedControl.d.ts.map +1 -1
- package/lib/typescript/module/src/SegmentedControlNativeComponent.d.ts +2 -0
- package/lib/typescript/module/src/SegmentedControlNativeComponent.d.ts.map +1 -1
- package/lib/typescript/module/src/TextField.d.ts +174 -0
- package/lib/typescript/module/src/TextField.d.ts.map +1 -0
- package/lib/typescript/module/src/TextFieldNativeComponent.d.ts +162 -0
- package/lib/typescript/module/src/TextFieldNativeComponent.d.ts.map +1 -0
- package/lib/typescript/module/src/focusRegistry.d.ts +13 -0
- package/lib/typescript/module/src/focusRegistry.d.ts.map +1 -0
- package/lib/typescript/module/src/icons.d.ts +52 -0
- package/lib/typescript/module/src/icons.d.ts.map +1 -0
- package/lib/typescript/module/src/index.d.ts +6 -0
- package/lib/typescript/module/src/index.d.ts.map +1 -1
- package/lib/typescript/module/src/index.web.d.ts +121 -0
- package/lib/typescript/module/src/index.web.d.ts.map +1 -1
- package/lib/typescript/module/src/labelStyle.d.ts +20 -0
- package/lib/typescript/module/src/labelStyle.d.ts.map +1 -0
- package/lib/typescript/module/src/sharedTypes.d.ts +6 -1
- package/lib/typescript/module/src/sharedTypes.d.ts.map +1 -1
- package/package.json +25 -4
- package/react-native.config.js +3 -0
- package/shared/PCButtonComponentDescriptors-custom.h +22 -0
- package/shared/PCButtonGroupComponentDescriptors-custom.h +22 -0
- package/shared/PCButtonGroupShadowNode-custom.cpp +48 -0
- package/shared/PCButtonGroupShadowNode-custom.h +55 -0
- package/shared/PCButtonGroupState-custom.h +61 -0
- package/shared/PCButtonShadowNode-custom.cpp +48 -0
- package/shared/PCButtonShadowNode-custom.h +55 -0
- package/shared/PCButtonState-custom.h +61 -0
- package/shared/PCTextFieldComponentDescriptors-custom.h +22 -0
- package/shared/PCTextFieldShadowNode-custom.cpp +47 -0
- package/shared/PCTextFieldShadowNode-custom.h +55 -0
- package/shared/PCTextFieldState-custom.h +61 -0
- package/shared/react/renderer/components/PlatformComponentsViewSpec/ComponentDescriptors.h +3 -0
- package/src/Button.tsx +144 -0
- package/src/ButtonGroup.tsx +233 -0
- package/src/ButtonGroupNativeComponent.ts +121 -0
- package/src/ButtonNativeComponent.ts +76 -0
- package/src/FloatingToolbar.tsx +113 -0
- package/src/FloatingToolbarNativeComponent.ts +31 -0
- package/src/SegmentedControl.tsx +26 -118
- package/src/SegmentedControlNativeComponent.ts +3 -0
- package/src/TextField.tsx +494 -0
- package/src/TextFieldNativeComponent.ts +218 -0
- package/src/focusRegistry.ts +89 -0
- package/src/icons.ts +116 -0
- package/src/index.tsx +6 -0
- package/src/index.web.tsx +194 -0
- package/src/labelStyle.ts +36 -0
- package/src/reactNativeInternals.d.ts +22 -0
- package/src/sharedTypes.ts +7 -1
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import UIKit
|
|
2
|
+
|
|
3
|
+
/// A native button: `UIButton` with a `UIButton.Configuration` chosen by the
|
|
4
|
+
/// variant, so it takes the system look of the iOS version it runs on.
|
|
5
|
+
@objcMembers
|
|
6
|
+
public final class PCButtonView: UIView {
|
|
7
|
+
// MARK: - Props (set from ObjC++)
|
|
8
|
+
|
|
9
|
+
public var label: String = "" {
|
|
10
|
+
didSet { if oldValue != label { applyConfiguration() } }
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/// "filled" | "tonal" | "outlined" | "text" | "elevated"
|
|
14
|
+
public var variant: String = "filled" {
|
|
15
|
+
didSet { if oldValue != variant { applyConfiguration() } }
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/// "xsmall" | "small" | "medium" | "large" | "xlarge"
|
|
19
|
+
public var size: String = "small" {
|
|
20
|
+
didSet { if oldValue != size { applyConfiguration() } }
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/// "" (UIKit default) | "round" | "square"
|
|
24
|
+
public var shape: String = "" {
|
|
25
|
+
didSet { if oldValue != shape { applyConfiguration() } }
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/// "enabled" | "disabled"
|
|
29
|
+
public var interactivity: String = "enabled" {
|
|
30
|
+
didSet { button.isEnabled = interactivity != "disabled" }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/// Container color; nil keeps the configuration's color
|
|
34
|
+
public var containerColor: UIColor? {
|
|
35
|
+
didSet { applyConfiguration() }
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/// Label and icon color; nil keeps the configuration's color
|
|
39
|
+
public var foregroundColor: UIColor? {
|
|
40
|
+
didSet { applyConfiguration() }
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/// Label font; nil keeps the configuration's font
|
|
44
|
+
public var labelFont: UIFont? {
|
|
45
|
+
didSet { applyConfiguration() }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/// Screen-reader label; empty uses the label
|
|
49
|
+
public var spokenLabel: String = "" {
|
|
50
|
+
didSet { button.accessibilityLabel = spokenLabel.isEmpty ? nil : spokenLabel }
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// MARK: - Events back to ObjC++
|
|
54
|
+
|
|
55
|
+
public var onPress: (() -> Void)?
|
|
56
|
+
|
|
57
|
+
/// Called when content changed after layout (an icon finished loading),
|
|
58
|
+
/// so the Fabric measurement can be refreshed.
|
|
59
|
+
public var onNeedsRemeasure: (() -> Void)?
|
|
60
|
+
|
|
61
|
+
// MARK: - Internal
|
|
62
|
+
|
|
63
|
+
private let button = UIButton(type: .system)
|
|
64
|
+
private var icon = PCButtonSupport.Icon.none
|
|
65
|
+
private var iconImage: UIImage?
|
|
66
|
+
|
|
67
|
+
/// Bumped on every icon change so late image loads can't apply a stale icon.
|
|
68
|
+
private var iconGeneration = 0
|
|
69
|
+
|
|
70
|
+
// MARK: - Init
|
|
71
|
+
|
|
72
|
+
public override init(frame: CGRect) {
|
|
73
|
+
super.init(frame: frame)
|
|
74
|
+
setup()
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
public required init?(coder: NSCoder) {
|
|
78
|
+
super.init(coder: coder)
|
|
79
|
+
setup()
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
private func setup() {
|
|
83
|
+
button.translatesAutoresizingMaskIntoConstraints = false
|
|
84
|
+
addSubview(button)
|
|
85
|
+
|
|
86
|
+
NSLayoutConstraint.activate([
|
|
87
|
+
button.topAnchor.constraint(equalTo: topAnchor),
|
|
88
|
+
button.bottomAnchor.constraint(equalTo: bottomAnchor),
|
|
89
|
+
button.leadingAnchor.constraint(equalTo: leadingAnchor),
|
|
90
|
+
button.trailingAnchor.constraint(equalTo: trailingAnchor),
|
|
91
|
+
])
|
|
92
|
+
|
|
93
|
+
button.addTarget(self, action: #selector(pressed), for: .touchUpInside)
|
|
94
|
+
applyConfiguration()
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
@objc private func pressed() {
|
|
98
|
+
onPress?()
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// MARK: - Props handling
|
|
102
|
+
|
|
103
|
+
/// Sets the icon from the flat spec fields. Images that are still loading
|
|
104
|
+
/// apply once they arrive.
|
|
105
|
+
public func setIcon(type: String, name: String, uri: String, scale: CGFloat, tinted: Bool) {
|
|
106
|
+
let next = PCButtonSupport.Icon(type: type, name: name, uri: uri, scale: scale, tinted: tinted)
|
|
107
|
+
guard next != icon else { return }
|
|
108
|
+
icon = next
|
|
109
|
+
iconGeneration += 1
|
|
110
|
+
let generation = iconGeneration
|
|
111
|
+
|
|
112
|
+
iconImage = PCButtonSupport.image(for: next) { [weak self] image in
|
|
113
|
+
guard let self, self.iconGeneration == generation else { return }
|
|
114
|
+
self.iconImage = image
|
|
115
|
+
self.applyConfiguration()
|
|
116
|
+
self.onNeedsRemeasure?()
|
|
117
|
+
}
|
|
118
|
+
applyConfiguration()
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
private func applyConfiguration() {
|
|
122
|
+
button.configuration = PCButtonSupport.makeConfiguration(
|
|
123
|
+
variant: variant,
|
|
124
|
+
selected: false,
|
|
125
|
+
size: size,
|
|
126
|
+
shape: shape,
|
|
127
|
+
label: label,
|
|
128
|
+
image: iconImage,
|
|
129
|
+
containerColor: containerColor,
|
|
130
|
+
foregroundColor: foregroundColor,
|
|
131
|
+
font: labelFont
|
|
132
|
+
)
|
|
133
|
+
invalidateIntrinsicContentSize()
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// MARK: - Sizing
|
|
137
|
+
|
|
138
|
+
public override var intrinsicContentSize: CGSize {
|
|
139
|
+
button.intrinsicContentSize
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
public override func sizeThatFits(_ size: CGSize) -> CGSize {
|
|
143
|
+
button.sizeThatFits(size)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/// Called by the measuring pipeline to get the size for Yoga layout: the
|
|
147
|
+
/// button's natural size, or its height at the given width.
|
|
148
|
+
@objc public func sizeForLayout(withConstrainedTo constrainedSize: CGSize) -> CGSize {
|
|
149
|
+
let fitted = button.sizeThatFits(
|
|
150
|
+
CGSize(width: constrainedSize.width > 0 ? constrainedSize.width : .greatestFiniteMagnitude,
|
|
151
|
+
height: .greatestFiniteMagnitude)
|
|
152
|
+
)
|
|
153
|
+
return CGSize(width: ceil(fitted.width), height: ceil(fitted.height))
|
|
154
|
+
}
|
|
155
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// PCFloatingToolbar.mm
|
|
2
|
+
//
|
|
3
|
+
// A floating toolbar: a capsule of Liquid Glass on iOS 26 (a blur material
|
|
4
|
+
// with a soft shadow on earlier versions) hosting the React children.
|
|
5
|
+
|
|
6
|
+
#import "PCFloatingToolbar.h"
|
|
7
|
+
|
|
8
|
+
#import <React/RCTComponentViewFactory.h>
|
|
9
|
+
#import <React/RCTConversions.h>
|
|
10
|
+
#import <React/RCTFabricComponentsPlugins.h>
|
|
11
|
+
|
|
12
|
+
#import <react/renderer/components/PlatformComponentsViewSpec/ComponentDescriptors.h>
|
|
13
|
+
#import <react/renderer/components/PlatformComponentsViewSpec/EventEmitters.h>
|
|
14
|
+
#import <react/renderer/components/PlatformComponentsViewSpec/Props.h>
|
|
15
|
+
|
|
16
|
+
#if __has_include(<PlatformComponents/PlatformComponents-Swift.h>)
|
|
17
|
+
#import <PlatformComponents/PlatformComponents-Swift.h>
|
|
18
|
+
#else
|
|
19
|
+
#import "PlatformComponents-Swift.h"
|
|
20
|
+
#endif
|
|
21
|
+
|
|
22
|
+
using namespace facebook::react;
|
|
23
|
+
|
|
24
|
+
@implementation PCFloatingToolbar {
|
|
25
|
+
PCFloatingToolbarView *_view;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
+ (ComponentDescriptorProvider)componentDescriptorProvider {
|
|
29
|
+
return concreteComponentDescriptorProvider<PCFloatingToolbarComponentDescriptor>();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
- (instancetype)initWithFrame:(CGRect)frame {
|
|
33
|
+
if (self = [super initWithFrame:frame]) {
|
|
34
|
+
_view = [[PCFloatingToolbarView alloc] initWithFrame:self.bounds];
|
|
35
|
+
_view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
|
36
|
+
[self addSubview:_view];
|
|
37
|
+
}
|
|
38
|
+
return self;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Mount children into the material's contentView
|
|
42
|
+
- (void)mountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index {
|
|
43
|
+
[_view.contentView insertSubview:childComponentView atIndex:index];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
- (void)unmountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index {
|
|
47
|
+
[childComponentView removeFromSuperview];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
- (void)updateProps:(Props::Shared const &)props
|
|
51
|
+
oldProps:(Props::Shared const &)oldProps {
|
|
52
|
+
const auto &newProps = *std::static_pointer_cast<const PCFloatingToolbarProps>(props);
|
|
53
|
+
const auto prevProps = std::static_pointer_cast<const PCFloatingToolbarProps>(oldProps);
|
|
54
|
+
|
|
55
|
+
// color -> material tint (SharedColor, already processed by React Native)
|
|
56
|
+
if (!prevProps || newProps.color != prevProps->color) {
|
|
57
|
+
_view.tintUIColor = RCTUIColorFromSharedColor(newProps.color);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ios.effect: "regular" | "clear"
|
|
61
|
+
const auto &newIos = newProps.ios;
|
|
62
|
+
const auto &oldIos = prevProps ? prevProps->ios : PCFloatingToolbarIosStruct{};
|
|
63
|
+
if (!prevProps || newIos.effect != oldIos.effect) {
|
|
64
|
+
_view.effectStyle = newIos.effect == "clear" ? @"clear" : @"regular";
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// android.variant: Android only
|
|
68
|
+
|
|
69
|
+
[super updateProps:props oldProps:oldProps];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
@end
|
|
73
|
+
|
|
74
|
+
Class<RCTComponentViewProtocol> PCFloatingToolbarCls(void) {
|
|
75
|
+
return PCFloatingToolbar.class;
|
|
76
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import UIKit
|
|
2
|
+
|
|
3
|
+
/// The floating toolbar container: a Liquid Glass capsule on iOS 26, and a
|
|
4
|
+
/// blur-material capsule with a soft shadow on earlier versions, where a blur
|
|
5
|
+
/// over a flat background would otherwise have no edge.
|
|
6
|
+
@objcMembers
|
|
7
|
+
public final class PCFloatingToolbarView: UIView {
|
|
8
|
+
// MARK: - Props (set from ObjC++)
|
|
9
|
+
|
|
10
|
+
/// "regular" | "clear"
|
|
11
|
+
public var effectStyle: String = "regular" {
|
|
12
|
+
didSet { if oldValue != effectStyle { applyEffect() } }
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/// Tint over the material; nil keeps the plain material
|
|
16
|
+
public var tintUIColor: UIColor? {
|
|
17
|
+
didSet { applyEffect() }
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/// React children mount here
|
|
21
|
+
public var contentView: UIView { effectView.contentView }
|
|
22
|
+
|
|
23
|
+
// MARK: - Internal
|
|
24
|
+
|
|
25
|
+
private let effectView = UIVisualEffectView(effect: nil)
|
|
26
|
+
|
|
27
|
+
/// Liquid Glass needs both the iOS 26 SDK at build time and iOS 26 at runtime.
|
|
28
|
+
private static var glassAvailable: Bool {
|
|
29
|
+
#if compiler(>=6.2)
|
|
30
|
+
if #available(iOS 26.0, *) { return true }
|
|
31
|
+
#endif
|
|
32
|
+
return false
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// MARK: - Init
|
|
36
|
+
|
|
37
|
+
public override init(frame: CGRect) {
|
|
38
|
+
super.init(frame: frame)
|
|
39
|
+
setup()
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
public required init?(coder: NSCoder) {
|
|
43
|
+
super.init(coder: coder)
|
|
44
|
+
setup()
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
private func setup() {
|
|
48
|
+
clipsToBounds = false
|
|
49
|
+
effectView.layer.cornerCurve = .continuous
|
|
50
|
+
addSubview(effectView)
|
|
51
|
+
|
|
52
|
+
if !Self.glassAvailable {
|
|
53
|
+
// The blur is clipped to the capsule; the shadow lives on this view
|
|
54
|
+
effectView.clipsToBounds = true
|
|
55
|
+
layer.shadowColor = UIColor.black.cgColor
|
|
56
|
+
layer.shadowOpacity = 0.12
|
|
57
|
+
layer.shadowRadius = 10
|
|
58
|
+
layer.shadowOffset = CGSize(width: 0, height: 4)
|
|
59
|
+
}
|
|
60
|
+
applyEffect()
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// MARK: - Layout
|
|
64
|
+
|
|
65
|
+
public override func layoutSubviews() {
|
|
66
|
+
super.layoutSubviews()
|
|
67
|
+
effectView.frame = bounds
|
|
68
|
+
let radius = min(bounds.width, bounds.height) / 2
|
|
69
|
+
effectView.layer.cornerRadius = radius
|
|
70
|
+
if !Self.glassAvailable {
|
|
71
|
+
layer.shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: radius).cgPath
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// MARK: - Effect
|
|
76
|
+
|
|
77
|
+
private func applyEffect() {
|
|
78
|
+
#if compiler(>=6.2)
|
|
79
|
+
if #available(iOS 26.0, *) {
|
|
80
|
+
let glass = UIGlassEffect(style: effectStyle == "clear" ? .clear : .regular)
|
|
81
|
+
if let tintUIColor {
|
|
82
|
+
glass.tintColor = tintUIColor
|
|
83
|
+
}
|
|
84
|
+
effectView.effect = glass
|
|
85
|
+
effectView.contentView.backgroundColor = nil
|
|
86
|
+
return
|
|
87
|
+
}
|
|
88
|
+
#endif
|
|
89
|
+
effectView.effect = UIBlurEffect(
|
|
90
|
+
style: effectStyle == "clear" ? .systemUltraThinMaterial : .systemThinMaterial
|
|
91
|
+
)
|
|
92
|
+
// UIBlurEffect has no tint; wash the content area with the color instead
|
|
93
|
+
effectView.contentView.backgroundColor = tintUIColor?.withAlphaComponent(0.35)
|
|
94
|
+
}
|
|
95
|
+
}
|