diff --git a/React/Fabric/Mounting/ComponentViews/Picker/RCTPickerComponentView.h b/React/Fabric/Mounting/ComponentViews/Picker/RCTPickerComponentView.h new file mode 100644 index 00000000000..62f6d1e61a6 --- /dev/null +++ b/React/Fabric/Mounting/ComponentViews/Picker/RCTPickerComponentView.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * UIView class for root component. + */ +@interface RCTPickerComponentView : RCTViewComponentView + +@end + +NS_ASSUME_NONNULL_END diff --git a/React/Fabric/Mounting/ComponentViews/Picker/RCTPickerComponentView.mm b/React/Fabric/Mounting/ComponentViews/Picker/RCTPickerComponentView.mm new file mode 100644 index 00000000000..bc9e3b4c4d5 --- /dev/null +++ b/React/Fabric/Mounting/ComponentViews/Picker/RCTPickerComponentView.mm @@ -0,0 +1,176 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import "RCTPickerComponentView.h" + +#import +#import +#import +#import + +#import "FBRCTFabricComponentsPlugins.h" +#import "RCTConversions.h" + +using namespace facebook::react; + +@interface RCTPickerComponentView () + +@end + +@implementation RCTPickerComponentView { + UIPickerView *_pickerView; + UIColor *_textColor; + UIFont *_font; + NSTextAlignment _textAlignment; + std::vector _items; + NSInteger _selectedIndex; + NSString *_accessibilityLabel; +} + +- (instancetype)initWithFrame:(CGRect)frame +{ + if (self = [super initWithFrame:frame]) { + _pickerView = [[UIPickerView alloc] initWithFrame:self.bounds]; + // TODO (T75217510) - Handle and test onChange, something like: + // [_pickerView addTarget:self action:@selector(onChange:) forControlEvents:UIControlEventValueChanged]; + self.contentView = _pickerView; + [self setPropsToDefault]; + } + + return self; +} + +- (void)setPropsToDefault +{ + static const auto defaultProps = std::make_shared(); + _props = defaultProps; + + _pickerView.delegate = self; + _pickerView.dataSource = self; + _textColor = [UIColor blackColor]; + _font = [UIFont systemFontOfSize:21]; + _textAlignment = NSTextAlignmentCenter; + _selectedIndex = NSNotFound; +} + +- (void)prepareForRecycle +{ + [super prepareForRecycle]; + _selectedIndex = NSNotFound; +} + +#pragma mark - RCTComponentViewProtocol + ++ (ComponentDescriptorProvider)componentDescriptorProvider +{ + return concreteComponentDescriptorProvider(); +} + +- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps +{ + const auto &oldPickerProps = *std::static_pointer_cast(_props); + const auto &newPickerProps = *std::static_pointer_cast(props); + + if (oldPickerProps.items != newPickerProps.items) { + _items = newPickerProps.items; + } + + if (oldPickerProps.selectedIndex != newPickerProps.selectedIndex) { + _selectedIndex = newPickerProps.selectedIndex; + } + + // TODO (T75217510) - Figure out how to forward styling. + if (oldPickerProps.style != newPickerProps.style) { + } + + // TODO (T75217510) - Figure out testID. + if (oldPickerProps.testID != newPickerProps.testID) { + } + + if (oldPickerProps.accessibilityLabel != newPickerProps.accessibilityLabel) { + _accessibilityLabel = [NSString stringWithUTF8String:newPickerProps.accessibilityLabel.c_str()]; + } + + [super updateProps:props oldProps:oldProps]; +} + +- (void)onChange:(UISwitch *)sender +{ + // TODO (T75217510) - Handle and test onChange +} + +// TODO (T75217510) - Handle Native Commands +#pragma mark - Native Commands + +#pragma mark - UIPickerViewDataSource protocol + +- (NSInteger)numberOfComponentsInPickerView:(__unused UIPickerView *)pickerView +{ + return 1; +} + +- (NSInteger)pickerView:(__unused UIPickerView *)pickerView numberOfRowsInComponent:(__unused NSInteger)component +{ + return _items.size(); +} + +#pragma mark - UIPickerViewDelegate methods + +- (NSString *)pickerView:(__unused UIPickerView *)pickerView + titleForRow:(NSInteger)row + forComponent:(__unused NSInteger)component +{ + return [NSString stringWithUTF8String:_items[row].label.c_str()]; + ; +} + +- (CGFloat)pickerView:(__unused UIPickerView *)pickerView rowHeightForComponent:(NSInteger)__unused component +{ + return _font.pointSize + 19; +} + +- (UIView *)pickerView:(UIPickerView *)pickerView + viewForRow:(NSInteger)row + forComponent:(NSInteger)component + reusingView:(UILabel *)label +{ + if (!label) { + label = [[UILabel alloc] initWithFrame:(CGRect){CGPointZero, + { + [pickerView rowSizeForComponent:component].width, + [pickerView rowSizeForComponent:component].height, + }}]; + } + label.font = _font; + // TODO (T75217510) - This should be something like RCTUIColorFromSharedColor(_items[row].textColor) ?: _textColor; + label.textColor = [UIColor blackColor]; + label.textAlignment = _textAlignment; + label.text = [self pickerView:pickerView titleForRow:row forComponent:component]; + return label; +} + +- (void)pickerView:(__unused UIPickerView *)pickerView + didSelectRow:(NSInteger)row + inComponent:(__unused NSInteger)component +{ + _selectedIndex = row; + // TODO (T75217510) - Handle and test onChange +} + +#pragma mark - UIPickerViewAccessibilityDelegate protocol + +- (NSString *)pickerView:(UIPickerView *)pickerView accessibilityLabelForComponent:(NSInteger)component +{ + return _accessibilityLabel; +} + +@end + +Class RCTPickerCls(void) +{ + return RCTPickerComponentView.class; +} diff --git a/React/Fabric/Mounting/ComponentViews/RCTFabricComponentsPlugins.h b/React/Fabric/Mounting/ComponentViews/RCTFabricComponentsPlugins.h index 4f6ea0e8156..47cd8759fab 100644 --- a/React/Fabric/Mounting/ComponentViews/RCTFabricComponentsPlugins.h +++ b/React/Fabric/Mounting/ComponentViews/RCTFabricComponentsPlugins.h @@ -35,6 +35,7 @@ Class RCTPullToRefreshViewCls(void) __attribute__((use Class RCTActivityIndicatorViewCls(void) __attribute__((used)); Class RCTSliderCls(void) __attribute__((used)); Class RCTSwitchCls(void) __attribute__((used)); +Class RCTPickerCls(void) __attribute__((used)); Class RCTUnimplementedNativeViewCls(void) __attribute__((used)); Class RCTParagraphCls(void) __attribute__((used)); Class RCTTextInputCls(void) __attribute__((used)); diff --git a/React/Fabric/Mounting/ComponentViews/RCTFabricComponentsPlugins.mm b/React/Fabric/Mounting/ComponentViews/RCTFabricComponentsPlugins.mm index a52c8c06e36..e5ed57f33af 100644 --- a/React/Fabric/Mounting/ComponentViews/RCTFabricComponentsPlugins.mm +++ b/React/Fabric/Mounting/ComponentViews/RCTFabricComponentsPlugins.mm @@ -24,6 +24,7 @@ Class RCTFabricComponentsProvider(const char *name) { {"ActivityIndicatorView", RCTActivityIndicatorViewCls}, {"Slider", RCTSliderCls}, {"Switch", RCTSwitchCls}, + {"Picker", RCTPickerCls}, {"UnimplementedNativeView", RCTUnimplementedNativeViewCls}, {"Paragraph", RCTParagraphCls}, {"TextInput", RCTTextInputCls}, diff --git a/ReactCommon/react/renderer/components/picker/BUCK b/ReactCommon/react/renderer/components/picker/BUCK index 985b331f4ac..9c0661e9370 100644 --- a/ReactCommon/react/renderer/components/picker/BUCK +++ b/ReactCommon/react/renderer/components/picker/BUCK @@ -19,11 +19,11 @@ APPLE_COMPILER_FLAGS = get_apple_compiler_flags() rn_xplat_cxx_library( name = "androidpicker", srcs = glob( - ["**/*.cpp"], + ["androidpicker/**/*.cpp"], exclude = glob(["tests/**/*.cpp"]), ), headers = glob( - ["**/*.h"], + ["androidpicker/**/*.h"], exclude = glob(["tests/**/*.h"]), ), header_namespace = "", diff --git a/ReactCommon/react/renderer/components/picker/iospicker/BUCK b/ReactCommon/react/renderer/components/picker/iospicker/BUCK new file mode 100644 index 00000000000..8e48198de3e --- /dev/null +++ b/ReactCommon/react/renderer/components/picker/iospicker/BUCK @@ -0,0 +1,58 @@ +load("@fbsource//tools/build_defs/apple:flag_defs.bzl", "get_preprocessor_flags_for_build_mode") +load( + "//tools/build_defs/oss:rn_defs.bzl", + "ANDROID", + "APPLE", + "CXX", + "YOGA_CXX_TARGET", + "get_apple_compiler_flags", + "get_apple_inspector_flags", + "react_native_xplat_target", + "rn_xplat_cxx_library", + "subdir_glob", +) + +APPLE_COMPILER_FLAGS = get_apple_compiler_flags() + +rn_xplat_cxx_library( + name = "iospicker", + srcs = glob( + ["**/*.cpp"], + ), + headers = glob( + ["**/*.h"], + ), + header_namespace = "", + exported_headers = subdir_glob( + [ + ("", "*.h"), + ], + prefix = "react/renderer/components/iospicker", + ), + compiler_flags = [ + "-fexceptions", + "-frtti", + "-std=c++14", + "-Wall", + ], + fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, + fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(), + force_static = True, + labels = ["supermodule:xplat/default/public.react_native.infra"], + platforms = (ANDROID, APPLE, CXX), + preprocessor_flags = [ + "-DLOG_TAG=\"ReactNative\"", + "-DWITH_FBSYSTRACE=1", + ], + visibility = ["PUBLIC"], + deps = [ + "//xplat/folly:headers_only", + YOGA_CXX_TARGET, + react_native_xplat_target("react/utils:utils"), + react_native_xplat_target("react/renderer/attributedstring:attributedstring"), + react_native_xplat_target("react/renderer/core:core"), + react_native_xplat_target("react/renderer/graphics:graphics"), + react_native_xplat_target("react/renderer/components/text:text"), + react_native_xplat_target("react/renderer/components/view:view"), + ], +) diff --git a/ReactCommon/react/renderer/components/picker/iospicker/PickerComponentDescriptor.h b/ReactCommon/react/renderer/components/picker/iospicker/PickerComponentDescriptor.h new file mode 100644 index 00000000000..d134689e784 --- /dev/null +++ b/ReactCommon/react/renderer/components/picker/iospicker/PickerComponentDescriptor.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include + +/* + * Descriptor for component. + */ +namespace facebook { +namespace react { + +class PickerComponentDescriptor final + : public ConcreteComponentDescriptor { + public: + using ConcreteComponentDescriptor::ConcreteComponentDescriptor; +}; + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/react/renderer/components/picker/iospicker/PickerEventEmitter.h b/ReactCommon/react/renderer/components/picker/iospicker/PickerEventEmitter.h new file mode 100644 index 00000000000..df523632ced --- /dev/null +++ b/ReactCommon/react/renderer/components/picker/iospicker/PickerEventEmitter.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +namespace facebook { +namespace react { + +class PickerEventEmitter : public ViewEventEmitter { + public: + using ViewEventEmitter::ViewEventEmitter; +}; + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/react/renderer/components/picker/iospicker/PickerProps.cpp b/ReactCommon/react/renderer/components/picker/iospicker/PickerProps.cpp new file mode 100644 index 00000000000..200c8253088 --- /dev/null +++ b/ReactCommon/react/renderer/components/picker/iospicker/PickerProps.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "PickerProps.h" + +#include +#include +#include + +namespace facebook { +namespace react { + +PickerProps::PickerProps( + PickerProps const &sourceProps, + RawProps const &rawProps) + : ViewProps(sourceProps, rawProps), + items(convertRawProp(rawProps, "items", sourceProps.items, {})), + selectedIndex(convertRawProp( + rawProps, + "selectedIndex", + sourceProps.selectedIndex, + {0})), + // TODO (T75217510) - This doesn't build, need to inherit from + // BaseTextProps? style(convertRawProp(rawProps, "style", + // sourceProps.style, {})), + testID(convertRawProp(rawProps, "testID", sourceProps.testID, {})), + accessibilityLabel(convertRawProp( + rawProps, + "accessibilityLabel", + sourceProps.accessibilityLabel, + {})){ + + }; + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/react/renderer/components/picker/iospicker/PickerProps.h b/ReactCommon/react/renderer/components/picker/iospicker/PickerProps.h new file mode 100644 index 00000000000..428f9b531be --- /dev/null +++ b/ReactCommon/react/renderer/components/picker/iospicker/PickerProps.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include + +#include + +namespace facebook { +namespace react { + +class PickerProps final : public ViewProps { + public: + PickerProps() = default; + PickerProps(PickerProps const &sourceProps, RawProps const &rawProps); + +#pragma mark - Props + + std::vector items{}; + int selectedIndex{0}; + TextAttributes style{}; + std::string const testID{}; + std::string const accessibilityLabel{}; +}; + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/react/renderer/components/picker/iospicker/PickerShadowNode.cpp b/ReactCommon/react/renderer/components/picker/iospicker/PickerShadowNode.cpp new file mode 100644 index 00000000000..2d39cdb106b --- /dev/null +++ b/ReactCommon/react/renderer/components/picker/iospicker/PickerShadowNode.cpp @@ -0,0 +1,16 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "PickerShadowNode.h" + +namespace facebook { +namespace react { + +extern const char PickerComponentName[] = "Picker"; + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/react/renderer/components/picker/iospicker/PickerShadowNode.h b/ReactCommon/react/renderer/components/picker/iospicker/PickerShadowNode.h new file mode 100644 index 00000000000..c2c80bc6ae2 --- /dev/null +++ b/ReactCommon/react/renderer/components/picker/iospicker/PickerShadowNode.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include +#include + +namespace facebook { +namespace react { + +extern const char PickerComponentName[]; + +/* + * `ShadowNode` for component. + */ +class PickerShadowNode final : public ConcreteViewShadowNode< + PickerComponentName, + PickerProps, + PickerEventEmitter, + PickerState> { + public: + using ConcreteViewShadowNode::ConcreteViewShadowNode; +}; + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/react/renderer/components/picker/iospicker/PickerState.h b/ReactCommon/react/renderer/components/picker/iospicker/PickerState.h new file mode 100644 index 00000000000..fa50b38dfdb --- /dev/null +++ b/ReactCommon/react/renderer/components/picker/iospicker/PickerState.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +namespace facebook { +namespace react { + +/* + * State for component. + */ +class PickerState final {}; + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/react/renderer/components/picker/iospicker/conversions.h b/ReactCommon/react/renderer/components/picker/iospicker/conversions.h new file mode 100644 index 00000000000..b52037bcb88 --- /dev/null +++ b/ReactCommon/react/renderer/components/picker/iospicker/conversions.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +#include + +namespace facebook { +namespace react { + +inline void fromRawValue( + const RawValue &value, + std::vector &items) { + auto array = (folly::dynamic)value; + for (auto itr = array.begin(); itr != array.end(); ++itr) { + // TODO (T75217510) - Use the itr to create the item instead of using these + // dummy values. + struct PickerItemsStruct item = { + .label = "LOL", .value = "LOL2", .textColor = 0}; + items.push_back(item); + } +} + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/react/renderer/components/picker/iospicker/primitives.h b/ReactCommon/react/renderer/components/picker/iospicker/primitives.h new file mode 100644 index 00000000000..d9804aa9886 --- /dev/null +++ b/ReactCommon/react/renderer/components/picker/iospicker/primitives.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +#include + +namespace facebook { +namespace react { + +struct PickerItemsStruct { + std::string label; + std::string value; + SharedColor textColor; + + bool operator==(const PickerItemsStruct &rhs) const { + return ( + label == rhs.label && value == rhs.value && textColor == rhs.textColor); + } +}; + +} // namespace react +} // namespace facebook