@spteck/fluentui-react-charts 0.1.8

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.
Files changed (127) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +462 -0
  3. package/dist/charts/BarChart/BarChart.d.ts +16 -0
  4. package/dist/charts/BarChart/index.d.ts +1 -0
  5. package/dist/charts/ComboChart/ComboChart.d.ts +16 -0
  6. package/dist/charts/ComboChart/index.d.ts +1 -0
  7. package/dist/charts/Doughnut/DoughnutChart.d.ts +14 -0
  8. package/dist/charts/Doughnut/index.d.ts +1 -0
  9. package/dist/charts/PieChart/PieChart.d.ts +14 -0
  10. package/dist/charts/PieChart/index.d.ts +1 -0
  11. package/dist/charts/areaChart/AreaChart.d.ts +15 -0
  12. package/dist/charts/areaChart/index.d.ts +1 -0
  13. package/dist/charts/barHorizontalChart/BarHotizontalChart.d.ts +15 -0
  14. package/dist/charts/barHorizontalChart/index.d.ts +1 -0
  15. package/dist/charts/bubbleChart/BubbleChart.d.ts +15 -0
  16. package/dist/charts/bubbleChart/index.d.ts +1 -0
  17. package/dist/charts/floatBarChart/FloatBarChart.d.ts +14 -0
  18. package/dist/charts/floatBarChart/index.d.ts +1 -0
  19. package/dist/charts/lineChart/LineChart.d.ts +14 -0
  20. package/dist/charts/lineChart/index.d.ts +1 -0
  21. package/dist/charts/polarChart/PolarChart.d.ts +14 -0
  22. package/dist/charts/polarChart/index.d.ts +1 -0
  23. package/dist/charts/radarChart/RadarChart.d.ts +14 -0
  24. package/dist/charts/radarChart/index.d.ts +1 -0
  25. package/dist/charts/scatterChart/ScatterChart.d.ts +14 -0
  26. package/dist/charts/scatterChart/index.d.ts +1 -0
  27. package/dist/charts/stackedLineChart/StackedLineChart.d.ts +14 -0
  28. package/dist/charts/stackedLineChart/index.d.ts +1 -0
  29. package/dist/charts/steamChart/SteamChart.d.ts +14 -0
  30. package/dist/charts/steamChart/index.d.ts +1 -0
  31. package/dist/components/DashBoard.d.ts +3 -0
  32. package/dist/components/RenderLegend/RenderLegend.d.ts +11 -0
  33. package/dist/components/RenderTooltip/RenderTooltip.d.ts +14 -0
  34. package/dist/components/buttonMenu/ButtonMenu.d.ts +3 -0
  35. package/dist/components/buttonMenu/IButtonMenuOption.d.ts +10 -0
  36. package/dist/components/buttonMenu/IButtonMenuProps.d.ts +37 -0
  37. package/dist/components/index.d.ts +15 -0
  38. package/dist/components/legendContainer/LegendContainer.d.ts +16 -0
  39. package/dist/components/legendeButton/LegendButton.d.ts +11 -0
  40. package/dist/components/renderSliceLegend/RenderSliceLegend.d.ts +9 -0
  41. package/dist/components/renderValueLegend/RenderValueLegend.d.ts +13 -0
  42. package/dist/components/stack/IStackProps.d.ts +76 -0
  43. package/dist/components/stack/Stack.d.ts +8 -0
  44. package/dist/components/themeProvider/ThemeProvider.d.ts +15 -0
  45. package/dist/constants/Constants.d.ts +1 -0
  46. package/dist/fluentui-react-charts.cjs.development.js +2916 -0
  47. package/dist/fluentui-react-charts.cjs.development.js.map +1 -0
  48. package/dist/fluentui-react-charts.cjs.production.min.js +2 -0
  49. package/dist/fluentui-react-charts.cjs.production.min.js.map +1 -0
  50. package/dist/fluentui-react-charts.esm.js +2905 -0
  51. package/dist/fluentui-react-charts.esm.js.map +1 -0
  52. package/dist/graphGlobalStyles/useGraphGlobalStyles.d.ts +5 -0
  53. package/dist/hooks/index.d.ts +1 -0
  54. package/dist/hooks/useGraphUtils.d.ts +38 -0
  55. package/dist/hooks/useResponsiveLegend.d.ts +8 -0
  56. package/dist/index.d.ts +3 -0
  57. package/dist/index.js +8 -0
  58. package/dist/models/IChart.d.ts +25 -0
  59. package/dist/models/index.d.ts +1 -0
  60. package/package.json +66 -0
  61. package/src/assets/sample1.png +0 -0
  62. package/src/assets/sample2.png +0 -0
  63. package/src/assets/sample3.png +0 -0
  64. package/src/charts/BarChart/BarChart.tsx +227 -0
  65. package/src/charts/BarChart/README.MD +335 -0
  66. package/src/charts/BarChart/index.ts +1 -0
  67. package/src/charts/ComboChart/ComboChart.tsx +209 -0
  68. package/src/charts/ComboChart/README.MD +347 -0
  69. package/src/charts/ComboChart/index.ts +1 -0
  70. package/src/charts/Doughnut/DoughnutChart.tsx +152 -0
  71. package/src/charts/Doughnut/README.MD +296 -0
  72. package/src/charts/Doughnut/index.ts +1 -0
  73. package/src/charts/PieChart/PieChart.tsx +148 -0
  74. package/src/charts/PieChart/README.MD +315 -0
  75. package/src/charts/PieChart/index.ts +1 -0
  76. package/src/charts/areaChart/AreaChart.tsx +195 -0
  77. package/src/charts/areaChart/README.MD +236 -0
  78. package/src/charts/areaChart/index.ts +1 -0
  79. package/src/charts/barHorizontalChart/BarHotizontalChart.tsx +200 -0
  80. package/src/charts/barHorizontalChart/README.MD +278 -0
  81. package/src/charts/barHorizontalChart/index.ts +2 -0
  82. package/src/charts/bubbleChart/BubbleChart.tsx +184 -0
  83. package/src/charts/bubbleChart/README.MD +275 -0
  84. package/src/charts/bubbleChart/index.ts +1 -0
  85. package/src/charts/floatBarChart/FloatBarChart.tsx +178 -0
  86. package/src/charts/floatBarChart/README.MD +354 -0
  87. package/src/charts/floatBarChart/index.ts +1 -0
  88. package/src/charts/lineChart/LineChart.tsx +200 -0
  89. package/src/charts/lineChart/README.MD +354 -0
  90. package/src/charts/lineChart/index.ts +1 -0
  91. package/src/charts/polarChart/PolarChart.tsx +161 -0
  92. package/src/charts/polarChart/README.MD +336 -0
  93. package/src/charts/polarChart/index.ts +1 -0
  94. package/src/charts/radarChart/README.MD +388 -0
  95. package/src/charts/radarChart/RadarChart.tsx +173 -0
  96. package/src/charts/radarChart/index.ts +1 -0
  97. package/src/charts/scatterChart/README.MD +335 -0
  98. package/src/charts/scatterChart/ScatterChart.tsx +155 -0
  99. package/src/charts/scatterChart/index.ts +1 -0
  100. package/src/charts/stackedLineChart/README.MD +396 -0
  101. package/src/charts/stackedLineChart/StackedLineChart.tsx +188 -0
  102. package/src/charts/stackedLineChart/index.ts +1 -0
  103. package/src/charts/steamChart/README.MD +414 -0
  104. package/src/charts/steamChart/SteamChart.tsx +236 -0
  105. package/src/charts/steamChart/index.ts +1 -0
  106. package/src/components/DashBoard.tsx +409 -0
  107. package/src/components/RenderLegend/RenderLegend.tsx +40 -0
  108. package/src/components/RenderTooltip/RenderTooltip.tsx +111 -0
  109. package/src/components/buttonMenu/ButtonMenu.tsx +186 -0
  110. package/src/components/buttonMenu/IButtonMenuOption.ts +9 -0
  111. package/src/components/buttonMenu/IButtonMenuProps.tsx +40 -0
  112. package/src/components/index.ts +15 -0
  113. package/src/components/legendContainer/LegendContainer.tsx +118 -0
  114. package/src/components/legendeButton/LegendButton.tsx +57 -0
  115. package/src/components/renderSliceLegend/RenderSliceLegend.tsx +46 -0
  116. package/src/components/renderValueLegend/RenderValueLegend.tsx +43 -0
  117. package/src/components/stack/IStackProps.tsx +94 -0
  118. package/src/components/stack/Stack.tsx +103 -0
  119. package/src/components/themeProvider/ThemeProvider.tsx +48 -0
  120. package/src/constants/Constants.tsx +23 -0
  121. package/src/graphGlobalStyles/useGraphGlobalStyles.ts +28 -0
  122. package/src/hooks/index.ts +1 -0
  123. package/src/hooks/useGraphUtils.tsx +314 -0
  124. package/src/hooks/useResponsiveLegend.ts +35 -0
  125. package/src/index.tsx +4 -0
  126. package/src/models/IChart.ts +50 -0
  127. package/src/models/index.ts +1 -0
