@cdek-it/react-native-ui-kit 0.2.8 → 0.3.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.
@@ -58,7 +58,9 @@ export const InputTextBase = memo(
58
58
  });
59
59
  }, [isFocused, labelAnimation, value]);
60
60
  const iconSize = useMemo(() => (floatLabel ? styles.iconSizeFloatLabel : styles.iconSize), [floatLabel, styles.iconSize, styles.iconSizeFloatLabel]);
61
- useImperativeHandle(propsInputRef, () => (inputRef.current ? { ...inputRef.current, clear } : null), [inputRef, clear]);
61
+ useImperativeHandle(propsInputRef, () => (inputRef.current
62
+ ? Object.assign(inputRef.current, { clear })
63
+ : null), [inputRef, clear]);
62
64
  const { makeTestId } = useMakeTestId(otherProps.testID || InputTextBaseTestId.default);
63
65
  const [userDefinedSecureTextEntry, setUserDefinedSecureTextEntry] = useState(true);
64
66
  const secureTextEntry = useMemo(() => secureTextEntryProp === 'toggleable'
@@ -1,7 +1,6 @@
1
1
  import type { ViewProps } from 'react-native-svg/lib/typescript/fabric/utils';
2
2
  interface SkeletonProps extends ViewProps {
3
3
  }
4
- export declare const ANIMATION_DURATION = 1200;
5
4
  /**
6
5
  * Используется для отображения контента в момент загрузки
7
6
  * @see https://www.figma.com/design/4TYeki0MDLhfPGJstbIicf/UI-kit-PrimeFace-(DS)?node-id=5241-3731
@@ -1,24 +1,36 @@
1
- import { memo, useEffect } from 'react';
1
+ import { memo, useCallback, useContext, useEffect, useRef } from 'react';
2
2
  import { View } from 'react-native';
3
- import Animated, { Easing, interpolate, useAnimatedStyle, useSharedValue, withRepeat, withTiming, } from 'react-native-reanimated';
3
+ import Animated, { useAnimatedStyle, useSharedValue, } from 'react-native-reanimated';
4
4
  import Svg, { Defs, LinearGradient, Rect, Stop } from 'react-native-svg';
5
+ import { SkeletonContext } from '../../utils/SkeletonContext';
5
6
  import { makeStyles } from '../../utils/makeStyles';
6
- export const ANIMATION_DURATION = 1200; // ms
7
7
  /**
8
8
  * Используется для отображения контента в момент загрузки
9
9
  * @see https://www.figma.com/design/4TYeki0MDLhfPGJstbIicf/UI-kit-PrimeFace-(DS)?node-id=5241-3731
10
10
  */
11
11
  export const Skeleton = memo(({ style, testID, ...rest }) => {
12
12
  const styles = useStyles();
13
- const animation = useSharedValue(0);
13
+ const { globalTranslateX, registerSkeleton, unregisterSkeleton, skeletonWidth, } = useContext(SkeletonContext);
14
+ const skeletonRef = useRef(null);
15
+ const skeletonX = useSharedValue(0);
16
+ const onLayout = useCallback(() => {
17
+ skeletonRef.current?.measure((_x, _y, _width, _height, pageX) => {
18
+ skeletonX.value = pageX;
19
+ });
20
+ }, [skeletonX]);
21
+ useEffect(() => {
22
+ registerSkeleton();
23
+ return unregisterSkeleton;
24
+ }, [registerSkeleton, unregisterSkeleton]);
14
25
  const animatedStyles = useAnimatedStyle(() => ({
15
- left: `${interpolate(animation.value, [0, 1], [-100, 100])}%`,
26
+ transform: [{ translateX: globalTranslateX.value - skeletonX.value }],
16
27
  }));
17
- useEffect(() => {
18
- animation.value = withRepeat(withTiming(1, { duration: ANIMATION_DURATION, easing: Easing.ease }), -1);
19
- }, [animation]);
20
- return (<View {...rest} style={[styles.container, style]} testID={testID ?? SkeletonTestId.root}>
21
- <Animated.View style={[styles.gradientContainer, animatedStyles]} testID={SkeletonTestId.animatedView}>
28
+ return (<View {...rest} ref={skeletonRef} style={[styles.container, style]} testID={testID ?? SkeletonTestId.root} onLayout={onLayout}>
29
+ <Animated.View style={[
30
+ styles.gradientContainer,
31
+ { width: skeletonWidth },
32
+ animatedStyles,
33
+ ]} testID={SkeletonTestId.animatedView}>
22
34
  <Svg testID={SkeletonTestId.svg}>
23
35
  <Defs>
24
36
  <LinearGradient id='gradient' x1='0' x2='1' y1='1' y2='1'>
@@ -38,12 +50,7 @@ const useStyles = makeStyles(({ border, theme }) => ({
38
50
  overflow: 'hidden',
39
51
  backgroundColor: theme.Misc.Skeleton.skeletonBg,
40
52
  },
41
- gradientContainer: {
42
- position: 'absolute',
43
- width: '100%',
44
- height: '100%',
45
- left: '-100%',
46
- },
53
+ gradientContainer: { position: 'absolute', height: '100%' },
47
54
  gradientColor: { backgroundColor: theme.Misc.Skeleton.skeletonAnimationBg },
48
55
  }));
49
56
  export const SkeletonTestId = {
@@ -1,4 +1,5 @@
1
1
  import { createContext, useCallback, useMemo, useState, } from 'react';
2
+ import { SkeletonContextProvider } from '../utils/SkeletonContext';
2
3
  import { ThemeVariant } from './types';
3
4
  const defaultThemeContext = {
4
5
  theme: ThemeVariant.Light,
@@ -15,6 +16,6 @@ export const ThemeContextProvider = ({ children, initialTheme = defaultThemeCont
15
16
  }, []);
16
17
  const contextValue = useMemo(() => ({ theme, fonts, changeTheme }), [theme, fonts, changeTheme]);
17
18
  return (<ThemeContext.Provider value={contextValue}>
18
- {children}
19
+ <SkeletonContextProvider>{children}</SkeletonContextProvider>
19
20
  </ThemeContext.Provider>);
20
21
  };
@@ -0,0 +1,15 @@
1
+ import { type ReactNode } from 'react';
2
+ import { type SharedValue } from 'react-native-reanimated';
3
+ interface SkeletonContextType {
4
+ globalTranslateX: Readonly<SharedValue<number>>;
5
+ registerSkeleton: () => void;
6
+ unregisterSkeleton: () => void;
7
+ skeletonWidth: number;
8
+ }
9
+ export declare const SkeletonContext: import("react").Context<SkeletonContextType>;
10
+ interface SkeletonContextProviderProps {
11
+ readonly children: ReactNode;
12
+ }
13
+ export declare const SKELETON_ANIMATION_DURATION = 1200;
14
+ export declare const SkeletonContextProvider: ({ children, }: SkeletonContextProviderProps) => import("react").JSX.Element;
15
+ export {};
@@ -0,0 +1,56 @@
1
+ import { createContext, useCallback, useEffect, useMemo, useRef, useState, } from 'react';
2
+ import { useWindowDimensions } from 'react-native';
3
+ import { Easing, useSharedValue, withRepeat, withTiming, useDerivedValue, interpolate, } from 'react-native-reanimated';
4
+ const emptyFunc = () => {
5
+ /** Empty */
6
+ };
7
+ const defaultSkeletonContext = {
8
+ globalTranslateX: {
9
+ value: 0,
10
+ addListener: emptyFunc,
11
+ removeListener: emptyFunc,
12
+ set: emptyFunc,
13
+ get: () => 0,
14
+ modify: emptyFunc,
15
+ },
16
+ registerSkeleton: emptyFunc,
17
+ unregisterSkeleton: emptyFunc,
18
+ skeletonWidth: 0,
19
+ };
20
+ export const SkeletonContext = createContext(defaultSkeletonContext);
21
+ export const SKELETON_ANIMATION_DURATION = 1200;
22
+ export const SkeletonContextProvider = ({ children, }) => {
23
+ const [skeletons, setSkeletons] = useState(0);
24
+ const runningAnimation = useRef(false);
25
+ const skeletonAnimation = useSharedValue(0);
26
+ const { width: screenWidth } = useWindowDimensions();
27
+ const globalTranslateX = useDerivedValue(() => interpolate(skeletonAnimation.value, [0, 1], [-screenWidth, screenWidth]));
28
+ const registerSkeleton = useCallback(() => {
29
+ setSkeletons((current) => current + 1);
30
+ }, []);
31
+ const unregisterSkeleton = useCallback(() => {
32
+ setSkeletons((current) => (current > 0 ? current - 1 : 0));
33
+ }, []);
34
+ useEffect(() => {
35
+ if (skeletons > 0 && !runningAnimation.current) {
36
+ skeletonAnimation.value = withRepeat(withTiming(1, {
37
+ duration: SKELETON_ANIMATION_DURATION,
38
+ easing: Easing.ease,
39
+ }), -1);
40
+ runningAnimation.current = true;
41
+ }
42
+ else if (skeletons === 0 && runningAnimation.current) {
43
+ skeletonAnimation.value = 0;
44
+ runningAnimation.current = false;
45
+ }
46
+ }, [skeletonAnimation, skeletons]);
47
+ const contextValue = useMemo(() => ({
48
+ globalTranslateX,
49
+ registerSkeleton,
50
+ unregisterSkeleton,
51
+ skeletonWidth: screenWidth,
52
+ }), [globalTranslateX, registerSkeleton, unregisterSkeleton, screenWidth]);
53
+ return (<SkeletonContext.Provider value={contextValue}>
54
+ {children}
55
+ </SkeletonContext.Provider>);
56
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cdek-it/react-native-ui-kit",
3
- "version": "0.2.8",
3
+ "version": "0.3.0",
4
4
  "description": "UI kit на основе Prime Faces, Prime Flex для React Native",
5
5
  "license": "MIT",
6
6
  "homepage": "https://developer.cdek.ru/design-system",
@@ -15,11 +15,11 @@
15
15
  "ui-kit",
16
16
  "uikit"
17
17
  ],
18
- "main": "./dist/index.js",
19
- "types": "./dist/index.d.ts",
20
18
  "publishConfig": {
21
19
  "registry": "https://registry.npmjs.org",
22
- "access": "public"
20
+ "access": "public",
21
+ "types": "./dist/index.d.ts",
22
+ "main": "./dist/index.js"
23
23
  },
24
24
  "files": [
25
25
  "dist"
@@ -38,7 +38,8 @@
38
38
  "prettier:check": "prettier . --check",
39
39
  "prettier:fix": "prettier . --write",
40
40
  "prettier:watch": "onchange . -- prettier --write --ignore-unknown \"{{changed}}\"",
41
- "release": "release-it"
41
+ "release": "release-it",
42
+ "pod-install": "bundle exec pod install --project-directory=ios"
42
43
  },
43
44
  "devDependencies": {
44
45
  "@babel/core": "7.28.3",
@@ -144,13 +145,6 @@
144
145
  }
145
146
  },
146
147
  "expo": {
147
- "autolinking": {
148
- "ios": {
149
- "searchPaths": [
150
- "./node_modules"
151
- ]
152
- }
153
- },
154
148
  "doctor": {
155
149
  "appConfigFieldsNotSyncedCheck": {
156
150
  "enabled": false
@@ -162,5 +156,7 @@
162
156
  "listUnknownPackages": false
163
157
  }
164
158
  }
165
- }
159
+ },
160
+ "types": "./dist/index.d.ts",
161
+ "main": "./dist/index.js"
166
162
  }