@fanvue/ui 2.9.2 → 2.9.3

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.
@@ -4,6 +4,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
4
4
  const jsxRuntime = require("react/jsx-runtime");
5
5
  const React = require("react");
6
6
  const cn = require("../../utils/cn.cjs");
7
+ const Button = require("../Button/Button.cjs");
7
8
  function _interopNamespaceDefault(e) {
8
9
  const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
9
10
  if (e) {
@@ -21,6 +22,18 @@ function _interopNamespaceDefault(e) {
21
22
  return Object.freeze(n);
22
23
  }
23
24
  const React__namespace = /* @__PURE__ */ _interopNamespaceDefault(React);
25
+ function isNonEmptyString(value) {
26
+ return typeof value === "string" && value.length > 0;
27
+ }
28
+ function hasSlotContent(value) {
29
+ if (value === void 0 || value === null || value === false) {
30
+ return false;
31
+ }
32
+ if (typeof value === "string") {
33
+ return value.length > 0;
34
+ }
35
+ return true;
36
+ }
24
37
  const EmptyState = React__namespace.forwardRef(
25
38
  ({
26
39
  className,
@@ -34,7 +47,29 @@ const EmptyState = React__namespace.forwardRef(
34
47
  }, ref) => {
35
48
  const isCentered = variant === "centered";
36
49
  const titleId = React__namespace.useId();
37
- const regionLabelledBy = title !== void 0 && title !== null && title !== false ? titleId : void 0;
50
+ const regionLabelledBy = hasSlotContent(title) ? titleId : void 0;
51
+ const renderedPrimary = primaryAction === void 0 || primaryAction === null || primaryAction === false || primaryAction === "" ? null : isNonEmptyString(primaryAction) ? /* @__PURE__ */ jsxRuntime.jsx(Button.Button, { variant: "brand", fullWidth: true, children: primaryAction }) : primaryAction;
52
+ const renderedSecondary = secondaryAction === void 0 || secondaryAction === null || secondaryAction === false || secondaryAction === "" ? null : isNonEmptyString(secondaryAction) ? /* @__PURE__ */ jsxRuntime.jsx(Button.Button, { variant: "secondary", fullWidth: true, children: secondaryAction }) : secondaryAction;
53
+ const renderedTitle = title === void 0 || title === null || title === false || title === "" ? null : isNonEmptyString(title) ? /* @__PURE__ */ jsxRuntime.jsx("h2", { id: titleId, className: "m-0 typography-bold-heading-lg text-content-primary", children: title }) : /* @__PURE__ */ jsxRuntime.jsx(
54
+ "div",
55
+ {
56
+ id: titleId,
57
+ className: "typography-bold-heading-lg text-content-primary min-w-0 w-full",
58
+ children: title
59
+ }
60
+ );
61
+ const renderedDescription = description === void 0 || description === null || description === false || description === "" ? null : isNonEmptyString(description) ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "m-0 typography-regular-body-lg text-content-secondary", children: description }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "typography-regular-body-lg text-content-secondary min-w-0 w-full", children: description });
62
+ const renderedMedia = media === void 0 || media === null || media === false || media === "" ? null : isNonEmptyString(media) ? /* @__PURE__ */ jsxRuntime.jsx(
63
+ "img",
64
+ {
65
+ src: media,
66
+ alt: "",
67
+ decoding: "async",
68
+ className: "block h-full w-full object-contain object-center"
69
+ }
70
+ ) : media;
71
+ const showMedia = renderedMedia !== null;
72
+ const showActions = renderedPrimary !== null || renderedSecondary !== null;
38
73
  return /* @__PURE__ */ jsxRuntime.jsxs(
39
74
  "section",
40
75
  {
@@ -48,7 +83,7 @@ const EmptyState = React__namespace.forwardRef(
48
83
  ),
49
84
  ...props,
50
85
  children: [
51
- media !== void 0 && media !== null && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-[280px] w-full overflow-hidden rounded-md", children: media }),
86
+ showMedia && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-[280px] w-full overflow-hidden rounded-md", children: renderedMedia }),
52
87
  /* @__PURE__ */ jsxRuntime.jsxs(
53
88
  "div",
54
89
  {
@@ -62,14 +97,14 @@ const EmptyState = React__namespace.forwardRef(
62
97
  isCentered ? "items-center" : "items-start"
63
98
  ),
64
99
  children: [
65
- title !== void 0 && title !== null && title !== false && /* @__PURE__ */ jsxRuntime.jsx("div", { id: titleId, className: "typography-bold-heading-lg text-content-primary", children: title }),
66
- description !== void 0 && description !== null && description !== false && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "typography-regular-body-lg text-content-secondary", children: description })
100
+ renderedTitle,
101
+ renderedDescription
67
102
  ]
68
103
  }
69
104
  ),
70
- primaryAction !== void 0 && primaryAction !== null || secondaryAction !== void 0 && secondaryAction !== null ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn.cn("flex flex-col gap-4", isCentered ? "items-center" : "items-start"), children: [
71
- primaryAction,
72
- secondaryAction
105
+ showActions ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn.cn("flex flex-col gap-4", isCentered ? "items-center" : "items-start"), children: [
106
+ renderedPrimary,
107
+ renderedSecondary
73
108
  ] }) : null
74
109
  ]
75
110
  }
