@cdek-it/react-native-ui-kit 1.0.0-beta.9 → 1.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,22 +1,15 @@
|
|
|
1
1
|
import { type AccessibilityProps, type StyleProp, type ViewProps, type ViewStyle } from 'react-native';
|
|
2
2
|
import { type SharedValue } from 'react-native-reanimated';
|
|
3
3
|
import { type SelectButtonItemProps } from './SelectButtonItem';
|
|
4
|
-
|
|
4
|
+
interface SelectButtonBaseProps extends AccessibilityProps, Pick<ViewProps, 'testID'> {
|
|
5
5
|
/** Массив кнопок. Должен содержать как минимум 2 кнопки. */
|
|
6
|
-
buttons: Array<Pick<SelectButtonItemProps, 'label' | 'showIcon' | 'Icon'> & {
|
|
6
|
+
buttons: Array<Pick<SelectButtonItemProps, 'accessibilityLabel' | 'label' | 'showIcon' | 'Icon'> & {
|
|
7
7
|
key: string;
|
|
8
8
|
}>;
|
|
9
9
|
/** true - если кнопки недоступны для нажатия */
|
|
10
10
|
disabled?: boolean;
|
|
11
|
-
/** Индекс выбранной кнопки при первом рендере */
|
|
12
|
-
initialIndex?: number;
|
|
13
11
|
/** Вызывается при нажатии на кнопку */
|
|
14
12
|
onPress?: (index: number) => void;
|
|
15
|
-
/**
|
|
16
|
-
* Анимированное значение 0...n-1, где n - это количество кнопок.
|
|
17
|
-
* Используется для контроля элемента извне. Если передано, то компонент становится управляемым.
|
|
18
|
-
*/
|
|
19
|
-
position?: SharedValue<number>;
|
|
20
13
|
/**
|
|
21
14
|
* Выбор размера элемента
|
|
22
15
|
* @default 'base'
|
|
@@ -25,8 +18,21 @@ export interface SelectButtonProps extends AccessibilityProps, Pick<ViewProps, '
|
|
|
25
18
|
/** Дополнительная стилизация для контейнера компонента */
|
|
26
19
|
style?: StyleProp<ViewStyle>;
|
|
27
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* `position` имеет приоритет над `selectedIndex`, а `selectedIndex` — над
|
|
23
|
+
* `initialIndex`. Одновременная передача сохранена для обратной совместимости.
|
|
24
|
+
*/
|
|
25
|
+
export interface SelectButtonProps extends SelectButtonBaseProps {
|
|
26
|
+
/** Индекс выбранной кнопки при первом рендере */
|
|
27
|
+
initialIndex?: number;
|
|
28
|
+
/** Индекс выбранной кнопки в контролируемом режиме */
|
|
29
|
+
selectedIndex?: number;
|
|
30
|
+
/** Значение позиции для синхронизации с внешней анимацией */
|
|
31
|
+
position?: SharedValue<number>;
|
|
32
|
+
}
|
|
28
33
|
/**
|
|
29
34
|
* Используется для маркировки элементов интерфейса
|
|
30
35
|
* @see https://www.figma.com/design/4TYeki0MDLhfPGJstbIicf/UI-kit-PrimeFace-(DS)?node-id=484-4921
|
|
31
36
|
*/
|
|
32
37
|
export declare const SelectButton: import("react").NamedExoticComponent<SelectButtonProps>;
|
|
38
|
+
export {};
|
|
@@ -1,15 +1,21 @@
|
|
|
1
|
-
import { memo, useCallback, useMemo, useRef, useState } from 'react';
|
|
1
|
+
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
2
2
|
import { View, } from 'react-native';
|
|
3
3
|
import Animated, { Easing, interpolate, useAnimatedReaction, useAnimatedStyle, useSharedValue, withTiming, } from 'react-native-reanimated';
|
|
4
4
|
import { StyleSheet } from 'react-native-unistyles';
|
|
5
5
|
import { scheduleOnRN } from 'react-native-worklets';
|
|
6
6
|
import { SelectButtonItem, } from './SelectButtonItem';
|
|
7
7
|
const DEFAULT_ANIMATION_DURATION = 200; // ms
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
// TODO(next major): экспортировать StrictSelectButtonProps как SelectButtonProps, после миграции удалить адаптер и тесты
|
|
9
|
+
const normalizeSelectButtonProps = ({ initialIndex, position, selectedIndex, ...baseProps }) => {
|
|
10
|
+
if (position !== undefined) {
|
|
11
|
+
return { ...baseProps, position };
|
|
12
|
+
}
|
|
13
|
+
if (selectedIndex !== undefined) {
|
|
14
|
+
return { ...baseProps, selectedIndex };
|
|
15
|
+
}
|
|
16
|
+
return { ...baseProps, initialIndex };
|
|
17
|
+
};
|
|
18
|
+
const SelectButtonStrict = memo(({ buttons, disabled, initialIndex: initialIndexProp, onPress: onPressProp, selectedIndex, size, style, testID, position: positionProp, ...rest }) => {
|
|
13
19
|
const buttonsLayoutListRef = useRef([]);
|
|
14
20
|
const buttonsLayoutList = useSharedValue([]);
|
|
15
21
|
const [frameKey, setFrameKey] = useState(Date.now());
|
|
@@ -27,15 +33,24 @@ export const SelectButton = memo(({ buttons, disabled, initialIndex: initialInde
|
|
|
27
33
|
scheduleOnRN(setFrameKey, Date.now());
|
|
28
34
|
}
|
|
29
35
|
});
|
|
30
|
-
const positionInner = useSharedValue(initialIndex);
|
|
36
|
+
const positionInner = useSharedValue(selectedIndex ?? initialIndex);
|
|
31
37
|
const position = useMemo(() => positionProp || positionInner, [positionInner, positionProp]);
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
if (selectedIndex === undefined) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
positionInner.value = withTiming(selectedIndex, {
|
|
43
|
+
duration: DEFAULT_ANIMATION_DURATION,
|
|
44
|
+
easing: Easing.linear,
|
|
45
|
+
});
|
|
46
|
+
}, [positionInner, selectedIndex]);
|
|
32
47
|
const animationInputRange = useMemo(() => buttons.map((_, index) => index), [buttons]);
|
|
33
48
|
const framePositionStyle = useAnimatedStyle(() => {
|
|
34
49
|
const left = interpolate(position.value, animationInputRange, animationInputRange.map((index) => buttonsLayoutList.value[index]?.x ?? 0));
|
|
35
50
|
const width = interpolate(position.value, animationInputRange, animationInputRange.map((index) => buttonsLayoutList.value[index]?.width ?? 0));
|
|
36
51
|
return { left, width };
|
|
37
52
|
});
|
|
38
|
-
const isUncontrolledComponent =
|
|
53
|
+
const isUncontrolledComponent = positionProp === undefined && selectedIndex === undefined;
|
|
39
54
|
const onPress = useCallback((index) => {
|
|
40
55
|
if (isUncontrolledComponent) {
|
|
41
56
|
positionInner.value = withTiming(index, {
|
|
@@ -46,11 +61,16 @@ export const SelectButton = memo(({ buttons, disabled, initialIndex: initialInde
|
|
|
46
61
|
onPressProp?.(index);
|
|
47
62
|
}, [isUncontrolledComponent, onPressProp, positionInner]);
|
|
48
63
|
return (<View collapsable={false} style={[styles.container, style]} testID={testID} {...rest}>
|
|
49
|
-
{buttons.map(({ label, Icon, key, showIcon }, index) => (<SelectButtonItem Icon={Icon} disabled={disabled} index={index} key={key} label={label} position={position} showIcon={showIcon} size={size} testID={`SelectButton_SelectButtonItem_${index}`} onLayout={(event) => onButtonLayout(index, event)} onPress={() => onPress(index)}/>))}
|
|
64
|
+
{buttons.map(({ accessibilityLabel, label, Icon, key, showIcon }, index) => (<SelectButtonItem Icon={Icon} accessibilityLabel={accessibilityLabel} disabled={disabled} index={index} key={key} label={label} position={position} showIcon={showIcon} size={size} testID={`SelectButton_SelectButtonItem_${index}`} onLayout={(event) => onButtonLayout(index, event)} onPress={() => onPress(index)}/>))}
|
|
50
65
|
|
|
51
66
|
{!disabled && (<Animated.View key={frameKey} style={[styles.frame, framePositionStyle]} testID='SelectButton_AnimatedFrame'/>)}
|
|
52
67
|
</View>);
|
|
53
68
|
});
|
|
69
|
+
/**
|
|
70
|
+
* Используется для маркировки элементов интерфейса
|
|
71
|
+
* @see https://www.figma.com/design/4TYeki0MDLhfPGJstbIicf/UI-kit-PrimeFace-(DS)?node-id=484-4921
|
|
72
|
+
*/
|
|
73
|
+
export const SelectButton = memo((props) => (<SelectButtonStrict {...normalizeSelectButtonProps(props)}/>));
|
|
54
74
|
const styles = StyleSheet.create(({ theme }) => ({
|
|
55
75
|
container: {
|
|
56
76
|
flexDirection: 'row',
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type ViewProps } from 'react-native';
|
|
2
2
|
import { type SharedValue } from 'react-native-reanimated';
|
|
3
3
|
import { type SvgSource } from '../../utils/SvgUniversal';
|
|
4
|
-
export interface SelectButtonItemProps extends Pick<ViewProps, 'onLayout' | 'testID'> {
|
|
4
|
+
export interface SelectButtonItemProps extends Pick<ViewProps, 'accessibilityLabel' | 'onLayout' | 'testID'> {
|
|
5
5
|
/** Индекс кнопки */
|
|
6
6
|
index: number;
|
|
7
7
|
/** Обработчик нажатия на кнопку */
|
|
@@ -13,7 +13,7 @@ export interface SelectButtonItemProps extends Pick<ViewProps, 'onLayout' | 'tes
|
|
|
13
13
|
position: SharedValue<number>;
|
|
14
14
|
/** true - если кнопка недоступна для нажатия */
|
|
15
15
|
disabled?: boolean;
|
|
16
|
-
/** Текст на
|
|
16
|
+
/** Текст на кнопке. Может быть опущен для сегмента только с иконкой. */
|
|
17
17
|
label?: string;
|
|
18
18
|
/**
|
|
19
19
|
* Выбор размера элемента
|
|
@@ -8,7 +8,7 @@ import { SvgUniversal } from '../../utils/SvgUniversal';
|
|
|
8
8
|
* Дочерний элемент компонента SelectButton. Не используется отдельно от SelectButton.
|
|
9
9
|
* @see https://www.figma.com/design/4TYeki0MDLhfPGJstbIicf/UI-kit-PrimeFace-(DS)?node-id=481-4393
|
|
10
10
|
*/
|
|
11
|
-
export const SelectButtonItem = memo(({ index, position, onPress, disabled, label, onLayout, size = 'base', showIcon = true, Icon, }) => {
|
|
11
|
+
export const SelectButtonItem = memo(({ accessibilityLabel, index, position, onPress, disabled, label, onLayout, testID, size = 'base', showIcon = true, Icon, }) => {
|
|
12
12
|
const sizeMap = useMemo(() => ({
|
|
13
13
|
small: { icon: styles.iconSmall, label: styles.labelSmall },
|
|
14
14
|
base: { icon: styles.iconBase, label: styles.labelBase },
|
|
@@ -32,11 +32,11 @@ export const SelectButtonItem = memo(({ index, position, onPress, disabled, labe
|
|
|
32
32
|
scheduleOnRN(setIsSelected, value === index);
|
|
33
33
|
}
|
|
34
34
|
});
|
|
35
|
-
return (<TouchableOpacity disabled={disabled} style={[
|
|
35
|
+
return (<TouchableOpacity accessibilityLabel={accessibilityLabel} accessibilityRole='button' accessibilityState={{ disabled, selected: isSelected }} disabled={disabled} style={[
|
|
36
36
|
styles.container,
|
|
37
37
|
styles[size],
|
|
38
38
|
disabled && styles.disabledContainer,
|
|
39
|
-
]} testID='SelectButtonItem_TouchableOpacity' onLayout={onLayout} onPress={onPress}>
|
|
39
|
+
]} testID={testID || 'SelectButtonItem_TouchableOpacity'} onLayout={onLayout} onPress={onPress}>
|
|
40
40
|
{Icon && showIcon ? (<SvgUniversal {...iconSize} source={Icon} testID='SelectButtonItem_Icon' uniProps={({ theme }) => ({
|
|
41
41
|
color: disabled
|
|
42
42
|
? theme.Button.Disabled.disabledButtonBorderColor
|
package/package.json
CHANGED