@unif/react-native-design 0.29.0 → 0.30.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 (56) hide show
  1. package/README.md +14 -14
  2. package/jest-resolver.js +12 -1
  3. package/jest-setup.js +33 -8
  4. package/lib/module/components/ui/Avatar/Avatar.js +4 -3
  5. package/lib/module/components/ui/Avatar/Avatar.js.map +1 -1
  6. package/lib/module/components/ui/Avatar/styles.js +10 -2
  7. package/lib/module/components/ui/Avatar/styles.js.map +1 -1
  8. package/lib/module/components/ui/AvatarGroup/AvatarGroup.js +112 -0
  9. package/lib/module/components/ui/AvatarGroup/AvatarGroup.js.map +1 -0
  10. package/lib/module/components/ui/AvatarGroup/index.js +4 -0
  11. package/lib/module/components/ui/AvatarGroup/index.js.map +1 -0
  12. package/lib/module/components/ui/AvatarGroup/layout.js +44 -0
  13. package/lib/module/components/ui/AvatarGroup/layout.js.map +1 -0
  14. package/lib/module/components/ui/AvatarGroup/normalize.js +20 -0
  15. package/lib/module/components/ui/AvatarGroup/normalize.js.map +1 -0
  16. package/lib/module/components/ui/AvatarGroup/styles.js +20 -0
  17. package/lib/module/components/ui/AvatarGroup/styles.js.map +1 -0
  18. package/lib/module/components/ui/AvatarGroup/types.js +4 -0
  19. package/lib/module/components/ui/AvatarGroup/types.js.map +1 -0
  20. package/lib/module/components/ui/index.js +1 -0
  21. package/lib/module/components/ui/index.js.map +1 -1
  22. package/lib/typescript/manual-tests/runtime-api/RuntimeApiScreen.d.ts +1 -1
  23. package/lib/typescript/src/components/ui/Avatar/Avatar.d.ts +2 -2
  24. package/lib/typescript/src/components/ui/Avatar/Avatar.d.ts.map +1 -1
  25. package/lib/typescript/src/components/ui/Avatar/index.d.ts +1 -1
  26. package/lib/typescript/src/components/ui/Avatar/index.d.ts.map +1 -1
  27. package/lib/typescript/src/components/ui/Avatar/styles.d.ts +4 -2
  28. package/lib/typescript/src/components/ui/Avatar/styles.d.ts.map +1 -1
  29. package/lib/typescript/src/components/ui/Avatar/types.d.ts +4 -0
  30. package/lib/typescript/src/components/ui/Avatar/types.d.ts.map +1 -1
  31. package/lib/typescript/src/components/ui/AvatarGroup/AvatarGroup.d.ts +5 -0
  32. package/lib/typescript/src/components/ui/AvatarGroup/AvatarGroup.d.ts.map +1 -0
  33. package/lib/typescript/src/components/ui/AvatarGroup/index.d.ts +3 -0
  34. package/lib/typescript/src/components/ui/AvatarGroup/index.d.ts.map +1 -0
  35. package/lib/typescript/src/components/ui/AvatarGroup/layout.d.ts +16 -0
  36. package/lib/typescript/src/components/ui/AvatarGroup/layout.d.ts.map +1 -0
  37. package/lib/typescript/src/components/ui/AvatarGroup/normalize.d.ts +9 -0
  38. package/lib/typescript/src/components/ui/AvatarGroup/normalize.d.ts.map +1 -0
  39. package/lib/typescript/src/components/ui/AvatarGroup/styles.d.ts +17 -0
  40. package/lib/typescript/src/components/ui/AvatarGroup/styles.d.ts.map +1 -0
  41. package/lib/typescript/src/components/ui/AvatarGroup/types.d.ts +36 -0
  42. package/lib/typescript/src/components/ui/AvatarGroup/types.d.ts.map +1 -0
  43. package/lib/typescript/src/components/ui/index.d.ts +3 -1
  44. package/lib/typescript/src/components/ui/index.d.ts.map +1 -1
  45. package/package.json +12 -12
  46. package/src/components/ui/Avatar/Avatar.tsx +9 -3
  47. package/src/components/ui/Avatar/index.ts +6 -1
  48. package/src/components/ui/Avatar/styles.ts +14 -3
  49. package/src/components/ui/Avatar/types.ts +5 -0
  50. package/src/components/ui/AvatarGroup/AvatarGroup.tsx +147 -0
  51. package/src/components/ui/AvatarGroup/index.ts +2 -0
  52. package/src/components/ui/AvatarGroup/layout.ts +56 -0
  53. package/src/components/ui/AvatarGroup/normalize.ts +28 -0
  54. package/src/components/ui/AvatarGroup/styles.ts +19 -0
  55. package/src/components/ui/AvatarGroup/types.ts +40 -0
  56. package/src/components/ui/index.ts +8 -1
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../../src/components/ui/Avatar/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9E,mEAAmE;AACnE,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAE1D;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;AAElE,MAAM,MAAM,WAAW,GAAG;IACxB;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC;IACd,iBAAiB;IACjB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,wBAAwB;IACxB,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,mBAAmB,CAAC;IAC7B,uCAAuC;IACvC,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,iBAAiB;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../../src/components/ui/Avatar/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9E,mEAAmE;AACnE,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAE1D,6CAA6C;AAC7C,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAE9C;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;AAElE,MAAM,MAAM,WAAW,GAAG;IACxB;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC;IACd,iBAAiB;IACjB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,qBAAqB;IACrB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,wBAAwB;IACxB,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,mBAAmB,CAAC;IAC7B,uCAAuC;IACvC,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,iBAAiB;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC"}
@@ -0,0 +1,5 @@
1
+ import React from 'react';
2
+ import type { AvatarGroupProps } from './types';
3
+ /** 重叠头像组;只拥有布局、溢出计数和可选溢出 action。 */
4
+ export declare function AvatarGroup({ items, size, shape, max, style, testID, onOverflowPress, overflowAccessibilityLabel, overflowAccessibilityHint, }: AvatarGroupProps): React.JSX.Element | null;
5
+ //# sourceMappingURL=AvatarGroup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AvatarGroup.d.ts","sourceRoot":"","sources":["../../../../../../src/components/ui/AvatarGroup/AvatarGroup.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAoB,MAAM,OAAO,CAAC;AAkBzC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAWhD,oCAAoC;AACpC,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,IAAW,EACX,KAAgB,EAChB,GAAG,EACH,KAAK,EACL,MAAM,EACN,eAAe,EACf,0BAA0B,EAC1B,yBAAyB,GAC1B,EAAE,gBAAgB,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,GAAG,IAAI,CA0G7C"}
@@ -0,0 +1,3 @@
1
+ export { AvatarGroup } from './AvatarGroup';
2
+ export type { AvatarGroupItem, AvatarGroupProps } from './types';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../src/components/ui/AvatarGroup/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC"}
@@ -0,0 +1,16 @@
1
+ import type { AvatarSize } from '../Avatar';
2
+ export type AvatarGroupHitSlop = Readonly<{
3
+ top: number;
4
+ right: number;
5
+ bottom: number;
6
+ left: number;
7
+ }>;
8
+ export type ResolvedAvatarGroupLayout = Readonly<{
9
+ box: number;
10
+ overlap: number;
11
+ borderWidth: number;
12
+ hitSlop: AvatarGroupHitSlop;
13
+ }>;
14
+ /** 五档尺寸共享的重叠、分隔边与 44pt 命中区几何。 */
15
+ export declare function resolveAvatarGroupLayout(size: AvatarSize): ResolvedAvatarGroupLayout;
16
+ //# sourceMappingURL=layout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../../../../../src/components/ui/AvatarGroup/layout.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAE5C,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC;IACxC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd,CAAC,CAAC;AAEH,MAAM,MAAM,yBAAyB,GAAG,QAAQ,CAAC;IAC/C,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,kBAAkB,CAAC;CAC7B,CAAC,CAAC;AAEH,iCAAiC;AACjC,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,UAAU,GACf,yBAAyB,CAmC3B"}
@@ -0,0 +1,9 @@
1
+ import type { AvatarGroupItem } from './types';
2
+ export type NormalizedAvatarGroup = Readonly<{
3
+ visibleItems: readonly AvatarGroupItem[];
4
+ overflowCount: number;
5
+ invalidMax: boolean;
6
+ }>;
7
+ /** AvatarGroup 唯一的 max 与可见成员归一化入口。 */
8
+ export declare function normalizeAvatarGroup(items: readonly AvatarGroupItem[], max: number | undefined): NormalizedAvatarGroup;
9
+ //# sourceMappingURL=normalize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../../../../../../src/components/ui/AvatarGroup/normalize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,MAAM,MAAM,qBAAqB,GAAG,QAAQ,CAAC;IAC3C,YAAY,EAAE,SAAS,eAAe,EAAE,CAAC;IACzC,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,OAAO,CAAC;CACrB,CAAC,CAAC;AAEH,sCAAsC;AACtC,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,SAAS,eAAe,EAAE,EACjC,GAAG,EAAE,MAAM,GAAG,SAAS,GACtB,qBAAqB,CAevB"}
@@ -0,0 +1,17 @@
1
+ import { type ColorTokens } from '../../../theme';
2
+ export declare const makeStyles: (_colors: ColorTokens) => Readonly<{
3
+ root: {
4
+ alignItems: "center";
5
+ flexDirection: "row";
6
+ };
7
+ overflow: {
8
+ alignItems: "center";
9
+ justifyContent: "center";
10
+ overflow: "hidden";
11
+ };
12
+ overflowText: {
13
+ fontWeight: "600";
14
+ lineHeight: undefined;
15
+ };
16
+ }>;
17
+ //# sourceMappingURL=styles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../../../../../src/components/ui/AvatarGroup/styles.ts"],"names":[],"mappings":"AACA,OAAO,EAAM,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAEtD,eAAO,MAAM,UAAU,GAAI,SAAS,WAAW;;;;;;;;;;;;;;EAe3C,CAAC"}
@@ -0,0 +1,36 @@
1
+ import type { ImageSourcePropType, StyleProp, ViewStyle } from 'react-native';
2
+ import type { AvatarShape, AvatarSize, AvatarVariant } from '../Avatar';
3
+ export type AvatarGroupItem = Readonly<{
4
+ /** 列表内唯一且稳定的身份键。 */
5
+ key: string;
6
+ /** 可见回退文字与 Avatar accessible name。 */
7
+ label: string;
8
+ source?: ImageSourcePropType;
9
+ variant?: AvatarVariant;
10
+ }>;
11
+ type SharedAvatarGroupProps = {
12
+ items: readonly AvatarGroupItem[];
13
+ /** 所有成员共用的尺寸,默认 'md'。 */
14
+ size?: AvatarSize;
15
+ /** 所有成员与溢出位共用的形态,默认 'circle'。 */
16
+ shape?: AvatarShape;
17
+ /** 最大视觉位数量,包含 +N 溢出位,必须是大于等于 2 的整数。 */
18
+ max?: number;
19
+ style?: StyleProp<ViewStyle>;
20
+ testID?: string;
21
+ };
22
+ type StaticOverflowProps = {
23
+ onOverflowPress?: never;
24
+ overflowAccessibilityLabel?: never;
25
+ overflowAccessibilityHint?: never;
26
+ };
27
+ type ActionableOverflowProps = {
28
+ onOverflowPress: () => void;
29
+ /** 默认按实际数量生成“查看其余 N 位成员”。 */
30
+ overflowAccessibilityLabel?: string;
31
+ /** 描述打开弹层、抽屉或跳转等消费端结果。 */
32
+ overflowAccessibilityHint?: string;
33
+ };
34
+ export type AvatarGroupProps = SharedAvatarGroupProps & (StaticOverflowProps | ActionableOverflowProps);
35
+ export {};
36
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../../src/components/ui/AvatarGroup/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9E,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAExE,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC;IACrC,oBAAoB;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,sCAAsC;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,mBAAmB,CAAC;IAC7B,OAAO,CAAC,EAAE,aAAa,CAAC;CACzB,CAAC,CAAC;AAEH,KAAK,sBAAsB,GAAG;IAC5B,KAAK,EAAE,SAAS,eAAe,EAAE,CAAC;IAClC,yBAAyB;IACzB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,iCAAiC;IACjC,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,uCAAuC;IACvC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,KAAK,mBAAmB,GAAG;IACzB,eAAe,CAAC,EAAE,KAAK,CAAC;IACxB,0BAA0B,CAAC,EAAE,KAAK,CAAC;IACnC,yBAAyB,CAAC,EAAE,KAAK,CAAC;CACnC,CAAC;AAEF,KAAK,uBAAuB,GAAG;IAC7B,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,6BAA6B;IAC7B,0BAA0B,CAAC,EAAE,MAAM,CAAC;IACpC,0BAA0B;IAC1B,yBAAyB,CAAC,EAAE,MAAM,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,sBAAsB,GACnD,CAAC,mBAAmB,GAAG,uBAAuB,CAAC,CAAC"}
@@ -1,5 +1,7 @@
1
1
  export { Avatar } from './Avatar';
