@fanvue/ui 3.21.1 → 3.23.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.
- package/dist/charts.d.ts +15 -7
- package/dist/cjs/components/ButtonIconGroup/ButtonIconGroup.cjs +58 -0
- package/dist/cjs/components/ButtonIconGroup/ButtonIconGroup.cjs.map +1 -0
- package/dist/cjs/components/Chart/ChartCard.cjs +67 -42
- package/dist/cjs/components/Chart/ChartCard.cjs.map +1 -1
- package/dist/cjs/components/CreatorTile/CreatorTile.cjs +2 -1
- package/dist/cjs/components/CreatorTile/CreatorTile.cjs.map +1 -1
- package/dist/cjs/components/UserDisplayName/UserDisplayName.cjs +13 -1
- package/dist/cjs/components/UserDisplayName/UserDisplayName.cjs.map +1 -1
- package/dist/cjs/components/UserItem/UserItem.cjs +15 -1
- package/dist/cjs/components/UserItem/UserItem.cjs.map +1 -1
- package/dist/cjs/index.cjs +2 -0
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/components/ButtonIconGroup/ButtonIconGroup.mjs +41 -0
- package/dist/components/ButtonIconGroup/ButtonIconGroup.mjs.map +1 -0
- package/dist/components/Chart/ChartCard.mjs +67 -42
- package/dist/components/Chart/ChartCard.mjs.map +1 -1
- package/dist/components/CreatorTile/CreatorTile.mjs +2 -1
- package/dist/components/CreatorTile/CreatorTile.mjs.map +1 -1
- package/dist/components/UserDisplayName/UserDisplayName.mjs +13 -1
- package/dist/components/UserDisplayName/UserDisplayName.mjs.map +1 -1
- package/dist/components/UserItem/UserItem.mjs +15 -1
- package/dist/components/UserItem/UserItem.mjs.map +1 -1
- package/dist/index.d.ts +77 -8
- package/dist/index.mjs +2 -0
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
package/dist/charts.d.ts
CHANGED
|
@@ -12,11 +12,12 @@ export declare type CardHierarchy = "primary" | "secondary";
|
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Wraps any chart with a structured header containing title, subtitle,
|
|
15
|
-
* optional trend
|
|
15
|
+
* optional trend indicator, date range label, info tooltip, and a loading
|
|
16
16
|
* skeleton state.
|
|
17
17
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
18
|
+
* At `hierarchy="primary"` this implements the V2 Insight Card surface, which
|
|
19
|
+
* differs from {@link Card}'s own primary hierarchy: a white fill, the strong
|
|
20
|
+
* border, and a 12px radius.
|
|
20
21
|
*
|
|
21
22
|
* @example
|
|
22
23
|
* ```tsx
|
|
@@ -35,7 +36,11 @@ export declare const ChartCard: React_2.ForwardRefExoticComponent<ChartCardProps
|
|
|
35
36
|
|
|
36
37
|
/** Props for {@link ChartCard}. */
|
|
37
38
|
export declare interface ChartCardProps extends Omit<React_2.HTMLAttributes<HTMLDivElement>, "title"> {
|
|
38
|
-
/**
|
|
39
|
+
/**
|
|
40
|
+
* Surface treatment. `primary` follows the V2 Insight Card spec (white fill,
|
|
41
|
+
* strong border, 12px radius); `secondary` uses the {@link Card} secondary
|
|
42
|
+
* surface. @default "primary"
|
|
43
|
+
*/
|
|
39
44
|
hierarchy?: CardHierarchy;
|
|
40
45
|
/** Card title text. Pass translated string for i18n. */
|
|
41
46
|
title: React_2.ReactNode;
|
|
@@ -47,11 +52,14 @@ export declare interface ChartCardProps extends Omit<React_2.HTMLAttributes<HTML
|
|
|
47
52
|
tooltipAriaLabel?: string;
|
|
48
53
|
/** Date range or period label shown below the subtitle. */
|
|
49
54
|
dateInfo?: React_2.ReactNode;
|
|
50
|
-
/**
|
|
55
|
+
/**
|
|
56
|
+
* Trend indicator config. Rendered as a coloured directional arrow and label
|
|
57
|
+
* beside the subtitle, so it is only shown when {@link subtitle} is provided.
|
|
58
|
+
*/
|
|
51
59
|
trendChip?: {
|
|
52
|
-
/** Display label (e.g. "12.5%"). */
|
|
60
|
+
/** Display label (e.g. "12.5% vs prev"). */
|
|
53
61
|
label: React_2.ReactNode;
|
|
54
|
-
/** Whether the trend is positive (green) or negative (red). */
|
|
62
|
+
/** Whether the trend is positive (green, arrow up) or negative (red, arrow down). */
|
|
55
63
|
trend: "positive" | "negative";
|
|
56
64
|
};
|
|
57
65
|
/** Show loading skeleton instead of content. @default false */
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
4
|
+
const jsxRuntime = require("react/jsx-runtime");
|
|
5
|
+
const React = require("react");
|
|
6
|
+
const cn = require("../../utils/cn.cjs");
|
|
7
|
+
function _interopNamespaceDefault(e) {
|
|
8
|
+
const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
|
|
9
|
+
if (e) {
|
|
10
|
+
for (const k in e) {
|
|
11
|
+
if (k !== "default") {
|
|
12
|
+
const d = Object.getOwnPropertyDescriptor(e, k);
|
|
13
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
14
|
+
enumerable: true,
|
|
15
|
+
get: () => e[k]
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
n.default = e;
|
|
21
|
+
return Object.freeze(n);
|
|
22
|
+
}
|
|
23
|
+
const React__namespace = /* @__PURE__ */ _interopNamespaceDefault(React);
|
|
24
|
+
const ORIENTATION_AWARE_ROLES = /* @__PURE__ */ new Set([
|
|
25
|
+
"toolbar",
|
|
26
|
+
"menu",
|
|
27
|
+
"menubar",
|
|
28
|
+
"listbox",
|
|
29
|
+
"radiogroup",
|
|
30
|
+
"tablist",
|
|
31
|
+
"tree"
|
|
32
|
+
]);
|
|
33
|
+
const ButtonIconGroup = React__namespace.forwardRef(
|
|
34
|
+
({ className, orientation = "horizontal", role = "group", children, ...props }, ref) => {
|
|
35
|
+
const supportsOrientation = typeof role === "string" && ORIENTATION_AWARE_ROLES.has(role);
|
|
36
|
+
const orientationProps = supportsOrientation ? { "aria-orientation": orientation } : {};
|
|
37
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
38
|
+
"div",
|
|
39
|
+
{
|
|
40
|
+
ref,
|
|
41
|
+
role,
|
|
42
|
+
...orientationProps,
|
|
43
|
+
"data-testid": "button-icon-group",
|
|
44
|
+
"data-orientation": orientation,
|
|
45
|
+
className: cn.cn(
|
|
46
|
+
"inline-flex items-center gap-1",
|
|
47
|
+
orientation === "vertical" && "flex-col",
|
|
48
|
+
className
|
|
49
|
+
),
|
|
50
|
+
...props,
|
|
51
|
+
children
|
|
52
|
+
}
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
);
|
|
56
|
+
ButtonIconGroup.displayName = "ButtonIconGroup";
|
|
57
|
+
exports.ButtonIconGroup = ButtonIconGroup;
|
|
58
|
+
//# sourceMappingURL=ButtonIconGroup.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ButtonIconGroup.cjs","sources":["../../../../src/components/ButtonIconGroup/ButtonIconGroup.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\n/** Orientation of the icon button cluster. */\nexport type ButtonIconGroupOrientation = \"horizontal\" | \"vertical\";\n\n// Roles whose ARIA semantics include an orientation. For these we mirror the\n// visual orientation so assistive tech isn't given the spec default\n// (horizontal) when the cluster is stacked vertically.\nconst ORIENTATION_AWARE_ROLES = new Set<string>([\n \"toolbar\",\n \"menu\",\n \"menubar\",\n \"listbox\",\n \"radiogroup\",\n \"tablist\",\n \"tree\",\n]);\n\nexport interface ButtonIconGroupProps extends React.HTMLAttributes<HTMLDivElement> {\n /**\n * Direction the icon buttons flow in. Use `vertical` for side rails and\n * stacked media controls. @default \"horizontal\"\n */\n orientation?: ButtonIconGroupOrientation;\n /**\n * The icon buttons to cluster together. Compose {@link IconButton} elements\n * here; the group only handles layout and spacing.\n */\n children: React.ReactNode;\n}\n\n/**\n * A pre-composed cluster of {@link IconButton}s for toolbars and action rows\n * (media controls, editing tools, content action bars). Items are spaced with a\n * fixed 4px gap and stay individually rounded — this is a spaced group, not a\n * segmented/joined control.\n *\n * Exposes `role=\"group\"` by default; pass an `aria-label` or `aria-labelledby`\n * to describe the cluster for assistive technology.\n *\n * This is a presentational layout primitive: it does not implement\n * roving-tabindex / arrow-key focus management. `role` is overridable, but if\n * you set an interactive-widget role such as `role=\"toolbar\"` you own the\n * keyboard behaviour that role implies. For orientation-aware roles the\n * `aria-orientation` is synced to `orientation` automatically.\n *\n * @example\n * ```tsx\n * <ButtonIconGroup aria-label=\"Media controls\">\n * <IconButton variant=\"tertiary\" size=\"48\" icon={<PlayIcon />} aria-label=\"Play\" />\n * <IconButton variant=\"tertiary\" size=\"48\" icon={<ForwardIcon />} aria-label=\"Skip\" />\n * </ButtonIconGroup>\n * ```\n */\nexport const ButtonIconGroup = React.forwardRef<HTMLDivElement, ButtonIconGroupProps>(\n ({ className, orientation = \"horizontal\", role = \"group\", children, ...props }, ref) => {\n const supportsOrientation = typeof role === \"string\" && ORIENTATION_AWARE_ROLES.has(role);\n // Spread rather than a static attribute so the value is only present for\n // orientation-aware roles (a bare div/group role does not support it).\n const orientationProps = supportsOrientation ? { \"aria-orientation\": orientation } : {};\n return (\n <div\n ref={ref}\n role={role}\n {...orientationProps}\n data-testid=\"button-icon-group\"\n data-orientation={orientation}\n className={cn(\n \"inline-flex items-center gap-1\",\n orientation === \"vertical\" && \"flex-col\",\n className,\n )}\n {...props}\n >\n {children}\n </div>\n );\n },\n);\n\nButtonIconGroup.displayName = \"ButtonIconGroup\";\n"],"names":["React","jsx","cn"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AASA,MAAM,8CAA8B,IAAY;AAAA,EAC9C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAsCM,MAAM,kBAAkBA,iBAAM;AAAA,EACnC,CAAC,EAAE,WAAW,cAAc,cAAc,OAAO,SAAS,UAAU,GAAG,MAAA,GAAS,QAAQ;AACtF,UAAM,sBAAsB,OAAO,SAAS,YAAY,wBAAwB,IAAI,IAAI;AAGxF,UAAM,mBAAmB,sBAAsB,EAAE,oBAAoB,YAAA,IAAgB,CAAA;AACrF,WACEC,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA;AAAA,QACC,GAAG;AAAA,QACJ,eAAY;AAAA,QACZ,oBAAkB;AAAA,QAClB,WAAWC,GAAAA;AAAAA,UACT;AAAA,UACA,gBAAgB,cAAc;AAAA,UAC9B;AAAA,QAAA;AAAA,QAED,GAAG;AAAA,QAEH;AAAA,MAAA;AAAA,IAAA;AAAA,EAGP;AACF;AAEA,gBAAgB,cAAc;;"}
|
|
@@ -6,7 +6,8 @@ const React = require("react");
|
|
|
6
6
|
const cn = require("../../utils/cn.cjs");
|
|
7
7
|
const Card = require("../Card/Card.cjs");
|
|
8
8
|
const IconButton = require("../IconButton/IconButton.cjs");
|
|
9
|
-
const
|
|
9
|
+
const ArrowUpRightIcon = require("../Icons/ArrowUpRightIcon.cjs");
|
|
10
|
+
const InfoIcon = require("../Icons/InfoIcon.cjs");
|
|
10
11
|
const Skeleton = require("../Skeleton/Skeleton.cjs");
|
|
11
12
|
const Tooltip = require("../Tooltip/Tooltip.cjs");
|
|
12
13
|
function _interopNamespaceDefault(e) {
|
|
@@ -27,8 +28,11 @@ function _interopNamespaceDefault(e) {
|
|
|
27
28
|
}
|
|
28
29
|
const React__namespace = /* @__PURE__ */ _interopNamespaceDefault(React);
|
|
29
30
|
const TREND_CLASSES = {
|
|
30
|
-
positive: "
|
|
31
|
-
negative: "
|
|
31
|
+
positive: "text-success-content",
|
|
32
|
+
negative: "text-error-content"
|
|
33
|
+
};
|
|
34
|
+
const SURFACE_CLASSES = {
|
|
35
|
+
primary: "rounded-sm border-border-strong bg-background-primary"
|
|
32
36
|
};
|
|
33
37
|
const ChartCard = React__namespace.forwardRef(
|
|
34
38
|
({
|
|
@@ -44,45 +48,66 @@ const ChartCard = React__namespace.forwardRef(
|
|
|
44
48
|
children,
|
|
45
49
|
...props
|
|
46
50
|
}, ref) => {
|
|
47
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
children:
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
51
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
52
|
+
Card.Card,
|
|
53
|
+
{
|
|
54
|
+
ref,
|
|
55
|
+
hierarchy,
|
|
56
|
+
noPadding: true,
|
|
57
|
+
className: cn.cn(SURFACE_CLASSES[hierarchy], className),
|
|
58
|
+
...props,
|
|
59
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2 p-4", children: [
|
|
60
|
+
loading ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
61
|
+
/* @__PURE__ */ jsxRuntime.jsx(Skeleton.Skeleton, { animation: "wave", variant: "rounded", className: "h-4 w-32" }),
|
|
62
|
+
/* @__PURE__ */ jsxRuntime.jsx(Skeleton.Skeleton, { animation: "wave", variant: "rounded", className: "h-7 w-44" }),
|
|
63
|
+
/* @__PURE__ */ jsxRuntime.jsx(Skeleton.Skeleton, { animation: "wave", variant: "rounded", className: "h-3 w-24" })
|
|
64
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
65
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
|
|
66
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "typography-body-small-14px-regular text-content-secondary", children: title }),
|
|
67
|
+
tooltip && /* @__PURE__ */ jsxRuntime.jsx(Tooltip.TooltipProvider, { children: /* @__PURE__ */ jsxRuntime.jsxs(Tooltip.Tooltip, { children: [
|
|
68
|
+
/* @__PURE__ */ jsxRuntime.jsx(Tooltip.TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
69
|
+
IconButton.IconButton,
|
|
70
|
+
{
|
|
71
|
+
variant: "tertiary",
|
|
72
|
+
size: "32",
|
|
73
|
+
"aria-label": tooltipAriaLabel,
|
|
74
|
+
className: "text-icons-primary hover:text-content-primary focus-visible:text-content-primary active:text-content-primary",
|
|
75
|
+
icon: /* @__PURE__ */ jsxRuntime.jsx(InfoIcon.InfoIcon, { size: 16 })
|
|
76
|
+
}
|
|
77
|
+
) }),
|
|
78
|
+
/* @__PURE__ */ jsxRuntime.jsx(Tooltip.TooltipContent, { children: tooltip })
|
|
79
|
+
] }) })
|
|
80
|
+
] }),
|
|
81
|
+
subtitle && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-end gap-2", children: [
|
|
82
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "typography-header-heading-sm text-content-primary", children: subtitle }),
|
|
83
|
+
trendChip && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
84
|
+
"span",
|
|
85
|
+
{
|
|
86
|
+
className: cn.cn(
|
|
87
|
+
"flex items-center gap-1 pb-px",
|
|
88
|
+
TREND_CLASSES[trendChip.trend]
|
|
89
|
+
),
|
|
90
|
+
children: [
|
|
91
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
92
|
+
ArrowUpRightIcon.ArrowUpRightIcon,
|
|
93
|
+
{
|
|
94
|
+
className: cn.cn(
|
|
95
|
+
"size-4 shrink-0",
|
|
96
|
+
trendChip.trend === "negative" && "rotate-90"
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
),
|
|
100
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "typography-body-small-14px-regular whitespace-nowrap", children: trendChip.label })
|
|
101
|
+
]
|
|
102
|
+
}
|
|
103
|
+
)
|
|
104
|
+
] }),
|
|
105
|
+
dateInfo && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "typography-description-12px-regular text-content-tertiary", children: dateInfo })
|
|
106
|
+
] }),
|
|
107
|
+
children && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-auto", children })
|
|
108
|
+
] })
|
|
109
|
+
}
|
|
110
|
+
);
|
|
86
111
|
}
|
|
87
112
|
);
|
|
88
113
|
ChartCard.displayName = "ChartCard";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChartCard.cjs","sources":["../../../../src/components/Chart/ChartCard.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { Card, type CardHierarchy } from \"../Card/Card\";\nimport { IconButton } from \"../IconButton/IconButton\";\nimport {
|
|
1
|
+
{"version":3,"file":"ChartCard.cjs","sources":["../../../../src/components/Chart/ChartCard.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { Card, type CardHierarchy } from \"../Card/Card\";\nimport { IconButton } from \"../IconButton/IconButton\";\nimport { ArrowUpRightIcon } from \"../Icons/ArrowUpRightIcon\";\nimport { InfoIcon } from \"../Icons/InfoIcon\";\nimport { Skeleton } from \"../Skeleton/Skeleton\";\nimport { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from \"../Tooltip/Tooltip\";\n\n/** Props for {@link ChartCard}. */\nexport interface ChartCardProps extends Omit<React.HTMLAttributes<HTMLDivElement>, \"title\"> {\n /**\n * Surface treatment. `primary` follows the V2 Insight Card spec (white fill,\n * strong border, 12px radius); `secondary` uses the {@link Card} secondary\n * surface. @default \"primary\"\n */\n hierarchy?: CardHierarchy;\n /** Card title text. Pass translated string for i18n. */\n title: React.ReactNode;\n /** Large subtitle value (e.g. formatted price or count). */\n subtitle?: React.ReactNode;\n /** Tooltip text shown next to the title. Pass translated string for i18n. */\n tooltip?: React.ReactNode;\n /** Accessible label for the info tooltip trigger. Override for i18n. @default \"More info\" */\n tooltipAriaLabel?: string;\n /** Date range or period label shown below the subtitle. */\n dateInfo?: React.ReactNode;\n /**\n * Trend indicator config. Rendered as a coloured directional arrow and label\n * beside the subtitle, so it is only shown when {@link subtitle} is provided.\n */\n trendChip?: {\n /** Display label (e.g. \"12.5% vs prev\"). */\n label: React.ReactNode;\n /** Whether the trend is positive (green, arrow up) or negative (red, arrow down). */\n trend: \"positive\" | \"negative\";\n };\n /** Show loading skeleton instead of content. @default false */\n loading?: boolean;\n /** Chart content rendered below the header. */\n children?: React.ReactNode;\n}\n\nconst TREND_CLASSES: Record<\"positive\" | \"negative\", string> = {\n positive: \"text-success-content\",\n negative: \"text-error-content\",\n};\n\nconst SURFACE_CLASSES: Partial<Record<CardHierarchy, string>> = {\n primary: \"rounded-sm border-border-strong bg-background-primary\",\n};\n\n/**\n * Wraps any chart with a structured header containing title, subtitle,\n * optional trend indicator, date range label, info tooltip, and a loading\n * skeleton state.\n *\n * At `hierarchy=\"primary\"` this implements the V2 Insight Card surface, which\n * differs from {@link Card}'s own primary hierarchy: a white fill, the strong\n * border, and a 12px radius.\n *\n * @example\n * ```tsx\n * <ChartCard\n * title=\"Revenue\"\n * subtitle=\"$4,523\"\n * trendChip={{ label: \"+12.5%\", trend: \"positive\" }}\n * dateInfo=\"Jan 1 – Mar 17\"\n * tooltip=\"Total revenue for the selected period.\"\n * >\n * <MyLineChart />\n * </ChartCard>\n * ```\n */\nexport const ChartCard = React.forwardRef<HTMLDivElement, ChartCardProps>(\n (\n {\n className,\n hierarchy = \"primary\",\n title,\n subtitle,\n tooltip,\n tooltipAriaLabel = \"More info\",\n dateInfo,\n trendChip,\n loading = false,\n children,\n ...props\n },\n ref,\n ) => {\n return (\n <Card\n ref={ref}\n hierarchy={hierarchy}\n noPadding\n className={cn(SURFACE_CLASSES[hierarchy], className)}\n {...props}\n >\n <div className=\"flex flex-col gap-2 p-4\">\n {loading ? (\n <>\n <Skeleton animation=\"wave\" variant=\"rounded\" className=\"h-4 w-32\" />\n <Skeleton animation=\"wave\" variant=\"rounded\" className=\"h-7 w-44\" />\n <Skeleton animation=\"wave\" variant=\"rounded\" className=\"h-3 w-24\" />\n </>\n ) : (\n <>\n <div className=\"flex items-center gap-1\">\n <span className=\"typography-body-small-14px-regular text-content-secondary\">\n {title}\n </span>\n {tooltip && (\n <TooltipProvider>\n <Tooltip>\n <TooltipTrigger asChild>\n <IconButton\n variant=\"tertiary\"\n size=\"32\"\n aria-label={tooltipAriaLabel}\n className=\"text-icons-primary hover:text-content-primary focus-visible:text-content-primary active:text-content-primary\"\n icon={<InfoIcon size={16} />}\n />\n </TooltipTrigger>\n <TooltipContent>{tooltip}</TooltipContent>\n </Tooltip>\n </TooltipProvider>\n )}\n </div>\n {subtitle && (\n <div className=\"flex flex-wrap items-end gap-2\">\n <span className=\"typography-header-heading-sm text-content-primary\">\n {subtitle}\n </span>\n {trendChip && (\n <span\n className={cn(\n \"flex items-center gap-1 pb-px\",\n TREND_CLASSES[trendChip.trend],\n )}\n >\n <ArrowUpRightIcon\n className={cn(\n \"size-4 shrink-0\",\n trendChip.trend === \"negative\" && \"rotate-90\",\n )}\n />\n <span className=\"typography-body-small-14px-regular whitespace-nowrap\">\n {trendChip.label}\n </span>\n </span>\n )}\n </div>\n )}\n {dateInfo && (\n <span className=\"typography-description-12px-regular text-content-tertiary\">\n {dateInfo}\n </span>\n )}\n </>\n )}\n {children && <div className=\"mt-auto\">{children}</div>}\n </div>\n </Card>\n );\n },\n);\nChartCard.displayName = \"ChartCard\";\n"],"names":["React","jsx","Card","cn","jsxs","Fragment","Skeleton","TooltipProvider","Tooltip","TooltipTrigger","IconButton","InfoIcon","TooltipContent","ArrowUpRightIcon"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2CA,MAAM,gBAAyD;AAAA,EAC7D,UAAU;AAAA,EACV,UAAU;AACZ;AAEA,MAAM,kBAA0D;AAAA,EAC9D,SAAS;AACX;AAwBO,MAAM,YAAYA,iBAAM;AAAA,EAC7B,CACE;AAAA,IACE;AAAA,IACA,YAAY;AAAA,IACZ;AAAA,IACA;AAAA,IACA;AAAA,IACA,mBAAmB;AAAA,IACnB;AAAA,IACA;AAAA,IACA,UAAU;AAAA,IACV;AAAA,IACA,GAAG;AAAA,EAAA,GAEL,QACG;AACH,WACEC,2BAAAA;AAAAA,MAACC,KAAAA;AAAAA,MAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA,WAAS;AAAA,QACT,WAAWC,GAAAA,GAAG,gBAAgB,SAAS,GAAG,SAAS;AAAA,QAClD,GAAG;AAAA,QAEJ,UAAAC,2BAAAA,KAAC,OAAA,EAAI,WAAU,2BACZ,UAAA;AAAA,UAAA,UACCA,2BAAAA,KAAAC,qBAAA,EACE,UAAA;AAAA,YAAAJ,+BAACK,SAAAA,YAAS,WAAU,QAAO,SAAQ,WAAU,WAAU,YAAW;AAAA,2CACjEA,SAAAA,UAAA,EAAS,WAAU,QAAO,SAAQ,WAAU,WAAU,YAAW;AAAA,2CACjEA,SAAAA,UAAA,EAAS,WAAU,QAAO,SAAQ,WAAU,WAAU,WAAA,CAAW;AAAA,UAAA,EAAA,CACpE,IAEAF,2BAAAA,KAAAC,WAAAA,UAAA,EACE,UAAA;AAAA,YAAAD,2BAAAA,KAAC,OAAA,EAAI,WAAU,2BACb,UAAA;AAAA,cAAAH,2BAAAA,IAAC,QAAA,EAAK,WAAU,6DACb,UAAA,OACH;AAAA,cACC,WACCA,2BAAAA,IAACM,yBAAA,EACC,UAAAH,gCAACI,QAAAA,SAAA,EACC,UAAA;AAAA,gBAAAP,2BAAAA,IAACQ,QAAAA,gBAAA,EAAe,SAAO,MACrB,UAAAR,2BAAAA;AAAAA,kBAACS,WAAAA;AAAAA,kBAAA;AAAA,oBACC,SAAQ;AAAA,oBACR,MAAK;AAAA,oBACL,cAAY;AAAA,oBACZ,WAAU;AAAA,oBACV,MAAMT,2BAAAA,IAACU,SAAAA,UAAA,EAAS,MAAM,GAAA,CAAI;AAAA,kBAAA;AAAA,gBAAA,GAE9B;AAAA,gBACAV,2BAAAA,IAACW,QAAAA,kBAAgB,UAAA,QAAA,CAAQ;AAAA,cAAA,EAAA,CAC3B,EAAA,CACF;AAAA,YAAA,GAEJ;AAAA,YACC,YACCR,2BAAAA,KAAC,OAAA,EAAI,WAAU,kCACb,UAAA;AAAA,cAAAH,2BAAAA,IAAC,QAAA,EAAK,WAAU,qDACb,UAAA,UACH;AAAA,cACC,aACCG,2BAAAA;AAAAA,gBAAC;AAAA,gBAAA;AAAA,kBACC,WAAWD,GAAAA;AAAAA,oBACT;AAAA,oBACA,cAAc,UAAU,KAAK;AAAA,kBAAA;AAAA,kBAG/B,UAAA;AAAA,oBAAAF,2BAAAA;AAAAA,sBAACY,iBAAAA;AAAAA,sBAAA;AAAA,wBACC,WAAWV,GAAAA;AAAAA,0BACT;AAAA,0BACA,UAAU,UAAU,cAAc;AAAA,wBAAA;AAAA,sBACpC;AAAA,oBAAA;AAAA,oBAEFF,2BAAAA,IAAC,QAAA,EAAK,WAAU,wDACb,oBAAU,MAAA,CACb;AAAA,kBAAA;AAAA,gBAAA;AAAA,cAAA;AAAA,YACF,GAEJ;AAAA,YAED,YACCA,2BAAAA,IAAC,QAAA,EAAK,WAAU,6DACb,UAAA,SAAA,CACH;AAAA,UAAA,GAEJ;AAAA,UAED,YAAYA,2BAAAA,IAAC,OAAA,EAAI,WAAU,WAAW,SAAA,CAAS;AAAA,QAAA,EAAA,CAClD;AAAA,MAAA;AAAA,IAAA;AAAA,EAGN;AACF;AACA,UAAU,cAAc;;"}
|
|
@@ -25,7 +25,8 @@ const React__namespace = /* @__PURE__ */ _interopNamespaceDefault(React);
|
|
|
25
25
|
const ASPECT_RATIO_CLASSES = {
|
|
26
26
|
tall: "aspect-5/4",
|
|
27
27
|
medium: "aspect-3/2",
|
|
28
|
-
short: "aspect-9/5"
|
|
28
|
+
short: "aspect-9/5",
|
|
29
|
+
banner: "aspect-5/2"
|
|
29
30
|
};
|
|
30
31
|
const CreatorTile = React__namespace.forwardRef(
|
|
31
32
|
({ className, background, name, tagline, avatar, action, aspectRatio = "medium", ...props }, ref) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CreatorTile.cjs","sources":["../../../../src/components/CreatorTile/CreatorTile.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { Avatar } from \"../Avatar/Avatar\";\n\n/** Width-to-height ratio preset for the tile. */\nexport type CreatorTileAspectRatio = \"tall\" | \"medium\" | \"short\";\n\nconst ASPECT_RATIO_CLASSES: Record<CreatorTileAspectRatio, string> = {\n tall: \"aspect-5/4\",\n medium: \"aspect-3/2\",\n short: \"aspect-9/5\",\n};\n\nexport interface CreatorTileProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Decorative background media rendered behind the creator content. */\n background: React.ReactNode;\n /** Creator display name shown as the prominent heading. */\n name: React.ReactNode;\n /** Optional secondary line shown under the name (e.g. handle or tagline). */\n tagline?: React.ReactNode;\n /** Avatar props forwarded to the inner {@link Avatar}. */\n avatar?: React.ComponentPropsWithoutRef<typeof Avatar>;\n /**\n * Action element rendered on the right of the profile row (e.g. a `Button`\n * for following the creator).\n */\n action?: React.ReactNode;\n /**\n * Width-to-height ratio preset.\n *\n * - `tall` –
|
|
1
|
+
{"version":3,"file":"CreatorTile.cjs","sources":["../../../../src/components/CreatorTile/CreatorTile.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { Avatar } from \"../Avatar/Avatar\";\n\n/** Width-to-height ratio preset for the tile. */\nexport type CreatorTileAspectRatio = \"tall\" | \"medium\" | \"short\" | \"banner\";\n\nconst ASPECT_RATIO_CLASSES: Record<CreatorTileAspectRatio, string> = {\n tall: \"aspect-5/4\",\n medium: \"aspect-3/2\",\n short: \"aspect-9/5\",\n banner: \"aspect-5/2\",\n};\n\nexport interface CreatorTileProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Decorative background media rendered behind the creator content. */\n background: React.ReactNode;\n /** Creator display name shown as the prominent heading. */\n name: React.ReactNode;\n /** Optional secondary line shown under the name (e.g. handle or tagline). */\n tagline?: React.ReactNode;\n /** Avatar props forwarded to the inner {@link Avatar}. */\n avatar?: React.ComponentPropsWithoutRef<typeof Avatar>;\n /**\n * Action element rendered on the right of the profile row (e.g. a `Button`\n * for following the creator).\n */\n action?: React.ReactNode;\n /**\n * Width-to-height ratio preset.\n *\n * - `tall` – 5:4, closest to square\n * - `medium` – 3:2 landscape\n * - `short` – 9:5 landscape\n * - `banner` – 5:2, the slimmest preset\n *\n * The profile row is a fixed ~74px tall, so `banner` needs at least 185px of\n * width before the ratio makes the tile shorter than its own row and the top\n * of the row is clipped. The taller presets reach that floor at widths too\n * narrow to be practical.\n *\n * @default \"medium\"\n */\n aspectRatio?: CreatorTileAspectRatio;\n}\n\n/**\n * A visual highlight tile showcasing a creator with a full-bleed background\n * media and an overlaid profile row containing an avatar, name, optional\n * tagline and an action element.\n *\n * @example\n * ```tsx\n * <CreatorTile\n * background={<img src=\"/creator.jpg\" alt=\"\" />}\n * avatar={{ src: \"/avatar.jpg\", alt: \"Aitana Lopez\", fallback: \"AL\" }}\n * name=\"Aitana Lopez\"\n * tagline=\"@fit_aitana\"\n * action={<Button variant=\"primary\">Follow</Button>}\n * />\n * ```\n */\nexport const CreatorTile = React.forwardRef<HTMLDivElement, CreatorTileProps>(\n (\n { className, background, name, tagline, avatar, action, aspectRatio = \"medium\", ...props },\n ref,\n ) => {\n const aspectClass = ASPECT_RATIO_CLASSES[aspectRatio];\n\n return (\n <div\n ref={ref}\n className={cn(\n \"relative isolate flex w-full flex-col justify-end overflow-hidden\",\n aspectClass,\n className,\n )}\n {...props}\n >\n <div className=\"pointer-events-none absolute inset-0 select-none *:h-full *:w-full [&>img]:object-cover [&>video]:object-cover\">\n {background}\n </div>\n <div className=\"pointer-events-none absolute inset-x-0 bottom-0 h-1/2 rounded-b-[inherit] bg-linear-to-t from-black/60 to-transparent\" />\n <div className=\"pointer-events-none absolute inset-x-0 bottom-0 h-1/2 overflow-hidden rounded-b-[inherit] backdrop-blur-md [-webkit-mask-image:linear-gradient(to_top,black,transparent)] [mask-image:linear-gradient(to_top,black,transparent)]\" />\n <div className=\"relative flex items-center justify-between gap-4 p-4\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <Avatar\n size={40}\n src={avatar?.src}\n alt={avatar?.alt ?? (typeof name === \"string\" ? name : undefined)}\n fallback={avatar?.fallback}\n {...avatar}\n />\n <div className=\"flex min-w-0 flex-col\">\n <p className=\"typography-body-default-16px-semibold m-0 truncate text-white\">\n {name}\n </p>\n {tagline ? (\n <p className=\"typography-body-small-14px-semibold m-0 truncate text-white/50\">\n {tagline}\n </p>\n ) : null}\n </div>\n </div>\n {action ? <div className=\"shrink-0\">{action}</div> : null}\n </div>\n </div>\n );\n },\n);\n\nCreatorTile.displayName = \"CreatorTile\";\n"],"names":["React","jsxs","cn","jsx","Avatar"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAOA,MAAM,uBAA+D;AAAA,EACnE,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,QAAQ;AACV;AAkDO,MAAM,cAAcA,iBAAM;AAAA,EAC/B,CACE,EAAE,WAAW,YAAY,MAAM,SAAS,QAAQ,QAAQ,cAAc,UAAU,GAAG,MAAA,GACnF,QACG;AACH,UAAM,cAAc,qBAAqB,WAAW;AAEpD,WACEC,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,WAAWC,GAAAA;AAAAA,UACT;AAAA,UACA;AAAA,UACA;AAAA,QAAA;AAAA,QAED,GAAG;AAAA,QAEJ,UAAA;AAAA,UAAAC,2BAAAA,IAAC,OAAA,EAAI,WAAU,kHACZ,UAAA,YACH;AAAA,UACAA,2BAAAA,IAAC,OAAA,EAAI,WAAU,wHAAA,CAAwH;AAAA,UACvIA,2BAAAA,IAAC,OAAA,EAAI,WAAU,mOAAA,CAAmO;AAAA,UAClPF,2BAAAA,KAAC,OAAA,EAAI,WAAU,wDACb,UAAA;AAAA,YAAAA,2BAAAA,KAAC,OAAA,EAAI,WAAU,mCACb,UAAA;AAAA,cAAAE,2BAAAA;AAAAA,gBAACC,OAAAA;AAAAA,gBAAA;AAAA,kBACC,MAAM;AAAA,kBACN,KAAK,QAAQ;AAAA,kBACb,KAAK,QAAQ,QAAQ,OAAO,SAAS,WAAW,OAAO;AAAA,kBACvD,UAAU,QAAQ;AAAA,kBACjB,GAAG;AAAA,gBAAA;AAAA,cAAA;AAAA,cAENH,2BAAAA,KAAC,OAAA,EAAI,WAAU,yBACb,UAAA;AAAA,gBAAAE,2BAAAA,IAAC,KAAA,EAAE,WAAU,iEACV,UAAA,MACH;AAAA,gBACC,UACCA,2BAAAA,IAAC,KAAA,EAAE,WAAU,kEACV,mBACH,IACE;AAAA,cAAA,EAAA,CACN;AAAA,YAAA,GACF;AAAA,YACC,SAASA,2BAAAA,IAAC,OAAA,EAAI,WAAU,YAAY,kBAAO,IAAS;AAAA,UAAA,EAAA,CACvD;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,EAGN;AACF;AAEA,YAAY,cAAc;;"}
|
|
@@ -4,6 +4,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
4
4
|
const jsxRuntime = require("react/jsx-runtime");
|
|
5
5
|
const React = require("react");
|
|
6
6
|
const cn = require("../../utils/cn.cjs");
|
|
7
|
+
const AIDisclosureIcon = require("../Icons/AIDisclosureIcon.cjs");
|
|
7
8
|
const VerifiedIcon = require("../Icons/VerifiedIcon.cjs");
|
|
8
9
|
const ProfileOnlineStatus = require("../ProfileOnlineStatus/ProfileOnlineStatus.cjs");
|
|
9
10
|
function _interopNamespaceDefault(e) {
|
|
@@ -25,6 +26,8 @@ function _interopNamespaceDefault(e) {
|
|
|
25
26
|
const React__namespace = /* @__PURE__ */ _interopNamespaceDefault(React);
|
|
26
27
|
const UserDisplayName = React__namespace.forwardRef(
|
|
27
28
|
({
|
|
29
|
+
aiDisclosure,
|
|
30
|
+
aiDisclosureLabel = "AI creator",
|
|
28
31
|
ambassador,
|
|
29
32
|
ambassadorLabel = "Ambassador",
|
|
30
33
|
children,
|
|
@@ -38,7 +41,7 @@ const UserDisplayName = React__namespace.forwardRef(
|
|
|
38
41
|
...props
|
|
39
42
|
}, ref) => {
|
|
40
43
|
const Component = component;
|
|
41
|
-
const badge = ambassador ? { label: ambassadorLabel, tint: "text-success-content" } : verified ? { label: verifiedLabel, tint: "text-
|
|
44
|
+
const badge = ambassador ? { label: ambassadorLabel, tint: "text-success-content" } : verified ? { label: verifiedLabel, tint: "text-current" } : null;
|
|
42
45
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
43
46
|
Component,
|
|
44
47
|
{
|
|
@@ -56,6 +59,15 @@ const UserDisplayName = React__namespace.forwardRef(
|
|
|
56
59
|
children: /* @__PURE__ */ jsxRuntime.jsx(VerifiedIcon.VerifiedIcon, { size: 16 })
|
|
57
60
|
}
|
|
58
61
|
),
|
|
62
|
+
aiDisclosure && /* @__PURE__ */ jsxRuntime.jsx(
|
|
63
|
+
"span",
|
|
64
|
+
{
|
|
65
|
+
role: "img",
|
|
66
|
+
"aria-label": aiDisclosureLabel,
|
|
67
|
+
className: "ml-2 inline-flex shrink-0 items-center text-current",
|
|
68
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(AIDisclosureIcon.AIDisclosureIcon, { size: 16 })
|
|
69
|
+
}
|
|
70
|
+
),
|
|
59
71
|
showOnlineStatus && /* @__PURE__ */ jsxRuntime.jsx(ProfileOnlineStatus.ProfileOnlineStatus, { label: onlineLabel, className: "shrink-0 ml-2" })
|
|
60
72
|
]
|
|
61
73
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UserDisplayName.cjs","sources":["../../../../src/components/UserDisplayName/UserDisplayName.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { VerifiedIcon } from \"../Icons/VerifiedIcon\";\nimport { ProfileOnlineStatus } from \"../ProfileOnlineStatus/ProfileOnlineStatus\";\n\nexport interface UserDisplayNameProps extends React.HTMLAttributes<HTMLElement> {\n /** Render an ambassador badge after the name. */\n ambassador?: boolean;\n /** Accessible label for the ambassador badge. @default \"Ambassador\" */\n ambassadorLabel?: string;\n /** HTML element used for the root wrapper. @default \"span\" */\n component?: \"span\" | \"h1\" | \"h2\" | \"h3\" | \"p\" | \"div\";\n /** Truncate the display name on a single line. @default true */\n noWrap?: boolean;\n /** Accessible label for the online-status indicator. @default \"Online\" */\n onlineLabel?: string;\n /** Render the inline online-status indicator after the name. @default false */\n showOnlineStatus?: boolean;\n /** Render a verified badge after the name (ignored when `ambassador` is set). */\n verified?: boolean;\n /** Accessible label for the verified badge. @default \"Verified\" */\n verifiedLabel?: string;\n}\n\n/**\n * Renders a user's display name with optional ambassador/verified badges and an\n * online-status indicator. Only the name shrinks and truncates; trailing badges\n * and the online-status indicator stay visible on the same line.\n *\n * Typography is not baked in: pass a `typography-*` utility via `className` on\n * the root and the name inherits it. Use `typography-body-small-14px-semibold`\n * for the standard display-name scale.\n *\n * When both `ambassador` and `verified` are set, the ambassador badge takes\n * precedence.
|
|
1
|
+
{"version":3,"file":"UserDisplayName.cjs","sources":["../../../../src/components/UserDisplayName/UserDisplayName.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { AIDisclosureIcon } from \"../Icons/AIDisclosureIcon\";\nimport { VerifiedIcon } from \"../Icons/VerifiedIcon\";\nimport { ProfileOnlineStatus } from \"../ProfileOnlineStatus/ProfileOnlineStatus\";\n\nexport interface UserDisplayNameProps extends React.HTMLAttributes<HTMLElement> {\n /** Render an AI-disclosure badge after the name, alongside any status badge. */\n aiDisclosure?: boolean;\n /** Accessible label for the AI-disclosure badge. @default \"AI creator\" */\n aiDisclosureLabel?: string;\n /** Render an ambassador badge after the name. */\n ambassador?: boolean;\n /** Accessible label for the ambassador badge. @default \"Ambassador\" */\n ambassadorLabel?: string;\n /** HTML element used for the root wrapper. @default \"span\" */\n component?: \"span\" | \"h1\" | \"h2\" | \"h3\" | \"p\" | \"div\";\n /** Truncate the display name on a single line. @default true */\n noWrap?: boolean;\n /** Accessible label for the online-status indicator. @default \"Online\" */\n onlineLabel?: string;\n /** Render the inline online-status indicator after the name. @default false */\n showOnlineStatus?: boolean;\n /** Render a verified badge after the name (ignored when `ambassador` is set). */\n verified?: boolean;\n /** Accessible label for the verified badge. @default \"Verified\" */\n verifiedLabel?: string;\n}\n\n/**\n * Renders a user's display name with optional ambassador/verified badges and an\n * online-status indicator. Only the name shrinks and truncates; trailing badges\n * and the online-status indicator stay visible on the same line.\n *\n * Typography is not baked in: pass a `typography-*` utility via `className` on\n * the root and the name inherits it. Use `typography-body-small-14px-semibold`\n * for the standard display-name scale.\n *\n * When both `ambassador` and `verified` are set, the ambassador badge takes\n * precedence. `aiDisclosure` is independent of both and renders after whichever\n * status badge is shown, since it discloses how the account is operated rather\n * than ranking it.\n *\n * The verified and AI-disclosure badges inherit the name's colour, so setting a\n * colour on the root tints them with it. That keeps them legible on surfaces\n * that fix their own text colour — over a cover image, for example, where a\n * theme-dependent tint would turn black against white text in light mode.\n *\n * The ambassador tint is deliberately not inherited: it uses `text-success-content`\n * (not `text-icons-brand-green`) so the green darkens in light mode and lightens\n * in dark mode, matching {@link ProfileOnlineStatus}; `text-icons-brand-green` is\n * fixed across modes.\n *\n * @example\n * ```tsx\n * <UserDisplayName className=\"typography-body-small-14px-semibold\">Jane Doe</UserDisplayName>\n * ```\n */\nexport const UserDisplayName = React.forwardRef<HTMLElement, UserDisplayNameProps>(\n (\n {\n aiDisclosure,\n aiDisclosureLabel = \"AI creator\",\n ambassador,\n ambassadorLabel = \"Ambassador\",\n children,\n className,\n component = \"span\",\n noWrap = true,\n onlineLabel = \"Online\",\n showOnlineStatus,\n verified,\n verifiedLabel = \"Verified\",\n ...props\n },\n ref,\n ) => {\n const Component = component as React.ElementType;\n\n const badge = ambassador\n ? { label: ambassadorLabel, tint: \"text-success-content\" }\n : verified\n ? { label: verifiedLabel, tint: \"text-current\" }\n : null;\n\n return (\n <Component\n ref={ref}\n className={cn(\"inline-flex max-w-full items-center\", className)}\n {...props}\n >\n <span className={cn(\"min-w-0\", noWrap && \"truncate\")}>{children}</span>\n {badge && (\n <span\n role=\"img\"\n aria-label={badge.label}\n className={cn(\"ml-2 inline-flex shrink-0 items-center\", badge.tint)}\n >\n <VerifiedIcon size={16} />\n </span>\n )}\n {aiDisclosure && (\n <span\n role=\"img\"\n aria-label={aiDisclosureLabel}\n className=\"ml-2 inline-flex shrink-0 items-center text-current\"\n >\n <AIDisclosureIcon size={16} />\n </span>\n )}\n {showOnlineStatus && <ProfileOnlineStatus label={onlineLabel} className=\"shrink-0 ml-2\" />}\n </Component>\n );\n },\n);\n\nUserDisplayName.displayName = \"UserDisplayName\";\n"],"names":["React","jsxs","cn","jsx","VerifiedIcon","AIDisclosureIcon","ProfileOnlineStatus"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AA0DO,MAAM,kBAAkBA,iBAAM;AAAA,EACnC,CACE;AAAA,IACE;AAAA,IACA,oBAAoB;AAAA,IACpB;AAAA,IACA,kBAAkB;AAAA,IAClB;AAAA,IACA;AAAA,IACA,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,cAAc;AAAA,IACd;AAAA,IACA;AAAA,IACA,gBAAgB;AAAA,IAChB,GAAG;AAAA,EAAA,GAEL,QACG;AACH,UAAM,YAAY;AAElB,UAAM,QAAQ,aACV,EAAE,OAAO,iBAAiB,MAAM,uBAAA,IAChC,WACE,EAAE,OAAO,eAAe,MAAM,mBAC9B;AAEN,WACEC,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,WAAWC,GAAAA,GAAG,uCAAuC,SAAS;AAAA,QAC7D,GAAG;AAAA,QAEJ,UAAA;AAAA,UAAAC,+BAAC,UAAK,WAAWD,GAAAA,GAAG,WAAW,UAAU,UAAU,GAAI,UAAS;AAAA,UAC/D,SACCC,2BAAAA;AAAAA,YAAC;AAAA,YAAA;AAAA,cACC,MAAK;AAAA,cACL,cAAY,MAAM;AAAA,cAClB,WAAWD,GAAAA,GAAG,0CAA0C,MAAM,IAAI;AAAA,cAElE,UAAAC,2BAAAA,IAACC,aAAAA,cAAA,EAAa,MAAM,GAAA,CAAI;AAAA,YAAA;AAAA,UAAA;AAAA,UAG3B,gBACCD,2BAAAA;AAAAA,YAAC;AAAA,YAAA;AAAA,cACC,MAAK;AAAA,cACL,cAAY;AAAA,cACZ,WAAU;AAAA,cAEV,UAAAA,2BAAAA,IAACE,iBAAAA,kBAAA,EAAiB,MAAM,GAAA,CAAI;AAAA,YAAA;AAAA,UAAA;AAAA,UAG/B,oBAAoBF,2BAAAA,IAACG,yCAAA,EAAoB,OAAO,aAAa,WAAU,gBAAA,CAAgB;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,EAG9F;AACF;AAEA,gBAAgB,cAAc;;"}
|
|
@@ -39,6 +39,10 @@ const UserItem = React__namespace.forwardRef(
|
|
|
39
39
|
showOnlineStatus,
|
|
40
40
|
showHandle = true,
|
|
41
41
|
showAvatar = true,
|
|
42
|
+
aiDisclosure = false,
|
|
43
|
+
aiDisclosureLabel,
|
|
44
|
+
verified = false,
|
|
45
|
+
verifiedLabel,
|
|
42
46
|
className,
|
|
43
47
|
...props
|
|
44
48
|
}, ref) => {
|
|
@@ -61,7 +65,17 @@ const UserItem = React__namespace.forwardRef(
|
|
|
61
65
|
}
|
|
62
66
|
),
|
|
63
67
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 overflow-hidden pl-2", children: [
|
|
64
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
68
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
69
|
+
UserDisplayName.UserDisplayName,
|
|
70
|
+
{
|
|
71
|
+
aiDisclosure,
|
|
72
|
+
aiDisclosureLabel,
|
|
73
|
+
verified,
|
|
74
|
+
verifiedLabel,
|
|
75
|
+
className: "typography-body-small-14px-semibold",
|
|
76
|
+
children: user.nickname || user.displayName
|
|
77
|
+
}
|
|
78
|
+
),
|
|
65
79
|
showHandle && /* @__PURE__ */ jsxRuntime.jsx(UserHandle.UserHandle, { children: user.handle })
|
|
66
80
|
] })
|
|
67
81
|
]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UserItem.cjs","sources":["../../../../src/components/UserItem/UserItem.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { getInitials } from \"../../utils/getInitials\";\nimport { Avatar } from \"../Avatar/Avatar\";\nimport { BellOffIcon } from \"../Icons/BellOffIcon\";\nimport { UserDisplayName } from \"../UserDisplayName/UserDisplayName\";\nimport { UserHandle } from \"../UserHandle/UserHandle\";\n\nconst AVATAR_SIZES = [16, 24, 32, 40, 48, 64, 88, 148] as const;\ntype AvatarSizeToken = (typeof AVATAR_SIZES)[number];\n\n/** Snap an arbitrary pixel size to the nearest supported {@link Avatar} size token. */\nconst getNearestAvatarSize = (size: number): AvatarSizeToken =>\n AVATAR_SIZES.reduce((prev, curr) =>\n Math.abs(curr - size) < Math.abs(prev - size) ? curr : prev,\n );\n\n/** Minimal user shape consumed by {@link UserItem}. */\nexport type UserItemUser = {\n avatarUri?: { url: string; url2x?: string };\n displayName: string;\n handle: string;\n nickname?: string;\n};\n\nexport interface UserItemProps extends React.HTMLAttributes<HTMLDivElement> {\n /** The user to display. */\n user: UserItemUser;\n /** Desired avatar size in px; snapped to the nearest supported token. @default 48 */\n avatarSize?: number;\n /** Show the muted indicator in the bottom-left corner. @default false */\n isMuted?: boolean;\n /** Whether the user is online (only shown when `showOnlineStatus` is also set). @default false */\n isOnline?: boolean;\n /** Render the avatar. @default true */\n showAvatar?: boolean;\n /** Render the handle line under the display name. @default true */\n showHandle?: boolean;\n /** Enable the avatar online indicator (combined with `isOnline`). */\n showOnlineStatus?: boolean;\n}\n\n/**\n * A compact user row showing an avatar, display name (or nickname) and handle,\n * with optional online and muted indicators.\n *\n * @example\n * ```tsx\n * <UserItem user={{ displayName: \"Jane Doe\", handle: \"jane_doe\" }} />\n * ```\n */\nexport const UserItem = React.forwardRef<HTMLDivElement, UserItemProps>(\n (\n {\n user,\n avatarSize,\n isOnline = false,\n isMuted = false,\n showOnlineStatus,\n showHandle = true,\n showAvatar = true,\n className,\n ...props\n },\n ref,\n ) => {\n return (\n <div\n ref={ref}\n className={cn(\"relative flex w-full items-center justify-start px-2 py-1\", className)}\n {...props}\n >\n {isMuted && (\n <div className=\"absolute bottom-0 left-0 z-1 flex rounded-xs bg-surface-primary p-1\">\n <BellOffIcon size={16} />\n </div>\n )}\n {showAvatar && (\n <Avatar\n size={getNearestAvatarSize(avatarSize || 48)}\n src={user.avatarUri?.url}\n alt={user.displayName}\n fallback={getInitials(user.displayName)}\n onlineIndicator={Boolean(showOnlineStatus && isOnline)}\n />\n )}\n <div className=\"flex-1 overflow-hidden pl-2\">\n <UserDisplayName
|
|
1
|
+
{"version":3,"file":"UserItem.cjs","sources":["../../../../src/components/UserItem/UserItem.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { getInitials } from \"../../utils/getInitials\";\nimport { Avatar } from \"../Avatar/Avatar\";\nimport { BellOffIcon } from \"../Icons/BellOffIcon\";\nimport { UserDisplayName } from \"../UserDisplayName/UserDisplayName\";\nimport { UserHandle } from \"../UserHandle/UserHandle\";\n\nconst AVATAR_SIZES = [16, 24, 32, 40, 48, 64, 88, 148] as const;\ntype AvatarSizeToken = (typeof AVATAR_SIZES)[number];\n\n/** Snap an arbitrary pixel size to the nearest supported {@link Avatar} size token. */\nconst getNearestAvatarSize = (size: number): AvatarSizeToken =>\n AVATAR_SIZES.reduce((prev, curr) =>\n Math.abs(curr - size) < Math.abs(prev - size) ? curr : prev,\n );\n\n/** Minimal user shape consumed by {@link UserItem}. */\nexport type UserItemUser = {\n avatarUri?: { url: string; url2x?: string };\n displayName: string;\n handle: string;\n nickname?: string;\n};\n\nexport interface UserItemProps extends React.HTMLAttributes<HTMLDivElement> {\n /** The user to display. */\n user: UserItemUser;\n /** Desired avatar size in px; snapped to the nearest supported token. @default 48 */\n avatarSize?: number;\n /** Show the muted indicator in the bottom-left corner. @default false */\n isMuted?: boolean;\n /** Whether the user is online (only shown when `showOnlineStatus` is also set). @default false */\n isOnline?: boolean;\n /** Render the avatar. @default true */\n showAvatar?: boolean;\n /** Render the handle line under the display name. @default true */\n showHandle?: boolean;\n /** Enable the avatar online indicator (combined with `isOnline`). */\n showOnlineStatus?: boolean;\n /** Render an AI-disclosure badge after the display name. @default false */\n aiDisclosure?: boolean;\n /** Accessible label for the AI-disclosure badge. @default \"AI creator\" */\n aiDisclosureLabel?: string;\n /** Render a verified badge after the display name. @default false */\n verified?: boolean;\n /** Accessible label for the verified badge. @default \"Verified\" */\n verifiedLabel?: string;\n}\n\n/**\n * A compact user row showing an avatar, display name (or nickname) and handle,\n * with optional verified and AI-disclosure badges and online and muted indicators.\n *\n * @example\n * ```tsx\n * <UserItem user={{ displayName: \"Jane Doe\", handle: \"jane_doe\" }} />\n * ```\n */\nexport const UserItem = React.forwardRef<HTMLDivElement, UserItemProps>(\n (\n {\n user,\n avatarSize,\n isOnline = false,\n isMuted = false,\n showOnlineStatus,\n showHandle = true,\n showAvatar = true,\n aiDisclosure = false,\n aiDisclosureLabel,\n verified = false,\n verifiedLabel,\n className,\n ...props\n },\n ref,\n ) => {\n return (\n <div\n ref={ref}\n className={cn(\"relative flex w-full items-center justify-start px-2 py-1\", className)}\n {...props}\n >\n {isMuted && (\n <div className=\"absolute bottom-0 left-0 z-1 flex rounded-xs bg-surface-primary p-1\">\n <BellOffIcon size={16} />\n </div>\n )}\n {showAvatar && (\n <Avatar\n size={getNearestAvatarSize(avatarSize || 48)}\n src={user.avatarUri?.url}\n alt={user.displayName}\n fallback={getInitials(user.displayName)}\n onlineIndicator={Boolean(showOnlineStatus && isOnline)}\n />\n )}\n <div className=\"flex-1 overflow-hidden pl-2\">\n <UserDisplayName\n aiDisclosure={aiDisclosure}\n aiDisclosureLabel={aiDisclosureLabel}\n verified={verified}\n verifiedLabel={verifiedLabel}\n className=\"typography-body-small-14px-semibold\"\n >\n {user.nickname || user.displayName}\n </UserDisplayName>\n {showHandle && <UserHandle>{user.handle}</UserHandle>}\n </div>\n </div>\n );\n },\n);\n\nUserItem.displayName = \"UserItem\";\n"],"names":["React","jsxs","cn","jsx","BellOffIcon","Avatar","getInitials","UserDisplayName","UserHandle"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAQA,MAAM,eAAe,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG;AAIrD,MAAM,uBAAuB,CAAC,SAC5B,aAAa;AAAA,EAAO,CAAC,MAAM,SACzB,KAAK,IAAI,OAAO,IAAI,IAAI,KAAK,IAAI,OAAO,IAAI,IAAI,OAAO;AACzD;AA4CK,MAAM,WAAWA,iBAAM;AAAA,EAC5B,CACE;AAAA,IACE;AAAA,IACA;AAAA,IACA,WAAW;AAAA,IACX,UAAU;AAAA,IACV;AAAA,IACA,aAAa;AAAA,IACb,aAAa;AAAA,IACb,eAAe;AAAA,IACf;AAAA,IACA,WAAW;AAAA,IACX;AAAA,IACA;AAAA,IACA,GAAG;AAAA,EAAA,GAEL,QACG;AACH,WACEC,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,WAAWC,GAAAA,GAAG,6DAA6D,SAAS;AAAA,QACnF,GAAG;AAAA,QAEH,UAAA;AAAA,UAAA,WACCC,2BAAAA,IAAC,SAAI,WAAU,uEACb,yCAACC,YAAAA,aAAA,EAAY,MAAM,IAAI,EAAA,CACzB;AAAA,UAED,cACCD,2BAAAA;AAAAA,YAACE,OAAAA;AAAAA,YAAA;AAAA,cACC,MAAM,qBAAqB,cAAc,EAAE;AAAA,cAC3C,KAAK,KAAK,WAAW;AAAA,cACrB,KAAK,KAAK;AAAA,cACV,UAAUC,YAAAA,YAAY,KAAK,WAAW;AAAA,cACtC,iBAAiB,QAAQ,oBAAoB,QAAQ;AAAA,YAAA;AAAA,UAAA;AAAA,UAGzDL,2BAAAA,KAAC,OAAA,EAAI,WAAU,+BACb,UAAA;AAAA,YAAAE,2BAAAA;AAAAA,cAACI,gBAAAA;AAAAA,cAAA;AAAA,gBACC;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA,WAAU;AAAA,gBAET,UAAA,KAAK,YAAY,KAAK;AAAA,cAAA;AAAA,YAAA;AAAA,YAExB,cAAcJ,2BAAAA,IAACK,WAAAA,YAAA,EAAY,UAAA,KAAK,OAAA,CAAO;AAAA,UAAA,EAAA,CAC1C;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,EAGN;AACF;AAEA,SAAS,cAAc;;"}
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -19,6 +19,7 @@ const BottomNavigation = require("./components/BottomNavigation/BottomNavigation
|
|
|
19
19
|
const BottomNavigationAction = require("./components/BottomNavigation/BottomNavigationAction.cjs");
|
|
20
20
|
const Breadcrumb = require("./components/Breadcrumb/Breadcrumb.cjs");
|
|
21
21
|
const Button = require("./components/Button/Button.cjs");
|
|
22
|
+
const ButtonIconGroup = require("./components/ButtonIconGroup/ButtonIconGroup.cjs");
|
|
22
23
|
const ButtonStack = require("./components/ButtonStack/ButtonStack.cjs");
|
|
23
24
|
const Card = require("./components/Card/Card.cjs");
|
|
24
25
|
const ChatInput = require("./components/ChatInput/ChatInput.cjs");
|
|
@@ -291,6 +292,7 @@ exports.BreadcrumbList = Breadcrumb.BreadcrumbList;
|
|
|
291
292
|
exports.BreadcrumbPage = Breadcrumb.BreadcrumbPage;
|
|
292
293
|
exports.BreadcrumbSeparator = Breadcrumb.BreadcrumbSeparator;
|
|
293
294
|
exports.Button = Button.Button;
|
|
295
|
+
exports.ButtonIconGroup = ButtonIconGroup.ButtonIconGroup;
|
|
294
296
|
exports.ButtonStack = ButtonStack.ButtonStack;
|
|
295
297
|
exports.Card = Card.Card;
|
|
296
298
|
exports.CardContent = Card.CardContent;
|
package/dist/cjs/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { cn } from "../../utils/cn.mjs";
|
|
5
|
+
const ORIENTATION_AWARE_ROLES = /* @__PURE__ */ new Set([
|
|
6
|
+
"toolbar",
|
|
7
|
+
"menu",
|
|
8
|
+
"menubar",
|
|
9
|
+
"listbox",
|
|
10
|
+
"radiogroup",
|
|
11
|
+
"tablist",
|
|
12
|
+
"tree"
|
|
13
|
+
]);
|
|
14
|
+
const ButtonIconGroup = React.forwardRef(
|
|
15
|
+
({ className, orientation = "horizontal", role = "group", children, ...props }, ref) => {
|
|
16
|
+
const supportsOrientation = typeof role === "string" && ORIENTATION_AWARE_ROLES.has(role);
|
|
17
|
+
const orientationProps = supportsOrientation ? { "aria-orientation": orientation } : {};
|
|
18
|
+
return /* @__PURE__ */ jsx(
|
|
19
|
+
"div",
|
|
20
|
+
{
|
|
21
|
+
ref,
|
|
22
|
+
role,
|
|
23
|
+
...orientationProps,
|
|
24
|
+
"data-testid": "button-icon-group",
|
|
25
|
+
"data-orientation": orientation,
|
|
26
|
+
className: cn(
|
|
27
|
+
"inline-flex items-center gap-1",
|
|
28
|
+
orientation === "vertical" && "flex-col",
|
|
29
|
+
className
|
|
30
|
+
),
|
|
31
|
+
...props,
|
|
32
|
+
children
|
|
33
|
+
}
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
);
|
|
37
|
+
ButtonIconGroup.displayName = "ButtonIconGroup";
|
|
38
|
+
export {
|
|
39
|
+
ButtonIconGroup
|
|
40
|
+
};
|
|
41
|
+
//# sourceMappingURL=ButtonIconGroup.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ButtonIconGroup.mjs","sources":["../../../src/components/ButtonIconGroup/ButtonIconGroup.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\n/** Orientation of the icon button cluster. */\nexport type ButtonIconGroupOrientation = \"horizontal\" | \"vertical\";\n\n// Roles whose ARIA semantics include an orientation. For these we mirror the\n// visual orientation so assistive tech isn't given the spec default\n// (horizontal) when the cluster is stacked vertically.\nconst ORIENTATION_AWARE_ROLES = new Set<string>([\n \"toolbar\",\n \"menu\",\n \"menubar\",\n \"listbox\",\n \"radiogroup\",\n \"tablist\",\n \"tree\",\n]);\n\nexport interface ButtonIconGroupProps extends React.HTMLAttributes<HTMLDivElement> {\n /**\n * Direction the icon buttons flow in. Use `vertical` for side rails and\n * stacked media controls. @default \"horizontal\"\n */\n orientation?: ButtonIconGroupOrientation;\n /**\n * The icon buttons to cluster together. Compose {@link IconButton} elements\n * here; the group only handles layout and spacing.\n */\n children: React.ReactNode;\n}\n\n/**\n * A pre-composed cluster of {@link IconButton}s for toolbars and action rows\n * (media controls, editing tools, content action bars). Items are spaced with a\n * fixed 4px gap and stay individually rounded — this is a spaced group, not a\n * segmented/joined control.\n *\n * Exposes `role=\"group\"` by default; pass an `aria-label` or `aria-labelledby`\n * to describe the cluster for assistive technology.\n *\n * This is a presentational layout primitive: it does not implement\n * roving-tabindex / arrow-key focus management. `role` is overridable, but if\n * you set an interactive-widget role such as `role=\"toolbar\"` you own the\n * keyboard behaviour that role implies. For orientation-aware roles the\n * `aria-orientation` is synced to `orientation` automatically.\n *\n * @example\n * ```tsx\n * <ButtonIconGroup aria-label=\"Media controls\">\n * <IconButton variant=\"tertiary\" size=\"48\" icon={<PlayIcon />} aria-label=\"Play\" />\n * <IconButton variant=\"tertiary\" size=\"48\" icon={<ForwardIcon />} aria-label=\"Skip\" />\n * </ButtonIconGroup>\n * ```\n */\nexport const ButtonIconGroup = React.forwardRef<HTMLDivElement, ButtonIconGroupProps>(\n ({ className, orientation = \"horizontal\", role = \"group\", children, ...props }, ref) => {\n const supportsOrientation = typeof role === \"string\" && ORIENTATION_AWARE_ROLES.has(role);\n // Spread rather than a static attribute so the value is only present for\n // orientation-aware roles (a bare div/group role does not support it).\n const orientationProps = supportsOrientation ? { \"aria-orientation\": orientation } : {};\n return (\n <div\n ref={ref}\n role={role}\n {...orientationProps}\n data-testid=\"button-icon-group\"\n data-orientation={orientation}\n className={cn(\n \"inline-flex items-center gap-1\",\n orientation === \"vertical\" && \"flex-col\",\n className,\n )}\n {...props}\n >\n {children}\n </div>\n );\n },\n);\n\nButtonIconGroup.displayName = \"ButtonIconGroup\";\n"],"names":[],"mappings":";;;;AASA,MAAM,8CAA8B,IAAY;AAAA,EAC9C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAsCM,MAAM,kBAAkB,MAAM;AAAA,EACnC,CAAC,EAAE,WAAW,cAAc,cAAc,OAAO,SAAS,UAAU,GAAG,MAAA,GAAS,QAAQ;AACtF,UAAM,sBAAsB,OAAO,SAAS,YAAY,wBAAwB,IAAI,IAAI;AAGxF,UAAM,mBAAmB,sBAAsB,EAAE,oBAAoB,YAAA,IAAgB,CAAA;AACrF,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA;AAAA,QACC,GAAG;AAAA,QACJ,eAAY;AAAA,QACZ,oBAAkB;AAAA,QAClB,WAAW;AAAA,UACT;AAAA,UACA,gBAAgB,cAAc;AAAA,UAC9B;AAAA,QAAA;AAAA,QAED,GAAG;AAAA,QAEH;AAAA,MAAA;AAAA,IAAA;AAAA,EAGP;AACF;AAEA,gBAAgB,cAAc;"}
|
|
@@ -4,12 +4,16 @@ import * as React from "react";
|
|
|
4
4
|
import { cn } from "../../utils/cn.mjs";
|
|
5
5
|
import { Card } from "../Card/Card.mjs";
|
|
6
6
|
import { IconButton } from "../IconButton/IconButton.mjs";
|
|
7
|
-
import {
|
|
7
|
+
import { ArrowUpRightIcon } from "../Icons/ArrowUpRightIcon.mjs";
|
|
8
|
+
import { InfoIcon } from "../Icons/InfoIcon.mjs";
|
|
8
9
|
import { Skeleton } from "../Skeleton/Skeleton.mjs";
|
|
9
10
|
import { TooltipProvider, Tooltip, TooltipTrigger, TooltipContent } from "../Tooltip/Tooltip.mjs";
|
|
10
11
|
const TREND_CLASSES = {
|
|
11
|
-
positive: "
|
|
12
|
-
negative: "
|
|
12
|
+
positive: "text-success-content",
|
|
13
|
+
negative: "text-error-content"
|
|
14
|
+
};
|
|
15
|
+
const SURFACE_CLASSES = {
|
|
16
|
+
primary: "rounded-sm border-border-strong bg-background-primary"
|
|
13
17
|
};
|
|
14
18
|
const ChartCard = React.forwardRef(
|
|
15
19
|
({
|
|
@@ -25,45 +29,66 @@ const ChartCard = React.forwardRef(
|
|
|
25
29
|
children,
|
|
26
30
|
...props
|
|
27
31
|
}, ref) => {
|
|
28
|
-
return /* @__PURE__ */ jsx(
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
children:
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
32
|
+
return /* @__PURE__ */ jsx(
|
|
33
|
+
Card,
|
|
34
|
+
{
|
|
35
|
+
ref,
|
|
36
|
+
hierarchy,
|
|
37
|
+
noPadding: true,
|
|
38
|
+
className: cn(SURFACE_CLASSES[hierarchy], className),
|
|
39
|
+
...props,
|
|
40
|
+
children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 p-4", children: [
|
|
41
|
+
loading ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
42
|
+
/* @__PURE__ */ jsx(Skeleton, { animation: "wave", variant: "rounded", className: "h-4 w-32" }),
|
|
43
|
+
/* @__PURE__ */ jsx(Skeleton, { animation: "wave", variant: "rounded", className: "h-7 w-44" }),
|
|
44
|
+
/* @__PURE__ */ jsx(Skeleton, { animation: "wave", variant: "rounded", className: "h-3 w-24" })
|
|
45
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
46
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
47
|
+
/* @__PURE__ */ jsx("span", { className: "typography-body-small-14px-regular text-content-secondary", children: title }),
|
|
48
|
+
tooltip && /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
49
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
50
|
+
IconButton,
|
|
51
|
+
{
|
|
52
|
+
variant: "tertiary",
|
|
53
|
+
size: "32",
|
|
54
|
+
"aria-label": tooltipAriaLabel,
|
|
55
|
+
className: "text-icons-primary hover:text-content-primary focus-visible:text-content-primary active:text-content-primary",
|
|
56
|
+
icon: /* @__PURE__ */ jsx(InfoIcon, { size: 16 })
|
|
57
|
+
}
|
|
58
|
+
) }),
|
|
59
|
+
/* @__PURE__ */ jsx(TooltipContent, { children: tooltip })
|
|
60
|
+
] }) })
|
|
61
|
+
] }),
|
|
62
|
+
subtitle && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-end gap-2", children: [
|
|
63
|
+
/* @__PURE__ */ jsx("span", { className: "typography-header-heading-sm text-content-primary", children: subtitle }),
|
|
64
|
+
trendChip && /* @__PURE__ */ jsxs(
|
|
65
|
+
"span",
|
|
66
|
+
{
|
|
67
|
+
className: cn(
|
|
68
|
+
"flex items-center gap-1 pb-px",
|
|
69
|
+
TREND_CLASSES[trendChip.trend]
|
|
70
|
+
),
|
|
71
|
+
children: [
|
|
72
|
+
/* @__PURE__ */ jsx(
|
|
73
|
+
ArrowUpRightIcon,
|
|
74
|
+
{
|
|
75
|
+
className: cn(
|
|
76
|
+
"size-4 shrink-0",
|
|
77
|
+
trendChip.trend === "negative" && "rotate-90"
|
|
78
|
+
)
|
|
79
|
+
}
|
|
80
|
+
),
|
|
81
|
+
/* @__PURE__ */ jsx("span", { className: "typography-body-small-14px-regular whitespace-nowrap", children: trendChip.label })
|
|
82
|
+
]
|
|
83
|
+
}
|
|
84
|
+
)
|
|
85
|
+
] }),
|
|
86
|
+
dateInfo && /* @__PURE__ */ jsx("span", { className: "typography-description-12px-regular text-content-tertiary", children: dateInfo })
|
|
87
|
+
] }),
|
|
88
|
+
children && /* @__PURE__ */ jsx("div", { className: "mt-auto", children })
|
|
89
|
+
] })
|
|
90
|
+
}
|
|
91
|
+
);
|
|
67
92
|
}
|
|
68
93
|
);
|
|
69
94
|
ChartCard.displayName = "ChartCard";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChartCard.mjs","sources":["../../../src/components/Chart/ChartCard.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { Card, type CardHierarchy } from \"../Card/Card\";\nimport { IconButton } from \"../IconButton/IconButton\";\nimport {
|
|
1
|
+
{"version":3,"file":"ChartCard.mjs","sources":["../../../src/components/Chart/ChartCard.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { Card, type CardHierarchy } from \"../Card/Card\";\nimport { IconButton } from \"../IconButton/IconButton\";\nimport { ArrowUpRightIcon } from \"../Icons/ArrowUpRightIcon\";\nimport { InfoIcon } from \"../Icons/InfoIcon\";\nimport { Skeleton } from \"../Skeleton/Skeleton\";\nimport { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from \"../Tooltip/Tooltip\";\n\n/** Props for {@link ChartCard}. */\nexport interface ChartCardProps extends Omit<React.HTMLAttributes<HTMLDivElement>, \"title\"> {\n /**\n * Surface treatment. `primary` follows the V2 Insight Card spec (white fill,\n * strong border, 12px radius); `secondary` uses the {@link Card} secondary\n * surface. @default \"primary\"\n */\n hierarchy?: CardHierarchy;\n /** Card title text. Pass translated string for i18n. */\n title: React.ReactNode;\n /** Large subtitle value (e.g. formatted price or count). */\n subtitle?: React.ReactNode;\n /** Tooltip text shown next to the title. Pass translated string for i18n. */\n tooltip?: React.ReactNode;\n /** Accessible label for the info tooltip trigger. Override for i18n. @default \"More info\" */\n tooltipAriaLabel?: string;\n /** Date range or period label shown below the subtitle. */\n dateInfo?: React.ReactNode;\n /**\n * Trend indicator config. Rendered as a coloured directional arrow and label\n * beside the subtitle, so it is only shown when {@link subtitle} is provided.\n */\n trendChip?: {\n /** Display label (e.g. \"12.5% vs prev\"). */\n label: React.ReactNode;\n /** Whether the trend is positive (green, arrow up) or negative (red, arrow down). */\n trend: \"positive\" | \"negative\";\n };\n /** Show loading skeleton instead of content. @default false */\n loading?: boolean;\n /** Chart content rendered below the header. */\n children?: React.ReactNode;\n}\n\nconst TREND_CLASSES: Record<\"positive\" | \"negative\", string> = {\n positive: \"text-success-content\",\n negative: \"text-error-content\",\n};\n\nconst SURFACE_CLASSES: Partial<Record<CardHierarchy, string>> = {\n primary: \"rounded-sm border-border-strong bg-background-primary\",\n};\n\n/**\n * Wraps any chart with a structured header containing title, subtitle,\n * optional trend indicator, date range label, info tooltip, and a loading\n * skeleton state.\n *\n * At `hierarchy=\"primary\"` this implements the V2 Insight Card surface, which\n * differs from {@link Card}'s own primary hierarchy: a white fill, the strong\n * border, and a 12px radius.\n *\n * @example\n * ```tsx\n * <ChartCard\n * title=\"Revenue\"\n * subtitle=\"$4,523\"\n * trendChip={{ label: \"+12.5%\", trend: \"positive\" }}\n * dateInfo=\"Jan 1 – Mar 17\"\n * tooltip=\"Total revenue for the selected period.\"\n * >\n * <MyLineChart />\n * </ChartCard>\n * ```\n */\nexport const ChartCard = React.forwardRef<HTMLDivElement, ChartCardProps>(\n (\n {\n className,\n hierarchy = \"primary\",\n title,\n subtitle,\n tooltip,\n tooltipAriaLabel = \"More info\",\n dateInfo,\n trendChip,\n loading = false,\n children,\n ...props\n },\n ref,\n ) => {\n return (\n <Card\n ref={ref}\n hierarchy={hierarchy}\n noPadding\n className={cn(SURFACE_CLASSES[hierarchy], className)}\n {...props}\n >\n <div className=\"flex flex-col gap-2 p-4\">\n {loading ? (\n <>\n <Skeleton animation=\"wave\" variant=\"rounded\" className=\"h-4 w-32\" />\n <Skeleton animation=\"wave\" variant=\"rounded\" className=\"h-7 w-44\" />\n <Skeleton animation=\"wave\" variant=\"rounded\" className=\"h-3 w-24\" />\n </>\n ) : (\n <>\n <div className=\"flex items-center gap-1\">\n <span className=\"typography-body-small-14px-regular text-content-secondary\">\n {title}\n </span>\n {tooltip && (\n <TooltipProvider>\n <Tooltip>\n <TooltipTrigger asChild>\n <IconButton\n variant=\"tertiary\"\n size=\"32\"\n aria-label={tooltipAriaLabel}\n className=\"text-icons-primary hover:text-content-primary focus-visible:text-content-primary active:text-content-primary\"\n icon={<InfoIcon size={16} />}\n />\n </TooltipTrigger>\n <TooltipContent>{tooltip}</TooltipContent>\n </Tooltip>\n </TooltipProvider>\n )}\n </div>\n {subtitle && (\n <div className=\"flex flex-wrap items-end gap-2\">\n <span className=\"typography-header-heading-sm text-content-primary\">\n {subtitle}\n </span>\n {trendChip && (\n <span\n className={cn(\n \"flex items-center gap-1 pb-px\",\n TREND_CLASSES[trendChip.trend],\n )}\n >\n <ArrowUpRightIcon\n className={cn(\n \"size-4 shrink-0\",\n trendChip.trend === \"negative\" && \"rotate-90\",\n )}\n />\n <span className=\"typography-body-small-14px-regular whitespace-nowrap\">\n {trendChip.label}\n </span>\n </span>\n )}\n </div>\n )}\n {dateInfo && (\n <span className=\"typography-description-12px-regular text-content-tertiary\">\n {dateInfo}\n </span>\n )}\n </>\n )}\n {children && <div className=\"mt-auto\">{children}</div>}\n </div>\n </Card>\n );\n },\n);\nChartCard.displayName = \"ChartCard\";\n"],"names":[],"mappings":";;;;;;;;;;AA2CA,MAAM,gBAAyD;AAAA,EAC7D,UAAU;AAAA,EACV,UAAU;AACZ;AAEA,MAAM,kBAA0D;AAAA,EAC9D,SAAS;AACX;AAwBO,MAAM,YAAY,MAAM;AAAA,EAC7B,CACE;AAAA,IACE;AAAA,IACA,YAAY;AAAA,IACZ;AAAA,IACA;AAAA,IACA;AAAA,IACA,mBAAmB;AAAA,IACnB;AAAA,IACA;AAAA,IACA,UAAU;AAAA,IACV;AAAA,IACA,GAAG;AAAA,EAAA,GAEL,QACG;AACH,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA,WAAS;AAAA,QACT,WAAW,GAAG,gBAAgB,SAAS,GAAG,SAAS;AAAA,QAClD,GAAG;AAAA,QAEJ,UAAA,qBAAC,OAAA,EAAI,WAAU,2BACZ,UAAA;AAAA,UAAA,UACC,qBAAA,UAAA,EACE,UAAA;AAAA,YAAA,oBAAC,YAAS,WAAU,QAAO,SAAQ,WAAU,WAAU,YAAW;AAAA,gCACjE,UAAA,EAAS,WAAU,QAAO,SAAQ,WAAU,WAAU,YAAW;AAAA,gCACjE,UAAA,EAAS,WAAU,QAAO,SAAQ,WAAU,WAAU,WAAA,CAAW;AAAA,UAAA,EAAA,CACpE,IAEA,qBAAA,UAAA,EACE,UAAA;AAAA,YAAA,qBAAC,OAAA,EAAI,WAAU,2BACb,UAAA;AAAA,cAAA,oBAAC,QAAA,EAAK,WAAU,6DACb,UAAA,OACH;AAAA,cACC,WACC,oBAAC,iBAAA,EACC,UAAA,qBAAC,SAAA,EACC,UAAA;AAAA,gBAAA,oBAAC,gBAAA,EAAe,SAAO,MACrB,UAAA;AAAA,kBAAC;AAAA,kBAAA;AAAA,oBACC,SAAQ;AAAA,oBACR,MAAK;AAAA,oBACL,cAAY;AAAA,oBACZ,WAAU;AAAA,oBACV,MAAM,oBAAC,UAAA,EAAS,MAAM,GAAA,CAAI;AAAA,kBAAA;AAAA,gBAAA,GAE9B;AAAA,gBACA,oBAAC,kBAAgB,UAAA,QAAA,CAAQ;AAAA,cAAA,EAAA,CAC3B,EAAA,CACF;AAAA,YAAA,GAEJ;AAAA,YACC,YACC,qBAAC,OAAA,EAAI,WAAU,kCACb,UAAA;AAAA,cAAA,oBAAC,QAAA,EAAK,WAAU,qDACb,UAAA,UACH;AAAA,cACC,aACC;AAAA,gBAAC;AAAA,gBAAA;AAAA,kBACC,WAAW;AAAA,oBACT;AAAA,oBACA,cAAc,UAAU,KAAK;AAAA,kBAAA;AAAA,kBAG/B,UAAA;AAAA,oBAAA;AAAA,sBAAC;AAAA,sBAAA;AAAA,wBACC,WAAW;AAAA,0BACT;AAAA,0BACA,UAAU,UAAU,cAAc;AAAA,wBAAA;AAAA,sBACpC;AAAA,oBAAA;AAAA,oBAEF,oBAAC,QAAA,EAAK,WAAU,wDACb,oBAAU,MAAA,CACb;AAAA,kBAAA;AAAA,gBAAA;AAAA,cAAA;AAAA,YACF,GAEJ;AAAA,YAED,YACC,oBAAC,QAAA,EAAK,WAAU,6DACb,UAAA,SAAA,CACH;AAAA,UAAA,GAEJ;AAAA,UAED,YAAY,oBAAC,OAAA,EAAI,WAAU,WAAW,SAAA,CAAS;AAAA,QAAA,EAAA,CAClD;AAAA,MAAA;AAAA,IAAA;AAAA,EAGN;AACF;AACA,UAAU,cAAc;"}
|
|
@@ -6,7 +6,8 @@ import { Avatar } from "../Avatar/Avatar.mjs";
|
|
|
6
6
|
const ASPECT_RATIO_CLASSES = {
|
|
7
7
|
tall: "aspect-5/4",
|
|
8
8
|
medium: "aspect-3/2",
|
|
9
|
-
short: "aspect-9/5"
|
|
9
|
+
short: "aspect-9/5",
|
|
10
|
+
banner: "aspect-5/2"
|
|
10
11
|
};
|
|
11
12
|
const CreatorTile = React.forwardRef(
|
|
12
13
|
({ className, background, name, tagline, avatar, action, aspectRatio = "medium", ...props }, ref) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CreatorTile.mjs","sources":["../../../src/components/CreatorTile/CreatorTile.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { Avatar } from \"../Avatar/Avatar\";\n\n/** Width-to-height ratio preset for the tile. */\nexport type CreatorTileAspectRatio = \"tall\" | \"medium\" | \"short\";\n\nconst ASPECT_RATIO_CLASSES: Record<CreatorTileAspectRatio, string> = {\n tall: \"aspect-5/4\",\n medium: \"aspect-3/2\",\n short: \"aspect-9/5\",\n};\n\nexport interface CreatorTileProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Decorative background media rendered behind the creator content. */\n background: React.ReactNode;\n /** Creator display name shown as the prominent heading. */\n name: React.ReactNode;\n /** Optional secondary line shown under the name (e.g. handle or tagline). */\n tagline?: React.ReactNode;\n /** Avatar props forwarded to the inner {@link Avatar}. */\n avatar?: React.ComponentPropsWithoutRef<typeof Avatar>;\n /**\n * Action element rendered on the right of the profile row (e.g. a `Button`\n * for following the creator).\n */\n action?: React.ReactNode;\n /**\n * Width-to-height ratio preset.\n *\n * - `tall` –
|
|
1
|
+
{"version":3,"file":"CreatorTile.mjs","sources":["../../../src/components/CreatorTile/CreatorTile.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { Avatar } from \"../Avatar/Avatar\";\n\n/** Width-to-height ratio preset for the tile. */\nexport type CreatorTileAspectRatio = \"tall\" | \"medium\" | \"short\" | \"banner\";\n\nconst ASPECT_RATIO_CLASSES: Record<CreatorTileAspectRatio, string> = {\n tall: \"aspect-5/4\",\n medium: \"aspect-3/2\",\n short: \"aspect-9/5\",\n banner: \"aspect-5/2\",\n};\n\nexport interface CreatorTileProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Decorative background media rendered behind the creator content. */\n background: React.ReactNode;\n /** Creator display name shown as the prominent heading. */\n name: React.ReactNode;\n /** Optional secondary line shown under the name (e.g. handle or tagline). */\n tagline?: React.ReactNode;\n /** Avatar props forwarded to the inner {@link Avatar}. */\n avatar?: React.ComponentPropsWithoutRef<typeof Avatar>;\n /**\n * Action element rendered on the right of the profile row (e.g. a `Button`\n * for following the creator).\n */\n action?: React.ReactNode;\n /**\n * Width-to-height ratio preset.\n *\n * - `tall` – 5:4, closest to square\n * - `medium` – 3:2 landscape\n * - `short` – 9:5 landscape\n * - `banner` – 5:2, the slimmest preset\n *\n * The profile row is a fixed ~74px tall, so `banner` needs at least 185px of\n * width before the ratio makes the tile shorter than its own row and the top\n * of the row is clipped. The taller presets reach that floor at widths too\n * narrow to be practical.\n *\n * @default \"medium\"\n */\n aspectRatio?: CreatorTileAspectRatio;\n}\n\n/**\n * A visual highlight tile showcasing a creator with a full-bleed background\n * media and an overlaid profile row containing an avatar, name, optional\n * tagline and an action element.\n *\n * @example\n * ```tsx\n * <CreatorTile\n * background={<img src=\"/creator.jpg\" alt=\"\" />}\n * avatar={{ src: \"/avatar.jpg\", alt: \"Aitana Lopez\", fallback: \"AL\" }}\n * name=\"Aitana Lopez\"\n * tagline=\"@fit_aitana\"\n * action={<Button variant=\"primary\">Follow</Button>}\n * />\n * ```\n */\nexport const CreatorTile = React.forwardRef<HTMLDivElement, CreatorTileProps>(\n (\n { className, background, name, tagline, avatar, action, aspectRatio = \"medium\", ...props },\n ref,\n ) => {\n const aspectClass = ASPECT_RATIO_CLASSES[aspectRatio];\n\n return (\n <div\n ref={ref}\n className={cn(\n \"relative isolate flex w-full flex-col justify-end overflow-hidden\",\n aspectClass,\n className,\n )}\n {...props}\n >\n <div className=\"pointer-events-none absolute inset-0 select-none *:h-full *:w-full [&>img]:object-cover [&>video]:object-cover\">\n {background}\n </div>\n <div className=\"pointer-events-none absolute inset-x-0 bottom-0 h-1/2 rounded-b-[inherit] bg-linear-to-t from-black/60 to-transparent\" />\n <div className=\"pointer-events-none absolute inset-x-0 bottom-0 h-1/2 overflow-hidden rounded-b-[inherit] backdrop-blur-md [-webkit-mask-image:linear-gradient(to_top,black,transparent)] [mask-image:linear-gradient(to_top,black,transparent)]\" />\n <div className=\"relative flex items-center justify-between gap-4 p-4\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <Avatar\n size={40}\n src={avatar?.src}\n alt={avatar?.alt ?? (typeof name === \"string\" ? name : undefined)}\n fallback={avatar?.fallback}\n {...avatar}\n />\n <div className=\"flex min-w-0 flex-col\">\n <p className=\"typography-body-default-16px-semibold m-0 truncate text-white\">\n {name}\n </p>\n {tagline ? (\n <p className=\"typography-body-small-14px-semibold m-0 truncate text-white/50\">\n {tagline}\n </p>\n ) : null}\n </div>\n </div>\n {action ? <div className=\"shrink-0\">{action}</div> : null}\n </div>\n </div>\n );\n },\n);\n\nCreatorTile.displayName = \"CreatorTile\";\n"],"names":[],"mappings":";;;;;AAOA,MAAM,uBAA+D;AAAA,EACnE,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,QAAQ;AACV;AAkDO,MAAM,cAAc,MAAM;AAAA,EAC/B,CACE,EAAE,WAAW,YAAY,MAAM,SAAS,QAAQ,QAAQ,cAAc,UAAU,GAAG,MAAA,GACnF,QACG;AACH,UAAM,cAAc,qBAAqB,WAAW;AAEpD,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,WAAW;AAAA,UACT;AAAA,UACA;AAAA,UACA;AAAA,QAAA;AAAA,QAED,GAAG;AAAA,QAEJ,UAAA;AAAA,UAAA,oBAAC,OAAA,EAAI,WAAU,kHACZ,UAAA,YACH;AAAA,UACA,oBAAC,OAAA,EAAI,WAAU,wHAAA,CAAwH;AAAA,UACvI,oBAAC,OAAA,EAAI,WAAU,mOAAA,CAAmO;AAAA,UAClP,qBAAC,OAAA,EAAI,WAAU,wDACb,UAAA;AAAA,YAAA,qBAAC,OAAA,EAAI,WAAU,mCACb,UAAA;AAAA,cAAA;AAAA,gBAAC;AAAA,gBAAA;AAAA,kBACC,MAAM;AAAA,kBACN,KAAK,QAAQ;AAAA,kBACb,KAAK,QAAQ,QAAQ,OAAO,SAAS,WAAW,OAAO;AAAA,kBACvD,UAAU,QAAQ;AAAA,kBACjB,GAAG;AAAA,gBAAA;AAAA,cAAA;AAAA,cAEN,qBAAC,OAAA,EAAI,WAAU,yBACb,UAAA;AAAA,gBAAA,oBAAC,KAAA,EAAE,WAAU,iEACV,UAAA,MACH;AAAA,gBACC,UACC,oBAAC,KAAA,EAAE,WAAU,kEACV,mBACH,IACE;AAAA,cAAA,EAAA,CACN;AAAA,YAAA,GACF;AAAA,YACC,SAAS,oBAAC,OAAA,EAAI,WAAU,YAAY,kBAAO,IAAS;AAAA,UAAA,EAAA,CACvD;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,EAGN;AACF;AAEA,YAAY,cAAc;"}
|
|
@@ -2,10 +2,13 @@
|
|
|
2
2
|
import { jsxs, jsx } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { cn } from "../../utils/cn.mjs";
|
|
5
|
+
import { AIDisclosureIcon } from "../Icons/AIDisclosureIcon.mjs";
|
|
5
6
|
import { VerifiedIcon } from "../Icons/VerifiedIcon.mjs";
|
|
6
7
|
import { ProfileOnlineStatus } from "../ProfileOnlineStatus/ProfileOnlineStatus.mjs";
|
|
7
8
|
const UserDisplayName = React.forwardRef(
|
|
8
9
|
({
|
|
10
|
+
aiDisclosure,
|
|
11
|
+
aiDisclosureLabel = "AI creator",
|
|
9
12
|
ambassador,
|
|
10
13
|
ambassadorLabel = "Ambassador",
|
|
11
14
|
children,
|
|
@@ -19,7 +22,7 @@ const UserDisplayName = React.forwardRef(
|
|
|
19
22
|
...props
|
|
20
23
|
}, ref) => {
|
|
21
24
|
const Component = component;
|
|
22
|
-
const badge = ambassador ? { label: ambassadorLabel, tint: "text-success-content" } : verified ? { label: verifiedLabel, tint: "text-
|
|
25
|
+
const badge = ambassador ? { label: ambassadorLabel, tint: "text-success-content" } : verified ? { label: verifiedLabel, tint: "text-current" } : null;
|
|
23
26
|
return /* @__PURE__ */ jsxs(
|
|
24
27
|
Component,
|
|
25
28
|
{
|
|
@@ -37,6 +40,15 @@ const UserDisplayName = React.forwardRef(
|
|
|
37
40
|
children: /* @__PURE__ */ jsx(VerifiedIcon, { size: 16 })
|
|
38
41
|
}
|
|
39
42
|
),
|
|
43
|
+
aiDisclosure && /* @__PURE__ */ jsx(
|
|
44
|
+
"span",
|
|
45
|
+
{
|
|
46
|
+
role: "img",
|
|
47
|
+
"aria-label": aiDisclosureLabel,
|
|
48
|
+
className: "ml-2 inline-flex shrink-0 items-center text-current",
|
|
49
|
+
children: /* @__PURE__ */ jsx(AIDisclosureIcon, { size: 16 })
|
|
50
|
+
}
|
|
51
|
+
),
|
|
40
52
|
showOnlineStatus && /* @__PURE__ */ jsx(ProfileOnlineStatus, { label: onlineLabel, className: "shrink-0 ml-2" })
|
|
41
53
|
]
|
|
42
54
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UserDisplayName.mjs","sources":["../../../src/components/UserDisplayName/UserDisplayName.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { VerifiedIcon } from \"../Icons/VerifiedIcon\";\nimport { ProfileOnlineStatus } from \"../ProfileOnlineStatus/ProfileOnlineStatus\";\n\nexport interface UserDisplayNameProps extends React.HTMLAttributes<HTMLElement> {\n /** Render an ambassador badge after the name. */\n ambassador?: boolean;\n /** Accessible label for the ambassador badge. @default \"Ambassador\" */\n ambassadorLabel?: string;\n /** HTML element used for the root wrapper. @default \"span\" */\n component?: \"span\" | \"h1\" | \"h2\" | \"h3\" | \"p\" | \"div\";\n /** Truncate the display name on a single line. @default true */\n noWrap?: boolean;\n /** Accessible label for the online-status indicator. @default \"Online\" */\n onlineLabel?: string;\n /** Render the inline online-status indicator after the name. @default false */\n showOnlineStatus?: boolean;\n /** Render a verified badge after the name (ignored when `ambassador` is set). */\n verified?: boolean;\n /** Accessible label for the verified badge. @default \"Verified\" */\n verifiedLabel?: string;\n}\n\n/**\n * Renders a user's display name with optional ambassador/verified badges and an\n * online-status indicator. Only the name shrinks and truncates; trailing badges\n * and the online-status indicator stay visible on the same line.\n *\n * Typography is not baked in: pass a `typography-*` utility via `className` on\n * the root and the name inherits it. Use `typography-body-small-14px-semibold`\n * for the standard display-name scale.\n *\n * When both `ambassador` and `verified` are set, the ambassador badge takes\n * precedence.
|
|
1
|
+
{"version":3,"file":"UserDisplayName.mjs","sources":["../../../src/components/UserDisplayName/UserDisplayName.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { AIDisclosureIcon } from \"../Icons/AIDisclosureIcon\";\nimport { VerifiedIcon } from \"../Icons/VerifiedIcon\";\nimport { ProfileOnlineStatus } from \"../ProfileOnlineStatus/ProfileOnlineStatus\";\n\nexport interface UserDisplayNameProps extends React.HTMLAttributes<HTMLElement> {\n /** Render an AI-disclosure badge after the name, alongside any status badge. */\n aiDisclosure?: boolean;\n /** Accessible label for the AI-disclosure badge. @default \"AI creator\" */\n aiDisclosureLabel?: string;\n /** Render an ambassador badge after the name. */\n ambassador?: boolean;\n /** Accessible label for the ambassador badge. @default \"Ambassador\" */\n ambassadorLabel?: string;\n /** HTML element used for the root wrapper. @default \"span\" */\n component?: \"span\" | \"h1\" | \"h2\" | \"h3\" | \"p\" | \"div\";\n /** Truncate the display name on a single line. @default true */\n noWrap?: boolean;\n /** Accessible label for the online-status indicator. @default \"Online\" */\n onlineLabel?: string;\n /** Render the inline online-status indicator after the name. @default false */\n showOnlineStatus?: boolean;\n /** Render a verified badge after the name (ignored when `ambassador` is set). */\n verified?: boolean;\n /** Accessible label for the verified badge. @default \"Verified\" */\n verifiedLabel?: string;\n}\n\n/**\n * Renders a user's display name with optional ambassador/verified badges and an\n * online-status indicator. Only the name shrinks and truncates; trailing badges\n * and the online-status indicator stay visible on the same line.\n *\n * Typography is not baked in: pass a `typography-*` utility via `className` on\n * the root and the name inherits it. Use `typography-body-small-14px-semibold`\n * for the standard display-name scale.\n *\n * When both `ambassador` and `verified` are set, the ambassador badge takes\n * precedence. `aiDisclosure` is independent of both and renders after whichever\n * status badge is shown, since it discloses how the account is operated rather\n * than ranking it.\n *\n * The verified and AI-disclosure badges inherit the name's colour, so setting a\n * colour on the root tints them with it. That keeps them legible on surfaces\n * that fix their own text colour — over a cover image, for example, where a\n * theme-dependent tint would turn black against white text in light mode.\n *\n * The ambassador tint is deliberately not inherited: it uses `text-success-content`\n * (not `text-icons-brand-green`) so the green darkens in light mode and lightens\n * in dark mode, matching {@link ProfileOnlineStatus}; `text-icons-brand-green` is\n * fixed across modes.\n *\n * @example\n * ```tsx\n * <UserDisplayName className=\"typography-body-small-14px-semibold\">Jane Doe</UserDisplayName>\n * ```\n */\nexport const UserDisplayName = React.forwardRef<HTMLElement, UserDisplayNameProps>(\n (\n {\n aiDisclosure,\n aiDisclosureLabel = \"AI creator\",\n ambassador,\n ambassadorLabel = \"Ambassador\",\n children,\n className,\n component = \"span\",\n noWrap = true,\n onlineLabel = \"Online\",\n showOnlineStatus,\n verified,\n verifiedLabel = \"Verified\",\n ...props\n },\n ref,\n ) => {\n const Component = component as React.ElementType;\n\n const badge = ambassador\n ? { label: ambassadorLabel, tint: \"text-success-content\" }\n : verified\n ? { label: verifiedLabel, tint: \"text-current\" }\n : null;\n\n return (\n <Component\n ref={ref}\n className={cn(\"inline-flex max-w-full items-center\", className)}\n {...props}\n >\n <span className={cn(\"min-w-0\", noWrap && \"truncate\")}>{children}</span>\n {badge && (\n <span\n role=\"img\"\n aria-label={badge.label}\n className={cn(\"ml-2 inline-flex shrink-0 items-center\", badge.tint)}\n >\n <VerifiedIcon size={16} />\n </span>\n )}\n {aiDisclosure && (\n <span\n role=\"img\"\n aria-label={aiDisclosureLabel}\n className=\"ml-2 inline-flex shrink-0 items-center text-current\"\n >\n <AIDisclosureIcon size={16} />\n </span>\n )}\n {showOnlineStatus && <ProfileOnlineStatus label={onlineLabel} className=\"shrink-0 ml-2\" />}\n </Component>\n );\n },\n);\n\nUserDisplayName.displayName = \"UserDisplayName\";\n"],"names":[],"mappings":";;;;;;;AA0DO,MAAM,kBAAkB,MAAM;AAAA,EACnC,CACE;AAAA,IACE;AAAA,IACA,oBAAoB;AAAA,IACpB;AAAA,IACA,kBAAkB;AAAA,IAClB;AAAA,IACA;AAAA,IACA,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,cAAc;AAAA,IACd;AAAA,IACA;AAAA,IACA,gBAAgB;AAAA,IAChB,GAAG;AAAA,EAAA,GAEL,QACG;AACH,UAAM,YAAY;AAElB,UAAM,QAAQ,aACV,EAAE,OAAO,iBAAiB,MAAM,uBAAA,IAChC,WACE,EAAE,OAAO,eAAe,MAAM,mBAC9B;AAEN,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,WAAW,GAAG,uCAAuC,SAAS;AAAA,QAC7D,GAAG;AAAA,QAEJ,UAAA;AAAA,UAAA,oBAAC,UAAK,WAAW,GAAG,WAAW,UAAU,UAAU,GAAI,UAAS;AAAA,UAC/D,SACC;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,MAAK;AAAA,cACL,cAAY,MAAM;AAAA,cAClB,WAAW,GAAG,0CAA0C,MAAM,IAAI;AAAA,cAElE,UAAA,oBAAC,cAAA,EAAa,MAAM,GAAA,CAAI;AAAA,YAAA;AAAA,UAAA;AAAA,UAG3B,gBACC;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,MAAK;AAAA,cACL,cAAY;AAAA,cACZ,WAAU;AAAA,cAEV,UAAA,oBAAC,kBAAA,EAAiB,MAAM,GAAA,CAAI;AAAA,YAAA;AAAA,UAAA;AAAA,UAG/B,oBAAoB,oBAAC,qBAAA,EAAoB,OAAO,aAAa,WAAU,gBAAA,CAAgB;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,EAG9F;AACF;AAEA,gBAAgB,cAAc;"}
|
|
@@ -20,6 +20,10 @@ const UserItem = React.forwardRef(
|
|
|
20
20
|
showOnlineStatus,
|
|
21
21
|
showHandle = true,
|
|
22
22
|
showAvatar = true,
|
|
23
|
+
aiDisclosure = false,
|
|
24
|
+
aiDisclosureLabel,
|
|
25
|
+
verified = false,
|
|
26
|
+
verifiedLabel,
|
|
23
27
|
className,
|
|
24
28
|
...props
|
|
25
29
|
}, ref) => {
|
|
@@ -42,7 +46,17 @@ const UserItem = React.forwardRef(
|
|
|
42
46
|
}
|
|
43
47
|
),
|
|
44
48
|
/* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-hidden pl-2", children: [
|
|
45
|
-
/* @__PURE__ */ jsx(
|
|
49
|
+
/* @__PURE__ */ jsx(
|
|
50
|
+
UserDisplayName,
|
|
51
|
+
{
|
|
52
|
+
aiDisclosure,
|
|
53
|
+
aiDisclosureLabel,
|
|
54
|
+
verified,
|
|
55
|
+
verifiedLabel,
|
|
56
|
+
className: "typography-body-small-14px-semibold",
|
|
57
|
+
children: user.nickname || user.displayName
|
|
58
|
+
}
|
|
59
|
+
),
|
|
46
60
|
showHandle && /* @__PURE__ */ jsx(UserHandle, { children: user.handle })
|
|
47
61
|
] })
|
|
48
62
|
]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UserItem.mjs","sources":["../../../src/components/UserItem/UserItem.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { getInitials } from \"../../utils/getInitials\";\nimport { Avatar } from \"../Avatar/Avatar\";\nimport { BellOffIcon } from \"../Icons/BellOffIcon\";\nimport { UserDisplayName } from \"../UserDisplayName/UserDisplayName\";\nimport { UserHandle } from \"../UserHandle/UserHandle\";\n\nconst AVATAR_SIZES = [16, 24, 32, 40, 48, 64, 88, 148] as const;\ntype AvatarSizeToken = (typeof AVATAR_SIZES)[number];\n\n/** Snap an arbitrary pixel size to the nearest supported {@link Avatar} size token. */\nconst getNearestAvatarSize = (size: number): AvatarSizeToken =>\n AVATAR_SIZES.reduce((prev, curr) =>\n Math.abs(curr - size) < Math.abs(prev - size) ? curr : prev,\n );\n\n/** Minimal user shape consumed by {@link UserItem}. */\nexport type UserItemUser = {\n avatarUri?: { url: string; url2x?: string };\n displayName: string;\n handle: string;\n nickname?: string;\n};\n\nexport interface UserItemProps extends React.HTMLAttributes<HTMLDivElement> {\n /** The user to display. */\n user: UserItemUser;\n /** Desired avatar size in px; snapped to the nearest supported token. @default 48 */\n avatarSize?: number;\n /** Show the muted indicator in the bottom-left corner. @default false */\n isMuted?: boolean;\n /** Whether the user is online (only shown when `showOnlineStatus` is also set). @default false */\n isOnline?: boolean;\n /** Render the avatar. @default true */\n showAvatar?: boolean;\n /** Render the handle line under the display name. @default true */\n showHandle?: boolean;\n /** Enable the avatar online indicator (combined with `isOnline`). */\n showOnlineStatus?: boolean;\n}\n\n/**\n * A compact user row showing an avatar, display name (or nickname) and handle,\n * with optional online and muted indicators.\n *\n * @example\n * ```tsx\n * <UserItem user={{ displayName: \"Jane Doe\", handle: \"jane_doe\" }} />\n * ```\n */\nexport const UserItem = React.forwardRef<HTMLDivElement, UserItemProps>(\n (\n {\n user,\n avatarSize,\n isOnline = false,\n isMuted = false,\n showOnlineStatus,\n showHandle = true,\n showAvatar = true,\n className,\n ...props\n },\n ref,\n ) => {\n return (\n <div\n ref={ref}\n className={cn(\"relative flex w-full items-center justify-start px-2 py-1\", className)}\n {...props}\n >\n {isMuted && (\n <div className=\"absolute bottom-0 left-0 z-1 flex rounded-xs bg-surface-primary p-1\">\n <BellOffIcon size={16} />\n </div>\n )}\n {showAvatar && (\n <Avatar\n size={getNearestAvatarSize(avatarSize || 48)}\n src={user.avatarUri?.url}\n alt={user.displayName}\n fallback={getInitials(user.displayName)}\n onlineIndicator={Boolean(showOnlineStatus && isOnline)}\n />\n )}\n <div className=\"flex-1 overflow-hidden pl-2\">\n <UserDisplayName
|
|
1
|
+
{"version":3,"file":"UserItem.mjs","sources":["../../../src/components/UserItem/UserItem.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { getInitials } from \"../../utils/getInitials\";\nimport { Avatar } from \"../Avatar/Avatar\";\nimport { BellOffIcon } from \"../Icons/BellOffIcon\";\nimport { UserDisplayName } from \"../UserDisplayName/UserDisplayName\";\nimport { UserHandle } from \"../UserHandle/UserHandle\";\n\nconst AVATAR_SIZES = [16, 24, 32, 40, 48, 64, 88, 148] as const;\ntype AvatarSizeToken = (typeof AVATAR_SIZES)[number];\n\n/** Snap an arbitrary pixel size to the nearest supported {@link Avatar} size token. */\nconst getNearestAvatarSize = (size: number): AvatarSizeToken =>\n AVATAR_SIZES.reduce((prev, curr) =>\n Math.abs(curr - size) < Math.abs(prev - size) ? curr : prev,\n );\n\n/** Minimal user shape consumed by {@link UserItem}. */\nexport type UserItemUser = {\n avatarUri?: { url: string; url2x?: string };\n displayName: string;\n handle: string;\n nickname?: string;\n};\n\nexport interface UserItemProps extends React.HTMLAttributes<HTMLDivElement> {\n /** The user to display. */\n user: UserItemUser;\n /** Desired avatar size in px; snapped to the nearest supported token. @default 48 */\n avatarSize?: number;\n /** Show the muted indicator in the bottom-left corner. @default false */\n isMuted?: boolean;\n /** Whether the user is online (only shown when `showOnlineStatus` is also set). @default false */\n isOnline?: boolean;\n /** Render the avatar. @default true */\n showAvatar?: boolean;\n /** Render the handle line under the display name. @default true */\n showHandle?: boolean;\n /** Enable the avatar online indicator (combined with `isOnline`). */\n showOnlineStatus?: boolean;\n /** Render an AI-disclosure badge after the display name. @default false */\n aiDisclosure?: boolean;\n /** Accessible label for the AI-disclosure badge. @default \"AI creator\" */\n aiDisclosureLabel?: string;\n /** Render a verified badge after the display name. @default false */\n verified?: boolean;\n /** Accessible label for the verified badge. @default \"Verified\" */\n verifiedLabel?: string;\n}\n\n/**\n * A compact user row showing an avatar, display name (or nickname) and handle,\n * with optional verified and AI-disclosure badges and online and muted indicators.\n *\n * @example\n * ```tsx\n * <UserItem user={{ displayName: \"Jane Doe\", handle: \"jane_doe\" }} />\n * ```\n */\nexport const UserItem = React.forwardRef<HTMLDivElement, UserItemProps>(\n (\n {\n user,\n avatarSize,\n isOnline = false,\n isMuted = false,\n showOnlineStatus,\n showHandle = true,\n showAvatar = true,\n aiDisclosure = false,\n aiDisclosureLabel,\n verified = false,\n verifiedLabel,\n className,\n ...props\n },\n ref,\n ) => {\n return (\n <div\n ref={ref}\n className={cn(\"relative flex w-full items-center justify-start px-2 py-1\", className)}\n {...props}\n >\n {isMuted && (\n <div className=\"absolute bottom-0 left-0 z-1 flex rounded-xs bg-surface-primary p-1\">\n <BellOffIcon size={16} />\n </div>\n )}\n {showAvatar && (\n <Avatar\n size={getNearestAvatarSize(avatarSize || 48)}\n src={user.avatarUri?.url}\n alt={user.displayName}\n fallback={getInitials(user.displayName)}\n onlineIndicator={Boolean(showOnlineStatus && isOnline)}\n />\n )}\n <div className=\"flex-1 overflow-hidden pl-2\">\n <UserDisplayName\n aiDisclosure={aiDisclosure}\n aiDisclosureLabel={aiDisclosureLabel}\n verified={verified}\n verifiedLabel={verifiedLabel}\n className=\"typography-body-small-14px-semibold\"\n >\n {user.nickname || user.displayName}\n </UserDisplayName>\n {showHandle && <UserHandle>{user.handle}</UserHandle>}\n </div>\n </div>\n );\n },\n);\n\nUserItem.displayName = \"UserItem\";\n"],"names":[],"mappings":";;;;;;;;;AAQA,MAAM,eAAe,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG;AAIrD,MAAM,uBAAuB,CAAC,SAC5B,aAAa;AAAA,EAAO,CAAC,MAAM,SACzB,KAAK,IAAI,OAAO,IAAI,IAAI,KAAK,IAAI,OAAO,IAAI,IAAI,OAAO;AACzD;AA4CK,MAAM,WAAW,MAAM;AAAA,EAC5B,CACE;AAAA,IACE;AAAA,IACA;AAAA,IACA,WAAW;AAAA,IACX,UAAU;AAAA,IACV;AAAA,IACA,aAAa;AAAA,IACb,aAAa;AAAA,IACb,eAAe;AAAA,IACf;AAAA,IACA,WAAW;AAAA,IACX;AAAA,IACA;AAAA,IACA,GAAG;AAAA,EAAA,GAEL,QACG;AACH,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,WAAW,GAAG,6DAA6D,SAAS;AAAA,QACnF,GAAG;AAAA,QAEH,UAAA;AAAA,UAAA,WACC,oBAAC,SAAI,WAAU,uEACb,8BAAC,aAAA,EAAY,MAAM,IAAI,EAAA,CACzB;AAAA,UAED,cACC;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,MAAM,qBAAqB,cAAc,EAAE;AAAA,cAC3C,KAAK,KAAK,WAAW;AAAA,cACrB,KAAK,KAAK;AAAA,cACV,UAAU,YAAY,KAAK,WAAW;AAAA,cACtC,iBAAiB,QAAQ,oBAAoB,QAAQ;AAAA,YAAA;AAAA,UAAA;AAAA,UAGzD,qBAAC,OAAA,EAAI,WAAU,+BACb,UAAA;AAAA,YAAA;AAAA,cAAC;AAAA,cAAA;AAAA,gBACC;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA,WAAU;AAAA,gBAET,UAAA,KAAK,YAAY,KAAK;AAAA,cAAA;AAAA,YAAA;AAAA,YAExB,cAAc,oBAAC,YAAA,EAAY,UAAA,KAAK,OAAA,CAAO;AAAA,UAAA,EAAA,CAC1C;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,EAGN;AACF;AAEA,SAAS,cAAc;"}
|
package/dist/index.d.ts
CHANGED
|
@@ -966,6 +966,47 @@ export declare type BulbIconProps = BaseIconProps;
|
|
|
966
966
|
*/
|
|
967
967
|
export declare const Button: React_2.ForwardRefExoticComponent<ButtonProps & React_2.RefAttributes<HTMLButtonElement>>;
|
|
968
968
|
|
|
969
|
+
/**
|
|
970
|
+
* A pre-composed cluster of {@link IconButton}s for toolbars and action rows
|
|
971
|
+
* (media controls, editing tools, content action bars). Items are spaced with a
|
|
972
|
+
* fixed 4px gap and stay individually rounded — this is a spaced group, not a
|
|
973
|
+
* segmented/joined control.
|
|
974
|
+
*
|
|
975
|
+
* Exposes `role="group"` by default; pass an `aria-label` or `aria-labelledby`
|
|
976
|
+
* to describe the cluster for assistive technology.
|
|
977
|
+
*
|
|
978
|
+
* This is a presentational layout primitive: it does not implement
|
|
979
|
+
* roving-tabindex / arrow-key focus management. `role` is overridable, but if
|
|
980
|
+
* you set an interactive-widget role such as `role="toolbar"` you own the
|
|
981
|
+
* keyboard behaviour that role implies. For orientation-aware roles the
|
|
982
|
+
* `aria-orientation` is synced to `orientation` automatically.
|
|
983
|
+
*
|
|
984
|
+
* @example
|
|
985
|
+
* ```tsx
|
|
986
|
+
* <ButtonIconGroup aria-label="Media controls">
|
|
987
|
+
* <IconButton variant="tertiary" size="48" icon={<PlayIcon />} aria-label="Play" />
|
|
988
|
+
* <IconButton variant="tertiary" size="48" icon={<ForwardIcon />} aria-label="Skip" />
|
|
989
|
+
* </ButtonIconGroup>
|
|
990
|
+
* ```
|
|
991
|
+
*/
|
|
992
|
+
export declare const ButtonIconGroup: React_2.ForwardRefExoticComponent<ButtonIconGroupProps & React_2.RefAttributes<HTMLDivElement>>;
|
|
993
|
+
|
|
994
|
+
/** Orientation of the icon button cluster. */
|
|
995
|
+
export declare type ButtonIconGroupOrientation = "horizontal" | "vertical";
|
|
996
|
+
|
|
997
|
+
export declare interface ButtonIconGroupProps extends React_2.HTMLAttributes<HTMLDivElement> {
|
|
998
|
+
/**
|
|
999
|
+
* Direction the icon buttons flow in. Use `vertical` for side rails and
|
|
1000
|
+
* stacked media controls. @default "horizontal"
|
|
1001
|
+
*/
|
|
1002
|
+
orientation?: ButtonIconGroupOrientation;
|
|
1003
|
+
/**
|
|
1004
|
+
* The icon buttons to cluster together. Compose {@link IconButton} elements
|
|
1005
|
+
* here; the group only handles layout and spacing.
|
|
1006
|
+
*/
|
|
1007
|
+
children: React_2.ReactNode;
|
|
1008
|
+
}
|
|
1009
|
+
|
|
969
1010
|
export declare interface ButtonProps extends React_2.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
970
1011
|
/** Visual style variant of the button. @default "primary" */
|
|
971
1012
|
variant?: ButtonVariant;
|
|
@@ -1850,7 +1891,7 @@ export declare type CreatorCoverSlot = string | React_2.ReactNode;
|
|
|
1850
1891
|
export declare const CreatorTile: React_2.ForwardRefExoticComponent<CreatorTileProps & React_2.RefAttributes<HTMLDivElement>>;
|
|
1851
1892
|
|
|
1852
1893
|
/** Width-to-height ratio preset for the tile. */
|
|
1853
|
-
export declare type CreatorTileAspectRatio = "tall" | "medium" | "short";
|
|
1894
|
+
export declare type CreatorTileAspectRatio = "tall" | "medium" | "short" | "banner";
|
|
1854
1895
|
|
|
1855
1896
|
export declare interface CreatorTileProps extends React_2.HTMLAttributes<HTMLDivElement> {
|
|
1856
1897
|
/** Decorative background media rendered behind the creator content. */
|
|
@@ -1869,9 +1910,15 @@ export declare interface CreatorTileProps extends React_2.HTMLAttributes<HTMLDiv
|
|
|
1869
1910
|
/**
|
|
1870
1911
|
* Width-to-height ratio preset.
|
|
1871
1912
|
*
|
|
1872
|
-
* - `tall` –
|
|
1873
|
-
* - `medium` –
|
|
1874
|
-
* - `short` –
|
|
1913
|
+
* - `tall` – 5:4, closest to square
|
|
1914
|
+
* - `medium` – 3:2 landscape
|
|
1915
|
+
* - `short` – 9:5 landscape
|
|
1916
|
+
* - `banner` – 5:2, the slimmest preset
|
|
1917
|
+
*
|
|
1918
|
+
* The profile row is a fixed ~74px tall, so `banner` needs at least 185px of
|
|
1919
|
+
* width before the ratio makes the tile shorter than its own row and the top
|
|
1920
|
+
* of the row is clipped. The taller presets reach that floor at widths too
|
|
1921
|
+
* narrow to be practical.
|
|
1875
1922
|
*
|
|
1876
1923
|
* @default "medium"
|
|
1877
1924
|
*/
|
|
@@ -6085,9 +6132,19 @@ export declare const UserCircleIcon: React_2.ForwardRefExoticComponent<React_2.S
|
|
|
6085
6132
|
* for the standard display-name scale.
|
|
6086
6133
|
*
|
|
6087
6134
|
* When both `ambassador` and `verified` are set, the ambassador badge takes
|
|
6088
|
-
* precedence.
|
|
6089
|
-
*
|
|
6090
|
-
*
|
|
6135
|
+
* precedence. `aiDisclosure` is independent of both and renders after whichever
|
|
6136
|
+
* status badge is shown, since it discloses how the account is operated rather
|
|
6137
|
+
* than ranking it.
|
|
6138
|
+
*
|
|
6139
|
+
* The verified and AI-disclosure badges inherit the name's colour, so setting a
|
|
6140
|
+
* colour on the root tints them with it. That keeps them legible on surfaces
|
|
6141
|
+
* that fix their own text colour — over a cover image, for example, where a
|
|
6142
|
+
* theme-dependent tint would turn black against white text in light mode.
|
|
6143
|
+
*
|
|
6144
|
+
* The ambassador tint is deliberately not inherited: it uses `text-success-content`
|
|
6145
|
+
* (not `text-icons-brand-green`) so the green darkens in light mode and lightens
|
|
6146
|
+
* in dark mode, matching {@link ProfileOnlineStatus}; `text-icons-brand-green` is
|
|
6147
|
+
* fixed across modes.
|
|
6091
6148
|
*
|
|
6092
6149
|
* @example
|
|
6093
6150
|
* ```tsx
|
|
@@ -6097,6 +6154,10 @@ export declare const UserCircleIcon: React_2.ForwardRefExoticComponent<React_2.S
|
|
|
6097
6154
|
export declare const UserDisplayName: React_2.ForwardRefExoticComponent<UserDisplayNameProps & React_2.RefAttributes<HTMLElement>>;
|
|
6098
6155
|
|
|
6099
6156
|
export declare interface UserDisplayNameProps extends React_2.HTMLAttributes<HTMLElement> {
|
|
6157
|
+
/** Render an AI-disclosure badge after the name, alongside any status badge. */
|
|
6158
|
+
aiDisclosure?: boolean;
|
|
6159
|
+
/** Accessible label for the AI-disclosure badge. @default "AI creator" */
|
|
6160
|
+
aiDisclosureLabel?: string;
|
|
6100
6161
|
/** Render an ambassador badge after the name. */
|
|
6101
6162
|
ambassador?: boolean;
|
|
6102
6163
|
/** Accessible label for the ambassador badge. @default "Ambassador" */
|
|
@@ -6144,7 +6205,7 @@ export declare type UserIconProps = BaseIconProps;
|
|
|
6144
6205
|
|
|
6145
6206
|
/**
|
|
6146
6207
|
* A compact user row showing an avatar, display name (or nickname) and handle,
|
|
6147
|
-
* with optional online and muted indicators.
|
|
6208
|
+
* with optional verified and AI-disclosure badges and online and muted indicators.
|
|
6148
6209
|
*
|
|
6149
6210
|
* @example
|
|
6150
6211
|
* ```tsx
|
|
@@ -6168,6 +6229,14 @@ export declare interface UserItemProps extends React_2.HTMLAttributes<HTMLDivEle
|
|
|
6168
6229
|
showHandle?: boolean;
|
|
6169
6230
|
/** Enable the avatar online indicator (combined with `isOnline`). */
|
|
6170
6231
|
showOnlineStatus?: boolean;
|
|
6232
|
+
/** Render an AI-disclosure badge after the display name. @default false */
|
|
6233
|
+
aiDisclosure?: boolean;
|
|
6234
|
+
/** Accessible label for the AI-disclosure badge. @default "AI creator" */
|
|
6235
|
+
aiDisclosureLabel?: string;
|
|
6236
|
+
/** Render a verified badge after the display name. @default false */
|
|
6237
|
+
verified?: boolean;
|
|
6238
|
+
/** Accessible label for the verified badge. @default "Verified" */
|
|
6239
|
+
verifiedLabel?: string;
|
|
6171
6240
|
}
|
|
6172
6241
|
|
|
6173
6242
|
/** Minimal user shape consumed by {@link UserItem}. */
|
package/dist/index.mjs
CHANGED
|
@@ -17,6 +17,7 @@ import { BottomNavigation } from "./components/BottomNavigation/BottomNavigation
|
|
|
17
17
|
import { BottomNavigationAction } from "./components/BottomNavigation/BottomNavigationAction.mjs";
|
|
18
18
|
import { Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator } from "./components/Breadcrumb/Breadcrumb.mjs";
|
|
19
19
|
import { Button } from "./components/Button/Button.mjs";
|
|
20
|
+
import { ButtonIconGroup } from "./components/ButtonIconGroup/ButtonIconGroup.mjs";
|
|
20
21
|
import { ButtonStack } from "./components/ButtonStack/ButtonStack.mjs";
|
|
21
22
|
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "./components/Card/Card.mjs";
|
|
22
23
|
import { ChatInput } from "./components/ChatInput/ChatInput.mjs";
|
|
@@ -301,6 +302,7 @@ export {
|
|
|
301
302
|
BreadcrumbSeparator,
|
|
302
303
|
BulbIcon,
|
|
303
304
|
Button,
|
|
305
|
+
ButtonIconGroup,
|
|
304
306
|
ButtonStack,
|
|
305
307
|
Calendar2Icon,
|
|
306
308
|
CalendarIcon,
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fanvue/ui",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.23.0",
|
|
4
4
|
"description": "React component library built with Tailwind CSS for Fanvue ecosystem",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"registry": "https://registry.npmjs.org",
|
|
@@ -142,7 +142,7 @@
|
|
|
142
142
|
"jsdom": "27.4.0",
|
|
143
143
|
"lint-staged": "16.2.7",
|
|
144
144
|
"playwright": "^1.58.1",
|
|
145
|
-
"postcss": "8.5.
|
|
145
|
+
"postcss": "8.5.20",
|
|
146
146
|
"react": "19.2.3",
|
|
147
147
|
"react-day-picker": "9.13.1",
|
|
148
148
|
"react-dom": "19.2.3",
|
|
@@ -166,9 +166,9 @@
|
|
|
166
166
|
"minimatch": ">=10.2.3",
|
|
167
167
|
"ajv": ">=8.18.0",
|
|
168
168
|
"fast-uri": ">=3.1.4",
|
|
169
|
-
"brace-expansion": ">=5.0.
|
|
169
|
+
"brace-expansion": ">=5.0.8",
|
|
170
170
|
"yaml": ">=2.8.3",
|
|
171
|
-
"postcss": ">=8.5.
|
|
171
|
+
"postcss": ">=8.5.18",
|
|
172
172
|
"tmp": ">=0.2.7",
|
|
173
173
|
"ws": ">=8.20.1",
|
|
174
174
|
"qs": ">=6.15.2",
|