@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.
- package/LICENSE +21 -0
- package/README.md +462 -0
- package/dist/charts/BarChart/BarChart.d.ts +16 -0
- package/dist/charts/BarChart/index.d.ts +1 -0
- package/dist/charts/ComboChart/ComboChart.d.ts +16 -0
- package/dist/charts/ComboChart/index.d.ts +1 -0
- package/dist/charts/Doughnut/DoughnutChart.d.ts +14 -0
- package/dist/charts/Doughnut/index.d.ts +1 -0
- package/dist/charts/PieChart/PieChart.d.ts +14 -0
- package/dist/charts/PieChart/index.d.ts +1 -0
- package/dist/charts/areaChart/AreaChart.d.ts +15 -0
- package/dist/charts/areaChart/index.d.ts +1 -0
- package/dist/charts/barHorizontalChart/BarHotizontalChart.d.ts +15 -0
- package/dist/charts/barHorizontalChart/index.d.ts +1 -0
- package/dist/charts/bubbleChart/BubbleChart.d.ts +15 -0
- package/dist/charts/bubbleChart/index.d.ts +1 -0
- package/dist/charts/floatBarChart/FloatBarChart.d.ts +14 -0
- package/dist/charts/floatBarChart/index.d.ts +1 -0
- package/dist/charts/lineChart/LineChart.d.ts +14 -0
- package/dist/charts/lineChart/index.d.ts +1 -0
- package/dist/charts/polarChart/PolarChart.d.ts +14 -0
- package/dist/charts/polarChart/index.d.ts +1 -0
- package/dist/charts/radarChart/RadarChart.d.ts +14 -0
- package/dist/charts/radarChart/index.d.ts +1 -0
- package/dist/charts/scatterChart/ScatterChart.d.ts +14 -0
- package/dist/charts/scatterChart/index.d.ts +1 -0
- package/dist/charts/stackedLineChart/StackedLineChart.d.ts +14 -0
- package/dist/charts/stackedLineChart/index.d.ts +1 -0
- package/dist/charts/steamChart/SteamChart.d.ts +14 -0
- package/dist/charts/steamChart/index.d.ts +1 -0
- package/dist/components/DashBoard.d.ts +3 -0
- package/dist/components/RenderLegend/RenderLegend.d.ts +11 -0
- package/dist/components/RenderTooltip/RenderTooltip.d.ts +14 -0
- package/dist/components/buttonMenu/ButtonMenu.d.ts +3 -0
- package/dist/components/buttonMenu/IButtonMenuOption.d.ts +10 -0
- package/dist/components/buttonMenu/IButtonMenuProps.d.ts +37 -0
- package/dist/components/index.d.ts +15 -0
- package/dist/components/legendContainer/LegendContainer.d.ts +16 -0
- package/dist/components/legendeButton/LegendButton.d.ts +11 -0
- package/dist/components/renderSliceLegend/RenderSliceLegend.d.ts +9 -0
- package/dist/components/renderValueLegend/RenderValueLegend.d.ts +13 -0
- package/dist/components/stack/IStackProps.d.ts +76 -0
- package/dist/components/stack/Stack.d.ts +8 -0
- package/dist/components/themeProvider/ThemeProvider.d.ts +15 -0
- package/dist/constants/Constants.d.ts +1 -0
- package/dist/fluentui-react-charts.cjs.development.js +2916 -0
- package/dist/fluentui-react-charts.cjs.development.js.map +1 -0
- package/dist/fluentui-react-charts.cjs.production.min.js +2 -0
- package/dist/fluentui-react-charts.cjs.production.min.js.map +1 -0
- package/dist/fluentui-react-charts.esm.js +2905 -0
- package/dist/fluentui-react-charts.esm.js.map +1 -0
- package/dist/graphGlobalStyles/useGraphGlobalStyles.d.ts +5 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/useGraphUtils.d.ts +38 -0
- package/dist/hooks/useResponsiveLegend.d.ts +8 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +8 -0
- package/dist/models/IChart.d.ts +25 -0
- package/dist/models/index.d.ts +1 -0
- package/package.json +66 -0
- package/src/assets/sample1.png +0 -0
- package/src/assets/sample2.png +0 -0
- package/src/assets/sample3.png +0 -0
- package/src/charts/BarChart/BarChart.tsx +227 -0
- package/src/charts/BarChart/README.MD +335 -0
- package/src/charts/BarChart/index.ts +1 -0
- package/src/charts/ComboChart/ComboChart.tsx +209 -0
- package/src/charts/ComboChart/README.MD +347 -0
- package/src/charts/ComboChart/index.ts +1 -0
- package/src/charts/Doughnut/DoughnutChart.tsx +152 -0
- package/src/charts/Doughnut/README.MD +296 -0
- package/src/charts/Doughnut/index.ts +1 -0
- package/src/charts/PieChart/PieChart.tsx +148 -0
- package/src/charts/PieChart/README.MD +315 -0
- package/src/charts/PieChart/index.ts +1 -0
- package/src/charts/areaChart/AreaChart.tsx +195 -0
- package/src/charts/areaChart/README.MD +236 -0
- package/src/charts/areaChart/index.ts +1 -0
- package/src/charts/barHorizontalChart/BarHotizontalChart.tsx +200 -0
- package/src/charts/barHorizontalChart/README.MD +278 -0
- package/src/charts/barHorizontalChart/index.ts +2 -0
- package/src/charts/bubbleChart/BubbleChart.tsx +184 -0
- package/src/charts/bubbleChart/README.MD +275 -0
- package/src/charts/bubbleChart/index.ts +1 -0
- package/src/charts/floatBarChart/FloatBarChart.tsx +178 -0
- package/src/charts/floatBarChart/README.MD +354 -0
- package/src/charts/floatBarChart/index.ts +1 -0
- package/src/charts/lineChart/LineChart.tsx +200 -0
- package/src/charts/lineChart/README.MD +354 -0
- package/src/charts/lineChart/index.ts +1 -0
- package/src/charts/polarChart/PolarChart.tsx +161 -0
- package/src/charts/polarChart/README.MD +336 -0
- package/src/charts/polarChart/index.ts +1 -0
- package/src/charts/radarChart/README.MD +388 -0
- package/src/charts/radarChart/RadarChart.tsx +173 -0
- package/src/charts/radarChart/index.ts +1 -0
- package/src/charts/scatterChart/README.MD +335 -0
- package/src/charts/scatterChart/ScatterChart.tsx +155 -0
- package/src/charts/scatterChart/index.ts +1 -0
- package/src/charts/stackedLineChart/README.MD +396 -0
- package/src/charts/stackedLineChart/StackedLineChart.tsx +188 -0
- package/src/charts/stackedLineChart/index.ts +1 -0
- package/src/charts/steamChart/README.MD +414 -0
- package/src/charts/steamChart/SteamChart.tsx +236 -0
- package/src/charts/steamChart/index.ts +1 -0
- package/src/components/DashBoard.tsx +409 -0
- package/src/components/RenderLegend/RenderLegend.tsx +40 -0
- package/src/components/RenderTooltip/RenderTooltip.tsx +111 -0
- package/src/components/buttonMenu/ButtonMenu.tsx +186 -0
- package/src/components/buttonMenu/IButtonMenuOption.ts +9 -0
- package/src/components/buttonMenu/IButtonMenuProps.tsx +40 -0
- package/src/components/index.ts +15 -0
- package/src/components/legendContainer/LegendContainer.tsx +118 -0
- package/src/components/legendeButton/LegendButton.tsx +57 -0
- package/src/components/renderSliceLegend/RenderSliceLegend.tsx +46 -0
- package/src/components/renderValueLegend/RenderValueLegend.tsx +43 -0
- package/src/components/stack/IStackProps.tsx +94 -0
- package/src/components/stack/Stack.tsx +103 -0
- package/src/components/themeProvider/ThemeProvider.tsx +48 -0
- package/src/constants/Constants.tsx +23 -0
- package/src/graphGlobalStyles/useGraphGlobalStyles.ts +28 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useGraphUtils.tsx +314 -0
- package/src/hooks/useResponsiveLegend.ts +35 -0
- package/src/index.tsx +4 -0
- package/src/models/IChart.ts +50 -0
- package/src/models/index.ts +1 -0
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
Field,
|
|
6
|
+
Menu,
|
|
7
|
+
MenuButton,
|
|
8
|
+
MenuItem,
|
|
9
|
+
MenuItemRadio,
|
|
10
|
+
MenuList,
|
|
11
|
+
MenuPopover,
|
|
12
|
+
MenuProps,
|
|
13
|
+
MenuTrigger,
|
|
14
|
+
Tooltip,
|
|
15
|
+
} from '@fluentui/react-components';
|
|
16
|
+
|
|
17
|
+
import { IButtonMenuOption } from './IButtonMenuOption';
|
|
18
|
+
import { IButtonMenuProps } from './IButtonMenuProps';
|
|
19
|
+
|
|
20
|
+
export const ButtonMenu: React.FunctionComponent<IButtonMenuProps> = (
|
|
21
|
+
props: React.PropsWithChildren<IButtonMenuProps>
|
|
22
|
+
) => {
|
|
23
|
+
const {
|
|
24
|
+
onSelected,
|
|
25
|
+
value,
|
|
26
|
+
styles,
|
|
27
|
+
options,
|
|
28
|
+
apparence,
|
|
29
|
+
shape,
|
|
30
|
+
label,
|
|
31
|
+
defaultButtonIcon = undefined,
|
|
32
|
+
showItemLabel = true,
|
|
33
|
+
showOnlyIcon = false,
|
|
34
|
+
DefaultButtonLabel,
|
|
35
|
+
as = 'Radiobutton',
|
|
36
|
+
buttonTooltip = undefined,
|
|
37
|
+
} = props;
|
|
38
|
+
const iconSelectedRef = React.useRef<JSX.Element>();
|
|
39
|
+
|
|
40
|
+
// State for selected option
|
|
41
|
+
const [selectedOption, setSelectedOption] = React.useState<IButtonMenuOption>(
|
|
42
|
+
value ?? options[0]
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
const [checkedValues, setCheckedValues] = React.useState<
|
|
46
|
+
Record<string, string[]>
|
|
47
|
+
>({
|
|
48
|
+
option: [value ? value?.key : options?.[0]?.key],
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const fieldRef = React.useRef<HTMLDivElement>(null);
|
|
52
|
+
|
|
53
|
+
React.useEffect(() => {
|
|
54
|
+
// Synchronize selectedOption with props
|
|
55
|
+
if (value !== undefined) {
|
|
56
|
+
setSelectedOption(value);
|
|
57
|
+
setCheckedValues({ option: [value.key] });
|
|
58
|
+
}
|
|
59
|
+
iconSelectedRef.current = options.find(
|
|
60
|
+
option => option.key === (value?.key ?? options[0]?.key)
|
|
61
|
+
)?.icon;
|
|
62
|
+
}, [value, options]);
|
|
63
|
+
|
|
64
|
+
const [open, setOpen] = React.useState(false);
|
|
65
|
+
const onOpenChange: MenuProps['onOpenChange'] = React.useCallback(
|
|
66
|
+
(_e: any, data: { open: boolean | ((prevState: boolean) => boolean) }) => {
|
|
67
|
+
setOpen(data.open);
|
|
68
|
+
},
|
|
69
|
+
[]
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
const getOptionLabel = React.useCallback((option: IButtonMenuOption) => {
|
|
73
|
+
return option?.text ?? '';
|
|
74
|
+
}, []);
|
|
75
|
+
|
|
76
|
+
const onCheckedValueChange: MenuProps['onCheckedValueChange'] = React.useCallback(
|
|
77
|
+
(
|
|
78
|
+
_e: unknown,
|
|
79
|
+
{ name, checkedItems }: { name: string; checkedItems: string[] }
|
|
80
|
+
) => {
|
|
81
|
+
if (name === 'option') {
|
|
82
|
+
const newOption = checkedItems[0];
|
|
83
|
+
setSelectedOption(options.find(option => option.key === newOption)!);
|
|
84
|
+
setCheckedValues({ option: checkedItems });
|
|
85
|
+
iconSelectedRef.current = options.find(
|
|
86
|
+
option => option.key === newOption
|
|
87
|
+
)?.icon;
|
|
88
|
+
onSelected(options.find(option => option.key === newOption)!);
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
[options, onSelected]
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
const buttonLabel = React.useMemo(() => {
|
|
95
|
+
if (showItemLabel) {
|
|
96
|
+
return (
|
|
97
|
+
getOptionLabel(selectedOption) ??
|
|
98
|
+
DefaultButtonLabel ??
|
|
99
|
+
'No data available'
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
return DefaultButtonLabel ?? 'Select an option';
|
|
103
|
+
}, [selectedOption, showItemLabel, getOptionLabel, DefaultButtonLabel]);
|
|
104
|
+
|
|
105
|
+
const RenderMenuButtonIcon = React.useCallback(() => {
|
|
106
|
+
return (
|
|
107
|
+
<Field label={label} style={styles} ref={fieldRef}>
|
|
108
|
+
<Menu open={open} onOpenChange={onOpenChange}>
|
|
109
|
+
<MenuTrigger disableButtonEnhancement>
|
|
110
|
+
<MenuButton
|
|
111
|
+
shape={shape ?? 'circular'}
|
|
112
|
+
icon={showItemLabel ? iconSelectedRef.current : defaultButtonIcon}
|
|
113
|
+
appearance={apparence ?? 'secondary'}
|
|
114
|
+
>
|
|
115
|
+
{showOnlyIcon ? '' : buttonLabel}
|
|
116
|
+
</MenuButton>
|
|
117
|
+
</MenuTrigger>
|
|
118
|
+
<MenuPopover style={{ minWidth: 'fit-content' }}>
|
|
119
|
+
<MenuList
|
|
120
|
+
onCheckedValueChange={onCheckedValueChange}
|
|
121
|
+
checkedValues={checkedValues}
|
|
122
|
+
style={{ width: fieldRef.current?.offsetWidth }}
|
|
123
|
+
>
|
|
124
|
+
{options.map((option: IButtonMenuOption) =>
|
|
125
|
+
as === 'Radiobutton' ? (
|
|
126
|
+
<MenuItemRadio
|
|
127
|
+
key={option.key}
|
|
128
|
+
name="option"
|
|
129
|
+
value={option.key}
|
|
130
|
+
icon={option.icon}
|
|
131
|
+
disabled={option.disabled}
|
|
132
|
+
>
|
|
133
|
+
{getOptionLabel(option)}
|
|
134
|
+
</MenuItemRadio>
|
|
135
|
+
) : (
|
|
136
|
+
<MenuItem
|
|
137
|
+
key={option.key}
|
|
138
|
+
onClick={() => {
|
|
139
|
+
onSelected(option);
|
|
140
|
+
setOpen(false);
|
|
141
|
+
}}
|
|
142
|
+
icon={option.icon}
|
|
143
|
+
disabled={option.disabled}
|
|
144
|
+
>
|
|
145
|
+
{getOptionLabel(option)}
|
|
146
|
+
</MenuItem>
|
|
147
|
+
)
|
|
148
|
+
)}
|
|
149
|
+
</MenuList>
|
|
150
|
+
</MenuPopover>
|
|
151
|
+
</Menu>
|
|
152
|
+
</Field>
|
|
153
|
+
);
|
|
154
|
+
}, [
|
|
155
|
+
label,
|
|
156
|
+
styles,
|
|
157
|
+
open,
|
|
158
|
+
onOpenChange,
|
|
159
|
+
shape,
|
|
160
|
+
showItemLabel,
|
|
161
|
+
defaultButtonIcon,
|
|
162
|
+
apparence,
|
|
163
|
+
showOnlyIcon,
|
|
164
|
+
buttonLabel,
|
|
165
|
+
onCheckedValueChange,
|
|
166
|
+
checkedValues,
|
|
167
|
+
options,
|
|
168
|
+
as,
|
|
169
|
+
getOptionLabel,
|
|
170
|
+
onSelected,
|
|
171
|
+
]);
|
|
172
|
+
|
|
173
|
+
return (
|
|
174
|
+
<>
|
|
175
|
+
{buttonTooltip !== undefined ? (
|
|
176
|
+
<Tooltip content={buttonTooltip} relationship="label">
|
|
177
|
+
<div>
|
|
178
|
+
<RenderMenuButtonIcon />
|
|
179
|
+
</div>
|
|
180
|
+
</Tooltip>
|
|
181
|
+
) : (
|
|
182
|
+
<RenderMenuButtonIcon />
|
|
183
|
+
)}
|
|
184
|
+
</>
|
|
185
|
+
);
|
|
186
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
import { IButtonMenuOption } from "./IButtonMenuOption";
|
|
4
|
+
import { Theme } from "@fluentui/react-components";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Props for the ButtonMenu component.
|
|
8
|
+
*
|
|
9
|
+
* @property onSelected - Callback invoked when an option is selected.
|
|
10
|
+
* @property value - The currently selected option.
|
|
11
|
+
* @property label - Optional label for the button menu.
|
|
12
|
+
* @property styles - Custom CSS styles for the component.
|
|
13
|
+
* @property theme - Theme object to style the component.
|
|
14
|
+
* @property options - Array of selectable options for the menu.
|
|
15
|
+
* @property apparence - Visual appearance of the button menu. Can be "primary", "subtle", "outline", or "transparent".
|
|
16
|
+
* @property shape - Shape of the button. Can be "circular" or "square".
|
|
17
|
+
* @property defaultButtonIcon - Icon to display on the default button.
|
|
18
|
+
* @property showItemLabel - Whether to show the label for each item.
|
|
19
|
+
* @property showOnlyIcon - Whether to display only the icon without the label.
|
|
20
|
+
* @property DefaultButtonLabel - Label to display on the default button.
|
|
21
|
+
* @property as - Determines the button type, either "Radiobutton" or "MenuButton".
|
|
22
|
+
* @property buttonTooltip - Tooltip text for the button.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
export interface IButtonMenuProps {
|
|
26
|
+
onSelected: (option: IButtonMenuOption) => void;
|
|
27
|
+
value?: IButtonMenuOption;
|
|
28
|
+
label?: string;
|
|
29
|
+
styles?: React.CSSProperties;
|
|
30
|
+
theme?: Theme;
|
|
31
|
+
options: IButtonMenuOption[];
|
|
32
|
+
apparence?: "primary" | "subtle" | "outline" | "transparent";
|
|
33
|
+
shape?: "circular" | "square";
|
|
34
|
+
defaultButtonIcon?: JSX.Element;
|
|
35
|
+
showItemLabel?: boolean;
|
|
36
|
+
showOnlyIcon?: boolean;
|
|
37
|
+
DefaultButtonLabel?: string;
|
|
38
|
+
as?: "Radiobutton" | "MenuButton";
|
|
39
|
+
buttonTooltip?: string;
|
|
40
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export * from '../charts/BarChart';
|
|
2
|
+
export * from '../charts/ComboChart';
|
|
3
|
+
export * from './DashBoard';
|
|
4
|
+
export * from '../charts/Doughnut';
|
|
5
|
+
export * from '../charts/PieChart';
|
|
6
|
+
export * from '../charts/areaChart';
|
|
7
|
+
export * from '../charts/barHorizontalChart';
|
|
8
|
+
export * from '../charts/bubbleChart';
|
|
9
|
+
export * from '../charts/floatBarChart';
|
|
10
|
+
export * from '../charts/lineChart';
|
|
11
|
+
export * from '../charts/polarChart';
|
|
12
|
+
export * from '../charts/radarChart';
|
|
13
|
+
export * from '../charts/scatterChart';
|
|
14
|
+
export * from '../charts/stackedLineChart';
|
|
15
|
+
export * from '../charts/steamChart';
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Menu,
|
|
3
|
+
MenuButton,
|
|
4
|
+
MenuItem,
|
|
5
|
+
MenuList,
|
|
6
|
+
MenuPopover,
|
|
7
|
+
MenuTrigger,
|
|
8
|
+
} from '@fluentui/react-components';
|
|
9
|
+
|
|
10
|
+
import { LegendButton } from '../legendeButton/LegendButton';
|
|
11
|
+
import React from 'react';
|
|
12
|
+
import Stack from '../stack/Stack';
|
|
13
|
+
import { css } from '@emotion/css';
|
|
14
|
+
|
|
15
|
+
interface LegendContainerProps<T> {
|
|
16
|
+
items: T[];
|
|
17
|
+
visibleLabels: string[];
|
|
18
|
+
toggleLabel: (label: string) => void;
|
|
19
|
+
renderLabel: (item: T) => string;
|
|
20
|
+
getColor: (item: T) => string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const styles = {
|
|
24
|
+
wrapper: css({
|
|
25
|
+
display: 'flex',
|
|
26
|
+
flexWrap: 'nowrap',
|
|
27
|
+
overflow: 'hidden',
|
|
28
|
+
justifyContent: 'center',
|
|
29
|
+
gap: 10,
|
|
30
|
+
padding: 2,
|
|
31
|
+
width: '100%',
|
|
32
|
+
boxSizing: 'border-box',
|
|
33
|
+
}),
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export function LegendContainer<T extends { label: string }>(
|
|
37
|
+
props: LegendContainerProps<T> & {
|
|
38
|
+
containerRef: React.RefObject<HTMLDivElement>;
|
|
39
|
+
|
|
40
|
+
visibleItems: T[];
|
|
41
|
+
overflowItems: T[];
|
|
42
|
+
}
|
|
43
|
+
) {
|
|
44
|
+
const {
|
|
45
|
+
containerRef,
|
|
46
|
+
|
|
47
|
+
visibleItems,
|
|
48
|
+
overflowItems,
|
|
49
|
+
visibleLabels,
|
|
50
|
+
toggleLabel,
|
|
51
|
+
renderLabel,
|
|
52
|
+
getColor,
|
|
53
|
+
} = props;
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<Stack
|
|
57
|
+
justifyContent="center"
|
|
58
|
+
alignItems="center"
|
|
59
|
+
marginLeft="10px"
|
|
60
|
+
marginRight="10px"
|
|
61
|
+
marginTop="25px"
|
|
62
|
+
marginBottom="20px"
|
|
63
|
+
>
|
|
64
|
+
<div ref={containerRef} className={styles.wrapper}>
|
|
65
|
+
{visibleItems.map(item => {
|
|
66
|
+
const label = item.label;
|
|
67
|
+
const isVisible =
|
|
68
|
+
visibleLabels.length === 0 || visibleLabels.includes(label);
|
|
69
|
+
return (
|
|
70
|
+
|
|
71
|
+
<LegendButton
|
|
72
|
+
key={label}
|
|
73
|
+
label={renderLabel(item)}
|
|
74
|
+
color={getColor(item)}
|
|
75
|
+
isVisible={isVisible}
|
|
76
|
+
onClick={() => toggleLabel(label)}
|
|
77
|
+
|
|
78
|
+
/>
|
|
79
|
+
|
|
80
|
+
);
|
|
81
|
+
})}
|
|
82
|
+
|
|
83
|
+
{overflowItems.length > 0 && (
|
|
84
|
+
<Menu>
|
|
85
|
+
<MenuTrigger disableButtonEnhancement>
|
|
86
|
+
<MenuButton size="small" appearance="transparent">
|
|
87
|
+
+{overflowItems.length}
|
|
88
|
+
</MenuButton>
|
|
89
|
+
</MenuTrigger>
|
|
90
|
+
<MenuPopover style={{ minWidth: 'fit-content', maxWidth: '200px' }}>
|
|
91
|
+
<MenuList>
|
|
92
|
+
{overflowItems.map(item => {
|
|
93
|
+
const label = item.label;
|
|
94
|
+
const isVisible =
|
|
95
|
+
visibleLabels.length === 0 || visibleLabels.includes(label);
|
|
96
|
+
return (
|
|
97
|
+
<MenuItem key={label} style={{ padding: 0 }}>
|
|
98
|
+
|
|
99
|
+
<LegendButton
|
|
100
|
+
label={renderLabel(item)}
|
|
101
|
+
color={getColor(item)}
|
|
102
|
+
isVisible={isVisible}
|
|
103
|
+
onClick={() => toggleLabel(label)}
|
|
104
|
+
style={{ width: '100px', textAlign: 'left' }}
|
|
105
|
+
/>
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
</MenuItem>
|
|
109
|
+
);
|
|
110
|
+
})}
|
|
111
|
+
</MenuList>
|
|
112
|
+
</MenuPopover>
|
|
113
|
+
</Menu>
|
|
114
|
+
)}
|
|
115
|
+
</div>
|
|
116
|
+
</Stack>
|
|
117
|
+
);
|
|
118
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// LegendButton.tsx
|
|
2
|
+
import { Button, Caption1, Tooltip, tokens } from '@fluentui/react-components';
|
|
3
|
+
|
|
4
|
+
import React from 'react';
|
|
5
|
+
|
|
6
|
+
interface LegendButtonProps {
|
|
7
|
+
label: string;
|
|
8
|
+
isVisible: boolean;
|
|
9
|
+
color: string;
|
|
10
|
+
onClick: () => void;
|
|
11
|
+
style?: React.CSSProperties;
|
|
12
|
+
buttonRef?: React.Ref<HTMLButtonElement>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const LegendButton: React.FC<LegendButtonProps> = ({
|
|
16
|
+
label,
|
|
17
|
+
isVisible,
|
|
18
|
+
color,
|
|
19
|
+
onClick,
|
|
20
|
+
style = {},
|
|
21
|
+
buttonRef,
|
|
22
|
+
}) => {
|
|
23
|
+
return (
|
|
24
|
+
<Tooltip content={label} relationship="label">
|
|
25
|
+
<Button
|
|
26
|
+
ref={buttonRef}
|
|
27
|
+
shape="circular"
|
|
28
|
+
size="small"
|
|
29
|
+
appearance={isVisible ? 'primary' : 'outline'}
|
|
30
|
+
onClick={onClick}
|
|
31
|
+
style={{
|
|
32
|
+
backgroundColor: isVisible ? color : 'transparent',
|
|
33
|
+
color: isVisible ? '#fff' : tokens.colorNeutralForeground1,
|
|
34
|
+
borderColor: color,
|
|
35
|
+
borderWidth: 1,
|
|
36
|
+
width: '100px',
|
|
37
|
+
padding: '4px 8px',
|
|
38
|
+
textAlign: 'center',
|
|
39
|
+
...style,
|
|
40
|
+
}}
|
|
41
|
+
>
|
|
42
|
+
<Caption1
|
|
43
|
+
as="span"
|
|
44
|
+
style={{
|
|
45
|
+
display: 'block',
|
|
46
|
+
overflow: 'hidden',
|
|
47
|
+
textOverflow: 'ellipsis',
|
|
48
|
+
whiteSpace: 'nowrap',
|
|
49
|
+
lineHeight: '1.25',
|
|
50
|
+
}}
|
|
51
|
+
>
|
|
52
|
+
{label}
|
|
53
|
+
</Caption1>
|
|
54
|
+
</Button>
|
|
55
|
+
</Tooltip>
|
|
56
|
+
);
|
|
57
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { LegendContainer } from '../legendContainer/LegendContainer';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { useResponsiveLegend } from '../../hooks/useResponsiveLegend';
|
|
4
|
+
|
|
5
|
+
export interface SliceLegendProps {
|
|
6
|
+
labels: string[];
|
|
7
|
+
colors: string[];
|
|
8
|
+
visibleLabels: string[];
|
|
9
|
+
toggleLabel: (label: string) => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const RenderSliceLegend: React.FC<SliceLegendProps> = ({
|
|
13
|
+
labels,
|
|
14
|
+
colors,
|
|
15
|
+
visibleLabels,
|
|
16
|
+
toggleLabel,
|
|
17
|
+
}) => {
|
|
18
|
+
interface SliceLegendItem {
|
|
19
|
+
label: string;
|
|
20
|
+
color: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const items: SliceLegendItem[] = labels.map((label, i) => ({ label, color: colors[i] }));
|
|
24
|
+
const {
|
|
25
|
+
containerRef,
|
|
26
|
+
|
|
27
|
+
visibleItems,
|
|
28
|
+
overflowItems,
|
|
29
|
+
} = useResponsiveLegend(items);
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<LegendContainer
|
|
33
|
+
containerRef={containerRef}
|
|
34
|
+
|
|
35
|
+
visibleItems={visibleItems}
|
|
36
|
+
overflowItems={overflowItems}
|
|
37
|
+
items={items}
|
|
38
|
+
visibleLabels={visibleLabels}
|
|
39
|
+
toggleLabel={toggleLabel}
|
|
40
|
+
renderLabel={item => item.label}
|
|
41
|
+
getColor={item => item.color}
|
|
42
|
+
/>
|
|
43
|
+
);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export default RenderSliceLegend;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { LegendContainer } from '../legendContainer/LegendContainer';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { useResponsiveLegend } from '../../hooks/useResponsiveLegend';
|
|
4
|
+
|
|
5
|
+
export interface LegendEntry {
|
|
6
|
+
label: string;
|
|
7
|
+
value: number;
|
|
8
|
+
color: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface ValueLegendProps {
|
|
12
|
+
entries: LegendEntry[];
|
|
13
|
+
visibleLabels: string[];
|
|
14
|
+
toggleLabel: (label: string) => void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const RenderValueLegend: React.FC<ValueLegendProps> = ({
|
|
18
|
+
entries,
|
|
19
|
+
visibleLabels,
|
|
20
|
+
toggleLabel,
|
|
21
|
+
}) => {
|
|
22
|
+
const {
|
|
23
|
+
containerRef,
|
|
24
|
+
|
|
25
|
+
visibleItems,
|
|
26
|
+
overflowItems,
|
|
27
|
+
} = useResponsiveLegend(entries);
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<LegendContainer
|
|
31
|
+
containerRef={containerRef}
|
|
32
|
+
visibleItems={visibleItems}
|
|
33
|
+
overflowItems={overflowItems}
|
|
34
|
+
items={entries}
|
|
35
|
+
visibleLabels={visibleLabels}
|
|
36
|
+
toggleLabel={toggleLabel}
|
|
37
|
+
renderLabel={entry => `${entry.label}: ${entry.value}`}
|
|
38
|
+
getColor={entry => entry.color}
|
|
39
|
+
/>
|
|
40
|
+
);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export default RenderValueLegend;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* StackProps interface defines the properties for the Stack component.
|
|
5
|
+
*/
|
|
6
|
+
export interface IStackProps {
|
|
7
|
+
/**
|
|
8
|
+
* Direction of stacking: horizontal (row) or vertical (column).
|
|
9
|
+
*/
|
|
10
|
+
direction?: "horizontal" | "vertical";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Flexbox justify-content property.
|
|
14
|
+
*/
|
|
15
|
+
justifyContent?: React.CSSProperties["justifyContent"];
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Flexbox align-items property.
|
|
19
|
+
*/
|
|
20
|
+
alignItems?: React.CSSProperties["alignItems"];
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Gap between items (can be predefined or custom value like '20px').
|
|
24
|
+
*/
|
|
25
|
+
gap?: "xs" | "s" | "m" | "l" | "xl" | "xxl" | string;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Column gap between items (custom value or predefined).
|
|
29
|
+
*/
|
|
30
|
+
columnGap?: string;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Row gap between items (custom value or predefined).
|
|
34
|
+
*/
|
|
35
|
+
rowGap?: string;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Predefined margin sizes (shorthand) or custom value.
|
|
39
|
+
*/
|
|
40
|
+
margin?: "xs" | "s" | "m" | "l" | "xl" | "xxl" | string;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Predefined padding sizes (shorthand) or custom value.
|
|
44
|
+
*/
|
|
45
|
+
padding?: "xs" | "s" | "m" | "l" | "xl" | "xxl" | string;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Individual margin properties (predefined or custom value).
|
|
49
|
+
*/
|
|
50
|
+
marginTop?: "xs" | "s" | "m" | "l" | "xl" | "xxl" | string;
|
|
51
|
+
marginBottom?: "xs" | "s" | "m" | "l" | "xl" | "xxl" | string;
|
|
52
|
+
marginLeft?: "xs" | "s" | "m" | "l" | "xl" | "xxl" | string;
|
|
53
|
+
marginRight?: "xs" | "s" | "m" | "l" | "xl" | "xxl" | string;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Individual padding properties (predefined or custom value).
|
|
57
|
+
*/
|
|
58
|
+
paddingTop?: "xs" | "s" | "m" | "l" | "xl" | "xxl" | string;
|
|
59
|
+
paddingBottom?: "xs" | "s" | "m" | "l" | "xl" | "xxl" | string;
|
|
60
|
+
paddingLeft?: "xs" | "s" | "m" | "l" | "xl" | "xxl" | string;
|
|
61
|
+
paddingRight?: "xs" | "s" | "m" | "l" | "xl" | "xxl" | string;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Width of the stack.
|
|
65
|
+
*/
|
|
66
|
+
width?: React.CSSProperties["width"];
|
|
67
|
+
|
|
68
|
+
/* Height of the stack. */
|
|
69
|
+
height?: React.CSSProperties["height"];
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Enable or disable wrapping of items.
|
|
73
|
+
*/
|
|
74
|
+
wrap?: boolean;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Child components to be rendered inside the stack.
|
|
78
|
+
*/
|
|
79
|
+
children: React.ReactNode;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Additional inline styles.
|
|
83
|
+
*/
|
|
84
|
+
style?: React.CSSProperties;
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Additional class names for the stack.
|
|
88
|
+
*/
|
|
89
|
+
className?: string;
|
|
90
|
+
/* Overflow property */
|
|
91
|
+
overflow?: React.CSSProperties["overflow"];
|
|
92
|
+
/** background Color */
|
|
93
|
+
background?: React.CSSProperties["backgroundColor"];
|
|
94
|
+
}
|