2
- export type { AvatarProps, AvatarSize, AvatarVariant } from './Avatar';
2
+ export type { AvatarProps, AvatarShape, AvatarSize, AvatarVariant, } from './Avatar';
3
+ export { AvatarGroup } from './AvatarGroup';
4
+ export type { AvatarGroupItem, AvatarGroupProps } from './AvatarGroup';
3
5
  export { BlurLayer } from './BlurLayer';
4
6
  export type { BlurLayerProps } from './BlurLayer';
5
7
  export { Button } from './Button';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/components/ui/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/components/ui/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unif/react-native-design",
3
- "version": "0.29.0",
3
+ "version": "0.30.1",
4
4
  "description": "Unif 设计系统:theme + icons + utils + components",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
@@ -85,13 +85,13 @@
85
85
  "@eslint/js": "^9.39.4",
86
86
  "@jest/globals": "^30.0.0",
87
87
  "@react-native-community/cli": "20.1.0",
88
- "@react-native-community/template": "0.86.2",
89
- "@react-native/babel-preset": "0.86.2",
90
- "@react-native/eslint-config": "0.86.2",
91
- "@react-native/jest-preset": "0.86.2",
92
- "@react-native/metro-config": "0.86.2",
88
+ "@react-native-community/template": "0.86.3",
89
+ "@react-native/babel-preset": "0.86.3",
90
+ "@react-native/eslint-config": "0.86.3",
91
+ "@react-native/jest-preset": "0.86.3",
92
+ "@react-native/metro-config": "0.86.3",
93
93
  "@release-it/conventional-changelog": "^11.0.0",
