@teamturing/react-native-kit 1.2.4 → 1.3.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.
Files changed (98) hide show
  1. package/lib/commonjs/component/ProfilePhoto/LightProfilePhoto.js +48 -0
  2. package/lib/commonjs/component/ProfilePhoto/LightProfilePhoto.js.map +1 -0
  3. package/lib/commonjs/component/ProfilePhoto/ProfileImageGroup.js +78 -0
  4. package/lib/commonjs/component/ProfilePhoto/ProfileImageGroup.js.map +1 -0
  5. package/lib/commonjs/component/ProfilePhoto/ProfilePhoto.js +68 -0
  6. package/lib/commonjs/component/ProfilePhoto/ProfilePhoto.js.map +1 -0
  7. package/lib/commonjs/component/ProfilePhoto/ProfilePhotoWithBorderGradient.js +53 -0
  8. package/lib/commonjs/component/ProfilePhoto/ProfilePhotoWithBorderGradient.js.map +1 -0
  9. package/lib/commonjs/component/ProfilePhoto/index.js +50 -0
  10. package/lib/commonjs/component/ProfilePhoto/index.js.map +1 -0
  11. package/lib/commonjs/component/ProfilePhoto/profile_outline_gray_90.png +0 -0
  12. package/lib/commonjs/component/ProfilePhoto/profile_outline_gray_90@2x.png +0 -0
  13. package/lib/commonjs/component/ProfilePhoto/profile_outline_gray_90@3x.png +0 -0
  14. package/lib/commonjs/component/index.js +11 -0
  15. package/lib/commonjs/component/index.js.map +1 -1
  16. package/lib/commonjs/hook/useTimeoutHandler.js +3 -1
  17. package/lib/commonjs/hook/useTimeoutHandler.js.map +1 -1
  18. package/lib/commonjs/util/WebImageUrlEnricher.js +27 -0
  19. package/lib/commonjs/util/WebImageUrlEnricher.js.map +1 -0
  20. package/lib/commonjs/util/WebImageUrlEnricher.test.js +22 -0
  21. package/lib/commonjs/util/WebImageUrlEnricher.test.js.map +1 -0
  22. package/lib/commonjs/util/index.js +11 -0
  23. package/lib/commonjs/util/index.js.map +1 -1
  24. package/lib/module/component/ProfilePhoto/LightProfilePhoto.js +44 -0
  25. package/lib/module/component/ProfilePhoto/LightProfilePhoto.js.map +1 -0
  26. package/lib/module/component/ProfilePhoto/ProfileImageGroup.js +74 -0
  27. package/lib/module/component/ProfilePhoto/ProfileImageGroup.js.map +1 -0
  28. package/lib/module/component/ProfilePhoto/ProfilePhoto.js +64 -0
  29. package/lib/module/component/ProfilePhoto/ProfilePhoto.js.map +1 -0
  30. package/lib/module/component/ProfilePhoto/ProfilePhotoWithBorderGradient.js +50 -0
  31. package/lib/module/component/ProfilePhoto/ProfilePhotoWithBorderGradient.js.map +1 -0
  32. package/lib/module/component/ProfilePhoto/index.js +7 -0
  33. package/lib/module/component/ProfilePhoto/index.js.map +1 -0
  34. package/lib/module/component/ProfilePhoto/profile_outline_gray_90.png +0 -0
  35. package/lib/module/component/ProfilePhoto/profile_outline_gray_90@2x.png +0 -0
  36. package/lib/module/component/ProfilePhoto/profile_outline_gray_90@3x.png +0 -0
  37. package/lib/module/component/index.js +1 -0
  38. package/lib/module/component/index.js.map +1 -1
  39. package/lib/module/hook/useTimeoutHandler.js +3 -1
  40. package/lib/module/hook/useTimeoutHandler.js.map +1 -1
  41. package/lib/module/util/WebImageUrlEnricher.js +23 -0
  42. package/lib/module/util/WebImageUrlEnricher.js.map +1 -0
  43. package/lib/module/util/WebImageUrlEnricher.test.js +22 -0
  44. package/lib/module/util/WebImageUrlEnricher.test.js.map +1 -0
  45. package/lib/module/util/index.js +1 -0
  46. package/lib/module/util/index.js.map +1 -1
  47. package/lib/typescript/commonjs/src/component/ProfilePhoto/LightProfilePhoto.d.ts +11 -0
  48. package/lib/typescript/commonjs/src/component/ProfilePhoto/LightProfilePhoto.d.ts.map +1 -0
  49. package/lib/typescript/commonjs/src/component/ProfilePhoto/ProfileImageGroup.d.ts +20 -0
  50. package/lib/typescript/commonjs/src/component/ProfilePhoto/ProfileImageGroup.d.ts.map +1 -0
  51. package/lib/typescript/commonjs/src/component/ProfilePhoto/ProfilePhoto.d.ts +14 -0
  52. package/lib/typescript/commonjs/src/component/ProfilePhoto/ProfilePhoto.d.ts.map +1 -0
  53. package/lib/typescript/commonjs/src/component/ProfilePhoto/ProfilePhotoWithBorderGradient.d.ts +10 -0
  54. package/lib/typescript/commonjs/src/component/ProfilePhoto/ProfilePhotoWithBorderGradient.d.ts.map +1 -0
  55. package/lib/typescript/commonjs/src/component/ProfilePhoto/index.d.ts +5 -0
  56. package/lib/typescript/commonjs/src/component/ProfilePhoto/index.d.ts.map +1 -0
  57. package/lib/typescript/commonjs/src/component/index.d.ts +1 -0
  58. package/lib/typescript/commonjs/src/component/index.d.ts.map +1 -1
  59. package/lib/typescript/commonjs/src/hook/useTimeoutHandler.d.ts.map +1 -1
  60. package/lib/typescript/commonjs/src/util/WebImageUrlEnricher.d.ts +12 -0
  61. package/lib/typescript/commonjs/src/util/WebImageUrlEnricher.d.ts.map +1 -0
  62. package/lib/typescript/commonjs/src/util/WebImageUrlEnricher.test.d.ts +2 -0
  63. package/lib/typescript/commonjs/src/util/WebImageUrlEnricher.test.d.ts.map +1 -0
  64. package/lib/typescript/commonjs/src/util/index.d.ts +1 -0
  65. package/lib/typescript/commonjs/src/util/index.d.ts.map +1 -1
  66. package/lib/typescript/module/src/component/ProfilePhoto/LightProfilePhoto.d.ts +11 -0
  67. package/lib/typescript/module/src/component/ProfilePhoto/LightProfilePhoto.d.ts.map +1 -0
  68. package/lib/typescript/module/src/component/ProfilePhoto/ProfileImageGroup.d.ts +20 -0
  69. package/lib/typescript/module/src/component/ProfilePhoto/ProfileImageGroup.d.ts.map +1 -0
  70. package/lib/typescript/module/src/component/ProfilePhoto/ProfilePhoto.d.ts +14 -0
  71. package/lib/typescript/module/src/component/ProfilePhoto/ProfilePhoto.d.ts.map +1 -0
  72. package/lib/typescript/module/src/component/ProfilePhoto/ProfilePhotoWithBorderGradient.d.ts +10 -0
  73. package/lib/typescript/module/src/component/ProfilePhoto/ProfilePhotoWithBorderGradient.d.ts.map +1 -0
  74. package/lib/typescript/module/src/component/ProfilePhoto/index.d.ts +5 -0
  75. package/lib/typescript/module/src/component/ProfilePhoto/index.d.ts.map +1 -0
  76. package/lib/typescript/module/src/component/index.d.ts +1 -0
  77. package/lib/typescript/module/src/component/index.d.ts.map +1 -1
  78. package/lib/typescript/module/src/hook/useTimeoutHandler.d.ts.map +1 -1
  79. package/lib/typescript/module/src/util/WebImageUrlEnricher.d.ts +12 -0
  80. package/lib/typescript/module/src/util/WebImageUrlEnricher.d.ts.map +1 -0
  81. package/lib/typescript/module/src/util/WebImageUrlEnricher.test.d.ts +2 -0
  82. package/lib/typescript/module/src/util/WebImageUrlEnricher.test.d.ts.map +1 -0
  83. package/lib/typescript/module/src/util/index.d.ts +1 -0
  84. package/lib/typescript/module/src/util/index.d.ts.map +1 -1
  85. package/package.json +5 -3
  86. package/src/component/ProfilePhoto/LightProfilePhoto.tsx +59 -0
  87. package/src/component/ProfilePhoto/ProfileImageGroup.tsx +98 -0
  88. package/src/component/ProfilePhoto/ProfilePhoto.tsx +81 -0
  89. package/src/component/ProfilePhoto/ProfilePhotoWithBorderGradient.tsx +39 -0
  90. package/src/component/ProfilePhoto/index.ts +4 -0
  91. package/src/component/ProfilePhoto/profile_outline_gray_90.png +0 -0
  92. package/src/component/ProfilePhoto/profile_outline_gray_90@2x.png +0 -0
  93. package/src/component/ProfilePhoto/profile_outline_gray_90@3x.png +0 -0
  94. package/src/component/index.ts +1 -1
  95. package/src/hook/useTimeoutHandler.ts +3 -1
  96. package/src/util/WebImageUrlEnricher.test.ts +14 -0
  97. package/src/util/WebImageUrlEnricher.ts +31 -0
  98. package/src/util/index.ts +1 -0
