@sproutsocial/seeds-react-empty-state 1.0.30 → 1.0.33

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.
@@ -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
  CLI Building entry: src/index.ts
4
4
  CLI Using tsconfig: tsconfig.json
5
5
  CLI tsup v8.5.0
@@ -8,14 +8,14 @@ $ tsup --dts
8
8
  CLI Cleaning output folder
9
9
  CJS Build start
10
10
  ESM Build start
11
- CJS dist/index.js 3.67 KB
12
- CJS dist/index.js.map 3.41 KB
13
- CJS ⚡️ Build success in 17ms
14
- ESM dist/esm/index.js 1.66 KB
15
- ESM dist/esm/index.js.map 3.27 KB
16
- ESM ⚡️ Build success in 18ms
11
+ CJS dist/index.js 7.46 KB
12
+ CJS dist/index.js.map 9.63 KB
13
+ CJS ⚡️ Build success in 80ms
14
+ ESM dist/esm/index.js 5.01 KB
15
+ ESM dist/esm/index.js.map 9.53 KB
16
+ ESM ⚡️ Build success in 81ms
17
17
  DTS Build start
18
- DTS ⚡️ Build success in 4584ms
19
- DTS dist/index.d.ts 858.00 B
20
- DTS dist/index.d.mts 858.00 B
21
- Done in 6.21s.
18
+ DTS ⚡️ Build success in 4468ms
19
+ DTS dist/index.d.ts 878.00 B
20
+ DTS dist/index.d.mts 878.00 B
21
+ Done in 6.20s.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # @sproutsocial/seeds-react-empty-state
2
2
 
3
+ ## 1.0.33
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [3371484]
8
+ - @sproutsocial/seeds-react-theme@4.4.0
9
+ - @sproutsocial/seeds-react-box@1.1.30
10
+
11
+ ## 1.0.32
12
+
13
+ ### Patch Changes
14
+
15
+ - Updated dependencies [56661b6]
16
+ - @sproutsocial/seeds-react-system-props@3.2.0
17
+ - @sproutsocial/seeds-react-theme@4.3.2
18
+ - @sproutsocial/seeds-react-box@1.1.29
19
+ - @sproutsocial/seeds-react-text@1.5.4
20
+
21
+ ## 1.0.31
22
+
23
+ ### Patch Changes
24
+
25
+ - 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.
26
+
3
27
  ## 1.0.30
4
28
 
