Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
205 changes: 190 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,14 @@ A flexible React Native Wheel Picker for iOS and Android without using the nativ


## Features
- Without native side.
- Unified API.
- Only native animations.
- [Support native feedback](#Native-Feedback).
- [Support virtualization](#withVirtualized).
- Compatible with Expo ([Snack][EXPO_SNACK]).
- Deep customization
- Written ```TypeScript```.
- Without native side.
- Unified API.
- Only native animations.
- [Support native feedback](#Native-Feedback).
- [Support virtualization](#withVirtualized).
- Compatible with Expo ([Snack][EXPO_SNACK]).
- Deep customization
- Written ```TypeScript```.

## Installation
```shell
Expand All @@ -62,9 +62,15 @@ yarn add @quidone/react-native-wheel-picker
- [Native Feedback](#Native-Feedback)
- [API](#API)
- [WheelPicker](#WheelPicker)
- [usePickerItemHeight](#usePickerItemHeight)
- [useScrollContentOffset](#useScrollContentOffset)
- [withVirtualized](#withVirtualized)
- [usePickerItemHeight](#usePickerItemHeight)
- [useScrollContentOffset](#useScrollContentOffset)
- [withVirtualized](#withVirtualized)
- [DatePicker (Beta)](#DatePicker)
- [Picker Control (Beta)](#Picker-Control)
- [usePickerControl](#usePickerControl)
- [withPickerControl](#withPickerControl)
- [useOnPickerValueChangedEffect](#useOnPickerValueChangedEffect)
- [useOnPickerValueChangingEffect](#useOnPickerValueChangingEffect)
- [Footer](#-Author)

## Usage
Expand All @@ -80,7 +86,7 @@ yarn install
cd example && yarn install && yarn ios
```

### Simple case
### WheelPicker usage

```jsx
import React, {useState} from 'react';
Expand All @@ -106,6 +112,123 @@ const App = () => {
export default App;
```

### DatePicker usage (Beta)

#### Simple case
```tsx
import React, {useState} from 'react';
import {DatePicker} from '@quidone/react-native-wheel-picker';

const App = () => {
const [date, setDate] = useState('2025-02-02');

return (
<DatePicker
date={date} // required format YYYY-MM-DD
onDateChanged={({date}) => setDate(date)}
/>
);
};
```

#### Customized case
You also have a lot of control over each WheelPicker and the rendering process;
you can add your own components between individual WheelPickers

> ⚠️ **Warning:** It is recommended to test the component in a release build of your application.
> There is an issue where synchronization of scrolling may occasionally slip during scrolling
> attempts when performance is low.

```tsx
import React, {useState} from 'react';
import {useStableCallback} from '@rozhkov/react-useful-hooks';
import {DatePicker} from '@quidone/react-native-wheel-picker';

const CustomizedDatePicker = () => {
const [date, setDate] = useState('2025-02-02');

const onDateChanged = useStableCallback(({date}: {date: string}) => {
setDate(date);
});

return (
<DatePicker
date={date}
onDateChanged={onDateChanged}
renderDate={() => <DatePicker.Date />}
renderMonth={() => <DatePicker.Month />}
renderYear={() => <DatePicker.Year />}
>
{({dateNodes}) => {
return dateNodes.map((x) => x.node);
}}
</DatePicker>
);
};
```

### PickerControl usage (Beta)

```tsx
import React, {useState} from 'react';
import WheelPicker, {
type PickerItem,
useOnPickerValueChangedEffect,
useOnPickerValueChangingEffect,
usePickerControl,
withPickerControl,
} from '@quidone/react-native-wheel-picker';
import {View} from 'react-native';

const ControlPicker = withPickerControl(WheelPicker);

type ControlPickersMap = {
value1: {item: PickerItem<number>};
value2: {item: PickerItem<number>};
};

const data = Array.from({length: 100}, (_, index) => ({value: index}));

const App = () => {
const [value, setValue] = useState({value1: 0, value2: 0});

const pickerControl = usePickerControl<ControlPickersMap>();

useOnPickerValueChangedEffect(pickerControl, (event) => {
setValue({
value1: event.pickers.value1.item.value,
value2: event.pickers.value2.item.value,
});
});

useOnPickerValueChangingEffect(pickerControl, (event) => {
// some logic
});

return (
<View style={{flexDirection: 'row', justifyContent: 'space-around'}}>
<ControlPicker
control={pickerControl}
pickerName={'value1'}
data={data}
value={value.value1}
width={100}
enableScrollByTapOnItem={true}
/>
<ControlPicker
control={pickerControl}
pickerName={'value2'}
data={data}
value={value.value2}
width={100}
enableScrollByTapOnItem={true}
/>
</View>
);
};
```


## Native Feedback

You can trigger native sound and impact with [@quidone/react-native-wheel-picker-feedback][FEEDBACK_GITHUB]
Expand Down Expand Up @@ -154,13 +277,13 @@ const App = () => {
- ```scrollEventThrottle?``` [object | array] - [original](https://reactnative.dev/docs/scrollview#scrolleventthrottle-ios)


### usePickerItemHeight
#### usePickerItemHeight
This hook returns the item height which was passed via props.

### useScrollContentOffset
#### useScrollContentOffset
This hook returns the animated value of the ScrollView offset.

### withVirtualized
#### withVirtualized
This HOC returns virtualized picker

```jsx
Expand All @@ -175,6 +298,58 @@ const VirtualizedWheelPicker = withVirtualized(WheelPicker);
- ```windowSize?``` - [original](https://reactnative.dev/docs/flatlist#windowsize).
- ```updateCellsBatchingPeriod?``` (default = 10) - [original](https://reactnative.dev/docs/flatlist#updatecellsbatchingperiod).

### DatePicker

A specialized picker component for selecting dates. It supports localization and deep customization.

#### Props
- ```date``` [string] - Current date in 'YYYY-MM-DD' format
- ```onDateChanged``` [function] - Callback fired when date selection is confirmed
- ```minDate?``` [string] - Minimum selectable date in 'YYYY-MM-DD' format
- ```maxDate?``` [string] - Maximum selectable date in 'YYYY-MM-DD' format
- ```locale?``` [string] - Locale for date formatting (default = 'en')
- ```renderDate?``` [function] - Custom renderer for date component
- ```renderMonth?``` [function] - Custom renderer for month component
- ```renderYear?``` [function] - Custom renderer for year component
- ```children?``` [function] - Render prop for customizing component layout

DatePicker also accepts all the common wheel picker props like `itemHeight`, `visibleItemCount`, `readOnly`, etc.

#### Subcomponents
DatePicker exposes subcomponents that can be used for custom layouts:
- ```DatePicker.Date``` - Day WheelPicker
- ```DatePicker.Month``` - Month WheelPicker
- ```DatePicker.Year``` - Year WheelPicker

### Picker Control

Picker Control provides a way to synchronize multiple WheelPicker components. It is used inside DatePicker.

Main goals:
1. Synchronize onValueChanged and onValueChanging events.
2. Synchronize the value selection process. If a value changes, all WheelPickers should accept this value, even if they are still spinning.

#### usePickerControl

A hook that creates a control object for connecting multiple pickers. See [example](#PickerControl-Usage)

#### withPickerControl

A HOC that connects a WheelPicker to a control object. See [example](#PickerControl-Usage)

##### Adding props
- ```control``` [object] - Control object created with `usePickerControl`
- ```pickerName``` [string] - Unique name for the picker within the control group

#### useOnPickerValueChangedEffect

Called when the value has been changed. This occurs during the inactive state of all WheelPickers. See [example](#PickerControl-Usage)

#### useOnPickerValueChangingEffect

Called when any of the connected WheelPickers changes. See [example](#PickerControl-Usage)


## 👨‍💻 Author
[Sergey Rozhkov][AUTHOR]

Expand Down
25 changes: 16 additions & 9 deletions example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,26 @@
"dependencies": {
"@expo/ngrok": "^4.1.1",
"@faker-js/faker": "^8.0.2",
"@gorhom/bottom-sheet": "^5",
"@quidone/react-native-wheel-picker-feedback": "^2.0.0",
"@react-native-picker/picker": "2.9.0",
"@react-native-picker/picker": "2.11.1",
"@react-navigation/elements": "^2.6.3",
"@react-navigation/native": "^7.1.17",
"@react-navigation/native-stack": "^7.3.25",
"@rozhkov/react-useful-hooks": "^1.0.9",
"expo": "^52.0.41",
"expo-splash-screen": "~0.29.22",
"expo-status-bar": "~2.0.1",
"react": "18.3.1",
"react-dom": "18.3.1",
"react-native": "0.76.7",
"expo": "^53.0.20",
"expo-splash-screen": "~0.30.10",
"expo-status-bar": "~2.2.3",
"react": "19.0.0",
"react-dom": "19.0.0",
"react-native": "0.79.5",
"react-native-builder-bob": "^0.38.4",
"react-native-elements": "^4.0.0-rc.2",
"react-native-safe-area-context": "4.12.0",
"react-native-web": "~0.19.10"
"react-native-gesture-handler": "~2.24.0",
"react-native-reanimated": "~3.17.4",
"react-native-safe-area-context": "5.4.0",
"react-native-screens": "~4.11.1",
"react-native-web": "^0.20.0"
},
"devDependencies": {
"@babel/core": "^7.24.0",
Expand Down
43 changes: 12 additions & 31 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,19 @@
import * as React from 'react';
import {ScrollView, StyleSheet} from 'react-native';
import {PickerConfigProvider} from './picker-config';
import {Box} from './components/base';
import {
AvatarCustomizedPickerBlockExample,
CompareWithNativeIOSBlockExample,
SimplePickerBlockExample,
} from './components/example-blocks';
import WheelPickerFeedback from '@quidone/react-native-wheel-picker-feedback';
import {NativeFeedbackProvider, RootNavigation} from './snack';
import {GestureHandlerRootView} from 'react-native-gesture-handler';
import {BottomSheetModalProvider} from '@gorhom/bottom-sheet';

const App = () => {
return (
<ScrollView contentContainerStyle={styles.contentContainer}>
<SimplePickerBlockExample />
<AvatarCustomizedPickerBlockExample />
<CompareWithNativeIOSBlockExample />
<Box height={100} />
</ScrollView>
<NativeFeedbackProvider module={WheelPickerFeedback}>
<GestureHandlerRootView style={{flex: 1}}>

Check warning on line 10 in example/src/App.tsx

View workflow job for this annotation

GitHub Actions / build

Inline style: { flex: 1 }

Check warning on line 10 in example/src/App.tsx

View workflow job for this annotation

GitHub Actions / static-check

Inline style: { flex: 1 }
<BottomSheetModalProvider>
<RootNavigation />
</BottomSheetModalProvider>
</GestureHandlerRootView>
</NativeFeedbackProvider>
);
};

const styles = StyleSheet.create({
contentContainer: {
paddingVertical: 60,
paddingHorizontal: 20,
alignItems: 'center',
},
});

const Providers = () => {
return (
<PickerConfigProvider>
<App />
</PickerConfigProvider>
);
};

export default Providers;
export default App;
42 changes: 0 additions & 42 deletions example/src/components/example-blocks/SimplePickerBlockExample.tsx

This file was deleted.

3 changes: 0 additions & 3 deletions example/src/components/example-blocks/index.ts

This file was deleted.

3 changes: 0 additions & 3 deletions example/src/picker-config/index.ts

This file was deleted.

Loading
Loading