@@ -0,0 +1,12 @@
1
+ export type ImageCrop = {
2
+ l: number;
3
+ r: number;
4
+ t: number;
5
+ b: number;
6
+ };
7
+ export declare function appendImageSizeQueryParams({ url, desiredWidth, crop, }: {
8
+ url: string;
9
+ desiredWidth: number;
10
+ crop?: ImageCrop;
11
+ }): string;
12
+ //# sourceMappingURL=WebImageUrlEnricher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WebImageUrlEnricher.d.ts","sourceRoot":"","sources":["../../../../../src/util/WebImageUrlEnricher.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,SAAS,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AACvE,wBAAgB,0BAA0B,CAAC,EACzC,GAAG,EACH,YAAY,EACZ,IAAI,GACL,EAAE;IACD,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,SAAS,CAAC;CAClB,GAAG,MAAM,CAgBT"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=WebImageUrlEnricher.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WebImageUrlEnricher.test.d.ts","sourceRoot":"","sources":["../../../../../src/util/WebImageUrlEnricher.test.ts"],"names":[],"mappings":""}
@@ -5,4 +5,5 @@ export * from './Px';
5
5
  export * from './EnvironmentUtil';
6
6
  export * from './createCtx';
7
7
  export * from './runAfterFlushMacroQueue';