5
29
  ### 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,155 @@
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 EmptyState = ({
7
- media,
8
- headline,
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
- display: "flex",
19
- flexDirection: "column",
20
- justifyContent: "center",
21
- mb: 450,
22
- children: media
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
- /* @__PURE__ */ jsxs(Text, { as: "div", textAlign: "center", children: [
26
- /* @__PURE__ */ jsx(
27
- Text,
28
- {
29
- as: "p",
30
- m: 0,
31
- color: "text.headline",
32
- fontWeight: "semibold",
33
- fontSize: 400,
34
- children: headline
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
- subtext && /* @__PURE__ */ jsx(Text, { as: "p", color: "text.subtext", fontSize: 200, mb: 0, mt: 400, children: subtext }),
38
- /* @__PURE__ */ jsxs(Box, { mt: primaryAction || secondaryAction ? 450 : 0, children: [
39
- primaryAction && React.cloneElement(primaryAction, {
40
- appearance: "primary"
41
- }),
42
- secondaryAction && /* @__PURE__ */ jsx(Box, { mt: 400, children: React.cloneElement(secondaryAction, {
43
- appearance: "unstyled"
44
- }) })
45
- ] })
46
- ] })
47
- ] });
48
- };
49
- var EmptyState_default = EmptyState;
81
+ }
82
+ }
83
+ }
84
+ return classes.join(" ");
85
+ }
86
+ var EmptyStateTailwind = React2.forwardRef(
87
+ ({
88
+ media,
89
+ headline,
90
+ subtext,
91
+ primaryAction,
92
+ secondaryAction,
93
+ className,
94
+ ...rest
95
+ }, ref) => {
96
+ const hasAction = Boolean(primaryAction || secondaryAction);
97
+ return /* @__PURE__ */ jsxs2(
98
+ "div",
99
+ {
100
+ "data-qa-emptystate": headline,
101
+ ...rest,
102
+ ref,
103
+ className: cn("seeds-empty-state", className),
104
+ children: [
105
+ media && /* @__PURE__ */ jsx2("div", { className: "seeds-empty-state-media", children: media }),
106
+ /* @__PURE__ */ jsxs2("div", { className: "seeds-empty-state-content", children: [
107
+ /* @__PURE__ */ jsx2("p", { className: "seeds-empty-state-headline", children: headline }),
108
+ subtext && /* @__PURE__ */ jsx2("p", { className: "seeds-empty-state-subtext", children: subtext }),
109
+ /* @__PURE__ */ jsxs2(
110
+ "div",
111
+ {
112
+ className: cn("seeds-empty-state-actions", {
113
+ "seeds-empty-state-actions-empty": !hasAction
114
+ }),
115
+ children: [
116
+ primaryAction && React2.cloneElement(primaryAction, {
117
+ appearance: "primary"
118
+ }),
119
+ secondaryAction && /* @__PURE__ */ jsx2("div", { className: "seeds-empty-state-secondary-action", children: React2.cloneElement(secondaryAction, {
120
+ appearance: "unstyled"
121
+ }) })
122
+ ]
123
+ }
124
+ )
125
+ ] })
126
+ ]
127
+ }
128
+ );
129
+ }
130
+ );
131
+ EmptyStateTailwind.displayName = "EmptyStateTailwind";
132
+
133
+ // src/EmptyStateHybrid.tsx
134
+ import { jsx as jsx3 } from "react/jsx-runtime";
135
+ var EmptyStateHybrid = React3.forwardRef(
136
+ (props, ref) => {
137
+ if (hasStyledProps(props) || isStyledExtension(props)) {
138
+ return /* @__PURE__ */ jsx3(EmptyStateStyled_default, { ...props, ref });
139
+ }
140
+ return /* @__PURE__ */ jsx3(EmptyStateTailwind, { ...props, ref });
141
+ }
142
+ );
143
+ EmptyStateHybrid.displayName = "EmptyState";
144
+ var EmptyStateHybrid_default = EmptyStateHybrid;
145
+
146
+ // src/EmptyState.tsx
147
+ import { jsx as jsx4 } from "react/jsx-runtime";
148
+ var EmptyState = React4.forwardRef(
149
+ (props, ref) => /* @__PURE__ */ jsx4(EmptyStateHybrid_default, { ...props, ref })
150
+ );
151
+ EmptyState.displayName = "EmptyState";
152
+ var EmptyState_default = memo(EmptyState);
50
153
 
51
154
  // src/EmptyStateTypes.ts
52
155
  import "react";
