@fanvue/ui 3.18.0 → 3.19.0

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.
@@ -24,19 +24,20 @@ function _interopNamespaceDefault(e) {
24
24
  const React__namespace = /* @__PURE__ */ _interopNamespaceDefault(React);
25
25
  const badgeVariants = {
26
26
  variant: {
27
- default: "bg-neutral-alphas-50 text-content-primary",
27
+ default: "bg-buttons-secondary-default text-content-primary",
28
28
  dark: "bg-neutral-alphas-600 text-content-always-white",
29
- success: "bg-success-surface text-success-content",
30
- warning: "bg-warning-surface text-warning-content",
31
- error: "bg-error-surface text-error-content",
32
- special: "bg-neutral-alphas-50 text-content-primary",
33
- info: "bg-info-surface text-info-content",
29
+ success: "bg-buttons-secondary-default text-content-primary",
30
+ warning: "bg-buttons-secondary-default text-content-primary",
31
+ error: "bg-buttons-secondary-default text-content-primary",
32
+ special: "bg-buttons-secondary-default text-content-primary",
33
+ info: "bg-buttons-secondary-default text-content-primary",
34
34
  successColour: "bg-success-surface text-content-primary",
35
35
  warningColour: "bg-warning-surface text-content-primary",
36
36
  errorColour: "bg-error-surface text-content-primary",
37
37
  infoColour: "bg-info-surface text-content-primary",
38
38
  aiGenerated: "bg-buttons-always-white-default text-content-always-black",
39
39
  negative: "bg-buttons-secondary-negative-default text-content-primary-inverted",
40
+ alwaysWhite: "bg-buttons-secondary-negative-default text-content-always-white",
40
41
  online: "bg-background-primary text-brand-primary-default",
41
42
  brand: "bg-brand-primary-default text-content-always-black",
42
43
  pink: "bg-brand-secondary-default text-content-always-black",
@@ -44,7 +45,7 @@ const badgeVariants = {
44
45
  pinkLight: "bg-brand-secondary-muted text-content-primary"
45
46
  },
46
47
  dotColor: {
47
- default: "bg-content-primary",
48
+ default: "bg-icons-primary",
48
49
  dark: "bg-content-always-white",
49
50
  success: "bg-success-content",
50
51
  warning: "bg-warning-content",
@@ -57,6 +58,7 @@ const badgeVariants = {
57
58
  infoColour: "bg-info-content",
58
59
  aiGenerated: "bg-content-always-black",
59
60
  negative: "bg-icons-primary-inverted",
61
+ alwaysWhite: "bg-brand-primary-default",
60
62
  online: "bg-brand-primary-default",
61
63
  brand: "bg-content-always-black",
62
64
  pink: "bg-content-always-black",
@@ -84,7 +86,7 @@ const Badge = React__namespace.forwardRef(
84
86
  "data-testid": "badge",
85
87
  className: cn.cn(
86
88
  // Base styles
87
- "typography-description-12px-semibold inline-flex h-6 min-w-0 items-center gap-2 rounded-full px-2",
89
+ "typography-badge-badgecaps inline-flex h-5 min-w-0 items-center gap-1 rounded-full px-2",
88
90
  // Variant styles
89
91
  badgeVariants.variant[variant],
90
92
  // Interactive
@@ -98,7 +100,7 @@ const Badge = React__namespace.forwardRef(
98
100
  leftIcon && /* @__PURE__ */ jsxRuntime.jsx(
99
101
  "span",
100
102
  {
101
- className: "flex shrink-0 items-center justify-center [&>svg]:size-3",
103
+ className: "flex shrink-0 items-center justify-center [&>svg]:size-2.5",
102
104
  "aria-hidden": "true",
103
105
  children: leftIcon
104
106
  }
@@ -110,11 +112,11 @@ const Badge = React__namespace.forwardRef(
110
112
  "aria-hidden": "true"
111
113
  }
112
114
  ),
113
- asChild ? /* @__PURE__ */ jsxRuntime.jsx(reactSlot.Slottable, { children }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 truncate", children }),
115
+ asChild ? /* @__PURE__ */ jsxRuntime.jsx(reactSlot.Slottable, { children }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 truncate [text-box:trim-both_cap_alphabetic]", children }),
114
116
  rightIcon && /* @__PURE__ */ jsxRuntime.jsx(
115
117
  "span",
116
118
  {
117
- className: "flex shrink-0 items-center justify-center [&>svg]:size-3",
119
+ className: "flex shrink-0 items-center justify-center [&>svg]:size-2.5",
118
120
  "aria-hidden": "true",
119
121
  children: rightIcon
120
122
  }
@@ -1 +1 @@
1
- {"version":3,"file":"Badge.cjs","sources":["../../../../src/components/Badge/Badge.tsx"],"sourcesContent":["import { Slot, Slottable } from \"@radix-ui/react-slot\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\nconst badgeVariants = {\n variant: {\n default: \"bg-neutral-alphas-50 text-content-primary\",\n dark: \"bg-neutral-alphas-600 text-content-always-white\",\n success: \"bg-success-surface text-success-content\",\n warning: \"bg-warning-surface text-warning-content\",\n error: \"bg-error-surface text-error-content\",\n special: \"bg-neutral-alphas-50 text-content-primary\",\n info: \"bg-info-surface text-info-content\",\n successColour: \"bg-success-surface text-content-primary\",\n warningColour: \"bg-warning-surface text-content-primary\",\n errorColour: \"bg-error-surface text-content-primary\",\n infoColour: \"bg-info-surface text-content-primary\",\n aiGenerated: \"bg-buttons-always-white-default text-content-always-black\",\n negative: \"bg-buttons-secondary-negative-default text-content-primary-inverted\",\n online: \"bg-background-primary text-brand-primary-default\",\n brand: \"bg-brand-primary-default text-content-always-black\",\n pink: \"bg-brand-secondary-default text-content-always-black\",\n brandLight: \"bg-brand-primary-muted text-content-primary\",\n pinkLight: \"bg-brand-secondary-muted text-content-primary\",\n },\n dotColor: {\n default: \"bg-content-primary\",\n dark: \"bg-content-always-white\",\n success: \"bg-success-content\",\n warning: \"bg-warning-content\",\n error: \"bg-error-content\",\n special: \"bg-special-default\",\n info: \"bg-info-content\",\n successColour: \"bg-success-content\",\n warningColour: \"bg-warning-content\",\n errorColour: \"bg-error-content\",\n infoColour: \"bg-info-content\",\n aiGenerated: \"bg-content-always-black\",\n negative: \"bg-icons-primary-inverted\",\n online: \"bg-brand-primary-default\",\n brand: \"bg-content-always-black\",\n pink: \"bg-content-always-black\",\n brandLight: \"bg-content-always-black\",\n pinkLight: \"bg-content-always-black\",\n },\n} as const;\n\n/** Visual style variant of the badge. */\nexport type BadgeVariant =\n | \"default\"\n | \"dark\"\n | \"success\"\n | \"warning\"\n | \"error\"\n | \"special\"\n | \"info\"\n | \"successColour\"\n | \"warningColour\"\n | \"errorColour\"\n | \"infoColour\"\n | \"aiGenerated\"\n | \"negative\"\n | \"online\"\n | \"brand\"\n | \"pink\"\n | \"brandLight\"\n | \"pinkLight\";\n\nexport interface BadgeProps extends React.HTMLAttributes<HTMLSpanElement> {\n /** Visual style variant of the badge. @default \"default\" */\n variant?: BadgeVariant;\n /** Whether to show a coloured status dot at the leading edge. @default true */\n leftDot?: boolean;\n /** Icon element displayed before the label. */\n leftIcon?: React.ReactNode;\n /** Icon element displayed after the label. */\n rightIcon?: React.ReactNode;\n /** Merge props onto a child element instead of rendering a `<span>`. @default false */\n asChild?: boolean;\n}\n\n/**\n * A small inline label for status, category, or metadata information.\n *\n * @example\n * ```tsx\n * <Badge variant=\"success\">Active</Badge>\n * ```\n */\nexport const Badge = React.forwardRef<HTMLSpanElement, BadgeProps>(\n (\n {\n className,\n variant = \"default\",\n leftDot = true,\n leftIcon,\n rightIcon,\n asChild = false,\n onClick,\n children,\n ...props\n },\n ref,\n ) => {\n const Comp = asChild ? Slot : \"span\";\n\n return (\n <Comp\n ref={ref}\n data-testid=\"badge\"\n className={cn(\n // Base styles\n \"typography-description-12px-semibold inline-flex h-6 min-w-0 items-center gap-2 rounded-full px-2\",\n // Variant styles\n badgeVariants.variant[variant],\n // Interactive\n onClick && \"cursor-pointer\",\n // Manual CSS overrides\n className,\n )}\n onClick={onClick}\n {...props}\n >\n {leftIcon && (\n <span\n className=\"flex shrink-0 items-center justify-center [&>svg]:size-3\"\n aria-hidden=\"true\"\n >\n {leftIcon}\n </span>\n )}\n {leftDot && (\n <span\n className={cn(\"size-1 shrink-0 rounded-full\", badgeVariants.dotColor[variant])}\n aria-hidden=\"true\"\n />\n )}\n {asChild ? (\n <Slottable>{children}</Slottable>\n ) : (\n <span className=\"min-w-0 truncate\">{children}</span>\n )}\n {rightIcon && (\n <span\n className=\"flex shrink-0 items-center justify-center [&>svg]:size-3\"\n aria-hidden=\"true\"\n >\n {rightIcon}\n </span>\n )}\n </Comp>\n );\n },\n);\n\nBadge.displayName = \"Badge\";\n"],"names":["React","Slot","jsxs","cn","jsx","Slottable"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAIA,MAAM,gBAAgB;AAAA,EACpB,SAAS;AAAA,IACP,SAAS;AAAA,IACT,MAAM;AAAA,IACN,SAAS;AAAA,IACT,SAAS;AAAA,IACT,OAAO;AAAA,IACP,SAAS;AAAA,IACT,MAAM;AAAA,IACN,eAAe;AAAA,IACf,eAAe;AAAA,IACf,aAAa;AAAA,IACb,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,WAAW;AAAA,EAAA;AAAA,EAEb,UAAU;AAAA,IACR,SAAS;AAAA,IACT,MAAM;AAAA,IACN,SAAS;AAAA,IACT,SAAS;AAAA,IACT,OAAO;AAAA,IACP,SAAS;AAAA,IACT,MAAM;AAAA,IACN,eAAe;AAAA,IACf,eAAe;AAAA,IACf,aAAa;AAAA,IACb,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,WAAW;AAAA,EAAA;AAEf;AA4CO,MAAM,QAAQA,iBAAM;AAAA,EACzB,CACE;AAAA,IACE;AAAA,IACA,UAAU;AAAA,IACV,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA,GAAG;AAAA,EAAA,GAEL,QACG;AACH,UAAM,OAAO,UAAUC,UAAAA,OAAO;AAE9B,WACEC,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,eAAY;AAAA,QACZ,WAAWC,GAAAA;AAAAA;AAAAA,UAET;AAAA;AAAA,UAEA,cAAc,QAAQ,OAAO;AAAA;AAAA,UAE7B,WAAW;AAAA;AAAA,UAEX;AAAA,QAAA;AAAA,QAEF;AAAA,QACC,GAAG;AAAA,QAEH,UAAA;AAAA,UAAA,YACCC,2BAAAA;AAAAA,YAAC;AAAA,YAAA;AAAA,cACC,WAAU;AAAA,cACV,eAAY;AAAA,cAEX,UAAA;AAAA,YAAA;AAAA,UAAA;AAAA,UAGJ,WACCA,2BAAAA;AAAAA,YAAC;AAAA,YAAA;AAAA,cACC,WAAWD,GAAAA,GAAG,gCAAgC,cAAc,SAAS,OAAO,CAAC;AAAA,cAC7E,eAAY;AAAA,YAAA;AAAA,UAAA;AAAA,UAGf,yCACEE,qBAAA,EAAW,SAAA,CAAS,IAErBD,+BAAC,QAAA,EAAK,WAAU,oBAAoB,SAAA,CAAS;AAAA,UAE9C,aACCA,2BAAAA;AAAAA,YAAC;AAAA,YAAA;AAAA,cACC,WAAU;AAAA,cACV,eAAY;AAAA,cAEX,UAAA;AAAA,YAAA;AAAA,UAAA;AAAA,QACH;AAAA,MAAA;AAAA,IAAA;AAAA,EAIR;AACF;AAEA,MAAM,cAAc;;"}
1
+ {"version":3,"file":"Badge.cjs","sources":["../../../../src/components/Badge/Badge.tsx"],"sourcesContent":["import { Slot, Slottable } from \"@radix-ui/react-slot\";\nimport * as React from \"react\";\nimport { cn } from \"@/utils/cn.ts\";\n\nconst badgeVariants = {\n variant: {\n default: \"bg-buttons-secondary-default text-content-primary\",\n dark: \"bg-neutral-alphas-600 text-content-always-white\",\n success: \"bg-buttons-secondary-default text-content-primary\",\n warning: \"bg-buttons-secondary-default text-content-primary\",\n error: \"bg-buttons-secondary-default text-content-primary\",\n special: \"bg-buttons-secondary-default text-content-primary\",\n info: \"bg-buttons-secondary-default text-content-primary\",\n successColour: \"bg-success-surface text-content-primary\",\n warningColour: \"bg-warning-surface text-content-primary\",\n errorColour: \"bg-error-surface text-content-primary\",\n infoColour: \"bg-info-surface text-content-primary\",\n aiGenerated: \"bg-buttons-always-white-default text-content-always-black\",\n negative: \"bg-buttons-secondary-negative-default text-content-primary-inverted\",\n alwaysWhite: \"bg-buttons-secondary-negative-default text-content-always-white\",\n online: \"bg-background-primary text-brand-primary-default\",\n brand: \"bg-brand-primary-default text-content-always-black\",\n pink: \"bg-brand-secondary-default text-content-always-black\",\n brandLight: \"bg-brand-primary-muted text-content-primary\",\n pinkLight: \"bg-brand-secondary-muted text-content-primary\",\n },\n dotColor: {\n default: \"bg-icons-primary\",\n dark: \"bg-content-always-white\",\n success: \"bg-success-content\",\n warning: \"bg-warning-content\",\n error: \"bg-error-content\",\n special: \"bg-special-default\",\n info: \"bg-info-content\",\n successColour: \"bg-success-content\",\n warningColour: \"bg-warning-content\",\n errorColour: \"bg-error-content\",\n infoColour: \"bg-info-content\",\n aiGenerated: \"bg-content-always-black\",\n negative: \"bg-icons-primary-inverted\",\n alwaysWhite: \"bg-brand-primary-default\",\n online: \"bg-brand-primary-default\",\n brand: \"bg-content-always-black\",\n pink: \"bg-content-always-black\",\n brandLight: \"bg-content-always-black\",\n pinkLight: \"bg-content-always-black\",\n },\n} as const;\n\n/** Visual style variant of the badge. */\nexport type BadgeVariant =\n | \"default\"\n | \"dark\"\n | \"success\"\n | \"warning\"\n | \"error\"\n | \"special\"\n | \"info\"\n | \"successColour\"\n | \"warningColour\"\n | \"errorColour\"\n | \"infoColour\"\n | \"aiGenerated\"\n | \"negative\"\n | \"alwaysWhite\"\n | \"online\"\n | \"brand\"\n | \"pink\"\n | \"brandLight\"\n | \"pinkLight\";\n\nexport interface BadgeProps extends React.HTMLAttributes<HTMLSpanElement> {\n /** Visual style variant of the badge. @default \"default\" */\n variant?: BadgeVariant;\n /** Whether to show a coloured status dot at the leading edge. @default true */\n leftDot?: boolean;\n /** Icon element displayed before the label. */\n leftIcon?: React.ReactNode;\n /** Icon element displayed after the label. */\n rightIcon?: React.ReactNode;\n /** Merge props onto a child element instead of rendering a `<span>`. @default false */\n asChild?: boolean;\n}\n\n/**\n * A small inline label for status, category, or metadata information.\n *\n * @example\n * ```tsx\n * <Badge variant=\"success\">Active</Badge>\n * ```\n */\nexport const Badge = React.forwardRef<HTMLSpanElement, BadgeProps>(\n (\n {\n className,\n variant = \"default\",\n leftDot = true,\n leftIcon,\n rightIcon,\n asChild = false,\n onClick,\n children,\n ...props\n },\n ref,\n ) => {\n const Comp = asChild ? Slot : \"span\";\n\n return (\n <Comp\n ref={ref}\n data-testid=\"badge\"\n className={cn(\n // Base styles\n \"typography-badge-badgecaps inline-flex h-5 min-w-0 items-center gap-1 rounded-full px-2\",\n // Variant styles\n badgeVariants.variant[variant],\n // Interactive\n onClick && \"cursor-pointer\",\n // Manual CSS overrides\n className,\n )}\n onClick={onClick}\n {...props}\n >\n {leftIcon && (\n <span\n className=\"flex shrink-0 items-center justify-center [&>svg]:size-2.5\"\n aria-hidden=\"true\"\n >\n {leftIcon}\n </span>\n )}\n {leftDot && (\n <span\n className={cn(\"size-1 shrink-0 rounded-full\", badgeVariants.dotColor[variant])}\n aria-hidden=\"true\"\n />\n )}\n {asChild ? (\n <Slottable>{children}</Slottable>\n ) : (\n <span className=\"min-w-0 truncate [text-box:trim-both_cap_alphabetic]\">{children}</span>\n )}\n {rightIcon && (\n <span\n className=\"flex shrink-0 items-center justify-center [&>svg]:size-2.5\"\n aria-hidden=\"true\"\n >\n {rightIcon}\n </span>\n )}\n </Comp>\n );\n },\n);\n\nBadge.displayName = \"Badge\";\n"],"names":["React","Slot","jsxs","cn","jsx","Slottable"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAIA,MAAM,gBAAgB;AAAA,EACpB,SAAS;AAAA,IACP,SAAS;AAAA,IACT,MAAM;AAAA,IACN,SAAS;AAAA,IACT,SAAS;AAAA,IACT,OAAO;AAAA,IACP,SAAS;AAAA,IACT,MAAM;AAAA,IACN,eAAe;AAAA,IACf,eAAe;AAAA,IACf,aAAa;AAAA,IACb,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,UAAU;AAAA,IACV,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,WAAW;AAAA,EAAA;AAAA,EAEb,UAAU;AAAA,IACR,SAAS;AAAA,IACT,MAAM;AAAA,IACN,SAAS;AAAA,IACT,SAAS;AAAA,IACT,OAAO;AAAA,IACP,SAAS;AAAA,IACT,MAAM;AAAA,IACN,eAAe;AAAA,IACf,eAAe;AAAA,IACf,aAAa;AAAA,IACb,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,UAAU;AAAA,IACV,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,WAAW;AAAA,EAAA;AAEf;AA6CO,MAAM,QAAQA,iBAAM;AAAA,EACzB,CACE;AAAA,IACE;AAAA,IACA,UAAU;AAAA,IACV,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA,GAAG;AAAA,EAAA,GAEL,QACG;AACH,UAAM,OAAO,UAAUC,UAAAA,OAAO;AAE9B,WACEC,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,eAAY;AAAA,QACZ,WAAWC,GAAAA;AAAAA;AAAAA,UAET;AAAA;AAAA,UAEA,cAAc,QAAQ,OAAO;AAAA;AAAA,UAE7B,WAAW;AAAA;AAAA,UAEX;AAAA,QAAA;AAAA,QAEF;AAAA,QACC,GAAG;AAAA,QAEH,UAAA;AAAA,UAAA,YACCC,2BAAAA;AAAAA,YAAC;AAAA,YAAA;AAAA,cACC,WAAU;AAAA,cACV,eAAY;AAAA,cAEX,UAAA;AAAA,YAAA;AAAA,UAAA;AAAA,UAGJ,WACCA,2BAAAA;AAAAA,YAAC;AAAA,YAAA;AAAA,cACC,WAAWD,GAAAA,GAAG,gCAAgC,cAAc,SAAS,OAAO,CAAC;AAAA,cAC7E,eAAY;AAAA,YAAA;AAAA,UAAA;AAAA,UAGf,yCACEE,qBAAA,EAAW,SAAA,CAAS,IAErBD,+BAAC,QAAA,EAAK,WAAU,wDAAwD,SAAA,CAAS;AAAA,UAElF,aACCA,2BAAAA;AAAAA,YAAC;AAAA,YAAA;AAAA,cACC,WAAU;AAAA,cACV,eAAY;AAAA,cAEX,UAAA;AAAA,YAAA;AAAA,UAAA;AAAA,QACH;AAAA,MAAA;AAAA,IAAA;AAAA,EAIR;AACF;AAEA,MAAM,cAAc;;"}
@@ -116,7 +116,7 @@ function BannerGuideBody({
116
116
  {
117
117
  variant: GUIDE_BADGE_VARIANT[appStoreVariant],
118
118
  leftDot: false,
119
- className: "typography-badge-badgecaps self-start",
119
+ className: "self-start",
120
120
  children: eyebrow
121
121
  }
122
122
  ),
@@ -1 +1 @@
1
- {"version":3,"file":"Banner.cjs","sources":["../../../../src/components/Banner/Banner.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { Badge, type BadgeVariant } from \"../Badge/Badge\";\nimport { IconButton } from \"../IconButton/IconButton\";\nimport { CrossIcon } from \"../Icons/CrossIcon\";\n\n/**\n * Matches the Fanvue Library Banner Figma component property `Type`\n * (`Default`, `Subtle`, `whatsNew`, `appStore1`, `appStore2`, `appStore3`).\n */\nexport type BannerVariant =\n | \"Default\"\n | \"Subtle\"\n | \"whatsNew\"\n | \"appStore1\"\n | \"appStore2\"\n | \"appStore3\";\n\n/** Layout (`Vertical` / `Horizontal` / `HorizontalSmall` in Figma). Ignored for `Subtle` and app store types. */\nexport type BannerLayout = \"vertical\" | \"horizontal\" | \"compact\";\n\ntype AppStoreVariant = \"appStore1\" | \"appStore2\" | \"appStore3\";\n\nconst APP_STORE_VARIANTS: readonly AppStoreVariant[] = [\"appStore1\", \"appStore2\", \"appStore3\"];\n\nfunction isAppStoreVariant(variant: BannerVariant): variant is AppStoreVariant {\n return (APP_STORE_VARIANTS as readonly string[]).includes(variant);\n}\n\nconst GUIDE_BADGE_VARIANT: Record<AppStoreVariant, BadgeVariant> = {\n appStore1: \"dark\",\n appStore2: \"default\",\n appStore3: \"default\",\n};\n\nconst BANNER_SHADOW = \"shadow-sm\";\n\nconst guideGradient: Record<AppStoreVariant, React.CSSProperties> = {\n appStore1: {\n backgroundImage:\n \"linear-gradient(125.54deg, var(--color-brand-primary-muted) 0%, var(--color-neutral-alphas-50) 100%)\",\n },\n appStore2: {\n backgroundImage:\n \"linear-gradient(125.54deg, var(--color-brand-secondary-muted) 0%, var(--color-brand-secondary-muted) 100%)\",\n },\n appStore3: {\n backgroundImage:\n \"linear-gradient(125.54deg, var(--color-brand-secondary-muted) 0%, color-mix(in srgb, var(--color-info-surface) 80%, transparent) 100%)\",\n },\n};\n\nexport interface BannerProps extends Omit<React.HTMLAttributes<HTMLElement>, \"title\"> {\n /** Figma `Type` — selects structure, surfaces, and app store gradient. */\n variant: BannerVariant;\n /**\n * Figma `Orientation`. Ignored when `variant` is `Subtle` (horizontal) or an app store type (vertical).\n * @default `\"vertical\"` for `Default`, `\"horizontal\"` for `whatsNew`\n */\n layout?: BannerLayout;\n /** Leading visual (image, illustration, or composite). */\n media?: React.ReactNode;\n /** Small uppercase label (e.g. HOW TO). */\n eyebrow?: React.ReactNode;\n /** Top badge row (e.g. NEW pill) — mainly for `Subtle`. */\n leadBadge?: React.ReactNode;\n /** Main heading. */\n title?: React.ReactNode;\n /** Primary body copy. */\n description?: React.ReactNode;\n /** Extra line under description (`Subtle`). */\n secondaryLine?: React.ReactNode;\n /** Left-stacked pill action (`Subtle`). */\n stackedAction?: React.ReactNode;\n /** Row under stacked action (`Subtle`). */\n statusRow?: React.ReactNode;\n /** Primary button (e.g. Learn more). */\n primaryAction?: React.ReactNode;\n /** Text-style CTA (`whatsNew` / app store). */\n textAction?: React.ReactNode;\n /** When set, shows a dismiss control (`Default` only). */\n onDismiss?: () => void;\n /** Accessible label for dismiss. @default \"Dismiss banner\" */\n dismissLabel?: string;\n}\n\nfunction BannerDismiss({\n onDismiss,\n dismissLabel,\n}: {\n onDismiss: () => void;\n dismissLabel: string;\n}) {\n return (\n <IconButton\n variant=\"contrast\"\n size=\"24\"\n icon={<CrossIcon />}\n onClick={onDismiss}\n aria-label={dismissLabel}\n className=\"hover:bg-white/10 active:bg-white/15\"\n />\n );\n}\n\nfunction resolveLayout(variant: BannerVariant, layoutProp: BannerLayout | undefined): BannerLayout {\n if (variant === \"Subtle\") {\n return \"horizontal\";\n }\n if (isAppStoreVariant(variant)) {\n return \"vertical\";\n }\n if (variant === \"whatsNew\") {\n return layoutProp ?? \"horizontal\";\n }\n return layoutProp ?? \"vertical\";\n}\n\nfunction bannerRootClass(variant: BannerVariant, layout: BannerLayout, className?: string): string {\n return cn(\n \"flex rounded-md\",\n BANNER_SHADOW,\n variant === \"Default\" && \"gap-3 bg-surface-primary-inverted p-4 text-content-primary-inverted\",\n variant === \"Subtle\" &&\n \"w-full max-w-[600px] items-start gap-3 border border-border-primary bg-surface-secondary p-4 text-content-primary\",\n variant === \"whatsNew\" &&\n layout === \"horizontal\" &&\n \"w-full max-w-[446px] items-center gap-4 bg-surface-purple-muted p-4 text-content-primary\",\n variant === \"whatsNew\" &&\n layout === \"vertical\" &&\n \"w-full max-w-[220px] flex-col items-stretch gap-4 border border-border-primary bg-surface-secondary p-4 text-content-primary\",\n variant === \"whatsNew\" &&\n layout === \"compact\" &&\n \"w-full max-w-[446px] items-start gap-4 bg-surface-purple-muted p-4 text-content-primary\",\n isAppStoreVariant(variant) && \"w-full max-w-[280px] flex-col gap-4 p-6\",\n layout === \"vertical\" && variant === \"Default\" && \"max-w-[360px]\",\n layout === \"horizontal\" && variant === \"Default\" && \"w-full max-w-[600px] items-start\",\n className,\n );\n}\n\ntype BannerSectionProps = React.ComponentPropsWithoutRef<\"section\"> & {\n labelledBy?: string;\n};\n\nconst BannerSection = React.forwardRef<HTMLElement, BannerSectionProps>(\n ({ className, labelledBy, children, ...rest }, ref) => (\n <section\n ref={ref}\n aria-labelledby={labelledBy}\n data-testid=\"banner\"\n className={className}\n {...rest}\n >\n {children}\n </section>\n ),\n);\nBannerSection.displayName = \"BannerSection\";\n\ntype GuideBodyProps = Pick<BannerProps, \"eyebrow\" | \"title\" | \"description\" | \"textAction\"> & {\n appStoreVariant: AppStoreVariant;\n labelledBy?: string;\n};\n\nfunction BannerGuideBody({\n appStoreVariant,\n eyebrow,\n title,\n description,\n textAction,\n labelledBy,\n}: GuideBodyProps) {\n return (\n <>\n {eyebrow !== undefined && eyebrow !== null && eyebrow !== false && (\n <Badge\n variant={GUIDE_BADGE_VARIANT[appStoreVariant]}\n leftDot={false}\n className=\"typography-badge-badgecaps self-start\"\n >\n {eyebrow}\n </Badge>\n )}\n {title !== undefined && title !== null && title !== false && (\n <p id={labelledBy} className=\"typography-body-default-16px-semibold text-content-primary\">\n {title}\n </p>\n )}\n {description !== undefined && description !== null && description !== false && (\n <p className=\"typography-body-small-14px-regular text-content-secondary\">{description}</p>\n )}\n {textAction}\n </>\n );\n}\n\ntype FeatureBodyProps = Pick<BannerProps, \"title\" | \"description\" | \"textAction\" | \"media\"> & {\n layout: BannerLayout;\n labelledBy?: string;\n};\n\nfunction BannerFeatureBody({\n layout,\n media,\n title,\n description,\n textAction,\n labelledBy,\n}: FeatureBodyProps) {\n const titleClass =\n layout === \"vertical\"\n ? \"typography-body-default-16px-semibold text-content-primary\"\n : \"typography-body-default-16px-semibold text-[18px] leading-6 text-content-primary\";\n const mediaWrap =\n layout === \"compact\"\n ? \"size-20 shrink-0 overflow-hidden rounded-sm\"\n : \"size-[132px] shrink-0 overflow-hidden rounded-sm\";\n\n return (\n <>\n {media !== undefined && media !== null && (\n <div className={cn(mediaWrap, layout === \"vertical\" && \"w-full [&>*]:mx-auto\")}>\n {media}\n </div>\n )}\n <div\n className={cn(\n \"flex min-w-0 flex-col gap-2\",\n layout === \"horizontal\" || layout === \"compact\" ? \"flex-1 justify-end\" : \"w-full\",\n )}\n >\n {title !== undefined && title !== null && title !== false && (\n <div id={labelledBy} className={titleClass}>\n {title}\n </div>\n )}\n {description !== undefined && description !== null && description !== false && (\n <p className=\"typography-body-small-14px-regular text-content-secondary\">{description}</p>\n )}\n {textAction}\n </div>\n </>\n );\n}\n\ntype SubtleBodyProps = Pick<\n BannerProps,\n | \"media\"\n | \"leadBadge\"\n | \"title\"\n | \"description\"\n | \"secondaryLine\"\n | \"stackedAction\"\n | \"statusRow\"\n | \"primaryAction\"\n> & { labelledBy?: string };\n\nfunction BannerSubtleBody({\n media,\n leadBadge,\n title,\n description,\n secondaryLine,\n stackedAction,\n statusRow,\n primaryAction,\n labelledBy,\n}: SubtleBodyProps) {\n const mediaSizeDefault = \"size-12 shrink-0 overflow-hidden rounded-xl\";\n return (\n <>\n {media !== undefined && media !== null && <div className={mediaSizeDefault}>{media}</div>}\n <div className=\"flex min-w-0 flex-1 items-end gap-3\">\n <div className=\"flex min-w-0 flex-1 flex-col gap-4\">\n {leadBadge !== undefined && leadBadge !== null && (\n <div className=\"flex flex-wrap items-center gap-2\">{leadBadge}</div>\n )}\n <div className=\"flex flex-col gap-1\">\n {title !== undefined && title !== null && title !== false && (\n <div id={labelledBy} className=\"typography-header-heading-xs text-content-primary\">\n {title}\n </div>\n )}\n <div className=\"flex flex-col gap-2\">\n {description !== undefined && description !== null && description !== false && (\n <p className=\"typography-body-small-14px-regular text-content-primary\">\n {description}\n </p>\n )}\n {secondaryLine !== undefined && secondaryLine !== null && secondaryLine !== false && (\n <p className=\"typography-description-12px-regular text-content-primary\">\n {secondaryLine}\n </p>\n )}\n </div>\n </div>\n {stackedAction !== undefined && stackedAction !== null && (\n <div className=\"flex flex-col items-end self-start\">{stackedAction}</div>\n )}\n {statusRow !== undefined && statusRow !== null && (\n <div className=\"flex flex-wrap items-center gap-2\">{statusRow}</div>\n )}\n </div>\n {primaryAction !== undefined && primaryAction !== null && (\n <div className=\"shrink-0 self-center\">{primaryAction}</div>\n )}\n </div>\n </>\n );\n}\n\ntype InverseBodyProps = Pick<\n BannerProps,\n \"media\" | \"eyebrow\" | \"title\" | \"description\" | \"primaryAction\"\n> & {\n layout: BannerLayout;\n labelledBy?: string;\n dismissSlot: React.ReactNode;\n};\n\nfunction BannerInverseBody({\n layout,\n media,\n eyebrow,\n title,\n description,\n primaryAction,\n labelledBy,\n dismissSlot,\n}: InverseBodyProps) {\n const mediaSizeDefault = \"size-12 shrink-0 overflow-hidden rounded-xl\";\n const titleClassInverse = \"typography-header-heading-xs text-content-primary-inverted\";\n const textColumn = (\n <div className=\"flex min-w-0 flex-1 flex-col gap-1\">\n {eyebrow !== undefined && eyebrow !== null && eyebrow !== false && (\n <p className=\"typography-description-12px-semibold text-content-primary-inverted\">\n {eyebrow}\n </p>\n )}\n {title !== undefined && title !== null && title !== false && (\n <div id={labelledBy} className={titleClassInverse}>\n {title}\n </div>\n )}\n {description !== undefined && description !== null && description !== false && (\n <p className=\"typography-body-small-14px-regular text-content-primary-inverted\">\n {description}\n </p>\n )}\n </div>\n );\n\n return (\n <>\n {media !== undefined && media !== null && <div className={mediaSizeDefault}>{media}</div>}\n {layout === \"horizontal\" ? (\n <div className=\"flex min-w-0 flex-1 items-end gap-3\">\n {textColumn}\n {primaryAction !== undefined && primaryAction !== null && (\n <div className=\"shrink-0\">{primaryAction}</div>\n )}\n </div>\n ) : (\n <div className=\"flex min-w-0 flex-1 flex-col gap-3\">\n {textColumn}\n {primaryAction !== undefined && primaryAction !== null && <div>{primaryAction}</div>}\n </div>\n )}\n {dismissSlot}\n </>\n );\n}\n\nexport const Banner = React.forwardRef<HTMLElement, BannerProps>(\n (\n {\n className,\n variant,\n layout: layoutProp,\n media,\n eyebrow,\n leadBadge,\n title,\n description,\n secondaryLine,\n stackedAction,\n statusRow,\n primaryAction,\n textAction,\n onDismiss,\n dismissLabel = \"Dismiss banner\",\n ...props\n },\n ref,\n ) => {\n const layout = resolveLayout(variant, layoutProp);\n const showDismiss = onDismiss !== undefined && variant === \"Default\";\n const titleId = React.useId();\n const regionLabelledBy =\n title !== undefined && title !== null && title !== false ? titleId : undefined;\n\n const rootClass = bannerRootClass(variant, layout, className);\n\n if (isAppStoreVariant(variant)) {\n return (\n <BannerSection\n ref={ref}\n labelledBy={regionLabelledBy}\n style={guideGradient[variant]}\n className={rootClass}\n {...props}\n >\n <BannerGuideBody\n appStoreVariant={variant}\n eyebrow={eyebrow}\n title={title}\n description={description}\n textAction={textAction}\n labelledBy={regionLabelledBy}\n />\n </BannerSection>\n );\n }\n\n if (variant === \"whatsNew\") {\n return (\n <BannerSection ref={ref} labelledBy={regionLabelledBy} className={rootClass} {...props}>\n <BannerFeatureBody\n layout={layout}\n media={media}\n title={title}\n description={description}\n textAction={textAction}\n labelledBy={regionLabelledBy}\n />\n </BannerSection>\n );\n }\n\n if (variant === \"Subtle\") {\n return (\n <BannerSection ref={ref} labelledBy={regionLabelledBy} className={rootClass} {...props}>\n <BannerSubtleBody\n media={media}\n leadBadge={leadBadge}\n title={title}\n description={description}\n secondaryLine={secondaryLine}\n stackedAction={stackedAction}\n statusRow={statusRow}\n primaryAction={primaryAction}\n labelledBy={regionLabelledBy}\n />\n </BannerSection>\n );\n }\n\n const dismissSlot =\n showDismiss && onDismiss !== undefined ? (\n <BannerDismiss onDismiss={onDismiss} dismissLabel={dismissLabel} />\n ) : null;\n\n return (\n <BannerSection ref={ref} labelledBy={regionLabelledBy} className={rootClass} {...props}>\n <div className=\"flex w-full items-start gap-3\">\n <BannerInverseBody\n layout={layout}\n media={media}\n eyebrow={eyebrow}\n title={title}\n description={description}\n primaryAction={primaryAction}\n labelledBy={regionLabelledBy}\n dismissSlot={dismissSlot}\n />\n </div>\n </BannerSection>\n );\n },\n);\n\nBanner.displayName = \"Banner\";\n"],"names":["jsx","IconButton","CrossIcon","cn","React","jsxs","Fragment","Badge"],"mappings":"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"}
1
+ {"version":3,"file":"Banner.cjs","sources":["../../../../src/components/Banner/Banner.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { Badge, type BadgeVariant } from \"../Badge/Badge\";\nimport { IconButton } from \"../IconButton/IconButton\";\nimport { CrossIcon } from \"../Icons/CrossIcon\";\n\n/**\n * Matches the Fanvue Library Banner Figma component property `Type`\n * (`Default`, `Subtle`, `whatsNew`, `appStore1`, `appStore2`, `appStore3`).\n */\nexport type BannerVariant =\n | \"Default\"\n | \"Subtle\"\n | \"whatsNew\"\n | \"appStore1\"\n | \"appStore2\"\n | \"appStore3\";\n\n/** Layout (`Vertical` / `Horizontal` / `HorizontalSmall` in Figma). Ignored for `Subtle` and app store types. */\nexport type BannerLayout = \"vertical\" | \"horizontal\" | \"compact\";\n\ntype AppStoreVariant = \"appStore1\" | \"appStore2\" | \"appStore3\";\n\nconst APP_STORE_VARIANTS: readonly AppStoreVariant[] = [\"appStore1\", \"appStore2\", \"appStore3\"];\n\nfunction isAppStoreVariant(variant: BannerVariant): variant is AppStoreVariant {\n return (APP_STORE_VARIANTS as readonly string[]).includes(variant);\n}\n\nconst GUIDE_BADGE_VARIANT: Record<AppStoreVariant, BadgeVariant> = {\n appStore1: \"dark\",\n appStore2: \"default\",\n appStore3: \"default\",\n};\n\nconst BANNER_SHADOW = \"shadow-sm\";\n\nconst guideGradient: Record<AppStoreVariant, React.CSSProperties> = {\n appStore1: {\n backgroundImage:\n \"linear-gradient(125.54deg, var(--color-brand-primary-muted) 0%, var(--color-neutral-alphas-50) 100%)\",\n },\n appStore2: {\n backgroundImage:\n \"linear-gradient(125.54deg, var(--color-brand-secondary-muted) 0%, var(--color-brand-secondary-muted) 100%)\",\n },\n appStore3: {\n backgroundImage:\n \"linear-gradient(125.54deg, var(--color-brand-secondary-muted) 0%, color-mix(in srgb, var(--color-info-surface) 80%, transparent) 100%)\",\n },\n};\n\nexport interface BannerProps extends Omit<React.HTMLAttributes<HTMLElement>, \"title\"> {\n /** Figma `Type` — selects structure, surfaces, and app store gradient. */\n variant: BannerVariant;\n /**\n * Figma `Orientation`. Ignored when `variant` is `Subtle` (horizontal) or an app store type (vertical).\n * @default `\"vertical\"` for `Default`, `\"horizontal\"` for `whatsNew`\n */\n layout?: BannerLayout;\n /** Leading visual (image, illustration, or composite). */\n media?: React.ReactNode;\n /** Small uppercase label (e.g. HOW TO). */\n eyebrow?: React.ReactNode;\n /** Top badge row (e.g. NEW pill) — mainly for `Subtle`. */\n leadBadge?: React.ReactNode;\n /** Main heading. */\n title?: React.ReactNode;\n /** Primary body copy. */\n description?: React.ReactNode;\n /** Extra line under description (`Subtle`). */\n secondaryLine?: React.ReactNode;\n /** Left-stacked pill action (`Subtle`). */\n stackedAction?: React.ReactNode;\n /** Row under stacked action (`Subtle`). */\n statusRow?: React.ReactNode;\n /** Primary button (e.g. Learn more). */\n primaryAction?: React.ReactNode;\n /** Text-style CTA (`whatsNew` / app store). */\n textAction?: React.ReactNode;\n /** When set, shows a dismiss control (`Default` only). */\n onDismiss?: () => void;\n /** Accessible label for dismiss. @default \"Dismiss banner\" */\n dismissLabel?: string;\n}\n\nfunction BannerDismiss({\n onDismiss,\n dismissLabel,\n}: {\n onDismiss: () => void;\n dismissLabel: string;\n}) {\n return (\n <IconButton\n variant=\"contrast\"\n size=\"24\"\n icon={<CrossIcon />}\n onClick={onDismiss}\n aria-label={dismissLabel}\n className=\"hover:bg-white/10 active:bg-white/15\"\n />\n );\n}\n\nfunction resolveLayout(variant: BannerVariant, layoutProp: BannerLayout | undefined): BannerLayout {\n if (variant === \"Subtle\") {\n return \"horizontal\";\n }\n if (isAppStoreVariant(variant)) {\n return \"vertical\";\n }\n if (variant === \"whatsNew\") {\n return layoutProp ?? \"horizontal\";\n }\n return layoutProp ?? \"vertical\";\n}\n\nfunction bannerRootClass(variant: BannerVariant, layout: BannerLayout, className?: string): string {\n return cn(\n \"flex rounded-md\",\n BANNER_SHADOW,\n variant === \"Default\" && \"gap-3 bg-surface-primary-inverted p-4 text-content-primary-inverted\",\n variant === \"Subtle\" &&\n \"w-full max-w-[600px] items-start gap-3 border border-border-primary bg-surface-secondary p-4 text-content-primary\",\n variant === \"whatsNew\" &&\n layout === \"horizontal\" &&\n \"w-full max-w-[446px] items-center gap-4 bg-surface-purple-muted p-4 text-content-primary\",\n variant === \"whatsNew\" &&\n layout === \"vertical\" &&\n \"w-full max-w-[220px] flex-col items-stretch gap-4 border border-border-primary bg-surface-secondary p-4 text-content-primary\",\n variant === \"whatsNew\" &&\n layout === \"compact\" &&\n \"w-full max-w-[446px] items-start gap-4 bg-surface-purple-muted p-4 text-content-primary\",\n isAppStoreVariant(variant) && \"w-full max-w-[280px] flex-col gap-4 p-6\",\n layout === \"vertical\" && variant === \"Default\" && \"max-w-[360px]\",\n layout === \"horizontal\" && variant === \"Default\" && \"w-full max-w-[600px] items-start\",\n className,\n );\n}\n\ntype BannerSectionProps = React.ComponentPropsWithoutRef<\"section\"> & {\n labelledBy?: string;\n};\n\nconst BannerSection = React.forwardRef<HTMLElement, BannerSectionProps>(\n ({ className, labelledBy, children, ...rest }, ref) => (\n <section\n ref={ref}\n aria-labelledby={labelledBy}\n data-testid=\"banner\"\n className={className}\n {...rest}\n >\n {children}\n </section>\n ),\n);\nBannerSection.displayName = \"BannerSection\";\n\ntype GuideBodyProps = Pick<BannerProps, \"eyebrow\" | \"title\" | \"description\" | \"textAction\"> & {\n appStoreVariant: AppStoreVariant;\n labelledBy?: string;\n};\n\nfunction BannerGuideBody({\n appStoreVariant,\n eyebrow,\n title,\n description,\n textAction,\n labelledBy,\n}: GuideBodyProps) {\n return (\n <>\n {eyebrow !== undefined && eyebrow !== null && eyebrow !== false && (\n <Badge\n variant={GUIDE_BADGE_VARIANT[appStoreVariant]}\n leftDot={false}\n className=\"self-start\"\n >\n {eyebrow}\n </Badge>\n )}\n {title !== undefined && title !== null && title !== false && (\n <p id={labelledBy} className=\"typography-body-default-16px-semibold text-content-primary\">\n {title}\n </p>\n )}\n {description !== undefined && description !== null && description !== false && (\n <p className=\"typography-body-small-14px-regular text-content-secondary\">{description}</p>\n )}\n {textAction}\n </>\n );\n}\n\ntype FeatureBodyProps = Pick<BannerProps, \"title\" | \"description\" | \"textAction\" | \"media\"> & {\n layout: BannerLayout;\n labelledBy?: string;\n};\n\nfunction BannerFeatureBody({\n layout,\n media,\n title,\n description,\n textAction,\n labelledBy,\n}: FeatureBodyProps) {\n const titleClass =\n layout === \"vertical\"\n ? \"typography-body-default-16px-semibold text-content-primary\"\n : \"typography-body-default-16px-semibold text-[18px] leading-6 text-content-primary\";\n const mediaWrap =\n layout === \"compact\"\n ? \"size-20 shrink-0 overflow-hidden rounded-sm\"\n : \"size-[132px] shrink-0 overflow-hidden rounded-sm\";\n\n return (\n <>\n {media !== undefined && media !== null && (\n <div className={cn(mediaWrap, layout === \"vertical\" && \"w-full [&>*]:mx-auto\")}>\n {media}\n </div>\n )}\n <div\n className={cn(\n \"flex min-w-0 flex-col gap-2\",\n layout === \"horizontal\" || layout === \"compact\" ? \"flex-1 justify-end\" : \"w-full\",\n )}\n >\n {title !== undefined && title !== null && title !== false && (\n <div id={labelledBy} className={titleClass}>\n {title}\n </div>\n )}\n {description !== undefined && description !== null && description !== false && (\n <p className=\"typography-body-small-14px-regular text-content-secondary\">{description}</p>\n )}\n {textAction}\n </div>\n </>\n );\n}\n\ntype SubtleBodyProps = Pick<\n BannerProps,\n | \"media\"\n | \"leadBadge\"\n | \"title\"\n | \"description\"\n | \"secondaryLine\"\n | \"stackedAction\"\n | \"statusRow\"\n | \"primaryAction\"\n> & { labelledBy?: string };\n\nfunction BannerSubtleBody({\n media,\n leadBadge,\n title,\n description,\n secondaryLine,\n stackedAction,\n statusRow,\n primaryAction,\n labelledBy,\n}: SubtleBodyProps) {\n const mediaSizeDefault = \"size-12 shrink-0 overflow-hidden rounded-xl\";\n return (\n <>\n {media !== undefined && media !== null && <div className={mediaSizeDefault}>{media}</div>}\n <div className=\"flex min-w-0 flex-1 items-end gap-3\">\n <div className=\"flex min-w-0 flex-1 flex-col gap-4\">\n {leadBadge !== undefined && leadBadge !== null && (\n <div className=\"flex flex-wrap items-center gap-2\">{leadBadge}</div>\n )}\n <div className=\"flex flex-col gap-1\">\n {title !== undefined && title !== null && title !== false && (\n <div id={labelledBy} className=\"typography-header-heading-xs text-content-primary\">\n {title}\n </div>\n )}\n <div className=\"flex flex-col gap-2\">\n {description !== undefined && description !== null && description !== false && (\n <p className=\"typography-body-small-14px-regular text-content-primary\">\n {description}\n </p>\n )}\n {secondaryLine !== undefined && secondaryLine !== null && secondaryLine !== false && (\n <p className=\"typography-description-12px-regular text-content-primary\">\n {secondaryLine}\n </p>\n )}\n </div>\n </div>\n {stackedAction !== undefined && stackedAction !== null && (\n <div className=\"flex flex-col items-end self-start\">{stackedAction}</div>\n )}\n {statusRow !== undefined && statusRow !== null && (\n <div className=\"flex flex-wrap items-center gap-2\">{statusRow}</div>\n )}\n </div>\n {primaryAction !== undefined && primaryAction !== null && (\n <div className=\"shrink-0 self-center\">{primaryAction}</div>\n )}\n </div>\n </>\n );\n}\n\ntype InverseBodyProps = Pick<\n BannerProps,\n \"media\" | \"eyebrow\" | \"title\" | \"description\" | \"primaryAction\"\n> & {\n layout: BannerLayout;\n labelledBy?: string;\n dismissSlot: React.ReactNode;\n};\n\nfunction BannerInverseBody({\n layout,\n media,\n eyebrow,\n title,\n description,\n primaryAction,\n labelledBy,\n dismissSlot,\n}: InverseBodyProps) {\n const mediaSizeDefault = \"size-12 shrink-0 overflow-hidden rounded-xl\";\n const titleClassInverse = \"typography-header-heading-xs text-content-primary-inverted\";\n const textColumn = (\n <div className=\"flex min-w-0 flex-1 flex-col gap-1\">\n {eyebrow !== undefined && eyebrow !== null && eyebrow !== false && (\n <p className=\"typography-description-12px-semibold text-content-primary-inverted\">\n {eyebrow}\n </p>\n )}\n {title !== undefined && title !== null && title !== false && (\n <div id={labelledBy} className={titleClassInverse}>\n {title}\n </div>\n )}\n {description !== undefined && description !== null && description !== false && (\n <p className=\"typography-body-small-14px-regular text-content-primary-inverted\">\n {description}\n </p>\n )}\n </div>\n );\n\n return (\n <>\n {media !== undefined && media !== null && <div className={mediaSizeDefault}>{media}</div>}\n {layout === \"horizontal\" ? (\n <div className=\"flex min-w-0 flex-1 items-end gap-3\">\n {textColumn}\n {primaryAction !== undefined && primaryAction !== null && (\n <div className=\"shrink-0\">{primaryAction}</div>\n )}\n </div>\n ) : (\n <div className=\"flex min-w-0 flex-1 flex-col gap-3\">\n {textColumn}\n {primaryAction !== undefined && primaryAction !== null && <div>{primaryAction}</div>}\n </div>\n )}\n {dismissSlot}\n </>\n );\n}\n\nexport const Banner = React.forwardRef<HTMLElement, BannerProps>(\n (\n {\n className,\n variant,\n layout: layoutProp,\n media,\n eyebrow,\n leadBadge,\n title,\n description,\n secondaryLine,\n stackedAction,\n statusRow,\n primaryAction,\n textAction,\n onDismiss,\n dismissLabel = \"Dismiss banner\",\n ...props\n },\n ref,\n ) => {\n const layout = resolveLayout(variant, layoutProp);\n const showDismiss = onDismiss !== undefined && variant === \"Default\";\n const titleId = React.useId();\n const regionLabelledBy =\n title !== undefined && title !== null && title !== false ? titleId : undefined;\n\n const rootClass = bannerRootClass(variant, layout, className);\n\n if (isAppStoreVariant(variant)) {\n return (\n <BannerSection\n ref={ref}\n labelledBy={regionLabelledBy}\n style={guideGradient[variant]}\n className={rootClass}\n {...props}\n >\n <BannerGuideBody\n appStoreVariant={variant}\n eyebrow={eyebrow}\n title={title}\n description={description}\n textAction={textAction}\n labelledBy={regionLabelledBy}\n />\n </BannerSection>\n );\n }\n\n if (variant === \"whatsNew\") {\n return (\n <BannerSection ref={ref} labelledBy={regionLabelledBy} className={rootClass} {...props}>\n <BannerFeatureBody\n layout={layout}\n media={media}\n title={title}\n description={description}\n textAction={textAction}\n labelledBy={regionLabelledBy}\n />\n </BannerSection>\n );\n }\n\n if (variant === \"Subtle\") {\n return (\n <BannerSection ref={ref} labelledBy={regionLabelledBy} className={rootClass} {...props}>\n <BannerSubtleBody\n media={media}\n leadBadge={leadBadge}\n title={title}\n description={description}\n secondaryLine={secondaryLine}\n stackedAction={stackedAction}\n statusRow={statusRow}\n primaryAction={primaryAction}\n labelledBy={regionLabelledBy}\n />\n </BannerSection>\n );\n }\n\n const dismissSlot =\n showDismiss && onDismiss !== undefined ? (\n <BannerDismiss onDismiss={onDismiss} dismissLabel={dismissLabel} />\n ) : null;\n\n return (\n <BannerSection ref={ref} labelledBy={regionLabelledBy} className={rootClass} {...props}>\n <div className=\"flex w-full items-start gap-3\">\n <BannerInverseBody\n layout={layout}\n media={media}\n eyebrow={eyebrow}\n title={title}\n description={description}\n primaryAction={primaryAction}\n labelledBy={regionLabelledBy}\n dismissSlot={dismissSlot}\n />\n </div>\n </BannerSection>\n );\n },\n);\n\nBanner.displayName = \"Banner\";\n"],"names":["jsx","IconButton","CrossIcon","cn","React","jsxs","Fragment","Badge"],"mappings":"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"}
@@ -5,6 +5,7 @@ const jsxRuntime = require("react/jsx-runtime");
5
5
  const reactSlot = require("@radix-ui/react-slot");
6
6
  const React = require("react");
7
7
  const cn = require("../../utils/cn.cjs");
8
+ const Count = require("../Count/Count.cjs");
8
9
  function _interopNamespaceDefault(e) {
9
10
  const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
10
11
  if (e) {
@@ -34,6 +35,9 @@ const Chip = React__namespace.forwardRef(
34
35
  leftIcon,
35
36
  rightIcon,
36
37
  notificationLabel,
38
+ notificationCount,
39
+ notificationMax,
40
+ notificationVariant = "brand",
37
41
  onClick,
38
42
  asChild = false,
39
43
  children,
@@ -129,7 +133,23 @@ const Chip = React__namespace.forwardRef(
129
133
  }
130
134
  )
131
135
  ] }),
132
- notificationLabel && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "typography-description-12px-semibold absolute -top-1 -right-1 flex h-4 min-w-4 items-center justify-center rounded-full bg-content-primary px-1 text-content-primary-inverted", children: notificationLabel })
136
+ (notificationCount != null || notificationLabel) && /* @__PURE__ */ jsxRuntime.jsx(
137
+ Count.Count,
138
+ {
139
+ variant: notificationVariant,
140
+ size: "32",
141
+ className: cn.cn(
142
+ // V2 Notification Count: 20px high/min-width, caption 12,
143
+ // 4px horizontal and 2px vertical padding, rounded-md.
144
+ "absolute top-[-4px] rounded-md px-1 py-0.5",
145
+ // Anchor the leading edge so wider values grow away from the label.
146
+ size === "40" ? "left-[calc(100%_-_16px)]" : "left-[calc(100%_-_11px)]"
147
+ ),
148
+ value: notificationCount ?? 0,
149
+ max: notificationMax,
150
+ children: notificationCount == null ? notificationLabel : void 0
151
+ }
152
+ )
133
153
  ] })
134
154
  }
135
155
  );
@@ -1 +1 @@
1
- {"version":3,"file":"Chip.cjs","sources":["../../../../src/components/Chip/Chip.tsx"],"sourcesContent":["import { Slot, Slottable } from \"@radix-ui/react-slot\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\n/** Visual variant of the chip. */\nexport type ChipVariant = \"rounded\" | \"square\" | \"dark\";\n/** Height of the chip in pixels. */\nexport type ChipSize = \"32\" | \"40\";\n\nexport interface ChipProps extends React.HTMLAttributes<HTMLElement> {\n /** Visual variant of the chip. @default \"rounded\" */\n variant?: ChipVariant;\n /** Height of the chip in pixels. @default \"32\" */\n size?: ChipSize;\n /** Whether the chip is in a selected (pressed) state. @default false */\n selected?: boolean;\n /** Whether the chip is disabled. @default false */\n disabled?: boolean;\n /** Whether to show a coloured status dot at the leading edge. @default false */\n leftDot?: boolean;\n /**\n * Whether the chip uses a dashed border for add/create affordances.\n * When `selected`, it becomes a subtle filled state with a solid border.\n * Has no effect when `variant=\"dark\"`.\n * @default false\n */\n dotted?: boolean;\n /** Icon element displayed before the label. */\n leftIcon?: React.ReactNode;\n /** Icon element displayed after the label. */\n rightIcon?: React.ReactNode;\n /** Notification badge content (e.g. `\"99+\"`). Passed as a string for i18n support. */\n notificationLabel?: string;\n /** Click handler — when provided, the chip renders as a `<button>` for accessibility. */\n onClick?: React.MouseEventHandler<HTMLElement>;\n /** Merge props onto a child element instead of rendering a wrapper. @default false */\n asChild?: boolean;\n}\n\n/**\n * A compact element for filters, tags, or toggleable actions. When an `onClick`\n * handler is provided, the chip renders as an interactive `<button>` with\n * `aria-pressed` support.\n *\n * @example\n * ```tsx\n * <Chip selected onClick={toggle}>Music</Chip>\n * ```\n */\nexport const Chip = React.forwardRef<HTMLButtonElement, ChipProps>(\n (\n {\n className,\n variant = \"rounded\",\n size = \"32\",\n selected = false,\n disabled = false,\n leftDot = false,\n dotted = false,\n leftIcon,\n rightIcon,\n notificationLabel,\n onClick,\n asChild = false,\n children,\n ...props\n },\n ref,\n // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Variant-heavy UI component\n ) => {\n const isInteractive = !!onClick && !asChild;\n const Comp = asChild ? Slot : isInteractive ? \"button\" : \"span\";\n const isDark = variant === \"dark\";\n\n // The dashed border is drawn as an SVG so the dash length matches the design\n // spec (8/8 for the 40px square, 6/6 otherwise). CSS `border-dashed` only\n // renders browser-default dash lengths, which are too short. The stroke colour\n // is driven by `currentColor`, and `group-hover`/`group-active` react to the\n // chip's interactive states. Rendered only in the default (non-`asChild`) path.\n const showDottedBorder = !isDark && dotted && !selected && !asChild;\n const dottedBorder = showDottedBorder ? (\n <svg\n aria-hidden=\"true\"\n className={cn(\n \"pointer-events-none absolute inset-0 size-full overflow-visible\",\n disabled ? \"text-buttons-chip-disabled\" : \"text-buttons-chip-dotted-default\",\n isInteractive &&\n !disabled &&\n \"group-hover:text-buttons-chip-dotted-hover-stroke group-active:text-buttons-chip-dotted-hover-stroke\",\n )}\n >\n <rect\n x=\"0.5\"\n y=\"0.5\"\n rx={variant === \"square\" ? 7.5 : size === \"40\" ? 19.5 : 15.5}\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1\"\n strokeDasharray={variant === \"square\" && size === \"40\" ? \"8 8\" : \"6 6\"}\n style={{ width: \"calc(100% - 1px)\", height: \"calc(100% - 1px)\" }}\n />\n </svg>\n ) : null;\n\n return (\n <Comp\n ref={ref}\n data-testid=\"chip\"\n className={cn(\n \"relative inline-flex min-w-0 items-center justify-center whitespace-nowrap motion-safe:transition-colors motion-safe:duration-150\",\n // Shape\n variant === \"square\" ? \"rounded-xs\" : \"rounded-full\",\n // Size (32px uses 12px text, 40px uses 14px text per the design spec)\n size === \"32\" && \"typography-description-12px-semibold h-8 py-1\",\n size === \"40\" && \"typography-body-small-14px-semibold h-10 py-2.5\",\n // Variant colors\n isDark && \"bg-neutral-alphas-600 text-content-always-white\",\n !isDark && selected && !dotted && \"bg-buttons-chip-active text-content-primary-inverted\",\n // Active + dotted is a subtle filled state with a solid (non-dashed) border.\n !isDark &&\n selected &&\n dotted &&\n \"border border-border-primary border-solid bg-inputs-inputs-primary text-content-primary\",\n !isDark && !selected && !dotted && \"bg-buttons-chip-default text-content-primary\",\n // Dotted (non-selected): the dashed border is drawn via SVG (`dottedBorder`).\n // `group` lets that SVG react to hover/active. `asChild` keeps a CSS dashed\n // border fallback since the SVG is only rendered in the default path.\n !isDark && !selected && dotted && \"bg-transparent text-content-primary\",\n !isDark && !selected && dotted && !asChild && \"group\",\n asChild &&\n !isDark &&\n !selected &&\n dotted &&\n (disabled\n ? \"border border-buttons-chip-disabled border-dashed\"\n : \"border border-buttons-chip-dotted-default border-dashed\"),\n // Interactive\n isInteractive && !disabled && \"cursor-pointer\",\n isInteractive &&\n !disabled &&\n !isDark &&\n !selected &&\n !dotted &&\n \"hover:bg-buttons-chip-hover\",\n isInteractive &&\n !disabled &&\n !isDark &&\n !selected &&\n dotted &&\n \"hover:bg-neutral-alphas-50 active:bg-neutral-alphas-50\",\n // Focus\n \"focus-visible:shadow-focus-ring focus-visible:outline-none\",\n // Disabled\n disabled && isDark && \"pointer-events-none opacity-50\",\n disabled && !isDark && \"pointer-events-none text-content-disabled\",\n // Solid (non-dotted) disabled chips get a muted fill; dotted ones stay\n // transparent with their dashed border (drawn via SVG).\n disabled && !isDark && !dotted && \"bg-buttons-chip-disabled\",\n className,\n )}\n {...(isInteractive && {\n type: \"button\" as const,\n disabled,\n \"aria-pressed\": selected,\n onClick,\n })}\n {...(!isInteractive && disabled && { \"aria-disabled\": true })}\n {...(selected && { \"data-selected\": \"\" })}\n {...props}\n >\n {asChild ? (\n <Slottable>{children}</Slottable>\n ) : (\n <>\n {dottedBorder}\n <span className=\"flex min-w-0 items-center gap-0.5 overflow-hidden px-3\">\n {leftDot && (\n <span className=\"size-2 shrink-0 rounded-full bg-current\" aria-hidden=\"true\" />\n )}\n {leftIcon && (\n <span className=\"flex shrink-0 items-center justify-center\" aria-hidden=\"true\">\n {leftIcon}\n </span>\n )}\n <span className=\"min-w-0 truncate\">{children}</span>\n {rightIcon && (\n <span\n className=\"flex size-5 shrink-0 items-center justify-center\"\n aria-hidden=\"true\"\n >\n {rightIcon}\n </span>\n )}\n </span>\n {notificationLabel && (\n <span className=\"typography-description-12px-semibold absolute -top-1 -right-1 flex h-4 min-w-4 items-center justify-center rounded-full bg-content-primary px-1 text-content-primary-inverted\">\n {notificationLabel}\n </span>\n )}\n </>\n )}\n </Comp>\n );\n },\n);\n\nChip.displayName = \"Chip\";\n"],"names":["React","Slot","jsx","cn","Slottable","jsxs","Fragment"],"mappings":"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"}
1
+ {"version":3,"file":"Chip.cjs","sources":["../../../../src/components/Chip/Chip.tsx"],"sourcesContent":["import { Slot, Slottable } from \"@radix-ui/react-slot\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { Count, type CountVariant } from \"../Count/Count\";\n\n/** Visual variant of the chip. */\nexport type ChipVariant = \"rounded\" | \"square\" | \"dark\";\n/** Height of the chip in pixels. */\nexport type ChipSize = \"32\" | \"40\";\n\nexport interface ChipProps extends React.HTMLAttributes<HTMLElement> {\n /** Visual variant of the chip. @default \"rounded\" */\n variant?: ChipVariant;\n /** Height of the chip in pixels. @default \"32\" */\n size?: ChipSize;\n /** Whether the chip is in a selected (pressed) state. @default false */\n selected?: boolean;\n /** Whether the chip is disabled. @default false */\n disabled?: boolean;\n /** Whether to show a coloured status dot at the leading edge. @default false */\n leftDot?: boolean;\n /**\n * Whether the chip uses a dashed border for add/create affordances.\n * When `selected`, it becomes a subtle filled state with a solid border.\n * Has no effect when `variant=\"dark\"`.\n * @default false\n */\n dotted?: boolean;\n /** Icon element displayed before the label. */\n leftIcon?: React.ReactNode;\n /** Icon element displayed after the label. */\n rightIcon?: React.ReactNode;\n /** Notification badge content (e.g. `\"99+\"`). Passed as a string for i18n support. */\n notificationLabel?: string;\n /** Numeric value for the notification badge. Uses the `Count` component for overflow formatting. Takes precedence over `notificationLabel` when both are provided. */\n notificationCount?: number;\n /** Maximum value before the badge shows overflow (e.g. `\"9+\"`). Only applies when `notificationCount` is set. @default 99 */\n notificationMax?: number;\n /** Colour variant of the notification badge. @default \"brand\" */\n notificationVariant?: CountVariant;\n /** Click handler — when provided, the chip renders as a `<button>` for accessibility. */\n onClick?: React.MouseEventHandler<HTMLElement>;\n /** Merge props onto a child element instead of rendering a wrapper. @default false */\n asChild?: boolean;\n}\n\n/**\n * A compact element for filters, tags, or toggleable actions. When an `onClick`\n * handler is provided, the chip renders as an interactive `<button>` with\n * `aria-pressed` support.\n *\n * @example\n * ```tsx\n * <Chip selected onClick={toggle}>Music</Chip>\n * ```\n */\nexport const Chip = React.forwardRef<HTMLButtonElement, ChipProps>(\n (\n {\n className,\n variant = \"rounded\",\n size = \"32\",\n selected = false,\n disabled = false,\n leftDot = false,\n dotted = false,\n leftIcon,\n rightIcon,\n notificationLabel,\n notificationCount,\n notificationMax,\n notificationVariant = \"brand\",\n onClick,\n asChild = false,\n children,\n ...props\n },\n ref,\n // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Variant-heavy UI component\n ) => {\n const isInteractive = !!onClick && !asChild;\n const Comp = asChild ? Slot : isInteractive ? \"button\" : \"span\";\n const isDark = variant === \"dark\";\n\n // The dashed border is drawn as an SVG so the dash length matches the design\n // spec (8/8 for the 40px square, 6/6 otherwise). CSS `border-dashed` only\n // renders browser-default dash lengths, which are too short. The stroke colour\n // is driven by `currentColor`, and `group-hover`/`group-active` react to the\n // chip's interactive states. Rendered only in the default (non-`asChild`) path.\n const showDottedBorder = !isDark && dotted && !selected && !asChild;\n const dottedBorder = showDottedBorder ? (\n <svg\n aria-hidden=\"true\"\n className={cn(\n \"pointer-events-none absolute inset-0 size-full overflow-visible\",\n disabled ? \"text-buttons-chip-disabled\" : \"text-buttons-chip-dotted-default\",\n isInteractive &&\n !disabled &&\n \"group-hover:text-buttons-chip-dotted-hover-stroke group-active:text-buttons-chip-dotted-hover-stroke\",\n )}\n >\n <rect\n x=\"0.5\"\n y=\"0.5\"\n rx={variant === \"square\" ? 7.5 : size === \"40\" ? 19.5 : 15.5}\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1\"\n strokeDasharray={variant === \"square\" && size === \"40\" ? \"8 8\" : \"6 6\"}\n style={{ width: \"calc(100% - 1px)\", height: \"calc(100% - 1px)\" }}\n />\n </svg>\n ) : null;\n\n return (\n <Comp\n ref={ref}\n data-testid=\"chip\"\n className={cn(\n \"relative inline-flex min-w-0 items-center justify-center whitespace-nowrap motion-safe:transition-colors motion-safe:duration-150\",\n // Shape\n variant === \"square\" ? \"rounded-xs\" : \"rounded-full\",\n // Size (32px uses 12px text, 40px uses 14px text per the design spec)\n size === \"32\" && \"typography-description-12px-semibold h-8 py-1\",\n size === \"40\" && \"typography-body-small-14px-semibold h-10 py-2.5\",\n // Variant colors\n isDark && \"bg-neutral-alphas-600 text-content-always-white\",\n !isDark && selected && !dotted && \"bg-buttons-chip-active text-content-primary-inverted\",\n // Active + dotted is a subtle filled state with a solid (non-dashed) border.\n !isDark &&\n selected &&\n dotted &&\n \"border border-border-primary border-solid bg-inputs-inputs-primary text-content-primary\",\n !isDark && !selected && !dotted && \"bg-buttons-chip-default text-content-primary\",\n // Dotted (non-selected): the dashed border is drawn via SVG (`dottedBorder`).\n // `group` lets that SVG react to hover/active. `asChild` keeps a CSS dashed\n // border fallback since the SVG is only rendered in the default path.\n !isDark && !selected && dotted && \"bg-transparent text-content-primary\",\n !isDark && !selected && dotted && !asChild && \"group\",\n asChild &&\n !isDark &&\n !selected &&\n dotted &&\n (disabled\n ? \"border border-buttons-chip-disabled border-dashed\"\n : \"border border-buttons-chip-dotted-default border-dashed\"),\n // Interactive\n isInteractive && !disabled && \"cursor-pointer\",\n isInteractive &&\n !disabled &&\n !isDark &&\n !selected &&\n !dotted &&\n \"hover:bg-buttons-chip-hover\",\n isInteractive &&\n !disabled &&\n !isDark &&\n !selected &&\n dotted &&\n \"hover:bg-neutral-alphas-50 active:bg-neutral-alphas-50\",\n // Focus\n \"focus-visible:shadow-focus-ring focus-visible:outline-none\",\n // Disabled\n disabled && isDark && \"pointer-events-none opacity-50\",\n disabled && !isDark && \"pointer-events-none text-content-disabled\",\n // Solid (non-dotted) disabled chips get a muted fill; dotted ones stay\n // transparent with their dashed border (drawn via SVG).\n disabled && !isDark && !dotted && \"bg-buttons-chip-disabled\",\n className,\n )}\n {...(isInteractive && {\n type: \"button\" as const,\n disabled,\n \"aria-pressed\": selected,\n onClick,\n })}\n {...(!isInteractive && disabled && { \"aria-disabled\": true })}\n {...(selected && { \"data-selected\": \"\" })}\n {...props}\n >\n {asChild ? (\n <Slottable>{children}</Slottable>\n ) : (\n <>\n {dottedBorder}\n <span className=\"flex min-w-0 items-center gap-0.5 overflow-hidden px-3\">\n {leftDot && (\n <span className=\"size-2 shrink-0 rounded-full bg-current\" aria-hidden=\"true\" />\n )}\n {leftIcon && (\n <span className=\"flex shrink-0 items-center justify-center\" aria-hidden=\"true\">\n {leftIcon}\n </span>\n )}\n <span className=\"min-w-0 truncate\">{children}</span>\n {rightIcon && (\n <span\n className=\"flex size-5 shrink-0 items-center justify-center\"\n aria-hidden=\"true\"\n >\n {rightIcon}\n </span>\n )}\n </span>\n {(notificationCount != null || notificationLabel) && (\n <Count\n variant={notificationVariant}\n size=\"32\"\n className={cn(\n // V2 Notification Count: 20px high/min-width, caption 12,\n // 4px horizontal and 2px vertical padding, rounded-md.\n \"absolute top-[-4px] rounded-md px-1 py-0.5\",\n // Anchor the leading edge so wider values grow away from the label.\n size === \"40\" ? \"left-[calc(100%_-_16px)]\" : \"left-[calc(100%_-_11px)]\",\n )}\n value={notificationCount ?? 0}\n max={notificationMax}\n >\n {notificationCount == null ? notificationLabel : undefined}\n </Count>\n )}\n </>\n )}\n </Comp>\n );\n },\n);\n\nChip.displayName = \"Chip\";\n"],"names":["React","Slot","jsx","cn","Slottable","jsxs","Fragment","Count"],"mappings":"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"}
@@ -25,12 +25,33 @@ const React__namespace = /* @__PURE__ */ _interopNamespaceDefault(React);
25
25
  function getDisplayValue(value, max) {
26
26
  return value > max ? `${max}+` : value.toString();
27
27
  }
28
+ function variantClasses(variant) {
29
+ switch (variant) {
30
+ case "default":
31
+ return "bg-content-primary text-content-primary-inverted";
32
+ case "contrast":
33
+ return "bg-content-always-white text-content-always-black";
34
+ case "brand":
35
+ return "bg-brand-primary-default text-content-always-black";
36
+ case "alert":
37
+ return "bg-error-content text-content-always-white";
38
+ case "pink":
39
+ return "bg-brand-secondary-default text-content-always-black";
40
+ case "info":
41
+ return "bg-info-content text-content-always-white";
42
+ case "success":
43
+ return "bg-success-content text-content-always-white";
44
+ case "warning":
45
+ return "bg-warning-content text-content-always-black";
46
+ }
47
+ }
28
48
  const Count = React__namespace.forwardRef(
29
49
  ({
30
50
  className,
31
51
  variant = "default",
32
52
  value = 0,
33
53
  max = 99,
54
+ showAmount = true,
34
55
  size = "32",
35
56
  asChild = false,
36
57
  children,
@@ -40,22 +61,30 @@ const Count = React__namespace.forwardRef(
40
61
  return null;
41
62
  }
42
63
  const Comp = asChild ? reactSlot.Slot : "span";
64
+ const colors = variantClasses(variant);
65
+ if (!showAmount) {
66
+ return /* @__PURE__ */ jsxRuntime.jsx(
67
+ Comp,
68
+ {
69
+ ref,
70
+ "data-testid": "count",
71
+ className: cn.cn("inline-flex size-2 shrink-0 rounded-full", colors, className),
72
+ ...props
73
+ }
74
+ );
75
+ }
43
76
  return /* @__PURE__ */ jsxRuntime.jsx(
44
77
  Comp,
45
78
  {
46
79
  ref,
80
+ "data-testid": "count",
47
81
  className: cn.cn(
48
82
  "typography-description-12px-semibold inline-flex shrink-0 items-center justify-center rounded-full tabular-nums leading-none",
49
83
  size === "16" && "h-3 min-w-3 px-0.5 text-[8px]",
84
+ // Kept at 16px so nav / IconButton badges don't grow; Chip nests this size.
50
85
  size === "24" && "h-4 min-w-4 px-1 text-[10px]",
51
86
  size === "32" && "h-5 min-w-5 px-1.5 text-[12px]",
52
- variant === "default" && "bg-content-primary text-content-primary-inverted",
53
- variant === "alert" && "bg-error-content text-content-always-white",
54
- variant === "brand" && "bg-brand-primary-default text-content-always-black",
55
- variant === "pink" && "bg-brand-secondary-default text-content-always-black",
56
- variant === "info" && "bg-info-content text-content-always-white",
57
- variant === "success" && "bg-success-content text-content-always-white",
58
- variant === "warning" && "bg-warning-content text-content-always-black",
87
+ colors,
59
88
  className
60
89
  ),
61
90
  ...props,
@@ -1 +1 @@
1
- {"version":3,"file":"Count.cjs","sources":["../../../../src/components/Count/Count.tsx"],"sourcesContent":["import { Slot } from \"@radix-ui/react-slot\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\n/** Colour variant for the count badge. */\nexport type CountVariant = \"default\" | \"alert\" | \"brand\" | \"pink\" | \"info\" | \"success\" | \"warning\";\n\n/** Size of the count badge, aligned with button and icon-button sizes. */\nexport type CountSize = \"16\" | \"24\" | \"32\";\n\nfunction getDisplayValue(value: number, max: number): string {\n return value > max ? `${max}+` : value.toString();\n}\n\nexport interface CountProps extends React.HTMLAttributes<HTMLSpanElement> {\n /** Colour variant of the count badge. @default \"default\" */\n variant?: CountVariant;\n /** Numeric value to display. Renders nothing when `0` and no `children` are provided. @default 0 */\n value?: number;\n /** Maximum value before showing overflow (e.g. `\"99+\"`). @default 99 */\n max?: number;\n /** Size of the count badge. @default \"32\" */\n size?: CountSize;\n /** Merge props onto a child element instead of rendering a `<span>`. @default false */\n asChild?: boolean;\n}\n\n/**\n * A numeric badge typically used for notification counts. Automatically\n * truncates values above `max` (e.g. `\"99+\"`). Renders nothing when the\n * value is `0` and no children are provided.\n *\n * @example\n * ```tsx\n * <Count value={5} variant=\"brand\" />\n * ```\n */\nexport const Count = React.forwardRef<HTMLSpanElement, CountProps>(\n (\n {\n className,\n variant = \"default\",\n value = 0,\n max = 99,\n size = \"32\",\n asChild = false,\n children,\n ...props\n },\n ref,\n ) => {\n if (value === 0 && !children) {\n return null;\n }\n\n const Comp = asChild ? Slot : \"span\";\n\n return (\n <Comp\n ref={ref}\n className={cn(\n \"typography-description-12px-semibold inline-flex shrink-0 items-center justify-center rounded-full tabular-nums leading-none\",\n size === \"16\" && \"h-3 min-w-3 px-0.5 text-[8px]\",\n size === \"24\" && \"h-4 min-w-4 px-1 text-[10px]\",\n size === \"32\" && \"h-5 min-w-5 px-1.5 text-[12px]\",\n variant === \"default\" && \"bg-content-primary text-content-primary-inverted\",\n variant === \"alert\" && \"bg-error-content text-content-always-white\",\n variant === \"brand\" && \"bg-brand-primary-default text-content-always-black\",\n variant === \"pink\" && \"bg-brand-secondary-default text-content-always-black\",\n variant === \"info\" && \"bg-info-content text-content-always-white\",\n variant === \"success\" && \"bg-success-content text-content-always-white\",\n variant === \"warning\" && \"bg-warning-content text-content-always-black\",\n className,\n )}\n {...props}\n >\n {children ?? getDisplayValue(value, max)}\n </Comp>\n );\n },\n);\n\nCount.displayName = \"Count\";\n"],"names":["React","Slot","jsx","cn"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAUA,SAAS,gBAAgB,OAAe,KAAqB;AAC3D,SAAO,QAAQ,MAAM,GAAG,GAAG,MAAM,MAAM,SAAA;AACzC;AAyBO,MAAM,QAAQA,iBAAM;AAAA,EACzB,CACE;AAAA,IACE;AAAA,IACA,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,OAAO;AAAA,IACP,UAAU;AAAA,IACV;AAAA,IACA,GAAG;AAAA,EAAA,GAEL,QACG;AACH,QAAI,UAAU,KAAK,CAAC,UAAU;AAC5B,aAAO;AAAA,IACT;AAEA,UAAM,OAAO,UAAUC,UAAAA,OAAO;AAE9B,WACEC,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,WAAWC,GAAAA;AAAAA,UACT;AAAA,UACA,SAAS,QAAQ;AAAA,UACjB,SAAS,QAAQ;AAAA,UACjB,SAAS,QAAQ;AAAA,UACjB,YAAY,aAAa;AAAA,UACzB,YAAY,WAAW;AAAA,UACvB,YAAY,WAAW;AAAA,UACvB,YAAY,UAAU;AAAA,UACtB,YAAY,UAAU;AAAA,UACtB,YAAY,aAAa;AAAA,UACzB,YAAY,aAAa;AAAA,UACzB;AAAA,QAAA;AAAA,QAED,GAAG;AAAA,QAEH,UAAA,YAAY,gBAAgB,OAAO,GAAG;AAAA,MAAA;AAAA,IAAA;AAAA,EAG7C;AACF;AAEA,MAAM,cAAc;;"}
1
+ {"version":3,"file":"Count.cjs","sources":["../../../../src/components/Count/Count.tsx"],"sourcesContent":["import { Slot } from \"@radix-ui/react-slot\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\n/**\n * Colour variant for the count badge.\n *\n * V2 Notification Count (Fanvue Library) types: `default`, `brand`, `contrast`.\n * Remaining values are retained extensions for existing consumers.\n */\nexport type CountVariant =\n | \"default\"\n | \"contrast\"\n | \"brand\"\n | \"alert\"\n | \"pink\"\n | \"info\"\n | \"success\"\n | \"warning\";\n\n/** Size of the count badge, aligned with button and icon-button sizes. */\nexport type CountSize = \"16\" | \"24\" | \"32\";\n\nfunction getDisplayValue(value: number, max: number): string {\n return value > max ? `${max}+` : value.toString();\n}\n\nfunction variantClasses(variant: CountVariant): string {\n switch (variant) {\n case \"default\":\n return \"bg-content-primary text-content-primary-inverted\";\n case \"contrast\":\n // V2: on dark or coloured hosts (filled buttons, images)\n return \"bg-content-always-white text-content-always-black\";\n case \"brand\":\n return \"bg-brand-primary-default text-content-always-black\";\n case \"alert\":\n return \"bg-error-content text-content-always-white\";\n case \"pink\":\n return \"bg-brand-secondary-default text-content-always-black\";\n case \"info\":\n return \"bg-info-content text-content-always-white\";\n case \"success\":\n return \"bg-success-content text-content-always-white\";\n case \"warning\":\n return \"bg-warning-content text-content-always-black\";\n }\n}\n\nexport interface CountProps extends React.HTMLAttributes<HTMLSpanElement> {\n /**\n * Colour variant of the count badge.\n * Figma V2 types: `default` | `brand` | `contrast`. @default \"default\"\n */\n variant?: CountVariant;\n /** Numeric value to display. Renders nothing when `0` and no `children` are provided. @default 0 */\n value?: number;\n /** Maximum value before showing overflow (e.g. `\"99+\"`). @default 99 */\n max?: number;\n /**\n * When `false`, renders an 8px unread dot instead of a numeric amount\n * (Figma Show Amount=False). @default true\n */\n showAmount?: boolean;\n /** Size of the count badge. @default \"32\" */\n size?: CountSize;\n /** Merge props onto a child element instead of rendering a `<span>`. @default false */\n asChild?: boolean;\n}\n\n/**\n * V2 Notification Count a small badge for unread activity on nav items,\n * icons, buttons, or chips. Truncates values above `max` (e.g. `\"99+\"`).\n * Renders nothing when the value is `0` and no children are provided.\n *\n * @example\n * ```tsx\n * <Count value={5} variant=\"brand\" />\n * <Count value={1} variant=\"brand\" showAmount={false} />\n * ```\n */\nexport const Count = React.forwardRef<HTMLSpanElement, CountProps>(\n (\n {\n className,\n variant = \"default\",\n value = 0,\n max = 99,\n showAmount = true,\n size = \"32\",\n asChild = false,\n children,\n ...props\n },\n ref,\n ) => {\n if (value === 0 && !children) {\n return null;\n }\n\n const Comp = asChild ? Slot : \"span\";\n const colors = variantClasses(variant);\n\n if (!showAmount) {\n return (\n <Comp\n ref={ref}\n data-testid=\"count\"\n className={cn(\"inline-flex size-2 shrink-0 rounded-full\", colors, className)}\n {...props}\n />\n );\n }\n\n return (\n <Comp\n ref={ref}\n data-testid=\"count\"\n className={cn(\n \"typography-description-12px-semibold inline-flex shrink-0 items-center justify-center rounded-full tabular-nums leading-none\",\n size === \"16\" && \"h-3 min-w-3 px-0.5 text-[8px]\",\n // Kept at 16px so nav / IconButton badges don't grow; Chip nests this size.\n size === \"24\" && \"h-4 min-w-4 px-1 text-[10px]\",\n size === \"32\" && \"h-5 min-w-5 px-1.5 text-[12px]\",\n colors,\n className,\n )}\n {...props}\n >\n {children ?? getDisplayValue(value, max)}\n </Comp>\n );\n },\n);\n\nCount.displayName = \"Count\";\n"],"names":["React","Slot","jsx","cn"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAuBA,SAAS,gBAAgB,OAAe,KAAqB;AAC3D,SAAO,QAAQ,MAAM,GAAG,GAAG,MAAM,MAAM,SAAA;AACzC;AAEA,SAAS,eAAe,SAA+B;AACrD,UAAQ,SAAA;AAAA,IACN,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAEH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,EAAA;AAEb;AAkCO,MAAM,QAAQA,iBAAM;AAAA,EACzB,CACE;AAAA,IACE;AAAA,IACA,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,aAAa;AAAA,IACb,OAAO;AAAA,IACP,UAAU;AAAA,IACV;AAAA,IACA,GAAG;AAAA,EAAA,GAEL,QACG;AACH,QAAI,UAAU,KAAK,CAAC,UAAU;AAC5B,aAAO;AAAA,IACT;AAEA,UAAM,OAAO,UAAUC,UAAAA,OAAO;AAC9B,UAAM,SAAS,eAAe,OAAO;AAErC,QAAI,CAAC,YAAY;AACf,aACEC,2BAAAA;AAAAA,QAAC;AAAA,QAAA;AAAA,UACC;AAAA,UACA,eAAY;AAAA,UACZ,WAAWC,GAAAA,GAAG,4CAA4C,QAAQ,SAAS;AAAA,UAC1E,GAAG;AAAA,QAAA;AAAA,MAAA;AAAA,IAGV;AAEA,WACED,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,eAAY;AAAA,QACZ,WAAWC,GAAAA;AAAAA,UACT;AAAA,UACA,SAAS,QAAQ;AAAA;AAAA,UAEjB,SAAS,QAAQ;AAAA,UACjB,SAAS,QAAQ;AAAA,UACjB;AAAA,UACA;AAAA,QAAA;AAAA,QAED,GAAG;AAAA,QAEH,UAAA,YAAY,gBAAgB,OAAO,GAAG;AAAA,MAAA;AAAA,IAAA;AAAA,EAG7C;AACF;AAEA,MAAM,cAAc;;"}
@@ -5,19 +5,20 @@ import * as React from "react";
5
5
  import { cn } from "../../utils/cn.mjs";
6
6
  const badgeVariants = {
7
7
  variant: {
8
- default: "bg-neutral-alphas-50 text-content-primary",
8
+ default: "bg-buttons-secondary-default text-content-primary",
9
9
  dark: "bg-neutral-alphas-600 text-content-always-white",
10
- success: "bg-success-surface text-success-content",
11
- warning: "bg-warning-surface text-warning-content",
12
- error: "bg-error-surface text-error-content",
13
- special: "bg-neutral-alphas-50 text-content-primary",
14
- info: "bg-info-surface text-info-content",
10
+ success: "bg-buttons-secondary-default text-content-primary",
11
+ warning: "bg-buttons-secondary-default text-content-primary",
12
+ error: "bg-buttons-secondary-default text-content-primary",
13
+ special: "bg-buttons-secondary-default text-content-primary",
14
+ info: "bg-buttons-secondary-default text-content-primary",
15
15
  successColour: "bg-success-surface text-content-primary",
16
16
  warningColour: "bg-warning-surface text-content-primary",
17
17
  errorColour: "bg-error-surface text-content-primary",
18
18
  infoColour: "bg-info-surface text-content-primary",
19
19
  aiGenerated: "bg-buttons-always-white-default text-content-always-black",
20
20
  negative: "bg-buttons-secondary-negative-default text-content-primary-inverted",
21
+ alwaysWhite: "bg-buttons-secondary-negative-default text-content-always-white",
21
22
  online: "bg-background-primary text-brand-primary-default",
22
23
  brand: "bg-brand-primary-default text-content-always-black",
23
24
  pink: "bg-brand-secondary-default text-content-always-black",
@@ -25,7 +26,7 @@ const badgeVariants = {
25
26
  pinkLight: "bg-brand-secondary-muted text-content-primary"
26
27
  },
27
28
  dotColor: {
28
- default: "bg-content-primary",
29
+ default: "bg-icons-primary",
29
30
  dark: "bg-content-always-white",
30
31
  success: "bg-success-content",
31
32
  warning: "bg-warning-content",
@@ -38,6 +39,7 @@ const badgeVariants = {
38
39
  infoColour: "bg-info-content",
39
40
  aiGenerated: "bg-content-always-black",
40
41
  negative: "bg-icons-primary-inverted",
42
+ alwaysWhite: "bg-brand-primary-default",
41
43
  online: "bg-brand-primary-default",
42
44
  brand: "bg-content-always-black",
43
45
  pink: "bg-content-always-black",
@@ -65,7 +67,7 @@ const Badge = React.forwardRef(
65
67
  "data-testid": "badge",
66
68
  className: cn(
67
69
  // Base styles
68
- "typography-description-12px-semibold inline-flex h-6 min-w-0 items-center gap-2 rounded-full px-2",
70
+ "typography-badge-badgecaps inline-flex h-5 min-w-0 items-center gap-1 rounded-full px-2",
69
71
  // Variant styles
70
72
  badgeVariants.variant[variant],
71
73
  // Interactive
@@ -79,7 +81,7 @@ const Badge = React.forwardRef(
79
81
  leftIcon && /* @__PURE__ */ jsx(
80
82
  "span",
81
83
  {
82
- className: "flex shrink-0 items-center justify-center [&>svg]:size-3",
84
+ className: "flex shrink-0 items-center justify-center [&>svg]:size-2.5",
83
85
  "aria-hidden": "true",
84
86
  children: leftIcon
85
87
  }
@@ -91,11 +93,11 @@ const Badge = React.forwardRef(
91
93
  "aria-hidden": "true"
92
94
  }
93
95
  ),
94
- asChild ? /* @__PURE__ */ jsx(Slottable, { children }) : /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate", children }),
96
+ asChild ? /* @__PURE__ */ jsx(Slottable, { children }) : /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate [text-box:trim-both_cap_alphabetic]", children }),
95
97
  rightIcon && /* @__PURE__ */ jsx(
96
98
  "span",
97
99
  {
98
- className: "flex shrink-0 items-center justify-center [&>svg]:size-3",
100
+ className: "flex shrink-0 items-center justify-center [&>svg]:size-2.5",
99
101
  "aria-hidden": "true",
100
102
  children: rightIcon
101
103
  }
@@ -1 +1 @@
1
- {"version":3,"file":"Badge.mjs","sources":["../../../src/components/Badge/Badge.tsx"],"sourcesContent":["import { Slot, Slottable } from \"@radix-ui/react-slot\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\nconst badgeVariants = {\n variant: {\n default: \"bg-neutral-alphas-50 text-content-primary\",\n dark: \"bg-neutral-alphas-600 text-content-always-white\",\n success: \"bg-success-surface text-success-content\",\n warning: \"bg-warning-surface text-warning-content\",\n error: \"bg-error-surface text-error-content\",\n special: \"bg-neutral-alphas-50 text-content-primary\",\n info: \"bg-info-surface text-info-content\",\n successColour: \"bg-success-surface text-content-primary\",\n warningColour: \"bg-warning-surface text-content-primary\",\n errorColour: \"bg-error-surface text-content-primary\",\n infoColour: \"bg-info-surface text-content-primary\",\n aiGenerated: \"bg-buttons-always-white-default text-content-always-black\",\n negative: \"bg-buttons-secondary-negative-default text-content-primary-inverted\",\n online: \"bg-background-primary text-brand-primary-default\",\n brand: \"bg-brand-primary-default text-content-always-black\",\n pink: \"bg-brand-secondary-default text-content-always-black\",\n brandLight: \"bg-brand-primary-muted text-content-primary\",\n pinkLight: \"bg-brand-secondary-muted text-content-primary\",\n },\n dotColor: {\n default: \"bg-content-primary\",\n dark: \"bg-content-always-white\",\n success: \"bg-success-content\",\n warning: \"bg-warning-content\",\n error: \"bg-error-content\",\n special: \"bg-special-default\",\n info: \"bg-info-content\",\n successColour: \"bg-success-content\",\n warningColour: \"bg-warning-content\",\n errorColour: \"bg-error-content\",\n infoColour: \"bg-info-content\",\n aiGenerated: \"bg-content-always-black\",\n negative: \"bg-icons-primary-inverted\",\n online: \"bg-brand-primary-default\",\n brand: \"bg-content-always-black\",\n pink: \"bg-content-always-black\",\n brandLight: \"bg-content-always-black\",\n pinkLight: \"bg-content-always-black\",\n },\n} as const;\n\n/** Visual style variant of the badge. */\nexport type BadgeVariant =\n | \"default\"\n | \"dark\"\n | \"success\"\n | \"warning\"\n | \"error\"\n | \"special\"\n | \"info\"\n | \"successColour\"\n | \"warningColour\"\n | \"errorColour\"\n | \"infoColour\"\n | \"aiGenerated\"\n | \"negative\"\n | \"online\"\n | \"brand\"\n | \"pink\"\n | \"brandLight\"\n | \"pinkLight\";\n\nexport interface BadgeProps extends React.HTMLAttributes<HTMLSpanElement> {\n /** Visual style variant of the badge. @default \"default\" */\n variant?: BadgeVariant;\n /** Whether to show a coloured status dot at the leading edge. @default true */\n leftDot?: boolean;\n /** Icon element displayed before the label. */\n leftIcon?: React.ReactNode;\n /** Icon element displayed after the label. */\n rightIcon?: React.ReactNode;\n /** Merge props onto a child element instead of rendering a `<span>`. @default false */\n asChild?: boolean;\n}\n\n/**\n * A small inline label for status, category, or metadata information.\n *\n * @example\n * ```tsx\n * <Badge variant=\"success\">Active</Badge>\n * ```\n */\nexport const Badge = React.forwardRef<HTMLSpanElement, BadgeProps>(\n (\n {\n className,\n variant = \"default\",\n leftDot = true,\n leftIcon,\n rightIcon,\n asChild = false,\n onClick,\n children,\n ...props\n },\n ref,\n ) => {\n const Comp = asChild ? Slot : \"span\";\n\n return (\n <Comp\n ref={ref}\n data-testid=\"badge\"\n className={cn(\n // Base styles\n \"typography-description-12px-semibold inline-flex h-6 min-w-0 items-center gap-2 rounded-full px-2\",\n // Variant styles\n badgeVariants.variant[variant],\n // Interactive\n onClick && \"cursor-pointer\",\n // Manual CSS overrides\n className,\n )}\n onClick={onClick}\n {...props}\n >\n {leftIcon && (\n <span\n className=\"flex shrink-0 items-center justify-center [&>svg]:size-3\"\n aria-hidden=\"true\"\n >\n {leftIcon}\n </span>\n )}\n {leftDot && (\n <span\n className={cn(\"size-1 shrink-0 rounded-full\", badgeVariants.dotColor[variant])}\n aria-hidden=\"true\"\n />\n )}\n {asChild ? (\n <Slottable>{children}</Slottable>\n ) : (\n <span className=\"min-w-0 truncate\">{children}</span>\n )}\n {rightIcon && (\n <span\n className=\"flex shrink-0 items-center justify-center [&>svg]:size-3\"\n aria-hidden=\"true\"\n >\n {rightIcon}\n </span>\n )}\n </Comp>\n );\n },\n);\n\nBadge.displayName = \"Badge\";\n"],"names":[],"mappings":";;;;;AAIA,MAAM,gBAAgB;AAAA,EACpB,SAAS;AAAA,IACP,SAAS;AAAA,IACT,MAAM;AAAA,IACN,SAAS;AAAA,IACT,SAAS;AAAA,IACT,OAAO;AAAA,IACP,SAAS;AAAA,IACT,MAAM;AAAA,IACN,eAAe;AAAA,IACf,eAAe;AAAA,IACf,aAAa;AAAA,IACb,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,WAAW;AAAA,EAAA;AAAA,EAEb,UAAU;AAAA,IACR,SAAS;AAAA,IACT,MAAM;AAAA,IACN,SAAS;AAAA,IACT,SAAS;AAAA,IACT,OAAO;AAAA,IACP,SAAS;AAAA,IACT,MAAM;AAAA,IACN,eAAe;AAAA,IACf,eAAe;AAAA,IACf,aAAa;AAAA,IACb,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,WAAW;AAAA,EAAA;AAEf;AA4CO,MAAM,QAAQ,MAAM;AAAA,EACzB,CACE;AAAA,IACE;AAAA,IACA,UAAU;AAAA,IACV,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA,GAAG;AAAA,EAAA,GAEL,QACG;AACH,UAAM,OAAO,UAAU,OAAO;AAE9B,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,eAAY;AAAA,QACZ,WAAW;AAAA;AAAA,UAET;AAAA;AAAA,UAEA,cAAc,QAAQ,OAAO;AAAA;AAAA,UAE7B,WAAW;AAAA;AAAA,UAEX;AAAA,QAAA;AAAA,QAEF;AAAA,QACC,GAAG;AAAA,QAEH,UAAA;AAAA,UAAA,YACC;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,WAAU;AAAA,cACV,eAAY;AAAA,cAEX,UAAA;AAAA,YAAA;AAAA,UAAA;AAAA,UAGJ,WACC;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,WAAW,GAAG,gCAAgC,cAAc,SAAS,OAAO,CAAC;AAAA,cAC7E,eAAY;AAAA,YAAA;AAAA,UAAA;AAAA,UAGf,8BACE,WAAA,EAAW,SAAA,CAAS,IAErB,oBAAC,QAAA,EAAK,WAAU,oBAAoB,SAAA,CAAS;AAAA,UAE9C,aACC;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,WAAU;AAAA,cACV,eAAY;AAAA,cAEX,UAAA;AAAA,YAAA;AAAA,UAAA;AAAA,QACH;AAAA,MAAA;AAAA,IAAA;AAAA,EAIR;AACF;AAEA,MAAM,cAAc;"}
1
+ {"version":3,"file":"Badge.mjs","sources":["../../../src/components/Badge/Badge.tsx"],"sourcesContent":["import { Slot, Slottable } from \"@radix-ui/react-slot\";\nimport * as React from \"react\";\nimport { cn } from \"@/utils/cn.ts\";\n\nconst badgeVariants = {\n variant: {\n default: \"bg-buttons-secondary-default text-content-primary\",\n dark: \"bg-neutral-alphas-600 text-content-always-white\",\n success: \"bg-buttons-secondary-default text-content-primary\",\n warning: \"bg-buttons-secondary-default text-content-primary\",\n error: \"bg-buttons-secondary-default text-content-primary\",\n special: \"bg-buttons-secondary-default text-content-primary\",\n info: \"bg-buttons-secondary-default text-content-primary\",\n successColour: \"bg-success-surface text-content-primary\",\n warningColour: \"bg-warning-surface text-content-primary\",\n errorColour: \"bg-error-surface text-content-primary\",\n infoColour: \"bg-info-surface text-content-primary\",\n aiGenerated: \"bg-buttons-always-white-default text-content-always-black\",\n negative: \"bg-buttons-secondary-negative-default text-content-primary-inverted\",\n alwaysWhite: \"bg-buttons-secondary-negative-default text-content-always-white\",\n online: \"bg-background-primary text-brand-primary-default\",\n brand: \"bg-brand-primary-default text-content-always-black\",\n pink: \"bg-brand-secondary-default text-content-always-black\",\n brandLight: \"bg-brand-primary-muted text-content-primary\",\n pinkLight: \"bg-brand-secondary-muted text-content-primary\",\n },\n dotColor: {\n default: \"bg-icons-primary\",\n dark: \"bg-content-always-white\",\n success: \"bg-success-content\",\n warning: \"bg-warning-content\",\n error: \"bg-error-content\",\n special: \"bg-special-default\",\n info: \"bg-info-content\",\n successColour: \"bg-success-content\",\n warningColour: \"bg-warning-content\",\n errorColour: \"bg-error-content\",\n infoColour: \"bg-info-content\",\n aiGenerated: \"bg-content-always-black\",\n negative: \"bg-icons-primary-inverted\",\n alwaysWhite: \"bg-brand-primary-default\",\n online: \"bg-brand-primary-default\",\n brand: \"bg-content-always-black\",\n pink: \"bg-content-always-black\",\n brandLight: \"bg-content-always-black\",\n pinkLight: \"bg-content-always-black\",\n },\n} as const;\n\n/** Visual style variant of the badge. */\nexport type BadgeVariant =\n | \"default\"\n | \"dark\"\n | \"success\"\n | \"warning\"\n | \"error\"\n | \"special\"\n | \"info\"\n | \"successColour\"\n | \"warningColour\"\n | \"errorColour\"\n | \"infoColour\"\n | \"aiGenerated\"\n | \"negative\"\n | \"alwaysWhite\"\n | \"online\"\n | \"brand\"\n | \"pink\"\n | \"brandLight\"\n | \"pinkLight\";\n\nexport interface BadgeProps extends React.HTMLAttributes<HTMLSpanElement> {\n /** Visual style variant of the badge. @default \"default\" */\n variant?: BadgeVariant;\n /** Whether to show a coloured status dot at the leading edge. @default true */\n leftDot?: boolean;\n /** Icon element displayed before the label. */\n leftIcon?: React.ReactNode;\n /** Icon element displayed after the label. */\n rightIcon?: React.ReactNode;\n /** Merge props onto a child element instead of rendering a `<span>`. @default false */\n asChild?: boolean;\n}\n\n/**\n * A small inline label for status, category, or metadata information.\n *\n * @example\n * ```tsx\n * <Badge variant=\"success\">Active</Badge>\n * ```\n */\nexport const Badge = React.forwardRef<HTMLSpanElement, BadgeProps>(\n (\n {\n className,\n variant = \"default\",\n leftDot = true,\n leftIcon,\n rightIcon,\n asChild = false,\n onClick,\n children,\n ...props\n },\n ref,\n ) => {\n const Comp = asChild ? Slot : \"span\";\n\n return (\n <Comp\n ref={ref}\n data-testid=\"badge\"\n className={cn(\n // Base styles\n \"typography-badge-badgecaps inline-flex h-5 min-w-0 items-center gap-1 rounded-full px-2\",\n // Variant styles\n badgeVariants.variant[variant],\n // Interactive\n onClick && \"cursor-pointer\",\n // Manual CSS overrides\n className,\n )}\n onClick={onClick}\n {...props}\n >\n {leftIcon && (\n <span\n className=\"flex shrink-0 items-center justify-center [&>svg]:size-2.5\"\n aria-hidden=\"true\"\n >\n {leftIcon}\n </span>\n )}\n {leftDot && (\n <span\n className={cn(\"size-1 shrink-0 rounded-full\", badgeVariants.dotColor[variant])}\n aria-hidden=\"true\"\n />\n )}\n {asChild ? (\n <Slottable>{children}</Slottable>\n ) : (\n <span className=\"min-w-0 truncate [text-box:trim-both_cap_alphabetic]\">{children}</span>\n )}\n {rightIcon && (\n <span\n className=\"flex shrink-0 items-center justify-center [&>svg]:size-2.5\"\n aria-hidden=\"true\"\n >\n {rightIcon}\n </span>\n )}\n </Comp>\n );\n },\n);\n\nBadge.displayName = \"Badge\";\n"],"names":[],"mappings":";;;;;AAIA,MAAM,gBAAgB;AAAA,EACpB,SAAS;AAAA,IACP,SAAS;AAAA,IACT,MAAM;AAAA,IACN,SAAS;AAAA,IACT,SAAS;AAAA,IACT,OAAO;AAAA,IACP,SAAS;AAAA,IACT,MAAM;AAAA,IACN,eAAe;AAAA,IACf,eAAe;AAAA,IACf,aAAa;AAAA,IACb,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,UAAU;AAAA,IACV,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,WAAW;AAAA,EAAA;AAAA,EAEb,UAAU;AAAA,IACR,SAAS;AAAA,IACT,MAAM;AAAA,IACN,SAAS;AAAA,IACT,SAAS;AAAA,IACT,OAAO;AAAA,IACP,SAAS;AAAA,IACT,MAAM;AAAA,IACN,eAAe;AAAA,IACf,eAAe;AAAA,IACf,aAAa;AAAA,IACb,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,UAAU;AAAA,IACV,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,WAAW;AAAA,EAAA;AAEf;AA6CO,MAAM,QAAQ,MAAM;AAAA,EACzB,CACE;AAAA,IACE;AAAA,IACA,UAAU;AAAA,IACV,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA,GAAG;AAAA,EAAA,GAEL,QACG;AACH,UAAM,OAAO,UAAU,OAAO;AAE9B,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,eAAY;AAAA,QACZ,WAAW;AAAA;AAAA,UAET;AAAA;AAAA,UAEA,cAAc,QAAQ,OAAO;AAAA;AAAA,UAE7B,WAAW;AAAA;AAAA,UAEX;AAAA,QAAA;AAAA,QAEF;AAAA,QACC,GAAG;AAAA,QAEH,UAAA;AAAA,UAAA,YACC;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,WAAU;AAAA,cACV,eAAY;AAAA,cAEX,UAAA;AAAA,YAAA;AAAA,UAAA;AAAA,UAGJ,WACC;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,WAAW,GAAG,gCAAgC,cAAc,SAAS,OAAO,CAAC;AAAA,cAC7E,eAAY;AAAA,YAAA;AAAA,UAAA;AAAA,UAGf,8BACE,WAAA,EAAW,SAAA,CAAS,IAErB,oBAAC,QAAA,EAAK,WAAU,wDAAwD,SAAA,CAAS;AAAA,UAElF,aACC;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,WAAU;AAAA,cACV,eAAY;AAAA,cAEX,UAAA;AAAA,YAAA;AAAA,UAAA;AAAA,QACH;AAAA,MAAA;AAAA,IAAA;AAAA,EAIR;AACF;AAEA,MAAM,cAAc;"}
@@ -97,7 +97,7 @@ function BannerGuideBody({
97
97
  {
98
98
  variant: GUIDE_BADGE_VARIANT[appStoreVariant],
99
99
  leftDot: false,
100
- className: "typography-badge-badgecaps self-start",
100
+ className: "self-start",
101
101
  children: eyebrow
102
102
  }
103
103
  ),
@@ -1 +1 @@
1
- {"version":3,"file":"Banner.mjs","sources":["../../../src/components/Banner/Banner.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { Badge, type BadgeVariant } from \"../Badge/Badge\";\nimport { IconButton } from \"../IconButton/IconButton\";\nimport { CrossIcon } from \"../Icons/CrossIcon\";\n\n/**\n * Matches the Fanvue Library Banner Figma component property `Type`\n * (`Default`, `Subtle`, `whatsNew`, `appStore1`, `appStore2`, `appStore3`).\n */\nexport type BannerVariant =\n | \"Default\"\n | \"Subtle\"\n | \"whatsNew\"\n | \"appStore1\"\n | \"appStore2\"\n | \"appStore3\";\n\n/** Layout (`Vertical` / `Horizontal` / `HorizontalSmall` in Figma). Ignored for `Subtle` and app store types. */\nexport type BannerLayout = \"vertical\" | \"horizontal\" | \"compact\";\n\ntype AppStoreVariant = \"appStore1\" | \"appStore2\" | \"appStore3\";\n\nconst APP_STORE_VARIANTS: readonly AppStoreVariant[] = [\"appStore1\", \"appStore2\", \"appStore3\"];\n\nfunction isAppStoreVariant(variant: BannerVariant): variant is AppStoreVariant {\n return (APP_STORE_VARIANTS as readonly string[]).includes(variant);\n}\n\nconst GUIDE_BADGE_VARIANT: Record<AppStoreVariant, BadgeVariant> = {\n appStore1: \"dark\",\n appStore2: \"default\",\n appStore3: \"default\",\n};\n\nconst BANNER_SHADOW = \"shadow-sm\";\n\nconst guideGradient: Record<AppStoreVariant, React.CSSProperties> = {\n appStore1: {\n backgroundImage:\n \"linear-gradient(125.54deg, var(--color-brand-primary-muted) 0%, var(--color-neutral-alphas-50) 100%)\",\n },\n appStore2: {\n backgroundImage:\n \"linear-gradient(125.54deg, var(--color-brand-secondary-muted) 0%, var(--color-brand-secondary-muted) 100%)\",\n },\n appStore3: {\n backgroundImage:\n \"linear-gradient(125.54deg, var(--color-brand-secondary-muted) 0%, color-mix(in srgb, var(--color-info-surface) 80%, transparent) 100%)\",\n },\n};\n\nexport interface BannerProps extends Omit<React.HTMLAttributes<HTMLElement>, \"title\"> {\n /** Figma `Type` — selects structure, surfaces, and app store gradient. */\n variant: BannerVariant;\n /**\n * Figma `Orientation`. Ignored when `variant` is `Subtle` (horizontal) or an app store type (vertical).\n * @default `\"vertical\"` for `Default`, `\"horizontal\"` for `whatsNew`\n */\n layout?: BannerLayout;\n /** Leading visual (image, illustration, or composite). */\n media?: React.ReactNode;\n /** Small uppercase label (e.g. HOW TO). */\n eyebrow?: React.ReactNode;\n /** Top badge row (e.g. NEW pill) — mainly for `Subtle`. */\n leadBadge?: React.ReactNode;\n /** Main heading. */\n title?: React.ReactNode;\n /** Primary body copy. */\n description?: React.ReactNode;\n /** Extra line under description (`Subtle`). */\n secondaryLine?: React.ReactNode;\n /** Left-stacked pill action (`Subtle`). */\n stackedAction?: React.ReactNode;\n /** Row under stacked action (`Subtle`). */\n statusRow?: React.ReactNode;\n /** Primary button (e.g. Learn more). */\n primaryAction?: React.ReactNode;\n /** Text-style CTA (`whatsNew` / app store). */\n textAction?: React.ReactNode;\n /** When set, shows a dismiss control (`Default` only). */\n onDismiss?: () => void;\n /** Accessible label for dismiss. @default \"Dismiss banner\" */\n dismissLabel?: string;\n}\n\nfunction BannerDismiss({\n onDismiss,\n dismissLabel,\n}: {\n onDismiss: () => void;\n dismissLabel: string;\n}) {\n return (\n <IconButton\n variant=\"contrast\"\n size=\"24\"\n icon={<CrossIcon />}\n onClick={onDismiss}\n aria-label={dismissLabel}\n className=\"hover:bg-white/10 active:bg-white/15\"\n />\n );\n}\n\nfunction resolveLayout(variant: BannerVariant, layoutProp: BannerLayout | undefined): BannerLayout {\n if (variant === \"Subtle\") {\n return \"horizontal\";\n }\n if (isAppStoreVariant(variant)) {\n return \"vertical\";\n }\n if (variant === \"whatsNew\") {\n return layoutProp ?? \"horizontal\";\n }\n return layoutProp ?? \"vertical\";\n}\n\nfunction bannerRootClass(variant: BannerVariant, layout: BannerLayout, className?: string): string {\n return cn(\n \"flex rounded-md\",\n BANNER_SHADOW,\n variant === \"Default\" && \"gap-3 bg-surface-primary-inverted p-4 text-content-primary-inverted\",\n variant === \"Subtle\" &&\n \"w-full max-w-[600px] items-start gap-3 border border-border-primary bg-surface-secondary p-4 text-content-primary\",\n variant === \"whatsNew\" &&\n layout === \"horizontal\" &&\n \"w-full max-w-[446px] items-center gap-4 bg-surface-purple-muted p-4 text-content-primary\",\n variant === \"whatsNew\" &&\n layout === \"vertical\" &&\n \"w-full max-w-[220px] flex-col items-stretch gap-4 border border-border-primary bg-surface-secondary p-4 text-content-primary\",\n variant === \"whatsNew\" &&\n layout === \"compact\" &&\n \"w-full max-w-[446px] items-start gap-4 bg-surface-purple-muted p-4 text-content-primary\",\n isAppStoreVariant(variant) && \"w-full max-w-[280px] flex-col gap-4 p-6\",\n layout === \"vertical\" && variant === \"Default\" && \"max-w-[360px]\",\n layout === \"horizontal\" && variant === \"Default\" && \"w-full max-w-[600px] items-start\",\n className,\n );\n}\n\ntype BannerSectionProps = React.ComponentPropsWithoutRef<\"section\"> & {\n labelledBy?: string;\n};\n\nconst BannerSection = React.forwardRef<HTMLElement, BannerSectionProps>(\n ({ className, labelledBy, children, ...rest }, ref) => (\n <section\n ref={ref}\n aria-labelledby={labelledBy}\n data-testid=\"banner\"\n className={className}\n {...rest}\n >\n {children}\n </section>\n ),\n);\nBannerSection.displayName = \"BannerSection\";\n\ntype GuideBodyProps = Pick<BannerProps, \"eyebrow\" | \"title\" | \"description\" | \"textAction\"> & {\n appStoreVariant: AppStoreVariant;\n labelledBy?: string;\n};\n\nfunction BannerGuideBody({\n appStoreVariant,\n eyebrow,\n title,\n description,\n textAction,\n labelledBy,\n}: GuideBodyProps) {\n return (\n <>\n {eyebrow !== undefined && eyebrow !== null && eyebrow !== false && (\n <Badge\n variant={GUIDE_BADGE_VARIANT[appStoreVariant]}\n leftDot={false}\n className=\"typography-badge-badgecaps self-start\"\n >\n {eyebrow}\n </Badge>\n )}\n {title !== undefined && title !== null && title !== false && (\n <p id={labelledBy} className=\"typography-body-default-16px-semibold text-content-primary\">\n {title}\n </p>\n )}\n {description !== undefined && description !== null && description !== false && (\n <p className=\"typography-body-small-14px-regular text-content-secondary\">{description}</p>\n )}\n {textAction}\n </>\n );\n}\n\ntype FeatureBodyProps = Pick<BannerProps, \"title\" | \"description\" | \"textAction\" | \"media\"> & {\n layout: BannerLayout;\n labelledBy?: string;\n};\n\nfunction BannerFeatureBody({\n layout,\n media,\n title,\n description,\n textAction,\n labelledBy,\n}: FeatureBodyProps) {\n const titleClass =\n layout === \"vertical\"\n ? \"typography-body-default-16px-semibold text-content-primary\"\n : \"typography-body-default-16px-semibold text-[18px] leading-6 text-content-primary\";\n const mediaWrap =\n layout === \"compact\"\n ? \"size-20 shrink-0 overflow-hidden rounded-sm\"\n : \"size-[132px] shrink-0 overflow-hidden rounded-sm\";\n\n return (\n <>\n {media !== undefined && media !== null && (\n <div className={cn(mediaWrap, layout === \"vertical\" && \"w-full [&>*]:mx-auto\")}>\n {media}\n </div>\n )}\n <div\n className={cn(\n \"flex min-w-0 flex-col gap-2\",\n layout === \"horizontal\" || layout === \"compact\" ? \"flex-1 justify-end\" : \"w-full\",\n )}\n >\n {title !== undefined && title !== null && title !== false && (\n <div id={labelledBy} className={titleClass}>\n {title}\n </div>\n )}\n {description !== undefined && description !== null && description !== false && (\n <p className=\"typography-body-small-14px-regular text-content-secondary\">{description}</p>\n )}\n {textAction}\n </div>\n </>\n );\n}\n\ntype SubtleBodyProps = Pick<\n BannerProps,\n | \"media\"\n | \"leadBadge\"\n | \"title\"\n | \"description\"\n | \"secondaryLine\"\n | \"stackedAction\"\n | \"statusRow\"\n | \"primaryAction\"\n> & { labelledBy?: string };\n\nfunction BannerSubtleBody({\n media,\n leadBadge,\n title,\n description,\n secondaryLine,\n stackedAction,\n statusRow,\n primaryAction,\n labelledBy,\n}: SubtleBodyProps) {\n const mediaSizeDefault = \"size-12 shrink-0 overflow-hidden rounded-xl\";\n return (\n <>\n {media !== undefined && media !== null && <div className={mediaSizeDefault}>{media}</div>}\n <div className=\"flex min-w-0 flex-1 items-end gap-3\">\n <div className=\"flex min-w-0 flex-1 flex-col gap-4\">\n {leadBadge !== undefined && leadBadge !== null && (\n <div className=\"flex flex-wrap items-center gap-2\">{leadBadge}</div>\n )}\n <div className=\"flex flex-col gap-1\">\n {title !== undefined && title !== null && title !== false && (\n <div id={labelledBy} className=\"typography-header-heading-xs text-content-primary\">\n {title}\n </div>\n )}\n <div className=\"flex flex-col gap-2\">\n {description !== undefined && description !== null && description !== false && (\n <p className=\"typography-body-small-14px-regular text-content-primary\">\n {description}\n </p>\n )}\n {secondaryLine !== undefined && secondaryLine !== null && secondaryLine !== false && (\n <p className=\"typography-description-12px-regular text-content-primary\">\n {secondaryLine}\n </p>\n )}\n </div>\n </div>\n {stackedAction !== undefined && stackedAction !== null && (\n <div className=\"flex flex-col items-end self-start\">{stackedAction}</div>\n )}\n {statusRow !== undefined && statusRow !== null && (\n <div className=\"flex flex-wrap items-center gap-2\">{statusRow}</div>\n )}\n </div>\n {primaryAction !== undefined && primaryAction !== null && (\n <div className=\"shrink-0 self-center\">{primaryAction}</div>\n )}\n </div>\n </>\n );\n}\n\ntype InverseBodyProps = Pick<\n BannerProps,\n \"media\" | \"eyebrow\" | \"title\" | \"description\" | \"primaryAction\"\n> & {\n layout: BannerLayout;\n labelledBy?: string;\n dismissSlot: React.ReactNode;\n};\n\nfunction BannerInverseBody({\n layout,\n media,\n eyebrow,\n title,\n description,\n primaryAction,\n labelledBy,\n dismissSlot,\n}: InverseBodyProps) {\n const mediaSizeDefault = \"size-12 shrink-0 overflow-hidden rounded-xl\";\n const titleClassInverse = \"typography-header-heading-xs text-content-primary-inverted\";\n const textColumn = (\n <div className=\"flex min-w-0 flex-1 flex-col gap-1\">\n {eyebrow !== undefined && eyebrow !== null && eyebrow !== false && (\n <p className=\"typography-description-12px-semibold text-content-primary-inverted\">\n {eyebrow}\n </p>\n )}\n {title !== undefined && title !== null && title !== false && (\n <div id={labelledBy} className={titleClassInverse}>\n {title}\n </div>\n )}\n {description !== undefined && description !== null && description !== false && (\n <p className=\"typography-body-small-14px-regular text-content-primary-inverted\">\n {description}\n </p>\n )}\n </div>\n );\n\n return (\n <>\n {media !== undefined && media !== null && <div className={mediaSizeDefault}>{media}</div>}\n {layout === \"horizontal\" ? (\n <div className=\"flex min-w-0 flex-1 items-end gap-3\">\n {textColumn}\n {primaryAction !== undefined && primaryAction !== null && (\n <div className=\"shrink-0\">{primaryAction}</div>\n )}\n </div>\n ) : (\n <div className=\"flex min-w-0 flex-1 flex-col gap-3\">\n {textColumn}\n {primaryAction !== undefined && primaryAction !== null && <div>{primaryAction}</div>}\n </div>\n )}\n {dismissSlot}\n </>\n );\n}\n\nexport const Banner = React.forwardRef<HTMLElement, BannerProps>(\n (\n {\n className,\n variant,\n layout: layoutProp,\n media,\n eyebrow,\n leadBadge,\n title,\n description,\n secondaryLine,\n stackedAction,\n statusRow,\n primaryAction,\n textAction,\n onDismiss,\n dismissLabel = \"Dismiss banner\",\n ...props\n },\n ref,\n ) => {\n const layout = resolveLayout(variant, layoutProp);\n const showDismiss = onDismiss !== undefined && variant === \"Default\";\n const titleId = React.useId();\n const regionLabelledBy =\n title !== undefined && title !== null && title !== false ? titleId : undefined;\n\n const rootClass = bannerRootClass(variant, layout, className);\n\n if (isAppStoreVariant(variant)) {\n return (\n <BannerSection\n ref={ref}\n labelledBy={regionLabelledBy}\n style={guideGradient[variant]}\n className={rootClass}\n {...props}\n >\n <BannerGuideBody\n appStoreVariant={variant}\n eyebrow={eyebrow}\n title={title}\n description={description}\n textAction={textAction}\n labelledBy={regionLabelledBy}\n />\n </BannerSection>\n );\n }\n\n if (variant === \"whatsNew\") {\n return (\n <BannerSection ref={ref} labelledBy={regionLabelledBy} className={rootClass} {...props}>\n <BannerFeatureBody\n layout={layout}\n media={media}\n title={title}\n description={description}\n textAction={textAction}\n labelledBy={regionLabelledBy}\n />\n </BannerSection>\n );\n }\n\n if (variant === \"Subtle\") {\n return (\n <BannerSection ref={ref} labelledBy={regionLabelledBy} className={rootClass} {...props}>\n <BannerSubtleBody\n media={media}\n leadBadge={leadBadge}\n title={title}\n description={description}\n secondaryLine={secondaryLine}\n stackedAction={stackedAction}\n statusRow={statusRow}\n primaryAction={primaryAction}\n labelledBy={regionLabelledBy}\n />\n </BannerSection>\n );\n }\n\n const dismissSlot =\n showDismiss && onDismiss !== undefined ? (\n <BannerDismiss onDismiss={onDismiss} dismissLabel={dismissLabel} />\n ) : null;\n\n return (\n <BannerSection ref={ref} labelledBy={regionLabelledBy} className={rootClass} {...props}>\n <div className=\"flex w-full items-start gap-3\">\n <BannerInverseBody\n layout={layout}\n media={media}\n eyebrow={eyebrow}\n title={title}\n description={description}\n primaryAction={primaryAction}\n labelledBy={regionLabelledBy}\n dismissSlot={dismissSlot}\n />\n </div>\n </BannerSection>\n );\n },\n);\n\nBanner.displayName = \"Banner\";\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Banner.mjs","sources":["../../../src/components/Banner/Banner.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { Badge, type BadgeVariant } from \"../Badge/Badge\";\nimport { IconButton } from \"../IconButton/IconButton\";\nimport { CrossIcon } from \"../Icons/CrossIcon\";\n\n/**\n * Matches the Fanvue Library Banner Figma component property `Type`\n * (`Default`, `Subtle`, `whatsNew`, `appStore1`, `appStore2`, `appStore3`).\n */\nexport type BannerVariant =\n | \"Default\"\n | \"Subtle\"\n | \"whatsNew\"\n | \"appStore1\"\n | \"appStore2\"\n | \"appStore3\";\n\n/** Layout (`Vertical` / `Horizontal` / `HorizontalSmall` in Figma). Ignored for `Subtle` and app store types. */\nexport type BannerLayout = \"vertical\" | \"horizontal\" | \"compact\";\n\ntype AppStoreVariant = \"appStore1\" | \"appStore2\" | \"appStore3\";\n\nconst APP_STORE_VARIANTS: readonly AppStoreVariant[] = [\"appStore1\", \"appStore2\", \"appStore3\"];\n\nfunction isAppStoreVariant(variant: BannerVariant): variant is AppStoreVariant {\n return (APP_STORE_VARIANTS as readonly string[]).includes(variant);\n}\n\nconst GUIDE_BADGE_VARIANT: Record<AppStoreVariant, BadgeVariant> = {\n appStore1: \"dark\",\n appStore2: \"default\",\n appStore3: \"default\",\n};\n\nconst BANNER_SHADOW = \"shadow-sm\";\n\nconst guideGradient: Record<AppStoreVariant, React.CSSProperties> = {\n appStore1: {\n backgroundImage:\n \"linear-gradient(125.54deg, var(--color-brand-primary-muted) 0%, var(--color-neutral-alphas-50) 100%)\",\n },\n appStore2: {\n backgroundImage:\n \"linear-gradient(125.54deg, var(--color-brand-secondary-muted) 0%, var(--color-brand-secondary-muted) 100%)\",\n },\n appStore3: {\n backgroundImage:\n \"linear-gradient(125.54deg, var(--color-brand-secondary-muted) 0%, color-mix(in srgb, var(--color-info-surface) 80%, transparent) 100%)\",\n },\n};\n\nexport interface BannerProps extends Omit<React.HTMLAttributes<HTMLElement>, \"title\"> {\n /** Figma `Type` — selects structure, surfaces, and app store gradient. */\n variant: BannerVariant;\n /**\n * Figma `Orientation`. Ignored when `variant` is `Subtle` (horizontal) or an app store type (vertical).\n * @default `\"vertical\"` for `Default`, `\"horizontal\"` for `whatsNew`\n */\n layout?: BannerLayout;\n /** Leading visual (image, illustration, or composite). */\n media?: React.ReactNode;\n /** Small uppercase label (e.g. HOW TO). */\n eyebrow?: React.ReactNode;\n /** Top badge row (e.g. NEW pill) — mainly for `Subtle`. */\n leadBadge?: React.ReactNode;\n /** Main heading. */\n title?: React.ReactNode;\n /** Primary body copy. */\n description?: React.ReactNode;\n /** Extra line under description (`Subtle`). */\n secondaryLine?: React.ReactNode;\n /** Left-stacked pill action (`Subtle`). */\n stackedAction?: React.ReactNode;\n /** Row under stacked action (`Subtle`). */\n statusRow?: React.ReactNode;\n /** Primary button (e.g. Learn more). */\n primaryAction?: React.ReactNode;\n /** Text-style CTA (`whatsNew` / app store). */\n textAction?: React.ReactNode;\n /** When set, shows a dismiss control (`Default` only). */\n onDismiss?: () => void;\n /** Accessible label for dismiss. @default \"Dismiss banner\" */\n dismissLabel?: string;\n}\n\nfunction BannerDismiss({\n onDismiss,\n dismissLabel,\n}: {\n onDismiss: () => void;\n dismissLabel: string;\n}) {\n return (\n <IconButton\n variant=\"contrast\"\n size=\"24\"\n icon={<CrossIcon />}\n onClick={onDismiss}\n aria-label={dismissLabel}\n className=\"hover:bg-white/10 active:bg-white/15\"\n />\n );\n}\n\nfunction resolveLayout(variant: BannerVariant, layoutProp: BannerLayout | undefined): BannerLayout {\n if (variant === \"Subtle\") {\n return \"horizontal\";\n }\n if (isAppStoreVariant(variant)) {\n return \"vertical\";\n }\n if (variant === \"whatsNew\") {\n return layoutProp ?? \"horizontal\";\n }\n return layoutProp ?? \"vertical\";\n}\n\nfunction bannerRootClass(variant: BannerVariant, layout: BannerLayout, className?: string): string {\n return cn(\n \"flex rounded-md\",\n BANNER_SHADOW,\n variant === \"Default\" && \"gap-3 bg-surface-primary-inverted p-4 text-content-primary-inverted\",\n variant === \"Subtle\" &&\n \"w-full max-w-[600px] items-start gap-3 border border-border-primary bg-surface-secondary p-4 text-content-primary\",\n variant === \"whatsNew\" &&\n layout === \"horizontal\" &&\n \"w-full max-w-[446px] items-center gap-4 bg-surface-purple-muted p-4 text-content-primary\",\n variant === \"whatsNew\" &&\n layout === \"vertical\" &&\n \"w-full max-w-[220px] flex-col items-stretch gap-4 border border-border-primary bg-surface-secondary p-4 text-content-primary\",\n variant === \"whatsNew\" &&\n layout === \"compact\" &&\n \"w-full max-w-[446px] items-start gap-4 bg-surface-purple-muted p-4 text-content-primary\",\n isAppStoreVariant(variant) && \"w-full max-w-[280px] flex-col gap-4 p-6\",\n layout === \"vertical\" && variant === \"Default\" && \"max-w-[360px]\",\n layout === \"horizontal\" && variant === \"Default\" && \"w-full max-w-[600px] items-start\",\n className,\n );\n}\n\ntype BannerSectionProps = React.ComponentPropsWithoutRef<\"section\"> & {\n labelledBy?: string;\n};\n\nconst BannerSection = React.forwardRef<HTMLElement, BannerSectionProps>(\n ({ className, labelledBy, children, ...rest }, ref) => (\n <section\n ref={ref}\n aria-labelledby={labelledBy}\n data-testid=\"banner\"\n className={className}\n {...rest}\n >\n {children}\n </section>\n ),\n);\nBannerSection.displayName = \"BannerSection\";\n\ntype GuideBodyProps = Pick<BannerProps, \"eyebrow\" | \"title\" | \"description\" | \"textAction\"> & {\n appStoreVariant: AppStoreVariant;\n labelledBy?: string;\n};\n\nfunction BannerGuideBody({\n appStoreVariant,\n eyebrow,\n title,\n description,\n textAction,\n labelledBy,\n}: GuideBodyProps) {\n return (\n <>\n {eyebrow !== undefined && eyebrow !== null && eyebrow !== false && (\n <Badge\n variant={GUIDE_BADGE_VARIANT[appStoreVariant]}\n leftDot={false}\n className=\"self-start\"\n >\n {eyebrow}\n </Badge>\n )}\n {title !== undefined && title !== null && title !== false && (\n <p id={labelledBy} className=\"typography-body-default-16px-semibold text-content-primary\">\n {title}\n </p>\n )}\n {description !== undefined && description !== null && description !== false && (\n <p className=\"typography-body-small-14px-regular text-content-secondary\">{description}</p>\n )}\n {textAction}\n </>\n );\n}\n\ntype FeatureBodyProps = Pick<BannerProps, \"title\" | \"description\" | \"textAction\" | \"media\"> & {\n layout: BannerLayout;\n labelledBy?: string;\n};\n\nfunction BannerFeatureBody({\n layout,\n media,\n title,\n description,\n textAction,\n labelledBy,\n}: FeatureBodyProps) {\n const titleClass =\n layout === \"vertical\"\n ? \"typography-body-default-16px-semibold text-content-primary\"\n : \"typography-body-default-16px-semibold text-[18px] leading-6 text-content-primary\";\n const mediaWrap =\n layout === \"compact\"\n ? \"size-20 shrink-0 overflow-hidden rounded-sm\"\n : \"size-[132px] shrink-0 overflow-hidden rounded-sm\";\n\n return (\n <>\n {media !== undefined && media !== null && (\n <div className={cn(mediaWrap, layout === \"vertical\" && \"w-full [&>*]:mx-auto\")}>\n {media}\n </div>\n )}\n <div\n className={cn(\n \"flex min-w-0 flex-col gap-2\",\n layout === \"horizontal\" || layout === \"compact\" ? \"flex-1 justify-end\" : \"w-full\",\n )}\n >\n {title !== undefined && title !== null && title !== false && (\n <div id={labelledBy} className={titleClass}>\n {title}\n </div>\n )}\n {description !== undefined && description !== null && description !== false && (\n <p className=\"typography-body-small-14px-regular text-content-secondary\">{description}</p>\n )}\n {textAction}\n </div>\n </>\n );\n}\n\ntype SubtleBodyProps = Pick<\n BannerProps,\n | \"media\"\n | \"leadBadge\"\n | \"title\"\n | \"description\"\n | \"secondaryLine\"\n | \"stackedAction\"\n | \"statusRow\"\n | \"primaryAction\"\n> & { labelledBy?: string };\n\nfunction BannerSubtleBody({\n media,\n leadBadge,\n title,\n description,\n secondaryLine,\n stackedAction,\n statusRow,\n primaryAction,\n labelledBy,\n}: SubtleBodyProps) {\n const mediaSizeDefault = \"size-12 shrink-0 overflow-hidden rounded-xl\";\n return (\n <>\n {media !== undefined && media !== null && <div className={mediaSizeDefault}>{media}</div>}\n <div className=\"flex min-w-0 flex-1 items-end gap-3\">\n <div className=\"flex min-w-0 flex-1 flex-col gap-4\">\n {leadBadge !== undefined && leadBadge !== null && (\n <div className=\"flex flex-wrap items-center gap-2\">{leadBadge}</div>\n )}\n <div className=\"flex flex-col gap-1\">\n {title !== undefined && title !== null && title !== false && (\n <div id={labelledBy} className=\"typography-header-heading-xs text-content-primary\">\n {title}\n </div>\n )}\n <div className=\"flex flex-col gap-2\">\n {description !== undefined && description !== null && description !== false && (\n <p className=\"typography-body-small-14px-regular text-content-primary\">\n {description}\n </p>\n )}\n {secondaryLine !== undefined && secondaryLine !== null && secondaryLine !== false && (\n <p className=\"typography-description-12px-regular text-content-primary\">\n {secondaryLine}\n </p>\n )}\n </div>\n </div>\n {stackedAction !== undefined && stackedAction !== null && (\n <div className=\"flex flex-col items-end self-start\">{stackedAction}</div>\n )}\n {statusRow !== undefined && statusRow !== null && (\n <div className=\"flex flex-wrap items-center gap-2\">{statusRow}</div>\n )}\n </div>\n {primaryAction !== undefined && primaryAction !== null && (\n <div className=\"shrink-0 self-center\">{primaryAction}</div>\n )}\n </div>\n </>\n );\n}\n\ntype InverseBodyProps = Pick<\n BannerProps,\n \"media\" | \"eyebrow\" | \"title\" | \"description\" | \"primaryAction\"\n> & {\n layout: BannerLayout;\n labelledBy?: string;\n dismissSlot: React.ReactNode;\n};\n\nfunction BannerInverseBody({\n layout,\n media,\n eyebrow,\n title,\n description,\n primaryAction,\n labelledBy,\n dismissSlot,\n}: InverseBodyProps) {\n const mediaSizeDefault = \"size-12 shrink-0 overflow-hidden rounded-xl\";\n const titleClassInverse = \"typography-header-heading-xs text-content-primary-inverted\";\n const textColumn = (\n <div className=\"flex min-w-0 flex-1 flex-col gap-1\">\n {eyebrow !== undefined && eyebrow !== null && eyebrow !== false && (\n <p className=\"typography-description-12px-semibold text-content-primary-inverted\">\n {eyebrow}\n </p>\n )}\n {title !== undefined && title !== null && title !== false && (\n <div id={labelledBy} className={titleClassInverse}>\n {title}\n </div>\n )}\n {description !== undefined && description !== null && description !== false && (\n <p className=\"typography-body-small-14px-regular text-content-primary-inverted\">\n {description}\n </p>\n )}\n </div>\n );\n\n return (\n <>\n {media !== undefined && media !== null && <div className={mediaSizeDefault}>{media}</div>}\n {layout === \"horizontal\" ? (\n <div className=\"flex min-w-0 flex-1 items-end gap-3\">\n {textColumn}\n {primaryAction !== undefined && primaryAction !== null && (\n <div className=\"shrink-0\">{primaryAction}</div>\n )}\n </div>\n ) : (\n <div className=\"flex min-w-0 flex-1 flex-col gap-3\">\n {textColumn}\n {primaryAction !== undefined && primaryAction !== null && <div>{primaryAction}</div>}\n </div>\n )}\n {dismissSlot}\n </>\n );\n}\n\nexport const Banner = React.forwardRef<HTMLElement, BannerProps>(\n (\n {\n className,\n variant,\n layout: layoutProp,\n media,\n eyebrow,\n leadBadge,\n title,\n description,\n secondaryLine,\n stackedAction,\n statusRow,\n primaryAction,\n textAction,\n onDismiss,\n dismissLabel = \"Dismiss banner\",\n ...props\n },\n ref,\n ) => {\n const layout = resolveLayout(variant, layoutProp);\n const showDismiss = onDismiss !== undefined && variant === \"Default\";\n const titleId = React.useId();\n const regionLabelledBy =\n title !== undefined && title !== null && title !== false ? titleId : undefined;\n\n const rootClass = bannerRootClass(variant, layout, className);\n\n if (isAppStoreVariant(variant)) {\n return (\n <BannerSection\n ref={ref}\n labelledBy={regionLabelledBy}\n style={guideGradient[variant]}\n className={rootClass}\n {...props}\n >\n <BannerGuideBody\n appStoreVariant={variant}\n eyebrow={eyebrow}\n title={title}\n description={description}\n textAction={textAction}\n labelledBy={regionLabelledBy}\n />\n </BannerSection>\n );\n }\n\n if (variant === \"whatsNew\") {\n return (\n <BannerSection ref={ref} labelledBy={regionLabelledBy} className={rootClass} {...props}>\n <BannerFeatureBody\n layout={layout}\n media={media}\n title={title}\n description={description}\n textAction={textAction}\n labelledBy={regionLabelledBy}\n />\n </BannerSection>\n );\n }\n\n if (variant === \"Subtle\") {\n return (\n <BannerSection ref={ref} labelledBy={regionLabelledBy} className={rootClass} {...props}>\n <BannerSubtleBody\n media={media}\n leadBadge={leadBadge}\n title={title}\n description={description}\n secondaryLine={secondaryLine}\n stackedAction={stackedAction}\n statusRow={statusRow}\n primaryAction={primaryAction}\n labelledBy={regionLabelledBy}\n />\n </BannerSection>\n );\n }\n\n const dismissSlot =\n showDismiss && onDismiss !== undefined ? (\n <BannerDismiss onDismiss={onDismiss} dismissLabel={dismissLabel} />\n ) : null;\n\n return (\n <BannerSection ref={ref} labelledBy={regionLabelledBy} className={rootClass} {...props}>\n <div className=\"flex w-full items-start gap-3\">\n <BannerInverseBody\n layout={layout}\n media={media}\n eyebrow={eyebrow}\n title={title}\n description={description}\n primaryAction={primaryAction}\n labelledBy={regionLabelledBy}\n dismissSlot={dismissSlot}\n />\n </div>\n </BannerSection>\n );\n },\n);\n\nBanner.displayName = \"Banner\";\n"],"names":[],"mappings":"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"}
@@ -3,6 +3,7 @@ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
3
3
  import { Slot, Slottable } from "@radix-ui/react-slot";
4
4
  import * as React from "react";
5
5
  import { cn } from "../../utils/cn.mjs";
6
+ import { Count } from "../Count/Count.mjs";
6
7
  const Chip = React.forwardRef(
7
8
  ({
8
9
  className,
@@ -15,6 +16,9 @@ const Chip = React.forwardRef(
15
16
  leftIcon,
16
17
  rightIcon,
17
18
  notificationLabel,
19
+ notificationCount,
20
+ notificationMax,
21
+ notificationVariant = "brand",
18
22
  onClick,
19
23
  asChild = false,
20
24
  children,
@@ -110,7 +114,23 @@ const Chip = React.forwardRef(
110
114
  }
111
115
  )
112
116
  ] }),
113
- notificationLabel && /* @__PURE__ */ jsx("span", { className: "typography-description-12px-semibold absolute -top-1 -right-1 flex h-4 min-w-4 items-center justify-center rounded-full bg-content-primary px-1 text-content-primary-inverted", children: notificationLabel })
117
+ (notificationCount != null || notificationLabel) && /* @__PURE__ */ jsx(
118
+ Count,
119
+ {
120
+ variant: notificationVariant,
121
+ size: "32",
122
+ className: cn(
123
+ // V2 Notification Count: 20px high/min-width, caption 12,
124
+ // 4px horizontal and 2px vertical padding, rounded-md.
125
+ "absolute top-[-4px] rounded-md px-1 py-0.5",
126
+ // Anchor the leading edge so wider values grow away from the label.
127
+ size === "40" ? "left-[calc(100%_-_16px)]" : "left-[calc(100%_-_11px)]"
128
+ ),
129
+ value: notificationCount ?? 0,
130
+ max: notificationMax,
131
+ children: notificationCount == null ? notificationLabel : void 0
132
+ }
133
+ )
114
134
  ] })
115
135
  }
116
136
  );
@@ -1 +1 @@
1
- {"version":3,"file":"Chip.mjs","sources":["../../../src/components/Chip/Chip.tsx"],"sourcesContent":["import { Slot, Slottable } from \"@radix-ui/react-slot\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\n/** Visual variant of the chip. */\nexport type ChipVariant = \"rounded\" | \"square\" | \"dark\";\n/** Height of the chip in pixels. */\nexport type ChipSize = \"32\" | \"40\";\n\nexport interface ChipProps extends React.HTMLAttributes<HTMLElement> {\n /** Visual variant of the chip. @default \"rounded\" */\n variant?: ChipVariant;\n /** Height of the chip in pixels. @default \"32\" */\n size?: ChipSize;\n /** Whether the chip is in a selected (pressed) state. @default false */\n selected?: boolean;\n /** Whether the chip is disabled. @default false */\n disabled?: boolean;\n /** Whether to show a coloured status dot at the leading edge. @default false */\n leftDot?: boolean;\n /**\n * Whether the chip uses a dashed border for add/create affordances.\n * When `selected`, it becomes a subtle filled state with a solid border.\n * Has no effect when `variant=\"dark\"`.\n * @default false\n */\n dotted?: boolean;\n /** Icon element displayed before the label. */\n leftIcon?: React.ReactNode;\n /** Icon element displayed after the label. */\n rightIcon?: React.ReactNode;\n /** Notification badge content (e.g. `\"99+\"`). Passed as a string for i18n support. */\n notificationLabel?: string;\n /** Click handler — when provided, the chip renders as a `<button>` for accessibility. */\n onClick?: React.MouseEventHandler<HTMLElement>;\n /** Merge props onto a child element instead of rendering a wrapper. @default false */\n asChild?: boolean;\n}\n\n/**\n * A compact element for filters, tags, or toggleable actions. When an `onClick`\n * handler is provided, the chip renders as an interactive `<button>` with\n * `aria-pressed` support.\n *\n * @example\n * ```tsx\n * <Chip selected onClick={toggle}>Music</Chip>\n * ```\n */\nexport const Chip = React.forwardRef<HTMLButtonElement, ChipProps>(\n (\n {\n className,\n variant = \"rounded\",\n size = \"32\",\n selected = false,\n disabled = false,\n leftDot = false,\n dotted = false,\n leftIcon,\n rightIcon,\n notificationLabel,\n onClick,\n asChild = false,\n children,\n ...props\n },\n ref,\n // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Variant-heavy UI component\n ) => {\n const isInteractive = !!onClick && !asChild;\n const Comp = asChild ? Slot : isInteractive ? \"button\" : \"span\";\n const isDark = variant === \"dark\";\n\n // The dashed border is drawn as an SVG so the dash length matches the design\n // spec (8/8 for the 40px square, 6/6 otherwise). CSS `border-dashed` only\n // renders browser-default dash lengths, which are too short. The stroke colour\n // is driven by `currentColor`, and `group-hover`/`group-active` react to the\n // chip's interactive states. Rendered only in the default (non-`asChild`) path.\n const showDottedBorder = !isDark && dotted && !selected && !asChild;\n const dottedBorder = showDottedBorder ? (\n <svg\n aria-hidden=\"true\"\n className={cn(\n \"pointer-events-none absolute inset-0 size-full overflow-visible\",\n disabled ? \"text-buttons-chip-disabled\" : \"text-buttons-chip-dotted-default\",\n isInteractive &&\n !disabled &&\n \"group-hover:text-buttons-chip-dotted-hover-stroke group-active:text-buttons-chip-dotted-hover-stroke\",\n )}\n >\n <rect\n x=\"0.5\"\n y=\"0.5\"\n rx={variant === \"square\" ? 7.5 : size === \"40\" ? 19.5 : 15.5}\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1\"\n strokeDasharray={variant === \"square\" && size === \"40\" ? \"8 8\" : \"6 6\"}\n style={{ width: \"calc(100% - 1px)\", height: \"calc(100% - 1px)\" }}\n />\n </svg>\n ) : null;\n\n return (\n <Comp\n ref={ref}\n data-testid=\"chip\"\n className={cn(\n \"relative inline-flex min-w-0 items-center justify-center whitespace-nowrap motion-safe:transition-colors motion-safe:duration-150\",\n // Shape\n variant === \"square\" ? \"rounded-xs\" : \"rounded-full\",\n // Size (32px uses 12px text, 40px uses 14px text per the design spec)\n size === \"32\" && \"typography-description-12px-semibold h-8 py-1\",\n size === \"40\" && \"typography-body-small-14px-semibold h-10 py-2.5\",\n // Variant colors\n isDark && \"bg-neutral-alphas-600 text-content-always-white\",\n !isDark && selected && !dotted && \"bg-buttons-chip-active text-content-primary-inverted\",\n // Active + dotted is a subtle filled state with a solid (non-dashed) border.\n !isDark &&\n selected &&\n dotted &&\n \"border border-border-primary border-solid bg-inputs-inputs-primary text-content-primary\",\n !isDark && !selected && !dotted && \"bg-buttons-chip-default text-content-primary\",\n // Dotted (non-selected): the dashed border is drawn via SVG (`dottedBorder`).\n // `group` lets that SVG react to hover/active. `asChild` keeps a CSS dashed\n // border fallback since the SVG is only rendered in the default path.\n !isDark && !selected && dotted && \"bg-transparent text-content-primary\",\n !isDark && !selected && dotted && !asChild && \"group\",\n asChild &&\n !isDark &&\n !selected &&\n dotted &&\n (disabled\n ? \"border border-buttons-chip-disabled border-dashed\"\n : \"border border-buttons-chip-dotted-default border-dashed\"),\n // Interactive\n isInteractive && !disabled && \"cursor-pointer\",\n isInteractive &&\n !disabled &&\n !isDark &&\n !selected &&\n !dotted &&\n \"hover:bg-buttons-chip-hover\",\n isInteractive &&\n !disabled &&\n !isDark &&\n !selected &&\n dotted &&\n \"hover:bg-neutral-alphas-50 active:bg-neutral-alphas-50\",\n // Focus\n \"focus-visible:shadow-focus-ring focus-visible:outline-none\",\n // Disabled\n disabled && isDark && \"pointer-events-none opacity-50\",\n disabled && !isDark && \"pointer-events-none text-content-disabled\",\n // Solid (non-dotted) disabled chips get a muted fill; dotted ones stay\n // transparent with their dashed border (drawn via SVG).\n disabled && !isDark && !dotted && \"bg-buttons-chip-disabled\",\n className,\n )}\n {...(isInteractive && {\n type: \"button\" as const,\n disabled,\n \"aria-pressed\": selected,\n onClick,\n })}\n {...(!isInteractive && disabled && { \"aria-disabled\": true })}\n {...(selected && { \"data-selected\": \"\" })}\n {...props}\n >\n {asChild ? (\n <Slottable>{children}</Slottable>\n ) : (\n <>\n {dottedBorder}\n <span className=\"flex min-w-0 items-center gap-0.5 overflow-hidden px-3\">\n {leftDot && (\n <span className=\"size-2 shrink-0 rounded-full bg-current\" aria-hidden=\"true\" />\n )}\n {leftIcon && (\n <span className=\"flex shrink-0 items-center justify-center\" aria-hidden=\"true\">\n {leftIcon}\n </span>\n )}\n <span className=\"min-w-0 truncate\">{children}</span>\n {rightIcon && (\n <span\n className=\"flex size-5 shrink-0 items-center justify-center\"\n aria-hidden=\"true\"\n >\n {rightIcon}\n </span>\n )}\n </span>\n {notificationLabel && (\n <span className=\"typography-description-12px-semibold absolute -top-1 -right-1 flex h-4 min-w-4 items-center justify-center rounded-full bg-content-primary px-1 text-content-primary-inverted\">\n {notificationLabel}\n </span>\n )}\n </>\n )}\n </Comp>\n );\n },\n);\n\nChip.displayName = \"Chip\";\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Chip.mjs","sources":["../../../src/components/Chip/Chip.tsx"],"sourcesContent":["import { Slot, Slottable } from \"@radix-ui/react-slot\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { Count, type CountVariant } from \"../Count/Count\";\n\n/** Visual variant of the chip. */\nexport type ChipVariant = \"rounded\" | \"square\" | \"dark\";\n/** Height of the chip in pixels. */\nexport type ChipSize = \"32\" | \"40\";\n\nexport interface ChipProps extends React.HTMLAttributes<HTMLElement> {\n /** Visual variant of the chip. @default \"rounded\" */\n variant?: ChipVariant;\n /** Height of the chip in pixels. @default \"32\" */\n size?: ChipSize;\n /** Whether the chip is in a selected (pressed) state. @default false */\n selected?: boolean;\n /** Whether the chip is disabled. @default false */\n disabled?: boolean;\n /** Whether to show a coloured status dot at the leading edge. @default false */\n leftDot?: boolean;\n /**\n * Whether the chip uses a dashed border for add/create affordances.\n * When `selected`, it becomes a subtle filled state with a solid border.\n * Has no effect when `variant=\"dark\"`.\n * @default false\n */\n dotted?: boolean;\n /** Icon element displayed before the label. */\n leftIcon?: React.ReactNode;\n /** Icon element displayed after the label. */\n rightIcon?: React.ReactNode;\n /** Notification badge content (e.g. `\"99+\"`). Passed as a string for i18n support. */\n notificationLabel?: string;\n /** Numeric value for the notification badge. Uses the `Count` component for overflow formatting. Takes precedence over `notificationLabel` when both are provided. */\n notificationCount?: number;\n /** Maximum value before the badge shows overflow (e.g. `\"9+\"`). Only applies when `notificationCount` is set. @default 99 */\n notificationMax?: number;\n /** Colour variant of the notification badge. @default \"brand\" */\n notificationVariant?: CountVariant;\n /** Click handler — when provided, the chip renders as a `<button>` for accessibility. */\n onClick?: React.MouseEventHandler<HTMLElement>;\n /** Merge props onto a child element instead of rendering a wrapper. @default false */\n asChild?: boolean;\n}\n\n/**\n * A compact element for filters, tags, or toggleable actions. When an `onClick`\n * handler is provided, the chip renders as an interactive `<button>` with\n * `aria-pressed` support.\n *\n * @example\n * ```tsx\n * <Chip selected onClick={toggle}>Music</Chip>\n * ```\n */\nexport const Chip = React.forwardRef<HTMLButtonElement, ChipProps>(\n (\n {\n className,\n variant = \"rounded\",\n size = \"32\",\n selected = false,\n disabled = false,\n leftDot = false,\n dotted = false,\n leftIcon,\n rightIcon,\n notificationLabel,\n notificationCount,\n notificationMax,\n notificationVariant = \"brand\",\n onClick,\n asChild = false,\n children,\n ...props\n },\n ref,\n // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Variant-heavy UI component\n ) => {\n const isInteractive = !!onClick && !asChild;\n const Comp = asChild ? Slot : isInteractive ? \"button\" : \"span\";\n const isDark = variant === \"dark\";\n\n // The dashed border is drawn as an SVG so the dash length matches the design\n // spec (8/8 for the 40px square, 6/6 otherwise). CSS `border-dashed` only\n // renders browser-default dash lengths, which are too short. The stroke colour\n // is driven by `currentColor`, and `group-hover`/`group-active` react to the\n // chip's interactive states. Rendered only in the default (non-`asChild`) path.\n const showDottedBorder = !isDark && dotted && !selected && !asChild;\n const dottedBorder = showDottedBorder ? (\n <svg\n aria-hidden=\"true\"\n className={cn(\n \"pointer-events-none absolute inset-0 size-full overflow-visible\",\n disabled ? \"text-buttons-chip-disabled\" : \"text-buttons-chip-dotted-default\",\n isInteractive &&\n !disabled &&\n \"group-hover:text-buttons-chip-dotted-hover-stroke group-active:text-buttons-chip-dotted-hover-stroke\",\n )}\n >\n <rect\n x=\"0.5\"\n y=\"0.5\"\n rx={variant === \"square\" ? 7.5 : size === \"40\" ? 19.5 : 15.5}\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1\"\n strokeDasharray={variant === \"square\" && size === \"40\" ? \"8 8\" : \"6 6\"}\n style={{ width: \"calc(100% - 1px)\", height: \"calc(100% - 1px)\" }}\n />\n </svg>\n ) : null;\n\n return (\n <Comp\n ref={ref}\n data-testid=\"chip\"\n className={cn(\n \"relative inline-flex min-w-0 items-center justify-center whitespace-nowrap motion-safe:transition-colors motion-safe:duration-150\",\n // Shape\n variant === \"square\" ? \"rounded-xs\" : \"rounded-full\",\n // Size (32px uses 12px text, 40px uses 14px text per the design spec)\n size === \"32\" && \"typography-description-12px-semibold h-8 py-1\",\n size === \"40\" && \"typography-body-small-14px-semibold h-10 py-2.5\",\n // Variant colors\n isDark && \"bg-neutral-alphas-600 text-content-always-white\",\n !isDark && selected && !dotted && \"bg-buttons-chip-active text-content-primary-inverted\",\n // Active + dotted is a subtle filled state with a solid (non-dashed) border.\n !isDark &&\n selected &&\n dotted &&\n \"border border-border-primary border-solid bg-inputs-inputs-primary text-content-primary\",\n !isDark && !selected && !dotted && \"bg-buttons-chip-default text-content-primary\",\n // Dotted (non-selected): the dashed border is drawn via SVG (`dottedBorder`).\n // `group` lets that SVG react to hover/active. `asChild` keeps a CSS dashed\n // border fallback since the SVG is only rendered in the default path.\n !isDark && !selected && dotted && \"bg-transparent text-content-primary\",\n !isDark && !selected && dotted && !asChild && \"group\",\n asChild &&\n !isDark &&\n !selected &&\n dotted &&\n (disabled\n ? \"border border-buttons-chip-disabled border-dashed\"\n : \"border border-buttons-chip-dotted-default border-dashed\"),\n // Interactive\n isInteractive && !disabled && \"cursor-pointer\",\n isInteractive &&\n !disabled &&\n !isDark &&\n !selected &&\n !dotted &&\n \"hover:bg-buttons-chip-hover\",\n isInteractive &&\n !disabled &&\n !isDark &&\n !selected &&\n dotted &&\n \"hover:bg-neutral-alphas-50 active:bg-neutral-alphas-50\",\n // Focus\n \"focus-visible:shadow-focus-ring focus-visible:outline-none\",\n // Disabled\n disabled && isDark && \"pointer-events-none opacity-50\",\n disabled && !isDark && \"pointer-events-none text-content-disabled\",\n // Solid (non-dotted) disabled chips get a muted fill; dotted ones stay\n // transparent with their dashed border (drawn via SVG).\n disabled && !isDark && !dotted && \"bg-buttons-chip-disabled\",\n className,\n )}\n {...(isInteractive && {\n type: \"button\" as const,\n disabled,\n \"aria-pressed\": selected,\n onClick,\n })}\n {...(!isInteractive && disabled && { \"aria-disabled\": true })}\n {...(selected && { \"data-selected\": \"\" })}\n {...props}\n >\n {asChild ? (\n <Slottable>{children}</Slottable>\n ) : (\n <>\n {dottedBorder}\n <span className=\"flex min-w-0 items-center gap-0.5 overflow-hidden px-3\">\n {leftDot && (\n <span className=\"size-2 shrink-0 rounded-full bg-current\" aria-hidden=\"true\" />\n )}\n {leftIcon && (\n <span className=\"flex shrink-0 items-center justify-center\" aria-hidden=\"true\">\n {leftIcon}\n </span>\n )}\n <span className=\"min-w-0 truncate\">{children}</span>\n {rightIcon && (\n <span\n className=\"flex size-5 shrink-0 items-center justify-center\"\n aria-hidden=\"true\"\n >\n {rightIcon}\n </span>\n )}\n </span>\n {(notificationCount != null || notificationLabel) && (\n <Count\n variant={notificationVariant}\n size=\"32\"\n className={cn(\n // V2 Notification Count: 20px high/min-width, caption 12,\n // 4px horizontal and 2px vertical padding, rounded-md.\n \"absolute top-[-4px] rounded-md px-1 py-0.5\",\n // Anchor the leading edge so wider values grow away from the label.\n size === \"40\" ? \"left-[calc(100%_-_16px)]\" : \"left-[calc(100%_-_11px)]\",\n )}\n value={notificationCount ?? 0}\n max={notificationMax}\n >\n {notificationCount == null ? notificationLabel : undefined}\n </Count>\n )}\n </>\n )}\n </Comp>\n );\n },\n);\n\nChip.displayName = \"Chip\";\n"],"names":[],"mappings":"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"}
@@ -6,12 +6,33 @@ import { cn } from "../../utils/cn.mjs";
6
6
  function getDisplayValue(value, max) {
7
7
  return value > max ? `${max}+` : value.toString();
8
8
  }
9
+ function variantClasses(variant) {
10
+ switch (variant) {
11
+ case "default":
12
+ return "bg-content-primary text-content-primary-inverted";
13
+ case "contrast":
14
+ return "bg-content-always-white text-content-always-black";
15
+ case "brand":
16
+ return "bg-brand-primary-default text-content-always-black";
17
+ case "alert":
18
+ return "bg-error-content text-content-always-white";
19
+ case "pink":
20
+ return "bg-brand-secondary-default text-content-always-black";
21
+ case "info":
22
+ return "bg-info-content text-content-always-white";
23
+ case "success":
24
+ return "bg-success-content text-content-always-white";
25
+ case "warning":
26
+ return "bg-warning-content text-content-always-black";
27
+ }
28
+ }
9
29
  const Count = React.forwardRef(
10
30
  ({
11
31
  className,
12
32
  variant = "default",
13
33
  value = 0,
14
34
  max = 99,
35
+ showAmount = true,
15
36
  size = "32",
16
37
  asChild = false,
17
38
  children,
@@ -21,22 +42,30 @@ const Count = React.forwardRef(
21
42
  return null;
22
43
  }
23
44
  const Comp = asChild ? Slot : "span";
45
+ const colors = variantClasses(variant);
46
+ if (!showAmount) {
47
+ return /* @__PURE__ */ jsx(
48
+ Comp,
49
+ {
50
+ ref,
51
+ "data-testid": "count",
52
+ className: cn("inline-flex size-2 shrink-0 rounded-full", colors, className),
53
+ ...props
54
+ }
55
+ );
56
+ }
24
57
  return /* @__PURE__ */ jsx(
25
58
  Comp,
26
59
  {
27
60
  ref,
61
+ "data-testid": "count",
28
62
  className: cn(
29
63
  "typography-description-12px-semibold inline-flex shrink-0 items-center justify-center rounded-full tabular-nums leading-none",
30
64
  size === "16" && "h-3 min-w-3 px-0.5 text-[8px]",
65
+ // Kept at 16px so nav / IconButton badges don't grow; Chip nests this size.
31
66
  size === "24" && "h-4 min-w-4 px-1 text-[10px]",
32
67
  size === "32" && "h-5 min-w-5 px-1.5 text-[12px]",
33
- variant === "default" && "bg-content-primary text-content-primary-inverted",
34
- variant === "alert" && "bg-error-content text-content-always-white",
35
- variant === "brand" && "bg-brand-primary-default text-content-always-black",
36
- variant === "pink" && "bg-brand-secondary-default text-content-always-black",
37
- variant === "info" && "bg-info-content text-content-always-white",
38
- variant === "success" && "bg-success-content text-content-always-white",
39
- variant === "warning" && "bg-warning-content text-content-always-black",
68
+ colors,
40
69
  className
41
70
  ),
42
71
  ...props,
@@ -1 +1 @@
1
- {"version":3,"file":"Count.mjs","sources":["../../../src/components/Count/Count.tsx"],"sourcesContent":["import { Slot } from \"@radix-ui/react-slot\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\n/** Colour variant for the count badge. */\nexport type CountVariant = \"default\" | \"alert\" | \"brand\" | \"pink\" | \"info\" | \"success\" | \"warning\";\n\n/** Size of the count badge, aligned with button and icon-button sizes. */\nexport type CountSize = \"16\" | \"24\" | \"32\";\n\nfunction getDisplayValue(value: number, max: number): string {\n return value > max ? `${max}+` : value.toString();\n}\n\nexport interface CountProps extends React.HTMLAttributes<HTMLSpanElement> {\n /** Colour variant of the count badge. @default \"default\" */\n variant?: CountVariant;\n /** Numeric value to display. Renders nothing when `0` and no `children` are provided. @default 0 */\n value?: number;\n /** Maximum value before showing overflow (e.g. `\"99+\"`). @default 99 */\n max?: number;\n /** Size of the count badge. @default \"32\" */\n size?: CountSize;\n /** Merge props onto a child element instead of rendering a `<span>`. @default false */\n asChild?: boolean;\n}\n\n/**\n * A numeric badge typically used for notification counts. Automatically\n * truncates values above `max` (e.g. `\"99+\"`). Renders nothing when the\n * value is `0` and no children are provided.\n *\n * @example\n * ```tsx\n * <Count value={5} variant=\"brand\" />\n * ```\n */\nexport const Count = React.forwardRef<HTMLSpanElement, CountProps>(\n (\n {\n className,\n variant = \"default\",\n value = 0,\n max = 99,\n size = \"32\",\n asChild = false,\n children,\n ...props\n },\n ref,\n ) => {\n if (value === 0 && !children) {\n return null;\n }\n\n const Comp = asChild ? Slot : \"span\";\n\n return (\n <Comp\n ref={ref}\n className={cn(\n \"typography-description-12px-semibold inline-flex shrink-0 items-center justify-center rounded-full tabular-nums leading-none\",\n size === \"16\" && \"h-3 min-w-3 px-0.5 text-[8px]\",\n size === \"24\" && \"h-4 min-w-4 px-1 text-[10px]\",\n size === \"32\" && \"h-5 min-w-5 px-1.5 text-[12px]\",\n variant === \"default\" && \"bg-content-primary text-content-primary-inverted\",\n variant === \"alert\" && \"bg-error-content text-content-always-white\",\n variant === \"brand\" && \"bg-brand-primary-default text-content-always-black\",\n variant === \"pink\" && \"bg-brand-secondary-default text-content-always-black\",\n variant === \"info\" && \"bg-info-content text-content-always-white\",\n variant === \"success\" && \"bg-success-content text-content-always-white\",\n variant === \"warning\" && \"bg-warning-content text-content-always-black\",\n className,\n )}\n {...props}\n >\n {children ?? getDisplayValue(value, max)}\n </Comp>\n );\n },\n);\n\nCount.displayName = \"Count\";\n"],"names":[],"mappings":";;;;;AAUA,SAAS,gBAAgB,OAAe,KAAqB;AAC3D,SAAO,QAAQ,MAAM,GAAG,GAAG,MAAM,MAAM,SAAA;AACzC;AAyBO,MAAM,QAAQ,MAAM;AAAA,EACzB,CACE;AAAA,IACE;AAAA,IACA,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,OAAO;AAAA,IACP,UAAU;AAAA,IACV;AAAA,IACA,GAAG;AAAA,EAAA,GAEL,QACG;AACH,QAAI,UAAU,KAAK,CAAC,UAAU;AAC5B,aAAO;AAAA,IACT;AAEA,UAAM,OAAO,UAAU,OAAO;AAE9B,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,WAAW;AAAA,UACT;AAAA,UACA,SAAS,QAAQ;AAAA,UACjB,SAAS,QAAQ;AAAA,UACjB,SAAS,QAAQ;AAAA,UACjB,YAAY,aAAa;AAAA,UACzB,YAAY,WAAW;AAAA,UACvB,YAAY,WAAW;AAAA,UACvB,YAAY,UAAU;AAAA,UACtB,YAAY,UAAU;AAAA,UACtB,YAAY,aAAa;AAAA,UACzB,YAAY,aAAa;AAAA,UACzB;AAAA,QAAA;AAAA,QAED,GAAG;AAAA,QAEH,UAAA,YAAY,gBAAgB,OAAO,GAAG;AAAA,MAAA;AAAA,IAAA;AAAA,EAG7C;AACF;AAEA,MAAM,cAAc;"}
1
+ {"version":3,"file":"Count.mjs","sources":["../../../src/components/Count/Count.tsx"],"sourcesContent":["import { Slot } from \"@radix-ui/react-slot\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\n/**\n * Colour variant for the count badge.\n *\n * V2 Notification Count (Fanvue Library) types: `default`, `brand`, `contrast`.\n * Remaining values are retained extensions for existing consumers.\n */\nexport type CountVariant =\n | \"default\"\n | \"contrast\"\n | \"brand\"\n | \"alert\"\n | \"pink\"\n | \"info\"\n | \"success\"\n | \"warning\";\n\n/** Size of the count badge, aligned with button and icon-button sizes. */\nexport type CountSize = \"16\" | \"24\" | \"32\";\n\nfunction getDisplayValue(value: number, max: number): string {\n return value > max ? `${max}+` : value.toString();\n}\n\nfunction variantClasses(variant: CountVariant): string {\n switch (variant) {\n case \"default\":\n return \"bg-content-primary text-content-primary-inverted\";\n case \"contrast\":\n // V2: on dark or coloured hosts (filled buttons, images)\n return \"bg-content-always-white text-content-always-black\";\n case \"brand\":\n return \"bg-brand-primary-default text-content-always-black\";\n case \"alert\":\n return \"bg-error-content text-content-always-white\";\n case \"pink\":\n return \"bg-brand-secondary-default text-content-always-black\";\n case \"info\":\n return \"bg-info-content text-content-always-white\";\n case \"success\":\n return \"bg-success-content text-content-always-white\";\n case \"warning\":\n return \"bg-warning-content text-content-always-black\";\n }\n}\n\nexport interface CountProps extends React.HTMLAttributes<HTMLSpanElement> {\n /**\n * Colour variant of the count badge.\n * Figma V2 types: `default` | `brand` | `contrast`. @default \"default\"\n */\n variant?: CountVariant;\n /** Numeric value to display. Renders nothing when `0` and no `children` are provided. @default 0 */\n value?: number;\n /** Maximum value before showing overflow (e.g. `\"99+\"`). @default 99 */\n max?: number;\n /**\n * When `false`, renders an 8px unread dot instead of a numeric amount\n * (Figma Show Amount=False). @default true\n */\n showAmount?: boolean;\n /** Size of the count badge. @default \"32\" */\n size?: CountSize;\n /** Merge props onto a child element instead of rendering a `<span>`. @default false */\n asChild?: boolean;\n}\n\n/**\n * V2 Notification Count a small badge for unread activity on nav items,\n * icons, buttons, or chips. Truncates values above `max` (e.g. `\"99+\"`).\n * Renders nothing when the value is `0` and no children are provided.\n *\n * @example\n * ```tsx\n * <Count value={5} variant=\"brand\" />\n * <Count value={1} variant=\"brand\" showAmount={false} />\n * ```\n */\nexport const Count = React.forwardRef<HTMLSpanElement, CountProps>(\n (\n {\n className,\n variant = \"default\",\n value = 0,\n max = 99,\n showAmount = true,\n size = \"32\",\n asChild = false,\n children,\n ...props\n },\n ref,\n ) => {\n if (value === 0 && !children) {\n return null;\n }\n\n const Comp = asChild ? Slot : \"span\";\n const colors = variantClasses(variant);\n\n if (!showAmount) {\n return (\n <Comp\n ref={ref}\n data-testid=\"count\"\n className={cn(\"inline-flex size-2 shrink-0 rounded-full\", colors, className)}\n {...props}\n />\n );\n }\n\n return (\n <Comp\n ref={ref}\n data-testid=\"count\"\n className={cn(\n \"typography-description-12px-semibold inline-flex shrink-0 items-center justify-center rounded-full tabular-nums leading-none\",\n size === \"16\" && \"h-3 min-w-3 px-0.5 text-[8px]\",\n // Kept at 16px so nav / IconButton badges don't grow; Chip nests this size.\n size === \"24\" && \"h-4 min-w-4 px-1 text-[10px]\",\n size === \"32\" && \"h-5 min-w-5 px-1.5 text-[12px]\",\n colors,\n className,\n )}\n {...props}\n >\n {children ?? getDisplayValue(value, max)}\n </Comp>\n );\n },\n);\n\nCount.displayName = \"Count\";\n"],"names":[],"mappings":";;;;;AAuBA,SAAS,gBAAgB,OAAe,KAAqB;AAC3D,SAAO,QAAQ,MAAM,GAAG,GAAG,MAAM,MAAM,SAAA;AACzC;AAEA,SAAS,eAAe,SAA+B;AACrD,UAAQ,SAAA;AAAA,IACN,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAEH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,EAAA;AAEb;AAkCO,MAAM,QAAQ,MAAM;AAAA,EACzB,CACE;AAAA,IACE;AAAA,IACA,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,aAAa;AAAA,IACb,OAAO;AAAA,IACP,UAAU;AAAA,IACV;AAAA,IACA,GAAG;AAAA,EAAA,GAEL,QACG;AACH,QAAI,UAAU,KAAK,CAAC,UAAU;AAC5B,aAAO;AAAA,IACT;AAEA,UAAM,OAAO,UAAU,OAAO;AAC9B,UAAM,SAAS,eAAe,OAAO;AAErC,QAAI,CAAC,YAAY;AACf,aACE;AAAA,QAAC;AAAA,QAAA;AAAA,UACC;AAAA,UACA,eAAY;AAAA,UACZ,WAAW,GAAG,4CAA4C,QAAQ,SAAS;AAAA,UAC1E,GAAG;AAAA,QAAA;AAAA,MAAA;AAAA,IAGV;AAEA,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,eAAY;AAAA,QACZ,WAAW;AAAA,UACT;AAAA,UACA,SAAS,QAAQ;AAAA;AAAA,UAEjB,SAAS,QAAQ;AAAA,UACjB,SAAS,QAAQ;AAAA,UACjB;AAAA,UACA;AAAA,QAAA;AAAA,QAED,GAAG;AAAA,QAEH,UAAA,YAAY,gBAAgB,OAAO,GAAG;AAAA,MAAA;AAAA,IAAA;AAAA,EAG7C;AACF;AAEA,MAAM,cAAc;"}
package/dist/index.d.ts CHANGED
@@ -715,7 +715,7 @@ export declare interface BadgeProps extends React_2.HTMLAttributes<HTMLSpanEleme
715
715
  }
716
716
 
717
717
  /** Visual style variant of the badge. */
718
- export declare type BadgeVariant = "default" | "dark" | "success" | "warning" | "error" | "special" | "info" | "successColour" | "warningColour" | "errorColour" | "infoColour" | "aiGenerated" | "negative" | "online" | "brand" | "pink" | "brandLight" | "pinkLight";
718
+ export declare type BadgeVariant = "default" | "dark" | "success" | "warning" | "error" | "special" | "info" | "successColour" | "warningColour" | "errorColour" | "infoColour" | "aiGenerated" | "negative" | "alwaysWhite" | "online" | "brand" | "pink" | "brandLight" | "pinkLight";
719
719
 
720
720
  /**
721
721
  * Bank icon. Renders at sizes 16, 24, or 32 px with outlined and filled variants.
@@ -1530,6 +1530,12 @@ export declare interface ChipProps extends React_2.HTMLAttributes<HTMLElement> {
1530
1530
  rightIcon?: React_2.ReactNode;
1531
1531
  /** Notification badge content (e.g. `"99+"`). Passed as a string for i18n support. */
1532
1532
  notificationLabel?: string;
1533
+ /** Numeric value for the notification badge. Uses the `Count` component for overflow formatting. Takes precedence over `notificationLabel` when both are provided. */
1534
+ notificationCount?: number;
1535
+ /** Maximum value before the badge shows overflow (e.g. `"9+"`). Only applies when `notificationCount` is set. @default 99 */
1536
+ notificationMax?: number;
1537
+ /** Colour variant of the notification badge. @default "brand" */
1538
+ notificationVariant?: CountVariant;
1533
1539
  /** Click handler — when provided, the chip renders as a `<button>` for accessibility. */
1534
1540
  onClick?: React_2.MouseEventHandler<HTMLElement>;
1535
1541
  /** Merge props onto a child element instead of rendering a wrapper. @default false */
@@ -1636,24 +1642,33 @@ export declare const CopyIcon: React_2.ForwardRefExoticComponent<BaseIconProps &
1636
1642
  export declare type CopyIconProps = BaseIconProps;
1637
1643
 
1638
1644
  /**
1639
- * A numeric badge typically used for notification counts. Automatically
1640
- * truncates values above `max` (e.g. `"99+"`). Renders nothing when the
1641
- * value is `0` and no children are provided.
1645
+ * V2 Notification Count a small badge for unread activity on nav items,
1646
+ * icons, buttons, or chips. Truncates values above `max` (e.g. `"99+"`).
1647
+ * Renders nothing when the value is `0` and no children are provided.
1642
1648
  *
1643
1649
  * @example
1644
1650
  * ```tsx
1645
1651
  * <Count value={5} variant="brand" />
1652
+ * <Count value={1} variant="brand" showAmount={false} />
1646
1653
  * ```
1647
1654
  */
1648
1655
  export declare const Count: React_2.ForwardRefExoticComponent<CountProps & React_2.RefAttributes<HTMLSpanElement>>;
1649
1656
 
1650
1657
  export declare interface CountProps extends React_2.HTMLAttributes<HTMLSpanElement> {
1651
- /** Colour variant of the count badge. @default "default" */
1658
+ /**
1659
+ * Colour variant of the count badge.
1660
+ * Figma V2 types: `default` | `brand` | `contrast`. @default "default"
1661
+ */
1652
1662
  variant?: CountVariant;
1653
1663
  /** Numeric value to display. Renders nothing when `0` and no `children` are provided. @default 0 */
1654
1664
  value?: number;
1655
1665
  /** Maximum value before showing overflow (e.g. `"99+"`). @default 99 */
1656
1666
  max?: number;
1667
+ /**
1668
+ * When `false`, renders an 8px unread dot instead of a numeric amount
1669
+ * (Figma Show Amount=False). @default true
1670
+ */
1671
+ showAmount?: boolean;
1657
1672
  /** Size of the count badge. @default "32" */
1658
1673
  size?: CountSize;
1659
1674
  /** Merge props onto a child element instead of rendering a `<span>`. @default false */
@@ -1663,8 +1678,13 @@ export declare interface CountProps extends React_2.HTMLAttributes<HTMLSpanEleme
1663
1678
  /** Size of the count badge, aligned with button and icon-button sizes. */
1664
1679
  export declare type CountSize = "16" | "24" | "32";
1665
1680
 
1666
- /** Colour variant for the count badge. */
1667
- export declare type CountVariant = "default" | "alert" | "brand" | "pink" | "info" | "success" | "warning";
1681
+ /**
1682
+ * Colour variant for the count badge.
1683
+ *
1684
+ * V2 Notification Count (Fanvue Library) types: `default`, `brand`, `contrast`.
1685
+ * Remaining values are retained extensions for existing consumers.
1686
+ */
1687
+ export declare type CountVariant = "default" | "contrast" | "brand" | "alert" | "pink" | "info" | "success" | "warning";
1668
1688
 
1669
1689
  /**
1670
1690
  * A portrait media card highlighting a creator with avatar, name, optional
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fanvue/ui",
3
- "version": "3.18.0",
3
+ "version": "3.19.0",
4
4
  "description": "React component library built with Tailwind CSS for Fanvue ecosystem",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org",