8
+ export * from './WebImageUrlEnricher';
8
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/util/index.ts"],"names":[],"mappings":"AAAA,cAAc,MAAM,CAAC;AACrB,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,MAAM,CAAC;AACrB,cAAc,mBAAmB,CAAC;AAClC,cAAc,aAAa,CAAC;AAC5B,cAAc,2BAA2B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/util/index.ts"],"names":[],"mappings":"AAAA,cAAc,MAAM,CAAC;AACrB,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,MAAM,CAAC;AACrB,cAAc,mBAAmB,CAAC;AAClC,cAAc,aAAa,CAAC;AAC5B,cAAc,2BAA2B,CAAC;AAC1C,cAAc,uBAAuB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teamturing/react-native-kit",
3
- "version": "1.2.4",
3
+ "version": "1.3.1",
4
4
  "description": "React Native Common Module for Team Turing",
5
5
  "scripts": {
6
6
  "example": "yarn workspace @teamturing/react-native-kit-example",
@@ -18,6 +18,7 @@
18
18
  "@mj-studio/react-native-spannable-string": "^1.1.4",
19
19
  "@react-native-community/hooks": "^3.0.0",
20
20
  "@teamturing/icons": "^1.37.2",
21
+ "@teamturing/utils": "^1.2.1",
21
22
  "polished": "^4.3.1"
22
23
  },
23
24
  "devDependencies": {
@@ -29,7 +30,8 @@
29
30
  "@testing-library/react-native": "^12.6.0",
30
31
  "@types/react": "^18.2.44",
31
32
  "eslint-config-expo": "^7.1.2",
32
- "eslint-import-resolver-typescript": "^3.6.1",
33
+ "eslint-import-resolver-typescript": "^3.6.3",
34
+ "eslint-plugin-import": "^2.29.1",
33
35
  "lottie-react-native": "^6.7.2",
34
36
  "moti": "^0.29.0",
35
37
  "react": "18.2.0",
@@ -156,5 +158,5 @@
156
158
  "bugs": {
157
159
  "url": "https://github.com/weareteamturing/bombe/issues"
158
160
  },
159
- "gitHead": "d9d6fa7332fbef4df49698bc7ea4caab9b157b04"
161
+ "gitHead": "3c14a1e46424fc2bcdf9de4340196cef6b674ef6"
160
162
  }
