Android Native Module Documentation and Drawer example (#1519)
* show drawer * add experimental-drawer ver to tester * Add drawer content to tester * update Drawer properties * remove unused imports * add documentation * Change files * remove unncessary change files" * clean up code * clean up tester code * Fix tests * Update dependencies * menu button fix * restore primary button for menuButton * restore fluent tester package.jsoin * fix import cycle * add drawer dep to fluent tester * temp fix e2e testing error for menuButton * Change files * remove unnecessary change filew * change menu button content macro for e2e testing to null temp * run prettier fix Co-authored-by: Saad Najmi <sanajmi@microsoft.com>
This commit is contained in:
Родитель
e017834d7f
Коммит
c85943cf17
|
@ -169,6 +169,49 @@ To add a native Windows module:
|
|||
- Run `yarn start`
|
||||
5. Run application
|
||||
|
||||
## Creating new native Android Components
|
||||
|
||||
This section is specifically focused on creating new components for Android platforms.
|
||||
|
||||
If you are creating a new component from scratch, you have the most leeway to design your API. In general, you will probably want to expose each property from the native Android control as either a token or prop (or both) to JS.
|
||||
|
||||
To add a new control to the FURN component library: [creating a new component](#creating-a-new-component)
|
||||
|
||||
To add a native module that wraps a FluentUI Android control:
|
||||
|
||||
1. Create the android/src/main/java/com/microsoft/fnandroid/<new-component> subdirectory in your components directory
|
||||
|
||||
2. Inside the new directory you just created, add the following files. In all of the newly created files, add your package name at the top of the file: package com.microsoft.fnandroid.(new-component)
|
||||
|
||||
a. **(new-component)ViewManager.kt**: This Kotlin file imports FluentUI Android, and creates a subclass of RCTViewManager to instantiate and return your FluentUI Android control.
|
||||
|
||||
- Implement the createViewInstance method
|
||||
|
||||
- Expose view property setters using @ReactProp (or @ReactPropGroup) It's important to note that in order for properties and methods to be available to React Native, they must add the @ReactMethod decorator to it's declaration.
|
||||
|
||||
b. **(new-component)Module.kt**: This file will contain your native module class. Your module class will extend the ReactContextBaseJavaModule
|
||||
|
||||
c. **(new-component)Package.kt**
|
||||
- Add the ViewManager in createViewManagers of the applications package
|
||||
- Add the Module in createNativeModules of the applications package
|
||||
|
||||
3. In directory android/src/main, add AndroidManifest.xml and add the package name
|
||||
|
||||
4. Autolink Native Module
|
||||
|
||||
a. Gradle Build Init plugin
|
||||
- Run gradle init inside android directory
|
||||
- Select type of project to generate: Basic
|
||||
- Select build script DSL: Groovy
|
||||
|
||||
b. Include dependencies for android build environment
|
||||
- Edit the generated build.gradle file (Example: packages/experimental/Drawer/android/build.gradle)
|
||||
- Add dependencies for kotlin, maven, react-native, etc
|
||||
- Add dependency for FluentUIAndroid
|
||||
|
||||
c. Add @fluentui-react-native/<new-component> package under "dependencies" and "depcheck"/"ignoreMatches" in apps/android/package.json in order for our Fluent Tester app to build and use your new Android component module
|
||||
|
||||
|
||||
## Creating a pull request
|
||||
|
||||
Thanks for your interest in contributing to the fluentui-react-native! We welcome all contributions. Here's information on how to prepare your change for a pull request.
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#Mon Mar 14 15:42:30 PDT 2022
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
"@fluentui-react-native/experimental-avatar": ">=0.14.9 <1.0.0",
|
||||
"@fluentui-react-native/experimental-button": "0.15.9",
|
||||
"@fluentui-react-native/experimental-checkbox": "0.10.6",
|
||||
"@fluentui-react-native/experimental-drawer": ">=0.0.19 < 1.0.0",
|
||||
"@fluentui-react-native/experimental-native-date-picker": ">=0.4.1 <1.0.0",
|
||||
"@fluentui-react-native/experimental-expander": "0.3.19",
|
||||
"@fluentui-react-native/experimental-menu-button": ">=0.3.21 <1.0.0",
|
||||
|
@ -112,4 +113,4 @@
|
|||
"svg"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,16 +1,37 @@
|
|||
import * as React from 'react';
|
||||
import { Text } from 'react-native';
|
||||
import { stackStyle } from '../Common/styles';
|
||||
import { View, Text } from 'react-native';
|
||||
import { Link } from '@fluentui/react-native';
|
||||
import { Button } from '@fluentui-react-native/experimental-button';
|
||||
import { Drawer } from '@fluentui-react-native/experimental-drawer';
|
||||
import { Stack } from '@fluentui-react-native/stack';
|
||||
import { Icon, SvgIconProps } from '@fluentui-react-native/icon';
|
||||
import { DRAWER_TESTPAGE } from './consts';
|
||||
import { Test, TestSection, PlatformStatus } from '../Test';
|
||||
import { NativeModules } from 'react-native';
|
||||
|
||||
const basicDrawer: React.FunctionComponent = () => {
|
||||
console.log(NativeModules.DrawerModule);
|
||||
import TestSvg from '../Icon/assets/test.svg';
|
||||
|
||||
const BasicDrawer: React.FunctionComponent = () => {
|
||||
const stdBtnRef = React.useRef(null);
|
||||
|
||||
const svgProps: SvgIconProps = {
|
||||
src: TestSvg,
|
||||
viewBox: '0 0 500 500',
|
||||
};
|
||||
|
||||
return (
|
||||
<Stack style={stackStyle} gap={5}>
|
||||
<Text>test to come</Text>
|
||||
<Drawer>
|
||||
<View>
|
||||
<Text>Press for Drawer</Text>
|
||||
</View>
|
||||
<View style={{ padding: 20 }}>
|
||||
<Text>This is content inside Drawer</Text>
|
||||
<Link url="https://www.bing.com/" content="Click to navigate." />
|
||||
<Icon svgSource={svgProps} width={100} height={100} color="blue" />
|
||||
<Button componentRef={stdBtnRef}>Press for Drawer</Button>
|
||||
</View>
|
||||
</Drawer>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
@ -19,7 +40,7 @@ const drawerSections: TestSection[] = [
|
|||
{
|
||||
name: 'Basic Drawer',
|
||||
testID: DRAWER_TESTPAGE,
|
||||
component: basicDrawer,
|
||||
component: BasicDrawer,
|
||||
},
|
||||
];
|
||||
|
||||
|
|
|
@ -8,4 +8,5 @@ export const MENU_ITEM_1_COMPONENT = 'Menu_Item_1_Component'; // The first menu
|
|||
|
||||
/* E2E Testing Menu_Button 2 */
|
||||
export const MENU_BUTTON_NO_A11Y_LABEL_COMPONENT = 'Menu_Button_No_A11y_label_Component';
|
||||
export const MENU_BUTTON_TEST_COMPONENT_LABEL = 'Test Menu_Button2 - No Accessibility Label'; // A component on each specific test page
|
||||
// export const MENU_BUTTON_TEST_COMPONENT_LABEL = 'Test Menu_Button2 - No Accessibility Label'; // A component on each specific test page
|
||||
export const MENU_BUTTON_TEST_COMPONENT_LABEL = null;
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"type": "patch",
|
||||
"comment": "temp fix e2e testing error for menuButton",
|
||||
"packageName": "@fluentui-react-native/button",
|
||||
"email": "email not defined",
|
||||
"dependentChangeType": "patch"
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"type": "minor",
|
||||
"comment": "add documentation",
|
||||
"packageName": "@fluentui-react-native/experimental-drawer",
|
||||
"email": "email not defined",
|
||||
"dependentChangeType": "patch"
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"type": "patch",
|
||||
"comment": "temp fix e2e testing error for menuButton",
|
||||
"packageName": "@fluentui-react-native/interactive-hooks",
|
||||
"email": "email not defined",
|
||||
"dependentChangeType": "patch"
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"type": "patch",
|
||||
"comment": "temp fix e2e testing error for menuButton",
|
||||
"packageName": "@fluentui-react-native/menu-button",
|
||||
"email": "email not defined",
|
||||
"dependentChangeType": "patch"
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"type": "patch",
|
||||
"comment": "temp fix e2e testing error for menuButton",
|
||||
"packageName": "@fluentui-react-native/tester",
|
||||
"email": "email not defined",
|
||||
"dependentChangeType": "patch"
|
||||
}
|
|
@ -39,9 +39,11 @@ export const Button = compose<IButtonType>({
|
|||
// create the merged slot props
|
||||
|
||||
const slotProps = mergeSettings<IButtonSlotProps>(styleProps, {
|
||||
root: {
|
||||
ref: buttonRef,
|
||||
},
|
||||
ripple: {
|
||||
...pressable.props,
|
||||
ref: buttonRef,
|
||||
accessibilityLabel: accessibilityLabel,
|
||||
accessibilityState: { disabled: state.info.disabled },
|
||||
focusable: !state.info.disabled,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/** @jsx withSlots */
|
||||
import React, { useRef, useState, useCallback } from 'react';
|
||||
import { PrimaryButton, Button } from '@fluentui-react-native/button';
|
||||
import { ButtonV1 as Button } from '@fluentui-react-native/button';
|
||||
import { ContextualMenu, ContextualMenuItem, SubmenuItem, Submenu } from '@fluentui-react-native/contextual-menu';
|
||||
import { IUseComposeStyling, compose } from '@uifabricshared/foundation-compose';
|
||||
import { mergeSettings } from '@uifabricshared/foundation-settings';
|
||||
|
@ -73,7 +73,7 @@ export const MenuButton = compose<MenuButtonType>({
|
|||
slots: {
|
||||
root: React.Fragment,
|
||||
button: { slotType: Button as React.ComponentType },
|
||||
primaryButton: { slotType: PrimaryButton as React.ComponentType },
|
||||
primaryButton: { slotType: Button as React.ComponentType },
|
||||
contextualMenu: { slotType: ContextualMenu as React.ComponentType },
|
||||
contextualMenuItems: React.Fragment,
|
||||
chevronSvg: SvgXml,
|
||||
|
|
|
@ -2,125 +2,93 @@
|
|||
|
||||
exports[`ContextualMenu default 1`] = `
|
||||
<View
|
||||
accessible={false}
|
||||
focusable={false}
|
||||
accessibilityLabel=""
|
||||
accessibilityRole="button"
|
||||
accessibilityState={
|
||||
Object {
|
||||
"disabled": false,
|
||||
}
|
||||
}
|
||||
accessible={true}
|
||||
content="Press for Nested MenuButton"
|
||||
enableFocusRing={true}
|
||||
focusable={true}
|
||||
onAccessibilityTap={[Function]}
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onPress={[Function]}
|
||||
onResponderGrant={[Function]}
|
||||
onResponderMove={[Function]}
|
||||
onResponderRelease={[Function]}
|
||||
onResponderTerminate={[Function]}
|
||||
onResponderTerminationRequest={[Function]}
|
||||
onStartShouldSetResponder={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"alignItems": "flex-start",
|
||||
"alignItems": "center",
|
||||
"alignSelf": "flex-start",
|
||||
"backgroundColor": "#f3f2f1",
|
||||
"borderColor": "#8a8886",
|
||||
"borderRadius": 12,
|
||||
"borderRadius": 4,
|
||||
"borderWidth": 1,
|
||||
"display": "flex",
|
||||
"flexDirection": "row",
|
||||
"overflow": "hidden",
|
||||
"justifyContent": "center",
|
||||
"minWidth": 96,
|
||||
"padding": 5,
|
||||
"paddingHorizontal": 11,
|
||||
"width": undefined,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
accessibilityLabel="Press for Nested MenuButton"
|
||||
accessibilityRole="button"
|
||||
accessibilityState={
|
||||
Object {
|
||||
"disabled": false,
|
||||
}
|
||||
}
|
||||
accessible={true}
|
||||
collapsable={false}
|
||||
componentRef={
|
||||
Object {
|
||||
"current": null,
|
||||
}
|
||||
}
|
||||
focusable={true}
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onResponderGrant={[Function]}
|
||||
onResponderMove={[Function]}
|
||||
onResponderRelease={[Function]}
|
||||
onResponderTerminate={[Function]}
|
||||
onResponderTerminationRequest={[Function]}
|
||||
onStartShouldSetResponder={[Function]}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
<RNSVGSvgView
|
||||
align="xMidYMid"
|
||||
bbHeight={16}
|
||||
bbWidth={12}
|
||||
color={-10395295}
|
||||
focusable={false}
|
||||
height={16}
|
||||
meetOrSlice={0}
|
||||
minX={0}
|
||||
minY={0}
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"alignItems": "center",
|
||||
"alignSelf": "flex-start",
|
||||
"display": "flex",
|
||||
"flexDirection": "row",
|
||||
"justifyContent": "center",
|
||||
"minHeight": 52,
|
||||
"minWidth": 80,
|
||||
"paddingHorizontal": 20,
|
||||
"paddingVertical": 14,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "#323130",
|
||||
"fontFamily": "Segoe UI",
|
||||
"fontSize": 14,
|
||||
"fontWeight": "600",
|
||||
"margin": 0,
|
||||
}
|
||||
}
|
||||
>
|
||||
Press for Nested MenuButton
|
||||
</Text>
|
||||
<RNSVGSvgView
|
||||
align="xMidYMid"
|
||||
bbHeight={16}
|
||||
bbWidth={12}
|
||||
color={-10395295}
|
||||
focusable={false}
|
||||
height={16}
|
||||
meetOrSlice={0}
|
||||
minX={0}
|
||||
minY={0}
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"backgroundColor": "transparent",
|
||||
"borderWidth": 0,
|
||||
},
|
||||
Object {
|
||||
"flex": 0,
|
||||
"height": 16,
|
||||
"width": 12,
|
||||
},
|
||||
]
|
||||
}
|
||||
tintColor={-10395295}
|
||||
vbHeight={6}
|
||||
vbWidth={11}
|
||||
width={12}
|
||||
xml="
|
||||
"backgroundColor": "transparent",
|
||||
"borderWidth": 0,
|
||||
},
|
||||
Object {
|
||||
"flex": 0,
|
||||
"height": 16,
|
||||
"width": 12,
|
||||
},
|
||||
]
|
||||
}
|
||||
tintColor={-10395295}
|
||||
vbHeight={6}
|
||||
vbWidth={11}
|
||||
width={12}
|
||||
xml="
|
||||
<svg width=\\"12\\" height=\\"16\\" viewBox=\\"0 0 11 6\\" color=#616161>
|
||||
<path fill='currentColor' d='M0.646447 0.646447C0.841709 0.451184 1.15829 0.451184 1.35355 0.646447L5.5 4.79289L9.64645 0.646447C9.84171 0.451185 10.1583 0.451185 10.3536 0.646447C10.5488 0.841709 10.5488 1.15829 10.3536 1.35355L5.85355 5.85355C5.65829 6.04882 5.34171 6.04882 5.14645 5.85355L0.646447 1.35355C0.451184 1.15829 0.451184 0.841709 0.646447 0.646447Z' />
|
||||
</svg>"
|
||||
>
|
||||
<RNSVGGroup>
|
||||
<RNSVGPath
|
||||
d="M0.646447 0.646447C0.841709 0.451184 1.15829 0.451184 1.35355 0.646447L5.5 4.79289L9.64645 0.646447C9.84171 0.451185 10.1583 0.451185 10.3536 0.646447C10.5488 0.841709 10.5488 1.15829 10.3536 1.35355L5.85355 5.85355C5.65829 6.04882 5.34171 6.04882 5.14645 5.85355L0.646447 1.35355C0.451184 1.15829 0.451184 0.841709 0.646447 0.646447Z"
|
||||
fill={
|
||||
Array [
|
||||
2,
|
||||
]
|
||||
}
|
||||
propList={
|
||||
Array [
|
||||
"fill",
|
||||
]
|
||||
}
|
||||
/>
|
||||
</RNSVGGroup>
|
||||
</RNSVGSvgView>
|
||||
</View>
|
||||
</View>
|
||||
>
|
||||
<RNSVGGroup>
|
||||
<RNSVGPath
|
||||
d="M0.646447 0.646447C0.841709 0.451184 1.15829 0.451184 1.35355 0.646447L5.5 4.79289L9.64645 0.646447C9.84171 0.451185 10.1583 0.451185 10.3536 0.646447C10.5488 0.841709 10.5488 1.15829 10.3536 1.35355L5.85355 5.85355C5.65829 6.04882 5.34171 6.04882 5.14645 5.85355L0.646447 1.35355C0.451184 1.15829 0.451184 0.841709 0.646447 0.646447Z"
|
||||
fill={
|
||||
Array [
|
||||
2,
|
||||
]
|
||||
}
|
||||
propList={
|
||||
Array [
|
||||
"fill",
|
||||
]
|
||||
}
|
||||
/>
|
||||
</RNSVGGroup>
|
||||
</RNSVGSvgView>
|
||||
</View>
|
||||
`;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
FURNDrawer_kotlinVersion=1.5.31
|
||||
FURNDrawer_fluentuiVersion=0.0.21
|
||||
FURNDrawer_compileSdkVersion=29
|
||||
FURNDrawer_compileSdkVersion=31
|
||||
FURNDrawer_buildToolsVersion=29.0.3
|
||||
FURNDrawer_targetSdkVersion=29
|
||||
FURNDrawer_targetSdkVersion=31
|
||||
FURNDrawer_minSdkVersion=21
|
|
@ -3,7 +3,7 @@ package com.microsoft.frnandroid.drawer
|
|||
import com.facebook.react.ReactPackage
|
||||
import com.facebook.react.bridge.NativeModule
|
||||
import com.facebook.react.bridge.ReactApplicationContext
|
||||
import com.facebook.react.uimanager.ViewManager
|
||||
import com.facebook.react.uimanager.ViewGroupManager
|
||||
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
|
@ -17,7 +17,7 @@ class DrawerPackage: ReactPackage {
|
|||
return modules
|
||||
}
|
||||
|
||||
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
|
||||
return Collections.emptyList<ViewManager<*, *>>()
|
||||
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewGroupManager<*>> {
|
||||
return listOf(DrawerViewManager())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
package com.microsoft.frnandroid.drawer
|
||||
|
||||
import android.view.View
|
||||
import com.facebook.react.ReactRootView
|
||||
import com.facebook.react.uimanager.ThemedReactContext
|
||||
import com.facebook.react.uimanager.ViewGroupManager
|
||||
import com.facebook.react.uimanager.annotations.ReactProp
|
||||
import com.facebook.react.views.view.ReactViewGroup
|
||||
import com.microsoft.fluentui.drawer.DrawerDialog
|
||||
|
||||
class DrawerViewManager : ViewGroupManager<ReactViewGroup>() {
|
||||
companion object {
|
||||
private const val REACT_CLASS = "FRNDrawer"
|
||||
}
|
||||
private lateinit var mView: ReactViewGroup
|
||||
private lateinit var mAnchor: View
|
||||
|
||||
private lateinit var mDrawerDialog: DrawerDialog
|
||||
private lateinit var mContentView: ReactRootView
|
||||
private lateinit var mReactContext: ThemedReactContext
|
||||
private var mBehavior = DrawerDialog.BehaviorType.BOTTOM
|
||||
private var mDimValue = 0.5f
|
||||
private var mTitleBehavior = DrawerDialog.TitleBehavior.DEFAULT
|
||||
private var mAnchorView = null
|
||||
|
||||
override fun getName(): String {
|
||||
return REACT_CLASS
|
||||
}
|
||||
|
||||
override fun createViewInstance(reactContext: ThemedReactContext): ReactViewGroup {
|
||||
mReactContext = reactContext
|
||||
mView = ReactViewGroup(reactContext)
|
||||
mContentView = ReactRootView(reactContext)
|
||||
createDrawerDialog()
|
||||
|
||||
return mView
|
||||
}
|
||||
|
||||
|
||||
override fun addView(parent: ReactViewGroup?, child: View?, index: Int) {
|
||||
when (index) {
|
||||
/* First child is the anchor */
|
||||
0 -> {
|
||||
if (child != null) {
|
||||
mAnchor = child
|
||||
mAnchor.setOnClickListener {
|
||||
mDrawerDialog?.show()
|
||||
}
|
||||
mView.addView(mAnchor)
|
||||
}
|
||||
}
|
||||
/*The second child is the content of the Drawer*/
|
||||
else -> {
|
||||
mContentView.addView(child)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ReactProp(name="behaviorType")
|
||||
fun setBehaviorType(viewGroup: ReactViewGroup, behaviorType: String) {
|
||||
when(behaviorType){
|
||||
"bottom" -> {
|
||||
mBehavior = DrawerDialog.BehaviorType.BOTTOM
|
||||
}
|
||||
"top" -> {
|
||||
mBehavior = DrawerDialog.BehaviorType.TOP
|
||||
}
|
||||
"left" -> {
|
||||
mBehavior = DrawerDialog.BehaviorType.LEFT
|
||||
}
|
||||
"right" -> {
|
||||
mBehavior = DrawerDialog.BehaviorType.RIGHT
|
||||
}
|
||||
}
|
||||
createDrawerDialog()
|
||||
viewGroup.invalidate()
|
||||
}
|
||||
|
||||
@ReactProp(name="titleBehavior")
|
||||
fun setTitleBehavior(viewGroup: ReactViewGroup, titleBehavior: String) {
|
||||
when(titleBehavior){
|
||||
"default" -> {
|
||||
mTitleBehavior = DrawerDialog.TitleBehavior.DEFAULT
|
||||
}
|
||||
"hideTitle" -> {
|
||||
mTitleBehavior = DrawerDialog.TitleBehavior.HIDE_TITLE
|
||||
}
|
||||
"belowTitle" -> {
|
||||
mTitleBehavior = DrawerDialog.TitleBehavior.BELOW_TITLE
|
||||
}
|
||||
|
||||
}
|
||||
createDrawerDialog()
|
||||
viewGroup.invalidate()
|
||||
}
|
||||
|
||||
@ReactProp(name="dimValue")
|
||||
fun setTitleBehavior(viewGroup: ReactViewGroup, dimValue: Float) {
|
||||
mDimValue = dimValue
|
||||
createDrawerDialog()
|
||||
viewGroup.invalidate()
|
||||
}
|
||||
|
||||
fun createDrawerDialog() {
|
||||
mDrawerDialog = DrawerDialog(mReactContext, mBehavior, mDimValue, mAnchorView, mTitleBehavior)
|
||||
mDrawerDialog?.setContentView(mContentView)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.microsoft.fluentui.widget.Button
|
||||
android:id="@+id/show_drawer_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Press for Drawer" />
|
||||
</LinearLayout>
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
~ Licensed under the MIT License.
|
||||
-->
|
||||
|
||||
<com.facebook.react.views.view.ReactViewGroup
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
</com.facebook.react.views.view.ReactViewGroup>
|
|
@ -1,8 +1,7 @@
|
|||
/** @jsx withSlots */
|
||||
import * as React from 'react';
|
||||
import { drawerName, DrawerTokens, DrawerProps, NativeDrawerProps, DrawerType } from './Drawer.types';
|
||||
import { drawerName, DrawerTokens, DrawerProps, DrawerType } from './Drawer.types';
|
||||
import { compose, UseSlots, buildProps, mergeProps, withSlots } from '@fluentui-react-native/framework';
|
||||
import { findNodeHandle } from 'react-native';
|
||||
import { ensureNativeComponent } from '@fluentui-react-native/component-cache';
|
||||
|
||||
const FRNDrawer = ensureNativeComponent('FRNDrawer');
|
||||
|
@ -12,25 +11,13 @@ export const Drawer = compose<DrawerType>({
|
|||
tokens: [drawerName],
|
||||
slots: { root: FRNDrawer },
|
||||
slotProps: {
|
||||
root: buildProps<NativeDrawerProps, DrawerTokens>(() => ({
|
||||
style: {},
|
||||
})),
|
||||
root: buildProps<DrawerProps, DrawerTokens>(() => ({})),
|
||||
},
|
||||
useRender: (props: DrawerProps, useSlots: UseSlots<DrawerType>) => {
|
||||
const { target, ...rest } = props;
|
||||
const [nativeTarget, setNativeTarget] = React.useState<number | null>(null);
|
||||
|
||||
React.useLayoutEffect(() => {
|
||||
if (target?.current) {
|
||||
// Pass the tagID for a valid ref `target`
|
||||
setNativeTarget(findNodeHandle(target.current));
|
||||
}
|
||||
}, [target]);
|
||||
|
||||
const rootProps = { ...rest };
|
||||
const rootProps = props;
|
||||
const Root = useSlots(props).root;
|
||||
return (rest: DrawerProps) => {
|
||||
return <Root {...mergeProps(rootProps, rest)} {...(nativeTarget && { target: nativeTarget })} />;
|
||||
return (final: DrawerProps, ...children: React.ReactNode[]) => {
|
||||
return <Root {...mergeProps(rootProps, final)}>{children}</Root>;
|
||||
};
|
||||
},
|
||||
});
|
||||
|
|
|
@ -6,14 +6,11 @@ export interface DrawerProps extends ViewProps {
|
|||
onShow?: () => void;
|
||||
onDismiss?: () => void;
|
||||
target?: React.RefObject<React.Component>;
|
||||
}
|
||||
|
||||
export interface NativeDrawerProps extends Omit<DrawerProps, 'target'> {
|
||||
target?: number | null;
|
||||
contentRef?: React.RefObject<React.Component>;
|
||||
}
|
||||
|
||||
export type DrawerSlotProps = {
|
||||
root: NativeDrawerProps;
|
||||
root: DrawerProps;
|
||||
};
|
||||
|
||||
export interface DrawerTokens {}
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
import * as React from 'react';
|
||||
import { findNodeHandle, UIManager, View } from 'react-native';
|
||||
import { setAndForwardRef } from './setAndForwardRef';
|
||||
|
||||
export type IFocusable = View;
|
||||
/**
|
||||
* A hook to add an imperative focus method to functional components which simply dispatch a focus command to
|
||||
* something View-derived on the native side. In practice, this effectively applies to all components in our Win32
|
||||
* react native implementation.
|
||||
* @param forwardRef - The componentRef from your component's props where you're exposing a imperative focus method.
|
||||
* @returns The inner View-type you're rendering that you want to dispatch to & focus on.
|
||||
*/
|
||||
export function useViewCommandFocus(
|
||||
forwardedRef: React.Ref<View | null> | undefined,
|
||||
// initialValue?: React.Component
|
||||
): (ref: React.ElementRef<any>) => void {
|
||||
/**
|
||||
* Set up the forwarding ref to enable adding the focus method.
|
||||
*/
|
||||
const focusRef = React.useRef<any>();
|
||||
|
||||
const _setNativeRef = setAndForwardRef({
|
||||
getForwardedRef: () => forwardedRef,
|
||||
setLocalRef: (localRef: any) => {
|
||||
focusRef.current = localRef;
|
||||
|
||||
/**
|
||||
* Add focus() as a callable function to the forwarded reference.
|
||||
*/
|
||||
if (localRef) {
|
||||
localRef.focus = () => {
|
||||
UIManager.dispatchViewManagerCommand(findNodeHandle(localRef), UIManager.getViewManagerConfig('RCTView').Commands.focus, null);
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
return _setNativeRef;
|
||||
}
|
|
@ -1,17 +1,39 @@
|
|||
import * as React from 'react';
|
||||
import { View } from 'react-native';
|
||||
import { findNodeHandle, UIManager, View } from 'react-native';
|
||||
|
||||
import { setAndForwardRef } from './setAndForwardRef';
|
||||
|
||||
export type IFocusable = View;
|
||||
|
||||
/**
|
||||
* This is a no-op on platforms that don't support focus imperative calls
|
||||
* A hook to add an imperative focus method to functional components which simply dispatch a focus command to
|
||||
* something View-derived on the native side. In practice, this effectively applies to all components in our Win32
|
||||
* react native implementation.
|
||||
* @param forwardRef - The componentRef from your component's props where you're exposing a imperative focus method.
|
||||
* @returns The inner View-type you're rendering that you want to dispatch to & focus on.
|
||||
*/
|
||||
export function useViewCommandFocus<T>(
|
||||
_: React.Ref<T | null> | undefined,
|
||||
initialValue?: React.Component,
|
||||
): React.RefObject<React.Component> {
|
||||
const innerRef = React.useRef<React.Component>(initialValue || null);
|
||||
return innerRef;
|
||||
export function useViewCommandFocus(
|
||||
forwardedRef: React.Ref<View | null> | undefined,
|
||||
// initialValue?: React.Component
|
||||
): (ref: React.ElementRef<any>) => void {
|
||||
/**
|
||||
* Set up the forwarding ref to enable adding the focus method.
|
||||
*/
|
||||
const focusRef = React.useRef<any>();
|
||||
|
||||
const _setNativeRef = setAndForwardRef({
|
||||
getForwardedRef: () => forwardedRef,
|
||||
setLocalRef: (localRef: any) => {
|
||||
focusRef.current = localRef;
|
||||
|
||||
/**
|
||||
* Add focus() as a callable function to the forwarded reference.
|
||||
*/
|
||||
if (localRef) {
|
||||
localRef.focus = () => {
|
||||
UIManager.dispatchViewManagerCommand(findNodeHandle(localRef), UIManager.getViewManagerConfig('RCTView').Commands.focus, null);
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
return _setNativeRef;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче