@sproutsocial/seeds-react-profile 0.3.35 → 0.3.38
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.
- package/.turbo/turbo-build.log +67 -11
- package/CHANGELOG.md +47 -0
- package/dist/esm/index.js +255 -23
- package/dist/esm/index.js.map +1 -1
- package/dist/index.d.mts +4 -42
- package/dist/index.d.ts +4 -42
- package/dist/index.js +255 -23
- package/dist/index.js.map +1 -1
- package/dist/inline-profile.css +73 -0
- package/dist/profile-card.css +74 -0
- package/dist/profile-token.css +23 -0
- package/package.json +21 -10
- package/src/InlineProfile.stories.tsx +27 -0
- package/src/InlineProfile.tsx +1 -153
- package/src/InlineProfileHybrid.tsx +18 -0
- package/src/InlineProfileStyled.tsx +143 -0
- package/src/InlineProfileTailwind.tsx +81 -0
- package/src/ProfileCard.stories.tsx +27 -0
- package/src/ProfileCard.tsx +4 -186
- package/src/ProfileCardHybrid.tsx +25 -0
- package/src/ProfileCardStyled.tsx +188 -0
- package/src/ProfileCardTailwind.tsx +154 -0
- package/src/ProfileToken.stories.tsx +16 -0
- package/src/ProfileToken.tsx +4 -40
- package/src/ProfileTokenHybrid.tsx +18 -0
- package/src/ProfileTokenStyled.tsx +37 -0
- package/src/ProfileTokenTailwind.tsx +40 -0
- package/src/__tests__/InlineProfile.test.tsx +37 -0
- package/src/__tests__/ProfileCard.test.tsx +64 -0
- package/src/__tests__/ProfileToken.test.tsx +12 -0
- package/src/cn.ts +28 -0
- package/src/inline-profile.css +73 -0
- package/src/profile-card.css +74 -0
- package/src/profile-token.css +23 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Seeds InlineProfile component classes
|
|
3
|
+
*
|
|
4
|
+
* Requires @sproutsocial/seeds-react-theme/dist/theme-all.css imported for
|
|
5
|
+
* CSS variable definitions and dark mode support.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
@layer components {
|
|
9
|
+
.seeds-inline-profile {
|
|
10
|
+
display: inline-flex;
|
|
11
|
+
flex-direction: row;
|
|
12
|
+
align-items: center;
|
|
13
|
+
gap: var(--space-200);
|
|
14
|
+
max-width: 100%;
|
|
15
|
+
font-size: var(--font-size-200);
|
|
16
|
+
line-height: var(--line-height-200);
|
|
17
|
+
color: var(--color-text-subtext);
|
|
18
|
+
text-align: inherit;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.seeds-inline-profile-avatar {
|
|
22
|
+
display: inline-flex;
|
|
23
|
+
flex-shrink: 0;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.seeds-inline-profile-content {
|
|
27
|
+
display: inline-flex;
|
|
28
|
+
align-items: center;
|
|
29
|
+
gap: var(--space-200);
|
|
30
|
+
min-width: 0;
|
|
31
|
+
flex: 1;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.seeds-inline-profile-network {
|
|
35
|
+
display: inline-flex;
|
|
36
|
+
align-items: center;
|
|
37
|
+
flex-shrink: 0;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.seeds-inline-profile-text {
|
|
41
|
+
display: inline-flex;
|
|
42
|
+
flex-direction: row;
|
|
43
|
+
align-items: center;
|
|
44
|
+
gap: var(--space-200);
|
|
45
|
+
min-width: 0;
|
|
46
|
+
flex: 1;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.seeds-inline-profile-name {
|
|
50
|
+
display: inline-block;
|
|
51
|
+
font-size: var(--font-size-200);
|
|
52
|
+
line-height: var(--line-height-200);
|
|
53
|
+
font-family: var(--font-family);
|
|
54
|
+
font-weight: var(--font-weight-semibold);
|
|
55
|
+
color: var(--color-text-body);
|
|
56
|
+
max-width: 100%;
|
|
57
|
+
overflow: hidden;
|
|
58
|
+
text-overflow: ellipsis;
|
|
59
|
+
white-space: nowrap;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.seeds-inline-profile-handle {
|
|
63
|
+
display: inline-block;
|
|
64
|
+
font-size: var(--font-size-200);
|
|
65
|
+
line-height: var(--line-height-200);
|
|
66
|
+
font-family: var(--font-family);
|
|
67
|
+
color: var(--color-text-subtext);
|
|
68
|
+
max-width: 100%;
|
|
69
|
+
overflow: hidden;
|
|
70
|
+
text-overflow: ellipsis;
|
|
71
|
+
white-space: nowrap;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Seeds ProfileCard component classes
|
|
3
|
+
*
|
|
4
|
+
* Requires @sproutsocial/seeds-react-theme/dist/theme-all.css imported for
|
|
5
|
+
* CSS variable definitions and dark mode support.
|
|
6
|
+
*
|
|
7
|
+
* The header background color is dynamic (consumer bannerColor or the network
|
|
8
|
+
* color for the partner) and is supplied via the
|
|
9
|
+
* --seeds-profile-card-banner-bg custom property set as an inline style on the
|
|
10
|
+
* header element; the background declaration below consumes it.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
@layer components {
|
|
14
|
+
.seeds-profile-card {
|
|
15
|
+
max-width: 300px;
|
|
16
|
+
border-radius: var(--radius-500);
|
|
17
|
+
background-color: var(--color-container-bg-base);
|
|
18
|
+
border: 1px solid var(--color-container-border-base);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.seeds-profile-card-header {
|
|
22
|
+
display: flex;
|
|
23
|
+
flex-direction: row;
|
|
24
|
+
position: relative;
|
|
25
|
+
height: 80px;
|
|
26
|
+
padding: var(--space-400) var(--space-400) 0 var(--space-400);
|
|
27
|
+
margin-bottom: 0;
|
|
28
|
+
color: var(--color-text-inverse);
|
|
29
|
+
background: var(--seeds-profile-card-banner-bg, transparent);
|
|
30
|
+
border-radius: var(--radius-500) var(--radius-500) 0 0;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.seeds-profile-card-footer {
|
|
34
|
+
border-top: 1px solid var(--color-container-border-base);
|
|
35
|
+
border-radius: 0 0 var(--radius-500) var(--radius-500);
|
|
36
|
+
padding: var(--space-400);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.seeds-profile-card-banner-content {
|
|
40
|
+
display: flex;
|
|
41
|
+
justify-content: flex-end;
|
|
42
|
+
width: 100%;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.seeds-profile-card-actions {
|
|
46
|
+
display: flex;
|
|
47
|
+
justify-content: flex-end;
|
|
48
|
+
gap: var(--space-100);
|
|
49
|
+
padding: 0 var(--space-300);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.seeds-profile-card-actions:empty {
|
|
53
|
+
min-height: 44px;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.seeds-profile-card-content {
|
|
57
|
+
display: flex;
|
|
58
|
+
flex-direction: column;
|
|
59
|
+
gap: var(--space-300);
|
|
60
|
+
padding: 0 var(--space-400) var(--space-400);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.seeds-profile-card-name-section {
|
|
64
|
+
display: flex;
|
|
65
|
+
align-items: center;
|
|
66
|
+
gap: var(--space-200);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.seeds-profile-card-info-row {
|
|
70
|
+
display: flex;
|
|
71
|
+
align-items: center;
|
|
72
|
+
gap: var(--space-300);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Seeds ProfileToken component classes
|
|
3
|
+
*
|
|
4
|
+
* Requires @sproutsocial/seeds-react-theme/dist/theme-all.css imported for
|
|
5
|
+
* CSS variable definitions and dark mode support.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
@layer components {
|
|
9
|
+
/*
|
|
10
|
+
* The rendered element carries both .seeds-token (from Token) and
|
|
11
|
+
* .seeds-profile-token at equal specificity, so the tighter profile padding
|
|
12
|
+
* would otherwise depend on CSS concatenation order. The compound selector
|
|
13
|
+
* raises specificity so this override deterministically wins over .seeds-token.
|
|
14
|
+
*/
|
|
15
|
+
.seeds-token.seeds-profile-token {
|
|
16
|
+
box-sizing: border-box;
|
|
17
|
+
display: inline-flex;
|
|
18
|
+
max-width: 100%;
|
|
19
|
+
vertical-align: middle;
|
|
20
|
+
padding: var(--space-100) var(--space-200);
|
|
21
|
+
margin: var(--space-100) 0;
|
|
22
|
+
}
|
|
23
|
+
}
|
package/package.json
CHANGED
|
@@ -1,15 +1,25 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sproutsocial/seeds-react-profile",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.38",
|
|
4
4
|
"description": "Seeds React Profile",
|
|
5
5
|
"author": "Sprout Social, Inc.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"main": "dist/index.js",
|
|
8
8
|
"module": "dist/esm/index.js",
|
|
9
9
|
"types": "dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"import": "./dist/esm/index.js",
|
|
13
|
+
"require": "./dist/index.js",
|
|
14
|
+
"types": "./dist/index.d.ts"
|
|
15
|
+
},
|
|
16
|
+
"./dist/inline-profile.css": "./dist/inline-profile.css",
|
|
17
|
+
"./dist/profile-card.css": "./dist/profile-card.css",
|
|
18
|
+
"./dist/profile-token.css": "./dist/profile-token.css"
|
|
19
|
+
},
|
|
10
20
|
"scripts": {
|
|
11
|
-
"build": "tsup --dts",
|
|
12
|
-
"build:debug": "tsup --dts --metafile",
|
|
21
|
+
"build": "tsup --dts && cp src/*.css dist/",
|
|
22
|
+
"build:debug": "tsup --dts --metafile && cp src/*.css dist/",
|
|
13
23
|
"dev": "tsup --watch --dts",
|
|
14
24
|
"clean": "rm -rf .turbo dist",
|
|
15
25
|
"clean:modules": "rm -rf node_modules",
|
|
@@ -20,13 +30,14 @@
|
|
|
20
30
|
"lint:fix": "eslint --fix"
|
|
21
31
|
},
|
|
22
32
|
"dependencies": {
|
|
23
|
-
"@sproutsocial/seeds-react-avatar": "^1.1.
|
|
24
|
-
"@sproutsocial/seeds-react-card": "^1.1.
|
|
25
|
-
"@sproutsocial/seeds-react-icon": "^2.4.
|
|
26
|
-
"@sproutsocial/seeds-react-link": "^1.1.
|
|
27
|
-
"@sproutsocial/seeds-react-partner-logo": "^1.7.
|
|
28
|
-
"@sproutsocial/seeds-react-
|
|
29
|
-
"@sproutsocial/seeds-react-
|
|
33
|
+
"@sproutsocial/seeds-react-avatar": "^1.1.19",
|
|
34
|
+
"@sproutsocial/seeds-react-card": "^1.1.44",
|
|
35
|
+
"@sproutsocial/seeds-react-icon": "^2.4.2",
|
|
36
|
+
"@sproutsocial/seeds-react-link": "^1.1.15",
|
|
37
|
+
"@sproutsocial/seeds-react-partner-logo": "^1.7.15",
|
|
38
|
+
"@sproutsocial/seeds-react-system-props": "^3.1.1",
|
|
39
|
+
"@sproutsocial/seeds-react-text": "^1.5.0",
|
|
40
|
+
"@sproutsocial/seeds-react-token": "^1.5.9"
|
|
30
41
|
},
|
|
31
42
|
"devDependencies": {
|
|
32
43
|
"@types/react": "^18.0.0",
|
|
@@ -153,6 +153,33 @@ export const LongNamesTruncation: Story = {
|
|
|
153
153
|
),
|
|
154
154
|
};
|
|
155
155
|
|
|
156
|
+
export const TailwindClassOverride: Story = {
|
|
157
|
+
name: "Tailwind class override",
|
|
158
|
+
render: () => (
|
|
159
|
+
<div style={{ display: "flex", flexDirection: "column", gap: "16px" }}>
|
|
160
|
+
<div style={{ border: "1px dashed #ccc" }}>
|
|
161
|
+
<InlineProfile
|
|
162
|
+
name="Default spacing"
|
|
163
|
+
secondaryName="@default"
|
|
164
|
+
avatarUrl={avatarUrl}
|
|
165
|
+
partnerName="twitter"
|
|
166
|
+
/>
|
|
167
|
+
</div>
|
|
168
|
+
<div style={{ border: "1px dashed #ccc" }}>
|
|
169
|
+
{/* Utility classes (p-300, mx-200) override the component CSS,
|
|
170
|
+
proving the @layer components fix lets Tailwind win the cascade. */}
|
|
171
|
+
<InlineProfile
|
|
172
|
+
name="Override Demo"
|
|
173
|
+
secondaryName="@override"
|
|
174
|
+
avatarUrl={avatarUrl}
|
|
175
|
+
partnerName="twitter"
|
|
176
|
+
className="p-300 mx-200"
|
|
177
|
+
/>
|
|
178
|
+
</div>
|
|
179
|
+
</div>
|
|
180
|
+
),
|
|
181
|
+
};
|
|
182
|
+
|
|
156
183
|
export const WithoutAvatarUrl: Story = {
|
|
157
184
|
args: {
|
|
158
185
|
name: "John Doe",
|
package/src/InlineProfile.tsx
CHANGED
|
@@ -1,153 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { PartnerLogo } from "@sproutsocial/seeds-react-partner-logo";
|
|
3
|
-
import { Avatar } from "@sproutsocial/seeds-react-avatar";
|
|
4
|
-
import type { TypeInlineProfileProps } from "./types";
|
|
5
|
-
import { VerifiedProfileIcon } from "./VerifiedProfileIcon";
|
|
6
|
-
|
|
7
|
-
const InlineProfileContainer = styled.span<{
|
|
8
|
-
$size?: "small" | "medium" | "large";
|
|
9
|
-
}>`
|
|
10
|
-
display: inline-flex;
|
|
11
|
-
flex-direction: row;
|
|
12
|
-
align-items: center;
|
|
13
|
-
gap: ${(props) => props.theme.space[200]};
|
|
14
|
-
max-width: 100%;
|
|
15
|
-
${(props) => props.theme.typography[200]}
|
|
16
|
-
color: ${(props) => props.theme.colors.text.subtext};
|
|
17
|
-
text-align: inherit;
|
|
18
|
-
`;
|
|
19
|
-
|
|
20
|
-
const InlineProfileAvatar = styled.span`
|
|
21
|
-
display: inline-flex;
|
|
22
|
-
flex-shrink: 0;
|
|
23
|
-
`;
|
|
24
|
-
|
|
25
|
-
const InlineProfileContent = styled.span`
|
|
26
|
-
display: inline-flex;
|
|
27
|
-
align-items: center;
|
|
28
|
-
gap: ${(props) => props.theme.space[200]};
|
|
29
|
-
min-width: 0;
|
|
30
|
-
flex: 1;
|
|
31
|
-
`;
|
|
32
|
-
|
|
33
|
-
const InlineProfileNetwork = styled.span`
|
|
34
|
-
display: inline-flex;
|
|
35
|
-
align-items: center;
|
|
36
|
-
flex-shrink: 0;
|
|
37
|
-
`;
|
|
38
|
-
|
|
39
|
-
const InlineProfileText = styled.span`
|
|
40
|
-
display: inline-flex;
|
|
41
|
-
flex-direction: row;
|
|
42
|
-
align-items: center;
|
|
43
|
-
gap: ${(props) => props.theme.space[200]};
|
|
44
|
-
min-width: 0;
|
|
45
|
-
flex: 1;
|
|
46
|
-
`;
|
|
47
|
-
|
|
48
|
-
const InlineProfileName = styled.span`
|
|
49
|
-
display: inline-block;
|
|
50
|
-
${(props) => props.theme.typography[200]}
|
|
51
|
-
font-family: ${(props) => props.theme.fontFamily};
|
|
52
|
-
font-weight: ${(props) => props.theme.fontWeights.semibold};
|
|
53
|
-
color: ${(props) => props.theme.colors.text.body};
|
|
54
|
-
max-width: 100%;
|
|
55
|
-
overflow: hidden;
|
|
56
|
-
text-overflow: ellipsis;
|
|
57
|
-
white-space: nowrap;
|
|
58
|
-
`;
|
|
59
|
-
|
|
60
|
-
const InlineProfileHandle = styled.span`
|
|
61
|
-
display: inline-block;
|
|
62
|
-
${(props) => props.theme.typography[200]}
|
|
63
|
-
font-family: ${(props) => props.theme.fontFamily};
|
|
64
|
-
color: ${(props) => props.theme.colors.text.subtext};
|
|
65
|
-
max-width: 100%;
|
|
66
|
-
overflow: hidden;
|
|
67
|
-
text-overflow: ellipsis;
|
|
68
|
-
white-space: nowrap;
|
|
69
|
-
`;
|
|
70
|
-
|
|
71
|
-
const avatarSizeMap = {
|
|
72
|
-
small: "16px",
|
|
73
|
-
medium: "20px",
|
|
74
|
-
large: "24px",
|
|
75
|
-
} as const;
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* A compact inline profile component that displays avatar, name/handle, and verification.
|
|
79
|
-
* Perfect for use in post headers, comments, or other compact spaces.
|
|
80
|
-
*
|
|
81
|
-
* @param props - Profile props
|
|
82
|
-
* @param props.name - Name of the profile
|
|
83
|
-
* @param props.secondaryName - Secondary name of the profile
|
|
84
|
-
* @param props.partnerName - Partner logo name
|
|
85
|
-
* @param props.partnerLogoLabel - Aria-label for the partner logo
|
|
86
|
-
* @param props.avatarSize - Size of the avatar
|
|
87
|
-
* @param props.avatarUrl - Image URL for the avatar
|
|
88
|
-
* @param props.children - Additional elements to render within the profile component
|
|
89
|
-
* @returns JSX.Element representing a user profile
|
|
90
|
-
*
|
|
91
|
-
* @example
|
|
92
|
-
* <InlineProfile
|
|
93
|
-
* name="John Doe"
|
|
94
|
-
* secondaryName="johndoe123"
|
|
95
|
-
* partnerName="facebook"
|
|
96
|
-
* partnerLogoLabel="Facebook Logo"
|
|
97
|
-
* avatarSize="24px"
|
|
98
|
-
* avatarUrl="https://example.com/avatar.jpg"
|
|
99
|
-
* >
|
|
100
|
-
* <Badge text="Moderator" badgeColor="green" />
|
|
101
|
-
* </InlineProfile>
|
|
102
|
-
*/
|
|
103
|
-
export const InlineProfile = ({
|
|
104
|
-
name,
|
|
105
|
-
secondaryName,
|
|
106
|
-
partnerName,
|
|
107
|
-
partnerLogoLabel,
|
|
108
|
-
size = "medium",
|
|
109
|
-
avatarSize: avatarSizeOverride,
|
|
110
|
-
avatarUrl,
|
|
111
|
-
verificationType,
|
|
112
|
-
className,
|
|
113
|
-
}: TypeInlineProfileProps) => {
|
|
114
|
-
const displayName = name || secondaryName || "Unknown Profile";
|
|
115
|
-
const avatarSize = avatarSizeOverride || avatarSizeMap[size];
|
|
116
|
-
|
|
117
|
-
return (
|
|
118
|
-
<InlineProfileContainer className={className} $size={size}>
|
|
119
|
-
{avatarUrl && (
|
|
120
|
-
<InlineProfileAvatar>
|
|
121
|
-
<Avatar src={avatarUrl} name={displayName} size={avatarSize} />
|
|
122
|
-
</InlineProfileAvatar>
|
|
123
|
-
)}
|
|
124
|
-
|
|
125
|
-
<InlineProfileContent>
|
|
126
|
-
{partnerName && (
|
|
127
|
-
<InlineProfileNetwork>
|
|
128
|
-
<PartnerLogo
|
|
129
|
-
partnerName={partnerName}
|
|
130
|
-
aria-label={partnerLogoLabel || `${partnerName} profile`}
|
|
131
|
-
size="small"
|
|
132
|
-
/>
|
|
133
|
-
</InlineProfileNetwork>
|
|
134
|
-
)}
|
|
135
|
-
|
|
136
|
-
<InlineProfileText>
|
|
137
|
-
{name && <InlineProfileName>{name}</InlineProfileName>}
|
|
138
|
-
|
|
139
|
-
{secondaryName && (
|
|
140
|
-
<InlineProfileHandle>{secondaryName}</InlineProfileHandle>
|
|
141
|
-
)}
|
|
142
|
-
</InlineProfileText>
|
|
143
|
-
|
|
144
|
-
{verificationType && (
|
|
145
|
-
<VerifiedProfileIcon
|
|
146
|
-
partnerName={partnerName}
|
|
147
|
-
verificationType={verificationType}
|
|
148
|
-
/>
|
|
149
|
-
)}
|
|
150
|
-
</InlineProfileContent>
|
|
151
|
-
</InlineProfileContainer>
|
|
152
|
-
);
|
|
153
|
-
};
|
|
1
|
+
export { InlineProfileHybrid as InlineProfile } from "./InlineProfileHybrid";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hybrid InlineProfile component.
|
|
3
|
+
* Routes between the Tailwind implementation (preferred) and the
|
|
4
|
+
* styled-components implementation (for consumers using styled-system props or
|
|
5
|
+
* a styled() extension).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { needsStyledComponents } from "@sproutsocial/seeds-react-system-props";
|
|
9
|
+
import type { TypeInlineProfileProps } from "./types";
|
|
10
|
+
import { InlineProfileStyled } from "./InlineProfileStyled";
|
|
11
|
+
import { InlineProfileTailwind } from "./InlineProfileTailwind";
|
|
12
|
+
|
|
13
|
+
export const InlineProfileHybrid = (props: TypeInlineProfileProps) => {
|
|
14
|
+
if (needsStyledComponents(props)) {
|
|
15
|
+
return <InlineProfileStyled {...props} />;
|
|
16
|
+
}
|
|
17
|
+
return <InlineProfileTailwind {...props} />;
|
|
18
|
+
};
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import styled from "styled-components";
|
|
2
|
+
import { PartnerLogo } from "@sproutsocial/seeds-react-partner-logo";
|
|
3
|
+
import { Avatar } from "@sproutsocial/seeds-react-avatar";
|
|
4
|
+
import type { TypeInlineProfileProps } from "./types";
|
|
5
|
+
import { VerifiedProfileIcon } from "./VerifiedProfileIcon";
|
|
6
|
+
|
|
7
|
+
const InlineProfileContainer = styled.span<{
|
|
8
|
+
$size?: "small" | "medium" | "large";
|
|
9
|
+
}>`
|
|
10
|
+
display: inline-flex;
|
|
11
|
+
flex-direction: row;
|
|
12
|
+
align-items: center;
|
|
13
|
+
gap: ${(props) => props.theme.space[200]};
|
|
14
|
+
max-width: 100%;
|
|
15
|
+
${(props) => props.theme.typography[200]}
|
|
16
|
+
color: ${(props) => props.theme.colors.text.subtext};
|
|
17
|
+
text-align: inherit;
|
|
18
|
+
`;
|
|
19
|
+
|
|
20
|
+
const InlineProfileAvatar = styled.span`
|
|
21
|
+
display: inline-flex;
|
|
22
|
+
flex-shrink: 0;
|
|
23
|
+
`;
|
|
24
|
+
|
|
25
|
+
const InlineProfileContent = styled.span`
|
|
26
|
+
display: inline-flex;
|
|
27
|
+
align-items: center;
|
|
28
|
+
gap: ${(props) => props.theme.space[200]};
|
|
29
|
+
min-width: 0;
|
|
30
|
+
flex: 1;
|
|
31
|
+
`;
|
|
32
|
+
|
|
33
|
+
const InlineProfileNetwork = styled.span`
|
|
34
|
+
display: inline-flex;
|
|
35
|
+
align-items: center;
|
|
36
|
+
flex-shrink: 0;
|
|
37
|
+
`;
|
|
38
|
+
|
|
39
|
+
const InlineProfileText = styled.span`
|
|
40
|
+
display: inline-flex;
|
|
41
|
+
flex-direction: row;
|
|
42
|
+
align-items: center;
|
|
43
|
+
gap: ${(props) => props.theme.space[200]};
|
|
44
|
+
min-width: 0;
|
|
45
|
+
flex: 1;
|
|
46
|
+
`;
|
|
47
|
+
|
|
48
|
+
const InlineProfileName = styled.span`
|
|
49
|
+
display: inline-block;
|
|
50
|
+
${(props) => props.theme.typography[200]}
|
|
51
|
+
font-family: ${(props) => props.theme.fontFamily};
|
|
52
|
+
font-weight: ${(props) => props.theme.fontWeights.semibold};
|
|
53
|
+
color: ${(props) => props.theme.colors.text.body};
|
|
54
|
+
max-width: 100%;
|
|
55
|
+
overflow: hidden;
|
|
56
|
+
text-overflow: ellipsis;
|
|
57
|
+
white-space: nowrap;
|
|
58
|
+
`;
|
|
59
|
+
|
|
60
|
+
const InlineProfileHandle = styled.span`
|
|
61
|
+
display: inline-block;
|
|
62
|
+
${(props) => props.theme.typography[200]}
|
|
63
|
+
font-family: ${(props) => props.theme.fontFamily};
|
|
64
|
+
color: ${(props) => props.theme.colors.text.subtext};
|
|
65
|
+
max-width: 100%;
|
|
66
|
+
overflow: hidden;
|
|
67
|
+
text-overflow: ellipsis;
|
|
68
|
+
white-space: nowrap;
|
|
69
|
+
`;
|
|
70
|
+
|
|
71
|
+
const avatarSizeMap = {
|
|
72
|
+
small: "16px",
|
|
73
|
+
medium: "20px",
|
|
74
|
+
large: "24px",
|
|
75
|
+
} as const;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Styled-components implementation of InlineProfile.
|
|
79
|
+
*
|
|
80
|
+
* Preserved as the legacy rendering path: the Hybrid routes here when the
|
|
81
|
+
* component is extended via styled() or receives styled-system props. The
|
|
82
|
+
* markup is byte-for-byte identical to the pre-migration InlineProfile.
|
|
83
|
+
*
|
|
84
|
+
* @param props - Profile props
|
|
85
|
+
* @param props.name - Name of the profile
|
|
86
|
+
* @param props.secondaryName - Secondary name of the profile
|
|
87
|
+
* @param props.partnerName - Partner logo name
|
|
88
|
+
* @param props.partnerLogoLabel - Aria-label for the partner logo
|
|
89
|
+
* @param props.avatarSize - Size of the avatar
|
|
90
|
+
* @param props.avatarUrl - Image URL for the avatar
|
|
91
|
+
* @returns JSX.Element representing a user profile
|
|
92
|
+
*/
|
|
93
|
+
export const InlineProfileStyled = ({
|
|
94
|
+
name,
|
|
95
|
+
secondaryName,
|
|
96
|
+
partnerName,
|
|
97
|
+
partnerLogoLabel,
|
|
98
|
+
size = "medium",
|
|
99
|
+
avatarSize: avatarSizeOverride,
|
|
100
|
+
avatarUrl,
|
|
101
|
+
verificationType,
|
|
102
|
+
className,
|
|
103
|
+
}: TypeInlineProfileProps) => {
|
|
104
|
+
const displayName = name || secondaryName || "Unknown Profile";
|
|
105
|
+
const avatarSize = avatarSizeOverride || avatarSizeMap[size];
|
|
106
|
+
|
|
107
|
+
return (
|
|
108
|
+
<InlineProfileContainer className={className} $size={size}>
|
|
109
|
+
{avatarUrl && (
|
|
110
|
+
<InlineProfileAvatar>
|
|
111
|
+
<Avatar src={avatarUrl} name={displayName} size={avatarSize} />
|
|
112
|
+
</InlineProfileAvatar>
|
|
113
|
+
)}
|
|
114
|
+
|
|
115
|
+
<InlineProfileContent>
|
|
116
|
+
{partnerName && (
|
|
117
|
+
<InlineProfileNetwork>
|
|
118
|
+
<PartnerLogo
|
|
119
|
+
partnerName={partnerName}
|
|
120
|
+
aria-label={partnerLogoLabel || `${partnerName} profile`}
|
|
121
|
+
size="small"
|
|
122
|
+
/>
|
|
123
|
+
</InlineProfileNetwork>
|
|
124
|
+
)}
|
|
125
|
+
|
|
126
|
+
<InlineProfileText>
|
|
127
|
+
{name && <InlineProfileName>{name}</InlineProfileName>}
|
|
128
|
+
|
|
129
|
+
{secondaryName && (
|
|
130
|
+
<InlineProfileHandle>{secondaryName}</InlineProfileHandle>
|
|
131
|
+
)}
|
|
132
|
+
</InlineProfileText>
|
|
133
|
+
|
|
134
|
+
{verificationType && (
|
|
135
|
+
<VerifiedProfileIcon
|
|
136
|
+
partnerName={partnerName}
|
|
137
|
+
verificationType={verificationType}
|
|
138
|
+
/>
|
|
139
|
+
)}
|
|
140
|
+
</InlineProfileContent>
|
|
141
|
+
</InlineProfileContainer>
|
|
142
|
+
);
|
|
143
|
+
};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tailwind/CSS-class implementation of InlineProfile.
|
|
3
|
+
* Uses Seeds InlineProfile CSS classes from inline-profile.css.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { PartnerLogo } from "@sproutsocial/seeds-react-partner-logo";
|
|
7
|
+
import { Avatar } from "@sproutsocial/seeds-react-avatar";
|
|
8
|
+
import type { TypeInlineProfileProps } from "./types";
|
|
9
|
+
import { VerifiedProfileIcon } from "./VerifiedProfileIcon";
|
|
10
|
+
import { cn } from "./cn";
|
|
11
|
+
|
|
12
|
+
const avatarSizeMap = {
|
|
13
|
+
small: "16px",
|
|
14
|
+
medium: "20px",
|
|
15
|
+
large: "24px",
|
|
16
|
+
} as const;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* A compact inline profile component that displays avatar, name/handle, and verification.
|
|
20
|
+
* Perfect for use in post headers, comments, or other compact spaces.
|
|
21
|
+
*
|
|
22
|
+
* @param props - Profile props
|
|
23
|
+
* @param props.name - Name of the profile
|
|
24
|
+
* @param props.secondaryName - Secondary name of the profile
|
|
25
|
+
* @param props.partnerName - Partner logo name
|
|
26
|
+
* @param props.partnerLogoLabel - Aria-label for the partner logo
|
|
27
|
+
* @param props.avatarSize - Size of the avatar
|
|
28
|
+
* @param props.avatarUrl - Image URL for the avatar
|
|
29
|
+
* @returns JSX.Element representing a user profile
|
|
30
|
+
*/
|
|
31
|
+
export const InlineProfileTailwind = ({
|
|
32
|
+
name,
|
|
33
|
+
secondaryName,
|
|
34
|
+
partnerName,
|
|
35
|
+
partnerLogoLabel,
|
|
36
|
+
size = "medium",
|
|
37
|
+
avatarSize: avatarSizeOverride,
|
|
38
|
+
avatarUrl,
|
|
39
|
+
verificationType,
|
|
40
|
+
className,
|
|
41
|
+
}: TypeInlineProfileProps) => {
|
|
42
|
+
const displayName = name || secondaryName || "Unknown Profile";
|
|
43
|
+
const avatarSize = avatarSizeOverride || avatarSizeMap[size];
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<span className={cn("seeds-inline-profile", className)}>
|
|
47
|
+
{avatarUrl && (
|
|
48
|
+
<span className="seeds-inline-profile-avatar">
|
|
49
|
+
<Avatar src={avatarUrl} name={displayName} size={avatarSize} />
|
|
50
|
+
</span>
|
|
51
|
+
)}
|
|
52
|
+
|
|
53
|
+
<span className="seeds-inline-profile-content">
|
|
54
|
+
{partnerName && (
|
|
55
|
+
<span className="seeds-inline-profile-network">
|
|
56
|
+
<PartnerLogo
|
|
57
|
+
partnerName={partnerName}
|
|
58
|
+
aria-label={partnerLogoLabel || `${partnerName} profile`}
|
|
59
|
+
size="small"
|
|
60
|
+
/>
|
|
61
|
+
</span>
|
|
62
|
+
)}
|
|
63
|
+
|
|
64
|
+
<span className="seeds-inline-profile-text">
|
|
65
|
+
{name && <span className="seeds-inline-profile-name">{name}</span>}
|
|
66
|
+
|
|
67
|
+
{secondaryName && (
|
|
68
|
+
<span className="seeds-inline-profile-handle">{secondaryName}</span>
|
|
69
|
+
)}
|
|
70
|
+
</span>
|
|
71
|
+
|
|
72
|
+
{verificationType && (
|
|
73
|
+
<VerifiedProfileIcon
|
|
74
|
+
partnerName={partnerName}
|
|
75
|
+
verificationType={verificationType}
|
|
76
|
+
/>
|
|
77
|
+
)}
|
|
78
|
+
</span>
|
|
79
|
+
</span>
|
|
80
|
+
);
|
|
81
|
+
};
|
|
@@ -238,6 +238,33 @@ export const DifferentNetworks: Story = {
|
|
|
238
238
|
),
|
|
239
239
|
};
|
|
240
240
|
|
|
241
|
+
export const TailwindClassOverride: Story = {
|
|
242
|
+
name: "Tailwind class override",
|
|
243
|
+
render: () => (
|
|
244
|
+
<div style={{ display: "flex", flexDirection: "column", gap: "16px" }}>
|
|
245
|
+
<ProfileCard
|
|
246
|
+
name="Default spacing"
|
|
247
|
+
secondaryName="@default"
|
|
248
|
+
partnerName="twitter"
|
|
249
|
+
avatarUrl="https://via.placeholder.com/60"
|
|
250
|
+
description="Default card spacing."
|
|
251
|
+
/>
|
|
252
|
+
{/* The bare className prop is not forwarded to the card root, so the
|
|
253
|
+
override goes through cardProps. Utility classes (p-300, mx-200)
|
|
254
|
+
override the component CSS, proving the @layer components fix lets
|
|
255
|
+
Tailwind win the cascade. */}
|
|
256
|
+
<ProfileCard
|
|
257
|
+
name="Override Demo"
|
|
258
|
+
secondaryName="@override"
|
|
259
|
+
partnerName="twitter"
|
|
260
|
+
avatarUrl="https://via.placeholder.com/60"
|
|
261
|
+
description="Card with Tailwind utility overrides."
|
|
262
|
+
cardProps={{ role: "presentation", className: "p-300 mx-200" }}
|
|
263
|
+
/>
|
|
264
|
+
</div>
|
|
265
|
+
),
|
|
266
|
+
};
|
|
267
|
+
|
|
241
268
|
export const LongContentTruncation: Story = {
|
|
242
269
|
render: () => (
|
|
243
270
|
<div style={{ display: "flex", flexDirection: "column", gap: "16px" }}>
|