@srcroot/ui 0.0.39 → 0.0.40

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/index.js CHANGED
@@ -372,11 +372,12 @@ async function init(options) {
372
372
  await initializer.run();
373
373
  }
374
374
 
375
- // src/cli/commands/add.ts
375
+ // src/cli/services/component-adder.ts
376
376
  import fs4 from "fs-extra";
377
377
  import path4 from "path";
378
378
  import ora2 from "ora";
379
379
  import prompts2 from "prompts";
380
+ import { execa as execa2 } from "execa";
380
381
  import { fileURLToPath as fileURLToPath3 } from "url";
381
382
 
382
383
  // src/cli/registry.ts
@@ -755,121 +756,192 @@ var REGISTRY = {
755
756
  description: "AI chat interface",
756
757
  category: "Data Display",
757
758
  dependencies: ["button", "input", "scroll-area", "avatar"]
759
+ },
760
+ chart: {
761
+ file: "ui/chart.tsx",
762
+ description: "Charts using Recharts",
763
+ category: "Data Display",
764
+ dependencies: [],
765
+ registryDependencies: ["recharts"]
758
766
  }
759
767
  };
760
768
 
761
- // src/cli/commands/add.ts
769
+ // src/cli/services/component-adder.ts
762
770
  var __dirname4 = path4.dirname(fileURLToPath3(import.meta.url));