@@ -0,0 +1,59 @@
1
+ import type { StyleProp, ViewStyle } from 'react-native';
2
+ import { View } from 'react-native';
3
+
4
+ import { is, appendImageSizeQueryParams } from '../../util';
5
+ import { Img } from '../Img';
6
+
7
+ import defaultImage from './profile_outline_gray_90.png';
8
+
9
+ // this is used for just rendering character image in light component like list item(shouldn't be heavy)
10
+ const LightProfilePhoto = ({
11
+ profileImageUrl,
12
+ style,
13
+ size: _size,
14
+ borderColor,
15
+ borderWidth = 0,
16
+ optimize = true,
17
+ }: {
18
+ profileImageUrl: string;
19
+ style?: StyleProp<Omit<ViewStyle, 'borderWidth' | 'borderColor'>>;
20
+ size?: number;
21
+ borderWidth?: number;
22
+ borderColor?: string;
23
+ optimize?: boolean;
24
+ }) => {
25
+ const size = _size || 56;
26
+ return (
27
+ <View
28
+ key={profileImageUrl}
29
+ style={[
30
+ {
31
+ alignItems: 'center',
32
+ justifyContent: 'center',
33
+ },
34
+ style,
35
+ {
36
+ width: size,
37
+ height: size,
38
+ borderRadius: 9999,
39
+ borderWidth: 0,
40
+ },
41
+ ]}
42
+ >
43
+ {Img.render(
44
+ is.notEmptyString(profileImageUrl)
45
+ ? optimize
46
+ ? appendImageSizeQueryParams({ url: profileImageUrl, desiredWidth: size })
47
+ : profileImageUrl
48
+ : defaultImage,
49
+ {
50
+ width: size,
51
+ height: size,
52
+ style: { borderWidth, borderColor, borderRadius: 999 },
53
+ },
54
+ )}
55
+ </View>
56
+ );
57
+ };
58
+
59
+ export { LightProfilePhoto };
@@ -0,0 +1,98 @@
1
+ import { toKoreanNumberUnitString } from '@teamturing/utils';
2
+ import type { ImageSourcePropType, ColorValue } from 'react-native';
3
+ import { View } from 'react-native';
4
+
5
+ import { palette, spacing } from '../../theme';
6
+ import { is, appendImageSizeQueryParams, applyOpacity } from '../../util';
7
+ import { Img } from '../Img';
8
+ import { RowCenter } from '../Layout';
9
+ import { Txt } from '../Txt';
10
+
11
+ type Props = {
12
+ count?: number;
13
+ imageUrls: (ImageSourcePropType | string)[];
14
+ stackDirection?: 'left' | 'right';
15
+ size?: number;
16
+ backgroundColor?: string;
17
+ showBorder?: boolean;
18
+ maxCircleCount?: number;
19
+ showsMoreCount?: boolean;
20
+
21
+ borderWidth?: number;
22
+ borderPadding?: number;
23
+ borderColor?: ColorValue;
24
+ resizeNetworkImage?: boolean;
25
+ imageBorderWidth?: number;
26
+ imageBorderColor?: ColorValue;
27
+ };
28
+ const ProfileImageGroup = ({
29
+ count: countProp,
30
+ imageUrls: _imageUrls,
31
+ stackDirection = 'right',
32
+ size = 36,
33
+ backgroundColor = palette.gray100,
34
+ showBorder = true,
35
+ maxCircleCount = 3,
36
+ showsMoreCount = true,
37
+
38
+ borderWidth = 1,
39
+ borderPadding = spacing[0.5],
40
+ borderColor = palette.gray200,
41
+ resizeNetworkImage = true,
42
+ imageBorderWidth = 2,
43
+ imageBorderColor = backgroundColor,
44
+ }: Props) => {
45
+ const isReverse = stackDirection === 'left';
46
+ const imageUrls = isReverse ? [..._imageUrls].reverse() : _imageUrls;
47
+
48
+ const totalCount = countProp ?? imageUrls.length;
49
+ const visibleProfileCount = Math.min(imageUrls.length, maxCircleCount);
50
+
51
+ return (
52
+ <RowCenter
53
+ reverse={isReverse}
54
+ style={showBorder ? { borderRadius: 999, borderWidth: borderWidth, padding: borderPadding, borderColor } : {}}
55
+ >
56
+ {imageUrls.slice(0, maxCircleCount).map((url, index) =>
57
+ Img.render(
58
+ resizeNetworkImage && is.string(url) ? appendImageSizeQueryParams({ url, desiredWidth: size }) : url,
59
+ {
60
+ key: index,
61
+ width: size,
62
+ height: size,
63
+ style: [
64
+ {
65
+ borderColor: imageBorderColor,
66
+ borderRadius: 999,
67
+ borderWidth: imageBorderWidth,
68
+ },
69
+ stackDirection === 'right'
70
+ ? { marginLeft: index !== 0 ? -size / 2 : 0 }
71
+ : { marginRight: index !== 0 ? -size / 2 : 0 },
72
+ ],
73
+ },
74
+ ),
75
+ )}
76
+ {showsMoreCount && totalCount > visibleProfileCount ? (
77
+ <View
78
+ style={[
79
+ {
80
+ width: size - 4,
81
+ height: size - 4,
82
+ position: 'absolute',
83
+ backgroundColor: applyOpacity(palette.black, 60),
84
+ borderRadius: 999,
85
+ alignItems: 'center',
86
+ justifyContent: 'center',
87
+ },
88
+ stackDirection === 'right' ? { right: spacing[showBorder ? 1 : 0] } : { left: spacing[showBorder ? 1 : 0] },
89
+ ]}
90
+ >
91
+ {Txt.XXS.Bold.White.render(`+${toKoreanNumberUnitString(totalCount - (maxCircleCount - 1))}`)}
92
+ </View>
93
+ ) : null}
94
+ </RowCenter>
95
+ );
96
+ };
97
+
98
+ export { ProfileImageGroup };
@@ -0,0 +1,81 @@
1
+ import type { ReactElement } from 'react';
2
+ import { type StyleProp, type ViewStyle, View } from 'react-native';
3
+
4
+ import { palette } from '../../theme';
5
+ import { is } from '../../util';
6
+ import { Icon } from '../Icon';
7
+ import { Touch } from '../Pressable';
8
+
9
+ import { LightProfilePhoto } from './LightProfilePhoto';
10
+
11
+ const defaultLength = 90;
12
+
13
+ type Props = {
14
+ style?: StyleProp<ViewStyle>;
15
+ onPress?: () => void;
16
+ showIcon?: boolean;
17
+ pressable?: boolean;
18
+ length?: number;
19
+ testID?: string;
20
+ profileImageUrl?: string;
21
+ };
22
+ const ProfilePhoto = ({
23
+ style,
24
+ onPress,
25
+ showIcon,
26
+ pressable = false,
27
+ length: _length,
28
+ testID,
29
+ profileImageUrl,
30
+ }: Props): ReactElement => {
31
+ const length = is.number(_length) ? _length : defaultLength;
32
+
33
+ return (
34
+ <View
35
+ style={[
36
+ style,
37
+ {
38
+ justifyContent: 'center',
39
+ alignItems: 'center',
40
+ width: length,
41
+ height: length,
42
+ },
43
+ ]}
44
+ >
45
+ <Touch
46
+ testID={testID || 'profile_photo_button'}
47
+ style={{
48
+ borderRadius: 9999,
49
+ width: '100%',
50
+ height: '100%',
51
+ }}
52
+ onPress={onPress}
53
+ disabled={!pressable}
54
+ >
55
+ <LightProfilePhoto profileImageUrl={profileImageUrl || ''} size={length} />
56
+ {showIcon ? (
57
+ <View
58
+ style={{
59
+ width: 32,
60
+ height: 32,
61
+ borderRadius: 16,
62
+ backgroundColor: palette.gray200,
63
+ alignItems: 'center',
64
+ justifyContent: 'center',
65
+ borderWidth: 2,
66
+ borderColor: palette.white,
67
+ position: 'absolute',
68
+ right: -6,
69
+ bottom: -6,
70
+ }}
71
+ pointerEvents={'none'}
72
+ >
73
+ <Icon name={'pen'} size={16} />
74
+ </View>
75
+ ) : null}
76
+ </Touch>
77
+ </View>
78
+ );
79
+ };
80
+
81
+ export { ProfilePhoto };
@@ -0,0 +1,39 @@
1
+ import { memo } from 'react';
2
+ import type { ColorValue } from 'react-native';
3
+ import { View } from 'react-native';
4
+ import { Circle, Defs, RadialGradient, Stop, Svg } from 'react-native-svg';
5
+
6
+ import { is } from '../../util';
7
+
8
+ import { LightProfilePhoto } from './LightProfilePhoto';
9
+
10
+ const ProfilePhotoWithBorderGradient = memo(
11
+ ({
12
+ profileImageUrl,
13
+ size,
14
+ borderGradientColor,
15
+ optimize,
16
+ }: {
17
+ profileImageUrl: string;
18
+ size: number;
19
+ borderGradientColor?: ColorValue;
20
+ optimize?: boolean;
21
+ }) => (
22
+ <View>
23
+ <LightProfilePhoto profileImageUrl={profileImageUrl} size={size} optimize={optimize} />
24
+ {is.string(borderGradientColor) ? (
25
+ <Svg height={size} width={size} style={{ position: 'absolute' }}>
26
+ <Defs>
27
+ <RadialGradient id={'grad'} cx={size / 2} cy={size / 2} gradientUnits={'userSpaceOnUse'}>
28
+ <Stop offset={'0.66'} stopColor={borderGradientColor} stopOpacity={0} />
29
+ <Stop offset={'1'} stopColor={borderGradientColor} stopOpacity={1} />
30
+ </RadialGradient>
31
+ </Defs>
32
+ <Circle r={size / 2 + 1} cx={size / 2} cy={size / 2} fill={'url(#grad)'} />
33
+ </Svg>
34
+ ) : null}
35
+ </View>
36
+ ),
37
+ );
38
+
39
+ export { ProfilePhotoWithBorderGradient };
@@ -0,0 +1,4 @@
1
+ export * from './LightProfilePhoto';
2
+ export * from './ProfileImageGroup';
3
+ export * from './ProfilePhoto';
4
+ export * from './ProfilePhotoWithBorderGradient';
@@ -50,9 +50,9 @@ export * from './Img';
50
50
  export * from './Icon';