@@ -0,0 +1,103 @@
1
+ import { mergeClasses, tokens } from "@fluentui/react-components";
2
+
3
+ import { IStackProps } from "./IStackProps";
4
+ import React from "react";
5
+ import { css } from "@emotion/css";
6
+
7
+ /**
8
+ * Mapping of predefined sizes to Fluent UI tokens.
9
+ */
10
+ const sizeMap: Record<string, string> = {
11
+ xs: tokens.spacingHorizontalXS,
12
+ s: tokens.spacingHorizontalS,
13
+ m: tokens.spacingHorizontalM,
14
+ l: tokens.spacingHorizontalL,
15
+ xl: tokens.spacingHorizontalXL,
16
+ xxl: tokens.spacingHorizontalXXL,
17
+ };
18
+
19
+ /**
20
+ * Stack component provides a flexible layout using Flexbox.
21
+ * It allows stacking child components either horizontally or vertically with predefined spacing options.
22
+ */
23
+ export const Stack: React.FC<IStackProps> = React.memo(
24
+ ({
25
+ direction = "vertical",
26
+ justifyContent = "flex-start",
27
+ alignItems = "stretch",
28
+ gap,
29
+ columnGap,
30
+ rowGap,
31
+ margin,
32
+ padding,
33
+ marginTop,
34
+ marginBottom,
35
+ marginLeft,
36
+ marginRight,
37
+ paddingTop,
38
+ paddingBottom,
39
+ paddingLeft,
40
+ paddingRight,
41
+ width,
42
+ height,
43
+ wrap = false,
44
+ children,
45
+ style,
46
+ className,
47
+ overflow,
48
+ background
49
+ }) => {
50
+ const stackStyle = css({
51
+ display: "flex",
52
+ flexDirection: direction === "horizontal" ? "row" : "column",
53
+ justifyContent,
54
+ alignItems,
55
+ gap: gap && sizeMap[gap] ? sizeMap[gap] : gap,
56
+ columnGap:
57
+ columnGap && sizeMap[columnGap] ? sizeMap[columnGap] : columnGap,
58
+ rowGap: rowGap && sizeMap[rowGap] ? sizeMap[rowGap] : rowGap,
59
+ margin: margin && sizeMap[margin] ? sizeMap[margin] : margin,
60
+ padding: padding && sizeMap[padding] ? sizeMap[padding] : padding,
61
+ marginTop:
62
+ marginTop && sizeMap[marginTop] ? sizeMap[marginTop] : marginTop,
63
+ marginBottom:
64
+ marginBottom && sizeMap[marginBottom]
65
+ ? sizeMap[marginBottom]
66
+ : marginBottom,
67
+ marginLeft:
68
+ marginLeft && sizeMap[marginLeft] ? sizeMap[marginLeft] : marginLeft,
69
+ marginRight:
70
+ marginRight && sizeMap[marginRight]
71
+ ? sizeMap[marginRight]
72
+ : marginRight,
73
+ paddingTop:
74
+ paddingTop && sizeMap[paddingTop] ? sizeMap[paddingTop] : paddingTop,
75
+ paddingBottom:
76
+ paddingBottom && sizeMap[paddingBottom]
77
+ ? sizeMap[paddingBottom]
78
+ : paddingBottom,
79
+ paddingLeft:
80
+ paddingLeft && sizeMap[paddingLeft]
81
+ ? sizeMap[paddingLeft]
82
+ : paddingLeft,
83
+ paddingRight:
84
+ paddingRight && sizeMap[paddingRight]
85
+ ? sizeMap[paddingRight]
86
+ : paddingRight,
87
+ width,
88
+ height,
89
+ overflow,
90
+ flexWrap: wrap ? "wrap" : "nowrap",
91
+ backgroundColor: background,
92
+ ...style,
93
+ });
94
+
95
+ return (
96
+ <div className={mergeClasses(className, stackStyle)}>{children}</div>
97
+ );
98
+ }
99
+ );
100
+
101
+ Stack.displayName = "Stack";
102
+
103
+ export default Stack;
@@ -0,0 +1,48 @@
1
+ import React, { ReactNode, createContext, useContext, useState } from 'react';
2
+ import { Theme, webDarkTheme, webLightTheme } from '@fluentui/react-components';
3
+
4
+ // Define your supported themes
5
+ export type SupportedTheme = 'light' | 'dark';
6
+
7
+ // Create context
8
+ interface ThemeContextValue {
9
+ themeName: SupportedTheme;
10
+ theme: Theme;
11
+ toggleTheme: () => void;
12
+ }
13
+
14
+ const ThemeContext = createContext<ThemeContextValue | undefined>(undefined);
15
+
16
+ // Create provider
17
+ interface ThemeProviderProps {
18
+ children: ReactNode;
19
+ defaultTheme?: SupportedTheme;
20
+ }
21
+
22
+ export const ThemeProvider: React.FC<ThemeProviderProps> = ({
23
+ children,
24
+ defaultTheme = 'light',
25
+ }) => {
26
+ const [themeName, setThemeName] = useState<SupportedTheme>(defaultTheme);
27
+
28
+ const toggleTheme = () => {
29
+ setThemeName(prev => (prev === 'light' ? 'dark' : 'light'));
30
+ };
31
+
32
+ const theme = themeName === 'light' ? webLightTheme : webDarkTheme;
33
+
34
+ return (
35
+ <ThemeContext.Provider value={{ themeName, theme, toggleTheme }}>
36
+ {children}
37
+ </ThemeContext.Provider>
38
+ );
39
+ };
40
+
41
+ // Hook to use the context
42
+ export const useThemeContext = (): ThemeContextValue => {
43
+ const context = useContext(ThemeContext);
44
+ if (!context) {
45
+ throw new Error('useThemeContext must be used within a ThemeProvider');
46
+ }
47
+ return context;
48
+ };
@@ -0,0 +1,23 @@
1
+ import { tokens as fluentTokens } from "@fluentui/react-components";
2
+
3
+ // Assign colors per series based on label
4
+ export function getFluentPalette() {
5
+ return [
6
+ fluentTokens?.colorPaletteBerryBackground2,
7
+ fluentTokens?.colorPaletteBlueBackground2,
8
+ fluentTokens?.colorPaletteGreenBackground2,
9
+ fluentTokens?.colorPaletteDarkOrangeBackground2,
10
+ fluentTokens?.colorPalettePinkBackground2,
11
+ fluentTokens?.colorPalettePurpleBackground2,
12
+ fluentTokens?.colorPaletteRedBackground2,
13
+ fluentTokens?.colorPaletteTealBackground2,
14
+ fluentTokens?.colorPaletteYellowBackground2,
15
+ fluentTokens?.colorPaletteMarigoldBackground2,
16
+ fluentTokens?.colorPaletteBrassBackground2,
17
+ fluentTokens?.colorPaletteForestBackground2,
18
+ fluentTokens?.colorPaletteSeafoamBackground2,
19
+ fluentTokens?.colorPaletteLavenderBackground2,
20
+ fluentTokens?.colorPaletteCornflowerBackground2,
21
+ ];
22
+ }
23
+
@@ -0,0 +1,28 @@
1
+ import { css } from "@emotion/css";
2
+
3
+ export const useGraphGlobalStyles = () => {
4
+ return {
5
+ chartWithLegend: css({
6
+ display: 'flex',
7
+ flexDirection: 'column',
8
+ height: '100%',
9
+ chartArea: {
10
+ flexGrow: 1,
11
+ minHeight: 0,
12
+ },
13
+ }),
14
+ chartArea: css`
15
+ flex-grow: 1;
16
+ min-height: 0;
17
+ `,
18
+ legendArea: css`
19
+ margin-top: 0px;
20
+ padding-top: 0px;
21
+
22
+ margin-Left: 10px;
23
+ margin-Right: 10px;
24
+
25
+ `,
26
+ };
27
+
28
+ }
@@ -0,0 +1 @@
1
+ export * from './useGraphUtils';
@@ -0,0 +1,314 @@
1
+ import {
2
+ ChartType,
3
+ TooltipCallbacks,
4
+ TooltipItem,
5
+ TooltipModel,
6
+ TooltipOptions,
7
+ } from 'chart.js';
8
+ import { Theme, webLightTheme } from '@fluentui/react-components';
9
+
10
+ import AreaChart from '../charts/areaChart/AreaChart';
11
+ import BarChart from '../charts/BarChart/BarChart';
12
+ import BarHorizontalChart from '../charts/barHorizontalChart/BarHotizontalChart';
13
+ import BubbleChart from '../charts/bubbleChart/BubbleChart';
14
+ import ComboChart from '../charts/ComboChart/ComboChart';
15
+ import DoughnutChart from '../charts/Doughnut/DoughnutChart';
16
+ import FloatingBarChart from '../charts/floatBarChart/FloatBarChart';
17
+ import { IChart } from '../models/IChart';
18
+ import LineChart from '../charts/lineChart/LineChart';
19
+ import PieChart from '../charts/PieChart/PieChart';
20
+ import PolarChart from '../charts/polarChart/PolarChart';
21
+ import RadarChart from '../charts/radarChart/RadarChart';
22
+ import React from 'react';
23
+ import ScatterChart from '../charts/scatterChart/ScatterChart';
24
+ import StackedLineChart from '../charts/stackedLineChart/StackedLineChart';
25
+ import SteamChart from '../charts/steamChart/SteamChart';
26
+ import { useMemo } from 'react';
27
+
28
+ /**
29
+ * Lightens a given hex color by a percentage amount (0 to 1).
30
+ */
31
+ export const lightenColor = (hex: string, amount: number): string => {
32
+ if (!/^#?[0-9A-Fa-f]{6}$/.test(hex)) return hex;
33
+ if (hex.startsWith('#')) hex = hex.slice(1);
34
+
35
+ let r = parseInt(hex.slice(0, 2), 16);
36
+ let g = parseInt(hex.slice(2, 4), 16);
37
+ let b = parseInt(hex.slice(4, 6), 16);
38
+
39
+ r = Math.min(255, Math.floor(r + (255 - r) * amount));
40
+ g = Math.min(255, Math.floor(g + (255 - g) * amount));
41
+ b = Math.min(255, Math.floor(b + (255 - b) * amount));
42
+
43
+ const toHex = (v: number) => v.toString(16).padStart(2, '0');
44
+ return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
45
+ };
46
+
47
+ export const getFluentPalette = (_theme: Theme): string[] => [
48
+ '#4e79a7',
49
+ '#f28e2c',
50
+ '#e15759',
51
+ '#76b7b2',
52
+ '#59a14f',
53
+ '#edc949',
54
+ '#af7aa1',
55
+ '#ff9da7',
56
+ '#9c755f',
57
+ '#bab0ab',
58
+ '#8cd17d',
59
+ '#b6992d',
60
+ '#d37295',
61
+ '#fabfd2',
62
+ '#79706e',
63
+ ];
64
+
65
+ const chartProps = (chart: IChart) => ({
66
+ data: chart.data,
67
+ title: chart.title,
68
+ getPrimary: (d: any) => d.name,
69
+ getSecondary: (d: any) => d.value,
70
+ });
71
+
72
+ const getChartComponent = (chart: IChart, theme: Theme) => {
73
+ const { type } = chart;
74
+ const fuiTheme = theme ?? webLightTheme;
75
+ switch (type) {
76
+ case 'bar':
77
+ return <BarChart {...chartProps(chart)} stacked={false} theme={fuiTheme} />;
78
+ case 'line':
79
+ return <LineChart {...chartProps(chart)} theme={fuiTheme} />;
80
+ case 'area':
81
+ return <AreaChart {...chartProps(chart)} stacked={false} theme={fuiTheme} />;
82
+
83
+ case 'bar-horizontal':
84
+ return <BarHorizontalChart {...chartProps(chart)} stacked={true} theme={fuiTheme} />;
85
+ case 'bubble':
86
+ return (
87
+ <BubbleChart {...chartProps(chart)} getRadius={d => d.radius ?? 1} theme={fuiTheme} />
88
+ );
89
+ case 'multiple-axes':
90
+ return (
91
+ <ComboChart
92
+ {...chartProps(chart)}
93
+ theme={fuiTheme}
94
+
95
+ data={chart.data.map((series: any) => ({
96
+ label: series.label,
97
+ type: series.type ?? 'bar',
98
+ data: series.data,
99
+ yAxisID: series.secondaryAxisId,
100
+
101
+ }))}
102
+ />
103
+ );
104
+ case 'steam':
105
+ return <SteamChart {...chartProps(chart)} theme={fuiTheme} />;
106
+ case 'floating-bar':
107
+ return (
108
+ <FloatingBarChart
109
+ getRange={d => [d.min ?? 0, d.max ?? 0]}
110
+ {...chartProps(chart)}
111
+ theme={fuiTheme}
112
+ />
113
+ );
114
+ case 'stacked-line':
115
+ return <StackedLineChart {...chartProps(chart)} theme={fuiTheme} />;
116
+ case 'doughnut':
117
+ return (
118
+ <DoughnutChart
119
+ getLabel={datum => datum.name}
120
+ getValue={datum => datum.value ?? 0}
121
+ {...chartProps(chart)}
122
+ theme={fuiTheme}
123
+ />
124
+ );
125
+ case 'pie':
126
+ return (
127
+ <PieChart
128
+ getLabel={datum => datum.name}
129
+ getValue={datum => datum.value ?? 0}
130
+ showDataLabels={true}
131
+ {...chartProps(chart)}
132
+ theme={fuiTheme}
133
+ />
134
+ );
135
+ case 'scatter':
136
+ return (
137
+ <ScatterChart
138
+ getX={d => {
139
+ if (typeof d.x === 'number') return d.x;
140
+ if (typeof d.x === 'string') return Number(d.x) || 0;
141
+ if (d.x instanceof Date) return d.x.getTime();
142
+ return 0;
143
+ }}
144
+ getY={d => (typeof d.y === 'number' ? d.y : 0)}
145
+ {...chartProps(chart)}
146
+ theme={fuiTheme}
147
+ showDataLabels={false}
148
+ />
149
+ );
150
+ case 'polar':
151
+ return (
152
+ <PolarChart
153
+ data={chart.data}
154
+ getLabel={d => d.name}
155
+ getValue={d => d.value ?? 0}
156
+ title={chart.title}
157
+ showDataLabels={true}
158
+ theme={fuiTheme}
159
+ />
160
+ );
161
+ case 'radar':
162
+ return (
163
+ <RadarChart
164
+ data={chart.data}
165
+ getLabel={d => d.name}
166
+ getValue={d => d.value ?? 0}
167
+ title={chart.title}
168
+ theme={fuiTheme}
169
+ />
170
+ );
171
+
172
+ default:
173
+ throw new Error(`Unsupported chart type: ${type}`);
174
+ }
175
+ };
176
+
177
+ /**
178
+ * Smart Fluent tooltip generator with chart-type awareness.
179
+ */
180
+
181
+ export function createFluentTooltip<TType extends ChartType>(
182
+ theme: Theme
183
+ ): Partial<TooltipOptions<TType>> {
184
+ const fontFamily = theme.fontFamilyBase;
185
+ const fontSize = parseInt(theme.fontSizeBase200.replace('px', '')) || 14;
186
+ const tooltipBg = theme.colorNeutralBackground1;
187
+ const tooltipTitleColor = theme.colorNeutralForeground1;
188
+ const tooltipBodyColor = theme.colorNeutralForeground2;
189
+ const borderColor = theme.colorNeutralStroke2;
190
+
191
+ const callbacks: TooltipCallbacks<TType> = {
192
+ title(this: TooltipModel<TType>, context: TooltipItem<TType>[]) {
193
+ return context[0]?.label ?? '';
194
+ },
195
+
196
+ label(this: TooltipModel<TType>, item: TooltipItem<TType>) {
197
+ const datasetLabel =
198
+ 'label' in (item.dataset as Record<string, any>) &&
199
+ typeof (item.dataset as Record<string, any>).label === 'string'
200
+ ? (item.dataset as Record<string, any>).label
201
+ : 'Value';
202
+
203
+ const raw = item.raw;
204
+
205
+ // Bubble format { x, y, r }
206
+ if (
207
+ typeof raw === 'object' &&
208
+ raw !== null &&
209
+ 'x' in raw &&
210
+ 'y' in raw &&
211
+ 'r' in raw
212
+ ) {
213
+ const { x, y, r } = raw as any;
214
+ return `${datasetLabel} — x: ${x}, y: ${y}, r: ${r}`;
215
+ }
216
+
217
+ // Scatter format { x, y }
218
+ if (typeof raw === 'object' && raw !== null && 'x' in raw && 'y' in raw) {
219
+ const { x, y } = raw as any;
220
+ return `${datasetLabel} — x: ${x}, y: ${y}`;
221
+ }
222
+
223
+ // Floating bar [min, max]
224
+ if (Array.isArray(raw) && raw.length === 2) {
225
+ const [min, max] = raw;
226
+ return `${datasetLabel}: ${min} – ${max}`;
227
+ }
228
+
229
+ // Default: single number or string
230
+ return `${datasetLabel}: ${raw}`;
231
+ },
232
+
233
+ beforeTitle: () => '',
234
+ afterTitle: () => '',
235
+ beforeBody: () => '',
236
+ afterBody: () => '',
237
+ beforeLabel: () => '',
238
+ afterLabel: () => '',
239
+ labelColor: () => undefined as any,
240
+ labelTextColor: () => '',
241
+ footer: () => '',
242
+ beforeFooter: () => '',
243
+ afterFooter: () => '',
244
+ labelPointStyle: () => ({
245
+ pointStyle: 'circle',
246
+ rotation: 0,
247
+ radius: 5,
248
+ }),
249
+ };
250
+
251
+ return {
252
+ enabled: true,
253
+ displayColors: true,
254
+ boxWidth: 10,
255
+ boxHeight: 10,
256
+ boxPadding: 5,
257
+ backgroundColor: tooltipBg,
258
+ borderColor,
259
+ borderWidth: 1,
260
+ cornerRadius: 4,
261
+ padding: 10,
262
+ titleColor: tooltipTitleColor,
263
+ bodyColor: tooltipBodyColor,
264
+ titleFont: { family: fontFamily, size: fontSize },
265
+ bodyFont: { family: fontFamily, size: fontSize },
266
+ callbacks,
267
+ };
268
+ }
269
+
270
+
271
+ /**
272
+ * Returns a Chart.js ticks callback to truncate long labels and add optional prefix/suffix.
273
+ */
274
+ export const createAxisLabelFormatter = ({
275
+ maxLength = 12,
276
+ suffix = '',
277
+ prefix = '',
278
+ }: {
279
+ maxLength?: number;
280
+ suffix?: string;
281
+ prefix?: string;
282
+ }) => {
283
+ return function (this: any, value: string | number): string {
284
+ const label = typeof value === 'number' ? this.getLabelForValue(value) : String(value);
285
+ const trimmed = label.length > maxLength ? label.slice(0, maxLength) + '…' : label;
286
+ return `${prefix}${trimmed}${suffix}`;
287
+ };
288
+ };
289
+
290
+
291
+ function debounce<T extends (...args: any[]) => void>(fn: T, delay: number): T {
292
+ let timer: ReturnType<typeof setTimeout> | null = null;
293
+ return function(this: any, ...args: any[]) {
294
+ if (timer) clearTimeout(timer);
295
+ timer = setTimeout(() => fn.apply(this, args), delay);
296
+ } as T;
297
+ }
298
+
299
+ /**
300
+ * useGraphUtils — shared theming and chart helpers.
301
+ */
302
+ export function useGraphUtils(theme?: Theme) {
303
+ return useMemo(
304
+ () => ({
305
+ lightenColor,
306
+ getFluentPalette,
307
+ getChartComponent,
308
+ createFluentTooltip,
309
+ createAxisLabelFormatter,
310
+ debounce
311
+ }),
312
+ [theme]
313
+ );
314
+ }
@@ -0,0 +1,35 @@
1
+ import { useEffect, useRef, useState } from 'react';
2
+
3
+ import { ResizeObserver } from '@juggle/resize-observer';
4
+ import { useGraphUtils } from './useGraphUtils';
5
+
6
+ const BUTTON_WIDTH = 100;
7
+ const GAP = 10;
8
+
9
+ export function useResponsiveLegend<T extends { label: string }>(items: T[]) {
10
+ const containerRef = useRef<HTMLDivElement>(null);
11
+ const [maxVisible, setMaxVisible] = useState(items.length);
12
+ const { debounce } = useGraphUtils();
13
+ useEffect(() => {
14
+ const measure = () => {
15
+ const containerWidth = containerRef.current?.offsetWidth ?? 0;
16
+ const itemTotalWidth = BUTTON_WIDTH + GAP;
17
+ const count = Math.floor(containerWidth / itemTotalWidth);
18
+ setMaxVisible(count);
19
+ };
20
+
21
+ const debouncedMeasure = debounce(measure, 100); // debounce resize
22
+
23
+ const observer = new ResizeObserver(debouncedMeasure);
24
+ if (containerRef.current) observer.observe(containerRef.current);
25
+ measure(); // initial
26
+
27
+ return () => observer.disconnect();
28
+ }, [items]);
29
+
30
+ return {
31
+ containerRef,
32
+ visibleItems: items.slice(0, maxVisible),
33
+ overflowItems: items.slice(maxVisible),
34
+ };
35
+ }
package/src/index.tsx ADDED
@@ -0,0 +1,4 @@
1
+
2
+ export * from './components';
3
+ export * from './hooks';
4
+ export * from './models';
@@ -0,0 +1,50 @@
1
+ export type ChartType =
2
+ | 'bar'
3
+ | 'line'
4
+ | 'area'
5
+ | 'band'
6
+ | 'bar-horizontal'
7
+ | 'bubble'
8
+ | 'steam'
9
+ | 'multiple-axes'
10
+ | 'floating-bar'
11
+ | 'stacked-line'
12
+ | 'doughnut'
13
+ | 'pie'
14
+ | 'radar'
15
+ | 'polar'
16
+ | 'combo'
17
+ | 'gauge'
18
+ | 'funnel'
19
+ | 'heatmap'
20
+ | 'treemap'
21
+ | 'sunburst'
22
+ | 'sankey'
23
+ | 'waterfall'
24
+ | 'boxplot'
25
+ | 'violin'
26
+ | 'radial-bar'
27
+ | 'sparkline'
28
+ | 'scatter';
29
+
30
+ export type ChartData = {
31
+ name: string;
32
+ value?: number;
33
+ min?: number;
34
+ max?: number;
35
+ radius?: number;
36
+ x?: number | string | Date;
37
+ y?: number;
38
+
39
+ };
40
+ export interface IChart {
41
+ id: string;
42
+ title: string;
43
+ type: ChartType;
44
+ data: { label: string; data: ChartData[]; secondaryAxisId?: string }[];
45
+ }
46
+ export interface BubbleDatum {
47
+ primary: number | string | Date;
48
+ secondary: number;
49
+ radius: number;
50
+ }
@@ -0,0 +1 @@
1
+ export * from './IChart';