763
- async function add(components, options) {
764
- const cwd = path4.resolve(options.cwd);
765
- if (options.all) {
766
- components = Object.keys(REGISTRY);
771
+ var ComponentAdder = class {
772
+ cwd;
773
+ options;
774
+ constructor(cwd, options) {
775
+ this.cwd = cwd;
776
+ this.options = options;
767
777
  }
768
- if (components.length === 0) {
769
- const { items } = await prompts2({
770
- type: "multiselect",
771
- name: "items",
772
- message: "Which components would you like to add?",
773
- hint: "Space to select. A to toggle all. Enter to submit.",
774
- instructions: false,
775
- choices: Object.keys(REGISTRY).map((name) => ({
776
- title: name,
777
- value: name
778
- }))
779
- });
780
- if (!items || items.length === 0) {
781
- logger.warn("No components selected.");
782
- process.exit(0);
778
+ async add(components) {
779
+ components = await this.resolveComponents(components);
780
+ const { valid, invalid } = this.validateComponents(components);
781
+ if (invalid.length > 0) {
782
+ logger.error(`Unknown components: ${invalid.join(", ")}`);
783
+ console.log("\nRun '@srcroot/ui list' to see available components.");
784
+ process.exit(1);
783
785
  }
784
- components = items;
785
- }
786
- const validComponents = [];
787
- const invalidComponents = [];
788
- for (const comp of components) {
789
- if (comp in REGISTRY) {
790
- validComponents.push(comp);
791
- } else {
792
- invalidComponents.push(comp);
786
+ const { componentDeps, packageDeps } = this.resolveDependencies(valid);
787
+ this.logPlan(componentDeps, packageDeps);
788
+ if (packageDeps.size > 0) {
789
+ await this.installPackages(Array.from(packageDeps));
793
790
  }
791
+ await this.copyComponents(Array.from(componentDeps));
792
+ logger.success("\n\u2705 Components added successfully!\n");
794
793
  }
795
- if (invalidComponents.length > 0) {
796
- logger.error(`Unknown components: ${invalidComponents.join(", ")}`);
797
- console.log("\nRun '@srcroot/ui list' to see available components.");
798
- process.exit(1);
794
+ async resolveComponents(components) {
795
+ if (this.options.all) {
796
+ return Object.keys(REGISTRY);
797
+ }
798
+ if (components.length === 0) {
799
+ const { items } = await prompts2({
800
+ type: "multiselect",
801
+ name: "items",
802
+ message: "Which components would you like to add?",
803
+ hint: "Space to select. A to toggle all. Enter to submit.",
804
+ instructions: false,
805
+ choices: Object.keys(REGISTRY).map((name) => ({
806
+ title: name,
807
+ value: name
808
+ }))
809
+ });
810
+ if (!items || items.length === 0) {
811
+ logger.warn("No components selected.");
812
+ process.exit(0);
813
+ }
814
+ return items;
815
+ }
816
+ return components;
799
817
  }
800
- const toInstall = /* @__PURE__ */ new Set();
801
- function resolveDeps(name) {
802
- if (toInstall.has(name)) return;
803
- toInstall.add(name);
804
- const comp = REGISTRY[name];
805
- if (comp.dependencies) {
806
- for (const dep of comp.dependencies) {
807
- resolveDeps(dep);
818
+ validateComponents(components) {
819
+ const valid = [];
820
+ const invalid = [];
821
+ for (const comp of components) {
822
+ if (comp in REGISTRY) {
823
+ valid.push(comp);
824
+ } else {
825
+ invalid.push(comp);
808
826
  }
809
827
  }
828
+ return { valid, invalid };
810
829
  }
811
- for (const comp of validComponents) {
812
- resolveDeps(comp);
830
+ resolveDependencies(components) {
831
+ const componentDeps = /* @__PURE__ */ new Set();
832
+ const packageDeps = /* @__PURE__ */ new Set();
833
+ const resolve = (name) => {
834
+ if (componentDeps.has(name)) return;
835
+ componentDeps.add(name);
836
+ const comp = REGISTRY[name];
837
+ if (comp.dependencies) {
838
+ for (const dep of comp.dependencies) {
839
+ resolve(dep);
840
+ }
841
+ }
842
+ if (comp.registryDependencies) {
843
+ for (const pkg of comp.registryDependencies) {
844
+ packageDeps.add(pkg);
845
+ }
846
+ }
847
+ };
848
+ for (const comp of components) {
849
+ resolve(comp);
850
+ }
851
+ return { componentDeps, packageDeps };
813
852
  }
814
- const componentsToAdd = Array.from(toInstall);
815
- if (componentsToAdd.length > 10) {
816
- logger.info(`
853
+ logPlan(componentDeps, packageDeps) {
854
+ const componentsToAdd = Array.from(componentDeps);
855
+ const packagesToInstall = Array.from(packageDeps);
856
+ if (componentsToAdd.length > 10) {
857
+ logger.info(`
817
858
  \u{1F4E6} Adding ${componentsToAdd.length} components...
818
859
  `);
819
- } else {
820
- logger.info("\n\u{1F4E6} Adding components:\n");
821
- componentsToAdd.forEach((name) => {
822
- console.log(` - ${name}`);
823
- });
860
+ } else {
861
+ logger.info("\n\u{1F4E6} Adding components:\n");
862
+ componentsToAdd.forEach((name) => {
863
+ console.log(` - ${name}`);
864
+ });
865
+ }
866
+ if (packagesToInstall.length > 0) {
867
+ logger.info("\n\u{1F4E6} Installing dependencies:\n");
868
+ packagesToInstall.forEach((pkg) => {
869
+ console.log(` - ${pkg}`);
870
+ });
871
+ }
872
+ console.log();
824
873
  }
825
- console.log();
826
- const spinner = ora2("Adding components...").start();
827
- const hasSrc = fs4.existsSync(path4.join(cwd, "src"));
828
- const srcPath = hasSrc ? path4.join(cwd, "src") : cwd;
829
- const componentsDir = path4.join(srcPath, "components", "ui");
830
- try {
831
- await fs4.ensureDir(componentsDir);
832
- for (const name of componentsToAdd) {
833
- const comp = REGISTRY[name];
834
- const fileName = path4.basename(comp.file);
835
- const targetPath = path4.join(componentsDir, fileName);
836
- if (fs4.existsSync(targetPath) && !options.overwrite) {
837
- spinner.stop();
838
- const { overwrite } = await prompts2({
839
- type: "confirm",
840
- name: "overwrite",
841
- message: `${fileName} already exists. Overwrite?`,
842
- initial: false
843
- });
844
- if (!overwrite) {
845
- spinner.info(`Skipped ${fileName}`);
874
+ async installPackages(packages) {
875
+ const packageManager = getPackageManager(this.cwd);
876
+ const spinner = ora2("Installing dependencies...").start();
877
+ const installCmd = packageManager === "npm" ? "install" : "add";
878
+ try {
879
+ await execa2(packageManager, [installCmd, ...packages], {
880
+ cwd: this.cwd
881
+ });
882
+ spinner.succeed("Dependencies installed");
883
+ } catch (error) {
884
+ spinner.fail("Failed to install dependencies");
885
+ logger.error(error);
886
+ logger.warn(`
887
+ Please manually install: ${packages.join(" ")}`);
888
+ }
889
+ }
890
+ async copyComponents(components) {
891
+ const spinner = ora2("Adding components...").start();
892
+ const hasSrc = fs4.existsSync(path4.join(this.cwd, "src"));
893
+ const srcPath = hasSrc ? path4.join(this.cwd, "src") : this.cwd;
894
+ const componentsDir = path4.join(srcPath, "components", "ui");
895
+ try {
896
+ await fs4.ensureDir(componentsDir);
897
+ for (const name of components) {
898
+ const comp = REGISTRY[name];
899
+ const fileName = path4.basename(comp.file);
900
+ const targetPath = path4.join(componentsDir, fileName);
901
+ if (fs4.existsSync(targetPath) && !this.options.overwrite) {
902
+ spinner.stop();
903
+ const { overwrite } = await prompts2({
904
+ type: "confirm",
905
+ name: "overwrite",
906
+ message: `${fileName} already exists. Overwrite?`,
907
+ initial: false
908
+ });
909
+ if (!overwrite) {
910
+ spinner.info(`Skipped ${fileName}`);
911
+ spinner.start("Adding components...");
912
+ continue;
913
+ }
846
914
  spinner.start("Adding components...");
915
+ }
916
+ const registryPath = path4.resolve(__dirname4, "..", "..", "registry", comp.file);
917
+ if (!fs4.existsSync(registryPath)) {
918
+ spinner.warn(`Registry file not found for ${name}: ${registryPath}`);
847
919
  continue;
848
920
  }
849
- spinner.start("Adding components...");
850
- }
851
- const registryPath = path4.resolve(__dirname4, "..", "src", "registry", comp.file);
852
- if (!fs4.existsSync(registryPath)) {
853
- spinner.warn(`Registry file not found for ${name}: ${registryPath}`);
854
- continue;
921
+ const content = await fs4.readFile(registryPath, "utf-8");
922
+ await fs4.writeFile(targetPath, content);
923
+ if (components.length > 10) {
924
+ spinner.text = `Adding ${fileName}...`;
925
+ } else {
926
+ spinner.succeed(`Added ${fileName}`);
927
+ }
855
928
  }
856
- const content = await fs4.readFile(registryPath, "utf-8");
857
- await fs4.writeFile(targetPath, content);
858
- if (componentsToAdd.length > 10) {
859
- spinner.text = `Adding ${fileName}...`;
860
- } else {
861
- spinner.succeed(`Added ${fileName}`);
929
+ if (components.length > 10) {
930
+ spinner.succeed(`Added ${components.length} components`);
862
931
  }
932
+ } catch (error) {
933
+ spinner.fail("Failed to add components");
934
+ console.error(error);
935
+ process.exit(1);
863
936
  }
864
- if (componentsToAdd.length > 10) {
865
- spinner.succeed(`Added ${componentsToAdd.length} components`);
866
- }
867
- logger.success("\n\u2705 Components added successfully!\n");
868
- } catch (error) {
869
- spinner.fail("Failed to add components");
870
- console.error(error);
871
- process.exit(1);
872
937
  }
938
+ };
939
+
940
+ // src/cli/commands/add.ts
941
+ async function add(components, options) {
942
+ const cwd = process.cwd();
943
+ const adder = new ComponentAdder(cwd, options);
944
+ await adder.add(components);
873
945
  }
874
946
 
875
947
  // src/cli/commands/list.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@srcroot/ui",
3
- "version": "0.0.39",
3
+ "version": "0.0.40",
4
4
  "description": "A UI library with polymorphic, accessible React components",
5
5
  "type": "module",
6
6
  "bin": {
@@ -11,7 +11,7 @@ interface GoogleTagManagerProps {
11
11
  }
12
12
 
13
13
  const GoogleTagManager: FC<GoogleTagManagerProps> = ({ containers }) => {
14
- const defaultServer = 'https://www.googletagmanager.com/gtm.js';
14
+ const defaultServer = 'https://www.googletagmanager.com';
15
15
 
16
16
  // Group containers by tagServer to avoid duplicate script loads
17
17
  const scriptsMap = containers.reduce((map, container) => {
@@ -32,7 +32,7 @@ const GoogleTagManager: FC<GoogleTagManagerProps> = ({ containers }) => {
32
32
  __html: `
33
33
  ${ids
34
34
  .map(
35
- id => `(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],j=d.createElement(s);j.async=true;j.src="${server}?"+i;f.parentNode.insertBefore(j,f);})(window,document,'script','dataLayer','${id}');`
35
+ id => `(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],j=d.createElement(s);j.async=true;j.src="${server}/gtm.js?"+i;f.parentNode.insertBefore(j,f);})(window,document,'script','dataLayer','${id}');`
36
36
  )
37
37
  .join('')}
38
38
  `,
@@ -45,7 +45,7 @@ const GoogleTagManager: FC<GoogleTagManagerProps> = ({ containers }) => {
45
45
  {scriptElements}
46
46
 
47
47
  <noscript>
48
- {containers.map(({ gtmId, tagServer = defaultServer }) => (
48
+ {containers.map(({ gtmId, tagServerUrl = defaultServer }) => (
49
49
  <iframe
50
50
  key={gtmId}
51
51
  src={`${tagServer}/ns.html?id=${gtmId}`}
@@ -0,0 +1,371 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { ResponsiveContainer } from "recharts"
5
+
6
+ import { cn } from "@/lib/utils"
7
+
8
+ // Format: { THEME_NAME: CSS_VARIABLE }
9
+ const THEMES = { light: "", dark: ".dark" } as const
10
+
11
+ export type ChartConfig = {
12
+ [k in string]: {
13
+ label?: React.ReactNode
14
+ icon?: React.ComponentType
15
+ } & (
16
+ | { color?: string; theme?: never }
17
+ | { color?: never; theme: Record<keyof typeof THEMES, string> }
18
+ )
19
+ }
20
+
21
+ type ChartContextProps = {
22
+ config: ChartConfig
23
+ }
24
+
25
+ const ChartContext = React.createContext<ChartContextProps | null>(null)
26
+
27
+ function useChart() {
28
+ const context = React.useContext(ChartContext)
29
+
30
+ if (!context) {
31
+ throw new Error("useChart must be used within a <ChartContainer />")
32
+ }
33
+
34
+ return context
35
+ }
36
+
37
+ const ChartContainer = React.forwardRef<
38
+ HTMLDivElement,
39
+ React.ComponentProps<"div"> & {
40
+ config: ChartConfig
41
+ children: React.ComponentProps<typeof ResponsiveContainer>["children"]
42
+ }
43
+ >(({ id, className, children, config, ...props }, ref) => {
44
+ const uniqueId = React.useId()
45
+ const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`
46
+
47
+ return (
48
+ <ChartContext.Provider value={{ config }}>
49
+ <div
50
+ data-chart={chartId}
51
+ ref={ref}
52
+ className={cn(
53
+ "flex aspect-video justify-center text-xs [&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-none [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-sector]:outline-none [&_.recharts-surface]:outline-none",
54
+ className
55
+ )}
56
+ {...props}
57
+ >
58
+ <ChartStyle id={chartId} config={config} />
59
+ <ResponsiveContainer>
60
+ {children}
61
+ </ResponsiveContainer>
62
+ </div>
63
+ </ChartContext.Provider>
64
+ )
65
+ })
66
+ ChartContainer.displayName = "ChartContainer"
67
+
68
+ const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
69
+ const colorConfig = Object.entries(config).filter(
70
+ ([_, config]) => config.theme || config.color
71
+ )
72
+
73
+ if (!colorConfig.length) {
74
+ return null
75
+ }
76
+
77
+ return (
78
+ <style
79
+ dangerouslySetInnerHTML={{
80
+ __html: Object.entries(THEMES)
81
+ .map(
82
+ ([theme, prefix]) => `
83
+ ${prefix} [data-chart=${id}] {
84
+ ${colorConfig
85
+ .map(([key, itemConfig]) => {
86
+ const color =
87
+ itemConfig.theme?.[theme as keyof typeof itemConfig.theme] ||
88
+ itemConfig.color
89
+ return color ? ` --color-${key}: ${color};` : null
90
+ })
91
+ .join("\n")}
92
+ }
93
+ `
94
+ )
95
+ .join("\n"),
96
+ }}
97
+ />
98
+ )
99
+ }
100
+
101
+ const ChartTooltip = ChartTooltipPrimitive
102
+
103
+ const ChartTooltipContent = React.forwardRef<
104
+ HTMLDivElement,
105
+ React.ComponentProps<typeof ChartTooltipPrimitive> &
106
+ React.ComponentProps<"div"> & {
107
+ hideLabel?: boolean
108
+ hideIndicator?: boolean
109
+ indicator?: "line" | "dot" | "dashed"
110
+ nameKey?: string
111
+ labelKey?: string
112
+ }
113
+ >(
114
+ (
115
+ {
116
+ active,
117
+ payload,
118
+ className,
119
+ indicator = "dot",
120
+ hideLabel = false,
121
+ hideIndicator = false,
122
+ label,
123
+ labelFormatter,
124
+ labelClassName,
125
+ formatter,
126
+ color,
127
+ nameKey,
128
+ labelKey,
129
+ },
130
+ ref
131
+ ) => {
132
+ const { config } = useChart()
133
+
134
+ const tooltipLabel = React.useMemo(() => {
135
+ if (hideLabel || !payload?.length) {
136
+ return null
137
+ }
138
+
139
+ const [item] = payload
140
+ const key = `${labelKey || item.dataKey || item.name || "value"}`
141
+ const itemConfig = getPayloadConfigFromPayload(config, item, key)
142
+ const value =
143
+ !labelKey && typeof label === "string"
144
+ ? config[label as keyof typeof config]?.label || label
145
+ : itemConfig?.label
146
+
147
+ if (labelFormatter) {
148
+ return (
149
+ <div className={cn("font-medium", labelClassName)}>
150
+ {labelFormatter(value, payload)}
151
+ </div>
152
+ )
153
+ }
154
+
155
+ return value ? (
156
+ <div className={cn("font-medium", labelClassName)}>{value}</div>
157
+ ) : null
158
+ }, [
159
+ label,
160
+ labelFormatter,
161
+ payload,
162
+ hideLabel,
163
+ labelClassName,
164
+ config,
165
+ labelKey,
166
+ ])
167
+
168
+ if (!active || !payload?.length) {
169
+ return null
170
+ }
171
+
172
+ const nestLabel = payload.length === 1 && indicator !== "dot"
173
+
174
+ return (
175
+ <div
176
+ ref={ref}
177
+ className={cn(
178
+ "grid min-w-[8rem] items-start gap-1.5 rounded-lg border border-border/50 bg-background px-2.5 py-1.5 text-xs shadow-xl",
179
+ className
180
+ )}
181
+ >
182
+ {!nestLabel ? tooltipLabel : null}
183
+ <div className="grid gap-1.5">
184
+ {payload.map((item, index) => {
185
+ const key = `${nameKey || item.name || item.dataKey || "value"}`
186
+ const itemConfig = getPayloadConfigFromPayload(config, item, key)
187
+ const indicatorColor = color || item.payload.fill || item.color
188
+
189
+ return (
190
+ <div
191
+ key={item.dataKey}
192
+ className={cn(
193
+ "flex items-center gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground",
194
+ indicator === "dot" && "items-center"
195
+ )}
196
+ >
197
+ {formatter && item?.value !== undefined && item.name ? (
198
+ formatter(item.value, item.name, item, index, item.payload)
199
+ ) : (
200
+ <>
201
+ {itemConfig?.icon ? (
202
+ <itemConfig.icon />
203
+ ) : (
204
+ !hideIndicator && (
205
+ <div
206
+ className={cn(
207
+ "shrink-0 rounded-[2px] border-[--color-border] bg-[--color-bg]",
208
+ {
209
+ "h-2.5 w-2.5": indicator === "dot",
210
+ "w-1": indicator === "line",
211
+ "w-0 border-[1.5px] border-dashed bg-transparent":
212
+ indicator === "dashed",
213
+ "my-0.5": nestLabel && indicator === "dashed",
214
+ }
215
+ )}
216
+ style={
217
+ {
218
+ "--color-bg": indicatorColor,
219
+ "--color-border": indicatorColor,
220
+ } as React.CSSProperties
221
+ }
222
+ />
223
+ )
224
+ )}
225
+ <div
226
+ className={cn(
227
+ "flex flex-1 justify-between leading-none",
228
+ nestLabel ? "items-end" : "items-center"
229
+ )}
230
+ >
231
+ <div className="grid gap-1.5">
232
+ {nestLabel ? tooltipLabel : null}
233
+ <span className="text-muted-foreground">
234
+ {itemConfig?.label || item.name}
235
+ </span>
236
+ </div>
237
+ {item.value && (
238
+ <span className="font-mono font-medium text-foreground tabular-nums">
239
+ {item.value.toLocaleString()}
240
+ </span>
241
+ )}
242
+ </div>
243
+ </>
244
+ )}
245
+ </div>
246
+ )
247
+ })}
248
+ </div>
249
+ </div>
250
+ )
251
+ }
252
+ )
253
+ ChartTooltipContent.displayName = "ChartTooltip"
254
+
255
+ const ChartLegend = ChartLegendPrimitive
256
+
257
+ const ChartLegendContent = React.forwardRef<
258
+ HTMLDivElement,
259
+ React.ComponentProps<"div"> &
260
+ Pick<React.ComponentProps<typeof ChartLegendPrimitive>, "payload" | "verticalAlign"> & {
261
+ hideIcon?: boolean
262
+ nameKey?: string
263
+ }
264
+ >(
265
+ (
266
+ { className, hideIcon = false, payload, verticalAlign = "bottom", nameKey },
267
+ ref
268
+ ) => {
269
+ const { config } = useChart()
270
+
271
+ if (!payload?.length) {
272
+ return null
273
+ }
274
+
275
+ return (
276
+ <div
277
+ ref={ref}
278
+ className={cn(
279
+ "flex items-center justify-center gap-4",
280
+ verticalAlign === "top" ? "pb-3" : "pt-3",
281
+ className
282
+ )}
283
+ >
284
+ {payload.map((item) => {
285
+ const key = `${nameKey || item.dataKey || "value"}`
286
+ const itemConfig = getPayloadConfigFromPayload(config, item, key)
287
+
288
+ return (
289
+ <div
290
+ key={item.value}
291
+ className={cn(
292
+ "flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground"
293
+ )}
294
+ >
295
+ {itemConfig?.icon && !hideIcon ? (
296
+ <itemConfig.icon />
297
+ ) : (
298
+ <div
299
+ className="h-2 w-2 shrink-0 rounded-[2px]"
300
+ style={{
301
+ backgroundColor: item.color,
302
+ }}
303
+ />
304
+ )}
305
+ {itemConfig?.label}
306
+ </div>
307
+ )
308
+ })}
309
+ </div>
310
+ )
311
+ }
312
+ )
313
+ ChartLegendContent.displayName = "ChartLegend"
314
+
315
+ // Helper to extract item config from a payload.
316
+ function getPayloadConfigFromPayload(
317
+ config: ChartConfig,
318
+ payload: unknown,
319
+ key: string
320
+ ) {
321
+ if (typeof payload !== "object" || payload === null) {
322
+ return undefined
323
+ }
324
+
325
+ const payloadPayload =
326
+ "payload" in payload &&
327
+ typeof payload.payload === "object" &&
328
+ payload.payload !== null
329
+ ? payload.payload
330
+ : undefined
331
+
332
+ let configLabelKey: string = key
333
+
334
+ if (
335
+ key in payload &&
336
+ typeof payload[key as keyof typeof payload] === "string"
337
+ ) {
338
+ configLabelKey = payload[key as keyof typeof payload] as string
339
+ } else if (
340
+ payloadPayload &&
341
+ key in payloadPayload &&
342
+ typeof payloadPayload[key as keyof typeof payloadPayload] === "string"
343
+ ) {
344
+ configLabelKey = payloadPayload[
345
+ key as keyof typeof payloadPayload
346
+ ] as string
347
+ }
348
+
349
+ return configLabelKey in config
350
+ ? config[configLabelKey]
351
+ : config[key as keyof typeof config]
352
+ }
353
+
354
+ // Recharts Primitive Imports - We import specific components to avoid large bundle reference but for now let's just use * and assume tree shaking or specific imports in consuming app.
355
+ // However, the above code uses RechartsPrimitive.Tooltip and Legend.
356
+ // Ideally we should import them from recharts.
357
+
358
+ import {
359
+ Legend as ChartLegendPrimitive,
360
+ Tooltip as ChartTooltipPrimitive,
361
+ ResponsiveContainer,
362
+ } from "recharts"
363
+
364
+ export {
365
+ ChartContainer,
366
+ ChartTooltip,
367
+ ChartTooltipContent,
368
+ ChartLegend,
369
+ ChartLegendContent,
370
+ ChartStyle,
371
+ }
@@ -1,7 +1,7 @@
1
1
  "use client"
2
2
 
3
3
  import * as React from "react"
4
- import { PanelLeft } from "lucide-react"
4
+ import { LuPanelLeft } from "react-icons/lu";
5
5
  import { cva, type VariantProps } from "class-variance-authority"
6
6
 
7
7
  import { cn } from "@/lib/utils"
@@ -236,7 +236,7 @@ const SidebarTrigger = React.forwardRef<
236
236
  }}
237
237
  {...props}
238
238
  >
239
- <PanelLeft />
239
+ <LuPanelLeft />
240
240
  <span className="sr-only">Toggle Sidebar</span>
241
241
  </Button>
242
242
  )