@@ -1 +1 @@
1
- {"version":3,"file":"EmptyState.cjs","sources":["../../../../src/components/EmptyState/EmptyState.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\nexport type EmptyStateVariant = \"default\" | \"centered\";\n\nexport interface EmptyStateProps extends Omit<React.HTMLAttributes<HTMLElement>, \"title\"> {\n /**\n * Matches Figma property `Property 1` (`Default` / `centered`).\n * @default \"default\"\n */\n variant?: EmptyStateVariant;\n /** Main heading. */\n title?: React.ReactNode;\n /** Supporting body copy. */\n description?: React.ReactNode;\n /** Top visual/illustration slot. */\n media?: React.ReactNode;\n /** Primary call-to-action node. */\n primaryAction?: React.ReactNode;\n /** Optional secondary action rendered below primary. */\n secondaryAction?: React.ReactNode;\n}\n\nexport const EmptyState = React.forwardRef<HTMLElement, EmptyStateProps>(\n (\n {\n className,\n variant = \"default\",\n title,\n description,\n media,\n primaryAction,\n secondaryAction,\n ...props\n },\n ref,\n ) => {\n const isCentered = variant === \"centered\";\n const titleId = React.useId();\n const regionLabelledBy =\n title !== undefined && title !== null && title !== false ? titleId : undefined;\n\n return (\n <section\n ref={ref}\n aria-labelledby={regionLabelledBy}\n data-testid=\"empty-state\"\n className={cn(\n \"flex w-full max-w-[375px] flex-col gap-6\",\n isCentered ? \"items-center text-center\" : \"items-start text-left\",\n className,\n )}\n {...props}\n >\n {media !== undefined && media !== null && (\n <div className=\"h-[280px] w-full overflow-hidden rounded-md\">{media}</div>\n )}\n\n <div\n className={cn(\"flex w-full flex-col gap-6\", isCentered ? \"items-center\" : \"items-start\")}\n >\n <div\n className={cn(\n \"flex w-full flex-col gap-4\",\n isCentered ? \"items-center\" : \"items-start\",\n )}\n >\n {title !== undefined && title !== null && title !== false && (\n <div id={titleId} className=\"typography-bold-heading-lg text-content-primary\">\n {title}\n </div>\n )}\n {description !== undefined && description !== null && description !== false && (\n <p className=\"typography-regular-body-lg text-content-secondary\">{description}</p>\n )}\n </div>\n\n {(primaryAction !== undefined && primaryAction !== null) ||\n (secondaryAction !== undefined && secondaryAction !== null) ? (\n <div className={cn(\"flex flex-col gap-4\", isCentered ? \"items-center\" : \"items-start\")}>\n {primaryAction}\n {secondaryAction}\n </div>\n ) : null}\n </div>\n </section>\n );\n },\n);\n\nEmptyState.displayName = \"EmptyState\";\n"],"names":["React","jsxs","cn","jsx"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAuBO,MAAM,aAAaA,iBAAM;AAAA,EAC9B,CACE;AAAA,IACE;AAAA,IACA,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG;AAAA,EAAA,GAEL,QACG;AACH,UAAM,aAAa,YAAY;AAC/B,UAAM,UAAUA,iBAAM,MAAA;AACtB,UAAM,mBACJ,UAAU,UAAa,UAAU,QAAQ,UAAU,QAAQ,UAAU;AAEvE,WACEC,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,mBAAiB;AAAA,QACjB,eAAY;AAAA,QACZ,WAAWC,GAAAA;AAAAA,UACT;AAAA,UACA,aAAa,6BAA6B;AAAA,UAC1C;AAAA,QAAA;AAAA,QAED,GAAG;AAAA,QAEH,UAAA;AAAA,UAAA,UAAU,UAAa,UAAU,uCAC/B,OAAA,EAAI,WAAU,+CAA+C,UAAA,MAAA,CAAM;AAAA,UAGtED,2BAAAA;AAAAA,YAAC;AAAA,YAAA;AAAA,cACC,WAAWC,GAAAA,GAAG,8BAA8B,aAAa,iBAAiB,aAAa;AAAA,cAEvF,UAAA;AAAA,gBAAAD,2BAAAA;AAAAA,kBAAC;AAAA,kBAAA;AAAA,oBACC,WAAWC,GAAAA;AAAAA,sBACT;AAAA,sBACA,aAAa,iBAAiB;AAAA,oBAAA;AAAA,oBAG/B,UAAA;AAAA,sBAAA,UAAU,UAAa,UAAU,QAAQ,UAAU,SAClDC,2BAAAA,IAAC,OAAA,EAAI,IAAI,SAAS,WAAU,mDACzB,UAAA,OACH;AAAA,sBAED,gBAAgB,UAAa,gBAAgB,QAAQ,gBAAgB,SACpEA,+BAAC,KAAA,EAAE,WAAU,qDAAqD,UAAA,YAAA,CAAY;AAAA,oBAAA;AAAA,kBAAA;AAAA,gBAAA;AAAA,gBAIhF,kBAAkB,UAAa,kBAAkB,QAClD,oBAAoB,UAAa,oBAAoB,OACpDF,2BAAAA,KAAC,OAAA,EAAI,WAAWC,GAAAA,GAAG,uBAAuB,aAAa,iBAAiB,aAAa,GAClF,UAAA;AAAA,kBAAA;AAAA,kBACA;AAAA,gBAAA,EAAA,CACH,IACE;AAAA,cAAA;AAAA,YAAA;AAAA,UAAA;AAAA,QACN;AAAA,MAAA;AAAA,IAAA;AAAA,EAGN;AACF;AAEA,WAAW,cAAc;;"}
1
+ {"version":3,"file":"EmptyState.cjs","sources":["../../../../src/components/EmptyState/EmptyState.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { Button } from \"../Button/Button\";\n\nexport type EmptyStateVariant = \"default\" | \"centered\";\n\n/** Slot that can be plain copy (styled by `EmptyState`) or custom markup. */\nexport type EmptyStateSlot = string | React.ReactNode;\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === \"string\" && value.length > 0;\n}\n\nfunction hasSlotContent(value: EmptyStateSlot | undefined): boolean {\n if (value === undefined || value === null || value === false) {\n return false;\n }\n if (typeof value === \"string\") {\n return value.length > 0;\n }\n return true;\n}\n\nexport interface EmptyStateProps extends Omit<React.HTMLAttributes<HTMLElement>, \"title\"> {\n /**\n * Matches Figma property `Property 1` (`Default` / `centered`).\n * @default \"default\"\n */\n variant?: EmptyStateVariant;\n /** Main heading. Strings use library heading styles; pass a node for full control. */\n title?: EmptyStateSlot;\n /** Supporting body copy. Strings use library body styles; pass a node for rich text. */\n description?: EmptyStateSlot;\n /**\n * Top visual / illustration slot.\n * A string is treated as an image URL (`<img src={…}>`); pass a node for custom layout.\n */\n media?: EmptyStateSlot;\n /**\n * Primary call to action.\n * A string renders a brand `Button` with that label; pass a node for links, loading, etc.\n */\n primaryAction?: EmptyStateSlot;\n /**\n * Secondary action below the primary.\n * A string renders a secondary `Button` with that label; pass a node when you need more control.\n */\n secondaryAction?: EmptyStateSlot;\n}\n\nexport const EmptyState = React.forwardRef<HTMLElement, EmptyStateProps>(\n (\n {\n className,\n variant = \"default\",\n title,\n description,\n media,\n primaryAction,\n secondaryAction,\n ...props\n },\n ref,\n ) => {\n const isCentered = variant === \"centered\";\n const titleId = React.useId();\n const regionLabelledBy = hasSlotContent(title) ? titleId : undefined;\n\n const renderedPrimary =\n primaryAction === undefined ||\n primaryAction === null ||\n primaryAction === false ||\n primaryAction === \"\" ? null : isNonEmptyString(primaryAction) ? (\n <Button variant=\"brand\" fullWidth>\n {primaryAction}\n </Button>\n ) : (\n primaryAction\n );\n\n const renderedSecondary =\n secondaryAction === undefined ||\n secondaryAction === null ||\n secondaryAction === false ||\n secondaryAction === \"\" ? null : isNonEmptyString(secondaryAction) ? (\n <Button variant=\"secondary\" fullWidth>\n {secondaryAction}\n </Button>\n ) : (\n secondaryAction\n );\n\n const renderedTitle =\n title === undefined ||\n title === null ||\n title === false ||\n title === \"\" ? null : isNonEmptyString(title) ? (\n <h2 id={titleId} className=\"m-0 typography-bold-heading-lg text-content-primary\">\n {title}\n </h2>\n ) : (\n <div\n id={titleId}\n className=\"typography-bold-heading-lg text-content-primary min-w-0 w-full\"\n >\n {title}\n </div>\n );\n\n const renderedDescription =\n description === undefined ||\n description === null ||\n description === false ||\n description === \"\" ? null : isNonEmptyString(description) ? (\n <p className=\"m-0 typography-regular-body-lg text-content-secondary\">{description}</p>\n ) : (\n <div className=\"typography-regular-body-lg text-content-secondary min-w-0 w-full\">\n {description}\n </div>\n );\n\n const renderedMedia =\n media === undefined ||\n media === null ||\n media === false ||\n media === \"\" ? null : isNonEmptyString(media) ? (\n <img\n src={media}\n alt=\"\"\n decoding=\"async\"\n className=\"block h-full w-full object-contain object-center\"\n />\n ) : (\n media\n );\n\n const showMedia = renderedMedia !== null;\n const showActions = renderedPrimary !== null || renderedSecondary !== null;\n\n return (\n <section\n ref={ref}\n aria-labelledby={regionLabelledBy}\n data-testid=\"empty-state\"\n className={cn(\n \"flex w-full max-w-[375px] flex-col gap-6\",\n isCentered ? \"items-center text-center\" : \"items-start text-left\",\n className,\n )}\n {...props}\n >\n {showMedia && (\n <div className=\"h-[280px] w-full overflow-hidden rounded-md\">{renderedMedia}</div>\n )}\n\n <div\n className={cn(\"flex w-full flex-col gap-6\", isCentered ? \"items-center\" : \"items-start\")}\n >\n <div\n className={cn(\n \"flex w-full flex-col gap-4\",\n isCentered ? \"items-center\" : \"items-start\",\n )}\n >\n {renderedTitle}\n {renderedDescription}\n </div>\n\n {showActions ? (\n <div className={cn(\"flex flex-col gap-4\", isCentered ? \"items-center\" : \"items-start\")}>\n {renderedPrimary}\n {renderedSecondary}\n </div>\n ) : null}\n </div>\n </section>\n );\n },\n);\n\nEmptyState.displayName = \"EmptyState\";\n"],"names":["React","Button","jsx","jsxs","cn"],"mappings":"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"}
@@ -1,7 +1,20 @@
1
1
  "use client";
2
- import { jsxs, jsx } from "react/jsx-runtime";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
4
  import { cn } from "../../utils/cn.mjs";
5
+ import { Button } from "../Button/Button.mjs";
6
+ function isNonEmptyString(value) {
7
+ return typeof value === "string" && value.length > 0;
8
+ }
9
+ function hasSlotContent(value) {
10
+ if (value === void 0 || value === null || value === false) {
11
+ return false;
12
+ }
13
+ if (typeof value === "string") {
14
+ return value.length > 0;
15
+ }
16
+ return true;
17
+ }
5
18
  const EmptyState = React.forwardRef(
6
19
  ({
7
20
  className,
@@ -15,7 +28,29 @@ const EmptyState = React.forwardRef(
15
28
  }, ref) => {
16
29
  const isCentered = variant === "centered";
17
30
  const titleId = React.useId();
18
- const regionLabelledBy = title !== void 0 && title !== null && title !== false ? titleId : void 0;
31
+ const regionLabelledBy = hasSlotContent(title) ? titleId : void 0;
32
+ const renderedPrimary = primaryAction === void 0 || primaryAction === null || primaryAction === false || primaryAction === "" ? null : isNonEmptyString(primaryAction) ? /* @__PURE__ */ jsx(Button, { variant: "brand", fullWidth: true, children: primaryAction }) : primaryAction;
33
+ const renderedSecondary = secondaryAction === void 0 || secondaryAction === null || secondaryAction === false || secondaryAction === "" ? null : isNonEmptyString(secondaryAction) ? /* @__PURE__ */ jsx(Button, { variant: "secondary", fullWidth: true, children: secondaryAction }) : secondaryAction;
34
+ const renderedTitle = title === void 0 || title === null || title === false || title === "" ? null : isNonEmptyString(title) ? /* @__PURE__ */ jsx("h2", { id: titleId, className: "m-0 typography-bold-heading-lg text-content-primary", children: title }) : /* @__PURE__ */ jsx(
35
+ "div",
36
+ {
37
+ id: titleId,
38
+ className: "typography-bold-heading-lg text-content-primary min-w-0 w-full",
39
+ children: title
40
+ }
41
+ );
42
+ const renderedDescription = description === void 0 || description === null || description === false || description === "" ? null : isNonEmptyString(description) ? /* @__PURE__ */ jsx("p", { className: "m-0 typography-regular-body-lg text-content-secondary", children: description }) : /* @__PURE__ */ jsx("div", { className: "typography-regular-body-lg text-content-secondary min-w-0 w-full", children: description });
43
+ const renderedMedia = media === void 0 || media === null || media === false || media === "" ? null : isNonEmptyString(media) ? /* @__PURE__ */ jsx(
44
+ "img",
45
+ {
46
+ src: media,
47
+ alt: "",
48
+ decoding: "async",
49
+ className: "block h-full w-full object-contain object-center"
50
+ }
51
+ ) : media;
52
+ const showMedia = renderedMedia !== null;
53
+ const showActions = renderedPrimary !== null || renderedSecondary !== null;
19
54
  return /* @__PURE__ */ jsxs(
20
55
  "section",
21
56
  {
@@ -29,7 +64,7 @@ const EmptyState = React.forwardRef(
29
64
  ),
30
65
  ...props,
31
66
  children: [
32
- media !== void 0 && media !== null && /* @__PURE__ */ jsx("div", { className: "h-[280px] w-full overflow-hidden rounded-md", children: media }),
67
+ showMedia && /* @__PURE__ */ jsx("div", { className: "h-[280px] w-full overflow-hidden rounded-md", children: renderedMedia }),
33
68
  /* @__PURE__ */ jsxs(
34
69
  "div",
35
70
  {
@@ -43,14 +78,14 @@ const EmptyState = React.forwardRef(
43
78
  isCentered ? "items-center" : "items-start"
44
79
  ),
45
80
  children: [
46
- title !== void 0 && title !== null && title !== false && /* @__PURE__ */ jsx("div", { id: titleId, className: "typography-bold-heading-lg text-content-primary", children: title }),
47
- description !== void 0 && description !== null && description !== false && /* @__PURE__ */ jsx("p", { className: "typography-regular-body-lg text-content-secondary", children: description })
81
+ renderedTitle,
82
+ renderedDescription
48
83
  ]
49
84
  }
50
85
  ),
51
- primaryAction !== void 0 && primaryAction !== null || secondaryAction !== void 0 && secondaryAction !== null ? /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-4", isCentered ? "items-center" : "items-start"), children: [
52
- primaryAction,
53
- secondaryAction
86
+ showActions ? /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-4", isCentered ? "items-center" : "items-start"), children: [
87
+ renderedPrimary,
88
+ renderedSecondary
54
89
  ] }) : null
55
90
  ]
56
91
  }
@@ -1 +1 @@
1
- {"version":3,"file":"EmptyState.mjs","sources":["../../../src/components/EmptyState/EmptyState.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\nexport type EmptyStateVariant = \"default\" | \"centered\";\n\nexport interface EmptyStateProps extends Omit<React.HTMLAttributes<HTMLElement>, \"title\"> {\n /**\n * Matches Figma property `Property 1` (`Default` / `centered`).\n * @default \"default\"\n */\n variant?: EmptyStateVariant;\n /** Main heading. */\n title?: React.ReactNode;\n /** Supporting body copy. */\n description?: React.ReactNode;\n /** Top visual/illustration slot. */\n media?: React.ReactNode;\n /** Primary call-to-action node. */\n primaryAction?: React.ReactNode;\n /** Optional secondary action rendered below primary. */\n secondaryAction?: React.ReactNode;\n}\n\nexport const EmptyState = React.forwardRef<HTMLElement, EmptyStateProps>(\n (\n {\n className,\n variant = \"default\",\n title,\n description,\n media,\n primaryAction,\n secondaryAction,\n ...props\n },\n ref,\n ) => {\n const isCentered = variant === \"centered\";\n const titleId = React.useId();\n const regionLabelledBy =\n title !== undefined && title !== null && title !== false ? titleId : undefined;\n\n return (\n <section\n ref={ref}\n aria-labelledby={regionLabelledBy}\n data-testid=\"empty-state\"\n className={cn(\n \"flex w-full max-w-[375px] flex-col gap-6\",\n isCentered ? \"items-center text-center\" : \"items-start text-left\",\n className,\n )}\n {...props}\n >\n {media !== undefined && media !== null && (\n <div className=\"h-[280px] w-full overflow-hidden rounded-md\">{media}</div>\n )}\n\n <div\n className={cn(\"flex w-full flex-col gap-6\", isCentered ? \"items-center\" : \"items-start\")}\n >\n <div\n className={cn(\n \"flex w-full flex-col gap-4\",\n isCentered ? \"items-center\" : \"items-start\",\n )}\n >\n {title !== undefined && title !== null && title !== false && (\n <div id={titleId} className=\"typography-bold-heading-lg text-content-primary\">\n {title}\n </div>\n )}\n {description !== undefined && description !== null && description !== false && (\n <p className=\"typography-regular-body-lg text-content-secondary\">{description}</p>\n )}\n </div>\n\n {(primaryAction !== undefined && primaryAction !== null) ||\n (secondaryAction !== undefined && secondaryAction !== null) ? (\n <div className={cn(\"flex flex-col gap-4\", isCentered ? \"items-center\" : \"items-start\")}>\n {primaryAction}\n {secondaryAction}\n </div>\n ) : null}\n </div>\n </section>\n );\n },\n);\n\nEmptyState.displayName = \"EmptyState\";\n"],"names":[],"mappings":";;;;AAuBO,MAAM,aAAa,MAAM;AAAA,EAC9B,CACE;AAAA,IACE;AAAA,IACA,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG;AAAA,EAAA,GAEL,QACG;AACH,UAAM,aAAa,YAAY;AAC/B,UAAM,UAAU,MAAM,MAAA;AACtB,UAAM,mBACJ,UAAU,UAAa,UAAU,QAAQ,UAAU,QAAQ,UAAU;AAEvE,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,mBAAiB;AAAA,QACjB,eAAY;AAAA,QACZ,WAAW;AAAA,UACT;AAAA,UACA,aAAa,6BAA6B;AAAA,UAC1C;AAAA,QAAA;AAAA,QAED,GAAG;AAAA,QAEH,UAAA;AAAA,UAAA,UAAU,UAAa,UAAU,4BAC/B,OAAA,EAAI,WAAU,+CAA+C,UAAA,MAAA,CAAM;AAAA,UAGtE;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,WAAW,GAAG,8BAA8B,aAAa,iBAAiB,aAAa;AAAA,cAEvF,UAAA;AAAA,gBAAA;AAAA,kBAAC;AAAA,kBAAA;AAAA,oBACC,WAAW;AAAA,sBACT;AAAA,sBACA,aAAa,iBAAiB;AAAA,oBAAA;AAAA,oBAG/B,UAAA;AAAA,sBAAA,UAAU,UAAa,UAAU,QAAQ,UAAU,SAClD,oBAAC,OAAA,EAAI,IAAI,SAAS,WAAU,mDACzB,UAAA,OACH;AAAA,sBAED,gBAAgB,UAAa,gBAAgB,QAAQ,gBAAgB,SACpE,oBAAC,KAAA,EAAE,WAAU,qDAAqD,UAAA,YAAA,CAAY;AAAA,oBAAA;AAAA,kBAAA;AAAA,gBAAA;AAAA,gBAIhF,kBAAkB,UAAa,kBAAkB,QAClD,oBAAoB,UAAa,oBAAoB,OACpD,qBAAC,OAAA,EAAI,WAAW,GAAG,uBAAuB,aAAa,iBAAiB,aAAa,GAClF,UAAA;AAAA,kBAAA;AAAA,kBACA;AAAA,gBAAA,EAAA,CACH,IACE;AAAA,cAAA;AAAA,YAAA;AAAA,UAAA;AAAA,QACN;AAAA,MAAA;AAAA,IAAA;AAAA,EAGN;AACF;AAEA,WAAW,cAAc;"}
1
+ {"version":3,"file":"EmptyState.mjs","sources":["../../../src/components/EmptyState/EmptyState.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { Button } from \"../Button/Button\";\n\nexport type EmptyStateVariant = \"default\" | \"centered\";\n\n/** Slot that can be plain copy (styled by `EmptyState`) or custom markup. */\nexport type EmptyStateSlot = string | React.ReactNode;\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === \"string\" && value.length > 0;\n}\n\nfunction hasSlotContent(value: EmptyStateSlot | undefined): boolean {\n if (value === undefined || value === null || value === false) {\n return false;\n }\n if (typeof value === \"string\") {\n return value.length > 0;\n }\n return true;\n}\n\nexport interface EmptyStateProps extends Omit<React.HTMLAttributes<HTMLElement>, \"title\"> {\n /**\n * Matches Figma property `Property 1` (`Default` / `centered`).\n * @default \"default\"\n */\n variant?: EmptyStateVariant;\n /** Main heading. Strings use library heading styles; pass a node for full control. */\n title?: EmptyStateSlot;\n /** Supporting body copy. Strings use library body styles; pass a node for rich text. */\n description?: EmptyStateSlot;\n /**\n * Top visual / illustration slot.\n * A string is treated as an image URL (`<img src={…}>`); pass a node for custom layout.\n */\n media?: EmptyStateSlot;\n /**\n * Primary call to action.\n * A string renders a brand `Button` with that label; pass a node for links, loading, etc.\n */\n primaryAction?: EmptyStateSlot;\n /**\n * Secondary action below the primary.\n * A string renders a secondary `Button` with that label; pass a node when you need more control.\n */\n secondaryAction?: EmptyStateSlot;\n}\n\nexport const EmptyState = React.forwardRef<HTMLElement, EmptyStateProps>(\n (\n {\n className,\n variant = \"default\",\n title,\n description,\n media,\n primaryAction,\n secondaryAction,\n ...props\n },\n ref,\n ) => {\n const isCentered = variant === \"centered\";\n const titleId = React.useId();\n const regionLabelledBy = hasSlotContent(title) ? titleId : undefined;\n\n const renderedPrimary =\n primaryAction === undefined ||\n primaryAction === null ||\n primaryAction === false ||\n primaryAction === \"\" ? null : isNonEmptyString(primaryAction) ? (\n <Button variant=\"brand\" fullWidth>\n {primaryAction}\n </Button>\n ) : (\n primaryAction\n );\n\n const renderedSecondary =\n secondaryAction === undefined ||\n secondaryAction === null ||\n secondaryAction === false ||\n secondaryAction === \"\" ? null : isNonEmptyString(secondaryAction) ? (\n <Button variant=\"secondary\" fullWidth>\n {secondaryAction}\n </Button>\n ) : (\n secondaryAction\n );\n\n const renderedTitle =\n title === undefined ||\n title === null ||\n title === false ||\n title === \"\" ? null : isNonEmptyString(title) ? (\n <h2 id={titleId} className=\"m-0 typography-bold-heading-lg text-content-primary\">\n {title}\n </h2>\n ) : (\n <div\n id={titleId}\n className=\"typography-bold-heading-lg text-content-primary min-w-0 w-full\"\n >\n {title}\n </div>\n );\n\n const renderedDescription =\n description === undefined ||\n description === null ||\n description === false ||\n description === \"\" ? null : isNonEmptyString(description) ? (\n <p className=\"m-0 typography-regular-body-lg text-content-secondary\">{description}</p>\n ) : (\n <div className=\"typography-regular-body-lg text-content-secondary min-w-0 w-full\">\n {description}\n </div>\n );\n\n const renderedMedia =\n media === undefined ||\n media === null ||\n media === false ||\n media === \"\" ? null : isNonEmptyString(media) ? (\n <img\n src={media}\n alt=\"\"\n decoding=\"async\"\n className=\"block h-full w-full object-contain object-center\"\n />\n ) : (\n media\n );\n\n const showMedia = renderedMedia !== null;\n const showActions = renderedPrimary !== null || renderedSecondary !== null;\n\n return (\n <section\n ref={ref}\n aria-labelledby={regionLabelledBy}\n data-testid=\"empty-state\"\n className={cn(\n \"flex w-full max-w-[375px] flex-col gap-6\",\n isCentered ? \"items-center text-center\" : \"items-start text-left\",\n className,\n )}\n {...props}\n >\n {showMedia && (\n <div className=\"h-[280px] w-full overflow-hidden rounded-md\">{renderedMedia}</div>\n )}\n\n <div\n className={cn(\"flex w-full flex-col gap-6\", isCentered ? \"items-center\" : \"items-start\")}\n >\n <div\n className={cn(\n \"flex w-full flex-col gap-4\",\n isCentered ? \"items-center\" : \"items-start\",\n )}\n >\n {renderedTitle}\n {renderedDescription}\n </div>\n\n {showActions ? (\n <div className={cn(\"flex flex-col gap-4\", isCentered ? \"items-center\" : \"items-start\")}>\n {renderedPrimary}\n {renderedSecondary}\n </div>\n ) : null}\n </div>\n </section>\n );\n },\n);\n\nEmptyState.displayName = \"EmptyState\";\n"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1354,18 +1354,30 @@ export declare interface EmptyStateProps extends Omit<React_2.HTMLAttributes<HTM
1354
1354
  * @default "default"
1355
1355
  */
1356
1356
  variant?: EmptyStateVariant;
1357
- /** Main heading. */
1358
- title?: React_2.ReactNode;
1359
- /** Supporting body copy. */
1360
- description?: React_2.ReactNode;
1361
- /** Top visual/illustration slot. */
1362
- media?: React_2.ReactNode;
1363
- /** Primary call-to-action node. */
1364
- primaryAction?: React_2.ReactNode;
1365
- /** Optional secondary action rendered below primary. */
1366
- secondaryAction?: React_2.ReactNode;
1357
+ /** Main heading. Strings use library heading styles; pass a node for full control. */
1358
+ title?: EmptyStateSlot;
1359
+ /** Supporting body copy. Strings use library body styles; pass a node for rich text. */
1360
+ description?: EmptyStateSlot;
1361
+ /**
1362
+ * Top visual / illustration slot.
1363
+ * A string is treated as an image URL (`<img src={…}>`); pass a node for custom layout.
1364
+ */
1365
+ media?: EmptyStateSlot;
1366
+ /**
1367
+ * Primary call to action.
1368
+ * A string renders a brand `Button` with that label; pass a node for links, loading, etc.
1369
+ */
1370
+ primaryAction?: EmptyStateSlot;
1371
+ /**
1372
+ * Secondary action below the primary.
1373
+ * A string renders a secondary `Button` with that label; pass a node when you need more control.
1374
+ */
1375
+ secondaryAction?: EmptyStateSlot;
1367
1376
  }
1368
1377
 
1378
+ /** Slot that can be plain copy (styled by `EmptyState`) or custom markup. */
1379
+ export declare type EmptyStateSlot = string | React_2.ReactNode;
1380
+
1369
1381
  export declare type EmptyStateVariant = "default" | "centered";
1370
1382
 
1371
1383
  /** An "×" inside a filled circle icon for error states (20 × 20). */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fanvue/ui",
3
- "version": "2.9.2",
3
+ "version": "2.9.3",
4
4
  "description": "React component library built with Tailwind CSS for Fanvue ecosystem",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org",