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,61 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <react/renderer/core/LayoutPrimitives.h>
|
|
4
|
+
#include <memory>
|
|
5
|
+
|
|
6
|
+
#if defined(RN_SERIALIZABLE_STATE) || defined(ANDROID)
|
|
7
|
+
#include <folly/dynamic.h>
|
|
8
|
+
#include <react/renderer/mapbuffer/MapBuffer.h>
|
|
9
|
+
#include <react/renderer/mapbuffer/MapBufferBuilder.h>
|
|
10
|
+
#endif
|
|
11
|
+
|
|
12
|
+
namespace facebook::react {
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Custom state for Button that holds the measured frame size from native.
|
|
16
|
+
* The native side measures the actual button and communicates the size to
|
|
17
|
+
* the shadow node for proper Yoga layout.
|
|
18
|
+
*/
|
|
19
|
+
struct PCButtonStateFrameSize {
|
|
20
|
+
using Shared = std::shared_ptr<const PCButtonStateFrameSize>;
|
|
21
|
+
|
|
22
|
+
Size frameSize{}; // {width, height} in points
|
|
23
|
+
|
|
24
|
+
PCButtonStateFrameSize() = default;
|
|
25
|
+
|
|
26
|
+
explicit PCButtonStateFrameSize(Size size) : frameSize(size) {}
|
|
27
|
+
|
|
28
|
+
bool operator==(const PCButtonStateFrameSize& other) const {
|
|
29
|
+
return frameSize.width == other.frameSize.width &&
|
|
30
|
+
frameSize.height == other.frameSize.height;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
bool operator!=(const PCButtonStateFrameSize& other) const {
|
|
34
|
+
return !(*this == other);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
#if defined(RN_SERIALIZABLE_STATE) || defined(ANDROID)
|
|
38
|
+
// Required for Android state serialization
|
|
39
|
+
PCButtonStateFrameSize(
|
|
40
|
+
const PCButtonStateFrameSize& previousState,
|
|
41
|
+
folly::dynamic data)
|
|
42
|
+
: frameSize(previousState.frameSize) {
|
|
43
|
+
if (data.isObject()) {
|
|
44
|
+
if (data.count("width") && data.count("height")) {
|
|
45
|
+
frameSize.width = static_cast<Float>(data["width"].asDouble());
|
|
46
|
+
frameSize.height = static_cast<Float>(data["height"].asDouble());
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
folly::dynamic getDynamic() const {
|
|
52
|
+
return folly::dynamic::object("width", frameSize.width)("height", frameSize.height);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
MapBuffer getMapBuffer() const {
|
|
56
|
+
return MapBufferBuilder::EMPTY();
|
|
57
|
+
}
|
|
58
|
+
#endif
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
} // namespace facebook::react
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <react/renderer/core/ConcreteComponentDescriptor.h>
|
|
4
|
+
|
|
5
|
+
// Forward declaration to avoid circular includes
|
|
6
|
+
namespace facebook::react {
|
|
7
|
+
class MeasuringPCTextFieldShadowNode;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// Include the actual shadow node definition
|
|
11
|
+
#include "PCTextFieldShadowNode-custom.h"
|
|
12
|
+
|
|
13
|
+
namespace facebook::react {
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Custom component descriptor that uses our measuring shadow node
|
|
17
|
+
* instead of the generated one.
|
|
18
|
+
*/
|
|
19
|
+
using MeasuringPCTextFieldComponentDescriptor =
|
|
20
|
+
ConcreteComponentDescriptor<MeasuringPCTextFieldShadowNode>;
|
|
21
|
+
|
|
22
|
+
} // namespace facebook::react
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#include "PCTextFieldShadowNode-custom.h"
|
|
2
|
+
|
|
3
|
+
#include <react/renderer/core/LayoutConstraints.h>
|
|
4
|
+
#include <algorithm>
|
|
5
|
+
|
|
6
|
+
namespace facebook::react {
|
|
7
|
+
|
|
8
|
+
Size MeasuringPCTextFieldShadowNode::measureContent(
|
|
9
|
+
const LayoutContext& /*layoutContext*/,
|
|
10
|
+
const LayoutConstraints& layoutConstraints) const {
|
|
11
|
+
|
|
12
|
+
const auto& stateData = this->getStateData();
|
|
13
|
+
Float measuredW = stateData.frameSize.width;
|
|
14
|
+
Float measuredH = stateData.frameSize.height;
|
|
15
|
+
|
|
16
|
+
const Float kHuge = static_cast<Float>(1.0e9);
|
|
17
|
+
|
|
18
|
+
// A text field fills the width it is offered; only an unbounded row (no
|
|
19
|
+
// width at all) gets a fixed fallback.
|
|
20
|
+
if (measuredW <= 0) {
|
|
21
|
+
const Float maxW = layoutConstraints.maximumSize.width;
|
|
22
|
+
measuredW = (maxW > 0 && maxW < kHuge) ? maxW : static_cast<Float>(kFallbackWidth);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const bool usingFallback = (measuredH <= 0);
|
|
26
|
+
if (usingFallback) {
|
|
27
|
+
#ifdef __ANDROID__
|
|
28
|
+
measuredH = static_cast<Float>(kFallbackHeightAndroid);
|
|
29
|
+
#else
|
|
30
|
+
measuredH = static_cast<Float>(kFallbackHeightIOS);
|
|
31
|
+
#endif
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
measuredW = std::max<Float>(measuredW, layoutConstraints.minimumSize.width);
|
|
35
|
+
measuredW = std::min<Float>(measuredW, layoutConstraints.maximumSize.width);
|
|
36
|
+
|
|
37
|
+
measuredH = std::max<Float>(measuredH, layoutConstraints.minimumSize.height);
|
|
38
|
+
// Only clamp to max height if we have real measured data (not fallback)
|
|
39
|
+
if (!usingFallback && layoutConstraints.maximumSize.height > 0 &&
|
|
40
|
+
layoutConstraints.maximumSize.height < kHuge) {
|
|
41
|
+
measuredH = std::min<Float>(measuredH, layoutConstraints.maximumSize.height);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return Size{measuredW, measuredH};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
} // namespace facebook::react
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <react/renderer/components/view/ConcreteViewShadowNode.h>
|
|
4
|
+
|
|
5
|
+
// Only include what we need for the shadow node definition
|
|
6
|
+
// Do NOT include ComponentDescriptors.h here to avoid circular dependency
|
|
7
|
+
#include <react/renderer/components/PlatformComponentsViewSpec/EventEmitters.h>
|
|
8
|
+
#include <react/renderer/components/PlatformComponentsViewSpec/Props.h>
|
|
9
|
+
|
|
10
|
+
#include "PCTextFieldState-custom.h"
|
|
11
|
+
|
|
12
|
+
namespace facebook::react {
|
|
13
|
+
|
|
14
|
+
extern const char PCTextFieldComponentName[];
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Custom ShadowNode for TextField that supports Yoga measurement.
|
|
18
|
+
*
|
|
19
|
+
* - The field takes the width Yoga offers (a text field fills its row)
|
|
20
|
+
* - Native measures the height at that width and updates state with it
|
|
21
|
+
* - Falls back to platform-specific heights until native has reported
|
|
22
|
+
*/
|
|
23
|
+
class MeasuringPCTextFieldShadowNode final : public ConcreteViewShadowNode<
|
|
24
|
+
PCTextFieldComponentName,
|
|
25
|
+
PCTextFieldProps,
|
|
26
|
+
PCTextFieldEventEmitter,
|
|
27
|
+
PCTextFieldStateFrameSize> {
|
|
28
|
+
public:
|
|
29
|
+
using ConcreteViewShadowNode::ConcreteViewShadowNode;
|
|
30
|
+
|
|
31
|
+
// Fallback heights used when native hasn't reported measurements yet
|
|
32
|
+
// iOS: a rounded-rect UITextField
|
|
33
|
+
static constexpr float kFallbackHeightIOS = 34.0f;
|
|
34
|
+
|
|
35
|
+
// Android: a Material 3 outlined text field
|
|
36
|
+
static constexpr float kFallbackHeightAndroid = 56.0f;
|
|
37
|
+
|
|
38
|
+
static constexpr float kFallbackWidth = 200.0f;
|
|
39
|
+
|
|
40
|
+
static ShadowNodeTraits BaseTraits() {
|
|
41
|
+
auto traits = ConcreteViewShadowNode::BaseTraits();
|
|
42
|
+
traits.set(ShadowNodeTraits::Trait::LeafYogaNode);
|
|
43
|
+
traits.set(ShadowNodeTraits::Trait::MeasurableYogaNode);
|
|
44
|
+
return traits;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Called by Yoga when it needs the intrinsic size of the component.
|
|
49
|
+
*/
|
|
50
|
+
Size measureContent(
|
|
51
|
+
const LayoutContext& layoutContext,
|
|
52
|
+
const LayoutConstraints& layoutConstraints) const override;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
} // namespace facebook::react
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <react/renderer/core/LayoutPrimitives.h>
|
|
4
|
+
#include <memory>
|
|
5
|
+
|
|
6
|
+
#if defined(RN_SERIALIZABLE_STATE) || defined(ANDROID)
|
|
7
|
+
#include <folly/dynamic.h>
|
|
8
|
+
#include <react/renderer/mapbuffer/MapBuffer.h>
|
|
9
|
+
#include <react/renderer/mapbuffer/MapBufferBuilder.h>
|
|
10
|
+
#endif
|
|
11
|
+
|
|
12
|
+
namespace facebook::react {
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Custom state for TextField that holds the measured frame size from native.
|
|
16
|
+
* The field fills the width it is given; native measures the height the
|
|
17
|
+
* label, text and supporting text need at that width and reports it here.
|
|
18
|
+
*/
|
|
19
|
+
struct PCTextFieldStateFrameSize {
|
|
20
|
+
using Shared = std::shared_ptr<const PCTextFieldStateFrameSize>;
|
|
21
|
+
|
|
22
|
+
Size frameSize{}; // {width, height} in points; width 0 = fill the available width
|
|
23
|
+
|
|
24
|
+
PCTextFieldStateFrameSize() = default;
|
|
25
|
+
|
|
26
|
+
explicit PCTextFieldStateFrameSize(Size size) : frameSize(size) {}
|
|
27
|
+
|
|
28
|
+
bool operator==(const PCTextFieldStateFrameSize& other) const {
|
|
29
|
+
return frameSize.width == other.frameSize.width &&
|
|
30
|
+
frameSize.height == other.frameSize.height;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
bool operator!=(const PCTextFieldStateFrameSize& other) const {
|
|
34
|
+
return !(*this == other);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
#if defined(RN_SERIALIZABLE_STATE) || defined(ANDROID)
|
|
38
|
+
// Required for Android state serialization
|
|
39
|
+
PCTextFieldStateFrameSize(
|
|
40
|
+
const PCTextFieldStateFrameSize& previousState,
|
|
41
|
+
folly::dynamic data)
|
|
42
|
+
: frameSize(previousState.frameSize) {
|
|
43
|
+
if (data.isObject()) {
|
|
44
|
+
if (data.count("width") && data.count("height")) {
|
|
45
|
+
frameSize.width = static_cast<Float>(data["width"].asDouble());
|
|
46
|
+
frameSize.height = static_cast<Float>(data["height"].asDouble());
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
folly::dynamic getDynamic() const {
|
|
52
|
+
return folly::dynamic::object("width", frameSize.width)("height", frameSize.height);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
MapBuffer getMapBuffer() const {
|
|
56
|
+
return MapBufferBuilder::EMPTY();
|
|
57
|
+
}
|
|
58
|
+
#endif
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
} // namespace facebook::react
|
|
@@ -8,3 +8,6 @@
|
|
|
8
8
|
#include "PCSelectionMenuComponentDescriptors-custom.h"
|
|
9
9
|
#include "PCDatePickerComponentDescriptors-custom.h"
|
|
10
10
|
#include "PCSegmentedControlComponentDescriptors-custom.h"
|
|
11
|
+
#include "PCButtonComponentDescriptors-custom.h"
|
|
12
|
+
#include "PCButtonGroupComponentDescriptors-custom.h"
|
|
13
|
+
#include "PCTextFieldComponentDescriptors-custom.h"
|
package/src/Button.tsx
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
// Button.tsx
|
|
2
|
+
import React, { useCallback, useMemo } from 'react';
|
|
3
|
+
import type { ColorValue, ViewProps } from 'react-native';
|
|
4
|
+
|
|
5
|
+
import NativeButton from './ButtonNativeComponent';
|
|
6
|
+
import { resolveIcon, type PlatformIcon } from './icons';
|
|
7
|
+
import { normalizeLabelStyle, type LabelStyle } from './labelStyle';
|
|
8
|
+
import type { AndroidMaterialStyle } from './sharedTypes';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Emphasis of the button, from highest to lowest.
|
|
12
|
+
*
|
|
13
|
+
* | Variant | Android (Material 3 Expressive) | iOS (`UIButton.Configuration`) |
|
|
14
|
+
* | ---------- | ------------------------------- | ------------------------------ |
|
|
15
|
+
* | `filled` | Filled button | `.filled()` |
|
|
16
|
+
* | `tonal` | Filled tonal button | `.tinted()` |
|
|
17
|
+
* | `outlined` | Outlined button | `.bordered()` |
|
|
18
|
+
* | `text` | Text button | `.plain()` |
|
|
19
|
+
* | `elevated` | Elevated button | `.gray()` |
|
|
20
|
+
*/
|
|
21
|
+
export type ButtonVariant =
|
|
22
|
+
'filled' | 'tonal' | 'outlined' | 'text' | 'elevated';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Button size. Material 3 Expressive defines five sizes; iOS maps them onto
|
|
26
|
+
* `UIButton.Configuration.Size` (`xsmall` → mini, `xlarge` → large).
|
|
27
|
+
*/
|
|
28
|
+
export type ButtonSize = 'xsmall' | 'small' | 'medium' | 'large' | 'xlarge';
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Corner shape. `round` is a pill; `square` keeps rounded corners. Unset
|
|
32
|
+
* means the platform default (round on Android, dynamic on iOS).
|
|
33
|
+
*/
|
|
34
|
+
export type ButtonShape = 'round' | 'square';
|
|
35
|
+
|
|
36
|
+
export interface ButtonProps extends ViewProps {
|
|
37
|
+
/** Button text. Omit for an icon-only button. */
|
|
38
|
+
label?: string;
|
|
39
|
+
|
|
40
|
+
/** Icon shown before the label, or alone when there is no label. */
|
|
41
|
+
icon?: PlatformIcon;
|
|
42
|
+
|
|
43
|
+
/** Emphasis. Default: `'filled'`. See {@link ButtonVariant}. */
|
|
44
|
+
variant?: ButtonVariant;
|
|
45
|
+
|
|
46
|
+
/** Size. Default: `'small'`, the Material 3 default. */
|
|
47
|
+
size?: ButtonSize;
|
|
48
|
+
|
|
49
|
+
/** Corner shape. Default: platform default. */
|
|
50
|
+
shape?: ButtonShape;
|
|
51
|
+
|
|
52
|
+
/** Whether the button is disabled. */
|
|
53
|
+
disabled?: boolean;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Container (background) color.
|
|
57
|
+
* Android: `backgroundTint`. iOS: `baseBackgroundColor`.
|
|
58
|
+
*/
|
|
59
|
+
color?: ColorValue;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Label and icon color.
|
|
63
|
+
* Android: text color and `iconTint`. iOS: `baseForegroundColor`.
|
|
64
|
+
*/
|
|
65
|
+
tintColor?: ColorValue;
|
|
66
|
+
|
|
67
|
+
/** Label font. */
|
|
68
|
+
labelStyle?: LabelStyle;
|
|
69
|
+
|
|
70
|
+
/** Screen-reader label. Defaults to `label`. */
|
|
71
|
+
accessibilityLabel?: string;
|
|
72
|
+
|
|
73
|
+
/** Called when the button is pressed. */
|
|
74
|
+
onPress?: () => void;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Android-specific configuration
|
|
78
|
+
*/
|
|
79
|
+
android?: {
|
|
80
|
+
/** Ripple color shown while pressing. */
|
|
81
|
+
rippleColor?: ColorValue;
|
|
82
|
+
|
|
83
|
+
/** Outline color (outlined variant). */
|
|
84
|
+
strokeColor?: ColorValue;
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Material style: Material 3 Expressive (default), or the classic
|
|
88
|
+
* Material 3 button, which has one size and shape (`size` and `shape`
|
|
89
|
+
* are ignored).
|
|
90
|
+
*/
|
|
91
|
+
material?: AndroidMaterialStyle;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
/** Test identifier */
|
|
95
|
+
testID?: string;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function Button(props: ButtonProps): React.ReactElement {
|
|
99
|
+
const {
|
|
100
|
+
label,
|
|
101
|
+
icon,
|
|
102
|
+
variant,
|
|
103
|
+
size,
|
|
104
|
+
shape,
|
|
105
|
+
disabled,
|
|
106
|
+
color,
|
|
107
|
+
tintColor,
|
|
108
|
+
labelStyle,
|
|
109
|
+
accessibilityLabel,
|
|
110
|
+
onPress,
|
|
111
|
+
android,
|
|
112
|
+
...viewProps
|
|
113
|
+
} = props;
|
|
114
|
+
|
|
115
|
+
const nativeIcon = useMemo(() => resolveIcon(icon), [icon]);
|
|
116
|
+
const nativeLabelStyle = useMemo(
|
|
117
|
+
() => normalizeLabelStyle(labelStyle),
|
|
118
|
+
[labelStyle]
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
const handlePress = useCallback(() => {
|
|
122
|
+
onPress?.();
|
|
123
|
+
}, [onPress]);
|
|
124
|
+
|
|
125
|
+
return (
|
|
126
|
+
<NativeButton
|
|
127
|
+
label={label ?? ''}
|
|
128
|
+
icon={nativeIcon}
|
|
129
|
+
variant={variant ?? 'filled'}
|
|
130
|
+
size={size ?? 'small'}
|
|
131
|
+
shape={shape ?? ''}
|
|
132
|
+
interactivity={disabled ? 'disabled' : 'enabled'}
|
|
133
|
+
color={color}
|
|
134
|
+
foregroundColor={tintColor}
|
|
135
|
+
androidRippleColor={android?.rippleColor}
|
|
136
|
+
androidStrokeColor={android?.strokeColor}
|
|
137
|
+
androidMaterial={android?.material ?? 'expressive'}
|
|
138
|
+
labelStyle={nativeLabelStyle}
|
|
139
|
+
spokenLabel={accessibilityLabel ?? ''}
|
|
140
|
+
onButtonPress={onPress ? handlePress : undefined}
|
|
141
|
+
{...viewProps}
|
|
142
|
+
/>
|
|
143
|
+
);
|
|
144
|
+
}
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
// ButtonGroup.tsx
|
|
2
|
+
import React, { useCallback, useMemo } from 'react';
|
|
3
|
+
import type { ColorValue, ViewProps } from 'react-native';
|
|
4
|
+
|
|
5
|
+
import type { ButtonShape, ButtonSize, ButtonVariant } from './Button';
|
|
6
|
+
import NativeButtonGroup, {
|
|
7
|
+
type ButtonGroupButton as NativeButton,
|
|
8
|
+
type ButtonGroupPressEvent,
|
|
9
|
+
type ButtonGroupSelectionEvent,
|
|
10
|
+
} from './ButtonGroupNativeComponent';
|
|
11
|
+
import { resolveIcon, type PlatformIcon } from './icons';
|
|
12
|
+
import { normalizeLabelStyle, type LabelStyle } from './labelStyle';
|
|
13
|
+
import type { AndroidMaterialStyle } from './sharedTypes';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Selection behavior of the group.
|
|
17
|
+
*
|
|
18
|
+
* - `none` (default): the buttons are plain actions.
|
|
19
|
+
* - `single`: at most one button is selected at a time.
|
|
20
|
+
* - `multiple`: any number of buttons can be selected.
|
|
21
|
+
*/
|
|
22
|
+
export type ButtonGroupSelection = 'none' | 'single' | 'multiple';
|
|
23
|
+
|
|
24
|
+
/** What happens to buttons that don't fit the group's width (Android). */
|
|
25
|
+
export type ButtonGroupOverflow = 'none' | 'menu' | 'wrap';
|
|
26
|
+
|
|
27
|
+
export interface ButtonGroupButtonProps {
|
|
28
|
+
/** Button text. Omit for an icon-only button. */
|
|
29
|
+
label?: string;
|
|
30
|
+
|
|
31
|
+
/** Unique value identifier for the button */
|
|
32
|
+
value: string;
|
|
33
|
+
|
|
34
|
+
/** Whether this specific button is disabled */
|
|
35
|
+
disabled?: boolean;
|
|
36
|
+
|
|
37
|
+
/** Optional icon. See {@link PlatformIcon}. */
|
|
38
|
+
icon?: PlatformIcon;
|
|
39
|
+
|
|
40
|
+
/** Screen-reader label. Defaults to `label`. */
|
|
41
|
+
accessibilityLabel?: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface ButtonGroupProps extends ViewProps {
|
|
45
|
+
/** Buttons to display */
|
|
46
|
+
buttons: readonly ButtonGroupButtonProps[];
|
|
47
|
+
|
|
48
|
+
/** Emphasis of every button. Default: `'outlined'`. */
|
|
49
|
+
variant?: ButtonVariant;
|
|
50
|
+
|
|
51
|
+
/** Size of every button. Default: `'small'`. */
|
|
52
|
+
size?: ButtonSize;
|
|
53
|
+
|
|
54
|
+
/** Corner shape of every button. Default: platform default. */
|
|
55
|
+
shape?: ButtonShape;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Connected group: the buttons share one outline with small inner corners
|
|
59
|
+
* (Material 3 "connected button group"). Default: `true` when `selection`
|
|
60
|
+
* is `'single'` or `'multiple'`, `false` otherwise.
|
|
61
|
+
*/
|
|
62
|
+
connected?: boolean;
|
|
63
|
+
|
|
64
|
+
/** Gap between buttons in points. Default: the platform's group spacing. */
|
|
65
|
+
spacing?: number;
|
|
66
|
+
|
|
67
|
+
/** Selection behavior. Default: `'none'`. See {@link ButtonGroupSelection}. */
|
|
68
|
+
selection?: ButtonGroupSelection;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Values of the selected buttons (controlled). Only the first value is
|
|
72
|
+
* used with `selection: 'single'`.
|
|
73
|
+
*/
|
|
74
|
+
selectedValues?: readonly string[];
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Whether at least one button must stay selected. Default: `true` for
|
|
78
|
+
* `single`, `false` for `multiple`.
|
|
79
|
+
*/
|
|
80
|
+
selectionRequired?: boolean;
|
|
81
|
+
|
|
82
|
+
/** Whether the entire group is disabled */
|
|
83
|
+
disabled?: boolean;
|
|
84
|
+
|
|
85
|
+
/** Container (background) color of the buttons. */
|
|
86
|
+
color?: ColorValue;
|
|
87
|
+
|
|
88
|
+
/** Label and icon color of the buttons. */
|
|
89
|
+
tintColor?: ColorValue;
|
|
90
|
+
|
|
91
|
+
/** Label font. */
|
|
92
|
+
labelStyle?: LabelStyle;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Called when a button is pressed, in every selection mode.
|
|
96
|
+
* @param value - The button's value
|
|
97
|
+
* @param index - The button's index
|
|
98
|
+
*/
|
|
99
|
+
onPress?: (value: string, index: number) => void;
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Called when the selection changes (`single` / `multiple` selection).
|
|
103
|
+
* @param values - Values of the selected buttons, in button order
|
|
104
|
+
*/
|
|
105
|
+
onSelectionChange?: (values: string[]) => void;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Android-specific configuration
|
|
109
|
+
*/
|
|
110
|
+
android?: {
|
|
111
|
+
/**
|
|
112
|
+
* What happens to buttons that don't fit: `'none'` (clipped), `'menu'`
|
|
113
|
+
* (moved into an overflow menu) or `'wrap'` (wrapped onto more rows).
|
|
114
|
+
* Default: `'none'`.
|
|
115
|
+
*/
|
|
116
|
+
overflow?: ButtonGroupOverflow;
|
|
117
|
+
|
|
118
|
+
/** Ripple color shown while pressing a button. */
|
|
119
|
+
rippleColor?: ColorValue;
|
|
120
|
+
|
|
121
|
+
/** Outline color (outlined variant). */
|
|
122
|
+
strokeColor?: ColorValue;
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Material style: Material 3 Expressive (default), or the classic
|
|
126
|
+
* Material 3 group, which has one size and shape (`size` and `shape`
|
|
127
|
+
* are ignored).
|
|
128
|
+
*/
|
|
129
|
+
material?: AndroidMaterialStyle;
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
/** Test identifier */
|
|
133
|
+
testID?: string;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function ButtonGroup(props: ButtonGroupProps): React.ReactElement {
|
|
137
|
+
const {
|
|
138
|
+
buttons,
|
|
139
|
+
variant,
|
|
140
|
+
size,
|
|
141
|
+
shape,
|
|
142
|
+
connected,
|
|
143
|
+
spacing,
|
|
144
|
+
selection,
|
|
145
|
+
selectedValues,
|
|
146
|
+
selectionRequired,
|
|
147
|
+
disabled,
|
|
148
|
+
color,
|
|
149
|
+
tintColor,
|
|
150
|
+
labelStyle,
|
|
151
|
+
onPress,
|
|
152
|
+
onSelectionChange,
|
|
153
|
+
android,
|
|
154
|
+
...viewProps
|
|
155
|
+
} = props;
|
|
156
|
+
|
|
157
|
+
const selectionMode = selection ?? 'none';
|
|
158
|
+
|
|
159
|
+
const nativeButtons = useMemo((): NativeButton[] => {
|
|
160
|
+
return buttons.map((button) => ({
|
|
161
|
+
label: button.label ?? '',
|
|
162
|
+
value: button.value,
|
|
163
|
+
disabled: button.disabled ? 'disabled' : 'enabled',
|
|
164
|
+
accessibilityLabel: button.accessibilityLabel ?? '',
|
|
165
|
+
...resolveIcon(button.icon),
|
|
166
|
+
}));
|
|
167
|
+
}, [buttons]);
|
|
168
|
+
|
|
169
|
+
const nativeSelectedValues = useMemo((): string[] => {
|
|
170
|
+
if (selectionMode === 'none' || !selectedValues) return [];
|
|
171
|
+
return selectionMode === 'single'
|
|
172
|
+
? selectedValues.slice(0, 1)
|
|
173
|
+
: [...selectedValues];
|
|
174
|
+
}, [selectionMode, selectedValues]);
|
|
175
|
+
|
|
176
|
+
const nativeLabelStyle = useMemo(
|
|
177
|
+
() => normalizeLabelStyle(labelStyle),
|
|
178
|
+
[labelStyle]
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
const handlePress = useCallback(
|
|
182
|
+
(e: { nativeEvent: ButtonGroupPressEvent }) => {
|
|
183
|
+
const { index, value } = e.nativeEvent;
|
|
184
|
+
onPress?.(value, index);
|
|
185
|
+
},
|
|
186
|
+
[onPress]
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
const handleSelectionChange = useCallback(
|
|
190
|
+
(e: { nativeEvent: ButtonGroupSelectionEvent }) => {
|
|
191
|
+
onSelectionChange?.([...e.nativeEvent.values]);
|
|
192
|
+
},
|
|
193
|
+
[onSelectionChange]
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
const nativeAndroid = useMemo(
|
|
197
|
+
() => ({
|
|
198
|
+
overflow: android?.overflow ?? 'none',
|
|
199
|
+
material: android?.material ?? 'expressive',
|
|
200
|
+
}),
|
|
201
|
+
[android]
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
const isConnected = connected ?? selectionMode !== 'none';
|
|
205
|
+
const isSelectionRequired =
|
|
206
|
+
selectionRequired ?? (selectionMode === 'single' ? true : false);
|
|
207
|
+
|
|
208
|
+
return (
|
|
209
|
+
<NativeButtonGroup
|
|
210
|
+
buttons={nativeButtons}
|
|
211
|
+
variant={variant ?? 'outlined'}
|
|
212
|
+
size={size ?? 'small'}
|
|
213
|
+
shape={shape ?? ''}
|
|
214
|
+
connected={isConnected ? 'true' : 'false'}
|
|
215
|
+
spacing={spacing ?? -1}
|
|
216
|
+
selection={selectionMode}
|
|
217
|
+
selectedValues={nativeSelectedValues}
|
|
218
|
+
selectionRequired={isSelectionRequired ? 'true' : 'false'}
|
|
219
|
+
interactivity={disabled ? 'disabled' : 'enabled'}
|
|
220
|
+
color={color}
|
|
221
|
+
foregroundColor={tintColor}
|
|
222
|
+
androidRippleColor={android?.rippleColor}
|
|
223
|
+
androidStrokeColor={android?.strokeColor}
|
|
224
|
+
labelStyle={nativeLabelStyle}
|
|
225
|
+
onButtonPress={onPress ? handlePress : undefined}
|
|
226
|
+
onGroupSelectionChange={
|
|
227
|
+
onSelectionChange ? handleSelectionChange : undefined
|
|
228
|
+
}
|
|
229
|
+
android={nativeAndroid}
|
|
230
|
+
{...viewProps}
|
|
231
|
+
/>
|
|
232
|
+
);
|
|
233
|
+
}
|