@@ -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 EmptyState = ({\n media,\n headline,\n subtext,\n primaryAction,\n secondaryAction,\n ...rest\n}: TypeEmptyStateProps) => {\n return (\n <Box maxWidth=\"400px\" mx=\"auto\" data-qa-emptystate={headline} {...rest}>\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\nexport default EmptyState;\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":";AAAA,YAAY,WAAW;AACvB,OAAO,SAAS;AAEhB,OAAO,UAAU;AAcT,cA2BA,YA3BA;AAXR,IAAM,aAAa,CAAC;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,MAA2B;AACzB,SACE,qBAAC,OAAI,UAAS,SAAQ,IAAG,QAAO,sBAAoB,UAAW,GAAG,MAC/D;AAAA,aACC;AAAA,MAAC;AAAA;AAAA,QACC,SAAQ;AAAA,QACR,eAAc;AAAA,QACd,gBAAe;AAAA,QACf,IAAI;AAAA,QAEH;AAAA;AAAA,IACH;AAAA,IAGF,qBAAC,QAAK,IAAG,OAAM,WAAU,UACvB;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,IAAG;AAAA,UACH,GAAG;AAAA,UACH,OAAM;AAAA,UACN,YAAW;AAAA,UACX,UAAU;AAAA,UAET;AAAA;AAAA,MACH;AAAA,MAEC,WACC,oBAAC,QAAK,IAAG,KAAI,OAAM,gBAAe,UAAU,KAAK,IAAI,GAAG,IAAI,KACzD,mBACH;AAAA,MAGF,qBAAC,OAAI,IAAI,iBAAiB,kBAAkB,MAAM,GAC/C;AAAA,yBACO,mBAAa,eAAe;AAAA,UAChC,YAAY;AAAA,QACd,CAAC;AAAA,QAEF,mBACC,oBAAC,OAAI,IAAI,KACN,UAAM,mBAAa,iBAAiB;AAAA,UACnC,YAAY;AAAA,QACd,CAAC,GACH;AAAA,SAEJ;AAAA,OACF;AAAA,KACF;AAEJ;AAEA,IAAO,qBAAQ;;;AC/Df,OAAuB;;;ACEvB,IAAO,gBAAQ;","names":[]}
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 {\n media,\n headline,\n subtext,\n primaryAction,\n secondaryAction,\n className,\n ...rest\n },\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 EmptyState: ({ media, headline, subtext, primaryAction, secondaryAction, ...rest }: TypeEmptyStateProps) => react_jsx_runtime.JSX.Element;
18
+ declare const _default: React__default.MemoExoticComponent<React__default.ForwardRefExoticComponent<Omit<TypeEmptyStateProps, "ref"> & React__default.RefAttributes<HTMLDivElement>>>;
19
19
 
20
- export { EmptyState, type TypeEmptyStateProps, EmptyState as default };
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 EmptyState: ({ media, headline, subtext, primaryAction, secondaryAction, ...rest }: TypeEmptyStateProps) => react_jsx_runtime.JSX.Element;
18
+ declare const _default: React__default.MemoExoticComponent<React__default.ForwardRefExoticComponent<Omit<TypeEmptyStateProps, "ref"> & React__default.RefAttributes<HTMLDivElement>>>;
19
19
 
20
- export { EmptyState, type TypeEmptyStateProps, EmptyState as default };
20
+ export { _default as EmptyState, type TypeEmptyStateProps, _default as default };
package/dist/index.js CHANGED
@@ -36,57 +36,157 @@ __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 EmptyState = ({
44
- media,
45
- headline,
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
- display: "flex",
56
- flexDirection: "column",
57
- justifyContent: "center",
58
- mb: 450,
59
- children: media
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
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_seeds_react_text.default, { as: "div", textAlign: "center", children: [
63
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
64
- import_seeds_react_text.default,
65
- {
66
- as: "p",
67
- m: 0,
68
- color: "text.headline",
69
- fontWeight: "semibold",
70
- fontSize: 400,
71
- children: headline
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
- 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 }),
75
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_seeds_react_box.default, { mt: primaryAction || secondaryAction ? 450 : 0, children: [
76
- primaryAction && React.cloneElement(primaryAction, {
77
- appearance: "primary"
78
- }),
79
- secondaryAction && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_seeds_react_box.default, { mt: 400, children: React.cloneElement(secondaryAction, {
80
- appearance: "unstyled"
81
- }) })
82
- ] })
83
- ] })
84
- ] });
85
- };
86
- var EmptyState_default = EmptyState;
115
+ }
116
+ }
117
+ }
118
+ return classes.join(" ");
119
+ }
120
+ var EmptyStateTailwind = import_react.default.forwardRef(
121
+ ({
122
+ media,
123
+ headline,
124
+ subtext,
125
+ primaryAction,
126
+ secondaryAction,
127
+ className,
128
+ ...rest
129
+ }, ref) => {
130
+ const hasAction = Boolean(primaryAction || secondaryAction);
131
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
132
+ "div",
133
+ {
134
+ "data-qa-emptystate": headline,
135
+ ...rest,
136
+ ref,
137
+ className: cn("seeds-empty-state", className),
138
+ children: [
139
+ media && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "seeds-empty-state-media", children: media }),
140
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "seeds-empty-state-content", children: [
141
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "seeds-empty-state-headline", children: headline }),
142
+ subtext && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "seeds-empty-state-subtext", children: subtext }),
143
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
144
+ "div",
145
+ {
146
+ className: cn("seeds-empty-state-actions", {
147
+ "seeds-empty-state-actions-empty": !hasAction
148
+ }),
149
+ children: [
150
+ primaryAction && import_react.default.cloneElement(primaryAction, {
151
+ appearance: "primary"
152
+ }),
153
+ secondaryAction && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "seeds-empty-state-secondary-action", children: import_react.default.cloneElement(secondaryAction, {
154
+ appearance: "unstyled"
155
+ }) })
156
+ ]
157
+ }
158
+ )
159
+ ] })
160
+ ]
161
+ }
162
+ );
163
+ }
164
+ );
165
+ EmptyStateTailwind.displayName = "EmptyStateTailwind";
166
+
167
+ // src/EmptyStateHybrid.tsx
168
+ var import_jsx_runtime3 = require("react/jsx-runtime");
169
+ var EmptyStateHybrid = import_react2.default.forwardRef(
170
+ (props, ref) => {
171
+ if ((0, import_seeds_react_system_props.hasStyledProps)(props) || (0, import_seeds_react_system_props.isStyledExtension)(props)) {
172
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(EmptyStateStyled_default, { ...props, ref });
173
+ }
174
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(EmptyStateTailwind, { ...props, ref });
175
+ }
176
+ );
177
+ EmptyStateHybrid.displayName = "EmptyState";
178
+ var EmptyStateHybrid_default = EmptyStateHybrid;
179
+
180
+ // src/EmptyState.tsx
181
+ var import_jsx_runtime4 = require("react/jsx-runtime");
182
+ var EmptyState = import_react3.default.forwardRef(
183
+ (props, ref) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(EmptyStateHybrid_default, { ...props, ref })
184
+ );
185
+ EmptyState.displayName = "EmptyState";
186
+ var EmptyState_default = (0, import_react3.memo)(EmptyState);
87
187
 
