Experimental iOS Component "Avatar" (#482)

* initial commit

* AvatarData also wrapped

* Change files

* Used Memo Cache to map JS API to Native API

* Update from PR comments

* Address PR Comments

* remove experimental avatar from core

* Remove commented code, fix dependencies

* Remove dependency from fluent-tester-ios

* Change include path

* Updated fluent tester package.json

* remove extra tab space
This commit is contained in:
Saad Najmi 2020-10-19 11:03:07 -07:00 коммит произвёл GitHub
Родитель b951659d25
Коммит 6a48956ff1
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
26 изменённых файлов: 486 добавлений и 8 удалений

4
.vscode/settings.json поставляемый
Просмотреть файл

@ -63,7 +63,11 @@
"editor.formatOnSave": false
},
"cSpell.words": [
"TESTPAGE",
"beachball",
"consts",
"fluentui",
"macos",
"nuget"
]
}

Просмотреть файл

@ -24,6 +24,7 @@
"@fluentui-react-native/framework": ">=0.3.8 <1.0.0",
"@fluentui-react-native/stack": ">=0.2.18 <1.0.0",
"@fluentui-react-native/interactive-hooks": ">=0.6.0 <1.0.0",
"@fluentui-react-native/experimental-avatar": ">= 0.1.0 < 1.0.0",
"@fluentui-react-native/experimental-button": "0.2.0",
"@uifabricshared/theming-react-native": ">=0.9.0 <1.0.0",
"@uifabricshared/theme-registry": ">=0.3.71 <1.0.0",

Просмотреть файл

@ -0,0 +1,61 @@
import * as React from 'react';
import { Avatar } from '@fluentui-react-native/experimental-avatar';
import { Stack } from '@fluentui-react-native/stack';
import { stackStyle } from '../Common/styles';
import { AVATAR_TESTPAGE } from './consts';
import { Test, TestSection, PlatformStatus } from '../Test';
const avatar: React.FunctionComponent<{}> = () => {
return (
<Stack style={stackStyle}>
<Avatar primaryText="John Smith" />
<Avatar primaryText="John Smith" avatarStyle="square" />
<Avatar primaryText="John Smith" presence="available" />
<Avatar primaryText="John Smith" secondaryText="johnsmith@microsoft.com" presence="outOfOffice" />
<Avatar secondaryText="johnsmith@microsoft.com" presence="outOfOffice" />
</Stack>
);
};
const stylizedAvatar: React.FunctionComponent<{}> = () => {
const CustomizedAvatar = Avatar.customize({
size: 'large',
});
return (
<Stack style={stackStyle}>
<CustomizedAvatar primaryText="John Smith" />
<CustomizedAvatar primaryText="John Smith" avatarStyle="square" />
<CustomizedAvatar primaryText="John Smith" presence="available" />
<CustomizedAvatar primaryText="John Smith" secondaryText="johnsmith@microsoft.com" presence="outOfOffice" />
<CustomizedAvatar secondaryText="johnsmith@microsoft.com" presence="outOfOffice" />
</Stack>
);
};
const avatarSections: TestSection[] = [
{
name: 'Basic Avatar',
testID: AVATAR_TESTPAGE,
component: avatar,
},
{
name: 'Stylized Avatar',
component: stylizedAvatar,
},
];
export const AvatarTest: React.FunctionComponent<{}> = () => {
const status: PlatformStatus = {
win32Status: 'Backlog',
uwpStatus: 'Backlog',
iosStatus: 'Beta',
macosStatus: 'Backlog',
androidStatus: 'Backlog',
};
const description =
'AvatarView is a visual representation of a user, entity, or group. If an image is supplied, it is cropped to a circle of the requested size. If an image is not supplied, initials are extracted from the given name and email address provided and displayed on a colorful background.';
return <Test name="Avatar Test" description={description} sections={avatarSections} status={status}></Test>;
};

Просмотреть файл

@ -0,0 +1,2 @@
export const HOMEPAGE_AVATAR_BUTTON = 'Homepage_Avatar_Button';
export const AVATAR_TESTPAGE = 'Avatar_TestPage';

Просмотреть файл

@ -0,0 +1,2 @@
export * from './AvatarTest';
export * from './consts';

Просмотреть файл