94
- "@sbaiahmed1/react-native-blur": "^4.6.2",
94
+ "@sbaiahmed1/react-native-blur": "6.0.1",
95
95
  "@types/react": "^19.2.0",
96
96
  "commitlint": "^21.0.1",
97
97
  "del-cli": "^7.0.0",
@@ -103,14 +103,14 @@
103
103
  "lefthook": "^2.1.4",
104
104
  "prettier": "^3.8.1",
105
105
  "react": "19.2.3",
106
- "react-native": "0.86.2",
106
+ "react-native": "0.86.3",
107
107
  "react-native-builder-bob": "^0.41.0",
108
108
  "react-native-gesture-handler": "^3.1.0",
109
- "react-native-reanimated": "~4.5.3",
109
+ "react-native-reanimated": "~4.6.0",
110
110
  "react-native-reanimated-carousel": "^5.0.0",
111
111
  "react-native-safe-area-context": "^5.7.0",
112
112
  "react-native-svg": "^15.15.5",
113
- "react-native-worklets": "^0.11.3",
113
+ "react-native-worklets": "^0.12.1",
114
114
  "release-it": "^20.0.1",
115
115
  "semver": "^7.8.5",
116
116
  "smol-toml": "^1.7.1",
@@ -122,11 +122,11 @@
122
122
  "react": ">=19.2.3 <20.0.0",
