@amabeth/repeating-wheel-picker 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +20 -0
- package/README.md +44 -0
- package/lib/module/index.js +235 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/index.d.ts +157 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/package.json +177 -0
- package/src/index.tsx +507 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 amabeth
|
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
in the Software without restriction, including without limitation the rights
|
|
7
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
furnished to do so, subject to the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be included in all
|
|
12
|
+
copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Readme
|
|
2
|
+
|
|
3
|
+
A React Native wheel picker that allows endless scrolling through repeating content
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npm install repeating-wheel-picker
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
import RepeatingWheelPicker, {
|
|
16
|
+
type RepeatingWheelPickerProps,
|
|
17
|
+
} from "repeating-wheel-picker";
|
|
18
|
+
|
|
19
|
+
// ...
|
|
20
|
+
const [, setSelected] = useState<string>();
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<RepeatingWheelPicker<string>
|
|
24
|
+
setSelected={setSelected}
|
|
25
|
+
initialIndex={0}
|
|
26
|
+
data={["first", "second", "third"]}
|
|
27
|
+
/>
|
|
28
|
+
);
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
## Contributing
|
|
33
|
+
|
|
34
|
+
Contributions are currently not intended.
|
|
35
|
+
|
|
36
|
+
## License
|
|
37
|
+
|
|
38
|
+
[MIT](LICENSE)
|
|
39
|
+
|
|
40
|
+
## [Changelog](CHANGELOG.md)
|
|
41
|
+
|
|
42
|
+
## [Impressum / Imprint](https://amabeth.github.io/#imprint)
|
|
43
|
+
|
|
44
|
+
---
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
4
|
+
import { Text, View, VirtualizedList } from "react-native";
|
|
5
|
+
import { LinearGradient } from "expo-linear-gradient";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Provides a wheel picker with repeating data that can be infinitely scrolled.
|
|
9
|
+
*
|
|
10
|
+
* @param properties configuration of the wheel picker
|
|
11
|
+
*/
|
|
12
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
|
+
export function RepeatingWheelPicker(properties) {
|
|
14
|
+
// set defaults for all unprovided optional properties
|
|
15
|
+
const props = useMemo(() => withDefaults(properties), [properties]);
|
|
16
|
+
// to always have enough data to scroll through, define how often the input data should be multiplied
|
|
17
|
+
const dataMultiplier = useMemo(() => Math.max(Math.round(90 / props.data.length), 3), [props.data.length]);
|
|
18
|
+
// difference between centered index and top most visible index
|
|
19
|
+
const indexDiffTopToCentered = useMemo(() => Math.floor(props.itemDisplayCount / 2), [props.itemDisplayCount]);
|
|
20
|
+
// offsets for all list elements
|
|
21
|
+
const offsets = useMemo(() => getOffsets(props.data.length, props.itemDisplayCount, props.itemHeight, dataMultiplier), [props.data.length, props.itemDisplayCount, props.itemHeight, dataMultiplier, props.containerVerticalPadding]);
|
|
22
|
+
|
|
23
|
+
// current selected item (centered one)
|
|
24
|
+
const [current, setCurrent] = useState(() => props.initialIndex + props.data.length * Math.floor(dataMultiplier / 2));
|
|
25
|
+
const listRef = useRef(null);
|
|
26
|
+
|
|
27
|
+
// call "setSelected" when the current top item or the data changed
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
const selectedElement = props.data[current % props.data.length]; // centered element
|
|
30
|
+
|
|
31
|
+
if (selectedElement !== undefined) {
|
|
32
|
+
props.setSelected(selectedElement);
|
|
33
|
+
}
|
|
34
|
+
}, [current, props.data]);
|
|
35
|
+
return /*#__PURE__*/_jsxs(View, {
|
|
36
|
+
onLayout: props.containerOnLayout,
|
|
37
|
+
style: {
|
|
38
|
+
...props.containerStyle,
|
|
39
|
+
height: props.itemHeight * props.itemDisplayCount + props.containerVerticalPadding * 2
|
|
40
|
+
},
|
|
41
|
+
children: [/*#__PURE__*/_jsx(VirtualizedList, {
|
|
42
|
+
ref: listRef,
|
|
43
|
+
scrollEnabled: props.enabled,
|
|
44
|
+
getItemCount: () => props.data.length * dataMultiplier,
|
|
45
|
+
initialScrollIndex: current - indexDiffTopToCentered,
|
|
46
|
+
initialNumToRender: props.data.length * dataMultiplier,
|
|
47
|
+
windowSize: props.data.length * dataMultiplier,
|
|
48
|
+
renderItem: ({
|
|
49
|
+
item,
|
|
50
|
+
index
|
|
51
|
+
}) => /*#__PURE__*/_jsx(Item, {
|
|
52
|
+
item: item,
|
|
53
|
+
props: props
|
|
54
|
+
}, index),
|
|
55
|
+
getItem: (_, index) => props.data[index % props.data.length],
|
|
56
|
+
getItemLayout: (_, index) => ({
|
|
57
|
+
length: props.itemHeight,
|
|
58
|
+
offset: offsets[index],
|
|
59
|
+
index: index
|
|
60
|
+
}),
|
|
61
|
+
keyExtractor: (_, index) => `${index}`
|
|
62
|
+
|
|
63
|
+
// disableIntervalMomentum={true}
|
|
64
|
+
,
|
|
65
|
+
decelerationRate: "fast",
|
|
66
|
+
snapToOffsets: offsets,
|
|
67
|
+
onMomentumScrollEnd: event => onMomentumScrollEnd(event.nativeEvent.contentOffset.y, setCurrent, props.data.length, props.itemHeight, dataMultiplier, indexDiffTopToCentered, props.containerVerticalPadding, listRef),
|
|
68
|
+
showsVerticalScrollIndicator: false,
|
|
69
|
+
style: {
|
|
70
|
+
flex: 1,
|
|
71
|
+
width: "100%",
|
|
72
|
+
borderRadius: props.containerStyle.borderRadius,
|
|
73
|
+
paddingVertical: props.containerVerticalPadding,
|
|
74
|
+
paddingHorizontal: props.containerHorizontalPadding
|
|
75
|
+
}
|
|
76
|
+
}), props.enableGradient && /*#__PURE__*/_jsx(View, {
|
|
77
|
+
style: {
|
|
78
|
+
backgroundColor: "transparent",
|
|
79
|
+
position: "absolute",
|
|
80
|
+
height: "100%",
|
|
81
|
+
width: "100%"
|
|
82
|
+
},
|
|
83
|
+
children: /*#__PURE__*/_jsx(FrontGradient, {
|
|
84
|
+
gradientFadeColor: props.gradientFadeColor,
|
|
85
|
+
borderRadius: props.containerStyle.borderRadius
|
|
86
|
+
})
|
|
87
|
+
})]
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
function Item({
|
|
91
|
+
item,
|
|
92
|
+
props
|
|
93
|
+
}) {
|
|
94
|
+
return /*#__PURE__*/_jsx(View, {
|
|
95
|
+
style: {
|
|
96
|
+
justifyContent: "center",
|
|
97
|
+
alignContent: "center",
|
|
98
|
+
backgroundColor: "transparent",
|
|
99
|
+
...props.itemContainerStyle,
|
|
100
|
+
paddingVertical: 0,
|
|
101
|
+
height: props.itemHeight
|
|
102
|
+
},
|
|
103
|
+
children: /*#__PURE__*/_jsx(Text, {
|
|
104
|
+
style: {
|
|
105
|
+
textAlign: "center",
|
|
106
|
+
...props.itemTextStyle
|
|
107
|
+
},
|
|
108
|
+
children: props.getLabel(item)
|
|
109
|
+
})
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
function FrontGradient({
|
|
113
|
+
gradientFadeColor,
|
|
114
|
+
borderRadius
|
|
115
|
+
}) {
|
|
116
|
+
return /*#__PURE__*/_jsx(LinearGradient, {
|
|
117
|
+
colors: [gradientFadeColor, "transparent", gradientFadeColor],
|
|
118
|
+
style: {
|
|
119
|
+
height: "100%",
|
|
120
|
+
width: "100%",
|
|
121
|
+
backgroundColor: "transparent",
|
|
122
|
+
borderRadius: borderRadius
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
function getOffsets(dataLength, itemDisplayCount, itemHeight, dataMultiplier) {
|
|
127
|
+
let offsets = [];
|
|
128
|
+
|
|
129
|
+
// calculate offset for all items
|
|
130
|
+
for (let i = 0; i < dataLength * dataMultiplier; i++) {
|
|
131
|
+
offsets[i] = itemOffset(i, itemDisplayCount, itemHeight);
|
|
132
|
+
}
|
|
133
|
+
return offsets;
|
|
134
|
+
}
|
|
135
|
+
function itemOffset(index, itemDisplayCount, itemHeight) {
|
|
136
|
+
return (index + (itemDisplayCount % 2 === 0 ? 0.5 : 0)) * itemHeight;
|
|
137
|
+
}
|
|
138
|
+
function onMomentumScrollEnd(offset, setCurrent, dataLength, itemHeight, dataMultiplier, indexDiffTopToCentered, verticalPadding, ref) {
|
|
139
|
+
// offset excluding padding
|
|
140
|
+
const innerOffset = offset - verticalPadding;
|
|
141
|
+
// get index of top most completely visible item
|
|
142
|
+
const currentTopIndex = Math.round(innerOffset / itemHeight);
|
|
143
|
+
|
|
144
|
+
// get current section within whole extended data (data * dataMultiplier)
|
|
145
|
+
// section 0 = [0, data.length)
|
|
146
|
+
// section 1 = [data.length, data.length * 2)
|
|
147
|
+
// ...
|
|
148
|
+
const currentSection = Math.floor(innerOffset / (dataLength * itemHeight));
|
|
149
|
+
// target section is always the middle one, so user can scroll seemingly infinitely
|
|
150
|
+
const targetSection = Math.floor(dataMultiplier / 2);
|
|
151
|
+
|
|
152
|
+
// get corresponding index of current top index in target section
|
|
153
|
+
const targetTopIndex = currentTopIndex + (targetSection - currentSection) * dataLength;
|
|
154
|
+
// set current index to centered one, if `targetTopIndex`was at the top
|
|
155
|
+
setCurrent(targetTopIndex + indexDiffTopToCentered);
|
|
156
|
+
if (currentSection === targetSection) {
|
|
157
|
+
// if target section is current section, stay in this section
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// if target section is different from current section, scroll to target
|
|
162
|
+
const targetOffset = offset + (targetTopIndex - currentTopIndex) * itemHeight;
|
|
163
|
+
ref.current?.scrollToOffset({
|
|
164
|
+
animated: false,
|
|
165
|
+
offset: targetOffset
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// props
|
|
170
|
+
|
|
171
|
+
function withDefaults(props) {
|
|
172
|
+
const defaultBackgroundColor = "black";
|
|
173
|
+
const defaultTextColor = "white";
|
|
174
|
+
const defaultTextSize = 18;
|
|
175
|
+
validateProps(props);
|
|
176
|
+
return {
|
|
177
|
+
...props,
|
|
178
|
+
// optional
|
|
179
|
+
containerOnLayout: props.containerOnLayout ?? (() => {}),
|
|
180
|
+
enabled: props.enabled ?? true,
|
|
181
|
+
getLabel: props.getLabel ?? (t => `${t}`),
|
|
182
|
+
itemHeight: props.itemHeight ?? (props.itemTextStyle?.fontSize ?? defaultTextSize) + 15,
|
|
183
|
+
itemDisplayCount: props.itemDisplayCount ?? 3,
|
|
184
|
+
containerVerticalPadding: props.containerVerticalPadding ?? 0,
|
|
185
|
+
containerHorizontalPadding: props.containerHorizontalPadding ?? 10,
|
|
186
|
+
containerStyle: {
|
|
187
|
+
...props.containerStyle,
|
|
188
|
+
backgroundColor: props.containerStyle?.backgroundColor ?? defaultBackgroundColor,
|
|
189
|
+
padding: 0,
|
|
190
|
+
paddingHorizontal: 0,
|
|
191
|
+
paddingVertical: 0,
|
|
192
|
+
paddingTop: 0,
|
|
193
|
+
paddingBottom: 0,
|
|
194
|
+
paddingLeft: 0,
|
|
195
|
+
paddingRight: 0
|
|
196
|
+
},
|
|
197
|
+
itemContainerStyle: {
|
|
198
|
+
...props.itemContainerStyle,
|
|
199
|
+
backgroundColor: props.itemContainerStyle?.backgroundColor ?? "transparent",
|
|
200
|
+
justifyContent: props.itemContainerStyle?.justifyContent ?? "center"
|
|
201
|
+
},
|
|
202
|
+
itemTextStyle: {
|
|
203
|
+
...props.itemTextStyle,
|
|
204
|
+
fontSize: props.itemTextStyle?.fontSize ?? defaultTextSize,
|
|
205
|
+
color: props.itemTextStyle?.color ?? defaultTextColor
|
|
206
|
+
},
|
|
207
|
+
enableGradient: props.enableGradient ?? true,
|
|
208
|
+
gradientFadeColor: props.gradientFadeColor ?? props.containerStyle?.backgroundColor ?? defaultBackgroundColor
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
function validateProps(props) {
|
|
212
|
+
if (props.initialIndex < 0 || props.initialIndex >= props.data.length) {
|
|
213
|
+
throw InvalidPropertiesError("initialIndex", String(props.initialIndex), "has to be in range [0, data.length)");
|
|
214
|
+
}
|
|
215
|
+
if (props.data.length < 2) {
|
|
216
|
+
throw InvalidPropertiesError("data.length", String(props.data.length), "has to be larger than 1");
|
|
217
|
+
}
|
|
218
|
+
if (props.itemDisplayCount !== undefined && props.itemDisplayCount < 1) {
|
|
219
|
+
throw InvalidPropertiesError("itemDisplayCount", String(props.itemDisplayCount), "has to be larger than 0");
|
|
220
|
+
}
|
|
221
|
+
if (props.itemDisplayCount !== undefined && !Number.isInteger(props.itemDisplayCount)) {
|
|
222
|
+
throw InvalidPropertiesError("itemDisplayCount", String(props.itemDisplayCount), "has to be an integer");
|
|
223
|
+
}
|
|
224
|
+
if (props.itemHeight !== undefined && props.itemHeight < 1) {
|
|
225
|
+
throw InvalidPropertiesError("itemHeight", String(props.itemHeight), "has to be larger than 0");
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
function InvalidPropertiesError(propertyName, propertyValue, violatedConstraint) {
|
|
229
|
+
return Error(`Value "${propertyValue}" is invalid for property "${propertyName}": ${violatedConstraint}`);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
*
|
|
234
|
+
*/
|
|
235
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["useEffect","useMemo","useRef","useState","Text","View","VirtualizedList","LinearGradient","jsx","_jsx","jsxs","_jsxs","RepeatingWheelPicker","properties","props","withDefaults","dataMultiplier","Math","max","round","data","length","indexDiffTopToCentered","floor","itemDisplayCount","offsets","getOffsets","itemHeight","containerVerticalPadding","current","setCurrent","initialIndex","listRef","selectedElement","undefined","setSelected","onLayout","containerOnLayout","style","containerStyle","height","children","ref","scrollEnabled","enabled","getItemCount","initialScrollIndex","initialNumToRender","windowSize","renderItem","item","index","Item","getItem","_","getItemLayout","offset","keyExtractor","decelerationRate","snapToOffsets","onMomentumScrollEnd","event","nativeEvent","contentOffset","y","showsVerticalScrollIndicator","flex","width","borderRadius","paddingVertical","paddingHorizontal","containerHorizontalPadding","enableGradient","backgroundColor","position","FrontGradient","gradientFadeColor","justifyContent","alignContent","itemContainerStyle","textAlign","itemTextStyle","getLabel","colors","dataLength","i","itemOffset","verticalPadding","innerOffset","currentTopIndex","currentSection","targetSection","targetTopIndex","targetOffset","scrollToOffset","animated","defaultBackgroundColor","defaultTextColor","defaultTextSize","validateProps","t","fontSize","padding","paddingTop","paddingBottom","paddingLeft","paddingRight","color","InvalidPropertiesError","String","Number","isInteger","propertyName","propertyValue","violatedConstraint","Error"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"module"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"module"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { type ColorValue, type LayoutChangeEvent, type TextStyle, type ViewStyle } from "react-native";
|
|
2
|
+
/**
|
|
3
|
+
* Provides a wheel picker with repeating data that can be infinitely scrolled.
|
|
4
|
+
*
|
|
5
|
+
* @param properties configuration of the wheel picker
|
|
6
|
+
*/
|
|
7
|
+
export declare function RepeatingWheelPicker<T>(properties: RepeatingWheelPickerProps<T>): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
/**
|
|
9
|
+
*
|
|
10
|
+
*/
|
|
11
|
+
export type RepeatingWheelPickerProps<T> = {
|
|
12
|
+
/**
|
|
13
|
+
* Function to set currently selected element and use it in your application.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* const [selected, setSelected] = useState(0);
|
|
18
|
+
*
|
|
19
|
+
* return (
|
|
20
|
+
* <RepeatingWheelPicker
|
|
21
|
+
* setSelected={setSelected}
|
|
22
|
+
* //...
|
|
23
|
+
* />
|
|
24
|
+
* );
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* @param t currently selected element
|
|
28
|
+
*/
|
|
29
|
+
setSelected: (t: T) => void;
|
|
30
|
+
/**
|
|
31
|
+
* Index to initially center.
|
|
32
|
+
*/
|
|
33
|
+
initialIndex: number;
|
|
34
|
+
/**
|
|
35
|
+
* Data to display.
|
|
36
|
+
*/
|
|
37
|
+
data: T[];
|
|
38
|
+
/**
|
|
39
|
+
* Function to retrieve the text to display for an element as a label.
|
|
40
|
+
*
|
|
41
|
+
* @defaultValue
|
|
42
|
+
* ```ts
|
|
43
|
+
* (t: T) => `${t}`
|
|
44
|
+
* ```
|
|
45
|
+
*
|
|
46
|
+
* @param t element to retrieve label for
|
|
47
|
+
*/
|
|
48
|
+
getLabel?: (t: T) => string;
|
|
49
|
+
/**
|
|
50
|
+
* Function called when the layout of the container changes.
|
|
51
|
+
*
|
|
52
|
+
* _Example usage for monitoring the container's height:_
|
|
53
|
+
* ```ts
|
|
54
|
+
* const [pickerHeight, setPickerHeight] = useState<number>(0);
|
|
55
|
+
*
|
|
56
|
+
* const onLayout = useCallback((event: LayoutChangeEvent) => {
|
|
57
|
+
* const { height } = event.nativeEvent.layout;
|
|
58
|
+
* setPickerHeight(height);
|
|
59
|
+
* }, []);
|
|
60
|
+
*
|
|
61
|
+
* return (
|
|
62
|
+
* <View style={{flexDirection: "row"}}>
|
|
63
|
+
* <View style={{height: height}}>
|
|
64
|
+
* <Text>Picker label</Text>
|
|
65
|
+
* </View>
|
|
66
|
+
* <RepeatingWheelPicker
|
|
67
|
+
* //...
|
|
68
|
+
* containerOnLayout={onLayout}
|
|
69
|
+
* />
|
|
70
|
+
* </View>
|
|
71
|
+
* );
|
|
72
|
+
* ```
|
|
73
|
+
*
|
|
74
|
+
* @defaultValue () => {}
|
|
75
|
+
*
|
|
76
|
+
* @param event layout change event that triggered `onLayout`
|
|
77
|
+
*/
|
|
78
|
+
containerOnLayout?: (event: LayoutChangeEvent) => void;
|
|
79
|
+
/**
|
|
80
|
+
* Enables / disables scrolling of the wheel picker.
|
|
81
|
+
*
|
|
82
|
+
* @defaultValue true
|
|
83
|
+
*/
|
|
84
|
+
enabled?: boolean;
|
|
85
|
+
/**
|
|
86
|
+
* Height per displayed item.
|
|
87
|
+
*
|
|
88
|
+
* @defaultValue itemTextStyle.fontSize + 15
|
|
89
|
+
*/
|
|
90
|
+
itemHeight?: number;
|
|
91
|
+
/**
|
|
92
|
+
* Number of items to display.
|
|
93
|
+
*
|
|
94
|
+
* @defaultValue 3
|
|
95
|
+
*/
|
|
96
|
+
itemDisplayCount?: number;
|
|
97
|
+
/**
|
|
98
|
+
* Vertical padding for the container of the wheel picker.
|
|
99
|
+
*
|
|
100
|
+
* @defaultValue 0
|
|
101
|
+
*/
|
|
102
|
+
containerVerticalPadding?: number;
|
|
103
|
+
/**
|
|
104
|
+
* Horizontal padding for the container of the wheel picker.
|
|
105
|
+
*
|
|
106
|
+
* @defaultValue 10
|
|
107
|
+
*/
|
|
108
|
+
containerHorizontalPadding?: number;
|
|
109
|
+
/**
|
|
110
|
+
* Styling for the container of the wheel picker.
|
|
111
|
+
*
|
|
112
|
+
* @defaultValue
|
|
113
|
+
* ```ts
|
|
114
|
+
* {
|
|
115
|
+
* backgroundColor: "black"
|
|
116
|
+
* }
|
|
117
|
+
* ```
|
|
118
|
+
*/
|
|
119
|
+
containerStyle?: ViewStyle;
|
|
120
|
+
/**
|
|
121
|
+
* Styling for the container of each element.
|
|
122
|
+
*
|
|
123
|
+
* @defaultValue
|
|
124
|
+
* ```ts
|
|
125
|
+
* {
|
|
126
|
+
* backgroundColor: "transparent",
|
|
127
|
+
* justifyContent: "center"
|
|
128
|
+
* }
|
|
129
|
+
* ```
|
|
130
|
+
*/
|
|
131
|
+
itemContainerStyle?: ViewStyle;
|
|
132
|
+
/**
|
|
133
|
+
* Styling for the text of the elements.
|
|
134
|
+
*
|
|
135
|
+
* @defaultValue
|
|
136
|
+
* ```ts
|
|
137
|
+
* {
|
|
138
|
+
* fontSize: "18",
|
|
139
|
+
* color: "white"
|
|
140
|
+
* }
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
143
|
+
itemTextStyle?: TextStyle;
|
|
144
|
+
/**
|
|
145
|
+
* If enabled, will show a gradient fade towards the top and bottom of the wheel picker.
|
|
146
|
+
*
|
|
147
|
+
* @defaultValue true
|
|
148
|
+
*/
|
|
149
|
+
enableGradient?: boolean;
|
|
150
|
+
/**
|
|
151
|
+
* Color the gradient should fade to at the top and bottom.
|
|
152
|
+
*
|
|
153
|
+
* @defaultValue containerStyle.backgroundColor
|
|
154
|
+
*/
|
|
155
|
+
gradientFadeColor?: ColorValue;
|
|
156
|
+
};
|
|
157
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,UAAU,EACf,KAAK,iBAAiB,EAEtB,KAAK,SAAS,EAEd,KAAK,SAAS,EAEf,MAAM,cAAc,CAAC;AAGtB;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,EACpC,UAAU,EAAE,yBAAyB,CAAC,CAAC,CAAC,2CAgHzC;AA+ND;;GAEG;AACH,MAAM,MAAM,yBAAyB,CAAC,CAAC,IAAI;IACzC;;;;;;;;;;;;;;;;OAgBG;IACH,WAAW,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,IAAI,CAAC;IAC5B;;OAEG;IACH,YAAY,EAAE,MAAM,CAAC;IACrB;;OAEG;IACH,IAAI,EAAE,CAAC,EAAE,CAAC;IAEV;;;;;;;;;OASG;IACH,QAAQ,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAC;IAC5B;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACH,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;IACvD;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;;;OAIG;IACH,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC;;;;OAIG;IACH,0BAA0B,CAAC,EAAE,MAAM,CAAC;IACpC;;;;;;;;;OASG;IACH,cAAc,CAAC,EAAE,SAAS,CAAC;IAC3B;;;;;;;;;;OAUG;IACH,kBAAkB,CAAC,EAAE,SAAS,CAAC;IAC/B;;;;;;;;;;OAUG;IACH,aAAa,CAAC,EAAE,SAAS,CAAC;IAE1B;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,UAAU,CAAC;CAChC,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@amabeth/repeating-wheel-picker",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A React Native wheel picker that allows endless scrolling through repeating content",
|
|
5
|
+
"main": "./lib/module/index.js",
|
|
6
|
+
"types": "./lib/typescript/src/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"source": "./src/index.tsx",
|
|
10
|
+
"types": "./lib/typescript/src/index.d.ts",
|
|
11
|
+
"default": "./lib/module/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./package.json": "./package.json"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"src",
|
|
17
|
+
"lib",
|
|
18
|
+
"android",
|
|
19
|
+
"ios",
|
|
20
|
+
"cpp",
|
|
21
|
+
"*.podspec",
|
|
22
|
+
"react-native.config.js",
|
|
23
|
+
"!ios/build",
|
|
24
|
+
"!android/build",
|
|
25
|
+
"!android/gradle",
|
|
26
|
+
"!android/gradlew",
|
|
27
|
+
"!android/gradlew.bat",
|
|
28
|
+
"!android/local.properties",
|
|
29
|
+
"!**/__tests__",
|
|
30
|
+
"!**/__fixtures__",
|
|
31
|
+
"!**/__mocks__",
|
|
32
|
+
"!**/.*"
|
|
33
|
+
],
|
|
34
|
+
"scripts": {
|
|
35
|
+
"example": "yarn workspace repeating-wheel-picker-example",
|
|
36
|
+
"test": "jest",
|
|
37
|
+
"typecheck": "tsc",
|
|
38
|
+
"lint": "eslint \"**/*.{js,ts,tsx}\"",
|
|
39
|
+
"clean": "del-cli lib",
|
|
40
|
+
"prepare": "bob build & husky",
|
|
41
|
+
"docs": "typedoc",
|
|
42
|
+
"release": "release-it --only-version"
|
|
43
|
+
},
|
|
44
|
+
"keywords": [
|
|
45
|
+
"react-native",
|
|
46
|
+
"ios",
|
|
47
|
+
"android"
|
|
48
|
+
],
|
|
49
|
+
"repository": {
|
|
50
|
+
"type": "git",
|
|
51
|
+
"url": "git+https://github.com/amabeth/repeating-wheel-picker.git"
|
|
52
|
+
},
|
|
53
|
+
"author": "amabeth <amabeth.dev@gmail.com> (https://github.com/amabeth)",
|
|
54
|
+
"license": "MIT",
|
|
55
|
+
"bugs": {
|
|
56
|
+
"url": "https://github.com/amabeth/repeating-wheel-picker/issues"
|
|
57
|
+
},
|
|
58
|
+
"homepage": "https://github.com/amabeth/repeating-wheel-picker#readme",
|
|
59
|
+
"publishConfig": {
|
|
60
|
+
"registry": "https://registry.npmjs.org/"
|
|
61
|
+
},
|
|
62
|
+
"devDependencies": {
|
|
63
|
+
"@commitlint/config-conventional": "^19.6.0",
|
|
64
|
+
"@eslint/compat": "^1.3.1",
|
|
65
|
+
"@eslint/eslintrc": "^3.3.1",
|
|
66
|
+
"@eslint/js": "^9.30.1",
|
|
67
|
+
"@evilmartians/lefthook": "^1.11.16",
|
|
68
|
+
"@react-native/babel-preset": "^0.80.1",
|
|
69
|
+
"@react-native/eslint-config": "^0.80.1",
|
|
70
|
+
"@release-it/conventional-changelog": "^10.0.1",
|
|
71
|
+
"@types/jest": "^30.0.0",
|
|
72
|
+
"@types/react": "^19.0.14",
|
|
73
|
+
"commitlint": "^19.8.1",
|
|
74
|
+
"del-cli": "^6.0.0",
|
|
75
|
+
"eslint": "^9.30.1",
|
|
76
|
+
"eslint-config-prettier": "^10.1.5",
|
|
77
|
+
"eslint-plugin-prettier": "^5.5.1",
|
|
78
|
+
"husky": "^9.1.7",
|
|
79
|
+
"jest": "^30.0.4",
|
|
80
|
+
"prettier": "^3.6.2",
|
|
81
|
+
"react-native-builder-bob": "^0.40.12",
|
|
82
|
+
"release-it": "^19.0.3",
|
|
83
|
+
"typedoc-github-theme": "^0.3.0",
|
|
84
|
+
"typedoc-plugin-coverage": "^4.0.1",
|
|
85
|
+
"typescript": "^5.8.3"
|
|
86
|
+
},
|
|
87
|
+
"peerDependencies": {
|
|
88
|
+
"expo-linear-gradient": "^14.1.5",
|
|
89
|
+
"react": "19.0.0",
|
|
90
|
+
"react-native": "0.79.5"
|
|
91
|
+
},
|
|
92
|
+
"overrides": {
|
|
93
|
+
"@react-native/eslint-config": {
|
|
94
|
+
"eslint": "$eslint"
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
"workspaces": [
|
|
98
|
+
"example"
|
|
99
|
+
],
|
|
100
|
+
"packageManager": "yarn@3.6.1",
|
|
101
|
+
"jest": {
|
|
102
|
+
"preset": "react-native",
|
|
103
|
+
"modulePathIgnorePatterns": [
|
|
104
|
+
"<rootDir>/example/node_modules",
|
|
105
|
+
"<rootDir>/lib/"
|
|
106
|
+
]
|
|
107
|
+
},
|
|
108
|
+
"commitlint": {
|
|
109
|
+
"extends": [
|
|
110
|
+
"@commitlint/config-conventional"
|
|
111
|
+
]
|
|
112
|
+
},
|
|
113
|
+
"release-it": {
|
|
114
|
+
"git": {
|
|
115
|
+
"commitMessage": "chore: release ${version}",
|
|
116
|
+
"tagName": "v${version}"
|
|
117
|
+
},
|
|
118
|
+
"npm": {
|
|
119
|
+
"publish": true
|
|
120
|
+
},
|
|
121
|
+
"github": {
|
|
122
|
+
"release": true
|
|
123
|
+
},
|
|
124
|
+
"plugins": {
|
|
125
|
+
"@release-it/conventional-changelog": {
|
|
126
|
+
"preset": {
|
|
127
|
+
"name": "angular"
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
"prettier": {
|
|
133
|
+
"quoteProps": "consistent",
|
|
134
|
+
"singleQuote": false,
|
|
135
|
+
"tabWidth": 2,
|
|
136
|
+
"trailingComma": "es5",
|
|
137
|
+
"useTabs": false
|
|
138
|
+
},
|
|
139
|
+
"react-native-builder-bob": {
|
|
140
|
+
"source": "src",
|
|
141
|
+
"output": "lib",
|
|
142
|
+
"targets": [
|
|
143
|
+
[
|
|
144
|
+
"module",
|
|
145
|
+
{
|
|
146
|
+
"esm": true
|
|
147
|
+
}
|
|
148
|
+
],
|
|
149
|
+
[
|
|
150
|
+
"typescript",
|
|
151
|
+
{
|
|
152
|
+
"project": "tsconfig.build.json"
|
|
153
|
+
}
|
|
154
|
+
]
|
|
155
|
+
]
|
|
156
|
+
},
|
|
157
|
+
"create-react-native-library": {
|
|
158
|
+
"languages": "js",
|
|
159
|
+
"type": "library",
|
|
160
|
+
"version": "0.51.1"
|
|
161
|
+
},
|
|
162
|
+
"typedocOptions": {
|
|
163
|
+
"entryPoints": [
|
|
164
|
+
"src/index.tsx"
|
|
165
|
+
],
|
|
166
|
+
"projectDocuments": [
|
|
167
|
+
"*.md"
|
|
168
|
+
],
|
|
169
|
+
"sort": [
|
|
170
|
+
"source-order"
|
|
171
|
+
],
|
|
172
|
+
"plugin": [
|
|
173
|
+
"typedoc-plugin-coverage",
|
|
174
|
+
"typedoc-github-theme"
|
|
175
|
+
]
|
|
176
|
+
}
|
|
177
|
+
}
|
package/src/index.tsx
ADDED
|
@@ -0,0 +1,507 @@
|
|
|
1
|
+
import { type RefObject, useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
+
import {
|
|
3
|
+
type AnimatableNumericValue,
|
|
4
|
+
type ColorValue,
|
|
5
|
+
type LayoutChangeEvent,
|
|
6
|
+
Text,
|
|
7
|
+
type TextStyle,
|
|
8
|
+
View,
|
|
9
|
+
type ViewStyle,
|
|
10
|
+
VirtualizedList,
|
|
11
|
+
} from "react-native";
|
|
12
|
+
import { LinearGradient } from "expo-linear-gradient";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Provides a wheel picker with repeating data that can be infinitely scrolled.
|
|
16
|
+
*
|
|
17
|
+
* @param properties configuration of the wheel picker
|
|
18
|
+
*/
|
|
19
|
+
export function RepeatingWheelPicker<T>(
|
|
20
|
+
properties: RepeatingWheelPickerProps<T>
|
|
21
|
+
) {
|
|
22
|
+
// set defaults for all unprovided optional properties
|
|
23
|
+
const props = useMemo(() => withDefaults(properties), [properties]);
|
|
24
|
+
// to always have enough data to scroll through, define how often the input data should be multiplied
|
|
25
|
+
const dataMultiplier = useMemo(
|
|
26
|
+
() => Math.max(Math.round(90 / props.data.length), 3),
|
|
27
|
+
[props.data.length]
|
|
28
|
+
);
|
|
29
|
+
// difference between centered index and top most visible index
|
|
30
|
+
const indexDiffTopToCentered = useMemo(
|
|
31
|
+
() => Math.floor(props.itemDisplayCount / 2),
|
|
32
|
+
[props.itemDisplayCount]
|
|
33
|
+
);
|
|
34
|
+
// offsets for all list elements
|
|
35
|
+
const offsets = useMemo((): number[] =>
|
|
36
|
+
getOffsets(
|
|
37
|
+
props.data.length,
|
|
38
|
+
props.itemDisplayCount,
|
|
39
|
+
props.itemHeight,
|
|
40
|
+
dataMultiplier),
|
|
41
|
+
[props.data.length, props.itemDisplayCount, props.itemHeight, dataMultiplier, props.containerVerticalPadding]
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
// current selected item (centered one)
|
|
45
|
+
const [current, setCurrent] = useState(() =>
|
|
46
|
+
props.initialIndex + props.data.length * Math.floor(dataMultiplier / 2)
|
|
47
|
+
);
|
|
48
|
+
const listRef = useRef<VirtualizedList<T>>(null);
|
|
49
|
+
|
|
50
|
+
// call "setSelected" when the current top item or the data changed
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
const selectedElement =
|
|
53
|
+
props.data[current % props.data.length]; // centered element
|
|
54
|
+
|
|
55
|
+
if (selectedElement !== undefined) {
|
|
56
|
+
props.setSelected(selectedElement);
|
|
57
|
+
}
|
|
58
|
+
}, [current, props.data]);
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<View
|
|
62
|
+
onLayout={props.containerOnLayout}
|
|
63
|
+
style={{
|
|
64
|
+
...props.containerStyle,
|
|
65
|
+
height:
|
|
66
|
+
props.itemHeight * props.itemDisplayCount + props.containerVerticalPadding * 2
|
|
67
|
+
}}
|
|
68
|
+
>
|
|
69
|
+
<VirtualizedList<T>
|
|
70
|
+
ref={listRef}
|
|
71
|
+
scrollEnabled={props.enabled}
|
|
72
|
+
getItemCount={() => props.data.length * dataMultiplier}
|
|
73
|
+
|
|
74
|
+
initialScrollIndex={current - indexDiffTopToCentered}
|
|
75
|
+
initialNumToRender={props.data.length * dataMultiplier}
|
|
76
|
+
windowSize={props.data.length * dataMultiplier}
|
|
77
|
+
|
|
78
|
+
renderItem={({ item, index }) => (
|
|
79
|
+
<Item item={item} props={props} key={index} />
|
|
80
|
+
)}
|
|
81
|
+
getItem={(_, index) =>
|
|
82
|
+
props.data[index % props.data.length]!
|
|
83
|
+
}
|
|
84
|
+
getItemLayout={(_, index) => ({
|
|
85
|
+
length: props.itemHeight,
|
|
86
|
+
offset: offsets[index]!,
|
|
87
|
+
index: index,
|
|
88
|
+
})}
|
|
89
|
+
keyExtractor={(_, index) => `${index}`}
|
|
90
|
+
|
|
91
|
+
// disableIntervalMomentum={true}
|
|
92
|
+
decelerationRate="fast"
|
|
93
|
+
snapToOffsets={offsets}
|
|
94
|
+
|
|
95
|
+
onMomentumScrollEnd={(event) =>
|
|
96
|
+
onMomentumScrollEnd(
|
|
97
|
+
event.nativeEvent.contentOffset.y,
|
|
98
|
+
setCurrent,
|
|
99
|
+
props.data.length,
|
|
100
|
+
props.itemHeight,
|
|
101
|
+
dataMultiplier,
|
|
102
|
+
indexDiffTopToCentered,
|
|
103
|
+
props.containerVerticalPadding,
|
|
104
|
+
listRef
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
showsVerticalScrollIndicator={false}
|
|
109
|
+
style={{
|
|
110
|
+
flex: 1,
|
|
111
|
+
width: "100%",
|
|
112
|
+
borderRadius: props.containerStyle.borderRadius,
|
|
113
|
+
paddingVertical: props.containerVerticalPadding,
|
|
114
|
+
paddingHorizontal: props.containerHorizontalPadding
|
|
115
|
+
}}
|
|
116
|
+
/>
|
|
117
|
+
|
|
118
|
+
{ props.enableGradient &&
|
|
119
|
+
<View
|
|
120
|
+
style={{
|
|
121
|
+
backgroundColor: "transparent",
|
|
122
|
+
position: "absolute",
|
|
123
|
+
height: "100%",
|
|
124
|
+
width: "100%"
|
|
125
|
+
}}
|
|
126
|
+
>
|
|
127
|
+
<FrontGradient gradientFadeColor={props.gradientFadeColor} borderRadius={props.containerStyle.borderRadius} />
|
|
128
|
+
</View>
|
|
129
|
+
}
|
|
130
|
+
</View>
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function Item<T>({
|
|
135
|
+
item,
|
|
136
|
+
props,
|
|
137
|
+
}: {
|
|
138
|
+
item: T;
|
|
139
|
+
props: RepeatingWheelPickerPropsWithDefaults<T>;
|
|
140
|
+
}) {
|
|
141
|
+
|
|
142
|
+
return (
|
|
143
|
+
<View
|
|
144
|
+
style={{
|
|
145
|
+
justifyContent: "center",
|
|
146
|
+
alignContent: "center",
|
|
147
|
+
backgroundColor: "transparent",
|
|
148
|
+
...props.itemContainerStyle,
|
|
149
|
+
paddingVertical: 0,
|
|
150
|
+
height: props.itemHeight,
|
|
151
|
+
}}
|
|
152
|
+
>
|
|
153
|
+
<Text style={{ textAlign: "center", ...props.itemTextStyle }}>
|
|
154
|
+
{props.getLabel(item)}
|
|
155
|
+
</Text>
|
|
156
|
+
</View>
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function FrontGradient({ gradientFadeColor, borderRadius }: { gradientFadeColor: ColorValue, borderRadius: string | AnimatableNumericValue | undefined }) {
|
|
161
|
+
|
|
162
|
+
return (
|
|
163
|
+
<LinearGradient
|
|
164
|
+
colors={[gradientFadeColor, "transparent", gradientFadeColor]}
|
|
165
|
+
style={{
|
|
166
|
+
height: "100%",
|
|
167
|
+
width: "100%",
|
|
168
|
+
backgroundColor: "transparent",
|
|
169
|
+
borderRadius: borderRadius
|
|
170
|
+
}}
|
|
171
|
+
/>
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function getOffsets(
|
|
176
|
+
dataLength: number,
|
|
177
|
+
itemDisplayCount: number,
|
|
178
|
+
itemHeight: number,
|
|
179
|
+
dataMultiplier: number
|
|
180
|
+
) {
|
|
181
|
+
let offsets = [];
|
|
182
|
+
|
|
183
|
+
// calculate offset for all items
|
|
184
|
+
for (let i = 0; i < dataLength * dataMultiplier; i++) {
|
|
185
|
+
offsets[i] = itemOffset(
|
|
186
|
+
i,
|
|
187
|
+
itemDisplayCount,
|
|
188
|
+
itemHeight
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return offsets;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function itemOffset(
|
|
196
|
+
index: number,
|
|
197
|
+
itemDisplayCount: number,
|
|
198
|
+
itemHeight: number
|
|
199
|
+
) {
|
|
200
|
+
|
|
201
|
+
return (index + (itemDisplayCount % 2 === 0 ? 0.5 : 0)) * itemHeight;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function onMomentumScrollEnd<T>(
|
|
205
|
+
offset: number,
|
|
206
|
+
setCurrent: (n: number) => void,
|
|
207
|
+
dataLength: number,
|
|
208
|
+
itemHeight: number,
|
|
209
|
+
dataMultiplier: number,
|
|
210
|
+
indexDiffTopToCentered: number,
|
|
211
|
+
verticalPadding: number,
|
|
212
|
+
ref: RefObject<VirtualizedList<T> | null>
|
|
213
|
+
) {
|
|
214
|
+
// offset excluding padding
|
|
215
|
+
const innerOffset = offset - verticalPadding;
|
|
216
|
+
// get index of top most completely visible item
|
|
217
|
+
const currentTopIndex = Math.round(innerOffset / itemHeight);
|
|
218
|
+
|
|
219
|
+
// get current section within whole extended data (data * dataMultiplier)
|
|
220
|
+
// section 0 = [0, data.length)
|
|
221
|
+
// section 1 = [data.length, data.length * 2)
|
|
222
|
+
// ...
|
|
223
|
+
const currentSection = Math.floor(innerOffset / (dataLength * itemHeight));
|
|
224
|
+
// target section is always the middle one, so user can scroll seemingly infinitely
|
|
225
|
+
const targetSection = Math.floor(dataMultiplier / 2);
|
|
226
|
+
|
|
227
|
+
// get corresponding index of current top index in target section
|
|
228
|
+
const targetTopIndex =
|
|
229
|
+
currentTopIndex + (targetSection - currentSection) * dataLength;
|
|
230
|
+
// set current index to centered one, if `targetTopIndex`was at the top
|
|
231
|
+
setCurrent(targetTopIndex + indexDiffTopToCentered);
|
|
232
|
+
|
|
233
|
+
if (currentSection === targetSection) {
|
|
234
|
+
// if target section is current section, stay in this section
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// if target section is different from current section, scroll to target
|
|
239
|
+
const targetOffset = offset + (targetTopIndex - currentTopIndex) * itemHeight;
|
|
240
|
+
ref.current?.scrollToOffset({ animated: false, offset: targetOffset });
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// props
|
|
244
|
+
|
|
245
|
+
function withDefaults<T>(
|
|
246
|
+
props: RepeatingWheelPickerProps<T>
|
|
247
|
+
): RepeatingWheelPickerPropsWithDefaults<T> {
|
|
248
|
+
const defaultBackgroundColor = "black";
|
|
249
|
+
const defaultTextColor = "white";
|
|
250
|
+
const defaultTextSize = 18;
|
|
251
|
+
|
|
252
|
+
validateProps(props);
|
|
253
|
+
|
|
254
|
+
return {
|
|
255
|
+
...props,
|
|
256
|
+
|
|
257
|
+
// optional
|
|
258
|
+
containerOnLayout: props.containerOnLayout ?? (() => {}),
|
|
259
|
+
enabled: props.enabled ?? true,
|
|
260
|
+
|
|
261
|
+
getLabel: props.getLabel ?? ((t: T) => `${t}`),
|
|
262
|
+
|
|
263
|
+
itemHeight: props.itemHeight ?? (props.itemTextStyle?.fontSize ?? defaultTextSize) + 15,
|
|
264
|
+
itemDisplayCount: props.itemDisplayCount ?? 3,
|
|
265
|
+
|
|
266
|
+
containerVerticalPadding: props.containerVerticalPadding ?? 0,
|
|
267
|
+
containerHorizontalPadding: props.containerHorizontalPadding ?? 10,
|
|
268
|
+
containerStyle: {
|
|
269
|
+
...props.containerStyle,
|
|
270
|
+
backgroundColor:
|
|
271
|
+
props.containerStyle?.backgroundColor ?? defaultBackgroundColor,
|
|
272
|
+
padding: 0,
|
|
273
|
+
paddingHorizontal: 0,
|
|
274
|
+
paddingVertical: 0,
|
|
275
|
+
paddingTop: 0,
|
|
276
|
+
paddingBottom: 0,
|
|
277
|
+
paddingLeft: 0,
|
|
278
|
+
paddingRight: 0
|
|
279
|
+
},
|
|
280
|
+
itemContainerStyle: {
|
|
281
|
+
...props.itemContainerStyle,
|
|
282
|
+
backgroundColor:
|
|
283
|
+
props.itemContainerStyle?.backgroundColor ?? "transparent",
|
|
284
|
+
justifyContent: props.itemContainerStyle?.justifyContent ?? "center"
|
|
285
|
+
},
|
|
286
|
+
itemTextStyle: {
|
|
287
|
+
...props.itemTextStyle,
|
|
288
|
+
fontSize: props.itemTextStyle?.fontSize ?? defaultTextSize,
|
|
289
|
+
color: props.itemTextStyle?.color ?? defaultTextColor,
|
|
290
|
+
},
|
|
291
|
+
|
|
292
|
+
enableGradient: props.enableGradient ?? true,
|
|
293
|
+
gradientFadeColor: props.gradientFadeColor ?? (props.containerStyle?.backgroundColor ?? defaultBackgroundColor)
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function validateProps<T>(props: RepeatingWheelPickerProps<T>) {
|
|
298
|
+
if (props.initialIndex < 0 || props.initialIndex >= props.data.length) {
|
|
299
|
+
throw InvalidPropertiesError(
|
|
300
|
+
"initialIndex",
|
|
301
|
+
String(props.initialIndex),
|
|
302
|
+
"has to be in range [0, data.length)"
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
if (props.data.length < 2) {
|
|
307
|
+
throw InvalidPropertiesError(
|
|
308
|
+
"data.length",
|
|
309
|
+
String(props.data.length),
|
|
310
|
+
"has to be larger than 1"
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if (props.itemDisplayCount !== undefined && props.itemDisplayCount < 1) {
|
|
315
|
+
throw InvalidPropertiesError(
|
|
316
|
+
"itemDisplayCount",
|
|
317
|
+
String(props.itemDisplayCount),
|
|
318
|
+
"has to be larger than 0"
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
if (props.itemDisplayCount !== undefined && !Number.isInteger(props.itemDisplayCount)) {
|
|
322
|
+
throw InvalidPropertiesError(
|
|
323
|
+
"itemDisplayCount",
|
|
324
|
+
String(props.itemDisplayCount),
|
|
325
|
+
"has to be an integer"
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (props.itemHeight !== undefined && props.itemHeight < 1) {
|
|
330
|
+
throw InvalidPropertiesError(
|
|
331
|
+
"itemHeight",
|
|
332
|
+
String(props.itemHeight),
|
|
333
|
+
"has to be larger than 0"
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
function InvalidPropertiesError(
|
|
339
|
+
propertyName: string,
|
|
340
|
+
propertyValue: string,
|
|
341
|
+
violatedConstraint: string
|
|
342
|
+
) {
|
|
343
|
+
return Error(
|
|
344
|
+
`Value "${propertyValue}" is invalid for property "${propertyName}": ${violatedConstraint}`
|
|
345
|
+
) as InvalidPropertiesError;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
interface InvalidPropertiesError extends Error {
|
|
349
|
+
name: "InvalidPropertiesError";
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
type RepeatingWheelPickerPropsWithDefaults<T> = RepeatingWheelPickerProps<T> &
|
|
353
|
+
Required<Omit<RepeatingWheelPickerProps<T>, "containerRef">>;
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
*
|
|
357
|
+
*/
|
|
358
|
+
export type RepeatingWheelPickerProps<T> = {
|
|
359
|
+
/**
|
|
360
|
+
* Function to set currently selected element and use it in your application.
|
|
361
|
+
*
|
|
362
|
+
* @example
|
|
363
|
+
* ```ts
|
|
364
|
+
* const [selected, setSelected] = useState(0);
|
|
365
|
+
*
|
|
366
|
+
* return (
|
|
367
|
+
* <RepeatingWheelPicker
|
|
368
|
+
* setSelected={setSelected}
|
|
369
|
+
* //...
|
|
370
|
+
* />
|
|
371
|
+
* );
|
|
372
|
+
* ```
|
|
373
|
+
*
|
|
374
|
+
* @param t currently selected element
|
|
375
|
+
*/
|
|
376
|
+
setSelected: (t: T) => void;
|
|
377
|
+
/**
|
|
378
|
+
* Index to initially center.
|
|
379
|
+
*/
|
|
380
|
+
initialIndex: number;
|
|
381
|
+
/**
|
|
382
|
+
* Data to display.
|
|
383
|
+
*/
|
|
384
|
+
data: T[];
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Function to retrieve the text to display for an element as a label.
|
|
388
|
+
*
|
|
389
|
+
* @defaultValue
|
|
390
|
+
* ```ts
|
|
391
|
+
* (t: T) => `${t}`
|
|
392
|
+
* ```
|
|
393
|
+
*
|
|
394
|
+
* @param t element to retrieve label for
|
|
395
|
+
*/
|
|
396
|
+
getLabel?: (t: T) => string;
|
|
397
|
+
/**
|
|
398
|
+
* Function called when the layout of the container changes.
|
|
399
|
+
*
|
|
400
|
+
* _Example usage for monitoring the container's height:_
|
|
401
|
+
* ```ts
|
|
402
|
+
* const [pickerHeight, setPickerHeight] = useState<number>(0);
|
|
403
|
+
*
|
|
404
|
+
* const onLayout = useCallback((event: LayoutChangeEvent) => {
|
|
405
|
+
* const { height } = event.nativeEvent.layout;
|
|
406
|
+
* setPickerHeight(height);
|
|
407
|
+
* }, []);
|
|
408
|
+
*
|
|
409
|
+
* return (
|
|
410
|
+
* <View style={{flexDirection: "row"}}>
|
|
411
|
+
* <View style={{height: height}}>
|
|
412
|
+
* <Text>Picker label</Text>
|
|
413
|
+
* </View>
|
|
414
|
+
* <RepeatingWheelPicker
|
|
415
|
+
* //...
|
|
416
|
+
* containerOnLayout={onLayout}
|
|
417
|
+
* />
|
|
418
|
+
* </View>
|
|
419
|
+
* );
|
|
420
|
+
* ```
|
|
421
|
+
*
|
|
422
|
+
* @defaultValue () => {}
|
|
423
|
+
*
|
|
424
|
+
* @param event layout change event that triggered `onLayout`
|
|
425
|
+
*/
|
|
426
|
+
containerOnLayout?: (event: LayoutChangeEvent) => void;
|
|
427
|
+
/**
|
|
428
|
+
* Enables / disables scrolling of the wheel picker.
|
|
429
|
+
*
|
|
430
|
+
* @defaultValue true
|
|
431
|
+
*/
|
|
432
|
+
enabled?: boolean;
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Height per displayed item.
|
|
436
|
+
*
|
|
437
|
+
* @defaultValue itemTextStyle.fontSize + 15
|
|
438
|
+
*/
|
|
439
|
+
itemHeight?: number;
|
|
440
|
+
/**
|
|
441
|
+
* Number of items to display.
|
|
442
|
+
*
|
|
443
|
+
* @defaultValue 3
|
|
444
|
+
*/
|
|
445
|
+
itemDisplayCount?: number;
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Vertical padding for the container of the wheel picker.
|
|
449
|
+
*
|
|
450
|
+
* @defaultValue 0
|
|
451
|
+
*/
|
|
452
|
+
containerVerticalPadding?: number;
|
|
453
|
+
/**
|
|
454
|
+
* Horizontal padding for the container of the wheel picker.
|
|
455
|
+
*
|
|
456
|
+
* @defaultValue 10
|
|
457
|
+
*/
|
|
458
|
+
containerHorizontalPadding?: number;
|
|
459
|
+
/**
|
|
460
|
+
* Styling for the container of the wheel picker.
|
|
461
|
+
*
|
|
462
|
+
* @defaultValue
|
|
463
|
+
* ```ts
|
|
464
|
+
* {
|
|
465
|
+
* backgroundColor: "black"
|
|
466
|
+
* }
|
|
467
|
+
* ```
|
|
468
|
+
*/
|
|
469
|
+
containerStyle?: ViewStyle;
|
|
470
|
+
/**
|
|
471
|
+
* Styling for the container of each element.
|
|
472
|
+
*
|
|
473
|
+
* @defaultValue
|
|
474
|
+
* ```ts
|
|
475
|
+
* {
|
|
476
|
+
* backgroundColor: "transparent",
|
|
477
|
+
* justifyContent: "center"
|
|
478
|
+
* }
|
|
479
|
+
* ```
|
|
480
|
+
*/
|
|
481
|
+
itemContainerStyle?: ViewStyle;
|
|
482
|
+
/**
|
|
483
|
+
* Styling for the text of the elements.
|
|
484
|
+
*
|
|
485
|
+
* @defaultValue
|
|
486
|
+
* ```ts
|
|
487
|
+
* {
|
|
488
|
+
* fontSize: "18",
|
|
489
|
+
* color: "white"
|
|
490
|
+
* }
|
|
491
|
+
* ```
|
|
492
|
+
*/
|
|
493
|
+
itemTextStyle?: TextStyle;
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* If enabled, will show a gradient fade towards the top and bottom of the wheel picker.
|
|
497
|
+
*
|
|
498
|
+
* @defaultValue true
|
|
499
|
+
*/
|
|
500
|
+
enableGradient?: boolean;
|
|
501
|
+
/**
|
|
502
|
+
* Color the gradient should fade to at the top and bottom.
|
|
503
|
+
*
|
|
504
|
+
* @defaultValue containerStyle.backgroundColor
|
|
505
|
+
*/
|
|
506
|
+
gradientFadeColor?: ColorValue;
|
|
507
|
+
};
|