88
188
  // src/EmptyStateTypes.ts
89
- var React2 = require("react");
189
+ var React5 = require("react");
90
190
 
91
191
  // src/index.ts
92
192
  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 EmptyState = ({\n media,\n headline,\n subtext,\n primaryAction,\n secondaryAction,\n ...rest\n}: TypeEmptyStateProps) => {\n return (\n <Box maxWidth=\"400px\" mx=\"auto\" data-qa-emptystate={headline} {...rest}>\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\nexport default EmptyState;\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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,YAAuB;AACvB,6BAAgB;AAEhB,8BAAiB;AAcT;AAXR,IAAM,aAAa,CAAC;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,MAA2B;AACzB,SACE,6CAAC,uBAAAA,SAAA,EAAI,UAAS,SAAQ,IAAG,QAAO,sBAAoB,UAAW,GAAG,MAC/D;AAAA,aACC;AAAA,MAAC,uBAAAA;AAAA,MAAA;AAAA,QACC,SAAQ;AAAA,QACR,eAAc;AAAA,QACd,gBAAe;AAAA,QACf,IAAI;AAAA,QAEH;AAAA;AAAA,IACH;AAAA,IAGF,6CAAC,wBAAAC,SAAA,EAAK,IAAG,OAAM,WAAU,UACvB;AAAA;AAAA,QAAC,wBAAAA;AAAA,QAAA;AAAA,UACC,IAAG;AAAA,UACH,GAAG;AAAA,UACH,OAAM;AAAA,UACN,YAAW;AAAA,UACX,UAAU;AAAA,UAET;AAAA;AAAA,MACH;AAAA,MAEC,WACC,4CAAC,wBAAAA,SAAA,EAAK,IAAG,KAAI,OAAM,gBAAe,UAAU,KAAK,IAAI,GAAG,IAAI,KACzD,mBACH;AAAA,MAGF,6CAAC,uBAAAD,SAAA,EAAI,IAAI,iBAAiB,kBAAkB,MAAM,GAC/C;AAAA,yBACO,mBAAa,eAAe;AAAA,UAChC,YAAY;AAAA,QACd,CAAC;AAAA,QAEF,mBACC,4CAAC,uBAAAA,SAAA,EAAI,IAAI,KACN,UAAM,mBAAa,iBAAiB;AAAA,UACnC,YAAY;AAAA,QACd,CAAC,GACH;AAAA,SAEJ;AAAA,OACF;AAAA,KACF;AAEJ;AAEA,IAAO,qBAAQ;;;AC/Df,IAAAE,SAAuB;;;AFEvB,IAAO,gBAAQ;","names":["Box","Text","React"]}
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 {\n media,\n headline,\n subtext,\n primaryAction,\n secondaryAction,\n className,\n ...rest\n },\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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,IAAAA,gBAA4B;;;ACY5B,IAAAC,gBAAkB;AAClB,sCAGO;;;AChBP,YAAuB;AACvB,6BAAgB;AAEhB,8BAAiB;AAiBP;AAdV,IAAM,mBAAyB;AAAA,EAC7B,CACE,EAAE,OAAO,UAAU,SAAS,eAAe,iBAAiB,GAAG,KAAK,GACpE,QACG;AACH,WACE;AAAA,MAAC,uBAAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,UAAS;AAAA,QACT,IAAG;AAAA,QACH,sBAAoB;AAAA,QACnB,GAAG;AAAA,QAEH;AAAA,mBACC;AAAA,YAAC,uBAAAA;AAAA,YAAA;AAAA,cACC,SAAQ;AAAA,cACR,eAAc;AAAA,cACd,gBAAe;AAAA,cACf,IAAI;AAAA,cAEH;AAAA;AAAA,UACH;AAAA,UAGF,6CAAC,wBAAAC,SAAA,EAAK,IAAG,OAAM,WAAU,UACvB;AAAA;AAAA,cAAC,wBAAAA;AAAA,cAAA;AAAA,gBACC,IAAG;AAAA,gBACH,GAAG;AAAA,gBACH,OAAM;AAAA,gBACN,YAAW;AAAA,gBACX,UAAU;AAAA,gBAET;AAAA;AAAA,YACH;AAAA,YAEC,WACC,4CAAC,wBAAAA,SAAA,EAAK,IAAG,KAAI,OAAM,gBAAe,UAAU,KAAK,IAAI,GAAG,IAAI,KACzD,mBACH;AAAA,YAGF,6CAAC,uBAAAD,SAAA,EAAI,IAAI,iBAAiB,kBAAkB,MAAM,GAC/C;AAAA,+BACO,mBAAa,eAAe;AAAA,gBAChC,YAAY;AAAA,cACd,CAAC;AAAA,cAEF,mBACC,4CAAC,uBAAAA,SAAA,EAAI,IAAI,KACN,UAAM,mBAAa,iBAAiB;AAAA,gBACnC,YAAY;AAAA,cACd,CAAC,GACH;AAAA,eAEJ;AAAA,aACF;AAAA;AAAA;AAAA,IACF;AAAA,EAEJ;AACF;AAEA,iBAAiB,cAAc;AAE/B,IAAO,2BAAQ;;;AChEf,mBAAkB;AAoDA,IAAAE,sBAAA;AA/ClB,SAAS,MACJ,QACK;AACR,QAAM,UAAoB,CAAC;AAE3B,aAAW,SAAS,QAAQ;AAC1B,QAAI,CAAC,MAAO;AAEZ,QAAI,OAAO,UAAU,UAAU;AAC7B,cAAQ,KAAK,KAAK;AAAA,IACpB,WAAW,OAAO,UAAU,UAAU;AACpC,iBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AAChD,YAAI,OAAO;AACT,kBAAQ,KAAK,GAAG;AAAA,QAClB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO,QAAQ,KAAK,GAAG;AACzB;AAEO,IAAM,qBAAqB,aAAAC,QAAM;AAAA,EAItC,CACE;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG;AAAA,EACL,GACA,QACG;AACH,UAAM,YAAY,QAAQ,iBAAiB,eAAe;AAE1D,WACE;AAAA,MAAC;AAAA;AAAA,QACC,sBAAoB;AAAA,QACnB,GAAI;AAAA,QACL;AAAA,QACA,WAAW,GAAG,qBAAqB,SAAS;AAAA,QAE3C;AAAA,mBAAS,6CAAC,SAAI,WAAU,2BAA2B,iBAAM;AAAA,UAE1D,8CAAC,SAAI,WAAU,6BACb;AAAA,yDAAC,OAAE,WAAU,8BAA8B,oBAAS;AAAA,YAEnD,WAAW,6CAAC,OAAE,WAAU,6BAA6B,mBAAQ;AAAA,YAE9D;AAAA,cAAC;AAAA;AAAA,gBACC,WAAW,GAAG,6BAA6B;AAAA,kBACzC,mCAAmC,CAAC;AAAA,gBACtC,CAAC;AAAA,gBAEA;AAAA,mCACC,aAAAA,QAAM,aAAa,eAAe;AAAA,oBAChC,YAAY;AAAA,kBACd,CAAC;AAAA,kBAEF,mBACC,6CAAC,SAAI,WAAU,sCACZ,uBAAAA,QAAM,aAAa,iBAAiB;AAAA,oBACnC,YAAY;AAAA,kBACd,CAAC,GACH;AAAA;AAAA;AAAA,YAEJ;AAAA,aACF;AAAA;AAAA;AAAA,IACF;AAAA,EAEJ;AACF;AAEA,mBAAmB,cAAc;;;AF/DpB,IAAAC,sBAAA;AAHb,IAAM,mBAAmB,cAAAC,QAAM;AAAA,EAC7B,CAAC,OAAO,QAAQ;AACd,YAAI,gDAAe,KAAK,SAAK,mDAAkB,KAAK,GAAG;AACrD,aAAO,6CAAC,4BAAkB,GAAG,OAAO,KAAU;AAAA,IAChD;AAGA,WAAO,6CAAC,sBAAoB,GAAG,OAAO,KAAU;AAAA,EAClD;AACF;AAEA,iBAAiB,cAAc;AAC/B,IAAO,2BAAQ;;;ADvBG,IAAAC,sBAAA;AADlB,IAAM,aAAa,cAAAC,QAAM;AAAA,EACvB,CAAC,OAAO,QAAQ,6CAAC,4BAAkB,GAAG,OAAO,KAAU;AACzD;AAEA,WAAW,cAAc;AAEzB,IAAO,yBAAQ,oBAAK,UAAU;;;AIhB9B,IAAAC,SAAuB;;;ALEvB,IAAO,gBAAQ;","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.30",
3
+ "version": "1.0.33",
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",
@@ -18,10 +26,10 @@
18
26
  "test:watch": "jest --watch --coverage=false"
19
27
  },