@ -1,4 +1,5 @@
import { TestDescription } from './TestComponents';
import { AvatarTest, HOMEPAGE_AVATAR_BUTTON } from './TestComponents/Avatar';
import { ButtonTest, HOMEPAGE_BUTTON_BUTTON } from './TestComponents/Button';
import { CalloutTest, HOMEPAGE_CALLOUT_BUTTON } from './TestComponents/Callout';
import { CheckboxTest, HOMEPAGE_CHECKBOX_BUTTON } from './TestComponents/Checkbox';
@ -16,6 +17,11 @@ import { HOMEPAGE_TEXT_BUTTON, TextTest } from './TestComponents/Text';
import { HOMEPAGE_THEME_BUTTON, ThemeTest } from './TestComponents/Theme';
export const tests: TestDescription[] = [
{
name: 'Avatar Test',
component: AvatarTest,
testPage: HOMEPAGE_AVATAR_BUTTON,
},
{
name: 'Button Test',
component: ButtonTest,

Просмотреть файл

@ -1,4 +1,5 @@
import { TestDescription } from './TestComponents';
import { AvatarTest, HOMEPAGE_AVATAR_BUTTON } from './TestComponents/Avatar';
import { ButtonTest, HOMEPAGE_BUTTON_BUTTON } from './TestComponents/Button';
import { CheckboxTest, HOMEPAGE_CHECKBOX_BUTTON } from './TestComponents/Checkbox';
import { FocusTrapTest, HOMEPAGE_FOCUSTRAPZONE_BUTTON } from './TestComponents/FocusTrapZone';
@ -16,6 +17,11 @@ import { HOMEPAGE_BUTTON_BUTTONEXPERIMENTAL, ExperimentalButtonTest } from './Te
import { HOMEPAGE_FOCUSZONE_BUTTON, FocusZoneTest } from './TestComponents/FocusZone';
export const tests: TestDescription[] = [
{
name: 'Avatar Test',
component: AvatarTest,
testPage: HOMEPAGE_AVATAR_BUTTON,
},
{
name: 'Button Test',
component: ButtonTest,

Просмотреть файл

@ -27,7 +27,7 @@ fi
use_flipper!(false)
use_test_app! do |target|
target.app do
pod 'FluentUI-React-Native-Avatar', :path => '../../../packages/experimental/Avatar/FluentUIReactNativeAvatar.podspec'
pod 'FluentUI-React-Native-Shimmer', :path => '../../../packages/components/Shimmer/FluentUIReactNativeShimmer.podspec'
script_phase name: 'Start Packager',

Просмотреть файл

@ -9,7 +9,10 @@ PODS:
- React-Core (= 0.62.2)
- React-jsi (= 0.62.2)
- ReactCommon/turbomodule/core (= 0.62.2)
- FluentUI-React-Native-Shimmer (0.2.9):
- FluentUI-React-Native-Avatar (0.1.0):
- MicrosoftFluentUI (~> 0.1.12)
- React
- FluentUI-React-Native-Shimmer (0.2.10):
- MicrosoftFluentUI (~> 0.1.12)
- React
- Folly (2018.10.22.00):
@ -22,7 +25,7 @@ PODS:
- DoubleConversion
- glog
- glog (0.3.5)
- MicrosoftFluentUI (0.1.12)
- MicrosoftFluentUI (0.1.13)
- QRCodeReader.swift (10.1.0)
- RCTRequired (0.62.2)
- RCTTypeSafety (0.62.2):
@ -248,13 +251,14 @@ PODS:
- ReactCommon/callinvoker (= 0.62.2)
- ReactTestApp-DevSupport (0.2.9)
- ReactTestApp-Resources (1.0.0-dev)
- SwiftLint (0.40.1)
- SwiftLint (0.40.3)
- Yoga (1.14.0)
DEPENDENCIES:
- DoubleConversion (from `../../../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
- FBLazyVector (from `../../../node_modules/react-native/Libraries/FBLazyVector`)
- FBReactNativeSpec (from `../../../node_modules/react-native/Libraries/FBReactNativeSpec`)
- FluentUI-React-Native-Avatar (from `../../../packages/experimental/Avatar/FluentUIReactNativeAvatar.podspec`)
- FluentUI-React-Native-Shimmer (from `../../../packages/components/Shimmer/FluentUIReactNativeShimmer.podspec`)
- Folly (from `../../../node_modules/react-native/third-party-podspecs/Folly.podspec`)
- glog (from `../../../node_modules/react-native/third-party-podspecs/glog.podspec`)
@ -300,6 +304,8 @@ EXTERNAL SOURCES:
:path: "../../../node_modules/react-native/Libraries/FBLazyVector"
FBReactNativeSpec:
:path: "../../../node_modules/react-native/Libraries/FBReactNativeSpec"
FluentUI-React-Native-Avatar:
:path: "../../../packages/experimental/Avatar/FluentUIReactNativeAvatar.podspec"
FluentUI-React-Native-Shimmer:
:path: "../../../packages/components/Shimmer/FluentUIReactNativeShimmer.podspec"
Folly:
@ -356,10 +362,11 @@ SPEC CHECKSUMS:
DoubleConversion: 5805e889d232975c086db112ece9ed034df7a0b2
FBLazyVector: 4aab18c93cd9546e4bfed752b4084585eca8b245
FBReactNativeSpec: 5465d51ccfeecb7faa12f9ae0024f2044ce4044e
FluentUI-React-Native-Shimmer: 74b1e66f2672d4351b16ec36904f20fb1b41713c
FluentUI-React-Native-Avatar: 4043d10df5dad8107a2a71f9ea627fd62811ff34
FluentUI-React-Native-Shimmer: a89d87c12baec72b263153f01f2eb61c30850f6f
Folly: 30e7936e1c45c08d884aa59369ed951a8e68cf51
glog: 1f3da668190260b06b429bb211bfbee5cd790c28
MicrosoftFluentUI: 6800fd4c017a5c5b946a24851ac2218748da45d2
MicrosoftFluentUI: dbebf152cd2e7864ace2d5079d509ec1fee19cbf
QRCodeReader.swift: 373a389fe9a22d513c879a32a6f647c58f4ef572
RCTRequired: cec6a34b3ac8a9915c37e7e4ad3aa74726ce4035
RCTTypeSafety: 93006131180074cffa227a1075802c89a49dd4ce
@ -382,9 +389,9 @@ SPEC CHECKSUMS:
ReactCommon: ed4e11d27609d571e7eee8b65548efc191116eb3
ReactTestApp-DevSupport: 9a0663d12f90115e646ddbf070566c646a7b9fd1
ReactTestApp-Resources: 5950ae44720217c6778ff03fb1d906c8fb3ce483
SwiftLint: bbfed21bf4451dcbd0f5cdbee44a18e06cf91b12
SwiftLint: dfd554ff0dff17288ee574814ccdd5cea85d76f7
Yoga: 3ebccbdd559724312790e7742142d062476b698e
PODFILE CHECKSUM: 42dde6a3630fe1498f92905782bda6387e25596e
PODFILE CHECKSUM: 92b58eb5d35bda5732fd91cad06be75b94e8f6b1
COCOAPODS: 1.9.3

Просмотреть файл

@ -0,0 +1,8 @@
{
"type": "minor",
"comment": "New experimental component avatar",
"packageName": "@fluentui/react-native",
"email": "saadnajmi2@gmail.com",
"dependentChangeType": "patch",
"date": "2020-09-24T08:29:24.155Z"
}

Просмотреть файл

@ -0,0 +1,8 @@
{
"type": "minor",
"comment": "New experimental component avatar",
"packageName": "@fluentui-react-native/experimental-avatar",
"email": "saadnajmi2@gmail.com",
"dependentChangeType": "patch",
"date": "2020-09-24T08:29:18.037Z"
}

Просмотреть файл

@ -0,0 +1,8 @@
{
"type": "minor",
"comment": "New experimental component avatar",
"packageName": "@fluentui-react-native/tester",
"email": "saadnajmi2@gmail.com",
"dependentChangeType": "patch",
"date": "2020-09-24T08:28:56.757Z"
}

Просмотреть файл

Просмотреть файл

Просмотреть файл

@ -0,0 +1,21 @@
require 'json'
package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
version = package['version']
Pod::Spec.new do |s|
s.name = 'FluentUI-React-Native-Avatar'
s.version = "#{version}"
s.summary = 'Fluent UI React Native Avatar Control'
s.homepage = "https://www.microsoft.com/design/fluent/#/"
s.author = { "Microsoft" => "fluentuinativeowners@microsoft.com"}
s.source = { :git => "https://github.com/microsoft/fluentui-react-native.git", :tag => "#{s.version}" }
s.swift_version = "5"
s.module_name = 'FluentUIReactNativeAvatar'
s.ios.deployment_target = "11.0"
s.ios.source_files = "ios/*.{swift,h,m}"
s.dependency 'React'
s.dependency 'MicrosoftFluentUI', '~> 0.1.12'
end

Просмотреть файл

@ -0,0 +1,3 @@
{
"extends": "@uifabricshared/build-native/api-extractor.json"
}

Просмотреть файл

@ -0,0 +1 @@
module.exports = require('@uifabricshared/build-native/babel.config');

Просмотреть файл

@ -0,0 +1 @@
#import <React/RCTViewManager.h>

Просмотреть файл

@ -0,0 +1,27 @@
import Foundation
import FluentUI
@objc(MSFAvatarViewManager)
class AvatarViewManager: RCTViewManager {
override func view()->UIView! {
let avatarView = AvatarView(avatarSize: .small)
return avatarView
}
override class func requiresMainQueueSetup() -> Bool {
return true
}
override func constantsToExport() -> [AnyHashable : Any]! {
return [
"sizes" : [
"xSmall" : AvatarSize.extraSmall.size.width,
"small" : AvatarSize.small.size.width,
"medium" : AvatarSize.medium.size.width,
"large" : AvatarSize.large.size.width,
"xLarge" : AvatarSize.extraLarge.size.width,
"xxLarge" : AvatarSize.extraExtraLarge.size.width
]
]
}
}

Просмотреть файл

@ -0,0 +1,76 @@
#import <React/RCTComponent.h>
#import <React/RCTViewManager.h>
@import FluentUI;
@implementation RCTConvert (MSFAvatarViewAdditions)
RCT_ENUM_CONVERTER(MSFAvatarSize, (@{
@"xSmall": @(MSFAvatarSizeExtraSmall),
@"small": @(MSFAvatarSizeSmall),
@"medium": @(MSFAvatarSizeMedium),
@"large": @(MSFAvatarSizeLarge),
@"xLarge": @(MSFAvatarSizeExtraLarge),
@"xxLarge": @(MSFAvatarSizeExtraExtraLarge),
}), MSFAvatarSizeSmall, integerValue);
RCT_ENUM_CONVERTER(MSFAvatarStyle, (@{
@"circle": @(MSFAvatarStyleCircle),
@"square": @(MSFAvatarStyleSquare),
}), MSFAvatarStyleCircle, integerValue);
RCT_ENUM_CONVERTER(MSFAvatarFallbackImageStyle, (@{
@"onAccentFilled": @(MSFAvatarFallbackImageStyleOnAccentFilled),
@"outlined": @(MSFAvatarFallbackImageStyleOutlined),
@"primaryFilled": @(MSFAvatarFallbackImageStylePrimaryFilled),
@"primaryOutlined": @(MSFAvatarFallbackImageStylePrimaryOutlined),
}), MSFAvatarFallbackImageStyleOutlined, integerValue);
RCT_ENUM_CONVERTER(MSFPresence, (@{
@"none": @(MSFPresenceNone),
@"available": @(MSFPresenceAvailable),
@"away": @(MSFPresenceAway),
@"busy": @(MSFPresenceBusy),
@"doNotDisturb": @(MSFPresenceDoNotDisturb),
@"outOfOffice": @(MSFPresenceOutOfOffice),
@"offline": @(MSFPresenceOffline),
@"unknown": @(MSFPresenceUnknown),
@"blocked": @(MSFPresenceBlocked),
}), MSFPresenceNone, integerValue);
+ (MSFAvatarData *)MSFAvatarData:(id)json
{
return [[MSFAvatarData alloc]initWithPrimaryText:[RCTConvert NSString:json[@"primaryText"]]
secondaryText:[RCTConvert NSString:json[@"secondaryText"]]
image:[RCTConvert UIImage:json[@"image"]]
presence:[RCTConvert MSFPresence:json[@"presence"]]
color:[RCTConvert UIColor:json[@"color"]]];
}
@end
@interface RCT_EXTERN_MODULE(MSFAvatarViewManager, RCTViewManager)
RCT_EXPORT_VIEW_PROPERTY(avatarSize, MSFAvatarSize);
RCT_EXPORT_VIEW_PROPERTY(avatarBackgroundColor, UIColor);
RCT_EXPORT_VIEW_PROPERTY(customBorderImage, UIImage);
RCT_REMAP_VIEW_PROPERTY(avatarStyle, style, MSFAvatarStyle)
RCT_EXPORT_VIEW_PROPERTY(borderColor, UIColor);
RCT_EXPORT_VIEW_PROPERTY(presence, MSFPresence);
RCT_EXPORT_VIEW_PROPERTY(useOpaquePresenceBorder, bool);
RCT_REMAP_VIEW_PROPERTY(accessibilityLabel, overrideAccessibilityLabel, NSString);
RCT_EXPORT_VIEW_PROPERTY(preferredFallbackImageStyle, MSFAvatarFallbackImageStyle);
RCT_REMAP_VIEW_PROPERTY(hasPointerInteractionIOS, hasPointerInteraction, bool)
RCT_CUSTOM_VIEW_PROPERTY(avatarData, MSFAvatarData, MSFAvatarView)
{
MSFAvatarData *avatarData = [RCTConvert MSFAvatarData:json];
[view setupWithAvatar:avatarData];
}
@end

Просмотреть файл

@ -0,0 +1,3 @@
const { preset } = require('@uifabricshared/build-native');
preset();

Просмотреть файл

@ -0,0 +1,40 @@
{
"name": "@fluentui-react-native/experimental-avatar",
"version": "0.1.0",
"description": "An Avatar view",
"main": "src/index.ts",
"module": "src/index.ts",
"typings": "lib/index.d.ts",
"onPublish": {
"main": "lib-commonjs/index.js",
"module": "lib/index.js"
},
"scripts": {
"build": "fluentui-scripts build",
"just": "fluentui-scripts",
"clean": "fluentui-scripts clean",
"lint": "fluentui-scripts eslint",
"depcheck": "fluentui-scripts depcheck",
"test": "fluentui-scripts jest",
"update-snapshots": "fluentui-scripts jest -u",
"verify-api": "fluentui-scripts verify-api-extractor",
"update-api": "fluentui-scripts update-api-extractor"
},
"dependencies": {
"@fluentui-react-native/component-cache": "^1.0.3",
"@fluentui-react-native/framework": "0.3.0"
},
"devDependencies": {
"@types/react-native": "^0.62.0",
"@uifabricshared/build-native": "^0.1.1",
"@uifabricshared/eslint-config-rules": "^0.1.1",
"react-native": "0.62.2",
"react": "16.11.0"
},
"peerDependencies": {
"react-native": ">=0.60.0",
"react": "16.11.0"
},
"author": "",
"license": "MIT"
}

Просмотреть файл

@ -0,0 +1,170 @@
/** @jsx withSlots */
import { compose, UseSlots, buildProps, mergeProps, withSlots } from '@fluentui-react-native/framework';
import { Image, NativeModules, ViewProps } from 'react-native';
import { ensureNativeComponent } from '@fluentui-react-native/component-cache';
import { useMemo } from 'react';
const avatarName = 'Avatar';
const NativeAvatarView = ensureNativeComponent('MSFAvatarView');
export type Size = 'xSmall' | 'small' | 'medium' | 'large' | 'xLarge' | 'xxLarge';
export type AvatarStyle = 'circle' | 'square';
export type Presence = 'none' | 'available' | 'away' | 'doNotDisturb' | 'outOfOffice' | 'offline' | 'unknown' | 'unknown' | 'blocked';
interface ExportedConstants {
sizes: { [key in Size]: number };
}
const ExportedNativeConstants: ExportedConstants = NativeModules.MSFAvatarViewManager;
export type AvatarData = {
/**
* The primary text to create initials with (e.g. a name)
*/
primaryText?: string;
/**
* The secondary text to create initials with if primary text is not provided (e.g. an email address)
*/
secondaryText?: string;
/**
* The image to be displayed
*/
image?: Image;
/**
* The color that represents this avatar.
* This color will override the initials view's background color.
* If the avatar view is configured to display a border, this will be the border's color.
* The colored border will not be displayed if a custom border image is provided.
*/
color?: string;
/**
* Image to be used as border around the avatar. It will be used as a pattern image color,
* but It will be scaled to fit the avatar size. If set, the hasBorder initializer value will be ignored,
* since it's assumed that the client intends to have a custom border.
*/
customBorderImage?: Image;
/**
* The avatar view's presence state.
* The presence state is only shown when the style is set to AvatarStyle.circle.
*/
presence?: Presence;
};
export type otherAvatarProps = {
/**
* Supported Avatar sizes
*/
size?: Size;
/**
* Background Color of initials
*/
backgroundColor?: string;
/**
* Image to be used as border around the avatar. It will be used as a pattern image color,
* but It will be scaled to fit the avatar size. If set, the hasBorder initializer value will be ignored,
* since it's assumed that the client intends to have a custom border.
*/
customBorderImage?: Image;
/**
* Shape of the AvatarView
* Circle is used to represent people
* Square is used to represent organizations or teams
*/
avatarStyle?: AvatarStyle;
/**
* The color of the border of the avatar view
*/
borderColor?: string;
/**
* When true, the presence status border is opaque. Otherwise, it is transparent.
*/
usesOpaquePresenceBorder?: boolean;
/**
* Used when avatarView doesn't have image or can't generate initials string
*/
preferredFallBackImageStyle?: string;
/**
* Set to true to enable the iPadOS pointer interactions on the avatar view, false by default.
*/
hasPointerInteractionIOS?: boolean;
};
export type AvatarTokens = {
/**
* Supported Avatar sizes
*/
size?: Size;
};
const tokensThatAreAlsoProps: (keyof AvatarTokens)[] = ['size'];
// The Javascript API is a flat list for all the props we can set on our component
export type AvatarProps = ViewProps & otherAvatarProps & AvatarData;
// The Native component API has a sub object that we flattened out in AvatarProps
export type NativeAvatarViewProps = ViewProps &
otherAvatarProps & {
avatarData?: AvatarData;
};
interface AvatarType {
props: AvatarProps;
slotProps: { root: NativeAvatarViewProps };
tokens: AvatarTokens;
}
export const Avatar = compose<AvatarType>({
displayName: avatarName,
tokens: [
{
size: 'small',
},
avatarName,
],
tokensThatAreAlsoProps,
slots: { root: NativeAvatarView },
slotProps: {
root: buildProps<NativeAvatarViewProps, AvatarTokens>(
(tokens) => ({
size: tokens.size,
style: {
height: ExportedNativeConstants.sizes[tokens.size],
width: ExportedNativeConstants.sizes[tokens.size],
},
}),
['size'],
),
},
render: (props: AvatarProps, useSlots: UseSlots<AvatarType>) => {
const Root = useSlots(props).root;
const memoizedAvatarData = useMemo(
() => ({
primaryText: props.primaryText,
secondaryText: props.secondaryText,
image: props.image,
color: props.color,
customBorderImage: props.customBorderImage,
presence: props.presence,
}),
[props.primaryText, props.secondaryText, props.image, props.color, props.customBorderImage, props.presence],
);
return (rest: AvatarProps) => <Root {...mergeProps(props, rest)} avatarData={memoizedAvatarData} />;
},
});

Просмотреть файл

@ -0,0 +1 @@
export * from './Avatar';

Просмотреть файл

@ -0,0 +1,7 @@
{
"extends": "@uifabricshared/build-native/tsconfig.json",
"compilerOptions": {
"outDir": "lib"
},
"include": ["src"]
}

Просмотреть файл

@ -1184,6 +1184,21 @@
exec-sh "^0.3.2"
minimist "^1.2.0"
"@fluentui-react-native/framework@0.3.0":
version "0.3.0"
resolved "https://registry.yarnpkg.com/@fluentui-react-native/framework/-/framework-0.3.0.tgz#dff9a330136db795b67bef805f57c6bb72c251b3"
integrity sha512-BAKOf0lX241Zy5G4qrACXcBjgVIWEYWPaQXo+7p7Ausc0/OD9Q/t9K6ndu2MeQkDz8O6lObd6mNs3uXee6YOfA==
dependencies:
"@fluentui-react-native/composition" ">=0.3.6 <1.0.0"
"@fluentui-react-native/immutable-merge" "^1.0.1"
"@fluentui-react-native/memo-cache" "^1.0.2"
"@fluentui-react-native/merge-props" ">=0.1.1 <1.0.0"
"@fluentui-react-native/tokens" ">=0.5.6 <1.0.0"
"@fluentui-react-native/use-slots" ">=0.3.6 <1.0.0"
"@fluentui-react-native/use-styling" ">=0.3.0 <1.0.0"
"@uifabricshared/theming-ramp" ">=0.10.5 <1.0.0"
"@uifabricshared/theming-react-native" ">=0.7.76 <1.0.0"
"@hapi/address@2.x.x":
version "2.1.4"
resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5"