51
51
  export * from './Header';
52
52
  export * from './FloatingRoundChip';
53
-
54
53
  export * from './dialog';
55
54
  export * from './Btn';
56
55
  export * from './Box';
57
56
  export * from './Banner';
58
57
  export * from './Animation';
58
+ export * from './ProfilePhoto';
@@ -7,7 +7,9 @@ function useTimeoutHandler() {
7
7
  const handler = useRef<any>(-1);
8
8
 
9
9
  useUnmount(() => {
10
- clearTimeout(handler.current);
10
+ if (handler.current !== -1) {
11
+ clearTimeout(handler.current);
12
+ }
11
13
  });
12
14
 
13
15
  return handler;
@@ -0,0 +1,14 @@
1
+ import { appendImageSizeQueryParams } from './WebImageUrlEnricher';
2
+
3
+ describe('appendImageSizeQueryParams', () => {
4
+ it('url이 올바른 경우', () => {
5
+ expect(appendImageSizeQueryParams({ url: 'http://test.jpg', desiredWidth: 100 })).toBe('http://test.jpg?w=256');
6
+ expect(appendImageSizeQueryParams({ url: 'http://test.jpg?x=1&t=5', desiredWidth: 100 })).toBe(
7
+ 'http://test.jpg?x=1&t=5&w=256',
8
+ );
9
+ });
10
+
11
+ it('url이 올바르지 않은 경우', () => {
12
+ expect(appendImageSizeQueryParams({ url: 1 as any, desiredWidth: 100 })).toBe(1);
13
+ });
14
+ });
@@ -0,0 +1,31 @@
1
+ import { Dimensions } from 'react-native';
2
+
3
+ import { is } from './is';
4
+
5
+ const scaleFactor = () => Math.min(2, Dimensions.get('window').scale);
6
+ export type ImageCrop = { l: number; r: number; t: number; b: number };
7
+ export function appendImageSizeQueryParams({
8
+ url,
9
+ desiredWidth,
10
+ crop,
11
+ }: {
12
+ url: string;
13
+ desiredWidth: number;
14
+ crop?: ImageCrop;
15
+ }): string {
16
+ if (!is.notEmptyString(url) || !url.startsWith('http')) {
17
+ return url;
18
+ }
19
+
20
+ const w = Math.max(128, Math.floor((desiredWidth * scaleFactor() + 128 - 1) / 128) * 128);
21
+
22
+ const cropQueryStrings = Object.entries(crop || {}).map(([key, value]) => `${key}=${Math.round(value)}`);
23
+
24
+ const queryString = [`w=${w}`, ...cropQueryStrings].join('&');
25
+
26
+ if (url.includes('?')) {
27
+ return `${url}&${queryString}`;
28
+ } else {
29
+ return `${url}?${queryString}`;
30
+ }
31
+ }
package/src/util/index.ts CHANGED
@@ -5,3 +5,4 @@ export * from './Px';
5
5
  export * from './EnvironmentUtil';
6
6
  export * from './createCtx';
7
7
  export * from './runAfterFlushMacroQueue';
8
+ export * from './WebImageUrlEnricher';