20
28
  "dependencies": {
21
- "@sproutsocial/seeds-react-theme": "^4.3.1",
22
- "@sproutsocial/seeds-react-system-props": "^3.1.4",
23
- "@sproutsocial/seeds-react-box": "^1.1.28",
24
- "@sproutsocial/seeds-react-text": "^1.5.3"
29
+ "@sproutsocial/seeds-react-theme": "^4.4.0",
30
+ "@sproutsocial/seeds-react-system-props": "^3.2.0",
31
+ "@sproutsocial/seeds-react-box": "^1.1.30",
32
+ "@sproutsocial/seeds-react-text": "^1.5.4"
25
33
  },
26
34
  "devDependencies": {
27
35
  "@types/react": "^18.0.0",
@@ -34,8 +42,8 @@
34
42
  "@sproutsocial/seeds-tsconfig": "*",
35
43
  "@sproutsocial/seeds-testing": "*",
36
44
  "@sproutsocial/seeds-react-testing-library": "*",
37
- "@sproutsocial/seeds-react-button": "^2.3.4",
38
- "@sproutsocial/seeds-react-image": "^1.0.28"
45
+ "@sproutsocial/seeds-react-button": "^2.3.6",
46
+ "@sproutsocial/seeds-react-image": "^1.0.31"
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
+ };
@@ -1,64 +1,17 @@
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 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
- <Text as="div" textAlign="center">
29
- <Text
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
- <Box mt={primaryAction || secondaryAction ? 450 : 0}>
46
- {primaryAction &&
47
- React.cloneElement(primaryAction, {
48
- appearance: "primary",
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
- {secondaryAction && (
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,89 @@
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
+ {
39
+ media,
40
+ headline,
41
+ subtext,
42
+ primaryAction,
43
+ secondaryAction,
44
+ className,
45
+ ...rest
46
+ },
47
+ ref
48
+ ) => {
49
+ const hasAction = Boolean(primaryAction || secondaryAction);
50
+
51
+ return (
52
+ <div
53
+ data-qa-emptystate={headline}
54
+ {...(rest as React.HTMLAttributes<HTMLDivElement>)}
55
+ ref={ref}
56
+ className={cn("seeds-empty-state", className)}
57
+ >
58
+ {media && <div className="seeds-empty-state-media">{media}</div>}
59
+
60
+ <div className="seeds-empty-state-content">
61
+ <p className="seeds-empty-state-headline">{headline}</p>
62
+
63
+ {subtext && <p className="seeds-empty-state-subtext">{subtext}</p>}
64
+
65
+ <div
66
+ className={cn("seeds-empty-state-actions", {
67
+ "seeds-empty-state-actions-empty": !hasAction,
68
+ })}
69
+ >
70
+ {primaryAction &&
71
+ React.cloneElement(primaryAction, {
72
+ appearance: "primary",
73
+ })}
74
+
75
+ {secondaryAction && (
76
+ <div className="seeds-empty-state-secondary-action">
77
+ {React.cloneElement(secondaryAction, {
78
+ appearance: "unstyled",
79
+ })}
80
+ </div>
81
+ )}
82
+ </div>
83
+ </div>
84
+ </div>
85
+ );
86
+ }
87
+ );
88
+
89
+ EmptyStateTailwind.displayName = "EmptyStateTailwind";
@@ -122,4 +122,50 @@ 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&apos;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(
164
+ <EmptyState headline="No content found" className="my-custom-class" />
165
+ );
166
+
167
+ const root = screen.getByDataQaLabel({ emptystate: "No content found" });
168
+ expect(root).toHaveClass("seeds-empty-state");
169
+ expect(root).toHaveClass("my-custom-class");
170
+ });
125
171
  });
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
+ }