@sproutsocial/seeds-react-empty-state 1.0.30 → 1.0.31
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 +11 -11
- package/CHANGELOG.md +6 -0
- package/dist/empty-state.css +68 -0
- package/dist/esm/index.js +135 -40
- package/dist/esm/index.js.map +1 -1
- package/dist/index.d.mts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +133 -41
- package/dist/index.js.map +1 -1
- package/package.json +12 -4
- package/src/EmptyState.stories.tsx +19 -0
- package/src/EmptyState.tsx +13 -60
- package/src/EmptyStateHybrid.tsx +35 -0
- package/src/EmptyStateStyled.tsx +70 -0
- package/src/EmptyStateTailwind.tsx +81 -0
- package/src/__tests__/EmptyState.test.tsx +44 -0
- package/src/css.d.ts +1 -0
- package/src/empty-state.css +68 -0
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
yarn run v1.22.22
|
|
2
|
-
$ tsup --dts
|
|
2
|
+
$ tsup --dts && cp src/empty-state.css dist/empty-state.css
|
|
3
3
|
[34mCLI[39m Building entry: src/index.ts
|
|
4
4
|
[34mCLI[39m Using tsconfig: tsconfig.json
|
|
5
5
|
[34mCLI[39m tsup v8.5.0
|
|
@@ -8,14 +8,14 @@ $ tsup --dts
|
|
|
8
8
|
[34mCLI[39m Cleaning output folder
|
|
9
9
|
[34mCJS[39m Build start
|
|
10
10
|
[34mESM[39m Build start
|
|
11
|
-
[32mCJS[39m [1mdist/index.js [22m[
|
|
12
|
-
[32mCJS[39m [1mdist/index.js.map [22m[
|
|
13
|
-
[32mCJS[39m ⚡️ Build success in
|
|
14
|
-
[32mESM[39m [1mdist/esm/index.js [22m[
|
|
15
|
-
[32mESM[39m [1mdist/esm/index.js.map [22m[
|
|
16
|
-
[32mESM[39m ⚡️ Build success in
|
|
11
|
+
[32mCJS[39m [1mdist/index.js [22m[32m7.43 KB[39m
|
|
12
|
+
[32mCJS[39m [1mdist/index.js.map [22m[32m9.54 KB[39m
|
|
13
|
+
[32mCJS[39m ⚡️ Build success in 23ms
|
|
14
|
+
[32mESM[39m [1mdist/esm/index.js [22m[32m4.98 KB[39m
|
|
15
|
+
[32mESM[39m [1mdist/esm/index.js.map [22m[32m9.44 KB[39m
|
|
16
|
+
[32mESM[39m ⚡️ Build success in 23ms
|
|
17
17
|
[34mDTS[39m Build start
|
|
18
|
-
[32mDTS[39m ⚡️ Build success in
|
|
19
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[
|
|
20
|
-
[32mDTS[39m [1mdist/index.d.mts [22m[
|
|
21
|
-
Done in
|
|
18
|
+
[32mDTS[39m ⚡️ Build success in 1399ms
|
|
19
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m878.00 B[39m
|
|
20
|
+
[32mDTS[39m [1mdist/index.d.mts [22m[32m878.00 B[39m
|
|
21
|
+
Done in 2.12s.
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @sproutsocial/seeds-react-empty-state
|
|
2
2
|
|
|
3
|
+
## 1.0.31
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- a635951: Migrate EmptyState to the hybrid Tailwind rendering pattern: renders via Tailwind / seeds-\* classes by default and falls back to styled-components for styled-system props, mixin props, and styled() extensions. No public API changes.
|
|
8
|
+
|
|
3
9
|
## 1.0.30
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Seeds EmptyState component classes
|
|
3
|
+
* Use these instead of writing out individual Tailwind utility classes.
|
|
4
|
+
*
|
|
5
|
+
* Requires @sproutsocial/seeds-react-theme/dist/theme-all.css imported for
|
|
6
|
+
* CSS variable definitions and dark mode support.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* <div class="seeds-empty-state">...</div>
|
|
10
|
+
*
|
|
11
|
+
* Rules live in @layer components so that consumer Tailwind utilities
|
|
12
|
+
* (e.g. mx-200, p-300) win in the cascade and can override these component
|
|
13
|
+
* styles, rather than tying/beating them on specificity.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
@layer components {
|
|
17
|
+
.seeds-empty-state {
|
|
18
|
+
max-width: 400px;
|
|
19
|
+
margin-inline: auto;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.seeds-empty-state-media {
|
|
23
|
+
display: flex;
|
|
24
|
+
flex-direction: column;
|
|
25
|
+
justify-content: center;
|
|
26
|
+
margin-bottom: var(--space-450);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.seeds-empty-state-content {
|
|
30
|
+
text-align: center;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.seeds-empty-state-headline {
|
|
34
|
+
margin: 0;
|
|
35
|
+
padding-left: 0;
|
|
36
|
+
padding-right: 0;
|
|
37
|
+
font-family: var(--font-family);
|
|
38
|
+
color: var(--color-text-headline);
|
|
39
|
+
font-weight: var(--font-weight-semibold);
|
|
40
|
+
font-size: var(--font-size-400);
|
|
41
|
+
line-height: var(--line-height-400);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.seeds-empty-state-subtext {
|
|
45
|
+
margin: 0;
|
|
46
|
+
margin-top: var(--space-400);
|
|
47
|
+
padding-left: 0;
|
|
48
|
+
padding-right: 0;
|
|
49
|
+
font-family: var(--font-family);
|
|
50
|
+
color: var(--color-text-subtext);
|
|
51
|
+
font-weight: var(--font-weight-normal);
|
|
52
|
+
font-size: var(--font-size-200);
|
|
53
|
+
line-height: var(--line-height-200);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.seeds-empty-state-actions {
|
|
57
|
+
margin-top: var(--space-450);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/* -empty MUST follow the base rule: equal specificity, source order wins */
|
|
61
|
+
.seeds-empty-state-actions-empty {
|
|
62
|
+
margin-top: 0;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.seeds-empty-state-secondary-action {
|
|
66
|
+
margin-top: var(--space-400);
|
|
67
|
+
}
|
|
68
|
+
}
|
package/dist/esm/index.js
CHANGED
|
@@ -1,52 +1,147 @@
|
|
|
1
1
|
// src/EmptyState.tsx
|
|
2
|
+
import React4, { memo } from "react";
|
|
3
|
+
|
|
4
|
+
// src/EmptyStateHybrid.tsx
|
|
5
|
+
import React3 from "react";
|
|
6
|
+
import {
|
|
7
|
+
hasStyledProps,
|
|
8
|
+
isStyledExtension
|
|
9
|
+
} from "@sproutsocial/seeds-react-system-props";
|
|
10
|
+
|
|
11
|
+
// src/EmptyStateStyled.tsx
|
|
2
12
|
import * as React from "react";
|
|
3
13
|
import Box from "@sproutsocial/seeds-react-box";
|
|
4
14
|
import Text from "@sproutsocial/seeds-react-text";
|
|
5
15
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
-
var
|
|
7
|
-
media,
|
|
8
|
-
|
|
9
|
-
subtext,
|
|
10
|
-
primaryAction,
|
|
11
|
-
secondaryAction,
|
|
12
|
-
...rest
|
|
13
|
-
}) => {
|
|
14
|
-
return /* @__PURE__ */ jsxs(Box, { maxWidth: "400px", mx: "auto", "data-qa-emptystate": headline, ...rest, children: [
|
|
15
|
-
media && /* @__PURE__ */ jsx(
|
|
16
|
+
var EmptyStateStyled = React.forwardRef(
|
|
17
|
+
({ media, headline, subtext, primaryAction, secondaryAction, ...rest }, ref) => {
|
|
18
|
+
return /* @__PURE__ */ jsxs(
|
|
16
19
|
Box,
|
|
17
20
|
{
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
ref,
|
|
22
|
+
maxWidth: "400px",
|
|
23
|
+
mx: "auto",
|
|
24
|
+
"data-qa-emptystate": headline,
|
|
25
|
+
...rest,
|
|
26
|
+
children: [
|
|
27
|
+
media && /* @__PURE__ */ jsx(
|
|
28
|
+
Box,
|
|
29
|
+
{
|
|
30
|
+
display: "flex",
|
|
31
|
+
flexDirection: "column",
|
|
32
|
+
justifyContent: "center",
|
|
33
|
+
mb: 450,
|
|
34
|
+
children: media
|
|
35
|
+
}
|
|
36
|
+
),
|
|
37
|
+
/* @__PURE__ */ jsxs(Text, { as: "div", textAlign: "center", children: [
|
|
38
|
+
/* @__PURE__ */ jsx(
|
|
39
|
+
Text,
|
|
40
|
+
{
|
|
41
|
+
as: "p",
|
|
42
|
+
m: 0,
|
|
43
|
+
color: "text.headline",
|
|
44
|
+
fontWeight: "semibold",
|
|
45
|
+
fontSize: 400,
|
|
46
|
+
children: headline
|
|
47
|
+
}
|
|
48
|
+
),
|
|
49
|
+
subtext && /* @__PURE__ */ jsx(Text, { as: "p", color: "text.subtext", fontSize: 200, mb: 0, mt: 400, children: subtext }),
|
|
50
|
+
/* @__PURE__ */ jsxs(Box, { mt: primaryAction || secondaryAction ? 450 : 0, children: [
|
|
51
|
+
primaryAction && React.cloneElement(primaryAction, {
|
|
52
|
+
appearance: "primary"
|
|
53
|
+
}),
|
|
54
|
+
secondaryAction && /* @__PURE__ */ jsx(Box, { mt: 400, children: React.cloneElement(secondaryAction, {
|
|
55
|
+
appearance: "unstyled"
|
|
56
|
+
}) })
|
|
57
|
+
] })
|
|
58
|
+
] })
|
|
59
|
+
]
|
|
23
60
|
}
|
|
24
|
-
)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
);
|
|
64
|
+
EmptyStateStyled.displayName = "EmptyState";
|
|
65
|
+
var EmptyStateStyled_default = EmptyStateStyled;
|
|
66
|
+
|
|
67
|
+
// src/EmptyStateTailwind.tsx
|
|
68
|
+
import React2 from "react";
|
|
69
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
70
|
+
function cn(...inputs) {
|
|
71
|
+
const classes = [];
|
|
72
|
+
for (const input of inputs) {
|
|
73
|
+
if (!input) continue;
|
|
74
|
+
if (typeof input === "string") {
|
|
75
|
+
classes.push(input);
|
|
76
|
+
} else if (typeof input === "object") {
|
|
77
|
+
for (const [key, value] of Object.entries(input)) {
|
|
78
|
+
if (value) {
|
|
79
|
+
classes.push(key);
|
|
35
80
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return classes.join(" ");
|
|
85
|
+
}
|
|
86
|
+
var EmptyStateTailwind = React2.forwardRef(
|
|
87
|
+
({ media, headline, subtext, primaryAction, secondaryAction, className, ...rest }, ref) => {
|
|
88
|
+
const hasAction = Boolean(primaryAction || secondaryAction);
|
|
89
|
+
return /* @__PURE__ */ jsxs2(
|
|
90
|
+
"div",
|
|
91
|
+
{
|
|
92
|
+
"data-qa-emptystate": headline,
|
|
93
|
+
...rest,
|
|
94
|
+
ref,
|
|
95
|
+
className: cn("seeds-empty-state", className),
|
|
96
|
+
children: [
|
|
97
|
+
media && /* @__PURE__ */ jsx2("div", { className: "seeds-empty-state-media", children: media }),
|
|
98
|
+
/* @__PURE__ */ jsxs2("div", { className: "seeds-empty-state-content", children: [
|
|
99
|
+
/* @__PURE__ */ jsx2("p", { className: "seeds-empty-state-headline", children: headline }),
|
|
100
|
+
subtext && /* @__PURE__ */ jsx2("p", { className: "seeds-empty-state-subtext", children: subtext }),
|
|
101
|
+
/* @__PURE__ */ jsxs2(
|
|
102
|
+
"div",
|
|
103
|
+
{
|
|
104
|
+
className: cn("seeds-empty-state-actions", {
|
|
105
|
+
"seeds-empty-state-actions-empty": !hasAction
|
|
106
|
+
}),
|
|
107
|
+
children: [
|
|
108
|
+
primaryAction && React2.cloneElement(primaryAction, {
|
|
109
|
+
appearance: "primary"
|
|
110
|
+
}),
|
|
111
|
+
secondaryAction && /* @__PURE__ */ jsx2("div", { className: "seeds-empty-state-secondary-action", children: React2.cloneElement(secondaryAction, {
|
|
112
|
+
appearance: "unstyled"
|
|
113
|
+
}) })
|
|
114
|
+
]
|
|
115
|
+
}
|
|
116
|
+
)
|
|
117
|
+
] })
|
|
118
|
+
]
|
|
119
|
+
}
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
);
|
|
123
|
+
EmptyStateTailwind.displayName = "EmptyStateTailwind";
|
|
124
|
+
|
|
125
|
+
// src/EmptyStateHybrid.tsx
|
|
126
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
127
|
+
var EmptyStateHybrid = React3.forwardRef(
|
|
128
|
+
(props, ref) => {
|
|
129
|
+
if (hasStyledProps(props) || isStyledExtension(props)) {
|
|
130
|
+
return /* @__PURE__ */ jsx3(EmptyStateStyled_default, { ...props, ref });
|
|
131
|
+
}
|
|
132
|
+
return /* @__PURE__ */ jsx3(EmptyStateTailwind, { ...props, ref });
|
|
133
|
+
}
|
|
134
|
+
);
|
|
135
|
+
EmptyStateHybrid.displayName = "EmptyState";
|
|
136
|
+
var EmptyStateHybrid_default = EmptyStateHybrid;
|
|
137
|
+
|
|
138
|
+
// src/EmptyState.tsx
|
|
139
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
140
|
+
var EmptyState = React4.forwardRef(
|
|
141
|
+
(props, ref) => /* @__PURE__ */ jsx4(EmptyStateHybrid_default, { ...props, ref })
|
|
142
|
+
);
|
|
143
|
+
EmptyState.displayName = "EmptyState";
|
|
144
|
+
var EmptyState_default = memo(EmptyState);
|
|
50
145
|
|
|
51
146
|
// src/EmptyStateTypes.ts
|
|
52
147
|
import "react";
|
package/dist/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/EmptyState.tsx","../../src/EmptyStateTypes.ts","../../src/index.ts"],"sourcesContent":["import * as React from \"react\";\nimport Box from \"@sproutsocial/seeds-react-box\";\n// eslint-disable-next-line import/no-deprecated\nimport Text from \"@sproutsocial/seeds-react-text\";\nimport type { TypeEmptyStateProps } from \"./EmptyStateTypes\";\n\nconst
|
|
1
|
+
{"version":3,"sources":["../../src/EmptyState.tsx","../../src/EmptyStateHybrid.tsx","../../src/EmptyStateStyled.tsx","../../src/EmptyStateTailwind.tsx","../../src/EmptyStateTypes.ts","../../src/index.ts"],"sourcesContent":["import React, { memo } from \"react\";\n\nimport type { TypeEmptyStateProps } from \"./EmptyStateTypes\";\nimport EmptyStateHybrid from \"./EmptyStateHybrid\";\n\n/**\n * EmptyState component. Automatically routes between the Tailwind\n * implementation (preferred) and the styled-components implementation (for\n * consumers using styled-system props or a styled() extension).\n */\nconst EmptyState = React.forwardRef<HTMLDivElement, TypeEmptyStateProps>(\n (props, ref) => <EmptyStateHybrid {...props} ref={ref} />\n);\n\nEmptyState.displayName = \"EmptyState\";\n\nexport default memo(EmptyState);\n","/**\n * Hybrid EmptyState Component\n * Automatically chooses between Tailwind and styled-components based on props.\n *\n * Routes to the styled-components path when a styled-system prop is present\n * (hasStyledProps) or the component was extended with styled()\n * (isStyledExtension). Otherwise renders the Tailwind path.\n *\n * No mixin check is needed: EmptyState's root applies no standalone mixin\n * beyond the styled-system set, so hasStyledProps covers every routing prop.\n */\n\nimport React from \"react\";\nimport {\n hasStyledProps,\n isStyledExtension,\n} from \"@sproutsocial/seeds-react-system-props\";\n\nimport type { TypeEmptyStateProps } from \"./EmptyStateTypes\";\nimport EmptyStateStyled from \"./EmptyStateStyled\"; // Styled-components version\nimport { EmptyStateTailwind } from \"./EmptyStateTailwind\"; // Tailwind version\n\nconst EmptyStateHybrid = React.forwardRef<HTMLDivElement, TypeEmptyStateProps>(\n (props, ref) => {\n if (hasStyledProps(props) || isStyledExtension(props)) {\n return <EmptyStateStyled {...props} ref={ref} />;\n }\n\n // Use Tailwind version (preferred - better performance)\n return <EmptyStateTailwind {...props} ref={ref} />;\n }\n);\n\nEmptyStateHybrid.displayName = \"EmptyState\";\nexport default EmptyStateHybrid;\n","import * as React from \"react\";\nimport Box from \"@sproutsocial/seeds-react-box\";\n// eslint-disable-next-line import/no-deprecated\nimport Text from \"@sproutsocial/seeds-react-text\";\nimport type { TypeEmptyStateProps } from \"./EmptyStateTypes\";\n\nconst EmptyStateStyled = React.forwardRef<HTMLDivElement, TypeEmptyStateProps>(\n (\n { media, headline, subtext, primaryAction, secondaryAction, ...rest },\n ref\n ) => {\n return (\n <Box\n ref={ref}\n maxWidth=\"400px\"\n mx=\"auto\"\n data-qa-emptystate={headline}\n {...rest}\n >\n {media && (\n <Box\n display=\"flex\"\n flexDirection=\"column\"\n justifyContent=\"center\"\n mb={450}\n >\n {media}\n </Box>\n )}\n\n <Text as=\"div\" textAlign=\"center\">\n <Text\n as=\"p\"\n m={0}\n color=\"text.headline\"\n fontWeight=\"semibold\"\n fontSize={400}\n >\n {headline}\n </Text>\n\n {subtext && (\n <Text as=\"p\" color=\"text.subtext\" fontSize={200} mb={0} mt={400}>\n {subtext}\n </Text>\n )}\n\n <Box mt={primaryAction || secondaryAction ? 450 : 0}>\n {primaryAction &&\n React.cloneElement(primaryAction, {\n appearance: \"primary\",\n })}\n\n {secondaryAction && (\n <Box mt={400}>\n {React.cloneElement(secondaryAction, {\n appearance: \"unstyled\",\n })}\n </Box>\n )}\n </Box>\n </Text>\n </Box>\n );\n }\n);\n\nEmptyStateStyled.displayName = \"EmptyState\";\n\nexport default EmptyStateStyled;\n","/**\n * Tailwind CSS implementation of EmptyState component\n * Uses Seeds empty-state CSS classes from empty-state.css\n */\n\nimport React from \"react\";\n\nimport type { TypeEmptyStateProps } from \"./EmptyStateTypes\";\n\n// Utility for merging class names properly\nfunction cn(\n ...inputs: (string | undefined | null | false | Record<string, boolean>)[]\n): string {\n const classes: string[] = [];\n\n for (const input of inputs) {\n if (!input) continue;\n\n if (typeof input === \"string\") {\n classes.push(input);\n } else if (typeof input === \"object\") {\n for (const [key, value] of Object.entries(input)) {\n if (value) {\n classes.push(key);\n }\n }\n }\n }\n\n return classes.join(\" \");\n}\n\nexport const EmptyStateTailwind = React.forwardRef<\n HTMLDivElement,\n TypeEmptyStateProps\n>(\n (\n { media, headline, subtext, primaryAction, secondaryAction, className, ...rest },\n ref\n ) => {\n const hasAction = Boolean(primaryAction || secondaryAction);\n\n return (\n <div\n data-qa-emptystate={headline}\n {...(rest as React.HTMLAttributes<HTMLDivElement>)}\n ref={ref}\n className={cn(\"seeds-empty-state\", className)}\n >\n {media && <div className=\"seeds-empty-state-media\">{media}</div>}\n\n <div className=\"seeds-empty-state-content\">\n <p className=\"seeds-empty-state-headline\">{headline}</p>\n\n {subtext && <p className=\"seeds-empty-state-subtext\">{subtext}</p>}\n\n <div\n className={cn(\"seeds-empty-state-actions\", {\n \"seeds-empty-state-actions-empty\": !hasAction,\n })}\n >\n {primaryAction &&\n React.cloneElement(primaryAction, {\n appearance: \"primary\",\n })}\n\n {secondaryAction && (\n <div className=\"seeds-empty-state-secondary-action\">\n {React.cloneElement(secondaryAction, {\n appearance: \"unstyled\",\n })}\n </div>\n )}\n </div>\n </div>\n </div>\n );\n }\n);\n\nEmptyStateTailwind.displayName = \"EmptyStateTailwind\";\n","import * as React from \"react\";\nimport type { TypeBoxProps } from \"@sproutsocial/seeds-react-box\";\n\nexport interface TypeEmptyStateProps extends TypeBoxProps {\n /** An image in (preferably in SVG format) */\n media?: React.ReactNode;\n\n /** A headline describing the empty state */\n headline: React.ReactNode;\n\n /** Optional description of the empty state */\n subtext?: React.ReactNode;\n\n /** Element for the primary call-to-action */\n primaryAction?: React.ReactElement;\n\n /** Element for any secondary call-to-action */\n secondaryAction?: React.ReactElement;\n}\n","import EmptyState from \"./EmptyState\";\n\nexport default EmptyState;\nexport { EmptyState };\nexport * from \"./EmptyStateTypes\";\n"],"mappings":"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","names":["React","React","React","jsx","jsxs","jsx","React","jsx","React"]}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
1
|
import * as React from 'react';
|
|
2
|
+
import React__default from 'react';
|
|
3
3
|
import { TypeBoxProps } from '@sproutsocial/seeds-react-box';
|
|
4
4
|
|
|
5
5
|
interface TypeEmptyStateProps extends TypeBoxProps {
|
|
@@ -15,6 +15,6 @@ interface TypeEmptyStateProps extends TypeBoxProps {
|
|
|
15
15
|
secondaryAction?: React.ReactElement;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
declare const
|
|
18
|
+
declare const _default: React__default.MemoExoticComponent<React__default.ForwardRefExoticComponent<Omit<TypeEmptyStateProps, "ref"> & React__default.RefAttributes<HTMLDivElement>>>;
|
|
19
19
|
|
|
20
|
-
export { EmptyState, type TypeEmptyStateProps,
|
|
20
|
+
export { _default as EmptyState, type TypeEmptyStateProps, _default as default };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
1
|
import * as React from 'react';
|
|
2
|
+
import React__default from 'react';
|
|
3
3
|
import { TypeBoxProps } from '@sproutsocial/seeds-react-box';
|
|
4
4
|
|
|
5
5
|
interface TypeEmptyStateProps extends TypeBoxProps {
|
|
@@ -15,6 +15,6 @@ interface TypeEmptyStateProps extends TypeBoxProps {
|
|
|
15
15
|
secondaryAction?: React.ReactElement;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
declare const
|
|
18
|
+
declare const _default: React__default.MemoExoticComponent<React__default.ForwardRefExoticComponent<Omit<TypeEmptyStateProps, "ref"> & React__default.RefAttributes<HTMLDivElement>>>;
|
|
19
19
|
|
|
20
|
-
export { EmptyState, type TypeEmptyStateProps,
|
|
20
|
+
export { _default as EmptyState, type TypeEmptyStateProps, _default as default };
|
package/dist/index.js
CHANGED
|
@@ -36,57 +36,149 @@ __export(index_exports, {
|
|
|
36
36
|
module.exports = __toCommonJS(index_exports);
|
|
37
37
|
|
|
38
38
|
// src/EmptyState.tsx
|
|
39
|
+
var import_react3 = __toESM(require("react"));
|
|
40
|
+
|
|
41
|
+
// src/EmptyStateHybrid.tsx
|
|
42
|
+
var import_react2 = __toESM(require("react"));
|
|
43
|
+
var import_seeds_react_system_props = require("@sproutsocial/seeds-react-system-props");
|
|
44
|
+
|
|
45
|
+
// src/EmptyStateStyled.tsx
|
|
39
46
|
var React = __toESM(require("react"));
|
|
40
47
|
var import_seeds_react_box = __toESM(require("@sproutsocial/seeds-react-box"));
|
|
41
48
|
var import_seeds_react_text = __toESM(require("@sproutsocial/seeds-react-text"));
|
|
42
49
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
43
|
-
var
|
|
44
|
-
media,
|
|
45
|
-
|
|
46
|
-
subtext,
|
|
47
|
-
primaryAction,
|
|
48
|
-
secondaryAction,
|
|
49
|
-
...rest
|
|
50
|
-
}) => {
|
|
51
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_seeds_react_box.default, { maxWidth: "400px", mx: "auto", "data-qa-emptystate": headline, ...rest, children: [
|
|
52
|
-
media && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
50
|
+
var EmptyStateStyled = React.forwardRef(
|
|
51
|
+
({ media, headline, subtext, primaryAction, secondaryAction, ...rest }, ref) => {
|
|
52
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
53
53
|
import_seeds_react_box.default,
|
|
54
54
|
{
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
55
|
+
ref,
|
|
56
|
+
maxWidth: "400px",
|
|
57
|
+
mx: "auto",
|
|
58
|
+
"data-qa-emptystate": headline,
|
|
59
|
+
...rest,
|
|
60
|
+
children: [
|
|
61
|
+
media && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
62
|
+
import_seeds_react_box.default,
|
|
63
|
+
{
|
|
64
|
+
display: "flex",
|
|
65
|
+
flexDirection: "column",
|
|
66
|
+
justifyContent: "center",
|
|
67
|
+
mb: 450,
|
|
68
|
+
children: media
|
|
69
|
+
}
|
|
70
|
+
),
|
|
71
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_seeds_react_text.default, { as: "div", textAlign: "center", children: [
|
|
72
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
73
|
+
import_seeds_react_text.default,
|
|
74
|
+
{
|
|
75
|
+
as: "p",
|
|
76
|
+
m: 0,
|
|
77
|
+
color: "text.headline",
|
|
78
|
+
fontWeight: "semibold",
|
|
79
|
+
fontSize: 400,
|
|
80
|
+
children: headline
|
|
81
|
+
}
|
|
82
|
+
),
|
|
83
|
+
subtext && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_seeds_react_text.default, { as: "p", color: "text.subtext", fontSize: 200, mb: 0, mt: 400, children: subtext }),
|
|
84
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_seeds_react_box.default, { mt: primaryAction || secondaryAction ? 450 : 0, children: [
|
|
85
|
+
primaryAction && React.cloneElement(primaryAction, {
|
|
86
|
+
appearance: "primary"
|
|
87
|
+
}),
|
|
88
|
+
secondaryAction && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_seeds_react_box.default, { mt: 400, children: React.cloneElement(secondaryAction, {
|
|
89
|
+
appearance: "unstyled"
|
|
90
|
+
}) })
|
|
91
|
+
] })
|
|
92
|
+
] })
|
|
93
|
+
]
|
|
60
94
|
}
|
|
61
|
-
)
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
);
|
|
98
|
+
EmptyStateStyled.displayName = "EmptyState";
|
|
99
|
+
var EmptyStateStyled_default = EmptyStateStyled;
|
|
100
|
+
|
|
101
|
+
// src/EmptyStateTailwind.tsx
|
|
102
|
+
var import_react = __toESM(require("react"));
|
|
103
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
104
|
+
function cn(...inputs) {
|
|
105
|
+
const classes = [];
|
|
106
|
+
for (const input of inputs) {
|
|
107
|
+
if (!input) continue;
|
|
108
|
+
if (typeof input === "string") {
|
|
109
|
+
classes.push(input);
|
|
110
|
+
} else if (typeof input === "object") {
|
|
111
|
+
for (const [key, value] of Object.entries(input)) {
|
|
112
|
+
if (value) {
|
|
113
|
+
classes.push(key);
|
|
72
114
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return classes.join(" ");
|
|
119
|
+
}
|
|
120
|
+
var EmptyStateTailwind = import_react.default.forwardRef(
|
|
121
|
+
({ media, headline, subtext, primaryAction, secondaryAction, className, ...rest }, ref) => {
|
|
122
|
+
const hasAction = Boolean(primaryAction || secondaryAction);
|
|
123
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
124
|
+
"div",
|
|
125
|
+
{
|
|
126
|
+
"data-qa-emptystate": headline,
|
|
127
|
+
...rest,
|
|
128
|
+
ref,
|
|
129
|
+
className: cn("seeds-empty-state", className),
|
|
130
|
+
children: [
|
|
131
|
+
media && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "seeds-empty-state-media", children: media }),
|
|
132
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "seeds-empty-state-content", children: [
|
|
133
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "seeds-empty-state-headline", children: headline }),
|
|
134
|
+
subtext && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "seeds-empty-state-subtext", children: subtext }),
|
|
135
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
136
|
+
"div",
|
|
137
|
+
{
|
|
138
|
+
className: cn("seeds-empty-state-actions", {
|
|
139
|
+
"seeds-empty-state-actions-empty": !hasAction
|
|
140
|
+
}),
|
|
141
|
+
children: [
|
|
142
|
+
primaryAction && import_react.default.cloneElement(primaryAction, {
|
|
143
|
+
appearance: "primary"
|
|
144
|
+
}),
|
|
145
|
+
secondaryAction && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "seeds-empty-state-secondary-action", children: import_react.default.cloneElement(secondaryAction, {
|
|
146
|
+
appearance: "unstyled"
|
|
147
|
+
}) })
|
|
148
|
+
]
|
|
149
|
+
}
|
|
150
|
+
)
|
|
151
|
+
] })
|
|
152
|
+
]
|
|
153
|
+
}
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
);
|
|
157
|
+
EmptyStateTailwind.displayName = "EmptyStateTailwind";
|
|
158
|
+
|
|
159
|
+
// src/EmptyStateHybrid.tsx
|
|
160
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
161
|
+
var EmptyStateHybrid = import_react2.default.forwardRef(
|
|
162
|
+
(props, ref) => {
|
|
163
|
+
if ((0, import_seeds_react_system_props.hasStyledProps)(props) || (0, import_seeds_react_system_props.isStyledExtension)(props)) {
|
|
164
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(EmptyStateStyled_default, { ...props, ref });
|
|
165
|
+
}
|
|
166
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(EmptyStateTailwind, { ...props, ref });
|
|
167
|
+
}
|
|
168
|
+
);
|
|
169
|
+
EmptyStateHybrid.displayName = "EmptyState";
|
|
170
|
+
var EmptyStateHybrid_default = EmptyStateHybrid;
|
|
171
|
+
|
|
172
|
+
// src/EmptyState.tsx
|
|
173
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
174
|
+
var EmptyState = import_react3.default.forwardRef(
|
|
175
|
+
(props, ref) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(EmptyStateHybrid_default, { ...props, ref })
|
|
176
|
+
);
|
|
177
|
+
EmptyState.displayName = "EmptyState";
|
|
178
|
+
var EmptyState_default = (0, import_react3.memo)(EmptyState);
|
|
87
179
|
|
|
88
180
|
// src/EmptyStateTypes.ts
|
|
89
|
-
var
|
|
181
|
+
var React5 = require("react");
|
|
90
182
|
|
|
91
183
|
// src/index.ts
|
|
92
184
|
var index_default = EmptyState_default;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/EmptyState.tsx","../src/EmptyStateTypes.ts"],"sourcesContent":["import EmptyState from \"./EmptyState\";\n\nexport default EmptyState;\nexport { EmptyState };\nexport * from \"./EmptyStateTypes\";\n","import * as React from \"react\";\nimport Box from \"@sproutsocial/seeds-react-box\";\n// eslint-disable-next-line import/no-deprecated\nimport Text from \"@sproutsocial/seeds-react-text\";\nimport type { TypeEmptyStateProps } from \"./EmptyStateTypes\";\n\nconst
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/EmptyState.tsx","../src/EmptyStateHybrid.tsx","../src/EmptyStateStyled.tsx","../src/EmptyStateTailwind.tsx","../src/EmptyStateTypes.ts"],"sourcesContent":["import EmptyState from \"./EmptyState\";\n\nexport default EmptyState;\nexport { EmptyState };\nexport * from \"./EmptyStateTypes\";\n","import React, { memo } from \"react\";\n\nimport type { TypeEmptyStateProps } from \"./EmptyStateTypes\";\nimport EmptyStateHybrid from \"./EmptyStateHybrid\";\n\n/**\n * EmptyState component. Automatically routes between the Tailwind\n * implementation (preferred) and the styled-components implementation (for\n * consumers using styled-system props or a styled() extension).\n */\nconst EmptyState = React.forwardRef<HTMLDivElement, TypeEmptyStateProps>(\n (props, ref) => <EmptyStateHybrid {...props} ref={ref} />\n);\n\nEmptyState.displayName = \"EmptyState\";\n\nexport default memo(EmptyState);\n","/**\n * Hybrid EmptyState Component\n * Automatically chooses between Tailwind and styled-components based on props.\n *\n * Routes to the styled-components path when a styled-system prop is present\n * (hasStyledProps) or the component was extended with styled()\n * (isStyledExtension). Otherwise renders the Tailwind path.\n *\n * No mixin check is needed: EmptyState's root applies no standalone mixin\n * beyond the styled-system set, so hasStyledProps covers every routing prop.\n */\n\nimport React from \"react\";\nimport {\n hasStyledProps,\n isStyledExtension,\n} from \"@sproutsocial/seeds-react-system-props\";\n\nimport type { TypeEmptyStateProps } from \"./EmptyStateTypes\";\nimport EmptyStateStyled from \"./EmptyStateStyled\"; // Styled-components version\nimport { EmptyStateTailwind } from \"./EmptyStateTailwind\"; // Tailwind version\n\nconst EmptyStateHybrid = React.forwardRef<HTMLDivElement, TypeEmptyStateProps>(\n (props, ref) => {\n if (hasStyledProps(props) || isStyledExtension(props)) {\n return <EmptyStateStyled {...props} ref={ref} />;\n }\n\n // Use Tailwind version (preferred - better performance)\n return <EmptyStateTailwind {...props} ref={ref} />;\n }\n);\n\nEmptyStateHybrid.displayName = \"EmptyState\";\nexport default EmptyStateHybrid;\n","import * as React from \"react\";\nimport Box from \"@sproutsocial/seeds-react-box\";\n// eslint-disable-next-line import/no-deprecated\nimport Text from \"@sproutsocial/seeds-react-text\";\nimport type { TypeEmptyStateProps } from \"./EmptyStateTypes\";\n\nconst EmptyStateStyled = React.forwardRef<HTMLDivElement, TypeEmptyStateProps>(\n (\n { media, headline, subtext, primaryAction, secondaryAction, ...rest },\n ref\n ) => {\n return (\n <Box\n ref={ref}\n maxWidth=\"400px\"\n mx=\"auto\"\n data-qa-emptystate={headline}\n {...rest}\n >\n {media && (\n <Box\n display=\"flex\"\n flexDirection=\"column\"\n justifyContent=\"center\"\n mb={450}\n >\n {media}\n </Box>\n )}\n\n <Text as=\"div\" textAlign=\"center\">\n <Text\n as=\"p\"\n m={0}\n color=\"text.headline\"\n fontWeight=\"semibold\"\n fontSize={400}\n >\n {headline}\n </Text>\n\n {subtext && (\n <Text as=\"p\" color=\"text.subtext\" fontSize={200} mb={0} mt={400}>\n {subtext}\n </Text>\n )}\n\n <Box mt={primaryAction || secondaryAction ? 450 : 0}>\n {primaryAction &&\n React.cloneElement(primaryAction, {\n appearance: \"primary\",\n })}\n\n {secondaryAction && (\n <Box mt={400}>\n {React.cloneElement(secondaryAction, {\n appearance: \"unstyled\",\n })}\n </Box>\n )}\n </Box>\n </Text>\n </Box>\n );\n }\n);\n\nEmptyStateStyled.displayName = \"EmptyState\";\n\nexport default EmptyStateStyled;\n","/**\n * Tailwind CSS implementation of EmptyState component\n * Uses Seeds empty-state CSS classes from empty-state.css\n */\n\nimport React from \"react\";\n\nimport type { TypeEmptyStateProps } from \"./EmptyStateTypes\";\n\n// Utility for merging class names properly\nfunction cn(\n ...inputs: (string | undefined | null | false | Record<string, boolean>)[]\n): string {\n const classes: string[] = [];\n\n for (const input of inputs) {\n if (!input) continue;\n\n if (typeof input === \"string\") {\n classes.push(input);\n } else if (typeof input === \"object\") {\n for (const [key, value] of Object.entries(input)) {\n if (value) {\n classes.push(key);\n }\n }\n }\n }\n\n return classes.join(\" \");\n}\n\nexport const EmptyStateTailwind = React.forwardRef<\n HTMLDivElement,\n TypeEmptyStateProps\n>(\n (\n { media, headline, subtext, primaryAction, secondaryAction, className, ...rest },\n ref\n ) => {\n const hasAction = Boolean(primaryAction || secondaryAction);\n\n return (\n <div\n data-qa-emptystate={headline}\n {...(rest as React.HTMLAttributes<HTMLDivElement>)}\n ref={ref}\n className={cn(\"seeds-empty-state\", className)}\n >\n {media && <div className=\"seeds-empty-state-media\">{media}</div>}\n\n <div className=\"seeds-empty-state-content\">\n <p className=\"seeds-empty-state-headline\">{headline}</p>\n\n {subtext && <p className=\"seeds-empty-state-subtext\">{subtext}</p>}\n\n <div\n className={cn(\"seeds-empty-state-actions\", {\n \"seeds-empty-state-actions-empty\": !hasAction,\n })}\n >\n {primaryAction &&\n React.cloneElement(primaryAction, {\n appearance: \"primary\",\n })}\n\n {secondaryAction && (\n <div className=\"seeds-empty-state-secondary-action\">\n {React.cloneElement(secondaryAction, {\n appearance: \"unstyled\",\n })}\n </div>\n )}\n </div>\n </div>\n </div>\n );\n }\n);\n\nEmptyStateTailwind.displayName = \"EmptyStateTailwind\";\n","import * as React from \"react\";\nimport type { TypeBoxProps } from \"@sproutsocial/seeds-react-box\";\n\nexport interface TypeEmptyStateProps extends TypeBoxProps {\n /** An image in (preferably in SVG format) */\n media?: React.ReactNode;\n\n /** A headline describing the empty state */\n headline: React.ReactNode;\n\n /** Optional description of the empty state */\n subtext?: React.ReactNode;\n\n /** Element for the primary call-to-action */\n primaryAction?: React.ReactElement;\n\n /** Element for any secondary call-to-action */\n secondaryAction?: React.ReactElement;\n}\n"],"mappings":"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","names":["import_react","import_react","Box","Text","import_jsx_runtime","React","import_jsx_runtime","React","import_jsx_runtime","React","React"]}
|
package/package.json
CHANGED
|
@@ -1,15 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sproutsocial/seeds-react-empty-state",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.31",
|
|
4
4
|
"description": "Seeds React EmptyState",
|
|
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
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/esm/index.js",
|
|
14
|
+
"require": "./dist/index.js"
|
|
15
|
+
},
|
|
16
|
+
"./dist/empty-state.css": "./dist/empty-state.css"
|
|
17
|
+
},
|
|
10
18
|
"scripts": {
|
|
11
|
-
"build": "tsup --dts",
|
|
12
|
-
"build:debug": "tsup --dts --metafile",
|
|
19
|
+
"build": "tsup --dts && cp src/empty-state.css dist/empty-state.css",
|
|
20
|
+
"build:debug": "tsup --dts --metafile && cp src/empty-state.css dist/empty-state.css",
|
|
13
21
|
"dev": "tsup --watch --dts",
|
|
14
22
|
"clean": "rm -rf .turbo dist",
|
|
15
23
|
"clean:modules": "rm -rf node_modules",
|
|
@@ -35,7 +43,7 @@
|
|
|
35
43
|
"@sproutsocial/seeds-testing": "*",
|
|
36
44
|
"@sproutsocial/seeds-react-testing-library": "*",
|
|
37
45
|
"@sproutsocial/seeds-react-button": "^2.3.4",
|
|
38
|
-
"@sproutsocial/seeds-react-image": "^1.0.
|
|
46
|
+
"@sproutsocial/seeds-react-image": "^1.0.29"
|
|
39
47
|
},
|
|
40
48
|
"peerDependencies": {
|
|
41
49
|
"styled-components": "^5.2.3"
|
|
@@ -85,3 +85,22 @@ export const WithSubtextPrimaryActionAndSecondaryAction: Story = {
|
|
|
85
85
|
secondaryAction: <Button>I'll do this later</Button>,
|
|
86
86
|
},
|
|
87
87
|
};
|
|
88
|
+
|
|
89
|
+
// Renders via the default Tailwind path (no styled-system props), exercising
|
|
90
|
+
// the seeds-empty-state CSS classes end to end.
|
|
91
|
+
export const TailwindMode: Story = {
|
|
92
|
+
args: {
|
|
93
|
+
media: (
|
|
94
|
+
<Image
|
|
95
|
+
alt="No assets matching your search or filters"
|
|
96
|
+
src="https://cl.ly/db498c7682df/download/analytics.svg"
|
|
97
|
+
m={0}
|
|
98
|
+
/>
|
|
99
|
+
),
|
|
100
|
+
headline: "Ready to create your first topic?",
|
|
101
|
+
subtext:
|
|
102
|
+
"Now that you have the basics down, it’s time to create a topic. Remember to look for the icon at any time you are feeling lost.",
|
|
103
|
+
primaryAction: <Button>Create a Topic</Button>,
|
|
104
|
+
secondaryAction: <Button>I'll do this later</Button>,
|
|
105
|
+
},
|
|
106
|
+
};
|
package/src/EmptyState.tsx
CHANGED
|
@@ -1,64 +1,17 @@
|
|
|
1
|
-
import
|
|
2
|
-
import Box from "@sproutsocial/seeds-react-box";
|
|
3
|
-
// eslint-disable-next-line import/no-deprecated
|
|
4
|
-
import Text from "@sproutsocial/seeds-react-text";
|
|
5
|
-
import type { TypeEmptyStateProps } from "./EmptyStateTypes";
|
|
6
|
-
|
|
7
|
-
const EmptyState = ({
|
|
8
|
-
media,
|
|
9
|
-
headline,
|
|
10
|
-
subtext,
|
|
11
|
-
primaryAction,
|
|
12
|
-
secondaryAction,
|
|
13
|
-
...rest
|
|
14
|
-
}: TypeEmptyStateProps) => {
|
|
15
|
-
return (
|
|
16
|
-
<Box maxWidth="400px" mx="auto" data-qa-emptystate={headline} {...rest}>
|
|
17
|
-
{media && (
|
|
18
|
-
<Box
|
|
19
|
-
display="flex"
|
|
20
|
-
flexDirection="column"
|
|
21
|
-
justifyContent="center"
|
|
22
|
-
mb={450}
|
|
23
|
-
>
|
|
24
|
-
{media}
|
|
25
|
-
</Box>
|
|
26
|
-
)}
|
|
1
|
+
import React, { memo } from "react";
|
|
27
2
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
as="p"
|
|
31
|
-
m={0}
|
|
32
|
-
color="text.headline"
|
|
33
|
-
fontWeight="semibold"
|
|
34
|
-
fontSize={400}
|
|
35
|
-
>
|
|
36
|
-
{headline}
|
|
37
|
-
</Text>
|
|
38
|
-
|
|
39
|
-
{subtext && (
|
|
40
|
-
<Text as="p" color="text.subtext" fontSize={200} mb={0} mt={400}>
|
|
41
|
-
{subtext}
|
|
42
|
-
</Text>
|
|
43
|
-
)}
|
|
3
|
+
import type { TypeEmptyStateProps } from "./EmptyStateTypes";
|
|
4
|
+
import EmptyStateHybrid from "./EmptyStateHybrid";
|
|
44
5
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
6
|
+
/**
|
|
7
|
+
* EmptyState component. Automatically routes between the Tailwind
|
|
8
|
+
* implementation (preferred) and the styled-components implementation (for
|
|
9
|
+
* consumers using styled-system props or a styled() extension).
|
|
10
|
+
*/
|
|
11
|
+
const EmptyState = React.forwardRef<HTMLDivElement, TypeEmptyStateProps>(
|
|
12
|
+
(props, ref) => <EmptyStateHybrid {...props} ref={ref} />
|
|
13
|
+
);
|
|
50
14
|
|
|
51
|
-
|
|
52
|
-
<Box mt={400}>
|
|
53
|
-
{React.cloneElement(secondaryAction, {
|
|
54
|
-
appearance: "unstyled",
|
|
55
|
-
})}
|
|
56
|
-
</Box>
|
|
57
|
-
)}
|
|
58
|
-
</Box>
|
|
59
|
-
</Text>
|
|
60
|
-
</Box>
|
|
61
|
-
);
|
|
62
|
-
};
|
|
15
|
+
EmptyState.displayName = "EmptyState";
|
|
63
16
|
|
|
64
|
-
export default EmptyState;
|
|
17
|
+
export default memo(EmptyState);
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hybrid EmptyState Component
|
|
3
|
+
* Automatically chooses between Tailwind and styled-components based on props.
|
|
4
|
+
*
|
|
5
|
+
* Routes to the styled-components path when a styled-system prop is present
|
|
6
|
+
* (hasStyledProps) or the component was extended with styled()
|
|
7
|
+
* (isStyledExtension). Otherwise renders the Tailwind path.
|
|
8
|
+
*
|
|
9
|
+
* No mixin check is needed: EmptyState's root applies no standalone mixin
|
|
10
|
+
* beyond the styled-system set, so hasStyledProps covers every routing prop.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import React from "react";
|
|
14
|
+
import {
|
|
15
|
+
hasStyledProps,
|
|
16
|
+
isStyledExtension,
|
|
17
|
+
} from "@sproutsocial/seeds-react-system-props";
|
|
18
|
+
|
|
19
|
+
import type { TypeEmptyStateProps } from "./EmptyStateTypes";
|
|
20
|
+
import EmptyStateStyled from "./EmptyStateStyled"; // Styled-components version
|
|
21
|
+
import { EmptyStateTailwind } from "./EmptyStateTailwind"; // Tailwind version
|
|
22
|
+
|
|
23
|
+
const EmptyStateHybrid = React.forwardRef<HTMLDivElement, TypeEmptyStateProps>(
|
|
24
|
+
(props, ref) => {
|
|
25
|
+
if (hasStyledProps(props) || isStyledExtension(props)) {
|
|
26
|
+
return <EmptyStateStyled {...props} ref={ref} />;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Use Tailwind version (preferred - better performance)
|
|
30
|
+
return <EmptyStateTailwind {...props} ref={ref} />;
|
|
31
|
+
}
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
EmptyStateHybrid.displayName = "EmptyState";
|
|
35
|
+
export default EmptyStateHybrid;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import Box from "@sproutsocial/seeds-react-box";
|
|
3
|
+
// eslint-disable-next-line import/no-deprecated
|
|
4
|
+
import Text from "@sproutsocial/seeds-react-text";
|
|
5
|
+
import type { TypeEmptyStateProps } from "./EmptyStateTypes";
|
|
6
|
+
|
|
7
|
+
const EmptyStateStyled = React.forwardRef<HTMLDivElement, TypeEmptyStateProps>(
|
|
8
|
+
(
|
|
9
|
+
{ media, headline, subtext, primaryAction, secondaryAction, ...rest },
|
|
10
|
+
ref
|
|
11
|
+
) => {
|
|
12
|
+
return (
|
|
13
|
+
<Box
|
|
14
|
+
ref={ref}
|
|
15
|
+
maxWidth="400px"
|
|
16
|
+
mx="auto"
|
|
17
|
+
data-qa-emptystate={headline}
|
|
18
|
+
{...rest}
|
|
19
|
+
>
|
|
20
|
+
{media && (
|
|
21
|
+
<Box
|
|
22
|
+
display="flex"
|
|
23
|
+
flexDirection="column"
|
|
24
|
+
justifyContent="center"
|
|
25
|
+
mb={450}
|
|
26
|
+
>
|
|
27
|
+
{media}
|
|
28
|
+
</Box>
|
|
29
|
+
)}
|
|
30
|
+
|
|
31
|
+
<Text as="div" textAlign="center">
|
|
32
|
+
<Text
|
|
33
|
+
as="p"
|
|
34
|
+
m={0}
|
|
35
|
+
color="text.headline"
|
|
36
|
+
fontWeight="semibold"
|
|
37
|
+
fontSize={400}
|
|
38
|
+
>
|
|
39
|
+
{headline}
|
|
40
|
+
</Text>
|
|
41
|
+
|
|
42
|
+
{subtext && (
|
|
43
|
+
<Text as="p" color="text.subtext" fontSize={200} mb={0} mt={400}>
|
|
44
|
+
{subtext}
|
|
45
|
+
</Text>
|
|
46
|
+
)}
|
|
47
|
+
|
|
48
|
+
<Box mt={primaryAction || secondaryAction ? 450 : 0}>
|
|
49
|
+
{primaryAction &&
|
|
50
|
+
React.cloneElement(primaryAction, {
|
|
51
|
+
appearance: "primary",
|
|
52
|
+
})}
|
|
53
|
+
|
|
54
|
+
{secondaryAction && (
|
|
55
|
+
<Box mt={400}>
|
|
56
|
+
{React.cloneElement(secondaryAction, {
|
|
57
|
+
appearance: "unstyled",
|
|
58
|
+
})}
|
|
59
|
+
</Box>
|
|
60
|
+
)}
|
|
61
|
+
</Box>
|
|
62
|
+
</Text>
|
|
63
|
+
</Box>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
EmptyStateStyled.displayName = "EmptyState";
|
|
69
|
+
|
|
70
|
+
export default EmptyStateStyled;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tailwind CSS implementation of EmptyState component
|
|
3
|
+
* Uses Seeds empty-state CSS classes from empty-state.css
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React from "react";
|
|
7
|
+
|
|
8
|
+
import type { TypeEmptyStateProps } from "./EmptyStateTypes";
|
|
9
|
+
|
|
10
|
+
// Utility for merging class names properly
|
|
11
|
+
function cn(
|
|
12
|
+
...inputs: (string | undefined | null | false | Record<string, boolean>)[]
|
|
13
|
+
): string {
|
|
14
|
+
const classes: string[] = [];
|
|
15
|
+
|
|
16
|
+
for (const input of inputs) {
|
|
17
|
+
if (!input) continue;
|
|
18
|
+
|
|
19
|
+
if (typeof input === "string") {
|
|
20
|
+
classes.push(input);
|
|
21
|
+
} else if (typeof input === "object") {
|
|
22
|
+
for (const [key, value] of Object.entries(input)) {
|
|
23
|
+
if (value) {
|
|
24
|
+
classes.push(key);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return classes.join(" ");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const EmptyStateTailwind = React.forwardRef<
|
|
34
|
+
HTMLDivElement,
|
|
35
|
+
TypeEmptyStateProps
|
|
36
|
+
>(
|
|
37
|
+
(
|
|
38
|
+
{ media, headline, subtext, primaryAction, secondaryAction, className, ...rest },
|
|
39
|
+
ref
|
|
40
|
+
) => {
|
|
41
|
+
const hasAction = Boolean(primaryAction || secondaryAction);
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<div
|
|
45
|
+
data-qa-emptystate={headline}
|
|
46
|
+
{...(rest as React.HTMLAttributes<HTMLDivElement>)}
|
|
47
|
+
ref={ref}
|
|
48
|
+
className={cn("seeds-empty-state", className)}
|
|
49
|
+
>
|
|
50
|
+
{media && <div className="seeds-empty-state-media">{media}</div>}
|
|
51
|
+
|
|
52
|
+
<div className="seeds-empty-state-content">
|
|
53
|
+
<p className="seeds-empty-state-headline">{headline}</p>
|
|
54
|
+
|
|
55
|
+
{subtext && <p className="seeds-empty-state-subtext">{subtext}</p>}
|
|
56
|
+
|
|
57
|
+
<div
|
|
58
|
+
className={cn("seeds-empty-state-actions", {
|
|
59
|
+
"seeds-empty-state-actions-empty": !hasAction,
|
|
60
|
+
})}
|
|
61
|
+
>
|
|
62
|
+
{primaryAction &&
|
|
63
|
+
React.cloneElement(primaryAction, {
|
|
64
|
+
appearance: "primary",
|
|
65
|
+
})}
|
|
66
|
+
|
|
67
|
+
{secondaryAction && (
|
|
68
|
+
<div className="seeds-empty-state-secondary-action">
|
|
69
|
+
{React.cloneElement(secondaryAction, {
|
|
70
|
+
appearance: "unstyled",
|
|
71
|
+
})}
|
|
72
|
+
</div>
|
|
73
|
+
)}
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
EmptyStateTailwind.displayName = "EmptyStateTailwind";
|
|
@@ -122,4 +122,48 @@ describe("EmptyState", () => {
|
|
|
122
122
|
const element = screen.getByText("I'll do this later");
|
|
123
123
|
expect(element).toBeInTheDocument();
|
|
124
124
|
});
|
|
125
|
+
|
|
126
|
+
it("renders the Tailwind path with seeds classes and no sc- classes by default", () => {
|
|
127
|
+
render(<EmptyState headline="No content found" />);
|
|
128
|
+
|
|
129
|
+
const root = screen.getByDataQaLabel({ emptystate: "No content found" });
|
|
130
|
+
expect(root).toHaveClass("seeds-empty-state");
|
|
131
|
+
// The Tailwind path emits plain DOM with seeds classes only — no
|
|
132
|
+
// styled-components output (sc-*) should appear on the component's layout.
|
|
133
|
+
expect(root.className).not.toMatch(/\bsc-/);
|
|
134
|
+
expect(root.querySelector('[class*="sc-"]')).toBeNull();
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it("routes to the styled-components fallback when a styled-system prop is passed", () => {
|
|
138
|
+
render(<EmptyState headline="No content found" maxWidth="600px" />);
|
|
139
|
+
|
|
140
|
+
const root = screen.getByDataQaLabel({ emptystate: "No content found" });
|
|
141
|
+
// styled-components renders sc-* classes onto the root Box; the Tailwind
|
|
142
|
+
// path never does. Content stays identical either way.
|
|
143
|
+
expect(root.className).toMatch(/\bsc-/);
|
|
144
|
+
expect(root).not.toHaveClass("seeds-empty-state");
|
|
145
|
+
expect(screen.getByText("No content found")).toBeInTheDocument();
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it("injects action appearances via cloneElement in the default path", () => {
|
|
149
|
+
render(
|
|
150
|
+
<EmptyState
|
|
151
|
+
headline="Ready to create your first topic?"
|
|
152
|
+
primaryAction={<Button>Create a Topic</Button>}
|
|
153
|
+
secondaryAction={<Button>I'll do this later</Button>}
|
|
154
|
+
/>
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
// Both cloned actions still render in the Tailwind path.
|
|
158
|
+
expect(screen.getByText("Create a Topic")).toBeInTheDocument();
|
|
159
|
+
expect(screen.getByText("I'll do this later")).toBeInTheDocument();
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it("appends a consumer className on the Tailwind root", () => {
|
|
163
|
+
render(<EmptyState headline="No content found" className="my-custom-class" />);
|
|
164
|
+
|
|
165
|
+
const root = screen.getByDataQaLabel({ emptystate: "No content found" });
|
|
166
|
+
expect(root).toHaveClass("seeds-empty-state");
|
|
167
|
+
expect(root).toHaveClass("my-custom-class");
|
|
168
|
+
});
|
|
125
169
|
});
|
package/src/css.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
declare module "*.css";
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Seeds EmptyState component classes
|
|
3
|
+
* Use these instead of writing out individual Tailwind utility classes.
|
|
4
|
+
*
|
|
5
|
+
* Requires @sproutsocial/seeds-react-theme/dist/theme-all.css imported for
|
|
6
|
+
* CSS variable definitions and dark mode support.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* <div class="seeds-empty-state">...</div>
|
|
10
|
+
*
|
|
11
|
+
* Rules live in @layer components so that consumer Tailwind utilities
|
|
12
|
+
* (e.g. mx-200, p-300) win in the cascade and can override these component
|
|
13
|
+
* styles, rather than tying/beating them on specificity.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
@layer components {
|
|
17
|
+
.seeds-empty-state {
|
|
18
|
+
max-width: 400px;
|
|
19
|
+
margin-inline: auto;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.seeds-empty-state-media {
|
|
23
|
+
display: flex;
|
|
24
|
+
flex-direction: column;
|
|
25
|
+
justify-content: center;
|
|
26
|
+
margin-bottom: var(--space-450);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.seeds-empty-state-content {
|
|
30
|
+
text-align: center;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.seeds-empty-state-headline {
|
|
34
|
+
margin: 0;
|
|
35
|
+
padding-left: 0;
|
|
36
|
+
padding-right: 0;
|
|
37
|
+
font-family: var(--font-family);
|
|
38
|
+
color: var(--color-text-headline);
|
|
39
|
+
font-weight: var(--font-weight-semibold);
|
|
40
|
+
font-size: var(--font-size-400);
|
|
41
|
+
line-height: var(--line-height-400);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.seeds-empty-state-subtext {
|
|
45
|
+
margin: 0;
|
|
46
|
+
margin-top: var(--space-400);
|
|
47
|
+
padding-left: 0;
|
|
48
|
+
padding-right: 0;
|
|
49
|
+
font-family: var(--font-family);
|
|
50
|
+
color: var(--color-text-subtext);
|
|
51
|
+
font-weight: var(--font-weight-normal);
|
|
52
|
+
font-size: var(--font-size-200);
|
|
53
|
+
line-height: var(--line-height-200);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.seeds-empty-state-actions {
|
|
57
|
+
margin-top: var(--space-450);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/* -empty MUST follow the base rule: equal specificity, source order wins */
|
|
61
|
+
.seeds-empty-state-actions-empty {
|
|
62
|
+
margin-top: 0;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.seeds-empty-state-secondary-action {
|
|
66
|
+
margin-top: var(--space-400);
|
|
67
|
+
}
|
|
68
|
+
}
|