react-native-slot-text 1.0.8 → 2.0.1

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.
@@ -1,10 +1,10 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { useEffect, useState, useRef, useCallback, Fragment } from "react";
3
- import { View, Text, Animated } from "react-native";
4
- import ReAnimated, { ZoomIn, StretchOutX } from 'react-native-reanimated';
2
+ import { useState, useRef, Fragment, useLayoutEffect, useCallback } from "react";
3
+ import { View, Text } from "react-native";
4
+ import { useSharedValue } from 'react-native-reanimated';
5
5
  import styles from './styles';
6
6
  import Slot from './Slot';
7
- import { formatString } from "./helpers";
7
+ import { getNewSlots, getNewCommaPositions } from './helpers';
8
8
  const DEFAULT_DURATION = 200;
9
9
  /**
10
10
  * AnimatedNumbers Component
@@ -22,108 +22,73 @@ const DEFAULT_DURATION = 200;
22
22
  * @returns {JSX.Element} The animated number component with slots for digits and commas.
23
23
  */
24
24
  const AnimatedNumbers = (props) => {
25
+ // [number] : static number
26
+ /// [number, null]: removing slot
27
+ // [null, number]: adding slot
28
+ // [number, number]: animating slot
25
29
  const idRef = useRef(`slots-${Math.random().toString(36).substring(7)}`);
26
- const [state, setState] = useState('idle');
27
- const [oldNumber, setOldNumber] = useState([]);
28
- const [newNumber, setNewNumber] = useState([]);
29
- const [animatingValue, setAnimatingValue] = useState();
30
- // The initial positions of the slots that are animating in
31
- // -1 indicates above the slot, 0 indicates the slot, 1 indicates below the slot
32
- const [inZeroPositions, setInZeroPositions] = useState([]);
33
- // The final positions of the slots animating out
34
- const [outFinalPositions, setOutFinalPositions] = useState([]);
35
- const [containerSize, setContainerSize] = useState({ width: 0, height: 0 });
36
- const containerWidth = useRef(new Animated.Value(0)).current;
37
- const containerHeight = useRef(new Animated.Value(0)).current;
38
- const measureRef = useRef(null);
39
- useEffect(() => {
40
- setOldNumber(`${props.prefix || ''}${props.value}`
41
- .split('')
42
- .map((val, i) => isNaN(val) ? val : parseInt(val)));
43
- }, []);
44
- useEffect(() => {
45
- // Animation is in progress, queue the new value
46
- if (state === 'idle' && oldNumber.join('') !== formatString(props.value, props.prefix)) {
47
- setAnimatingValue(formatString(props.value, props.prefix));
30
+ // Indexes of slots that have finished animating
31
+ const slotsTrashBin = useSharedValue([]);
32
+ const queuedNumber = useSharedValue(null);
33
+ const [slots, setSlots] = useState([]);
34
+ const [commaPositions, setCommaPositions] = useState([]);
35
+ const [commaWidth, setCommaWidth] = useState(0);
36
+ const [sizesMeasured, setSizesMeasured] = useState(false);
37
+ const [keys, setKeys] = useState(Array.from({ length: props.value.toString().length }).map((_, i) => i));
38
+ const [slotHeight, setSlotHeight] = useState(0);
39
+ const [charSizes, setCharSizes] = useState(Array.from({ length: 10 }).map((_, i) => 0));
40
+ useLayoutEffect(() => {
41
+ if (slots.some(s => s.length > 1)) {
42
+ queuedNumber.value = props.value;
48
43
  }
49
- }, [props.value, state]);
50
- useEffect(() => {
51
- if (!animatingValue)
52
- return;
53
- setState('animating');
54
- // If the old number is not set, this means it's the initial rendering of this component
55
- // and the value needs to be loaded
56
- if (oldNumber?.length === 0) {
57
- setOldNumber(animatingValue
58
- .split('')
59
- .map((val, i) => isNaN(val) ? val : parseInt(val)));
60
- }
61
- // Prepare the new number for animation
62
44
  else {
63
- setNewNumber(animatingValue
64
- .split('')
65
- .map((val, i) => isNaN(val) ? val : parseInt(val)));
66
- // The positions of the slots that are animating in
67
- let newValuePositions = Array(animatingValue.length).fill(0);
68
- // Compare from the right to the left
69
- // * If the slots are the same value, no animation is needed for the slot, just stick it in the -1 position
70
- // * If one of the slots is a string, the new slot will move down
71
- // * If the new slot is greater than the old slot, the new slot will move down
72
- // or up depending on which is greater
73
- for (let i = 0; i < newValuePositions.length; i++) {
74
- const oldNum = oldNumber[i];
75
- const newNum = isNaN(animatingValue.charAt(i))
76
- ? animatingValue.charAt(i)
77
- : parseInt(animatingValue.charAt(i));
78
- if (oldNum === newNum) {
79
- newValuePositions[i] = undefined;
80
- }
81
- else {
82
- newValuePositions[i] = typeof oldNum === 'string' || typeof newNum === 'string'
83
- ? -1
84
- : oldNum === undefined ? -1 : newNum > oldNum ? -1 : 1;
85
- }
45
+ const parseFromLeft = props.value.toString()[0] === slots[0]?.[0]?.toString();
46
+ if (props.includeComma) {
47
+ const newCommaPositions = getNewCommaPositions(props.value.toString(), commaPositions, parseFromLeft);
48
+ setCommaPositions(newCommaPositions);
86
49
  }
87
- setInZeroPositions(newValuePositions);
88
- setOutFinalPositions([
89
- ...newValuePositions.slice(0, oldNumber.length).map(val => val ? val * -1 : 0),
90
- ...Array(Math.max(0, oldNumber.length - animatingValue.length)).fill(-1)
91
- ]);
50
+ const newSlots = getNewSlots(props.value, slots, parseFromLeft);
51
+ setKeys(Array.from({ length: newSlots.length }).map((_, i) => i));
52
+ setSlots(newSlots);
92
53
  }
93
- }, [animatingValue]);
94
- const onMeasureLayout = useCallback((e) => {
95
- if (containerSize.width === 0) {
96
- containerWidth.setValue(e.nativeEvent.layout.width);
97
- containerHeight.setValue(e.nativeEvent.layout.height);
54
+ }, [props.value]);
55
+ const onCompleted = useCallback(() => {
56
+ const cleanedSlots = slots
57
+ .filter(s => (Number.isFinite(s[0]) && s[1] !== null) || Number.isFinite(s[1]))
58
+ .map(s => Number.isFinite(s[1]) ? [s[1]] : [s[0]]);
59
+ const numberOfSlotsRemoved = Math.max(slots.length - cleanedSlots.length, 0);
60
+ const cleanedCommas = commaPositions
61
+ .map(c => c === -1 ? null : c === 1 ? 0 : c)
62
+ .slice(slots[0]?.[1] === null ? numberOfSlotsRemoved : 0) // Trim from left
63
+ .slice(0, slots[slots.length - 1]?.[1] === null ? -1 * numberOfSlotsRemoved : undefined); // Trim from right
64
+ if (cleanedSlots.length < slots.length) {
65
+ setKeys(prev => prev.slice(-1 * cleanedSlots.length));
98
66
  }
99
67
  else {
100
- Animated.timing(containerWidth, {
101
- toValue: e.nativeEvent.layout.width,
102
- useNativeDriver: false,
103
- duration: containerSize.width > e.nativeEvent.layout.width ? props.animationDuration || DEFAULT_DURATION : 0
104
- }).start();
105
- Animated.timing(containerHeight, {
106
- toValue: e.nativeEvent.layout.height,
107
- duration: 100,
108
- useNativeDriver: false
109
- }).start();
68
+ setKeys(Array.from({ length: cleanedSlots.length }).map((_, i) => i));
110
69
  }
111
- setContainerSize(e.nativeEvent.layout);
112
- }, []);
113
- const onCompleted = useCallback(() => {
114
- setOldNumber(newNumber);
115
- setNewNumber([]);
116
- setInZeroPositions([]);
117
- setOutFinalPositions([]);
118
- setState('idle');
119
- }, [newNumber]);
120
- return (_jsxs(_Fragment, { children: [_jsxs(Animated.View, { style: [
121
- { width: containerWidth, height: containerHeight },
122
- styles.animatedNumbers
123
- ], children: [_jsx(View, { style: styles.slotsContainer, children: oldNumber.map((val, i) => (_jsxs(Fragment, { children: [(oldNumber.length - i) % 3 === 0 && i > 1 && props.includeComma &&
124
- _jsx(ReAnimated.View, { entering: ZoomIn.duration((props.animationDuration || DEFAULT_DURATION) / 2).withInitialValues({ opacity: 0 }), exiting: StretchOutX.withInitialValues({ opacity: 1 }).duration(props.animationDuration || DEFAULT_DURATION), children: _jsx(Text, { style: props.fontStyle, children: "," }) }, `${idRef}-comma-${i}-old`), _jsx(Slot, { value: val, height: containerSize.height, initial: 0, final: outFinalPositions[i] || 0, animationDuration: props.animationDuration || DEFAULT_DURATION, fontStyle: props.fontStyle }, `${idRef}-${val}-${i}-old`)] }, `${idRef}-slot-${i}`))) }), _jsx(View, { style: styles.slotsContainer, children: newNumber.map((val, i) => (_jsxs(Fragment, { children: [(newNumber.length - i) % 3 === 0 && i > 1 && props.includeComma &&
125
- _jsx(ReAnimated.View, { entering: ZoomIn.duration((props.animationDuration || DEFAULT_DURATION) / 2), exiting: StretchOutX.withInitialValues({ opacity: 1 }).duration(props.animationDuration || DEFAULT_DURATION), style: styles.hiddenComma, children: _jsx(Text, { style: props.fontStyle, children: "," }) }), _jsx(Slot, { value: val, initial: inZeroPositions[i] || -1, final: inZeroPositions[i] ? 0 : -1, height: containerSize.height, animationDuration: props.animationDuration || DEFAULT_DURATION, fontStyle: props.fontStyle, onCompleted: onCompleted })] }, `${idRef}-${val}-${i}-new`))) })] }), newNumber.length > 0 &&
126
- _jsxs(View, { onLayout: onMeasureLayout, style: styles.spacer, ref: measureRef, children: [props.prefix && _jsx(Text, { style: props.fontStyle, children: props.prefix }), newNumber.map((val, i) => (_jsx(Text, { style: props.fontStyle, children: val }, `${val}-${i}`)))] })] }));
70
+ setSlots(cleanedSlots);
71
+ setCommaPositions(cleanedCommas);
72
+ slotsTrashBin.value = [];
73
+ }, [slots, commaPositions]);
74
+ return (_jsxs(_Fragment, { children: [_jsxs(View, { style: styles.slotsContainer, children: [props.prefix && (_jsx(Text, { style: props.fontStyle, children: props.prefix })), slots.map((slot, i) => {
75
+ const callback = i === slots.findIndex(s => s.length > 1) ? onCompleted : undefined;
76
+ return (_jsx(Slot, { slot: slot, index: i, height: slotHeight, charSizes: charSizes, commaWidth: commaWidth, commaPositions: props.includeComma && commaPositions.length ? commaPositions : undefined, onCompleted: callback, animationDuration: (props.animationDuration || DEFAULT_DURATION), fontStyle: props.fontStyle }, `${idRef.current}-${keys[i]}`));
77
+ }), _jsx(Text, { style: [styles.spacer, props.fontStyle], children: "1" })] }), !sizesMeasured && Array.from({ length: 10 }).concat(',').map((_, i) => (_jsx(Fragment, { children: _jsx(Text, { style: [styles.hiddenSlot, props.fontStyle], onLayout: (e) => {
78
+ setSlotHeight(e.nativeEvent.layout.height);
79
+ if (i === 10) {
80
+ setCommaWidth(e.nativeEvent.layout.width);
81
+ }
82
+ else {
83
+ const charSize = e.nativeEvent.layout.width;
84
+ setCharSizes(prev => {
85
+ const newSizes = [...prev];
86
+ newSizes[i] = charSize;
87
+ return newSizes;
88
+ });
89
+ setSizesMeasured(true);
90
+ }
91
+ }, children: i === 10 ? ',' : i }, `slot-${i}`) }, `measure-slot-${i}`)))] }));
127
92
  };
128
93
  /*
129
94
  Basic logic:
package/Slot.js CHANGED
@@ -1,27 +1,113 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { useRef, useEffect } from 'react';
3
- import { Text, Animated } from 'react-native';
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useLayoutEffect, useState } from 'react';
3
+ import { View } from 'react-native';
4
+ import { Text } from 'react-native';
5
+ import Animated, { useSharedValue, withTiming, withSequence, useAnimatedStyle, interpolate, runOnJS, Easing } from 'react-native-reanimated';
6
+ import styles from './styles';
4
7
  const Slot = (props) => {
5
- const Y = useRef(new Animated.Value(props.initial * props.height));
6
- useEffect(() => {
7
- if (props.final !== undefined) {
8
- Animated.timing(Y.current, {
9
- toValue: props.final * props.height,
10
- duration: props.animationDuration,
11
- useNativeDriver: true
12
- }).start(() => {
13
- if (props.onCompleted) {
14
- props.onCompleted();
15
- }
8
+ const [value1, setValue1] = useState();
9
+ const [value2, setValue2] = useState();
10
+ const margin = useSharedValue(0);
11
+ const y = useSharedValue(0);
12
+ const incomingY = useSharedValue(0);
13
+ const commaScale = useSharedValue(0);
14
+ const commaWidth = useSharedValue(0);
15
+ // React to slot value changes
16
+ useLayoutEffect(() => {
17
+ if (!props.slot) {
18
+ return;
19
+ }
20
+ const incomingValue = props.slot[1];
21
+ const currentValue = props.slot[0];
22
+ if (incomingValue === undefined) {
23
+ y.value = 0;
24
+ incomingY.value = props.height;
25
+ margin.value = 0;
26
+ }
27
+ // Removing slot
28
+ else if (incomingValue === null) {
29
+ y.value = withTiming(-1 * props.height, { duration: props.animationDuration, easing: Easing.ease }, () => {
30
+ props.onCompleted && runOnJS(props.onCompleted)();
16
31
  });
32
+ margin.value = withTiming(-1 * props.charSizes[currentValue], { duration: props.animationDuration / 2 });
33
+ }
34
+ // Adding slot
35
+ else if (currentValue === null) {
36
+ margin.value = withTiming(props.charSizes[incomingValue], { duration: props.animationDuration / 2 });
37
+ incomingY.value = withSequence(withTiming(-1 * props.height, { duration: 0 }), withTiming(0, { duration: props.animationDuration, easing: Easing.ease }, () => {
38
+ props.onCompleted && runOnJS(props.onCompleted)();
39
+ }));
40
+ }
41
+ // Animating slot
42
+ else {
43
+ margin.value = withTiming((props.charSizes[incomingValue] - props.charSizes[currentValue]), { duration: props.animationDuration / 2 });
44
+ if (incomingValue > currentValue) {
45
+ y.value = withTiming(props.height, { duration: props.animationDuration, easing: Easing.ease });
46
+ incomingY.value = withSequence(withTiming(-1 * props.height, { duration: 0, easing: Easing.ease }), withTiming(0, { duration: props.animationDuration }, () => { props.onCompleted && runOnJS(props.onCompleted)(); }));
47
+ }
48
+ else if (incomingValue < currentValue) {
49
+ y.value = withTiming(-1 * props.height, { duration: props.animationDuration, easing: Easing.ease });
50
+ incomingY.value = withSequence(withTiming(1 * props.height, { duration: 0, easing: Easing.ease }), withTiming(0, { duration: props.animationDuration }, () => { props.onCompleted && runOnJS(props.onCompleted)(); }));
51
+ }
52
+ }
53
+ }, [props.slot]);
54
+ useLayoutEffect(() => {
55
+ if (props.slot) {
56
+ setValue1(props.slot[0]);
57
+ setValue2(props.slot[1]);
58
+ }
59
+ }, [props.slot]);
60
+ useLayoutEffect(() => {
61
+ if (props.commaPositions?.[props.index] === 1) {
62
+ commaScale.value = withTiming(1, { duration: props.animationDuration });
63
+ commaWidth.value = withSequence(withTiming(0, { duration: 0 }), withTiming(props.commaWidth, { duration: props.animationDuration }));
64
+ }
65
+ else if (props.commaPositions?.[props.index] === -1) {
66
+ commaScale.value = withSequence(withTiming(1, { duration: 0 }), withTiming(0, { duration: props.animationDuration }));
67
+ const timeout = setTimeout(() => {
68
+ commaWidth.value = withTiming(0, { duration: props.animationDuration });
69
+ }, 0);
70
+ return () => clearTimeout(timeout);
71
+ }
72
+ else if (props.commaPositions?.[props.index] === 0) {
73
+ commaScale.value = 1;
74
+ commaWidth.value = props.commaWidth;
75
+ }
76
+ else {
77
+ commaScale.value = 0;
78
+ commaWidth.value = 0;
17
79
  }
18
- }, [props.final]);
19
- return (_jsx(Animated.View, { style: [{
20
- transform: [{ translateY: Y.current }],
21
- opacity: Y.current.interpolate({
22
- inputRange: [-1 * props.height, 0, props.height],
23
- outputRange: [0, 1, 0]
24
- })
25
- }], children: _jsx(Text, { style: props.fontStyle, children: props.value }) }));
80
+ }, [props.commaPositions, props.commaWidth]);
81
+ const commaStyle = useAnimatedStyle(() => ({
82
+ transform: [{ scale: commaScale.value }],
83
+ width: commaWidth.value
84
+ }));
85
+ const currentStyle = useAnimatedStyle(() => ({
86
+ transform: [
87
+ { translateY: y.value },
88
+ { scale: interpolate(y.value, [-1 * props.height, 0, props.height], [0, 1, 0]) }
89
+ ],
90
+ marginRight: margin.value,
91
+ opacity: interpolate(y.value, [-1 * Math.pow(props.height, .95), 0, Math.pow(props.height, .95)], [0, 1, 0]),
92
+ }));
93
+ const incomingStyle = useAnimatedStyle(() => {
94
+ return ({
95
+ transform: [
96
+ { translateY: incomingY.value },
97
+ { scale: interpolate(incomingY.value, [-1 * props.height, 0, props.height], [0, 1, 0]) },
98
+ ],
99
+ opacity: interpolate(incomingY.value, [-1 * Math.pow(props.height, .95), 0, Math.pow(props.height, .95)], [0, 1, 0]),
100
+ position: 'absolute'
101
+ });
102
+ });
103
+ const incomingTextStyle = useAnimatedStyle(() => ({
104
+ transform: [{ rotateX: `${interpolate(incomingY.value, [-1 * props.height / 2, 0, props.height / 2], [90, 0, -90])}deg` }]
105
+ }));
106
+ const currentTextStyle = useAnimatedStyle(() => ({
107
+ transform: [{ rotateX: `${interpolate(y.value, [-1 * props.height / 2, 0, props.height / 2], [90, 0, -90])}deg` }]
108
+ }));
109
+ return (_jsxs(View, { style: styles.slotContainer, children: [_jsx(Animated.View, { style: currentStyle, children: Number.isFinite(value1) &&
110
+ _jsx(Animated.Text, { style: [props.fontStyle, currentTextStyle], children: value1 }) }), _jsx(Animated.View, { style: incomingStyle, children: Number.isFinite(value2) &&
111
+ _jsx(Animated.Text, { style: [props.fontStyle, incomingTextStyle], children: value2 }) }), _jsx(Text, { style: styles.hiddenSlot, children: "1" }), _jsx(Animated.View, { style: commaStyle, children: _jsx(Text, { style: props.fontStyle, children: "," }) })] }));
26
112
  };
27
113
  export default Slot;
package/helpers.d.ts CHANGED
@@ -1 +1,3 @@
1
- export declare const formatString: (v: string, p?: string) => string;
1
+ import type { SlotValue, CommaPosition } from "./types";
2
+ export declare const getNewSlots: (newValue: number, currentSlots: SlotValue[], parseFromLeft: boolean) => SlotValue[];
3
+ export declare const getNewCommaPositions: (newValue: string, currentCommaPositions: CommaPosition[], parseFromLeft: boolean) => CommaPosition[];
package/helpers.js CHANGED
@@ -1 +1,78 @@
1
- export const formatString = (v, p) => `${p || ''}${v}`;
1
+ export const getNewSlots = (newValue, currentSlots, parseFromLeft) => {
2
+ const iterLength = Math.max(newValue.toString().length, currentSlots.length);
3
+ const shiftCurrent = parseFromLeft
4
+ ? 0
5
+ : newValue.toString().length > currentSlots.length ? currentSlots.length - newValue.toString().length : 0;
6
+ const shiftIncoming = parseFromLeft
7
+ ? 0
8
+ : currentSlots.length > newValue.toString().length ? newValue.toString().length - currentSlots.length : 0;
9
+ const newSlots = Array.from({ length: iterLength }, (_, i) => [i]);
10
+ Array.from({ length: iterLength }, (_, i) => parseFromLeft ? i : iterLength - i - 1).forEach(i => {
11
+ const newValueParsed = parseInt(`${newValue}`[i + shiftIncoming]);
12
+ const currentSlotValue = currentSlots[i + shiftCurrent]?.[0];
13
+ // First pass through, just set the slots since shared slots is empty
14
+ if (currentSlots.length === 0) {
15
+ return newSlots[i] = [newValueParsed];
16
+ }
17
+ // Removing slot
18
+ if (isNaN(newValueParsed)) {
19
+ newSlots[i] = [currentSlotValue, null];
20
+ }
21
+ // Adding slot
22
+ else if (currentSlotValue === undefined || currentSlotValue === null) {
23
+ newSlots[i] = [null, newValueParsed];
24
+ }
25
+ // Animating slot
26
+ else if (currentSlotValue !== newValueParsed) {
27
+ newSlots[i] = [currentSlotValue, newValueParsed];
28
+ }
29
+ // Static slot
30
+ else {
31
+ newSlots[i] = [newValueParsed];
32
+ }
33
+ });
34
+ return newSlots;
35
+ };
36
+ // Takes in a new value and spits out an array of comma positions
37
+ // for all of the commas being added, removed, or staying the sameM
38
+ export const getNewCommaPositions = (newValue, currentCommaPositions, parseFromLeft) => {
39
+ const parseLength = Math.max(newValue.length, currentCommaPositions.length);
40
+ // On first render, no size change
41
+ const sizeChange = currentCommaPositions.length === 0
42
+ ? 0
43
+ : newValue.length - currentCommaPositions.length;
44
+ const windowSize = sizeChange > 0 ? parseLength : parseLength + sizeChange;
45
+ const newCommaPositions = Array.from({ length: parseLength }, (_, i) => null);
46
+ // Copy over the current comma positions
47
+ currentCommaPositions.forEach((c, i) => {
48
+ if (parseFromLeft || sizeChange < 0) {
49
+ newCommaPositions[i] = c;
50
+ }
51
+ else {
52
+ newCommaPositions[i + sizeChange] = c;
53
+ }
54
+ });
55
+ for (let i = 0; i < parseLength; i++) {
56
+ const index = parseFromLeft ? i : parseLength - i - 1;
57
+ const windowIndex = parseFromLeft ? i : windowSize - i - 1;
58
+ const isCommaIndex = (windowSize - windowIndex - 1) % 3 === 0 && (windowSize - windowIndex - 1) !== 0;
59
+ const outSizeVisibleWindow = parseFromLeft
60
+ ? index > windowSize - 1
61
+ : index < parseLength - windowSize;
62
+ // If in the section that's being removed, animate out all commmas
63
+ if (outSizeVisibleWindow && currentCommaPositions.length > 0) {
64
+ newCommaPositions[index] = Number.isFinite(newCommaPositions[index]) ? -1 : null;
65
+ }
66
+ // If is a comma index
67
+ else if (isCommaIndex) {
68
+ newCommaPositions[index] = Number.isFinite(newCommaPositions[index])
69
+ ? 0
70
+ : currentCommaPositions.length === 0 ? 0 : 1;
71
+ }
72
+ // Animate out since the comma doesn't match the new value's comma positions
73
+ else if (Number.isFinite(newCommaPositions[index])) {
74
+ newCommaPositions[index] = -1;
75
+ }
76
+ }
77
+ return newCommaPositions;
78
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-slot-text",
3
- "version": "1.0.8",
3
+ "version": "2.0.1",
4
4
  "author": "sc-mitton <scm.mitton@gmail.com> (https://github.com/sc-mitton)",
5
5
  "main": "index.js",
6
6
  "module": "index.ts",
package/styles.d.ts CHANGED
@@ -1,18 +1,17 @@
1
1
  declare const styles: {
2
- animatedNumbers: {
3
- overflow: "hidden";
4
- };
5
2
  spacer: {
6
- position: "absolute";
7
- flexDirection: "row";
8
- opacity: number;
3
+ width: number;
9
4
  };
10
5
  slotsContainer: {
11
- position: "absolute";
12
6
  flexDirection: "row";
13
7
  justifyContent: "center";
14
8
  };
15
- hiddenComma: {
9
+ slotContainer: {
10
+ flexDirection: "row";
11
+ alignItems: "flex-end";
12
+ };
13
+ hiddenSlot: {
14
+ position: "absolute";
16
15
  opacity: number;
17
16
  };
18
17
  };
package/styles.js CHANGED
@@ -1,19 +1,18 @@
1
1
  import { StyleSheet } from 'react-native';
2
2
  const styles = StyleSheet.create({
3
- animatedNumbers: {
4
- overflow: 'hidden',
5
- },
6
3
  spacer: {
7
- position: 'absolute',
8
- flexDirection: 'row',
9
- opacity: 0,
4
+ width: 0,
10
5
  },
11
6
  slotsContainer: {
12
- position: 'absolute',
13
7
  flexDirection: 'row',
14
8
  justifyContent: 'center',
15
9
  },
16
- hiddenComma: {
10
+ slotContainer: {
11
+ flexDirection: 'row',
12
+ alignItems: 'flex-end'
13
+ },
14
+ hiddenSlot: {
15
+ position: 'absolute',
17
16
  opacity: 0,
18
17
  }
19
18
  });
package/types.d.ts CHANGED
@@ -1,17 +1,30 @@
1
1
  import type { StyleProp, TextStyle } from 'react-native';
2
+ import type { SharedValue } from 'react-native-reanimated';
2
3
  export type Position = -1 | 0 | 1;
4
+ export type SlotValue = [number] | [number | null, number | null];
3
5
  export interface SlotProps {
4
- value: number | string;
6
+ slot: SlotValue;
7
+ index: number;
5
8
  animationDuration: number;
6
9
  fontStyle?: StyleProp<TextStyle>;
7
- initial: Position;
8
- final: Position;
9
- height: number;
10
- isNew?: boolean;
10
+ commaWidth: number;
11
11
  onCompleted?: () => void;
12
+ commaPositions?: CommaPosition[];
13
+ charSizes: number[];
14
+ height: number;
15
+ }
16
+ export type CommaPosition = 1 | -1 | 0 | null;
17
+ export interface CommaProps {
18
+ isEntering: boolean;
19
+ isExiting: boolean;
20
+ width: SharedValue<number>;
21
+ animationDuration: number;
22
+ fontStyle?: StyleProp<TextStyle>;
23
+ onExited: () => void;
24
+ onEntered: () => void;
12
25
  }
13
26
  export interface AnimatedNumbersProps {
14
- value: `${number}`;
27
+ value: number;
15
28
  fontStyle?: StyleProp<TextStyle>;
16
29
  animationDuration?: number;
17
30
  prefix?: string;
package/types.js CHANGED
@@ -1 +1 @@
1
- import { Text } from 'react-native';
1
+ export {};