Initial commit for Fabric Picker

Summary:
This is a starting point for the handwritten Fabric Picker component. It is incomplete, and needs to be landed with the rest of the stack above it.

In general, this creates a new `ComponentView`, `ComponentDescriptor`, `ShadowNode`, `Props` and a few other boilerplate classes for Picker. A bunch of the logic in `ComponentView` was copied over from the Paper `RCTPicker` and `RCTPickerManager`.

What works in this diff:
- A `<Picker>` with items can be created in JS, and a corresponding `UIPicker` is created in native with placeholder text, default styling and the correct amount of items

What doesn't work yet (implemented in later diffs):
- Parsing items to use correct text and styling in native
- Events/commands

Reviewed By: sammy-SC

Differential Revision: D23941821

fbshipit-source-id: e049ca6004757fbd1361985644d5dbb8f53e1ce6
This commit is contained in:
Peter Argany
2020-10-13 11:19:29 -07:00
committed by Facebook GitHub Bot
parent 71bb19827b
commit 38cb06cbd3
15 changed files with 506 additions and 2 deletions
@@ -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 <React/RCTViewComponentView.h>
NS_ASSUME_NONNULL_BEGIN
/**
* UIView class for root <Picker> component.
*/
@interface RCTPickerComponentView : RCTViewComponentView
@end
NS_ASSUME_NONNULL_END
@@ -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 <React/RCTConvert.h>
#import <UIKit/UIKit.h>
#import <react/renderer/components/iospicker/PickerComponentDescriptor.h>
#import <react/renderer/components/iospicker/PickerProps.h>
#import "FBRCTFabricComponentsPlugins.h"
#import "RCTConversions.h"
using namespace facebook::react;
@interface RCTPickerComponentView () <UIPickerViewAccessibilityDelegate, UIPickerViewDelegate, UIPickerViewDataSource>
@end
@implementation RCTPickerComponentView {
UIPickerView *_pickerView;
UIColor *_textColor;
UIFont *_font;
NSTextAlignment _textAlignment;
std::vector<PickerItemsStruct> _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<const PickerProps>();
_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<PickerComponentDescriptor>();
}
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
{
const auto &oldPickerProps = *std::static_pointer_cast<const PickerProps>(_props);
const auto &newPickerProps = *std::static_pointer_cast<const PickerProps>(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<RCTComponentViewProtocol> RCTPickerCls(void)
{
return RCTPickerComponentView.class;
}
@@ -35,6 +35,7 @@ Class<RCTComponentViewProtocol> RCTPullToRefreshViewCls(void) __attribute__((use
Class<RCTComponentViewProtocol> RCTActivityIndicatorViewCls(void) __attribute__((used));
Class<RCTComponentViewProtocol> RCTSliderCls(void) __attribute__((used));
Class<RCTComponentViewProtocol> RCTSwitchCls(void) __attribute__((used));
Class<RCTComponentViewProtocol> RCTPickerCls(void) __attribute__((used));
Class<RCTComponentViewProtocol> RCTUnimplementedNativeViewCls(void) __attribute__((used));
Class<RCTComponentViewProtocol> RCTParagraphCls(void) __attribute__((used));
Class<RCTComponentViewProtocol> RCTTextInputCls(void) __attribute__((used));
@@ -24,6 +24,7 @@ Class<RCTComponentViewProtocol> RCTFabricComponentsProvider(const char *name) {
{"ActivityIndicatorView", RCTActivityIndicatorViewCls},
{"Slider", RCTSliderCls},
{"Switch", RCTSwitchCls},
{"Picker", RCTPickerCls},
{"UnimplementedNativeView", RCTUnimplementedNativeViewCls},
{"Paragraph", RCTParagraphCls},
{"TextInput", RCTTextInputCls},
@@ -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 = "",
@@ -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"),
],
)
@@ -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 <react/renderer/components/iospicker/PickerShadowNode.h>
#include <react/renderer/core/ConcreteComponentDescriptor.h>
/*
* Descriptor for <Picker> component.
*/
namespace facebook {
namespace react {
class PickerComponentDescriptor final
: public ConcreteComponentDescriptor<PickerShadowNode> {
public:
using ConcreteComponentDescriptor::ConcreteComponentDescriptor;
};
} // namespace react
} // namespace facebook
@@ -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 <react/renderer/components/view/ViewEventEmitter.h>
namespace facebook {
namespace react {
class PickerEventEmitter : public ViewEventEmitter {
public:
using ViewEventEmitter::ViewEventEmitter;
};
} // namespace react
} // namespace facebook
@@ -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 <react/renderer/attributedstring/conversions.h>
#include <react/renderer/components/iospicker/conversions.h>
#include <react/renderer/core/propsConversions.h>
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
@@ -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 <react/renderer/components/iospicker/primitives.h>
#include <react/renderer/components/text/BaseTextProps.h>
#include <react/renderer/components/view/ViewProps.h>
#include <vector>
namespace facebook {
namespace react {
class PickerProps final : public ViewProps {
public:
PickerProps() = default;
PickerProps(PickerProps const &sourceProps, RawProps const &rawProps);
#pragma mark - Props
std::vector<PickerItemsStruct> items{};
int selectedIndex{0};
TextAttributes style{};
std::string const testID{};
std::string const accessibilityLabel{};
};
} // namespace react
} // namespace facebook
@@ -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
@@ -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 <react/renderer/components/iospicker/PickerEventEmitter.h>
#include <react/renderer/components/iospicker/PickerProps.h>
#include <react/renderer/components/iospicker/PickerState.h>
#include <react/renderer/components/view/ConcreteViewShadowNode.h>
namespace facebook {
namespace react {
extern const char PickerComponentName[];
/*
* `ShadowNode` for <Picker> component.
*/
class PickerShadowNode final : public ConcreteViewShadowNode<
PickerComponentName,
PickerProps,
PickerEventEmitter,
PickerState> {
public:
using ConcreteViewShadowNode::ConcreteViewShadowNode;
};
} // namespace react
} // namespace facebook
@@ -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 <Picker> component.
*/
class PickerState final {};
} // namespace react
} // namespace facebook
@@ -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 <react/renderer/components/iospicker/primitives.h>
#include <vector>
namespace facebook {
namespace react {
inline void fromRawValue(
const RawValue &value,
std::vector<PickerItemsStruct> &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
@@ -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 <react/renderer/graphics/Color.h>
#include <string>
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