diff --git a/README.md b/README.md index 019d8f07ccee74c759769df28242ea01316d0c12..6a7df4367318b09ed382ba7eb4d91ee55b532011 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ | 44 | [react-lifecycles-compat](https://github.com/reactjs/react-lifecycles-compat) | 3.0.4 | \- | \- | [链接](/zh-cn/react-lifecycles-compat.md) | | 45 | [react-native-action-button](https://github.com/mastermoo/react-native-action-button) | 2.8.5 | \- | \- | [链接](/zh-cn/react-native-action-button.md) | | 46 | [react-native-animate-number](https://github.com/wkh237/react-native-animate-number) | 0.1.2 | \- | \- | [链接](/zh-cn/react-native-animate-number.md) | -| 47 | [react-native-aria](https://github.com/gluestack/react-native-aria/) | 0.2.4 | \- | \- | [链接](/zh-cn/react-native-aria.md) | +| 47 | [react-native-aria](https://github.com/gluestack/react-native-aria/) | 0.2.3 | \- | \- | [链接](/zh-cn/react-native-aria.md) | | 48 | [react-native-autoheight-webview](https://github.com/iou90/react-native-autoheight-webview) | 1.6.5 | \- | [@react-native-oh-tpl/react-native-autoheight-webview](https://github.com/react-native-oh-library/react-native-autoheight-webview/releases) | [链接](/zh-cn/react-native-autoheight-webview.md) | | 49 | [react-native-base64](https://github.com/eranbo/react-native-base64) | 0.2.1 | \- | \- | [链接](/zh-cn/react-native-base64.md) | | 50 | [react-native-blob-util](https://github.com/RonRadtke/react-native-blob-util) | 0.19.6 | 是 | [@react-native-oh-tpl/react-native-blob-util](https://github.com/react-native-oh-library/react-native-blob-util/releases) | [链接](/zh-cn/react-native-blob-util.md) | diff --git a/zh-cn/README.md b/zh-cn/README.md index bf59cc2e3285bc7c123ce943c5c81e21922ad471..ab8cf8e814f49fb119d543cb4fcadb7ed8eb5dbd 100644 --- a/zh-cn/README.md +++ b/zh-cn/README.md @@ -72,7 +72,7 @@ | 44 | [react-lifecycles-compat](https://github.com/reactjs/react-lifecycles-compat) | 3.0.4 | \- | \- | [链接](/zh-cn/react-lifecycles-compat.md) | | 45 | [react-native-action-button](https://github.com/mastermoo/react-native-action-button) | 2.8.5 | \- | \- | [链接](/zh-cn/react-native-action-button.md) | | 46 | [react-native-animate-number](https://github.com/wkh237/react-native-animate-number) | 0.1.2 | \- | \- | [链接](/zh-cn/react-native-animate-number.md) | -| 47 | [react-native-aria](https://github.com/gluestack/react-native-aria/) | 0.2.4 | \- | \- | [链接](/zh-cn/react-native-aria.md) | +| 47 | [react-native-aria](https://github.com/gluestack/react-native-aria/) | 0.2.3 | \- | \- | [链接](/zh-cn/react-native-aria.md) | | 48 | [react-native-autoheight-webview](https://github.com/iou90/react-native-autoheight-webview) | 1.6.5 | \- | [@react-native-oh-tpl/react-native-autoheight-webview](https://github.com/react-native-oh-library/react-native-autoheight-webview/releases) | [链接](/zh-cn/react-native-autoheight-webview.md) | | 49 | [react-native-base64](https://github.com/eranbo/react-native-base64) | 0.2.1 | \- | \- | [链接](/zh-cn/react-native-base64.md) | | 50 | [react-native-blob-util](https://github.com/RonRadtke/react-native-blob-util) | 0.19.6 | 是 | [@react-native-oh-tpl/react-native-blob-util](https://github.com/react-native-oh-library/react-native-blob-util/releases) | [链接](/zh-cn/react-native-blob-util.md) | diff --git a/zh-cn/react-native-aria.md b/zh-cn/react-native-aria.md index 69f372bdbee9cf500b7059466824b6487c59a2da..4cc565728cc4318f72584022517e30dfc735a3b0 100644 --- a/zh-cn/react-native-aria.md +++ b/zh-cn/react-native-aria.md @@ -13,7 +13,7 @@

-> [!tip] [Github 地址](https://github.com/gluestack/react-native-aria/tree/v0.2.4) +> [!TIP] [Github 地址](https://github.com/gluestack/react-native-aria/tree/main) ## 安装与使用 @@ -24,7 +24,7 @@ React Native ARIA是可增量采用的。每个组件都作为单独的包发布 #### **yarn** ```bash -yarn add @react-native-aria/button@0.2.2 +yarn add react-native-aria@0.2.3 ``` @@ -32,17 +32,17 @@ yarn add @react-native-aria/button@0.2.2 #### **npm** ```bash -npm install @react-native-aria/button@0.2.2 +npm install react-native-aria@0.2.3 ``` -除了单独的包之外,我们还提供了一个总包,其中包含所有React Native ARIA hooks +除了总包之外,我们还提供了一些单独包,如:@react-native-aria/button #### **yarn** ```bash -yarn add react-native-aria@0.2.2 +yarn add @react-native-aria/button@0.2.7 ``` @@ -50,7 +50,7 @@ yarn add react-native-aria@0.2.2 #### **npm** ```bash -npm install react-native-aria@0.2.2 +npm install @react-native-aria/button@0.2.7 ``` @@ -60,119 +60,118 @@ npm install react-native-aria@0.2.2 ```js import React from "react"; import { useToggleButton } from "@react-native-aria/button"; -import { Pressable, Text, View } from "react-native"; import { useToggleState } from "@react-stately/toggle"; +import { Pressable, Text, View } from "react-native"; import { useRef } from "react"; -export function ToggleButton(props) { - const ref = useRef(null); - let state = useToggleState(props); - let { buttonProps, isPressed } = useToggleButton(props, state, ref); //useToggleButton 是一个用于创建可切换按钮的 Hook,接受三个参数 - - return ( - - - - A simple toggle button - - +export function ToggleButton(props: any) { + const ref = useRef(null); + let state = useToggleState(props); + let { buttonProps, isPressed } = useToggleButton(props, state); - - - {state.isSelected ? "Selected" : "Not Selected"} + return ( + + ToggleButton: + + 点击切换 + - - - ); + ); } -export default function AriaDemo() { - return ( - - Toggle button - - ); -} +export default ToggleButton + ``` 下面的代码展示了useCheckbox与useCheckboxGroup的基本使用场景: ```javascript -import React ,{ useContext, useRef }from 'react'; -import {useCheckboxGroupState} from '@react-stately/checkbox'; -import {useCheckbox, useCheckboxGroup,useCheckboxGroupItem} from '@react-native-aria/checkbox'; -import {Platform, Pressable, Text, View} from 'react-native'; -import {VisuallyHidden} from '@react-aria/visually-hidden'; -import {useFocusRing} from '@react-native-aria/focus'; +import React, { useContext, useRef } from 'react'; +import { Pressable, View, Text } from 'react-native'; +import { useCheckbox, useCheckboxGroupItem, useCheckboxGroup } from '@react-native-aria/checkbox'; +import { useToggleState } from '@react-stately/toggle'; +import { useCheckboxGroupState } from "@react-stately/checkbox"; +import { useFocusRing } from '@react-native-aria/focus'; + let CheckboxGroupContext = React.createContext(null); +const CheckboxItems = [{ key: 'soccer', value: '足球' }, { key: 'baseball', value: '棒球' }, { key: 'basketball', value: '篮球' }] +const findName = (value: string) => { + const item = CheckboxItems.find(i => { return i.key === value; }) + return item && item.value +} + export function CheckboxGroup(props: any) { - let {children, label} = props; - let state = useCheckboxGroupState(props); - let {checkboxgroupProps, labelProps} = useCheckboxGroup(props, state); - - return ( - - {label && {label}} - - {children} - - - ); + let { children, label } = props; + let state = useCheckboxGroupState(props); + let { groupProps, labelProps } = useCheckboxGroup(props, state); + + return ( + + {label && {label}} + + {children} + + 已经选择:{props.value.map(i=>{ + return {findName(i)} + })} + + ); } + + export function Checkbox(props: any) { + let groupState = useContext(CheckboxGroupContext); + let inputRef = useRef(null); + + let { isFocusVisible, focusProps } = useFocusRing(); + + let { inputProps } = groupState ? useCheckboxGroupItem( + { + ...props, + isRequired: props.isRequired, + validationState: props.validationState, + }, + groupState, + inputRef + ) : useCheckbox(props, useToggleState(props), inputRef); - let state = useContext(CheckboxGroupContext); - const inputRef = React.useRef(null); - let {isFocusVisible, focusProps} = useFocusRing(); - let { inputProps } = state - ? - useCheckboxGroupItem( - { - ...props, - isRequired: props.isRequired, - validationState: props.validationState, - }, - state, - inputRef - ) - : - useCheckbox(props, useToggleState(props), inputRef); - return ( - <> - - - - {props.children} + return ( + + + + {props.children} + + - {inputProps.checked ? 'selected' : 'not selected'} - - - ); + ); } -export default function CheckboxDemo() { - return ( - - - Dogs - Cats - rabbits - - - ); -} +export const CheckboxExample = () => { + const [state, setCheckbox] = React.useState([]); + + return ( + { + setCheckbox(val); + }} + > + {CheckboxItems.map(i => { + return + {i.value} + ; + })} + + ); +}; +export default CheckboxExample ``` @@ -182,70 +181,71 @@ export default function CheckboxDemo() { import React from "react"; import { useRadioGroupState } from "@react-stately/radio"; import { useRadio, useRadioGroup } from "@react-native-aria/radio"; -import { Platform, Pressable, Text, View } from "react-native"; -import { VisuallyHidden } from "@react-aria/visually-hidden"; +import { Pressable, Text, View } from "react-native"; import { useFocusRing } from "@react-native-aria/focus"; let RadioContext = React.createContext({}); +const radioItems = [{ key: 'dogs', value: '狗子' }, { key: 'cats', value: '猫儿' }] +const findName = (value: string) => { + const item = radioItems.find(i => { return i.key === value; }) + return item && item.value +} + export function RadioGroup(props: any) { - let { children, label } = props; - let state = useRadioGroupState(props); - let { radioGroupProps, labelProps } = useRadioGroup(props, state); - - return ( - - {label} - - {children} - - - ); + let { children, label } = props; + let state = useRadioGroupState(props); + let { radioGroupProps, labelProps } = useRadioGroup(props, state); + + return ( + + {label} + + {children} + + 已经选择:{findName(state.selectedValue as string)} + + ); } export function Radio(props: any) { - let { state, isReadOnly, isDisabled } = React.useContext(RadioContext); - const inputRef = React.useRef(null); - let { inputProps } = useRadio( - { isReadOnly, isDisabled, ...props }, - state, - inputRef - ); - let { isFocusVisible, focusProps } = useFocusRing(); - - let isSelected = state.selectedValue === props.value; - - return ( - <> - - - - + let { state, isReadOnly, isDisabled } = React.useContext(RadioContext); + const inputRef = React.useRef(null); + let { inputProps } = useRadio( + { isReadOnly, isDisabled, ...props }, + state, + inputRef + ); + let { isFocusVisible, focusProps } = useFocusRing(); + + return ( + + + {props.children} - {props.children} - - {isSelected ? "selected" : "not selected"} - - ); + ); } -export default function RadioDemo() { - return ( - - - Dogs - Cats - - - ); -} +export const RadioExample = () => { + return ( + + + {radioItems.map(i => { + return {i.value} + })} + + + ); +}; +export default RadioExample + ``` 下面的代码展示了useSwitch的基本使用场景: @@ -253,169 +253,151 @@ export default function RadioDemo() { ```javascript import { useToggleState } from "@react-stately/toggle"; import React, { useRef } from "react"; -import { - StyleSheet, - Text, - View, - Animated, - Platform, - Pressable, -} from "react-native"; +import { StyleSheet, Text, View, Animated, Pressable } from "react-native"; import { useSwitch } from "@react-native-aria/switch"; import { useFocusRing } from "@react-native-aria/focus"; import { VisuallyHidden } from "@react-aria/visually-hidden"; const calculateDimensions = (size: any) => { - switch (size) { - case "small": - return { - width: 40, - padding: 10, - circleWidth: 15, - circleHeight: 15, - translateX: 22, - }; - case "large": - return { - width: 70, - padding: 20, - circleWidth: 30, - circleHeight: 30, - translateX: 38, - }; - default: - return { - width: 46, - padding: 12, - circleWidth: 18, - circleHeight: 18, - translateX: 26, - }; - } + switch (size) { + case "small": + return { + width: 40, + padding: 10, + circleWidth: 15, + circleHeight: 15, + translateX: 22, + }; + case "large": + return { + width: 70, + padding: 20, + circleWidth: 30, + circleHeight: 30, + translateX: 38, + }; + default: + return { + width: 46, + padding: 12, + circleWidth: 18, + circleHeight: 18, + translateX: 26, + }; + } }; + const defaultProps = { - isOn: false, - onColor: "#4cd137", - offColor: "#ecf0f1", - size: "medium", - labelStyle: {}, - thumbOnStyle: {}, - thumbOffStyle: {}, - trackOnStyle: {}, - trackOffStyle: {}, - icon: null, - disabled: false, - animationSpeed: 300, - useNativeDriver: true, - circleColor: "white", + isOn: false, + onColor: "#4cd137", + offColor: "#ecf0f1", + size: "medium", + labelStyle: {}, + thumbOnStyle: {}, + thumbOffStyle: {}, + trackOnStyle: {}, + trackOffStyle: {}, + icon: null, + disabled: false, + animationSpeed: 300, + useNativeDriver: true, + circleColor: "white", }; + export function Switch(origProps: any) { - const props = { - ...defaultProps, - ...origProps, - }; - - const offsetX = useRef(new Animated.Value(0)); - const dimensions = useRef(calculateDimensions(props.size)); - - const createToggleSwitchStyle = () => [ - { - justifyContent: "center", - width: dimensions.current.width, - borderRadius: 20, - padding: dimensions.current.padding, - backgroundColor: props.isOn ? props.onColor : props.offColor, - }, - props.isOn ? props.trackOnStyle : props.trackOffStyle, - ]; - - const createInsideCircleStyle = () => [ - { - alignItems: "center", - justifyContent: "center", - margin: Platform.OS === "web" ? 0 : 4, - left: Platform.OS === "web" ? 4 : 0, - position: "absolute", - backgroundColor: props.circleColor, - transform: [{ translateX: offsetX.current }], - width: dimensions.current.circleWidth, - height: dimensions.current.circleHeight, - borderRadius: dimensions.current.circleWidth / 2, - shadowColor: "#000", - shadowOffset: { - width: 0, - height: 2, - }, - shadowOpacity: 0.2, - shadowRadius: 2.5, - elevation: 1.5, - }, - props.isOn ? props.thumbOnStyle : props.thumbOffStyle, - ]; - - const { isOn, icon } = props; - - const toValue = isOn - ? dimensions.current.width - dimensions.current.translateX - : 0; - - Animated.timing(offsetX.current, { - toValue, - duration: props.animationSpeed, - useNativeDriver: props.useNativeDriver, - }).start(); - - return ( - - - {icon} - - {isOn ? "on" : "off"} - - ); -} + const props = { + ...defaultProps, + ...origProps, + }; + + const offsetX = useRef(new Animated.Value(0)); + const dimensions = useRef(calculateDimensions(props.size)); + + const createToggleSwitchStyle = () => [ + { + justifyContent: "center", + width: dimensions.current.width, + borderRadius: 20, + padding: dimensions.current.padding, + backgroundColor: props.isOn ? props.onColor : props.offColor, + }, + props.isOn ? props.trackOnStyle : props.trackOffStyle, + ]; + + const createInsideCircleStyle = () => [ + { + alignItems: "center", + justifyContent: "center", + margin: 4, + left: 0, + position: "absolute", + backgroundColor: props.circleColor, + transform: [{ translateX: offsetX.current }], + width: dimensions.current.circleWidth, + height: dimensions.current.circleHeight, + borderRadius: dimensions.current.circleWidth / 2, + shadowColor: "#000", + shadowOffset: { + width: 0, + height: 2, + }, + shadowOpacity: 0.2, + shadowRadius: 2.5, + elevation: 1.5, + }, + props.isOn ? props.thumbOnStyle : props.thumbOffStyle, + ]; + + const { isOn, icon, label, labelStyle = {} } = props; + + const toValue = isOn + ? dimensions.current.width - dimensions.current.translateX + : 0; + + Animated.timing(offsetX.current, { + toValue, + duration: props.animationSpeed, + useNativeDriver: props.useNativeDriver, + }).start(); -const styles = StyleSheet.create({ - container: { - alignItems: "center", - }, - labelStyle: { - marginHorizontal: 10, - }, -}); + return ( + + {label && {label}} + + {icon} + + {isOn ? "开" : "关"} + + ); +} export function ControlledSwitch() { - const state = useToggleState(); - const { isFocusVisible, focusProps } = useFocusRing(); - const inputRef = useRef(null); - let { inputProps } = useSwitch( - { "aria-label": "Example switch" }, - state, - inputRef - ); + const state = useToggleState(); + const { isFocusVisible, focusProps } = useFocusRing(); + const inputRef = useRef(null); + let { inputProps } = useSwitch( + { "aria-label": "Example switch" }, + state, + inputRef + ); + return ( - - - - - + + + + + ); - } -export default function SwitchDemo () { - return ( - - - - ); + } +export default ControlledSwitch ``` @@ -423,109 +405,108 @@ export default function SwitchDemo () { ```javascript import React from "react"; -import { - OverlayContainer, - useOverlayPosition, - OverlayProvider, -} from "@react-native-aria/overlays"; -import { - View, - Text, - Pressable, - TouchableWithoutFeedback, - StyleSheet, -} from "react-native"; - -// Button to close overlay on outside click -function CloseButton(props) { - return ( - - - - ); +import { useOverlayPosition, OverlayProvider, OverlayContainer } from "@react-native-aria/overlays"; +import { useButton } from "@react-native-aria/button"; +import { View, Text, Pressable, ScrollView, Modal } from "react-native"; +import { useToggleState } from "@react-stately/toggle"; + +const positions = [ + "top", + "left", + "right", + "bottom", + "top left", + "top right", + "left top", + "left bottom", + "bottom right, bottom left", + "right top", + "right bottom", +]; + +export function TriggerWrapper() { + const [placement, setPlacement] = React.useState(-1); + React.useEffect(() => { + const id = setInterval(() => { + setPlacement((prev) => (prev + 1) % positions.length); + }, 2000); + return () => clearInterval(id); + }, []); + + return ; } -const OverlayView = ({ triggerRef, placement }) => { - let overlayRef = React.useRef(); - - const { overlayProps } = useOverlayPosition({ - placement, - targetRef: triggerRef, - overlayRef, - offset: 10, - }); - - return ( - - - Hello World - - - ); +const OverlayView = ({ targetRef, placement = 'top' }) => { + let overlayRef = React.useRef(); + + const { overlayProps } = useOverlayPosition({ + placement: placement as any, + targetRef, + overlayRef, + offset: 10, + }); + + return ( + overlayRef.current = node} + > + + Hello world + + + ); }; -function Trigger({ placement }) { - let triggerRef = React.useRef(); - const [visible, setVisible] = React.useState(false); - const toggleVisible = () => { - setVisible(!visible); - }; - - return ( - - - - Trigger - - - {visible && ( - - - - - )} - - ); -} +export function Trigger({ placement }: any) { + let ref = React.useRef(); + const toggleState = useToggleState(); -export default function OverlaysDemo() { - return ( - - - - - ); + let { buttonProps } = useButton({ onPress: toggleState.toggle }, ref); + + return ( + + useOverlayPosition: + + + 点我一下 + + + + + + + + + + + ); } +export default TriggerWrapper -const styles = StyleSheet.create({ - wrapper: { - height: 500, - alignItems: "center", - justifyContent: "center", - }, - trigger: { - flexDirection: "row", - borderWidth: 1, - paddingHorizontal: 10, - paddingVertical: 10, - }, -}); ``` 更多hooks请参考[react-native-aria官方文档](https://geekyants.github.io/react-native-aria/docs/) @@ -541,9 +522,9 @@ const styles = StyleSheet.create({ ## 属性 -> [!tip] "Platform"列表示该属性在原三方库上支持的平台。 +> [!TIP] "Platform"列表示该属性在原三方库上支持的平台。 -> [!tip] "HarmonyOS Support"列为 yes 表示 HarmonyOS 平台支持该属性;no 则表示不支持;partially 表示部分支持。使用方法跨平台一致,效果对标 iOS 或 Android 的效果。 +> [!TIP] "HarmonyOS Support"列为 yes 表示 HarmonyOS 平台支持该属性;no 则表示不支持;partially 表示部分支持。使用方法跨平台一致,效果对标 iOS 或 Android 的效果。 以下属性已验证,详情见 [react-native-aria官方文档](https://geekyants.github.io/react-native-aria/docs/) @@ -553,13 +534,13 @@ const styles = StyleSheet.create({ | Name | Description | Type | Required | Platform | HarmonyOS Support | |--------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------|----------|----------|-------------------| -| useToggleButton | Provides the behavior and accessibility implementation for a toggle button component. ToggleButtons allow users to toggle a selection on or off, for example switching between two states or modes. | Function | no | All | yes | -| useCheckbox | Provides the behavior and accessibility implementation for a checkbox component. Checkboxes allow users to select multiple items from a list of individual items, or to mark one individual item as selected. | Function | no | All | yes | -| useCheckboxGroup | Provides the behavior and accessibility implementation for a checkbox group component. Checkbox groups allow users to select multiple items from a list of options. | Function | no | All | yes | -| useRadioGroup | Provides the behavior and accessibility implementation for a radio group component. Radio groups allow users to select a single item from a list of mutually exclusive options. | Function | no | All | yes | -| useSwitch | Provides the behavior and accessibility implementation for a switch component. A switch is similar to a checkbox, but represents on/off values as opposed to selection. | Function | no | All | yes | -| OverlayContainer | Provides React Portal like functionality for React Native apps which can be useful for displaying absolutely positioned components like Menu, Tooltip, Popover. | Function | no | All | yes | -| useOverlayPosition | Handles positioning overlays like popovers and menus relative to a trigger element, and updating the position when the window resizes. | Function | no | All | yes | +| useToggleButton | Provides the behavior and accessibility implementation for a toggle button component. ToggleButtons allow users to toggle a selection on or off, for example switching between two states or modes. | Function | no | iOS,Android | yes | +| useCheckbox | Provides the behavior and accessibility implementation for a checkbox component. Checkboxes allow users to select multiple items from a list of individual items, or to mark one individual item as selected. | Function | no | iOS,Android | yes | +| useCheckboxGroup | Provides the behavior and accessibility implementation for a checkbox group component. Checkbox groups allow users to select multiple items from a list of options. | Function | no | iOS,Android | yes | +| useRadioGroup | Provides the behavior and accessibility implementation for a radio group component. Radio groups allow users to select a single item from a list of mutually exclusive options. | Function | no | iOS,Android | yes | +| useSwitch | Provides the behavior and accessibility implementation for a switch component. A switch is similar to a checkbox, but represents on/off values as opposed to selection. | Function | no | iOS,Android | yes | +| OverlayContainer | Provides React Portal like functionality for React Native apps which can be useful for displaying absolutely positioned components like Menu, Tooltip, Popover. | Function | no | iOS,Android | yes | +| useOverlayPosition | Handles positioning overlays like popovers and menus relative to a trigger element, and updating the position when the window resizes. | Function | no | iOS,Android | yes | ## 遗留问题