/* * Copyright (c) 2018-present, Facebook, Inc. * * This source code is licensed under the MIT license found in the LICENSE * file in the root directory of this source tree. * */ #if FB_SONARKIT_ENABLED #import "SKComponentLayoutDescriptor.h" #import #import #import #import #import #import #import #import #import #import #import #import #import "SKSubDescriptor.h" #import "SKComponentLayoutWrapper.h" #import "CKComponent+Sonar.h" #import "Utils.h" @implementation SKComponentLayoutDescriptor { NSDictionary *CKFlexboxAlignSelfEnumMap; NSDictionary *CKFlexboxPositionTypeEnumMap; NSArray*_registeredSubdescriptors; } - (void)setUp { [super setUp]; if (!_registeredSubdescriptors) { _registeredSubdescriptors = [NSArray new]; } static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ [self initEnumMaps]; }); } - (void)initEnumMaps { CKFlexboxAlignSelfEnumMap = @{ @(CKFlexboxAlignSelfAuto): @"auto", @(CKFlexboxAlignSelfStart): @"start", @(CKFlexboxAlignSelfEnd): @"end", @(CKFlexboxAlignSelfCenter): @"center", @(CKFlexboxAlignSelfBaseline): @"baseline", @(CKFlexboxAlignSelfStretch): @"stretch", }; CKFlexboxPositionTypeEnumMap = @{ @(CKFlexboxPositionTypeRelative): @"relative", @(CKFlexboxPositionTypeAbsolute): @"absolute", }; } - (NSString *)identifierForNode:(SKComponentLayoutWrapper *)node { return node.identifier; } - (NSString *)nameForNode:(SKComponentLayoutWrapper *)node { return [node.component sonar_getName]; } - (NSString *)decorationForNode:(SKComponentLayoutWrapper *)node { return [node.component sonar_getDecoration]; } - (NSUInteger)childCountForNode:(SKComponentLayoutWrapper *)node { NSUInteger count = node.children.size(); if (count == 0) { count = node.component.viewContext.view ? 1 : 0; } return count; } - (id)childForNode:(SKComponentLayoutWrapper *)node atIndex:(NSUInteger)index { if (node.children.size() == 0) { return node.component.viewContext.view; } return node.children[index]; } - (NSArray *> *> *)dataForNode:(SKComponentLayoutWrapper *)node { NSMutableArray *> *> *data = [NSMutableArray new]; if (node.isFlexboxChild) { [data addObject: [SKNamed newWithName:@"Layout" withValue:[self propsForFlexboxChild:node.flexboxChild]]]; } NSMutableDictionary *extraData = [[NSMutableDictionary alloc] init]; for (SKSubDescriptor *s in _registeredSubdescriptors) { [extraData setObject:[s getDataForNode:node] forKey:[s getName]]; } if (extraData.count > 0) { [data addObject: [SKNamed newWithName:@"Extra Sections" withValue:extraData]]; } [data addObjectsFromArray:[node.component sonar_getData]]; return data; } - (void)addSubDescriptors:(nonnull NSArray*)subDescriptors{ _registeredSubdescriptors = subDescriptors; } - (NSDictionary *)propsForFlexboxChild:(CKFlexboxComponentChild)child { return @{ @"spacingBefore": SKObject(@(child.spacingBefore)), @"spacingAfter": SKObject(@(child.spacingAfter)), @"flexGrow": SKObject(@(child.flexGrow)), @"flexShrink": SKObject(@(child.flexShrink)), @"zIndex": SKObject(@(child.zIndex)), @"useTextRounding": SKObject(@(child.useTextRounding)), @"margin": flexboxRect(child.margin), @"flexBasis": relativeDimension(child.flexBasis), @"padding": flexboxRect(child.padding), @"alignSelf": CKFlexboxAlignSelfEnumMap[@(child.alignSelf)], @"position": @{ @"type": CKFlexboxPositionTypeEnumMap[@(child.position.type)], @"start": relativeDimension(child.position.start), @"top": relativeDimension(child.position.top), @"end": relativeDimension(child.position.end), @"bottom": relativeDimension(child.position.bottom), @"left": relativeDimension(child.position.left), @"right": relativeDimension(child.position.right), }, @"aspectRatio": @(child.aspectRatio.aspectRatio()), }; } - (NSDictionary *)dataMutationsForNode:(SKComponentLayoutWrapper *)node { return [node.component sonar_getDataMutations]; } - (NSArray *> *)attributesForNode:(SKComponentLayoutWrapper *)node { NSMutableArray *> *attributes = [NSMutableArray array]; [attributes addObject:[SKNamed newWithName:@"responder" withValue:SKObject(NSStringFromClass([node.component.nextResponder class]))]]; return attributes; } - (void)setHighlighted:(BOOL)highlighted forNode:(SKComponentLayoutWrapper *)node { SKHighlightOverlay *overlay = [SKHighlightOverlay sharedInstance]; if (highlighted) { CKComponentViewContext viewContext = node.component.viewContext; [overlay mountInView: viewContext.view withFrame: viewContext.frame]; } else { [overlay unmount]; } } - (void)hitTest:(SKTouch *)touch forNode:(SKComponentLayoutWrapper *)node { if (node.children.size() == 0) { UIView *componentView = node.component.viewContext.view; if (componentView != nil) { if ([touch containedIn: componentView.bounds]) { [touch continueWithChildIndex: 0 withOffset: componentView.bounds.origin]; return; } } } NSInteger index = 0; for (index = node.children.size() - 1; index >= 0; index--) { const auto child = node.children[index]; CGRect frame = { .origin = child.position, .size = child.size }; if ([touch containedIn: frame]) { [touch continueWithChildIndex: index withOffset: child.position]; return; } } [touch finish]; } @end #endif