Fabric iOS: allow using fallback component for unsupported ones
Summary: This allows an unsupported component to be rendered as a "unimplemented view" for better visualization of which component is missing. It is off by default, but configurable in the component factory. For now, the layout simply follows regular <View />, which means the width/height etc is based on the react component styling. The side effect is that components with 0 height/width won't show up at all. Reviewed By: mdvacca Differential Revision: D14869656 fbshipit-source-id: f31e012fb7dc1c64fcc431ea5aa45079a23a618e
This commit is contained in:
Родитель
63343dad2e
Коммит
417adf526f
|
@ -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.
|
||||
*
|
||||
* @format
|
||||
* @flow
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const requireNativeComponent = require('requireNativeComponent');
|
||||
|
||||
import type {ViewProps} from 'ViewPropTypes';
|
||||
import type {ViewStyleProp} from 'StyleSheet';
|
||||
import type {NativeComponent} from 'ReactNative';
|
||||
|
||||
type NativeProps = $ReadOnly<{|
|
||||
...ViewProps,
|
||||
name?: ?string,
|
||||
style?: ?ViewStyleProp,
|
||||
|}>;
|
||||
|
||||
type UnimplementedViewNativeType = Class<NativeComponent<NativeProps>>;
|
||||
|
||||
module.exports = ((requireNativeComponent(
|
||||
'UnimplementedNativeView',
|
||||
): any): UnimplementedViewNativeType);
|
|
@ -0,0 +1,43 @@
|
|||
/**
|
||||
* 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.
|
||||
*
|
||||
* @format
|
||||
* @flow
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import type {SchemaType} from '../../../packages/react-native-codegen/src/CodegenSchema.js';
|
||||
|
||||
const UnimplementedNativeViewSchema: SchemaType = {
|
||||
modules: {
|
||||
UnimplementedNativeViewSchema: {
|
||||
components: {
|
||||
UnimplementedNativeView: {
|
||||
extendsProps: [
|
||||
{
|
||||
type: 'ReactNativeBuiltInType',
|
||||
knownTypeName: 'ReactNativeCoreViewProps',
|
||||
},
|
||||
],
|
||||
events: [],
|
||||
props: [
|
||||
{
|
||||
name: 'name',
|
||||
optional: true,
|
||||
typeAnnotation: {
|
||||
type: 'StringTypeAnnotation',
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = UnimplementedNativeViewSchema;
|
|
@ -0,0 +1,18 @@
|
|||
/**
|
||||
* 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 <UIKit/UIKit.h>
|
||||
|
||||
#import <React/RCTViewComponentView.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface RCTUnimplementedNativeComponentView : RCTViewComponentView
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,60 @@
|
|||
/**
|
||||
* 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 "RCTUnimplementedNativeComponentView.h"
|
||||
|
||||
#import <react/components/rncore/EventEmitters.h>
|
||||
#import <react/components/rncore/Props.h>
|
||||
#import <react/components/rncore/ShadowNodes.h>
|
||||
|
||||
using namespace facebook::react;
|
||||
|
||||
@implementation RCTUnimplementedNativeComponentView {
|
||||
UILabel *_label;
|
||||
}
|
||||
|
||||
#pragma mark - RCTComponentViewProtocol
|
||||
|
||||
+ (ComponentHandle)componentHandle
|
||||
{
|
||||
return UnimplementedNativeViewShadowNode::Handle();
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
{
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
static const auto defaultProps = std::make_shared<const UnimplementedNativeViewProps>();
|
||||
_props = defaultProps;
|
||||
|
||||
CGRect bounds = self.bounds;
|
||||
_label = [[UILabel alloc] initWithFrame:bounds];
|
||||
_label.backgroundColor = [UIColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:0.3];
|
||||
_label.layoutMargins = UIEdgeInsetsMake(12, 12, 12, 12);
|
||||
_label.lineBreakMode = NSLineBreakByWordWrapping;
|
||||
_label.numberOfLines = 0;
|
||||
_label.textAlignment = NSTextAlignmentCenter;
|
||||
_label.textColor = [UIColor whiteColor];
|
||||
|
||||
self.contentView = _label;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)updateProps:(SharedProps)props oldProps:(SharedProps)oldProps
|
||||
{
|
||||
const auto &oldViewProps = *std::static_pointer_cast<const UnimplementedNativeViewProps>(oldProps ?: _props);
|
||||
const auto &newViewProps = *std::static_pointer_cast<const UnimplementedNativeViewProps>(props);
|
||||
|
||||
[super updateProps:props oldProps:oldProps];
|
||||
|
||||
if (oldViewProps.name != newViewProps.name) {
|
||||
_label.text = [NSString stringWithFormat:@"'%s' is not Fabric compatible yet.", newViewProps.name.c_str()];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
|
@ -17,6 +17,7 @@
|
|||
#import "RCTScrollViewComponentView.h"
|
||||
#import "RCTSliderComponentView.h"
|
||||
#import "RCTSwitchComponentView.h"
|
||||
#import "RCTUnimplementedNativeComponentView.h"
|
||||
#import "RCTViewComponentView.h"
|
||||
|
||||
using namespace facebook::react;
|
||||
|
@ -39,6 +40,7 @@ using namespace facebook::react;
|
|||
[componentViewFactory registerComponentViewClass:[RCTActivityIndicatorViewComponentView class]];
|
||||
[componentViewFactory registerComponentViewClass:[RCTSliderComponentView class]];
|
||||
[componentViewFactory registerComponentViewClass:[RCTSwitchComponentView class]];
|
||||
[componentViewFactory registerComponentViewClass:[RCTUnimplementedNativeComponentView class]];
|
||||
|
||||
return componentViewFactory;
|
||||
}
|
||||
|
|
|
@ -92,9 +92,12 @@ const ComponentDescriptor &ComponentDescriptorRegistry::at(
|
|||
|
||||
auto it = _registryByName.find(unifiedComponentName);
|
||||
if (it == _registryByName.end()) {
|
||||
throw std::invalid_argument(
|
||||
("Unable to find componentDescriptor for " + unifiedComponentName)
|
||||
.c_str());
|
||||
if (_fallbackComponentDescriptor == nullptr) {
|
||||
throw std::invalid_argument(
|
||||
("Unable to find componentDescriptor for " + unifiedComponentName)
|
||||
.c_str());
|
||||
}
|
||||
return *_fallbackComponentDescriptor.get();
|
||||
}
|
||||
return *it->second;
|
||||
}
|
||||
|
@ -123,5 +126,16 @@ SharedShadowNode ComponentDescriptorRegistry::createNode(
|
|||
return shadowNode;
|
||||
}
|
||||
|
||||
void ComponentDescriptorRegistry::setFallbackComponentDescriptor(
|
||||
SharedComponentDescriptor descriptor) {
|
||||
_fallbackComponentDescriptor = descriptor;
|
||||
registerComponentDescriptor(descriptor);
|
||||
}
|
||||
|
||||
const SharedComponentDescriptor
|
||||
ComponentDescriptorRegistry::getFallbackComponentDescriptor() const {
|
||||
return _fallbackComponentDescriptor;
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
|
|
|
@ -39,10 +39,13 @@ class ComponentDescriptorRegistry {
|
|||
Tag rootTag,
|
||||
const folly::dynamic &props,
|
||||
const SharedEventTarget &eventTarget) const;
|
||||
void setFallbackComponentDescriptor(SharedComponentDescriptor descriptor);
|
||||
const SharedComponentDescriptor getFallbackComponentDescriptor() const;
|
||||
|
||||
private:
|
||||
better::map<ComponentHandle, SharedComponentDescriptor> _registryByHandle;
|
||||
better::map<ComponentName, SharedComponentDescriptor> _registryByName;
|
||||
SharedComponentDescriptor _fallbackComponentDescriptor;
|
||||
};
|
||||
|
||||
} // namespace react
|
||||
|
|
|
@ -18,13 +18,22 @@ SharedShadowNode UIManager::createNode(
|
|||
SystraceSection s("UIManager::createNode");
|
||||
|
||||
auto &componentDescriptor = componentDescriptorRegistry_->at(name);
|
||||
auto fallbackDescriptor =
|
||||
componentDescriptorRegistry_->getFallbackComponentDescriptor();
|
||||
|
||||
const auto &props = componentDescriptor.cloneProps(nullptr, rawProps);
|
||||
const auto &state = componentDescriptor.createInitialState(props);
|
||||
|
||||
auto shadowNode = componentDescriptor.createShadowNode({
|
||||
/* .tag = */ tag,
|
||||
/* .rootTag = */ surfaceId,
|
||||
/* .props = */ props,
|
||||
/* .props = */
|
||||
fallbackDescriptor != nullptr &&
|
||||
fallbackDescriptor->getComponentHandle() ==
|
||||
componentDescriptor.getComponentHandle()
|
||||
? componentDescriptor.cloneProps(
|
||||
nullptr, RawProps(folly::dynamic::object("name", name)))
|
||||
: props,
|
||||
/* .eventEmitter = */
|
||||
componentDescriptor.createEventEmitter(std::move(eventTarget), tag),
|
||||
/* .children = */ ShadowNodeFragment::childrenPlaceholder(),
|
||||
|
|
Загрузка…
Ссылка в новой задаче