@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
- export interface SelectButtonProps extends AccessibilityProps, Pick<ViewProps, 'testID'> {
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
- * @see https://www.figma.com/design/4TYeki0MDLhfPGJstbIicf/UI-kit-PrimeFace-(DS)?node-id=484-4921
11
- */
12
- export const SelectButton = memo(({ buttons, disabled, initialIndex: initialIndexProp, onPress: onPressProp, size, style, testID, position: positionProp, ...rest }) => {
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 = useMemo(() => !positionProp, [positionProp]);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cdek-it/react-native-ui-kit",
3
- "version": "1.0.0-beta.9",
3
+ "version": "1.0.1",
4
4
  "description": "UI kit на основе Prime Faces, Prime Flex для React Native",
5
5
  "license": "MIT",
6
6
  "homepage": "https://developer.cdek.ru/design-system",