123
123
  "react-native": ">=0.86.0",
124
124
  "react-native-gesture-handler": ">=3.0.0 <4.0.0",
125
- "react-native-reanimated": ">=4.5.2 <4.6.0",
125
+ "react-native-reanimated": ">=4.5.2 <4.7.0",
126
126
  "react-native-reanimated-carousel": ">=5.0.0 <6.0.0",
127
127
  "react-native-safe-area-context": ">=5",
128
128
  "react-native-svg": ">=15",
129
- "react-native-worklets": ">=0.11.0 <0.12.0"
129
+ "react-native-worklets": ">=0.11.0 <0.13.0"
130
130
  },
131
131
  "engines": {
132
132
  "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
@@ -3,11 +3,16 @@ import { Text, View } from 'react-native';
3
3
  import { scaleFontMetric, useColors, useFontScale } from '../../../theme';
4
4
  import { resolveImageSource } from '../../../utils/imageSource';
5
5
  import { ImageAttempt } from '../shared/ImageAttempt';
6
- import { paletteFor, sizingFor, styles } from './styles';
6
+ import {
7
+ paletteFor,
8
+ resolveAvatarBorderRadius,
9
+ sizingFor,
10
+ styles,
11
+ } from './styles';
7
12
  import type { AvatarProps } from './types';
8
13
 
9
14
  /**
10
- * 圆形头像 —— 5 档尺寸 × 4 种配色 × 可选图片 source。
15
+ * 头像 —— 2 种形态 × 5 档尺寸 × 4 种配色 × 可选图片 source。
11
16
  *
12
17
  * 合法 source 由 keyed ImageAttempt 独立持有失败状态;非法 source 或加载失败
13
18
  * 时渲染 label + variant 背景色。
@@ -15,6 +20,7 @@ import type { AvatarProps } from './types';
15
20
  export function Avatar({
16
21
  label,
17
22
  size = 'md',
23
+ shape = 'circle',
18
24
  variant = 'neutral',
19
25
  source,
20
26
  style,
@@ -42,7 +48,7 @@ export function Avatar({
42
48
  {
43
49
  width: dims.box,
44
50
  height: dims.box,
45
- borderRadius: dims.box / 2,
51
+ borderRadius: resolveAvatarBorderRadius(size, shape),
46
52
  backgroundColor: palette.bg,
47
53
  },
48
54
  style,
@@ -1,2 +1,7 @@
1
1
  export { Avatar } from './Avatar';
2
- export type { AvatarProps, AvatarSize, AvatarVariant } from './types';
2
+ export type {
3
+ AvatarProps,
4
+ AvatarShape,
5
+ AvatarSize,
6
+ AvatarVariant,
7
+ } from './types';
@@ -1,8 +1,8 @@
1
1
  import { StyleSheet } from 'react-native';
2
- import { avatar, fw, rf, type ColorTokens } from '../../../theme';
3
- import type { AvatarSize, AvatarVariant } from './types';
2
+ import { avatar, fw, radius, rf, type ColorTokens } from '../../../theme';
3
+ import type { AvatarShape, AvatarSize, AvatarVariant } from './types';
4
4
 
5
- /** Avatar 静态 base —— 圆形容器(borderRadius / 尺寸由 sizingFor 派生)。 */
5
+ /** Avatar 静态 base —— 容器圆角 / 尺寸由 resolver 派生。 */
6
6
  export const styles = StyleSheet.create({
7
7
  base: {
8
8
  alignItems: 'center',
@@ -36,6 +36,17 @@ export function sizingFor(size: AvatarSize): { box: number; fs: number } {
36
36
  }
37
37
  }
38
38
 
39
+ /** circle 保留直径一半的现有视觉;square 按尺寸使用既有圆角 token。 */
40
+ export function resolveAvatarBorderRadius(
41
+ size: AvatarSize,
42
+ shape: AvatarShape
43
+ ): number {
44
+ if (shape === 'circle') return sizingFor(size).box / 2;
45
+ if (size === 'xs' || size === 'sm') return radius.xs;
46
+ if (size === 'xl') return radius.md;
47
+ return radius.sm;
48
+ }
49
+
39
50
  /** Avatar 配色推导:4 种 variant → { bg, fg }。签名与 Tag/Button 的 paletteFor 一致。 */
40
51
  export function paletteFor(
41
52
  variant: AvatarVariant,
@@ -3,6 +3,9 @@ import type { ImageSourcePropType, StyleProp, ViewStyle } from 'react-native';
3
3
  /** 头像尺寸(对应 avatar.xs..xl):xs=18 / sm=28 / md=32 / lg=40 / xl=56 */
4
4
  export type AvatarSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
5
5
 
6
+ /** 头像形态:circle 为圆形,square 为带 token 圆角的方形。 */
7
+ export type AvatarShape = 'circle' | 'square';
8
+
6
9
  /**
7
10
  * 头像配色变体(仅在显示 letter / fallback 时生效,showImage 时被覆盖):
8
11
  * - `brand` 品牌橙底(AI 头像专用)
@@ -20,6 +23,8 @@ export type AvatarProps = {
20
23
  label: string;
21
24
  /** 尺寸,默认 'md' */
22
25
  size?: AvatarSize;
26
+ /** 形态,默认 'circle' */
27
+ shape?: AvatarShape;
23
28
  /** 配色变体,默认 'neutral' */
24
29
  variant?: AvatarVariant;
25
30
  /**
@@ -0,0 +1,147 @@
1
+ import React, { useEffect } from 'react';
2
+ import { Text, View, type ViewStyle } from 'react-native';
3
+ import { Pressable } from 'react-native-gesture-handler';
4
+ import {
5
+ pressedOpacity,
6
+ scaleFontMetric,
7
+ useColors,
8
+ useFontScale,
9
+ useThemedStyles,
10
+ } from '../../../theme';
11
+ import { createLogger } from '../../../utils/logger';
12
+ import { childTestID } from '../../../utils/testID';
13
+ import { Avatar } from '../Avatar';
14
+ import { resolveAvatarBorderRadius, sizingFor } from '../Avatar/styles';
15
+ import { normalizeNonBlankText } from '../shared/accessibilityName';
16
+ import { resolveAvatarGroupLayout } from './layout';
17
+ import { normalizeAvatarGroup } from './normalize';
18
+ import { makeStyles } from './styles';
19
+ import type { AvatarGroupProps } from './types';
20
+
21
+ const log = createLogger('AvatarGroup');
22
+ const warned = new Set<string>();
23
+
24
+ function warnOnce(key: string, message: string): void {
25
+ if (warned.has(key)) return;
26
+ warned.add(key);
27
+ log.warn(message);
28
+ }
29
+
30
+ /** 重叠头像组;只拥有布局、溢出计数和可选溢出 action。 */
31
+ export function AvatarGroup({
32
+ items,
33
+ size = 'md',
34
+ shape = 'circle',
35
+ max,
36
+ style,
37
+ testID,
38
+ onOverflowPress,
39
+ overflowAccessibilityLabel,
40
+ overflowAccessibilityHint,
41
+ }: AvatarGroupProps): React.JSX.Element | null {
42
+ const colors = useColors();
43
+ const fontScale = useFontScale();
44
+ const styles = useThemedStyles(makeStyles);
45
+ const normalized = normalizeAvatarGroup(items, max);
46
+ const layout = resolveAvatarGroupLayout(size);
47
+ const avatarSizing = sizingFor(size);
48
+ const overflowFontSize = scaleFontMetric(avatarSizing.fs, fontScale);
49
+ const customOverflowLabel = normalizeNonBlankText(overflowAccessibilityLabel);
50
+ const hasBlankOverflowLabel =
51
+ normalized.overflowCount > 0 &&
52
+ onOverflowPress !== undefined &&
53
+ overflowAccessibilityLabel !== undefined &&
54
+ customOverflowLabel === undefined;
55
+
56
+ useEffect(() => {
57
+ if (typeof __DEV__ === 'undefined' || !__DEV__) return;
58
+ if (normalized.invalidMax) {
59
+ warnOnce(
60
+ `max:${String(max)}`,
61
+ `max 必须是大于等于 2 的有限整数,传入 ${String(max)},已展示全部成员。`
62
+ );
63
+ }
64
+ if (hasBlankOverflowLabel) {
65
+ warnOnce(
66
+ 'overflowAccessibilityLabel:blank',
67
+ 'overflowAccessibilityLabel 不能为空白,已回退默认名称。'
68
+ );
69
+ }
70
+ }, [hasBlankOverflowLabel, max, normalized.invalidMax]);
71
+
72
+ if (items.length === 0) return null;
73
+
74
+ const slotStyle = (index: number): ViewStyle => ({
75
+ borderColor: colors.surface,
76
+ borderWidth: layout.borderWidth,
77
+ marginStart: index === 0 ? 0 : -layout.overlap,
78
+ zIndex: index,
79
+ });
80
+ const overflowCount = normalized.overflowCount;
81
+ const overflowLabel = `+${overflowCount}`;
82
+ const overflowA11yLabel =
83
+ onOverflowPress === undefined
84
+ ? `还有 ${overflowCount} 位成员`
85
+ : (customOverflowLabel ?? `查看其余 ${overflowCount} 位成员`);
86
+ const overflowStyle: ViewStyle = {
87
+ ...slotStyle(normalized.visibleItems.length),
88
+ width: layout.box,
89
+ height: layout.box,
90
+ borderRadius: resolveAvatarBorderRadius(size, shape),
91
+ backgroundColor: colors.primaryContainer,
92
+ };
93
+ const overflowText = (
94
+ <Text
95
+ style={[
96
+ styles.overflowText,
97
+ { color: colors.primary, fontSize: overflowFontSize },
98
+ ]}
99
+ numberOfLines={1}
100
+ >
101
+ {overflowLabel}
102
+ </Text>
103
+ );
104
+
105
+ return (
106
+ <View style={[styles.root, style]} testID={testID}>
107
+ {normalized.visibleItems.map((item, index) => (
108
+ <Avatar
109
+ key={item.key}
110
+ label={item.label}
111
+ source={item.source}
112
+ variant={item.variant}
113
+ size={size}
114
+ shape={shape}
115
+ style={slotStyle(index)}
116
+ />
117
+ ))}
118
+ {overflowCount === 0 ? null : onOverflowPress === undefined ? (
119
+ <View
120
+ accessible
121
+ accessibilityLabel={overflowA11yLabel}
122
+ style={[styles.overflow, overflowStyle]}
123
+ testID={childTestID(testID, 'overflow')}
124
+ >
125
+ {overflowText}
126
+ </View>
127
+ ) : (
128
+ <Pressable
129
+ accessible
130
+ accessibilityRole="button"
131
+ accessibilityLabel={overflowA11yLabel}
132
+ accessibilityHint={overflowAccessibilityHint}
133
+ onPress={onOverflowPress}
134
+ hitSlop={layout.hitSlop}
135
+ style={({ pressed }) => [
136
+ styles.overflow,
137
+ overflowStyle,
138
+ { opacity: pressed ? pressedOpacity : 1 },
139
+ ]}
140
+ testID={childTestID(testID, 'overflow')}
141
+ >
142
+ {overflowText}
143
+ </Pressable>
144
+ )}
145
+ </View>
146
+ );
147
+ }
@@ -0,0 +1,2 @@
1
+ export { AvatarGroup } from './AvatarGroup';
2
+ export type { AvatarGroupItem, AvatarGroupProps } from './types';
@@ -0,0 +1,56 @@
1
+ import { avatar, fixed, r, space } from '../../../theme';
2
+ import type { AvatarSize } from '../Avatar';
3
+
4
+ export type AvatarGroupHitSlop = Readonly<{
5
+ top: number;
6
+ right: number;
7
+ bottom: number;
8
+ left: number;
9
+ }>;
10
+
11
+ export type ResolvedAvatarGroupLayout = Readonly<{
12
+ box: number;
13
+ overlap: number;
14
+ borderWidth: number;
15
+ hitSlop: AvatarGroupHitSlop;
16
+ }>;
17
+
18
+ /** 五档尺寸共享的重叠、分隔边与 44pt 命中区几何。 */
19
+ export function resolveAvatarGroupLayout(
20
+ size: AvatarSize
21
+ ): ResolvedAvatarGroupLayout {
22
+ const box = avatar[size];
23
+ let overlap: number;
24
+ let borderWidth: number;
25
+
26
+ switch (size) {
27
+ case 'xs':
28
+ overlap = space['1'];
29
+ borderWidth = r(1);
30
+ break;
31
+ case 'sm':
32
+ overlap = space['2'];
33
+ borderWidth = r(1);
34
+ break;
35
+ case 'md':
36
+ overlap = space['3'];
37
+ borderWidth = r(1);
38
+ break;
39
+ case 'lg':
40
+ overlap = space['4'];
41
+ borderWidth = r(2);
42
+ break;
43
+ case 'xl':
44
+ overlap = space['6'];
45
+ borderWidth = r(2);
46
+ break;
47
+ }
48
+
49
+ const inset = Math.max(0, (fixed.hitTarget - box) / 2);
50
+ return {
51
+ box,
52
+ overlap,
53
+ borderWidth,
54
+ hitSlop: { top: inset, right: inset, bottom: inset, left: inset },
55
+ };
56
+ }
@@ -0,0 +1,28 @@
1
+ import type { AvatarGroupItem } from './types';
2
+
3
+ export type NormalizedAvatarGroup = Readonly<{
4
+ visibleItems: readonly AvatarGroupItem[];
5
+ overflowCount: number;
6
+ invalidMax: boolean;
7
+ }>;
8
+
9
+ /** AvatarGroup 唯一的 max 与可见成员归一化入口。 */
10
+ export function normalizeAvatarGroup(
11
+ items: readonly AvatarGroupItem[],
12
+ max: number | undefined
13
+ ): NormalizedAvatarGroup {
14
+ const invalidMax =
15
+ max !== undefined &&
16
+ (!Number.isFinite(max) || !Number.isInteger(max) || max < 2);
17
+
18
+ if (max === undefined || invalidMax || items.length <= max) {
19
+ return { visibleItems: items, overflowCount: 0, invalidMax };
20
+ }
21
+
22
+ const visibleCount = max - 1;
23
+ return {
24
+ visibleItems: items.slice(0, visibleCount),
25
+ overflowCount: items.length - visibleCount,
26
+ invalidMax: false,
27
+ };
28
+ }
@@ -0,0 +1,19 @@
1
+ import { StyleSheet } from 'react-native';
2
+ import { fw, type ColorTokens } from '../../../theme';
3
+
4
+ export const makeStyles = (_colors: ColorTokens) =>
5
+ StyleSheet.create({
6
+ root: {
7
+ alignItems: 'center',
8
+ flexDirection: 'row',
9
+ },
10
+ overflow: {
11
+ alignItems: 'center',
12
+ justifyContent: 'center',
13
+ overflow: 'hidden',
14
+ },
15
+ overflowText: {
16
+ fontWeight: fw.semi,
17
+ lineHeight: undefined,
18
+ },
19
+ });
@@ -0,0 +1,40 @@
1
+ import type { ImageSourcePropType, StyleProp, ViewStyle } from 'react-native';
2
+ import type { AvatarShape, AvatarSize, AvatarVariant } from '../Avatar';
3
+
4
+ export type AvatarGroupItem = Readonly<{
5
+ /** 列表内唯一且稳定的身份键。 */
6
+ key: string;
7
+ /** 可见回退文字与 Avatar accessible name。 */
8
+ label: string;
9
+ source?: ImageSourcePropType;
10
+ variant?: AvatarVariant;
11
+ }>;
12
+
13
+ type SharedAvatarGroupProps = {
14
+ items: readonly AvatarGroupItem[];
15
+ /** 所有成员共用的尺寸,默认 'md'。 */
16
+ size?: AvatarSize;
17
+ /** 所有成员与溢出位共用的形态,默认 'circle'。 */
18
+ shape?: AvatarShape;
19
+ /** 最大视觉位数量,包含 +N 溢出位,必须是大于等于 2 的整数。 */
20
+ max?: number;
21
+ style?: StyleProp<ViewStyle>;
22
+ testID?: string;
23
+ };
24
+
25
+ type StaticOverflowProps = {
26
+ onOverflowPress?: never;
27
+ overflowAccessibilityLabel?: never;
28
+ overflowAccessibilityHint?: never;
29
+ };
30
+
31
+ type ActionableOverflowProps = {
32
+ onOverflowPress: () => void;
33
+ /** 默认按实际数量生成“查看其余 N 位成员”。 */
34
+ overflowAccessibilityLabel?: string;
35
+ /** 描述打开弹层、抽屉或跳转等消费端结果。 */
36
+ overflowAccessibilityHint?: string;
37
+ };
38
+
39
+ export type AvatarGroupProps = SharedAvatarGroupProps &
40
+ (StaticOverflowProps | ActionableOverflowProps);
@@ -1,5 +1,12 @@
1
1
  export { Avatar } from './Avatar';
2
- export type { AvatarProps, AvatarSize, AvatarVariant } from './Avatar';
2
+ export type {
3
+ AvatarProps,
4
+ AvatarShape,
5
+ AvatarSize,
6
+ AvatarVariant,
7
+ } from './Avatar';
8
+ export { AvatarGroup } from './AvatarGroup';
9
+ export type { AvatarGroupItem, AvatarGroupProps } from './AvatarGroup';
3
10
  export { BlurLayer } from './BlurLayer';
4
11
  export type { BlurLayerProps } from './BlurLayer';
5
12
  export { Button } from './Button';