@fanvue/ui 1.18.1 → 1.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/charts.d.ts +372 -0
- package/dist/charts.mjs +28 -0
- package/dist/charts.mjs.map +1 -0
- package/dist/cjs/charts.cjs +28 -0
- package/dist/cjs/charts.cjs.map +1 -0
- package/dist/cjs/components/Accordion/AccordionContent.cjs +11 -3
- package/dist/cjs/components/Accordion/AccordionContent.cjs.map +1 -1
- package/dist/cjs/components/Accordion/AccordionItem.cjs +1 -5
- package/dist/cjs/components/Accordion/AccordionItem.cjs.map +1 -1
- package/dist/cjs/components/Autocomplete/useAutocomplete.cjs.map +1 -1
- package/dist/cjs/components/BottomNavigation/BottomNavigation.cjs +5 -17
- package/dist/cjs/components/BottomNavigation/BottomNavigation.cjs.map +1 -1
- package/dist/cjs/components/BottomNavigation/BottomNavigationAction.cjs +6 -22
- package/dist/cjs/components/BottomNavigation/BottomNavigationAction.cjs.map +1 -1
- package/dist/cjs/components/Chart/ChartCard.cjs +88 -0
- package/dist/cjs/components/Chart/ChartCard.cjs.map +1 -0
- package/dist/cjs/components/Chart/ChartCenterLabel.cjs +51 -0
- package/dist/cjs/components/Chart/ChartCenterLabel.cjs.map +1 -0
- package/dist/cjs/components/Chart/ChartContainer.cjs +64 -0
- package/dist/cjs/components/Chart/ChartContainer.cjs.map +1 -0
- package/dist/cjs/components/Chart/ChartLegend.cjs +69 -0
- package/dist/cjs/components/Chart/ChartLegend.cjs.map +1 -0
- package/dist/cjs/components/Chart/ChartLoadingOverlay.cjs +35 -0
- package/dist/cjs/components/Chart/ChartLoadingOverlay.cjs.map +1 -0
- package/dist/cjs/components/Chart/ChartPieLegend.cjs +54 -0
- package/dist/cjs/components/Chart/ChartPieLegend.cjs.map +1 -0
- package/dist/cjs/components/Chart/ChartSeriesToggle.cjs +65 -0
- package/dist/cjs/components/Chart/ChartSeriesToggle.cjs.map +1 -0
- package/dist/cjs/components/Chart/ChartStyle.cjs +40 -0
- package/dist/cjs/components/Chart/ChartStyle.cjs.map +1 -0
- package/dist/cjs/components/Chart/ChartTooltip.cjs +147 -0
- package/dist/cjs/components/Chart/ChartTooltip.cjs.map +1 -0
- package/dist/cjs/components/Chart/chartUtils.cjs +23 -0
- package/dist/cjs/components/Chart/chartUtils.cjs.map +1 -0
- package/dist/cjs/components/Chart/useChart.cjs +32 -0
- package/dist/cjs/components/Chart/useChart.cjs.map +1 -0
- package/dist/components/Accordion/AccordionContent.mjs +11 -3
- package/dist/components/Accordion/AccordionContent.mjs.map +1 -1
- package/dist/components/Accordion/AccordionItem.mjs +1 -5
- package/dist/components/Accordion/AccordionItem.mjs.map +1 -1
- package/dist/components/Autocomplete/useAutocomplete.mjs.map +1 -1
- package/dist/components/BottomNavigation/BottomNavigation.mjs +5 -17
- package/dist/components/BottomNavigation/BottomNavigation.mjs.map +1 -1
- package/dist/components/BottomNavigation/BottomNavigationAction.mjs +6 -22
- package/dist/components/BottomNavigation/BottomNavigationAction.mjs.map +1 -1
- package/dist/components/Chart/ChartCard.mjs +71 -0
- package/dist/components/Chart/ChartCard.mjs.map +1 -0
- package/dist/components/Chart/ChartCenterLabel.mjs +34 -0
- package/dist/components/Chart/ChartCenterLabel.mjs.map +1 -0
- package/dist/components/Chart/ChartContainer.mjs +47 -0
- package/dist/components/Chart/ChartContainer.mjs.map +1 -0
- package/dist/components/Chart/ChartLegend.mjs +52 -0
- package/dist/components/Chart/ChartLegend.mjs.map +1 -0
- package/dist/components/Chart/ChartLoadingOverlay.mjs +18 -0
- package/dist/components/Chart/ChartLoadingOverlay.mjs.map +1 -0
- package/dist/components/Chart/ChartPieLegend.mjs +37 -0
- package/dist/components/Chart/ChartPieLegend.mjs.map +1 -0
- package/dist/components/Chart/ChartSeriesToggle.mjs +48 -0
- package/dist/components/Chart/ChartSeriesToggle.mjs.map +1 -0
- package/dist/components/Chart/ChartStyle.mjs +23 -0
- package/dist/components/Chart/ChartStyle.mjs.map +1 -0
- package/dist/components/Chart/ChartTooltip.mjs +130 -0
- package/dist/components/Chart/ChartTooltip.mjs.map +1 -0
- package/dist/components/Chart/chartUtils.mjs +23 -0
- package/dist/components/Chart/chartUtils.mjs.map +1 -0
- package/dist/components/Chart/useChart.mjs +15 -0
- package/dist/components/Chart/useChart.mjs.map +1 -0
- package/dist/index.d.ts +2 -6
- package/package.json +18 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChartStyle.cjs","sources":["../../../../src/components/Chart/ChartStyle.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { THEMES } from \"./chartUtils\";\nimport type { ChartConfig } from \"./types\";\n\n/** Props for the scoped CSS variable injector used by {@link ChartContainer}. */\nexport interface ChartStyleProps {\n /** Unique identifier scoped to the chart instance. */\n id: string;\n /** Chart configuration mapping data keys to colors and themes. */\n config: ChartConfig;\n}\n\n/**\n * Injects a scoped `<style>` tag that maps each config entry to a\n * `--color-{key}` CSS custom property, with light/dark theme support.\n *\n * Rendered automatically by {@link ChartContainer} — you rarely need this directly.\n */\nexport const ChartStyle = React.memo(function ChartStyle({ id, config }: ChartStyleProps) {\n const colorEntries = Object.entries(config).filter(([, entry]) => entry.theme || entry.color);\n\n if (colorEntries.length === 0) return null;\n\n const css = Object.entries(THEMES)\n .map(([theme, prefix]) => {\n const vars = colorEntries\n .map(([key, entry]) => {\n const color = entry.theme?.[theme as keyof typeof entry.theme] || entry.color;\n return color ? ` --color-${key}: ${color};` : null;\n })\n .filter(Boolean)\n .join(\"\\n\");\n return `${prefix} [data-chart=${id}] {\\n${vars}\\n}`;\n })\n .join(\"\\n\");\n\n return <style>{css}</style>;\n});\n\nChartStyle.displayName = \"ChartStyle\";\n"],"names":["React","ChartStyle","THEMES","jsx"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAkBO,MAAM,aAAaA,iBAAM,KAAK,SAASC,YAAW,EAAE,IAAI,UAA2B;AACxF,QAAM,eAAe,OAAO,QAAQ,MAAM,EAAE,OAAO,CAAC,CAAA,EAAG,KAAK,MAAM,MAAM,SAAS,MAAM,KAAK;AAE5F,MAAI,aAAa,WAAW,EAAG,QAAO;AAEtC,QAAM,MAAM,OAAO,QAAQC,WAAAA,MAAM,EAC9B,IAAI,CAAC,CAAC,OAAO,MAAM,MAAM;AACxB,UAAM,OAAO,aACV,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM;AACrB,YAAM,QAAQ,MAAM,QAAQ,KAAiC,KAAK,MAAM;AACxE,aAAO,QAAQ,aAAa,GAAG,KAAK,KAAK,MAAM;AAAA,IACjD,CAAC,EACA,OAAO,OAAO,EACd,KAAK,IAAI;AACZ,WAAO,GAAG,MAAM,gBAAgB,EAAE;AAAA,EAAQ,IAAI;AAAA;AAAA,EAChD,CAAC,EACA,KAAK,IAAI;AAEZ,SAAOC,2BAAAA,IAAC,WAAO,UAAA,IAAA,CAAI;AACrB,CAAC;AAED,WAAW,cAAc;;"}
|
|
@@ -0,0 +1,147 @@
|
|
|
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 recharts = require("recharts");
|
|
7
|
+
const cn = require("../../utils/cn.cjs");
|
|
8
|
+
const chartUtils = require("./chartUtils.cjs");
|
|
9
|
+
const useChart = require("./useChart.cjs");
|
|
10
|
+
function _interopNamespaceDefault(e) {
|
|
11
|
+
const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
|
|
12
|
+
if (e) {
|
|
13
|
+
for (const k in e) {
|
|
14
|
+
if (k !== "default") {
|
|
15
|
+
const d = Object.getOwnPropertyDescriptor(e, k);
|
|
16
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
17
|
+
enumerable: true,
|
|
18
|
+
get: () => e[k]
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
n.default = e;
|
|
24
|
+
return Object.freeze(n);
|
|
25
|
+
}
|
|
26
|
+
const React__namespace = /* @__PURE__ */ _interopNamespaceDefault(React);
|
|
27
|
+
const ChartTooltip = recharts.Tooltip;
|
|
28
|
+
function TooltipRow({
|
|
29
|
+
item,
|
|
30
|
+
itemConfig,
|
|
31
|
+
indicator,
|
|
32
|
+
indicatorColor,
|
|
33
|
+
hideIndicator,
|
|
34
|
+
nestLabel,
|
|
35
|
+
tooltipLabel
|
|
36
|
+
}) {
|
|
37
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
38
|
+
itemConfig?.icon ? /* @__PURE__ */ jsxRuntime.jsx(itemConfig.icon, {}) : !hideIndicator && /* @__PURE__ */ jsxRuntime.jsx(
|
|
39
|
+
"div",
|
|
40
|
+
{
|
|
41
|
+
className: cn.cn("shrink-0 rounded-[2px] border-(--color-border) bg-(--color-bg)", {
|
|
42
|
+
"h-2.5 w-2.5": indicator === "dot",
|
|
43
|
+
"w-1": indicator === "line",
|
|
44
|
+
"w-0 border-[1.5px] border-dashed bg-transparent": indicator === "dashed",
|
|
45
|
+
"my-0.5": nestLabel && indicator === "dashed"
|
|
46
|
+
}),
|
|
47
|
+
style: {
|
|
48
|
+
"--color-bg": indicatorColor,
|
|
49
|
+
"--color-border": indicatorColor
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
),
|
|
53
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
54
|
+
"div",
|
|
55
|
+
{
|
|
56
|
+
className: cn.cn(
|
|
57
|
+
"flex flex-1 justify-between leading-none",
|
|
58
|
+
nestLabel ? "items-end" : "items-center"
|
|
59
|
+
),
|
|
60
|
+
children: [
|
|
61
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-1.5", children: [
|
|
62
|
+
nestLabel ? tooltipLabel : null,
|
|
63
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-foreground-tertiary", children: itemConfig?.label || item.name })
|
|
64
|
+
] }),
|
|
65
|
+
item.value !== void 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium font-mono text-foreground-default tabular-nums", children: typeof item.value === "number" ? item.value.toLocaleString() : item.value })
|
|
66
|
+
]
|
|
67
|
+
}
|
|
68
|
+
)
|
|
69
|
+
] });
|
|
70
|
+
}
|
|
71
|
+
const ChartTooltipContent = React__namespace.forwardRef(
|
|
72
|
+
({
|
|
73
|
+
active,
|
|
74
|
+
payload,
|
|
75
|
+
className,
|
|
76
|
+
indicator = "dot",
|
|
77
|
+
hideLabel = false,
|
|
78
|
+
hideIndicator = false,
|
|
79
|
+
label,
|
|
80
|
+
labelFormatter,
|
|
81
|
+
labelClassName,
|
|
82
|
+
formatter,
|
|
83
|
+
color,
|
|
84
|
+
nameKey,
|
|
85
|
+
labelKey
|
|
86
|
+
}, ref) => {
|
|
87
|
+
const { config } = useChart.useChart();
|
|
88
|
+
const tooltipLabel = React__namespace.useMemo(() => {
|
|
89
|
+
if (hideLabel || !payload?.length) return null;
|
|
90
|
+
const [item] = payload;
|
|
91
|
+
const key = String(labelKey || item?.dataKey || item?.name || "value");
|
|
92
|
+
const itemConfig = chartUtils.resolveConfigEntry(config, item, key);
|
|
93
|
+
const value = !labelKey && typeof label === "string" ? config[label]?.label || label : itemConfig?.label;
|
|
94
|
+
if (labelFormatter && payload) {
|
|
95
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn.cn("font-medium", labelClassName), children: labelFormatter(value, payload) });
|
|
96
|
+
}
|
|
97
|
+
if (!value) return null;
|
|
98
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn.cn("font-medium", labelClassName), children: value });
|
|
99
|
+
}, [label, labelFormatter, payload, hideLabel, labelClassName, config, labelKey]);
|
|
100
|
+
if (!active || !payload?.length) return null;
|
|
101
|
+
const nestLabel = payload.length === 1 && indicator !== "dot";
|
|
102
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
103
|
+
"div",
|
|
104
|
+
{
|
|
105
|
+
ref,
|
|
106
|
+
className: cn.cn(
|
|
107
|
+
"grid min-w-32 items-start gap-1.5 rounded-lg border border-neutral-200 bg-surface-container px-2.5 py-1.5 text-xs shadow-float",
|
|
108
|
+
className
|
|
109
|
+
),
|
|
110
|
+
children: [
|
|
111
|
+
!nestLabel ? tooltipLabel : null,
|
|
112
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid gap-1.5", children: chartUtils.filterVisiblePayload(payload).map((item, index) => {
|
|
113
|
+
const key = String(nameKey || item.dataKey || item.name || "value");
|
|
114
|
+
const itemConfig = chartUtils.resolveConfigEntry(config, item, key);
|
|
115
|
+
const indicatorColor = color || item.payload?.fill || item.color;
|
|
116
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
117
|
+
"div",
|
|
118
|
+
{
|
|
119
|
+
className: cn.cn(
|
|
120
|
+
"flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-foreground-tertiary",
|
|
121
|
+
indicator === "dot" && "items-center"
|
|
122
|
+
),
|
|
123
|
+
children: formatter && item?.value !== void 0 && item.name ? formatter(item.value, item.name, item, index, payload) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
124
|
+
TooltipRow,
|
|
125
|
+
{
|
|
126
|
+
item,
|
|
127
|
+
itemConfig,
|
|
128
|
+
indicator,
|
|
129
|
+
indicatorColor,
|
|
130
|
+
hideIndicator,
|
|
131
|
+
nestLabel,
|
|
132
|
+
tooltipLabel
|
|
133
|
+
}
|
|
134
|
+
)
|
|
135
|
+
},
|
|
136
|
+
`${item.dataKey ?? item.name ?? index}`
|
|
137
|
+
);
|
|
138
|
+
}) })
|
|
139
|
+
]
|
|
140
|
+
}
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
);
|
|
144
|
+
ChartTooltipContent.displayName = "ChartTooltipContent";
|
|
145
|
+
exports.ChartTooltip = ChartTooltip;
|
|
146
|
+
exports.ChartTooltipContent = ChartTooltipContent;
|
|
147
|
+
//# sourceMappingURL=ChartTooltip.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChartTooltip.cjs","sources":["../../../../src/components/Chart/ChartTooltip.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { Tooltip as RechartsTooltip } from \"recharts\";\nimport type { NameType, Payload, ValueType } from \"recharts/types/component/DefaultTooltipContent\";\nimport { cn } from \"../../utils/cn\";\nimport { filterVisiblePayload, resolveConfigEntry } from \"./chartUtils\";\nimport type { ChartConfigEntry } from \"./types\";\nimport { useChart } from \"./useChart\";\n\n/** Re-export of Recharts `Tooltip` — use with `content={<ChartTooltipContent />}`. */\nexport const ChartTooltip = RechartsTooltip;\n\n/** Indicator shape rendered beside each tooltip row. */\nexport type ChartTooltipIndicator = \"dot\" | \"line\" | \"dashed\";\n\n/** Props for {@link ChartTooltipContent}. */\nexport interface ChartTooltipContentProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Whether the tooltip is currently active/visible. Passed by Recharts. */\n active?: boolean;\n /** Tooltip payload data. Passed by Recharts. */\n payload?: Payload<ValueType, NameType>[];\n /** Axis label. Passed by Recharts. */\n label?: string | number;\n /** Custom label formatter. */\n labelFormatter?: (\n label: string | number,\n payload: Payload<ValueType, NameType>[],\n ) => React.ReactNode;\n /** Custom value formatter. */\n formatter?: (\n value: ValueType,\n name: NameType,\n item: Payload<ValueType, NameType>,\n index: number,\n payload: Payload<ValueType, NameType>[],\n ) => React.ReactNode;\n /** CSS class for the label element. */\n labelClassName?: string;\n /** Hide the tooltip header label. @default false */\n hideLabel?: boolean;\n /** Hide the color indicator beside each row. @default false */\n hideIndicator?: boolean;\n /** Visual style of the color indicator. @default \"dot\" */\n indicator?: ChartTooltipIndicator;\n /**\n * Data key used to resolve the display name from config.\n * Useful when the payload `name` differs from the config key.\n */\n nameKey?: string;\n /**\n * Data key used to resolve the header label from config.\n * Falls back to the first payload item's `dataKey`.\n */\n labelKey?: string;\n /** Override indicator color for all rows. */\n color?: string;\n}\n\nfunction TooltipRow({\n item,\n itemConfig,\n indicator,\n indicatorColor,\n hideIndicator,\n nestLabel,\n tooltipLabel,\n}: {\n item: Payload<ValueType, NameType>;\n itemConfig: ChartConfigEntry | undefined;\n indicator: ChartTooltipIndicator;\n indicatorColor: unknown;\n hideIndicator: boolean;\n nestLabel: boolean;\n tooltipLabel: React.ReactNode;\n}) {\n return (\n <>\n {itemConfig?.icon ? (\n <itemConfig.icon />\n ) : (\n !hideIndicator && (\n <div\n className={cn(\"shrink-0 rounded-[2px] border-(--color-border) bg-(--color-bg)\", {\n \"h-2.5 w-2.5\": indicator === \"dot\",\n \"w-1\": indicator === \"line\",\n \"w-0 border-[1.5px] border-dashed bg-transparent\": indicator === \"dashed\",\n \"my-0.5\": nestLabel && indicator === \"dashed\",\n })}\n style={\n {\n \"--color-bg\": indicatorColor,\n \"--color-border\": indicatorColor,\n } as React.CSSProperties\n }\n />\n )\n )}\n <div\n className={cn(\n \"flex flex-1 justify-between leading-none\",\n nestLabel ? \"items-end\" : \"items-center\",\n )}\n >\n <div className=\"grid gap-1.5\">\n {nestLabel ? tooltipLabel : null}\n <span className=\"text-foreground-tertiary\">{itemConfig?.label || item.name}</span>\n </div>\n {item.value !== undefined && (\n <span className=\"font-medium font-mono text-foreground-default tabular-nums\">\n {typeof item.value === \"number\" ? item.value.toLocaleString() : item.value}\n </span>\n )}\n </div>\n </>\n );\n}\n\n/**\n * Styled tooltip content for use with `<ChartTooltip content={<ChartTooltipContent />} />`.\n *\n * Reads chart config from context to resolve labels, colors, and icons.\n * Supports dot/line/dashed indicators. Pass translated `label`s in config for i18n.\n *\n * @example\n * ```tsx\n * <ChartTooltip\n * content={<ChartTooltipContent indicator=\"line\" />}\n * />\n * ```\n */\nexport const ChartTooltipContent = React.forwardRef<HTMLDivElement, ChartTooltipContentProps>(\n (\n {\n active,\n payload,\n className,\n indicator = \"dot\",\n hideLabel = false,\n hideIndicator = false,\n label,\n labelFormatter,\n labelClassName,\n formatter,\n color,\n nameKey,\n labelKey,\n },\n ref,\n ) => {\n const { config } = useChart();\n\n const tooltipLabel = React.useMemo(() => {\n if (hideLabel || !payload?.length) return null;\n\n const [item] = payload;\n const key = String(labelKey || item?.dataKey || item?.name || \"value\");\n const itemConfig = resolveConfigEntry(config, item, key);\n const value =\n !labelKey && typeof label === \"string\"\n ? config[label as keyof typeof config]?.label || label\n : itemConfig?.label;\n\n if (labelFormatter && payload) {\n return (\n <div className={cn(\"font-medium\", labelClassName)}>\n {labelFormatter(value as string | number, payload)}\n </div>\n );\n }\n\n if (!value) return null;\n return <div className={cn(\"font-medium\", labelClassName)}>{value}</div>;\n }, [label, labelFormatter, payload, hideLabel, labelClassName, config, labelKey]);\n\n if (!active || !payload?.length) return null;\n\n const nestLabel = payload.length === 1 && indicator !== \"dot\";\n\n return (\n <div\n ref={ref}\n className={cn(\n \"grid min-w-32 items-start gap-1.5 rounded-lg border border-neutral-200 bg-surface-container px-2.5 py-1.5 text-xs shadow-float\",\n className,\n )}\n >\n {!nestLabel ? tooltipLabel : null}\n <div className=\"grid gap-1.5\">\n {filterVisiblePayload(payload).map((item, index) => {\n const key = String(nameKey || item.dataKey || item.name || \"value\");\n const itemConfig = resolveConfigEntry(config, item, key);\n const indicatorColor =\n color || (item.payload as Record<string, unknown>)?.fill || item.color;\n\n return (\n <div\n key={`${item.dataKey ?? item.name ?? index}`}\n className={cn(\n \"flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-foreground-tertiary\",\n indicator === \"dot\" && \"items-center\",\n )}\n >\n {formatter && item?.value !== undefined && item.name ? (\n formatter(item.value, item.name, item, index, payload)\n ) : (\n <TooltipRow\n item={item}\n itemConfig={itemConfig}\n indicator={indicator}\n indicatorColor={indicatorColor}\n hideIndicator={hideIndicator}\n nestLabel={nestLabel}\n tooltipLabel={tooltipLabel}\n />\n )}\n </div>\n );\n })}\n </div>\n </div>\n );\n },\n);\n\nChartTooltipContent.displayName = \"ChartTooltipContent\";\n"],"names":["RechartsTooltip","jsxs","Fragment","jsx","cn","React","useChart","resolveConfigEntry","filterVisiblePayload"],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
4
|
+
const THEMES = { light: "", dark: ".dark" };
|
|
5
|
+
function filterVisiblePayload(items) {
|
|
6
|
+
return items.filter((item) => item.type !== "none");
|
|
7
|
+
}
|
|
8
|
+
function resolveConfigEntry(config, payload, key) {
|
|
9
|
+
if (typeof payload !== "object" || payload === null) return void 0;
|
|
10
|
+
const p = payload;
|
|
11
|
+
const nested = typeof p.payload === "object" && p.payload !== null ? p.payload : void 0;
|
|
12
|
+
let configKey = key;
|
|
13
|
+
if (key in p && typeof p[key] === "string") {
|
|
14
|
+
configKey = p[key];
|
|
15
|
+
} else if (nested && key in nested && typeof nested[key] === "string") {
|
|
16
|
+
configKey = nested[key];
|
|
17
|
+
}
|
|
18
|
+
return config[configKey] ?? config[key];
|
|
19
|
+
}
|
|
20
|
+
exports.THEMES = THEMES;
|
|
21
|
+
exports.filterVisiblePayload = filterVisiblePayload;
|
|
22
|
+
exports.resolveConfigEntry = resolveConfigEntry;
|
|
23
|
+
//# sourceMappingURL=chartUtils.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chartUtils.cjs","sources":["../../../../src/components/Chart/chartUtils.ts"],"sourcesContent":["import type { ChartConfig, ChartThemeKey } from \"./types\";\n\nexport const THEMES: Record<ChartThemeKey, string> = { light: \"\", dark: \".dark\" };\n\nexport function filterVisiblePayload<T extends { type?: string }>(items: readonly T[]): T[] {\n return items.filter((item) => item.type !== \"none\");\n}\n\n/**\n * Resolves the {@link ChartConfig} entry for a given tooltip/legend payload\n * item. Recharts wraps the original data point inside `payload.payload` —\n * this function checks both levels before falling back to a direct `key` lookup.\n */\nexport function resolveConfigEntry(config: ChartConfig, payload: unknown, key: string) {\n if (typeof payload !== \"object\" || payload === null) return undefined;\n\n const p = payload as Record<string, unknown>;\n const nested =\n typeof p.payload === \"object\" && p.payload !== null\n ? (p.payload as Record<string, unknown>)\n : undefined;\n\n let configKey = key;\n\n if (key in p && typeof p[key] === \"string\") {\n configKey = p[key] as string;\n } else if (nested && key in nested && typeof nested[key] === \"string\") {\n configKey = nested[key] as string;\n }\n\n return config[configKey] ?? config[key];\n}\n"],"names":[],"mappings":";;;AAEO,MAAM,SAAwC,EAAE,OAAO,IAAI,MAAM,QAAA;AAEjE,SAAS,qBAAkD,OAA0B;AAC1F,SAAO,MAAM,OAAO,CAAC,SAAS,KAAK,SAAS,MAAM;AACpD;AAOO,SAAS,mBAAmB,QAAqB,SAAkB,KAAa;AACrF,MAAI,OAAO,YAAY,YAAY,YAAY,KAAM,QAAO;AAE5D,QAAM,IAAI;AACV,QAAM,SACJ,OAAO,EAAE,YAAY,YAAY,EAAE,YAAY,OAC1C,EAAE,UACH;AAEN,MAAI,YAAY;AAEhB,MAAI,OAAO,KAAK,OAAO,EAAE,GAAG,MAAM,UAAU;AAC1C,gBAAY,EAAE,GAAG;AAAA,EACnB,WAAW,UAAU,OAAO,UAAU,OAAO,OAAO,GAAG,MAAM,UAAU;AACrE,gBAAY,OAAO,GAAG;AAAA,EACxB;AAEA,SAAO,OAAO,SAAS,KAAK,OAAO,GAAG;AACxC;;;;"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
4
|
+
const React = require("react");
|
|
5
|
+
function _interopNamespaceDefault(e) {
|
|
6
|
+
const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
|
|
7
|
+
if (e) {
|
|
8
|
+
for (const k in e) {
|
|
9
|
+
if (k !== "default") {
|
|
10
|
+
const d = Object.getOwnPropertyDescriptor(e, k);
|
|
11
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
12
|
+
enumerable: true,
|
|
13
|
+
get: () => e[k]
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
n.default = e;
|
|
19
|
+
return Object.freeze(n);
|
|
20
|
+
}
|
|
21
|
+
const React__namespace = /* @__PURE__ */ _interopNamespaceDefault(React);
|
|
22
|
+
const ChartContext = React__namespace.createContext(null);
|
|
23
|
+
function useChart() {
|
|
24
|
+
const ctx = React__namespace.useContext(ChartContext);
|
|
25
|
+
if (!ctx) {
|
|
26
|
+
throw new Error("useChart must be used within a <ChartContainer />");
|
|
27
|
+
}
|
|
28
|
+
return ctx;
|
|
29
|
+
}
|
|
30
|
+
exports.ChartContext = ChartContext;
|
|
31
|
+
exports.useChart = useChart;
|
|
32
|
+
//# sourceMappingURL=useChart.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useChart.cjs","sources":["../../../../src/components/Chart/useChart.ts"],"sourcesContent":["import * as React from \"react\";\nimport type { ChartConfig } from \"./types\";\n\ntype ChartContextValue = {\n config: ChartConfig;\n};\n\nexport const ChartContext = React.createContext<ChartContextValue | null>(null);\n\n/** Access the nearest {@link ChartContainer}'s config. Throws if used outside a chart. */\nexport function useChart(): ChartContextValue {\n const ctx = React.useContext(ChartContext);\n if (!ctx) {\n throw new Error(\"useChart must be used within a <ChartContainer />\");\n }\n return ctx;\n}\n"],"names":["React"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAOO,MAAM,eAAeA,iBAAM,cAAwC,IAAI;AAGvE,SAAS,WAA8B;AAC5C,QAAM,MAAMA,iBAAM,WAAW,YAAY;AACzC,MAAI,CAAC,KAAK;AACR,UAAM,IAAI,MAAM,mDAAmD;AAAA,EACrE;AACA,SAAO;AACT;;;"}
|
|
@@ -9,14 +9,22 @@ const AccordionContent = React.forwardRef(({ className, children, noPadding, ...
|
|
|
9
9
|
ref,
|
|
10
10
|
className: cn(
|
|
11
11
|
"overflow-hidden",
|
|
12
|
-
"typography-regular-body-md text-foreground-secondary",
|
|
13
12
|
"motion-safe:data-[state=closed]:animate-accordion-collapse",
|
|
14
13
|
"motion-safe:data-[state=open]:animate-accordion-expand",
|
|
15
|
-
!noPadding && "px-3 pb-3",
|
|
16
14
|
className
|
|
17
15
|
),
|
|
18
16
|
...props,
|
|
19
|
-
children: /* @__PURE__ */ jsx(
|
|
17
|
+
children: /* @__PURE__ */ jsx(
|
|
18
|
+
"div",
|
|
19
|
+
{
|
|
20
|
+
className: cn(
|
|
21
|
+
"overflow-wrap-anywhere min-w-0",
|
|
22
|
+
"typography-regular-body-md text-foreground-secondary",
|
|
23
|
+
!noPadding && "px-3 pt-2 pb-3"
|
|
24
|
+
),
|
|
25
|
+
children
|
|
26
|
+
}
|
|
27
|
+
)
|
|
20
28
|
}
|
|
21
29
|
));
|
|
22
30
|
AccordionContent.displayName = "AccordionContent";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AccordionContent.mjs","sources":["../../../src/components/Accordion/AccordionContent.tsx"],"sourcesContent":["import * as AccordionPrimitive from \"@radix-ui/react-accordion\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\n/** Props for the {@link AccordionContent} panel component. */\nexport type AccordionContentProps = React.ComponentPropsWithoutRef<\n typeof AccordionPrimitive.Content\n> & {\n /** Remove the default inner padding (`px-3 pb-3`). Useful when you need custom content layout. */\n noPadding?: boolean;\n};\n\n/** Renders the collapsible content panel for an {@link AccordionItem}. Animates open and closed. */\nexport const AccordionContent = React.forwardRef<\n React.ComponentRef<typeof AccordionPrimitive.Content>,\n AccordionContentProps\n>(({ className, children, noPadding, ...props }, ref) => (\n <AccordionPrimitive.Content\n ref={ref}\n className={cn(\n \"overflow-hidden\",\n \"
|
|
1
|
+
{"version":3,"file":"AccordionContent.mjs","sources":["../../../src/components/Accordion/AccordionContent.tsx"],"sourcesContent":["import * as AccordionPrimitive from \"@radix-ui/react-accordion\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\n/** Props for the {@link AccordionContent} panel component. */\nexport type AccordionContentProps = React.ComponentPropsWithoutRef<\n typeof AccordionPrimitive.Content\n> & {\n /** Remove the default inner padding (`px-3 pb-3`). Useful when you need custom content layout. */\n noPadding?: boolean;\n};\n\n/** Renders the collapsible content panel for an {@link AccordionItem}. Animates open and closed. */\nexport const AccordionContent = React.forwardRef<\n React.ComponentRef<typeof AccordionPrimitive.Content>,\n AccordionContentProps\n>(({ className, children, noPadding, ...props }, ref) => (\n <AccordionPrimitive.Content\n ref={ref}\n className={cn(\n \"overflow-hidden\",\n \"motion-safe:data-[state=closed]:animate-accordion-collapse\",\n \"motion-safe:data-[state=open]:animate-accordion-expand\",\n className,\n )}\n {...props}\n >\n <div\n className={cn(\n \"overflow-wrap-anywhere min-w-0\",\n \"typography-regular-body-md text-foreground-secondary\",\n !noPadding && \"px-3 pt-2 pb-3\",\n )}\n >\n {children}\n </div>\n </AccordionPrimitive.Content>\n));\n\nAccordionContent.displayName = \"AccordionContent\";\n"],"names":[],"mappings":";;;;;AAaO,MAAM,mBAAmB,MAAM,WAGpC,CAAC,EAAE,WAAW,UAAU,WAAW,GAAG,SAAS,QAC/C;AAAA,EAAC,mBAAmB;AAAA,EAAnB;AAAA,IACC;AAAA,IACA,WAAW;AAAA,MACT;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,IAED,GAAG;AAAA,IAEJ,UAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,WAAW;AAAA,UACT;AAAA,UACA;AAAA,UACA,CAAC,aAAa;AAAA,QAAA;AAAA,QAGf;AAAA,MAAA;AAAA,IAAA;AAAA,EACH;AACF,CACD;AAED,iBAAiB,cAAc;"}
|
|
@@ -7,11 +7,7 @@ const AccordionItem = React.forwardRef(({ className, ...props }, ref) => /* @__P
|
|
|
7
7
|
AccordionPrimitive.Item,
|
|
8
8
|
{
|
|
9
9
|
ref,
|
|
10
|
-
className: cn(
|
|
11
|
-
"overflow-hidden rounded-xl bg-surface-container",
|
|
12
|
-
"border border-neutral-200",
|
|
13
|
-
className
|
|
14
|
-
),
|
|
10
|
+
className: cn("rounded-xl bg-surface-container", "border border-neutral-200", className),
|
|
15
11
|
...props
|
|
16
12
|
}
|
|
17
13
|
));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AccordionItem.mjs","sources":["../../../src/components/Accordion/AccordionItem.tsx"],"sourcesContent":["import * as AccordionPrimitive from \"@radix-ui/react-accordion\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\n/** Props for the {@link AccordionItem} component. */\nexport type AccordionItemProps = React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>;\n\n/** A single collapsible section within an {@link Accordion}. Contains an {@link AccordionTrigger} and {@link AccordionContent}. */\nexport const AccordionItem = React.forwardRef<\n React.ComponentRef<typeof AccordionPrimitive.Item>,\n AccordionItemProps\n>(({ className, ...props }, ref) => (\n <AccordionPrimitive.Item\n ref={ref}\n className={cn(\
|
|
1
|
+
{"version":3,"file":"AccordionItem.mjs","sources":["../../../src/components/Accordion/AccordionItem.tsx"],"sourcesContent":["import * as AccordionPrimitive from \"@radix-ui/react-accordion\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\n/** Props for the {@link AccordionItem} component. */\nexport type AccordionItemProps = React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>;\n\n/** A single collapsible section within an {@link Accordion}. Contains an {@link AccordionTrigger} and {@link AccordionContent}. */\nexport const AccordionItem = React.forwardRef<\n React.ComponentRef<typeof AccordionPrimitive.Item>,\n AccordionItemProps\n>(({ className, ...props }, ref) => (\n <AccordionPrimitive.Item\n ref={ref}\n className={cn(\"rounded-xl bg-surface-container\", \"border border-neutral-200\", className)}\n {...props}\n />\n));\n\nAccordionItem.displayName = \"AccordionItem\";\n"],"names":[],"mappings":";;;;;AAQO,MAAM,gBAAgB,MAAM,WAGjC,CAAC,EAAE,WAAW,GAAG,MAAA,GAAS,QAC1B;AAAA,EAAC,mBAAmB;AAAA,EAAnB;AAAA,IACC;AAAA,IACA,WAAW,GAAG,mCAAmC,6BAA6B,SAAS;AAAA,IACtF,GAAG;AAAA,EAAA;AACN,CACD;AAED,cAAc,cAAc;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAutocomplete.mjs","sources":["../../../src/components/Autocomplete/useAutocomplete.ts"],"sourcesContent":["import * as React from \"react\";\nimport type { AutocompleteOption, AutocompleteProps } from \"./Autocomplete\";\nimport { CREATE_PREFIX, defaultFilter, getLabel } from \"./constants\";\n\n// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Combobox hook managing interconnected controlled/uncontrolled state\nexport function useAutocomplete(props: AutocompleteProps) {\n const {\n id,\n options,\n disabled = false,\n filterFn,\n creatable = false,\n creatableLabel,\n onCreate,\n inputValue: inputValueProp,\n onInputChange,\n open: openProp,\n defaultOpen = false,\n onOpenChange,\n } = props;\n\n const generatedId = React.useId();\n const inputId = id ?? generatedId;\n const helperTextId = `${inputId}-helper`;\n const listboxId = `${inputId}-listbox`;\n\n const inputRef = React.useRef<HTMLInputElement>(null);\n const listRef = React.useRef<HTMLDivElement>(null);\n\n const isMulti = props.multiple === true;\n\n // --- single-select state ---\n const [internalValue, setInternalValue] = React.useState<string | null>(\n (!isMulti && props.defaultValue) || null,\n );\n const selectedValue: string | null = !isMulti\n ? props.value !== undefined\n ? props.value\n : internalValue\n : null;\n\n // --- multi-select state ---\n const [internalMultiValue, setInternalMultiValue] = React.useState<string[]>(\n isMulti && props.defaultValue ? props.defaultValue : [],\n );\n const selectedMultiValues: string[] = isMulti\n ? props.value !== undefined\n ? props.value\n : internalMultiValue\n : [];\n\n // --- input text ---\n const [internalInputValue, setInternalInputValue] = React.useState(\"\");\n const searchText = inputValueProp !== undefined ? inputValueProp : internalInputValue;\n\n // --- open state ---\n const [internalOpen, setInternalOpen] = React.useState(defaultOpen);\n const isOpen = openProp !== undefined ? openProp : internalOpen;\n\n const setOpen = React.useCallback(\n (next: boolean) => {\n if (openProp === undefined) setInternalOpen(next);\n onOpenChange?.(next);\n },\n [openProp, onOpenChange],\n );\n\n // --- active index ---\n const [activeIndex, setActiveIndex] = React.useState(-1);\n\n // --- filtering ---\n const filter = filterFn ?? defaultFilter;\n\n const filteredOptions = React.useMemo(() => {\n if (!searchText) return options;\n return options.filter((o) => filter(o, searchText));\n }, [options, searchText, filter]);\n\n const showCreate =\n creatable &&\n searchText.length > 0 &&\n !options.some((o) => (o.label ?? o.value).toLowerCase() === searchText.toLowerCase());\n\n const visibleOptions = React.useMemo(() => {\n if (!showCreate) return filteredOptions;\n const createOption: AutocompleteOption = {\n value: `${CREATE_PREFIX}${searchText}`,\n label: creatableLabel ? creatableLabel(searchText) : searchText,\n };\n return [...filteredOptions, createOption];\n }, [filteredOptions, showCreate, searchText, creatableLabel]);\n\n const prevOptionsLengthRef = React.useRef(visibleOptions.length);\n const prevSearchTextRef = React.useRef(searchText);\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: intentionally reset only when search text or option count actually changes\n React.useEffect(() => {\n if (\n searchText !== prevSearchTextRef.current ||\n visibleOptions.length !== prevOptionsLengthRef.current\n ) {\n setActiveIndex(-1);\n prevSearchTextRef.current = searchText;\n prevOptionsLengthRef.current = visibleOptions.length;\n }\n }, [searchText, visibleOptions.length]);\n\n // --- scroll active option into view ---\n React.useEffect(() => {\n if (activeIndex < 0 || !listRef.current) return;\n const el = listRef.current.querySelector(`[data-option-index=\"${activeIndex}\"]`);\n if (typeof el?.scrollIntoView === \"function\") {\n el.scrollIntoView({ block: \"nearest\" });\n }\n }, [activeIndex]);\n\n // --- helpers ---\n function clearInputText() {\n if (inputValueProp === undefined) setInternalInputValue(\"\");\n onInputChange?.(\"\");\n }\n\n function selectSingle(val: string | null) {\n if (!isMulti && props.value === undefined) setInternalValue(val);\n if (!isMulti) (props as { onChange?: (v: string | null) => void }).onChange?.(val);\n }\n\n function toggleMulti(val: string) {\n const next = selectedMultiValues.includes(val)\n ? selectedMultiValues.filter((v) => v !== val)\n : [...selectedMultiValues, val];\n if (isMulti && props.value === undefined) setInternalMultiValue(next);\n if (isMulti) (props as { onChange?: (v: string[]) => void }).onChange?.(next);\n }\n\n function handleSelect(option: AutocompleteOption) {\n if (option.value.startsWith(CREATE_PREFIX)) {\n const raw = option.value.slice(CREATE_PREFIX.length);\n onCreate?.(raw);\n if (isMulti) {\n toggleMulti(raw);\n } else {\n selectSingle(raw);\n setOpen(false);\n }\n clearInputText();\n return;\n }\n\n if (isMulti) {\n toggleMulti(option.value);\n clearInputText();\n } else {\n selectSingle(option.value);\n clearInputText();\n setOpen(false);\n }\n }\n\n function handleInputChange(e: React.ChangeEvent<HTMLInputElement>) {\n const val = e.target.value;\n if (inputValueProp === undefined) setInternalInputValue(val);\n onInputChange?.(val);\n if (!isOpen) setOpen(true);\n }\n\n function findNextEnabled(start: number, direction: 1 | -1): number | null {\n let idx = start;\n while (idx >= 0 && idx < visibleOptions.length && visibleOptions[idx]?.disabled) {\n idx += direction;\n }\n return idx >= 0 && idx < visibleOptions.length ? idx : null;\n }\n\n // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Flat switch is clearer than splitting into separate handler functions\n function handleKeyDown(e: React.KeyboardEvent) {\n if (disabled) return;\n\n switch (e.key) {\n case \"ArrowDown\": {\n e.preventDefault();\n if (!isOpen) {\n setOpen(true);\n return;\n }\n setActiveIndex((prev) => findNextEnabled(prev + 1, 1) ?? prev);\n break;\n }\n case \"ArrowUp\": {\n e.preventDefault();\n if (!isOpen) {\n setOpen(true);\n return;\n }\n setActiveIndex((prev) => findNextEnabled(prev - 1, -1) ?? prev);\n break;\n }\n case \"Enter\": {\n if (isOpen && activeIndex >= 0 && activeIndex < visibleOptions.length) {\n e.preventDefault();\n const opt = visibleOptions[activeIndex];\n if (opt && !opt.disabled) handleSelect(opt);\n }\n break;\n }\n case \"Escape\": {\n e.preventDefault();\n if (isOpen) setOpen(false);\n break;\n }\n case \"Backspace\": {\n if (isMulti && searchText === \"\" && selectedMultiValues.length > 0) {\n const lastVal = selectedMultiValues[selectedMultiValues.length - 1];\n if (lastVal !== undefined) toggleMulti(lastVal);\n }\n break;\n }\n case \"Home\": {\n if (isOpen) {\n e.preventDefault();\n const idx = findNextEnabled(0, 1);\n if (idx !== null) setActiveIndex(idx);\n }\n break;\n }\n case \"End\": {\n if (isOpen) {\n e.preventDefault();\n const idx = findNextEnabled(visibleOptions.length - 1, -1);\n if (idx !== null) setActiveIndex(idx);\n }\n break;\n }\n }\n }\n\n function handleClear(e: React.MouseEvent) {\n e.stopPropagation();\n if (isMulti) {\n if (props.value === undefined) setInternalMultiValue([]);\n if (isMulti) (props as { onChange?: (v: string[]) => void }).onChange?.([]);\n } else {\n selectSingle(null);\n }\n clearInputText();\n inputRef.current?.focus();\n }\n\n function handleBlur(e: React.FocusEvent<HTMLInputElement>) {\n const container = e.currentTarget.closest(\"[data-autocomplete-root]\");\n if (container && e.relatedTarget instanceof Node && container.contains(e.relatedTarget)) {\n return;\n }\n if (\n e.relatedTarget instanceof Node &&\n (e.relatedTarget as Element).closest?.(\"[data-radix-popper-content-wrapper]\")\n ) {\n return;\n }\n if (isOpen) setOpen(false);\n }\n\n function handleFocus() {\n if (!disabled && !isOpen) setOpen(true);\n }\n\n function handleContainerClick() {\n if (!disabled) {\n inputRef.current?.focus();\n if (!isOpen) setOpen(true);\n }\n }\n\n function handleOpenChange(next: boolean) {\n setOpen(next);\n if (!next) {\n clearInputText();\n setActiveIndex(-1);\n }\n }\n\n const selectedOption = React.useMemo(\n () => options.find((o) => o.value === selectedValue),\n [options, selectedValue],\n );\n\n const selectedMultiOptions = React.useMemo(\n () =>\n selectedMultiValues\n .map((v) => options.find((o) => o.value === v))\n .filter(Boolean) as AutocompleteOption[],\n [options, selectedMultiValues],\n );\n\n const hasClearableValue = isMulti ? selectedMultiValues.length > 0 : selectedValue != null;\n\n const displayInputValue = React.useMemo(() => {\n if (searchText) return searchText;\n if (isMulti || isOpen) return \"\";\n return selectedOption ? getLabel(selectedOption) : \"\";\n }, [searchText, isMulti, isOpen, selectedOption]);\n\n const activeDescendantId = activeIndex >= 0 ? `${listboxId}-option-${activeIndex}` : undefined;\n\n return {\n inputId,\n helperTextId,\n listboxId,\n inputRef,\n listRef,\n isMulti,\n isOpen,\n selectedValue,\n selectedMultiValues,\n selectedMultiOptions,\n searchText,\n visibleOptions,\n activeIndex,\n activeDescendantId,\n hasClearableValue,\n displayInputValue,\n setActiveIndex,\n handleSelect,\n handleInputChange,\n handleKeyDown,\n handleClear,\n handleBlur,\n handleFocus,\n handleContainerClick,\n handleOpenChange,\n toggleMulti,\n };\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useAutocomplete.mjs","sources":["../../../src/components/Autocomplete/useAutocomplete.ts"],"sourcesContent":["import * as React from \"react\";\nimport type { AutocompleteOption, AutocompleteProps } from \"./Autocomplete\";\nimport { CREATE_PREFIX, defaultFilter, getLabel } from \"./constants\";\n\n// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Combobox hook managing interconnected controlled/uncontrolled state\nexport function useAutocomplete(props: AutocompleteProps) {\n const {\n id,\n options,\n disabled = false,\n filterFn,\n creatable = false,\n creatableLabel,\n onCreate,\n inputValue: inputValueProp,\n onInputChange,\n open: openProp,\n defaultOpen = false,\n onOpenChange,\n } = props;\n\n const generatedId = React.useId();\n const inputId = id ?? generatedId;\n const helperTextId = `${inputId}-helper`;\n const listboxId = `${inputId}-listbox`;\n\n const inputRef = React.useRef<HTMLInputElement>(null);\n const listRef = React.useRef<HTMLDivElement>(null);\n\n const isMulti = props.multiple === true;\n\n // --- single-select state ---\n const [internalValue, setInternalValue] = React.useState<string | null>(\n (!isMulti && props.defaultValue) || null,\n );\n const selectedValue: string | null = !isMulti\n ? props.value !== undefined\n ? props.value\n : internalValue\n : null;\n\n // --- multi-select state ---\n const [internalMultiValue, setInternalMultiValue] = React.useState<string[]>(\n isMulti && props.defaultValue ? props.defaultValue : [],\n );\n const selectedMultiValues: string[] = isMulti\n ? props.value !== undefined\n ? props.value\n : internalMultiValue\n : [];\n\n // --- input text ---\n const [internalInputValue, setInternalInputValue] = React.useState(\"\");\n const searchText = inputValueProp !== undefined ? inputValueProp : internalInputValue;\n\n // --- open state ---\n const [internalOpen, setInternalOpen] = React.useState(defaultOpen);\n const isOpen = openProp !== undefined ? openProp : internalOpen;\n\n const setOpen = React.useCallback(\n (next: boolean) => {\n if (openProp === undefined) setInternalOpen(next);\n onOpenChange?.(next);\n },\n [openProp, onOpenChange],\n );\n\n // --- active index ---\n const [activeIndex, setActiveIndex] = React.useState(-1);\n\n // --- filtering ---\n const filter = filterFn ?? defaultFilter;\n\n const filteredOptions = React.useMemo(() => {\n if (!searchText) return options;\n return options.filter((o) => filter(o, searchText));\n }, [options, searchText, filter]);\n\n const showCreate =\n creatable &&\n searchText.length > 0 &&\n !options.some((o) => (o.label ?? o.value).toLowerCase() === searchText.toLowerCase());\n\n const visibleOptions = React.useMemo(() => {\n if (!showCreate) return filteredOptions;\n const createOption: AutocompleteOption = {\n value: `${CREATE_PREFIX}${searchText}`,\n label: creatableLabel ? creatableLabel(searchText) : searchText,\n };\n return [...filteredOptions, createOption];\n }, [filteredOptions, showCreate, searchText, creatableLabel]);\n\n const prevOptionsLengthRef = React.useRef(visibleOptions.length);\n const prevSearchTextRef = React.useRef(searchText);\n\n React.useEffect(() => {\n if (\n searchText !== prevSearchTextRef.current ||\n visibleOptions.length !== prevOptionsLengthRef.current\n ) {\n setActiveIndex(-1);\n prevSearchTextRef.current = searchText;\n prevOptionsLengthRef.current = visibleOptions.length;\n }\n }, [searchText, visibleOptions.length]);\n\n // --- scroll active option into view ---\n React.useEffect(() => {\n if (activeIndex < 0 || !listRef.current) return;\n const el = listRef.current.querySelector(`[data-option-index=\"${activeIndex}\"]`);\n if (typeof el?.scrollIntoView === \"function\") {\n el.scrollIntoView({ block: \"nearest\" });\n }\n }, [activeIndex]);\n\n // --- helpers ---\n function clearInputText() {\n if (inputValueProp === undefined) setInternalInputValue(\"\");\n onInputChange?.(\"\");\n }\n\n function selectSingle(val: string | null) {\n if (!isMulti && props.value === undefined) setInternalValue(val);\n if (!isMulti) (props as { onChange?: (v: string | null) => void }).onChange?.(val);\n }\n\n function toggleMulti(val: string) {\n const next = selectedMultiValues.includes(val)\n ? selectedMultiValues.filter((v) => v !== val)\n : [...selectedMultiValues, val];\n if (isMulti && props.value === undefined) setInternalMultiValue(next);\n if (isMulti) (props as { onChange?: (v: string[]) => void }).onChange?.(next);\n }\n\n function handleSelect(option: AutocompleteOption) {\n if (option.value.startsWith(CREATE_PREFIX)) {\n const raw = option.value.slice(CREATE_PREFIX.length);\n onCreate?.(raw);\n if (isMulti) {\n toggleMulti(raw);\n } else {\n selectSingle(raw);\n setOpen(false);\n }\n clearInputText();\n return;\n }\n\n if (isMulti) {\n toggleMulti(option.value);\n clearInputText();\n } else {\n selectSingle(option.value);\n clearInputText();\n setOpen(false);\n }\n }\n\n function handleInputChange(e: React.ChangeEvent<HTMLInputElement>) {\n const val = e.target.value;\n if (inputValueProp === undefined) setInternalInputValue(val);\n onInputChange?.(val);\n if (!isOpen) setOpen(true);\n }\n\n function findNextEnabled(start: number, direction: 1 | -1): number | null {\n let idx = start;\n while (idx >= 0 && idx < visibleOptions.length && visibleOptions[idx]?.disabled) {\n idx += direction;\n }\n return idx >= 0 && idx < visibleOptions.length ? idx : null;\n }\n\n // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Flat switch is clearer than splitting into separate handler functions\n function handleKeyDown(e: React.KeyboardEvent) {\n if (disabled) return;\n\n switch (e.key) {\n case \"ArrowDown\": {\n e.preventDefault();\n if (!isOpen) {\n setOpen(true);\n return;\n }\n setActiveIndex((prev) => findNextEnabled(prev + 1, 1) ?? prev);\n break;\n }\n case \"ArrowUp\": {\n e.preventDefault();\n if (!isOpen) {\n setOpen(true);\n return;\n }\n setActiveIndex((prev) => findNextEnabled(prev - 1, -1) ?? prev);\n break;\n }\n case \"Enter\": {\n if (isOpen && activeIndex >= 0 && activeIndex < visibleOptions.length) {\n e.preventDefault();\n const opt = visibleOptions[activeIndex];\n if (opt && !opt.disabled) handleSelect(opt);\n }\n break;\n }\n case \"Escape\": {\n e.preventDefault();\n if (isOpen) setOpen(false);\n break;\n }\n case \"Backspace\": {\n if (isMulti && searchText === \"\" && selectedMultiValues.length > 0) {\n const lastVal = selectedMultiValues[selectedMultiValues.length - 1];\n if (lastVal !== undefined) toggleMulti(lastVal);\n }\n break;\n }\n case \"Home\": {\n if (isOpen) {\n e.preventDefault();\n const idx = findNextEnabled(0, 1);\n if (idx !== null) setActiveIndex(idx);\n }\n break;\n }\n case \"End\": {\n if (isOpen) {\n e.preventDefault();\n const idx = findNextEnabled(visibleOptions.length - 1, -1);\n if (idx !== null) setActiveIndex(idx);\n }\n break;\n }\n }\n }\n\n function handleClear(e: React.MouseEvent) {\n e.stopPropagation();\n if (isMulti) {\n if (props.value === undefined) setInternalMultiValue([]);\n if (isMulti) (props as { onChange?: (v: string[]) => void }).onChange?.([]);\n } else {\n selectSingle(null);\n }\n clearInputText();\n inputRef.current?.focus();\n }\n\n function handleBlur(e: React.FocusEvent<HTMLInputElement>) {\n const container = e.currentTarget.closest(\"[data-autocomplete-root]\");\n if (container && e.relatedTarget instanceof Node && container.contains(e.relatedTarget)) {\n return;\n }\n if (\n e.relatedTarget instanceof Node &&\n (e.relatedTarget as Element).closest?.(\"[data-radix-popper-content-wrapper]\")\n ) {\n return;\n }\n if (isOpen) setOpen(false);\n }\n\n function handleFocus() {\n if (!disabled && !isOpen) setOpen(true);\n }\n\n function handleContainerClick() {\n if (!disabled) {\n inputRef.current?.focus();\n if (!isOpen) setOpen(true);\n }\n }\n\n function handleOpenChange(next: boolean) {\n setOpen(next);\n if (!next) {\n clearInputText();\n setActiveIndex(-1);\n }\n }\n\n const selectedOption = React.useMemo(\n () => options.find((o) => o.value === selectedValue),\n [options, selectedValue],\n );\n\n const selectedMultiOptions = React.useMemo(\n () =>\n selectedMultiValues\n .map((v) => options.find((o) => o.value === v))\n .filter(Boolean) as AutocompleteOption[],\n [options, selectedMultiValues],\n );\n\n const hasClearableValue = isMulti ? selectedMultiValues.length > 0 : selectedValue != null;\n\n const displayInputValue = React.useMemo(() => {\n if (searchText) return searchText;\n if (isMulti || isOpen) return \"\";\n return selectedOption ? getLabel(selectedOption) : \"\";\n }, [searchText, isMulti, isOpen, selectedOption]);\n\n const activeDescendantId = activeIndex >= 0 ? `${listboxId}-option-${activeIndex}` : undefined;\n\n return {\n inputId,\n helperTextId,\n listboxId,\n inputRef,\n listRef,\n isMulti,\n isOpen,\n selectedValue,\n selectedMultiValues,\n selectedMultiOptions,\n searchText,\n visibleOptions,\n activeIndex,\n activeDescendantId,\n hasClearableValue,\n displayInputValue,\n setActiveIndex,\n handleSelect,\n handleInputChange,\n handleKeyDown,\n handleClear,\n handleBlur,\n handleFocus,\n handleContainerClick,\n handleOpenChange,\n toggleMulti,\n };\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -2,27 +2,15 @@
|
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { cn } from "../../utils/cn.mjs";
|
|
5
|
-
const BottomNavigationContext = React.createContext({
|
|
6
|
-
showLabelsOnlyWhenActive: false,
|
|
7
|
-
hideLabels: false
|
|
8
|
-
});
|
|
5
|
+
const BottomNavigationContext = React.createContext({});
|
|
9
6
|
function useBottomNavigationContext() {
|
|
10
7
|
return React.useContext(BottomNavigationContext);
|
|
11
8
|
}
|
|
12
9
|
const BottomNavigation = React.forwardRef(
|
|
13
|
-
({
|
|
14
|
-
className,
|
|
15
|
-
children,
|
|
16
|
-
value,
|
|
17
|
-
onValueChange,
|
|
18
|
-
showLabelsOnlyWhenActive = false,
|
|
19
|
-
hideLabels = false,
|
|
20
|
-
hideOnDesktop = false,
|
|
21
|
-
...props
|
|
22
|
-
}, ref) => {
|
|
10
|
+
({ className, children, value, onValueChange, hideOnDesktop = false, ...props }, ref) => {
|
|
23
11
|
const contextValue = React.useMemo(
|
|
24
|
-
() => ({ value, onValueChange
|
|
25
|
-
[value, onValueChange
|
|
12
|
+
() => ({ value, onValueChange }),
|
|
13
|
+
[value, onValueChange]
|
|
26
14
|
);
|
|
27
15
|
return /* @__PURE__ */ jsx(BottomNavigationContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(
|
|
28
16
|
"nav",
|
|
@@ -31,7 +19,7 @@ const BottomNavigation = React.forwardRef(
|
|
|
31
19
|
className: cn(
|
|
32
20
|
"fixed inset-x-0 bottom-0",
|
|
33
21
|
"flex h-[calc(env(safe-area-inset-bottom,0px)+68px)] items-center justify-around",
|
|
34
|
-
"border-neutral-200 border-t bg-surface-page
|
|
22
|
+
"border-neutral-200 border-t bg-surface-page",
|
|
35
23
|
"pb-[env(safe-area-inset-bottom,0px)]",
|
|
36
24
|
hideOnDesktop && "md:hidden",
|
|
37
25
|
className
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BottomNavigation.mjs","sources":["../../../src/components/BottomNavigation/BottomNavigation.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\nexport interface BottomNavigationProps extends React.HTMLAttributes<HTMLElement> {\n /** The currently selected action value. */\n value?: string;\n /** Called when the selected action changes. */\n onValueChange?: (value: string) => void;\n /** When `true`,
|
|
1
|
+
{"version":3,"file":"BottomNavigation.mjs","sources":["../../../src/components/BottomNavigation/BottomNavigation.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\nexport interface BottomNavigationProps extends React.HTMLAttributes<HTMLElement> {\n /** The currently selected action value. */\n value?: string;\n /** Called when the selected action changes. */\n onValueChange?: (value: string) => void;\n /** When `true`, the navigation bar is hidden on viewports wider than `md` (768 px). @default false */\n hideOnDesktop?: boolean;\n}\n\ninterface BottomNavigationContextValue {\n value?: string;\n onValueChange?: (value: string) => void;\n}\n\nconst BottomNavigationContext = React.createContext<BottomNavigationContextValue>({});\n\nexport function useBottomNavigationContext(): BottomNavigationContextValue {\n return React.useContext(BottomNavigationContext);\n}\n\nexport const BottomNavigation = React.forwardRef<HTMLElement, BottomNavigationProps>(\n ({ className, children, value, onValueChange, hideOnDesktop = false, ...props }, ref) => {\n const contextValue = React.useMemo<BottomNavigationContextValue>(\n () => ({ value, onValueChange }),\n [value, onValueChange],\n );\n\n return (\n <BottomNavigationContext.Provider value={contextValue}>\n <nav\n ref={ref}\n className={cn(\n \"fixed inset-x-0 bottom-0\",\n \"flex h-[calc(env(safe-area-inset-bottom,0px)+68px)] items-center justify-around\",\n \"border-neutral-200 border-t bg-surface-page\",\n \"pb-[env(safe-area-inset-bottom,0px)]\",\n hideOnDesktop && \"md:hidden\",\n className,\n )}\n style={{ zIndex: \"var(--fanvue-ui-portal-z-index, 50)\", ...props.style }}\n {...props}\n >\n {children}\n </nav>\n </BottomNavigationContext.Provider>\n );\n },\n);\n\nBottomNavigation.displayName = \"BottomNavigation\";\n"],"names":[],"mappings":";;;;AAiBA,MAAM,0BAA0B,MAAM,cAA4C,EAAE;AAE7E,SAAS,6BAA2D;AACzE,SAAO,MAAM,WAAW,uBAAuB;AACjD;AAEO,MAAM,mBAAmB,MAAM;AAAA,EACpC,CAAC,EAAE,WAAW,UAAU,OAAO,eAAe,gBAAgB,OAAO,GAAG,MAAA,GAAS,QAAQ;AACvF,UAAM,eAAe,MAAM;AAAA,MACzB,OAAO,EAAE,OAAO;MAChB,CAAC,OAAO,aAAa;AAAA,IAAA;AAGvB,WACE,oBAAC,wBAAwB,UAAxB,EAAiC,OAAO,cACvC,UAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,WAAW;AAAA,UACT;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,iBAAiB;AAAA,UACjB;AAAA,QAAA;AAAA,QAEF,OAAO,EAAE,QAAQ,uCAAuC,GAAG,MAAM,MAAA;AAAA,QAChE,GAAG;AAAA,QAEH;AAAA,MAAA;AAAA,IAAA,GAEL;AAAA,EAEJ;AACF;AAEA,iBAAiB,cAAc;"}
|
|
@@ -5,14 +5,8 @@ import * as React from "react";
|
|
|
5
5
|
import { cn } from "../../utils/cn.mjs";
|
|
6
6
|
import { useBottomNavigationContext } from "./BottomNavigation.mjs";
|
|
7
7
|
const BottomNavigationAction = React.forwardRef(({ className, value, icon, label, badge, onClick, asChild = false, children, ...props }, ref) => {
|
|
8
|
-
const {
|
|
9
|
-
value: selectedValue,
|
|
10
|
-
onValueChange,
|
|
11
|
-
showLabelsOnlyWhenActive,
|
|
12
|
-
hideLabels
|
|
13
|
-
} = useBottomNavigationContext();
|
|
8
|
+
const { value: selectedValue, onValueChange } = useBottomNavigationContext();
|
|
14
9
|
const isActive = selectedValue === value;
|
|
15
|
-
const showLabel = !hideLabels && (!showLabelsOnlyWhenActive || isActive);
|
|
16
10
|
const handleClick = (e) => {
|
|
17
11
|
onValueChange?.(value);
|
|
18
12
|
onClick?.(e);
|
|
@@ -24,13 +18,13 @@ const BottomNavigationAction = React.forwardRef(({ className, value, icon, label
|
|
|
24
18
|
ref,
|
|
25
19
|
...!asChild && { type: "button" },
|
|
26
20
|
"aria-current": isActive ? "page" : void 0,
|
|
27
|
-
"aria-label":
|
|
21
|
+
"aria-label": label,
|
|
28
22
|
"data-state": isActive ? "active" : "inactive",
|
|
29
23
|
className: cn(
|
|
30
24
|
"relative flex min-w-0 flex-1 cursor-pointer flex-col items-center justify-center gap-0.5 overflow-hidden px-2 py-2",
|
|
25
|
+
"text-foreground-default",
|
|
31
26
|
"motion-safe:transition-colors motion-safe:duration-150 motion-safe:ease-in-out",
|
|
32
27
|
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-surface-page",
|
|
33
|
-
isActive ? "text-brand-accent-default" : "text-foreground-tertiary hover:text-foreground-secondary",
|
|
34
28
|
className
|
|
35
29
|
),
|
|
36
30
|
onClick: handleClick,
|
|
@@ -38,19 +32,9 @@ const BottomNavigationAction = React.forwardRef(({ className, value, icon, label
|
|
|
38
32
|
children: [
|
|
39
33
|
asChild && /* @__PURE__ */ jsx(Slottable, { children }),
|
|
40
34
|
/* @__PURE__ */ jsxs("span", { className: "relative inline-flex", children: [
|
|
41
|
-
/* @__PURE__ */ jsx("span", { className: "flex size-7", "aria-hidden": "true", children: icon }),
|
|
42
|
-
badge && /* @__PURE__ */ jsx("span", { className: "absolute -end-1 -top-
|
|
43
|
-
] })
|
|
44
|
-
showLabel && label && /* @__PURE__ */ jsx(
|
|
45
|
-
"span",
|
|
46
|
-
{
|
|
47
|
-
className: cn(
|
|
48
|
-
"typography-semibold-body-xs min-w-0 max-w-full truncate",
|
|
49
|
-
isActive ? "text-brand-accent-default" : "text-foreground-tertiary"
|
|
50
|
-
),
|
|
51
|
-
children: label
|
|
52
|
-
}
|
|
53
|
-
)
|
|
35
|
+
/* @__PURE__ */ jsx("span", { className: "flex items-center justify-center [&>svg]:size-7", "aria-hidden": "true", children: icon }),
|
|
36
|
+
badge && /* @__PURE__ */ jsx("span", { className: "absolute -end-1 -top-2.5", children: badge })
|
|
37
|
+
] })
|
|
54
38
|
]
|
|
55
39
|
}
|
|
56
40
|
);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BottomNavigationAction.mjs","sources":["../../../src/components/BottomNavigation/BottomNavigationAction.tsx"],"sourcesContent":["import { Slot, Slottable } from \"@radix-ui/react-slot\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { useBottomNavigationContext } from \"./BottomNavigation\";\n\nexport interface BottomNavigationActionProps\n extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, \"value\"> {\n /** Unique value that identifies this action. */\n value: string;\n /** Icon element displayed
|
|
1
|
+
{"version":3,"file":"BottomNavigationAction.mjs","sources":["../../../src/components/BottomNavigation/BottomNavigationAction.tsx"],"sourcesContent":["import { Slot, Slottable } from \"@radix-ui/react-slot\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { useBottomNavigationContext } from \"./BottomNavigation\";\n\nexport interface BottomNavigationActionProps\n extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, \"value\"> {\n /** Unique value that identifies this action. */\n value: string;\n /** Icon element displayed in the action. */\n icon: React.ReactElement;\n /** Accessible label applied as `aria-label`. */\n label?: string;\n /** Optional badge element (e.g. {@link Count}) rendered at the top-end corner of the icon. */\n badge?: React.ReactNode;\n /** Merge props onto a child element instead of rendering a `<button>`. @default false */\n asChild?: boolean;\n}\n\nexport const BottomNavigationAction = React.forwardRef<\n HTMLButtonElement,\n BottomNavigationActionProps\n>(({ className, value, icon, label, badge, onClick, asChild = false, children, ...props }, ref) => {\n const { value: selectedValue, onValueChange } = useBottomNavigationContext();\n\n const isActive = selectedValue === value;\n\n const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {\n onValueChange?.(value);\n onClick?.(e);\n };\n\n const Comp = asChild ? Slot : \"button\";\n\n return (\n <Comp\n ref={ref}\n {...(!asChild && { type: \"button\" as const })}\n aria-current={isActive ? \"page\" : undefined}\n aria-label={label}\n data-state={isActive ? \"active\" : \"inactive\"}\n className={cn(\n \"relative flex min-w-0 flex-1 cursor-pointer flex-col items-center justify-center gap-0.5 overflow-hidden px-2 py-2\",\n \"text-foreground-default\",\n \"motion-safe:transition-colors motion-safe:duration-150 motion-safe:ease-in-out\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-surface-page\",\n className,\n )}\n onClick={handleClick}\n {...props}\n >\n {asChild && <Slottable>{children}</Slottable>}\n <span className=\"relative inline-flex\">\n <span className=\"flex items-center justify-center [&>svg]:size-7\" aria-hidden=\"true\">\n {icon}\n </span>\n {badge && <span className=\"absolute -end-1 -top-2.5\">{badge}</span>}\n </span>\n </Comp>\n );\n});\n\nBottomNavigationAction.displayName = \"BottomNavigationAction\";\n"],"names":[],"mappings":";;;;;;AAmBO,MAAM,yBAAyB,MAAM,WAG1C,CAAC,EAAE,WAAW,OAAO,MAAM,OAAO,OAAO,SAAS,UAAU,OAAO,UAAU,GAAG,MAAA,GAAS,QAAQ;AACjG,QAAM,EAAE,OAAO,eAAe,cAAA,IAAkB,2BAAA;AAEhD,QAAM,WAAW,kBAAkB;AAEnC,QAAM,cAAc,CAAC,MAA2C;AAC9D,oBAAgB,KAAK;AACrB,cAAU,CAAC;AAAA,EACb;AAEA,QAAM,OAAO,UAAU,OAAO;AAE9B,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC;AAAA,MACC,GAAI,CAAC,WAAW,EAAE,MAAM,SAAA;AAAA,MACzB,gBAAc,WAAW,SAAS;AAAA,MAClC,cAAY;AAAA,MACZ,cAAY,WAAW,WAAW;AAAA,MAClC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MAAA;AAAA,MAEF,SAAS;AAAA,MACR,GAAG;AAAA,MAEH,UAAA;AAAA,QAAA,WAAW,oBAAC,aAAW,SAAA,CAAS;AAAA,QACjC,qBAAC,QAAA,EAAK,WAAU,wBACd,UAAA;AAAA,UAAA,oBAAC,QAAA,EAAK,WAAU,mDAAkD,eAAY,QAC3E,UAAA,MACH;AAAA,UACC,SAAS,oBAAC,QAAA,EAAK,WAAU,4BAA4B,UAAA,MAAA,CAAM;AAAA,QAAA,EAAA,CAC9D;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGN,CAAC;AAED,uBAAuB,cAAc;"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { cn } from "../../utils/cn.mjs";
|
|
5
|
+
import { Card } from "../Card/Card.mjs";
|
|
6
|
+
import { IconButton } from "../IconButton/IconButton.mjs";
|
|
7
|
+
import { InfoCircleIcon } from "../Icons/InfoCircleIcon.mjs";
|
|
8
|
+
import { Skeleton } from "../Skeleton/Skeleton.mjs";
|
|
9
|
+
import { TooltipProvider, Tooltip, TooltipTrigger, TooltipContent } from "../Tooltip/Tooltip.mjs";
|
|
10
|
+
const TREND_CLASSES = {
|
|
11
|
+
positive: "bg-success-background text-success-default",
|
|
12
|
+
negative: "bg-error-background text-error-default"
|
|
13
|
+
};
|
|
14
|
+
const ChartCard = React.forwardRef(
|
|
15
|
+
({
|
|
16
|
+
className,
|
|
17
|
+
title,
|
|
18
|
+
subtitle,
|
|
19
|
+
tooltip,
|
|
20
|
+
tooltipAriaLabel = "More info",
|
|
21
|
+
dateInfo,
|
|
22
|
+
trendChip,
|
|
23
|
+
loading = false,
|
|
24
|
+
children,
|
|
25
|
+
...props
|
|
26
|
+
}, ref) => {
|
|
27
|
+
return /* @__PURE__ */ jsx(Card, { ref, variant: "outlined", noPadding: true, className, ...props, children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 p-4", children: [
|
|
28
|
+
loading ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
29
|
+
/* @__PURE__ */ jsx(Skeleton, { animation: "wave", variant: "rounded", className: "h-4 w-32" }),
|
|
30
|
+
/* @__PURE__ */ jsx(Skeleton, { animation: "wave", variant: "rounded", className: "h-7 w-44" }),
|
|
31
|
+
/* @__PURE__ */ jsx(Skeleton, { animation: "wave", variant: "rounded", className: "h-3 w-24" })
|
|
32
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
33
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
34
|
+
/* @__PURE__ */ jsx("span", { className: "typography-semibold-body-md text-foreground-default", children: title }),
|
|
35
|
+
tooltip && /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
36
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
37
|
+
IconButton,
|
|
38
|
+
{
|
|
39
|
+
variant: "tertiary",
|
|
40
|
+
size: "24",
|
|
41
|
+
"aria-label": tooltipAriaLabel,
|
|
42
|
+
icon: /* @__PURE__ */ jsx(InfoCircleIcon, { className: "size-4 text-foreground-tertiary" })
|
|
43
|
+
}
|
|
44
|
+
) }),
|
|
45
|
+
/* @__PURE__ */ jsx(TooltipContent, { children: tooltip })
|
|
46
|
+
] }) })
|
|
47
|
+
] }),
|
|
48
|
+
subtitle && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
49
|
+
/* @__PURE__ */ jsx("span", { className: "typography-bold-heading-sm text-foreground-default", children: subtitle }),
|
|
50
|
+
trendChip && /* @__PURE__ */ jsx(
|
|
51
|
+
"span",
|
|
52
|
+
{
|
|
53
|
+
className: cn(
|
|
54
|
+
"typography-semibold-body-sm rounded-full px-2 py-0.5",
|
|
55
|
+
TREND_CLASSES[trendChip.trend]
|
|
56
|
+
),
|
|
57
|
+
children: trendChip.label
|
|
58
|
+
}
|
|
59
|
+
)
|
|
60
|
+
] }),
|
|
61
|
+
dateInfo && /* @__PURE__ */ jsx("span", { className: "typography-regular-body-sm text-foreground-tertiary", children: dateInfo })
|
|
62
|
+
] }),
|
|
63
|
+
/* @__PURE__ */ jsx("div", { className: "mt-auto", children })
|
|
64
|
+
] }) });
|
|
65
|
+
}
|
|
66
|
+
);
|
|
67
|
+
ChartCard.displayName = "ChartCard";
|
|
68
|
+
export {
|
|
69
|
+
ChartCard
|
|
70
|
+
};
|
|
71
|
+
//# sourceMappingURL=ChartCard.mjs.map
|
|
@@ -0,0 +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 } from \"../Card/Card\";\nimport { IconButton } from \"../IconButton/IconButton\";\nimport { InfoCircleIcon } from \"../Icons/InfoCircleIcon\";\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 /** 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 /** Trend indicator chip config. */\n trendChip?: {\n /** Display label (e.g. \"12.5%\"). */\n label: React.ReactNode;\n /** Whether the trend is positive (green) or negative (red). */\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: \"bg-success-background text-success-default\",\n negative: \"bg-error-background text-error-default\",\n};\n\n/**\n * Wraps any chart with a structured header containing title, subtitle,\n * optional trend chip, date range label, info tooltip, and a loading\n * skeleton state.\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 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 ref={ref} variant=\"outlined\" noPadding className={className} {...props}>\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.5\">\n <span className=\"typography-semibold-body-md text-foreground-default\">{title}</span>\n {tooltip && (\n <TooltipProvider>\n <Tooltip>\n <TooltipTrigger asChild>\n <IconButton\n variant=\"tertiary\"\n size=\"24\"\n aria-label={tooltipAriaLabel}\n icon={<InfoCircleIcon className=\"size-4 text-foreground-tertiary\" />}\n />\n </TooltipTrigger>\n <TooltipContent>{tooltip}</TooltipContent>\n </Tooltip>\n </TooltipProvider>\n )}\n </div>\n {subtitle && (\n <div className=\"flex items-center gap-2\">\n <span className=\"typography-bold-heading-sm text-foreground-default\">\n {subtitle}\n </span>\n {trendChip && (\n <span\n className={cn(\n \"typography-semibold-body-sm rounded-full px-2 py-0.5\",\n TREND_CLASSES[trendChip.trend],\n )}\n >\n {trendChip.label}\n </span>\n )}\n </div>\n )}\n {dateInfo && (\n <span className=\"typography-regular-body-sm text-foreground-tertiary\">\n {dateInfo}\n </span>\n )}\n </>\n )}\n <div className=\"mt-auto\">{children}</div>\n </div>\n </Card>\n );\n },\n);\nChartCard.displayName = \"ChartCard\";\n"],"names":[],"mappings":";;;;;;;;;AAiCA,MAAM,gBAAyD;AAAA,EAC7D,UAAU;AAAA,EACV,UAAU;AACZ;AAoBO,MAAM,YAAY,MAAM;AAAA,EAC7B,CACE;AAAA,IACE;AAAA,IACA;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,oBAAC,MAAA,EAAK,KAAU,SAAQ,YAAW,WAAS,MAAC,WAAuB,GAAG,OACrE,UAAA,qBAAC,OAAA,EAAI,WAAU,2BACZ,UAAA;AAAA,MAAA,UACC,qBAAA,UAAA,EACE,UAAA;AAAA,QAAA,oBAAC,YAAS,WAAU,QAAO,SAAQ,WAAU,WAAU,YAAW;AAAA,4BACjE,UAAA,EAAS,WAAU,QAAO,SAAQ,WAAU,WAAU,YAAW;AAAA,4BACjE,UAAA,EAAS,WAAU,QAAO,SAAQ,WAAU,WAAU,WAAA,CAAW;AAAA,MAAA,EAAA,CACpE,IAEA,qBAAA,UAAA,EACE,UAAA;AAAA,QAAA,qBAAC,OAAA,EAAI,WAAU,6BACb,UAAA;AAAA,UAAA,oBAAC,QAAA,EAAK,WAAU,uDAAuD,UAAA,OAAM;AAAA,UAC5E,WACC,oBAAC,iBAAA,EACC,UAAA,qBAAC,SAAA,EACC,UAAA;AAAA,YAAA,oBAAC,gBAAA,EAAe,SAAO,MACrB,UAAA;AAAA,cAAC;AAAA,cAAA;AAAA,gBACC,SAAQ;AAAA,gBACR,MAAK;AAAA,gBACL,cAAY;AAAA,gBACZ,MAAM,oBAAC,gBAAA,EAAe,WAAU,kCAAA,CAAkC;AAAA,cAAA;AAAA,YAAA,GAEtE;AAAA,YACA,oBAAC,kBAAgB,UAAA,QAAA,CAAQ;AAAA,UAAA,EAAA,CAC3B,EAAA,CACF;AAAA,QAAA,GAEJ;AAAA,QACC,YACC,qBAAC,OAAA,EAAI,WAAU,2BACb,UAAA;AAAA,UAAA,oBAAC,QAAA,EAAK,WAAU,sDACb,UAAA,UACH;AAAA,UACC,aACC;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,WAAW;AAAA,gBACT;AAAA,gBACA,cAAc,UAAU,KAAK;AAAA,cAAA;AAAA,cAG9B,UAAA,UAAU;AAAA,YAAA;AAAA,UAAA;AAAA,QACb,GAEJ;AAAA,QAED,YACC,oBAAC,QAAA,EAAK,WAAU,uDACb,UAAA,SAAA,CACH;AAAA,MAAA,GAEJ;AAAA,MAEF,oBAAC,OAAA,EAAI,WAAU,WAAW,SAAA,CAAS;AAAA,IAAA,EAAA,CACrC,EAAA,CACF;AAAA,EAEJ;AACF;AACA,UAAU,cAAc;"}
|