;
31 | }
32 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Dash/measureMeHOC.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * 获取 React-Native components 的 `width` 和 `height`
3 | * 代码来自:https://github.com/obipawan/react-native-measureme
4 | */
5 | import React, { useState } from 'react';
6 | import { View } from 'react-native';
7 | import type { ViewProps } from 'react-native';
8 |
9 | export type MeasureMeState = {
10 | width: number;
11 | height: number;
12 | initialRender: true;
13 | };
14 |
15 | const measureMeHOC =
16 | (ComposedComponent: React.ComponentType
): React.FC
=>
17 | props => {
18 | const [state, setState] = useState();
19 |
20 | const handleLayout: ViewProps['onLayout'] = ({
21 | nativeEvent: { layout: { width = 0, height = 0 } = {} } = {},
22 | }) => {
23 | setState({ width, height, initialRender: true });
24 | };
25 |
26 | return state?.initialRender ? (
27 |
28 | ) : (
29 |
30 | );
31 | };
32 |
33 | export default measureMeHOC;
34 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/DatetimePicker/DatetimePicker.tsx:
--------------------------------------------------------------------------------
1 | import React, { forwardRef } from 'react';
2 | import type { View } from 'react-native';
3 | import DatePicker from './DatePicker';
4 | import TimePicker from './TimePicker';
5 | import type { DateTimePickerProps, TimePickerProps, DatePickerProps } from './type';
6 |
7 | const DatetimePicker = forwardRef((props, ref) => {
8 | const isTimePicker = props.type === 'time';
9 |
10 | if (isTimePicker) {
11 | return ;
12 | }
13 |
14 | return ;
15 | });
16 |
17 | export default DatetimePicker;
18 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/DatetimePicker/index.ts:
--------------------------------------------------------------------------------
1 | import DatetimePicker from './DatetimePicker';
2 |
3 | export default DatetimePicker;
4 | export { DatetimePicker };
5 | export type { DateTimePickerProps, DatetimePickerType } from './type';
6 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/DatetimePicker/utils.ts:
--------------------------------------------------------------------------------
1 | export function times(n: number, iteratee: (index: number) => string): unknown[] {
2 | let index = -1;
3 | const result = Array(n);
4 |
5 | while (++index < n) {
6 | result[index] = iteratee(index);
7 | }
8 |
9 | return result;
10 | }
11 |
12 | export function getMonthEndDay(year: number, month: number): number {
13 | return 32 - new Date(year, month - 1, 32).getDate();
14 | }
15 |
16 | export function padZero(num: number | string, targetLength = 2): string {
17 | let str = `${num}`;
18 |
19 | while (str.length < targetLength) {
20 | str = `0${str}`;
21 | }
22 |
23 | return str;
24 | }
25 |
26 | export const splitDate = (_date: Date): [number, number, number, number, number] => {
27 | const year = _date.getFullYear();
28 | const month = _date.getMonth() + 1;
29 | const date = _date.getDate();
30 | const hour = _date.getHours();
31 | const minute = _date.getMinutes();
32 |
33 | return [year, month, date, hour, minute];
34 | };
35 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Divider/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Divider 分割线
3 | desc: 用于将内容分隔为多个区域。
4 | demo: /divider
5 |
6 | nav:
7 | path: /
8 |
9 | group:
10 | title: 展示组件
11 | ---
12 |
13 | ### 基础用法
14 |
15 | 默认渲染一条水平分割线。
16 |
17 | ```jsx
18 |
19 | ```
20 |
21 | ### 展示文字
22 |
23 | 通过插槽在可以分割线中间插入内容。
24 |
25 | ```jsx
26 | 文字
27 | ```
28 |
29 | ### 内容位置
30 |
31 | 通过 `contentPosition` 指定内容所在位置。
32 |
33 | ```jsx
34 | 文字
35 | 文字
36 | ```
37 |
38 | ### 虚线
39 |
40 | 添加 `dashed` 属性使分割线渲染为虚线。
41 |
42 | ```jsx
43 | 文字
44 | ```
45 |
46 | ### 自定义样式
47 |
48 | 可以直接通过 `style` 属性设置分割线的样式。
49 |
50 | ```jsx
51 |
56 | 文字
57 |
58 | ```
59 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Divider/index.ts:
--------------------------------------------------------------------------------
1 | import Divider from './Divider';
2 |
3 | export default Divider;
4 | export { Divider };
5 | export type { DividerProps } from './interface';
6 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Divider/interface.ts:
--------------------------------------------------------------------------------
1 | import type { ViewStyle, TextStyle, StyleProp } from 'react-native';
2 |
3 | export interface DividerProps {
4 | /**
5 | * 外层容器自定义样式
6 | */
7 | style?: StyleProp;
8 |
9 | /**
10 | * 自定义文字样式
11 | */
12 | textStyle?: StyleProp;
13 |
14 | /**
15 | * 自定义线样式
16 | */
17 | lineStyle?: StyleProp;
18 |
19 | /**
20 | * 是否使用虚线
21 | *
22 | * @default false
23 | */
24 | dashed?: boolean;
25 |
26 | /**
27 | * 是否使用 0.5px 线
28 | *
29 | * @default true
30 | */
31 | hairline?: boolean;
32 |
33 | /**
34 | * 内容位置,可选值为 `'left' | 'center' | 'right'`
35 | *
36 | * @default 'center'
37 | */
38 | contentPosition?: 'left' | 'center' | 'right';
39 | children?: string;
40 | }
41 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Divider/style.ts:
--------------------------------------------------------------------------------
1 | import { StyleSheet } from 'react-native';
2 | import type { DividerProps } from './interface';
3 |
4 | type Params = Pick;
5 |
6 | export const createStyle = (theme: DiceUI.Theme, { dashed, hairline, contentPosition }: Params) => {
7 | return StyleSheet.create({
8 | divider: {
9 | alignItems: 'center',
10 | flexDirection: 'row',
11 | marginVertical: theme.divider_margin_vertical,
12 | },
13 |
14 | line: {
15 | borderBottomWidth: hairline ? StyleSheet.hairlineWidth : 1,
16 | borderColor: theme.divider_border_color,
17 | borderStyle: dashed ? 'dashed' : 'solid',
18 | flex: 1,
19 | height: 0,
20 | },
21 |
22 | lineLeft: {
23 | marginRight: theme.divider_margin_horizontal,
24 | maxWidth: contentPosition === 'left' ? theme.divider_content_left_width : 'auto',
25 | },
26 |
27 | lineRight: {
28 | marginLeft: theme.divider_margin_horizontal,
29 | maxWidth: contentPosition === 'right' ? theme.divider_content_right_width : 'auto',
30 | },
31 |
32 | text: {
33 | color: theme.divider_text_color,
34 | fontSize: theme.divider_font_size,
35 | lineHeight: theme.divider_line_height,
36 | },
37 | });
38 | };
39 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Empty/index.ts:
--------------------------------------------------------------------------------
1 | import Empty from './Empty';
2 |
3 | export default Empty;
4 | export { Empty };
5 | export type { EmptyProps } from './type';
6 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Empty/style.ts:
--------------------------------------------------------------------------------
1 | import { StyleSheet } from 'react-native';
2 | import type { ViewStyle, TextStyle } from 'react-native';
3 |
4 | type Styles = {
5 | empty: ViewStyle;
6 | description: TextStyle;
7 | bottom: ViewStyle;
8 | };
9 |
10 | export const createStyle = (theme: DiceUI.Theme): Styles => {
11 | return StyleSheet.create({
12 | bottom: {
13 | marginTop: theme.empty_bottom_margin_top,
14 | },
15 | description: {
16 | color: theme.empty_description_color,
17 | fontSize: theme.empty_description_font_size,
18 | lineHeight: theme.empty_description_line_height,
19 | marginTop: theme.empty_description_margin_top,
20 | paddingHorizontal: theme.empty_description_padding_horizontal,
21 | paddingVertical: theme.empty_description_padding_vertical,
22 | },
23 | empty: {
24 | alignItems: 'center',
25 | justifyContent: 'center',
26 | paddingHorizontal: theme.empty_padding_horizontal,
27 | paddingVertical: theme.empty_padding_vertical,
28 | },
29 | });
30 | };
31 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Empty/type.ts:
--------------------------------------------------------------------------------
1 | import type { ViewProps } from 'react-native';
2 |
3 | export interface EmptyProps extends ViewProps {
4 | /** 图片类型,可选值为 error network search,支持传入图片 URL */
5 | image?: 'default' | 'error' | 'network' | 'search' | string | React.ReactNode;
6 | imageSize?: number;
7 | /** 图片下方的描述文字 */
8 | description?: React.ReactNode;
9 | }
10 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Field/index.ts:
--------------------------------------------------------------------------------
1 | import Field from './Field';
2 |
3 | export default Field;
4 | export { Field };
5 | export type { FieldProps, FieldInstance, FieldType } from './type';
6 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Form/Form.tsx:
--------------------------------------------------------------------------------
1 | import React, { useImperativeHandle } from 'react';
2 | import { View } from 'react-native';
3 | import { useForm, FieldValues, FormProvider } from 'react-hook-form';
4 | import { FormContext } from './FormContext';
5 | import type { FormProps } from './type';
6 |
7 | const Form = (props: FormProps) => {
8 | const {
9 | children,
10 | showValidateMessage = true,
11 | layout,
12 | colon,
13 | style,
14 | form,
15 | mode = 'onChange',
16 | ...formProps
17 | } = props;
18 | const methods = useForm({ mode, ...formProps });
19 |
20 | useImperativeHandle(form, () => methods);
21 |
22 | return (
23 | {...methods}>
24 |
25 | {children}
26 |
27 |
28 | );
29 | };
30 |
31 | export default Form;
32 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Form/FormContext.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import type { FormLayout } from './type';
3 |
4 | export interface FormContextType {
5 | layout?: FormLayout;
6 | colon?: boolean;
7 | showValidateMessage?: boolean;
8 | }
9 |
10 | export const FormContext = React.createContext({} as FormContextType);
11 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Form/FormItem.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import isUndefined from 'lodash-es/isUndefined';
3 | import { useController, useFormContext } from 'react-hook-form';
4 | import Field from '../Field';
5 | import type { FormItemProps } from './type';
6 |
7 | const FormItem = (props: FormItemProps) => {
8 | const { name, children, defaultValue, rules, shouldUnregister, required, ...fieldProps } = props;
9 | const { control } = useFormContext();
10 | const { field, fieldState } = useController({
11 | control,
12 | name,
13 | defaultValue,
14 | rules,
15 | shouldUnregister,
16 | });
17 |
18 | const isRequired = !isUndefined(required) ? required : rules && !!rules?.required;
19 |
20 | const renderChildren = (child: React.ReactElement) => {
21 | const { onChange, onBlur, value } = field;
22 | return React.cloneElement(child, { ...child.props, onChange, onBlur, value });
23 | };
24 |
25 | return (
26 |
32 | {React.isValidElement(children) ? renderChildren(children) : children}
33 |
34 | );
35 | };
36 |
37 | export default FormItem;
38 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Form/FormList.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { useFieldArray, useFormContext } from 'react-hook-form';
3 | import type { UseFieldArrayProps } from 'react-hook-form';
4 |
5 | export interface FormListProps extends Omit {
6 | children?: (params: ReturnType) => React.ReactNode;
7 | }
8 |
9 | const FormList = ({ children, ...props }: FormListProps): JSX.Element => {
10 | const { control } = useFormContext();
11 | const arrayReturn = useFieldArray({ control, ...props });
12 |
13 | return <>{children?.(arrayReturn)}>;
14 | };
15 |
16 | export default FormList;
17 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Form/FormSubscribe.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { useWatch, useFormContext } from 'react-hook-form';
3 | import type { DeepPartialSkipArrayKey } from 'react-hook-form';
4 |
5 | type ChildrenType = (changedValues: DeepPartialSkipArrayKey) => React.ReactNode;
6 |
7 | export interface FormSubscribeProps {
8 | to: any;
9 | children: ChildrenType;
10 | }
11 |
12 | const FormSubscribe = (props: FormSubscribeProps): JSX.Element => {
13 | const { control } = useFormContext();
14 | const watch = useWatch({ control, name: props.to });
15 |
16 | return <>{props.children(watch)}>;
17 | };
18 |
19 | export default FormSubscribe;
20 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Form/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Form 表单
3 | desc: 用于数据录入、校验,支持输入框、单选框、复选框、文件上传等类型。
4 | demo: /form
5 |
6 | nav:
7 | path: /
8 |
9 | group:
10 | title: 表单组件
11 | ---
12 |
13 | > Form 组件是基于[react-hook-form](https://react-hook-form.com/)的封装
14 |
15 | ## 引入
16 |
17 | ```js
18 | import { Form } from '@pingtou/rn-vant';
19 | ```
20 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Form/index.ts:
--------------------------------------------------------------------------------
1 | import { useWatch, useFieldArray } from 'react-hook-form';
2 | import _Form from './Form';
3 | import Item from './FormItem';
4 | import List from './FormList';
5 | import Subscribe from './FormSubscribe';
6 |
7 | const Form = Object.assign(_Form, { Item, Subscribe, List, useWatch, useFieldArray });
8 |
9 | export { Form };
10 | export default Form;
11 | export type { FormProps, FormItemProps, FormInstance } from './type';
12 | export type { FormSubscribeProps } from './FormSubscribe';
13 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Grid/Grid.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { View } from 'react-native';
3 | import { useThemeFactory } from '../Theme';
4 | import type { GridProps } from './type';
5 | import { createStyle } from './style';
6 |
7 | const Grid = (props: GridProps): JSX.Element => {
8 | const { style, children, border, gutter } = props;
9 | const { styles } = useThemeFactory(createStyle);
10 |
11 | return (
12 |
13 | {React.Children.toArray(children)
14 | .filter(React.isValidElement)
15 | .map((child: React.ReactElement, index: number) =>
16 | React.cloneElement(child, {
17 | index,
18 | parent: props,
19 | })
20 | )}
21 |
22 | );
23 | };
24 |
25 | Grid.defaultProps = {
26 | center: true,
27 | border: true,
28 | columnNum: 4,
29 | };
30 |
31 | export default Grid;
32 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Grid/index.ts:
--------------------------------------------------------------------------------
1 | import Grid from './Grid';
2 | import GridItem from './GridItem';
3 |
4 | const GridNamespace = Object.assign(Grid, { Item: GridItem });
5 | export default GridNamespace;
6 | export { GridNamespace as Grid, GridItem };
7 | export type { GridProps, GridItemProps, GridDirection } from './type';
8 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Grid/type.ts:
--------------------------------------------------------------------------------
1 | import type { ViewProps, ViewStyle } from 'react-native';
2 | import type { BadgeProps } from '../Badge/type';
3 |
4 | export type GridDirection = 'horizontal' | 'vertical';
5 |
6 | export interface GridProps extends ViewProps {
7 | /** 是否将格子固定为正方形 */
8 | square?: boolean;
9 | /** 是否将格子内容居中显示 */
10 | center?: boolean;
11 | /** 是否显示边框 */
12 | border?: boolean;
13 | /** 格子之间的间距 */
14 | gutter?: number;
15 | /** 是否调换图标和文本的位置 */
16 | reverse?: boolean;
17 | /** 图标大小 */
18 | iconSize?: number;
19 | /** 格子内容排列的方向,可选值为 `horizontal` */
20 | direction?: GridDirection;
21 | /** 列数 */
22 | columnNum?: number;
23 | }
24 |
25 | export interface GridItemProps extends ViewProps {
26 | /** 图标右上角徽标 */
27 | badge?: BadgeProps;
28 | /** 文字 */
29 | text?: string | React.ReactNode;
30 | /** 图标 */
31 | icon?: React.ReactNode;
32 | /** 图标颜色,等同于 Icon 组件的 color 属性 */
33 | iconColor?: string;
34 | contentStyle?: ViewStyle;
35 | onPress?: () => void;
36 | }
37 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Image/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Image 图片
3 | desc: 增强版的 img 标签,提供多种图片填充模式,支持图片懒加载、加载中提示、加载失败提示。
4 | demo: /image
5 | nav:
6 | path: /
7 |
8 | group:
9 | title: 基础组件
10 | ---
11 |
12 | ## 基础用法
13 |
14 | ```jsx
15 | {
18 | console.log('??');
19 | }}
20 | />
21 | ```
22 |
23 | ## 填充模式
24 |
25 | ```jsx
26 | const resizeMode: ImageResizeMode[] = ['center', 'contain', 'cover', 'repeat', 'stretch'];
27 |
28 |
29 | {resizeMode.map(it => (
30 |
31 |
32 | {it}
33 |
34 | ))}
35 |
36 | ```
37 |
38 | ## 圆形图片
39 |
40 | 通过 `round` 属性可以设置图片变圆
41 |
42 | ```jsx
43 |
44 | ```
45 |
46 | ## 加载中提示
47 |
48 | Image 组件提供了默认的加载中提示,支持通过 `loading` 自定义内容。
49 |
50 | ```jsx
51 | }
54 | />
55 | ```
56 |
57 | ## 加载失败提示
58 |
59 | Image 组件提供了默认的加载失败提示,支持通过 `alt` 自定义内容。
60 |
61 | ```jsx
62 |
63 | ```
64 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Image/index.ts:
--------------------------------------------------------------------------------
1 | import Image from './Image';
2 |
3 | export { Image };
4 | export default Image;
5 | export type { ImageProps } from './type';
6 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/ImagePicker/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: ImagePicker 图片选择器
3 | desc: 用于将本地的图片上传至服务器,并在上传过程中展示预览图和上传进度。
4 | demo: /image-picker
5 | nav:
6 | path: /
7 |
8 | group:
9 | title: 表单组件
10 | ---
11 |
12 | ## 基础用法
13 |
14 | ```tsx
15 | import React from 'react';
16 | import { ImagePicker } from '@pingtou/rn-vant';
17 |
18 | const defaultValue = [
19 | {
20 | url: 'https://img.yzcdn.cn/vant/sand.jpg',
21 | fileName: '图片名称',
22 | },
23 | {
24 | url: 'https://img.yzcdn.cn/vant/tree.jpg',
25 | fileName: '图片名称',
26 | },
27 | ];
28 |
29 | export default () => {
30 | return ;
31 | };
32 | ```
33 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/ImagePicker/index.ts:
--------------------------------------------------------------------------------
1 | import ImagePicker from './ImagePicker';
2 |
3 | export default ImagePicker;
4 | export { ImagePicker };
5 | export type { ImagePickerProps } from './types';
6 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/ImagePreview/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: ImagePreview 图片预览
3 | desc: 图片预览,支持函数调用和组件调用两种方式。
4 | demo: /image-preview
5 | nav:
6 | path: /
7 |
8 | group:
9 | title: 展示组件
10 | ---
11 |
12 | ## 基础用法
13 |
14 | 直接传入图片数组,即可展示图片预览。
15 |
16 | ```jsx
17 | import React from 'react';
18 | import { Cell, ImagePreview } from '@pingtou/rn-vant';
19 |
20 | const images = [
21 | 'https://img.yzcdn.cn/vant/apple-1.jpg',
22 | 'https://img.yzcdn.cn/vant/apple-2.jpg',
23 | 'https://img.yzcdn.cn/vant/apple-3.jpg',
24 | ];
25 |
26 | export default () => {
27 | return (
28 | {
32 | ImagePreview.open({
33 | images,
34 | onChange: index => console.log(`当前展示第${index + 1}张`),
35 | });
36 | }}
37 | />
38 | );
39 | };
40 | ```
41 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/ImagePreview/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ImagePreviewInner from './ImagePreview';
3 | import { PortalRef } from '../ConfigProvider';
4 | import type { ImagePreviewProps } from './type';
5 |
6 | let currentKey = 0;
7 |
8 | const openImagePreview = (options: Omit) => {
9 | const key = `image_preview_${++currentKey}`;
10 |
11 | const handleClosed = () => {
12 | PortalRef.current?.removePortal(key);
13 | options.onClosed?.();
14 | };
15 |
16 | const renderImagePreview = () => (
17 |
18 | );
19 |
20 | PortalRef.current?.addPortal(key, renderImagePreview());
21 |
22 | return { close: handleClosed };
23 | };
24 |
25 | const ImagePreview = Object.assign(ImagePreviewInner, { open: openImagePreview });
26 |
27 | export { ImagePreview };
28 | export default ImagePreview;
29 | export type { ImagePreviewProps, ImagePreviewItemProps } from './type';
30 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/ImagePreview/type.ts:
--------------------------------------------------------------------------------
1 | export type CloseParams = { url: string; index: number };
2 |
3 | type CouldClose = boolean | Promise;
4 |
5 | export interface ImagePreviewProps {
6 | visible?: boolean;
7 | overlay?: boolean;
8 | // lazyload?: LazyImageType;
9 | maxZoom?: number;
10 | closeable?: boolean;
11 | showIndicators?: boolean;
12 | showIndex?: boolean;
13 | indexRender?: ({ index, len }: { index: number; len: number }) => React.ReactNode;
14 | beforeClose?: (active: string | number) => CouldClose;
15 | onClose?: (p?: CloseParams) => void;
16 | onClosed?: () => void;
17 | onChange?: (index: number) => void;
18 | images?: string[];
19 | swipeDuration?: number;
20 | startPosition?: number;
21 | closeIcon?: React.ReactNode;
22 | /** 只在点击关闭按钮时关闭ImagePreview组件 */
23 | closeOnlyClickCloseIcon?: boolean;
24 | testID?: string;
25 | }
26 |
27 | export interface ImagePreviewItemProps {
28 | // lazyload: LazyImageType;
29 | image: string;
30 | maxZoom: number;
31 | onTap: () => void;
32 | onZoomChange?: (zoom: number) => void;
33 | }
34 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/IndexBar/IndexBarContext.ts:
--------------------------------------------------------------------------------
1 | import { createContext, Context } from 'react';
2 |
3 | export interface IndexBarContextState {
4 | zIndex?: number | string;
5 | highlightColor?: string;
6 | sticky?: boolean;
7 | }
8 |
9 | const IndexBarContext: Context = createContext({});
10 |
11 | export default IndexBarContext;
12 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/IndexBar/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: IndexBar 索引栏
3 | desc: 用于列表的索引分类显示和快速定位。
4 | demo: /index-bar
5 | nav:
6 | path: /
7 |
8 | group:
9 | title: 导航组件
10 | ---
11 |
12 | ## 基础用法
13 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/IndexBar/index.ts:
--------------------------------------------------------------------------------
1 | import _IndexBar from './IndexBar';
2 | import IndexAnchor from './IndexAnchor';
3 |
4 | const IndexBar = Object.assign(_IndexBar, { Anchor: IndexAnchor });
5 |
6 | export default IndexBar;
7 | export { IndexBar, IndexAnchor };
8 | export type { IndexBarProps, IndexBarInstance, IndexAnchorProps } from './types';
9 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Input/Input.tsx:
--------------------------------------------------------------------------------
1 | import React, { forwardRef } from 'react';
2 | import BaseInput from './BaseInput';
3 | import type { InputProps, InputInstance } from './type';
4 |
5 | const Input = forwardRef((props, ref) => (
6 |
7 | ));
8 |
9 | export default Input;
10 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Input/TextArea.tsx:
--------------------------------------------------------------------------------
1 | import React, { forwardRef } from 'react';
2 | import { View, Text } from 'react-native';
3 | import isFunction from 'lodash-es/isFunction';
4 | import { useControllableValue } from '../hooks';
5 | import { useThemeFactory } from '../Theme';
6 | import BaseInput from './BaseInput';
7 | import { createTextAreaStyle } from './style';
8 | import type { TextAreaProps, InputInstance } from './type';
9 |
10 | const TextArea = forwardRef((props, ref) => {
11 | const { showWordLimit = false, maxLength, rows = 2 } = props;
12 | const [value, setValue] = useControllableValue(props);
13 | const { styles } = useThemeFactory(createTextAreaStyle);
14 |
15 | const renderWordLimit = () => {
16 | const count = (value ? `${value}` : '').length;
17 |
18 | if (isFunction(showWordLimit)) return showWordLimit({ currentCount: count, maxLength });
19 |
20 | if (maxLength) return `${count}/${maxLength}`;
21 |
22 | return null;
23 | };
24 |
25 | return (
26 |
27 |
28 | {!!showWordLimit && {renderWordLimit()}}
29 |
30 | );
31 | });
32 |
33 | export default TextArea;
34 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Input/index.ts:
--------------------------------------------------------------------------------
1 | import _Input from './Input';
2 | import TextArea from './TextArea';
3 |
4 | const Input = Object.assign(_Input, { TextArea });
5 |
6 | export default Input;
7 | export { Input };
8 | export type {
9 | InputProps,
10 | InputInstance,
11 | TextAreaProps,
12 | InputTextAlign,
13 | InputSharedProps,
14 | } from './type';
15 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/KeyboardSpace/index.ts:
--------------------------------------------------------------------------------
1 | import KeyboardSpace from './KeyboardSpace';
2 |
3 | export default KeyboardSpace;
4 | export { KeyboardSpace };
5 | export type { KeyboardSpaceProps } from './KeyboardSpace';
6 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Layout/Col.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC, useContext } from 'react';
2 | import { View, ViewStyle } from 'react-native';
3 | import RowContext from './RowContext';
4 |
5 | interface Props {
6 | /**
7 | * 列元素宽度
8 | */
9 | span?: number;
10 | /**
11 | * 列元素偏移距离
12 | */
13 | offset?: number;
14 | /**
15 | * 组件样式
16 | */
17 | style?: ViewStyle;
18 | }
19 |
20 | const getPercent = (count?: number) => (count ? `${(count / 24) * 100}%` : undefined);
21 |
22 | const LayoutCol: FC = ({ span, offset, style, children }) => {
23 | const { gutter } = useContext(RowContext);
24 |
25 | const mergedStyle: ViewStyle = gutter
26 | ? {
27 | paddingLeft: gutter / 2,
28 | paddingRight: gutter / 2,
29 | }
30 | : {};
31 |
32 | return (
33 |
34 | {children}
35 |
36 | );
37 | };
38 |
39 | LayoutCol.displayName = 'Layout.Col';
40 |
41 | export default LayoutCol;
42 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Layout/RowContext.ts:
--------------------------------------------------------------------------------
1 | import { createContext } from 'react';
2 |
3 | export interface RowContextState {
4 | gutter?: number;
5 | }
6 |
7 | const RowContext = createContext({});
8 |
9 | export default RowContext;
10 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Layout/index.ts:
--------------------------------------------------------------------------------
1 | import LayoutRow from './Row';
2 | import LayoutCol from './Col';
3 |
4 | export interface LayoutProps {
5 | Row: typeof LayoutRow;
6 | Col: typeof LayoutCol;
7 | }
8 |
9 | export const Layout: LayoutProps = { Row: LayoutRow, Col: LayoutCol };
10 |
11 | export default Layout;
12 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/LinearGradient/index.ts:
--------------------------------------------------------------------------------
1 | import LinearGradient from './LinearGradient';
2 | import type { LinearGradientProps } from './types';
3 |
4 | export default LinearGradient;
5 | export { LinearGradient, LinearGradientProps };
6 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/LinearGradient/types.ts:
--------------------------------------------------------------------------------
1 | import type { ViewProps, ColorValue } from 'react-native';
2 |
3 | export interface LinearGradientProps extends ViewProps {
4 | colors: ColorValue[];
5 | start?: { x: number; y: number };
6 | end?: { x: number; y: number };
7 | locations?: number[];
8 | }
9 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Loading/Loading.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC, memo } from 'react';
2 | import { View, Text } from 'react-native';
3 | import { useThemeFactory } from '../Theme';
4 | import Circular from './Circular';
5 | import Spinner from './Spinner';
6 | import type { LoadingProps } from './type';
7 | import { createStyle } from './style';
8 |
9 | const Loading: FC = props => {
10 | const { styles, theme } = useThemeFactory(createStyle);
11 | const {
12 | children,
13 | size = 30,
14 | type = 'circular',
15 | vertical,
16 | textColor,
17 | textSize,
18 | style,
19 | color = theme.gray_5,
20 | ...rest
21 | } = props;
22 |
23 | return (
24 |
28 | {type === 'circular' ? (
29 |
30 | ) : (
31 |
32 | )}
33 | {children && (
34 |
40 | {children}
41 |
42 | )}
43 |
44 | );
45 | };
46 |
47 | export default memo(Loading);
48 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Loading/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Loading 加载
3 | desc: 加载图标,用于表示加载中的过渡状态。
4 | demo: /loading
5 |
6 | nav:
7 | path: /
8 |
9 | group:
10 | title: 反馈组件
11 | ---
12 |
13 | ## 加载类型
14 |
15 | 通过 `type` 属性可以设置加载图标的类型,默认为 `circular`,可选值为 `spinner`。
16 |
17 | ```jsx
18 |
19 |
20 |
21 | ```
22 |
23 | ## 自定义颜色
24 |
25 | 通过 `color` 属性设置加载图标的颜色。
26 |
27 | ```jsx
28 |
29 |
30 |
31 | ```
32 |
33 | ## 单元格大小
34 |
35 | 通过 size 属性可以控制单元格的大小。
36 |
37 | ```jsx
38 | |
39 | |
40 | ```
41 |
42 | ## 自定义大小
43 |
44 | 通过 `size` 属性设置加载图标的大小。
45 |
46 | ```jsx
47 |
48 |
49 |
50 | ```
51 |
52 | ## 加载文案
53 |
54 | ```jsx
55 |
56 | 加载中...
57 |
58 | ```
59 |
60 | ## 垂直排列
61 |
62 | 设置 `vertical` 属性后,图标和文案会垂直排列。
63 |
64 | ```jsx
65 |
66 | 加载中...
67 |
68 | ```
69 |
70 | ## 自定义文案颜色
71 |
72 | 通过 `color` 或者 `textColor` 属性设置加载文案的颜色。
73 |
74 | ```jsx
75 |
76 | 加载中...
77 |
78 |
79 | 加载中...
80 |
81 | ```
82 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Loading/index.ts:
--------------------------------------------------------------------------------
1 | import Loading from './Loading';
2 |
3 | export default Loading;
4 | export { Loading };
5 | export type { LoadingProps } from './type';
6 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Loading/style.ts:
--------------------------------------------------------------------------------
1 | import { StyleSheet } from 'react-native';
2 |
3 | export const createStyle = (theme: DiceUI.Theme) => {
4 | const textFontSize = theme.font_size_md;
5 |
6 | return StyleSheet.create({
7 | text: {
8 | fontSize: textFontSize,
9 | marginLeft: theme.padding_xs,
10 | },
11 | verticalText: {
12 | fontSize: textFontSize,
13 | marginTop: theme.padding_xs,
14 | },
15 | });
16 | };
17 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Loading/type.ts:
--------------------------------------------------------------------------------
1 | import type { ViewProps } from 'react-native';
2 |
3 | export type LoadingType = 'spinner' | 'circular';
4 |
5 | export interface LoadingProps extends ViewProps {
6 | /**
7 | * 颜色
8 | * @default #c8c9cc
9 | */
10 | color?: string;
11 | /**
12 | * 加载图标大小
13 | * @default 30
14 | */
15 | size?: number;
16 | /**
17 | * 类型
18 | * @default circular
19 | */
20 | type?: LoadingType;
21 | /**
22 | * 文字大小
23 | */
24 | textSize?: number;
25 | /**
26 | * 文字颜色
27 | * @default #c8c9cc
28 | */
29 | textColor?: string;
30 | /**
31 | * 是否垂直排列图标和文字内容
32 | * @default false
33 | */
34 | vertical?: boolean;
35 | }
36 |
37 | export interface LoadingIconProps {
38 | color: string;
39 | size: number;
40 | }
41 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/NavBar/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: NavBar 导航栏
3 | desc: 为页面提供导航功能,常用于页面顶部。
4 | demo: /nav-bar
5 |
6 | nav:
7 | path: /
8 |
9 | group:
10 | title: 导航组件
11 | ---
12 |
13 | ### 基础用法
14 |
15 | ```jsx
16 | Toast.message('返回')}
22 | onPressRight={() => Toast.message('按钮')}
23 | />
24 | ```
25 |
26 | ### 自定义内容
27 |
28 | 自定义导航栏两侧的内容。
29 |
30 | ```jsx
31 | Toast.message('返回')}
36 | rightText={}
37 | onPressRight={() => Toast.message('按钮')}
38 | />
39 | ```
40 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/NavBar/index.ts:
--------------------------------------------------------------------------------
1 | import NavBar from './NavBar';
2 |
3 | export default NavBar;
4 | export { NavBar };
5 | export type { NavBarProps } from './interface';
6 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/NavBar/interface.ts:
--------------------------------------------------------------------------------
1 | import type { ViewStyle, StyleProp, TextStyle } from 'react-native';
2 |
3 | export type LoadingType = 'circular' | 'spinner';
4 |
5 | export interface NavBarProps {
6 | /**
7 | * 最外层的样式
8 | */
9 | style?: StyleProp;
10 |
11 | /**
12 | * 左箭头样式
13 | */
14 | leftArrowStyle?: StyleProp;
15 |
16 | /**
17 | * 标题样式
18 | */
19 | titleTextStyle?: StyleProp;
20 | /**
21 | * 标题
22 | */
23 | title?: React.ReactNode;
24 | /**
25 | * 左侧文案
26 | */
27 | leftText?: React.ReactNode;
28 | /**
29 | * 右侧文案
30 | */
31 | rightText?: React.ReactNode;
32 | /**
33 | * 是否显示左侧箭头
34 | * @default true
35 | */
36 | leftArrow?: boolean;
37 | /**
38 | * 是否开启顶部安全区适配
39 | */
40 | safeAreaInsetTop?: boolean;
41 | /**
42 | * 是否显示下边框
43 | * @default true
44 | */
45 | border?: boolean;
46 | onPressLeft?: () => void;
47 | onPressRight?: () => void;
48 | }
49 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/NoticeBar/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: NoticeBar 通知栏
3 | desc: 用于循环播放展示一组消息通知。
4 | demo: /notice-bar
5 |
6 | nav:
7 | path: /
8 |
9 | group:
10 | title: 展示组件
11 | ---
12 |
13 | ### 基础用法
14 |
15 | 通过 `text` 属性设置通知栏的内容,通过 `leftIcon` 属性设置通知栏左侧的图标。
16 |
17 | ```jsx
18 |
19 | ```
20 |
21 | ### 滚动播放
22 |
23 | 通知栏的内容长度溢出时会自动打点,通过 `scrollable` 属性可以控制内容长度溢出时自动滚动。
24 |
25 | ```jsx
26 |
27 | ```
28 |
29 | ### 多行展示
30 |
31 | 文字较长时,可以通过设置 `wrapable` 属性来开启多行展示。
32 |
33 | ```jsx
34 |
35 | ```
36 |
37 | ### 通知栏模式
38 |
39 | 通知栏支持 `closeable` 和 `link` 两种模式。
40 |
41 | ```jsx
42 |
43 | 技术是开发它的人的共同灵魂。
44 |
45 |
46 | 技术是开发它的人的共同灵魂。
47 | ```
48 |
49 | ### 自定义样式
50 |
51 | 通过 `color` 属性设置文本颜色,通过 `background` 属性设置背景色。
52 |
53 | ```jsx
54 |
60 | ```
61 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/NoticeBar/index.ts:
--------------------------------------------------------------------------------
1 | import NoticeBar from './NoticeBar';
2 |
3 | export default NoticeBar;
4 | export { NoticeBar };
5 | export type { NoticeBarProps, NoticeBarMode, NoticeBarInstance } from './interface';
6 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/NoticeBar/interface.ts:
--------------------------------------------------------------------------------
1 | import type React from 'react';
2 | import type { ViewStyle, StyleProp } from 'react-native';
3 |
4 | export type NoticeBarMode = '' | 'closeable' | 'link';
5 |
6 | export type NoticeBarInstance = {
7 | /** 重置通知栏到初始状态 */
8 | reset: () => void;
9 | };
10 |
11 | export interface NoticeBarProps {
12 | /**
13 | * 最外层的样式
14 | */
15 | style?: StyleProp;
16 |
17 | /**
18 | * 通知栏模式
19 | */
20 | mode?: NoticeBarMode;
21 | /**
22 | * 左侧图标名称或图片链接
23 | */
24 | leftIcon?: string | React.ReactNode;
25 | rightIcon?: string | React.ReactNode;
26 | /**
27 | * 滚动条背景
28 | */
29 | background?: string;
30 | /**
31 | * 通知文本颜色
32 | */
33 | color?: string;
34 | /**
35 | * 通知文本内容
36 | */
37 | text?: React.ReactNode;
38 | /**
39 | * 是否开启滚动播放,内容长度溢出时默认开启
40 | */
41 | scrollable?: boolean;
42 | /**
43 | * 是否开启文本换行,只在禁用滚动时生效
44 | */
45 | wrapable?: boolean;
46 | /**
47 | * 滚动速率 (px/s)
48 | * @default 60
49 | */
50 | speed?: number;
51 | /**
52 | * 动画延迟时间 (ms)
53 | * @default 1000
54 | */
55 | delay?: number;
56 | /**
57 | * 关闭通知栏时触发
58 | */
59 | onClose?: () => void;
60 | /**
61 | * 点击通知栏时触发
62 | */
63 | onPress?: () => void;
64 | /**
65 | * 每当滚动栏重新开始滚动时触发
66 | */
67 | onReplay?: () => void;
68 | children?: React.ReactNode;
69 | }
70 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/NoticeBar/style.ts:
--------------------------------------------------------------------------------
1 | import { StyleSheet } from 'react-native';
2 |
3 | export const createStyle = (theme: DiceUI.Theme) => {
4 | return StyleSheet.create({
5 | content: {
6 | color: theme.notice_bar_text_color,
7 | fontSize: theme.notice_bar_font_size,
8 | },
9 |
10 | leftIcon: {
11 | minWidth: theme.notice_bar_icon_min_width,
12 | },
13 |
14 | noWrapable: {
15 | height: theme.notice_bar_height,
16 | paddingHorizontal: theme.notice_bar_padding_horizontal,
17 | paddingVertical: theme.notice_bar_padding_vertical,
18 | },
19 |
20 | rightIcon: {
21 | justifyContent: 'flex-end',
22 | minWidth: theme.notice_bar_icon_min_width,
23 | },
24 |
25 | wrap: {
26 | flex: 1,
27 | overflow: 'hidden',
28 | },
29 |
30 | wrapable: {
31 | paddingHorizontal: theme.notice_bar_wrapable_padding_horizontal,
32 | paddingVertical: theme.notice_bar_wrapable_padding_vertical,
33 | },
34 |
35 | wrapper: {
36 | alignItems: 'center',
37 | backgroundColor: theme.notice_bar_background_color,
38 | flexDirection: 'row',
39 | overflow: 'hidden',
40 | },
41 | });
42 | };
43 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Notify/style.ts:
--------------------------------------------------------------------------------
1 | import { StyleSheet } from 'react-native';
2 | import constants from '../utils/constants';
3 |
4 | export const createStyle = (theme: DiceUI.Theme) => {
5 | return StyleSheet.create({
6 | danger: {
7 | backgroundColor: theme.notify_danger_background_color,
8 | },
9 | notify: {
10 | alignItems: 'center',
11 | flexDirection: 'row',
12 | justifyContent: 'center',
13 | paddingHorizontal: theme.notify_padding_horizontal,
14 | paddingVertical: theme.notify_padding_vertical,
15 | width: constants.screenWidth,
16 | },
17 | primary: {
18 | backgroundColor: theme.notify_primary_background_color,
19 | },
20 | success: {
21 | backgroundColor: theme.notify_success_background_color,
22 | },
23 | text: {
24 | color: theme.notify_text_color,
25 | fontSize: theme.notify_font_size,
26 | lineHeight: theme.notify_line_height,
27 | },
28 | warning: {
29 | backgroundColor: theme.notify_warning_background_color,
30 | },
31 | });
32 | };
33 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/NumberKeyboard/SlideAnimatable.tsx:
--------------------------------------------------------------------------------
1 | import React, { useRef } from 'react';
2 | import type { View, ViewProps } from 'react-native';
3 | import * as Animatable from 'react-native-animatable';
4 | import { useUpdateEffect, useLayout } from '../hooks';
5 |
6 | interface SlideAnimatableProps extends Pick {
7 | visible: boolean;
8 | duration: number;
9 | }
10 |
11 | const SlideAnimatable: React.FC = ({
12 | children,
13 | style,
14 | visible,
15 | duration,
16 | }) => {
17 | const handleViewRef = useRef(null);
18 | const [layout, onLayout] = useLayout();
19 |
20 | useUpdateEffect(() => {
21 | if (visible) {
22 | handleViewRef.current?.transition?.(
23 | { translateY: layout.height },
24 | { translateY: 0 },
25 | duration
26 | );
27 | } else {
28 | handleViewRef.current?.transitionTo?.({ translateY: layout.height }, duration);
29 | }
30 | }, [visible]);
31 |
32 | return (
33 |
34 | {children}
35 |
36 | );
37 | };
38 |
39 | export default SlideAnimatable;
40 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/NumberKeyboard/index.ts:
--------------------------------------------------------------------------------
1 | import NumberKeyboard from './NumberKeyboard';
2 |
3 | export default NumberKeyboard;
4 | export { NumberKeyboard };
5 | export type { KeyType, NumberKeyboardTheme, KeyConfig, NumberKeyboardProps } from './types';
6 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Overlay/index.ts:
--------------------------------------------------------------------------------
1 | import Overlay from './Overlay';
2 |
3 | export default Overlay;
4 | export { Overlay };
5 | export type { OverlayProps } from './type';
6 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Overlay/style.ts:
--------------------------------------------------------------------------------
1 | import { StyleSheet } from 'react-native';
2 |
3 | export const createStyle = (theme: DiceUI.Theme) => {
4 | return StyleSheet.create({
5 | backdrop: {
6 | ...StyleSheet.absoluteFillObject,
7 | backgroundColor: theme.overlay_background_color,
8 | },
9 | container: {
10 | ...StyleSheet.absoluteFillObject,
11 | alignItems: 'center',
12 | justifyContent: 'center',
13 | },
14 | });
15 | };
16 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Overlay/type.ts:
--------------------------------------------------------------------------------
1 | import type React from 'react';
2 | import type { StyleProp, ViewStyle } from 'react-native';
3 |
4 | export type OverlayProps = {
5 | // 是否展示遮罩层
6 | visible?: boolean;
7 | // 背景容器的样式
8 | backdropStyle?: StyleProp;
9 | // 叠加在背景容器上的组件的样式
10 | overlayStyle?: StyleProp;
11 | // 背景是否透明
12 | transparent?: boolean;
13 | // 动画时长
14 | duration?: number;
15 | children?: React.ReactNode;
16 | // 点击背景容器时触发的事件
17 | onBackdropPress?: () => void;
18 | onPress?: () => void;
19 | // 动画结束后触发的事件
20 | onFadeDone?: () => void;
21 | };
22 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/PanningViews/asPanViewConsumer.tsx:
--------------------------------------------------------------------------------
1 | import React, { Component, Ref } from 'react';
2 | import PanningContext from './PanningContext';
3 |
4 | function asPanViewConsumer(
5 | WrappedComponent: React.ComponentType
6 | ): React.ComponentClass {
7 | class PanViewConsumer extends Component {
8 | contentRef: any;
9 |
10 | saveRef = (r: Ref>) => {
11 | this.contentRef = r;
12 | };
13 |
14 | render() {
15 | return (
16 |
17 | {context => }
18 |
19 | );
20 | }
21 | }
22 |
23 | return PanViewConsumer as any;
24 | }
25 |
26 | export default asPanViewConsumer;
27 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/PanningViews/index.ts:
--------------------------------------------------------------------------------
1 | export { default as PanDismissibleView } from './PanDismissibleView';
2 | export { default as PanGestureView } from './PanGestureView';
3 | export { default as PanListenerView } from './PanListenerView';
4 | export { default as PanResponderView } from './PanResponderView';
5 | export { default as PanningContext } from './PanningContext';
6 | export { default as PanningProvider } from './PanningProvider';
7 |
8 | export type {
9 | PanningDirections,
10 | PanLocationProps,
11 | PanDirectionsProps,
12 | PanningContextState,
13 | PanAmountsProps,
14 | } from './PanningContext';
15 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/PasswordInput/PasswordInput.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { View } from 'react-native';
3 | import { useThemeFactory } from '../Theme';
4 | import type { PasswordInputProps, PasswordInputInstance } from './types';
5 | import { createStyle } from './style';
6 |
7 | const PasswordInput = React.forwardRef(() => {
8 | const { styles } = useThemeFactory(createStyle);
9 |
10 | return ;
11 | });
12 |
13 | export default PasswordInput;
14 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/PasswordInput/index.ts:
--------------------------------------------------------------------------------
1 | import PasswordInput from './PasswordInput';
2 |
3 | export { PasswordInput };
4 | export type { PasswordInputProps } from './types';
5 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/PasswordInput/style.ts:
--------------------------------------------------------------------------------
1 | import { StyleSheet, ViewStyle } from 'react-native';
2 | import type { Vars } from '../styles';
3 |
4 | export const passwordInputDefaultVars = (vars: Vars) => ({
5 | password_input_height: 50,
6 | password_input_margin_vertical: 0,
7 | password_input_margin_horizontal: vars.padding_md,
8 | password_input_font_size: 20,
9 | password_input_border_radius: 6,
10 | password_input_background_color: vars.white,
11 | password_input_info_color: vars.gray_6,
12 | password_input_info_font_size: vars.font_size_md,
13 | password_input_error_info_color: vars.danger_color,
14 | password_input_dot_size: 10,
15 | password_input_dot_color: vars.black,
16 | password_input_text_color: vars.text_color,
17 | password_input_cursor_color: vars.text_color,
18 | password_input_cursor_width: 1,
19 | password_input_cursor_height: '40%',
20 | password_input_cursor_animation_duration: 1,
21 | password_input_item_border_radius: 0,
22 | });
23 |
24 | interface Styles {
25 | wrapper: ViewStyle;
26 | }
27 |
28 | export const createStyle = (theme: DiceUI.Theme): Styles => {
29 | return StyleSheet.create({
30 | wrapper: {
31 | marginHorizontal: theme.password_input_margin_horizontal,
32 | marginVertical: theme.password_input_margin_vertical,
33 | position: 'relative',
34 | },
35 | });
36 | };
37 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/PasswordInput/types.ts:
--------------------------------------------------------------------------------
1 | import type React from 'react';
2 |
3 | export interface PasswordInputProps {
4 | /** 默认值 */
5 | value?: string;
6 | /** 输入框下方文字提示 */
7 | info?: React.ReactNode;
8 | /** 输入框下方错误提示 */
9 | errorInfo?: React.ReactNode;
10 | /** 输入框格子之间的间距 */
11 | gutter?: number;
12 | /** 输入框类型, ['number', 'text] */
13 | type?: string;
14 | /** 密码最大长度 */
15 | length?: number;
16 | /** 自动聚焦 */
17 | autoFocus?: boolean;
18 | /** 是否隐藏密码内容 */
19 | mask?: boolean;
20 | /** 输入校验规则 */
21 | validator?: () => void;
22 | /** 高亮样式 */
23 | highlightClass?: string;
24 | /** 密码change事件 */
25 | onChange?: (v: string) => void;
26 | /** 密码提交事件(位数满了自动提交) */
27 | onSubmit?: (v: string) => void;
28 | /** 输入框聚焦时触发 */
29 | onFocus?: (e: React.FocusEvent) => void;
30 | onBlur?: (e: React.FocusEvent) => void;
31 | }
32 |
33 | export type PasswordInputInstance = {
34 | focus: () => void;
35 | blur: () => void;
36 | clear: () => void;
37 | };
38 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Picker/index.ts:
--------------------------------------------------------------------------------
1 | import Picker from './Picker';
2 |
3 | export { Picker };
4 | export default Picker;
5 | export type { PickerProps, PickerOption, PickerFieldNames, PickerToolbarPosition } from './type';
6 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Popover/index.ts:
--------------------------------------------------------------------------------
1 | import Popover from './Popover';
2 |
3 | export { Popover };
4 | export type {
5 | PopoverProps,
6 | PopoverPlacement,
7 | PopoverTheme,
8 | PopoverInstance,
9 | PopoverAction,
10 | } from './type';
11 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Popup/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Popup 弹出层
3 | desc: 弹出层容器,用于展示弹窗、信息提示等内容,支持多个弹出层叠加展示。
4 | demo: /popup
5 |
6 | nav:
7 | path: /
8 |
9 | group:
10 | title: 基础组件
11 | ---
12 |
13 | ## 基础用法
14 |
15 | 通过 `visible` 控制弹出层是否展示。
16 |
17 | ```jsx
18 | import React, { useState } from 'react';
19 | import { Popup } from '@pingtou/rn-vant';
20 |
21 | export default () => {
22 | const [visible, setVisible] = useState(false);
23 |
24 | return (
25 |
26 | demo
27 |
28 | );
29 | }
30 | ```
31 |
32 | ## 弹出位置
33 |
34 | 通过 `position` 属性设置弹出位置,默认居中弹出,可以设置为 `top`、`bottom`、`left`、`right`。
35 |
36 | ```jsx
37 |
38 | demo
39 |
40 | ```
41 |
42 | ## 关闭图标
43 |
44 | 设置 `closeable` 属性后,会在弹出层的右上角显示关闭图标,并且可以通过 `closeIcon` 属性自定义图标,使用 `closeIconPosition` 属性可以自定义图标位置。
45 |
46 | ```jsx
47 |
54 | demo
55 |
56 | ```
57 |
58 | ## 圆角弹窗
59 |
60 | 设置 `round` 属性后,弹窗会根据弹出位置添加不同的圆角样式。
61 |
62 | ```jsx
63 |
64 | demo
65 |
66 | ```
67 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Popup/index.ts:
--------------------------------------------------------------------------------
1 | import Popup from './Popup';
2 |
3 | export { Popup };
4 | export default Popup;
5 | export type { PopupProps, PopupPosition, PopupCloseIconPosition } from './type';
6 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Portal/index.tsx:
--------------------------------------------------------------------------------
1 | export * from '@gorhom/portal';
2 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Progress/Progress.tsx:
--------------------------------------------------------------------------------
1 | import React, { forwardRef, useState } from 'react';
2 | import { View, Text } from 'react-native';
3 | import type { LayoutChangeEvent } from 'react-native';
4 | import { useThemeFactory } from '../Theme';
5 | import type { ProgressProps } from './type';
6 | import { createStyle } from './style';
7 |
8 | const Progress = forwardRef((props, ref) => {
9 | const { style, percentage = 0, showPivot = true, pivotText, ...extra } = props;
10 | const { styles } = useThemeFactory(createStyle, { ...extra, percentage });
11 | const [pivotWidth, setPivotWidth] = useState(0);
12 |
13 | // 获取进度文字的宽度
14 | const onPivotLayout = (event: LayoutChangeEvent) => {
15 | setPivotWidth(event.nativeEvent.layout.width);
16 | };
17 |
18 | return (
19 |
20 |
21 | {showPivot && (
22 |
26 | {pivotText ?? `${percentage}%`}
27 |
28 | )}
29 |
30 | );
31 | });
32 |
33 | Progress.displayName = 'Progress';
34 |
35 | export default Progress;
36 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Progress/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Progress 进度条
3 | desc: 用于展示操作的当前进度。
4 | demo: /progress
5 |
6 | nav:
7 | path: /
8 |
9 | group:
10 | title: 展示组件
11 | ---
12 |
13 | ## 基础用法
14 |
15 | 进度条默认为蓝色,使用 `percentage` 属性来设置当前进度。
16 |
17 | ```jsx
18 |
19 | ```
20 |
21 | ## 线条粗细
22 |
23 | 通过 `strokeWidth` 可以设置进度条的粗细。
24 |
25 | ```jsx
26 |
27 | ```
28 |
29 | ## 置灰
30 |
31 | 设置 `inactive` 属性后进度条将置灰。
32 |
33 | ```jsx
34 |
35 | ```
36 |
37 | ## 样式定制
38 |
39 | 可以使用 `pivotText` 属性自定义文字,`color` 属性自定义进度条颜色。
40 |
41 | ```jsx
42 |
43 |
44 |
50 | ```
51 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Progress/index.ts:
--------------------------------------------------------------------------------
1 | import Progress from './Progress';
2 |
3 | export default Progress;
4 | export { Progress };
5 | export type { ProgressProps } from './type';
6 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Progress/type.ts:
--------------------------------------------------------------------------------
1 | import type { StyleProp, ViewStyle } from 'react-native';
2 |
3 | export interface ProgressProps {
4 | style?: StyleProp;
5 | /**
6 | * 进度条颜色
7 | */
8 | color?: string;
9 | /**
10 | * 是否置灰
11 | */
12 | inactive?: boolean;
13 | /**
14 | * 进度文字内容
15 | */
16 | pivotText?: React.ReactNode;
17 | /**
18 | * 进度文字颜色
19 | */
20 | textColor?: string;
21 | /**
22 | * 是否显示进度文字
23 | */
24 | showPivot?: boolean;
25 | /**
26 | * 进度文字背景色
27 | */
28 | pivotColor?: string;
29 | /**
30 | * 轨道颜色
31 | */
32 | trackColor?: string;
33 | /**
34 | * 进度条粗细
35 | */
36 | strokeWidth?: number;
37 | /**
38 | * 进度百分比
39 | */
40 | percentage?: number;
41 | }
42 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Radio/context.ts:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import type { StyleProp, ViewStyle } from 'react-native';
3 |
4 | export type RadioValueType = string | number | boolean;
5 |
6 | export interface RadioOptionType {
7 | label: React.ReactNode;
8 | value: RadioValueType;
9 | style?: StyleProp;
10 | disabled?: boolean;
11 | onChange?: () => void;
12 | }
13 | export interface RadioGroupContext {
14 | toggleOption?: (option: RadioOptionType) => void;
15 | value?: any;
16 | disabled?: boolean;
17 | }
18 |
19 | export const GroupContext = React.createContext(null);
20 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Radio/index.style.ts:
--------------------------------------------------------------------------------
1 | import { StyleSheet } from 'react-native';
2 |
3 | const createStyle = (theme: DiceUI.Theme) => {
4 | return StyleSheet.create({
5 | icon: {
6 | alignItems: 'center',
7 | borderColor: theme.radio_icon_border_color,
8 | borderWidth: 1,
9 | justifyContent: 'center',
10 | },
11 | iconChecked: {
12 | backgroundColor: theme.radio_checked_icon_color,
13 | borderColor: theme.radio_checked_icon_color,
14 | },
15 | iconDisabled: {
16 | backgroundColor: theme.radio_disabled_background_color,
17 | borderColor: theme.radio_disabled_icon_color,
18 | },
19 | label: {
20 | color: theme.radio_label_color,
21 | },
22 | labelContainer: {
23 | marginLeft: theme.radio_label_margin,
24 | },
25 | labelDisabled: {
26 | color: theme.radio_disabled_label_color,
27 | },
28 | radio: {
29 | alignItems: 'center',
30 | flexDirection: 'row',
31 | },
32 | });
33 | };
34 |
35 | export default createStyle;
36 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Radio/index.ts:
--------------------------------------------------------------------------------
1 | import InternalRadio from './Radio';
2 | import Group from './Group';
3 |
4 | export type { RadioProps } from './Radio';
5 | export type { RadioGroupProps } from './Group';
6 | export type { RadioOptionType } from './context';
7 |
8 | type RadioType = typeof InternalRadio;
9 |
10 | interface RadioProps extends RadioType {
11 | Group: typeof Group;
12 | __ANT_CHECKBOX: boolean;
13 | }
14 |
15 | export const Radio = InternalRadio as RadioProps;
16 |
17 | Radio.Group = Group;
18 | Radio.__ANT_CHECKBOX = true;
19 |
20 | export default Radio;
21 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Rate/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Rate 评分
3 | desc: 用于对事物进行评级操作。
4 | demo: /rate
5 |
6 | nav:
7 | path: /
8 |
9 | group:
10 | title: 表单组件
11 | ---
12 |
13 | ## 基础用法
14 |
15 | 通过 `value` 来绑定当前评分值。
16 |
17 | ```jsx
18 |
19 | ```
20 |
21 | ```jsx
22 | export default () => {
23 | const [value, setValue] = useState(3);
24 | return setValue(current)} />;
25 | };
26 | ```
27 |
28 | ## 自定义图标
29 |
30 | 通过 `icon` 属性设置选中时的图标,`voidIcon` 属性设置未选中时的图标。
31 |
32 | ```jsx
33 |
34 | ```
35 |
36 | ## 自定义样式
37 |
38 | 通过 `size` 属性设置图标大小,`color` 属性设置选中时的颜色,`voidColor` 设置未选中时的颜色。
39 |
40 | ```jsx
41 |
42 | ```
43 |
44 | ## 半星
45 |
46 | 设置 `allowHalf` 属性后可以选中半星。
47 |
48 | ```jsx
49 |
50 | ```
51 |
52 | ## 自定义数量
53 |
54 | 通过 `count` 属性设置评分总数。
55 |
56 | ```jsx
57 |
58 | ```
59 |
60 | ## 禁用状态
61 |
62 | 通过 `disabled` 属性来禁用评分。
63 |
64 | ```jsx
65 |
66 | ```
67 |
68 | ## 只读状态显示小数
69 |
70 | 设置 `readonly` 和 `allowHalf` 属性后,Rate 组件可以展示任意小数结果。
71 |
72 | ```jsx
73 |
74 | ```
75 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Rate/index.ts:
--------------------------------------------------------------------------------
1 | import Rate from './Rate';
2 |
3 | export default Rate;
4 | export { Rate };
5 | export type { RateProps } from './type';
6 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Rate/style.ts:
--------------------------------------------------------------------------------
1 | import { StyleSheet } from 'react-native';
2 | import type { ViewStyle } from 'react-native';
3 |
4 | interface Styles {
5 | wrapper: ViewStyle;
6 | item: ViewStyle;
7 | half: ViewStyle;
8 | }
9 |
10 | export const createStyle = (): Styles => {
11 | return StyleSheet.create({
12 | half: {
13 | left: 0,
14 | overflow: 'hidden',
15 | position: 'absolute',
16 | top: 0,
17 | },
18 |
19 | item: {
20 | position: 'relative',
21 | },
22 |
23 | wrapper: {
24 | alignItems: 'center',
25 | flexDirection: 'row',
26 | },
27 | });
28 | };
29 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Rate/type.ts:
--------------------------------------------------------------------------------
1 | import type { StyleProp, ViewStyle } from 'react-native';
2 | import type { IconNames } from '@pingtou/rn-vant-icons';
3 |
4 | export interface RateProps {
5 | /**
6 | * 图标大小,默认单位为px
7 | * @default 20
8 | */
9 | size?: number;
10 | /**
11 | * 选中时的颜色
12 | * @default 'f44336'
13 | */
14 | color?: string;
15 | /**
16 | * 图标间距,默认单位为px
17 | * @default 4
18 | */
19 | gutter?: number;
20 | /**
21 | * 是否为只读状态,只读状态下无法修改评分
22 | */
23 | readonly?: boolean;
24 | /** 是否禁用评分 */
25 | disabled?: boolean;
26 | /** 是否允许半选 */
27 | allowHalf?: boolean;
28 | /** 未选中时的颜色 */
29 | voidColor?: string;
30 | /**
31 | * 是否可以通过滑动手势选择评分
32 | * @default true
33 | */
34 | touchable?: boolean;
35 | /** 禁用时的颜色 */
36 | disabledColor?: string;
37 | /** 当前分值 */
38 | value?: number;
39 | /** 默认分值 */
40 | defaultValue?: number;
41 | /** 当前分值变化时触发的事件 */
42 | onChange?: (v: number) => void;
43 | /**
44 | * 选中时的图标名称或图片链接
45 | * @default 'star'
46 | */
47 | icon?: IconNames;
48 | /**
49 | * 未选中时的图标名称或图片链接
50 | * @default 'star-o'
51 | */
52 | voidIcon?: IconNames;
53 | /**
54 | * 图标总数
55 | * @default 5
56 | */
57 | count?: number;
58 | style?: StyleProp;
59 | }
60 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Search/index.ts:
--------------------------------------------------------------------------------
1 | import Search from './Search';
2 |
3 | export default Search;
4 | export { Search };
5 | export type { SearchProps, SearchInstance } from './types';
6 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Search/types.ts:
--------------------------------------------------------------------------------
1 | import type React from 'react';
2 | import type { FieldCommonProps } from '../Field/type';
3 |
4 | export interface SearchProps extends FieldCommonProps {
5 | /** 搜索框左侧文本 */
6 | label?: React.ReactNode;
7 | /** 取消按钮文字 */
8 | actionText?: React.ReactNode;
9 | /** 搜索框外部背景色 */
10 | background?: string;
11 | /** 是否在搜索框右侧显示取消按钮 */
12 | showAction?: boolean;
13 | /** 搜索框形状,可选值为 round */
14 | action?: React.ReactNode;
15 | shape?: 'square' | 'round';
16 | /** 确定搜索时触发 */
17 | onSearch?: (val: string) => void;
18 | /** 点击取消按钮时触发 */
19 | onCancel?: () => void;
20 | }
21 |
22 | export type SearchInstance = {
23 | /** 获取输入框焦点 */
24 | focus?: (e: React.MouseEvent) => void;
25 | /** 取消输入框焦点 */
26 | blur?: (e: React.MouseEvent) => void;
27 | };
28 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Selector/CheckMark.tsx:
--------------------------------------------------------------------------------
1 | import React, { memo } from 'react';
2 | import Svg, { SvgProps, G, Polyline } from 'react-native-svg';
3 |
4 | export const CheckMark = memo((props: SvgProps) => (
5 |
25 | ));
26 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Selector/index.ts:
--------------------------------------------------------------------------------
1 | import Selector from './Selector';
2 |
3 | export default Selector;
4 | export { Selector };
5 | export type { SelectorValue, SelectorProps, SelectorOption } from './type';
6 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Selector/type.ts:
--------------------------------------------------------------------------------
1 | import type { ReactNode } from 'react';
2 | import type { StyleProp, ViewStyle } from 'react-native';
3 |
4 | export type SelectorValue = string | number;
5 |
6 | export interface SelectorOption {
7 | // 文字
8 | label: ReactNode;
9 | // 描述
10 | description?: ReactNode;
11 | // 选项的值
12 | value: V;
13 | // 是否禁用
14 | disabled?: boolean;
15 | }
16 |
17 | export type SelectorProps = {
18 | // 可选项
19 | options: SelectorOption[];
20 | // 行展示数
21 | columns?: number;
22 | // 是否允许多选
23 | multiple?: boolean;
24 | // 是否全局禁止选中
25 | disabled?: boolean;
26 | // 默认项
27 | defaultValue?: V[];
28 | // 选中项
29 | value?: V[];
30 | // 选项改变时触发
31 | onChange?: (v: V[], extend: { items: SelectorOption[] }) => void;
32 | // 是否显示对勾角标
33 | showCheckMark?: boolean;
34 | style?: StyleProp;
35 | };
36 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Slider/index.tsx:
--------------------------------------------------------------------------------
1 | import Slider from './Slider';
2 |
3 | export default Slider;
4 | export { Slider };
5 | export type { SliderProps } from './types';
6 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Space/index.ts:
--------------------------------------------------------------------------------
1 | import Space from './Space';
2 |
3 | export { Space };
4 | export default Space;
5 | export type { SpaceProps } from './type';
6 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Space/style.ts:
--------------------------------------------------------------------------------
1 | import type { FlexAlignType, FlexStyle } from 'react-native';
2 | import type { SpaceProps } from './type';
3 |
4 | export const flexAlign: Record['align'], FlexAlignType> = {
5 | baseline: 'baseline',
6 | center: 'center',
7 | end: 'flex-end',
8 | start: 'flex-start',
9 | };
10 |
11 | export const flexJustify: Record['justify'], FlexStyle['justifyContent']> = {
12 | around: 'space-around',
13 | between: 'space-between',
14 | center: 'center',
15 | end: 'flex-end',
16 | evenly: 'space-evenly',
17 | start: 'flex-start',
18 | stretch: undefined,
19 | };
20 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Space/type.ts:
--------------------------------------------------------------------------------
1 | import type React from 'react';
2 | import type { Pressable, StyleProp, ViewStyle } from 'react-native';
3 |
4 | type PressableProps = React.ComponentProps;
5 |
6 | export interface SpaceProps extends PressableProps {
7 | /** 间距方向
8 | * @default horizontal
9 | */
10 | direction?: 'horizontal' | 'vertical';
11 | /** 交叉轴对齐方式 */
12 | align?: 'start' | 'end' | 'center' | 'baseline';
13 | /** 主轴对齐方式 */
14 | justify?: 'start' | 'end' | 'center' | 'between' | 'around' | 'evenly' | 'stretch';
15 | /** 是否自动换行,仅在 horizontal 时有效 */
16 | wrap?: boolean;
17 | /**
18 | * 间距大小
19 | * 设为数组时则分别设置垂直方向和水平方向的间距大小
20 | * @default 8px
21 | */
22 | gap?: number;
23 | style?: StyleProp;
24 | /** 分隔内容 */
25 | divider?: React.ReactNode;
26 | }
27 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Stepper/index.ts:
--------------------------------------------------------------------------------
1 | import Stepper from './Stepper';
2 |
3 | export { Stepper };
4 | export type { StepperProps, StepperTheme } from './type';
5 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/SwipeCell/index.ts:
--------------------------------------------------------------------------------
1 | import SwipeCell from './SwipeCell';
2 |
3 | export { SwipeCell };
4 | export type { SwipeCellInstance, SwipeCellProps, SwipeCellRenderAction } from './type';
5 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/SwipeCell/type.ts:
--------------------------------------------------------------------------------
1 | import type React from 'react';
2 | import type { StyleProp, ViewStyle } from 'react-native';
3 | import type { SwipeableProps } from 'react-native-gesture-handler/Swipeable';
4 |
5 | export type SwipeCellRenderAction = SwipeableProps['renderLeftActions'];
6 |
7 | export interface SwipeCellProps {
8 | style?: StyleProp;
9 | /** 标识符,可以在事件参数中获取到 */
10 | name?: string | number;
11 | /** 左侧滑动区域的内容 */
12 | leftAction?: React.ReactNode | SwipeCellRenderAction;
13 | /** 右侧滑动区域的内容 */
14 | rightAction?: React.ReactNode | SwipeCellRenderAction;
15 | /** 是否禁用 */
16 | disabled?: boolean;
17 | /** 打开时触发 */
18 | onOpen?: ({ name, position }: { name: string | number; position: SwipeCellSide }) => void;
19 | /** 关闭时触发 */
20 | onClose?: ({ name, position }: { name: string | number; position: SwipeCellSide }) => void;
21 | children?: React.ReactNode;
22 | }
23 |
24 | export type SwipeCellSide = 'left' | 'right';
25 |
26 | export type SwipeCellInstance = {
27 | open: (side: SwipeCellSide) => void;
28 | close: () => void;
29 | };
30 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Swiper/SwiperItem.tsx:
--------------------------------------------------------------------------------
1 | import React, { forwardRef } from 'react';
2 | import { View, Pressable } from 'react-native';
3 |
4 | import type { SwiperItemProps } from './type';
5 |
6 | const SwiperItem = forwardRef((props, ref) => {
7 | const { children, ...rest } = props;
8 |
9 | return (
10 |
11 | {children}
12 |
13 | );
14 | });
15 |
16 | export default SwiperItem;
17 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Swiper/index.ts:
--------------------------------------------------------------------------------
1 | import InternalSwiper from './Swiper';
2 | import SwiperItem from './SwiperItem';
3 |
4 | export const Swiper = Object.assign(InternalSwiper, { Item: SwiperItem });
5 |
6 | export default Swiper;
7 | export type { SwiperInstance, SwiperProps } from './type';
8 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Swiper/type.ts:
--------------------------------------------------------------------------------
1 | import type { PressableProps, ViewProps } from 'react-native';
2 | import type { ReactNode } from 'react';
3 |
4 | export interface SwiperProps extends ViewProps {
5 | /** 滑块宽度 */
6 | width?: number;
7 | /** 滑块高度 */
8 | height?: number;
9 | /** 初始位置索引值 */
10 | initialSwipe?: number;
11 | /** 是否允许手势滑动 */
12 | touchable?: boolean;
13 | /** 自动轮播间隔,单位为 ms */
14 | autoplay?: boolean | number;
15 | /** 是否开启循环播放 */
16 | loop?: boolean;
17 | /** 是否为纵向滚动 */
18 | vertical?: boolean;
19 | /** 每一页轮播结束后触发 */
20 | onChange?: (index: number) => void;
21 | /** 自定义指示器 */
22 | indicator?: boolean | ((total: number, current: number) => ReactNode);
23 | }
24 |
25 | export type SwiperItemProps = PressableProps;
26 |
27 | export type SwiperInstance = {
28 | activeIndex: number;
29 | swipeTo: (index: number) => void;
30 | swipeNext: () => void;
31 | swipePrev: () => void;
32 | };
33 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Switch/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Switch 开关
3 | desc: 用于在打开和关闭状态之间进行切换。
4 | demo: /switch
5 |
6 | nav:
7 | path: /
8 |
9 | group:
10 | title: 基础组件
11 | ---
12 |
13 | ## 基础用法
14 |
15 | 通过 `defaultChecked` 默认开关的选中状态,`true` 表示开,`false` 表示关。
16 |
17 | ```jsx
18 |
19 | ```
20 |
21 | ## 禁用状态
22 |
23 | 通过 `disabled` 属性来禁用开关,禁用状态下开关不可点击。
24 |
25 | ```jsx
26 |
27 | ```
28 |
29 | ## 加载状态
30 |
31 | 通过 `loading` 属性设置开关为加载状态,加载状态下开关不可点击。
32 |
33 | ```jsx
34 |
35 | ```
36 |
37 | ## 自定义大小
38 |
39 | 通过 `size` 属性自定义开关的大小。
40 |
41 | ```jsx
42 |
43 | ```
44 |
45 | ## 自定义颜色
46 |
47 | `activeColor` 属性表示打开时的背景色,`inactiveColor` 表示关闭时的背景色。
48 |
49 | ```jsx
50 |
51 | ```
52 |
53 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Switch/index.ts:
--------------------------------------------------------------------------------
1 | import Switch from './Switch';
2 |
3 | export default Switch;
4 | export { Switch };
5 | export type { SwitchProps } from './interface';
6 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Switch/interface.ts:
--------------------------------------------------------------------------------
1 | import type { ViewStyle, StyleProp } from 'react-native';
2 |
3 | export interface SwitchProps {
4 | /**
5 | * 开关尺寸
6 | */
7 | size?: number;
8 | /**
9 | * 是否为加载状态
10 | */
11 | loading?: boolean;
12 | /**
13 | * 是否为禁用状态
14 | */
15 | disabled?: boolean;
16 | /**
17 | * 开关选中状态
18 | */
19 | checked?: boolean;
20 | /**
21 | * 开关默认选中状态
22 | */
23 | defaultChecked?: boolean;
24 | /**
25 | * 打开时的背景色
26 | */
27 | activeColor?: string;
28 | /**
29 | * 关闭时的背景色
30 | */
31 | inactiveColor?: string;
32 | /**
33 | * 打开时对应的值
34 | * @default true
35 | */
36 | activeValue?: any;
37 | /**
38 | * 关闭时对应的值
39 | * @default false
40 | */
41 | inactiveValue?: any;
42 | /**
43 | * 开关状态切换时触发
44 | */
45 | onChange?: (val: any) => void;
46 | onPress?: () => void;
47 | style?: StyleProp;
48 | }
49 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Tabs/TabPane.tsx:
--------------------------------------------------------------------------------
1 | import React, { forwardRef, useContext, useState, useMemo } from 'react';
2 | import { View } from 'react-native';
3 | import { TabsContext } from './TabsContext';
4 | import type { TabPaneProps } from './type';
5 |
6 | export const TabPane = forwardRef((props, ref) => {
7 | const { children, index, style } = props;
8 | const parent = useContext(TabsContext);
9 | const { animated, swipeable, lazyRender, lazyRenderPlaceholder } = parent.props;
10 |
11 | const active = parent.selectedIndex === index;
12 |
13 | const [inited, setInited] = useState(() => active);
14 |
15 | const isActive = useMemo(() => {
16 | if (active && !inited) {
17 | setInited(true);
18 | }
19 | return active;
20 | }, [active, inited]);
21 |
22 | const show = isActive;
23 | const shouldRender = inited || !lazyRender;
24 | const Content = shouldRender ? children : lazyRenderPlaceholder;
25 |
26 | if (animated || swipeable) {
27 | return {Content};
28 | }
29 |
30 | return (
31 |
32 | {Content}
33 |
34 | );
35 | });
36 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Tabs/TabsContext.ts:
--------------------------------------------------------------------------------
1 | import { createContext } from 'react';
2 | import type { TabsProps } from './type';
3 |
4 | export interface TabsContextState {
5 | props: React.PropsWithChildren;
6 | selectedIndex: number;
7 | setCurrentIndex: (index: number) => void;
8 | }
9 |
10 | export const TabsContext = createContext({} as TabsContextState);
11 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Tabs/index.ts:
--------------------------------------------------------------------------------
1 | import { TabPane } from './TabPane';
2 | import TabInner from './Tabs';
3 |
4 | export const Tabs = Object.assign(TabInner, { TabPane });
5 |
6 | export * from './type';
7 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Tabs/utils.ts:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export function parseChildList } & T>(
4 | children: React.ReactNode
5 | ): R[] {
6 | return React.Children.toArray(children).reduce((result, node) => {
7 | if (React.isValidElement(node)) {
8 | const key = node.key !== undefined ? String(node.key) : undefined;
9 | result.push({
10 | key,
11 | ...node.props,
12 | node,
13 | });
14 | }
15 | return result;
16 | }, []);
17 | }
18 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Tag/index.tsx:
--------------------------------------------------------------------------------
1 | import Tag from './Tag';
2 |
3 | export default Tag;
4 | export { Tag };
5 | export type { TagProps } from './interface';
6 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Theme/index.tsx:
--------------------------------------------------------------------------------
1 | import { useMemo } from 'react';
2 | import { createTheming } from '@callstack/react-theme-provider';
3 | import type { StyleSheet } from 'react-native';
4 | import { defaultTheme } from '../styles';
5 |
6 | export const { ThemeProvider, withTheme, useTheme } = createTheming(
7 | defaultTheme as DiceUI.Theme
8 | );
9 |
10 | type ThemeFactoryCallBack> = {
11 | styles: T;
12 | theme: DiceUI.Theme;
13 | };
14 |
15 | export function useThemeFactory, P>(
16 | fun: (theme: DiceUI.Theme, ...extra: P[]) => T,
17 | ...params: P[]
18 | ): ThemeFactoryCallBack {
19 | const theme = useTheme();
20 | const styles = useMemo(() => fun(theme, ...params), [fun, theme, params]);
21 |
22 | return { styles, theme };
23 | }
24 |
25 | export default {
26 | ThemeProvider,
27 | withTheme,
28 | useTheme,
29 | useThemeFactory,
30 | };
31 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Toast/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Toast 轻提示
3 | desc: 在页面中间弹出黑色半透明提示,用于消息通知、加载提示、操作结果提示等场景。
4 | demo: /toast
5 |
6 | nav:
7 | path: /
8 |
9 | group:
10 | title: 基础组件
11 | ---
12 |
13 | ## 引入
14 |
15 | ```jsx
16 | import { Toast } from '@pingtou/rn-vant';
17 | ```
18 |
19 | ## 文字提示
20 |
21 | ```jsx
22 | Toast.message('提示内容')
23 | ```
24 |
25 | ## 加载提示
26 |
27 | 使用 `Toast.loading` 方法展示加载提示。
28 |
29 | ```jsx
30 | Toast.loading('加载中...')
31 | ```
32 |
33 | ## 成功/失败提示
34 |
35 | 使用 `Toast.success` 方法展示成功提示,使用 `Toast.fail` 方法展示失败提示。
36 |
37 | ```jsx
38 | Toast.success('成功文案');
39 | Toast.fail('失败文案');
40 | ```
41 |
42 | ## 自定义图标
43 |
44 | 通过 `icon` 选项可以自定义图标,支持传入图标名称,通过loadingType 属性可以自定义加载图标类型。
45 |
46 | ```jsx
47 | Toast.show({
48 | text: '自定义图标',
49 | icon: 'like-o',
50 | })
51 |
52 | Toast.loading({
53 | text: '加载中...',
54 | loadingType: 'spinner',
55 | })
56 | ```
57 |
58 | ## 自定义位置
59 |
60 | Toast 默认渲染在屏幕正中位置,通过 `position` 属性可以控制 Toast 展示的位置。
61 |
62 | ```jsx
63 | Toast.message({
64 | text: '提示内容',
65 | position: 'top',
66 | })
67 |
68 | Toast.message({
69 | text: '提示内容',
70 | position: 'bottom',
71 | })
72 | ```
73 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/TouchableOpacity/type.ts:
--------------------------------------------------------------------------------
1 | import type { PressableProps, ViewProps } from 'react-native';
2 |
3 | export interface TouchableOpacityProps extends Omit {
4 | style?: ViewProps['style'];
5 | // 背景颜色
6 | backgroundColor?: string;
7 | // onPress 回调的节流时间,单位 ms
8 | throttleTime?: number;
9 | // 节流函数的参数
10 | throttleOptions?: { leading: boolean; trailing: boolean };
11 | // 按下时的背景颜色
12 | activeBackgroundColor?: string;
13 | // 按下时背景透明度
14 | activeOpacity?: number;
15 | }
16 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/TouchableRipple/index.ts:
--------------------------------------------------------------------------------
1 | import TouchableRipple from './TouchableRipple';
2 |
3 | export default TouchableRipple;
4 | export { TouchableRipple };
5 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Transitions/Fade.tsx:
--------------------------------------------------------------------------------
1 | import React, { memo, forwardRef } from 'react';
2 |
3 | import Transition from './Transition';
4 | import type { FadeProps } from './types';
5 |
6 | const Fade = forwardRef((props, ref) => {
7 | const { children, in: animationState, entryDuration, exitDuration, ...rest } = props;
8 |
9 | return (
10 |
18 | {children}
19 |
20 | );
21 | });
22 |
23 | Fade.defaultProps = {
24 | entryDuration: 500,
25 | exitDuration: 500,
26 | };
27 | Fade.displayName = 'Transitions.Fade';
28 |
29 | export default memo(Fade);
30 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Transitions/ScaleFade.tsx:
--------------------------------------------------------------------------------
1 | import React, { memo, forwardRef } from 'react';
2 |
3 | import type { ScaleFadeProps } from './types';
4 | import Transition from './Transition';
5 |
6 | const ScaleFade = forwardRef((props, ref) => {
7 | const { children, initialScale, duration, in: animationState, ...rest } = props;
8 |
9 | return (
10 |
18 | {children}
19 |
20 | );
21 | });
22 |
23 | ScaleFade.defaultProps = {
24 | duration: 500,
25 | initialScale: 0.9,
26 | };
27 | ScaleFade.displayName = 'Transitions.ScaleFade';
28 |
29 | export default memo(ScaleFade);
30 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Transitions/index.ts:
--------------------------------------------------------------------------------
1 | import Fade from './Fade';
2 | import ScaleFade from './ScaleFade';
3 | import Transition from './Transition';
4 | import SlideFade from './SlideFade';
5 | import Stagger from './Stagger';
6 | import Slide from './Slide';
7 |
8 | export const Transitions = {
9 | Fade,
10 | ScaleFade,
11 | Transition,
12 | SlideFade,
13 | Stagger,
14 | Slide,
15 | };
16 |
17 | export default Transitions;
18 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Typography/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import type { TypographyTextProps, TypographyTitleProps, TypographyLinkProps } from './type';
3 | import Typography from './Typography';
4 |
5 | const Text = (props: TypographyTextProps) => ;
6 | const Title = (props: TypographyTitleProps) => ;
7 | const Link = (props: TypographyLinkProps) => ;
8 |
9 | const TypographyNamespace = Object.assign(Typography, { Text, Title, Link });
10 |
11 | export default TypographyNamespace;
12 | export { TypographyNamespace as Typography };
13 | export type {
14 | TypographyBaseProps as TypographyProps,
15 | TypographySize,
16 | TypographyType,
17 | TypographyTitleLevel,
18 | } from './type';
19 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Typography/type.ts:
--------------------------------------------------------------------------------
1 | import type { TextProps } from 'react-native';
2 |
3 | export type TypographyType = 'danger' | 'secondary' | 'light' | 'primary' | 'success' | 'warning';
4 | export type TypographySize = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl';
5 | export type TypographyTitleLevel = 1 | 2 | 3 | 4 | 5;
6 |
7 | export interface TypographyBaseProps extends TextProps {
8 | type?: TypographyType;
9 | size?: TypographySize;
10 | level?: TypographyTitleLevel;
11 | disabled?: boolean;
12 | delete?: boolean;
13 | underline?: boolean;
14 | center?: boolean;
15 | strong?: boolean;
16 | ellipsis?: boolean | number;
17 | onPress?: () => void;
18 | }
19 |
20 | export type TypographyTextProps = Omit;
21 | export type TypographyTitleProps = TypographyBaseProps;
22 | export type TypographyLinkProps = Omit;
23 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/Uploader/type.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bijinfeng/rn-vant/a465ecb3f06dbdaf084b98455c6812b93f16fc43/packages/rn-vant/src/Uploader/type.ts
--------------------------------------------------------------------------------
/packages/rn-vant/src/View/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Animated, View as RNView, SafeAreaView } from 'react-native';
3 | import type { ViewProps } from './type';
4 |
5 | const View = React.forwardRef((props: ViewProps, ref) => {
6 | const { children, useSafeArea, animated, style, ...rest } = props;
7 |
8 | const Element = useSafeArea ? SafeAreaView : RNView;
9 | const Container: React.ClassType = animated
10 | ? Animated.createAnimatedComponent(Element)
11 | : Element;
12 |
13 | return (
14 |
15 | {children}
16 |
17 | );
18 | });
19 |
20 | export default View;
21 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/View/type.ts:
--------------------------------------------------------------------------------
1 | import type { ViewProps as RNViewProps, StyleProp, ViewStyle, Animated } from 'react-native';
2 |
3 | export interface ViewProps extends Omit {
4 | /**
5 | * 是否开启安全区适配, 为 true 时使用 SafeAreaView
6 | */
7 | useSafeArea?: boolean;
8 | /**
9 | * 是否使用 Animate.View
10 | */
11 | animated?: boolean;
12 | style?: StyleProp>;
13 | }
14 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/hooks/index.ts:
--------------------------------------------------------------------------------
1 | export { default as useMemoizedFn } from './useMemoizedFn';
2 | export { default as useControllableValue } from './useControllableValue';
3 | export { default as useUpdateEffect } from './useUpdateEffect';
4 | export { default as useSetState } from './useSetState';
5 | export { default as useUpdate } from './useUpdate';
6 | export { default as useOrientation } from './useOrientation';
7 | export { default as useScrollTo } from './useScrollTo';
8 | export { default as useRefState } from './useRefState';
9 | export { default as useRefs } from './useRefs';
10 | export { default as useLazyRender } from './useLazyRender';
11 | export { default as useLazyRef } from './useLazyRef';
12 | export { default as useAnimatedValue } from './useAnimatedValue';
13 | export { default as useAnimatedValueArray } from './useAnimatedValueArray';
14 | export { default as useLayout } from './useLayout';
15 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/hooks/useAnimatedValue.ts:
--------------------------------------------------------------------------------
1 | import { Animated } from 'react-native';
2 | import useLazyRef from './useLazyRef';
3 |
4 | const useAnimatedValue = (initialValue: number): Animated.Value => {
5 | const { current } = useLazyRef(() => new Animated.Value(initialValue));
6 |
7 | return current;
8 | };
9 | export default useAnimatedValue;
10 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/hooks/useAnimatedValueArray.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { Animated } from 'react-native';
3 |
4 | export default function useAnimatedValueArray(initialValues: number[]): Animated.Value[] {
5 | const refs = React.useRef([]);
6 |
7 | refs.current.length = initialValues.length;
8 | initialValues.forEach((initialValue, i) => {
9 | refs.current[i] = refs.current[i] ?? new Animated.Value(initialValue);
10 | });
11 |
12 | return refs.current;
13 | }
14 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/hooks/useDestroyed.ts:
--------------------------------------------------------------------------------
1 | import { useRef, useCallback, useEffect } from 'react';
2 |
3 | /**
4 | * 组件是否已经被销毁了
5 | */
6 | const useDestroyed = () => {
7 | const DestroyedRef = useRef(true);
8 | const getDestroyed = useCallback(() => DestroyedRef.current, []);
9 |
10 | useEffect(() => {
11 | DestroyedRef.current = false;
12 |
13 | return () => {
14 | DestroyedRef.current = true;
15 | };
16 | }, []);
17 |
18 | return getDestroyed;
19 | };
20 |
21 | export default useDestroyed;
22 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/hooks/useLayout.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import type { LayoutChangeEvent } from 'react-native';
3 |
4 | export default function useLayout() {
5 | const [layout, setLayout] = React.useState<{
6 | height: number;
7 | width: number;
8 | measured: boolean;
9 | }>({ height: 0, width: 0, measured: false });
10 |
11 | const onLayout = React.useCallback(
12 | (e: LayoutChangeEvent) => {
13 | const { height, width } = e.nativeEvent.layout;
14 |
15 | if (height === layout.height && width === layout.width) {
16 | return;
17 | }
18 |
19 | setLayout({
20 | height,
21 | width,
22 | measured: true,
23 | });
24 | },
25 | [layout.height, layout.width]
26 | );
27 |
28 | return [layout, onLayout] as const;
29 | }
30 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/hooks/useLazyRef.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export default function useLazyRef(callback: () => T): React.MutableRefObject {
4 | const lazyRef = React.useRef();
5 |
6 | if (lazyRef.current === undefined) {
7 | lazyRef.current = callback();
8 | }
9 |
10 | return lazyRef as React.MutableRefObject;
11 | }
12 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/hooks/useLazyRender.ts:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect, ReactNode } from 'react';
2 |
3 | function useLazyRender(show: boolean): (render: () => React.ReactNode) => () => ReactNode {
4 | const [inited, setInited] = useState(false);
5 |
6 | useEffect(() => {
7 | if (show) {
8 | setInited(show);
9 | }
10 | }, [show]);
11 |
12 | return render => () => inited ? render() : null;
13 | }
14 |
15 | export default useLazyRender;
16 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/hooks/useMemoizedFn.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * 代码来源:https://github.com/alibaba/hooks/blob/master/packages/hooks/src/useMemoizedFn/index.ts
3 | * 关于 this:https://www.jianshu.com/p/8b3a2513d8e5
4 | */
5 |
6 | import { useMemo, useRef } from 'react';
7 |
8 | type noop = (...args: any[]) => any;
9 |
10 | /**
11 | * 持久化 function 的 hook
12 | */
13 | function useMemoizedFn(fn: T) {
14 | if (process.env.NODE_ENV === 'development') {
15 | if (typeof fn !== 'function') {
16 | console.error(`useMemoizedFn expected parameter is a function, got ${typeof fn}`);
17 | }
18 | }
19 |
20 | const fnRef = useRef(fn);
21 |
22 | // why not write `fnRef.current = fn`?
23 | // https://github.com/alibaba/hooks/issues/728
24 | fnRef.current = useMemo(() => fn, [fn]);
25 |
26 | const memoizedFn = useRef();
27 | if (!memoizedFn.current) {
28 | memoizedFn.current = function (this: T, ...args) {
29 | return fnRef.current.apply(this, args);
30 | } as T;
31 | }
32 |
33 | return memoizedFn.current;
34 | }
35 |
36 | export default useMemoizedFn;
37 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/hooks/useOrientation.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useCallback, useState } from 'react';
2 | import constants, { orientations } from '../utils/constants';
3 | import useUpdateEffect from './useUpdateEffect';
4 |
5 | interface UseOrientationProps {
6 | onOrientationChange?: (orientation: orientations) => void;
7 | }
8 |
9 | type UseOrientation = ({ onOrientationChange }: UseOrientationProps) => {
10 | orientation: orientations;
11 | };
12 |
13 | /**
14 | * 监听屏幕方向变动的 hook
15 | */
16 | const useOrientation: UseOrientation = ({ onOrientationChange }) => {
17 | const [orientation, setOrientation] = useState(constants.orientation);
18 |
19 | const orientationChangeListener = useCallback(() => {
20 | setOrientation(constants.orientation);
21 | }, []);
22 |
23 | useEffect(() => {
24 | const listener = constants.addDimensionsEventListener(orientationChangeListener);
25 | return () => constants.removeDimensionsEventListener(listener);
26 | }, []);
27 |
28 | useUpdateEffect(() => {
29 | onOrientationChange?.(orientation);
30 | }, [orientation]);
31 |
32 | return { orientation };
33 | };
34 |
35 | export default useOrientation;
36 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/hooks/useRefState.ts:
--------------------------------------------------------------------------------
1 | import { useCallback, useRef, useState } from 'react';
2 | import type { Dispatch, SetStateAction, MutableRefObject } from 'react';
3 | import isFunction from 'lodash-es/isFunction';
4 |
5 | type StateType = T | (() => T);
6 |
7 | export default function useRefState(
8 | initialState: StateType
9 | ): [T, Dispatch>, MutableRefObject] {
10 | const [state, setState] = useState(initialState);
11 | const ref = useRef(state);
12 | const setRafState = useCallback(
13 | patch => {
14 | setState(prevState => {
15 | // eslint-disable-next-line no-return-assign
16 | return (ref.current = isFunction(patch) ? patch(prevState) : patch);
17 | });
18 | },
19 | [state]
20 | );
21 | return [state, setRafState, ref];
22 | }
23 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/hooks/useRefs.ts:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import type { View } from 'react-native';
3 |
4 | export default function useRefs(): [T[], (index: number) => (el: T) => void, () => void] {
5 | const refs = React.useRef([]);
6 |
7 | const setRefs = React.useCallback(
8 | (index: number) => (el: T) => {
9 | if (el) refs.current[index] = el;
10 | },
11 | []
12 | );
13 |
14 | const reset = React.useCallback(() => {
15 | refs.current = [];
16 | }, []);
17 |
18 | return [refs.current, setRefs, reset];
19 | }
20 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/hooks/useSetState.ts:
--------------------------------------------------------------------------------
1 | import { useCallback } from 'react';
2 | import isFunction from 'lodash-es/isFunction';
3 | import useRefState from './useRefState';
4 | import useUnmountedRef from './useUnmountedRef';
5 |
6 | // eslint-disable-next-line @typescript-eslint/ban-types
7 | const useSetState = (
8 | initialState: T = {} as T
9 | ): [T, (patch: Partial | ((prevState: T) => Partial)) => void, React.MutableRefObject] => {
10 | const unmountedRef = useUnmountedRef();
11 | const [state, setState, ref] = useRefState(initialState);
12 |
13 | const setMergeState = useCallback(patch => {
14 | if (unmountedRef.current) return;
15 | setState(prevState => ({ ...prevState, ...(isFunction(patch) ? patch(prevState) : patch) }));
16 | }, []);
17 |
18 | return [state, setMergeState, ref];
19 | };
20 |
21 | export default useSetState;
22 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/hooks/useUnmountedRef.ts:
--------------------------------------------------------------------------------
1 | import { useRef, useEffect } from 'react';
2 |
3 | const useUnmountedRef = (): React.MutableRefObject => {
4 | const unmountedRef = useRef(false);
5 | useEffect(() => {
6 | unmountedRef.current = false;
7 |
8 | return () => {
9 | unmountedRef.current = true;
10 | };
11 | }, []);
12 | return unmountedRef;
13 | };
14 |
15 | export default useUnmountedRef;
16 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/hooks/useUpdate.ts:
--------------------------------------------------------------------------------
1 | import { useCallback, useState } from 'react';
2 |
3 | const useUpdate = () => {
4 | const [, setState] = useState({});
5 |
6 | return useCallback(() => setState({}), []);
7 | };
8 |
9 | export default useUpdate;
10 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/hooks/useUpdateEffect.ts:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react';
2 | import { createUpdateEffect } from '../utils/createUpdateEffect';
3 |
4 | export default createUpdateEffect(useEffect);
5 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/locale/index.ts:
--------------------------------------------------------------------------------
1 | export { default as zhCN, mergeLocale } from './lang/zh-CN';
2 | export type { PartialLocale } from './lang/zh-CN';
3 | export { default as zhHK } from './lang/zh-HK';
4 | export { default as zhTW } from './lang/zh-TW';
5 | export { default as enUS } from './lang/en-US';
6 | export { default as jaJP } from './lang/ja-JP';
7 | export { default as frFR } from './lang/fr-FR';
8 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/locale/lang/zh-CN.ts:
--------------------------------------------------------------------------------
1 | import { deepAssign } from '../../utils/deepAssign';
2 | import { base } from './base';
3 | import type { Locale } from './types';
4 |
5 | type DeepPartial = {
6 | [P in keyof T]?: DeepPartial;
7 | };
8 |
9 | export type PartialLocale = DeepPartial;
10 |
11 | const zhCN = deepAssign(base, {});
12 |
13 | const mergeLocale = (baseLocal: Locale, mergeLocal: PartialLocale): Locale => {
14 | return deepAssign(baseLocal, mergeLocal) as Locale;
15 | };
16 |
17 | export { mergeLocale };
18 |
19 | export default zhCN;
20 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/styles/darkTheme.ts:
--------------------------------------------------------------------------------
1 | import * as _vars from './variables';
2 | import { createDefaultTheme } from './defaultTheme';
3 | import { numberKeyBoardDarkVars } from '../NumberKeyboard/style';
4 |
5 | const darkVars = {
6 | ..._vars,
7 | text_color: '#f5f5f5',
8 | text_color_2: '#707070',
9 | text_color_3: '#4d4d4d',
10 | border_color: '#3a3a3c',
11 | active_color: '#3a3a3c',
12 | background: '#000',
13 | background_2: '#1c1c1e',
14 | background_3: '#37363b',
15 | };
16 |
17 | const createDarkTheme = (vars: typeof _vars) => ({
18 | ...createDefaultTheme(vars),
19 | ...vars,
20 | dark: false,
21 |
22 | // Button
23 | button_plain_background_color: 'transparent',
24 |
25 | // Calendar
26 | calendar_month_mark_color: 'rgba(100, 101, 102, 0.2)',
27 | calendar_day_disabled_color: vars.gray_7,
28 |
29 | // Picker
30 | picker_mask_top_color: ['rgba(0, 0, 0, 0.6)', 'rgba(0, 0, 0, 0.1)'],
31 | picker_mask_bottom_color: ['rgba(0, 0, 0, 0.1)', 'rgba(0, 0, 0, 0.6)'],
32 |
33 | // NumberKeyboard
34 | ...numberKeyBoardDarkVars(vars),
35 | });
36 |
37 | export const darkTheme = createDarkTheme(darkVars as typeof _vars);
38 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/styles/index.ts:
--------------------------------------------------------------------------------
1 | import type * as _vars from './variables';
2 | import type { defaultTheme } from './defaultTheme';
3 |
4 | export type Vars = typeof _vars;
5 |
6 | /** 默认变量类型 */
7 | export type ThemeVarType = typeof defaultTheme;
8 |
9 | export { defaultTheme } from './defaultTheme';
10 | export { darkTheme } from './darkTheme';
11 | export * from './shortHand';
12 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/tests/shared/snapshotTest.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import renderer from 'react-test-renderer';
3 |
4 | const snapshotTest = (name: string, Component: React.ComponentType) => {
5 | it(name, () => {
6 | const component = renderer.create();
7 |
8 | expect(component.toJSON()).toMatchSnapshot();
9 | });
10 | };
11 |
12 | export default snapshotTest;
13 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/types.ts:
--------------------------------------------------------------------------------
1 | import type { PressableProps } from 'react-native';
2 | import type { ThemeVarType } from './styles';
3 |
4 | export type $Omit = Pick>;
5 | export type $RemoveChildren> = $Omit<
6 | React.ComponentPropsWithoutRef,
7 | 'children'
8 | >;
9 |
10 | export type EllipsizeProp = 'head' | 'middle' | 'tail' | 'clip';
11 |
12 | declare global {
13 | // eslint-disable-next-line @typescript-eslint/no-namespace
14 | namespace DiceUI {
15 | type Theme = ThemeVarType;
16 | }
17 | }
18 |
19 | type Inline = Partial<
20 | {
21 | /**
22 | * @default None
23 | * @type PressableProps except click handlers
24 | */
25 | pressableProps: Omit;
26 | } & Pick
27 | >;
28 |
29 | export type InlinePressableProps = Inline<
30 | PressableProps,
31 | 'onPress' | 'onLongPress' | 'onPressIn' | 'onPressOut'
32 | >;
33 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/utils/cloneReactNode.ts:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export const cloneReactNode = (
4 | node: React.ReactNode,
5 | props?: Record
6 | ): React.ReactNode => {
7 | if (React.isValidElement(node) && props) {
8 | return React.cloneElement>(node, props);
9 | }
10 |
11 | return node;
12 | };
13 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/utils/createUpdateEffect.ts:
--------------------------------------------------------------------------------
1 | import { useRef } from 'react';
2 | import type { useEffect, useLayoutEffect } from 'react';
3 |
4 | type effectHookType = typeof useEffect | typeof useLayoutEffect;
5 |
6 | export const createUpdateEffect: (hook: effectHookType) => effectHookType =
7 | hook => (effect, deps) => {
8 | const isMounted = useRef(false);
9 |
10 | // for react-refresh
11 | hook(() => {
12 | return () => {
13 | isMounted.current = false;
14 | };
15 | }, []);
16 |
17 | // eslint-disable-next-line consistent-return
18 | hook(() => {
19 | if (!isMounted.current) {
20 | isMounted.current = true;
21 | } else {
22 | return effect();
23 | }
24 | }, deps);
25 | };
26 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/utils/deepAssign.ts:
--------------------------------------------------------------------------------
1 | import isObject from 'lodash-es/isObject';
2 | import isNil from 'lodash-es/isNil';
3 |
4 | type ObjectIndex = Record;
5 |
6 | function assignKey(to: ObjectIndex, from: ObjectIndex, key: string) {
7 | const val = from[key];
8 |
9 | if (isNil(val)) {
10 | return;
11 | }
12 |
13 | if (!Object.prototype.hasOwnProperty.call(to, key) || !isObject(val)) {
14 | // eslint-disable-next-line no-param-reassign
15 | to[key] = val;
16 | } else {
17 | // eslint-disable-next-line
18 | to[key] = deepAssign(Object(to[key]), from[key]);
19 | }
20 | }
21 |
22 | export function deepAssign(to: ObjectIndex, from: ObjectIndex): ObjectIndex {
23 | Object.keys(from).forEach(key => {
24 | assignKey(to, from, key);
25 | });
26 |
27 | return to;
28 | }
29 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/utils/devLog.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-console */
2 | import { isDev } from './isDev';
3 |
4 | export function devWarning(component: string, message: string): void {
5 | if (isDev) {
6 | console.warn(`[rn-vant: ${component}] ${message}`);
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/utils/getContrastingColor.ts:
--------------------------------------------------------------------------------
1 | import type { ColorValue } from 'react-native';
2 | import color from 'color';
3 |
4 | const getContrastingColor = (input: ColorValue, light: string, dark: string): string => {
5 | if (typeof input === 'string') {
6 | return color(input).isLight() ? dark : light;
7 | }
8 |
9 | return light;
10 | };
11 |
12 | export default getContrastingColor;
13 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/utils/getShortHand.ts:
--------------------------------------------------------------------------------
1 | const getShortHand = (style: 'padding' | 'margin', ...values: any[]) => {
2 | if (values.length === 1) {
3 | return { [style]: values[0] };
4 | }
5 | const _genCss = (...list: number[]) => ({
6 | [style + 'Top']: list[0],
7 | [style + 'Right']: list[1],
8 | [style + 'Bottom']: list[2],
9 | [style + 'Left']: list[3],
10 | });
11 | if (values.length === 2) {
12 | return _genCss(values[0], values[1], values[0], values[1]);
13 | }
14 | if (values.length === 3) {
15 | return _genCss(values[0], values[1], values[2], values[1]);
16 | }
17 | return _genCss(values[0], values[1], values[2], values[3]);
18 | };
19 |
20 | /**
21 | * 简写 padding 和 margin
22 | */
23 | export const padding = (...values: Array) => getShortHand('padding', ...values);
24 | export const margin = (...values: Array) => getShortHand('margin', ...values);
25 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/utils/isDev.ts:
--------------------------------------------------------------------------------
1 | export const isDev = process.env.NODE_ENV === 'development';
2 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/utils/isIcon.ts:
--------------------------------------------------------------------------------
1 | import type { IconNames } from '@pingtou/rn-vant-icons';
2 | import isString from 'lodash-es/isString';
3 |
4 | export const isIcon = (icon: IconNames | React.ReactNode): icon is IconNames => isString(icon);
5 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/utils/isString.ts:
--------------------------------------------------------------------------------
1 | const { toString } = Object.prototype;
2 |
3 | /**
4 | * Gets the `toStringTag` of `value`.
5 | *
6 | * @private
7 | * @param {*} value The value to query.
8 | * @returns {string} Returns the `toStringTag`.
9 | */
10 | function getTag(value: any) {
11 | if (value == null) {
12 | return value === undefined ? '[object Undefined]' : '[object Null]';
13 | }
14 | return toString.call(value);
15 | }
16 |
17 | // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
18 | const isString = (value: any): value is string => {
19 | const type = typeof value;
20 | return (
21 | type === 'string' ||
22 | (type === 'object' &&
23 | value != null &&
24 | !Array.isArray(value) &&
25 | getTag(value) === '[object String]')
26 | );
27 | };
28 |
29 | export default isString;
30 |
--------------------------------------------------------------------------------
/packages/rn-vant/src/utils/validate.ts:
--------------------------------------------------------------------------------
1 | export function isNumeric(val: string): boolean {
2 | return /^\d+(\.\d+)?$/.test(val);
3 | }
4 |
5 | export function isNaN(val: number): val is typeof NaN {
6 | if (Number.isNaN) {
7 | return Number.isNaN(val);
8 | }
9 |
10 | // eslint-disable-next-line no-self-compare
11 | return val !== val;
12 | }
13 |
--------------------------------------------------------------------------------
/packages/rn-vant/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig",
3 | "exclude": ["example"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/rn-vant/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "paths": {
5 | "@pingtou/rn-vant": ["./src/index"],
6 | },
7 | },
8 | "exclude": ["lib/**/*"]
9 | }
10 |
--------------------------------------------------------------------------------
/scripts/bootstrap.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | // eslint-disable-next-line camelcase
3 | const child_process = require('child_process');
4 |
5 | const root = path.resolve(__dirname, '..');
6 | const args = process.argv.slice(2);
7 | const options = {
8 | cwd: process.cwd(),
9 | env: process.env,
10 | stdio: 'inherit',
11 | encoding: 'utf-8',
12 | };
13 |
14 | let result;
15 |
16 | if (process.cwd() !== root || args.length) {
17 | result = child_process.spawnSync('yarn', args, options);
18 | } else {
19 | result = child_process.spawnSync('yarn', ['bootstrap'], options);
20 | }
21 |
22 | process.exitCode = result.status;
23 |
--------------------------------------------------------------------------------
/scripts/verify-commit-msg.js:
--------------------------------------------------------------------------------
1 | const chalk = require('chalk');
2 | const msgPath = process.env.HUSKY_GIT_PARAMS;
3 | const msg = require('fs').readFileSync(msgPath, 'utf-8').trim();
4 |
5 | const commitRE =
6 | /^(revert: )?(feat|fix|polish|docs|style|refactor|perf|test|workflow|ci|chore|types|build)(\(.+\))?: .{1,50}/;
7 |
8 | if (!commitRE.test(msg)) {
9 | console.log();
10 | console.error(
11 | ` ${chalk.bgRed.white(' ERROR ')} ${chalk.red(`invalid commit message format.`)}\n\n` +
12 | chalk.red(
13 | ` Proper commit message format is required for automated changelog generation. Examples:\n\n`
14 | ) +
15 | ` ${chalk.green(`feat(compiler): add 'comments' option`)}\n` +
16 | ` ${chalk.green(`fix(v-model): handle events on blur (close #28)`)}\n\n` +
17 | chalk.red(` See .gitlab/COMMIT_CONVENTION.md for more details.\n`) +
18 | chalk.red(
19 | ` You can also use ${chalk.cyan(`git cz`)} to interactively generate a commit message.\n`
20 | )
21 | );
22 | process.exit(1);
23 | }
24 |
--------------------------------------------------------------------------------
/tsconfig.base.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": ".",
4 | "allowUnreachableCode": false,
5 | "allowUnusedLabels": false,
6 | "esModuleInterop": true,
7 | "importsNotUsedAsValues": "error",
8 | "forceConsistentCasingInFileNames": true,
9 | "jsx": "react",
10 | "lib": ["esnext", "dom"],
11 | "module": "esnext",
12 | "moduleResolution": "node",
13 | "noFallthroughCasesInSwitch": true,
14 | "noImplicitReturns": true,
15 | "noImplicitUseStrict": false,
16 | "noStrictGenericChecks": false,
17 | "noUnusedLocals": true,
18 | "noUnusedParameters": true,
19 | "resolveJsonModule": true,
20 | "skipLibCheck": true,
21 | "strict": true,
22 | "target": "esnext"
23 | },
24 | }
--------------------------------------------------------------------------------
|